built-in diff: assorted updates.
[git.git] / builtin-diff.c
1 /*
2  * Builtin "git diff"
3  *
4  * Copyright (c) 2006 Junio C Hamano
5  */
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"
15
16 /* NEEDSWORK: struct object has place for name but we _do_
17  * know mode when we extracted the blob out of a tree, which
18  * we currently lose.
19  */
20 struct blobinfo {
21         unsigned char sha1[20];
22         const char *name;
23 };
24
25 static const char builtin_diff_usage[] =
26 "diff <options> <rev>{0,2} -- <path>*";
27
28 static int builtin_diff_files(struct rev_info *revs,
29                               int argc, const char **argv)
30 {
31         int silent = 0;
32         while (1 < argc) {
33                 const char *arg = argv[1];
34                 if (!strcmp(arg, "--base"))
35                         revs->max_count = 1;
36                 else if (!strcmp(arg, "--ours"))
37                         revs->max_count = 2;
38                 else if (!strcmp(arg, "--theirs"))
39                         revs->max_count = 3;
40                 else if (!strcmp(arg, "-q"))
41                         silent = 1;
42                 else if (!strcmp(arg, "--raw"))
43                         revs->diffopt.output_format = DIFF_FORMAT_RAW;
44                 else
45                         usage(builtin_diff_usage);
46                 argv++; argc--;
47         }
48         /*
49          * Make sure there are NO revision (i.e. pending object) parameter,
50          * specified rev.max_count is reasonable (0 <= n <= 3), and
51          * there is no other revision filtering parameter.
52          */
53         if (revs->pending_objects ||
54             revs->min_age != -1 ||
55             revs->max_age != -1 ||
56             3 < revs->max_count)
57                 usage(builtin_diff_usage);
58         if (revs->max_count < 0 &&
59             (revs->diffopt.output_format == DIFF_FORMAT_PATCH))
60                 revs->combine_merges = revs->dense_combined_merges = 1;
61         /*
62          * Backward compatibility wart - "diff-files -s" used to
63          * defeat the common diff option "-s" which asked for
64          * DIFF_FORMAT_NO_OUTPUT.
65          */
66         if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
67                 revs->diffopt.output_format = DIFF_FORMAT_RAW;
68         return run_diff_files(revs, silent);
69 }
70
71 static void stuff_change(struct diff_options *opt,
72                          unsigned old_mode, unsigned new_mode,
73                          const unsigned char *old_sha1,
74                          const unsigned char *new_sha1,
75                          const char *old_name,
76                          const char *new_name)
77 {
78         struct diff_filespec *one, *two;
79
80         if (memcmp(null_sha1, old_sha1, 20) &&
81             memcmp(null_sha1, new_sha1, 20) &&
82             !memcmp(old_sha1, new_sha1, 20))
83                 return;
84
85         if (opt->reverse_diff) {
86                 unsigned tmp;
87                 const
88                         const unsigned char *tmp_u;
89                 const char *tmp_c;
90                 tmp = old_mode; old_mode = new_mode; new_mode = tmp;
91                 tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
92                 tmp_c = old_name; old_name = new_name; new_name = tmp_c;
93         }
94         one = alloc_filespec(old_name);
95         two = alloc_filespec(new_name);
96         fill_filespec(one, old_sha1, old_mode);
97         fill_filespec(two, new_sha1, new_mode);
98
99         /* NEEDSWORK: shouldn't this part of diffopt??? */
100         diff_queue(&diff_queued_diff, one, two);
101 }
102
103 static int builtin_diff_b_f(struct rev_info *revs,
104                             int argc, const char **argv,
105                             struct blobinfo *blob,
106                             const char *path)
107 {
108         /* Blob vs file in the working tree*/
109         struct stat st;
110
111         while (1 < argc) {
112                 const char *arg = argv[1];
113                 if (!strcmp(arg, "--raw"))
114                         revs->diffopt.output_format = DIFF_FORMAT_RAW;
115                 else
116                         usage(builtin_diff_usage);
117                 argv++; argc--;
118         }
119         if (lstat(path, &st))
120                 die("'%s': %s", path, strerror(errno));
121         if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
122                 die("'%s': not a regular file or symlink", path);
123         stuff_change(&revs->diffopt,
124                      canon_mode(st.st_mode), canon_mode(st.st_mode),
125                      blob[0].sha1, null_sha1,
126                      blob[0].name, path);
127         diffcore_std(&revs->diffopt);
128         diff_flush(&revs->diffopt);
129         return 0;
130 }
131
132 static int builtin_diff_blobs(struct rev_info *revs,
133                               int argc, const char **argv,
134                               struct blobinfo *blob)
135 {
136         /* Blobs */
137         unsigned mode = canon_mode(S_IFREG | 0644);
138
139         while (1 < argc) {
140                 const char *arg = argv[1];
141                 if (!strcmp(arg, "--raw"))
142                         revs->diffopt.output_format = DIFF_FORMAT_RAW;
143                 else
144                         usage(builtin_diff_usage);
145                 argv++; argc--;
146         }
147         stuff_change(&revs->diffopt,
148                      mode, mode,
149                      blob[0].sha1, blob[1].sha1,
150                      blob[1].name, blob[1].name);
151         diffcore_std(&revs->diffopt);
152         diff_flush(&revs->diffopt);
153         return 0;
154 }
155
156 static int builtin_diff_index(struct rev_info *revs,
157                               int argc, const char **argv)
158 {
159         int cached = 0;
160         while (1 < argc) {
161                 const char *arg = argv[1];
162                 if (!strcmp(arg, "--cached"))
163                         cached = 1;
164                 else if (!strcmp(arg, "--raw"))
165                         revs->diffopt.output_format = DIFF_FORMAT_RAW;
166                 else
167                         usage(builtin_diff_usage);
168                 argv++; argc--;
169         }
170         /*
171          * Make sure there is one revision (i.e. pending object),
172          * and there is no revision filtering parameters.
173          */
174         if (!revs->pending_objects || revs->pending_objects->next ||
175             revs->max_count != -1 || revs->min_age != -1 ||
176             revs->max_age != -1)
177                 usage(builtin_diff_usage);
178         return run_diff_index(revs, cached);
179 }
180
181 static int builtin_diff_tree(struct rev_info *revs,
182                              int argc, const char **argv,
183                              struct object_list *ent)
184 {
185         const unsigned char *(sha1[2]);
186         int swap = 1;
187         while (1 < argc) {
188                 const char *arg = argv[1];
189                 if (!strcmp(arg, "--raw"))
190                         revs->diffopt.output_format = DIFF_FORMAT_RAW;
191                 else
192                         usage(builtin_diff_usage);
193                 argv++; argc--;
194         }
195
196         /* We saw two trees, ent[0] and ent[1].
197          * unless ent[0] is unintesting, they are swapped
198          */
199         if (ent[0].item->flags & UNINTERESTING)
200                 swap = 0;
201         sha1[swap] = ent[0].item->sha1;
202         sha1[1-swap] = ent[1].item->sha1;
203         diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
204         log_tree_diff_flush(revs);
205         return 0;
206 }
207
208 static int builtin_diff_combined(struct rev_info *revs,
209                                  int argc, const char **argv,
210                                  struct object_list *ent,
211                                  int ents)
212 {
213         const unsigned char (*parent)[20];
214         int i;
215
216         while (1 < argc) {
217                 const char *arg = argv[1];
218                 if (!strcmp(arg, "--raw"))
219                         revs->diffopt.output_format = DIFF_FORMAT_RAW;
220                 else
221                         usage(builtin_diff_usage);
222                 argv++; argc--;
223         }
224         if (!revs->dense_combined_merges && !revs->combine_merges)
225                 revs->dense_combined_merges = revs->combine_merges = 1;
226         parent = xmalloc(ents * sizeof(*parent));
227         /* Again, the revs are all reverse */
228         for (i = 0; i < ents; i++)
229                 memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
230         diff_tree_combined(parent[0], parent + 1, ents - 1,
231                            revs->dense_combined_merges, revs);
232         return 0;
233 }
234
235 static void add_head(struct rev_info *revs)
236 {
237         unsigned char sha1[20];
238         struct object *obj;
239         if (get_sha1("HEAD", sha1))
240                 return;
241         obj = parse_object(sha1);
242         if (!obj)
243                 return;
244         add_object(obj, &revs->pending_objects, NULL, "HEAD");
245 }
246
247 int cmd_diff(int argc, const char **argv, char **envp)
248 {
249         struct rev_info rev;
250         struct object_list *list, ent[100];
251         int ents = 0, blobs = 0, paths = 0;
252         const char *path = NULL;
253         struct blobinfo blob[2];
254
255         /*
256          * We could get N tree-ish in the rev.pending_objects list.
257          * Also there could be M blobs there, and P pathspecs.
258          *
259          * N=0, M=0:
260          *      cache vs files (diff-files)
261          * N=0, M=2:
262          *      compare two random blobs.  P must be zero.
263          * N=0, M=1, P=1:
264          *      compare a blob with a working tree file.
265          *
266          * N=1, M=0:
267          *      tree vs cache (diff-index --cached)
268          *
269          * N=2, M=0:
270          *      tree vs tree (diff-tree)
271          *
272          * Other cases are errors.
273          */
274         
275         git_config(git_diff_config);
276         init_revisions(&rev);
277         rev.diffopt.output_format = DIFF_FORMAT_PATCH;
278
279         argc = setup_revisions(argc, argv, &rev, NULL);
280         /* Do we have --cached and not have a pending object, then
281          * default to HEAD by hand.  Eek.
282          */
283         if (!rev.pending_objects) {
284                 int i;
285                 for (i = 1; i < argc; i++) {
286                         const char *arg = argv[i];
287                         if (!strcmp(arg, "--"))
288                                 break;
289                         else if (!strcmp(arg, "--cached")) {
290                                 add_head(&rev);
291                                 break;
292                         }
293                 }
294         }
295
296         for (list = rev.pending_objects; list; list = list->next) {
297                 struct object *obj = list->item;
298                 const char *name = list->name;
299                 int flags = (obj->flags & UNINTERESTING);
300                 if (!obj->parsed)
301                         obj = parse_object(obj->sha1);
302                 obj = deref_tag(obj, NULL, 0);
303                 if (!obj)
304                         die("invalid object '%s' given.", name);
305                 if (!strcmp(obj->type, commit_type))
306                         obj = &((struct commit *)obj)->tree->object;
307                 if (!strcmp(obj->type, tree_type)) {
308                         if (ARRAY_SIZE(ent) <= ents)
309                                 die("more than %d trees given: '%s'",
310                                     ARRAY_SIZE(ent), name);
311                         obj->flags |= flags;
312                         ent[ents].item = obj;
313                         ent[ents].name = name;
314                         ents++;
315                         continue;
316                 }
317                 if (!strcmp(obj->type, blob_type)) {
318                         if (2 <= blobs)
319                                 die("more than two blobs given: '%s'", name);
320                         memcpy(blob[blobs].sha1, obj->sha1, 20);
321                         blob[blobs].name = name;
322                         blobs++;
323                         continue;
324                         
325                 }
326                 die("unhandled object '%s' given.", name);
327         }
328         if (rev.prune_data) {
329                 const char **pathspec = rev.prune_data;
330                 while (*pathspec) {
331                         if (!path)
332                                 path = *pathspec;
333                         paths++;
334                         pathspec++;
335                 }
336         }
337
338         /*
339          * Now, do the arguments look reasonable?
340          */
341         if (!ents) {
342                 switch (blobs) {
343                 case 0:
344                         return builtin_diff_files(&rev, argc, argv);
345                         break;
346                 case 1:
347                         if (paths != 1)
348                                 usage(builtin_diff_usage);
349                         return builtin_diff_b_f(&rev, argc, argv, blob, path);
350                         break;
351                 case 2:
352                         if (paths)
353                                 usage(builtin_diff_usage);
354                         return builtin_diff_blobs(&rev, argc, argv, blob);
355                         break;
356                 default:
357                         usage(builtin_diff_usage);
358                 }
359         }
360         else if (blobs)
361                 usage(builtin_diff_usage);
362         else if (ents == 1)
363                 return builtin_diff_index(&rev, argc, argv);
364         else if (ents == 2)
365                 return builtin_diff_tree(&rev, argc, argv, ent);
366         else
367                 return builtin_diff_combined(&rev, argc, argv, ent, ents);
368         usage(builtin_diff_usage);
369 }