* More symlink/directory troubles @ 2009-07-28 22:13 James Pickens 2009-07-28 22:13 ` [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink James Pickens 0 siblings, 1 reply; 18+ messages in thread From: James Pickens @ 2009-07-28 22:13 UTC (permalink / raw) To: git, gitster; +Cc: barvik This is a follow up to the original thread and patch at http://article.gmane.org/gmane.comp.version-control.git/122297. There was a bug report about problems when a directory is replaced with a symlink. I said that the patch fixed the bug for me, but I didn't test thoroughly enough, because it turns out there are 3 bugs, and the patch only fixed one of them. I am sending some test scripts to demonstrate the bugs in hopes of spurring some activity here. I think the convention is to use test_expect_failure for this sort of test, so that's what I did. Unfortunately I have very little time in the next 2 weeks, so I probably won't be able to do much more than send the tests, and fix them up if necessary. In 2 weeks I can take a look at the C code, if it isn't already fixed by then. James ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink. 2009-07-28 22:13 More symlink/directory troubles James Pickens @ 2009-07-28 22:13 ` James Pickens 2009-07-28 22:13 ` [PATCH 2/2] Demonstrate merge failure " James Pickens 2009-07-29 8:19 ` [PATCH 1/2] Demonstrate bugs " Michael J Gruber 0 siblings, 2 replies; 18+ messages in thread From: James Pickens @ 2009-07-28 22:13 UTC (permalink / raw) To: git, gitster; +Cc: barvik, James Pickens This test creates two directories, a/b and a/b-2, then replaces a/b with a symlink to a/b-2, then merges that change into another branch that contains an unrelated change. There are two bugs: 1. 'git checkout' wrongly deletes work tree file a/b-2/d. 2. 'git merge' wrongly deletes work tree file a/b-2/d. Signed-off-by: James Pickens <james.e.pickens@intel.com> --- t/t6035-merge-dir-to-symlink.sh | 32 ++++++++++++++++++++++++++++++++ 1 files changed, 32 insertions(+), 0 deletions(-) create mode 100755 t/t6035-merge-dir-to-symlink.sh diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh new file mode 100755 index 0000000..926c8ed --- /dev/null +++ b/t/t6035-merge-dir-to-symlink.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +test_description='merging when a directory was replaced with a symlink' +. ./test-lib.sh + +test_expect_success setup ' + mkdir -p a/b/c a/b-2/c && + > a/b/c/d && + > a/b-2/c/d && + > a/x && + git add -A && + git commit -m base && + rm -rf a/b && + ln -s b-2 a/b && + git add -A && + git commit -m "dir to symlink" +' + +test_expect_failure 'checkout should not delete a/b-2/c/d' ' + git checkout -b temp HEAD^ && + test -f a/b-2/c/d +' + +test_expect_failure 'merge should not delete a/b-2/c/d' ' + echo x > a/x && + git add a/x && + git commit -m x && + git merge master && + test -f a/b-2/c/d +' + +test_done -- 1.6.2.5.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/2] Demonstrate merge failure when a directory is replaced with a symlink. 2009-07-28 22:13 ` [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink James Pickens @ 2009-07-28 22:13 ` James Pickens 2009-07-29 8:29 ` Michael J Gruber 2009-07-29 8:19 ` [PATCH 1/2] Demonstrate bugs " Michael J Gruber 1 sibling, 1 reply; 18+ messages in thread From: James Pickens @ 2009-07-28 22:13 UTC (permalink / raw) To: git, gitster; +Cc: barvik, James Pickens This test creates two directories, a/b and a/b-2, replaces a/b-2 with a symlink to a/b, then merges that change into another branch that contains unrelated changes. Since the changes are unrelated, the merge should be free of conflicts, but 'git merge' gives a file/directory conflict. Note that this test is almost identical to t6035, except that instead of replacing a/b with a symlink, it replaces a/b-2 with a symlink. This test results in a merge conflict, whereas t6035 does not. Signed-off-by: James Pickens <james.e.pickens@intel.com> --- t/t6036-merge-dir-to-symlink.sh | 30 ++++++++++++++++++++++++++++++ 1 files changed, 30 insertions(+), 0 deletions(-) create mode 100755 t/t6036-merge-dir-to-symlink.sh diff --git a/t/t6036-merge-dir-to-symlink.sh b/t/t6036-merge-dir-to-symlink.sh new file mode 100755 index 0000000..020db7c --- /dev/null +++ b/t/t6036-merge-dir-to-symlink.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +test_description='merging when a directory was replaced with a symlink' +. ./test-lib.sh + +test_expect_success setup ' + mkdir -p a/b/c a/b-2/c && + > a/b/c/d && + > a/b-2/c/d && + > a/x && + git add -A && + git commit -m base && + rm -rf a/b-2 && + ln -s b a/b-2 && + git add -A && + git commit -m "dir to symlink" +' + +test_expect_failure 'checkout should not delete a/b/c/d' ' + git checkout -b temp HEAD^ && + test -f a/b/c/d +' + +test_expect_failure 'merge should not have conflicts' ' + echo x > a/x && + git add a/x && + git commit -m x && + git merge master' + +test_done -- 1.6.2.5.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH 2/2] Demonstrate merge failure when a directory is replaced with a symlink. 2009-07-28 22:13 ` [PATCH 2/2] Demonstrate merge failure " James Pickens @ 2009-07-29 8:29 ` Michael J Gruber 2009-07-29 16:39 ` Pickens, James E 0 siblings, 1 reply; 18+ messages in thread From: Michael J Gruber @ 2009-07-29 8:29 UTC (permalink / raw) To: James Pickens; +Cc: git, gitster, barvik James Pickens venit, vidit, dixit 29.07.2009 00:13: > This test creates two directories, a/b and a/b-2, replaces a/b-2 with a > symlink to a/b, then merges that change into another branch that > contains unrelated changes. Since the changes are unrelated, the merge > should be free of conflicts, but 'git merge' gives a file/directory > conflict. > > Note that this test is almost identical to t6035, except that instead of > replacing a/b with a symlink, it replaces a/b-2 with a symlink. This > test results in a merge conflict, whereas t6035 does not. In fact they are identical: Exchange b for b-2 and vice versa everywhere and you get the same test, except for the fact that in 1/2 you "test -f" in the last step. But I'm sure that test fails at the merge step already (because of a dirty worktree), doesn't it? You should see this when running the test with -d/-v. (I'm guessing, I haven't run your test.) > > Signed-off-by: James Pickens <james.e.pickens@intel.com> > --- > t/t6036-merge-dir-to-symlink.sh | 30 ++++++++++++++++++++++++++++++ > 1 files changed, 30 insertions(+), 0 deletions(-) > create mode 100755 t/t6036-merge-dir-to-symlink.sh > > diff --git a/t/t6036-merge-dir-to-symlink.sh b/t/t6036-merge-dir-to-symlink.sh > new file mode 100755 > index 0000000..020db7c > --- /dev/null > +++ b/t/t6036-merge-dir-to-symlink.sh > @@ -0,0 +1,30 @@ > +#!/bin/sh > + > +test_description='merging when a directory was replaced with a symlink' > +. ./test-lib.sh > + > +test_expect_success setup ' > + mkdir -p a/b/c a/b-2/c && > + > a/b/c/d && > + > a/b-2/c/d && > + > a/x && > + git add -A && > + git commit -m base && > + rm -rf a/b-2 && > + ln -s b a/b-2 && > + git add -A && > + git commit -m "dir to symlink" > +' > + > +test_expect_failure 'checkout should not delete a/b/c/d' ' > + git checkout -b temp HEAD^ && > + test -f a/b/c/d > +' > + > +test_expect_failure 'merge should not have conflicts' ' > + echo x > a/x && > + git add a/x && > + git commit -m x && > + git merge master' > + > +test_done As in 1/2, I think the first expect_failure leaves a dirty/unexpected worktree (d missing) which causes the merge failure in the last step. Michael ^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH 2/2] Demonstrate merge failure when a directory is replaced with a symlink. 2009-07-29 8:29 ` Michael J Gruber @ 2009-07-29 16:39 ` Pickens, James E 0 siblings, 0 replies; 18+ messages in thread From: Pickens, James E @ 2009-07-29 16:39 UTC (permalink / raw) To: Michael J Gruber Cc: git@vger.kernel.org, gitster@pobox.com, barvik@broadpark.no On Wed, Jul 29, 2009, Michael J Gruber<git@drmicha.warpmail.net> wrote: > In fact they are identical: Exchange b for b-2 and vice versa everywhere > and you get the same test, except for the fact that in 1/2 you "test -f" > in the last step. But I'm sure that test fails at the merge step already > (because of a dirty worktree), doesn't it? You should see this when > running the test with -d/-v. (I'm guessing, I haven't run your test.) <snip> > As in 1/2, I think the first expect_failure leaves a dirty/unexpected > worktree (d missing) which causes the merge failure in the last step. That's true, but this test still fails even after applying Kjetil's patch that fixes the first problem (so the work tree is clean before 'git merge' is run). James ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink. 2009-07-28 22:13 ` [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink James Pickens 2009-07-28 22:13 ` [PATCH 2/2] Demonstrate merge failure " James Pickens @ 2009-07-29 8:19 ` Michael J Gruber 2009-07-29 8:33 ` Junio C Hamano 1 sibling, 1 reply; 18+ messages in thread From: Michael J Gruber @ 2009-07-29 8:19 UTC (permalink / raw) To: James Pickens; +Cc: git, gitster, barvik James Pickens venit, vidit, dixit 29.07.2009 00:13: > This test creates two directories, a/b and a/b-2, then replaces a/b with > a symlink to a/b-2, then merges that change into another branch that > contains an unrelated change. > > There are two bugs: > 1. 'git checkout' wrongly deletes work tree file a/b-2/d. > 2. 'git merge' wrongly deletes work tree file a/b-2/d. > > Signed-off-by: James Pickens <james.e.pickens@intel.com> > --- > t/t6035-merge-dir-to-symlink.sh | 32 ++++++++++++++++++++++++++++++++ > 1 files changed, 32 insertions(+), 0 deletions(-) > create mode 100755 t/t6035-merge-dir-to-symlink.sh > > diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh > new file mode 100755 > index 0000000..926c8ed > --- /dev/null > +++ b/t/t6035-merge-dir-to-symlink.sh > @@ -0,0 +1,32 @@ > +#!/bin/sh > + > +test_description='merging when a directory was replaced with a symlink' > +. ./test-lib.sh > + > +test_expect_success setup ' > + mkdir -p a/b/c a/b-2/c && > + > a/b/c/d && > + > a/b-2/c/d && > + > a/x && > + git add -A && > + git commit -m base && > + rm -rf a/b && > + ln -s b-2 a/b && > + git add -A && > + git commit -m "dir to symlink" > +' > + > +test_expect_failure 'checkout should not delete a/b-2/c/d' ' > + git checkout -b temp HEAD^ && > + test -f a/b-2/c/d > +' > + > +test_expect_failure 'merge should not delete a/b-2/c/d' ' > + echo x > a/x && > + git add a/x && > + git commit -m x && > + git merge master && > + test -f a/b-2/c/d > +' > + > +test_done Isn't the failure of the second test caused by that of the first one? a/b-2/c/d is gone from the worktree, and master does not touch it, so the merge leaves the worktree version (non-existent) as is. Michael ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink. 2009-07-29 8:19 ` [PATCH 1/2] Demonstrate bugs " Michael J Gruber @ 2009-07-29 8:33 ` Junio C Hamano 2009-07-29 16:57 ` Pickens, James E 2009-07-29 17:48 ` [PATCH v2] " Pickens, James E 0 siblings, 2 replies; 18+ messages in thread From: Junio C Hamano @ 2009-07-29 8:33 UTC (permalink / raw) To: Michael J Gruber; +Cc: James Pickens, git, gitster, Kjetil Barvik Michael J Gruber <git@drmicha.warpmail.net> writes: >> +test_expect_failure 'checkout should not delete a/b-2/c/d' ' >> + git checkout -b temp HEAD^ && >> + test -f a/b-2/c/d >> +' >> + >> +test_expect_failure 'merge should not delete a/b-2/c/d' ' >> + echo x > a/x && >> + git add a/x && >> + git commit -m x && >> + git merge master && >> + test -f a/b-2/c/d >> +' >> + >> +test_done > > Isn't the failure of the second test caused by that of the first one? > a/b-2/c/d is gone from the worktree, and master does not touch it, so > the merge leaves the worktree version (non-existent) as is. To avoid that impression the second test should probably have been written to start from a clean slate, using "reset --hard" or something. Kjetil's patch actually fixes the first one, but the second one will still show breakage. I wonder if the breakage is in recursive merge or in the generic read-tree three-way merge code. I highly suspect that using "git merge -s resolve" would make the test pass. Historically recursive merge is known to be careless in many corner cases. ^ permalink raw reply [flat|nested] 18+ messages in thread
* RE: [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink. 2009-07-29 8:33 ` Junio C Hamano @ 2009-07-29 16:57 ` Pickens, James E 2009-07-29 17:48 ` [PATCH v2] " Pickens, James E 1 sibling, 0 replies; 18+ messages in thread From: Pickens, James E @ 2009-07-29 16:57 UTC (permalink / raw) To: Junio C Hamano, Michael J Gruber; +Cc: git@vger.kernel.org, Kjetil Barvik On Wed, Jul 29, 2009, Junio C Hamano<gitster@pobox.com> wrote: > Michael J Gruber <git@drmicha.warpmail.net> writes: >> Isn't the failure of the second test caused by that of the first one? >> a/b-2/c/d is gone from the worktree, and master does not touch it, so >> the merge leaves the worktree version (non-existent) as is. > > To avoid that impression the second test should probably have been written > to start from a clean slate, using "reset --hard" or something. I'll send a new patch shortly that combines the two tests into one and includes the "reset --hard". > Kjetil's patch actually fixes the first one, but the second one will still > show breakage. > > I wonder if the breakage is in recursive merge or in the generic read-tree > three-way merge code. I highly suspect that using "git merge -s resolve" > would make the test pass. Historically recursive merge is known to be > careless in many corner cases. You're right, using the resolve strategy does make the test pass. James ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 8:33 ` Junio C Hamano 2009-07-29 16:57 ` Pickens, James E @ 2009-07-29 17:48 ` Pickens, James E 2009-07-29 18:31 ` Junio C Hamano 1 sibling, 1 reply; 18+ messages in thread From: Pickens, James E @ 2009-07-29 17:48 UTC (permalink / raw) To: Junio C Hamano, git@vger.kernel.org; +Cc: Kjetil Barvik, Michael J Gruber This test creates two directories, a/b and a/b-2, then replaces a/b with a symlink to a/b-2, then merges that change into another branch that contains an unrelated change. There are two bugs: 1. 'git checkout' wrongly deletes work tree file a/b-2/d. 2. 'git merge' wrongly deletes work tree file a/b-2/d. The test goes on to create another branch in which a/b-2 is replaced with a symlink to a/b (i.e., the reverse of what was done the first time), and merge it with the "unrelated changes" branch. There's another bug: 3. Since the changes are unrelated, the merge should be clean, but git reports a conflict. Note that using the resolve strategy instead of recursive makes the second bug go away, but not the third one. Signed-off-by: James Pickens <james.e.pickens@intel.com> --- This version combines the two tests into one, and cleans up between steps so that the early failures don't affect the later tests. This time I include the bare minimum commands inside the test_expect_failure calls, which seems like the right thing to do, since the other commands are expected to "succeed" (exit code of 0). BTW I'm sending this patch using 'git format-patch' + Outlook instead of 'git send-email'; apologies if it gets botched. t/t6035-merge-dir-to-symlink.sh | 49 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 49 insertions(+), 0 deletions(-) create mode 100755 t/t6035-merge-dir-to-symlink.sh diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh new file mode 100755 index 0000000..94a9f32 --- /dev/null +++ b/t/t6035-merge-dir-to-symlink.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='merging when a directory was replaced with a symlink' +. ./test-lib.sh + +test_expect_success 'setup a merge where dir a/b changed to symlink' ' + mkdir -p a/b/c a/b-2/c && + > a/b/c/d && + > a/b-2/c/d && + > a/x && + git add -A && + git commit -m base && + rm -rf a/b && + ln -s b-2 a/b && + git add -A && + git commit -m "dir to symlink" && + git checkout -b temp HEAD^ +' + +test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' + test -f a/b-2/c/d +' + +test_expect_success 'clean the work tree and do the merge' ' + git reset --hard && + test -f a/b-2/c/d && + echo x > a/x && + git add a/x && + git commit -m x && + git merge master +' + +test_expect_failure 'merge should not have deleted a/b-2/c/d' ' + test -f a/b-2/c/d +' + +test_expect_success 'setup a merge where dir a/b-2 changed to symlink' ' + git checkout -f -b temp2 master^ && + rm -rf a/b-2 && + ln -s b a/b-2 && + git add -A && + git commit -m "dir a/b-2 to symlink" +' + +test_expect_failure 'merge should not have conflicts' ' + git merge temp +' + +test_done -- 1.6.2.5 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 17:48 ` [PATCH v2] " Pickens, James E @ 2009-07-29 18:31 ` Junio C Hamano 2009-07-29 21:02 ` [PATCH v3] " Pickens, James E 0 siblings, 1 reply; 18+ messages in thread From: Junio C Hamano @ 2009-07-29 18:31 UTC (permalink / raw) To: Pickens, James E; +Cc: git@vger.kernel.org, Kjetil Barvik, Michael J Gruber "Pickens, James E" <james.e.pickens@intel.com> writes: > This test creates two directories, a/b and a/b-2, then replaces a/b with > a symlink to a/b-2, then merges that change into another branch that > contains an unrelated change. Thanks. > Note that using the resolve strategy instead of recursive makes the > second bug go away, but not the third one. It is better to have separate tests for documentation purposes to help people who track down the breakage in such a case. > +test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' > + test -f a/b-2/c/d > +' > + > +test_expect_success 'clean the work tree and do the merge' ' > + git reset --hard && > + test -f a/b-2/c/d && > + echo x > a/x && > + git add a/x && > + git commit -m x && > + git merge master > +' > + > +test_expect_failure 'merge should not have deleted a/b-2/c/d' ' > + test -f a/b-2/c/d > +' So... test_expect_success 'setup for merge test' ' ... git commit -m x && git tag baseline ' test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' ' git reset --hard && git checkout baseline^0 && git merge -s resolve master ' test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' ' git reset --hard && git checkout baseline^0 && git merge -s recursive master ' Likewise for the other one. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 18:31 ` Junio C Hamano @ 2009-07-29 21:02 ` Pickens, James E 2009-07-29 22:08 ` Linus Torvalds 0 siblings, 1 reply; 18+ messages in thread From: Pickens, James E @ 2009-07-29 21:02 UTC (permalink / raw) To: Junio C Hamano; +Cc: git@vger.kernel.org, Kjetil Barvik, Michael J Gruber This test creates two directories, a/b and a/b-2, then replaces a/b with a symlink to a/b-2, then merges that change into the 'baseline' commit, which contains an unrelated change. There are two bugs: 1. 'git checkout' incorrectly deletes work tree file a/b-2/d. 2. 'git merge' incorrectly deletes work tree file a/b-2/d. The test goes on to create another branch in which a/b-2 is replaced with a symlink to a/b (i.e., the reverse of what was done the first time), and merge it into the 'baseline' commit. There is a different bug: 3. The merge should be clean, but git reports a conflict. Signed-off-by: James Pickens <james.e.pickens@intel.com> --- Ok, one more try. I added Junio's latest feedback, and also added more checks for correct merge results after each merge. For the merges that incorrectly report conflicts, those checks won't be executed since the conflict stops the test. If/when the bug causing the merge conflict is fixed, it will become important to check the merge results, so those checks might as well be there from the beginning. t/t6035-merge-dir-to-symlink.sh | 76 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 76 insertions(+), 0 deletions(-) create mode 100755 t/t6035-merge-dir-to-symlink.sh diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh new file mode 100755 index 0000000..89e8e6a --- /dev/null +++ b/t/t6035-merge-dir-to-symlink.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +test_description='merging when a directory was replaced with a symlink' +. ./test-lib.sh + +test_expect_success 'create a commit where dir a/b changed to symlink' ' + mkdir -p a/b/c a/b-2/c && + > a/b/c/d && + > a/b-2/c/d && + > a/x && + git add -A && + git commit -m base && + git tag start && + rm -rf a/b && + ln -s b-2 a/b && + git add -A && + git commit -m "dir to symlink" && + git checkout start^0 +' + +test_expect_failure 'checkout should not have deleted a/b-2/c/d' ' + test -f a/b-2/c/d +' + +test_expect_success 'setup for merge test' ' + git reset --hard && + test -f a/b-2/c/d && + echo x > a/x && + git add a/x && + git commit -m x && + git tag baseline +' + +test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s resolve master && + test -h a/b && + test -f a/b-2/c/d +' + +test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s recursive master && + test -h a/b && + test -f a/b-2/c/d +' + +test_expect_success 'setup a merge where dir a/b-2 changed to symlink' ' + git reset --hard && + git checkout start^0 && + rm -rf a/b-2 && + ln -s b a/b-2 && + git add -A && + git commit -m "dir a/b-2 to symlink" && + git tag test2 +' + +test_expect_failure 'merge should not have conflicts (resolve)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s resolve test2 && + test -h a/b-2 && + test -f a/b/c/d +' + +test_expect_failure 'merge should not have conflicts (recursive)' ' + git reset --hard && + git checkout baseline^0 && + git merge -s recursive test2 && + test -h a/b-2 && + test -f a/b/c/d +' + +test_done -- 1.6.2.5 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 21:02 ` [PATCH v3] " Pickens, James E @ 2009-07-29 22:08 ` Linus Torvalds 2009-07-29 22:44 ` Junio C Hamano ` (4 more replies) 0 siblings, 5 replies; 18+ messages in thread From: Linus Torvalds @ 2009-07-29 22:08 UTC (permalink / raw) To: Pickens, James E Cc: Junio C Hamano, git@vger.kernel.org, Kjetil Barvik, Michael J Gruber On Wed, 29 Jul 2009, Pickens, James E wrote: > > This test creates two directories, a/b and a/b-2, then replaces a/b with > a symlink to a/b-2, then merges that change into the 'baseline' commit, > which contains an unrelated change. Great tests. This patch should fix the 'checkout' issue. I made it use a new generic helper function ("check_path()"), since there are other cases like this that use just 'lstat()', and I bet we want to change that. The 'merge' issue is different, though: it's not due to a blind 'lstat()', but due to a blind 'unlink()' done by 'remove_path()'. I think 'remove_path()' should be taught to look for symlinks, and remove just the symlink - but that's a bit more work, especially since the symlink cache doesn't seem to expose any way to get the "what is the first symlink path" information. Kjetil, can you look at that? Linus --- cache.h | 3 +++ entry.c | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletions(-) diff --git a/cache.h b/cache.h index e6c7f33..9222774 100644 --- a/cache.h +++ b/cache.h @@ -468,6 +468,9 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); +/* "careful lstat()" */ +extern int check_path(const char *path, int len, struct stat *st); + #define REFRESH_REALLY 0x0001 /* ignore_valid */ #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ diff --git a/entry.c b/entry.c index d3e86c7..f276cf3 100644 --- a/entry.c +++ b/entry.c @@ -175,6 +175,19 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout return 0; } +/* + * This is like 'lstat()', except it refuses to follow symlinks + * in the path. + */ +int check_path(const char *path, int len, struct stat *st) +{ + if (has_symlink_leading_path(path, len)) { + errno = ENOENT; + return -1; + } + return lstat(path, st); +} + int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) { static char path[PATH_MAX + 1]; @@ -188,7 +201,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); len += ce_namelen(ce); - if (!lstat(path, &st)) { + if (!check_path(path, len, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 22:08 ` Linus Torvalds @ 2009-07-29 22:44 ` Junio C Hamano 2009-07-29 23:01 ` Kjetil Barvik ` (3 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: Junio C Hamano @ 2009-07-29 22:44 UTC (permalink / raw) To: Linus Torvalds Cc: Pickens, James E, Junio C Hamano, git@vger.kernel.org, Kjetil Barvik, Michael J Gruber Linus Torvalds <torvalds@linux-foundation.org> writes: > On Wed, 29 Jul 2009, Pickens, James E wrote: >> >> This test creates two directories, a/b and a/b-2, then replaces a/b with >> a symlink to a/b-2, then merges that change into the 'baseline' commit, >> which contains an unrelated change. > > Great tests. > > This patch should fix the 'checkout' issue. Thanks. I've queued v2 with local fixes and Kjetil's earlier fix in 'pu' that updates has_symlink_leading_path() breakage. Will take a look at your patch (and v3 test) later. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 22:08 ` Linus Torvalds 2009-07-29 22:44 ` Junio C Hamano @ 2009-07-29 23:01 ` Kjetil Barvik 2009-07-29 23:58 ` Linus Torvalds ` (2 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: Kjetil Barvik @ 2009-07-29 23:01 UTC (permalink / raw) To: Linus Torvalds Cc: Pickens, James E, Junio C Hamano, git@vger.kernel.org, Michael J Gruber Linus Torvalds <torvalds@linux-foundation.org> writes: > On Wed, 29 Jul 2009, Pickens, James E wrote: >> >> This test creates two directories, a/b and a/b-2, then replaces a/b with >> a symlink to a/b-2, then merges that change into the 'baseline' commit, >> which contains an unrelated change. > > Great tests. > > This patch should fix the 'checkout' issue. > > I made it use a new generic helper function ("check_path()"), since there > are other cases like this that use just 'lstat()', and I bet we want to > change that. > > The 'merge' issue is different, though: it's not due to a blind 'lstat()', > but due to a blind 'unlink()' done by 'remove_path()'. I think > 'remove_path()' should be taught to look for symlinks, and remove just the > symlink - but that's a bit more work, especially since the symlink cache > doesn't seem to expose any way to get the "what is the first symlink path" > information. > > Kjetil, can you look at that? Yes, I will take a look. Also, on all the other mails CC'ed to me today. Give me a cople of days. Sorry, I do not work at "full normal speed" for the moment. But, I will try to my best. -- kjetil ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 22:08 ` Linus Torvalds 2009-07-29 22:44 ` Junio C Hamano 2009-07-29 23:01 ` Kjetil Barvik @ 2009-07-29 23:58 ` Linus Torvalds 2009-07-30 1:06 ` Linus Torvalds 2009-07-30 3:26 ` Junio C Hamano 2009-07-30 6:05 ` Junio C Hamano 4 siblings, 1 reply; 18+ messages in thread From: Linus Torvalds @ 2009-07-29 23:58 UTC (permalink / raw) To: Pickens, James E Cc: Junio C Hamano, git@vger.kernel.org, Kjetil Barvik, Michael J Gruber On Wed, 29 Jul 2009, Linus Torvalds wrote: > > The 'merge' issue is different, though: it's not due to a blind 'lstat()', > but due to a blind 'unlink()' done by 'remove_path()'. I think > 'remove_path()' should be taught to look for symlinks, and remove just the > symlink - but that's a bit more work, especially since the symlink cache > doesn't seem to expose any way to get the "what is the first symlink path" > information. > > Kjetil, can you look at that? Hmm... This looks like it should do it. It doesn't make the test _pass_ (we don't seem to be creating a/b-2/c/d properly - I haven't checked why yet, but I suspect it is becasue we think it already exists due to the symlinked version lstat'ing fine), but it seems to do the right thing. Linus --- dir.c | 20 -------------------- symlinks.c | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/dir.c b/dir.c index e05b850..2204826 100644 --- a/dir.c +++ b/dir.c @@ -911,23 +911,3 @@ void setup_standard_excludes(struct dir_struct *dir) if (excludes_file && !access(excludes_file, R_OK)) add_excludes_from_file(dir, excludes_file); } - -int remove_path(const char *name) -{ - char *slash; - - if (unlink(name) && errno != ENOENT) - return -1; - - slash = strrchr(name, '/'); - if (slash) { - char *dirs = xstrdup(name); - slash = dirs + (slash - name); - do { - *slash = '\0'; - } while (rmdir(dirs) && (slash = strrchr(dirs, '/'))); - free(dirs); - } - return 0; -} - diff --git a/symlinks.c b/symlinks.c index 4bdded3..349c8d5 100644 --- a/symlinks.c +++ b/symlinks.c @@ -306,3 +306,29 @@ void remove_scheduled_dirs(void) { do_remove_scheduled_dirs(0); } + +int remove_path(const char *name) +{ + char *slash; + + /* + * If we have a leading symlink, we remove + * just the symlink! + */ + if (has_symlink_leading_path(name, strlen(name))) + name = default_cache.path; + + if (unlink(name) && errno != ENOENT) + return -1; + + slash = strrchr(name, '/'); + if (slash) { + char *dirs = xstrdup(name); + slash = dirs + (slash - name); + do { + *slash = '\0'; + } while (rmdir(dirs) && (slash = strrchr(dirs, '/'))); + free(dirs); + } + return 0; +} ^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 23:58 ` Linus Torvalds @ 2009-07-30 1:06 ` Linus Torvalds 0 siblings, 0 replies; 18+ messages in thread From: Linus Torvalds @ 2009-07-30 1:06 UTC (permalink / raw) To: Pickens, James E Cc: Junio C Hamano, git@vger.kernel.org, Kjetil Barvik, Michael J Gruber On Wed, 29 Jul 2009, Linus Torvalds wrote: > > Hmm... This looks like it should do it. > > It doesn't make the test _pass_ (we don't seem to be creating a/b-2/c/d > properly - I haven't checked why yet, but I suspect it is becasue we think > it already exists due to the symlinked version lstat'ing fine), but it > seems to do the right thing. Never mind. The patch does what I set out to do, but it's not relevant for the problem. What happens is: - we remove a/b/c/d to make room for the a/b symlink: merge_trees -> git_merge_trees -> check_updates -> checkout_entry -> remove_subtree("a/b") -> recursive rm This is correct - then we create the a/b symlink to a/b2 merge_trees -> git_merge_trees -> check_updates -> checkout_entry -> write_entry -> symlink This is correct - Then we remove 'a/b/c/d' again for the 'unmerged_cache()' case: merge_trees -> process_entry -> remove_file because th eprocess_entry() will decide that the original tree had that "a/b/c/d" file (true) that needs to be removed (false - we already did that as part of creating "a/b"). Annoying. Linus ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 22:08 ` Linus Torvalds ` (2 preceding siblings ...) 2009-07-29 23:58 ` Linus Torvalds @ 2009-07-30 3:26 ` Junio C Hamano 2009-07-30 6:05 ` Junio C Hamano 4 siblings, 0 replies; 18+ messages in thread From: Junio C Hamano @ 2009-07-30 3:26 UTC (permalink / raw) To: Linus Torvalds Cc: Pickens, James E, Junio C Hamano, git@vger.kernel.org, Kjetil Barvik, Michael J Gruber Linus Torvalds <torvalds@linux-foundation.org> writes: > This patch should fix the 'checkout' issue. > > I made it use a new generic helper function ("check_path()"), since there > are other cases like this that use just 'lstat()', and I bet we want to > change that. > > The 'merge' issue is different, though: it's not due to a blind 'lstat()', > but due to a blind 'unlink()' done by 'remove_path()'. I think > 'remove_path()' should be taught to look for symlinks, and remove just the > symlink - but that's a bit more work, especially since the symlink cache > doesn't seem to expose any way to get the "what is the first symlink path" > information. This is a good thing to do, but the James's "checkout" test fails for an unrelated reason. The tree has 120000 blob a36b773 a/b -> b-2 100644 blob e69de29 a/b-2/c/d 100644 blob e69de29 a/x checked out, and wants to switch to 100644 blob e69de29 a/b-2/c/d 100644 blob e69de29 a/b/c/d 100644 blob e69de29 a/x checkout_entry() is called to check out "a/b/c/d". If "a/b" symlink were still there, the lstat() you fixed will be fooled. But in James's test, because the symlink "a/b" is tracked in the switched-from commit and is being obliterated by switching to a tree that has a directory there, we (should) have called deleted_entry() on a/b to mark it for removal, and inside check_updates() before going into the loop to call checkout_entry(), we would have already removed the symlink "a/b" that is going away inside unlink_entry(). The problem is that has_symlink_or_noent_leading_path() called from there lies, without Kjetil's fix c52dc70 (lstat_cache: guard against full match of length of 'name' parameter, 2009-06-14) that is in 'pu'. If the original tree in the test did not have "a/b" tracked, but has an untracked symlink "a/b" that points at b-2, then "a/b" will stay in the work tree when the codepath your patch touches is reached, and the problem will be demonstrated. Your patch will fix that issue. So both fixes are necessary, and we need a separate test to illustrate what your patch fixes. I'll push out some updates to do so. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v3] Demonstrate bugs when a directory is replaced with a symlink 2009-07-29 22:08 ` Linus Torvalds ` (3 preceding siblings ...) 2009-07-30 3:26 ` Junio C Hamano @ 2009-07-30 6:05 ` Junio C Hamano 4 siblings, 0 replies; 18+ messages in thread From: Junio C Hamano @ 2009-07-30 6:05 UTC (permalink / raw) To: Linus Torvalds Cc: Pickens, James E, git@vger.kernel.org, Kjetil Barvik, Michael J Gruber Linus Torvalds <torvalds@linux-foundation.org> writes: > This patch should fix the 'checkout' issue. > > I made it use a new generic helper function ("check_path()"), since there > are other cases like this that use just 'lstat()', and I bet we want to > change that. > > The 'merge' issue is different, though: it's not due to a blind 'lstat()', > but due to a blind 'unlink()' done by 'remove_path()'. I think > 'remove_path()' should be taught to look for symlinks, and remove just the > symlink - but that's a bit more work, especially since the symlink cache > doesn't seem to expose any way to get the "what is the first symlink path" > information. I think this is a good thing to do regardless, but the James's "checkout" test fails for an unrelated reason. The tree has 120000 blob a36b773 a/b -> b-2 100644 blob e69de29 a/b-2/c/d 100644 blob e69de29 a/x checked out, and wants to switch to 100644 blob e69de29 a/b-2/c/d 100644 blob e69de29 a/b/c/d 100644 blob e69de29 a/x checkout_entry() is called to check out "a/b/c/d". If "a/b" symlink were still there, the lstat() you fixed will be fooled. But in James's test, because the symlink "a/b" is tracked in the switched-from commit and is being obliterated by switching to a tree that has a directory there, we (should) have called deleted_entry() on a/b to mark it for removal, and inside check_updates() before going into the loop to call checkout_entry(), we would have already removed the symlink "a/b" that is going away inside unlink_entry(). The problem is that has_symlink_or_noent_leading_path() called from there lies, without Kjetil's fix c52dc70 (lstat_cache: guard against full match of length of 'name' parameter, 2009-06-14) that is in 'pu'. If the original tree in the test did not have "a/b" tracked, but has an untracked symlink "a/b" that points at b-2, then "a/b" will stay in the work tree when the codepath your patch touches is reached, and the problem will be demonstrated. Your patch will fix that issue. So both fixes are necessary, and we need a separate test to illustrate what your patch fixes. I'll push out some updates. ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2009-07-30 6:05 UTC | newest] Thread overview: 18+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-07-28 22:13 More symlink/directory troubles James Pickens 2009-07-28 22:13 ` [PATCH 1/2] Demonstrate bugs when a directory is replaced with a symlink James Pickens 2009-07-28 22:13 ` [PATCH 2/2] Demonstrate merge failure " James Pickens 2009-07-29 8:29 ` Michael J Gruber 2009-07-29 16:39 ` Pickens, James E 2009-07-29 8:19 ` [PATCH 1/2] Demonstrate bugs " Michael J Gruber 2009-07-29 8:33 ` Junio C Hamano 2009-07-29 16:57 ` Pickens, James E 2009-07-29 17:48 ` [PATCH v2] " Pickens, James E 2009-07-29 18:31 ` Junio C Hamano 2009-07-29 21:02 ` [PATCH v3] " Pickens, James E 2009-07-29 22:08 ` Linus Torvalds 2009-07-29 22:44 ` Junio C Hamano 2009-07-29 23:01 ` Kjetil Barvik 2009-07-29 23:58 ` Linus Torvalds 2009-07-30 1:06 ` Linus Torvalds 2009-07-30 3:26 ` Junio C Hamano 2009-07-30 6:05 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).