mirror of
https://github.com/Byron/gitoxide
synced 2025-10-06 01:52:40 +02:00
726 lines
25 KiB
Rust
726 lines
25 KiB
Rust
use crate::util::named_subrepo_opts;
|
|
use gix_testtools::tempfile;
|
|
|
|
mod object_database_impl {
|
|
use gix_object::{Exists, Find, FindHeader};
|
|
|
|
#[test]
|
|
fn empty_tree_is_always_present() -> crate::Result {
|
|
let repo = crate::named_subrepo_opts("make_basic_repo.sh", "unborn", gix::open::Options::isolated())?;
|
|
let empty_tree = gix::ObjectId::empty_tree(repo.object_hash());
|
|
assert!(repo.exists(&empty_tree));
|
|
assert_eq!(
|
|
repo.try_header(&empty_tree)?.expect("tree present"),
|
|
gix_object::Header {
|
|
kind: gix_object::Kind::Tree,
|
|
size: 0
|
|
}
|
|
);
|
|
let mut buf = repo.empty_reusable_buffer();
|
|
buf.push(42);
|
|
assert_eq!(
|
|
repo.try_find(&empty_tree, &mut buf)?.expect("tree present").kind,
|
|
gix_object::Kind::Tree
|
|
);
|
|
assert_eq!(buf.len(), 0, "the data in the buffer matches the empty tree");
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "tree-editor")]
|
|
mod edit_tree {
|
|
use crate::util::hex_to_id;
|
|
use gix::bstr::{BStr, BString};
|
|
use gix_object::tree::EntryKind;
|
|
|
|
#[test]
|
|
// Some part of the test validation the implementation for this exists, but it's needless nonetheless.
|
|
#[allow(clippy::needless_borrows_for_generic_args)]
|
|
fn from_head_tree() -> crate::Result {
|
|
let (repo, _tmp) = crate::repo_rw("make_packed_and_loose.sh")?;
|
|
let head_tree_id = repo.head_tree_id()?;
|
|
assert_eq!(
|
|
display_tree(head_tree_id, &repo),
|
|
"24374df94315568adfaee119d038f710d1f45397
|
|
├── that ce013625030ba8dba906f756967f9e9ca394464a.100644
|
|
└── this 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
"
|
|
);
|
|
let this_id = hex_to_id("317e9677c3bcffd006f9fc84bbb0a54ef1676197");
|
|
let that_id = hex_to_id("ce013625030ba8dba906f756967f9e9ca394464a");
|
|
let mut editor = repo.edit_tree(head_tree_id)?;
|
|
let actual = editor
|
|
.upsert("a/b", EntryKind::Blob, this_id)?
|
|
.upsert(String::from("this/subdir/that"), EntryKind::Blob, this_id)?
|
|
.upsert(BString::from("that/other/that"), EntryKind::Blob, that_id)?
|
|
.remove(BStr::new("that"))?
|
|
.remove(&String::from("that"))?
|
|
.remove(&BString::from("that"))?
|
|
.write()?;
|
|
|
|
assert_eq!(
|
|
display_tree(actual, &repo),
|
|
"fe02a8bd15e4c0476d938f772f1eece6d164b1bd
|
|
├── a
|
|
│ └── b 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
└── this
|
|
└── subdir
|
|
└── that 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
",
|
|
"all trees are actually written, or else we couldn't visualize them."
|
|
);
|
|
|
|
let actual = editor
|
|
.upsert("a/b", EntryKind::Blob, that_id)?
|
|
.upsert(String::from("this/subdir/that"), EntryKind::Blob, this_id)?
|
|
.remove(BStr::new("does-not-exist"))?
|
|
.write()?;
|
|
assert_eq!(
|
|
display_tree(actual, &repo),
|
|
"219596ff52fc84b6b39bc327f202d408cc02e1db
|
|
├── a
|
|
│ └── b ce013625030ba8dba906f756967f9e9ca394464a.100644
|
|
└── this
|
|
└── subdir
|
|
└── that 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
",
|
|
"existing blobs can also be changed"
|
|
);
|
|
|
|
let mut cursor = editor.cursor_at("something/very/nested/to/add/entries/to")?;
|
|
let actual = cursor
|
|
.upsert("a/b", EntryKind::Blob, this_id)?
|
|
.upsert(String::from("this/subdir/that"), EntryKind::Blob, that_id)?
|
|
.upsert(BString::from("that/other/that"), EntryKind::Blob, that_id)?
|
|
.remove(BStr::new("that"))?
|
|
.write()?;
|
|
|
|
assert_eq!(
|
|
display_tree(actual, &repo),
|
|
"35ea623106198f21b6959dd2731740e5153db2bb
|
|
├── a
|
|
│ └── b 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
└── this
|
|
└── subdir
|
|
└── that ce013625030ba8dba906f756967f9e9ca394464a.100644
|
|
",
|
|
"all remaining subtrees are written from the cursor position"
|
|
);
|
|
|
|
let actual = editor.write()?;
|
|
assert_eq!(
|
|
display_tree(actual, &repo),
|
|
"9ebdc2c1d22e91636fa876a51521464f8a88dd6f
|
|
├── a
|
|
│ └── b ce013625030ba8dba906f756967f9e9ca394464a.100644
|
|
├── something
|
|
│ └── very
|
|
│ └── nested
|
|
│ └── to
|
|
│ └── add
|
|
│ └── entries
|
|
│ └── to
|
|
│ ├── a
|
|
│ │ └── b 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
│ └── this
|
|
│ └── subdir
|
|
│ └── that ce013625030ba8dba906f756967f9e9ca394464a.100644
|
|
└── this
|
|
└── subdir
|
|
└── that 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
",
|
|
"it looks as it should when seen from the root tree"
|
|
);
|
|
|
|
editor.set_root(&head_tree_id.object()?.into_tree())?;
|
|
let actual = editor.write()?;
|
|
assert_eq!(
|
|
display_tree(actual, &repo),
|
|
"24374df94315568adfaee119d038f710d1f45397
|
|
├── that ce013625030ba8dba906f756967f9e9ca394464a.100644
|
|
└── this 317e9677c3bcffd006f9fc84bbb0a54ef1676197.100644
|
|
",
|
|
"it's possible to set the editor to any tree after creating it, could help with memory re-use"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn submodules_are_not_checked_for_existence() -> crate::Result {
|
|
let repo = crate::named_subrepo_opts("make_submodules.sh", "with-submodules", gix::open::Options::isolated())?
|
|
.with_object_memory();
|
|
let mut editor = repo.head_tree()?.edit()?;
|
|
let actual = editor.write()?;
|
|
assert_eq!(
|
|
actual,
|
|
repo.head_tree_id()?,
|
|
"Nothing changed, but it did validate the root tree that it would want to write"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn missing_objects_and_illformed_path_components_trigger_error() -> crate::Result {
|
|
let (repo, _tmp) = crate::repo_rw("make_packed_and_loose.sh")?;
|
|
let tree = repo.head_tree_id()?.object()?.into_tree();
|
|
let mut editor = tree.edit()?;
|
|
let actual = editor
|
|
.upsert("non-existing", EntryKind::Blob, repo.object_hash().null())?
|
|
.write()?;
|
|
assert_eq!(
|
|
actual,
|
|
tree.id(),
|
|
"nulls are pruned before writing the tree, so it just rewrites the same tree"
|
|
);
|
|
|
|
let err = editor
|
|
.upsert(
|
|
"non-existing",
|
|
EntryKind::Blob,
|
|
hex_to_id("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
|
|
)?
|
|
.write()
|
|
.unwrap_err();
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"The object aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa (100644) at 'non-existing' could not be found",
|
|
"each entry to be written is checked for existence"
|
|
);
|
|
|
|
let this_id = hex_to_id("317e9677c3bcffd006f9fc84bbb0a54ef1676197");
|
|
let err = editor
|
|
.remove("non-existing")?
|
|
.upsert(".git", EntryKind::Blob, this_id)?
|
|
.write()
|
|
.expect_err(".git is universally forbidden in trees");
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"The object 317e9677c3bcffd006f9fc84bbb0a54ef1676197 (100644) has an invalid filename: '.git'",
|
|
"each component is validated"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
mod utils {
|
|
use gix::bstr::{BStr, ByteSlice};
|
|
use gix::Repository;
|
|
use gix_hash::ObjectId;
|
|
|
|
fn display_tree_recursive(
|
|
tree_id: ObjectId,
|
|
repo: &Repository,
|
|
name: Option<&BStr>,
|
|
) -> anyhow::Result<termtree::Tree<String>> {
|
|
let tree = repo.find_tree(tree_id)?.decode()?.to_owned();
|
|
let mut termtree = termtree::Tree::new(if let Some(name) = name {
|
|
if tree.entries.is_empty() {
|
|
format!("{name} (empty)")
|
|
} else {
|
|
name.to_string()
|
|
}
|
|
} else {
|
|
tree_id.to_string()
|
|
});
|
|
|
|
for entry in &tree.entries {
|
|
if entry.mode.is_tree() {
|
|
termtree.push(display_tree_recursive(entry.oid, repo, Some(entry.filename.as_bstr()))?);
|
|
} else {
|
|
termtree.push(format!(
|
|
"{} {}.{}",
|
|
entry.filename,
|
|
entry.oid,
|
|
entry.mode.kind().as_octal_str()
|
|
));
|
|
}
|
|
}
|
|
Ok(termtree)
|
|
}
|
|
|
|
pub(super) fn display_tree(tree_id: impl Into<ObjectId>, odb: &Repository) -> String {
|
|
display_tree_recursive(tree_id.into(), odb, None)
|
|
.expect("tree exists and everything was written")
|
|
.to_string()
|
|
}
|
|
}
|
|
use utils::display_tree;
|
|
}
|
|
mod write_object {
|
|
use crate::repository::object::empty_bare_in_memory_repo;
|
|
|
|
#[test]
|
|
fn empty_tree() -> crate::Result {
|
|
let repo = empty_bare_in_memory_repo()?;
|
|
let oid = repo.write_object(gix::objs::TreeRef::empty())?;
|
|
assert_eq!(
|
|
oid,
|
|
gix::hash::ObjectId::empty_tree(repo.object_hash()),
|
|
"it produces a well-known empty tree id"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn commit_with_invalid_author() -> crate::Result {
|
|
let repo = empty_bare_in_memory_repo()?;
|
|
let actor = gix::actor::Signature {
|
|
name: "1 < 0".into(),
|
|
email: Default::default(),
|
|
time: Default::default(),
|
|
};
|
|
let commit = gix::objs::Commit {
|
|
tree: gix::hash::ObjectId::empty_tree(repo.object_hash()),
|
|
author: actor.clone(),
|
|
committer: actor,
|
|
parents: Default::default(),
|
|
encoding: None,
|
|
message: Default::default(),
|
|
extra_headers: vec![],
|
|
};
|
|
assert_eq!(
|
|
repo.write_object(commit).unwrap_err().to_string(),
|
|
r"Signature name or email must not contain '<', '>' or \n",
|
|
"the actor is invalid so triggers an error when persisting it"
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
mod write_blob {
|
|
use std::io::{Seek, SeekFrom};
|
|
|
|
use crate::repository::object::empty_bare_repo;
|
|
use crate::{repository::object::empty_bare_in_memory_repo, util::hex_to_id};
|
|
|
|
#[test]
|
|
fn from_slice() -> crate::Result {
|
|
let (_tmp, repo) = empty_bare_repo()?;
|
|
let expected = hex_to_id("95d09f2b10159347eece71399a7e2e907ea3df4f");
|
|
assert!(!repo.has_object(expected));
|
|
|
|
let oid = repo.write_blob(b"hello world")?;
|
|
assert_eq!(oid, expected);
|
|
|
|
let mut other_repo = gix::open_opts(repo.path(), gix::open::Options::isolated())?;
|
|
other_repo.objects.enable_object_memory();
|
|
assert!(
|
|
other_repo.has_object(oid),
|
|
"we definitely don't accidentally write to memory only"
|
|
);
|
|
let in_memory_id = other_repo.write_blob("hello world - to memory")?;
|
|
assert!(!repo.has_object(in_memory_id), "the object was never written to disk…");
|
|
assert!(
|
|
other_repo.has_object(in_memory_id),
|
|
"…and exists only in the instance that wrote it"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn from_stream() -> crate::Result {
|
|
let repo = empty_bare_in_memory_repo()?;
|
|
let mut cursor = std::io::Cursor::new(b"hello world");
|
|
let mut seek_cursor = cursor.clone();
|
|
let mut repo = repo.without_freelist();
|
|
let oid = repo.write_blob_stream(&mut cursor)?;
|
|
assert_eq!(oid, hex_to_id("95d09f2b10159347eece71399a7e2e907ea3df4f"));
|
|
|
|
seek_cursor.seek(SeekFrom::Start(6))?;
|
|
let oid = repo.write_blob_stream(&mut seek_cursor)?;
|
|
assert_eq!(
|
|
oid,
|
|
hex_to_id("04fea06420ca60892f73becee3614f6d023a4b7f"),
|
|
"it computes the object size correctly"
|
|
);
|
|
|
|
assert_eq!(
|
|
oid.object()?.data,
|
|
&b"world"[..],
|
|
"the seek position is taken into account, so only part of the input data is written"
|
|
);
|
|
|
|
assert!(repo.set_freelist(None).is_none(), "previous list was already dropped");
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn writes_avoid_io_using_duplicate_check() -> crate::Result {
|
|
let mut repo = crate::named_repo("make_packed_and_loose.sh")?;
|
|
let store = gix::odb::loose::Store::at(repo.git_dir().join("objects"), repo.object_hash());
|
|
let loose_count = store.iter().count();
|
|
assert_eq!(loose_count, 3, "there are some loose objects");
|
|
assert_eq!(
|
|
repo.objects.iter()?.count() - loose_count,
|
|
6,
|
|
"there is packed objects as well"
|
|
);
|
|
|
|
for id in repo.objects.iter()? {
|
|
let id = id?;
|
|
assert!(repo.has_object(id));
|
|
let obj = repo.find_object(id)?;
|
|
let header = repo.find_header(id)?;
|
|
assert_eq!(obj.kind, header.kind(), "header and object agree");
|
|
assert_eq!(repo.try_find_header(id)?, Some(header));
|
|
use gix_object::Kind::*;
|
|
match obj.kind {
|
|
Commit => {
|
|
let commit = obj.into_commit();
|
|
let new_id = repo.write_object(commit.decode()?)?;
|
|
assert_eq!(new_id, id);
|
|
}
|
|
Tag => {
|
|
let tag = obj.into_tag();
|
|
let new_id = repo.write_object(tag.decode()?)?;
|
|
assert_eq!(new_id, id);
|
|
}
|
|
Tree => {
|
|
let tree = obj.into_tree();
|
|
let new_id = repo.write_object(tree.decode()?)?;
|
|
assert_eq!(new_id, id);
|
|
}
|
|
Blob => {
|
|
let blob = obj.into_blob();
|
|
let new_id = repo.write_blob(&blob.data)?;
|
|
assert_eq!(new_id, id);
|
|
let new_id = repo.write_blob_stream(std::io::Cursor::new(&blob.data))?;
|
|
assert_eq!(new_id, id);
|
|
}
|
|
}
|
|
}
|
|
|
|
assert_eq!(
|
|
store.iter().count(),
|
|
loose_count,
|
|
"no new object was written as all of them already existed"
|
|
);
|
|
|
|
{
|
|
let buf = repo.empty_reusable_buffer();
|
|
assert!(buf.is_empty(), "the freelist buffer must be clearerd");
|
|
let mut other_buf = buf.clone();
|
|
other_buf.inner = Vec::new();
|
|
}
|
|
|
|
let freelist = repo.set_freelist(None).expect("free list is present by default");
|
|
assert_eq!(
|
|
freelist.len(),
|
|
2,
|
|
"only one object was read at a time, and one is written"
|
|
);
|
|
|
|
let mut repo_clone = repo.clone();
|
|
assert!(
|
|
repo_clone.set_freelist(None).is_none(),
|
|
"new instances inherit the free-list configuration of their parent"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
mod find {
|
|
use gix_pack::Find;
|
|
|
|
use crate::basic_repo;
|
|
|
|
#[test]
|
|
fn find_and_try_find_with_and_without_object_cache() -> crate::Result {
|
|
let mut repo = basic_repo()?;
|
|
|
|
assert_eq!(
|
|
repo.worktrees()?.len(),
|
|
0,
|
|
"it's OK to query linked worktrees in a repo without worktrees"
|
|
);
|
|
for round in 1..=2 {
|
|
match round {
|
|
1 => repo.object_cache_size(None),
|
|
2 => repo.object_cache_size(128 * 1024),
|
|
_ => unreachable!("BUG"),
|
|
}
|
|
for commit_id in repo.head()?.into_peeled_id()?.ancestors().all()? {
|
|
let commit = commit_id?;
|
|
assert_eq!(commit.id().object()?.kind, gix_object::Kind::Commit);
|
|
assert_eq!(commit.id().header()?.kind(), gix_object::Kind::Commit);
|
|
if round == 2 {
|
|
assert_eq!(
|
|
commit.id().object()?.kind,
|
|
gix_object::Kind::Commit,
|
|
"repeated request triggers cache and doesn't fail"
|
|
);
|
|
}
|
|
assert_eq!(
|
|
commit.id().try_object()?.expect("exists").kind,
|
|
gix_object::Kind::Commit,
|
|
);
|
|
assert_eq!(
|
|
commit.id().try_header()?.expect("exists").kind(),
|
|
gix_object::Kind::Commit,
|
|
);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn empty_tree_can_always_be_found() -> crate::Result {
|
|
let repo = basic_repo()?;
|
|
let empty_tree = gix::hash::ObjectId::empty_tree(repo.object_hash());
|
|
assert_eq!(repo.find_object(empty_tree)?.into_tree().iter().count(), 0);
|
|
assert!(repo.has_object(empty_tree));
|
|
assert_eq!(
|
|
repo.find_header(empty_tree)?,
|
|
gix_odb::find::Header::Loose {
|
|
kind: gix_object::Kind::Tree,
|
|
size: 0,
|
|
},
|
|
"empty tree is considered a loose object"
|
|
);
|
|
assert_eq!(
|
|
repo.try_find_object(empty_tree)?
|
|
.expect("present")
|
|
.into_tree()
|
|
.iter()
|
|
.count(),
|
|
0
|
|
);
|
|
assert_eq!(
|
|
repo.try_find_header(empty_tree)?,
|
|
Some(gix_odb::find::Header::Loose {
|
|
kind: gix_object::Kind::Tree,
|
|
size: 0,
|
|
}),
|
|
"empty tree is considered a loose object"
|
|
);
|
|
|
|
let mut buf = Vec::new();
|
|
assert!(
|
|
repo.objects.try_find(&empty_tree, &mut buf)?.is_none(),
|
|
"the lower level has no such special case so one can determine if this object exists or not"
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
mod tag {
|
|
#[test]
|
|
fn simple() -> crate::Result {
|
|
let (repo, _keep) = crate::repo_rw("make_basic_repo.sh")?;
|
|
let current_head_id = repo.head_id()?;
|
|
let message = "a multi\nline message";
|
|
let tag_ref = repo.tag(
|
|
"v1.0.0",
|
|
current_head_id,
|
|
gix_object::Kind::Commit,
|
|
Some(repo.committer().expect("present")?),
|
|
message,
|
|
gix_ref::transaction::PreviousValue::MustNotExist,
|
|
)?;
|
|
assert_eq!(tag_ref.name().as_bstr(), "refs/tags/v1.0.0");
|
|
assert_ne!(tag_ref.id(), current_head_id, "it points to the tag object");
|
|
let tag = tag_ref.id().object()?;
|
|
let tag = tag.try_to_tag_ref()?;
|
|
assert_eq!(tag.name, "v1.0.0");
|
|
assert_eq!(current_head_id, tag.target(), "the tag points to the commit");
|
|
assert_eq!(tag.target_kind, gix_object::Kind::Commit);
|
|
assert_eq!(
|
|
tag.tagger.as_ref().expect("tagger").actor(),
|
|
repo.committer().expect("present")?.actor()
|
|
);
|
|
assert_eq!(tag.message, message);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
mod commit_as {
|
|
use gix_date::parse::TimeBuf;
|
|
use gix_testtools::tempfile;
|
|
|
|
#[test]
|
|
fn specify_committer_and_author() -> crate::Result {
|
|
let tmp = tempfile::tempdir()?;
|
|
let repo = gix::ThreadSafeRepository::init_opts(
|
|
&tmp,
|
|
gix::create::Kind::WithWorktree,
|
|
Default::default(),
|
|
gix::open::Options::isolated(),
|
|
)?
|
|
.to_thread_local();
|
|
let empty_tree = repo.empty_tree();
|
|
let committer = gix::actor::Signature {
|
|
name: "c".into(),
|
|
email: "c@example.com".into(),
|
|
time: gix_date::parse_header("1 +0030").unwrap(),
|
|
};
|
|
let author = gix::actor::Signature {
|
|
name: "a".into(),
|
|
email: "a@example.com".into(),
|
|
time: gix_date::parse_header("3 +0100").unwrap(),
|
|
};
|
|
|
|
let commit_id = repo.commit_as(
|
|
committer.to_ref(&mut TimeBuf::default()),
|
|
author.to_ref(&mut TimeBuf::default()),
|
|
"HEAD",
|
|
"initial",
|
|
empty_tree.id,
|
|
gix::commit::NO_PARENT_IDS,
|
|
)?;
|
|
let commit = commit_id.object()?.into_commit();
|
|
|
|
let mut buf = TimeBuf::default();
|
|
assert_eq!(commit.committer()?, committer.to_ref(&mut buf));
|
|
assert_eq!(commit.author()?, author.to_ref(&mut buf));
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
mod commit {
|
|
use gix_testtools::tempfile;
|
|
|
|
use crate::{freeze_time, restricted_and_git, util::hex_to_id};
|
|
|
|
#[test]
|
|
fn parent_in_initial_commit_causes_failure() -> crate::Result {
|
|
let tmp = tempfile::tempdir()?;
|
|
let repo = gix::ThreadSafeRepository::init_opts(
|
|
&tmp,
|
|
gix::create::Kind::WithWorktree,
|
|
Default::default(),
|
|
crate::restricted(),
|
|
)?
|
|
.to_thread_local();
|
|
let empty_tree_id = repo.write_object(gix::objs::Tree::empty())?.detach();
|
|
let err = repo
|
|
.commit("HEAD", "initial", empty_tree_id, [empty_tree_id])
|
|
.unwrap_err();
|
|
assert_eq!(
|
|
err.to_string(),
|
|
"Reference \"refs/heads/main\" was supposed to exist with value 4b825dc642cb6eb9a060e54bf8d69288fbee4904, but didn't.",
|
|
"cannot provide parent id in initial commit"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[serial_test::serial]
|
|
fn single_line_initial_commit_empty_tree_ref_nonexisting() -> crate::Result {
|
|
let _env = freeze_time();
|
|
let tmp = tempfile::tempdir()?;
|
|
let repo = gix::ThreadSafeRepository::init_opts(
|
|
&tmp,
|
|
gix::create::Kind::WithWorktree,
|
|
Default::default(),
|
|
restricted_and_git(),
|
|
)?
|
|
.to_thread_local();
|
|
let empty_tree_id = repo.write_object(gix::objs::Tree::empty())?;
|
|
let commit_id = repo.commit("HEAD", "initial", empty_tree_id, gix::commit::NO_PARENT_IDS)?;
|
|
assert_eq!(
|
|
commit_id,
|
|
hex_to_id("3a774843723a713a8d361b4d4d98ad4092ef05bd"),
|
|
"the commit id is stable"
|
|
);
|
|
|
|
let head = repo.head()?.try_into_referent().expect("born");
|
|
assert_eq!(head.name().as_bstr(), "refs/heads/main", "'main' is the default name");
|
|
assert_eq!(
|
|
head.log_iter()
|
|
.rev()?
|
|
.expect("log present")
|
|
.next()
|
|
.expect("one line")?
|
|
.message,
|
|
"commit (initial): initial"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[serial_test::serial]
|
|
fn multi_line_commit_message_uses_first_line_in_ref_log_ref_nonexisting() -> crate::Result {
|
|
let _env = freeze_time();
|
|
let (repo, _keep) = crate::repo_rw_opts("make_basic_repo.sh", restricted_and_git())?;
|
|
let parent = repo.find_reference("HEAD")?.peel_to_id_in_place()?;
|
|
let empty_tree_id = parent.object()?.to_commit_ref_iter().tree_id().expect("tree to be set");
|
|
assert_eq!(
|
|
parent
|
|
.try_object()?
|
|
.expect("present")
|
|
.to_commit_ref_iter()
|
|
.tree_id()
|
|
.expect("tree to be set"),
|
|
empty_tree_id,
|
|
"try and non-try work the same"
|
|
);
|
|
let first_commit_id = repo.commit("HEAD", "hello there \r\n\nthe body", empty_tree_id, Some(parent))?;
|
|
assert_eq!(
|
|
first_commit_id,
|
|
hex_to_id("e7c7273539cfc1a52802fa9d61aa578f6ccebcb4"),
|
|
"the commit id is stable"
|
|
);
|
|
|
|
let head_log_entries: Vec<_> = repo
|
|
.head()?
|
|
.log_iter()
|
|
.rev()?
|
|
.expect("log present")
|
|
.map(Result::unwrap)
|
|
.map(|l| l.message)
|
|
.collect();
|
|
assert_eq!(
|
|
head_log_entries,
|
|
vec!["commit: hello there", "commit: c2", "commit (initial): c1"],
|
|
"we get the actual HEAD log, not the log of some reference"
|
|
);
|
|
let current_commit = repo.head()?.into_peeled_id()?;
|
|
assert_eq!(current_commit, first_commit_id, "the commit was set");
|
|
|
|
let second_commit_id = repo.commit(
|
|
"refs/heads/new-branch",
|
|
"committing into a new branch creates it",
|
|
empty_tree_id,
|
|
Some(first_commit_id),
|
|
)?;
|
|
|
|
assert_eq!(
|
|
second_commit_id,
|
|
hex_to_id("e1412f169e0812eb260601bdab3854ca0f1a7b33"),
|
|
"the second commit id is stable"
|
|
);
|
|
|
|
let mut branch = repo.find_reference("new-branch")?;
|
|
let current_commit = branch.peel_to_id_in_place()?;
|
|
assert_eq!(current_commit, second_commit_id, "the commit was set");
|
|
|
|
let mut log = branch.log_iter();
|
|
let mut log_iter = log.rev()?.expect("log present");
|
|
assert_eq!(
|
|
log_iter.next().expect("one line")?.message,
|
|
"commit: committing into a new branch creates it"
|
|
);
|
|
assert!(
|
|
log_iter.next().is_none(),
|
|
"there is only one log line in the new branch"
|
|
);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn empty_bare_in_memory_repo() -> crate::Result<gix::Repository> {
|
|
Ok(named_subrepo_opts("make_basic_repo.sh", "bare.git", gix::open::Options::isolated())?.with_object_memory())
|
|
}
|
|
|
|
fn empty_bare_repo() -> crate::Result<(tempfile::TempDir, gix::Repository)> {
|
|
let tmp = tempfile::tempdir()?;
|
|
let repo = gix::ThreadSafeRepository::init_opts(
|
|
tmp.path(),
|
|
gix::create::Kind::Bare,
|
|
gix::create::Options::default(),
|
|
gix::open::Options::isolated(),
|
|
)?
|
|
.into();
|
|
Ok((tmp, repo))
|
|
}
|