Git: Twelve Curated Tips And Workflows From The Trenches
My Evernote tech-tips folder and my Notational Velocity stash have been collecting a huge list of git tips, many of which I have to look up again and again. Until I finally give up and create aliases for them (by the way you can find some of my more advanced git aliases explained in my previous post).
I expect many git practitioners will find them useful as much as I do. Here we go, let’s start from tiny and simple things first.
Making ‘git diff’ wrap long lines
This has been driving me mad for a while. My git diff
would not wrap lines
and leave a lot of information hidden from view in my terminal. Luckily the
solution for this is easy.
If you use less
as default pager just type -S
while viewing the
diff to reenable wrapping in less
.
You can also use git config to setup pager to wrap:
$ git config core.pager 'less -r'
Sets the pager setting for the current project.
$ git config --global core.pager 'less -r'
Sets the pager globally for all projects. (Stack Overflow reference)
Set a global proxy
In some network configurations you might need to use a proxy with your git
.
It is a straight forward oneliner to do so:
git config --global https.proxy https://user:password@address:port
Clone only a specific branch
For big projects or for ease of access sometimes you want to clone just one branch. To clone a branch without fetching other branches here’s what you can do:
mkdir $BRANCH
cd $BRANCH
git init
git remote add -t $BRANCH -f origin $REMOTE_REPO
git checkout $BRANCH
Diff file against remote branch
This is basic git knowledge but it does not hurt to show you. Here is the outline:
git diff localbranch remotebranch filepath
As an example, supposing you have a few local and remote branches fetched and up-to-date:
[developer][ubuntu:~/delixl][631]
$ git branch -ra
* 631
fsh-502
master
remotes/6.29
remotes/6.30
remotes/6.31
remotes/ECM-1670.3
remotes/trunk
You can perform a diff with one of the remote branches with:
git diff master ECM-1670.3 pom.xml
diff --git a/pom.xml b/pom.xml
index f3fc810..27154e3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
<groupId>nl.delixl</groupId>
<artifactId>delixl</artifactId>
<packaging>pom</packaging>
- <version>6.32-SNAPSHOT</version>
+ <version>7.30-SNAPSHOT</version>
[...]
List all deleted files in the repository
You might want to restore them, you might want to double check some merge behavior, in any case it’s quite useful to be able to list all the files that have been deleted in your repository. Here’s how to go about it:
git log --diff-filter=D --summary
If you want to restore some of them see this.
If you don’t want all the information about which commit they were removed in, you can just add a grep delete in there.
git log --diff-filter=D --summary | grep delete
Search for a string in all revisions of entire git history
I had to do this many times. I remember a piece of code, a function name, a constant, a file, that was there. And now it’s gone. What happened? In which commit was it removed? This trick rocks as you can search through the entire git diff history for a string.
Update: Jaime in the comments thread pointed me to the right way to do this:
git log -Stext
I was doing it in an inefficient way found on SO:
git rev-list --all | (
while read revision; do
git grep -F 'Your search string' $revision
done
)
Apply a patch from another (unrelated) local repository
The proper way to cherry-pick a commit from another repository is to first add the other repository as a remote, fetch its changes and then cherry-pick the commit.
But if you want a raw and quick procedure, that works even between unrelated repositories you can do:
git --git-dir=../some_other_repo/.git format-patch -k -1 --stdout <commit SHA>| git am -3 -k
Making a more recent branch the new master
Sometimes you work on a separate better_branch
for quite some time, and
you’re so satisfied with it that it makes sense to just make it the new
master
. So how do you do it? As usual stack overflow to the rescue:
-
Switch to the
better_branch
:git checkout better_branch
-
Keep the full content of
better_branch
but record a merge:git merge --strategy=ours master
-
Switch to
master
again:git checkout master
-
Fast-forward master up to the merge:
git merge better_branch
If you want your history to be a little clearer, you can customize the commit message to clarify what you are doing. You can change the second step to:
git merge --strategy=ours --no-commit master
git commit # Here add your custom message to the commit template
Adding an initial empty commit to a branch to allow full rebase
This tip rewrites history so as usual you should only do it on branches that you haven’t shared with anyone, otherwise you risk breaking things for all your peers.
-
Create a new empty branch i.e.
newroot
git checkout --orphan newroot git rm --cached -r . git clean -f -d
-
Create the empty commit
git commit --allow-empty -m '[empty] initial commit'
-
Replay the whole content of the branch
git rebase --onto newroot --root master
-
Delete the temporary branch
newroot
git branch -d newroot
Done. Your master had its history rewritten to include an empty root commit. (Stack overflow reference)
Zero a branch to do something radically different
Sometimes you want a branch where you can start everything from scratch, or
keep some code that logically is related to your master but tracks another
cross-functional aspect of it. Like gh-pages
in a github project.
So how do you zero a branch, erasing all its history so that you can start something new?
Update: helpful user in the comments and in the reddit thread pointed me to a much easier way to accomplish this:
git checkout --orphan new_empty_branch
My original process was way more convoluted:
-
Checkout a branch:
git checkout -b branch_to_zero
-
Follow the tip above about adding an initial empty commit and after that you can very easily zero a branch by doing a simple reset.
-
Reset hard the branch to the initial commit you just created
git reset --hard initial_commit
How to modify a specified commit?
The amend command (git commit --amend
) is very useful to rework your last
commit before pushing it. But what if the commit you want to change is not the
last?
You can use git rebase, for example, if you want to modify commit bbc643cd
,
run:
$ git rebase bbc643cd^ --interactive
In the default editor, modify pick
to edit
in the line whose commit you
want to modify. Make your changes and then stage them with:
$ git add <filepattern>
Now you can use:
$ git commit --amend
To modify the commit, and after that
$ git rebase --continue
To return back to the previous head commit. (Stack Overflow reference)
How to stash only one file out of multiple files that have changed
git stash --keep-index
Will stash everything that you haven’t previously
added. Just git add the things you want to keep, then run it.
By using this you can split an old commit into more than one changeset:
-
Rebase interactively from your last good commit:
git rebase -i last_good_commit
-
Mark some changes as
edit
.git reset HEAD^
-
Add the files you want to keep in this change:
git add file1 file2 file3
-
Stash everything that you haven’t previously added:
git stash --keep-index
-
Fix things up as necessary. Don’t forget to
git add
any changes.git commit git stash pop
-
Repeat, from step 2, as necessary.
git rebase --continue
Conclusion
I hope you enjoyed these tips and workflows roundup. More is in store if there is interest.
Notifications for new articles and virtual interactions with the author of this (me!) are available on twitter @durdn.
5 December 2012