]> git.ipfire.org Git - thirdparty/git.git/blob - builtin/diff.c
c6231edce4e8225108a0a1d6fac19ec463bad419
[thirdparty/git.git] / builtin / diff.c
1 /*
2 * Builtin "git diff"
3 *
4 * Copyright (c) 2006 Junio C Hamano
5 */
6
7 #define USE_THE_REPOSITORY_VARIABLE
8 #define DISABLE_SIGN_COMPARE_WARNINGS
9
10 #include "builtin.h"
11 #include "config.h"
12 #include "ewah/ewok.h"
13 #include "lockfile.h"
14 #include "color.h"
15 #include "commit.h"
16 #include "gettext.h"
17 #include "tag.h"
18 #include "diff.h"
19 #include "diff-merges.h"
20 #include "diffcore.h"
21 #include "preload-index.h"
22 #include "read-cache-ll.h"
23 #include "revision.h"
24 #include "log-tree.h"
25 #include "setup.h"
26 #include "oid-array.h"
27 #include "tree.h"
28
29 #define DIFF_NO_INDEX_EXPLICIT 1
30 #define DIFF_NO_INDEX_IMPLICIT 2
31
32 static const char builtin_diff_usage[] =
33 "git diff [<options>] [<commit>] [--] [<path>...]\n"
34 " or: git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]\n"
35 " or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n"
36 " or: git diff [<options>] <commit>...<commit> [--] [<path>...]\n"
37 " or: git diff [<options>] <blob> <blob>\n"
38 " or: git diff [<options>] --no-index [--] <path> <path> [<pathspec>...]"
39 "\n"
40 COMMON_DIFF_OPTIONS_HELP;
41
42 static const char *blob_path(struct object_array_entry *entry)
43 {
44 return entry->path ? entry->path : entry->name;
45 }
46
47 static void stuff_change(struct diff_options *opt,
48 unsigned old_mode, unsigned new_mode,
49 const struct object_id *old_oid,
50 const struct object_id *new_oid,
51 int old_oid_valid,
52 int new_oid_valid,
53 const char *old_path,
54 const char *new_path)
55 {
56 struct diff_filespec *one, *two;
57
58 if (!is_null_oid(old_oid) && !is_null_oid(new_oid) &&
59 oideq(old_oid, new_oid) && (old_mode == new_mode))
60 return;
61
62 if (opt->flags.reverse_diff) {
63 SWAP(old_mode, new_mode);
64 SWAP(old_oid, new_oid);
65 SWAP(old_path, new_path);
66 }
67
68 if (opt->prefix &&
69 (strncmp(old_path, opt->prefix, opt->prefix_length) ||
70 strncmp(new_path, opt->prefix, opt->prefix_length)))
71 return;
72
73 one = alloc_filespec(old_path);
74 two = alloc_filespec(new_path);
75 fill_filespec(one, old_oid, old_oid_valid, old_mode);
76 fill_filespec(two, new_oid, new_oid_valid, new_mode);
77
78 diff_queue(&diff_queued_diff, one, two);
79 }
80
81 static void builtin_diff_b_f(struct rev_info *revs,
82 int argc, const char **argv UNUSED,
83 struct object_array_entry **blob)
84 {
85 /* Blob vs file in the working tree*/
86 struct stat st;
87 const char *path;
88
89 if (argc > 1)
90 usage(builtin_diff_usage);
91
92 GUARD_PATHSPEC(&revs->prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
93 path = revs->prune_data.items[0].match;
94
95 if (lstat(path, &st))
96 die_errno(_("failed to stat '%s'"), path);
97 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
98 die(_("'%s': not a regular file or symlink"), path);
99
100 diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
101
102 if (blob[0]->mode == S_IFINVALID)
103 blob[0]->mode = canon_mode(st.st_mode);
104
105 stuff_change(&revs->diffopt,
106 blob[0]->mode, canon_mode(st.st_mode),
107 &blob[0]->item->oid, null_oid(the_hash_algo),
108 1, 0,
109 blob[0]->path ? blob[0]->path : path,
110 path);
111 diffcore_std(&revs->diffopt);
112 diff_flush(&revs->diffopt);
113 }
114
115 static void builtin_diff_blobs(struct rev_info *revs,
116 int argc, const char **argv UNUSED,
117 struct object_array_entry **blob)
118 {
119 const unsigned mode = canon_mode(S_IFREG | 0644);
120
121 if (argc > 1)
122 usage(builtin_diff_usage);
123
124 if (blob[0]->mode == S_IFINVALID)
125 blob[0]->mode = mode;
126
127 if (blob[1]->mode == S_IFINVALID)
128 blob[1]->mode = mode;
129
130 stuff_change(&revs->diffopt,
131 blob[0]->mode, blob[1]->mode,
132 &blob[0]->item->oid, &blob[1]->item->oid,
133 1, 1,
134 blob_path(blob[0]), blob_path(blob[1]));
135 diffcore_std(&revs->diffopt);
136 diff_flush(&revs->diffopt);
137 }
138
139 static void builtin_diff_index(struct rev_info *revs,
140 int argc, const char **argv)
141 {
142 unsigned int option = 0;
143 while (1 < argc) {
144 const char *arg = argv[1];
145 if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
146 option |= DIFF_INDEX_CACHED;
147 else if (!strcmp(arg, "--merge-base"))
148 option |= DIFF_INDEX_MERGE_BASE;
149 else
150 usage(builtin_diff_usage);
151 argv++; argc--;
152 }
153 /*
154 * Make sure there is one revision (i.e. pending object),
155 * and there is no revision filtering parameters.
156 */
157 if (revs->pending.nr != 1 ||
158 revs->max_count != -1 || revs->min_age != -1 ||
159 revs->max_age != -1)
160 usage(builtin_diff_usage);
161 if (!(option & DIFF_INDEX_CACHED)) {
162 setup_work_tree();
163 if (repo_read_index_preload(the_repository,
164 &revs->diffopt.pathspec, 0) < 0) {
165 die_errno("repo_read_index_preload");
166 }
167 } else if (repo_read_index(the_repository) < 0) {
168 die_errno("repo_read_cache");
169 }
170 run_diff_index(revs, option);
171 }
172
173 static void builtin_diff_tree(struct rev_info *revs,
174 int argc, const char **argv,
175 struct object_array_entry *ent0,
176 struct object_array_entry *ent1)
177 {
178 const struct object_id *(oid[2]);
179 struct object_id mb_oid;
180 int merge_base = 0;
181
182 while (1 < argc) {
183 const char *arg = argv[1];
184 if (!strcmp(arg, "--merge-base"))
185 merge_base = 1;
186 else
187 usage(builtin_diff_usage);
188 argv++; argc--;
189 }
190
191 if (merge_base) {
192 diff_get_merge_base(revs, &mb_oid);
193 oid[0] = &mb_oid;
194 oid[1] = &revs->pending.objects[1].item->oid;
195 } else {
196 int swap = 0;
197
198 /*
199 * We saw two trees, ent0 and ent1. If ent1 is uninteresting,
200 * swap them.
201 */
202 if (ent1->item->flags & UNINTERESTING)
203 swap = 1;
204 oid[swap] = &ent0->item->oid;
205 oid[1 - swap] = &ent1->item->oid;
206 }
207 diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
208 log_tree_diff_flush(revs);
209 }
210
211 static void builtin_diff_combined(struct rev_info *revs,
212 int argc, const char **argv UNUSED,
213 struct object_array_entry *ent,
214 int ents, int first_non_parent)
215 {
216 struct oid_array parents = OID_ARRAY_INIT;
217 int i;
218
219 if (argc > 1)
220 usage(builtin_diff_usage);
221
222 if (first_non_parent < 0)
223 die(_("no merge given, only parents."));
224 if (first_non_parent >= ents)
225 BUG("first_non_parent out of range: %d", first_non_parent);
226
227 diff_merges_set_dense_combined_if_unset(revs);
228
229 for (i = 0; i < ents; i++) {
230 if (i != first_non_parent)
231 oid_array_append(&parents, &ent[i].item->oid);
232 }
233 diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs);
234 oid_array_clear(&parents);
235 }
236
237 static void refresh_index_quietly(void)
238 {
239 struct lock_file lock_file = LOCK_INIT;
240 int fd;
241
242 fd = repo_hold_locked_index(the_repository, &lock_file, 0);
243 if (fd < 0)
244 return;
245 discard_index(the_repository->index);
246 repo_read_index(the_repository);
247 refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
248 NULL);
249 repo_update_index_if_able(the_repository, &lock_file);
250 }
251
252 static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
253 {
254 unsigned int options = 0;
255
256 while (1 < argc && argv[1][0] == '-') {
257 if (!strcmp(argv[1], "--base"))
258 revs->max_count = 1;
259 else if (!strcmp(argv[1], "--ours"))
260 revs->max_count = 2;
261 else if (!strcmp(argv[1], "--theirs"))
262 revs->max_count = 3;
263 else if (!strcmp(argv[1], "-q"))
264 options |= DIFF_SILENT_ON_REMOVED;
265 else if (!strcmp(argv[1], "-h"))
266 usage(builtin_diff_usage);
267 else {
268 error(_("invalid option: %s"), argv[1]);
269 usage(builtin_diff_usage);
270 }
271 argv++; argc--;
272 }
273
274 /*
275 * "diff --base" should not combine merges because it was not
276 * asked to. "diff -c" should not densify (if the user wants
277 * dense one, --cc can be explicitly asked for, or just rely
278 * on the default).
279 */
280 if (revs->max_count == -1 &&
281 (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
282 diff_merges_set_dense_combined_if_unset(revs);
283
284 setup_work_tree();
285 if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
286 0) < 0) {
287 die_errno("repo_read_index_preload");
288 }
289 run_diff_files(revs, options);
290 }
291
292 struct symdiff {
293 struct bitmap *skip;
294 int warn;
295 const char *base, *left, *right;
296 };
297
298 /*
299 * Check for symmetric-difference arguments, and if present, arrange
300 * everything we need to know to handle them correctly. As a bonus,
301 * weed out all bogus range-based revision specifications, e.g.,
302 * "git diff A..B C..D" or "git diff A..B C" get rejected.
303 *
304 * For an actual symmetric diff, *symdiff is set this way:
305 *
306 * - its skip is non-NULL and marks *all* rev->pending.objects[i]
307 * indices that the caller should ignore (extra merge bases, of
308 * which there might be many, and A in A...B). Note that the
309 * chosen merge base and right side are NOT marked.
310 * - warn is set if there are multiple merge bases.
311 * - base, left, and right point to the names to use in a
312 * warning about multiple merge bases.
313 *
314 * If there is no symmetric diff argument, sym->skip is NULL and
315 * sym->warn is cleared. The remaining fields are not set.
316 */
317 static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)
318 {
319 int i, is_symdiff = 0, basecount = 0, othercount = 0;
320 int lpos = -1, rpos = -1, basepos = -1;
321 struct bitmap *map = NULL;
322
323 /*
324 * Use the whence fields to find merge bases and left and
325 * right parts of symmetric difference, so that we do not
326 * depend on the order that revisions are parsed. If there
327 * are any revs that aren't from these sources, we have a
328 * "git diff C A...B" or "git diff A...B C" case. Or we
329 * could even get "git diff A...B C...E", for instance.
330 *
331 * If we don't have just one merge base, we pick one
332 * at random.
333 *
334 * NB: REV_CMD_LEFT, REV_CMD_RIGHT are also used for A..B,
335 * so we must check for SYMMETRIC_LEFT too. The two arrays
336 * rev->pending.objects and rev->cmdline.rev are parallel.
337 */
338 for (i = 0; i < rev->cmdline.nr; i++) {
339 struct object *obj = rev->pending.objects[i].item;
340 switch (rev->cmdline.rev[i].whence) {
341 case REV_CMD_MERGE_BASE:
342 if (basepos < 0)
343 basepos = i;
344 basecount++;
345 break; /* do mark all bases */
346 case REV_CMD_LEFT:
347 if (lpos >= 0)
348 usage(builtin_diff_usage);
349 lpos = i;
350 if (obj->flags & SYMMETRIC_LEFT) {
351 is_symdiff = 1;
352 break; /* do mark A */
353 }
354 continue;
355 case REV_CMD_RIGHT:
356 if (rpos >= 0)
357 usage(builtin_diff_usage);
358 rpos = i;
359 continue; /* don't mark B */
360 case REV_CMD_PARENTS_ONLY:
361 case REV_CMD_REF:
362 case REV_CMD_REV:
363 othercount++;
364 continue;
365 }
366 if (!map)
367 map = bitmap_new();
368 bitmap_set(map, i);
369 }
370
371 /*
372 * Forbid any additional revs for both A...B and A..B.
373 */
374 if (lpos >= 0 && othercount > 0)
375 usage(builtin_diff_usage);
376
377 if (!is_symdiff) {
378 bitmap_free(map);
379 sym->warn = 0;
380 sym->skip = NULL;
381 return;
382 }
383
384 sym->left = rev->pending.objects[lpos].name;
385 sym->right = rev->pending.objects[rpos].name;
386 if (basecount == 0)
387 die(_("%s...%s: no merge base"), sym->left, sym->right);
388 sym->base = rev->pending.objects[basepos].name;
389 bitmap_unset(map, basepos); /* unmark the base we want */
390 sym->warn = basecount > 1;
391 sym->skip = map;
392 }
393
394 static void symdiff_release(struct symdiff *sdiff)
395 {
396 bitmap_free(sdiff->skip);
397 }
398
399 int cmd_diff(int argc,
400 const char **argv,
401 const char *prefix,
402 struct repository *repo UNUSED)
403 {
404 int i;
405 struct rev_info rev;
406 struct object_array ent = OBJECT_ARRAY_INIT;
407 int first_non_parent = -1;
408 int blobs = 0, paths = 0;
409 struct object_array_entry *blob[2];
410 int nongit = 0, no_index = 0;
411 int result;
412 struct symdiff sdiff;
413
414 /*
415 * We could get N tree-ish in the rev.pending_objects list.
416 * Also there could be M blobs there, and P pathspecs. --cached may
417 * also be present.
418 *
419 * N=0, M=0:
420 * cache vs files (diff-files)
421 *
422 * N=0, M=0, --cached:
423 * HEAD vs cache (diff-index --cached)
424 *
425 * N=0, M=2:
426 * compare two random blobs. P must be zero.
427 *
428 * N=0, M=1, P=1:
429 * compare a blob with a working tree file.
430 *
431 * N=1, M=0:
432 * tree vs files (diff-index)
433 *
434 * N=1, M=0, --cached:
435 * tree vs cache (diff-index --cached)
436 *
437 * N=2, M=0:
438 * tree vs tree (diff-tree)
439 *
440 * N=0, M=0, P=2:
441 * compare two filesystem entities (aka --no-index).
442 *
443 * Other cases are errors.
444 */
445
446 /* Were we asked to do --no-index explicitly? */
447 for (i = 1; i < argc; i++) {
448 if (!strcmp(argv[i], "--")) {
449 i++;
450 break;
451 }
452 if (!strcmp(argv[i], "--no-index"))
453 no_index = DIFF_NO_INDEX_EXPLICIT;
454 if (argv[i][0] != '-')
455 break;
456 }
457
458 prefix = setup_git_directory_gently(&nongit);
459
460 if (!nongit) {
461 prepare_repo_settings(the_repository);
462 the_repository->settings.command_requires_full_index = 0;
463 }
464
465 if (!no_index) {
466 /*
467 * Treat git diff with at least one path outside of the
468 * repo the same as if the command would have been executed
469 * outside of a git repository. In this case it behaves
470 * the same way as "git diff --no-index <a> <b>", which acts
471 * as a colourful "diff" replacement.
472 */
473 if (nongit || ((argc == i + 2) &&
474 (!path_inside_repo(prefix, argv[i]) ||
475 !path_inside_repo(prefix, argv[i + 1]))))
476 no_index = DIFF_NO_INDEX_IMPLICIT;
477 }
478
479 /*
480 * When operating outside of a Git repository we need to have a hash
481 * algorithm at hand so that we can generate the blob hashes. We
482 * default to SHA1 here, but may eventually want to change this to be
483 * configurable via a command line option.
484 */
485 if (nongit)
486 repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
487
488 init_diff_ui_defaults();
489 git_config(git_diff_ui_config, NULL);
490 prefix = precompose_argv_prefix(argc, argv, prefix);
491
492 repo_init_revisions(the_repository, &rev, prefix);
493
494 /* Set up defaults that will apply to both no-index and regular diffs. */
495 init_diffstat_widths(&rev.diffopt);
496 rev.diffopt.flags.allow_external = 1;
497 rev.diffopt.flags.allow_textconv = 1;
498
499 /* If this is a no-index diff, just run it and exit there. */
500 if (no_index)
501 exit(diff_no_index(&rev, the_repository->hash_algo,
502 no_index == DIFF_NO_INDEX_IMPLICIT,
503 argc, argv));
504
505
506 /*
507 * Otherwise, we are doing the usual "git" diff; set up any
508 * further defaults that apply to regular diffs.
509 */
510 rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
511
512 /*
513 * Default to intent-to-add entries invisible in the
514 * index. This makes them show up as new files in diff-files
515 * and not at all in diff-cached.
516 */
517 rev.diffopt.ita_invisible_in_index = 1;
518
519 if (nongit)
520 die(_("Not a git repository"));
521 argc = setup_revisions(argc, argv, &rev, NULL);
522 if (!rev.diffopt.output_format) {
523 rev.diffopt.output_format = DIFF_FORMAT_PATCH;
524 diff_setup_done(&rev.diffopt);
525 }
526
527 rev.diffopt.flags.recursive = 1;
528 rev.diffopt.rotate_to_strict = 1;
529
530 setup_diff_pager(&rev.diffopt);
531
532 /*
533 * Do we have --cached and not have a pending object, then
534 * default to HEAD by hand. Eek.
535 */
536 if (!rev.pending.nr) {
537 int i;
538 for (i = 1; i < argc; i++) {
539 const char *arg = argv[i];
540 if (!strcmp(arg, "--"))
541 break;
542 else if (!strcmp(arg, "--cached") ||
543 !strcmp(arg, "--staged")) {
544 add_head_to_pending(&rev);
545 if (!rev.pending.nr) {
546 struct tree *tree;
547 tree = lookup_tree(the_repository,
548 the_repository->hash_algo->empty_tree);
549 add_pending_object(&rev, &tree->object, "HEAD");
550 }
551 break;
552 }
553 }
554 }
555
556 symdiff_prepare(&rev, &sdiff);
557 for (i = 0; i < rev.pending.nr; i++) {
558 struct object_array_entry *entry = &rev.pending.objects[i];
559 struct object *obj = entry->item;
560 const char *name = entry->name;
561 int flags = (obj->flags & UNINTERESTING);
562 if (!obj->parsed)
563 obj = parse_object(the_repository, &obj->oid);
564 obj = deref_tag(the_repository, obj, NULL, 0);
565 if (!obj)
566 die(_("invalid object '%s' given."), name);
567 if (obj->type == OBJ_COMMIT)
568 obj = &repo_get_commit_tree(the_repository,
569 ((struct commit *)obj))->object;
570
571 if (obj->type == OBJ_TREE) {
572 if (sdiff.skip && bitmap_get(sdiff.skip, i))
573 continue;
574 obj->flags |= flags;
575 add_object_array(obj, name, &ent);
576 if (first_non_parent < 0 &&
577 (i >= rev.cmdline.nr || /* HEAD by hand. */
578 rev.cmdline.rev[i].whence != REV_CMD_PARENTS_ONLY))
579 first_non_parent = ent.nr - 1;
580 } else if (obj->type == OBJ_BLOB) {
581 if (2 <= blobs)
582 die(_("more than two blobs given: '%s'"), name);
583 blob[blobs] = entry;
584 blobs++;
585
586 } else {
587 die(_("unhandled object '%s' given."), name);
588 }
589 }
590 if (rev.prune_data.nr)
591 paths += rev.prune_data.nr;
592
593 /*
594 * Now, do the arguments look reasonable?
595 */
596 if (!ent.nr) {
597 switch (blobs) {
598 case 0:
599 builtin_diff_files(&rev, argc, argv);
600 break;
601 case 1:
602 if (paths != 1)
603 usage(builtin_diff_usage);
604 builtin_diff_b_f(&rev, argc, argv, blob);
605 break;
606 case 2:
607 if (paths)
608 usage(builtin_diff_usage);
609 builtin_diff_blobs(&rev, argc, argv, blob);
610 break;
611 default:
612 usage(builtin_diff_usage);
613 }
614 }
615 else if (blobs)
616 usage(builtin_diff_usage);
617 else if (ent.nr == 1)
618 builtin_diff_index(&rev, argc, argv);
619 else if (ent.nr == 2) {
620 if (sdiff.warn)
621 warning(_("%s...%s: multiple merge bases, using %s"),
622 sdiff.left, sdiff.right, sdiff.base);
623 builtin_diff_tree(&rev, argc, argv,
624 &ent.objects[0], &ent.objects[1]);
625 } else
626 builtin_diff_combined(&rev, argc, argv,
627 ent.objects, ent.nr,
628 first_non_parent);
629 result = diff_result_code(&rev);
630 if (1 < rev.diffopt.skip_stat_unmatch)
631 refresh_index_quietly();
632 release_revisions(&rev);
633 object_array_clear(&ent);
634 symdiff_release(&sdiff);
635 return result;
636 }