Skip to content

Commit 5b1048c

Browse files
committed
Edit trees directly when running create_wd_tree() to bypass the index.
1 parent 099ac50 commit 5b1048c

File tree

3 files changed

+95
-8
lines changed

3 files changed

+95
-8
lines changed

Cargo.lock

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/gitbutler-repo/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ publish = false
77

88
[dependencies]
99
git2.workspace = true
10-
gix.workspace = true
10+
gix = { workspace = true, features = ["status", "tree-editor"] }
1111
anyhow = "1.0.86"
1212
bstr.workspace = true
1313
tokio = { workspace = true, features = [

crates/gitbutler-repo/src/repository_ext.rs

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ use std::os::windows::process::CommandExt;
55
use std::{io::Write, path::Path, process::Stdio, str};
66

77
use anyhow::{anyhow, bail, Context, Result};
8-
use bstr::BString;
8+
use bstr::{BString, ByteSlice};
99
use git2::{BlameOptions, Tree};
1010
use gitbutler_branch::{gix_to_git2_signature, SignaturePurpose};
1111
use gitbutler_commit::{commit_buffer::CommitBuffer, commit_headers::CommitHeadersV2};
1212
use gitbutler_config::git::{GbConfig, GitConfig};
1313
use gitbutler_error::error::Code;
1414
use gitbutler_reference::{Refname, RemoteRefname};
15+
use gix::status::plumbing::index_as_worktree::{Change, EntryStatus};
1516
use tracing::instrument;
1617

1718
use crate::{Config, LogUntil};
@@ -147,14 +148,71 @@ impl RepositoryExt for git2::Repository {
147148
}
148149
}
149150

150-
/// Note that this will add all untracked files in the worktree to the index,
151-
/// and write a tree from it.
152-
/// The index won't be stored though.
151+
/// Note that this will add all untracked and modified files in the worktree to
152+
/// the object database, and create a tree from it.
153+
///
154+
/// Note that right now, it doesn't skip big files.
153155
#[instrument(level = tracing::Level::DEBUG, skip(self), err(Debug))]
154156
fn create_wd_tree(&self) -> Result<Tree> {
155-
let mut index = self.index()?;
156-
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
157-
let oid = index.write_tree()?;
157+
let repo = gix::open(self.path())?;
158+
let workdir = repo.work_dir().context("Need non-bare repository")?;
159+
let mut head_tree = repo.edit_tree(repo.head_tree_id()?)?;
160+
let status_changes = repo
161+
.status(gix::progress::Discard)?
162+
.index_worktree_rewrites(None)
163+
.index_worktree_submodules(None)
164+
.into_index_worktree_iter(None::<BString>)?;
165+
for change in status_changes {
166+
let change = change?;
167+
match change {
168+
// modified or tracked files are unconditionally added as blob.
169+
gix::status::index_worktree::iter::Item::Modification {
170+
rela_path,
171+
status: EntryStatus::Change(Change::Type | Change::Modification { .. }),
172+
..
173+
}
174+
| gix::status::index_worktree::iter::Item::DirectoryContents {
175+
entry: gix::dir::Entry { rela_path, .. },
176+
..
177+
} => {
178+
let path = workdir.join(gix::path::from_bstr(&rela_path));
179+
let Ok(md) = std::fs::symlink_metadata(&path) else {
180+
continue;
181+
};
182+
let (id, kind) = if md.is_symlink() {
183+
let target = std::fs::read_link(&path).with_context(|| {
184+
format!(
185+
"Failed to read link at '{}' for adding to the object database",
186+
path.display()
187+
)
188+
})?;
189+
let id = repo.write_blob(gix::path::into_bstr(target).as_bytes())?;
190+
(id, gix::object::tree::EntryKind::Link)
191+
} else {
192+
let mut file = std::fs::File::open(&path).with_context(|| {
193+
format!(
194+
"Could not open file at '{}' for adding it to the object database",
195+
path.display()
196+
)
197+
})?;
198+
let kind = if gix::fs::is_executable(&md) {
199+
gix::object::tree::EntryKind::BlobExecutable
200+
} else {
201+
gix::object::tree::EntryKind::Blob
202+
};
203+
(repo.write_blob_stream(&mut file)?, kind)
204+
};
205+
206+
head_tree.upsert(rela_path, kind, id)?;
207+
}
208+
gix::status::index_worktree::iter::Item::Rewrite { .. } => {
209+
unreachable!("disabled")
210+
}
211+
_ => {}
212+
}
213+
}
214+
215+
let oid = git2::Oid::from_bytes(head_tree.write()?.as_bytes())?;
158216
self.find_tree(oid).map(Into::into).map_err(Into::into)
159217
}
160218

0 commit comments

Comments
 (0)