--date-order is an excellent alternative to --topo-order if you want a feel for the *actual history*, chronologically, of your project. I use it often, with --graph as well; it's a great way to get an overview of a project's recent development history. However, in a project that rebases various in-development topic-branches often, it gets hard to demonstrate a *chronological history* of changes to the codebase, as this always “resets” the COMMITTER_DATE (which --date-order uses) to the time the rebase happened; which often means ‘last time all of the topic-branches were rebased on the latest fixes in master.’ Thus, I've added an --authorship-order version of --date-order, which relies upon the AUTHOR_DATE instead of the COMMITTER_DATE; this means that old commits will continue to show up chronologically in-order despite rebasing. --- builtin/log.c | 2 +- builtin/rev-list.c | 1 + builtin/rev-parse.c | 1 + builtin/show-branch.c | 12 ++++- commit.c | 83 ++++++++++++++++++++++++++++++---- commit.h | 3 +- contrib/completion/git-completion.bash | 4 +- po/de.po | 4 +- po/git.pot | 2 +- po/sv.po | 4 +- po/vi.po | 4 +- po/zh_CN.po | 4 +- revision.c | 11 ++++- revision.h | 1 + 14 files changed, 110 insertions(+), 26 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 9e21232..54d4d7f 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -237,7 +237,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->lifo, revs->use_author); while (list && i) { struct commit *commit = list->item; switch (simplify_commit(revs, commit)) { diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 67701be..cfa5d1f 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -30,6 +30,7 @@ static const char rev_list_usage[] = " ordering output:\n" " --topo-order\n" " --date-order\n" +" --authorship-order\n" " --reverse\n" " formatting output:\n" " --parents\n" diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index f267a1d..d08aebd 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -65,6 +65,7 @@ static int is_rev_argument(const char *arg) "--tags", "--topo-order", "--date-order", + "--authorship-order", "--unpacked", NULL }; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 90fc6b1..ac06ac3 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -6,7 +6,7 @@ #include "parse-options.h" static const char* show_branch_usage[] = { - N_("git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), + N_("git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), N_("git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"), NULL }; @@ -631,6 +631,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int all_heads = 0, all_remotes = 0; int all_mask, all_revs; int lifo = 1; + int use_author = 0; char head[128]; const char *head_p; int head_len; @@ -667,6 +668,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) N_("show refs unreachable from any other ref")), OPT_BOOLEAN(0, "topo-order", &lifo, N_("show commits in topological order")), + OPT_BOOLEAN(0, "authorship-order", &use_author, + N_("like --date-order, but with the *author* date")), OPT_BOOLEAN(0, "topics", &topics, N_("show only commits not on the first branch")), OPT_SET_INT(0, "sparse", &dense, @@ -694,6 +697,11 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (all_heads) all_remotes = 1; + /* I'm having trouble figuring out exactly what `lifo` stores. Why do both 'date-order' and + * 'topo-order' set the same variable!? Aren't they mutually exclusive? Since *both* set it, for + * the moment, I'm going to set it for '--authorship-order'; but that seems counterintuitive. */ + if (use_author) + lifo = 1; if (extra || reflog) { /* "listing" mode is incompatible with @@ -900,7 +908,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, lifo, use_author); /* Give names to commits */ if (!sha1_name && !no_name) diff --git a/commit.c b/commit.c index 888e02a..b8a0f60 100644 --- a/commit.c +++ b/commit.c @@ -78,7 +78,34 @@ struct commit *lookup_commit_reference_by_name(const char *name) return commit; } -static unsigned long parse_commit_date(const char *buf, const char *tail) +static unsigned long parse_commit_author_date(const char *buf, const char *tail) +{ + const char *dateptr; + + if (buf + 6 >= tail) + return 0; + if (memcmp(buf, "author", 6)) + return 0; + while (buf < tail && *buf++ != '>') + /* nada */; + if (buf >= tail) + return 0; + dateptr = buf; + while (buf < tail && *buf++ != '\n') + /* nada */; + if (buf + 9 >= tail) + return 0; + if (memcmp(buf, "committer", 9)) + return 0; + while (buf < tail && *buf++ != '\n') + /* nada */; + if (buf >= tail) + return 0; + /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */ + return strtoul(dateptr, NULL, 10); +} + +static unsigned long parse_commit_committer_date(const char *buf, const char *tail) { const char *dateptr; @@ -301,7 +328,8 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s pptr = &commit_list_insert(new_parent, pptr)->next; } } - item->date = parse_commit_date(bufptr, tail); + item->date = parse_commit_committer_date(bufptr, tail); + item->author_date = parse_commit_author_date(bufptr, tail); return 0; } @@ -380,6 +408,19 @@ void free_commit_list(struct commit_list *list) } } +struct commit_list * commit_list_insert_by_author_date(struct commit *item, struct commit_list **list) +{ + struct commit_list **pp = list; + struct commit_list *p; + while ((p = *pp) != NULL) { + if (p->item->author_date < item->author_date) { + break; + } + pp = &p->next; + } + return commit_list_insert(item, pp); +} + struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list) { struct commit_list **pp = list; @@ -393,6 +434,17 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm return commit_list_insert(item, pp); } +static int commit_list_compare_by_author_date(const void *a, const void *b) +{ + unsigned long a_date = ((const struct commit_list *)a)->item->author_date; + unsigned long b_date = ((const struct commit_list *)b)->item->author_date; + if (a_date < b_date) + return 1; + if (a_date > b_date) + return -1; + return 0; +} + static int commit_list_compare_by_date(const void *a, const void *b) { unsigned long a_date = ((const struct commit_list *)a)->item->date; @@ -414,6 +466,12 @@ static void commit_list_set_next(void *a, void *next) ((struct commit_list *)a)->next = next; } +void commit_list_sort_by_author_date(struct commit_list **list) +{ + *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next, + commit_list_compare_by_author_date); +} + void commit_list_sort_by_date(struct commit_list **list) { *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next, @@ -509,7 +567,7 @@ struct commit *pop_commit(struct commit_list **stack) /* * 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, int lifo, int use_author) { struct commit_list *next, *orig = *list; struct commit_list *work, **insert; @@ -554,8 +612,12 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) } /* process the list in topological order */ - if (!lifo) - commit_list_sort_by_date(&work); + if (!lifo) { + if (use_author) + commit_list_sort_by_author_date(&work); + else + commit_list_sort_by_date(&work); + } pptr = list; *list = NULL; @@ -580,10 +642,13 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) * guaranteeing topological order. */ if (--parent->indegree == 1) { - if (!lifo) - commit_list_insert_by_date(parent, &work); - else - commit_list_insert(parent, &work); + if (!lifo) { + if (use_author) + commit_list_insert_by_author_date(parent, &work); + else + commit_list_insert_by_date(parent, &work); + } else { + commit_list_insert(parent, &work); } } } /* diff --git a/commit.h b/commit.h index 67bd509..de07525 100644 --- a/commit.h +++ b/commit.h @@ -17,6 +17,7 @@ struct commit { void *util; unsigned int indegree; unsigned long date; + unsigned long author_date; struct commit_list *parents; struct tree *tree; char *buffer; @@ -150,7 +151,7 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark); * in addition, when lifo == 0, commits on parallel tracks are * sorted in the dates order. */ -void sort_in_topological_order(struct commit_list ** list, int lifo); +void sort_in_topological_order(struct commit_list ** list, int lifo, int use_author); struct commit_graft { unsigned char sha1[20]; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 91234d4..f051e53 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1445,7 +1445,7 @@ _git_log () $__git_log_common_options $__git_log_shortlog_options $__git_log_gitk_options - --root --topo-order --date-order --reverse + --root --topo-order --date-order --authorship-order --reverse --follow --full-diff --abbrev-commit --abbrev= --relative-date --date= @@ -2291,7 +2291,7 @@ _git_show_branch () case "$cur" in --*) __gitcomp " - --all --remotes --topo-order --current --more= + --all --remotes --topo-order --authorship-order --current --more= --list --independent --merge-base --no-name --color --no-color --sha1-name --sparse --topics --reflog diff --git a/po/de.po b/po/de.po index 4901488..0dc184f 100644 --- a/po/de.po +++ b/po/de.po @@ -8716,12 +8716,12 @@ msgstr "Ausgabe mit Zeilenumbrüchen" #: builtin/show-branch.c:9 msgid "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" msgstr "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<Wann>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] " "[(<Revision> | <glob>)...]" diff --git a/po/git.pot b/po/git.pot index 4a9d4ef..325348d 100644 --- a/po/git.pot +++ b/po/git.pot @@ -8123,7 +8123,7 @@ msgstr "" #: builtin/show-branch.c:9 msgid "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" diff --git a/po/sv.po b/po/sv.po index a5c88c9..5091224 100644 --- a/po/sv.po +++ b/po/sv.po @@ -8478,12 +8478,12 @@ msgstr "Radbryt utdata" #: builtin/show-branch.c:9 msgid "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" msgstr "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<när>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<mönster>)...]" diff --git a/po/vi.po b/po/vi.po index c6af8d5..ec41ff8 100644 --- a/po/vi.po +++ b/po/vi.po @@ -8622,12 +8622,12 @@ msgstr "Ngắt dòng khi quá dài" #: builtin/show-branch.c:9 msgid "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" msgstr "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<khi>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" diff --git a/po/zh_CN.po b/po/zh_CN.po index ba757d9..a666aed 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8446,12 +8446,12 @@ msgstr "折行输出" #: builtin/show-branch.c:9 msgid "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" msgstr "" -"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--" +"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order | --authorship-order] [--" "current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --" "independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | " "<glob>)...]" diff --git a/revision.c b/revision.c index 518cd08..2d077ce 100644 --- a/revision.c +++ b/revision.c @@ -1053,6 +1053,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->pruning.add_remove = file_add_remove; revs->pruning.change = file_change; revs->lifo = 1; + revs->use_author = 0; revs->dense = 1; revs->prefix = prefix; revs->max_age = -1; @@ -1394,6 +1395,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--topo-order")) { revs->lifo = 1; revs->topo_order = 1; + revs->use_author = 0; } else if (!strcmp(arg, "--simplify-merges")) { revs->simplify_merges = 1; revs->topo_order = 1; @@ -1412,6 +1414,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--date-order")) { revs->lifo = 0; revs->topo_order = 1; + revs->use_author = 0; + } else if (!strcmp(arg, "--authorship-order")) { + revs->lifo = 0; + revs->topo_order = 1; + revs->use_author = 1; } else if (!prefixcmp(arg, "--early-output")) { int count = 100; switch (arg[14]) { @@ -2191,7 +2198,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->lifo, revs->use_author); if (revs->line_level_traverse) line_log_filter(revs); if (revs->simplify_merges) @@ -2503,7 +2510,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->lifo, revs->use_author); } static struct commit *get_revision_internal(struct rev_info *revs) diff --git a/revision.h b/revision.h index a313a13..09effab 100644 --- a/revision.h +++ b/revision.h @@ -73,6 +73,7 @@ struct rev_info { simplify_history:1, lifo:1, topo_order:1, + use_author:1, simplify_merges:1, simplify_by_decoration:1, tag_objects:1, -- 1.8.1.3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html