Git fork
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
18static 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
23static 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
36static 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
46static 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
60int cmd_merge_file(int argc,
61 const char **argv,
62 const char *prefix,
63 struct repository *repo UNUSED)
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 if (startup_info->have_repository) {
99 /* Read the configuration file */
100 repo_config(the_repository, git_xmerge_config, NULL);
101 if (0 <= git_xmerge_style)
102 xmp.style = git_xmerge_style;
103 }
104
105 argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
106 if (argc != 3)
107 usage_with_options(merge_file_usage, options);
108 if (quiet) {
109 if (!freopen("/dev/null", "w", stderr))
110 return error_errno("failed to redirect stderr to /dev/null");
111 }
112
113 if (object_id)
114 setup_git_directory();
115
116 for (i = 0; i < 3; i++) {
117 char *fname;
118 struct object_id oid;
119 mmfile_t *mmf = mmfs + i;
120
121 if (!names[i])
122 names[i] = argv[i];
123
124 fname = prefix_filename(prefix, argv[i]);
125
126 if (object_id) {
127 if (repo_get_oid(the_repository, argv[i], &oid))
128 ret = error(_("object '%s' does not exist"),
129 argv[i]);
130 else if (!oideq(&oid, the_hash_algo->empty_blob))
131 read_mmblob(mmf, &oid);
132 else
133 read_mmfile(mmf, "/dev/null");
134 } else if (read_mmfile(mmf, fname)) {
135 ret = -1;
136 }
137 if (ret != -1 && (mmf->size > MAX_XDIFF_SIZE ||
138 buffer_is_binary(mmf->ptr, mmf->size))) {
139 ret = error("Cannot merge binary files: %s",
140 argv[i]);
141 }
142
143 free(fname);
144 if (ret)
145 goto cleanup;
146
147 }
148
149 xmp.ancestor = names[1];
150 xmp.file1 = names[0];
151 xmp.file2 = names[2];
152 ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
153
154 if (ret >= 0) {
155 if (object_id && !to_stdout) {
156 struct object_id oid;
157 if (result.size) {
158 if (odb_write_object(the_repository->objects, result.ptr,
159 result.size, OBJ_BLOB, &oid) < 0)
160 ret = error(_("Could not write object file"));
161 } else {
162 oidcpy(&oid, the_hash_algo->empty_blob);
163 }
164 if (ret >= 0)
165 printf("%s\n", oid_to_hex(&oid));
166 } else {
167 const char *filename = argv[0];
168 char *fpath = prefix_filename(prefix, argv[0]);
169 FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
170
171 if (!f)
172 ret = error_errno("Could not open %s for writing",
173 filename);
174 else if (result.size &&
175 fwrite(result.ptr, result.size, 1, f) != 1)
176 ret = error_errno("Could not write to %s", filename);
177 else if (fclose(f))
178 ret = error_errno("Could not close %s", filename);
179 free(fpath);
180 }
181 free(result.ptr);
182 }
183
184 if (ret > 127)
185 ret = 127;
186
187cleanup:
188 for (i = 0; i < 3; i++)
189 free(mmfs[i].ptr);
190
191 return ret;
192}