threads / discuss / 13974

Guided merge with override

Subject: Guided merge with override

## tl;dr

22 messages between Jun 16, 2008 and Jun 21, 2008.

replies: 21people: 7as markdown or json

Geoff Russell· Jun 16, 2008, 04:16 UTC · lore

I have a two repositories A and B. B is a tiny subset of the files in A and all have been modified. If I do a "git pull B" into A, I get conflicts. I always want to resolve these by accepting the version from B. Is there a magic "override" switch to let me do this?

Cheers, Geoff Russell

Miklos Vajna· Jun 16, 2008, 09:25 UTC · re: Geoff Russell · lore

Re: Guided merge with override

On Mon, Jun 16, 2008 at 01:46:38PM +0930, Geoff Russell <geoffrey.russell@gmail.com> wrote:
Show 6 quoted lines
> I have a two repositories A and B.  B is a tiny subset of the files in
> A and all have been
> modified.   If I do a "git pull B" into A, I get conflicts. I always
> want to resolve these
> by accepting the version from B. Is there a magic "override" switch to
> let me do this?
There was a thread about this:
http://thread.gmane.org/gmane.comp.version-control.git/84047
and then you can do a git pull -s theirs B.
Johannes Sixt· Jun 16, 2008, 10:16 UTC · re: Miklos Vajna · lore

Re: Guided merge with override

Miklos Vajna schrieb:
Show 13 quoted lines
> On Mon, Jun 16, 2008 at 01:46:38PM +0930, Geoff Russell <geoffrey.russell@gmail.com> wrote:
>> I have a two repositories A and B.  B is a tiny subset of the files in
>> A and all have been
>> modified.   If I do a "git pull B" into A, I get conflicts. I always
>> want to resolve these
>> by accepting the version from B. Is there a magic "override" switch to
>> let me do this?
> 
> There was a thread about this:
> 
> http://thread.gmane.org/gmane.comp.version-control.git/84047
> 
> and then you can do a git pull -s theirs B.

I don't think that's what Geoff needs. The 'theirs' strategy replaces the entire tree by 'their' - B's - tree. But IIUC, only the subset of files that are contained in B should be replaced by B's version, the rest of the files should remain unchanged. This is quite different from 'theirs' strategy.

The solution depends on whether *all* files in B should be taken, or only those files in B where there's a merge conflict. I don't know an easy way to do the former, but the latter I'd do like this:

	$ git diff --name-only | xargs git checkout B --
-- Hannes
Miklos Vajna· Jun 16, 2008, 21:16 UTC · re: Johannes Sixt · lore

Re: Guided merge with override

On Mon, Jun 16, 2008 at 12:16:12PM +0200, Johannes Sixt <j.sixt@viscovery.net> wrote:
> I don't think that's what Geoff needs. The 'theirs' strategy replaces the
> entire tree by 'their' - B's - tree. But IIUC, only the subset of files
> that are contained in B should be replaced by B's version, the rest of the
> files should remain unchanged. This is quite different from 'theirs' strategy.
Thanks, I missed that difference, and sorry for the wrong suggestion.
Sverre Rabbelier· Jun 16, 2008, 22:21 UTC · re: Johannes Sixt · lore

Re: Guided merge with override

On Mon, Jun 16, 2008 at 12:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
Show 5 quoted lines
> The solution depends on whether *all* files in B should be taken, or only
> those files in B where there's a merge conflict. I don't know an easy way
> to do the former, but the latter I'd do like this:
>
>        $ git diff --name-only | xargs git checkout B --

Wouldn't something similar work but do a 'git ls-files' and filter it on files that have a merge conflict?

-- 
Cheers,

Sverre Rabbelier
Geoff Russell· Jun 16, 2008, 22:45 UTC · re: Sverre Rabbelier · lore

Re: Guided merge with override

Thanks everybody,
On 6/17/08, Sverre Rabbelier <alturin@gmail.com> wrote:
Show 6 quoted lines
> On Mon, Jun 16, 2008 at 12:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>  > The solution depends on whether *all* files in B should be taken, or only
>  > those files in B where there's a merge conflict. I don't know an easy way
>  > to do the former, but the latter I'd do like this:
>  >
>  >        $ git diff --name-only | xargs git checkout B --

This looks like a manageable approach and better than the scripting I was thinking about -- ie. scan the conflict files with perl and fix them!

Cheers, Geoff

Show 11 quoted lines
>
>
> Wouldn't something similar work but do a 'git ls-files' and filter it
>  on files that have a merge conflict?
>
>  --
>  Cheers,
>
>
>  Sverre Rabbelier
>
-- 
6 Fifth Ave,
St Morris, S.A. 5068
Australia
Ph: 041 8805 184 / 08 8332 5069
Junio C Hamano· Jun 17, 2008, 20:04 UTC · re: Geoff Russell · lore

Re: Guided merge with override

"Geoff Russell" <geoffrey.russell@gmail.com> writes:
Show 12 quoted lines
> Thanks everybody,
>
> On 6/17/08, Sverre Rabbelier <alturin@gmail.com> wrote:
>> On Mon, Jun 16, 2008 at 12:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>>  > The solution depends on whether *all* files in B should be taken, or only
>>  > those files in B where there's a merge conflict. I don't know an easy way
>>  > to do the former, but the latter I'd do like this:
>>  >
>>  >        $ git diff --name-only | xargs git checkout B --
>
> This looks like a manageable approach and better than the scripting I was
> thinking about -- ie. scan the conflict files with perl and fix them!
Careful.

