Useful git tricks

At Ableton, we use git extensively, aiming for a very good git history that tends to look like literate programming. Therefore, I have accumulated a few git config and commands to help with these workflows. To get utmost confidence when writing this page, I referenced the git rebase help page, it is a very good documentation piece that I recommend you to read top to bottom one day.

Here are useful git config flags and commands that solves common situations.

useful git config flags

Avoid adding flags that are almost always used.

  1. git config --global rebase.updateRefs true
  2. git config --global rebase.rescheduleFailedExec true
  3. git config --global rebase.rebaseMerges no-rebase-cousins 
  4. git config --global rebase.autoStash true
  5. git config --global rebase.autoSquash true
  6. bonus: git config --global commit.verbose true 

They can still be disabled with --no-flag, for example --no-update-refs 

The default flags of rebase are conservative, but I found that the optional rebase algorithms/heuristics to be predictable enough that I was always using them.

So to avoid having to specify them over and over, I put them in options:

  • updateRefs: rebase branches that are on the way
  • rescheduleFailedExec: if an exec step fails, reschedule that step in the todo so that the exec step will run again when continuing the rebase
  • rebaseMerges: if the commits to rebase contain merge commits, don’t drop merge commits. I use no-rebase-cousins for simplicity but more advances use cases may require rebase-cousins instead.
  • autoStash: stash before the rebase, stash pop after the rebase
  • autoSquash: automatically resolve fixup/squash/amend/reword commits
  • commit verbose: adds to the commit message the commit diff to make it easy to read the commit content when editing the commit log. especially useful when rewording multiple commits in the same git rebase run

Intro: git-rebase-todo  file format

git rebase --interactive  will automatically call git rebase --edit-todo before starting the rebase.

pick deadbee The oneline of this commitpick fa1afe1 The oneline of the nextcommit. . .

It is just an ordered list of $command $commit-sha $some-description-that-will be-ignored :

  • pick the commit
  • drop the commit (deleting the line also drops the commit)
  • edit the commit: rebase will stop here and can be continued with git rebase --continue 
  • break : rebase will stop here
  • reword : interactively open the editor as if you did a git commit , and automatically continues once the editor is closed
  • squash : fold two or more commits into one, and open the editor to edit the commit message, and automatically continues once the editor is closed
  • fixup : fold two or more commits into one, only keeping the commit log message of the first commit

git rebase will stop if a commit fails to apply. When you are done editing and/or resolving conflicts you can continue with git rebase --continue.

FAQ: I created a branch at some point but it needs to be on origin/master 

if you have a history like this:

           X
            \
         A---M---B
        /
---o---O---P---origin/master

Suppose you want to rebase the side branch starting at A  onto origin/master . Make sure that the current HEAD is B , and call

git rebase -i -r --onto origin/master O

FAQ: I want to reorder and edit commits. How can I check that it still compiles?

pick deadbee Implement feature XXX
fixup f1a5c00 Fix to feature XXX
exec make
pick c0ffeee The oneline of the nextcommit
edit deadbab The oneline of the commit after
exec cd subdir; make test

If it stops at some point, you can continue with git rebase --continue .

You may want to check that your history editing did not break anything by running a test, or at least recompiling at intermediate points in history by using the “exec” command (shortcut “x”). You may do so by creating a todo list like this one.

The interactive rebase will stop when a command fails (i.e. exits with non-0 status) to give you an opportunity to fix the problem. You can continue with git rebase --continue.

FAQ: I want that each commit of my branch configures and builds. How can I check that every commit builds?

$ git rebase -ix "make" origin/master

This command lets you check that intermediate commits compile correctly. The todo list becomes like that:

pick 5928aea one
exec "make"
pick 04d0fda two
exec "make"

FAQ: I created commits on a branch, but then realize an earlier commit should be fixed

  1. git add fileA.txt
  2. git commit -m "A"
  3. git add fileB.txt
  4. git commit -m "B"
  5. echo "lorem ipsum" >> fileA.txt 
  6. git add fileA.txt
  7. git commit --fixup COMMIT-SHA

