Code

Merge branch 'sb/dashless'
authorJunio C Hamano <gitster@pobox.com>
Thu, 17 Jul 2008 00:22:50 +0000 (17:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 17 Jul 2008 00:22:50 +0000 (17:22 -0700)
* sb/dashless:
  Make usage strings dash-less
  t/: Use "test_must_fail git" instead of "! git"
  t/test-lib.sh: exit with small negagive int is ok with test_must_fail

Conflicts:
builtin-blame.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-shortlog.c
git-am.sh
t/t4150-am.sh
t/t4200-rerere.sh

29 files changed:
1  2 
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-commit.c
builtin-describe.c
builtin-fmt-merge-msg.c
builtin-grep.c
builtin-init-db.c
builtin-log.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-rerere.c
builtin-rev-list.c
builtin-revert.c
builtin-shortlog.c
builtin-tag.c
builtin-upload-archive.c
contrib/examples/git-merge.sh
git-am.sh
git-cvsserver.perl
git-quiltimport.sh
git-svn.perl
help.c
index-pack.c
t/t4150-am.sh
t/t4200-rerere.sh
t/t7600-merge.sh
t/t9106-git-svn-commit-diff-clobber.sh

diff --combined builtin-archive.c
index 88204bf733ebdba79b1ac175da8ec26f6cfec597,5cca460129efd900fb194620a0ac91da5c71e38a..d5e3af879e7935ae3d5a6fad11951071e379855e
  #include "attr.h"
  
  static const char archive_usage[] = \
- "git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
+ "git archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
  
 -static struct archiver_desc
 -{
 -      const char *name;
 -      write_archive_fn_t write_archive;
 -      parse_extra_args_fn_t parse_extra;
 -} archivers[] = {
 -      { "tar", write_tar_archive, NULL },
 -      { "zip", write_zip_archive, parse_extra_zip_args },
 +#define USES_ZLIB_COMPRESSION 1
 +
 +const struct archiver archivers[] = {
 +      { "tar", write_tar_archive },
 +      { "zip", write_zip_archive, USES_ZLIB_COMPRESSION },
  };
  
  static int run_remote_archiver(const char *remote, int argc,
        return !!rv;
  }
  
 -static int init_archiver(const char *name, struct archiver *ar)
 +static const struct archiver *lookup_archiver(const char *name)
  {
 -      int rv = -1, i;
 +      int i;
  
        for (i = 0; i < ARRAY_SIZE(archivers); i++) {
 -              if (!strcmp(name, archivers[i].name)) {
 -                      memset(ar, 0, sizeof(*ar));
 -                      ar->name = archivers[i].name;
 -                      ar->write_archive = archivers[i].write_archive;
 -                      ar->parse_extra = archivers[i].parse_extra;
 -                      rv = 0;
 -                      break;
 -              }
 +              if (!strcmp(name, archivers[i].name))
 +                      return &archivers[i];
        }
 -      return rv;
 +      return NULL;
  }
  
  void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)
@@@ -136,12 -145,12 +136,12 @@@ void parse_treeish_arg(const char **arg
        ar_args->time = archive_time;
  }
  
 -int parse_archive_args(int argc, const char **argv, struct archiver *ar)
 +int parse_archive_args(int argc, const char **argv, const struct archiver **ar,
 +              struct archiver_args *args)
  {
 -      const char *extra_argv[MAX_EXTRA_ARGS];
 -      int extra_argc = 0;
        const char *format = "tar";
        const char *base = "";
 +      int compression_level = -1;
        int verbose = 0;
        int i;
  
                        i++;
                        break;
                }
 -              if (arg[0] == '-') {
 -                      if (extra_argc > MAX_EXTRA_ARGS - 1)
 -                              die("Too many extra options");
 -                      extra_argv[extra_argc++] = arg;
 +              if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') {
 +                      compression_level = arg[1] - '0';
                        continue;
                }
 +              if (arg[0] == '-')
 +                      die("Unknown argument: %s", arg);
                break;
        }
  
        /* We need at least one parameter -- tree-ish */
        if (argc - 1 < i)
                usage(archive_usage);
 -      if (init_archiver(format, ar) < 0)
 +      *ar = lookup_archiver(format);
 +      if (!*ar)
                die("Unknown archive format '%s'", format);
  
 -      if (extra_argc) {
 -              if (!ar->parse_extra)
 -                      die("'%s' format does not handle %s",
 -                          ar->name, extra_argv[0]);
 -              ar->args.extra = ar->parse_extra(extra_argc, extra_argv);
 +      if (compression_level != -1) {
 +              if ((*ar)->flags & USES_ZLIB_COMPRESSION)
 +                      zlib_compression_level = compression_level;
 +              else {
 +                      die("Argument not supported for format '%s': -%d",
 +                                      format, compression_level);
 +              }
        }
 -      ar->args.verbose = verbose;
 -      ar->args.base = base;
 +      args->verbose = verbose;
 +      args->base = base;
 +      args->baselen = strlen(base);
  
        return i;
  }
@@@ -233,8 -238,7 +233,8 @@@ static const char *extract_remote_arg(i
  
  int cmd_archive(int argc, const char **argv, const char *prefix)
  {
 -      struct archiver ar;
 +      const struct archiver *ar = NULL;
 +      struct archiver_args args;
        int tree_idx;
        const char *remote = NULL;
  
  
        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
  
 -      memset(&ar, 0, sizeof(ar));
 -      tree_idx = parse_archive_args(argc, argv, &ar);
 +      tree_idx = parse_archive_args(argc, argv, &ar, &args);
        if (prefix == NULL)
                prefix = setup_git_directory();
  
        argv += tree_idx;
 -      parse_treeish_arg(argv, &ar.args, prefix);
 -      parse_pathspec_arg(argv + 1, &ar.args);
 +      parse_treeish_arg(argv, &args, prefix);
 +      parse_pathspec_arg(argv + 1, &args);
  
 -      return ar.write_archive(&ar.args);
 +      return ar->write_archive(&args);
  }
diff --combined builtin-blame.c
index 8827f1e0c694be107b8c0e8112599c81b1c1c12a,91850a53e1a9a1b8ad9b478829a6cc9b32984b59..9bced3b2626eb72ccb4b8756b47163db4c026540
  #include "cache-tree.h"
  #include "path-list.h"
  #include "mailmap.h"
 +#include "parse-options.h"
  
- static char blame_usage[] = "git-blame [options] [rev-opts] [rev] [--] file";
 -static char blame_usage[] =
 -"git blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
 -"  -c                  Use the same output mode as git-annotate (Default: off)\n"
 -"  -b                  Show blank SHA-1 for boundary commits (Default: off)\n"
 -"  -l                  Show long commit SHA1 (Default: off)\n"
 -"  --root              Do not treat root commits as boundaries (Default: off)\n"
 -"  -t                  Show raw timestamp (Default: off)\n"
 -"  -f, --show-name     Show original filename (Default: auto)\n"
 -"  -n, --show-number   Show original linenumber (Default: off)\n"
 -"  -s                  Suppress author name and timestamp (Default: off)\n"
 -"  -p, --porcelain     Show in a format designed for machine consumption\n"
 -"  -w                  Ignore whitespace differences\n"
 -"  -L n,m              Process only line range n,m, counting from 1\n"
 -"  -M, -C              Find line movements within and across files\n"
 -"  --incremental       Show blame entries as we find them, incrementally\n"
 -"  --contents file     Use <file>'s contents as the final image\n"
 -"  -S revs-file        Use revisions from revs-file instead of calling git-rev-list\n";
++static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
 +
 +static const char *blame_opt_usage[] = {
 +      blame_usage,
 +      "",
 +      "[rev-opts] are documented in git-rev-list(1)",
 +      NULL
 +};
  
  static int longest_file;
  static int longest_author;
@@@ -35,7 -43,6 +35,7 @@@ static int max_orig_digits
  static int max_digits;
  static int max_score_digits;
  static int show_root;
 +static int reverse;
  static int blank_boundary;
  static int incremental;
  static int cmd_is_annotate;
@@@ -84,7 -91,7 +84,7 @@@ struct origin 
   * Given an origin, prepare mmfile_t structure to be used by the
   * diff machinery
   */
 -static char *fill_origin_blob(struct origin *o, mmfile_t *file)
 +static void fill_origin_blob(struct origin *o, mmfile_t *file)
  {
        if (!o->file.ptr) {
                enum object_type type;
        }
        else
                *file = o->file;
 -      return file->ptr;
  }
  
  /*
@@@ -153,10 -161,6 +153,10 @@@ struct blame_entry 
         */
        char guilty;
  
 +      /* true if the entry has been scanned for copies in the current parent
 +       */
 +      char scanned;
 +
        /* the line number of the first line of this group in the
         * suspect's file; internally all line numbers are 0 based.
         */
  struct scoreboard {
        /* the final commit (i.e. where we started digging from) */
        struct commit *final;
 -
 +      struct rev_info *revs;
        const char *path;
  
        /*
@@@ -1012,8 -1016,7 +1012,8 @@@ static int find_move_in_parent(struct s
        while (made_progress) {
                made_progress = 0;
                for (e = sb->ent; e; e = e->next) {
 -                      if (e->guilty || !same_suspect(e->suspect, target))
 +                      if (e->guilty || !same_suspect(e->suspect, target) ||
 +                          ent_score(sb, e) < blame_move_score)
                                continue;
                        find_copy_in_blob(sb, e, parent, split, &file_p);
                        if (split[1].suspect &&
@@@ -1038,7 -1041,6 +1038,7 @@@ struct blame_list 
   */
  static struct blame_list *setup_blame_list(struct scoreboard *sb,
                                           struct origin *target,
 +                                         int min_score,
                                           int *num_ents_p)
  {
        struct blame_entry *e;
        struct blame_list *blame_list = NULL;
  
        for (e = sb->ent, num_ents = 0; e; e = e->next)
 -              if (!e->guilty && same_suspect(e->suspect, target))
 +              if (!e->scanned && !e->guilty &&
 +                  same_suspect(e->suspect, target) &&
 +                  min_score < ent_score(sb, e))
                        num_ents++;
        if (num_ents) {
                blame_list = xcalloc(num_ents, sizeof(struct blame_list));
                for (e = sb->ent, i = 0; e; e = e->next)
 -                      if (!e->guilty && same_suspect(e->suspect, target))
 +                      if (!e->scanned && !e->guilty &&
 +                          same_suspect(e->suspect, target) &&
 +                          min_score < ent_score(sb, e))
                                blame_list[i++].ent = e;
        }
        *num_ents_p = num_ents;
        return blame_list;
  }
  
 +/*
 + * Reset the scanned status on all entries.
 + */
 +static void reset_scanned_flag(struct scoreboard *sb)
 +{
 +      struct blame_entry *e;
 +      for (e = sb->ent; e; e = e->next)
 +              e->scanned = 0;
 +}
 +
  /*
   * For lines target is suspected for, see if we can find code movement
   * across file boundary from the parent commit.  porigin is the path
@@@ -1090,7 -1078,7 +1090,7 @@@ static int find_copy_in_parent(struct s
        struct blame_list *blame_list;
        int num_ents;
  
 -      blame_list = setup_blame_list(sb, target, &num_ents);
 +      blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
        if (!blame_list)
                return 1; /* nothing remains for this target */
  
                                split_blame(sb, split, blame_list[j].ent);
                                made_progress = 1;
                        }
 +                      else
 +                              blame_list[j].ent->scanned = 1;
                        decref_split(split);
                }
                free(blame_list);
  
                if (!made_progress)
                        break;
 -              blame_list = setup_blame_list(sb, target, &num_ents);
 +              blame_list = setup_blame_list(sb, target, blame_copy_score, &num_ents);
                if (!blame_list) {
                        retval = 1;
                        break;
                }
        }
 +      reset_scanned_flag(sb);
        diff_flush(&diff_opts);
        diff_tree_release_paths(&diff_opts);
        return retval;