The above pipeline gives quite different result from "scan the conflict files with perl and fix them".

If the result you want from the "scan and fix the conflicts" approach is to take as much automerge as possible, and punt only on conflicting parts by discarding your work (side note: I hear this wish often and still I have not heard satisfactory explanation why people think that could be a sane result, though), the above pipeline is not what you want. It not only discards your work in conflicting parts but also all your work from even cleanly automerged parts of a path that has conflicts.

For example, suppose the original, our version and their version are like these respectively:

        (orig)    (ours)     (theirs)
        1         one        uno
        2         2          2
        3         3          3
        4         4          4
        5         5          5
        6         6          6
        7         7          7
        8         eight      8
If you merge these with natural 3-way merge, you would get this:
        $ git-merge-file -p ours orig theirs
        <<<<<<< ours
        one
        =======
        uno
        >>>>>>> theirs
        2
        3
        4
        5
        6
        7
        eight
Note: git-merge-file mimicks the "merge" program from RCS suite.  The
      three file parameters are mine, old and yours (in alphabetical order
      as easy-to-remember mnemonic) and means "update mine taking the
      change that you made to old to reach yours".

That's a conflict. The above suggested pipeline would give "their" version. That means the result does not have your change s/8/eight/.

It all depends on what you really mean by take "theirs", but you might have wanted to have this instead, with the "scan and fix the conflicts" approach you hinted in your message:

        $ git-merge-file -p --theirs ours orig theirs
        uno
        2
        3
        4
        5
        6
        7
        eight

That retains your change in non-conflicting part while favoring their change where conflicts are.

No, neither --theirs nor --ours option exists in your version of git. But here is a patch to add it to git-merge-file.

---
 builtin-merge-file.c |   10 ++++++++--
 xdiff/xdiff.h        |    8 +++++++-
 xdiff/xmerge.c       |   24 ++++++++++++++++--------
 3 files changed, 31 insertions(+), 11 deletions(-)
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 3605960..7d4ca8c 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -4,7 +4,7 @@
 #include "xdiff-interface.h"
 
 static const char merge_file_usage[] =
-"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+"git merge-file [-p | --stdout] [-q | --quiet] [--ours|--theirs] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
 
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
@@ -13,6 +13,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
 	int ret = 0, i = 0, to_stdout = 0;
+	int flags, favor = 0;
 
 	while (argc > 4) {
 		if (!strcmp(argv[1], "-L") && i < 3) {
@@ -25,6 +26,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-q") ||
 				!strcmp(argv[1], "--quiet"))
 			freopen("/dev/null", "w", stderr);
+		else if (!strcmp(argv[1], "--ours"))
+			favor = XDL_MERGE_FAVOR_OURS;
+		else if (!strcmp(argv[1], "--theirs"))
+			favor = XDL_MERGE_FAVOR_THEIRS;
 		else
 			usage(merge_file_usage);
 		argc--;
@@ -45,8 +50,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 					argv[i + 1]);
 	}
 
+	flags = XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS_ALNUM, favor);
 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-			&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
+			&xpp, flags, &result);
 
 	for (i = 0; i < 3; i++)
 		free(mmfs[i].ptr);
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 413082e..d40cf21 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -99,9 +99,15 @@ long xdl_mmfile_size(mmfile_t *mmf);
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 
+#define XDL_MERGE_FAVOR_OURS		1
+#define XDL_MERGE_FAVOR_THEIRS		2
+#define XDL_MERGE_FAVOR(flag)		(((flag)>>4) & 03)
+#define XDL_MERGE_LEVEL(flag)		((flag) & 07)
+#define XDL_MERGE_FLAGS(level,flag)	((level) | ((flag)<<4))
+
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result);
+		xpparam_t const *xpp, int flag, mmbuffer_t *result);
 
 #ifdef __cplusplus
 }
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 82b3573..88c29ae 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -114,7 +114,9 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 }
 
 static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
-		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+				 xdfenv_t *xe2, const char *name2,
+				 int favor,
+				 xdmerge_t *m, char *dest)
 {
 	const int marker_size = 7;
 	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
@@ -124,6 +126,9 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
 	int size, i1, j;
 
 	for (size = i1 = 0; m; m = m->next) {
+		if (favor && !m->mode)
+			m->mode = favor;
+
 		if (m->mode == 0) {
 			size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0,
 					dest ? dest + size : NULL);
@@ -322,8 +327,9 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
  * returns < 0 on error, == 0 for no conflicts, else number of conflicts
  */
 static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
-		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-		int level, xpparam_t const *xpp, mmbuffer_t *result) {
+			xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
+			int level, int favor,
+			xpparam_t const *xpp, mmbuffer_t *result) {
 	xdmerge_t *changes, *c;
 	int i1, i2, chg1, chg2;
 
@@ -430,25 +436,27 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	/* output */
 	if (result) {
 		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
-			changes, NULL);
+						 favor, changes, NULL);
 		result->ptr = xdl_malloc(size);
 		if (!result->ptr) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
 		result->size = size;
-		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
-				result->ptr);
+		xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+				      favor, changes, result->ptr);
 	}
 	return xdl_cleanup_merge(changes);
 }
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result) {
+		xpparam_t const *xpp, int flag, mmbuffer_t *result) {
 	xdchange_t *xscr1, *xscr2;
 	xdfenv_t xe1, xe2;
 	int status;
+	int level = XDL_MERGE_LEVEL(flag);
+	int favor = XDL_MERGE_FAVOR(flag);
 
 	result->ptr = NULL;
 	result->size = 0;
@@ -482,7 +490,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		} else {
 			status = xdl_do_merge(&xe1, xscr1, name1,
 					      &xe2, xscr2, name2,
-					      level, xpp, result);
+					      level, favor, xpp, result);
 		}
 		xdl_free_script(xscr1);
 		xdl_free_script(xscr2);
