44//! that are already tracked in the repo. Following the amend,
55//! the command performs a restack.
66
7+ use std:: convert:: TryFrom ;
78use std:: ffi:: OsString ;
89use std:: fmt:: Write ;
910use std:: time:: { SystemTime , UNIX_EPOCH } ;
@@ -25,10 +26,13 @@ use tracing::instrument;
2526
2627use crate :: opts:: { MoveOptions , ResolveRevsetOptions } ;
2728use lib:: core:: config:: get_restack_preserve_timestamps;
29+ use lib:: core:: dag:: commit_set_to_vec;
2830use lib:: core:: effects:: Effects ;
2931use lib:: core:: eventlog:: { Event , EventLogDb , EventReplayer } ;
3032use lib:: core:: formatting:: Pluralize ;
31- use lib:: git:: { AmendFastOptions , GitRunInfo , MaybeZeroOid , Repo , ResolvedReferenceInfo } ;
33+ use lib:: git:: {
34+ AmendFastOptions , GitRunInfo , MaybeZeroOid , NonZeroOid , Repo , ResolvedReferenceInfo ,
35+ } ;
3236
3337/// Amends the existing HEAD commit.
3438#[ instrument]
@@ -37,6 +41,7 @@ pub fn amend(
3741 git_run_info : & GitRunInfo ,
3842 resolve_revset_options : & ResolveRevsetOptions ,
3943 move_options : & MoveOptions ,
44+ reparent : bool ,
4045) -> eyre:: Result < ExitCode > {
4146 let now = SystemTime :: now ( ) ;
4247 let timestamp = now. duration_since ( SystemTime :: UNIX_EPOCH ) ?. as_secs_f64 ( ) ;
@@ -46,7 +51,7 @@ pub fn amend(
4651 let event_replayer = EventReplayer :: from_event_log_db ( effects, & repo, & event_log_db) ?;
4752 let event_cursor = event_replayer. make_default_cursor ( ) ;
4853 let references_snapshot = repo. get_references_snapshot ( ) ?;
49- let dag = Dag :: open_and_sync (
54+ let mut dag = Dag :: open_and_sync (
5055 effects,
5156 & repo,
5257 & event_replayer,
@@ -76,6 +81,21 @@ pub fn amend(
7681 return Ok ( ExitCode ( 1 ) ) ;
7782 }
7883
84+ let build_options = BuildRebasePlanOptions {
85+ force_rewrite_public_commits : move_options. force_rewrite_public_commits ,
86+ dump_rebase_constraints : move_options. dump_rebase_constraints ,
87+ dump_rebase_plan : move_options. dump_rebase_plan ,
88+ detect_duplicate_commits_via_patch_id : move_options. detect_duplicate_commits_via_patch_id ,
89+ } ;
90+ let commits_to_verify = dag. query ( ) . descendants ( CommitSet :: from ( head_oid) ) ?;
91+ let commits_to_verify = dag. filter_visible_commits ( commits_to_verify) ?;
92+ if let Err ( err) =
93+ RebasePlanPermissions :: verify_rewrite_set ( & dag, & build_options, & commits_to_verify) ?
94+ {
95+ err. describe ( effects, & repo) ?;
96+ return Ok ( ExitCode ( 1 ) ) ;
97+ } ;
98+
7999 let event_tx_id = event_log_db. make_transaction_id ( now, "amend" ) ?;
80100 let ( snapshot, status) =
81101 repo. get_status ( effects, git_run_info, & index, & head_info, Some ( event_tx_id) ) ?;
@@ -146,6 +166,12 @@ pub fn amend(
146166 ) ?;
147167 mark_commit_reachable ( & repo, amended_commit_oid)
148168 . wrap_err ( "Marking commit as reachable for GC purposes." ) ?;
169+ dag. sync_from_oids (
170+ effects,
171+ & repo,
172+ CommitSet :: empty ( ) ,
173+ CommitSet :: from ( amended_commit_oid) ,
174+ ) ?;
149175
150176 let rebase_plan = {
151177 let build_options = BuildRebasePlanOptions {
@@ -173,6 +199,28 @@ pub fn amend(
173199 builder. move_subtree ( head_oid, head_commit. get_parent_oids ( ) ) ?;
174200 builder. replace_commit ( head_oid, amended_commit_oid) ?;
175201
202+ // To keep the contents of all descendant commits the same, forcibly
203+ // replace the children commits, and then rely on normal patch
204+ // application to apply the rest.
205+ if reparent {
206+ let descendants = dag
207+ . query ( )
208+ . descendants ( CommitSet :: from ( head_oid) ) ?
209+ . difference ( & CommitSet :: from ( head_oid) ) ;
210+ let descendants = dag. filter_visible_commits ( descendants) ?;
211+ for descendant_oid in commit_set_to_vec ( & descendants) ? {
212+ let parents = dag. query ( ) . parent_names ( descendant_oid. into ( ) ) ?;
213+ builder. move_subtree (
214+ descendant_oid,
215+ parents
216+ . into_iter ( )
217+ . map ( NonZeroOid :: try_from)
218+ . try_collect ( ) ?,
219+ ) ?;
220+ builder. replace_commit ( descendant_oid, descendant_oid) ?;
221+ }
222+ }
223+
176224 let thread_pool = ThreadPoolBuilder :: new ( ) . build ( ) ?;
177225 let repo_pool = RepoResource :: new_pool ( & repo) ?;
178226 match builder. build ( effects, & thread_pool, & repo_pool) ? {
0 commit comments