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

Submodules init and update #270

Merged
merged 12 commits into from
Feb 21, 2017
Merged

Submodules init and update #270

merged 12 commits into from
Feb 21, 2017

Conversation

mcuadros
Copy link
Contributor

@mcuadros mcuadros commented Feb 13, 2017

This is the implementation of git submodule init and git submodule update, this implementation has required make some changes non direct related:

  • storage: git.Storer move to storage.Storer and module handling 498dbf7, due to the requirement of make reference to the storage.Storer as part of itself was moved to another package in other to avoid a cyclic dependency
  • plumbing/object: allow TreeIter return SubModule entries b18d649 the submodule were ignored, now are being returned without problem
  • format/index: sort the Entries before encode 940a16c, this was a fix since a Index entries should be save in order by Name
  • config: adding Config.Core.Worktree f8b5557
  • Repository.Init now handles non-standard .git location65351f8, it creates a.git` file in the root of the worktree and sets Config.Worktree when the .git folder is not in a standard location

Once this is merge, I will do another PR renaming config.RemoteConfig to config.Remote to mimic config.Submodule

@mcuadros mcuadros changed the title Submodules init and update Submodules init and update (wip) Feb 14, 2017
@mcuadros mcuadros changed the title Submodules init and update (wip) Submodules init and update Feb 15, 2017
URL: url,
Depth: 1,
URL: url,
RecurseSubmodules: git.DefaultRecursivity,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change the name from DefaultRecursivity to DefaultRecurseModules or DefaultSubmoduleRecursivity or something like that, so that it does not get confused with other parameters related with recursivite of non-submodule stuff (e.g. reference resolution).

options.go Outdated
type SubmoduleUpdateOptions struct {
// Init initializes the submodules recorded in the index
Init bool
// NoFetch tell to the update command to don’t fetch new objects from the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to don’t -> to not

@@ -139,3 +142,9 @@ func (e *Encoder) padEntry(wrote int) error {
func (e *Encoder) encodeFooter() error {
return binary.Write(e.w, e.hash.Sum(nil))
}

type ByName []Entry
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private type?

@@ -21,6 +22,8 @@ type Storer interface {
storer.ShallowStorer
storer.IndexStorer
config.ConfigStorer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove extra line here.

c.Assert(err, IsNil)
c.Assert(storer, NotNil)

storer, err = s.Storer.Module("foo")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point in calling Module twice?
(explain this in the name of the test)

Actually what is the point of this whole test?

Copy link
Contributor Author

@mcuadros mcuadros Feb 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests that returns a module. The function should returns a module if exists or not. The duplicate code is an error

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand your answer.

Can you explain what you are testing in the name of the test or on a comment?

s[name] = NewStorage()
}

return s[name], nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't look up twice in the map, use a variable to store the results of the first lookup.

ModuleStorer
}

type ModuleStorer interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be explained, What if the module doesn't exists?

URL: url,
Depth: 1,
URL: url,
RecursiveSubmodules: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change name to imperative: RecurseSubmodules.

doc.go Outdated
@@ -7,4 +7,4 @@
// 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
package git // import "srcd.works/go-git.v4"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup

options.go Outdated
@@ -32,6 +32,10 @@ type CloneOptions struct {
SingleBranch bool
// Limit fetching to the specified number of commits
Depth int
// RecursiveSubmodules after the clone is created, initialize all submodules
// within, using their default settings. This option is ignored if the
// cloned repository does not have a worktree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end the paragraph with a full stop.

repository.go Outdated
@@ -270,11 +265,13 @@ func (r *Repository) clone(o *CloneOptions) error {
Progress: o.Progress,
})
if err != nil {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line.

repository.go Outdated
return err
}

head, err := storer.ResolveReference(remoteRefs, o.ReferenceName)
if err != nil {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line.

repository.go Outdated
@@ -283,12 +280,33 @@ func (r *Repository) clone(o *CloneOptions) error {
}

if err := r.updateWorktree(); err != nil {
fmt.Println("q", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover?

repository.go Outdated
return err
}

return s.Init()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have a recursive flag, so the init can be done recursively?

worktree.go Outdated
const gitmodulesFile = ".gitmodules"

func (w *Worktree) Submodules() (Submodules, error) {
l := make(Submodules, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this after calling readGitmodulesFile, and make it with the size of the m.Submodules,

worktree.go Outdated

m := config.NewModules()
return m, m.Unmarshal(input)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove empty line.

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

const gitmodulesFile = ".gitmodules"

func (w *Worktree) Submodules() (Submodules, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

document it, especially why it can return (empty slice and nil) and what does it means. Is it an error if the .gitmodules is empty?

@@ -81,7 +81,7 @@ func (iter *FileIter) Next() (*File, error) {
return nil, err
}

if entry.Mode.IsDir() {
if entry.Mode.IsDir() || entry.Mode == SubmoduleMode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert the logic here. Just continue if it is not a file, otherwise we are always adding exceptions as they come along.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate?

Copy link
Contributor

@alcortesm alcortesm Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, this is how it works:

The FileIter.Next method returns Files by going over all the elements in the tree, ignoring Dirs, and now (added in this PR) Submodules.

This is what I propose, instead:

The FileIter.Next method returns Files by going over all the elements in the tree, ignoring everything that is not a file (without naming explicitly what are not files).

This way we don't have to change the code everytime we support a new TreeEntry mode.

So instead of:

if entry.Mode.IsDir() || entry.Mode == SubmoduleMode {
    continue
}

I suggest:

if !entry.Mode.IsFile() && !entry.Mode.IsFileDeprecatedGroupWriteable() {
    continue
}


var count int
i := tree.Files()
i.ForEach(func(f *File) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several things that are wrong with this test:

  • it is too complex to test that the obtained must be a single file.
  • If Files return an empty list of file, I'm pretty sure the test will pass, so we will never know if submodules are ignored.
  • the name of the test does not explain what it is doing (check that submodules are ignored)
  • the test is not calling the function that has the logic under test (FileIter.Next)

Copy link
Contributor Author

@mcuadros mcuadros Feb 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is too complex to test that the obtained must be a single file.

It's asserting the full content of the commit, in this case is only one file that is not a submodule

If Files return an empty list of file, I'm pretty sure the test will pass, so we will never know if submodules are ignored.

Nope, we have that is returned only one file, not less or more.

the name of the test does not explain what it is doing (check that submodules are ignored)

I can change the name, but usually for me is explaining that is walking in a commit with submodule, I am not want to explain the logic behind.

the test is not calling the function that has the logic under test (FileIter.Next)

ForEach calls Next

@@ -247,3 +247,29 @@ func (s *FileSuite) TestFileIter(c *C) {

c.Assert(count, Equals, 1)
}

func (s *FileSuite) TestFileIterSubmodule(c *C) {
st, err := filesystem.NewStorage(fixtures.ByTag("submodule").One().DotGit())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, comment what repository are you using for this test, it is very hard to know what should be inside the commit below otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, if an object is referenced, we should use the url and not a tag

@@ -262,6 +265,41 @@ func (s *TreeSuite) TestTreeWalkerNextNonRecursive(c *C) {
c.Assert(count, Equals, 8)
}

func (s *TreeSuite) TestTreeWalkerNextSubmodule(c *C) {
st, err := filesystem.NewStorage(fixtures.ByTag("submodule").One().DotGit())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, comment what repository are you using for this test, it is very hard to know what should be inside the commit below otherwise.

}

var count int
walker := NewTreeWalker(tree, true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

close the walker, or make NewTreeWalker return the close closure so we never forget about it again.

@@ -61,6 +62,8 @@ func (e *Encoder) encodeHeader(idx *Index) error {
}

func (e *Encoder) encodeEntries(idx *Index) error {
sort.Sort(ByName(idx.Entries))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? please explain and add tests if this is a bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already covered by the tests, I introduced another entry in the tests

@@ -390,6 +390,11 @@ func (d *DotGit) readReferenceFile(refsPath, refFile string) (ref *plumbing.Refe
return plumbing.NewReferenceFromStrings(refFile, line), nil
}

// Module return a billy.Filesystem poiting to the module folder
func (d *DotGit) Module(name string) billy.Filesystem {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

dir *dotgit.DotGit
}

func (s *ModuleStorage) Module(name string) (storage.Storer, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests and comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is already covered by the tests. And the documentation is on the interface as in the other methods

"srcd.works/go-git.v4/storage/filesystem/internal/dotgit"
)

type ModuleStorage struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this type?, can we just make dotgit implement storage.ModuleStorer instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the pattern we are using. I prefer keep dotgit isolate

config/config.go Outdated
@@ -37,6 +37,8 @@ type Config struct {
// IsBare if true this repository is assumed to be bare and has no
// working directory associated with it
IsBare bool
// Worktree is the path to the root of the working tree
Worktree string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change name to WorkTtree.

Add full stop at the end of the phrase.

We should call it WorkTreePath.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worktree is a single word, is a concept. I think that is enough to explained that is a path, since a worktree and is a string.

config/config.go Outdated
@@ -76,6 +78,7 @@ const (
fetchKey = "fetch"
urlKey = "url"
bareKey = "bare"
worktreeKey = "worktree"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to workTreeKey.

return r, setWorktreeAndStoragePaths(r, worktree)
}

func setWorktreeAndStoragePaths(r *Repository, worktree billy.Filesystem) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capitalize tree in the function name.

return err
}

func setConfigWorktree(r *Repository, worktree, storage billy.Filesystem) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capitalize tree in function name.

submodule.go Outdated
Branch string
URL string

m *config.Submodule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it called m?

submodule.go Outdated
URL string

m *config.Submodule
w *Worktree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to WorkTree.

submodule.go Outdated
r *Repository
}

// Config returns the submodule config
func (s *Submodule) Config() *config.Submodule {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make m public instead of a getter?

worktree.go Outdated
@@ -210,6 +210,22 @@ func (w *Worktree) getMode(fi billy.FileInfo) os.FileMode {

const gitmodulesFile = ".gitmodules"

// Submodule returns the submodule with the given name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explains what happens if it does not exists.

worktree.go Outdated
return w.newSubmodule(c)
}

// Submodules returns all the available submodules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain that this also init the submodules and why?

config/config.go Outdated
@@ -40,8 +40,10 @@ type Config struct {
// Worktree is the path to the root of the working tree
Worktree string
}
// Remote list of repository remotes
// Remotes list of repository remotes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop and add a verb.

config/config.go Outdated
Remotes map[string]*RemoteConfig
// Submodules list of repository submodules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop and add a verb.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain what is the key to the map.

for _, sub := range s.Subsections {
m := &Submodule{}
m.unmarshal(sub)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why, I don't understand the coding standards we use in the project, I'm just trying to make your code look like the rest of the code in the project. We usually only insert empty lines before a return or to separate "code paragraphs".

options.go Outdated
@@ -9,9 +9,18 @@ import (
"srcd.works/go-git.v4/plumbing/transport"
)

// SubmoduleRescursivity defines how depth will affect any submodule recursive
// operation
type SubmoduleRescursivity int
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative name: SubmoduleRecursionDepth, explain what negative values will do and how to make it infinite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just change the time to uint, infinity by itself is not allowed, a number should be given always, even if is very high

options.go Outdated
const (
// DefaultRemoteName name of the default Remote, just like git command
DefaultRemoteName = "origin"

// NoRecursivity disables the recursion for a submodule operation
NoRecursivity SubmoduleRescursivity = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative: NonRecursive.

options.go Outdated
// NoRecursivity disables the recursion for a submodule operation
NoRecursivity SubmoduleRescursivity = 0
// DefaultRecursivity allow recursion in a submodule operation
DefaultRecursivity SubmoduleRescursivity = 10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternative: DefaultSubmoduleRecursionDepth.

options.go Outdated
@@ -71,6 +80,9 @@ type PullOptions struct {
Depth int
// Auth credentials, if required, to use with the remote repository
Auth transport.AuthMethod
// RecurseSubmodules controls if new commits of all populated submodules
// should be fetched too
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop.

options.go Outdated
@@ -152,3 +164,16 @@ func (o *PushOptions) Validate() error {

return nil
}

// SubmoduleUpdateOptions describes how a submodule update should be performed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add full stop.

options.go Outdated

// SubmoduleUpdateOptions describes how a submodule update should be performed
type SubmoduleUpdateOptions struct {
// Init initializes the submodules recorded in the index
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add full stop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs much better explanation. It is a bool, but does not make sense with the comment.

@@ -18,6 +18,9 @@ type Storer interface {
ModuleStorer
}

// ModuleStorer allows interact with the modules' Storers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop.

Rewrite the sentence a little bit, it is hard to understand, suggestion:

Types that keep track of the relation between submodule names and their Storers implement this interface.

type ModuleStorer interface {
// Module returns a Storer reprensting a submodule, if not exists returns a
// new empty Storer is returned
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop.

Rewrite the sentence, it is hard to understand, suggestion:

Module returns the Storer of a submodule given its name.  If the name is unknown
to the ModuleStorer, it will create a new empty Storer and associate it to the name.

config/config.go Outdated
Worktree string
}
// Remotes 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop.

config/config.go Outdated
Remotes map[string]*RemoteConfig
// Submodules list of repository submodules
// Submodules list of repository submodules, the key of the map is the name
// of the submodule, should equal to Submodule.Name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End the sentence with a full stop.

@@ -81,7 +81,7 @@ func (iter *FileIter) Next() (*File, error) {
return nil, err
}

if entry.Mode.IsDir() {
if entry.Mode.IsDir() || entry.Mode == SubmoduleMode {
Copy link
Contributor

@alcortesm alcortesm Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, this is how it works:

The FileIter.Next method returns Files by going over all the elements in the tree, ignoring Dirs, and now (added in this PR) Submodules.

This is what I propose, instead:

The FileIter.Next method returns Files by going over all the elements in the tree, ignoring everything that is not a file (without naming explicitly what are not files).

This way we don't have to change the code everytime we support a new TreeEntry mode.

So instead of:

if entry.Mode.IsDir() || entry.Mode == SubmoduleMode {
    continue
}

I suggest:

if !entry.Mode.IsFile() && !entry.Mode.IsFileDeprecatedGroupWriteable() {
    continue
}

for _, sub := range s.Subsections {
m := &Submodule{}
m.unmarshal(sub)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why, I don't understand the coding standards we use in the project, I'm just trying to make your code look like the rest of the code in the project. We usually only insert empty lines before a return or to separate "code paragraphs".

Copy link
Contributor

@alcortesm alcortesm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice fixes, I agree with most of them, still some small issues pending, though.

@mcuadros mcuadros merged commit 867b106 into src-d:master Feb 21, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants