The new strategy resolves only two heads, and the result of the merge is always the second head. It can be useful with `rebase --onto`, because it always resolves conflicts in favor of the commits being applied. The patch includes an update to git-rebase's documentation, showing how to use the new patch to convert an "--amend"ing commit into a separate one, as if --amend had not been used. --- .gitignore | 1 + Documentation/git-rebase.txt | 39 +++++++++++++++++++++++++ Documentation/merge-strategies.txt | 6 ++++ Makefile | 3 +- builtin-merge-theirs.c | 56 ++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git-merge.sh | 6 ++-- git.c | 1 + t/t3409-rebase-merge-theirs.sh | 43 +++++++++++++++++++++++++++ 9 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 builtin-merge-theirs.c create mode 100755 t/t3409-rebase-merge-theirs.sh This is equivalent to this script that I suggested in http://permalink.gmane.org/gmane.comp.version-control.git/83528 #! /bin/sh eval git reset \$$# -- . git-checkout-index -q -f -a The use-case in the manual is also based on that thread. diff --git a/.gitignore b/.gitignore index 4ff2fec..6d000e5 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ git-merge-recursive git-merge-resolve git-merge-stupid git-merge-subtree +git-merge-theirs git-mergetool git-mktag git-mktree diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index cc4e94f..577ae83 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -168,6 +168,45 @@ This is useful if F and G were flawed in some way, or should not be part of topicA. Note that the argument to --onto and the <upstream> parameter can be any valid commit-ish. +`git rebase` rewrites history, and this is in general something that +you do not want to do when somebody else is pulling from your +repository. For this reason, history rewrites ("forced updates") +are often forbidden when pushing to a remote repository. However, +there are other commands that rewrite history, and indeed rebasing +can help fixing mistakes and reverting to the published history. + +For example, `git commit --amend` also has this effect, and it can +happen that you use it even though you had already pushed to the +remote repository before amending your commit. You can then +use `git rebase` (with the --onto option) to transform the `--amend` +commit into a separate commit (as if you had not used the `--amend` +option). The situation is like this: + +------------ + o---D---E origin/master + \ + E'---F---G---H---I master +------------ + +because the parent of the amended commit E' is D, that is +origin/master^. To avoid a forced update from master to +origin/master, you need the history to look like this: + +------------ + o---D---E origin/master + \ + E''---F'---G'---H'---I' master +------------ + +You can achieve this with: + + git-rebase -s theirs --onto origin/master origin/master^ master + +The merge strategy `-s theirs` resolves conflicts in favor of the commits +being rebased---in this case, you know that the only conflicts will occur +when replaying E', and you definitely E'' to have those changes. + + In case of conflict, git-rebase will stop at the first problematic commit and leave conflict markers in the tree. You can use git diff to locate the markers (<<<<<<) and make edits to resolve the conflict. For each diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt index 1276f85..384c34a 100644 --- a/Documentation/merge-strategies.txt +++ b/Documentation/merge-strategies.txt @@ -34,6 +34,12 @@ ours:: be used to supersede old development history of side branches. +theirs:: + This resolves only two heads, and the result of the merge is + always the second head. It can be useful with `rebase --onto`, + because it always resolves conflicts in favor of the commits + being applied. + subtree:: This is a modified recursive strategy. When merging trees A and B, if B corresponds to a subtree of A, B is first adjusted to diff --git a/Makefile b/Makefile index cce5a6e..6241e5d 100644 --- a/Makefile +++ b/Makefile @@ -515,6 +515,7 @@ BUILTIN_OBJS += builtin-merge-base.o BUILTIN_OBJS += builtin-merge-file.o BUILTIN_OBJS += builtin-merge-ours.o BUILTIN_OBJS += builtin-merge-recursive.o +BUILTIN_OBJS += builtin-merge-theirs.o BUILTIN_OBJS += builtin-mv.o BUILTIN_OBJS += builtin-name-rev.o BUILTIN_OBJS += builtin-pack-objects.o @@ -1343,7 +1344,7 @@ check-docs:: case "$$v" in \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ git-merge-resolve | git-merge-stupid | git-merge-subtree | \ - git-fsck-objects | git-init-db | \ + git-merge-theirs | git-fsck-objects | git-init-db | \ git-?*--?* ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ diff --git a/builtin-merge-theirs.c b/builtin-merge-theirs.c new file mode 100644 index 0000000..6f3e66a --- /dev/null +++ b/builtin-merge-theirs.c @@ -0,0 +1,56 @@ +/* + * Implementation the `theirs' merge strategy. + * + * Copyright (c) 2008 Paolo Bonzini + * + * A one-way merge, declaring that the merged-from tree trumps everybody else. + */ + +#include "cache.h" +#include "run-command.h" +#include "tree.h" +#include "tree-walk.h" +#include "unpack-trees.h" + +int cmd_merge_theirs(int argc, const char **argv, const char *prefix) +{ + int i, fd; + struct tree *tree; + struct tree_desc desc; + struct unpack_trees_options opts; + struct lock_file lock_file; + unsigned char sha1[20]; + + if (argc < 4) + die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]); + + for (i = 1; i < argc; ++i) + if (!strcmp(argv[i], "--")) + break; + if (argc - i != 3) /* "--" "<head>" "<remote>" */ + die("Not handling anything other than two heads merge."); + if (get_sha1(argv[i + 2], sha1)) + die("%s: not a valid SHA1", argv[i + 2]); + if (unmerged_cache()) + die("you need to resolve your current index first"); + + fd = hold_locked_index(&lock_file, 1); + + memset(&opts, 0, sizeof(opts)); + opts.update = 1; + opts.reset = 1; + opts.merge = 1; + opts.fn = oneway_merge; + opts.src_index = &the_index; + opts.dst_index = &the_index; + + tree = parse_tree_indirect(sha1); + init_tree_desc(&desc, tree->buffer, tree->size); + if (unpack_trees(1, &desc, &opts)) + return 128; + + if (write_cache(fd, active_cache, active_nr) || + commit_locked_index(&lock_file)) + die("unable to write new index file"); + return 0; +} diff --git a/builtin.h b/builtin.h index 8bda111..933ba84 100644 --- a/builtin.h +++ b/builtin.h @@ -60,6 +60,7 @@ extern int cmd_merge_base(int argc, const char **argv, const char *prefix); extern int cmd_merge_ours(int argc, const char **argv, const char *prefix); extern int cmd_merge_file(int argc, const char **argv, const char *prefix); extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix); +extern int cmd_merge_theirs(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); diff --git a/git-merge.sh b/git-merge.sh index 2d177c1..24ba651 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -30,11 +30,11 @@ test -z "$(git ls-files -u)" || LF=' ' -all_strategies='recur recursive octopus resolve stupid ours subtree' +all_strategies='recur recursive octopus resolve stupid ours theirs subtree' default_twohead_strategies='recursive' default_octopus_strategies='octopus' -no_fast_forward_strategies='subtree ours' -no_trivial_strategies='recursive recur subtree ours' +no_fast_forward_strategies='subtree ours theirs' +no_trivial_strategies='recursive recur subtree ours theirs' use_strategies= allow_fast_forward=t diff --git a/git.c b/git.c index 272bf03..4ee0822 100644 --- a/git.c +++ b/git.c @@ -326,6 +326,7 @@ static void handle_internal_command(int argc, const char **argv) { "merge-ours", cmd_merge_ours, RUN_SETUP }, { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE }, { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE }, + { "merge-theirs", cmd_merge_theirs, RUN_SETUP }, { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, diff --git a/t/t3409-rebase-merge-theirs.sh b/t/t3409-rebase-merge-theirs.sh new file mode 100755 index 0000000..01a0434 --- /dev/null +++ b/t/t3409-rebase-merge-theirs.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# +# Copyright (c) 2008 Paolo Bonzini +# + +test_description='git rebase -s theirs test' + +. ./test-lib.sh + +test_expect_success setup ' + T="A quick brown fox jumps over the lazy dog." + echo "$T" >file && + echo "$T" >untouched + git add file untouched && + git commit -m"initial" && + for i in 1 2 3 4 5 + do + echo "$i $T" + done >file && + git commit -a -m"more work" && + git branch origin && + sed "s/5/AMEND/" file >file.tmp && + mv file.tmp file && + git commit --amend -a -m"master amended." && + for i in 6 7 8 9 10 + do + echo "$i $T" >> file && + git commit -a -m"$i" || exit 1 + done && + git branch amended +' + +test_expect_success 'grafting amended history using rebase -s theirs...' ' + git rebase -s theirs --onto origin origin^ master && + git diff amended HEAD' + +test_expect_success '... should allow a subsequent fast forward merge' ' + git checkout origin && + git merge master && + test $(git rev-parse origin) = $(git rev-parse master) +' + +test_done -- 1.5.5 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html