Merge pull request #1125 from gitbutlerapp/Virtual-branch-1

all purpose tree builder
This commit is contained in:
Nikita Galaiko 2023-08-31 09:58:11 +02:00 committed by GitHub
commit 15bdff860c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 65 deletions

View File

@ -433,48 +433,37 @@ impl Repository {
// update last timestamp
sessions::Writer::new(self).write(session)?;
let mut tree_builder = self
.git_repository
.treebuilder(None)
.context("failed to create tree builder")?;
tree_builder
.insert(
"session",
build_session_tree(self)
.context("failed to build session tree")?
.into(),
0o040000,
)
.context("failed to insert session tree")?;
tree_builder
.insert(
"wd",
build_wd_tree(self, project_repository)
.context("failed to build working directory tree")?
.into(),
0o040000,
)
.context("failed to insert wd tree")?;
tree_builder
.insert(
"logs",
build_log_tree(self, project_repository)
.context("failed to build logs tree")?
.into(),
0o040000,
)
.context("failed to insert logs tree")?;
tree_builder
.insert(
"branches",
build_branches_tree(self)
.context("failed to build branches tree")?
.into(),
0o040000,
)
.context("failed to insert branches tree")?;
let mut tree_builder = self.git_repository.treebuilder(None);
tree_builder.upsert(
"session",
build_session_tree(self)
.context("failed to build session tree")?
.into(),
git::FileMode::Tree,
);
tree_builder.upsert(
"wd",
build_wd_tree(self, project_repository)
.context("failed to build working directory tree")?
.into(),
git::FileMode::Tree,
);
tree_builder.upsert(
"logs",
build_log_tree(self, project_repository)
.context("failed to build logs tree")?
.into(),
git::FileMode::Tree,
);
tree_builder.upsert(
"branches",
build_branches_tree(self)
.context("failed to build branches tree")?
.into(),
git::FileMode::Tree,
);
let tree_id = tree_builder.write().context("failed to write tree")?.into();
let tree_id = tree_builder.write().context("failed to write tree")?;
let user = self.users_store.get().context("failed to get user")?;
@ -613,14 +602,6 @@ impl Repository {
let id = id?;
let commit = repo.find_commit(id.into())?;
let copy_tree = |tree: &git::Tree| -> Result<git::Oid> {
let tree_builder = self.git_repository.treebuilder(Some(tree))?;
let tree_oid = tree_builder.write()?;
Ok(tree_oid.into())
};
let tree = self.git_repository.find_tree(copy_tree(&commit.tree()?)?)?;
match self.git_repository.head() {
Result::Ok(head) => {
let parent = head.peel_to_commit()?;
@ -630,7 +611,7 @@ impl Repository {
&commit.author(),
&commit.committer(),
commit.message().unwrap(),
&tree,
&commit.tree()?,
&[&parent],
)
.context("failed to commit")?;
@ -642,7 +623,7 @@ impl Repository {
&commit.author(),
&commit.committer(),
commit.message().unwrap(),
&tree,
&commit.tree()?,
&[],
)
.context("failed to commit")?;

View File

@ -1,6 +1,8 @@
use std::path;
use super::{AnnotatedCommit, Branch, Commit, Index, Oid, Reference, Remote, Result, Tree};
use super::{
AnnotatedCommit, Branch, Commit, Index, Oid, Reference, Remote, Result, Tree, TreeBuilder,
};
// wrapper around git2::Repository to get control over how it's used.
pub struct Repository(git2::Repository);
@ -186,8 +188,8 @@ impl Repository {
self.0.config()
}
pub fn treebuilder(&self, tree: Option<&Tree>) -> Result<git2::TreeBuilder> {
self.0.treebuilder(tree.map(Into::into))
pub fn treebuilder<'repo>(&'repo self, tree: Option<&'repo Tree>) -> TreeBuilder<'repo> {
TreeBuilder::new(self, tree)
}
pub fn path(&self) -> &path::Path {

View File

@ -72,3 +72,60 @@ impl<'repo> TreeEntry<'repo> {
self.entry.name()
}
}
#[derive(PartialEq)]
pub enum FileMode {
Blob,
BlobExecutable,
Link,
Tree,
}
impl From<FileMode> for git2::FileMode {
fn from(filemod: FileMode) -> Self {
match filemod {
FileMode::Blob => git2::FileMode::Blob,
FileMode::BlobExecutable => git2::FileMode::BlobExecutable,
FileMode::Link => git2::FileMode::Link,
FileMode::Tree => git2::FileMode::Tree,
}
}
}
pub struct TreeBuilder<'repo> {
repo: &'repo git2::Repository,
builder: git2::build::TreeUpdateBuilder,
base: Option<&'repo git2::Tree<'repo>>,
}
impl<'repo> TreeBuilder<'repo> {
pub fn new(repo: &'repo Repository, base: Option<&'repo Tree>) -> Self {
TreeBuilder {
repo: repo.into(),
builder: git2::build::TreeUpdateBuilder::new(),
base: base.map(Into::into),
}
}
pub fn upsert<P: AsRef<path::Path>>(&mut self, filename: P, oid: Oid, filemode: FileMode) {
self.builder
.upsert(filename.as_ref(), oid.into(), filemode.into());
}
pub fn remove<P: AsRef<path::Path>>(&mut self, filename: P) {
self.builder.remove(filename.as_ref());
}
pub fn write(&mut self) -> Result<Oid> {
let repo: &git2::Repository = self.repo.into();
if let Some(base) = self.base {
let tree_id = self.builder.create_updated(&repo, base.into())?;
Ok(tree_id.into())
} else {
let empty_tree_id = repo.treebuilder(None)?.write()?;
let empty_tree = repo.find_tree(empty_tree_id)?;
let tree_id = self.builder.create_updated(&repo, &empty_tree)?;
Ok(tree_id.into())
}
}
}

View File

@ -2737,7 +2737,7 @@ fn tree_to_file_list(repository: &git::Repository, tree: &git::Tree) -> Vec<Stri
.get_path(std::path::Path::new(path))
.unwrap_or_else(|_| panic!("failed to get tree entry for path {}", path));
let object = entry
.to_object(repository.into())
.to_object(repository)
.unwrap_or_else(|_| panic!("failed to get object for tree entry {}", path));
if object.kind() == Some(git2::ObjectType::Blob) {
file_list.push(path.to_string());
@ -2759,7 +2759,7 @@ fn tree_to_entry_list(
.get_path(std::path::Path::new(path))
.unwrap_or_else(|_| panic!("failed to get tree entry for path {}", path));
let object = entry
.to_object(repository.into())
.to_object(repository)
.unwrap_or_else(|_| panic!("failed to get object for tree entry {}", path));
let blob = object.as_blob().expect("failed to get blob");
// convert content to string

View File

@ -1689,7 +1689,7 @@ pub fn write_tree(
let head_commit = git_repository.find_commit(target.sha)?;
let base_tree = head_commit.tree()?;
let mut builder = git2::build::TreeUpdateBuilder::new();
let mut builder = git_repository.treebuilder(Some(&base_tree));
// now update the index with content in the working directory for each file
for file in files {
// convert this string to a Path
@ -1699,19 +1699,19 @@ pub fn write_tree(
// if file exists
if full_path.exists() {
// if file is executable, use 755, otherwise 644
let mut filemode = git2::FileMode::Blob;
let mut filemode = git::FileMode::Blob;
// check if full_path file is executable
if let Ok(metadata) = std::fs::symlink_metadata(&full_path) {
if metadata.permissions().mode() & 0o111 != 0 {
filemode = git2::FileMode::BlobExecutable;
filemode = git::FileMode::BlobExecutable;
}
if metadata.file_type().is_symlink() {
filemode = git2::FileMode::Link;
filemode = git::FileMode::Link;
}
}
// get the blob
if filemode == git2::FileMode::Link {
if filemode == git::FileMode::Link {
// it's a symlink, make the content the path of the link
let link_target = std::fs::read_link(&full_path)?;
// make link_target into a relative path
@ -1727,7 +1727,7 @@ pub fn write_tree(
if file.binary {
let new_blob_oid = &file.hunks[0].diff;
// convert string to Oid
let new_blob_oid = git2::Oid::from_str(new_blob_oid)?;
let new_blob_oid = new_blob_oid.parse().context("failed to diff as oid")?;
builder.upsert(rel_path, new_blob_oid, filemode);
} else {
// blob from tree_entry
@ -1773,9 +1773,7 @@ pub fn write_tree(
}
// now write out the tree
let tree_oid = builder
.create_updated(git_repository.into(), (&base_tree).into())
.context("failed to create updated tree")?;
let tree_oid = builder.write().context("failed to write updated tree")?;
Ok(tree_oid.into())
}