Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit b3b6e51

Browse files
committed
submodule init implementation
1 parent 498dbf7 commit b3b6e51

File tree

10 files changed

+142
-50
lines changed

10 files changed

+142
-50
lines changed

_examples/clone/main.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ func main() {
1717
Info("git clone %s %s", url, directory)
1818

1919
r, err := git.PlainClone(directory, false, &git.CloneOptions{
20-
URL: url,
21-
Depth: 1,
20+
URL: url,
21+
RecursiveSubmodules: true,
22+
Depth: 1,
2223
})
2324

2425
CheckIfError(err)

common.go

+1-18
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,6 @@
11
package git
22

3-
import (
4-
"strings"
5-
6-
"srcd.works/go-git.v4/config"
7-
"srcd.works/go-git.v4/plumbing/storer"
8-
)
9-
10-
// Storer is a generic storage of objects, references and any information
11-
// related to a particular repository. The package srcd.works/go-git.v4/storage
12-
// contains two implementation a filesystem base implementation (such as `.git`)
13-
// and a memory implementations being ephemeral
14-
type Storer interface {
15-
storer.EncodedObjectStorer
16-
storer.ReferenceStorer
17-
storer.ShallowStorer
18-
storer.IndexStorer
19-
config.ConfigStorer
20-
}
3+
import "strings"
214

225
// countLines returns the number of lines in a string à la git, this is
236
// The newline character is assumed to be '\n'. The empty string

doc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
// It is highly extensible, we have been following the open/close principle in
88
// its design to facilitate extensions, mainly focusing the efforts on the
99
// persistence of the objects.
10-
package git
10+
package git // import "srcd.works/go-git.v4"

options.go

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type CloneOptions struct {
3232
SingleBranch bool
3333
// Limit fetching to the specified number of commits
3434
Depth int
35+
// RecursiveSubmodules after the clone is created, initialize all submodules
36+
// within, using their default settings. This option is ignored if the
37+
// cloned repository does not have a worktree
38+
RecursiveSubmodules bool
3539
// Progress is where the human readable information sent by the server is
3640
// stored, if nil nothing is stored and the capability (if supported)
3741
// no-progress, is sent to the server to avoid send this information

remote.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"srcd.works/go-git.v4/plumbing/storer"
1717
"srcd.works/go-git.v4/plumbing/transport"
1818
"srcd.works/go-git.v4/plumbing/transport/client"
19+
"srcd.works/go-git.v4/storage"
1920
"srcd.works/go-git.v4/storage/memory"
2021
"srcd.works/go-git.v4/utils/ioutil"
2122
)
@@ -25,10 +26,10 @@ var NoErrAlreadyUpToDate = errors.New("already up-to-date")
2526
// Remote represents a connection to a remote repository
2627
type Remote struct {
2728
c *config.RemoteConfig
28-
s Storer
29+
s storage.Storer
2930
}
3031

31-
func newRemote(s Storer, c *config.RemoteConfig) *Remote {
32+
func newRemote(s storage.Storer, c *config.RemoteConfig) *Remote {
3233
return &Remote{s: s, c: c}
3334
}
3435

@@ -321,7 +322,9 @@ func getHaves(localRefs storer.ReferenceStorer) ([]plumbing.Hash, error) {
321322
return haves, nil
322323
}
323324

324-
func getWants(spec []config.RefSpec, localStorer Storer, remoteRefs storer.ReferenceStorer) ([]plumbing.Hash, error) {
325+
func getWants(
326+
spec []config.RefSpec, localStorer storage.Storer, remoteRefs storer.ReferenceStorer,
327+
) ([]plumbing.Hash, error) {
325328
wantTags := true
326329
for _, s := range spec {
327330
if !s.IsWildcard() {

remote_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"srcd.works/go-git.v4/config"
1212
"srcd.works/go-git.v4/plumbing"
1313
"srcd.works/go-git.v4/plumbing/storer"
14+
"srcd.works/go-git.v4/storage"
1415
"srcd.works/go-git.v4/storage/filesystem"
1516
"srcd.works/go-git.v4/storage/memory"
1617

@@ -126,7 +127,7 @@ func (s *RemoteSuite) TestFetchWithProgress(c *C) {
126127
}
127128

128129
type mockPackfileWriter struct {
129-
Storer
130+
storage.Storer
130131
PackfileWriterCalled bool
131132
}
132133

repository.go

+29-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"srcd.works/go-git.v4/plumbing"
1111
"srcd.works/go-git.v4/plumbing/object"
1212
"srcd.works/go-git.v4/plumbing/storer"
13+
"srcd.works/go-git.v4/storage"
1314
"srcd.works/go-git.v4/storage/filesystem"
1415

1516
"srcd.works/go-billy.v1"
@@ -29,7 +30,7 @@ var (
2930

3031
// Repository represents a git repository
3132
type Repository struct {
32-
Storer Storer
33+
Storer storage.Storer
3334

3435
r map[string]*Remote
3536
wt billy.Filesystem
@@ -38,7 +39,7 @@ type Repository struct {
3839
// Init creates an empty git repository, based on the given Storer and worktree.
3940
// The worktree Filesystem is optional, if nil a bare repository is created. If
4041
// the given storer is not empty ErrRepositoryAlreadyExists is returned
41-
func Init(s Storer, worktree billy.Filesystem) (*Repository, error) {
42+
func Init(s storage.Storer, worktree billy.Filesystem) (*Repository, error) {
4243
r := newRepository(s, worktree)
4344
_, err := r.Reference(plumbing.HEAD, false)
4445
switch err {
@@ -66,7 +67,7 @@ func Init(s Storer, worktree billy.Filesystem) (*Repository, error) {
6667
// The worktree can be nil when the repository being opened is bare, if the
6768
// repository is a normal one (not bare) and worktree is nil the err
6869
// ErrWorktreeNotProvided is returned
69-
func Open(s Storer, worktree billy.Filesystem) (*Repository, error) {
70+
func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error) {
7071
_, err := s.Reference(plumbing.HEAD)
7172
if err == plumbing.ErrReferenceNotFound {
7273
return nil, ErrRepositoryNotExists
@@ -91,7 +92,7 @@ func Open(s Storer, worktree billy.Filesystem) (*Repository, error) {
9192
// Clone a repository into the given Storer and worktree Filesystem with the
9293
// given options, if worktree is nil a bare repository is created. If the given
9394
// storer is not empty ErrRepositoryAlreadyExists is returned
94-
func Clone(s Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) {
95+
func Clone(s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) {
9596
r, err := Init(s, worktree)
9697
if err != nil {
9798
return nil, err
@@ -159,7 +160,7 @@ func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error)
159160
return r, r.clone(o)
160161
}
161162

162-
func newRepository(s Storer, worktree billy.Filesystem) *Repository {
163+
func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository {
163164
return &Repository{
164165
Storer: s,
165166
wt: worktree,
@@ -247,12 +248,6 @@ func (r *Repository) clone(o *CloneOptions) error {
247248
return err
248249
}
249250

250-
// marks the repository as bare in the config, until we have Worktree, all
251-
// the repository are bare
252-
if err := r.setIsBare(true); err != nil {
253-
return err
254-
}
255-
256251
c := &config.RemoteConfig{
257252
Name: o.RemoteName,
258253
URL: o.URL,
@@ -270,11 +265,13 @@ func (r *Repository) clone(o *CloneOptions) error {
270265
Progress: o.Progress,
271266
})
272267
if err != nil {
268+
273269
return err
274270
}
275271

276272
head, err := storer.ResolveReference(remoteRefs, o.ReferenceName)
277273
if err != nil {
274+
278275
return err
279276
}
280277

@@ -283,12 +280,33 @@ func (r *Repository) clone(o *CloneOptions) error {
283280
}
284281

285282
if err := r.updateWorktree(); err != nil {
283+
fmt.Println("q", err)
286284
return err
287285
}
288286

287+
if o.RecursiveSubmodules && r.wt != nil {
288+
if err := r.initSubmodules(); err != nil {
289+
return err
290+
}
291+
}
292+
289293
return r.updateRemoteConfig(remote, o, c, head)
290294
}
291295

296+
func (r *Repository) initSubmodules() error {
297+
w, err := r.Worktree()
298+
if err != nil {
299+
return err
300+
}
301+
302+
s, err := w.Submodules()
303+
if err != nil {
304+
return err
305+
}
306+
307+
return s.Init()
308+
}
309+
292310
func (r *Repository) cloneRefSpec(o *CloneOptions,
293311
c *config.RemoteConfig) []config.RefSpec {
294312

repository_test.go

+4-14
Original file line numberDiff line numberDiff line change
@@ -516,26 +516,21 @@ func (s *RepositorySuite) TestPullProgress(c *C) {
516516
}
517517

518518
func (s *RepositorySuite) TestPullAdd(c *C) {
519-
path := fixtures.Basic().One().Worktree().Base()
519+
path := fixtures.Basic().ByTag("worktree").One().Worktree().Base()
520520

521-
r, _ := Init(memory.NewStorage(), nil)
522-
err := r.clone(&CloneOptions{
521+
r, err := Clone(memory.NewStorage(), nil, &CloneOptions{
523522
URL: fmt.Sprintf("file://%s", filepath.Join(path, ".git")),
524523
})
525524

526525
c.Assert(err, IsNil)
527526

528527
storage := r.Storer.(*memory.Storage)
529-
c.Assert(storage.Objects, HasLen, 31)
528+
c.Assert(storage.Objects, HasLen, 28)
530529

531530
branch, err := r.Reference("refs/heads/master", false)
532531
c.Assert(err, IsNil)
533532
c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
534533

535-
branch, err = r.Reference("refs/remotes/origin/branch", false)
536-
c.Assert(err, IsNil)
537-
c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881")
538-
539534
ExecuteOnPath(c, path,
540535
"touch foo",
541536
"git add foo",
@@ -546,16 +541,11 @@ func (s *RepositorySuite) TestPullAdd(c *C) {
546541
c.Assert(err, IsNil)
547542

548543
// the commit command has introduced a new commit, tree and blob
549-
c.Assert(storage.Objects, HasLen, 34)
544+
c.Assert(storage.Objects, HasLen, 31)
550545

551546
branch, err = r.Reference("refs/heads/master", false)
552547
c.Assert(err, IsNil)
553548
c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5")
554-
555-
// the commit command, was in the local branch, so the remote should be read ok
556-
branch, err = r.Reference("refs/remotes/origin/branch", false)
557-
c.Assert(err, IsNil)
558-
c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881")
559549
}
560550

561551
func (s *RepositorySuite) TestPushToEmptyRepository(c *C) {

submodule.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package git
2+
3+
import "srcd.works/go-git.v4/plumbing"
4+
5+
type Submodule struct {
6+
Name string
7+
Branch string
8+
URL string
9+
10+
r *Repository
11+
}
12+
13+
func (s *Submodule) Init() error {
14+
return s.r.clone(&CloneOptions{
15+
URL: s.URL,
16+
ReferenceName: plumbing.ReferenceName(s.Branch),
17+
})
18+
}
19+
20+
type Submodules []*Submodule
21+
22+
func (s Submodules) Init() error {
23+
for _, sub := range s {
24+
if err := sub.Init(); err != nil {
25+
return err
26+
}
27+
}
28+
29+
return nil
30+
}

worktree.go

+62
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"errors"
55
"fmt"
66
"io"
7+
"io/ioutil"
78
"os"
89

10+
"srcd.works/go-git.v4/config"
911
"srcd.works/go-git.v4/plumbing"
1012
"srcd.works/go-git.v4/plumbing/format/index"
1113
"srcd.works/go-git.v4/plumbing/object"
@@ -104,6 +106,7 @@ func (w *Worktree) Status() (Status, error) {
104106

105107
files, err := readDirAll(w.fs)
106108
if err != nil {
109+
fmt.Println("ch", err)
107110
return nil, err
108111
}
109112

@@ -167,6 +170,61 @@ func (w *Worktree) getMode(fi billy.FileInfo) os.FileMode {
167170
return object.FileMode
168171
}
169172

173+
const gitmodulesFile = ".gitmodules"
174+
175+
func (w *Worktree) Submodules() (Submodules, error) {
176+
l := make(Submodules, 0)
177+
m, err := w.readGitmodulesFile()
178+
if err != nil || m == nil {
179+
return l, err
180+
}
181+
182+
for _, c := range m.Submodules {
183+
s, err := w.newSubmodule(c)
184+
if err != nil {
185+
return nil, err
186+
}
187+
188+
l = append(l, s)
189+
}
190+
191+
return l, nil
192+
}
193+
194+
func (w *Worktree) newSubmodule(m *config.Submodule) (*Submodule, error) {
195+
s, err := w.r.Storer.Module(m.Name)
196+
if err != nil {
197+
return nil, err
198+
}
199+
200+
return &Submodule{
201+
Name: m.Name,
202+
URL: m.URL,
203+
204+
r: newRepository(s, w.fs.Dir(m.Path)),
205+
}, nil
206+
}
207+
208+
func (w *Worktree) readGitmodulesFile() (*config.Modules, error) {
209+
f, err := w.fs.Open(gitmodulesFile)
210+
if err != nil {
211+
if os.IsNotExist(err) {
212+
return nil, nil
213+
}
214+
215+
return nil, err
216+
}
217+
218+
input, err := ioutil.ReadAll(f)
219+
if err != nil {
220+
return nil, err
221+
}
222+
223+
m := config.NewModules()
224+
return m, m.Unmarshal(input)
225+
226+
}
227+
170228
// Status current status of a Worktree
171229
type Status map[string]*FileStatus
172230

@@ -287,6 +345,10 @@ func doReadDirAll(fs billy.Filesystem, path string, files map[string]billy.FileI
287345

288346
l, err := fs.ReadDir(path)
289347
if err != nil {
348+
if os.IsNotExist(err) {
349+
return nil
350+
}
351+
290352
return err
291353
}
292354

0 commit comments

Comments
 (0)