Merge branch 'nd/ignore-might-be-precious'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Dec 2011 06:55:07 +0000 (22:55 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Dec 2011 06:55:07 +0000 (22:55 -0800)
* nd/ignore-might-be-precious:
checkout,merge: disallow overwriting ignored files with --no-overwrite-ignore

1  2 
builtin/checkout.c
builtin/merge.c
diff --combined builtin/checkout.c
index 3f5d9b629a515d1531ff801fabdb9a72246d5d5c,5f9474d1efaed83c8e4ffaff39cb641ce91b6f79..fdd2e0b9d8fc6781810fbf17db956997265f99ea
@@@ -34,6 -34,7 +34,7 @@@ struct checkout_opts 
        int force_detach;
        int writeout_stage;
        int writeout_error;
+       int overwrite_ignore;
  
        /* not set by parse_options */
        int branch_exists;
@@@ -288,7 -289,7 +289,7 @@@ static int checkout_paths(struct tree *
            commit_locked_index(lock_file))
                die(_("unable to write new index file"));
  
 -      resolve_ref("HEAD", rev, 0, &flag);
 +      read_ref_full("HEAD", rev, 0, &flag);
        head = lookup_commit_reference_gently(rev, 1);
  
        errs |= post_checkout_hook(head, head, 0);
@@@ -409,9 -410,11 +410,11 @@@ static int merge_working_tree(struct ch
                topts.gently = opts->merge && old->commit;
                topts.verbose_update = !opts->quiet;
                topts.fn = twoway_merge;
-               topts.dir = xcalloc(1, sizeof(*topts.dir));
-               topts.dir->flags |= DIR_SHOW_IGNORED;
-               setup_standard_excludes(topts.dir);
+               if (opts->overwrite_ignore) {
+                       topts.dir = xcalloc(1, sizeof(*topts.dir));
+                       topts.dir->flags |= DIR_SHOW_IGNORED;
+                       setup_standard_excludes(topts.dir);
+               }
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           EMPTY_TREE_SHA1_BIN);
@@@ -540,9 -543,7 +543,9 @@@ static void update_refs_for_switch(stru
                else
                        create_branch(old->name, opts->new_branch, new->name,
                                      opts->new_branch_force ? 1 : 0,
 -                                    opts->new_branch_log, opts->track);
 +                                    opts->new_branch_log,
 +                                    opts->new_branch_force ? 1 : 0,
 +                                    opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
        }
                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);
 +                              if (opts->new_branch_force)
 +                                      fprintf(stderr, _("Reset branch '%s'\n"),
 +                                              new->name);
 +                              else
 +                                      fprintf(stderr, _("Already on '%s'\n"),
 +                                              new->name);
                        } else if (opts->new_branch) {
                                if (opts->branch_exists)
                                        fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
@@@ -705,9 -702,7 +708,9 @@@ static int switch_branches(struct check
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
 -      old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
 +      old.path = resolve_ref("HEAD", rev, 0, &flag);
 +      if (old.path)
 +              old.path = xstrdup(old.path);
        old.commit = lookup_commit_reference_gently(rev, 1);
        if (!(flag & REF_ISSYMREF)) {
                free((char *)old.path);
@@@ -874,7 -869,7 +877,7 @@@ static int parse_branchname_arg(int arg
        setup_branch_path(new);
  
        if (!check_refname_format(new->path, 0) &&
 -          resolve_ref(new->path, branch_rev, 1, NULL))
 +          !read_ref(new->path, branch_rev))
                hashcpy(rev, branch_rev);
        else
                new->path = NULL; /* not an existing branch */
@@@ -934,6 -929,7 +937,7 @@@ int cmd_checkout(int argc, const char *
                            3),
                OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
                OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
+               OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"),
                OPT_STRING(0, "conflict", &conflict_style, "style",
                           "conflict style (merge or diff3)"),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
  
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
+       opts.overwrite_ignore = 1;
  
        gitmodules_config();
        git_config(git_checkout_config, &opts);
                struct strbuf buf = STRBUF_INIT;
  
                opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
 -                                                           !!opts.new_branch_force, 0);
 +                                                           !!opts.new_branch_force,
 +                                                           !!opts.new_branch_force);
  
                strbuf_release(&buf);
        }
