git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Incremental use of fast-import may cause conflicting notes
@ 2011-11-23 12:09 Henrik Grubbström
  2011-11-23 12:10 ` Henrik Grubbström
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Henrik Grubbström @ 2011-11-23 12:09 UTC (permalink / raw)
  To: Git Mailing list; +Cc: Johan Herland, Jonathan Nieder

[-- Attachment #1: Type: TEXT/PLAIN, Size: 1318 bytes --]

Hi.

Background: I have an incremental repository-walker creating a 
corresponding documentation repository from a source repository
that uses git-notes to store its state, a use for which notes
seem very suitable.

Problem: When the number of notes in the root of the notes branch
increases beyond a threshold, fast-import changes the fanout. This 
is as designed, but the problem is that when fast-import is restarted
it won't remember the fanout, and will start writing files in the root 
again. This means that there may be multiple notes-files for the same 
commit, eg both de/adbeef and deadbeef.

This is not what the user expects, and is not good practice, even if it in 
this case actually works, since the latter is defined to have priority.
I'm however not sure if eg fast_import.c:do_change_note_fanout() will do 
the right thing if/when the fanout is changed again.

The problem is probably due to b->num_notes not being initialized properly 
when the old non-empty root commit for the notes branch is loaded in 
parse_from()/parse_new_commit().

My workaround for now is to use filedeleteall and restore all the notes
by hand in the first new commit on the notes branch.

Version of git: 1.7.6.4 (gentoo)

Thanks,

--
Henrik Grubbström					grubba@grubba.org
Roxen Internet Software AB				grubba@roxen.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Incremental use of fast-import may cause conflicting notes
  2011-11-23 12:09 Incremental use of fast-import may cause conflicting notes Henrik Grubbström
@ 2011-11-23 12:10 ` Henrik Grubbström
  2011-11-24 23:09 ` Jonathan Nieder
  2011-11-25  0:09 ` [RFC/PATCH 0/3] fast-import: Fix incremental use of notes Johan Herland
  2 siblings, 0 replies; 7+ messages in thread
From: Henrik Grubbström @ 2011-11-23 12:10 UTC (permalink / raw)
  To: Git Mailing list; +Cc: Johan Herland, Jonathan Nieder