Johannes Schindelin· Jun 18, 2008, 15:19 UTC · re: Junio C Hamano · lore

Re: Guided merge with override

Hi,
On Tue, 17 Jun 2008, Junio C Hamano wrote:
> No, neither --theirs nor --ours option exists in your version of git. 
> But here is a patch to add it to git-merge-file.

Looks good, thanks! Just to be safe, this should be accompanied by tet cases. But I did not see anything wrong with the patch.

Ciao, Dscho

Johannes Schindelin· Jun 18, 2008, 15:28 UTC · re: Johannes Schindelin · lore

Re: Guided merge with override

Hi,
On Wed, 18 Jun 2008, Johannes Schindelin wrote:
Show 7 quoted lines
> On Tue, 17 Jun 2008, Junio C Hamano wrote:
> 
> > No, neither --theirs nor --ours option exists in your version of git. 
> > But here is a patch to add it to git-merge-file.
> 
> Looks good, thanks!  Just to be safe, this should be accompanied by tet 
> cases.  But I did not see anything wrong with the patch.

Thinking about this again, there could be a problem: in case of complex merges, it is possible that the sides are switched around for an intermediate merge. IOW you'd expect it to take "theirs", but it really takes "ours".

Hrm.

Ciao, Dscho

Junio C Hamano· Jun 18, 2008, 16:31 UTC · re: Johannes Schindelin · lore

Re: Guided merge with override

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Thinking about this again, there could be a problem: in case of complex 
> merges, it is possible that the sides are switched around for an 
> intermediate merge.  IOW you'd expect it to take "theirs", but it really 
> takes "ours".
Are you thinking about using this in merge-recursive?

I do not think there is any reason to use this during intermediate merges done inside merge-recursive. The point of recursive merge is to create a neutral intermediate merge result, with conflicts and all. Do this only during the final round and you are fine (for some definition of "fine" --- I still have not heard a convincing argument as to why it is even a good thing to be able to take one side for only parts that did conflict, while taking the change from the other side in places that did not).

Johannes Schindelin· Jun 18, 2008, 19:29 UTC · re: Junio C Hamano · lore

Re: Guided merge with override

Hi,
On Wed, 18 Jun 2008, Junio C Hamano wrote:
Show 8 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > Thinking about this again, there could be a problem: in case of complex 
> > merges, it is possible that the sides are switched around for an 
> > intermediate merge.  IOW you'd expect it to take "theirs", but it really 
> > takes "ours".
> 
> Are you thinking about using this in merge-recursive?
Indeed I was.
> I do not think there is any reason to use this during intermediate 
> merges done inside merge-recursive.

That is right, but for some stupid reason I did not realize that git-merge-file is not even called by merge-recursive.

So the accompanying patch for merge-recursive would use the --theirs or --ours logic only in the !index_only case, i.e. the final merge.

And I guess we'd have similar logic as for merge-subtree, introducing merge-recursive-ours and merge-recursive-theirs.

Very nice.

Ciao, Dscho

Junio C Hamano· Jun 20, 2008, 07:38 UTC · re: Junio C Hamano · lore

[PATCH 1/2] git-merge-file --ours, --theirs

Often people want their conflicting merges autoresolved by favouring upstream changes (or their own --- it's the same thing), and hinted to run "git diff --name-only | xargs git checkout MERGE_HEAD --". This is essentially to accept automerge results for the paths that are fully resolved automatically while taking their version of the file in full for paths that have conflicts.

This is problematic on two counts.

One problem is that this is not exactly what these people want. They usually want to salvage as much automerge result as possible. In particular, they want to keep autoresolved parts in conflicting paths, as well as the paths that are fully autoresolved.

This patch teaches two new modes of operation to the lowest-lever merge machinery, xdl_merge(). Instead of leaving the conflicted lines from both sides enclosed in <<<, ===, and >>> markers, you can tell the conflicts to be resolved favouring your side or their side of changes.

A larger problem is that this tends to encourage a bad workflow by allowing them to record such a mixed up half-merge result as a full commit without auditing. This commit does not tackle this latter issue. In git, we usually give long enough rope to users with strange wishes as long as the risky features is not on by default.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 * Essentially the same patch but with documentation.
 Documentation/git-merge-file.txt |   12 ++++++++++--
 builtin-merge-file.c             |   10 ++++++++--
 xdiff/xdiff.h                    |    8 +++++++-
 xdiff/xmerge.c                   |   24 ++++++++++++++++--------
 4 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 149f131..87e07d3 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -10,7 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git-merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
-	[-p|--stdout] [-q|--quiet] <current-file> <base-file> <other-file>
+	[--ours|--theirs] [-p|--stdout] [-q|--quiet]
+	<current-file> <base-file> <other-file>
 
 
 DESCRIPTION
@@ -34,7 +35,9 @@ normally outputs a warning and brackets the conflict with <<<<<<< and
 	>>>>>>> B
 
 If there are conflicts, the user should edit the result and delete one of
-the alternatives.
+the alternatives.  When `--ours` or `--theirs` option is in effect, however,
+these conflicts are resolved favouring lines from `<current-file>` or
+lines from `<other-file>` respectively.
 
 The exit value of this program is negative on error, and the number of
 conflicts otherwise. If the merge was clean, the exit value is 0.
@@ -62,6 +65,11 @@ OPTIONS
 -q::
 	Quiet;  do  not  warn about conflicts.
 
+--ours::
+--theirs::
+	Instead of leaving conflicts in the file, resolve conflicts
+	favouring our (or their) side of the lines.
+
 
 EXAMPLES
 --------
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 3605960..7d4ca8c 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -4,7 +4,7 @@
 #include "xdiff-interface.h"
 
 static const char merge_file_usage[] =
-"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+"git merge-file [-p | --stdout] [-q | --quiet] [--ours|--theirs] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
 
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
@@ -13,6 +13,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
 	int ret = 0, i = 0, to_stdout = 0;
+	int flags, favor = 0;
 
 	while (argc > 4) {
 		if (!strcmp(argv[1], "-L") && i < 3) {
@@ -25,6 +26,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-q") ||
 				!strcmp(argv[1], "--quiet"))
 			freopen("/dev/null", "w", stderr);
+		else if (!strcmp(argv[1], "--ours"))
+			favor = XDL_MERGE_FAVOR_OURS;
+		else if (!strcmp(argv[1], "--theirs"))
+			favor = XDL_MERGE_FAVOR_THEIRS;
 		else
 			usage(merge_file_usage);
 		argc--;
@@ -45,8 +50,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 					argv[i + 1]);
 	}
 
