Skip to content

Commit a660534

Browse files
committed
Merge branch 'jc/maint-no-reflog-expire-unreach-for-head'
* jc/maint-no-reflog-expire-unreach-for-head: reflog --expire-unreachable: special case entries in "HEAD" reflog more war on "sleep" in tests Document gc.<pattern>.reflogexpire variables Conflicts: Documentation/config.txt
2 parents 455bda9 + 03cb91b commit a660534

File tree

5 files changed

+92
-19
lines changed

5 files changed

+92
-19
lines changed

Documentation/config.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -946,13 +946,19 @@ gc.pruneexpire::
946946
unreachable objects immediately.
947947

948948
gc.reflogexpire::
949+
gc.<pattern>.reflogexpire::
949950
'git reflog expire' removes reflog entries older than
950-
this time; defaults to 90 days.
951+
this time; defaults to 90 days. With "<pattern>" (e.g.
952+
"refs/stash") in the middle the setting applies only to
953+
the refs that match the <pattern>.
951954

952955
gc.reflogexpireunreachable::
956+
gc.<ref>.reflogexpireunreachable::
953957
'git reflog expire' removes reflog entries older than
954958
this time and are not reachable from the current tip;
955-
defaults to 30 days.
959+
defaults to 30 days. With "<pattern>" (e.g. "refs/stash")
960+
in the middle, the setting applies only to the refs that
961+
match the <pattern>.
956962

957963
gc.rerereresolved::
958964
Records of conflicted merge you resolved earlier are

Documentation/git-gc.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ commits prior to the amend or rebase occurring. Since these changes
8888
are not part of the current project most users will want to expire
8989
them sooner. This option defaults to '30 days'.
9090

91+
The above two configuration variables can be given to a pattern. For
92+
example, this sets non-default expiry values only to remote tracking
93+
branches:
94+
95+
------------
96+
[gc "refs/remotes/*"]
97+
reflogExpire = never
98+
reflogexpireUnreachable = 3 days
99+
------------
100+
91101
The optional configuration variable 'gc.rerereresolved' indicates
92102
how long records of conflicted merge you resolved earlier are
93103
kept. This defaults to 60 days.