[-- Attachment #1: Type: TEXT/PLAIN, Size: 1437 bytes --]

On Wed, 23 Nov 2011, Henrik Grubbström wrote:

> Hi.
>
> Background: I have an incremental repository-walker creating a corresponding 
> documentation repository from a source repository
> that uses git-notes to store its state, a use for which notes
> seem very suitable.
>
> Problem: When the number of notes in the root of the notes branch
> increases beyond a threshold, fast-import changes the fanout. This is as 
> designed, but the problem is that when fast-import is restarted
> it won't remember the fanout, and will start writing files in the root again. 
> This means that there may be multiple notes-files for the same commit, eg 
> both de/adbeef and deadbeef.
>
> This is not what the user expects, and is not good practice, even if it in 
> this case actually works, since the latter is defined to have priority.
> I'm however not sure if eg fast_import.c:do_change_note_fanout() will do the 
> right thing if/when the fanout is changed again.
>
> The problem is probably due to b->num_notes not being initialized properly 
> when the old non-empty root commit for the notes branch is loaded in 
> parse_from()/parse_new_commit().
>
> My workaround for now is to use filedeleteall and restore all the notes
> by hand in the first new commit on the notes branch.
>
> Version of git: 1.7.6.4 (gentoo)

Oops, wrong machine... 1.7.8.rc3 (gentoo)

> Thanks,

--
Henrik Grubbström					grubba@roxen.com
Roxen Internet Software AB

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Incremental use of fast-import may cause conflicting notes
  2011-11-23 12:09 Incremental use of fast-import may cause conflicting notes Henrik Grubbström
  2011-11-23 12:10 ` Henrik Grubbström
@ 2011-11-24 23:09 ` Jonathan Nieder
  2011-11-25  0:09 ` [RFC/PATCH 0/3] fast-import: Fix incremental use of notes Johan Herland
  2 siblings, 0 replies; 7+ messages in thread
From: Jonathan Nieder @ 2011-11-24 23:09 UTC (permalink / raw)
  To: Henrik Grubbström; +Cc: Git Mailing list, Johan Herland, David Barr

Hi Henrik,

Henrik Grubbström wrote:

> Background: I have an incremental repository-walker creating a corresponding
> documentation repository from a source repository
> that uses git-notes to store its state, a use for which notes
> seem very suitable.

Nice.

[...]
> when fast-import is restarted
> it won't remember the fanout, and will start writing files in the root
> again. This means that there may be multiple notes-files for the same
> commit, eg both de/adbeef and deadbeef.
[...]
> The problem is probably due to b->num_notes not being initialized properly
> when the old non-empty root commit for the notes branch is loaded in
> parse_from()/parse_new_commit().

Sounds like a bug.  Can you suggest a reproduction recipe (ideally as
a patch to t/t9301-fast-import-notes.sh), a fix, or both?

Thanks.

Regards,
Jonathan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC/PATCH 0/3] fast-import: Fix incremental use of notes
  2011-11-23 12:09 Incremental use of fast-import may cause conflicting notes Henrik Grubbström
  2011-11-23 12:10 ` Henrik Grubbström
  2011-11-24 23:09 ` Jonathan Nieder
@ 2011-11-25  0:09 ` Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 1/3] t9301: Fix testcase covering up a bug in fast-import's notes fanout handling Johan Herland
                     ` (2 more replies)
  2 siblings, 3 replies; 7+ messages in thread
From: Johan Herland @ 2011-11-25  0:09 UTC (permalink / raw)
  To: grubba; +Cc: git, jrnieder, johan

Hi,

This is a first attempt at fixing the bug reported by Henrik.

The first two patches provide testcases for the bug, and the last patch
provides a fix.

Have fun! :)

...Johan


Johan Herland (3):
  t9301: Fix testcase covering up a bug in fast-import's notes fanout handling
  t9301: Add 2nd testcase exposing bugs in fast-import's notes fanout handling
  fast-import: Fix incorrect fanout level when modifying existing notes refs

 fast-import.c                |   28 ++++++++++++++++--
 t/t9301-fast-import-notes.sh |   63 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 83 insertions(+), 8 deletions(-)

-- 
1.7.5.rc1.3.g4d7b

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC/PATCH 1/3] t9301: Fix testcase covering up a bug in fast-import's notes fanout handling
  2011-11-25  0:09 ` [RFC/PATCH 0/3] fast-import: Fix incremental use of notes Johan Herland
