diff --git a/src/IDF/Scm.php b/src/IDF/Scm.php
index 60f010a..f474cd5 100644
--- a/src/IDF/Scm.php
+++ b/src/IDF/Scm.php
@@ -316,6 +316,34 @@ class IDF_Scm
         throw new Pluf_Exception_NotImplemented();
     }
 
+    /**
+     * Returns all recorded changes which lead to the particular commit
+     * or revision.
+     *
+     * Example output:
+     *
+     * stdClass object {
+     *  'additions' => array('path/to/file', 'path/to/directory', ...),
+     *  'deletions' => array('path/to/file', 'path/to/directory', ...),
+     *  'renames' => array('old/path/to/file' => 'new/path/to/file', ...)
+     *  'patches' => array('path/to/file', ...),
+     *  'properties' => array('path/to/file' => array(
+     *              'propname' => 'propvalue', 'deletedprop' => null, ...)
+     *              ),
+     *      ...)
+     * }
+     *
+     * Each member of the returned object is mandatory, but may contain
+     * an empty array if no changes were recorded.
+     *
+     * @param string    A commit identifier
+     * @return object with arrays of individual changes
+     */
+    public function getChanges($commit)
+    {
+        throw new Pluf_Exception_NotImplemented();
+    }
+
     /**
      * Get latest changes.
      *
diff --git a/src/IDF/Scm/Git.php b/src/IDF/Scm/Git.php
index 50edc78..ca05429 100644
--- a/src/IDF/Scm/Git.php
+++ b/src/IDF/Scm/Git.php
@@ -431,10 +431,10 @@ class IDF_Scm_Git extends IDF_Scm
                 }
             }
             $out = self::parseLog($log);
-            $out[0]->changes = implode("\n", $change);
+            $out[0]->diff = implode("\n", $change);
         } else {
             $out = self::parseLog($out);
-            $out[0]->changes = '';
+            $out[0]->diff = '';
         }
 
         $out[0]->branch = implode(', ', $this->inBranches($commit, null));
diff --git a/src/IDF/Scm/Mercurial.php b/src/IDF/Scm/Mercurial.php
index ad2f383..b52e9f9 100644
--- a/src/IDF/Scm/Mercurial.php
+++ b/src/IDF/Scm/Mercurial.php
@@ -359,7 +359,7 @@ class IDF_Scm_Mercurial extends IDF_Scm
             }
         }
         $out = self::parseLog($log, 6);
-        $out[0]->changes = implode("\n", $change);
+        $out[0]->diff = implode("\n", $change);
         return $out[0];
     }
 
diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php
index 3afbd78..fb39d5b 100644
--- a/src/IDF/Scm/Monotone.php
+++ b/src/IDF/Scm/Monotone.php
@@ -599,6 +599,75 @@ class IDF_Scm_Monotone extends IDF_Scm
         );
     }
 
+    /**
+     * @see IDF_Scm::getChanges()
+     */
+    public function getChanges($commit)
+    {
+        $revs = $this->_resolveSelector($commit);
+        if (count($revs) == 0)
+            return null;
+
+        $revision = $revs[0];
+        $out = $this->stdio->exec(array('get_revision', $revision));
+        $stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
+
+        $return = (object) array(
+            'additions'  => array(),
+            'deletions'  => array(),
+            'renames'    => array(),
+            'patches'    => array(),
+            'properties' => array(),
+        );
+
+        foreach ($stanzas as $stanza) {
+            if ($stanza[0]['key'] == 'format_version' ||
+                $stanza[0]['key'] == 'old_revision' ||
+                $stanza[0]['key'] == 'new_manifest')
+                continue;
+
+            if ($stanza[0]['key'] == 'add_file' ||
+                $stanza[0]['key'] == 'add_dir') {
+                $return->additions[] = $stanza[0]['values'][0];
+                continue;
+            }
+
+            if ($stanza[0]['key'] == 'delete') {
+                $return->deletions[] = $stanza[0]['values'][0];
+                continue;
+            }
+
+            if ($stanza[0]['key'] == 'rename') {
+                $return->renames[$stanza[0]['values'][0]] =
+                    $stanza[1]['values'][0];
+                continue;
+            }
+
+            if ($stanza[0]['key'] == 'patch') {
+                $return->patches[] = $stanza[0]['values'][0];
+                continue;
+            }
+
+            if ($stanza[0]['key'] == 'clear' ||
+                $stanza[0]['key'] == 'set') {
+
+                $filename = $stanza[0]['values'][0];
+                if (!array_key_exists($filename, $return->properties)) {
+                    $return->properties[$filename] = array();
+                }
+                $key = $stanza[1]['values'][0];
+                $value = null;
+                if (isset($stanza[2])) {
+                    $value = $stanza[2]['values'][0];
+                }
+                $return->properties[$filename][$key] = $value;
+                continue;
+            }
+        }
+
+        return $return;
+    }
+
     /**
      * @see IDF_Scm::getCommit()
      */
