From d7eac7fd89c1d3e620203fb9cef22243aae0a436 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 10 Nov 2023 07:48:22 +0900 Subject: [PATCH] Move init_metadata to Rust --- src/cinnabar-helper.c | 127 ++-------------------------------------- src/cinnabar-helper.h | 4 ++ src/graft.rs | 1 + src/libgit.rs | 2 +- src/store.rs | 132 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 138 insertions(+), 128 deletions(-) diff --git a/src/cinnabar-helper.c b/src/cinnabar-helper.c index d5fbafb6f..3bb72989b 100644 --- a/src/cinnabar-helper.c +++ b/src/cinnabar-helper.c @@ -450,7 +450,7 @@ void create_git_tree(const struct object_id *tree_id, recurse_create_git_tree(tree_id, ref_tree, NULL, result, &git_tree_cache); } -static void init_replace_map(void) +void init_replace_map(void) { the_repository->objects->replace_map = xmalloc(sizeof(*the_repository->objects->replace_map)); @@ -458,7 +458,7 @@ static void init_replace_map(void) the_repository->objects->replace_map_initialized = 1; } -static void reset_replace_map(void) +void reset_replace_map(void) { oidmap_free(the_repository->objects->replace_map, 1); FREE_AND_NULL(the_repository->objects->replace_map); @@ -470,128 +470,13 @@ 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(struct commit *c) +unsigned int replace_map_tablesize(void) { - 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; - - 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 = repo_get_commit_buffer(the_repository, c, NULL); - body = strstr(msg, "\n\n") + 2; - 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); - repo_unuse_commit_buffer(the_repository, c, msg); - goto old; - } else if (!strcmp("unified-manifests-v2", (*f)->buf)) - metadata_flags |= UNIFIED_MANIFESTS_v2; - else { - strbuf_list_free(flags); - repo_unuse_commit_buffer(the_repository, c, msg); - goto new; - } - } - strbuf_list_free(flags); - repo_unuse_commit_buffer(the_repository, c, msg); - - 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(); - init_replace_map(); - - tree = repo_get_commit_tree(the_repository, 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 -upgrade: - die("Git-cinnabar metadata needs upgrade. " - "Please run `git cinnabar upgrade`."); -#endif + return the_repository->objects->replace_map->map.tablesize; } +extern void init_metadata(struct commit *c); + void dump_ref_updates(void); extern void reset_changeset_heads(void); diff --git a/src/cinnabar-helper.h b/src/cinnabar-helper.h index d7a7a1847..b9eec939c 100644 --- a/src/cinnabar-helper.h +++ b/src/cinnabar-helper.h @@ -44,6 +44,7 @@ void create_git_tree(const struct object_id *tree_id, void do_reload(struct object_id *); unsigned int replace_map_size(void); +unsigned int replace_map_tablesize(void); const struct object_id *repo_lookup_replace_object( struct repository *r, const struct object_id *oid); @@ -88,4 +89,7 @@ int get_worktree_is_detached(const struct worktree *wr); const struct object_id *get_worktree_head_oid(const struct worktree *wr); +void init_replace_map(void); +void reset_replace_map(void); + #endif diff --git a/src/graft.rs b/src/graft.rs index a9acd68ac..8113361b6 100644 --- a/src/graft.rs +++ b/src/graft.rs @@ -19,6 +19,7 @@ use crate::store::{has_metadata, GeneratedGitChangesetMetadata, RawHgChangeset}; extern "C" { fn replace_map_size() -> c_uint; + pub fn replace_map_tablesize() -> c_uint; } pub fn grafted() -> bool { diff --git a/src/libgit.rs b/src/libgit.rs index 987170e5c..e0ab4ae2c 100644 --- a/src/libgit.rs +++ b/src/libgit.rs @@ -551,7 +551,7 @@ pub struct commit(c_void); pub struct commit_list(c_void); extern "C" { - fn commit_oid(c: *const commit) -> *const object_id; + pub fn commit_oid(c: *const commit) -> *const object_id; fn get_revision(revs: *mut rev_info) -> *const commit; diff --git a/src/store.rs b/src/store.rs index bef185ee5..62bf60328 100644 --- a/src/store.rs +++ b/src/store.rs @@ -18,6 +18,7 @@ use std::sync::Mutex; use bit_vec::BitVec; use bstr::{BStr, BString, ByteSlice}; use derive_more::Deref; +use either::Either; use getset::{CopyGetters, Getters}; use hex_literal::hex; use indexmap::IndexMap; @@ -32,8 +33,8 @@ use crate::cinnabar::{ GitChangesetId, GitChangesetMetadataId, GitFileId, GitFileMetadataId, GitManifestId, GitManifestTree, GitManifestTreeId, }; -use crate::git::{BlobId, CommitId, GitObjectId, TreeId, TreeIsh}; -use crate::graft::{graft, grafted, GraftError}; +use crate::git::{BlobId, CommitId, GitObjectId, GitOid, RecursedTreeEntry, TreeId, TreeIsh}; +use crate::graft::{graft, grafted, replace_map_tablesize, GraftError}; use crate::hg::{HgChangesetId, HgFileId, HgManifestId, HgObjectId}; use crate::hg_bundle::{ read_rev_chunk, rev_chunk, BundlePartInfo, BundleSpec, BundleWriter, RevChunkIter, @@ -45,12 +46,13 @@ use crate::libcinnabar::{ strslice_mut, }; use crate::libgit::{ - changesets_oid, die, files_meta_oid, get_oid_blob, git2hg_oid, hg2git_oid, manifests_oid, - metadata_oid, object_id, strbuf, Commit, RawBlob, RawCommit, RawTree, RefTransaction, + changesets_oid, commit, commit_oid, die, files_meta_oid, for_each_ref_in, get_oid_blob, + git2hg_oid, hg2git_oid, manifests_oid, metadata_oid, object_id, strbuf, Commit, RawBlob, + RawCommit, RawTree, RefTransaction, }; use crate::oid::ObjectId; use crate::progress::{progress_enabled, Progress}; -use crate::tree_util::{diff_by_path, Empty, ParseTree, RecurseTree}; +use crate::tree_util::{diff_by_path, Empty, ParseTree, RecurseTree, WithPath}; use crate::util::{ FromBytes, ImmutBString, OsStrExt, RcExt, ReadExt, SliceExt, ToBoxed, Transpose, }; @@ -65,10 +67,11 @@ pub const BROKEN_REF: &str = "refs/cinnabar/broken"; pub const NOTES_REF: &str = "refs/notes/cinnabar"; extern "C" { - pub static metadata_flags: c_int; + pub static mut metadata_flags: c_int; } pub const FILES_META: c_int = 0x1; +pub const UNIFIED_MANIFESTS_V2: c_int = 0x2; pub fn has_metadata() -> bool { unsafe { metadata_flags != 0 } @@ -2034,9 +2037,126 @@ pub fn merge_metadata(git_url: Url, hg_url: Option, branch: Option<&[u8]>) } extern "C" { + fn init_replace_map(); + fn reset_replace_map(); fn store_replace_map(result: *mut object_id); } +fn old_metadata() { + 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." + ); +} + +fn new_metadata() { + die!( + "It looks like this repository was used with a newer version of git-cinnabar. \ + Cannot use this version." + ); +} + +#[allow(dead_code)] +fn need_upgrade() { + die!("Git-cinnabar metadata needs upgrade. Please run `git cinnabar upgrade` first."); +} + +#[no_mangle] +pub unsafe extern "C" fn init_metadata(c: *const commit) { + let cid = if let Some(c) = c.as_ref() { + CommitId::from_unchecked(GitObjectId::from(commit_oid(c).as_ref().unwrap().clone())) + } else { + metadata_oid = object_id::default(); + changesets_oid = object_id::default(); + manifests_oid = object_id::default(); + hg2git_oid = object_id::default(); + git2hg_oid = object_id::default(); + files_meta_oid = object_id::default(); + return; + }; + let c = RawCommit::read(cid).unwrap(); + let c = c.parse().unwrap(); + if !(5..=6).contains(&c.parents().len()) { + die!("Invalid metadata?"); + } + for (cid, field) in Some(cid).iter().chain(c.parents()[..5].iter()).zip([ + &mut metadata_oid, + &mut changesets_oid, + &mut manifests_oid, + &mut hg2git_oid, + &mut git2hg_oid, + &mut files_meta_oid, + ]) { + *field = (*cid).into(); + } + for flag in c.body().split(|&b| b == b' ') { + match flag { + b"files-meta" => { + metadata_flags |= FILES_META; + } + b"unified-manifests" => old_metadata(), + b"unified-manifests-v2" => { + metadata_flags |= UNIFIED_MANIFESTS_V2; + } + _ => new_metadata(), + } + } + if metadata_flags != FILES_META | UNIFIED_MANIFESTS_V2 { + old_metadata(); + } + let mut count = 0; + for_each_ref_in("refs/cinnabar/branches/", |_, _| -> Result<(), ()> { + count += 1; + Ok(()) + }) + .ok(); + if count > 0 { + old_metadata(); + } + + reset_replace_map(); + + let tree = RawTree::read(c.tree()).unwrap(); + let mut replaces = BTreeMap::new(); + for (path, oid) in tree.into_iter().map(WithPath::unzip) { + match oid { + Either::Right(RecursedTreeEntry { + oid: GitOid::Commit(replace_with), + .. + }) => { + if let Ok(original) = CommitId::from_bytes(&path) { + if original == replace_with { + warn!("self-referencing graft: {}", original); + } else { + replaces + .entry(original) + .and_modify(|_| die!("duplicate replace: {}", original)) + .or_insert_with(|| replace_with); + } + } else { + warn!("bad replace name: {}", path.as_bstr()); + } + } + _ => die!("Invalid metadata"), + } + } + init_replace_map(); + for (original, replace_with) in replaces.into_iter() { + do_set_replace(&original.into(), &replace_with.into()); + } + if replace_map_tablesize() == 0 { + let mut count = 0; + for_each_ref_in("refs/cinnabar/replace/", |_, _| -> Result<(), ()> { + count += 1; + Ok(()) + }) + .ok(); + if count > 0 { + old_metadata(); + } + } +} + pub fn do_store_metadata() -> CommitId { let mut hg2git_ = object_id::default(); let mut git2hg_ = object_id::default();