Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

FWIW, I've been using this alias for the past couple years for fixup commits, and I've been happy with it:

> 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.




Nice! I use git revise[^1] a lot which does a similar thing but without the fixup commit. I I’ll try using fzf to make it interactive though. Thanks!

[^1]: https://github.com/mystor/git-revise


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.

Muchas gracias.


Yeah, I use about a dozen git aliases in my normal workflow. In case it's helpful, here are the relevant ones for this flow:

  alias git_main_branch='git rev-parse --abbrev-ref origin/HEAD | cut -d/ -f2'
  alias gapa='git add --patch'
  alias grbm='git rebase -i --autosquash $(git_main_branch)'
  alias gfx='git commit --fixup $(git log $(git_main_branch)..HEAD --oneline| fzf| cut -d" " -f1)'
Another favorite is:

  alias gmru="git for-each-ref --sort=-committerdate --count=50 refs/heads/ --format='%(HEAD) %(refname:short) | %(committerdate:relative) | %(contents:subject)'| fzf | sed -e 's/^[^[[:alnum:]]]*[[:space:]]*//' | cut -d' ' -f1| xargs -I _ git checkout _"
gmru (git most recently used) will show you the branches you've been working on recently and let you use fzf to select one to check out.


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 sounds like what `git add -p` is for, stage part of the current changes.


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.


"Automatic" sound like it would fail about as often as it would help.


Might be able to use the multimode flag in the fzf command above and it should let you select more than one using Tab and Shift+Tab.


Couldn't you just use --patch with the alias to achieve that?


I have this one in mine: https://github.com/paul/dotfiles/blob/master/git/.gitconfig#...

    # 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


Yeah, you're probably right. I guess I haven't run it on any files with spaces in the 6 years since I added it to my dotfiles.


Funny that I've been doing something nearly identical, but with way more boilerplate.

    fzfCommit() {
      local FZF_PROMPT="${FZF_PROMPT:=Commit: }"
      git log --oneline | fzf --border --prompt="$FZF_PROMPT" --height=10 --preview="git show {+1} --color=always" --no-sort --reverse | cut -d' ' -f1 | tr '\n' ' ' | sed 's/[[:space:]]$//';
    }
    function gfixup {
      local commit=$(FZF_PROMPT='Fixup Commit: ' fzfCommit)
      if [[ -z "$commit" ]]; then
        return 1
      fi
      set -x
      git commit --fixup "$commit" --allow-empty > /dev/null || return 1
      git rebase --interactive "$commit"~ --autosquash || return 1
    }


I’ve been using this:

alias gfixup="git commit -v --fixup HEAD && GIT_SEQUENCE_EDITOR=touch git rebase -i --stat --autosquash --autostash HEAD~2"

From what I understand it does the same thing as this crate for the most part. All I do after is:

git push —force-with-lease

Not sure what you get from the crate otherwise


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!


Seems like you can add —no-edit and get the same behavior, now I can delete that alias. Thanks again :)

(Edit: typo)


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.


You can also set `EDITOR=true` for that `git commit --amend` if you forget about `--no-edit`.


I guess the crate version is easier to soft reset?


With a shell such as fish, one can "git commit --fixup <tab>" and a list of commits will be displayed.


sounds like how magit lets you create fixup commits in emacs


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.


Worse in fact, since magit lets you fixup, squash, or instafix.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: