From 08f704f294e4a3525b405c2cb1f4ee85e73fd92c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Jun 2013 16:07:14 -0700 Subject: [PATCH 1/9] toposort: rename "lifo" field The primary invariant of sort_in_topological_order() is that a parent commit is not emitted until all children of it are. When traversing a forked history like this with "git log C E": A----B----C \ D----E we ensure that A is emitted after all of B, C, D, and E are done, B has to wait until C is done, and D has to wait until E is done. In some applications, however, we would further want to control how these child commits B, C, D and E on two parallel ancestry chains are shown. Most of the time, we would want to see C and B emitted together, and then E and D, and finally A (i.e. the --topo-order output). The "lifo" parameter of the sort_in_topological_order() function is used to control this behaviour. We start the traversal by knowing two commits, C and E. While keeping in mind that we also need to inspect E later, we pick C first to inspect, and we notice and record that B needs to be inspected. By structuring the "work to be done" set as a LIFO stack, we ensure that B is inspected next, before other in-flight commits we had known that we will need to inspect, e.g. E. When showing in --date-order, we would want to see commits ordered by timestamps, i.e. show C, E, B and D in this order before showing A, possibly mixing commits from two parallel histories together. When "lifo" parameter is set to false, the function keeps the "work to be done" set sorted in the date order to realize this semantics. After inspecting C, we add B to the "work to be done" set, but the next commit we inspect from the set is E which is newer than B. The name "lifo", however, is too strongly tied to the way how the function implements its behaviour, and does not describe what the behaviour _means_. Replace this field with an enum rev_sort_order, with two possible values: REV_SORT_IN_GRAPH_ORDER and REV_SORT_BY_COMMIT_DATE, and update the existing code. The mechanical replacement rule is: "lifo == 0" is equivalent to "sort_order == REV_SORT_BY_COMMIT_DATE" "lifo == 1" is equivalent to "sort_order == REV_SORT_IN_GRAPH_ORDER" Signed-off-by: Junio C Hamano --- builtin/log.c | 2 +- builtin/show-branch.c | 14 ++++++++------ commit.c | 12 ++++++++---- commit.h | 14 +++++++++++--- revision.c | 10 +++++----- revision.h | 6 +++++- 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 8f0b2e84fe..8d26042b01 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -205,7 +205,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list) int i = revs->early_output; int show_header = 1; - sort_in_topological_order(&list, revs->lifo); + sort_in_topological_order(&list, revs->sort_order); while (list && i) { struct commit *commit = list->item; switch (simplify_commit(revs, commit)) { diff --git a/builtin/show-branch.c b/builtin/show-branch.c index d208fd6c68..7c57985c40 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -631,7 +631,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int num_rev, i, extra = 0; int all_heads = 0, all_remotes = 0; int all_mask, all_revs; - int lifo = 1; + enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER; char head[128]; const char *head_p; int head_len; @@ -666,15 +666,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) N_("show possible merge bases")), OPT_BOOLEAN(0, "independent", &independent, N_("show refs unreachable from any other ref")), - OPT_BOOLEAN(0, "topo-order", &lifo, - N_("show commits in topological order")), + OPT_SET_INT(0, "topo-order", &sort_order, + N_("show commits in topological order"), + REV_SORT_IN_GRAPH_ORDER), OPT_BOOLEAN(0, "topics", &topics, N_("show only commits not on the first branch")), OPT_SET_INT(0, "sparse", &dense, N_("show merges reachable from only one tip"), 0), - OPT_SET_INT(0, "date-order", &lifo, + OPT_SET_INT(0, "date-order", &sort_order, N_("show commits where no parent comes before its " - "children"), 0), + "children"), + REV_SORT_BY_COMMIT_DATE), { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("[,]"), N_("show most recent ref-log entries starting at " "base"), @@ -901,7 +903,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) exit(0); /* Sort topologically */ - sort_in_topological_order(&seen, lifo); + sort_in_topological_order(&seen, sort_order); /* Give names to commits */ if (!sha1_name && !no_name) diff --git a/commit.c b/commit.c index f97456ddfa..11b9635df2 100644 --- a/commit.c +++ b/commit.c @@ -512,7 +512,7 @@ define_commit_slab(indegree_slab, int); /* * Performs an in-place topological sort on the list supplied. */ -void sort_in_topological_order(struct commit_list ** list, int lifo) +void sort_in_topological_order(struct commit_list ** list, enum rev_sort_order sort_order) { struct commit_list *next, *orig = *list; struct commit_list *work, **insert; @@ -561,7 +561,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) } /* process the list in topological order */ - if (!lifo) + if (sort_order != REV_SORT_IN_GRAPH_ORDER) commit_list_sort_by_date(&work); pptr = list; @@ -588,10 +588,14 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) * guaranteeing topological order. */ if (--(*pi) == 1) { - if (!lifo) + switch (sort_order) { + case REV_SORT_BY_COMMIT_DATE: commit_list_insert_by_date(parent, &work); - else + break; + default: /* REV_SORT_IN_GRAPH_ORDER */ commit_list_insert(parent, &work); + break; + } } } /* diff --git a/commit.h b/commit.h index 70e749d69f..247e474e44 100644 --- a/commit.h +++ b/commit.h @@ -139,15 +139,23 @@ struct commit *pop_commit(struct commit_list **stack); void clear_commit_marks(struct commit *commit, unsigned int mark); void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark); + +enum rev_sort_order { + REV_SORT_IN_GRAPH_ORDER = 0, + REV_SORT_BY_COMMIT_DATE +}; + /* * Performs an in-place topological sort of list supplied. * * invariant of resulting list is: * a reachable from b => ord(b) < ord(a) - * in addition, when lifo == 0, commits on parallel tracks are - * sorted in the dates order. + * sort_order further specifies: + * REV_SORT_IN_GRAPH_ORDER: try to show a commit on a single-parent + * chain together. + * REV_SORT_BY_COMMIT_DATE: show eligible commits in committer-date order. */ -void sort_in_topological_order(struct commit_list ** list, int lifo); +void sort_in_topological_order(struct commit_list **, enum rev_sort_order); struct commit_graft { unsigned char sha1[20]; diff --git a/revision.c b/revision.c index cf620c6b36..966ebbc542 100644 --- a/revision.c +++ b/revision.c @@ -1038,7 +1038,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) DIFF_OPT_SET(&revs->pruning, QUICK); revs->pruning.add_remove = file_add_remove; revs->pruning.change = file_change; - revs->lifo = 1; + revs->sort_order = REV_SORT_IN_GRAPH_ORDER; revs->dense = 1; revs->prefix = prefix; revs->max_age = -1; @@ -1373,7 +1373,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--merge")) { revs->show_merge = 1; } else if (!strcmp(arg, "--topo-order")) { - revs->lifo = 1; + revs->sort_order = REV_SORT_IN_GRAPH_ORDER; revs->topo_order = 1; } else if (!strcmp(arg, "--simplify-merges")) { revs->simplify_merges = 1; @@ -1391,7 +1391,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->prune = 1; load_ref_decorations(DECORATE_SHORT_REFS); } else if (!strcmp(arg, "--date-order")) { - revs->lifo = 0; + revs->sort_order = REV_SORT_BY_COMMIT_DATE; revs->topo_order = 1; } else if (!prefixcmp(arg, "--early-output")) { int count = 100; @@ -2165,7 +2165,7 @@ int prepare_revision_walk(struct rev_info *revs) if (limit_list(revs) < 0) return -1; if (revs->topo_order) - sort_in_topological_order(&revs->commits, revs->lifo); + sort_in_topological_order(&revs->commits, revs->sort_order); if (revs->simplify_merges) simplify_merges(revs); if (revs->children.name) @@ -2480,7 +2480,7 @@ static void create_boundary_commit_list(struct rev_info *revs) * If revs->topo_order is set, sort the boundary commits * in topological order */ - sort_in_topological_order(&revs->commits, revs->lifo); + sort_in_topological_order(&revs->commits, revs->sort_order); } static struct commit *get_revision_internal(struct rev_info *revs) diff --git a/revision.h b/revision.h index 5da09ee3ef..2a5e325a36 100644 --- a/revision.h +++ b/revision.h @@ -4,6 +4,7 @@ #include "parse-options.h" #include "grep.h" #include "notes.h" +#include "commit.h" #define SEEN (1u<<0) #define UNINTERESTING (1u<<1) @@ -60,6 +61,10 @@ struct rev_info { const char *prefix; const char *def; struct pathspec prune_data; + + /* topo-sort */ + enum rev_sort_order sort_order; + unsigned int early_output:1, ignore_missing:1; @@ -70,7 +75,6 @@ struct rev_info { show_all:1, remove_empty_trees:1, simplify_history:1, - lifo:1, topo_order:1, simplify_merges:1, simplify_by_decoration:1, From b4b594a3154078430b04fad4f6ffbed9c7274be5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Jun 2013 19:13:50 -0700 Subject: [PATCH 2/9] prio-queue: priority queue of pointers to structs Traditionally we used a singly linked list of commits to hold a set of in-flight commits while traversing history. The most typical use of the list is to add commits that are newly discovered to it, keep the list sorted by commit timestamp, pick up the newest one from the list, and keep digging. The cost of keeping the singly linked list sorted is nontrivial, and this typical use pattern better matches a priority queue. Introduce a prio-queue structure, that can be used either as a LIFO stack, or a priority queue. This will be used in the next patch to hold in-flight commits during sort-in-topological-order. Tests and the idea to make it usable for any "void *" pointers to "things" are by Jeff King. Bugs are mine. Signed-off-by: Junio C Hamano --- .gitignore | 1 + Makefile | 3 ++ prio-queue.c | 71 +++++++++++++++++++++++++++++++++++++++++++ prio-queue.h | 45 +++++++++++++++++++++++++++ t/t0009-prio-queue.sh | 50 ++++++++++++++++++++++++++++++ test-prio-queue.c | 39 ++++++++++++++++++++++++ 6 files changed, 209 insertions(+) create mode 100644 prio-queue.c create mode 100644 prio-queue.h create mode 100755 t/t0009-prio-queue.sh create mode 100644 test-prio-queue.c diff --git a/.gitignore b/.gitignore index 6669bf0c6c..b753817ad4 100644 --- a/.gitignore +++ b/.gitignore @@ -190,6 +190,7 @@ /test-mktemp /test-parse-options /test-path-utils +/test-prio-queue /test-regex /test-revision-walking /test-run-command diff --git a/Makefile b/Makefile index 598d6313da..0246194abe 100644 --- a/Makefile +++ b/Makefile @@ -552,6 +552,7 @@ TEST_PROGRAMS_NEED_X += test-mergesort TEST_PROGRAMS_NEED_X += test-mktemp TEST_PROGRAMS_NEED_X += test-parse-options TEST_PROGRAMS_NEED_X += test-path-utils +TEST_PROGRAMS_NEED_X += test-prio-queue TEST_PROGRAMS_NEED_X += test-regex TEST_PROGRAMS_NEED_X += test-revision-walking TEST_PROGRAMS_NEED_X += test-run-command @@ -685,6 +686,7 @@ LIB_H += parse-options.h LIB_H += patch-ids.h LIB_H += pathspec.h LIB_H += pkt-line.h +LIB_H += prio-queue.h LIB_H += progress.h LIB_H += prompt.h LIB_H += quote.h @@ -824,6 +826,7 @@ LIB_OBJS += pathspec.o LIB_OBJS += pkt-line.o LIB_OBJS += preload-index.o LIB_OBJS += pretty.o +LIB_OBJS += prio-queue.o LIB_OBJS += progress.o LIB_OBJS += prompt.o LIB_OBJS += quote.o diff --git a/prio-queue.c b/prio-queue.c new file mode 100644 index 0000000000..f2a4973a01 --- /dev/null +++ b/prio-queue.c @@ -0,0 +1,71 @@ +#include "cache.h" +#include "commit.h" +#include "prio-queue.h" + +void clear_prio_queue(struct prio_queue *queue) +{ + free(queue->array); + queue->nr = 0; + queue->alloc = 0; + queue->array = NULL; +} + +void prio_queue_put(struct prio_queue *queue, void *thing) +{ + prio_queue_compare_fn compare = queue->compare; + int ix, parent; + + /* Append at the end */ + ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc); + queue->array[queue->nr++] = thing; + if (!compare) + return; /* LIFO */ + + /* Bubble up the new one */ + for (ix = queue->nr - 1; ix; ix = parent) { + parent = (ix - 1) / 2; + if (compare(queue->array[parent], queue->array[ix], + queue->cb_data) <= 0) + break; + + thing = queue->array[parent]; + queue->array[parent] = queue->array[ix]; + queue->array[ix] = thing; + } +} + +void *prio_queue_get(struct prio_queue *queue) +{ + void *result, *swap; + int ix, child; + prio_queue_compare_fn compare = queue->compare; + + if (!queue->nr) + return NULL; + if (!compare) + return queue->array[--queue->nr]; /* LIFO */ + + result = queue->array[0]; + if (!--queue->nr) + return result; + + queue->array[0] = queue->array[queue->nr]; + + /* Push down the one at the root */ + for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) { + child = ix * 2 + 1; /* left */ + if ((child + 1 < queue->nr) && + (compare(queue->array[child], queue->array[child + 1], + queue->cb_data) >= 0)) + child++; /* use right child */ + + if (compare(queue->array[ix], queue->array[child], + queue->cb_data) <= 0) + break; + + swap = queue->array[child]; + queue->array[child] = queue->array[ix]; + queue->array[ix] = swap; + } + return result; +} diff --git a/prio-queue.h b/prio-queue.h new file mode 100644 index 0000000000..c766abec3d --- /dev/null +++ b/prio-queue.h @@ -0,0 +1,45 @@ +#ifndef PRIO_QUEUE_H +#define PRIO_QUEUE_H + +/* + * A priority queue implementation, primarily for keeping track of + * commits in the 'date-order' so that we process them from new to old + * as they are discovered, but can be used to hold any pointer to + * struct. The caller is responsible for supplying a function to + * compare two "things". + * + * Alternatively, this data structure can also be used as a LIFO stack + * by specifying NULL as the comparison function. + */ + +/* + * Compare two "things", one and two; the third parameter is cb_data + * in the prio_queue structure. The result is returned as a sign of + * the return value, being the same as the sign of the result of + * subtracting "two" from "one" (i.e. negative if "one" sorts earlier + * than "two"). + */ +typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data); + +struct prio_queue { + prio_queue_compare_fn compare; + void *cb_data; + int alloc, nr; + void **array; +}; + +/* + * Add the "thing" to the queue. + */ +extern void prio_queue_put(struct prio_queue *, void *thing); + +/* + * Extract the "thing" that compares the smallest out of the queue, + * or NULL. If compare function is NULL, the queue acts as a LIFO + * stack. + */ +extern void *prio_queue_get(struct prio_queue *); + +extern void clear_prio_queue(struct prio_queue *); + +#endif /* PRIO_QUEUE_H */ diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh new file mode 100755 index 0000000000..94045c3fad --- /dev/null +++ b/t/t0009-prio-queue.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='basic tests for priority queue implementation' +. ./test-lib.sh + +cat >expect <<'EOF' +1 +2 +3 +4 +5 +5 +6 +7 +8 +9 +10 +EOF +test_expect_success 'basic ordering' ' + test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +2 +3 +4 +1 +5 +6 +EOF +test_expect_success 'mixed put and get' ' + test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +1 +2 +NULL +1 +2 +NULL +EOF +test_expect_success 'notice empty queue' ' + test-prio-queue 1 2 get get get 1 2 get get get >actual && + test_cmp expect actual +' + +test_done diff --git a/test-prio-queue.c b/test-prio-queue.c new file mode 100644 index 0000000000..7be72f0086 --- /dev/null +++ b/test-prio-queue.c @@ -0,0 +1,39 @@ +#include "cache.h" +#include "prio-queue.h" + +static int intcmp(const void *va, const void *vb, void *data) +{ + const int *a = va, *b = vb; + return *a - *b; +} + +static void show(int *v) +{ + if (!v) + printf("NULL\n"); + else + printf("%d\n", *v); + free(v); +} + +int main(int argc, char **argv) +{ + struct prio_queue pq = { intcmp }; + + while (*++argv) { + if (!strcmp(*argv, "get")) + show(prio_queue_get(&pq)); + else if (!strcmp(*argv, "dump")) { + int *v; + while ((v = prio_queue_get(&pq))) + show(v); + } + else { + int *v = malloc(sizeof(*v)); + *v = atoi(*argv); + prio_queue_put(&pq, v); + } + } + + return 0; +} From da24b1044f7dc85cda52d6423f5a794a7074fbf8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Jun 2013 21:58:12 -0700 Subject: [PATCH 3/9] sort-in-topological-order: use prio-queue Use the prio-queue data structure to implement a priority queue of commits sorted by committer date, when handling --date-order. The structure can also be used as a simple LIFO stack, which is a good match for --topo-order processing. Signed-off-by: Junio C Hamano --- commit.c | 75 ++++++++++++++++++++++++++++++---------------------- prio-queue.c | 13 +++++++++ prio-queue.h | 3 +++ 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/commit.c b/commit.c index 11b9635df2..8b84ebf2b7 100644 --- a/commit.c +++ b/commit.c @@ -9,6 +9,7 @@ #include "gpg-interface.h" #include "mergesort.h" #include "commit-slab.h" +#include "prio-queue.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@ -509,21 +510,42 @@ struct commit *pop_commit(struct commit_list **stack) /* count number of children that have not been emitted */ define_commit_slab(indegree_slab, int); +static int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused) +{ + const struct commit *a = a_, *b = b_; + /* newer commits with larger date first */ + if (a->date < b->date) + return 1; + else if (a->date > b->date) + return -1; + return 0; +} + /* * Performs an in-place topological sort on the list supplied. */ -void sort_in_topological_order(struct commit_list ** list, enum rev_sort_order sort_order) +void sort_in_topological_order(struct commit_list **list, enum rev_sort_order sort_order) { struct commit_list *next, *orig = *list; - struct commit_list *work, **insert; struct commit_list **pptr; struct indegree_slab indegree; + struct prio_queue queue; + struct commit *commit; if (!orig) return; *list = NULL; init_indegree_slab(&indegree); + memset(&queue, '\0', sizeof(queue)); + switch (sort_order) { + default: /* REV_SORT_IN_GRAPH_ORDER */ + queue.compare = NULL; + break; + case REV_SORT_BY_COMMIT_DATE: + queue.compare = compare_commits_by_commit_date; + break; + } /* Mark them and clear the indegree */ for (next = orig; next; next = next->next) { @@ -533,7 +555,7 @@ void sort_in_topological_order(struct commit_list ** list, enum rev_sort_order s /* update the indegree */ for (next = orig; next; next = next->next) { - struct commit_list * parents = next->item->parents; + struct commit_list *parents = next->item->parents; while (parents) { struct commit *parent = parents->item; int *pi = indegree_slab_at(&indegree, parent); @@ -551,30 +573,28 @@ void sort_in_topological_order(struct commit_list ** list, enum rev_sort_order s * * the tips serve as a starting set for the work queue. */ - work = NULL; - insert = &work; for (next = orig; next; next = next->next) { struct commit *commit = next->item; if (*(indegree_slab_at(&indegree, commit)) == 1) - insert = &commit_list_insert(commit, insert)->next; + prio_queue_put(&queue, commit); } - /* process the list in topological order */ - if (sort_order != REV_SORT_IN_GRAPH_ORDER) - commit_list_sort_by_date(&work); + /* + * This is unfortunate; the initial tips need to be shown + * in the order given from the revision traversal machinery. + */ + if (sort_order == REV_SORT_IN_GRAPH_ORDER) + prio_queue_reverse(&queue); + + /* We no longer need the commit list */ + free_commit_list(orig); pptr = list; *list = NULL; - while (work) { - struct commit *commit; - struct commit_list *parents, *work_item; + while ((commit = prio_queue_get(&queue)) != NULL) { + struct commit_list *parents; - work_item = work; - work = work_item->next; - work_item->next = NULL; - - commit = work_item->item; for (parents = commit->parents; parents ; parents = parents->next) { struct commit *parent = parents->item; int *pi = indegree_slab_at(&indegree, parent); @@ -587,27 +607,20 @@ void sort_in_topological_order(struct commit_list ** list, enum rev_sort_order s * when all their children have been emitted thereby * guaranteeing topological order. */ - if (--(*pi) == 1) { - switch (sort_order) { - case REV_SORT_BY_COMMIT_DATE: - commit_list_insert_by_date(parent, &work); - break; - default: /* REV_SORT_IN_GRAPH_ORDER */ - commit_list_insert(parent, &work); - break; - } - } + if (--(*pi) == 1) + prio_queue_put(&queue, parent); } /* - * work_item is a commit all of whose children - * have already been emitted. we can emit it now. + * all children of commit have already been + * emitted. we can emit it now. */ *(indegree_slab_at(&indegree, commit)) = 0; - *pptr = work_item; - pptr = &work_item->next; + + pptr = &commit_list_insert(commit, pptr)->next; } clear_indegree_slab(&indegree); + clear_prio_queue(&queue); } /* merge-base stuff */ diff --git a/prio-queue.c b/prio-queue.c index f2a4973a01..c9f8c6d253 100644 --- a/prio-queue.c +++ b/prio-queue.c @@ -2,6 +2,19 @@ #include "commit.h" #include "prio-queue.h" +void prio_queue_reverse(struct prio_queue *queue) +{ + int i, j; + + if (queue->compare != NULL) + die("BUG: prio_queue_reverse() on non-LIFO queue"); + for (i = 0; i <= (j = (queue->nr - 1) - i); i++) { + struct commit *swap = queue->array[i]; + queue->array[i] = queue->array[j]; + queue->array[j] = swap; + } +} + void clear_prio_queue(struct prio_queue *queue) { free(queue->array); diff --git a/prio-queue.h b/prio-queue.h index c766abec3d..9c3cd1f875 100644 --- a/prio-queue.h +++ b/prio-queue.h @@ -42,4 +42,7 @@ extern void *prio_queue_get(struct prio_queue *); extern void clear_prio_queue(struct prio_queue *); +/* Reverse the LIFO elements */ +extern void prio_queue_reverse(struct prio_queue *); + #endif /* PRIO_QUEUE_H */ From 81c6b38b67019eff48e8e5c324bb77f6c796c0b5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 7 Jun 2013 10:35:54 -0700 Subject: [PATCH 4/9] 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 --- Documentation/rev-list-options.txt | 4 ++ commit.c | 74 ++++++++++++++++++++++++++++++ commit.h | 3 +- revision.c | 3 ++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 3bdbf5e856..8302402c7d 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -617,6 +617,10 @@ By default, the commits are shown in reverse chronological order. Show no parents before all of its children are shown, but otherwise show commits in the commit timestamp order. +--author-date-order:: + Show no parents before all of its children are shown, but + otherwise show commits in the author timestamp order. + --topo-order:: Show no parents before all of its children are shown, and avoid showing commits on multiple lines of history diff --git a/commit.c b/commit.c index 8b84ebf2b7..076c1fa606 100644 --- a/commit.c +++ b/commit.c @@ -510,6 +510,68 @@ struct commit *pop_commit(struct commit_list **stack) /* count number of children that have not been emitted */ define_commit_slab(indegree_slab, int); +/* record author-date for each commit object */ +define_commit_slab(author_date_slab, unsigned long); + +static void record_author_date(struct author_date_slab *author_date, + struct commit *commit) +{ + const char *buf, *line_end; + char *buffer = NULL; + struct ident_split ident; + char *date_end; + unsigned long date; + + if (!commit->buffer) { + unsigned long size; + enum object_type type; + buffer = read_sha1_file(commit->object.sha1, &type, &size); + if (!buffer) + return; + } + + for (buf = commit->buffer ? commit->buffer : buffer; + buf; + buf = line_end + 1) { + line_end = strchrnul(buf, '\n'); + if (prefixcmp(buf, "author ")) { + if (!line_end[0] || line_end[1] == '\n') + return; /* end of header */ + continue; + } + if (split_ident_line(&ident, + buf + strlen("author "), + line_end - (buf + strlen("author "))) || + !ident.date_begin || !ident.date_end) + goto fail_exit; /* malformed "author" line */ + break; + } + + date = strtoul(ident.date_begin, &date_end, 10); + if (date_end != ident.date_end) + goto fail_exit; /* malformed date */ + *(author_date_slab_at(author_date, commit)) = date; + +fail_exit: + free(buffer); +} + +static int compare_commits_by_author_date(const void *a_, const void *b_, + void *cb_data) +{ + const struct commit *a = a_, *b = b_; + struct author_date_slab *author_date = cb_data; + unsigned long a_date = *(author_date_slab_at(author_date, a)); + unsigned long b_date = *(author_date_slab_at(author_date, b)); + + /* newer commits with larger date first */ + if (a_date < b_date) + return 1; + else if (a_date > b_date) + return -1; + return 0; +} + static int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused) { 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 struct indegree_slab indegree; struct prio_queue queue; struct commit *commit; + struct author_date_slab author_date; if (!orig) return; @@ -538,6 +601,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so init_indegree_slab(&indegree); memset(&queue, '\0', sizeof(queue)); + switch (sort_order) { default: /* REV_SORT_IN_GRAPH_ORDER */ queue.compare = NULL; @@ -545,12 +609,20 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so case REV_SORT_BY_COMMIT_DATE: queue.compare = compare_commits_by_commit_date; break; + case REV_SORT_BY_AUTHOR_DATE: + init_author_date_slab(&author_date); + queue.compare = compare_commits_by_author_date; + queue.cb_data = &author_date; + break; } /* Mark them and clear the indegree */ for (next = orig; next; next = next->next) { struct commit *commit = next->item; *(indegree_slab_at(&indegree, commit)) = 1; + /* also record the author dates, if needed */ + if (sort_order == REV_SORT_BY_AUTHOR_DATE) + record_author_date(&author_date, commit); } /* update the indegree */ @@ -621,6 +693,8 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so clear_indegree_slab(&indegree); clear_prio_queue(&queue); + if (sort_order == REV_SORT_BY_AUTHOR_DATE) + clear_author_date_slab(&author_date); } /* merge-base stuff */ diff --git a/commit.h b/commit.h index 247e474e44..e43dfd0182 100644 --- a/commit.h +++ b/commit.h @@ -142,7 +142,8 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark); enum rev_sort_order { REV_SORT_IN_GRAPH_ORDER = 0, - REV_SORT_BY_COMMIT_DATE + REV_SORT_BY_COMMIT_DATE, + REV_SORT_BY_AUTHOR_DATE }; /* diff --git a/revision.c b/revision.c index 966ebbc542..12d9b64b3a 100644 --- a/revision.c +++ b/revision.c @@ -1393,6 +1393,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--date-order")) { revs->sort_order = REV_SORT_BY_COMMIT_DATE; revs->topo_order = 1; + } else if (!strcmp(arg, "--author-date-order")) { + revs->sort_order = REV_SORT_BY_AUTHOR_DATE; + revs->topo_order = 1; } else if (!prefixcmp(arg, "--early-output")) { int count = 100; switch (arg[14]) { From 50e5a252225137f0fbb18a0ff72a0ae052e09d24 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Jun 2013 10:12:48 -0700 Subject: [PATCH 5/9] t/lib-t6000: style fixes Mostly fixes to initial indentation with 8-SP (they should be HT) and wrapping long lines. Signed-off-by: Junio C Hamano --- t/lib-t6000.sh | 87 +++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh index ea25dd89e5..102d567815 100644 --- a/t/lib-t6000.sh +++ b/t/lib-t6000.sh @@ -1,55 +1,50 @@ : included from 6002 and others -[ -d .git/refs/tags ] || mkdir -p .git/refs/tags +mkdir -p .git/refs/tags -:> sed.script +>sed.script -# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags -tag() -{ +# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags +tag () { _tag=$1 - [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist" - cat .git/refs/tags/$_tag + test -f ".git/refs/tags/$_tag" || error "tag: \"$_tag\" does not exist" + cat ".git/refs/tags/$_tag" } # Generate a commit using the text specified to make it unique and the tree # named by the tag specified. -unique_commit() -{ +unique_commit () { _text=$1 - _tree=$2 + _tree=$2 shift 2 - echo $_text | git commit-tree $(tag $_tree) "$@" + echo "$_text" | git commit-tree $(tag "$_tree") "$@" } # Save the output of a command into the tag specified. Prepend # a substitution script for the tag onto the front of sed.script -save_tag() -{ +save_tag () { _tag=$1 - [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..." + test -n "$_tag" || error "usage: save_tag tag commit-args ..." shift 1 - "$@" >.git/refs/tags/$_tag + "$@" >".git/refs/tags/$_tag" - echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp - cat sed.script >> sed.script.tmp + echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp + cat sed.script >>sed.script.tmp rm sed.script mv sed.script.tmp sed.script } # Replace unhelpful sha1 hashses with their symbolic equivalents -entag() -{ +entag () { sed -f sed.script } # Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL # tag to a specified value. Restore the original value on return. -as_author() -{ +as_author () { _author=$1 shift 1 - _save=$GIT_AUTHOR_EMAIL + _save=$GIT_AUTHOR_EMAIL GIT_AUTHOR_EMAIL="$_author" export GIT_AUTHOR_EMAIL @@ -63,45 +58,41 @@ as_author() fi } -commit_date() -{ - _commit=$1 - git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p" +commit_date () { + _commit=$1 + git cat-file commit $_commit | + sed -n "s/^committer .*> \([0-9]*\) .*/\1/p" } -on_committer_date() -{ - _date=$1 - shift 1 - GIT_COMMITTER_DATE="$_date" - export GIT_COMMITTER_DATE - "$@" - unset GIT_COMMITTER_DATE +on_committer_date () { + _date=$1 + shift 1 + GIT_COMMITTER_DATE="$_date" + export GIT_COMMITTER_DATE + "$@" + unset GIT_COMMITTER_DATE } # Execute a command and suppress any error output. -hide_error() -{ +hide_error () { "$@" 2>/dev/null } -check_output() -{ +check_output () { _name=$1 shift 1 - if eval "$*" | entag > $_name.actual + if eval "$*" | entag >"$_name.actual" then - test_cmp $_name.expected $_name.actual + test_cmp "$_name.expected" "$_name.actual" else - return 1; + return 1 fi } # Turn a reasonable test description into a reasonable test name. # All alphanums translated into -'s which are then compressed and stripped # from front and back. -name_from_description() -{ +name_from_description () { perl -pe ' s/[^A-Za-z0-9.]/-/g; s/-+/-/g; @@ -119,9 +110,11 @@ name_from_description() test_output_expect_success() { _description=$1 - _test=$2 - [ $# -eq 2 ] || error "usage: test_output_expect_success description test < $_name.expected + _test=$2 + test $# -eq 2 || + error "usage: test_output_expect_success description test <"$_name.expected" test_expect_success "$_description" "check_output $_name \"$_test\"" } From 841dc6935ea0f2ecfc778501edc66dc2c828d333 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Jun 2013 10:29:59 -0700 Subject: [PATCH 6/9] topology tests: teach a helper to take abbreviated timestamps The on_committer_date helper in t/lib-t6000 is used in t6002 and t6003 with timestamps on a single day within a single minute (i.e. 1971-08-16 00:00) and the tests repeat this over and over. The actual value of the timestamp, however, does not matter very much; only their relative ordering does. Introduce another helper to expand only the suffix of the timestamp to a full timestamp to make the lines shorter, and use it in this helper. Also, because all the commits in the test are made with specific GIT_COMMITTER_DATE, stop unsetting it at the end of the helper. We'll be specifying the author timestamp to these test commits in a later patch, which will be helped with this change. Also remove a test that was commented-out from t6003; it used to test a commit with the same parent listed twice, which was allowed by mistake but was fixed long time ago. Signed-off-by: Junio C Hamano --- t/lib-t6000.sh | 17 +++++-- t/t6002-rev-list-bisect.sh | 82 +++++++++++++++++----------------- t/t6003-rev-list-topo-order.sh | 57 +++++++++++------------ 3 files changed, 80 insertions(+), 76 deletions(-) diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh index 102d567815..e2c361563b 100644 --- a/t/lib-t6000.sh +++ b/t/lib-t6000.sh @@ -64,13 +64,22 @@ commit_date () { sed -n "s/^committer .*> \([0-9]*\) .*/\1/p" } +# Assign the value of fake date to a variable, but +# allow fairly common "1971-08-16 00:00" to be omittd +assign_fake_date () { + case "$2" in + ??:??:??) eval "$1='1971-08-16 $2'" ;; + ??:??) eval "$1='1971-08-16 00:$2'" ;; + ??) eval "$1='1971-08-16 00:00:$2'" ;; + *) eval "$1='$2'" ;; + esac +} + on_committer_date () { - _date=$1 - shift 1 - GIT_COMMITTER_DATE="$_date" + assign_fake_date GIT_COMMITTER_DATE "$1" export GIT_COMMITTER_DATE + shift 1 "$@" - unset GIT_COMMITTER_DATE } # Execute a command and suppress any error output. diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh index fb07536a0f..43ad772484 100755 --- a/t/t6002-rev-list-bisect.sh +++ b/t/t6002-rev-list-bisect.sh @@ -39,25 +39,25 @@ test_bisection_diff() date >path0 git update-index --add path0 save_tag tree git write-tree -on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree -on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root -on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0 -on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1 -on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2 -on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0 -on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0 -on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1 -on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1 -on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2 -on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 -on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2 -on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1 -on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2 -on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 -on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 -on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4 -on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3 -on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4 +on_committer_date "00:00" hide_error save_tag root unique_commit root tree +on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root +on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0 +on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1 +on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2 +on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0 +on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0 +on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1 +on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1 +on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2 +on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 +on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2 +on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1 +on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2 +on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 +on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 +on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4 +on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3 +on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4 git update-ref HEAD $(tag l5) @@ -90,29 +90,29 @@ git update-ref HEAD $(tag l5) # F -on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree -on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F -on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8 -on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7 -on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6 -on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F -on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4 -on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3 -on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2 -on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5 -on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4 -on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3 -on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2 -on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1 +on_committer_date "00:00" hide_error save_tag F unique_commit F tree +on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F +on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8 +on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7 +on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6 +on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F +on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4 +on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3 +on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2 +on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5 +on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4 +on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3 +on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2 +on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1 -on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree -on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U -on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0 -on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0 -on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0 -on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0 -on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0 -on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5 +on_committer_date "00:00" hide_error save_tag U unique_commit U tree +on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U +on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0 +on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0 +on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0 +on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0 +on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0 +on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5 test_sequence() { diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index e4c52b0214..bb66b8b48e 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -16,39 +16,34 @@ list_duplicates() date >path0 git update-index --add path0 save_tag tree git write-tree -on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree -on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root -on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0 -on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1 -on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2 -on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0 -on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0 -on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1 -on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1 -on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2 -on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 -on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2 -on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1 -on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2 -on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 -on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 -on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4 -on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3 -on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4 -on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3 -on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4 -on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree -on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root -on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0 -on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1 -on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5 +on_committer_date "00:00" hide_error save_tag root unique_commit root tree +on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root +on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0 +on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1 +on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2 +on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0 +on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0 +on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1 +on_committer_date "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1 +on_committer_date "00:09" save_tag b3 unique_commit b3 tree -p b2 +on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 +on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2 +on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1 +on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2 +on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 +on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 +on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4 +on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3 +on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4 +on_committer_date "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3 +on_committer_date "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4 +on_committer_date "00:21" hide_error save_tag alt_root unique_commit alt_root tree +on_committer_date "00:22" save_tag r0 unique_commit r0 tree -p alt_root +on_committer_date "00:23" save_tag r1 unique_commit r1 tree -p r0 +on_committer_date "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1 +on_committer_date "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5 -# -# note: as of 20/6, it isn't possible to create duplicate parents, so this -# can't be tested. -# -#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3 hide_error save_tag e1 as_author e@example.com unique_commit e1 tree save_tag e2 as_author e@example.com unique_commit e2 tree -p e1 save_tag f1 as_author f@example.com unique_commit f1 tree -p e1 From b9f80fdaea3a35fa156807e42e52d875bf6ad301 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Jun 2013 10:52:06 -0700 Subject: [PATCH 7/9] t6003: add --date-order test The "--date-order" output is a slight twist of "--topo-order" in that commits from parallel histories are shown in their committer date order without an attempt to clump commits from a single line of history together like --topo-order does. Signed-off-by: Junio C Hamano --- t/t6003-rev-list-topo-order.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index bb66b8b48e..0e3b7e1ea3 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -100,6 +100,28 @@ l0 root EOF +test_output_expect_success 'simple date order' 'git rev-list --date-order HEAD' < Date: Fri, 21 Jun 2013 11:03:32 -0700 Subject: [PATCH 8/9] topology tests: teach a helper to set author dates as well Introduce on_dates helper that is similar to on_committer_date but also sets the author date, not just the committer date. At this step, just set the same timestamp to the author date as the committer date, as no test looks at author date yet. Signed-off-by: Junio C Hamano --- t/lib-t6000.sh | 8 ++++++ t/t6003-rev-list-topo-order.sh | 52 +++++++++++++++++----------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh index e2c361563b..4ffd90127e 100644 --- a/t/lib-t6000.sh +++ b/t/lib-t6000.sh @@ -82,6 +82,14 @@ on_committer_date () { "$@" } +on_dates () { + assign_fake_date GIT_COMMITTER_DATE "$1" + assign_fake_date GIT_AUTHOR_DATE "$2" + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE + shift 2 + "$@" +} + # Execute a command and suppress any error output. hide_error () { "$@" 2>/dev/null diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index 0e3b7e1ea3..77bf2ca1e1 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -16,32 +16,32 @@ list_duplicates() date >path0 git update-index --add path0 save_tag tree git write-tree -on_committer_date "00:00" hide_error save_tag root unique_commit root tree -on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root -on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0 -on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1 -on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2 -on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0 -on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0 -on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1 -on_committer_date "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1 -on_committer_date "00:09" save_tag b3 unique_commit b3 tree -p b2 -on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 -on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2 -on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1 -on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2 -on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 -on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 -on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4 -on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3 -on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4 -on_committer_date "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3 -on_committer_date "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4 -on_committer_date "00:21" hide_error save_tag alt_root unique_commit alt_root tree -on_committer_date "00:22" save_tag r0 unique_commit r0 tree -p alt_root -on_committer_date "00:23" save_tag r1 unique_commit r1 tree -p r0 -on_committer_date "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1 -on_committer_date "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5 +on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree +on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root +on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0 +on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1 +on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2 +on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0 +on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0 +on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1 +on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1 +on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2 +on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 +on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2 +on_dates "00:12" "00:12" save_tag a2 unique_commit a2 tree -p a1 +on_dates "00:13" "00:13" save_tag a3 unique_commit a3 tree -p a2 +on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 +on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 +on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4 +on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3 +on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4 +on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3 +on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4 +on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree +on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root +on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0 +on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1 +on_dates "00:25" "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5 hide_error save_tag e1 as_author e@example.com unique_commit e1 tree From aff2e7c0677d882690213d7ddea4a868f8b18325 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Jun 2013 11:08:02 -0700 Subject: [PATCH 9/9] t6003: add --author-date-order test Tweak the --topo/date-order test vector a bit and mark the author dates of two commits (a2 and a3) earlier than their own committer dates, making them much older than other commits that are made on parallel branches to simulate the case where a long running topic was rebased recently. They will show up as recent in the --date-order output due to their timestamps, but they appear a lot later in the --author-date-order output, even though their committer timestamp says otherwise. Signed-off-by: Junio C Hamano --- t/t6003-rev-list-topo-order.sh | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index 77bf2ca1e1..24d1836f41 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -28,8 +28,8 @@ on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2 on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2 -on_dates "00:12" "00:12" save_tag a2 unique_commit a2 tree -p a1 -on_dates "00:13" "00:13" save_tag a3 unique_commit a3 tree -p a2 +on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1 +on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2 on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4 @@ -122,6 +122,28 @@ l0 root EOF +test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order HEAD' <