After reviewing the pro git book I found the following git features and commands I didn’t realize existed. Here’s the topics covered in this post:
- Interactive Staging
- Git Rerere
- Git Log / Shortlog
- Git Patches
- Showing Conflicted Files
- Managing Git Stash
- Git Clean
- Git Grep, Line and Log Search
- Commit Templates
- Git Pretty Log
- Git Autocomplete
- Quickly Switching Between Branches
Interactive Staging
Git includes an “interactive” mode to ease command line use. Interactive mode is especially helpful when you have several files changed but only want to commit certain files or even certain parts of the changed files. Enter interactive mode using -i
or interactive
:
rmacdonald-XPS-15-9560 ~/Documents/Android/my_project (my_git_branch) $ git add -i staged unstaged path 1: unchanged +2/-1 Project/app/src/main/java/com/my/project/Class1.java 2: unchanged +4/-3 Project/app/src/main/java/com/my/project/Class2.java 3: unchanged +5/-1 Project/app/src/main/java/com/my/project/Class3.java *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> |
You’ll be given a what now
prompt with options listed under *** Commands ***
. These options are:
1: status / 6: diff / 7: quit / 8: help
These are self explanatory commands (equivalent to git status
, git diff
, exit/ctl+c and a help menu.
2: update
Changed files are listed by number (see above). Using these numbers you can quickly add (stage) whole files by their listed number. To add Class1.java and Class2.java to staged enter 1,2
:
$ what now> 2 $ update>> 1,2 |
3: revert
You can unstage using revert
by choosing option 3. For example you can unstaged Class1.java
by:
$ what now> 3 $ Revert>> 1 |
4: add untracked
Add all untracked files just by choosing option 4.
$ What now> 4 1: new_file 2: another_new_file |
5: patch
It’s possible to commit only partial changes of a single file while omitting other changes and leaving them unstaged by using the patch option:
rmacdonald-XPS-15-9560 ~/Documents/Android/my_project (local_test) $ git add -i staged unstaged path 1: unchanged +3/-0 Project/app/src/main/java/com/Class1.java *** Commands *** 1: status 2: update 3: revert 4: add untracked 5: patch 6: diff 7: quit 8: help What now> 5 staged unstaged path 1: unchanged +3/-0 Project/app/src/main/java/com/Class1.java Patch update>> diff --git a/Project/app/src/main/java/com/Class1.java b/Project/app/src/main/java/com/Class1.java index e53448071a..73763261c2 100644 --- a/Project/app/src/main/java/com/Class1.java +++ b/Project/app/src/main/java/com/Class1.java @@ -119,6 +119,8 @@ public class Class1 { + // Add this change Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y @@ -347,6 +349,7 @@ public class Class1 { + // Dont add this change Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n rmacdonald-XPS-15-9560 ~/Documents/Android/my_project (local_test) $ git status On branch local_test Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: Project/app/src/main/java/com/Class1.java Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Project/app/src/main/java/com/Class1.java |
Voila, you have staged a partial change from Class1 while keeping the other changes unstaged. The same file is both staged and unstaged.
Git Rerere
Git rerere
stands for “reuse recorded resolution”. The idea is that git remembers how you chose to resolve hunk conflicts and applies them again in the future instead of having you resolve them again manually. This is particularly handy for long lived “feature” branches away from the “master” branch. With rerere
enabled you can periodically perform a merge, resolve the conflicts then back out of the merge. Git will remember how you resolved the conflicts along the way. When it’s time to merge (where you would normally have to go through many conflicts) git will apply all the fixes you applied in the past manually greatly simplifying the merge process. To enable rerere
globally enter:
$ git config --global rerere.enabled true |
To work with rerere
use the following workflow:
# show pre-merge recorded state $ git rerere status # will show the current state of the resolution $ git rerere diff # Fix the conflicts and add and commit the conflicted file $ git add conflicted_file $ git commit # Reset the changes as rerere has saved them $ git reset --hard HEAD^ |
When it comes time to merge/rebase the feature branch for a pull request all the conflicts resolved using the above workflow will be automatically applied.
Git Log / Shortlog
Git can format logs and changelogs by author, email, format, etc. Documentation for git log / shortlog
Git Patches
If you want to send someone changes you have on your local machine without committing them use the ">"
unix operator. For example:
# generate a git patch file named patch_name containing the current changes git diff > patch_name |
Anyone who has the file patch_name
can apply it to their local by running:
# apply the patch git apply patch_name |
Also see Git format-patch – Prepare patches for e-mail submission.
Show Conflicted Files
When dealing with conflicted files (during a merge for example) you can list the affected files by running:
$ git ls-files -u |
Managing Git Stash
git stash
is a handy tool for saving changes to your local without committing them. Here’s a few git stash
features I wasn’t aware of:
# remove a stash from stack git stash drop # apply and remove a stash from the top of the stack git stash pop |
Git Clean
git clean
is used to remove untracked files from the local working tree. Clean also has an interactive mode (-i
) that can be used to accelerate the process. See Git Clean Documentation for more information including all of the available arguments.
Git Grep, Log and Line Log
Git Grep:
Works like unix grep
except it searches through the entire git tree and history. Has many configurable options for filtering, display and more. For example if you’re looking for the name of something (such as a function, var, etc.) that was potentially renamed you can:
$ git grep method_name Project/path/app/File.java: void method_name(String arg); |
Git Grep Documentation for more information including all of the available arguments.
Git Log Search:
Useful if you’re searching for where something was introduced. It’s basically git blame
but performs a search instead. For example if you’re looking for when a constant variable was introduced you could:
$ git log -S SAVED_SESSIONS_KEY --oneline c53a688044 Refactored class honeybee with fabulous new methods a35525861c Added support for new fantastic feature |
Commit Template
Good commit messages make reviewing pull requests and commits more descriptive and efficient. You can automate this process by editing your commit.template
. Edit the ~/.gitconfig
file with the following.
[commit] template = ~/.gitmessage |
Add the new ~/.gitmessage
file containing a template such as the following:
Purpose of change: * Required By: * |
Anywhere your commit message appears (for example during pull requests) will automatically be populated with the template keeping communication consistent.
Git Pretty Log
I prefer to use the command line while working with git. I’ve relied on an alias named lol
to display a “tree” style graph to analyze branch status, tags, etc. Add the following entry to your ~/.gitconfig
file to utilize lol
:
[alias] lol = log --graph --decorate --pretty=oneline --abbrev-commit --all |
Then run the command:
$ git lol * 83bc947 (HEAD, origin/develop, develop) Added device frames to playstore screenshots. | * 26df0c3 (origin/issue-131, issue-131) issue-131: bump slide day time picker build tooling versions. |/ * 3cc3a8c (tag: 1.0.2) Merged in issue-130 (pull request #87) Issue-130: 1.0.3 release |\ | * 77d9a27 (origin/issue-130, issue-130) issue-130: 1.0.3 release |/ * 2b22c85 Merged in issue-129 (pull request #86) Issue-129: fix comma issue with fr locale. |
You’ll get colored output indicating the state of each commit / branch. Navigate up and down with j
and k
. Perform searches with /
.
Git Autocomplete
You can enable linux style autocompletion to your commands (press tab to autocomplete) by adding the autocomplete.bash script to your local machine. Git Autocomplete GitHub. Add it by running:
# Save the script to your local machine curl https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash -o ~/.git-completion.bash |
Then adding the following snippet to your ~/.bash_profile
or ~/.bashrc
(depending on your local configuration:
if [ -f ~/.git-completion.bash ]; then . ~/.git-completion.bash fi |
You should now be able to autocomplete git commands and even branches found on your local machine.
Quickly Switching Between Branches
Similar to cd -
you can quickly switch to the last checked out branch using git checkout -
# on develop, checking out branch issue-143 (develop) $ git checkout issue-143 # on branch issue-143, checking out develop (issue-143) $ git checkout - # back on develop (develop) $ |
Summary
I prefer to use the command line interface of git over GUI applications such as sourcetree. The above commands have accelerated my efficiency with git allowing me to focus more on development and less on VCS.