Merge branch 'jf/merge-ignore-ws'

* jf/merge-ignore-ws:
  merge-recursive: options to ignore whitespace changes
  merge-recursive --patience
  ll-merge: replace flag argument with options struct
  merge-recursive: expose merge options for builtin merge
This commit is contained in:
Junio C Hamano 2010-10-26 21:40:54 -07:00
commit 75b17fee72
12 changed files with 351 additions and 97 deletions

View File

@ -40,6 +40,28 @@ the other tree did, declaring 'our' history contains all that happened in it.
theirs;; theirs;;
This is opposite of 'ours'. This is opposite of 'ours'.
patience;;
With this option, 'merge-recursive' spends a little extra time
to avoid mismerges that sometimes occur due to unimportant
matching lines (e.g., braces from distinct functions). Use
this when the branches to be merged have diverged wildly.
See also linkgit:git-diff[1] `--patience`.
ignore-space-change;;
ignore-all-space;;
ignore-space-at-eol;;
Treats lines with the indicated type of whitespace change as
unchanged for the sake of a three-way merge. Whitespace
changes mixed with other changes to a line are not ignored.
See also linkgit:git-diff[1] `-b`, `-w`, and
`--ignore-space-at-eol`.
+
* If 'their' version only introduces whitespace changes to a line,
'our' version is used;
* If 'our' version introduces whitespace changes but 'their'
version includes a substantial change, 'their' version is used;
* Otherwise, the merge proceeds in the usual way.
renormalize;; renormalize;;
This runs a virtual check-out and check-in of all three stages This runs a virtual check-out and check-in of all three stages
of a file when resolving a three-way merge. This option is of a file when resolving a three-way merge. This option is

View File

@ -17,6 +17,40 @@ responsible for a few things.
path-specific merge drivers (specified in `.gitattributes`) path-specific merge drivers (specified in `.gitattributes`)
into account. into account.
Data structures
---------------
* `mmbuffer_t`, `mmfile_t`
These store data usable for use by the xdiff backend, for writing and
for reading, respectively. See `xdiff/xdiff.h` for the definitions
and `diff.c` for examples.
* `struct ll_merge_options`
This describes the set of options the calling program wants to affect
the operation of a low-level (single file) merge. Some options:
`virtual_ancestor`::
Behave as though this were part of a merge between common
ancestors in a recursive merge.
If a helper program is specified by the
`[merge "<driver>"] recursive` configuration, it will
be used (see linkgit:gitattributes[5]).
`variant`::
Resolve local conflicts automatically in favor
of one side or the other (as in 'git merge-file'
`--ours`/`--theirs`/`--union`). Can be `0`,
`XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
`XDL_MERGE_FAVOR_UNION`.
`renormalize`::
Resmudge and clean the "base", "theirs" and "ours" files
before merging. Use this when the merge is likely to have
overlapped with a change in smudge/clean or end-of-line
normalization rules.
Low-level (single file) merge Low-level (single file) merge
----------------------------- -----------------------------
@ -28,15 +62,24 @@ Low-level (single file) merge
`.git/info/attributes` into account. Returns 0 for a `.git/info/attributes` into account. Returns 0 for a
clean merge. clean merge.
The caller: Calling sequence:
1. allocates an mmbuffer_t variable for the result; * Prepare a `struct ll_merge_options` to record options.
2. allocates and fills variables with the file's original content If you have no special requests, skip this and pass `NULL`
and two modified versions (using `read_mmfile`, for example); as the `opts` parameter to use the default options.
3. calls ll_merge();
4. reads the output from result_buf.ptr and result_buf.size; * Allocate an mmbuffer_t variable for the result.
5. releases buffers when finished (free(ancestor.ptr); free(ours.ptr);
free(theirs.ptr); free(result_buf.ptr);). * Allocate and fill variables with the file's original content
and two modified versions (using `read_mmfile`, for example).
* Call `ll_merge()`.
* Read the merged content from `result_buf.ptr` and `result_buf.size`.
* Release buffers when finished. A simple
`free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
free(result_buf.ptr);` will do.
If the modifications do not merge cleanly, `ll_merge` will return a If the modifications do not merge cleanly, `ll_merge` will return a
nonzero value and `result_buf` will generally include a description of nonzero value and `result_buf` will generally include a description of
@ -47,18 +90,6 @@ The `ancestor_label`, `our_label`, and `their_label` parameters are
used to label the different sides of a conflict if the merge driver used to label the different sides of a conflict if the merge driver
supports this. supports this.
The `flag` parameter is a bitfield:
- The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an
internal merge to consolidate ancestors for a recursive merge.
- The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically
resolved in favor of one side or the other (as in 'git merge-file'
`--ours`/`--theirs`/`--union`).
They can be populated by `create_ll_flag`, whose argument can be
`XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
`XDL_MERGE_FAVOR_UNION`.
Everything else Everything else
--------------- ---------------

