If it only lets you select one, that's strictly less powerful. What if I want some parts of it into one commit and another parts into another? The `hg absorb` works for this case.
Yeah, it's definitely less powerful that what absorb is doing. I wasn't trying to argue that it was equivalent. I just wanted to share a bash one-liner that I've had success with in case others find it helpful.
> What if I want some parts of it into one commit and another parts into another?
Looks like absorb will automatically break out every hunk into a separate fixup commit. My one-liner will create 1 fixup commit for everything that's staged. That's typically what I need, but on the occasions it's not, I use `git add -p`, as kadoban mentioned, to stage exactly what I want for each commit.
Oh, hrm, looking at this description and the one liner, I rather like.
Once you mentioned `git add -p` I realised that this is pretty much what I do already, except with a far more efficient way of selecting the relevant commit to do it to.
Then you use `git gui`, which is part of the git distribution itself, or `tig` if TUIs are your thing. I have a key binding for `git commit --squash=%(commit)` in my tig config, so I can interactively select lines or hunks to stage and then the target commit for the squash.
That still requires you to manually select hunks. The point of `hg absorb` is to automatically select hunks even if these hunks are to be squashed into different commits.
# make a fixup commit for the last time the file was modified
cff = "!f() { [ -n $@ ] && git add $@ && git commit --fixup $(git last-sha $@); }; f"
# Get latest sha for file(s)
last-sha = log -n1 --pretty=format:%h --grep 'fixup!' --invert-grep
Given a file like `git cff path/to/file.rb`, It'll find the last commit that touched that file, and make a fixup commit for it. Its great for the try-this-change-on-CI cycle: Change the Dockerfile, git cff Dockerfile, push, repeat, without it screwing up because you changed a different file while you were working on it.
It won't matter until it does, but $@ expands arguments into separate words but those expansions are themselves only word-preserved if the $@ is itself quoted. The [ will probably get away with what you want, but its use in $(git add) and $(git last-sha) almost certainly will not
$ cat > tmp.sh <<'FOO'
for a in $@; do echo "a=$a"; done
echo
for b in "$@"; do echo "b=$b"; done
echo
for c in $*; do echo "c=$c"; done
echo
for d in "$*"; do echo "d=$d"; done
echo
FOO
$ bash tmp.sh 'alpha beta' $'charlie\ndelta'
a=alpha
a=beta
a=charlie
a=delta
b=alpha beta
b=charlie
delta
c=alpha
c=beta
c=charlie
c=delta
d=alpha beta charlie
delta
Your alias seems like a completely unecessary complexity. If you want to meld new changes into your branch head you can just alias “git commit --amend”, you don’t need that mess.
Absorb will find the commits to fix up for each change in the working copy, it doesn’t just merge everything into the head.
I see, the reason it’s that long complicated alias was that I didn’t want to open up the editor to change the commit every time I updated. “git commit —amend” does that.
I read the rough how it works and it now makes sense. I might give it a try. Thanks!
That is correct, and there is a `--edit` to revert that, so my personal alias is to `git ci --amend --no-edit` such that by default it just merges the staging into the HEAD, and then tacking on `-e` will open the commit message in an editor to expand it.
This was 100% my inspiration. I used emacs+magit for years. After switching away from emacs for dev work, I still cracked it open for git interactions for another year or so. Eventually, I moved entirely to the shell with a bunch of aliases to replicate my magit workflow.
> gfx='git commit --fixup $(git log $(git merge-base main HEAD)..HEAD --oneline| fzf| cut -d" " -f1)'
It shows you the commits on the current branch and lets you select one via fzf. It then creates the fixup commit based on the commit you selected.