git
/
reachable.c
406 строк · 9.5 Кб
1#define USE_THE_REPOSITORY_VARIABLE
2
3#include "git-compat-util.h"
4#include "gettext.h"
5#include "hex.h"
6#include "refs.h"
7#include "commit.h"
8#include "blob.h"
9#include "diff.h"
10#include "revision.h"
11#include "reachable.h"
12#include "cache-tree.h"
13#include "progress.h"
14#include "list-objects.h"
15#include "packfile.h"
16#include "worktree.h"
17#include "object-store-ll.h"
18#include "pack-bitmap.h"
19#include "pack-mtimes.h"
20#include "config.h"
21#include "run-command.h"
22#include "sequencer.h"
23
24struct connectivity_progress {
25struct progress *progress;
26unsigned long count;
27};
28
29static void update_progress(struct connectivity_progress *cp)
30{
31cp->count++;
32if ((cp->count & 1023) == 0)
33display_progress(cp->progress, cp->count);
34}
35
36static void add_one_file(const char *path, struct rev_info *revs)
37{
38struct strbuf buf = STRBUF_INIT;
39struct object_id oid;
40struct object *object;
41
42if (!read_oneliner(&buf, path, READ_ONELINER_SKIP_IF_EMPTY)) {
43strbuf_release(&buf);
44return;
45}
46strbuf_trim(&buf);
47if (!get_oid_hex(buf.buf, &oid)) {
48object = parse_object_or_die(&oid, buf.buf);
49add_pending_object(revs, object, "");
50}
51strbuf_release(&buf);
52}
53
54/* Mark objects recorded in rebase state files as reachable. */
55static void add_rebase_files(struct rev_info *revs)
56{
57struct strbuf buf = STRBUF_INIT;
58size_t len;
59const char *path[] = {
60"rebase-apply/autostash",
61"rebase-apply/orig-head",
62"rebase-merge/autostash",
63"rebase-merge/orig-head",
64};
65struct worktree **worktrees = get_worktrees();
66
67for (struct worktree **wt = worktrees; *wt; wt++) {
68strbuf_reset(&buf);
69strbuf_addstr(&buf, get_worktree_git_dir(*wt));
70strbuf_complete(&buf, '/');
71len = buf.len;
72for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
73strbuf_setlen(&buf, len);
74strbuf_addstr(&buf, path[i]);
75add_one_file(buf.buf, revs);
76}
77}
78strbuf_release(&buf);
79free_worktrees(worktrees);
80}
81
82static int add_one_ref(const char *path, const char *referent UNUSED, const struct object_id *oid,
83int flag, void *cb_data)
84{
85struct rev_info *revs = (struct rev_info *)cb_data;
86struct object *object;
87
88if ((flag & REF_ISSYMREF) && (flag & REF_ISBROKEN)) {
89warning("symbolic ref is dangling: %s", path);
90return 0;
91}
92
93object = parse_object_or_die(oid, path);
94add_pending_object(revs, object, "");
95
96return 0;
97}
98
99/*
100* The traversal will have already marked us as SEEN, so we
101* only need to handle any progress reporting here.
102*/
103static void mark_object(struct object *obj UNUSED,
104const char *name UNUSED,
105void *data)
106{
107update_progress(data);
108}
109
110static void mark_commit(struct commit *c, void *data)
111{
112mark_object(&c->object, NULL, data);
113}
114
115struct recent_data {
116struct rev_info *revs;
117timestamp_t timestamp;
118report_recent_object_fn *cb;
119int ignore_in_core_kept_packs;
120
121struct oidset extra_recent_oids;
122int extra_recent_oids_loaded;
123};
124
125static int run_one_gc_recent_objects_hook(struct oidset *set,
126const char *args)
127{
128struct child_process cmd = CHILD_PROCESS_INIT;
129struct strbuf buf = STRBUF_INIT;
130FILE *out;
131int ret = 0;
132
133cmd.use_shell = 1;
134cmd.out = -1;
135
136strvec_push(&cmd.args, args);
137
138if (start_command(&cmd))
139return -1;
140
141out = xfdopen(cmd.out, "r");
142while (strbuf_getline(&buf, out) != EOF) {
143struct object_id oid;
144const char *rest;
145
146if (parse_oid_hex(buf.buf, &oid, &rest) || *rest) {
147ret = error(_("invalid extra cruft tip: '%s'"), buf.buf);
148break;
149}
150
151oidset_insert(set, &oid);
152}
153
154fclose(out);
155ret |= finish_command(&cmd);
156
157strbuf_release(&buf);
158return ret;
159}
160
161static void load_gc_recent_objects(struct recent_data *data)
162{
163const struct string_list *programs;
164int ret = 0;
165size_t i;
166
167data->extra_recent_oids_loaded = 1;
168
169if (git_config_get_string_multi("gc.recentobjectshook", &programs))
170return;
171
172for (i = 0; i < programs->nr; i++) {
173ret = run_one_gc_recent_objects_hook(&data->extra_recent_oids,
174programs->items[i].string);
175if (ret)
176die(_("unable to enumerate additional recent objects"));
177}
178}
179
180static int obj_is_recent(const struct object_id *oid, timestamp_t mtime,
181struct recent_data *data)
182{
183if (mtime > data->timestamp)
184return 1;
185
186if (!data->extra_recent_oids_loaded)
187load_gc_recent_objects(data);
188return oidset_contains(&data->extra_recent_oids, oid);
189}
190
191static void add_recent_object(const struct object_id *oid,
192struct packed_git *pack,
193off_t offset,
194timestamp_t mtime,
195struct recent_data *data)
196{
197struct object *obj;
198enum object_type type;
199
200if (!obj_is_recent(oid, mtime, data))
201return;
202
203/*
204* We do not want to call parse_object here, because
205* inflating blobs and trees could be very expensive.
206* However, we do need to know the correct type for
207* later processing, and the revision machinery expects
208* commits and tags to have been parsed.
209*/
210type = oid_object_info(the_repository, oid, NULL);
211if (type < 0)
212die("unable to get object info for %s", oid_to_hex(oid));
213
214switch (type) {
215case OBJ_TAG:
216case OBJ_COMMIT:
217obj = parse_object_or_die(oid, NULL);
218break;
219case OBJ_TREE:
220obj = (struct object *)lookup_tree(the_repository, oid);
221break;
222case OBJ_BLOB:
223obj = (struct object *)lookup_blob(the_repository, oid);
224break;
225default:
226die("unknown object type for %s: %s",
227oid_to_hex(oid), type_name(type));
228}
229
230if (!obj)
231die("unable to lookup %s", oid_to_hex(oid));
232
233add_pending_object(data->revs, obj, "");
234if (data->cb)
235data->cb(obj, pack, offset, mtime);
236}
237
238static int want_recent_object(struct recent_data *data,
239const struct object_id *oid)
240{
241if (data->ignore_in_core_kept_packs &&
242has_object_kept_pack(oid, IN_CORE_KEEP_PACKS))
243return 0;
244return 1;
245}
246
247static int add_recent_loose(const struct object_id *oid,
248const char *path, void *data)
249{
250struct stat st;
251struct object *obj;
252
253if (!want_recent_object(data, oid))
254return 0;
255
256obj = lookup_object(the_repository, oid);
257
258if (obj && obj->flags & SEEN)
259return 0;
260
261if (stat(path, &st) < 0) {
262/*
263* It's OK if an object went away during our iteration; this
264* could be due to a simultaneous repack. But anything else
265* we should abort, since we might then fail to mark objects
266* which should not be pruned.
267*/
268if (errno == ENOENT)
269return 0;
270return error_errno("unable to stat %s", oid_to_hex(oid));
271}
272
273add_recent_object(oid, NULL, 0, st.st_mtime, data);
274return 0;
275}
276
277static int add_recent_packed(const struct object_id *oid,
278struct packed_git *p,
279uint32_t pos,
280void *data)
281{
282struct object *obj;
283timestamp_t mtime = p->mtime;
284
285if (!want_recent_object(data, oid))
286return 0;
287
288obj = lookup_object(the_repository, oid);
289
290if (obj && obj->flags & SEEN)
291return 0;
292if (p->is_cruft) {
293if (load_pack_mtimes(p) < 0)
294die(_("could not load cruft pack .mtimes"));
295mtime = nth_packed_mtime(p, pos);
296}
297add_recent_object(oid, p, nth_packed_object_offset(p, pos), mtime, data);
298return 0;
299}
300
301int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
302timestamp_t timestamp,
303report_recent_object_fn *cb,
304int ignore_in_core_kept_packs)
305{
306struct recent_data data;
307enum for_each_object_flags flags;
308int r;
309
310data.revs = revs;
311data.timestamp = timestamp;
312data.cb = cb;
313data.ignore_in_core_kept_packs = ignore_in_core_kept_packs;
314
315oidset_init(&data.extra_recent_oids, 0);
316data.extra_recent_oids_loaded = 0;
317
318r = for_each_loose_object(add_recent_loose, &data,
319FOR_EACH_OBJECT_LOCAL_ONLY);
320if (r)
321goto done;
322
323flags = FOR_EACH_OBJECT_LOCAL_ONLY | FOR_EACH_OBJECT_PACK_ORDER;
324if (ignore_in_core_kept_packs)
325flags |= FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS;
326
327r = for_each_packed_object(add_recent_packed, &data, flags);
328
329done:
330oidset_clear(&data.extra_recent_oids);
331
332return r;
333}
334
335static int mark_object_seen(const struct object_id *oid,
336enum object_type type,
337int exclude UNUSED,
338uint32_t name_hash UNUSED,
339struct packed_git *found_pack UNUSED,
340off_t found_offset UNUSED)
341{
342struct object *obj = lookup_object_by_type(the_repository, oid, type);
343if (!obj)
344die("unable to create object '%s'", oid_to_hex(oid));
345
346obj->flags |= SEEN;
347return 0;
348}
349
350void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
351timestamp_t mark_recent, struct progress *progress)
352{
353struct connectivity_progress cp;
354struct bitmap_index *bitmap_git;
355
356/*
357* Set up revision parsing, and mark us as being interested
358* in all object types, not just commits.
359*/
360revs->tag_objects = 1;
361revs->blob_objects = 1;
362revs->tree_objects = 1;
363
364/* Add all refs from the index file */
365add_index_objects_to_pending(revs, 0);
366
367/* Add all external refs */
368refs_for_each_ref(get_main_ref_store(the_repository), add_one_ref,
369revs);
370
371/* detached HEAD is not included in the list above */
372refs_head_ref(get_main_ref_store(the_repository), add_one_ref, revs);
373other_head_refs(add_one_ref, revs);
374
375/* rebase autostash and orig-head */
376add_rebase_files(revs);
377
378/* Add all reflog info */
379if (mark_reflog)
380add_reflogs_to_pending(revs, 0);
381
382cp.progress = progress;
383cp.count = 0;
384
385bitmap_git = prepare_bitmap_walk(revs, 0);
386if (bitmap_git) {
387traverse_bitmap_commit_list(bitmap_git, revs, mark_object_seen);
388free_bitmap_index(bitmap_git);
389} else {
390if (prepare_revision_walk(revs))
391die("revision walk setup failed");
392traverse_commit_list(revs, mark_commit, mark_object, &cp);
393}
394
395if (mark_recent) {
396revs->ignore_missing_links = 1;
397if (add_unseen_recent_objects_to_traversal(revs, mark_recent,
398NULL, 0))
399die("unable to mark recent objects");
400if (prepare_revision_walk(revs))
401die("revision walk setup failed");
402traverse_commit_list(revs, mark_commit, mark_object, &cp);
403}
404
405display_progress(cp.progress, cp.count);
406}
407