THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

542 lines
14KB

  1. #include "cache.h"
  2. #include "range-diff.h"
  3. #include "string-list.h"
  4. #include "run-command.h"
  5. #include "argv-array.h"
  6. #include "hashmap.h"
  7. #include "xdiff-interface.h"
  8. #include "linear-assignment.h"
  9. #include "diffcore.h"
  10. #include "commit.h"
  11. #include "pretty.h"
  12. #include "userdiff.h"
  13. #include "apply.h"
  14. struct patch_util {
  15. /* For the search for an exact match */
  16. struct hashmap_entry e;
  17. const char *diff, *patch;
  18. int i, shown;
  19. int diffsize;
  20. size_t diff_offset;
  21. /* the index of the matching item in the other branch, or -1 */
  22. int matching;
  23. struct object_id oid;
  24. };
  25. static size_t find_end_of_line(char *buffer, unsigned long size)
  26. {
  27. char *eol = memchr(buffer, '\n', size);
  28. if (!eol)
  29. return size;
  30. *eol = '\0';
  31. return eol + 1 - buffer;
  32. }
  33. /*
  34. * Reads the patches into a string list, with the `util` field being populated
  35. * as struct object_id (will need to be free()d).
  36. */
  37. static int read_patches(const char *range, struct string_list *list)
  38. {
  39. struct child_process cp = CHILD_PROCESS_INIT;
  40. struct strbuf buf = STRBUF_INIT, contents = STRBUF_INIT;
  41. struct patch_util *util = NULL;
  42. int in_header = 1;
  43. char *line, *current_filename = NULL;
  44. int offset, len;
  45. size_t size;
  46. argv_array_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges",
  47. "--reverse", "--date-order", "--decorate=no",
  48. "--no-prefix",
  49. /*
  50. * Choose indicators that are not used anywhere
  51. * else in diffs, but still look reasonable
  52. * (e.g. will not be confusing when debugging)
  53. */
  54. "--output-indicator-new=>",
  55. "--output-indicator-old=<",
  56. "--output-indicator-context=#",
  57. "--no-abbrev-commit", range,
  58. NULL);
  59. cp.out = -1;
  60. cp.no_stdin = 1;
  61. cp.git_cmd = 1;
  62. if (start_command(&cp))
  63. return error_errno(_("could not start `log`"));
  64. if (strbuf_read(&contents, cp.out, 0) < 0) {
  65. error_errno(_("could not read `log` output"));
  66. finish_command(&cp);
  67. return -1;
  68. }
  69. line = contents.buf;
  70. size = contents.len;
  71. for (offset = 0; size > 0; offset += len, size -= len, line += len) {
  72. const char *p;
  73. len = find_end_of_line(line, size);
  74. line[len - 1] = '\0';
  75. if (skip_prefix(line, "commit ", &p)) {
  76. if (util) {
  77. string_list_append(list, buf.buf)->util = util;
  78. strbuf_reset(&buf);
  79. }
  80. util = xcalloc(sizeof(*util), 1);
  81. if (get_oid(p, &util->oid)) {
  82. error(_("could not parse commit '%s'"), p);
  83. free(util);
  84. string_list_clear(list, 1);
  85. strbuf_release(&buf);
  86. strbuf_release(&contents);
  87. finish_command(&cp);
  88. return -1;
  89. }
  90. util->matching = -1;
  91. in_header = 1;
  92. continue;
  93. }
  94. if (starts_with(line, "diff --git")) {
  95. struct patch patch = { 0 };
  96. struct strbuf root = STRBUF_INIT;
  97. int linenr = 0;
  98. in_header = 0;
  99. strbuf_addch(&buf, '\n');
  100. if (!util->diff_offset)
  101. util->diff_offset = buf.len;
  102. line[len - 1] = '\n';
  103. len = parse_git_diff_header(&root, &linenr, 0, line,
  104. len, size, &patch);
  105. if (len < 0)
  106. die(_("could not parse git header '%.*s'"), (int)len, line);
  107. strbuf_addstr(&buf, " ## ");
  108. if (patch.is_new > 0)
  109. strbuf_addf(&buf, "%s (new)", patch.new_name);
  110. else if (patch.is_delete > 0)
  111. strbuf_addf(&buf, "%s (deleted)", patch.old_name);
  112. else if (patch.is_rename)
  113. strbuf_addf(&buf, "%s => %s", patch.old_name, patch.new_name);
  114. else
  115. strbuf_addstr(&buf, patch.new_name);
  116. free(current_filename);
  117. if (patch.is_delete > 0)
  118. current_filename = xstrdup(patch.old_name);
  119. else
  120. current_filename = xstrdup(patch.new_name);
  121. if (patch.new_mode && patch.old_mode &&
  122. patch.old_mode != patch.new_mode)
  123. strbuf_addf(&buf, " (mode change %06o => %06o)",
  124. patch.old_mode, patch.new_mode);
  125. strbuf_addstr(&buf, " ##");
  126. } else if (in_header) {
  127. if (starts_with(line, "Author: ")) {
  128. strbuf_addstr(&buf, " ## Metadata ##\n");
  129. strbuf_addstr(&buf, line);
  130. strbuf_addstr(&buf, "\n\n");
  131. strbuf_addstr(&buf, " ## Commit message ##\n");
  132. } else if (starts_with(line, " ")) {
  133. p = line + len - 2;
  134. while (isspace(*p) && p >= line)
  135. p--;
  136. strbuf_add(&buf, line, p - line + 1);
  137. strbuf_addch(&buf, '\n');
  138. }
  139. continue;
  140. } else if (skip_prefix(line, "@@ ", &p)) {
  141. p = strstr(p, "@@");
  142. strbuf_addstr(&buf, "@@");
  143. if (current_filename && p[2])
  144. strbuf_addf(&buf, " %s:", current_filename);
  145. if (p)
  146. strbuf_addstr(&buf, p + 2);
  147. } else if (!line[0])
  148. /*
  149. * A completely blank (not ' \n', which is context)
  150. * line is not valid in a diff. We skip it
  151. * silently, because this neatly handles the blank
  152. * separator line between commits in git-log
  153. * output.
  154. */
  155. continue;
  156. else if (line[0] == '>') {
  157. strbuf_addch(&buf, '+');
  158. strbuf_addstr(&buf, line + 1);
  159. } else if (line[0] == '<') {
  160. strbuf_addch(&buf, '-');
  161. strbuf_addstr(&buf, line + 1);
  162. } else if (line[0] == '#') {
  163. strbuf_addch(&buf, ' ');
  164. strbuf_addstr(&buf, line + 1);
  165. } else {
  166. strbuf_addch(&buf, ' ');
  167. strbuf_addstr(&buf, line);
  168. }
  169. strbuf_addch(&buf, '\n');
  170. util->diffsize++;
  171. }
  172. strbuf_release(&contents);
  173. if (util)
  174. string_list_append(list, buf.buf)->util = util;
  175. strbuf_release(&buf);
  176. free(current_filename);
  177. if (finish_command(&cp))
  178. return -1;
  179. return 0;
  180. }
  181. static int patch_util_cmp(const void *dummy, const struct patch_util *a,
  182. const struct patch_util *b, const char *keydata)
  183. {
  184. return strcmp(a->diff, keydata ? keydata : b->diff);
  185. }
  186. static void find_exact_matches(struct string_list *a, struct string_list *b)
  187. {
  188. struct hashmap map;
  189. int i;
  190. hashmap_init(&map, (hashmap_cmp_fn)patch_util_cmp, NULL, 0);
  191. /* First, add the patches of a to a hash map */
  192. for (i = 0; i < a->nr; i++) {
  193. struct patch_util *util = a->items[i].util;
  194. util->i = i;
  195. util->patch = a->items[i].string;
  196. util->diff = util->patch + util->diff_offset;
  197. hashmap_entry_init(util, strhash(util->diff));
  198. hashmap_add(&map, util);
  199. }
  200. /* Now try to find exact matches in b */
  201. for (i = 0; i < b->nr; i++) {
  202. struct patch_util *util = b->items[i].util, *other;
  203. util->i = i;
  204. util->patch = b->items[i].string;
  205. util->diff = util->patch + util->diff_offset;
  206. hashmap_entry_init(util, strhash(util->diff));
  207. other = hashmap_remove(&map, util, NULL);
  208. if (other) {
  209. if (other->matching >= 0)
  210. BUG("already assigned!");
  211. other->matching = i;
  212. util->matching = other->i;
  213. }
  214. }
  215. hashmap_free(&map, 0);
  216. }
  217. static void diffsize_consume(void *data, char *line, unsigned long len)
  218. {
  219. (*(int *)data)++;
  220. }
  221. static void diffsize_hunk(void *data, long ob, long on, long nb, long nn,
  222. const char *funcline, long funclen)
  223. {
  224. diffsize_consume(data, NULL, 0);
  225. }
  226. static int diffsize(const char *a, const char *b)
  227. {
  228. xpparam_t pp = { 0 };
  229. xdemitconf_t cfg = { 0 };
  230. mmfile_t mf1, mf2;
  231. int count = 0;
  232. mf1.ptr = (char *)a;
  233. mf1.size = strlen(a);
  234. mf2.ptr = (char *)b;
  235. mf2.size = strlen(b);
  236. cfg.ctxlen = 3;
  237. if (!xdi_diff_outf(&mf1, &mf2,
  238. diffsize_hunk, diffsize_consume, &count,
  239. &pp, &cfg))
  240. return count;
  241. error(_("failed to generate diff"));
  242. return COST_MAX;
  243. }
  244. static void get_correspondences(struct string_list *a, struct string_list *b,
  245. int creation_factor)
  246. {
  247. int n = a->nr + b->nr;
  248. int *cost, c, *a2b, *b2a;
  249. int i, j;
  250. ALLOC_ARRAY(cost, st_mult(n, n));
  251. ALLOC_ARRAY(a2b, n);
  252. ALLOC_ARRAY(b2a, n);
  253. for (i = 0; i < a->nr; i++) {
  254. struct patch_util *a_util = a->items[i].util;
  255. for (j = 0; j < b->nr; j++) {
  256. struct patch_util *b_util = b->items[j].util;
  257. if (a_util->matching == j)
  258. c = 0;
  259. else if (a_util->matching < 0 && b_util->matching < 0)
  260. c = diffsize(a_util->diff, b_util->diff);
  261. else
  262. c = COST_MAX;
  263. cost[i + n * j] = c;
  264. }
  265. c = a_util->matching < 0 ?
  266. a_util->diffsize * creation_factor / 100 : COST_MAX;
  267. for (j = b->nr; j < n; j++)
  268. cost[i + n * j] = c;
  269. }
  270. for (j = 0; j < b->nr; j++) {
  271. struct patch_util *util = b->items[j].util;
  272. c = util->matching < 0 ?
  273. util->diffsize * creation_factor / 100 : COST_MAX;
  274. for (i = a->nr; i < n; i++)
  275. cost[i + n * j] = c;
  276. }
  277. for (i = a->nr; i < n; i++)
  278. for (j = b->nr; j < n; j++)
  279. cost[i + n * j] = 0;
  280. compute_assignment(n, n, cost, a2b, b2a);
  281. for (i = 0; i < a->nr; i++)
  282. if (a2b[i] >= 0 && a2b[i] < b->nr) {
  283. struct patch_util *a_util = a->items[i].util;
  284. struct patch_util *b_util = b->items[a2b[i]].util;
  285. a_util->matching = a2b[i];
  286. b_util->matching = i;
  287. }
  288. free(cost);
  289. free(a2b);
  290. free(b2a);
  291. }
  292. static void output_pair_header(struct diff_options *diffopt,
  293. int patch_no_width,
  294. struct strbuf *buf,
  295. struct strbuf *dashes,
  296. struct patch_util *a_util,
  297. struct patch_util *b_util)
  298. {
  299. struct object_id *oid = a_util ? &a_util->oid : &b_util->oid;
  300. struct commit *commit;
  301. char status;
  302. const char *color_reset = diff_get_color_opt(diffopt, DIFF_RESET);
  303. const char *color_old = diff_get_color_opt(diffopt, DIFF_FILE_OLD);
  304. const char *color_new = diff_get_color_opt(diffopt, DIFF_FILE_NEW);
  305. const char *color_commit = diff_get_color_opt(diffopt, DIFF_COMMIT);
  306. const char *color;
  307. if (!dashes->len)
  308. strbuf_addchars(dashes, '-',
  309. strlen(find_unique_abbrev(oid,
  310. DEFAULT_ABBREV)));
  311. if (!b_util) {
  312. color = color_old;
  313. status = '<';
  314. } else if (!a_util) {
  315. color = color_new;
  316. status = '>';
  317. } else if (strcmp(a_util->patch, b_util->patch)) {
  318. color = color_commit;
  319. status = '!';
  320. } else {
  321. color = color_commit;
  322. status = '=';
  323. }
  324. strbuf_reset(buf);
  325. strbuf_addstr(buf, status == '!' ? color_old : color);
  326. if (!a_util)
  327. strbuf_addf(buf, "%*s: %s ", patch_no_width, "-", dashes->buf);
  328. else
  329. strbuf_addf(buf, "%*d: %s ", patch_no_width, a_util->i + 1,
  330. find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV));
  331. if (status == '!')
  332. strbuf_addf(buf, "%s%s", color_reset, color);
  333. strbuf_addch(buf, status);
  334. if (status == '!')
  335. strbuf_addf(buf, "%s%s", color_reset, color_new);
  336. if (!b_util)
  337. strbuf_addf(buf, " %*s: %s", patch_no_width, "-", dashes->buf);
  338. else
  339. strbuf_addf(buf, " %*d: %s", patch_no_width, b_util->i + 1,
  340. find_unique_abbrev(&b_util->oid, DEFAULT_ABBREV));
  341. commit = lookup_commit_reference(the_repository, oid);
  342. if (commit) {
  343. if (status == '!')
  344. strbuf_addf(buf, "%s%s", color_reset, color);
  345. strbuf_addch(buf, ' ');
  346. pp_commit_easy(CMIT_FMT_ONELINE, commit, buf);
  347. }
  348. strbuf_addf(buf, "%s\n", color_reset);
  349. fwrite(buf->buf, buf->len, 1, diffopt->file);
  350. }
  351. static struct userdiff_driver section_headers = {
  352. .funcname = { "^ ## (.*) ##$\n"
  353. "^.?@@ (.*)$", REG_EXTENDED }
  354. };
  355. static struct diff_filespec *get_filespec(const char *name, const char *p)
  356. {
  357. struct diff_filespec *spec = alloc_filespec(name);
  358. fill_filespec(spec, &null_oid, 0, 0100644);
  359. spec->data = (char *)p;
  360. spec->size = strlen(p);
  361. spec->should_munmap = 0;
  362. spec->is_stdin = 1;
  363. spec->driver = &section_headers;
  364. return spec;
  365. }
  366. static void patch_diff(const char *a, const char *b,
  367. struct diff_options *diffopt)
  368. {
  369. diff_queue(&diff_queued_diff,
  370. get_filespec("a", a), get_filespec("b", b));
  371. diffcore_std(diffopt);
  372. diff_flush(diffopt);
  373. }
  374. static void output(struct string_list *a, struct string_list *b,
  375. struct diff_options *diffopt)
  376. {
  377. struct strbuf buf = STRBUF_INIT, dashes = STRBUF_INIT;
  378. int patch_no_width = decimal_width(1 + (a->nr > b->nr ? a->nr : b->nr));
  379. int i = 0, j = 0;
  380. /*
  381. * We assume the user is really more interested in the second argument
  382. * ("newer" version). To that end, we print the output in the order of
  383. * the RHS (the `b` parameter). To put the LHS (the `a` parameter)
  384. * commits that are no longer in the RHS into a good place, we place
  385. * them once we have shown all of their predecessors in the LHS.
  386. */
  387. while (i < a->nr || j < b->nr) {
  388. struct patch_util *a_util, *b_util;
  389. a_util = i < a->nr ? a->items[i].util : NULL;
  390. b_util = j < b->nr ? b->items[j].util : NULL;
  391. /* Skip all the already-shown commits from the LHS. */
  392. while (i < a->nr && a_util->shown)
  393. a_util = ++i < a->nr ? a->items[i].util : NULL;
  394. /* Show unmatched LHS commit whose predecessors were shown. */
  395. if (i < a->nr && a_util->matching < 0) {
  396. output_pair_header(diffopt, patch_no_width,
  397. &buf, &dashes, a_util, NULL);
  398. i++;
  399. continue;
  400. }
  401. /* Show unmatched RHS commits. */
  402. while (j < b->nr && b_util->matching < 0) {
  403. output_pair_header(diffopt, patch_no_width,
  404. &buf, &dashes, NULL, b_util);
  405. b_util = ++j < b->nr ? b->items[j].util : NULL;
  406. }
  407. /* Show matching LHS/RHS pair. */
  408. if (j < b->nr) {
  409. a_util = a->items[b_util->matching].util;
  410. output_pair_header(diffopt, patch_no_width,
  411. &buf, &dashes, a_util, b_util);
  412. if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT))
  413. patch_diff(a->items[b_util->matching].string,
  414. b->items[j].string, diffopt);
  415. a_util->shown = 1;
  416. j++;
  417. }
  418. }
  419. strbuf_release(&buf);
  420. strbuf_release(&dashes);
  421. }
  422. static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
  423. {
  424. return data;
  425. }
  426. int show_range_diff(const char *range1, const char *range2,
  427. int creation_factor, int dual_color,
  428. struct diff_options *diffopt)
  429. {
  430. int res = 0;
  431. struct string_list branch1 = STRING_LIST_INIT_DUP;
  432. struct string_list branch2 = STRING_LIST_INIT_DUP;
  433. if (read_patches(range1, &branch1))
  434. res = error(_("could not parse log for '%s'"), range1);
  435. if (!res && read_patches(range2, &branch2))
  436. res = error(_("could not parse log for '%s'"), range2);
  437. if (!res) {
  438. struct diff_options opts;
  439. struct strbuf indent = STRBUF_INIT;
  440. if (diffopt)
  441. memcpy(&opts, diffopt, sizeof(opts));
  442. else
  443. diff_setup(&opts);
  444. if (!opts.output_format)
  445. opts.output_format = DIFF_FORMAT_PATCH;
  446. opts.flags.suppress_diff_headers = 1;
  447. opts.flags.dual_color_diffed_diffs = dual_color;
  448. opts.flags.suppress_hunk_header_line_count = 1;
  449. opts.output_prefix = output_prefix_cb;
  450. strbuf_addstr(&indent, " ");
  451. opts.output_prefix_data = &indent;
  452. diff_setup_done(&opts);
  453. find_exact_matches(&branch1, &branch2);
  454. get_correspondences(&branch1, &branch2, creation_factor);
  455. output(&branch1, &branch2, &opts);
  456. strbuf_release(&indent);
  457. }
  458. string_list_clear(&branch1, 1);
  459. string_list_clear(&branch2, 1);
  460. return res;
  461. }