Merge branch 'jc/fix-diff-files-unmerged' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 13 May 2011 17:41:54 +0000 (10:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 13 May 2011 17:41:54 +0000 (10:41 -0700)
* jc/fix-diff-files-unmerged:
diff-files: show unmerged entries correctly
diff: remove often unused parameters from diff_unmerge()
diff.c: return filepair from diff_unmerge()
test: use $_z40 from test-lib

1  2 
diff-lib.c
diff.c
diff.h
t/t1400-update-ref.sh
t/t1501-worktree.sh
t/t3200-branch.sh
t/t3600-rm.sh
t/t4002-diff-basic.sh
t/t4027-diff-submodule.sh
t/t7012-skip-worktree-writing.sh
t/test-lib.sh
diff --combined diff-lib.c
index 2870de400ed533d83c77269ee1654af212c6510c,b782476e4ee7769854d06ad0bbafd1df99ae19b4..3b5f2242a597ff1b44a3af6be72cb14e6e0d5455
@@@ -103,15 -103,16 +103,17 @@@ int run_diff_files(struct rev_info *rev
                unsigned dirty_submodule = 0;
  
                if (DIFF_OPT_TST(&revs->diffopt, QUICK) &&
 -                      DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
 +                  !revs->diffopt.filter &&
 +                  DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
                        break;
  
 -              if (!ce_path_match(ce, revs->prune_data))
 +              if (!ce_path_match(ce, &revs->prune_data))
                        continue;
  
                if (ce_stage(ce)) {
                        struct combine_diff_path *dpath;
+                       struct diff_filepair *pair;
+                       unsigned int wt_mode = 0;
                        int num_compare_stages = 0;
                        size_t path_len;
  
  
                        changed = check_removed(ce, &st);
                        if (!changed)
-                               dpath->mode = ce_mode_from_stat(ce, st.st_mode);
+                               wt_mode = ce_mode_from_stat(ce, st.st_mode);
                        else {
                                if (changed < 0) {
                                        perror(ce->name);
                                }
                                if (silent_on_removed)
                                        continue;
+                               wt_mode = 0;
                        }
+                       dpath->mode = wt_mode;
  
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
                         * Show the diff for the 'ce' if we found the one
                         * from the desired stage.
                         */
-                       diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
+                       pair = diff_unmerge(&revs->diffopt, ce->name);
+                       if (wt_mode)
+                               pair->two->mode = wt_mode;
                        if (ce_stage(ce) != diff_unmerged_stage)
                                continue;
                }
@@@ -373,8 -378,9 +379,9 @@@ static void do_oneway_diff(struct unpac
        match_missing = !revs->ignore_merges;
  
        if (cached && idx && ce_stage(idx)) {
-               diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode,
-                            idx->sha1);
+               struct diff_filepair *pair;
+               pair = diff_unmerge(&revs->diffopt, idx->name);
+               fill_filespec(pair->one, idx->sha1, idx->ce_mode);
                return;
        }
  
@@@ -428,7 -434,7 +435,7 @@@ static int oneway_diff(struct cache_ent
        if (tree == o->df_conflict_entry)
                tree = NULL;
  
 -      if (ce_path_match(idx ? idx : tree, revs->prune_data))
 +      if (ce_path_match(idx ? idx : tree, &revs->prune_data))
                do_oneway_diff(o, idx, tree);
  
        return 0;
@@@ -502,7 -508,7 +509,7 @@@ int do_diff_cache(const unsigned char *
        active_nr = dst - active_cache;
  
        init_revisions(&revs, NULL);
 -      revs.prune_data = opt->paths;
 +      init_pathspec(&revs.prune_data, opt->pathspec.raw);
        tree = parse_tree_indirect(tree_sha1);
        if (!tree)
                die("bad tree object %s", sha1_to_hex(tree_sha1));
diff --combined diff.c
index 5376d01e1b26f0297e20f14bd8adcfefd374ab08,d2c5c563bc84ba048ebccdc692a84663b8a74e9a..ba45a7df112da57b567e723bae695b330cfdfdba
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -23,7 -23,7 +23,7 @@@
  #endif
  
  static int diff_detect_rename_default;
 -static int diff_rename_limit_default = 200;
 +static int diff_rename_limit_default = 400;
  static int diff_suppress_blank_empty;
  int diff_use_color_default = -1;
  static const char *diff_word_regex_cfg;
@@@ -245,15 -245,6 +245,15 @@@ static int fill_mmfile(mmfile_t *mf, st
        return 0;
  }
  
 +/* like fill_mmfile, but only for size, so we can avoid retrieving blob */
 +static unsigned long diff_filespec_size(struct diff_filespec *one)
 +{
 +      if (!DIFF_FILE_VALID(one))
 +              return 0;
 +      diff_populate_filespec(one, 1);
 +      return one->size;
 +}
 +
  static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
  {
        char *ptr = mf->ptr;
@@@ -615,20 -606,22 +615,20 @@@ static void diff_words_append(char *lin
        buffer->text.ptr[buffer->text.size] = '\0';
  }
  
 -struct diff_words_style_elem
 -{
 +struct diff_words_style_elem {
        const char *prefix;
        const char *suffix;
        const char *color; /* NULL; filled in by the setup code if
                            * color is enabled */
  };
  
 -struct diff_words_style
 -{
 +struct diff_words_style {
        enum diff_words_type type;
        struct diff_words_style_elem new, old, ctx;
        const char *newline;
  };
  
 -struct diff_words_style diff_words_styles[] = {
 +static struct diff_words_style diff_words_styles[] = {
        { DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
        { DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
        { DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" }
@@@ -1242,7 -1235,7 +1242,7 @@@ static void show_stats(struct diffstat_
        uintmax_t max_change = 0, max_len = 0;
        int total_files = data->nr;
        int width, name_width;
 -      const char *reset, *set, *add_c, *del_c;
 +      const char *reset, *add_c, *del_c;
        const char *line_prefix = "";
        struct strbuf *msg = NULL;
  
  
        /* Find the longest filename and max number of changes */
        reset = diff_get_color_opt(options, DIFF_RESET);
 -      set   = diff_get_color_opt(options, DIFF_PLAIN);
        add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
        del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
  
@@@ -1538,36 -1532,8 +1538,36 @@@ static void show_dirstat(struct diff_op
                struct diff_filepair *p = q->queue[i];
                const char *name;
                unsigned long copied, added, damage;
 +              int content_changed;
  
 -              name = p->one->path ? p->one->path : p->two->path;
 +              name = p->two->path ? p->two->path : p->one->path;
 +
 +              if (p->one->sha1_valid && p->two->sha1_valid)
 +                      content_changed = hashcmp(p->one->sha1, p->two->sha1);
 +              else
 +                      content_changed = 1;
 +
 +              if (!content_changed) {
 +                      /*
 +                       * The SHA1 has not changed, so pre-/post-content is
 +                       * identical. We can therefore skip looking at the
 +                       * file contents altogether.
 +                       */
 +                      damage = 0;
 +                      goto found_damage;
 +              }
 +
 +              if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
 +                      /*
 +                       * In --dirstat-by-file mode, we don't really need to
 +                       * look at the actual file contents at all.
 +                       * The fact that the SHA1 changed is enough for us to
 +                       * add this file to the list of results
 +                       * (with each file contributing equal damage).
 +                       */
 +                      damage = 1;
 +                      goto found_damage;
 +              }
  
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
                        diff_populate_filespec(p->one, 0);
                /*
                 * Original minus copied is the removed material,
                 * added is the new material.  They are both damages
 -               * made to the preimage. In --dirstat-by-file mode, count
 -               * damaged files, not damaged lines. This is done by
 -               * counting only a single damaged line per file.
 +               * made to the preimage.
 +               * If the resulting damage is zero, we know that
 +               * diffcore_count_changes() considers the two entries to
 +               * be identical, but since content_changed is true, we
 +               * know that there must have been _some_ kind of change,
 +               * so we force all entries to have damage > 0.
                 */
                damage = (p->one->size - copied) + added;
 -              if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
 +              if (!damage)
                        damage = 1;
  
 +found_damage:
                ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
                dir.files[dir.nr].name = name;
                dir.files[dir.nr].changed = damage;
@@@ -1809,14 -1771,8 +1809,14 @@@ static void emit_binary_diff(FILE *file
  
  static void diff_filespec_load_driver(struct diff_filespec *one)
  {
 -      if (!one->driver)
 +      /* Use already-loaded driver */
 +      if (one->driver)
 +              return;
 +
 +      if (S_ISREG(one->mode))
                one->driver = userdiff_find_by_path(one->path);
 +
 +      /* Fallback to default settings */
        if (!one->driver)
                one->driver = userdiff_find_by_name("default");
  }
@@@ -1864,7 -1820,8 +1864,7 @@@ struct userdiff_driver *get_textconv(st
  {
        if (!DIFF_FILE_VALID(one))
                return NULL;
 -      if (!S_ISREG(one->mode))
 -              return NULL;
 +
        diff_filespec_load_driver(one);
        if (!one->driver->textconv)
                return NULL;
@@@ -2117,28 -2074,25 +2117,28 @@@ static void builtin_diffstat(const cha
                data->is_unmerged = 1;
                return;
        }
 -      if (complete_rewrite) {
 +
 +      if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
 +              data->is_binary = 1;
 +              data->added = diff_filespec_size(two);
 +              data->deleted = diff_filespec_size(one);
 +      }
 +
 +      else if (complete_rewrite) {
                diff_populate_filespec(one, 0);
                diff_populate_filespec(two, 0);
                data->deleted = count_lines(one->data, one->size);
                data->added = count_lines(two->data, two->size);
 -              goto free_and_return;
        }
 -      if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 -              die("unable to read files to diff");
  
 -      if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
 -              data->is_binary = 1;
 -              data->added = mf2.size;
 -              data->deleted = mf1.size;
 -      } else {
 +      else {
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
                xdemitconf_t xecfg;
  
 +              if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 +                      die("unable to read files to diff");
 +
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xpp.flags = o->xdl_opts;
                              &xpp, &xecfg);
        }
  
 - free_and_return:
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
  }
@@@ -2198,7 -2153,7 +2198,7 @@@ static void builtin_checkdiff(const cha
  
                        ecbdata.ws_rule = data.ws_rule;
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
 -                      blank_at_eof = ecbdata.blank_at_eof_in_preimage;
 +                      blank_at_eof = ecbdata.blank_at_eof_in_postimage;
  
                        if (blank_at_eof) {
                                static char *err;
@@@ -2431,14 -2386,10 +2431,14 @@@ int diff_populate_filespec(struct diff_
        }
        else {
                enum object_type type;
 -              if (size_only)
 +              if (size_only) {
                        type = sha1_object_info(s->sha1, &s->size);
 -              else {
 +                      if (type < 0)
 +                              die("unable to read %s", sha1_to_hex(s->sha1));
 +              } else {
                        s->data = read_sha1_file(s->sha1, &type, &s->size);
 +                      if (!s->data)
 +                              die("unable to read %s", sha1_to_hex(s->sha1));
                        s->should_free = 1;
                }
        }
@@@ -3189,23 -3140,20 +3189,23 @@@ int diff_opt_parse(struct diff_options 
                return stat_opt(options, av);
  
        /* renames options */
 -      else if (!prefixcmp(arg, "-B")) {
 +      else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
 +               !strcmp(arg, "--break-rewrites")) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
 -                      return -1;
 +                      return error("invalid argument to -B: %s", arg+2);
        }
 -      else if (!prefixcmp(arg, "-M")) {
 +      else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") ||
 +               !strcmp(arg, "--find-renames")) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
 -                      return -1;
 +                      return error("invalid argument to -M: %s", arg+2);
                options->detect_rename = DIFF_DETECT_RENAME;
        }
 -      else if (!prefixcmp(arg, "-C")) {
 +      else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") ||
 +               !strcmp(arg, "--find-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        DIFF_OPT_SET(options, FIND_COPIES_HARDER);
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
 -                      return -1;
 +                      return error("invalid argument to -C: %s", arg+2);
                options->detect_rename = DIFF_DETECT_COPY;
        }
        else if (!strcmp(arg, "--no-renames"))
        }
        else if ((argcount = short_opt('S', av, &optarg))) {
                options->pickaxe = optarg;
 +              options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
 +              return argcount;
 +      } else if ((argcount = short_opt('G', av, &optarg))) {
 +              options->pickaxe = optarg;
 +              options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
                return argcount;
        }
        else if (!strcmp(arg, "--pickaxe-all"))
 -              options->pickaxe_opts = DIFF_PICKAXE_ALL;
 +              options->pickaxe_opts |= DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
 -              options->pickaxe_opts = DIFF_PICKAXE_REGEX;
 +              options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
        else if ((argcount = short_opt('O', av, &optarg))) {
                options->orderfile = optarg;
                return argcount;
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
                options->file = fopen(optarg, "w");
                if (!options->file)
 -                      die_errno("Could not open '%s'", arg + strlen("--output="));
 +                      die_errno("Could not open '%s'", optarg);
                options->close_file = 1;
                return argcount;
        } else
        return 1;
  }
  
 -static int parse_num(const char **cp_p)
 +int parse_rename_score(const char **cp_p)
  {
        unsigned long num, scale;
        int ch, dot;
@@@ -3418,26 -3361,10 +3418,26 @@@ static int diff_scoreopt_parse(const ch
        if (*opt++ != '-')
                return -1;
        cmd = *opt++;
 +      if (cmd == '-') {
 +              /* convert the long-form arguments into short-form versions */
 +              if (!prefixcmp(opt, "break-rewrites")) {
 +                      opt += strlen("break-rewrites");
 +                      if (*opt == 0 || *opt++ == '=')
 +                              cmd = 'B';
 +              } else if (!prefixcmp(opt, "find-copies")) {
 +                      opt += strlen("find-copies");
 +                      if (*opt == 0 || *opt++ == '=')
 +                              cmd = 'C';
 +              } else if (!prefixcmp(opt, "find-renames")) {
 +                      opt += strlen("find-renames");
 +                      if (*opt == 0 || *opt++ == '=')
 +                              cmd = 'M';
 +              }
 +      }
        if (cmd != 'M' && cmd != 'C' && cmd != 'B')
                return -1; /* that is not a -M, -C nor -B option */
  
 -      opt1 = parse_num(&opt);
 +      opt1 = parse_rename_score(&opt);
        if (cmd != 'B')
                opt2 = 0;
        else {
                        return -1; /* we expect -B80/99 or -B80 */
                else {
                        opt++;
 -                      opt2 = parse_num(&opt);
 +                      opt2 = parse_rename_score(&opt);
                }
        }
        if (*opt != 0)
@@@ -3600,7 -3527,7 +3600,7 @@@ static void diff_flush_stat(struct diff
  
        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
 -              return; /* no tree diffs in patch format */
 +              return; /* no useful stat for tree diffs */
  
        run_diffstat(p, o, diffstat);
  }
@@@ -3613,7 -3540,7 +3613,7 @@@ static void diff_flush_checkdiff(struc
  
        if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
            (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
 -              return; /* no tree diffs in patch format */
 +              return; /* nothing to check in tree diffs */
  
        run_checkdiff(p, o);
  }
@@@ -3938,7 -3865,7 +3938,7 @@@ static int diff_get_patch_id(struct dif
  
                xpp.flags = 0;
                xecfg.ctxlen = 3;
 -              xecfg.flags = XDL_EMIT_FUNCNAMES;
 +              xecfg.flags = 0;
                xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
                              &xpp, &xecfg);
        }
@@@ -4249,7 -4176,7 +4249,7 @@@ void diffcore_std(struct diff_options *
                        diffcore_merge_broken();
        }
        if (options->pickaxe)
 -              diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
 +              diffcore_pickaxe(options);
        if (options->orderfile)
                diffcore_order(options->orderfile);
        if (!options->found_follow)
@@@ -4381,20 -4308,20 +4381,20 @@@ void diff_change(struct diff_options *o
                DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
- void diff_unmerge(struct diff_options *options,
-                 const char *path,
-                 unsigned mode, const unsigned char *sha1)
+ struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
  {
+       struct diff_filepair *pair;
        struct diff_filespec *one, *two;
  
        if (options->prefix &&
            strncmp(path, options->prefix, options->prefix_length))
-               return;
+               return NULL;
  
        one = alloc_filespec(path);
        two = alloc_filespec(path);
-       fill_filespec(one, sha1, mode);
-       diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
+       pair = diff_queue(&diff_queued_diff, one, two);
+       pair->is_unmerged = 1;
+       return pair;
  }
  
  static char *run_textconv(const char *pgm, struct diff_filespec *spec,
@@@ -4452,7 -4379,7 +4452,7 @@@ size_t fill_textconv(struct userdiff_dr
                return df->size;
        }
  
 -      if (driver->textconv_cache) {
 +      if (driver->textconv_cache && df->sha1_valid) {
                *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
                                          &size);
                if (*outbuf)
        if (!*outbuf)
                die("unable to read files to diff");
  
 -      if (driver->textconv_cache) {
 +      if (driver->textconv_cache && df->sha1_valid) {
                /* ignore errors, as we might be in a readonly repository */
                notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
                                size);
diff --combined diff.h
index 007a0554d4b252e83e98f5578758d51ec0c6e120,3edb705b4dc6c7f8bb1febdb6440c96141232c01..42b49d8f228fd2cc680ab9a2e332819d46f2f875
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -110,8 -110,7 +110,8 @@@ struct diff_options 
        int pickaxe_opts;
        int rename_score;
        int rename_limit;
 -      int warn_on_too_large_rename;
 +      int needed_rename_limit;
 +      int show_rename_progress;
        int dirstat_percent;
        int setup;
        int abbrev;
        FILE *file;
        int close_file;
  
 -      int nr_paths;
 -      const char **paths;
 -      int *pathlens;
 +      struct pathspec pathspec;
        change_fn_t change;
        add_remove_fn_t add_remove;
        diff_format_fn_t format_callback;
@@@ -208,10 -209,7 +208,7 @@@ extern void diff_change(struct diff_opt
                        const char *fullpath,
                        unsigned dirty_submodule1, unsigned dirty_submodule2);
  
- extern void diff_unmerge(struct diff_options *,
-                        const char *path,
-                        unsigned mode,
-                        const unsigned char *sha1);
+ extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);
  
  #define DIFF_SETUP_REVERSE            1
  #define DIFF_SETUP_USE_CACHE          2
@@@ -237,9 -235,6 +234,9 @@@ extern int diff_setup_done(struct diff_
  #define DIFF_PICKAXE_ALL      1
  #define DIFF_PICKAXE_REGEX    2
  
 +#define DIFF_PICKAXE_KIND_S   4 /* traditional plumbing counter */
 +#define DIFF_PICKAXE_KIND_G   8 /* grep in the patch */
 +
  extern void diffcore_std(struct diff_options *);
  extern void diffcore_fix_diff_index(struct diff_options *);
  
@@@ -314,6 -309,4 +311,6 @@@ extern size_t fill_textconv(struct user
  
  extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
  
 +extern int parse_rename_score(const char **cp_p);
 +
  #endif /* DIFF_H */
diff --combined t/t1400-update-ref.sh
index ff747f8229bb46df070ea9aad2702f4d1c703420,6b3991576538034b0ba2907130a1a35ced71610e..4fd83a667ac8b08bef090d303bc4434969874780
@@@ -6,7 -6,7 +6,7 @@@
  test_description='Test git update-ref and basic ref logging'
  . ./test-lib.sh
  
- Z=0000000000000000000000000000000000000000
+ Z=$_z40
  
  test_expect_success setup '
  
@@@ -52,8 -52,9 +52,8 @@@ rm -f .git/$
  
  test_expect_success \
        "fail to create $n" \
 -      "touch .git/$n_dir
 -       git update-ref $n $A >out 2>err"'
 -       test $? != 0'
 +      "touch .git/$n_dir &&
 +       test_must_fail git update-ref $n $A >out 2>err"
  rm -f .git/$n_dir out err
  
  test_expect_success \
@@@ -184,55 -185,55 +184,55 @@@ gd="Thu, 26 May 2005 18:33:00 -0500
  ld="Thu, 26 May 2005 18:43:00 -0500"
  test_expect_success \
        'Query "master@{May 25 2005}" (before history)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
         test '"$C"' = $(cat o) &&
         test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
  test_expect_success \
        "Query master@{2005-05-25} (before history)" \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify master@{2005-05-25} >o 2>e &&
         test '"$C"' = $(cat o) &&
         echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
  test_expect_success \
        'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
         test '"$C"' = $(cat o) &&
         test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"'
  test_expect_success \
        'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
         test '"$C"' = $(cat o) &&
         test "" = "$(cat e)"'
  test_expect_success \
        'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
         test '"$A"' = $(cat o) &&
         test "" = "$(cat e)"'
  test_expect_success \
        'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
         test '"$B"' = $(cat o) &&
         test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
  test_expect_success \
        'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
         test '"$Z"' = $(cat o) &&
         test "" = "$(cat e)"'
  test_expect_success \
        'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
         test '"$E"' = $(cat o) &&
         test "" = "$(cat e)"'
  test_expect_success \
        'Query "master@{2005-05-28}" (past end of history)' \
 -      'rm -f o e
 +      'rm -f o e &&
         git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
         test '"$D"' = $(cat o) &&
         test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
@@@ -246,7 -247,7 +246,7 @@@ test_expect_success 
       git add F &&
         GIT_AUTHOR_DATE="2005-05-26 23:30" \
         GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
 -       h_TEST=$(git rev-parse --verify HEAD)
 +       h_TEST=$(git rev-parse --verify HEAD) &&
         echo The other day this did not work. >M &&
         echo And then Bob told me how to fix it. >>M &&
         echo OTHER >F &&
diff --combined t/t1501-worktree.sh
index da6252b1179c47d39393c983d88c75d84a507cb7,dc7c99257ce15102a8f5c9986e17599aeb9d1ca0..63849836c8b088eb495dc565ff909c367c868301
@@@ -7,7 -7,6 +7,6 @@@ test_expect_success 'setup' 
        EMPTY_TREE=$(git write-tree) &&
        EMPTY_BLOB=$(git hash-object -t blob --stdin </dev/null) &&
        CHANGED_BLOB=$(echo changed | git hash-object -t blob --stdin) &&
-       ZEROES=0000000000000000000000000000000000000000 &&
        EMPTY_BLOB7=$(echo $EMPTY_BLOB | sed "s/\(.......\).*/\1/") &&
        CHANGED_BLOB7=$(echo $CHANGED_BLOB | sed "s/\(.......\).*/\1/") &&
  
@@@ -239,10 -238,10 +238,10 @@@ test_expect_success '_gently() groks re
  
  test_expect_success 'diff-index respects work tree under .git dir' '
        cat >diff-index-cached.expected <<-EOF &&
-       :000000 100644 $ZEROES $EMPTY_BLOB A    sub/dir/tracked
+       :000000 100644 $_z40 $EMPTY_BLOB A      sub/dir/tracked
        EOF
        cat >diff-index.expected <<-EOF &&
-       :000000 100644 $ZEROES $ZEROES A        sub/dir/tracked
+       :000000 100644 $_z40 $_z40 A    sub/dir/tracked
        EOF
  
        (
  
  test_expect_success 'diff-files respects work tree under .git dir' '
        cat >diff-files.expected <<-EOF &&
-       :100644 100644 $EMPTY_BLOB $ZEROES M    sub/dir/tracked
+       :100644 100644 $EMPTY_BLOB $_z40 M      sub/dir/tracked
        EOF
  
        (
@@@ -340,11 -339,4 +339,11 @@@ test_expect_success 'make_relative_pat
        git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
  '
  
 +test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
 +      GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work \
 +      test-subprocess --setup-work-tree rev-parse --show-toplevel >actual &&
 +      echo "$(pwd)/repo.git/work" >expected &&
 +      test_cmp expected actual
 +'
 +
  test_done
diff --combined t/t3200-branch.sh
index 286a2a68692dd7a70e34851f75098506a1fc783a,4c4cff19634546d37b956dadbe9306db15ecbde4..463ef1909f5e31be078f504546f5ea8ab3154d3c
@@@ -26,17 -26,6 +26,17 @@@ test_expect_success 
       ! test -f .git/refs/heads/--help
  '
  
 +test_expect_success 'branch -h in broken repository' '
 +      mkdir broken &&
 +      (
 +              cd broken &&
 +              git init &&
 +              >.git/refs/heads/master &&
 +              test_expect_code 129 git branch -h >usage 2>&1
 +      ) &&
 +      grep "[Uu]sage" broken/usage
 +'
 +
  test_expect_success \
      'git branch abc should create a branch' \
      'git branch abc && test -f .git/refs/heads/abc'
@@@ -46,7 -35,7 +46,7 @@@ test_expect_success 
      'git branch a/b/c && test -f .git/refs/heads/a/b/c'
  
  cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from master
$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000       branch: Created from master
  EOF
  test_expect_success \
      'git branch -l d/e/f should create a branch and a log' \
@@@ -203,7 -192,7 +203,7 @@@ test_expect_success 'test deleting bran
       test -z "$(git config branch.my7.remote)" &&
       test -z "$(git config branch.my7.merge)"'
  
 -test_expect_success 'test deleting branch without config' \
 +test_expect_success C_LOCALE_OUTPUT 'test deleting branch without config' \
      'git branch my7 s &&
       sha1=$(git rev-parse my7 | cut -c 1-7) &&
       test "$(git branch -d my7 2>&1)" = "Deleted branch my7 (was $sha1)."'
@@@ -223,14 -212,9 +223,14 @@@ test_expect_success 
      'branch from non-branch HEAD w/--track causes failure' \
      'test_must_fail git branch --track my10 HEAD^'
  
 +test_expect_success \
 +    'branch from tag w/--track causes failure' \
 +    'git tag foobar &&
 +     test_must_fail git branch --track my11 foobar'
 +
  # Keep this test last, as it changes the current branch
  cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from master
$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000       branch: Created from master
  EOF
  test_expect_success \
      'git checkout -b g/h/i -l should create a branch and a log' \
@@@ -493,15 -477,6 +493,15 @@@ test_expect_success 'autosetuprebase al
        test "z$(git config branch.myr20.rebase)" = z
  '
  
 +test_expect_success 'autosetuprebase always on detached HEAD' '
 +      git config branch.autosetupmerge always &&
 +      test_when_finished git checkout master &&
 +      git checkout HEAD^0 &&
 +      git branch my11 &&
 +      test -z "$(git config branch.my11.remote)" &&
 +      test -z "$(git config branch.my11.merge)"
 +'
 +
  test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
        git config branch.autosetuprebase garbage &&
        test_must_fail git branch
diff --combined t/t3600-rm.sh
index cd093bd34730a3dba6eec7719d3b0170e517fb5e,66523bd8ba95b6075a06cb668e1eb5b0a0eff9f1..9fd28bcf3435f831fa24285dd703c82b8396d635
@@@ -96,7 -96,7 +96,7 @@@ test_expect_success FUNNYNAMES 
      "git rm -f 'space embedded' 'tab  embedded' 'newline
  embedded'"
  
 -test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
 +test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
        chmod a-w . &&
        test_must_fail git rm -f baz &&
        chmod 775 .
@@@ -240,11 -240,10 +240,10 @@@ test_expect_success 'refresh index befo
  
  test_expect_success 'choking "git rm" should not let it die with cruft' '
        git reset -q --hard &&
-       H=0000000000000000000000000000000000000000 &&
        i=0 &&
        while test $i -lt 12000
        do
-           echo "100644 $H 0   some-file-$i"
+           echo "100644 $_z40 0        some-file-$i"
            i=$(( $i + 1 ))
        done | git update-index --index-info &&
        git rm -n "some-file-*" | :;
diff --combined t/t4002-diff-basic.sh
index 9fb8ca06a84b3f3e60f466cc33bc2de786a9fc90,66e1a52c6c2fe351c0e30bfc07c7adeb4f0ff3ba..a5e8b830834f4b5feb531f8f4f4d08462325b1de
@@@ -126,15 -126,12 +126,12 @@@ cat >.test-recursive-AB <<\EO
  :100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M    Z/NM
  EOF
  
- x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
- x40="$x40$x40$x40$x40$x40$x40$x40$x40"
- z40='0000000000000000000000000000000000000000'
  cmp_diff_files_output () {
      # diff-files never reports additions.  Also it does not fill in the
      # object ID for the changed files because it wants you to look at the
      # filesystem.
      sed <"$2" >.test-tmp \
-       -e '/^:000000 /d;s/'$x40'\( [MCRNDU][0-9]*\)    /'$z40'\1       /' &&
+       -e '/^:000000 /d;s/'$_x40'\( [MCRNDU][0-9]*\)   /'$_z40'\1      /' &&
      test_cmp "$1" .test-tmp
  }
  
@@@ -205,8 -202,8 +202,8 @@@ test_expect_success 
      'rm -fr Z [A-Z][A-Z] &&
       git read-tree $tree_A &&
       git checkout-index -f -a &&
 -     git read-tree --reset $tree_O || return 1
 -     git update-index --refresh >/dev/null ;# this can exit non-zero
 +     git read-tree --reset $tree_O &&
 +     test_must_fail git update-index --refresh -q &&
       git diff-files >.test-a &&
       cmp_diff_files_output .test-a .test-recursive-OA'
  
@@@ -215,8 -212,8 +212,8 @@@ test_expect_success 
      'rm -fr Z [A-Z][A-Z] &&
       git read-tree $tree_B &&
       git checkout-index -f -a &&
 -     git read-tree --reset $tree_O || return 1
 -     git update-index --refresh >/dev/null ;# this can exit non-zero
 +     git read-tree --reset $tree_O &&
 +     test_must_fail git update-index --refresh -q &&
       git diff-files >.test-a &&
       cmp_diff_files_output .test-a .test-recursive-OB'
  
@@@ -225,8 -222,8 +222,8 @@@ test_expect_success 
      'rm -fr Z [A-Z][A-Z] &&
       git read-tree $tree_B &&
       git checkout-index -f -a &&
 -     git read-tree --reset $tree_A || return 1
 -     git update-index --refresh >/dev/null ;# this can exit non-zero
 +     git read-tree --reset $tree_A &&
 +     test_must_fail git update-index --refresh -q &&
       git diff-files >.test-a &&
       cmp_diff_files_output .test-a .test-recursive-AB'
  
index 241a74d2a20276d711944d3e203083b515486e77,fbe44a3271acb1a82eb9a62640d0e221b417d608..518bf9524e0b55181f3c440d202b69c12a0b314f
@@@ -5,7 -5,6 +5,6 @@@ test_description='difference in submodu
  . ./test-lib.sh
  . "$TEST_DIRECTORY"/diff-lib.sh
  
- _z40=0000000000000000000000000000000000000000
  test_expect_success setup '
        test_tick &&
        test_create_repo sub &&
@@@ -316,11 -315,11 +315,11 @@@ test_expect_success 'git diff (empty su
  test_expect_success 'conflicted submodule setup' '
  
        # 39 efs
 -      c=fffffffffffffffffffffffffffffffffffffff
 +      c=fffffffffffffffffffffffffffffffffffffff &&
        (
 -              echo "000000 $_z40 0    sub"
 -              echo "160000 1$c 1      sub"
 -              echo "160000 2$c 2      sub"
 +              echo "000000 $_z40 0    sub" &&
 +              echo "160000 1$c 1      sub" &&
 +              echo "160000 2$c 2      sub" &&
                echo "160000 3$c 3      sub"
        ) | git update-index --index-info &&
        echo >expect.nosub '\''diff --cc sub
index 14fcb1c7f5d8366827bc6e3256f118a757d2e996,d70fe2fe303c083f5cd0328758db9602e3af5381..c4104009e1c1f75c5d094b92d00d82a0d7ece2ae
@@@ -54,7 -54,7 +54,7 @@@ test_expect_success 'read-tree removes 
  '
  
  NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
- ZERO_SHA0=0000000000000000000000000000000000000000
  setup_absent() {
        test -f 1 && rm 1
        git update-index --remove 1 &&
@@@ -124,13 -124,13 +124,13 @@@ cat >expected <<EO
  Would remove expected
  Would remove result
  EOF
 -test_expect_success 'git-clean, absent case' '
 +test_expect_success C_LOCALE_OUTPUT 'git-clean, absent case' '
        setup_absent &&
        git clean -n > result &&
        test_cmp expected result
  '
  
 -test_expect_success 'git-clean, dirty case' '
 +test_expect_success C_LOCALE_OUTPUT 'git-clean, dirty case' '
        setup_dirty &&
        git clean -n > result &&
        test_cmp expected result
diff --combined t/test-lib.sh
index abc47f3abc5d925ed217d0d95f10bf078bc26034,7afa25fa835f4f5b0e1a18e3cf110296ea8ce948..8a274fbe7916c6c2b661757e496788ee8463e664
@@@ -43,25 -43,33 +43,25 @@@ TERM=dum
  export LANG LC_ALL PAGER TERM TZ
  EDITOR=:
  unset VISUAL
 -unset GIT_EDITOR
 -unset AUTHOR_DATE
 -unset AUTHOR_EMAIL
 -unset AUTHOR_NAME
 -unset COMMIT_AUTHOR_EMAIL
 -unset COMMIT_AUTHOR_NAME
  unset EMAIL
 -unset GIT_ALTERNATE_OBJECT_DIRECTORIES
 -unset GIT_AUTHOR_DATE
 +unset $(perl -e '
 +      my @env = keys %ENV;
 +      my $ok = join("|", qw(
 +              TRACE
 +              DEBUG
 +              USE_LOOKUP
 +              TEST
 +              .*_TEST
 +              PROVE
 +              VALGRIND
 +      ));
 +      my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
 +      print join("\n", @vars);
 +')
  GIT_AUTHOR_EMAIL=author@example.com
  GIT_AUTHOR_NAME='A U Thor'
 -unset GIT_COMMITTER_DATE
  GIT_COMMITTER_EMAIL=committer@example.com
  GIT_COMMITTER_NAME='C O Mitter'
 -unset GIT_DIFF_OPTS
 -unset GIT_DIR
 -unset GIT_WORK_TREE
 -unset GIT_EXTERNAL_DIFF
 -unset GIT_INDEX_FILE
 -unset GIT_OBJECT_DIRECTORY
 -unset GIT_CEILING_DIRECTORIES
 -unset SHA1_FILE_DIRECTORIES
 -unset SHA1_FILE_DIRECTORY
 -unset GIT_NOTES_REF
 -unset GIT_NOTES_DISPLAY_REF
 -unset GIT_NOTES_REWRITE_REF
 -unset GIT_NOTES_REWRITE_MODE
  GIT_MERGE_VERBOSITY=5
  export GIT_MERGE_VERBOSITY
  export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
@@@ -89,6 -97,9 +89,9 @@@ esa
  _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
  _x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
  
+ # Zero SHA-1
+ _z40=0000000000000000000000000000000000000000
  # Each test should start with something like this, after copyright notices:
  #
  # test_description='Description of this test...
@@@ -230,51 -241,14 +233,51 @@@ test_set_editor () 
  }
  
  test_decode_color () {
 -      sed     -e 's/.\[1m/<WHITE>/g' \
 -              -e 's/.\[31m/<RED>/g' \
 -              -e 's/.\[32m/<GREEN>/g' \
 -              -e 's/.\[33m/<YELLOW>/g' \
 -              -e 's/.\[34m/<BLUE>/g' \
 -              -e 's/.\[35m/<MAGENTA>/g' \
 -              -e 's/.\[36m/<CYAN>/g' \
 -              -e 's/.\[m/<RESET>/g'
 +      awk '
 +              function name(n) {
 +                      if (n == 0) return "RESET";
 +                      if (n == 1) return "BOLD";
 +                      if (n == 30) return "BLACK";
 +                      if (n == 31) return "RED";
 +                      if (n == 32) return "GREEN";
 +                      if (n == 33) return "YELLOW";
 +                      if (n == 34) return "BLUE";
 +                      if (n == 35) return "MAGENTA";
 +                      if (n == 36) return "CYAN";
 +                      if (n == 37) return "WHITE";
 +                      if (n == 40) return "BLACK";
 +                      if (n == 41) return "BRED";
 +                      if (n == 42) return "BGREEN";
 +                      if (n == 43) return "BYELLOW";
 +                      if (n == 44) return "BBLUE";
 +                      if (n == 45) return "BMAGENTA";
 +                      if (n == 46) return "BCYAN";
 +                      if (n == 47) return "BWHITE";
 +              }
 +              {
 +                      while (match($0, /\033\[[0-9;]*m/) != 0) {
 +                              printf "%s<", substr($0, 1, RSTART-1);
 +                              codes = substr($0, RSTART+2, RLENGTH-3);
 +                              if (length(codes) == 0)
 +                                      printf "%s", name(0)
 +                              else {
 +                                      n = split(codes, ary, ";");
 +                                      sep = "";
 +                                      for (i = 1; i <= n; i++) {
 +                                              printf "%s%s", sep, name(ary[i]);
 +                                              sep = ";"
 +                                      }
 +                              }
 +                              printf ">";
 +                              $0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
 +                      }
 +                      print
 +              }
 +      '
 +}
 +
 +nul_to_q () {
 +      perl -pe 'y/\000/Q/'
  }
  
  q_to_nul () {
@@@ -297,17 -271,6 +300,17 @@@ remove_cr () 
        tr '\015' Q | sed -e 's/Q$//'
  }
  
 +# In some bourne shell implementations, the "unset" builtin returns
 +# nonzero status when a variable to be unset was not set in the first
 +# place.
 +#
 +# Use sane_unset when that should not be considered an error.
 +
 +sane_unset () {
 +      unset "$@"
 +      return 0
 +}
 +
  test_tick () {
        if test -z "${test_tick+set}"
        then
@@@ -402,15 -365,6 +405,15 @@@ test_have_prereq () 
        test $total_prereq = $ok_prereq
  }
  
 +test_declared_prereq () {
 +      case ",$test_prereq," in
 +      *,$1,*)
 +              return 0
 +              ;;
 +      esac
 +      return 1
 +}
 +
  # You are not expected to call test_ok_ and test_failure_ directly, use
  # the text_expect_* functions instead.
  
@@@ -463,17 -417,17 +466,17 @@@ test_skip () 
                        break
                esac
        done
 -      if test -z "$to_skip" && test -n "$prereq" &&
 -         ! test_have_prereq "$prereq"
 +      if test -z "$to_skip" && test -n "$test_prereq" &&
 +         ! test_have_prereq "$test_prereq"
        then
                to_skip=t
        fi
        case "$to_skip" in
        t)
                of_prereq=
 -              if test "$missing_prereq" != "$prereq"
 +              if test "$missing_prereq" != "$test_prereq"
                then
 -                      of_prereq=" of $prereq"
 +                      of_prereq=" of $test_prereq"
                fi
  
                say_color skip >&3 "skipping test: $@"
  }
  
  test_expect_failure () {
 -      test "$#" = 3 && { prereq=$1; shift; } || prereq=
 +      test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
 +      export test_prereq
        if ! test_skip "$@"
        then
                say >&3 "checking known breakage: $2"
  }
  
  test_expect_success () {
 -      test "$#" = 3 && { prereq=$1; shift; } || prereq=
 +      test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
 +      export test_prereq
        if ! test_skip "$@"
        then
                say >&3 "expecting success: $2"
        echo >&3 ""
  }
  
 -test_expect_code () {
 -      test "$#" = 4 && { prereq=$1; shift; } || prereq=
 -      test "$#" = 3 ||
 -      error "bug in the test script: not 3 or 4 parameters to test-expect-code"
 -      if ! test_skip "$@"
 -      then
 -              say >&3 "expecting exit code $1: $3"
 -              test_run_ "$3"
 -              if [ "$?" = 0 -a "$eval_ret" = "$1" ]
 -              then
 -                      test_ok_ "$2"
 -              else
 -                      test_failure_ "$@"
 -              fi
 -      fi
 -      echo >&3 ""
 -}
 -
  # test_external runs external test scripts that provide continuous
  # test output about their progress, and succeeds/fails on
  # zero/non-zero exit code.  It outputs the test output on stdout even
  # Usage: test_external description command arguments...
  # Example: test_external 'Perl API' perl ../path/to/test.pl
  test_external () {
 -      test "$#" = 4 && { prereq=$1; shift; } || prereq=
 +      test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 3 ||
        error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
        descr="$1"
        shift
 +      export test_prereq
        if ! test_skip "$descr" "$@"
        then
                # Announce the script to reduce confusion about the
@@@ -639,28 -608,6 +642,28 @@@ test_path_is_missing () 
        fi
  }
  
 +# test_line_count checks that a file has the number of lines it
 +# ought to. For example:
 +#
 +#     test_expect_success 'produce exactly one line of output' '
 +#             do something >output &&
 +#             test_line_count = 1 output
 +#     '
 +#
 +# is like "test $(wc -l <output) = 1" except that it passes the
 +# output through when the number of lines is wrong.
 +
 +test_line_count () {
 +      if test $# != 3
 +      then
 +              error "bug in the test script: not 3 parameters to test_line_count"
 +      elif ! test $(wc -l <"$3") "$1" "$2"
 +      then
 +              echo "test_line_count: line count for $3 !$1 $2"
 +              cat "$3"
 +              return 1
 +      fi
 +}
  
  # This is not among top-level (test_expect_success | test_expect_failure)
  # but is a prefix that can be used in the test script, like:
@@@ -714,28 -661,6 +717,28 @@@ test_might_fail () 
        return 0
  }
  
 +# Similar to test_must_fail and test_might_fail, but check that a
 +# given command exited with a given exit code. Meant to be used as:
 +#
 +#     test_expect_success 'Merge with d/f conflicts' '
 +#             test_expect_code 1 git merge "merge msg" B master
 +#     '
 +
 +test_expect_code () {
 +      want_code=$1
 +      shift
 +      "$@"
 +      exit_code=$?
 +      if test $exit_code = $want_code
 +      then
 +              echo >&2 "test_expect_code: command exited with $exit_code: $*"
 +              return 0
 +      else
 +              echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
 +              return 1
 +      fi
 +}
 +
  # test_cmp is a helper function to compare actual and expected output.
  # You can use it like:
  #
@@@ -943,8 -868,8 +946,8 @@@ f
  GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
  unset GIT_CONFIG
  GIT_CONFIG_NOSYSTEM=1
 -GIT_CONFIG_NOGLOBAL=1
 -export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
 +GIT_ATTR_NOSYSTEM=1
 +export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
  
  . "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
  
@@@ -993,14 -918,14 +996,14 @@@ rm -fr "$test" || 
        exit 1
  }
  
 +HOME="$TRASH_DIRECTORY"
 +export HOME
 +
  test_create_repo "$test"
  # Use -P to resolve symlinks in our working directory so that the cwd
  # in subprocesses like git equals our $PWD (for pathname comparisons).
  cd -P "$test" || exit 1
  
 -HOME=$(pwd)
 -export HOME
 -
  this_test=${0##*/}
  this_test=${this_test%%-*}
  for skp in $GIT_SKIP_TESTS
@@@ -1048,35 -973,17 +1051,35 @@@ case $(uname -s) i
        # no POSIX permissions
        # backslashes in pathspec are converted to '/'
        # exec does not inherit the PID
 +      test_set_prereq MINGW
 +      test_set_prereq SED_STRIPS_CR
 +      ;;
 +*CYGWIN*)
 +      test_set_prereq POSIXPERM
 +      test_set_prereq EXECKEEPSPID
 +      test_set_prereq NOT_MINGW
 +      test_set_prereq SED_STRIPS_CR
        ;;
  *)
        test_set_prereq POSIXPERM
        test_set_prereq BSLASHPSPEC
        test_set_prereq EXECKEEPSPID
 +      test_set_prereq NOT_MINGW
        ;;
  esac
  
  test -z "$NO_PERL" && test_set_prereq PERL
  test -z "$NO_PYTHON" && test_set_prereq PYTHON
  
 +# Can we rely on git's output in the C locale?
 +if test -n "$GETTEXT_POISON"
 +then
 +      GIT_GETTEXT_POISON=YesPlease
 +      export GIT_GETTEXT_POISON
 +else
 +      test_set_prereq C_LOCALE_OUTPUT
 +fi
 +
  # test whether the filesystem supports symbolic links
  ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
  rm -f y