Merge branch 'mz/rerere-remaining'

* mz/rerere-remaining:
  mergetool: don't skip modify/remove conflicts
  rerere "remaining"
This commit is contained in:
Junio C Hamano 2011-02-27 21:58:30 -08:00
commit 66a6a31420
5 changed files with 123 additions and 19 deletions

View file

@ -8,7 +8,7 @@
#include "xdiff-interface.h"
static const char * const rerere_usage[] = {
"git rerere [clear | status | diff | gc]",
"git rerere [clear | status | remaining | diff | gc]",
NULL,
};
@ -156,7 +156,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
else if (!strcmp(argv[0], "status"))
for (i = 0; i < merge_rr.nr; i++)
printf("%s\n", merge_rr.items[i].string);
else if (!strcmp(argv[0], "diff"))
else if (!strcmp(argv[0], "remaining")) {
rerere_remaining(&merge_rr);
for (i = 0; i < merge_rr.nr; i++) {
if (merge_rr.items[i].util != RERERE_RESOLVED)
printf("%s\n", merge_rr.items[i].string);
else
/* prepare for later call to
* string_list_clear() */
merge_rr.items[i].util = NULL;
}
} else if (!strcmp(argv[0], "diff"))
for (i = 0; i < merge_rr.nr; i++) {
const char *path = merge_rr.items[i].string;
const char *name = (const char *)merge_rr.items[i].util;

View file

@ -269,7 +269,7 @@ rerere=false
files_to_merge() {
if test "$rerere" = true
then
git rerere status
git rerere remaining
else
git ls-files -u | sed -e 's/^[^ ]* //' | sort -u
fi

View file

@ -7,6 +7,11 @@
#include "ll-merge.h"
#include "attr.h"
#define RESOLVED 0
#define PUNTED 1
#define THREE_STAGED 2
void *RERERE_RESOLVED = &RERERE_RESOLVED;
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
static int rerere_enabled = -1;
@ -345,21 +350,74 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
return hunk_no;
}
static int check_one_conflict(int i, int *type)
{
struct cache_entry *e = active_cache[i];
if (!ce_stage(e)) {
*type = RESOLVED;
return i + 1;
}
*type = PUNTED;
if (ce_stage(e) == 1) {
if (active_nr <= ++i)
return i + 1;
}
/* Only handle regular files with both stages #2 and #3 */
if (i + 1 < active_nr) {
struct cache_entry *e2 = active_cache[i];
struct cache_entry *e3 = active_cache[i + 1];
if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
ce_same_name(e, e3) &&
S_ISREG(e2->ce_mode) &&
S_ISREG(e3->ce_mode))
*type = THREE_STAGED;
}
/* Skip the entries with the same name */
while (i < active_nr && ce_same_name(e, active_cache[i]))
i++;
return i;
}
static int find_conflict(struct string_list *conflict)
{
int i;
if (read_cache() < 0)
return error("Could not read index");
for (i = 0; i+1 < active_nr; i++) {
struct cache_entry *e2 = active_cache[i];
struct cache_entry *e3 = active_cache[i+1];
if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
ce_same_name(e2, e3) &&
S_ISREG(e2->ce_mode) &&
S_ISREG(e3->ce_mode)) {
string_list_insert(conflict, (const char *)e2->name);
i++; /* skip over both #2 and #3 */
for (i = 0; i < active_nr;) {
int conflict_type;
struct cache_entry *e = active_cache[i];
i = check_one_conflict(i, &conflict_type);
if (conflict_type == THREE_STAGED)
string_list_insert(conflict, (const char *)e->name);
}
return 0;
}
int rerere_remaining(struct string_list *merge_rr)
{
int i;
if (read_cache() < 0)
return error("Could not read index");
for (i = 0; i < active_nr;) {
int conflict_type;
struct cache_entry *e = active_cache[i];
i = check_one_conflict(i, &conflict_type);
if (conflict_type == PUNTED)
string_list_insert(merge_rr, (const char *)e->name);
else if (conflict_type == RESOLVED) {
struct string_list_item *it;
it = string_list_lookup(merge_rr, (const char *)e->name);
if (it != NULL) {
free(it->util);
it->util = RERERE_RESOLVED;
}
}
}
return 0;

View file

@ -6,11 +6,19 @@
#define RERERE_AUTOUPDATE 01
#define RERERE_NOAUTOUPDATE 02
/*
* Marks paths that have been hand-resolved and added to the
* index. Set in the util field of such paths after calling
* rerere_remaining.
*/
extern void *RERERE_RESOLVED;
extern int setup_rerere(struct string_list *, int);
extern int rerere(int);
extern const char *rerere_path(const char *hex, const char *file);
extern int has_rerere_resolution(const char *hex);
extern int rerere_forget(const char **);
extern int rerere_remaining(struct string_list *);
#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
"update the index with reused conflict resolution if possible")

View file

@ -16,23 +16,33 @@ Testing basic merge tool invocation'
test_expect_success 'setup' '
git config rerere.enabled true &&
echo master >file1 &&
echo master file11 >file11 &&
echo master file12 >file12 &&
echo master file13 >file13 &&
echo master file14 >file14 &&
mkdir subdir &&
echo master sub >subdir/file3 &&
git add file1 subdir/file3 &&
git commit -m "added file1" &&
git add file1 file1[1-4] subdir/file3 &&
git commit -m "add initial versions" &&
git checkout -b branch1 master &&
echo branch1 change >file1 &&
echo branch1 newfile >file2 &&
echo branch1 change file11 >file11 &&
echo branch1 change file13 >file13 &&
echo branch1 sub >subdir/file3 &&
git add file1 file2 subdir/file3 &&
git add file1 file11 file13 file2 subdir/file3 &&
git rm file12 &&
git commit -m "branch1 changes" &&
git checkout master &&
echo master updated >file1 &&
echo master new >file2 &&
echo master updated file12 >file12 &&
echo master updated file14 >file14 &&
echo master new sub >subdir/file3 &&
git add file1 file2 subdir/file3 &&
git add file1 file12 file14 file2 subdir/file3 &&
git rm file11 &&
git commit -m "master updates" &&
git config merge.tool mytool &&
@ -46,6 +56,8 @@ test_expect_success 'custom mergetool' '
( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
test "$(cat file1)" = "master updated" &&
test "$(cat file2)" = "master new" &&
test "$(cat subdir/file3)" = "master new sub" &&
@ -59,6 +71,8 @@ test_expect_success 'mergetool crlf' '
( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
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' '
cd subdir &&
( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
test "$(cat ../file1)" = "master updated" &&
test "$(cat ../file2)" = "master new" &&
git commit -m "branch1 resolved with mergetool - subdir"
@ -92,6 +108,8 @@ test_expect_success 'mergetool skips autoresolved' '
git checkout -b test4 branch1 &&
test_must_fail git merge master &&
test -n "$(git ls-files -u)" &&
( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
output="$(git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git reset --hard
@ -102,13 +120,23 @@ test_expect_success 'mergetool merges all from subdir' '
cd subdir &&
git config rerere.enabled false &&
test_must_fail git merge master &&
git mergetool --no-prompt &&
( yes "d" "d" | git mergetool --no-prompt ) &&
test "$(cat ../file1)" = "master updated" &&
test "$(cat ../file2)" = "master new" &&
test "$(cat file3)" = "master new sub" &&
git add ../file1 ../file2 file3 &&
git commit -m "branch2 resolved by mergetool from subdir"
)
'
test_expect_success 'mergetool skips resolved paths when rerere is active' '
git config rerere.enabled true &&
rm -rf .git/rr-cache &&
git checkout -b test5 branch1
test_must_fail git merge master >/dev/null 2>&1 &&
( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
output="$(yes "n" | git mergetool --no-prompt)" &&
test "$output" = "No files need merging" &&
git reset --hard
'
test_done