From 8860fd42fcf5a7853f7d7c2198793183320293ff Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 11 Jan 2007 11:47:48 +0100 Subject: [PATCH] Teach the revision walker to walk by reflogs with --walk-reflogs When called with "--walk-reflogs", as long as there are reflogs available, the walker will take this information into account, rather than the parent information in the commit object. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 +- log-tree.c | 3 + reflog-walk.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++ reflog-walk.h | 11 +++ revision.c | 11 +++ revision.h | 2 + 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 reflog-walk.c create mode 100644 reflog-walk.h diff --git a/Makefile b/Makefile index 8432ab8ba..1a29392a1 100644 --- a/Makefile +++ b/Makefile @@ -254,7 +254,7 @@ LIB_OBJS = \ interpolate.o \ lockfile.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ - reachable.o \ + reachable.o reflog-walk.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ diff --git a/log-tree.c b/log-tree.c index 35be33aaf..f043ad372 100644 --- a/log-tree.c +++ b/log-tree.c @@ -2,6 +2,7 @@ #include "diff.h" #include "commit.h" #include "log-tree.h" +#include "reflog-walk.h" static void show_parents(struct commit *commit, int abbrev) { @@ -223,6 +224,8 @@ void show_log(struct rev_info *opt, const char *sep) printf("%s", diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + if (opt->reflog_info) + show_reflog_message(opt->reflog_info); } /* diff --git a/reflog-walk.c b/reflog-walk.c new file mode 100644 index 000000000..d4b49c735 --- /dev/null +++ b/reflog-walk.c @@ -0,0 +1,235 @@ +#include "cache.h" +#include "commit.h" +#include "refs.h" +#include "diff.h" +#include "revision.h" +#include "path-list.h" + +struct complete_reflogs { + char *ref; + struct reflog_info { + unsigned char osha1[20], nsha1[20]; + char *email; + unsigned long timestamp; + int tz; + char *message; + } *items; + int nr, alloc; +}; + +static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) +{ + struct complete_reflogs *array = cb_data; + struct reflog_info *item; + + if (array->nr >= array->alloc) { + array->alloc = alloc_nr(array->nr + 1); + array->items = xrealloc(array->items, array->alloc * + sizeof(struct reflog_info)); + } + item = array->items + array->nr; + memcpy(item->osha1, osha1, 20); + memcpy(item->nsha1, nsha1, 20); + item->email = xstrdup(email); + item->timestamp = timestamp; + item->tz = tz; + item->message = xstrdup(message); + array->nr++; + return 0; +} + +static struct complete_reflogs *read_complete_reflog(const char *ref) +{ + struct complete_reflogs *reflogs = + xcalloc(sizeof(struct complete_reflogs), 1); + reflogs->ref = xstrdup(ref); + for_each_reflog_ent(ref, read_one_reflog, reflogs); + if (reflogs->nr == 0) { + unsigned char sha1[20]; + const char *name = resolve_ref(ref, sha1, 1, NULL); + if (name) + for_each_reflog_ent(name, read_one_reflog, reflogs); + } + if (reflogs->nr == 0) { + int len = strlen(ref); + char *refname = xmalloc(len + 12); + sprintf(refname, "refs/%s", ref); + for_each_reflog_ent(refname, read_one_reflog, reflogs); + if (reflogs->nr == 0) { + sprintf(refname, "refs/heads/%s", ref); + for_each_reflog_ent(refname, read_one_reflog, reflogs); + } + free(refname); + } + return reflogs; +} + +static int get_reflog_recno_by_time(struct complete_reflogs *array, + unsigned long timestamp) +{ + int i; + for (i = array->nr - 1; i >= 0; i++) + if (timestamp >= array->items[i].timestamp) + return i; + return -1; +} + +struct commit_info_lifo { + struct commit_info { + struct commit *commit; + void *util; + } *items; + int nr, alloc; +}; + +static struct commit_info *get_commit_info(struct commit *commit, + struct commit_info_lifo *lifo, int pop) +{ + int i; + for (i = 0; i < lifo->nr; i++) + if (lifo->items[i].commit == commit) { + struct commit_info *result = &lifo->items[i]; + if (pop) { + if (i + 1 < lifo->nr) + memmove(lifo->items + i, + lifo->items + i + 1, + (lifo->nr - i) * + sizeof(struct commit_info)); + lifo->nr--; + } + return result; + } + return NULL; +} + +static void add_commit_info(struct commit *commit, void *util, + struct commit_info_lifo *lifo) +{ + struct commit_info *info; + if (lifo->nr >= lifo->alloc) { + lifo->alloc = alloc_nr(lifo->nr + 1); + lifo->items = xrealloc(lifo->items, + lifo->alloc * sizeof(struct commit_info)); + } + info = lifo->items + lifo->nr; + info->commit = commit; + info->util = util; + lifo->nr++; +} + +struct commit_reflog { + int flag, recno; + struct complete_reflogs *reflogs; +}; + +struct reflog_walk_info { + struct commit_info_lifo reflogs; + struct path_list complete_reflogs; + struct commit_reflog *last_commit_reflog; +}; + +void init_reflog_walk(struct reflog_walk_info** info) +{ + *info = xcalloc(sizeof(struct reflog_walk_info), 1); +} + +void add_reflog_for_walk(struct reflog_walk_info *info, + struct commit *commit, const char *name) +{ + unsigned long timestamp = 0; + int recno = -1; + struct path_list_item *item; + struct complete_reflogs *reflogs; + char *branch, *at = strchr(name, '@'); + struct commit_reflog *commit_reflog; + + branch = xstrdup(name); + if (at && at[1] == '{') { + char *ep; + branch[at - name] = '\0'; + recno = strtoul(at + 2, &ep, 10); + if (*ep != '}') { + recno = -1; + timestamp = approxidate(at + 2); + } + } else + recno = 0; + + item = path_list_lookup(branch, &info->complete_reflogs); + if (item) + reflogs = item->util; + else { + reflogs = read_complete_reflog(branch); + if (!reflogs || reflogs->nr == 0) + die("No reflogs found for '%s'", branch); + path_list_insert(branch, &info->complete_reflogs)->util + = reflogs; + } + + commit_reflog = xcalloc(sizeof(struct commit_reflog), 1); + if (recno < 0) { + commit_reflog->flag = 1; + commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); + if (commit_reflog->recno < 0) { + free(branch); + free(commit_reflog); + return; + } + } else + commit_reflog->recno = reflogs->nr - recno - 1; + commit_reflog->reflogs = reflogs; + + add_commit_info(commit, commit_reflog, &info->reflogs); +} + +void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) +{ + struct commit_info *commit_info = + get_commit_info(commit, &info->reflogs, 0); + struct commit_reflog *commit_reflog; + struct reflog_info *reflog; + + info->last_commit_reflog = NULL; + if (!commit_info) + return; + + commit_reflog = commit_info->util; + if (commit_reflog->recno < 0) { + commit->parents = NULL; + return; + } + + reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; + info->last_commit_reflog = commit_reflog; + commit_reflog->recno--; + commit_info->commit = (struct commit *)parse_object(reflog->osha1); + if (!commit_info->commit) { + commit->parents = NULL; + return; + } + + commit->parents = xcalloc(sizeof(struct commit_list), 1); + commit->parents->item = commit_info->commit; + commit->object.flags &= ~(ADDED | SEEN | SHOWN); +} + +void show_reflog_message(struct reflog_walk_info* info) +{ + if (info && info->last_commit_reflog) { + struct commit_reflog *commit_reflog = info->last_commit_reflog; + struct reflog_info *info; + + printf("Reflog: %s@{", commit_reflog->reflogs->ref); + info = &commit_reflog->reflogs->items[commit_reflog->recno + 1]; + if (commit_reflog->flag) + printf("%s", show_rfc2822_date(info->timestamp, + info->tz)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("} (%s)\nReflog message: %s", + info->email, info->message); + } +} diff --git a/reflog-walk.h b/reflog-walk.h new file mode 100644 index 000000000..787996b37 --- /dev/null +++ b/reflog-walk.h @@ -0,0 +1,11 @@ +#ifndef REFLOG_WALK_H +#define REFLOG_WALK_H + +extern void init_reflog_walk(struct reflog_walk_info** info); +extern void add_reflog_for_walk(struct reflog_walk_info *info, + struct commit *commit, const char *name); +extern void fake_reflog_parent(struct reflog_walk_info *info, + struct commit *commit); +extern void show_reflog_message(struct reflog_walk_info* info); + +#endif diff --git a/revision.c b/revision.c index f2ddd95e2..ebd025064 100644 --- a/revision.c +++ b/revision.c @@ -7,6 +7,7 @@ #include "refs.h" #include "revision.h" #include "grep.h" +#include "reflog-walk.h" static char *path_name(struct name_path *path, const char *name) { @@ -116,6 +117,9 @@ void mark_parents_uninteresting(struct commit *commit) void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) { add_object_array(obj, name, &revs->pending); + if (revs->reflog_info && obj->type == OBJ_COMMIT) + add_reflog_for_walk(revs->reflog_info, + (struct commit *)obj, name); } static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) @@ -864,6 +868,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch handle_reflog(revs, flags); continue; } + if (!strcmp(arg, "--walk-reflogs")) { + init_reflog_walk(&revs->reflog_info); + continue; + } if (!strcmp(arg, "--not")) { flags ^= UNINTERESTING; continue; @@ -1210,6 +1218,9 @@ static struct commit *get_revision_1(struct rev_info *revs) revs->commits = entry->next; free(entry); + if (revs->reflog_info) + fake_reflog_parent(revs->reflog_info, commit); + /* * If we haven't done the list limiting, we need to look at * the parents here. We also need to do the date-based limiting diff --git a/revision.h b/revision.h index 8f7907d7a..d93481f68 100644 --- a/revision.h +++ b/revision.h @@ -89,6 +89,8 @@ struct rev_info { topo_sort_set_fn_t topo_setter; topo_sort_get_fn_t topo_getter; + + struct reflog_walk_info *reflog_info; }; #define REV_TREE_SAME 0 -- 2.26.2