diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-07-26 14:41:59 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-26 14:41:59 +0200 |
commit | e19163e22eb19b352dd022f6edc9d81e1cd7a7ed (patch) | |
tree | ca08a52fdb31bd90637cec71351578436f254b4b | |
parent | d7b898ea3be0342f064ad63f93aaffdb6518a5b3 (diff) | |
parent | 064051f972e90dd55e6c941f04b58b4a36dfedf1 (diff) | |
download | go-git-e19163e22eb19b352dd022f6edc9d81e1cd7a7ed.tar.gz |
Merge pull request #509 from mcuadros/ctx-main
*: package context support in Repository, Remote and Submodule
-rw-r--r-- | remote.go | 55 | ||||
-rw-r--r-- | remote_test.go | 41 | ||||
-rw-r--r-- | repository.go | 83 | ||||
-rw-r--r-- | repository_test.go | 117 | ||||
-rw-r--r-- | submodule.go | 37 | ||||
-rw-r--r-- | submodule_test.go | 12 | ||||
-rw-r--r-- | worktree.go | 18 | ||||
-rw-r--r-- | worktree_test.go | 3 |
8 files changed, 300 insertions, 66 deletions
@@ -52,6 +52,16 @@ func (r *Remote) String() string { // Push performs a push to the remote. Returns NoErrAlreadyUpToDate if the // remote was already up-to-date. func (r *Remote) Push(o *PushOptions) error { + return r.PushContext(context.Background(), o) +} + +// PushContext performs a push to the remote. Returns NoErrAlreadyUpToDate if +// the remote was already up-to-date. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (r *Remote) PushContext(ctx context.Context, o *PushOptions) error { // TODO: Sideband support if err := o.Validate(); err != nil { return err @@ -124,7 +134,7 @@ func (r *Remote) Push(o *PushOptions) error { return err } - rs, err := pushHashes(s, r.s, req, hashesToPush) + rs, err := pushHashes(ctx, s, r.s, req, hashesToPush) if err != nil { return err } @@ -165,14 +175,30 @@ func (r *Remote) updateRemoteReferenceStorage( return nil } -// Fetch fetches references from the remote to the local repository. -// no changes to be fetched and no local references to update, or an error. -func (r *Remote) Fetch(o *FetchOptions) error { - _, err := r.fetch(o) +// FetchContext fetches references along with the objects necessary to complete +// their histories. +// +// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are +// no changes to be fetched, or an error. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (r *Remote) FetchContext(ctx context.Context, o *FetchOptions) error { + _, err := r.fetch(ctx, o) return err } -func (r *Remote) fetch(o *FetchOptions) (storer.ReferenceStorer, error) { +// Fetch fetches references along with the objects necessary to complete their +// histories. +// +// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are +// no changes to be fetched, or an error. +func (r *Remote) Fetch(o *FetchOptions) error { + return r.FetchContext(context.Background(), o) +} + +func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (storer.ReferenceStorer, error) { if o.RemoteName == "" { o.RemoteName = r.c.Name } @@ -219,7 +245,7 @@ func (r *Remote) fetch(o *FetchOptions) (storer.ReferenceStorer, error) { return nil, err } - if err := r.fetchPack(o, s, req); err != nil { + if err := r.fetchPack(ctx, o, s, req); err != nil { return nil, err } } @@ -268,10 +294,10 @@ func newClient(url string) (transport.Transport, transport.Endpoint, error) { return c, ep, err } -func (r *Remote) fetchPack(o *FetchOptions, s transport.UploadPackSession, +func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.UploadPackSession, req *packp.UploadPackRequest) (err error) { - reader, err := s.UploadPack(context.TODO(), req) + reader, err := s.UploadPack(ctx, req) if err != nil { return err } @@ -698,8 +724,13 @@ func referencesToHashes(refs storer.ReferenceStorer) ([]plumbing.Hash, error) { return hs, nil } -func pushHashes(sess transport.ReceivePackSession, sto storer.EncodedObjectStorer, - req *packp.ReferenceUpdateRequest, hs []plumbing.Hash) (*packp.ReportStatus, error) { +func pushHashes( + ctx context.Context, + sess transport.ReceivePackSession, + sto storer.EncodedObjectStorer, + req *packp.ReferenceUpdateRequest, + hs []plumbing.Hash, +) (*packp.ReportStatus, error) { rd, wr := io.Pipe() req.Packfile = rd @@ -714,7 +745,7 @@ func pushHashes(sess transport.ReceivePackSession, sto storer.EncodedObjectStore done <- wr.Close() }() - rs, err := sess.ReceivePack(context.TODO(), req) + rs, err := sess.ReceivePack(ctx, req) if err != nil { return nil, err } diff --git a/remote_test.go b/remote_test.go index 43276c2..ece052a 100644 --- a/remote_test.go +++ b/remote_test.go @@ -2,6 +2,7 @@ package git import ( "bytes" + "context" "io" "io/ioutil" "os" @@ -98,6 +99,23 @@ func (s *RemoteSuite) TestFetch(c *C) { }) } +func (s *RemoteSuite) TestFetchContext(c *C) { + r := newRemote(memory.NewStorage(), &config.RemoteConfig{ + URL: s.GetLocalRepositoryURL(fixtures.ByTag("tags").One()), + }) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + err := r.FetchContext(ctx, &FetchOptions{ + RefSpecs: []config.RefSpec{ + config.RefSpec("+refs/heads/master:refs/remotes/origin/master"), + }, + }) + c.Assert(err, NotNil) + +} + func (s *RemoteSuite) TestFetchWithAllTags(c *C) { r := newRemote(memory.NewStorage(), &config.RemoteConfig{ URL: s.GetLocalRepositoryURL(fixtures.ByTag("tags").One()), @@ -340,6 +358,29 @@ func (s *RemoteSuite) TestPushToEmptyRepository(c *C) { } +func (s *RemoteSuite) TestPushContext(c *C) { + url := c.MkDir() + _, err := PlainInit(url, true) + c.Assert(err, IsNil) + + fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit() + sto, err := filesystem.NewStorage(fs) + c.Assert(err, IsNil) + + r := newRemote(sto, &config.RemoteConfig{ + Name: DefaultRemoteName, + URL: url, + }) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + err = r.PushContext(ctx, &PushOptions{ + RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"}, + }) + c.Assert(err, NotNil) +} + func (s *RemoteSuite) TestPushTags(c *C) { url := c.MkDir() server, err := PlainInit(url, true) diff --git a/repository.go b/repository.go index 014b92d..b80f8d9 100644 --- a/repository.go +++ b/repository.go @@ -1,6 +1,7 @@ package git import ( + "context" "errors" "fmt" stdioutil "io/ioutil" @@ -168,19 +169,36 @@ func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error) { // Clone a repository into the given Storer and worktree Filesystem with the // given options, if worktree is nil a bare repository is created. If the given -// storer is not empty ErrRepositoryAlreadyExists is returned +// storer is not empty ErrRepositoryAlreadyExists is returned. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. func Clone(s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) { + return CloneContext(context.Background(), s, worktree, o) +} + +// CloneContext a repository into the given Storer and worktree Filesystem with +// the given options, if worktree is nil a bare repository is created. If the +// given storer is not empty ErrRepositoryAlreadyExists is returned. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func CloneContext( + ctx context.Context, s storage.Storer, worktree billy.Filesystem, o *CloneOptions, +) (*Repository, error) { r, err := Init(s, worktree) if err != nil { return nil, err } - return r, r.clone(o) + return r, r.clone(ctx, o) } // PlainInit create an empty git repository at the given path. isBare defines // if the repository will have worktree (non-bare) or not (bare), if the path -// is not empty ErrRepositoryAlreadyExists is returned +// is not empty ErrRepositoryAlreadyExists is returned. func PlainInit(path string, isBare bool) (*Repository, error) { var wt, dot billy.Filesystem @@ -279,14 +297,25 @@ func dotGitFileToOSFilesystem(path string, fs billy.Filesystem) (billy.Filesyste // PlainClone a repository into the path with the given options, isBare defines // if the new repository will be bare or normal. If the path is not empty -// ErrRepositoryAlreadyExists is returned +// ErrRepositoryAlreadyExists is returned. func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) { + return PlainCloneContext(context.Background(), path, isBare, o) +} + +// PlainCloneContext a repository into the path with the given options, isBare +// defines if the new repository will be bare or normal. If the path is not empty +// ErrRepositoryAlreadyExists is returned. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func PlainCloneContext(ctx context.Context, path string, isBare bool, o *CloneOptions) (*Repository, error) { r, err := PlainInit(path, isBare) if err != nil { return nil, err } - return r, r.clone(o) + return r, r.clone(ctx, o) } func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository { @@ -372,7 +401,7 @@ func (r *Repository) DeleteRemote(name string) error { } // Clone clones a remote repository -func (r *Repository) clone(o *CloneOptions) error { +func (r *Repository) clone(ctx context.Context, o *CloneOptions) error { if err := o.Validate(); err != nil { return err } @@ -386,7 +415,7 @@ func (r *Repository) clone(o *CloneOptions) error { return err } - head, err := r.fetchAndUpdateReferences(&FetchOptions{ + head, err := r.fetchAndUpdateReferences(ctx, &FetchOptions{ RefSpecs: r.cloneRefSpec(o, c), Depth: o.Depth, Auth: o.Auth, @@ -469,7 +498,7 @@ func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.Remot } func (r *Repository) fetchAndUpdateReferences( - o *FetchOptions, ref plumbing.ReferenceName, + ctx context.Context, o *FetchOptions, ref plumbing.ReferenceName, ) (*plumbing.Reference, error) { if err := o.Validate(); err != nil { @@ -482,7 +511,7 @@ func (r *Repository) fetchAndUpdateReferences( } objsUpdated := true - remoteRefs, err := remote.fetch(o) + remoteRefs, err := remote.fetch(ctx, o) if err == NoErrAlreadyUpToDate { objsUpdated = false } else if err != nil { @@ -581,10 +610,25 @@ func updateReferenceStorerIfNeeded( return false, nil } -// Fetch fetches changes from a remote repository. +// Fetch fetches references along with the objects necessary to complete +// their histories, from the remote named as FetchOptions.RemoteName. +// // Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are // no changes to be fetched, or an error. func (r *Repository) Fetch(o *FetchOptions) error { + return r.FetchContext(context.Background(), o) +} + +// FetchContext fetches references along with the objects necessary to complete +// their histories, from the remote named as FetchOptions.RemoteName. +// +// Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are +// no changes to be fetched, or an error. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (r *Repository) FetchContext(ctx context.Context, o *FetchOptions) error { if err := o.Validate(); err != nil { return err } @@ -594,11 +638,24 @@ func (r *Repository) Fetch(o *FetchOptions) error { return err } - return remote.Fetch(o) + return remote.FetchContext(ctx, o) } -// Push pushes changes to a remote. +// Push performs a push to the remote. Returns NoErrAlreadyUpToDate if +// the remote was already up-to-date, from the remote named as +// FetchOptions.RemoteName. func (r *Repository) Push(o *PushOptions) error { + return r.PushContext(context.Background(), o) +} + +// PushContext performs a push to the remote. Returns NoErrAlreadyUpToDate if +// the remote was already up-to-date, from the remote named as +// FetchOptions.RemoteName. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (r *Repository) PushContext(ctx context.Context, o *PushOptions) error { if err := o.Validate(); err != nil { return err } @@ -608,7 +665,7 @@ func (r *Repository) Push(o *PushOptions) error { return err } - return remote.Push(o) + return remote.PushContext(ctx, o) } // Log returns the commit history from the given LogOptions. diff --git a/repository_test.go b/repository_test.go index 3cc8d48..7f984f6 100644 --- a/repository_test.go +++ b/repository_test.go @@ -2,6 +2,7 @@ package git import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -165,6 +166,17 @@ func (s *RepositorySuite) TestClone(c *C) { c.Assert(remotes, HasLen, 1) } +func (s *RepositorySuite) TestCloneContext(c *C) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := CloneContext(ctx, memory.NewStorage(), nil, &CloneOptions{ + URL: s.GetBasicLocalRepositoryURL(), + }) + + c.Assert(err, NotNil) +} + func (s *RepositorySuite) TestCreateRemoteAndRemote(c *C) { r, _ := Init(memory.NewStorage(), nil) remote, err := r.CreateRemote(&config.RemoteConfig{ @@ -370,11 +382,7 @@ func (s *RepositorySuite) TestPlainOpenNotExists(c *C) { } func (s *RepositorySuite) TestPlainClone(c *C) { - dir, err := ioutil.TempDir("", "plain-clone") - c.Assert(err, IsNil) - defer os.RemoveAll(dir) - - r, err := PlainClone(dir, false, &CloneOptions{ + r, err := PlainClone(c.MkDir(), false, &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -385,6 +393,17 @@ func (s *RepositorySuite) TestPlainClone(c *C) { c.Assert(remotes, HasLen, 1) } +func (s *RepositorySuite) TestPlainCloneContext(c *C) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := PlainCloneContext(ctx, c.MkDir(), false, &CloneOptions{ + URL: s.GetBasicLocalRepositoryURL(), + }) + + c.Assert(err, NotNil) +} + func (s *RepositorySuite) TestPlainCloneWithRecurseSubmodules(c *C) { dir, err := ioutil.TempDir("", "plain-clone-submodule") c.Assert(err, IsNil) @@ -426,6 +445,20 @@ func (s *RepositorySuite) TestFetch(c *C) { c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") } +func (s *RepositorySuite) TestFetchContext(c *C) { + r, _ := Init(memory.NewStorage(), nil) + _, err := r.CreateRemote(&config.RemoteConfig{ + Name: DefaultRemoteName, + URL: s.GetBasicLocalRepositoryURL(), + }) + c.Assert(err, IsNil) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + c.Assert(r.FetchContext(ctx, &FetchOptions{}), NotNil) +} + func (s *RepositorySuite) TestCloneWithProgress(c *C) { fs := memfs.New() @@ -447,7 +480,7 @@ func (s *RepositorySuite) TestCloneDeep(c *C) { c.Assert(err, Equals, plumbing.ErrReferenceNotFound) c.Assert(head, IsNil) - err = r.clone(&CloneOptions{ + err = r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -486,7 +519,7 @@ func (s *RepositorySuite) TestCloneConfig(c *C) { c.Assert(err, Equals, plumbing.ErrReferenceNotFound) c.Assert(head, IsNil) - err = r.clone(&CloneOptions{ + err = r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -508,7 +541,7 @@ func (s *RepositorySuite) TestCloneSingleBranchAndNonHEAD(c *C) { c.Assert(err, Equals, plumbing.ErrReferenceNotFound) c.Assert(head, IsNil) - err = r.clone(&CloneOptions{ + err = r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), ReferenceName: plumbing.ReferenceName("refs/heads/branch"), SingleBranch: true, @@ -545,7 +578,7 @@ func (s *RepositorySuite) TestCloneSingleBranch(c *C) { c.Assert(err, Equals, plumbing.ErrReferenceNotFound) c.Assert(head, IsNil) - err = r.clone(&CloneOptions{ + err = r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), SingleBranch: true, }) @@ -576,7 +609,7 @@ func (s *RepositorySuite) TestCloneSingleBranch(c *C) { func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), ReferenceName: plumbing.ReferenceName("refs/tags/v1.0.0"), }) @@ -590,6 +623,7 @@ func (s *RepositorySuite) TestCloneDetachedHEAD(c *C) { func (s *RepositorySuite) TestPush(c *C) { url := c.MkDir() + fmt.Println(url) server, err := PlainInit(url, true) c.Assert(err, IsNil) @@ -615,6 +649,27 @@ func (s *RepositorySuite) TestPush(c *C) { }) } +func (s *RepositorySuite) TestPushContext(c *C) { + url := c.MkDir() + fmt.Println(url) + _, err := PlainInit(url, true) + c.Assert(err, IsNil) + + _, err = s.Repository.CreateRemote(&config.RemoteConfig{ + Name: "foo", + URL: url, + }) + c.Assert(err, IsNil) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + err = s.Repository.PushContext(ctx, &PushOptions{ + RemoteName: "foo", + }) + c.Assert(err, NotNil) +} + func (s *RepositorySuite) TestPushDepth(c *C) { url := c.MkDir() server, err := PlainClone(url, true, &CloneOptions{ @@ -670,7 +725,7 @@ func (s *RepositorySuite) TestPushNonExistentRemote(c *C) { func (s *RepositorySuite) TestLog(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -698,7 +753,7 @@ func (s *RepositorySuite) TestLog(c *C) { func (s *RepositorySuite) TestLogHead(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -730,7 +785,7 @@ func (s *RepositorySuite) TestLogHead(c *C) { func (s *RepositorySuite) TestLogError(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -744,7 +799,7 @@ func (s *RepositorySuite) TestLogError(c *C) { func (s *RepositorySuite) TestCommit(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -768,7 +823,7 @@ func (s *RepositorySuite) TestCommit(c *C) { func (s *RepositorySuite) TestCommits(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) count := 0 @@ -791,7 +846,7 @@ func (s *RepositorySuite) TestCommits(c *C) { func (s *RepositorySuite) TestBlob(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) @@ -813,7 +868,7 @@ func (s *RepositorySuite) TestBlob(c *C) { func (s *RepositorySuite) TestBlobs(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) count := 0 @@ -840,7 +895,7 @@ func (s *RepositorySuite) TestTagObject(c *C) { ) r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: url}) + err := r.clone(context.Background(), &CloneOptions{URL: url}) c.Assert(err, IsNil) hash := plumbing.NewHash("ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc") @@ -858,7 +913,7 @@ func (s *RepositorySuite) TestTags(c *C) { ) r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: url}) + err := r.clone(context.Background(), &CloneOptions{URL: url}) c.Assert(err, IsNil) count := 0 @@ -903,7 +958,7 @@ func (s *RepositorySuite) TestNotes(c *C) { ) r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: url}) + err := r.clone(context.Background(), &CloneOptions{URL: url}) c.Assert(err, IsNil) count := 0 @@ -922,7 +977,7 @@ func (s *RepositorySuite) TestNotes(c *C) { func (s *RepositorySuite) TestTree(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), }) c.Assert(err, IsNil) @@ -945,7 +1000,7 @@ func (s *RepositorySuite) TestTree(c *C) { func (s *RepositorySuite) TestTrees(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) count := 0 @@ -973,7 +1028,7 @@ func (s *RepositorySuite) TestTagObjects(c *C) { ) r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: url}) + err := r.clone(context.Background(), &CloneOptions{URL: url}) c.Assert(err, IsNil) count := 0 @@ -998,7 +1053,7 @@ func (s *RepositorySuite) TestTagObjects(c *C) { func (s *RepositorySuite) TestCommitIterClosePanic(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) commits, err := r.CommitObjects() @@ -1008,7 +1063,7 @@ func (s *RepositorySuite) TestCommitIterClosePanic(c *C) { func (s *RepositorySuite) TestRef(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) ref, err := r.Reference(plumbing.HEAD, false) @@ -1022,7 +1077,7 @@ func (s *RepositorySuite) TestRef(c *C) { func (s *RepositorySuite) TestRefs(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) c.Assert(err, IsNil) @@ -1034,7 +1089,7 @@ func (s *RepositorySuite) TestRefs(c *C) { func (s *RepositorySuite) TestObject(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) hash := plumbing.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5") @@ -1047,7 +1102,7 @@ func (s *RepositorySuite) TestObject(c *C) { func (s *RepositorySuite) TestObjects(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) count := 0 @@ -1069,7 +1124,7 @@ func (s *RepositorySuite) TestObjects(c *C) { func (s *RepositorySuite) TestObjectNotFound(c *C) { r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) + err := r.clone(context.Background(), &CloneOptions{URL: s.GetBasicLocalRepositoryURL()}) c.Assert(err, IsNil) hash := plumbing.NewHash("0a3fb06ff80156fb153bcdcc58b5e16c2d27625c") @@ -1099,7 +1154,7 @@ func (s *RepositorySuite) TestResolveRevision(c *C) { ) r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: url}) + err := r.clone(context.Background(), &CloneOptions{URL: url}) c.Assert(err, IsNil) datas := map[string]string{ @@ -1128,7 +1183,7 @@ func (s *RepositorySuite) TestResolveRevisionWithErrors(c *C) { ) r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{URL: url}) + err := r.clone(context.Background(), &CloneOptions{URL: url}) c.Assert(err, IsNil) datas := map[string]string{ diff --git a/submodule.go b/submodule.go index 572e6f7..ac027c2 100644 --- a/submodule.go +++ b/submodule.go @@ -2,10 +2,11 @@ package git import ( "bytes" + "context" "errors" "fmt" - billy "gopkg.in/src-d/go-billy.v3" + "gopkg.in/src-d/go-billy.v3" "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/format/index" @@ -139,10 +140,21 @@ func (s *Submodule) Repository() (*Repository, error) { // submodule should be initialized first calling the Init method or setting in // the options SubmoduleUpdateOptions.Init equals true func (s *Submodule) Update(o *SubmoduleUpdateOptions) error { - return s.update(o, plumbing.ZeroHash) + return s.UpdateContext(context.Background(), o) } -func (s *Submodule) update(o *SubmoduleUpdateOptions, forceHash plumbing.Hash) error { +// UpdateContext the registered submodule to match what the superproject +// expects, the submodule should be initialized first calling the Init method or +// setting in the options SubmoduleUpdateOptions.Init equals true. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (s *Submodule) UpdateContext(ctx context.Context, o *SubmoduleUpdateOptions) error { + return s.update(ctx, o, plumbing.ZeroHash) +} + +func (s *Submodule) update(ctx context.Context, o *SubmoduleUpdateOptions, forceHash plumbing.Hash) error { if !s.initialized && !o.Init { return ErrSubmoduleNotInitialized } @@ -173,7 +185,7 @@ func (s *Submodule) update(o *SubmoduleUpdateOptions, forceHash plumbing.Hash) e return err } - if err := s.fetchAndCheckout(r, o, hash); err != nil { + if err := s.fetchAndCheckout(ctx, r, o, hash); err != nil { return err } @@ -202,9 +214,11 @@ func (s *Submodule) doRecursiveUpdate(r *Repository, o *SubmoduleUpdateOptions) return l.Update(new) } -func (s *Submodule) fetchAndCheckout(r *Repository, o *SubmoduleUpdateOptions, hash plumbing.Hash) error { +func (s *Submodule) fetchAndCheckout( + ctx context.Context, r *Repository, o *SubmoduleUpdateOptions, hash plumbing.Hash, +) error { if !o.NoFetch { - err := r.Fetch(&FetchOptions{}) + err := r.FetchContext(ctx, &FetchOptions{}) if err != nil && err != NoErrAlreadyUpToDate { return err } @@ -239,8 +253,17 @@ func (s Submodules) Init() error { // Update updates all the submodules in this list. func (s Submodules) Update(o *SubmoduleUpdateOptions) error { + return s.UpdateContext(context.Background(), o) +} + +// UpdateContext updates all the submodules in this list. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (s Submodules) UpdateContext(ctx context.Context, o *SubmoduleUpdateOptions) error { for _, sub := range s { - if err := sub.Update(o); err != nil { + if err := sub.UpdateContext(ctx, o); err != nil { return err } } diff --git a/submodule_test.go b/submodule_test.go index b398664..dd797c5 100644 --- a/submodule_test.go +++ b/submodule_test.go @@ -1,6 +1,7 @@ package git import ( + "context" "io/ioutil" "os" "path/filepath" @@ -190,3 +191,14 @@ func (s *SubmoduleSuite) TestSubmodulesStatus(c *C) { c.Assert(err, IsNil) c.Assert(status, HasLen, 2) } + +func (s *SubmoduleSuite) TestSubmodulesUpdateContext(c *C) { + sm, err := s.Worktree.Submodules() + c.Assert(err, IsNil) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + err = sm.UpdateContext(ctx, &SubmoduleUpdateOptions{Init: true}) + c.Assert(err, NotNil) +} diff --git a/worktree.go b/worktree.go index 0c15d4c..5768888 100644 --- a/worktree.go +++ b/worktree.go @@ -1,6 +1,7 @@ package git import ( + "context" "errors" "fmt" "io" @@ -36,11 +37,22 @@ type Worktree struct { // Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are // no changes to be fetched, or an error. func (w *Worktree) Pull(o *PullOptions) error { + return w.PullContext(context.Background(), o) +} + +// PullContext incorporates changes from a remote repository into the current +// branch. Returns nil if the operation is successful, NoErrAlreadyUpToDate if +// there are no changes to be fetched, or an error. +// +// The provided Context must be non-nil. If the context expires before the +// operation is complete, an error is returned. The context only affects to the +// transport operations. +func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { if err := o.Validate(); err != nil { return err } - head, err := w.r.fetchAndUpdateReferences(&FetchOptions{ + head, err := w.r.fetchAndUpdateReferences(ctx, &FetchOptions{ RemoteName: o.RemoteName, Depth: o.Depth, Auth: o.Auth, @@ -334,7 +346,9 @@ func (w *Worktree) checkoutChangeSubmodule(name string, return err } - return sub.update(&SubmoduleUpdateOptions{}, e.Hash) + // TODO: the submodule update should be reviewed as reported at: + // https://github.com/src-d/go-git/issues/415 + return sub.update(context.TODO(), &SubmoduleUpdateOptions{}, e.Hash) case merkletrie.Insert: mode, err := e.Mode.ToOSFileMode() if err != nil { diff --git a/worktree_test.go b/worktree_test.go index 97c4055..150176f 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -2,6 +2,7 @@ package git import ( "bytes" + "context" "io/ioutil" "os" "path/filepath" @@ -83,7 +84,7 @@ func (s *WorktreeSuite) TestPullUpdateReferencesIfNeeded(c *C) { func (s *WorktreeSuite) TestPullInSingleBranch(c *C) { r, _ := Init(memory.NewStorage(), memfs.New()) - err := r.clone(&CloneOptions{ + err := r.clone(context.Background(), &CloneOptions{ URL: s.GetBasicLocalRepositoryURL(), SingleBranch: true, }) |