apply: plug the three-way merge logic in

When a patch does not apply to what we have, but we know the preimage the
patch was made against, we apply the patch to the preimage to compute what
the patch author wanted the result to look like, and attempt a three-way
merge between the result and our version, using the intended preimage as
the base version.

When we are applying the patch using the index, we would additionally need
to add the object names of these three blobs involved in the merge, which
is not yet done in this step, but we add a field to "struct patch" so that
later write-out step can use it.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2012-05-09 16:10:51 -07:00
parent 519d1a5b4e
commit 28ff051268

View File

@ -16,6 +16,8 @@
#include "dir.h" #include "dir.h"
#include "diff.h" #include "diff.h"
#include "parse-options.h" #include "parse-options.h"
#include "xdiff-interface.h"
#include "ll-merge.h"
/* /*
* --check turns on checking that the working tree matches the * --check turns on checking that the working tree matches the
@ -194,12 +196,16 @@ struct patch {
unsigned int is_copy:1; unsigned int is_copy:1;
unsigned int is_rename:1; unsigned int is_rename:1;
unsigned int recount:1; unsigned int recount:1;
unsigned int conflicted_threeway:1;
struct fragment *fragments; struct fragment *fragments;
char *result; char *result;
size_t resultsize; size_t resultsize;
char old_sha1_prefix[41]; char old_sha1_prefix[41];
char new_sha1_prefix[41]; char new_sha1_prefix[41];
struct patch *next; struct patch *next;
/* three-way fallback result */
unsigned char threeway_stage[3][20];
}; };
static void free_fragment_list(struct fragment *list) static void free_fragment_list(struct fragment *list)
@ -3146,7 +3152,29 @@ static int three_way_merge(struct image *image,
const unsigned char *ours, const unsigned char *ours,
const unsigned char *theirs) const unsigned char *theirs)
{ {
return -1; /* for now */ mmfile_t base_file, our_file, their_file;
mmbuffer_t result = { NULL };
int status;
read_mmblob(&base_file, base);
read_mmblob(&our_file, ours);
read_mmblob(&their_file, theirs);
status = ll_merge(&result, path,
&base_file, "base",
&our_file, "ours",
&their_file, "theirs", NULL);
free(base_file.ptr);
free(our_file.ptr);
free(their_file.ptr);
if (status < 0 || !result.ptr) {
free(result.ptr);
return -1;
}
clear_image(image);
image->buf = result.ptr;
image->len = result.size;
return status;
} }
static int try_threeway(struct image *image, struct patch *patch, static int try_threeway(struct image *image, struct patch *patch,
@ -3155,6 +3183,7 @@ static int try_threeway(struct image *image, struct patch *patch,
unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
size_t len; size_t len;
int status;
char *img; char *img;
struct image tmp_image; struct image tmp_image;
@ -3167,6 +3196,9 @@ static int try_threeway(struct image *image, struct patch *patch,
if (get_sha1(patch->old_sha1_prefix, pre_sha1) || if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
read_blob_object(&buf, pre_sha1, patch->old_mode)) read_blob_object(&buf, pre_sha1, patch->old_mode))
return error("repository lacks the necessary blob to fall back on 3-way merge."); return error("repository lacks the necessary blob to fall back on 3-way merge.");
fprintf(stderr, "Falling back to three-way merge...\n");
img = strbuf_detach(&buf, &len); img = strbuf_detach(&buf, &len);
prepare_image(&tmp_image, img, len, 1); prepare_image(&tmp_image, img, len, 1);
/* Apply the patch to get the post image */ /* Apply the patch to get the post image */
@ -3186,8 +3218,23 @@ static int try_threeway(struct image *image, struct patch *patch,
clear_image(&tmp_image); clear_image(&tmp_image);
/* in-core three-way merge between post and our using pre as base */ /* in-core three-way merge between post and our using pre as base */
return three_way_merge(image, status = three_way_merge(image, patch->new_name,
patch->new_name, pre_sha1, our_sha1, post_sha1); pre_sha1, our_sha1, post_sha1);
if (status < 0) {
fprintf(stderr, "Failed to fall back on three-way merge...\n");
return status;
}
if (status) {
patch->conflicted_threeway = 1;
hashcpy(patch->threeway_stage[0], pre_sha1);
hashcpy(patch->threeway_stage[1], our_sha1);
hashcpy(patch->threeway_stage[2], post_sha1);
fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
} else {
fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
}
return 0;
} }
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)