Merge branch 'js/am-3-merge-recursive-direct'
authorJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:20 +0000 (12:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:20 +0000 (12:33 -0700)
"git am -3" calls "git merge-recursive" when it needs to fall back
to a three-way merge; this call has been turned into an internal
subroutine call instead of spawning a separate subprocess.

* js/am-3-merge-recursive-direct:
merge-recursive: flush output buffer even when erroring out
merge_trees(): ensure that the callers release output buffer
merge-recursive: offer an option to retain the output in 'obuf'
merge-recursive: write the commit title in one go
merge-recursive: flush output buffer before printing error messages
am -3: use merge_recursive() directly again
merge-recursive: switch to returning errors instead of dying
merge-recursive: handle return values indicating errors
merge-recursive: allow write_tree_from_memory() to error out
merge-recursive: avoid returning a wholesale struct
merge_recursive: abort properly upon errors
prepare the builtins for a libified merge_recursive()
merge-recursive: clarify code in was_tracked()
die(_("BUG")): avoid translating bug messages
die("bug"): report bugs consistently
t5520: verify that `pull --rebase` shows the helpful advice when failing

1  2 
builtin/am.c
grep.c
sequencer.c
sha1_file.c
transport.c
wt-status.c
diff --combined builtin/am.c
index 8aa9b5b93666937234224d689ac16ce235ba9b90,cfb79ea906f9c9911244845af2a984afe9a73843..739b34dcf25a1355246bed436ec7fdb4723d7ca7
@@@ -1578,48 -1578,19 +1578,19 @@@ static int build_fake_ancestor(const st
        return 0;
  }
  
- /**
-  * Do the three-way merge using fake ancestor, their tree constructed
-  * from the fake ancestor and the postimage of the patch, and our
-  * state.
-  */
- static int run_fallback_merge_recursive(const struct am_state *state,
-                                       unsigned char *orig_tree,
-                                       unsigned char *our_tree,
-                                       unsigned char *their_tree)
- {
-       struct child_process cp = CHILD_PROCESS_INIT;
-       int status;
-       cp.git_cmd = 1;
-       argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
-                        sha1_to_hex(their_tree), linelen(state->msg), state->msg);
-       if (state->quiet)
-               argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
-       argv_array_push(&cp.args, "merge-recursive");
-       argv_array_push(&cp.args, sha1_to_hex(orig_tree));
-       argv_array_push(&cp.args, "--");
-       argv_array_push(&cp.args, sha1_to_hex(our_tree));
-       argv_array_push(&cp.args, sha1_to_hex(their_tree));
-       status = run_command(&cp) ? (-1) : 0;
-       discard_cache();
-       read_cache();
-       return status;
- }
  /**
   * Attempt a threeway merge, using index_path as the temporary index.
   */
  static int fall_back_threeway(const struct am_state *state, const char *index_path)
  {
-       unsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ],
-                     our_tree[GIT_SHA1_RAWSZ];
+       struct object_id orig_tree, their_tree, our_tree;
+       const struct object_id *bases[1] = { &orig_tree };
+       struct merge_options o;
+       struct commit *result;
+       char *their_tree_name;
  
-       if (get_sha1("HEAD", our_tree) < 0)
-               hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+       if (get_oid("HEAD", &our_tree) < 0)
+               hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
  
        if (build_fake_ancestor(state, index_path))
                return error("could not build fake ancestor");
        discard_cache();
        read_cache_from(index_path);
  
-       if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL))
+       if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))
                return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
  
        say(state, stdout, _("Using index info to reconstruct a base tree..."));
                init_revisions(&rev_info, NULL);
                rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
                diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
-               add_pending_sha1(&rev_info, "HEAD", our_tree, 0);
+               add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
                diff_setup_done(&rev_info.diffopt);
                run_diff_index(&rev_info, 1);
        }
                return error(_("Did you hand edit your patch?\n"
                                "It does not apply to blobs recorded in its index."));
  
