diff options
| author | Junio C Hamano <gitster@pobox.com> | 2013-07-01 12:41:22 -0700 | 
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2013-07-01 12:41:23 -0700 | 
| commit | 534f0e0996c0a5a0bea5bae8ae088a80a929932b (patch) | |
| tree | 734ca0d418edf3e0443f3c4e4d751d5cbfa8be05 /commit.c | |
| parent | 55f34c8d39dac8a90d4944a77cb7614256c62018 (diff) | |
| parent | aff2e7c0677d882690213d7ddea4a868f8b18325 (diff) | |
| download | git-534f0e0996c0a5a0bea5bae8ae088a80a929932b.tar.gz | |
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
Diffstat (limited to 'commit.c')
| -rw-r--r-- | commit.c | 145 | 
1 files changed, 118 insertions, 27 deletions
| @@ -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; - -		work_item = work; -		work = work_item->next; -		work_item->next = NULL; +	while ((commit = prio_queue_get(&queue)) != NULL) { +		struct commit_list *parents; -		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 */ | 
