Code

Merge branch 'cb/maint-orphan-merge-noclobber'
authorJunio C Hamano <gitster@pobox.com>
Wed, 24 Nov 2010 23:55:36 +0000 (15:55 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 24 Nov 2010 23:55:36 +0000 (15:55 -0800)
* cb/maint-orphan-merge-noclobber:
  do not overwrite untracked during merge from unborn branch

1  2 
builtin/merge.c
t/t7607-merge-overwrite.sh

diff --combined builtin/merge.c
index 9ec13f14bdcc3996fe18ed6fdf761ec07e627792,2a6c49f6ce4209fdc59b18498c58399f17044d79..c24a7be020d10540581b41c21a2ecf83df5563d9
@@@ -42,7 -42,7 +42,7 @@@ static const char * const builtin_merge
        NULL
  };
  
 -static int show_diffstat = 1, option_log, squash;
 +static int show_diffstat = 1, shortlog_len, squash;
  static int option_commit = 1, allow_fast_forward = 1;
  static int fast_forward_only;
  static int allow_trivial = 1, have_message;
@@@ -54,7 -54,6 +54,7 @@@ static size_t use_strategies_nr, use_st
  static const char **xopts;
  static size_t xopts_nr, xopts_alloc;
  static const char *branch;
 +static int option_renormalize;
  static int verbosity;
  static int allow_rerere_auto;
  
@@@ -132,7 -131,6 +132,7 @@@ static struct strategy *get_strategy(co
  
        ret = xcalloc(1, sizeof(struct strategy));
        ret->name = xstrdup(name);
 +      ret->attr = NO_TRIVIAL;
        return ret;
  }
  
