diff --git a/Makefile b/Makefile index 23bbb90683b527..79cb91feec2ff3 100644 --- a/Makefile +++ b/Makefile @@ -275,6 +275,7 @@ BUILTIN_OBJS = \ builtin-ls-tree.o \ builtin-mailinfo.o \ builtin-mailsplit.o \ + builtin-merge-file.o \ builtin-mv.o \ builtin-name-rev.o \ builtin-pack-objects.o \ diff --git a/builtin-merge-file.c b/builtin-merge-file.c new file mode 100644 index 00000000000000..41fb92edfbd051 --- /dev/null +++ b/builtin-merge-file.c @@ -0,0 +1,72 @@ +#include "cache.h" +#include "xdiff/xdiff.h" + +static const char merge_file_usage[] = +"git merge-file [-L name1 [-L orig [-L name2]]] file1 orig_file file2"; + +static int read_file(mmfile_t *ptr, const char *filename) +{ + struct stat st; + FILE *f; + + if (stat(filename, &st)) + return error("Could not stat %s", filename); + if ((f = fopen(filename, "rb")) == NULL) + return error("Could not open %s", filename); + ptr->ptr = xmalloc(st.st_size); + if (fread(ptr->ptr, st.st_size, 1, f) != 1) + return error("Could not read %s", filename); + fclose(f); + ptr->size = st.st_size; + return 0; +} + +int cmd_merge_file(int argc, char **argv, char **envp) +{ + char *names[3]; + mmfile_t mmfs[3]; + mmbuffer_t result = {NULL, 0}; + xpparam_t xpp = {XDF_NEED_MINIMAL}; + int ret = 0, i = 0; + + while (argc > 4) { + if (!strcmp(argv[1], "-L")) { + names[i++] = argv[2]; + argc -= 2; + argv += 2; + continue; + } + usage(merge_file_usage); + } + + if (argc != 4) + usage(merge_file_usage); + + for (; i < 3; i++) + names[i] = argv[i + 1]; + + for (i = 0; i < 3; i++) + if (read_file(mmfs + i, argv[i + 1])) + return -1; + + ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2], + &xpp, XDL_MERGE_ZEALOUS, &result); + + for (i = 0; i < 3; i++) + free(mmfs[i].ptr); + + if (ret >= 0) { + char *filename = argv[1]; + FILE *f = fopen(filename, "wb"); + + if (!f) + ret = error("Could not open %s for writing", filename); + else if (fwrite(result.ptr, result.size, 1, f) != 1) + ret = error("Could not write to %s", filename); + else if (fclose(f)) + ret = error("Could not close %s", filename); + free(result.ptr); + } + + return ret; +} diff --git a/builtin.h b/builtin.h index b5116f30e47cb4..08519e7c828b79 100644 --- a/builtin.h +++ b/builtin.h @@ -42,6 +42,7 @@ extern int cmd_ls_files(int argc, const char **argv, const char *prefix); extern int cmd_ls_tree(int argc, const char **argv, const char *prefix); extern int cmd_mailinfo(int argc, const char **argv, const char *prefix); extern int cmd_mailsplit(int argc, const char **argv, const char *prefix); +extern int cmd_merge_file(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); diff --git a/git.c b/git.c index 357330e02cf4c5..016ee8adb730ea 100644 --- a/git.c +++ b/git.c @@ -247,6 +247,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "ls-tree", cmd_ls_tree, RUN_SETUP }, { "mailinfo", cmd_mailinfo }, { "mailsplit", cmd_mailsplit }, + { "merge-file", cmd_merge_file }, { "mv", cmd_mv, RUN_SETUP }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh new file mode 100644 index 00000000000000..5d9b6f34b84dd7 --- /dev/null +++ b/t/t6023-merge-file.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +test_description='RCS merge replacement: merge-file' +. ./test-lib.sh + +cat > orig.txt << EOF +Dominus regit me, +et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +EOF + +cat > new1.txt << EOF +Dominus regit me, +et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +Nam et si ambulavero in medio umbrae mortis, +non timebo mala, quoniam tu mecum es: +virga tua et baculus tuus ipsa me consolata sunt. +EOF + +cat > new2.txt << EOF +Dominus regit me, et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +EOF + +cat > new3.txt << EOF +DOMINUS regit me, +et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +EOF + +cat > new4.txt << EOF +Dominus regit me, et nihil mihi deerit. +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +EOF +echo -n "propter nomen suum." >> new4.txt + +cp new1.txt test.txt +test_expect_success "merge without conflict" \ + "git-merge-file test.txt orig.txt new2.txt" + +cp new1.txt test2.txt +test_expect_success "merge without conflict (missing LF at EOF)" \ + "git-merge-file test2.txt orig.txt new2.txt" + +test_expect_success "merge result added missing LF" \ + "diff -u test.txt test2.txt" + +cp test.txt backup.txt +test_expect_failure "merge with conflicts" \ + "git-merge-file test.txt orig.txt new3.txt" + +cat > expect.txt << EOF +<<<<<<< test.txt +Dominus regit me, et nihil mihi deerit. +======= +DOMINUS regit me, +et nihil mihi deerit. +>>>>>>> new3.txt +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +Nam et si ambulavero in medio umbrae mortis, +non timebo mala, quoniam tu mecum es: +virga tua et baculus tuus ipsa me consolata sunt. +EOF + +test_expect_success "expected conflict markers" "diff -u test.txt expect.txt" + +cp backup.txt test.txt +test_expect_failure "merge with conflicts, using -L" \ + "git-merge-file -L 1 -L 2 test.txt orig.txt new3.txt" + +cat > expect.txt << EOF +<<<<<<< 1 +Dominus regit me, et nihil mihi deerit. +======= +DOMINUS regit me, +et nihil mihi deerit. +>>>>>>> new3.txt +In loco pascuae ibi me collocavit, +super aquam refectionis educavit me; +animam meam convertit, +deduxit me super semitas jusitiae, +propter nomen suum. +Nam et si ambulavero in medio umbrae mortis, +non timebo mala, quoniam tu mecum es: +virga tua et baculus tuus ipsa me consolata sunt. +EOF + +test_expect_success "expected conflict markers, with -L" \ + "diff -u test.txt expect.txt" + +test_done +