Robot Has No Heart

Xavier Shay blogs here

A robot that does not have a heart

Bash script to keep a git clone synced with a remote

Use the following under a process manager (such as runit) to keep a local git clone in sync with a remote, when a push based solution isn’t an option. Most other versions either neglect to verify remote is correct, or use git pull which can fail if someone has been monkeying with the local version.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function update_git_repo() {
  GIT_DIR=$1
  GIT_REMOTE=$2
  GIT_BRANCH=${3:-master}

  if [ ! -d $GIT_DIR ]; then
    CURRENT_SHA=""
    git clone --depth 1 $GIT_REMOTE $GIT_DIR -b $GIT_BRANCH
  else
    CURRENT_REMOTE=$(cd $GIT_DIR && git config --get remote.origin.url || true)

    if [ "$GIT_REMOTE" == "$CURRENT_REMOTE" ]; then
      CURRENT_SHA=$(cat $GIT_DIR/.git/refs/heads/$GIT_BRANCH)
    else
      rm -Rf $GIT_DIR
      exit 0 # Process manager should restart this script
    fi
  fi

  cd $GIT_DIR && \
    git fetch && \
    git reset --hard origin/$GIT_BRANCH

  NEW_SHA=$(cat $GIT_DIR/.git/refs/heads/$GIT_BRANCH)
}

update_git_repo "/tmp/myrepo" "git://example.com/my/repo.git"

sleep 60 # No need for a tight loop

Automatically pushing git repositories to Bitbucket

Bitbucket gives you unlimited private repositories. It’s the perfect place to archive all my crap to. Here is a script to create remotes for all repositories in a folder and push them up. I had 38 of them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
$usr    = "xaviershay"
$remote = "bitbucket"

def main
  directories_in_cwd.each do |entry|
    existing_remotes = remotes_for(entry)

    action_performed = if existing_remotes
      if already_added?(existing_remotes)
        "EXISTING"
      else
        create_remote_repository(entry)
        push_local_repository_to_remote(entry)
        "ADD"
      end
    else
      "SKIP"
    end

    puts action_performed + " #{entry}"
  end
end

def directories_in_cwd
  Dir.entries(".").select {|entry|
    File.directory?(entry) && !%w(. ..).include?(entry)
  }
end

def remotes_for(entry)
  gitconfig = "#{entry}/.git/config"
  return unless File.exists?(gitconfig)
  existing_remotes = `cat #{gitconfig} | grep "url ="`.split("\n")
end

def already_added?(existing)
  existing.any? {|x| x.include?($remote) }
end

def create_remote_repository(entry)
  run %{curl -s -i --netrc -X POST -d "name=#{entry}" } +
          %{-d "is_private=True" -d "scm=git" } +
          %{https://api.bitbucket.org/1.0/repositories/}
end

def push_local_repository_to_remote(entry)
  Dir.chdir(entry) do
    run "git remote add #{$remote} git@bitbucket.org:#{$usr}/#{entry}.git"
    run "git push #{$remote} master"
  end
end

def run(cmd)
  `#{cmd}`
end

main

So you aren’t prompted for username and password every time, you should create a `.netrc` file.

1
2
> cat ~/.netrc
machine api.bitbucket.org login xaviershay password notmyrealpassword

Code to test ratio per commit with git

I came across a post titled visualizing commits with bubble charts

That seems pretty neat. I don’t have the visualization yet, but I put together a script to pull the required data from a git repository:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# usage: gitstats HEAD~5..

revs=`git log --format="%H" $1`

for rev in $revs; do
  author=`git log --format="%an" -n 1 $rev`
  date=`git log --format="%at" -n 1 $rev`

  git show --stat $rev |
    sed '$d' |
    egrep "(lib|spec)" |
    awk -v author="$author" -v rev="$rev" -v date="$date" '{
      split($1,a,"/"); sum[a[1]] += $3
    } END {
      if (sum["lib"]) print rev "," date "," author "," (sum["spec"] + sum["lib"]) "," (sum["spec"]/sum["lib"])
    } '
done

Would be nice not to shell out to git log three times, if anyone has any suggestions. This gives you one line per commit with the ref, timestamp, author, lines changed, code:test ratio, for example:

1
e10db7972b236c9b5e3eddc13e879f120cc4a82f,1333223104,Xavier Shay,42,1.33333
  • Posted on May 13, 2012
  • Tagged code, git

Storing build time in git notes with zsh

Playing around with git notes, having seen them on the github blog. I needed to update to git 1.7.2 (homebrew has it). The following shell command stores the run time of your specs inside a note on the latest commit:

1
{time rake spec} 2> >(tail -n 1 | cut -f 10 -d ' ' - |  git notes --ref=buildtime add -F - -f )

Breaking down the tricky bits:

{time rake spec} Honestly, I cargo culted the curly braces, and can’t find a good description of exactly what they do in this instance. It’s some sort of grouping thing: I found without them time didn’t apply properly.

2> time prints its output to STDERR, 2> redirects STDERR to the next argument. It is kind of like |, but for STDERR rather than STDOUT.

1
{time sleep 0.1} 2> /tmp/time.log

>( ... ) Rather than redirecting STDERR to a file, this allows us to pipe it in to more commands.

tail -n 1 rake spec also prints to STDERR, so pipe through tail to grab only the last line (which will be from time)

cut -f 10 -d ' ' - Split the line on a space character, choose the tenth column of the output from time, which is the total time taken. The trailing - says “read from STDIN”.

git notes --ref=buildtime add -F - -f Add a note to the latest commit (HEAD is default) in the buildtime namespace. -F - reads the note content from STDIN, which by now is only the final time taken for the spec run, and -f forces an update of the note if it already exists.

  • Posted on September 06, 2010
  • Tagged code, git, zsh
A pretty flower Another pretty flower