Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2 #define DISABLE_SIGN_COMPARE_WARNINGS
3
4 #include "builtin.h"
5 #include "abspath.h"
6 #include "diff.h"
7 #include "hex.h"
8 #include "object-file.h"
9 #include "object-name.h"
10 #include "odb.h"
11 #include "config.h"
12 #include "gettext.h"
13 #include "setup.h"
14 #include "xdiff/xdiff.h"
15 #include "xdiff-interface.h"
16 #include "parse-options.h"
17
18 static const char *const merge_file_usage[] = {
19 N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
20 NULL
21 };
22
23 static int label_cb(const struct option *opt, const char *arg, int unset)
24 {
25 static int label_count = 0;
26 const char **names = (const char **)opt->value;
27
28 BUG_ON_OPT_NEG(unset);
29
30 if (label_count >= 3)
31 return error("too many labels on the command line");
32 names[label_count++] = arg;
33 return 0;
34 }
35
36 static int set_diff_algorithm(xpparam_t *xpp,
37 const char *alg)
38 {
39 long diff_algorithm = parse_algorithm_value(alg);
40 if (diff_algorithm < 0)
41 return -1;
42 xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
43 return 0;
44 }
45
46 static int diff_algorithm_cb(const struct option *opt,
47 const char *arg, int unset)
48 {
49 xpparam_t *xpp = opt->value;
50
51 BUG_ON_OPT_NEG(unset);
52
53 if (set_diff_algorithm(xpp, arg))
54 return error(_("option diff-algorithm accepts \"myers\", "
55 "\"minimal\", \"patience\" and \"histogram\""));
56
57 return 0;
58 }
59
60 int cmd_merge_file(int argc,
61 const char **argv,
62 const char *prefix,
63 struct repository *repo)
64 {
65 const char *names[3] = { 0 };
66 mmfile_t mmfs[3] = { 0 };
67 mmbuffer_t result = { 0 };
68 xmparam_t xmp = { 0 };
69 int ret = 0, i = 0, to_stdout = 0, object_id = 0;
70 int quiet = 0;
71 struct option options[] = {
72 OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
73 OPT_BOOL(0, "object-id", &object_id, N_("use object IDs instead of filenames")),
74 OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
75 OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
76 XDL_MERGE_ZEALOUS_DIFF3),
77 OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
78 XDL_MERGE_FAVOR_OURS),
79 OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
80 XDL_MERGE_FAVOR_THEIRS),
81 OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
82 XDL_MERGE_FAVOR_UNION),
83 OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"),
84 N_("choose a diff algorithm"),
85 PARSE_OPT_NONEG, diff_algorithm_cb),
86 OPT_INTEGER(0, "marker-size", &xmp.marker_size,
87 N_("for conflicts, use this marker size")),
88 OPT__QUIET(&quiet, N_("do not warn about conflicts")),
89 OPT_CALLBACK('L', NULL, names, N_("name"),
90 N_("set labels for file1/orig-file/file2"), &label_cb),
91 OPT_END(),
92 };
93
94 xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
95 xmp.style = 0;
96 xmp.favor = 0;
97
98 /* Read the configuration file */
99 repo_config(repo, git_xmerge_config, NULL);
100 if (0 <= git_xmerge_style)
101 xmp.style = git_xmerge_style;
102
103 argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
104 if (argc != 3)
105 usage_with_options(merge_file_usage, options);
106 if (quiet) {
107 if (!freopen("/dev/null", "w", stderr))
108 return error_errno("failed to redirect stderr to /dev/null");
109 }
110
111 if (!repo && object_id)
112 /* emit the correct "not a git repo" error in this case */
113 setup_git_directory(the_repository);
114
115 for (i = 0; i < 3; i++) {
116 char *fname;
117 struct object_id oid;
118 mmfile_t *mmf = mmfs + i;
119
120 if (!names[i])
121 names[i] = argv[i];
122
123 fname = prefix_filename(prefix, argv[i]);
124
125 if (object_id) {
126 if (repo_get_oid(the_repository, argv[i], &oid))
127 ret = error(_("object '%s' does not exist"),
128 argv[i]);
129 else if (!oideq(&oid, the_hash_algo->empty_blob))
130 read_mmblob(mmf, the_repository->objects, &oid);
131 else
132 read_mmfile(mmf, "/dev/null");
133 } else if (read_mmfile(mmf, fname)) {
134 ret = -1;
135 }
136 if (ret != -1 && (mmf->size > MAX_XDIFF_SIZE ||
137 buffer_is_binary(mmf->ptr, mmf->size))) {
138 ret = error("Cannot merge binary files: %s",
139 argv[i]);
140 }
141
142 free(fname);
143 if (ret)
144 goto cleanup;
145
146 }
147
148 xmp.ancestor = names[1];
149 xmp.file1 = names[0];
150 xmp.file2 = names[2];
151 ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
152
153 if (ret >= 0) {
154 if (object_id && !to_stdout) {
155 struct object_id oid;
156 if (result.size) {
157 if (odb_write_object(the_repository->objects, result.ptr,
158 result.size, OBJ_BLOB, &oid) < 0)
159 ret = error(_("Could not write object file"));
160 } else {
161 oidcpy(&oid, the_hash_algo->empty_blob);
162 }
163 if (ret >= 0)
164 printf("%s\n", oid_to_hex(&oid));
165 } else {
166 const char *filename = argv[0];
167 char *fpath = prefix_filename(prefix, argv[0]);
168 FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
169
170 if (!f)
171 ret = error_errno("Could not open %s for writing",
172 filename);
173 else if (result.size &&
174 fwrite(result.ptr, result.size, 1, f) != 1)
175 ret = error_errno("Could not write to %s", filename);
176 else if (fclose(f))
177 ret = error_errno("Could not close %s", filename);
178 free(fpath);
179 }
180 free(result.ptr);
181 }
182
183 if (ret > 127)
184 ret = 127;
185
186 cleanup:
187 for (i = 0; i < 3; i++)
188 free(mmfs[i].ptr);
189
190 return ret;
191 }