Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6abbba9

Browse files
authored
Merge pull request #60 from sdboyer/submodules
Defend against the waking nightmare that is git submodules
2 parents ccb44ce + f0e30a8 commit 6abbba9

2 files changed

Lines changed: 159 additions & 2 deletions

File tree

git.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func (s GitRepo) Vcs() Type {
7070

7171
// Get is used to perform an initial clone of a repository.
7272
func (s *GitRepo) Get() error {
73-
out, err := s.run("git", "clone", s.Remote(), s.LocalPath())
73+
out, err := s.run("git", "clone", "--recursive", s.Remote(), s.LocalPath())
7474

7575
// There are some windows cases where Git cannot create the parent directory,
7676
// if it does not already exist, to the location it's trying to create the
@@ -151,7 +151,8 @@ func (s *GitRepo) Update() error {
151151
if err != nil {
152152
return NewRemoteError("Unable to update repository", err, string(out))
153153
}
154-
return nil
154+
155+
return s.defendAgainstSubmodules()
155156
}
156157

157158
// UpdateVersion sets the version of a package currently checked out via Git.
@@ -160,6 +161,30 @@ func (s *GitRepo) UpdateVersion(version string) error {
160161
if err != nil {
161162
return NewLocalError("Unable to update checked out version", err, string(out))
162163
}
164+
165+
return s.defendAgainstSubmodules()
166+
}
167+
168+
// defendAgainstSubmodules tries to keep repo state sane in the event of
169+
// submodules. Or nested submodules. What a great idea, submodules.
170+
func (s *GitRepo) defendAgainstSubmodules() error {
171+
// First, update them to whatever they should be, if there should happen to be any.
172+
out, err := s.RunFromDir("git", "submodule", "update", "--init", "--recursive")
173+
if err != nil {
174+
return NewLocalError("Unexpected error while defensively updating submodules", err, string(out))
175+
}
176+
// Now, do a special extra-aggressive clean in case changing versions caused
177+
// one or more submodules to go away.
178+
out, err = s.RunFromDir("git", "clean", "-x", "-d", "-f", "-f")
179+
if err != nil {
180+
return NewLocalError("Unexpected error while defensively cleaning up after possible derelict submodule directories", err, string(out))
181+
}
182+
// Then, repeat just in case there are any nested submodules that went away.
183+
out, err = s.RunFromDir("git", "submodule", "foreach", "--recursive", "git", "clean", "-x", "-d", "-f", "-f")
184+
if err != nil {
185+
return NewLocalError("Unexpected error while defensively cleaning up after possible derelict nested submodule directories", err, string(out))
186+
}
187+
163188
return nil
164189
}
165190

@@ -359,6 +384,12 @@ func (s *GitRepo) ExportDir(dir string) error {
359384
if err != nil {
360385
return NewLocalError("Unable to export source", err, string(out))
361386
}
387+
// and now, the horror of submodules
388+
out, err = s.RunFromDir("git", "submodule", "foreach", "--recursive", "'git checkout-index -f -a --prefix=\""+filepath.Join(dir, "$path")+"\"'")
389+
s.log(out)
390+
if err != nil {
391+
return NewLocalError("Error while exporting submodule sources", err, string(out))
392+
}
362393

363394
return nil
364395
}

git_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package vcs
22

33
import (
4+
"fmt"
45
"io/ioutil"
56
"path/filepath"
67
"time"
@@ -356,3 +357,128 @@ func TestGitInit(t *testing.T) {
356357
t.Error(err)
357358
}
358359
}
360+
361+
func TestGitSubmoduleHandling(t *testing.T) {
362+
tempDir, err := ioutil.TempDir("", "go-vcs-git-submodule-tests")
363+
if err != nil {
364+
t.Fatal(err)
365+
}
366+
defer func() {
367+
err = os.RemoveAll(tempDir)
368+
if err != nil {
369+
t.Error(err)
370+
}
371+
}()
372+
373+
dumplocal := func(err error) string {
374+
if terr, ok := err.(*LocalError); ok {
375+
return fmt.Sprintf("msg: %s\norig: %s\nout: %s", terr.Error(), terr.Original(), terr.Out())
376+
}
377+
return err.Error()
378+
}
379+
380+
subdirExists := func(dir ...string) bool {
381+
_, err := os.Stat(filepath.Join(append([]string{tempDir}, dir...)...))
382+
return err == nil
383+
}
384+
385+
// Initial clone should get version with two submodules, each of which have
386+
// their own submodule
387+
repo, err := NewGitRepo("https://github.com/sdboyer/subm", tempDir)
388+
if err != nil {
389+
t.Fatal(dumplocal(err))
390+
}
391+
err = repo.Get()
392+
if err != nil {
393+
t.Fatalf("unable to clone Git repo. Err was %s", dumplocal(err))
394+
}
395+
396+
// Verify we are on the right version.
397+
v, err := repo.Version()
398+
if v != "18e3a5f6fc7f6d577e732e7a5ab2caf990efbf8f" {
399+
t.Fatalf("did not start from expected rev, tests could fail - bailing out (got %s)", v)
400+
}
401+
if err != nil {
402+
t.Fatal(dumplocal(err))
403+
}
404+
405+
if !subdirExists("subm1", ".git") {
406+
t.Fatal("subm1 submodule does not exist on initial clone/checkout")
407+
}
408+
if !subdirExists("subm1", "dep-test", ".git") {
409+
t.Fatal("dep-test submodule nested under subm1 does not exist on initial clone/checkout")
410+
}
411+
412+
if !subdirExists("subm-again", ".git") {
413+
t.Fatal("subm-again submodule does not exist on initial clone/checkout")
414+
}
415+
if !subdirExists("subm-again", "dep-test", ".git") {
416+
t.Fatal("dep-test submodule nested under subm-again does not exist on initial clone/checkout")
417+
}
418+
419+
// Now switch to version with no submodules, make sure they all go away
420+
err = repo.UpdateVersion("e677f82015f72ac1c8fafa66b5463163b3597af2")
421+
if err != nil {
422+
t.Fatalf("checking out needed version failed with err: %s", dumplocal(err))
423+
}
424+
425+
if subdirExists("subm1") {
426+
t.Fatal("checking out version without submodule did not clean up immediate submodules")
427+
}
428+
if subdirExists("subm1", "dep-test") {
429+
t.Fatal("checking out version without submodule did not clean up nested submodules")
430+
}
431+
if subdirExists("subm-again") {
432+
t.Fatal("checking out version without submodule did not clean up immediate submodules")
433+
}
434+
if subdirExists("subm-again", "dep-test") {
435+
t.Fatal("checking out version without submodule did not clean up nested submodules")
436+
}
437+
438+
err = repo.UpdateVersion("aaf7aa1bc4c3c682cc530eca8f80417088ee8540")
439+
if err != nil {
440+
t.Fatalf("checking out needed version failed with err: %s", dumplocal(err))
441+
}
442+
443+
if !subdirExists("subm1", ".git") {
444+
t.Fatal("checking out version with immediate submodule did not set up git subrepo")
445+
}
446+
447+
err = repo.UpdateVersion("6cc4669af468f3b4f16e7e96275ad01ade5b522f")
448+
if err != nil {
449+
t.Fatalf("checking out needed version failed with err: %s", dumplocal(err))
450+
}
451+
452+
if !subdirExists("subm1", "dep-test", ".git") {
453+
t.Fatal("checking out version with nested submodule did not set up nested git subrepo")
454+
}
455+
456+
err = repo.UpdateVersion("aaf7aa1bc4c3c682cc530eca8f80417088ee8540")
457+
if err != nil {
458+
t.Fatalf("checking out needed version failed with err: %s", dumplocal(err))
459+
}
460+
461+
if subdirExists("subm1", "dep-test") {
462+
t.Fatal("rolling back to version without nested submodule did not clean up the nested submodule")
463+
}
464+
465+
err = repo.UpdateVersion("18e3a5f6fc7f6d577e732e7a5ab2caf990efbf8f")
466+
if err != nil {
467+
t.Fatalf("checking out needed version failed with err: %s", dumplocal(err))
468+
}
469+
470+
if !subdirExists("subm1", ".git") {
471+
t.Fatal("subm1 submodule does not exist after switch from other commit")
472+
}
473+
if !subdirExists("subm1", "dep-test", ".git") {
474+
t.Fatal("dep-test submodule nested under subm1 does not exist after switch from other commit")
475+
}
476+
477+
if !subdirExists("subm-again", ".git") {
478+
t.Fatal("subm-again submodule does not exist after switch from other commit")
479+
}
480+
if !subdirExists("subm-again", "dep-test", ".git") {
481+
t.Fatal("dep-test submodule nested under subm-again does not exist after switch from other commit")
482+
}
483+
484+
}

0 commit comments

Comments
 (0)