+	flags = XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS_ALNUM, favor);
 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-			&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
+			&xpp, flags, &result);
 
 	for (i = 0; i < 3; i++)
 		free(mmfs[i].ptr);
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 413082e..d40cf21 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -99,9 +99,15 @@ long xdl_mmfile_size(mmfile_t *mmf);
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 
+#define XDL_MERGE_FAVOR_OURS		1
+#define XDL_MERGE_FAVOR_THEIRS		2
+#define XDL_MERGE_FAVOR(flag)		(((flag)>>4) & 03)
+#define XDL_MERGE_LEVEL(flag)		((flag) & 07)
+#define XDL_MERGE_FLAGS(level,flag)	((level) | ((flag)<<4))
+
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result);
+		xpparam_t const *xpp, int flag, mmbuffer_t *result);
 
 #ifdef __cplusplus
 }
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 82b3573..88c29ae 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -114,7 +114,9 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 }
 
 static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
-		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+				 xdfenv_t *xe2, const char *name2,
+				 int favor,
+				 xdmerge_t *m, char *dest)
 {
 	const int marker_size = 7;
 	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
@@ -124,6 +126,9 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
 	int size, i1, j;
 
 	for (size = i1 = 0; m; m = m->next) {
+		if (favor && !m->mode)
+			m->mode = favor;
+
 		if (m->mode == 0) {
 			size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0,
 					dest ? dest + size : NULL);
@@ -322,8 +327,9 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
  * returns < 0 on error, == 0 for no conflicts, else number of conflicts
  */
 static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
-		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-		int level, xpparam_t const *xpp, mmbuffer_t *result) {
+			xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
+			int level, int favor,
+			xpparam_t const *xpp, mmbuffer_t *result) {
 	xdmerge_t *changes, *c;
 	int i1, i2, chg1, chg2;
 
@@ -430,25 +436,27 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	/* output */
 	if (result) {
 		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
-			changes, NULL);
+						 favor, changes, NULL);
 		result->ptr = xdl_malloc(size);
 		if (!result->ptr) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
 		result->size = size;
-		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
-				result->ptr);
+		xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+				      favor, changes, result->ptr);
 	}
 	return xdl_cleanup_merge(changes);
 }
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result) {
+		xpparam_t const *xpp, int flag, mmbuffer_t *result) {
 	xdchange_t *xscr1, *xscr2;
 	xdfenv_t xe1, xe2;
 	int status;
+	int level = XDL_MERGE_LEVEL(flag);
+	int favor = XDL_MERGE_FAVOR(flag);
 
 	result->ptr = NULL;
 	result->size = 0;
@@ -482,7 +490,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		} else {
 			status = xdl_do_merge(&xe1, xscr1, name1,
 					      &xe2, xscr2, name2,
-					      level, xpp, result);
+					      level, favor, xpp, result);
 		}
 		xdl_free_script(xscr1);
 		xdl_free_script(xscr2);
-- 
1.5.6.6.gd3e97
Junio C Hamano· Jun 20, 2008, 07:48 UTC · re: Junio C Hamano · lore

[PATCH 2/2] git-merge-recursive-{ours,theirs}

This uses the low-level mechanism for "ours" and "theirs" autoresolution introduced by the previous commit to introduce two additional merge strategies, merge-recursive-ours and merge-recursive-theirs.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Makefile                     |    3 ++
 builtin-merge-recursive.c    |   37 ++++++++++++++++++++++++---
 git-merge.sh                 |    3 +-
 git.c                        |    2 +
 ll-merge.c                   |   24 +++++++++++------
 ll-merge.h                   |    4 ++-
 t/t6034-merge-ours-theirs.sh |   56 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 114 insertions(+), 15 deletions(-)
 create mode 100755 t/t6034-merge-ours-theirs.sh