-       if (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL))
+       if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
                return error("could not write tree");
  
        say(state, stdout, _("Falling back to patching base and 3-way merge..."));
         * changes.
         */
  
-       if (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) {
+       init_merge_options(&o);
+       o.branch1 = "HEAD";
+       their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+       o.branch2 = their_tree_name;
+       if (state->quiet)
+               o.verbosity = 0;
+       if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
                rerere(state->allow_rerere_autoupdate);
+               free(their_tree_name);
                return error(_("Failed to merge in the changes."));
        }
  
+       free(their_tree_name);
        return 0;
  }
  
@@@ -1840,8 -1822,6 +1822,8 @@@ static void am_run(struct am_state *sta
                const char *mail = am_path(state, msgnum(state));
                int apply_status;
  
 +              reset_ident_date();
 +
                if (!file_exists(mail))
                        goto next;
  
diff --combined grep.c
index 58d599e6475cd1472c824743ec60b00361a396c3,22cbb73f26c174939e51bae469f5fbb9e7b2417d..d7d00b87cb2a28332ccad4ea4ff676e3f2bc2611
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -163,7 -163,17 +163,7 @@@ void grep_init(struct grep_opt *opt, co
        color_set(opt->color_sep, def->color_sep);
  }
  
 -void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
 -{
 -      if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
 -              grep_set_pattern_type_option(pattern_type, opt);
 -      else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
 -              grep_set_pattern_type_option(opt->pattern_type_option, opt);
 -      else if (opt->extended_regexp_option)
 -              grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
 -}
 -
 -void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
 +static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
  {
        switch (pattern_type) {
        case GREP_PATTERN_TYPE_UNSPECIFIED:
        }
  }
  
 +void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
 +{
 +      if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
 +              grep_set_pattern_type_option(pattern_type, opt);
 +      else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
 +              grep_set_pattern_type_option(opt->pattern_type_option, opt);
 +      else if (opt->extended_regexp_option)
 +              grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
 +}
 +
  static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
                                        const char *origin, int no,
                                        enum grep_pat_token t,
@@@ -693,10 -693,10 +693,10 @@@ static struct grep_expr *prep_header_pa
  
        for (p = opt->header_list; p; p = p->next) {
                if (p->token != GREP_PATTERN_HEAD)
-                       die("bug: a non-header pattern in grep header list.");
+                       die("BUG: a non-header pattern in grep header list.");
                if (p->field < GREP_HEADER_FIELD_MIN ||
                    GREP_HEADER_FIELD_MAX <= p->field)
-                       die("bug: unknown header field %d", p->field);
+                       die("BUG: unknown header field %d", p->field);
                compile_regexp(p, opt);
        }
  
  
                h = compile_pattern_atom(&pp);
                if (!h || pp != p->next)
-                       die("bug: malformed header expr");
+                       die("BUG: malformed header expr");
                if (!header_group[p->field]) {
                        header_group[p->field] = h;
                        continue;
@@@ -1514,7 -1514,7 +1514,7 @@@ static int grep_source_1(struct grep_op
                case GREP_BINARY_TEXT:
                        break;
                default:
-                       die("bug: unknown binary handling mode");
+                       die("BUG: unknown binary handling mode");
                }
        }
  
diff --combined sequencer.c
index 7b1eb146455b4b36a1e52298e9fc2405f0a30776,ec50519df92eb50f74db8e7db05f35d0ebb913b2..2e9c7d0f03734c20f1fb9d5c6214a7960d80b112
@@@ -112,7 -112,7 +112,7 @@@ static void remove_sequencer_state(void
  {
        struct strbuf seq_dir = STRBUF_INIT;
  
 -      strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
 +      strbuf_addstr(&seq_dir, git_path(SEQ_DIR));
        remove_dir_recursively(&seq_dir, 0);
        strbuf_release(&seq_dir);
  }
@@@ -293,6 -293,9 +293,9 @@@ static int do_recursive_merge(struct co
        clean = merge_trees(&o,
                            head_tree,
                            next_tree, base_tree, &result);
+       strbuf_release(&o.obuf);
+       if (clean < 0)
+               return clean;
  
        if (active_cache_changed &&
            write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
@@@ -559,6 -562,8 +562,8 @@@ static int do_pick_commit(struct commi
        if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
                res = do_recursive_merge(base, next, base_label, next_label,
                                         head, &msgbuf, opts);
+               if (res < 0)
+                       return res;
                write_message(&msgbuf, git_path_merge_msg());
        } else {
                struct commit_list *common = NULL;
diff --combined sha1_file.c
index 3066b5f71c1eb2916081967f396c8427e3e75406,5085fe03f1461fc5620ad57e5cbb8d87e1ff44cc..02940f1920300c290e922174159fd7177844adda
@@@ -23,7 -23,6 +23,7 @@@
  #include "bulk-checkin.h"
  #include "streaming.h"
  #include "dir.h"
 +#include "mru.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -60,6 -59,14 +60,6 @@@ static struct cached_object empty_tree 
        0
  };
  
 -/*
 - * A pointer to the last packed_git in which an object was found.
 - * When an object is sought, we look in this packfile first, because
 - * objects that are looked up at similar times are often in the same
 - * packfile as one another.
 - */
 -static struct packed_git *last_found_pack;
 -
  static struct cached_object *find_cached_object(const unsigned char *sha1)
  {
        int i;
@@@ -515,9 -522,6 +515,9 @@@ static size_t peak_pack_mapped
  static size_t pack_mapped;
  struct packed_git *packed_git;
  
 +static struct mru packed_git_mru_storage;
 +struct mru *packed_git_mru = &packed_git_mru_storage;
 +
  void pack_report(void)
  {
        fprintf(stderr,
@@@ -791,7 -795,7 +791,7 @@@ void close_all_packs(void
  
        for (p = packed_git; p; p = p->next)
                if (p->do_not_close)
-                       die("BUG! Want to close pack marked 'do-not-close'");
+                       die("BUG: want to close pack marked 'do-not-close'");
                else
                        close_pack(p);
  }
@@@ -887,6 -891,36 +887,6 @@@ void close_pack_index(struct packed_gi
        }
  }
  
 -/*
 - * This is used by git-repack in case a newly created pack happens to
 - * contain the same set of objects as an existing one.  In that case
 - * the resulting file might be different even if its name would be the
 - * same.  It is best to close any reference to the old pack before it is
 - * replaced on disk.  Of course no index pointers or windows for given pack
 - * must subsist at this point.  If ever objects from this pack are requested
 - * again, the new version of the pack will be reinitialized through
 - * reprepare_packed_git().
 - */
 -void free_pack_by_name(const char *pack_name)
 -{
 -      struct packed_git *p, **pp = &packed_git;
 -
 -      while (*pp) {
 -              p = *pp;
 -              if (strcmp(pack_name, p->pack_name) == 0) {
 -                      clear_delta_base_cache();
 -                      close_pack(p);
 -                      free(p->bad_object_sha1);
 -                      *pp = p->next;
 -                      if (last_found_pack == p)
 -                              last_found_pack = NULL;
 -                      free(p);
 -                      return;
 -              }
 -              pp = &p->next;
 -      }
 -}
 -
  static unsigned int get_max_fd_limit(void)
  {
  #ifdef RLIMIT_NOFILE
@@@ -1351,15 -1385,6 +1351,15 @@@ static void rearrange_packed_git(void
        free(ary);
  }
  
 +static void prepare_packed_git_mru(void)
 +{
 +      struct packed_git *p;
 +
 +      mru_clear(packed_git_mru);
 +      for (p = packed_git; p; p = p->next)
 +              mru_append(packed_git_mru, p);
 +}
 +
  static int prepare_packed_git_run_once = 0;
  void prepare_packed_git(void)
  {
                alt->name[-1] = '/';
        }
        rearrange_packed_git();
 +      prepare_packed_git_mru();
        prepare_packed_git_run_once = 1;
  }
  
@@@ -2257,7 -2281,7 +2257,7 @@@ void *unpack_entry(struct packed_git *p
  
                if (do_check_packed_object_crc && p->index_version > 1) {
                        struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 -                      unsigned long len = revidx[1].offset - obj_offset;
 +                      off_t len = revidx[1].offset - obj_offset;
                        if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
                                const unsigned char *sha1 =
                                        nth_packed_object_sha1(p, revidx->nr);
        case OBJ_OFS_DELTA:
        case OBJ_REF_DELTA:
                if (data)
-                       die("BUG in unpack_entry: left loop at a valid delta");
+                       die("BUG: unpack_entry: left loop at a valid delta");
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
@@@ -2580,15 -2604,21 +2580,15 @@@ static int fill_pack_entry(const unsign
   */
  static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
  {
 -      struct packed_git *p;
 +      struct mru_entry *p;
  
        prepare_packed_git();
        if (!packed_git)
                return 0;
  
 -      if (last_found_pack && fill_pack_entry(sha1, e, last_found_pack))
 -              return 1;
 -
 -      for (p = packed_git; p; p = p->next) {
 -              if (p == last_found_pack)
 -                      continue; /* we already checked this one */
 -
 -              if (fill_pack_entry(sha1, e, p)) {
 -                      last_found_pack = p;
 +      for (p = packed_git_mru->head; p; p = p->next) {
 +              if (fill_pack_entry(sha1, e, p->item)) {
 +                      mru_mark(packed_git_mru, p);
                        return 1;
                }
        }
diff --combined transport.c
index 4ba48b05960203f9c5ed3360e99382bc945ae7e4,04d945425a495b9e230a77fc47fcebd482cd97b4..c5772a14eeec3d5651796c8051443dd76583c05d
@@@ -513,7 -513,6 +513,7 @@@ static int git_transport_push(struct tr
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
        args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
        args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
 +      args.push_options = transport->push_options;
        args.url = transport->url;
  
        if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
@@@ -567,7 -566,7 +567,7 @@@ void transport_take_over(struct transpo
        struct git_transport_data *data;
  
        if (!transport->smart_options)
-               die("Bug detected: Taking over transport requires non-NULL "
+               die("BUG: taking over transport requires non-NULL "
                    "smart_options field.");
  
        data = xcalloc(1, sizeof(*data));
diff --combined wt-status.c
index 3175ec6ddb6d6e40f8e2cfcbbb0b4acf5f0b5e98,f8ae0c2dd05d4997d617c09a14b09c3ba347f587..6225a2d89f2c38bea6ed64295a28e7e07d19522c
@@@ -263,7 -263,7 +263,7 @@@ static const char *wt_status_unmerged_s
        case 7:
                return _("both modified:");
        default:
-               die("bug: unhandled unmerged status %x", stagemask);
+               die("BUG: unhandled unmerged status %x", stagemask);
        }
  }
  
@@@ -388,7 -388,7 +388,7 @@@ static void wt_status_print_change_data
        status_printf(s, color(WT_STATUS_HEADER, s), "\t");
        what = wt_status_diff_status_string(status);
        if (!what)
-               die("bug: unhandled diff status %c", status);
+               die("BUG: unhandled diff status %c", status);
        len = label_width - utf8_strwidth(what);
        assert(len >= 0);
        if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)
@@@ -948,12 -948,9 +948,12 @@@ static void show_merge_in_progress(stru
  {
        if (has_unmerged(s)) {
                status_printf_ln(s, color, _("You have unmerged paths."));
 -              if (s->hints)
 +              if (s->hints) {
                        status_printf_ln(s, color,
 -                              _("  (fix conflicts and run \"git commit\")"));
 +                                       _("  (fix conflicts and run \"git commit\")"));
 +                      status_printf_ln(s, color,
 +                                       _("  (use \"git merge --abort\" to abort the merge)"));
 +              }
        } else {
                s-> commitable = 1;
                status_printf_ln(s, color,