@@@ -1207,48 -1192,18 +1207,48 @@@ static void pass_whole_blame(struct sco
        }
  }
  
 -#define MAXPARENT 16
 +/*
 + * We pass blame from the current commit to its parents.  We keep saying
 + * "parent" (and "porigin"), but what we mean is to find scapegoat to
 + * exonerate ourselves.
 + */
 +static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
 +{
 +      if (!reverse)
 +              return commit->parents;
 +      return lookup_decoration(&revs->children, &commit->object);
 +}
 +
 +static int num_scapegoats(struct rev_info *revs, struct commit *commit)
 +{
 +      int cnt;
 +      struct commit_list *l = first_scapegoat(revs, commit);
 +      for (cnt = 0; l; l = l->next)
 +              cnt++;
 +      return cnt;
 +}
 +
 +#define MAXSG 16
  
  static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
  {
 -      int i, pass;
 +      struct rev_info *revs = sb->revs;
 +      int i, pass, num_sg;
        struct commit *commit = origin->commit;
 -      struct commit_list *parent;
 -      struct origin *parent_origin[MAXPARENT], *porigin;
 -
 -      memset(parent_origin, 0, sizeof(parent_origin));
 +      struct commit_list *sg;
 +      struct origin *sg_buf[MAXSG];
 +      struct origin *porigin, **sg_origin = sg_buf;
 +
 +      num_sg = num_scapegoats(revs, commit);
 +      if (!num_sg)
 +              goto finish;
 +      else if (num_sg < ARRAY_SIZE(sg_buf))
 +              memset(sg_buf, 0, sizeof(sg_buf));
 +      else
 +              sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
  
 -      /* The first pass looks for unrenamed path to optimize for
 +      /*
 +       * The first pass looks for unrenamed path to optimize for
         * common cases, then we look for renames in the second pass.
         */
        for (pass = 0; pass < 2; pass++) {
                                       struct commit *, struct origin *);
                find = pass ? find_rename : find_origin;
  
 -              for (i = 0, parent = commit->parents;
 -                   i < MAXPARENT && parent;
 -                   parent = parent->next, i++) {
 -                      struct commit *p = parent->item;
 +              for (i = 0, sg = first_scapegoat(revs, commit);
 +                   i < num_sg && sg;
 +                   sg = sg->next, i++) {
 +                      struct commit *p = sg->item;
                        int j, same;
  
 -                      if (parent_origin[i])
 +                      if (sg_origin[i])
                                continue;
                        if (parse_commit(p))
                                continue;
                                goto finish;
                        }
                        for (j = same = 0; j < i; j++)
 -                              if (parent_origin[j] &&
 -                                  !hashcmp(parent_origin[j]->blob_sha1,
 +                              if (sg_origin[j] &&
 +                                  !hashcmp(sg_origin[j]->blob_sha1,
                                             porigin->blob_sha1)) {
                                        same = 1;
                                        break;
                                }
                        if (!same)
 -                              parent_origin[i] = porigin;
 +                              sg_origin[i] = porigin;
                        else
                                origin_decref(porigin);
                }
        }
  
        num_commits++;
 -      for (i = 0, parent = commit->parents;
 -           i < MAXPARENT && parent;
 -           parent = parent->next, i++) {
 -              struct origin *porigin = parent_origin[i];
 +      for (i = 0, sg = first_scapegoat(revs, commit);
 +           i < num_sg && sg;
 +           sg = sg->next, i++) {
 +              struct origin *porigin = sg_origin[i];
                if (!porigin)
                        continue;
                if (pass_blame_to_parent(sb, origin, porigin))
         * Optionally find moves in parents' files.
         */
        if (opt & PICKAXE_BLAME_MOVE)
 -              for (i = 0, parent = commit->parents;
 -                   i < MAXPARENT && parent;
 -                   parent = parent->next, i++) {
 -                      struct origin *porigin = parent_origin[i];
 +              for (i = 0, sg = first_scapegoat(revs, commit);
 +                   i < num_sg && sg;
 +                   sg = sg->next, i++) {
 +                      struct origin *porigin = sg_origin[i];
                        if (!porigin)
                                continue;
                        if (find_move_in_parent(sb, origin, porigin))
         * Optionally find copies from parents' files.
         */
        if (opt & PICKAXE_BLAME_COPY)
 -              for (i = 0, parent = commit->parents;
 -                   i < MAXPARENT && parent;
 -                   parent = parent->next, i++) {
 -                      struct origin *porigin = parent_origin[i];
 -                      if (find_copy_in_parent(sb, origin, parent->item,
 +              for (i = 0, sg = first_scapegoat(revs, commit);
 +                   i < num_sg && sg;
 +                   sg = sg->next, i++) {
 +                      struct origin *porigin = sg_origin[i];
 +                      if (find_copy_in_parent(sb, origin, sg->item,
                                                porigin, opt))
                                goto finish;
                }
  
   finish:
 -      for (i = 0; i < MAXPARENT; i++) {
 -              if (parent_origin[i]) {
 -                      drop_origin_blob(parent_origin[i]);
 -                      origin_decref(parent_origin[i]);
 +      for (i = 0; i < num_sg; i++) {
 +              if (sg_origin[i]) {
 +                      drop_origin_blob(sg_origin[i]);
 +                      origin_decref(sg_origin[i]);
                }
        }
        drop_origin_blob(origin);
 +      if (sg_buf != sg_origin)
 +              free(sg_origin);
  }
  
  /*
@@@ -1534,10 -1487,8 +1534,10 @@@ static void found_guilty_entry(struct b
   * is still unknown, pick one blame_entry, and allow its current
   * suspect to pass blames to its parents.
   */
 -static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
 +static void assign_blame(struct scoreboard *sb, int opt)
  {
 +      struct rev_info *revs = sb->revs;
 +
        while (1) {
                struct blame_entry *ent;
                struct commit *commit;
                commit = suspect->commit;
                if (!commit->object.parsed)
                        parse_commit(commit);
 -              if (!(commit->object.flags & UNINTERESTING) &&
 -                  !(revs->max_age != -1 && commit->date < revs->max_age))
 +              if (reverse ||
 +                  (!(commit->object.flags & UNINTERESTING) &&
 +                   !(revs->max_age != -1 && commit->date < revs->max_age)))
                        pass_blame(sb, suspect, opt);
                else {
                        commit->object.flags |= UNINTERESTING;
@@@ -2056,10 -2006,6 +2056,10 @@@ static int git_blame_config(const char 
        return git_default_config(var, value, cb);
  }
  
 +/*
 + * Prepare a dummy commit that represents the work tree (or staged) item.
 + * Note that annotating work tree item never works in the reverse.
 + */
  static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
  {
        struct commit *commit;
        return commit;
  }
  
 +static const char *prepare_final(struct scoreboard *sb)
 +{
 +      int i;
 +      const char *final_commit_name = NULL;
 +      struct rev_info *revs = sb->revs;
 +
 +      /*
 +       * There must be one and only one positive commit in the
 +       * revs->pending array.
 +       */
 +      for (i = 0; i < revs->pending.nr; i++) {
 +              struct object *obj = revs->pending.objects[i].item;
 +              if (obj->flags & UNINTERESTING)
 +                      continue;
 +              while (obj->type == OBJ_TAG)
 +                      obj = deref_tag(obj, NULL, 0);
 +              if (obj->type != OBJ_COMMIT)
 +                      die("Non commit %s?", revs->pending.objects[i].name);
 +              if (sb->final)
 +                      die("More than one commit to dig from %s and %s?",
 +                          revs->pending.objects[i].name,
 +                          final_commit_name);
 +              sb->final = (struct commit *) obj;
 +              final_commit_name = revs->pending.objects[i].name;
 +      }
 +      return final_commit_name;
 +}
 +
 +static const char *prepare_initial(struct scoreboard *sb)
 +{
 +      int i;
 +      const char *final_commit_name = NULL;
 +      struct rev_info *revs = sb->revs;
 +
 +      /*
 +       * There must be one and only one negative commit, and it must be
 +       * the boundary.
 +       */
 +      for (i = 0; i < revs->pending.nr; i++) {
 +              struct object *obj = revs->pending.objects[i].item;
 +              if (!(obj->flags & UNINTERESTING))
 +                      continue;
 +              while (obj->type == OBJ_TAG)
 +                      obj = deref_tag(obj, NULL, 0);
 +              if (obj->type != OBJ_COMMIT)
 +                      die("Non commit %s?", revs->pending.objects[i].name);
 +              if (sb->final)
 +                      die("More than one commit to dig down to %s and %s?",
 +                          revs->pending.objects[i].name,
 +                          final_commit_name);
 +              sb->final = (struct commit *) obj;
 +              final_commit_name = revs->pending.objects[i].name;
 +      }
 +      if (!final_commit_name)
 +              die("No commit to dig down to?");
 +      return final_commit_name;
 +}
 +
 +static int blame_copy_callback(const struct option *option, const char *arg, int unset)
 +{
 +      int *opt = option->value;
 +
 +      /*
 +       * -C enables copy from removed files;
 +       * -C -C enables copy from existing files, but only
 +       *       when blaming a new file;
 +       * -C -C -C enables copy from existing files for
 +       *          everybody
 +       */
 +      if (*opt & PICKAXE_BLAME_COPY_HARDER)
 +              *opt |= PICKAXE_BLAME_COPY_HARDEST;
 +      if (*opt & PICKAXE_BLAME_COPY)
 +              *opt |= PICKAXE_BLAME_COPY_HARDER;
 +      *opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
 +
 +      if (arg)
 +              blame_copy_score = parse_score(arg);
 +      return 0;
 +}
 +
 +static int blame_move_callback(const struct option *option, const char *arg, int unset)
 +{
 +      int *opt = option->value;
 +
 +      *opt |= PICKAXE_BLAME_MOVE;
 +
 +      if (arg)
 +              blame_move_score = parse_score(arg);
 +      return 0;
 +}
 +
 +static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
 +{
 +      const char **bottomtop = option->value;
 +      if (!arg)
 +              return -1;
 +      if (*bottomtop)
 +              die("More than one '-L n,m' option given");
 +      *bottomtop = arg;
 +      return 0;
 +}
 +
  int cmd_blame(int argc, const char **argv, const char *prefix)
  {
        struct rev_info revs;
        struct scoreboard sb;
        struct origin *o;
        struct blame_entry *ent;
 -      int i, seen_dashdash, unk, opt;
 -      long bottom, top, lno;
 -      int output_option = 0;
 -      int show_stats = 0;
 -      const char *revs_file = NULL;
 +      long dashdash_pos, bottom, top, lno;
        const char *final_commit_name = NULL;
        enum object_type type;
 -      const char *bottomtop = NULL;
 -      const char *contents_from = NULL;
 +
 +      static const char *bottomtop = NULL;
 +      static int output_option = 0, opt = 0;
 +      static int show_stats = 0;
 +      static const char *revs_file = NULL;
 +      static const char *contents_from = NULL;
 +      static const struct option options[] = {
 +              OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"),
 +              OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"),
 +              OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"),
 +              OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"),
 +              OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE),
 +              OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
 +              OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
 +              OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
 +              OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
 +              OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
 +              OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
 +              OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
 +              OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
 +              OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
 +              OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
 +              { OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
 +              { OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
 +              OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
 +              OPT_END()
 +      };
 +
 +      struct parse_opt_ctx_t ctx;
  
        cmd_is_annotate = !strcmp(argv[0], "annotate");
  
        git_config(git_blame_config, NULL);
 +      init_revisions(&revs, NULL);
        save_commit_buffer = 0;
 -
 -      opt = 0;
 -      seen_dashdash = 0;
 -      for (unk = i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 -              if (*arg != '-')
 -                      break;
 -              else if (!strcmp("-b", arg))
 -                      blank_boundary = 1;
 -              else if (!strcmp("--root", arg))
 -                      show_root = 1;
 -              else if (!strcmp(arg, "--show-stats"))
 -                      show_stats = 1;
 -              else if (!strcmp("-c", arg))
 -                      output_option |= OUTPUT_ANNOTATE_COMPAT;
 -              else if (!strcmp("-t", arg))
 -                      output_option |= OUTPUT_RAW_TIMESTAMP;
 -              else if (!strcmp("-l", arg))
 -                      output_option |= OUTPUT_LONG_OBJECT_NAME;
 -              else if (!strcmp("-s", arg))
 -                      output_option |= OUTPUT_NO_AUTHOR;
 -              else if (!strcmp("-w", arg))
 -                      xdl_opts |= XDF_IGNORE_WHITESPACE;
 -              else if (!strcmp("-S", arg) && ++i < argc)
 -                      revs_file = argv[i];
 -              else if (!prefixcmp(arg, "-M")) {
 -                      opt |= PICKAXE_BLAME_MOVE;
 -                      blame_move_score = parse_score(arg+2);
 +      dashdash_pos = 0;
 +
 +      parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
 +                          PARSE_OPT_KEEP_ARGV0);
 +      for (;;) {
 +              switch (parse_options_step(&ctx, options, blame_opt_usage)) {
 +              case PARSE_OPT_HELP:
 +                      exit(129);
 +              case PARSE_OPT_DONE:
 +                      if (ctx.argv[0])
 +                              dashdash_pos = ctx.cpidx;
 +                      goto parse_done;
                }
 -              else if (!prefixcmp(arg, "-C")) {
 -                      /*
 -                       * -C enables copy from removed files;
 -                       * -C -C enables copy from existing files, but only
 -                       *       when blaming a new file;
 -                       * -C -C -C enables copy from existing files for
 -                       *          everybody
 -                       */
 -                      if (opt & PICKAXE_BLAME_COPY_HARDER)
 -                              opt |= PICKAXE_BLAME_COPY_HARDEST;
 -                      if (opt & PICKAXE_BLAME_COPY)
 -                              opt |= PICKAXE_BLAME_COPY_HARDER;
 -                      opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
 -                      blame_copy_score = parse_score(arg+2);
 -              }
 -              else if (!prefixcmp(arg, "-L")) {
 -                      if (!arg[2]) {
 -                              if (++i >= argc)
 -                                      usage(blame_usage);
 -                              arg = argv[i];
 -                      }
 -                      else
 -                              arg += 2;
 -                      if (bottomtop)
 -                              die("More than one '-L n,m' option given");
 -                      bottomtop = arg;
 -              }
 -              else if (!strcmp("--contents", arg)) {
 -                      if (++i >= argc)
 -                              usage(blame_usage);
 -                      contents_from = argv[i];
 -              }
 -              else if (!strcmp("--incremental", arg))
 -                      incremental = 1;
 -              else if (!strcmp("--score-debug", arg))
 -                      output_option |= OUTPUT_SHOW_SCORE;
 -              else if (!strcmp("-f", arg) ||
 -                       !strcmp("--show-name", arg))
 -                      output_option |= OUTPUT_SHOW_NAME;
 -              else if (!strcmp("-n", arg) ||
 -                       !strcmp("--show-number", arg))
 -                      output_option |= OUTPUT_SHOW_NUMBER;
 -              else if (!strcmp("-p", arg) ||
 -                       !strcmp("--porcelain", arg))
 -                      output_option |= OUTPUT_PORCELAIN;
 -              else if (!strcmp("--", arg)) {
 -                      seen_dashdash = 1;
 -                      i++;
 -                      break;
 +
 +              if (!strcmp(ctx.argv[0], "--reverse")) {
 +                      ctx.argv[0] = "--children";
 +                      reverse = 1;
                }
 -              else
 -                      argv[unk++] = arg;
 +              parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
        }
 +parse_done:
 +      argc = parse_options_end(&ctx);
  
        if (!blame_move_score)
                blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
         *
         * The remaining are:
         *
 -       * (1) if seen_dashdash, its either
 -       *     "-options -- <path>" or
 -       *     "-options -- <path> <rev>".
 -       *     but the latter is allowed only if there is no
 -       *     options that we passed to revision machinery.
 +       * (1) if dashdash_pos != 0, its either
 +       *     "blame [revisions] -- <path>" or
 +       *     "blame -- <path> <rev>"
         *
 -       * (2) otherwise, we may have "--" somewhere later and
 -       *     might be looking at the first one of multiple 'rev'
 -       *     parameters (e.g. " master ^next ^maint -- path").
 -       *     See if there is a dashdash first, and give the
 -       *     arguments before that to revision machinery.
 -       *     After that there must be one 'path'.
 +       * (2) otherwise, its one of the two:
 +       *     "blame [revisions] <path>"
 +       *     "blame <path> <rev>"
         *
 -       * (3) otherwise, its one of the three:
 -       *     "-options <path> <rev>"
 -       *     "-options <rev> <path>"
 -       *     "-options <path>"
 -       *     but again the first one is allowed only if
 -       *     there is no options that we passed to revision
 -       *     machinery.
 +       * Note that we must strip out <path> from the arguments: we do not
 +       * want the path pruning but we may want "bottom" processing.
         */
 -
 -      if (seen_dashdash) {
 -              /* (1) */
 -              if (argc <= i)
 -                      usage(blame_usage);
 -              path = add_prefix(prefix, argv[i]);
 -              if (i + 1 == argc - 1) {
 -                      if (unk != 1)
 -                              usage(blame_usage);
 -                      argv[unk++] = argv[i + 1];
 +      if (dashdash_pos) {
 +              switch (argc - dashdash_pos - 1) {
 +              case 2: /* (1b) */
 +                      if (argc != 4)
 +                              usage_with_options(blame_opt_usage, options);
 +                      /* reorder for the new way: <rev> -- <path> */
 +                      argv[1] = argv[3];
 +                      argv[3] = argv[2];
 +                      argv[2] = "--";
 +                      /* FALLTHROUGH */
 +              case 1: /* (1a) */
 +                      path = add_prefix(prefix, argv[--argc]);
 +                      argv[argc] = NULL;
 +                      break;
 +              default:
 +                      usage_with_options(blame_opt_usage, options);
                }
 -              else if (i + 1 != argc)
 -                      /* garbage at end */
 -                      usage(blame_usage);
 -      }
 -      else {
 -              int j;
 -              for (j = i; !seen_dashdash && j < argc; j++)
 -                      if (!strcmp(argv[j], "--"))
 -                              seen_dashdash = j;
 -              if (seen_dashdash) {
 -                      /* (2) */
 -                      if (seen_dashdash + 1 != argc - 1)
 -                              usage(blame_usage);
 -                      path = add_prefix(prefix, argv[seen_dashdash + 1]);
 -                      for (j = i; j < seen_dashdash; j++)
 -                              argv[unk++] = argv[j];
 +      } else {
 +              if (argc < 2)
 +                      usage_with_options(blame_opt_usage, options);
 +              path = add_prefix(prefix, argv[argc - 1]);
 +              if (argc == 3 && !has_path_in_work_tree(path)) { /* (2b) */
 +                      path = add_prefix(prefix, argv[1]);
 +                      argv[1] = argv[2];
                }
 -              else {
 -                      /* (3) */
 -                      if (argc <= i)
 -                              usage(blame_usage);
 -                      path = add_prefix(prefix, argv[i]);
 -                      if (i + 1 == argc - 1) {
 -                              final_commit_name = argv[i + 1];
 -
 -                              /* if (unk == 1) we could be getting
 -                               * old-style
 -                               */
 -                              if (unk == 1 && !has_path_in_work_tree(path)) {
 -                                      path = add_prefix(prefix, argv[i + 1]);
 -                                      final_commit_name = argv[i];
 -                              }
 -                      }
 -                      else if (i != argc - 1)
 -                              usage(blame_usage); /* garbage at end */
 +              argv[argc - 1] = "--";
  
 -                      setup_work_tree();
 -                      if (!has_path_in_work_tree(path))
 -                              die("cannot stat path %s: %s",
 -                                  path, strerror(errno));
 -              }
 +              setup_work_tree();
 +              if (!has_path_in_work_tree(path))
 +                      die("cannot stat path %s: %s", path, strerror(errno));
        }
  
 -      if (final_commit_name)
 -              argv[unk++] = final_commit_name;
 -
 -      /*
 -       * Now we got rev and path.  We do not want the path pruning
 -       * but we may want "bottom" processing.
 -       */
 -      argv[unk++] = "--"; /* terminate the rev name */
 -      argv[unk] = NULL;
 -
 -      init_revisions(&revs, NULL);
 -      setup_revisions(unk, argv, &revs, NULL);
 +      setup_revisions(argc, argv, &revs, NULL);
        memset(&sb, 0, sizeof(sb));
  
 -      /*
 -       * There must be one and only one positive commit in the
 -       * revs->pending array.
 -       */
 -      for (i = 0; i < revs.pending.nr; i++) {
 -              struct object *obj = revs.pending.objects[i].item;
 -              if (obj->flags & UNINTERESTING)
 -                      continue;
 -              while (obj->type == OBJ_TAG)
 -                      obj = deref_tag(obj, NULL, 0);
 -              if (obj->type != OBJ_COMMIT)
 -                      die("Non commit %s?",
 -                          revs.pending.objects[i].name);
 -              if (sb.final)
 -                      die("More than one commit to dig from %s and %s?",
 -                          revs.pending.objects[i].name,
 -                          final_commit_name);
 -              sb.final = (struct commit *) obj;
 -              final_commit_name = revs.pending.objects[i].name;
 -      }
 +      sb.revs = &revs;
 +      if (!reverse)
 +              final_commit_name = prepare_final(&sb);
 +      else if (contents_from)
 +              die("--contents and --children do not blend well.");
 +      else
 +              final_commit_name = prepare_initial(&sb);
  
        if (!sb.final) {
                /*
        if (!incremental)
                setup_pager();
  
 -      assign_blame(&sb, &revs, opt);
 +      assign_blame(&sb, opt);
  
        if (incremental)
                return 0;
diff --combined builtin-branch.c
index 7dae22c0197c2d3d062b0fa34c15b6c9a489eb31,5cc3771ddd77f63b02e1f20b8b4f74390aa1f66a..b885bd132b5e5b2979afd451f4e81f184ec9c969
  #include "branch.h"
  
  static const char * const builtin_branch_usage[] = {
-       "git-branch [options] [-r | -a] [--merged | --no-merged]",
-       "git-branch [options] [-l] [-f] <branchname> [<start-point>]",
-       "git-branch [options] [-r] (-d | -D) <branchname>",
-       "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>",
+       "git branch [options] [-r | -a] [--merged | --no-merged]",
+       "git branch [options] [-l] [-f] <branchname> [<start-point>]",
+       "git branch [options] [-r] (-d | -D) <branchname>",
+       "git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
        NULL
  };
  
@@@ -46,12 -46,7 +46,12 @@@ enum color_branch 
        COLOR_BRANCH_CURRENT = 4,
  };
  
 -static int mergefilter = -1;
 +static enum merge_filter {
 +      NO_FILTER = 0,
 +      SHOW_NOT_MERGED,
 +      SHOW_MERGED,
 +} merge_filter;
 +static unsigned char merge_filter_ref[20];
  
  static int parse_branch_color_slot(const char *var, int ofs)
  {
@@@ -239,15 -234,13 +239,15 @@@ static int append_ref(const char *refna
        if ((kind & ref_list->kinds) == 0)
                return 0;
  
 -      if (mergefilter > -1) {
 +      if (merge_filter != NO_FILTER) {
                branch.item = lookup_commit_reference_gently(sha1, 1);
                if (!branch.item)
                        die("Unable to lookup tip of branch %s", refname);
 -              if (mergefilter == 0 && has_commit(head_sha1, &branch))
 +              if (merge_filter == SHOW_NOT_MERGED &&
 +                  has_commit(merge_filter_ref, &branch))
                        return 0;
 -              if (mergefilter == 1 && !has_commit(head_sha1, &branch))
 +              if (merge_filter == SHOW_MERGED &&
 +                  !has_commit(merge_filter_ref, &branch))
                        return 0;
        }
  
@@@ -289,21 -282,6 +289,21 @@@ static int ref_cmp(const void *r1, cons
        return strcmp(c1->name, c2->name);
  }
  
 +static void fill_tracking_info(char *stat, const char *branch_name)
 +{
 +      int ours, theirs;
 +      struct branch *branch = branch_get(branch_name);
 +
 +      if (!stat_tracking_info(branch, &ours, &theirs) || (!ours && !theirs))
 +              return;
 +      if (!ours)
 +              sprintf(stat, "[behind %d] ", theirs);
 +      else if (!theirs)
 +              sprintf(stat, "[ahead %d] ", ours);
 +      else
 +              sprintf(stat, "[ahead %d, behind %d] ", ours, theirs);
 +}
 +
  static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                           int abbrev, int current)
  {
        if (verbose) {
                struct strbuf subject;
                const char *sub = " **** invalid ref ****";
 +              char stat[128];
  
                strbuf_init(&subject, 0);
 +              stat[0] = '\0';
  
                commit = lookup_commit(item->sha1);
                if (commit && !parse_commit(commit)) {
                                            &subject, 0, NULL, NULL, 0, 0);
                        sub = subject.buf;
                }
 -              printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
 +
 +              if (item->kind == REF_LOCAL_BRANCH)
 +                      fill_tracking_info(stat, item->name);
 +
 +              printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
                       maxwidth, item->name,
                       branch_get_color(COLOR_BRANCH_RESET),
 -                     find_unique_abbrev(item->sha1, abbrev), sub);
 +                     find_unique_abbrev(item->sha1, abbrev),
 +                     stat, sub);
                strbuf_release(&subject);
        } else {
                printf("%c %s%s%s\n", c, branch_get_color(color), item->name,
@@@ -450,20 -421,6 +450,20 @@@ static int opt_parse_with_commit(const 
        return 0;
  }
  
 +static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
 +{
 +      merge_filter = ((opt->long_name[0] == 'n')
 +                      ? SHOW_NOT_MERGED
 +                      : SHOW_MERGED);
 +      if (unset)
 +              merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */
 +      if (!arg)
 +              arg = "HEAD";
 +      if (get_sha1(arg, merge_filter_ref))
 +              die("malformed object name %s", arg);
 +      return 0;
 +}
 +
  int cmd_branch(int argc, const char **argv, const char *prefix)
  {
        int delete = 0, rename = 0, force_create = 0;
                OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
                OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
                        REF_REMOTE_BRANCH),
 -              OPT_CALLBACK(0, "contains", &with_commit, "commit",
 -                           "print only branches that contain the commit",
 -                           opt_parse_with_commit),
 +              {
 +                      OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
 +                      "print only branches that contain the commit",
 +                      PARSE_OPT_LASTARG_DEFAULT,
 +                      opt_parse_with_commit, (intptr_t)"HEAD",
 +              },
                {
                        OPTION_CALLBACK, 0, "with", &with_commit, "commit",
                        "print only branches that contain the commit",
 -                      PARSE_OPT_HIDDEN, opt_parse_with_commit,
 +                      PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
 +                      opt_parse_with_commit, (intptr_t) "HEAD",
                },
                OPT__ABBREV(&abbrev),
  
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
                OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
                OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
 -              OPT_SET_INT(0, "merged", &mergefilter, "list only merged branches", 1),
 +              {
 +                      OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
 +                      "commit", "print only not merged branches",
 +                      PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
 +                      opt_parse_merge_filter, (intptr_t) "HEAD",
 +              },
 +              {
 +                      OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
 +                      "commit", "print only merged branches",
 +                      PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
 +                      opt_parse_merge_filter, (intptr_t) "HEAD",
 +              },
                OPT_END(),
        };
  
                branch_use_color = git_use_color_default;
  
        track = git_branch_track;
 -      argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
 -      if (!!delete + !!rename + !!force_create > 1)
 -              usage_with_options(builtin_branch_usage, options);
  
        head = resolve_ref("HEAD", head_sha1, 0, NULL);
        if (!head)
                        die("HEAD not found below refs/heads!");
                head += 11;
        }
 +      hashcpy(merge_filter_ref, head_sha1);
 +
 +      argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
 +      if (!!delete + !!rename + !!force_create > 1)
 +              usage_with_options(builtin_branch_usage, options);
  
        if (delete)
                return delete_branches(argc, argv, delete > 1, kinds);
diff --combined builtin-commit.c
index bdc83df55bcaeb51eb51b1a3198033eb69236409,41cf5f2dc2ca6b1de455268d0e700bfa3ef20451..ed3fe3f7ee033e09f87c0a86dc7db70b13c6a262
  #include "utf8.h"
  #include "parse-options.h"
  #include "path-list.h"
 +#include "rerere.h"
  #include "unpack-trees.h"
  
  static const char * const builtin_commit_usage[] = {
-       "git-commit [options] [--] <filepattern>...",
+       "git commit [options] [--] <filepattern>...",
        NULL
  };
  
  static const char * const builtin_status_usage[] = {
-       "git-status [options] [--] <filepattern>...",
+       "git status [options] [--] <filepattern>...",
        NULL
  };
  
diff --combined builtin-describe.c
index 62a19056ef05989b0b48d0939758a66175b3f442,5dabca950d26a2468ba0ab241ab184cda3c761b5..ec404c839b6542deb4e15ca342fd3c0afbbedd2e
@@@ -10,7 -10,7 +10,7 @@@
  #define MAX_TAGS      (FLAG_BITS - 1)
  
  static const char * const describe_usage[] = {
-       "git-describe [options] <committish>*",
+       "git describe [options] <committish>*",
        NULL
  };
  
@@@ -20,7 -20,7 +20,7 @@@ static int tags;      /* But allow any tags 
  static int longformat;
  static int abbrev = DEFAULT_ABBREV;
  static int max_candidates = 10;
 -const char *pattern = NULL;
 +static const char *pattern;
  static int always;
  
  struct commit_name {
diff --combined builtin-fmt-merge-msg.c
index dbd7d2ddae2d4b44bcccf9977d435cf334a0c418,b34b01ff510502da8fc1142c1f83746f41e2fd03..df02ba7afdd615492361a1897a9dedd6ab233c96
@@@ -6,7 -6,7 +6,7 @@@
  #include "tag.h"
  
  static const char *fmt_merge_msg_usage =
-       "git-fmt-merge-msg [--log] [--no-log] [--file <file>]";
+       "git fmt-merge-msg [--log] [--no-log] [--file <file>]";
  
  static int merge_summary;
  
@@@ -159,24 -159,23 +159,24 @@@ static int handle_line(char *line
  }
  
  static void print_joined(const char *singular, const char *plural,
 -              struct list *list)
 +              struct list *list, struct strbuf *out)
  {
        if (list->nr == 0)
                return;
        if (list->nr == 1) {
 -              printf("%s%s", singular, list->list[0]);
 +              strbuf_addf(out, "%s%s", singular, list->list[0]);
        } else {
                int i;
 -              printf("%s", plural);
 +              strbuf_addstr(out, plural);
                for (i = 0; i < list->nr - 1; i++)
 -                      printf("%s%s", i > 0 ? ", " : "", list->list[i]);
 -              printf(" and %s", list->list[list->nr - 1]);
 +                      strbuf_addf(out, "%s%s", i > 0 ? ", " : "", list->list[i]);
 +              strbuf_addf(out, " and %s", list->list[list->nr - 1]);
        }
  }
  
  static void shortlog(const char *name, unsigned char *sha1,
 -              struct commit *head, struct rev_info *rev, int limit)
 +              struct commit *head, struct rev_info *rev, int limit,
 +              struct strbuf *out)
  {
        int i, count = 0;
        struct commit *commit;
        }
  
        if (count > limit)
 -              printf("\n* %s: (%d commits)\n", name, count);
 +              strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
        else
 -              printf("\n* %s:\n", name);
 +              strbuf_addf(out, "\n* %s:\n", name);
  
        for (i = 0; i < subjects.nr; i++)
                if (i >= limit)
 -                      printf("  ...\n");
 +                      strbuf_addf(out, "  ...\n");
                else
 -                      printf("  %s\n", subjects.list[i]);
 +                      strbuf_addf(out, "  %s\n", subjects.list[i]);
  
        clear_commit_marks((struct commit *)branch, flags);
        clear_commit_marks(head, flags);
        free_list(&subjects);
  }
  
 -int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 -{
 -      int limit = 20, i = 0;
 +int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
 +      int limit = 20, i = 0, pos = 0;
        char line[1024];
 -      FILE *in = stdin;
 -      const char *sep = "";
 +      char *p = line, *sep = "";
        unsigned char head_sha1[20];
        const char *current_branch;
  
 -      git_config(fmt_merge_msg_config, NULL);
 -
 -      while (argc > 1) {
 -              if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary"))
 -                      merge_summary = 1;
 -              else if (!strcmp(argv[1], "--no-log")
 -                              || !strcmp(argv[1], "--no-summary"))
 -                      merge_summary = 0;
 -              else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
 -                      if (argc < 3)
 -                              die ("Which file?");
 -                      if (!strcmp(argv[2], "-"))
 -                              in = stdin;
 -                      else {
 -                              fclose(in);
 -                              in = fopen(argv[2], "r");
 -                              if (!in)
 -                                      die("cannot open %s", argv[2]);
 -                      }
 -                      argc--; argv++;
 -              } else
 -                      break;
 -              argc--; argv++;
 -      }
 -
 -      if (argc > 1)
 -              usage(fmt_merge_msg_usage);
 -
        /* get current branch */
        current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
        if (!current_branch)
        if (!prefixcmp(current_branch, "refs/heads/"))
                current_branch += 11;
  
 -      while (fgets(line, sizeof(line), in)) {
 +      /* get a line */
 +      while (pos < in->len) {
 +              int len;
 +              char *newline;
 +
 +              p = in->buf + pos;
 +              newline = strchr(p, '\n');
 +              len = newline ? newline - p : strlen(p);
 +              pos += len + !!newline;
                i++;
 -              if (line[0] == 0)
 -                      continue;
 -              if (handle_line(line))
 -                      die ("Error in line %d: %s", i, line);
 +              p[len] = 0;
 +              if (handle_line(p))
 +                      die ("Error in line %d: %.*s", i, len, p);
        }
  
 -      printf("Merge ");
 +      strbuf_addstr(out, "Merge ");
        for (i = 0; i < srcs.nr; i++) {
                struct src_data *src_data = srcs.payload[i];
                const char *subsep = "";
  
 -              printf(sep);
 +              strbuf_addstr(out, sep);
                sep = "; ";
  
                if (src_data->head_status == 1) {
 -                      printf(srcs.list[i]);
 +                      strbuf_addstr(out, srcs.list[i]);
                        continue;
                }
                if (src_data->head_status == 3) {
                        subsep = ", ";
 -                      printf("HEAD");
 +                      strbuf_addstr(out, "HEAD");
                }
                if (src_data->branch.nr) {
 -                      printf(subsep);
 +                      strbuf_addstr(out, subsep);
                        subsep = ", ";
 -                      print_joined("branch ", "branches ", &src_data->branch);
 +                      print_joined("branch ", "branches ", &src_data->branch,
 +                                      out);
                }
                if (src_data->r_branch.nr) {
 -                      printf(subsep);
 +                      strbuf_addstr(out, subsep);
                        subsep = ", ";
                        print_joined("remote branch ", "remote branches ",
 -                                      &src_data->r_branch);
 +                                      &src_data->r_branch, out);
                }
                if (src_data->tag.nr) {
 -                      printf(subsep);
 +                      strbuf_addstr(out, subsep);
                        subsep = ", ";
 -                      print_joined("tag ", "tags ", &src_data->tag);
 +                      print_joined("tag ", "tags ", &src_data->tag, out);
                }
                if (src_data->generic.nr) {
 -                      printf(subsep);
 -                      print_joined("commit ", "commits ", &src_data->generic);
 +                      strbuf_addstr(out, subsep);
 +                      print_joined("commit ", "commits ", &src_data->generic,
 +                                      out);
                }
                if (strcmp(".", srcs.list[i]))
 -                      printf(" of %s", srcs.list[i]);
 +                      strbuf_addf(out, " of %s", srcs.list[i]);
        }
  
        if (!strcmp("master", current_branch))
 -              putchar('\n');
 +              strbuf_addch(out, '\n');
        else
 -              printf(" into %s\n", current_branch);
 +              strbuf_addf(out, " into %s\n", current_branch);
  
        if (merge_summary) {
                struct commit *head;
                struct rev_info rev;
  
                head = lookup_commit(head_sha1);
 -              init_revisions(&rev, prefix);
 +              init_revisions(&rev, NULL);
                rev.commit_format = CMIT_FMT_ONELINE;
                rev.ignore_merges = 1;
                rev.limited = 1;
  
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.list[i], origins.payload[i],
 -                                      head, &rev, limit);
 +                                      head, &rev, limit, out);
        }
 +      return 0;
 +}
 +
 +int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 +{
 +      FILE *in = stdin;
 +      struct strbuf input, output;
 +      int ret;
 +
 +      git_config(fmt_merge_msg_config, NULL);
 +
 +      while (argc > 1) {
 +              if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary"))
 +                      merge_summary = 1;
 +              else if (!strcmp(argv[1], "--no-log")
 +                              || !strcmp(argv[1], "--no-summary"))
 +                      merge_summary = 0;
 +              else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
 +                      if (argc < 3)
 +                              die ("Which file?");
 +                      if (!strcmp(argv[2], "-"))
 +                              in = stdin;
 +                      else {
 +                              fclose(in);
 +                              in = fopen(argv[2], "r");
 +                              if (!in)
 +                                      die("cannot open %s", argv[2]);
 +                      }
 +                      argc--; argv++;
 +              } else
 +                      break;
 +              argc--; argv++;
 +      }
 +
 +      if (argc > 1)
 +              usage(fmt_merge_msg_usage);
  
 -      /* No cleanup yet; is standalone anyway */
 +      strbuf_init(&input, 0);
 +      if (strbuf_read(&input, fileno(in), 0) < 0)
 +              die("could not read input file %s", strerror(errno));
 +      strbuf_init(&output, 0);
  
 +      ret = fmt_merge_msg(merge_summary, &input, &output);
 +      if (ret)
 +              return ret;
 +      printf("%s", output.buf);
        return 0;
  }
diff --combined builtin-grep.c
index 647535061c350b68f1583fc12b049a37eb17481c,0cac39590d1b080fdc9a5879cade356a28dfdc6a..7bf6a7156cdd8e236d451b9be4d4754c2c517e66
@@@ -427,35 -427,33 +427,35 @@@ static int grep_tree(struct grep_opt *o
        struct name_entry entry;
        char *down;
        int tn_len = strlen(tree_name);
 -      char *path_buf = xmalloc(PATH_MAX + tn_len + 100);
 +      struct strbuf pathbuf;
 +
 +      strbuf_init(&pathbuf, PATH_MAX + tn_len);
  
        if (tn_len) {
 -              tn_len = sprintf(path_buf, "%s:", tree_name);
 -              down = path_buf + tn_len;
 -              strcat(down, base);
 -      }
 -      else {
 -              down = path_buf;
 -              strcpy(down, base);
 +              strbuf_add(&pathbuf, tree_name, tn_len);
 +              strbuf_addch(&pathbuf, ':');
 +              tn_len = pathbuf.len;
        }
 -      len = strlen(path_buf);
 +      strbuf_addstr(&pathbuf, base);
 +      len = pathbuf.len;
  
        while (tree_entry(tree, &entry)) {
 -              strcpy(path_buf + len, entry.path);
 +              int te_len = tree_entry_len(entry.path, entry.sha1);
 +              pathbuf.len = len;
 +              strbuf_add(&pathbuf, entry.path, te_len);
  
                if (S_ISDIR(entry.mode))
                        /* Match "abc/" against pathspec to
                         * decide if we want to descend into "abc"
                         * directory.
                         */
 -                      strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/");
 +                      strbuf_addch(&pathbuf, '/');
  
 +              down = pathbuf.buf + tn_len;
                if (!pathspec_matches(paths, down))
                        ;
                else if (S_ISREG(entry.mode))
 -                      hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len);
 +                      hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
                else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
                        struct tree_desc sub;
                        free(data);
                }
        }
 +      strbuf_release(&pathbuf);
        return hit;
  }
  
@@@ -498,7 -495,7 +498,7 @@@ static int grep_object(struct grep_opt 
  }
  
  static const char builtin_grep_usage[] =
- "git-grep <option>* <rev>* [-e] <pattern> [<path>...]";
+ "git grep <option>* <rev>* [-e] <pattern> [<path>...]";
  
  static const char emsg_invalid_context_len[] =
  "%s: invalid context length argument";
diff --combined builtin-init-db.c
index 5ba213a595fb9d7de4d36ff5cab888bde2035722,53aff68881ca90d7f35d76f04c93439c2b8be1b7..38b4fcb6db547cf6b7a814db621e6f05b583c420
@@@ -115,8 -115,18 +115,8 @@@ static void copy_templates(const char *
  
        if (!template_dir)
                template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
 -      if (!template_dir) {
 -              /*
 -               * if the hard-coded template is relative, it is
 -               * interpreted relative to the exec_dir
 -               */
 -              template_dir = DEFAULT_GIT_TEMPLATE_DIR;
 -              if (!is_absolute_path(template_dir)) {
 -                      struct strbuf d = STRBUF_INIT;
 -                      strbuf_addf(&d, "%s/%s", git_exec_path(), template_dir);
 -                      template_dir = strbuf_detach(&d, NULL);
 -              }
 -      }
 +      if (!template_dir)
 +              template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
        strcpy(template_path, template_dir);
        template_len = strlen(template_path);
        if (template_path[template_len-1] != '/') {
@@@ -354,7 -364,7 +354,7 @@@ static int guess_repository_type(const 
  }
  
  static const char init_db_usage[] =
- "git-init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]]";
+ "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]]";
  
  /*
   * If you want to, you can share the DB area with any number of branches.
diff --combined builtin-log.c
index 617aa67f2b2d1fd82c34eea62fd73145ff646ae0,cb3b33a4f28d7755a5fb320c259984a10e17fa37..f4975cf35f7f1555739f7657ee62ed983d18cb84
@@@ -313,7 -313,7 +313,7 @@@ static int show_object(const unsigned c
  
  static int show_tree_object(const unsigned char *sha1,
                const char *base, int baselen,
 -              const char *pathname, unsigned mode, int stage)
 +              const char *pathname, unsigned mode, int stage, void *context)
  {
        printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
        return 0;
@@@ -366,7 -366,7 +366,7 @@@ int cmd_show(int argc, const char **arg
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
 -                                      show_tree_object);
 +                                      show_tree_object, NULL);
                        break;
                case OBJ_COMMIT:
                        rev.pending.nr = rev.pending.alloc = 0;
@@@ -1082,7 -1082,7 +1082,7 @@@ static int add_pending_commit(const cha
  }
  
  static const char cherry_usage[] =
- "git-cherry [-v] <upstream> [<head>] [<limit>]";
+ "git cherry [-v] <upstream> [<head>] [<limit>]";
  int cmd_cherry(int argc, const char **argv, const char *prefix)
  {
        struct rev_info revs;
diff --combined builtin-ls-tree.c
index 0da047c3bd40f4a7db43f70e2e00c0adcf673188,ac7135868865130f6ad43a63bb395087c67ee20f..d25767a1f7eb0a8b45bc1eed6b9aa95de0847f18
@@@ -23,7 -23,7 +23,7 @@@ static int chomp_prefix
  static const char *ls_tree_prefix;
  
  static const char ls_tree_usage[] =
-       "git-ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
+       "git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
  
  static int show_recursive(const char *base, int baselen, const char *pathname)
  {
@@@ -56,7 -56,7 +56,7 @@@
  }
  
  static int show_tree(const unsigned char *sha1, const char *base, int baselen,
 -                   const char *pathname, unsigned mode, int stage)
 +              const char *pathname, unsigned mode, int stage, void *context)
  {
        int retval = 0;
        const char *type = blob_type;
@@@ -189,7 -189,7 +189,7 @@@ int cmd_ls_tree(int argc, const char **
        tree = parse_tree_indirect(sha1);
        if (!tree)
                die("not a tree object");
 -      read_tree_recursive(tree, "", 0, 0, pathspec, show_tree);
 +      read_tree_recursive(tree, "", 0, 0, pathspec, show_tree, NULL);
  
        return 0;
  }
diff --combined builtin-mailinfo.c
index 13f0502b9e0bca9e4119896c01786b99cc54441e,b3d281e36767c18b2a68ccd94154673cfc537272..a4f99c88622ef7ca0fea21b51082271ff237fb46
@@@ -334,9 -334,7 +334,9 @@@ static int check_header(char *line, uns
                return 1;
        if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
                for (i = 0; header[i]; i++) {
 -                      if (!memcmp("Subject: ", header[i], 9)) {
 +                      if (!memcmp("Subject", header[i], 7)) {
 +                              if (!hdr_data[i])
 +                                      hdr_data[i] = xmalloc(linesize + 20);
                                if (! handle_header(line, hdr_data[i], 0)) {
                                        return 1;
                                }
@@@ -962,7 -960,7 +962,7 @@@ static int mailinfo(FILE *in, FILE *out
  }
  
  static const char mailinfo_usage[] =
-       "git-mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
 -      "git mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
++      "git mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
  
  int cmd_mailinfo(int argc, const char **argv, const char *prefix)
  {
diff --combined builtin-mailsplit.c
index e8cbe678ecdc0863ebe92e6499dd2b1eacf77db6,0dc4cd9a82a37ab94e3922a61595513ebfb57978..13c60c39a402834fbcdb58928c5b5686b7b05a51
@@@ -9,7 -9,7 +9,7 @@@
  #include "path-list.h"
  
  static const char git_mailsplit_usage[] =
- "git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
 -"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>|<Maildir>...";
++"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
  
  static int is_from_line(const char *line, int len)
  {
diff --combined builtin-rerere.c
index 5d40e16932a4d8f3761b9aaebc21b630be904f8a,62ef7cfcf35663f90d724e5aba6dfaaf3b55c0ab..580580502cc0d84400edca8e2bda5ce607b43b58
@@@ -1,17 -1,26 +1,17 @@@
  #include "builtin.h"
  #include "cache.h"
  #include "path-list.h"
 +#include "rerere.h"
  #include "xdiff/xdiff.h"
  #include "xdiff-interface.h"
  
 -#include <time.h>
 -
  static const char git_rerere_usage[] =
- "git-rerere [clear | status | diff | gc]";
+ "git rerere [clear | status | diff | gc]";
  
  /* these values are days */
  static int cutoff_noresolve = 15;
  static int cutoff_resolve = 60;
  
 -/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
 -static int rerere_enabled = -1;
 -
 -/* automatically update cleanly resolved paths to the index */
 -static int rerere_autoupdate;
 -
 -static char *merge_rr_path;
 -
  static const char *rr_path(const char *name, const char *file)
  {
        return git_path("rr-cache/%s/%s", name, file);
@@@ -29,6 -38,179 +29,6 @@@ static int has_resolution(const char *n
        return !stat(rr_path(name, "postimage"), &st);
  }
  
 -static void read_rr(struct path_list *rr)
 -{
 -      unsigned char sha1[20];
 -      char buf[PATH_MAX];
 -      FILE *in = fopen(merge_rr_path, "r");
 -      if (!in)
 -              return;
 -      while (fread(buf, 40, 1, in) == 1) {
 -              int i;
 -              char *name;
 -              if (get_sha1_hex(buf, sha1))
 -                      die("corrupt MERGE_RR");
 -              buf[40] = '\0';
 -              name = xstrdup(buf);
 -              if (fgetc(in) != '\t')
 -                      die("corrupt MERGE_RR");
 -              for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
 -                      ; /* do nothing */
 -              if (i == sizeof(buf))
 -                      die("filename too long");
 -              path_list_insert(buf, rr)->util = name;
 -      }
 -      fclose(in);
 -}
 -
 -static struct lock_file write_lock;
 -
 -static int write_rr(struct path_list *rr, int out_fd)
 -{
 -      int i;
 -      for (i = 0; i < rr->nr; i++) {
 -              const char *path;
 -              int length;
 -              if (!rr->items[i].util)
 -                      continue;
 -              path = rr->items[i].path;
 -              length = strlen(path) + 1;
 -              if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
 -                  write_in_full(out_fd, "\t", 1) != 1 ||
 -                  write_in_full(out_fd, path, length) != length)
 -                      die("unable to write rerere record");
 -      }
 -      if (commit_lock_file(&write_lock) != 0)
 -              die("unable to write rerere record");
 -      return 0;
 -}
 -
 -static int handle_file(const char *path,
 -       unsigned char *sha1, const char *output)
 -{
 -      SHA_CTX ctx;
 -      char buf[1024];
 -      int hunk = 0, hunk_no = 0;
 -      struct strbuf one, two;
 -      FILE *f = fopen(path, "r");
 -      FILE *out = NULL;
 -
 -      if (!f)
 -              return error("Could not open %s", path);
 -
 -      if (output) {
 -              out = fopen(output, "w");
 -              if (!out) {
 -                      fclose(f);
 -                      return error("Could not write %s", output);
 -              }
 -      }
 -
 -      if (sha1)
 -              SHA1_Init(&ctx);
 -
 -      strbuf_init(&one, 0);
 -      strbuf_init(&two,  0);
 -      while (fgets(buf, sizeof(buf), f)) {
 -              if (!prefixcmp(buf, "<<<<<<< "))
 -                      hunk = 1;
 -              else if (!prefixcmp(buf, "======="))
 -                      hunk = 2;
 -              else if (!prefixcmp(buf, ">>>>>>> ")) {
 -                      if (strbuf_cmp(&one, &two) > 0)
 -                              strbuf_swap(&one, &two);
 -                      hunk_no++;
 -                      hunk = 0;
 -                      if (out) {
 -                              fputs("<<<<<<<\n", out);
 -                              fwrite(one.buf, one.len, 1, out);
 -                              fputs("=======\n", out);
 -                              fwrite(two.buf, two.len, 1, out);
 -                              fputs(">>>>>>>\n", out);
 -                      }
 -                      if (sha1) {
 -                              SHA1_Update(&ctx, one.buf ? one.buf : "",
 -                                          one.len + 1);
 -                              SHA1_Update(&ctx, two.buf ? two.buf : "",
 -                                          two.len + 1);
 -                      }
 -                      strbuf_reset(&one);
 -                      strbuf_reset(&two);
 -              } else if (hunk == 1)
 -                      strbuf_addstr(&one, buf);
 -              else if (hunk == 2)
 -                      strbuf_addstr(&two, buf);
 -              else if (out)
 -                      fputs(buf, out);
 -      }
 -      strbuf_release(&one);
 -      strbuf_release(&two);
 -
 -      fclose(f);
 -      if (out)
 -              fclose(out);
 -      if (sha1)
 -              SHA1_Final(sha1, &ctx);
 -      if (hunk) {
 -              if (output)
 -                      unlink(output);
 -              return error("Could not parse conflict hunks in %s", path);
 -      }
 -      return hunk_no;
 -}
 -
 -static int find_conflict(struct path_list *conflict)
 -{
 -      int i;
 -      if (read_cache() < 0)
 -              return error("Could not read index");
 -      for (i = 0; i+1 < active_nr; i++) {
 -              struct cache_entry *e2 = active_cache[i];
 -              struct cache_entry *e3 = active_cache[i+1];
 -              if (ce_stage(e2) == 2 &&
 -                  ce_stage(e3) == 3 &&
 -                  ce_same_name(e2, e3) &&
 -                  S_ISREG(e2->ce_mode) &&
 -                  S_ISREG(e3->ce_mode)) {
 -                      path_list_insert((const char *)e2->name, conflict);
 -                      i++; /* skip over both #2 and #3 */
 -              }
 -      }
 -      return 0;
 -}
 -
 -static int merge(const char *name, const char *path)
 -{
 -      int ret;
 -      mmfile_t cur, base, other;
 -      mmbuffer_t result = {NULL, 0};
 -      xpparam_t xpp = {XDF_NEED_MINIMAL};
 -
 -      if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
 -              return 1;
 -
 -      if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
 -                      read_mmfile(&base, rr_path(name, "preimage")) ||
 -                      read_mmfile(&other, rr_path(name, "postimage")))
 -              return 1;
 -      ret = xdl_merge(&base, &cur, "", &other, "",
 -                      &xpp, XDL_MERGE_ZEALOUS, &result);
 -      if (!ret) {
 -              FILE *f = fopen(path, "w");
 -              if (!f)
 -                      return error("Could not write to %s", path);
 -              fwrite(result.ptr, result.size, 1, f);
 -              fclose(f);
 -      }
 -
 -      free(cur.ptr);
 -      free(base.ptr);
 -      free(other.ptr);
 -      free(result.ptr);
 -
 -      return ret;
 -}
 -
  static void unlink_rr_item(const char *name)
  {
        unlink(rr_path(name, "thisimage"));
        rmdir(git_path("rr-cache/%s", name));
  }
  
 +static int git_rerere_gc_config(const char *var, const char *value, void *cb)
 +{
 +      if (!strcmp(var, "gc.rerereresolved"))
 +              cutoff_resolve = git_config_int(var, value);
 +      else if (!strcmp(var, "gc.rerereunresolved"))
 +              cutoff_noresolve = git_config_int(var, value);
 +      else
 +              return git_default_config(var, value, cb);
 +      return 0;
 +}
 +
  static void garbage_collect(struct path_list *rr)
  {
        struct path_list to_remove = { NULL, 0, 0, 1 };
        int i, cutoff;
        time_t now = time(NULL), then;
  
 +      git_config(git_rerere_gc_config, NULL);
        dir = opendir(git_path("rr-cache"));
        while ((e = readdir(dir))) {
                const char *name = e->d_name;
@@@ -109,25 -279,181 +109,25 @@@ static int diff_two(const char *file1, 
        return 0;
  }
  
 -static struct lock_file index_lock;
 -
 -static int update_paths(struct path_list *update)
 -{
 -      int i;
 -      int fd = hold_locked_index(&index_lock, 0);
 -      int status = 0;
 -
 -      if (fd < 0)
 -              return -1;
 -
 -      for (i = 0; i < update->nr; i++) {
 -              struct path_list_item *item = &update->items[i];
 -              if (add_file_to_cache(item->path, ADD_CACHE_IGNORE_ERRORS))
 -                      status = -1;
 -      }
 -
 -      if (!status && active_cache_changed) {
 -              if (write_cache(fd, active_cache, active_nr) ||
 -                  commit_locked_index(&index_lock))
 -                      die("Unable to write new index file");
 -      } else if (fd >= 0)
 -              rollback_lock_file(&index_lock);
 -      return status;
 -}
 -
 -static int do_plain_rerere(struct path_list *rr, int fd)
 -{
 -      struct path_list conflict = { NULL, 0, 0, 1 };
 -      struct path_list update = { NULL, 0, 0, 1 };
 -      int i;
 -
 -      find_conflict(&conflict);
 -
 -      /*
 -       * MERGE_RR records paths with conflicts immediately after merge
 -       * failed.  Some of the conflicted paths might have been hand resolved
 -       * in the working tree since then, but the initial run would catch all
 -       * and register their preimages.
 -       */
 -
 -      for (i = 0; i < conflict.nr; i++) {
 -              const char *path = conflict.items[i].path;
 -              if (!path_list_has_path(rr, path)) {
 -                      unsigned char sha1[20];
 -                      char *hex;
 -                      int ret;
 -                      ret = handle_file(path, sha1, NULL);
 -                      if (ret < 1)
 -                              continue;
 -                      hex = xstrdup(sha1_to_hex(sha1));
 -                      path_list_insert(path, rr)->util = hex;
 -                      if (mkdir(git_path("rr-cache/%s", hex), 0755))
 -                              continue;;
 -                      handle_file(path, NULL, rr_path(hex, "preimage"));
 -                      fprintf(stderr, "Recorded preimage for '%s'\n", path);
 -              }
 -      }
 -
 -      /*
 -       * Now some of the paths that had conflicts earlier might have been
 -       * hand resolved.  Others may be similar to a conflict already that
 -       * was resolved before.
 -       */
 -
 -      for (i = 0; i < rr->nr; i++) {
 -              int ret;
 -              const char *path = rr->items[i].path;
 -              const char *name = (const char *)rr->items[i].util;
 -
 -              if (has_resolution(name)) {
 -                      if (!merge(name, path)) {
 -                              fprintf(stderr, "Resolved '%s' using "
 -                                              "previous resolution.\n", path);
 -                              if (rerere_autoupdate)
 -                                      path_list_insert(path, &update);
 -                              goto mark_resolved;
 -                      }
 -              }
 -
 -              /* Let's see if we have resolved it. */
 -              ret = handle_file(path, NULL, NULL);
 -              if (ret)
 -                      continue;
 -
 -              fprintf(stderr, "Recorded resolution for '%s'.\n", path);
 -              copy_file(rr_path(name, "postimage"), path, 0666);
 -      mark_resolved:
 -              rr->items[i].util = NULL;
 -      }
 -
 -      if (update.nr)
 -              update_paths(&update);
 -
 -      return write_rr(rr, fd);
 -}
 -
 -static int git_rerere_config(const char *var, const char *value, void *cb)
 -{
 -      if (!strcmp(var, "gc.rerereresolved"))
 -              cutoff_resolve = git_config_int(var, value);
 -      else if (!strcmp(var, "gc.rerereunresolved"))
 -              cutoff_noresolve = git_config_int(var, value);
 -      else if (!strcmp(var, "rerere.enabled"))
 -              rerere_enabled = git_config_bool(var, value);
 -      else if (!strcmp(var, "rerere.autoupdate"))
 -              rerere_autoupdate = git_config_bool(var, value);
 -      else
 -              return git_default_config(var, value, cb);
 -      return 0;
 -}
 -
 -static int is_rerere_enabled(void)
 -{
 -      struct stat st;
 -      const char *rr_cache;
 -      int rr_cache_exists;
 -
 -      if (!rerere_enabled)
 -              return 0;
 -
 -      rr_cache = git_path("rr-cache");
 -      rr_cache_exists = !stat(rr_cache, &st) && S_ISDIR(st.st_mode);
 -      if (rerere_enabled < 0)
 -              return rr_cache_exists;
 -
 -      if (!rr_cache_exists &&
 -          (mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
 -              die("Could not create directory %s", rr_cache);
 -      return 1;
 -}
 -
 -static int setup_rerere(struct path_list *merge_rr)
 -{
 -      int fd;
 -
 -      git_config(git_rerere_config, NULL);
 -      if (!is_rerere_enabled())
 -              return -1;
 -
 -      merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
 -      fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
 -      read_rr(merge_rr);
 -      return fd;
 -}
 -
 -int rerere(void)
 -{
 -      struct path_list merge_rr = { NULL, 0, 0, 1 };
 -      int fd;
 -
 -      fd = setup_rerere(&merge_rr);
 -      if (fd < 0)
 -              return 0;
 -      return do_plain_rerere(&merge_rr, fd);
 -}
 -
  int cmd_rerere(int argc, const char **argv, const char *prefix)
  {
        struct path_list merge_rr = { NULL, 0, 0, 1 };
        int i, fd;
  
 +      if (argc < 2)
 +              return rerere();
 +
        fd = setup_rerere(&merge_rr);
        if (fd < 0)
                return 0;
  
 -      if (argc < 2)
 -              return do_plain_rerere(&merge_rr, fd);
 -      else if (!strcmp(argv[1], "clear")) {
 +      if (!strcmp(argv[1], "clear")) {
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *name = (const char *)merge_rr.items[i].util;
                        if (!has_resolution(name))
                                unlink_rr_item(name);
                }
 -              unlink(merge_rr_path);
 +              unlink(git_path("rr-cache/MERGE_RR"));
        } else if (!strcmp(argv[1], "gc"))
                garbage_collect(&merge_rr);
        else if (!strcmp(argv[1], "status"))
diff --combined builtin-rev-list.c
index b4a2c447f22163b3165a0c6081899498e3048b51,114114b2b7b8c092fc41cfb22d7d976f3695d47d..8e1720c45bfa2ffc170b9880a6c90fcc3741bd46
@@@ -17,7 -17,7 +17,7 @@@
  #define COUNTED               (1u<<16)
  
  static const char rev_list_usage[] =
- "git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
+ "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
  "  limiting output:\n"
  "    --max-count=nr\n"
  "    --max-age=epoch\n"
@@@ -37,7 -37,6 +37,7 @@@
  "    --reverse\n"
  "  formatting output:\n"
  "    --parents\n"
 +"    --children\n"
  "    --objects | --objects-edge\n"
  "    --unpacked\n"
  "    --header | --pretty\n"
@@@ -91,15 -90,6 +91,15 @@@ static void show_commit(struct commit *
                        parents = parents->next;
                }
        }
 +      if (revs.children.name) {
 +              struct commit_list *children;
 +
 +              children = lookup_decoration(&revs.children, &commit->object);
 +              while (children) {
 +                      printf(" %s", sha1_to_hex(children->item->object.sha1));
 +                      children = children->next;
 +              }
 +      }
        show_decorations(commit);
        if (revs.commit_format == CMIT_FMT_ONELINE)
                putchar(' ');
diff --combined builtin-revert.c
index f3d452418c25b0d95a911a050f75ba190d6513c1,ebfa3bb48856c1e5b42f9e330dbd46cb0cd1964c..e9da870d22c14c32a0e0a6cb71b933c79a2d8b53
   */
  
  static const char * const revert_usage[] = {
-       "git-revert [options] <commit-ish>",
+       "git revert [options] <commit-ish>",
        NULL
  };
  
  static const char * const cherry_pick_usage[] = {
-       "git-cherry-pick [options] <commit-ish>",
+       "git cherry-pick [options] <commit-ish>",
        NULL
  };
  
@@@ -206,7 -206,6 +206,7 @@@ static int merge_recursive(const char *
  {
        char buffer[256];
        const char *argv[6];
 +      int i = 0;
  
        sprintf(buffer, "GITHEAD_%s", head_sha1);
        setenv(buffer, head_name, 1);
         * and $prev on top of us (when reverting), or the change between
         * $prev and $commit on top of us (when cherry-picking or replaying).
         */
 -      argv[0] = "merge-recursive";
 -      argv[1] = base_sha1;
 -      argv[2] = "--";
 -      argv[3] = head_sha1;
 -      argv[4] = next_sha1;
 -      argv[5] = NULL;
 +      argv[i++] = "merge-recursive";
 +      if (base_sha1)
 +              argv[i++] = base_sha1;
 +      argv[i++] = "--";
 +      argv[i++] = head_sha1;
 +      argv[i++] = next_sha1;
 +      argv[i++] = NULL;
  
        return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD);
  }
@@@ -299,12 -297,9 +299,12 @@@ static int revert_or_cherry_pick(int ar
                discard_cache();
        }
  
 -      if (!commit->parents)
 -              die ("Cannot %s a root commit", me);
 -      if (commit->parents->next) {
 +      if (!commit->parents) {
 +              if (action == REVERT)
 +                      die ("Cannot revert a root commit");
 +              parent = NULL;
 +      }
 +      else if (commit->parents->next) {
                /* Reverting or cherry-picking a merge commit */
                int cnt;
                struct commit_list *p;
                }
        }
  
 -      if (merge_recursive(sha1_to_hex(base->object.sha1),
 +      if (merge_recursive(base == NULL ?
 +                              NULL : sha1_to_hex(base->object.sha1),
                                sha1_to_hex(head), "HEAD",
                                sha1_to_hex(next->object.sha1), oneline) ||
                        write_cache_as_tree(head, 0, NULL)) {
diff --combined builtin-shortlog.c
index f8bcbfce4009337c4aea0b923a620fc4697e4df3,fcb0da9fc80ca7cd00e44d58113c7fa7f4102a3a..94c4723856924a9643bcaf6c14fd9679667cbbdd
@@@ -7,14 -7,9 +7,14 @@@
  #include "utf8.h"
  #include "mailmap.h"
  #include "shortlog.h"
 +#include "parse-options.h"
  
 -static const char shortlog_usage[] =
 -"git shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
 +static char const * const shortlog_usage[] = {
-       "git-shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
++      "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
 +      "",
 +      "[rev-opts] are documented in git-rev-list(1)",
 +      NULL
 +};
  
  static int compare_by_number(const void *a1, const void *a2)
  {
@@@ -154,15 -149,6 +154,15 @@@ void shortlog_add_commit(struct shortlo
        if (!author)
                die("Missing author: %s",
                    sha1_to_hex(commit->object.sha1));
 +      if (log->user_format) {
 +              struct strbuf buf = STRBUF_INIT;
 +
 +              pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &buf,
 +                      DEFAULT_ABBREV, "", "", DATE_NORMAL, 0);
 +              insert_one_record(log, author, buf.buf);
 +              strbuf_release(&buf);
 +              return;
 +      }
        if (*buffer)
                buffer++;
        insert_one_record(log, author, !*buffer ? "<none>" : buffer);
@@@ -178,19 -164,21 +178,19 @@@ static void get_from_rev(struct rev_inf
                shortlog_add_commit(log, commit);
  }
  
 -static int parse_uint(char const **arg, int comma)
 +static int parse_uint(char const **arg, int comma, int defval)
  {
        unsigned long ul;
        int ret;
        char *endp;
  
        ul = strtoul(*arg, &endp, 10);
 -      if (endp != *arg && *endp && *endp != comma)
 +      if (*endp && *endp != comma)
                return -1;
 -      ret = (int) ul;
 -      if (ret != ul)
 +      if (ul > INT_MAX)
                return -1;
 -      *arg = endp;
 -      if (**arg)
 -              (*arg)++;
 +      ret = *arg == endp ? defval : (int)ul;
 +      *arg = *endp ? endp + 1 : endp;
        return ret;
  }
  
@@@ -199,30 -187,30 +199,30 @@@ static const char wrap_arg_usage[] = "-
  #define DEFAULT_INDENT1 6
  #define DEFAULT_INDENT2 9
  
 -static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
 +static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
  {
 -      arg += 2; /* skip -w */
 -
 -      *wrap = parse_uint(&arg, ',');
 -      if (*wrap < 0)
 -              die(wrap_arg_usage);
 -      *in1 = parse_uint(&arg, ',');
 -      if (*in1 < 0)
 -              die(wrap_arg_usage);
 -      *in2 = parse_uint(&arg, '\0');
 -      if (*in2 < 0)
 -              die(wrap_arg_usage);
 -
 -      if (!*wrap)
 -              *wrap = DEFAULT_WRAPLEN;
 -      if (!*in1)
 -              *in1 = DEFAULT_INDENT1;
 -      if (!*in2)
 -              *in2 = DEFAULT_INDENT2;
 -      if (*wrap &&
 -          ((*in1 && *wrap <= *in1) ||
 -           (*in2 && *wrap <= *in2)))
 -              die(wrap_arg_usage);
 +      struct shortlog *log = opt->value;
 +
 +      log->wrap_lines = !unset;
 +      if (unset)
 +              return 0;
 +      if (!arg) {
 +              log->wrap = DEFAULT_WRAPLEN;
 +              log->in1 = DEFAULT_INDENT1;
 +              log->in2 = DEFAULT_INDENT2;
 +              return 0;
 +      }
 +
 +      log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
 +      log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
 +      log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
 +      if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
 +              return error(wrap_arg_usage);
 +      if (log->wrap &&
 +          ((log->in1 && log->wrap <= log->in1) ||
 +           (log->in2 && log->wrap <= log->in2)))
 +              return error(wrap_arg_usage);
 +      return 0;
  }
  
  void shortlog_init(struct shortlog *log)
  
  int cmd_shortlog(int argc, const char **argv, const char *prefix)
  {
 -      struct shortlog log;
 -      struct rev_info rev;
 +      static struct shortlog log;
 +      static struct rev_info rev;
        int nongit;
  
 +      static const struct option options[] = {
 +              OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
 +                          "sort output according to the number of commits per author"),
 +              OPT_BOOLEAN('s', "summary", &log.summary,
 +                          "Suppress commit descriptions, only provides commit count"),
 +              OPT_BOOLEAN('e', "email", &log.email,
 +                          "Show the email address of each author"),
 +              { OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
 +                      "Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
 +              OPT_END(),
 +      };
 +
 +      struct parse_opt_ctx_t ctx;
 +
        prefix = setup_git_directory_gently(&nongit);
        shortlog_init(&log);
 -
 -      /* since -n is a shadowed rev argument, parse our args first */
 -      while (argc > 1) {
 -              if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
 -                      log.sort_by_number = 1;
 -              else if (!strcmp(argv[1], "-s") ||
 -                              !strcmp(argv[1], "--summary"))
 -                      log.summary = 1;
 -              else if (!strcmp(argv[1], "-e") ||
 -                       !strcmp(argv[1], "--email"))
 -                      log.email = 1;
 -              else if (!prefixcmp(argv[1], "-w")) {
 -                      log.wrap_lines = 1;
 -                      parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
 +      init_revisions(&rev, prefix);
 +      parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
 +                          PARSE_OPT_KEEP_ARGV0);
 +
 +      for (;;) {
 +              switch (parse_options_step(&ctx, options, shortlog_usage)) {
 +              case PARSE_OPT_HELP:
 +                      exit(129);
 +              case PARSE_OPT_DONE:
 +                      goto parse_done;
                }
 -              else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
 -                      usage(shortlog_usage);
 -              else
 -                      break;
 -              argv++;
 -              argc--;
 +              parse_revision_opt(&rev, &ctx, options, shortlog_usage);
        }
 -      init_revisions(&rev, prefix);
 -      argc = setup_revisions(argc, argv, &rev, NULL);
 -      if (argc > 1)
 -              die ("unrecognized argument: %s", argv[1]);
 +parse_done:
 +      argc = parse_options_end(&ctx);
 +
 +      if (setup_revisions(argc, argv, &rev, NULL) != 1) {
 +              error("unrecognized argument: %s", argv[1]);
 +              usage_with_options(shortlog_usage, options);
 +      }
 +
 +      log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
  
        /* assume HEAD if from a tty */
        if (!nongit && !rev.pending.nr && isatty(0))
diff --combined builtin-tag.c
index a70922b21c4bc2ff552e00cd65ecba6c17a33000,73cb7bfc857eaad8e7902a2be8cd81c8093eb18d..c2cca6cb6dc46a221c65092d7c419445d6442b5f
  #include "parse-options.h"
  
  static const char * const git_tag_usage[] = {
-       "git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
-       "git-tag -d <tagname>...",
-       "git-tag -l [-n[<num>]] [<pattern>]",
-       "git-tag -v <tagname>...",
+       "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
+       "git tag -d <tagname>...",
+       "git tag -l [-n[<num>]] [<pattern>]",
+       "git tag -v <tagname>...",
        NULL
  };
  
@@@ -202,7 -202,6 +202,7 @@@ static int do_sign(struct strbuf *buffe
        const char *args[4];
        char *bracket;
        int len;
 +      int i, j;
  
        if (!*signingkey) {
                if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
        if (finish_command(&gpg) || !len || len < 0)
                return error("gpg failed to sign the tag");
  
 +      /* Strip CR from the line endings, in case we are on Windows. */
 +      for (i = j = 0; i < buffer->len; i++)
 +              if (buffer->buf[i] != '\r') {
 +                      if (i != j)
 +                              buffer->buf[j] = buffer->buf[i];
 +                      j++;
 +              }
 +      strbuf_setlen(buffer, j);
 +
        return 0;
  }
  
diff --combined builtin-upload-archive.c
index 295e24c2fa95f6e59380024ca757ad8dec80688f,64381330cd339b3397a87c77a8451df524c7e726..13a6c6203eff7d8dc96a56fa5709dc9eb69da0a6
@@@ -8,19 -8,18 +8,19 @@@
  #include "sideband.h"
  
  static const char upload_archive_usage[] =
-       "git-upload-archive <repo>";
+       "git upload-archive <repo>";
  
  static const char deadchild[] =
- "git-upload-archive: archiver died with error";
+ "git upload-archive: archiver died with error";
  
  static const char lostchild[] =
- "git-upload-archive: archiver process was lost";
+ "git upload-archive: archiver process was lost";
  
  
  static int run_upload_archive(int argc, const char **argv, const char *prefix)
  {
 -      struct archiver ar;
 +      const struct archiver *ar;
 +      struct archiver_args args;
        const char *sent_argv[MAX_ARGS];
        const char *arg_cmd = "argument ";
        char *p, buf[4096];
        sent_argv[sent_argc] = NULL;
  
        /* parse all options sent by the client */
 -      treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar);
 +      treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar, &args);
  
 -      parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix);
 -      parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args);
 +      parse_treeish_arg(sent_argv + treeish_idx, &args, prefix);
 +      parse_pathspec_arg(sent_argv + treeish_idx + 1, &args);
  
 -      return ar.write_archive(&ar.args);
 +      return ar->write_archive(&args);
  }
  
  static void error_clnt(const char *fmt, ...)
index 8026ccff4a459a75148740382646f7fe10b00255,0000000000000000000000000000000000000000..e9588eec33ba5b64d186ff048bb040c18c57e6bc
mode 100755,000000..100755
--- /dev/null
@@@ -1,554 -1,0 +1,554 @@@
- git-merge [options] <remote>...
- git-merge [options] <msg> HEAD <remote>
 +#!/bin/sh
 +#
 +# Copyright (c) 2005 Junio C Hamano
 +#
 +
 +OPTIONS_KEEPDASHDASH=
 +OPTIONS_SPEC="\
++git merge [options] <remote>...
++git merge [options] <msg> HEAD <remote>
 +--
 +stat                 show a diffstat at the end of the merge
 +n                    don't show a diffstat at the end of the merge
 +summary              (synonym to --stat)
 +log                  add list of one-line log to merge commit message
 +squash               create a single commit instead of doing a merge
 +commit               perform a commit if the merge succeeds (default)
 +ff                   allow fast forward (default)
 +s,strategy=          merge strategy to use
 +m,message=           message to be used for the merge commit (if any)
 +"
 +
 +SUBDIRECTORY_OK=Yes
 +. git-sh-setup
 +require_work_tree
 +cd_to_toplevel
 +
 +test -z "$(git ls-files -u)" ||
 +      die "You are in the middle of a conflicted merge."
 +
 +LF='
 +'
 +
 +all_strategies='recur recursive octopus resolve stupid ours subtree'
 +default_twohead_strategies='recursive'
 +default_octopus_strategies='octopus'
 +no_fast_forward_strategies='subtree ours'
 +no_trivial_strategies='recursive recur subtree ours'
 +use_strategies=
 +
 +allow_fast_forward=t
 +allow_trivial_merge=t
 +squash= no_commit= log_arg=
 +
 +dropsave() {
 +      rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
 +               "$GIT_DIR/MERGE_STASH" || exit 1
 +}
 +
 +savestate() {
 +      # Stash away any local modifications.
 +      git stash create >"$GIT_DIR/MERGE_STASH"
 +}
 +
 +restorestate() {
 +        if test -f "$GIT_DIR/MERGE_STASH"
 +      then
 +              git reset --hard $head >/dev/null
 +              git stash apply $(cat "$GIT_DIR/MERGE_STASH")
 +              git update-index --refresh >/dev/null
 +      fi
 +}
 +
 +finish_up_to_date () {
 +      case "$squash" in
 +      t)
 +              echo "$1 (nothing to squash)" ;;
 +      '')
 +              echo "$1" ;;
 +      esac
 +      dropsave
 +}
 +
 +squash_message () {
 +      echo Squashed commit of the following:
 +      echo
 +      git log --no-merges --pretty=medium ^"$head" $remoteheads
 +}
 +
 +finish () {
 +      if test '' = "$2"
 +      then
 +              rlogm="$GIT_REFLOG_ACTION"
 +      else
 +              echo "$2"
 +              rlogm="$GIT_REFLOG_ACTION: $2"
 +      fi
 +      case "$squash" in
 +      t)
 +              echo "Squash commit -- not updating HEAD"
 +              squash_message >"$GIT_DIR/SQUASH_MSG"
 +              ;;
 +      '')
 +              case "$merge_msg" in
 +              '')
 +                      echo "No merge message -- not updating HEAD"
 +                      ;;
 +              *)
 +                      git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
 +                      git gc --auto
 +                      ;;
 +              esac
 +              ;;
 +      esac
 +      case "$1" in
 +      '')
 +              ;;
 +      ?*)
 +              if test "$show_diffstat" = t
 +              then
 +                      # We want color (if set), but no pager
 +                      GIT_PAGER='' git diff --stat --summary -M "$head" "$1"
 +              fi
 +              ;;
 +      esac
 +
 +      # Run a post-merge hook
 +        if test -x "$GIT_DIR"/hooks/post-merge
 +        then
 +          case "$squash" in
 +          t)
 +                "$GIT_DIR"/hooks/post-merge 1
 +              ;;
 +          '')
 +                "$GIT_DIR"/hooks/post-merge 0
 +              ;;
 +          esac
 +        fi
 +}
 +
 +merge_name () {
 +      remote="$1"
 +      rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
 +      bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
 +      if test "$rh" = "$bh"
 +      then
 +              echo "$rh               branch '$remote' of ."
 +      elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
 +              git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
 +      then
 +              echo "$rh               branch '$truname' (early part) of ."
 +      elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
 +      then
 +              sed -e 's/      not-for-merge   /               /' -e 1q \
 +                      "$GIT_DIR/FETCH_HEAD"
 +      else
 +              echo "$rh               commit '$remote'"
 +      fi
 +}
 +
 +parse_config () {
 +      while test $# != 0; do
 +              case "$1" in
 +              -n|--no-stat|--no-summary)
 +                      show_diffstat=false ;;
 +              --stat|--summary)
 +                      show_diffstat=t ;;
 +              --log|--no-log)
 +                      log_arg=$1 ;;
 +              --squash)
 +                      test "$allow_fast_forward" = t ||
 +                              die "You cannot combine --squash with --no-ff."
 +                      squash=t no_commit=t ;;
 +              --no-squash)
 +                      squash= no_commit= ;;
 +              --commit)
 +                      no_commit= ;;
 +              --no-commit)
 +                      no_commit=t ;;
 +              --ff)
 +                      allow_fast_forward=t ;;
 +              --no-ff)
 +                      test "$squash" != t ||
 +                              die "You cannot combine --squash with --no-ff."
 +                      allow_fast_forward=f ;;
 +              -s|--strategy)
 +                      shift
 +                      case " $all_strategies " in
 +                      *" $1 "*)
 +                              use_strategies="$use_strategies$1 " ;;
 +                      *)
 +                              die "available strategies are: $all_strategies" ;;
 +                      esac
 +                      ;;
 +              -m|--message)
 +                      shift
 +                      merge_msg="$1"
 +                      have_message=t
 +                      ;;
 +              --)
 +                      shift
 +                      break ;;
 +              *)      usage ;;
 +              esac
 +              shift
 +      done
 +      args_left=$#
 +}
 +
 +test $# != 0 || usage
 +
 +have_message=
 +
 +if branch=$(git-symbolic-ref -q HEAD)
 +then
 +      mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions")
 +      if test -n "$mergeopts"
 +      then
 +              parse_config $mergeopts --
 +      fi
 +fi
 +
 +parse_config "$@"
 +while test $args_left -lt $#; do shift; done
 +
 +if test -z "$show_diffstat"; then
 +    test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
 +    test "$(git config --bool merge.stat)" = false && show_diffstat=false
 +    test -z "$show_diffstat" && show_diffstat=t
 +fi
 +
 +# This could be traditional "merge <msg> HEAD <commit>..."  and the
 +# way we can tell it is to see if the second token is HEAD, but some
 +# people might have misused the interface and used a committish that
 +# is the same as HEAD there instead.  Traditional format never would
 +# have "-m" so it is an additional safety measure to check for it.
 +
 +if test -z "$have_message" &&
 +      second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) &&
 +      head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) &&
 +      test "$second_token" = "$head_commit"
 +then
 +      merge_msg="$1"
 +      shift
 +      head_arg="$1"
 +      shift
 +elif ! git rev-parse --verify HEAD >/dev/null 2>&1
 +then
 +      # If the merged head is a valid one there is no reason to
 +      # forbid "git merge" into a branch yet to be born.  We do
 +      # the same for "git pull".
 +      if test 1 -ne $#
 +      then
 +              echo >&2 "Can merge only exactly one commit into empty head"
 +              exit 1
 +      fi
 +
 +      rh=$(git rev-parse --verify "$1^0") ||
 +              die "$1 - not something we can merge"
 +
 +      git update-ref -m "initial pull" HEAD "$rh" "" &&
 +      git read-tree --reset -u HEAD
 +      exit
 +
 +else
 +      # We are invoked directly as the first-class UI.
 +      head_arg=HEAD
 +
 +      # All the rest are the commits being merged; prepare
 +      # the standard merge summary message to be appended to
 +      # the given message.  If remote is invalid we will die
 +      # later in the common codepath so we discard the error
 +      # in this loop.
 +      merge_name=$(for remote
 +              do
 +                      merge_name "$remote"
 +              done | git fmt-merge-msg $log_arg
 +      )
 +      merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
 +fi
 +head=$(git rev-parse --verify "$head_arg"^0) || usage
 +
 +# All the rest are remote heads
 +test "$#" = 0 && usage ;# we need at least one remote head.
 +set_reflog_action "merge $*"
 +
 +remoteheads=
 +for remote
 +do
 +      remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) ||
 +          die "$remote - not something we can merge"
 +      remoteheads="${remoteheads}$remotehead "
 +      eval GITHEAD_$remotehead='"$remote"'
 +      export GITHEAD_$remotehead
 +done
 +set x $remoteheads ; shift
 +
 +case "$use_strategies" in
 +'')
 +      case "$#" in
 +      1)
 +              var="`git config --get pull.twohead`"
 +              if test -n "$var"
 +              then
 +                      use_strategies="$var"
 +              else
 +                      use_strategies="$default_twohead_strategies"
 +              fi ;;
 +      *)
 +              var="`git config --get pull.octopus`"
 +              if test -n "$var"
 +              then
 +                      use_strategies="$var"
 +              else
 +                      use_strategies="$default_octopus_strategies"
 +              fi ;;
 +      esac
 +      ;;
 +esac
 +
 +for s in $use_strategies
 +do
 +      for ss in $no_fast_forward_strategies
 +      do
 +              case " $s " in
 +              *" $ss "*)
 +                      allow_fast_forward=f
 +                      break
 +                      ;;
 +              esac
 +      done
 +      for ss in $no_trivial_strategies
 +      do
 +              case " $s " in
 +              *" $ss "*)
 +                      allow_trivial_merge=f
 +                      break
 +                      ;;
 +              esac
 +      done
 +done
 +
 +case "$#" in
 +1)
 +      common=$(git merge-base --all $head "$@")
 +      ;;
 +*)
 +      common=$(git show-branch --merge-base $head "$@")
 +      ;;
 +esac
 +echo "$head" >"$GIT_DIR/ORIG_HEAD"
 +
 +case "$allow_fast_forward,$#,$common,$no_commit" in
 +?,*,'',*)
 +      # No common ancestors found. We need a real merge.
 +      ;;
 +?,1,"$1",*)
 +      # If head can reach all the merge then we are up to date.
 +      # but first the most common case of merging one remote.
 +      finish_up_to_date "Already up-to-date."
 +      exit 0
 +      ;;
 +t,1,"$head",*)
 +      # Again the most common case of merging one remote.
 +      echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
 +      git update-index --refresh 2>/dev/null
 +      msg="Fast forward"
 +      if test -n "$have_message"
 +      then
 +              msg="$msg (no commit created; -m option ignored)"
 +      fi
 +      new_head=$(git rev-parse --verify "$1^0") &&
 +      git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
 +      finish "$new_head" "$msg" || exit
 +      dropsave
 +      exit 0
 +      ;;
 +?,1,?*"$LF"?*,*)
 +      # We are not doing octopus and not fast forward.  Need a
 +      # real merge.
 +      ;;
 +?,1,*,)
 +      # We are not doing octopus, not fast forward, and have only
 +      # one common.
 +      git update-index --refresh 2>/dev/null
 +      case "$allow_trivial_merge" in
 +      t)
 +              # See if it is really trivial.
 +              git var GIT_COMMITTER_IDENT >/dev/null || exit
 +              echo "Trying really trivial in-index merge..."
 +              if git read-tree --trivial -m -u -v $common $head "$1" &&
 +                 result_tree=$(git write-tree)
 +              then
 +                      echo "Wonderful."
 +                      result_commit=$(
 +                              printf '%s\n' "$merge_msg" |
 +                              git commit-tree $result_tree -p HEAD -p "$1"
 +                      ) || exit
 +                      finish "$result_commit" "In-index merge"
 +                      dropsave
 +                      exit 0
 +              fi
 +              echo "Nope."
 +      esac
 +      ;;
 +*)
 +      # An octopus.  If we can reach all the remote we are up to date.
 +      up_to_date=t
 +      for remote
 +      do
 +              common_one=$(git merge-base --all $head $remote)
 +              if test "$common_one" != "$remote"
 +              then
 +                      up_to_date=f
 +                      break
 +              fi
 +      done
 +      if test "$up_to_date" = t
 +      then
 +              finish_up_to_date "Already up-to-date. Yeeah!"
 +              exit 0
 +      fi
 +      ;;
 +esac
 +
 +# We are going to make a new commit.
 +git var GIT_COMMITTER_IDENT >/dev/null || exit
 +
 +# At this point, we need a real merge.  No matter what strategy
 +# we use, it would operate on the index, possibly affecting the
 +# working tree, and when resolved cleanly, have the desired tree
 +# in the index -- this means that the index must be in sync with
 +# the $head commit.  The strategies are responsible to ensure this.
 +
 +case "$use_strategies" in
 +?*' '?*)
 +    # Stash away the local changes so that we can try more than one.
 +    savestate
 +    single_strategy=no
 +    ;;
 +*)
 +    rm -f "$GIT_DIR/MERGE_STASH"
 +    single_strategy=yes
 +    ;;
 +esac
 +
 +result_tree= best_cnt=-1 best_strategy= wt_strategy=
 +merge_was_ok=
 +for strategy in $use_strategies
 +do
 +    test "$wt_strategy" = '' || {
 +      echo "Rewinding the tree to pristine..."
 +      restorestate
 +    }
 +    case "$single_strategy" in
 +    no)
 +      echo "Trying merge strategy $strategy..."
 +      ;;
 +    esac
 +
 +    # Remember which strategy left the state in the working tree
 +    wt_strategy=$strategy
 +
 +    git-merge-$strategy $common -- "$head_arg" "$@"
 +    exit=$?
 +    if test "$no_commit" = t && test "$exit" = 0
 +    then
 +        merge_was_ok=t
 +      exit=1 ;# pretend it left conflicts.
 +    fi
 +
 +    test "$exit" = 0 || {
 +
 +      # The backend exits with 1 when conflicts are left to be resolved,
 +      # with 2 when it does not handle the given merge at all.
 +
 +      if test "$exit" -eq 1
 +      then
 +          cnt=`{
 +              git diff-files --name-only
 +              git ls-files --unmerged
 +          } | wc -l`
 +          if test $best_cnt -le 0 -o $cnt -le $best_cnt
 +          then
 +              best_strategy=$strategy
 +              best_cnt=$cnt
 +          fi
 +      fi
 +      continue
 +    }
 +
 +    # Automerge succeeded.
 +    result_tree=$(git write-tree) && break
 +done
 +
 +# If we have a resulting tree, that means the strategy module
 +# auto resolved the merge cleanly.
 +if test '' != "$result_tree"
 +then
 +    if test "$allow_fast_forward" = "t"
 +    then
 +        parents=$(git show-branch --independent "$head" "$@")
 +    else
 +        parents=$(git rev-parse "$head" "$@")
 +    fi
 +    parents=$(echo "$parents" | sed -e 's/^/-p /')
 +    result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
 +    finish "$result_commit" "Merge made by $wt_strategy."
 +    dropsave
 +    exit 0
 +fi
 +
 +# Pick the result from the best strategy and have the user fix it up.
 +case "$best_strategy" in
 +'')
 +      restorestate
 +      case "$use_strategies" in
 +      ?*' '?*)
 +              echo >&2 "No merge strategy handled the merge."
 +              ;;
 +      *)
 +              echo >&2 "Merge with strategy $use_strategies failed."
 +              ;;
 +      esac
 +      exit 2
 +      ;;
 +"$wt_strategy")
 +      # We already have its result in the working tree.
 +      ;;
 +*)
 +      echo "Rewinding the tree to pristine..."
 +      restorestate
 +      echo "Using the $best_strategy to prepare resolving by hand."
 +      git-merge-$best_strategy $common -- "$head_arg" "$@"
 +      ;;
 +esac
 +
 +if test "$squash" = t
 +then
 +      finish
 +else
 +      for remote
 +      do
 +              echo $remote
 +      done >"$GIT_DIR/MERGE_HEAD"
 +      printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG"
 +fi
 +
 +if test "$merge_was_ok" = t
 +then
 +      echo >&2 \
 +      "Automatic merge went well; stopped before committing as requested"
 +      exit 0
 +else
 +      {
 +          echo '
 +Conflicts:
 +'
 +              git ls-files --unmerged |
 +              sed -e 's/^[^   ]*      /       /' |
 +              uniq
 +      } >>"$GIT_DIR/MERGE_MSG"
 +      git rerere
 +      die "Automatic merge failed; fix conflicts and then commit the result."
 +fi
