8 static unsigned long default_reflog_expire
;
9 static unsigned long default_reflog_expire_unreachable
;
11 struct expire_reflog_cb
{
14 struct commit
*ref_commit
;
15 unsigned long expire_total
;
16 unsigned long expire_unreachable
;
19 static int tree_is_complete(const unsigned char *sha1
)
21 struct tree_desc desc
;
25 buf
= read_sha1_file(sha1
, type
, &desc
.size
);
30 const unsigned char *elem
;
34 elem
= tree_entry_extract(&desc
, &name
, &mode
);
35 if (!has_sha1_file(elem
) ||
36 (S_ISDIR(mode
) && !tree_is_complete(elem
))) {
40 update_tree_entry(&desc
);
46 static int keep_entry(struct commit
**it
, unsigned char *sha1
)
48 struct commit
*commit
;
51 if (is_null_sha1(sha1
))
53 commit
= lookup_commit_reference_gently(sha1
, 1);
57 /* Make sure everything in this commit exists. */
58 parse_object(commit
->object
.sha1
);
59 if (!tree_is_complete(commit
->tree
->object
.sha1
))
65 static int expire_reflog_ent(unsigned char *osha1
, unsigned char *nsha1
,
66 char *data
, void *cb_data
)
68 struct expire_reflog_cb
*cb
= cb_data
;
69 unsigned long timestamp
;
71 struct commit
*old
, *new;
73 cp
= strchr(data
, '>');
74 if (!cp
|| *++cp
!= ' ')
76 timestamp
= strtoul(cp
, &ep
, 10);
79 if (timestamp
< cb
->expire_total
)
82 if (!keep_entry(&old
, osha1
) || !keep_entry(&new, nsha1
))
85 if ((timestamp
< cb
->expire_unreachable
) &&
87 (old
&& !in_merge_bases(old
, cb
->ref_commit
)) ||
88 (new && !in_merge_bases(new, cb
->ref_commit
))))
92 fprintf(cb
->newlog
, "%s %s %s",
93 sha1_to_hex(osha1
), sha1_to_hex(nsha1
), data
);
97 fprintf(stderr
, "would prune %s", data
);
101 struct cmd_reflog_expire_cb
{
103 unsigned long expire_total
;
104 unsigned long expire_unreachable
;
107 static int expire_reflog(const char *ref
, const unsigned char *sha1
, int unused
, void *cb_data
)
109 struct cmd_reflog_expire_cb
*cmd
= cb_data
;
110 struct expire_reflog_cb cb
;
111 struct ref_lock
*lock
;
112 char *newlog_path
= NULL
;
115 if (strncmp(ref
, "refs/", 5))
116 return error("not a ref '%s'", ref
);
118 memset(&cb
, 0, sizeof(cb
));
119 /* we take the lock for the ref itself to prevent it from
122 lock
= lock_ref_sha1(ref
+ 5, sha1
);
124 return error("cannot lock ref '%s'", ref
);
125 if (!file_exists(lock
->log_file
))
128 newlog_path
= xstrdup(git_path("logs/%s.lock", ref
));
129 cb
.newlog
= fopen(newlog_path
, "w");
132 cb
.ref_commit
= lookup_commit_reference_gently(sha1
, 1);
135 "warning: ref '%s' does not point at a commit\n", ref
);
137 cb
.expire_total
= cmd
->expire_total
;
138 cb
.expire_unreachable
= cmd
->expire_unreachable
;
139 for_each_reflog_ent(ref
, expire_reflog_ent
, &cb
);
142 if (fclose(cb
.newlog
))
143 status
|= error("%s: %s", strerror(errno
),
145 if (rename(newlog_path
, lock
->log_file
)) {
146 status
|= error("cannot rename %s to %s",
147 newlog_path
, lock
->log_file
);
156 static int reflog_expire_config(const char *var
, const char *value
)
158 if (!strcmp(var
, "gc.reflogexpire"))
159 default_reflog_expire
= approxidate(value
);
160 else if (!strcmp(var
, "gc.reflogexpireunreachable"))
161 default_reflog_expire_unreachable
= approxidate(value
);
163 return git_default_config(var
, value
);
167 static const char reflog_expire_usage
[] =
168 "git-reflog expire [--dry-run] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
170 static int cmd_reflog_expire(int argc
, const char **argv
, const char *prefix
)
172 struct cmd_reflog_expire_cb cb
;
173 unsigned long now
= time(NULL
);
174 int i
, status
, do_all
;
176 git_config(reflog_expire_config
);
178 save_commit_buffer
= 0;
180 memset(&cb
, 0, sizeof(cb
));
182 if (!default_reflog_expire_unreachable
)
183 default_reflog_expire_unreachable
= now
- 30 * 24 * 3600;
184 if (!default_reflog_expire
)
185 default_reflog_expire
= now
- 90 * 24 * 3600;
186 cb
.expire_total
= default_reflog_expire
;
187 cb
.expire_unreachable
= default_reflog_expire_unreachable
;
189 for (i
= 1; i
< argc
; i
++) {
190 const char *arg
= argv
[i
];
191 if (!strcmp(arg
, "--dry-run") || !strcmp(arg
, "-n"))
193 else if (!strncmp(arg
, "--expire=", 9))
194 cb
.expire_total
= approxidate(arg
+ 9);
195 else if (!strncmp(arg
, "--expire-unreachable=", 21))
196 cb
.expire_unreachable
= approxidate(arg
+ 21);
197 else if (!strcmp(arg
, "--all"))
199 else if (!strcmp(arg
, "--")) {
203 else if (arg
[0] == '-')
204 usage(reflog_expire_usage
);
209 status
|= for_each_ref(expire_reflog
, &cb
);
211 const char *ref
= argv
[i
++];
212 unsigned char sha1
[20];
213 if (!resolve_ref(ref
, sha1
, 1, NULL
)) {
214 status
|= error("%s points nowhere!", ref
);
217 status
|= expire_reflog(ref
, sha1
, 0, &cb
);
222 static const char reflog_usage
[] =
223 "git-reflog (expire | ...)";
225 int cmd_reflog(int argc
, const char **argv
, const char *prefix
)
229 else if (!strcmp(argv
[1], "expire"))
230 return cmd_reflog_expire(argc
- 1, argv
+ 1, prefix
);