* [PATCH 00/12] Towards a better merge resolution support @ 2008-08-30 0:42 Junio C Hamano 2008-08-30 0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano ` (2 more replies) 0 siblings, 3 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This consists of two loosely related topics on improving conflicted merge resolution support. The early part of the series is what you already saw. In addition to recording a conflicted merge in the RCS merge style we have traditionally used, this allows you to optionally use "diff3 -m" style. The difference is that the latter format shows the part from the common ancestor that corresponds to the parts both sides modified to cause the conflict, in addition to the changes done on each side. This can be chosen by setting a configuration variable. Rerere mechanism is updated to understand this new format as well, and conflicts from either formats interoperate well, because rerere mechanism only records and uses the changes made on each side, not what was in the common ancestor. The last four patches are to "git checkout" that checks things out of the index. When resolving conflicts, sometimes you would screw up the state of the working tree so badly that you would wish to redo the merge from the beginning for one file, without having to redo the whole merge. Some other times, you already know changes made on one side already solves everything the other side attempted to do, and would want to take the change from that side as a whole. New options supported by "git checkout" when checking out from the index for these purposes are: * git checkout -m -- path This recreates the merge using information staged in stages 1/2/3; * git checkout --ours -- path This writes 'our' version (stage #2) out for the path to the working tree; * git checkout --theirs -- path This writes 'their' version (stage #3) out for the path to the working tree; None of these operations mark the path resolved. They are to help you prepare the working tree into a shape suitable as the resolution, and you will still conclude it with "git add path". Junio C Hamano (12): xdl_fill_merge_buffer(): separate out a too deeply nested function xdiff-merge: optionally show conflicts in "diff3 -m" style xmerge.c: minimum readability fixups xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less rerere.c: use symbolic constants to keep track of parsing states rerere: understand "diff3 -m" style conflicts with the original merge.conflictstyle: choose between "merge" and "diff3 -m" styles git-merge-recursive: learn to honor merge.conflictstyle checkout: do not check out unmerged higher stages randomly checkout: allow ignoring unmerged paths when checking out of the index checkout --ours/--theirs checkout -m: recreate merge when checking out of unmerged index Documentation/config.txt | 8 ++ builtin-checkout.c | 206 +++++++++++++++++++++++++++++++++++---- builtin-merge-file.c | 17 +++- builtin-merge-recursive.c | 2 +- ll-merge.c | 16 +++- rerere.c | 29 ++++-- t/t6023-merge-file.sh | 44 +++++++++ t/t7201-co.sh | 133 +++++++++++++++++++++++++ xdiff-interface.c | 20 ++++ xdiff-interface.h | 2 + xdiff/xdiff.h | 6 + xdiff/xmerge.c | 237 +++++++++++++++++++++++++++++++-------------- 12 files changed, 612 insertions(+), 108 deletions(-) ^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function 2008-08-30 0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano 2008-08-30 9:14 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Johannes Schindelin 2008-09-01 9:39 ` [PATCH 00/12] Towards a better merge resolution support Alex Riesen 2008-09-01 9:44 ` Alex Riesen 2 siblings, 2 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This simply moves code around to make a separate function that prepares a single conflicted hunk with markers into the buffer. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- xdiff/xmerge.c | 121 ++++++++++++++++++++++++++++++++----------------------- 1 files changed, 70 insertions(+), 51 deletions(-) diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index 82b3573..6ffaa4f 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -113,65 +113,84 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) return size; } -static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, - xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest) +static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, + xdfenv_t *xe2, const char *name2, + int size, int i, + xdmerge_t *m, char *dest) { const int marker_size = 7; int marker1_size = (name1 ? strlen(name1) + 1 : 0); int marker2_size = (name2 ? strlen(name2) + 1 : 0); - int conflict_marker_size = 3 * (marker_size + 1) - + marker1_size + marker2_size; - int size, i1, j; - - for (size = i1 = 0; m; m = m->next) { - if (m->mode == 0) { - size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0, - dest ? dest + size : NULL); - if (dest) { - for (j = 0; j < marker_size; j++) - dest[size++] = '<'; - if (marker1_size) { - dest[size] = ' '; - memcpy(dest + size + 1, name1, - marker1_size - 1); - size += marker1_size; - } - dest[size++] = '\n'; - } else - size += conflict_marker_size; - size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, - dest ? dest + size : NULL); - if (dest) { - for (j = 0; j < marker_size; j++) - dest[size++] = '='; - dest[size++] = '\n'; - } - size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, - dest ? dest + size : NULL); - if (dest) { - for (j = 0; j < marker_size; j++) - dest[size++] = '>'; - if (marker2_size) { - dest[size] = ' '; - memcpy(dest + size + 1, name2, - marker2_size - 1); - size += marker2_size; - } - dest[size++] = '\n'; - } - } else if (m->mode == 1) - size += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0, - dest ? dest + size : NULL); + int j; + + /* Before conflicting part */ + size += xdl_recs_copy(xe1, i, m->i1 - i, 0, + dest ? dest + size : NULL); + + if (!dest) { + size += marker_size + 1 + marker1_size; + } else { + for (j = 0; j < marker_size; j++) + dest[size++] = '<'; + if (marker1_size) { + dest[size] = ' '; + memcpy(dest + size + 1, name1, marker1_size - 1); + size += marker1_size; + } + dest[size++] = '\n'; + } + + /* Postimage from side #1 */ + size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, + dest ? dest + size : NULL); + if (!dest) { + size += marker_size + 1; + } else { + for (j = 0; j < marker_size; j++) + dest[size++] = '='; + dest[size++] = '\n'; + } + + /* Postimage from side #2 */ + size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, + dest ? dest + size : NULL); + if (!dest) { + size += marker_size + 1 + marker2_size; + } else { + for (j = 0; j < marker_size; j++) + dest[size++] = '>'; + if (marker2_size) { + dest[size] = ' '; + memcpy(dest + size + 1, name2, marker2_size - 1); + size += marker2_size; + } + dest[size++] = '\n'; + } + return size; +} + +static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, + xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest) +{ + int size, i; + + for (size = i = 0; m; m = m->next) { + if (m->mode == 0) + size = fill_conflict_hunk(xe1, name1, xe2, name2, + size, i, m, dest); + else if (m->mode == 1) + size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0, + dest ? dest + size : NULL); else if (m->mode == 2) - size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1, - m->i1 + m->chg2 - i1, 0, - dest ? dest + size : NULL); + size += xdl_recs_copy(xe2, m->i2 - m->i1 + i, + m->i1 + m->chg2 - i, 0, + dest ? dest + size : NULL); else continue; - i1 = m->i1 + m->chg1; + i = m->i1 + m->chg1; } - size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0, - dest ? dest + size : NULL); + size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, + dest ? dest + size : NULL); return size; } -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style 2008-08-30 0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano 2008-08-30 9:29 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Johannes Schindelin 2008-08-30 9:14 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Johannes Schindelin 1 sibling, 2 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git When showing conflicting merges, we traditionally followed RCS's merge output format. The output shows: <<<<<<< postimage from one side; ======= postimage of the other side; and >>>>>>> Some poeple find it easier to be able to understand what is going on when they can view the common ancestor's version, which is used by "diff3 -m", which shows: <<<<<<< postimage from one side; ||||||| shared preimage; ======= postimage of the other side; and >>>>>>> This is an initial step to bring that as an optional feature to git. Only "git merge-file" has been converted, with "--diff3" option. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-merge-file.c | 10 ++++- t/t6023-merge-file.sh | 37 +++++++++++++++++ xdiff/xdiff.h | 6 +++ xdiff/xmerge.c | 103 ++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 135 insertions(+), 21 deletions(-) diff --git a/builtin-merge-file.c b/builtin-merge-file.c index 3605960..5b4f020 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -4,7 +4,7 @@ #include "xdiff-interface.h" static const char merge_file_usage[] = -"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"; +"git merge-file [-p | --stdout] [--diff3] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"; int cmd_merge_file(int argc, const char **argv, const char *prefix) { @@ -13,6 +13,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) mmbuffer_t result = {NULL, 0}; xpparam_t xpp = {XDF_NEED_MINIMAL}; int ret = 0, i = 0, to_stdout = 0; + int merge_level = XDL_MERGE_ZEALOUS_ALNUM; + int merge_style = 0; while (argc > 4) { if (!strcmp(argv[1], "-L") && i < 3) { @@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[1], "-q") || !strcmp(argv[1], "--quiet")) freopen("/dev/null", "w", stderr); + else if (!strcmp(argv[1], "--diff3")) { + merge_style = XDL_MERGE_DIFF3; + merge_level = XDL_MERGE_EAGER; + } else usage(merge_file_usage); argc--; @@ -46,7 +52,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, XDL_MERGE_ZEALOUS_ALNUM, &result); + &xpp, merge_level | merge_style, &result); for (i = 0; i < 3; i++) free(mmfs[i].ptr); diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index 42620e0..f3484e3 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -161,4 +161,41 @@ test_expect_success 'ZEALOUS_ALNUM' ' ' +cat >expect <<\EOF +Dominus regit me, +<<<<<<< new8.txt +et nihil mihi deerit; + + + + +In loco pascuae ibi me collocavit; +super aquam refectionis educavit me. +||||||| +et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +======= +et nihil mihi deerit, + + + + +In loco pascuae ibi me collocavit -- +super aquam refectionis educavit me, +>>>>>>> new9.txt +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +Nam et si ambulavero in medio umbrae mortis, +non timebo mala, quoniam TU mecum es: +virga tua et baculus tuus ipsa me consolata sunt. +EOF + +test_expect_success '"diff3 -m" style output' ' + test_must_fail git merge-file -p --diff3 \ + new8.txt new5.txt new9.txt >actual && + test_cmp expect actual +' + test_done diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h index 413082e..deebe02 100644 --- a/xdiff/xdiff.h +++ b/xdiff/xdiff.h @@ -50,10 +50,16 @@ extern "C" { #define XDL_BDOP_CPY 2 #define XDL_BDOP_INSB 3 +/* merge simplification levels */ #define XDL_MERGE_MINIMAL 0 #define XDL_MERGE_EAGER 1 #define XDL_MERGE_ZEALOUS 2 #define XDL_MERGE_ZEALOUS_ALNUM 3 +#define XDL_MERGE_LEVEL_MASK 0x0f + +/* merge output styles */ +#define XDL_MERGE_DIFF3 0x8000 +#define XDL_MERGE_STYLE_MASK 0x8000 typedef struct s_mmfile { char *ptr; diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index 6ffaa4f..29cdbea 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -30,17 +30,32 @@ typedef struct s_xdmerge { * 2 = no conflict, take second. */ int mode; + /* + * These point at the respective postimages. E.g. <i1,chg1> is + * how side #1 wants to change the common ancestor; if there is no + * overlap, lines before i1 in the postimage of side #1 appear + * in the merge result as a region touched by neither side. + */ long i1, i2; long chg1, chg2; + /* + * These point at the preimage; of course there is just one + * preimage, that is from the shared common ancestor. + */ + long i0; + long chg0; } xdmerge_t; static int xdl_append_merge(xdmerge_t **merge, int mode, - long i1, long chg1, long i2, long chg2) + long i0, long chg0, + long i1, long chg1, + long i2, long chg2) { xdmerge_t *m = *merge; if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) { if (mode != m->mode) m->mode = 0; + m->chg0 = i0 + chg0 - m->i0; m->chg1 = i1 + chg1 - m->i1; m->chg2 = i2 + chg2 - m->i2; } else { @@ -49,6 +64,8 @@ static int xdl_append_merge(xdmerge_t **merge, int mode, return -1; m->next = NULL; m->mode = mode; + m->i0 = i0; + m->chg0 = chg0; m->i1 = i1; m->chg1 = chg1; m->i2 = i2; @@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, return 0; } -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) { - xrecord_t **recs = xe->xdf2.recs + i; + xrecord_t **recs; int size = 0; + recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i; + if (count < 1) return 0; @@ -113,9 +132,19 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) return size; } +static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +{ + return xdl_recs_copy_0(0, xe, i, count, add_nl, dest); +} + +static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +{ + return xdl_recs_copy_0(1, xe, i, count, add_nl, dest); +} + static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, xdfenv_t *xe2, const char *name2, - int size, int i, + int size, int i, int style, xdmerge_t *m, char *dest) { const int marker_size = 7; @@ -143,6 +172,20 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1, /* Postimage from side #1 */ size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, dest ? dest + size : NULL); + + if (style == XDL_MERGE_DIFF3) { + /* Shared preimage */ + if (!dest) { + size += marker_size + 1; + } else { + for (j = 0; j < marker_size; j++) + dest[size++] = '|'; + dest[size++] = '\n'; + } + size += xdl_orig_copy(xe1, m->i0, m->chg0, 1, + dest ? dest + size : NULL); + } + if (!dest) { size += marker_size + 1; } else { @@ -170,14 +213,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, xdmerge_t *m, char *dest) + xdfenv_t *xe2, const char *name2, + xdmerge_t *m, char *dest, int style) { int size, i; for (size = i = 0; m; m = m->next) { if (m->mode == 0) size = fill_conflict_hunk(xe1, name1, xe2, name2, - size, i, m, dest); + size, i, style, m, dest); else if (m->mode == 1) size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0, dest ? dest + size : NULL); @@ -342,9 +386,11 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, */ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, xdfenv_t *xe2, xdchange_t *xscr2, const char *name2, - int level, xpparam_t const *xpp, mmbuffer_t *result) { + int flags, xpparam_t const *xpp, mmbuffer_t *result) { xdmerge_t *changes, *c; - int i1, i2, chg1, chg2; + int i0, i1, i2, chg0, chg1, chg2; + int level = flags & XDL_MERGE_LEVEL_MASK; + int style = flags & XDL_MERGE_STYLE_MASK; c = changes = NULL; @@ -352,11 +398,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, if (!changes) changes = c; if (xscr1->i1 + xscr1->chg1 < xscr2->i1) { + i0 = xscr1->i1; i1 = xscr1->i2; i2 = xscr2->i2 - xscr2->i1 + xscr1->i1; + chg0 = xscr1->chg1; chg1 = xscr1->chg2; chg2 = xscr1->chg1; - if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) { + if (xdl_append_merge(&c, 1, + i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } @@ -364,11 +413,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, continue; } if (xscr2->i1 + xscr2->chg1 < xscr1->i1) { + i0 = xscr2->i1; i1 = xscr1->i2 - xscr1->i1 + xscr2->i1; i2 = xscr2->i2; + chg0 = xscr2->chg1; chg1 = xscr2->chg1; chg2 = xscr2->chg2; - if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) { + if (xdl_append_merge(&c, 2, + i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } @@ -385,19 +437,26 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, int off = xscr1->i1 - xscr2->i1; int ffo = off + xscr1->chg1 - xscr2->chg1; + i0 = xscr1->i1; i1 = xscr1->i2; i2 = xscr2->i2; - if (off > 0) + if (off > 0) { + i0 -= off; i1 -= off; + } else i2 += off; + chg0 = xscr1->i1 + xscr1->chg1 - i0; chg1 = xscr1->i2 + xscr1->chg2 - i1; chg2 = xscr2->i2 + xscr2->chg2 - i2; if (ffo > 0) chg2 += ffo; - else + else { + chg0 -= ffo; chg1 -= ffo; - if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) { + } + if (xdl_append_merge(&c, 0, + i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } @@ -414,11 +473,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, while (xscr1) { if (!changes) changes = c; + i0 = xscr1->i1; i1 = xscr1->i2; i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec; + chg0 = xscr1->chg1; chg1 = xscr1->chg2; chg2 = xscr1->chg1; - if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) { + if (xdl_append_merge(&c, 1, + i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } @@ -427,11 +489,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, while (xscr2) { if (!changes) changes = c; + i0 = xscr2->i1; i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec; i2 = xscr2->i2; + chg0 = xscr2->chg1; chg1 = xscr2->chg1; chg2 = xscr2->chg2; - if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) { + if (xdl_append_merge(&c, 2, + i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); return -1; } @@ -449,7 +514,7 @@ 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); + changes, NULL, style); result->ptr = xdl_malloc(size); if (!result->ptr) { xdl_cleanup_merge(changes); @@ -457,14 +522,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, } result->size = size; xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes, - result->ptr); + result->ptr, style); } return xdl_cleanup_merge(changes); } int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, mmfile_t *mf2, const char *name2, - xpparam_t const *xpp, int level, mmbuffer_t *result) { + xpparam_t const *xpp, int flags, mmbuffer_t *result) { xdchange_t *xscr1, *xscr2; xdfenv_t xe1, xe2; int status; @@ -501,7 +566,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, } else { status = xdl_do_merge(&xe1, xscr1, name1, &xe2, xscr2, name2, - level, xpp, result); + flags, xpp, result); } xdl_free_script(xscr1); xdl_free_script(xscr2); -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 03/12] xmerge.c: minimum readability fixups 2008-08-30 0:42 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano 2008-08-30 9:31 ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin 2008-08-30 9:29 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Johannes Schindelin 1 sibling, 2 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This replaces hardcoded magic constants with symbolic ones for readability, and swaps one if/else blocks to better match the order in which 0/1/2 variables are handled to nearby codepath. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- xdiff/xmerge.c | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-) diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index 29cdbea..7dcd405 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -427,7 +427,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, xscr2 = xscr2->next; continue; } - if (level < 1 || xscr1->i1 != xscr2->i1 || + if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 || xscr1->chg1 != xscr2->chg1 || xscr1->chg2 != xscr2->chg2 || xdl_merge_cmp_lines(xe1, xscr1->i2, @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, chg0 = xscr1->i1 + xscr1->chg1 - i0; chg1 = xscr1->i2 + xscr1->chg2 - i1; chg2 = xscr2->i2 + xscr2->chg2 - i2; - if (ffo > 0) - chg2 += ffo; - else { + if (ffo < 0) { chg0 -= ffo; chg1 -= ffo; - } + } else + chg2 += ffo; if (xdl_append_merge(&c, 0, i0, chg0, i1, chg1, i2, chg2)) { xdl_cleanup_merge(changes); @@ -505,9 +504,10 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, if (!changes) changes = c; /* refine conflicts */ - if (level > 1 && + if (XDL_MERGE_ZEALOUS <= level && (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 || - xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) { + xdl_simplify_non_conflicts(xe1, changes, + XDL_MERGE_ZEALOUS < level) < 0)) { xdl_cleanup_merge(changes); return -1; } -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less 2008-08-30 0:42 ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano 2008-08-30 9:34 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Johannes Schindelin 2008-08-30 9:31 ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin 1 sibling, 2 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git When showing a conflicting merge result, and "--diff3 -m" style is asked for, this patch makes sure that the merge reduction level does not exceed XDL_MERGE_EAGER. This is because "diff3 -m" style output would not make sense for anything more aggressive than XDL_MERGE_EAGER, because of the way how the merge reduction works. "git merge-file" no longer has to force MERGE_EAGER when "--diff3" is asked for because of this change. Suppose a common ancestor (shared preimage) is modified to postimage #1 and #2 (each letter represents one line): ##### postimage#1: 1234ABCDE789 | / | / preimage: 123456789 | \ postimage#2: 1234AXYE789 #### XDL_MERGE_MINIMAL and XDL_MERGE_EAGER would: (1) find the s/56/ABCDE/ done on one side and s/56/AXYE/ done on the other side, (2) notice that they touch an overlapping area, and (3) mark it as a conflict, "ABCDE vs AXYE". The difference between the two algorithms is that EAGER drops the hunk altogether if the postimages match (i.e. both sides modified the same way), while MINIMAL keeps it. There is no other operation performed to the hunk. As the result, lines marked with "#" in the above picure will be in the RCS merge style output like this (letters <, = and > represent conflict marker lines): output: 1234<ABCDE=AXYE>789 ; with MINIMAL/EAGER The part from the preimage that corresponds to these conflicting changes is "56", which is what "diff3 -m" style output adds to it: output: 1234<ABCDE|56=AXYE>789 ; in "diff3 -m" style Now, XDL_MERGE_ZEALOUS looks at the differences between the changes two postimages made in order to reduce the number of lines in the conflicting regions. It notices that both sides start their new contents with "A", and excludes it from the output (it also excludes "E" for the same reason). The conflict that used to be "ABCDE vs AXYE" is now "BCD vs XY": output: 1234A<BCD=XY>E789 ; with ZEALOUS There could even be matching parts between two postimages in the middle. Instead of one side rewriting the shared "56" to "ABCDE" and the other side to "AXYE", imagine the case where the postimages are "ABCDE" and "AXCYE", in which case instead of having one conflicted hunk "BCD vs XY", you would have two conflicting hunks "B vs X" and "D vs Y". In either case, once you reduce "ABCDE vs AXYE" to "BCD vs XY" (or "ABCDE vs AXCYE" to "B vs X" and "D vs Y"), there is no part from the preimage that corresponds to the conflicting change made in both postimages anymore. In other words, conflict reduced by ZEALOUS algorithm cannot be expressed in "diff3 -m" style. Representing the last illustration like this is misleading to say the least: output: 1234A<BCD|56=XY>E789 ; broken "diff3 -m" style because the preimage was not ...4A56E... to begin with. "A" and "E" are common only between the postimages. Even worse, once a single conflicting hunk is split into multiple ones (recall the example of breaking "ABCDE vs AXCYE" to "B vs X" and "D vs Y"), there is no sane way to distribute the preimage text across split conflicting hunks. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-merge-file.c | 4 +--- xdiff/xmerge.c | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/builtin-merge-file.c b/builtin-merge-file.c index 5b4f020..1e92510 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -27,10 +27,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[1], "-q") || !strcmp(argv[1], "--quiet")) freopen("/dev/null", "w", stderr); - else if (!strcmp(argv[1], "--diff3")) { + else if (!strcmp(argv[1], "--diff3")) merge_style = XDL_MERGE_DIFF3; - merge_level = XDL_MERGE_EAGER; - } else usage(merge_file_usage); argc--; diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index 7dcd405..d9737f0 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -392,6 +392,15 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, int level = flags & XDL_MERGE_LEVEL_MASK; int style = flags & XDL_MERGE_STYLE_MASK; + if (style == XDL_MERGE_DIFF3) { + /* + * "diff3 -m" output does not make sense for anything + * more aggressive than XDL_MERGE_EAGER. + */ + if (XDL_MERGE_EAGER < level) + level = XDL_MERGE_EAGER; + } + c = changes = NULL; while (xscr1 && xscr2) { -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states 2008-08-30 0:42 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original Junio C Hamano 2008-08-30 9:34 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Johannes Schindelin 1 sibling, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git These hardcoded integers make the code harder to follow than necessary; replace them with enums to make it easier to read, before adding support for optionally parsing "diff3 -m" style conflict markers. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- rerere.c | 23 +++++++++++++---------- 1 files changed, 13 insertions(+), 10 deletions(-) diff --git a/rerere.c b/rerere.c index 323e493..bf74b26 100644 --- a/rerere.c +++ b/rerere.c @@ -75,7 +75,10 @@ static int handle_file(const char *path, { SHA_CTX ctx; char buf[1024]; - int hunk = 0, hunk_no = 0; + int hunk_no = 0; + enum { + RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, + } hunk = RR_CONTEXT; struct strbuf one, two; FILE *f = fopen(path, "r"); FILE *out = NULL; @@ -98,20 +101,20 @@ static int handle_file(const char *path, strbuf_init(&two, 0); while (fgets(buf, sizeof(buf), f)) { if (!prefixcmp(buf, "<<<<<<< ")) { - if (hunk) + if (hunk != RR_CONTEXT) goto bad; - hunk = 1; + hunk = RR_SIDE_1; } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) { - if (hunk != 1) + if (hunk != RR_SIDE_1) goto bad; - hunk = 2; + hunk = RR_SIDE_2; } else if (!prefixcmp(buf, ">>>>>>> ")) { - if (hunk != 2) + if (hunk != RR_SIDE_2) goto bad; if (strbuf_cmp(&one, &two) > 0) strbuf_swap(&one, &two); hunk_no++; - hunk = 0; + hunk = RR_CONTEXT; if (out) { fputs("<<<<<<<\n", out); fwrite(one.buf, one.len, 1, out); @@ -127,9 +130,9 @@ static int handle_file(const char *path, } strbuf_reset(&one); strbuf_reset(&two); - } else if (hunk == 1) + } else if (hunk == RR_SIDE_1) strbuf_addstr(&one, buf); - else if (hunk == 2) + else if (hunk == RR_SIDE_2) strbuf_addstr(&two, buf); else if (out) fputs(buf, out); @@ -146,7 +149,7 @@ static int handle_file(const char *path, fclose(out); if (sha1) SHA1_Final(sha1, &ctx); - if (hunk) { + if (hunk != RR_CONTEXT) { if (output) unlink(output); return error("Could not parse conflict hunks in %s", path); -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original 2008-08-30 0:42 ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This teaches rerere to grok conflicts expressed in "diff3 -m" style output, where the version from the common ancestor is output after the first side, preceded by a "|||||||" line. The rerere database needs to keep only the versions from two sides, so the code parses the original copy and discards it. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- rerere.c | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rerere.c b/rerere.c index bf74b26..4e2c9dd 100644 --- a/rerere.c +++ b/rerere.c @@ -77,7 +77,7 @@ static int handle_file(const char *path, char buf[1024]; int hunk_no = 0; enum { - RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, + RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL, } hunk = RR_CONTEXT; struct strbuf one, two; FILE *f = fopen(path, "r"); @@ -104,9 +104,13 @@ static int handle_file(const char *path, if (hunk != RR_CONTEXT) goto bad; hunk = RR_SIDE_1; - } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) { + } else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) { if (hunk != RR_SIDE_1) goto bad; + hunk = RR_ORIGINAL; + } else if (!prefixcmp(buf, "=======") && isspace(buf[7])) { + if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL) + goto bad; hunk = RR_SIDE_2; } else if (!prefixcmp(buf, ">>>>>>> ")) { if (hunk != RR_SIDE_2) @@ -132,6 +136,8 @@ static int handle_file(const char *path, strbuf_reset(&two); } else if (hunk == RR_SIDE_1) strbuf_addstr(&one, buf); + else if (hunk == RR_ORIGINAL) + ; /* discard */ else if (hunk == RR_SIDE_2) strbuf_addstr(&two, buf); else if (out) -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles 2008-08-30 0:42 ` [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano 2008-08-30 9:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Johannes Schindelin 0 siblings, 2 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This teaches "git merge-file" to honor merge.conflictstyle configuration variable, whose value can be "merge" (default) or "diff3". Signed-off-by: Junio C Hamano <gitster@pobox.com> --- Documentation/config.txt | 8 ++++++++ builtin-merge-file.c | 9 +++++++++ t/t6023-merge-file.sh | 9 ++++++++- xdiff-interface.c | 20 ++++++++++++++++++++ xdiff-interface.h | 2 ++ 5 files changed, 47 insertions(+), 1 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index af57d94..cb4c4ca 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -893,6 +893,14 @@ man.<tool>.path:: Override the path for the given tool that may be used to display help in the 'man' format. See linkgit:git-help[1]. +merge.conflictstyle:: + Specify the style in which conflicted hunks are written out to + working tree files upon merge. The default is "merge", which + shows `<<<<<<<` conflict marker, change made by one side, + `=======` marker, change made by the other side, and then + `>>>>>>>` marker. An alternate style, "diff3", adds `|||||||` + marker and the original text before `=======` marker. + mergetool.<tool>.path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. diff --git a/builtin-merge-file.c b/builtin-merge-file.c index 1e92510..f009e73 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -15,6 +15,15 @@ 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; + int nongit; + + prefix = setup_git_directory_gently(&nongit); + if (!nongit) { + /* Read the configuration file */ + git_config(git_xmerge_config, NULL); + if (git_xmerge_style > 0) + merge_style = git_xmerge_style; + } while (argc > 4) { if (!strcmp(argv[1], "-L") && i < 3) { diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index f3484e3..93ec517 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -192,10 +192,17 @@ non timebo mala, quoniam TU mecum es: virga tua et baculus tuus ipsa me consolata sunt. EOF -test_expect_success '"diff3 -m" style output' ' +test_expect_success '"diff3 -m" style output (1)' ' test_must_fail git merge-file -p --diff3 \ new8.txt new5.txt new9.txt >actual && test_cmp expect actual ' +test_expect_success '"diff3 -m" style output (2)' ' + git config merge.conflictstyle diff3 && + test_must_fail git merge-file -p \ + new8.txt new5.txt new9.txt >actual && + test_cmp expect actual +' + test_done diff --git a/xdiff-interface.c b/xdiff-interface.c index 944ad98..8457601 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -249,3 +249,23 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value) value = ep + 1; } } + +int git_xmerge_style = -1; + +int git_xmerge_config(const char *var, const char *value, void *cb) +{ + if (!strcasecmp(var, "merge.conflictstyle")) { + if (!value) + die("'%s' is not a boolean", var); + if (!strcmp(value, "diff3")) + git_xmerge_style = XDL_MERGE_DIFF3; + else if (!strcmp(value, "merge")) + git_xmerge_style = 0; + else + die("unknown style '%s' given for '%s'", + value, var); + return 0; + } + return git_default_config(var, value, cb); +} + diff --git a/xdiff-interface.h b/xdiff-interface.h index 558492b..b3b5c93 100644 --- a/xdiff-interface.h +++ b/xdiff-interface.h @@ -17,5 +17,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename); int buffer_is_binary(const char *ptr, unsigned long size); extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line); +extern int git_xmerge_config(const char *var, const char *value, void *cb); +extern int git_xmerge_style; #endif -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle 2008-08-30 0:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 09/12] checkout: do not check out unmerged higher stages randomly Junio C Hamano 2008-08-30 9:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Johannes Schindelin 1 sibling, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This teaches the low-level ll_xdl_merge() routine to honor merge.conflictstyle configuration variable, so that merge-recursive strategy can show the conflicts in the style of user's choice. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-merge-recursive.c | 2 +- ll-merge.c | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index dfb363e..f3b6ede 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -1348,7 +1348,7 @@ static int merge_config(const char *var, const char *value, void *cb) merge_rename_limit = git_config_int(var, value); return 0; } - return git_default_config(var, value, cb); + return git_xmerge_config(var, value, cb); } int cmd_merge_recursive(int argc, const char **argv, const char *prefix) diff --git a/ll-merge.c b/ll-merge.c index 9837c84..4a71614 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -63,6 +63,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, int virtual_ancestor) { xpparam_t xpp; + int style = 0; if (buffer_is_binary(orig->ptr, orig->size) || buffer_is_binary(src1->ptr, src1->size) || @@ -77,10 +78,12 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, } memset(&xpp, 0, sizeof(xpp)); + if (git_xmerge_style >= 0) + style = git_xmerge_style; return xdl_merge(orig, src1, name1, src2, name2, - &xpp, XDL_MERGE_ZEALOUS, + &xpp, XDL_MERGE_ZEALOUS | style, result); } @@ -95,10 +98,15 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused, char *src, *dst; long size; const int marker_size = 7; - - int status = ll_xdl_merge(drv_unused, result, path_unused, - orig, src1, NULL, src2, NULL, - virtual_ancestor); + int status, saved_style; + + /* We have to force the RCS "merge" style */ + saved_style = git_xmerge_style; + git_xmerge_style = 0; + status = ll_xdl_merge(drv_unused, result, path_unused, + orig, src1, NULL, src2, NULL, + virtual_ancestor); + git_xmerge_style = saved_style; if (status <= 0) return status; size = result->size; -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 09/12] checkout: do not check out unmerged higher stages randomly 2008-08-30 0:42 ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git During a conflicted merge when you have unmerged stages for a path F in the index, if you said: $ git checkout F we rewrote F as many times as we have stages for it, and the last one (typically "theirs") was left in the work tree, without resolving the conflict. This fixes it by noticing that a specified pathspec pattern matches an unmerged path, and by erroring out. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-checkout.c | 27 +++++++++++++++++++++++++++ t/t7201-co.sh | 23 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 0 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index b380ad6..9b33f3a 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -76,6 +76,15 @@ static int read_tree_some(struct tree *tree, const char **pathspec) return 0; } +static int skip_same_name(struct cache_entry *ce, int pos) +{ + while (++pos < active_nr && + !strcmp(active_cache[pos]->name, ce->name)) + ; /* skip */ + return pos; +} + + static int checkout_paths(struct tree *source_tree, const char **pathspec) { int pos; @@ -107,6 +116,20 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) if (report_path_error(ps_matched, pathspec, 0)) return 1; + /* Any unmerged paths? */ + for (pos = 0; pos < active_nr; pos++) { + struct cache_entry *ce = active_cache[pos]; + if (pathspec_match(pathspec, NULL, ce->name, 0) && + ce_stage(ce)) { + errs = 1; + error("path '%s' is unmerged", ce->name); + pos = skip_same_name(ce, pos) - 1; + continue; + } + } + if (errs) + return 1; + /* Now we are committed to check them out */ memset(&state, 0, sizeof(state)); state.force = 1; @@ -114,6 +137,10 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0)) { + if (ce_stage(ce)) { + pos = skip_same_name(ce, pos) - 1; + continue; + } errs |= checkout_entry(ce, &state, NULL); } } diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 1dff84d..303cf62 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -369,4 +369,27 @@ test_expect_success \ 'checkout with --track, but without -b, fails with too short tracked name' ' test_must_fail git checkout --track renamer' +test_expect_success 'checkout an unmerged path should fail' ' + rm -f .git/index && + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + ( + echo "100644 $A 0 fild" && + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" && + echo "100644 $A 0 filf" + ) | git update-index --index-info && + echo "none of the above" >sample && + cat sample >fild && + cat sample >file && + cat sample >filf && + test_must_fail git checkout fild file filf && + test_cmp sample fild && + test_cmp sample filf && + test_cmp sample file +' + test_done + -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index 2008-08-30 0:42 ` [PATCH 09/12] checkout: do not check out unmerged higher stages randomly Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 11/12] checkout --ours/--theirs Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git Earlier we made "git checkout $pathspec" to atomically refuse the operation of $pathspec matched any path with unmerged stages. This patch allows: $ git checkout -f a b c to ignore, instead of error out on, such unmerged paths. The fix to prevent checkout of an unmerged path from random stages is still there. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-checkout.c | 41 +++++++++++++++++++++++------------------ t/t7201-co.sh | 24 +++++++++++++++++++++++- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 9b33f3a..49c43d9 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -20,6 +20,17 @@ static const char * const checkout_usage[] = { NULL, }; +struct checkout_opts { + int quiet; + int merge; + int force; + int writeout_error; + + const char *new_branch; + int new_branch_log; + enum branch_track track; +}; + static int post_checkout_hook(struct commit *old, struct commit *new, int changed) { @@ -85,7 +96,8 @@ static int skip_same_name(struct cache_entry *ce, int pos) } -static int checkout_paths(struct tree *source_tree, const char **pathspec) +static int checkout_paths(struct tree *source_tree, const char **pathspec, + struct checkout_opts *opts) { int pos; struct checkout state; @@ -121,8 +133,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0) && ce_stage(ce)) { - errs = 1; - error("path '%s' is unmerged", ce->name); + if (!opts->force) { + errs = 1; + error("path '%s' is unmerged", ce->name); + } else { + warning("path '%s' is unmerged", ce->name); + } pos = skip_same_name(ce, pos) - 1; continue; } @@ -178,17 +194,6 @@ static void describe_detached_head(char *msg, struct commit *commit) strbuf_release(&sb); } -struct checkout_opts { - int quiet; - int merge; - int force; - int writeout_error; - - const char *new_branch; - int new_branch_log; - enum branch_track track; -}; - static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) { struct unpack_trees_options opts; @@ -569,15 +574,15 @@ no_reference: die("invalid path specification"); /* Checkout paths */ - if (opts.new_branch || opts.force || opts.merge) { + if (opts.new_branch || opts.merge) { if (argc == 1) { - die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); + die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); } else { - die("git checkout: updating paths is incompatible with switching branches/forcing"); + die("git checkout: updating paths is incompatible with switching branches."); } } - return checkout_paths(source_tree, pathspec); + return checkout_paths(source_tree, pathspec, &opts); } if (new.name && !new.commit) { diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 303cf62..b7274eb 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -391,5 +391,27 @@ test_expect_success 'checkout an unmerged path should fail' ' test_cmp sample file ' -test_done +test_expect_success 'checkout with an unmerged path can be ignored' ' + rm -f .git/index && + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + ( + echo "100644 $A 0 fild" && + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" && + echo "100644 $A 0 filf" + ) | git update-index --index-info && + echo "none of the above" >sample && + echo ourside >expect && + cat sample >fild && + cat sample >file && + cat sample >filf && + git checkout -f fild file filf && + test_cmp expect fild && + test_cmp expect filf && + test_cmp sample file +' +test_done -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 11/12] checkout --ours/--theirs 2008-08-30 0:42 ` [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 2008-08-30 0:42 ` [PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This lets you to check out 'our' (or 'their') version of an unmerged path out of the index while resolving conflicts. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-checkout.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- t/t7201-co.sh | 25 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 49c43d9..bdfdb65 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -24,6 +24,7 @@ struct checkout_opts { int quiet; int merge; int force; + int writeout_stage; int writeout_error; const char *new_branch; @@ -95,6 +96,32 @@ static int skip_same_name(struct cache_entry *ce, int pos) return pos; } +static int check_stage(int stage, struct cache_entry *ce, int pos) +{ + while (pos < active_nr && + !strcmp(active_cache[pos]->name, ce->name)) { + if (ce_stage(active_cache[pos]) == stage) + return 0; + pos++; + } + return error("path '%s' does not have %s version", + ce->name, + (stage == 2) ? "our" : "their"); +} + +static int checkout_stage(int stage, struct cache_entry *ce, int pos, + struct checkout *state) +{ + while (pos < active_nr && + !strcmp(active_cache[pos]->name, ce->name)) { + if (ce_stage(active_cache[pos]) == stage) + return checkout_entry(active_cache[pos], state, NULL); + pos++; + } + return error("path '%s' does not have %s version", + ce->name, + (stage == 2) ? "our" : "their"); +} static int checkout_paths(struct tree *source_tree, const char **pathspec, struct checkout_opts *opts) @@ -106,6 +133,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, int flag; struct commit *head; int errs = 0; + int stage = opts->writeout_stage; int newfd; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -131,13 +159,16 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, /* Any unmerged paths? */ for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; - if (pathspec_match(pathspec, NULL, ce->name, 0) && - ce_stage(ce)) { - if (!opts->force) { + if (pathspec_match(pathspec, NULL, ce->name, 0)) { + if (!ce_stage(ce)) + continue; + if (stage) { + errs |= check_stage(stage, ce, pos); + } else if (opts->force) { + warning("path '%s' is unmerged", ce->name); + } else { errs = 1; error("path '%s' is unmerged", ce->name); - } else { - warning("path '%s' is unmerged", ce->name); } pos = skip_same_name(ce, pos) - 1; continue; @@ -154,6 +185,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0)) { if (ce_stage(ce)) { + if (stage) + errs |= checkout_stage(stage, ce, pos, &state); pos = skip_same_name(ce, pos) - 1; continue; } @@ -458,6 +491,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), OPT_SET_INT('t', "track", &opts.track, "track", BRANCH_TRACK_EXPLICIT), + OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage", + 2), + OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage", + 3), OPT_BOOLEAN('f', NULL, &opts.force, "force"), OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), OPT_END(), diff --git a/t/t7201-co.sh b/t/t7201-co.sh index b7274eb..85c792c 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -414,4 +414,29 @@ test_expect_success 'checkout with an unmerged path can be ignored' ' test_cmp sample file ' +test_expect_success 'checkout unmerged stage' ' + rm -f .git/index && + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + ( + echo "100644 $A 0 fild" && + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" && + echo "100644 $A 0 filf" + ) | git update-index --index-info && + echo "none of the above" >sample && + echo ourside >expect && + cat sample >fild && + cat sample >file && + cat sample >filf && + git checkout --ours . && + test_cmp expect fild && + test_cmp expect filf && + test_cmp expect file && + git checkout --theirs file && + test ztheirside = "z$(cat file)" +' + test_done -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index 2008-08-30 0:42 ` [PATCH 11/12] checkout --ours/--theirs Junio C Hamano @ 2008-08-30 0:42 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 0:42 UTC (permalink / raw) To: git This teaches git-checkout to recreate a merge out of unmerged index entries while resolving conflicts. With this patch, checking out an unmerged path from the index now have the following possibilities: * Without any option, an attempt to checkout an unmerged path will atomically fail (i.e. no other cleanly-merged paths are checked out either); * With "-f", other cleanly-merged paths are checked out, and unmerged paths are ignored; * With "--ours" or "--theirs, the contents from the specified stage is checked out; * With "-m" (we should add "--merge" as synonym), the 3-way merge is recreated from the staged object names and checked out. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- builtin-checkout.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++---- t/t7201-co.sh | 63 ++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 8 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index bdfdb65..8b5da7f 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -13,6 +13,9 @@ #include "diff.h" #include "revision.h" #include "remote.h" +#include "blob.h" +#include "xdiff-interface.h" +#include "ll-merge.h" static const char * const checkout_usage[] = { "git checkout [options] <branch>", @@ -109,6 +112,19 @@ static int check_stage(int stage, struct cache_entry *ce, int pos) (stage == 2) ? "our" : "their"); } +static int check_all_stage(struct cache_entry *ce, int pos) +{ + if (ce_stage(ce) != 1 || + active_nr <= pos + 2 || + strcmp(active_cache[pos+1]->name, ce->name) || + ce_stage(active_cache[pos+1]) != 2 || + strcmp(active_cache[pos+2]->name, ce->name) || + ce_stage(active_cache[pos+2]) != 3) + return error("path '%s' does not have all three versions", + ce->name); + return 0; +} + static int checkout_stage(int stage, struct cache_entry *ce, int pos, struct checkout *state) { @@ -123,6 +139,77 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos, (stage == 2) ? "our" : "their"); } +/* NEEDSWORK: share with merge-recursive */ +static void fill_mm(const unsigned char *sha1, mmfile_t *mm) +{ + unsigned long size; + enum object_type type; + + if (!hashcmp(sha1, null_sha1)) { + mm->ptr = xstrdup(""); + mm->size = 0; + return; + } + + mm->ptr = read_sha1_file(sha1, &type, &size); + if (!mm->ptr || type != OBJ_BLOB) + die("unable to read blob object %s", sha1_to_hex(sha1)); + mm->size = size; +} + +static int checkout_merged(int pos, struct checkout *state) +{ + struct cache_entry *ce = active_cache[pos]; + const char *path = ce->name; + mmfile_t ancestor, ours, theirs; + int status; + unsigned char sha1[20]; + mmbuffer_t result_buf; + + if (ce_stage(ce) != 1 || + active_nr <= pos + 2 || + strcmp(active_cache[pos+1]->name, path) || + ce_stage(active_cache[pos+1]) != 2 || + strcmp(active_cache[pos+2]->name, path) || + ce_stage(active_cache[pos+2]) != 3) + return error("path '%s' does not have all 3 versions", path); + + fill_mm(active_cache[pos]->sha1, &ancestor); + fill_mm(active_cache[pos+1]->sha1, &ours); + fill_mm(active_cache[pos+2]->sha1, &theirs); + + status = ll_merge(&result_buf, path, &ancestor, + &ours, "ours", &theirs, "theirs", 1); + free(ancestor.ptr); + free(ours.ptr); + free(theirs.ptr); + if (status < 0 || !result_buf.ptr) { + free(result_buf.ptr); + return error("path '%s': cannot merge", path); + } + + /* + * NEEDSWORK: + * There is absolutely no reason to write this as a blob object + * and create a phoney cache entry just to leak. This hack is + * primarily to get to the write_entry() machinery that massages + * the contents to work-tree format and writes out which only + * allows it for a cache entry. The code in write_entry() needs + * to be refactored to allow us to feed a <buffer, size, mode> + * instead of a cache entry. Such a refactoring would help + * merge_recursive as well (it also writes the merge result to the + * object database even when it may contain conflicts). + */ + if (write_sha1_file(result_buf.ptr, result_buf.size, + blob_type, sha1)) + die("Unable to add merge result for '%s'", path); + ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode), + sha1, + path, 2, 0); + status = checkout_entry(ce, state, NULL); + return status; +} + static int checkout_paths(struct tree *source_tree, const char **pathspec, struct checkout_opts *opts) { @@ -134,7 +221,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, struct commit *head; int errs = 0; int stage = opts->writeout_stage; - + int merge = opts->merge; int newfd; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -166,6 +253,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, errs |= check_stage(stage, ce, pos); } else if (opts->force) { warning("path '%s' is unmerged", ce->name); + } else if (opts->merge) { + errs |= check_all_stage(ce, pos); } else { errs = 1; error("path '%s' is unmerged", ce->name); @@ -184,13 +273,15 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec, for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0)) { - if (ce_stage(ce)) { - if (stage) - errs |= checkout_stage(stage, ce, pos, &state); - pos = skip_same_name(ce, pos) - 1; + if (!ce_stage(ce)) { + errs |= checkout_entry(ce, &state, NULL); continue; } - errs |= checkout_entry(ce, &state, NULL); + if (stage) + errs |= checkout_stage(stage, ce, pos, &state); + else if (merge) + errs |= checkout_merged(pos, &state); + pos = skip_same_name(ce, pos) - 1; } } @@ -478,6 +569,11 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) return ret || opts->writeout_error; } +static int git_checkout_config(const char *var, const char *value, void *cb) +{ + return git_xmerge_config(var, value, cb); +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; @@ -504,7 +600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); - git_config(git_default_config, NULL); + git_config(git_checkout_config, NULL); opts.track = BRANCH_TRACK_UNSPECIFIED; @@ -611,7 +707,7 @@ no_reference: die("invalid path specification"); /* Checkout paths */ - if (opts.new_branch || opts.merge) { + if (opts.new_branch) { if (argc == 1) { die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); } else { @@ -619,6 +715,9 @@ no_reference: } } + if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) + die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."); + return checkout_paths(source_tree, pathspec, &opts); } diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 85c792c..6016915 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -439,4 +439,67 @@ test_expect_success 'checkout unmerged stage' ' test ztheirside = "z$(cat file)" ' +test_expect_success 'checkout with --merge' ' + rm -f .git/index && + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + ( + echo "100644 $A 0 fild" && + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" && + echo "100644 $A 0 filf" + ) | git update-index --index-info && + echo "none of the above" >sample && + echo ourside >expect && + cat sample >fild && + cat sample >file && + cat sample >filf && + git checkout -m -- fild file filf && + ( + echo "<<<<<<< ours" + echo ourside + echo "=======" + echo theirside + echo ">>>>>>> theirs" + ) >merged && + test_cmp expect fild && + test_cmp expect filf && + test_cmp merged file +' + +test_expect_success 'checkout with --merge, in diff3 -m style' ' + git config merge.conflictstyle diff3 && + rm -f .git/index && + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + ( + echo "100644 $A 0 fild" && + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" && + echo "100644 $A 0 filf" + ) | git update-index --index-info && + echo "none of the above" >sample && + echo ourside >expect && + cat sample >fild && + cat sample >file && + cat sample >filf && + git checkout -m -- fild file filf && + ( + echo "<<<<<<< ours" + echo ourside + echo "|||||||" + echo original + echo "=======" + echo theirside + echo ">>>>>>> theirs" + ) >merged && + test_cmp expect fild && + test_cmp expect filf && + test_cmp merged file +' + test_done -- 1.6.0.1.149.ga4c44 ^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles 2008-08-30 0:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano 2008-08-30 0:42 ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano @ 2008-08-30 9:42 ` Johannes Schindelin 1 sibling, 0 replies; 26+ messages in thread From: Johannes Schindelin @ 2008-08-30 9:42 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Hi, On Fri, 29 Aug 2008, Junio C Hamano wrote: > diff --git a/builtin-merge-file.c b/builtin-merge-file.c > index 1e92510..f009e73 100644 > --- a/builtin-merge-file.c > +++ b/builtin-merge-file.c > @@ -15,6 +15,15 @@ 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; > + int nongit; > + > + prefix = setup_git_directory_gently(&nongit); > + if (!nongit) { > + /* Read the configuration file */ > + git_config(git_xmerge_config, NULL); > + if (git_xmerge_style > 0) > + merge_style = git_xmerge_style; Did you not mean ">="? In the future, the default merge style could very well change... Ciao, Dscho ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less 2008-08-30 0:42 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano 2008-08-30 0:42 ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano @ 2008-08-30 9:34 ` Johannes Schindelin 1 sibling, 0 replies; 26+ messages in thread From: Johannes Schindelin @ 2008-08-30 9:34 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Hi, On Fri, 29 Aug 2008, Junio C Hamano wrote: > Representing the last illustration like this is misleading to say the > least: > > output: 1234A<BCD|56=XY>E789 ; broken "diff3 -m" style > > because the preimage was not ...4A56E... to begin with. "A" and "E" are > common only between the postimages. Okay, I understand now. Thanks, Dscho ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 03/12] xmerge.c: minimum readability fixups 2008-08-30 0:42 ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano 2008-08-30 0:42 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano @ 2008-08-30 9:31 ` Johannes Schindelin 2008-08-30 15:42 ` Junio C Hamano 1 sibling, 1 reply; 26+ messages in thread From: Johannes Schindelin @ 2008-08-30 9:31 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Hi, On Fri, 29 Aug 2008, Junio C Hamano wrote: > diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c > index 29cdbea..7dcd405 100644 > --- a/xdiff/xmerge.c > +++ b/xdiff/xmerge.c > @@ -427,7 +427,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, > xscr2 = xscr2->next; > continue; > } > - if (level < 1 || xscr1->i1 != xscr2->i1 || > + if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 || Yeah, okay, sorry. > @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, > chg0 = xscr1->i1 + xscr1->chg1 - i0; > chg1 = xscr1->i2 + xscr1->chg2 - i1; > chg2 = xscr2->i2 + xscr2->chg2 - i2; > - if (ffo > 0) > - chg2 += ffo; > - else { > + if (ffo < 0) { > chg0 -= ffo; > chg1 -= ffo; > - } > + } else > + chg2 += ffo; I do not understand why the order was changed, but hey, I do not care that deeply. Ciao, Dscho ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 03/12] xmerge.c: minimum readability fixups 2008-08-30 9:31 ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin @ 2008-08-30 15:42 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2008-08-30 15:42 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git Johannes Schindelin <Johannes.Schindelin@gmx.de> writes: >> @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, >> chg0 = xscr1->i1 + xscr1->chg1 - i0; >> chg1 = xscr1->i2 + xscr1->chg2 - i1; >> chg2 = xscr2->i2 + xscr2->chg2 - i2; >> - if (ffo > 0) >> - chg2 += ffo; >> - else { >> + if (ffo < 0) { >> chg0 -= ffo; >> chg1 -= ffo; >> - } >> + } else >> + chg2 += ffo; > > I do not understand why the order was changed, but hey, I do not care that > deeply. Everywhere else the code deals with variables var0, var1 and var2 in this order, and that is because "if" blocks are consistently about changes made on side#1 while "else" blocks are about changes made on side#2. This statement alone was inconsistent, and now it all reads 0, 1 and then 2. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style 2008-08-30 0:42 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano 2008-08-30 0:42 ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano @ 2008-08-30 9:29 ` Johannes Schindelin 1 sibling, 0 replies; 26+ messages in thread From: Johannes Schindelin @ 2008-08-30 9:29 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Hi, On Fri, 29 Aug 2008, Junio C Hamano wrote: > diff --git a/builtin-merge-file.c b/builtin-merge-file.c > index 3605960..5b4f020 100644 > --- a/builtin-merge-file.c > +++ b/builtin-merge-file.c > @@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) > else if (!strcmp(argv[1], "-q") || > !strcmp(argv[1], "--quiet")) > freopen("/dev/null", "w", stderr); > + else if (!strcmp(argv[1], "--diff3")) { > + merge_style = XDL_MERGE_DIFF3; > + merge_level = XDL_MERGE_EAGER; > + } FWIW I do not follow your reasoning why --diff3 does not make sense for anything more eager than MERGE_EAGER. All that ZEALOUS and ZEALOUS_ALNUM (the latter of which is useless at the moment, since it is not enabled for git-merge) do is change the way the conflicting regions are displayed, but they do not leave out conflicting regions. So I actually suspect that ZEALOUS_ALNUM will be _especially_ useful with --diff3, since it is designed to skip the single curly brackets that would disrupt the reading pleasure otherwise. > diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h > index 413082e..deebe02 100644 > --- a/xdiff/xdiff.h > +++ b/xdiff/xdiff.h > @@ -50,10 +50,16 @@ extern "C" { > #define XDL_BDOP_CPY 2 > #define XDL_BDOP_INSB 3 > > +/* merge simplification levels */ > #define XDL_MERGE_MINIMAL 0 > #define XDL_MERGE_EAGER 1 > #define XDL_MERGE_ZEALOUS 2 > #define XDL_MERGE_ZEALOUS_ALNUM 3 > +#define XDL_MERGE_LEVEL_MASK 0x0f > + > +/* merge output styles */ > +#define XDL_MERGE_DIFF3 0x8000 > +#define XDL_MERGE_STYLE_MASK 0x8000 Hmm. This is not the Linux kernel, I think we could safely pass around two integers instead of one. > @@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, > return 0; > } > > -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) > +static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest) You rewrapped many function headers already; I wonder why this one was left out. The rest looks pretty much obviously correct to me; I was too lazy/ran out of time to apply the patch and look through the resulting code, though, but I guess that you searched for "i1" and "chg1" and added the code for i0 and chg0 where necessary. So: ACK. Ciao, Dscho ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function 2008-08-30 0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano 2008-08-30 0:42 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano @ 2008-08-30 9:14 ` Johannes Schindelin 1 sibling, 0 replies; 26+ messages in thread From: Johannes Schindelin @ 2008-08-30 9:14 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Hi, On Fri, 29 Aug 2008, Junio C Hamano wrote: > This simply moves code around to make a separate function that prepares > a single conflicted hunk with markers into the buffer. Apart from renaming "i1" to "i", inverting the order of the if clauses, and making it more obvious that the calculation of size without dest is correct, this is a straight forward refactoring. Ciao, Dscho ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-08-30 0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano 2008-08-30 0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano @ 2008-09-01 9:39 ` Alex Riesen 2008-09-01 9:44 ` Alex Riesen 2 siblings, 0 replies; 26+ messages in thread From: Alex Riesen @ 2008-09-01 9:39 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Junio C Hamano, Sat, Aug 30, 2008 02:42:31 +0200: > The early part of the series is what you already saw. In addition to > recording a conflicted merge in the RCS merge style we have traditionally > used, this allows you to optionally use "diff3 -m" style. The difference > is that the latter format shows the part from the common ancestor that > corresponds to the parts both sides modified to cause the conflict, in > addition to the changes done on each side. This can be chosen by setting > a configuration variable. Rerere mechanism is updated to understand this > new format as well, and conflicts from either formats interoperate well, > because rerere mechanism only records and uses the changes made on each > side, not what was in the common ancestor. > > The last four patches are to "git checkout" that checks things out of the > index. ... I like that and started using the patches. Do you have any specific area I should pay a special attention to? ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-08-30 0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano 2008-08-30 0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano 2008-09-01 9:39 ` [PATCH 00/12] Towards a better merge resolution support Alex Riesen @ 2008-09-01 9:44 ` Alex Riesen 2008-09-01 9:50 ` Abhijit Menon-Sen 2008-09-01 10:38 ` Junio C Hamano 2 siblings, 2 replies; 26+ messages in thread From: Alex Riesen @ 2008-09-01 9:44 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Junio C Hamano, Sat, Aug 30, 2008 02:42:31 +0200: > This consists of two loosely related topics on improving conflicted merge > resolution support. > > The early part of the series is what you already saw. In addition to > recording a conflicted merge in the RCS merge style we have traditionally > used, this allows you to optionally use "diff3 -m" style. The difference > is that the latter format shows the part from the common ancestor that > corresponds to the parts both sides modified to cause the conflict, in > addition to the changes done on each side. This can be chosen by setting > a configuration variable. Rerere mechanism is updated to understand this > new format as well, and conflicts from either formats interoperate well, > because rerere mechanism only records and uses the changes made on each > side, not what was in the common ancestor. This reminds me: when resolving a conflict in a git repo (when trying something from next or pu), I often notice that I'd like to resolve it the same way it was done on next or pu. IOW, copy the commit resolution from some other merge commit. Maybe can be a way to use rerere mechanism with that? ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-09-01 9:44 ` Alex Riesen @ 2008-09-01 9:50 ` Abhijit Menon-Sen 2008-09-01 12:20 ` Thomas Rast 2008-09-01 10:38 ` Junio C Hamano 1 sibling, 1 reply; 26+ messages in thread From: Abhijit Menon-Sen @ 2008-09-01 9:50 UTC (permalink / raw) To: Alex Riesen; +Cc: Junio C Hamano, git At 2008-09-01 11:44:12 +0200, raa.lkml@gmail.com wrote: > > IOW, copy the commit resolution from some other merge commit. Maybe > can be a way to use rerere mechanism with that? That's what I'm trying to implement on Dscho's suggestion. I'm still just trying to understand the code, so any suggestions about how to do this are very welcome. -- ams ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-09-01 9:50 ` Abhijit Menon-Sen @ 2008-09-01 12:20 ` Thomas Rast 0 siblings, 0 replies; 26+ messages in thread From: Thomas Rast @ 2008-09-01 12:20 UTC (permalink / raw) To: Abhijit Menon-Sen; +Cc: Alex Riesen, Junio C Hamano, git [-- Attachment #1: Type: text/plain, Size: 1560 bytes --] Abhijit Menon-Sen wrote: > At 2008-09-01 11:44:12 +0200, raa.lkml@gmail.com wrote: > > > > IOW, copy the commit resolution from some other merge commit. Maybe > > can be a way to use rerere mechanism with that? > > That's what I'm trying to implement on Dscho's suggestion. I'm still > just trying to understand the code, so any suggestions about how to > do this are very welcome. Random idea: you could use a script that just replays the merges present in history and lets rerere record them. Like so: -- 8< -- #!/bin/sh . "$(git --exec-path)/git-sh-setup" require_work_tree git update-index --refresh || die "can't run with dirty index" git rev-list --parents "$@" | grep '.* .* .*' | while read merge firstparent otherparents do git checkout $firstparent >/dev/null 2>/dev/null git merge $otherparents >/dev/null if test -z "$(git ls-files -u)"; then echo -n 'no conflicts: ' git --no-pager log -1 --pretty=oneline --abbrev-commit $merge continue fi git rerere git ls-files -t | grep ^M | cut -c 3- | xargs git checkout $merge -- git rerere git reset --hard >/dev/null 2>/dev/null echo -n 'recorded: ' git --no-pager log -1 --pretty=oneline --abbrev-commit $merge done -- >8 -- The intended usage is like ./rerereimport.sh v1.6.0..origin/next to import all resolutions in the specified range. Granted, it's probably not as good and definitely not as fast as an automatic feature integrated with the merge machinery. - Thomas -- Thomas Rast trast@student.ethz.ch [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-09-01 9:44 ` Alex Riesen 2008-09-01 9:50 ` Abhijit Menon-Sen @ 2008-09-01 10:38 ` Junio C Hamano 2008-09-01 11:34 ` Alex Riesen 1 sibling, 1 reply; 26+ messages in thread From: Junio C Hamano @ 2008-09-01 10:38 UTC (permalink / raw) To: Alex Riesen; +Cc: git Alex Riesen <raa.lkml@gmail.com> writes: > ... IOW, copy the commit > resolution from some other merge commit. Maybe can be a way to use > rerere mechanism with that? If you know which merge I did you want to steal from, you can prime your rerere database by pretending to be me, doing the merge. Something like: $ git checkout $merge^1 ;# detach to the parent of merge $ git merge $merge^2 ;# pretend you were me to redo it $ git diff -R $merge | git apply --index ;# and get what I did $ git rerere ;# have rerere record the resolution ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-09-01 10:38 ` Junio C Hamano @ 2008-09-01 11:34 ` Alex Riesen 2008-09-01 17:26 ` Junio C Hamano 0 siblings, 1 reply; 26+ messages in thread From: Alex Riesen @ 2008-09-01 11:34 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Junio C Hamano, Mon, Sep 01, 2008 12:38:25 +0200: > Alex Riesen <raa.lkml@gmail.com> writes: > > > ... IOW, copy the commit > > resolution from some other merge commit. Maybe can be a way to use > > rerere mechanism with that? > > If you know which merge I did you want to steal from, you can prime your > rerere database by pretending to be me, doing the merge. Something like: > > $ git checkout $merge^1 ;# detach to the parent of merge > $ git merge $merge^2 ;# pretend you were me to redo it > $ git diff -R $merge | git apply --index ;# and get what I did I ended up using $ git checkout Merge^1 $ git merge Merge^2 $ git diff -R Merge | git apply $ git diff -R Merge --name-only -z | git update-index -z --stdin $ git rerere Just git apply --index complained about the files missing from the index: $ git tag Merge c5e2ace70271b481632aaf987361027ca4592df6 $ gco Merge^1 Previous HEAD position was c5e2ace... Merge branch 'jc/better-conflict-resolution' into next HEAD is now at 2392877... Merge branch 'master' into next $ git merge Merge^2 Auto-merging Documentation/config.txt Auto-merging Documentation/git-checkout.txt CONFLICT (content): Merge conflict in Documentation/git-checkout.txt Auto-merging builtin-checkout.c CONFLICT (content): Merge conflict in builtin-checkout.c Auto-merging builtin-merge-recursive.c Auto-merging t/t6023-merge-file.sh Auto-merging t/t7201-co.sh CONFLICT (content): Merge conflict in t/t7201-co.sh Auto-merging xdiff-interface.c Auto-merging xdiff-interface.h Recorded preimage for 'Documentation/git-checkout.txt' Recorded preimage for 'builtin-checkout.c' Recorded preimage for 't/t7201-co.sh' Automatic merge failed; fix conflicts and then commit the result. $ git diff -R Merge |git apply --index error: Documentation/git-checkout.txt: does not exist in index error: builtin-checkout.c: does not exist in index error: t/t7201-co.sh: does not exist in index > $ git rerere ;# have rerere record the resolution Well, it works, but it's a bit of work and hard to automate (needs a working tree). An option to merge: $ git merge <branch> conflict ... investigate ... find a resolution in <resolution> $ git reset --hard $ git merge --rerere <resolution> branch check... Ok. $ git commit or rerere: $ git merge <branch> conflict ... investigate ... find a resolution in <resolution> $ git rerere <resolution> $ git reset --hard $ git merge <branch> check... Ok. $ git commit These could be more convenient. ^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 00/12] Towards a better merge resolution support 2008-09-01 11:34 ` Alex Riesen @ 2008-09-01 17:26 ` Junio C Hamano 0 siblings, 0 replies; 26+ messages in thread From: Junio C Hamano @ 2008-09-01 17:26 UTC (permalink / raw) To: Alex Riesen; +Cc: git Alex Riesen <raa.lkml@gmail.com> writes: > Junio C Hamano, Mon, Sep 01, 2008 12:38:25 +0200: >> Alex Riesen <raa.lkml@gmail.com> writes: >> >> > ... IOW, copy the commit >> > resolution from some other merge commit. Maybe can be a way to use >> > rerere mechanism with that? >> >> If you know which merge I did you want to steal from, you can prime your >> rerere database by pretending to be me, doing the merge. Something like: >> >> $ git checkout $merge^1 ;# detach to the parent of merge >> $ git merge $merge^2 ;# pretend you were me to redo it >> $ git diff -R $merge | git apply --index ;# and get what I did > > I ended up using I think the last step should be "git read-tree --reset -u $merge" if we really want the minimum sequence. ^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2008-09-01 17:27 UTC | newest] Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-08-30 0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano 2008-08-30 0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano 2008-08-30 0:42 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano 2008-08-30 0:42 ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano 2008-08-30 0:42 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano 2008-08-30 0:42 ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano 2008-08-30 0:42 ` [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original Junio C Hamano 2008-08-30 0:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano 2008-08-30 0:42 ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano 2008-08-30 0:42 ` [PATCH 09/12] checkout: do not check out unmerged higher stages randomly Junio C Hamano 2008-08-30 0:42 ` [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index Junio C Hamano 2008-08-30 0:42 ` [PATCH 11/12] checkout --ours/--theirs Junio C Hamano 2008-08-30 0:42 ` [PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index Junio C Hamano 2008-08-30 9:42 ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Johannes Schindelin 2008-08-30 9:34 ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Johannes Schindelin 2008-08-30 9:31 ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin 2008-08-30 15:42 ` Junio C Hamano 2008-08-30 9:29 ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Johannes Schindelin 2008-08-30 9:14 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Johannes Schindelin 2008-09-01 9:39 ` [PATCH 00/12] Towards a better merge resolution support Alex Riesen 2008-09-01 9:44 ` Alex Riesen 2008-09-01 9:50 ` Abhijit Menon-Sen 2008-09-01 12:20 ` Thomas Rast 2008-09-01 10:38 ` Junio C Hamano 2008-09-01 11:34 ` Alex Riesen 2008-09-01 17:26 ` Junio C Hamano
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).