# HG changeset patch # User Mads Kiilerich # Date 2018-02-12 01:38:02 # Node ID 3dbb625d5f9c152bfec17f712c84f4a992956e6a # Parent f200ce5efce60cb63e071da106acabf3c0a98c78 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. diff --git a/kallithea/controllers/feed.py b/kallithea/controllers/feed.py --- a/kallithea/controllers/feed.py +++ b/kallithea/controllers/feed.py @@ -63,8 +63,8 @@ class FeedController(BaseRepoController) desc_msg = [(_('%s committed on %s') % (h.person(cs.author), h.fmt_date(cs.date))) + '
'] # branches, tags, bookmarks - if cs.branch: - desc_msg.append('branch: %s
' % cs.branch) + for branch in cs.branches: + desc_msg.append('branch: %s
' % branch) for book in cs.bookmarks: desc_msg.append('bookmark: %s
' % book) for tag in cs.tags: diff --git a/kallithea/controllers/files.py b/kallithea/controllers/files.py --- a/kallithea/controllers/files.py +++ b/kallithea/controllers/files.py @@ -189,7 +189,7 @@ class FilesController(BaseRepoController # 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 @@ class FilesController(BaseRepoController 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,)) diff --git a/kallithea/controllers/pullrequests.py b/kallithea/controllers/pullrequests.py --- a/kallithea/controllers/pullrequests.py +++ b/kallithea/controllers/pullrequests.py @@ -102,11 +102,11 @@ class PullrequestsController(BaseRepoCon 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') diff --git a/kallithea/lib/vcs/backends/base.py b/kallithea/lib/vcs/backends/base.py --- a/kallithea/lib/vcs/backends/base.py +++ b/kallithea/lib/vcs/backends/base.py @@ -1038,6 +1038,11 @@ class EmptyChangeset(BaseChangeset): 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] diff --git a/kallithea/lib/vcs/backends/git/changeset.py b/kallithea/lib/vcs/backends/git/changeset.py --- a/kallithea/lib/vcs/backends/git/changeset.py +++ b/kallithea/lib/vcs/backends/git/changeset.py @@ -91,13 +91,19 @@ class GitChangeset(BaseChangeset): @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 diff --git a/kallithea/lib/vcs/backends/hg/changeset.py b/kallithea/lib/vcs/backends/hg/changeset.py --- a/kallithea/lib/vcs/backends/hg/changeset.py +++ b/kallithea/lib/vcs/backends/hg/changeset.py @@ -41,6 +41,10 @@ class MercurialChangeset(BaseChangeset): return safe_unicode(self._ctx.branch()) @LazyProperty + def branches(self): + return [safe_unicode(self._ctx.branch())] + + @LazyProperty def closesbranch(self): return self._ctx.closesbranch() diff --git a/kallithea/templates/changelog/changelog_table.html b/kallithea/templates/changelog/changelog_table.html --- a/kallithea/templates/changelog/changelog_table.html +++ b/kallithea/templates/changelog/changelog_table.html @@ -97,8 +97,10 @@ %if cs.phase: ${cs.phase} %endif - %if show_branch and cs.branch: - ${h.link_to(cs.branch,h.url('changelog_home',repo_name=repo_name,branch=cs.branch))} + %if show_branch: + %for branch in cs.branches: + ${h.link_to(branch,h.url('changelog_home',repo_name=repo_name,branch=branch))} + %endfor %endif diff --git a/kallithea/templates/changeset/changeset.html b/kallithea/templates/changeset/changeset.html --- a/kallithea/templates/changeset/changeset.html +++ b/kallithea/templates/changeset/changeset.html @@ -68,9 +68,9 @@ ${h.link_to(tag,h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))} %endfor - %if c.changeset.branch: - ${h.link_to(c.changeset.branch,h.url('changelog_home',repo_name=c.repo_name,branch=c.changeset.branch))} - %endif + %for branch in c.changeset.branches: + ${h.link_to(branch,h.url('changelog_home',repo_name=c.repo_name,branch=branch))} + %endfor
diff --git a/kallithea/templates/changeset/changeset_range.html b/kallithea/templates/changeset/changeset_range.html --- a/kallithea/templates/changeset/changeset_range.html +++ b/kallithea/templates/changeset/changeset_range.html @@ -90,11 +90,11 @@ ${h.link_to(tag,h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))} %endfor - %if cs.branch: - - ${h.link_to(cs.branch,h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))} - - %endif + %for branch in cs.branches: + + ${h.link_to(branch,h.url('changeset_home',repo_name=c.cs_repo.repo_name,revision=cs.raw_id))} + + %endfor
diff --git a/kallithea/tests/vcs/test_branches.py b/kallithea/tests/vcs/test_branches.py --- a/kallithea/tests/vcs/test_branches.py +++ b/kallithea/tests/vcs/test_branches.py @@ -53,6 +53,7 @@ class BranchesTestCaseMixin(_BackendTest ) 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 @@ class BranchesTestCaseMixin(_BackendTest ) 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')) diff --git a/kallithea/tests/vcs/test_changesets.py b/kallithea/tests/vcs/test_changesets.py --- a/kallithea/tests/vcs/test_changesets.py +++ b/kallithea/tests/vcs/test_changesets.py @@ -78,6 +78,7 @@ class _ChangesetsWithCommitsTestCaseixin ) 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 @@ class _ChangesetsWithCommitsTestCaseixin ) 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() diff --git a/kallithea/tests/vcs/test_git.py b/kallithea/tests/vcs/test_git.py --- a/kallithea/tests/vcs/test_git.py +++ b/kallithea/tests/vcs/test_git.py @@ -309,16 +309,19 @@ class TestGitChangeset(object): 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? diff --git a/kallithea/tests/vcs/test_hg.py b/kallithea/tests/vcs/test_hg.py --- a/kallithea/tests/vcs/test_hg.py +++ b/kallithea/tests/vcs/test_hg.py @@ -320,14 +320,17 @@ class TestMercurialChangeset(object): 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