Thiemo Mättig

Thiemo’s Weblog

I keep quoting the hilarious »Memorizing Six Git Commands – A Practical Guide« by @ThePracticalDev, because I find it so true. Quick, can you name more than six? It was about time I would list mine. So here they are, extracted from my Bash history, in alphabetical order:

git add -p <optionalPathsOrFileNames>. The plain git add marks local changes to be committed in the next step – a process Git calls »staging«. The -p option (for »patch«) allows to not only pick files, but individual »hunks« from each file. I heavily use this feature for two purposes: It allows me to review the changes I made locally in detail, line by line, before I hand them in as a commit. I will, for example, typically find some debug lines I missed, and can skip them interactively by pressing the N key. It also allows me to pile up some minor changes locally, and still hand them in as individual commits, even if they all happen to be in the same file. The later is a result of me always following the »Boy Scout Rule« (also referred to as »Opportunistic Refactoring«): While working on the code necessary to fulfill a story, I often find something to clean up left and right, e.g. missing type hints or unused code. Having the -p option at hand allows me to clean such code up the moment I see it, and still hand in clean, easy to review commits.

git blame is only listed here to document the fact that I never use it on the command line, but in my IDE and the web interfaces Gerrit and GitHub provide.

git checkout -b <newBranchName> creates a new branch. More precisely, it assigns a new branch name to the current head. I got used to this form of the command, but found me wondering why while collecting this list. Shouldn't git branch <newBranchName> do the same? The answer is »not exactly«: git branch only creates the branch, but does not make it the current one. git checkout -b does both.

git checkout -t origin/<branchNameOnGitHub> downloads a change from GitHub into a local, equally named branch, so I can continue working on it. Gerrit requires to use git review -d <…> instead.

git checkout <otherBranchName> <fileNames> is what I typically use instead of a cherry-pick. I checkout the branch that contains the changes I'm interested in, then move to a new branch and pick the files I want with a few specific git checkout commands.

git checkout master goes back to the default branch after I finished working on the code and sending it in. I find it useful to make sure I'm always on the master branch by default, so I avoid accidentally looking at old code, for example.

git commit puts all the changes I staged with git add before into a new commit I can then push or send in for review. git commit will also ask me for a commit message, which needs to follow a specific format when used along with the Gerrit code review system.

git commit --amend is an exceptional case when working with GitHub, but the default when working with Gerrit. The difference is subtle but tells a lot about the different conceptual ideas behind the two systems. Gerrit does not track edits made to a change (in GitHub-slang »pull request«) via a sequence of commits, but via a unique »change ID« that is stored in the last line of the commit message. Amending a commit means replacing it with a different one, but that's fine, because Gerrit will continue to track previous iterations of a change separately. GitHub on the other hand looses information about the previous state of a commit if it gets amended. If the history of a GitHub pull request should be easily visible to others, the GitHub-way is to build a sequence of commits stacked on top of each other. This also means one can choose to opt-out of that on GitHub by squashing or amending commits, while Gerrit does not allow that.

git d <HEAD|HEAD^|HEAD^^|optionalTagOrCommitId> is the only self-made shortcut I regularly use. My d expands to git diff -w --color-words. The --color-words option not only – literally – colors words, it also greatly reduces the size of diffs by showing small changes inline, instead of always repeating entire lines. The -w option ignores all whitespace. Both options help focusing on what's essential in a change. Whitespace typically isn't. In the rare situations where it is, I type git diff instead. Here is how you can add this alias via the command line: git config --global alias.d 'diff -w --color-words'.

git fetch --all syncs all local branches with what happened on the remote in the meantime, without actually changing anything in the local branches. This command helps getting rid of the warning message that appears when trying to push something to the remote, but the local base branch is not identical any more to the one on the remote. This is not a problem most of the time, but can become a bit annoying. Re-fetching and by that re-syncing everything helps.

git gc triggers the garbage collector. Typically Git does this on it's own when appropriate. However, I sometimes do this manually after pruning or deleting a lot of branches to free up disk space.

git log does not only show the current branch name like git status does, but the history before that, including the full commit messages.

git pull syncs the local branch up with changes that might have been made on the remote in the meantime.

git push origin <branchNameOnGitHub> syncs the remote with what happened on my local machine in the meantime. All changes I have staged and committed, possible multiple times, get send to the remote. Since I'm mostly working with Gerrit nowadays, I don't use git push that often. Gerrit replaces it with it's own git review.

git rebase -i <origin/master|otherCommitId> starts an interactive rebase. I find me doing this very often, especially when working with Gerrit. The -i option gives me great control: For a start, I can always see the sequence or »chain« of unmerged commits my current branch is based on. It allows me to manipulate the way commits are based on each other, e.g. removing outdated ones or even reordering them. This is an immensely powerful tool when the goal is to create a series of concise, clean changes that depend on each other, but can easily be reviewed independently of each other (even in reverse order, which is often the better approach).

git rebase --continue continues the rebase process after conflicts have been resolved and marked as resolved via git add. In my experience this is one of the most troublesome commands, because one can forget it and heavily mess up everything afterwards. Unfortunately most other commands don't warn if I'm still in the middle of a rebase. Sometimes I blindly execute git rebase --abort just to be sure about this.

git remote prune <origin|gerrit> goes along with git gc. It frees up disk space by deleting local copies of branches that don't exist any more on the remote.

git reset undoes previews git add that have not been committed yet. This is called »unstaging«. Nothing gets lost. All that needs to be redone are these git add.

git reset --hard is the dangerous kind of reset that actually reverts changes that have been made to local files.

git reset --hard origin/master goes even further and replaces the upstream branch the local copy is connected to with something else. Typically, my local master is already connected to the upstream origin/master. The command still does have an effect then: It's basically identical to git reset --hard and git pull.

git review hands a commit in for review to the Gerrit code review system. This custom command is essentially a glorified version of git push including some Gerrit-specific convenience I don't need to memorize.

git review -d <gerritSequenceNumber> downloads an existing change from Gerrit so I can continue working on it. (GitHub requires to use git checkout -t <…> for that.) The number the -d option requires is neither the commit nor the change ID, but the much shorter sequence number one can find in the URL for each Gerrit change. Note that the -d option also performs a hard reset, and by that destroys local changes! Always do a git status and, if needed, git stash before.

git stash <list|pop|drop> <optionalStashId> is what I would describe as a wonderful, glorified clipboard, powered by the diff and merge capabilities one would expect from Git. It deserves it's own article – and got many already. I highly recommend reading up on git stash.

git status shows the current branch name, if local changes exist, and if they have been staged via git add already.

Technically, these are 16. ;-)

For all the above, type git help <command> to learn more about each command.

Kommentare zu diesem Beitrag können per E-Mail an den Autor gesandt werden.

[ < Zurück zur Übersicht ]

Impressum & Datenschutz