@@@ -177,9 -175,8 +177,9 @@@ static struct option builtin_merge_opti
        OPT_BOOLEAN(0, "stat", &show_diffstat,
                "show a diffstat at the end of the merge"),
        OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
 -      OPT_BOOLEAN(0, "log", &option_log,
 -              "add list of one-line log to merge commit message"),
 +      { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
 +        "add (at most <n>) entries from shortlog to merge commit message",
 +        PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
        OPT_BOOLEAN(0, "squash", &squash,
                "create a single commit instead of doing a merge"),
        OPT_BOOLEAN(0, "commit", &option_commit,
@@@ -234,6 -231,24 +234,24 @@@ static void save_state(void
                die("not a valid object: %s", buffer.buf);
  }
  
+ static void read_empty(unsigned const char *sha1, int verbose)
+ {
+       int i = 0;
+       const char *args[7];
+       args[i++] = "read-tree";
+       if (verbose)
+               args[i++] = "-v";
+       args[i++] = "-m";
+       args[i++] = "-u";
+       args[i++] = EMPTY_TREE_SHA1_HEX;
+       args[i++] = sha1_to_hex(sha1);
+       args[i] = NULL;
+       if (run_command_v_opt(args, RUN_GIT_CMD))
+               die("read-tree failed");
+ }
  static void reset_hard(unsigned const char *sha1, int verbose)
  {
        int i = 0;
@@@ -403,7 -418,7 +421,7 @@@ static void merge_name(const char *remo
                        goto cleanup;
                }
                if (!prefixcmp(found_ref, "refs/remotes/")) {
 -                      strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n",
 +                      strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
                strbuf_addstr(&truname, "refs/heads/");
                strbuf_addstr(&truname, remote);
                strbuf_setlen(&truname, truname.len - len);
 -              if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
 +              if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
                                    sha1_to_hex(remote_head->sha1),
@@@ -489,8 -504,7 +507,8 @@@ static int git_merge_config(const char 
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
                if (argc < 0)
 -                      die("Bad branch.%s.mergeoptions string", branch);
 +                      die("Bad branch.%s.mergeoptions string: %s", branch,
 +                          split_cmdline_strerror(argc));
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
 -      else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
 -              option_log = git_config_bool(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;
 +      }
        return git_diff_ui_config(k, v, cb);
  }
  
@@@ -637,11 -642,18 +655,11 @@@ static int try_merge_strategy(const cha
                if (!strcmp(strategy, "subtree"))
                        o.subtree_shift = "";
  
 -              for (x = 0; x < xopts_nr; x++) {
 -                      if (!strcmp(xopts[x], "ours"))
 -                              o.recursive_variant = MERGE_RECURSIVE_OURS;
 -                      else if (!strcmp(xopts[x], "theirs"))
 -                              o.recursive_variant = MERGE_RECURSIVE_THEIRS;
 -                      else if (!strcmp(xopts[x], "subtree"))
 -                              o.subtree_shift = "";
 -                      else if (!prefixcmp(xopts[x], "subtree="))
 -                              o.subtree_shift = xopts[x]+8;
 -                      else
 +              o.renormalize = option_renormalize;
 +
 +              for (x = 0; x < xopts_nr; x++)
 +                      if (parse_merge_opt(&o, xopts[x]))
                                die("Unknown option for merge-recursive: -X%s", xopts[x]);
 -              }
  
                o.branch1 = head_arg;
                o.branch2 = remoteheads->item->util;
@@@ -710,7 -722,7 +728,7 @@@ int checkout_fast_forward(const unsigne
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
 -      opts.msgs = get_porcelain_error_msgs();
 +      setup_unpack_trees_porcelain(&opts, "merge");
  
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
@@@ -822,7 -834,7 +840,7 @@@ static int finish_automerge(struct comm
        return 0;
  }
  
 -static int suggest_conflicts(void)
 +static int suggest_conflicts(int renormalizing)
  {
        FILE *fp;
        int pos;
@@@ -985,7 -997,7 +1003,7 @@@ int cmd_merge(int argc, const char **ar
                        die("%s - not something we can merge", argv[0]);
                update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
                                DIE_ON_ERR);
-               reset_hard(remote_head->sha1, 0);
+               read_empty(remote_head->sha1, 0);
                return 0;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
  
 -              if (have_message && option_log)
 -                      fmt_merge_msg_shortlog(&merge_names, &merge_msg);
 -              else if (!have_message)
 -                      fmt_merge_msg(option_log, &merge_names, &merge_msg);
 -
 -
 -              if (!(have_message && !option_log) && merge_msg.len)
 -                      strbuf_setlen(&merge_msg, merge_msg.len-1);
 +              if (!have_message || shortlog_len) {
 +                      fmt_merge_msg(&merge_names, &merge_msg, !have_message,
 +                                    shortlog_len);
 +                      if (merge_msg.len)
 +                              strbuf_setlen(&merge_msg, merge_msg.len - 1);
 +              }
        }
  
        if (head_invalid || !argc)
                        "stopped before committing as requested\n");
                return 0;
        } else
 -              return suggest_conflicts();
 +              return suggest_conflicts(option_renormalize);
  }
index d82349a6a8d6c12c3ca4398a18bd6c1dd42efdc9,8317a574ce2a56162094e817499fb188043ba8da..3988900fc33f0f5f9cdd416096df619aec2d577e
@@@ -31,7 -31,7 +31,7 @@@ test_expect_success 'setup' 
  test_expect_success 'will not overwrite untracked file' '
        git reset --hard c1 &&
        cat important > c2.c &&
 -      ! git merge c2 &&
 +      test_must_fail git merge c2 &&
        test_cmp important c2.c
  '
  
@@@ -39,7 -39,7 +39,7 @@@ test_expect_success 'will not overwrit
        git reset --hard c1 &&
        cat important > c2.c &&
        git add c2.c &&
 -      ! git merge c2 &&
 +      test_must_fail git merge c2 &&
        test_cmp important c2.c
  '
  
@@@ -48,7 -48,7 +48,7 @@@ test_expect_success 'will not overwrit
        cat important > c2.c &&
        git add c2.c &&
        rm c2.c &&
 -      ! git merge c2 &&
 +      test_must_fail git merge c2 &&
        git checkout c2.c &&
        test_cmp important c2.c
  '
@@@ -58,7 -58,7 +58,7 @@@ test_expect_success 'will not overwrit
        git rm c1.c &&
        git commit -m "rm c1.c" &&
        cat important > c1.c &&
 -      ! git merge c1a &&
 +      test_must_fail git merge c1a &&
        test_cmp important c1.c
  '
  
@@@ -68,7 -68,7 +68,7 @@@ test_expect_success 'will not overwrit
        git commit -m "rm c1.c" &&
        cat important > c1.c &&
        git add c1.c &&
 -      ! git merge c1a &&
 +      test_must_fail git merge c1a &&
        test_cmp important c1.c
  '
  
@@@ -79,9 -79,25 +79,25 @@@ test_expect_success 'will not overwrit
        cat important > c1.c &&
        git add c1.c &&
        rm c1.c &&
 -      ! git merge c1a &&
 +      test_must_fail git merge c1a &&
        git checkout c1.c &&
        test_cmp important c1.c
  '
  
+ cat >expect <<\EOF
+ error: Untracked working tree file 'c0.c' would be overwritten by merge.
+ fatal: read-tree failed
+ EOF
+ test_expect_success 'will not overwrite untracked file on unborn branch' '
+       git reset --hard c0 &&
+       git rm -fr . &&
+       git checkout --orphan new &&
+       cp important c0.c &&
+       test_must_fail git merge c0 2>out &&
+       test_cmp out expect &&
+       test_path_is_missing .git/MERGE_HEAD &&
+       test_cmp important c0.c
+ '
  test_done