aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony HAMON <antham@users.noreply.github.com>2017-12-01 16:27:52 +0100
committerMáximo Cuadros <mcuadros@gmail.com>2017-12-01 16:27:52 +0100
commit44c364fe3b7b8cdc0f9623afe870d6781a97ebb4 (patch)
tree03284e910c75da7c896381b439c04498eabe63db
parente20d3347d26f0b7193502e2ad7386d7c504b0cde (diff)
downloadgo-git-44c364fe3b7b8cdc0f9623afe870d6781a97ebb4.tar.gz
Fix revision solver for branch and tag (#660)
fix Repository.ResolveRevision for branch and tag
-rw-r--r--_examples/README.md7
-rw-r--r--_examples/common.go2
-rw-r--r--_examples/common_test.go1
-rw-r--r--_examples/revision/main.go32
-rw-r--r--plumbing/reference.go6
-rw-r--r--repository.go43
-rw-r--r--repository_test.go45
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 {