Skip to content

Commit 66a6a31

Browse files
committed
Merge branch 'mz/rerere-remaining'
* mz/rerere-remaining: mergetool: don't skip modify/remove conflicts rerere "remaining"
2 parents 7d5c884 + 2f59c94 commit 66a6a31

File tree

5 files changed

+123
-19
lines changed

5 files changed

+123
-19
lines changed

builtin/rerere.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "xdiff-interface.h"
99

1010
static const char * const rerere_usage[] = {
11-
"git rerere [clear | status | diff | gc]",
11+
"git rerere [clear | status | remaining | diff | gc]",
1212
NULL,
1313
};
1414

@@ -156,7 +156,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
156156
else if (!strcmp(argv[0], "status"))
157157
for (i = 0; i < merge_rr.nr; i++)
158158
printf("%s\n", merge_rr.items[i].string);
159-
else if (!strcmp(argv[0], "diff"))
159+
else if (!strcmp(argv[0], "remaining")) {
160+
rerere_remaining(&merge_rr);
161+
for (i = 0; i < merge_rr.nr; i++) {
162+
if (merge_rr.items[i].util != RERERE_RESOLVED)
163+
printf("%s\n", merge_rr.items[i].string);
164+
else
165+
/* prepare for later call to
166+
* string_list_clear() */
167+
merge_rr.items[i].util = NULL;
168+
}
169+
} else if (!strcmp(argv[0], "diff"))
160170
for (i = 0; i < merge_rr.nr; i++) {
161171
const char *path = merge_rr.items[i].string;
162172
const char *name = (const char *)merge_rr.items[i].util;

git-mergetool.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ rerere=false
269269
files_to_merge() {
270270
if test "$rerere" = true
271271
then
272-
git rerere status
272+
git rerere remaining
273273
else
274274
git ls-files -u | sed -e 's/^[^ ]* //' | sort -u
275275
fi

rerere.c

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
#include "ll-merge.h"
88
#include "attr.h"
99

10+
#define RESOLVED 0
11+
#define PUNTED 1
12+
#define THREE_STAGED 2
13+
void *RERERE_RESOLVED = &RERERE_RESOLVED;
14+
1015
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
1116
static int rerere_enabled = -1;
1217

@@ -345,21 +350,74 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
345350
return hunk_no;
346351
}
347352

348-
static int find_conflict(struct string_list *conflict)
353+
static int check_one_conflict(int i, int *type)
349354
{
350-
int i;
351-
if (read_cache() < 0)
352-
return error("Could not read index");
353-
for (i = 0; i+1 < active_nr; i++) {
355+
struct cache_entry *e = active_cache[i];
356+
357+
if (!ce_stage(e)) {
358+
*type = RESOLVED;
359+
return i + 1;
360+
}
361+
362+
*type = PUNTED;
363+
if (ce_stage(e) == 1) {
364+
if (active_nr <= ++i)
365+
return i + 1;
366+
}
367+
368+
/* Only handle regular files with both stages #2 and #3 */
369+
if (i + 1 < active_nr) {
354370
struct cache_entry *e2 = active_cache[i];
355-
struct cache_entry *e3 = active_cache[i+1];
371+
struct cache_entry *e3 = active_cache[i + 1];
356372
if (ce_stage(e2) == 2 &&
357373
ce_stage(e3) == 3 &&
358-
ce_same_name(e2, e3) &&
374+
ce_same_name(e, e3) &&
359375
S_ISREG(e2->ce_mode) &&
360-
S_ISREG(e3->ce_mode)) {
361-
string_list_insert(conflict, (const char *)e2->name);
362-
i++; /* skip over both #2 and #3 */
376+
S_ISREG(e3->ce_mode))
377+
*type = THREE_STAGED;
378+
}
379+
380+
/* Skip the entries with the same name */
381+
while (i < active_nr && ce_same_name(e, active_cache[i]))
382+
i++;
383+
return i;
384+
}
385+
386+
static int find_conflict(struct string_list *conflict)
387+
{
388+
int i;
389+
if (read_cache() < 0)
390+
return error("Could not read index");
391+
392+
for (i = 0; i < active_nr;) {
393+
int conflict_type;
394+
struct cache_entry *e = active_cache[i];
395+
i = check_one_conflict(i, &conflict_type);
396+
if (conflict_type == THREE_STAGED)
397+
string_list_insert(conflict, (const char *)e->name);
398+
}
399+
return 0;
400+
}
401+
402+
int rerere_remaining(struct string_list *merge_rr)
403+
{
404+
int i;
405+
if (read_cache() < 0)
406+
return error("Could not read index");
407+
408+
for (i = 0; i < active_nr;) {
409+
int conflict_type;
410+
struct cache_entry *e = active_cache[i];
411+
i = check_one_conflict(i, &conflict_type);
412+
if (conflict_type == PUNTED)
413+
string_list_insert(merge_rr, (const char *)e->name);
414+
else if (conflict_type == RESOLVED) {
415+
struct string_list_item *it;
416+
it = string_list_lookup(merge_rr, (const char *)e->name);
417+
if (it != NULL) {
418+
free(it->util);
419+
it->util = RERERE_RESOLVED;
420+
}
363421
}
364422
}
365423
return 0;

rerere.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@
66
#define RERERE_AUTOUPDATE 01
77
#define RERERE_NOAUTOUPDATE 02
88

9+
/*
10+
* Marks paths that have been hand-resolved and added to the
11+
* index. Set in the util field of such paths after calling
12+
* rerere_remaining.
13+
*/
14+
extern void *RERERE_RESOLVED;
15+
916
extern int setup_rerere(struct string_list *, int);
1017
extern int rerere(int);
1118
extern const char *rerere_path(const char *hex, const char *file);
1219
extern int has_rerere_resolution(const char *hex);
1320
extern int rerere_forget(const char **);
21+
extern int rerere_remaining(struct string_list *);
1422

1523
#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
1624
"update the index with reused conflict resolution if possible")

t/t7610-mergetool.sh

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,33 @@ Testing basic merge tool invocation'
1616
test_expect_success 'setup' '
1717
git config rerere.enabled true &&
1818
echo master >file1 &&
19+
echo master file11 >file11 &&
20+
echo master file12 >file12 &&
21+
echo master file13 >file13 &&
22+
echo master file14 >file14 &&
1923
mkdir subdir &&
2024
echo master sub >subdir/file3 &&
21-
git add file1 subdir/file3 &&
22-
git commit -m "added file1" &&
25+
git add file1 file1[1-4] subdir/file3 &&
26+
git commit -m "add initial versions" &&
2327
2428
git checkout -b branch1 master &&
2529
echo branch1 change >file1 &&
2630
echo branch1 newfile >file2 &&
31+
echo branch1 change file11 >file11 &&
32+
echo branch1 change file13 >file13 &&
2733
echo branch1 sub >subdir/file3 &&
28-
git add file1 file2 subdir/file3 &&
34+
git add file1 file11 file13 file2 subdir/file3 &&
35+
git rm file12 &&
2936
git commit -m "branch1 changes" &&
3037
3138
git checkout master &&
3239
echo master updated >file1 &&
3340
echo master new >file2 &&
41+
echo master updated file12 >file12 &&
42+
echo master updated file14 >file14 &&
3443
echo master new sub >subdir/file3 &&
35-
git add file1 file2 subdir/file3 &&
44+
git add file1 file12 file14 file2 subdir/file3 &&
45+
git rm file11 &&
3646
git commit -m "master updates" &&
3747
3848
git config merge.tool mytool &&
@@ -46,6 +56,8 @@ test_expect_success 'custom mergetool' '
4656
( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
4757
( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
4858
( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
59+
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
60+
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
4961
test "$(cat file1)" = "master updated" &&
5062
test "$(cat file2)" = "master new" &&
5163
test "$(cat subdir/file3)" = "master new sub" &&
@@ -59,6 +71,8 @@ test_expect_success 'mergetool crlf' '
5971
( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
6072
( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
6173
( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
74+
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
75+
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
6276
test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
6377
test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
6478
test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
@@ -82,6 +96,8 @@ test_expect_success 'mergetool on file in parent dir' '
8296
cd subdir &&
8397
( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
8498
( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
99+
( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
100+
( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
85101
test "$(cat ../file1)" = "master updated" &&
86102
test "$(cat ../file2)" = "master new" &&
87103
git commit -m "branch1 resolved with mergetool - subdir"
@@ -92,6 +108,8 @@ test_expect_success 'mergetool skips autoresolved' '
92108
git checkout -b test4 branch1 &&
93109
test_must_fail git merge master &&
94110
test -n "$(git ls-files -u)" &&
111+
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
112+
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
95113
output="$(git mergetool --no-prompt)" &&
96114
test "$output" = "No files need merging" &&
97115
git reset --hard
@@ -102,13 +120,23 @@ test_expect_success 'mergetool merges all from subdir' '
102120
cd subdir &&
103121
git config rerere.enabled false &&
104122
test_must_fail git merge master &&
105-
git mergetool --no-prompt &&
123+
( yes "d" "d" | git mergetool --no-prompt ) &&
106124
test "$(cat ../file1)" = "master updated" &&
107125
test "$(cat ../file2)" = "master new" &&
108126
test "$(cat file3)" = "master new sub" &&
109-
git add ../file1 ../file2 file3 &&
110127
git commit -m "branch2 resolved by mergetool from subdir"
111128
)
112129
'
113130

131+
test_expect_success 'mergetool skips resolved paths when rerere is active' '
132+
git config rerere.enabled true &&
133+
rm -rf .git/rr-cache &&
134+
git checkout -b test5 branch1
135+
test_must_fail git merge master >/dev/null 2>&1 &&
136+
( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
137+
output="$(yes "n" | git mergetool --no-prompt)" &&
138+
test "$output" = "No files need merging" &&
139+
git reset --hard
140+
'
141+
114142
test_done

0 commit comments

Comments
 (0)