Posts Tagged by Git
git bisect run – example
| 15-Mar-2013 | Posted by Sonia Hamilton under Git, Golang |
Git bisect is a great tool for finding bugs in a program. But many examples show manual uses of git bisect – here’s an example of automating the process using git bisect run.
tl;dr
Using git bisect run is easy if you’ve make small atomic commits and you have good tests. run makes a large debug easier (compared to manually doing git bisect good and git bisect bad) – you’re less likely to make errors due to boredom. And run means you can use an iterative process – use rebase to split bad commits then just run again.
Example
So I had an elusive bug in a long running process (an snmp poller, calculator and aggregator for a large network). I had a point where the program was good, but I’d added more features since good and now results were bad. The first step was to write a shell script to be called from git bisect run:
% cat bisect.sh
#!/bin/bash
# copy this to ~ before running with `git bisect run ~/bisect.sh`
cp ~/Makefile .
make clean
# make modifies manpage output, so stash after build
if make &> /dev/null ; then
git stash
git stash clear
else
git stash
git stash clear
exit 125
fi
sudo cat /var/tmp/empty > /var/log/abc/abc-poller.log
sudo ./abc-poller --tmp --once -d 2 -c 150 || exit 125
echo "=== poller finished"
percent=`godir=/var/tmp/data/abcmon/poll_queue/new ~/checker | \
tail -1 | awk '{print $5}' | awk -F. '{print $1}'`
echo "=== percent is $percent"
(( percent < 5 ))
Things that make writing the test script easier:
- first get it working outside of git bisect run – usually means echoing results along the way
- already having a test suite that produces a quantified pass/fail output, In my case the I had already written the checker program, whose last line of output contained a “percentage failure” figure
Next step was having an abbreviated log of commits to refer to:
% git log --oneline f00f232 sql.go - better debugging # bad 9780d44 dummy .gitignore, so out dir preserved 0de0796 Makefile for nsch1abcs01 a2f6c96 defaults - 20 workers, udp 15 b8ee3d9 GOMAXPROCS() 04f21ba start v0.0.2 dbc6a60 Makefile: Jenkins as default for env vars 557a5e3 more work on stats ef6a453 remove excessive debugging ccc4644 remove file buffering - wasn't writing..??? 98bf4b1 stats write failing 9a9682d move type queue_t struct 467aeed buffered writes for queue file 148a8cc stats: + device_run, device_ok c61be42 done chan *Stats_t; calculate_value() bool 0e41461 debugging - print out device_id as %5s 7cbe167 default workers 5000, correct stop/start commands 544e8d7 gather statistics 6990cf2 rename data chan to device_id 5e8562b rename sql -> sqlconn; global var fd52d89 remove dead code 4d7b9b1 device_for() - err if count != 1 6ceb305 deb: fail if version main.go isn't same changelog 5ce1855 deb: rules producing abc-poller_0.0.1_amd64.deb db5bff4 deb: cleaned up Makefile, roffs 76674c2 JSON -> SQL; version 0.0.1 4cf198e deb: basic removal of 64 references d980f26 deb: rename 64 to vanilla b933570 deb: remove 32 bit stuff 970bd82 current debug level is 2; adjust output 70f8921 Revert "deb build - don't init, cron while testing" 855ce00 default debug is 2; misc tidy f3d80e1 remove timeoutOpt - no longer used 48ced38 misc tidys before release a3fe13c runonceOpt, revert cycling code # good 24cd998 use passed in udpOpt 5ca1830 remove stash/sender.old.go 06b9566 remove gsnmpgo; use gosnmp bffd430 rules: add note about "too many open files"
Mark bad and good, start the run, go and have a coffee :-)
% git bisect start f00f232 a3fe13c % git bisect run ~/bisect.sh # lots of output
I get the result that the ominously named 557a5e3 more work on stats is the first bad commit – I remember it as one of those large “kitchen sink” commits done at the end of the day. So “first rule of fightclub git” remembered – always do small atomic commits.
I have a useful shell function gri() – I used that to interactively rebase and break up 557a5e3 into many small commits:
gri () {
git rebase -i HEAD~${1:-7}
}
After rebasing git log looked like this – notice the many small commits named “bisect1″ etc:
382b3ee defaults - 20 workers, udp 15 11db314 GOMAXPROCS() 4b547ea start v0.0.2 c92feef Makefile: Jenkins as default for env vars 8fcc595 bisect6: calc/noncalc ad3a18f bisect5: tweak debug msgs 311bb8d bisect4: mv stats init 957c234 bisect3: remove stats from send_gosnmp() 3c01d62 bisect2: use Add(); Calcs/NonCalcs 947cb27 bisect1: move Stats_t; Add() ef6a453 remove excessive debugging ccc4644 remove file buffering - wasn't writing..??? 98bf4b1 stats write failing
And here’s the real win of writing bisect.sh – you can just keep rebasing and running until you’ve narrowed down the bad code to a few lines:
=== poller finished
=== percent is 49
947cb27fd57642dc545ee23090d7ae8fd8b14b3f is the first bad commit
commit 947cb27fd57642dc545ee23090d7ae8fd8b14b3f
Author: Sonia Hamilton <sonia@snowfrog.net>
Date: Thu Mar 14 10:02:42 2013 +1100
bisect1: move Stats_t; Add()
I do another interactive rebase, fix the logic error, and then HEAD is good.
git – delete local tracking branches
| 23-Jan-2013 | Posted by Sonia Hamilton under Git |
(Just a summary of Stack Overflow “How do you Remove an Invalid Remote Branch Reference from Git?”).
To delete a local tracking branch (without deleting the remote branch), do:
git branch -rd remote/branch
And of course to delete the remote branch:
git push remote :branch
Occasionally a gc will help, but usually shouldn’t be used:
git gc --prune=now
So, when would you want to use this? Let’s say the repository you’re tracking has a lot of branches (eg the Linux Kernel). You start tracking branch “foo”, do some work with it, merge some of it in to your branch “bar”, then push “bar” up to the remote repository. Or, you’ve got a whole lot of dev branches you’ve merged pushed to your backup repository and also merged into your master branch.
In either case you’ve got a collection of tracking branches you don’t want to see anymore, so clean them up:
% git branch -r soniah/dev.a soniah/dev.b soniah/dev.c soniah/dev.d soniah/master % git branch -rd soniah/dev.a Deleted remote branch soniah/dev.a (was deadbeef). % git branch -rd soniah/dev.b Deleted remote branch soniah/dev.b (was deadbeef). # now remote branches is cleaner: % git branch -r soniah/dev.c soniah/dev.d soniah/master
git pull -f (git force pull)
| 03-Dec-2012 | Posted by Sonia Hamilton under Git |
Git has a “force push” option (git push -f remote branch), but it doesn’t have a “force pull” option (like git pull -f remote branch).
This works:
% git fetch remote branch % git reset --hard FETCH_HEAD % git clean -df
Or, as a function for your bash/zsh config file:
gpuf () {
# git pull -f $1
remote=${1:?"need remote to force pull from"}
current_branch=$(git symbolic-ref -q HEAD)
current_branch=${current_branch##refs/heads/}
current_branch=${current_branch:-HEAD}
if [ $current_branch = 'HEAD' ] ; then
echo
echo "On a detached head. Exiting..."
exit 1
fi
git fetch $remote $current_branch
git reset --hard FETCH_HEAD
git clean -df
}
git push to non-bare repo: warning: updating the current branch
| 17-Aug-2012 | Posted by Sonia Hamilton under Git |
Some doco for my work mates, and others learners of git…
When I was starting with git (many moons ago), learning to pull from a remote repository was easy. But the book I read didn’t explain how to push to a remote repository. There was lots of doco on pushing to github, but I wanted to push and pull between my own repositories. When I pushed, I got messages saying warning: updating the current branch, and sometimes I’d have to manually clean up the remote branch – yuk.
localbox% git push remotebox master Counting objects: 1, done. Writing objects: 100% (1/1), 252 bytes, done. Total 1 (delta 0), reused 0 (delta 0) remote: warning: updating the current branch To sonia@remotebox.net:/home/sonia/dotfiles f387640..13740c5 master -> master
Then I learned about bare repositories. Sweet, now I could cleanly push to (bare) remote repositories:
# convention with bare repositories is .git suffix remotebox% git clone --bare dotfiles dotfiles.git Cloning into bare repository 'dotfiles.git'... done.
But what if you want to push to a non-bare repo? Firewalls mean you often have to hop across several machines using ssh tunnels, and setting up bare repositories all over the place is ‘non-optimal’. Or, you want to push between laptops. Or, whatever…
Here’s a recipe – just push to a dummy branch (I usually call it ‘push’), then do a normal merge on the remote:
# I'm on localbox and I want to push to remotebox, which isn't bare
# git br -a shows me all the remote branches:
localbox% git br -a
* master
remotes/bourke/master
remotes/bourke/foo
remotes/bourke/bar
# pushing to master, foo or bar will give problems, let's push to 'push'
localbox% git push remotebox master:push
Total 0 (delta 0), reused 0 (delta 0)
To sonia@remotebox.net:/home/sonia/dotfiles
* [new branch] master -> push
# let's look at that command:
% git push remotebox master:push
^ ^ ^
remote | ^
from |
to
# ie I'm pushing from master branch to the push branch on remotebox
On the remotebox, merge in the push:
remotebox% git br -a # check all my branches * master foo bar push remotebox% git co master # checkout master remotebox% git merge push # merge push into master remotebox% git br -d push # cleanup
Et Voilà! Done.
Update
After a bit more reading on git, I realised that there is another solution. You can push to any branch on a non-bare repository, as long as that branch isn’t currently checked out on the remote.
PS I have lots of shortcuts in my ~/.gitconfig, like co and br. Saves typing!
[color]
ui = auto
[color "branch"]
current = green
local = yellow
remote = red
[color "diff"]
meta = blue bold
frag = magenta bold
old = red bold
new = green bold
[alias]
ci = commit
co = checkout
st = status
br = branch
sb = show-branch
sbs = show-branch --sha1-name
gr = log --graph --oneline --left-right
cl = clean -x -d -f
dt = difftool
difft = difftool
[push]
default = matching
[receive]
denyCurrentBranch = warn
Git – ignore whitespace
| 13-Jun-2012 | Posted by Sonia Hamilton under Git, Python |
My new favourite git option:
% git diff -w
Ignore white space in diffs. Unfortunately, there doesn’t seem to be an option for just ignoring whitespace changes at the start of line – would be handy for Python.
Recent Comments
<<EOF>>was eaten...cat <>~/.vi...