1 #define USE_THE_REPOSITORY_VARIABLE
3 #include "git-compat-util.h"
5 #include "object-store-ll.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
;
24 tree
= lookup_tree(the_repository
, oid
);
27 if (tree
->object
.flags
& SEEN
)
29 if (tree
->object
.flags
& INCOMPLETE
)
33 enum object_type type
;
35 void *data
= repo_read_object_file(the_repository
, oid
, &type
,
38 tree
->object
.flags
|= INCOMPLETE
;
44 init_tree_desc(&desc
, &tree
->object
.oid
, tree
->buffer
, tree
->size
);
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
;
53 free_tree_buffer(tree
);
56 tree
->object
.flags
|= SEEN
;
60 static int commit_is_complete(struct commit
*commit
)
62 struct object_array study
;
63 struct object_array found
;
64 int is_incomplete
= 0;
68 if (commit
->object
.flags
& SEEN
)
70 if (commit
->object
.flags
& INCOMPLETE
)
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
;
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
) {
96 else if (c
->object
.flags
& SEEN
)
98 for (parent
= c
->parents
; parent
; parent
= parent
->next
) {
99 struct commit
*p
= parent
->item
;
100 if (p
->object
.flags
& STUDYING
)
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
112 for (i
= 0; i
< found
.nr
; i
++) {
114 (struct commit
*)found
.objects
[i
].item
;
115 if (!tree_is_complete(get_commit_tree_oid(c
))) {
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
;
130 commit
->object
.flags
|= INCOMPLETE
;
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
))
155 commit
= lookup_commit_reference_gently(the_repository
, oid
, 1);
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
167 if (!commit_is_complete(commit
))
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
;
190 struct commit_list
*parent
;
191 struct commit
*commit
= pop_commit(&pending
);
192 if (commit
->object
.flags
& REACHABLE
)
194 if (repo_parse_commit(the_repository
, commit
))
196 commit
->object
.flags
|= REACHABLE
;
197 if (commit
->date
< expire_limit
) {
198 commit_list_insert(commit
, &leftover
);
201 parent
= commit
->parents
;
203 commit
= parent
->item
;
204 parent
= parent
->next
;
205 if (commit
->object
.flags
& REACHABLE
)
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.
220 if (is_null_oid(oid
))
223 commit
= lookup_commit_reference_gently(the_repository
, oid
,
226 /* Not a commit -- keep it */
231 /* Reachable from the current ref? Don't prune. */
232 if (commit
->object
.flags
& REACHABLE
)
235 if (cb
->mark_list
&& cb
->mark_limit
) {
236 cb
->mark_limit
= 0; /* dig down to the root */
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
)
257 old_commit
= new_commit
= NULL
;
258 if (cb
->cmd
.stalefix
&&
259 (!keep_entry(&old_commit
, ooid
) || !keep_entry(&new_commit
, noid
)))
262 if (timestamp
< cb
->cmd
.expire_unreachable
) {
263 switch (cb
->unreachable_expire_kind
) {
268 if (unreachable(cb
, old_commit
, ooid
) || unreachable(cb
, new_commit
, noid
))
274 if (cb
->cmd
.recno
&& --(cb
->cmd
.recno
) == 0)
280 int should_expire_reflog_ent_verbose(struct object_id
*ooid
,
281 struct object_id
*noid
,
283 timestamp_t timestamp
, int tz
,
284 const char *message
, void *cb_data
)
286 struct expire_reflog_policy_cb
*cb
= cb_data
;
289 expire
= should_expire_reflog_ent(ooid
, noid
, email
, timestamp
, tz
,
293 printf("keep %s", message
);
294 else if (cb
->dry_run
)
295 printf("would prune %s", message
);
297 printf("prune %s", message
);
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
)
310 tip_commit
= lookup_commit_reference_gently(the_repository
, oid
, 1);
313 commit_list_insert(tip_commit
, list
);
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
,
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
;
335 commit
= lookup_commit(the_repository
, oid
);
336 if (commit
&& is_null_oid(&commit
->object
.oid
))
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
) {
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
);
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
;
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
) {
371 for (elem
= cb
->tips
; elem
; elem
= elem
->next
)
372 clear_commit_marks(elem
->item
, REACHABLE
);
373 free_commit_list(cb
->tips
);
376 clear_commit_marks(cb
->tip_commit
, REACHABLE
);
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
)
396 int reflog_delete(const char *rev
, enum expire_reflog_flags flags
, int verbose
)
398 struct cmd_reflog_expire_cb cmd
= { 0 };
400 reflog_expiry_should_prune_fn
*should_prune_fn
= should_expire_reflog_ent
;
401 const char *spec
= strstr(rev
, "@{");
404 struct expire_reflog_policy_cb cb
= {
405 .dry_run
= !!(flags
& EXPIRE_REFLOGS_DRY_RUN
),
409 should_prune_fn
= should_expire_reflog_ent_verbose
;
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
);
419 recno
= strtoul(spec
+ 2, &ep
, 10);
422 refs_for_each_reflog_ent(get_main_ref_store(the_repository
),
423 ref
, count_reflog_ent
, &cmd
);
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;
432 status
|= refs_reflog_expire(get_main_ref_store(the_repository
), ref
,
434 reflog_expiry_prepare
,
436 reflog_expiry_cleanup
,