Merge branch 'jk/war-on-git-path'
authorJunio C Hamano <gitster@pobox.com>
Wed, 26 Apr 2017 06:39:08 +0000 (15:39 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Apr 2017 06:39:08 +0000 (15:39 +0900)
While handy, "git_path()" is a dangerous function to use as a
callsite that uses it safely one day can be broken by changes
to other code that calls it. Reduction of its use continues.

* jk/war-on-git-path:
am: drop "dir" parameter from am_state_init
replace strbuf_addstr(git_path()) with git_path_buf()
replace xstrdup(git_path(...)) with git_pathdup(...)
use git_path_* helper functions
branch: add edit_description() helper
bisect: add git_path_bisect_terms helper

1  2 
bisect.c
builtin/am.c
builtin/branch.c
builtin/commit.c
builtin/config.c
builtin/pull.c
builtin/worktree.c
fast-import.c
sequencer.c
diff --combined bisect.c
index 03af06c66cebcbc0699b8a074f9ef120da09248a,fb9c9b126f28b2a84465883736f15b33af970c41..08c9fb7266c3b2ff8b970c21d350be4adfe7b0fc
+++ b/bisect.c
@@@ -12,8 -12,8 +12,8 @@@
  #include "sha1-array.h"
  #include "argv-array.h"
  
 -static struct sha1_array good_revs;
 -static struct sha1_array skipped_revs;
 +static struct oid_array good_revs;
 +static struct oid_array skipped_revs;
  
  static struct object_id *current_bad_oid;
  
@@@ -200,7 -200,6 +200,7 @@@ static struct commit_list *best_bisecti
  {
        struct commit_list *p;
        struct commit_dist *array = xcalloc(nr, sizeof(*array));
 +      struct strbuf buf = STRBUF_INIT;
        int cnt, i;
  
        for (p = list, cnt = 0; p; p = p->next) {
        }
        QSORT(array, cnt, compare_commit_dist);
        for (p = list, i = 0; i < cnt; i++) {
 -              char buf[100]; /* enough for dist=%d */
                struct object *obj = &(array[i].commit->object);
  
 -              snprintf(buf, sizeof(buf), "dist=%d", array[i].distance);
 -              add_name_decoration(DECORATION_NONE, buf, obj);
 +              strbuf_reset(&buf);
 +              strbuf_addf(&buf, "dist=%d", array[i].distance);
 +              add_name_decoration(DECORATION_NONE, buf.buf, obj);
  
                p->item = array[i].commit;
                p = p->next;
        }
        if (p)
                p->next = NULL;
 +      strbuf_release(&buf);
        free(array);
        return list;
  }
@@@ -415,9 -413,9 +415,9 @@@ static int register_ref(const char *ref
                current_bad_oid = xmalloc(sizeof(*current_bad_oid));
                oidcpy(current_bad_oid, oid);
        } else if (starts_with(refname, good_prefix.buf)) {
 -              sha1_array_append(&good_revs, oid->hash);
 +              oid_array_append(&good_revs, oid);
        } else if (starts_with(refname, "skip-")) {
 -              sha1_array_append(&skipped_revs, oid->hash);
 +              oid_array_append(&skipped_revs, oid);
        }
  
        strbuf_release(&good_prefix);
@@@ -432,6 -430,7 +432,7 @@@ static int read_bisect_refs(void
  
  static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
  static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+ static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
  
  static void read_bisect_paths(struct argv_array *array)
  {
        fclose(fp);
  }
  
 -static char *join_sha1_array_hex(struct sha1_array *array, char delim)
 +static char *join_sha1_array_hex(struct oid_array *array, char delim)
  {
        struct strbuf joined_hexs = STRBUF_INIT;
        int i;
  
        for (i = 0; i < array->nr; i++) {
 -              strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
 +              strbuf_addstr(&joined_hexs, oid_to_hex(array->oid + i));
                if (i + 1 < array->nr)
                        strbuf_addch(&joined_hexs, delim);
        }
@@@ -501,7 -500,8 +502,7 @@@ struct commit_list *filter_skipped(stru
        while (list) {
                struct commit_list *next = list->next;
                list->next = NULL;
 -              if (0 <= sha1_array_lookup(&skipped_revs,
 -                                         list->item->object.oid.hash)) {
 +              if (0 <= oid_array_lookup(&skipped_revs, &list->item->object.oid)) {
                        if (skipped_first && !*skipped_first)
                                *skipped_first = 1;
                        /* Move current to tried list */
@@@ -622,7 -622,7 +623,7 @@@ static void bisect_rev_setup(struct rev
        argv_array_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid));
        for (i = 0; i < good_revs.nr; i++)
                argv_array_pushf(&rev_argv, good_format,
 -                               sha1_to_hex(good_revs.sha1[i]));
 +                               oid_to_hex(good_revs.oid + i));
        argv_array_push(&rev_argv, "--");
        if (read_paths)
                read_bisect_paths(&rev_argv);
@@@ -683,7 -683,7 +684,7 @@@ static int is_expected_rev(const struc
  
  static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
  {
 -      char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
 +      char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
  
        memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
        update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
  }
  
 -static struct commit *get_commit_reference(const unsigned char *sha1)
 +static struct commit *get_commit_reference(const struct object_id *oid)
  {
 -      struct commit *r = lookup_commit_reference(sha1);
 +      struct commit *r = lookup_commit_reference(oid->hash);
        if (!r)
 -              die(_("Not a valid commit name %s"), sha1_to_hex(sha1));
 +              die(_("Not a valid commit name %s"), oid_to_hex(oid));
        return r;
  }
  
@@@ -716,9 -716,9 +717,9 @@@ static struct commit **get_bad_and_good
        int i, n = 0;
  
        ALLOC_ARRAY(rev, 1 + good_revs.nr);
 -      rev[n++] = get_commit_reference(current_bad_oid->hash);
 +      rev[n++] = get_commit_reference(current_bad_oid);
        for (i = 0; i < good_revs.nr; i++)
 -              rev[n++] = get_commit_reference(good_revs.sha1[i]);
 +              rev[n++] = get_commit_reference(good_revs.oid + i);
        *rev_nr = n;
  
        return rev;
@@@ -755,9 -755,9 +756,9 @@@ static void handle_bad_merge_base(void
        exit(1);
  }
  
 -static void handle_skipped_merge_base(const unsigned char *mb)
 +static void handle_skipped_merge_base(const struct object_id *mb)
  {
 -      char *mb_hex = sha1_to_hex(mb);
 +      char *mb_hex = oid_to_hex(mb);
        char *bad_hex = oid_to_hex(current_bad_oid);
        char *good_hex = join_sha1_array_hex(&good_revs, ' ');
  
@@@ -788,16 -788,16 +789,16 @@@ static void check_merge_bases(int no_ch
        result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
  
        for (; result; result = result->next) {
 -              const unsigned char *mb = result->item->object.oid.hash;
 -              if (!hashcmp(mb, current_bad_oid->hash)) {
 +              const struct object_id *mb = &result->item->object.oid;
 +              if (!oidcmp(mb, current_bad_oid)) {
                        handle_bad_merge_base();
 -              } else if (0 <= sha1_array_lookup(&good_revs, mb)) {
 +              } else if (0 <= oid_array_lookup(&good_revs, mb)) {
                        continue;
 -              } else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
 +              } else if (0 <= oid_array_lookup(&skipped_revs, mb)) {
                        handle_skipped_merge_base(mb);
                } else {
                        printf(_("Bisecting: a merge base must be tested\n"));
 -                      exit(bisect_checkout(mb, no_checkout));
 +                      exit(bisect_checkout(mb->hash, no_checkout));
                }
        }
  
@@@ -906,7 -906,7 +907,7 @@@ static void show_diff_tree(const char *
  void read_bisect_terms(const char **read_bad, const char **read_good)
  {
        struct strbuf str = STRBUF_INIT;
-       const char *filename = git_path("BISECT_TERMS");
+       const char *filename = git_path_bisect_terms();
        FILE *fp = fopen(filename, "r");
  
        if (!fp) {
diff --combined builtin/am.c
index f08b7e6626021e425e4712763bee2236b8eab47b,7a6cf9533db3a5157f48ab01d2ac7c14d4ffebb4..a95dd8b4e6c793d9e51658e2ccbe535cf5a5af45
@@@ -134,17 -134,15 +134,15 @@@ struct am_state 
  };
  
  /**
-  * Initializes am_state with the default values. The state directory is set to
-  * dir.
+  * Initializes am_state with the default values.
   */
- static void am_state_init(struct am_state *state, const char *dir)
+ static void am_state_init(struct am_state *state)
  {
        int gpgsign;
  
        memset(state, 0, sizeof(*state));
  
-       assert(dir);
-       state->dir = xstrdup(dir);
+       state->dir = git_pathdup("rebase-apply");
  
        state->prec = 4;
  
@@@ -762,18 -760,14 +760,18 @@@ static int split_mail_conv(mail_conv_f
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
  
                out = fopen(mail, "w");
 -              if (!out)
 +              if (!out) {
 +                      if (in != stdin)
 +                              fclose(in);
                        return error_errno(_("could not open '%s' for writing"),
                                           mail);
 +              }
  
                ret = fn(out, in, keep_cr);
  
                fclose(out);
 -              fclose(in);
 +              if (in != stdin)
 +                      fclose(in);
  
                if (ret)
                        return error(_("could not parse patch '%s'"), *paths);
@@@ -1053,7 -1047,7 +1051,7 @@@ static void am_setup(struct am_state *s
        } else {
                write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
 -                      delete_ref("ORIG_HEAD", NULL, 0);
 +                      delete_ref(NULL, "ORIG_HEAD", NULL, 0);
        }
  
        /*
@@@ -1185,39 -1179,42 +1183,39 @@@ static void NORETURN die_user_resolve(c
        exit(128);
  }
  
 -static void am_signoff(struct strbuf *sb)
 +/**
 + * Appends signoff to the "msg" field of the am_state.
 + */
 +static void am_append_signoff(struct am_state *state)
  {
        char *cp;
        struct strbuf mine = STRBUF_INIT;
 +      struct strbuf sb = STRBUF_INIT;
  
 -      /* Does it end with our own sign-off? */
 +      strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
 +
 +      /* our sign-off */
        strbuf_addf(&mine, "\n%s%s\n",
                    sign_off_header,
                    fmt_name(getenv("GIT_COMMITTER_NAME"),
                             getenv("GIT_COMMITTER_EMAIL")));
 -      if (mine.len < sb->len &&
 -          !strcmp(mine.buf, sb->buf + sb->len - mine.len))
 +
 +      /* Does sb end with it already? */
 +      if (mine.len < sb.len &&
 +          !strcmp(mine.buf, sb.buf + sb.len - mine.len))
                goto exit; /* no need to duplicate */
  
        /* Does it have any Signed-off-by: in the text */
 -      for (cp = sb->buf;
 +      for (cp = sb.buf;
             cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
             cp = strchr(cp, '\n')) {
 -              if (sb->buf == cp || cp[-1] == '\n')
 +              if (sb.buf == cp || cp[-1] == '\n')
                        break;
        }
  
 -      strbuf_addstr(sb, mine.buf + !!cp);
 +      strbuf_addstr(&sb, mine.buf + !!cp);
  exit:
        strbuf_release(&mine);
 -}
 -
 -/**
 - * Appends signoff to the "msg" field of the am_state.
 - */
 -static void am_append_signoff(struct am_state *state)
 -{
 -      struct strbuf sb = STRBUF_INIT;
 -
 -      strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
 -      am_signoff(&sb);
        state->msg = strbuf_detach(&sb, &state->msg_len);
  }
  
@@@ -1322,6 -1319,9 +1320,6 @@@ static int parse_mail(struct am_state *
        strbuf_addbuf(&msg, &mi.log_message);
        strbuf_stripspace(&msg, 0);
  
 -      if (state->signoff)
 -              am_signoff(&msg);
 -
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
  
@@@ -1846,9 -1846,6 +1844,9 @@@ static void am_run(struct am_state *sta
                        if (skip)
                                goto next; /* mail should be skipped */
  
 +                      if (state->signoff)
 +                              am_append_signoff(state);
 +
                        write_author_script(state);
                        write_commit_msg(state);
                }
@@@ -2173,7 -2170,7 +2171,7 @@@ static void am_abort(struct am_state *s
                                has_curr_head ? &curr_head : NULL, 0,
                                UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
 -              delete_ref(curr_branch, NULL, REF_NODEREF);
 +              delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
  
        free(curr_branch);
        am_destroy(state);
@@@ -2323,7 -2320,7 +2321,7 @@@ int cmd_am(int argc, const char **argv
  
        git_config(git_am_config, NULL);
  
-       am_state_init(&state, git_path("rebase-apply"));
+       am_state_init(&state);
  
        in_progress = am_in_progress(&state);
        if (in_progress)
diff --combined builtin/branch.c
index 0552c42ad115bba218f35d4836f1021e3385f6c2,c55ea58040284243bec41bb4a41227cf66fe0084..48a513a84dbf6cf2516a10cbcced267369bc747c
@@@ -28,21 -28,20 +28,21 @@@ static const char * const builtin_branc
        N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
        N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
        N_("git branch [<options>] [-r | -a] [--points-at]"),
 +      N_("git branch [<options>] [-r | -a] [--format]"),
        NULL
  };
  
  static const char *head;
 -static unsigned char head_sha1[20];
 +static struct object_id head_oid;
  
  static int branch_use_color = -1;
  static char branch_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
 -      GIT_COLOR_NORMAL,       /* PLAIN */
 -      GIT_COLOR_RED,          /* REMOTE */
 -      GIT_COLOR_NORMAL,       /* LOCAL */
 -      GIT_COLOR_GREEN,        /* CURRENT */
 -      GIT_COLOR_BLUE,         /* UPSTREAM */
 +      GIT_COLOR_NORMAL,       /* PLAIN */
 +      GIT_COLOR_RED,          /* REMOTE */
 +      GIT_COLOR_NORMAL,       /* LOCAL */
 +      GIT_COLOR_GREEN,        /* CURRENT */
 +      GIT_COLOR_BLUE,         /* UPSTREAM */
  };
  enum color_branch {
        BRANCH_COLOR_RESET = 0,
@@@ -118,13 -117,13 +118,13 @@@ static int branch_merged(int kind, cons
        if (kind == FILTER_REFS_BRANCHES) {
                struct branch *branch = branch_get(name);
                const char *upstream = branch_get_upstream(branch, NULL);
 -              unsigned char sha1[20];
 +              struct object_id oid;
  
                if (upstream &&
                    (reference_name = reference_name_to_free =
                     resolve_refdup(upstream, RESOLVE_REF_READING,
 -                                  sha1, NULL)) != NULL)
 -                      reference_rev = lookup_commit_reference(sha1);
 +                                  oid.hash, NULL)) != NULL)
 +                      reference_rev = lookup_commit_reference(oid.hash);
        }
        if (!reference_rev)
                reference_rev = head_rev;
  }
  
  static int check_branch_commit(const char *branchname, const char *refname,
 -                             const unsigned char *sha1, struct commit *head_rev,
 +                             const struct object_id *oid, struct commit *head_rev,
                               int kinds, int force)
  {
 -      struct commit *rev = lookup_commit_reference(sha1);
 +      struct commit *rev = lookup_commit_reference(oid->hash);
        if (!rev) {
                error(_("Couldn't look up commit object for '%s'"), refname);
                return -1;
@@@ -184,7 -183,7 +184,7 @@@ static int delete_branches(int argc, co
                           int quiet)
  {
        struct commit *head_rev = NULL;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        char *name = NULL;
        const char *fmt;
        int i;
        }
  
        if (!force) {
 -              head_rev = lookup_commit_reference(head_sha1);
 +              head_rev = lookup_commit_reference(head_oid.hash);
                if (!head_rev)
                        die(_("Couldn't look up commit object for HEAD"));
        }
                                        RESOLVE_REF_READING
                                        | RESOLVE_REF_NO_RECURSE
                                        | RESOLVE_REF_ALLOW_BAD_NAME,
 -                                      sha1, &flags);
 +                                      oid.hash, &flags);
                if (!target) {
                        error(remote_branch
                              ? _("remote-tracking branch '%s' not found.")
                }
  
                if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
 -                  check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
 +                  check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
                                        force)) {
                        ret = 1;
                        goto next;
                }
  
 -              if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
 +              if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash,
                               REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
                               bname.buf,
                               (flags & REF_ISBROKEN) ? "broken"
                               : (flags & REF_ISSYMREF) ? target
 -                             : find_unique_abbrev(sha1, DEFAULT_ABBREV));
 +                             : find_unique_abbrev(oid.hash, DEFAULT_ABBREV));
                }
                delete_branch_config(bname.buf);
  
        return(ret);
  }
  
 -static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
 -              int show_upstream_ref)
 +static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
  {
 -      int ours, theirs;
 -      char *ref = NULL;
 -      struct branch *branch = branch_get(branch_name);
 -      const char *upstream;
 -      struct strbuf fancy = STRBUF_INIT;
 -      int upstream_is_gone = 0;
 -      int added_decoration = 1;
 -
 -      if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
 -              if (!upstream)
 -                      return;
 -              upstream_is_gone = 1;
 -      }
 -
 -      if (show_upstream_ref) {
 -              ref = shorten_unambiguous_ref(upstream, 0);
 -              if (want_color(branch_use_color))
 -                      strbuf_addf(&fancy, "%s%s%s",
 -                                      branch_get_color(BRANCH_COLOR_UPSTREAM),
 -                                      ref, branch_get_color(BRANCH_COLOR_RESET));
 -              else
 -                      strbuf_addstr(&fancy, ref);
 -      }
 +      int i, max = 0;
 +      for (i = 0; i < refs->nr; i++) {
 +              struct ref_array_item *it = refs->items[i];
 +              const char *desc = it->refname;
 +              int w;
  
 -      if (upstream_is_gone) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
 -              else
 -                      added_decoration = 0;
 -      } else if (!ours && !theirs) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s]"), fancy.buf);
 -              else
 -                      added_decoration = 0;
 -      } else if (!ours) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
 -              else
 -                      strbuf_addf(stat, _("[behind %d]"), theirs);
 +              skip_prefix(it->refname, "refs/heads/", &desc);
 +              skip_prefix(it->refname, "refs/remotes/", &desc);
 +              if (it->kind == FILTER_REFS_DETACHED_HEAD) {
 +                      char *head_desc = get_head_description();
 +                      w = utf8_strwidth(head_desc);
 +                      free(head_desc);
 +              } else
 +                      w = utf8_strwidth(desc);
  
 -      } else if (!theirs) {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
 -              else
 -                      strbuf_addf(stat, _("[ahead %d]"), ours);
 -      } else {
 -              if (show_upstream_ref)
 -                      strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
 -                                  fancy.buf, ours, theirs);
 -              else
 -                      strbuf_addf(stat, _("[ahead %d, behind %d]"),
 -                                  ours, theirs);
 +              if (it->kind == FILTER_REFS_REMOTES)
 +                      w += remote_bonus;
 +              if (w > max)
 +                      max = w;
        }
 -      strbuf_release(&fancy);
 -      if (added_decoration)
 -              strbuf_addch(stat, ' ');
 -      free(ref);
 +      return max;
  }
  
 -static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
 -                           struct ref_filter *filter, const char *refname)
 +static const char *quote_literal_for_format(const char *s)
  {
 -      struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
 -      const char *sub = _(" **** invalid ref ****");
 -      struct commit *commit = item->commit;
 +      static struct strbuf buf = STRBUF_INIT;
  
 -      if (!parse_commit(commit)) {
 -              pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
 -              sub = subject.buf;
 +      strbuf_reset(&buf);
 +      while (*s) {
 +              const char *ep = strchrnul(s, '%');
 +              if (s < ep)
 +                      strbuf_add(&buf, s, ep - s);
 +              if (*ep == '%') {
 +                      strbuf_addstr(&buf, "%%");
 +                      s = ep + 1;
 +              } else {
 +                      s = ep;
 +              }
        }
 -
 -      if (item->kind == FILTER_REFS_BRANCHES)
 -              fill_tracking_info(&stat, refname, filter->verbose > 1);
 -
 -      strbuf_addf(out, " %s %s%s",
 -              find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
 -              stat.buf, sub);
 -      strbuf_release(&stat);
 -      strbuf_release(&subject);
 +      return buf.buf;
  }
  
 -static char *get_head_description(void)
 +static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
  {
 -      struct strbuf desc = STRBUF_INIT;
 -      struct wt_status_state state;
 -      memset(&state, 0, sizeof(state));
 -      wt_status_get_state(&state, 1);
 -      if (state.rebase_in_progress ||
 -          state.rebase_interactive_in_progress)
 -              strbuf_addf(&desc, _("(no branch, rebasing %s)"),
 -                          state.branch);
 -      else if (state.bisect_in_progress)
 -              strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
 -                          state.branch);
 -      else if (state.detached_from) {
 -              if (state.detached_at)
 -                      /* TRANSLATORS: make sure this matches
 -                         "HEAD detached at " in wt-status.c */
 -                      strbuf_addf(&desc, _("(HEAD detached at %s)"),
 -                              state.detached_from);
 -              else
 -                      /* TRANSLATORS: make sure this matches
 -                         "HEAD detached from " in wt-status.c */
 -                      strbuf_addf(&desc, _("(HEAD detached from %s)"),
 -                              state.detached_from);
 -      }
 -      else
 -              strbuf_addstr(&desc, _("(no branch)"));
 -      free(state.branch);
 -      free(state.onto);
 -      free(state.detached_from);
 -      return strbuf_detach(&desc, NULL);
 -}
 +      struct strbuf fmt = STRBUF_INIT;
 +      struct strbuf local = STRBUF_INIT;
 +      struct strbuf remote = STRBUF_INIT;
  
 -static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
 -                                    struct ref_filter *filter, const char *remote_prefix)
 -{
 -      char c;
 -      int current = 0;
 -      int color;
 -      struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
 -      const char *prefix_to_show = "";
 -      const char *prefix_to_skip = NULL;
 -      const char *desc = item->refname;
 -      char *to_free = NULL;
 +      strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)",
 +                  branch_get_color(BRANCH_COLOR_CURRENT));
  
 -      switch (item->kind) {
 -      case FILTER_REFS_BRANCHES:
 -              prefix_to_skip = "refs/heads/";
 -              skip_prefix(desc, prefix_to_skip, &desc);
 -              if (!filter->detached && !strcmp(desc, head))
 -                      current = 1;
 +      if (filter->verbose) {
 +              struct strbuf obname = STRBUF_INIT;
 +
 +              if (filter->abbrev < 0)
 +                      strbuf_addf(&obname, "%%(objectname:short)");
 +              else if (!filter->abbrev)
 +                      strbuf_addf(&obname, "%%(objectname)");
                else
 -                      color = BRANCH_COLOR_LOCAL;
 -              break;
 -      case FILTER_REFS_REMOTES:
 -              prefix_to_skip = "refs/remotes/";
 -              skip_prefix(desc, prefix_to_skip, &desc);
 -              color = BRANCH_COLOR_REMOTE;
 -              prefix_to_show = remote_prefix;
 -              break;
 -      case FILTER_REFS_DETACHED_HEAD:
 -              desc = to_free = get_head_description();
 -              current = 1;
 -              break;
 -      default:
 -              color = BRANCH_COLOR_PLAIN;
 -              break;
 -      }
 +                      strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
  
 -      c = ' ';
 -      if (current) {
 -              c = '*';
 -              color = BRANCH_COLOR_CURRENT;
 -      }
 +              strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
 +              strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
 +              strbuf_addf(&local, " %s ", obname.buf);
  
 -      strbuf_addf(&name, "%s%s", prefix_to_show, desc);
 -      if (filter->verbose) {
 -              int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
 -              strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
 -                          maxwidth + utf8_compensation, name.buf,
 -                          branch_get_color(BRANCH_COLOR_RESET));
 -      } else
 -              strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
 -                          name.buf, branch_get_color(BRANCH_COLOR_RESET));
 -
 -      if (item->symref) {
 -              const char *symref = item->symref;
 -              if (prefix_to_skip)
 -                      skip_prefix(symref, prefix_to_skip, &symref);
 -              strbuf_addf(&out, " -> %s", symref);
 -      }
 -      else if (filter->verbose)
 -              /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
 -              add_verbose_info(&out, item, filter, desc);
 -      if (column_active(colopts)) {
 -              assert(!filter->verbose && "--column and --verbose are incompatible");
 -              string_list_append(&output, out.buf);
 +              if (filter->verbose > 1)
 +                      strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
 +                                  "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
 +                                  branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
 +              else
 +                      strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
 +
 +              strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
 +                          "%%(if)%%(symref)%%(then) -> %%(symref:short)"
 +                          "%%(else) %s %%(contents:subject)%%(end)",
 +                          branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
 +                          branch_get_color(BRANCH_COLOR_RESET), obname.buf);
 +              strbuf_release(&obname);
        } else {
 -              printf("%s\n", out.buf);
 +              strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 +                          branch_get_color(BRANCH_COLOR_RESET));
 +              strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 +                          branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),
 +                          branch_get_color(BRANCH_COLOR_RESET));
        }
 -      strbuf_release(&name);
 -      strbuf_release(&out);
 -      free(to_free);
 -}
 -
 -static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
 -{
 -      int i, max = 0;
 -      for (i = 0; i < refs->nr; i++) {
 -              struct ref_array_item *it = refs->items[i];
 -              const char *desc = it->refname;
 -              int w;
  
 -              skip_prefix(it->refname, "refs/heads/", &desc);
 -              skip_prefix(it->refname, "refs/remotes/", &desc);
 -              w = utf8_strwidth(desc);
 +      strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
  
 -              if (it->kind == FILTER_REFS_REMOTES)
 -                      w += remote_bonus;
 -              if (w > max)
 -                      max = w;
 -      }
 -      return max;
 +      strbuf_release(&local);
 +      strbuf_release(&remote);
 +      return strbuf_detach(&fmt, NULL);
  }
  
 -static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
 +static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
  {
        int i;
        struct ref_array array;
        int maxwidth = 0;
        const char *remote_prefix = "";
 +      struct strbuf out = STRBUF_INIT;
 +      char *to_free = NULL;
  
        /*
         * If we are listing more than just remote branches,
  
        memset(&array, 0, sizeof(array));
  
 -      verify_ref_format("%(refname)%(symref)");
        filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
  
        if (filter->verbose)
                maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
  
 +      if (!format)
 +              format = to_free = build_format(filter, maxwidth, remote_prefix);
 +      verify_ref_format(format);
 +
        ref_array_sort(sorting, &array);
  
 -      for (i = 0; i < array.nr; i++)
 -              format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
 +      for (i = 0; i < array.nr; i++) {
 +              format_ref_array_item(array.items[i], format, 0, &out);
 +              if (column_active(colopts)) {
 +                      assert(!filter->verbose && "--column and --verbose are incompatible");
 +                       /* format to a string_list to let print_columns() do its job */
 +                      string_list_append(&output, out.buf);
 +              } else {
 +                      fwrite(out.buf, 1, out.len, stdout);
 +                      putchar('\n');
 +              }
 +              strbuf_release(&out);
 +      }
  
        ref_array_clear(&array);
 +      free(to_free);
  }
  
  static void reject_rebase_or_bisect_branch(const char *target)
