Skip to content

Commit 0200235

Browse files
feat(split): add --discard flag
1 parent 2cce29d commit 0200235

File tree

5 files changed

+282
-11
lines changed

5 files changed

+282
-11
lines changed

git-branchless-lib/src/git/index.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use tracing::instrument;
55

66
use crate::core::eventlog::EventTransactionId;
77

8-
use super::{FileMode, GitRunInfo, GitRunOpts, GitRunResult, MaybeZeroOid, NonZeroOid, Repo};
8+
use super::{FileMode, GitRunInfo, GitRunOpts, GitRunResult, MaybeZeroOid, NonZeroOid, Repo, Tree};
99

1010
/// The possible stages for items in the index.
1111
#[derive(Copy, Clone, Debug)]
@@ -88,6 +88,12 @@ impl Index {
8888
},
8989
})
9090
}
91+
92+
/// Update the index from the given tree and write it to disk.
93+
pub fn update_from_tree(&mut self, tree: &Tree) -> eyre::Result<()> {
94+
self.inner.read_tree(&tree.inner)?;
95+
self.inner.write().wrap_err("writing index")
96+
}
9197
}
9298

9399
/// The command to update the index, as defined by `git update-index`.

git-branchless-opts/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,13 @@ pub enum Command {
663663
files: Vec<String>,
664664

665665
/// Restack any descendents onto the split commit, not the extracted commit.
666-
#[clap(action, short = 'd', long = "detach")]
666+
#[clap(action, short = 'd', long)]
667667
detach: bool,
668668

669+
/// After extracting the changes, don't recommit them.
670+
#[clap(action, short = 'D', long = "discard", conflicts_with("detach"))]
671+
discard: bool,
672+
669673
/// Options for resolving revset expressions.
670674
#[clap(flatten)]
671675
resolve_revset_options: ResolveRevsetOptions,

git-branchless/src/commands/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,19 @@ fn command_main(ctx: CommandContext, opts: Opts) -> EyreExitOr<()> {
182182

183183
Command::Split {
184184
detach,
185+
discard,
185186
files,
186187
resolve_revset_options,
187188
revset,
188189
move_options,
189190
} => {
190-
let split_mode = match detach {
191-
true => split::SplitMode::DetachAfter,
192-
false => split::SplitMode::InsertAfter,
191+
let split_mode = match (detach, discard) {
192+
(true, false) => split::SplitMode::DetachAfter,
193+
(false, true) => split::SplitMode::Discard,
194+
(false, false) => split::SplitMode::InsertAfter,
195+
(true, true) => {
196+
unreachable!("clap should prevent this")
197+
}
193198
};
194199

195200
split::split(

git-branchless/src/commands/split.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use tracing::instrument;
3838
/// What should `split` do with the extracted changes?
3939
pub enum SplitMode {
4040
DetachAfter,
41+
Discard,
4142
InsertAfter,
4243
}
4344

@@ -128,12 +129,14 @@ pub fn split(
128129
let (parent_tree, mut remainder_tree) = match (&split_mode, parent_commits.as_slice()) {
129130
// split the commit by removing the changes from the target, and then
130131
// cherry picking the orignal target as the "extracted" commit
131-
(SplitMode::InsertAfter, [only_parent]) | (SplitMode::DetachAfter, [only_parent]) => {
132+
(SplitMode::InsertAfter, [only_parent])
133+
| (SplitMode::Discard, [only_parent])
134+
| (SplitMode::DetachAfter, [only_parent]) => {
132135
(only_parent.get_tree()?, target_commit.get_tree()?)
133136
}
134137

135138
// no parent: use an empty tree for comparison
136-
(SplitMode::InsertAfter, []) | (SplitMode::DetachAfter, []) => {
139+
(SplitMode::InsertAfter, []) | (SplitMode::Discard, []) | (SplitMode::DetachAfter, []) => {
137140
(make_empty_tree(&repo)?, target_commit.get_tree()?)
138141
}
139142

@@ -218,9 +221,9 @@ pub fn split(
218221
let target_entry = target_tree.get_path(path)?;
219222
let temp_tree_oid = match (parent_entry, target_entry, &split_mode) {
220223
// added => remove from remainder commit
221-
(None, Some(_), SplitMode::InsertAfter) | (None, Some(_), SplitMode::DetachAfter) => {
222-
remainder_tree.remove(&repo, path)?
223-
}
224+
(None, Some(_), SplitMode::InsertAfter)
225+
| (None, Some(_), SplitMode::DetachAfter)
226+
| (None, Some(_), SplitMode::Discard) => remainder_tree.remove(&repo, path)?,
224227

225228
// deleted or modified => replace w/ parent content in split commit
226229
(Some(parent_entry), _, _) => {
@@ -280,6 +283,7 @@ pub fn split(
280283
}])?;
281284

282285
let extracted_commit_oid = match split_mode {
286+
SplitMode::Discard => None,
283287
SplitMode::InsertAfter | SplitMode::DetachAfter => {
284288
let extracted_tree = repo.cherry_pick_fast(
285289
&target_commit,
@@ -354,6 +358,7 @@ pub fn split(
354358
struct CleanUp {
355359
checkout_target: Option<CheckoutTarget>,
356360
rewritten_oids: Vec<(NonZeroOid, MaybeZeroOid)>,
361+
reset_index: bool,
357362
}
358363

359364
let cleanup = match (target_state, &split_mode, extracted_commit_oid) {
@@ -363,26 +368,36 @@ pub fn split(
363368
CleanUp {
364369
checkout_target: None,
365370
rewritten_oids: vec![(target_oid, MaybeZeroOid::NonZero(extracted_commit_oid))],
371+
reset_index: false,
366372
}
367373
}
368374

375+
(TargetState::CurrentBranch, SplitMode::Discard, None) => CleanUp {
376+
checkout_target: None,
377+
rewritten_oids: vec![(target_oid, MaybeZeroOid::NonZero(remainder_commit_oid))],
378+
reset_index: true,
379+
},
380+
369381
// commit to split checked out as detached HEAD, don't extend any
370382
// branches, but explicitly check out the newly split commit
371383
(TargetState::DetachedHead, _, _) => CleanUp {
372384
checkout_target: Some(CheckoutTarget::Oid(remainder_commit_oid)),
373385
rewritten_oids: vec![(target_oid, MaybeZeroOid::NonZero(remainder_commit_oid))],
386+
reset_index: false,
374387
},
375388

376389
// some other commit or branch was checked out, default behavior is fine
377-
(TargetState::CurrentBranch, _, _) | (TargetState::Other, _, _) => CleanUp {
390+
(TargetState::CurrentBranch | TargetState::Other, _, _) => CleanUp {
378391
checkout_target: None,
379392
rewritten_oids: vec![(target_oid, MaybeZeroOid::NonZero(remainder_commit_oid))],
393+
reset_index: false,
380394
},
381395
};
382396

383397
let CleanUp {
384398
checkout_target,
385399
rewritten_oids,
400+
reset_index,
386401
} = cleanup;
387402

388403
move_branches(
@@ -410,6 +425,11 @@ pub fn split(
410425
)?);
411426
}
412427

428+
if reset_index {
429+
let mut index = repo.get_index()?;
430+
index.update_from_tree(&remainder_tree)?;
431+
}
432+
413433
let mut builder = RebasePlanBuilder::new(&dag, permissions);
414434
let children = dag.query_children(CommitSet::from(target_oid))?;
415435
for child in dag.commit_set_to_vec(&children)? {

0 commit comments

Comments
 (0)