Skip to content

Commit aec9722

Browse files
fix(move --fixup): Try to get on disk fixups working
1 parent 89096e8 commit aec9722

File tree

5 files changed

+267
-145
lines changed

5 files changed

+267
-145
lines changed

git-branchless-lib/src/core/rewrite/plan.rs

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -132,28 +132,35 @@ impl ToString for RebaseCommand {
132132
commits_to_apply_oids,
133133
} => match commits_to_apply_oids.as_slice() {
134134
[] => String::new(),
135-
[commit_oid] => format!("pick {}", commit_oid),
136-
commit_oids => {
137-
// FIXME ondisk rebases are more likely to cause merge
138-
// conflicts if an ancestor fixup commit has a hunk that
139-
// intersects with the dest/target commit. (See
140-
// `test_move_fixup_ancestor_into_head`) This code extracts
141-
// the dest commit as the `pick` and remaining commits are
142-
// `fixup`s. One possible solution would be to just use the
143-
// first commit as the `pick` to preserve the application
144-
// order of the commits/hunks, then to issue a final rebase
145-
// command to clean it all up, eg
146-
// `git commit --amend --no-edit --reuse-message`
147-
// I attempted this, but it resulted in unexpected commit
148-
// graphs and empty commits.
149-
let fixups: String = commit_oids
150-
.iter()
151-
.filter(|oid| oid != &original_commit_oid)
152-
.map(|oid| format!("fixup {}\n", oid))
153-
.collect();
154-
format!("pick {}\n{}", original_commit_oid, fixups)
155-
.trim_end()
156-
.to_string()
135+
[commit_oid] => format!("pick {commit_oid}"),
136+
[pick_oid, fixup_oids @ ..] => {
137+
// The base pick+fixup plan. This will be sufficient if we
138+
// are squashing all of the commits into original_commit_oid
139+
let mut plan = vec![
140+
format!("pick {pick_oid}"),
141+
fixup_oids
142+
.iter()
143+
.map(|oid| format!("fixup {}\n", oid))
144+
.collect(),
145+
];
146+
147+
if pick_oid != original_commit_oid {
148+
// We aren't squashing into original_commit_oid, so the
149+
// squashed commit will have the wrong metadata. We need
150+
// to apply the correct metadata, hide the commit with
151+
// incorrect metadata, and register the new commit as
152+
// the rewritten version of original_commit_oid.
153+
let cleanup_plan = vec![
154+
format!("exec git commit --amend --no-edit --reuse-message {original_commit_oid}"),
155+
// FIXME This is causing "Skipping commit (was already applied upstream)" to be displayed for each fixup commit
156+
// FIXME I'm assuming that $(...) is not portable and will need to be changed
157+
"exec git branchless hook-skip-upstream-applied-commit $(git rev-parse @{1})".to_string(),
158+
format!("exec git branchless hook-skip-upstream-applied-commit {original_commit_oid} $(git rev-parse HEAD)")
159+
];
160+
plan.extend(cleanup_plan.into_iter());
161+
}
162+
163+
plan.join("\n")
157164
}
158165
},
159166
RebaseCommand::Merge {
@@ -1170,9 +1177,18 @@ impl<'a> RebasePlanBuilder<'a> {
11701177
};
11711178
let first_parent_oid = *parent_oids.first().unwrap();
11721179
first_dest_oid.get_or_insert(first_parent_oid);
1173-
acc.push(RebaseCommand::Reset {
1174-
target: OidOrLabel::Oid(first_parent_oid),
1175-
});
1180+
// FIXME this may be necessary in some fixup cases, but feels drastic
1181+
// I *think* it makes sense, though: if we're building from roots down (roots out?)
1182+
// then parents will be (should be) dealt with first anyway ... no maybe it's OK?
1183+
if !state
1184+
.constraints
1185+
.commits_to_move()
1186+
.contains(&first_parent_oid)
1187+
{
1188+
acc.push(RebaseCommand::Reset {
1189+
target: OidOrLabel::Oid(first_parent_oid),
1190+
});
1191+
}
11761192

11771193
let upstream_patch_ids = if *detect_duplicate_commits_via_patch_id {
11781194
let (effects, _progress) =

git-branchless-lib/src/core/rewrite/rewrite_hooks.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,11 +486,12 @@ pub fn hook_drop_commit_if_empty(
486486
Ok(())
487487
}
488488

489-
/// For rebases, if a commit is known to have been applied upstream, skip it
489+
/// TODO For rebases, if a commit is known to have been applied upstream, skip it
490490
/// without attempting to apply it.
491491
pub fn hook_skip_upstream_applied_commit(
492492
effects: &Effects,
493493
commit_oid: NonZeroOid,
494+
rewritten_oid: MaybeZeroOid,
494495
) -> eyre::Result<()> {
495496
let repo = Repo::from_current_dir()?;
496497
let commit = repo.find_commit_or_fail(commit_oid)?;
@@ -516,7 +517,7 @@ pub fn hook_skip_upstream_applied_commit(
516517
add_rewritten_list_entries(
517518
&repo.get_tempfile_dir(),
518519
&repo.get_rebase_state_dir_path().join("rewritten-list"),
519-
&[(commit_oid, MaybeZeroOid::Zero)],
520+
&[(commit_oid, rewritten_oid)],
520521
)?;
521522

522523
Ok(())

git-branchless/src/commands/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::convert::TryInto;
2626
use std::ffi::OsString;
2727
use std::fmt::Write;
2828
use std::path::PathBuf;
29+
use std::str::FromStr;
2930
use std::time::SystemTime;
3031

3132
use clap::Parser;
@@ -34,6 +35,7 @@ use cursive::utils::markup::StyledString;
3435
use eyre::Context;
3536
use itertools::Itertools;
3637
use lib::core::rewrite::MergeConflictRemediation;
38+
use lib::git::MaybeZeroOid;
3739
use lib::git::Repo;
3840
use lib::git::RepoError;
3941
use lib::util::ExitCode;
@@ -196,9 +198,17 @@ fn do_main_and_drop_locals() -> eyre::Result<i32> {
196198
ExitCode(0)
197199
}
198200

199-
Command::HookSkipUpstreamAppliedCommit { commit_oid } => {
201+
Command::HookSkipUpstreamAppliedCommit {
202+
commit_oid,
203+
rewritten_oid,
204+
} => {
200205
let commit_oid: NonZeroOid = commit_oid.parse()?;
201-
hooks::hook_skip_upstream_applied_commit(&effects, commit_oid)?;
206+
let rewritten_oid = MaybeZeroOid::from_str(
207+
rewritten_oid
208+
.unwrap_or_else(|| String::from("00000000"))
209+
.as_str(),
210+
)?;
211+
hooks::hook_skip_upstream_applied_commit(&effects, commit_oid, rewritten_oid)?;
202212
ExitCode(0)
203213
}
204214

git-branchless/src/opts.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ pub enum Command {
253253
/// The OID of the commit that was skipped.
254254
#[clap(value_parser)]
255255
commit_oid: String,
256+
257+
/// FIXME The (optional) OID of the commit that was skipped.
258+
#[clap(value_parser)]
259+
rewritten_oid: Option<String>,
256260
},
257261

258262
/// Initialize the branchless workflow for this repository.

0 commit comments

Comments
 (0)