merge-file.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #include "builtin.h"
  2. #include "cache.h"
  3. #include "config.h"
  4. #include "xdiff/xdiff.h"
  5. #include "xdiff-interface.h"
  6. #include "parse-options.h"
  7. static const char *const merge_file_usage[] = {
  8. N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
  9. NULL
  10. };
  11. static int label_cb(const struct option *opt, const char *arg, int unset)
  12. {
  13. static int label_count = 0;
  14. const char **names = (const char **)opt->value;
  15. BUG_ON_OPT_NEG(unset);
  16. if (label_count >= 3)
  17. return error("too many labels on the command line");
  18. names[label_count++] = arg;
  19. return 0;
  20. }
  21. int cmd_merge_file(int argc, const char **argv, const char *prefix)
  22. {
  23. const char *names[3] = { NULL, NULL, NULL };
  24. mmfile_t mmfs[3];
  25. mmbuffer_t result = {NULL, 0};
  26. xmparam_t xmp = {{0}};
  27. int ret = 0, i = 0, to_stdout = 0;
  28. int quiet = 0;
  29. struct option options[] = {
  30. OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
  31. OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
  32. OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
  33. XDL_MERGE_FAVOR_OURS),
  34. OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
  35. XDL_MERGE_FAVOR_THEIRS),
  36. OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
  37. XDL_MERGE_FAVOR_UNION),
  38. OPT_INTEGER(0, "marker-size", &xmp.marker_size,
  39. N_("for conflicts, use this marker size")),
  40. OPT__QUIET(&quiet, N_("do not warn about conflicts")),
  41. OPT_CALLBACK('L', NULL, names, N_("name"),
  42. N_("set labels for file1/orig-file/file2"), &label_cb),
  43. OPT_END(),
  44. };
  45. xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
  46. xmp.style = 0;
  47. xmp.favor = 0;
  48. if (startup_info->have_repository) {
  49. /* Read the configuration file */
  50. git_config(git_xmerge_config, NULL);
  51. if (0 <= git_xmerge_style)
  52. xmp.style = git_xmerge_style;
  53. }
  54. argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
  55. if (argc != 3)
  56. usage_with_options(merge_file_usage, options);
  57. if (quiet) {
  58. if (!freopen("/dev/null", "w", stderr))
  59. return error_errno("failed to redirect stderr to /dev/null");
  60. }
  61. for (i = 0; i < 3; i++) {
  62. char *fname;
  63. int ret;
  64. if (!names[i])
  65. names[i] = argv[i];
  66. fname = prefix_filename(prefix, argv[i]);
  67. ret = read_mmfile(mmfs + i, fname);
  68. free(fname);
  69. if (ret)
  70. return -1;
  71. if (mmfs[i].size > MAX_XDIFF_SIZE ||
  72. buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
  73. return error("Cannot merge binary files: %s",
  74. argv[i]);
  75. }
  76. xmp.ancestor = names[1];
  77. xmp.file1 = names[0];
  78. xmp.file2 = names[2];
  79. ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
  80. for (i = 0; i < 3; i++)
  81. free(mmfs[i].ptr);
  82. if (ret >= 0) {
  83. const char *filename = argv[0];
  84. char *fpath = prefix_filename(prefix, argv[0]);
  85. FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
  86. if (!f)
  87. ret = error_errno("Could not open %s for writing",
  88. filename);
  89. else if (result.size &&
  90. fwrite(result.ptr, result.size, 1, f) != 1)
  91. ret = error_errno("Could not write to %s", filename);
  92. else if (fclose(f))
  93. ret = error_errno("Could not close %s", filename);
  94. free(result.ptr);
  95. free(fpath);
  96. }
  97. if (ret > 127)
  98. ret = 127;
  99. return ret;
  100. }