Merge branch 'jk/write-broken-index-with-nul-sha1'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:40:27 +0000 (11:40 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:40:27 +0000 (11:40 -0700)
Earlier we started rejecting an attempt to add 0{40} object name to
the index and to tree objects, but it sometimes is necessary to
allow so to be able to use tools like filter-branch to correct such
broken tree objects.

* jk/write-broken-index-with-nul-sha1:
write_index: optionally allow broken null sha1s

git-filter-branch.sh
read-cache.c
t/t7009-filter-branch-null-sha1.sh [new file with mode: 0755]
index ac2a005fdb23c48d8451188ffd7b1c8194b0295f..98e8fe43d2fdde1b76e8f10f4c9473e23c8f347c 100755 (executable)
@@ -283,11 +283,12 @@ while read commit parents; do
 
        case "$filter_subdir" in
        "")
-               git read-tree -i -m $commit
+               GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
                ;;
        *)
                # The commit may not have the subdirectory at all
-               err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
+               err=$(GIT_ALLOW_NULL_SHA1=1 \
+                     git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
                        if ! git rev-parse -q --verify $commit:"$filter_subdir"
                        then
                                rm -f "$GIT_INDEX_FILE"
index 885943a6df77da646481c34d2876d7fdaa5c12b8..6bbe1b1fb3435247e1114cbf496cd9d7640f3def 100644 (file)
@@ -1818,8 +1818,17 @@ int write_index(struct index_state *istate, int newfd)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
-               if (is_null_sha1(ce->sha1))
-                       return error("cache entry has null sha1: %s", ce->name);
+               if (is_null_sha1(ce->sha1)) {
+                       static const char msg[] = "cache entry has null sha1: %s";
+                       static int allow = -1;
+
+                       if (allow < 0)
+                               allow = git_env_bool("GIT_ALLOW_NULL_SHA1", 0);
+                       if (allow)
+                               warning(msg, ce->name);
+                       else
+                               return error(msg, ce->name);
+               }
                if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh
new file mode 100755 (executable)
index 0000000..a997f7a
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='filter-branch removal of trees with null sha1'
+. ./test-lib.sh
+
+test_expect_success 'setup: base commits' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three
+'
+
+test_expect_success 'setup: a commit with a bogus null sha1 in the tree' '
+       {
+               git ls-tree HEAD &&
+               printf "160000 commit $_z40\\tbroken\\n"
+       } >broken-tree
+       echo "add broken entry" >msg &&
+
+       tree=$(git mktree <broken-tree) &&
+       test_tick &&
+       commit=$(git commit-tree $tree -p HEAD <msg) &&
+       git update-ref HEAD "$commit"
+'
+
+# we have to make one more commit on top removing the broken
+# entry, since otherwise our index does not match HEAD (and filter-branch will
+# complain). We could make the index match HEAD, but doing so would involve
+# writing a null sha1 into the index.
+test_expect_success 'setup: bring HEAD and index in sync' '
+       test_tick &&
+       git commit -a -m "back to normal"
+'
+
+test_expect_success 'filter commands are still checked' '
+       test_must_fail git filter-branch \
+               --force --prune-empty \
+               --index-filter "git rm --cached --ignore-unmatch three.t"
+'
+
+test_expect_success 'removing the broken entry works' '
+       echo three >expect &&
+       git filter-branch \
+               --force --prune-empty \
+               --index-filter "git rm --cached --ignore-unmatch broken" &&
+       git log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_done