diff --git a/src/cinnabar-fast-import.c b/src/cinnabar-fast-import.c index 3500a7a8e..da452b34f 100644 --- a/src/cinnabar-fast-import.c +++ b/src/cinnabar-fast-import.c @@ -285,74 +285,6 @@ int write_object_file_flags(const void *buf, size_t len, enum object_type type, return 0; } -void hg_file_store(struct hg_file *file, struct hg_file *reference) -{ - struct object_id oid; - struct strslice *last_blob = NULL; - struct object_entry *oe = NULL; - - if (file->metadata.buf) { - store_git_object(OBJ_BLOB, file->metadata, &oid, NULL, NULL); - add_files_meta(&file->oid, &oid); - } - - if (reference) { - last_blob = &reference->content; - oe = reference->content_oe; - } - - store_git_object(OBJ_BLOB, file->content, &oid, last_blob, oe); - add_hg2git(&file->oid, &oid); - - file->content_oe = get_object_entry(&oid); -} - -void store_file(struct rev_chunk *chunk) -{ - static struct hg_file last_file; - struct hg_file file; - struct strbuf data = STRBUF_INIT; - struct strslice diff; - struct rev_diff_part part; - size_t last_end = 0; - - if (is_empty_hg_file(chunk->node)) - return; - - if (!hg_oideq(chunk->delta_node, &last_file.oid)) { - hg_file_release(&last_file); - - if (!is_null_hg_oid(chunk->delta_node)) - hg_file_load(&last_file, chunk->delta_node); - - } - - rev_diff_start_iter(&diff, chunk); - while (rev_diff_iter_next(&diff, &part)) { - if (part.start > last_file.file.len || part.start < last_end) - die("Malformed file chunk for %s", - hg_oid_to_hex(chunk->node)); - strbuf_add(&data, last_file.file.buf + last_end, - part.start - last_end); - strbuf_addslice(&data, part.data); - - last_end = part.end; - } - - if (last_file.file.len < last_end) - die("Malformed file chunk for %s", hg_oid_to_hex(chunk->node)); - - strbuf_add(&data, last_file.file.buf + last_end, - last_file.file.len - last_end); - - hg_file_init(&file); - hg_file_from_memory(&file, chunk->node, &data); - - hg_file_store(&file, &last_file); - hg_file_swap(&file, &last_file); - hg_file_release(&file); -} - struct manifest_line { struct strslice path; struct hg_object_id oid; diff --git a/src/cinnabar-fast-import.h b/src/cinnabar-fast-import.h index 49336eb88..782ad0dbe 100644 --- a/src/cinnabar-fast-import.h +++ b/src/cinnabar-fast-import.h @@ -41,7 +41,6 @@ void do_set_replace(const struct object_id *replaced, void do_set_(const char *what, const struct hg_object_id *hg_id, const struct object_id *git_id); -void store_file(struct rev_chunk *chunk); void store_manifest(struct rev_chunk *chunk, const struct strslice last_manifest_content, struct strslice_mut data); diff --git a/src/hg-data.c b/src/hg-data.c index 049862e2b..bc177b24f 100644 --- a/src/hg-data.c +++ b/src/hg-data.c @@ -3,10 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "git-compat-util.h" -#include "object-store.h" -#include "cinnabar-helper.h" -#include "cinnabar-notes.h" -#include "cinnabar-fast-import.h" #include "hg-data.h" static const struct hg_object_id empty_hg_file = {{ @@ -25,96 +21,3 @@ int is_empty_hg_file(const struct hg_object_id *oid) { return hg_oideq(&empty_hg_file, oid); } - -static void _hg_file_split(struct hg_file *result, size_t metadata_len) -{ - result->metadata.buf = metadata_len ? result->file.buf + 2 : NULL; - result->metadata.len = metadata_len - 4; - result->content.buf = result->file.buf + metadata_len; - result->content.len = result->file.len - metadata_len; -} - -void hg_file_load(struct hg_file *result, const struct hg_object_id *oid) -{ - const struct object_id *note; - struct object_info oi = OBJECT_INFO_INIT; - char *content; - enum object_type type; - unsigned long len; - size_t metadata_len; - - oi.typep = &type; - oi.sizep = &len; - oi.contentp = (void **) &content; - - strbuf_release(&result->file); - hg_oidcpy(&result->oid, oid); - - if (is_empty_hg_file(oid)) - return; - - note = get_files_meta(oid); - if (note) { - if (oid_object_info_extended( - the_repository, note, &oi, - OBJECT_INFO_DIE_IF_CORRUPT) != 0) - die("Missing data"); - strbuf_add(&result->file, "\1\n", 2); - strbuf_add(&result->file, content, len); - strbuf_add(&result->file, "\1\n", 2); - free(content); - } - - metadata_len = result->file.len; - - note = resolve_hg2git(oid); - if (!note) - die("Missing data"); - - if (oid_object_info_extended( - the_repository, note, &oi, - OBJECT_INFO_DIE_IF_CORRUPT) != 0) - die("Missing data"); - - strbuf_add(&result->file, content, len); - free(content); - - // Note this duplicates work read_object_file already did. - result->content_oe = get_object_entry(note); - - _hg_file_split(result, metadata_len); -} - -void hg_file_from_memory(struct hg_file *result, - const struct hg_object_id *oid, struct strbuf *buf) -{ - size_t metadata_len = 0; - - strbuf_swap(&result->file, buf); - hg_oidcpy(&result->oid, oid); - result->content_oe = NULL; - - if (result->file.len > 4 && memcmp(result->file.buf, "\1\n", 2) == 0) { - char *metadata_end = strstr(result->file.buf + 2, "\1\n"); - if (metadata_end) - metadata_len = metadata_end + 2 - result->file.buf; - } - - _hg_file_split(result, metadata_len); -} - -void hg_file_init(struct hg_file *file) -{ - hg_oidclr(&file->oid); - strbuf_init(&file->file, 0); - file->metadata.buf = NULL; - file->metadata.len = 0; - file->content = strbuf_as_slice(&file->file); - file->content_oe = NULL; -} - -void hg_file_release(struct hg_file *file) -{ - strbuf_release(&file->file); - hg_file_init(file); -} diff --git a/src/hg-data.h b/src/hg-data.h index 885fffffa..decf205b3 100644 --- a/src/hg-data.h +++ b/src/hg-data.h @@ -64,34 +64,6 @@ int is_null_hg_oid(const struct hg_object_id *oid); int is_empty_hg_file(const struct hg_object_id *oid); -struct hg_file { - struct hg_object_id oid; - - struct strbuf file; - struct strslice metadata; - struct strslice content; - struct object_entry *content_oe; -}; - -void hg_file_load(struct hg_file *result, const struct hg_object_id *oid); - -void hg_file_from_memory(struct hg_file *result, - const struct hg_object_id *oid, struct strbuf *buf); - -static inline void hg_file_swap(struct hg_file *a, struct hg_file *b) -{ - SWAP(*a, *b); -} - -void hg_file_init(struct hg_file *file); - -void hg_file_release(struct hg_file *file); - -void hg_file_store(struct hg_file *file, struct hg_file *reference); - void add_hg2git(const struct hg_object_id *oid, const struct object_id *note_oid); -void add_files_meta(const struct hg_object_id *oid, const struct object_id *note_oid); - -const struct object_id *get_files_meta(const struct hg_object_id *oid); #endif diff --git a/src/libcinnabar.rs b/src/libcinnabar.rs index 5c90e1436..a5bc472d0 100644 --- a/src/libcinnabar.rs +++ b/src/libcinnabar.rs @@ -204,42 +204,20 @@ fn for_each_note_in(notes: &mut cinnabar_not #[no_mangle] pub unsafe extern "C" fn resolve_hg2git(oid: *const hg_object_id) -> *const object_id { - get_note_hg(METADATA.hg2git_mut(), oid) -} - -unsafe fn get_note_hg(notes: &mut hg_notes_tree, oid: *const hg_object_id) -> *const object_id { let git_oid = GitObjectId::from_raw_bytes(HgObjectId::from(oid.as_ref().unwrap().clone()).as_raw_bytes()) .unwrap(); - cinnabar_get_note(&mut notes.0, &git_oid.into()) + cinnabar_get_note(&mut METADATA.hg2git_mut().0, &git_oid.into()) } #[no_mangle] -pub unsafe extern "C" fn get_files_meta(oid: *const hg_object_id) -> *const object_id { - get_note_hg(METADATA.files_meta_mut(), oid) -} - -unsafe fn add_note_hg( - notes: &mut hg_notes_tree, - oid: *const hg_object_id, - note_oid: *const object_id, -) { - notes.add_note( +pub unsafe extern "C" fn add_hg2git(oid: *const hg_object_id, note_oid: *const object_id) { + METADATA.hg2git_mut().add_note( HgObjectId::from(oid.as_ref().unwrap().clone()), note_oid.as_ref().unwrap().clone().into(), ); } -#[no_mangle] -pub unsafe extern "C" fn add_hg2git(oid: *const hg_object_id, note_oid: *const object_id) { - add_note_hg(METADATA.hg2git_mut(), oid, note_oid); -} - -#[no_mangle] -pub unsafe extern "C" fn add_files_meta(oid: *const hg_object_id, note_oid: *const object_id) { - add_note_hg(METADATA.files_meta_mut(), oid, note_oid); -} - pub fn store_metadata_notes(notes: &mut cinnabar_notes_tree, reference: CommitId) -> CommitId { let mut result = object_id::default(); let mut tree = object_id::default(); diff --git a/src/libgit.rs b/src/libgit.rs index 2aaf6990e..87f7a293c 100644 --- a/src/libgit.rs +++ b/src/libgit.rs @@ -163,6 +163,11 @@ pub struct remote(c_void); #[repr(transparent)] pub struct child_process(c_void); + +#[allow(non_camel_case_types)] +#[repr(transparent)] +pub struct object_entry(c_void); + #[allow(non_camel_case_types)] #[repr(C)] pub struct active_request_slot { diff --git a/src/store.rs b/src/store.rs index bae3d8964..b1379db8e 100644 --- a/src/store.rs +++ b/src/store.rs @@ -43,8 +43,8 @@ use crate::hg_connect_http::HttpRequest; use crate::hg_data::{hash_data, GitAuthorship, HgAuthorship, HgCommitter}; use crate::libcinnabar::{git_notes_tree, hg_notes_tree, strslice, strslice_mut}; use crate::libgit::{ - die, for_each_ref_in, get_oid_blob, object_id, strbuf, Commit, RawBlob, RawCommit, RawTree, - RefTransaction, + die, for_each_ref_in, get_oid_blob, object_entry, object_id, object_type, strbuf, Commit, + RawBlob, RawCommit, RawTree, RefTransaction, }; use crate::oid::ObjectId; use crate::progress::{progress_enabled, Progress}; @@ -1208,12 +1208,20 @@ extern "C" { pub fn store_git_blob(blob_buf: strslice, result: *mut object_id); fn store_git_tree(tree_buf: strslice, reference: *const object_id, result: *mut object_id); pub fn store_git_commit(commit_buf: strslice, result: *mut object_id); + fn store_git_object( + typ: object_type, + buf: strslice, + result: *mut object_id, + reference: *const strslice, + reference_entry: *const object_entry, + ); pub fn do_set_replace(replaced: *const object_id, replace_with: *const object_id); fn create_git_tree( tree_id: *const object_id, ref_tree: *const object_id, result: *mut object_id, ); + fn get_object_entry(oid: *const object_id) -> *const object_entry; } pub enum SetWhat { @@ -1561,7 +1569,6 @@ pub fn create_changeset( #[allow(improper_ctypes)] extern "C" { pub fn store_manifest(chunk: *const rev_chunk, reference_mn: strslice, stored_mn: strslice_mut); - fn store_file(chunk: *const rev_chunk); } #[no_mangle] @@ -1725,8 +1732,10 @@ pub fn store_changegroup(metadata: &mut Metadata, input: R, version: u8 !buf.is_empty() } { files.set(files.get() + 1); + let mut previous_file = None; for (file, ()) in RevChunkIter::new(version, &mut input).zip(&mut progress) { let node = HgFileId::from_unchecked(file.node()); + let delta_node = HgFileId::from_unchecked(file.delta_node()); let parents = [ HgFileId::from_unchecked(file.parent1()), HgFileId::from_unchecked(file.parent2()), @@ -1755,9 +1764,77 @@ pub fn store_changegroup(metadata: &mut Metadata, input: R, version: u8 } } } + if node == RawHgFile::EMPTY_OID { + // Creating the empty blob is handled when creating the git tree for + // the corresponding changeset. We have nothing to associate the blob + // with here. + continue; + } + let reference_file = previous_file + .take() + .and_then(|(fid, file)| (fid == delta_node).then_some(file)) + .unwrap_or_else(|| { + RawHgFile::read_hg(if delta_node.is_null() { + RawHgFile::EMPTY_OID + } else { + delta_node + }) + .unwrap() + }); + + let mut raw_file = Vec::new(); + let mut last_end = 0; + for diff in file.iter_diff() { + if diff.start() > reference_file.len() || diff.start() < last_end { + die!("Malformed file chunk for {node}"); + } + raw_file.extend_from_slice(&reference_file[last_end..diff.start()]); + raw_file.extend_from_slice(diff.data()); + last_end = diff.end(); + } + if reference_file.len() < last_end { + die!("Malformed file chunk for {node}"); + } + raw_file.extend_from_slice(&reference_file[last_end..]); + let mut content = &raw_file[..]; + if content.starts_with(b"\x01\n") { + let [file_metadata, file_content] = + content[2..].splitn_exact(&b"\x01\n"[..]).unwrap(); + unsafe { + let mut metadata_oid = object_id::default(); + store_git_blob(file_metadata.into(), &mut metadata_oid); + metadata + .files_meta_mut() + .add_note(node.into(), metadata_oid.into()); + } + content = file_content; + } unsafe { - store_file(&file.into()); + let mut file_oid = object_id::default(); + if let Some(reference_entry) = (!delta_node.is_null()) + .then(|| { + get_object_entry( + &GitObjectId::from_raw_bytes(delta_node.as_raw_bytes()) + .unwrap() + .into(), + ) + .as_ref() + }) + .flatten() + { + store_git_object( + object_type::OBJ_BLOB, + content.into(), + &mut file_oid, + &reference_file[..].into(), + reference_entry, + ); + } else { + store_git_blob(content.into(), &mut file_oid); + }; + metadata.hg2git_mut().add_note(node.into(), file_oid.into()); } + previous_file = Some((node, RawHgFile(raw_file.into()))); } } drop(progress);