#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cache.h"
#include "attr.h"
#include "blob.h"
#include "commit.h"
#include "config.h"
#include "diff.h"
#include "diffcore.h"
#include "exec-cmd.h"
#include "hashmap.h"
#include "log-tree.h"
#include "shallow.h"
#include "strslice.h"
#include "strbuf.h"
#include "string-list.h"
#include "streaming.h"
#include "object.h"
#include "oidset.h"
#include "quote.h"
#include "refs.h"
#include "remote.h"
#include "replace-object.h"
#include "revision.h"
#include "run-command.h"
#include "tree.h"
#include "tree-walk.h"
#include "hg-data.h"
#include "cinnabar-helper.h"
#include "cinnabar-fast-import.h"
#include "cinnabar-notes.h"
#define _STRINGIFY(s) # s
#define STRINGIFY(s) _STRINGIFY(s)
struct notes_tree git2hg, hg2git, files_meta;
struct object_id metadata_oid, changesets_oid, manifests_oid, git2hg_oid,
hg2git_oid, files_meta_oid;
struct oidset hg2git_seen = OIDSET_INIT;
int metadata_flags = 0;
struct iter_tree_context {
void *ctx;
iter_tree_cb callback;
struct object_list *list;
int recursive;
};
static int do_iter_tree(const struct object_id *oid, struct strbuf *base,
const char *pathname, unsigned mode, void *context)
{
struct iter_tree_context *ctx = context;
if (S_ISDIR(mode)) {
object_list_insert((struct object *)lookup_tree(the_repository, oid),
&ctx->list);
if (ctx->recursive)
return READ_TREE_RECURSIVE;
}
ctx->callback(oid, base, pathname, mode, ctx->ctx);
return 0;
}
int iter_tree(const struct object_id *oid, iter_tree_cb callback, void *context, int recursive) {
struct tree *tree = NULL;
struct iter_tree_context ctx = { context, callback, NULL, recursive };
struct pathspec match_all;
tree = parse_tree_indirect(oid);
if (!tree)
return 0;
memset(&match_all, 0, sizeof(match_all));
read_tree(the_repository, tree, &match_all, do_iter_tree, &ctx);
while (ctx.list) {
struct object *obj = ctx.list->item;
struct object_list *elem = ctx.list;
ctx.list = elem->next;
free(elem);
free_tree_buffer((struct tree *)obj);
}
return 1;
}
struct object_id *commit_oid(struct commit *c) {
return &c->object.oid;
}
struct rev_info *rev_list_new(int argc, const char **argv) {
struct rev_info *revs = xmalloc(sizeof(*revs));
init_revisions(revs, NULL);
setup_revisions(argc, argv, revs, NULL);
if (prepare_revision_walk(revs))
die("revision walk setup failed");
return revs;
}
void rev_list_finish(struct rev_info *revs) {
clear_object_flags(ALL_REV_FLAGS | TOPO_WALK_EXPLORED | TOPO_WALK_INDEGREE);
release_revisions(revs);
free(revs);
}
int maybe_boundary(struct rev_info *revs, struct commit *commit) {
struct commit_list *parent;
struct commit_graft *graft;
if (commit->object.flags & BOUNDARY)
return 1;
parent = commit->parents;
if (revs->boundary && !parent &&
is_repository_shallow(the_repository) &&
(graft = lookup_commit_graft(
the_repository, &commit->object.oid)) != NULL &&
graft->nr_parent < 0) {
return 2;
}
return 0;
}
struct diff_tree_file {
struct object_id *oid;
char *path;
unsigned short mode;
};
struct diff_tree_item {
struct diff_tree_file a;
struct diff_tree_file b;
unsigned short int score;
char status;
};
struct diff_tree_ctx {
void (*cb)(void *, struct diff_tree_item *);
void *context;
};
static void diff_tree_cb(struct diff_queue_struct *q,
struct diff_options *opt, void *data)
{
struct diff_tree_ctx *ctx = data;
int i;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
if (p->status == 0)
die("internal diff status error");
if (p->status != DIFF_STATUS_UNKNOWN) {
struct diff_tree_item item = {
{ &p->one->oid, p->one->path, p->one->mode },
{ &p->two->oid, p->two->path, p->two->mode },
p->score,
p->status,
};
ctx->cb(ctx->context, &item);
}
}
}
void diff_tree_(int argc, const char **argv, void (*cb)(void *, struct diff_tree_item *), void *context)
{
struct diff_tree_ctx ctx = { cb, context };
struct rev_info revs;
init_revisions(&revs, NULL);
revs.diff = 1;
setup_revisions(argc, argv, &revs, NULL);
revs.diffopt.output_format = DIFF_FORMAT_CALLBACK;
revs.diffopt.format_callback = diff_tree_cb;
revs.diffopt.format_callback_data = &ctx;
revs.diffopt.flags.recursive = 1;
if (revs.pending.nr != 2)
die("diff-tree needs two revs");
diff_tree_oid(&revs.pending.objects[0].item->oid,
&revs.pending.objects[1].item->oid,
"", &revs.diffopt);
log_tree_diff_flush(&revs);
release_revisions(&revs);
}
void ensure_notes(struct notes_tree *notes)
{
if (!notes_initialized(notes)) {
const struct object_id *oid;
int flags = 0;
if (notes == &git2hg)
oid = &git2hg_oid;
else if (notes == &hg2git)
oid = &hg2git_oid;
else if (notes == &files_meta) {
oid = &files_meta_oid;
if (!(metadata_flags & FILES_META))
flags = NOTES_INIT_EMPTY;
} else
die("Unknown notes tree");
if (is_null_oid(oid))
flags = NOTES_INIT_EMPTY;
init_notes(notes, oid_to_hex(oid), combine_notes_ignore, flags);
}
}
const struct object_id *repo_lookup_replace_object(
struct repository *r, const struct object_id *oid)
{
return lookup_replace_object(r, oid);
}
const struct object_id *resolve_hg(
struct notes_tree* tree, const struct hg_object_id *oid, size_t len)
{
struct object_id git_oid;
const struct object_id *note;
ensure_notes(tree);
note = get_note_hg(tree, oid);
if (len == 40)
return note;
hg_oidcpy2git(&git_oid, oid);
return get_abbrev_note(tree, &git_oid, len);
}
const struct object_id *resolve_hg2git(const struct hg_object_id *oid,
size_t len)
{
return resolve_hg(&hg2git, oid, len);
}
static const char *hgattr(unsigned int mode)
{
if (S_ISGITLINK(mode)) {
if ((mode & 0755) == 0755)
return "x";
else if ((mode & 0644) == 0644)
return "";
else if ((mode & 0777) == 0)
return "l";
}
die("Unsupported mode %06o", mode);
}
static void track_tree(struct tree *tree, struct object_list **tree_list)
{
if (tree_list) {
object_list_insert(&tree->object, tree_list);
tree->object.flags |= SEEN;
}
}
struct manifest_tree_state {
struct tree *tree;
struct tree_desc desc;
};
static int manifest_tree_state_init(const struct object_id *tree_id,
struct manifest_tree_state *result,
struct object_list **tree_list)
{
result->tree = parse_tree_indirect(tree_id);
if (!result->tree)
return -1;
track_tree(result->tree, tree_list);
init_tree_desc(&result->desc, result->tree->buffer,
result->tree->size);
return 0;
}
struct merge_manifest_tree_state {
struct manifest_tree_state state_a, state_b;
struct name_entry entry_a, entry_b;
struct strslice entry_a_path, entry_b_path;
int cmp;
};
struct merge_name_entry {
struct name_entry *entry_a, *entry_b;
struct strslice path;
};
static int merge_manifest_tree_state_init(const struct object_id *tree_id_a,
const struct object_id *tree_id_b,
struct merge_manifest_tree_state *result,
struct object_list **tree_list)
{
int ret;
memset(result, 0, sizeof(*result));
result->cmp = 0;
if (tree_id_a) {
ret = manifest_tree_state_init(tree_id_a, &result->state_a, tree_list);
if (ret)
return ret;
} else {
result->entry_a_path = empty_strslice();
result->cmp = 1;
}
if (tree_id_b) {
return manifest_tree_state_init(tree_id_b, &result->state_b, tree_list);
} else if (result->cmp == 0) {
result->entry_b_path = empty_strslice();
result->cmp = -1;
return 0;
}
return 1;
}
static int merge_tree_entry(struct merge_manifest_tree_state *state,
struct merge_name_entry *entries)
{
if (state->cmp <= 0) {
if (tree_entry(&state->state_a.desc, &state->entry_a)) {
state->entry_a_path = strslice_from_str(state->entry_a.path);
} else {
state->entry_a_path = empty_strslice();
}
}
if (state->cmp >= 0) {
if (tree_entry(&state->state_b.desc, &state->entry_b)) {
state->entry_b_path = strslice_from_str(state->entry_b.path);
} else {
state->entry_b_path = empty_strslice();
}
}
if (!state->entry_a_path.len) {
if (!state->entry_b_path.len)
return 0;
state->cmp = 1;
} else if (!state->entry_b_path.len) {
state->cmp = -1;
} else {
state->cmp = base_name_compare(
state->entry_a_path.buf, state->entry_a_path.len, state->entry_a.mode,
state->entry_b_path.buf, state->entry_b_path.len, state->entry_b.mode);
}
if (state->cmp <= 0) {
entries->entry_a = &state->entry_a;
entries->path = state->entry_a_path;
} else {
entries->entry_a = NULL;
}
if (state->cmp >= 0) {
entries->entry_b = &state->entry_b;
entries->path = state->entry_b_path;
} else {
entries->entry_b = NULL;
}
return 1;
}
static int manifest_entry_equal(const struct name_entry *e1,
const struct name_entry *e2)
{
return (e1->mode == e2->mode) && (oidcmp(&e1->oid, &e2->oid) == 0);
}
static int path_match(struct strslice base, struct strslice name,
struct strslice path)
{
struct strslice slice;
if (!strslice_startswith(path, base) ||
!strslice_startswith(strslice_slice(path, base.len, SIZE_MAX),
name))
return 0;
slice = strslice_slice(path, name.len + base.len, 1);
return slice.len == 1 && (slice.buf[0] == '\0' || slice.buf[0] == '/');
}
static void recurse_manifest(const struct object_id *ref_tree_id,
struct strslice ref_manifest,
const struct object_id *tree_id,
struct strbuf *manifest, struct strslice base,
struct object_list **tree_list)
{
struct merge_manifest_tree_state state;
struct merge_name_entry entries;
struct strslice cursor;
struct strslice underscore = { 1, "_" };
struct strbuf dir = STRBUF_INIT;
if (merge_manifest_tree_state_init(ref_tree_id, tree_id, &state, tree_list))
goto corrupted;
while (merge_tree_entry(&state, &entries)) {
if (!strslice_startswith(entries.path, underscore))
goto corrupted;
cursor = ref_manifest;
if (entries.entry_a) {
size_t len = base.len + entries.path.len + 40;
do {
strslice_split_once(&ref_manifest, '\n');
} while (S_ISDIR(entries.entry_a->mode) &&
(ref_manifest.len > len) &&
path_match(base, strslice_slice(
entries.path, 1, SIZE_MAX), ref_manifest));
}
if (!entries.entry_b)
continue;
if (entries.entry_a && entries.entry_b &&
manifest_entry_equal(entries.entry_a, entries.entry_b)) {
strbuf_add(manifest, cursor.buf,
cursor.len - ref_manifest.len);
continue;
}
if (entries.entry_b && !S_ISDIR(entries.entry_b->mode)) {
strbuf_addslice(manifest, base);
strbuf_addslice(manifest, strslice_slice(
entries.path, 1, SIZE_MAX));
strbuf_addf(manifest, "%c%s%s\n", '\0',
oid_to_hex(&entries.entry_b->oid),
hgattr(entries.entry_b->mode));
continue;
}
strbuf_addslice(&dir, base);
strbuf_addslice(&dir, strslice_slice(
entries.path, 1, SIZE_MAX));
strbuf_addch(&dir, '/');
if (entries.entry_a && entries.entry_b &&
S_ISDIR(entries.entry_a->mode)) {
recurse_manifest(&entries.entry_a->oid, cursor,
&entries.entry_b->oid, manifest,
strbuf_as_slice(&dir), tree_list);
} else
recurse_manifest(NULL, empty_strslice(),
&entries.entry_b->oid, manifest,
strbuf_as_slice(&dir), tree_list);
strbuf_release(&dir);
}
return;
corrupted:
die("Corrupted metadata");
}
struct manifest {
struct object_id tree_id;
struct strbuf content;
struct object_list *tree_list;
};
#define MANIFEST_INIT { { { 0, } }, STRBUF_INIT, NULL }
static struct manifest generated_manifest = MANIFEST_INIT;
struct strbuf *generate_manifest(const struct object_id *oid)
{
struct strbuf content = STRBUF_INIT;
struct object_list *tree_list = NULL;
struct object_list *previous_list = generated_manifest.tree_list;
while (previous_list) {
previous_list->item->flags &= ~SEEN;
previous_list = previous_list->next;
}
if (oidcmp(&generated_manifest.tree_id, oid) == 0) {
return &generated_manifest.content;
}
if (generated_manifest.content.len) {
struct strslice gm;
gm = strbuf_slice(&generated_manifest.content, 0, SIZE_MAX);
strbuf_grow(&content, generated_manifest.content.alloc - 1);
recurse_manifest(&generated_manifest.tree_id, gm,
oid, &content, empty_strslice(), &tree_list);
} else {
recurse_manifest(NULL, empty_strslice(), oid, &content,
empty_strslice(), &tree_list);
}
oidcpy(&generated_manifest.tree_id, oid);
strbuf_swap(&content, &generated_manifest.content);
strbuf_release(&content);
previous_list = generated_manifest.tree_list;
generated_manifest.tree_list = tree_list;
while (previous_list) {
struct object *obj = previous_list->item;
struct object_list *elem = previous_list;
previous_list = elem->next;
free(elem);
if (!(obj->flags & SEEN))
free_tree_buffer((struct tree *)obj);
}
return &generated_manifest.content;
}
static void get_manifest_oid(const struct commit *commit, struct hg_object_id *oid)
{
const char *msg;
const char *hex_sha1;
msg = get_commit_buffer(commit, NULL);
hex_sha1 = strstr(msg, "\n\n") + 2;
if (get_sha1_hex(hex_sha1, oid->hash))
hg_oidclr(oid);
unuse_commit_buffer(commit, msg);
}
static void hg_sha1(struct strbuf *data, const struct hg_object_id *parent1,
const struct hg_object_id *parent2, struct hg_object_id *result)
{
git_SHA_CTX ctx;
if (!parent1)
parent1 = &hg_null_oid;
if (!parent2)
parent2 = &hg_null_oid;
git_SHA1_Init(&ctx);
if (hg_oidcmp(parent1, parent2) < 0) {
git_SHA1_Update(&ctx, parent1, 20);
git_SHA1_Update(&ctx, parent2, 20);
} else {
git_SHA1_Update(&ctx, parent2, 20);
git_SHA1_Update(&ctx, parent1, 20);
}
git_SHA1_Update(&ctx, data->buf, data->len);
git_SHA1_Final(result->hash, &ctx);
}
int check_manifest(const struct object_id *oid,
struct hg_object_id *hg_oid)
{
struct hg_object_id parent1, parent2, stored, computed;
const struct commit *manifest_commit;
struct strbuf *manifest;
manifest = generate_manifest(oid);
if (!manifest)
return 0;
manifest_commit = lookup_commit(the_repository, oid);
if (!manifest_commit)
return 0;
if (manifest_commit->parents) {
get_manifest_oid(manifest_commit->parents->item, &parent1);
if (manifest_commit->parents->next) {
get_manifest_oid(manifest_commit->parents->next->item,
&parent2);
} else
hg_oidclr(&parent2);
} else {
hg_oidclr(&parent1);
hg_oidclr(&parent2);
}
if (!hg_oid)
hg_oid = &computed;
hg_sha1(manifest, &parent1, &parent2, hg_oid);
get_manifest_oid(manifest_commit, &stored);
return hg_oideq(&stored, hg_oid);
}
int check_file(const struct hg_object_id *oid,
const struct hg_object_id *parent1,
const struct hg_object_id *parent2)
{
struct hg_file file;
struct hg_object_id result;
hg_file_init(&file);
hg_file_load(&file, oid);
hg_sha1(&file.file, parent1, parent2, &result);
if (hg_oideq(oid, &result))
goto ok;
hg_sha1(&file.file, parent1, NULL, &result);
if (hg_oideq(oid, &result))
goto ok;
hg_sha1(&file.file, parent2, NULL, &result);
if (hg_oideq(oid, &result))
goto ok;
hg_sha1(&file.file, parent1, parent1, &result);
if (hg_oideq(oid, &result))
goto ok;
hg_sha1(&file.file, NULL, NULL, &result);
if (!hg_oideq(oid, &result))
goto error;
ok:
hg_file_release(&file);
return 1;
error:
hg_file_release(&file);
return 0;
}
static void reset_heads(struct oid_array *heads)
{
oid_array_clear(heads);
heads->sorted = 1;
}
void reset_manifest_heads(void)
{
reset_heads(&manifest_heads);
}
static struct name_entry *
lazy_tree_entry_by_name(struct manifest_tree_state *state,
const struct object_id *tree_id,
const char *path)
{
int cmp;
if (!tree_id)
return NULL;
if (!state->tree) {
if (manifest_tree_state_init(tree_id, state, NULL))
return NULL;
}
while (state->desc.size &&
(cmp = strcmp(state->desc.entry.path, path)) < 0)
update_tree_entry(&state->desc);
if (state->desc.size && cmp == 0)
return &state->desc.entry;
return NULL;
}
struct oid_map_entry {
struct hashmap_entry ent;
struct object_id old_oid;
struct object_id new_oid;
};
static int oid_map_entry_cmp(const void *cmpdata, const struct hashmap_entry *e1,
const struct hashmap_entry *e2, const void *keydata)
{
const struct oid_map_entry *entry1 =
container_of(e1, const struct oid_map_entry, ent);
const struct oid_map_entry *entry2 =
container_of(e2, const struct oid_map_entry, ent);
return oidcmp(&entry1->old_oid, &entry2->old_oid);
}
static void recurse_create_git_tree(const struct object_id *tree_id,
const struct object_id *reference,
const struct object_id *merge_tree_id,
struct object_id *result,
struct hashmap *cache)
{
struct oid_map_entry k, *cache_entry = NULL;
if (!merge_tree_id) {
hashmap_entry_init(&k.ent, oidhash(tree_id));
oidcpy(&k.old_oid, tree_id);
cache_entry = hashmap_get_entry(cache, &k, ent, NULL);
}
if (!cache_entry) {
struct merge_manifest_tree_state state;
struct manifest_tree_state ref_state = { NULL, };
struct merge_name_entry entries;
struct strbuf tree_buf = STRBUF_INIT;
if (merge_manifest_tree_state_init(tree_id, merge_tree_id, &state, NULL))
goto corrupted;
while (merge_tree_entry(&state, &entries)) {
struct object_id oid;
struct name_entry *entry = entries.entry_a ? entries.entry_a : entries.entry_b;
unsigned mode = entry->mode;
struct strslice entry_path;
struct strslice underscore = { 1, "_" };
if (!strslice_startswith(entries.path, underscore))
goto corrupted;
entry_path = strslice_slice(entries.path, 1, SIZE_MAX);
if (entry_path.len == 0) {
if (!S_ISDIR(mode))
goto corrupted;
if (merge_tree_id)
continue;
recurse_create_git_tree(
tree_id, reference, &entry->oid, result, cache);
goto cleanup;
} else if (S_ISDIR(mode)) {
struct name_entry *ref_entry;
ref_entry = lazy_tree_entry_by_name(
&ref_state, reference, entry_path.buf);
recurse_create_git_tree(
&entry->oid,
ref_entry ? &ref_entry->oid : NULL,
(entries.entry_b && S_ISDIR(entries.entry_b->mode))
? &entries.entry_b->oid : NULL,
&oid, cache);
} else {
const struct object_id *file_oid;
struct hg_object_id hg_oid;
oidcpy2hg(&hg_oid, &entry->oid);
if (is_empty_hg_file(&hg_oid))
file_oid = ensure_empty_blob();
else
file_oid = resolve_hg2git(&hg_oid, 40);
if (!file_oid)
goto corrupted;
oidcpy(&oid, file_oid);
mode &= 0777;
if (!mode)
mode = S_IFLNK;
else
mode = S_IFREG | mode;
}
strbuf_addf(&tree_buf, "%o ", canon_mode(mode));
strbuf_addslice(&tree_buf, entry_path);
strbuf_addch(&tree_buf, '\0');
strbuf_add(&tree_buf, oid.hash, 20);
}
if (!merge_tree_id) {
cache_entry = xmalloc(sizeof(k));
cache_entry->ent = k.ent;
cache_entry->old_oid = k.old_oid;
}
store_git_tree(&tree_buf, reference, cache_entry ? &cache_entry->new_oid : result);
strbuf_release(&tree_buf);
if (!merge_tree_id) {
hashmap_add(cache, &cache_entry->ent);
}
cleanup:
if (state.state_a.tree)
free_tree_buffer(state.state_a.tree);
if (state.state_b.tree)
free_tree_buffer(state.state_b.tree);
if (ref_state.tree)
free_tree_buffer(ref_state.tree);
}
if (result && cache_entry)
oidcpy(result, &cache_entry->new_oid);
return;
corrupted:
die("Corrupt mercurial metadata");
}
static struct hashmap git_tree_cache;
void create_git_tree(const struct object_id *tree_id,
const struct object_id *ref_tree,
struct object_id *result)
{
recurse_create_git_tree(tree_id, ref_tree, NULL, result, &git_tree_cache);
}
static void reset_replace_map(void)
{
oidmap_free(the_repository->objects->replace_map, 1);
FREE_AND_NULL(the_repository->objects->replace_map);
the_repository->objects->replace_map_initialized = 0;
}
unsigned int replace_map_size(void)
{
return hashmap_get_size(&the_repository->objects->replace_map->map);
}
static int count_refs(const char *refname, const struct object_id *oid,
int flags, void *cb_dat) {
size_t *count = (size_t *)cb_dat;
(*count)++;
return 0;
}
static void init_metadata(void)
{
struct commit *c;
struct commit_list *cl;
const char *msg, *body;
struct strbuf **flags, **f;
struct tree *tree;
struct tree_desc desc;
struct name_entry entry;
struct replace_object *replace;
size_t count = 0;
c = lookup_commit_reference_by_name(METADATA_REF);
if (!c) {
oidcpy(&metadata_oid, null_oid());
oidcpy(&changesets_oid, null_oid());
oidcpy(&manifests_oid, null_oid());
oidcpy(&hg2git_oid, null_oid());
oidcpy(&git2hg_oid, null_oid());
oidcpy(&files_meta_oid, null_oid());
return;
}
oidcpy(&metadata_oid, &c->object.oid);
cl = c->parents;
if (!cl) die("Invalid metadata?");
oidcpy(&changesets_oid, &cl->item->object.oid);
cl = cl->next;
if (!cl) die("Invalid metadata?");
oidcpy(&manifests_oid, &cl->item->object.oid);
cl = cl->next;
if (!cl) die("Invalid metadata?");
oidcpy(&hg2git_oid, &cl->item->object.oid);
cl = cl->next;
if (!cl) die("Invalid metadata?");
oidcpy(&git2hg_oid, &cl->item->object.oid);
cl = cl->next;
if (!cl) die("Invalid metadata?");
oidcpy(&files_meta_oid, &cl->item->object.oid);
msg = get_commit_buffer(c, NULL);
body = strstr(msg, "\n\n") + 2;
unuse_commit_buffer(c, msg);
flags = strbuf_split_str(body, ' ', -1);
for (f = flags; *f; f++) {
strbuf_trim(*f);
if (!strcmp("files-meta", (*f)->buf))
metadata_flags |= FILES_META;
else if (!strcmp("unified-manifests", (*f)->buf)) {
strbuf_list_free(flags);
goto old;
} else if (!strcmp("unified-manifests-v2", (*f)->buf))
metadata_flags |= UNIFIED_MANIFESTS_v2;
else {
strbuf_list_free(flags);
goto new;
}
}
strbuf_list_free(flags);
if (!(metadata_flags & (FILES_META | UNIFIED_MANIFESTS_v2)))
goto old;
for_each_ref_in("refs/cinnabar/branches/", count_refs, &count);
if (count)
goto old;
reset_replace_map();
the_repository->objects->replace_map =
xmalloc(sizeof(*the_repository->objects->replace_map));
oidmap_init(the_repository->objects->replace_map, 0);
the_repository->objects->replace_map_initialized = 1;
tree = get_commit_tree(c);
parse_tree(tree);
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object_id original_oid;
if (entry.pathlen != 40 ||
get_oid_hex(entry.path, &original_oid)) {
struct strbuf buf = STRBUF_INIT;
strbuf_add(&buf, entry.path, entry.pathlen);
warning(_("bad replace name: %s"), buf.buf);
strbuf_release(&buf);
continue;
}
if (oideq(&entry.oid, &original_oid)) {
warning(_("self-referencing graft: %s"),
oid_to_hex(&original_oid));
continue;
}
replace = xmalloc(sizeof(*replace));
oidcpy(&replace->original.oid, &original_oid);
oidcpy(&replace->replacement, &entry.oid);
if (oidmap_put(the_repository->objects->replace_map, replace))
die(_("duplicate replace: %s"),
oid_to_hex(&replace->original.oid));
}
if (the_repository->objects->replace_map->map.tablesize == 0) {
count = 0;
for_each_ref_in("refs/cinnabar/replace/", count_refs, &count);
if (count > 0)
goto old;
}
return;
old:
die("Metadata from git-cinnabar versions older than 0.5.0 is not "
"supported.\n"
"Please run `git cinnabar upgrade` with version 0.5.x first.");
new:
die("It looks like this repository was used with a newer version of "
"git-cinnabar. Cannot use this version.");
#if 0#endif
}
void dump_ref_updates(void);
extern void reset_changeset_heads(void);
void do_reload(void)
{
if (notes_initialized(&git2hg))
free_notes(&git2hg);
if (notes_initialized(&hg2git))
free_notes(&hg2git);
if (notes_initialized(&files_meta))
free_notes(&files_meta);
oidset_clear(&hg2git_seen);
hashmap_clear_and_free(&git_tree_cache, struct oid_map_entry, ent);
hashmap_init(&git_tree_cache, oid_map_entry_cmp, NULL, 0);
oid_array_clear(&manifest_heads);
dump_ref_updates();
metadata_flags = 0;
reset_replace_map();
init_metadata();
reset_changeset_heads();
}
static void init_git_config(void)
{
struct child_process proc = CHILD_PROCESS_INIT;
struct strbuf path = STRBUF_INIT;
const char *env = getenv(EXEC_PATH_ENVIRONMENT);
if (env && *env) {
setup_path();
}
strvec_pushl(&proc.args, "git", "config", "--system", "-e", NULL);
strvec_push(&proc.env, "GIT_EDITOR=echo");
proc.no_stdin = 1;
proc.no_stderr = 1;
capture_command(&proc, &path, 0);
strbuf_trim_trailing_newline(&path);
if (path.len)
setenv("GIT_CONFIG_SYSTEM", path.buf, 1);
strbuf_release(&path);
}
static void cleanup_git_config(void)
{
const char *value;
if (!git_config_get_value("cinnabar.fsck", &value)) {
char *user_config, *xdg_config;
git_global_config(&user_config, &xdg_config);
if (user_config) {
if (access_or_warn(user_config, R_OK, 0) &&
xdg_config &&
!access_or_warn(xdg_config, R_OK, 0))
{
git_config_set_in_file_gently(
xdg_config, "cinnabar.fsck", NULL);
} else {
git_config_set_in_file_gently(
user_config, "cinnabar.fsck", NULL);
}
}
free(user_config);
free(xdg_config);
user_config = git_pathdup("config");
if (user_config) {
git_config_set_in_file_gently(
user_config, "cinnabar.fsck", NULL);
}
free(user_config);
}
}
static void restore_sigpipe_to_default(void)
{
sigset_t unblock;
sigemptyset(&unblock);
sigaddset(&unblock, SIGPIPE);
sigprocmask(SIG_UNBLOCK, &unblock, NULL);
signal(SIGPIPE, SIG_DFL);
}
const char *remote_get_name(const struct remote *remote)
{
return remote->name;
}
void remote_get_url(const struct remote *remote, const char * const **url,
int* url_nr)
{
*url = remote->url;
*url_nr = remote->url_nr;
}
int remote_skip_default_update(const struct remote *remote)
{
return remote->skip_default_update;
}
static int nongit = 0;
extern NORETURN void do_panic(const char *err, size_t len);
static NORETURN void die_panic(const char *err, va_list params)
{
char msg[4096];
int len = vsnprintf(msg, sizeof(msg), err, params);
do_panic(msg, (size_t)(len < 0) ? 0 : len);
}
void init_cinnabar(const char *argv0)
{
set_die_routine(die_panic);
sanitize_stdfds();
restore_sigpipe_to_default();
git_resolve_executable_dir(argv0);
git_setup_gettext();
initialize_the_repository();
attr_start();
init_git_config();
setup_git_directory_gently(&nongit);
git_config(git_diff_basic_config, NULL);
cleanup_git_config();
save_commit_buffer = 0;
warn_on_object_refname_ambiguity = 0;
}
static int initialized = 0;
int init_cinnabar_2(void)
{
if (nongit) {
return 0;
}
init_metadata();
hashmap_init(&git_tree_cache, oid_map_entry_cmp, NULL, 0);
initialized = 1;
return 1;
}
void done_cinnabar(void)
{
if (notes_initialized(&git2hg))
free_notes(&git2hg);
if (notes_initialized(&hg2git))
free_notes(&hg2git);
if (notes_initialized(&files_meta))
free_notes(&files_meta);
oidset_clear(&hg2git_seen);
hashmap_clear_and_free(&git_tree_cache, struct oid_map_entry, ent);
}
int common_exit(const char *file, int line, int code)
{
return code;
}