diff --git a/contrib/misc/fill_actions.py b/contrib/misc/fill_actions.py
new file mode 100755
index 0000000000000000000000000000000000000000..6b94252e84cef59f8e9aa92ac78fbe6c229cc430
--- /dev/null
+++ b/contrib/misc/fill_actions.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+
+from optparse import OptionParser
+import json
+import random
+import time
+import datetime
+import re
+import sqlite3
+
+if __name__ == '__main__':
+    parser = OptionParser()
+    parser.add_option("-r", "--requests", dest="requests",
+                  help="Number of review requests", default=1)
+    parser.add_option("-a", "--actions", dest="actions", default=10,
+                  help="Number of actions per review request")
+    parser.add_option("-d", "--days",  dest="days", default=10,
+            help="Maximum time between first and last action in a request")
+    parser.add_option("-s", "--span",  dest="span", default=100,
+                  help="Time in days between first and last request")
+
+    (options, args) = parser.parse_args()
+    span = int(options.span) - int(options.days)
+    now = int(time.time())
+    req_min_sec = now - int(options.span) * 60 * 60 * 24
+    req_max_sec = now - int(options.days) * 60 * 60 * 24
+    conn = sqlite3.connect('../../reviewboard.db')
+    c = conn.cursor()
+    print conn
+    c_count = 0
+    for i in xrange(int(options.requests)):
+        acts = dict(actions=[])
+        request_created = random.randint(req_min_sec, req_max_sec)
+        action_min = request_created
+        action_max = request_created + int(options.days) * 60 * 60 * 24
+        m = 0
+        for j in range(int(options.actions)):
+                ts = random.randint(action_min, action_max)
+                a = dict(verb='the verb', user_id=1, username='admin',
+                         ts=ts)
+                acts['actions'].append(a)
+                if ts > m:
+                    m = ts
+        acts = json.dumps(acts)
+        c_count += 1
+        c.execute('''INSERT INTO "reviews_reviewrequest"
+                (submitter_id, time_added, last_updated, last_review_timestamp, action_feed,
+                    status, public, summary, description, testing_done, bugs_closed,
+                    diffset_history_id, branch)
+                VALUES
+                (1, datetime(), ? , ? , ? ,"P", 1, "la la", "bla bla",
+                "","", 1, "");''', (m, m, acts))
+        if c_count % 100 == 0:
+            conn.commit()
+    conn.commit()
diff --git a/reviewboard/reviews/admin.py b/reviewboard/reviews/admin.py
index 6e805c18d982086b53e3dc13d1b03a63f86ccb40..eea8c73f161ce1841f4ac3779e0f15df722138a1 100644
--- a/reviewboard/reviews/admin.py
+++ b/reviewboard/reviews/admin.py
@@ -96,7 +96,6 @@ class ReviewAdmin(admin.ModelAdmin):
         })
     )
 
-
 class ReviewRequestAdmin(admin.ModelAdmin):
     list_display = ('summary', 'submitter', 'status', 'public', 'last_updated')
     list_filter = ('public', 'status', 'time_added', 'last_updated',
@@ -110,7 +109,7 @@ class ReviewRequestAdmin(admin.ModelAdmin):
             'fields': ('submitter', 'public', 'status',
                        'summary', 'description', 'testing_done',
                        'bugs_closed', 'repository', 'branch', 'changenum',
-                       'time_added')
+                       'time_added', 'action_feed')
         }),
         (_('Reviewers'), {
             'fields': ('target_people', 'target_groups'),
@@ -129,6 +128,8 @@ class ReviewRequestAdmin(admin.ModelAdmin):
         }),
     )
 
