log: --author-date-order

Sometimes people would want to view the commits in parallel histories in the order of author dates, not committer dates. Teach "topo-order" sort machinery to do so, using a commit-info slab to record the author dates of each commit, and prio-queue to sort them. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jun 7, 2013 at 10:35 UTC 81c6b38b67019eff48e8e5c324bb77f6c796c0b5
4 files changed +83 -1
Documentation/rev-list-options.txt
+4
@@ -617,6 +617,10 @@ By default, the commits are shown in reverse chronological order.
617 Show no parents before all of its children are shown, but
618 otherwise show commits in the commit timestamp order.
619
620 +--author-date-order::
621 + Show no parents before all of its children are shown, but
622 + otherwise show commits in the author timestamp order.
623 +
624 --topo-order::
625 Show no parents before all of its children are shown, and
626 avoid showing commits on multiple lines of history
commit.c
+74
@@ -510,6 +510,68 @@ struct commit *pop_commit(struct commit_list **stack)
510 /* count number of children that have not been emitted */
511 define_commit_slab(indegree_slab, int);
512
513 +/* record author-date for each commit object */
514 +define_commit_slab(author_date_slab, unsigned long);
515 +
516 +static void record_author_date(struct author_date_slab *author_date,
517 + struct commit *commit)
518 +{
519 + const char *buf, *line_end;
520 + char *buffer = NULL;
521 + struct ident_split ident;
522 + char *date_end;
523 + unsigned long date;
524 +
525 + if (!commit->buffer) {
526 + unsigned long size;
527 + enum object_type type;
528 + buffer = read_sha1_file(commit->object.sha1, &type, &size);
529 + if (!buffer)
530 + return;
531 + }
532 +
533 + for (buf = commit->buffer ? commit->buffer : buffer;
534 + buf;
535 + buf = line_end + 1) {
536 + line_end = strchrnul(buf, '\n');
537 + if (prefixcmp(buf, "author ")) {
538 + if (!line_end[0] || line_end[1] == '\n')
539 + return; /* end of header */
540 + continue;
541 + }
542 + if (split_ident_line(&ident,
543 + buf + strlen("author "),
544 + line_end - (buf + strlen("author "))) ||
545 + !ident.date_begin || !ident.date_end)
546 + goto fail_exit; /* malformed "author" line */
547 + break;
548 + }
549 +
550 + date = strtoul(ident.date_begin, &date_end, 10);
551 + if (date_end != ident.date_end)
552 + goto fail_exit; /* malformed date */
553 + *(author_date_slab_at(author_date, commit)) = date;
554 +
555 +fail_exit:
556 + free(buffer);
557 +}
558 +
559 +static int compare_commits_by_author_date(const void *a_, const void *b_,
560 + void *cb_data)
561 +{
562 + const struct commit *a = a_, *b = b_;
563 + struct author_date_slab *author_date = cb_data;
564 + unsigned long a_date = *(author_date_slab_at(author_date, a));
565 + unsigned long b_date = *(author_date_slab_at(author_date, b));
566 +
567 + /* newer commits with larger date first */
568 + if (a_date < b_date)
569 + return 1;
570 + else if (a_date > b_date)
571 + return -1;
572 + return 0;
573 +}
574 +
575 static int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
576 {
577 const struct commit *a = a_, *b = b_;
@@ -531,6 +593,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
593 struct indegree_slab indegree;
594 struct prio_queue queue;
595 struct commit *commit;
596 + struct author_date_slab author_date;
597
598 if (!orig)
599 return;
@@ -538,6 +601,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
601
602 init_indegree_slab(&indegree);
603 memset(&queue, '\0', sizeof(queue));
604 +
605 switch (sort_order) {
606 default: /* REV_SORT_IN_GRAPH_ORDER */
607 queue.compare = NULL;
@@ -545,12 +609,20 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
609 case REV_SORT_BY_COMMIT_DATE:
610 queue.compare = compare_commits_by_commit_date;
611 break;
612 + case REV_SORT_BY_AUTHOR_DATE:
613 + init_author_date_slab(&author_date);
614 + queue.compare = compare_commits_by_author_date;
615 + queue.cb_data = &author_date;
616 + break;
617 }
618
619 /* Mark them and clear the indegree */
620 for (next = orig; next; next = next->next) {
621 struct commit *commit = next->item;
622 *(indegree_slab_at(&indegree, commit)) = 1;
623 + /* also record the author dates, if needed */
624 + if (sort_order == REV_SORT_BY_AUTHOR_DATE)
625 + record_author_date(&author_date, commit);
626 }
627
628 /* update the indegree */
@@ -621,6 +693,8 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
693
694 clear_indegree_slab(&indegree);
695 clear_prio_queue(&queue);
696 + if (sort_order == REV_SORT_BY_AUTHOR_DATE)
697 + clear_author_date_slab(&author_date);
698 }
699
700 /* merge-base stuff */
commit.h
+2 -1
@@ -142,7 +142,8 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
142
143 enum rev_sort_order {
144 REV_SORT_IN_GRAPH_ORDER = 0,
145 - REV_SORT_BY_COMMIT_DATE
145 + REV_SORT_BY_COMMIT_DATE,
146 + REV_SORT_BY_AUTHOR_DATE
147 };
148
149 /*
revision.c
+3
@@ -1393,6 +1393,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1393 } else if (!strcmp(arg, "--date-order")) {
1394 revs->sort_order = REV_SORT_BY_COMMIT_DATE;
1395 revs->topo_order = 1;
1396 + } else if (!strcmp(arg, "--author-date-order")) {
1397 + revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
1398 + revs->topo_order = 1;
1399 } else if (!prefixcmp(arg, "--early-output")) {
1400 int count = 100;
1401 switch (arg[14]) {