diff --git a/TracToGitlabPlugin.php b/TracToGitlabPlugin.php
index d01ba4972e6a71b5b9aceadc690a4247c08b7182..7ed3947e282443475f0a14c8db26277bc7d5b8ee 100644
--- a/TracToGitlabPlugin.php
+++ b/TracToGitlabPlugin.php
@@ -13,7 +13,6 @@ final class TracToGitlabPlugin extends StudIPPlugin implements StandardPlugin, S
             return;
         }
 
-
         $this->buildNavigation();
     }
 
@@ -120,6 +119,10 @@ final class TracToGitlabPlugin extends StudIPPlugin implements StandardPlugin, S
             'translations',
             new Navigation(_('Übersetzungen'), PluginEngine::getURL($this, [], 'translations'))
         );
+        $navigation->addSubNavigation(
+            'merge',
+            new Navigation(_('Zu portierende Bugfixes'), PluginEngine::getURL($this, [], 'merge'))
+        );
 
         Navigation::addItem('/gitlab-dashboard', $navigation);
     }
diff --git a/controllers/dashboard.php b/controllers/dashboard.php
index a5d7ade8fe2956454813590f20fc171e062fb977..15ab26b56dbb7164fd192d254717a6126e25ffc4 100644
--- a/controllers/dashboard.php
+++ b/controllers/dashboard.php
@@ -17,6 +17,7 @@ final class DashboardController extends TracToGitlab\Controller
 
         Navigation::activateItem('/gitlab-dashboard/dashboard');
         PageLayout::setTitle(_('Übersicht der Issues in gitlab'));
+        $this->activateNavigation('dashboard');
 
         $this->setupSidebar();
     }
@@ -100,6 +101,7 @@ final class DashboardController extends TracToGitlab\Controller
         usort($issues, function ($a, $b) {
             return $a['id'] - $b['id'];
         });
+
         return array_map(function ($issue) {
             return new TracToGitlab\GitlabIssue($issue);
         }, $issues);
diff --git a/controllers/merge.php b/controllers/merge.php
new file mode 100644
index 0000000000000000000000000000000000000000..e997eed535eb8142de6f8d7d7ad5fbe9cad0c1d1
--- /dev/null
+++ b/controllers/merge.php
@@ -0,0 +1,106 @@
+<?php
+final class MergeController extends TracToGitlab\GitlabController
+{
+    public function before_filter(&$action, &$args)
+    {
+        parent::before_filter($action, $args);
+
+        PageLayout::setTitle(_('Übersicht noch zu portierender Bugfixes'));
+        $this->activateNavigation('merge');
+    }
+
+    public function index_action()
+    {
+        $data = $this->readFromCache('issues-to-merge', true);
+        if ($data === false) {
+            $this->issues = $this->fetchIssues();
+            $this->writeToCache('issues-to-merge', $this->issues);
+        } else {
+            $this->issues = $data['data'];
+
+            Sidebar::get()->addWidget(new TemplateWidget(
+                'Aus dem Cache',
+                $this->get_template_factory()->open(
+                    $this->get_default_template('sidebar'),
+                ),
+                ['time' => time()]
+            ));
+        }
+    }
+
+    public function diff_action($mr_iid)
+    {
+//        $versions = $this->gitlab->mergeRequests()->
+    }
+
+    private function fetchIssues(): array
+    {
+        $issues = $this->gitlabPager->fetchAll(
+            $this->gitlab->issues(),
+            'all',
+            [
+                $this->gitlabProjectId,
+                [
+                    'sort'      => 'asc',
+                    'scope'     => 'all',
+                    'milestone' => 'None',
+                    'labels'    => 'BIEST',
+                    'state'     => 'closed',
+                ]
+            ]
+        );
+
+        $issues = array_filter($issues, function ($issue) {
+            foreach (['worksforme', 'wontfix', 'Duplicate', 'invalid'] as $label) {
+                if (in_array($label, $issue['labels'])) {
+                    return false;
+                }
+            }
+
+            $has_version = array_reduce($issue['labels'], function ($has_version, $label) {
+                return $has_version || strpos($label, 'Version::') === 0;
+            }, false);
+            return $has_version;
+        });
+
+        $issues = array_map(function ($issue) {
+            $issue['studip_version'] = $this->extractVersion($issue['labels']);
+            $issue['mr'] = $this->fetchRelatedMergeRequest($issue['iid']);
+            return $issue;
+        }, $issues);
+
+        usort($issues, function ($a, $b) {
+            return strcmp($b['studip_version'], $a['studip_version']);
+        });
+
+        return $issues;
+    }
+
+    private function fetchRelatedMergeRequest(int $issue_id)
+    {
+        $mrs = $this->gitlab->issues()->closedByMergeRequests(
+            $this->gitlabProjectId,
+            $issue_id
+        );
+        foreach ($mrs as $mr) {
+            if ($mr['state'] === 'merged') {
+                return $mr['iid'];
+            }
+        }
+        return null;
+    }
+
+    private function extractVersion(array $labels): string
+    {
+        $version = '';
+        foreach ($labels as $label) {
+            if (strpos($label, 'Version::') === 0) {
+                $v = substr($label, 9);
+                if (!$version || $v < $version) {
+                    $version = $v;
+                }
+            }
+        }
+        return $version;
+    }
+}
diff --git a/lib/Controller.php b/lib/Controller.php
index 18801cdee27fe958756f8b434befb8dd87a3db94..8e669c75727d049a2d017fe90a49bfa2eb029278 100644
--- a/lib/Controller.php
+++ b/lib/Controller.php
@@ -20,4 +20,9 @@ abstract class Controller extends \PluginController
 
         return $container[$offset];
     }
