diff options
42 files changed, 1092 insertions, 2031 deletions
diff --git a/_examples/clone/main.go b/_examples/clone/main.go index 7d173a6..bcdb6a9 100644 --- a/_examples/clone/main.go +++ b/_examples/clone/main.go @@ -14,11 +14,11 @@ func main() { directory := os.Args[2] // Clone the given repository to the given directory - Info("git clone %s %s", url, directory) + Info("git clone %s %s --recursive", url, directory) r, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: url, - Depth: 1, + URL: url, + RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, }) CheckIfError(err) @@ -1,23 +1,8 @@ package git -import ( - "strings" +import "strings" - "srcd.works/go-git.v4/config" - "srcd.works/go-git.v4/plumbing/storer" -) - -// Storer is a generic storage of objects, references and any information -// related to a particular repository. The package srcd.works/go-git.v4/storage -// contains two implementation a filesystem base implementation (such as `.git`) -// and a memory implementations being ephemeral -type Storer interface { - storer.EncodedObjectStorer - storer.ReferenceStorer - storer.ShallowStorer - storer.IndexStorer - config.ConfigStorer -} +const defaultDotGitPath = ".git" // countLines returns the number of lines in a string à la git, this is // The newline character is assumed to be '\n'. The empty string diff --git a/config/config.go b/config/config.go index 866ae8e..259ebf9 100644 --- a/config/config.go +++ b/config/config.go @@ -32,14 +32,19 @@ var ( // Config contains the repository configuration // ftp://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES type Config struct { - // Core variables Core struct { // IsBare if true this repository is assumed to be bare and has no - // working directory associated with it + // working directory associated with it. IsBare bool + // Worktree is the path to the root of the working tree. + Worktree string } - // Remote 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. Remotes map[string]*RemoteConfig + // Submodules list of repository submodules, the key of the map is the name + // of the submodule, should equal to Submodule.Name. + Submodules map[string]*Submodule // contains the raw information of a config file, the main goal is preserve // the parsed information from the original format, to avoid missing @@ -47,15 +52,16 @@ type Config struct { raw *format.Config } -// NewConfig returns a new empty Config +// NewConfig returns a new empty Config. func NewConfig() *Config { return &Config{ - Remotes: make(map[string]*RemoteConfig, 0), - raw: format.New(), + Remotes: make(map[string]*RemoteConfig, 0), + Submodules: make(map[string]*Submodule, 0), + raw: format.New(), } } -// Validate validates the fields and sets the default values +// Validate validates the fields and sets the default values. func (c *Config) Validate() error { for name, r := range c.Remotes { if r.Name != name { @@ -71,14 +77,16 @@ func (c *Config) Validate() error { } const ( - remoteSection = "remote" - coreSection = "core" - fetchKey = "fetch" - urlKey = "url" - bareKey = "bare" + remoteSection = "remote" + submoduleSection = "submodule" + coreSection = "core" + fetchKey = "fetch" + urlKey = "url" + bareKey = "bare" + worktreeKey = "worktree" ) -// Unmarshal parses a git-config file and stores it +// Unmarshal parses a git-config file and stores it. func (c *Config) Unmarshal(b []byte) error { r := bytes.NewBuffer(b) d := format.NewDecoder(r) @@ -89,6 +97,7 @@ func (c *Config) Unmarshal(b []byte) error { } c.unmarshalCore() + c.unmarshalSubmodules() return c.unmarshalRemotes() } @@ -97,6 +106,8 @@ func (c *Config) unmarshalCore() { if s.Options.Get(bareKey) == "true" { c.Core.IsBare = true } + + c.Core.Worktree = s.Options.Get(worktreeKey) } func (c *Config) unmarshalRemotes() error { @@ -113,10 +124,21 @@ func (c *Config) unmarshalRemotes() error { return nil } -// Marshal returns Config encoded as a git-config file +func (c *Config) unmarshalSubmodules() { + s := c.raw.Section(submoduleSection) + for _, sub := range s.Subsections { + m := &Submodule{} + m.unmarshal(sub) + + c.Submodules[m.Name] = m + } +} + +// Marshal returns Config encoded as a git-config file. func (c *Config) Marshal() ([]byte, error) { c.marshalCore() c.marshalRemotes() + c.marshalSubmodules() buf := bytes.NewBuffer(nil) if err := format.NewEncoder(buf).Encode(c.raw); err != nil { @@ -129,6 +151,10 @@ func (c *Config) Marshal() ([]byte, error) { func (c *Config) marshalCore() { s := c.raw.Section(coreSection) s.SetOption(bareKey, fmt.Sprintf("%t", c.Core.IsBare)) + + if c.Core.Worktree != "" { + s.SetOption(worktreeKey, c.Core.Worktree) + } } func (c *Config) marshalRemotes() { @@ -142,7 +168,22 @@ func (c *Config) marshalRemotes() { } } -// RemoteConfig contains the configuration for a given remote repository +func (c *Config) marshalSubmodules() { + s := c.raw.Section(submoduleSection) + s.Subsections = make(format.Subsections, len(c.Submodules)) + + var i int + for _, r := range c.Submodules { + section := r.marshal() + // the submodule section at config is a subset of the .gitmodule file + // we should remove the non-valid options for the config file. + section.RemoveOption(pathKey) + s.Subsections[i] = section + i++ + } +} + +// RemoteConfig contains the configuration for a given remote repository. type RemoteConfig struct { // Name of the remote Name string @@ -156,7 +197,7 @@ type RemoteConfig struct { raw *format.Subsection } -// Validate validates the fields and sets the default values +// Validate validates the fields and sets the default values. func (c *RemoteConfig) Validate() error { if c.Name == "" { return ErrRemoteConfigEmptyName diff --git a/config/config_test.go b/config/config_test.go index 2bcefe4..cfab36d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -9,9 +9,14 @@ var _ = Suite(&ConfigSuite{}) func (s *ConfigSuite) TestUnmarshall(c *C) { input := []byte(`[core] bare = true + worktree = foo [remote "origin"] url = git@github.com:mcuadros/go-git.git fetch = +refs/heads/*:refs/remotes/origin/* +[submodule "qux"] + path = qux + url = https://github.com/foo/qux.git + branch = bar [branch "master"] remote = origin merge = refs/heads/master @@ -22,15 +27,51 @@ func (s *ConfigSuite) TestUnmarshall(c *C) { c.Assert(err, IsNil) c.Assert(cfg.Core.IsBare, Equals, true) + c.Assert(cfg.Core.Worktree, Equals, "foo") c.Assert(cfg.Remotes, HasLen, 1) c.Assert(cfg.Remotes["origin"].Name, Equals, "origin") c.Assert(cfg.Remotes["origin"].URL, Equals, "git@github.com:mcuadros/go-git.git") c.Assert(cfg.Remotes["origin"].Fetch, DeepEquals, []RefSpec{"+refs/heads/*:refs/remotes/origin/*"}) + c.Assert(cfg.Submodules, HasLen, 1) + c.Assert(cfg.Submodules["qux"].Name, Equals, "qux") + c.Assert(cfg.Submodules["qux"].URL, Equals, "https://github.com/foo/qux.git") + c.Assert(cfg.Submodules["qux"].Branch, Equals, "bar") + +} + +func (s *ConfigSuite) TestMarshall(c *C) { + output := []byte(`[core] + bare = true + worktree = bar +[remote "origin"] + url = git@github.com:mcuadros/go-git.git +[submodule "qux"] + url = https://github.com/foo/qux.git +`) + + cfg := NewConfig() + cfg.Core.IsBare = true + cfg.Core.Worktree = "bar" + cfg.Remotes["origin"] = &RemoteConfig{ + Name: "origin", + URL: "git@github.com:mcuadros/go-git.git", + } + + cfg.Submodules["qux"] = &Submodule{ + Name: "qux", + URL: "https://github.com/foo/qux.git", + } + + b, err := cfg.Marshal() + c.Assert(err, IsNil) + + c.Assert(string(b), Equals, string(output)) } func (s *ConfigSuite) TestUnmarshallMarshall(c *C) { input := []byte(`[core] bare = true + worktree = foo custom = ignored [remote "origin"] url = git@github.com:mcuadros/go-git.git diff --git a/config/modules.go b/config/modules.go index 4d98b16..3d01117 100644 --- a/config/modules.go +++ b/config/modules.go @@ -15,7 +15,7 @@ var ( // Modules defines the submodules properties, represents a .gitmodules file // https://www.kernel.org/pub/software/scm/git/docs/gitmodules.html type Modules struct { - // Submodules is a map of submodules being the key the name of the submodule + // Submodules is a map of submodules being the key the name of the submodule. Submodules map[string]*Submodule raw *format.Config @@ -30,12 +30,11 @@ func NewModules() *Modules { } const ( - submoduleSection = "submodule" - pathKey = "path" - branchKey = "branch" + pathKey = "path" + branchKey = "branch" ) -// Unmarshal parses a git-config file and stores it +// Unmarshal parses a git-config file and stores it. func (m *Modules) Unmarshal(b []byte) error { r := bytes.NewBuffer(b) d := format.NewDecoder(r) @@ -56,7 +55,7 @@ func (m *Modules) Unmarshal(b []byte) error { return nil } -// Marshal returns Modules encoded as a git-config file +// Marshal returns Modules encoded as a git-config file. func (m *Modules) Marshal() ([]byte, error) { s := m.raw.Section(submoduleSection) s.Subsections = make(format.Subsections, len(m.Submodules)) @@ -75,12 +74,12 @@ func (m *Modules) Marshal() ([]byte, error) { return buf.Bytes(), nil } -// Submodule defines a submodule +// Submodule defines a submodule. type Submodule struct { // Name module name Name string // Path defines the path, relative to the top-level directory of the Git - // working tree, + // working tree. Path string // URL defines a URL from which the submodule repository can be cloned. URL string @@ -89,11 +88,11 @@ type Submodule struct { Branch string // raw representation of the subsection, filled by marshal or unmarshal are - // called + // called. raw *format.Subsection } -// Validate validates the fields and sets the default values +// Validate validates the fields and sets the default values. func (m *Submodule) Validate() error { if m.Path == "" { return ErrModuleEmptyPath diff --git a/config/refspec.go b/config/refspec.go index dd68edc..9441df8 100644 --- a/config/refspec.go +++ b/config/refspec.go @@ -49,7 +49,7 @@ func (s RefSpec) Validate() error { return ErrRefSpecMalformedWildcard } -// IsForceUpdate returns if update is allowed in non fast-forward merges +// IsForceUpdate returns if update is allowed in non fast-forward merges. func (s RefSpec) IsForceUpdate() bool { if s[0] == refSpecForce[0] { return true @@ -67,7 +67,7 @@ func (s RefSpec) IsDelete() bool { return false } -// Src return the src side +// Src return the src side. func (s RefSpec) Src() string { spec := string(s) start := strings.Index(spec, refSpecForce) + 1 @@ -76,7 +76,7 @@ func (s RefSpec) Src() string { return spec[start:end] } -// Match match the given plumbing.ReferenceName against the source +// Match match the given plumbing.ReferenceName against the source. func (s RefSpec) Match(n plumbing.ReferenceName) bool { if !s.IsWildcard() { return s.matchExact(n) @@ -85,7 +85,7 @@ func (s RefSpec) Match(n plumbing.ReferenceName) bool { return s.matchGlob(n) } -// IsWildcard returns true if the RefSpec contains a wildcard +// IsWildcard returns true if the RefSpec contains a wildcard. func (s RefSpec) IsWildcard() bool { return strings.Index(string(s), refSpecWildcard) != -1 } @@ -110,7 +110,7 @@ func (s RefSpec) matchGlob(n plumbing.ReferenceName) bool { strings.HasSuffix(name, suffix) } -// Dst returns the destination for the given remote reference +// Dst returns the destination for the given remote reference. func (s RefSpec) Dst(n plumbing.ReferenceName) plumbing.ReferenceName { spec := string(s) start := strings.Index(spec, refSpecSeparator) + 1 @@ -133,7 +133,7 @@ func (s RefSpec) String() string { return string(s) } -// MatchAny returns true if any of the RefSpec match with the given ReferenceName +// MatchAny returns true if any of the RefSpec match with the given ReferenceName. func MatchAny(l []RefSpec, n plumbing.ReferenceName) bool { for _, r := range l { if r.Match(n) { diff --git a/cshared/README.md b/cshared/README.md deleted file mode 100644 index ea32b4b..0000000 --- a/cshared/README.md +++ /dev/null @@ -1,79 +0,0 @@ -cshared -======= - -Building --------- -go 1.6+ -``` -go build -o libgogit.so -buildmode=c-shared github.com/src-d/go-git/cshared -``` -Two files must appear: libgogit.h and libgogit.so. The second must be -a shared library, not an ar archive (may happen when something goes wrong). -Check the exported symbols with `nm -g`. - -How it works ------------- - -Nearly every public Go function is mirrored in the corresponding *_cshared.go -file. struct fields are also mirrored with getters and setters. The functions -are marked with `//export ...` "magic" cgo comment so that they appear -in defined symbols of a shared library built with `-buildmode=c-shared`. - -Go pointers may not be passed out of cgo functions, so we maintain the -two-way registry of all active Go objects mapped to `Handle`-s (`uint64`). -Every time we need to return a reference to Go object outside, we call -`RegisterObject(interface{})` which returns a new `Handle` or reuses -an existing one if the object has already been registered. Then we -return the obtained `Handle`. When we need to receive a Go object reference -in cgo function parameters, we accept `uint64` and retrieve the `interface{}` -with `GetObject(Handle)` which can be casted to the underlying type with a -type assertion. When the object is no longer needed, we invoke -`UnregisterObject(Handle)`. - -Although `interface{]` is just two `uintptr`-s inside, it is not a hashable -type and we cannot use it a as key in our backward registry mapping. -We are using the data `uintptr` as the key there. Since several distinct -objects may exist with the same data pointer (e.g. struct and first field -of the struct), the value of that mapping is a slice of `Handle`-s. - -All the mentioned service functions are goroutine- and threadsafe. - -`std_cshared.go` contains the cgo wrappers for standard library objects. - -Debugging ---------- -`c_dump_object()` prints the current state of the two-way object registry -to stdout. `c_set_trace()` activates echoing of `RegisterObject()` and -`UnregisterObject()` invocations. - -Caveats -------- -Normally, we pass over a pointer to object as `interface{}` into `RegisterObject()` -so that it can be mutated later. It requires the corresponding -pointer-to-type type assertion in cgo functions. If you mess with this, -the cgo function will, of course, panic. - -A cgo function is allowed to take Go's `string` parameters. `string`'s -data must point to some memory and cgo does not copy the incoming foreign -memory into Go memory automatically. What's worse, `string`-s are immutable -and when you copy it, the copy points to the same memory. This means that -if you pass in a `string` which was constructed using `malloc()`, for example, -and later `free()` it, all Go strings created from the function parameter -will point to the invalid memory. Actually, this allowance violates the -cgo pointer passing rules stated just several blocks of texts -below the example of string parameters - this is crazy, but we have to live -with this, as usual in Go world. So, *all incoming `string`-s must be immediately -safely copied with `CopyString()` once they are used*. - -Returning strings and byte slices is also funny: you have to use `C.CString` -> `*C.char` -and additionally return the length as another result tuple member if needed. -`C.CString` copies the memory pointed by `string` to a `malloc()`-ed region -and it is the responsibility of the other side to `free()` it or it will leak -otherwise. - -Another tricky part is in `c_std_map_get_str_str` and similar places -where you need to return `*C.char` from an unaddressable array accessed under -a pseudonym type through reflection. The only way I've found working -is using `reflect.Copy` to byte slice (copy), then `CBytes` (copy) and -finally another (copy) on the receiving side because the latter must be -`free()`-d.
\ No newline at end of file diff --git a/cshared/auth_method_cshared.go b/cshared/auth_method_cshared.go deleted file mode 100644 index 0f0e3a1..0000000 --- a/cshared/auth_method_cshared.go +++ /dev/null @@ -1,192 +0,0 @@ -// +build ignore -package main - -import ( - "C" - "strings" - - "golang.org/x/crypto/ssh" - "srcd.works/go-git.v4/plumbing/transport/http" - gssh "srcd.works/go-git.v4/plumbing/transport/ssh" -) - -//export c_NewBasicAuth -func c_NewBasicAuth(username, password string) uint64 { - auth := http.NewBasicAuth(CopyString(username), CopyString(password)) - return uint64(RegisterObject(auth)) -} - -//export c_ParseRawPrivateKey -func c_ParseRawPrivateKey(pemBytes []byte) (uint64, int, *C.char) { - pkey, err := ssh.ParseRawPrivateKey(pemBytes) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - // pointer is received - no need for & - return uint64(RegisterObject(pkey)), ErrorCodeSuccess, nil -} - -//export c_ParsePrivateKey -func c_ParsePrivateKey(pemBytes []byte) (uint64, int, *C.char) { - signer, err := ssh.ParsePrivateKey(pemBytes) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&signer)), ErrorCodeSuccess, nil -} - -//export c_NewPublicKey -func c_NewPublicKey(key uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(key)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - key_obj := obj.(ssh.PublicKey) - pkey, err := ssh.NewPublicKey(key_obj) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&pkey)), ErrorCodeSuccess, nil -} - -//export c_NewSignerFromKey -func c_NewSignerFromKey(key uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(key)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - signer, err := ssh.NewSignerFromKey(obj) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&signer)), ErrorCodeSuccess, nil -} - -//export c_MarshalAuthorizedKey -func c_MarshalAuthorizedKey(key uint64) (*C.char, int) { - obj, ok := GetObject(Handle(key)) - if !ok { - return nil, 0 - } - obj_key := obj.(ssh.PublicKey) - mak := ssh.MarshalAuthorizedKey(obj_key) - return C.CString(string(mak)), len(mak) -} - -//export c_ParsePublicKey -func c_ParsePublicKey(in []byte) (uint64, int, *C.char) { - pkey, err := ssh.ParsePublicKey(in) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&pkey)), ErrorCodeSuccess, nil -} - -//export c_ParseAuthorizedKey -func c_ParseAuthorizedKey(in []byte) (uint64, *C.char, *C.char, *C.char, int, int, *C.char) { - pkey, comment, options, rest, err := ssh.ParseAuthorizedKey(in) - if err != nil { - return IH, nil, nil, nil, 0, ErrorCodeInternal, - C.CString(err.Error()) - } - pkey_handle := RegisterObject(&pkey) - mopt := strings.Join(options, "\xff") - return uint64(pkey_handle), C.CString(comment), C.CString(mopt), - C.CString(string(rest)), len(rest), ErrorCodeSuccess, nil -} - -//export c_ssh_Password_New -func c_ssh_Password_New(user, pass string) uint64 { - obj := gssh.Password{User: CopyString(user), Pass: CopyString(pass)} - return uint64(RegisterObject(&obj)) -} - -//export c_ssh_Password_get_User -func c_ssh_Password_get_User(p uint64) *C.char { - obj, ok := GetObject(Handle(p)) - if !ok { - return nil - } - return C.CString(obj.(*gssh.Password).User) -} - -//export c_ssh_Password_set_User -func c_ssh_Password_set_User(p uint64, v string) { - obj, ok := GetObject(Handle(p)) - if !ok { - return - } - obj.(*gssh.Password).User = CopyString(v) -} - -//export c_ssh_Password_get_Pass -func c_ssh_Password_get_Pass(p uint64) *C.char { - obj, ok := GetObject(Handle(p)) - if !ok { - return nil - } - return C.CString(obj.(*gssh.Password).Pass) -} - -//export c_ssh_Password_set_Pass -func c_ssh_Password_set_Pass(p uint64, v string) { - obj, ok := GetObject(Handle(p)) - if !ok { - return - } - obj.(*gssh.Password).Pass = CopyString(v) -} - -//c_ssh_PublicKeys_New -func c_ssh_PublicKeys_New(user string, signer uint64) uint64 { - obj, ok := GetObject(Handle(signer)) - if !ok { - return IH - } - pk := gssh.PublicKeys{User: CopyString(user), Signer: obj.(ssh.Signer)} - return uint64(RegisterObject(&pk)) -} - -//export c_ssh_PublicKeys_get_User -func c_ssh_PublicKeys_get_User(p uint64) *C.char { - obj, ok := GetObject(Handle(p)) - if !ok { - return nil - } - return C.CString(obj.(*gssh.PublicKeys).User) -} - -//export c_ssh_PublicKeys_set_User -func c_ssh_PublicKeys_set_User(p uint64, v string) { - obj, ok := GetObject(Handle(p)) - if !ok { - return - } - obj.(*gssh.PublicKeys).User = CopyString(v) -} - -//export c_ssh_PublicKeys_get_Signer -func c_ssh_PublicKeys_get_Signer(p uint64) uint64 { - obj, ok := GetObject(Handle(p)) - if !ok { - return IH - } - handle, ok := GetHandle(&obj.(*gssh.PublicKeys).Signer) - if !ok { - return IH - } - return uint64(handle) -} - -//export c_ssh_PublicKeys_set_Signer -func c_ssh_PublicKeys_set_Signer(p uint64, v uint64) { - obj, ok := GetObject(Handle(p)) - if !ok { - return - } - signer, ok := GetObject(Handle(v)) - if !ok { - return - } - obj.(*gssh.PublicKeys).Signer = *signer.(*ssh.Signer) -} diff --git a/cshared/blame_cshared.go b/cshared/blame_cshared.go deleted file mode 100644 index 8dfee31..0000000 --- a/cshared/blame_cshared.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build ignore -package main - -import ( - "C" - - "srcd.works/go-git.v4" -) - -//export c_Blame_get_Path -func c_Blame_get_Path(b uint64) *C.char { - obj, ok := GetObject(Handle(b)) - if !ok { - return nil - } - blame := obj.(*git.BlameResult) - return C.CString(blame.Path) -} - -//export c_Blame_get_Rev -func c_Blame_get_Rev(b uint64) *C.char { - obj, ok := GetObject(Handle(b)) - if !ok { - return nil - } - blame := obj.(*git.BlameResult) - return CBytes(blame.Rev[:]) -} - -//export c_Blame_get_Lines_len -func c_Blame_get_Lines_len(b uint64) int { - obj, ok := GetObject(Handle(b)) - if !ok { - return 0 - } - blame := obj.(*git.BlameResult) - return len(blame.Lines) -} - -//export c_Blame_get_Lines_item -func c_Blame_get_Lines_item(b uint64, i int) { - obj, ok := GetObject(Handle(b)) - if !ok { - return - } - blame := obj.(*git.BlameResult) - line := blame.Lines[i] - _ = line -} diff --git a/cshared/commit_cshared.go b/cshared/commit_cshared.go deleted file mode 100644 index 78f9e4a..0000000 --- a/cshared/commit_cshared.go +++ /dev/null @@ -1,223 +0,0 @@ -// +build ignore -package main - -import ( - "C" - "io" - "reflect" - "unsafe" - - "srcd.works/go-git.v4" - "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" - "srcd.works/go-git.v4/plumbing/storer" -) - -//export c_Commit_get_Hash -func c_Commit_get_Hash(c uint64) *C.char { - obj, ok := GetObject(Handle(c)) - if !ok { - return nil - } - commit := obj.(*object.Commit) - return CBytes(commit.Hash[:]) -} - -//export c_Commit_get_Author -func c_Commit_get_Author(c uint64) uint64 { - obj, ok := GetObject(Handle(c)) - if !ok { - return IH - } - commit := obj.(*object.Commit) - author := &commit.Author - author_handle := RegisterObject(author) - return uint64(author_handle) -} - -//export c_Commit_get_Committer -func c_Commit_get_Committer(c uint64) uint64 { - obj, ok := GetObject(Handle(c)) - if !ok { - return IH - } - commit := obj.(*object.Commit) - committer := &commit.Committer - committer_handle := RegisterObject(committer) - return uint64(committer_handle) -} - -//export c_Commit_get_Message -func c_Commit_get_Message(c uint64) *C.char { - obj, ok := GetObject(Handle(c)) - if !ok { - return nil - } - commit := obj.(*object.Commit) - return C.CString(commit.Message) -} - -//export c_Commit_Tree -func c_Commit_Tree(c uint64) uint64 { - obj, ok := GetObject(Handle(c)) - if !ok { - return IH - } - commit := obj.(*object.Commit) - tree, err := commit.Tree() - if err != nil { - return IH - } - - tree_handle := RegisterObject(tree) - return uint64(tree_handle) -} - -//export c_Commit_Parents -func c_Commit_Parents(c uint64) uint64 { - obj, ok := GetObject(Handle(c)) - if !ok { - return IH - } - commit := obj.(*object.Commit) - parents := commit.Parents() - parents_handle := RegisterObject(parents) - return uint64(parents_handle) -} - -//export c_Commit_NumParents -func c_Commit_NumParents(c uint64) int { - obj, ok := GetObject(Handle(c)) - if !ok { - return -1 - } - commit := obj.(*object.Commit) - return commit.NumParents() -} - -//export c_Commit_File -func c_Commit_File(c uint64, path string) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(c)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - commit := obj.(*object.Commit) - file, err := commit.File(CopyString(path)) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - file_handle := RegisterObject(file) - return uint64(file_handle), ErrorCodeSuccess, nil -} - -//export c_Commit_ID -func c_Commit_ID(c uint64) *C.char { - return c_Commit_get_Hash(c) -} - -//export c_Commit_Type -func c_Commit_Type(c uint64) int8 { - obj, ok := GetObject(Handle(c)) - if !ok { - return -1 - } - commit := obj.(*object.Commit) - return int8(commit.Type()) -} - -//export c_Commit_Decode -func c_Commit_Decode(o uint64) (uint64, int, *C.char) { - commit := object.Commit{} - obj, ok := GetObject(Handle(o)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - cobj := obj.(*plumbing.EncodedObject) - err := commit.Decode(*cobj) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&commit)), ErrorCodeSuccess, nil -} - -//export c_Commit_String -func c_Commit_String(c uint64) *C.char { - obj, ok := GetObject(Handle(c)) - if !ok { - return nil - } - commit := obj.(*object.Commit) - return C.CString(commit.String()) -} - -//export c_Commit_References -func c_Commit_References(c uint64, path string) (*C.char, int, int, *C.char) { - obj, ok := GetObject(Handle(c)) - if !ok { - return nil, 0, ErrorCodeNotFound, C.CString(MessageNotFound) - } - commit := obj.(*object.Commit) - refs, err := git.References(commit, CopyString(path)) - if err != nil { - return nil, 0, ErrorCodeInternal, C.CString(err.Error()) - } - handles := make([]uint64, len(refs)) - for i, c := range refs { - handles[i] = uint64(RegisterObject(c)) - } - size := 8 * len(handles) - dest := C.malloc(C.size_t(size)) - header := (*reflect.SliceHeader)(unsafe.Pointer(&handles)) - header.Len *= 8 - copy((*[1 << 30]byte)(dest)[:], *(*[]byte)(unsafe.Pointer(header))) - return (*C.char)(dest), size / 8, ErrorCodeSuccess, nil -} - -//export c_Commit_Blame -func c_Commit_Blame(c uint64, path string) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(c)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - commit := obj.(*object.Commit) - blame, err := git.Blame(commit, CopyString(path)) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(blame)), ErrorCodeSuccess, nil -} - -//export c_NewCommitIter -func c_NewCommitIter(r uint64, iter uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - s := obj.(storer.EncodedObjectStorer) - obj, ok = GetObject(Handle(iter)) - if !ok { - return IH - } - obj_iter := obj.(storer.EncodedObjectIter) - commit_iter := object.NewCommitIter(s, obj_iter) - handle := RegisterObject(commit_iter) - return uint64(handle) -} - -//export c_CommitIter_Next -func c_CommitIter_Next(iter uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(iter)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - commitIter := obj.(*object.CommitIter) - commit, err := commitIter.Next() - if err != nil { - if err == io.EOF { - return IH, ErrorCodeSuccess, nil - } - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - handle := RegisterObject(commit) - return uint64(handle), ErrorCodeSuccess, nil -} diff --git a/cshared/file_cshared.go b/cshared/file_cshared.go deleted file mode 100644 index bace1f5..0000000 --- a/cshared/file_cshared.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "C" - "io" - "io/ioutil" - - "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" - "srcd.works/go-git.v4/plumbing/storer" -) - -//export c_File_get_Name -func c_File_get_Name(f uint64) *C.char { - obj, ok := GetObject(Handle(f)) - if !ok { - return nil - } - file := obj.(*object.File) - return C.CString(file.Name) -} - -//export c_File_get_Mode -func c_File_get_Mode(f uint64) uint32 { - obj, ok := GetObject(Handle(f)) - if !ok { - return 0 - } - file := obj.(*object.File) - return uint32(file.Mode) -} - -//export c_File_get_Hash -func c_File_get_Hash(b uint64) *C.char { - obj, ok := GetObject(Handle(b)) - if !ok { - return nil - } - file := obj.(*object.File) - return CBytes(file.Hash[:]) -} - -//export c_File_Size -func c_File_Size(b uint64) int64 { - obj, ok := GetObject(Handle(b)) - if !ok { - return -1 - } - file := obj.(*object.File) - return file.Size -} - -//export c_File_Decode -func c_File_Decode(o uint64) uint64 { - obj, ok := GetObject(Handle(o)) - if !ok { - return IH - } - cobj := obj.(*plumbing.EncodedObject) - file := object.File{} - file.Decode(*cobj) - return uint64(RegisterObject(&file)) -} - -//export c_File_Read -func c_File_Read(b uint64) (int, *C.char) { - obj, ok := GetObject(Handle(b)) - if !ok { - return ErrorCodeNotFound, C.CString(MessageNotFound) - } - file := obj.(*object.File) - reader, err := file.Reader() - if err != nil { - return ErrorCodeInternal, C.CString(err.Error()) - } - data, err := ioutil.ReadAll(reader) - reader.Close() - if err != nil { - return ErrorCodeInternal, C.CString(err.Error()) - } - return len(data), CBytes(data) -} - -//export c_File_Type -func c_File_Type(c uint64) int8 { - obj, ok := GetObject(Handle(c)) - if !ok { - return -1 - } - file := obj.(*object.File) - return int8(file.Type()) -} - -//export c_NewFileIter -func c_NewFileIter(r uint64, t uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - storer := obj.(storer.EncodedObjectStorer) - obj, ok = GetObject(Handle(t)) - if !ok { - return IH - } - tree := obj.(*object.Tree) - iter := object.NewFileIter(storer, tree) - return uint64(RegisterObject(iter)) -} - -//export c_FileIter_Next -func c_FileIter_Next(i uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(i)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - iter := obj.(*object.FileIter) - file, err := iter.Next() - if err != nil { - if err == io.EOF { - return IH, ErrorCodeSuccess, nil - } - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(file)), ErrorCodeSuccess, nil -} - -//export c_FileIter_Close -func c_FileIter_Close(i uint64) { - obj, ok := GetObject(Handle(i)) - if !ok { - return - } - iter := obj.(*object.FileIter) - iter.Close() -} diff --git a/cshared/objects.go b/cshared/objects.go deleted file mode 100644 index d83f224..0000000 --- a/cshared/objects.go +++ /dev/null @@ -1,190 +0,0 @@ -// +build ignore -package main - -import ( - "C" - "fmt" - "math" - "reflect" - "sync" -) - -type Handle uint64 - -const ( - ErrorCodeSuccess = iota - ErrorCodeNotFound = -iota - ErrorCodeInternal = -iota -) - -const MessageNotFound string = "object not found" -const InvalidHandle Handle = 0 -const IH uint64 = uint64(InvalidHandle) - -var counter Handle = InvalidHandle -var opMutex sync.Mutex -var registryHandle2Obj map[Handle]interface{} = map[Handle]interface{}{} -var registryObj2Handle map[uintptr][]Handle = map[uintptr][]Handle{} -var trace bool = false - -func getNewHandle() Handle { - counter++ - if counter == math.MaxUint64 { - panic("Handle cache is exhausted") - } - return counter -} - -func RegisterObject(obj interface{}) Handle { - data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1] - if trace { - fmt.Printf("RegisterObject 0x%x\t%v\n", data_ptr, obj) - } - opMutex.Lock() - defer opMutex.Unlock() - handles, ok := registryObj2Handle[data_ptr] - if ok { - for _, h := range handles { - other, ok := registryHandle2Obj[h] - if !ok { - panic("Inconsistent internal object mapping state (1)") - } - if other == obj { - if trace { - fmt.Printf("RegisterObject 0x%x reused %d\n", data_ptr, h) - } - return h - } - } - } - handle := getNewHandle() - registryHandle2Obj[handle] = obj - registryObj2Handle[data_ptr] = append(registryObj2Handle[data_ptr], handle) - if trace { - c_dump_objects() - } - return handle -} - -func UnregisterObject(handle Handle) int { - if trace { - fmt.Printf("UnregisterObject %d\n", handle) - } - if handle == InvalidHandle { - return ErrorCodeNotFound - } - opMutex.Lock() - defer opMutex.Unlock() - obj, ok := registryHandle2Obj[handle] - if !ok { - return ErrorCodeNotFound - } - delete(registryHandle2Obj, handle) - data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1] - other_handles, ok := registryObj2Handle[data_ptr] - if !ok { - panic(fmt.Sprintf("Inconsistent internal object mapping state (2): %d", - handle)) - } - hi := -1 - for i, h := range other_handles { - if h == handle { - hi = i - break - } - } - if hi < 0 { - panic(fmt.Sprintf("Inconsistent internal object mapping state (3): %d", - handle)) - } - if len(other_handles) == 1 { - delete(registryObj2Handle, data_ptr) - } else { - registryObj2Handle[data_ptr] = append(other_handles[:hi], other_handles[hi+1:]...) - } - if trace { - c_dump_objects() - } - return ErrorCodeSuccess -} - -func GetObject(handle Handle) (interface{}, bool) { - if handle == InvalidHandle { - return nil, false - } - opMutex.Lock() - defer opMutex.Unlock() - a, b := registryHandle2Obj[handle] - return a, b -} - -func GetHandle(obj interface{}) (Handle, bool) { - data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1] - opMutex.Lock() - defer opMutex.Unlock() - handles, ok := registryObj2Handle[data_ptr] - if !ok { - return InvalidHandle, false - } - for _, h := range handles { - candidate := registryHandle2Obj[h] - if candidate == obj { - return h, true - } - } - return InvalidHandle, false -} - -func CopyString(str string) string { - buf := make([]byte, len(str)) - copy(buf, []byte(str)) - return string(buf) -} - -// https://github.com/golang/go/issues/14838 -func CBytes(bytes []byte) *C.char { - ptr := C.malloc(C.size_t(len(bytes))) - copy((*[1 << 30]byte)(ptr)[:], bytes) - return (*C.char)(ptr) -} - -func SafeIsNil(v reflect.Value) bool { - defer func() { recover() }() - return v.IsNil() -} - -//export c_dispose -func c_dispose(handle uint64) { - UnregisterObject(Handle(handle)) -} - -//export c_objects_size -func c_objects_size() int { - return len(registryHandle2Obj) -} - -//export c_dump_objects -func c_dump_objects() { - fmt.Printf("handles (%d):\n", len(registryHandle2Obj)) - for h, obj := range registryHandle2Obj { - fmt.Printf("0x%x\t0x%x %v\n", h, - reflect.ValueOf(&obj).Elem().InterfaceData()[1], obj) - } - fmt.Println() - phs := 0 - for _, h := range registryObj2Handle { - phs += len(h) - } - fmt.Printf("pointers (%d):\n", phs) - for ptr, h := range registryObj2Handle { - fmt.Printf("0x%x\t%v\n", ptr, h) - } -} - -//export c_set_trace -func c_set_trace(val bool) { - trace = val -} - -// dummy main() is needed by the linker -func main() {} diff --git a/cshared/objects_cshared.go b/cshared/objects_cshared.go deleted file mode 100644 index 49ddeb7..0000000 --- a/cshared/objects_cshared.go +++ /dev/null @@ -1,109 +0,0 @@ -// +build ignore -package main - -import ( - "C" - "io/ioutil" - "time" - - "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" -) - -//export c_Signature_Name -func c_Signature_Name(s uint64) *C.char { - obj, ok := GetObject(Handle(s)) - if !ok { - return nil - } - sign := obj.(*object.Signature) - return C.CString(sign.Name) -} - -//export c_Signature_Email -func c_Signature_Email(s uint64) *C.char { - obj, ok := GetObject(Handle(s)) - if !ok { - return nil - } - sign := obj.(*object.Signature) - return C.CString(sign.Email) -} - -//export c_Signature_When -func c_Signature_When(s uint64) *C.char { - obj, ok := GetObject(Handle(s)) - if !ok { - return nil - } - sign := obj.(*object.Signature) - return C.CString(sign.When.Format(time.RFC3339)) -} - -//export c_Signature_Decode -func c_Signature_Decode(b []byte) uint64 { - sign := object.Signature{} - sign.Decode(b) - return uint64(RegisterObject(&sign)) -} - -//export c_Blob_get_Hash -func c_Blob_get_Hash(b uint64) *C.char { - obj, ok := GetObject(Handle(b)) - if !ok { - return nil - } - blob := obj.(*object.Blob) - return CBytes(blob.Hash[:]) -} - -//export c_Blob_Size -func c_Blob_Size(b uint64) int64 { - obj, ok := GetObject(Handle(b)) - if !ok { - return -1 - } - blob := obj.(*object.Blob) - return blob.Size -} - -//export c_Blob_Decode -func c_Blob_Decode(o uint64) uint64 { - obj, ok := GetObject(Handle(o)) - if !ok { - return IH - } - cobj := obj.(*plumbing.EncodedObject) - blob := object.Blob{} - blob.Decode(*cobj) - return uint64(RegisterObject(&blob)) -} - -//export c_Blob_Read -func c_Blob_Read(b uint64) (int, *C.char) { - obj, ok := GetObject(Handle(b)) - if !ok { - return ErrorCodeNotFound, C.CString(MessageNotFound) - } - blob := obj.(*object.Blob) - reader, err := blob.Reader() - if err != nil { - return ErrorCodeInternal, C.CString(err.Error()) - } - data, err := ioutil.ReadAll(reader) - reader.Close() - if err != nil { - return ErrorCodeInternal, C.CString(err.Error()) - } - return len(data), CBytes(data) -} - -//export c_Blob_Type -func c_Blob_Type(c uint64) int8 { - obj, ok := GetObject(Handle(c)) - if !ok { - return -1 - } - blob := obj.(*object.Blob) - return int8(blob.Type()) -} diff --git a/cshared/remote_cshared.go b/cshared/remote_cshared.go deleted file mode 100644 index e7da87c..0000000 --- a/cshared/remote_cshared.go +++ /dev/null @@ -1,191 +0,0 @@ -// +build ignore -package main - -import "C" - -/* - -//export c_Remote_get_Endpoint -func c_Remote_get_Endpoint(r uint64) *C.char { - obj, ok := GetObject(Handle(r)) - if !ok { - return nil - } - remote := obj.(*git.Remote) - return C.CString(string(remote.Endpoint)) -} - -//export c_Remote_set_Endpoint -func c_Remote_set_Endpoint(r uint64, value string) { - obj, ok := GetObject(Handle(r)) - if !ok { - return - } - remote := obj.(*git.Remote) - remote.Endpoint = common.Endpoint(CopyString(value)) -} - -//export c_Remote_get_Auth -func c_Remote_get_Auth(r uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - remote := obj.(*git.Remote) - return uint64(RegisterObject(&remote.Auth)) -} - -//export c_Remote_set_Auth -func c_Remote_set_Auth(r uint64, value uint64) { - obj, ok := GetObject(Handle(r)) - if !ok { - return - } - remote := obj.(*git.Remote) - obj, ok = GetObject(Handle(value)) - if !ok { - return - } - remote.Auth = *obj.(*common.AuthMethod) -} - -//export c_NewRemote -func c_NewRemote(url string) (uint64, int, *C.char) { - remote, err := git.NewRemote(CopyString(url)) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(remote)), ErrorCodeSuccess, nil -} - -//export c_NewAuthenticatedRemote -func c_NewAuthenticatedRemote(url string, auth uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(auth)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - auth_method := *obj.(*common.AuthMethod) - remote, err := git.NewAuthenticatedRemote(CopyString(url), auth_method) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(remote)), ErrorCodeSuccess, nil -} - -//export c_Remote_Connect -func c_Remote_Connect(r uint64) (int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return ErrorCodeNotFound, nil - } - remote := obj.(*git.Remote) - err := remote.Connect() - if err != nil { - return ErrorCodeInternal, C.CString(err.Error()) - } - return ErrorCodeSuccess, nil -} - -//export c_Remote_Info -func c_Remote_Info(r uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - remote := obj.(*git.Remote) - return uint64(RegisterObject(remote.Info())) -} - -//export c_Remote_Capabilities -func c_Remote_Capabilities(r uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - remote := obj.(*git.Remote) - return uint64(RegisterObject(remote.Capabilities())) -} - -//export c_Remote_DefaultBranch -func c_Remote_DefaultBranch(r uint64) *C.char { - obj, ok := GetObject(Handle(r)) - if !ok { - return nil - } - remote := obj.(*git.Remote) - return C.CString(remote.DefaultBranch()) -} - -//export c_Remote_Head -func c_Remote_Head(r uint64) (*C.char, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return nil, ErrorCodeNotFound, C.CString(MessageNotFound) - } - remote := obj.(*git.Remote) - hash, err := remote.Head() - if err != nil { - return nil, ErrorCodeInternal, C.CString(err.Error()) - } - return CBytes(hash[:]), ErrorCodeSuccess, nil -} - -//export c_Remote_Fetch -func c_Remote_Fetch(r uint64, req uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - remote := obj.(*git.Remote) - obj, ok = GetObject(Handle(req)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - request := obj.(*common.UploadPackRequest) - reader, err := remote.Fetch(request) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(reader)), ErrorCodeSuccess, nil -} - -//export c_Remote_FetchDefaultBranch -func c_Remote_FetchDefaultBranch(r uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - remote := obj.(*git.Remote) - reader, err := remote.FetchDefaultBranch() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(reader)), ErrorCodeSuccess, nil -} - -//export c_Remote_Ref -func c_Remote_Ref(r uint64, refName string) (*C.char, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return nil, ErrorCodeNotFound, C.CString(MessageNotFound) - } - remote := obj.(*git.Remote) - hash, err := remote.Ref(CopyString(refName)) - if err != nil { - return nil, ErrorCodeInternal, C.CString(err.Error()) - } - return CBytes(hash[:]), ErrorCodeSuccess, nil -} - -//export c_Remote_Refs -func c_Remote_Refs(r uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - remote := obj.(*git.Remote) - refs := remote.Refs() - return uint64(RegisterObject(refs)) -} - -*/ diff --git a/cshared/repository_cshared.go b/cshared/repository_cshared.go deleted file mode 100644 index 76755bf..0000000 --- a/cshared/repository_cshared.go +++ /dev/null @@ -1,232 +0,0 @@ -// +build ignore -package main - -import "C" - -/* - -//export c_Repository -func c_Repository() uint64 { - repo := &git.Repository{} - repo_handle := RegisterObject(repo) - return uint64(repo_handle) -} - -//export c_NewRepository -func c_NewRepository(url string, auth uint64) (uint64, int, *C.char) { - var repo *git.Repository - var err error - url = CopyString(url) - if auth != IH { - real_auth, ok := GetObject(Handle(auth)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo, err = git.NewRepository(url, real_auth.(common.AuthMethod)) - } else { - repo, err = git.NewRepository(url, nil) - } - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - repo_handle := RegisterObject(repo) - return uint64(repo_handle), ErrorCodeSuccess, nil -} - -//export c_NewPlainRepository -func c_NewPlainRepository() uint64 { - return uint64(RegisterObject(git.NewPlainRepository())) -} - -//export c_Repository_get_Remotes -func c_Repository_get_Remotes(r uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - repo := obj.(*git.Repository) - return uint64(RegisterObject(&repo.Remotes)) -} - -//export c_Repository_set_Remotes -func c_Repository_set_Remotes(r uint64, val uint64) { - obj, ok := GetObject(Handle(r)) - if !ok { - return - } - repo := obj.(*git.Repository) - obj, ok = GetObject(Handle(val)) - if !ok { - return - } - repo.Remotes = *obj.(*map[string]*git.Remote) -} - -//export c_Repository_get_Storage -func c_Repository_get_Storage(r uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - repo := obj.(*git.Repository) - return uint64(RegisterObject(&repo.Storage)) -} - -//export c_Repository_set_Storage -func c_Repository_set_Storage(r uint64, val uint64) { - obj, ok := GetObject(Handle(r)) - if !ok { - return - } - repo := obj.(*git.Repository) - obj, ok = GetObject(Handle(val)) - if !ok { - return - } - repo.Storage = *obj.(*plumbing.ObjectStorage) -} - -//export c_Repository_Pull -func c_Repository_Pull(r uint64, remoteName, branch string) (int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - err := repo.Pull(remoteName, CopyString(branch)) - if err == nil { - return ErrorCodeSuccess, nil - } - return ErrorCodeInternal, C.CString(err.Error()) -} - -//export c_Repository_PullDefault -func c_Repository_PullDefault(r uint64) (int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - err := repo.PullDefault() - if err == nil { - return ErrorCodeSuccess, nil - } - return ErrorCodeInternal, C.CString(err.Error()) -} - -//export c_Repository_Commit -func c_Repository_Commit(r uint64, h []byte) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - var hash plumbing.Hash - copy(hash[:], h) - commit, err := repo.Commit(hash) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - commit_handle := RegisterObject(commit) - return uint64(commit_handle), ErrorCodeSuccess, nil -} - -//export c_Repository_Commits -func c_Repository_Commits(r uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - iter, err := repo.Commits() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - iter_handle := RegisterObject(iter) - return uint64(iter_handle), ErrorCodeSuccess, nil -} - -//export c_Repository_Tree -func c_Repository_Tree(r uint64, h []byte) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - var hash plumbing.Hash - copy(hash[:], h) - tree, err := repo.Tree(hash) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - tree_handle := RegisterObject(tree) - return uint64(tree_handle), ErrorCodeSuccess, nil -} - -//export c_Repository_Blob -func c_Repository_Blob(r uint64, h []byte) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - var hash plumbing.Hash - copy(hash[:], h) - blob, err := repo.Blob(hash) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - blob_handle := RegisterObject(blob) - return uint64(blob_handle), ErrorCodeSuccess, nil -} - -//export c_Repository_Tag -func c_Repository_Tag(r uint64, h []byte) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - var hash plumbing.Hash - copy(hash[:], h) - tag, err := repo.Tag(hash) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - tag_handle := RegisterObject(tag) - return uint64(tag_handle), ErrorCodeSuccess, nil -} - -//export c_Repository_Tags -func c_Repository_Tags(r uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - iter, err := repo.Tags() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - iter_handle := RegisterObject(iter) - return uint64(iter_handle), ErrorCodeSuccess, nil -} - -//export c_Repository_Object -func c_Repository_Object(r uint64, h []byte) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - repo := obj.(*git.Repository) - var hash plumbing.Hash - copy(hash[:], h) - robj, err := repo.Object(hash) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - robj_handle := RegisterObject(robj) - return uint64(robj_handle), ErrorCodeSuccess, nil -} - -*/ diff --git a/cshared/std_cshared.go b/cshared/std_cshared.go deleted file mode 100644 index 90514b8..0000000 --- a/cshared/std_cshared.go +++ /dev/null @@ -1,143 +0,0 @@ -// +build ignore -package main - -import ( - "C" - "reflect" - "strings" -) - -//export c_std_map_get_str_str -func c_std_map_get_str_str(m uint64, key string) *C.char { - obj, ok := GetObject(Handle(m)) - if !ok { - return nil - } - mapval := reflect.ValueOf(obj) - if mapval.Kind() == reflect.Ptr { - mapval = mapval.Elem() - } - if mapval.Kind() != reflect.Map { - return nil - } - val := mapval.MapIndex(reflect.ValueOf(key)) - if !val.IsValid() || SafeIsNil(val) { - return nil - } - if (val.Kind() == reflect.Slice || val.Kind() == reflect.Array) && - val.Type().Elem().Kind() == reflect.Uint8 { - arr := make([]byte, val.Len(), val.Len()) - reflect.Copy(reflect.ValueOf(arr), val) - return CBytes(arr) - } - return C.CString(val.String()) -} - -//export c_std_map_get_str_obj -func c_std_map_get_str_obj(m uint64, key string) uint64 { - obj, ok := GetObject(Handle(m)) - if !ok { - return IH - } - mapval := reflect.ValueOf(obj) - if mapval.Kind() == reflect.Ptr { - mapval = mapval.Elem() - } - if mapval.Kind() != reflect.Map { - return IH - } - val := mapval.MapIndex(reflect.ValueOf(key)) - if !val.IsValid() || SafeIsNil(val) { - return IH - } - val_handle := RegisterObject(val.Interface()) - return uint64(val_handle) -} - -//export c_std_map_get_obj_obj -func c_std_map_get_obj_obj(m uint64, key uint64) uint64 { - obj, ok := GetObject(Handle(m)) - if !ok { - return IH - } - mapval := reflect.ValueOf(obj) - if mapval.Kind() == reflect.Ptr { - mapval = mapval.Elem() - } - if mapval.Kind() != reflect.Map { - return IH - } - obj, ok = GetObject(Handle(key)) - if !ok { - return IH - } - val := mapval.MapIndex(reflect.ValueOf(obj)) - if !val.IsValid() || SafeIsNil(val) { - return IH - } - val_handle := RegisterObject(val.Interface()) - return uint64(val_handle) -} - -//export c_std_map_keys_str -func c_std_map_keys_str(m uint64) *C.char { - obj, ok := GetObject(Handle(m)) - if !ok { - return nil - } - mapval := reflect.ValueOf(obj) - if mapval.Kind() == reflect.Ptr { - mapval = mapval.Elem() - } - if mapval.Kind() != reflect.Map { - return nil - } - keys := mapval.MapKeys() - keys_str := make([]string, 0, len(keys)) - for _, k := range keys { - keys_str = append(keys_str, k.String()) - } - return C.CString(strings.Join(keys_str, "\xff")) -} - -//export c_std_map_len -func c_std_map_len(m uint64) int { - obj, ok := GetObject(Handle(m)) - if !ok { - return -1 - } - mapval := reflect.ValueOf(obj) - if mapval.Kind() == reflect.Ptr { - mapval = mapval.Elem() - } - if mapval.Kind() != reflect.Map { - return -1 - } - return mapval.Len() -} - -//export c_std_map_set_str -func c_std_map_set_str(m uint64, key string, val uint64) { - obj, ok := GetObject(Handle(m)) - if !ok { - return - } - mapval := reflect.ValueOf(obj) - if mapval.Kind() == reflect.Ptr { - mapval = mapval.Elem() - } else { - return - } - if mapval.Kind() != reflect.Map { - return - } - if val == IH { - mapval.SetMapIndex(reflect.ValueOf(key), reflect.Value{}) - } else { - obj, ok := GetObject(Handle(val)) - if !ok { - return - } - mapval.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(obj)) - } -} diff --git a/cshared/tag_cshared.go b/cshared/tag_cshared.go deleted file mode 100644 index b13bcf5..0000000 --- a/cshared/tag_cshared.go +++ /dev/null @@ -1,188 +0,0 @@ -// +build ignore -package main - -import ( - "C" - "io" - - "srcd.works/go-git.v4/plumbing" - "srcd.works/go-git.v4/plumbing/object" - "srcd.works/go-git.v4/plumbing/storer" -) - -func c_Tag_get_Hash(t uint64) *C.char { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil - } - tag := obj.(*object.Tag) - return CBytes(tag.Hash[:]) -} - -func c_Tag_get_Name(t uint64) *C.char { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil - } - tag := obj.(*object.Tag) - return C.CString(tag.Name) -} - -func c_Tag_get_Tagger(t uint64) uint64 { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH - } - tag := obj.(*object.Tag) - return uint64(RegisterObject(&tag.Tagger)) -} - -func c_Tag_get_Message(t uint64) *C.char { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil - } - tag := obj.(*object.Tag) - return C.CString(tag.Message) -} - -func c_Tag_get_TargetType(t uint64) int8 { - obj, ok := GetObject(Handle(t)) - if !ok { - return -1 - } - tag := obj.(*object.Tag) - return int8(tag.TargetType) -} - -func c_Tag_get_Target(t uint64) *C.char { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil - } - tag := obj.(*object.Tag) - return CBytes(tag.Target[:]) -} - -//export c_Tag_Type -func c_Tag_Type(t uint64) int8 { - obj, ok := GetObject(Handle(t)) - if !ok { - return -1 - } - tag := obj.(*object.Tag) - return int8(tag.Type()) -} - -//export c_Tag_Decode -func c_Tag_Decode(o uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(o)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - cobj := obj.(*plumbing.EncodedObject) - tag := object.Tag{} - err := tag.Decode(*cobj) - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&tag)), ErrorCodeSuccess, nil -} - -//export c_Tag_Commit -func c_Tag_Commit(t uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - tag := obj.(*object.Tag) - commit, err := tag.Commit() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(commit)), ErrorCodeSuccess, nil -} - -//export c_Tag_Tree -func c_Tag_Tree(t uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - tag := obj.(*object.Tag) - tree, err := tag.Tree() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(tree)), ErrorCodeSuccess, nil -} - -//export c_Tag_Blob -func c_Tag_Blob(t uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - tag := obj.(*object.Tag) - blob, err := tag.Blob() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(blob)), ErrorCodeSuccess, nil -} - -//export c_Tag_Object -func c_Tag_Object(t uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - tag := obj.(*object.Tag) - object, err := tag.Object() - if err != nil { - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(&object)), ErrorCodeSuccess, nil -} - -//export c_Tag_String -func c_Tag_String(t uint64) *C.char { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil - } - tag := obj.(*object.Tag) - return C.CString(tag.String()) -} - -//export c_NewTagIter -func c_NewTagIter(r uint64, i uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - s := obj.(storer.EncodedObjectStorer) - obj, ok = GetObject(Handle(i)) - if !ok { - return IH - } - iter := obj.(*storer.EncodedObjectIter) - return uint64(RegisterObject(object.NewTagIter(s, *iter))) -} - -//export c_TagIter_Next -func c_TagIter_Next(i uint64) (uint64, int, *C.char) { - obj, ok := GetObject(Handle(i)) - if !ok { - return IH, ErrorCodeNotFound, C.CString(MessageNotFound) - } - tagiter := obj.(*object.TagIter) - tag, err := tagiter.Next() - if err != nil { - if err == io.EOF { - return IH, ErrorCodeSuccess, nil - } - return IH, ErrorCodeInternal, C.CString(err.Error()) - } - return uint64(RegisterObject(tag)), ErrorCodeSuccess, nil -} diff --git a/cshared/tree_cshared.go b/cshared/tree_cshared.go deleted file mode 100644 index c47ecfb..0000000 --- a/cshared/tree_cshared.go +++ /dev/null @@ -1,169 +0,0 @@ -// Created by cgo - DO NOT EDIT - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:2 -package main - -/* -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:5 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:4 -import ( -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:7 - "srcd.works/go-git.v4" - "srcd.works/go-git.v4/plumbing" -) - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:13 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:12 -func c_Tree_get_Entries_len(t uint64) int { - obj, ok := GetObject(Handle(t)) - if !ok { - return 0 - } - tree := obj.(*object.Tree) - return len(tree.Entries) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:23 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:22 -func c_Tree_get_Entries_item(t uint64, index int) (*_Ctype_char, uint32, *_Ctype_char) { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil, 0, nil - } - tree := obj.(*object.Tree) - item := tree.Entries[index] - return _Cfunc_CString(item.Name), uint32(item.Mode), CBytes(item.Hash[:]) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:34 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:33 -func c_Tree_get_Hash(t uint64) *_Ctype_char { - obj, ok := GetObject(Handle(t)) - if !ok { - return nil - } - tree := obj.(*object.Tree) - return CBytes(tree.Hash[:]) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:44 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:43 -func c_Tree_File(t uint64, path string) (uint64, int, *_Ctype_char) { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH, ErrorCodeNotFound, _Cfunc_CString(MessageNotFound) - } - tree := obj.(*object.Tree) - file, err := tree.File(CopyString(path)) - if err != nil { - return IH, ErrorCodeInternal, _Cfunc_CString(err.Error()) - } - return uint64(RegisterObject(file)), ErrorCodeSuccess, nil -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:58 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:57 -func c_Tree_Type(t uint64) int8 { - obj, ok := GetObject(Handle(t)) - if !ok { - return -1 - } - tree := obj.(*object.Tree) - return int8(tree.Type()) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:68 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:67 -func c_Tree_Files(t uint64) uint64 { - obj, ok := GetObject(Handle(t)) - if !ok { - return IH - } - tree := obj.(*object.Tree) - iter := tree.Files() - return uint64(RegisterObject(iter)) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:79 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:78 -func c_Tree_Decode(o uint64) (uint64, int, *_Ctype_char) { - obj, ok := GetObject(Handle(o)) - if !ok { - return IH, ErrorCodeNotFound, _Cfunc_CString(MessageNotFound) - } - cobj := obj.(*plumbing.EncodedObject) - tree := object.Tree{} - err := tree.Decode(*cobj) - if err != nil { - return IH, ErrorCodeInternal, _Cfunc_CString(err.Error()) - } - return uint64(RegisterObject(&tree)), ErrorCodeSuccess, nil -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:94 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:93 -func c_NewTreeWalker(r uint64, t uint64) uint64 { - obj, ok := GetObject(Handle(r)) - if !ok { - return IH - } - repo := obj.(*git.Repository) - obj, ok = GetObject(Handle(t)) - if !ok { - return IH - } - tree := obj.(*object.Tree) - walker := object.NewTreeIter(repo, tree) - return uint64(RegisterObject(walker)) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:110 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:109 -func c_TreeWalker_Next(tw uint64) (*_Ctype_char, *_Ctype_char, uint32, *_Ctype_char, int, *_Ctype_char) { - obj, ok := GetObject(Handle(tw)) - if !ok { - return nil, nil, 0, nil, ErrorCodeNotFound, _Cfunc_CString(MessageNotFound) - } - walker := obj.(*object.TreeIter) - name, entry, err := walker.Next() - if err != nil { - return nil, nil, 0, nil, ErrorCodeInternal, _Cfunc_CString(err.Error()) - } - return _Cfunc_CString(name), _Cfunc_CString(entry.Name), uint32(entry.Mode), - CBytes(entry.Hash[:]), ErrorCodeSuccess, nil -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:125 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:124 -func c_TreeWalker_Tree(tw uint64) uint64 { - obj, ok := GetObject(Handle(tw)) - if !ok { - return IH - } - walker := obj.(*object.TreeIter) - return uint64(RegisterObject(walker.Tree())) -} - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:135 - -//line /home/mcuadros/workspace/go/src/srcd.works/go-git.v4/cshared/tree_cshared.go:134 -func c_TreeWalker_Close(tw uint64) { - obj, ok := GetObject(Handle(tw)) - if !ok { - return - } - walker := obj.(*object.TreeIter) - walker.Close() -} -*/ @@ -9,36 +9,49 @@ import ( "srcd.works/go-git.v4/plumbing/transport" ) +// SubmoduleRescursivity defines how depth will affect any submodule recursive +// operation. +type SubmoduleRescursivity uint + const ( - // DefaultRemoteName name of the default Remote, just like git command + // DefaultRemoteName name of the default Remote, just like git command. DefaultRemoteName = "origin" + + // NoRecurseSubmodules disables the recursion for a submodule operation. + NoRecurseSubmodules SubmoduleRescursivity = 0 + // DefaultSubmoduleRecursionDepth allow recursion in a submodule operation. + DefaultSubmoduleRecursionDepth SubmoduleRescursivity = 10 ) var ( ErrMissingURL = errors.New("URL field is required") ) -// CloneOptions describes how a clone should be performed +// CloneOptions describes how a clone should be performed. type CloneOptions struct { - // The (possibly remote) repository URL to clone from + // The (possibly remote) repository URL to clone from. URL string - // Auth credentials, if required, to use with the remote repository + // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod - // Name of the remote to be added, by default `origin` + // Name of the remote to be added, by default `origin`. RemoteName string - // Remote branch to clone + // Remote branch to clone. ReferenceName plumbing.ReferenceName - // Fetch only ReferenceName if true + // Fetch only ReferenceName if true. SingleBranch bool - // Limit fetching to the specified number of commits + // Limit fetching to the specified number of commits. Depth int + // RecurseSubmodules 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. + RecurseSubmodules SubmoduleRescursivity // Progress is where the human readable information sent by the server is // stored, if nil nothing is stored and the capability (if supported) - // no-progress, is sent to the server to avoid send this information + // no-progress, is sent to the server to avoid send this information. Progress sideband.Progress } -// Validate validates the fields and sets the default values +// Validate validates the fields and sets the default values. func (o *CloneOptions) Validate() error { if o.URL == "" { return ErrMissingURL @@ -55,7 +68,7 @@ func (o *CloneOptions) Validate() error { return nil } -// PullOptions describes how a pull should be performed +// PullOptions describes how a pull should be performed. type PullOptions struct { // Name of the remote to be pulled. If empty, uses the default. RemoteName string @@ -65,11 +78,14 @@ type PullOptions struct { SingleBranch bool // Limit fetching to the specified number of commits. Depth int - // Auth credentials, if required, to use with the remote repository + // 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. + RecurseSubmodules SubmoduleRescursivity // Progress is where the human readable information sent by the server is // stored, if nil nothing is stored and the capability (if supported) - // no-progress, is sent to the server to avoid send this information + // no-progress, is sent to the server to avoid send this information. Progress sideband.Progress } @@ -94,15 +110,15 @@ type FetchOptions struct { // Depth limit fetching to the specified number of commits from the tip of // each remote branch history. Depth int - // Auth credentials, if required, to use with the remote repository + // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod // Progress is where the human readable information sent by the server is // stored, if nil nothing is stored and the capability (if supported) - // no-progress, is sent to the server to avoid send this information + // no-progress, is sent to the server to avoid send this information. Progress sideband.Progress } -// Validate validates the fields and sets the default values +// Validate validates the fields and sets the default values. func (o *FetchOptions) Validate() error { if o.RemoteName == "" { o.RemoteName = DefaultRemoteName @@ -117,18 +133,18 @@ func (o *FetchOptions) Validate() error { return nil } -// PushOptions describes how a push should be performed +// PushOptions describes how a push should be performed. type PushOptions struct { // RemoteName is the name of the remote to be pushed to. RemoteName string // RefSpecs specify what destination ref to update with what source // object. A refspec with empty src can be used to delete a reference. RefSpecs []config.RefSpec - // Auth credentials, if required, to use with the remote repository + // Auth credentials, if required, to use with the remote repository. Auth transport.AuthMethod } -// Validate validates the fields and sets the default values +// Validate validates the fields and sets the default values. func (o *PushOptions) Validate() error { if o.RemoteName == "" { o.RemoteName = DefaultRemoteName @@ -148,3 +164,16 @@ func (o *PushOptions) Validate() error { return nil } + +// SubmoduleUpdateOptions describes how a submodule update should be performed. +type SubmoduleUpdateOptions struct { + // Init, if true initializes the submodules recorded in the index. + Init bool + // NoFetch tell to the update command to not fetch new objects from the + // remote site. + NoFetch bool + // RecurseSubmodules the update is performed not only in the submodules of + // the current repository but also in any nested submodules inside those + // submodules (and so on). Until the SubmoduleRescursivity is reached. + RecurseSubmodules SubmoduleRescursivity +} diff --git a/plumbing/format/index/encoder.go b/plumbing/format/index/encoder.go index e5de135..bdb10c1 100644 --- a/plumbing/format/index/encoder.go +++ b/plumbing/format/index/encoder.go @@ -6,6 +6,7 @@ import ( "errors" "hash" "io" + "sort" "time" "srcd.works/go-git.v4/utils/binary" @@ -61,6 +62,8 @@ func (e *Encoder) encodeHeader(idx *Index) error { } func (e *Encoder) encodeEntries(idx *Index) error { + sort.Sort(byName(idx.Entries)) + for _, entry := range idx.Entries { if err := e.encodeEntry(&entry); err != nil { return err @@ -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 + +func (l byName) Len() int { return len(l) } +func (l byName) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l byName) Less(i, j int) bool { return l[i].Name < l[j].Name } diff --git a/plumbing/format/index/encoder_test.go b/plumbing/format/index/encoder_test.go index 914ee26..a6a0ea2 100644 --- a/plumbing/format/index/encoder_test.go +++ b/plumbing/format/index/encoder_test.go @@ -26,6 +26,11 @@ func (s *IndexSuite) TestEncode(c *C) { }, { CreatedAt: time.Now(), ModifiedAt: time.Now(), + Name: "bar", + Size: 82, + }, { + CreatedAt: time.Now(), + ModifiedAt: time.Now(), Name: strings.Repeat(" ", 20), Size: 82, }}, @@ -42,6 +47,11 @@ func (s *IndexSuite) TestEncode(c *C) { c.Assert(err, IsNil) c.Assert(idx, DeepEquals, output) + + c.Assert(output.Entries[0].Name, Equals, strings.Repeat(" ", 20)) + c.Assert(output.Entries[1].Name, Equals, "bar") + c.Assert(output.Entries[2].Name, Equals, "foo") + } func (s *IndexSuite) TestEncodeUnsuportedVersion(c *C) { diff --git a/plumbing/format/index/index.go b/plumbing/format/index/index.go index e5dc178..a95dba2 100644 --- a/plumbing/format/index/index.go +++ b/plumbing/format/index/index.go @@ -36,9 +36,14 @@ const ( // in the worktree, having information about the working files. Changes in // worktree are detected using this Index. The Index is also used during merges type Index struct { - Version uint32 - Entries []Entry - Cache *Tree + // Version is index version + Version uint32 + // Entries collection of entries represented by this Index. The order of + // this collection is not guaranteed + Entries []Entry + // Cache represents the 'Cached tree' extension + Cache *Tree + // ResolveUndo represents the 'Resolve undo' extension ResolveUndo *ResolveUndo } @@ -84,7 +89,7 @@ type TreeEntry struct { // Path component (relative to its parent directory) Path string // Entries is the number of entries in the index that is covered by the tree - // this entry represents + // this entry represents. Entries int // Trees is the number that represents the number of subtrees this tree has Trees int diff --git a/plumbing/object/file.go b/plumbing/object/file.go index 35e7f24..4866777 100644 --- a/plumbing/object/file.go +++ b/plumbing/object/file.go @@ -81,7 +81,7 @@ func (iter *FileIter) Next() (*File, error) { return nil, err } - if entry.Mode.IsDir() { + if entry.Mode.IsDir() || entry.Mode == SubmoduleMode { continue } diff --git a/plumbing/object/file_test.go b/plumbing/object/file_test.go index 4c8bbb6..ff01c9f 100644 --- a/plumbing/object/file_test.go +++ b/plumbing/object/file_test.go @@ -247,3 +247,31 @@ func (s *FileSuite) TestFileIter(c *C) { c.Assert(count, Equals, 1) } + +func (s *FileSuite) TestFileIterSubmodule(c *C) { + dotgit := fixtures.ByURL("https://github.com/git-fixtures/submodule.git").One().DotGit() + st, err := filesystem.NewStorage(dotgit) + + c.Assert(err, IsNil) + + hash := plumbing.NewHash("a692ec699bff9117c1ed91752afbb7d9d272ebef") + commit, err := GetCommit(st, hash) + c.Assert(err, IsNil) + + tree, err := commit.Tree() + c.Assert(err, IsNil) + + expected := []string{ + ".gitmodules", + } + + var count int + i := tree.Files() + i.ForEach(func(f *File) error { + c.Assert(f.Name, Equals, expected[count]) + count++ + return nil + }) + + c.Assert(count, Equals, 1) +} diff --git a/plumbing/object/tree.go b/plumbing/object/tree.go index 3bcd80a..27d8578 100644 --- a/plumbing/object/tree.go +++ b/plumbing/object/tree.go @@ -375,11 +375,6 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) { return } - if entry.Mode == SubmoduleMode { - err = nil - continue - } - if entry.Mode.IsDir() { obj, err = GetTree(w.s, entry.Hash) } diff --git a/plumbing/object/tree_test.go b/plumbing/object/tree_test.go index be721b9..8ea31bb 100644 --- a/plumbing/object/tree_test.go +++ b/plumbing/object/tree_test.go @@ -4,7 +4,10 @@ import ( "io" "os" + fixtures "github.com/src-d/go-git-fixtures" + "srcd.works/go-git.v4/plumbing" + "srcd.works/go-git.v4/storage/filesystem" . "gopkg.in/check.v1" "srcd.works/go-git.v4/plumbing/storer" @@ -262,6 +265,44 @@ func (s *TreeSuite) TestTreeWalkerNextNonRecursive(c *C) { c.Assert(count, Equals, 8) } +func (s *TreeSuite) TestTreeWalkerNextSubmodule(c *C) { + dotgit := fixtures.ByURL("https://github.com/git-fixtures/submodule.git").One().DotGit() + st, err := filesystem.NewStorage(dotgit) + c.Assert(err, IsNil) + + hash := plumbing.NewHash("a692ec699bff9117c1ed91752afbb7d9d272ebef") + commit, err := GetCommit(st, hash) + c.Assert(err, IsNil) + + tree, err := commit.Tree() + c.Assert(err, IsNil) + + expected := []string{ + ".gitmodules", + "basic", + "itself", + } + + var count int + walker := NewTreeWalker(tree, true) + defer walker.Close() + + for { + name, entry, err := walker.Next() + if err == io.EOF { + break + } + + c.Assert(err, IsNil) + c.Assert(entry, NotNil) + c.Assert(name, Equals, expected[count]) + + count++ + } + + c.Assert(count, Equals, 3) +} + var treeWalkerExpects = []struct { Path, Mode, Name, Hash, Tree string }{{ @@ -16,6 +16,7 @@ import ( "srcd.works/go-git.v4/plumbing/storer" "srcd.works/go-git.v4/plumbing/transport" "srcd.works/go-git.v4/plumbing/transport/client" + "srcd.works/go-git.v4/storage" "srcd.works/go-git.v4/storage/memory" "srcd.works/go-git.v4/utils/ioutil" ) @@ -25,10 +26,10 @@ var NoErrAlreadyUpToDate = errors.New("already up-to-date") // Remote represents a connection to a remote repository type Remote struct { c *config.RemoteConfig - s Storer + s storage.Storer } -func newRemote(s Storer, c *config.RemoteConfig) *Remote { +func newRemote(s storage.Storer, c *config.RemoteConfig) *Remote { return &Remote{s: s, c: c} } @@ -321,7 +322,9 @@ func getHaves(localRefs storer.ReferenceStorer) ([]plumbing.Hash, error) { return haves, nil } -func getWants(spec []config.RefSpec, localStorer Storer, remoteRefs storer.ReferenceStorer) ([]plumbing.Hash, error) { +func getWants( + spec []config.RefSpec, localStorer storage.Storer, remoteRefs storer.ReferenceStorer, +) ([]plumbing.Hash, error) { wantTags := true for _, s := range spec { if !s.IsWildcard() { diff --git a/remote_test.go b/remote_test.go index 1e34905..e13ef20 100644 --- a/remote_test.go +++ b/remote_test.go @@ -11,6 +11,7 @@ import ( "srcd.works/go-git.v4/config" "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/storer" + "srcd.works/go-git.v4/storage" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-git.v4/storage/memory" @@ -126,7 +127,7 @@ func (s *RemoteSuite) TestFetchWithProgress(c *C) { } type mockPackfileWriter struct { - Storer + storage.Storer PackfileWriterCalled bool } diff --git a/repository.go b/repository.go index a8dd7ef..c065a26 100644 --- a/repository.go +++ b/repository.go @@ -4,12 +4,14 @@ import ( "errors" "fmt" "os" + "path/filepath" "srcd.works/go-git.v4/config" "srcd.works/go-git.v4/internal/revision" "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/object" "srcd.works/go-git.v4/plumbing/storer" + "srcd.works/go-git.v4/storage" "srcd.works/go-git.v4/storage/filesystem" "srcd.works/go-billy.v1" @@ -29,7 +31,7 @@ var ( // Repository represents a git repository type Repository struct { - Storer Storer + Storer storage.Storer r map[string]*Remote wt billy.Filesystem @@ -38,7 +40,7 @@ type Repository struct { // Init creates an empty git repository, based on the given Storer and worktree. // The worktree Filesystem is optional, if nil a bare repository is created. If // the given storer is not empty ErrRepositoryAlreadyExists is returned -func Init(s Storer, worktree billy.Filesystem) (*Repository, error) { +func Init(s storage.Storer, worktree billy.Filesystem) (*Repository, error) { r := newRepository(s, worktree) _, err := r.Reference(plumbing.HEAD, false) switch err { @@ -56,9 +58,75 @@ func Init(s Storer, worktree billy.Filesystem) (*Repository, error) { if worktree == nil { r.setIsBare(true) + return r, nil } - return r, nil + return r, setWorktreeAndStoragePaths(r, worktree) +} + +func setWorktreeAndStoragePaths(r *Repository, worktree billy.Filesystem) error { + type fsBased interface { + Filesystem() billy.Filesystem + } + + // .git file is only created if the storage is file based and the file + // system is osfs.OS + fs, isFSBased := r.Storer.(fsBased) + if !isFSBased { + return nil + } + + _, isOS := fs.Filesystem().(*osfs.OS) + if !isOS { + return nil + } + + if err := createDotGitFile(worktree, fs.Filesystem()); err != nil { + return err + } + + return setConfigWorktree(r, worktree, fs.Filesystem()) +} + +func createDotGitFile(worktree, storage billy.Filesystem) error { + path, err := filepath.Rel(worktree.Base(), storage.Base()) + if err != nil { + path = storage.Base() + } + + if path == ".git" { + // not needed, since the folder is the default place + return nil + } + + f, err := worktree.Create(".git") + if err != nil { + return err + } + + defer f.Close() + _, err = fmt.Fprintf(f, "gitdir: %s\n", path) + return err +} + +func setConfigWorktree(r *Repository, worktree, storage billy.Filesystem) error { + path, err := filepath.Rel(storage.Base(), worktree.Base()) + if err != nil { + path = worktree.Base() + } + + if path == ".." { + // not needed, since the folder is the default place + return nil + } + + cfg, err := r.Storer.Config() + if err != nil { + return err + } + + cfg.Core.Worktree = path + return r.Storer.SetConfig(cfg) } // Open opens a git repository using the given Storer and worktree filesystem, @@ -66,7 +134,7 @@ func Init(s Storer, worktree billy.Filesystem) (*Repository, error) { // The worktree can be nil when the repository being opened is bare, if the // repository is a normal one (not bare) and worktree is nil the err // ErrWorktreeNotProvided is returned -func Open(s Storer, worktree billy.Filesystem) (*Repository, error) { +func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error) { _, err := s.Reference(plumbing.HEAD) if err == plumbing.ErrReferenceNotFound { return nil, ErrRepositoryNotExists @@ -91,7 +159,7 @@ func Open(s 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 -func Clone(s Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) { +func Clone(s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) { r, err := Init(s, worktree) if err != nil { return nil, err @@ -159,7 +227,7 @@ func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) return r, r.clone(o) } -func newRepository(s Storer, worktree billy.Filesystem) *Repository { +func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository { return &Repository{ Storer: s, wt: worktree, @@ -247,12 +315,6 @@ func (r *Repository) clone(o *CloneOptions) error { return err } - // marks the repository as bare in the config, until we have Worktree, all - // the repository are bare - if err := r.setIsBare(true); err != nil { - return err - } - c := &config.RemoteConfig{ Name: o.RemoteName, URL: o.URL, @@ -286,9 +348,32 @@ func (r *Repository) clone(o *CloneOptions) error { return err } + if o.RecurseSubmodules != NoRecurseSubmodules && r.wt != nil { + if err := r.updateSubmodules(o.RecurseSubmodules); err != nil { + return err + } + } + return r.updateRemoteConfig(remote, o, c, head) } +func (r *Repository) updateSubmodules(recursion SubmoduleRescursivity) error { + w, err := r.Worktree() + if err != nil { + return err + } + + s, err := w.Submodules() + if err != nil { + return err + } + + return s.Update(&SubmoduleUpdateOptions{ + Init: true, + RecurseSubmodules: recursion, + }) +} + func (r *Repository) cloneRefSpec(o *CloneOptions, c *config.RemoteConfig) []config.RefSpec { @@ -462,7 +547,17 @@ func (r *Repository) Pull(o *PullOptions) error { return NoErrAlreadyUpToDate } - return r.updateWorktree() + if err := r.updateWorktree(); err != nil { + return err + } + + if o.RecurseSubmodules != NoRecurseSubmodules && r.wt != nil { + if err := r.updateSubmodules(o.RecurseSubmodules); err != nil { + return err + } + } + + return nil } func (r *Repository) updateWorktree() error { diff --git a/repository_test.go b/repository_test.go index 1b5b345..89ea188 100644 --- a/repository_test.go +++ b/repository_test.go @@ -18,6 +18,7 @@ import ( . "gopkg.in/check.v1" "srcd.works/go-billy.v1/memfs" + "srcd.works/go-billy.v1/osfs" ) type RepositorySuite struct { @@ -36,6 +37,52 @@ func (s *RepositorySuite) TestInit(c *C) { c.Assert(cfg.Core.IsBare, Equals, false) } +func (s *RepositorySuite) TestInitNonStandardDotGit(c *C) { + dir, err := ioutil.TempDir("", "init-non-standard") + c.Assert(err, IsNil) + c.Assert(os.RemoveAll(dir), IsNil) + + fs := osfs.New(dir) + storage, err := filesystem.NewStorage(fs.Dir("storage")) + c.Assert(err, IsNil) + + r, err := Init(storage, fs.Dir("worktree")) + c.Assert(err, IsNil) + c.Assert(r, NotNil) + + f, err := fs.Open("worktree/.git") + c.Assert(err, IsNil) + + all, err := ioutil.ReadAll(f) + c.Assert(err, IsNil) + c.Assert(string(all), Equals, "gitdir: ../storage\n") + + cfg, err := r.Config() + c.Assert(err, IsNil) + c.Assert(cfg.Core.Worktree, Equals, "../worktree") +} + +func (s *RepositorySuite) TestInitStandardDotGit(c *C) { + dir, err := ioutil.TempDir("", "init-standard") + c.Assert(err, IsNil) + c.Assert(os.RemoveAll(dir), IsNil) + + fs := osfs.New(dir) + storage, err := filesystem.NewStorage(fs.Dir(".git")) + c.Assert(err, IsNil) + + r, err := Init(storage, fs) + c.Assert(err, IsNil) + c.Assert(r, NotNil) + + l, err := fs.ReadDir(".git") + c.Assert(len(l) > 0, Equals, true) + + cfg, err := r.Config() + c.Assert(err, IsNil) + c.Assert(cfg.Core.Worktree, Equals, "") +} + func (s *RepositorySuite) TestInitBare(c *C) { r, err := Init(memory.NewStorage(), nil) c.Assert(err, IsNil) @@ -246,6 +293,24 @@ func (s *RepositorySuite) TestPlainClone(c *C) { c.Assert(remotes, HasLen, 1) } +func (s *RepositorySuite) TestPlainCloneWithRecurseSubmodules(c *C) { + dir, err := ioutil.TempDir("", "plain-clone-submodule") + c.Assert(err, IsNil) + defer os.RemoveAll(dir) + + path := fixtures.ByTag("submodule").One().Worktree().Base() + r, err := PlainClone(dir, false, &CloneOptions{ + URL: fmt.Sprintf("file://%s", path), + RecurseSubmodules: DefaultSubmoduleRecursionDepth, + }) + + c.Assert(err, IsNil) + + cfg, err := r.Config() + c.Assert(cfg.Remotes, HasLen, 1) + c.Assert(cfg.Submodules, HasLen, 2) +} + func (s *RepositorySuite) TestFetch(c *C) { r, _ := Init(memory.NewStorage(), nil) _, err := r.CreateRemote(&config.RemoteConfig{ @@ -515,27 +580,44 @@ func (s *RepositorySuite) TestPullProgress(c *C) { c.Assert(buf.Len(), Not(Equals), 0) } +func (s *RepositorySuite) TestPullProgressWithRecursion(c *C) { + path := fixtures.ByTag("submodule").One().Worktree().Base() + + dir, err := ioutil.TempDir("", "plain-clone-submodule") + c.Assert(err, IsNil) + defer os.RemoveAll(dir) + + r, _ := PlainInit(dir, false) + r.CreateRemote(&config.RemoteConfig{ + Name: DefaultRemoteName, + URL: fmt.Sprintf("file://%s", path), + }) + + err = r.Pull(&PullOptions{ + RecurseSubmodules: DefaultSubmoduleRecursionDepth, + }) + c.Assert(err, IsNil) + + cfg, err := r.Config() + c.Assert(cfg.Submodules, HasLen, 2) +} + func (s *RepositorySuite) TestPullAdd(c *C) { - path := fixtures.Basic().One().Worktree().Base() + path := fixtures.Basic().ByTag("worktree").One().Worktree().Base() - r, _ := Init(memory.NewStorage(), nil) - err := r.clone(&CloneOptions{ + r, err := Clone(memory.NewStorage(), nil, &CloneOptions{ URL: fmt.Sprintf("file://%s", filepath.Join(path, ".git")), }) c.Assert(err, IsNil) storage := r.Storer.(*memory.Storage) - c.Assert(storage.Objects, HasLen, 31) + c.Assert(storage.Objects, HasLen, 28) branch, err := r.Reference("refs/heads/master", false) c.Assert(err, IsNil) c.Assert(branch.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - branch, err = r.Reference("refs/remotes/origin/branch", false) - c.Assert(err, IsNil) - c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881") - ExecuteOnPath(c, path, "touch foo", "git add foo", @@ -546,16 +628,11 @@ func (s *RepositorySuite) TestPullAdd(c *C) { c.Assert(err, IsNil) // the commit command has introduced a new commit, tree and blob - c.Assert(storage.Objects, HasLen, 34) + c.Assert(storage.Objects, HasLen, 31) branch, err = r.Reference("refs/heads/master", false) c.Assert(err, IsNil) c.Assert(branch.Hash().String(), Not(Equals), "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") - - // the commit command, was in the local branch, so the remote should be read ok - branch, err = r.Reference("refs/remotes/origin/branch", false) - c.Assert(err, IsNil) - c.Assert(branch.Hash().String(), Equals, "e8d3ffab552895c19b9fcf7aa264d277cde33881") } func (s *RepositorySuite) TestPushToEmptyRepository(c *C) { diff --git a/storage/filesystem/internal/dotgit/dotgit.go b/storage/filesystem/internal/dotgit/dotgit.go index 6646e18..b46f827 100644 --- a/storage/filesystem/internal/dotgit/dotgit.go +++ b/storage/filesystem/internal/dotgit/dotgit.go @@ -21,13 +21,13 @@ const ( configPath = "config" indexPath = "index" shallowPath = "shallow" + modulePath = "module" + objectsPath = "objects" + packPath = "pack" + refsPath = "refs" tmpPackedRefsPrefix = "._packed-refs" - objectsPath = "objects" - packPath = "pack" - refsPath = "refs" - packExt = ".pack" idxExt = ".idx" ) @@ -454,6 +454,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 { + return d.fs.Dir(d.fs.Join(modulePath, name)) +} + func isHex(s string) bool { for _, b := range []byte(s) { if isNum(b) { diff --git a/storage/filesystem/internal/dotgit/dotgit_test.go b/storage/filesystem/internal/dotgit/dotgit_test.go index 226b299..57dfb53 100644 --- a/storage/filesystem/internal/dotgit/dotgit_test.go +++ b/storage/filesystem/internal/dotgit/dotgit_test.go @@ -434,3 +434,11 @@ func (s *SuiteDotGit) TestObjectNotFound(c *C) { c.Assert(err, NotNil) c.Assert(file, IsNil) } + +func (s *SuiteDotGit) TestSubmodules(c *C) { + fs := fixtures.ByTag("submodule").One().DotGit() + dir := New(fs) + + m := dir.Module("basic") + c.Assert(strings.HasSuffix(m.Base(), ".git/module/basic"), Equals, true) +} diff --git a/storage/filesystem/module.go b/storage/filesystem/module.go new file mode 100644 index 0000000..e8985d8 --- /dev/null +++ b/storage/filesystem/module.go @@ -0,0 +1,14 @@ +package filesystem + +import ( + "srcd.works/go-git.v4/storage" + "srcd.works/go-git.v4/storage/filesystem/internal/dotgit" +) + +type ModuleStorage struct { + dir *dotgit.DotGit +} + +func (s *ModuleStorage) Module(name string) (storage.Storer, error) { + return NewStorage(s.dir.Module(name)) +} diff --git a/storage/filesystem/storage.go b/storage/filesystem/storage.go index 7021d3a..9895507 100644 --- a/storage/filesystem/storage.go +++ b/storage/filesystem/storage.go @@ -11,11 +11,14 @@ import ( // standard git format (this is, the .git directory). Zero values of this type // are not safe to use, see the NewStorage function below. type Storage struct { + fs billy.Filesystem + ObjectStorage ReferenceStorage IndexStorage ShallowStorage ConfigStorage + ModuleStorage } // NewStorage returns a new Storage backed by a given `fs.Filesystem` @@ -27,10 +30,18 @@ func NewStorage(fs billy.Filesystem) (*Storage, error) { } return &Storage{ + fs: fs, + ObjectStorage: o, ReferenceStorage: ReferenceStorage{dir: dir}, IndexStorage: IndexStorage{dir: dir}, ShallowStorage: ShallowStorage{dir: dir}, ConfigStorage: ConfigStorage{dir: dir}, + ModuleStorage: ModuleStorage{dir: dir}, }, nil } + +// Filesystem returns the underlying filesystem +func (s *Storage) Filesystem() billy.Filesystem { + return s.fs +} diff --git a/storage/filesystem/storage_test.go b/storage/filesystem/storage_test.go index e398d22..7300de7 100644 --- a/storage/filesystem/storage_test.go +++ b/storage/filesystem/storage_test.go @@ -6,6 +6,7 @@ import ( "srcd.works/go-git.v4/storage/test" . "gopkg.in/check.v1" + "srcd.works/go-billy.v1/memfs" "srcd.works/go-billy.v1/osfs" ) @@ -23,3 +24,11 @@ func (s *StorageSuite) SetUpTest(c *C) { s.BaseStorageSuite = test.NewBaseStorageSuite(storage) } + +func (s *StorageSuite) TestFilesystem(c *C) { + fs := memfs.New() + storage, err := NewStorage(fs) + c.Assert(err, IsNil) + + c.Assert(storage.Filesystem(), Equals, fs) +} diff --git a/storage/memory/storage.go b/storage/memory/storage.go index 9b55b1f..92aeec9 100644 --- a/storage/memory/storage.go +++ b/storage/memory/storage.go @@ -8,6 +8,7 @@ import ( "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/format/index" "srcd.works/go-git.v4/plumbing/storer" + "srcd.works/go-git.v4/storage" ) var ErrUnsupportedObjectType = fmt.Errorf("unsupported object type") @@ -22,6 +23,7 @@ type Storage struct { ShallowStorage IndexStorage ReferenceStorage + ModuleStorage } // NewStorage returns a new Storage base on memory @@ -37,6 +39,7 @@ func NewStorage() *Storage { Blobs: make(map[plumbing.Hash]plumbing.EncodedObject, 0), Tags: make(map[plumbing.Hash]plumbing.EncodedObject, 0), }, + ModuleStorage: make(ModuleStorage, 0), } } @@ -232,3 +235,16 @@ func (s *ShallowStorage) SetShallow(commits []plumbing.Hash) error { func (s ShallowStorage) Shallow() ([]plumbing.Hash, error) { return s, nil } + +type ModuleStorage map[string]*Storage + +func (s ModuleStorage) Module(name string) (storage.Storer, error) { + if m, ok := s[name]; ok { + return m, nil + } + + m := NewStorage() + s[name] = m + + return m, nil +} diff --git a/storage/storer.go b/storage/storer.go new file mode 100644 index 0000000..d217209 --- /dev/null +++ b/storage/storer.go @@ -0,0 +1,26 @@ +package storage + +import ( + "srcd.works/go-git.v4/config" + "srcd.works/go-git.v4/plumbing/storer" +) + +// Storer is a generic storage of objects, references and any information +// related to a particular repository. The package srcd.works/go-git.v4/storage +// contains two implementation a filesystem base implementation (such as `.git`) +// and a memory implementations being ephemeral +type Storer interface { + storer.EncodedObjectStorer + storer.ReferenceStorer + storer.ShallowStorer + storer.IndexStorer + config.ConfigStorer + ModuleStorer +} + +// ModuleStorer allows interact with the modules' Storers +type ModuleStorer interface { + // Module returns a Storer reprensting a submodule, if not exists returns a + // new empty Storer is returned + Module(name string) (Storer, error) +} diff --git a/storage/test/storage_suite.go b/storage/test/storage_suite.go index e09a673..2a10c78 100644 --- a/storage/test/storage_suite.go +++ b/storage/test/storage_suite.go @@ -11,6 +11,7 @@ import ( "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/format/index" "srcd.works/go-git.v4/plumbing/storer" + "srcd.works/go-git.v4/storage" . "gopkg.in/check.v1" ) @@ -21,6 +22,7 @@ type Storer interface { storer.ShallowStorer storer.IndexStorer config.ConfigStorer + storage.ModuleStorer } type TestObject struct { @@ -321,7 +323,9 @@ func (s *BaseStorageSuite) TestSetConfigAndConfig(c *C) { cfg, err := s.Storer.Config() c.Assert(err, IsNil) - c.Assert(cfg, DeepEquals, expected) + + c.Assert(cfg.Core.IsBare, DeepEquals, expected.Core.IsBare) + c.Assert(cfg.Remotes, DeepEquals, expected.Remotes) } func (s *BaseStorageSuite) TestIndex(c *C) { @@ -353,6 +357,16 @@ func (s *BaseStorageSuite) TestSetConfigInvalid(c *C) { c.Assert(err, NotNil) } +func (s *BaseStorageSuite) TestModule(c *C) { + storer, err := s.Storer.Module("foo") + c.Assert(err, IsNil) + c.Assert(storer, NotNil) + + storer, err = s.Storer.Module("foo") + c.Assert(err, IsNil) + c.Assert(storer, NotNil) +} + func objectEquals(a plumbing.EncodedObject, b plumbing.EncodedObject) error { ha := a.Hash() hb := b.Hash() diff --git a/submodule.go b/submodule.go new file mode 100644 index 0000000..e329fda --- /dev/null +++ b/submodule.go @@ -0,0 +1,174 @@ +package git + +import ( + "errors" + + "srcd.works/go-git.v4/config" + "srcd.works/go-git.v4/plumbing" +) + +var ( + ErrSubmoduleAlreadyInitialized = errors.New("submodule already initialized") + ErrSubmoduleNotInitialized = errors.New("submodule not initialized") +) + +// Submodule a submodule allows you to keep another Git repository in a +// subdirectory of your repository. +type Submodule struct { + initialized bool + + c *config.Submodule + w *Worktree +} + +// Config returns the submodule config +func (s *Submodule) Config() *config.Submodule { + return s.c +} + +// Init initialize the submodule reading the recoreded Entry in the index for +// the given submodule +func (s *Submodule) Init() error { + cfg, err := s.w.r.Storer.Config() + if err != nil { + return err + } + + _, ok := cfg.Submodules[s.c.Name] + if ok { + return ErrSubmoduleAlreadyInitialized + } + + s.initialized = true + + cfg.Submodules[s.c.Name] = s.c + return s.w.r.Storer.SetConfig(cfg) +} + +// Repository returns the Repository represented by this submodule +func (s *Submodule) Repository() (*Repository, error) { + storer, err := s.w.r.Storer.Module(s.c.Name) + if err != nil { + return nil, err + } + + _, err = storer.Reference(plumbing.HEAD) + if err != nil && err != plumbing.ErrReferenceNotFound { + return nil, err + } + + worktree := s.w.fs.Dir(s.c.Path) + if err == nil { + return Open(storer, worktree) + } + + r, err := Init(storer, worktree) + if err != nil { + return nil, err + } + + _, err = r.CreateRemote(&config.RemoteConfig{ + Name: DefaultRemoteName, + URL: s.c.URL, + }) + + return r, err +} + +// Update the registered submodule to match what the superproject expects, the +// submodule should be initilized first calling the Init method or setting in +// the options SubmoduleUpdateOptions.Init equals true +func (s *Submodule) Update(o *SubmoduleUpdateOptions) error { + if !s.initialized && !o.Init { + return ErrSubmoduleNotInitialized + } + + if !s.initialized && o.Init { + if err := s.Init(); err != nil { + return err + } + } + + e, err := s.w.readIndexEntry(s.c.Path) + if err != nil { + return err + } + + r, err := s.Repository() + if err != nil { + return err + } + + if err := s.fetchAndCheckout(r, o, e.Hash); err != nil { + return err + } + + return s.doRecrusiveUpdate(r, o) +} + +func (s *Submodule) doRecrusiveUpdate(r *Repository, o *SubmoduleUpdateOptions) error { + if o.RecurseSubmodules == NoRecurseSubmodules { + return nil + } + + w, err := r.Worktree() + if err != nil { + return err + } + + l, err := w.Submodules() + if err != nil { + return err + } + + new := &SubmoduleUpdateOptions{} + *new = *o + new.RecurseSubmodules-- + return l.Update(new) +} + +func (s *Submodule) fetchAndCheckout(r *Repository, o *SubmoduleUpdateOptions, hash plumbing.Hash) error { + if !o.NoFetch { + err := r.Fetch(&FetchOptions{}) + if err != nil && err != NoErrAlreadyUpToDate { + return err + } + } + + w, err := r.Worktree() + if err != nil { + return err + } + + if err := w.Checkout(hash); err != nil { + return err + } + + head := plumbing.NewHashReference(plumbing.HEAD, hash) + return r.Storer.SetReference(head) +} + +// Submodules list of several submodules from the same repository +type Submodules []*Submodule + +// Init initializes the submodules in this list +func (s Submodules) Init() error { + for _, sub := range s { + if err := sub.Init(); err != nil { + return err + } + } + + return nil +} + +// Update updates all the submodules in this list +func (s Submodules) Update(o *SubmoduleUpdateOptions) error { + for _, sub := range s { + if err := sub.Update(o); err != nil { + return err + } + } + + return nil +} diff --git a/submodule_test.go b/submodule_test.go new file mode 100644 index 0000000..a933965 --- /dev/null +++ b/submodule_test.go @@ -0,0 +1,164 @@ +package git + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/src-d/go-git-fixtures" + + . "gopkg.in/check.v1" + "srcd.works/go-git.v4/plumbing" +) + +type SubmoduleSuite struct { + BaseSuite + Worktree *Worktree + path string +} + +var _ = Suite(&SubmoduleSuite{}) + +func (s *SubmoduleSuite) SetUpTest(c *C) { + path := fixtures.ByTag("submodule").One().Worktree().Base() + + dir, err := ioutil.TempDir("", "submodule") + c.Assert(err, IsNil) + + r, err := PlainClone(dir, false, &CloneOptions{ + URL: fmt.Sprintf("file://%s", filepath.Join(path)), + }) + + c.Assert(err, IsNil) + + s.Repository = r + s.Worktree, err = r.Worktree() + c.Assert(err, IsNil) + + s.path = dir +} + +func (s *SubmoduleSuite) TearDownTest(c *C) { + err := os.RemoveAll(s.path) + c.Assert(err, IsNil) +} + +func (s *SubmoduleSuite) TestInit(c *C) { + sm, err := s.Worktree.Submodule("basic") + c.Assert(err, IsNil) + + err = sm.Init() + c.Assert(err, IsNil) + + cfg, err := s.Repository.Config() + c.Assert(err, IsNil) + + c.Assert(cfg.Submodules, HasLen, 1) + c.Assert(cfg.Submodules["basic"], NotNil) +} + +func (s *SubmoduleSuite) TestUpdate(c *C) { + sm, err := s.Worktree.Submodule("basic") + c.Assert(err, IsNil) + + err = sm.Update(&SubmoduleUpdateOptions{ + Init: true, + }) + + c.Assert(err, IsNil) + + r, err := sm.Repository() + c.Assert(err, IsNil) + + ref, err := r.Reference(plumbing.HEAD, true) + c.Assert(err, IsNil) + c.Assert(ref.Hash().String(), Equals, "6ecf0ef2c2dffb796033e5a02219af86ec6584e5") + +} + +func (s *SubmoduleSuite) TestUpdateWithoutInit(c *C) { + sm, err := s.Worktree.Submodule("basic") + c.Assert(err, IsNil) + + err = sm.Update(&SubmoduleUpdateOptions{}) + c.Assert(err, Equals, ErrSubmoduleNotInitialized) +} + +func (s *SubmoduleSuite) TestUpdateWithNotFetch(c *C) { + sm, err := s.Worktree.Submodule("basic") + c.Assert(err, IsNil) + + err = sm.Update(&SubmoduleUpdateOptions{ + Init: true, + NoFetch: true, + }) + + // Since we are not fetching, the object is not there + c.Assert(err, Equals, plumbing.ErrObjectNotFound) +} + +func (s *SubmoduleSuite) TestUpdateWithRecursion(c *C) { + sm, err := s.Worktree.Submodule("itself") + c.Assert(err, IsNil) + + err = sm.Update(&SubmoduleUpdateOptions{ + Init: true, + RecurseSubmodules: 2, + }) + + c.Assert(err, IsNil) + + _, err = s.Worktree.fs.Stat("itself/basic/LICENSE") + c.Assert(err, IsNil) +} + +func (s *SubmoduleSuite) TestUpdateWithInitAndUpdate(c *C) { + sm, err := s.Worktree.Submodule("basic") + c.Assert(err, IsNil) + + err = sm.Update(&SubmoduleUpdateOptions{ + Init: true, + }) + c.Assert(err, IsNil) + + idx, err := s.Repository.Storer.Index() + c.Assert(err, IsNil) + + for i, e := range idx.Entries { + if e.Name == "basic" { + e.Hash = plumbing.NewHash("b029517f6300c2da0f4b651b8642506cd6aaf45d") + } + + idx.Entries[i] = e + } + + err = s.Repository.Storer.SetIndex(idx) + c.Assert(err, IsNil) + + err = sm.Update(&SubmoduleUpdateOptions{}) + c.Assert(err, IsNil) + + r, err := sm.Repository() + c.Assert(err, IsNil) + + ref, err := r.Reference(plumbing.HEAD, true) + c.Assert(err, IsNil) + c.Assert(ref.Hash().String(), Equals, "b029517f6300c2da0f4b651b8642506cd6aaf45d") + +} + +func (s *SubmoduleSuite) TestSubmodulesInit(c *C) { + sm, err := s.Worktree.Submodules() + c.Assert(err, IsNil) + + err = sm.Init() + c.Assert(err, IsNil) + + sm, err = s.Worktree.Submodules() + c.Assert(err, IsNil) + + for _, m := range sm { + c.Assert(m.initialized, Equals, true) + } +} diff --git a/worktree.go b/worktree.go index 58e008e..2a4e5d8 100644 --- a/worktree.go +++ b/worktree.go @@ -4,8 +4,10 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" + "srcd.works/go-git.v4/config" "srcd.works/go-git.v4/plumbing" "srcd.works/go-git.v4/plumbing/format/index" "srcd.works/go-git.v4/plumbing/object" @@ -14,6 +16,7 @@ import ( ) var ErrWorktreeNotClean = errors.New("worktree is not clean") +var ErrSubmoduleNotFound = errors.New("submodule not found") type Worktree struct { r *Repository @@ -35,29 +38,57 @@ func (w *Worktree) Checkout(commit plumbing.Hash) error { return err } - files, err := c.Files() + t, err := c.Tree() if err != nil { return err } idx := &index.Index{Version: 2} - if err := files.ForEach(func(f *object.File) error { - return w.checkoutFile(f, idx) - }); err != nil { - return err + walker := object.NewTreeWalker(t, true) + + for { + name, entry, err := walker.Next() + if err == io.EOF { + break + } + + if err != nil { + return err + } + + if err := w.checkoutEntry(name, &entry, idx); err != nil { + return err + } } return w.r.Storer.SetIndex(idx) } -func (w *Worktree) checkoutFile(f *object.File, idx *index.Index) error { - from, err := f.Reader() +func (w *Worktree) checkoutEntry(name string, e *object.TreeEntry, idx *index.Index) error { + if e.Mode == object.SubmoduleMode { + return w.addIndexFromTreeEntry(name, e, idx) + } + + if e.Mode.IsDir() { + return nil + } + + return w.checkoutFile(name, e, idx) +} + +func (w *Worktree) checkoutFile(name string, e *object.TreeEntry, idx *index.Index) error { + blob, err := object.GetBlob(w.r.Storer, e.Hash) + if err != nil { + return err + } + + from, err := blob.Reader() if err != nil { return err } defer from.Close() - to, err := w.fs.OpenFile(f.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode.Perm()) + to, err := w.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, e.Mode.Perm()) if err != nil { return err } @@ -67,20 +98,30 @@ func (w *Worktree) checkoutFile(f *object.File, idx *index.Index) error { } defer to.Close() - return w.indexFile(f, idx) + return w.addIndexFromFile(name, e, idx) } var fillSystemInfo func(e *index.Entry, sys interface{}) -func (w *Worktree) indexFile(f *object.File, idx *index.Index) error { - fi, err := w.fs.Stat(f.Name) +func (w *Worktree) addIndexFromTreeEntry(name string, f *object.TreeEntry, idx *index.Index) error { + idx.Entries = append(idx.Entries, index.Entry{ + Hash: f.Hash, + Name: name, + Mode: object.SubmoduleMode, + }) + + return nil +} + +func (w *Worktree) addIndexFromFile(name string, f *object.TreeEntry, idx *index.Index) error { + fi, err := w.fs.Stat(name) if err != nil { return err } e := index.Entry{ Hash: f.Hash, - Name: f.Name, + Name: name, Mode: w.getMode(fi), ModifiedAt: fi.ModTime(), Size: uint32(fi.Size()), @@ -167,6 +208,90 @@ func (w *Worktree) getMode(fi billy.FileInfo) os.FileMode { return object.FileMode } +const gitmodulesFile = ".gitmodules" + +// Submodule returns the submodule with the given name +func (w *Worktree) Submodule(name string) (*Submodule, error) { + l, err := w.Submodules() + if err != nil { + return nil, err + } + + for _, m := range l { + if m.Config().Name == name { + return m, nil + } + } + + return nil, ErrSubmoduleNotFound +} + +// Submodules returns all the available submodules +func (w *Worktree) Submodules() (Submodules, error) { + l := make(Submodules, 0) + m, err := w.readGitmodulesFile() + if err != nil || m == nil { + return l, err + } + + c, err := w.r.Config() + for _, s := range m.Submodules { + l = append(l, w.newSubmodule(s, c.Submodules[s.Name])) + } + + return l, nil +} + +func (w *Worktree) newSubmodule(fromModules, fromConfig *config.Submodule) *Submodule { + m := &Submodule{w: w} + m.initialized = fromConfig != nil + + if !m.initialized { + m.c = fromModules + return m + } + + m.c = fromConfig + m.c.Path = fromModules.Path + return m +} + +func (w *Worktree) readGitmodulesFile() (*config.Modules, error) { + f, err := w.fs.Open(gitmodulesFile) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + + return nil, err + } + + input, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + m := config.NewModules() + return m, m.Unmarshal(input) +} + +func (w *Worktree) readIndexEntry(path string) (index.Entry, error) { + var e index.Entry + + idx, err := w.r.Storer.Index() + if err != nil { + return e, err + } + + for _, e := range idx.Entries { + if e.Name == path { + return e, nil + } + } + + return e, fmt.Errorf("unable to find %q entry in the index", path) +} + // Status current status of a Worktree type Status map[string]*FileStatus @@ -281,17 +406,25 @@ func readDirAll(filesystem billy.Filesystem) (map[string]billy.FileInfo, error) } func doReadDirAll(fs billy.Filesystem, path string, files map[string]billy.FileInfo) error { - if path == ".git" { + if path == defaultDotGitPath { return nil } l, err := fs.ReadDir(path) if err != nil { + if os.IsNotExist(err) { + return nil + } + return err } for _, info := range l { file := fs.Join(path, info.Name()) + if file == defaultDotGitPath { + continue + } + if !info.IsDir() { files[file] = info continue diff --git a/worktree_test.go b/worktree_test.go index 8ca3d4f..81d35b1 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -7,6 +7,7 @@ import ( "srcd.works/go-git.v4/plumbing/format/index" "srcd.works/go-git.v4/plumbing/object" + "github.com/src-d/go-git-fixtures" . "gopkg.in/check.v1" "srcd.works/go-billy.v1/memfs" "srcd.works/go-billy.v1/osfs" @@ -116,7 +117,6 @@ func (s *WorktreeSuite) TestCheckoutIndexOS(c *C) { } func (s *WorktreeSuite) TestStatus(c *C) { - h, err := s.Repository.Head() c.Assert(err, IsNil) @@ -164,3 +164,31 @@ func (s *WorktreeSuite) TestStatusModified(c *C) { c.Assert(err, IsNil) c.Assert(status.IsClean(), Equals, false) } + +func (s *WorktreeSuite) TestSubmodule(c *C) { + path := fixtures.ByTag("submodule").One().Worktree().Base() + r, err := PlainOpen(path) + c.Assert(err, IsNil) + + w, err := r.Worktree() + c.Assert(err, IsNil) + + m, err := w.Submodule("basic") + c.Assert(err, IsNil) + + c.Assert(m.Config().Name, Equals, "basic") +} + +func (s *WorktreeSuite) TestSubmodules(c *C) { + path := fixtures.ByTag("submodule").One().Worktree().Base() + r, err := PlainOpen(path) + c.Assert(err, IsNil) + + w, err := r.Worktree() + c.Assert(err, IsNil) + + l, err := w.Submodules() + c.Assert(err, IsNil) + + c.Assert(l, HasLen, 2) +} |