Check out the appropriate branch in executor

This mimics the behavior of Zuul cloner and allows the user to
specify what branches of each repo should be checked out in the
jobdir at the start of the job.

(Of course, the job is free to check out other branches as needed;
all of them will have the appropriate future state.)

Change-Id: I93af5c49cb0404944636c7e63d203cdb564b267c
diff --git a/zuul/executor/client.py b/zuul/executor/client.py
index 1d9cd72..5a1820e 100644
--- a/zuul/executor/client.py
+++ b/zuul/executor/client.py
@@ -261,6 +261,10 @@
         params['timeout'] = job.timeout
         params['items'] = merger_items
         params['projects'] = []
+        if hasattr(item.change, 'branch'):
+            params['branch'] = item.change.branch
+        else:
+            params['branch'] = None
         params['override_branch'] = job.override_branch
         params['repo_state'] = item.current_build_set.repo_state
 
diff --git a/zuul/executor/server.py b/zuul/executor/server.py
index e175fbf..b3dc9f2 100644
--- a/zuul/executor/server.py
+++ b/zuul/executor/server.py
@@ -587,6 +587,15 @@
                 # a work complete result, don't run any jobs
                 return
 
+        for project in args['projects']:
+            repo = repos[project['canonical_name']]
+            self.checkoutBranch(repo,
+                                project['name'],
+                                args['branch'],
+                                args['override_branch'],
+                                project['override_branch'],
+                                project['default_branch'])
+
         # Delete the origin remote from each repo we set up since
         # it will not be valid within the jobs.
         for repo in repos.values():
@@ -641,6 +650,30 @@
             repo.setRef('refs/heads/' + branch, commit)
         return True
 
+    def checkoutBranch(self, repo, project_name, zuul_branch,
+                       job_branch, project_override_branch,
+                       project_default_branch):
+        branches = repo.getBranches()
+        if project_override_branch in branches:
+            self.log.info("Checking out %s project override branch %s",
+                          project_name, project_override_branch)
+            repo.checkoutLocalBranch(project_override_branch)
+        elif job_branch in branches:
+            self.log.info("Checking out %s job branch %s",
+                          project_name, job_branch)
+            repo.checkoutLocalBranch(job_branch)
+        elif zuul_branch and zuul_branch in branches:
+            self.log.info("Checking out %s zuul branch %s",
+                          project_name, zuul_branch)
+            repo.checkoutLocalBranch(zuul_branch)
+        elif project_default_branch in branches:
+            self.log.info("Checking out %s project default branch %s",
+                          project_name, project_default_branch)
+            repo.checkoutLocalBranch(project_default_branch)
+        else:
+            raise Exception("Project %s does not have the default branch %s" %
+                            (project_name, project_default_branch))
+
     def runPlaybooks(self, args):
         result = None
 
diff --git a/zuul/merger/merger.py b/zuul/merger/merger.py
index 4df24aa..b0183d0 100644
--- a/zuul/merger/merger.py
+++ b/zuul/merger/merger.py
@@ -134,6 +134,10 @@
         origin = repo.remotes.origin
         return branch in origin.refs
 
+    def getBranches(self):
+        repo = self.createRepoObject()
+        return [x.name for x in repo.heads]
+
     def getCommitFromRef(self, refname):
         repo = self.createRepoObject()
         if refname not in repo.refs: