Skip to content

Commit 2d70309

Browse files
committed
Retarget intermediate branches during rebase
1 parent a32f2a7 commit 2d70309

File tree

2 files changed

+70
-13
lines changed

2 files changed

+70
-13
lines changed

src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ fn do_rebase_inner(
138138
) -> Result<(), anyhow::Error> {
139139
let sig = repo.signature()?;
140140

141+
let mut branches: HashMap<Oid, Branch> = HashMap::new();
142+
for (branch, _type) in repo.branches(Some(git2::BranchType::Local))?.flatten() {
143+
let oid = branch.get().peel_to_commit()?.id();
144+
// TODO: handle multiple branches pointing to the same commit
145+
branches.insert(oid, branch);
146+
}
147+
141148
match rebase.next() {
142149
Some(ref res) => {
143150
let op = res.as_ref().map_err(|e| anyhow!("No commit: {}", e))?;
@@ -169,7 +176,12 @@ fn do_rebase_inner(
169176
Some(Pick) => {
170177
let commit = repo.find_commit(op.id())?;
171178
if commit.message() != fixup_message {
172-
rebase.commit(None, &sig, None)?;
179+
let new_id = rebase.commit(None, &sig, None)?;
180+
if let Some(branch) = branches.get_mut(&commit.id()) {
181+
branch
182+
.get_mut()
183+
.set_target(new_id, "git-fixup retarget historical branch")?;
184+
}
173185
}
174186
}
175187
Some(Fixup) | Some(Squash) | Some(Exec) | Some(Edit) | Some(Reword) => {

tests/basic.rs

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,10 @@ fn simple_straightline_commits() {
7676
let td = assert_fs::TempDir::new().unwrap();
7777
git_init(&td);
7878

79-
for n in &["a", "b"] {
80-
git_file_commit(n, &td);
81-
}
79+
git_commits(&["a", "b"], &td);
8280
git(&["checkout", "-b", "changes"], &td);
8381
git(&["branch", "-u", "main"], &td);
84-
for n in &["target", "d"] {
85-
git_file_commit(&n, &td);
86-
}
82+
git_commits(&["target", "d"], &td);
8783

8884
let log = git_log(&td);
8985
assert_eq!(
@@ -122,14 +118,10 @@ fn test_no_commit_in_range() {
122118
let td = assert_fs::TempDir::new().unwrap();
123119
git_init(&td);
124120

125-
for n in &["a", "b", "c", "d"] {
126-
git_file_commit(n, &td);
127-
}
121+
git_commits(&["a", "b", "c", "d"], &td);
128122
git(&["checkout", "-b", "changes", ":/c"], &td);
129123
git(&["branch", "-u", "main"], &td);
130-
for n in &["target", "f", "g"] {
131-
git_file_commit(&n, &td);
132-
}
124+
git_commits(&["target", "f", "g"], &td);
133125

134126
let out = git_log(&td);
135127
assert_eq!(
@@ -171,9 +163,62 @@ new
171163
);
172164
}
173165

166+
#[test]
167+
fn retarget_branches_in_range() {
168+
let td = assert_fs::TempDir::new().unwrap();
169+
git_init(&td);
170+
171+
git_commits(&["a", "b"], &td);
172+
git(&["checkout", "-b", "intermediate"], &td);
173+
git_commits(&["target", "c", "d"], &td);
174+
175+
git(&["checkout", "-b", "changes"], &td);
176+
git_commits(&["e", "f"], &td);
177+
178+
let expected = "\
179+
* f HEAD -> changes
180+
* e
181+
* d intermediate
182+
* c
183+
* target
184+
* b main
185+
* a
186+
";
187+
let out = git_log(&td);
188+
assert_eq!(out, expected, "log:\n{}\nexpected:\n{}", out, expected);
189+
190+
td.child("new").touch().unwrap();
191+
git(&["add", "new"], &td);
192+
193+
fixup(&td).args(&["-P", "target"]).assert().success();
194+
195+
let (files, err) = git_changed_files("target", &td);
196+
197+
assert_eq!(
198+
files,
199+
"\
200+
file_target
201+
new
202+
",
203+
"out: {} err: {}",
204+
files,
205+
err
206+
);
207+
208+
// should be identical to before
209+
let out = git_log(&td);
210+
assert_eq!(out, expected, "\nactual:\n{}\nexpected:\n{}", out, expected);
211+
}
212+
174213
///////////////////////////////////////////////////////////////////////////////
175214
// Helpers
176215

216+
fn git_commits(ids: &[&str], tempdir: &assert_fs::TempDir) {
217+
for n in ids {
218+
git_file_commit(n, &tempdir);
219+
}
220+
}
221+
177222
fn git_init(tempdir: &assert_fs::TempDir) {
178223
git(&["init", "--initial-branch=main"], &tempdir);
179224
git(&["config", "user.email", "[email protected]"], &tempdir);

0 commit comments

Comments
 (0)