Merge branch 'jk/format-patch-am'
authorJunio C Hamano <gitster@pobox.com>
Tue, 31 May 2011 19:19:11 +0000 (12:19 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 31 May 2011 19:19:11 +0000 (12:19 -0700)
* jk/format-patch-am:
format-patch: preserve subject newlines with -k
clean up calling conventions for pretty.c functions
pretty: add pp_commit_easy function for simple callers
mailinfo: always clean up rfc822 header folding
t: test subject handling in format-patch / am pipeline

Conflicts:
builtin/branch.c
builtin/log.c
commit.h

1  2 
builtin/branch.c
builtin/checkout.c
builtin/log.c
builtin/merge.c
builtin/rev-list.c
builtin/shortlog.c
builtin/show-branch.c
commit.h
log-tree.c
pretty.c
revision.h
diff --combined builtin/branch.c
index 9cca1b9afc20caf6333d0269386249f41fa8746f,d8f15221ed5b7f67875549685fe7aad92909a1d4..d6ab93bfbb369965e76e384e6984a29b667cdfbe
@@@ -133,12 -133,12 +133,12 @@@ static int branch_merged(int kind, cons
        if ((head_rev != reference_rev) &&
            in_merge_bases(rev, &head_rev, 1) != merged) {
                if (merged)
 -                      warning("deleting branch '%s' that has been merged to\n"
 -                              "         '%s', but it is not yet merged to HEAD.",
 +                      warning(_("deleting branch '%s' that has been merged to\n"
 +                              "         '%s', but not yet merged to HEAD."),
                                name, reference_name);
                else
 -                      warning("not deleting branch '%s' that is not yet merged to\n"
 -                              "         '%s', even though it is merged to HEAD.",
 +                      warning(_("not deleting branch '%s' that is not yet merged to\n"
 +                              "         '%s', even though it is merged to HEAD."),
                                name, reference_name);
        }
        return merged;
@@@ -157,8 -157,7 +157,8 @@@ static int delete_branches(int argc, co
        switch (kinds) {
        case REF_REMOTE_BRANCH:
                fmt = "refs/remotes/%s";
 -              remote = "remote ";
 +              /* TRANSLATORS: This is "remote " in "remote branch '%s' not found" */
 +              remote = _("remote ");
                force = 1;
                break;
        case REF_LOCAL_BRANCH:
                remote = "";
                break;
        default:
 -              die("cannot use -a with -d");
 +              die(_("cannot use -a with -d"));
        }
  
        if (!force) {
                head_rev = lookup_commit_reference(head_sha1);
                if (!head_rev)
 -                      die("Couldn't look up commit object for HEAD");
 +                      die(_("Couldn't look up commit object for HEAD"));
        }
        for (i = 0; i < argc; i++, strbuf_release(&bname)) {
                strbuf_branchname(&bname, argv[i]);
                if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
 -                      error("Cannot delete the branch '%s' "
 -                            "which you are currently on.", bname.buf);
 +                      error(_("Cannot delete the branch '%s' "
 +                            "which you are currently on."), bname.buf);
                        ret = 1;
                        continue;
                }
  
                name = xstrdup(mkpath(fmt, bname.buf));
                if (!resolve_ref(name, sha1, 1, NULL)) {
 -                      error("%sbranch '%s' not found.",
 +                      error(_("%sbranch '%s' not found."),
                                        remote, bname.buf);
                        ret = 1;
                        continue;
  
                rev = lookup_commit_reference(sha1);
                if (!rev) {
 -                      error("Couldn't look up commit object for '%s'", name);
 +                      error(_("Couldn't look up commit object for '%s'"), name);
                        ret = 1;
                        continue;
                }
  
                if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) {
 -                      error("The branch '%s' is not fully merged.\n"
 +                      error(_("The branch '%s' is not fully merged.\n"
                              "If you are sure you want to delete it, "
 -                            "run 'git branch -D %s'.", bname.buf, bname.buf);
 +                            "run 'git branch -D %s'."), bname.buf, bname.buf);
                        ret = 1;
                        continue;
                }
  
                if (delete_ref(name, sha1, 0)) {
 -                      error("Error deleting %sbranch '%s'", remote,
 +                      error(_("Error deleting %sbranch '%s'"), remote,
                              bname.buf);
                        ret = 1;
                } else {
                        struct strbuf buf = STRBUF_INIT;
 -                      printf("Deleted %sbranch %s (was %s).\n", remote,
 +                      printf(_("Deleted %sbranch %s (was %s).\n"), remote,
                               bname.buf,
                               find_unique_abbrev(sha1, DEFAULT_ABBREV));
                        strbuf_addf(&buf, "branch.%s", bname.buf);
                        if (git_config_rename_section(buf.buf, NULL) < 0)
 -                              warning("Update of config-file failed");
 +                              warning(_("Update of config-file failed"));
                        strbuf_release(&buf);
                }
        }
@@@ -301,7 -300,7 +301,7 @@@ static int append_ref(const char *refna
        if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
                commit = lookup_commit_reference_gently(sha1, 1);
                if (!commit) {
 -                      cb->ret = error("branch '%s' does not point at a commit", refname);
 +                      cb->ret = error(_("branch '%s' does not point at a commit"), refname);
                        return 0;
                }
  
@@@ -373,11 -372,11 +373,11 @@@ static void fill_tracking_info(struct s
                strbuf_addf(stat, "%s: ",
                        shorten_unambiguous_ref(branch->merge[0]->dst, 0));
        if (!ours)
 -              strbuf_addf(stat, "behind %d] ", theirs);
 +              strbuf_addf(stat, _("behind %d] "), theirs);
        else if (!theirs)
 -              strbuf_addf(stat, "ahead %d] ", ours);
 +              strbuf_addf(stat, _("ahead %d] "), ours);
        else
 -              strbuf_addf(stat, "ahead %d, behind %d] ", ours, theirs);
 +              strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs);
  }
  
  static int matches_merge_filter(struct commit *commit)
        return (is_merged == (merge_filter == SHOW_MERGED));
  }
  
-               struct pretty_print_context ctx = {0};
-               pretty_print_commit(CMIT_FMT_ONELINE, commit,
-                                   &subject, &ctx);
 +static void add_verbose_info(struct strbuf *out, struct ref_item *item,
 +                           int verbose, int abbrev)
 +{
 +      struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
 +      const char *sub = " **** invalid ref ****";
 +      struct commit *commit = item->commit;
 +
 +      if (commit && !parse_commit(commit)) {
++              pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
 +              sub = subject.buf;
 +      }
 +
 +      if (item->kind == REF_LOCAL_BRANCH)
 +              fill_tracking_info(&stat, item->name, verbose > 1);
 +
 +      strbuf_addf(out, " %s %s%s",
 +              find_unique_abbrev(item->commit->object.sha1, abbrev),
 +              stat.buf, sub);
 +      strbuf_release(&stat);
 +      strbuf_release(&subject);
 +}
 +
  static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
                           int abbrev, int current, char *prefix)
  {
  
        if (item->dest)
                strbuf_addf(&out, " -> %s", item->dest);
 -      else if (verbose) {
 -              struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
 -              const char *sub = " **** invalid ref ****";
 -
 -              commit = item->commit;
 -              if (commit && !parse_commit(commit)) {
 -                      pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
 -                      sub = subject.buf;
 -              }
 -
 -              if (item->kind == REF_LOCAL_BRANCH)
 -                      fill_tracking_info(&stat, item->name, verbose > 1);
 -
 -              strbuf_addf(&out, " %s %s%s",
 -                      find_unique_abbrev(item->commit->object.sha1, abbrev),
 -                      stat.buf, sub);
 -              strbuf_release(&stat);
 -              strbuf_release(&subject);
 -      }
 +      else if (verbose)
 +              /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
 +              add_verbose_info(&out, item, verbose, abbrev);
        printf("%s\n", out.buf);
        strbuf_release(&name);
        strbuf_release(&out);
@@@ -482,7 -473,7 +480,7 @@@ static void show_detached(struct ref_li
  
        if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
                struct ref_item item;
 -              item.name = xstrdup("(no branch)");
 +              item.name = xstrdup(_("(no branch)"));
                item.len = strlen(item.name);
                item.kind = REF_LOCAL_BRANCH;
                item.dest = NULL;
@@@ -542,7 -533,7 +540,7 @@@ static int print_ref_list(int kinds, in
        free_ref_list(&ref_list);
  
        if (cb.ret)
 -              error("some refs could not be read");
 +              error(_("some refs could not be read"));
  
        return cb.ret;
  }
@@@ -555,7 -546,7 +553,7 @@@ static void rename_branch(const char *o
        int recovery = 0;
  
        if (!oldname)
 -              die("cannot rename the current branch while not on any.");
 +              die(_("cannot rename the current branch while not on any."));
  
        if (strbuf_check_branch_ref(&oldref, oldname)) {
                /*
                if (resolve_ref(oldref.buf, sha1, 1, NULL))
                        recovery = 1;
                else
 -                      die("Invalid branch name: '%s'", oldname);
 +                      die(_("Invalid branch name: '%s'"), oldname);
        }
  
        if (strbuf_check_branch_ref(&newref, newname))
 -              die("Invalid branch name: '%s'", newname);
 +              die(_("Invalid branch name: '%s'"), newname);
  
        if (resolve_ref(newref.buf, sha1, 1, NULL) && !force)
 -              die("A branch named '%s' already exists.", newref.buf + 11);
 +              die(_("A branch named '%s' already exists."), newref.buf + 11);
  
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
  
        if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
 -              die("Branch rename failed");
 +              die(_("Branch rename failed"));
        strbuf_release(&logmsg);
  
        if (recovery)
 -              warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
 +              warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
  
        /* no need to pass logmsg here as HEAD didn't really move */
        if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
 -              die("Branch renamed to %s, but HEAD is not updated!", newname);
 +              die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
  
        strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
        strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
        strbuf_release(&newref);
        if (git_config_rename_section(oldsection.buf, newsection.buf) < 0)
 -              die("Branch is renamed, but update of config-file failed");
 +              die(_("Branch is renamed, but update of config-file failed"));
        strbuf_release(&oldsection);
        strbuf_release(&newsection);
  }
@@@ -608,7 -599,7 +606,7 @@@ static int opt_parse_merge_filter(cons
        if (!arg)
                arg = "HEAD";
        if (get_sha1(arg, merge_filter_ref))
 -              die("malformed object name %s", arg);
 +              die(_("malformed object name %s"), arg);
        return 0;
  }
  
@@@ -682,13 -673,13 +680,13 @@@ int cmd_branch(int argc, const char **a
  
        head = resolve_ref("HEAD", head_sha1, 0, NULL);
        if (!head)
 -              die("Failed to resolve HEAD as a valid ref.");
 +              die(_("Failed to resolve HEAD as a valid ref."));
        head = xstrdup(head);
        if (!strcmp(head, "HEAD")) {
                detached = 1;
        } else {
                if (prefixcmp(head, "refs/heads/"))
 -                      die("HEAD not found below refs/heads!");
 +                      die(_("HEAD not found below refs/heads!"));
                head += 11;
        }
        hashcpy(merge_filter_ref, head_sha1);
                rename_branch(argv[0], argv[1], rename > 1);
        else if (argc <= 2) {
                if (kinds != REF_LOCAL_BRANCH)
 -                      die("-a and -r options to 'git branch' do not make sense with a branch name");
 +                      die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
                              force_create, reflog, track);
        } else
diff --combined builtin/checkout.c
index 4761769512a8abcdd5652a93d39be18154e251dd,c1759dc3a44c7d4df4d9bed03f22ab7be8df2257..28cdc51b85e7d433dca085c0080f964d19a391b4
@@@ -30,7 -30,6 +30,7 @@@ struct checkout_opts 
        int quiet;
        int merge;
        int force;
 +      int force_detach;
        int writeout_stage;
        int writeout_error;
  
@@@ -79,10 -78,7 +79,10 @@@ static int update_some(const unsigned c
  
  static int read_tree_some(struct tree *tree, const char **pathspec)
  {
 -      read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL);
 +      struct pathspec ps;
 +      init_pathspec(&ps, pathspec);
 +      read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL);
 +      free_pathspec(&ps);
  
        /* update the index with the given tree's info
         * for all args, expanding wildcards, and exit
@@@ -107,10 -103,9 +107,10 @@@ static int check_stage(int stage, struc
                        return 0;
                pos++;
        }
 -      return error("path '%s' does not have %s version",
 -                   ce->name,
 -                   (stage == 2) ? "our" : "their");
 +      if (stage == 2)
 +              return error(_("path '%s' does not have our version"), ce->name);
 +      else
 +              return error(_("path '%s' does not have their version"), ce->name);
  }
  
  static int check_all_stages(struct cache_entry *ce, int pos)
            ce_stage(active_cache[pos+1]) != 2 ||
            strcmp(active_cache[pos+2]->name, ce->name) ||
            ce_stage(active_cache[pos+2]) != 3)
 -              return error("path '%s' does not have all three versions",
 +              return error(_("path '%s' does not have all three versions"),
                             ce->name);
        return 0;
  }
@@@ -135,10 -130,9 +135,10 @@@ static int checkout_stage(int stage, st
                        return checkout_entry(active_cache[pos], state, NULL);
                pos++;
        }
 -      return error("path '%s' does not have %s version",
 -                   ce->name,
 -                   (stage == 2) ? "our" : "their");
 +      if (stage == 2)
 +              return error(_("path '%s' does not have our version"), ce->name);
 +      else
 +              return error(_("path '%s' does not have their version"), ce->name);
  }
  
  static int checkout_merged(int pos, struct checkout *state)
            ce_stage(active_cache[pos+1]) != 2 ||
            strcmp(active_cache[pos+2]->name, path) ||
            ce_stage(active_cache[pos+2]) != 3)
 -              return error("path '%s' does not have all 3 versions", path);
 +              return error(_("path '%s' does not have all 3 versions"), path);
  
        read_mmblob(&ancestor, active_cache[pos]->sha1);
        read_mmblob(&ours, active_cache[pos+1]->sha1);
        free(theirs.ptr);
        if (status < 0 || !result_buf.ptr) {
                free(result_buf.ptr);
 -              return error("path '%s': cannot merge", path);
 +              return error(_("path '%s': cannot merge"), path);
        }
  
        /*
         */
        if (write_sha1_file(result_buf.ptr, result_buf.size,
                            blob_type, sha1))
 -              die("Unable to add merge result for '%s'", path);
 +              die(_("Unable to add merge result for '%s'"), path);
        ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
                              sha1,
                              path, 2, 0);
        if (!ce)
 -              die("make_cache_entry failed for path '%s'", path);
 +              die(_("make_cache_entry failed for path '%s'"), path);
        status = checkout_entry(ce, state, NULL);
        return status;
  }
@@@ -217,7 -211,7 +217,7 @@@ static int checkout_paths(struct tree *
  
        newfd = hold_locked_index(lock_file, 1);
        if (read_cache_preload(pathspec) < 0)
 -              return error("corrupt index file");
 +              return error(_("corrupt index file"));
  
        if (source_tree)
                read_tree_some(source_tree, pathspec);
                        if (!ce_stage(ce))
                                continue;
                        if (opts->force) {
 -                              warning("path '%s' is unmerged", ce->name);
 +                              warning(_("path '%s' is unmerged"), ce->name);
                        } else if (stage) {
                                errs |= check_stage(stage, ce, pos);
                        } else if (opts->merge) {
                                errs |= check_all_stages(ce, pos);
                        } else {
                                errs = 1;
 -                              error("path '%s' is unmerged", ce->name);
 +                              error(_("path '%s' is unmerged"), ce->name);
                        }
                        pos = skip_same_name(ce, pos) - 1;
                }
  
        if (write_cache(newfd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
 -              die("unable to write new index file");
 +              die(_("unable to write new index file"));
  
        resolve_ref("HEAD", rev, 0, &flag);
        head = lookup_commit_reference_gently(rev, 1);
@@@ -298,17 -292,16 +298,16 @@@ static void show_local_changes(struct o
        rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        if (diff_setup_done(&rev.diffopt) < 0)
 -              die("diff_setup_done failed");
 +              die(_("diff_setup_done failed"));
        add_pending_object(&rev, head, NULL);
        run_diff_index(&rev, 0);
  }
  
 -static void describe_detached_head(char *msg, struct commit *commit)
 +static void describe_detached_head(const char *msg, struct commit *commit)
  {
        struct strbuf sb = STRBUF_INIT;
-       struct pretty_print_context ctx = {0};
        parse_commit(commit);
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx);
+       pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        fprintf(stderr, "%s %s... %s\n", msg,
                find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
@@@ -372,7 -365,7 +371,7 @@@ static int merge_working_tree(struct ch
        int newfd = hold_locked_index(lock_file, 1);
  
        if (read_cache_preload(NULL) < 0)
 -              return error("corrupt index file");
 +              return error(_("corrupt index file"));
  
        resolve_undo_clear();
        if (opts->force) {
                refresh_cache(REFRESH_QUIET);
  
                if (unmerged_cache()) {
 -                      error("you need to resolve your current index first");
 +                      error(_("you need to resolve your current index first"));
                        return 1;
                }
  
                topts.dir->exclude_per_dir = ".gitignore";
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
 -                                         (unsigned char *)EMPTY_TREE_SHA1_BIN);
 +                                         EMPTY_TREE_SHA1_BIN);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                tree = parse_tree_indirect(new->commit->object.sha1);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
        if (write_cache(newfd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
 -              die("unable to write new index file");
 +              die(_("unable to write new index file"));
  
        if (!opts->force && !opts->quiet)
                show_local_changes(&new->commit->object, &opts->diff_options);
@@@ -525,7 -518,7 +524,7 @@@ static void update_refs_for_switch(stru
                                temp = log_all_ref_updates;
                                log_all_ref_updates = 1;
                                if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
 -                                      fprintf(stderr, "Can not do reflog for '%s'\n",
 +                                      fprintf(stderr, _("Can not do reflog for '%s'\n"),
                                            opts->new_orphan_branch);
                                        log_all_ref_updates = temp;
                                        return;
        strbuf_addf(&msg, "checkout: moving from %s to %s",
                    old_desc ? old_desc : "(invalid)", new->name);
  
 -      if (new->path) {
 +      if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
 +              /* Nothing to do. */
 +      } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
 +              update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
 +                         REF_NODEREF, DIE_ON_ERR);
 +              if (!opts->quiet) {
 +                      if (old->path && advice_detached_head)
 +                              detach_advice(old->path, new->name);
 +                      describe_detached_head(_("HEAD is now at"), new->commit);
 +              }
 +      } else if (new->path) { /* Switch branches. */
                create_symref("HEAD", new->path, msg.buf);
                if (!opts->quiet) {
 -                      if (old->path && !strcmp(new->path, old->path))
 -                              fprintf(stderr, "Already on '%s'\n",
 -                                      new->name);
 -                      else if (opts->new_branch)
 -                              fprintf(stderr, "Switched to%s branch '%s'\n",
 -                                      opts->branch_exists ? " and reset" : " a new",
 +                      if (old->path && !strcmp(new->path, old->path)) {
 +                              fprintf(stderr, _("Already on '%s'\n"),
                                        new->name);
 -                      else
 -                              fprintf(stderr, "Switched to branch '%s'\n",
 +                      } else if (opts->new_branch) {
 +                              if (opts->branch_exists)
 +                                      fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
 +                              else
 +                                      fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name);
 +                      } else {
 +                              fprintf(stderr, _("Switched to branch '%s'\n"),
                                        new->name);
 +                      }
                }
                if (old->path && old->name) {
                        char log_file[PATH_MAX], ref_file[PATH_MAX];
                        if (!file_exists(ref_file) && file_exists(log_file))
                                remove_path(log_file);
                }
 -      } else if (strcmp(new->name, "HEAD")) {
 -              update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
 -                         REF_NODEREF, DIE_ON_ERR);
 -              if (!opts->quiet) {
 -                      if (old->path && advice_detached_head)
 -                              detach_advice(old->path, new->name);
 -                      describe_detached_head("HEAD is now at", new->commit);
 -              }
        }
        remove_branch_state();
        strbuf_release(&msg);
 -      if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
 +      if (!opts->quiet &&
 +          (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
                report_tracking(new);
  }
  
-       struct pretty_print_context ctx = { 0 };
 +struct rev_list_args {
 +      int argc;
 +      int alloc;
 +      const char **argv;
 +};
 +
 +static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
 +{
 +      ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
 +      args->argv[args->argc++] = s;
 +}
 +
 +static int add_one_ref_to_rev_list_arg(const char *refname,
 +                                     const unsigned char *sha1,
 +                                     int flags,
 +                                     void *cb_data)
 +{
 +      add_one_rev_list_arg(cb_data, refname);
 +      return 0;
 +}
 +
 +static int clear_commit_marks_from_one_ref(const char *refname,
 +                                    const unsigned char *sha1,
 +                                    int flags,
 +                                    void *cb_data)
 +{
 +      struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 +      if (commit)
 +              clear_commit_marks(commit, -1);
 +      return 0;
 +}
 +
 +static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 +{
-       pretty_print_commit(CMIT_FMT_ONELINE, commit, sb, &ctx);
 +      parse_commit(commit);
 +      strbuf_addstr(sb, "  ");
 +      strbuf_addstr(sb,
 +              find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 +      strbuf_addch(sb, ' ');
++      pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
 +      strbuf_addch(sb, '\n');
 +}
 +
 +#define ORPHAN_CUTOFF 4
 +static void suggest_reattach(struct commit *commit, struct rev_info *revs)
 +{
 +      struct commit *c, *last = NULL;
 +      struct strbuf sb = STRBUF_INIT;
 +      int lost = 0;
 +      while ((c = get_revision(revs)) != NULL) {
 +              if (lost < ORPHAN_CUTOFF)
 +                      describe_one_orphan(&sb, c);
 +              last = c;
 +              lost++;
 +      }
 +      if (ORPHAN_CUTOFF < lost) {
 +              int more = lost - ORPHAN_CUTOFF;
 +              if (more == 1)
 +                      describe_one_orphan(&sb, last);
 +              else
 +                      strbuf_addf(&sb, _(" ... and %d more.\n"), more);
 +      }
 +
 +      fprintf(stderr,
 +              Q_(
 +              /* The singular version */
 +              "Warning: you are leaving %d commit behind, "
 +              "not connected to\n"
 +              "any of your branches:\n\n"
 +              "%s\n"
 +              "If you want to keep it by creating a new branch, "
 +              "this may be a good time\nto do so with:\n\n"
 +              " git branch new_branch_name %s\n\n",
 +              /* The plural version */
 +              "Warning: you are leaving %d commits behind, "
 +              "not connected to\n"
 +              "any of your branches:\n\n"
 +              "%s\n"
 +              "If you want to keep them by creating a new branch, "
 +              "this may be a good time\nto do so with:\n\n"
 +              " git branch new_branch_name %s\n\n",
 +              /* Give ngettext() the count */
 +              lost),
 +              lost,
 +              sb.buf,
 +              sha1_to_hex(commit->object.sha1));
 +      strbuf_release(&sb);
 +}
 +
 +/*
 + * We are about to leave commit that was at the tip of a detached
 + * HEAD.  If it is not reachable from any ref, this is the last chance
 + * for the user to do so without resorting to reflog.
 + */
 +static void orphaned_commit_warning(struct commit *commit)
 +{
 +      struct rev_list_args args = { 0, 0, NULL };
 +      struct rev_info revs;
 +
 +      add_one_rev_list_arg(&args, "(internal)");
 +      add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
 +      add_one_rev_list_arg(&args, "--not");
 +      for_each_ref(add_one_ref_to_rev_list_arg, &args);
 +      add_one_rev_list_arg(&args, "--");
 +      add_one_rev_list_arg(&args, NULL);
 +
 +      init_revisions(&revs, NULL);
 +      if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
 +              die(_("internal error: only -- alone should have been left"));
 +      if (prepare_revision_walk(&revs))
 +              die(_("internal error in revision walk"));
 +      if (!(commit->object.flags & UNINTERESTING))
 +              suggest_reattach(commit, &revs);
 +      else
 +              describe_detached_head(_("Previous HEAD position was"), commit);
 +
 +      clear_commit_marks(commit, -1);
 +      for_each_ref(clear_commit_marks_from_one_ref, NULL);
 +}
 +
  static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
  {
        int ret = 0;
                new->name = "HEAD";
                new->commit = old.commit;
                if (!new->commit)
 -                      die("You are on a branch yet to be born");
 +                      die(_("You are on a branch yet to be born"));
                parse_commit(new->commit);
        }
  
        if (ret)
                return ret;
  
 -      /*
 -       * If we were on a detached HEAD, but have now moved to
 -       * a new commit, we want to mention the old commit once more
 -       * to remind the user that it might be lost.
 -       */
        if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 -              describe_detached_head("Previous HEAD position was", old.commit);
 +              orphaned_commit_warning(old.commit);
  
        update_refs_for_switch(opts, &old, new);
  
@@@ -803,123 -674,11 +800,123 @@@ static const char *unique_tracking_name
        return NULL;
  }
  
 +static int parse_branchname_arg(int argc, const char **argv,
 +                              int dwim_new_local_branch_ok,
 +                              struct branch_info *new,
 +                              struct tree **source_tree,
 +                              unsigned char rev[20],
 +                              const char **new_branch)
 +{
 +      int argcount = 0;
 +      unsigned char branch_rev[20];
 +      const char *arg;
 +      int has_dash_dash;
 +
 +      /*
 +       * case 1: git checkout <ref> -- [<paths>]
 +       *
 +       *   <ref> must be a valid tree, everything after the '--' must be
 +       *   a path.
 +       *
 +       * case 2: git checkout -- [<paths>]
 +       *
 +       *   everything after the '--' must be paths.
 +       *
 +       * case 3: git checkout <something> [<paths>]
 +       *
 +       *   With no paths, if <something> is a commit, that is to
 +       *   switch to the branch or detach HEAD at it.  As a special case,
 +       *   if <something> is A...B (missing A or B means HEAD but you can
 +       *   omit at most one side), and if there is a unique merge base
 +       *   between A and B, A...B names that merge base.
 +       *
 +       *   With no paths, if <something> is _not_ a commit, no -t nor -b
 +       *   was given, and there is a tracking branch whose name is
 +       *   <something> in one and only one remote, then this is a short-hand
 +       *   to fork local <something> from that remote-tracking branch.
 +       *
 +       *   Otherwise <something> shall not be ambiguous.
 +       *   - If it's *only* a reference, treat it like case (1).
 +       *   - If it's only a path, treat it like case (2).
 +       *   - else: fail.
 +       *
 +       */
 +      if (!argc)
 +              return 0;
 +
 +      if (!strcmp(argv[0], "--"))     /* case (2) */
 +              return 1;
 +
 +      arg = argv[0];
 +      has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
 +
 +      if (!strcmp(arg, "-"))
 +              arg = "@{-1}";
 +
 +      if (get_sha1_mb(arg, rev)) {
 +              if (has_dash_dash)          /* case (1) */
 +                      die(_("invalid reference: %s"), arg);
 +              if (dwim_new_local_branch_ok &&
 +                  !check_filename(NULL, arg) &&
 +                  argc == 1) {
 +                      const char *remote = unique_tracking_name(arg);
 +                      if (!remote || get_sha1(remote, rev))
 +                              return argcount;
 +                      *new_branch = arg;
 +                      arg = remote;
 +                      /* DWIMmed to create local branch */
 +              } else {
 +                      return argcount;
 +              }
 +      }
 +
 +      /* we can't end up being in (2) anymore, eat the argument */
 +      argcount++;
 +      argv++;
 +      argc--;
 +
 +      new->name = arg;
 +      setup_branch_path(new);
 +
 +      if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
 +          resolve_ref(new->path, branch_rev, 1, NULL))
 +              hashcpy(rev, branch_rev);
 +      else
 +              new->path = NULL; /* not an existing branch */
 +
 +      new->commit = lookup_commit_reference_gently(rev, 1);
 +      if (!new->commit) {
 +              /* not a commit */
 +              *source_tree = parse_tree_indirect(rev);
 +      } else {
 +              parse_commit(new->commit);
 +              *source_tree = new->commit->tree;
 +      }
 +
 +      if (!*source_tree)                   /* case (1): want a tree */
 +              die(_("reference is not a tree: %s"), arg);
 +      if (!has_dash_dash) {/* case (3 -> 1) */
 +              /*
 +               * Do not complain the most common case
 +               *      git checkout branch
 +               * even if there happen to be a file called 'branch';
 +               * it would be extremely annoying.
 +               */
 +              if (argc)
 +                      verify_non_filename(NULL, arg);
 +      } else {
 +              argcount++;
 +              argv++;
 +              argc--;
 +      }
 +
 +      return argcount;
 +}
 +
  int cmd_checkout(int argc, const char **argv, const char *prefix)
  {
        struct checkout_opts opts;
        unsigned char rev[20];
 -      const char *arg;
        struct branch_info new;
        struct tree *source_tree = NULL;
        char *conflict_style = NULL;
                OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
                           "create/reset and checkout a branch"),
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
 +              OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"),
                OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
                OPT_END(),
        };
 -      int has_dash_dash;
  
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
  
        /* we can assume from now on new_branch = !new_branch_force */
        if (opts.new_branch && opts.new_branch_force)
 -              die("-B cannot be used with -b");
 +              die(_("-B cannot be used with -b"));
  
        /* copy -B over to -b, so that we can just check the latter */
        if (opts.new_branch_force)
                opts.new_branch = opts.new_branch_force;
  
        if (patch_mode && (opts.track > 0 || opts.new_branch
 -                         || opts.new_branch_log || opts.merge || opts.force))
 -              die ("--patch is incompatible with all other options");
 +                         || opts.new_branch_log || opts.merge || opts.force
 +                         || opts.force_detach))
 +              die (_("--patch is incompatible with all other options"));
 +
 +      if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
 +              die(_("--detach cannot be used with -b/-B/--orphan"));
 +      if (opts.force_detach && 0 < opts.track)
 +              die(_("--detach cannot be used with -t"));
  
        /* --track without -b should DWIM */
        if (0 < opts.track && !opts.new_branch) {
                const char *argv0 = argv[0];
                if (!argc || !strcmp(argv0, "--"))
 -                      die ("--track needs a branch name");
 +                      die (_("--track needs a branch name"));
                if (!prefixcmp(argv0, "refs/"))
                        argv0 += 5;
                if (!prefixcmp(argv0, "remotes/"))
                        argv0 += 8;
                argv0 = strchr(argv0, '/');
                if (!argv0 || !argv0[1])
 -                      die ("Missing branch name; try -b");
 +                      die (_("Missing branch name; try -b"));
                opts.new_branch = argv0 + 1;
        }
  
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
 -                      die("--orphan and -b|-B are mutually exclusive");
 +                      die(_("--orphan and -b|-B are mutually exclusive"));
                if (opts.track > 0)
 -                      die("--orphan cannot be used with -t");
 +                      die(_("--orphan cannot be used with -t"));
                opts.new_branch = opts.new_orphan_branch;
        }
  
        }
  
        if (opts.force && opts.merge)
 -              die("git checkout: -f and -m are incompatible");
 +              die(_("git checkout: -f and -m are incompatible"));
  
        /*
 -       * case 1: git checkout <ref> -- [<paths>]
 -       *
 -       *   <ref> must be a valid tree, everything after the '--' must be
 -       *   a path.
 +       * Extract branch name from command line arguments, so
 +       * all that is left is pathspecs.
         *
 -       * case 2: git checkout -- [<paths>]
 -       *
 -       *   everything after the '--' must be paths.
 -       *
 -       * case 3: git checkout <something> [<paths>]
 -       *
 -       *   With no paths, if <something> is a commit, that is to
 -       *   switch to the branch or detach HEAD at it.  As a special case,
 -       *   if <something> is A...B (missing A or B means HEAD but you can
 -       *   omit at most one side), and if there is a unique merge base
 -       *   between A and B, A...B names that merge base.
 +       * Handle
         *
 -       *   With no paths, if <something> is _not_ a commit, no -t nor -b
 -       *   was given, and there is a remote-tracking branch whose name is
 -       *   <something> in one and only one remote, then this is a short-hand
 -       *   to fork local <something> from that remote-tracking branch.
 -       *
 -       *   Otherwise <something> shall not be ambiguous.
 -       *   - If it's *only* a reference, treat it like case (1).
 -       *   - If it's only a path, treat it like case (2).
 -       *   - else: fail.
 +       *  1) git checkout <tree> -- [<paths>]
 +       *  2) git checkout -- [<paths>]
 +       *  3) git checkout <something> [<paths>]
         *
 +       * including "last branch" syntax and DWIM-ery for names of
 +       * remote branches, erroring out for invalid or ambiguous cases.
         */
        if (argc) {
 -              if (!strcmp(argv[0], "--")) {       /* case (2) */
 -                      argv++;
 -                      argc--;
 -                      goto no_reference;
 -              }
 -
 -              arg = argv[0];
 -              has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
 -
 -              if (!strcmp(arg, "-"))
 -                      arg = "@{-1}";
 -
 -              if (get_sha1_mb(arg, rev)) {
 -                      if (has_dash_dash)          /* case (1) */
 -                              die("invalid reference: %s", arg);
 -                      if (!patch_mode &&
 -                          dwim_new_local_branch &&
 -                          opts.track == BRANCH_TRACK_UNSPECIFIED &&
 -                          !opts.new_branch &&
 -                          !check_filename(NULL, arg) &&
 -                          argc == 1) {
 -                              const char *remote = unique_tracking_name(arg);
 -                              if (!remote || get_sha1(remote, rev))
 -                                      goto no_reference;
 -                              opts.new_branch = arg;
 -                              arg = remote;
 -                              /* DWIMmed to create local branch */
 -                      }
 -                      else
 -                              goto no_reference;
 -              }
 -
 -              /* we can't end up being in (2) anymore, eat the argument */
 -              argv++;
 -              argc--;
 -
 -              new.name = arg;
 -              if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
 -                      setup_branch_path(&new);
 -
 -                      if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
 -                          resolve_ref(new.path, rev, 1, NULL))
 -                              ;
 -                      else
 -                              new.path = NULL;
 -                      parse_commit(new.commit);
 -                      source_tree = new.commit->tree;
 -              } else
 -                      source_tree = parse_tree_indirect(rev);
 -
 -              if (!source_tree)                   /* case (1): want a tree */
 -                      die("reference is not a tree: %s", arg);
 -              if (!has_dash_dash) {/* case (3 -> 1) */
 -                      /*
 -                       * Do not complain the most common case
 -                       *      git checkout branch
 -                       * even if there happen to be a file called 'branch';
 -                       * it would be extremely annoying.
 -                       */
 -                      if (argc)
 -                              verify_non_filename(NULL, arg);
 -              }
 -              else {
 -                      argv++;
 -                      argc--;
 -              }
 +              int dwim_ok =
 +                      !patch_mode &&
 +                      dwim_new_local_branch &&
 +                      opts.track == BRANCH_TRACK_UNSPECIFIED &&
 +                      !opts.new_branch;
 +              int n = parse_branchname_arg(argc, argv, dwim_ok,
 +                              &new, &source_tree, rev, &opts.new_branch);
 +              argv += n;
 +              argc -= n;
        }
  
 -no_reference:
 -
        if (opts.track == BRANCH_TRACK_UNSPECIFIED)
                opts.track = git_branch_track;
  
                const char **pathspec = get_pathspec(prefix, argv);
  
                if (!pathspec)
 -                      die("invalid path specification");
 +                      die(_("invalid path specification"));
  
                if (patch_mode)
                        return interactive_checkout(new.name, pathspec, &opts);
                /* Checkout paths */
                if (opts.new_branch) {
                        if (argc == 1) {
 -                              die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
 +                              die(_("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?"), argv[0]);
                        } else {
 -                              die("git checkout: updating paths is incompatible with switching branches.");
 +                              die(_("git checkout: updating paths is incompatible with switching branches."));
                        }
                }
  
 +              if (opts.force_detach)
 +                      die(_("git checkout: --detach does not take a path argument"));
 +
                if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
 -                      die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
 +                      die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
  
                return checkout_paths(source_tree, pathspec, &opts);
        }
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
                if (strbuf_check_branch_ref(&buf, opts.new_branch))
 -                      die("git checkout: we do not like '%s' as a branch name.",
 +                      die(_("git checkout: we do not like '%s' as a branch name."),
                            opts.new_branch);
                if (!get_sha1(buf.buf, rev)) {
                        opts.branch_exists = 1;
                        if (!opts.new_branch_force)
 -                              die("git checkout: branch %s already exists",
 +                              die(_("git checkout: branch %s already exists"),
                                    opts.new_branch);
                }
                strbuf_release(&buf);
        }
  
        if (new.name && !new.commit) {
 -              die("Cannot switch branch to a non-commit.");
 +              die(_("Cannot switch branch to a non-commit."));
        }
        if (opts.writeout_stage)
 -              die("--ours/--theirs is incompatible with switching branches.");
 +              die(_("--ours/--theirs is incompatible with switching branches."));
  
        return switch_branches(&opts, &new);
  }
diff --combined builtin/log.c
index 224b16792042c5633a2f1820082b3ca18d81e5fd,0e46e5ae95a6ebb33d98cfde7505b50ed1a51c4d..5c2af590047d92554d87acc5d7113a2f1f730a96
  /* Set a default date-time format for git log ("log.date" config variable) */
  static const char *default_date_mode = NULL;
  
 +static int default_abbrev_commit;
  static int default_show_root = 1;
  static int decoration_style;
 +static int decoration_given;
  static const char *fmt_patch_subject_prefix = "PATCH";
  static const char *fmt_pretty;
  
 -static const char * const builtin_log_usage =
 +static const char * const builtin_log_usage[] = {
        "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
 -      "   or: git show [options] <object>...";
 +      "   or: git show [options] <object>...",
 +      NULL
 +};
  
  static int parse_decoration_style(const char *var, const char *value)
  {
        return -1;
  }
  
 -static void cmd_log_init(int argc, const char **argv, const char *prefix,
 -                       struct rev_info *rev, struct setup_revision_opt *opt)
 +static int decorate_callback(const struct option *opt, const char *arg, int unset)
  {
 -      int i;
 -      int decoration_given = 0;
 -      struct userformat_want w;
 +      if (unset)
 +              decoration_style = 0;
 +      else if (arg)
 +              decoration_style = parse_decoration_style("command line", arg);
 +      else
 +              decoration_style = DECORATE_SHORT_REFS;
 +
 +      if (decoration_style < 0)
 +              die("invalid --decorate option: %s", arg);
 +
 +      decoration_given = 1;
  
 +      return 0;
 +}
 +
 +static void cmd_log_init_defaults(struct rev_info *rev)
 +{
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
        if (fmt_pretty)
                get_commit_format(fmt_pretty, rev);
        rev->verbose_header = 1;
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
 +      rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
  
        if (default_date_mode)
                rev->date_mode = parse_date_format(default_date_mode);
 +}
 +
 +static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 +                       struct rev_info *rev, struct setup_revision_opt *opt)
 +{
 +      struct userformat_want w;
 +      int quiet = 0, source = 0;
 +
 +      const struct option builtin_log_options[] = {
 +              OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
 +              OPT_BOOLEAN(0, "source", &source, "show source"),
 +              { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
 +                PARSE_OPT_OPTARG, decorate_callback},
 +              OPT_END()
 +      };
 +
 +      argc = parse_options(argc, argv, prefix,
 +                           builtin_log_options, builtin_log_usage,
 +                           PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
 +                           PARSE_OPT_KEEP_DASHDASH);
  
 -      /*
 -       * Check for -h before setup_revisions(), or "git log -h" will
 -       * fail when run without a git directory.
 -       */
 -      if (argc == 2 && !strcmp(argv[1], "-h"))
 -              usage(builtin_log_usage);
        argc = setup_revisions(argc, argv, rev, opt);
 +      if (quiet)
 +              rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
 +
 +      /* Any arguments at this point are not recognized */
 +      if (argc > 1)
 +              die("unrecognized argument: %s", argv[1]);
  
        memset(&w, 0, sizeof(w));
        userformat_find_requirements(NULL, &w);
                rev->always_show_header = 0;
        if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
                rev->always_show_header = 0;
 -              if (rev->diffopt.nr_paths != 1)
 +              if (rev->diffopt.pathspec.nr != 1)
                        usage("git logs can only follow renames on one pathname at a time");
        }
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 -              if (!strcmp(arg, "--decorate")) {
 -                      decoration_style = DECORATE_SHORT_REFS;
 -                      decoration_given = 1;
 -              } else if (!prefixcmp(arg, "--decorate=")) {
 -                      const char *v = skip_prefix(arg, "--decorate=");
 -                      decoration_style = parse_decoration_style(arg, v);
 -                      if (decoration_style < 0)
 -                              die("invalid --decorate option: %s", arg);
 -                      decoration_given = 1;
 -              } else if (!strcmp(arg, "--no-decorate")) {
 +
 +      if (source)
 +              rev->show_source = 1;
 +
 +      if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
 +              /*
 +               * "log --pretty=raw" is special; ignore UI oriented
 +               * configuration variables such as decoration.
 +               */
 +              if (!decoration_given)
                        decoration_style = 0;
 -              } else if (!strcmp(arg, "--source")) {
 -                      rev->show_source = 1;
 -              } else if (!strcmp(arg, "-h")) {
 -                      usage(builtin_log_usage);
 -              } else
 -                      die("unrecognized argument: %s", arg);
 +              if (!rev->abbrev_commit_given)
 +                      rev->abbrev_commit = 0;
        }
  
 -      /*
 -       * defeat log.decorate configuration interacting with --pretty=raw
 -       * from the command line.
 -       */
 -      if (!decoration_given && rev->pretty_given
 -          && rev->commit_format == CMIT_FMT_RAW)
 -              decoration_style = 0;
 -
        if (decoration_style) {
                rev->show_decorations = 1;
                load_ref_decorations(decoration_style);
        setup_pager();
  }
  
 +static void cmd_log_init(int argc, const char **argv, const char *prefix,
 +                       struct rev_info *rev, struct setup_revision_opt *opt)
 +{
 +      cmd_log_init_defaults(rev);
 +      cmd_log_init_finish(argc, argv, prefix, rev, opt);
 +}
 +
  /*
   * This gives a rough estimate for how many commits we
   * will print out in the list.
@@@ -183,7 -153,7 +183,7 @@@ static void show_early_header(struct re
                if (rev->commit_format != CMIT_FMT_ONELINE)
                        putchar(rev->diffopt.line_termination);
        }
 -      printf("Final output: %d %s\n", nr, stage);
 +      printf(_("Final output: %d %s\n"), nr, stage);
  }
  
  static struct itimerval early_output_timer;
@@@ -277,14 -247,12 +277,14 @@@ static void finish_early_output(struct 
  static int cmd_log_walk(struct rev_info *rev)
  {
        struct commit *commit;
 +      int saved_nrl = 0;
 +      int saved_dcctc = 0;
  
        if (rev->early_output)
                setup_early_output(rev);
  
        if (prepare_revision_walk(rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
  
        if (rev->early_output)
                finish_early_output(rev);
         * retain that state information if replacing rev->diffopt in this loop
         */
        while ((commit = get_revision(rev)) != NULL) {
 -              log_tree_commit(rev, commit);
 +              if (!log_tree_commit(rev, commit) &&
 +                  rev->max_count >= 0)
 +                      /*
 +                       * We decremented max_count in get_revision,
 +                       * but we didn't actually show the commit.
 +                       */
 +                      rev->max_count++;
                if (!rev->reflog_info) {
                        /* we allow cycles in reflog ancestry */
                        free(commit->buffer);
                }
                free_commit_list(commit->parents);
                commit->parents = NULL;
 +              if (saved_nrl < rev->diffopt.needed_rename_limit)
 +                      saved_nrl = rev->diffopt.needed_rename_limit;
 +              if (rev->diffopt.degraded_cc_to_c)
 +                      saved_dcctc = 1;
        }
 +      rev->diffopt.degraded_cc_to_c = saved_dcctc;
 +      rev->diffopt.needed_rename_limit = saved_nrl;
 +
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
            DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
                return 02;
@@@ -330,10 -285,6 +330,10 @@@ static int git_log_config(const char *v
                return git_config_string(&fmt_pretty, var, value);
        if (!strcmp(var, "format.subjectprefix"))
                return git_config_string(&fmt_patch_subject_prefix, var, value);
 +      if (!strcmp(var, "log.abbrevcommit")) {
 +              default_abbrev_commit = git_config_bool(var, value);
 +              return 0;
 +      }
        if (!strcmp(var, "log.date"))
                return git_config_string(&default_date_mode, var, value);
        if (!strcmp(var, "log.decorate")) {
@@@ -376,9 -327,11 +376,11 @@@ int cmd_whatchanged(int argc, const cha
  static void show_tagger(char *buf, int len, struct rev_info *rev)
  {
        struct strbuf out = STRBUF_INIT;
+       struct pretty_print_context pp = {0};
  
-       pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
-               get_log_output_encoding());
+       pp.fmt = rev->commit_format;
+       pp.date_mode = rev->date_mode;
+       pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
        printf("%s", out.buf);
        strbuf_release(&out);
  }
@@@ -392,7 -345,7 +394,7 @@@ static int show_object(const unsigned c
        int offset = 0;
  
        if (!buf)
 -              return error("Could not read object %s", sha1_to_hex(sha1));
 +              return error(_("Could not read object %s"), sha1_to_hex(sha1));
  
        if (show_tag_object)
                while (offset < size && buf[offset] != '\n') {
@@@ -439,7 -392,6 +441,7 @@@ int cmd_show(int argc, const char **arg
        struct rev_info rev;
        struct object_array_entry *objects;
        struct setup_revision_opt opt;
 +      struct pathspec match_all;
        int i, count, ret = 0;
  
        git_config(git_log_config, NULL);
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
  
 +      init_pathspec(&match_all, NULL);
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.always_show_header = 1;
                                break;
                        o = parse_object(t->tagged->sha1);
                        if (!o)
 -                              ret = error("Could not read object %s",
 +                              ret = error(_("Could not read object %s"),
                                            sha1_to_hex(t->tagged->sha1));
                        objects[i].item = o;
                        i--;
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
 -                      read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
 +                      read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
                                        show_tree_object, NULL);
                        rev.shown_one = 1;
                        break;
                        ret = cmd_log_walk(&rev);
                        break;
                default:
 -                      ret = error("Unknown type: %d", o->type);
 +                      ret = error(_("Unknown type: %d"), o->type);
                }
        }
        free(objects);
@@@ -527,15 -478,20 +529,15 @@@ int cmd_log_reflog(int argc, const cha
  
        init_revisions(&rev, prefix);
        init_reflog_walk(&rev.reflog_info);
 -      rev.abbrev_commit = 1;
        rev.verbose_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
 -      cmd_log_init(argc, argv, prefix, &rev, &opt);
 -
 -      /*
 -       * This means that we override whatever commit format the user gave
 -       * on the cmd line.  Sad, but cmd_log_init() currently doesn't
 -       * allow us to set a different default.
 -       */
 +      cmd_log_init_defaults(&rev);
 +      rev.abbrev_commit = 1;
        rev.commit_format = CMIT_FMT_ONELINE;
        rev.use_terminator = 1;
        rev.always_show_header = 1;
 +      cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
  
        return cmd_log_walk(&rev);
  }
@@@ -600,7 -556,7 +602,7 @@@ static int git_format_config(const cha
  {
        if (!strcmp(var, "format.headers")) {
                if (!value)
 -                      die("format.headers without value");
 +                      die(_("format.headers without value"));
                add_header(value);
                return 0;
        }
@@@ -663,7 -619,7 +665,7 @@@ static FILE *realstdout = NULL
  static const char *output_directory = NULL;
  static int outdir_offset;
  
 -static int reopen_stdout(struct commit *commit, struct rev_info *rev)
 +static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
        int suffix_len = strlen(fmt_patch_suffix) + 1;
                strbuf_addstr(&filename, output_directory);
                if (filename.len >=
                    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
 -                      return error("name of output directory is too long");
 +                      return error(_("name of output directory is too long"));
                if (filename.buf[filename.len - 1] != '/')
                        strbuf_addch(&filename, '/');
        }
  
        get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
  
 -      if (!DIFF_OPT_TST(&rev->diffopt, QUICK))
 +      if (!quiet)
                fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
  
        if (freopen(filename.buf, "w", stdout) == NULL)
 -              return error("Cannot open patch file %s", filename.buf);
 +              return error(_("Cannot open patch file %s"), filename.buf);
  
        strbuf_release(&filename);
        return 0;
@@@ -697,7 -653,7 +699,7 @@@ static void get_patch_ids(struct rev_in
        unsigned flags1, flags2;
  
        if (rev->pending.nr != 2)
 -              die("Need exactly one range.");
 +              die(_("Need exactly one range."));
  
        o1 = rev->pending.objects[0].item;
        flags1 = o1->flags;
        flags2 = o2->flags;
  
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
 -              die("Not a range.");
 +              die(_("Not a range."));
  
        init_patch_ids(ids);
  
        add_pending_object(&check_rev, o1, "o1");
        add_pending_object(&check_rev, o2, "o2");
        if (prepare_revision_walk(&check_rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
  
        while ((commit = get_revision(&check_rev)) != NULL) {
                /* ignore merges */
@@@ -742,7 -698,7 +744,7 @@@ static void gen_message_id(struct rev_i
        const char *email_end = strrchr(committer, '>');
        struct strbuf buf = STRBUF_INIT;
        if (!email_start || !email_end || email_start > email_end - 1)
 -              die("Could not extract email from committer identity.");
 +              die(_("Could not extract email from committer identity."));
        strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
                    (unsigned long) time(NULL),
                    (int)(email_end - email_start - 1), email_start + 1);
@@@ -758,14 -714,11 +760,12 @@@ static void print_signature(void
  static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int numbered, int numbered_files,
                              struct commit *origin,
 -                            int nr, struct commit **list, struct commit *head)
 +                            int nr, struct commit **list, struct commit *head,
 +                            int quiet)
  {
        const char *committer;
-       const char *subject_start = NULL;
        const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
        const char *msg;
-       const char *extra_headers = rev->extra_headers;
        struct shortlog log;
        struct strbuf sb = STRBUF_INIT;
        int i;
        struct diff_options opts;
        int need_8bit_cte = 0;
        struct commit *commit = NULL;
+       struct pretty_print_context pp = {0};
  
        if (rev->commit_format != CMIT_FMT_EMAIL)
 -              die("Cover letter needs email format");
 +              die(_("Cover letter needs email format"));
  
        committer = git_committer_info(0);
  
                        sha1_to_hex(head->object.sha1), committer, committer);
        }
  
 -      if (!use_stdout && reopen_stdout(commit, rev))
 +      if (!use_stdout && reopen_stdout(commit, rev, quiet))
                return;
  
        if (commit) {
                free(commit);
        }
  
-       log_write_email_headers(rev, head, &subject_start, &extra_headers,
+       log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
  
        for (i = 0; !need_8bit_cte && i < nr; i++)
                        need_8bit_cte = 1;
  
        msg = body;
-       pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
-                    encoding);
-       pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers,
-                     encoding, need_8bit_cte);
-       pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0);
+       pp.fmt = CMIT_FMT_EMAIL;
+       pp.date_mode = DATE_RFC2822;
+       pp_user_info(&pp, NULL, &sb, committer, encoding);
+       pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
+       pp_remainder(&pp, &msg, &sb, 0);
        printf("%s\n", sb.buf);
  
        strbuf_release(&sb);
@@@ -868,7 -822,7 +869,7 @@@ static const char *clean_message_id(con
                m++;
        }
        if (!z)
 -              die("insane in-reply-to: %s", msg_id);
 +              die(_("insane in-reply-to: %s"), msg_id);
        if (++z == m)
                return a;
        return xmemdupz(a, z - a);
@@@ -941,7 -895,7 +942,7 @@@ static int output_directory_callback(co
  {
        const char **dir = (const char **)opt->value;
        if (*dir)
 -              die("Two output directories?");
 +              die(_("Two output directories?"));
        *dir = arg;
        return 0;
  }
@@@ -1036,7 -990,6 +1037,7 @@@ int cmd_format_patch(int argc, const ch
        char *add_signoff = NULL;
        struct strbuf buf = STRBUF_INIT;
        int use_patch_format = 0;
 +      int quiet = 0;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            "use [PATCH n/m] even with a single patch",
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, "signature",
                            "add a signature"),
 +              OPT_BOOLEAN(0, "quiet", &quiet,
 +                          "don't print the patch filenames"),
                OPT_END()
        };
  
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
        rev.diff = 1;
 -      rev.no_merges = 1;
 +      rev.max_parents = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
                committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
                endpos = strchr(committer, '>');
                if (!endpos)
 -                      die("bogus committer info %s", committer);
 +                      die(_("bogus committer info %s"), committer);
                add_signoff = xmemdupz(committer, endpos - committer + 1);
        }
  
                numbered = 0;
  
        if (numbered && keep_subject)
 -              die ("-n and -k are mutually exclusive.");
 +              die (_("-n and -k are mutually exclusive."));
        if (keep_subject && subject_prefix)
 -              die ("--subject-prefix and -k are mutually exclusive.");
 +              die (_("--subject-prefix and -k are mutually exclusive."));
+       rev.preserve_subject = keep_subject;
  
        argc = setup_revisions(argc, argv, &rev, &s_r_opt);
        if (argc > 1)
 -              die ("unrecognized argument: %s", argv[1]);
 +              die (_("unrecognized argument: %s"), argv[1]);
  
        if (rev.diffopt.output_format & DIFF_FORMAT_NAME)
 -              die("--name-only does not make sense");
 +              die(_("--name-only does not make sense"));
        if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS)
 -              die("--name-status does not make sense");
 +              die(_("--name-status does not make sense"));
        if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF)
 -              die("--check does not make sense");
 +              die(_("--check does not make sense"));
  
        if (!use_patch_format &&
                (!rev.diffopt.output_format ||
  
        if (output_directory) {
                if (use_stdout)
 -                      die("standard output, or directory, which one?");
 +                      die(_("standard output, or directory, which one?"));
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
 -                      die_errno("Could not create directory '%s'",
 +                      die_errno(_("Could not create directory '%s'"),
                                  output_directory);
        }
  
                realstdout = xfdopen(xdup(1), "w");
  
        if (prepare_revision_walk(&rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
        rev.boundary = 1;
        while ((commit = get_revision(&rev)) != NULL) {
                if (commit->object.flags & BOUNDARY) {
                if (thread)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout, numbered, numbered_files,
 -                                origin, nr, list, head);
 +                                origin, nr, list, head, quiet);
                total++;
                start_number--;
        }
                }
  
                if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
 -                                               &rev))
 -                      die("Failed to create output files");
 +                                               &rev, quiet))
 +                      die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
                commit->buffer = NULL;
@@@ -1402,23 -1354,6 +1404,22 @@@ static const char * const cherry_usage[
        NULL
  };
  
-               struct pretty_print_context ctx = {0};
-               pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx);
 +static void print_commit(char sign, struct commit *commit, int verbose,
 +                       int abbrev)
 +{
 +      if (!verbose) {
 +              printf("%c %s\n", sign,
 +                     find_unique_abbrev(commit->object.sha1, abbrev));
 +      } else {
 +              struct strbuf buf = STRBUF_INIT;
++              pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
 +              printf("%c %s %s\n", sign,
 +                     find_unique_abbrev(commit->object.sha1, abbrev),
 +                     buf.buf);
 +              strbuf_release(&buf);
 +      }
 +}
 +
  int cmd_cherry(int argc, const char **argv, const char *prefix)
  {
        struct rev_info revs;
                if (!current_branch || !current_branch->merge
                                        || !current_branch->merge[0]
                                        || !current_branch->merge[0]->dst) {
 -                      fprintf(stderr, "Could not find a tracked"
 +                      fprintf(stderr, _("Could not find a tracked"
                                        " remote branch, please"
 -                                      " specify <upstream> manually.\n");
 +                                      " specify <upstream> manually.\n"));
                        usage_with_options(cherry_usage, options);
                }
  
        DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
  
        if (add_pending_commit(head, &revs, 0))
 -              die("Unknown commit %s", head);
 +              die(_("Unknown commit %s"), head);
        if (add_pending_commit(upstream, &revs, UNINTERESTING))
 -              die("Unknown commit %s", upstream);
 +              die(_("Unknown commit %s"), upstream);
  
        /* Don't say anything if head and upstream are the same. */
        if (revs.pending.nr == 2) {
        get_patch_ids(&revs, &ids, prefix);
  
        if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
 -              die("Unknown commit %s", limit);
 +              die(_("Unknown commit %s"), limit);
  
        /* reverse the list of commits */
        if (prepare_revision_walk(&revs))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
        while ((commit = get_revision(&revs)) != NULL) {
                /* ignore merges */
                if (commit->parents && commit->parents->next)
                commit = list->item;
                if (has_commit_patch_id(commit, &ids))
                        sign = '-';
 -
 -              if (verbose) {
 -                      struct strbuf buf = STRBUF_INIT;
 -                      pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
 -                      printf("%c %s %s\n", sign,
 -                             find_unique_abbrev(commit->object.sha1, abbrev),
 -                             buf.buf);
 -                      strbuf_release(&buf);
 -              }
 -              else {
 -                      printf("%c %s\n", sign,
 -                             find_unique_abbrev(commit->object.sha1, abbrev));
 -              }
 -
 +              print_commit(sign, commit, verbose, abbrev);
                list = list->next;
        }
  
diff --combined builtin/merge.c
index 5a2a1eb797c88990337f0b9cbb5dcabc73222a22,c902e81cf189c687652bb7752080438f51e15380..325891edb610945d01899b102993202af279bf3f
@@@ -25,7 -25,6 +25,7 @@@
  #include "help.h"
  #include "merge-recursive.h"
  #include "resolve-undo.h"
 +#include "remote.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -38,9 -37,8 +38,9 @@@ struct strategy 
  };
  
  static const char * const builtin_merge_usage[] = {
 -      "git merge [options] <remote>...",
 -      "git merge [options] <msg> HEAD <remote>",
 +      "git merge [options] [<commit>...]",
 +      "git merge [options] <msg> HEAD <commit>",
 +      "git merge --abort",
        NULL
  };
  
@@@ -56,13 -54,10 +56,13 @@@ static size_t use_strategies_nr, use_st
  static const char **xopts;
  static size_t xopts_nr, xopts_alloc;
  static const char *branch;
 +static char *branch_mergeoptions;
  static int option_renormalize;
  static int verbosity;
  static int allow_rerere_auto;
  static int abort_current_merge;
 +static int show_progress = -1;
 +static int default_to_upstream;
  
  static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@@ -85,7 -80,7 +85,7 @@@ static int option_parse_message(const s
                strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg);
                have_message = 1;
        } else
 -              return error("switch `m' requires a value");
 +              return error(_("switch `m' requires a value"));
        return 0;
  }
  
