From 08f704f294e4a3525b405c2cb1f4ee85e73fd92c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Jun 2013 16:07:14 -0700 Subject: 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 --- commit.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'commit.h') 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]; -- cgit 1.2.3-korg From 81c6b38b67019eff48e8e5c324bb77f6c796c0b5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 7 Jun 2013 10:35:54 -0700 Subject: 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(-) (limited to 'commit.h') 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]) { -- cgit 1.2.3-korg