changeset 7142:3dbb625d5f9c

vcs: introduce 'branches' attribute on changesets, making it possible for Git to show multiple branches for a changeset Mercurial changesets will always have have exactly one branch (which might be "default"). The VCS data model was the same. Git allows for a changeset to have 0 or more branches ... and possibly one of them as active. The right data model is thus to have an enumerable of branches. We thus add a 'branches' attribute and use it where applicable. The existing 'branch' attribute used some heuristics to decide which branch use as "the" branch ... and in some places code (and tests) rely on that. We thus keep that old method, knowing that some of its uses probably should move to 'branches'. The code for retrieving Git branches is based on work by Dominik Ruf.
author Mads Kiilerich <mads@kiilerich.com>
date Mon, 12 Feb 2018 02:38:02 +0100
parents f200ce5efce6
children dc7e37ec3dfd
files kallithea/controllers/feed.py kallithea/controllers/files.py kallithea/controllers/pullrequests.py kallithea/lib/vcs/backends/base.py kallithea/lib/vcs/backends/git/changeset.py kallithea/lib/vcs/backends/hg/changeset.py kallithea/templates/changelog/changelog_table.html kallithea/templates/changeset/changeset.html kallithea/templates/changeset/changeset_range.html kallithea/tests/vcs/test_branches.py kallithea/tests/vcs/test_changesets.py kallithea/tests/vcs/test_git.py kallithea/tests/vcs/test_hg.py
diffstat 13 files changed, 48 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/controllers/feed.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/controllers/feed.py	Mon Feb 12 02:38:02 2018 +0100
@@ -63,8 +63,8 @@
         desc_msg = [(_('%s committed on %s')
                      % (h.person(cs.author), h.fmt_date(cs.date))) + '<br/>']
         # branches, tags, bookmarks
-        if cs.branch:
-            desc_msg.append('branch: %s<br/>' % cs.branch)
+        for branch in cs.branches:
+            desc_msg.append('branch: %s<br/>' % branch)
         for book in cs.bookmarks:
             desc_msg.append('bookmark: %s<br/>' % book)
         for tag in cs.tags:
--- a/kallithea/controllers/files.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/controllers/files.py	Mon Feb 12 02:38:02 2018 +0100
@@ -189,7 +189,7 @@
 
         # TODO: tags and bookmarks?
         c.revision_options = [(c.changeset.raw_id,
-                              _('%s at %s') % (c.changeset.branch, h.short_id(c.changeset.raw_id)))] + \
+                              _('%s at %s') % (b, h.short_id(c.changeset.raw_id))) for b in c.changeset.branches] + \
             [(n, b) for b, n in c.db_repo_scm_instance.branches.items()]
         if c.db_repo_scm_instance.closed_branches:
             prefix = _('(closed)') + ' '
@@ -755,7 +755,7 @@
         branches_group = ([], _("Branches"))
         tags_group = ([], _("Tags"))
         for chs in changesets:
-            #_branch = '(%s)' % chs.branch if (cs.repository.alias == 'hg') else ''
+            # TODO: loop over chs.branches ... but that will not give all the bogus None branches for Git ...
             _branch = chs.branch
             n_desc = '%s (%s)' % (h.show_id(chs), _branch)
             changesets_group[0].append((chs.raw_id, n_desc,))
--- a/kallithea/controllers/pullrequests.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/controllers/pullrequests.py	Mon Feb 12 02:38:02 2018 +0100
@@ -102,11 +102,11 @@
             for i in repo._repo.revs(
                 "sort(parents(branch(id(%s)) and merge()) - branch(id(%s)), -rev)",
                 branch_rev, branch_rev):
-                abranch = repo.get_changeset(i).branch
-                if abranch not in peerbranches:
-                    n = 'branch:%s:%s' % (abranch, repo.get_changeset(abranch).raw_id)
-                    peers.append((n, abranch))
-                    peerbranches.add(abranch)
+                for abranch in repo.get_changeset(i).branches:
+                    if abranch not in peerbranches:
+                        n = 'branch:%s:%s' % (abranch, repo.get_changeset(abranch).raw_id)
+                        peers.append((n, abranch))
+                        peerbranches.add(abranch)
 
         selected = None
         tiprev = repo.tags.get('tip')
