You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by he...@apache.org on 2015/04/01 23:10:31 UTC

[14/45] allura git commit: [#7830] ticket:729 One-click merge for git

[#7830] ticket:729 One-click merge for git


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/121ebed7
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/121ebed7
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/121ebed7

Branch: refs/heads/hss/7072
Commit: 121ebed71e8395f3a436bbecc88d180f0e3b7108
Parents: 9c06caf
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 18 17:00:27 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Mar 20 20:40:29 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py         | 13 ++++++++
 Allura/allura/model/repository.py               | 24 +++++++++++++++
 Allura/allura/templates/repo/merge_request.html | 26 ++++++++++++++++
 ForgeGit/forgegit/model/git_repo.py             | 32 ++++++++++++++++++++
 4 files changed, 95 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/121ebed7/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index a3c1541..78bff44 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -441,6 +441,19 @@ class MergeRequestController(object):
             self.req.status = status
         redirect('.')
 
+    @expose()
+    @require_post()
+    def merge(self):
+        require_access(c.app, 'write')
+        if self.req.status != 'open' or not self.req.can_merge():
+            raise exc.HTTPNotFound
+        ok = self.req.merge()
+        if ok:
+            flash('Merged successfully', 'ok')
+        else:
+            flash('Merge failed. Please, merge manually', 'error')
+        redirect(self.req.url())
+
 
 class RefsController(object):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/121ebed7/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 0030aa5..5e4a171 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -805,6 +805,30 @@ class MergeRequest(VersionedArtifact, ActivityObject):
                 self.request_number, self.project.name, self.app.repo.name))
         return result
 
+    def can_merge(self):
+        if not self.app.forkable:
+            return False
+        try:
+            result = self.app.repo.can_merge(self)
+        except:
+            log.exception(
+                "Can't determine if merge request %s can be merged",
+                self.url())
+            return False
+        return result
+
+    def merge(self):
+        if not self.app.forkable:
+            return False
+        try:
+            self.app.repo.merge(self)
+        except:
+            log.exception("Can't merge merge request %s", self.url())
+            return False
+        self.status = 'merged'
+        session(self).flush(self)
+        return True
+
 
 # Basic commit information
 # One of these for each commit in the physical repo on disk. The _id is the

http://git-wip-us.apache.org/repos/asf/allura/blob/121ebed7/Allura/allura/templates/repo/merge_request.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/repo/merge_request.html b/Allura/allura/templates/repo/merge_request.html
index 3c79654..060b5b9 100644
--- a/Allura/allura/templates/repo/merge_request.html
+++ b/Allura/allura/templates/repo/merge_request.html
@@ -44,6 +44,25 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
 
     <div>{{g.markdown.convert(req.description)}}</div>
 
+    {% if req.status == 'open' and c.app.forkable and h.has_access(c.app, 'write')() %}
+      {% set can_merge = req.can_merge() %}
+      <div class="grid-19">
+        <form action="merge" method="POST">
+          {{ lib.csrf_token() }}
+          <input type="submit" value="Merge"{% if not can_merge %}disabled="disabled"{% endif %}>
+          {% if can_merge %}
+            <div class="merge-ok">
+              Merge request has no conflicts. You can merge automatically.
+            </div>
+          {% else %}
+            <div class="merge-conflicts">
+              Merge request has conflicts. Follow manual instructions below to merge.
+            </div>
+          {% endif %}
+        </form>
+      </div>
+    {% endif %}
+
     {{ c.log_widget.display(value=req.commits, app=downstream_app) }}
 
     <div class="grid-19"><a href="#discussion_holder">Discuss</a></div>
@@ -87,3 +106,10 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
         count=count)}}
   </div>
 {% endblock %}
+
+{% block extra_css %}
+<style type="text/css">
+  .merge-ok { color: green; }
+  .merge-conflicts { color: red; }
+</style>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/allura/blob/121ebed7/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 7fda67a..831da5b 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -19,6 +19,7 @@ import os
 import shutil
 import string
 import logging
+import tempfile
 from datetime import datetime
 
 import tg
@@ -95,6 +96,37 @@ class Repository(M.Repository):
             merge_request.downstream.commit_id,
         )
 
+    def can_merge(self, mr):
+        """
+        Given merge request `mr` determine if it can be merged w/o conflicts.
+        """
+        g = self._impl._git.git
+        # http://stackoverflow.com/a/6283843
+        # fetch source branch
+        g.fetch(mr.downstream_repo_url, mr.source_branch)
+        # find merge base
+        merge_base = g.merge_base(mr.downstream.commit_id, mr.target_branch)
+        # print out merge result, but don't actually touch anything
+        merge_tree = g.merge_tree(
+            merge_base, mr.target_branch, mr.downstream.commit_id)
+        return '+<<<<<<<' not in merge_tree
+
+    def merge(self, mr):
+        g = self._impl._git.git
+        # can't merge in bare repo, so need to clone
+        tmp_path = tempfile.mkdtemp()
+        tmp_repo = git.Repo.clone_from(
+            self.clone_url('rw'),
+            to_path=tmp_path,
+            bare=False)
+        tmp_repo = GitImplementation(Object(full_fs_path=tmp_path))._git
+        tmp_repo.git.fetch('origin', mr.target_branch)
+        tmp_repo.git.checkout(mr.target_branch)
+        tmp_repo.git.fetch(mr.downstream_repo_url, mr.source_branch)
+        tmp_repo.git.merge(mr.downstream.commit_id)
+        tmp_repo.git.push('origin', mr.target_branch)
+        shutil.rmtree(tmp_path, ignore_errors=True)
+
     def rev_to_commit_id(self, rev):
         return self._impl.rev_parse(rev).hexsha