Edit trees directly when running create_wd_tree() to bypass the index.

This commit is contained in:
Sebastian Thiel 2024-09-14 15:16:15 +02:00
parent 099ac50f5d
commit 31626b726b
No known key found for this signature in database
GPG Key ID: 9CB5EE7895E8268B
3 changed files with 100 additions and 8 deletions

29
Cargo.lock generated
View File

@ -2716,6 +2716,7 @@ dependencies = [
"gix-revision",
"gix-revwalk 0.15.0",
"gix-sec 0.10.8 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
"gix-status",
"gix-submodule",
"gix-tempfile 14.0.2 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
"gix-trace 0.1.10 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
@ -3573,6 +3574,28 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "gix-status"
version = "0.13.0"
source = "git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a#72daa46bad9d397ef2cc48a3cffda23f414ccd8a"
dependencies = [
"bstr",
"filetime",
"gix-diff",
"gix-dir",
"gix-features 0.38.2 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
"gix-filter",
"gix-fs 0.11.3 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
"gix-hash 0.14.2 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
"gix-index 0.35.0",
"gix-object 0.44.0",
"gix-path 0.10.11 (git+https://github.com/Byron/gitoxide?rev=72daa46bad9d397ef2cc48a3cffda23f414ccd8a)",
"gix-pathspec",
"gix-worktree 0.36.0",
"portable-atomic",
"thiserror",
]
[[package]]
name = "gix-submodule"
version = "0.14.0"
@ -5827,6 +5850,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "portable-atomic"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
[[package]]
name = "powerfmt"
version = "0.2.0"

View File

@ -7,7 +7,7 @@ publish = false
[dependencies]
git2.workspace = true
gix.workspace = true
gix = { workspace = true, features = ["status", "tree-editor"] }
anyhow = "1.0.86"
bstr.workspace = true
tokio = { workspace = true, features = [

View File

@ -5,13 +5,14 @@ use std::os::windows::process::CommandExt;
use std::{io::Write, path::Path, process::Stdio, str};
use anyhow::{anyhow, bail, Context, Result};
use bstr::BString;
use bstr::{BString, ByteSlice};
use git2::{BlameOptions, Tree};
use gitbutler_branch::{gix_to_git2_signature, SignaturePurpose};
use gitbutler_commit::{commit_buffer::CommitBuffer, commit_headers::CommitHeadersV2};
use gitbutler_config::git::{GbConfig, GitConfig};
use gitbutler_error::error::Code;
use gitbutler_reference::{Refname, RemoteRefname};
use gix::status::plumbing::index_as_worktree::{Change, EntryStatus};
use tracing::instrument;
use crate::{Config, LogUntil};
@ -147,14 +148,76 @@ impl RepositoryExt for git2::Repository {
}
}
/// Note that this will add all untracked files in the worktree to the index,
/// and write a tree from it.
/// The index won't be stored though.
/// Note that this will add all untracked and modified files in the worktree to
/// the object database, and create a tree from it.
///
/// Note that right now, it doesn't skip big files.
#[instrument(level = tracing::Level::DEBUG, skip(self), err(Debug))]
fn create_wd_tree(&self) -> Result<Tree> {
let mut index = self.index()?;
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
let oid = index.write_tree()?;
let repo = gix::open(self.path())?;
let workdir = repo.work_dir().context("Need non-bare repository")?;
let mut head_tree_editor = repo.edit_tree(repo.head_tree_id()?)?;
let status_changes = repo
.status(gix::progress::Discard)?
.index_worktree_rewrites(None)
.index_worktree_submodules(None)
.into_index_worktree_iter(None::<BString>)?;
for change in status_changes {
let change = change?;
match change {
// modified or tracked files are unconditionally added as blob.
gix::status::index_worktree::iter::Item::Modification {
rela_path,
status: EntryStatus::Change(Change::Type | Change::Modification { .. }),
..
}
| gix::status::index_worktree::iter::Item::DirectoryContents {
entry:
gix::dir::Entry {
rela_path,
status: gix::dir::entry::Status::Untracked,
..
},
..
} => {
let path = workdir.join(gix::path::from_bstr(&rela_path));
let Ok(md) = std::fs::symlink_metadata(&path) else {
continue;
};
let (id, kind) = if md.is_symlink() {
let target = std::fs::read_link(&path).with_context(|| {
format!(
"Failed to read link at '{}' for adding to the object database",
path.display()
)
})?;
let id = repo.write_blob(gix::path::into_bstr(target).as_bytes())?;
(id, gix::object::tree::EntryKind::Link)
} else {
let mut file = std::fs::File::open(&path).with_context(|| {
format!(
"Could not open file at '{}' for adding it to the object database",
path.display()
)
})?;
let kind = if gix::fs::is_executable(&md) {
gix::object::tree::EntryKind::BlobExecutable
} else {
gix::object::tree::EntryKind::Blob
};
(repo.write_blob_stream(&mut file)?, kind)
};
head_tree_editor.upsert(rela_path, kind, id)?;
}
gix::status::index_worktree::iter::Item::Rewrite { .. } => {
unreachable!("disabled")
}
_ => {}
}
}
let oid = git2::Oid::from_bytes(head_tree_editor.write()?.as_bytes())?;
self.find_tree(oid).map(Into::into).map_err(Into::into)
}