@ 2011-11-25  0:09   ` Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 2/3] t9301: Add 2nd testcase exposing bugs " Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 3/3] fast-import: Fix incorrect fanout level when modifying existing notes refs Johan Herland
  2 siblings, 0 replies; 7+ messages in thread
From: Johan Herland @ 2011-11-25  0:09 UTC (permalink / raw)
  To: grubba; +Cc: git, jrnieder, johan

There is a bug in fast-import where the fanout levels of an existing notes
tree being loaded into the fast-import machinery is disregarded. Instead, any
tree loaded is assumed to have a fanout level of 0. If the true fanout level
is deeper, any attempt to remove a note from that tree will silently fail
(as the note will not be found at fanout level 0).

However, this bug was covered up by the way in which the t9301 testcase was
written: When generating the fast-import commands to test mass removal of
notes, we appended these commands to an already existing 'input' file which
happened to already contain the fast-import commands used in the previous
subtest to generate the very same notes tree. This would normally be harmless
(but suboptimal) as the notes created were identical to the notes already
present in the notes tree. But the act of repeating all the notes additions
caused the internal fast-import data structures to recalculate the fanout,
instead of hanging on to the initial (incorrect) fanout (that causes the bug
described above). Thus, the subsequent removal of notes in the same 'input'
file would succeed, thereby covering up the bug described above.

This patch creates a new 'input' file instead of appending to the file from
the previous subtest. Thus, we end up properly testing removal of notes that
were added by a previous fast-import command. As a side effect, the notes
removal can no longer refer to commits using the marks set by the previous
fast-import run, instead the commits names must be referenced directly.

The underlying fast-import bug is still present after this patch, but now we
have at least uncovered it. Therefore, the affected subtests are labeled as
expected failures until the underlying bug is fixed.

Signed-off-by: Johan Herland <johan@herland.net>
---
 t/t9301-fast-import-notes.sh |   13 ++++++-------
 1 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index 463254c..fd08161 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -507,7 +507,7 @@ test_expect_success 'verify that non-notes are untouched by a fanout change' '
 '
 remaining_notes=10
 test_tick
-cat >>input <<INPUT_END
+cat >input <<INPUT_END
 commit refs/notes/many_notes
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
@@ -516,12 +516,11 @@ COMMIT
 from refs/notes/many_notes^0
 INPUT_END
 
-i=$remaining_notes
-while test $i -lt $num_commits
+i=$(($num_commits - $remaining_notes))
+for sha1 in $(git rev-list -n $i refs/heads/many_commits)
 do
-	i=$(($i + 1))
 	cat >>input <<INPUT_END
-N 0000000000000000000000000000000000000000 :$i
+N 0000000000000000000000000000000000000000 $sha1
 INPUT_END
 done
 
@@ -541,7 +540,7 @@ EXPECT_END
 	i=$(($i - 1))
 done
 
-test_expect_success 'remove lots of notes' '
+test_expect_failure 'remove lots of notes' '
 
 	git fast-import <input &&
 	GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
@@ -550,7 +549,7 @@ test_expect_success 'remove lots of notes' '
 
 '
 
-test_expect_success 'verify that removing notes trigger fanout consolidation' '
+test_expect_failure 'verify that removing notes trigger fanout consolidation' '
 
 	# All entries in the top-level notes tree should be a full SHA1
 	git ls-tree --name-only -r refs/notes/many_notes |
-- 
1.7.5.rc1.3.g4d7b

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC/PATCH 2/3] t9301: Add 2nd testcase exposing bugs in fast-import's notes fanout handling
  2011-11-25  0:09 ` [RFC/PATCH 0/3] fast-import: Fix incremental use of notes Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 1/3] t9301: Fix testcase covering up a bug in fast-import's notes fanout handling Johan Herland
