hgext/extdiff.py
changeset 45127 da2e69a278df
parent 45126 48c38018bd77
child 45128 d23881b17388
--- a/hgext/extdiff.py	Tue Jul 07 12:42:39 2020 +0530
+++ b/hgext/extdiff.py	Tue Jul 07 13:13:18 2020 +0530
@@ -382,6 +382,145 @@
     return 1
 
 
+def diffrevs(
+    ui,
+    repo,
+    node1a,
+    node1b,
+    node2,
+    matcher,
+    tmproot,
+    cmdline,
+    do3way,
+    guitool,
+    opts,
+):
+
+    subrepos = opts.get(b'subrepos')
+    st = repo.status(node1a, node2, matcher, listsubrepos=subrepos)
+    mod_a, add_a, rem_a = set(st.modified), set(st.added), set(st.removed)
+    if do3way:
+        stb = repo.status(node1b, node2, matcher, listsubrepos=subrepos)
+        mod_b, add_b, rem_b = (
+            set(stb.modified),
+            set(stb.added),
+            set(stb.removed),
+        )
+    else:
+        mod_b, add_b, rem_b = set(), set(), set()
+    modadd = mod_a | add_a | mod_b | add_b
+    common = modadd | rem_a | rem_b
+    if not common:
+        return 0
+    # Always make a copy of node1a (and node1b, if applicable)
+    dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
+    dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0]
+    rev1a = b'@%d' % repo[node1a].rev()
+    if do3way:
+        dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
+        dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, subrepos)[0]
+        rev1b = b'@%d' % repo[node1b].rev()
+    else:
+        dir1b = None
+        rev1b = b''
+
+    fnsandstat = []
+
+    # If node2 in not the wc or there is >1 change, copy it
+    dir2root = b''
+    rev2 = b''
+    if node2:
+        dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
+        rev2 = b'@%d' % repo[node2].rev()
+    elif len(common) > 1:
+        # we only actually need to get the files to copy back to
+        # the working dir in this case (because the other cases
+        # are: diffing 2 revisions or single file -- in which case
+        # the file is already directly passed to the diff tool).
+        dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot, subrepos)
+    else:
+        # This lets the diff tool open the changed file directly
+        dir2 = b''
+        dir2root = repo.root
+
+    label1a = rev1a
+    label1b = rev1b
+    label2 = rev2
+
+    # If only one change, diff the files instead of the directories
+    # Handle bogus modifies correctly by checking if the files exist
+    if len(common) == 1:
+        common_file = util.localpath(common.pop())
+        dir1a = os.path.join(tmproot, dir1a, common_file)
+        label1a = common_file + rev1a
+        if not os.path.isfile(dir1a):
+            dir1a = pycompat.osdevnull
+        if do3way:
+            dir1b = os.path.join(tmproot, dir1b, common_file)
+            label1b = common_file + rev1b
+            if not os.path.isfile(dir1b):
+                dir1b = pycompat.osdevnull
+        dir2 = os.path.join(dir2root, dir2, common_file)
+        label2 = common_file + rev2
+
+    if not opts.get(b'per_file'):
+        # Run the external tool on the 2 temp directories or the patches
+        cmdline = formatcmdline(
+            cmdline,
+            repo.root,
+            do3way=do3way,
+            parent1=dir1a,
+            plabel1=label1a,
+            parent2=dir1b,
+            plabel2=label1b,
+            child=dir2,
+            clabel=label2,
+        )
+        ui.debug(b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot))
+        ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
+    else:
+        # Run the external tool once for each pair of files
+        _runperfilediff(
+            cmdline,
+            repo.root,
+            ui,
+            guitool=guitool,
+            do3way=do3way,
+            confirm=opts.get(b'confirm'),
+            commonfiles=common,
+            tmproot=tmproot,
+            dir1a=dir1a,
+            dir1b=dir1b,
+            dir2root=dir2root,
+            dir2=dir2,
+            rev1a=rev1a,
+            rev1b=rev1b,
+            rev2=rev2,
+        )
+
+    for copy_fn, working_fn, st in fnsandstat:
+        cpstat = os.lstat(copy_fn)
+        # Some tools copy the file and attributes, so mtime may not detect
+        # all changes.  A size check will detect more cases, but not all.
+        # The only certain way to detect every case is to diff all files,
+        # which could be expensive.
+        # copyfile() carries over the permission, so the mode check could
+        # be in an 'elif' branch, but for the case where the file has
+        # changed without affecting mtime or size.
+        if (
+            cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
+            or cpstat.st_size != st.st_size
+            or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)
+        ):
+            ui.debug(
+                b'file changed while diffing. '
+                b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)
+            )
+            util.copyfile(copy_fn, working_fn)
+
+    return 1
+
+
 def dodiff(ui, repo, cmdline, pats, opts, guitool=False):
     '''Do the actual diff:
 
@@ -406,9 +545,6 @@
         else:
             ctx1b = repo[nullid]
 
-    perfile = opts.get(b'per_file')
-    confirm = opts.get(b'confirm')
-
     node1a = ctx1a.node()
     node1b = ctx1b.node()
     node2 = ctx2.node()
@@ -418,33 +554,15 @@
         if node1b == nullid:
             do3way = False
 
-    subrepos = opts.get(b'subrepos')
-
     matcher = scmutil.match(repo[node2], pats, opts)
 
     if opts.get(b'patch'):
-        if subrepos:
+        if opts.get(b'subrepos'):
             raise error.Abort(_(b'--patch cannot be used with --subrepos'))
-        if perfile:
+        if opts.get(b'per_file'):
             raise error.Abort(_(b'--patch cannot be used with --per-file'))
         if node2 is None:
             raise error.Abort(_(b'--patch requires two revisions'))
-    else:
-        st = repo.status(node1a, node2, matcher, listsubrepos=subrepos)
-        mod_a, add_a, rem_a = set(st.modified), set(st.added), set(st.removed)
-        if do3way:
-            stb = repo.status(node1b, node2, matcher, listsubrepos=subrepos)
-            mod_b, add_b, rem_b = (
-                set(stb.modified),
-                set(stb.added),
-                set(stb.removed),
-            )
-        else:
-            mod_b, add_b, rem_b = set(), set(), set()
-        modadd = mod_a | add_a | mod_b | add_b
-        common = modadd | rem_a | rem_b
-        if not common:
-            return 0
 
     tmproot = pycompat.mkdtemp(prefix=b'extdiff.')
     try:
@@ -453,119 +571,20 @@
                 ui, repo, node1a, node2, tmproot, matcher, cmdline, do3way
             )
 
-        # Always make a copy of node1a (and node1b, if applicable)
-        dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
-        dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot, subrepos)[0]
-        rev1a = b'@%d' % repo[node1a].rev()
-        if do3way:
-            dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
-            dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot, subrepos)[
-                0
-            ]
-            rev1b = b'@%d' % repo[node1b].rev()
-        else:
-            dir1b = None
-            rev1b = b''
-
-        fnsandstat = []
-
-        # If node2 in not the wc or there is >1 change, copy it
-        dir2root = b''
-        rev2 = b''
-        if node2:
-            dir2 = snapshot(ui, repo, modadd, node2, tmproot, subrepos)[0]
-            rev2 = b'@%d' % repo[node2].rev()
-        elif len(common) > 1:
-            # we only actually need to get the files to copy back to
-            # the working dir in this case (because the other cases
-            # are: diffing 2 revisions or single file -- in which case
-            # the file is already directly passed to the diff tool).
-            dir2, fnsandstat = snapshot(
-                ui, repo, modadd, None, tmproot, subrepos
-            )
-        else:
-            # This lets the diff tool open the changed file directly
-            dir2 = b''
-            dir2root = repo.root
-
-        label1a = rev1a
-        label1b = rev1b
-        label2 = rev2
-
-        # If only one change, diff the files instead of the directories
-        # Handle bogus modifies correctly by checking if the files exist
-        if len(common) == 1:
-            common_file = util.localpath(common.pop())
-            dir1a = os.path.join(tmproot, dir1a, common_file)
-            label1a = common_file + rev1a
-            if not os.path.isfile(dir1a):
-                dir1a = pycompat.osdevnull
-            if do3way:
-                dir1b = os.path.join(tmproot, dir1b, common_file)
-                label1b = common_file + rev1b
-                if not os.path.isfile(dir1b):
-                    dir1b = pycompat.osdevnull
-            dir2 = os.path.join(dir2root, dir2, common_file)
-            label2 = common_file + rev2
+        return diffrevs(
+            ui,
+            repo,
+            node1a,
+            node1b,
+            node2,
+            matcher,
+            tmproot,
+            cmdline,
+            do3way,
+            guitool,
+            opts,
+        )
 
-        if not perfile:
-            # Run the external tool on the 2 temp directories or the patches
-            cmdline = formatcmdline(
-                cmdline,
-                repo.root,
-                do3way=do3way,
-                parent1=dir1a,
-                plabel1=label1a,
-                parent2=dir1b,
-                plabel2=label1b,
-                child=dir2,
-                clabel=label2,
-            )
-            ui.debug(
-                b'running %r in %s\n' % (pycompat.bytestr(cmdline), tmproot)
-            )
-            ui.system(cmdline, cwd=tmproot, blockedtag=b'extdiff')
-        else:
-            # Run the external tool once for each pair of files
-            _runperfilediff(
-                cmdline,
-                repo.root,
-                ui,
-                guitool=guitool,
-                do3way=do3way,
-                confirm=confirm,
-                commonfiles=common,
-                tmproot=tmproot,
-                dir1a=dir1a,
-                dir1b=dir1b,
-                dir2root=dir2root,
-                dir2=dir2,
-                rev1a=rev1a,
-                rev1b=rev1b,
-                rev2=rev2,
-            )
-
-        for copy_fn, working_fn, st in fnsandstat:
-            cpstat = os.lstat(copy_fn)
-            # Some tools copy the file and attributes, so mtime may not detect
-            # all changes.  A size check will detect more cases, but not all.
-            # The only certain way to detect every case is to diff all files,
-            # which could be expensive.
-            # copyfile() carries over the permission, so the mode check could
-            # be in an 'elif' branch, but for the case where the file has
-            # changed without affecting mtime or size.
-            if (
-                cpstat[stat.ST_MTIME] != st[stat.ST_MTIME]
-                or cpstat.st_size != st.st_size
-                or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100)
-            ):
-                ui.debug(
-                    b'file changed while diffing. '
-                    b'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)
-                )
-                util.copyfile(copy_fn, working_fn)
-
-        return 1
     finally:
         ui.note(_(b'cleaning up temp directory\n'))
         shutil.rmtree(tmproot)