Merge branch 'jc/topo-author-date-sort'

"git log" learned the "--author-date-order" option, with which the
output is topologically sorted and commits in parallel histories
are shown intermixed together based on the author timestamp.

* jc/topo-author-date-sort:
  t6003: add --author-date-order test
  topology tests: teach a helper to set author dates as well
  t6003: add --date-order test
  topology tests: teach a helper to take abbreviated timestamps
  t/lib-t6000: style fixes
  log: --author-date-order
  sort-in-topological-order: use prio-queue
  prio-queue: priority queue of pointers to structs
  toposort: rename "lifo" field
This commit is contained in:
Junio C Hamano 2013-07-01 12:41:22 -07:00
commit 534f0e0996
16 changed files with 549 additions and 162 deletions

1
.gitignore vendored
View File

@ -191,6 +191,7 @@
/test-mktemp
/test-parse-options
/test-path-utils
/test-prio-queue
/test-read-cache
/test-regex
/test-revision-walking

View File

@ -625,6 +625,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

View File

@ -569,6 +569,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-read-cache
TEST_PROGRAMS_NEED_X += test-regex
TEST_PROGRAMS_NEED_X += test-revision-walking
@ -705,6 +706,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
@ -846,6 +848,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

View File

@ -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->sort_order);
while (list && i) {
struct commit *commit = list->item;
switch (simplify_commit(revs, commit)) {

View File

@ -630,7 +630,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;
@ -665,15 +665,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>[,<base>]"),
N_("show <n> most recent ref-log entries starting at "
"base"),
@ -900,7 +902,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)

145
commit.c
View File

@ -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 **);
@ -518,31 +519,124 @@ 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_;
/* 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, 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;
struct commit_list **pptr;
struct indegree_slab indegree;
struct prio_queue queue;
struct commit *commit;
struct author_date_slab author_date;
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;
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 */
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);
@ -560,30 +654,28 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
*
* 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 (!lifo)
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);
@ -596,23 +688,22 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
* when all their children have been emitted thereby
* guaranteeing topological order.
*/
if (--(*pi) == 1) {
if (!lifo)
commit_list_insert_by_date(parent, &work);
else
commit_list_insert(parent, &work);
}
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);
if (sort_order == REV_SORT_BY_AUTHOR_DATE)
clear_author_date_slab(&author_date);
}
/* merge-base stuff */

View File

@ -142,15 +142,24 @@ void clear_commit_marks(struct commit *commit, unsigned int mark);
void clear_commit_marks_many(int nr, 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,
REV_SORT_BY_AUTHOR_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];

84
prio-queue.c Normal file
View File

@ -0,0 +1,84 @@
#include "cache.h"
#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);
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;
}

48
prio-queue.h Normal file
View File

@ -0,0 +1,48 @@
#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 *);
/* Reverse the LIFO elements */
extern void prio_queue_reverse(struct prio_queue *);
#endif /* PRIO_QUEUE_H */

View File

@ -1296,7 +1296,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;
@ -1638,7 +1638,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;
@ -1656,7 +1656,10 @@ 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 (!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;
@ -2606,7 +2609,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->line_level_traverse)
line_log_filter(revs);
if (revs->simplify_merges)
@ -2924,7 +2927,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)

View File

@ -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)
@ -62,6 +63,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;
@ -72,7 +77,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,

View File

@ -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,58 @@ 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
# 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 () {
assign_fake_date GIT_COMMITTER_DATE "$1"
export GIT_COMMITTER_DATE
shift 1
"$@"
}
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()
{
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 +127,11 @@ name_from_description()
test_output_expect_success()
{
_description=$1
_test=$2
[ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
_name=$(echo $_description | name_from_description)
cat > $_name.expected
_test=$2
test $# -eq 2 ||
error "usage: test_output_expect_success description test <<EOF ... EOF"
_name=$(echo $_description | name_from_description)
cat >"$_name.expected"
test_expect_success "$_description" "check_output $_name \"$_test\""
}

50
t/t0009-prio-queue.sh Executable file
View File

@ -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

View File

@ -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()
{

View File

@ -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_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: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
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
#
# 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
@ -105,6 +100,50 @@ l0
root
EOF
test_output_expect_success 'simple date order' 'git rev-list --date-order HEAD' <<EOF
l5
l4
l3
a4
b4
a3
a2
c3
c2
b3
b2
c1
b1
a1
a0
l2
l1
l0
root
EOF
test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order HEAD' <<EOF
l5
l4
l3
a4
b4
c3
c2
b3
b2
c1
b1
a3
a2
a1
a0
l2
l1
l0
root
EOF
test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order g4' <<EOF
g4
h2

39
test-prio-queue.c Normal file
View File

@ -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;
}