changeset 865:4d246150d850

git_handler: handle case where file is renamed and replaced by a symlink This edge case that rename detection introduces wasn't handled previously -- the file would be renamed but the symlink wouldn't be added.
author Siddharth Agarwal <sid0@fb.com>
date Thu, 26 Feb 2015 17:37:20 -0800
parents 9e0954d9abb3
children 3004fd28a8c5
files hggit/git_handler.py tests/test-renames.t
diffstat 2 files changed, 121 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/hggit/git_handler.py
+++ b/hggit/git_handler.py
@@ -1326,6 +1326,10 @@
             renames = {}
             rename_detector = self._rename_detector
 
+        # this set is unused if rename detection isn't enabled -- that makes the
+        # code below simpler
+        renamed_out = set()
+
         changes = diff_tree.tree_changes(self.git.object_store, btree, tree,
                                          rename_detector=rename_detector)
 
@@ -1355,6 +1359,17 @@
             # there.
             # This is not an issue for gitlink <-> {symlink, regular file}
             # transitions because they write to separate dictionaries.
+            #
+            # There's a similar edge case when rename detection is enabled: if a
+            # file is renamed and then replaced by a symlink (typically to the
+            # new location), it is returned by dulwich as an add and a
+            # rename. The order of those results is unspecified. Handle both
+            # cases:
+            # rename first, then add -- delete stored in 'new = file' case with
+            # renamed_out, then renamed_out check passes in 'old = file' case so
+            # is overwritten.
+            # add first, then rename -- add stored in 'old = file' case, then
+            # membership check fails in 'new = file' case so is overwritten.
             if newmode == 0160000:
                 # new = gitlink
                 gitlinks[newfile] = newsha
@@ -1375,12 +1390,18 @@
                 files[newfile] = False, newmode, newsha
                 if renames is not None and newfile != oldfile:
                     renames[newfile] = oldfile
-                    if change.type == diff_tree.CHANGE_RENAME:
+                    renamed_out.add(oldfile)
+                    # the membership check is explained in a comment above
+                    if (change.type == diff_tree.CHANGE_RENAME and
+                        oldfile not in files):
                         files[oldfile] = True, None, None
             else:
                 # old = file
-                # the membership check is explained in a comment above
-                if oldfile not in files:
+                #   files  renamed_out  |  action
+                #     no       *        |   write
+                #    yes       no       |  ignore
+                #    yes      yes       |   write
+                if oldfile not in files or oldfile in renamed_out:
                     files[oldfile] = True, None, None
 
         return files, gitlinks, renames
--- a/tests/test-renames.t
+++ b/tests/test-renames.t
@@ -51,6 +51,20 @@
   rm 'gitsubmodule'
   $ fn_git_commit -m 'move submodule'
 
+Rename a file elsewhere and replace it with a symlink:
+
+  $ git mv beta beta-new
+  $ ln -s beta-new beta
+  $ git add beta
+  $ fn_git_commit -m 'beta renamed'
+
+Rename the file back:
+
+  $ git rm beta
+  rm 'beta'
+  $ git mv beta-new beta
+  $ fn_git_commit -m 'beta renamed back'
+
   $ git checkout -f -b not-master 2>&1 | sed s/\'/\"/g
   Switched to a new branch "not-master"
 
@@ -58,7 +72,75 @@
   $ hg clone -q gitrepo hgrepo
   $ cd hgrepo
   $ hg log -p --graph --template "{rev} {node} {desc|firstline}\n{join(extras, ' ')}\n\n"
-  @  4 d22608e850ea875936802e119831f1789f5d98bd move submodule
+  @  6 10614bb16f4d240ba81b6a71d76a7aa160621a29 beta renamed back
+  |  branch=default hg-git-rename-source=git
+  |
+  |  diff --git a/beta b/beta
+  |  old mode 120000
+  |  new mode 100644
+  |  --- a/beta
+  |  +++ b/beta
+  |  @@ -1,1 +1,12 @@
+  |  -beta-new
+  |  \ No newline at end of file
+  |  +1
+  |  +2
+  |  +3
+  |  +4
+  |  +5
+  |  +6
+  |  +7
+  |  +8
+  |  +9
+  |  +10
+  |  +11
+  |  +12
+  |  diff --git a/beta-new b/beta-new
+  |  deleted file mode 100644
+  |  --- a/beta-new
+  |  +++ /dev/null
+  |  @@ -1,12 +0,0 @@
+  |  -1
+  |  -2
+  |  -3
+  |  -4
+  |  -5
+  |  -6
+  |  -7
+  |  -8
+  |  -9
+  |  -10
+  |  -11
+  |  -12
+  |
+  o  5 96ad24db491a180ccd330556129d75377e201f63 beta renamed
+  |  branch=default hg-git-rename-source=git
+  |
+  |  diff --git a/beta b/beta
+  |  old mode 100644
+  |  new mode 120000
+  |  --- a/beta
+  |  +++ b/beta
+  |  @@ -1,12 +1,1 @@
+  |  -1
+  |  -2
+  |  -3
+  |  -4
+  |  -5
+  |  -6
+  |  -7
+  |  -8
+  |  -9
+  |  -10
+  |  -11
+  |  -12
+  |  +beta-new
+  |  \ No newline at end of file
+  |  diff --git a/beta b/beta-new
+  |  copy from beta
+  |  copy to beta-new
+  |
+  o  4 d22608e850ea875936802e119831f1789f5d98bd move submodule
   |  branch=default hg-git-rename-source=git
   |
   |  diff --git a/.gitmodules b/.gitmodules
