Git fork
at reftables-rust 192 lines 5.2 kB view 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 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}