+
+    protected function activateNavigation(string ...$path): void
+    {
+        \Navigation::activateItem('/gitlab-dashboard/' . implode('/', $path));
+    }
 }
diff --git a/lib/GitlabController.php b/lib/GitlabController.php
new file mode 100644
index 0000000000000000000000000000000000000000..e4ac7647a486b7f5a93a090159c9bd70a9bae6b8
--- /dev/null
+++ b/lib/GitlabController.php
@@ -0,0 +1,43 @@
+<?php
+namespace TracToGitlab;
+
+use Gitlab\Api\AbstractApi;
+
+abstract class GitlabController extends Controller
+{
+    private $gitlabCache;
+
+    public function before_filter(&$action, &$args)
+    {
+        parent::before_filter($action, $args);
+
+        $this->gitlabCache = \StudipCacheFactory::getCache();
+    }
+
+    protected function readFromCache(string $key, bool $raw = false)
+    {
+        $cached = $this->gitlabCache->read($key);
+        if (!$cached) {
+            return false;
+        }
+        $data = json_decode($cached, true);
+        return $raw ? $data : $data['data'];
+    }
+
+    protected function writeToCache(string $key, $data, $expire = 5 * 60)
+    {
+        $this->gitlabCache->write($key, json_encode([
+            'data' => $data,
+            'time' => time(),
+        ]), $expire);
+    }
+
+    protected function getCachedDate(string $key)
+    {
+        $data = $this->readFromCache($key, true);
+        if ($data === false) {
+            return false;
+        }
+        return $data['time'] ?? false;
+    }
+}
diff --git a/views/dashboard/index.php b/views/dashboard/index.php
index b4680c23cf4bc87c95a97b35e25b27f07ee2cc19..34066f81cd8de68cf9bcbabdb02f7cf43c99f068 100644
--- a/views/dashboard/index.php
+++ b/views/dashboard/index.php
@@ -1,3 +1,9 @@
+<?php
+/**
+ * @var array<string, string> $mapping
+ * @var array<int, TracToGitlab\GitlabIssue> $issues
+ */
+?>
 <table class="default">
     <thead>
         <tr>
diff --git a/views/merge/index.php b/views/merge/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..a335d64102e2e69f689670f7d179a6321e057d57
--- /dev/null
+++ b/views/merge/index.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @var array<int, array> $issues
+ */
+?>
+<table class="default">
+    <thead>
+        <tr>
+            <th><?= _('Issue') ?></th>
+            <th><?= _('Autor') ?></th>
+            <th><?= _('Bearbeiter') ?></th>
+            <th><?= _('Version') ?></th>
+<!--            <th></th>-->
+        </tr>
+    </thead>
+    <tbody>
+    <? if (!$issues): ?>
+        <tr>
+            <td colspan="4" style="text-align: center">
+                <?= _('Keine portierbaren Bugfixes vorhanden 🥳🍺') ?>
+            </td>
+        </tr>
+    <? endif; ?>
+    <? foreach ($issues as $issue): ?>
+        <tr>
+            <td>
+                <a href="<?= htmlReady($issue['web_url']) ?>" target="_blank">
+                    #<?= htmlReady($issue['iid']) ?>:
+                    <?= htmlReady($issue['title']) ?>
+            </td>
+            <td><?= htmlReady($issue['author']['username']) ?></td>
+            <td><?= htmlReady(implode(',', array_column($issue['assignees'], 'username'))) ?></td>
+            <td><?= htmlReady($issue['studip_version']) ?></td>
+<!--            <td>-->
+<!--            --><?// if ($issue['mr']): ?>
+<!--                <a href="--><?//= $controller->diff($issue['mr']) ?><!--" target="_blank">-->
+<!--                    --><?//= Icon::create('file-generic') ?>
+<!--                </a>-->
+<!--            --><?// endif; ?>
+<!--            </td>-->
+        </tr>
+    <? endforeach; ?>
+    </tbody>
+</table>
diff --git a/views/merge/sidebar.php b/views/merge/sidebar.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8de1a3a16f952649f3a1edc52f86726ff438cb6
--- /dev/null
+++ b/views/merge/sidebar.php
@@ -0,0 +1 @@
+Zeitpunkt: <?= date('d.m.Y H:i:s', $time) ?>