diff --git a/gertty/app.py b/gertty/app.py
index a69fab1..6c35934 100644
--- a/gertty/app.py
+++ b/gertty/app.py
@@ -14,6 +14,7 @@
 # under the License.
 
 import argparse
+import datetime
 import dateutil
 import logging
 import os
@@ -602,6 +603,58 @@ class App(object):
             lambda button: self.backScreen())
         self.popup(dialog, min_height=min_height)
 
+    def saveReviews(self, revision_keys, approvals, message, upload, submit):
+        message_keys = []
+        with self.db.getSession() as session:
+            account = session.getAccountByUsername(self.config.username)
+            for revision_key in revision_keys:
+                k = self._saveReview(session, account, revision_key,
+                                     approvals, message, upload, submit)
+                if k:
+                    message_keys.append(k)
+        return message_keys
+
+    def _saveReview(self, session, account, revision_key,
+                    approvals, message, upload, submit):
+        message_key = None
+        revision = session.getRevision(revision_key)
+        change = revision.change
+        draft_approvals = {}
+        for approval in change.draft_approvals:
+            draft_approvals[approval.category] = approval
+
+        categories = set()
+        for label in change.permitted_labels:
+            categories.add(label.category)
+        for category in categories:
+            value = approvals.get(category, 0)
+            approval = draft_approvals.get(category)
+            if not approval:
+                approval = change.createApproval(account, category, 0, draft=True)
+                draft_approvals[category] = approval
+            approval.value = value
+        draft_message = revision.getPendingMessage()
+        if not draft_message:
+            draft_message = revision.getDraftMessage()
+        if not draft_message:
+            if message or upload:
+                draft_message = revision.createMessage(None, account,
+                                                       datetime.datetime.utcnow(),
+                                                       '', draft=True)
+        if draft_message:
+            draft_message.created = datetime.datetime.utcnow()
+            draft_message.message = message
+            draft_message.pending = upload
+            message_key = draft_message.key
+        if upload:
+            change.reviewed = True
+        if submit:
+            change.status = 'SUBMITTED'
+            change.pending_status = True
+            change.pending_status_message = None
+        return message_key
+
+
 
 def version():
     return "Gertty version: %s" % gertty.version.version_info.version_string()
diff --git a/gertty/keymap.py b/gertty/keymap.py
index a1bd006..61c00f0 100644
--- a/gertty/keymap.py
+++ b/gertty/keymap.py
@@ -41,6 +41,7 @@ TOGGLE_REVIEWED = 'toggle reviewed'
 TOGGLE_HIDDEN = 'toggle hidden'
 TOGGLE_STARRED = 'toggle starred'
 TOGGLE_HELD = 'toggle held'
+TOGGLE_MARK = 'toggle process mark'
 REVIEW = 'review'
 DIFF = 'diff'
 LOCAL_CHECKOUT = 'local checkout'
@@ -92,6 +93,7 @@ DEFAULT_KEYMAP = {
     TOGGLE_HIDDEN: 'k',
     TOGGLE_STARRED: '*',
     TOGGLE_HELD: '!',
+    TOGGLE_MARK: '%',
     REVIEW: 'r',
     DIFF: 'd',
     LOCAL_CHECKOUT: 'c',
diff --git a/gertty/palette.py b/gertty/palette.py
index 7a4ab11..fae8756 100644
--- a/gertty/palette.py
+++ b/gertty/palette.py
@@ -86,6 +86,8 @@ DEFAULT_PALETTE={
     'focused-starred-change': ['light cyan,standout', ''],
     'held-change': ['light red', ''],
     'focused-held-change': ['light red,standout', ''],
+    'marked-change': ['dark cyan', ''],
+    'focused-marked-change': ['dark cyan,standout', ''],
     }
 
 # A delta from the default palette
diff --git a/gertty/view/change.py b/gertty/view/change.py
index 659714f..7f401df 100644
--- a/gertty/view/change.py
+++ b/gertty/view/change.py
@@ -84,10 +84,9 @@ class CherryPickDialog(urwid.WidgetWrap):
 
 class ReviewDialog(urwid.WidgetWrap):
     signals = ['submit', 'save', 'cancel']
-    def __init__(self, revision_row):
-        self.revision_row = revision_row
-        self.change_view = revision_row.change_view
-        self.app = self.change_view.app
+    def __init__(self, app, revision_key):
+        self.revision_key = revision_key
+        self.app = app
         save_button = mywid.FixedButton(u'Save')
         submit_button = mywid.FixedButton(u'Save and Submit')
         cancel_button = mywid.FixedButton(u'Cancel')
@@ -98,11 +97,6 @@ class ReviewDialog(urwid.WidgetWrap):
         urwid.connect_signal(cancel_button, 'click',
             lambda button:self._emit('cancel'))
 
-        buttons = [('pack', save_button)]
-        if revision_row.can_submit:
-            buttons.append(('pack', submit_button))
-        buttons.append(('pack', cancel_button))
-        buttons = urwid.Columns(buttons, dividechars=2)
         rows = []
         categories = []
         values = {}
@@ -110,8 +104,13 @@ class ReviewDialog(urwid.WidgetWrap):
         self.button_groups = {}
         message = ''
         with self.app.db.getSession() as session:
-            revision = session.getRevision(self.revision_row.revision_key)
+            revision = session.getRevision(self.revision_key)
             change = revision.change
+            buttons = [('pack', save_button)]
+            if revision.can_submit:
+                buttons.append(('pack', submit_button))
+            buttons.append(('pack', cancel_button))
+            buttons = urwid.Columns(buttons, dividechars=2)
             if revision == change.revisions[-1]:
                 for label in change.labels:
                     d = descriptions.setdefault(label.category, {})
@@ -174,15 +173,14 @@ class ReviewDialog(urwid.WidgetWrap):
         fill = urwid.Filler(pile, valign='top')
         super(ReviewDialog, self).__init__(urwid.LineBox(fill, 'Review'))
 
-    def save(self, upload=False, submit=False):
+    def getValues(self):
         approvals = {}
         for category, group in self.button_groups.items():
             for button in group:
                 if button.state:
                     approvals[category] = button._value
         message = self.message.edit_text.strip()
-        self.change_view.saveReview(self.revision_row.revision_key, approvals,
-                                    message, upload, submit)
+        return (approvals, message)
 
     def keypress(self, size, key):
         r = super(ReviewDialog, self).keypress(size, key)
@@ -201,7 +199,8 @@ class ReviewButton(mywid.FixedButton):
             lambda button: self.openReview())
 
     def openReview(self):