--- a/kallithea/lib/vcs/backends/base.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/lib/vcs/backends/base.py	Mon Feb 12 02:38:02 2018 +0100
@@ -1038,6 +1038,11 @@
         return get_backend(self.alias).DEFAULT_BRANCH_NAME
 
     @LazyProperty
+    def branches(self):
+        from kallithea.lib.vcs.backends import get_backend
+        return [get_backend(self.alias).DEFAULT_BRANCH_NAME]
+
+    @LazyProperty
     def short_id(self):
         return self.raw_id[:12]
 
--- a/kallithea/lib/vcs/backends/git/changeset.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/lib/vcs/backends/git/changeset.py	Mon Feb 12 02:38:02 2018 +0100
@@ -91,13 +91,19 @@
 
     @LazyProperty
     def branch(self):
-
+        # Note: This function will return one branch name for the changeset -
+        # that might not make sense in Git where branches() is a better match
+        # for the basic model
         heads = self.repository._heads(reverse=False)
-
         ref = heads.get(self.raw_id)
         if ref:
             return safe_unicode(ref)
 
+    @LazyProperty
+    def branches(self):
+        heads = self.repository._heads(reverse=True)
+        return [b for b in heads if heads[b] == self.raw_id] # FIXME: Inefficient ... and returning None!
+
     def _fix_path(self, path):
         """
         Paths are stored without trailing slash so we need to get rid off it if
--- a/kallithea/lib/vcs/backends/hg/changeset.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/lib/vcs/backends/hg/changeset.py	Mon Feb 12 02:38:02 2018 +0100
@@ -41,6 +41,10 @@
         return safe_unicode(self._ctx.branch())
 
     @LazyProperty
+    def branches(self):
+        return [safe_unicode(self._ctx.branch())]
+
+    @LazyProperty
     def closesbranch(self):
         return self._ctx.closesbranch()
 
--- a/kallithea/templates/changelog/changelog_table.html	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/templates/changelog/changelog_table.html	Mon Feb 12 02:38:02 2018 +0100
@@ -97,8 +97,10 @@
               %if cs.phase:
                 <span class="label label-phase" title="Phase">${cs.phase}</span>
               %endif
-              %if show_branch and cs.branch:
-                <span class="label label-branch" title="${_('Branch %s' % cs.branch)}">${h.link_to(cs.branch,h.url('changelog_home',repo_name=repo_name,branch=cs.branch))}</span>
+              %if show_branch:
+                %for branch in cs.branches:
+                  <span class="label label-branch" title="${_('Branch %s' % branch)}">${h.link_to(branch,h.url('changelog_home',repo_name=repo_name,branch=branch))}</span>
+                %endfor
               %endif
             </div>
           </div>
--- a/kallithea/templates/changeset/changeset.html	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/templates/changeset/changeset.html	Mon Feb 12 02:38:02 2018 +0100
@@ -68,9 +68,9 @@
                          <span class="label label-tag"  title="${_('Tag %s') % tag}">${h.link_to(tag,h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
                         %endfor
 
-                        %if c.changeset.branch:
-                         <span class="label label-branch" title="${_('Branch %s') % c.changeset.branch}">${h.link_to(c.changeset.branch,h.url('changelog_home',repo_name=c.repo_name,branch=c.changeset.branch))}</span>
-                        %endif
+                        %for branch in c.changeset.branches:
+                          <span class="label label-branch" title="${_('Branch %s') % branch}">${h.link_to(branch,h.url('changelog_home',repo_name=c.repo_name,branch=branch))}</span>
+                        %endfor
                     </span>
 
                     <div class="changes">
--- a/kallithea/templates/changeset/changeset_range.html	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/templates/changeset/changeset_range.html	Mon Feb 12 02:38:02 2018 +0100
@@ -90,11 +90,11 @@
                     <span class="label label-tag" title="${_('Tag %s') % tag}">
                     ${h.link_to(tag,h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))}</span>
                 %endfor
-                %if cs.branch:
-                <span class="label label-branch" title="${_('Branch %s') % cs.branch}">
-                   ${h.link_to(cs.branch,h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))}
-                </span>
-                %endif
+                %for branch in cs.branches:
+                  <span class="label label-branch" title="${_('Branch %s') % branch}">
+                    ${h.link_to(branch,h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))}
+                  </span>
+                %endfor
               </span>
             </div>
           </div>
--- a/kallithea/tests/vcs/test_branches.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/tests/vcs/test_branches.py	Mon Feb 12 02:38:02 2018 +0100
@@ -53,6 +53,7 @@
         )
         assert 'foobar' in self.repo.branches
         assert foobar_tip.branch == 'foobar'
+        assert foobar_tip.branches == ['foobar']
 
     def test_new_head(self):
         tip = self.repo.get_changeset()
@@ -81,6 +82,7 @@
         )
 
         assert newest_tip.branch == self.backend_class.DEFAULT_BRANCH_NAME
+        assert newest_tip.branches == [self.backend_class.DEFAULT_BRANCH_NAME]
 
     def test_branch_with_slash_in_name(self):
         self.imc.add(vcs.nodes.FileNode('extrafile', content='Some data\n'))
--- a/kallithea/tests/vcs/test_changesets.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/tests/vcs/test_changesets.py	Mon Feb 12 02:38:02 2018 +0100
@@ -78,6 +78,7 @@
         )
         assert 'foobar' in self.repo.branches
         assert foobar_tip.branch == 'foobar'
+        assert foobar_tip.branches == ['foobar']
         # 'foobar' should be the only branch that contains the new commit
         branch_tips = self.repo.branches.values()
         assert branch_tips.count(str(foobar_tip.raw_id)) == 1
@@ -109,6 +110,7 @@
         )
 
         assert newest_tip.branch == self.backend_class.DEFAULT_BRANCH_NAME
+        assert newest_tip.branches == [self.backend_class.DEFAULT_BRANCH_NAME]
 
     def test_get_changesets_respects_branch_name(self):
         tip = self.repo.get_changeset()
--- a/kallithea/tests/vcs/test_git.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/tests/vcs/test_git.py	Mon Feb 12 02:38:02 2018 +0100
@@ -309,16 +309,19 @@
         rev0 = self.repo.revisions[0]
         chset0 = self.repo.get_changeset(rev0)
         assert chset0.branch is None # should be 'master'?
+        assert chset0.branches == [] # should be 'master'?
         assert chset0.tags == []
 
         rev10 = self.repo.revisions[10]
         chset10 = self.repo.get_changeset(rev10)
         assert chset10.branch is None # should be 'master'?
+        assert chset10.branches == [] # should be 'master'?
         assert chset10.tags == []
 
         rev44 = self.repo.revisions[44]
         chset44 = self.repo.get_changeset(rev44)
         assert chset44.branch is None # should be 'web-branch'?
+        assert chset44.branches == [] # should be 'web-branch'?
 
         tip = self.repo.get_changeset('tip')
         assert 'tip' not in tip.tags # it should be?
--- a/kallithea/tests/vcs/test_hg.py	Sun Jan 28 00:16:18 2018 +0100
+++ b/kallithea/tests/vcs/test_hg.py	Mon Feb 12 02:38:02 2018 +0100
@@ -320,14 +320,17 @@
     def test_branch_and_tags(self):
         chset0 = self.repo.get_changeset(0)
         assert chset0.branch == 'default'
+        assert chset0.branches == ['default']
         assert chset0.tags == []
 
         chset10 = self.repo.get_changeset(10)
         assert chset10.branch == 'default'
+        assert chset10.branches == ['default']
         assert chset10.tags == []
 
         chset44 = self.repo.get_changeset(44)
         assert chset44.branch == 'web'
+        assert chset44.branches == ['web']
 
         tip = self.repo.get_changeset('tip')
         assert 'tip' in tip.tags