Coverage Report

Created: 2026-10-17 07:15

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_loose *loose,
50
          const struct object_id *oid,
51
          const struct object_id *compat_oid)
52
0
{
53
0
  struct loose_object_map *map = loose->map;
54
0
  int inserted = 0;
55
56
0
  inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
57
0
  inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
58
0
  if (inserted)
59
0
    oidtree_insert(loose->cache, compat_oid, NULL);
60
61
0
  return inserted;
62
0
}
63
64
int loose_object_map_load(struct odb_source_loose *loose)
65
0
{
66
0
  struct repository *repo = loose->base.odb->repo;
67
0
  struct strbuf buf = STRBUF_INIT;
68
0
  char *path;
69
0
  FILE *fp;
70
0
  int ret = -1;
71
72
0
  if (!should_use_loose_object_map(repo))
73
0
    return 0;
74
75
0
  if (!loose->map)
76
0
    loose_object_map_init(&loose->map);
77
0
  if (!loose->cache) {
78
0
    ALLOC_ARRAY(loose->cache, 1);
79
0
    oidtree_init(loose->cache);
80
0
  }
81
82
0
  insert_loose_map(loose, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
83
0
  insert_loose_map(loose, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
84
0
  insert_loose_map(loose, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
85
86
0
  path = xstrfmt("%s/loose-object-idx", loose->base.path);
87
0
  fp = fopen(path, "rb");
88
0
  if (!fp) {
89
0
    free(path);
90
0
    return 0;
91
0
  }
92
93
0
  if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
94
0
    goto err;
95
0
  while (!strbuf_getline_lf(&buf, fp)) {
96
0
    const char *p;
97
0
    struct object_id oid, compat_oid;
98
0
    if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
99
0
        *p++ != ' ' ||
100
0
        parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
101
0
        p != buf.buf + buf.len)
102
0
      goto err;
103
0
    insert_loose_map(loose, &oid, &compat_oid);
104
0
  }
105
106
0
  ret = ferror(fp) ? -1 : 0;
107
0
err:
108
0
  fclose(fp);
109
0
  strbuf_release(&buf);
110
0
  free(path);
111
0
  return ret;
112
0
}
113
114
int repo_read_loose_object_map(struct repository *repo)
115
0
{
116
0
  struct odb_source *source;
117
118
0
  for (source = repo->objects->sources; source; source = source->next) {
119
0
    struct odb_source_files *files = odb_source_files_downcast(source);
120
0
    if (loose_object_map_load(files->loose) < 0)
121
0
      return -1;
122
0
  }
123
124
0
  return 0;
125
0
}
126
127
int repo_write_loose_object_map(struct repository *repo)
128
0
{
129
0
  struct odb_source_files *files = odb_source_files_downcast(repo->objects->sources);
130
0
  kh_oid_map_t *map = files->loose->map->to_compat;
131
0
  struct lock_file lock;
132
0
  int fd;
133
0
  khiter_t iter;
134
0
  struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
135
136
0
  if (!should_use_loose_object_map(repo))
137
0
    return 0;
138
139
0
  repo_common_path_replace(repo, &path, "objects/loose-object-idx");
140
0
  fd = repo_hold_lock_file_for_update_timeout(repo, &lock, path.buf,
141
0
                LOCK_DIE_ON_ERROR, -1);
142
0
  iter = kh_begin(map);
143
0
  if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
144
0
    goto errout;
145
146
0
  for (; iter != kh_end(map); iter++) {
147
0
    if (kh_exist(map, iter)) {
148
0
      if (oideq(&kh_key(map, iter), repo->hash_algo->empty_tree) ||
149
0
          oideq(&kh_key(map, iter), repo->hash_algo->empty_blob))
150
0
        continue;
151
0
      strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
152
0
      if (write_in_full(fd, buf.buf, buf.len) < 0)
153
0
        goto errout;
154
0
      strbuf_reset(&buf);
155
0
    }
156
0
  }
157
0
  strbuf_release(&buf);
158
0
  if (commit_lock_file(&lock) < 0) {
159
0
    error_errno(_("could not write loose object index %s"), path.buf);
160
0
    strbuf_release(&path);
161
0
    return -1;
162
0
  }
163
0
  strbuf_release(&path);
164
0
  return 0;
165
0
errout:
166
0
  rollback_lock_file(&lock);
167
0
  strbuf_release(&buf);
168
0
  error_errno(_("failed to write loose object index %s"), path.buf);
169
0
  strbuf_release(&path);
170
0
  return -1;
171
0
}
172
173
static int write_one_object(struct odb_source_loose *loose,
174
          const struct object_id *oid,
175
          const struct object_id *compat_oid)
176
0
{
177
0
  struct lock_file lock;
178
0
  int fd;
179
0
  struct stat st;
180
0
  struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
181
182
0
  strbuf_addf(&path, "%s/loose-object-idx", loose->base.path);
183
0
  repo_hold_lock_file_for_update_timeout(loose->base.odb->repo, &lock,
184
0
                 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(loose->base.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
  if (fd >= 0)
207
0
    close(fd);
208
0
  rollback_lock_file(&lock);
209
0
  strbuf_release(&buf);
210
0
  strbuf_release(&path);
211
0
  return -1;
212
0
}
213
214
int repo_add_loose_object_map(struct odb_source_loose *loose,
215
            const struct object_id *oid,
216
            const struct object_id *compat_oid)
217
0
{
218
0
  int inserted = 0;
219
220
0
  if (!should_use_loose_object_map(loose->base.odb->repo))
221
0
    return 0;
222
223
0
  inserted = insert_loose_map(loose, oid, compat_oid);
224
0
  if (inserted)
225
0
    return write_one_object(loose, oid, compat_oid);
226
0
  return 0;
227
0
}
228
229
int repo_loose_object_map_oid(struct repository *repo,
230
            const struct object_id *src,
231
            const struct git_hash_algo *to,
232
            struct object_id *dest)
233
0
{
234
0
  struct odb_source *source;
235
0
  kh_oid_map_t *map;
236
0
  khiter_t pos;
237
238
0
  for (source = repo->objects->sources; source; source = source->next) {
239
0
    struct odb_source_files *files = odb_source_files_downcast(source);
240
0
    struct loose_object_map *loose_map = files->loose->map;
241
0
    if (!loose_map)
242
0
      continue;
243
0
    map = (to == repo->compat_hash_algo) ?
244
0
      loose_map->to_compat :
245
0
      loose_map->to_storage;
246
0
    pos = kh_get_oid_map(map, *src);
247
0
    if (pos < kh_end(map)) {
248
0
      oidcpy(dest, kh_value(map, pos));
249
0
      return 0;
250
0
    }
251
0
  }
252
0
  return -1;
253
0
}
254
255
void loose_object_map_clear(struct loose_object_map **map)
256
0
{
257
0
  struct loose_object_map *m = *map;
258
0
  struct object_id *oid;
259
260
0
  if (!m)
261
0
    return;
262
263
0
  kh_foreach_value(m->to_compat, oid, free(oid));
264
0
  kh_foreach_value(m->to_storage, oid, free(oid));
265
0
  kh_destroy_oid_map(m->to_compat);
266
0
  kh_destroy_oid_map(m->to_storage);
267
0
  free(m);
268
  *map = NULL;
269
0
}