git: Use FindCheckouts() when determining if a project supports content merging

A project may map to multiple checkouts. When checking if a project
supports content merging, we only care for the version that is actually
patchable.

Change the handling to work on the patchable checkout rather than
the projects dictionary. Get rid of the simple projects dictionary
since project -> attributes is not a 1:1 mapping any more.

BUG=chromium:330225
TEST=buildbot/run_tests
TEST=trybot on pre-cq-group, lumpy-{paladin,canary}, paladin-master,
     link-tot-paladin

Change-Id: I1a29854deb700d99c7cbfbe295b60e539331ce16
Reviewed-on: https://chromium-review.googlesource.com/185604
Tested-by: Gaurav Shah <gauravsh@chromium.org>
Reviewed-by: David James <davidjames@chromium.org>
Commit-Queue: Gaurav Shah <gauravsh@chromium.org>
diff --git a/lib/cros_build_lib_unittest.py b/lib/cros_build_lib_unittest.py
index 5acfcae..f23662a 100755
--- a/lib/cros_build_lib_unittest.py
+++ b/lib/cros_build_lib_unittest.py
@@ -882,7 +882,7 @@
           <remote name="foon" fetch="http://localhost" />
         </manifest>""")
     manifest = git.ManifestCheckout(self.tempdir)
-    self.assertEqual(list(manifest.projects), ['monkeys'])
+    self.assertEqual(list(manifest.checkouts_by_name), ['monkeys'])
     self.assertEqual(list(manifest.remotes), ['foon'])
 
   # pylint: disable=E1101
diff --git a/lib/git.py b/lib/git.py
index c15873a..7b5dc24 100644
--- a/lib/git.py
+++ b/lib/git.py
@@ -342,7 +342,6 @@
     """
 
     self.default = {}
-    self.projects = {}
     self.checkouts_by_path = {}
     self.checkouts_by_name = {}
     self.remotes = {}
@@ -370,7 +369,6 @@
       attrs.setdefault('alias', attrs['name'])
       self.remotes[attrs['name']] = attrs
     elif name == 'project':
-      self.projects[attrs['name']] = attrs
       self.checkouts_by_path[attrs['path']] = attrs
       self.checkouts_by_name.setdefault(attrs['name'], []).append(attrs)
     elif name == 'manifest':
@@ -534,11 +532,6 @@
       manifest_path = os.path.join(root, '.repo', 'manifest.xml')
     return root, manifest_path
 
-  def AssertProjectIsPushable(self, project):
-    """Verify that the |project| has push_* attributes populated."""
-    for checkout in self.FindCheckouts(project):
-      checkout.AssertPushable()
-
   def ProjectIsContentMerging(self, project):
     """Returns whether the given project has content merging enabled in git.
 
@@ -550,16 +543,21 @@
       True if content merging is enabled.
 
     Raises:
+      AssertionError: If no patchable checkout was found or if the patchable
+        checkout does not have a pushable project remote.
       RunCommandError: If the branch can't be fetched due to network
         conditions or if this was invoked against a <gerrit-2.2 server,
         or a mirror that has refs/meta/config stripped from it.
     """
     result = self._content_merging.get(project)
     if result is None:
-      self.AssertProjectIsPushable(project)
-      data = self.projects[project]
-      self._content_merging[project] = result = _GitRepoIsContentMerging(
-          data['local_path'], data['push_remote'])
+      checkouts = self.FindCheckouts(project, only_patchable=True)
+      if len(checkouts) < 1:
+        raise AssertionError('No patchable checkout of %s was found' % project)
+      for checkout in checkouts:
+        checkout.AssertPushable()
+        self._content_merging[project] = result = _GitRepoIsContentMerging(
+            checkout['local_path'], checkout['push_remote'])
     return result
 
   def FindCheckouts(self, project, branch=None, only_patchable=False):
@@ -653,9 +651,8 @@
   def _FinalizeAllProjectData(self):
     """Rewrite projects mixing defaults in and adding our attributes."""
     Manifest._FinalizeAllProjectData(self)
-    for d in (self.projects, self.checkouts_by_path):
-      for key, value in d.iteritems():
-        d[key] = ProjectCheckout(value)
+    for key, value in self.checkouts_by_path.iteritems():
+      self.checkouts_by_path[key] = ProjectCheckout(value)
     for key, value in self.checkouts_by_name.iteritems():
       self.checkouts_by_name[key] = \
           [ProjectCheckout(x) for x in value]
diff --git a/scripts/cros_list_modified_packages.py b/scripts/cros_list_modified_packages.py
old mode 100644
new mode 100755
index 3dbd5a1..77f3875
--- a/scripts/cros_list_modified_packages.py
+++ b/scripts/cros_list_modified_packages.py
@@ -59,7 +59,7 @@
     """
     manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
     self._tasks = []
-    for project in set(projects).intersection(manifest.projects):
+    for project in set(projects).intersection(manifest.checkouts_by_name):
       for checkout in manifest.FindCheckouts(project):
         self._tasks.append((project, checkout.GetPath(absolute=True)))
     self._result_queue = multiprocessing.Queue(len(self._tasks))