Merge "Add job option to hold following changes."
diff --git a/zuul/model.py b/zuul/model.py
index 9f3bae5..86fdc5c 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -332,6 +332,19 @@
     def __repr__(self):
         return '<Change 0x%x %s>' % (id(self), self._id())
 
+    def equals(self, other):
+        if self.number:
+            if (self.number == other.number and
+                self.patchset == other.patchset):
+                return True
+            return False
+        if self.ref:
+            if (self.ref == other.ref and
+                self.newrew == other.newrev):
+                return True
+            return False
+        return False
+
     def _filterJobs(self, jobs):
         return filter(lambda job: job.eventMatches(self.event), jobs)
 
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 553d0f5..232804d 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -404,7 +404,16 @@
                 return True
         return False
 
+    def isChangeAlreadyInQueue(self, change):
+        for c in self.getChangesInQueue():
+            if change.equals(c):
+                return True
+        return False
+
     def addChange(self, change):
+        if self.isChangeAlreadyInQueue(change):
+            self.log.debug("Change %s is already in queue, ignoring" % change)
+            return
         self.log.debug("Adding change %s" % change)
         if self.start_action:
             try:
@@ -514,11 +523,15 @@
         self.updateBuildDescriptions(change.current_build_set)
         return ret
 
-    def formatStatusHTML(self):
+    def getChangesInQueue(self):
         changes = []
         for build, change in self.building_jobs.items():
             if change not in changes:
                 changes.append(change)
+        return changes
+
+    def formatStatusHTML(self):
+        changes = self.getChangesInQueue()
         ret = ''
         for change in changes:
             ret += change.formatStatus(html=True)
@@ -578,6 +591,9 @@
         self.log.error("Unable to find change queue for project %s" % project)
 
     def addChange(self, change):
+        if self.isChangeAlreadyInQueue(change):
+            self.log.debug("Change %s is already in queue, ignoring" % change)
+            return
         self.log.debug("Adding change %s" % change)
         change_queue = self.getQueue(change.project)
         if change_queue:
@@ -672,6 +688,12 @@
 possibly reporting" % (change.change_behind, change))
             self.possiblyReportChange(change.change_behind)
 
+    def getChangesInQueue(self):
+        changes = []
+        for shared_queue in self.change_queues:
+            changes.extend(shared_queue.queue)
+        return changes
+
     def formatStatusHTML(self):
         ret = ''
         ret += '\n'