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

1  2 
builtin/rev-list.c
commit.c
t/t1450-fsck.sh
t/t5601-clone.sh
t/test-lib-functions.sh
diff --combined builtin/rev-list.c
index 425a5774db0697774df499fc16a626ab1e1efcf9,7de083b5f60b74e339085fcdd61e54dfa36559f2..9f31837d303f1f302266fdd03298ddce73867ba0
@@@ -238,7 -238,7 +238,7 @@@ static inline void finish_object__ma(st
  static int finish_object(struct object *obj, const char *name, void *cb_data)
  {
        struct rev_list_info *info = cb_data;
 -      if (!has_object_file(&obj->oid)) {
 +      if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
                finish_object__ma(obj);
                return 1;
        }
@@@ -379,7 -379,6 +379,6 @@@ int cmd_rev_list(int argc, const char *
        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
                }
        }
  
+       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));
diff --combined commit.c
index a9e74647dc822606d06f79bcf94030b32998bd69,e2cde566a97e0d5fce7eec0873f3a7364ec70110..8fa1883c61c580578a755cdf2da009203d8d386e
+++ b/commit.c
@@@ -340,21 -340,15 +340,21 @@@ void free_commit_buffer(struct parsed_o
        }
  }
  
 -struct tree *get_commit_tree(const struct commit *commit)
 +static inline void set_commit_tree(struct commit *c, struct tree *t)
 +{
 +      c->maybe_tree = t;
 +}
 +
 +struct tree *repo_get_commit_tree(struct repository *r,
 +                                const struct commit *commit)
  {
        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(the_repository, commit);
++              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)
  
  void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
  {
 -      c->maybe_tree = NULL;
 +      set_commit_tree(c, NULL);
        c->index = 0;
        free_commit_buffer(pool, c);
        free_commit_list(c->parents);
@@@ -412,7 -406,7 +412,7 @@@ int parse_commit_buffer(struct reposito
        if (get_oid_hex(bufptr + 5, &parent) < 0)
                return error("bad tree pointer in commit %s",
                             oid_to_hex(&item->object.oid));
 -      item->maybe_tree = lookup_tree(r, &parent);
 +      set_commit_tree(item, lookup_tree(r, &parent));
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
  
diff --combined t/t1450-fsck.sh
index 49f08d5b9c078412af8ee6343519094521c86c24,0b4d874de67eb88b30fd822c2b052ddebce2819b..0f268a36642760906b59bc9a8db28a29fcb962fa
@@@ -256,10 -256,6 +256,6 @@@ test_expect_success 'unparseable tree o
        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" &&
@@@ -740,7 -736,7 +736,7 @@@ test_expect_success 'fsck detects trunc
  # for each of type, we have one version which is referenced by another object
  # (and so while unreachable, not dangling), and another variant which really is
  # dangling.
 -test_expect_success 'fsck notices dangling objects' '
 +test_expect_success 'create dangling-object repository' '
        git init dangling &&
        (
                cd dangling &&
                commit=$(git commit-tree $tree) &&
                dcommit=$(git commit-tree -p $commit $tree) &&
  
 -              cat >expect <<-EOF &&
 +              cat >expect <<-EOF
                dangling blob $dblob
                dangling commit $dcommit
                dangling tree $dtree
                EOF
 +      )
 +'
  
 +test_expect_success 'fsck notices dangling objects' '
 +      (
 +              cd dangling &&
                git fsck >actual &&
                # the output order is non-deterministic, as it comes from a hash
                sort <actual >actual.sorted &&
        )
  '
  
 +test_expect_success 'fsck --connectivity-only notices dangling objects' '
 +      (
 +              cd dangling &&
 +              git fsck --connectivity-only >actual &&
 +              # the output order is non-deterministic, as it comes from a hash
 +              sort <actual >actual.sorted &&
 +              test_i18ncmp expect actual.sorted
 +      )
 +'
 +
  test_expect_success 'fsck $name notices bogus $name' '
        test_must_fail git fsck bogus &&
        test_must_fail git fsck $ZERO_OID
diff --combined t/t5601-clone.sh
index 23854cab263d467ba189879add1fb6b408e00792,3f4994301060fa2c0147b7ca1b879e23b6484851..de9d99cf88a5b72a3aa6e190124dff36e67818b6
@@@ -345,7 -345,7 +345,7 @@@ expect_ssh () 
  }
  
  test_expect_success 'clone myhost:src uses ssh' '
 -      git clone myhost:src ssh-clone &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone myhost:src ssh-clone &&
        expect_ssh myhost src
  '
  
@@@ -356,12 -356,12 +356,12 @@@ test_expect_success !MINGW,!CYGWIN 'clo
  '
  
  test_expect_success 'bracketed hostnames are still ssh' '
 -      git clone "[myhost:123]:src" ssh-bracket-clone &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone &&
        expect_ssh "-p 123" myhost src
  '
  
  test_expect_success 'OpenSSH variant passes -4' '
 -      git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
        expect_ssh "-4 -p 123" myhost src
  '
  
@@@ -405,7 -405,7 +405,7 @@@ test_expect_success 'OpenSSH-like uplin
        test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
        GIT_SSH="$TRASH_DIRECTORY/uplink" &&
        test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
 -      git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
 +      GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
        expect_ssh "-p 123" myhost src
  '
  
@@@ -444,14 -444,14 +444,14 @@@ test_expect_success 'single quoted plin
  
  test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
 -      GIT_SSH_VARIANT=ssh \
 -      git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
 +      GIT_TEST_PROTOCOL_VERSION=0 GIT_SSH_VARIANT=ssh \
 +              git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
        expect_ssh "-p 123" myhost src
  '
  
  test_expect_success 'ssh.variant overrides plink detection' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
 -      git -c ssh.variant=ssh \
 +      GIT_TEST_PROTOCOL_VERSION=0 git -c ssh.variant=ssh \
                clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
        expect_ssh "-p 123" myhost src
  '
@@@ -482,7 -482,7 +482,7 @@@ counter=
  # $3 path
  test_clone_url () {
        counter=$(($counter + 1))
 -      test_might_fail git clone "$1" tmp$counter &&
 +      test_might_fail env GIT_TEST_PROTOCOL_VERSION=0 git clone "$1" tmp$counter &&
        shift &&
        expect_ssh "$@"
  }
@@@ -611,10 -611,6 +611,6 @@@ test_expect_success 'GIT_TRACE_PACKFIL
        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 &&
        (
@@@ -733,4 -729,6 +729,4 @@@ test_expect_success 'partial clone usin
        partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
  '
  
 -stop_httpd
 -
  test_done
diff --combined t/test-lib-functions.sh
index 788ea1f18b99c5edacd9ae0ee81310141c799c6e,349eabe851722306bb3c38669c33f09c2f2b74df..8270de74beafb931f09f296557406c0d158d48de
@@@ -593,15 -593,6 +593,15 @@@ test_dir_is_empty () 
        fi
  }
  
 +# Check if the file exists and has a size greater than zero
 +test_file_not_empty () {
 +      if ! test -s "$1"
 +      then
 +              echo "'$1' is not a non-empty file."
 +              false
 +      fi
 +}
 +
  test_path_is_missing () {
        if test -e "$1"
        then
@@@ -943,34 -934,6 +943,34 @@@ test_when_finished () 
                } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
  }
  
 +# This function can be used to schedule some commands to be run
 +# unconditionally at the end of the test script, e.g. to stop a daemon:
 +#
 +#     test_expect_success 'test git daemon' '
 +#             git daemon &
 +#             daemon_pid=$! &&
 +#             test_atexit 'kill $daemon_pid' &&
 +#             hello world
 +#     '
 +#
 +# The commands will be executed before the trash directory is removed,
 +# i.e. the atexit commands will still be able to access any pidfiles or
 +# socket files.
 +#
 +# Note that these commands will be run even when a test script run
 +# with '--immediate' fails.  Be careful with your atexit commands to
 +# minimize any changes to the failed state.
 +
 +test_atexit () {
 +      # We cannot detect when we are in a subshell in general, but by
 +      # doing so on Bash is better than nothing (the test will
 +      # silently pass on other shells).
 +      test "${BASH_SUBSHELL-0}" = 0 ||
 +      error "bug in test script: test_atexit does nothing in a subshell"
 +      test_atexit_cleanup="{ $*
 +              } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
 +}
 +
  # Most tests can use the created repository, but some may need to create more.
  # Usage: test_create_repo <directory>
  test_create_repo () {
@@@ -1239,6 -1202,12 +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"