Skip to content

Commit 0566652

Browse files
committed
git: use the real gitdir for checkpoints
Changes: - thread the resolved git directory from RealGitRepository into GitBinary - keep the working directory and git metadata directory separate in checkpoint-related code paths - create temp index files from the real gitdir instead of assuming <working-directory>/.git is a directory - read the default index and info/exclude overrides from the real gitdir - add regression coverage for temp index path generation in worktree/submodule-style layouts Why: - checkpoint operations fail in submodules and git worktrees because .git is a pointer file in those layouts, not a directory - that currently surfaces as "Not a directory" errors when ACP checkpoints try to create temporary index files or apply exclude overrides - git commands should still execute from the worktree, but repository metadata must be resolved from the actual gitdir Validation: - cargo fmt -p git - cargo test -p git --lib - cargo clippy -p git --lib --tests -- -D warnings
1 parent 5920e22 commit 0566652

1 file changed

Lines changed: 50 additions & 15 deletions

File tree

crates/git/src/repository.rs

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ impl RealGitRepository {
10341034
self.any_git_binary_path.clone(),
10351035
self.working_directory()
10361036
.with_context(|| "Can't run git commands without a working directory")?,
1037+
self.path(),
10371038
self.executor.clone(),
10381039
self.is_trusted(),
10391040
))
@@ -1088,6 +1089,7 @@ pub async fn get_git_committer(cx: &AsyncApp) -> GitCommitter {
10881089
let git = GitBinary::new(
10891090
git_binary_path.unwrap_or(PathBuf::from("git")),
10901091
paths::home_dir().clone(),
1092+
paths::home_dir().join(".git"),
10911093
cx.background_executor().clone(),
10921094
true,
10931095
);
@@ -2244,6 +2246,7 @@ impl GitRepository for RealGitRepository {
22442246
cx: AsyncApp,
22452247
) -> BoxFuture<'_, Result<RemoteCommandOutput>> {
22462248
let working_directory = self.working_directory();
2249+
let git_directory = self.path();
22472250
let executor = cx.background_executor().clone();
22482251
let git_binary_path = self.system_git_binary_path.clone();
22492252
let is_trusted = self.is_trusted();
@@ -2255,6 +2258,7 @@ impl GitRepository for RealGitRepository {
22552258
let git = GitBinary::new(
22562259
git_binary_path,
22572260
working_directory,
2261+
git_directory,
22582262
executor.clone(),
22592263
is_trusted,
22602264
);
@@ -2286,6 +2290,7 @@ impl GitRepository for RealGitRepository {
22862290
cx: AsyncApp,
22872291
) -> BoxFuture<'_, Result<RemoteCommandOutput>> {
22882292
let working_directory = self.working_directory();
2293+
let git_directory = self.path();
22892294
let executor = cx.background_executor().clone();
22902295
let git_binary_path = self.system_git_binary_path.clone();
22912296
let is_trusted = self.is_trusted();
@@ -2297,6 +2302,7 @@ impl GitRepository for RealGitRepository {
22972302
let git = GitBinary::new(
22982303
git_binary_path,
22992304
working_directory,
2305+
git_directory,
23002306
executor.clone(),
23012307
is_trusted,
23022308
);
@@ -2326,6 +2332,7 @@ impl GitRepository for RealGitRepository {
23262332
cx: AsyncApp,
23272333
) -> BoxFuture<'_, Result<RemoteCommandOutput>> {
23282334
let working_directory = self.working_directory();
2335+
let git_directory = self.path();
23292336
let remote_name = format!("{}", fetch_options);
23302337
let git_binary_path = self.system_git_binary_path.clone();
23312338
let executor = cx.background_executor().clone();
@@ -2338,6 +2345,7 @@ impl GitRepository for RealGitRepository {
23382345
let git = GitBinary::new(
23392346
git_binary_path,
23402347
working_directory,
2348+
git_directory,
23412349
executor.clone(),
23422350
is_trusted,
23432351
);
@@ -2990,6 +2998,7 @@ async fn exclude_files(git: &GitBinary) -> Result<GitExcludeOverride> {
29902998
pub(crate) struct GitBinary {
29912999
git_binary_path: PathBuf,
29923000
working_directory: PathBuf,
3001+
git_directory: PathBuf,
29933002
executor: BackgroundExecutor,
29943003
index_file_path: Option<PathBuf>,
29953004
envs: HashMap<String, String>,
@@ -3000,12 +3009,14 @@ impl GitBinary {
30003009
pub(crate) fn new(
30013010
git_binary_path: PathBuf,
30023011
working_directory: PathBuf,
3012+
git_directory: PathBuf,
30033013
executor: BackgroundExecutor,
30043014
is_trusted: bool,
30053015
) -> Self {
30063016
Self {
30073017
git_binary_path,
30083018
working_directory,
3019+
git_directory,
30093020
executor,
30103021
index_file_path: None,
30113022
envs: HashMap::default(),
@@ -3051,12 +3062,9 @@ impl GitBinary {
30513062

30523063
// Copy the default index file so that Git doesn't have to rebuild the
30533064
// whole index from scratch. This might fail if this is an empty repository.
3054-
smol::fs::copy(
3055-
self.working_directory.join(".git").join("index"),
3056-
&index_file_path,
3057-
)
3058-
.await
3059-
.ok();
3065+
smol::fs::copy(self.git_directory.join("index"), &index_file_path)
3066+
.await
3067+
.ok();
30603068

30613069
self.index_file_path = Some(index_file_path.clone());
30623070
let result = f(self).await;
@@ -3070,19 +3078,13 @@ impl GitBinary {
30703078
}
30713079

30723080
pub async fn with_exclude_overrides(&self) -> Result<GitExcludeOverride> {
3073-
let path = self
3074-
.working_directory
3075-
.join(".git")
3076-
.join("info")
3077-
.join("exclude");
3081+
let path = self.git_directory.join("info").join("exclude");
30783082

30793083
GitExcludeOverride::new(path).await
30803084
}
30813085

30823086
fn path_for_index_id(&self, id: Uuid) -> PathBuf {
3083-
self.working_directory
3084-
.join(".git")
3085-
.join(format!("index-{}.tmp", id))
3087+
self.git_directory.join(format!("index-{}.tmp", id))
30863088
}
30873089

30883090
pub async fn run<S>(&self, args: &[S]) -> Result<String>
@@ -3405,6 +3407,7 @@ mod tests {
34053407
let git = GitBinary::new(
34063408
PathBuf::from("git"),
34073409
dir.path().to_path_buf(),
3410+
dir.path().join(".git"),
34083411
cx.executor(),
34093412
false,
34103413
);
@@ -3418,6 +3421,7 @@ mod tests {
34183421
let git = GitBinary::new(
34193422
PathBuf::from("git"),
34203423
dir.path().to_path_buf(),
3424+
dir.path().join(".git"),
34213425
cx.executor(),
34223426
false,
34233427
);
@@ -3437,6 +3441,7 @@ mod tests {
34373441
let git = GitBinary::new(
34383442
PathBuf::from("git"),
34393443
dir.path().to_path_buf(),
3444+
dir.path().join(".git"),
34403445
cx.executor(),
34413446
false,
34423447
);
@@ -3462,6 +3467,7 @@ mod tests {
34623467
let git = GitBinary::new(
34633468
PathBuf::from("git"),
34643469
dir.path().to_path_buf(),
3470+
dir.path().join(".git"),
34653471
cx.executor(),
34663472
true,
34673473
);
@@ -3480,6 +3486,7 @@ mod tests {
34803486
let git = GitBinary::new(
34813487
PathBuf::from("git"),
34823488
dir.path().to_path_buf(),
3489+
dir.path().join(".git"),
34833490
cx.executor(),
34843491
true,
34853492
);
@@ -3494,6 +3501,27 @@ mod tests {
34943501
);
34953502
}
34963503

3504+
#[gpui::test]
3505+
async fn test_path_for_index_id_uses_real_git_directory(cx: &mut TestAppContext) {
3506+
cx.executor().allow_parking();
3507+
let working_directory = PathBuf::from("/code/worktree");
3508+
let git_directory = PathBuf::from("/code/repo/.git/modules/worktree");
3509+
let git = GitBinary::new(
3510+
PathBuf::from("git"),
3511+
working_directory,
3512+
git_directory.clone(),
3513+
cx.executor(),
3514+
false,
3515+
);
3516+
3517+
let path = git.path_for_index_id(Uuid::nil());
3518+
3519+
assert_eq!(
3520+
path,
3521+
git_directory.join(format!("index-{}.tmp", Uuid::nil()))
3522+
);
3523+
}
3524+
34973525
#[gpui::test]
34983526
async fn test_checkpoint_basic(cx: &mut TestAppContext) {
34993527
disable_git_global_config();
@@ -4380,13 +4408,20 @@ mod tests {
43804408
/// Force a Git garbage collection on the repository.
43814409
fn gc(&self) -> BoxFuture<'_, Result<()>> {
43824410
let working_directory = self.working_directory();
4411+
let git_directory = self.path();
43834412
let git_binary_path = self.any_git_binary_path.clone();
43844413
let executor = self.executor.clone();
43854414
self.executor
43864415
.spawn(async move {
43874416
let git_binary_path = git_binary_path.clone();
43884417
let working_directory = working_directory?;
4389-
let git = GitBinary::new(git_binary_path, working_directory, executor, true);
4418+
let git = GitBinary::new(
4419+
git_binary_path,
4420+
working_directory,
4421+
git_directory,
4422+
executor,
4423+
true,
4424+
);
43904425
git.run(&["gc", "--prune"]).await?;
43914426
Ok(())
43924427
})

0 commit comments

Comments
 (0)