Coverage Report

Created: 2026-03-31 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/loose.c
Line
Count
Source
1
#include "git-compat-util.h"
2
#include "hash.h"
3
#include "path.h"
4
#include "object-file.h"
5
#include "odb.h"
6
#include "odb/source-files.h"
7
#include "hex.h"
8
#include "repository.h"
9
#include "wrapper.h"
10
#include "gettext.h"
11
#include "loose.h"
12
#include "lockfile.h"
13
#include "oidtree.h"
14
15
static const char *loose_object_header = "# loose-object-idx\n";
16
17
static inline int should_use_loose_object_map(struct repository *repo)
18
0
{
19
0
  return repo->compat_hash_algo && repo->gitdir;
20
0
}
21
22
void loose_object_map_init(struct loose_object_map **map)
23
0
{
24
0
  struct loose_object_map *m;
25
0
  m = xmalloc(sizeof(**map));
26
0
  m->to_compat = kh_init_oid_map();
27
0
  m->to_storage = kh_init_oid_map();
28
0
  *map = m;
29
0
}
30
31
static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
32
0
{
33
0
  khiter_t pos;
34
0
  int ret;
35
0
  struct object_id *stored;
36
37
0
  pos = kh_put_oid_map(map, *key, &ret);
38
39
  /* This item already exists in the map. */
40
0
  if (ret == 0)
41
0
    return 0;
42
43
0
  stored = xmalloc(sizeof(*stored));
44
0
  oidcpy(stored, value);
45
0
  kh_value(map, pos) = stored;
46
0
  return 1;
47
0
}
48
49
static int insert_loose_map(struct odb_source *source,
50
          const struct object_id *oid,
51
          const struct object_id *compat_oid)
52
0
{
53
0
  struct odb_source_files *files = odb_source_files_downcast(source);
54
0
  struct loose_object_map *map = files->loose->map;
55
0
  int inserted = 0;
56
57
0
  inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
58
0
  inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
59
0
  if (inserted)
60
0
    oidtree_insert(files->loose->cache, compat_oid);
61
62
0
  return inserted;
63
0
}
64
65
static int load_one_loose_object_map(struct repository *repo, struct odb_source *source)
66
0
{
67
0
  struct odb_source_files *files = odb_source_files_downcast(source);
68
0
  struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
69
0
  FILE *fp;
70
71
0
  if (!files->loose->map)
72
0
    loose_object_map_init(&files->loose->map);
73
0
  if (!files->loose->cache) {
74
0
    ALLOC_ARRAY(files->loose->cache, 1);
75
0
    oidtree_init(files->loose->cache);
76
0
  }
77
78
0
  insert_loose_map(source, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
79
0
  insert_loose_map(source, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
80
0
  insert_loose_map(source, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
81
82
0
  repo_common_path_replace(repo, &path, "objects/loose-object-idx");
83
0
  fp = fopen(path.buf, "rb");
84
0
  if (!fp) {
85
0
    strbuf_release(&path);
86
0
    return 0;
87
0
  }
88
89
0
  errno = 0;
90
0
  if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
91
0
    goto err;
92
0
  while (!strbuf_getline_lf(&buf, fp)) {
93
0
    const char *p;
94
0
    struct object_id oid, compat_oid;
95
0
    if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
96
0
        *p++ != ' ' ||
97
0
        parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
98
0
        p != buf.buf + buf.len)
99
0
      goto err;
100
0
    insert_loose_map(source, &oid, &compat_oid);
101
0
  }
102
103
0
  strbuf_release(&buf);
104
0
  strbuf_release(&path);
105
0
  return errno ? -1 : 0;
106
0
err:
107
0
  strbuf_release(&buf);
108
0
  strbuf_release(&path);
109
0
  return -1;
110
0
}
111
112
int repo_read_loose_object_map(struct repository *repo)
113
0
{
114
0
  struct odb_source *source;
115
116
0
  if (!should_use_loose_object_map(repo))
117
0
    return 0;
118
119
0
  odb_prepare_alternates(repo->objects);
120
121
0
  for (source = repo->objects->sources; source; source = source->next) {
122
0
    if (load_one_loose_object_map(repo, source) < 0) {
123
0
      return -1;
124
0
    }
125
0
  }
126
0
  return 0;
127
0
}
128
129
int repo_write_loose_object_map(struct repository *repo)
130
0
{
131
0
  struct odb_source_files *files = odb_source_files_downcast(repo->objects->sources);
132
0
  kh_oid_map_t *map = files->loose->map->to_compat;
133
0
  struct lock_file lock;
134
0
  int fd;
135
0
  khiter_t iter;
136
0
  struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
137
138
0
  if (!should_use_loose_object_map(repo))
139
0
    return 0;
140
141
0
  repo_common_path_replace(repo, &path, "objects/loose-object-idx");
142
0
  fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
143
0
  iter = kh_begin(map);
144
0
  if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
145
0
    goto errout;
146
147
0
  for (; iter != kh_end(map); iter++) {
148
0
    if (kh_exist(map, iter)) {
149
0
      if (oideq(&kh_key(map, iter), repo->hash_algo->empty_tree) ||
150
0
          oideq(&kh_key(map, iter), repo->hash_algo->empty_blob))
151
0
        continue;
152
0
      strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
153
0
      if (write_in_full(fd, buf.buf, buf.len) < 0)
154
0
        goto errout;
155
0
      strbuf_reset(&buf);
156
0
    }
157
0
  }
158
0
  strbuf_release(&buf);
159
0
  if (commit_lock_file(&lock) < 0) {
160
0
    error_errno(_("could not write loose object index %s"), path.buf);
161
0
    strbuf_release(&path);
162
0
    return -1;
163
0
  }
164
0
  strbuf_release(&path);
165
0
  return 0;
166
0
errout:
167
0
  rollback_lock_file(&lock);
168
0
  strbuf_release(&buf);
169
0
  error_errno(_("failed to write loose object index %s"), path.buf);
170
0
  strbuf_release(&path);
171
0
  return -1;
172
0
}
173
174
static int write_one_object(struct odb_source *source,
175
          const struct object_id *oid,
176
          const struct object_id *compat_oid)
177
0
{
178
0
  struct lock_file lock;
179
0
  int fd;
180
0
  struct stat st;
181
0
  struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
182
183
0
  strbuf_addf(&path, "%s/loose-object-idx", source->path);
184
0
  hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
185
186
0
  fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
187
0
  if (fd < 0)
188
0
    goto errout;
189
0
  if (fstat(fd, &st) < 0)
190
0
    goto errout;
191
0
  if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
192
0
    goto errout;
193
194
0
  strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
195
0
  if (write_in_full(fd, buf.buf, buf.len) < 0)
196
0
    goto errout;
197
0
  if (close(fd))
198
0
    goto errout;
199
0
  adjust_shared_perm(source->odb->repo, path.buf);
200
0
  rollback_lock_file(&lock);
201
0
  strbuf_release(&buf);
202
0
  strbuf_release(&path);
203
0
  return 0;
204
0
errout:
205
0
  error_errno(_("failed to write loose object index %s"), path.buf);
206
0
  close(fd);
207
0
  rollback_lock_file(&lock);
208
0
  strbuf_release(&buf);
209
0
  strbuf_release(&path);
210
0
  return -1;
211
0
}
212
213
int repo_add_loose_object_map(struct odb_source *source,
214
            const struct object_id *oid,
215
            const struct object_id *compat_oid)
216
0
{
217
0
  int inserted = 0;
218
219
0
  if (!should_use_loose_object_map(source->odb->repo))
220
0
    return 0;
221
222
0
  inserted = insert_loose_map(source, oid, compat_oid);
223
0
  if (inserted)
224
0
    return write_one_object(source, oid, compat_oid);
225
0
  return 0;
226
0
}
227
228
int repo_loose_object_map_oid(struct repository *repo,
229
            const struct object_id *src,
230
            const struct git_hash_algo *to,
231
            struct object_id *dest)
232
0
{
233
0
  struct odb_source *source;
234
0
  kh_oid_map_t *map;
235
0
  khiter_t pos;
236
237
0
  for (source = repo->objects->sources; source; source = source->next) {
238
0
    struct odb_source_files *files = odb_source_files_downcast(source);
239
0
    struct loose_object_map *loose_map = files->loose->map;
240
0
    if (!loose_map)
241
0
      continue;
242
0
    map = (to == repo->compat_hash_algo) ?
243
0
      loose_map->to_compat :
244
0
      loose_map->to_storage;
245
0
    pos = kh_get_oid_map(map, *src);
246
0
    if (pos < kh_end(map)) {
247
0
      oidcpy(dest, kh_value(map, pos));
248
0
      return 0;
249
0
    }
250
0
  }
251
0
  return -1;
252
0
}
253
254
void loose_object_map_clear(struct loose_object_map **map)
255
0
{
256
0
  struct loose_object_map *m = *map;
257
0
  struct object_id *oid;
258
259
0
  if (!m)
260
0
    return;
261
262
0
  kh_foreach_value(m->to_compat, oid, free(oid));
263
0
  kh_foreach_value(m->to_storage, oid, free(oid));
264
0
  kh_destroy_oid_map(m->to_compat);
265
0
  kh_destroy_oid_map(m->to_storage);
266
0
  free(m);
267
  *map = NULL;
268
0
}