-        self.dialog = ReviewDialog(self.revision_row)
+        self.dialog = ReviewDialog(self.change_view.app,
+                                   self.revision_row.revision_key)
         urwid.connect_signal(self.dialog, 'save',
             lambda button: self.closeReview(True, False))
         urwid.connect_signal(self.dialog, 'submit',
@@ -213,7 +212,9 @@ class ReviewButton(mywid.FixedButton):
                                    min_width=60, min_height=20)
 
     def closeReview(self, upload, submit):
-        self.dialog.save(upload, submit)
+        approvals, message = self.dialog.getValues()
+        self.change_view.saveReview(self.revision_row.revision_key, approvals,
+                                    message, upload, submit)
         self.change_view.app.backScreen()
 
 class RevisionRow(urwid.WidgetWrap):
@@ -1037,46 +1038,10 @@ class ChangeView(urwid.WidgetWrap):
         self.saveReview(row.revision_key, approvals, '', True, submit)
 
     def saveReview(self, revision_key, approvals, message, upload, submit):
-        message_key = None
-        with self.app.db.getSession() as session:
-            account = session.getAccountByUsername(self.app.config.username)
-            revision = session.getRevision(revision_key)
-            change = revision.change
-            draft_approvals = {}
-            for approval in change.draft_approvals:
-                draft_approvals[approval.category] = approval
-
-            categories = set()
-            for label in change.permitted_labels:
-                categories.add(label.category)
-            for category in categories:
-                value = approvals.get(category, 0)
-                approval = draft_approvals.get(category)
-                if not approval:
-                    approval = change.createApproval(account, category, 0, draft=True)
-                    draft_approvals[category] = approval
-                approval.value = value
-            draft_message = revision.getPendingMessage()
-            if not draft_message:
-                draft_message = revision.getDraftMessage()
-            if not draft_message:
-                if message or upload:
-                    draft_message = revision.createMessage(None, account,
-                                                           datetime.datetime.utcnow(),
-                                                           '', draft=True)
-            if draft_message:
-                draft_message.created = datetime.datetime.utcnow()
-                draft_message.message = message
-                draft_message.pending = upload
-                message_key = draft_message.key
-            if upload:
-                change.reviewed = True
-            if submit:
-                change.status = 'SUBMITTED'
-                change.pending_status = True
-                change.pending_status_message = None
-        # Outside of db session
+        message_keys = self.app.saveReviews([revision_key], approvals,
+                                            message, upload, submit)
         if upload:
-            self.app.sync.submitTask(
-                sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY))
+            for message_key in message_keys:
+                self.app.sync.submitTask(
+                    sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY))
         self.refresh()
diff --git a/gertty/view/change_list.py b/gertty/view/change_list.py
index 8475207..430465a 100644
--- a/gertty/view/change_list.py
+++ b/gertty/view/change_list.py
@@ -45,12 +45,14 @@ class ThreadStack(object):
     def countChildren(self):
         return [len(x[1]) for x in self.stack]
 
