Johannes Schindelin  committed 7f3140c

git repack: keep commits hidden by a graft

When you have grafts that pretend that a given commit has different
parents than the ones recorded in the commit object, it is dangerous
to let 'git repack' remove those hidden parents, as you can easily
remove the graft and end up with a broken repository.

So let's play it safe and keep those parent objects and everything
that is reachable by them, in addition to the grafted parents.

As this behavior can only be triggered by git pack-objects, and as that
command handles duplicate parents gracefully, we do not bother to cull
duplicated parents that may result by using both true and grafted

Signed-off-by: Johannes Schindelin <>
Signed-off-by: Junio C Hamano <>

  • Participants
  • Parent commits 1ec6482

Comments (0)

Files changed (7)

File Documentation/git-pack-objects.txt

 'git pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
 	[--local] [--incremental] [--window=N] [--depth=N] [--all-progress]
-	[--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
+	[--revs [--unpacked | --all]*] [--stdout | base-name]
+	[--keep-true-parents] < object-list
 	to force the version for the generated pack index, and to force
 	64-bit index entries on objects located above the given offset.
+	With this option, parents that are hidden by grafts are packed
+	nevertheless.

File builtin-pack-objects.c

 				die("bad %s", arg);
+		if (!strcmp(arg, "--keep-true-parents")) {
+			grafts_replace_parents = 0;
+			continue;
+		}
 extern enum object_creation_mode object_creation_mode;
+extern int grafts_replace_parents;
 extern int repository_format_version;
 extern int check_repository_format(void);
 		    bufptr[47] != '\n')
 			return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
 		bufptr += 48;
-		if (graft)
+		/*
+		 * The clone is shallow if nr_parent < 0, and we must
+		 * not traverse its real parents even when we unhide them.
+		 */
+		if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
 		new_parent = lookup_commit(parent);
 		if (new_parent)

File environment.c

 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
+int grafts_replace_parents = 1;
 /* Parallel index stat data preload? */
 int core_preload_index = 0;


 args="$args $local $quiet $no_reuse$extra"
-names=$(git pack-objects --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
 	exit 1
 if [ -z "$names" ]; then
 	if test -z "$quiet"; then

File t/

 	test_must_fail git show $csha1
-test_expect_failure 'objects made unreachable by grafts only are kept' '
+test_expect_success 'objects made unreachable by grafts only are kept' '
 	test_tick &&
 	git commit --allow-empty -m "commit 4" &&
 	H0=$(git rev-parse HEAD) &&