Skip to content

Commit f22eaeb

Browse files
committed
improve: enhance error message for non-git repositories
Add user-friendly error message when git chain is run outside a git repository: - Replace technical git2 error with clear, actionable message - Add helpful hints directing users to run 'git init' - Style error output consistently with colored formatting (error: in red, hint: in yellow) - Mirror Git's own error message style for familiarity Add integration test for non-git repository edge case: - Test in system temp directory to avoid finding parent repos - Verify proper error message and exit status - Include diagnostic printing following CLAUDE.md guidelines All 53 tests passing, make ci-local verified.
1 parent 3bb54c2 commit f22eaeb

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

src/git_chain/core.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::process;
22

33
use colored::*;
4-
use git2::{BranchType, Config, ConfigLevel, Error, ErrorCode, ObjectType, Repository};
4+
use git2::{BranchType, Config, ConfigLevel, Error, ErrorClass, ErrorCode, ObjectType, Repository};
55
use regex::Regex;
66

77
use super::GitChain;
@@ -12,7 +12,28 @@ impl GitChain {
1212
pub fn init() -> Result<Self, Error> {
1313
let name_of_current_executable = executable_name();
1414

15-
let repo = Repository::discover(".")?;
15+
let repo = match Repository::discover(".") {
16+
Ok(repo) => repo,
17+
Err(ref e)
18+
if e.class() == ErrorClass::Repository && e.code() == ErrorCode::NotFound =>
19+
{
20+
eprintln!(
21+
"{} Not a git repository (or any of the parent directories)",
22+
"error:".red().bold()
23+
);
24+
eprintln!(
25+
"\n{} This command must be run inside a git repository.",
26+
"hint:".yellow().bold()
27+
);
28+
eprintln!(
29+
"{} Run {} to create a new git repository.",
30+
"hint:".yellow().bold(),
31+
"git init".bold()
32+
);
33+
process::exit(1);
34+
}
35+
Err(e) => return Err(e),
36+
};
1637

1738
if repo.is_bare() {
1839
eprintln!(

tests/misc.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#[path = "common/mod.rs"]
22
pub mod common;
33

4+
use std::fs;
5+
46
use common::{
57
create_new_file, first_commit_all, generate_path_to_repo, get_current_branch_name,
68
run_test_bin_expect_err, setup_git_repo, teardown_git_repo,
@@ -31,3 +33,55 @@ fn no_subcommand() {
3133

3234
teardown_git_repo(repo_name);
3335
}
36+
37+
#[test]
38+
fn not_a_git_repo() {
39+
// Create a directory in the system temp location to avoid finding parent git repos
40+
let temp_dir = std::env::temp_dir();
41+
let path_to_non_git_dir = temp_dir.join("git_chain_test_not_a_repo");
42+
43+
// Create a directory that is NOT a git repository
44+
fs::remove_dir_all(&path_to_non_git_dir).ok();
45+
fs::create_dir_all(&path_to_non_git_dir).unwrap();
46+
47+
// Run git chain in the non-git directory
48+
let args: Vec<String> = vec![];
49+
let output = run_test_bin_expect_err(&path_to_non_git_dir, args);
50+
51+
let stdout = String::from_utf8_lossy(&output.stdout);
52+
let stderr = String::from_utf8_lossy(&output.stderr);
53+
54+
// Diagnostic printing
55+
println!("=== TEST DIAGNOSTICS ===");
56+
println!("Test directory: {:?}", path_to_non_git_dir);
57+
println!("STDOUT: {}", stdout);
58+
println!("STDERR: {}", stderr);
59+
println!("EXIT STATUS: {}", output.status);
60+
println!("Is directory a git repo: false (intentional test condition)");
61+
println!("======");
62+
63+
// Uncomment to stop test execution and inspect state with captured output
64+
// assert!(false, "DEBUG STOP: not_a_git_repo test section");
65+
// assert!(false, "stdout: {}", stdout);
66+
// assert!(false, "stderr: {}", stderr);
67+
// assert!(false, "status code: {}", output.status.code().unwrap_or(0));
68+
69+
// Specific assertions based on expected behavior
70+
assert!(
71+
!output.status.success(),
72+
"Command should fail when run in non-git directory"
73+
);
74+
assert!(
75+
stderr.contains("Not a git repository"),
76+
"Error message should mention 'Not a git repository', got: {}",
77+
stderr
78+
);
79+
assert!(
80+
stderr.contains("This command must be run inside a git repository"),
81+
"Error message should provide helpful hint, got: {}",
82+
stderr
83+
);
84+
85+
// Clean up
86+
fs::remove_dir_all(&path_to_non_git_dir).ok();
87+
}

0 commit comments

Comments
 (0)