Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 1 | #include "builtin.h" |
| 2 | #include "cache.h" |
| 3 | #include "commit.h" |
| 4 | #include "diff.h" |
| 5 | #include "path-list.h" |
| 6 | #include "revision.h" |
Johannes Schindelin | 3714e7c | 2006-12-22 22:15:59 +0100 | [diff] [blame] | 7 | #include "utf8.h" |
Junio C Hamano | 7c1c678 | 2007-04-27 00:41:15 -0700 | [diff] [blame] | 8 | #include "mailmap.h" |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 9 | #include "shortlog.h" |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 10 | |
| 11 | static const char shortlog_usage[] = |
Junio C Hamano | 55ef8a4 | 2008-04-12 15:38:20 -0700 | [diff] [blame] | 12 | "git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]"; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 13 | |
| 14 | static int compare_by_number(const void *a1, const void *a2) |
| 15 | { |
| 16 | const struct path_list_item *i1 = a1, *i2 = a2; |
| 17 | const struct path_list *l1 = i1->util, *l2 = i2->util; |
| 18 | |
| 19 | if (l1->nr < l2->nr) |
Johannes Schindelin | 6d6ab61 | 2006-11-21 21:12:06 +0100 | [diff] [blame] | 20 | return 1; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 21 | else if (l1->nr == l2->nr) |
| 22 | return 0; |
| 23 | else |
Johannes Schindelin | 6d6ab61 | 2006-11-21 21:12:06 +0100 | [diff] [blame] | 24 | return -1; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 25 | } |
| 26 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 27 | static void insert_one_record(struct shortlog *log, |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 28 | const char *author, |
| 29 | const char *oneline) |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 30 | { |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 31 | const char *dot3 = log->common_repo_prefix; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 32 | char *buffer, *p; |
| 33 | struct path_list_item *item; |
| 34 | struct path_list *onelines; |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 35 | char namebuf[1024]; |
| 36 | size_t len; |
| 37 | const char *eol; |
| 38 | const char *boemail, *eoemail; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 39 | |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 40 | boemail = strchr(author, '<'); |
| 41 | if (!boemail) |
| 42 | return; |
| 43 | eoemail = strchr(boemail, '>'); |
| 44 | if (!eoemail) |
| 45 | return; |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 46 | if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) { |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 47 | while (author < boemail && isspace(*author)) |
| 48 | author++; |
| 49 | for (len = 0; |
| 50 | len < sizeof(namebuf) - 1 && author + len < boemail; |
| 51 | len++) |
| 52 | namebuf[len] = author[len]; |
| 53 | while (0 < len && isspace(namebuf[len-1])) |
| 54 | len--; |
| 55 | namebuf[len] = '\0'; |
| 56 | } |
Junio C Hamano | 4602c17 | 2007-12-07 17:19:31 -0800 | [diff] [blame] | 57 | else |
| 58 | len = strlen(namebuf); |
| 59 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 60 | if (log->email) { |
Junio C Hamano | 4602c17 | 2007-12-07 17:19:31 -0800 | [diff] [blame] | 61 | size_t room = sizeof(namebuf) - len - 1; |
| 62 | int maillen = eoemail - boemail + 1; |
| 63 | snprintf(namebuf + len, room, " %.*s", maillen, boemail); |
| 64 | } |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 65 | |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 66 | buffer = xstrdup(namebuf); |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 67 | item = path_list_insert(buffer, &log->list); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 68 | if (item->util == NULL) |
| 69 | item->util = xcalloc(1, sizeof(struct path_list)); |
| 70 | else |
| 71 | free(buffer); |
| 72 | |
Andy Whitcroft | c1ce83a | 2008-03-05 14:24:10 +0000 | [diff] [blame] | 73 | /* Skip any leading whitespace, including any blank lines. */ |
| 74 | while (*oneline && isspace(*oneline)) |
| 75 | oneline++; |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 76 | eol = strchr(oneline, '\n'); |
| 77 | if (!eol) |
| 78 | eol = oneline + strlen(oneline); |
Junio C Hamano | cc44c76 | 2007-02-20 01:53:29 -0800 | [diff] [blame] | 79 | if (!prefixcmp(oneline, "[PATCH")) { |
Johannes Schindelin | 72019cd | 2006-11-19 17:28:25 +0100 | [diff] [blame] | 80 | char *eob = strchr(oneline, ']'); |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 81 | if (eob && (!eol || eob < eol)) |
| 82 | oneline = eob + 1; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 83 | } |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 84 | while (*oneline && isspace(*oneline) && *oneline != '\n') |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 85 | oneline++; |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 86 | len = eol - oneline; |
| 87 | while (len && isspace(oneline[len-1])) |
| 88 | len--; |
| 89 | buffer = xmemdupz(oneline, len); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 90 | |
Junio C Hamano | c95044d | 2006-11-25 00:01:27 -0800 | [diff] [blame] | 91 | if (dot3) { |
| 92 | int dot3len = strlen(dot3); |
| 93 | if (dot3len > 5) { |
| 94 | while ((p = strstr(buffer, dot3)) != NULL) { |
| 95 | int taillen = strlen(p) - dot3len; |
| 96 | memcpy(p, "/.../", 5); |
| 97 | memmove(p + 5, p + dot3len, taillen + 1); |
| 98 | } |
| 99 | } |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 100 | } |
| 101 | |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 102 | onelines = item->util; |
| 103 | if (onelines->nr >= onelines->alloc) { |
| 104 | onelines->alloc = alloc_nr(onelines->nr); |
| 105 | onelines->items = xrealloc(onelines->items, |
| 106 | onelines->alloc |
| 107 | * sizeof(struct path_list_item)); |
| 108 | } |
| 109 | |
| 110 | onelines->items[onelines->nr].util = NULL; |
| 111 | onelines->items[onelines->nr++].path = buffer; |
| 112 | } |
| 113 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 114 | static void read_from_stdin(struct shortlog *log) |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 115 | { |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 116 | char author[1024], oneline[1024]; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 117 | |
Junio C Hamano | 1e931cb | 2007-12-07 17:07:41 -0800 | [diff] [blame] | 118 | while (fgets(author, sizeof(author), stdin) != NULL) { |
| 119 | if (!(author[0] == 'A' || author[0] == 'a') || |
| 120 | prefixcmp(author + 1, "uthor: ")) |
| 121 | continue; |
| 122 | while (fgets(oneline, sizeof(oneline), stdin) && |
| 123 | oneline[0] != '\n') |
| 124 | ; /* discard headers */ |
| 125 | while (fgets(oneline, sizeof(oneline), stdin) && |
| 126 | oneline[0] == '\n') |
| 127 | ; /* discard blanks */ |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 128 | insert_one_record(log, author + 8, oneline); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 129 | } |
| 130 | } |
| 131 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 132 | void shortlog_add_commit(struct shortlog *log, struct commit *commit) |
| 133 | { |
| 134 | const char *author = NULL, *buffer; |
| 135 | |
| 136 | buffer = commit->buffer; |
| 137 | while (*buffer && *buffer != '\n') { |
| 138 | const char *eol = strchr(buffer, '\n'); |
| 139 | |
| 140 | if (eol == NULL) |
| 141 | eol = buffer + strlen(buffer); |
| 142 | else |
| 143 | eol++; |
| 144 | |
| 145 | if (!prefixcmp(buffer, "author ")) |
| 146 | author = buffer + 7; |
| 147 | buffer = eol; |
| 148 | } |
| 149 | if (!author) |
| 150 | die("Missing author: %s", |
| 151 | sha1_to_hex(commit->object.sha1)); |
| 152 | if (*buffer) |
| 153 | buffer++; |
| 154 | insert_one_record(log, author, !*buffer ? "<none>" : buffer); |
| 155 | } |
| 156 | |
| 157 | static void get_from_rev(struct rev_info *rev, struct shortlog *log) |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 158 | { |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 159 | struct commit *commit; |
| 160 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 161 | if (prepare_revision_walk(rev)) |
| 162 | die("revision walk setup failed"); |
| 163 | while ((commit = get_revision(rev)) != NULL) |
| 164 | shortlog_add_commit(log, commit); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 165 | } |
| 166 | |
Junio C Hamano | 3d711d9 | 2007-04-08 01:28:00 -0700 | [diff] [blame] | 167 | static int parse_uint(char const **arg, int comma) |
| 168 | { |
| 169 | unsigned long ul; |
| 170 | int ret; |
| 171 | char *endp; |
| 172 | |
| 173 | ul = strtoul(*arg, &endp, 10); |
| 174 | if (endp != *arg && *endp && *endp != comma) |
| 175 | return -1; |
| 176 | ret = (int) ul; |
| 177 | if (ret != ul) |
| 178 | return -1; |
| 179 | *arg = endp; |
| 180 | if (**arg) |
| 181 | (*arg)++; |
| 182 | return ret; |
| 183 | } |
| 184 | |
| 185 | static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]"; |
| 186 | #define DEFAULT_WRAPLEN 76 |
| 187 | #define DEFAULT_INDENT1 6 |
| 188 | #define DEFAULT_INDENT2 9 |
| 189 | |
| 190 | static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap) |
| 191 | { |
| 192 | arg += 2; /* skip -w */ |
| 193 | |
| 194 | *wrap = parse_uint(&arg, ','); |
| 195 | if (*wrap < 0) |
| 196 | die(wrap_arg_usage); |
| 197 | *in1 = parse_uint(&arg, ','); |
| 198 | if (*in1 < 0) |
| 199 | die(wrap_arg_usage); |
| 200 | *in2 = parse_uint(&arg, '\0'); |
| 201 | if (*in2 < 0) |
| 202 | die(wrap_arg_usage); |
| 203 | |
| 204 | if (!*wrap) |
| 205 | *wrap = DEFAULT_WRAPLEN; |
| 206 | if (!*in1) |
| 207 | *in1 = DEFAULT_INDENT1; |
| 208 | if (!*in2) |
| 209 | *in2 = DEFAULT_INDENT2; |
| 210 | if (*wrap && |
| 211 | ((*in1 && *wrap <= *in1) || |
| 212 | (*in2 && *wrap <= *in2))) |
| 213 | die(wrap_arg_usage); |
| 214 | } |
| 215 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 216 | void shortlog_init(struct shortlog *log) |
| 217 | { |
| 218 | memset(log, 0, sizeof(*log)); |
| 219 | |
| 220 | read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix); |
| 221 | |
| 222 | log->list.strdup_paths = 1; |
| 223 | log->wrap = DEFAULT_WRAPLEN; |
| 224 | log->in1 = DEFAULT_INDENT1; |
| 225 | log->in2 = DEFAULT_INDENT2; |
| 226 | } |
| 227 | |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 228 | int cmd_shortlog(int argc, const char **argv, const char *prefix) |
| 229 | { |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 230 | struct shortlog log; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 231 | struct rev_info rev; |
Jonas Fonseca | abe549e | 2008-03-14 22:35:24 +0100 | [diff] [blame] | 232 | int nongit; |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 233 | |
Jonas Fonseca | abe549e | 2008-03-14 22:35:24 +0100 | [diff] [blame] | 234 | prefix = setup_git_directory_gently(&nongit); |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 235 | shortlog_init(&log); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 236 | |
Johannes Schindelin | 6d6ab61 | 2006-11-21 21:12:06 +0100 | [diff] [blame] | 237 | /* since -n is a shadowed rev argument, parse our args first */ |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 238 | while (argc > 1) { |
| 239 | if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered")) |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 240 | log.sort_by_number = 1; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 241 | else if (!strcmp(argv[1], "-s") || |
| 242 | !strcmp(argv[1], "--summary")) |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 243 | log.summary = 1; |
Junio C Hamano | 4602c17 | 2007-12-07 17:19:31 -0800 | [diff] [blame] | 244 | else if (!strcmp(argv[1], "-e") || |
| 245 | !strcmp(argv[1], "--email")) |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 246 | log.email = 1; |
Junio C Hamano | 3d711d9 | 2007-04-08 01:28:00 -0700 | [diff] [blame] | 247 | else if (!prefixcmp(argv[1], "-w")) { |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 248 | log.wrap_lines = 1; |
| 249 | parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap); |
Junio C Hamano | 3d711d9 | 2007-04-08 01:28:00 -0700 | [diff] [blame] | 250 | } |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 251 | else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) |
| 252 | usage(shortlog_usage); |
| 253 | else |
Johannes Schindelin | 6d6ab61 | 2006-11-21 21:12:06 +0100 | [diff] [blame] | 254 | break; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 255 | argv++; |
| 256 | argc--; |
| 257 | } |
Johannes Schindelin | 6d6ab61 | 2006-11-21 21:12:06 +0100 | [diff] [blame] | 258 | init_revisions(&rev, prefix); |
| 259 | argc = setup_revisions(argc, argv, &rev, NULL); |
| 260 | if (argc > 1) |
| 261 | die ("unrecognized argument: %s", argv[1]); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 262 | |
Junio C Hamano | 3384a2d | 2007-12-11 10:09:04 -0800 | [diff] [blame] | 263 | /* assume HEAD if from a tty */ |
Jonas Fonseca | abe549e | 2008-03-14 22:35:24 +0100 | [diff] [blame] | 264 | if (!nongit && !rev.pending.nr && isatty(0)) |
Junio C Hamano | 3384a2d | 2007-12-11 10:09:04 -0800 | [diff] [blame] | 265 | add_head_to_pending(&rev); |
Junio C Hamano | 0497c62 | 2007-03-08 02:12:06 -0800 | [diff] [blame] | 266 | if (rev.pending.nr == 0) { |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 267 | read_from_stdin(&log); |
Junio C Hamano | 0497c62 | 2007-03-08 02:12:06 -0800 | [diff] [blame] | 268 | } |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 269 | else |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 270 | get_from_rev(&rev, &log); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 271 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 272 | shortlog_output(&log); |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | void shortlog_output(struct shortlog *log) |
| 277 | { |
| 278 | int i, j; |
| 279 | if (log->sort_by_number) |
| 280 | qsort(log->list.items, log->list.nr, sizeof(struct path_list_item), |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 281 | compare_by_number); |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 282 | for (i = 0; i < log->list.nr; i++) { |
| 283 | struct path_list *onelines = log->list.items[i].util; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 284 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 285 | if (log->summary) { |
| 286 | printf("%6d\t%s\n", onelines->nr, log->list.items[i].path); |
Nicolas Pitre | ac60c94 | 2006-11-21 15:49:45 -0500 | [diff] [blame] | 287 | } else { |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 288 | printf("%s (%d):\n", log->list.items[i].path, onelines->nr); |
Johannes Schindelin | 3714e7c | 2006-12-22 22:15:59 +0100 | [diff] [blame] | 289 | for (j = onelines->nr - 1; j >= 0; j--) { |
Junio C Hamano | 3d711d9 | 2007-04-08 01:28:00 -0700 | [diff] [blame] | 290 | const char *msg = onelines->items[j].path; |
| 291 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 292 | if (log->wrap_lines) { |
| 293 | int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap); |
| 294 | if (col != log->wrap) |
Junio C Hamano | 3d711d9 | 2007-04-08 01:28:00 -0700 | [diff] [blame] | 295 | putchar('\n'); |
| 296 | } |
| 297 | else |
| 298 | printf(" %s\n", msg); |
Johannes Schindelin | 3714e7c | 2006-12-22 22:15:59 +0100 | [diff] [blame] | 299 | } |
| 300 | putchar('\n'); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 301 | } |
| 302 | |
| 303 | onelines->strdup_paths = 1; |
| 304 | path_list_clear(onelines, 1); |
| 305 | free(onelines); |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 306 | log->list.items[i].util = NULL; |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 307 | } |
| 308 | |
Daniel Barkalow | 552bcac | 2008-02-25 18:24:14 -0500 | [diff] [blame] | 309 | log->list.strdup_paths = 1; |
| 310 | path_list_clear(&log->list, 1); |
| 311 | log->mailmap.strdup_paths = 1; |
| 312 | path_list_clear(&log->mailmap, 1); |
Johannes Schindelin | b8ec592 | 2006-10-22 13:23:31 +0200 | [diff] [blame] | 313 | } |