@@@ -485,15 -582,14 +485,15 @@@ static void rename_branch(const char *o
  
        if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch rename failed"));
 -      strbuf_release(&logmsg);
  
        if (recovery)
                warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
  
 -      if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
 +      if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
  
 +      strbuf_release(&logmsg);
 +
        strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
        strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
        strbuf_release(&newsection);
  }
  
- static const char edit_description[] = "BRANCH_DESCRIPTION";
+ static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
  
  static int edit_branch_description(const char *branch_name)
  {
                      "  %s\n"
                      "Lines starting with '%c' will be stripped.\n"),
                    branch_name, comment_line_char);
-       write_file_buf(git_path(edit_description), buf.buf, buf.len);
+       write_file_buf(edit_description(), buf.buf, buf.len);
        strbuf_reset(&buf);
-       if (launch_editor(git_path(edit_description), &buf, NULL)) {
+       if (launch_editor(edit_description(), &buf, NULL)) {
                strbuf_release(&buf);
                return -1;
        }
@@@ -545,7 -641,6 +545,7 @@@ int cmd_branch(int argc, const char **a
        struct ref_filter filter;
        int icase = 0;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
 +      const char *format = NULL;
  
        struct option options[] = {
                OPT_GROUP(N_("Generic options")),
                OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
                        FILTER_REFS_REMOTES),
                OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
 +              OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
                OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
 +              OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
                OPT__ABBREV(&filter.abbrev),
  
                OPT_GROUP(N_("Specific git-branch actions:")),
                        N_("print only branches of the object"), 0, parse_opt_object_name
                },
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 +              OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
                OPT_END(),
        };
  
 +      setup_ref_filter_porcelain_msg();
 +
        memset(&filter, 0, sizeof(filter));
        filter.kind = FILTER_REFS_BRANCHES;
        filter.abbrev = -1;
  
        track = git_branch_track;
  
 -      head = resolve_refdup("HEAD", 0, head_sha1, NULL);
 +      head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
        if (!head)
                die(_("Failed to resolve HEAD as a valid ref."));
        if (!strcmp(head, "HEAD"))
        if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
                list = 1;
  
 -      if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
 +      if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
 +          filter.no_commit)
                list = 1;
  
        if (!!delete + !!rename + !!new_upstream +
                if (!sorting)
                        sorting = ref_default_sorting();
                sorting->ignore_case = icase;
 -              print_ref_list(&filter, sorting);
 +              print_ref_list(&filter, sorting, format);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                return 0;
diff --combined builtin/commit.c
index ad188fea9ec552c65c4b82c3558977dd6648fe77,bbaa4a0da590ad0ea3d51685f06943c2f658a00e..1d805f5da8abd1cfafc41c5c47fd048b2549fbf4
@@@ -496,7 -496,7 +496,7 @@@ static const char *prepare_index(int ar
  static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
                      struct wt_status *s)
  {
 -      unsigned char sha1[20];
 +      struct object_id oid;
  
        if (s->relative_paths)
                s->prefix = prefix;
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
 -      s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
 +      s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
        if (!s->is_initial)
 -              hashcpy(s->sha1_commit, sha1);
 +              hashcpy(s->sha1_commit, oid.hash);
        s->status_format = status_format;
        s->ignore_submodule_arg = ignore_submodule_arg;
  
@@@ -821,9 -821,9 +821,9 @@@ static int prepare_to_commit(const cha
                                        "If this is not correct, please remove the file\n"
                                        "       %s\n"
                                        "and try again.\n"),
-                               git_path(whence == FROM_MERGE
-                                        ? "MERGE_HEAD"
-                                        : "CHERRY_PICK_HEAD"));
+                               whence == FROM_MERGE ?
+                                       git_path_merge_head() :
+                                       git_path_cherry_pick_head());
                }
  
                fprintf(s->fp, "\n");
                commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
        } else {
 -              unsigned char sha1[20];
 +              struct object_id oid;
                const char *parent = "HEAD";
  
                if (!active_nr && read_cache() < 0)
                if (amend)
                        parent = "HEAD^1";
  
 -              if (get_sha1(parent, sha1)) {
 +              if (get_sha1(parent, oid.hash)) {
                        int i, ita_nr = 0;
  
                        for (i = 0; i < active_nr; i++)
@@@ -1332,7 -1332,7 +1332,7 @@@ int cmd_status(int argc, const char **a
  {
        static struct wt_status s;
        int fd;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
  
        fd = hold_locked_index(&index_lock, 0);
  
 -      s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 +      s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
        if (!s.is_initial)
 -              hashcpy(s.sha1_commit, sha1);
 +              hashcpy(s.sha1_commit, oid.hash);
  
        s.ignore_submodule_arg = ignore_submodule_arg;
        s.status_format = status_format;
  
  static const char *implicit_ident_advice(void)
  {
 -      char *user_config = expand_user_path("~/.gitconfig");
 +      char *user_config = expand_user_path("~/.gitconfig", 0);
        char *xdg_config = xdg_config_home("config");
        int config_exists = file_exists(user_config) || file_exists(xdg_config);
  
  
  }
  
 -static void print_summary(const char *prefix, const unsigned char *sha1,
 +static void print_summary(const char *prefix, const struct object_id *oid,
                          int initial_commit)
  {
        struct rev_info rev;
        struct commit *commit;
        struct strbuf format = STRBUF_INIT;
 -      unsigned char junk_sha1[20];
 +      struct object_id junk_oid;
        const char *head;
        struct pretty_print_context pctx = {0};
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
  
 -      commit = lookup_commit(sha1);
 +      commit = lookup_commit(oid->hash);
        if (!commit)
                die(_("couldn't look up newly created commit"));
        if (parse_commit(commit))
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
 -      head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
 +      head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@@ -1522,8 -1522,8 +1522,8 @@@ static int git_commit_config(const cha
        return git_status_config(k, v, s);
  }
  
 -static int run_rewrite_hook(const unsigned char *oldsha1,
 -                          const unsigned char *newsha1)
 +static int run_rewrite_hook(const struct object_id *oldoid,
 +                          const struct object_id *newoid)
  {
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *argv[3];
        code = start_command(&proc);
        if (code)
                return code;
 -      strbuf_addf(&sb, "%s %s\n", sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
 +      strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
        sigchain_push(SIGPIPE, SIG_IGN);
        write_in_full(proc.in, sb.buf, sb.len);
        close(proc.in);
@@@ -1636,7 -1636,7 +1636,7 @@@ int cmd_commit(int argc, const char **a
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        struct commit_list *parents = NULL;
        struct stat statbuf;
        struct commit *current_head = NULL;
        status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        s.colopts = 0;
  
 -      if (get_sha1("HEAD", sha1))
 +      if (get_sha1("HEAD", oid.hash))
                current_head = NULL;
        else {
 -              current_head = lookup_commit_or_die(sha1, "HEAD");
 +              current_head = lookup_commit_or_die(oid.hash, "HEAD");
                if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
        }
  
        if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
 -                       parents, sha1, author_ident.buf, sign_commit, extra)) {
 +                       parents, oid.hash, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
  
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
 -          ref_transaction_update(transaction, "HEAD", sha1,
 +          ref_transaction_update(transaction, "HEAD", oid.hash,
                                   current_head
                                   ? current_head->object.oid.hash : null_sha1,
                                   0, sb.buf, &err) ||
                cfg = init_copy_notes_for_rewrite("amend");
                if (cfg) {
                        /* we are amending, so current_head is not NULL */
 -                      copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1);
 +                      copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash);
                        finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
 -              run_rewrite_hook(current_head->object.oid.hash, sha1);
 +              run_rewrite_hook(&current_head->object.oid, &oid);
        }
        if (!quiet)
 -              print_summary(prefix, sha1, !current_head);
 +              print_summary(prefix, &oid, !current_head);
  
        strbuf_release(&err);
        return 0;
diff --combined builtin/config.c
index 1b7ed5af0839d25e063e19c9a83b4233a7c2a3ca,07fcc2388b9755f3d69e86b00c90ced7f73041c7..3a554ad50ca60cd327ce94433022f8d42f024f52
@@@ -26,8 -26,7 +26,8 @@@ static int use_global_config, use_syste
  static struct git_config_source given_config_source;
  static int actions, types;
  static int end_null;
 -static int respect_includes = -1;
 +static int respect_includes_opt = -1;
 +static struct config_options config_options;
  static int show_origin;
  
  #define ACTION_GET (1<<0)
@@@ -82,7 -81,7 +82,7 @@@ static struct option builtin_config_opt
        OPT_GROUP(N_("Other")),
        OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
 -      OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
 +      OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
        OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
        OPT_END(),
  };
@@@ -243,7 -242,7 +243,7 @@@ static int get_value(const char *key_, 
        }
  
        git_config_with_options(collect_config, &values,
 -                              &given_config_source, respect_includes);
 +                              &given_config_source, &config_options);
  
        ret = !values.nr;
  
@@@ -321,7 -320,7 +321,7 @@@ static void get_color(const char *var, 
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
 -                              &given_config_source, respect_includes);
 +                              &given_config_source, &config_options);
  
        if (!get_color_found && def_color) {
                if (color_parse(def_color, parsed_color) < 0)
@@@ -353,7 -352,7 +353,7 @@@ static int get_colorbool(const char *va
        get_diff_color_found = -1;
        get_color_ui_found = -1;
        git_config_with_options(git_get_colorbool_config, NULL,
 -                              &given_config_source, respect_includes);
 +                              &given_config_source, &config_options);
  
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
@@@ -442,7 -441,7 +442,7 @@@ static int get_urlmatch(const char *var
        }
  
        git_config_with_options(urlmatch_config_entry, &config,
 -                              &given_config_source, respect_includes);
 +                              &given_config_source, &config_options);
  
        ret = !values.nr;
  
@@@ -503,7 -502,7 +503,7 @@@ int cmd_config(int argc, const char **a
        }
  
        if (use_global_config) {
 -              char *user_config = expand_user_path("~/.gitconfig");
 +              char *user_config = expand_user_path("~/.gitconfig", 0);
                char *xdg_config = xdg_config_home("config");
  
                if (!user_config)
        else if (given_config_source.file) {
                if (!is_absolute_path(given_config_source.file) && prefix)
                        given_config_source.file =
 -                              xstrdup(prefix_filename(prefix,
 -                                                      strlen(prefix),
 -                                                      given_config_source.file));
 +                              prefix_filename(prefix, given_config_source.file);
        }
  
 -      if (respect_includes == -1)
 -              respect_includes = !given_config_source.file;
 +      if (respect_includes_opt == -1)
 +              config_options.respect_includes = !given_config_source.file;
 +      else
 +              config_options.respect_includes = respect_includes_opt;
  
        if (end_null) {
                term = '\0';
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                                            &given_config_source,
 -                                          respect_includes) < 0) {
 +                                          &config_options) < 0) {
                        if (given_config_source.file)
                                die_errno("unable to read config file '%s'",
                                          given_config_source.file);
                if (given_config_source.blob)
                        die("editing blobs is not supported");
                git_config(git_default_config, NULL);
-               config_file = xstrdup(given_config_source.file ?
-                                     given_config_source.file : git_path("config"));
+               config_file = given_config_source.file ?
+                               xstrdup(given_config_source.file) :
+                               git_pathdup("config");
                if (use_global_config) {
                        int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
                        if (fd >= 0) {
diff --combined builtin/pull.c
index d8aa26d8abe4e436f95f4476133967cf17f29988,d8032e838c63e4253d7ac544e52c331ec1751e77..dd1a4a94e41ed31617d31c80e097cbc044b3e3f3
@@@ -330,21 -330,21 +330,21 @@@ static int git_pull_config(const char *
   * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge
   * into merge_heads.
   */
 -static void get_merge_heads(struct sha1_array *merge_heads)
 +static void get_merge_heads(struct oid_array *merge_heads)
  {
-       const char *filename = git_path("FETCH_HEAD");
+       const char *filename = git_path_fetch_head();
        FILE *fp;
        struct strbuf sb = STRBUF_INIT;
 -      unsigned char sha1[GIT_SHA1_RAWSZ];
 +      struct object_id oid;
  
        if (!(fp = fopen(filename, "r")))
                die_errno(_("could not open '%s' for reading"), filename);
        while (strbuf_getline_lf(&sb, fp) != EOF) {
 -              if (get_sha1_hex(sb.buf, sha1))
 +              if (get_oid_hex(sb.buf, &oid))
                        continue;  /* invalid line: does not start with SHA1 */
                if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t"))
                        continue;  /* ref is not-for-merge */
 -              sha1_array_append(merge_heads, sha1);
 +              oid_array_append(merge_heads, &oid);
        }
        fclose(fp);
        strbuf_release(&sb);
@@@ -514,8 -514,8 +514,8 @@@ static int run_fetch(const char *repo, 
  /**
   * "Pulls into void" by branching off merge_head.
   */
 -static int pull_into_void(const unsigned char *merge_head,
 -              const unsigned char *curr_head)
 +static int pull_into_void(const struct object_id *merge_head,
 +              const struct object_id *curr_head)
  {
        /*
         * Two-way merge: we treat the index as based on an empty tree,
         * index/worktree changes that the user already made on the unborn
         * branch.
         */
 -      if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0))
 +      if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head->hash, 0))
                return 1;
  
 -      if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
 +      if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR))
                return 1;
  
        return 0;
@@@ -647,7 -647,7 +647,7 @@@ static const char *get_tracking_branch(
   * current branch forked from its remote tracking branch. Returns 0 on success,
   * -1 on failure.
   */
 -static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
 +static int get_rebase_fork_point(struct object_id *fork_point, const char *repo,
                const char *refspec)
  {
        int ret;
        if (ret)
                goto cleanup;
  
 -      ret = get_sha1_hex(sb.buf, fork_point);
 +      ret = get_oid_hex(sb.buf, fork_point);
        if (ret)
                goto cleanup;
  
@@@ -691,24 -691,24 +691,24 @@@ cleanup
   * Sets merge_base to the octopus merge base of curr_head, merge_head and
   * fork_point. Returns 0 if a merge base is found, 1 otherwise.
   */
 -static int get_octopus_merge_base(unsigned char *merge_base,
 -              const unsigned char *curr_head,
 -              const unsigned char *merge_head,
 -              const unsigned char *fork_point)
 +static int get_octopus_merge_base(struct object_id *merge_base,
 +              const struct object_id *curr_head,
 +              const struct object_id *merge_head,
 +              const struct object_id *fork_point)
  {
        struct commit_list *revs = NULL, *result;
  
 -      commit_list_insert(lookup_commit_reference(curr_head), &revs);
 -      commit_list_insert(lookup_commit_reference(merge_head), &revs);
 -      if (!is_null_sha1(fork_point))
 -              commit_list_insert(lookup_commit_reference(fork_point), &revs);
 +      commit_list_insert(lookup_commit_reference(curr_head->hash), &revs);
 +      commit_list_insert(lookup_commit_reference(merge_head->hash), &revs);
 +      if (!is_null_oid(fork_point))
 +              commit_list_insert(lookup_commit_reference(fork_point->hash), &revs);
  
        result = reduce_heads(get_octopus_merge_bases(revs));
        free_commit_list(revs);
        if (!result)
                return 1;
  
 -      hashcpy(merge_base, result->item->object.oid.hash);
 +      oidcpy(merge_base, &result->item->object.oid);
        return 0;
  }
  
   * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
   * appropriate arguments and returns its exit status.
   */
 -static int run_rebase(const unsigned char *curr_head,
 -              const unsigned char *merge_head,
 -              const unsigned char *fork_point)
 +static int run_rebase(const struct object_id *curr_head,
 +              const struct object_id *merge_head,
 +              const struct object_id *fork_point)
  {
        int ret;
 -      unsigned char oct_merge_base[GIT_SHA1_RAWSZ];
 +      struct object_id oct_merge_base;
        struct argv_array args = ARGV_ARRAY_INIT;
  
 -      if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point))
 -              if (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point))
 +      if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point))
 +              if (!is_null_oid(fork_point) && !oidcmp(&oct_merge_base, fork_point))
                        fork_point = NULL;
  
        argv_array_push(&args, "rebase");
                warning(_("ignoring --verify-signatures for rebase"));
  
        argv_array_push(&args, "--onto");
 -      argv_array_push(&args, sha1_to_hex(merge_head));
 +      argv_array_push(&args, oid_to_hex(merge_head));
  
 -      if (fork_point && !is_null_sha1(fork_point))
 -              argv_array_push(&args, sha1_to_hex(fork_point));
 +      if (fork_point && !is_null_oid(fork_point))
 +              argv_array_push(&args, oid_to_hex(fork_point));
        else
 -              argv_array_push(&args, sha1_to_hex(merge_head));
 +              argv_array_push(&args, oid_to_hex(merge_head));
  
        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
        argv_array_clear(&args);
  int cmd_pull(int argc, const char **argv, const char *prefix)
  {
        const char *repo, **refspecs;
 -      struct sha1_array merge_heads = SHA1_ARRAY_INIT;
 -      unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];
 -      unsigned char rebase_fork_point[GIT_SHA1_RAWSZ];
 +      struct oid_array merge_heads = OID_ARRAY_INIT;
 +      struct object_id orig_head, curr_head;
 +      struct object_id rebase_fork_point;
  
        if (!getenv("GIT_REFLOG_ACTION"))
                set_reflog_message(argc, argv);
        if (read_cache_unmerged())
                die_resolve_conflict("pull");
  
-       if (file_exists(git_path("MERGE_HEAD")))
+       if (file_exists(git_path_merge_head()))
                die_conclude_merge();
  
 -      if (get_sha1("HEAD", orig_head))
 -              hashclr(orig_head);
 +      if (get_oid("HEAD", &orig_head))
 +              oidclr(&orig_head);
  
        if (!opt_rebase && opt_autostash != -1)
                die(_("--[no-]autostash option is only valid with --rebase."));
                if (opt_autostash != -1)
                        autostash = opt_autostash;
  
 -              if (is_null_sha1(orig_head) && !is_cache_unborn())
 +              if (is_null_oid(&orig_head) && !is_cache_unborn())
                        die(_("Updating an unborn branch with changes added to the index."));
  
                if (!autostash)
                        require_clean_work_tree(N_("pull with rebase"),
                                _("please commit or stash them."), 1, 0);
  
 -              if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
 -                      hashclr(rebase_fork_point);
 +              if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
 +                      oidclr(&rebase_fork_point);
        }
  
        if (run_fetch(repo, refspecs))
        if (opt_dry_run)
                return 0;
  
 -      if (get_sha1("HEAD", curr_head))
 -              hashclr(curr_head);
 +      if (get_oid("HEAD", &curr_head))
 +              oidclr(&curr_head);
  
 -      if (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) &&
 -                      hashcmp(orig_head, curr_head)) {
 +      if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
 +                      oidcmp(&orig_head, &curr_head)) {
                /*
                 * The fetch involved updating the current branch.
                 *
  
                warning(_("fetch updated the current branch head.\n"
                        "fast-forwarding your working tree from\n"
 -                      "commit %s."), sha1_to_hex(orig_head));
 +                      "commit %s."), oid_to_hex(&orig_head));
  
 -              if (checkout_fast_forward(orig_head, curr_head, 0))
 +              if (checkout_fast_forward(orig_head.hash, curr_head.hash, 0))
                        die(_("Cannot fast-forward your working tree.\n"
                                "After making sure that you saved anything precious from\n"
                                "$ git diff %s\n"
                                "output, run\n"
                                "$ git reset --hard\n"
 -                              "to recover."), sha1_to_hex(orig_head));
 +                              "to recover."), oid_to_hex(&orig_head));
        }
  
        get_merge_heads(&merge_heads);
        if (!merge_heads.nr)
                die_no_merge_candidates(repo, refspecs);
  
 -      if (is_null_sha1(orig_head)) {
 +      if (is_null_oid(&orig_head)) {
                if (merge_heads.nr > 1)
                        die(_("Cannot merge multiple branches into empty head."));
 -              return pull_into_void(*merge_heads.sha1, curr_head);
 +              return pull_into_void(merge_heads.oid, &curr_head);
        }
        if (opt_rebase && merge_heads.nr > 1)
                die(_("Cannot rebase onto multiple branches."));
                struct commit_list *list = NULL;
                struct commit *merge_head, *head;
  
 -              head = lookup_commit_reference(orig_head);
 +              head = lookup_commit_reference(orig_head.hash);
                commit_list_insert(head, &list);
 -              merge_head = lookup_commit_reference(merge_heads.sha1[0]);
 +              merge_head = lookup_commit_reference(merge_heads.oid[0].hash);
                if (is_descendant_of(merge_head, list)) {
                        /* we can fast-forward this without invoking rebase */
                        opt_ff = "--ff-only";
                        return run_merge();
                }
 -              return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);
 +              return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
        } else {
                return run_merge();
        }
