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 <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
bcf3161876
commit
8860fd42fc
2
Makefile
2
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 \
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
235
reflog-walk.c
Normal file
235
reflog-walk.c
Normal file
@ -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
reflog-walk.h
Normal file
11
reflog-walk.h
Normal file
@ -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
revision.c
11
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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user