Patchwork [5,of,5] git: implement diff manifest method

login
register
mail settings
Submitter Josef 'Jeff' Sipek
Date June 1, 2020, 3:32 p.m.
Message ID <c9d3c553309f1b666265.1591025569@meili>
Download mbox | patch
Permalink /patch/46454/
State Accepted
Headers show

Comments

Josef 'Jeff' Sipek - June 1, 2020, 3:32 p.m.
# HG changeset patch
# User Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
# Date 1591024345 14400
#      Mon Jun 01 11:12:25 2020 -0400
# Node ID c9d3c553309f1b6662659e94dbd3fb358e84bdfe
# Parent  c8ee7f58b11b835918e1e83b89741999f8809866
git: implement diff manifest method

This makes 'hg diff' work.
Josef 'Jeff' Sipek - June 1, 2020, 4:19 p.m.
On Mon, Jun 01, 2020 at 11:32:49 -0400, Josef 'Jeff' Sipek wrote:
> # HG changeset patch
> # User Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
> # Date 1591024345 14400
> #      Mon Jun 01 11:12:25 2020 -0400
> # Node ID c9d3c553309f1b6662659e94dbd3fb358e84bdfe
> # Parent  c8ee7f58b11b835918e1e83b89741999f8809866
> git: implement diff manifest method
> 
> This makes 'hg diff' work.
> 
> diff --git a/hgext/git/manifest.py b/hgext/git/manifest.py
> --- a/hgext/git/manifest.py
> +++ b/hgext/git/manifest.py
> @@ -127,8 +127,78 @@ class gittreemanifest(object):
>          return dir in self._dirs
>  
>      def diff(self, other, match=None, clean=False):

Bah.  I completely forgot to deal with matches - so this always producess an
unfiltered diff.

> -        # TODO
> -        assert False
> +        '''Finds changes between the current manifest and m2.
> +
> +        The result is returned as a dict with filename as key and
> +        values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
> +        nodeid in the current/other manifest and fl1/fl2 is the flag
> +        in the current/other manifest. Where the file does not exist,
> +        the nodeid will be None and the flags will be the empty
> +        string.
> +        '''
> +        result = {}
> +
> +        def _iterativediff(t1, t2, subdir):
> +            """compares two trees and appends new tree nodes to examine to
> +            the stack"""
> +            if t1 is not None and t2 is not None and t1.id == t2.id: # TODO: check dirtyness

And about this. :|

But the other 4 commits are fine, IMO :)

Jeff.

Patch

diff --git a/hgext/git/manifest.py b/hgext/git/manifest.py
--- a/hgext/git/manifest.py
+++ b/hgext/git/manifest.py
@@ -127,8 +127,78 @@  class gittreemanifest(object):
         return dir in self._dirs
 
     def diff(self, other, match=None, clean=False):
-        # TODO
-        assert False
+        '''Finds changes between the current manifest and m2.
+
+        The result is returned as a dict with filename as key and
+        values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
+        nodeid in the current/other manifest and fl1/fl2 is the flag
+        in the current/other manifest. Where the file does not exist,
+        the nodeid will be None and the flags will be the empty
+        string.
+        '''
+        result = {}
+
+        def _iterativediff(t1, t2, subdir):
+            """compares two trees and appends new tree nodes to examine to
+            the stack"""
+            if t1 is not None and t2 is not None and t1.id == t2.id: # TODO: check dirtyness
+                return
+            if t1 is None:
+                t1 = ()
+            if t2 is None:
+                t2 = ()
+
+            for e1 in t1:
+                realname = subdir + pycompat.fsencode(e1.name)
+
+                if e1.type == pygit2.GIT_OBJ_TREE:
+                    try:
+                        e2 = t2[e1.name]
+                    except KeyError:
+                        e2 = None
+
+                    if e2.type != pygit2.GIT_OBJ_TREE:
+                        e2 = None
+
+                    stack.append((realname + b'/', e1, e2))
+                else:
+                    n1, fl1 = self.find(realname)
+
+                    try:
+                        e2 = t2[e1.name]
+                        n2, fl2 = other.find(realname)
+                    except KeyError:
+                        e2 = None
+                        n2, fl2 = (None, b'')
+
+                    if e2 is not None and e2.type == pygit2.GIT_OBJ_TREE:
+                        stack.append((realname + b'/', None, e2))
+
+                    if n1 != n2 or fl1 != fl2:
+                        result[realname] = ((n1, fl1), (n2, fl2))
+                    elif clean:
+                        result[realname] = None
+
+            for e2 in t2:
+                if e2.name in t1:
+                    continue
+
+                realname = subdir + pycompat.fsencode(e2.name)
+
+                if e2.type == pygit2.GIT_OBJ_TREE:
+                    stack.append((realname + b'/', None, e2))
+                else:
+                    n2, fl2 = other.find(realname)
+                    result[realname] = ((None, b''), (n2, fl2))
+
+        stack = []
+        _iterativediff(self._tree, other._tree, b'')
+        while stack:
+            subdir, t1, t2 = stack.pop()
+            # stack is populated in the function call
+            _iterativediff(t1, t2, subdir)
+
+        return result
 
     def setflag(self, path, flag):
         node, unused_flag = self._resolve_entry(path)