@@ -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) =
0 commit comments