You can continue working as long as needed. Eventually then, you can rebase all the changes and the TODO will automatically contain fixup todos.

  1. git rebase -i origin/master 

Also, if you want to modify the commit log message you can:

  1. git commit --fixup=amend:COMMIT-SHA that will modify the content, and replace the commit log message
  2. git commit --fixup=reword:COMMIT-SHA that will only replace the commit log message

FAQ: One old commit I did is too big. How can I split into two commits?

  1. git rebase -i COMMIT-TO-SPLIT-SHA^  ( ^ means parent )
  2. mark the commit to split with the action “edit”
  3. git reset HEAD^ 
  4. git add  (possibly interactively) or git gui 
  5. git commit with the appropriate message
  6. repeat last two steps until working tree is clean
  7. git rebase --continue 

FAQ: One old commit do not belong to this branch. How can I split into two branches?

This commit is largely inspired from the git rebase documentation. Let us imagine a branch called matthieutalbot-cmake , that contains a commit modifying some tls support (unrelated to cmake). First, we create the needed branch

$ git checkout -b matthieutalbot-tlsv1.3&& git checkout -

then rebase todo will look like

label onto
reset onto
pick 192837Switch fromGNU Makefiles to CMake
pick 5a6c7eDocument the switch to CMake
pick 918273Fix detection of OpenSSL inCMake
pick afbecd http: add support forTLS v1.3
pick fdbaec Fix detection of cURL inCMake on Windows
update-ref refs/heads/matthieutalbot-tlsv1.3

and you can make it look like that

label onto
reset onto
pick afbecd http: add support forTLS v1.3
update-ref refs/heads/mtt-tlsv1.3
pick 192837Switch fromGNU Makefiles to CMake
pick 5a6c7eDocument the switch to CMake
pick 918273Fix detection of OpenSSL inCMake
pick fdbaec Fix detection of cURL inCMake on Windows

The one commit in this list that is not related to CMake may very well have been motivated by working on fixing all those bugs introduced by switching to CMake, but it addresses a different concern. To split this branch into two topic branches, the todo list could be edited like this.

label/reset/merge  instructions are explained in the --rebase-merges documentation: in contrast to a regular interactive rebase, there are label, reset and merge commands in addition to pick ones.

The label command associates a label with the current HEAD when that command is executed. (These labels are created as worktree-local refs (refs/rewritten/<label>) that will be deleted when the rebase finishes. That way, rebase operations in multiple worktrees linked to the same repository do not interfere with one another. )

The reset command resets the HEAD, index and worktree to the specified revision. It is similar to an exec git reset --hard <label>, but refuses to overwrite untracked files.

For completeness, there is also the merge command which merges the specified revision(s) into whatever is HEAD at that time. -c  or -C  With -C <original-commit>, the commit message of the specified merge commit will be used. When the -C is changed to a lower-case -c, the message will be opened in an editor after a successful merge so that the user can edit the message.

Failed label , reset and merge commands are always rescheduled.

FAQ: I have updated multiple branches. How can I push them all at once?

By using some convention for branch naming, you can then use this snipped (if I prepend all my branches with matthieutalbot-)

$ cat >> $HOME/.bashrc <<'HERE_DOC'
function gpushforce (){
    git branch --list'matthieutalbot-*'--format='%(refname:lstrip=2)'|
       xargs -t git push --force-with-lease origin}
HERE_DOC

FAQ: I have pushed multiple branches on one computer. How can I pull them all at once?

By using the same convention for branch naming as gpushforce (see above)

$ cat >> $HOME/.bashrc <<'HERE_DOC'
function gfetchforce (){
    echo "Careful not to loose any local commits!"
git branch --list ${BRANCH_PREFIX}* --format='+%(refname:lstrip=2):%(refname:lstrip=2)' |
git fetch origin --verbose --update-head-ok --stdin
HERE_DOC

Leave a Comment