+
 class ChangeRow(urwid.Button):
     change_focus_map = {None: 'focused',
                         'unreviewed-change': 'focused-unreviewed-change',
                         'reviewed-change': 'focused-reviewed-change',
                         'starred-change': 'focused-starred-change',
                         'held-change': 'focused-held-change',
+                        'marked-change': 'focused-marked-change',
                         'positive-label': 'focused-positive-label',
                         'negative-label': 'focused-negative-label',
                         'min-label': 'focused-min-label',
@@ -70,6 +72,7 @@ class ChangeRow(urwid.Button):
         self.updated = urwid.Text(u'')
         self.project = urwid.Text(u'', wrap='clip')
         self.owner = urwid.Text(u'', wrap='clip')
+        self.mark = False
         cols = [(6, self.number), ('weight', 4, self.subject)]
         if project:
             cols.append(('weight', 1, self.project))
@@ -99,6 +102,9 @@ class ChangeRow(urwid.Button):
         if change.held:
             flag = '!'
             style = 'held-change'
+        if self.mark:
+            flag = '%'
+            style = 'marked-change'
         subject = flag + subject
         self.row_style.set_attr_map({None: style})
         self.subject.set_text(subject)
@@ -107,6 +113,7 @@ class ChangeRow(urwid.Button):
         self.owner.set_text(change.owner_name)
         self.project_name = change.project.name
         self.commit_sha = change.revisions[-1].commit
+        self.current_revision_key = change.revisions[-1].key
         today = self.app.time(datetime.datetime.utcnow()).date()
         updated_time = self.app.time(change.updated)
         if today == updated_time.date():
@@ -171,8 +178,12 @@ class ChangeListView(urwid.WidgetWrap):
              "Toggle the reviewed flag for the currently selected change"),
             (key(keymap.TOGGLE_STARRED),
              "Toggle the starred flag for the currently selected change"),
+            (key(keymap.TOGGLE_MARK),
+             "Toggle the process mark for the currently selected change"),
             (key(keymap.REFRESH),
              refresh_help),
+            (key(keymap.REVIEW),
+             "Leave reviews for the marked changes"),
             (key(keymap.SORT_BY_NUMBER),
              "Sort changes by number"),
             (key(keymap.SORT_BY_UPDATED),
@@ -458,6 +469,20 @@ class ChangeListView(urwid.WidgetWrap):
                 change = session.getChange(change_key)
                 row.update(change, self.categories)
             return None
+        if keymap.TOGGLE_MARK in commands:
+            if not len(self.listbox.body):
+                return None
+            pos = self.listbox.focus_position
+            change_key = self.listbox.body[pos].change_key
+            row = self.change_rows[change_key]
+            row.mark = not row.mark
+            with self.app.db.getSession() as session:
+                change = session.getChange(change_key)
+                row.update(change, self.categories)
+            if pos < len(self.listbox.body)-1:
+                pos += 1
+                self.listbox.focus_position = pos
+            return None
         if keymap.REFRESH in commands:
             if self.project_key:
                 self.app.sync.submitTask(
@@ -467,6 +492,11 @@ class ChangeListView(urwid.WidgetWrap):
                     sync.SyncSubscribedProjectsTask(sync.HIGH_PRIORITY))
             self.app.status.update()
             return None
+        if keymap.REVIEW in commands:
+            marked = [row for row in self.change_rows.values() if row.mark]
+            if marked:
+                self.openReview(marked)
+            return None
         if keymap.SORT_BY_NUMBER in commands:
             if not len(self.listbox.body):
                 return None
@@ -513,3 +543,27 @@ class ChangeListView(urwid.WidgetWrap):
             self.app.changeScreen(view)
         except gertty.view.DisplayError as e:
             self.app.error(e.message)
+
+    def openReview(self, rows):
+        dialog = view_change.ReviewDialog(self.app, rows[0].current_revision_key)
+        urwid.connect_signal(dialog, 'save',
+            lambda button: self.closeReview(dialog, rows, True, False))
+        urwid.connect_signal(dialog, 'submit',
+            lambda button: self.closeReview(dialog, rows, True, True))
+        urwid.connect_signal(dialog, 'cancel',
+            lambda button: self.closeReview(dialog, rows, False, False))
+        self.app.popup(dialog,
+                       relative_width=50, relative_height=75,
+                       min_width=60, min_height=20)
+
+    def closeReview(self, dialog, rows, upload, submit):
+        approvals, message = dialog.getValues()
+        revision_keys = [row.current_revision_key for row in rows]
+        message_keys = self.app.saveReviews(revision_keys, approvals,
+                                            message, upload, submit)
+        if upload:
+            for message_key in message_keys:
+                self.app.sync.submitTask(
+                    sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY))
+        self.refresh()
+        self.app.backScreen()