diff --git a/common.py b/common.py
index f48a95753653abe17518c2a9b464000baf8e8dda..8eb60dedb7f492d34411ba97db61c35f8997d852 100755
--- a/common.py
+++ b/common.py
@@ -52,34 +52,6 @@ def get_latest_commit(gitlab, project, branch_name):
     return branch.commit
 
 
-def get_merge_request(
-    gitlab, project, state, source_branch=None, target_branch=None, commit=None
-):
-    """Get merge request by source and target branch and optionally commit sha"""
-    merge_request = None
-    with gitlab:
-        try:
-            merge_requests = project.mergerequests.list(
-                source_branch=source_branch,
-                target_branch=target_branch,
-                state=state,
-                all=True,
-                retry_transient_errors=True,
-            )
-        except GitlabGetError as e:
-            sys.exit(
-                "ERROR: could not list merge requests for project '%s': %s"
-                % (project.name, e)
-            )
-        if commit:
-            for mr in merge_requests:
-                if mr.sha == commit:
-                    merge_request = mr
-        elif merge_requests:
-            merge_request = merge_requests[0]
-    return merge_request
-
-
 def rebase_merge_request(gitlab, project, merge_request):
     """Attempt to rebase a merge request and return the updated merge request object"""
     # Rebasing takes more than one API call, see:
diff --git a/get_manifest_projects.py b/get_manifest_projects.py
new file mode 100755
index 0000000000000000000000000000000000000000..39e600179129f1c033f6698717e27a105b8635e0
--- /dev/null
+++ b/get_manifest_projects.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+import argparse
+import sys
+from lxml import etree
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--manifest",
+        help="""manifest file to parse projects from""",
+        dest="manifest",
+        required=True,
+    )
+    parser.add_argument(
+        "--remote",
+        help="""get only projects with this remote""",
+        dest="remote",
+        required=False,
+    )
+
+    args, _ = parser.parse_known_args()
+
+    # Parse manifest file
+    try:
+        manifest = etree.parse(args.manifest)
+    except FileNotFoundError:
+        sys.exit("ERROR: file '%s' not found" % args.manifest)
+
+    # Find project references in manifest
+    if args.remote:
+        find_expression = "project[@remote='%s']" % args.remote
+    else:
+        find_expression = "project"
+    projects = manifest.findall(find_expression)
+
+    for project in projects:
+        print(project.get("name"))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/get_merge_requests.py b/get_merge_requests.py
new file mode 100755
index 0000000000000000000000000000000000000000..3f2fdb3f8c8035df147b1b76c20558580bcee71a
--- /dev/null
+++ b/get_merge_requests.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+import common
+
+import argparse
+import sys
+from gitlab import Gitlab, GitlabGetError
+
+
+def get_merge_requests(
+    gitlab, project, state, source_branch=None, target_branch=None, commit=None
+):
+    """Get merge request by source and target branch and optionally commit sha"""
+    merge_requests = []
+    with gitlab:
+        try:
+            all_merge_requests = project.mergerequests.list(
+                source_branch=source_branch,
+                target_branch=target_branch,
+                state=state,
+                all=True,
+                retry_transient_errors=True,
+            )
+        except GitlabGetError as e:
+            sys.exit(
+                "ERROR: could not list merge requests for project '%s': %s"
+                % (project.name, e)
+            )
+        if commit:
+            for mr in all_merge_requests:
+                if mr.sha == commit:
+                    merge_requests.append(mr)
+        elif all_merge_requests:
+            merge_requests = all_merge_requests
+    return merge_requests
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--gitlab-url",
+        help="""URL to the GitLab instance""",
+        dest="gitlab_url",
+        required=True,
+    )
+    parser.add_argument(
+        "--token",
+        help="""GitLab REST API private access token""",
+        dest="token",
+        required=True,
+    )
+    parser.add_argument(
+        "--project",
+        help="""name of the GitLab project""",
+        dest="project",
+        required=True,
+    )
+    parser.add_argument(
+        "--state",
+        help="""state of the merge request (opened, closed, locked, or merged)""",
+        dest="state",
+        required=True,
+    )
+    parser.add_argument(
+        "--source-branch",
+        help="""source branch of the merge request""",
+        dest="source_branch",
+        required=False,
+    )
+    parser.add_argument(
+        "--target-branch",
+        help="""target branch of the merge request""",
+        dest="target_branch",
+        required=False,
+    )
+    parser.add_argument(
+        "--commit",
+        help="""commit sha of the merge request""",
+        dest="commit",
+        required=False,
+    )
+
+    args, _ = parser.parse_known_args()
+
+    gitlab = Gitlab(args.gitlab_url, private_token=args.token)
+    project = common.get_project(gitlab, args.project)
+
+    mrs = get_merge_requests(
+        gitlab,
+        project,
+        state=args.state,
+        source_branch=args.source_branch,
+        target_branch=args.target_branch,
+        commit=args.commit,
+    )
+
+    for mr in mrs:
+        print(mr.iid)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/merge_into_manifest.py b/merge_into_manifest.py
index 98a87beca091073443d60ba6a79e018ad2e69e8c..7f530d63bdb1e17262cd10a693bb067fdf9723a4 100755
--- a/merge_into_manifest.py
+++ b/merge_into_manifest.py
@@ -8,6 +8,7 @@ from gitlab import (
     GitlabGetError,
 )
 from accept_merge_request import accept_merge_request
+from get_merge_requests import get_merge_requests
 from integrate_into_manifest import integrate_into_manifest
 
 
