strvec: declare the `strvec_push_nodup()` function globally
[git.git] / reflog.c
blob5ca944529b30ccaf30d527789478349d915f10c1
1 #define USE_THE_REPOSITORY_VARIABLE
3 #include "git-compat-util.h"
4 #include "gettext.h"
5 #include "object-store-ll.h"
6 #include "reflog.h"
7 #include "refs.h"
8 #include "revision.h"
9 #include "tree.h"
10 #include "tree-walk.h"
12 /* Remember to update object flag allocation in object.h */
13 #define INCOMPLETE (1u<<10)
14 #define STUDYING (1u<<11)
15 #define REACHABLE (1u<<12)
17 static int tree_is_complete(const struct object_id *oid)
19 struct tree_desc desc;
20 struct name_entry entry;
21 int complete;
22 struct tree *tree;
24 tree = lookup_tree(the_repository, oid);
25 if (!tree)
26 return 0;
27 if (tree->object.flags & SEEN)
28 return 1;
29 if (tree->object.flags & INCOMPLETE)
30 return 0;
32 if (!tree->buffer) {
33 enum object_type type;
34 unsigned long size;
35 void *data = repo_read_object_file(the_repository, oid, &type,
36 &size);
37 if (!data) {
38 tree->object.flags |= INCOMPLETE;
39 return 0;
41 tree->buffer = data;
42 tree->size = size;
44 init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
45 complete = 1;
46 while (tree_entry(&desc, &entry)) {
47 if (!repo_has_object_file(the_repository, &entry.oid) ||
48 (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
49 tree->object.flags |= INCOMPLETE;
50 complete = 0;
53 free_tree_buffer(tree);
55 if (complete)
56 tree->object.flags |= SEEN;
57 return complete;
60 static int commit_is_complete(struct commit *commit)
62 struct object_array study;
63 struct object_array found;
64 int is_incomplete = 0;
65 int i;
67 /* early return */
68 if (commit->object.flags & SEEN)
69 return 1;
70 if (commit->object.flags & INCOMPLETE)
71 return 0;
73 * Find all commits that are reachable and are not marked as
74 * SEEN. Then make sure the trees and blobs contained are
75 * complete. After that, mark these commits also as SEEN.
76 * If some of the objects that are needed to complete this
77 * commit are missing, mark this commit as INCOMPLETE.
79 memset(&study, 0, sizeof(study));
80 memset(&found, 0, sizeof(found));
81 add_object_array(&commit->object, NULL, &study);
82 add_object_array(&commit->object, NULL, &found);
83 commit->object.flags |= STUDYING;
84 while (study.nr) {
85 struct commit *c;
86 struct commit_list *parent;
88 c = (struct commit *)object_array_pop(&study);
89 if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
90 c->object.flags |= INCOMPLETE;
92 if (c->object.flags & INCOMPLETE) {
93 is_incomplete = 1;
94 break;
96 else if (c->object.flags & SEEN)
97 continue;
98 for (parent = c->parents; parent; parent = parent->next) {
99 struct commit *p = parent->item;
100 if (p->object.flags & STUDYING)
101 continue;
102 p->object.flags |= STUDYING;
103 add_object_array(&p->object, NULL, &study);
104 add_object_array(&p->object, NULL, &found);
107 if (!is_incomplete) {
109 * make sure all commits in "found" array have all the
110 * necessary objects.
112 for (i = 0; i < found.nr; i++) {
113 struct commit *c =
114 (struct commit *)found.objects[i].item;
115 if (!tree_is_complete(get_commit_tree_oid(c))) {
116 is_incomplete = 1;
117 c->object.flags |= INCOMPLETE;
120 if (!is_incomplete) {
121 /* mark all found commits as complete, iow SEEN */
122 for (i = 0; i < found.nr; i++)
123 found.objects[i].item->flags |= SEEN;
126 /* clear flags from the objects we traversed */
127 for (i = 0; i < found.nr; i++)
128 found.objects[i].item->flags &= ~STUDYING;
129 if (is_incomplete)
130 commit->object.flags |= INCOMPLETE;
131 else {
133 * If we come here, we have (1) traversed the ancestry chain
134 * from the "commit" until we reach SEEN commits (which are
135 * known to be complete), and (2) made sure that the commits
136 * encountered during the above traversal refer to trees that
137 * are complete. Which means that we know *all* the commits
138 * we have seen during this process are complete.
140 for (i = 0; i < found.nr; i++)
141 found.objects[i].item->flags |= SEEN;
143 /* free object arrays */
144 object_array_clear(&study);
145 object_array_clear(&found);
146 return !is_incomplete;
149 static int keep_entry(struct commit **it, struct object_id *oid)
151 struct commit *commit;
153 if (is_null_oid(oid))
154 return 1;
155 commit = lookup_commit_reference_gently(the_repository, oid, 1);
156 if (!commit)
157 return 0;
160 * Make sure everything in this commit exists.
162 * We have walked all the objects reachable from the refs
163 * and cache earlier. The commits reachable by this commit
164 * must meet SEEN commits -- and then we should mark them as
165 * SEEN as well.
167 if (!commit_is_complete(commit))
168 return 0;
169 *it = commit;
170 return 1;
174 * Starting from commits in the cb->mark_list, mark commits that are
175 * reachable from them. Stop the traversal at commits older than
176 * the expire_limit and queue them back, so that the caller can call
177 * us again to restart the traversal with longer expire_limit.
179 static void mark_reachable(struct expire_reflog_policy_cb *cb)
181 struct commit_list *pending;
182 timestamp_t expire_limit = cb->mark_limit;
183 struct commit_list *leftover = NULL;
185 for (pending = cb->mark_list; pending; pending = pending->next)
186 pending->item->object.flags &= ~REACHABLE;
188 pending = cb->mark_list;
189 while (pending) {
190 struct commit_list *parent;
191 struct commit *commit = pop_commit(&pending);
192 if (commit->object.flags & REACHABLE)
193 continue;
194 if (repo_parse_commit(the_repository, commit))
195 continue;
196 commit->object.flags |= REACHABLE;
197 if (commit->date < expire_limit) {
198 commit_list_insert(commit, &leftover);
199 continue;
201 parent = commit->parents;
202 while (parent) {
203 commit = parent->item;
204 parent = parent->next;
205 if (commit->object.flags & REACHABLE)
206 continue;
207 commit_list_insert(commit, &pending);
210 cb->mark_list = leftover;
213 static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
216 * We may or may not have the commit yet - if not, look it
217 * up using the supplied sha1.
219 if (!commit) {
220 if (is_null_oid(oid))
221 return 0;
223 commit = lookup_commit_reference_gently(the_repository, oid,
226 /* Not a commit -- keep it */
227 if (!commit)
228 return 0;
231 /* Reachable from the current ref? Don't prune. */
232 if (commit->object.flags & REACHABLE)
233 return 0;
235 if (cb->mark_list && cb->mark_limit) {
236 cb->mark_limit = 0; /* dig down to the root */
237 mark_reachable(cb);
240 return !(commit->object.flags & REACHABLE);
244 * Return true iff the specified reflog entry should be expired.
246 int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
247 const char *email UNUSED,
248 timestamp_t timestamp, int tz UNUSED,
249 const char *message UNUSED, void *cb_data)
251 struct expire_reflog_policy_cb *cb = cb_data;
252 struct commit *old_commit, *new_commit;
254 if (timestamp < cb->cmd.expire_total)
255 return 1;
257 old_commit = new_commit = NULL;
258 if (cb->cmd.stalefix &&
259 (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
260 return 1;
262 if (timestamp < cb->cmd.expire_unreachable) {
263 switch (cb->unreachable_expire_kind) {
264 case UE_ALWAYS:
265 return 1;
266 case UE_NORMAL:
267 case UE_HEAD:
268 if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
269 return 1;
270 break;
274 if (cb->cmd.recno && --(cb->cmd.recno) == 0)
275 return 1;
277 return 0;
280 int should_expire_reflog_ent_verbose(struct object_id *ooid,
281 struct object_id *noid,
282 const char *email,
283 timestamp_t timestamp, int tz,
284 const char *message, void *cb_data)
286 struct expire_reflog_policy_cb *cb = cb_data;
287 int expire;
289 expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
290 message, cb);
292 if (!expire)
293 printf("keep %s", message);
294 else if (cb->dry_run)
295 printf("would prune %s", message);
296 else
297 printf("prune %s", message);
299 return expire;
302 static int push_tip_to_list(const char *refname UNUSED,
303 const struct object_id *oid,
304 int flags, void *cb_data)
306 struct commit_list **list = cb_data;
307 struct commit *tip_commit;
308 if (flags & REF_ISSYMREF)
309 return 0;
310 tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
311 if (!tip_commit)
312 return 0;
313 commit_list_insert(tip_commit, list);
314 return 0;
317 static int is_head(const char *refname)
319 const char *stripped_refname;
320 parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
321 return !strcmp(stripped_refname, "HEAD");
324 void reflog_expiry_prepare(const char *refname,
325 const struct object_id *oid,
326 void *cb_data)
328 struct expire_reflog_policy_cb *cb = cb_data;
329 struct commit_list *elem;
330 struct commit *commit = NULL;
332 if (!cb->cmd.expire_unreachable || is_head(refname)) {
333 cb->unreachable_expire_kind = UE_HEAD;
334 } else {
335 commit = lookup_commit(the_repository, oid);
336 if (commit && is_null_oid(&commit->object.oid))
337 commit = NULL;
338 cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
341 if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
342 cb->unreachable_expire_kind = UE_ALWAYS;
344 switch (cb->unreachable_expire_kind) {
345 case UE_ALWAYS:
346 return;
347 case UE_HEAD:
348 refs_for_each_ref(get_main_ref_store(the_repository),
349 push_tip_to_list, &cb->tips);
350 for (elem = cb->tips; elem; elem = elem->next)
351 commit_list_insert(elem->item, &cb->mark_list);
352 break;
353 case UE_NORMAL:
354 commit_list_insert(commit, &cb->mark_list);
355 /* For reflog_expiry_cleanup() below */
356 cb->tip_commit = commit;
358 cb->mark_limit = cb->cmd.expire_total;
359 mark_reachable(cb);
362 void reflog_expiry_cleanup(void *cb_data)
364 struct expire_reflog_policy_cb *cb = cb_data;
365 struct commit_list *elem;
367 switch (cb->unreachable_expire_kind) {
368 case UE_ALWAYS:
369 return;
370 case UE_HEAD:
371 for (elem = cb->tips; elem; elem = elem->next)
372 clear_commit_marks(elem->item, REACHABLE);
373 free_commit_list(cb->tips);
374 break;
375 case UE_NORMAL:
376 clear_commit_marks(cb->tip_commit, REACHABLE);
377 break;
379 for (elem = cb->mark_list; elem; elem = elem->next)
380 clear_commit_marks(elem->item, REACHABLE);
381 free_commit_list(cb->mark_list);
384 int count_reflog_ent(struct object_id *ooid UNUSED,
385 struct object_id *noid UNUSED,
386 const char *email UNUSED,
387 timestamp_t timestamp, int tz UNUSED,
388 const char *message UNUSED, void *cb_data)
390 struct cmd_reflog_expire_cb *cb = cb_data;
391 if (!cb->expire_total || timestamp < cb->expire_total)
392 cb->recno++;
393 return 0;
396 int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
398 struct cmd_reflog_expire_cb cmd = { 0 };
399 int status = 0;
400 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
401 const char *spec = strstr(rev, "@{");
402 char *ep, *ref;
403 int recno;
404 struct expire_reflog_policy_cb cb = {
405 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
408 if (verbose)
409 should_prune_fn = should_expire_reflog_ent_verbose;
411 if (!spec)
412 return error(_("not a reflog: %s"), rev);
414 if (!repo_dwim_log(the_repository, rev, spec - rev, NULL, &ref)) {
415 status |= error(_("no reflog for '%s'"), rev);
416 goto cleanup;
419 recno = strtoul(spec + 2, &ep, 10);
420 if (*ep == '}') {
421 cmd.recno = -recno;
422 refs_for_each_reflog_ent(get_main_ref_store(the_repository),
423 ref, count_reflog_ent, &cmd);
424 } else {
425 cmd.expire_total = approxidate(spec + 2);
426 refs_for_each_reflog_ent(get_main_ref_store(the_repository),
427 ref, count_reflog_ent, &cmd);
428 cmd.expire_total = 0;
431 cb.cmd = cmd;
432 status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
433 flags,
434 reflog_expiry_prepare,
435 should_prune_fn,
436 reflog_expiry_cleanup,
437 &cb);
439 cleanup:
440 free(ref);
441 return status;