Skip to content

Commit eb8381c

Browse files
Nicolas PitreJunio C Hamano
authored andcommitted
scan reflogs independently from refs
Currently, the search for all reflogs depends on the existence of corresponding refs under the .git/refs/ directory. Let's scan the .git/logs/ directory directly instead. Signed-off-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent a7e4fbf commit eb8381c

File tree

5 files changed

+67
-9
lines changed

5 files changed

+67
-9
lines changed

builtin-reflog.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
245245
char *log_file, *newlog_path = NULL;
246246
int status = 0;
247247

248-
if (strncmp(ref, "refs/", 5))
249-
return error("not a ref '%s'", ref);
250-
251248
memset(&cb, 0, sizeof(cb));
252249
/* we take the lock for the ref itself to prevent it from
253250
* getting updated.
254251
*/
255-
lock = lock_ref_sha1(ref + 5, sha1);
252+
lock = lock_any_ref_for_update(ref, sha1);
256253
if (!lock)
257254
return error("cannot lock ref '%s'", ref);
258255
log_file = xstrdup(git_path("logs/%s", ref));
@@ -353,7 +350,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
353350
}
354351

355352
if (do_all)
356-
status |= for_each_ref(expire_reflog, &cb);
353+
status |= for_each_reflog(expire_reflog, &cb);
357354
while (i < argc) {
358355
const char *ref = argv[i++];
359356
unsigned char sha1[20];

fsck-objects.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,12 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
477477
return 0;
478478
}
479479

480+
static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
481+
{
482+
for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
483+
return 0;
484+
}
485+
480486
static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
481487
{
482488
struct object *obj;
@@ -495,14 +501,13 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
495501
obj->used = 1;
496502
mark_reachable(obj, REACHABLE);
497503

498-
for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);
499-
500504
return 0;
501505
}
502506

503507
static void get_default_heads(void)
504508
{
505509
for_each_ref(fsck_handle_ref, NULL);
510+
for_each_reflog(fsck_handle_reflog, NULL);
506511

507512
/*
508513
* Not having any default heads isn't really fatal, but

reachable.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
188188
/* Add all external refs */
189189
for_each_ref(add_one_ref, revs);
190190

191-
/* Add all reflog info from refs */
191+
/* Add all reflog info */
192192
if (mark_reflog)
193-
for_each_ref(add_one_reflog, revs);
193+
for_each_reflog(add_one_reflog, revs);
194194

195195
/*
196196
* Set up the revision walk - this will move all commits

refs.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,3 +1201,53 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
12011201
return ret;
12021202
}
12031203

1204+
static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
1205+
{
1206+
DIR *dir = opendir(git_path("logs/%s", base));
1207+
int retval = errno;
1208+
1209+
if (dir) {
1210+
struct dirent *de;
1211+
int baselen = strlen(base);
1212+
char *log = xmalloc(baselen + 257);
1213+
1214+
memcpy(log, base, baselen);
1215+
if (baselen && base[baselen-1] != '/')
1216+
log[baselen++] = '/';
1217+
1218+
while ((de = readdir(dir)) != NULL) {
1219+
struct stat st;
1220+
int namelen;
1221+
1222+
if (de->d_name[0] == '.')
1223+
continue;
1224+
namelen = strlen(de->d_name);
1225+
if (namelen > 255)
1226+
continue;
1227+
if (has_extension(de->d_name, ".lock"))
1228+
continue;
1229+
memcpy(log + baselen, de->d_name, namelen+1);
1230+
if (stat(git_path("logs/%s", log), &st) < 0)
1231+
continue;
1232+
if (S_ISDIR(st.st_mode)) {
1233+
retval = do_for_each_reflog(log, fn, cb_data);
1234+
} else {
1235+
unsigned char sha1[20];
1236+
if (!resolve_ref(log, sha1, 0, NULL))
1237+
retval = error("bad ref for %s", log);
1238+
else
1239+
retval = fn(log, sha1, 0, cb_data);
1240+
}
1241+
if (retval)
1242+
break;
1243+
}
1244+
free(log);
1245+
closedir(dir);
1246+
}
1247+
return retval;
1248+
}
1249+
1250+
int for_each_reflog(each_ref_fn fn, void *cb_data)
1251+
{
1252+
return do_for_each_reflog("", fn, cb_data);
1253+
}

refs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned
4848
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
4949
int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
5050

51+
/*
52+
* Calls the specified function for each reflog file until it returns nonzero,
53+
* and returns the value
54+
*/
55+
extern int for_each_reflog(each_ref_fn, void *);
56+
5157
/** Returns 0 if target has the right format for a ref. **/
5258
extern int check_ref_format(const char *target);
5359

0 commit comments

Comments
 (0)