Skip to content
Snippets Groups Projects
Commit 214889b7 authored by Nick Thomas's avatar Nick Thomas
Browse files

update dependencies

parent b9b3176b
No related branches found
No related tags found
1 merge request!1Initial implementation of an elasticsearch indexer in Go
Pipeline #
Showing
with 982 additions and 127 deletions
Loading
Loading
@@ -3,10 +3,10 @@ package git
import (
"fmt"
 
"srcd.works/go-git.v4"
"srcd.works/go-git.v4/plumbing"
"srcd.works/go-git.v4/plumbing/difftree"
"srcd.works/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/utils/merkletrie"
)
 
var (
Loading
Loading
@@ -37,7 +37,7 @@ func NewRepo(projectPath string, fromSHA string, toSHA string) (*Repo, error) {
} else {
out.FromHash = plumbing.NewHash(fromSHA)
 
commit, err := repo.Commit(out.FromHash)
commit, err := repo.CommitObject(out.FromHash)
if err != nil {
return nil, fmt.Errorf("Bad from SHA (%s): %s", out.FromHash, err)
}
Loading
Loading
@@ -56,7 +56,7 @@ func NewRepo(projectPath string, fromSHA string, toSHA string) (*Repo, error) {
out.ToHash = plumbing.NewHash(toSHA)
}
 
commit, err := repo.Commit(out.ToHash)
commit, err := repo.CommitObject(out.ToHash)
if err != nil {
return nil, fmt.Errorf("Bad to SHA (%s): %s", out.ToHash, err)
}
Loading
Loading
@@ -66,7 +66,7 @@ func NewRepo(projectPath string, fromSHA string, toSHA string) (*Repo, error) {
return out, nil
}
 
func (r *Repo) Diff() (difftree.Changes, error) {
func (r *Repo) Diff() (object.Changes, error) {
var fromTree, toTree *object.Tree
 
if r.FromCommit != nil {
Loading
Loading
@@ -83,7 +83,7 @@ func (r *Repo) Diff() (difftree.Changes, error) {
return nil, err
}
 
return difftree.DiffTree(fromTree, toTree)
return object.DiffTree(fromTree, toTree)
}
 
type FileFunc func(file *object.File, fromCommit, toCommit *object.Commit) error
Loading
Loading
@@ -100,14 +100,19 @@ func (r *Repo) EachFileChange(ins, mod, del FileFunc) error {
return err
}
 
switch change.Action {
case difftree.Insert:
action, err := change.Action()
if err != nil {
return err
}
switch action {
case merkletrie.Insert:
toF.Name = change.To.Name
err = ins(toF, r.FromCommit, r.ToCommit)
case difftree.Modify:
case merkletrie.Modify:
toF.Name = change.To.Name
err = mod(toF, r.FromCommit, r.ToCommit)
case difftree.Delete:
case merkletrie.Delete:
fromF.Name = change.From.Name
err = del(fromF, r.FromCommit, r.ToCommit)
default:
Loading
Loading
Loading
Loading
@@ -5,7 +5,7 @@ import (
"fmt"
"io/ioutil"
 
"srcd.works/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/object"
 
"gitlab.com/gitlab-org/es-git-go/linguist"
)
Loading
Loading
Loading
Loading
@@ -3,7 +3,7 @@ package indexer
import (
"fmt"
 
"srcd.works/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/object"
)
 
type Commit struct {
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ import (
"fmt"
"log"
 
"srcd.works/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/object"
 
"gitlab.com/gitlab-org/es-git-go/git"
)
Loading
Loading
Loading
Loading
@@ -3,7 +3,7 @@ package indexer
import (
"time"
 
"srcd.works/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/object"
)
 
const (
Loading
Loading
# General
WORKDIR = $(PWD)
# Go parameters
GOCMD = go
GOTEST = $(GOCMD) test -v
# Coverage
COVERAGE_REPORT = coverage.txt
COVERAGE_PROFILE = profile.out
COVERAGE_MODE = atomic
test-coverage:
cd $(WORKDIR); \
echo "" > $(COVERAGE_REPORT); \
for dir in `find . -name "*.go" | grep -o '.*/' | sort | uniq`; do \
$(GOTEST) $$dir -coverprofile=$(COVERAGE_PROFILE) -covermode=$(COVERAGE_MODE); \
if [ $$? != 0 ]; then \
exit 2; \
fi; \
if [ -f $(COVERAGE_PROFILE) ]; then \
cat $(COVERAGE_PROFILE) >> $(COVERAGE_REPORT); \
rm $(COVERAGE_PROFILE); \
fi; \
done; \
# go-billy [![GoDoc](https://godoc.org/srcd.works/go-billy.v1?status.svg)](https://godoc.org/srcd.works/go-billy.v1) [![Build Status](https://travis-ci.org/src-d/go-billy.svg)](https://travis-ci.org/src-d/go-billy) [![codebeat badge](https://codebeat.co/badges/03bdec03-b477-4472-bbe3-b552e3799174)](https://codebeat.co/projects/github-com-src-d-go-billy)
# go-billy [![GoDoc](https://godoc.org/gopkg.in/src-d/go-billy.v2?status.svg)](https://godoc.org/gopkg.in/src-d/go-billy.v2) [![Build Status](https://travis-ci.org/src-d/go-billy.svg)](https://travis-ci.org/src-d/go-billy) [![codebeat badge](https://codebeat.co/badges/03bdec03-b477-4472-bbe3-b552e3799174)](https://codebeat.co/projects/github-com-src-d-go-billy)
 
An interface to abstract several storages.
 
Loading
Loading
@@ -8,7 +8,7 @@ This library was extracted from
## Installation
 
```go
go get -u srcd.works/go-billy.v1/...
go get -u gopkg.in/src-d/go-billy.v2/...
```
 
## Why billy?
Loading
Loading
Loading
Loading
@@ -21,6 +21,7 @@ var (
// * Get a temporal file.
// * Rename files.
// * Remove files.
// * Create directories.
// * Join parts of path.
// * Obtain a filesystem starting on a subdirectory in the current filesystem.
// * Get the base path for the filesystem.
Loading
Loading
@@ -35,6 +36,7 @@ type Filesystem interface {
TempFile(dir, prefix string) (File, error)
Rename(from, to string) error
Remove(filename string) error
MkdirAll(filename string, perm os.FileMode) error
Join(elem ...string) string
Dir(path string) Filesystem
Base() string
Loading
Loading
@@ -65,3 +67,80 @@ func (f *BaseFile) Filename() string {
func (f *BaseFile) IsClosed() bool {
return f.Closed
}
type removerAll interface {
RemoveAll(string) error
}
// RemoveAll removes path and any children it contains.
// It removes everything it can but returns the first error
// it encounters. If the path does not exist, RemoveAll
// returns nil (no error).
func RemoveAll(fs Filesystem, path string) error {
r, ok := fs.(removerAll)
if ok {
return r.RemoveAll(path)
}
return removeAll(fs, path)
}
func removeAll(fs Filesystem, path string) error {
// This implementation is adapted from os.RemoveAll.
// Simple case: if Remove works, we're done.
err := fs.Remove(path)
if err == nil || os.IsNotExist(err) {
return nil
}
// Otherwise, is this a directory we need to recurse into?
dir, serr := fs.Stat(path)
if serr != nil {
if os.IsNotExist(serr) {
return nil
}
return serr
}
if !dir.IsDir() {
// Not a directory; return the error from Remove.
return err
}
// Directory.
fis, err := fs.ReadDir(path)
if err != nil {
if os.IsNotExist(err) {
// Race. It was deleted between the Lstat and Open.
// Return nil per RemoveAll's docs.
return nil
}
return err
}
// Remove contents & return first error.
err = nil
for _, fi := range fis {
cpath := fs.Join(path, fi.Name())
err1 := removeAll(fs, cpath)
if err == nil {
err = err1
}
}
// Remove directory.
err1 := fs.Remove(path)
if err1 == nil || os.IsNotExist(err1) {
return nil
}
if err == nil {
err = err1
}
return err
}
// Package os provides a billy filesystem for the OS.
package osfs // import "srcd.works/go-billy.v1/osfs"
package osfs // import "gopkg.in/src-d/go-billy.v2/osfs"
 
import (
"io/ioutil"
Loading
Loading
@@ -7,7 +7,12 @@ import (
"path"
"path/filepath"
 
"srcd.works/go-billy.v1"
"gopkg.in/src-d/go-billy.v2"
)
const (
defaultDirectoryMode = 0755
defaultCreateMode = 0666
)
 
// OS is a filesystem based on the os filesystem
Loading
Loading
@@ -25,7 +30,7 @@ func New(baseDir string) *OS {
// Create creates a file and opens it with standard permissions
// and modes O_RDWR, O_CREATE and O_TRUNC.
func (fs *OS) Create(filename string) (billy.File, error) {
return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode)
}
 
// OpenFile is equivalent to standard os.OpenFile.
Loading
Loading
@@ -55,7 +60,7 @@ func (fs *OS) OpenFile(filename string, flag int, perm os.FileMode) (billy.File,
func (fs *OS) createDir(fullpath string) error {
dir := filepath.Dir(fullpath)
if dir != "." {
if err := os.MkdirAll(dir, 0755); err != nil {
if err := os.MkdirAll(dir, defaultDirectoryMode); err != nil {
return err
}
}
Loading
Loading
@@ -93,6 +98,12 @@ func (fs *OS) Rename(from, to string) error {
return os.Rename(from, to)
}
 
// MkdirAll creates a directory.
func (fs *OS) MkdirAll(path string, perm os.FileMode) error {
fullpath := fs.Join(fs.base, path)
return os.MkdirAll(fullpath, defaultDirectoryMode)
}
// Open opens a file in read-only mode.
func (fs *OS) Open(filename string) (billy.File, error) {
return fs.OpenFile(filename, os.O_RDONLY, 0)
Loading
Loading
@@ -151,6 +162,13 @@ func (fs *OS) Base() string {
return fs.base
}
 
// RemoveAll removes a file or directory recursively. Removes everything it can,
// but returns the first error.
func (fs *OS) RemoveAll(path string) error {
fullpath := fs.Join(fs.base, path)
return os.RemoveAll(fullpath)
}
// osFile represents a file in the os filesystem
type osFile struct {
billy.BaseFile
Loading
Loading
Loading
Loading
@@ -16,7 +16,7 @@ COVERAGE_PROFILE = profile.out
COVERAGE_MODE = atomic
 
ifneq ($(origin CI), undefined)
WORKDIR := $(GOPATH)/src/srcd.works/go-git.v4
WORKDIR := $(GOPATH)/src/gopkg.in/src-d/go-git.v4
endif
 
build-git:
Loading
Loading
@@ -31,11 +31,11 @@ build-git:
fi
 
test:
cd $(WORKDIR); \
@cd $(WORKDIR); \
$(GOTEST) ./...
 
test-coverage:
cd $(WORKDIR); \
@cd $(WORKDIR); \
echo "" > $(COVERAGE_REPORT); \
for dir in `find . -name "*.go" | grep -o '.*/' | sort | uniq`; do \
$(GOTEST) $$dir -coverprofile=$(COVERAGE_PROFILE) -covermode=$(COVERAGE_MODE); \
Loading
Loading
# go-git [![GoDoc](https://godoc.org/srcd.works/go-git.v4?status.svg)](https://godoc.org/srcd.works/go-git.v4) [![Build Status](https://travis-ci.org/src-d/go-git.svg)](https://travis-ci.org/src-d/go-git) [![codecov.io](https://codecov.io/github/src-d/go-git/coverage.svg)](https://codecov.io/github/src-d/go-git) [![codebeat badge](https://codebeat.co/badges/b6cb2f73-9e54-483d-89f9-4b95a911f40c)](https://codebeat.co/projects/github-com-src-d-go-git)
# go-git [![GoDoc](https://godoc.org/gopkg.in/src-d/go-git.v4?status.svg)](https://godoc.org/github.com/src-d/go-git) [![Build Status](https://travis-ci.org/src-d/go-git.svg)](https://travis-ci.org/src-d/go-git) [![codecov.io](https://codecov.io/github/src-d/go-git/coverage.svg)](https://codecov.io/github/src-d/go-git) [![codebeat badge](https://codebeat.co/badges/b6cb2f73-9e54-483d-89f9-4b95a911f40c)](https://codebeat.co/projects/github-com-src-d-go-git)
 
A low level and highly extensible git implementation in **pure Go**.
A highly extensible git implementation in **pure Go**.
 
*go-git* aims to reach the completeness of [libgit2](https://libgit2.github.com/) or [jgit](http://www.eclipse.org/jgit/), nowadays covers the **majority** of the plumbing **read operations** and **some** of the main **write operations**, but lacks the main porcelain operations such as merges.
 
Loading
Loading
@@ -20,27 +20,53 @@ Installation
The recommended way to install *go-git* is:
 
```
go get -u srcd.works/go-git.v4/...
go get -u gopkg.in/src-d/go-git.v4/...
```
 
 
Examples
--------
 
Cloning a repository and printing the history of HEAD, just like `git log` does
> Please note that the functions `CheckIfError` and `Info` used in the examples are from the [examples package](https://github.com/src-d/go-git/blob/master/_examples/common.go#L17) just to be used in the examples.
 
> Please note that the functions `CheckIfError` and `Info` used in the examples are from the [examples package](https://github.com/src-d/go-git/blob/master/examples/common.go#L17) just to be used in the examples.
 
### Basic example
A basic example that mimics the standard `git clone` command
 
```go
// Instances an in-memory git repository
r := git.NewMemoryRepository()
// Clone the given repository to the given directory
Info("git clone https://github.com/src-d/go-git")
_, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{
URL: "https://github.com/src-d/go-git",
Progress: os.Stdout,
})
CheckIfError(err)
```
Outputs:
```
Counting objects: 4924, done.
Compressing objects: 100% (1333/1333), done.
Total 4924 (delta 530), reused 6 (delta 6), pack-reused 3533
```
### In-memory example
Cloning a repository into memory and printing the history of HEAD, just like `git log` does
 
// Clones the given repository, creating the remote, the local branches
// and fetching the objects, exactly as:
```go
// Clones the given repository in memory, creating the remote, the local
// branches and fetching the objects, exactly as:
Info("git clone https://github.com/src-d/go-siva")
 
err := r.Clone(&git.CloneOptions{URL: "https://github.com/src-d/go-siva"})
r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
URL: "https://github.com/src-d/go-siva",
})
CheckIfError(err)
 
// Gets the HEAD history from HEAD, just like does:
Loading
Loading
@@ -83,7 +109,7 @@ Date: Fri Nov 11 13:23:22 2016 +0100
...
```
 
You can find this [example](examples/log/main.go) and many other at the [examples](examples) folder
You can find this [example](_examples/log/main.go) and many other at the [examples](_examples) folder
 
Contribute
----------
Loading
Loading
Loading
Loading
@@ -8,62 +8,57 @@ import (
"strings"
"unicode/utf8"
 
"srcd.works/go-git.v4/plumbing"
"srcd.works/go-git.v4/plumbing/object"
"srcd.works/go-git.v4/utils/diff"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/utils/diff"
)
 
// BlameResult represents the result of a Blame operation.
type BlameResult struct {
Path string
Rev plumbing.Hash
// Path is the path of the File that we're blaming.
Path string
// Rev (Revision) is the hash of the specified Commit used to generate this result.
Rev plumbing.Hash
// Lines contains every line with its authorship.
Lines []*Line
}
 
// 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 assigned 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 which 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 function in general:
//
// 1. Add memoization between revlist and assign.
//
// 2. It is using much more memory than needed, see the TODOs below.
// Blame returns a BlameResult with the information about the last author of
// each line from file `path` at commit `c`.
func Blame(c *object.Commit, path string) (*BlameResult, error) {
// The file to blame is identified by the input arguments:
// commit and path. commit is a Commit object obtained from a Repository. Path
// represents a path to a specific file contained into the repository.
//
// 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 is assigned 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 which 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 function in general:
// 1. Add memoization between revlist and assign.
// 2. It is using much more memory than needed, see the TODOs below.
b := new(blame)
b.fRev = c
b.path = path
Loading
Loading
@@ -88,6 +83,11 @@ func Blame(c *object.Commit, path string) (*BlameResult, error) {
return nil, err
}
 
// 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.
lines, err := newLines(finalLines, b.sliceGraph(len(b.graph)-1))
if err != nil {
return nil, err
Loading
Loading
@@ -102,8 +102,10 @@ func Blame(c *object.Commit, path string) (*BlameResult, error) {
 
// Line values represent the contents and author of a line in BlamedResult values.
type Line struct {
Author string // email address of the author of the line.
Text string // original text of the line.
// Author is the email address of the last author that modified the line.
Author string
// Text is the original text of the line.
Text string
}
 
func newLine(author, text string) *Line {
Loading
Loading
@@ -128,11 +130,16 @@ func newLines(contents []string, commits []*object.Commit) ([]*Line, error) {
// this struct is internally used by the blame function to hold its
// inputs, outputs and state.
type blame struct {
path string // the path of the file to blame
fRev *object.Commit // the commit of the final revision of the file to blame
revs []*object.Commit // the chain of revisions affecting the the file to blame
data []string // the contents of the file across all its revisions
graph [][]*object.Commit // the graph of the lines in the file across all the revisions TODO: not all commits are needed, only the current rev and the prev
// the path of the file to blame
path string
// the commit of the final revision of the file to blame
fRev *object.Commit
// the chain of revisions affecting the the file to blame
revs []*object.Commit
// the contents of the file across all its revisions
data []string
// the graph of the lines in the file across all the revisions
graph [][]*object.Commit
}
 
// calculte the history of a file "path", starting from commit "from", sorted by commit date.
Loading
Loading
@@ -148,6 +155,7 @@ func (b *blame) fillRevs() error {
 
// build graph of a file from its revision history
func (b *blame) fillGraphAndData() error {
//TODO: not all commits are needed, only the current rev and the prev
b.graph = make([][]*object.Commit, len(b.revs))
b.data = make([]string, len(b.revs)) // file contents in all the revisions
// for every revision of the file, starting with the first
Loading
Loading
package git
 
import (
"strings"
import "strings"
 
"srcd.works/go-git.v4/config"
"srcd.works/go-git.v4/plumbing/storer"
)
// Storer is a generic storage of objects, references and any information
// related to a particular repository. Some Storer implementations persist the
// information in a system directory (such as `.git`) and others
// implementations are in memory being ephemeral
type Storer interface {
storer.EncodedObjectStorer
storer.ReferenceStorer
storer.ShallowStorer
storer.IndexStorer
config.ConfigStorer
}
const defaultDotGitPath = ".git"
 
// countLines returns the number of lines in a string à la git, this is
// The newline character is assumed to be '\n'. The empty string
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@ import (
"errors"
"fmt"
 
format "srcd.works/go-git.v4/plumbing/format/config"
format "gopkg.in/src-d/go-git.v4/plumbing/format/config"
)
 
const (
Loading
Loading
@@ -32,14 +32,19 @@ var (
// Config contains the repository configuration
// ftp://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES
type Config struct {
// Core variables
Core struct {
// IsBare if true this repository is assumed to be bare and has no
// working directory associated with it
// working directory associated with it.
IsBare bool
// Worktree is the path to the root of the working tree.
Worktree string
}
// Remote list of repository remotes
// Remotes list of repository remotes, the key of the map is the name
// of the remote, should equal to RemoteConfig.Name.
Remotes map[string]*RemoteConfig
// Submodules list of repository submodules, the key of the map is the name
// of the submodule, should equal to Submodule.Name.
Submodules map[string]*Submodule
 
// contains the raw information of a config file, the main goal is preserve
// the parsed information from the original format, to avoid missing
Loading
Loading
@@ -47,15 +52,16 @@ type Config struct {
raw *format.Config
}
 
// NewConfig returns a new empty Config
// NewConfig returns a new empty Config.
func NewConfig() *Config {
return &Config{
Remotes: make(map[string]*RemoteConfig, 0),
raw: format.New(),
Remotes: make(map[string]*RemoteConfig, 0),
Submodules: make(map[string]*Submodule, 0),
raw: format.New(),
}
}
 
// Validate validates the fields and sets the default values
// Validate validates the fields and sets the default values.
func (c *Config) Validate() error {
for name, r := range c.Remotes {
if r.Name != name {
Loading
Loading
@@ -71,14 +77,16 @@ func (c *Config) Validate() error {
}
 
const (
remoteSection = "remote"
coreSection = "core"
fetchKey = "fetch"
urlKey = "url"
bareKey = "bare"
remoteSection = "remote"
submoduleSection = "submodule"
coreSection = "core"
fetchKey = "fetch"
urlKey = "url"
bareKey = "bare"
worktreeKey = "worktree"
)
 
// Unmarshal parses a git-config file and stores it
// Unmarshal parses a git-config file and stores it.
func (c *Config) Unmarshal(b []byte) error {
r := bytes.NewBuffer(b)
d := format.NewDecoder(r)
Loading
Loading
@@ -89,6 +97,7 @@ func (c *Config) Unmarshal(b []byte) error {
}
 
c.unmarshalCore()
c.unmarshalSubmodules()
return c.unmarshalRemotes()
}
 
Loading
Loading
@@ -97,6 +106,8 @@ func (c *Config) unmarshalCore() {
if s.Options.Get(bareKey) == "true" {
c.Core.IsBare = true
}
c.Core.Worktree = s.Options.Get(worktreeKey)
}
 
func (c *Config) unmarshalRemotes() error {
Loading
Loading
@@ -113,10 +124,21 @@ func (c *Config) unmarshalRemotes() error {
return nil
}
 
// Marshal returns Config encoded as a git-config file
func (c *Config) unmarshalSubmodules() {
s := c.raw.Section(submoduleSection)
for _, sub := range s.Subsections {
m := &Submodule{}
m.unmarshal(sub)
c.Submodules[m.Name] = m
}
}
// Marshal returns Config encoded as a git-config file.
func (c *Config) Marshal() ([]byte, error) {
c.marshalCore()
c.marshalRemotes()
c.marshalSubmodules()
 
buf := bytes.NewBuffer(nil)
if err := format.NewEncoder(buf).Encode(c.raw); err != nil {
Loading
Loading
@@ -129,6 +151,10 @@ func (c *Config) Marshal() ([]byte, error) {
func (c *Config) marshalCore() {
s := c.raw.Section(coreSection)
s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare))
if c.Core.Worktree != "" {
s.SetOption(worktreeKey, c.Core.Worktree)
}
}
 
func (c *Config) marshalRemotes() {
Loading
Loading
@@ -142,7 +168,22 @@ func (c *Config) marshalRemotes() {
}
}
 
// RemoteConfig contains the configuration for a given remote repository
func (c *Config) marshalSubmodules() {
s := c.raw.Section(submoduleSection)
s.Subsections = make(format.Subsections, len(c.Submodules))
var i int
for _, r := range c.Submodules {
section := r.marshal()
// the submodule section at config is a subset of the .gitmodule file
// we should remove the non-valid options for the config file.
section.RemoveOption(pathKey)
s.Subsections[i] = section
i++
}
}
// RemoteConfig contains the configuration for a given remote repository.
type RemoteConfig struct {
// Name of the remote
Name string
Loading
Loading
@@ -156,7 +197,7 @@ type RemoteConfig struct {
raw *format.Subsection
}
 
// Validate validates the fields and sets the default values
// Validate validates the fields and sets the default values.
func (c *RemoteConfig) Validate() error {
if c.Name == "" {
return ErrRemoteConfigEmptyName
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ import (
"bytes"
"errors"
 
format "srcd.works/go-git.v4/plumbing/format/config"
format "gopkg.in/src-d/go-git.v4/plumbing/format/config"
)
 
var (
Loading
Loading
@@ -15,7 +15,7 @@ var (
// Modules defines the submodules properties, represents a .gitmodules file
// https://www.kernel.org/pub/software/scm/git/docs/gitmodules.html
type Modules struct {
// Submodules is a map of submodules being the key the name of the submodule
// Submodules is a map of submodules being the key the name of the submodule.
Submodules map[string]*Submodule
 
raw *format.Config
Loading
Loading
@@ -30,12 +30,11 @@ func NewModules() *Modules {
}
 
const (
submoduleSection = "submodule"
pathKey = "path"
branchKey = "branch"
pathKey = "path"
branchKey = "branch"
)
 
// Unmarshal parses a git-config file and stores it
// Unmarshal parses a git-config file and stores it.
func (m *Modules) Unmarshal(b []byte) error {
r := bytes.NewBuffer(b)
d := format.NewDecoder(r)
Loading
Loading
@@ -56,7 +55,7 @@ func (m *Modules) Unmarshal(b []byte) error {
return nil
}
 
// Marshal returns Modules encoded as a git-config file
// Marshal returns Modules encoded as a git-config file.
func (m *Modules) Marshal() ([]byte, error) {
s := m.raw.Section(submoduleSection)
s.Subsections = make(format.Subsections, len(m.Submodules))
Loading
Loading
@@ -75,12 +74,12 @@ func (m *Modules) Marshal() ([]byte, error) {
return buf.Bytes(), nil
}
 
// Submodule defines a submodule
// Submodule defines a submodule.
type Submodule struct {
// Name module name
Name string
// Path defines the path, relative to the top-level directory of the Git
// working tree,
// working tree.
Path string
// URL defines a URL from which the submodule repository can be cloned.
URL string
Loading
Loading
@@ -89,11 +88,11 @@ type Submodule struct {
Branch string
 
// raw representation of the subsection, filled by marshal or unmarshal are
// called
// called.
raw *format.Subsection
}
 
// Validate validates the fields and sets the default values
// Validate validates the fields and sets the default values.
func (m *Submodule) Validate() error {
if m.Path == "" {
return ErrModuleEmptyPath
Loading
Loading
Loading
Loading
@@ -4,7 +4,7 @@ import (
"errors"
"strings"
 
"srcd.works/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing"
)
 
const (
Loading
Loading
@@ -49,7 +49,7 @@ func (s RefSpec) Validate() error {
return ErrRefSpecMalformedWildcard
}
 
// IsForceUpdate returns if update is allowed in non fast-forward merges
// IsForceUpdate returns if update is allowed in non fast-forward merges.
func (s RefSpec) IsForceUpdate() bool {
if s[0] == refSpecForce[0] {
return true
Loading
Loading
@@ -67,7 +67,7 @@ func (s RefSpec) IsDelete() bool {
return false
}
 
// Src return the src side
// Src return the src side.
func (s RefSpec) Src() string {
spec := string(s)
start := strings.Index(spec, refSpecForce) + 1
Loading
Loading
@@ -76,7 +76,7 @@ func (s RefSpec) Src() string {
return spec[start:end]
}
 
// Match match the given plumbing.ReferenceName against the source
// Match match the given plumbing.ReferenceName against the source.
func (s RefSpec) Match(n plumbing.ReferenceName) bool {
if !s.IsWildcard() {
return s.matchExact(n)
Loading
Loading
@@ -85,7 +85,7 @@ func (s RefSpec) Match(n plumbing.ReferenceName) bool {
return s.matchGlob(n)
}
 
// IsWildcard returns true if the RefSpec contains a wildcard
// IsWildcard returns true if the RefSpec contains a wildcard.
func (s RefSpec) IsWildcard() bool {
return strings.Index(string(s), refSpecWildcard) != -1
}
Loading
Loading
@@ -110,7 +110,7 @@ func (s RefSpec) matchGlob(n plumbing.ReferenceName) bool {
strings.HasSuffix(name, suffix)
}
 
// Dst returns the destination for the given remote reference
// Dst returns the destination for the given remote reference.
func (s RefSpec) Dst(n plumbing.ReferenceName) plumbing.ReferenceName {
spec := string(s)
start := strings.Index(spec, refSpecSeparator) + 1
Loading
Loading
@@ -133,7 +133,7 @@ func (s RefSpec) String() string {
return string(s)
}
 
// MatchAny returns true if any of the RefSpec match with the given ReferenceName
// MatchAny returns true if any of the RefSpec match with the given ReferenceName.
func MatchAny(l []RefSpec, n plumbing.ReferenceName) bool {
for _, r := range l {
if r.Match(n) {
Loading
Loading
// A highly extensible git implementation in pure Go.
//
// go-git aims to reach the completeness of libgit2 or jgit, nowadays covers the
// majority of the plumbing read operations and some of the main write
// operations, but lacks the main porcelain operations such as merges.
//
// It is highly extensible, we have been following the open/close principle in
// its design to facilitate extensions, mainly focusing the efforts on the
// persistence of the objects.
package git // import "gopkg.in/src-d/go-git.v4"
// Package revision extracts git revision from string
// More informations about revision : https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html
package revision
import (
"bytes"
"fmt"
"io"
"regexp"
"strconv"
"time"
)
// ErrInvalidRevision is emitted if string doesn't match valid revision
type ErrInvalidRevision struct {
s string
}
func (e *ErrInvalidRevision) Error() string {
return "Revision invalid : " + e.s
}
// Revisioner represents a revision component.
// A revision is made of multiple revision components
// obtained after parsing a revision string,
// for instance revision "master~" will be converted in
// two revision components Ref and TildePath
type Revisioner interface {
}
// Ref represents a reference name : HEAD, master
type Ref string
// TildePath represents ~, ~{n}
type TildePath struct {
Depth int
}
// CaretPath represents ^, ^{n}
type CaretPath struct {
Depth int
}
// CaretReg represents ^{/foo bar}
type CaretReg struct {
Regexp *regexp.Regexp
Negate bool
}
// CaretType represents ^{commit}
type CaretType struct {
ObjectType string
}
// AtReflog represents @{n}
type AtReflog struct {
Depth int
}
// AtCheckout represents @{-n}
type AtCheckout struct {
Depth int
}
// AtUpstream represents @{upstream}, @{u}
type AtUpstream struct {
BranchName string
}
// AtPush represents @{push}
type AtPush struct {
BranchName string
}
// AtDate represents @{"2006-01-02T15:04:05Z"}
type AtDate struct {
Date time.Time
}
// ColonReg represents :/foo bar
type ColonReg struct {
Regexp *regexp.Regexp
Negate bool
}
// ColonPath represents :./<path> :<path>
type ColonPath struct {
Path string
}
// ColonStagePath represents :<n>:/<path>
type ColonStagePath struct {
Path string
Stage int
}
// Parser represents a parser
// use to tokenize and transform to revisioner chunks
// a given string
type Parser struct {
s *scanner
currentParsedChar struct {
tok token
lit string
}
unreadLastChar bool
}
// NewParserFromString returns a new instance of parser from a string.
func NewParserFromString(s string) *Parser {
return NewParser(bytes.NewBufferString(s))
}
// NewParser returns a new instance of parser.
func NewParser(r io.Reader) *Parser {
return &Parser{s: newScanner(r)}
}
// scan returns the next token from the underlying scanner
// or the last scanned token if an unscan was requested
func (p *Parser) scan() (token, string, error) {
if p.unreadLastChar {
p.unreadLastChar = false
return p.currentParsedChar.tok, p.currentParsedChar.lit, nil
}
tok, lit, err := p.s.scan()
p.currentParsedChar.tok, p.currentParsedChar.lit = tok, lit
return tok, lit, err
}
// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.unreadLastChar = true }
// Parse explode a revision string into revisioner chunks
func (p *Parser) Parse() ([]Revisioner, error) {
var rev Revisioner
var revs []Revisioner
var tok token
var err error
for {
tok, _, err = p.scan()
if err != nil {
return nil, err
}
switch tok {
case at:
rev, err = p.parseAt()
case tilde:
rev, err = p.parseTilde()
case caret:
rev, err = p.parseCaret()
case colon:
rev, err = p.parseColon()
case eof:
err = p.validateFullRevision(&revs)
if err != nil {
return []Revisioner{}, err
}
return revs, nil
default:
p.unscan()
rev, err = p.parseRef()
}
if err != nil {
return []Revisioner{}, err
}
revs = append(revs, rev)
}
}
// validateFullRevision ensures all revisioner chunks make a valid revision
func (p *Parser) validateFullRevision(chunks *[]Revisioner) error {
var hasReference bool
for i, chunk := range *chunks {
switch chunk.(type) {
case Ref:
if i == 0 {
hasReference = true
} else {
return &ErrInvalidRevision{`reference must be defined once at the beginning`}
}
case AtDate:
if len(*chunks) == 1 || hasReference && len(*chunks) == 2 {
return nil
}
return &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{<ISO-8601 date>}, @{<ISO-8601 date>}`}
case AtReflog:
if len(*chunks) == 1 || hasReference && len(*chunks) == 2 {
return nil
}
return &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{<n>}, @{<n>}`}
case AtCheckout:
if len(*chunks) == 1 {
return nil
}
return &ErrInvalidRevision{`"@" statement is not valid, could be : @{-<n>}`}
case AtUpstream:
if len(*chunks) == 1 || hasReference && len(*chunks) == 2 {
return nil
}
return &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{upstream}, @{upstream}, <refname>@{u}, @{u}`}
case AtPush:
if len(*chunks) == 1 || hasReference && len(*chunks) == 2 {
return nil
}
return &ErrInvalidRevision{`"@" statement is not valid, could be : <refname>@{push}, @{push}`}
case TildePath, CaretPath, CaretReg:
if !hasReference {
return &ErrInvalidRevision{`"~" or "^" statement must have a reference defined at the beginning`}
}
case ColonReg:
if len(*chunks) == 1 {
return nil
}
return &ErrInvalidRevision{`":" statement is not valid, could be : :/<regexp>`}
case ColonPath:
if i == len(*chunks)-1 && hasReference || len(*chunks) == 1 {
return nil
}
return &ErrInvalidRevision{`":" statement is not valid, could be : <revision>:<path>`}
case ColonStagePath:
if len(*chunks) == 1 {
return nil
}
return &ErrInvalidRevision{`":" statement is not valid, could be : :<n>:<path>`}
}
}
return nil
}
// parseAt extract @ statements
func (p *Parser) parseAt() (Revisioner, error) {
var tok, nextTok token
var lit, nextLit string
var err error
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
if tok != obrace {
p.unscan()
return Ref("HEAD"), nil
}
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
nextTok, nextLit, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == word && (lit == "u" || lit == "upstream") && nextTok == cbrace:
return AtUpstream{}, nil
case tok == word && lit == "push" && nextTok == cbrace:
return AtPush{}, nil
case tok == number && nextTok == cbrace:
n, _ := strconv.Atoi(lit)
return AtReflog{n}, nil
case tok == minus && nextTok == number:
n, _ := strconv.Atoi(nextLit)
t, _, err := p.scan()
if err != nil {
return nil, err
}
if t != cbrace {
return nil, &ErrInvalidRevision{fmt.Sprintf(`missing "}" in @{-n} structure`)}
}
return AtCheckout{n}, nil
default:
p.unscan()
date := lit
for {
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == cbrace:
t, err := time.Parse("2006-01-02T15:04:05Z", date)
if err != nil {
return nil, &ErrInvalidRevision{fmt.Sprintf(`wrong date "%s" must fit ISO-8601 format : 2006-01-02T15:04:05Z`, date)}
}
return AtDate{t}, nil
default:
date += lit
}
}
}
}
// parseTilde extract ~ statements
func (p *Parser) parseTilde() (Revisioner, error) {
var tok token
var lit string
var err error
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == number:
n, _ := strconv.Atoi(lit)
return TildePath{n}, nil
default:
p.unscan()
return TildePath{1}, nil
}
}
// parseCaret extract ^ statements
func (p *Parser) parseCaret() (Revisioner, error) {
var tok token
var lit string
var err error
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == obrace:
r, err := p.parseCaretBraces()
if err != nil {
return nil, err
}
return r, nil
case tok == number:
n, _ := strconv.Atoi(lit)
if n > 2 {
return nil, &ErrInvalidRevision{fmt.Sprintf(`"%s" found must be 0, 1 or 2 after "^"`, lit)}
}
return CaretPath{n}, nil
default:
p.unscan()
return CaretPath{1}, nil
}
}
// parseCaretBraces extract ^{<data>} statements
func (p *Parser) parseCaretBraces() (Revisioner, error) {
var tok, nextTok token
var lit, _ string
start := true
var re string
var negate bool
var err error
for {
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
nextTok, _, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == word && nextTok == cbrace && (lit == "commit" || lit == "tree" || lit == "blob" || lit == "tag" || lit == "object"):
return CaretType{lit}, nil
case re == "" && tok == cbrace:
return CaretType{"tag"}, nil
case re == "" && tok == emark && nextTok == emark:
re += lit
case re == "" && tok == emark && nextTok == minus:
negate = true
case re == "" && tok == emark:
return nil, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component sequences starting with "/!" others than those defined are reserved`)}
case re == "" && tok == slash:
p.unscan()
case tok != slash && start:
return nil, &ErrInvalidRevision{fmt.Sprintf(`"%s" is not a valid revision suffix brace component`, lit)}
case tok != cbrace:
p.unscan()
re += lit
case tok == cbrace:
p.unscan()
reg, err := regexp.Compile(re)
if err != nil {
return CaretReg{}, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component, %s`, err.Error())}
}
return CaretReg{reg, negate}, nil
}
start = false
}
}
// parseColon extract : statements
func (p *Parser) parseColon() (Revisioner, error) {
var tok token
var err error
tok, _, err = p.scan()
if err != nil {
return nil, err
}
switch tok {
case slash:
return p.parseColonSlash()
default:
p.unscan()
return p.parseColonDefault()
}
}
// parseColonSlash extract :/<data> statements
func (p *Parser) parseColonSlash() (Revisioner, error) {
var tok, nextTok token
var lit string
var re string
var negate bool
var err error
for {
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
nextTok, _, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == emark && nextTok == emark:
re += lit
case re == "" && tok == emark && nextTok == minus:
negate = true
case re == "" && tok == emark:
return nil, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component sequences starting with "/!" others than those defined are reserved`)}
case tok == eof:
p.unscan()
reg, err := regexp.Compile(re)
if err != nil {
return ColonReg{}, &ErrInvalidRevision{fmt.Sprintf(`revision suffix brace component, %s`, err.Error())}
}
return ColonReg{reg, negate}, nil
default:
p.unscan()
re += lit
}
}
}
// parseColonDefault extract :<data> statements
func (p *Parser) parseColonDefault() (Revisioner, error) {
var tok token
var lit string
var path string
var stage int
var err error
var n = -1
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
nextTok, _, err := p.scan()
if err != nil {
return nil, err
}
if tok == number && nextTok == colon {
n, _ = strconv.Atoi(lit)
}
switch n {
case 0, 1, 2, 3:
stage = n
default:
path += lit
p.unscan()
}
for {
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
switch {
case tok == eof && n == -1:
return ColonPath{path}, nil
case tok == eof:
return ColonStagePath{path, stage}, nil
default:
path += lit
}
}
}
// parseRef extract reference name
func (p *Parser) parseRef() (Revisioner, error) {
var tok, prevTok token
var lit, buf string
var endOfRef bool
var err error
for {
tok, lit, err = p.scan()
if err != nil {
return nil, err
}
switch tok {
case eof, at, colon, tilde, caret:
endOfRef = true
}
err := p.checkRefFormat(tok, lit, prevTok, buf, endOfRef)
if err != nil {
return "", err
}
if endOfRef {
p.unscan()
return Ref(buf), nil
}
buf += lit
prevTok = tok
}
}
// checkRefFormat ensure reference name follow rules defined here :
// https://git-scm.com/docs/git-check-ref-format
func (p *Parser) checkRefFormat(token token, literal string, previousToken token, buffer string, endOfRef bool) error {
switch token {
case aslash, space, control, qmark, asterisk, obracket:
return &ErrInvalidRevision{fmt.Sprintf(`must not contains "%s"`, literal)}
}
switch {
case (token == dot || token == slash) && buffer == "":
return &ErrInvalidRevision{fmt.Sprintf(`must not start with "%s"`, literal)}
case previousToken == slash && endOfRef:
return &ErrInvalidRevision{`must not end with "/"`}
case previousToken == dot && endOfRef:
return &ErrInvalidRevision{`must not end with "."`}
case token == dot && previousToken == slash:
return &ErrInvalidRevision{`must not contains "/."`}
case previousToken == dot && token == dot:
return &ErrInvalidRevision{`must not contains ".."`}
case previousToken == slash && token == slash:
return &ErrInvalidRevision{`must not contains consecutively "/"`}
case (token == slash || endOfRef) && len(buffer) > 4 && buffer[len(buffer)-5:] == ".lock":
return &ErrInvalidRevision{"cannot end with .lock"}
}
return nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment