unreachable objects immediately.
gc.reflogexpire::
+gc.<pattern>.reflogexpire::
'git reflog expire' removes reflog entries older than
- this time; defaults to 90 days.
+ this time; defaults to 90 days. With "<pattern>" (e.g.
+ "refs/stash") in the middle the setting applies only to
+ the refs that match the <pattern>.
gc.reflogexpireunreachable::
+gc.<ref>.reflogexpireunreachable::
'git reflog expire' removes reflog entries older than
this time and are not reachable from the current tip;
- defaults to 30 days.
+ defaults to 30 days. With "<pattern>" (e.g. "refs/stash")
+ in the middle, the setting applies only to the refs that
+ match the <pattern>.
gc.rerereresolved::
Records of conflicted merge you resolved earlier are
are not part of the current project most users will want to expire
them sooner. This option defaults to '30 days'.
+The above two configuration variables can be given to a pattern. For
+example, this sets non-default expiry values only to remote tracking
+branches:
+
+------------
+[gc "refs/remotes/*"]
+ reflogExpire = never
+ reflogexpireUnreachable = 3 days
+------------
+
The optional configuration variable 'gc.rerereresolved' indicates
how long records of conflicted merge you resolved earlier are
kept. This defaults to 60 days.
struct expire_reflog_cb {
FILE *newlog;
- const char *ref;
- struct commit *ref_commit;
+ enum {
+ UE_NORMAL,
+ UE_ALWAYS,
+ UE_HEAD
+ } unreachable_expire_kind;
struct commit_list *mark_list;
unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
goto prune;
if (timestamp < cb->cmd->expire_unreachable) {
- if (!cb->ref_commit)
+ if (cb->unreachable_expire_kind == UE_ALWAYS)
goto prune;
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
goto prune;
return 0;
}
+static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct commit_list **list = cb_data;
+ struct commit *tip_commit;
+ if (flags & REF_ISSYMREF)
+ return 0;
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ return 0;
+ commit_list_insert(tip_commit, list);
+ return 0;
+}
+
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
{
struct cmd_reflog_expire_cb *cmd = cb_data;
struct expire_reflog_cb cb;
struct ref_lock *lock;
char *log_file, *newlog_path = NULL;
+ struct commit *tip_commit;
+ struct commit_list *tips;
int status = 0;
memset(&cb, 0, sizeof(cb));
cb.newlog = fopen(newlog_path, "w");
}
- cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
- cb.ref = ref;
cb.cmd = cmd;
- if (cb.ref_commit) {
- cb.mark_list = NULL;
- commit_list_insert(cb.ref_commit, &cb.mark_list);
+
+ if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
+ tip_commit = NULL;
+ cb.unreachable_expire_kind = UE_HEAD;
+ } else {
+ tip_commit = lookup_commit_reference_gently(sha1, 1);
+ if (!tip_commit)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+ else
+ cb.unreachable_expire_kind = UE_NORMAL;
+ }
+
+ if (cmd->expire_unreachable <= cmd->expire_total)
+ cb.unreachable_expire_kind = UE_ALWAYS;
+
+ cb.mark_list = NULL;
+ tips = NULL;
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for_each_ref(push_tip_to_list, &tips);
+ for (elem = tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb.mark_list);
+ } else {
+ commit_list_insert(tip_commit, &cb.mark_list);
+ }
cb.mark_limit = cmd->expire_total;
mark_reachable(&cb);
}
+
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
- if (cb.ref_commit)
- clear_commit_marks(cb.ref_commit, REACHABLE);
+
+ if (cb.unreachable_expire_kind != UE_ALWAYS) {
+ if (cb.unreachable_expire_kind == UE_HEAD) {
+ struct commit_list *elem;
+ for (elem = tips; elem; elem = elem->next)
+ clear_commit_marks(tip_commit, REACHABLE);
+ free_commit_list(tips);
+ } else {
+ clear_commit_marks(tip_commit, REACHABLE);
+ }
+ }
finish:
if (cb.newlog) {
if (fclose(cb.newlog)) {
echo content1 > file1 &&
echo content2 > file2 &&
git add . &&
+ test_tick &&
git commit -m initial_commit &&
# Create two packs
# The first pack will contain all of the objects except one
echo content3 > file3 &&
objsha1=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
git add file3 &&
+ test_tick &&
git commit -m commit_file3 &&
git repack -a -d -l &&
git prune-packed &&
rm -f .git/objects/pack/* &&
echo new_content >> file1 &&
git add file1 &&
+ test_tick &&
git commit -m more_content &&
git repack &&
git repack -a -d &&
mv .git/objects/pack/* alt_objects/pack/ &&
csha1=$(git rev-parse HEAD^{commit}) &&
git reset --hard HEAD^ &&
- sleep 1 &&
- git reflog expire --expire=now --expire-unreachable=now --all &&
+ test_tick &&
+ git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
# The pack-objects call on the next line is equivalent to
# git repack -A -d without the call to prune-packed
git pack-objects --honor-pack-keep --non-empty --all --reflog \
H1=$(git rev-parse HEAD^) &&
H2=$(git rev-parse HEAD^^) &&
echo "$H0 $H2" > .git/info/grafts &&
- git reflog expire --expire=now --expire-unreachable=now --all &&
+ git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
git repack -a -d &&
git cat-file -t $H1
'
test_expect_success '-A with -d option leaves unreachable objects unpacked' '
echo content > file1 &&
git add . &&
+ test_tick &&
git commit -m initial_commit &&
# create a transient branch with unique content
git checkout -b transient_branch &&
echo more content >> file1 &&
# record the objects created in the database for file, commit, tree
fsha1=$(git hash-object file1) &&
+ test_tick &&
git commit -a -m more_content &&
csha1=$(git rev-parse HEAD^{commit}) &&
tsha1=$(git rev-parse HEAD^{tree}) &&
git checkout master &&
echo even more content >> file1 &&
+ test_tick &&
git commit -a -m even_more_content &&
# delete the transient branch
git branch -D transient_branch &&
git show $fsha1 &&
git show $csha1 &&
git show $tsha1 &&
- # now expire the reflog
- sleep 1 &&
- git reflog expire --expire-unreachable=now --all &&
+ # now expire the reflog, while keeping reachable ones but expiring
+ # unreachables immediately
+ test_tick &&
+ sometimeago=$(( $test_tick - 10000 )) &&
+ git reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&
# and repack
git repack -A -d -l &&
# verify objects are retained unpacked
test 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&
packfile=$(ls .git/objects/pack/pack-*.pack) &&
git branch -D transient_branch &&
- sleep 1 &&
+ test_tick &&
git repack -A -l &&
test ! -f "$fsha1path" &&
test ! -f "$csha1path" &&