[PATCH 0/8] The return of -Xours, -Xtheirs, -Xsubtree=dir

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[PATCH 0/8] The return of -Xours, -Xtheirs, -Xsubtree=dir

Avery Pennarun
As discussed earlier today, this brings back Junio's earlier patch series
that introduced (and then used) a -X option for configuring merge
strategies.  My favourite use of this is -Xsubtree=<dir>, which lets you
provide the actual subdir prefix when using the subtree merge strategy.

Avery Pennarun (8):
  git-merge-file --ours, --theirs
  builtin-merge.c: call exclude_cmds() correctly.
  git-merge-recursive-{ours,theirs}
  Teach git-merge to pass -X<option> to the backend strategy module
  Teach git-pull to pass -X<option> to git-merge
  Make "subtree" part more orthogonal to the rest of merge-recursive.
  Extend merge-subtree tests to test -Xsubtree=dir.
  Document that merge strategies can now take their own options

 .gitignore                         |    2 +
 Documentation/git-merge-file.txt   |   12 +++++-
 Documentation/merge-options.txt    |    4 ++
 Documentation/merge-strategies.txt |   29 ++++++++++++++-
 builtin-checkout.c                 |    2 +-
 builtin-merge-file.c               |    5 ++-
 builtin-merge-recursive.c          |   24 ++++++++++---
 builtin-merge.c                    |   44 +++++++++++++++++++++--
 cache.h                            |    1 +
 contrib/examples/git-merge.sh      |    3 +-
 git-compat-util.h                  |    1 +
 git-pull.sh                        |   17 ++++++++-
 git.c                              |    2 +
 ll-merge.c                         |   20 +++++-----
 ll-merge.h                         |    2 +-
 match-trees.c                      |   69 +++++++++++++++++++++++++++++++++++-
 merge-recursive.c                  |   35 +++++++++++++++---
 merge-recursive.h                  |    7 +++-
 strbuf.c                           |    9 +++++
 t/t6029-merge-subtree.sh           |   47 ++++++++++++++++++++++++-
 t/t6034-merge-ours-theirs.sh       |   64 +++++++++++++++++++++++++++++++++
 xdiff/xdiff.h                      |    7 +++-
 xdiff/xmerge.c                     |   11 +++++-
 23 files changed, 377 insertions(+), 40 deletions(-)
 create mode 100755 t/t6034-merge-ours-theirs.sh


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

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

Avery Pennarun
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.

(Patch originally by Junio Hamano <[hidden email]>.)

Signed-off-by: Avery Pennarun <[hidden email]>
---
 Documentation/git-merge-file.txt |   12 ++++++++++--
 builtin-merge-file.c             |    5 ++++-
 xdiff/xdiff.h                    |    7 ++++++-
 xdiff/xmerge.c                   |   11 +++++++++--
 4 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 3035373..b9d2276 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 lines containing
  >>>>>>> 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 afd2ea7..8f22aa8 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -29,11 +29,14 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
  int ret = 0, i = 0, to_stdout = 0;
  int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
  int merge_style = 0, quiet = 0;
+ int merge_favor = 0;
  int nongit;
 
  struct option options[] = {
  OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
  OPT_SET_INT(0, "diff3", &merge_style, "use a diff3 based merge", XDL_MERGE_DIFF3),
+ OPT_SET_INT(0, "ours", &merge_favor, "for conflicts, use our version", XDL_MERGE_FAVOR_OURS),
+ OPT_SET_INT(0, "theirs", &merge_favor, "for conflicts, use their version", XDL_MERGE_FAVOR_THEIRS),
  OPT__QUIET(&quiet),
  OPT_CALLBACK('L', NULL, names, "name",
      "set labels for file1/orig_file/file2", &label_cb),
@@ -68,7 +71,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
  }
 
  ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
- &xpp, merge_level | merge_style, &result);
+ &xpp, merge_level | merge_style | merge_favor, &result);
 
  for (i = 0; i < 3; i++)
  free(mmfs[i].ptr);
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 4da052a..2cce49d 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -58,6 +58,11 @@ extern "C" {
 #define XDL_MERGE_ZEALOUS_ALNUM 3
 #define XDL_MERGE_LEVEL_MASK 0x0f
 
+/* merge favor modes */
+#define XDL_MERGE_FAVOR_OURS 0x0010
+#define XDL_MERGE_FAVOR_THEIRS 0x0020
+#define XDL_MERGE_FAVOR(flags) (((flags)>>4) & 3)
+
 /* merge output styles */
 #define XDL_MERGE_DIFF3 0x8000
 #define XDL_MERGE_STYLE_MASK 0x8000
@@ -110,7 +115,7 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 
 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 flags, mmbuffer_t *result);
 
 #ifdef __cplusplus
 }
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 1cb65a9..2325f6d 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -214,11 +214,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 
 static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
  xdfenv_t *xe2, const char *name2,
