Merge branch 'rs/commit-list-sort-in-batch'
Setting up a revision traversal with many starting points was inefficient as these were placed in a date-order priority queue one-by-one. By René Scharfe (3) and Junio C Hamano (1) * rs/commit-list-sort-in-batch: mergesort: rename it to llist_mergesort() revision: insert unsorted, then sort in prepare_revision_walk() commit: use mergesort() in commit_list_sort_by_date() add mergesort() for linked lists
This commit is contained in:
commit
ba8e6326f1
1
.gitignore
vendored
1
.gitignore
vendored
@ -181,6 +181,7 @@
|
|||||||
/test-index-version
|
/test-index-version
|
||||||
/test-line-buffer
|
/test-line-buffer
|
||||||
/test-match-trees
|
/test-match-trees
|
||||||
|
/test-mergesort
|
||||||
/test-mktemp
|
/test-mktemp
|
||||||
/test-parse-options
|
/test-parse-options
|
||||||
/test-path-utils
|
/test-path-utils
|
||||||
|
3
Makefile
3
Makefile
@ -481,6 +481,7 @@ TEST_PROGRAMS_NEED_X += test-genrandom
|
|||||||
TEST_PROGRAMS_NEED_X += test-index-version
|
TEST_PROGRAMS_NEED_X += test-index-version
|
||||||
TEST_PROGRAMS_NEED_X += test-line-buffer
|
TEST_PROGRAMS_NEED_X += test-line-buffer
|
||||||
TEST_PROGRAMS_NEED_X += test-match-trees
|
TEST_PROGRAMS_NEED_X += test-match-trees
|
||||||
|
TEST_PROGRAMS_NEED_X += test-mergesort
|
||||||
TEST_PROGRAMS_NEED_X += test-mktemp
|
TEST_PROGRAMS_NEED_X += test-mktemp
|
||||||
TEST_PROGRAMS_NEED_X += test-parse-options
|
TEST_PROGRAMS_NEED_X += test-parse-options
|
||||||
TEST_PROGRAMS_NEED_X += test-path-utils
|
TEST_PROGRAMS_NEED_X += test-path-utils
|
||||||
@ -591,6 +592,7 @@ LIB_H += log-tree.h
|
|||||||
LIB_H += mailmap.h
|
LIB_H += mailmap.h
|
||||||
LIB_H += merge-file.h
|
LIB_H += merge-file.h
|
||||||
LIB_H += merge-recursive.h
|
LIB_H += merge-recursive.h
|
||||||
|
LIB_H += mergesort.h
|
||||||
LIB_H += notes.h
|
LIB_H += notes.h
|
||||||
LIB_H += notes-cache.h
|
LIB_H += notes-cache.h
|
||||||
LIB_H += notes-merge.h
|
LIB_H += notes-merge.h
|
||||||
@ -695,6 +697,7 @@ LIB_OBJS += mailmap.o
|
|||||||
LIB_OBJS += match-trees.o
|
LIB_OBJS += match-trees.o
|
||||||
LIB_OBJS += merge-file.o
|
LIB_OBJS += merge-file.o
|
||||||
LIB_OBJS += merge-recursive.o
|
LIB_OBJS += merge-recursive.o
|
||||||
|
LIB_OBJS += mergesort.o
|
||||||
LIB_OBJS += name-hash.o
|
LIB_OBJS += name-hash.o
|
||||||
LIB_OBJS += notes.o
|
LIB_OBJS += notes.o
|
||||||
LIB_OBJS += notes-cache.o
|
LIB_OBJS += notes-cache.o
|
||||||
|
44
commit.c
44
commit.c
@ -7,6 +7,7 @@
|
|||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "notes.h"
|
#include "notes.h"
|
||||||
#include "gpg-interface.h"
|
#include "gpg-interface.h"
|
||||||
|
#include "mergesort.h"
|
||||||
|
|
||||||
int save_commit_buffer = 1;
|
int save_commit_buffer = 1;
|
||||||
|
|
||||||
@ -360,6 +361,21 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *
|
|||||||
return new_list;
|
return new_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void commit_list_reverse(struct commit_list **list_p)
|
||||||
|
{
|
||||||
|
struct commit_list *prev = NULL, *curr = *list_p, *next;
|
||||||
|
|
||||||
|
if (!list_p)
|
||||||
|
return;
|
||||||
|
while (curr) {
|
||||||
|
next = curr->next;
|
||||||
|
curr->next = prev;
|
||||||
|
prev = curr;
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
*list_p = prev;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned commit_list_count(const struct commit_list *l)
|
unsigned commit_list_count(const struct commit_list *l)
|
||||||
{
|
{
|
||||||
unsigned c = 0;
|
unsigned c = 0;
|
||||||
@ -390,15 +406,31 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
|
|||||||
return commit_list_insert(item, pp);
|
return commit_list_insert(item, pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int commit_list_compare_by_date(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
unsigned long a_date = ((const struct commit_list *)a)->item->date;
|
||||||
|
unsigned long b_date = ((const struct commit_list *)b)->item->date;
|
||||||
|
if (a_date < b_date)
|
||||||
|
return 1;
|
||||||
|
if (a_date > b_date)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *commit_list_get_next(const void *a)
|
||||||
|
{
|
||||||
|
return ((const struct commit_list *)a)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commit_list_set_next(void *a, void *next)
|
||||||
|
{
|
||||||
|
((struct commit_list *)a)->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
void commit_list_sort_by_date(struct commit_list **list)
|
void commit_list_sort_by_date(struct commit_list **list)
|
||||||
{
|
{
|
||||||
struct commit_list *ret = NULL;
|
*list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
|
||||||
while (*list) {
|
commit_list_compare_by_date);
|
||||||
commit_list_insert_by_date((*list)->item, &ret);
|
|
||||||
*list = (*list)->next;
|
|
||||||
}
|
|
||||||
*list = ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct commit *pop_most_recent_commit(struct commit_list **list,
|
struct commit *pop_most_recent_commit(struct commit_list **list,
|
||||||
|
1
commit.h
1
commit.h
@ -57,6 +57,7 @@ unsigned commit_list_count(const struct commit_list *l);
|
|||||||
struct commit_list *commit_list_insert_by_date(struct commit *item,
|
struct commit_list *commit_list_insert_by_date(struct commit *item,
|
||||||
struct commit_list **list);
|
struct commit_list **list);
|
||||||
void commit_list_sort_by_date(struct commit_list **list);
|
void commit_list_sort_by_date(struct commit_list **list);
|
||||||
|
void commit_list_reverse(struct commit_list **list_p);
|
||||||
|
|
||||||
void free_commit_list(struct commit_list *list);
|
void free_commit_list(struct commit_list *list);
|
||||||
|
|
||||||
|
73
mergesort.c
Normal file
73
mergesort.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "mergesort.h"
|
||||||
|
|
||||||
|
struct mergesort_sublist {
|
||||||
|
void *ptr;
|
||||||
|
unsigned long len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *get_nth_next(void *list, unsigned long n,
|
||||||
|
void *(*get_next_fn)(const void *))
|
||||||
|
{
|
||||||
|
while (n-- && list)
|
||||||
|
list = get_next_fn(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *pop_item(struct mergesort_sublist *l,
|
||||||
|
void *(*get_next_fn)(const void *))
|
||||||
|
{
|
||||||
|
void *p = l->ptr;
|
||||||
|
l->ptr = get_next_fn(l->ptr);
|
||||||
|
l->len = l->ptr ? (l->len - 1) : 0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *llist_mergesort(void *list,
|
||||||
|
void *(*get_next_fn)(const void *),
|
||||||
|
void (*set_next_fn)(void *, void *),
|
||||||
|
int (*compare_fn)(const void *, const void *))
|
||||||
|
{
|
||||||
|
unsigned long l;
|
||||||
|
|
||||||
|
if (!list)
|
||||||
|
return NULL;
|
||||||
|
for (l = 1; ; l *= 2) {
|
||||||
|
void *curr;
|
||||||
|
struct mergesort_sublist p, q;
|
||||||
|
|
||||||
|
p.ptr = list;
|
||||||
|
q.ptr = get_nth_next(p.ptr, l, get_next_fn);
|
||||||
|
if (!q.ptr)
|
||||||
|
break;
|
||||||
|
p.len = q.len = l;
|
||||||
|
|
||||||
|
if (compare_fn(p.ptr, q.ptr) > 0)
|
||||||
|
list = curr = pop_item(&q, get_next_fn);
|
||||||
|
else
|
||||||
|
list = curr = pop_item(&p, get_next_fn);
|
||||||
|
|
||||||
|
while (p.ptr) {
|
||||||
|
while (p.len || q.len) {
|
||||||
|
void *prev = curr;
|
||||||
|
|
||||||
|
if (!p.len)
|
||||||
|
curr = pop_item(&q, get_next_fn);
|
||||||
|
else if (!q.len)
|
||||||
|
curr = pop_item(&p, get_next_fn);
|
||||||
|
else if (compare_fn(p.ptr, q.ptr) > 0)
|
||||||
|
curr = pop_item(&q, get_next_fn);
|
||||||
|
else
|
||||||
|
curr = pop_item(&p, get_next_fn);
|
||||||
|
set_next_fn(prev, curr);
|
||||||
|
}
|
||||||
|
p.ptr = q.ptr;
|
||||||
|
p.len = l;
|
||||||
|
q.ptr = get_nth_next(p.ptr, l, get_next_fn);
|
||||||
|
q.len = q.ptr ? l : 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
set_next_fn(curr, NULL);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
17
mergesort.h
Normal file
17
mergesort.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef MERGESORT_H
|
||||||
|
#define MERGESORT_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sort linked list in place.
|
||||||
|
* - get_next_fn() returns the next element given an element of a linked list.
|
||||||
|
* - set_next_fn() takes two elements A and B, and makes B the "next" element
|
||||||
|
* of A on the list.
|
||||||
|
* - compare_fn() takes two elements A and B, and returns negative, 0, positive
|
||||||
|
* as the same sign as "subtracting" B from A.
|
||||||
|
*/
|
||||||
|
void *llist_mergesort(void *list,
|
||||||
|
void *(*get_next_fn)(const void *),
|
||||||
|
void (*set_next_fn)(void *, void *),
|
||||||
|
int (*compare_fn)(const void *, const void *));
|
||||||
|
|
||||||
|
#endif
|
@ -2076,11 +2076,13 @@ int prepare_revision_walk(struct rev_info *revs)
|
|||||||
if (commit) {
|
if (commit) {
|
||||||
if (!(commit->object.flags & SEEN)) {
|
if (!(commit->object.flags & SEEN)) {
|
||||||
commit->object.flags |= SEEN;
|
commit->object.flags |= SEEN;
|
||||||
commit_list_insert_by_date(commit, &revs->commits);
|
commit_list_insert(commit, &revs->commits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e++;
|
e++;
|
||||||
}
|
}
|
||||||
|
commit_list_reverse(&revs->commits);
|
||||||
|
commit_list_sort_by_date(&revs->commits);
|
||||||
if (!revs->leak_pending)
|
if (!revs->leak_pending)
|
||||||
free(list);
|
free(list);
|
||||||
|
|
||||||
|
52
test-mergesort.c
Normal file
52
test-mergesort.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "mergesort.h"
|
||||||
|
|
||||||
|
struct line {
|
||||||
|
char *text;
|
||||||
|
struct line *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *get_next(const void *a)
|
||||||
|
{
|
||||||
|
return ((const struct line *)a)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_next(void *a, void *b)
|
||||||
|
{
|
||||||
|
((struct line *)a)->next = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_strings(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct line *x = a, *y = b;
|
||||||
|
return strcmp(x->text, y->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
struct line *line, *p = NULL, *lines = NULL;
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (strbuf_getwholeline(&sb, stdin, '\n'))
|
||||||
|
break;
|
||||||
|
line = xmalloc(sizeof(struct line));
|
||||||
|
line->text = strbuf_detach(&sb, NULL);
|
||||||
|
if (p) {
|
||||||
|
line->next = p->next;
|
||||||
|
p->next = line;
|
||||||
|
} else {
|
||||||
|
line->next = NULL;
|
||||||
|
lines = line;
|
||||||
|
}
|
||||||
|
p = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = llist_mergesort(lines, get_next, set_next, compare_strings);
|
||||||
|
|
||||||
|
while (lines) {
|
||||||
|
printf("%s", lines->text);
|
||||||
|
lines = lines->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user