diff --combined builtin/merge.c
index a1c85344b2b54f957ea69ace864ea99b7d295405,07102c45fa6df150b029b0e9f7f3e11831716123..98ed54a5f5722aba1146cbf627e3b95cc8c14f30
@@@ -26,7 -26,6 +26,7 @@@
  #include "merge-recursive.h"
  #include "resolve-undo.h"
  #include "remote.h"
 +#include "fmt-merge-msg.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -45,10 -44,11 +45,11 @@@ static const char * const builtin_merge
        NULL
  };
  
 -static int show_diffstat = 1, shortlog_len, squash;
 +static int show_diffstat = 1, shortlog_len = -1, squash;
  static int option_commit = 1, allow_fast_forward = 1;
  static int fast_forward_only, option_edit;
  static int allow_trivial = 1, have_message;
+ static int overwrite_ignore = 1;
  static struct strbuf merge_msg;
  static struct commit_list *remoteheads;
  static struct strategy **use_strategies;
@@@ -208,6 -208,7 +209,7 @@@ static struct option builtin_merge_opti
        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_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
        OPT_END()
  };
  
@@@ -409,11 -410,21 +411,11 @@@ static void finish(struct commit *head_
        strbuf_release(&reflog_message);
  }
  
 -static struct object *want_commit(const char *name)
 -{
 -      struct object *obj;
 -      unsigned char sha1[20];
 -      if (get_sha1(name, sha1))
 -              return NULL;
 -      obj = parse_object(sha1);
 -      return peel_to_type(name, 0, obj, OBJ_COMMIT);
 -}
 -
  /* Get the name for the merge commit's message. */
  static void merge_name(const char *remote, struct strbuf *msg)
  {
 -      struct object *remote_head;
 -      unsigned char branch_head[20], buf_sha[20];
 +      struct commit *remote_head;
 +      unsigned char branch_head[20];
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
        const char *ptr;
        remote = bname.buf;
  
        memset(branch_head, 0, sizeof(branch_head));
 -      remote_head = want_commit(remote);
 +      remote_head = get_merge_parent(remote);
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
  
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
 +              if (!prefixcmp(found_ref, "refs/tags/")) {
 +                      strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
 +                                  sha1_to_hex(branch_head), remote);
 +                      goto cleanup;
 +              }
                if (!prefixcmp(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                strbuf_addstr(&truname, "refs/heads/");
                strbuf_addstr(&truname, remote);
                strbuf_setlen(&truname, truname.len - len);
 -              if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
 +              if (ref_exists(truname.buf)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
 -                                  sha1_to_hex(remote_head->sha1),
 +                                  sha1_to_hex(remote_head->object.sha1),
                                    truname.buf + 11,
                                    (early ? " (early part)" : ""));
                        strbuf_release(&truname);
                goto cleanup;
        }
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
 -              sha1_to_hex(remote_head->sha1), remote);
 +              sha1_to_hex(remote_head->object.sha1), remote);
  cleanup:
        strbuf_release(&buf);
        strbuf_release(&bname);
