Git tips from the trenches
ochronus.com
ochronus.com
> git-rerere
will prevent you from getting stuck resolving the same merge conflicts repeatedly, by remembering how you resolved them the last time.
Also, instead of passing any arguments to "git-log", I usually just use "tig", an ncurses display of the commits: https://blogs.atlassian.com/2013/05/git-tig/
Finally, when I was first taught Git, I was told that I should never need to comment out code again. I never understood how this was possible until I learned how to use 'git add -p' and 'git rebase -i' in tandem.
> will prevent you from getting stuck resolving the same merge conflicts repeatedly, by remembering how you resolved them the last time.
That's on by default these days; if you re-do a merge and git finds the conflict in rerere, it'll automatically use the saved resolution rather than inserting conflict markers.
Agree, tig is awesome and in my opinion the best git GUI (or at least representation). Use the normal commandline tools for commiting/cutting/rebasing/... and tig to see where everything is at!
+1 for tig. Definitely my favourite git interface. Its status view is also very handy for viewing diffs, and adding/removing files to the index, as well as reverting changes.
Also, it now has mouse support. I know this, cos I added it :)
git log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
(Kudos to the unremembered internet person who posted this in the first place)
git bisect
If you're ever looking for "the commit that broke feature XYZ", git bisect is your trusty minion.Here's a tip for anyone using bisect with a Rails app (or any system that uses database migrations):
I do a rake db:migrate after a pull to make sure the database schema is up to date. So the first time I used git bisect on a Rails app, I figured I would use db:migrate after each bisect step.
On this project, as in many Rails projects, schema.rb is checked into git along with the migration files.
Somewhat mysteriously, after some of the bisect steps and db:migrate operations I was seeing an uncommitted schema.rb that was different from the one in git. That shouldn't have happened, I thought.
I finally figured out what perhaps should have been obvious: db:migrate was working fine when bisect moved forward in the history, but it was messing up when bisect moved backward across a migration.
Rails experts will know what went wrong here: After bisect moved backward, the db:migrate had no way of knowing which database changes to reverse, because the migrations that were now in the "future" relative to the latest checkout were removed before I ran the migrate.
I fixed it by using db:reset instead of db:migrate, at least after bisect moved backward in time.
Another bisect tip: Sometimes it can be advantageous to run the equivalent of a bisect manually instead of automatically. For example, you may have some pretty good hunches about which commits may or may not be related to the problem. Or you may want to do a db:migrate before moving back in time to avoid the problem I described above, and by doing the checkouts manually you will know which migrations to reverse before doing the checkout.
In my case, I was looking at a somewhat intermittent bug. As I narrowed down the possible bad commits I wanted to keep track of the specific ones I'd looked at, and git bisect doesn't do this for you. So each time I checked out a different commit I created a local branch first on the commit I'd determined to be (likely) good or bad, giving them names like Good1, Good2, Bad1, Bad2, etc.
This way I could just look at the history as I went along and see at a glance which commits I'd investigated so far.
I guess this trick would work with git bisect as well - just create a local branch before each bisect step.
There may be some better way to do this, but it worked pretty well for me.
BTW, I use SmartGit/Hg and really like it. I know everyone likes their command lines, but I greatly prefer a visual way of working with a source code repo.
It does, since v1.0 (so about 6 years ago): http://www.selenic.com/mercurial/hg.1.html#bisect
As is usually the case, it's not active by default.
> Rails experts will know what went wrong here: After bisect moved backward, the db:migrate had no way of knowing which database changes to reverse, because the migrations that were now in the "future" relative to the latest checkout were removed before I ran the migrate.
And you can't even do that automatically, because git doesn't have a pre-checkout hook (assuming bisect uses checkout internally, which I'm not certain of)
At that point, git will check out the "middle" revision and you can do whatever's needed to decide if you should mark it as either good or bad with 'git bisect [good|bad]'. This will check out the new "middle" - lather, rinse, repeat until you get down to one commit.
When you're done, 'git bisect reset' will take you back to the present.
While this isn't quite as quick as the automated version, it's a lifesaver when tracking down a regression in a library whose dependencies have changed radically (finding what b0rked your tests between Rails 3.0 and 4.0, for instance).
does this actually work? `git branch --merged` for me also
returns '* master', which when xarg'd would include the '*'
and also 'master'. I don't want to delete master, and I
certainly dont want to delete * if it globs.
EDIT: made this into a code block as I have no idea how to make asterisks display.error: Cannot delete the branch 'master' which you are currently on.
alias gclean='git remote prune origin; git branch --merged | grep -v -E "(\*|master)" | xargs -n 1 git branch -d'
git rev-list --all | xargs git grep '<YOUR REGEX>'On my machine, using postgres's repository, I get the following:
> time git grep foo > /dev/null
0.22s user 0.25s system 151% cpu 0.312 total
> time ag foo > /dev/null
0.85s user 0.19s system 174% cpu 0.596 total
> time grep foo $(git ls-tree --full-tree --name-only -r HEAD) > /dev/null
0.13s user 0.10s system 93% cpu 0.255 total
grep's faster than git grep. In fact, grep is already as fast as git grep just ignoring .git: > time grep foo -r . --exclude-dir=.git > /dev/null
0.15s user 0.16s system 91% cpu 0.338 totalI ran a test on a large repository and here are my results. The repository was Hadoop, and is available from git://github.com/apache/hadoop-common.git.
cmccabe@keter:~/hadoop4> du -cksh .
375M .
375M total
sudo -- sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches'
cmccabe@keter:~/hadoop4> /usr/bin/time git grep 'class TestDefaultNameNodePort'
hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDefaultNameNodePort.java:public class TestDefaultNameNodePort {
0.11user 0.34system 0:00.74elapsed 61%CPU (0avgtext+0avgdata 60512maxresident)k
260256inputs+0outputs (19major+9718minor)pagefaults 0swaps
sudo -- sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches'
cmccabe@keter:~/hadoop4> /usr/bin/time grep -rI --exclude .git 'class TestDefaultNameNodePort' *
hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDefaultNameNodePort.java:public class TestDefaultNameNodePort {
0.13user 0.56system 0:02.40elapsed 29%CPU (0avgtext+0avgdata 5584maxresident)k
252792inputs+0outputs (2major+414minor)pagefaults 0swaps
So you can see that it is faster, even when excluding the .git directory.Running grep a second time without clearing the cache gives a bogus result:
cmccabe@keter:~/hadoop4> /usr/bin/time grep -rI --exclude .git 'class TestDefaultNameNodePort' *
hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDefaultNameNodePort.java:public class TestDefaultNameNodePort {
0.03user 0.04system 0:00.08elapsed 97%CPU (0avgtext+0avgdata 5584maxresident)k
0inputs+0outputs (0major+416minor)pagefaults 0swaps
This is because the data is all in the page cache at that point, so we're not actually accessing the disk.I was curious about the true source of the speedup, and so I checked the output of the 'perf' tool. git had 1,922 CPU migrations, whereas grep had 52. Following up on this, you can see that git is spawning a bunch of threads, whereas grep only has one thread.
cmccabe@keter:~/hadoop4> strace -f -e trace=clone git grep 'class TestDefaultNameNodePort' 2>&1 | grep -c '] +++ exited with '
8
cmccabe@keter:~/hadoop4> strace -f -e trace=clone grep -rI --exclude=.git 'class TestDefaultNameNodePort' * 2>&1 | grep -c '] +++ exited with '
0
I also think git might be cheating and using a simpler regex engine than grep, but at this point I got bored. Case closed.And AFAIK it can run the grep in parallel. Practically any non-archaic machine has multiple cores now, so git-grep can easily be faster than regular grep from command line.
git status --untracked=no
This shows only files that are tracked. I tend to do a lot of work which leaves files that I don't want to check in lying around in my git repo, this eliminates these and lets me see exactly what I have been working on. The slight caveat to this is when you are working on a new file that you have not yet checked in ever.
This goes hand in hand with:
git add -u
Which only adds untracked files. With aliased commands this usually results in a flow like this:
> git stu
> git adu
I'm a neat-freak when it comes to file placement, and have a ~/tmp directory specifically for dropping one-off files without cluttering up repos and such.
Are the things your repo isn't tracking mostly temporary/build files? or things that are going to be checked in eventually?
alias g='git'
alias pull='git pull origin master'
alias push='git push origin master'
alias gc='git commit -m $1'
alias ga='git add -A'
# Adds all and commits (gg 'Commit message')
function gg () {
git add -A;
git commit -m "$1";
git push origin master;
}alias gs='git status'
alias gd='git diff --color'
alias gdc='git diff --cached'
alias gstat='git diff --stat'
Mostly because this helps me keep a clean history, which is something I value highly.
git blame -w
Ignore the whitespace if a block was [un]indented, attribute to original author instead of person re-indenting due to control structure change around block.And for all the GUIs available, most of them either don't even touch blame, or just return the output of the CLI blame command. The only GUI I've seen so far which makes it easy to traverse history through blame is for bazaar (qblame/qannotate).
IntelliJ IDEA also has a pretty nice blame panel. It's not as full-featured as SmartGit, but it's right there in the editor for any file with a quick Alt-G-N.
I would definitely recommend trying out SmartGit.
...and a tool to work faster with Git: http://qwikgit.nicoschuele.com
for k in `git branch -a|perl -pe s/^..//`;do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k|head -n 1`\\t$k;done|sort -r
So "everyone" (every developer, ever) is a "he"? I'm not here to bash the author, but remarks like this (whether or not they're intentional) really don't help make the field of software engineering, which is already quite the boy's club, any more accessible to non-male people.