Git fork
1#define USE_THE_REPOSITORY_VARIABLE
2#define DISABLE_SIGN_COMPARE_WARNINGS
3
4#include "git-compat-util.h"
5#include "config.h"
6#include "environment.h"
7#include "gettext.h"
8#include "parse-options.h"
9#include "odb.h"
10#include "reflog.h"
11#include "refs.h"
12#include "revision.h"
13#include "tree.h"
14#include "tree-walk.h"
15#include "wildmatch.h"
16
17static struct reflog_expire_entry_option *find_cfg_ent(struct reflog_expire_options *opts,
18 const char *pattern, size_t len)
19{
20 struct reflog_expire_entry_option *ent;
21
22 if (!opts->entries_tail)
23 opts->entries_tail = &opts->entries;
24
25 for (ent = opts->entries; ent; ent = ent->next)
26 if (!xstrncmpz(ent->pattern, pattern, len))
27 return ent;
28
29 FLEX_ALLOC_MEM(ent, pattern, pattern, len);
30 *opts->entries_tail = ent;
31 opts->entries_tail = &(ent->next);
32 return ent;
33}
34
35int reflog_expire_config(const char *var, const char *value,
36 const struct config_context *ctx, void *cb)
37{
38 struct reflog_expire_options *opts = cb;
39 const char *pattern, *key;
40 size_t pattern_len;
41 timestamp_t expire;
42 int slot;
43 struct reflog_expire_entry_option *ent;
44
45 if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
46 return git_default_config(var, value, ctx, cb);
47
48 if (!strcmp(key, "reflogexpire")) {
49 slot = REFLOG_EXPIRE_TOTAL;
50 if (git_config_expiry_date(&expire, var, value))
51 return -1;
52 } else if (!strcmp(key, "reflogexpireunreachable")) {
53 slot = REFLOG_EXPIRE_UNREACH;
54 if (git_config_expiry_date(&expire, var, value))
55 return -1;
56 } else
57 return git_default_config(var, value, ctx, cb);
58
59 if (!pattern) {
60 switch (slot) {
61 case REFLOG_EXPIRE_TOTAL:
62 opts->default_expire_total = expire;
63 break;
64 case REFLOG_EXPIRE_UNREACH:
65 opts->default_expire_unreachable = expire;
66 break;
67 }
68 return 0;
69 }
70
71 ent = find_cfg_ent(opts, pattern, pattern_len);
72 if (!ent)
73 return -1;
74 switch (slot) {
75 case REFLOG_EXPIRE_TOTAL:
76 ent->expire_total = expire;
77 break;
78 case REFLOG_EXPIRE_UNREACH:
79 ent->expire_unreachable = expire;
80 break;
81 }
82 return 0;
83}
84
85void reflog_clear_expire_config(struct reflog_expire_options *opts)
86{
87 struct reflog_expire_entry_option *ent = opts->entries, *tmp;
88
89 while (ent) {
90 tmp = ent;
91 ent = ent->next;
92 free(tmp);
93 }
94
95 opts->entries = NULL;
96 opts->entries_tail = NULL;
97}
98
99void reflog_expire_options_set_refname(struct reflog_expire_options *cb,
100 const char *ref)
101{
102 struct reflog_expire_entry_option *ent;
103
104 if (cb->explicit_expiry == (REFLOG_EXPIRE_TOTAL|REFLOG_EXPIRE_UNREACH))
105 return; /* both given explicitly -- nothing to tweak */
106
107 for (ent = cb->entries; ent; ent = ent->next) {
108 if (!wildmatch(ent->pattern, ref, 0)) {
109 if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
110 cb->expire_total = ent->expire_total;
111 if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
112 cb->expire_unreachable = ent->expire_unreachable;
113 return;
114 }
115 }
116
117 /*
118 * If unconfigured, make stash never expire
119 */
120 if (!strcmp(ref, "refs/stash")) {
121 if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
122 cb->expire_total = 0;
123 if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
124 cb->expire_unreachable = 0;
125 return;
126 }
127
128 /* Nothing matched -- use the default value */
129 if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
130 cb->expire_total = cb->default_expire_total;
131 if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
132 cb->expire_unreachable = cb->default_expire_unreachable;
133}
134
135/* Remember to update object flag allocation in object.h */
136#define INCOMPLETE (1u<<10)
137#define STUDYING (1u<<11)
138#define REACHABLE (1u<<12)
139
140static int tree_is_complete(const struct object_id *oid)
141{
142 struct tree_desc desc;
143 struct name_entry entry;
144 int complete;
145 struct tree *tree;
146
147 tree = lookup_tree(the_repository, oid);
148 if (!tree)
149 return 0;
150 if (tree->object.flags & SEEN)
151 return 1;
152 if (tree->object.flags & INCOMPLETE)
153 return 0;
154
155 if (!tree->buffer) {
156 enum object_type type;
157 unsigned long size;
158 void *data = odb_read_object(the_repository->objects, oid,
159 &type, &size);
160 if (!data) {
161 tree->object.flags |= INCOMPLETE;
162 return 0;
163 }
164 tree->buffer = data;
165 tree->size = size;
166 }
167 init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
168 complete = 1;
169 while (tree_entry(&desc, &entry)) {
170 if (!odb_has_object(the_repository->objects, &entry.oid,
171 HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
172 (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
173 tree->object.flags |= INCOMPLETE;
174 complete = 0;
175 }
176 }
177 free_tree_buffer(tree);
178
179 if (complete)
180 tree->object.flags |= SEEN;
181 return complete;
182}
183
184static int commit_is_complete(struct commit *commit)
185{
186 struct object_array study;
187 struct object_array found;
188 int is_incomplete = 0;
189 int i;
190
191 /* early return */
192 if (commit->object.flags & SEEN)
193 return 1;
194 if (commit->object.flags & INCOMPLETE)
195 return 0;
196 /*
197 * Find all commits that are reachable and are not marked as
198 * SEEN. Then make sure the trees and blobs contained are
199 * complete. After that, mark these commits also as SEEN.
200 * If some of the objects that are needed to complete this
201 * commit are missing, mark this commit as INCOMPLETE.
202 */
203 memset(&study, 0, sizeof(study));
204 memset(&found, 0, sizeof(found));
205 add_object_array(&commit->object, NULL, &study);
206 add_object_array(&commit->object, NULL, &found);
207 commit->object.flags |= STUDYING;
208 while (study.nr) {
209 struct commit *c;
210 struct commit_list *parent;
211
212 c = (struct commit *)object_array_pop(&study);
213 if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
214 c->object.flags |= INCOMPLETE;
215
216 if (c->object.flags & INCOMPLETE) {
217 is_incomplete = 1;
218 break;
219 }
220 else if (c->object.flags & SEEN)
221 continue;
222 for (parent = c->parents; parent; parent = parent->next) {
223 struct commit *p = parent->item;
224 if (p->object.flags & STUDYING)
225 continue;
226 p->object.flags |= STUDYING;
227 add_object_array(&p->object, NULL, &study);
228 add_object_array(&p->object, NULL, &found);
229 }
230 }
231 if (!is_incomplete) {
232 /*
233 * make sure all commits in "found" array have all the
234 * necessary objects.
235 */
236 for (i = 0; i < found.nr; i++) {
237 struct commit *c =
238 (struct commit *)found.objects[i].item;
239 if (!tree_is_complete(get_commit_tree_oid(c))) {
240 is_incomplete = 1;
241 c->object.flags |= INCOMPLETE;
242 }
243 }
244 if (!is_incomplete) {
245 /* mark all found commits as complete, iow SEEN */
246 for (i = 0; i < found.nr; i++)
247 found.objects[i].item->flags |= SEEN;
248 }
249 }
250 /* clear flags from the objects we traversed */
251 for (i = 0; i < found.nr; i++)
252 found.objects[i].item->flags &= ~STUDYING;
253 if (is_incomplete)
254 commit->object.flags |= INCOMPLETE;
255 else {
256 /*
257 * If we come here, we have (1) traversed the ancestry chain
258 * from the "commit" until we reach SEEN commits (which are
259 * known to be complete), and (2) made sure that the commits
260 * encountered during the above traversal refer to trees that
261 * are complete. Which means that we know *all* the commits
262 * we have seen during this process are complete.
263 */
264 for (i = 0; i < found.nr; i++)
265 found.objects[i].item->flags |= SEEN;
266 }
267 /* free object arrays */
268 object_array_clear(&study);
269 object_array_clear(&found);
270 return !is_incomplete;
271}
272
273static int keep_entry(struct commit **it, struct object_id *oid)
274{
275 struct commit *commit;
276
277 if (is_null_oid(oid))
278 return 1;
279 commit = lookup_commit_reference_gently(the_repository, oid, 1);
280 if (!commit)
281 return 0;
282
283 /*
284 * Make sure everything in this commit exists.
285 *
286 * We have walked all the objects reachable from the refs
287 * and cache earlier. The commits reachable by this commit
288 * must meet SEEN commits -- and then we should mark them as
289 * SEEN as well.
290 */
291 if (!commit_is_complete(commit))
292 return 0;
293 *it = commit;
294 return 1;
295}
296
297/*
298 * Starting from commits in the cb->mark_list, mark commits that are
299 * reachable from them. Stop the traversal at commits older than
300 * the expire_limit and queue them back, so that the caller can call
301 * us again to restart the traversal with longer expire_limit.
302 */
303static void mark_reachable(struct expire_reflog_policy_cb *cb)
304{
305 struct commit_list *pending;
306 timestamp_t expire_limit = cb->mark_limit;
307 struct commit_list *leftover = NULL;
308
309 for (pending = cb->mark_list; pending; pending = pending->next)
310 pending->item->object.flags &= ~REACHABLE;
311
312 pending = cb->mark_list;
313 while (pending) {
314 struct commit_list *parent;
315 struct commit *commit = pop_commit(&pending);
316 if (commit->object.flags & REACHABLE)
317 continue;
318 if (repo_parse_commit(the_repository, commit))
319 continue;
320 commit->object.flags |= REACHABLE;
321 if (commit->date < expire_limit) {
322 commit_list_insert(commit, &leftover);
323 continue;
324 }
325 parent = commit->parents;
326 while (parent) {
327 commit = parent->item;
328 parent = parent->next;
329 if (commit->object.flags & REACHABLE)
330 continue;
331 commit_list_insert(commit, &pending);
332 }
333 }
334 cb->mark_list = leftover;
335}
336
337static int is_unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
338{
339 /*
340 * We may or may not have the commit yet - if not, look it
341 * up using the supplied sha1.
342 */
343 if (!commit) {
344 if (is_null_oid(oid))
345 return 0;
346
347 commit = lookup_commit_reference_gently(the_repository, oid,
348 1);
349
350 /* Not a commit -- keep it */
351 if (!commit)
352 return 0;
353 }
354
355 /* Reachable from the current ref? Don't prune. */
356 if (commit->object.flags & REACHABLE)
357 return 0;
358
359 if (cb->mark_list && cb->mark_limit) {
360 cb->mark_limit = 0; /* dig down to the root */
361 mark_reachable(cb);
362 }
363
364 return !(commit->object.flags & REACHABLE);
365}
366
367/*
368 * Return true iff the specified reflog entry should be expired.
369 */
370int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
371 const char *email UNUSED,
372 timestamp_t timestamp, int tz UNUSED,
373 const char *message UNUSED, void *cb_data)
374{
375 struct expire_reflog_policy_cb *cb = cb_data;
376 struct commit *old_commit, *new_commit;
377
378 if (timestamp < cb->opts.expire_total)
379 return 1;
380
381 old_commit = new_commit = NULL;
382 if (cb->opts.stalefix &&
383 (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
384 return 1;
385
386 if (timestamp < cb->opts.expire_unreachable) {
387 switch (cb->unreachable_expire_kind) {
388 case UE_ALWAYS:
389 return 1;
390 case UE_NORMAL:
391 case UE_HEAD:
392 if (is_unreachable(cb, old_commit, ooid) || is_unreachable(cb, new_commit, noid))
393 return 1;
394 break;
395 }
396 }
397
398 if (cb->opts.recno && --(cb->opts.recno) == 0)
399 return 1;
400
401 return 0;
402}
403
404int should_expire_reflog_ent_verbose(struct object_id *ooid,
405 struct object_id *noid,
406 const char *email,
407 timestamp_t timestamp, int tz,
408 const char *message, void *cb_data)
409{
410 struct expire_reflog_policy_cb *cb = cb_data;
411 int expire;
412
413 expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
414 message, cb);
415
416 if (!expire)
417 printf("keep %s", message);
418 else if (cb->dry_run)
419 printf("would prune %s", message);
420 else
421 printf("prune %s", message);
422
423 return expire;
424}
425
426static int push_tip_to_list(const char *refname UNUSED,
427 const char *referent UNUSED,
428 const struct object_id *oid,
429 int flags, void *cb_data)
430{
431 struct commit_list **list = cb_data;
432 struct commit *tip_commit;
433 if (flags & REF_ISSYMREF)
434 return 0;
435 tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
436 if (!tip_commit)
437 return 0;
438 commit_list_insert(tip_commit, list);
439 return 0;
440}
441
442static int is_head(const char *refname)
443{
444 const char *stripped_refname;
445 parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
446 return !strcmp(stripped_refname, "HEAD");
447}
448
449void reflog_expiry_prepare(const char *refname,
450 const struct object_id *oid,
451 void *cb_data)
452{
453 struct expire_reflog_policy_cb *cb = cb_data;
454 struct commit_list *elem;
455 struct commit *commit = NULL;
456
457 if (!cb->opts.expire_unreachable || is_head(refname)) {
458 cb->unreachable_expire_kind = UE_HEAD;
459 } else {
460 commit = lookup_commit_reference_gently(the_repository,
461 oid, 1);
462 if (commit && is_null_oid(&commit->object.oid))
463 commit = NULL;
464 cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
465 }
466
467 if (cb->opts.expire_unreachable <= cb->opts.expire_total)
468 cb->unreachable_expire_kind = UE_ALWAYS;
469
470 switch (cb->unreachable_expire_kind) {
471 case UE_ALWAYS:
472 return;
473 case UE_HEAD:
474 refs_for_each_ref(get_main_ref_store(the_repository),
475 push_tip_to_list, &cb->tips);
476 for (elem = cb->tips; elem; elem = elem->next)
477 commit_list_insert(elem->item, &cb->mark_list);
478 break;
479 case UE_NORMAL:
480 commit_list_insert(commit, &cb->mark_list);
481 /* For reflog_expiry_cleanup() below */
482 cb->tip_commit = commit;
483 }
484 cb->mark_limit = cb->opts.expire_total;
485 mark_reachable(cb);
486}
487
488void reflog_expiry_cleanup(void *cb_data)
489{
490 struct expire_reflog_policy_cb *cb = cb_data;
491 struct commit_list *elem;
492
493 switch (cb->unreachable_expire_kind) {
494 case UE_ALWAYS:
495 return;
496 case UE_HEAD:
497 for (elem = cb->tips; elem; elem = elem->next)
498 clear_commit_marks(elem->item, REACHABLE);
499 free_commit_list(cb->tips);
500 break;
501 case UE_NORMAL:
502 clear_commit_marks(cb->tip_commit, REACHABLE);
503 break;
504 }
505 for (elem = cb->mark_list; elem; elem = elem->next)
506 clear_commit_marks(elem->item, REACHABLE);
507 free_commit_list(cb->mark_list);
508}
509
510int count_reflog_ent(const char *refname UNUSED,
511 struct object_id *ooid UNUSED,
512 struct object_id *noid UNUSED,
513 const char *email UNUSED,
514 timestamp_t timestamp, int tz UNUSED,
515 const char *message UNUSED, void *cb_data)
516{
517 struct reflog_expire_options *cb = cb_data;
518 if (!cb->expire_total || timestamp < cb->expire_total)
519 cb->recno++;
520 return 0;
521}
522
523int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
524{
525 struct reflog_expire_options opts = { 0 };
526 int status = 0;
527 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
528 const char *spec = strstr(rev, "@{");
529 char *ep, *ref;
530 int recno;
531 struct expire_reflog_policy_cb cb = {
532 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
533 };
534
535 if (verbose)
536 should_prune_fn = should_expire_reflog_ent_verbose;
537
538 if (!spec)
539 return error(_("not a reflog: %s"), rev);
540
541 if (!repo_dwim_log(the_repository, rev, spec - rev, NULL, &ref)) {
542 status |= error(_("no reflog for '%s'"), rev);
543 goto cleanup;
544 }
545
546 recno = strtoul(spec + 2, &ep, 10);
547 if (*ep == '}') {
548 opts.recno = -recno;
549 refs_for_each_reflog_ent(get_main_ref_store(the_repository),
550 ref, count_reflog_ent, &opts);
551 } else {
552 opts.expire_total = approxidate(spec + 2);
553 refs_for_each_reflog_ent(get_main_ref_store(the_repository),
554 ref, count_reflog_ent, &opts);
555 opts.expire_total = 0;
556 }
557
558 cb.opts = opts;
559 status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
560 flags,
561 reflog_expiry_prepare,
562 should_prune_fn,
563 reflog_expiry_cleanup,
564 &cb);
565
566 cleanup:
567 free(ref);
568 return status;
569}