diff --git a/Makefile b/Makefile
index b003e3e..82d2892 100644
--- a/Makefile
+++ b/Makefile
@@ -304,6 +304,8 @@ BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-get-tar-commit-id$X
 BUILT_INS += git-init$X
+BUILT_INS += git-merge-recursive-ours$X
+BUILT_INS += git-merge-recursive-theirs$X
 BUILT_INS += git-merge-subtree$X
 BUILT_INS += git-peek-remote$X
 BUILT_INS += git-repo-config$X
@@ -1381,6 +1383,7 @@ check-docs::
 	do \
 		case "$$v" in \
 		git-merge-octopus | git-merge-ours | git-merge-recursive | \
+		git-merge-recursive-ours | git-merge-recursive-theirs | \
 		git-merge-resolve | git-merge-stupid | git-merge-subtree | \
 		git-fsck-objects | git-init-db | \
 		git-?*--?* ) continue ;; \
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 4aa28a1..a355e7a 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -20,7 +20,11 @@
 #include "attr.h"
 #include "merge-recursive.h"
 
-static int subtree_merge;
+static enum {
+	MERGE_RECURSIVE_SUBTREE = 1,
+	MERGE_RECURSIVE_OURS,
+	MERGE_RECURSIVE_THEIRS,
+} merge_recursive_variants;
 
 static struct tree *shift_tree_object(struct tree *one, struct tree *two)
 {
@@ -642,6 +646,7 @@ static int merge_3way(mmbuffer_t *result_buf,
 	mmfile_t orig, src1, src2;
 	char *name1, *name2;
 	int merge_status;
+	int flag, favor;
 
 	name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
 	name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -650,9 +655,26 @@ static int merge_3way(mmbuffer_t *result_buf,
 	fill_mm(a->sha1, &src1);
 	fill_mm(b->sha1, &src2);
 
+	if (index_only)
+		favor = 0;
+	else {
+		switch (merge_recursive_variants) {
+		case MERGE_RECURSIVE_OURS:
+			favor = XDL_MERGE_FAVOR_OURS;
+			break;
+		case MERGE_RECURSIVE_THEIRS:
+			favor = XDL_MERGE_FAVOR_THEIRS;
+			break;
+		default:
+			favor = 0;
+			break;
+		}
+	}
+	flag = LL_MERGE_FLAGS(index_only, favor);
+
 	merge_status = ll_merge(result_buf, a->path, &orig,
 				&src1, name1, &src2, name2,
-				index_only);
+				flag);
 
 	free(name1);
 	free(name2);
@@ -1171,7 +1193,7 @@ int merge_trees(struct tree *head,
 {
 	int code, clean;
 
-	if (subtree_merge) {
+	if (merge_recursive_variants == MERGE_RECURSIVE_SUBTREE) {
 		merge = shift_tree_object(head, merge);
 		common = shift_tree_object(head, common);
 	}
@@ -1379,11 +1401,18 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 	int index_fd;
 
+	merge_recursive_variants = 0;
 	if (argv[0]) {
 		int namelen = strlen(argv[0]);
 		if (8 < namelen &&
 		    !strcmp(argv[0] + namelen - 8, "-subtree"))
-			subtree_merge = 1;
+			merge_recursive_variants = MERGE_RECURSIVE_SUBTREE;
+		else if (5 < namelen &&
+			 !strcmp(argv[0] + namelen - 5, "-ours"))
+			merge_recursive_variants = MERGE_RECURSIVE_OURS;
+		else if (7 < namelen &&
+			 !strcmp(argv[0] + namelen - 7, "-theirs"))
+			merge_recursive_variants = MERGE_RECURSIVE_THEIRS;
 	}
 
 	git_config(merge_config, NULL);
diff --git a/git-merge.sh b/git-merge.sh
index 8026ccf..39b5cd9 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -31,10 +31,11 @@ LF='
 '
 
 all_strategies='recur recursive octopus resolve stupid ours subtree'
+all_strategies="$all_strategies recursive-ours recursive-theirs"
 default_twohead_strategies='recursive'
 default_octopus_strategies='octopus'
 no_fast_forward_strategies='subtree ours'
-no_trivial_strategies='recursive recur subtree ours'
+no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
 use_strategies=
 
 allow_fast_forward=t
diff --git a/git.c b/git.c
index 59f0fcc..44cb8eb 100644
--- a/git.c
+++ b/git.c
@@ -328,6 +328,8 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
 		{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+		{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+		{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
diff --git a/ll-merge.c b/ll-merge.c
index 9837c84..c6a05bf 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -19,7 +19,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
 			   mmfile_t *orig,
 			   mmfile_t *src1, const char *name1,
 			   mmfile_t *src2, const char *name2,
-			   int virtual_ancestor);
+			   int flag);
 
 struct ll_merge_driver {
 	const char *name;
@@ -39,13 +39,15 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
 			   mmfile_t *orig,
 			   mmfile_t *src1, const char *name1,
 			   mmfile_t *src2, const char *name2,
-			   int virtual_ancestor)
+			   int flag)
 {
 	/*
 	 * The tentative merge result is "ours" for the final round,
 	 * or common ancestor for an internal merge.  Still return
 	 * "conflicted merge" status.
 	 */
+	int virtual_ancestor = flag & 01;
+
 	mmfile_t *stolen = virtual_ancestor ? orig : src1;
 
 	result->ptr = stolen->ptr;
@@ -60,9 +62,10 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 			mmfile_t *orig,
 			mmfile_t *src1, const char *name1,
 			mmfile_t *src2, const char *name2,
-			int virtual_ancestor)
+			int flag)
 {
 	xpparam_t xpp;
+	int favor = ((flag)>>1) & 03;
 
 	if (buffer_is_binary(orig->ptr, orig->size) ||
 	    buffer_is_binary(src1->ptr, src1->size) ||
@@ -73,14 +76,15 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 				       path_unused,
 				       orig, src1, name1,
 				       src2, name2,
-				       virtual_ancestor);
+				       flag);
 	}
 
 	memset(&xpp, 0, sizeof(xpp));
 	return xdl_merge(orig,
 			 src1, name1,
 			 src2, name2,
-			 &xpp, XDL_MERGE_ZEALOUS,
+			 &xpp,
+			 XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, favor),
 			 result);
 }
 
@@ -90,11 +94,12 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
 			  mmfile_t *orig,
 			  mmfile_t *src1, const char *name1,
 			  mmfile_t *src2, const char *name2,
-			  int virtual_ancestor)
+			  int flag)
 {
 	char *src, *dst;
 	long size;
 	const int marker_size = 7;
+	int virtual_ancestor = flag & 01;
 
 	int status = ll_xdl_merge(drv_unused, result, path_unused,
 				  orig, src1, NULL, src2, NULL,
@@ -158,7 +163,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
 			mmfile_t *orig,
 			mmfile_t *src1, const char *name1,
 			mmfile_t *src2, const char *name2,
-			int virtual_ancestor)
+			int flag)
 {
 	char temp[3][50];
 	char cmdbuf[2048];
@@ -362,10 +367,11 @@ int ll_merge(mmbuffer_t *result_buf,
 	     mmfile_t *ancestor,
 	     mmfile_t *ours, const char *our_label,
 	     mmfile_t *theirs, const char *their_label,
-	     int virtual_ancestor)
+	     int flag)
 {
 	const char *ll_driver_name;
 	const struct ll_merge_driver *driver;
+	int virtual_ancestor = flag & 01;
 
 	ll_driver_name = git_path_check_merge(path);
 	driver = find_ll_merge_driver(ll_driver_name);
@@ -375,5 +381,5 @@ int ll_merge(mmbuffer_t *result_buf,
 	return driver->fn(driver, result_buf, path,
 			  ancestor,
 			  ours, our_label,
-			  theirs, their_label, virtual_ancestor);
+			  theirs, their_label, flag);
 }