diff --combined builtin/worktree.c
index 9993ded41aaaaa9a9291bf51b9949a97418a5e76,9cceaf451963987f361cd5e3d81b930004f079b9..57caa0855e0e8078d6f100f41cab76aac7784887
@@@ -106,8 -106,7 +106,7 @@@ static void prune_worktrees(void
                        printf("%s\n", reason.buf);
                if (show_only)
                        continue;
-               strbuf_reset(&path);
-               strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
+               git_path_buf(&path, "worktrees/%s", d->d_name);
                ret = remove_dir_recursively(&path, 0);
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
@@@ -215,8 -214,7 +214,7 @@@ static int add_worktree(const char *pat
        }
  
        name = worktree_basename(path, &len);
-       strbuf_addstr(&sb_repo,
-                     git_path("worktrees/%.*s", (int)(path + len - name), name));
+       git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
        len = sb_repo.len;
        if (safe_create_leading_directories_const(sb_repo.buf))
                die_errno(_("could not create leading directories of '%s'"),
@@@ -318,8 -316,7 +316,8 @@@ static int add(int ac, const char **av
  {
        struct add_opts opts;
        const char *new_branch_force = NULL;
 -      const char *path, *branch;
 +      char *path;
 +      const char *branch;
        struct option options[] = {
                OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
        if (ac < 1 || ac > 2)
                usage_with_options(worktree_usage, options);
  
 -      path = prefix_filename(prefix, strlen(prefix), av[0]);
 +      path = prefix_filename(prefix, av[0]);
        branch = ac < 2 ? "HEAD" : av[1];
  
        if (!strcmp(branch, "-"))
diff --combined fast-import.c
index 1ea3a08609bd8ece7a22ce33c78a0a798304375f,d2ef8a87295482059d2a8af7c69b3553817b5a07..cf58f875b889eb0360df1f58cacf4f4017c6c10f
@@@ -890,15 -890,14 +890,15 @@@ static struct tree_content *dup_tree_co
  
  static void start_packfile(void)
  {
 -      static char tmp_file[PATH_MAX];
 +      struct strbuf tmp_file = STRBUF_INIT;
        struct packed_git *p;
        struct pack_header hdr;
        int pack_fd;
  
 -      pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
 -                            "pack/tmp_pack_XXXXXX");
 -      FLEX_ALLOC_STR(p, pack_name, tmp_file);
 +      pack_fd = odb_mkstemp(&tmp_file, "pack/tmp_pack_XXXXXX");
 +      FLEX_ALLOC_STR(p, pack_name, tmp_file.buf);
 +      strbuf_release(&tmp_file);
 +
        p->pack_fd = pack_fd;
        p->do_not_close = 1;
        pack_file = sha1fd(pack_fd, p->pack_name);
@@@ -1174,8 -1173,7 +1174,8 @@@ static int store_object
                delta_count_by_type[type]++;
                e->depth = last->depth + 1;
  
 -              hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
 +              hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr),
 +                                                    OBJ_OFS_DELTA, deltalen);
                sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
  
                pack_size += sizeof(hdr) - pos;
        } else {
                e->depth = 0;
 -              hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
 +              hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr),
 +                                                    type, dat->len);
                sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
        }
