Closed hjfreyer closed 3 months ago
Here's something I whipped up quickly:
#!/bin/bash -e
TEMPDIR=$(mktemp -d)
A=$(stg top)
A_ID=$(stg id $A)
stg pop
B=$(stg top)
B_ID=$(stg id $B)
stg pop
echo $A
echo $A_ID
echo $B
echo $B_ID
stg rename $A swap-temp-a # Hold onto these in case something goes wrong...
stg rename $B swap-temp-b
git log --format=%B -n 1 $A_ID > $TEMPDIR/msg-a
git log --format=%B -n 1 $B_ID > $TEMPDIR/msg-b
stg new -n $A -f $TEMPDIR/msg-a
git checkout $A_ID .
stg refresh
stg new -n $B -f $TEMPDIR/msg-b
git checkout $B_ID .
stg refresh
stg delete swap-temp-a
stg delete swap-temp-b
Hi @hjfreyer. Thanks for posting this feature request.
I'm wondering if you are aware of stg spill
and stg pop --spill
?
Also stg push --set-tree
?
For the workflow enumerated with the starting state main > B
, I might suggest:
stg pop --spill
&& git reset
. Patch B
is not altered; the worktree contains all the modifications from patch B
.git add -i
. Stage the hunks that belong in the to-be-created patch A
.stg new --index --refresh --name A
. Create patch A
with the relevant content that has been staged in the index.git restore .
. We had modifications remaining in the worktree. If the right stuff was staged, then what remained corresponded to what we want in patch B
, but we don't need these changes because the existing patch B
already points to a tree that contains all these changes. So we reset the worktree in preparation for our next magic step...stg push --set-tree
. This special form of push says "push the next patch, but instead of treating it like a diff and applying it, just use the tree that this patch/commit already points at". So after this step, the worktree will be in the same state as before step 1, but the diff captured by patch B
will no longer include those changes that we've captured in patch A
. The final stack state is main > A > B
.There are certainly valid variations on this workflow that would achieve the same ends. For example, after step 1, you could immediately stg new --name A
(create an empty patch A
) and then iteratively stage hunks/files and capture them with stg refresh --index
.
And after step 3, instead of doing the git restore .
(which could feel scary), you could instead capture those changes in a patch stg new --refresh --name pre-B
, then do a regular stg push
of the existing patch B
, which should net you an empty patch B
, and then finally do a stg squash pre-B B
to get to one B
patch and use the original B
's message.
So with spill and stg push --set-tree
in your arsenal, I'm wondering if you still see a need for a swap
command?
--set-tree
is exactly what I needed! Thanks for the tip!
Sometimes I'm working on a change
B
, when I realize that it'd be more sensible to first have a changeA
, and then haveB
on top of that. When I'm lucky,A
andB
operate on disjoint files, but I'm pretty frequently unlucky.My normal workflow in this situation is the following:
stg new A
. Now my stack ismain > B > A
B
that doesn't belong inA
, using the diff betweenmain
andB
as a reference.stg refresh
.stg new B2
. Now my stack ismain > B > A > B2
.git checkout HEAD~~ .
, restoring everything inB2
to the way it was as ofB
.stg refresh
.stg squash B A
. Fix up all the commit messages to remove traces ofB
.B
so I can reuse it inB2
.I'd love it if there was a
stg swap A B
command that I could use at step 4. The state of the tree would be exactly the same as it was at bothA
andB
, but the order would be reversed (and therefore the diff would be inverted).I've done this procedure enough times that I probably ought to write a script to automate it, but native support would be even better!