Merge "Delay initialization of local repos"
diff --git a/tests/fixtures/layout-delayed-repo-init.yaml b/tests/fixtures/layout-delayed-repo-init.yaml
new file mode 100644
index 0000000..79f9898
--- /dev/null
+++ b/tests/fixtures/layout-delayed-repo-init.yaml
@@ -0,0 +1,44 @@
+pipelines:
+  - name: check
+    manager: IndependentPipelineManager
+    trigger:
+      - event: patchset-created
+    success:
+      verified: 1
+    failure:
+      verified: -1
+
+  - name: post
+    manager: IndependentPipelineManager
+    trigger:
+      - event: ref-updated
+        ref: ^(?!refs/).*$
+
+  - name: gate
+    manager: DependentPipelineManager
+    failure-message: Build failed.  For information on how to proceed, see http://wiki.example.org/Test_Failures
+    trigger:
+      - event: comment-added
+        approval:
+          - approved: 1
+    success:
+      verified: 2
+      submit: true
+    failure:
+      verified: -2
+    start:
+      verified: 0
+    precedence: high
+
+projects:
+  - name: org/new-project
+    check:
+      - project-merge:
+        - project-test1
+        - project-test2
+    gate:
+      - project-merge:
+        - project-test1
+        - project-test2
+    post:
+      - project-post
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 4e59db8..85eb8ad 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -148,7 +148,11 @@
                 f.close()
                 repo.index.add([fn])
 
-        return repo.index.commit(msg)
+        r = repo.index.commit(msg)
+        repo.head.reference = 'master'
+        repo.head.reset(index=True, working_tree=True)
+        repo.git.clean('-x', '-f', '-d')
+        return r
 
     def addPatchset(self, files=[], large=False):
         self.latest_patchset += 1
@@ -2445,4 +2449,23 @@
                          'SUCCESS')
         self.assertEqual(A.data['status'], 'MERGED')
         self.assertEqual(A.reported, 2)
-        self.assertEmptyQueues()
+
+    def test_delayed_repo_init(self):
+        self.config.set('zuul', 'layout_config',
+                        'tests/fixtures/layout-delayed-repo-init.yaml')
+        self.sched.reconfigure(self.config)
+
+        self.init_repo("org/new-project")
+        A = self.fake_gerrit.addFakeChange('org/new-project', 'master', 'A')
+
+        A.addApproval('CRVW', 2)
+        self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+        self.waitUntilSettled()
+        self.assertEqual(self.getJobFromHistory('project-merge').result,
+                         'SUCCESS')
+        self.assertEqual(self.getJobFromHistory('project-test1').result,
+                         'SUCCESS')
+        self.assertEqual(self.getJobFromHistory('project-test2').result,
+                         'SUCCESS')
+        self.assertEqual(A.data['status'], 'MERGED')
+        self.assertEqual(A.reported, 2)
diff --git a/zuul/merger.py b/zuul/merger.py
index 8ab3ee8..94db499 100644
--- a/zuul/merger.py
+++ b/zuul/merger.py
@@ -29,24 +29,37 @@
     def __init__(self, remote, local, email, username):
         self.remote_url = remote
         self.local_path = local
-        self._ensure_cloned()
-        self.repo = git.Repo(self.local_path)
-        if email:
-            self.repo.config_writer().set_value('user', 'email', email)
-        if username:
-            self.repo.config_writer().set_value('user', 'name', username)
-        self.repo.config_writer().write()
+        self.email = email
+        self.username = username
+        self._initialized = False
+        try:
+            self._ensure_cloned()
+        except:
+            self.log.exception("Unable to initialize repo for %s" % remote)
 
     def _ensure_cloned(self):
+        if self._initialized:
+            return
         if not os.path.exists(self.local_path):
             self.log.debug("Cloning from %s to %s" % (self.remote_url,
                                                       self.local_path))
             git.Repo.clone_from(self.remote_url, self.local_path)
+        self.repo = git.Repo(self.local_path)
+        if self.email:
+            self.repo.config_writer().set_value('user', 'email',
+                                                self.email)
+        if self.username:
+            self.repo.config_writer().set_value('user', 'name',
+                                                self.username)
+        self.repo.config_writer().write()
+        self._initialized = True
 
     def recreateRepoObject(self):
+        self._ensure_cloned()
         self.repo = git.Repo(self.local_path)
 
     def reset(self):
+        self._ensure_cloned()
         self.log.debug("Resetting repository %s" % self.local_path)
         self.update()
         origin = self.repo.remotes.origin
@@ -64,21 +77,25 @@
         return self.repo.heads[branch]
 
     def checkout(self, ref):
+        self._ensure_cloned()
         self.log.debug("Checking out %s" % ref)
         self.repo.head.reference = ref
         self.repo.head.reset(index=True, working_tree=True)
 
     def cherryPick(self, ref):
+        self._ensure_cloned()
         self.log.debug("Cherry-picking %s" % ref)
         self.fetch(ref)
         self.repo.git.cherry_pick("FETCH_HEAD")
 
     def merge(self, ref):
+        self._ensure_cloned()
         self.log.debug("Merging %s" % ref)
         self.fetch(ref)
         self.repo.git.merge("FETCH_HEAD")
 
     def fetch(self, ref):
+        self._ensure_cloned()
         # The git.remote.fetch method may read in git progress info and
         # interpret it improperly causing an AssertionError. Because the
         # data was fetched properly subsequent fetches don't seem to fail.
@@ -97,16 +114,19 @@
         self.repo = git.Repo(self.local_path)
 
     def createZuulRef(self, ref, commit='HEAD'):
+        self._ensure_cloned()
         self.log.debug("CreateZuulRef %s at %s " % (ref, commit))
         ref = ZuulReference.create(self.repo, ref, commit)
         return ref.commit
 
     def push(self, local, remote):
+        self._ensure_cloned()
         self.log.debug("Pushing %s:%s to %s " % (local, remote,
                                                  self.remote_url))
         self.repo.remotes.origin.push('%s:%s' % (local, remote))
 
     def update(self):
+        self._ensure_cloned()
         self.log.debug("Updating repository %s" % self.local_path)
         origin = self.repo.remotes.origin
         origin.update()
@@ -151,7 +171,7 @@
 
             self.repos[project] = repo
         except:
-            self.log.exception("Unable to initialize repo for %s" % project)
+            self.log.exception("Unable to add project %s" % project)
 
     def getRepo(self, project):
         r = self.repos.get(project, None)