View File

@ -161,7 +161,7 @@ static int checkout_merged(int pos, struct checkout *state)
* merge.renormalize set, too * merge.renormalize set, too
*/ */
status = ll_merge(&result_buf, path, &ancestor, "base", status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs", 0); &ours, "ours", &theirs, "theirs", NULL);
free(ancestor.ptr); free(ancestor.ptr);
free(ours.ptr); free(ours.ptr);
free(theirs.ptr); free(theirs.ptr);

View File

@ -2,6 +2,7 @@
#include "commit.h" #include "commit.h"
#include "tag.h" #include "tag.h"
#include "merge-recursive.h" #include "merge-recursive.h"
#include "xdiff-interface.h"
static const char builtin_merge_recursive_usage[] = static const char builtin_merge_recursive_usage[] =
"git %s <base>... -- <head> <remote> ..."; "git %s <base>... -- <head> <remote> ...";
@ -40,19 +41,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
if (!prefixcmp(arg, "--")) { if (!prefixcmp(arg, "--")) {
if (!arg[2]) if (!arg[2])
break; break;
if (!strcmp(arg+2, "ours")) if (parse_merge_opt(&o, arg + 2))
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.subtree_shift = "";
else if (!prefixcmp(arg+2, "subtree="))
o.subtree_shift = arg + 10;
else if (!strcmp(arg+2, "renormalize"))
o.renormalize = 1;
else if (!strcmp(arg+2, "no-renormalize"))
o.renormalize = 0;
else
die("Unknown option %s", arg); die("Unknown option %s", arg);
continue; continue;
} }

View File

@ -639,25 +639,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
o.renormalize = option_renormalize; o.renormalize = option_renormalize;
/* for (x = 0; x < xopts_nr; x++)
* NEEDSWORK: merge with table in builtin/merge-recursive if (parse_merge_opt(&o, xopts[x]))
*/
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.subtree_shift = "";
else if (!prefixcmp(xopts[x], "subtree="))
o.subtree_shift = xopts[x]+8;
else if (!strcmp(xopts[x], "renormalize"))
o.renormalize = 1;
else if (!strcmp(xopts[x], "no-renormalize"))
o.renormalize = 0;
else
die("Unknown option for merge-recursive: -X%s", xopts[x]); die("Unknown option for merge-recursive: -X%s", xopts[x]);
}
o.branch1 = head_arg; o.branch1 = head_arg;
o.branch2 = remoteheads->item->util; o.branch2 = remoteheads->item->util;

View File

@ -18,7 +18,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
mmfile_t *orig, const char *orig_name, mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1, mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2, mmfile_t *src2, const char *name2,
int flag, const struct ll_merge_options *opts,
int marker_size); int marker_size);
struct ll_merge_driver { struct ll_merge_driver {
@ -39,14 +39,18 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig, const char *orig_name, mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1, mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2, mmfile_t *src2, const char *name2,
int flag, int marker_size) const struct ll_merge_options *opts,
int marker_size)
{ {
mmfile_t *stolen;
assert(opts);
/* /*
* The tentative merge result is "ours" for the final round, * The tentative merge result is "ours" for the final round,
* or common ancestor for an internal merge. Still return * or common ancestor for an internal merge. Still return
* "conflicted merge" status. * "conflicted merge" status.
*/ */
mmfile_t *stolen = (flag & LL_OPT_VIRTUAL_ANCESTOR) ? orig : src1; stolen = opts->virtual_ancestor ? orig : src1;
result->ptr = stolen->ptr; result->ptr = stolen->ptr;
result->size = stolen->size; result->size = stolen->size;
@ -60,9 +64,11 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig, const char *orig_name, mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1, mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2, mmfile_t *src2, const char *name2,
int flag, int marker_size) const struct ll_merge_options *opts,
int marker_size)
{ {
xmparam_t xmp; xmparam_t xmp;
assert(opts);
if (buffer_is_binary(orig->ptr, orig->size) || if (buffer_is_binary(orig->ptr, orig->size) ||
buffer_is_binary(src1->ptr, src1->size) || buffer_is_binary(src1->ptr, src1->size) ||
@ -74,12 +80,13 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
orig, orig_name, orig, orig_name,
src1, name1, src1, name1,
src2, name2, src2, name2,
flag, marker_size); opts, marker_size);
} }
memset(&xmp, 0, sizeof(xmp)); memset(&xmp, 0, sizeof(xmp));
xmp.level = XDL_MERGE_ZEALOUS; xmp.level = XDL_MERGE_ZEALOUS;
xmp.favor = ll_opt_favor(flag); xmp.favor = opts->variant;
xmp.xpp.flags = opts->xdl_opts;
if (git_xmerge_style >= 0) if (git_xmerge_style >= 0)
xmp.style = git_xmerge_style; xmp.style = git_xmerge_style;
if (marker_size > 0) if (marker_size > 0)
@ -96,15 +103,17 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
mmfile_t *orig, const char *orig_name, mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1, mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2, mmfile_t *src2, const char *name2,
int flag, int marker_size) const struct ll_merge_options *opts,
int marker_size)
{ {
/* Use union favor */ /* Use union favor */
flag &= ~LL_OPT_FAVOR_MASK; struct ll_merge_options o;
flag |= create_ll_flag(XDL_MERGE_FAVOR_UNION); assert(opts);
o = *opts;
o.variant = XDL_MERGE_FAVOR_UNION;
return ll_xdl_merge(drv_unused, result, path_unused, return ll_xdl_merge(drv_unused, result, path_unused,
orig, NULL, src1, NULL, src2, NULL, orig, NULL, src1, NULL, src2, NULL,
flag, marker_size); &o, marker_size);
return 0;
} }
#define LL_BINARY_MERGE 0 #define LL_BINARY_MERGE 0
@ -136,7 +145,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
mmfile_t *orig, const char *orig_name, mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1, mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2, mmfile_t *src2, const char *name2,
int flag, int marker_size) const struct ll_merge_options *opts,
int marker_size)
{ {
char temp[4][50]; char temp[4][50];
struct strbuf cmd = STRBUF_INIT; struct strbuf cmd = STRBUF_INIT;
@ -144,6 +154,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
const char *args[] = { NULL, NULL }; const char *args[] = { NULL, NULL };
int status, fd, i; int status, fd, i;
struct stat st; struct stat st;
assert(opts);
dict[0].placeholder = "O"; dict[0].value = temp[0]; dict[0].placeholder = "O"; dict[0].value = temp[0];
dict[1].placeholder = "A"; dict[1].value = temp[1]; dict[1].placeholder = "A"; dict[1].value = temp[1];
@ -337,15 +348,21 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *ancestor, const char *ancestor_label, mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label, mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label, mmfile_t *theirs, const char *their_label,
int flag) const struct ll_merge_options *opts)
{ {
static struct git_attr_check check[2]; static struct git_attr_check check[2];
const char *ll_driver_name = NULL; const char *ll_driver_name = NULL;
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
const struct ll_merge_driver *driver; const struct ll_merge_driver *driver;
int virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;
if (flag & LL_OPT_RENORMALIZE) { if (!opts) {
struct ll_merge_options default_opts = {0};
return ll_merge(result_buf, path, ancestor, ancestor_label,
ours, our_label, theirs, their_label,
&default_opts);
}
if (opts->renormalize) {
normalize_file(ancestor, path); normalize_file(ancestor, path);
normalize_file(ours, path); normalize_file(ours, path);
normalize_file(theirs, path); normalize_file(theirs, path);
@ -359,11 +376,11 @@ int ll_merge(mmbuffer_t *result_buf,
} }
} }
driver = find_ll_merge_driver(ll_driver_name); driver = find_ll_merge_driver(ll_driver_name);
if (virtual_ancestor && driver->recursive) if (opts->virtual_ancestor && driver->recursive)
driver = find_ll_merge_driver(driver->recursive); driver = find_ll_merge_driver(driver->recursive);
return driver->fn(driver, result_buf, path, ancestor, ancestor_label, return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
ours, our_label, theirs, their_label, ours, our_label, theirs, their_label,
flag, marker_size); opts, marker_size);
} }
int ll_merge_marker_size(const char *path) int ll_merge_marker_size(const char *path)

View File

@ -5,27 +5,19 @@
#ifndef LL_MERGE_H #ifndef LL_MERGE_H
#define LL_MERGE_H #define LL_MERGE_H
#define LL_OPT_VIRTUAL_ANCESTOR (1 << 0) struct ll_merge_options {
#define LL_OPT_FAVOR_MASK ((1 << 1) | (1 << 2)) unsigned virtual_ancestor : 1;
#define LL_OPT_FAVOR_SHIFT 1 unsigned variant : 2; /* favor ours, favor theirs, or union merge */
#define LL_OPT_RENORMALIZE (1 << 3) unsigned renormalize : 1;
long xdl_opts;
static inline int ll_opt_favor(int flag) };
{
return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;
}
static inline int create_ll_flag(int favor)
{
return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);
}
int ll_merge(mmbuffer_t *result_buf, int ll_merge(mmbuffer_t *result_buf,
const char *path, const char *path,
mmfile_t *ancestor, const char *ancestor_label, mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label, mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label, mmfile_t *theirs, const char *their_label,
int flag); const struct ll_merge_options *opts);
int ll_merge_marker_size(const char *path); int ll_merge_marker_size(const char *path);

View File

@ -37,7 +37,7 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
* common ancestor. * common ancestor.
*/ */
merge_status = ll_merge(&res, path, base, NULL, merge_status = ll_merge(&res, path, base, NULL,
our, ".our", their, ".their", 0); our, ".our", their, ".their", NULL);
if (merge_status < 0) if (merge_status < 0)
return NULL; return NULL;

View File