diff --combined git-am.sh
index cc8787b460e601889fd90c5dba2055e9eb85bc22,14578ceb57645502fe9af25c4c1cfd757a852b68..c5b0039d860d6820769488ed8f22272c9a25fdb7
+++ b/git-am.sh
@@@ -5,9 -5,9 +5,9 @@@
  SUBDIRECTORY_OK=Yes
  OPTIONS_KEEPDASHDASH=
  OPTIONS_SPEC="\
- git-am [options] [<mbox>|<Maildir>...]
- git-am [options] --resolved
- git-am [options] --skip
 -git am [options] <mbox>|<Maildir>...
++git am [options] [<mbox>|<Maildir>...]
+ git am [options] --resolved
+ git am [options] --skip
  --
  d,dotest=       (removed -- do not use)
  i,interactive   run interactively
@@@ -30,8 -30,7 +30,8 @@@ set_reflog_action a
  require_work_tree
  cd_to_toplevel
  
 -git var GIT_COMMITTER_IDENT >/dev/null || exit
 +git var GIT_COMMITTER_IDENT >/dev/null ||
 +      die "You need to set your committer info first"
  
  stop_here () {
      echo "$1" >"$dotest/next"
@@@ -119,7 -118,7 +119,7 @@@ It does not apply to blobs recorded in 
  }
  
  prec=4
 -dotest=".dotest"
 +dotest="$GIT_DIR/rebase"
  sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
  resolvemsg= resume=
  git_apply_opt=
