Skip to main content

nils_test_support/
git.rs

1use std::fs;
2use std::path::Path;
3use std::process::{Command, Output};
4
5use tempfile::TempDir;
6
7#[derive(Debug, Clone)]
8pub struct InitRepoOptions {
9    pub branch: Option<String>,
10    pub initial_commit: bool,
11    pub initial_commit_name: String,
12    pub initial_commit_contents: String,
13    pub initial_commit_message: String,
14}
15
16impl InitRepoOptions {
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    pub fn with_branch(mut self, branch: impl Into<String>) -> Self {
22        self.branch = Some(branch.into());
23        self
24    }
25
26    pub fn without_branch(mut self) -> Self {
27        self.branch = None;
28        self
29    }
30
31    pub fn with_initial_commit(mut self) -> Self {
32        self.initial_commit = true;
33        self
34    }
35}
36
37impl Default for InitRepoOptions {
38    fn default() -> Self {
39        Self {
40            branch: Some("main".to_string()),
41            initial_commit: false,
42            initial_commit_name: "README.md".to_string(),
43            initial_commit_contents: "init".to_string(),
44            initial_commit_message: "init".to_string(),
45        }
46    }
47}
48
49pub fn git_output(dir: &Path, args: &[&str]) -> Output {
50    Command::new("git")
51        .args(args)
52        .current_dir(dir)
53        .output()
54        .expect("git command failed to spawn")
55}
56
57pub fn git(dir: &Path, args: &[&str]) -> String {
58    let output = git_output(dir, args);
59    if !output.status.success() {
60        panic!(
61            "git {:?} failed: {}{}",
62            args,
63            String::from_utf8_lossy(&output.stderr),
64            String::from_utf8_lossy(&output.stdout)
65        );
66    }
67    String::from_utf8_lossy(&output.stdout).to_string()
68}
69
70pub fn git_with_env(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> String {
71    let output = git_output_with_env(dir, args, envs);
72    if !output.status.success() {
73        panic!(
74            "git {:?} failed: {}{}",
75            args,
76            String::from_utf8_lossy(&output.stderr),
77            String::from_utf8_lossy(&output.stdout)
78        );
79    }
80    String::from_utf8_lossy(&output.stdout).to_string()
81}
82
83fn git_output_with_env(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> Output {
84    let mut cmd = Command::new("git");
85    cmd.args(args).current_dir(dir);
86    for (key, value) in envs {
87        cmd.env(key, value);
88    }
89    cmd.output().expect("git command failed to spawn")
90}
91
92pub fn init_repo_at_with(dir: &Path, options: InitRepoOptions) {
93    git(dir, &["init", "-q"]);
94
95    if let Some(branch) = options.branch.as_deref() {
96        // Make the initial branch deterministic across environments.
97        git(dir, &["checkout", "-q", "-B", branch]);
98    }
99
100    git(dir, &["config", "user.email", "test@example.com"]);
101    git(dir, &["config", "user.name", "Test User"]);
102    git(dir, &["config", "commit.gpgsign", "false"]);
103    git(dir, &["config", "tag.gpgSign", "false"]);
104
105    if options.initial_commit {
106        let file_path = dir.join(&options.initial_commit_name);
107        fs::write(&file_path, &options.initial_commit_contents).expect("write initial commit");
108        git(dir, &["add", &options.initial_commit_name]);
109        git(dir, &["commit", "-m", &options.initial_commit_message]);
110    }
111}
112
113pub fn init_repo_with(options: InitRepoOptions) -> TempDir {
114    let dir = TempDir::new().expect("tempdir");
115    init_repo_at_with(dir.path(), options);
116    dir
117}
118
119pub fn worktree_add_branch(repo: &Path, worktree_path: &Path, branch: &str) {
120    let worktree_path = worktree_path.to_string_lossy().to_string();
121    git(repo, &["worktree", "add", &worktree_path, "-b", branch]);
122}
123
124pub fn commit_file(dir: &Path, name: &str, contents: &str, message: &str) -> String {
125    let path = dir.join(name);
126    fs::write(&path, contents).expect("write file");
127    git(dir, &["add", name]);
128    git(dir, &["commit", "-m", message]);
129    git(dir, &["rev-parse", "HEAD"]).trim().to_string()
130}
131
132pub fn repo_id(dir: &Path) -> String {
133    dir.file_name()
134        .and_then(|value| value.to_str())
135        .unwrap_or("")
136        .to_string()
137}