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