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