]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/diff.c
The sixth batch
[thirdparty/git.git] / builtin / diff.c
CommitLineData
65056021
JH
1/*
2 * Builtin "git diff"
3 *
4 * Copyright (c) 2006 Junio C Hamano
5 */
41f43b82 6
03eae9af 7#define USE_THE_REPOSITORY_VARIABLE
41f43b82
PS
8#define DISABLE_SIGN_COMPARE_WARNINGS
9
bc5c5ec0 10#include "builtin.h"
b2141fc1 11#include "config.h"
8bfcb3a6 12#include "ewah/ewok.h"
697cc8ef 13#include "lockfile.h"
6b2f2d98 14#include "color.h"
65056021 15#include "commit.h"
f394e093 16#include "gettext.h"
65056021
JH
17#include "tag.h"
18#include "diff.h"
3b6c17b5 19#include "diff-merges.h"
65056021 20#include "diffcore.h"
fbffdfb1 21#include "preload-index.h"
08c46a49 22#include "read-cache-ll.h"
65056021
JH
23#include "revision.h"
24#include "log-tree.h"
e38da487 25#include "setup.h"
fe299ec5 26#include "oid-array.h"
d4a4f929 27#include "tree.h"
65056021 28
470faf96
TG
29#define DIFF_NO_INDEX_EXPLICIT 1
30#define DIFF_NO_INDEX_IMPLICIT 2
31
65056021 32static const char builtin_diff_usage[] =
b7e10b2c 33"git diff [<options>] [<commit>] [--] [<path>...]\n"
eb448631
DL
34" or: git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]\n"
35" or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n"
74f3390d
SY
36" or: git diff [<options>] <commit>...<commit> [--] [<path>...]\n"
37" or: git diff [<options>] <blob> <blob>\n"
09fb155f 38" or: git diff [<options>] --no-index [--] <path> <path> [<pathspec>...]"
acf7828e 39"\n"
b7e10b2c 40COMMON_DIFF_OPTIONS_HELP;
65056021 41
158b06ca
JK
42static const char *blob_path(struct object_array_entry *entry)
43{
44 return entry->path ? entry->path : entry->name;
45}
46
65056021
JH
47static void stuff_change(struct diff_options *opt,
48 unsigned old_mode, unsigned new_mode,
9c4b0f66 49 const struct object_id *old_oid,
50 const struct object_id *new_oid,
51 int old_oid_valid,
52 int new_oid_valid,
d04ec74b
JK
53 const char *old_path,
54 const char *new_path)
65056021
JH
55{
56 struct diff_filespec *one, *two;
57
9c4b0f66 58 if (!is_null_oid(old_oid) && !is_null_oid(new_oid) &&
4a7e27e9 59 oideq(old_oid, new_oid) && (old_mode == new_mode))
65056021
JH
60 return;
61
0d1e0e78 62 if (opt->flags.reverse_diff) {
35d803bc 63 SWAP(old_mode, new_mode);
9c4b0f66 64 SWAP(old_oid, new_oid);
d04ec74b 65 SWAP(old_path, new_path);
65056021 66 }
cd676a51
JH
67
68 if (opt->prefix &&
d04ec74b
JK
69 (strncmp(old_path, opt->prefix, opt->prefix_length) ||
70 strncmp(new_path, opt->prefix, opt->prefix_length)))
cd676a51
JH
71 return;
72
d04ec74b
JK
73 one = alloc_filespec(old_path);
74 two = alloc_filespec(new_path);
f9704c2d
BW
75 fill_filespec(one, old_oid, old_oid_valid, old_mode);
76 fill_filespec(two, new_oid, new_oid_valid, new_mode);
65056021 77
65056021
JH
78 diff_queue(&diff_queued_diff, one, two);
79}
80
c0049ca0
JK
81static void builtin_diff_b_f(struct rev_info *revs,
82 int argc, const char **argv UNUSED,
83 struct object_array_entry **blob)
65056021
JH
84{
85 /* Blob vs file in the working tree*/
86 struct stat st;
887c6c18 87 const char *path;
65056021 88
a610786f
TH
89 if (argc > 1)
90 usage(builtin_diff_usage);
91
887c6c18
NTND
92 GUARD_PATHSPEC(&revs->prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL);
93 path = revs->prune_data.items[0].match;
94
65056021 95 if (lstat(path, &st))
54214529 96 die_errno(_("failed to stat '%s'"), path);
65056021 97 if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
54214529 98 die(_("'%s': not a regular file or symlink"), path);
01618a3a 99
a5a818ee
JH
100 diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
101
42f5ba5b
JK
102 if (blob[0]->mode == S_IFINVALID)
103 blob[0]->mode = canon_mode(st.st_mode);
01618a3a 104
65056021 105 stuff_change(&revs->diffopt,
42f5ba5b 106 blob[0]->mode, canon_mode(st.st_mode),
7d70b29c 107 &blob[0]->item->oid, null_oid(the_hash_algo),
e5450100 108 1, 0,
30d005c0
JK
109 blob[0]->path ? blob[0]->path : path,
110 path);
65056021
JH
111 diffcore_std(&revs->diffopt);
112 diff_flush(&revs->diffopt);
65056021
JH
113}
114
c0049ca0
JK
115static void builtin_diff_blobs(struct rev_info *revs,
116 int argc, const char **argv UNUSED,
117 struct object_array_entry **blob)
65056021 118{
33de80b1 119 const unsigned mode = canon_mode(S_IFREG | 0644);
65056021 120
a610786f
TH
121 if (argc > 1)
122 usage(builtin_diff_usage);
123
42f5ba5b
JK
124 if (blob[0]->mode == S_IFINVALID)
125 blob[0]->mode = mode;
01618a3a 126
42f5ba5b
JK
127 if (blob[1]->mode == S_IFINVALID)
128 blob[1]->mode = mode;
01618a3a 129
65056021 130 stuff_change(&revs->diffopt,
42f5ba5b
JK
131 blob[0]->mode, blob[1]->mode,
132 &blob[0]->item->oid, &blob[1]->item->oid,
e5450100 133 1, 1,
158b06ca 134 blob_path(blob[0]), blob_path(blob[1]));
65056021
JH
135 diffcore_std(&revs->diffopt);
136 diff_flush(&revs->diffopt);
65056021
JH
137}
138
c0049ca0
JK
139static void builtin_diff_index(struct rev_info *revs,
140 int argc, const char **argv)
65056021 141{
4c3fe82e 142 unsigned int option = 0;
65056021
JH
143 while (1 < argc) {
144 const char *arg = argv[1];
2baf1850 145 if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
4c3fe82e 146 option |= DIFF_INDEX_CACHED;
0f5a1d44
DL
147 else if (!strcmp(arg, "--merge-base"))
148 option |= DIFF_INDEX_MERGE_BASE;
65056021
JH
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 */
1f1e895f 157 if (revs->pending.nr != 1 ||
65056021
JH
158 revs->max_count != -1 || revs->min_age != -1 ||
159 revs->max_age != -1)
160 usage(builtin_diff_usage);
4c3fe82e 161 if (!(option & DIFF_INDEX_CACHED)) {
7349afd2 162 setup_work_tree();
07047d68
ÆAB
163 if (repo_read_index_preload(the_repository,
164 &revs->diffopt.pathspec, 0) < 0) {
3755077b 165 die_errno("repo_read_index_preload");
7349afd2 166 }
07047d68 167 } else if (repo_read_index(the_repository) < 0) {
3755077b 168 die_errno("repo_read_cache");
b4e1e4a7 169 }
25bd3acd 170 run_diff_index(revs, option);
65056021
JH
171}
172
c0049ca0
JK
173static 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)
65056021 177{
9c4b0f66 178 const struct object_id *(oid[2]);
3d09c228
DL
179 struct object_id mb_oid;
180 int merge_base = 0;
a610786f 181
3d09c228
DL
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 }
0fe7c1de 190
3d09c228
DL
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 }
66f414f8 207 diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
65056021 208 log_tree_diff_flush(revs);
65056021
JH
209}
210
c0049ca0
JK
211static 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)
0fe7c1de 215{
910650d2 216 struct oid_array parents = OID_ARRAY_INIT;
0fe7c1de
JH
217 int i;
218
a610786f
TH
219 if (argc > 1)
220 usage(builtin_diff_usage);
221
a79c6b60
RS
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
3b6c17b5
SO
227 diff_merges_set_dense_combined_if_unset(revs);
228
a79c6b60
RS
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);
910650d2 234 oid_array_clear(&parents);
0fe7c1de
JH
235}
236
aecbf914
JH
237static void refresh_index_quietly(void)
238{
837e34eb 239 struct lock_file lock_file = LOCK_INIT;
aecbf914
JH
240 int fd;
241
07047d68 242 fd = repo_hold_locked_index(the_repository, &lock_file, 0);
aecbf914
JH
243 if (fd < 0)
244 return;
f59aa5e0 245 discard_index(the_repository->index);
07047d68 246 repo_read_index(the_repository);
f59aa5e0 247 refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
07047d68 248 NULL);
1b0d968b 249 repo_update_index_if_able(the_repository, &lock_file);
aecbf914
JH
250}
251
c0049ca0 252static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
0569e9b8 253{
0569e9b8
JH
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;
1c370ea4
MM
265 else if (!strcmp(argv[1], "-h"))
266 usage(builtin_diff_usage);
5ad6e2b4
JK
267 else {
268 error(_("invalid option: %s"), argv[1]);
269 usage(builtin_diff_usage);
270 }
0569e9b8
JH
271 argv++; argc--;
272 }
273
903e09a3
JH
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 */
3b6c17b5 280 if (revs->max_count == -1 &&
0569e9b8 281 (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
3b6c17b5 282 diff_merges_set_dense_combined_if_unset(revs);
0569e9b8 283
4f38f6b5 284 setup_work_tree();
07047d68
ÆAB
285 if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec,
286 0) < 0) {
3755077b 287 die_errno("repo_read_index_preload");
0569e9b8 288 }
25bd3acd 289 run_diff_files(revs, options);
0569e9b8
JH
290}
291
8bfcb3a6
CT
292struct 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 */
317static 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 }
afe8a907 366 if (!map)
8bfcb3a6
CT
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;
8bfcb3a6
CT
386 if (basecount == 0)
387 die(_("%s...%s: no merge base"), sym->left, sym->right);
5f46e610 388 sym->base = rev->pending.objects[basepos].name;
8bfcb3a6
CT
389 bitmap_unset(map, basepos); /* unmark the base we want */
390 sym->warn = basecount > 1;
391 sym->skip = map;
392}
393
77d4b3dd
PS
394static void symdiff_release(struct symdiff *sdiff)
395{
396 bitmap_free(sdiff->skip);
397}
398
9b1cb507
JC
399int cmd_diff(int argc,
400 const char **argv,
401 const char *prefix,
402 struct repository *repo UNUSED)
65056021 403{
1f1e895f 404 int i;
65056021 405 struct rev_info rev;
33055fa8 406 struct object_array ent = OBJECT_ARRAY_INIT;
a79c6b60 407 int first_non_parent = -1;
33055fa8 408 int blobs = 0, paths = 0;
42f5ba5b 409 struct object_array_entry *blob[2];
470faf96 410 int nongit = 0, no_index = 0;
c0049ca0 411 int result;
8bfcb3a6 412 struct symdiff sdiff;
65056021
JH
413
414 /*
415 * We could get N tree-ish in the rev.pending_objects list.
a9d7689c
DL
416 * Also there could be M blobs there, and P pathspecs. --cached may
417 * also be present.
65056021
JH
418 *
419 * N=0, M=0:
a9d7689c
DL
420 * cache vs files (diff-files)
421 *
422 * N=0, M=0, --cached:
423 * HEAD vs cache (diff-index --cached)
424 *
65056021
JH
425 * N=0, M=2:
426 * compare two random blobs. P must be zero.
a9d7689c 427 *
65056021 428 * N=0, M=1, P=1:
a9d7689c 429 * compare a blob with a working tree file.
65056021
JH
430 *
431 * N=1, M=0:
a9d7689c 432 * tree vs files (diff-index)
65056021 433 *
a9d7689c 434 * N=1, M=0, --cached:
65056021
JH
435 * tree vs cache (diff-index --cached)
436 *
437 * N=2, M=0:
438 * tree vs tree (diff-tree)
439 *
0569e9b8
JH
440 * N=0, M=0, P=2:
441 * compare two filesystem entities (aka --no-index).
442 *
65056021
JH
443 * Other cases are errors.
444 */
230f544e 445
470faf96
TG
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
28a4e580 458 prefix = setup_git_directory_gently(&nongit);
6df5762d 459
51ba65b5
LD
460 if (!nongit) {
461 prepare_repo_settings(the_repository);
462 the_repository->settings.command_requires_full_index = 0;
463 }
464
28a4e580 465 if (!no_index) {
475b362c
JK
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 }
470faf96 478
ab274909
PS
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
5404c116 488 init_diff_ui_defaults();
ef90d6d4 489 git_config(git_diff_ui_config, NULL);
5c327502 490 prefix = precompose_argv_prefix(argc, argv, prefix);
6b2f2d98 491
2abf3503 492 repo_init_revisions(the_repository, &rev, prefix);
0569e9b8 493
287ab28b 494 /* Set up defaults that will apply to both no-index and regular diffs. */
4ca7a3fd 495 init_diffstat_widths(&rev.diffopt);
0d1e0e78
BW
496 rev.diffopt.flags.allow_external = 1;
497 rev.diffopt.flags.allow_textconv = 1;
61af494c 498
287ab28b
JK
499 /* If this is a no-index diff, just run it and exit there. */
500 if (no_index)
7d70b29c
PS
501 exit(diff_no_index(&rev, the_repository->hash_algo,
502 no_index == DIFF_NO_INDEX_IMPLICIT,
dcd6a8c0
JH
503 argc, argv));
504
287ab28b
JK
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
0231ae71
NTND
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
0569e9b8 519 if (nongit)
54214529 520 die(_("Not a git repository"));
0569e9b8 521 argc = setup_revisions(argc, argv, &rev, NULL);
047fbe90 522 if (!rev.diffopt.output_format) {
c9b5ef99 523 rev.diffopt.output_format = DIFF_FORMAT_PATCH;
28452655 524 diff_setup_done(&rev.diffopt);
047fbe90 525 }
61af494c 526
0d1e0e78 527 rev.diffopt.flags.recursive = 1;
1eb4136a 528 rev.diffopt.rotate_to_strict = 1;
c9b5ef99 529
1af3d977 530 setup_diff_pager(&rev.diffopt);
89d07f75 531
0569e9b8
JH
532 /*
533 * Do we have --cached and not have a pending object, then
65056021
JH
534 * default to HEAD by hand. Eek.
535 */
1f1e895f 536 if (!rev.pending.nr) {
65056021
JH
537 int i;
538 for (i = 1; i < argc; i++) {
539 const char *arg = argv[i];
540 if (!strcmp(arg, "--"))
541 break;
2baf1850
DS
542 else if (!strcmp(arg, "--cached") ||
543 !strcmp(arg, "--staged")) {
3384a2df 544 add_head_to_pending(&rev);
a2b7a3b3
NTND
545 if (!rev.pending.nr) {
546 struct tree *tree;
f86bcc7b
SB
547 tree = lookup_tree(the_repository,
548 the_repository->hash_algo->empty_tree);
a2b7a3b3
NTND
549 add_pending_object(&rev, &tree->object, "HEAD");
550 }
65056021
JH
551 break;
552 }
553 }
554 }
555
8bfcb3a6 556 symdiff_prepare(&rev, &sdiff);
1f1e895f 557 for (i = 0; i < rev.pending.nr; i++) {
026f09e7
MH
558 struct object_array_entry *entry = &rev.pending.objects[i];
559 struct object *obj = entry->item;
560 const char *name = entry->name;
65056021
JH
561 int flags = (obj->flags & UNINTERESTING);
562 if (!obj->parsed)
109cd76d 563 obj = parse_object(the_repository, &obj->oid);
a74093da 564 obj = deref_tag(the_repository, obj, NULL, 0);
65056021 565 if (!obj)
54214529 566 die(_("invalid object '%s' given."), name);
1974632c 567 if (obj->type == OBJ_COMMIT)
ecb5091f
ÆAB
568 obj = &repo_get_commit_tree(the_repository,
569 ((struct commit *)obj))->object;
5b1e14ea 570
1974632c 571 if (obj->type == OBJ_TREE) {
8bfcb3a6
CT
572 if (sdiff.skip && bitmap_get(sdiff.skip, i))
573 continue;
65056021 574 obj->flags |= flags;
33055fa8 575 add_object_array(obj, name, &ent);
a79c6b60
RS
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;
5b1e14ea 580 } else if (obj->type == OBJ_BLOB) {
65056021 581 if (2 <= blobs)
54214529 582 die(_("more than two blobs given: '%s'"), name);
42f5ba5b 583 blob[blobs] = entry;
65056021 584 blobs++;
230f544e 585
5b1e14ea
MH
586 } else {
587 die(_("unhandled object '%s' given."), name);
65056021 588 }
65056021 589 }
887c6c18 590 if (rev.prune_data.nr)
afe069d1 591 paths += rev.prune_data.nr;
65056021
JH
592
593 /*
594 * Now, do the arguments look reasonable?
595 */
33055fa8 596 if (!ent.nr) {
65056021
JH
597 switch (blobs) {
598 case 0:
c0049ca0 599 builtin_diff_files(&rev, argc, argv);
65056021
JH
600 break;
601 case 1:
602 if (paths != 1)
603 usage(builtin_diff_usage);
c0049ca0 604 builtin_diff_b_f(&rev, argc, argv, blob);
65056021
JH
605 break;
606 case 2:
0fe7c1de
JH
607 if (paths)
608 usage(builtin_diff_usage);
c0049ca0 609 builtin_diff_blobs(&rev, argc, argv, blob);
65056021
JH
610 break;
611 default:
612 usage(builtin_diff_usage);
613 }
614 }
615 else if (blobs)
616 usage(builtin_diff_usage);
33055fa8 617 else if (ent.nr == 1)
c0049ca0 618 builtin_diff_index(&rev, argc, argv);
8bfcb3a6
CT
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);
c0049ca0
JK
623 builtin_diff_tree(&rev, argc, argv,
624 &ent.objects[0], &ent.objects[1]);
c008c0ff 625 } else
c0049ca0
JK
626 builtin_diff_combined(&rev, argc, argv,
627 ent.objects, ent.nr,
628 first_non_parent);
4460e052 629 result = diff_result_code(&rev);
aecbf914
JH
630 if (1 < rev.diffopt.skip_stat_unmatch)
631 refresh_index_quietly();
bf1b32d0 632 release_revisions(&rev);
ac95f5d3 633 object_array_clear(&ent);
77d4b3dd 634 symdiff_release(&sdiff);
41bbf9d5 635 return result;
65056021 636}