treewide: remove cache.h inclusion due to object-file.h changes
[alt-git.git] / reflog.c
blob9c0944308849088e8b69596986fdefd13ee147db
1 #include "git-compat-util.h"
2 #include "gettext.h"
3 #include "object-store.h"
4 #include "reflog.h"
5 #include "refs.h"
6 #include "revision.h"
7 #include "worktree.h"
9 /* Remember to update object flag allocation in object.h */
10 #define INCOMPLETE (1u<<10)
11 #define STUDYING (1u<<11)
12 #define REACHABLE (1u<<12)
14 static int tree_is_complete(const struct object_id *oid)
16 struct tree_desc desc;
17 struct name_entry entry;
18 int complete;
19 struct tree *tree;
21 tree = lookup_tree(the_repository, oid);
22 if (!tree)
23 return 0;
24 if (tree->object.flags & SEEN)
25 return 1;
26 if (tree->object.flags & INCOMPLETE)
27 return 0;
29 if (!tree->buffer) {
30 enum object_type type;
31 unsigned long size;
32 void *data = repo_read_object_file(the_repository, oid, &type,
33 &size);
34 if (!data) {
35 tree->object.flags |= INCOMPLETE;
36 return 0;
38 tree->buffer = data;
39 tree->size = size;
41 init_tree_desc(&desc, tree->buffer, tree->size);
42 complete = 1;
43 while (tree_entry(&desc, &entry)) {
44 if (!repo_has_object_file(the_repository, &entry.oid) ||
45 (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
46 tree->object.flags |= INCOMPLETE;
47 complete = 0;
50 free_tree_buffer(tree);
52 if (complete)
53 tree->object.flags |= SEEN;
54 return complete;
57 static int commit_is_complete(struct commit *commit)
59 struct object_array study;
60 struct object_array found;
61 int is_incomplete = 0;
62 int i;
64 /* early return */
65 if (commit->object.flags & SEEN)
66 return 1;
67 if (commit->object.flags & INCOMPLETE)
68 return 0;
70 * Find all commits that are reachable and are not marked as
71 * SEEN. Then make sure the trees and blobs contained are
72 * complete. After that, mark these commits also as SEEN.
73 * If some of the objects that are needed to complete this
74 * commit are missing, mark this commit as INCOMPLETE.
76 memset(&study, 0, sizeof(study));
77 memset(&found, 0, sizeof(found));
78 add_object_array(&commit->object, NULL, &study);
79 add_object_array(&commit->object, NULL, &found);
80 commit->object.flags |= STUDYING;
81 while (study.nr) {
82 struct commit *c;
83 struct commit_list *parent;
85 c = (struct commit *)object_array_pop(&study);
86 if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
87 c->object.flags |= INCOMPLETE;
89 if (c->object.flags & INCOMPLETE) {
90 is_incomplete = 1;
91 break;
93 else if (c->object.flags & SEEN)
94 continue;
95 for (parent = c->parents; parent; parent = parent->next) {
96 struct commit *p = parent->item;
97 if (p->object.flags & STUDYING)
98 continue;
99 p->object.flags |= STUDYING;
100 add_object_array(&p->object, NULL, &study);
101 add_object_array(&p->object, NULL, &found);
104 if (!is_incomplete) {
106 * make sure all commits in "found" array have all the
107 * necessary objects.
109 for (i = 0; i < found.nr; i++) {
110 struct commit *c =
111 (struct commit *)found.objects[i].item;
112 if (!tree_is_complete(get_commit_tree_oid(c))) {
113 is_incomplete = 1;
114 c->object.flags |= INCOMPLETE;
117 if (!is_incomplete) {
118 /* mark all found commits as complete, iow SEEN */
119 for (i = 0; i < found.nr; i++)
120 found.objects[i].item->flags |= SEEN;
123 /* clear flags from the objects we traversed */
124 for (i = 0; i < found.nr; i++)
125 found.objects[i].item->flags &= ~STUDYING;
126 if (is_incomplete)
127 commit->object.flags |= INCOMPLETE;
128 else {
130 * If we come here, we have (1) traversed the ancestry chain
131 * from the "commit" until we reach SEEN commits (which are
132 * known to be complete), and (2) made sure that the commits
133 * encountered during the above traversal refer to trees that
134 * are complete. Which means that we know *all* the commits
135 * we have seen during this process are complete.
137 for (i = 0; i < found.nr; i++)
138 found.objects[i].item->flags |= SEEN;
140 /* free object arrays */
141 object_array_clear(&study);
142 object_array_clear(&found);
143 return !is_incomplete;
146 static int keep_entry(struct commit **it, struct object_id *oid)
148 struct commit *commit;
150 if (is_null_oid(oid))
151 return 1;
152 commit = lookup_commit_reference_gently(the_repository, oid, 1);
153 if (!commit)
154 return 0;
157 * Make sure everything in this commit exists.
159 * We have walked all the objects reachable from the refs
160 * and cache earlier. The commits reachable by this commit
161 * must meet SEEN commits -- and then we should mark them as
162 * SEEN as well.
164 if (!commit_is_complete(commit))
165 return 0;
166 *it = commit;
167 return 1;
171 * Starting from commits in the cb->mark_list, mark commits that are
172 * reachable from them. Stop the traversal at commits older than
173 * the expire_limit and queue them back, so that the caller can call
174 * us again to restart the traversal with longer expire_limit.
176 static void mark_reachable(struct expire_reflog_policy_cb *cb)
178 struct commit_list *pending;
179 timestamp_t expire_limit = cb->mark_limit;
180 struct commit_list *leftover = NULL;
182 for (pending = cb->mark_list; pending; pending = pending->next)
183 pending->item->object.flags &= ~REACHABLE;
185 pending = cb->mark_list;
186 while (pending) {
187 struct commit_list *parent;
188 struct commit *commit = pop_commit(&pending);
189 if (commit->object.flags & REACHABLE)
190 continue;
191 if (repo_parse_commit(the_repository, commit))
192 continue;
193 commit->object.flags |= REACHABLE;
194 if (commit->date < expire_limit) {
195 commit_list_insert(commit, &leftover);
196 continue;
198 parent = commit->parents;
199 while (parent) {
200 commit = parent->item;
201 parent = parent->next;
202 if (commit->object.flags & REACHABLE)
203 continue;
204 commit_list_insert(commit, &pending);
207 cb->mark_list = leftover;
210 static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
213 * We may or may not have the commit yet - if not, look it
214 * up using the supplied sha1.
216 if (!commit) {
217 if (is_null_oid(oid))
218 return 0;
220 commit = lookup_commit_reference_gently(the_repository, oid,
223 /* Not a commit -- keep it */
224 if (!commit)
225 return 0;
228 /* Reachable from the current ref? Don't prune. */
229 if (commit->object.flags & REACHABLE)
230 return 0;
232 if (cb->mark_list && cb->mark_limit) {
233 cb->mark_limit = 0; /* dig down to the root */
234 mark_reachable(cb);
237 return !(commit->object.flags & REACHABLE);
241 * Return true iff the specified reflog entry should be expired.
243 int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
244 const char *email UNUSED,
245 timestamp_t timestamp, int tz UNUSED,
246 const char *message UNUSED, void *cb_data)
248 struct expire_reflog_policy_cb *cb = cb_data;
249 struct commit *old_commit, *new_commit;
251 if (timestamp < cb->cmd.expire_total)
252 return 1;
254 old_commit = new_commit = NULL;
255 if (cb->cmd.stalefix &&
256 (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
257 return 1;
259 if (timestamp < cb->cmd.expire_unreachable) {
260 switch (cb->unreachable_expire_kind) {
261 case UE_ALWAYS:
262 return 1;
263 case UE_NORMAL:
264 case UE_HEAD:
265 if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
266 return 1;
267 break;
271 if (cb->cmd.recno && --(cb->cmd.recno) == 0)
272 return 1;
274 return 0;
277 int should_expire_reflog_ent_verbose(struct object_id *ooid,
278 struct object_id *noid,
279 const char *email,
280 timestamp_t timestamp, int tz,
281 const char *message, void *cb_data)
283 struct expire_reflog_policy_cb *cb = cb_data;
284 int expire;
286 expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
287 message, cb);
289 if (!expire)
290 printf("keep %s", message);
291 else if (cb->dry_run)
292 printf("would prune %s", message);
293 else
294 printf("prune %s", message);
296 return expire;
299 static int push_tip_to_list(const char *refname UNUSED,
300 const struct object_id *oid,
301 int flags, void *cb_data)
303 struct commit_list **list = cb_data;
304 struct commit *tip_commit;
305 if (flags & REF_ISSYMREF)
306 return 0;
307 tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
308 if (!tip_commit)
309 return 0;
310 commit_list_insert(tip_commit, list);
311 return 0;
314 static int is_head(const char *refname)
316 const char *stripped_refname;
317 parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
318 return !strcmp(stripped_refname, "HEAD");
321 void reflog_expiry_prepare(const char *refname,
322 const struct object_id *oid,
323 void *cb_data)
325 struct expire_reflog_policy_cb *cb = cb_data;
326 struct commit_list *elem;
327 struct commit *commit = NULL;
329 if (!cb->cmd.expire_unreachable || is_head(refname)) {
330 cb->unreachable_expire_kind = UE_HEAD;
331 } else {
332 commit = lookup_commit(the_repository, oid);
333 if (commit && is_null_oid(&commit->object.oid))
334 commit = NULL;
335 cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
338 if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
339 cb->unreachable_expire_kind = UE_ALWAYS;
341 switch (cb->unreachable_expire_kind) {
342 case UE_ALWAYS:
343 return;
344 case UE_HEAD:
345 for_each_ref(push_tip_to_list, &cb->tips);
346 for (elem = cb->tips; elem; elem = elem->next)
347 commit_list_insert(elem->item, &cb->mark_list);
348 break;
349 case UE_NORMAL:
350 commit_list_insert(commit, &cb->mark_list);
351 /* For reflog_expiry_cleanup() below */
352 cb->tip_commit = commit;
354 cb->mark_limit = cb->cmd.expire_total;
355 mark_reachable(cb);
358 void reflog_expiry_cleanup(void *cb_data)
360 struct expire_reflog_policy_cb *cb = cb_data;
361 struct commit_list *elem;
363 switch (cb->unreachable_expire_kind) {
364 case UE_ALWAYS:
365 return;
366 case UE_HEAD:
367 for (elem = cb->tips; elem; elem = elem->next)
368 clear_commit_marks(elem->item, REACHABLE);
369 free_commit_list(cb->tips);
370 break;
371 case UE_NORMAL:
372 clear_commit_marks(cb->tip_commit, REACHABLE);
373 break;
375 for (elem = cb->mark_list; elem; elem = elem->next)
376 clear_commit_marks(elem->item, REACHABLE);
377 free_commit_list(cb->mark_list);
380 int count_reflog_ent(struct object_id *ooid UNUSED,
381 struct object_id *noid UNUSED,
382 const char *email UNUSED,
383 timestamp_t timestamp, int tz UNUSED,
384 const char *message UNUSED, void *cb_data)
386 struct cmd_reflog_expire_cb *cb = cb_data;
387 if (!cb->expire_total || timestamp < cb->expire_total)
388 cb->recno++;
389 return 0;
392 int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
394 struct cmd_reflog_expire_cb cmd = { 0 };
395 int status = 0;
396 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
397 const char *spec = strstr(rev, "@{");
398 char *ep, *ref;
399 int recno;
400 struct expire_reflog_policy_cb cb = {
401 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
404 if (verbose)
405 should_prune_fn = should_expire_reflog_ent_verbose;
407 if (!spec)
408 return error(_("not a reflog: %s"), rev);
410 if (!dwim_log(rev, spec - rev, NULL, &ref)) {
411 status |= error(_("no reflog for '%s'"), rev);
412 goto cleanup;
415 recno = strtoul(spec + 2, &ep, 10);
416 if (*ep == '}') {
417 cmd.recno = -recno;
418 for_each_reflog_ent(ref, count_reflog_ent, &cmd);
419 } else {
420 cmd.expire_total = approxidate(spec + 2);
421 for_each_reflog_ent(ref, count_reflog_ent, &cmd);
422 cmd.expire_total = 0;
425 cb.cmd = cmd;
426 status |= reflog_expire(ref, flags,
427 reflog_expiry_prepare,
428 should_prune_fn,
429 reflog_expiry_cleanup,
430 &cb);
432 cleanup:
433 free(ref);
434 return status;