diff --git a/src/bin/ent/main.rs b/src/bin/ent/main.rs index 1c9ddaa..1363641 100644 --- a/src/bin/ent/main.rs +++ b/src/bin/ent/main.rs @@ -45,6 +45,15 @@ enum Commands { issue_id: String, description: Option, }, + + /// Sync entomologist data with remote. This fetches from the remote, + /// merges the remote entomologist data branch with the local one, + /// and pushes the result back to the remote. + Sync { + /// Name of the git remote to sync with. + #[arg(default_value_t = String::from("origin"))] + remote: String, + }, } fn handle_command(args: &Args, issues_dir: &std::path::Path) -> anyhow::Result<()> { @@ -154,6 +163,24 @@ fn handle_command(args: &Args, issues_dir: &std::path::Path) -> anyhow::Result<( } } } + + Commands::Sync { remote } => { + if args.issues_dir.is_some() { + return Err(anyhow::anyhow!( + "`sync` operates on a branch, don't specify `issues_dir`" + )); + } + // FIXME: Kinda bogus to re-do this thing we just did in + // `main()`. Maybe `main()` shouldn't create the worktree, + // maybe we should do it here in `handle_command()`? + // That way also each command could decide if it wants a + // read-only worktree or a read/write one. + let branch = match &args.issues_branch { + Some(branch) => branch, + None => "entomologist-data", + }; + entomologist::git::sync(issues_dir, remote, branch)?; + } } Ok(()) diff --git a/src/git.rs b/src/git.rs index caa5e4b..30f703e 100644 --- a/src/git.rs +++ b/src/git.rs @@ -124,6 +124,62 @@ pub fn git_commit_file(file: &std::path::Path) -> Result<(), GitError> { Ok(()) } +pub fn git_fetch(dir: &std::path::Path, remote: &str) -> Result<(), GitError> { + let result = std::process::Command::new("git") + .args(["fetch", remote]) + .current_dir(dir) + .output()?; + if !result.status.success() { + println!("stdout: {}", std::str::from_utf8(&result.stdout).unwrap()); + println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap()); + return Err(GitError::Oops); + } + Ok(()) +} + +pub fn sync(dir: &std::path::Path, remote: &str, branch: &str) -> Result<(), GitError> { + // We do all the work in a directory that's (FIXME) hopefully a + // worktree. If anything goes wrong we just fail out and ask the + // human to fix it by hand :-/ + // 1. `git fetch` + // 2. `git merge REMOTE/BRANCH` + // 3. `git push REMOTE BRANCH` + + git_fetch(dir, remote)?; + + // Merge remote branch into local. + let result = std::process::Command::new("git") + .args(["merge", &format!("{}/{}", remote, branch)]) + .current_dir(dir) + .output()?; + if !result.status.success() { + println!( + "Sync failed! Merge error! Help, a human needs to fix the mess in {:?}", + dir + ); + println!("stdout: {}", std::str::from_utf8(&result.stdout).unwrap()); + println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap()); + return Err(GitError::Oops); + } + + // Push merged branch to remote. + let result = std::process::Command::new("git") + .args(["push", remote, branch]) + .current_dir(dir) + .output()?; + if !result.status.success() { + println!( + "Sync failed! Push error! Help, a human needs to fix the mess in {:?}", + dir + ); + println!("stdout: {}", std::str::from_utf8(&result.stdout).unwrap()); + println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap()); + return Err(GitError::Oops); + } + + Ok(()) +} + pub fn create_orphan_branch(branch: &str) -> Result<(), GitError> { { let tmp_worktree = tempfile::tempdir().unwrap(); diff --git a/src/issue.rs b/src/issue.rs index c762c82..e099616 100644 --- a/src/issue.rs +++ b/src/issue.rs @@ -75,7 +75,6 @@ impl fmt::Display for State { State::InProgress => "inprogress", State::Done => "done", State::WontDo => "wontdo", - }; write!(f, "{fmt_str}") } @@ -145,7 +144,6 @@ impl Issue { let mut dir = std::path::PathBuf::from(&self.dir); dir.push("comments"); if !dir.exists() { - println!("creating {}", dir.to_string_lossy()); std::fs::create_dir(&dir)?; }