Merge "jiri: Return proper errors from hooks and git-hooks"
diff --git a/gitutil/.api b/gitutil/.api
index c86d89c..dcd0a23 100644
--- a/gitutil/.api
+++ b/gitutil/.api
@@ -44,6 +44,7 @@
pkg gitutil, method (*Git) Push(string, string, ...PushOpt) error
pkg gitutil, method (*Git) Rebase(string) error
pkg gitutil, method (*Git) RebaseAbort() error
+pkg gitutil, method (*Git) RefExistsOnBranch(string, string) bool
pkg gitutil, method (*Git) RemoteUrl(string) (string, error)
pkg gitutil, method (*Git) Remove(...string) error
pkg gitutil, method (*Git) RemoveUntrackedFiles() error
diff --git a/gitutil/git.go b/gitutil/git.go
index ee77840..c6a6675 100644
--- a/gitutil/git.go
+++ b/gitutil/git.go
@@ -536,6 +536,15 @@
return g.run("rebase", "--abort")
}
+// RefExistsOnBranch returns true if the given ref exists on the given branch.
+func (g *Git) RefExistsOnBranch(ref, branch string) bool {
+ args := []string{"merge-base", "--is-ancestor", ref, branch}
+ if err := g.run(args...); err != nil {
+ return false
+ }
+ return true
+}
+
// Remove removes the given files.
func (g *Git) Remove(fileNames ...string) error {
args := []string{"rm"}
diff --git a/project/project.go b/project/project.go
index d8a8cb8..dc47bdd 100644
--- a/project/project.go
+++ b/project/project.go
@@ -1450,13 +1450,20 @@
}
switch project.Protocol {
case "git":
- // Having a specific revision trumps everything else.
+ git := gitutil.New(jirix.NewSeq())
+ remoteBranch := "origin/" + project.RemoteBranch
+
+ // If a specific revision is set, make sure it exists on the specified
+ // remote branch, and reset to that revision.
if project.Revision != "HEAD" {
- return gitutil.New(jirix.NewSeq()).Reset(project.Revision)
+ if !git.RefExistsOnBranch(project.Revision, remoteBranch) {
+ return fmt.Errorf("revision %q does not exist on remote branch %q", project.Revision, remoteBranch)
+ }
+ return git.Reset(project.Revision)
}
- // If no revision, reset to the configured remote branch, or master
- // if no remote branch.
- return gitutil.New(jirix.NewSeq()).Reset("origin/" + project.RemoteBranch)
+
+ // If no revision is set, reset to the remote branch.
+ return git.Reset(remoteBranch)
default:
return UnsupportedProtocolErr(project.Protocol)
}
@@ -2042,10 +2049,6 @@
if err := s.Chdir(tmpDir).Done(); err != nil {
return err
}
- // TODO(toddw): Why call Reset here, when resetProject is called just below?
- if err := gitutil.New(jirix.NewSeq()).Reset(op.project.Revision); err != nil {
- return err
- }
default:
return UnsupportedProtocolErr(op.project.Protocol)
}
@@ -2170,7 +2173,7 @@
}
func (op moveOperation) String() string {
- return fmt.Sprintf("move project %q located in %q to %q and advance it to %q", op.project.Name, op.source, op.destination, fmtRevision(op.project.Revision))
+ return fmt.Sprintf("move project %q located in %q to %q and advance it to %q of %q", op.project.Name, op.source, op.destination, fmtRevision(op.project.Revision), op.project.RemoteBranch)
}
func (op moveOperation) Test(jirix *jiri.X, updates *fsUpdates) error {
@@ -2214,7 +2217,7 @@
}
func (op updateOperation) String() string {
- return fmt.Sprintf("advance project %q located in %q to %q", op.project.Name, op.source, fmtRevision(op.project.Revision))
+ return fmt.Sprintf("advance project %q located in %q to %q of %q", op.project.Name, op.source, fmtRevision(op.project.Revision), op.project.RemoteBranch)
}
func (op updateOperation) Test(jirix *jiri.X, _ *fsUpdates) error {
diff --git a/project/project_test.go b/project/project_test.go
index a060e36..b4c69af 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -172,7 +172,7 @@
return revision
}
-// Fix the revision in the manifest file.
+// Set the revision in the manifest file.
func setRevisionForProject(t *testing.T, jirix *jiri.X, manifestDir, name, revision string) {
m, err := project.ManifestFromFile(jirix, filepath.Join(manifestDir, "v2", "default"))
if err != nil {
@@ -188,7 +188,28 @@
}
}
if !updated {
- t.Fatalf("failed to fix revision for project %v", name)
+ t.Fatalf("failed to set revision for project %v", name)
+ }
+ commitManifest(t, jirix, m, manifestDir)
+}
+
+// Set the remote branch in the manifest file.
+func setRemoteBranchForProject(t *testing.T, jirix *jiri.X, manifestDir, name, remoteBranch string) {
+ m, err := project.ManifestFromFile(jirix, filepath.Join(manifestDir, "v2", "default"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ updated := false
+ for i, p := range m.Projects {
+ if p.Name == name {
+ p.RemoteBranch = remoteBranch
+ m.Projects[i] = p
+ updated = true
+ break
+ }
+ }
+ if !updated {
+ t.Fatalf("failed to fix remote branch for project %v", name)
}
commitManifest(t, jirix, m, manifestDir)
}
@@ -523,14 +544,26 @@
t.Fatalf("%v", err)
}
- // Commit to a non-master branch of a remote project and check that
- // UpdateUniverse() can update the local project to point to a revision on
- // that branch.
+ // Commit to master and non-master branches of a remote project.
+ nonMasterBranch := "non_master"
writeReadme(t, jirix, remoteProjects[5], "master commit")
- createAndCheckoutBranch(t, jirix, remoteProjects[5], "non_master")
+ createAndCheckoutBranch(t, jirix, remoteProjects[5], nonMasterBranch)
writeReadme(t, jirix, remoteProjects[5], "non master commit")
remoteBranchRevision := currentRevision(t, jirix, remoteProjects[5])
+
+ // Set the revision to the non-master revision, but keep the remote branch
+ // set to master.
setRevisionForProject(t, jirix, remoteManifest, remoteProjects[5], remoteBranchRevision)
+ // Check that UpdateUniverse() fails when updating to a revision that does
+ // not occur on the remote branch (master).
+ if err := project.UpdateUniverse(jirix, true); err == nil {
+ t.Fatalf("expected project.UpdateUniverse() with revision that does not occur on remote branch to fail, but it did not")
+ }
+
+ // Set the project remote branch to the non-master branch with the revision.
+ setRemoteBranchForProject(t, jirix, remoteManifest, remoteProjects[5], nonMasterBranch)
+ // Check that UpdateUniverse() can update the local project to point to a
+ // revision on the non-master remote branch.
if err := project.UpdateUniverse(jirix, true); err != nil {
t.Fatalf("%v", err)
}