patch: refactor content diffing part in separate fn so extensions can wrap
authorPulkit Goyal <7895pulkit@gmail.com>
Mon, 06 Jul 2020 15:31:53 +0530
changeset 45098 20a65e397943
parent 45097 dff208398ede
child 45099 de1d3f4246e9
patch: refactor content diffing part in separate fn so extensions can wrap Right now extdiff uses it's own logic using archival to diff two versions of file using external diff tools. This makes the extdiff functionality non-extensible. This series is an attempt to refactor core patch and diff functionality so that extdiff can wrap and reuse it. This will help us in using external diffing tools at more places and not just extdiff command only then. Differential Revision: https://phab.mercurial-scm.org/D8685
mercurial/patch.py
--- a/mercurial/patch.py	Thu Jul 09 12:52:04 2020 +0200
+++ b/mercurial/patch.py	Mon Jul 06 15:31:53 2020 +0530
@@ -2922,6 +2922,18 @@
         yield f1, f2, copyop
 
 
+def _gitindex(text):
+    if not text:
+        text = b""
+    l = len(text)
+    s = hashutil.sha1(b'blob %d\0' % l)
+    s.update(text)
+    return hex(s.digest())
+
+
+_gitmode = {b'l': b'120000', b'x': b'100755', b'': b'100644'}
+
+
 def trydiff(
     repo,
     revs,
@@ -2944,14 +2956,6 @@
     pathfn is applied to every path in the diff output.
     '''
 
-    def gitindex(text):
-        if not text:
-            text = b""
-        l = len(text)
-        s = hashutil.sha1(b'blob %d\0' % l)
-        s.update(text)
-        return hex(s.digest())
-
     if opts.noprefix:
         aprefix = bprefix = b''
     else:
@@ -2968,8 +2972,6 @@
     date1 = dateutil.datestr(ctx1.date())
     date2 = dateutil.datestr(ctx2.date())
 
-    gitmode = {b'l': b'120000', b'x': b'100755', b'': b'100644'}
-
     if not pathfn:
         pathfn = lambda f: f
 
@@ -3023,11 +3025,11 @@
                 b'diff --git %s%s %s%s' % (aprefix, path1, bprefix, path2)
             )
             if not f1:  # added
-                header.append(b'new file mode %s' % gitmode[flag2])
+                header.append(b'new file mode %s' % _gitmode[flag2])
             elif not f2:  # removed
-                header.append(b'deleted file mode %s' % gitmode[flag1])
+                header.append(b'deleted file mode %s' % _gitmode[flag1])
             else:  # modified/copied/renamed
-                mode1, mode2 = gitmode[flag1], gitmode[flag2]
+                mode1, mode2 = _gitmode[flag1], _gitmode[flag2]
                 if mode1 != mode2:
                     header.append(b'old mode %s' % mode1)
                     header.append(b'new mode %s' % mode2)
@@ -3071,39 +3073,66 @@
             if fctx2 is not None:
                 content2 = fctx2.data()
 
-        if binary and opts.git and not opts.nobinary:
-            text = mdiff.b85diff(content1, content2)
-            if text:
-                header.append(
-                    b'index %s..%s' % (gitindex(content1), gitindex(content2))
+        data1 = (ctx1, fctx1, path1, flag1, content1, date1)
+        data2 = (ctx2, fctx2, path2, flag2, content2, date2)
+        yield diffcontent(data1, data2, header, binary, opts)
+
+
+def diffcontent(data1, data2, header, binary, opts):
+    """ diffs two versions of a file.
+
+    data1 and data2 are tuples containg:
+
+        * ctx: changeset for the file
+        * fctx: file context for that file
+        * path1: name of the file
+        * flag: flags of the file
+        * content: full content of the file (can be null in case of binary)
+        * date: date of the changeset
+
+    header: the patch header
+    binary: whether the any of the version of file is binary or not
+    opts:   user passed options
+
+    It exists as a separate function so that extensions like extdiff can wrap
+    it and use the file content directly.
+    """
+
+    ctx1, fctx1, path1, flag1, content1, date1 = data1
+    ctx2, fctx2, path2, flag2, content2, date2 = data2
+    if binary and opts.git and not opts.nobinary:
+        text = mdiff.b85diff(content1, content2)
+        if text:
+            header.append(
+                b'index %s..%s' % (_gitindex(content1), _gitindex(content2))
+            )
+        hunks = ((None, [text]),)
+    else:
+        if opts.git and opts.index > 0:
+            flag = flag1
+            if flag is None:
+                flag = flag2
+            header.append(
+                b'index %s..%s %s'
+                % (
+                    _gitindex(content1)[0 : opts.index],
+                    _gitindex(content2)[0 : opts.index],
+                    _gitmode[flag],
                 )
-            hunks = ((None, [text]),)
-        else:
-            if opts.git and opts.index > 0:
-                flag = flag1
-                if flag is None:
-                    flag = flag2
-                header.append(
-                    b'index %s..%s %s'
-                    % (
-                        gitindex(content1)[0 : opts.index],
-                        gitindex(content2)[0 : opts.index],
-                        gitmode[flag],
-                    )
-                )
-
-            uheaders, hunks = mdiff.unidiff(
-                content1,
-                date1,
-                content2,
-                date2,
-                path1,
-                path2,
-                binary=binary,
-                opts=opts,
             )
-            header.extend(uheaders)
-        yield fctx1, fctx2, header, hunks
+
+        uheaders, hunks = mdiff.unidiff(
+            content1,
+            date1,
+            content2,
+            date2,
+            path1,
+            path2,
+            binary=binary,
+            opts=opts,
+        )
+        header.extend(uheaders)
+    return fctx1, fctx2, header, hunks
 
 
 def diffstatsum(stats):