Re: git rebase chokes on directory -> symlink -> directory
- From
Junio C Hamano <junkio@cox.net>
- Date
- May 14, 2007, 04:37 UTC
- Message-ID
- <7v1whkow9z.fsf@assigned-by-dhcp.cox.net>
- In-Reply-To
- <7vps569904.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> writes:
Show 11 quoted lines
> However. > > I usually have "[apply] whitespace = strip" in my ~/.gitconfig, > but during this verification run, I disabled it to keep rebase > from falling back to 3-way merge using merge-recursive. If I > turn it on, rebase still fails and I strongly suspect "rebase > -m" would fail the same way, although I haven't tried it (it > takes too much time). > > I'll be somewhat busy this weekend, so I would welcome anybody > else beating me to fixing the problem in merge-recursive.
Yuck. merge-recursive does seem to have problem with D/F conflict in general; I suspect this is not limited to cases that involve symbolic links.
First, the setup.
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/hpa/linux-2.6-newsetup.git/ $ cd linux-2.6-newsetup.git $ git fetch git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git/ $ git branch other b2ad90f4969226fe8cf3edc5330711ed5fc20105 $ git branch ancestor other^1 $ git reset --hard ancestor $ echo >>Makefile ; git add Makefile ; git commit -m 'change one' $ git branch try
To see that the underlying read-tree is working as expected (and do not have to be debugged), we can first try the usual three-way read tree:
$ git read-tree -m -u ancestor HEAD other $ git ls-files -u --abbrev arch/x86-64
This would show...
120000 ad3f146 3 arch/x86_64/boot
100644 495f20c 1 arch/x86_64/boot/.gitignore
100644 495f20c 2 arch/x86_64/boot/.gitignore
100644 ee6f650 1 arch/x86_64/boot/Makefile
100644 ee6f650 2 arch/x86_64/boot/Makefile
...The current HEAD (the one that we tagged as 'try') has the directory arch/x86_64/boot/, but all of its files are unchanged from the "common ancestor", so they all have identical stages 1 and 2, with stage 3 missing. The other tree has boot/ as a symlink. These "One-side removes other side does not touch" and "one-side adds" cases are left unmerged by "read-tree -m -u", as that is how merge-recursive can find renames to begin with.
Now we've seen what the read-tree (which is the same machinery used as git_merge_trees() in mege-recursive) does with these three trees, let's see how merge-recursive finishes this off:
$ git reset --hard try
$ git merge-recursive ancestor -- HEAD other
CONFLICT (directory/file): There is a directory with name arch/x86_64/boot in HEAD. Added arch/x86_64/boot as arch/x86_64/boot~other
Removed arch/x86_64/boot/.gitignore
Removed arch/x86_64/boot/Makefile
Removed arch/x86_64/boot/bootsect.S
Removed arch/x86_64/boot/compressed/Makefile
Removed arch/x86_64/boot/compressed/head.S
Removed arch/x86_64/boot/compressed/misc.c
Removed arch/x86_64/boot/compressed/vmlinux.lds
Removed arch/x86_64/boot/compressed/vmlinux.scr
Removed arch/x86_64/boot/install.sh
Removed arch/x86_64/boot/mtools.conf.in
Removed arch/x86_64/boot/setup.S
Removed arch/x86_64/boot/tools/.gitignore
Removed arch/x86_64/boot/tools/build.cThere is no rename, so rename detection does not interfere, but the D/F conflict detection code in merge-recursive thinks that arch/x86_64/boot is a directory in one tree (yes, it is true in the original tree, but it goes away as all files under it), and a non-directory in another. And instead of replacing the boot/ directory that becomes empty with a new symlink boot/, it creates the boot~funny-name symlink and fails the operation.
When it actually is checking out arch/x86_64/boot out of the resolved index, it should notice that (1) arch/x86_64/boot directory is unnecessary to house anything in the resulting index anymore, and that (2) there is no locally created untracked file that is 'precious' [*1*]. Then it can rmdir() and create the boot/ symlink in its place. To fix this, it may be necessary to update its checkout code to perform the "remove first then create" two-pass process git-apply does.
It is unfortunate that for merge-recursive it is probably too cumbersome to always do the right thing, but I think it should at least notice that arch/x86_64/boot whose files all disappear does not conflict with creation of the new symlink.