rerere forget path: forget recorded resolution
After you find out an earlier resolution you told rerere to use was a mismerge, there is no easy way to clear it. A new subcommand "forget" can be used to tell git to forget a recorded resolution, so that you can redo the merge from scratch. Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
27d6b08536
commit
dea4562bf5
@ -110,6 +110,8 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
if (!strcmp(argv[1], "-h"))
|
if (!strcmp(argv[1], "-h"))
|
||||||
usage(git_rerere_usage);
|
usage(git_rerere_usage);
|
||||||
|
else if (!strcmp(argv[1], "forget"))
|
||||||
|
return rerere_forget(argv + 2);
|
||||||
|
|
||||||
fd = setup_rerere(&merge_rr);
|
fd = setup_rerere(&merge_rr);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
|
133
rerere.c
133
rerere.c
@ -3,6 +3,9 @@
|
|||||||
#include "rerere.h"
|
#include "rerere.h"
|
||||||
#include "xdiff/xdiff.h"
|
#include "xdiff/xdiff.h"
|
||||||
#include "xdiff-interface.h"
|
#include "xdiff-interface.h"
|
||||||
|
#include "dir.h"
|
||||||
|
#include "resolve-undo.h"
|
||||||
|
#include "ll-merge.h"
|
||||||
|
|
||||||
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
|
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
|
||||||
static int rerere_enabled = -1;
|
static int rerere_enabled = -1;
|
||||||
@ -223,6 +226,87 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
|
|||||||
return hunk_no;
|
return hunk_no;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct rerere_io_mem {
|
||||||
|
struct rerere_io io;
|
||||||
|
struct strbuf input;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
|
||||||
|
{
|
||||||
|
struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
|
||||||
|
char *ep;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
strbuf_release(sb);
|
||||||
|
if (!io->input.len)
|
||||||
|
return -1;
|
||||||
|
ep = strchrnul(io->input.buf, '\n');
|
||||||
|
if (*ep == '\n')
|
||||||
|
ep++;
|
||||||
|
len = ep - io->input.buf;
|
||||||
|
strbuf_add(sb, io->input.buf, len);
|
||||||
|
strbuf_remove(&io->input, 0, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_cache(const char *path, unsigned char *sha1, const char *output)
|
||||||
|
{
|
||||||
|
mmfile_t mmfile[3];
|
||||||
|
mmbuffer_t result = {NULL, 0};
|
||||||
|
struct cache_entry *ce;
|
||||||
|
int pos, len, i, hunk_no;
|
||||||
|
struct rerere_io_mem io;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reproduce the conflicted merge in-core
|
||||||
|
*/
|
||||||
|
len = strlen(path);
|
||||||
|
pos = cache_name_pos(path, len);
|
||||||
|
if (0 <= pos)
|
||||||
|
return -1;
|
||||||
|
pos = -pos - 1;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
enum object_type type;
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
|
mmfile[i].size = 0;
|
||||||
|
mmfile[i].ptr = NULL;
|
||||||
|
if (active_nr <= pos)
|
||||||
|
break;
|
||||||
|
ce = active_cache[pos++];
|
||||||
|
if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
|
||||||
|
|| ce_stage(ce) != i + 1)
|
||||||
|
break;
|
||||||
|
mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
|
||||||
|
mmfile[i].size = size;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (!mmfile[i].ptr && !mmfile[i].size)
|
||||||
|
mmfile[i].ptr = xstrdup("");
|
||||||
|
}
|
||||||
|
ll_merge(&result, path, &mmfile[0],
|
||||||
|
&mmfile[1], "ours",
|
||||||
|
&mmfile[2], "theirs", 0);
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
free(mmfile[i].ptr);
|
||||||
|
|
||||||
|
memset(&io, 0, sizeof(&io));
|
||||||
|
io.io.getline = rerere_mem_getline;
|
||||||
|
if (output)
|
||||||
|
io.io.output = fopen(output, "w");
|
||||||
|
else
|
||||||
|
io.io.output = NULL;
|
||||||
|
strbuf_init(&io.input, 0);
|
||||||
|
strbuf_attach(&io.input, result.ptr, result.size, result.size);
|
||||||
|
|
||||||
|
hunk_no = handle_path(sha1, (struct rerere_io *)&io);
|
||||||
|
strbuf_release(&io.input);
|
||||||
|
if (io.io.output)
|
||||||
|
fclose(io.io.output);
|
||||||
|
return hunk_no;
|
||||||
|
}
|
||||||
|
|
||||||
static int find_conflict(struct string_list *conflict)
|
static int find_conflict(struct string_list *conflict)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -434,3 +518,52 @@ int rerere(void)
|
|||||||
return 0;
|
return 0;
|
||||||
return do_plain_rerere(&merge_rr, fd);
|
return do_plain_rerere(&merge_rr, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rerere_forget_one_path(const char *path, struct string_list *rr)
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
char *hex;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = handle_cache(path, sha1, NULL);
|
||||||
|
if (ret < 1)
|
||||||
|
return error("Could not parse conflict hunks in '%s'", path);
|
||||||
|
hex = xstrdup(sha1_to_hex(sha1));
|
||||||
|
filename = rerere_path(hex, "postimage");
|
||||||
|
if (unlink(filename))
|
||||||
|
return (errno == ENOENT
|
||||||
|
? error("no remembered resolution for %s", path)
|
||||||
|
: error("cannot unlink %s: %s", filename, strerror(errno)));
|
||||||
|
|
||||||
|
handle_cache(path, sha1, rerere_path(hex, "preimage"));
|
||||||
|
fprintf(stderr, "Updated preimage for '%s'\n", path);
|
||||||
|
|
||||||
|
|
||||||
|
string_list_insert(path, rr)->util = hex;
|
||||||
|
fprintf(stderr, "Forgot resolution for %s\n", path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rerere_forget(const char **pathspec)
|
||||||
|
{
|
||||||
|
int i, fd;
|
||||||
|
struct string_list conflict = { NULL, 0, 0, 1 };
|
||||||
|
struct string_list merge_rr = { NULL, 0, 0, 1 };
|
||||||
|
|
||||||
|
if (read_cache() < 0)
|
||||||
|
return error("Could not read index");
|
||||||
|
|
||||||
|
fd = setup_rerere(&merge_rr);
|
||||||
|
|
||||||
|
unmerge_cache(pathspec);
|
||||||
|
find_conflict(&conflict);
|
||||||
|
for (i = 0; i < conflict.nr; i++) {
|
||||||
|
struct string_list_item *it = &conflict.items[i];
|
||||||
|
if (!match_pathspec(pathspec, it->string, strlen(it->string),
|
||||||
|
0, NULL))
|
||||||
|
continue;
|
||||||
|
rerere_forget_one_path(it->string, &merge_rr);
|
||||||
|
}
|
||||||
|
return write_rr(&merge_rr, fd);
|
||||||
|
}
|
||||||
|
1
rerere.h
1
rerere.h
@ -7,5 +7,6 @@ extern int setup_rerere(struct string_list *);
|
|||||||
extern int rerere(void);
|
extern int rerere(void);
|
||||||
extern const char *rerere_path(const char *hex, const char *file);
|
extern const char *rerere_path(const char *hex, const char *file);
|
||||||
extern int has_rerere_resolution(const char *hex);
|
extern int has_rerere_resolution(const char *hex);
|
||||||
|
extern int rerere_forget(const char **);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -115,4 +115,29 @@ test_expect_success 'unmerge with plumbing' '
|
|||||||
test $(wc -l <actual) = 3
|
test $(wc -l <actual) = 3
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'rerere and rerere --forget' '
|
||||||
|
mkdir .git/rr-cache &&
|
||||||
|
prime_resolve_undo &&
|
||||||
|
echo record the resolution &&
|
||||||
|
git rerere &&
|
||||||
|
rerere_id=$(cd .git/rr-cache && echo */postimage) &&
|
||||||
|
rerere_id=${rerere_id%/postimage} &&
|
||||||
|
test -f .git/rr-cache/$rerere_id/postimage &&
|
||||||
|
git checkout -m file &&
|
||||||
|
echo resurrect the conflict &&
|
||||||
|
grep "^=======" file &&
|
||||||
|
echo reresolve the conflict &&
|
||||||
|
git rerere &&
|
||||||
|
test "z$(cat file)" = zdifferent &&
|
||||||
|
echo register the resolution again &&
|
||||||
|
git add file &&
|
||||||
|
check_resolve_undo kept file initial:file second:file third:file &&
|
||||||
|
test -z "$(git ls-files -u)" &&
|
||||||
|
git rerere forget file &&
|
||||||
|
! test -f .git/rr-cache/$rerere_id/postimage &&
|
||||||
|
tr "\0" "\n" <.git/MERGE_RR >actual &&
|
||||||
|
echo "$rerere_id file" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user