@@@ -195,7 -194,7 +195,7 @@@ the
                false
                ;;
        esac ||
 -      die "previous dotest directory $dotest still exists but mbox given."
 +      die "previous rebase directory $dotest still exists but mbox given."
        resume=yes
  else
        # Make sure we are not given --skip nor --resolved
                : >"$dotest/rebasing"
        else
                : >"$dotest/applying"
 +              git update-ref ORIG_HEAD HEAD
        fi
  fi
  
@@@ -325,7 -323,7 +325,7 @@@ d
                        <"$dotest"/info >/dev/null &&
                        go_next && continue
  
 -              test -s $dotest/patch || {
 +              test -s "$dotest/patch" || {
                        echo "Patch is empty.  Was it split wrong?"
                        stop_here $this
                }
diff --combined git-cvsserver.perl
index b00d1c29cd3a7d2cd8bbcd7ea4f531e050fcfcb9,d2d374a6ced994d349a673a98e69c01793d2678c..e5ba57f3946cc5f38cd25bfd0b52ff6ac2209c5b
@@@ -101,7 -101,7 +101,7 @@@ my $work 
  $log->info("--------------- STARTING -----------------");
  
  my $usage =
-     "Usage: git-cvsserver [options] [pserver|server] [<directory> ...]\n".
+     "Usage: git cvsserver [options] [pserver|server] [<directory> ...]\n".
      "    --base-path <path>  : Prepend to requested CVSROOT\n".
      "    --strict-paths      : Don't allow recursing into subdirectories\n".
      "    --export-all        : Don't check for gitcvs.enabled in config\n".
@@@ -1884,7 -1884,7 +1884,7 @@@ sub req_annotat
      }
  
      # done; get out of the tempdir
 -    cleanupWorkDir();
 +    cleanupWorkTree();
  
      print "ok\n";
  