@@ -626,7 +695,7 @@ class IDF_Scm_Monotone extends IDF_Scm
         $res['branch'] = implode(', ', $certs['branch']);
         $res['commit'] = $revs[0];
 
-        $res['changes'] = ($getdiff) ? $this->_getDiff($revs[0]) : '';
+        $res['diff'] = ($getdiff) ? $this->_getDiff($revs[0]) : '';
 
         return (object) $res;
     }
diff --git a/src/IDF/Scm/Svn.php b/src/IDF/Scm/Svn.php
index b0963a1..0144738 100644
--- a/src/IDF/Scm/Svn.php
+++ b/src/IDF/Scm/Svn.php
@@ -414,7 +414,7 @@ class IDF_Scm_Svn extends IDF_Scm
         $res['date'] = gmdate('Y-m-d H:i:s', strtotime((string) $xml->logentry->date));
         $res['title'] = (string) $xml->logentry->msg;
         $res['commit'] = (string) $xml->logentry['revision'];
-        $res['changes'] = ($getdiff) ? $this->getDiff($commit) : '';
+        $res['diff'] = ($getdiff) ? $this->getDiff($commit) : '';
         $res['tree'] = '';
         $res['branch'] = '';
         return (object) $res;
diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php
index ee2d680..6652d66 100644
--- a/src/IDF/Views/Source.php
+++ b/src/IDF/Views/Source.php
@@ -297,8 +297,9 @@ class IDF_Views_Source
         $title = sprintf(__('%s Commit Details'), (string) $request->project);
         $page_title = sprintf(__('%s Commit Details - %s'), (string) $request->project, $commit);
         $rcommit = IDF_Commit::getOrAdd($cobject, $request->project);
-        $diff = new IDF_Diff($cobject->changes);
+        $diff = new IDF_Diff($cobject->diff);
         $diff->parse();
+        $changes = $scm->getChanges($commit);
         $scmConf = $request->conf->getVal('scm', 'git');
         $branches = $scm->getBranches();
         $in_branches = $scm->inBranches($cobject->commit, '');
@@ -311,6 +312,7 @@ class IDF_Views_Source
                                                      'diff' => $diff,
                                                      'cobject' => $cobject,
                                                      'commit' => $commit,
+                                                     'changes' => $changes,
                                                      'branches' => $branches,
                                                      'tree_in' => $in_branches,
                                                      'tags' => $tags,
diff --git a/src/IDF/templates/idf/source/commit.html b/src/IDF/templates/idf/source/commit.html
index c04ab80..e918fee 100644
--- a/src/IDF/templates/idf/source/commit.html
+++ b/src/IDF/templates/idf/source/commit.html
@@ -18,20 +18,45 @@
 
 | {trans 'Message:'} | {issuetext $cobject.title, $request}{if isset($cobject.full_message)} 
 {issuetext $cobject.full_message, $request, true, false, true, true, true}{/if}
 | 
-{if count($diff.files)}
 
-| {trans 'Files:'}+ | {trans 'Changes:'} | -{foreach $diff.files as $filename=>$diffdef}
-{assign $ndiff = count($diffdef['chunks'])}
-{$filename} ({blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans}) +
 
+{foreach $changes.deletions as $filename}
+{/foreach}
+{foreach $changes.renames as $oldname => $newname}
+| D | {$filename}{if !empty($diff.files[$filename])} ({trans 'full'}){/if} |  +{/foreach}
+{foreach $changes.additions as $filename}
+| R | {$oldname} -> {$newname} |  +{/foreach}
+{foreach $changes.patches as $filename}
+{assign $ndiff = count($diff.files[$filename]['chunks'])}
+| A | {$filename}{if !empty($diff.files[$filename])} ({trans 'full'}){/if} |  +{/foreach}
+{foreach $changes.properties as $filename => $properties}
+| M | {$filename}{if !empty($diff.files[$filename])} ({blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans}){/if} |  +{/foreach}
+| P | {$filename}
+ 
+{foreach $properties as $key => $value}
+++{/foreach}
+| {$key}+{if $value == null}
+ | {trans 'removed'}+{else}
+ | {$value}+{/if}
+ |  |  | 
-{/if}
 
+
 {if count($diff.files)}
-{trans 'Change Details'}
+{trans 'File differences'}
 
 {$diff.as_html()}
 {/if}{if count($diff.files) or $large_commit}
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css
index 273f539..ef64f05 100644
--- a/www/media/idf/css/style.css
+++ b/www/media/idf/css/style.css
@@ -504,6 +504,21 @@ table.commit th {
 table.commit td, table.commit th {
   padding: 3px;
 }
+table.commit table.changes td {
+  padding: 2px;
+}
+table.commit table.changes table.properties {
+  margin: 0;
+}
+table.commit table.changes table.properties tr:nth-child(even) {
+  background: #E4E8E0;
+}
+table.commit table.changes table.properties td {
+  white-space: pre-wrap;
+}
+table.commit table.changes table.properties td.removed {
+  font-style: italic;
+}
 
 /**
  * syntax highlighting of diffs