Coverage Report

Created: 2026-11-12 06:03

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_files *files = odb_source_files_downcast(repo->objects->source);
117
118
0
  for (struct odb_files_dir *dir = files->dirs; dir; dir = dir->next) {
119
0
    if (loose_object_map_load(dir->loose) < 0)
120
0
      return -1;
121
0
  }
122
123
0
  return 0;
124
0
}
125
126
static int write_one_object(struct odb_source_loose *loose,
127
          const struct object_id *oid,
128
          const struct object_id *compat_oid)
129
0
{
130
0
  struct lock_file lock;
131
0
  int fd;
132
0
  struct stat st;
133
0
  struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
134
135
0
  strbuf_addf(&path, "%s/loose-object-idx", loose->base.path);
136
0
  repo_hold_lock_file_for_update_timeout(loose->base.odb->repo, &lock,
137
0
                 path.buf, LOCK_DIE_ON_ERROR, -1);
138
139
0
  fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
140
0
  if (fd < 0)
141
0
    goto errout;
142
0
  if (fstat(fd, &st) < 0)
143
0
    goto errout;
144
0
  if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
145
0
    goto errout;
146
147
0
  strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
148
0
  if (write_in_full(fd, buf.buf, buf.len) < 0)
149
0
    goto errout;
150
0
  if (close(fd))
151
0
    goto errout;
152
0
  adjust_shared_perm(loose->base.odb->repo, path.buf);
153
0
  rollback_lock_file(&lock);
154
0
  strbuf_release(&buf);
155
0
  strbuf_release(&path);
156
0
  return 0;
157
0
errout:
158
0
  error_errno(_("failed to write loose object index %s"), path.buf);
159
0
  if (fd >= 0)
160
0
    close(fd);
161
0
  rollback_lock_file(&lock);
162
0
  strbuf_release(&buf);
163
0
  strbuf_release(&path);
164
0
  return -1;
165
0
}
166
167
int repo_add_loose_object_map(struct odb_source_loose *loose,
168
            const struct object_id *oid,
169
            const struct object_id *compat_oid)
170
0
{
171
0
  int inserted = 0;
172
173
0
  if (!should_use_loose_object_map(loose->base.odb->repo))
174
0
    return 0;
175
176
0
  inserted = insert_loose_map(loose, oid, compat_oid);
177
0
  if (inserted)
178
0
    return write_one_object(loose, oid, compat_oid);
179
0
  return 0;
180
0
}
181
182
int repo_loose_object_map_oid(struct repository *repo,
183
            const struct object_id *src,
184
            const struct git_hash_algo *to,
185
            struct object_id *dest)
186
0
{
187
0
  struct odb_source_files *files = odb_source_files_downcast(repo->objects->source);
188
0
  kh_oid_map_t *map;
189
0
  khiter_t pos;
190
191
0
  for (struct odb_files_dir *dir = files->dirs; dir; dir = dir->next) {
192
0
    struct loose_object_map *loose_map = dir->loose->map;
193
0
    if (!loose_map)
194
0
      continue;
195
0
    map = (to == repo->compat_hash_algo) ?
196
0
      loose_map->to_compat :
197
0
      loose_map->to_storage;
198
0
    pos = kh_get_oid_map(map, *src);
199
0
    if (pos < kh_end(map)) {
200
0
      oidcpy(dest, kh_value(map, pos));
201
0
      return 0;
202
0
    }
203
0
  }
204
0
  return -1;
205
0
}
206
207
void loose_object_map_clear(struct loose_object_map **map)
208
0
{
209
0
  struct loose_object_map *m = *map;
210
0
  struct object_id *oid;
211
212
0
  if (!m)
213
0
    return;
214
215
0
  kh_foreach_value(m->to_compat, oid, free(oid));
216
0
  kh_foreach_value(m->to_storage, oid, free(oid));
217
0
  kh_destroy_oid_map(m->to_compat);
218
0
  kh_destroy_oid_map(m->to_storage);
219
0
  free(m);
220
  *map = NULL;
221
0
}