2 * Builtin "git log" and related commands (show, whatchanged)
4 * (C) Copyright 2006 Linus Torvalds
16 static int default_show_root
= 1;
18 /* this is in builtin-diff.c */
19 void add_head(struct rev_info
*revs
);
21 static void cmd_log_init(int argc
, const char **argv
, const char *prefix
,
24 rev
->abbrev
= DEFAULT_ABBREV
;
25 rev
->commit_format
= CMIT_FMT_DEFAULT
;
26 rev
->verbose_header
= 1;
27 rev
->show_root_diff
= default_show_root
;
28 argc
= setup_revisions(argc
, argv
, rev
, "HEAD");
29 if (rev
->diffopt
.pickaxe
|| rev
->diffopt
.filter
)
30 rev
->always_show_header
= 0;
32 die("unrecognized argument: %s", argv
[1]);
35 static int cmd_log_walk(struct rev_info
*rev
)
37 struct commit
*commit
;
39 prepare_revision_walk(rev
);
40 while ((commit
= get_revision(rev
)) != NULL
) {
41 log_tree_commit(rev
, commit
);
43 commit
->buffer
= NULL
;
44 free_commit_list(commit
->parents
);
45 commit
->parents
= NULL
;
50 static int git_log_config(const char *var
, const char *value
)
52 if (!strcmp(var
, "log.showroot")) {
53 default_show_root
= git_config_bool(var
, value
);
56 return git_diff_ui_config(var
, value
);
59 int cmd_whatchanged(int argc
, const char **argv
, const char *prefix
)
63 git_config(git_log_config
);
64 init_revisions(&rev
, prefix
);
66 rev
.diffopt
.recursive
= 1;
67 rev
.simplify_history
= 0;
68 cmd_log_init(argc
, argv
, prefix
, &rev
);
69 if (!rev
.diffopt
.output_format
)
70 rev
.diffopt
.output_format
= DIFF_FORMAT_RAW
;
71 return cmd_log_walk(&rev
);
74 int cmd_show(int argc
, const char **argv
, const char *prefix
)
78 git_config(git_log_config
);
79 init_revisions(&rev
, prefix
);
81 rev
.diffopt
.recursive
= 1;
82 rev
.combine_merges
= 1;
83 rev
.dense_combined_merges
= 1;
84 rev
.always_show_header
= 1;
85 rev
.ignore_merges
= 0;
87 cmd_log_init(argc
, argv
, prefix
, &rev
);
88 return cmd_log_walk(&rev
);
91 int cmd_log(int argc
, const char **argv
, const char *prefix
)
95 git_config(git_log_config
);
96 init_revisions(&rev
, prefix
);
97 rev
.always_show_header
= 1;
98 cmd_log_init(argc
, argv
, prefix
, &rev
);
99 return cmd_log_walk(&rev
);
102 static int istitlechar(char c
)
104 return (c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') ||
105 (c
>= '0' && c
<= '9') || c
== '.' || c
== '_';
108 static char *extra_headers
= NULL
;
109 static int extra_headers_size
= 0;
111 static int git_format_config(const char *var
, const char *value
)
113 if (!strcmp(var
, "format.headers")) {
114 int len
= strlen(value
);
115 extra_headers_size
+= len
+ 1;
116 extra_headers
= xrealloc(extra_headers
, extra_headers_size
);
117 extra_headers
[extra_headers_size
- len
- 1] = 0;
118 strcat(extra_headers
, value
);
121 if (!strcmp(var
, "diff.color")) {
124 return git_log_config(var
, value
);
128 static FILE *realstdout
= NULL
;
129 static const char *output_directory
= NULL
;
131 static void reopen_stdout(struct commit
*commit
, int nr
, int keep_subject
)
137 if (output_directory
) {
138 strlcpy(filename
, output_directory
, 1010);
139 len
= strlen(filename
);
140 if (filename
[len
- 1] != '/')
141 filename
[len
++] = '/';
144 sprintf(filename
+ len
, "%04d", nr
);
145 len
= strlen(filename
);
147 sol
= strstr(commit
->buffer
, "\n\n");
152 /* strip [PATCH] or [PATCH blabla] */
153 if (!keep_subject
&& !strncmp(sol
, "[PATCH", 6)) {
154 char *eos
= strchr(sol
+ 6, ']');
156 while (isspace(*eos
))
162 for (j
= 0; len
< 1024 - 6 && sol
[j
] && sol
[j
] != '\n'; j
++) {
163 if (istitlechar(sol
[j
])) {
165 filename
[len
++] = '-';
168 filename
[len
++] = sol
[j
];
170 while (sol
[j
+ 1] == '.')
175 while (filename
[len
- 1] == '.' || filename
[len
- 1] == '-')
178 strcpy(filename
+ len
, ".txt");
179 fprintf(realstdout
, "%s\n", filename
);
180 freopen(filename
, "w", stdout
);
183 static int get_patch_id(struct commit
*commit
, struct diff_options
*options
,
187 diff_tree_sha1(commit
->parents
->item
->object
.sha1
,
188 commit
->object
.sha1
, "", options
);
190 diff_root_tree_sha1(commit
->object
.sha1
, "", options
);
191 diffcore_std(options
);
192 return diff_flush_patch_id(options
, sha1
);
195 static void get_patch_ids(struct rev_info
*rev
, struct diff_options
*options
, const char *prefix
)
197 struct rev_info check_rev
;
198 struct commit
*commit
;
199 struct object
*o1
, *o2
;
200 unsigned flags1
, flags2
;
201 unsigned char sha1
[20];
203 if (rev
->pending
.nr
!= 2)
204 die("Need exactly one range.");
206 o1
= rev
->pending
.objects
[0].item
;
208 o2
= rev
->pending
.objects
[1].item
;
211 if ((flags1
& UNINTERESTING
) == (flags2
& UNINTERESTING
))
215 options
->recursive
= 1;
216 if (diff_setup_done(options
) < 0)
217 die("diff_setup_done failed");
219 /* given a range a..b get all patch ids for b..a */
220 init_revisions(&check_rev
, prefix
);
221 o1
->flags
^= UNINTERESTING
;
222 o2
->flags
^= UNINTERESTING
;
223 add_pending_object(&check_rev
, o1
, "o1");
224 add_pending_object(&check_rev
, o2
, "o2");
225 prepare_revision_walk(&check_rev
);
227 while ((commit
= get_revision(&check_rev
)) != NULL
) {
229 if (commit
->parents
&& commit
->parents
->next
)
232 if (!get_patch_id(commit
, options
, sha1
))
233 created_object(sha1
, xcalloc(1, sizeof(struct object
)));
236 /* reset for next revision walk */
237 clear_commit_marks((struct commit
*)o1
,
238 SEEN
| UNINTERESTING
| SHOWN
| ADDED
);
239 clear_commit_marks((struct commit
*)o2
,
240 SEEN
| UNINTERESTING
| SHOWN
| ADDED
);
245 static void gen_message_id(char *dest
, unsigned int length
, char *base
)
247 const char *committer
= git_committer_info(1);
248 const char *email_start
= strrchr(committer
, '<');
249 const char *email_end
= strrchr(committer
, '>');
250 if(!email_start
|| !email_end
|| email_start
> email_end
- 1)
251 die("Could not extract email from committer identity.");
252 snprintf(dest
, length
, "%s.%lu.git.%.*s", base
,
253 (unsigned long) time(NULL
),
254 (int)(email_end
- email_start
- 1), email_start
+ 1);
257 int cmd_format_patch(int argc
, const char **argv
, const char *prefix
)
259 struct commit
*commit
;
260 struct commit
**list
= NULL
;
262 int nr
= 0, total
, i
, j
;
265 int start_number
= -1;
266 int keep_subject
= 0;
267 int ignore_if_in_upstream
= 0;
269 const char *in_reply_to
= NULL
;
270 struct diff_options patch_id_opts
;
271 char *add_signoff
= NULL
;
272 char message_id
[1024];
273 char ref_message_id
[1024];
276 git_config(git_format_config
);
277 init_revisions(&rev
, prefix
);
278 rev
.commit_format
= CMIT_FMT_EMAIL
;
279 rev
.verbose_header
= 1;
281 rev
.combine_merges
= 0;
282 rev
.ignore_merges
= 1;
283 rev
.diffopt
.msg_sep
= "";
284 rev
.diffopt
.recursive
= 1;
286 rev
.extra_headers
= extra_headers
;
289 * Parse the arguments before setup_revisions(), or something
290 * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
291 * possibly a valid SHA1.
293 for (i
= 1, j
= 1; i
< argc
; i
++) {
294 if (!strcmp(argv
[i
], "--stdout"))
296 else if (!strcmp(argv
[i
], "-n") ||
297 !strcmp(argv
[i
], "--numbered"))
299 else if (!strncmp(argv
[i
], "--start-number=", 15))
300 start_number
= strtol(argv
[i
] + 15, NULL
, 10);
301 else if (!strcmp(argv
[i
], "--start-number")) {
304 die("Need a number for --start-number");
305 start_number
= strtol(argv
[i
], NULL
, 10);
307 else if (!strcmp(argv
[i
], "-k") ||
308 !strcmp(argv
[i
], "--keep-subject")) {
312 else if (!strcmp(argv
[i
], "--output-directory") ||
313 !strcmp(argv
[i
], "-o")) {
316 die("Which directory?");
317 if (output_directory
)
318 die("Two output directories?");
319 output_directory
= argv
[i
];
321 else if (!strcmp(argv
[i
], "--signoff") ||
322 !strcmp(argv
[i
], "-s")) {
323 const char *committer
;
325 committer
= git_committer_info(1);
326 endpos
= strchr(committer
, '>');
328 die("bogos committer info %s\n", committer
);
329 add_signoff
= xmalloc(endpos
- committer
+ 2);
330 memcpy(add_signoff
, committer
, endpos
- committer
+ 1);
331 add_signoff
[endpos
- committer
+ 1] = 0;
333 else if (!strcmp(argv
[i
], "--attach"))
334 rev
.mime_boundary
= git_version_string
;
335 else if (!strncmp(argv
[i
], "--attach=", 9))
336 rev
.mime_boundary
= argv
[i
] + 9;
337 else if (!strcmp(argv
[i
], "--ignore-if-in-upstream"))
338 ignore_if_in_upstream
= 1;
339 else if (!strcmp(argv
[i
], "--thread"))
341 else if (!strncmp(argv
[i
], "--in-reply-to=", 14))
342 in_reply_to
= argv
[i
] + 14;
343 else if (!strcmp(argv
[i
], "--in-reply-to")) {
346 die("Need a Message-Id for --in-reply-to");
347 in_reply_to
= argv
[i
];
354 if (start_number
< 0)
356 if (numbered
&& keep_subject
)
357 die ("-n and -k are mutually exclusive.");
359 argc
= setup_revisions(argc
, argv
, &rev
, "HEAD");
361 die ("unrecognized argument: %s", argv
[1]);
363 if (!rev
.diffopt
.output_format
)
364 rev
.diffopt
.output_format
= DIFF_FORMAT_DIFFSTAT
| DIFF_FORMAT_PATCH
;
366 if (!output_directory
)
367 output_directory
= prefix
;
369 if (output_directory
) {
371 die("standard output, or directory, which one?");
372 if (mkdir(output_directory
, 0777) < 0 && errno
!= EEXIST
)
373 die("Could not create directory %s",
377 if (rev
.pending
.nr
== 1) {
378 rev
.pending
.objects
[0].item
->flags
|= UNINTERESTING
;
382 if (ignore_if_in_upstream
)
383 get_patch_ids(&rev
, &patch_id_opts
, prefix
);
386 realstdout
= fdopen(dup(1), "w");
388 prepare_revision_walk(&rev
);
389 while ((commit
= get_revision(&rev
)) != NULL
) {
390 unsigned char sha1
[20];
393 if (commit
->parents
&& commit
->parents
->next
)
396 if (ignore_if_in_upstream
&&
397 !get_patch_id(commit
, &patch_id_opts
, sha1
) &&
402 list
= xrealloc(list
, nr
* sizeof(list
[0]));
403 list
[nr
- 1] = commit
;
407 rev
.total
= total
+ start_number
- 1;
408 rev
.add_signoff
= add_signoff
;
409 rev
.ref_message_id
= in_reply_to
;
413 rev
.nr
= total
- nr
+ (start_number
- 1);
414 /* Make the second and subsequent mails replies to the first */
416 if (nr
== (total
- 2)) {
417 strncpy(ref_message_id
, message_id
,
418 sizeof(ref_message_id
));
419 ref_message_id
[sizeof(ref_message_id
)-1]='\0';
420 rev
.ref_message_id
= ref_message_id
;
422 gen_message_id(message_id
, sizeof(message_id
),
423 sha1_to_hex(commit
->object
.sha1
));
424 rev
.message_id
= message_id
;
427 reopen_stdout(commit
, rev
.nr
, keep_subject
);
428 shown
= log_tree_commit(&rev
, commit
);
429 free(commit
->buffer
);
430 commit
->buffer
= NULL
;
432 /* We put one extra blank line between formatted
433 * patches and this flag is used by log-tree code
434 * to see if it needs to emit a LF before showing
435 * the log; when using one file per patch, we do
436 * not want the extra blank line.
441 if (rev
.mime_boundary
)
442 printf("\n--%s%s--\n\n\n",
443 mime_boundary_leader
,
446 printf("-- \n%s\n\n", git_version_string
);
455 static int add_pending_commit(const char *arg
, struct rev_info
*revs
, int flags
)
457 unsigned char sha1
[20];
458 if (get_sha1(arg
, sha1
) == 0) {
459 struct commit
*commit
= lookup_commit_reference(sha1
);
461 commit
->object
.flags
|= flags
;
462 add_pending_object(revs
, &commit
->object
, arg
);
469 static const char cherry_usage
[] =
470 "git-cherry [-v] <upstream> [<head>] [<limit>]";
471 int cmd_cherry(int argc
, const char **argv
, const char *prefix
)
473 struct rev_info revs
;
474 struct diff_options patch_id_opts
;
475 struct commit
*commit
;
476 struct commit_list
*list
= NULL
;
477 const char *upstream
;
478 const char *head
= "HEAD";
479 const char *limit
= NULL
;
482 if (argc
> 1 && !strcmp(argv
[1], "-v")) {
502 init_revisions(&revs
, prefix
);
504 revs
.combine_merges
= 0;
505 revs
.ignore_merges
= 1;
506 revs
.diffopt
.recursive
= 1;
508 if (add_pending_commit(head
, &revs
, 0))
509 die("Unknown commit %s", head
);
510 if (add_pending_commit(upstream
, &revs
, UNINTERESTING
))
511 die("Unknown commit %s", upstream
);
513 /* Don't say anything if head and upstream are the same. */
514 if (revs
.pending
.nr
== 2) {
515 struct object_array_entry
*o
= revs
.pending
.objects
;
516 if (hashcmp(o
[0].item
->sha1
, o
[1].item
->sha1
) == 0)
520 get_patch_ids(&revs
, &patch_id_opts
, prefix
);
522 if (limit
&& add_pending_commit(limit
, &revs
, UNINTERESTING
))
523 die("Unknown commit %s", limit
);
525 /* reverse the list of commits */
526 prepare_revision_walk(&revs
);
527 while ((commit
= get_revision(&revs
)) != NULL
) {
529 if (commit
->parents
&& commit
->parents
->next
)
532 commit_list_insert(commit
, &list
);
536 unsigned char sha1
[20];
540 if (!get_patch_id(commit
, &patch_id_opts
, sha1
) &&
545 static char buf
[16384];
546 pretty_print_commit(CMIT_FMT_ONELINE
, commit
, ~0,
547 buf
, sizeof(buf
), 0, NULL
, NULL
, 0);
548 printf("%c %s %s\n", sign
,
549 sha1_to_hex(commit
->object
.sha1
), buf
);
552 printf("%c %s\n", sign
,
553 sha1_to_hex(commit
->object
.sha1
));