+ int favor,
  xdmerge_t *m, char *dest, int style)
 {
  int size, i;
 
  for (size = i = 0; m; m = m->next) {
+ if (favor && !m->mode)
+                 m->mode = favor;
+
  if (m->mode == 0)
  size = fill_conflict_hunk(xe1, name1, xe2, name2,
   size, i, style, m, dest);
@@ -391,6 +395,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
  int i0, i1, i2, chg0, chg1, chg2;
  int level = flags & XDL_MERGE_LEVEL_MASK;
  int style = flags & XDL_MERGE_STYLE_MASK;
+ int favor = XDL_MERGE_FAVOR(flags);
 
  if (style == XDL_MERGE_DIFF3) {
  /*
@@ -523,14 +528,14 @@ 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, style);
+ favor, changes, NULL, style);
  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,
+ xdl_fill_merge_buffer(xe1, name1, xe2, name2, favor, changes,
       result->ptr, style);
  }
  return xdl_cleanup_merge(changes);
@@ -542,6 +547,8 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
  xdchange_t *xscr1, *xscr2;
  xdfenv_t xe1, xe2;
  int status;
+ int level = flags & XDL_MERGE_LEVEL_MASK;
+ int favor = XDL_MERGE_FAVOR(flags);
 
  result->ptr = NULL;
  result->size = 0;
--
1.6.6.rc0.62.gaccf


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 2/8] builtin-merge.c: call exclude_cmds() correctly.

Avery Pennarun
In reply to this post by Avery Pennarun
We need to call exclude_cmds() after the loop, not during the loop, because
excluding a command from the array can change the indexes of objects in the
array.  The result is that, depending on file ordering, some commands
weren't excluded as they should have been.

Signed-off-by: Avery Pennarun <[hidden email]>
---
 builtin-merge.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/builtin-merge.c b/builtin-merge.c
index 57eedd4..855cf65 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -107,8 +107,8 @@ static struct strategy *get_strategy(const char *name)
  found = 1;
  if (!found)
  add_cmdname(&not_strategies, ent->name, ent->len);
- exclude_cmds(&main_cmds, &not_strategies);
  }
+ exclude_cmds(&main_cmds, &not_strategies);
  }
  if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
  fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
--
1.6.6.rc0.62.gaccf


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

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

Avery Pennarun
In reply to this post by Avery Pennarun
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.

(Patch originally by Junio Hamano <[hidden email]>.)

Signed-off-by: Avery Pennarun <[hidden email]>
---
 .gitignore                    |    2 +
 Makefile                      |    3 ++
 builtin-checkout.c            |    2 +-
 builtin-merge-recursive.c     |    9 ++++--
 builtin-merge.c               |    4 ++-
 contrib/examples/git-merge.sh |    3 +-
 git-compat-util.h             |    1 +
 git.c                         |    2 +
 ll-merge.c                    |   20 +++++++-------
 ll-merge.h                    |    2 +-
 merge-recursive.c             |   21 ++++++++++++++-
 merge-recursive.h             |    6 +++-
 strbuf.c                      |    9 ++++++
 t/t6034-merge-ours-theirs.sh  |   56 +++++++++++++++++++++++++++++++++++++++++
 14 files changed, 120 insertions(+), 20 deletions(-)
 create mode 100755 t/t6034-merge-ours-theirs.sh

diff --git a/.gitignore b/.gitignore
index ac02a58..87467d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,6 +79,8 @@
 /git-merge-one-file
 /git-merge-ours
 /git-merge-recursive
+/git-merge-recursive-ours
+/git-merge-recursive-theirs
 /git-merge-resolve
 /git-merge-subtree
 /git-mergetool
diff --git a/Makefile b/Makefile
index 5a0b3d4..f92b375 100644
--- a/Makefile
+++ b/Makefile
@@ -401,6 +401,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
@@ -1909,6 +1911,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-subtree | \
  git-fsck-objects | git-init-db | \
  git-?*--?* ) continue ;; \
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 64f3a11..b392d1b 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -167,7 +167,7 @@ static int checkout_merged(int pos, struct checkout *state)
  fill_mm(active_cache[pos+2]->sha1, &theirs);
 
  status = ll_merge(&result_buf, path, &ancestor,
-  &ours, "ours", &theirs, "theirs", 1);
+  &ours, "ours", &theirs, "theirs", 1, 0);
  free(ancestor.ptr);
  free(ours.ptr);
  free(theirs.ptr);
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 710674c..f5082da 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -27,9 +27,12 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
  init_merge_options(&o);
  if (argv[0]) {
  int namelen = strlen(argv[0]);
- if (8 < namelen &&
-    !strcmp(argv[0] + namelen - 8, "-subtree"))
- o.subtree_merge = 1;
+ if (!suffixcmp(argv[0], "-subtree"))
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ else if (!suffixcmp(argv[0], "-ours"))
+ o.recursive_variant = MERGE_RECURSIVE_OURS;
+ else if (!suffixcmp(argv[0], "-theirs"))
+ o.recursive_variant = MERGE_RECURSIVE_THEIRS;
  }
 
  if (argc < 4)
diff --git a/builtin-merge.c b/builtin-merge.c
index 855cf65..df089bb 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -55,6 +55,8 @@ static int verbosity;
 
 static struct strategy all_strategy[] = {
  { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
+ { "recursive-ours", DEFAULT_TWOHEAD | NO_TRIVIAL },
+ { "recursive-theirs", DEFAULT_TWOHEAD | NO_TRIVIAL },
  { "octopus",    DEFAULT_OCTOPUS },
  { "resolve",    0 },
  { "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
@@ -563,7 +565,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 
  init_merge_options(&o);
  if (!strcmp(strategy, "subtree"))
- o.subtree_merge = 1;
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
 
  o.branch1 = head_arg;
  o.branch2 = remoteheads->item->util;
diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh
index 500635f..8f617fc 100755
--- a/contrib/examples/git-merge.sh
+++ b/contrib/examples/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-compat-util.h b/git-compat-util.h
index 5c59687..f64cc45 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -198,6 +198,7 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
 extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
+extern int suffixcmp(const char *str, const char *suffix);
 extern time_t tm_to_time_t(const struct tm *tm);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
diff --git a/git.c b/git.c
index 11544cd..4735f11 100644
--- a/git.c
+++ b/git.c
@@ -332,6 +332,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 },
  { "mktree", cmd_mktree, RUN_SETUP },
  { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
diff --git a/ll-merge.c b/ll-merge.c
index 2d6b6d6..cc6814f 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -18,7 +18,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 virtual_ancestor, int favor);
 
 struct ll_merge_driver {
  const char *name;
@@ -38,7 +38,7 @@ 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 virtual_ancestor, int favor)
 {
  /*
  * The tentative merge result is "ours" for the final round,
@@ -59,7 +59,7 @@ 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 virtual_ancestor, int favor)
 {
  xpparam_t xpp;
  int style = 0;
@@ -73,7 +73,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
        path,
        orig, src1, name1,
        src2, name2,
-       virtual_ancestor);
+       virtual_ancestor, favor);
  }
 
  memset(&xpp, 0, sizeof(xpp));
@@ -82,7 +82,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
  return xdl_merge(orig,
  src1, name1,
  src2, name2,
- &xpp, XDL_MERGE_ZEALOUS | style,
+ &xpp, XDL_MERGE_ZEALOUS | style | favor,
  result);
 }
 
@@ -92,7 +92,7 @@ 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 virtual_ancestor, int favor)
 {
  char *src, *dst;
  long size;
@@ -104,7 +104,7 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
  git_xmerge_style = 0;
  status = ll_xdl_merge(drv_unused, result, path_unused,
       orig, src1, NULL, src2, NULL,
-      virtual_ancestor);
+      virtual_ancestor, favor);
  git_xmerge_style = saved_style;
  if (status <= 0)
  return status;
@@ -165,7 +165,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 virtual_ancestor, int favor)
 {
  char temp[3][50];
  struct strbuf cmd = STRBUF_INIT;
@@ -356,7 +356,7 @@ 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 virtual_ancestor, int favor)
 {
  const char *ll_driver_name;
  const struct ll_merge_driver *driver;
@@ -369,5 +369,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, virtual_ancestor, favor);
 }
diff --git a/ll-merge.h b/ll-merge.h
index 5388422..2c94fdb 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -10,6 +10,6 @@ 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 virtual_ancestor, int favor);
 
 #endif
diff --git a/merge-recursive.c b/merge-recursive.c
index a91208f..257bf8f 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -642,6 +642,23 @@ static int merge_3way(struct merge_options *o,
  mmfile_t orig, src1, src2;
  char *name1, *name2;
  int merge_status;
+ int favor;
+
+ if (o->call_depth)
+         favor = 0;
+ else {
+ switch (o->recursive_variant) {
+ case MERGE_RECURSIVE_OURS:
+ favor = XDL_MERGE_FAVOR_OURS;
+ break;
+ case MERGE_RECURSIVE_THEIRS:
+ favor = XDL_MERGE_FAVOR_THEIRS;
+ break;
+ default:
+ favor = 0;
+ break;
+ }
+ }
 
  if (strcmp(a->path, b->path)) {
  name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
@@ -657,7 +674,7 @@ static int merge_3way(struct merge_options *o,
 
  merge_status = ll_merge(result_buf, a->path, &orig,
  &src1, name1, &src2, name2,
- o->call_depth);
+ o->call_depth, favor);
 
  free(name1);
  free(name2);
@@ -1196,7 +1213,7 @@ int merge_trees(struct merge_options *o,
 {
  int code, clean;
 
- if (o->subtree_merge) {
+ if (o->recursive_variant == MERGE_RECURSIVE_SUBTREE) {
  merge = shift_tree_object(head, merge);
  common = shift_tree_object(head, common);
  }
diff --git a/merge-recursive.h b/merge-recursive.h
index fd138ca..9d54219 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -6,7 +6,11 @@
 struct merge_options {
  const char *branch1;
  const char *branch2;
- unsigned subtree_merge : 1;
+ enum {
+         MERGE_RECURSIVE_SUBTREE = 1,
+         MERGE_RECURSIVE_OURS,
+         MERGE_RECURSIVE_THEIRS,
+ } recursive_variant;
  unsigned buffer_output : 1;
  int verbosity;
  int diff_rename_limit;
diff --git a/strbuf.c b/strbuf.c
index a6153dc..d71a623 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -10,6 +10,15 @@ int prefixcmp(const char *str, const char *prefix)
  return (unsigned char)*prefix - (unsigned char)*str;
 }
 
+int suffixcmp(const char *str, const char *suffix)
+{
+ int len = strlen(str), suflen = strlen(suffix);
+ if (len < suflen)
+ return -1;
+ else
+ return strcmp(str + len - suflen, suffix);
+}
+
 /*
  * Used as the default ->buf value, so that people can always assume
  * buf is non NULL and ->buf is NUL terminated even for a freshly
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.6.6.rc0.62.gaccf


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 4/8] Teach git-merge to pass -X<option> to the backend strategy module

Avery Pennarun
In reply to this post by Avery Pennarun
Distinguishing slight variation of modes of operation between the vanilla
merge-recursive and merge-recursive-ours by the command name may have been
an easy way to experiment, but we should bite the bullet and allow backend
specific options to be given by the end user.

(Patch originally by Junio Hamano <[hidden email]>.)

Signed-off-by: Avery Pennarun <[hidden email]>
---
 Makefile                     |    3 ---
 builtin-merge-recursive.c    |   21 +++++++++++++++------
 builtin-merge.c              |   40 ++++++++++++++++++++++++++++++++++++----
 t/t6034-merge-ours-theirs.sh |    4 ++--
 4 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/Makefile b/Makefile
index f92b375..5a0b3d4 100644
--- a/Makefile
+++ b/Makefile
@@ -401,8 +401,6 @@ 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
@@ -1911,7 +1909,6 @@ 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-subtree | \
  git-fsck-objects | git-init-db | \
  git-?*--?* ) continue ;; \
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index f5082da..53f8f05 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -29,18 +29,27 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
  int namelen = strlen(argv[0]);
  if (!suffixcmp(argv[0], "-subtree"))
  o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
- else if (!suffixcmp(argv[0], "-ours"))
- o.recursive_variant = MERGE_RECURSIVE_OURS;
- else if (!suffixcmp(argv[0], "-theirs"))
- o.recursive_variant = MERGE_RECURSIVE_THEIRS;
  }
 
  if (argc < 4)
  usagef("%s <base>... -- <head> <remote> ...", argv[0]);
 
  for (i = 1; i < argc; ++i) {
- if (!strcmp(argv[i], "--"))
- break;
+ const char *arg = argv[i];
+
+ if (!prefixcmp(arg, "--")) {
+ if (!arg[2])
+ break;
+ if (!strcmp(arg+2, "ours"))
+ o.recursive_variant = MERGE_RECURSIVE_OURS;
+ else if (!strcmp(arg+2, "theirs"))
+ o.recursive_variant = MERGE_RECURSIVE_THEIRS;
+ else if (!strcmp(arg+2, "subtree"))
+ o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ else
+ die("Unknown option %s", arg);
+ continue;
+ }
  if (bases_count < ARRAY_SIZE(bases)-1) {
  unsigned char *sha = xmalloc(20);
  if (get_sha1(argv[i], sha))
diff --git a/builtin-merge.c b/builtin-merge.c
index df089bb..9a95bc8 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -50,13 +50,13 @@ static struct commit_list *remoteheads;
 static unsigned char head[20], stash[20];
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
+static const char **xopts;
+static size_t xopts_nr, xopts_alloc;
 static const char *branch;
 static int verbosity;
 
 static struct strategy all_strategy[] = {
  { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
- { "recursive-ours", DEFAULT_TWOHEAD | NO_TRIVIAL },
- { "recursive-theirs", DEFAULT_TWOHEAD | NO_TRIVIAL },
  { "octopus",    DEFAULT_OCTOPUS },
  { "resolve",    0 },
  { "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
@@ -148,6 +148,17 @@ static int option_parse_strategy(const struct option *opt,
  return 0;
 }
 
+static int option_parse_x(const struct option *opt,
+  const char *arg, int unset)
+{
+ if (unset)
+ return 0;
+
+ ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+ xopts[xopts_nr++] = xstrdup(arg);
+ return 0;
+}
+
 static int option_parse_n(const struct option *opt,
   const char *arg, int unset)
 {
@@ -174,6 +185,8 @@ static struct option builtin_merge_options[] = {
  "abort if fast-forward is not possible"),
  OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
  "merge strategy to use", option_parse_strategy),
+ OPT_CALLBACK('X', "extended", &xopts, "option=value",
+ "option for selected merge strategy", option_parse_x),
  OPT_CALLBACK('m', "message", &merge_msg, "message",
  "message to be used for the merge commit (if any)",
  option_parse_message),
@@ -536,7 +549,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
       const char *head_arg)
 {
  const char **args;
- int i = 0, ret;
+ int i = 0, x = 0, ret;
  struct commit_list *j;
  struct strbuf buf = STRBUF_INIT;
  int index_fd;
@@ -566,6 +579,17 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
  init_merge_options(&o);
  if (!strcmp(strategy, "subtree"))
  o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+
+ 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.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ else
+ die("Unknown option for merge-recursive: -X%s", xopts[x]);
+ }
 
  o.branch1 = head_arg;
  o.branch2 = remoteheads->item->util;
@@ -583,10 +607,16 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
  rollback_lock_file(lock);
  return clean ? 0 : 1;
  } else {
- args = xmalloc((4 + commit_list_count(common) +
+ args = xmalloc((4 + xopts_nr + commit_list_count(common) +
  commit_list_count(remoteheads)) * sizeof(char *));
  strbuf_addf(&buf, "merge-%s", strategy);
  args[i++] = buf.buf;
+ for (x = 0; x < xopts_nr; x++) {
+ char *s = xmalloc(strlen(xopts[x])+2+1);
+ strcpy(s, "--");
+ strcpy(s+2, xopts[x]);
+ args[i++] = s;
+ }
  for (j = common; j; j = j->next)
  args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
  args[i++] = "--";
@@ -597,6 +627,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
  ret = run_command_v_opt(args, RUN_GIT_CMD);
  strbuf_release(&buf);
  i = 1;
+ for (x = 0; x < xopts_nr; x++)
+ free((void *)args[i++]);
  for (j = common; j; j = j->next)
  free((void *)args[i++]);
  i += 2;
diff --git a/t/t6034-merge-ours-theirs.sh b/t/t6034-merge-ours-theirs.sh
index 56a9247..08c9f79 100755
--- a/t/t6034-merge-ours-theirs.sh
+++ b/t/t6034-merge-ours-theirs.sh
@@ -35,7 +35,7 @@ test_expect_success 'plain recursive - should conflict' '
 
 test_expect_success 'recursive favouring theirs' '
  git reset --hard master &&
- git merge -s recursive-theirs side &&
+ git merge -s recursive -Xtheirs side &&
  ! grep nine file &&
  grep nueve file &&
  ! grep 9 file &&
@@ -45,7 +45,7 @@ test_expect_success 'recursive favouring theirs' '
 
 test_expect_success 'recursive favouring ours' '
  git reset --hard master &&
- git merge -s recursive-ours side &&
+ git merge -s recursive -X ours side &&
  grep nine file &&
  ! grep nueve file &&
  ! grep 9 file &&
--
1.6.6.rc0.62.gaccf

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 5/8] Teach git-pull to pass -X<option> to git-merge

Avery Pennarun
In reply to this post by Avery Pennarun
(Patch originally by Junio Hamano <[hidden email]>.)

Signed-off-by: Avery Pennarun <[hidden email]>
---
 git-pull.sh                  |   17 +++++++++++++++--
 t/t6034-merge-ours-theirs.sh |    8 ++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/git-pull.sh b/git-pull.sh
index bfeb4a0..6d961b6 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -18,6 +18,7 @@ test -z "$(git ls-files -u)" ||
 
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
 log_arg= verbosity=
+merge_args=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@ -62,6 +63,18 @@ do
  esac
  strategy_args="${strategy_args}-s $strategy "
  ;;
+ -X*)
+ case "$#,$1" in
+ 1,-X)
+ usage ;;
+ *,-X)
+ xx="-X $2"
+ shift ;;
+ *,*)
+ xx="$1" ;;
+ esac
+ merge_args="$merge_args$xx "
+ ;;
  -r|--r|--re|--reb|--reba|--rebas|--rebase)
  rebase=true
  ;;
@@ -216,7 +229,7 @@ fi
 
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
- exec git-rebase $diffstat $strategy_args --onto $merge_head \
+ exec git-rebase $diffstat $strategy_args $merge_args --onto $merge_head \
  ${oldremoteref:-$merge_head}
-exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
+exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args $merge_args \
  "$merge_name" HEAD $merge_head $verbosity
diff --git a/t/t6034-merge-ours-theirs.sh b/t/t6034-merge-ours-theirs.sh
index 08c9f79..8ab3d61 100755
--- a/t/t6034-merge-ours-theirs.sh
+++ b/t/t6034-merge-ours-theirs.sh
@@ -53,4 +53,12 @@ test_expect_success 'recursive favouring ours' '
  ! grep 1 file
 '
 
+test_expect_success 'pull with -X' '
+ git reset --hard master && git pull -s recursive -Xours . side &&
+ git reset --hard master && git pull -s recursive -X ours . side &&
+ git reset --hard master && git pull -s recursive -Xtheirs . side &&
+ git reset --hard master && git pull -s recursive -X theirs . side &&
+ git reset --hard master && ! git pull -s recursive -X bork . side
+'
+
 test_done
--
1.6.6.rc0.62.gaccf

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 6/8] Make "subtree" part more orthogonal to the rest of merge-recursive.

Avery Pennarun
In reply to this post by Avery Pennarun
This makes "subtree" more orthogonal to the rest of recursive merge, so
that you can use subtree and ours/theirs features at the same time.  For
example, you can now say:

        git merge -s subtree -Xtheirs other

to merge with "other" branch while shifting it up or down to match the
shape of the tree of the current branch, and resolving conflicts favoring
the changes "other" branch made over changes made in the current branch.

It also allows the prefix used to shift the trees to be specified using
the "-Xsubtree=$prefix" option.  Giving an empty prefix tells the command
to figure out how much to shift trees automatically as we have always
done.  "merge -s subtree" is the same as "merge -s recursive -Xsubtree="
(or "merge -s recursive -Xsubtree").

(Patch originally by Junio Hamano <[hidden email]>.)

Signed-off-by: Avery Pennarun <[hidden email]>
---
 builtin-merge-recursive.c |    6 ++-
 builtin-merge.c           |    6 ++-
 cache.h                   |    1 +
 match-trees.c             |   69 ++++++++++++++++++++++++++++++++++++++++++++-
 merge-recursive.c         |   16 +++++++---
 merge-recursive.h         |    3 +-
 6 files changed, 90 insertions(+), 11 deletions(-)

diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 53f8f05..d9404e1 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -28,7 +28,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
  if (argv[0]) {
  int namelen = strlen(argv[0]);
  if (!suffixcmp(argv[0], "-subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ o.subtree_shift = "";
  }
 
  if (argc < 4)
@@ -45,7 +45,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
  else if (!strcmp(arg+2, "theirs"))
  o.recursive_variant = MERGE_RECURSIVE_THEIRS;
  else if (!strcmp(arg+2, "subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ o.subtree_shift = "";
+ else if (!prefixcmp(arg+2, "subtree="))
+ o.subtree_shift = arg + 10;
  else
  die("Unknown option %s", arg);
  continue;
diff --git a/builtin-merge.c b/builtin-merge.c
index 9a95bc8..a64b8f2 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -578,7 +578,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 
  init_merge_options(&o);
  if (!strcmp(strategy, "subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+ o.subtree_shift = "";
 
  for (x = 0; x < xopts_nr; x++) {
  if (!strcmp(xopts[x], "ours"))
@@ -586,7 +586,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
  else if (!strcmp(xopts[x], "theirs"))
  o.recursive_variant = MERGE_RECURSIVE_THEIRS;
  else if (!strcmp(xopts[x], "subtree"))
- o.recursive_variant = MERGE_RECURSIVE_SUBTREE;
+                         o.subtree_shift = "";
+ else if (!prefixcmp(xopts[x], "subtree="))
+ o.subtree_shift = xopts[x]+8;
  else
  die("Unknown option for merge-recursive: -X%s", xopts[x]);
  }
diff --git a/cache.h b/cache.h
index bf468e5..c6902d2 100644
--- a/cache.h
+++ b/cache.h
@@ -993,6 +993,7 @@ extern int diff_auto_refresh_index;
 
 /* match-trees.c */
 void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
+void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
 
 /*
  * whitespace rules.
diff --git a/match-trees.c b/match-trees.c
index 0fd6df7..26f7ed1 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -185,7 +185,7 @@ static void match_trees(const unsigned char *hash1,
  * tree object by replacing it with another tree "hash2".
  */
 static int splice_tree(const unsigned char *hash1,
-       char *prefix,
+       const char *prefix,
        const unsigned char *hash2,
        unsigned char *result)
 {
@@ -264,6 +264,13 @@ void shift_tree(const unsigned char *hash1,
  char *del_prefix;
  int add_score, del_score;
 
+ /*
+ * NEEDSWORK: this limits the recursion depth to hardcoded
+ * value '2' to avoid excessive overhead.
+ */
+ if (!depth_limit)
+ depth_limit = 2;
+
  add_score = del_score = score_trees(hash1, hash2);
  add_prefix = xcalloc(1, 1);
  del_prefix = xcalloc(1, 1);
@@ -301,3 +308,63 @@ void shift_tree(const unsigned char *hash1,
 
  splice_tree(hash1, add_prefix, hash2, shifted);
 }
+
+/*
+ * The user says the trees will be shifted by this much.
+ * Unfortunately we cannot fundamentally tell which one to
+ * be prefixed, as recursive merge can work in either direction.
+ */
+void shift_tree_by(const unsigned char *hash1,
+   const unsigned char *hash2,
+   unsigned char *shifted,
+   const char *shift_prefix)
+{
+ unsigned char sub1[20], sub2[20];
+ unsigned mode1, mode2;
+ unsigned candidate = 0;
+
+ /* Can hash2 be a tree at shift_prefix in tree hash1? */
+ if (!get_tree_entry(hash1, shift_prefix, sub1, &mode1) &&
+    S_ISDIR(mode1))
+ candidate |= 1;
+
+ /* Can hash1 be a tree at shift_prefix in tree hash2? */
+ if (!get_tree_entry(hash2, shift_prefix, sub2, &mode2) &&
+    S_ISDIR(mode2))
+ candidate |= 2;
+
+ if (candidate == 3) {
+ /* Both are plausible -- we need to evaluate the score */
+ int best_score = score_trees(hash1, hash2);
+ int score;
+
+ candidate = 0;
+ score = score_trees(sub1, hash2);
+ if (score > best_score) {
+ candidate = 1;
+ best_score = score;
+ }
+ score = score_trees(sub2, hash1);
+ if (score > best_score)
+ candidate = 2;
+ }
+
+ if (!candidate) {
+ /* Neither is plausible -- do not shift */
+ hashcpy(shifted, hash2);
+ return;
+ }
+
+ if (candidate == 1)
+ /*
+ * shift tree2 down by adding shift_prefix above it
+ * to match tree1.
+ */
+ splice_tree(hash1, shift_prefix, hash2, shifted);
+ else
+ /*
+ * shift tree2 up by removing shift_prefix from it
+ * to match tree1.
+ */
+ hashcpy(shifted, sub2);
+}
diff --git a/merge-recursive.c b/merge-recursive.c
index 257bf8f..79b45ed 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -21,7 +21,8 @@
 #include "merge-recursive.h"
 #include "dir.h"
 
-static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+static struct tree *shift_tree_object(struct tree *one, struct tree *two,
+      const char *subtree_shift)
 {
  unsigned char shifted[20];
 
@@ -29,7 +30,12 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)
  * NEEDSWORK: this limits the recursion depth to hardcoded
  * value '2' to avoid excessive overhead.
  */
- shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+ if (!*subtree_shift) {
+ shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
+ } else {
+ shift_tree_by(one->object.sha1, two->object.sha1, shifted,
+      subtree_shift);
+ }
  if (!hashcmp(two->object.sha1, shifted))
  return two;
  return lookup_tree(shifted);
@@ -1213,9 +1219,9 @@ int merge_trees(struct merge_options *o,
 {
  int code, clean;
 
- if (o->recursive_variant == MERGE_RECURSIVE_SUBTREE) {
- merge = shift_tree_object(head, merge);
- common = shift_tree_object(head, common);
+ if (o->subtree_shift) {
+ merge = shift_tree_object(head, merge, o->subtree_shift);
+ common = shift_tree_object(head, common, o->subtree_shift);
  }
 
  if (sha_eq(common->object.sha1, merge->object.sha1)) {
diff --git a/merge-recursive.h b/merge-recursive.h
index 9d54219..d9347ce 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -7,10 +7,11 @@ struct merge_options {
  const char *branch1;
  const char *branch2;
  enum {
-         MERGE_RECURSIVE_SUBTREE = 1,
+ MERGE_RECURSIVE_NORMAL = 0,
          MERGE_RECURSIVE_OURS,
          MERGE_RECURSIVE_THEIRS,
  } recursive_variant;
+ const char *subtree_shift;
  unsigned buffer_output : 1;
  int verbosity;
  int diff_rename_limit;
--
1.6.6.rc0.62.gaccf


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 7/8] Extend merge-subtree tests to test -Xsubtree=dir.

Avery Pennarun
In reply to this post by Avery Pennarun
This tests the configurable -Xsubtree feature of merge-recursive.

Signed-off-by: Avery Pennarun <[hidden email]>
---
 t/t6029-merge-subtree.sh |   47 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 46 insertions(+), 1 deletions(-)

diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
index 5bbfa44..3900d9f 100755
--- a/t/t6029-merge-subtree.sh
+++ b/t/t6029-merge-subtree.sh
@@ -52,6 +52,7 @@ test_expect_success 'initial merge' '
  git merge -s ours --no-commit gui/master &&
  git read-tree --prefix=git-gui/ -u gui/master &&
  git commit -m "Merge git-gui as our subdirectory" &&
+ git checkout -b work &&
  git ls-files -s >actual &&
  (
  echo "100644 $o1 0 git-gui/git-gui.sh"
@@ -65,9 +66,10 @@ test_expect_success 'merge update' '
  echo git-gui2 > git-gui.sh &&
  o3=$(git hash-object git-gui.sh) &&
  git add git-gui.sh &&
+ git checkout -b master2 &&
  git commit -m "update git-gui" &&
  cd ../git &&
- git pull -s subtree gui master &&
+ git pull -s subtree gui master2 &&
  git ls-files -s >actual &&
  (
  echo "100644 $o3 0 git-gui/git-gui.sh"
@@ -76,4 +78,47 @@ test_expect_success 'merge update' '
  test_cmp expected actual
 '
 
+test_expect_success 'initial ambiguous subtree' '
+ cd ../git &&
+ git reset --hard master &&
+ git checkout -b master2 &&
+ git merge -s ours --no-commit gui/master &&
+ git read-tree --prefix=git-gui2/ -u gui/master &&
+ git commit -m "Merge git-gui2 as our subdirectory" &&
+ git checkout -b work2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o1 0 git-gui2/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge using explicit' '
+ cd ../git &&
+ git reset --hard master2 &&
+ git pull -Xsubtree=git-gui gui master2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o3 0 git-gui/git-gui.sh"
+ echo "100644 $o1 0 git-gui2/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge2 using explicit' '
+ cd ../git &&
+ git reset --hard master2 &&
+ git pull -Xsubtree=git-gui2 gui master2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o3 0 git-gui2/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
 test_done
--
1.6.6.rc0.62.gaccf


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

[PATCH 8/8] Document that merge strategies can now take their own options

Avery Pennarun
In reply to this post by Avery Pennarun
Also document the recently added -Xtheirs, -Xours and -Xsubtree[=path]
options to the merge-recursive strategy.

(Patch originally by Junio Hamano <[hidden email]>.)

Signed-off-by: Avery Pennarun <[hidden email]>
---
 Documentation/merge-options.txt    |    4 ++++
 Documentation/merge-strategies.txt |   29 ++++++++++++++++++++++++++++-
 2 files changed, 32 insertions(+), 1 deletions(-)

diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index fec3394..95244d2 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -74,3 +74,7 @@ option can be used to override --squash.
 -v::
 --verbose::
  Be verbose.
+
+-X<option>::
+ Pass merge strategy specific option through to the merge
+ strategy.
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index 42910a3..360dd6f 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -1,6 +1,11 @@
 MERGE STRATEGIES
 ----------------
 
+The merge mechanism ('git-merge' and 'git-pull' commands) allows the
+backend 'merge strategies' to be chosen with `-s` option.  Some strategies
+can also take their own options, which can be passed by giving `-X<option>`
+arguments to 'git-merge' and/or 'git-pull'.
+
 resolve::
  This can only resolve two heads (i.e. the current branch
  and another branch you pulled from) using a 3-way merge
@@ -20,6 +25,27 @@ recursive::
  Additionally this can detect and handle merges involving
  renames.  This is the default merge strategy when
  pulling or merging one branch.
++
+The 'recursive' strategy can take the following options:
+
+ours;;
+ This option forces conflicting hunks to be auto-resolved cleanly by
+ favoring 'our' version.  Changes from the other tree that do not
+ conflict with our side are reflected to the merge result.
++
+This should not be confused with the 'ours' merge strategy, which does not
+even look at what the other tree contains at all.  That one discards everything
+the other tree did, declaring 'our' history contains all that happened in it.
+
+theirs;;
+ This is opposite of 'ours'.
+
+subtree[=path];;
+ This option is a more advanced form of 'subtree' strategy, where
+ the strategy makes a guess on how two trees must be shifted to
+ match with each other when merging.  Instead, the specified path
+ is prefixed (or stripped from the beginning) to make the shape of
+ two trees to match.
 
 octopus::
  This resolves cases with more than two heads, but refuses to do
@@ -33,7 +59,8 @@ ours::
  merge is always that of the current branch head, effectively
  ignoring all changes from all other branches.  It is meant to
  be used to supersede old development history of side
- branches.
+ branches.  Note that this is different from the -Xours option to
+ the 'recursive' merge strategy.
 
 subtree::
  This is a modified recursive strategy. When merging trees A and
--
1.6.6.rc0.62.gaccf


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 2/8] builtin-merge.c: call exclude_cmds() correctly.

Junio C Hamano
In reply to this post by Avery Pennarun
"Avery Pennarun" <[hidden email]> writes:

> We need to call exclude_cmds() after the loop, not during the loop, because
> excluding a command from the array can change the indexes of objects in the
> array.  The result is that, depending on file ordering, some commands
> weren't excluded as they should have been.

As an independent bugfix, I would prefer this to be made against 'maint'
and not as a part of this series.

How did you notice it?  Can you make a test case out of your experience of
noticing this bug in the first place, by the way (I am suspecting that you
saw some breakage and chased it in the debugger)?


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

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

Junio C Hamano
In reply to this post by Avery Pennarun
"Avery Pennarun" <[hidden email]> writes:

> 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.
>
> (Patch originally by Junio Hamano <[hidden email]>.)
>
> Signed-off-by: Avery Pennarun <[hidden email]>

Two comments.

 - The original series was done over a few weeks in 'pu' and this
   intermediate step was done before a better alternative of not using
   these two extra merge strategies were discovered ("...may have been an
   easy way to experiment, but we should bite the bullet", in the next
   patch).

   As the second round to seriously polish the series for inclusion, it
   would make much more sense to squash this with the next patch to erase
   this failed approach that has already been shown as clearly inferiour.

 - I think we should avoid adding the extra argument to ll_merge_fn() by
   combining virtual_ancestor and favor into one "flags" parameter.  If
   you do so, we do not have to change the callsites again next time we
   need to add new optional features that needs only a few bits.

   I vaguely recall that I did the counterpart of this patch that way
   exactly for the above reason, but it is more than a year ago, so maybe
   I didn't do it that way.
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 4/8] Teach git-merge to pass -X<option> to the backend strategy module

Junio C Hamano
In reply to this post by Avery Pennarun
Avery Pennarun <[hidden email]> writes:

> Distinguishing slight variation of modes of operation between the vanilla
> merge-recursive and merge-recursive-ours by the command name may have been
> an easy way to experiment, but we should bite the bullet and allow backend
> specific options to be given by the end user.
>
> (Patch originally by Junio Hamano <[hidden email]>.)
>
> Signed-off-by: Avery Pennarun <[hidden email]>
> ---
>  Makefile                     |    3 ---
>  builtin-merge-recursive.c    |   21 +++++++++++++++------
>  builtin-merge.c              |   40 ++++++++++++++++++++++++++++++++++++----
>  t/t6034-merge-ours-theirs.sh |    4 ++--
>  4 files changed, 53 insertions(+), 15 deletions(-)

You added .gitignore entries for the two programs previously, and are
removing them in this patch, but forgot to remove them from .gitignore in
this patch.

As I already suggested you to, if you squash this to the previous one, it
is not a big deal, though.

> diff --git a/builtin-merge.c b/builtin-merge.c
> index df089bb..9a95bc8 100644
> --- a/builtin-merge.c
> +++ b/builtin-merge.c
> @@ -148,6 +148,17 @@ static int option_parse_strategy(const struct option *opt,
>   return 0;
>  }
>  
> +static int option_parse_x(const struct option *opt,
> +  const char *arg, int unset)
> +{
> + if (unset)
> + return 0;

Should "merge --no-extended" silently be ignored, or be diagnosed as an
error?

> @@ -174,6 +185,8 @@ static struct option builtin_merge_options[] = {
>   "abort if fast-forward is not possible"),
>   OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
>   "merge strategy to use", option_parse_strategy),
> + OPT_CALLBACK('X', "extended", &xopts, "option=value",
> + "option for selected merge strategy", option_parse_x),

I actually didn't name X for "extended" but more for "external" (to the
merge program proper).  "--strategy-option" perhaps?

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 5/8] Teach git-pull to pass -X<option> to git-merge

Junio C Hamano
In reply to this post by Avery Pennarun
Avery Pennarun <[hidden email]> writes:

> (Patch originally by Junio Hamano <[hidden email]>.)
>
> Signed-off-by: Avery Pennarun <[hidden email]>

You should take the full authorship of this patch without even mentioning
"originally by".  It has no code from me.

> @@ -216,7 +229,7 @@ fi
>  
>  merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
>  test true = "$rebase" &&
> - exec git-rebase $diffstat $strategy_args --onto $merge_head \
> + exec git-rebase $diffstat $strategy_args $merge_args --onto $merge_head \
>   ${oldremoteref:-$merge_head}
> -exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
> +exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args $merge_args \
>   "$merge_name" HEAD $merge_head $verbosity

This part needs the usual "sq-then-eval" trick; -X subtree="My Programs"
on the command line will be split by the shell if you didn't do so.
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 6/8] Make "subtree" part more orthogonal to the rest of merge-recursive.

Junio C Hamano
In reply to this post by Avery Pennarun
"Avery Pennarun" <[hidden email]> writes:

> diff --git a/merge-recursive.c b/merge-recursive.c
> index 257bf8f..79b45ed 100644
> --- a/merge-recursive.c
> +++ b/merge-recursive.c
> @@ -21,7 +21,8 @@
>  #include "merge-recursive.h"
>  #include "dir.h"
>  
> -static struct tree *shift_tree_object(struct tree *one, struct tree *two)
> +static struct tree *shift_tree_object(struct tree *one, struct tree *two,
> +      const char *subtree_shift)
>  {
>   unsigned char shifted[20];
>  
> @@ -29,7 +30,12 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)
>   * NEEDSWORK: this limits the recursion depth to hardcoded
>   * value '2' to avoid excessive overhead.
>   */
> - shift_tree(one->object.sha1, two->object.sha1, shifted, 2);

The block comment with NEEDSWORK should be removed from here.  I may have
forgotten to in the original one, but that is not an excuse to replicate a
bad job ;-)

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/8] git-merge-file --ours, --theirs

Junio C Hamano
In reply to this post by Avery Pennarun
"Avery Pennarun" <[hidden email]> writes:

> ...
> 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.

Typo/Grammo.  "risky features are not on by default".

> (Patch originally by Junio Hamano <[hidden email]>.)
>
> Signed-off-by: Avery Pennarun <[hidden email]>

Except for parse-optification, this one is more or less a verbatim copy of
my patch, and I think I probably deserve an in-body "From: " line for this
[PATCH 1/8], [PATCH 6/8] and [PATCH 8/8] to take the full authorship of
them.

> diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
> index 4da052a..2cce49d 100644
> --- a/xdiff/xdiff.h
> +++ b/xdiff/xdiff.h
> @@ -58,6 +58,11 @@ extern "C" {
>  #define XDL_MERGE_ZEALOUS_ALNUM 3
>  #define XDL_MERGE_LEVEL_MASK 0x0f
>  
> +/* merge favor modes */
> +#define XDL_MERGE_FAVOR_OURS 0x0010
> +#define XDL_MERGE_FAVOR_THEIRS 0x0020
> +#define XDL_MERGE_FAVOR(flags) (((flags)>>4) & 3)

This is a bad change.  It forces the high-level layer of the resulting
code to be aware that the favor bits are shifted by 4 and it is different
from what the low-level layer expects.  If I were porting it to
parse-options, I would have kept OURS = 1 and THEIRS = 2 as the original
patch, and instead did something like:

  ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
- &xpp, merge_level | merge_style, &result);
+ &xpp, XDL_MERGE_FLAGS(merge_level, merge_style, merge_favor), &result);

with an updated definition like this:

    #define XDL_MERGE_FLAGS(level, style, favor) ((level)|(style)|((favor)<<4)

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/8] git-merge-file --ours, --theirs

Nanako Shiraishi
Quoting Junio C Hamano <[hidden email]>

> Except for parse-optification, this one is more or less a verbatim copy of
> my patch, and I think I probably deserve an in-body "From: " line for this
> [PATCH 1/8], [PATCH 6/8] and [PATCH 8/8] to take the full authorship of
> them.

Could you give an guideline to decide when to take authorship and when to
give it to others?  The above seems somewhat arbitrary to me.

--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/8] git-merge-file --ours, --theirs

Junio C Hamano
Nanako Shiraishi <[hidden email]> writes:

> Quoting Junio C Hamano <[hidden email]>
>
>> Except for parse-optification, this one is more or less a verbatim copy of
>> my patch, and I think I probably deserve an in-body "From: " line for this
>> [PATCH 1/8], [PATCH 6/8] and [PATCH 8/8] to take the full authorship of
>> them.
>
> Could you give an guideline to decide when to take authorship and when to
> give it to others?  The above seems somewhat arbitrary to me.

It might seem that way to you, as you do not write much C, but I am
reasonably sure Avery understands after having worked on the series.

Imagine that Avery were an area expert (the subsystem maintainer) on "git
merge" and downwards, and somebody who did not know that "merge" has
already been rewritten in C, nor some parts of the system have been
rewritten to use parse-options, submitted a patch series for review and
Avery is helping to polish it up [*1*].

As the subsystem maintainer, Avery looks at the patches, updates parts of
the code that is based on obsolete infrastructure, adds lacking tests and
documentation as necessary, and forwards the tested result upwards for
inclusion.  How would the messages from Avery to me look?

Patches that were majorly reworked should be attributed to Avery, and
obviously new patches that are added for missing tests should be, too.
For example, changes made to git-merge.sh by the original submitter must
be discarded and redone from scratch to builtin-merge.c, and if you look
at the changes, it would be quite obvious that the original patch wouldn't
have served as anything more than giving the specification.

But the ones with minor updates should retain the original authorship.
It unfortunately is not black-and-white, though.

In any case, where does Avery's credit go?  Is there a point in helping to
polish others' patches?

It is recoded on the Signed-off-by line.  When somebody passes a patch
from somebody else, an S-o-b is added for DCO purposes, but it also leaves
the "patch trail"---these people looked at the patch, spent effort to make
sure it is suitable for inclusion by reviewing, polishing, and enhancing.
A subsystem maintainer, or anybody who helps to polish others
contribution, may not necessarily have his name as the "author" of the
patch, and if the patch forwarding is done via e-mail, his name won't be
on the "committer" line either.  But the contribution is still noted and
appreciated, and the hint to pay attention to is by counting non-author
S-o-b and Tested-by lines in the commit messages.

cf. http://lwn.net/SubscriberLink/363456/50efdecf49af77ba/ check the last
table.


[Footnote]

*1* That somebody happens to be me from 18 months ago, but the important
point here is that the person is not Avery as the subsystem maintainer (in
other words, it is immaterial that it happens to be the same person as the
toplevel maintainer).
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/8] git-merge-file --ours, --theirs