@@@ -1240,7 -1237,9 +1240,7 @@@ static void stream_blob(uintmax_t len, 
        sha1file_checkpoint(pack_file, &checkpoint);
        offset = checkpoint.offset;
  
 -      hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
 -      if (out_sz <= hdrlen)
 -              die("impossibly large object header");
 +      hdrlen = xsnprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
  
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, out_buf, hdrlen);
  
        git_deflate_init(&s, pack_compression_level);
  
 -      hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
 -      if (out_sz <= hdrlen)
 -              die("impossibly large object header");
 +      hdrlen = encode_in_pack_object_header(out_buf, out_sz, OBJ_BLOB, len);
  
        s.next_out = out_buf + hdrlen;
        s.avail_out = out_sz - hdrlen;
@@@ -1751,7 -1752,7 +1751,7 @@@ static int update_branch(struct branch 
  
        if (is_null_sha1(b->sha1)) {
                if (b->delete)
 -                      delete_ref(b->name, NULL, 0);
 +                      delete_ref(NULL, b->name, NULL, 0);
                return 0;
        }
        if (read_ref(b->name, old_sha1))
@@@ -3002,7 -3003,7 +3002,7 @@@ static void parse_get_mark(const char *
        if (!oe)
                die("Unknown mark: %s", command_buf.buf);
  
 -      snprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1));
 +      xsnprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1));
        cat_blob_write(output, 41);
  }
  