diff --git a/ll-merge.h b/ll-merge.h
index 5388422..5daef58 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -5,11 +5,13 @@
 #ifndef LL_MERGE_H
 #define LL_MERGE_H
 
+#define LL_MERGE_FLAGS(virtual_ancestor,favor) ((!!(virtual_ancestor)) | ((favor)<<1))
+
 int ll_merge(mmbuffer_t *result_buf,
 	     const char *path,
 	     mmfile_t *ancestor,
 	     mmfile_t *ours, const char *our_label,
 	     mmfile_t *theirs, const char *their_label,
-	     int virtual_ancestor);
+	     int flag);
 
 #endif
diff --git a/t/t6034-merge-ours-theirs.sh b/t/t6034-merge-ours-theirs.sh
new file mode 100755
index 0000000..56a9247
--- /dev/null
+++ b/t/t6034-merge-ours-theirs.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='Merge-recursive ours and theirs variants'
+. ./test-lib.sh
+
+test_expect_success setup '
+	for i in 1 2 3 4 5 6 7 8 9
+	do
+		echo "$i"
+	done >file &&
+	git add file &&
+	cp file elif &&
+	git commit -m initial &&
+
+	sed -e "s/1/one/" -e "s/9/nine/" >file <elif &&
+	git commit -a -m ours &&
+
+	git checkout -b side HEAD^ &&
+
+	sed -e "s/9/nueve/" >file <elif &&
+	git commit -a -m theirs &&
+
+	git checkout master^0
+'
+
+test_expect_success 'plain recursive - should conflict' '
+	git reset --hard master &&
+	test_must_fail git merge -s recursive side &&
+	grep nine file &&
+	grep nueve file &&
+	! grep 9 file &&
+	grep one file &&
+	! grep 1 file
+'
+
+test_expect_success 'recursive favouring theirs' '
+	git reset --hard master &&
+	git merge -s recursive-theirs side &&
+	! grep nine file &&
+	grep nueve file &&
+	! grep 9 file &&
+	grep one file &&
+	! grep 1 file
+'
+
+test_expect_success 'recursive favouring ours' '
+	git reset --hard master &&
+	git merge -s recursive-ours side &&
+	grep nine file &&
+	! grep nueve file &&
+	! grep 9 file &&
+	grep one file &&
+	! grep 1 file
+'
+
+test_done
-- 
1.5.6.6.gd3e97
Johannes Schindelin· Jun 20, 2008, 12:58 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/2] git-merge-recursive-{ours,theirs}

