1/*2* Builtin "git diff"3*4* Copyright (c) 2006 Junio C Hamano5*/6#include "cache.h"7#include "commit.h"8#include "blob.h"9#include "tag.h"10#include "diff.h"11#include "diffcore.h"12#include "revision.h"13#include "log-tree.h"14#include "builtin.h"1516struct blobinfo {17unsigned char sha1[20];18const char *name;19unsigned mode;20};2122static const char builtin_diff_usage[] =23"git-diff <options> <rev>{0,2} -- <path>*";2425static void stuff_change(struct diff_options *opt,26unsigned old_mode, unsigned new_mode,27const unsigned char *old_sha1,28const unsigned char *new_sha1,29const char *old_name,30const char *new_name)31{32struct diff_filespec *one, *two;3334if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&35!hashcmp(old_sha1, new_sha1) && (old_mode == new_mode))36return;3738if (opt->reverse_diff) {39unsigned tmp;40const unsigned char *tmp_u;41const char *tmp_c;42tmp = old_mode; old_mode = new_mode; new_mode = tmp;43tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;44tmp_c = old_name; old_name = new_name; new_name = tmp_c;45}46one = alloc_filespec(old_name);47two = alloc_filespec(new_name);48fill_filespec(one, old_sha1, old_mode);49fill_filespec(two, new_sha1, new_mode);5051/* NEEDSWORK: shouldn't this part of diffopt??? */52diff_queue(&diff_queued_diff, one, two);53}5455static int builtin_diff_b_f(struct rev_info *revs,56int argc, const char **argv,57struct blobinfo *blob,58const char *path)59{60/* Blob vs file in the working tree*/61struct stat st;6263if (argc > 1)64usage(builtin_diff_usage);6566if (lstat(path, &st))67die("'%s': %s", path, strerror(errno));68if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))69die("'%s': not a regular file or symlink", path);7071if (blob[0].mode == S_IFINVALID)72blob[0].mode = canon_mode(st.st_mode);7374stuff_change(&revs->diffopt,75blob[0].mode, canon_mode(st.st_mode),76blob[0].sha1, null_sha1,77path, path);78diffcore_std(&revs->diffopt);79diff_flush(&revs->diffopt);80return 0;81}8283static int builtin_diff_blobs(struct rev_info *revs,84int argc, const char **argv,85struct blobinfo *blob)86{87unsigned mode = canon_mode(S_IFREG | 0644);8889if (argc > 1)90usage(builtin_diff_usage);9192if (blob[0].mode == S_IFINVALID)93blob[0].mode = mode;9495if (blob[1].mode == S_IFINVALID)96blob[1].mode = mode;9798stuff_change(&revs->diffopt,99blob[0].mode, blob[1].mode,100blob[0].sha1, blob[1].sha1,101blob[0].name, blob[1].name);102diffcore_std(&revs->diffopt);103diff_flush(&revs->diffopt);104return 0;105}106107static int builtin_diff_index(struct rev_info *revs,108int argc, const char **argv)109{110int cached = 0;111while (1 < argc) {112const char *arg = argv[1];113if (!strcmp(arg, "--cached"))114cached = 1;115else116usage(builtin_diff_usage);117argv++; argc--;118}119/*120* Make sure there is one revision (i.e. pending object),121* and there is no revision filtering parameters.122*/123if (revs->pending.nr != 1 ||124revs->max_count != -1 || revs->min_age != -1 ||125revs->max_age != -1)126usage(builtin_diff_usage);127if (read_cache() < 0) {128perror("read_cache");129return -1;130}131return run_diff_index(revs, cached);132}133134static int builtin_diff_tree(struct rev_info *revs,135int argc, const char **argv,136struct object_array_entry *ent)137{138const unsigned char *(sha1[2]);139int swap = 0;140141if (argc > 1)142usage(builtin_diff_usage);143144/* We saw two trees, ent[0] and ent[1].145* if ent[1] is uninteresting, they are swapped146*/147if (ent[1].item->flags & UNINTERESTING)148swap = 1;149sha1[swap] = ent[0].item->sha1;150sha1[1-swap] = ent[1].item->sha1;151diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);152log_tree_diff_flush(revs);153return 0;154}155156static int builtin_diff_combined(struct rev_info *revs,157int argc, const char **argv,158struct object_array_entry *ent,159int ents)160{161const unsigned char (*parent)[20];162int i;163164if (argc > 1)165usage(builtin_diff_usage);166167if (!revs->dense_combined_merges && !revs->combine_merges)168revs->dense_combined_merges = revs->combine_merges = 1;169parent = xmalloc(ents * sizeof(*parent));170/* Again, the revs are all reverse */171for (i = 0; i < ents; i++)172hashcpy((unsigned char *)(parent + i),173ent[ents - 1 - i].item->sha1);174diff_tree_combined(parent[0], parent + 1, ents - 1,175revs->dense_combined_merges, revs);176return 0;177}178179void add_head(struct rev_info *revs)180{181unsigned char sha1[20];182struct object *obj;183if (get_sha1("HEAD", sha1))184return;185obj = parse_object(sha1);186if (!obj)187return;188add_pending_object(revs, obj, "HEAD");189}190191static void refresh_index_quietly(void)192{193struct lock_file *lock_file;194int fd;195196lock_file = xcalloc(1, sizeof(struct lock_file));197fd = hold_locked_index(lock_file, 0);198if (fd < 0)199return;200discard_cache();201read_cache();202refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);203204if (active_cache_changed &&205!write_cache(fd, active_cache, active_nr) && !close(fd))206commit_locked_index(lock_file);207208rollback_lock_file(lock_file);209}210211int cmd_diff(int argc, const char **argv, const char *prefix)212{213int i;214struct rev_info rev;215struct object_array_entry ent[100];216int ents = 0, blobs = 0, paths = 0;217const char *path = NULL;218struct blobinfo blob[2];219int nongit = 0;220int result = 0;221222/*223* We could get N tree-ish in the rev.pending_objects list.224* Also there could be M blobs there, and P pathspecs.225*226* N=0, M=0:227* cache vs files (diff-files)228* N=0, M=2:229* compare two random blobs. P must be zero.230* N=0, M=1, P=1:231* compare a blob with a working tree file.232*233* N=1, M=0:234* tree vs cache (diff-index --cached)235*236* N=2, M=0:237* tree vs tree (diff-tree)238*239* Other cases are errors.240*/241242prefix = setup_git_directory_gently(&nongit);243git_config(git_diff_ui_config);244init_revisions(&rev, prefix);245rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;246247if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix))248argc = 0;249else250argc = setup_revisions(argc, argv, &rev, NULL);251if (!rev.diffopt.output_format) {252rev.diffopt.output_format = DIFF_FORMAT_PATCH;253if (diff_setup_done(&rev.diffopt) < 0)254die("diff_setup_done failed");255}256rev.diffopt.allow_external = 1;257rev.diffopt.recursive = 1;258259/* If the user asked for our exit code then don't start a260* pager or we would end up reporting its exit code instead.261*/262if (!rev.diffopt.exit_with_status)263setup_pager();264265/* Do we have --cached and not have a pending object, then266* default to HEAD by hand. Eek.267*/268if (!rev.pending.nr) {269int i;270for (i = 1; i < argc; i++) {271const char *arg = argv[i];272if (!strcmp(arg, "--"))273break;274else if (!strcmp(arg, "--cached")) {275add_head(&rev);276if (!rev.pending.nr)277die("No HEAD commit to compare with (yet)");278break;279}280}281}282283for (i = 0; i < rev.pending.nr; i++) {284struct object_array_entry *list = rev.pending.objects+i;285struct object *obj = list->item;286const char *name = list->name;287int flags = (obj->flags & UNINTERESTING);288if (!obj->parsed)289obj = parse_object(obj->sha1);290obj = deref_tag(obj, NULL, 0);291if (!obj)292die("invalid object '%s' given.", name);293if (obj->type == OBJ_COMMIT)294obj = &((struct commit *)obj)->tree->object;295if (obj->type == OBJ_TREE) {296if (ARRAY_SIZE(ent) <= ents)297die("more than %d trees given: '%s'",298(int) ARRAY_SIZE(ent), name);299obj->flags |= flags;300ent[ents].item = obj;301ent[ents].name = name;302ents++;303continue;304}305if (obj->type == OBJ_BLOB) {306if (2 <= blobs)307die("more than two blobs given: '%s'", name);308hashcpy(blob[blobs].sha1, obj->sha1);309blob[blobs].name = name;310blob[blobs].mode = list->mode;311blobs++;312continue;313314}315die("unhandled object '%s' given.", name);316}317if (rev.prune_data) {318const char **pathspec = rev.prune_data;319while (*pathspec) {320if (!path)321path = *pathspec;322paths++;323pathspec++;324}325}326327/*328* Now, do the arguments look reasonable?329*/330if (!ents) {331switch (blobs) {332case 0:333result = run_diff_files_cmd(&rev, argc, argv);334break;335case 1:336if (paths != 1)337usage(builtin_diff_usage);338result = builtin_diff_b_f(&rev, argc, argv, blob, path);339break;340case 2:341if (paths)342usage(builtin_diff_usage);343result = builtin_diff_blobs(&rev, argc, argv, blob);344break;345default:346usage(builtin_diff_usage);347}348}349else if (blobs)350usage(builtin_diff_usage);351else if (ents == 1)352result = builtin_diff_index(&rev, argc, argv);353else if (ents == 2)354result = builtin_diff_tree(&rev, argc, argv, ent);355else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {356/* diff A...B where there is one sane merge base between357* A and B. We have ent[0] == merge-base, ent[1] == A,358* and ent[2] == B. Show diff between the base and B.359*/360ent[1] = ent[2];361result = builtin_diff_tree(&rev, argc, argv, ent);362}363else364result = builtin_diff_combined(&rev, argc, argv,365ent, ents);366if (rev.diffopt.exit_with_status)367result = rev.diffopt.has_changes;368369if (1 < rev.diffopt.skip_stat_unmatch)370refresh_index_quietly();371return result;372}