Code

Merge branch 'jc/better-conflict-resolution'
authorShawn O. Pearce <spearce@spearce.org>
Mon, 29 Sep 2008 17:04:21 +0000 (10:04 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Mon, 29 Sep 2008 17:15:07 +0000 (10:15 -0700)
* jc/better-conflict-resolution:
  Fix AsciiDoc errors in merge documentation
  git-merge documentation: describe how conflict is presented
  checkout --conflict=<style>: recreate merge in a non-default style
  checkout -m: recreate merge when checking out of unmerged index
  git-merge-recursive: learn to honor merge.conflictstyle
  merge.conflictstyle: choose between "merge" and "diff3 -m" styles
  rerere: understand "diff3 -m" style conflicts with the original
  rerere.c: use symbolic constants to keep track of parsing states
  xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less
  xmerge.c: minimum readability fixups
  xdiff-merge: optionally show conflicts in "diff3 -m" style
  xdl_fill_merge_buffer(): separate out a too deeply nested function
  checkout --ours/--theirs: allow checking out one side of a conflicting merge
  checkout -f: allow ignoring unmerged paths when checking out of the index

Conflicts:
Documentation/git-checkout.txt
builtin-checkout.c
builtin-merge-recursive.c
t/t7201-co.sh

1  2 
Documentation/config.txt
Documentation/git-checkout.txt
Documentation/git-merge.txt
builtin-checkout.c
rerere.c
t/t6023-merge-file.sh
t/t7201-co.sh
xdiff-interface.c
xdiff-interface.h

Simple merge
index be54a0299fb2b6849993aa27d18c829ce91e7e00,13b106d6264686da90a543d9a84670348eba6939..82e154de4970c1c2cb35335591cfe457e80a89c1
@@@ -8,8 -8,8 +8,8 @@@ git-checkout - Checkout a branch or pat
  SYNOPSIS
  --------
  [verse]
 -'git checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>]
 +'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>]
- 'git checkout' [<tree-ish>] [--] <paths>...
+ 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
  
  DESCRIPTION
  -----------
@@@ -21,15 -21,10 +21,14 @@@ specified, <new_branch>.  Using -b wil
  be created; in this case you can use the --track or --no-track
  options, which will be passed to `git branch`.
  
 +As a convenience, --track will default to create a branch whose
 +name is constructed from the specified branch name by stripping
 +the first namespace level.
 +
  When <paths> are given, this command does *not* switch
  branches.  It updates the named paths in the working tree from
- the index file (i.e. it runs `git checkout-index -f -u`), or
- from a named commit.  In
- this case, the `-f` and `-b` options are meaningless and giving
+ the index file, or from a named commit.  In
+ this case, the `-b` options is meaningless and giving
  either of them results in an error.  <tree-ish> argument can be
  used to specify a specific tree-ish (i.e. commit, tag or tree)
  to update the index for the given paths before updating the
Simple merge
index 075667c9cc8aabe4157900635b2aabc4f55f448c,79214327b0d99369b3ce384ed1900342f5874c87..b572b3bf69c791912717eae313942316cd77ac37
@@@ -467,30 -602,20 +607,37 @@@ int cmd_checkout(int argc, const char *
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
  
-       git_config(git_default_config, NULL);
+       git_config(git_checkout_config, NULL);
  
 -      opts.track = git_branch_track;
 +      opts.track = BRANCH_TRACK_UNSPECIFIED;
  
        argc = parse_options(argc, argv, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
  
 +      /* --track without -b should DWIM */
 +      if (0 < opts.track && !opts.new_branch) {
 +              const char *argv0 = argv[0];
 +              if (!argc || !strcmp(argv0, "--"))
 +                      die ("--track needs a branch name");
 +              if (!prefixcmp(argv0, "refs/"))
 +                      argv0 += 5;
 +              if (!prefixcmp(argv0, "remotes/"))
 +                      argv0 += 8;
 +              argv0 = strchr(argv0, '/');
 +              if (!argv0 || !argv0[1])
 +                      die ("Missing branch name; try -b");
 +              opts.new_branch = argv0 + 1;
 +      }
 +
 +      if (opts.track == BRANCH_TRACK_UNSPECIFIED)
 +              opts.track = git_branch_track;
+       if (conflict_style) {
+               opts.merge = 1; /* implied */
+               git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
+       }
+       if (!opts.new_branch && (opts.track != git_branch_track))
+               die("git checkout: --track and --no-track require -b");
  
        if (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
@@@ -582,21 -707,12 +729,24 @@@ no_reference
                        }
                }
  
-               return checkout_paths(source_tree, pathspec);
+               if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+                       die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
+               return checkout_paths(source_tree, pathspec, &opts);
        }
  
 +      if (opts.new_branch) {
 +              struct strbuf buf;
 +              strbuf_init(&buf, 0);
 +              strbuf_addstr(&buf, "refs/heads/");
 +              strbuf_addstr(&buf, opts.new_branch);
 +              if (!get_sha1(buf.buf, rev))
 +                      die("git checkout: branch %s already exists", opts.new_branch);
 +              if (check_ref_format(buf.buf))
 +                      die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
 +              strbuf_release(&buf);
 +      }
 +
        if (new.name && !new.commit) {
                die("Cannot switch branch to a non-commit.");
        }
diff --cc rerere.c
Simple merge
Simple merge
diff --cc t/t7201-co.sh
index 62d73f934a0c0be2d41a30d11308430838f6c9c0,ac49311cf2c845475eed00ce5b7d8f364770d1c4..82769b89fc79d91982420ddfc83ce6c3dd672095
@@@ -337,39 -337,7 +337,39 @@@ test_expect_success 
      test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
      test_must_fail git checkout --track -b track'
  
- test_expect_success 'checkout an unmerged path should fail' '
 +test_expect_success \
 +    'checkout with --track fakes a sensible -b <name>' '
 +    git update-ref refs/remotes/origin/koala/bear renamer &&
 +    git update-ref refs/new/koala/bear renamer &&
 +
 +    git checkout --track origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track refs/remotes/origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track remotes/origin/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 +
 +    git checkout master && git branch -D koala/bear &&
 +
 +    git checkout --track refs/new/koala/bear &&
 +    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
 +    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 +'
 +
 +test_expect_success \
 +    'checkout with --track, but without -b, fails with too short tracked name' '
 +    test_must_fail git checkout --track renamer'
 +
+ setup_conflicting_index () {
        rm -f .git/index &&
        O=$(echo original | git hash-object -w --stdin) &&
        A=$(echo ourside | git hash-object -w --stdin) &&
@@@ -391,14 -363,119 +395,129 @@@ test_expect_success 'checkout an unmerg
        test_cmp sample file
  '
  
+ test_expect_success 'checkout with an unmerged path can be ignored' '
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       echo ourside >expect &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       git checkout -f fild file filf &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp sample file
+ '
+ test_expect_success 'checkout unmerged stage' '
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       echo ourside >expect &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       git checkout --ours . &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp expect file &&
+       git checkout --theirs file &&
+       test ztheirside = "z$(cat file)"
+ '
+ test_expect_success 'checkout with --merge' '
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       echo ourside >expect &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       git checkout -m -- fild file filf &&
+       (
+               echo "<<<<<<< ours"
+               echo ourside
+               echo "======="
+               echo theirside
+               echo ">>>>>>> theirs"
+       ) >merged &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp merged file
+ '
+ test_expect_success 'checkout with --merge, in diff3 -m style' '
+       git config merge.conflictstyle diff3 &&
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       echo ourside >expect &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       git checkout -m -- fild file filf &&
+       (
+               echo "<<<<<<< ours"
+               echo ourside
+               echo "|||||||"
+               echo original
+               echo "======="
+               echo theirside
+               echo ">>>>>>> theirs"
+       ) >merged &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp merged file
+ '
+ test_expect_success 'checkout --conflict=merge, overriding config' '
+       git config merge.conflictstyle diff3 &&
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       echo ourside >expect &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       git checkout --conflict=merge -- fild file filf &&
+       (
+               echo "<<<<<<< ours"
+               echo ourside
+               echo "======="
+               echo theirside
+               echo ">>>>>>> theirs"
+       ) >merged &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp merged file
+ '
+ test_expect_success 'checkout --conflict=diff3' '
+       git config --unset merge.conflictstyle
+       setup_conflicting_index &&
+       echo "none of the above" >sample &&
+       echo ourside >expect &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       git checkout --conflict=diff3 -- fild file filf &&
+       (
+               echo "<<<<<<< ours"
+               echo ourside
+               echo "|||||||"
+               echo original
+               echo "======="
+               echo theirside
+               echo ">>>>>>> theirs"
+       ) >merged &&
+       test_cmp expect fild &&
+       test_cmp expect filf &&
+       test_cmp merged file
+ '
 +test_expect_success 'failing checkout -b should not break working tree' '
 +      git reset --hard master &&
 +      git symbolic-ref HEAD refs/heads/master &&
 +      test_must_fail git checkout -b renamer side^ &&
 +      test $(git symbolic-ref HEAD) = refs/heads/master &&
 +      git diff --exit-code &&
 +      git diff --cached --exit-code
 +
 +'
 +
  test_done
Simple merge
Simple merge