Jiri fails when referred tag is not in any branch
If error occures while checking out head, try to fetch tag first before
returning a error
TO-315
Change-Id: I49268e640f4877c49b2bc4b75a4b49ec2c8c1b04
diff --git a/git/git.go b/git/git.go
index 7f0693f..23de266 100644
--- a/git/git.go
+++ b/git/git.go
@@ -90,6 +90,25 @@
return g.FetchRefspec(remote, "", opts...)
}
+func (g *Git) CreateLightweightTag(name string) error {
+ repo, err := git2go.OpenRepository(g.rootDir)
+ if err != nil {
+ return err
+ }
+ defer repo.Free()
+ head, err := repo.Head()
+ if err != nil {
+ return err
+ }
+ defer head.Free()
+ c, err := repo.LookupCommit(head.Target())
+ if err != nil {
+ return err
+ }
+ _, err = repo.Tags.CreateLightweight(name, c, false)
+ return err
+}
+
// FetchRefspec fetches refs and tags from the given remote for a particular refspec.
func (g *Git) FetchRefspec(remoteName, refspec string, opts ...FetchOpt) error {
repo, err := git2go.OpenRepository(g.rootDir)
diff --git a/gitutil/git.go b/gitutil/git.go
index 70b91cf..f83aab0 100644
--- a/gitutil/git.go
+++ b/gitutil/git.go
@@ -490,6 +490,7 @@
prune := false
updateShallow := false
depth := 0
+ fetchTag := ""
for _, opt := range opts {
switch typedOpt := opt.(type) {
case TagsOpt:
@@ -502,6 +503,8 @@
depth = int(typedOpt)
case UpdateShallowOpt:
updateShallow = bool(typedOpt)
+ case FetchTagOpt:
+ fetchTag = string(typedOpt)
}
}
args := []string{}
@@ -524,6 +527,9 @@
if remote != "" {
args = append(args, remote)
}
+ if fetchTag != "" {
+ args = append(args, "tag", fetchTag)
+ }
if refspec != "" {
args = append(args, refspec)
}
diff --git a/gitutil/options.go b/gitutil/options.go
index e598c29..b01705d 100644
--- a/gitutil/options.go
+++ b/gitutil/options.go
@@ -73,6 +73,10 @@
func (TagsOpt) fetchOpt() {}
+type FetchTagOpt string
+
+func (FetchTagOpt) fetchOpt() {}
+
type AllOpt bool
func (AllOpt) fetchOpt() {}
diff --git a/project/project.go b/project/project.go
index 99d9119..de39df0 100644
--- a/project/project.go
+++ b/project/project.go
@@ -1424,7 +1424,21 @@
return err
}
git := gitutil.New(jirix, gitutil.RootDirOpt(project.Path))
- return git.CheckoutBranch(revision, gitutil.DetachOpt(true), gitutil.ForceOpt(forceCheckout))
+ err = git.CheckoutBranch(revision, gitutil.DetachOpt(true), gitutil.ForceOpt(forceCheckout))
+ if err == nil {
+ return nil
+ }
+ if project.Revision != "" && project.Revision != "HEAD" {
+ //might be a tag
+ if err2 := fetch(jirix, project.Path, "origin", gitutil.FetchTagOpt(project.Revision)); err2 != nil {
+ // error while fetching tag, return original err and debug log this err
+ jirix.Logger.Debugf("Error while fetching tag for project %s (%s): %s\n\n", project.Name, project.Path, err2)
+ return err
+ } else {
+ return git.CheckoutBranch(revision, gitutil.DetachOpt(true), gitutil.ForceOpt(forceCheckout))
+ }
+ }
+ return err
}
func tryRebase(jirix *jiri.X, project Project, branch string) (bool, error) {
diff --git a/project/project_test.go b/project/project_test.go
index 0c4c3d6..c9dfc0f 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -1578,6 +1578,62 @@
checkJiriRevFiles(t, fake.X, localProjects[1])
}
+func TestTagNotContainedInBranch(t *testing.T) {
+ localProjects, fake, cleanup := setupUniverse(t)
+ defer cleanup()
+ if err := fake.UpdateUniverse(false); err != nil {
+ t.Fatal(err)
+ }
+
+ gitRemote := gitutil.New(fake.X, gitutil.UserNameOpt("John Doe"), gitutil.UserEmailOpt("john.doe@example.com"), gitutil.RootDirOpt(fake.Projects[localProjects[1].Name]))
+ gr := git.NewGit(fake.Projects[localProjects[1].Name])
+ if err := gitRemote.CreateAndCheckoutBranch("non-master"); err != nil {
+ t.Fatal(err)
+ }
+
+ // Create commits in remote repo
+ writeReadme(t, fake.X, fake.Projects[localProjects[1].Name], "non-master commit")
+ writeFile(t, fake.X, fake.Projects[localProjects[1].Name], "file1", "file1")
+ file1CommitRev, _ := gr.CurrentRevision()
+ if err := gr.CreateLightweightTag("testtag"); err != nil {
+ t.Fatalf("Creating tag: %s", err)
+
+ }
+ if err := gitRemote.CheckoutBranch("master"); err != nil {
+ t.Fatal(err)
+ }
+ if err := gitRemote.DeleteBranch("non-master", gitutil.ForceOpt(true)); err != nil {
+ t.Fatal(err)
+ }
+
+ m, err := fake.ReadRemoteManifest()
+ if err != nil {
+ t.Fatal(err)
+ }
+ projects := []project.Project{}
+ for _, p := range m.Projects {
+ if p.Name == localProjects[1].Name {
+ p.Revision = "testtag"
+ }
+ projects = append(projects, p)
+ }
+ m.Projects = projects
+ if err := fake.WriteRemoteManifest(m); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := fake.UpdateUniverse(false); err != nil {
+ t.Fatal(err)
+ }
+
+ gl := git.NewGit(localProjects[1].Path)
+ // It rebased properly and pulled latest changes
+ localRev, _ := gl.CurrentRevision()
+ if file1CommitRev != localRev {
+ t.Fatalf("Current commit is %v, it should be %v\n", localRev, file1CommitRev)
+ }
+}
+
// TestCheckoutSnapshotUrl tests checking out snapshot functionality from a url
func TestCheckoutSnapshotUrl(t *testing.T) {
testCheckoutSnapshot(t, true)