@@@ -3202,7 -3203,7 +3202,7 @@@ static char* make_fast_import_path(cons
  {
        if (!relative_marks_paths || is_absolute_path(path))
                return xstrdup(path);
-       return xstrdup(git_path("info/fast-import/%s", path));
+       return git_pathdup("info/fast-import/%s", path);
  }
  
  static void option_import_marks(const char *marks,
diff --combined sequencer.c
index 77afecaebf0980bec4f938b15bd08ae35ba09d83,f52c30fbe2b9ddc61eaf23c7a9a8b3d6d7e0d1a0..130cc868e511e646badde9ebc0894602e27ea37b
@@@ -602,12 -602,6 +602,12 @@@ N_("you have staged changes in your wor
  "\n"
  "  git rebase --continue\n");
  
 +#define ALLOW_EMPTY (1<<0)
 +#define EDIT_MSG    (1<<1)
 +#define AMEND_MSG   (1<<2)
 +#define CLEANUP_MSG (1<<3)
 +#define VERIFY_MSG  (1<<4)
 +
  /*
   * If we are cherry-pick, and if the merge did not result in
   * hand-editing, we will hit this commit and inherit the original
   * author metadata.
   */
  static int run_git_commit(const char *defmsg, struct replay_opts *opts,
 -                        int allow_empty, int edit, int amend,
 -                        int cleanup_commit_message)
 +                        unsigned int flags)
  {
        struct child_process cmd = CHILD_PROCESS_INIT;
        const char *value;
        cmd.git_cmd = 1;
  
        if (is_rebase_i(opts)) {
 -              if (!edit) {
 +              if (!(flags & EDIT_MSG)) {
                        cmd.stdout_to_stderr = 1;
                        cmd.err = -1;
                }
        }
  
        argv_array_push(&cmd.args, "commit");
 -      argv_array_push(&cmd.args, "-n");
  
 -      if (amend)
 +      if (!(flags & VERIFY_MSG))
 +              argv_array_push(&cmd.args, "-n");
 +      if ((flags & AMEND_MSG))
                argv_array_push(&cmd.args, "--amend");
        if (opts->gpg_sign)
                argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
                argv_array_push(&cmd.args, "-s");
        if (defmsg)
                argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
 -      if (cleanup_commit_message)
 +      if ((flags & CLEANUP_MSG))
                argv_array_push(&cmd.args, "--cleanup=strip");
 -      if (edit)
 +      if ((flags & EDIT_MSG))
                argv_array_push(&cmd.args, "-e");
 -      else if (!cleanup_commit_message &&
 +      else if (!(flags & CLEANUP_MSG) &&
                 !opts->signoff && !opts->record_origin &&
                 git_config_get_value("commit.cleanup", &value))
                argv_array_push(&cmd.args, "--cleanup=verbatim");
  
 -      if (allow_empty)
 +      if ((flags & ALLOW_EMPTY))
                argv_array_push(&cmd.args, "--allow-empty");
  
        if (opts->allow_empty_message)
@@@ -932,14 -926,14 +932,14 @@@ static void record_in_rewritten(struct 
  static int do_pick_commit(enum todo_command command, struct commit *commit,
                struct replay_opts *opts, int final_fixup)
  {
 -      int edit = opts->edit, cleanup_commit_message = 0;
 -      const char *msg_file = edit ? NULL : git_path_merge_msg();
 +      unsigned int flags = opts->edit ? EDIT_MSG : 0;
 +      const char *msg_file = opts->edit ? NULL : git_path_merge_msg();
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
        struct commit_message msg = { NULL, NULL, NULL, NULL };
        struct strbuf msgbuf = STRBUF_INIT;
 -      int res, unborn = 0, amend = 0, allow = 0;
 +      int res, unborn = 0, allow;
  
        if (opts->no_commit) {
                /*
                        opts);
                if (res || command != TODO_REWORD)
                        goto leave;
 -              edit = amend = 1;
 +              flags |= EDIT_MSG | AMEND_MSG;
 +              if (command == TODO_REWORD)
 +                      flags |= VERIFY_MSG;
                msg_file = NULL;
                goto fast_forward_edit;
        }
        }
  
        if (command == TODO_REWORD)
 -              edit = 1;
 +              flags |= EDIT_MSG | VERIFY_MSG;
        else if (is_fixup(command)) {
                if (update_squash_messages(command, commit, opts))
                        return -1;
 -              amend = 1;
 +              flags |= AMEND_MSG;
                if (!final_fixup)
                        msg_file = rebase_path_squash_msg();
                else if (file_exists(rebase_path_fixup_msg())) {
 -                      cleanup_commit_message = 1;
 +                      flags |= CLEANUP_MSG;
                        msg_file = rebase_path_fixup_msg();
                } else {
-                       const char *dest = git_path("SQUASH_MSG");
+                       const char *dest = git_path_squash_msg();
                        unlink(dest);
                        if (copy_file(dest, rebase_path_squash_msg(), 0666))
                                return error(_("could not rename '%s' to '%s'"),
                                             rebase_path_squash_msg(), dest);
-                       unlink(git_path("MERGE_MSG"));
+                       unlink(git_path_merge_msg());
                        msg_file = dest;
 -                      edit = 1;
 +                      flags |= EDIT_MSG;
                }
        }
  
        if (allow < 0) {
                res = allow;
                goto leave;
 -      }
 +      } else if (allow)
 +              flags |= ALLOW_EMPTY;
        if (!opts->no_commit)
  fast_forward_edit:
 -              res = run_git_commit(msg_file, opts, allow, edit, amend,
 -                                   cleanup_commit_message);
 +              res = run_git_commit(msg_file, opts, flags);
  
        if (!res && final_fixup) {
                unlink(rebase_path_fixup_msg());
@@@ -1820,10 -1812,10 +1820,10 @@@ static int error_failed_squash(struct c
                return error(_("could not rename '%s' to '%s'"),
                        rebase_path_squash_msg(), rebase_path_message());
        unlink(rebase_path_fixup_msg());
-       unlink(git_path("MERGE_MSG"));
-       if (copy_file(git_path("MERGE_MSG"), rebase_path_message(), 0666))
+       unlink(git_path_merge_msg());
+       if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
                return error(_("could not copy '%s' to '%s'"),
-                            rebase_path_message(), git_path("MERGE_MSG"));
+                            rebase_path_message(), git_path_merge_msg());
        return error_with_patch(commit, subject, subject_len, opts, 1, 0);
  }
  
@@@ -2005,8 -1997,7 +2005,8 @@@ static int pick_commits(struct todo_lis
                        if (item->command == TODO_EDIT) {
                                struct commit *commit = item->commit;
                                if (!res)
 -                                      warning(_("stopped at %s... %.*s"),
 +                                      fprintf(stderr,
 +                                              _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
                                                item->arg_len, item->arg);
                                return error_with_patch(commit,
@@@ -2162,12 -2153,12 +2162,12 @@@ static int continue_single_pick(void
  
  static int commit_staged_changes(struct replay_opts *opts)
  {
 -      int amend = 0;
 +      unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
  
        if (has_unstaged_changes(1))
                return error(_("cannot rebase: You have unstaged changes."));
        if (!has_uncommitted_changes(0)) {
-               const char *cherry_pick_head = git_path("CHERRY_PICK_HEAD");
+               const char *cherry_pick_head = git_path_cherry_pick_head();
  
                if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
                        return error(_("could not remove CHERRY_PICK_HEAD"));
                                       "--continue' again."));
  
                strbuf_release(&rev);
 -              amend = 1;
 +              flags |= AMEND_MSG;
        }
  
 -      if (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))
 +      if (run_git_commit(rebase_path_message(), opts, flags))
                return error(_("could not commit staged changes."));
        unlink(rebase_path_amend());
        return 0;