Merge branch 'mb/fast-import-delete-root'
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 18:38:34 +0000 (11:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 18:38:34 +0000 (11:38 -0700)
An attempt to remove the entire tree in the "git fast-import" input
stream caused it to misbehave.

* mb/fast-import-delete-root:
fast-import: fix segfault in store_tree()
t9300: test filedelete command

1  2 
fast-import.c
t/t9300-fast-import.sh
diff --combined fast-import.c
index 9e6134d0346835173a937b2366f18ba013b38c0e,e380804831f7c386e41dee85d7229448b9be30e0..487f1f81ac170acf87bae2801f6472c0768e3e24
@@@ -946,12 -946,10 +946,12 @@@ static void unkeep_all_packs(void
  
  static void end_packfile(void)
  {
 -      struct packed_git *old_p = pack_data, *new_p;
 +      if (!pack_data)
 +              return;
  
        clear_delta_base_cache();
        if (object_count) {
 +              struct packed_git *new_p;
                unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                pack_id++;
        }
        else {
 -              close(old_p->pack_fd);
 -              unlink_or_warn(old_p->pack_name);
 +              close(pack_data->pack_fd);
 +              unlink_or_warn(pack_data->pack_name);
        }
 -      free(old_p);
 +      free(pack_data);
 +      pack_data = NULL;
  
        /* We can't carry a delta across packfiles. */
        strbuf_release(&last_blob.data);
@@@ -1422,7 -1419,7 +1422,7 @@@ static void mktree(struct tree_content 
  
  static void store_tree(struct tree_entry *root)
  {
-       struct tree_content *t = root->tree;
+       struct tree_content *t;
        unsigned int i, j, del;
        struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
        struct object_entry *le = NULL;
        if (!is_null_sha1(root->versions[1].sha1))
                return;
  
+       if (!root->tree)
+               load_tree(root);
+       t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                if (t->entries[i]->tree)
                        store_tree(t->entries[i]);
@@@ -1682,9 -1683,8 +1686,9 @@@ found_entry
  static int update_branch(struct branch *b)
  {
        static const char *msg = "fast-import";
 -      struct ref_lock *lock;
 +      struct ref_transaction *transaction;
        unsigned char old_sha1[20];
 +      struct strbuf err = STRBUF_INIT;
  
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
                        delete_ref(b->name, old_sha1, 0);
                return 0;
        }
 -      lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
 -      if (!lock)
 -              return error("Unable to lock %s", b->name);
        if (!force_update && !is_null_sha1(old_sha1)) {
                struct commit *old_cmit, *new_cmit;
  
                old_cmit = lookup_commit_reference_gently(old_sha1, 0);
                new_cmit = lookup_commit_reference_gently(b->sha1, 0);
 -              if (!old_cmit || !new_cmit) {
 -                      unlock_ref(lock);
 +              if (!old_cmit || !new_cmit)
                        return error("Branch %s is missing commits.", b->name);
 -              }
  
                if (!in_merge_bases(old_cmit, new_cmit)) {
 -                      unlock_ref(lock);
                        warning("Not updating %s"
                                " (new tip %s does not contain %s)",
                                b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
                        return -1;
                }
        }
 -      if (write_ref_sha1(lock, b->sha1, msg) < 0)
 -              return error("Unable to update %s", b->name);
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction ||
 +          ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
 +                                 0, 1, &err) ||
 +          ref_transaction_commit(transaction, msg, &err)) {
 +              ref_transaction_free(transaction);
 +              error("%s", err.buf);
 +              strbuf_release(&err);
 +              return -1;
 +      }
 +      ref_transaction_free(transaction);
 +      strbuf_release(&err);
        return 0;
  }
  
@@@ -1738,32 -1734,15 +1742,32 @@@ static void dump_tags(void
  {
        static const char *msg = "fast-import";
        struct tag *t;
 -      struct ref_lock *lock;
 -      char ref_name[PATH_MAX];
 +      struct strbuf ref_name = STRBUF_INIT;
 +      struct strbuf err = STRBUF_INIT;
 +      struct ref_transaction *transaction;
  
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction) {
 +              failure |= error("%s", err.buf);
 +              goto cleanup;
 +      }
        for (t = first_tag; t; t = t->next_tag) {
 -              sprintf(ref_name, "tags/%s", t->name);
 -              lock = lock_ref_sha1(ref_name, NULL);
 -              if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
 -                      failure |= error("Unable to update %s", ref_name);
 +              strbuf_reset(&ref_name);
 +              strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 +
 +              if (ref_transaction_update(transaction, ref_name.buf, t->sha1,
 +                                         NULL, 0, 0, &err)) {
 +                      failure |= error("%s", err.buf);
 +                      goto cleanup;
 +              }
        }
 +      if (ref_transaction_commit(transaction, msg, &err))
 +              failure |= error("%s", err.buf);
 +
 + cleanup:
 +      ref_transaction_free(transaction);
 +      strbuf_release(&ref_name);
 +      strbuf_release(&err);
  }
  
  static void dump_marks_helper(FILE *f,
@@@ -1996,7 -1975,7 +2000,7 @@@ static int parse_data(struct strbuf *sb
        return 1;
  }
  
 -static int validate_raw_date(const char *src, char *result, int maxlen)
 +static int validate_raw_date(const char *src, struct strbuf *result)
  {
        const char *orig_src = src;
        char *endp;
                return -1;
  
        num = strtoul(src + 1, &endp, 10);
 -      if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
 -          1400 < num)
 +      if (errno || endp == src + 1 || *endp || 1400 < num)
                return -1;
  
 -      strcpy(result, orig_src);
 +      strbuf_addstr(result, orig_src);
        return 0;
  }
  
@@@ -2025,7 -2005,7 +2029,7 @@@ static char *parse_ident(const char *bu
  {
        const char *ltgt;
        size_t name_len;
 -      char *ident;
 +      struct strbuf ident = STRBUF_INIT;
  
        /* ensure there is a space delimiter even if there is no name */
        if (*buf == '<')
                die("Missing space after > in ident string: %s", buf);
        ltgt++;
        name_len = ltgt - buf;
 -      ident = xmalloc(name_len + 24);
 -      strncpy(ident, buf, name_len);
 +      strbuf_add(&ident, buf, name_len);
  
        switch (whenspec) {
        case WHENSPEC_RAW:
 -              if (validate_raw_date(ltgt, ident + name_len, 24) < 0)
 +              if (validate_raw_date(ltgt, &ident) < 0)
                        die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
                break;
        case WHENSPEC_RFC2822:
 -              if (parse_date(ltgt, ident + name_len, 24) < 0)
 +              if (parse_date(ltgt, &ident) < 0)
                        die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
                break;
        case WHENSPEC_NOW:
                if (strcmp("now", ltgt))
                        die("Date in ident must be 'now': %s", buf);
 -              datestamp(ident + name_len, 24);
 +              datestamp(&ident);
                break;
        }
  
 -      return ident;
 +      return strbuf_detach(&ident, NULL);
  }
  
  static void parse_and_store_blob(
@@@ -3297,34 -3278,36 +3301,34 @@@ static void parse_option(const char *op
        die("This version of fast-import does not support option: %s", option);
  }
  
 -static int git_pack_config(const char *k, const char *v, void *cb)
 +static void git_pack_config(void)
  {
 -      if (!strcmp(k, "pack.depth")) {
 -              max_depth = git_config_int(k, v);
 +      int indexversion_value;
 +      unsigned long packsizelimit_value;
 +
 +      if (!git_config_get_ulong("pack.depth", &max_depth)) {
                if (max_depth > MAX_DEPTH)
                        max_depth = MAX_DEPTH;
 -              return 0;
        }
 -      if (!strcmp(k, "pack.compression")) {
 -              int level = git_config_int(k, v);
 -              if (level == -1)
 -                      level = Z_DEFAULT_COMPRESSION;
 -              else if (level < 0 || level > Z_BEST_COMPRESSION)
 -                      die("bad pack compression level %d", level);
 -              pack_compression_level = level;
 +      if (!git_config_get_int("pack.compression", &pack_compression_level)) {
 +              if (pack_compression_level == -1)
 +                      pack_compression_level = Z_DEFAULT_COMPRESSION;
 +              else if (pack_compression_level < 0 ||
 +                       pack_compression_level > Z_BEST_COMPRESSION)
 +                      git_die_config("pack.compression",
 +                                      "bad pack compression level %d", pack_compression_level);
                pack_compression_seen = 1;
 -              return 0;
        }
 -      if (!strcmp(k, "pack.indexversion")) {
 -              pack_idx_opts.version = git_config_int(k, v);
 +      if (!git_config_get_int("pack.indexversion", &indexversion_value)) {
 +              pack_idx_opts.version = indexversion_value;
                if (pack_idx_opts.version > 2)
 -                      die("bad pack.indexversion=%"PRIu32,
 -                          pack_idx_opts.version);
 -              return 0;
 -      }
 -      if (!strcmp(k, "pack.packsizelimit")) {
 -              max_packsize = git_config_ulong(k, v);
 -              return 0;
 +                      git_die_config("pack.indexversion",
 +                                      "bad pack.indexversion=%"PRIu32, pack_idx_opts.version);
        }
 -      return git_default_config(k, v, cb);
 +      if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
 +              max_packsize = packsizelimit_value;
 +
 +      git_config(git_default_config, NULL);
  }
  
  static const char fast_import_usage[] =
@@@ -3377,7 -3360,7 +3381,7 @@@ int main(int argc, char **argv
  
        setup_git_directory();
        reset_pack_idx_option(&pack_idx_opts);
 -      git_config(git_pack_config, NULL);
 +      git_pack_config();
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
  
diff --combined t/t9300-fast-import.sh
index 99f51614ad9ec3de8abf272d6fc6a778707a2292,d400442a4a8891a6f20e1b764fa19488f803c9c4..60ef3a74e8e904ff29839e7ad6b939c20bdae0a5
@@@ -2336,7 -2336,7 +2336,7 @@@ test_expect_success 'R: cat-blob-fd mus
        test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
  '
  
 -test_expect_success NOT_MINGW 'R: print old blob' '
 +test_expect_success !MINGW 'R: print old blob' '
        blob=$(echo "yes it can" | git hash-object -w --stdin) &&
        cat >expect <<-EOF &&
        ${blob} blob 11
        test_cmp expect actual
  '
  
 -test_expect_success NOT_MINGW 'R: in-stream cat-blob-fd not respected' '
 +test_expect_success !MINGW 'R: in-stream cat-blob-fd not respected' '
        echo hello >greeting &&
        blob=$(git hash-object -w greeting) &&
        cat >expect <<-EOF &&
        test_cmp expect actual.1
  '
  
 -test_expect_success NOT_MINGW 'R: print new blob' '
 +test_expect_success !MINGW 'R: print new blob' '
        blob=$(echo "yep yep yep" | git hash-object --stdin) &&
        cat >expect <<-EOF &&
        ${blob} blob 12
        test_cmp expect actual
  '
  
 -test_expect_success NOT_MINGW 'R: print new blob by sha1' '
 +test_expect_success !MINGW 'R: print new blob by sha1' '
        blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
        cat >expect <<-EOF &&
        ${blob} blob 25
@@@ -3017,4 -3017,108 +3017,108 @@@ test_expect_success 'T: empty reset doe
        git rev-parse --verify refs/heads/not-to-delete
  '
  
+ ###
+ ### series U (filedelete)
+ ###
+ cat >input <<INPUT_END
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ test setup
+ COMMIT
+ M 100644 inline hello.c
+ data <<BLOB
+ blob 1
+ BLOB
+ M 100644 inline good/night.txt
+ data <<BLOB
+ sleep well
+ BLOB
+ M 100644 inline good/bye.txt
+ data <<BLOB
+ au revoir
+ BLOB
+ INPUT_END
+ test_expect_success 'U: initialize for U tests' '
+       git fast-import <input
+ '
+ cat >input <<INPUT_END
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ delete good/night.txt
+ COMMIT
+ from refs/heads/U^0
+ D good/night.txt
+ INPUT_END
+ test_expect_success 'U: filedelete file succeeds' '
+       git fast-import <input
+ '
+ cat >expect <<EOF
+ :100644 000000 2907ebb4bf85d91bf0716bb3bd8a68ef48d6da76 0000000000000000000000000000000000000000 D    good/night.txt
+ EOF
+ git diff-tree -M -r U^1 U >actual
+ test_expect_success 'U: validate file delete result' '
+       compare_diff_raw expect actual
+ '
+ cat >input <<INPUT_END
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ delete good dir
+ COMMIT
+ from refs/heads/U^0
+ D good
+ INPUT_END
+ test_expect_success 'U: filedelete directory succeeds' '
+       git fast-import <input
+ '
+ cat >expect <<EOF
+ :100644 000000 69cb75792f55123d8389c156b0b41c2ff00ed507 0000000000000000000000000000000000000000 D    good/bye.txt
+ EOF
+ git diff-tree -M -r U^1 U >actual
+ test_expect_success 'U: validate directory delete result' '
+       compare_diff_raw expect actual
+ '
+ cat >input <<INPUT_END
+ commit refs/heads/U
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ must succeed
+ COMMIT
+ from refs/heads/U^0
+ D ""
+ INPUT_END
+ test_expect_success 'U: filedelete root succeeds' '
+     git fast-import <input
+ '
+ cat >expect <<EOF
+ :100644 000000 c18147dc648481eeb65dc5e66628429a64843327 0000000000000000000000000000000000000000 D    hello.c
+ EOF
+ git diff-tree -M -r U^1 U >actual
+ test_expect_success 'U: validate root delete result' '
+       compare_diff_raw expect actual
+ '
  test_done