| @@ -130,7 +130,7 @@ require ( | |||||
| gopkg.in/macaron.v1 v1.3.2 | gopkg.in/macaron.v1 v1.3.2 | ||||
| gopkg.in/redis.v2 v2.3.2 // indirect | gopkg.in/redis.v2 v2.3.2 // indirect | ||||
| gopkg.in/src-d/go-billy.v4 v4.3.0 | gopkg.in/src-d/go-billy.v4 v4.3.0 | ||||
| gopkg.in/src-d/go-git.v4 v4.10.0 | |||||
| gopkg.in/src-d/go-git.v4 v4.11.0 | |||||
| gopkg.in/testfixtures.v2 v2.5.0 | gopkg.in/testfixtures.v2 v2.5.0 | ||||
| mvdan.cc/xurls/v2 v2.0.0 | mvdan.cc/xurls/v2 v2.0.0 | ||||
| strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a | strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a | ||||
| @@ -380,8 +380,8 @@ gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek | |||||
| gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= | gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= | ||||
| gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs= | gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs= | ||||
| gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= | gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= | ||||
| gopkg.in/src-d/go-git.v4 v4.10.0 h1:NWjTJTQnk8UpIGlssuefyDZ6JruEjo5s88vm88uASbw= | |||||
| gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= | |||||
| gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU= | |||||
| gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= | |||||
| gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M= | gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M= | ||||
| gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU= | gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU= | ||||
| gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw= | gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw= | ||||
| @@ -1,8 +1,8 @@ | |||||
| language: go | language: go | ||||
| go: | go: | ||||
| - "1.10" | |||||
| - "1.11" | - "1.11" | ||||
| - "1.12" | |||||
| go_import_path: gopkg.in/src-d/go-git.v4 | go_import_path: gopkg.in/src-d/go-git.v4 | ||||
| @@ -229,7 +229,7 @@ var ( | |||||
| ErrCreateRequiresBranch = errors.New("Branch is mandatory when Create is used") | ErrCreateRequiresBranch = errors.New("Branch is mandatory when Create is used") | ||||
| ) | ) | ||||
| // CheckoutOptions describes how a checkout 31operation should be performed. | |||||
| // CheckoutOptions describes how a checkout operation should be performed. | |||||
| type CheckoutOptions struct { | type CheckoutOptions struct { | ||||
| // Hash is the hash of the commit to be checked out. If used, HEAD will be | // Hash is the hash of the commit to be checked out. If used, HEAD will be | ||||
| // in detached mode. If Create is not used, Branch and Hash are mutually | // in detached mode. If Create is not used, Branch and Hash are mutually | ||||
| @@ -288,7 +288,7 @@ const ( | |||||
| // ResetOptions describes how a reset operation should be performed. | // ResetOptions describes how a reset operation should be performed. | ||||
| type ResetOptions struct { | type ResetOptions struct { | ||||
| // Commit, if commit is pressent set the current branch head (HEAD) to it. | |||||
| // Commit, if commit is present set the current branch head (HEAD) to it. | |||||
| Commit plumbing.Hash | Commit plumbing.Hash | ||||
| // Mode, form resets the current branch head to Commit and possibly updates | // Mode, form resets the current branch head to Commit and possibly updates | ||||
| // the index (resetting it to the tree of Commit) and the working tree | // the index (resetting it to the tree of Commit) and the working tree | ||||
| @@ -61,6 +61,11 @@ func (c *ObjectLRU) Put(obj plumbing.EncodedObject) { | |||||
| c.actualSize += objSize | c.actualSize += objSize | ||||
| for c.actualSize > c.MaxSize { | for c.actualSize > c.MaxSize { | ||||
| last := c.ll.Back() | last := c.ll.Back() | ||||
| if last == nil { | |||||
| c.actualSize = 0 | |||||
| break | |||||
| } | |||||
| lastObj := last.Value.(plumbing.EncodedObject) | lastObj := last.Value.(plumbing.EncodedObject) | ||||
| lastSize := FileSize(lastObj.Size()) | lastSize := FileSize(lastObj.Size()) | ||||
| @@ -76,8 +76,8 @@ func (c *Commit) Tree() (*Tree, error) { | |||||
| return GetTree(c.s, c.TreeHash) | return GetTree(c.s, c.TreeHash) | ||||
| } | } | ||||
| // Patch returns the Patch between the actual commit and the provided one. | |||||
| // Error will be return if context expires. Provided context must be non-nil | |||||
| // PatchContext returns the Patch between the actual commit and the provided one. | |||||
| // Error will be return if context expires. Provided context must be non-nil. | |||||
| func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) { | func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) { | ||||
| fromTree, err := c.Tree() | fromTree, err := c.Tree() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -291,25 +291,33 @@ func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) { | |||||
| return err | return err | ||||
| } | } | ||||
| // Stats shows the status of commit. | |||||
| // Stats returns the stats of a commit. | |||||
| func (c *Commit) Stats() (FileStats, error) { | func (c *Commit) Stats() (FileStats, error) { | ||||
| // Get the previous commit. | |||||
| ci := c.Parents() | |||||
| parentCommit, err := ci.Next() | |||||
| return c.StatsContext(context.Background()) | |||||
| } | |||||
| // StatsContext returns the stats of a commit. Error will be return if context | |||||
| // expires. Provided context must be non-nil. | |||||
| func (c *Commit) StatsContext(ctx context.Context) (FileStats, error) { | |||||
| fromTree, err := c.Tree() | |||||
| if err != nil { | if err != nil { | ||||
| if err == io.EOF { | |||||
| emptyNoder := treeNoder{} | |||||
| parentCommit = &Commit{ | |||||
| Hash: emptyNoder.hash, | |||||
| // TreeHash: emptyNoder.parent.Hash, | |||||
| s: c.s, | |||||
| } | |||||
| } else { | |||||
| return nil, err | |||||
| } | |||||
| toTree := &Tree{} | |||||
| if c.NumParents() != 0 { | |||||
| firstParent, err := c.Parents().Next() | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| toTree, err = firstParent.Tree() | |||||
| if err != nil { | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| } | } | ||||
| patch, err := parentCommit.Patch(c) | |||||
| patch, err := toTree.PatchContext(ctx, fromTree) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| @@ -67,7 +67,7 @@ func (w *bfsCommitIterator) Next() (*Commit, error) { | |||||
| for _, h := range c.ParentHashes { | for _, h := range c.ParentHashes { | ||||
| err := w.appendHash(c.s, h) | err := w.appendHash(c.s, h) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, nil | |||||
| return nil, err | |||||
| } | } | ||||
| } | } | ||||
| @@ -320,11 +320,18 @@ func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats { | |||||
| } | } | ||||
| for _, chunk := range fp.Chunks() { | for _, chunk := range fp.Chunks() { | ||||
| s := chunk.Content() | |||||
| switch chunk.Type() { | switch chunk.Type() { | ||||
| case fdiff.Add: | case fdiff.Add: | ||||
| cs.Addition += strings.Count(chunk.Content(), "\n") | |||||
| cs.Addition += strings.Count(s, "\n") | |||||
| if s[len(s)-1] != '\n' { | |||||
| cs.Addition++ | |||||
| } | |||||
| case fdiff.Delete: | case fdiff.Delete: | ||||
| cs.Deletion += strings.Count(chunk.Content(), "\n") | |||||
| cs.Deletion += strings.Count(s, "\n") | |||||
| if s[len(s)-1] != '\n' { | |||||
| cs.Deletion++ | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -135,7 +135,7 @@ func (t *Tree) FindEntry(path string) (*TreeEntry, error) { | |||||
| pathCurrent := "" | pathCurrent := "" | ||||
| // search for the longest path in the tree path cache | // search for the longest path in the tree path cache | ||||
| for i := len(pathParts); i > 1; i-- { | |||||
| for i := len(pathParts) - 1; i > 1; i-- { | |||||
| path := filepath.Join(pathParts[:i]...) | path := filepath.Join(pathParts[:i]...) | ||||
| tree, ok := t.t[path] | tree, ok := t.t[path] | ||||
| @@ -1020,7 +1020,12 @@ func pushHashes( | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| done := make(chan error) | |||||
| // Set buffer size to 1 so the error message can be written when | |||||
| // ReceivePack fails. Otherwise the goroutine will be blocked writing | |||||
| // to the channel. | |||||
| done := make(chan error, 1) | |||||
| go func() { | go func() { | ||||
| e := packfile.NewEncoder(wr, s, useRefDeltas) | e := packfile.NewEncoder(wr, s, useRefDeltas) | ||||
| if _, err := e.Encode(hs, config.Pack.Window); err != nil { | if _, err := e.Encode(hs, config.Pack.Window); err != nil { | ||||
| @@ -1033,6 +1038,8 @@ func pushHashes( | |||||
| rs, err := sess.ReceivePack(ctx, req) | rs, err := sess.ReceivePack(ctx, req) | ||||
| if err != nil { | if err != nil { | ||||
| // close the pipe to unlock encode write | |||||
| _ = rd.Close() | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| @@ -49,6 +49,7 @@ var ( | |||||
| ErrRepositoryAlreadyExists = errors.New("repository already exists") | ErrRepositoryAlreadyExists = errors.New("repository already exists") | ||||
| ErrRemoteNotFound = errors.New("remote not found") | ErrRemoteNotFound = errors.New("remote not found") | ||||
| ErrRemoteExists = errors.New("remote already exists") | ErrRemoteExists = errors.New("remote already exists") | ||||
| ErrAnonymousRemoteName = errors.New("anonymous remote name must be 'anonymous'") | |||||
| ErrWorktreeNotProvided = errors.New("worktree should be provided") | ErrWorktreeNotProvided = errors.New("worktree should be provided") | ||||
| ErrIsBareRepository = errors.New("worktree not available in a bare repository") | ErrIsBareRepository = errors.New("worktree not available in a bare repository") | ||||
| ErrUnableToResolveCommit = errors.New("unable to resolve commit") | ErrUnableToResolveCommit = errors.New("unable to resolve commit") | ||||
| @@ -492,6 +493,22 @@ func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) { | |||||
| return remote, r.Storer.SetConfig(cfg) | return remote, r.Storer.SetConfig(cfg) | ||||
| } | } | ||||
| // CreateRemoteAnonymous creates a new anonymous remote. c.Name must be "anonymous". | |||||
| // It's used like 'git fetch git@github.com:src-d/go-git.git master:master'. | |||||
| func (r *Repository) CreateRemoteAnonymous(c *config.RemoteConfig) (*Remote, error) { | |||||
| if err := c.Validate(); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| if c.Name != "anonymous" { | |||||
| return nil, ErrAnonymousRemoteName | |||||
| } | |||||
| remote := newRemote(r.Storer, c) | |||||
| return remote, nil | |||||
| } | |||||
| // DeleteRemote delete a remote from the repository and delete the config | // DeleteRemote delete a remote from the repository and delete the config | ||||
| func (r *Repository) DeleteRemote(name string) error { | func (r *Repository) DeleteRemote(name string) error { | ||||
| cfg, err := r.Storer.Config() | cfg, err := r.Storer.Config() | ||||
| @@ -8,14 +8,30 @@ package diff | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "time" | |||||
| "github.com/sergi/go-diff/diffmatchpatch" | "github.com/sergi/go-diff/diffmatchpatch" | ||||
| ) | ) | ||||
| // Do computes the (line oriented) modifications needed to turn the src | // Do computes the (line oriented) modifications needed to turn the src | ||||
| // string into the dst string. | |||||
| // string into the dst string. The underlying algorithm is Meyers, | |||||
| // its complexity is O(N*d) where N is min(lines(src), lines(dst)) and d | |||||
| // is the size of the diff. | |||||
| func Do(src, dst string) (diffs []diffmatchpatch.Diff) { | func Do(src, dst string) (diffs []diffmatchpatch.Diff) { | ||||
| // the default timeout is time.Second which may be too small under heavy load | |||||
| return DoWithTimeout(src, dst, time.Hour) | |||||
| } | |||||
| // DoWithTimeout computes the (line oriented) modifications needed to turn the src | |||||
| // string into the dst string. The `timeout` argument specifies the maximum | |||||
| // amount of time it is allowed to spend in this function. If the timeout | |||||
| // is exceeded, the parts of the strings which were not considered are turned into | |||||
| // a bulk delete+insert and the half-baked suboptimal result is returned at once. | |||||
| // The underlying algorithm is Meyers, its complexity is O(N*d) where N is | |||||
| // min(lines(src), lines(dst)) and d is the size of the diff. | |||||
| func DoWithTimeout (src, dst string, timeout time.Duration) (diffs []diffmatchpatch.Diff) { | |||||
| dmp := diffmatchpatch.New() | dmp := diffmatchpatch.New() | ||||
| dmp.DiffTimeout = timeout | |||||
| wSrc, wDst, warray := dmp.DiffLinesToRunes(src, dst) | wSrc, wDst, warray := dmp.DiffLinesToRunes(src, dst) | ||||
| diffs = dmp.DiffMainRunes(wSrc, wDst, false) | diffs = dmp.DiffMainRunes(wSrc, wDst, false) | ||||
| diffs = dmp.DiffCharsToLines(diffs, warray) | diffs = dmp.DiffCharsToLines(diffs, warray) | ||||
| @@ -152,17 +152,6 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error { | |||||
| } | } | ||||
| } | } | ||||
| if !opts.Force { | |||||
| unstaged, err := w.containsUnstagedChanges() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if unstaged { | |||||
| return ErrUnstagedChanges | |||||
| } | |||||
| } | |||||
| c, err := w.getCommitFromCheckoutOptions(opts) | c, err := w.getCommitFromCheckoutOptions(opts) | ||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| @@ -142,12 +142,16 @@ func (w *Worktree) diffStagingWithWorktree(reverse bool) (merkletrie.Changes, er | |||||
| func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { | func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { | ||||
| patterns, err := gitignore.ReadPatterns(w.Filesystem, nil) | patterns, err := gitignore.ReadPatterns(w.Filesystem, nil) | ||||
| if err != nil || len(patterns) == 0 { | |||||
| if err != nil { | |||||
| return changes | return changes | ||||
| } | } | ||||
| patterns = append(patterns, w.Excludes...) | patterns = append(patterns, w.Excludes...) | ||||
| if len(patterns) == 0 { | |||||
| return changes | |||||
| } | |||||
| m := gitignore.NewMatcher(patterns) | m := gitignore.NewMatcher(patterns) | ||||
| var res merkletrie.Changes | var res merkletrie.Changes | ||||
| @@ -422,7 +422,7 @@ gopkg.in/src-d/go-billy.v4 | |||||
| gopkg.in/src-d/go-billy.v4/util | gopkg.in/src-d/go-billy.v4/util | ||||
| gopkg.in/src-d/go-billy.v4/helper/chroot | gopkg.in/src-d/go-billy.v4/helper/chroot | ||||
| gopkg.in/src-d/go-billy.v4/helper/polyfill | gopkg.in/src-d/go-billy.v4/helper/polyfill | ||||
| # gopkg.in/src-d/go-git.v4 v4.10.0 | |||||
| # gopkg.in/src-d/go-git.v4 v4.11.0 | |||||
| gopkg.in/src-d/go-git.v4 | gopkg.in/src-d/go-git.v4 | ||||
| gopkg.in/src-d/go-git.v4/config | gopkg.in/src-d/go-git.v4/config | ||||
| gopkg.in/src-d/go-git.v4/plumbing | gopkg.in/src-d/go-git.v4/plumbing | ||||