Skip to content

Commit

Permalink
Teach the revision walker to walk by reflogs with --walk-reflogs
Browse files Browse the repository at this point in the history
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 <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
  • Loading branch information
dscho authored and Junio C Hamano committed Jan 21, 2007
1 parent bcf3161 commit 8860fd4
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -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 \
Expand Down
3 changes: 3 additions & 0 deletions log-tree.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}

/*
Expand Down
235 changes: 235 additions & 0 deletions 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);
}
}
11 changes: 11 additions & 0 deletions 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
11 changes: 11 additions & 0 deletions revision.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions revision.h
Expand Up @@ -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
Expand Down

0 comments on commit 8860fd4

Please sign in to comment.