Git post merge hook to be notified when important/notable files change Jul 29

Most of us work in projects where other devs screw up our code and introduce unintentional bugs. The right way to fix this is to have lots of integration/unit tests to catch them when they happen. However, sometimes you may not have all the integration tests you need. I wrote this git merge hook to be notified whenever I update my local repo and if any notable files have changed.

The .git/hooks/post-merge file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env ruby

changed_files = `git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD`.lines.map(&:chomp)
tracked_files = File.read("./tracked-files").lines.map(&:chomp)

notable_files = changed_files & tracked_files

if notable_files.empty?
  puts "----------NO NOTABLE CHANGES----------"
  exit(0)
end

puts "----------NOTABLE CHANGES:----------"
puts notable_files.join("\n")
puts "----------------------------------------"

system("git diff ORIG_HEAD HEAD -- #{notable_files.join(" ")}")

This expects you to have a file called “tracked-files” at the root of your repo.

Sample tracked-files

1
2
app/controllers/application_controller.rb
app/controllers/myawesome_controller.rb

A better throttler? Nov 6

In a previous post I wrote a simple throttler using channels. On karlseguin’s suggestion, I reimplemented it using sync.Cond. You can check it out at https://bitbucket.org/utils/sync2.

Throttler allows you to restrict a goroutine to a max number of concurrent executions. Think of it as a mutex which allows ‘n’ number of locks to be acquired.

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
58
59
60
61
62
63
64
65
66
67
68
package sync2

import (
    "sync"
)

//Throttler allows you to restrict a goroutine to a max
//number of concurrent executions. Think of it as a mutex
//which allows 'n' number of locks to be acquired.
type Throttler interface {
    //Lock stops execution and waits if the number of goroutines
    //running has reached the max level of the throttler.
    //It returns the throttler on which it is being called to allow chaining
    //    e.g.
    //    var t= sync2.NewThrottler(10)
    //    func throttledFunc(){
    //      defer t.Lock().Unlock()
    //      ..
    //     }
    Lock() Throttler
    //Unlock releases the throttle on the function/goroutine
    //so other goroutines can continue execution if they were waiting.
    Unlock()
}

type throttler struct {
    l       sync.Locker
    c       *sync.Cond
    running int
    max     int
}

//make sure throttler implements the interface
var _ Throttler = &throttler{}

//NewThrottler creates a throttler which allows you to limit
//the number of concurrent executions of a goroutine or function
func NewThrottler(max int) Throttler {
    l := &sync.Mutex{}
    c := sync.NewCond(l)
    return &throttler{c: c, max: max, l: l}
}

//Unlock releases the throttle on the function/goroutine
//so other goroutines can continue execution if they were waiting.
func (t *throttler) Unlock() {
    t.l.Lock()
    defer t.l.Unlock()
    t.running--
    //signal one of the waiting goroutines to start executing
    t.c.Signal()
}

//Lock stops execution and waits if the number of goroutines
//running has reached the max level of the throttler.
func (t *throttler) Lock() Throttler {
    t.l.Lock()
    defer t.l.Unlock()

    //wait till we have an empty slot
    for (t.max - t.running) < 1 {
        t.c.Wait() //suspends execution of the calling goroutine
    }

    //now we are running
    t.running++
    return t
}

Simple function throttler in golang Nov 4

Go is fun, I love writing code in go. I am rewriting the whole backend for http://www.websrvr.in/ using go and am loving every moment of it. Here is a small throttler I wrote to throttle goroutines in my code. The simplicity of this blows my mind, In another language I would have to bend over backwards to get this done.

Play with it in Go playground

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
package main

import (
    "fmt"
    "sync"
    "time"
)

//start of throttler
type Throttler chan bool

func NewThrottler(n int) Throttler {
    return make(chan bool, n)
}

func (t Throttler) Fill() Throttler {
    t <- true
    fmt.Print("+") //for demo
    return t
}

func (t Throttler) Drain() {
    <-t
    fmt.Print("-") //for demo
}

//end of throttler
//this is all the code you need.
//Also remove the fmt.Print statements if you want to use this in your code

func main() {
    //creates a throttler which runs a maximum of 2 routines concurrently
    t := NewThrottler(2)

    //demo code
    st := time.Now()
    wg := &sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            //used to throttle
            defer wg.Done()
            defer t.Fill().Drain()
            fmt.Print("W")
            time.Sleep(1 * time.Millisecond) //to simulate work
        }()
    }

    wg.Wait()
    fmt.Println("\nTime to finish:", time.Now().Sub(st))
}

//Output
//+W+W--+W+W--+W+W--+W+W--+W+W--
//Time to finish: 5.756916ms