Attached is a preliminary hackish patch to sort git-rev-list in local commit order. While I don't know how useful this really is, it's presented as an alternative to the repo-id proposal. This will work even if the branch happens to be from a single repository, where repo-id will not. However, shared commit objects can cause problems so for best results use private commit objects for each repository.
For purposes of testing, this patch changes the Cogito default of linking objects to copying, for local repository pull operations. This patch will work with _existing_ repositories where local commit times have been maintained.
Also attached, is a little test script that demonstrates the local commit time order. After running the test script, you can use the cg-log command in each of the M and R directories to see the difference even though the two repositories share a head commit.
This patch is not nearly ready for inclusion anywhere just meant for comment. It is based off Petr's cogito tree (commit fa6e9eb368e949e78c4e66217461cf624b52b0a2).
cache.h | 1 cg-pull | 4 - commit.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- commit.h | 6 ++ rev-list.c | 2 sha1_file.c | 8 +++ 6 files changed, 137 insertions(+), 5 deletions(-)
Sean
Index: cache.h =================================================================== --- a/cache.h (mode:100644) +++ b/cache.h (mode:100644) @@ -157,6 +157,7 @@ extern int write_sha1_from_fd(const unsigned char *sha1, int fd); extern int has_sha1_file(const unsigned char *sha1); +extern unsigned long sha1_local_date(const unsigned char *sha1); /* Convert to/from hex/sha1 representation */ extern int get_sha1(const char *str, unsigned char *sha1); Index: cg-pull =================================================================== --- a/cg-pull (mode:100755) +++ b/cg-pull (mode:100755) @@ -143,7 +143,7 @@ [ "$1" = "-i" ] && shift [ "$1" = "-s" ] && shift - cp_flags_l="-va" + cp_flags_l="-vdR" if [ "$1" = "-u" ]; then cp_flags_l="$cp_flags_l -lu" shift @@ -163,7 +163,7 @@ } pull_local () { - git-local-pull -a -l -v "$(cat "$_git/refs/heads/$1")" "$2" + git-local-pull -a -v "$(cat "$_git/refs/heads/$1")" "$2" } if echo "$uri" | grep -q "^http://"; then Index: commit.c =================================================================== --- a/commit.c (mode:100644) +++ b/commit.c (mode:100644) @@ -2,6 +2,7 @@ #include "cache.h" #include <string.h> #include <limits.h> +#include <stdlib.h> const char *commit_type = "commit"; @@ -13,6 +14,7 @@ memset(ret, 0, sizeof(struct commit)); created_object(sha1, &ret->object); ret->object.type = commit_type; + ret->local_date = sha1_local_date(sha1); return ret; } if (obj->type != commit_type) { @@ -41,6 +43,18 @@ return date; } +static void insert_by_local_date(struct commit_list **list, struct commit *item) +{ + struct commit_list **pp = list; + struct commit_list *p; + while ((p = *pp) != NULL) { + if (p->item->local_date > item->local_date) + break; + pp = &p->next; + } + commit_list_insert(item, pp); +} + int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) { void *bufptr = buffer; @@ -58,12 +72,13 @@ !get_sha1_hex(bufptr + 7, parent)) { struct commit *new_parent = lookup_commit(parent); if (new_parent) { - commit_list_insert(new_parent, &item->parents); + insert_by_local_date(&item->parents, new_parent); add_ref(&item->object, &new_parent->object); } bufptr += 48; } item->date = parse_commit_date(bufptr); + item->merge_nodes = NULL; return 0; } @@ -152,3 +167,107 @@ } return ret; } + +struct commit_list *copy_commit_list(struct commit_list *list) +{ + struct commit_list *copy = NULL; + while (list) { + commit_list_insert(list->item, ©); + list = list->next; + } + return copy; +} + +int found_on_list(struct commit *item, struct commit_list *list) +{ + while (list) { + if (list->item == item) + return 1; + list = list->next; + } + return 0; +} + +static struct commit *process_local_list(struct commit_list **list_p, + int this_mark, int other_mark) +{ + struct commit *item = (*list_p)->item; + + if (item->object.flags & other_mark) { + return item; + } else { + pop_most_recent_commit(list_p, this_mark); + } + return NULL; +} + +struct commit *common_local_ancestor(struct commit *rev1, struct commit *rev2) +{ + struct commit_list *rev1list = NULL; + struct commit_list *rev2list = NULL; + + commit_list_insert(rev1, &rev1list); rev1->object.flags |= 0x1; + commit_list_insert(rev2, &rev2list); rev2->object.flags |= 0x2; + parse_commit(rev1); parse_commit(rev2); + + while (rev1list || rev2list) { + struct commit *ret; + if (!rev1list) { + // process 2 + ret = process_local_list(&rev2list, 0x2, 0x1); + } else if (!rev2list) { + // process 1 + ret = process_local_list(&rev1list, 0x1, 0x2); + } else if (rev1list->item->local_date + < rev2list->item->local_date) { + // process 2 + ret = process_local_list(&rev2list, 0x2, 0x1); + } else { + // process 1 + ret = process_local_list(&rev1list, 0x1, 0x2); + } + if (ret) { + free_commit_list(rev1list); + free_commit_list(rev2list); + return ret; + } + } + return NULL; +} + +void insert_merge_nodes(struct commit_list *plist, + struct commit_list *stop, + struct commit *node) +{ + struct commit_list *p; + for (p=plist; p != stop; p=p->next) + commit_list_insert( common_local_ancestor(node, p->item), + &node->merge_nodes); +} + +struct commit *pop_newest_local_commit( struct commit_list **list, + unsigned int mark) +{ + struct commit *ret = (*list)->item; + struct commit_list *parents = ret->parents; + struct commit_list *old = *list; + struct commit_list *prev = ret->merge_nodes; + + *list = (*list)->next; + free(old); + + /* Loop expects parents to be ordered oldest to newest on local time */ + while (parents) { + struct commit *commit = parents->item; + parse_commit(commit); + if (!((commit->object.flags & mark) | + found_on_list(commit, ret->merge_nodes))) { + commit->object.flags |= mark; + prev = commit->merge_nodes = copy_commit_list(prev); + insert_merge_nodes(ret->parents, parents, commit); + commit_list_insert(commit, list); + } + parents = parents->next; + } + return ret; +} Index: commit.h =================================================================== --- a/commit.h (mode:100644) +++ b/commit.h (mode:100644) @@ -11,8 +11,9 @@ struct commit { struct object object; - unsigned long date; + unsigned long date, local_date; struct commit_list *parents; + struct commit_list *merge_nodes; struct tree *tree; }; @@ -36,4 +37,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list, unsigned int mark); +struct commit *pop_newest_local_commit( struct commit_list **list, + unsigned int mark); + #endif /* COMMIT_H */ Index: rev-list.c =================================================================== --- a/rev-list.c (mode:100644) +++ b/rev-list.c (mode:100644) @@ -38,7 +38,7 @@ commit_list_insert(commit, &list); do { - struct commit *commit = pop_most_recent_commit(&list, 0x1); + struct commit *commit = pop_newest_local_commit(&list, 0x4); if (min_age != -1 && (commit->date > min_age)) continue; Index: sha1_file.c =================================================================== --- a/sha1_file.c (mode:100644) +++ b/sha1_file.c (mode:100644) @@ -577,6 +577,14 @@ return !!find_sha1_file(sha1, &st); } +unsigned long sha1_local_date(const unsigned char *sha1) +{ + struct stat st; + if (find_sha1_file(sha1, &st)) + return st.st_mtime; + return 0; +} + int index_fd(unsigned char *sha1, int fd, struct stat *st) { unsigned long size = st->st_size;