builtin/reflog.c

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ struct cmd_reflog_expire_cb {
3434

3535
struct expire_reflog_cb {
3636
FILE *newlog;
37-
const char *ref;
38-
struct commit *ref_commit;
37+
enum {
38+
UE_NORMAL,
39+
UE_ALWAYS,
40+
UE_HEAD
41+
} unreachable_expire_kind;
3942
struct commit_list *mark_list;
4043
unsigned long mark_limit;
4144
struct cmd_reflog_expire_cb *cmd;
@@ -305,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
305308
goto prune;
306309

307310
if (timestamp < cb->cmd->expire_unreachable) {
308-
if (!cb->ref_commit)
311+
if (cb->unreachable_expire_kind == UE_ALWAYS)
309312
goto prune;
310313
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
311314
goto prune;
@@ -332,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
332335
return 0;
333336
}
334337

338+
static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
339+
{
340+
struct commit_list **list = cb_data;
341+
struct commit *tip_commit;
342+
if (flags & REF_ISSYMREF)
343+
return 0;
344+
tip_commit = lookup_commit_reference_gently(sha1, 1);
345+
if (!tip_commit)
346+
return 0;
347+
commit_list_insert(tip_commit, list);
348+
return 0;
349+
}
350+
335351
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
336352
{
337353
struct cmd_reflog_expire_cb *cmd = cb_data;
338354
struct expire_reflog_cb cb;
339355
struct ref_lock *lock;
340356
char *log_file, *newlog_path = NULL;
357+
struct commit *tip_commit;
358+
struct commit_list *tips;
341359
int status = 0;
342360

343361
memset(&cb, 0, sizeof(cb));
@@ -357,18 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
357375
cb.newlog = fopen(newlog_path, "w");
358376
}
359377

360-
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
361-
cb.ref = ref;
362378
cb.cmd = cmd;
363-
if (cb.ref_commit) {
364-
cb.mark_list = NULL;
365-
commit_list_insert(cb.ref_commit, &cb.mark_list);
379+
380+
if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
381+
tip_commit = NULL;
382+
cb.unreachable_expire_kind = UE_HEAD;
383+
} else {
384+
tip_commit = lookup_commit_reference_gently(sha1, 1);
385+
if (!tip_commit)
386+
cb.unreachable_expire_kind = UE_ALWAYS;
387+
else
388+
cb.unreachable_expire_kind = UE_NORMAL;
389+
}
390+
391+
if (cmd->expire_unreachable <= cmd->expire_total)
392+
cb.unreachable_expire_kind = UE_ALWAYS;
393+
394+
cb.mark_list = NULL;
395+
tips = NULL;
396+
if (cb.unreachable_expire_kind != UE_ALWAYS) {
397+
if (cb.unreachable_expire_kind == UE_HEAD) {
398+
struct commit_list *elem;
399+
for_each_ref(push_tip_to_list, &tips);
400+
for (elem = tips; elem; elem = elem->next)
401+
commit_list_insert(elem->item, &cb.mark_list);
402+
} else {
403+
commit_list_insert(tip_commit, &cb.mark_list);
404+
}
366405
cb.mark_limit = cmd->expire_total;
367406
mark_reachable(&cb);
368407
}
408+
369409
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
370-
if (cb.ref_commit)
371-
clear_commit_marks(cb.ref_commit, REACHABLE);
410+
411+
if (cb.unreachable_expire_kind != UE_ALWAYS) {
412+
if (cb.unreachable_expire_kind == UE_HEAD) {
413+
struct commit_list *elem;
414+
for (elem = tips; elem; elem = elem->next)
415+
clear_commit_marks(tip_commit, REACHABLE);
416+
free_commit_list(tips);
417+
} else {
418+
clear_commit_marks(tip_commit, REACHABLE);
419+
}
420+
}
372421
finish:
373422
if (cb.newlog) {
374423
if (fclose(cb.newlog)) {

t/t7700-repack.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ test_expect_success 'objects in packs marked .keep are not repacked' '
88
echo content1 > file1 &&
99
echo content2 > file2 &&
1010
git add . &&
11+
test_tick &&
1112
git commit -m initial_commit &&
1213
# Create two packs
1314
# The first pack will contain all of the objects except one
@@ -40,6 +41,7 @@ test_expect_success 'loose objects in alternate ODB are not repacked' '
4041
echo content3 > file3 &&
4142
objsha1=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
4243
git add file3 &&
44+
test_tick &&
4345
git commit -m commit_file3 &&
4446
git repack -a -d -l &&
4547
git prune-packed &&
@@ -73,6 +75,7 @@ test_expect_success 'packed obs in alt ODB are repacked when local repo has pack
7375
rm -f .git/objects/pack/* &&
7476
echo new_content >> file1 &&
7577
git add file1 &&
78+
test_tick &&
7679
git commit -m more_content &&
7780
git repack &&
7881
git repack -a -d &&
@@ -118,8 +121,8 @@ test_expect_success 'packed unreachable obs in alternate ODB are not loosened' '
118121
mv .git/objects/pack/* alt_objects/pack/ &&
119122
csha1=$(git rev-parse HEAD^{commit}) &&
120123
git reset --hard HEAD^ &&
121-
sleep 1 &&
122-
git reflog expire --expire=now --expire-unreachable=now --all &&
124+
test_tick &&
125+
git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
123126
# The pack-objects call on the next line is equivalent to
124127
# git repack -A -d without the call to prune-packed
125128
git pack-objects --honor-pack-keep --non-empty --all --reflog \
@@ -156,7 +159,7 @@ test_expect_success 'objects made unreachable by grafts only are kept' '
156159
H1=$(git rev-parse HEAD^) &&
157160
H2=$(git rev-parse HEAD^^) &&
158161
echo "$H0 $H2" > .git/info/grafts &&
159-
git reflog expire --expire=now --expire-unreachable=now --all &&
162+
git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
160163
git repack -a -d &&
161164
git cat-file -t $H1
162165
'

t/t7701-repack-unpack-unreachable.sh

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,20 @@ tsha1=
1111
test_expect_success '-A with -d option leaves unreachable objects unpacked' '
1212
echo content > file1 &&
1313
git add . &&
14+
test_tick &&
1415
git commit -m initial_commit &&
1516
# create a transient branch with unique content
1617
git checkout -b transient_branch &&
1718
echo more content >> file1 &&
1819
# record the objects created in the database for file, commit, tree
1920
fsha1=$(git hash-object file1) &&
21+
test_tick &&
2022
git commit -a -m more_content &&
2123
csha1=$(git rev-parse HEAD^{commit}) &&
2224
tsha1=$(git rev-parse HEAD^{tree}) &&
2325
git checkout master &&
2426
echo even more content >> file1 &&
27+
test_tick &&
2528
git commit -a -m even_more_content &&
2629
# delete the transient branch
2730
git branch -D transient_branch &&
@@ -34,9 +37,11 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
3437
git show $fsha1 &&
3538
git show $csha1 &&
3639
git show $tsha1 &&
37-
# now expire the reflog
38-
sleep 1 &&
39-
git reflog expire --expire-unreachable=now --all &&
40+
# now expire the reflog, while keeping reachable ones but expiring
41+
# unreachables immediately
42+
test_tick &&
43+
sometimeago=$(( $test_tick - 10000 )) &&
44+
git reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&
4045
# and repack
4146
git repack -A -d -l &&
4247
# verify objects are retained unpacked
@@ -71,7 +76,7 @@ test_expect_success '-A without -d option leaves unreachable objects packed' '
7176
test 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&
7277
packfile=$(ls .git/objects/pack/pack-*.pack) &&
7378
git branch -D transient_branch &&
74-
sleep 1 &&
79+
test_tick &&
7580
git repack -A -l &&
7681
test ! -f "$fsha1path" &&
7782
test ! -f "$csha1path" &&

0 commit comments

Comments
 (0)