repack -ad: prune the list of shallow commits
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Wed, 24 Oct 2018 15:56:13 +0000 (08:56 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 25 Oct 2018 03:59:27 +0000 (12:59 +0900)
`git repack` can drop unreachable commits without further warning,
making the corresponding entries in `.git/shallow` invalid, which causes
serious problems when deepening the branches.

One scenario where unreachable commits are dropped by `git repack` is
when a `git fetch --prune` (or even a `git fetch` when a ref was
force-pushed in the meantime) can make a commit unreachable that was
reachable before.

Therefore it is not safe to assume that a `git repack -adlf` will keep
unreachable commits alone (under the assumption that they had not been
packed in the first place, which is an assumption at least some of Git's
code seems to make).

This is particularly important to keep in mind when looking at the
`.git/shallow` file: if any commits listed in that file become
unreachable, it is not a problem, but if they go missing, it *is* a
problem. One symptom of this problem is that a deepening fetch may now
fail with

fatal: error in object: unshallow <commit-hash>

To avoid this problem, let's prune the shallow list in `git repack` when
the `-d` option is passed, unless `-A` is passed, too (which would force
the now-unreachable objects to be turned into loose objects instead of
being deleted). Additionally, we also need to take `--keep-reachable`
and `--unpack-unreachable=<date>` into account.

Note: an alternative solution discussed during the review of this patch
was to teach `git fetch` to simply ignore entries in .git/shallow if the
corresponding commits do not exist locally. A quick test, however,
revealed that the .git/shallow file is written during a shallow *clone*,
in which case the commits do not exist, either, but the "shallow" line
*does* need to be sent. Therefore, this approach would be a lot more
finicky than the approach presented by the this patch.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/repack.c
t/t5537-fetch-shallow.sh
index d5886039cc6656609962fd522a27f61eda6cd0ec..3c30e16ec5483c2a88cbe96f67475746487b31a9 100644 (file)
@@ -531,6 +531,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                if (!po_args.quiet && isatty(2))
                        opts |= PRUNE_PACKED_VERBOSE;
                prune_packed_objects(opts);
+
+               if (!keep_unreachable &&
+                   (!(pack_everything & LOOSEN_UNREACHABLE) ||
+                    unpack_unreachable) &&
+                   is_repository_shallow(the_repository))
+                       prune_shallow(PRUNE_QUICK);
        }
 
        if (!no_update_server_info)
index 686fdc9280532c18cfd5f30595466d07a08e106e..6faf17e17a133f31245f89fd0fe9bad1687ac5fa 100755 (executable)
@@ -186,7 +186,7 @@ EOF
        test_cmp expect actual
 '
 
-test_expect_failure '.git/shallow is edited by repack' '
+test_expect_success '.git/shallow is edited by repack' '
        git init shallow-server &&
        test_commit -C shallow-server A &&
        test_commit -C shallow-server B &&