- Dienstag, 2018-08-28 13:38
- 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 addmarks local changes to be committed in the next step – a process Git calls »staging«. The
-poption (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
-poption at hand allows me to clean such code up the moment I see it, and still hand in clean, easy to review commits.
git blameis 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 branchonly creates the branch, but does not make it the current one.
git checkout -bdoes 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 mastergoes 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 commitputs all the changes I staged with
git addbefore into a new commit I can then push or send in for review.
git commitwill 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 --amendis 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
git diff -w --color-words. The
--color-wordsoption 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
-woption 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 diffinstead. Here is how you can add this alias via the command line:
git config --global alias.d 'diff -w --color-words'.
git fetch --allsyncs 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 gctriggers 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 logdoes not only show the current branch name like
git statusdoes, but the history before that, including the full commit messages.
git pullsyncs 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 pushthat often. Gerrit replaces it with it's own
git rebase -i <origin/master|otherCommitId>starts an interactive rebase. I find me doing this very often, especially when working with Gerrit. The
-ioption 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 --continuecontinues 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 --abortjust 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 resetundoes previews
git addthat have not been committed yet. This is called »unstaging«. Nothing gets lost. All that needs to be redone are these
git reset --hardis the dangerous kind of reset that actually reverts changes that have been made to local files.
git reset --hard origin/mastergoes even further and replaces the upstream branch the local copy is connected to with something else. Typically, my local
masteris already connected to the upstream
origin/master. The command still does have an effect then: It's basically identical to
git reset --hardand
git reviewhands a commit in for review to the Gerrit code review system. This custom command is essentially a glorified version of
git pushincluding 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
-doption 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
-doption also performs a hard reset, and by that destroys local changes! Always do a
git statusand, if needed,
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 statusshows the current branch name, if local changes exist, and if they have been staged via
Technically, these are 16. ;-)
For all the above, type
git help <command>to learn more about each command.