diff --combined git-quiltimport.sh
index d1efa1d7419c23b65af1dccf4b8a00ed0e94c79c,e0649cd69838a24513cb6f3173fdff320aae7646..c9aecfd9132f2cb199add497d6c6ab2ebb79fdba
@@@ -1,7 -1,7 +1,7 @@@
  #!/bin/sh
  OPTIONS_KEEPDASHDASH=
  OPTIONS_SPEC="\
- git-quiltimport [options]
+ git quiltimport [options]
  --
  n,dry-run     dry run
  author=       author name and email address for patches without any
@@@ -53,7 -53,7 +53,7 @@@ if ! [ -d "$QUILT_PATCHES" ] ; the
  fi
  
  # Temporary directories
 -tmp_dir=.dotest
 +tmp_dir="$GIT_DIR"/rebase
  tmp_msg="$tmp_dir/msg"
  tmp_patch="$tmp_dir/patch"
  tmp_info="$tmp_dir/info"
diff --combined git-svn.perl
index 3750e47c202ebdcb25e657eecd69ef5252d45221,f6b878a32264a24bff7bb79b090f353797b92ff8..28aeb03fdcb65d8b63851cc92e74327b3325d8b8
@@@ -261,7 -261,7 +261,7 @@@ sub usage 
        my $fd = $exit ? \*STDERR : \*STDOUT;
        print $fd <<"";
  git-svn - bidirectional operations between a single Subversion tree and git