@@ -63,18 +64,19 @@ def main():
     manifest_project = common.get_project(gitlab, args.manifest_project)
 
     # Get source merge request
-    source_mr = common.get_merge_request(
+    mrs = get_merge_requests(
         gitlab,
         project,
         target_branch=args.master_branch,
         state="merged",
         commit=args.commit,
     )
-    if not source_mr:
+    if not mrs:
         sys.exit(
             "ERROR: could not determine source merge request for commit %s"
             % args.commit
         )
+    source_mr = mrs[0]
 
     # Get original branch for commit
     original_branch = source_mr.source_branch
@@ -82,15 +84,15 @@ def main():
     target_branch = args.master_branch
 
     # Check if merge request already exists
-    mr = common.get_merge_request(
+    mrs = get_merge_requests(
         gitlab,
         manifest_project,
         source_branch=integration_branch,
         target_branch=target_branch,
         state="opened",
     )
-    if mr:
-        sys.exit("ERROR: There is already an open merge request:\n%s" % mr.web_url)
+    if mrs:
+        sys.exit("ERROR: There is already an open merge request:\n%s" % mrs[0].web_url)
 
     with gitlab:
 
diff --git a/retrigger_mr_pipeline.py b/retrigger_mr_pipeline.py
new file mode 100755
index 0000000000000000000000000000000000000000..635cd84fec7886ca7ad125a24c4a7ad2eef59f6f
--- /dev/null
+++ b/retrigger_mr_pipeline.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+import common
+
+import argparse
+import sys
+from gitlab import Gitlab, GitlabGetError, GitlabHttpError
+
+
+def retrigger_mr_pipeline(gitlab, project, mr):
+    with gitlab:
+        # Creating a merge request pipeline is not implemented in python-gitlab, so we
+        # have to construct the HTTP request manually.
+        # https://github.com/python-gitlab/python-gitlab/issues/1239
+        # https://docs.gitlab.com/ce/api/merge_requests.html#create-mr-pipeline
+        path = "%s/%s/pipelines" % (mr.manager.path, mr.iid)
+        try:
+            pipeline = gitlab.http_post(path)
+        except GitlabHttpError as e:
+            sys.exit(
+                "ERROR: could not create pipeline for merge request %s!%s: %s"
+                % (project.name, mr.iid, e)
+            )
+
+        print("Created new pipeline for %s :" % mr.web_url)
+        print(pipeline.get("web_url"))
+
+        return pipeline
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--gitlab-url",
+        help="""URL to the GitLab instance""",
+        dest="gitlab_url",
+        required=True,
+    )
+    parser.add_argument(
+        "--token",
+        help="""GitLab REST API private access token""",
+        dest="token",
+        required=True,
+    )
+    parser.add_argument(
+        "--project",
+        help="""name of the GitLab project""",
+        dest="project",
+        required=True,
+    )
+    parser.add_argument(
+        "--iid",
+        help="""iid of the merge request""",
+        dest="iid",
+        required=True,
+    )
+
+    args, _ = parser.parse_known_args()
+
+    gitlab = Gitlab(args.gitlab_url, private_token=args.token)
+    project = common.get_project(gitlab, args.project)
+    try:
+        mr = project.mergerequests.get(args.iid)
+    except GitlabGetError as e:
+        sys.exit(
+            "ERROR: could not get merge request %s:!%s: %s"
+            % (project.name, args.iid, e)
+        )
+    if not mr:
+        sys.exit("ERROR: could not find merge request %s!%s" % (project.name, args.iid))
+
+    retrigger_mr_pipeline(gitlab, project, mr)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/retrigger_mr_pipelines.py b/retrigger_mr_pipelines.py
new file mode 100755
index 0000000000000000000000000000000000000000..7944f4d576a8d5d9f9e39bb57a56968ef542370c
--- /dev/null
+++ b/retrigger_mr_pipelines.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+import common
+
+import argparse
+from gitlab import Gitlab
+
+from get_merge_requests import get_merge_requests
+from retrigger_mr_pipeline import retrigger_mr_pipeline
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--gitlab-url",
+        help="""URL to the GitLab instance""",
+        dest="gitlab_url",
+        required=True,
+    )
+    parser.add_argument(
+        "--token",
+        help="""GitLab REST API private access token""",
+        dest="token",
+        required=True,
+    )
+    parser.add_argument(
+        "--project",
+        help="""name of the GitLab project""",
+        dest="project",
+        required=True,
+    )
+    parser.add_argument(
+        "--state",
+        help="""state of the merge request (opened, closed, locked, or merged)""",
+        dest="state",
+        required=True,
+    )
+    parser.add_argument(
+        "--source-branch",
+        help="""source branch of the merge request""",
+        dest="source_branch",
+        required=False,
+    )
+    parser.add_argument(
+        "--target-branch",
+        help="""target branch of the merge request""",
+        dest="target_branch",
+        required=False,
+    )
+    parser.add_argument(
+        "--commit",
+        help="""commit sha of the merge request""",
+        dest="commit",
+        required=False,
+    )
+
+    args, _ = parser.parse_known_args()
+
+    gitlab = Gitlab(args.gitlab_url, private_token=args.token)
+    project = common.get_project(gitlab, args.project)
+
+    mrs = get_merge_requests(
+        gitlab,
+        project,
+        state=args.state,
+        source_branch=args.source_branch,
+        target_branch=args.target_branch,
+        commit=args.commit,
+    )
+
+    for mr in mrs:
+        retrigger_mr_pipeline(gitlab, project, mr)
+
+
+if __name__ == "__main__":
+    main()