Nanako Shiraishi
Quoting Junio C Hamano <[hidden email]> writes:

> In any case, where does Avery's credit go?  Is there a point in helping to
> polish others' patches?
>
> It is recoded on the Signed-off-by line.  When somebody passes a patch
> from somebody else, an S-o-b is added for DCO purposes, but it also leaves
> the "patch trail"---these people looked at the patch, spent effort to make
> sure it is suitable for inclusion by reviewing, polishing, and enhancing.
> A subsystem maintainer, or anybody who helps to polish others
> contribution, may not necessarily have his name as the "author" of the
> patch, and if the patch forwarding is done via e-mail, his name won't be
> on the "committer" line either.  But the contribution is still noted and
> appreciated, and the hint to pay attention to is by counting non-author
> S-o-b and Tested-by lines in the commit messages.

I understand. Thank you for a detailed explanation.

--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1/8] git-merge-file --ours, --theirs

Avery Pennarun
In reply to this post by Junio C Hamano
On Thu, Nov 26, 2009 at 1:17 AM, Junio C Hamano <[hidden email]> wrote:
> Except for parse-optification, this one is more or less a verbatim copy of
> my patch, and I think I probably deserve an in-body "From: " line for this
> [PATCH 1/8], [PATCH 6/8] and [PATCH 8/8] to take the full authorship of
> them.
[...]
> Imagine that Avery were an area expert (the subsystem maintainer) on "git
> merge" and downwards, and somebody who did not know that "merge" has
> already been rewritten in C, nor some parts of the system have been
> rewritten to use parse-options, submitted a patch series for review and
> Avery is helping to polish it up [*1*].