- Usage: $0 <command> [options] [arguments]\n
+ Usage: git svn <command> [options] [arguments]\n
  
        print $fd "Available commands:\n" unless $cmd;
  
@@@ -537,13 -537,13 +537,13 @@@ sub cmd_find_rev 
                my $head = shift;
                $head ||= 'HEAD';
                my @refs;
 -              my (undef, undef, undef, $gs) = working_head_info($head, \@refs);
 +              my (undef, undef, $uuid, $gs) = working_head_info($head, \@refs);
                unless ($gs) {
                        die "Unable to determine upstream SVN information from ",
                            "$head history\n";
                }
                my $desired_revision = substr($revision_or_hash, 1);
 -              $result = $gs->rev_map_get($desired_revision);
 +              $result = $gs->rev_map_get($desired_revision, $uuid);
        } else {
                my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
                $result = $rev;
@@@ -1162,7 -1162,7 +1162,7 @@@ sub working_head_info 
                if (defined $url && defined $rev) {
                        next if $max{$url} and $max{$url} < $rev;
                        if (my $gs = Git::SVN->find_by_url($url)) {
 -                              my $c = $gs->rev_map_get($rev);
 +                              my $c = $gs->rev_map_get($rev, $uuid);
                                if ($c && $c eq $hash) {
                                        close $fh; # break the pipe
                                        return ($url, $rev, $uuid, $gs);
@@@ -1416,17 -1416,11 +1416,17 @@@ sub fetch_all 
  
  sub read_all_remotes {
        my $r = {};
 +      my $use_svm_props = eval { command_oneline(qw/config --bool
 +          svn.useSvmProps/) };
 +      $use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
        foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
                if (m!^(.+)\.fetch=\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!) {
                        my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
                        $local_ref =~ s{^/}{};
                        $r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
 +                      $r->{$remote}->{svm} = {} if $use_svm_props;
 +              } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
 +                      $r->{$1}->{svm} = {};
                } elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
                        $r->{$1}->{url} = $2;
                } elsif (m!^(.+)\.(branches|tags)=
                        }
                }
        }
 +
 +      map {
 +              if (defined $r->{$_}->{svm}) {
 +                      my $svm;
 +                      eval {
 +                              my $section = "svn-remote.$_";
 +                              $svm = {
 +                                      source => tmp_config('--get',
 +                                          "$section.svm-source"),
 +                                      replace => tmp_config('--get',
 +                                          "$section.svm-replace"),
 +                              }
 +                      };
 +                      $r->{$_}->{svm} = $svm;
 +              }
 +      } keys %$r;
 +
        $r;
  }
  
