Coverage Report

Created: 2024-09-16 06:10

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