diff --git a/src/cinnabar-fast-import.c b/src/cinnabar-fast-import.c index 1c7024ad4..1f1cd0c32 100644 --- a/src/cinnabar-fast-import.c +++ b/src/cinnabar-fast-import.c @@ -16,7 +16,6 @@ static void cinnabar_unregister_shallow(const struct object_id *oid); #include "hg-bundle.h" #include "hg-data.h" #include "list.h" -#include "oid-array.h" #include "replace-object.h" #include "shallow.h" #include "strslice.h" @@ -263,80 +262,6 @@ const struct object_id empty_tree = { { 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04, }, GIT_HASH_SHA1 }; -struct oid_array manifest_heads = OID_ARRAY_INIT; -int manifest_heads_dirty = 0; - -static void oid_array_insert(struct oid_array *array, int index, - const struct object_id *oid) -{ - ALLOC_GROW(array->oid, array->nr + 1, array->alloc); - memmove(&array->oid[index+1], &array->oid[index], - sizeof(array->oid[0]) * (array->nr++ - index)); - oidcpy(&array->oid[index], oid); - if (array == &manifest_heads) - manifest_heads_dirty = 1; -} - -static void oid_array_remove(struct oid_array *array, int index) -{ - memmove(&array->oid[index], &array->oid[index+1], - sizeof(array->oid[0]) * (--array->nr - index)); - if (array == &manifest_heads) - manifest_heads_dirty = 1; -} - -void ensure_heads(struct oid_array *heads) -{ - struct commit *c = NULL; - struct commit_list *parent; - - /* We always keep the array sorted, so if it's not sorted, it's - * not initialized. */ - if (heads->sorted) - return; - - heads->sorted = 1; - if (heads == &manifest_heads) { - c = lookup_commit_reference(the_repository, &manifests_oid); - if (repo_parse_commit(the_repository, c)) - c = NULL; - } - for (parent = c ? c->parents : NULL; parent; - parent = parent->next) { - const struct object_id *parent_sha1 = - &parent->item->object.oid; - if (!heads->nr || oidcmp(&heads->oid[heads->nr-1], - parent_sha1)) { - oid_array_insert(heads, heads->nr, parent_sha1); - } else { - /* This should not happen, but just in case, - * instead of failing. */ - add_head(heads, parent_sha1); - } - } -} - -void add_head(struct oid_array *heads, const struct object_id *oid) -{ - struct commit *c = NULL; - struct commit_list *parent; - int pos; - - ensure_heads(heads); - c = lookup_commit(the_repository, oid); - parse_commit_or_die(c); - - for (parent = c->parents; parent; parent = parent->next) { - pos = oid_array_lookup(heads, &parent->item->object.oid); - if (pos >= 0) - oid_array_remove(heads, pos); - } - pos = oid_array_lookup(heads, oid); - if (pos >= 0) - return; - oid_array_insert(heads, -pos - 1, oid); -} - static void handle_changeset_conflict(const struct hg_object_id *hg_id, struct object_id *git_id) { @@ -410,7 +335,6 @@ void do_set_(const char *what, const struct hg_object_id *hg_id, const struct object_id *git_id) { enum object_type type; - struct oid_array *heads = NULL; struct notes_tree *notes = &hg2git; int is_changeset = 0; @@ -419,9 +343,7 @@ void do_set_(const char *what, const struct hg_object_id *hg_id, type = OBJ_BLOB; } else if (!strcmp(what, "manifest") || !strcmp(what, "changeset")) { type = OBJ_COMMIT; - if (what[0] == 'm') - heads = &manifest_heads; - else + if (what[0] != 'm') is_changeset = 1; } else if (!strcmp(what, "changeset-metadata")) { type = OBJ_BLOB; @@ -463,8 +385,6 @@ void do_set_(const char *what, const struct hg_object_id *hg_id, if (is_changeset) handle_changeset_conflict(hg_id, &git_id_); add_note_hg(notes, hg_id, &git_id_); - if (heads) - add_head(heads, &git_id_); } } @@ -635,6 +555,8 @@ static void manifest_metadata_path(struct strbuf *out, struct strslice *in) strbuf_addslice(out, *in); } +extern void add_manifest_head(const struct object_id *manifest); + void store_manifest(struct rev_chunk *chunk, const struct strslice last_manifest_content, struct strslice_mut stored_manifest) @@ -793,7 +715,7 @@ void store_manifest(struct rev_chunk *chunk, strbuf_release(&data); ensure_notes(&hg2git); add_note_hg(&hg2git, &last_manifest_oid, &last_manifest->oid); - add_head(&manifest_heads, &last_manifest->oid); + add_manifest_head(&last_manifest->oid); if ((cinnabar_check(CHECK_MANIFESTS)) && !check_manifest(&last_manifest->oid)) die("sha1 mismatch for node %s", hg_oid_to_hex(chunk->node)); @@ -803,16 +725,8 @@ void store_manifest(struct rev_chunk *chunk, die("Malformed manifest chunk for %s", hg_oid_to_hex(chunk->node)); } -static int add_manifests_parent(const struct object_id *oid, void *data) -{ - struct strbuf *buf = data; - strbuf_addstr(buf, "parent "); - strbuf_addstr(buf, oid_to_hex(oid)); - strbuf_addch(buf, '\n'); - return 0; -} - extern void store_changesets_metadata(struct object_id *result); +extern void store_manifests_metadata(struct object_id *result); void store_metadata_notes( struct notes_tree *notes, const struct object_id *reference, @@ -864,26 +778,7 @@ void do_store_metadata(struct object_id *result) { store_metadata_notes(&hg2git, &hg2git_oid, &hg2git_); store_metadata_notes(&git2hg, &git2hg_oid, &git2hg_); store_metadata_notes(&files_meta, &files_meta_oid, &files_meta_); - - if (manifest_heads_dirty) { - strbuf_addf( - &buf, "tree %s\n", - oid_to_hex(&empty_tree)); - ensure_heads(&manifest_heads); - oid_array_for_each_unique( - &manifest_heads, add_manifests_parent, - &buf); - strbuf_addstr( - &buf, - "author 0 +0000\n" - "committer 0 +0000\n" - "\n"); - store_git_commit(&buf, &manifests); - strbuf_release(&buf); - } else { - oidcpy(&manifests, &manifests_oid); - } - + store_manifests_metadata(&manifests); store_changesets_metadata(&changesets); if (!is_null_oid(&metadata_oid)) { oidcpy(&previous, &metadata_oid); diff --git a/src/cinnabar-fast-import.h b/src/cinnabar-fast-import.h index 32e616ad3..9d8cf5f89 100644 --- a/src/cinnabar-fast-import.h +++ b/src/cinnabar-fast-import.h @@ -26,8 +26,6 @@ void store_git_commit(struct strbuf *commit_buf, struct object_id *result); void store_git_blob(struct strbuf *blob_buf, struct object_id *result); -void add_head(struct oid_array *heads, const struct object_id *oid); - const struct object_id *ensure_empty_blob(void); void do_cleanup(int rollback); diff --git a/src/cinnabar-helper.c b/src/cinnabar-helper.c index d9d227a73..023efbfd1 100644 --- a/src/cinnabar-helper.c +++ b/src/cinnabar-helper.c @@ -23,7 +23,6 @@ #include "string-list.h" #include "streaming.h" #include "object.h" -#include "oid-array.h" #include "oidset.h" #include "path.h" #include "quote.h" @@ -332,19 +331,6 @@ static int merge_tree_entry(struct merge_manifest_tree_state *state, return 1; } -static void reset_heads(struct oid_array *heads) -{ - oid_array_clear(heads); - // We don't want subsequent ensure_heads to refill the array, - // so mark it as sorted, which means it's initialized. - 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, @@ -650,6 +636,7 @@ static void init_metadata(struct commit *c) void dump_ref_updates(void); extern void reset_changeset_heads(void); +extern void reset_manifest_heads(void); void do_reload(struct object_id *oid) { @@ -658,8 +645,6 @@ void do_reload(struct object_id *oid) done_cinnabar(); hashmap_init(&git_tree_cache, oid_map_entry_cmp, NULL, 0); - oid_array_clear(&manifest_heads); - dump_ref_updates(); metadata_flags = 0; @@ -673,6 +658,7 @@ void do_reload(struct object_id *oid) } init_metadata(c); reset_changeset_heads(); + reset_manifest_heads(); } static void init_git_config(void) diff --git a/src/cinnabar-helper.h b/src/cinnabar-helper.h index a06be4c88..b7c1de2ce 100644 --- a/src/cinnabar-helper.h +++ b/src/cinnabar-helper.h @@ -23,10 +23,6 @@ extern int metadata_flags; extern int cinnabar_check(int); -extern struct oid_array manifest_heads; - -void ensure_heads(struct oid_array *heads); - extern struct notes_tree git2hg, hg2git, files_meta; extern void ensure_notes(struct notes_tree *notes); @@ -48,7 +44,6 @@ void create_git_tree(const struct object_id *tree_id, const struct object_id *ref_tree, struct object_id *result); -void reset_manifest_heads(void); void do_reload(struct object_id *); unsigned int replace_map_size(void); diff --git a/src/libgit.rs b/src/libgit.rs index 2f58580b2..2a6ad3550 100644 --- a/src/libgit.rs +++ b/src/libgit.rs @@ -67,6 +67,7 @@ impl From for GitObjectId { extern "C" { pub static mut metadata_oid: object_id; pub static mut changesets_oid: object_id; + pub static mut manifests_oid: object_id; pub static mut git2hg_oid: object_id; } diff --git a/src/main.rs b/src/main.rs index 69fcc0c96..8bcd71f56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,7 +139,7 @@ use util::{CStrExt, IteratorExt, OsStrExt, SliceExt, Transpose}; use crate::hg_bundle::BundleReader; use crate::hg_connect::{decodecaps, find_common, UnbundleResponse}; use crate::progress::set_progress; -use crate::store::{do_set_replace, reset_manifest_heads, set_changeset_heads, Dag, Traversal}; +use crate::store::{clear_manifest_heads, do_set_replace, set_changeset_heads, Dag, Traversal}; use crate::tree_util::{Empty, ParseTree, WithPath}; use crate::util::{FromBytes, ToBoxed}; @@ -3109,7 +3109,7 @@ fn do_fsck_full( } unsafe { - reset_manifest_heads(); + clear_manifest_heads(); for h in manifest_heads { // TODO: This is gross. let m = RawCommit::read(h).unwrap(); diff --git a/src/store.rs b/src/store.rs index 640f3f281..82ef45981 100644 --- a/src/store.rs +++ b/src/store.rs @@ -45,8 +45,8 @@ use crate::libcinnabar::{ files_meta, git2hg, git_notes_tree, hg2git, hg_object_id, strslice, strslice_mut, }; use crate::libgit::{ - changesets_oid, die, get_oid_blob, object_id, strbuf, Commit, RawBlob, RawCommit, RawTree, - RefTransaction, + changesets_oid, die, get_oid_blob, manifests_oid, object_id, strbuf, Commit, RawBlob, + RawCommit, RawTree, RefTransaction, }; use crate::oid::ObjectId; use crate::progress::{progress_enabled, Progress}; @@ -914,6 +914,60 @@ impl ChangesetHeads { pub static CHANGESET_HEADS: Lazy> = Lazy::new(|| Mutex::new(ChangesetHeads::from_stored_metadata())); +#[derive(Debug)] +pub struct ManifestHeads { + heads: BTreeSet, +} + +impl ManifestHeads { + pub fn new() -> Self { + ManifestHeads { + heads: BTreeSet::new(), + } + } + + fn from_stored_metadata() -> Self { + let manifests_cid = + CommitId::from_raw_bytes(unsafe { manifests_oid.as_raw_bytes() }).unwrap(); + if manifests_cid.is_null() { + ManifestHeads::new() + } else { + ManifestHeads::from_metadata(manifests_cid) + } + } + + pub fn from_metadata(cid: CommitId) -> Self { + let mut result = ManifestHeads::new(); + + let commit = RawCommit::read(cid).unwrap(); + let commit = commit.parse().unwrap(); + for p in commit.parents() { + result.heads.insert(GitManifestId::from_unchecked(*p)); + } + result + } + + pub fn add(&mut self, head: GitManifestId) { + let commit = RawCommit::read(head.into()).unwrap(); + let commit = commit.parse().unwrap(); + for p in commit.parents() { + self.heads.remove(&GitManifestId::from_unchecked(*p)); + } + self.heads.insert(head); + } + + pub fn heads(&self) -> impl Iterator { + self.heads.iter() + } + + pub fn is_empty(&self) -> bool { + self.heads.is_empty() + } +} + +pub static MANIFEST_HEADS: Lazy> = + Lazy::new(|| Mutex::new(ManifestHeads::from_stored_metadata())); + #[derive(Default)] pub struct TagSet { tags: IndexMap, (HgChangesetId, HashSet)>, @@ -1037,6 +1091,40 @@ pub unsafe extern "C" fn reset_changeset_heads() { *heads = ChangesetHeads::from_stored_metadata(); } +#[no_mangle] +pub unsafe extern "C" fn store_manifests_metadata(result: *mut object_id) { + let result = result.as_mut().unwrap(); + let mut commit = strbuf::new(); + writeln!(commit, "tree {}", RawTree::EMPTY_OID).ok(); + let heads = MANIFEST_HEADS.lock().unwrap(); + for head in heads.heads() { + writeln!(commit, "parent {}", head).ok(); + } + writeln!(commit, "author 0 +0000").ok(); + writeln!(commit, "committer 0 +0000\n").ok(); + store_git_commit(&commit, result); +} + +#[no_mangle] +pub unsafe extern "C" fn add_manifest_head(mn: *const object_id) { + let mut heads = MANIFEST_HEADS.lock().unwrap(); + heads.add(GitManifestId::from_unchecked(CommitId::from_unchecked( + mn.as_ref().unwrap().clone().into(), + ))); +} + +#[no_mangle] +pub unsafe extern "C" fn reset_manifest_heads() { + let mut heads = MANIFEST_HEADS.lock().unwrap(); + *heads = ManifestHeads::from_stored_metadata(); +} + +#[no_mangle] +pub unsafe extern "C" fn clear_manifest_heads() { + let mut heads = MANIFEST_HEADS.lock().unwrap(); + *heads = ManifestHeads::new(); +} + pub fn set_changeset_heads(new_heads: ChangesetHeads) { let mut heads = CHANGESET_HEADS.lock().unwrap(); *heads = new_heads; @@ -1054,7 +1142,6 @@ extern "C" { ref_tree: *const object_id, result: *mut object_id, ); - pub fn reset_manifest_heads(); } pub enum SetWhat { @@ -1069,7 +1156,17 @@ pub fn do_set(what: SetWhat, hg_id: HgObjectId, git_id: GitObjectId) { let what = match what { SetWhat::Changeset => cstr!("changeset"), SetWhat::ChangesetMeta => cstr!("changeset-metadata"), - SetWhat::Manifest => cstr!("manifest"), + SetWhat::Manifest => { + if !git_id.is_null() { + MANIFEST_HEADS + .lock() + .unwrap() + .add(GitManifestId::from_unchecked(CommitId::from_unchecked( + git_id, + ))); + } + cstr!("manifest") + } SetWhat::File => cstr!("file"), SetWhat::FileMeta => cstr!("file-meta"), };