Hi,
On Fri, 20 Jun 2008, Junio C Hamano wrote:
Show 22 quoted lines
> diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
> index 4aa28a1..a355e7a 100644
> --- a/builtin-merge-recursive.c
> +++ b/builtin-merge-recursive.c
> @@ -650,9 +655,26 @@ static int merge_3way(mmbuffer_t *result_buf,
>  	fill_mm(a->sha1, &src1);
>  	fill_mm(b->sha1, &src2);
>  
> +	if (index_only)
> +		favor = 0;
> +	else {
> +		switch (merge_recursive_variants) {
> +		case MERGE_RECURSIVE_OURS:
> +			favor = XDL_MERGE_FAVOR_OURS;
> +			break;
> +		case MERGE_RECURSIVE_THEIRS:
> +			favor = XDL_MERGE_FAVOR_THEIRS;
> +			break;
> +		default:
> +			favor = 0;
> +			break;
> +		}
Hrm.  I would have preferred something like this:
	if (!index_only && merge_recursive_variants == MERGE_RECURSIVE_OURS)
		favor = XDL_MERGE_FAVOR_OURS;
	if (!index_only && merge_recursive_variants == MERGE_RECURSIVE_THEIRS)
		favor = XDL_MERGE_FAVOR_THEIRS;
	else
		favor = 0;
Show 7 quoted lines
> +	}
> +	flag = LL_MERGE_FLAGS(index_only, favor);
> +
>  	merge_status = ll_merge(result_buf, a->path, &orig,
>  				&src1, name1, &src2, name2,
> -				index_only);
> +				flag);

Sorry, but in my opinion this flag mangling makes the whole code uglier. Why not just add another parameter?

Or if you are really concerned about future enhancements to ll_merge(), use a struct.

