aboutsummaryrefslogtreecommitdiffstats
path: root/blame
diff options
context:
space:
mode:
authorAlberto Cortés <alberto@sourced.tech>2015-11-16 03:20:01 +0100
committerAlberto Cortés <alberto@sourced.tech>2015-11-25 11:09:51 +0100
commitd643cea1e8a6d618b2eddfdbed086c7bdf208658 (patch)
treeb862c72ccb674910d24eac2ab424a7fb5ea3f0fb /blame
parentcaab43e7f4ee10a15b2af826485b688473b34356 (diff)
downloadgo-git-d643cea1e8a6d618b2eddfdbed086c7bdf208658.tar.gz
Blame support for files
This also includes a diff package and revlist package (needed by blame) Some extra packfiles (<1MB) are also included, to be used as fixtures in the tests.
Diffstat (limited to 'blame')
-rw-r--r--blame/blame.go223
-rwxr-xr-xblame/blame2humantest.bash47
-rw-r--r--blame/blame_test.go810
3 files changed, 1080 insertions, 0 deletions
diff --git a/blame/blame.go b/blame/blame.go
new file mode 100644
index 0000000..774362b
--- /dev/null
+++ b/blame/blame.go
@@ -0,0 +1,223 @@
+// Package blame contains blaming functionality for files in the repo.
+//
+// Blaming a file is finding what commit was the last to modify each of
+// the lines in the file, therefore the output of a blaming operation is
+// usualy a slice of commits, one commit per line in the file.
+//
+// This package also provides a pretty print function to output the
+// results of a blame in a similar format to the git-blame command.
+package blame
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+
+ "gopkg.in/src-d/go-git.v2"
+ "gopkg.in/src-d/go-git.v2/core"
+ "gopkg.in/src-d/go-git.v2/diff"
+ "gopkg.in/src-d/go-git.v2/revlist"
+)
+
+// Blame returns the last commit that modified each line of a file in
+// a repository.
+//
+// The file to blame is identified by the input arguments: repo, commit and path.
+// The output is a slice of commits, one for each line in the file.
+//
+// Blaming a file is a two step process:
+//
+// 1. Create a linear history of the commits affecting a file. We use
+// revlist.New for that.
+//
+// 2. Then build a graph with a node for every line in every file in
+// the history of the file.
+//
+// Each node (line) holds the commit where it was introduced or
+// last modified. To achieve that we use the FORWARD algorithm
+// described in Zimmermann, et al. "Mining Version Archives for
+// Co-changed Lines", in proceedings of the Mining Software
+// Repositories workshop, Shanghai, May 22-23, 2006.
+//
+// Each node is asigned a commit: Start by the nodes in the first
+// commit. Assign that commit as the creator of all its lines.
+//
+// Then jump to the nodes in the next commit, and calculate the diff
+// between the two files. Newly created lines get
+// assigned the new commit as its origin. Modified lines also get
+// this new commit. Untouched lines retain the old commit.
+//
+// All this work is done in the assignOrigin function.
+//
+// This function holds all the internal relevant data in a blame
+// struct, that is not exported.
+//
+// TODO: ways to improve the efficiency of this function:
+//
+// 1. Improve revlist
+//
+// 2. Improve how to traverse the history (example a backward
+// traversal will be much more efficient)
+//
+// TODO: ways to improve the functrion in general
+//
+// 1. Add memoization betweenn revlist and assign.
+//
+// 2. It is using much more memmory than needed, see the TODOs below.
+func Blame(repo *git.Repository, commit *git.Commit, path string) ([]*git.Commit, error) {
+ // init the internal blame struct
+ b := new(blame)
+ b.repo = repo
+ b.fRev = commit
+ b.path = path
+
+ // calculte the history of the file and store it in the
+ // internal blame struct.
+ var err error
+ b.revs, err = revlist.New(b.repo, b.fRev, b.path)
+ if err != nil {
+ return nil, err
+ }
+ sort.Sort(b.revs) // for forward blame, we need the history sorted by commit date
+
+ // allocate space for the data in all the revisions of the file
+ b.data = make([]string, len(b.revs))
+
+ // init the graph
+ b.graph = make([][]vertex, len(b.revs))
+
+ // for all every revision of the file, starting with the first
+ // one...
+ var found bool
+ for i, rev := range b.revs {
+ // get the contents of the file
+ b.data[i], found = git.Data(b.path, rev)
+ if !found {
+ continue
+ }
+ // count its lines
+ nLines := git.CountLines(b.data[i])
+ // create a node for each line
+ b.graph[i] = make([]vertex, nLines)
+ // assign a commit to each node
+ // if this is the first revision, then the node is assigned to
+ // this first commit.
+ if i == 0 {
+ for j := 0; j < nLines; j++ {
+ b.graph[i][j] = vertex(b.revs[i])
+ }
+ } else {
+ // if this is not the first commit, then assign to the old
+ // commit or to the new one, depending on what the diff
+ // says.
+ b.assignOrigin(i, i-1)
+ }
+ }
+
+ // fill in the output results: copy the nodes of the last revision
+ // into the result.
+ fVs := b.graph[len(b.graph)-1]
+ result := make([]*git.Commit, 0, len(fVs))
+ for _, v := range fVs {
+ c := git.Commit(*v)
+ result = append(result, &c)
+ }
+ return result, nil
+}
+
+// this struct is internally used by the blame function to hold its
+// intputs, outputs and state.
+type blame struct {
+ repo *git.Repository // the repo holding the history of the file to blame
+ path string // the path of the file to blame
+ fRev *git.Commit // the commit of the final revision of the file to blame
+ revs revlist.Revs // the chain of revisions affecting the the file to blame
+ data []string // the contents on the file in all the revisions TODO: not all data is needed, only the current rev and the prev
+ graph [][]vertex // the graph of the lines in the file across all the revisions TODO: not all vertexes are needed, only the current rev and the prev
+}
+
+type vertex *git.Commit // a vertex only needs to store the original commit it came from
+
+// Assigns origin to vertexes in current (c) rev from data in its previous (p)
+// revision
+func (b *blame) assignOrigin(c, p int) {
+ // assign origin based on diff info
+ hunks := diff.Do(b.data[p], b.data[c])
+ sl := -1 // source line
+ dl := -1 // destination line
+ for h := range hunks {
+ hLines := git.CountLines(hunks[h].Text)
+ for hl := 0; hl < hLines; hl++ {
+ // fmt.Printf("file=%q, rev=%d, r=%d, h=%d, hunk=%v, hunkLine=%d\n", file, rev, r, h, hunks[h], hl)
+ switch {
+ case hunks[h].Type == 0:
+ sl++
+ dl++
+ b.graph[c][dl] = b.graph[p][sl]
+ case hunks[h].Type == 1:
+ dl++
+ b.graph[c][dl] = vertex(b.revs[c])
+ case hunks[h].Type == -1:
+ sl++
+ default:
+ panic("unreachable")
+ }
+ }
+ }
+}
+
+// This will print the results of a Blame as in git-blame.
+func (b *blame) PrettyPrint() string {
+ var buf bytes.Buffer
+
+ contents, found := git.Data(b.path, b.fRev)
+ if !found {
+ panic("PrettyPrint: internal error in repo.Data")
+ }
+
+ lines := strings.Split(contents, "\n")
+ // max line number length
+ mlnl := len(fmt.Sprintf("%s", strconv.Itoa(len(lines))))
+ // max author length
+ mal := b.maxAuthorLength()
+ format := fmt.Sprintf("%%s (%%-%ds %%%dd) %%s\n",
+ mal, mlnl)
+
+ fVs := b.graph[len(b.graph)-1]
+ for ln, v := range fVs {
+ fmt.Fprintf(&buf, format, v.Hash.String()[:8],
+ prettyPrintAuthor(fVs[ln]), ln+1, lines[ln])
+ }
+ return buf.String()
+}
+
+// utility function to pretty print the author.
+func prettyPrintAuthor(c *git.Commit) string {
+ return fmt.Sprintf("%s %s", c.Author.Name, c.Author.When.Format("2006-01-02"))
+}
+
+// utility function to calculate the number of runes needed
+// to print the longest author name in the blame of a file.
+func (b *blame) maxAuthorLength() int {
+ memo := make(map[core.Hash]struct{}, len(b.graph)-1)
+ fVs := b.graph[len(b.graph)-1]
+ m := 0
+ for ln := range fVs {
+ if _, ok := memo[fVs[ln].Hash]; ok {
+ continue
+ }
+ memo[fVs[ln].Hash] = struct{}{}
+ m = max(m, utf8.RuneCountInString(prettyPrintAuthor(fVs[ln])))
+ }
+ return m
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/blame/blame2humantest.bash b/blame/blame2humantest.bash
new file mode 100755
index 0000000..259988f
--- /dev/null
+++ b/blame/blame2humantest.bash
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -e
+
+repo=`git remote show origin | grep Fetch | cut -d' ' -f5`
+branch="master"
+if [ "$#" -eq 1 ] ; then
+ commit=`git log | head -1 | cut -d' ' -f2`
+ path=$1
+elif [ "$#" -eq 2 ] ; then
+ commit=$1
+ path=$2
+else
+ echo "bad number of parameters" > /dev/stderr
+ echo > /dev/stderr
+ echo " try with: [commit] path" > /dev/stderr
+ exit
+fi
+
+blames=`git blame --root $path | cut -d' ' -f1`
+declare -a blame
+i=0
+for shortBlame in $blames ; do
+ blame[$i]=`git show $shortBlame | head -1 | cut -d' ' -f2`
+ i=`expr $i + 1`
+done
+
+# some remotes have the .git, other don't,
+# repoDot makes sure all have
+repoDot="${repo%.git}.git"
+
+echo -e "\t{\"${repoDot}\", \"${branch}\", \"${commit}\", \"${path}\", concat(&[]string{},"
+prev=""
+count=1
+for i in ${blame[@]} ; do
+ if [ "${prev}" == "" ] ; then
+ prev=$i
+ elif [ "$prev" == "$i" ] ; then
+ count=`expr $count + 1`
+ else
+ echo -e "\t\trepeat(\"${prev}\", $count),"
+ count=1
+ prev=$i
+ fi
+done
+echo -e "\t\trepeat(\"${prev}\", $count),"
+echo -e "\t)},"
diff --git a/blame/blame_test.go b/blame/blame_test.go
new file mode 100644
index 0000000..81c02f0
--- /dev/null
+++ b/blame/blame_test.go
@@ -0,0 +1,810 @@
+package blame
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "testing"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/src-d/go-git.v2"
+ "gopkg.in/src-d/go-git.v2/core"
+ "gopkg.in/src-d/go-git.v2/formats/packfile"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+type SuiteCommon struct {
+ repos map[string]*git.Repository
+}
+
+var _ = Suite(&SuiteCommon{})
+
+var fixtureRepos = [...]struct {
+ url string
+ packfile string
+}{
+ {"https://github.com/tyba/git-fixture.git", "../formats/packfile/fixtures/git-fixture.ofs-delta"},
+ {"https://github.com/spinnaker/spinnaker.git", "../formats/packfile/fixtures/spinnaker-spinnaker.pack"},
+}
+
+// create the repositories of the fixtures
+func (s *SuiteCommon) SetUpSuite(c *C) {
+ s.repos = make(map[string]*git.Repository, 0)
+ for _, fixRepo := range fixtureRepos {
+ s.repos[fixRepo.url] = git.NewPlainRepository()
+
+ d, err := os.Open(fixRepo.packfile)
+ defer func() {
+ c.Assert(d.Close(), IsNil)
+ }()
+ c.Assert(err, IsNil)
+
+ r := packfile.NewReader(d)
+ r.Format = packfile.OFSDeltaFormat // TODO: how to know the format of a pack file ahead of time?
+
+ _, err = r.Read(s.repos[fixRepo.url].Storage)
+ c.Assert(err, IsNil)
+ }
+}
+
+var blameTests = [...]struct {
+ // input data to revlist
+ repo string
+ branch string // TODO: remove this, it is no longer needed for local packfiles
+ commit string
+ path string
+ // expected output data form the revlist
+ blames []string
+}{
+ // use the blame2humantest.bash script to easily add more tests.
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "binary.jpg", concat(&[]string{},
+ repeat("35e85108805c84807bc66a02d91535e1e24b38b9", 285),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "CHANGELOG", concat(&[]string{},
+ repeat("b8e471f58bcbca63b07bda20e428190409c2db47", 1),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "go/example.go", concat(&[]string{},
+ repeat("918c48b83bd081e863dbe1b80f8998f058cd8294", 142),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "json/long.json", concat(&[]string{},
+ repeat("af2d6a6954d532f8ffb47615169c8fdf9d383a1a", 6492),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "json/short.json", concat(&[]string{},
+ repeat("af2d6a6954d532f8ffb47615169c8fdf9d383a1a", 22),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "LICENSE", concat(&[]string{},
+ repeat("b029517f6300c2da0f4b651b8642506cd6aaf45d", 22),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "php/crappy.php", concat(&[]string{},
+ repeat("918c48b83bd081e863dbe1b80f8998f058cd8294", 259),
+ )},
+ {"https://github.com/tyba/git-fixture.git", "master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", "vendor/foo.go", concat(&[]string{},
+ repeat("6ecf0ef2c2dffb796033e5a02219af86ec6584e5", 7),
+ )},
+ /*
+ // Failed
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "InstallSpinnaker.sh", concat(&[]string{},
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 2),
+ repeat("a47d0aaeda421f06df248ad65bd58230766bf118", 1),
+ repeat("23673af3ad70b50bba7fdafadc2323302f5ba520", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 29),
+ repeat("9a06d3f20eabb254d0a1e2ff7735ef007ccd595e", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 4),
+ repeat("a47d0aaeda421f06df248ad65bd58230766bf118", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 5),
+ repeat("0c5bb1e4392e751f884f3c57de5d4aee72c40031", 2),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 3),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 7),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 2),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 5),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 7),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 3),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 6),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 10),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 4),
+ repeat("0c5bb1e4392e751f884f3c57de5d4aee72c40031", 2),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 2),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 4),
+ repeat("23673af3ad70b50bba7fdafadc2323302f5ba520", 4),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 4),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("0c5bb1e4392e751f884f3c57de5d4aee72c40031", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 13),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 2),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 6),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 2),
+ repeat("0c5bb1e4392e751f884f3c57de5d4aee72c40031", 1),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 4),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 3),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 2),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 4),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 3),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 15),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 1),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 1),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 8),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 2),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 12),
+ repeat("505577dc87d300cf562dc4702a05a5615d90d855", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 5),
+ repeat("370d61cdbc1f3c90db6759f1599ccbabd40ad6c1", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 4),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 1),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 5),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 3),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 2),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 2),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 9),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 1),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 3),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 4),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("8eb116de9128c314ac8a6f5310ca500b8c74f5db", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 6),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 6),
+ repeat("d2f6214b625db706384b378a29cc4c22237db97a", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 3),
+ repeat("d2f6214b625db706384b378a29cc4c22237db97a", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 4),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("c9c2a0ec03968ab17e8b16fdec9661eb1dbea173", 1),
+ repeat("d2f6214b625db706384b378a29cc4c22237db97a", 2),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 12),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 5),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 3),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 5),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 3),
+ repeat("a47d0aaeda421f06df248ad65bd58230766bf118", 5),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 5),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 2),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 1),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("b2c7142082d52b09ca20228606c31c7479c0833e", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("495c7118e7cf757aa04eab410b64bfb5b5149ad2", 1),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 1),
+ repeat("495c7118e7cf757aa04eab410b64bfb5b5149ad2", 3),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 1),
+ repeat("495c7118e7cf757aa04eab410b64bfb5b5149ad2", 1),
+ repeat("50d0556563599366f29cb286525780004fa5a317", 1),
+ repeat("dd2d03c19658ff96d371aef00e75e2e54702da0e", 1),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 1),
+ repeat("dd2d03c19658ff96d371aef00e75e2e54702da0e", 2),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 2),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 1),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("b5c6053a46993b20d1b91e7b7206bffa54669ad7", 1),
+ repeat("9e74d009894d73dd07773ea6b3bdd8323db980f7", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("d4b48a39aba7d3bd3e8abef2274a95b112d1ae73", 4),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 1),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ repeat("d2f6214b625db706384b378a29cc4c22237db97a", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 3),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 2),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 2),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 4),
+ repeat("d2f6214b625db706384b378a29cc4c22237db97a", 1),
+ repeat("b7015a5d36990d69a054482556127b9c7404a24a", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 5),
+ repeat("b41d7c0e5b20bbe7c8eb6606731a3ff68f4e3941", 2),
+ repeat("d2f6214b625db706384b378a29cc4c22237db97a", 1),
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 5),
+ repeat("ba486de7c025457963701114c683dcd4708e1dee", 4),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 1),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 3),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 1),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 3),
+ repeat("6328ee836affafc1b52127147b5ca07300ac78e6", 2),
+ repeat("01e65d67eed8afcb67a6bdf1c962541f62b299c9", 3),
+ repeat("3de4f77c105f700f50d9549d32b9a05a01b46c4b", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 2),
+ repeat("370d61cdbc1f3c90db6759f1599ccbabd40ad6c1", 6),
+ repeat("dd7e66c862209e8b912694a582a09c0db3227f0d", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 2),
+ repeat("dd7e66c862209e8b912694a582a09c0db3227f0d", 3),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("dd7e66c862209e8b912694a582a09c0db3227f0d", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 3),
+ )},
+ */
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/reconfigure_spinnaker.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 22),
+ repeat("c89dab0d42f1856d157357e9010f8cc6a12f5b1f", 7),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/validate_configuration.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 29),
+ repeat("1e3d328a2cabda5d0aaddc5dec65271343e0dc37", 19),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 15),
+ repeat("b5d999e2986e190d81767cd3cfeda0260f9f6fb8", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 12),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("b5d999e2986e190d81767cd3cfeda0260f9f6fb8", 8),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("b5d999e2986e190d81767cd3cfeda0260f9f6fb8", 4),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 46),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("1e3d328a2cabda5d0aaddc5dec65271343e0dc37", 42),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("1e3d328a2cabda5d0aaddc5dec65271343e0dc37", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("1e3d328a2cabda5d0aaddc5dec65271343e0dc37", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 8),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("1e3d328a2cabda5d0aaddc5dec65271343e0dc37", 3),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 12),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 10),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 69),
+ repeat("b5d999e2986e190d81767cd3cfeda0260f9f6fb8", 7),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/run.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 185),
+ )},
+ /*
+ // Fail by 3
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/configurator.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 53),
+ repeat("c89dab0d42f1856d157357e9010f8cc6a12f5b1f", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("e805183c72f0426fb073728c01901c2fd2db1da6", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 6),
+ repeat("023d4fb17b76e0fe0764971df8b8538b735a1d67", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 36),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 3),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("c89dab0d42f1856d157357e9010f8cc6a12f5b1f", 13),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("c89dab0d42f1856d157357e9010f8cc6a12f5b1f", 18),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 1),
+ repeat("023d4fb17b76e0fe0764971df8b8538b735a1d67", 17),
+ repeat("c89dab0d42f1856d157357e9010f8cc6a12f5b1f", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 43),
+ )},
+ */
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/fetch.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 140),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/yaml_util.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 68),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 8),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("023d4fb17b76e0fe0764971df8b8538b735a1d67", 3),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 7),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 12),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 33),
+ repeat("bb6325e4e629fc7348a6d0e6842280d5304160ff", 40),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("b5d999e2986e190d81767cd3cfeda0260f9f6fb8", 7),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("b5d999e2986e190d81767cd3cfeda0260f9f6fb8", 4),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/spinnaker_runner.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 235),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 13),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 7),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 20),
+ repeat("707bcdce04eabdb0549868ad1a8efa2d76b9bdf1", 7),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 21),
+ repeat("2b28ea424acc8f2817d3298c143fae68bcad91a7", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 15),
+ repeat("d73f9cee49a5ad27a42a6e18af7c49a8f28ad8a8", 17),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 336),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/transform_old_config.py", concat(&[]string{},
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 43),
+ repeat("4584fab37e93d66fd1896d07fa3427f8056711bc", 1),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 12),
+ repeat("bb6325e4e629fc7348a6d0e6842280d5304160ff", 1),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 16),
+ repeat("0777fadf4ca6f458d7071de414f9bd5417911037", 1),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 1),
+ repeat("0777fadf4ca6f458d7071de414f9bd5417911037", 1),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 8),
+ repeat("0777fadf4ca6f458d7071de414f9bd5417911037", 1),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 31),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/spinnaker/__init__.py", []string{}},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "pylib/yaml/LICENSE", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 19),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "settings.gradle", concat(&[]string{},
+ repeat("ce9f123d790717599aaeb76bc62510de437761be", 1),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "gradle/wrapper/gradle-wrapper.jar", concat(&[]string{},
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 1),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 7),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 2),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 2),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 3),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 1),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 10),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 11),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 29),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 7),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 58),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 1),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 1),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 2),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 2),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 13),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 4),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 3),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 13),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 2),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 9),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 3),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 1),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 17),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 3),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 6),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 6),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 3),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 5),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 4),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 3),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 2),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 6),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 55),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "gradle/wrapper/gradle-wrapper.properties", concat(&[]string{},
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ repeat("11d6c1020b1765e236ca65b2709d37b5bfdba0f4", 4),
+ repeat("bc02440df2ff95a014a7b3cb11b98c3a2bded777", 1),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "gradle/buildViaTravis.sh", concat(&[]string{},
+ repeat("7ecc2ad58e24a5b52504985467a10c6a3bb85b9b", 24),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/settings.js", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 17),
+ repeat("99534ecc895fe17a1d562bb3049d4168a04d0865", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 43),
+ repeat("d2838db9f6ef9628645e7d04cd9658a83e8708ea", 1),
+ repeat("637ba49300f701cfbd859c1ccf13c4f39a9ba1c8", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 13),
+ )},
+ /*
+ // fail a few lines
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/default-spinnaker-local.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 9),
+ repeat("5e09821cbd7d710405b61cab0a795c2982a71b9c", 2),
+ repeat("99534ecc895fe17a1d562bb3049d4168a04d0865", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 2),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 5),
+ repeat("5e09821cbd7d710405b61cab0a795c2982a71b9c", 2),
+ repeat("a596972a661d9a7deca8abd18b52ce1a39516e89", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 5),
+ repeat("5e09821cbd7d710405b61cab0a795c2982a71b9c", 1),
+ repeat("8980daf661408a3faa1f22c225702a5c1d11d5c9", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 25),
+ repeat("caf6d62e8285d4681514dd8027356fb019bc97ff", 1),
+ repeat("eaf7614cad81e8ab5c813dd4821129d0c04ea449", 1),
+ repeat("caf6d62e8285d4681514dd8027356fb019bc97ff", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 24),
+ repeat("974b775a8978b120ff710cac93a21c7387b914c9", 2),
+ repeat("3ce7b902a51bac2f10994f7d1f251b616c975e54", 1),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 6),
+ repeat("41e96c54a478e5d09dd07ed7feb2d8d08d8c7e3c", 14),
+ repeat("7c8d9a6081d9cb7a56c479bfe64d70540ea32795", 5),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 2),
+ )},
+ */
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/igor.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 15),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/echo.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 15),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 1),
+ repeat("7c8d9a6081d9cb7a56c479bfe64d70540ea32795", 4),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 24),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/orca.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 40),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/rosco.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 9),
+ repeat("24dc2d465c85cb242262ab6bc236bde3ffbb93e0", 3),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 3),
+ repeat("caf6d62e8285d4681514dd8027356fb019bc97ff", 2),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 3),
+ repeat("974b775a8978b120ff710cac93a21c7387b914c9", 2),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 1),
+ repeat("974b775a8978b120ff710cac93a21c7387b914c9", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 2),
+ repeat("974b775a8978b120ff710cac93a21c7387b914c9", 2),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/rush.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 18),
+ )},
+ /*
+ // fail one line
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/spinnaker.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 32),
+ repeat("41e96c54a478e5d09dd07ed7feb2d8d08d8c7e3c", 2),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 1),
+ repeat("41e96c54a478e5d09dd07ed7feb2d8d08d8c7e3c", 6),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 2),
+ repeat("41e96c54a478e5d09dd07ed7feb2d8d08d8c7e3c", 2),
+ repeat("5a2a845bc08974a36d599a4a4b7e25be833823b0", 2),
+ repeat("41e96c54a478e5d09dd07ed7feb2d8d08d8c7e3c", 3),
+ repeat("7c8d9a6081d9cb7a56c479bfe64d70540ea32795", 3),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 50),
+ repeat("974b775a8978b120ff710cac93a21c7387b914c9", 2),
+ repeat("d4553dac205023fa77652308af1a2d1cf52138fb", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 9),
+ repeat("caf6d62e8285d4681514dd8027356fb019bc97ff", 1),
+ repeat("eaf7614cad81e8ab5c813dd4821129d0c04ea449", 1),
+ repeat("caf6d62e8285d4681514dd8027356fb019bc97ff", 1),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 39),
+ repeat("079e42e7c979541b6fab7343838f7b9fd4a360cd", 6),
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 15),
+ )},
+ */
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "config/gate.yml", concat(&[]string{},
+ repeat("ae904e8d60228c21c47368f6a10f1cc9ca3aeebf", 29),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/run_dev.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 23),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/build_google_image.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 16),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 18),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/refresh_source.py", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 21),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 14),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 12),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 35),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 10),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 34),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 11),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 17),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 8),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 1),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 23),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 8),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 5),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 6),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 7),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 1),
+ repeat("ba10a5d5615f68eb9115cada1d639066a53ddc4d", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 7),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 1),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 9),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a80d310d9ca42c3422627f2ecca7ee1dbefa602a", 13),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 11),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 6),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 3),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("b192881117651465df2385ef89344dd5dd4810f3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 5),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 12),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 11),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("1e14f94bcf82694fdc7e2dcbbfdbbed58db0f4d9", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 14),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 12),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("13ad4df676a16caf2ff1ca216be615d5aee37db3", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 36),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 7),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 4),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 21),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 3),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 6),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 19),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 8),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 14),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("3d3819741bada73fb950f14309c97e1f63492ec6", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("e3620408a7776039f7853a21748921ea2a281953", 1),
+ )},
+ /*
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/install_development.sh", concat(&[]string{},
+ repeat("99534ecc895fe17a1d562bb3049d4168a04d0865", 1),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 71),
+ )},
+ */
+ /*
+ // FAIL two lines interchanged
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/bootstrap_dev.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 95),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 10),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 7),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 3),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 12),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 6),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 4),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("376599177551c3f04ccc94d71bbb4d037dec0c3f", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 17),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 2),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 8),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 4),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 1),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 6),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 4),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 10),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 2),
+ repeat("fc28a378558cdb5bbc08b6dcb96ee77c5b716760", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 8),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 1),
+ repeat("fc28a378558cdb5bbc08b6dcb96ee77c5b716760", 1),
+ repeat("d1ff4e13e9e0b500821aa558373878f93487e34b", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 4),
+ repeat("24551a5d486969a2972ee05e87f16444890f9555", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("24551a5d486969a2972ee05e87f16444890f9555", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 8),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 13),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 5),
+ repeat("24551a5d486969a2972ee05e87f16444890f9555", 1),
+ repeat("838aed816872c52ed435e4876a7b64dba0bed500", 8),
+ )},
+ */
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/build_release.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 17),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/stop_dev.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 23),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/build_google_image.packer", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("8fe3f13ad04ee25fde0add4ed19d29acd49a5916", 1),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 1),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 2),
+ repeat("8fe3f13ad04ee25fde0add4ed19d29acd49a5916", 1),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 14),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 1),
+ repeat("0d9c9cef53af38cefcb6801bb492aaed3f2c9a42", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 8),
+ repeat("6eb5d9c5225224bfe59c401182a2939d6c27fc00", 1),
+ repeat("f66196ceed7d6aeca313b0632657ab762487ced3", 1),
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 3),
+ )},
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/refresh_source.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 17),
+ )},
+ /*
+ // FAIL move?
+ {"https://github.com/spinnaker/spinnaker.git", "master", "f39d86f59a0781f130e8de6b2115329c1fbe9545", "dev/create_google_dev_vm.sh", concat(&[]string{},
+ repeat("a24001f6938d425d0e7504bdf5d27fc866a85c3d", 20),
+ )},
+ */
+}
+
+// run a blame on all the suite's tests
+func (s *SuiteCommon) TestBlame(c *C) {
+ for _, t := range blameTests {
+ fmt.Println("Blamming", t.repo, t.branch, t.commit, t.path)
+ repo, ok := s.repos[t.repo]
+ c.Assert(ok, Equals, true)
+
+ commit, err := repo.Commit(core.NewHash(t.commit))
+ c.Assert(err, IsNil)
+
+ blames, err := Blame(repo, commit, t.path)
+ c.Assert(err, IsNil)
+
+ c.Assert(len(blames), Equals, len(t.blames), Commentf("\nrepo=%s, branch=%s, commit=%s, path=%s",
+ t.repo, t.branch, t.commit, t.path))
+ c.Assert(blames, DeepEquals, s.commits(c, t.repo, t.blames...), Commentf("\nrepo=%s, branch=%s, commit=%s, path=%s, \n%s",
+ t.repo, t.branch, t.commit, t.path, compareSideBySide2(t.blames, blames)))
+ }
+}
+
+// TODO: duplicated from revlist/revlist_test.go
+// returns the commits from a slice of hashes
+func (s *SuiteCommon) commits(cc *C, repo string, hs ...string) []*git.Commit {
+ r, ok := s.repos[repo]
+ cc.Assert(ok, Equals, true)
+ result := make([]*git.Commit, 0, len(hs))
+ for _, h := range hs {
+ c, err := r.Commit(core.NewHash(h))
+ cc.Assert(err, IsNil)
+ result = append(result, c)
+ }
+ return result
+}
+
+// TODO: duplicated from revlist/revlist_test.go
+// same length is assumed
+func compareSideBySide2(a []string, b []*git.Commit) string {
+ var buf bytes.Buffer
+ buf.WriteString("\t EXPECTED OBTAINED ")
+ var sep string
+ var obtained string
+ for i := range a {
+ obtained = b[i].Hash.String()
+ if a[i] != obtained {
+ sep = "------"
+ } else {
+ sep = " "
+ }
+ buf.WriteString(fmt.Sprintf("\n%d", i+1))
+ buf.WriteString(sep)
+ buf.WriteString(a[i])
+ buf.WriteString(sep)
+ buf.WriteString(obtained)
+ }
+ return buf.String()
+}
+
+// utility function to avoid writing so many repeated commits
+func repeat(s string, n int) []string {
+ if n < 0 {
+ panic("repeat: n < 0")
+ }
+ r := make([]string, 0, n)
+ for i := 0; i < n; i++ {
+ r = append(r, s)
+ }
+ return r
+}
+
+// utility function to concat slices
+func concat(dst *[]string, vargs ...[]string) []string {
+ for _, ss := range vargs {
+ for _, s := range ss {
+ *dst = append(*dst, s)
+ }
+ }
+ return *dst
+}