@@@ -1586,21 -1563,13 +1586,21 @@@ sub find_by_url { # repos_root and, pat
                }
                my $p = $path;
                my $rwr = rewrite_root({repo_id => $repo_id});
 +              my $svm = $remotes->{$repo_id}->{svm}
 +                      if defined $remotes->{$repo_id}->{svm};
                unless (defined $p) {
                        $p = $full_url;
                        my $z = $u;
 +                      my $prefix = '';
                        if ($rwr) {
                                $z = $rwr;
 +                      } elsif (defined $svm) {
 +                              $z = $svm->{source};
 +                              $prefix = $svm->{replace};
 +                              $prefix =~ s#^\Q$u\E(?:/|$)##;
 +                              $prefix =~ s#/$##;
                        }
 -                      $p =~ s#^\Q$z\E(?:/|$)## or next;
 +                      $p =~ s#^\Q$z\E(?:/|$)#$prefix# or next;
                }
                foreach my $f (keys %$fetch) {
                        next if $f ne $p;
@@@ -4650,7 -4619,7 +4650,7 @@@ sub migrate_from_v1 
        mkpath([$svn_dir]);
        print STDERR "Data from a previous version of git-svn exists, but\n\t",
                     "$svn_dir\n\t(required for this version ",
 -                   "($::VERSION) of git-svn) does not. exist\n";
 +                   "($::VERSION) of git-svn) does not exist.\n";
        my ($fh, $ctx) = command_output_pipe(qw/rev-parse --symbolic --all/);
        while (<$fh>) {
                my $x = $_;
diff --combined help.c
index 52d39b88a36e92b00828fdb8c8d056f9b79a37e0,ee15955b0bdf665681c3e7762eae4b925bec64eb..bfc84aed10d49b1eb641d920a102e94d2e905fda
--- 1/help.c
--- 2/help.c
+++ b/help.c
@@@ -40,7 -40,7 +40,7 @@@ static struct option builtin_help_optio
  };
  
  static const char * const builtin_help_usage[] = {
-       "git-help [--all] [--man|--web|--info] [command]",
+       "git help [--all] [--man|--web|--info] [command]",
        NULL
  };
  
@@@ -633,29 -633,15 +633,29 @@@ static void show_info_page(const char *
  static void get_html_page_path(struct strbuf *page_path, const char *page)
  {
        struct stat st;
 +      const char *html_path = system_path(GIT_HTML_PATH);
  
        /* Check that we have a git documentation directory. */
 -      if (stat(GIT_HTML_PATH "/git.html", &st) || !S_ISREG(st.st_mode))
 -              die("'%s': not a documentation directory.", GIT_HTML_PATH);
 +      if (stat(mkpath("%s/git.html", html_path), &st)
 +          || !S_ISREG(st.st_mode))
 +              die("'%s': not a documentation directory.", html_path);
  
        strbuf_init(page_path, 0);
 -      strbuf_addf(page_path, GIT_HTML_PATH "/%s.html", page);
 +      strbuf_addf(page_path, "%s/%s.html", html_path, page);
  }
  
 +/*
 + * If open_html is not defined in a platform-specific way (see for
 + * example compat/mingw.h), we use the script web--browse to display
 + * HTML.
 + */
 +#ifndef open_html
 +void open_html(const char *path)
 +{
 +      execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
 +}
 +#endif
 +
  static void show_html_page(const char *git_cmd)
  {
        const char *page = cmd_to_page(git_cmd);
  
        get_html_page_path(&page_path, page);
  
 -      execl_git_cmd("web--browse", "-c", "help.browser", page_path.buf, NULL);
 +      open_html(page_path.buf);
  }
  
  void help_unknown_cmd(const char *cmd)
diff --combined index-pack.c
index b4ec7361748640c7ced04c10e9087e12c553d928,bb2e6a306f94b8dce8a5a1cd0b9cbec94f432322..ac20a46d1594ee1350b11d001f92bf0a8d887890
@@@ -10,7 -10,7 +10,7 @@@
  #include "fsck.h"
  
  static const char index_pack_usage[] =
- "git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
+ "git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
  
  struct object_entry
  {
@@@ -26,14 -26,6 +26,14 @@@ union delta_base 
        off_t offset;
  };
  
 +struct base_data {
 +      struct base_data *base;
 +      struct base_data *child;
 +      struct object_entry *obj;
 +      void *data;
 +      unsigned long size;
 +};
 +
  /*
   * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
   * to memcmp() only the first 20 bytes.
@@@ -51,8 -43,6 +51,8 @@@ struct delta_entr
  
  static struct object_entry *objects;
  static struct delta_entry *deltas;
 +static struct base_data *base_cache;
 +static size_t base_cache_used;
  static int nr_objects;
  static int nr_deltas;
  static int nr_resolved_deltas;
@@@ -221,46 -211,6 +221,46 @@@ static void bad_object(unsigned long of
        die("pack has bad object at offset %lu: %s", offset, buf);
  }
  
 +static void prune_base_data(struct base_data *retain)
 +{
 +      struct base_data *b = base_cache;
 +      for (b = base_cache;
 +           base_cache_used > delta_base_cache_limit && b;
 +           b = b->child) {
 +              if (b->data && b != retain) {
 +                      free(b->data);
 +                      b->data = NULL;
 +                      base_cache_used -= b->size;
 +              }
 +      }
 +}
 +
 +static void link_base_data(struct base_data *base, struct base_data *c)
 +{
 +      if (base)
 +              base->child = c;
 +      else
 +              base_cache = c;
 +
 +      c->base = base;
 +      c->child = NULL;
 +      base_cache_used += c->size;
 +      prune_base_data(c);
 +}
 +
 +static void unlink_base_data(struct base_data *c)
 +{
 +      struct base_data *base = c->base;
 +      if (base)
 +              base->child = NULL;
 +      else
 +              base_cache = NULL;
 +      if (c->data) {
 +              free(c->data);
 +              base_cache_used -= c->size;
 +      }
 +}
 +
  static void *unpack_entry_data(unsigned long offset, unsigned long size)
  {
        z_stream stream;
@@@ -476,60 -426,33 +476,60 @@@ static void sha1_object(const void *dat
        }
  }
  
 -static void resolve_delta(struct object_entry *delta_obj, void *base_data,
 -                        unsigned long base_size, enum object_type type)
 +static void *get_base_data(struct base_data *c)
 +{
 +      if (!c->data) {
 +              struct object_entry *obj = c->obj;
 +
 +              if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
 +                      void *base = get_base_data(c->base);
 +                      void *raw = get_data_from_pack(obj);
 +                      c->data = patch_delta(
 +                              base, c->base->size,
 +                              raw, obj->size,
 +                              &c->size);
 +                      free(raw);
 +                      if (!c->data)
 +                              bad_object(obj->idx.offset, "failed to apply delta");
 +              } else
 +                      c->data = get_data_from_pack(obj);
 +
 +              base_cache_used += c->size;
 +              prune_base_data(c);
 +      }
 +      return c->data;
 +}
 +
 +static void resolve_delta(struct object_entry *delta_obj,
 +                        struct base_data *base_obj, enum object_type type)
  {
        void *delta_data;
        unsigned long delta_size;
 -      void *result;
 -      unsigned long result_size;
        union delta_base delta_base;
        int j, first, last;
 +      struct base_data result;
  
        delta_obj->real_type = type;
        delta_data = get_data_from_pack(delta_obj);
        delta_size = delta_obj->size;
 -      result = patch_delta(base_data, base_size, delta_data, delta_size,
 -                           &result_size);
 +      result.data = patch_delta(get_base_data(base_obj), base_obj->size,
 +                           delta_data, delta_size,
 +                           &result.size);
        free(delta_data);
 -      if (!result)
 +      if (!result.data)
                bad_object(delta_obj->idx.offset, "failed to apply delta");
 -      sha1_object(result, result_size, type, delta_obj->idx.sha1);
 +      sha1_object(result.data, result.size, type, delta_obj->idx.sha1);
        nr_resolved_deltas++;
  
 +      result.obj = delta_obj;
 +      link_base_data(base_obj, &result);
 +
        hashcpy(delta_base.sha1, delta_obj->idx.sha1);
        if (!find_delta_children(&delta_base, &first, &last)) {
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
                        if (child->real_type == OBJ_REF_DELTA)
 -                              resolve_delta(child, result, result_size, type);
 +                              resolve_delta(child, &result, type);
                }
        }
  
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
                        if (child->real_type == OBJ_OFS_DELTA)
 -                              resolve_delta(child, result, result_size, type);
 +                              resolve_delta(child, &result, type);
                }
        }
  
 -      free(result);
 +      unlink_base_data(&result);
  }
  
  static int compare_delta_entry(const void *a, const void *b)
@@@ -558,6 -481,7 +558,6 @@@ static void parse_pack_objects(unsigne
  {
        int i;
        struct delta_entry *delta = deltas;
 -      void *data;
        struct stat st;
  
        /*
                                nr_objects);
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
 -              data = unpack_raw_entry(obj, &delta->base);
 +              void *data = unpack_raw_entry(obj, &delta->base);
                obj->real_type = obj->type;
                if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
                        nr_deltas++;
                struct object_entry *obj = &objects[i];
                union delta_base base;
                int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
 +              struct base_data base_obj;
  
                if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
                        continue;
                ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
                if (!ref && !ofs)
                        continue;
 -              data = get_data_from_pack(obj);
 +              base_obj.data = get_data_from_pack(obj);
 +              base_obj.size = obj->size;
 +              base_obj.obj = obj;
 +              link_base_data(NULL, &base_obj);
 +
                if (ref)
                        for (j = ref_first; j <= ref_last; j++) {
                                struct object_entry *child = objects + deltas[j].obj_no;
                                if (child->real_type == OBJ_REF_DELTA)
 -                                      resolve_delta(child, data,
 -                                                    obj->size, obj->type);
 +                                      resolve_delta(child, &base_obj, obj->type);
                        }
                if (ofs)
                        for (j = ofs_first; j <= ofs_last; j++) {
                                struct object_entry *child = objects + deltas[j].obj_no;
                                if (child->real_type == OBJ_OFS_DELTA)
 -                                      resolve_delta(child, data,
 -                                                    obj->size, obj->type);
 +                                      resolve_delta(child, &base_obj, obj->type);
                        }
 -              free(data);
 +              unlink_base_data(&base_obj);
                display_progress(progress, nr_resolved_deltas);
        }
  }
@@@ -680,8 -601,7 +680,8 @@@ static int write_compressed(int fd, voi
        return size;
  }
  
 -static void append_obj_to_pack(const unsigned char *sha1, void *buf,
 +static struct object_entry *append_obj_to_pack(
 +                             const unsigned char *sha1, void *buf,
                               unsigned long size, enum object_type type)
  {
        struct object_entry *obj = &objects[nr_objects++];
        obj[1].idx.offset = obj[0].idx.offset + n;
        obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32);
        hashcpy(obj->idx.sha1, sha1);
 +      return obj;
  }
  
  static int delta_pos_compare(const void *_a, const void *_b)
@@@ -737,31 -656,28 +737,31 @@@ static void fix_unresolved_deltas(int n
  
        for (i = 0; i < n; i++) {
                struct delta_entry *d = sorted_by_pos[i];
 -              void *data;
 -              unsigned long size;
                enum object_type type;
                int j, first, last;
 +              struct base_data base_obj;
  
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
                        continue;
 -              data = read_sha1_file(d->base.sha1, &type, &size);
 -              if (!data)
 +              base_obj.data = read_sha1_file(d->base.sha1, &type, &base_obj.size);
 +              if (!base_obj.data)
                        continue;
  
 +              if (check_sha1_signature(d->base.sha1, base_obj.data,
 +                              base_obj.size, typename(type)))
 +                      die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
 +              base_obj.obj = append_obj_to_pack(d->base.sha1, base_obj.data,
 +                      base_obj.size, type);
 +              link_base_data(NULL, &base_obj);
 +
                find_delta_children(&d->base, &first, &last);
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
                        if (child->real_type == OBJ_REF_DELTA)
 -                              resolve_delta(child, data, size, type);
 +                              resolve_delta(child, &base_obj, type);
                }
  
 -              if (check_sha1_signature(d->base.sha1, data, size, typename(type)))
 -                      die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
 -              append_obj_to_pack(d->base.sha1, data, size, type);
 -              free(data);
 +              unlink_base_data(&base_obj);
                display_progress(progress, nr_resolved_deltas);
        }
        free(sorted_by_pos);
diff --combined t/t4150-am.sh
index 5cbd5ef6798bcef1c011893a4759cac4047dc6b7,476f20b50a402f6843b4588d93d3acfdacf3603b..98ba020d895f05c269fbba2a91c66d874245b145
@@@ -102,7 -102,7 +102,7 @@@ test_expect_success 'am applies patch c
        git checkout first &&
        test_tick &&
        git am <patch1 &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        test -z "$(git diff second)" &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@@ -123,7 -123,7 +123,7 @@@ test_expect_success 'am changes committ
        test_tick &&
        git checkout first &&
        git am patch2 &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
        test -z "$(git diff master..HEAD)" &&
        test -z "$(git diff master^..HEAD^)" &&
@@@ -163,7 -163,7 +163,7 @@@ test_expect_success 'am without --keep 
  test_expect_success 'am --keep really keeps the subject' '
        git checkout HEAD^ &&
        git am --keep patch4 &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        git-cat-file commit HEAD |
                grep -q -F "Re: Re: Re: [PATCH 1/5 v2] third"
  '
@@@ -176,51 -176,51 +176,51 @@@ test_expect_success 'am -3 falls back t
        test_tick &&
        git commit -m "copied stuff" &&
        git am -3 lorem-move.patch &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        test -z "$(git diff lorem)"
  '
  
  test_expect_success 'am pauses on conflict' '
        git checkout lorem2^^ &&
-       ! git am lorem-move.patch &&
+       test_must_fail git am lorem-move.patch &&
 -      test -d .dotest
 +      test -d .git/rebase
  '
  
  test_expect_success 'am --skip works' '
        git am --skip &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        test -z "$(git diff lorem2^^ -- file)" &&
        test goodbye = "$(cat another)"
  '
  
  test_expect_success 'am --resolved works' '
        git checkout lorem2^^ &&
-       ! git am lorem-move.patch &&
+       test_must_fail git am lorem-move.patch &&
 -      test -d .dotest &&
 +      test -d .git/rebase &&
        echo resolved >>file &&
        git add file &&
        git am --resolved &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        test goodbye = "$(cat another)"
  '
  
  test_expect_success 'am takes patches from a Pine mailbox' '
        git checkout first &&
        cat pine patch1 | git am &&
 -      ! test -d .dotest &&
 +      ! test -d .git/rebase &&
        test -z "$(git diff master^..HEAD)"
  '
  
  test_expect_success 'am fails on mail without patch' '
-       ! git am <failmail &&
+       test_must_fail git am <failmail &&
 -      rm -r .dotest/
 +      rm -r .git/rebase/
  '
  
  test_expect_success 'am fails on empty patch' '
        echo "---" >>failmail &&
-       ! git am <failmail &&
+       test_must_fail git am <failmail &&
        git am --skip &&
 -      ! test -d .dotest
 +      ! test -d .git/rebase
  '
  
  test_expect_success 'am works from stdin in subdirectory' '
diff --combined t/t4200-rerere.sh
index b5a4202998f51cd282ae46c7caec6e615ee47e3f,746d61f8451d42b8a62d2e7afb9bde5256ff592b..b68ab11f2915789cd04ac6bd43363aeab2079198
@@@ -9,8 -9,6 +9,8 @@@ test_description='git rerer
  . ./test-lib.sh
  
  cat > a1 << EOF
 +Some title
 +==========
  Whether 'tis nobler in the mind to suffer
  The slings and arrows of outrageous fortune,
  Or to take arms against a sea of troubles,
@@@ -26,8 -24,6 +26,8 @@@ git commit -q -a -m initia
  
  git checkout -b first
  cat >> a1 << EOF
 +Some title
 +==========
  To die, to sleep;
  To sleep: perchance to dream: ay, there's the rub;
  For in that sleep of death what dreams may come
@@@ -39,13 -35,13 +39,13 @@@ git commit -q -a -m firs
  
  git checkout -b second master
  git show first:a1 |
 -sed -e 's/To die, t/To die! T/' > a1
 +sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1
  echo "* END *" >>a1
  git commit -q -a -m second
  
  test_expect_success 'nothing recorded without rerere' '
        (rm -rf .git/rr-cache; git config rerere.enabled false) &&
-       ! git merge first &&
+       test_must_fail git merge first &&
        ! test -d .git/rr-cache
  '
  
@@@ -54,19 -50,19 +54,19 @@@ test_expect_success 'conflicting merge
        git reset --hard &&
        mkdir .git/rr-cache &&
        git config --unset rerere.enabled &&
-       ! git merge first
+       test_must_fail git merge first
  '
  
 -sha1=$(sed -e 's/     .*//' .git/rr-cache/MERGE_RR)
 +sha1=$(sed -e 's/     .*//' .git/MERGE_RR)
  rr=.git/rr-cache/$sha1
 -test_expect_success 'recorded preimage' "grep ======= $rr/preimage"
 +test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
  
  test_expect_success 'rerere.enabled works, too' '
        rm -rf .git/rr-cache &&
        git config rerere.enabled true &&
        git reset --hard &&
-       ! git merge first &&
+       test_must_fail git merge first &&
 -      grep ======= $rr/preimage
 +      grep ^=======$ $rr/preimage
  '
  
  test_expect_success 'no postimage or thisimage yet' \
@@@ -75,7 -71,7 +75,7 @@@
  test_expect_success 'preimage has right number of lines' '
  
        cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
 -      test $cnt = 9
 +      test $cnt = 13
  
  '
  
@@@ -84,23 -80,13 +84,23 @@@ git show first:a1 > a
  cat > expect << EOF
  --- a/a1
  +++ b/a1
 -@@ -6,17 +6,9 @@
 +@@ -1,4 +1,4 @@
 +-Some Title
 ++Some title
 + ==========
 + Whether 'tis nobler in the mind to suffer
 + The slings and arrows of outrageous fortune,
 +@@ -8,21 +8,11 @@
   The heart-ache and the thousand natural shocks
   That flesh is heir to, 'tis a consummation
   Devoutly to be wish'd.
  -<<<<<<<
 +-Some Title
 +-==========
  -To die! To sleep;
  -=======
 + Some title
 + ==========
   To die, to sleep;
  ->>>>>>>
   To sleep: perchance to dream: ay, there's the rub;
@@@ -134,16 -120,16 +134,16 @@@ test_expect_success 'another conflictin
        git checkout -b third master &&
        git show second^:a1 | sed "s/To die: t/To die! T/" > a1 &&
        git commit -q -a -m third &&
-       ! git pull . first
+       test_must_fail git pull . first
  '
  
  git show first:a1 | sed 's/To die: t/To die! T/' > expect
 -test_expect_success 'rerere kicked in' "! grep ======= a1"
 +test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
  
  test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
  
  rm $rr/postimage
 -echo "$sha1   a1" | perl -pe 'y/\012/\000/' > .git/rr-cache/MERGE_RR
 +echo "$sha1   a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
  
  test_expect_success 'rerere clear' 'git rerere clear'
  
@@@ -189,8 -175,8 +189,8 @@@ test_expect_success 'file2 added differ
        echo Bello > file2 &&
        git add file2 &&
        git commit -m version2 &&
-       ! git merge fourth &&
+       test_must_fail git merge fourth &&
 -      sha1=$(sed -e "s/       .*//" .git/rr-cache/MERGE_RR) &&
 +      sha1=$(sed -e "s/       .*//" .git/MERGE_RR) &&
        rr=.git/rr-cache/$sha1 &&
        echo Cello > file2 &&
        git add file2 &&
diff --combined t/t7600-merge.sh
index 9ab5bdacc41d026ea81c65b0d95a61a61521429a,7d182b5f11ca40b35a74aba04df1086bc91724be..26cf8dc7c15ce397d7c36b2dd39a9d38db9856d7
@@@ -126,7 -126,7 +126,7 @@@ verify_merge() 
                echo "[OOPS] unmerged files"
                false
        fi &&
-       if ! git diff --exit-code
+       if test_must_fail git diff --exit-code
        then
                echo "[OOPS] working tree != index"
                false
@@@ -221,13 -221,37 +221,13 @@@ test_expect_success 'setup' 
  
  test_debug 'gitk --all'
  
 -test_expect_success 'test option parsing' '
 -      if git merge -$ c1
 -      then
 -              echo "[OOPS] -$ accepted"
 -              false
 -      fi &&
 -      if git merge --no-such c1
 -      then
 -              echo "[OOPS] --no-such accepted"
 -              false
 -      fi &&
 -      if git merge -s foobar c1
 -      then
 -              echo "[OOPS] -s foobar accepted"
 -              false
 -      fi &&
 -      if git merge -s=foobar c1
 -      then
 -              echo "[OOPS] -s=foobar accepted"
 -              false
 -      fi &&
 -      if git merge -m
 -      then
 -              echo "[OOPS] missing commit msg accepted"
 -              false
 -      fi &&
 -      if git merge
 -      then
 -              echo "[OOPS] missing commit references accepted"
 -              false
 -      fi
 +test_expect_failure 'test option parsing' '
 +      test_must_fail git merge -$ c1 &&
 +      test_must_fail git merge --no-such c1 &&
 +      test_must_fail git merge -s foobar c1 &&
 +      test_must_fail git merge -s=foobar c1 &&
 +      test_must_fail git merge -m &&
 +      test_must_fail git merge
  '
  
  test_expect_success 'merge c0 with c1' '
@@@ -441,51 -465,11 +441,51 @@@ test_expect_success 'merge log message
        git merge --no-log c2 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
        verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
 +
        git merge --log c3 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
 +      verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
 +
 +      git reset --hard HEAD^ &&
 +      git config merge.log yes &&
 +      git merge c3 &&
 +      git show -s --pretty=format:%b HEAD >msg.act &&
        verify_diff msg.log msg.act "[OOPS] bad merge log message"
  '
  
  test_debug 'gitk --all'
  
 +test_expect_success 'merge c1 with c0, c2, c0, and c1' '
 +       git reset --hard c1 &&
 +       git config branch.master.mergeoptions "" &&
 +       test_tick &&
 +       git merge c0 c2 c0 c1 &&
 +       verify_merge file result.1-5 &&
 +       verify_parents $c1 $c2
 +'
 +
 +test_debug 'gitk --all'
 +
 +test_expect_success 'merge c1 with c0, c2, c0, and c1' '
 +       git reset --hard c1 &&
 +       git config branch.master.mergeoptions "" &&
 +       test_tick &&
 +       git merge c0 c2 c0 c1 &&
 +       verify_merge file result.1-5 &&
 +       verify_parents $c1 $c2
 +'
 +
 +test_debug 'gitk --all'
 +
 +test_expect_success 'merge c1 with c1 and c2' '
 +       git reset --hard c1 &&
 +       git config branch.master.mergeoptions "" &&
 +       test_tick &&
 +       git merge c1 c2 &&
 +       verify_merge file result.1-5 &&
 +       verify_parents $c1 $c2
 +'
 +
 +test_debug 'gitk --all'
 +
  test_done
index 27a65e05df20c2695b9fb849be62d715c968a433,a57ff68e070399e500ad74812291c541edaedd3e..08bf1f08e331790d1a4e2fd1c698ecd5ea860e41
@@@ -27,7 -27,7 +27,7 @@@ test_expect_success 'commit change fro
  test_expect_success 'commit conflicting change from git' '
        echo second line from git >> file &&
        git commit -a -m "second line from git" &&
-       ! git-svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
+       test_must_fail git-svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
  '
  
  test_expect_success 'commit complementing change from git' '
@@@ -52,7 -52,7 +52,7 @@@ test_expect_success 'dcommit fails to c
        rm -rf t.svn &&
        echo "fourth line from git" >> file &&
        git commit -a -m "fourth line from git" &&
-       ! git-svn dcommit
+       test_must_fail git-svn dcommit
        '
  
  test_expect_success 'dcommit does the svn equivalent of an index merge' "
@@@ -83,11 -83,11 +83,11 @@@ test_expect_success 'multiple dcommit f
        git commit -a -m 'new file' &&
        echo clobber > file &&
        git commit -a -m 'clobber' &&
-       ! git svn dcommit
+       test_must_fail git svn dcommit
        "
  
  
 -test_expect_success 'check that rebase really failed' 'test -d .dotest'
 +test_expect_success 'check that rebase really failed' 'test -d .git/rebase'
  
  test_expect_success 'resolve, continue the rebase and dcommit' "
        echo clobber and I really mean it > file &&