@ 2011-11-25  0:09   ` Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 3/3] fast-import: Fix incorrect fanout level when modifying existing notes refs Johan Herland
  2 siblings, 0 replies; 7+ messages in thread
From: Johan Herland @ 2011-11-25  0:09 UTC (permalink / raw)
  To: grubba; +Cc: git, jrnieder, johan

The previous patch exposed a bug in fast-import where _removing_ an existing
note fails (when that note resides on a non-zero fanout level, and was added
prior to this fast-import run).

This patch demostrates the same issue when _changing_ an existing note
(subject to the same circumstances).

Discovered-by: Henrik Grubbström <grubba@roxen.com>
Signed-off-by: Johan Herland <johan@herland.net>
---
 t/t9301-fast-import-notes.sh |   54 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 54 insertions(+), 0 deletions(-)

diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index fd08161..57d85a6 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -505,6 +505,60 @@ test_expect_success 'verify that non-notes are untouched by a fanout change' '
 	test_cmp expect_non-note3 actual
 
 '
+
+# Change the notes for the three top commits
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+changing notes for the top three commits
+COMMIT
+from refs/notes/many_notes^0
+INPUT_END
+
+rm expect
+i=$num_commits
+j=0
+while test $j -lt 3
+do
+	cat >>input <<INPUT_END
+N inline refs/heads/many_commits~$j
+data <<EOF
+changed note for commit #$i
+EOF
+INPUT_END
+	cat >>expect <<EXPECT_END
+    commit #$i
+    changed note for commit #$i
+EXPECT_END
+	i=$(($i - 1))
+	j=$(($j + 1))
+done
+
+test_expect_failure 'change a few existing notes' '
+
+	git fast-import <input &&
+	GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
+	    grep "^    " > actual &&
+	test_cmp expect actual
+
+'
+
+test_expect_failure 'verify that changing notes respect existing fanout' '
+
+	# None of the entries in the top-level notes tree should be a full SHA1
+	git ls-tree --name-only refs/notes/many_notes |
+	while read path
+	do
+		if test $(expr length "$path") -ge 40
+		then
+			return 1
+		fi
+	done
+
+'
+
 remaining_notes=10
 test_tick
 cat >input <<INPUT_END
-- 
1.7.5.rc1.3.g4d7b

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC/PATCH 3/3] fast-import: Fix incorrect fanout level when modifying existing notes refs
  2011-11-25  0:09 ` [RFC/PATCH 0/3] fast-import: Fix incremental use of notes Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 1/3] t9301: Fix testcase covering up a bug in fast-import's notes fanout handling Johan Herland
  2011-11-25  0:09   ` [RFC/PATCH 2/3] t9301: Add 2nd testcase exposing bugs " Johan Herland
@ 2011-11-25  0:09   ` Johan Herland
  2 siblings, 0 replies; 7+ messages in thread
From: Johan Herland @ 2011-11-25  0:09 UTC (permalink / raw)
  To: grubba; +Cc: git, jrnieder, johan

This fixes the bug uncovered by the tests added in the previous two patches.

When an existing notes ref was loaded into the fast-import machinery, the
num_notes counter associated with that ref remained == 0, even though the
true number of notes in the loaded ref was higher. This caused a fanout
level of 0 to be used, although the actual fanout of the tree could be > 0.
Manipulating the notes tree at an incorrect fanout level causes removals to
silently fail, and modifications of existing notes to instead produce an
additional note (leaving the old object in place at a different fanout level).

This patch fixes the bug by explicitly counting the number of notes in the
notes tree whenever it looks like the num_notes counter could be wrong (when
num_notes == 0). There may be false positives (i.e. triggering the counting
when the notes tree is truly empty), but in those cases, the counting should
not take long.

Signed-off-by: Johan Herland <johan@herland.net>
---
 fast-import.c                |   28 +++++++++++++++++++++++++---
 t/t9301-fast-import-notes.sh |    8 ++++----
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/fast-import.c b/fast-import.c
index 8d8ea3c..f4bfe0f 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -2173,6 +2173,11 @@ static uintmax_t do_change_note_fanout(
 
 		if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) {
 			/* This is a note entry */
+			if (fanout == 0xff) {
+				/* Counting mode, no rename */
+				num_notes++;
+				continue;
+			}
 			construct_path_with_fanout(hex_sha1, fanout, realpath);
 			if (!strcmp(fullpath, realpath)) {
 				/* Note entry is in correct location */
@@ -2379,7 +2384,7 @@ static void file_change_cr(struct branch *b, int rename)
 		leaf.tree);
 }
 