@ -605,22 +605,26 @@ static int merge_3way(struct merge_options *o,
const char *branch2) const char *branch2)
{ {
mmfile_t orig, src1, src2; mmfile_t orig, src1, src2;
struct ll_merge_options ll_opts = {0};
char *base_name, *name1, *name2; char *base_name, *name1, *name2;
int merge_status; int merge_status;
int favor;
if (o->call_depth) ll_opts.renormalize = o->renormalize;
favor = 0; ll_opts.xdl_opts = o->xdl_opts;
else {
if (o->call_depth) {
ll_opts.virtual_ancestor = 1;
ll_opts.variant = 0;
} else {
switch (o->recursive_variant) { switch (o->recursive_variant) {
case MERGE_RECURSIVE_OURS: case MERGE_RECURSIVE_OURS:
favor = XDL_MERGE_FAVOR_OURS; ll_opts.variant = XDL_MERGE_FAVOR_OURS;
break; break;
case MERGE_RECURSIVE_THEIRS: case MERGE_RECURSIVE_THEIRS:
favor = XDL_MERGE_FAVOR_THEIRS; ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
break; break;
default: default:
favor = 0; ll_opts.variant = 0;
break; break;
} }
} }
@ -643,10 +647,7 @@ static int merge_3way(struct merge_options *o,
read_mmblob(&src2, b->sha1); read_mmblob(&src2, b->sha1);
merge_status = ll_merge(result_buf, a->path, &orig, base_name, merge_status = ll_merge(result_buf, a->path, &orig, base_name,
&src1, name1, &src2, name2, &src1, name1, &src2, name2, &ll_opts);
((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
(o->renormalize ? LL_OPT_RENORMALIZE : 0) |
create_ll_flag(favor)));
free(name1); free(name1);
free(name2); free(name2);
@ -1550,3 +1551,32 @@ void init_merge_options(struct merge_options *o)
memset(&o->current_directory_set, 0, sizeof(struct string_list)); memset(&o->current_directory_set, 0, sizeof(struct string_list));
o->current_directory_set.strdup_strings = 1; o->current_directory_set.strdup_strings = 1;
} }
int parse_merge_opt(struct merge_options *o, const char *s)
{
if (!s || !*s)
return -1;
if (!strcmp(s, "ours"))
o->recursive_variant = MERGE_RECURSIVE_OURS;
else if (!strcmp(s, "theirs"))
o->recursive_variant = MERGE_RECURSIVE_THEIRS;
else if (!strcmp(s, "subtree"))
o->subtree_shift = "";
else if (!prefixcmp(s, "subtree="))
o->subtree_shift = s + strlen("subtree=");
else if (!strcmp(s, "patience"))
o->xdl_opts |= XDF_PATIENCE_DIFF;
else if (!strcmp(s, "ignore-space-change"))
o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(s, "ignore-all-space"))
o->xdl_opts |= XDF_IGNORE_WHITESPACE;
else if (!strcmp(s, "ignore-space-at-eol"))
o->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
else if (!strcmp(s, "renormalize"))
o->renormalize = 1;
else if (!strcmp(s, "no-renormalize"))
o->renormalize = 0;
else
return -1;
return 0;
}

View File

@ -15,6 +15,7 @@ struct merge_options {
const char *subtree_shift; const char *subtree_shift;
unsigned buffer_output : 1; unsigned buffer_output : 1;
unsigned renormalize : 1; unsigned renormalize : 1;
long xdl_opts;
int verbosity; int verbosity;
int diff_rename_limit; int diff_rename_limit;
int merge_rename_limit; int merge_rename_limit;
@ -52,6 +53,8 @@ int merge_recursive_generic(struct merge_options *o,
void init_merge_options(struct merge_options *o); void init_merge_options(struct merge_options *o);
struct tree *write_tree_from_memory(struct merge_options *o); struct tree *write_tree_from_memory(struct merge_options *o);
int parse_merge_opt(struct merge_options *out, const char *s);
/* builtin/merge.c */ /* builtin/merge.c */
int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes); int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);

View File

@ -325,7 +325,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
*/ */
ll_merge(&result, path, &mmfile[0], NULL, ll_merge(&result, path, &mmfile[0], NULL,
&mmfile[1], "ours", &mmfile[1], "ours",
&mmfile[2], "theirs", 0); &mmfile[2], "theirs", NULL);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
free(mmfile[i].ptr); free(mmfile[i].ptr);

View File

@ -0,0 +1,186 @@
#!/bin/sh
test_description='merge-recursive options
* [master] Clarify
! [remote] Remove cruft
--
+ [remote] Remove cruft
* [master] Clarify
*+ [remote^] Initial revision
* ok 1: setup
'
. ./test-lib.sh
test_expect_success 'setup' '
conflict_hunks () {
sed -n -e "
/^<<<</ b inconflict
b
: inconflict
p
/^>>>>/ b
n
b inconflict
" "$@"
} &&
cat <<-\EOF >text.txt &&
Hope, he says, cherishes the soul of him who lives in
justice and holiness and is the nurse of his age and the
companion of his journey;--hope which is mightiest to sway
the restless soul of man.
How admirable are his words! And the great blessing of riches, I do
not say to every man, but to a good man, is, that he has had no
occasion to deceive or to defraud others, either intentionally or
unintentionally; and when he departs to the world below he is not in
any apprehension about offerings due to the gods or debts which he owes
to men. Now to this peace of mind the possession of wealth greatly
contributes; and therefore I say, that, setting one thing against
another, of the many advantages which wealth has to give, to a man of
sense this is in my opinion the greatest.
Well said, Cephalus, I replied; but as concerning justice, what is
it?--to speak the truth and to pay your debts--no more than this? And
even to this are there not exceptions? Suppose that a friend when in
his right mind has deposited arms with me and he asks for them when he
is not in his right mind, ought I to give them back to him? No one
would say that I ought or that I should be right in doing so, any more
than they would say that I ought always to speak the truth to one who
is in his condition.
You are quite right, he replied.
But then, I said, speaking the truth and paying your debts is not a
correct definition of justice.
CEPHALUS - SOCRATES - POLEMARCHUS
Quite correct, Socrates, if Simonides is to be believed, said
Polemarchus interposing.
I fear, said Cephalus, that I must go now, for I have to look after the
sacrifices, and I hand over the argument to Polemarchus and the company.
EOF
git add text.txt &&
test_tick &&
git commit -m "Initial revision" &&
git checkout -b remote &&
sed -e "
s/\. /\. /g
s/[?] /? /g
s/ / /g
s/--/---/g
s/but as concerning/but as con cerning/
/CEPHALUS - SOCRATES - POLEMARCHUS/ d
" text.txt >text.txt+ &&
mv text.txt+ text.txt &&
git commit -a -m "Remove cruft" &&
git checkout master &&
sed -e "
s/\(not in his right mind\),\(.*\)/\1;\2Q/
s/Quite correct\(.*\)/It is too correct\1Q/
s/unintentionally/un intentionally/
/un intentionally/ s/$/Q/
s/Polemarchus interposing./Polemarchus, interposing.Q/
/justice and holiness/ s/$/Q/
/pay your debts/ s/$/Q/
" text.txt | q_to_cr >text.txt+ &&
mv text.txt+ text.txt &&
git commit -a -m "Clarify" &&
git show-branch --all
'
test_expect_success 'naive merge fails' '
git read-tree --reset -u HEAD &&
test_must_fail git merge-recursive HEAD^ -- HEAD remote &&
test_must_fail git update-index --refresh &&
grep "<<<<<<" text.txt
'
test_expect_success '--ignore-space-change makes merge succeed' '
git read-tree --reset -u HEAD &&
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
'
test_expect_success '--ignore-space-change: our w/s-only change wins' '
q_to_cr <<-\EOF >expected &&
justice and holiness and is the nurse of his age and theQ
EOF
git read-tree --reset -u HEAD &&
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
grep "justice and holiness" text.txt >actual &&
test_cmp expected actual
'
test_expect_success '--ignore-space-change: their real change wins over w/s' '
cat <<-\EOF >expected &&
it?---to speak the truth and to pay your debts---no more than this? And
EOF
git read-tree --reset -u HEAD &&
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
grep "pay your debts" text.txt >actual &&
test_cmp expected actual
'
test_expect_success '--ignore-space-change: does not ignore new spaces' '
cat <<-\EOF >expected1 &&
Well said, Cephalus, I replied; but as con cerning justice, what is
EOF
q_to_cr <<-\EOF >expected2 &&
un intentionally; and when he departs to the world below he is not inQ
EOF
git read-tree --reset -u HEAD &&
git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
grep "Well said" text.txt >actual1 &&
grep "when he departs" text.txt >actual2 &&
test_cmp expected1 actual1 &&
test_cmp expected2 actual2
'
test_expect_success '--ignore-all-space drops their new spaces' '
cat <<-\EOF >expected &&
Well said, Cephalus, I replied; but as concerning justice, what is
EOF
git read-tree --reset -u HEAD &&
git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
grep "Well said" text.txt >actual &&
test_cmp expected actual
'
test_expect_success '--ignore-all-space keeps our new spaces' '
q_to_cr <<-\EOF >expected &&
un intentionally; and when he departs to the world below he is not inQ
EOF
git read-tree --reset -u HEAD &&
git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
grep "when he departs" text.txt >actual &&
test_cmp expected actual
'
test_expect_success '--ignore-space-at-eol' '
q_to_cr <<-\EOF >expected &&
<<<<<<< HEAD
is not in his right mind; ought I to give them back to him? No oneQ
=======
is not in his right mind, ought I to give them back to him? No one
>>>>>>> remote
EOF
git read-tree --reset -u HEAD &&
test_must_fail git merge-recursive --ignore-space-at-eol \
HEAD^ -- HEAD remote &&
conflict_hunks text.txt >actual &&
test_cmp expected actual
'
test_done