Skip to content

Commit 7574416

Browse files
authored
Merge pull request #159 from blairconrad/rebasey-when-wet
Suppress git rebase when the --dry-run flag is present
2 parents bc179c5 + 3361958 commit 7574416

File tree

4 files changed

+97
-31
lines changed

4 files changed

+97
-31
lines changed

Cargo.lock

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,4 @@ memchr = "2.3"
3535
anyhow = "1.0"
3636

3737
[dev-dependencies]
38-
current_dir = "0.1.0"
3938
tempfile = "3.1"

src/lib.rs

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod owned;
88
mod stack;
99

1010
use std::io::Write;
11+
use std::path::Path;
1112

1213
pub struct Config<'a> {
1314
pub dry_run: bool,
@@ -372,6 +373,25 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository
372373
assert!(number_of_parents <= 1);
373374

374375
let mut command = Command::new("git");
376+
377+
// We'd generally expect to be run from within the repository, but just in case,
378+
// try to have git run rebase from the repository root.
379+
// This simplifies writing tests that execute from within git-absorb's source directory
380+
// but operate on temporary repositories created elsewhere.
381+
// (The tests could explicitly change directories, but then must be serialized.)
382+
let repo_path = repo.path().parent().map(Path::to_str).flatten();
383+
match repo_path {
384+
Some(path) => {
385+
command.args(["-C", path]);
386+
}
387+
_ => {
388+
warn!(
389+
logger,
390+
"Could not determine repository path for rebase. Running in current directory."
391+
);
392+
}
393+
}
394+
375395
command.args(["rebase", "--interactive", "--autosquash", "--autostash"]);
376396

377397
for arg in config.rebase_options {
@@ -387,9 +407,14 @@ fn run_with_repo(logger: &slog::Logger, config: &Config, repo: &git2::Repository
387407
command.arg(&base_commit_sha);
388408
}
389409

390-
// Don't check that we have successfully absorbed everything, nor git's
391-
// exit code -- as git will print helpful messages on its own.
392-
command.status().expect("could not run git rebase");
410+
if config.dry_run {
411+
info!(logger, "would have run git rebase"; "command" => format!("{:?}", command));
412+
} else {
413+
debug!(logger, "running git rebase"; "command" => format!("{:?}", command));
414+
// Don't check that we have successfully absorbed everything, nor git's
415+
// exit code -- as git will print helpful messages on its own.
416+
command.status().expect("could not run git rebase");
417+
}
393418
}
394419

395420
Ok(())
@@ -687,7 +712,7 @@ mod tests {
687712
and_rebase: true,
688713
..DEFAULT_CONFIG
689714
};
690-
repo_utils::run_in_repo(&ctx, || run_with_repo(&logger, &config, &ctx.repo)).unwrap();
715+
run_with_repo(&logger, &config, &ctx.repo).unwrap();
691716

692717
let mut revwalk = ctx.repo.revwalk().unwrap();
693718
revwalk.push_head().unwrap();
@@ -709,7 +734,7 @@ mod tests {
709734
rebase_options: &vec!["--signoff"],
710735
..DEFAULT_CONFIG
711736
};
712-
repo_utils::run_in_repo(&ctx, || run_with_repo(&logger, &config, &ctx.repo)).unwrap();
737+
run_with_repo(&logger, &config, &ctx.repo).unwrap();
713738

714739
let mut revwalk = ctx.repo.revwalk().unwrap();
715740
revwalk.push_head().unwrap();
@@ -761,6 +786,66 @@ mod tests {
761786
assert!(is_something_in_index);
762787
}
763788