@@@ -538,8 -544,6 +540,8 @@@ static void parse_branch_merge_options(
  
  static int git_merge_config(const char *k, const char *v, void *cb)
  {
 +      int status;
 +
        if (branch && !prefixcmp(k, "branch.") &&
                !prefixcmp(k + 7, branch) &&
                !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
                return git_config_string(&pull_octopus, k, v);
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
 -      else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
 -              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);
 -              if (is_bool && shortlog_len)
 -                      shortlog_len = DEFAULT_MERGE_LOG_LEN;
 -              return 0;
 -      } else if (!strcmp(k, "merge.ff")) {
 +      else if (!strcmp(k, "merge.ff")) {
                int boolval = git_config_maybe_bool(k, v);
                if (0 <= boolval) {
                        allow_fast_forward = boolval;
                default_to_upstream = git_config_bool(k, v);
                return 0;
        }
 +      status = fmt_merge_msg_config(k, v, cb);
 +      if (status)
 +              return status;
        return git_diff_ui_config(k, v, cb);
  }
  
@@@ -711,7 -720,7 +713,7 @@@ static int try_merge_strategy(const cha
                                die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
  
                o.branch1 = head_arg;
 -              o.branch2 = remoteheads->item->util;
 +              o.branch2 = merge_remote_util(remoteheads->item)->name;
  
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
@@@ -766,10 -775,12 +768,12 @@@ int checkout_fast_forward(const unsigne
        memset(&trees, 0, sizeof(trees));
        memset(&opts, 0, sizeof(opts));
        memset(&t, 0, sizeof(t));
-       memset(&dir, 0, sizeof(dir));
-       dir.flags |= DIR_SHOW_IGNORED;
-       setup_standard_excludes(&dir);
-       opts.dir = &dir;
+       if (overwrite_ignore) {
+               memset(&dir, 0, sizeof(dir));
+               dir.flags |= DIR_SHOW_IGNORED;
+               setup_standard_excludes(&dir);
+               opts.dir = &dir;
+       }
  
        opts.head_idx = 1;
        opts.src_index = &the_index;
@@@ -1051,16 -1062,9 +1055,16 @@@ static void write_merge_state(void
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
  
 -      for (j = remoteheads; j; j = j->next)
 -              strbuf_addf(&buf, "%s\n",
 -                      sha1_to_hex(j->item->object.sha1));
 +      for (j = remoteheads; j; j = j->next) {
 +              unsigned const char *sha1;
 +              struct commit *c = j->item;
 +              if (c->util && merge_remote_util(c)->obj) {
 +                      sha1 = merge_remote_util(c)->obj->sha1;
 +              } else {
 +                      sha1 = c->object.sha1;
 +              }
 +              strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
 +      }
        filename = git_path("MERGE_HEAD");
        fd = open(filename, O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
@@@ -1091,7 -1095,7 +1095,7 @@@ int cmd_merge(int argc, const char **ar
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
 -      int flag, i;
 +      int flag, i, ret = 0;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        const char *best_strategy = NULL, *wt_strategy = NULL;
         * current branch.
         */
        branch = resolve_ref("HEAD", head_sha1, 0, &flag);
 -      if (branch && !prefixcmp(branch, "refs/heads/"))
 -              branch += 11;
 +      if (branch) {
 +              if (!prefixcmp(branch, "refs/heads/"))
 +                      branch += 11;
 +              branch = xstrdup(branch);
 +      }
        if (!branch || is_null_sha1(head_sha1))
                head_commit = NULL;
        else
                parse_branch_merge_options(branch_mergeoptions);
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
                        builtin_merge_usage, 0);
 +      if (shortlog_len < 0)
 +              shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
  
        if (verbosity < 0 && show_progress == -1)
                show_progress = 0;
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
  
                /* Invoke 'git reset --merge' */
 -              return cmd_reset(nargc, nargv, prefix);
 +              ret = cmd_reset(nargc, nargv, prefix);
 +              goto done;
        }
  
        if (read_cache_unmerged())
                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], "-"))
 +              if (!argc) {
 +                      if (default_to_upstream)
 +                              argc = setup_with_upstream(&argv);
 +                      else
 +                              die(_("No commit specified and merge.defaultToUpstream not set."));
 +              } else if (argc == 1 && !strcmp(argv[0], "-"))
                        argv[0] = "@{-1}";
        }
        if (!argc)
                argv += 2;
                argc -= 2;
        } else if (!head_commit) {
 -              struct object *remote_head;
 +              struct commit *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                 * to forbid "git merge" into a branch yet to be born.
                if (!allow_fast_forward)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
 -              remote_head = want_commit(argv[0]);
 +              remote_head = get_merge_parent(argv[0]);
                if (!remote_head)
                        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);
 -              return 0;
 +              read_empty(remote_head->object.sha1, 0);
 +              update_ref("initial pull", "HEAD", remote_head->object.sha1,
 +                         NULL, 0, DIE_ON_ERR);
 +              goto done;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
  
                head_arg = "HEAD";
  
                /*
 -               * All the rest are the commits being merged;
 -               * prepare the standard merge summary message to
 -               * be appended to the given message.  If remote
 -               * is invalid we will die later in the common
 -               * codepath so we discard the error in this
 -               * loop.
 +               * All the rest are the commits being merged; prepare
 +               * the standard merge summary message to be appended
 +               * to the given message.
                 */
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
  
                if (!have_message || shortlog_len) {
 -                      fmt_merge_msg(&merge_names, &merge_msg, !have_message,
 -                                    shortlog_len);
 +                      struct fmt_merge_msg_opts opts;
 +                      memset(&opts, 0, sizeof(opts));
 +                      opts.add_title = !have_message;
 +                      opts.shortlog_len = shortlog_len;
 +
 +                      fmt_merge_msg(&merge_names, &merge_msg, &opts);
                        if (merge_msg.len)
                                strbuf_setlen(&merge_msg, merge_msg.len - 1);
                }
        strbuf_reset(&buf);
  
        for (i = 0; i < argc; i++) {
 -              struct object *o;
 -              struct commit *commit;
 -
 -              o = want_commit(argv[i]);
 -              if (!o)
 +              struct commit *commit = get_merge_parent(argv[i]);
 +              if (!commit)
                        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;
 -
 -              strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
 +              strbuf_addf(&buf, "GITHEAD_%s",
 +                          sha1_to_hex(commit->object.sha1));
                setenv(buf.buf, argv[i], 1);
                strbuf_reset(&buf);
 +              if (merge_remote_util(commit) &&
 +                  merge_remote_util(commit)->obj &&
 +                  merge_remote_util(commit)->obj->type == OBJ_TAG) {
 +                      option_edit = 1;
 +                      allow_fast_forward = 0;
 +              }
        }
  
        if (!use_strategies) {
                 * but first the most common case of merging one remote.
                 */
                finish_up_to_date("Already up-to-date.");
 -              return 0;
 +              goto done;
        } else if (allow_fast_forward && !remoteheads->next &&
                        !common->next &&
                        !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
                /* Again the most common case of merging one remote. */
                struct strbuf msg = STRBUF_INIT;
 -              struct object *o;
 +              struct commit *commit;
                char hex[41];
  
                strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
                if (have_message)
                        strbuf_addstr(&msg,
                                " (no commit created; -m option ignored)");
 -              o = want_commit(sha1_to_hex(remoteheads->item->object.sha1));
 -              if (!o)
 -                      return 1;
 +              commit = remoteheads->item;
 +              if (!commit) {
 +                      ret = 1;
 +                      goto done;
 +              }
  
 -              if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1))
 -                      return 1;
 +              if (checkout_fast_forward(head_commit->object.sha1,
 +                                        commit->object.sha1)) {
 +                      ret = 1;
 +                      goto done;
 +              }
  
 -              finish(head_commit, o->sha1, msg.buf);
 +              finish(head_commit, commit->object.sha1, msg.buf);
                drop_save();
 -              return 0;
 +              goto done;
        } else if (!remoteheads->next && common->next)
                ;
                /*
                        git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        printf(_("Trying really trivial in-index merge...\n"));
                        if (!read_tree_trivial(common->item->object.sha1,
 -                                      head_commit->object.sha1, remoteheads->item->object.sha1))
 -                              return merge_trivial(head_commit);
 +                                             head_commit->object.sha1,
 +                                             remoteheads->item->object.sha1)) {
 +                              ret = merge_trivial(head_commit);
 +                              goto done;
 +                      }
                        printf(_("Nope.\n"));
                }
        } else {
                }
                if (up_to_date) {
                        finish_up_to_date("Already up-to-date. Yeeah!");
 -                      return 0;
 +                      goto done;
                }
        }
  
         * If we have a resulting tree, that means the strategy module
         * auto resolved the merge cleanly.
         */
 -      if (automerge_was_ok)
 -              return finish_automerge(head_commit, common, result_tree,
 -                                      wt_strategy);
 +      if (automerge_was_ok) {
 +              ret = finish_automerge(head_commit, common, result_tree,
 +                                     wt_strategy);
 +              goto done;
 +      }
  
        /*
         * Pick the result from the best strategy and have the user fix
                else
                        fprintf(stderr, _("Merge with strategy %s failed.\n"),
                                use_strategies[0]->name);
 -              return 2;
 +              ret = 2;
 +              goto done;
        } else if (best_strategy == wt_strategy)
                ; /* We already have its result in the working tree. */
        else {
        else
                write_merge_state();
  
 -      if (merge_was_ok) {
 +      if (merge_was_ok)
                fprintf(stderr, _("Automatic merge went well; "
                        "stopped before committing as requested\n"));
 -              return 0;
 -      } else
 -              return suggest_conflicts(option_renormalize);
 +      else
 +              ret = suggest_conflicts(option_renormalize);
 +
 +done:
 +      free((char *)branch);
 +      return ret;
  }