builtin-shortlog.con commit git-svn: add tests for command-line usage of init and clone commands (41337e2)
   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"
   7#include "utf8.h"
   8#include "mailmap.h"
   9
  10static const char shortlog_usage[] =
  11"git-shortlog [-n] [-s] [<commit-id>... ]";
  12
  13static char *common_repo_prefix;
  14
  15static int compare_by_number(const void *a1, const void *a2)
  16{
  17        const struct path_list_item *i1 = a1, *i2 = a2;
  18        const struct path_list *l1 = i1->util, *l2 = i2->util;
  19
  20        if (l1->nr < l2->nr)
  21                return 1;
  22        else if (l1->nr == l2->nr)
  23                return 0;
  24        else
  25                return -1;
  26}
  27
  28static struct path_list mailmap = {NULL, 0, 0, 0};
  29
  30static void insert_author_oneline(struct path_list *list,
  31                const char *author, int authorlen,
  32                const char *oneline, int onelinelen)
  33{
  34        const char *dot3 = common_repo_prefix;
  35        char *buffer, *p;
  36        struct path_list_item *item;
  37        struct path_list *onelines;
  38
  39        while (authorlen > 0 && isspace(author[authorlen - 1]))
  40                authorlen--;
  41
  42        buffer = xmemdupz(author, authorlen);
  43        item = path_list_insert(buffer, list);
  44        if (item->util == NULL)
  45                item->util = xcalloc(1, sizeof(struct path_list));
  46        else
  47                free(buffer);
  48
  49        if (!prefixcmp(oneline, "[PATCH")) {
  50                char *eob = strchr(oneline, ']');
  51
  52                if (eob) {
  53                        while (isspace(eob[1]) && eob[1] != '\n')
  54                                eob++;
  55                        if (eob - oneline < onelinelen) {
  56                                onelinelen -= eob - oneline;
  57                                oneline = eob;
  58                        }
  59                }
  60        }
  61
  62        while (onelinelen > 0 && isspace(oneline[0])) {
  63                oneline++;
  64                onelinelen--;
  65        }
  66        while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
  67                onelinelen--;
  68        buffer = xmemdupz(oneline, onelinelen);
  69
  70        if (dot3) {
  71                int dot3len = strlen(dot3);
  72                if (dot3len > 5) {
  73                        while ((p = strstr(buffer, dot3)) != NULL) {
  74                                int taillen = strlen(p) - dot3len;
  75                                memcpy(p, "/.../", 5);
  76                                memmove(p + 5, p + dot3len, taillen + 1);
  77                        }
  78                }
  79        }
  80
  81        onelines = item->util;
  82        if (onelines->nr >= onelines->alloc) {
  83                onelines->alloc = alloc_nr(onelines->nr);
  84                onelines->items = xrealloc(onelines->items,
  85                                onelines->alloc
  86                                * sizeof(struct path_list_item));
  87        }
  88
  89        onelines->items[onelines->nr].util = NULL;
  90        onelines->items[onelines->nr++].path = buffer;
  91}
  92
  93static void read_from_stdin(struct path_list *list)
  94{
  95        char buffer[1024];
  96
  97        while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
  98                char *bob;
  99                if ((buffer[0] == 'A' || buffer[0] == 'a') &&
 100                                !prefixcmp(buffer + 1, "uthor: ") &&
 101                                (bob = strchr(buffer + 7, '<')) != NULL) {
 102                        char buffer2[1024], offset = 0;
 103
 104                        if (map_email(&mailmap, bob + 1, buffer, sizeof(buffer)))
 105                                bob = buffer + strlen(buffer);
 106                        else {
 107                                offset = 8;
 108                                while (buffer + offset < bob &&
 109                                       isspace(bob[-1]))
 110                                        bob--;
 111                        }
 112
 113                        while (fgets(buffer2, sizeof(buffer2), stdin) &&
 114                                        buffer2[0] != '\n')
 115                                ; /* chomp input */
 116                        if (fgets(buffer2, sizeof(buffer2), stdin)) {
 117                                int l2 = strlen(buffer2);
 118                                int i;
 119                                for (i = 0; i < l2; i++)
 120                                        if (!isspace(buffer2[i]))
 121                                                break;
 122                                insert_author_oneline(list,
 123                                                buffer + offset,
 124                                                bob - buffer - offset,
 125                                                buffer2 + i, l2 - i);
 126                        }
 127                }
 128        }
 129}
 130
 131static void get_from_rev(struct rev_info *rev, struct path_list *list)
 132{
 133        char scratch[1024];
 134        struct commit *commit;
 135
 136        prepare_revision_walk(rev);
 137        while ((commit = get_revision(rev)) != NULL) {
 138                const char *author = NULL, *oneline, *buffer;
 139                int authorlen = authorlen, onelinelen;
 140
 141                /* get author and oneline */
 142                for (buffer = commit->buffer; buffer && *buffer != '\0' &&
 143                                *buffer != '\n'; ) {
 144                        const char *eol = strchr(buffer, '\n');
 145
 146                        if (eol == NULL)
 147                                eol = buffer + strlen(buffer);
 148                        else
 149                                eol++;
 150
 151                        if (!prefixcmp(buffer, "author ")) {
 152                                char *bracket = strchr(buffer, '<');
 153
 154                                if (bracket == NULL || bracket > eol)
 155                                        die("Invalid commit buffer: %s",
 156                                            sha1_to_hex(commit->object.sha1));
 157
 158                                if (map_email(&mailmap, bracket + 1, scratch,
 159                                                        sizeof(scratch))) {
 160                                        author = scratch;
 161                                        authorlen = strlen(scratch);
 162                                } else {
 163                                        if (bracket[-1] == ' ')
 164                                                bracket--;
 165
 166                                        author = buffer + 7;
 167                                        authorlen = bracket - buffer - 7;
 168                                }
 169                        }
 170                        buffer = eol;
 171                }
 172
 173                if (author == NULL)
 174                        die ("Missing author: %s",
 175                                        sha1_to_hex(commit->object.sha1));
 176
 177                if (buffer == NULL || *buffer == '\0') {
 178                        oneline = "<none>";
 179                        onelinelen = sizeof(oneline) + 1;
 180                } else {
 181                        char *eol;
 182
 183                        oneline = buffer + 1;
 184                        eol = strchr(oneline, '\n');
 185                        if (eol == NULL)
 186                                onelinelen = strlen(oneline);
 187                        else
 188                                onelinelen = eol - oneline;
 189                }
 190
 191                insert_author_oneline(list,
 192                                author, authorlen, oneline, onelinelen);
 193        }
 194
 195}
 196
 197static int parse_uint(char const **arg, int comma)
 198{
 199        unsigned long ul;
 200        int ret;
 201        char *endp;
 202
 203        ul = strtoul(*arg, &endp, 10);
 204        if (endp != *arg && *endp && *endp != comma)
 205                return -1;
 206        ret = (int) ul;
 207        if (ret != ul)
 208                return -1;
 209        *arg = endp;
 210        if (**arg)
 211                (*arg)++;
 212        return ret;
 213}
 214
 215static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
 216#define DEFAULT_WRAPLEN 76
 217#define DEFAULT_INDENT1 6
 218#define DEFAULT_INDENT2 9
 219
 220static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
 221{
 222        arg += 2; /* skip -w */
 223
 224        *wrap = parse_uint(&arg, ',');
 225        if (*wrap < 0)
 226                die(wrap_arg_usage);
 227        *in1 = parse_uint(&arg, ',');
 228        if (*in1 < 0)
 229                die(wrap_arg_usage);
 230        *in2 = parse_uint(&arg, '\0');
 231        if (*in2 < 0)
 232                die(wrap_arg_usage);
 233
 234        if (!*wrap)
 235                *wrap = DEFAULT_WRAPLEN;
 236        if (!*in1)
 237                *in1 = DEFAULT_INDENT1;
 238        if (!*in2)
 239                *in2 = DEFAULT_INDENT2;
 240        if (*wrap &&
 241            ((*in1 && *wrap <= *in1) ||
 242             (*in2 && *wrap <= *in2)))
 243                die(wrap_arg_usage);
 244}
 245
 246int cmd_shortlog(int argc, const char **argv, const char *prefix)
 247{
 248        struct rev_info rev;
 249        struct path_list list = { NULL, 0, 0, 1 };
 250        int i, j, sort_by_number = 0, summary = 0;
 251        int wrap_lines = 0;
 252        int wrap = DEFAULT_WRAPLEN;
 253        int in1 = DEFAULT_INDENT1;
 254        int in2 = DEFAULT_INDENT2;
 255
 256        /* since -n is a shadowed rev argument, parse our args first */
 257        while (argc > 1) {
 258                if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
 259                        sort_by_number = 1;
 260                else if (!strcmp(argv[1], "-s") ||
 261                                !strcmp(argv[1], "--summary"))
 262                        summary = 1;
 263                else if (!prefixcmp(argv[1], "-w")) {
 264                        wrap_lines = 1;
 265                        parse_wrap_args(argv[1], &in1, &in2, &wrap);
 266                }
 267                else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
 268                        usage(shortlog_usage);
 269                else
 270                        break;
 271                argv++;
 272                argc--;
 273        }
 274        init_revisions(&rev, prefix);
 275        argc = setup_revisions(argc, argv, &rev, NULL);
 276        if (argc > 1)
 277                die ("unrecognized argument: %s", argv[1]);
 278
 279        read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
 280
 281        if (rev.pending.nr == 0) {
 282                if (isatty(0))
 283                        fprintf(stderr, "(reading log to summarize from standard input)\n");
 284                read_from_stdin(&list);
 285        }
 286        else
 287                get_from_rev(&rev, &list);
 288
 289        if (sort_by_number)
 290                qsort(list.items, list.nr, sizeof(struct path_list_item),
 291                        compare_by_number);
 292
 293        for (i = 0; i < list.nr; i++) {
 294                struct path_list *onelines = list.items[i].util;
 295
 296                if (summary) {
 297                        printf("%s: %d\n", list.items[i].path, onelines->nr);
 298                } else {
 299                        printf("%s (%d):\n", list.items[i].path, onelines->nr);
 300                        for (j = onelines->nr - 1; j >= 0; j--) {
 301                                const char *msg = onelines->items[j].path;
 302
 303                                if (wrap_lines) {
 304                                        int col = print_wrapped_text(msg, in1, in2, wrap);
 305                                        if (col != wrap)
 306                                                putchar('\n');
 307                                }
 308                                else
 309                                        printf("      %s\n", msg);
 310                        }
 311                        putchar('\n');
 312                }
 313
 314                onelines->strdup_paths = 1;
 315                path_list_clear(onelines, 1);
 316                free(onelines);
 317                list.items[i].util = NULL;
 318        }
 319
 320        list.strdup_paths = 1;
 321        path_list_clear(&list, 1);
 322        mailmap.strdup_paths = 1;
 323        path_list_clear(&mailmap, 1);
 324
 325        return 0;
 326}