diff options
author | Anthony HAMON <antham@users.noreply.github.com> | 2017-12-01 16:27:52 +0100 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2017-12-01 16:27:52 +0100 |
commit | 44c364fe3b7b8cdc0f9623afe870d6781a97ebb4 (patch) | |
tree | 03284e910c75da7c896381b439c04498eabe63db | |
parent | e20d3347d26f0b7193502e2ad7386d7c504b0cde (diff) | |
download | go-git-44c364fe3b7b8cdc0f9623afe870d6781a97ebb4.tar.gz |
Fix revision solver for branch and tag (#660)
fix Repository.ResolveRevision for branch and tag
-rw-r--r-- | _examples/README.md | 7 | ||||
-rw-r--r-- | _examples/common.go | 2 | ||||
-rw-r--r-- | _examples/common_test.go | 1 | ||||
-rw-r--r-- | _examples/revision/main.go | 32 | ||||
-rw-r--r-- | plumbing/reference.go | 6 | ||||
-rw-r--r-- | repository.go | 43 | ||||
-rw-r--r-- | repository_test.go | 45 |
7 files changed, 82 insertions, 54 deletions
diff --git a/_examples/README.md b/_examples/README.md index 10594ab..271379b 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -11,9 +11,10 @@ Here you can find a list of annotated _go-git_ examples: - [remotes](remotes/main.go) - Working with remotes: adding, removing, etc - [progress](progress/main.go) - Printing the progress information from the sideband - [push](push/main.go) - Push repository to default remote (origin) -- [checkout](checkout/main.go) - check out a specific commit from a repository -- [tag](tag/main.go) - list/print repository tags -- [pull](pull/main.go) - pull changes from a remote repository +- [checkout](checkout/main.go) - Check out a specific commit from a repository +- [tag](tag/main.go) - List/print repository tags +- [pull](pull/main.go) - Pull changes from a remote repository +- [revision](revision/main.go) - Solve a revision into a commit ### Advanced - [custom_http](custom_http/main.go) - Replacing the HTTP client using a custom one - [storage](storage/README.md) - Implementing a custom storage system diff --git a/_examples/common.go b/_examples/common.go index 2719c0e..81ed647 100644 --- a/_examples/common.go +++ b/_examples/common.go @@ -6,7 +6,7 @@ import ( "strings" ) -// CheckArgs should be used to esnure the right command line arguments are +// CheckArgs should be used to ensure the right command line arguments are // passed before executing an example. func CheckArgs(arg ...string) { if len(os.Args) < len(arg)+1 { diff --git a/_examples/common_test.go b/_examples/common_test.go index d164297..6f8df45 100644 --- a/_examples/common_test.go +++ b/_examples/common_test.go @@ -23,6 +23,7 @@ var args = map[string][]string{ "open": {cloneRepository(defaultURL, tempFolder())}, "progress": {defaultURL, tempFolder()}, "push": {setEmptyRemote(cloneRepository(defaultURL, tempFolder()))}, + "revision": {cloneRepository(defaultURL, tempFolder()), "master~2^"}, "showcase": {defaultURL, tempFolder()}, "tag": {cloneRepository(defaultURL, tempFolder())}, "pull": {createRepositoryWithRemote(tempFolder(), defaultURL)}, diff --git a/_examples/revision/main.go b/_examples/revision/main.go new file mode 100644 index 0000000..a2389bd --- /dev/null +++ b/_examples/revision/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "os" + + "gopkg.in/src-d/go-git.v4" + . "gopkg.in/src-d/go-git.v4/_examples" + "gopkg.in/src-d/go-git.v4/plumbing" +) + +// Example how to resolve a revision into its commit counterpart +func main() { + CheckArgs("<path>", "<revision>") + + path := os.Args[1] + revision := os.Args[2] + + // We instantiate a new repository targeting the given path (the .git folder) + r, err := git.PlainOpen(path) + CheckIfError(err) + + // Resolve revision into a sha1 commit, only some revisions are resolved + // look at the doc to get more details + Info("git rev-parse %s", revision) + + h, err := r.ResolveRevision(plumbing.Revision(revision)) + + CheckIfError(err) + + fmt.Println(h.String()) +} diff --git a/plumbing/reference.go b/plumbing/reference.go index 2c30fe0..2f53d4e 100644 --- a/plumbing/reference.go +++ b/plumbing/reference.go @@ -15,10 +15,10 @@ const ( symrefPrefix = "ref: " ) -// refRevParseRules are a set of rules to parse references into short names. +// RefRevParseRules are a set of rules to parse references into short names. // These are the same rules as used by git in shorten_unambiguous_ref. // See: https://github.com/git/git/blob/e0aaa1b6532cfce93d87af9bc813fb2e7a7ce9d7/refs.c#L417 -var refRevParseRules = []string{ +var RefRevParseRules = []string{ "refs/%s", "refs/tags/%s", "refs/heads/%s", @@ -83,7 +83,7 @@ func (r ReferenceName) String() string { func (r ReferenceName) Short() string { s := string(r) res := s - for _, format := range refRevParseRules { + for _, format := range RefRevParseRules { _, err := fmt.Sscanf(s, format, &res) if err == nil { continue diff --git a/repository.go b/repository.go index 7cdc0d5..8a966c6 100644 --- a/repository.go +++ b/repository.go @@ -891,6 +891,9 @@ func (r *Repository) Worktree() (*Worktree, error) { } // ResolveRevision resolves revision to corresponding hash. +// +// Implemented resolvers : HEAD, branch, tag, heads/branch, refs/heads/branch, +// refs/tags/tag, refs/remotes/origin/branch, refs/remotes/origin/HEAD, tilde and caret (HEAD~1, master~^, tag~2, ref/heads/master~1, ...), selection by text (HEAD^{/fix nasty bug}) func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, error) { p := revision.NewParserFromString(string(rev)) @@ -905,15 +908,22 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err for _, item := range items { switch item.(type) { case revision.Ref: - ref, err := storer.ResolveReference(r.Storer, plumbing.ReferenceName(item.(revision.Ref))) + revisionRef := item.(revision.Ref) + var ref *plumbing.Reference - if err != nil { - return &plumbing.ZeroHash, err + for _, rule := range append([]string{"%s"}, plumbing.RefRevParseRules...) { + ref, err = storer.ResolveReference(r.Storer, plumbing.ReferenceName(fmt.Sprintf(rule, revisionRef))) + + if err == nil { + break + } } - h := ref.Hash() + if ref == nil { + return &plumbing.ZeroHash, plumbing.ErrReferenceNotFound + } - commit, err = r.CommitObject(h) + commit, err = r.CommitObject(ref.Hash()) if err != nil { return &plumbing.ZeroHash, err @@ -986,29 +996,6 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err } commit = c - case revision.AtDate: - history := object.NewCommitPreorderIter(commit, nil, nil) - - date := item.(revision.AtDate).Date - - var c *object.Commit - err := history.ForEach(func(hc *object.Commit) error { - if date.Equal(hc.Committer.When.UTC()) || hc.Committer.When.UTC().Before(date) { - c = hc - return storer.ErrStop - } - - return nil - }) - if err != nil { - return &plumbing.ZeroHash, err - } - - if c == nil { - return &plumbing.ZeroHash, fmt.Errorf(`No commit exists prior to date "%s"`, date.String()) - } - - commit = c } } diff --git a/repository_test.go b/repository_test.go index 2ebc597..cf0805a 100644 --- a/repository_test.go +++ b/repository_test.go @@ -1265,24 +1265,32 @@ func (s *RepositorySuite) TestWorktreeBare(c *C) { } func (s *RepositorySuite) TestResolveRevision(c *C) { - url := s.GetLocalRepositoryURL( - fixtures.ByURL("https://github.com/git-fixtures/basic.git").One(), - ) - - r, _ := Init(memory.NewStorage(), nil) - err := r.clone(context.Background(), &CloneOptions{URL: url}) + f := fixtures.ByURL("https://github.com/git-fixtures/basic.git").One() + sto, err := filesystem.NewStorage(f.DotGit()) + c.Assert(err, IsNil) + r, err := Open(sto, f.DotGit()) c.Assert(err, IsNil) datas := map[string]string{ - "HEAD": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", - "refs/heads/master~2^^~": "b029517f6300c2da0f4b651b8642506cd6aaf45d", - "HEAD~2^^~": "b029517f6300c2da0f4b651b8642506cd6aaf45d", - "HEAD~3^2": "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69", - "HEAD~3^2^0": "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69", - "HEAD~2^{/binary file}": "35e85108805c84807bc66a02d91535e1e24b38b9", - "HEAD~^{!-some}": "1669dce138d9b841a518c64b10914d88f5e488ea", - "HEAD@{2015-03-31T11:56:18Z}": "918c48b83bd081e863dbe1b80f8998f058cd8294", - "HEAD@{2015-03-31T11:49:00Z}": "1669dce138d9b841a518c64b10914d88f5e488ea", + "HEAD": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "heads/master~1": "918c48b83bd081e863dbe1b80f8998f058cd8294", + "refs/heads/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "refs/heads/master~2^^~": "b029517f6300c2da0f4b651b8642506cd6aaf45d", + "refs/tags/v1.0.0": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "refs/remotes/origin/master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "refs/remotes/origin/HEAD": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "HEAD~2^^~": "b029517f6300c2da0f4b651b8642506cd6aaf45d", + "HEAD~3^2": "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69", + "HEAD~3^2^0": "a5b8b09e2f8fcb0bb99d3ccb0958157b40890d69", + "HEAD~2^{/binary file}": "35e85108805c84807bc66a02d91535e1e24b38b9", + "HEAD~^{/!-some}": "1669dce138d9b841a518c64b10914d88f5e488ea", + "master": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "branch": "e8d3ffab552895c19b9fcf7aa264d277cde33881", + "v1.0.0": "6ecf0ef2c2dffb796033e5a02219af86ec6584e5", + "branch~1": "918c48b83bd081e863dbe1b80f8998f058cd8294", + "v1.0.0~1": "918c48b83bd081e863dbe1b80f8998f058cd8294", + "master~1": "918c48b83bd081e863dbe1b80f8998f058cd8294", } for rev, hash := range datas { @@ -1303,10 +1311,9 @@ func (s *RepositorySuite) TestResolveRevisionWithErrors(c *C) { c.Assert(err, IsNil) datas := map[string]string{ - "efs/heads/master~": "reference not found", - "HEAD^3": `Revision invalid : "3" found must be 0, 1 or 2 after "^"`, - "HEAD^{/whatever}": `No commit message match regexp : "whatever"`, - "HEAD@{2015-03-31T09:49:00Z}": `No commit exists prior to date "2015-03-31 09:49:00 +0000 UTC"`, + "efs/heads/master~": "reference not found", + "HEAD^3": `Revision invalid : "3" found must be 0, 1 or 2 after "^"`, + "HEAD^{/whatever}": `No commit message match regexp : "whatever"`, } for rev, rerr := range datas { |