2008-07-09 12:58:57 +00:00
|
|
|
#include "cache.h"
|
2008-07-21 18:03:49 +00:00
|
|
|
#include "string-list.h"
|
2008-07-09 12:58:57 +00:00
|
|
|
#include "rerere.h"
|
|
|
|
#include "xdiff-interface.h"
|
2009-12-25 23:51:32 +00:00
|
|
|
#include "dir.h"
|
|
|
|
#include "resolve-undo.h"
|
|
|
|
#include "ll-merge.h"
|
2010-01-17 07:28:46 +00:00
|
|
|
#include "attr.h"
|
2008-07-09 12:58:57 +00:00
|
|
|
|
|
|
|
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
|
|
|
|
static int rerere_enabled = -1;
|
|
|
|
|
|
|
|
/* automatically update cleanly resolved paths to the index */
|
|
|
|
static int rerere_autoupdate;
|
|
|
|
|
|
|
|
static char *merge_rr_path;
|
|
|
|
|
2009-02-14 22:21:04 +00:00
|
|
|
const char *rerere_path(const char *hex, const char *file)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
2009-02-14 22:21:04 +00:00
|
|
|
return git_path("rr-cache/%s/%s", hex, file);
|
2008-07-09 12:58:57 +00:00
|
|
|
}
|
|
|
|
|
2009-02-14 22:21:04 +00:00
|
|
|
int has_rerere_resolution(const char *hex)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
struct stat st;
|
2009-02-14 22:21:04 +00:00
|
|
|
return !stat(rerere_path(hex, "postimage"), &st);
|
2008-07-09 12:58:57 +00:00
|
|
|
}
|
|
|
|
|
2008-07-21 18:03:49 +00:00
|
|
|
static void read_rr(struct string_list *rr)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
FILE *in = fopen(merge_rr_path, "r");
|
|
|
|
if (!in)
|
|
|
|
return;
|
|
|
|
while (fread(buf, 40, 1, in) == 1) {
|
|
|
|
int i;
|
|
|
|
char *name;
|
|
|
|
if (get_sha1_hex(buf, sha1))
|
|
|
|
die("corrupt MERGE_RR");
|
|
|
|
buf[40] = '\0';
|
|
|
|
name = xstrdup(buf);
|
|
|
|
if (fgetc(in) != '\t')
|
|
|
|
die("corrupt MERGE_RR");
|
|
|
|
for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
|
|
|
|
; /* do nothing */
|
|
|
|
if (i == sizeof(buf))
|
|
|
|
die("filename too long");
|
2008-07-21 18:03:49 +00:00
|
|
|
string_list_insert(buf, rr)->util = name;
|
2008-07-09 12:58:57 +00:00
|
|
|
}
|
|
|
|
fclose(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct lock_file write_lock;
|
|
|
|
|
2008-07-21 18:03:49 +00:00
|
|
|
static int write_rr(struct string_list *rr, int out_fd)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < rr->nr; i++) {
|
|
|
|
const char *path;
|
|
|
|
int length;
|
|
|
|
if (!rr->items[i].util)
|
|
|
|
continue;
|
2008-07-21 18:03:49 +00:00
|
|
|
path = rr->items[i].string;
|
2008-07-09 12:58:57 +00:00
|
|
|
length = strlen(path) + 1;
|
|
|
|
if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
|
use write_str_in_full helper to avoid literal string lengths
In 2d14d65 (Use a clearer style to issue commands to remote helpers,
2009-09-03) I happened to notice two changes like this:
- write_in_full(helper->in, "list\n", 5);
+
+ strbuf_addstr(&buf, "list\n");
+ write_in_full(helper->in, buf.buf, buf.len);
+ strbuf_reset(&buf);
IMHO, it would be better to define a new function,
static inline ssize_t write_str_in_full(int fd, const char *str)
{
return write_in_full(fd, str, strlen(str));
}
and then use it like this:
- strbuf_addstr(&buf, "list\n");
- write_in_full(helper->in, buf.buf, buf.len);
- strbuf_reset(&buf);
+ write_str_in_full(helper->in, "list\n");
Thus not requiring the added allocation, and still avoiding
the maintenance risk of literal string lengths.
These days, compilers are good enough that strlen("literal")
imposes no run-time cost.
Transformed via this:
perl -pi -e \
's/write_in_full\((.*?), (".*?"), \d+\)/write_str_in_full($1, $2)/'\
$(git grep -l 'write_in_full.*"')
Signed-off-by: Jim Meyering <meyering@redhat.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-12 08:54:32 +00:00
|
|
|
write_str_in_full(out_fd, "\t") != 1 ||
|
2008-07-09 12:58:57 +00:00
|
|
|
write_in_full(out_fd, path, length) != length)
|
|
|
|
die("unable to write rerere record");
|
|
|
|
}
|
|
|
|
if (commit_lock_file(&write_lock) != 0)
|
|
|
|
die("unable to write rerere record");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-12-05 00:35:48 +00:00
|
|
|
static void ferr_write(const void *p, size_t count, FILE *fp, int *err)
|
|
|
|
{
|
|
|
|
if (!count || *err)
|
|
|
|
return;
|
|
|
|
if (fwrite(p, count, 1, fp) != 1)
|
|
|
|
*err = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void ferr_puts(const char *s, FILE *fp, int *err)
|
|
|
|
{
|
|
|
|
ferr_write(s, strlen(s), fp, err);
|
|
|
|
}
|
|
|
|
|
2009-12-25 22:34:53 +00:00
|
|
|
struct rerere_io {
|
|
|
|
int (*getline)(struct strbuf *, struct rerere_io *);
|
|
|
|
FILE *output;
|
|
|
|
int wrerror;
|
|
|
|
/* some more stuff */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void rerere_io_putstr(const char *str, struct rerere_io *io)
|
|
|
|
{
|
|
|
|
if (io->output)
|
|
|
|
ferr_puts(str, io->output, &io->wrerror);
|
|
|
|
}
|
|
|
|
|
2010-01-17 07:06:45 +00:00
|
|
|
static void rerere_io_putconflict(int ch, int size, struct rerere_io *io)
|
|
|
|
{
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
if (size < sizeof(buf) - 2) {
|
|
|
|
memset(buf, ch, size);
|
|
|
|
buf[size] = '\n';
|
|
|
|
buf[size + 1] = '\0';
|
|
|
|
size = 0;
|
|
|
|
} else {
|
|
|
|
int sz = sizeof(buf) - 1;
|
|
|
|
if (size <= sz)
|
|
|
|
sz -= (sz - size) + 1;
|
|
|
|
memset(buf, ch, sz);
|
|
|
|
buf[sz] = '\0';
|
|
|
|
size -= sz;
|
|
|
|
}
|
|
|
|
rerere_io_putstr(buf, io);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-25 22:34:53 +00:00
|
|
|
static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io)
|
|
|
|
{
|
|
|
|
if (io->output)
|
|
|
|
ferr_write(mem, sz, io->output, &io->wrerror);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rerere_io_file {
|
|
|
|
struct rerere_io io;
|
|
|
|
FILE *input;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
|
|
|
|
{
|
|
|
|
struct rerere_io_file *io = (struct rerere_io_file *)io_;
|
|
|
|
return strbuf_getwholeline(sb, io->input, '\n');
|
|
|
|
}
|
|
|
|
|
2010-01-17 07:06:45 +00:00
|
|
|
static int is_cmarker(char *buf, int marker_char, int marker_size, int want_sp)
|
|
|
|
{
|
|
|
|
while (marker_size--)
|
|
|
|
if (*buf++ != marker_char)
|
|
|
|
return 0;
|
|
|
|
if (want_sp && *buf != ' ')
|
|
|
|
return 0;
|
|
|
|
return isspace(*buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
2008-10-01 18:05:20 +00:00
|
|
|
git_SHA_CTX ctx;
|
2008-08-29 17:12:23 +00:00
|
|
|
int hunk_no = 0;
|
|
|
|
enum {
|
2010-05-14 09:31:35 +00:00
|
|
|
RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL
|
2008-08-29 17:12:23 +00:00
|
|
|
} hunk = RR_CONTEXT;
|
2008-10-09 19:12:12 +00:00
|
|
|
struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
|
2009-12-25 21:55:29 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2008-07-09 12:58:57 +00:00
|
|
|
|
|
|
|
if (sha1)
|
2008-10-01 18:05:20 +00:00
|
|
|
git_SHA1_Init(&ctx);
|
2008-07-09 12:58:57 +00:00
|
|
|
|
2009-12-25 22:34:53 +00:00
|
|
|
while (!io->getline(&buf, io)) {
|
2010-01-17 07:06:45 +00:00
|
|
|
if (is_cmarker(buf.buf, '<', marker_size, 1)) {
|
2008-08-29 17:12:23 +00:00
|
|
|
if (hunk != RR_CONTEXT)
|
2008-07-09 12:58:57 +00:00
|
|
|
goto bad;
|
2008-08-29 17:12:23 +00:00
|
|
|
hunk = RR_SIDE_1;
|
2010-01-17 07:06:45 +00:00
|
|
|
} else if (is_cmarker(buf.buf, '|', marker_size, 0)) {
|
2008-08-29 17:12:23 +00:00
|
|
|
if (hunk != RR_SIDE_1)
|
2008-07-09 12:58:57 +00:00
|
|
|
goto bad;
|
2008-08-29 17:24:45 +00:00
|
|
|
hunk = RR_ORIGINAL;
|
2010-01-17 07:06:45 +00:00
|
|
|
} else if (is_cmarker(buf.buf, '=', marker_size, 0)) {
|
2008-08-29 17:24:45 +00:00
|
|
|
if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
|
2008-07-09 12:58:57 +00:00
|
|
|
goto bad;
|
2008-08-29 17:12:23 +00:00
|
|
|
hunk = RR_SIDE_2;
|
2010-01-17 07:06:45 +00:00
|
|
|
} else if (is_cmarker(buf.buf, '>', marker_size, 1)) {
|
2008-08-29 17:12:23 +00:00
|
|
|
if (hunk != RR_SIDE_2)
|
2008-07-09 12:58:57 +00:00
|
|
|
goto bad;
|
|
|
|
if (strbuf_cmp(&one, &two) > 0)
|
|
|
|
strbuf_swap(&one, &two);
|
|
|
|
hunk_no++;
|
2008-08-29 17:12:23 +00:00
|
|
|
hunk = RR_CONTEXT;
|
2010-01-17 07:06:45 +00:00
|
|
|
rerere_io_putconflict('<', marker_size, io);
|
2009-12-25 22:34:53 +00:00
|
|
|
rerere_io_putmem(one.buf, one.len, io);
|
2010-01-17 07:06:45 +00:00
|
|
|
rerere_io_putconflict('=', marker_size, io);
|
2009-12-25 22:34:53 +00:00
|
|
|
rerere_io_putmem(two.buf, two.len, io);
|
2010-01-17 07:06:45 +00:00
|
|
|
rerere_io_putconflict('>', marker_size, io);
|
2008-07-09 12:58:57 +00:00
|
|
|
if (sha1) {
|
2008-10-01 18:05:20 +00:00
|
|
|
git_SHA1_Update(&ctx, one.buf ? one.buf : "",
|
2008-07-09 12:58:57 +00:00
|
|
|
one.len + 1);
|
2008-10-01 18:05:20 +00:00
|
|
|
git_SHA1_Update(&ctx, two.buf ? two.buf : "",
|
2008-07-09 12:58:57 +00:00
|
|
|
two.len + 1);
|
|
|
|
}
|
|
|
|
strbuf_reset(&one);
|
|
|
|
strbuf_reset(&two);
|
2008-08-29 17:12:23 +00:00
|
|
|
} else if (hunk == RR_SIDE_1)
|
2009-12-25 21:55:29 +00:00
|
|
|
strbuf_addstr(&one, buf.buf);
|
2008-08-29 17:24:45 +00:00
|
|
|
else if (hunk == RR_ORIGINAL)
|
|
|
|
; /* discard */
|
2008-08-29 17:12:23 +00:00
|
|
|
else if (hunk == RR_SIDE_2)
|
2009-12-25 21:55:29 +00:00
|
|
|
strbuf_addstr(&two, buf.buf);
|
2009-12-25 22:34:53 +00:00
|
|
|
else
|
|
|
|
rerere_io_putstr(buf.buf, io);
|
2008-07-09 12:58:57 +00:00
|
|
|
continue;
|
|
|
|
bad:
|
|
|
|
hunk = 99; /* force error exit */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strbuf_release(&one);
|
|
|
|
strbuf_release(&two);
|
2009-12-25 21:55:29 +00:00
|
|
|
strbuf_release(&buf);
|
2008-07-09 12:58:57 +00:00
|
|
|
|
|
|
|
if (sha1)
|
2008-10-01 18:05:20 +00:00
|
|
|
git_SHA1_Final(sha1, &ctx);
|
2009-12-25 22:34:53 +00:00
|
|
|
if (hunk != RR_CONTEXT)
|
|
|
|
return -1;
|
|
|
|
return hunk_no;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_file(const char *path, unsigned char *sha1, const char *output)
|
|
|
|
{
|
|
|
|
int hunk_no = 0;
|
|
|
|
struct rerere_io_file io;
|
2010-01-17 07:28:46 +00:00
|
|
|
int marker_size = ll_merge_marker_size(path);
|
2009-12-25 22:34:53 +00:00
|
|
|
|
|
|
|
memset(&io, 0, sizeof(io));
|
|
|
|
io.io.getline = rerere_file_getline;
|
|
|
|
io.input = fopen(path, "r");
|
|
|
|
io.io.wrerror = 0;
|
|
|
|
if (!io.input)
|
|
|
|
return error("Could not open %s", path);
|
|
|
|
|
|
|
|
if (output) {
|
|
|
|
io.io.output = fopen(output, "w");
|
|
|
|
if (!io.io.output) {
|
|
|
|
fclose(io.input);
|
|
|
|
return error("Could not write %s", output);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-17 07:06:45 +00:00
|
|
|
hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
|
2009-12-25 22:34:53 +00:00
|
|
|
|
|
|
|
fclose(io.input);
|
|
|
|
if (io.io.wrerror)
|
|
|
|
error("There were errors while writing %s (%s)",
|
|
|
|
path, strerror(io.io.wrerror));
|
|
|
|
if (io.io.output && fclose(io.io.output))
|
|
|
|
io.io.wrerror = error("Failed to flush %s: %s",
|
|
|
|
path, strerror(errno));
|
|
|
|
|
|
|
|
if (hunk_no < 0) {
|
2008-07-09 12:58:57 +00:00
|
|
|
if (output)
|
2009-04-29 21:22:56 +00:00
|
|
|
unlink_or_warn(output);
|
2008-07-09 12:58:57 +00:00
|
|
|
return error("Could not parse conflict hunks in %s", path);
|
|
|
|
}
|
2009-12-25 22:34:53 +00:00
|
|
|
if (io.io.wrerror)
|
2008-12-05 00:35:48 +00:00
|
|
|
return -1;
|
2008-07-09 12:58:57 +00:00
|
|
|
return hunk_no;
|
|
|
|
}
|
|
|
|
|
2009-12-25 23:51:32 +00:00
|
|
|
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;
|
2010-01-17 07:28:46 +00:00
|
|
|
int marker_size = ll_merge_marker_size(path);
|
2009-12-25 23:51:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reproduce the conflicted merge in-core
|
|
|
|
*/
|
|
|
|
len = strlen(path);
|
|
|
|
pos = cache_name_pos(path, len);
|
|
|
|
if (0 <= pos)
|
2008-12-05 00:35:48 +00:00
|
|
|
return -1;
|
2009-12-25 23:51:32 +00:00
|
|
|
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("");
|
|
|
|
}
|
2010-08-05 11:24:58 +00:00
|
|
|
/*
|
|
|
|
* NEEDSWORK: handle conflicts from merges with
|
|
|
|
* merge.renormalize set, too
|
|
|
|
*/
|
2010-03-21 00:38:58 +00:00
|
|
|
ll_merge(&result, path, &mmfile[0], NULL,
|
2009-12-25 23:51:32 +00:00
|
|
|
&mmfile[1], "ours",
|
|
|
|
&mmfile[2], "theirs", 0);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
free(mmfile[i].ptr);
|
|
|
|
|
2010-01-28 14:52:16 +00:00
|
|
|
memset(&io, 0, sizeof(io));
|
2009-12-25 23:51:32 +00:00
|
|
|
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);
|
|
|
|
|
2010-01-17 07:06:45 +00:00
|
|
|
hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size);
|
2009-12-25 23:51:32 +00:00
|
|
|
strbuf_release(&io.input);
|
|
|
|
if (io.io.output)
|
|
|
|
fclose(io.io.output);
|
2008-07-09 12:58:57 +00:00
|
|
|
return hunk_no;
|
|
|
|
}
|
|
|
|
|
2008-07-21 18:03:49 +00:00
|
|
|
static int find_conflict(struct string_list *conflict)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
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)) {
|
2008-07-21 18:03:49 +00:00
|
|
|
string_list_insert((const char *)e2->name, conflict);
|
2008-07-09 12:58:57 +00:00
|
|
|
i++; /* skip over both #2 and #3 */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
rerere: never renormalize
plain rerere performs three tasks; let us consider how the new
merge.renormalize option should apply to each.
After an unsuccessful merge, rerere records conflict hunks from the
work tree under .git/rr-cache. If the merge was performed with
merge.renormalize enabled, both sides of the conflict hunk use the
current work tree’s end-of-line and smudge rules; there is not really
much of a choice.
After a successful manual resolution, rerere records the postimage.
Here, also, the file will be in the current work tree’s canonical
format and there is not much to do about it.
When encountering that conflict again, merge looks up the preimage
and postimage using the conflict hunk as a key and runs a three-way
merge to apply that resolution to the work tree. Since the conflict
hunk used the current work tree’s canonical format, chances are the
version in the work tree, the preimage, and the postimage will, too.
In fact using the merge.renormalize machinery is exactly the wrong
thing to do, since its result has been run through convert_to_git
and therefore is not suitable for writing to the work tree.
The only affected caller is "git merge".
NEEDSWORK: lacks test
Cc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-05 11:30:26 +00:00
|
|
|
static int merge(const char *name, const char *path)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2010-02-23 20:11:53 +00:00
|
|
|
mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
|
2008-07-09 12:58:57 +00:00
|
|
|
mmbuffer_t result = {NULL, 0};
|
|
|
|
|
2009-02-14 22:21:04 +00:00
|
|
|
if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
|
2008-07-09 12:58:57 +00:00
|
|
|
return 1;
|
|
|
|
|
2009-02-14 22:21:04 +00:00
|
|
|
if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
|
|
|
|
read_mmfile(&base, rerere_path(name, "preimage")) ||
|
2010-02-23 20:11:53 +00:00
|
|
|
read_mmfile(&other, rerere_path(name, "postimage"))) {
|
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
rerere: never renormalize
plain rerere performs three tasks; let us consider how the new
merge.renormalize option should apply to each.
After an unsuccessful merge, rerere records conflict hunks from the
work tree under .git/rr-cache. If the merge was performed with
merge.renormalize enabled, both sides of the conflict hunk use the
current work tree’s end-of-line and smudge rules; there is not really
much of a choice.
After a successful manual resolution, rerere records the postimage.
Here, also, the file will be in the current work tree’s canonical
format and there is not much to do about it.
When encountering that conflict again, merge looks up the preimage
and postimage using the conflict hunk as a key and runs a three-way
merge to apply that resolution to the work tree. Since the conflict
hunk used the current work tree’s canonical format, chances are the
version in the work tree, the preimage, and the postimage will, too.
In fact using the merge.renormalize machinery is exactly the wrong
thing to do, since its result has been run through convert_to_git
and therefore is not suitable for writing to the work tree.
The only affected caller is "git merge".
NEEDSWORK: lacks test
Cc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-05 11:30:26 +00:00
|
|
|
ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", 0);
|
2008-07-09 12:58:57 +00:00
|
|
|
if (!ret) {
|
|
|
|
FILE *f = fopen(path, "w");
|
|
|
|
if (!f)
|
2008-12-05 00:35:48 +00:00
|
|
|
return error("Could not open %s: %s", path,
|
|
|
|
strerror(errno));
|
|
|
|
if (fwrite(result.ptr, result.size, 1, f) != 1)
|
|
|
|
error("Could not write %s: %s", path, strerror(errno));
|
|
|
|
if (fclose(f))
|
|
|
|
return error("Writing %s failed: %s", path,
|
|
|
|
strerror(errno));
|
2008-07-09 12:58:57 +00:00
|
|
|
}
|
|
|
|
|
2010-02-23 20:11:53 +00:00
|
|
|
out:
|
2008-07-09 12:58:57 +00:00
|
|
|
free(cur.ptr);
|
|
|
|
free(base.ptr);
|
|
|
|
free(other.ptr);
|
|
|
|
free(result.ptr);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct lock_file index_lock;
|
|
|
|
|
2008-07-21 18:03:49 +00:00
|
|
|
static int update_paths(struct string_list *update)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int fd = hold_locked_index(&index_lock, 0);
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; i < update->nr; i++) {
|
2008-07-21 18:03:49 +00:00
|
|
|
struct string_list_item *item = &update->items[i];
|
|
|
|
if (add_file_to_cache(item->string, ADD_CACHE_IGNORE_ERRORS))
|
2008-07-09 12:58:57 +00:00
|
|
|
status = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!status && active_cache_changed) {
|
|
|
|
if (write_cache(fd, active_cache, active_nr) ||
|
|
|
|
commit_locked_index(&index_lock))
|
|
|
|
die("Unable to write new index file");
|
|
|
|
} else if (fd >= 0)
|
|
|
|
rollback_lock_file(&index_lock);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
rerere: never renormalize
plain rerere performs three tasks; let us consider how the new
merge.renormalize option should apply to each.
After an unsuccessful merge, rerere records conflict hunks from the
work tree under .git/rr-cache. If the merge was performed with
merge.renormalize enabled, both sides of the conflict hunk use the
current work tree’s end-of-line and smudge rules; there is not really
much of a choice.
After a successful manual resolution, rerere records the postimage.
Here, also, the file will be in the current work tree’s canonical
format and there is not much to do about it.
When encountering that conflict again, merge looks up the preimage
and postimage using the conflict hunk as a key and runs a three-way
merge to apply that resolution to the work tree. Since the conflict
hunk used the current work tree’s canonical format, chances are the
version in the work tree, the preimage, and the postimage will, too.
In fact using the merge.renormalize machinery is exactly the wrong
thing to do, since its result has been run through convert_to_git
and therefore is not suitable for writing to the work tree.
The only affected caller is "git merge".
NEEDSWORK: lacks test
Cc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-05 11:30:26 +00:00
|
|
|
static int do_plain_rerere(struct string_list *rr, int fd)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
2008-07-21 18:03:49 +00:00
|
|
|
struct string_list conflict = { NULL, 0, 0, 1 };
|
|
|
|
struct string_list update = { NULL, 0, 0, 1 };
|
2008-07-09 12:58:57 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
find_conflict(&conflict);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MERGE_RR records paths with conflicts immediately after merge
|
|
|
|
* failed. Some of the conflicted paths might have been hand resolved
|
|
|
|
* in the working tree since then, but the initial run would catch all
|
|
|
|
* and register their preimages.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (i = 0; i < conflict.nr; i++) {
|
2008-07-21 18:03:49 +00:00
|
|
|
const char *path = conflict.items[i].string;
|
|
|
|
if (!string_list_has_string(rr, path)) {
|
2008-07-09 12:58:57 +00:00
|
|
|
unsigned char sha1[20];
|
|
|
|
char *hex;
|
|
|
|
int ret;
|
|
|
|
ret = handle_file(path, sha1, NULL);
|
|
|
|
if (ret < 1)
|
|
|
|
continue;
|
|
|
|
hex = xstrdup(sha1_to_hex(sha1));
|
2008-07-21 18:03:49 +00:00
|
|
|
string_list_insert(path, rr)->util = hex;
|
2008-07-09 12:58:57 +00:00
|
|
|
if (mkdir(git_path("rr-cache/%s", hex), 0755))
|
2009-02-11 01:42:04 +00:00
|
|
|
continue;
|
2009-02-14 22:21:04 +00:00
|
|
|
handle_file(path, NULL, rerere_path(hex, "preimage"));
|
2008-07-09 12:58:57 +00:00
|
|
|
fprintf(stderr, "Recorded preimage for '%s'\n", path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now some of the paths that had conflicts earlier might have been
|
|
|
|
* hand resolved. Others may be similar to a conflict already that
|
|
|
|
* was resolved before.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (i = 0; i < rr->nr; i++) {
|
|
|
|
int ret;
|
2008-07-21 18:03:49 +00:00
|
|
|
const char *path = rr->items[i].string;
|
2008-07-09 12:58:57 +00:00
|
|
|
const char *name = (const char *)rr->items[i].util;
|
|
|
|
|
2009-02-14 22:21:04 +00:00
|
|
|
if (has_rerere_resolution(name)) {
|
rerere: never renormalize
plain rerere performs three tasks; let us consider how the new
merge.renormalize option should apply to each.
After an unsuccessful merge, rerere records conflict hunks from the
work tree under .git/rr-cache. If the merge was performed with
merge.renormalize enabled, both sides of the conflict hunk use the
current work tree’s end-of-line and smudge rules; there is not really
much of a choice.
After a successful manual resolution, rerere records the postimage.
Here, also, the file will be in the current work tree’s canonical
format and there is not much to do about it.
When encountering that conflict again, merge looks up the preimage
and postimage using the conflict hunk as a key and runs a three-way
merge to apply that resolution to the work tree. Since the conflict
hunk used the current work tree’s canonical format, chances are the
version in the work tree, the preimage, and the postimage will, too.
In fact using the merge.renormalize machinery is exactly the wrong
thing to do, since its result has been run through convert_to_git
and therefore is not suitable for writing to the work tree.
The only affected caller is "git merge".
NEEDSWORK: lacks test
Cc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-05 11:30:26 +00:00
|
|
|
if (!merge(name, path)) {
|
2008-07-09 12:58:57 +00:00
|
|
|
if (rerere_autoupdate)
|
2008-07-21 18:03:49 +00:00
|
|
|
string_list_insert(path, &update);
|
2008-07-17 03:25:18 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"%s '%s' using previous resolution.\n",
|
|
|
|
rerere_autoupdate
|
|
|
|
? "Staged" : "Resolved",
|
|
|
|
path);
|
2008-07-09 12:58:57 +00:00
|
|
|
goto mark_resolved;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Let's see if we have resolved it. */
|
|
|
|
ret = handle_file(path, NULL, NULL);
|
|
|
|
if (ret)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fprintf(stderr, "Recorded resolution for '%s'.\n", path);
|
2009-02-14 22:21:04 +00:00
|
|
|
copy_file(rerere_path(name, "postimage"), path, 0666);
|
2008-07-09 12:58:57 +00:00
|
|
|
mark_resolved:
|
|
|
|
rr->items[i].util = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (update.nr)
|
|
|
|
update_paths(&update);
|
|
|
|
|
|
|
|
return write_rr(rr, fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int git_rerere_config(const char *var, const char *value, void *cb)
|
|
|
|
{
|
|
|
|
if (!strcmp(var, "rerere.enabled"))
|
|
|
|
rerere_enabled = git_config_bool(var, value);
|
|
|
|
else if (!strcmp(var, "rerere.autoupdate"))
|
|
|
|
rerere_autoupdate = git_config_bool(var, value);
|
|
|
|
else
|
|
|
|
return git_default_config(var, value, cb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_rerere_enabled(void)
|
|
|
|
{
|
|
|
|
const char *rr_cache;
|
|
|
|
int rr_cache_exists;
|
|
|
|
|
|
|
|
if (!rerere_enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rr_cache = git_path("rr-cache");
|
2008-09-09 08:27:07 +00:00
|
|
|
rr_cache_exists = is_directory(rr_cache);
|
2008-07-09 12:58:57 +00:00
|
|
|
if (rerere_enabled < 0)
|
|
|
|
return rr_cache_exists;
|
|
|
|
|
|
|
|
if (!rr_cache_exists &&
|
|
|
|
(mkdir(rr_cache, 0777) || adjust_shared_perm(rr_cache)))
|
|
|
|
die("Could not create directory %s", rr_cache);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-12-04 08:20:48 +00:00
|
|
|
int setup_rerere(struct string_list *merge_rr, int flags)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
git_config(git_rerere_config, NULL);
|
|
|
|
if (!is_rerere_enabled())
|
|
|
|
return -1;
|
|
|
|
|
2009-12-04 08:20:48 +00:00
|
|
|
if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
|
|
|
|
rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
|
2008-10-27 10:22:09 +00:00
|
|
|
merge_rr_path = git_pathdup("MERGE_RR");
|
2008-10-17 22:44:39 +00:00
|
|
|
fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
|
|
|
|
LOCK_DIE_ON_ERROR);
|
2008-07-09 12:58:57 +00:00
|
|
|
read_rr(merge_rr);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2009-12-04 08:20:48 +00:00
|
|
|
int rerere(int flags)
|
2008-07-09 12:58:57 +00:00
|
|
|
{
|
2008-07-21 18:03:49 +00:00
|
|
|
struct string_list merge_rr = { NULL, 0, 0, 1 };
|
2008-07-09 12:58:57 +00:00
|
|
|
int fd;
|
|
|
|
|
2009-12-04 08:20:48 +00:00
|
|
|
fd = setup_rerere(&merge_rr, flags);
|
2008-07-09 12:58:57 +00:00
|
|
|
if (fd < 0)
|
|
|
|
return 0;
|
rerere: never renormalize
plain rerere performs three tasks; let us consider how the new
merge.renormalize option should apply to each.
After an unsuccessful merge, rerere records conflict hunks from the
work tree under .git/rr-cache. If the merge was performed with
merge.renormalize enabled, both sides of the conflict hunk use the
current work tree’s end-of-line and smudge rules; there is not really
much of a choice.
After a successful manual resolution, rerere records the postimage.
Here, also, the file will be in the current work tree’s canonical
format and there is not much to do about it.
When encountering that conflict again, merge looks up the preimage
and postimage using the conflict hunk as a key and runs a three-way
merge to apply that resolution to the work tree. Since the conflict
hunk used the current work tree’s canonical format, chances are the
version in the work tree, the preimage, and the postimage will, too.
In fact using the merge.renormalize machinery is exactly the wrong
thing to do, since its result has been run through convert_to_git
and therefore is not suitable for writing to the work tree.
The only affected caller is "git merge".
NEEDSWORK: lacks test
Cc: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-08-05 11:30:26 +00:00
|
|
|
return do_plain_rerere(&merge_rr, fd);
|
2008-07-09 12:58:57 +00:00
|
|
|
}
|
2009-12-25 23:51:32 +00:00
|
|
|
|
|
|
|
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");
|
|
|
|
|
2010-01-20 22:44:31 +00:00
|
|
|
fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
|
2009-12-25 23:51:32 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|