[HN Gopher] Correct Git commits with git-autofixup
___________________________________________________________________
Correct Git commits with git-autofixup
Author : tosh
Score : 75 points
Date : 2021-03-18 08:55 UTC (14 hours ago)
(HTM) web link (symflower.com)
(TXT) w3m dump (symflower.com)
| Tobu wrote:
| git-absorb is another implementation:
|
| - https://github.com/tummychow/git-absorb
|
| - https://lib.rs/crates/git-absorb
| NateEag wrote:
| Can anyone give a comparison of the two from experience?
|
| I'm painfully familiar with the problem they solve and am
| delighted to discover they exist.
|
| I'd love some input about how they differ.
| Ambroisie wrote:
| I have only used `git-absorb`, but one difference from
| reading the article is that it does its own analysis of your
| previous commits to choose what each line should `fixup`.
| Because of this, `git-absorb` can only fix up commits up to a
| specified base (10 commits earlier by default) when you use
| it.
| krobelus wrote:
| > does its own analysis of your previous commits
|
| Not really; you can pass any commit ref to git-autofixup,
| and it will create commits only for commits since that ref.
| So it's for the user to decide :)
| krobelus wrote:
| I have looked at both but only used git-autofixup. Some
| observations:
|
| - git-autofixup has a changelog
|
| - git-autofixup includes sober, technical documentation that
| actually explains how it works. OTOH, git-absorb has a great
| elevator pitch for users that are not so familiar with Git.
|
| - git-autofixup does not have unresolved bugs, unlike git-
| absorb
|
| - git-absorb creates fixup commits for the last 10 commits,
| which seems really odd. It's better to use symbolic
| references like @{upstream}, but it looks like they dont'
| support this yet?
|
| - git-autofixup is stable and mostly done software, while
| git-absorb has a sizeable list of todos
|
| - git-autofixup is written in Perl (like some tools in Git
| itself), making it easier to install than git-absorb which
| uses Rust.
|
| Then again, as author of this article, I'm obviously biased
| ;)
| hashkb wrote:
| For this issue and so many others, everyone should just get
| comfortable with interactive rebase. Crutches like this trap you
| in this space of never really learning git. And that's a tragedy.
| All because you're impatient? Afraid?
| quesera wrote:
| Coming from the other direction, I am very familiar with
| interactive rebase, and I use it on my working branch all the
| time before pushing.
|
| I am struggling to understand the use case for autofixup and
| absorb. The descriptions talk about post-code-review changes,
| which means the branch has been pushed. So are these tools only
| for a flow that uses force-pushing regularly?
|
| I don't think that would be a feature for my teams. We think of
| code review as an event in time, and changes post-code review
| should be clearly differentiated from changes pre-code review.
|
| Am I missing the point?
| vlovich123 wrote:
| I rely on code review tools that can show me the difference
| between two patch sets. I really love Phabricator for that
| ability - push a set of commits and each gets a review.
| Absorb any fixes, push and suddenly each commit has a new
| version that you can diff against old ones if you care. It
| makes reviewing the patch set in totality trickier. However,
| since I'm practice you want individual commits within a set
| to be standalone ones, I've rarely missed the ability.
| Vinnl wrote:
| I generally use autosquash when a code review of a PR turns
| up things I missed, which should have been part of a
| particular commit on that PR's branch. However, I don't want
| to amend the previous commit right away if I'd like another
| review for the new changes. Thus, I do a `git commit --fixup
| <HEAD|hash>` and push it. I can then ask the review to only
| look at the new commit, I won't break their local checkouts
| of the branch, and only when everything's been approved I run
| `git rebase --autosquash` and then merge it in.
|
| (Before I started using autosquash for this, I'd do the same,
| except I had to figure out at the time of merging onto which
| commit every new commit had to be fixed up, rather than at
| the time of writing it.)
| Vinnl wrote:
| This whole article seems to hinge on the fact that you already
| know how to run an interactive rebase though?
|
| As in, I'm very comfortable doing an interactive rebase, but
| have to figure out the correct commit to fixup on is tedious,
| so it sounds very useful to me if a tool can help me with that.
| globular-toast wrote:
| Unfortunately I think fixup commits are one of the least
| understood parts of git. People seem to confuse them with
| rebasing. They support rebasing but don't replace it.
| zimmski wrote:
| Learning rebasing is one thing but wasting hours every week on
| rebasing because you are doing the same thing over and over
| again is another. When i do a one line fixup i just do not want
| to figure out the correct commit and then rebase i just want it
| to happen because i already know how i would rebase that: you
| just use git-autofixup or other similar tools discussed here to
| get it done.
| tomxor wrote:
| It's possible the other reason is trying to manipulate git
| history too frequently. Keep in mind this comes from a
| prolific git history abuser and rebaser... if hacking on a
| branch, a trail of messy commits is fine, it's your work in
| progress, it's also useful if you messed up otherwise you are
| forced to use reflog. Rebase when you are at the final stage
| of tidying things up and squashing commits down into a
| legible history, not on every single amendment... for this
| style of history rewriting rebase is perfect because you have
| the full power to rearrange rewrite and squash commits.
| zimmski wrote:
| Totally agree. I guess what i am trying to say is that
| learning rebase is a must and powerful tool but if you are
| doing the same things again and again you should think
| about automating them and use your thinking-power and time
| for non-automated things.
| urxvtcd wrote:
| I'd disagree. The longer the branch to rebase the larger
| the chance of conflicts occuring.
| hashkb wrote:
| Rebase early and often. Always keep a close eye on upstream
| while you're working downstream. Otherwise you're making it
| harder on yourself when you do that final cleanup.
| tomxor wrote:
| That's a fair point, if you are working against a fast
| moving target that forces you to rebase onto it often
| then you really want a short and simple history... a
| trail of WIPs or changes on top of changes tend blow up
| into a cascade of conflicts.
|
| However in my original comment I was actually just
| referring to the scenario where you only want to rewrite
| history onto the same base, in which case I stand by my
| suggestion... In fact I find this can be a good strategy
| if your history has grown and you also need to rebase
| onto a new upstream - i.e first clean up your history
| onto the same base with a `git rebase -i current-base`
| and squash it all down and tidy it up, then do the `git
| rebase upstream` so you can do conflict resolution with
| more holistic commit diffs.
| hashkb wrote:
| Practice. You'll get better and faster. I do it tens of times
| daily and each fixup takes ten or fifteen seconds.
| zimmski wrote:
| Yep. I have that practice too and i thought that was good
| enough but with git-autofixup and my posted alias i am down
| to under one second. So it will save you 1-14 sesconds of
| your life with every fixup you do... unless it cannot be
| done automatically. Try it and let us know what you think
| :-)
| zimmski wrote:
| git-autofixup definitely changed my daily development life.... I
| mostly use it with this Git alias
|
| ``` autofixup = !"autofixup origin/master --exit-code; test $?
| -lt 2 && GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash
| $(git merge-base HEAD origin/master)" ```
|
| Works almost every time perfectly, and when not i always get a
| comment during a code review.
| jasonpeacock wrote:
| > when not i always get a comment during a code review.
|
| Why during CR? Is the error not obvious to you? You should be
| able to check the results of your changes before pushing,
| right?
| zimmski wrote:
| I/we do but sometimes you just overlook something especially
| if there are a lot of changes. But having someone else review
| what you do almost completely removes that problem.
|
| I usually do not look through all commits again when a basic
| review already happend and i "just" integrate the review
| comments. That is where for me personally these problems
| happen that git-autofixup is sometimes wrong but in over a
| few thousand commits in the last months this happend only
| thrice. Which for me just speaks for the tool IMHO. Ow the
| original author big time :-)
| rollcat wrote:
| If you're an Emacs user, I must recommend magit
| (https://magit.vc/) - the interactive rebasing (including
| squashing/fixups) is one of the best UIs (overall, not just git)
| I've seen in my life.
|
| I'd switch away from Emacs but magit keeps me hooked!
| shepmaster wrote:
| Does Magit have a similar feature to what is discussed in this
| article? Specifically "magically figure out which previous
| commit these changes should be combined with"?
| globular-toast wrote:
| It doesn't do it magically, but it makes it a lot easier to
| do. Easy enough that I've never really wanted a tool like
| this.
|
| Having said that, if autofixup works well it might be worth
| integrating it into magit via a plugin as it would certainly
| reduce the repetition.
| shepmaster wrote:
| Could you share what your workflow would be? I'm assuming
| you put the point on the line you want to fix, then use
| Magit to start a rebase at the last commit that changed
| that line, then apply your changes, then continue?
|
| I'm a casual Magit user, so learning from other users would
| be very beneficial.
| globular-toast wrote:
| I typed it out but HN seems to be rate limiting me or
| something and tells me I'm "posting too fast". I seem to
| be able to post shorter comments, though.
|
| I've been meaning to write an article on a fixup workflow
| for a while now. Just need to find somewhere to host it.
| shepmaster wrote:
| You could throw it in a gist to start with.
| jpeloquin wrote:
| That works. My habit is to make the fix, commit it as
| "!fixup whatever", start an interactive rebase, move the
| fixup commit just after the bad commit (M-|), press f on
| the fixup commit to tell rebase to merge it with the bad
| commit, and run the rebase (C-c C-c). This is pretty much
| the same method as yours, but committing first means the
| fixup will still be there if you have to cancel & redo
| the rebase for some reason, such as unexpected and
| confusing merge conflicts.
| shepmaster wrote:
| Right, that process is what I do now, outside of Magit.
| The point of this article is a tool that (attempts to)
| automatically find the correct commit to combine with.
| Conceptually it removes the effort taken to move the
| fixup commit to the right spot; both keystroke-wise and
| mental effort wise.
|
| That's what I'm looking for: a Magit-native way to make
| my little fixups without having to manually track which
| fixup should be applied where in history.
| globular-toast wrote:
| > move the fixup commit just after the bad commit (M-|),
| press f on the fixup commit to tell rebase to merge it
| with the bad commit,
|
| That's what --autosquash is for. If you do fixup commits
| correctly (using --fixup), it will put them in the
| correct place and mark them as fixups for you.
| shepmaster wrote:
| > using --fixup
|
| If I understand you correctly, this requires that I think
| about which commit I want to fixup. Once identified, I
| can mark my fixup with what commit it should be combined
| with automatically.
|
| My understanding of the article is that it attempts to
| automatically figure out which commit to combine with.
| That's the feature that I'm interested in.
| globular-toast wrote:
| Yeah, it does require you to think about it. But magit
| has really quick and easy diffing and blame so it can
| make it significantly easier. In practice I rarely have
| to think very hard before knowing which commit to fixup.
| jpeloquin wrote:
| So I guess what I should be doing is make fix - magit-
| blame to get the commit ID for the last prior edit - copy
| commit ID or message to clipboard - magit commit with `c
| F`, C-s to the commit ID I just copied, and C-c C-c to
| set up and run the autosquash rebase.
| shepmaster wrote:
| This process is what I think the article describes and
| what I'm curious if can be automated by magit itself.
| jpeloquin wrote:
| Magit appears to have an integration with 'git absorb'.
| The issue that tracked the integration discussed git
| absorb as providing similar function to git autofixup. I
| don't particularly desire unsupervised changes to my
| commit history though so don't plan on looking into it
| further.
|
| https://github.com/magit/magit/issues/3053
| Amorymeltzer wrote:
| Magit incorporates git-autofixup directly via `magit-commit-
| autofixup`; also available is `magit-commit-absorb` for git-
| absorb.
| sundarurfriend wrote:
| How does Vim's fugitive (https://github.com/tpope/vim-
| fugitive/) plugin compare to this, in case anyone here has used
| them both? I've only used it for some basic things so far, and
| it seems nice enough, but I'm wondering if it's good with more
| advanced git-fu like the above.
| pjc50 wrote:
| Hmm. This sounds like my workflow with gerrit, which goes like
| this:
|
| - each work item has its own branch. When pushed, gerrit turns
| this into a review.
|
| - a git alias, "alias.fixlast=commit -a --amend --no-edit" which
| amends the last commit to match the working directory
|
| Plus these configurations:
|
| branch.autosetuprebase=local
|
| branch.autosetupmerge=always
|
| pull.rebase=true
|
| The effect is that every branch I create off the main "develop"
| branch is automatically set up to rebase from that branch when I
| do "git pull". So when I get some review comments for review
| "xyz", I just do checkout - pull - make changes - fixup - push.
|
| Gerrit's workflow is slighly unusual in that each review must be
| a single git change, but it keeps a history of its own of
| previous changes you made to that review.
| chrismorgan wrote:
| An excerpt from my ~/.gitconfig, showing a related approach
| (piggy-backing on git-revise): [alias] #
| Revise into the commit that last changed File rf = "!f() {
| if [ $# -eq 0 ]; then REV=\"$(git status --porcelain --untracked-
| files=no | sed '/^ /d;s/^.. //' | xargs -n1 git rev-list -1 HEAD
| --)\"; NUM_REVS=\"$(echo \"$REV\" | wc -l)\"; if [ $NUM_REVS -ne
| 1 ]; then >&2 echo Files in the index were not all last modified
| in the same commit; exit 1; fi; else REV=\"$(git rev-list -1 HEAD
| -- \"$1\")\"; shift; fi; git revise \"$REV\" \"$@\"; }; f"
|
| (This alias is three times as long as my next longest aliases,
| which are ports of Mercurial's id, tip, incoming and outgoing
| commands.)
|
| This is more coarse-grained than the technique in this article,
| as it only goes down to the file level--because that was
| sufficient for me when I wrote the alias, and probably easier to
| implement. I've been vaguely contemplating trying git-autofixup
| and git-absorb for a while too, which include what are
| essentially more polished and powerful versions of my alias.
| muxator wrote:
| hg incoming and hg outgoing are really useful.
|
| When I am in git, I use this version from
| https://github.com/sympy/sympy/wiki/Git-hg-rosetta-
| stone#set...: [alias] outgoing = !git
| fetch && git log FETCH_HEAD.. incoming = !git fetch &&
| git log ..FETCH_HEAD
|
| Feature-wise, the mercurial command is way more powerful:
| https://www.mercurial-scm.org/doc/hg.1.html#incoming
| chrismorgan wrote:
| My aliases for those two deals with remote-tracking branches
| (see git-rev-parse(1) for documentation of the @{upstream}
| part if you're not familiar with it). Might as well provide a
| more full excerpt: # I almost always use glog
| rather than log. glog = log --graph # "Short log"
| slog = log --graph --oneline # `git id` = `git rev-list
| --max-count=1` with default refspec HEAD. id = "!f() {
| case \"x$1\" in x-*|x) refspec=HEAD;; *) refspec=\"$1\";
| shift;; esac; git rev-list --max-count=1 \"$refspec\" \"$@\";
| }; f" # `git tip` = `git log --max-count=1` with
| default refspec HEAD. tip = "!f() { case \"x$1\" in
| x-*|x) refspec=HEAD;; *) refspec=\"$1\"; shift;; esac; git
| log --max-count=1 \"$refspec\" \"$@\"; }; f" # `git
| out` = glog commits that exist locally but not on the
| upstream branch. No way to refer to different origins, sorry.
| out = "!f() { case \"x$1\" in x-*|x) branch=;; *)
| branch=\"$1\"; shift;; esac; git glog
| \"$branch@{upstream}..$branch\" \"$@\"; }; f" sout =
| "!f() { case \"x$1\" in x-*|x) branch=;; *) branch=\"$1\";
| shift;; esac; git slog \"$branch@{upstream}..$branch\"
| \"$@\"; }; f" # `git in` = glog commits that exist on
| the upstream branch (remember to fetch them first) but not
| locally. in = "!f() { case \"x$1\" in x-*|x) branch=;;
| *) branch=\"$1\"; shift;; esac; git glog
| \"$branch..$branch@{upstream}\" \"$@\"; }; f" sin =
| "!f() { case \"x$1\" in x-*|x) branch=;; *) branch=\"$1\";
| shift;; esac; git slog \"$branch..$branch@{upstream}\"
| \"$@\"; }; f"
| taisalie wrote:
| You know that can go in $PATH/git-rf, right.
| chrismorgan wrote:
| If it grew much bigger I'd do that, but until it's definitely
| too large, keeping all of this stuff in ~/.gitconfig is
| simpler, especially for sharing across heterogeneous
| machines.
| taisalie wrote:
| You can add a version-controlled directory to your PATH and
| put scripts in it. This can be in the same repository as
| your .gitconfig. So you can graduate aliases to scripts.
| chrismorgan wrote:
| Copying a single file is regularly easier, and more
| flexible when working on machines not set up with your
| SSH keys or whatever.
| rurban wrote:
| I'll keep using mine. It advances on magits autofixup by fixing
| up multiple files. And it checks if the commit was already
| published. git blame is not needed.
|
| https://github.com/rurban/home-bin/blob/master/git-cifixup
| samwestdev wrote:
| Sublime Merge do the same thing in just a couple of clicks, just
| saying
| globular-toast wrote:
| I doubt it. There's an open feature request currently for
| adding fixup commits. This article goes a step beyond that. Are
| you sure you understood the article?
| ruffel wrote:
| Select two commits. Edit Commit -> Squash Selected Commits,
| ignoring new messages (fixup)
| globular-toast wrote:
| That's not a fixup commit, it's a rebase. It's missing the
| point of fixup commits which is to commit now and defer the
| rebase until later.
|
| It's also completely missing the point of the feature under
| discussion here which is to automatically decide which
| commit to squash the fix into.
| yegle wrote:
| This is a lot like hg absorb. https://github.com/quark-
| zju/timepad/blob/master/assets/hgab... explained `hg absorb`
| pretty well with good visual.
___________________________________________________________________
(page generated 2021-03-18 23:02 UTC)