diff options
-rw-r--r-- | builtin-log.c | 153 | ||||
-rw-r--r-- | builtin.h | 1 | ||||
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | commit.c | 37 | ||||
-rw-r--r-- | commit.h | 3 | ||||
-rw-r--r-- | date.c | 29 | ||||
-rw-r--r-- | git.c | 1 | ||||
-rw-r--r-- | log-tree.c | 45 | ||||
-rw-r--r-- | rev-list.c | 2 | ||||
-rw-r--r-- | revision.h | 1 | ||||
-rw-r--r-- | show-branch.c | 2 |
11 files changed, 251 insertions, 24 deletions
diff --git a/builtin-log.c b/builtin-log.c index 69f2911cb4..0027998f10 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -9,6 +9,7 @@ #include "diff.h" #include "revision.h" #include "log-tree.h" +#include "builtin.h" static int cmd_log_wc(int argc, const char **argv, char **envp, struct rev_info *rev) @@ -67,3 +68,155 @@ int cmd_log(int argc, const char **argv, char **envp) rev.diffopt.recursive = 1; return cmd_log_wc(argc, argv, envp, &rev); } + +static int istitlechar(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '.' || c == '_'; +} + +static FILE *realstdout = NULL; +static char *output_directory = NULL; + +static void reopen_stdout(struct commit *commit, int nr, int keep_subject) +{ + char filename[1024]; + char *sol; + int len = 0; + + if (output_directory) { + strncpy(filename, output_directory, 1010); + len = strlen(filename); + if (filename[len - 1] != '/') + filename[len++] = '/'; + } + + sprintf(filename + len, "%04d", nr); + len = strlen(filename); + + sol = strstr(commit->buffer, "\n\n"); + if (sol) { + int j, space = 1; + + sol += 2; + /* strip [PATCH] or [PATCH blabla] */ + if (!keep_subject && !strncmp(sol, "[PATCH", 6)) { + char *eos = strchr(sol + 6, ']'); + if (eos) { + while (isspace(*eos)) + eos++; + sol = eos; + } + } + + for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) { + if (istitlechar(sol[j])) { + if (space) { + filename[len++] = '-'; + space = 0; + } + filename[len++] = sol[j]; + if (sol[j] == '.') + while (sol[j + 1] == '.') + j++; + } else + space = 1; + } + while (filename[len - 1] == '.' || filename[len - 1] == '-') + len--; + } + strcpy(filename + len, ".txt"); + fprintf(realstdout, "%s\n", filename); + freopen(filename, "w", stdout); +} + +int cmd_format_patch(int argc, const char **argv, char **envp) +{ + struct commit *commit; + struct commit **list = NULL; + struct rev_info rev; + int nr = 0, total, i, j; + int use_stdout = 0; + int numbered = 0; + int keep_subject = 0; + + init_revisions(&rev); + rev.commit_format = CMIT_FMT_EMAIL; + rev.verbose_header = 1; + rev.diff = 1; + rev.diffopt.with_raw = 0; + rev.diffopt.with_stat = 1; + rev.combine_merges = 0; + rev.ignore_merges = 1; + rev.diffopt.output_format = DIFF_FORMAT_PATCH; + + /* + * Parse the arguments before setup_revisions(), or something + * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is + * possibly a valid SHA1. + */ + for (i = 1, j = 1; i < argc; i++) { + if (!strcmp(argv[i], "--stdout")) + use_stdout = 1; + else if (!strcmp(argv[i], "-n") || + !strcmp(argv[i], "--numbered")) + numbered = 1; + else if (!strcmp(argv[i], "-k") || + !strcmp(argv[i], "--keep-subject")) { + keep_subject = 1; + rev.total = -1; + } else if (!strcmp(argv[i], "-o")) { + if (argc < 3) + die ("Which directory?"); + if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST) + die("Could not create directory %s", + argv[i + 1]); + output_directory = strdup(argv[i + 1]); + i++; + } else + argv[j++] = argv[i]; + } + argc = j; + + if (numbered && keep_subject < 0) + die ("-n and -k are mutually exclusive."); + + argc = setup_revisions(argc, argv, &rev, "HEAD"); + if (argc > 1) + die ("unrecognized argument: %s", argv[1]); + + if (!use_stdout) + realstdout = fdopen(dup(1), "w"); + + prepare_revision_walk(&rev); + while ((commit = get_revision(&rev)) != NULL) { + /* ignore merges */ + if (commit->parents && commit->parents->next) + continue; + nr++; + list = realloc(list, nr * sizeof(list[0])); + list[nr - 1] = commit; + } + total = nr; + if (numbered) + rev.total = total; + while (0 <= --nr) { + int shown; + commit = list[nr]; + rev.nr = total - nr; + if (!use_stdout) + reopen_stdout(commit, rev.nr, keep_subject); + shown = log_tree_commit(&rev, commit); + free(commit->buffer); + commit->buffer = NULL; + if (shown) + printf("-- \n%s\n\n", git_version_string); + if (!use_stdout) + fclose(stdout); + } + if (output_directory) + free(output_directory); + free(list); + return 0; +} + @@ -20,6 +20,7 @@ extern int cmd_whatchanged(int argc, const char **argv, char **envp); extern int cmd_show(int argc, const char **argv, char **envp); extern int cmd_log(int argc, const char **argv, char **envp); extern int cmd_diff(int argc, const char **argv, char **envp); +extern int cmd_format_patch(int argc, const char **argv, char **envp); extern int cmd_count_objects(int argc, const char **argv, char **envp); extern int cmd_push(int argc, const char **argv, char **envp); @@ -251,6 +251,7 @@ extern void *read_object_with_reference(const unsigned char *sha1, unsigned char *sha1_ret); const char *show_date(unsigned long time, int timezone); +const char *show_rfc2822_date(unsigned long time, int timezone); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); @@ -36,6 +36,8 @@ enum cmit_fmt get_commit_format(const char *arg) return CMIT_FMT_FULL; if (!strcmp(arg, "=fuller")) return CMIT_FMT_FULLER; + if (!strcmp(arg, "=email")) + return CMIT_FMT_EMAIL; if (!strcmp(arg, "=oneline")) return CMIT_FMT_ONELINE; die("invalid --pretty format"); @@ -428,6 +430,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); + if (fmt == CMIT_FMT_EMAIL) { + what = "From"; + filler = ""; + } ret = sprintf(buf, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); @@ -435,6 +441,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); break; + case CMIT_FMT_EMAIL: + ret += sprintf(buf + ret, "Date: %s\n", + show_rfc2822_date(time, tz)); + break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); break; @@ -445,10 +455,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c return ret; } -static int is_empty_line(const char *line, int len) +static int is_empty_line(const char *line, int *len_p) { + int len = *len_p; while (len && isspace(line[len-1])) len--; + *len_p = len; return !len; } @@ -457,7 +469,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com struct commit_list *parent = commit->parents; int offset; - if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next) + if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + !parent || !parent->next) return 0; offset = sprintf(buf, "Merge:"); @@ -476,14 +489,17 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) { int hdr = 1, body = 0; unsigned long offset = 0; - int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4; + int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + indent = 0; + for (;;) { const char *line = msg; int linelen = get_one_line(msg, len); @@ -506,7 +522,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit if (hdr) { if (linelen == 1) { hdr = 0; - if (fmt != CMIT_FMT_ONELINE) + if ((fmt != CMIT_FMT_ONELINE) && !subject) buf[offset++] = '\n'; continue; } @@ -544,20 +560,29 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit continue; } - if (is_empty_line(line, linelen)) { + if (is_empty_line(line, &linelen)) { if (!body) continue; + if (subject) + continue; if (fmt == CMIT_FMT_SHORT) break; } else { body = 1; } + if (subject) { + int slen = strlen(subject); + memcpy(buf + offset, subject, slen); + offset += slen; + } memset(buf + offset, ' ', indent); memcpy(buf + offset + indent, line, linelen); offset += linelen + indent; + buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + subject = NULL; } while (offset && isspace(buf[offset-1])) offset--; @@ -45,12 +45,13 @@ enum cmit_fmt { CMIT_FMT_FULL, CMIT_FMT_FULLER, CMIT_FMT_ONELINE, + CMIT_FMT_EMAIL, CMIT_FMT_UNSPECIFIED, }; extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject); /** Removes the first commit from a list sorted by date, and adds all * of its parents. @@ -42,18 +42,24 @@ static const char *weekday_names[] = { * thing, which means that tz -0100 is passed in as the integer -100, * even though it means "sixty minutes off" */ -const char *show_date(unsigned long time, int tz) +static struct tm *time_to_tm(unsigned long time, int tz) { - struct tm *tm; time_t t; - static char timebuf[200]; int minutes; minutes = tz < 0 ? -tz : tz; minutes = (minutes / 100)*60 + (minutes % 100); minutes = tz < 0 ? -minutes : minutes; t = time + minutes * 60; - tm = gmtime(&t); + return gmtime(&t); +} + +const char *show_date(unsigned long time, int tz) +{ + struct tm *tm; + static char timebuf[200]; + + tm = time_to_tm(time, tz); if (!tm) return NULL; sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d", @@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz) return timebuf; } +const char *show_rfc2822_date(unsigned long time, int tz) +{ + struct tm *tm; + static char timebuf[200]; + + tm = time_to_tm(time, tz); + if (!tm) + return NULL; + sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d", + weekday_names[tm->tm_wday], tm->tm_mday, + month_names[tm->tm_mon], tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, tm->tm_sec, tz); + return timebuf; +} + /* * Check these. And note how it doesn't do the summer-time conversion. * @@ -47,6 +47,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "whatchanged", cmd_whatchanged }, { "show", cmd_show }, { "push", cmd_push }, + { "fmt-patch", cmd_format_patch }, { "count-objects", cmd_count_objects }, { "diff", cmd_diff }, }; diff --git a/log-tree.c b/log-tree.c index b90ba6762a..526d578e98 100644 --- a/log-tree.c +++ b/log-tree.c @@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; const char *extra; int len; + char* subject = NULL; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -49,19 +50,38 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) /* * Print header line of header.. */ - printf("%s%s", - opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", - diff_unique_abbrev(commit->object.sha1, abbrev_commit)); - if (opt->parents) - show_parents(commit, abbrev_commit); - if (parent) - printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit)); - putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + + if (opt->commit_format == CMIT_FMT_EMAIL) { + if (opt->total > 0) { + static char buffer[64]; + snprintf(buffer, sizeof(buffer), + "Subject: [PATCH %d/%d] ", + opt->nr, opt->total); + subject = buffer; + } else if (opt->total == 0) + subject = "Subject: [PATCH] "; + else + subject = "Subject: "; + + printf("From %s Thu Apr 7 15:13:13 2005\n", + sha1_to_hex(commit->object.sha1)); + } else { + printf("%s%s", + opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", + diff_unique_abbrev(commit->object.sha1, abbrev_commit)); + if (opt->parents) + show_parents(commit, abbrev_commit); + if (parent) + printf(" (from %s)", + diff_unique_abbrev(parent->object.sha1, + abbrev_commit)); + putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + } /* * And then the pretty-printed message itself */ - len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev); + len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject); printf("%s%s%s", this_header, extra, sep); } @@ -166,15 +186,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log int log_tree_commit(struct rev_info *opt, struct commit *commit) { struct log_info log; + int shown; log.commit = commit; log.parent = NULL; opt->loginfo = &log; - if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) { + shown = log_tree_diff(opt, commit, &log); + if (!shown && opt->loginfo && opt->always_show_header) { log.parent = NULL; show_log(opt, opt->loginfo, ""); + shown = 1; } opt->loginfo = NULL; - return 0; + return shown; } diff --git a/rev-list.c b/rev-list.c index 8b0ec388fa..235ae4c7e1 100644 --- a/rev-list.c +++ b/rev-list.c @@ -84,7 +84,7 @@ static void show_commit(struct commit *commit) static char pretty_header[16384]; pretty_print_commit(revs.commit_format, commit, ~0, pretty_header, sizeof(pretty_header), - revs.abbrev); + revs.abbrev, NULL); printf("%s%c", pretty_header, hdr_termination); } fflush(stdout); diff --git a/revision.h b/revision.h index 48d7b4ca94..62759f7bc0 100644 --- a/revision.h +++ b/revision.h @@ -58,6 +58,7 @@ struct rev_info { unsigned int abbrev; enum cmit_fmt commit_format; struct log_info *loginfo; + int nr, total; /* special limits */ int max_count; diff --git a/show-branch.c b/show-branch.c index 268c57b180..bbe26c2e7a 100644 --- a/show-branch.c +++ b/show-branch.c @@ -259,7 +259,7 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->object.util; if (commit->object.parsed) pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - pretty, sizeof(pretty), 0); + pretty, sizeof(pretty), 0, NULL); else strcpy(pretty, "(unavailable)"); if (!strncmp(pretty, "[PATCH] ", 8)) |