@@ -177,8 +259,8 @@
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 01661f396ff940d709465c43e1a8be7f90817bc8
-  # Parent  8aff1302f945109f4ec3daa0a1c4102bbc4dd2e4
+  # Node ID fc770f3d5429f9406cb45eaf4331e16d5f7a700d
+  # Parent  8542264382fc0ad8acf981974805d73bf89e9521
   delta/epsilon
   
   diff --git a/gamma b/delta
@@ -210,8 +292,10 @@
 
   $ cd ../gitrepo
   $ git log master --pretty=oneline
-  7283d2253aa25f55777dd963a3d148048e0f9646 delta/epsilon
-  a317f1d089c8f20846717173058ab10054523e0f gamma2
+  254e71fefd695af5bcbac61c6e5e57cbbada37b8 delta/epsilon
+  bf71b4d53de0f136931cc80482b1d1f47162630a gamma2
+  f95497455dfa891b4cd9b524007eb9514c3ab654 beta renamed back
+  055f482277da6cd3dd37c7093d06983bad68f782 beta renamed
   d7f31298f27df8a9226eddb1e4feb96922c46fa5 move submodule
   c610256cb6959852d9e70d01902a06726317affc add submodule
   e1348449e0c3a417b086ed60fc13f068d4aa8b26 gamma
@@ -221,7 +305,7 @@
 Make sure the right metadata is stored
   $ git cat-file commit master^
   tree 0adbde18545845f3b42ad1a18939ed60a9dec7a8
-  parent d7f31298f27df8a9226eddb1e4feb96922c46fa5
+  parent f95497455dfa891b4cd9b524007eb9514c3ab654
   author test <none@none> 0 +0000
   committer test <none@none> 0 +0000
   HG:rename-source hg
@@ -229,7 +313,7 @@
   gamma2
   $ git cat-file commit master
   tree f8f32f4e20b56a5a74582c6a5952c175bf9ec155
-  parent a317f1d089c8f20846717173058ab10054523e0f
+  parent bf71b4d53de0f136931cc80482b1d1f47162630a
   author test <none@none> 0 +0000
   committer test <none@none> 0 +0000
   HG:rename gamma:delta
@@ -247,8 +331,8 @@
   # User test
   # Date 0 0
   #      Thu Jan 01 00:00:00 1970 +0000
-  # Node ID 01661f396ff940d709465c43e1a8be7f90817bc8
-  # Parent  8aff1302f945109f4ec3daa0a1c4102bbc4dd2e4
+  # Node ID fc770f3d5429f9406cb45eaf4331e16d5f7a700d
+  # Parent  8542264382fc0ad8acf981974805d73bf89e9521
   delta/epsilon
   
   diff --git a/gamma b/delta
@@ -278,8 +362,10 @@
   $ hg gexport
   $ cd .hg/git
   $ git log master --pretty=oneline
-  7283d2253aa25f55777dd963a3d148048e0f9646 delta/epsilon
-  a317f1d089c8f20846717173058ab10054523e0f gamma2
+  254e71fefd695af5bcbac61c6e5e57cbbada37b8 delta/epsilon
+  bf71b4d53de0f136931cc80482b1d1f47162630a gamma2
+  f95497455dfa891b4cd9b524007eb9514c3ab654 beta renamed back
+  055f482277da6cd3dd37c7093d06983bad68f782 beta renamed
   d7f31298f27df8a9226eddb1e4feb96922c46fa5 move submodule
   c610256cb6959852d9e70d01902a06726317affc add submodule
   e1348449e0c3a417b086ed60fc13f068d4aa8b26 gamma