Skip to content

Commit c5cfe78

Browse files
mlcui-corparxanas
authored andcommitted
fix(git): fix "not a git repository" when running in repo projects
This works around libgit2/libgit2#6401 by using the current working directory, instead of `libgit2`'s incorrect repo working copy path, when a `repo` project is detected.
1 parent 599bfd0 commit c5cfe78

File tree

1 file changed

+45
-14
lines changed
  • git-branchless-lib/src/git

1 file changed

+45
-14
lines changed

git-branchless-lib/src/git/run.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::convert::TryInto;
33
use std::ffi::{OsStr, OsString};
44
use std::fmt::Write;
55
use std::io::{BufRead, BufReader, Read, Write as WriteIo};
6-
use std::path::PathBuf;
6+
use std::path::{Component, Path, PathBuf};
77
use std::process::{Command, ExitStatus, Stdio};
88
use std::sync::Arc;
99
use std::thread::{self, JoinHandle};
@@ -243,6 +243,48 @@ impl GitRunInfo {
243243
}
244244
}
245245

246+
/// Returns the working directory for commands run on a given `Repo`.
247+
///
248+
/// This is typically the working copy path for the repo.
249+
///
250+
/// Some commands (notably `git status`) do not function correctly when run
251+
/// from the git repo (i.e. `.git`) path.
252+
/// Hooks should also be run from the working copy path - from
253+
/// `githooks(5)`: Before Git invokes a hook, it changes its working
254+
/// directory to either $GIT_DIR in a bare repository or the root of the
255+
/// working tree in a non-bare repository.
256+
///
257+
/// This contains additional logic for `repo`-managed projects to work
258+
/// around an upstream `libgit2` issue.
259+
fn working_directory<'a>(&'a self, repo: &'a Repo) -> &'a Path {
260+
repo.get_working_copy_path()
261+
// `libgit2` returns the working copy path as the parent of the
262+
// `.git` directory. However, if the `.git` directory is a symlink,
263+
// `libgit2` *resolves the symlink*, returning an incorrect working
264+
// directory: https://github.com/libgit2/libgit2/issues/6401
265+
//
266+
// This notably occurs when working with `repo`-managed projects,
267+
// which symlinks `.git` directories to a subdirectory of a shared
268+
// `.repo` directory. To work around this bug, we instead use the
269+
// current working directory when we detect a `repo`-managed
270+
// project.
271+
//
272+
// This workaround may result in slightly incorrect hook behavior
273+
// for `repo`-managed projects, as the current working directory may
274+
// be a subdirectory of the root of the working tree.
275+
.map(|working_copy| {
276+
if working_copy
277+
.components()
278+
.contains(&Component::Normal(OsStr::new(".repo")))
279+
{
280+
&self.working_directory
281+
} else {
282+
working_copy
283+
}
284+
})
285+
.unwrap_or_else(|| repo.get_path())
286+
}
287+
246288
fn run_silent_inner(
247289
&self,
248290
repo: &Repo,
@@ -260,12 +302,7 @@ impl GitRunInfo {
260302
stdin,
261303
} = opts;
262304

263-
// Prefer running in the working copy path to the repo path, because
264-
// some commands (notably `git status`) to not function correctly
265-
// when run from the git repo (i.e. `.git`) path.
266-
let repo_path = repo
267-
.get_working_copy_path()
268-
.unwrap_or_else(|| repo.get_path());
305+
let repo_path = self.working_directory(repo);
269306
// Technically speaking, we should be able to work with non-UTF-8 repository
270307
// paths. Need to make the typechecker accept it.
271308
let repo_path = repo_path.to_str().ok_or_else(|| {
@@ -383,13 +420,7 @@ impl GitRunInfo {
383420

384421
if hook_dir.join(hook_name).exists() {
385422
let mut child = Command::new(get_sh().ok_or_else(|| eyre!("could not get sh"))?)
386-
// From `githooks(5)`: Before Git invokes a hook, it changes its
387-
// working directory to either $GIT_DIR in a bare repository or the
388-
// root of the working tree in a non-bare repository.
389-
.current_dir(
390-
repo.get_working_copy_path()
391-
.unwrap_or_else(|| repo.get_path()),
392-
)
423+
.current_dir(self.working_directory(repo))
393424
.arg("-c")
394425
.arg(format!("{hook_name} \"$@\""))
395426
.arg(hook_name) // "$@" expands "$1" "$2" "$3" ... but we also must specify $0.

0 commit comments

Comments
 (0)