Merge branch 'mv/merge-noff'
authorJunio C Hamano <gitster@pobox.com>
Sun, 19 Oct 2008 23:06:21 +0000 (16:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 19 Oct 2008 23:06:21 +0000 (16:06 -0700)
* mv/merge-noff:
builtin-commit: use reduce_heads() only when appropriate

Conflicts:
builtin-commit.c
t/t7600-merge.sh

1  2 
builtin-commit.c
builtin-merge.c
t/t7600-merge.sh
diff --combined builtin-commit.c
index 2c33af276671ff0ba4bffee437a03e7fc2a7fae2,93f360f768b828298826a6c088df456e6d2648ba..33b659edce478fb0d3d4bb723e4f54c13db0efb1
@@@ -448,7 -448,7 +448,7 @@@ static int prepare_to_commit(const cha
  {
        struct stat statbuf;
        int commitable, saved_color_setting;
 -      struct strbuf sb;
 +      struct strbuf sb = STRBUF_INIT;
        char *buffer;
        FILE *fp;
        const char *hook_arg1 = NULL;
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
  
 -      strbuf_init(&sb, 0);
        if (message.len) {
                strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
                stripspace(&sb, 0);
  
        if (signoff) {
 -              struct strbuf sob;
 +              struct strbuf sob = STRBUF_INIT;
                int i;
  
 -              strbuf_init(&sob, 0);
                strbuf_addstr(&sob, sign_off_header);
                strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
                                             getenv("GIT_COMMITTER_EMAIL")));
   */
  static int message_is_empty(struct strbuf *sb)
  {
 -      struct strbuf tmpl;
 +      struct strbuf tmpl = STRBUF_INIT;
        const char *nl;
        int eol, i, start = 0;
  
                return 0;
  
        /* See if the template is just a prefix of the message. */
 -      strbuf_init(&tmpl, 0);
        if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
                stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
                if (start + tmpl.len <= sb->len &&
@@@ -879,9 -882,6 +879,9 @@@ static void print_summary(const char *p
  {
        struct rev_info rev;
        struct commit *commit;
 +      static const char *format = "format:%h: \"%s\"";
 +      unsigned char junk_sha1[20];
 +      const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
  
        commit = lookup_commit(sha1);
        if (!commit)
  
        rev.verbose_header = 1;
        rev.show_root_diff = 1;
 -      get_commit_format("format:%h: %s", &rev);
 +      get_commit_format(format, &rev);
        rev.always_show_header = 0;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
 -      printf("Created %scommit ", initial_commit ? "initial " : "");
 +      printf("[%s%s]: created ",
 +              !prefixcmp(head, "refs/heads/") ?
 +                      head + 11 :
 +                      !strcmp(head, "HEAD") ?
 +                              "detached HEAD" :
 +                              head,
 +              initial_commit ? " (root-commit)" : "");
  
        if (!log_tree_commit(&rev, commit)) {
                struct strbuf buf = STRBUF_INIT;
 -              format_commit_message(commit, "%h: %s", &buf, DATE_NORMAL);
 +              format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
                printf("%s\n", buf.buf);
                strbuf_release(&buf);
        }
@@@ -937,12 -931,14 +937,14 @@@ static const char commit_utf8_warn[] 
  
  int cmd_commit(int argc, const char **argv, const char *prefix)
  {
 -      struct strbuf sb;
 +      struct strbuf sb = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl, *p;
        unsigned char commit_sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
+       struct stat statbuf;
+       int allow_fast_forward = 1;
  
        git_config(git_commit_config, NULL);
  
                return 1;
        }
  
 -      strbuf_init(&sb, 0);
        /* Determine parents */
        if (initial_commit) {
                reflog_msg = "commit (initial)";
                for (c = commit->parents; c; c = c->next)
                        pptr = &commit_list_insert(c->item, pptr)->next;
        } else if (in_merge) {
 -              struct strbuf m;
 +              struct strbuf m = STRBUF_INIT;
                FILE *fp;
  
                reflog_msg = "commit (merge)";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
 -              strbuf_init(&m, 0);
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
                        die("could not open %s for reading: %s",
                }
                fclose(fp);
                strbuf_release(&m);
+               if (!stat(git_path("MERGE_MODE"), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
+                               die("could not read MERGE_MODE: %s",
+                                               strerror(errno));
+                       if (!strcmp(sb.buf, "no-ff"))
+                               allow_fast_forward = 0;
+               }
+               if (allow_fast_forward)
+                       parents = reduce_heads(parents);
        } else {
                reflog_msg = "commit";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
-       parents = reduce_heads(parents);
  
        /* Finally, get the commit message */
+       strbuf_reset(&sb);
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
                rollback_index_files();
                die("could not read commit message");
  
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
+       unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
  
        if (commit_index_files())
diff --combined builtin-merge.c
index 5e2b7f12c35ab1bfebda66f7ee9180c68e7f23c4,4c9ed5da0949f60a20b2e18ac1d892bb5c6af4a4..5e7910bd8da0a603dd82e7502c28a603bf80fa82
@@@ -123,7 -123,8 +123,7 @@@ static struct strategy *get_strategy(co
                exit(1);
        }
  
 -      ret = xmalloc(sizeof(struct strategy));
 -      memset(ret, 0, sizeof(struct strategy));
 +      ret = xcalloc(1, sizeof(struct strategy));
        ret->name = xstrdup(name);
        return ret;
  }
@@@ -179,6 -180,7 +179,7 @@@ static void drop_save(void
  {
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
+       unlink(git_path("MERGE_MODE"));
  }
  
  static void save_state(void)
@@@ -226,7 -228,7 +227,7 @@@ static void reset_hard(unsigned const c
  
  static void restore_state(void)
  {
 -      struct strbuf sb;
 +      struct strbuf sb = STRBUF_INIT;
        const char *args[] = { "stash", "apply", NULL, NULL };
  
        if (is_null_sha1(stash))
  
        reset_hard(head, 1);
  
 -      strbuf_init(&sb, 0);
        args[2] = sha1_to_hex(stash);
  
        /*
@@@ -257,7 -260,7 +258,7 @@@ static void squash_message(void
  {
        struct rev_info rev;
        struct commit *commit;
 -      struct strbuf out;
 +      struct strbuf out = STRBUF_INIT;
        struct commit_list *j;
        int fd;
  
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
  
 -      strbuf_init(&out, 0);
        strbuf_addstr(&out, "Squashed commit of the following:\n");
        while ((commit = get_revision(&rev)) != NULL) {
                strbuf_addch(&out, '\n');
@@@ -325,8 -329,9 +326,8 @@@ static int run_hook(const char *name
  
  static void finish(const unsigned char *new_head, const char *msg)
  {
 -      struct strbuf reflog_message;
 +      struct strbuf reflog_message = STRBUF_INIT;
  
 -      strbuf_init(&reflog_message, 0);
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
        else {
@@@ -377,7 -382,7 +378,7 @@@ static void merge_name(const char *remo
  {
        struct object *remote_head;
        unsigned char branch_head[20], buf_sha[20];
 -      struct strbuf buf;
 +      struct strbuf buf = STRBUF_INIT;
        const char *ptr;
        int len, early;
  
        if (!remote_head)
                die("'%s' does not point to a commit", remote);
  
 -      strbuf_init(&buf, 0);
        strbuf_addstr(&buf, "refs/heads/");
        strbuf_addstr(&buf, remote);
        resolve_ref(buf.buf, branch_head, 0, 0);
        if (!strcmp(remote, "FETCH_HEAD") &&
                        !access(git_path("FETCH_HEAD"), R_OK)) {
                FILE *fp;
 -              struct strbuf line;
 +              struct strbuf line = STRBUF_INIT;
                char *ptr;
  
 -              strbuf_init(&line, 0);
                fp = fopen(git_path("FETCH_HEAD"), "r");
                if (!fp)
                        die("could not open %s for reading: %s",
@@@ -540,17 -547,7 +541,17 @@@ static int try_merge_strategy(const cha
        const char **args;
        int i = 0, ret;
        struct commit_list *j;
 -      struct strbuf buf;
 +      struct strbuf buf = STRBUF_INIT;
 +      int index_fd;
 +      struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 +
 +      index_fd = hold_locked_index(lock, 1);
 +      refresh_cache(REFRESH_QUIET);
 +      if (active_cache_changed &&
 +                      (write_cache(index_fd, active_cache, active_nr) ||
 +                       commit_locked_index(lock)))
 +              return error("Unable to write index.");
 +      rollback_lock_file(lock);
  
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                int clean;
        } else {
                args = xmalloc((4 + commit_list_count(common) +
                                        commit_list_count(remoteheads)) * sizeof(char *));
 -              strbuf_init(&buf, 0);
                strbuf_addf(&buf, "merge-%s", strategy);
                args[i++] = buf.buf;
                for (j = common; j; j = j->next)
@@@ -726,12 -724,12 +727,12 @@@ static void add_strategies(const char *
  static int merge_trivial(void)
  {
        unsigned char result_tree[20], result_commit[20];
 -      struct commit_list *parent = xmalloc(sizeof(struct commit_list *));
 +      struct commit_list *parent = xmalloc(sizeof(*parent));
  
        write_tree_trivial(result_tree);
        printf("Wonderful.\n");
        parent->item = lookup_commit(head);
 -      parent->next = xmalloc(sizeof(struct commit_list *));
 +      parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
@@@ -841,7 -839,7 +842,7 @@@ static int evaluate_result(void
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
        unsigned char result_tree[20];
 -      struct strbuf buf;
 +      struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
        int flag, head_invalid = 0, i;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
         * Traditional format never would have "-m" so it is an
         * additional safety measure to check for it.
         */
 -      strbuf_init(&buf, 0);
  
        if (!have_message && is_old_style_invocation(argc, argv)) {
                strbuf_addstr(&merge_msg, argv[0]);
                reset_hard(remote_head->sha1, 0);
                return 0;
        } else {
 -              struct strbuf msg;
 +              struct strbuf msg = STRBUF_INIT;
  
                /* We are invoked directly as the first-class UI. */
                head_arg = "HEAD";
                 * codepath so we discard the error in this
                 * loop.
                 */
 -              strbuf_init(&msg, 0);
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &msg);
                fmt_merge_msg(option_log, &msg, &merge_msg);
                        !common->next &&
                        !hashcmp(common->item->object.sha1, head)) {
                /* Again the most common case of merging one remote. */
 -              struct strbuf msg;
 +              struct strbuf msg = STRBUF_INIT;
                struct object *o;
                char hex[41];
  
                        hex,
                        find_unique_abbrev(remoteheads->item->object.sha1,
                        DEFAULT_ABBREV));
 -              strbuf_init(&msg, 0);
                strbuf_addstr(&msg, "Fast forward");
                if (have_message)
                        strbuf_addstr(&msg,
                        merge_msg.len)
                        die("Could not write to %s", git_path("MERGE_MSG"));
                close(fd);
+               fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
+               if (fd < 0)
+                       die("Could open %s for writing", git_path("MERGE_MODE"));
+               strbuf_reset(&buf);
+               if (!allow_fast_forward)
+                       strbuf_addf(&buf, "no-ff");
+               if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+                       die("Could not write to %s", git_path("MERGE_MODE"));
+               close(fd);
        }
  
        if (merge_was_ok) {
diff --combined t/t7600-merge.sh
index 3a36a95b9ac42b35e680109df4daf87fc219fb53,209d7cd4b8f9497cef160a1a1b11f93cb7347c8f..e5b210bc960c8433d6758f3932a86647208297ef
@@@ -511,37 -511,20 +511,53 @@@ test_expect_success 'in-index merge' 
  
  test_debug 'gitk --all'
  
 +test_expect_success 'refresh the index before merging' '
 +      git reset --hard c1 &&
 +      sleep 1 &&
 +      touch file &&
 +      git merge c3
 +'
 +
 +cat >expected <<EOF
 +Merge branch 'c5' (early part)
 +EOF
 +
 +test_expect_success 'merge early part of c2' '
 +      git reset --hard c3 &&
 +      echo c4 > c4.c &&
 +      git add c4.c &&
 +      git commit -m c4 &&
 +      git tag c4 &&
 +      echo c5 > c5.c &&
 +      git add c5.c &&
 +      git commit -m c5 &&
 +      git tag c5 &&
 +      git reset --hard c3 &&
 +      echo c6 > c6.c &&
 +      git add c6.c &&
 +      git commit -m c6 &&
 +      git tag c6 &&
 +      git merge c5~1 &&
 +      git show -s --pretty=format:%s HEAD > actual &&
 +      test_cmp actual expected
 +'
 +
 +test_debug 'gitk --all'
 +
+ test_expect_success 'merge --no-ff --no-commit && commit' '
+       git reset --hard c0 &&
+       git merge --no-ff --no-commit c1 &&
+       EDITOR=: git commit &&
+       verify_parents $c0 $c1
+ '
+ test_debug 'gitk --all'
+ test_expect_success 'amending no-ff merge commit' '
+       EDITOR=: git commit --amend &&
+       verify_parents $c0 $c1
+ '
+ test_debug 'gitk --all'
  test_done