Merge branch 'tb/unexpected'
authorJunio C Hamano <gitster@pobox.com>
Wed, 8 May 2019 15:37:25 +0000 (00:37 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 8 May 2019 15:37:25 +0000 (00:37 +0900)
Code tightening against a "wrong" object appearing where an object
of a different type is expected, instead of blindly assuming that
the connection between objects are correctly made.

* tb/unexpected:
rev-list: detect broken root trees
rev-list: let traversal die when --missing is not in use
get_commit_tree(): return NULL for broken tree
list-objects.c: handle unexpected non-tree entries
list-objects.c: handle unexpected non-blob entries
t: introduce tests for unexpected object types
t: move 'hex2oct' into test-lib-functions.sh

builtin/rev-list.c
commit.c
list-objects.c
t/t1007-hash-object.sh
t/t1450-fsck.sh
t/t5601-clone.sh
t/t6102-rev-list-unexpected-objects.sh [new file with mode: 0755]
t/test-lib-functions.sh
index 425a5774db0697774df499fc16a626ab1e1efcf9..9f31837d303f1f302266fdd03298ddce73867ba0 100644 (file)
@@ -379,7 +379,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        repo_init_revisions(the_repository, &revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
-       revs.do_not_die_on_missing_tree = 1;
 
        /*
         * Scan the argument list before invoking setup_revisions(), so that we
@@ -409,6 +408,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (arg_missing_action)
+               revs.do_not_die_on_missing_tree = 1;
+
        argc = setup_revisions(argc, argv, &revs, &s_r_opt);
 
        memset(&info, 0, sizeof(info));
index a9e74647dc822606d06f79bcf94030b32998bd69..8fa1883c61c580578a755cdf2da009203d8d386e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -351,10 +351,10 @@ struct tree *repo_get_commit_tree(struct repository *r,
        if (commit->maybe_tree || !commit->object.parsed)
                return commit->maybe_tree;
 
-       if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
-               BUG("commit has NULL tree, but was not loaded from commit-graph");
+       if (commit->graph_pos != COMMIT_NOT_FROM_GRAPH)
+               return get_commit_tree_in_graph(r, commit);
 
-       return get_commit_tree_in_graph(r, commit);
+       return NULL;
 }
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
index dc77361e11d02a4eaa994f4351310a0d54d33ef6..b5651ddd5bfdd6cda4047f71cc3cb66fc31a793d 100644 (file)
@@ -125,6 +125,11 @@ static void process_tree_contents(struct traversal_context *ctx,
 
                if (S_ISDIR(entry.mode)) {
                        struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);
+                       if (!t) {
+                               die(_("entry '%s' in tree %s has tree mode, "
+                                     "but is not a tree"),
+                                   entry.path, oid_to_hex(&tree->object.oid));
+                       }
                        t->object.flags |= NOT_USER_GIVEN;
                        process_tree(ctx, t, base, entry.path);
                }
@@ -133,6 +138,11 @@ static void process_tree_contents(struct traversal_context *ctx,
                                        base, entry.path);
                else {
                        struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
+                       if (!b) {
+                               die(_("entry '%s' in tree %s has blob mode, "
+                                     "but is not a blob"),
+                                   entry.path, oid_to_hex(&tree->object.oid));
+                       }
                        b->object.flags |= NOT_USER_GIVEN;
                        process_blob(ctx, b, base, entry.path);
                }
@@ -364,6 +374,9 @@ static void do_traverse(struct traversal_context *ctx)
                        struct tree *tree = get_commit_tree(commit);
                        tree->object.flags |= NOT_USER_GIVEN;
                        add_pending_tree(ctx->revs, tree);
+               } else if (commit->object.parsed) {
+                       die(_("unable to load root tree for commit %s"),
+                             oid_to_hex(&commit->object.oid));
                }
                ctx->show_commit(commit, ctx->show_data);
 
index a37753047e084c9be40251e7c4286084bff6870a..7099d33508a0571cd8f960fcd80c46e4364f1766 100755 (executable)
@@ -199,10 +199,6 @@ test_expect_success 'too-short tree' '
        test_i18ngrep "too-short tree object" err
 '
 
-hex2oct() {
-    perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'malformed mode in tree' '
        hex_sha1=$(echo foo | git hash-object --stdin -w) &&
        bin_sha1=$(echo $hex_sha1 | hex2oct) &&
index 49f08d5b9c078412af8ee6343519094521c86c24..0f268a36642760906b59bc9a8db28a29fcb962fa 100755 (executable)
@@ -256,10 +256,6 @@ test_expect_success 'unparseable tree object' '
        test_i18ngrep ! "fatal: empty filename in tree entry" out
 '
 
-hex2oct() {
-       perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'tree entry with type mismatch' '
        test_when_finished "remove_object \$blob" &&
        test_when_finished "remove_object \$tree" &&
index 23854cab263d467ba189879add1fb6b408e00792..de9d99cf88a5b72a3aa6e190124dff36e67818b6 100755 (executable)
@@ -611,10 +611,6 @@ test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
        git -C replay.git index-pack -v --stdin <tmp.pack
 '
 
-hex2oct () {
-       perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'clone on case-insensitive fs' '
        git init icasefs &&
        (
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
new file mode 100755 (executable)
index 0000000..28611c9
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='git rev-list should handle unexpected object types'
+
+. ./test-lib.sh
+
+test_expect_success 'setup well-formed objects' '
+       blob="$(printf "foo" | git hash-object -w --stdin)" &&
+       tree="$(printf "100644 blob $blob\tfoo" | git mktree)" &&
+       commit="$(git commit-tree $tree -m "first commit")" &&
+       git cat-file commit $commit >good-commit
+'
+
+test_expect_success 'setup unexpected non-blob entry' '
+       printf "100644 foo\0$(echo $tree | hex2oct)" >broken-tree &&
+       broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
+'
+
+test_expect_failure 'traverse unexpected non-blob entry (lone)' '
+       test_must_fail git rev-list --objects $broken_tree
+'
+
+test_expect_success 'traverse unexpected non-blob entry (seen)' '
+       test_must_fail git rev-list --objects $tree $broken_tree >output 2>&1 &&
+       test_i18ngrep "is not a blob" output
+'
+
+test_expect_success 'setup unexpected non-tree entry' '
+       printf "40000 foo\0$(echo $blob | hex2oct)" >broken-tree &&
+       broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
+'
+
+test_expect_success 'traverse unexpected non-tree entry (lone)' '
+       test_must_fail git rev-list --objects $broken_tree
+'
+
+test_expect_success 'traverse unexpected non-tree entry (seen)' '
+       test_must_fail git rev-list --objects $blob $broken_tree >output 2>&1 &&
+       test_i18ngrep "is not a tree" output
+'
+
+test_expect_success 'setup unexpected non-commit parent' '
+       sed "/^author/ { h; s/.*/parent $blob/; G; }" <good-commit \
+               >broken-commit &&
+       broken_commit="$(git hash-object -w --literally -t commit \
+               broken-commit)"
+'
+
+test_expect_success 'traverse unexpected non-commit parent (lone)' '
+       test_must_fail git rev-list --objects $broken_commit >output 2>&1 &&
+       test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'traverse unexpected non-commit parent (seen)' '
+       test_must_fail git rev-list --objects $commit $broken_commit \
+               >output 2>&1 &&
+       test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'setup unexpected non-tree root' '
+       sed -e "s/$tree/$blob/" <good-commit >broken-commit &&
+       broken_commit="$(git hash-object -w --literally -t commit \
+               broken-commit)"
+'
+
+test_expect_success 'traverse unexpected non-tree root (lone)' '
+       test_must_fail git rev-list --objects $broken_commit
+'
+
+test_expect_success 'traverse unexpected non-tree root (seen)' '
+       test_must_fail git rev-list --objects $blob $broken_commit \
+               >output 2>&1 &&
+       test_i18ngrep "not a tree" output
+'
+
+test_expect_success 'setup unexpected non-commit tag' '
+       git tag -a -m "tagged commit" tag $commit &&
+       git cat-file tag tag >good-tag &&
+       test_when_finished "git tag -d tag" &&
+       sed -e "s/$commit/$blob/" <good-tag >broken-tag &&
+       tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_success 'traverse unexpected non-commit tag (lone)' '
+       test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-commit tag (seen)' '
+       test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
+       test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'setup unexpected non-tree tag' '
+       git tag -a -m "tagged tree" tag $tree &&
+       git cat-file tag tag >good-tag &&
+       test_when_finished "git tag -d tag" &&
+       sed -e "s/$tree/$blob/" <good-tag >broken-tag &&
+       tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_success 'traverse unexpected non-tree tag (lone)' '
+       test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-tree tag (seen)' '
+       test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
+       test_i18ngrep "not a tree" output
+'
+
+test_expect_success 'setup unexpected non-blob tag' '
+       git tag -a -m "tagged blob" tag $blob &&
+       git cat-file tag tag >good-tag &&
+       test_when_finished "git tag -d tag" &&
+       sed -e "s/$blob/$commit/" <good-tag >broken-tag &&
+       tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_failure 'traverse unexpected non-blob tag (lone)' '
+       test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-blob tag (seen)' '
+       test_must_fail git rev-list --objects $commit $tag >output 2>&1 &&
+       test_i18ngrep "not a blob" output
+'
+
+test_done
index 788ea1f18b99c5edacd9ae0ee81310141c799c6e..8270de74beafb931f09f296557406c0d158d48de 100644 (file)
@@ -1239,6 +1239,12 @@ depacketize () {
        '
 }
 
+# Converts base-16 data into base-8. The output is given as a sequence of
+# escaped octals, suitable for consumption by 'printf'.
+hex2oct () {
+       perl -ne 'printf "\\%03o", hex for /../g'
+}
+
 # Set the hash algorithm in use to $1.  Only useful when testing the testsuite.
 test_set_hash () {
        test_hash_algo="$1"