Merge branch 'travis-ci' into integration
[git] / builtin / shortlog.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "commit.h"
4 #include "string-list.h"
5 #include "revision.h"
6 #include "shortlog.h"
7 #include "parse-options.h"
8
9 static char const * const shortlog_usage[] = {
10         N_("git shortlog [<options>] [<revision-range>] [[--] [<path>...]]"),
11         NULL
12 };
13
14 static void read_from_stdin(struct shortlog *log)
15 {
16         char author[1024], oneline[1024];
17
18         while (fgets(author, sizeof(author), stdin) != NULL) {
19                 if (!(author[0] == 'A' || author[0] == 'a') ||
20                     !starts_with(author + 1, "uthor: "))
21                         continue;
22                 while (fgets(oneline, sizeof(oneline), stdin) &&
23                        oneline[0] != '\n')
24                         ; /* discard headers */
25                 while (fgets(oneline, sizeof(oneline), stdin) &&
26                        oneline[0] == '\n')
27                         ; /* discard blanks */
28                 shortlog_insert_one_record(log, author + 8, oneline);
29         }
30 }
31
32 static void get_from_rev(struct rev_info *rev, struct shortlog *log)
33 {
34         struct commit *commit;
35
36         if (prepare_revision_walk(rev))
37                 die(_("revision walk setup failed"));
38         while ((commit = get_revision(rev)) != NULL)
39                 shortlog_add_commit(log, commit);
40 }
41
42 static int parse_uint(char const **arg, int comma, int defval)
43 {
44         unsigned long ul;
45         int ret;
46         char *endp;
47
48         ul = strtoul(*arg, &endp, 10);
49         if (*endp && *endp != comma)
50                 return -1;
51         if (ul > INT_MAX)
52                 return -1;
53         ret = *arg == endp ? defval : (int)ul;
54         *arg = *endp ? endp + 1 : endp;
55         return ret;
56 }
57
58 static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
59
60 static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
61 {
62         struct shortlog *log = opt->value;
63
64         log->wrap_lines = !unset;
65         if (unset)
66                 return 0;
67         if (!arg) {
68                 log->wrap = DEFAULT_WRAPLEN;
69                 log->in1 = DEFAULT_INDENT1;
70                 log->in2 = DEFAULT_INDENT2;
71                 return 0;
72         }
73
74         log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
75         log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
76         log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
77         if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
78                 return error(wrap_arg_usage);
79         if (log->wrap &&
80             ((log->in1 && log->wrap <= log->in1) ||
81              (log->in2 && log->wrap <= log->in2)))
82                 return error(wrap_arg_usage);
83         return 0;
84 }
85
86 int cmd_shortlog(int argc, const char **argv, const char *prefix)
87 {
88         static struct shortlog log;
89         static struct rev_info rev;
90         int nongit = !startup_info->have_repository;
91
92         static const struct option options[] = {
93                 OPT_BOOL('n', "numbered", &log.sort_by_number,
94                          N_("sort output according to the number of commits per author")),
95                 OPT_BOOL('s', "summary", &log.summary,
96                          N_("Suppress commit descriptions, only provides commit count")),
97                 OPT_BOOL('e', "email", &log.email,
98                          N_("Show the email address of each author")),
99                 { OPTION_CALLBACK, 'w', NULL, &log, N_("w[,i1[,i2]]"),
100                         N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args },
101                 OPT_END(),
102         };
103
104         struct parse_opt_ctx_t ctx;
105
106         git_config(git_default_config, NULL);
107         shortlog_init(&log);
108         init_revisions(&rev, prefix);
109         parse_options_start(&ctx, argc, argv, prefix, options,
110                             PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
111
112         for (;;) {
113                 switch (parse_options_step(&ctx, options, shortlog_usage)) {
114                 case PARSE_OPT_HELP:
115                         exit(129);
116                 case PARSE_OPT_DONE:
117                         goto parse_done;
118                 }
119                 parse_revision_opt(&rev, &ctx, options, shortlog_usage);
120         }
121 parse_done:
122         argc = parse_options_end(&ctx);
123
124         if (setup_revisions(argc, argv, &rev, NULL) != 1) {
125                 error(_("unrecognized argument: %s"), argv[1]);
126                 usage_with_options(shortlog_usage, options);
127         }
128
129         log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
130         log.abbrev = rev.abbrev;
131
132         /* assume HEAD if from a tty */
133         if (!nongit && !rev.pending.nr && isatty(0))
134                 add_head_to_pending(&rev);
135         if (rev.pending.nr == 0) {
136                 if (isatty(0))
137                         fprintf(stderr, _("(reading log message from standard input)\n"));
138                 read_from_stdin(&log);
139         }
140         else
141                 get_from_rev(&rev, &log);
142
143         shortlog_output(&log);
144         return 0;
145 }