I'm quite open to doing this however you want; I definitely consider
it your patch series.  My main measurable contribution is just the
unit tests that I wrote.

However, when thinking about this, I wasn't worried so much about the
correct placement of credit as of discredit.  The merge code has
changed sufficiently since you wrote this patch series that every one
of them required quite a lot of conflict resolution.  Most of the
conflicts were pretty obvious how to resolve, but it was tedious and
error prone, and there's a reasonably high probability that I screwed
up something while doing so.

I imagined what people would expect to see when they do 'git blame' to
explain the source of a problem.  If they see your name, you might be
blamed for my errors; if they see my name with a "based on a patch by
Junio" in the changelog, then I would be (probably correctly) blamed
for the errors, while you can retain credit for the success.

Mostly, however, I didn't want to be sending out patches in your name
that weren't actually done by you.  If you'd like me to do so,
however, then I will :)

>> +/* merge favor modes */
>> +#define XDL_MERGE_FAVOR_OURS 0x0010
>> +#define XDL_MERGE_FAVOR_THEIRS 0x0020
>> +#define XDL_MERGE_FAVOR(flags) (((flags)>>4) & 3)
>
> This is a bad change.  It forces the high-level layer of the resulting
> code to be aware that the favor bits are shifted by 4 and it is different
> from what the low-level layer expects.  If I were porting it to
> parse-options, I would have kept OURS = 1 and THEIRS = 2 as the original
> patch, [...]

