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