-static void note_change_n(struct branch *b, unsigned char old_fanout)
+static void note_change_n(struct branch *b, unsigned char *old_fanout)
 {
 	const char *p = command_buf.buf + 2;
 	static struct strbuf uq = STRBUF_INIT;
@@ -2390,6 +2395,23 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
 	uint16_t inline_data = 0;
 	unsigned char new_fanout;
 
+	/*
+	 * When loading a branch, we don't traverse its tree to count the real
+	 * number of notes (too expensive to do this for all non-note refs).
+	 * This means that recently loaded notes refs might incorrectly have
+	 * b->num_notes == 0, and consequently, old_fanout might be wrong.
+	 *
+	 * Fix this by traversing the tree and counting the number of notes
+	 * when b->num_notes == 0. If the notes tree is truly empty, the
+	 * calculation should not take long.
+	 */
+	if (b->num_notes == 0 && *old_fanout == 0) {
+		/* Invoke change_note_fanout() in "counting mode". */
+		b->num_notes = change_note_fanout(&b->branch_tree, 0xff);
+		*old_fanout = convert_num_notes_to_fanout(b->num_notes);
+	}
+
+	/* Now parse the notemodify command. */
 	/* <dataref> or 'inline' */
 	if (*p == ':') {
 		char *x;
@@ -2450,7 +2472,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
 			    typename(type), command_buf.buf);
 	}
 
-	construct_path_with_fanout(sha1_to_hex(commit_sha1), old_fanout, path);
+	construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path);
 	if (tree_content_remove(&b->branch_tree, path, NULL))
 		b->num_notes--;
 
@@ -2637,7 +2659,7 @@ static void parse_new_commit(void)
 		else if (!prefixcmp(command_buf.buf, "C "))
 			file_change_cr(b, 0);
 		else if (!prefixcmp(command_buf.buf, "N "))
-			note_change_n(b, prev_fanout);
+			note_change_n(b, &prev_fanout);
 		else if (!strcmp("deleteall", command_buf.buf))
 			file_change_deleteall(b);
 		else if (!prefixcmp(command_buf.buf, "ls "))
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index 57d85a6..83acf68 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -536,7 +536,7 @@ EXPECT_END
 	j=$(($j + 1))
 done
 
-test_expect_failure 'change a few existing notes' '
+test_expect_success 'change a few existing notes' '
 
 	git fast-import <input &&
 	GIT_NOTES_REF=refs/notes/many_notes git log -n3 refs/heads/many_commits |
@@ -545,7 +545,7 @@ test_expect_failure 'change a few existing notes' '
 
 '
 
-test_expect_failure 'verify that changing notes respect existing fanout' '
+test_expect_success 'verify that changing notes respect existing fanout' '
 
 	# None of the entries in the top-level notes tree should be a full SHA1
 	git ls-tree --name-only refs/notes/many_notes |
@@ -594,7 +594,7 @@ EXPECT_END
 	i=$(($i - 1))
 done
 
-test_expect_failure 'remove lots of notes' '
+test_expect_success 'remove lots of notes' '
 
 	git fast-import <input &&
 	GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
@@ -603,7 +603,7 @@ test_expect_failure 'remove lots of notes' '
 
 '
 
-test_expect_failure 'verify that removing notes trigger fanout consolidation' '
+test_expect_success 'verify that removing notes trigger fanout consolidation' '
 
 	# All entries in the top-level notes tree should be a full SHA1
 	git ls-tree --name-only -r refs/notes/many_notes |
-- 
1.7.5.rc1.3.g4d7b

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-11-25  1:10 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-23 12:09 Incremental use of fast-import may cause conflicting notes Henrik Grubbström
2011-11-23 12:10 ` Henrik Grubbström
2011-11-24 23:09 ` Jonathan Nieder
2011-11-25  0:09 ` [RFC/PATCH 0/3] fast-import: Fix incremental use of notes Johan Herland
2011-11-25  0:09   ` [RFC/PATCH 1/3] t9301: Fix testcase covering up a bug in fast-import's notes fanout handling Johan Herland
2011-11-25  0:09   ` [RFC/PATCH 2/3] t9301: Add 2nd testcase exposing bugs " Johan Herland
2011-11-25  0:09   ` [RFC/PATCH 3/3] fast-import: Fix incorrect fanout level when modifying existing notes refs Johan Herland

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).