Remove metajobs

The upcoming inheritance makes this unecessary.

Change-Id: I1c96ea60c18746b141ed27c13c991fba5599c5c6
diff --git a/tests/test_model.py b/tests/test_model.py
index 2711618..d4c7880 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -39,11 +39,6 @@
         change.files = ['foo']
         self.assertTrue(self.job.changeMatches(change))
 
-    def test_copy_retains_skip_if(self):
-        job = model.Job('job')
-        job.copy(self.job)
-        self.assertTrue(job.skip_if_matcher)
-
     def _assert_job_booleans_are_not_none(self, job):
         self.assertIsNotNone(job.voting)
         self.assertIsNotNone(job.hold_following_changes)
@@ -51,14 +46,3 @@
     def test_job_sets_defaults_for_boolean_attributes(self):
         job = model.Job('job')
         self._assert_job_booleans_are_not_none(job)
-
-    def test_metajob_does_not_set_defaults_for_boolean_attributes(self):
-        job = model.Job('^job')
-        self.assertIsNone(job.voting)
-        self.assertIsNone(job.hold_following_changes)
-
-    def test_metajob_copy_does_not_set_undefined_boolean_attributes(self):
-        job = model.Job('job')
-        metajob = model.Job('^job')
-        job.copy(metajob)
-        self._assert_job_booleans_are_not_none(job)
diff --git a/zuul/model.py b/zuul/model.py
index e06c755..2893d6f 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -433,7 +433,6 @@
 
 class Job(object):
     def __init__(self, name):
-        # If you add attributes here, be sure to add them to the copy method.
         self.name = name
         self.queue_name = None
         self.failure_message = None
@@ -441,20 +440,15 @@
         self.failure_pattern = None
         self.success_pattern = None
         self.parameter_function = None
-        # A metajob should only supply values for attributes that have
-        # been explicitly provided, so avoid setting boolean defaults.
-        if self.is_metajob:
-            self.hold_following_changes = None
-            self.voting = None
-        else:
-            self.hold_following_changes = False
-            self.voting = True
+        self.hold_following_changes = False
+        self.voting = True
         self.branches = []
         self._branches = []
         self.files = []
         self._files = []
         self.skip_if_matcher = None
         self.swift = {}
+        self.parent = None
 
     def __str__(self):
         return self.name
@@ -462,37 +456,6 @@
     def __repr__(self):
         return '<Job %s>' % (self.name)
 
-    @property
-    def is_metajob(self):
-        return self.name.startswith('^')
-
-    def copy(self, other):
-        if other.failure_message:
-            self.failure_message = other.failure_message
-        if other.success_message:
-            self.success_message = other.success_message
-        if other.failure_pattern:
-            self.failure_pattern = other.failure_pattern
-        if other.success_pattern:
-            self.success_pattern = other.success_pattern
-        if other.parameter_function:
-            self.parameter_function = other.parameter_function
-        if other.branches:
-            self.branches = other.branches[:]
-            self._branches = other._branches[:]
-        if other.files:
-            self.files = other.files[:]
-            self._files = other._files[:]
-        if other.skip_if_matcher:
-            self.skip_if_matcher = other.skip_if_matcher.copy()
-        if other.swift:
-            self.swift.update(other.swift)
-        # Only non-None values should be copied for boolean attributes.
-        if other.hold_following_changes is not None:
-            self.hold_following_changes = other.hold_following_changes
-        if other.voting is not None:
-            self.voting = other.voting
-
     def changeMatches(self, change):
         matches_branch = False
         for branch in self.branches:
@@ -1325,21 +1288,12 @@
         self.projects = {}
         self.pipelines = OrderedDict()
         self.jobs = {}
-        self.metajobs = []
 
     def getJob(self, name):
         if name in self.jobs:
             return self.jobs[name]
         job = Job(name)
-        if job.is_metajob:
-            regex = re.compile(name)
-            self.metajobs.append((regex, job))
-        else:
-            # Apply attributes from matching meta-jobs
-            for regex, metajob in self.metajobs:
-                if regex.match(name):
-                    job.copy(metajob)
-            self.jobs[name] = job
+        self.jobs[name] = job
         return job
 
 
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 268e78b..979f93e 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -567,10 +567,6 @@
                     config_jobs = config_project[pipeline.name]
                     add_jobs(job_tree, config_jobs)
 
-        # All jobs should be defined at this point, get rid of
-        # metajobs so that getJob isn't doing anything weird.
-        layout.metajobs = []
-
         for pipeline in layout.pipelines.values():
             pipeline.manager._postConfig(layout)