+    readonly_fields = ('action_feed', )
+
     actions = [
         'close_submitted',
         'close_discarded',
@@ -240,4 +241,4 @@ admin.site.register(ReviewRequest, ReviewRequestAdmin)
 admin.site.register(ReviewRequestDraft, ReviewRequestDraftAdmin)
 admin.site.register(Screenshot, ScreenshotAdmin)
 admin.site.register(ScreenshotComment, ScreenshotCommentAdmin)
-admin.site.register(FileAttachmentComment, FileAttachmentCommentAdmin)
\ No newline at end of file
+admin.site.register(FileAttachmentComment, FileAttachmentCommentAdmin)
diff --git a/reviewboard/reviews/datagrids.py b/reviewboard/reviews/datagrids.py
index 55aa62cee13f0d9e0250b7a7412ef1811b641261..9a7caf69b6bd203c58467480fa4c05f0ced18498 100644
--- a/reviewboard/reviews/datagrids.py
+++ b/reviewboard/reviews/datagrids.py
@@ -651,6 +651,8 @@ class DashboardDataGrid(ReviewRequestDataGrid):
             self.queryset = ReviewRequest.objects.to_user(
                 user, user, local_site=self.local_site)
             self.title = _(u"All Incoming Review Requests")
+        elif view == 'action_feed':
+            self.title = _(u"Latest Changes")
         else:
             raise Http404
 
diff --git a/reviewboard/reviews/evolutions/__init__.py b/reviewboard/reviews/evolutions/__init__.py
index a9ad1e7a612a5c92cec7e9d1f962269837126662..e563d53554224a5fedf1c8cafdfe2bd6df19a4a7 100644
--- a/reviewboard/reviews/evolutions/__init__.py
+++ b/reviewboard/reviews/evolutions/__init__.py
@@ -11,4 +11,5 @@ SEQUENCE = [
     'default_reviewer_local_site',
     'add_issues_to_comments',
     'file_attachments',
+    'action_feed'
 ]
diff --git a/reviewboard/reviews/models.py b/reviewboard/reviews/models.py
index a73cdfbe582617d69743a57b43914d5247d5c4fc..f270d1efb9810d47f456ba65d46d059349be310b 100644
--- a/reviewboard/reviews/models.py
+++ b/reviewboard/reviews/models.py
@@ -1,5 +1,6 @@
 import os
 import re
+import time
 
 from django.contrib.auth.models import User
 from django.db import models
@@ -9,7 +10,7 @@ from django.utils.html import escape
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _
 from djblets.util.db import ConcurrencyManager
-from djblets.util.fields import CounterField, ModificationTimestampField
+from djblets.util.fields import CounterField, ModificationTimestampField, JSONField
 from djblets.util.misc import get_object_or_none
 from djblets.util.templatetags.djblets_images import crop_image, thumbnail
 
@@ -343,10 +344,12 @@ class ReviewRequest(models.Model):
 
     local_site = models.ForeignKey(LocalSite, blank=True, null=True)
     local_id = models.IntegerField('site-local ID', blank=True, null=True)
+
+    action_feed = JSONField(_("action feed"), default={'actions': []})
 
     # Set this up with the ReviewRequestManager
     objects = ReviewRequestManager()
-
+
     def get_participants(self):
         """
         Returns a list of all people who have been involved in discussing
@@ -677,6 +680,8 @@ class ReviewRequest(models.Model):
 
             self.changedescs.add(changedesc)
             self.status = type
+            self.action_feed['actions'].append(dict(verb='has changed status',
+                user_id=user.pk, username=user.username, ts=time.time()))
             self.save(update_counts=True)
 
             review_request_closed.send(sender=self.__class__, user=user,
@@ -689,6 +694,9 @@ class ReviewRequest(models.Model):
             changedesc.text = description or ""
             changedesc.save()
 
+            self.action_feed['actions'].append(
+                    dict(verb='changed submission description',
+                        user_id=user.pk, username=user.username, ts=time.time()))
             # Needed to renew last-update.
             self.save()
 
@@ -711,7 +719,7 @@ class ReviewRequest(models.Model):
             changedesc = ChangeDescription()
             changedesc.record_field_change('status', self.status,
                                            self.PENDING_REVIEW)
-
+
             if self.status == self.DISCARDED:
                 # A draft is needed if reopening a discarded review request.
                 self.public = False
@@ -723,6 +731,9 @@ class ReviewRequest(models.Model):
                 changedesc.public = True
                 changedesc.save()
                 self.changedescs.add(changedesc)
+                self.action_feed['actions'].append(dict(user_id=user.pk,
+                    username=user.username, verb='reopened the review request',
+                    ts=time.time()))
 
             self.status = self.PENDING_REVIEW
             self.save(update_counts=True)
@@ -778,6 +789,12 @@ class ReviewRequest(models.Model):
             draft.delete()
         else:
             changes = None
+
+        if changes is not None:
+            self.action_feed['actions'].append(
+                dict(verb='updated the review request',
+                     user_id = user.pk, username = user.username,
+                     ts=time.time()))
 
         self.public = True
         self.save(update_counts=True)
@@ -1641,6 +1658,14 @@ class Review(models.Model):
             comment.save()
 
         # Update the last_updated timestamp on the review request.
+        verb  = 'published a '
+
+        if self.is_reply():
+            verb += 'reply'
+        else:
+            verb += 'review'
+        self.review_request.action_feed['actions'].append(dict(user_id=user.pk, 
+            username=user.username, verb=verb, ts=time.time()))
         self.review_request.last_review_timestamp = self.timestamp
         self.review_request.save()
 
@@ -1684,3 +1709,7 @@ class Review(models.Model):
     class Meta:
         ordering = ['timestamp']
         get_latest_by = 'timestamp'
+
+
+
+
diff --git a/reviewboard/reviews/templatetags/reviewtags.py b/reviewboard/reviews/templatetags/reviewtags.py
index 78ab5cef77c8364ae7815f0d18666f412293508a..f821f99514eb0e6f477f17eecd6ec5ba229ba082 100644
--- a/reviewboard/reviews/templatetags/reviewtags.py
+++ b/reviewboard/reviews/templatetags/reviewtags.py
@@ -377,7 +377,9 @@ def dashboard_entry(context, level, text, view, param=None):
     url = None
     group_name = None
 
-    if view == 'to-group':
+    if datagrid is None:
+        show_count = False
+    elif view == 'to-group':
         group_name = param
         count = datagrid.counts['groups'].get(group_name,
             datagrid.counts['starred_groups'].get(group_name, 0))
@@ -392,6 +394,8 @@ def dashboard_entry(context, level, text, view, param=None):
     elif view == "url":
         url = param
         show_count = False
+    elif view == "action_feed":
+        show_count = True
     else:
         raise template.TemplateSyntaxError, \
             "Invalid view type '%s' passed to 'dashboard_entry' tag." % view
diff --git a/reviewboard/reviews/urls.py b/reviewboard/reviews/urls.py
index 351084f1104656a8da29a81f4f7ecacb54aa3090..094c61df5799deda91de1af61f64f35714f44977 100644
--- a/reviewboard/reviews/urls.py
+++ b/reviewboard/reviews/urls.py
@@ -60,4 +60,3 @@ urlpatterns = patterns('reviewboard.reviews.views',
     # Search
     url(r'^search/$', 'search', name="search"),
 )
-
diff --git a/reviewboard/reviews/views.py b/reviewboard/reviews/views.py
index 2eb47a138a5969fdf787d74fc42d2feae19a9b79..a5771ae71e16ee78451f6b1f93e7e01182454350 100644
--- a/reviewboard/reviews/views.py
+++ b/reviewboard/reviews/views.py
@@ -1,5 +1,6 @@
 import logging
 import time
+
 from datetime import datetime
 
 from django.conf import settings
@@ -57,6 +58,7 @@ from reviewboard.scmtools.errors import SCMError
 from reviewboard.site.models import LocalSite
 from reviewboard.webapi.encoder import status_to_string
 
+from operator import itemgetter
 
 #####
 ##### Helper functions
@@ -570,8 +572,8 @@ def group_list(request,
     return grid.render_to_response(template_name)
 
 
-@login_required
-@valid_prefs_required
+#@login_required
+#@valid_prefs_required
 def dashboard(request,
               template_name='reviews/dashboard.html',
               local_site_name=None):
@@ -588,9 +590,9 @@ def dashboard(request,
         * 'watched-groups'
         * 'incoming'
         * 'mine'
+        * 'action_feed'
     """
     view = request.GET.get('view', None)
-
     if local_site_name:
         local_site = get_object_or_404(LocalSite, name=local_site_name)
         if not local_site.is_accessible_by(request.user):
@@ -602,14 +604,48 @@ def dashboard(request,
         # This is special. We want to return a list of groups, not
         # review requests.
         grid = WatchedGroupDataGrid(request, local_site=local_site)
+    elif view == "action_feed" or view is None:
+        view = "action_feed"
+        grid = ActionFeed(request)
     else:
         grid = DashboardDataGrid(request, local_site=local_site)
-
     return grid.render_to_response(template_name, extra_context={
         'sidebar_hooks': DashboardHook.hooks,
+        'view' : view
     })
 
 
+class ActionFeed():
+
+    def __init__(self, request):
+        self.request = request
+
+    def render_to_response(self, template_name, extra_context={}):
+        qs = ReviewRequest.objects.all()[:200]
+        stream = []
+        total = 0
+        TOTAL = 500
+        for request in qs:
+            for action in request.action_feed['actions']:
+                total += 1
+                action['target'] = request
+                action['ts'] = datetime.fromtimestamp(action['ts'])
+                stream.append(action)
+                if total == TOTAL:
+                    break
+            if total == TOTAL:
+                break
+        stream = sorted(stream, key=itemgetter('ts'), reverse=True)
+        context = {
+            'user': self.request.user,
+            'stream': stream
+        }
+        context.update(extra_context)
+
+        return render_to_response(template_name, RequestContext(self.request,
+                                                                    context))
+
+
 @check_login_required
 def group(request,
           name,
@@ -894,6 +930,7 @@ def diff_fragment(request,
                               interdiffset_id, chunkindex, template_name)
 
 
+
 @check_login_required
 def preview_review_request_email(
     request,
diff --git a/reviewboard/settings.py b/reviewboard/settings.py
index 7764d7fead1ad31cd1bf6368d24ac9374b1634c5..76940b25ee845e3ecf7cc6a9c682438fc976e4e1 100644
--- a/reviewboard/settings.py
+++ b/reviewboard/settings.py
@@ -286,6 +286,13 @@ PIPELINE_CSS = {
         'output_filename': 'rb/css/common.min.css',
         'absolute_paths': False,
     },
+    'action-feed': {
+        'source_filenames': (
+            'rb/css/action-feed.less',
+        ),
+        'output_filename' : 'rb/css/action-feed.min.css',
+        'absolute_paths' : False
+    },
     'reviews': {
         'source_filenames': (
             'rb/css/diffviewer.less',
@@ -327,3 +334,4 @@ elif DEBUG:
 
 # Packages to unit test
 TEST_PACKAGES = ['reviewboard']
+
diff --git a/reviewboard/static/rb/css/action-feed.less b/reviewboard/static/rb/css/action-feed.less
new file mode 100644
index 0000000000000000000000000000000000000000..624952fab640d236c47b5b420c7b9da1badba80a
--- /dev/null
+++ b/reviewboard/static/rb/css/action-feed.less
@@ -0,0 +1,74 @@
+@import "defs.less";
+
+.container_action_feed{
+    padding: 3px;
+}
+
+.action_feed{
+
+    background-color: #fefadf;
+    background-image: url('../images/review_request_box_top_bg.png');
+
+    .box-inner {
+        background-image: url('../images/review_box_bottom_bg.png') !important;
+    }
+
+    .reviewer {
+        float: left;
+        font-weight: bold;
+
+        a {
+            position: relative;
+        }
+    }
+
+    .posted_time {
+        text-align: right;
+    }
+
+    .header {
+        padding: 2px 4px;
+
+        a {
+            color: black;
+            text-decoration: none;
+        }
+    }
+
+    .body {
+        background-color: #FAFAFA;
+        border: 1px #AAAAAA solid;
+        margin: 5px;
+        padding: 10px;
+
+        .body_top, .body_bottom {
+            margin: 0;
+        }
+    }
+
+
+
+}
+
+.box {
+    &.collapsed {
+        .collapse-button {
+            background: url('../images/expand.png') no-repeat;
+        }
+
+        .body {
+            display: none;
+        }
+    }
+
+    .collapse-button {
+        background: url('../images/collapse.png') no-repeat;
+        cursor: pointer;
+        float: left;
+        height: 15px;
+        padding-right: 5px;
+        width: 15px;
+    }
+
+
+}
diff --git a/reviewboard/templates/reviews/action_feed.html b/reviewboard/templates/reviews/action_feed.html
new file mode 100644
index 0000000000000000000000000000000000000000..499146d4cbb391d41f91993c773f79bc07104a44
--- /dev/null
+++ b/reviewboard/templates/reviews/action_feed.html
@@ -0,0 +1,33 @@
+{% load i18n %}
+{% load djblets_deco %}
+{% load djblets_utils %}
+{% load reviewtags %}
+{% load staticfiles %}
+{% load tz %}
+{% load compressed %}
+
+
+<div class = "container_action_feed">
+{% for activity in stream %}
+
+{% definevar "boxclass" %}action_feed {% enddefinevar %}
+{% box boxclass %}
+
+<div class="main">
+ <div class="header">
+  <div class="collapse-button"></div>
+  <div class="reviewer"><a href="{% url user activity.user_id %}" class="user">{{activity.username}}</a> {{activity.verb }} </div>
+  <div class="posted_time">{% localtime on %}{% blocktrans with activity.ts|timesince as timestamp_since  and activity.ts|date:"F jS, Y, P" as timestamp_date %}Posted {{ timestamp_since }} ago ({{ timestamp_date }}){% endblocktrans %}{% endlocaltime %}</div>
+ </div>
+
+ <div class="body">
+   <pre class="body_top reviewtext">{{ activity.target}}</pre>
+ </div><!-- body -->
+</div><!-- main -->
+{% endbox %}
+{% endfor %}
+
+ </div>
+</div>
+
+</div>
diff --git a/reviewboard/templates/reviews/dashboard.html b/reviewboard/templates/reviews/dashboard.html
index c57bcfe80d5fdeef75ec2e269ff6caadec665de6..cf8959408d48209097b07f3205e2279b2d24b0f8 100755
--- a/reviewboard/templates/reviews/dashboard.html
+++ b/reviewboard/templates/reviews/dashboard.html
@@ -3,6 +3,7 @@
 {% load i18n %}
 {% load reviewtags %}
 {% load staticfiles %}
+{% load compressed %}
 
 {% block title %}{% trans "My Dashboard" %}{% endblock %}
 
@@ -10,9 +11,13 @@
 <meta http-equiv="refresh" content="300" />
 {% endblock %}
 
+
 {% block css %}
 {{block.super}}
 <link rel="stylesheet" type="text/css" href="{% static "djblets/css/datagrid.css" %}" />
+{% if view == 'action_feed' %}
+{% compressed_css "action-feed" %}
+{% endif %}
 {% endblock %}
 
 {% block scripts-post %}
@@ -28,6 +33,7 @@
    <col class="count" />
   </colgroup>
   <tbody>
+{% dashboard_entry "main-item" "Action Feed"      "action_feed" %}
 {% dashboard_entry "main-item" "Starred Reviews"  "starred" %}
 {% dashboard_entry "main-item" "Outgoing Reviews" "outgoing" %}
 {% dashboard_entry "main-item" "Incoming Reviews" "incoming" %}
@@ -53,7 +59,11 @@
   </tbody>
  </table>
  <div id="dashboard-main" class="clearfix">
+{% if view == 'action_feed' %}
+{% include "reviews/action_feed.html" %}
+{% else %}
 {{datagrid.render_listview}}
+{% endif %}
  </div>
 </div>
 {% endbox %}
diff --git a/reviewboard/webapi/tests.py b/reviewboard/webapi/tests.py
index 5026f2ec81de3eb05ea65d1d2c8390e7e6b99398..fe02ba11c7afcef37a63ed8c3219e898839f5587 100644
--- a/reviewboard/webapi/tests.py
+++ b/reviewboard/webapi/tests.py
@@ -4574,11 +4574,7 @@ class DiffResourceTests(BaseWebAPITestCase):
         """Testing the POST review-requests/<id>/diffs/ API with a local site"""
         self._login_user(local_site=True)
 
-        repo = self.repository
-        self.repository.local_site = \
-            LocalSite.objects.get(name=self.local_site_name)
-        self.repository.save()
-
+        repo = Repository.objects.get(name='Review Board Git')
         rsp = self._postNewReviewRequest(local_site_name=self.local_site_name,
                                          repository=repo)
 
@@ -4586,17 +4582,17 @@ class DiffResourceTests(BaseWebAPITestCase):
 
         diff_filename = os.path.join(
             os.path.dirname(os.path.dirname(__file__)),
-            'scmtools', 'testdata', 'svn_makefile.diff')
+            'scmtools', 'testdata', 'git_deleted_file_indication.diff')
         f = open(diff_filename, 'r')
-        rsp = self.apiPost(rsp['review_request']['links']['diffs']['href'], {
-            'path': f,
-            'basedir': '/trunk',
-        }, expected_mimetype=self.item_mimetype)
+        rsp = self.apiPost(rsp['review_request']['links']['diffs']['href'],
+                           { 'path': f, },
+                           expected_mimetype=self.item_mimetype)
         f.close()
 
         self.assertEqual(rsp['stat'], 'ok')
         self.assertEqual(rsp['diff']['name'],
-                         'svn_makefile.diff')
+                         'git_deleted_file_indication.diff')
+
 
     @add_fixtures(['test_reviewrequests'])
     def test_get_diffs(self):
