зеркало из https://github.com/microsoft/git.git
260 строки
6.6 KiB
C
260 строки
6.6 KiB
C
#include "git-compat-util.h"
|
|
#include "hash.h"
|
|
#include "path.h"
|
|
#include "object-store.h"
|
|
#include "hex.h"
|
|
#include "wrapper.h"
|
|
#include "gettext.h"
|
|
#include "loose.h"
|
|
#include "lockfile.h"
|
|
#include "oidtree.h"
|
|
|
|
static const char *loose_object_header = "# loose-object-idx\n";
|
|
|
|
static inline int should_use_loose_object_map(struct repository *repo)
|
|
{
|
|
return repo->compat_hash_algo && repo->gitdir;
|
|
}
|
|
|
|
void loose_object_map_init(struct loose_object_map **map)
|
|
{
|
|
struct loose_object_map *m;
|
|
m = xmalloc(sizeof(**map));
|
|
m->to_compat = kh_init_oid_map();
|
|
m->to_storage = kh_init_oid_map();
|
|
*map = m;
|
|
}
|
|
|
|
static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
|
|
{
|
|
khiter_t pos;
|
|
int ret;
|
|
struct object_id *stored;
|
|
|
|
pos = kh_put_oid_map(map, *key, &ret);
|
|
|
|
/* This item already exists in the map. */
|
|
if (ret == 0)
|
|
return 0;
|
|
|
|
stored = xmalloc(sizeof(*stored));
|
|
oidcpy(stored, value);
|
|
kh_value(map, pos) = stored;
|
|
return 1;
|
|
}
|
|
|
|
static int insert_loose_map(struct object_directory *odb,
|
|
const struct object_id *oid,
|
|
const struct object_id *compat_oid)
|
|
{
|
|
struct loose_object_map *map = odb->loose_map;
|
|
int inserted = 0;
|
|
|
|
inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
|
|
inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
|
|
if (inserted)
|
|
oidtree_insert(odb->loose_objects_cache, compat_oid);
|
|
|
|
return inserted;
|
|
}
|
|
|
|
static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
|
|
{
|
|
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
|
|
FILE *fp;
|
|
|
|
if (!dir->loose_map)
|
|
loose_object_map_init(&dir->loose_map);
|
|
if (!dir->loose_objects_cache) {
|
|
ALLOC_ARRAY(dir->loose_objects_cache, 1);
|
|
oidtree_init(dir->loose_objects_cache);
|
|
}
|
|
|
|
insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
|
|
insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
|
|
insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
|
|
|
|
strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
|
|
fp = fopen(path.buf, "rb");
|
|
if (!fp) {
|
|
strbuf_release(&path);
|
|
return 0;
|
|
}
|
|
|
|
errno = 0;
|
|
if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
|
|
goto err;
|
|
while (!strbuf_getline_lf(&buf, fp)) {
|
|
const char *p;
|
|
struct object_id oid, compat_oid;
|
|
if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
|
|
*p++ != ' ' ||
|
|
parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
|
|
p != buf.buf + buf.len)
|
|
goto err;
|
|
insert_loose_map(dir, &oid, &compat_oid);
|
|
}
|
|
|
|
strbuf_release(&buf);
|
|
strbuf_release(&path);
|
|
return errno ? -1 : 0;
|
|
err:
|
|
strbuf_release(&buf);
|
|
strbuf_release(&path);
|
|
return -1;
|
|
}
|
|
|
|
int repo_read_loose_object_map(struct repository *repo)
|
|
{
|
|
struct object_directory *dir;
|
|
|
|
if (!should_use_loose_object_map(repo))
|
|
return 0;
|
|
|
|
prepare_alt_odb(repo);
|
|
|
|
for (dir = repo->objects->odb; dir; dir = dir->next) {
|
|
if (load_one_loose_object_map(repo, dir) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int repo_write_loose_object_map(struct repository *repo)
|
|
{
|
|
kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
|
|
struct lock_file lock;
|
|
int fd;
|
|
khiter_t iter;
|
|
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
|
|
|
|
if (!should_use_loose_object_map(repo))
|
|
return 0;
|
|
|
|
strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
|
|
fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
|
|
iter = kh_begin(map);
|
|
if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
|
|
goto errout;
|
|
|
|
for (; iter != kh_end(map); iter++) {
|
|
if (kh_exist(map, iter)) {
|
|
if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
|
|
oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
|
|
continue;
|
|
strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
|
|
if (write_in_full(fd, buf.buf, buf.len) < 0)
|
|
goto errout;
|
|
strbuf_reset(&buf);
|
|
}
|
|
}
|
|
strbuf_release(&buf);
|
|
if (commit_lock_file(&lock) < 0) {
|
|
error_errno(_("could not write loose object index %s"), path.buf);
|
|
strbuf_release(&path);
|
|
return -1;
|
|
}
|
|
strbuf_release(&path);
|
|
return 0;
|
|
errout:
|
|
rollback_lock_file(&lock);
|
|
strbuf_release(&buf);
|
|
error_errno(_("failed to write loose object index %s\n"), path.buf);
|
|
strbuf_release(&path);
|
|
return -1;
|
|
}
|
|
|
|
static int write_one_object(struct repository *repo, const struct object_id *oid,
|
|
const struct object_id *compat_oid)
|
|
{
|
|
struct lock_file lock;
|
|
int fd;
|
|
struct stat st;
|
|
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
|
|
|
|
strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
|
|
hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
|
|
|
|
fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
|
|
if (fd < 0)
|
|
goto errout;
|
|
if (fstat(fd, &st) < 0)
|
|
goto errout;
|
|
if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
|
|
goto errout;
|
|
|
|
strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
|
|
if (write_in_full(fd, buf.buf, buf.len) < 0)
|
|
goto errout;
|
|
if (close(fd))
|
|
goto errout;
|
|
adjust_shared_perm(path.buf);
|
|
rollback_lock_file(&lock);
|
|
strbuf_release(&buf);
|
|
strbuf_release(&path);
|
|
return 0;
|
|
errout:
|
|
error_errno(_("failed to write loose object index %s\n"), path.buf);
|
|
close(fd);
|
|
rollback_lock_file(&lock);
|
|
strbuf_release(&buf);
|
|
strbuf_release(&path);
|
|
return -1;
|
|
}
|
|
|
|
int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
|
|
const struct object_id *compat_oid)
|
|
{
|
|
int inserted = 0;
|
|
|
|
if (!should_use_loose_object_map(repo))
|
|
return 0;
|
|
|
|
inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
|
|
if (inserted)
|
|
return write_one_object(repo, oid, compat_oid);
|
|
return 0;
|
|
}
|
|
|
|
int repo_loose_object_map_oid(struct repository *repo,
|
|
const struct object_id *src,
|
|
const struct git_hash_algo *to,
|
|
struct object_id *dest)
|
|
{
|
|
struct object_directory *dir;
|
|
kh_oid_map_t *map;
|
|
khiter_t pos;
|
|
|
|
for (dir = repo->objects->odb; dir; dir = dir->next) {
|
|
struct loose_object_map *loose_map = dir->loose_map;
|
|
if (!loose_map)
|
|
continue;
|
|
map = (to == repo->compat_hash_algo) ?
|
|
loose_map->to_compat :
|
|
loose_map->to_storage;
|
|
pos = kh_get_oid_map(map, *src);
|
|
if (pos < kh_end(map)) {
|
|
oidcpy(dest, kh_value(map, pos));
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void loose_object_map_clear(struct loose_object_map **map)
|
|
{
|
|
struct loose_object_map *m = *map;
|
|
struct object_id *oid;
|
|
|
|
if (!m)
|
|
return;
|
|
|
|
kh_foreach_value(m->to_compat, oid, free(oid));
|
|
kh_foreach_value(m->to_storage, oid, free(oid));
|
|
kh_destroy_oid_map(m->to_compat);
|
|
kh_destroy_oid_map(m->to_storage);
|
|
free(m);
|
|
*map = NULL;
|
|
}
|