Re: git rebase chokes on directory -> symlink -> directory

3 messages, 3 authors, 2016-06-15 · open the first message on its own page

Re: git rebase chokes on directory -> symlink -> directory

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:43:09

"H. Peter Anvin" [off-list ref] writes:
Applying 'Revert "x86-64: Make arch/x86-64/boot a symlink to
arch/i386/boot"'

Adds trailing whitespace.
.dotest/patch:117:FDARGS =
Adds trailing whitespace.
.dotest/patch:350: * Page 0 is deliberately kept safe, since System
Management Mode code in
Adds trailing whitespace.
.dotest/patch:352: * useful for future device drivers that either access
the BIOS via VM86
Adds trailing whitespace.
.dotest/patch:648: *
Adds trailing whitespace.
.dotest/patch:649: * This is a collection of several routines from
gzip-1.0.3
error: arch/x86_64/boot/.gitignore: already exists in working directory
error: arch/x86_64/boot/Makefile: already exists in working directory
error: arch/x86_64/boot/compressed/Makefile: already exists in working
directory
error: arch/x86_64/boot/compressed/head.S: already exists in working
directory
error: arch/x86_64/boot/compressed/misc.c: already exists in working
directory
error: arch/x86_64/boot/compressed/vmlinux.lds: already exists in
working directory
error: arch/x86_64/boot/compressed/vmlinux.scr: already exists in
working directory
error: arch/x86_64/boot/install.sh: already exists in working directory
error: arch/x86_64/boot/mtools.conf.in: already exists in working directory
error: arch/x86_64/boot/tools/.gitignore: already exists in working
directory
error: arch/x86_64/boot/tools/build.c: already exists in working directory
Ahh.

 * The tree state before this patch is applied has arch/x86_64/boot
   as a symlink pointing at ../i386/boot/

 * The patch tries to remove arch/x86_64/boot symlink, and
   create bunch of files there: .gitignore, Makefile, etc.

This is unfortunately a bit deeper than just git-rebase.  You
exposed a nasty corner case problem in the stock git-apply,
probably one of the most important tool the kernel project uses
every day.

git-apply tries to be careful while applying patches; it never
touches the working tree until it is convinced that the patch
would apply cleanly.  One of the check it does is that when it
knows a path is going to be created by the patch (in your
example, arch/x86_64/boot/.gitignore), it runs lstat() on the
path to make sure it does not exist.

This leads to a false alarm.  Because we do not touch the
working tree before all the check passes, when we try to make
sure that arch/x86_64/boot/.gitignore does not exist yet, we
haven't removed the arch/x86_64/boot symlink.  The lstat() check
ends up seeing arch/i386/boot/.gitignore through the
yet-to-be-removed symlink, and says "Hey, you already have a
file there, but what you fed me is a patch to create a new
file. I am not going to clobber what you have in the working
tree."

We have similar checks to see a file we are going to modify does
exist and match the preimage of the diff, which is done by
directly opening and reading the file.

For a file we are going to delete, we make sure that it does
exist and matches what is going to be removed (a removal patch
records the full preimage, so we check what you have in your
working tree matches it in full -- otherwise we would risk
losing your local changes), which again is done by directly
opening and reading the file.

These checks need to be adjusted so that they are not fooled by
symlinks in the middle.

 - To make sure something does not exist, first lstat().  If it
   does not exist, it does not, so be happy.  If it _does_, we
   might be getting fooled by a symlink in the middle, so break
   leading paths and see if there are symlinks involved.  When
   we are checking for a path a/b/c/d, if any of a, a/b, a/b/c
   is a symlink, then a/b/c/d does _NOT_ exist, for the purpose
   of our test.

   This would fix this particular case you saw, and would not
   add extra overhead in the usual case.

 - To make sure something already exists, first lstat().  If it
   does not exist, barf (up to this, we already do).  Even if it
   does seem to exist, we might be getting fooled by a symlink
   in the middle, so make sure leading paths are not symlinks.

   This would make the normal codepath much more expensive for
   deep trees, which is a bit worrisome.

When the code writes out the result after the checks pass, we
first delete all the paths that are going to be deleted, and
also delete all the paths that are being modified.  Then in the
second pass, we create all the paths that are being created and
also modified by writing their final contents out, while
creating leading directories as needed.  Because of this, a
patch that removes a symlink and then makes it a directory to
hold new files would first remove the symlink and then create
the directory at the right location and deposit newly created
files there.  So I do not expect any change is needed in the
writeout codepath.

By the way, I noticed a few things while diagnosing this.

 * It was very considerate of you to leave "rebase-1" branch for
   my postmortem in the repository.  You know what are needed
   for debugging very well.

 * git-rebase with -m is dog slow.  There were people who
   advocated to make it the default, but they probably are
   either working in a very small project, or working on a
   filesystem that even git-apply is slow that the speed
   difference does not matter to them.

Re: git rebase chokes on directory -> symlink -> directory

From: Johannes Sixt <hidden>
Date: 2016-06-15 22:43:09

Junio C Hamano wrote:
 * git-rebase with -m is dog slow.  There were people who
   advocated to make it the default, but they probably are
   either working in a very small project, or working on a
   filesystem that even git-apply is slow that the speed
   difference does not matter to them.
Heh... you name it. But just yesterday rebase -m was a life saver for me
because of its rename detection.

-- Hannes

Re: git rebase chokes on directory -> symlink -> directory

From: Shawn O. Pearce <hidden>
Date: 2016-06-15 22:43:09

Junio C Hamano [off-list ref] wrote:
 * git-rebase with -m is dog slow.  There were people who
   advocated to make it the default, but they probably are
   either working in a very small project, or working on a
   filesystem that even git-apply is slow that the speed
   difference does not matter to them.
That would have been me saying make -m default.  Because on git.git
I don't really see a huge performance difference on my Mac, and the
bigger projects that I work on are on Cygwin, where *everything*
is slow as hell.

The few places there that I do need rebase, I actually also need
the rename detection that am -3 or rebase -m would do, so I just
wind up paying the penalty anyway.  Or really I just get burned
because I run the op without the magic "do what I really need"
flag, and it dies, and I have to reset and start it again, which
just takes longer than if -3 or -m was the default.

But that's not the situation everyone else has, so its reasonable
that -m ain't the default.  ;-)

-- 
Shawn.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help