2

I sometimes encounter a situation where I need to fix one line of code in a commit that is not on the tip of branch any more. (it is second or third from the top). I can of course put that last commit or two away somewhere, amend the problematic commit and then put those two back on top if it, but I was wondering if there is some one click solution

1) SourceTree
2) git command line

as I consider that whole hoopla-doopla described above a bit tedious.

It goes without saying I am talking about private code, feature branches, nothing pushed to the holy corporate repository, so changing history is not an issue here at all.

Any help, ideas?

Earl Grey
  • 7,426
  • 6
  • 39
  • 59

3 Answers3

7

When I'm in this situation I use git rebase -i, e.g., if I want to change bad_commit:

git rebase -i bad_commit^

then change the action for bad_commit in the resulting list to e or edit. Rebasing will stop with this message:

You can amend the commit now, with

    git commit --amend

Once you are satisfied with your changes, run

    git rebase --continue

You're free to make whatever changes you want to bad_commit. Then when you git rebase --continue the commits that followed it will be re-created appropriately.

Wolf
  • 4,254
  • 1
  • 21
  • 30
  • Note: this is easier than counting backwards from `HEAD` sometimes, and is otherwise exactly the same as the method in my answer. That is, there's no special magic with using `HEAD~n` vs `commit^`, both just tell `rebase -i` where the last commit to keep intact is. – torek Dec 11 '13 at 18:29
2

From the command line, if you want to modify something in the 2nd commit back:

git rebase -i HEAD~2

Change the first line from pick to edit and follow the usual procedure from there.

Note that this actually makes new commits, and does the "hoopla-doopla" as you've called it, via the interactive-rebase script; it just automates it all for you (which, well, that's what computers are for, right? :-) ).

If you have a very new commit-chain, or are reaching very far back so that (e.g.) HEAD~7 (or however far back you need to go) takes you all the way to a root commit, you may need to use:

git rebase -i --root

instead (but if you're going very far back, you likely need to be careful about rebasing across merges; see the rebase documentation).

torek
  • 448,244
  • 59
  • 642
  • 775
1

That's a "fixup". It is not a one click solution, but it is just 2 commands. Basically, you commit the change using

git commit --fixup=<commit that should have included this change> ...

and this new commit will have a marker with a comment that starts with fixup! ....

Then do a

git rebase -i --autosquash

and the rebase will automatically setup the rebase instructions so the fixup commit is move to right after the target commit and marked as 'fixup' so that the rebase will collapse the two commits together.

One nice thing is that you can wait to do the rebase/autosquash until later, so you can keep your focus on your current work. You can even collect up a few and autosquash them all at once.

If you often forget the --autosquash option, you can set it automatically

git config rebase.autosquash true

but @torek notes there are recent bug fixes to the autosquash feature and he recommends caution about using rebase.autosquash unless you use at least 1.7.10.5 or 1.8.4.

===

git commit --fixup:

--fixup=<commit>
Construct a commit message for use with rebase --autosquash.
The commit message will be the subject line from the specified
commit with a prefix of "fixup! ". See git-rebase(1) for details.

--squash=<commit>
Construct a commit message for use with rebase --autosquash.
The commit message subject line is taken from the specified commit
with a prefix of "squash! ". Can be used with additional commit
message options (-m/-c/-C/-F). See git-rebase(1) for details.

git rebase -i --autosquash

--autosquash
--no-autosquash
When the commit log message begins with "squash! ..."
(or "fixup! ..."), and there is a commit whose title
begins with the same ..., automatically modify the todo
list of rebase -i so that the commit marked for squashing
comes right after the commit to be modified, and change
the action of the moved commit from pick to squash (or fixup).
Ignores subsequent "fixup! " or "squash! " after the first,
in case you referred to an earlier fixup/squash with git
commit --fixup/--squash.
Bert F
  • 85,407
  • 12
  • 106
  • 123
  • 1
    Nice. Worth noting: there were some fixes for auto-squash modes in 1.7.10.5 and 1.8.4. I'd leave `rebase.autosquash` off if your git is older than 1.7.10.5, at least. – torek Dec 12 '13 at 06:32
  • @torek - Nice tip! Indeed, I'm using 1.8.3.4! – Bert F Dec 12 '13 at 16:05