@@@ -122,13 -117,13 +122,13 @@@ static struct strategy *get_strategy(co
                exclude_cmds(&main_cmds, &not_strategies);
        }
        if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
 -              fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
 -              fprintf(stderr, "Available strategies are:");
 +              fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name);
 +              fprintf(stderr, _("Available strategies are:"));
                for (i = 0; i < main_cmds.cnt; i++)
                        fprintf(stderr, " %s", main_cmds.names[i]->name);
                fprintf(stderr, ".\n");
                if (other_cmds.cnt) {
 -                      fprintf(stderr, "Available custom strategies are:");
 +                      fprintf(stderr, _("Available custom strategies are:"));
                        for (i = 0; i < other_cmds.cnt; i++)
                                fprintf(stderr, " %s", other_cmds.names[i]->name);
                        fprintf(stderr, ".\n");
@@@ -200,12 -195,11 +200,12 @@@ static struct option builtin_merge_opti
        OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
                "option for selected merge strategy", option_parse_x),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
 -              "message to be used for the merge commit (if any)",
 +              "merge commit message (for a non-fast-forward merge)",
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
        OPT_BOOLEAN(0, "abort", &abort_current_merge,
                "abort the current in-progress merge"),
 +      OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
        OPT_END()
  };
  
@@@ -230,17 -224,17 +230,17 @@@ static void save_state(void
        cp.git_cmd = 1;
  
        if (start_command(&cp))
 -              die("could not run stash.");
 +              die(_("could not run stash."));
        len = strbuf_read(&buffer, cp.out, 1024);
        close(cp.out);
  
        if (finish_command(&cp) || len < 0)
 -              die("stash failed");
 +              die(_("stash failed"));
        else if (!len)
                return;
        strbuf_setlen(&buffer, buffer.len-1);
        if (get_sha1(buffer.buf, stash))
 -              die("not a valid object: %s", buffer.buf);
 +              die(_("not a valid object: %s"), buffer.buf);
  }
  
  static void read_empty(unsigned const char *sha1, int verbose)
        args[i] = NULL;
  
        if (run_command_v_opt(args, RUN_GIT_CMD))
 -              die("read-tree failed");
 +              die(_("read-tree failed"));
  }
  
  static void reset_hard(unsigned const char *sha1, int verbose)
        args[i] = NULL;
  
        if (run_command_v_opt(args, RUN_GIT_CMD))
 -              die("read-tree failed");
 +              die(_("read-tree failed"));
  }
  
  static void restore_state(void)
  static void finish_up_to_date(const char *msg)
  {
        if (verbosity >= 0)
 -              printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
 +              printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg);
        drop_save();
  }
  
@@@ -317,10 -311,10 +317,10 @@@ static void squash_message(void
        int fd;
        struct pretty_print_context ctx = {0};
  
 -      printf("Squash commit -- not updating HEAD\n");
 +      printf(_("Squash commit -- not updating HEAD\n"));
        fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
 -              die_errno("Could not write to '%s'", git_path("SQUASH_MSG"));
 +              die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG"));
  
        init_revisions(&rev, NULL);
        rev.ignore_merges = 1;
  
        setup_revisions(0, NULL, &rev, NULL);
        if (prepare_revision_walk(&rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
  
        ctx.abbrev = rev.abbrev;
        ctx.date_mode = rev.date_mode;
+       ctx.fmt = rev.commit_format;
  
        strbuf_addstr(&out, "Squashed commit of the following:\n");
        while ((commit = get_revision(&rev)) != NULL) {
                strbuf_addch(&out, '\n');
                strbuf_addf(&out, "commit %s\n",
                        sha1_to_hex(commit->object.sha1));
-               pretty_print_commit(rev.commit_format, commit, &out, &ctx);
+               pretty_print_commit(&ctx, commit, &out);
        }
        if (write(fd, out.buf, out.len) < 0)
 -              die_errno("Writing SQUASH_MSG");
 +              die_errno(_("Writing SQUASH_MSG"));
        if (close(fd))
 -              die_errno("Finishing SQUASH_MSG");
 +              die_errno(_("Finishing SQUASH_MSG"));
        strbuf_release(&out);
  }
  
@@@ -370,7 -365,7 +371,7 @@@ static void finish(const unsigned char 
                squash_message();
        } else {
                if (verbosity >= 0 && !merge_msg.len)
 -                      printf("No merge message -- not updating HEAD\n");
 +                      printf(_("No merge message -- not updating HEAD\n"));
                else {
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
                        update_ref(reflog_message.buf, "HEAD",
                if (diff_use_color_default > 0)
                        DIFF_OPT_SET(&opts, COLOR_DIFF);
                if (diff_setup_done(&opts) < 0)
 -                      die("diff_setup_done failed");
 +                      die(_("diff_setup_done failed"));
                diff_tree_sha1(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
@@@ -421,7 -416,7 +422,7 @@@ static void merge_name(const char *remo
        memset(branch_head, 0, sizeof(branch_head));
        remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
        if (!remote_head)
 -              die("'%s' does not point to a commit", remote);
 +              die(_("'%s' does not point to a commit"), remote);
  
        if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
                if (!prefixcmp(found_ref, "refs/heads/")) {
  
                fp = fopen(git_path("FETCH_HEAD"), "r");
                if (!fp)
 -                      die_errno("could not open '%s' for reading",
 +                      die_errno(_("could not open '%s' for reading"),
                                  git_path("FETCH_HEAD"));
                strbuf_getline(&line, fp, '\n');
                fclose(fp);
@@@ -504,34 -499,26 +505,34 @@@ cleanup
        strbuf_release(&bname);
  }
  
 +static void parse_branch_merge_options(char *bmo)
 +{
 +      const char **argv;
 +      int argc;
 +
 +      if (!bmo)
 +              return;
 +      argc = split_cmdline(bmo, &argv);
 +      if (argc < 0)
 +              die(_("Bad branch.%s.mergeoptions string: %s"), branch,
 +                  split_cmdline_strerror(argc));
 +      argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
 +      memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
 +      argc++;
 +      argv[0] = "branch.*.mergeoptions";
 +      parse_options(argc, argv, NULL, builtin_merge_options,
 +                    builtin_merge_usage, 0);
 +      free(argv);
 +}
 +
  static int git_merge_config(const char *k, const char *v, void *cb)
  {
        if (branch && !prefixcmp(k, "branch.") &&
                !prefixcmp(k + 7, branch) &&
                !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
 -              const char **argv;
 -              int argc;
 -              char *buf;
 -
 -              buf = xstrdup(v);
 -              argc = split_cmdline(buf, &argv);
 -              if (argc < 0)
 -                      die("Bad branch.%s.mergeoptions string: %s", branch,
 -                          split_cmdline_strerror(argc));
 -              argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
 -              memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
 -              argc++;
 -              parse_options(argc, argv, NULL, builtin_merge_options,
 -                            builtin_merge_usage, 0);
 -              free(buf);
 +              free(branch_mergeoptions);
 +              branch_mergeoptions = xstrdup(v);
 +              return 0;
        }
  
        if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
                int is_bool;
                shortlog_len = git_config_bool_or_int(k, v, &is_bool);
                if (!is_bool && shortlog_len < 0)
 -                      return error("%s: negative length %s", k, v);
 +                      return error(_("%s: negative length %s"), k, v);
                if (is_bool && shortlog_len)
                        shortlog_len = DEFAULT_MERGE_LOG_LEN;
                return 0;
 +      } else if (!strcmp(k, "merge.ff")) {
 +              int boolval = git_config_maybe_bool(k, v);
 +              if (0 <= boolval) {
 +                      allow_fast_forward = boolval;
 +              } else if (v && !strcmp(v, "only")) {
 +                      allow_fast_forward = 1;
 +                      fast_forward_only = 1;
 +              } /* do not barf on values from future versions of git */
 +              return 0;
 +      } else if (!strcmp(k, "merge.defaulttoupstream")) {
 +              default_to_upstream = git_config_bool(k, v);
 +              return 0;
        }
        return git_diff_ui_config(k, v, cb);
  }
@@@ -605,19 -580,10 +606,19 @@@ static int read_tree_trivial(unsigned c
  static void write_tree_trivial(unsigned char *sha1)
  {
        if (write_cache_as_tree(sha1, 0, NULL))
 -              die("git write-tree failed to write a tree");
 +              die(_("git write-tree failed to write a tree"));
 +}
 +
 +static const char *merge_argument(struct commit *commit)
 +{
 +      if (commit)
 +              return sha1_to_hex(commit->object.sha1);
 +      else
 +              return EMPTY_TREE_SHA1_HEX;
  }
  
 -int try_merge_command(const char *strategy, struct commit_list *common,
 +int try_merge_command(const char *strategy, size_t xopts_nr,
 +                    const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
  {
        const char **args;
                args[i++] = s;
        }
        for (j = common; j; j = j->next)
 -              args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
 +              args[i++] = xstrdup(merge_argument(j->item));
        args[i++] = "--";
        args[i++] = head_arg;
        for (j = remotes; j; j = j->next)
 -              args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
 +              args[i++] = xstrdup(merge_argument(j->item));
        args[i] = NULL;
        ret = run_command_v_opt(args, RUN_GIT_CMD);
        strbuf_release(&buf);
        free(args);
        discard_cache();
        if (read_cache() < 0)
 -              die("failed to read the cache");
 +              die(_("failed to read the cache"));
        resolve_undo_clear();
  
        return ret;
@@@ -672,7 -638,7 +673,7 @@@ static int try_merge_strategy(const cha
        if (active_cache_changed &&
                        (write_cache(index_fd, active_cache, active_nr) ||
                         commit_locked_index(lock)))
 -              return error("Unable to write index.");
 +              return error(_("Unable to write index."));
        rollback_lock_file(lock);
  
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                struct commit_list *j;
  
                if (remoteheads->next) {
 -                      error("Not handling anything other than two heads merge.");
 +                      error(_("Not handling anything other than two heads merge."));
                        return 2;
                }
  
                        o.subtree_shift = "";
  
                o.renormalize = option_renormalize;
 +              o.show_rename_progress =
 +                      show_progress == -1 ? isatty(2) : show_progress;
  
                for (x = 0; x < xopts_nr; x++)
                        if (parse_merge_opt(&o, xopts[x]))
 -                              die("Unknown option for merge-recursive: -X%s", xopts[x]);
 +                              die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
  
                o.branch1 = head_arg;
                o.branch2 = remoteheads->item->util;
                if (active_cache_changed &&
                                (write_cache(index_fd, active_cache, active_nr) ||
                                 commit_locked_index(lock)))
 -                      die ("unable to write %s", get_index_file());
 +                      die (_("unable to write %s"), get_index_file());
                rollback_lock_file(lock);
                return clean ? 0 : 1;
        } else {
 -              return try_merge_command(strategy, common, head_arg, remoteheads);
 +              return try_merge_command(strategy, xopts_nr, xopts,
 +                                              common, head_arg, remoteheads);
        }
  }
  
@@@ -785,7 -748,7 +786,7 @@@ int checkout_fast_forward(const unsigne
                return -1;
        if (write_cache(fd, active_cache, active_nr) ||
                commit_locked_index(lock_file))
 -              die("unable to write new index file");
 +              die(_("unable to write new index file"));
        return 0;
  }
  
@@@ -833,44 -796,17 +834,44 @@@ static void add_strategies(const char *
  
  }
  
 +static void write_merge_msg(void)
 +{
 +      int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
 +      if (fd < 0)
 +              die_errno(_("Could not open '%s' for writing"),
 +                        git_path("MERGE_MSG"));
 +      if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len)
 +              die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG"));
 +      close(fd);
 +}
 +
 +static void read_merge_msg(void)
 +{
 +      strbuf_reset(&merge_msg);
 +      if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0)
 +              die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG"));
 +}
 +
 +static void run_prepare_commit_msg(void)
 +{
 +      write_merge_msg();
 +      run_hook(get_index_file(), "prepare-commit-msg",
 +               git_path("MERGE_MSG"), "merge", NULL, NULL);
 +      read_merge_msg();
 +}
 +
  static int merge_trivial(void)
  {
        unsigned char result_tree[20], result_commit[20];
        struct commit_list *parent = xmalloc(sizeof(*parent));
  
        write_tree_trivial(result_tree);
 -      printf("Wonderful.\n");
 +      printf(_("Wonderful.\n"));
        parent->item = lookup_commit(head);
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
 +      run_prepare_commit_msg();
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
        finish(result_commit, "In-index merge");
        drop_save();
@@@ -900,7 -836,6 +901,7 @@@ static int finish_automerge(struct comm
        }
        free_commit_list(remoteheads);
        strbuf_addch(&merge_msg, '\n');
 +      run_prepare_commit_msg();
        commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
        strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
        finish(result_commit, buf.buf);
@@@ -916,7 -851,7 +917,7 @@@ static int suggest_conflicts(int renorm
  
        fp = fopen(git_path("MERGE_MSG"), "a");
        if (!fp)
 -              die_errno("Could not open '%s' for writing",
 +              die_errno(_("Could not open '%s' for writing"),
                          git_path("MERGE_MSG"));
        fprintf(fp, "\nConflicts:\n");
        for (pos = 0; pos < active_nr; pos++) {
        }
        fclose(fp);
        rerere(allow_rerere_auto);
 -      printf("Automatic merge failed; "
 -                      "fix conflicts and then commit the result.\n");
 +      printf(_("Automatic merge failed; "
 +                      "fix conflicts and then commit the result.\n"));
        return 1;
  }
  
@@@ -947,7 -882,7 +948,7 @@@ static struct commit *is_old_style_invo
                        return NULL;
                second_token = lookup_commit_reference_gently(second_sha1, 0);
                if (!second_token)
 -                      die("'%s' is not a commit", argv[1]);
 +                      die(_("'%s' is not a commit"), argv[1]);
                if (hashcmp(second_token->object.sha1, head))
                        return NULL;
        }
@@@ -977,35 -912,6 +978,35 @@@ static int evaluate_result(void
        return cnt;
  }
  
 +/*
 + * Pretend as if the user told us to merge with the tracking
 + * branch we have for the upstream of the current branch
 + */
 +static int setup_with_upstream(const char ***argv)
 +{
 +      struct branch *branch = branch_get(NULL);
 +      int i;
 +      const char **args;
 +
 +      if (!branch)
 +              die(_("No current branch."));
 +      if (!branch->remote)
 +              die(_("No remote for the current branch."));
 +      if (!branch->merge_nr)
 +              die(_("No default upstream defined for the current branch."));
 +
 +      args = xcalloc(branch->merge_nr + 1, sizeof(char *));
 +      for (i = 0; i < branch->merge_nr; i++) {
 +              if (!branch->merge[i]->dst)
 +                      die(_("No remote tracking branch for %s from %s"),
 +                          branch->merge[i]->src, branch->remote_name);
 +              args[i] = branch->merge[i]->dst;
 +      }
 +      args[i] = NULL;
 +      *argv = args;
 +      return i;
 +}
 +
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
        unsigned char result_tree[20];
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
  
 +      if (branch_mergeoptions)
 +              parse_branch_merge_options(branch_mergeoptions);
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
                        builtin_merge_usage, 0);
  
 +      if (verbosity < 0 && show_progress == -1)
 +              show_progress = 0;
 +
        if (abort_current_merge) {
                int nargc = 2;
                const char *nargv[] = {"reset", "--merge", NULL};
  
                if (!file_exists(git_path("MERGE_HEAD")))
 -                      die("There is no merge to abort (MERGE_HEAD missing).");
 +                      die(_("There is no merge to abort (MERGE_HEAD missing)."));
  
                /* Invoke 'git reset --merge' */
                return cmd_reset(nargc, nargv, prefix);
                 * add/rm <file>', just 'git commit'.
                 */
                if (advice_resolve_conflict)
 -                      die("You have not concluded your merge (MERGE_HEAD exists).\n"
 -                          "Please, commit your changes before you can merge.");
 +                      die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
 +                                "Please, commit your changes before you can merge."));
 +              else
 +                      die(_("You have not concluded your merge (MERGE_HEAD exists)."));
 +      }
 +      if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
 +              if (advice_resolve_conflict)
 +                      die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 +                          "Please, commit your changes before you can merge."));
                else
 -                      die("You have not concluded your merge (MERGE_HEAD exists).");
 +                      die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
        }
        resolve_undo_clear();
  
  
        if (squash) {
                if (!allow_fast_forward)
 -                      die("You cannot combine --squash with --no-ff.");
 +                      die(_("You cannot combine --squash with --no-ff."));
                option_commit = 0;
        }
  
        if (!allow_fast_forward && fast_forward_only)
 -              die("You cannot combine --no-ff with --ff-only.");
 +              die(_("You cannot combine --no-ff with --ff-only."));
  
 +      if (!abort_current_merge) {
 +              if (!argc && default_to_upstream)
 +                      argc = setup_with_upstream(&argv);
 +              else if (argc == 1 && !strcmp(argv[0], "-"))
 +                      argv[0] = "@{-1}";
 +      }
        if (!argc)
                usage_with_options(builtin_merge_usage,
                        builtin_merge_options);
                 * We do the same for "git pull".
                 */
                if (argc != 1)
 -                      die("Can merge only exactly one commit into "
 -                              "empty head");
 +                      die(_("Can merge only exactly one commit into "
 +                              "empty head"));
                if (squash)
 -                      die("Squash commit into empty head not supported yet");
 +                      die(_("Squash commit into empty head not supported yet"));
                if (!allow_fast_forward)
 -                      die("Non-fast-forward commit does not make sense into "
 -                          "an empty head");
 +                      die(_("Non-fast-forward commit does not make sense into "
 +                          "an empty head"));
                remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
                if (!remote_head)
 -                      die("%s - not something we can merge", argv[0]);
 +                      die(_("%s - not something we can merge"), argv[0]);
 +              read_empty(remote_head->sha1, 0);
                update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
                                DIE_ON_ERR);
 -              read_empty(remote_head->sha1, 0);
                return 0;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
  
                o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
                if (!o)
 -                      die("%s - not something we can merge", argv[i]);
 +                      die(_("%s - not something we can merge"), argv[i]);
                commit = lookup_commit(o->sha1);
                commit->util = (void *)argv[i];
                remotes = &commit_list_insert(commit, remotes)->next;
                strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
  
                if (verbosity >= 0)
 -                      printf("Updating %s..%s\n",
 +                      printf(_("Updating %s..%s\n"),
                                hex,
                                find_unique_abbrev(remoteheads->item->object.sha1,
                                DEFAULT_ABBREV));
                if (allow_trivial && !fast_forward_only) {
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_ERROR_ON_NO_NAME);
 -                      printf("Trying really trivial in-index merge...\n");
 +                      printf(_("Trying really trivial in-index merge...\n"));
                        if (!read_tree_trivial(common->item->object.sha1,
                                        head, remoteheads->item->object.sha1))
                                return merge_trivial();
 -                      printf("Nope.\n");
 +                      printf(_("Nope.\n"));
                }
        } else {
                /*
        }
  
        if (fast_forward_only)
 -              die("Not possible to fast-forward, aborting.");
 +              die(_("Not possible to fast-forward, aborting."));
  
        /* We are going to make a new commit. */
        git_committer_info(IDENT_ERROR_ON_NO_NAME);
        for (i = 0; i < use_strategies_nr; i++) {
                int ret;
                if (i) {
 -                      printf("Rewinding the tree to pristine...\n");
 +                      printf(_("Rewinding the tree to pristine...\n"));
                        restore_state();
                }
                if (use_strategies_nr != 1)
 -                      printf("Trying merge strategy %s...\n",
 +                      printf(_("Trying merge strategy %s...\n"),
                                use_strategies[i]->name);
                /*
                 * Remember which strategy left the state in the working
                restore_state();
                if (use_strategies_nr > 1)
                        fprintf(stderr,
 -                              "No merge strategy handled the merge.\n");
 +                              _("No merge strategy handled the merge.\n"));
                else
 -                      fprintf(stderr, "Merge with strategy %s failed.\n",
 +                      fprintf(stderr, _("Merge with strategy %s failed.\n"),
                                use_strategies[0]->name);
                return 2;
        } else if (best_strategy == wt_strategy)
                ; /* We already have its result in the working tree. */
        else {
 -              printf("Rewinding the tree to pristine...\n");
 +              printf(_("Rewinding the tree to pristine...\n"));
                restore_state();
 -              printf("Using the %s to prepare resolving by hand.\n",
 +              printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
                try_merge_strategy(best_strategy, common, head_arg);
        }
                                sha1_to_hex(j->item->object.sha1));
                fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
                if (fd < 0)
 -                      die_errno("Could not open '%s' for writing",
 +                      die_errno(_("Could not open '%s' for writing"),
                                  git_path("MERGE_HEAD"));
                if (write_in_full(fd, buf.buf, buf.len) != buf.len)
 -                      die_errno("Could not write to '%s'", git_path("MERGE_HEAD"));
 +                      die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
                close(fd);
                strbuf_addch(&merge_msg, '\n');
 -              fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
 -              if (fd < 0)
 -                      die_errno("Could not open '%s' for writing",
 -                                git_path("MERGE_MSG"));
 -              if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
 -                      merge_msg.len)
 -                      die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
 -              close(fd);
 +              write_merge_msg();
                fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
                if (fd < 0)
 -                      die_errno("Could not open '%s' for writing",
 +                      die_errno(_("Could not open '%s' for writing"),
                                  git_path("MERGE_MODE"));
                strbuf_reset(&buf);
                if (!allow_fast_forward)
                        strbuf_addf(&buf, "no-ff");
                if (write_in_full(fd, buf.buf, buf.len) != buf.len)
 -                      die_errno("Could not write to '%s'", git_path("MERGE_MODE"));
 +                      die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
                close(fd);
        }
  
        if (merge_was_ok) {
 -              fprintf(stderr, "Automatic merge went well; "
 -                      "stopped before committing as requested\n");
 +              fprintf(stderr, _("Automatic merge went well; "
 +                      "stopped before committing as requested\n"));
                return 0;
        } else
                return suggest_conflicts(option_renormalize);
diff --combined builtin/rev-list.c
index 4be66998f6bcb998df094d0c434dfab6bbdcc8c7,0ec42fc75c9bb8e9045f807a764d7a2d06e253de..56727e8c1d9d87fe3e3bf3919ba86d27d0735758
@@@ -16,10 -16,6 +16,10 @@@ static const char rev_list_usage[] 
  "    --min-age=<epoch>\n"
  "    --sparse\n"
  "    --no-merges\n"
 +"    --min-parents=<n>\n"
 +"    --no-min-parents\n"
 +"    --max-parents=<n>\n"
 +"    --no-max-parents\n"
  "    --remove-empty\n"
  "    --all\n"
  "    --branches\n"
@@@ -55,9 -51,7 +55,9 @@@ static void show_commit(struct commit *
        graph_show_commit(revs->graph);
  
        if (revs->count) {
 -              if (commit->object.flags & SYMMETRIC_LEFT)
 +              if (commit->object.flags & PATCHSAME)
 +                      revs->count_same++;
 +              else if (commit->object.flags & SYMMETRIC_LEFT)
                        revs->count_left++;
                else
                        revs->count_right++;
        if (info->header_prefix)
                fputs(info->header_prefix, stdout);
  
 -      if (!revs->graph) {
 -              if (commit->object.flags & BOUNDARY)
 -                      putchar('-');
 -              else if (commit->object.flags & UNINTERESTING)
 -                      putchar('^');
 -              else if (revs->left_right) {
 -                      if (commit->object.flags & SYMMETRIC_LEFT)
 -                              putchar('<');
 -                      else
 -                              putchar('>');
 -              }
 -      }
 +      if (!revs->graph)
 +              fputs(get_revision_mark(revs, commit), stdout);
        if (revs->abbrev_commit && revs->abbrev)
                fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
                      stdout);
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
                ctx.date_mode = revs->date_mode;
-               pretty_print_commit(revs->commit_format, commit, &buf, &ctx);
+               ctx.fmt = revs->commit_format;
+               pretty_print_commit(&ctx, commit, &buf);
                if (revs->graph) {
                        if (buf.len) {
                                if (revs->commit_format != CMIT_FMT_ONELINE)
@@@ -408,12 -413,8 +409,12 @@@ int cmd_rev_list(int argc, const char *
                             &info);
  
        if (revs.count) {
 -              if (revs.left_right)
 +              if (revs.left_right && revs.cherry_mark)
 +                      printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same);
 +              else if (revs.left_right)
                        printf("%d\t%d\n", revs.count_left, revs.count_right);
 +              else if (revs.cherry_mark)
 +                      printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same);
                else
                        printf("%d\n", revs.count_left + revs.count_right);
        }
diff --combined builtin/shortlog.c
index b6f4b0eb03b66dda2f691ff5c80b78321c1b701d,074fd26048755f6097cb9ff8719b493083d294f6..37f3193a3366725a6b11bdb55db7b6ffc6e9b3ce
@@@ -29,6 -29,9 +29,6 @@@ static int compare_by_number(const voi
                return -1;
  }
  
 -const char *format_subject(struct strbuf *sb, const char *msg,
 -                         const char *line_separator);
 -
  static void insert_one_record(struct shortlog *log,
                              const char *author,
                              const char *oneline)
@@@ -138,9 -141,8 +138,8 @@@ void shortlog_add_commit(struct shortlo
        const char *author = NULL, *buffer;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf ufbuf = STRBUF_INIT;
-       struct pretty_print_context ctx = {0};
  
-       pretty_print_commit(CMIT_FMT_RAW, commit, &buf, &ctx);
+       pp_commit_easy(CMIT_FMT_RAW, commit, &buf);
        buffer = buf.buf;
        while (*buffer && *buffer != '\n') {
                const char *eol = strchr(buffer, '\n');
                buffer = eol;
        }
        if (!author)
 -              die("Missing author: %s",
 +              die(_("Missing author: %s"),
                    sha1_to_hex(commit->object.sha1));
        if (log->user_format) {
                struct pretty_print_context ctx = {0};
+               ctx.fmt = CMIT_FMT_USERFORMAT;
                ctx.abbrev = log->abbrev;
                ctx.subject = "";
                ctx.after_subject = "";
                ctx.date_mode = DATE_NORMAL;
-               pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &ufbuf, &ctx);
+               pretty_print_commit(&ctx, commit, &ufbuf);
                buffer = ufbuf.buf;
        } else if (*buffer) {
                buffer++;
@@@ -178,7 -181,7 +178,7 @@@ static void get_from_rev(struct rev_inf
        struct commit *commit;
  
        if (prepare_revision_walk(rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
        while ((commit = get_revision(rev)) != NULL)
                shortlog_add_commit(log, commit);
  }
@@@ -281,7 -284,7 +281,7 @@@ parse_done
        argc = parse_options_end(&ctx);
  
        if (setup_revisions(argc, argv, &rev, NULL) != 1) {
 -              error("unrecognized argument: %s", argv[1]);
 +              error(_("unrecognized argument: %s"), argv[1]);
                usage_with_options(shortlog_usage, options);
        }
  
                add_head_to_pending(&rev);
        if (rev.pending.nr == 0) {
                if (isatty(0))
 -                      fprintf(stderr, "(reading log message from standard input)\n");
 +                      fprintf(stderr, _("(reading log message from standard input)\n"));
                read_from_stdin(&log);
        }
        else
diff --combined builtin/show-branch.c
index 1abcd9e02e64022e2619db3de40c7b3a731d4479,a5fc2aa88426b584945b132a21d2cc97176a0993..facc63a79ec0c86fd9df59792cd4f20030af5db1
@@@ -12,6 -12,16 +12,6 @@@ static const char* show_branch_usage[] 
  };
  
  static int showbranch_use_color = -1;
 -static char column_colors[][COLOR_MAXLEN] = {
 -      GIT_COLOR_RED,
 -      GIT_COLOR_GREEN,
 -      GIT_COLOR_YELLOW,
 -      GIT_COLOR_BLUE,
 -      GIT_COLOR_MAGENTA,
 -      GIT_COLOR_CYAN,
 -};
 -
 -#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
  
  static int default_num;
  static int default_alloc;
@@@ -27,7 -37,7 +27,7 @@@ static const char **default_arg
  static const char *get_color_code(int idx)
  {
        if (showbranch_use_color)
 -              return column_colors[idx];
 +              return column_colors_ansi[idx % column_colors_ansi_max];
        return "";
  }
  
@@@ -283,8 -293,7 +283,7 @@@ static void show_one_commit(struct comm
        struct commit_name *name = commit->util;
  
        if (commit->object.parsed) {
-               struct pretty_print_context ctx = {0};
-               pretty_print_commit(CMIT_FMT_ONELINE, commit, &pretty, &ctx);
+               pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty);
                pretty_str = pretty.buf;
        }
        if (!prefixcmp(pretty_str, "[PATCH] "))
@@@ -882,7 -891,7 +881,7 @@@ int cmd_show_branch(int ac, const char 
                                for (j = 0; j < i; j++)
                                        putchar(' ');
                                printf("%s%c%s [%s] ",
 -                                     get_color_code(i % COLUMN_COLORS_MAX),
 +                                     get_color_code(i),
                                       is_head ? '*' : '!',
                                       get_color_reset_code(), ref_name[i]);
                        }
                                else
                                        mark = '+';
                                printf("%s%c%s",
 -                                     get_color_code(i % COLUMN_COLORS_MAX),
 +                                     get_color_code(i),
                                       mark, get_color_reset_code());
                        }
                        putchar(' ');
diff --combined commit.h
index 3114bd1781c3c5e735dfc1b1a7b4131270992f14,e985dcc6e5d3d0f19db8bd691d226d00b234ae98..a2d571b97410fa857b4c177325c4556dac50fe3f
+++ b/commit.h
@@@ -38,7 -38,7 +38,7 @@@ struct commit *lookup_commit_reference_
                                              int quiet);
  struct commit *lookup_commit_reference_by_name(const char *name);
  
 -int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
 +int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
  int parse_commit(struct commit *item);
  
  /* Find beginning and length of commit subject. */
@@@ -68,10 -68,13 +68,12 @@@ enum cmit_fmt 
        CMIT_FMT_UNSPECIFIED
  };
  
 -struct pretty_print_context
 -{
 +struct pretty_print_context {
+       enum cmit_fmt fmt;
        int abbrev;
        const char *subject;
        const char *after_subject;
+       int preserve_subject;
        enum date_mode date_mode;
        int need_8bit_cte;
        int show_notes;
@@@ -90,26 -93,24 +92,26 @@@ extern char *logmsg_reencode(const stru
  extern char *reencode_commit_message(const struct commit *commit,
                                     const char **encoding_p);
  extern void get_commit_format(const char *arg, struct rev_info *);
 +extern const char *format_subject(struct strbuf *sb, const char *msg,
 +                                const char *line_separator);
  extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
  extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
- extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-                               struct strbuf *sb,
-                               const struct pretty_print_context *context);
- void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-                  const char *line, enum date_mode dmode,
-                  const char *encoding);
- void pp_title_line(enum cmit_fmt fmt,
+ extern void pretty_print_commit(const struct pretty_print_context *pp,
+                               const struct commit *commit,
+                               struct strbuf *sb);
+ extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+                          struct strbuf *sb);
+ void pp_user_info(const struct pretty_print_context *pp,
+                 const char *what, struct strbuf *sb,
+                 const char *line, const char *encoding);
+ void pp_title_line(const struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
-                  const char *subject,
-                  const char *after_subject,
                   const char *encoding,
                   int need_8bit_cte);
- void pp_remainder(enum cmit_fmt fmt,
+ void pp_remainder(const struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent);
@@@ -145,6 -146,8 +147,6 @@@ struct commit_graft *read_graft_line(ch
  int register_commit_graft(struct commit_graft *, int);
  struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
  
 -const unsigned char *lookup_replace_object(const unsigned char *sha1);
 -
  extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
  extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
  extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
@@@ -159,7 -162,7 +161,7 @@@ extern struct commit_list *get_shallow_
  int is_descendant_of(struct commit *, struct commit_list *);
  int in_merge_bases(struct commit *, struct commit **, int);
  
 -extern int interactive_add(int argc, const char **argv, const char *prefix);
 +extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
  extern int run_add_interactive(const char *revision, const char *patch_mode,
                               const char **pathspec);
  
diff --combined log-tree.c
index 296f417dfcc2fe966527064aa4a95dc96a8eca2d,0c41789356f7fc54129a73cefcb815c5a9d8a7ba..e9457019d5ac7aff1e185e195f37b851732f6210
@@@ -294,9 -294,8 +294,9 @@@ void log_write_email_headers(struct rev
        if (opt->total > 0) {
                static char buffer[64];
                snprintf(buffer, sizeof(buffer),
 -                       "Subject: [%s %0*d/%d] ",
 +                       "Subject: [%s%s%0*d/%d] ",
                         opt->subject_prefix,
 +                       *opt->subject_prefix ? " " : "",
                         digits_in_number(opt->total),
                         opt->nr, opt->total);
                subject = buffer;
@@@ -381,8 -380,18 +381,8 @@@ void show_log(struct rev_info *opt
        if (!opt->verbose_header) {
                graph_show_commit(opt->graph);
  
 -              if (!opt->graph) {
 -                      if (commit->object.flags & BOUNDARY)
 -                              putchar('-');
 -                      else if (commit->object.flags & UNINTERESTING)
 -                              putchar('^');
 -                      else if (opt->left_right) {
 -                              if (commit->object.flags & SYMMETRIC_LEFT)
 -                                      putchar('<');
 -                              else
 -                                      putchar('>');
 -                      }
 -              }
 +              if (!opt->graph)
 +                      put_revision_mark(opt, commit);
                fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
                if (opt->commit_format != CMIT_FMT_ONELINE)
                        fputs("commit ", stdout);
  
 -              if (!opt->graph) {
 -                      if (commit->object.flags & BOUNDARY)
 -                              putchar('-');
 -                      else if (commit->object.flags & UNINTERESTING)
 -                              putchar('^');
 -                      else if (opt->left_right) {
 -                              if (commit->object.flags & SYMMETRIC_LEFT)
 -                                      putchar('<');
 -                              else
 -                                      putchar('>');
 -                      }
 -              }
 +              if (!opt->graph)
 +                      put_revision_mark(opt, commit);
                fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
                      stdout);
                if (opt->print_parents)
        ctx.date_mode = opt->date_mode;
        ctx.abbrev = opt->diffopt.abbrev;
        ctx.after_subject = extra_headers;
+       ctx.preserve_subject = opt->preserve_subject;
        ctx.reflog_info = opt->reflog_info;
-       pretty_print_commit(opt->commit_format, commit, &msgbuf, &ctx);
+       ctx.fmt = opt->commit_format;
+       pretty_print_commit(&ctx, commit, &msgbuf);
  
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
diff --combined pretty.c
index 52174fd14722d131d533013120517f5fcb8d82f6,193bafd8352e72b0c3a7c65748500a558392e917..f45eb54e4c99b8d67e4aa85f9a6218ea7a560592
+++ b/pretty.c
@@@ -208,58 -208,6 +208,58 @@@ int has_non_ascii(const char *s
        return 0;
  }
  
 +static int is_rfc822_special(char ch)
 +{
 +      switch (ch) {
 +      case '(':
 +      case ')':
 +      case '<':
 +      case '>':
 +      case '[':
 +      case ']':
 +      case ':':
 +      case ';':
 +      case '@':
 +      case ',':
 +      case '.':
 +      case '"':
 +      case '\\':
 +              return 1;
 +      default:
 +              return 0;
 +      }
 +}
 +
 +static int has_rfc822_specials(const char *s, int len)
 +{
 +      int i;
 +      for (i = 0; i < len; i++)
 +              if (is_rfc822_special(s[i]))
 +                      return 1;
 +      return 0;
 +}
 +
 +static void add_rfc822_quoted(struct strbuf *out, const char *s, int len)
 +{
 +      int i;
 +
 +      /* just a guess, we may have to also backslash-quote */
 +      strbuf_grow(out, len + 2);
 +
 +      strbuf_addch(out, '"');
 +      for (i = 0; i < len; i++) {
 +              switch (s[i]) {
 +              case '"':
 +              case '\\':
 +                      strbuf_addch(out, '\\');
 +                      /* fall through */
 +              default:
 +                      strbuf_addch(out, s[i]);
 +              }
 +      }
 +      strbuf_addch(out, '"');
 +}
 +
  static int is_rfc2047_special(char ch)
  {
        return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
@@@ -318,16 -266,16 +318,16 @@@ needquote
        strbuf_addstr(sb, "?=");
  }
  
- void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
-                 const char *line, enum date_mode dmode,
-                 const char *encoding)
+ void pp_user_info(const struct pretty_print_context *pp,
+                 const char *what, struct strbuf *sb,
+                 const char *line, const char *encoding)
  {
        char *date;
        int namelen;
        unsigned long time;
        int tz;
  
-       if (fmt == CMIT_FMT_ONELINE)
+       if (pp->fmt == CMIT_FMT_ONELINE)
                return;
        date = strchr(line, '>');
        if (!date)
        time = strtoul(date, &date, 10);
        tz = strtol(date, NULL, 10);
  
-       if (fmt == CMIT_FMT_EMAIL) {
+       if (pp->fmt == CMIT_FMT_EMAIL) {
                char *name_tail = strchr(line, '<');
                int display_name_length;
                int final_line;
                        name_tail--;
                display_name_length = name_tail - line;
                strbuf_addstr(sb, "From: ");
 -              add_rfc2047(sb, line, display_name_length, encoding);
 +              if (!has_rfc822_specials(line, display_name_length)) {
 +                      add_rfc2047(sb, line, display_name_length, encoding);
 +              } else {
 +                      struct strbuf quoted = STRBUF_INIT;
 +                      add_rfc822_quoted(&quoted, line, display_name_length);
 +                      add_rfc2047(sb, quoted.buf, quoted.len, encoding);
 +                      strbuf_release(&quoted);
 +              }
                for (final_line = 0; final_line < sb->len; final_line++)
                        if (sb->buf[sb->len - final_line - 1] == '\n')
                                break;
                strbuf_addch(sb, '\n');
        } else {
                strbuf_addf(sb, "%s: %.*s%.*s\n", what,
-                             (fmt == CMIT_FMT_FULLER) ? 4 : 0,
+                             (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
                              "    ", namelen, line);
        }
-       switch (fmt) {
+       switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
-               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, dmode));
+               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
                strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
                break;
        case CMIT_FMT_FULLER:
-               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
+               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, pp->date_mode));
                break;
        default:
                /* notin' */
@@@ -408,12 -349,12 +408,12 @@@ static const char *skip_empty_lines(con
        return msg;
  }
  
- static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
-                       const struct commit *commit, int abbrev)
+ static void add_merge_info(const struct pretty_print_context *pp,
+                          struct strbuf *sb, const struct commit *commit)
  {
        struct commit_list *parent = commit->parents;
  
-       if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+       if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
            !parent || !parent->next)
                return;
  
        while (parent) {
                struct commit *p = parent->item;
                const char *hex = NULL;
-               if (abbrev)
-                       hex = find_unique_abbrev(p->object.sha1, abbrev);
+               if (pp->abbrev)
+                       hex = find_unique_abbrev(p->object.sha1, pp->abbrev);
                if (!hex)
                        hex = sha1_to_hex(p->object.sha1);
                parent = parent->next;
@@@ -944,7 -885,11 +944,7 @@@ static size_t format_commit_one(struct 
                                              c->abbrev_parent_hashes.off;
                return 1;
        case 'm':               /* left/right/bottom */
 -              strbuf_addch(sb, (commit->object.flags & BOUNDARY)
 -                               ? '-'
 -                               : (commit->object.flags & SYMMETRIC_LEFT)
 -                               ? '<'
 -                               : '>');
 +              strbuf_addstr(sb, get_revision_mark(NULL, commit));
                return 1;
        case 'd':
                format_decoration(sb, commit);
@@@ -1084,7 -1029,7 +1084,7 @@@ void userformat_find_requirements(cons
                        return;
                fmt = user_format;
        }
 -      strbuf_expand(&dummy, user_format, userformat_want_item, w);
 +      strbuf_expand(&dummy, fmt, userformat_want_item, w);
        strbuf_release(&dummy);
  }
  
@@@ -1116,9 -1061,7 +1116,7 @@@ void format_commit_message(const struc
                free(context.message);
  }
  
- static void pp_header(enum cmit_fmt fmt,
-                     int abbrev,
-                     enum date_mode dmode,
+ static void pp_header(const struct pretty_print_context *pp,
                      const char *encoding,
                      const struct commit *commit,
                      const char **msg_p,
                        /* End of header */
                        return;
  
-               if (fmt == CMIT_FMT_RAW) {
+               if (pp->fmt == CMIT_FMT_RAW) {
                        strbuf_add(sb, line, linelen);
                        continue;
                }
                                ;
                        /* with enough slop */
                        strbuf_grow(sb, num * 50 + 20);
-                       add_merge_info(fmt, sb, commit, abbrev);
+                       add_merge_info(pp, sb, commit);
                        parents_shown = 1;
                }
  
                 */
                if (!memcmp(line, "author ", 7)) {
                        strbuf_grow(sb, linelen + 80);
-                       pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
+                       pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
                if (!memcmp(line, "committer ", 10) &&
-                   (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
+                   (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
-                       pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
+                       pp_user_info(pp, "Commit", sb, line + 10, encoding);
                }
        }
  }
  
- void pp_title_line(enum cmit_fmt fmt,
+ void pp_title_line(const struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
-                  const char *subject,
-                  const char *after_subject,
                   const char *encoding,
                   int need_8bit_cte)
  {
        struct strbuf title;
  
        strbuf_init(&title, 80);
-       *msg_p = format_subject(&title, *msg_p, " ");
+       *msg_p = format_subject(&title, *msg_p,
+                               pp->preserve_subject ? "\n" : " ");
  
        strbuf_grow(sb, title.len + 1024);
-       if (subject) {
-               strbuf_addstr(sb, subject);
+       if (pp->subject) {
+               strbuf_addstr(sb, pp->subject);
                add_rfc2047(sb, title.buf, title.len, encoding);
        } else {
                strbuf_addbuf(sb, &title);
                        "Content-Transfer-Encoding: 8bit\n";
                strbuf_addf(sb, header_fmt, encoding);
        }
-       if (after_subject) {
-               strbuf_addstr(sb, after_subject);
+       if (pp->after_subject) {
+               strbuf_addstr(sb, pp->after_subject);
        }
-       if (fmt == CMIT_FMT_EMAIL) {
+       if (pp->fmt == CMIT_FMT_EMAIL) {
                strbuf_addch(sb, '\n');
        }
        strbuf_release(&title);
  }
  
- void pp_remainder(enum cmit_fmt fmt,
+ void pp_remainder(const struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent)
                if (is_empty_line(line, &linelen)) {
                        if (first)
                                continue;
-                       if (fmt == CMIT_FMT_SHORT)
+                       if (pp->fmt == CMIT_FMT_SHORT)
                                break;
                }
                first = 0;
@@@ -1259,19 -1201,19 +1256,19 @@@ char *reencode_commit_message(const str
        return logmsg_reencode(commit, encoding);
  }
  
- void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
-                        struct strbuf *sb,
-                        const struct pretty_print_context *context)
+ void pretty_print_commit(const struct pretty_print_context *pp,
+                        const struct commit *commit,
+                        struct strbuf *sb)
  {
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg = commit->buffer;
        char *reencoded;
        const char *encoding;
-       int need_8bit_cte = context->need_8bit_cte;
+       int need_8bit_cte = pp->need_8bit_cte;
  
-       if (fmt == CMIT_FMT_USERFORMAT) {
-               format_commit_message(commit, user_format, sb, context);
+       if (pp->fmt == CMIT_FMT_USERFORMAT) {
+               format_commit_message(commit, user_format, sb, pp);
                return;
        }
  
                msg = reencoded;
        }
  
-       if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
                indent = 0;
  
        /*
         * We need to check and emit Content-type: to mark it
         * as 8-bit if we haven't done so.
         */
-       if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
+       if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
                int i, ch, in_body;
  
                for (in_body = i = 0; (ch = msg[i]); i++) {
                }
        }
  
-       pp_header(fmt, context->abbrev, context->date_mode, encoding,
-                 commit, &msg, sb);
-       if (fmt != CMIT_FMT_ONELINE && !context->subject) {
+       pp_header(pp, encoding, commit, &msg, sb);
+       if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) {
                strbuf_addch(sb, '\n');
        }
  
        msg = skip_empty_lines(msg);
  
        /* These formats treat the title line specially. */
-       if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
-               pp_title_line(fmt, &msg, sb, context->subject,
-                             context->after_subject, encoding, need_8bit_cte);
+       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+               pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
  
        beginning_of_body = sb->len;
-       if (fmt != CMIT_FMT_ONELINE)
-               pp_remainder(fmt, &msg, sb, indent);
+       if (pp->fmt != CMIT_FMT_ONELINE)
+               pp_remainder(pp, &msg, sb, indent);
        strbuf_rtrim(sb);
  
        /* Make sure there is an EOLN for the non-oneline case */
-       if (fmt != CMIT_FMT_ONELINE)
+       if (pp->fmt != CMIT_FMT_ONELINE)
                strbuf_addch(sb, '\n');
  
        /*
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
-       if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+       if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
-       if (context->show_notes)
+       if (pp->show_notes)
                format_display_notes(commit->object.sha1, sb, encoding,
                                     NOTES_SHOW_HEADER | NOTES_INDENT);
  
        free(reencoded);
  }
+ void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
+                   struct strbuf *sb)
+ {
+       struct pretty_print_context pp = {0};
+       pp.fmt = fmt;
+       pretty_print_commit(&pp, commit, sb);
+ }
diff --combined revision.h
index 4499cebded6bc4348c0648a77d6e0dae4cf00acf,f8ddd83e79db99bfe93dee96696b6606916bd403..3d64adad18e2c889b7a7b7367f99c239f958dc70
@@@ -14,8 -14,7 +14,8 @@@
  #define CHILD_SHOWN   (1u<<6)
  #define ADDED         (1u<<7) /* Parents already parsed and added? */
  #define SYMMETRIC_LEFT        (1u<<8)
 -#define ALL_REV_FLAGS ((1u<<9)-1)
 +#define PATCHSAME     (1u<<9)
 +#define ALL_REV_FLAGS ((1u<<10)-1)
  
  #define DECORATE_SHORT_REFS   1
  #define DECORATE_FULL_REFS    2
@@@ -35,13 -34,14 +35,13 @@@ struct rev_info 
        /* Basic information */
        const char *prefix;
        const char *def;
 -      void *prune_data;
 -      unsigned int early_output;
 +      struct pathspec prune_data;
 +      unsigned int    early_output:1,
 +                      ignore_missing:1;
  
        /* Traversal flags */
        unsigned int    dense:1,
                        prune:1,
 -                      no_merges:1,
 -                      merges_only:1,
                        no_walk:1,
                        show_all:1,
                        remove_empty_trees:1,
@@@ -59,8 -59,6 +59,8 @@@
                        boundary:2,
                        count:1,
                        left_right:1,
 +                      left_only:1,
 +                      right_only:1,
                        rewrite_parents:1,
                        print_parents:1,
                        show_source:1,
@@@ -68,7 -66,6 +68,7 @@@
                        reverse:1,
                        reverse_output_stage:1,
                        cherry_pick:1,
 +                      cherry_mark:1,
                        bisect:1,
                        ancestry_path:1,
                        first_parent_only:1;
                        show_notes_given:1,
                        pretty_given:1,
                        abbrev_commit:1,
 +                      abbrev_commit_given:1,
                        use_terminator:1,
                        missing_newline:1,
-                       date_mode_explicit:1;
+                       date_mode_explicit:1,
+                       preserve_subject:1;
        unsigned int    disable_stdin:1;
  
        enum date_mode date_mode;
        int max_count;
        unsigned long max_age;
        unsigned long min_age;
 +      int min_parents;
 +      int max_parents;
  
        /* diff info for patches and for paths limiting */
        struct diff_options diffopt;
        /* commit counts */
        int count_left;
        int count_right;
 +      int count_same;
  };
  
  #define REV_TREE_SAME         0
@@@ -170,8 -164,6 +171,8 @@@ extern int handle_revision_arg(const ch
  
  extern int prepare_revision_walk(struct rev_info *revs);
  extern struct commit *get_revision(struct rev_info *revs);
 +extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
 +extern void put_revision_mark(const struct rev_info *revs, const struct commit *commit);
  
  extern void mark_parents_uninteresting(struct commit *commit);
  extern void mark_tree_uninteresting(struct tree *tree);