789+
#[test]
790+
fn dry_run_flag() {
791+
let ctx = repo_utils::prepare_and_stage();
792+
793+
// run 'git-absorb'
794+
let drain = slog::Discard;
795+
let logger = slog::Logger::root(drain, o!());
796+
let config = Config {
797+
dry_run: true,
798+
..DEFAULT_CONFIG
799+
};
800+
run_with_repo(&logger, &config, &ctx.repo).unwrap();
801+
802+
let mut revwalk = ctx.repo.revwalk().unwrap();
803+
revwalk.push_head().unwrap();
804+
assert_eq!(revwalk.count(), 1);
805+
let is_something_in_index = !nothing_left_in_index(&ctx.repo).unwrap();
806+
assert!(is_something_in_index);
807+
}
808+
809+
#[test]
810+
fn dry_run_flag_with_and_rebase_flag() {
811+
let (ctx, path) = repo_utils::prepare_repo();
812+
repo_utils::set_config_option(&ctx.repo, "core.editor", "true");
813+
814+
// create a fixup commit that 'git rebase' will act on if called
815+
let tree = repo_utils::stage_file_changes(&ctx, &path);
816+
let signature = ctx.repo.signature().unwrap();
817+
let head_commit = ctx.repo.head().unwrap().peel_to_commit().unwrap();
818+
ctx.repo
819+
.commit(
820+
Some("HEAD"),
821+
&signature,
822+
&signature,
823+
&format!("fixup! {}\n", head_commit.id()),
824+
&tree,
825+
&[&head_commit],
826+
)
827+
.unwrap();
828+
829+
// stage one more change so 'git-absorb' won't exit early
830+
repo_utils::stage_file_changes(&ctx, &path);
831+
832+
// run 'git-absorb'
833+
let drain = slog::Discard;
834+
let logger = slog::Logger::root(drain, o!());
835+
let config = Config {
836+
and_rebase: true,
837+
dry_run: true,
838+
..DEFAULT_CONFIG
839+
};
840+
run_with_repo(&logger, &config, &ctx.repo).unwrap();
841+
842+
let mut revwalk = ctx.repo.revwalk().unwrap();
843+
revwalk.push_head().unwrap();
844+
assert_eq!(revwalk.count(), 2); // git rebase wasn't called so both commits persist
845+
let is_something_in_index = !nothing_left_in_index(&ctx.repo).unwrap();
846+
assert!(is_something_in_index);
847+
}
848+
764849
fn autostage_common(ctx: &repo_utils::Context, file_path: &PathBuf) -> (PathBuf, PathBuf) {
765850
// 1 modification w/o staging
766851
let path = ctx.join(&file_path);

src/tests/repo_utils.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#[cfg(test)]
2-
use anyhow::Result;
3-
use current_dir::Cwd;
2+
use git2::Tree;
43
use std::path::{Path, PathBuf};
54
pub struct Context {
65
pub repo: git2::Repository,
@@ -63,37 +62,27 @@ pub fn add<'r>(repo: &'r git2::Repository, path: &Path) -> git2::Tree<'r> {
6362
/// Prepare an empty repo, and stage some changes.
6463
pub fn prepare_and_stage() -> Context {
6564
let (ctx, file_path) = prepare_repo();
65+
stage_file_changes(&ctx, &file_path);
66+
ctx
67+
}
6668

69+
/// Modify a file in the repository and stage the changes.
70+
pub fn stage_file_changes<'r>(ctx: &'r Context, file_path: &PathBuf) -> Tree<'r> {
6771
// add some lines to our file
6872
let path = ctx.join(&file_path);
6973
let contents = std::fs::read_to_string(&path).unwrap();
7074
let modifications = format!("new_line1\n{contents}\nnew_line2");
7175
std::fs::write(&path, &modifications).unwrap();
7276

7377
// stage it
74-
add(&ctx.repo, &file_path);
75-
76-
ctx
78+
add(&ctx.repo, &file_path)
7779
}
7880

7981
/// Set the named repository config option to value.
8082
pub fn set_config_option(repo: &git2::Repository, name: &str, value: &str) {
8183
repo.config().unwrap().set_str(name, value).unwrap();
8284
}
8385

84-
/// Run a function while in the working directory of the repository.
85-
///
86-
/// Can be used to ensure that at most one test changes the working
87-
/// directory at a time, preventing clashes.
88-
pub fn run_in_repo<F>(ctx: &Context, f: F) -> Result<()>
89-
where
90-
F: FnOnce() -> Result<()>,
91-
{
92-
let mut locked_cwd = Cwd::mutex().lock().unwrap();
93-
locked_cwd.set(ctx.dir.path()).unwrap();
94-
f()
95-
}
96-
9786
/// Become a new author - set the user.name and user.email config options.
9887
pub fn become_author(repo: &git2::Repository, name: &str, email: &str) {
9988
let mut config = repo.config().unwrap();

0 commit comments

Comments
 (0)