Show 17 quoted lines
> @@ -1379,11 +1401,18 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
>  	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
>  	int index_fd;
>  
> +	merge_recursive_variants = 0;
>  	if (argv[0]) {
>  		int namelen = strlen(argv[0]);
>  		if (8 < namelen &&
>  		    !strcmp(argv[0] + namelen - 8, "-subtree"))
> -			subtree_merge = 1;
> +			merge_recursive_variants = MERGE_RECURSIVE_SUBTREE;
> +		else if (5 < namelen &&
> +			 !strcmp(argv[0] + namelen - 5, "-ours"))
> +			merge_recursive_variants = MERGE_RECURSIVE_OURS;
> +		else if (7 < namelen &&
> +			 !strcmp(argv[0] + namelen - 7, "-theirs"))
> +			merge_recursive_variants = MERGE_RECURSIVE_THEIRS;
This just cries out loud for a new function suffixcmp().

I will not say anything about the long lines in git-merge.sh, since I fully expect builtin-merge to happen Real Soon Now.

Anyhow, your comments about this driving the wrong workflow still apply. Maybe we want to display them really, really prominently in Documentation/merge-strategies.txt.

Ciao, Dscho

Junio C Hamano· Jun 21, 2008, 09:46 UTC · re: Johannes Schindelin · lore

Re: [PATCH 2/2] git-merge-recursive-{ours,theirs}

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 19 quoted lines
>> @@ -1379,11 +1401,18 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
>>  	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
>>  	int index_fd;
>>  
>> +	merge_recursive_variants = 0;
>>  	if (argv[0]) {
>>  		int namelen = strlen(argv[0]);
>>  		if (8 < namelen &&
>>  		    !strcmp(argv[0] + namelen - 8, "-subtree"))
>> -			subtree_merge = 1;
>> +			merge_recursive_variants = MERGE_RECURSIVE_SUBTREE;
>> +		else if (5 < namelen &&
>> +			 !strcmp(argv[0] + namelen - 5, "-ours"))
>> +			merge_recursive_variants = MERGE_RECURSIVE_OURS;
>> +		else if (7 < namelen &&
>> +			 !strcmp(argv[0] + namelen - 7, "-theirs"))
>> +			merge_recursive_variants = MERGE_RECURSIVE_THEIRS;
>
> This just cries out loud for a new function suffixcmp().

Actually, I think "git-merge-recursive-theirs" is a mistake. We should bite the bullet and give "git-merge" an ability to pass backend specific parameters to "git-merge-recursive". The new convention could be that anything that begins with -X is passed to the backend.

E.g.
	git merge -Xfavor=theirs foo
        git merge -Xsubtree=/=gitk-git paulus

As you noticed already, subtree is just a funny optional behaviour attached to recursive, so are theirs and ours. The above two would invoke git-merge-recursive like so:

	git merge-recursive -Xfavor=theirs <base> -- HEAD MERGE_HEAD
	git merge-recursive -Xsubtree=/=gitk-git <base> -- HEAD MERGE_HEAD
We could even mix these two if we are ambitious.
Johannes Schindelin· Jun 21, 2008, 16:29 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/2] git-merge-recursive-{ours,theirs}

Hi,
On Sat, 21 Jun 2008, Junio C Hamano wrote:
Show 25 quoted lines
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> @@ -1379,11 +1401,18 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
> >>  	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
> >>  	int index_fd;
> >>  
> >> +	merge_recursive_variants = 0;
> >>  	if (argv[0]) {
> >>  		int namelen = strlen(argv[0]);
> >>  		if (8 < namelen &&
> >>  		    !strcmp(argv[0] + namelen - 8, "-subtree"))
> >> -			subtree_merge = 1;
> >> +			merge_recursive_variants = MERGE_RECURSIVE_SUBTREE;
> >> +		else if (5 < namelen &&
> >> +			 !strcmp(argv[0] + namelen - 5, "-ours"))
> >> +			merge_recursive_variants = MERGE_RECURSIVE_OURS;
> >> +		else if (7 < namelen &&
> >> +			 !strcmp(argv[0] + namelen - 7, "-theirs"))
> >> +			merge_recursive_variants = MERGE_RECURSIVE_THEIRS;
> >
> > This just cries out loud for a new function suffixcmp().
> 
> Actually, I think "git-merge-recursive-theirs" is a mistake.  We should
> bite the bullet and give "git-merge" an ability to pass backend specific
> parameters to "git-merge-recursive".
Fair enough.
Show 16 quoted lines
> The new convention could be that anything that begins with -X is passed 
> to the backend.
> 
> E.g.
> 
> 	git merge -Xfavor=theirs foo
>         git merge -Xsubtree=/=gitk-git paulus
> 
> As you noticed already, subtree is just a funny optional behaviour
> attached to recursive, so are theirs and ours.  The above two would invoke
> git-merge-recursive like so:
> 
> 	git merge-recursive -Xfavor=theirs <base> -- HEAD MERGE_HEAD
> 	git merge-recursive -Xsubtree=/=gitk-git <base> -- HEAD MERGE_HEAD
> 
> We could even mix these two if we are ambitious.
Looks fine to me.  And much cleaner than the hardlinking.

Ciao, Dscho

Jakub Narebski· Jun 21, 2008, 16:56 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/2] git-merge-recursive-{ours,theirs}

Junio C Hamano <gitster@pobox.com> writes:
Show 9 quoted lines
> Actually, I think "git-merge-recursive-theirs" is a mistake.  We should
> bite the bullet and give "git-merge" an ability to pass backend specific
> parameters to "git-merge-recursive".  The new convention could be that
> anything that begins with -X is passed to the backend.
> 
> E.g.
> 
> 	git merge -Xfavor=theirs foo
>       git merge -Xsubtree=/=gitk-git paulus

Gaaah... only after reading it for third time I see that it isn't some funky "=/=" symbol, but subtree with grafing '/' in one side to 'gitk-git' subdirectory in other side.

-- 
Jakub Narebski
Poland
ShadeHawk on #git
Johannes Sixt· Jun 17, 2008, 06:16 UTC · re: Sverre Rabbelier · lore

Re: Guided merge with override

Sverre Rabbelier schrieb:
Show 9 quoted lines
> On Mon, Jun 16, 2008 at 12:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>> The solution depends on whether *all* files in B should be taken, or only
>> those files in B where there's a merge conflict. I don't know an easy way
>> to do the former, but the latter I'd do like this:
>>
>>        $ git diff --name-only | xargs git checkout B --
> 
> Wouldn't something similar work but do a 'git ls-files' and filter it
> on files that have a merge conflict?

Well, you could 'git ls-files --unmerged', but that prints the whole index entry, not just the name, and then you need a more complicated pipeline.

-- Hannes
Sverre Rabbelier· Jun 17, 2008, 08:53 UTC · re: Johannes Sixt · lore

Re: Guided merge with override

On Tue, Jun 17, 2008 at 8:16 AM, Johannes Sixt <j.sixt@viscovery.net> wrote:
Show 13 quoted lines
> Sverre Rabbelier schrieb:
>> On Mon, Jun 16, 2008 at 12:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>>> The solution depends on whether *all* files in B should be taken, or only
>>> those files in B where there's a merge conflict. I don't know an easy way
>>> to do the former, but the latter I'd do like this:
>>>
>>>        $ git diff --name-only | xargs git checkout B --
>>
>> Wouldn't something similar work but do a 'git ls-files' and filter it
>> on files that have a merge conflict?
>
> Well, you could 'git ls-files --unmerged', but that prints the whole index
> entry, not just the name, and then you need a more complicated pipeline.

How about 'git ls-files -t | grep "^M " | xargs git checkout B --', that would list all files that are unmerged and check them out?

-- 
Cheers,

Sverre Rabbelier
Johannes Schindelin· Jun 17, 2008, 09:48 UTC · re: Sverre Rabbelier · lore

Re: Guided merge with override

Hi,
On Tue, 17 Jun 2008, Sverre Rabbelier wrote:
> How about 'git ls-files -t | grep "^M " | xargs git checkout B --',
You probably meant 'sed -n "s/^M //p"' instead of 'grep "^M"', right?

Of course, I think that a better way would be to ask "git diff --name-only --diff-filter=U".

Ciao, Dscho

Sverre Rabbelier· Jun 17, 2008, 09:53 UTC · re: Johannes Schindelin · lore

Re: Guided merge with override

On Tue, Jun 17, 2008 at 11:48 AM, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

> On Tue, 17 Jun 2008, Sverre Rabbelier wrote:
>> How about 'git ls-files -t | grep "^M " | xargs git checkout B --',
>
> You probably meant 'sed -n "s/^M //p"' instead of 'grep "^M"', right?
Whoops, yes, that would be a lot better :P.
> Of course, I think that a better way would be to ask "git diff
> --name-only --diff-filter=U".

Hmmm, not having an unfinished merge at hand, would that require the sed on top too?

-- 
Cheers,

Sverre Rabbelier
Johannes Schindelin· Jun 17, 2008, 10:17 UTC · re: Sverre Rabbelier · lore

Re: Guided merge with override

Hi,
On Tue, 17 Jun 2008, Sverre Rabbelier wrote:
Show 8 quoted lines
> On Tue, Jun 17, 2008 at 11:48 AM, Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
>
> > Of course, I think that a better way would be to ask "git diff 
> > --name-only --diff-filter=U".
> 
> Hmmm, not having an unfinished merge at hand, would that require the
> sed on top too?
Nope.  The "--name-only" says it will print only the name.

Ciao, Dscho

← back to recent threads