Ouch, yes, that wasn't very clear thinking on my part.  I meant for
XDL_MERGE_FAVOR(flags) to return either XDL_MERGE_FAVOR_OURS or
XDL_MERGE_FAVOR_THEIRS, but clearly it doesn't.  Will fix.

Avery
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 2/8] builtin-merge.c: call exclude_cmds() correctly.

Avery Pennarun
In reply to this post by Junio C Hamano
On Thu, Nov 26, 2009 at 12:36 AM, Junio C Hamano <[hidden email]> wrote:

> "Avery Pennarun" <[hidden email]> writes:
>
>> We need to call exclude_cmds() after the loop, not during the loop, because
>> excluding a command from the array can change the indexes of objects in the
>> array.  The result is that, depending on file ordering, some commands
>> weren't excluded as they should have been.
>
> As an independent bugfix, I would prefer this to be made against 'maint'
> and not as a part of this series.
>
> How did you notice it?  Can you make a test case out of your experience of
> noticing this bug in the first place, by the way (I am suspecting that you
> saw some breakage and chased it in the debugger)?

The story behind this one is a bit silly, but since you asked: I
forgot to add recursive-ours and recursive-theirs to the list of known
merge strategies, but was surprised to find that my test for
recursive-theirs passed, while recursive-ours didn't.  Investigating
further, I found that the printed list of "found" strategies included
recursive-theirs but not recursive-ours.  Turns out that this is
because when recursive-ours was being (correctly) removed, that slot
in the array was being filled by recursive-theirs, and then
immediately i++, which meant that recursive-theirs was never checked
for exclusion as it should have been.

Of course, after fixing this bug *both* tests were broken, but the
correct thing to do was add both strategies to the list, which hides
the effect of this bugfix.

Since the bug is actually that *too many* strategies are listed
instead of too few, it's pretty minor and I doubt it needs to go into
maint.  Also, I don't know of a way to test it that would be reliable.
 And I doubt this particular bug will recur anyway.  (If it were too
*few* strategies listed, I'm guessing it would be caught by any number
of other tests.)

Suggestions welcome.

Thanks,

Avery
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
12