Today, git is the standard when it comes to version control system in software development and for many other uses.
Commands like git commit
, checkout
, pull
, push
, status
are executed multiple times a day. However, git has a lot of more advanced features, which are not frequently used. Today I want to talk about five commands, which usually are not needed that often, but are quite useful at the right moment.
1. git bisect
Imagine a situation where something was recently working, but now it does not. Or, perhaps, all the tests were passing, but now one little test fails. It is not trivial to understand what happened. To simplify the investigation process we may want to find out the exact commit, which introduced the bug. git bisect is our friend.
It uses binary search between bad (current commit which is not working) and good (a commit from the past which was working) commits to find which commit introduced the bug.
1. git bisect start # let's start our workflow
2. git bisect bad # current commit is broken, mark it a bad
3. git bisect good <commit_id> # let bisect know which commit was working
# After step #3 bisect checks out a commit between good and bad
4. # we check if current commit has the issue or not
5a. git bisect good # if test at step #4 succeeded, we mark current commit a good
5b. git bisect bad # if test at step #4 failed, we mark current commit as bad
# repeat #4 and #5 until we find the commit which introduced the bug
Instead of bad
and good
we can also use new
and old
.
2. git worktree
Let’s talk about different scenario. We have a large repository, which many people are working on simultaneously. It takes quite a bit of time to clone the repo and also some time to fetch all the changes regularly. We use git-flow and have a branch per feature.
Now imagine that, for some reason, we need to work on a different branch and keep our current one open at the same time. There are a couple of ways to achieve this. The most obvious one is to clone the same repo once again and checkout a different branch. However cloning takes time as our repo is quite big plus it creates a bit of mess/confusion as now we should have something like my-project and my-project2 in our projects directory. Also we will have to keep both folders in sync with the origin which means extra pull
operations. This is where git worktree helps. Instead of cloning the same repo one more time we can do this:
1. git worktree add ../worktrees/myproject mybranch # create new working tree
2. git worktree list # list all working trees connected to our repo
... # do some work, then clean up
3. rm -r ../worktrees/myproject
4. git worktree prune
After step #1, we have two folders in our projects directory: myproject
and worktrees/myproject
. Both may point to the same branch or to two different branches, but they are connected to the same repository and we didn’t have run git clone
second time.
3. git stash push -m “message”
stash command is much more popular than the two mentioned above. It is handy when there is a need to save unfinished changes somewhere without committing them. So we can do:
git stash # save local changes to stash entry
... # do something here, e.g. jump to a different branch
git stash list # print all available stash entries
git stash apply stash@{0} # apply changes from entry '0' without removing it from the list
git stash pop # apply changes from the last added entry and remove it from the list
What is very important when creating a stash entry in my opinion is command options. By default git stash
associates a message from the latest commit with the newly stash entry. This may be good enough if we need our stash entry for a couple of minutes, but what if we need to create two stash entries one after another and keep them for a couple of days or a week? The output of git stash list
will be very confusing.
So, it’s always better to use:
git stash push -m "changes description"
git stash save "changes description" # older deprecated alternative
stash push
has a couple of other useful options:
-k
,--keep-index
will not stash changes which have been already added to the index-u
,--include-untracked
will add untracked files to the stash
It’s a good idea to create two-three git aliases for different variations of git stash push
.
4. git var -l
This command is different from the others, because it is educational. It prints out git logical and configuration variables. From its output we can find out:
- preconfigured aliases and aliases configured by us
- that our default editor is
vim
- what’s the current default push strategy
- and much much more…
It’s a good educational tool. If needed a setting from git var -l
output can be researched deeper and changed.
As per git var docs configuration variables listing functionality is deprecated in favor of git config -l
.
Also, in case you have noticed an alias, which you have never configured, in git var/config -l
output. E.g.:
alias.ac=!git add . && git commit
Check out git config -l --show-origin
output to find where does it come from.
5. git commit [-v, –amend –no-edit]
This command could be an exception from the list, as it is needed quite often and it is more widely known and used. But it’s useful enough that I want to include it in my list.
Sometimes, we need to review our changes before committing to come up with a better commit description. git commit -v
will open a text editor and will display all the changes in addition to filenames to be committed. I have an alias git ci
for this command.
git commit --amend
is something I was really missing back in svn times. It is useful when we make a commit and realize that we forgot to mention something in the commit message or that it has a typo. --no-edit
option can be added when we are happy with our commit message, but want to include a forgotten file into the last commit. I have aliases git ca
and git can
for these two commands.
Afterword
git is really powerful. It is worthwhile to take a quick look at the output of git help -a
to get a very general idea of what it can do in addition to commands we all use every day and to check out periodically other people .gitconfig
files on Github for new ideas on what else in our git workflow can be optimized and improved.