feature(scm): added multibranch support for git and mercurial

This commit is contained in:
RhodeCode Admin 2025-12-21 08:27:11 +01:00
parent 4c45b5eb14
commit 102205ec73
51 changed files with 446 additions and 217 deletions

View file

@ -41,8 +41,7 @@ class TestGetRepoChangeset(object):
if details == "full": if details == "full":
assert result["refs"]["bookmarks"] == getattr(commit, "bookmarks", []) assert result["refs"]["bookmarks"] == getattr(commit, "bookmarks", [])
branches = [commit.branch] if commit.branch else [] assert result["refs"]["branches"] == commit.branches
assert result["refs"]["branches"] == branches
assert result["refs"]["tags"] == commit.tags assert result["refs"]["tags"] == commit.tags
@pytest.mark.parametrize("details", ["basic", "extended", "full"]) @pytest.mark.parametrize("details", ["basic", "extended", "full"])

View file

@ -324,7 +324,7 @@ def get_repo_changeset(request, apiuser, repoid, revision, details=Optional("bas
raise JSONRPCError("ret_type must be one of %s" % (",".join(_changes_details_types))) raise JSONRPCError("ret_type must be one of %s" % (",".join(_changes_details_types)))
vcs_repo = repo.scm_instance() vcs_repo = repo.scm_instance()
pre_load = ["author", "branch", "date", "message", "parents", "status", "_commit"] pre_load = ["author", "branches", "date", "message", "parents", "status", "_commit"]
try: try:
commit = repo.get_commit(commit_id=revision, pre_load=pre_load) commit = repo.get_commit(commit_id=revision, pre_load=pre_load)
@ -383,7 +383,7 @@ def get_repo_changesets(request, apiuser, repoid, start_rev, limit, details=Opti
raise JSONRPCError("ret_type must be one of %s" % (",".join(_changes_details_types))) raise JSONRPCError("ret_type must be one of %s" % (",".join(_changes_details_types)))
limit = int(limit) limit = int(limit)
pre_load = ["author", "branch", "date", "message", "parents", "status", "_commit"] pre_load = ["author", "branches", "date", "message", "parents", "status", "_commit"]
vcs_repo = repo.scm_instance() vcs_repo = repo.scm_instance()
# SVN needs a special case to distinguish its index and commit id # SVN needs a special case to distinguish its index and commit id
@ -408,7 +408,7 @@ def get_repo_changesets(request, apiuser, repoid, start_rev, limit, details=Opti
_cs_json["diff"] = build_commit_data(vcs_repo, commit, changes_details) _cs_json["diff"] = build_commit_data(vcs_repo, commit, changes_details)
if changes_details == "full": if changes_details == "full":
_cs_json["refs"] = { _cs_json["refs"] = {
"branches": [commit.branch], "branches": commit.branches,
"bookmarks": getattr(commit, "bookmarks", []), "bookmarks": getattr(commit, "bookmarks", []),
"tags": commit.tags, "tags": commit.tags,
} }

View file

@ -444,7 +444,7 @@ class RepoAppView(BaseAppView):
def get_commit_preload_attrs(cls): def get_commit_preload_attrs(cls):
pre_load = [ pre_load = [
"author", "author",
"branch", "branches",
"date", "date",
"message", "message",
"parents", "parents",

View file

@ -24,11 +24,10 @@ from rhodecode.apps._base import ADMIN_PREFIX
from rhodecode.lib.hash_utils import md5_safe from rhodecode.lib.hash_utils import md5_safe
from rhodecode.model.db import RhodeCodeUi from rhodecode.model.db import RhodeCodeUi
from rhodecode.model.meta import Session from rhodecode.model.meta import Session
from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel
from rhodecode.tests import assert_session_flash from rhodecode.tests import assert_session_flash
from rhodecode.tests.routes import route_path from rhodecode.tests.routes import route_path
UPDATE_DATA_QUALNAME = "rhodecode.model.update.UpdateModel.get_update_data" UPDATE_DATA_QUALNAME = "rhodecode.model.update.UpdateModel.get_update_data"
@ -207,8 +206,8 @@ class TestAdminSettingsVcs(object):
assert "important_tags/v0.5" in repo.tags assert "important_tags/v0.5" in repo.tags
def test_add_same_svn_value_twice_shows_an_error_message(self, form_defaults, csrf_token, settings_util): def test_add_same_svn_value_twice_shows_an_error_message(self, form_defaults, csrf_token, settings_util):
settings_util.create_rhodecode_ui("vcs_svn_branch", "/test") settings_util.create_rhodecode_ui(VcsSettingsModel.SVN_BRANCH_SECTION, "/test")
settings_util.create_rhodecode_ui("vcs_svn_tag", "/test") settings_util.create_rhodecode_ui(VcsSettingsModel.SVN_TAG_SECTION, "/test")
response = self.app.post( response = self.app.post(
route_path("admin_settings_vcs_update"), route_path("admin_settings_vcs_update"),
@ -227,8 +226,8 @@ class TestAdminSettingsVcs(object):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"section", "section",
[ [
"vcs_svn_branch", VcsSettingsModel.SVN_BRANCH_SECTION,
"vcs_svn_tag", VcsSettingsModel.SVN_TAG_SECTION,
], ],
) )
def test_delete_svn_patterns(self, section, csrf_token, settings_util): def test_delete_svn_patterns(self, section, csrf_token, settings_util):
@ -243,8 +242,8 @@ class TestAdminSettingsVcs(object):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"section", "section",
[ [
"vcs_svn_branch", VcsSettingsModel.SVN_BRANCH_SECTION,
"vcs_svn_tag", VcsSettingsModel.SVN_TAG_SECTION,
], ],
) )
def test_delete_svn_patterns_raises_404_when_no_xhr(self, section, csrf_token, settings_util): def test_delete_svn_patterns_raises_404_when_no_xhr(self, section, csrf_token, settings_util):

View file

@ -66,6 +66,8 @@ class AdminSettingsView(BaseAppView):
c = self._get_local_tmpl_context() c = self._get_local_tmpl_context()
c.labs_active = str2bool(rhodecode.CONFIG.get("labs_settings_active", "true")) c.labs_active = str2bool(rhodecode.CONFIG.get("labs_settings_active", "true"))
c.navlist = navigation_list(self.request) c.navlist = navigation_list(self.request)
c.svn_default_branches = RhodeCodeUi.SVN_BRANCHES_PATTERNS
c.svn_default_tags = RhodeCodeUi.SVN_TAGS_PATTERNS
return c return c
@classmethod @classmethod
@ -127,6 +129,7 @@ class AdminSettingsView(BaseAppView):
c.svn_tag_patterns = model.get_global_svn_tag_patterns() c.svn_tag_patterns = model.get_global_svn_tag_patterns()
c.svn_generate_config = rhodecode.ConfigGet().get_bool(config_keys.generate_config) c.svn_generate_config = rhodecode.ConfigGet().get_bool(config_keys.generate_config)
c.svn_config_path = rhodecode.ConfigGet().get_str(config_keys.config_file_path) c.svn_config_path = rhodecode.ConfigGet().get_str(config_keys.config_file_path)
defaults = self._form_defaults() defaults = self._form_defaults()
data = render("rhodecode:templates/admin/settings/settings.mako", self._get_template_context(c), self.request) data = render("rhodecode:templates/admin/settings/settings.mako", self._get_template_context(c), self.request)

View file

@ -107,7 +107,7 @@ class HoverCardsRepoView(RepoAppView):
def hovercard_repo_commit(self): def hovercard_repo_commit(self):
c = self.load_default_context() c = self.load_default_context()
commit_id = self.request.matchdict["commit_id"] commit_id = self.request.matchdict["commit_id"]
pre_load = ["author", "branch", "date", "message"] pre_load = ["author", "branches", "date", "message"]
try: try:
c.commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id, pre_load=pre_load) c.commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id, pre_load=pre_load)
except (CommitDoesNotExistError, EmptyRepositoryError): except (CommitDoesNotExistError, EmptyRepositoryError):

View file

@ -29,7 +29,7 @@ from rhodecode.lib.ext_json import json
from rhodecode.lib.str_utils import safe_str from rhodecode.lib.str_utils import safe_str
from rhodecode.lib.vcs import nodes from rhodecode.lib.vcs import nodes
from rhodecode.lib.vcs.conf import settings from rhodecode.lib.vcs.conf import settings
from rhodecode.model.db import Session, Repository from rhodecode.model.db import Session, Repository, RhodeCodeUi
from rhodecode.tests import assert_session_flash from rhodecode.tests import assert_session_flash
from rhodecode.tests.fixtures.rc_fixture import Fixture from rhodecode.tests.fixtures.rc_fixture import Fixture
@ -181,8 +181,9 @@ class TestFilesViews(object):
response.mustcontain(msgbox % (commit.message,)) response.mustcontain(msgbox % (commit.message,))
assert_response = response.assert_response() assert_response = response.assert_response()
if commit.branch: if commit.branches:
assert_response.element_contains(".tags.tags-main .branchtag", commit.branch) for branch in commit.branches:
assert_response.element_contains(".tags.tags-main .branchtag", branch)
if commit.tags: if commit.tags:
for tag in commit.tags: for tag in commit.tags:
assert_response.element_contains(".tags.tags-main .tagtag", tag) assert_response.element_contains(".tags.tags-main .tagtag", tag)
@ -230,15 +231,32 @@ class TestFilesViews(object):
for author in expected_authors[backend.alias]: for author in expected_authors[backend.alias]:
response.mustcontain(author) response.mustcontain(author)
def test_file_source_history(self, backend, xhr_header): def test_file_source_history(self, backend, xhr_header, settings_util):
# set svn branch defaults
for branch in RhodeCodeUi.SVN_BRANCHES_PATTERNS:
settings_util.create_repo_rhodecode_ui(backend, RhodeCodeUi.SVN_BRANCH_ID, branch)
for tag in RhodeCodeUi.SVN_TAGS_PATTERNS:
settings_util.create_repo_rhodecode_ui(backend, RhodeCodeUi.SVN_TAG_ID, tag)
Session().commit()
response = self.app.get( response = self.app.get(
route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"), route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"),
extra_environ=xhr_header, extra_environ=xhr_header,
) )
assert get_node_history(backend.alias) == json.loads(response.body) assert get_node_history(backend.alias) == json.loads(response.body)
def test_file_source_history_svn(self, backend_svn, xhr_header): def test_file_source_history_svn(self, backend_svn, xhr_header, settings_util):
simple_repo = backend_svn["svn-simple-layout"] simple_repo = backend_svn["svn-simple-layout"]
# set svn branch defaults
for branch in RhodeCodeUi.SVN_BRANCHES_PATTERNS:
settings_util.create_repo_rhodecode_ui(simple_repo, RhodeCodeUi.SVN_BRANCH_ID, branch)
for tag in RhodeCodeUi.SVN_TAGS_PATTERNS:
settings_util.create_repo_rhodecode_ui(simple_repo, RhodeCodeUi.SVN_TAG_ID, tag)
Session().commit()
response = self.app.get( response = self.app.get(
route_path( route_path(
"repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py" "repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py"

View file

@ -124,7 +124,7 @@ class TestSummaryView(object):
"sh": {"count": 2, "desc": ["Bash"]}, "sh": {"count": 2, "desc": ["Bash"]},
"bat": {"count": 1, "desc": ["Batch"]}, "bat": {"count": 1, "desc": ["Batch"]},
"cfg": {"count": 1, "desc": ["Ini"]}, "cfg": {"count": 1, "desc": ["Ini"]},
"html": {"count": 1, "desc": ["EvoqueHtml", "Html"]}, "html": {"count": 1, "desc": ["Html"]},
"ini": {"count": 1, "desc": ["Ini"]}, "ini": {"count": 1, "desc": ["Ini"]},
"js": {"count": 1, "desc": ["Javascript"]}, "js": {"count": 1, "desc": ["Javascript"]},
"makefile": {"count": 1, "desc": ["Makefile", "Makefile"]}, "makefile": {"count": 1, "desc": ["Makefile", "Makefile"]},
@ -136,7 +136,7 @@ class TestSummaryView(object):
"sh": {"count": 2, "desc": ["Bash"]}, "sh": {"count": 2, "desc": ["Bash"]},
"bat": {"count": 1, "desc": ["Batch"]}, "bat": {"count": 1, "desc": ["Batch"]},
"cfg": {"count": 1, "desc": ["Ini"]}, "cfg": {"count": 1, "desc": ["Ini"]},
"html": {"count": 1, "desc": ["EvoqueHtml", "Html"]}, "html": {"count": 1, "desc": ["Html"]},
"ini": {"count": 1, "desc": ["Ini"]}, "ini": {"count": 1, "desc": ["Ini"]},
"js": {"count": 1, "desc": ["Javascript"]}, "js": {"count": 1, "desc": ["Javascript"]},
"makefile": {"count": 1, "desc": ["Makefile", "Makefile"]}, "makefile": {"count": 1, "desc": ["Makefile", "Makefile"]},
@ -144,7 +144,7 @@ class TestSummaryView(object):
"svn": { "svn": {
"py": {"count": 75, "desc": ["Python"]}, "py": {"count": 75, "desc": ["Python"]},
"rst": {"count": 16, "desc": ["Rst"]}, "rst": {"count": 16, "desc": ["Rst"]},
"html": {"count": 11, "desc": ["EvoqueHtml", "Html"]}, "html": {"count": 11, "desc": ["Html"]},
"css": {"count": 2, "desc": ["Css"]}, "css": {"count": 2, "desc": ["Css"]},
"bat": {"count": 1, "desc": ["Batch"]}, "bat": {"count": 1, "desc": ["Batch"]},
"cfg": {"count": 1, "desc": ["Ini"]}, "cfg": {"count": 1, "desc": ["Ini"]},

View file

@ -88,7 +88,7 @@ class RepoCommitsView(RepoAppView):
commit_range = commit_id_range.split("...")[:2] commit_range = commit_id_range.split("...")[:2]
try: try:
pre_load = ["author", "branch", "date", "message", "parents"] pre_load = ["author", "branches", "date", "message", "parents"]
if self.rhodecode_vcs_repo.alias == "hg": if self.rhodecode_vcs_repo.alias == "hg":
pre_load += ["hidden", "obsolete", "phase"] pre_load += ["hidden", "obsolete", "phase"]

View file

@ -192,7 +192,7 @@ class RepoCompareView(RepoAppView):
c.source_ref_type = source_ref_type c.source_ref_type = source_ref_type
c.target_ref_type = target_ref_type c.target_ref_type = target_ref_type
pre_load = ["author", "date", "message", "branch"] pre_load = ["author", "date", "message", "branches"]
c.ancestor = None c.ancestor = None
try: try:

View file

@ -99,7 +99,7 @@ class RepoFeedView(RepoAppView):
return date return date
def _get_commits(self): def _get_commits(self):
pre_load = ["author", "branch", "date", "message", "parents"] pre_load = ["author", "branches", "date", "message", "parents"]
if self.rhodecode_vcs_repo.is_empty(): if self.rhodecode_vcs_repo.is_empty():
return [] return []

View file

@ -289,7 +289,7 @@ class RepoFilesView(RepoAppView):
else: else:
commit = repo.get_commit(commit_id=commit_id) commit = repo.get_commit(commit_id=commit_id)
if commit: if commit:
branch_name = commit.branch branch_name = commit.last_branch
sha_commit_id = commit.raw_id sha_commit_id = commit.raw_id
return branch_name, sha_commit_id, is_head return branch_name, sha_commit_id, is_head
@ -715,9 +715,9 @@ class RepoFilesView(RepoAppView):
) )
c.on_branch_head = is_head c.on_branch_head = is_head
branch = c.commit.branch if (c.commit.branch and "/" not in c.commit.branch) else None branch = c.commit.last_branch if (c.commit.last_branch and "/" not in c.commit.last_branch) else None
c.branch_or_raw_id = branch or c.commit.raw_id c.branch_or_raw_id = branch or c.commit.raw_id
c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id) c.branch_name = c.commit.last_branch or h.short_id(c.commit.raw_id)
author = c.file_last_commit.author author = c.file_last_commit.author
c.authors = [[h.email(author), h.person(author, "username_or_name_or_email"), 1]] c.authors = [[h.email(author), h.person(author, "username_or_name_or_email"), 1]]
@ -973,7 +973,7 @@ class RepoFilesView(RepoAppView):
# calculate history based on tip # calculate history based on tip
tip = self.rhodecode_vcs_repo.get_commit() tip = self.rhodecode_vcs_repo.get_commit()
if commits is None: if commits is None:
pre_load = ["author", "branch"] pre_load = ["author", "branches"]
try: try:
commits = tip.get_path_history(safe_bytes(f_path), pre_load=pre_load) commits = tip.get_path_history(safe_bytes(f_path), pre_load=pre_load)
except (NodeDoesNotExistError, CommitError): except (NodeDoesNotExistError, CommitError):
@ -983,7 +983,7 @@ class RepoFilesView(RepoAppView):
history = [] history = []
commits_group = ([], _("Changesets")) commits_group = ([], _("Changesets"))
for commit in commits: for commit in commits:
branch = " (%s)" % commit.branch if commit.branch else "" branch = " (%s)" % commit.last_branch if commit.last_branch else ""
n_desc = f"r{commit.idx}:{commit.short_id}{branch}" n_desc = f"r{commit.idx}:{commit.short_id}{branch}"
commits_group[0].append((commit.raw_id, n_desc, "sha")) commits_group[0].append((commit.raw_id, n_desc, "sha"))
history.append(commits_group) history.append(commits_group)
@ -1296,9 +1296,8 @@ class RepoFilesView(RepoAppView):
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias, branch=self.db_repo.landing_ref_name) c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias, branch=self.db_repo.landing_ref_name)
if self.rhodecode_vcs_repo.is_empty(): if self.rhodecode_vcs_repo.is_empty():
# for empty repository we cannot check for current branch, we rely on # for empty repository we cannot check for current branch, we rely on default landing ref
# c.commit.branch instead _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True
_branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
else: else:
_branch_name, _sha_commit_id, is_head = self._is_valid_head( _branch_name, _sha_commit_id, is_head = self._is_valid_head(
commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
@ -1338,9 +1337,8 @@ class RepoFilesView(RepoAppView):
default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
if self.rhodecode_vcs_repo.is_empty(): if self.rhodecode_vcs_repo.is_empty():
# for empty repository we cannot check for current branch, we rely on # for empty repository we cannot check for current branch, we rely on default landing ref
# c.commit.branch instead _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True
_branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
else: else:
_branch_name, _sha_commit_id, is_head = self._is_valid_head( _branch_name, _sha_commit_id, is_head = self._is_valid_head(
commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
@ -1424,9 +1422,8 @@ class RepoFilesView(RepoAppView):
default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
if self.rhodecode_vcs_repo.is_empty(): if self.rhodecode_vcs_repo.is_empty():
# for empty repository we cannot check for current branch, we rely on # for empty repository we cannot check for current branch, we rely on default landing ref
# c.commit.branch instead _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True
_branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
else: else:
_branch_name, _sha_commit_id, is_head = self._is_valid_head( _branch_name, _sha_commit_id, is_head = self._is_valid_head(
commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
@ -1529,9 +1526,8 @@ class RepoFilesView(RepoAppView):
default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
if self.rhodecode_vcs_repo.is_empty(): if self.rhodecode_vcs_repo.is_empty():
# for empty repository we cannot check for current branch, we rely on # for empty repository we cannot check for current branch, we rely on default landing ref
# c.commit.branch instead _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True
_branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
else: else:
_branch_name, _sha_commit_id, is_head = self._is_valid_head( _branch_name, _sha_commit_id, is_head = self._is_valid_head(
commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name

View file

@ -858,7 +858,7 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
missing_requirements = False missing_requirements = False
try: try:
pre_load = ["author", "date", "message", "branch", "parents"] pre_load = ["author", "date", "message", "branches", "parents"]
pull_request_commits = pull_request_at_ver.revisions pull_request_commits = pull_request_at_ver.revisions
log.debug("Loading %s commits from %s", len(pull_request_commits), commits_source_repo) log.debug("Loading %s commits from %s", len(pull_request_commits), commits_source_repo)

View file

@ -29,6 +29,7 @@ from rhodecode.apps._base import RepoAppView
from rhodecode.apps.svn_support import config_keys from rhodecode.apps.svn_support import config_keys
from rhodecode.lib import helpers as h from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired
from rhodecode.model.db import RhodeCodeUi
from rhodecode.model.forms import RepoVcsSettingsForm from rhodecode.model.forms import RepoVcsSettingsForm
from rhodecode.model.meta import Session from rhodecode.model.meta import Session
from rhodecode.model.settings import VcsSettingsModel, SettingNotFound from rhodecode.model.settings import VcsSettingsModel, SettingNotFound
@ -39,6 +40,8 @@ log = logging.getLogger(__name__)
class RepoSettingsVcsView(RepoAppView): class RepoSettingsVcsView(RepoAppView):
def load_default_context(self): def load_default_context(self):
c = self._get_local_tmpl_context() c = self._get_local_tmpl_context()
c.svn_default_branches = RhodeCodeUi.SVN_BRANCHES_PATTERNS
c.svn_default_tags = RhodeCodeUi.SVN_TAGS_PATTERNS
return c return c
def _vcs_form_defaults(self, repo_name): def _vcs_form_defaults(self, repo_name):

View file

@ -260,11 +260,11 @@ class TestSubversionServer(object):
"first_resp, expected_match", "first_resp, expected_match",
[ [
( (
b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( 24:TortoiseSVN-1.14.9.29743 ) )", b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( 24:TortoiseSVN-1.14.9.29743 ) ) ",
None, None,
), ),
( (
b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( ) )", b"( 2 ( edit-pipeline svndiff1 accepts-svndiff2 absent-entries depth mergeinfo log-revprops ) 44:svn+ssh://rc@code.example.com/TestRepo/trunk 34:SVN/1.14.5 (x64-microsoft-windows) ( ) ) ",
None, None,
), ),
], ],

View file

@ -67,7 +67,7 @@ def _commits_as_dict(event, commit_ids, repos):
cs_data = { cs_data = {
"raw_id": commit_id, "raw_id": commit_id,
"short_id": commit_id, "short_id": commit_id,
"branch": None, "branches": [],
"git_ref_change": "tag_add", "git_ref_change": "tag_add",
"message": f"Added new tag {raw_id}", "message": f"Added new tag {raw_id}",
"author": event.actor.full_contact, "author": event.actor.full_contact,
@ -81,7 +81,7 @@ def _commits_as_dict(event, commit_ids, repos):
cs_data = { cs_data = {
"raw_id": commit_id, "raw_id": commit_id,
"short_id": commit_id, "short_id": commit_id,
"branch": None, "branches": [],
"git_ref_change": "branch_delete", "git_ref_change": "branch_delete",
"message": f"Deleted branch {raw_id}", "message": f"Deleted branch {raw_id}",
"author": event.actor.full_contact, "author": event.actor.full_contact,
@ -95,7 +95,6 @@ def _commits_as_dict(event, commit_ids, repos):
cs = vcs_repo.get_commit(commit_id) cs = vcs_repo.get_commit(commit_id)
except CommitDoesNotExistError: except CommitDoesNotExistError:
continue # maybe its in next repo continue # maybe its in next repo
cs_data = cs.__json__() cs_data = cs.__json__()
cs_data["refs"] = cs._get_refs() cs_data["refs"] = cs._get_refs()
@ -380,17 +379,19 @@ class RepoPushEvent(RepoVCSEvent):
data = super().as_dict() data = super().as_dict()
def branch_url(branch_name): def branch_url(branch_name):
return "{}/changelog?branch={}".format(data["repo"]["url"], branch_name) return f"{data['repo']['url']}/changelog?branch={branch_name}"
def tag_url(tag_name): def tag_url(tag_name):
return "{}/files/{}/".format(data["repo"]["url"], tag_name) return f"{data['repo']['url']}/files/{tag_name}/"
commits = _commits_as_dict(self, commit_ids=self.pushed_commit_ids, repos=[self.repo]) commits = _commits_as_dict(self, commit_ids=self.pushed_commit_ids, repos=[self.repo])
last_branch = None # fill branches for every single commit
last_branch = []
for commit in reversed(commits): for commit in reversed(commits):
commit["branch"] = commit["branch"] or last_branch commit["branches"] = commit["branches"] or last_branch
last_branch = commit["branch"] last_branch = commit["branches"]
issues = _issues_as_dict(commits) issues = _issues_as_dict(commits)
branches = set() branches = set()
@ -399,8 +400,9 @@ class RepoPushEvent(RepoVCSEvent):
if commit["refs"]["tags"]: if commit["refs"]["tags"]:
for tag in commit["refs"]["tags"]: for tag in commit["refs"]["tags"]:
tags.add(tag) tags.add(tag)
if commit["branch"]: if commit["branches"]:
branches.add(commit["branch"]) for branch in commit["branches"]:
branches.add(branch)
# maybe we have branches in new_refs ? # maybe we have branches in new_refs ?
try: try:

View file

@ -230,16 +230,18 @@ class CommitParsingDataHandler(object):
# special case for GIT that allows creating tags, # special case for GIT that allows creating tags,
# deleting branches without associated commit # deleting branches without associated commit
continue continue
commit_branch = commit["branch"]
if commit_branch not in branches_commits: maybe_commit_branches = commit["branches"]
_branch = branch_data[commit_branch] if commit_branch else commit_branch
branch_commits = {"branch": _branch, "branch_head": "", "commits": []}
branches_commits[commit_branch] = branch_commits
branch_commits = branches_commits[commit_branch] for commit_branch in maybe_commit_branches:
branch_commits["commits"].append(commit) if commit_branch not in branches_commits:
branch_commits["branch_head"] = commit["raw_id"] _branch = branch_data[commit_branch] if commit_branch else commit_branch
branch_commits = {"branch": _branch, "branch_head": "", "commits": []}
branches_commits[commit_branch] = branch_commits
branch_commits = branches_commits[commit_branch]
branch_commits["commits"].append(commit)
branch_commits["branch_head"] = commit["raw_id"]
return branches_commits return branches_commits

View file

@ -106,6 +106,7 @@ def get_logger(obj):
# init main celery app # init main celery app
celery_app = Celery() celery_app = Celery()
celery_app.user_options["preload"].add(preload_option_ini) celery_app.user_options["preload"].add(preload_option_ini)
celery_app.user_options["preload"].add(preload_option_ini_var) celery_app.user_options["preload"].add(preload_option_ini_var)
@ -215,6 +216,7 @@ def task_success_signal(result, **kwargs):
@signals.task_retry.connect @signals.task_retry.connect
def task_retry_signal(request, reason, einfo, **kwargs): def task_retry_signal(request, reason, einfo, **kwargs):
log.warning("Task: %s failed !! reason: %s", request, reason)
meta.Session.remove() meta.Session.remove()
closer = celery_app.conf["PYRAMID_CLOSER"] closer = celery_app.conf["PYRAMID_CLOSER"]
if closer: if closer:
@ -234,7 +236,7 @@ def task_failure_signal(task_id, exception, args, kwargs, traceback, einfo, **ka
store_exception(id(exc_info), exc_info, prefix="rhodecode-celery") store_exception(id(exc_info), exc_info, prefix="rhodecode-celery")
statsd = StatsdClient.statsd statsd = StatsdClient.statsd
if statsd: if statsd:
exc_type = "{}.{}".format(einfo.__class__.__module__, einfo.__class__.__name__) exc_type = f"{einfo.__class__.__module__}.{einfo.__class__.__name__}"
statsd.incr("rhodecode_exception_total", tags=["exc_source:celery", "type:{}".format(exc_type)]) statsd.incr("rhodecode_exception_total", tags=["exc_source:celery", "type:{}".format(exc_type)])
closer = celery_app.conf["PYRAMID_CLOSER"] closer = celery_app.conf["PYRAMID_CLOSER"]

View file

@ -750,7 +750,7 @@ def _get_diffset(
source_ref_id = pull_request.source_ref_parts.commit_id source_ref_id = pull_request.source_ref_parts.commit_id
target_ref_id = pull_request.target_ref_parts.commit_id target_ref_id = pull_request.target_ref_parts.commit_id
pre_load = ["author", "date", "message", "branch", "parents"] pre_load = ["author", "date", "message", "branches", "parents"]
target_commit_final = target_repo.get_commit(commit_id=safe_str(target_ref_id), pre_load=pre_load) target_commit_final = target_repo.get_commit(commit_id=safe_str(target_ref_id), pre_load=pre_load)
source_commit_final = source_repo.get_commit(commit_id=safe_str(source_ref_id), pre_load=pre_load) source_commit_final = source_repo.get_commit(commit_id=safe_str(source_ref_id), pre_load=pre_load)

View file

@ -391,13 +391,10 @@ class DbManage(object):
self.sa.add(hggit) self.sa.add(hggit)
# set svn branch defaults # set svn branch defaults
branches = ["/branches/*", "/trunk"] for branch in RhodeCodeUi.SVN_BRANCHES_PATTERNS:
tags = ["/tags/*"]
for branch in branches:
settings_model.create_ui_section_value(RhodeCodeUi.SVN_BRANCH_ID, branch) settings_model.create_ui_section_value(RhodeCodeUi.SVN_BRANCH_ID, branch)
for tag in tags: for tag in RhodeCodeUi.SVN_TAGS_PATTERNS:
settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag) settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
def create_auth_plugin_options(self, skip_existing=False): def create_auth_plugin_options(self, skip_existing=False):

View file

@ -694,6 +694,7 @@ def get_lexer_safe(mimetype=None, filepath=None):
defaulting to plain text if none could be found defaulting to plain text if none could be found
""" """
lexer = None lexer = None
try: try:
if mimetype: if mimetype:
lexer = get_lexer_for_mimetype(mimetype) lexer = get_lexer_for_mimetype(mimetype)

View file

@ -76,11 +76,13 @@ class Reference:
def branch(self): def branch(self):
if self.type == "branch": if self.type == "branch":
return self.name return self.name
return None
@property @property
def bookmark(self): def bookmark(self):
if self.type == "book": if self.type == "book":
return self.name return self.name
return None
@property @property
def to_str(self): def to_str(self):
@ -334,7 +336,8 @@ class BaseCommit:
""" """
repository = None repository = None
branch = None branches: list | None = None
tags: list | None = None
""" """
Depending on the backend this should be set to the branch name of the Depending on the backend this should be set to the branch name of the
@ -375,7 +378,7 @@ class BaseCommit:
"date": self.date, "date": self.date,
"author": self.author, "author": self.author,
"parents": parents, "parents": parents,
"branch": self.branch, "branches": self.branches,
} }
def __getstate__(self): def __getstate__(self):
@ -392,9 +395,9 @@ class BaseCommit:
def _get_refs(self): def _get_refs(self):
return { return {
"branches": [self.branch] if self.branch else [], "branches": self.branches if self.branches else [],
"tags": self.tags if self.tags else [],
"bookmarks": getattr(self, "bookmarks", []), "bookmarks": getattr(self, "bookmarks", []),
"tags": self.tags,
} }
@LazyProperty @LazyProperty
@ -698,12 +701,12 @@ class BaseCommit:
return self._find_next(indexes, branch) return self._find_next(indexes, branch)
def _find_next(self, indexes, branch=None): def _find_next(self, indexes, branch=None):
if branch and self.branch != branch: if branch and branch not in self.branches:
raise VCSError("Branch option used on commit not belonging to that branch") raise VCSError("Branch option used on commit not belonging to that branch")
for next_idx in indexes: for next_idx in indexes:
commit = self.repository.get_commit(commit_idx=next_idx) commit = self.repository.get_commit(commit_idx=next_idx)
if branch and branch != commit.branch: if branch and branch not in commit.branches:
continue continue
return commit return commit
raise CommitDoesNotExistError raise CommitDoesNotExistError
@ -725,7 +728,7 @@ class BaseCommit:
def walk(self, top_url=b""): def walk(self, top_url=b""):
""" """
Similar to os.walk method. Insted of filesystem it walks through Similar to os.walk method. Instead of filesystem it walks through
commit starting at given ``topurl``. Returns generator of tuples commit starting at given ``topurl``. Returns generator of tuples
(top_node, dirnodes, filenodes). (top_node, dirnodes, filenodes).
""" """
@ -796,6 +799,10 @@ class BaseCommit:
warnings.warn("Use get_path_commit instead", DeprecationWarning) warnings.warn("Use get_path_commit instead", DeprecationWarning)
return self.get_path_commit(path) return self.get_path_commit(path)
@LazyProperty
def last_branch(self) -> str | None:
return self.branches[0] if self.branches else None
class BaseRepository(object): class BaseRepository(object):
""" """
@ -1057,7 +1064,7 @@ class BaseRepository(object):
""" """
Allows index based access to the commit objects of this repository. Allows index based access to the commit objects of this repository.
""" """
pre_load = ["author", "branch", "date", "message", "parents"] pre_load = ["author", "branches", "date", "message", "parents"]
if isinstance(key, slice): if isinstance(key, slice):
return self._get_range(key, pre_load) return self._get_range(key, pre_load)
return self.get_commit(commit_idx=key, pre_load=pre_load) return self.get_commit(commit_idx=key, pre_load=pre_load)
@ -1672,7 +1679,15 @@ class EmptyCommit(BaseCommit):
""" """
def __init__( def __init__(
self, commit_id=EMPTY_COMMIT_ID, repo=None, alias=None, idx=-1, message="", author="", date=None, branch=None self,
commit_id=EMPTY_COMMIT_ID,
repo=None,
alias=None,
idx=-1,
message="",
author="",
date=None,
branch: list = None,
): ):
self._empty_commit_id = commit_id self._empty_commit_id = commit_id
# TODO: johbo: Solve idx parameter, default value does not make # TODO: johbo: Solve idx parameter, default value does not make
@ -1683,7 +1698,9 @@ class EmptyCommit(BaseCommit):
self.date = date or datetime.datetime.fromtimestamp(0) self.date = date or datetime.datetime.fromtimestamp(0)
self.repository = repo self.repository = repo
self.alias = alias self.alias = alias
self._branch = branch if branch and not isinstance(branch, list):
branch = [branch]
self._branches = branch or []
@LazyProperty @LazyProperty
def raw_id(self): def raw_id(self):
@ -1696,14 +1713,23 @@ class EmptyCommit(BaseCommit):
@LazyProperty @LazyProperty
def branch(self): def branch(self):
raise ValueError("Deprecated usage of .branch property")
return self._branches[self.idx]
# If branch was explicitly set, return it # If branch was explicitly set, return it
if self._branch: branches = self.branches
return self._branch if branches:
return branches[:1]
if self.alias: else:
from rhodecode.lib.vcs.backends import get_backend from rhodecode.lib.vcs.backends import get_backend
return get_backend(self.alias).DEFAULT_BRANCH_NAME return [get_backend(self.alias).DEFAULT_BRANCH_NAME]
@LazyProperty
def branches(self) -> list[str]:
# If branches was explicitly set, return it
if self._branches:
return self._branches
return []
@LazyProperty @LazyProperty
def short_id(self): def short_id(self):

View file

@ -96,8 +96,8 @@ class GitCommit(base.BaseCommit):
value = utcdate_fromtimestamp(*value) value = utcdate_fromtimestamp(*value)
elif attr == "parents": elif attr == "parents":
value = self._make_commits(value) value = self._make_commits(value)
elif attr == "branch": elif attr == "branches":
value = self._set_branch(value) value = value
self.__dict__[attr] = value self.__dict__[attr] = value
@LazyProperty @LazyProperty
@ -154,15 +154,10 @@ class GitCommit(base.BaseCommit):
branches.append(name) branches.append(name)
return branches return branches
def _set_branch(self, branches):
if branches:
# actually commit can have multiple branches in git
return safe_str(branches[0])
@LazyProperty @LazyProperty
def branch(self): def branches(self) -> list[str]:
branches = self._remote.branch(self.raw_id) branches = self._remote.branches(self.raw_id)
return self._set_branch(branches) return branches
def _get_path_tree_id_and_type(self, path: bytes): def _get_path_tree_id_and_type(self, path: bytes):
if path in self._path_type_cache: if path in self._path_type_cache:

View file

@ -78,8 +78,10 @@ class MercurialCommit(base.BaseCommit):
result = self._remote.bulk_request(self.raw_id, pre_load) result = self._remote.bulk_request(self.raw_id, pre_load)
for attr, value in result.items(): for attr, value in result.items():
if attr in ["author", "branch", "message"]: if attr in ["author", "message"]:
value = safe_str(value) value = safe_str(value)
elif attr in ["branches"]:
value = list(value)
elif attr == "affected_files": elif attr == "affected_files":
value = list(map(safe_str, value)) value = list(map(safe_str, value))
elif attr == "date": elif attr == "date":
@ -96,8 +98,8 @@ class MercurialCommit(base.BaseCommit):
return tags return tags
@LazyProperty @LazyProperty
def branch(self): def branches(self) -> list[str]:
return safe_str(self._remote.ctx_branch(self.raw_id)) return self._remote.ctx_branch(self.raw_id)
@LazyProperty @LazyProperty
def bookmarks(self): def bookmarks(self):

View file

@ -78,6 +78,10 @@ class SubversionCommit(base.BaseCommit):
def _properties(self): def _properties(self):
return self._remote.revision_properties(self._svn_rev) return self._remote.revision_properties(self._svn_rev)
@LazyProperty
def branches(self) -> list[str]:
return self._remote.revision_branches(self._svn_rev)
@LazyProperty @LazyProperty
def parents(self): def parents(self):
parent_idx = self.idx - 1 parent_idx = self.idx - 1

View file

@ -46,7 +46,6 @@ from rhodecode.lib.vcs.exceptions import (
NodeDoesNotExistError, NodeDoesNotExistError,
) )
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -127,7 +126,9 @@ class SubversionRepository(base.BaseRepository):
@LazyProperty @LazyProperty
def branches(self): def branches(self):
return self._tags_or_branches("vcs_svn_branch") from rhodecode.model.settings import VcsSettingsModel
return self._tags_or_branches(VcsSettingsModel.SVN_BRANCH_SECTION)
@LazyProperty @LazyProperty
def branches_closed(self): def branches_closed(self):
@ -147,7 +148,9 @@ class SubversionRepository(base.BaseRepository):
@LazyProperty @LazyProperty
def tags(self): def tags(self):
return self._tags_or_branches("vcs_svn_tag") from rhodecode.model.settings import VcsSettingsModel
return self._tags_or_branches(VcsSettingsModel.SVN_TAG_SECTION)
def _tags_or_branches(self, config_section): def _tags_or_branches(self, config_section):
found_items = {} found_items = {}

View file

@ -54,6 +54,8 @@ EXCEPTIONS_MAP = {
def _remote_call(url, payload, exceptions_map, session, retries=3): def _remote_call(url, payload, exceptions_map, session, retries=3):
response = None
for attempt in range(retries): for attempt in range(retries):
try: try:
response = session.post(url, data=msgpack.packb(payload)) response = session.post(url, data=msgpack.packb(payload))
@ -73,6 +75,11 @@ def _remote_call(url, payload, exceptions_map, session, retries=3):
else: else:
raise raise
if response is None:
e = pycurl.E_RECV_ERROR
log.error("Failed to connect to remote server: %s", e)
raise exceptions.HttpVCSCommunicationError(e)
if response.status_code >= 400: if response.status_code >= 400:
content_type = response.content_type content_type = response.content_type
log.error("Call to %s returned non 200 HTTP code: %s [%s]", url, response.status_code, content_type) log.error("Call to %s returned non 200 HTTP code: %s [%s]", url, response.status_code, content_type)

View file

@ -501,7 +501,10 @@ class RhodeCodeUi(Base, BaseModel):
# SVN PATTERNS # SVN PATTERNS
SVN_BRANCH_ID = "vcs_svn_branch" SVN_BRANCH_ID = "vcs_svn_branch"
SVN_BRANCHES_PATTERNS = ["/branches/*", "/trunk"]
SVN_TAG_ID = "vcs_svn_tag" SVN_TAG_ID = "vcs_svn_tag"
SVN_TAGS_PATTERNS = ["/tags/*"]
ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None) ui_section = Column("ui_section", String(255), nullable=True, unique=None, default=None)
@ -2685,7 +2688,7 @@ class Repository(Base, BaseModel):
empty = scm_repo is None or scm_repo.is_empty() empty = scm_repo is None or scm_repo.is_empty()
if not empty: if not empty:
cs_cache = scm_repo.get_commit(pre_load=["author", "date", "message", "parents", "branch"]) cs_cache = scm_repo.get_commit(pre_load=["author", "date", "message", "parents", "branches"])
repo_commit_count = scm_repo.count() repo_commit_count = scm_repo.count()
else: else:
cs_cache = EmptyCommit() cs_cache = EmptyCommit()

View file

@ -52,6 +52,7 @@ from rhodecode.lib.pyramid_utils import get_current_request
from rhodecode import BACKENDS from rhodecode import BACKENDS
from rhodecode.lib import helpers from rhodecode.lib import helpers
from rhodecode.model import validators as v from rhodecode.model import validators as v
from rhodecode.model.settings import VcsSettingsModel
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -477,8 +478,8 @@ def ApplicationUiSettingsForm(localizer):
class _ApplicationUiSettingsForm(_BaseVcsSettingsForm): class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
extensions_hggit = v.StringBoolean(if_missing=False) extensions_hggit = v.StringBoolean(if_missing=False)
new_svn_branch = v.ValidSvnPattern(localizer, section="vcs_svn_branch") new_svn_branch = v.ValidSvnPattern(localizer, section=VcsSettingsModel.SVN_BRANCH_SECTION)
new_svn_tag = v.ValidSvnPattern(localizer, section="vcs_svn_tag") new_svn_tag = v.ValidSvnPattern(localizer, section=VcsSettingsModel.SVN_TAG_SECTION)
return _ApplicationUiSettingsForm return _ApplicationUiSettingsForm
@ -488,8 +489,8 @@ def RepoVcsSettingsForm(localizer, repo_name):
class _RepoVcsSettingsForm(_BaseVcsSettingsForm): class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
inherit_global_settings = v.StringBoolean(if_missing=False) inherit_global_settings = v.StringBoolean(if_missing=False)
new_svn_branch = v.ValidSvnPattern(localizer, section="vcs_svn_branch", repo_name=repo_name) new_svn_branch = v.ValidSvnPattern(localizer, section=VcsSettingsModel.SVN_BRANCH_SECTION, repo_name=repo_name)
new_svn_tag = v.ValidSvnPattern(localizer, section="vcs_svn_tag", repo_name=repo_name) new_svn_tag = v.ValidSvnPattern(localizer, section=VcsSettingsModel.SVN_TAG_SECTION, repo_name=repo_name)
return _RepoVcsSettingsForm return _RepoVcsSettingsForm

View file

@ -1221,7 +1221,7 @@ class PullRequestModel(BaseModel):
# re-compute commit ids # re-compute commit ids
old_commit_ids = pull_request.revisions old_commit_ids = pull_request.revisions
pre_load = ["author", "date", "message", "branch"] pre_load = ["author", "date", "message", "branches"]
commit_ranges = target_repo.compare( commit_ranges = target_repo.compare(
target_commit.raw_id, source_commit.raw_id, source_repo, merge=True, pre_load=pre_load target_commit.raw_id, source_commit.raw_id, source_repo, merge=True, pre_load=pre_load
) )
@ -2338,7 +2338,7 @@ class MergeCheck(object):
# for mercurial we can always figure out the branch from the commit # for mercurial we can always figure out the branch from the commit
# in case of bookmark # in case of bookmark
target_commit = pull_request.target_repo.get_commit(target_commit_id) target_commit = pull_request.target_repo.get_commit(target_commit_id)
branch_name = target_commit.branch branch_name = target_commit.last_branch
rule, branch_perm = auth_user.get_rule_and_branch_permission(pull_request.target_repo.repo_name, branch_name) rule, branch_perm = auth_user.get_rule_and_branch_permission(pull_request.target_repo.repo_name, branch_name)
if branch_perm and branch_perm == "branch.none": if branch_perm and branch_perm == "branch.none":

View file

@ -489,7 +489,7 @@ class ScmModel(BaseModel):
imc.change(FileNode(f_path, content, mode=commit.get_file_mode(f_path))) imc.change(FileNode(f_path, content, mode=commit.get_file_mode(f_path)))
try: try:
# TODO: handle pre-push action ! # TODO: handle pre-push action !
tip = imc.commit(message=message, author=author, parents=[commit], branch=branch or commit.branch) tip = imc.commit(message=message, author=author, parents=[commit], branch=branch or commit.last_branch)
except Exception as e: except Exception as e:
log.error(traceback.format_exc()) log.error(traceback.format_exc())
raise IMCCommitError(str(e)) raise IMCCommitError(str(e))
@ -831,7 +831,7 @@ class ScmModel(BaseModel):
imc.add(FileNode(path, content=content)) imc.add(FileNode(path, content=content))
# TODO: handle pre push scenario # TODO: handle pre push scenario
tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.branch) tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.last_branch)
self.mark_for_invalidation(repo.repo_name) self.mark_for_invalidation(repo.repo_name)
if trigger_push_hook: if trigger_push_hook:
@ -874,7 +874,7 @@ class ScmModel(BaseModel):
try: try:
# TODO: handle pre push scenario commit changes # TODO: handle pre push scenario commit changes
tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.branch) tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.last_branch)
except NodeNotChangedError: except NodeNotChangedError:
raise raise
except Exception as e: except Exception as e:
@ -910,7 +910,7 @@ class ScmModel(BaseModel):
imc.change(file_node) imc.change(file_node)
try: try:
tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.branch) tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.last_branch)
except NodeNotChangedError: except NodeNotChangedError:
raise raise
except Exception as e: except Exception as e:
@ -963,7 +963,7 @@ class ScmModel(BaseModel):
imc.remove(FileNode(path, content=content)) imc.remove(FileNode(path, content=content))
# TODO: handle pre push scenario # TODO: handle pre push scenario
tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.branch) tip = imc.commit(message=message, author=author, parents=parents, branch=parent_commit.last_branch)
self.mark_for_invalidation(repo.repo_name) self.mark_for_invalidation(repo.repo_name)
if trigger_push_hook: if trigger_push_hook:

View file

@ -337,7 +337,7 @@ def assert_repo_settings(func):
return _wrapper return _wrapper
class IssueTrackerSettingsModel(object): class IssueTrackerSettingsModel:
INHERIT_SETTINGS = "inherit_issue_tracker_settings" INHERIT_SETTINGS = "inherit_issue_tracker_settings"
SETTINGS_PREFIX = "issuetracker_" SETTINGS_PREFIX = "issuetracker_"
@ -450,7 +450,7 @@ class IssueTrackerSettingsModel(object):
return setting return setting
class VcsSettingsModel(object): class VcsSettingsModel:
INHERIT_SETTINGS = "inherit_vcs_settings" INHERIT_SETTINGS = "inherit_vcs_settings"
GENERAL_SETTINGS = ( GENERAL_SETTINGS = (
"use_outdated_comments", "use_outdated_comments",

View file

@ -45,6 +45,9 @@ function registerRCRoutes() {
pyroutes.register('admin_security_modify_allowed_vcs_client_versions', '/_admin/security/modify/allowed_vcs_client_versions', []); pyroutes.register('admin_security_modify_allowed_vcs_client_versions', '/_admin/security/modify/allowed_vcs_client_versions', []);
pyroutes.register('admin_security_update', '/_admin/security/audit/update', []); pyroutes.register('admin_security_update', '/_admin/security/audit/update', []);
pyroutes.register('admin_settings', '/_admin/settings', []); pyroutes.register('admin_settings', '/_admin/settings', []);
pyroutes.register('admin_settings_ai', '/_admin/settings/ai', []);
pyroutes.register('admin_settings_ai_update', '/_admin/settings/ai/update', []);
pyroutes.register('admin_settings_ai_update_models', '/_admin/settings/ai/model/version', []);
pyroutes.register('admin_settings_email', '/_admin/settings/email', []); pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []); pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []); pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
@ -277,6 +280,7 @@ function registerRCRoutes() {
pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']); pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']); pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
pyroutes.register('pullrequest_start_ai_code_review', '/%(repo_name)s/pull-request/%(pull_request_id)s/ai/review', ['repo_name', 'pull_request_id']);
pyroutes.register('pullrequest_todos', '/%(repo_name)s/pull-request/%(pull_request_id)s/todos', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_todos', '/%(repo_name)s/pull-request/%(pull_request_id)s/todos', ['repo_name', 'pull_request_id']);
pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']); pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
pyroutes.register('register', '/_admin/register', []); pyroutes.register('register', '/_admin/register', []);

View file

@ -150,7 +150,12 @@
</div> </div>
</div> </div>
<div class="label"> <div class="label">
<span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span> <span class="help-block">
${_('Patterns for identifying SVN branches and tags.')}
${_('Examples defaults')}:<br/>
Branches: ${", ".join(c.svn_default_branches)}<br/>
Tags: ${", ".join(c.svn_default_tags)}
</span>
</div> </div>
<div class="field branch_patterns"> <div class="field branch_patterns">

View file

@ -333,7 +333,7 @@
var _html = ''; var _html = '';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> ' _html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
.replace('__branch__', data.results[0].branch) .replace('__branch__', data.results[0].branches.join(","))
.replace('__rev__', 'r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0, 6))) .replace('__rev__', 'r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0, 6)))
.replace('__title__', data.results[0].message) .replace('__title__', data.results[0].message)
.replace('__url__', pyroutes.url('repo_commit', { .replace('__url__', pyroutes.url('repo_commit', {
@ -342,7 +342,7 @@
})); }));
_html += ' | '; _html += ' | ';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> ' _html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
.replace('__branch__', data.results[1].branch) .replace('__branch__', data.results[1].branches.join(","))
.replace('__rev__', 'r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0, 6))) .replace('__rev__', 'r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0, 6)))
.replace('__title__', data.results[1].message) .replace('__title__', data.results[1].message)
.replace('__url__', pyroutes.url('repo_commit', { .replace('__url__', pyroutes.url('repo_commit', {
@ -380,7 +380,7 @@
var _html = ''; var _html = '';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>' _html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
.replace('__branch__', data.results[0].branch) .replace('__branch__', data.results[0].branches.join(","))
.replace('__rev__', 'r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0, 6))) .replace('__rev__', 'r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0, 6)))
.replace('__title__', data.results[0].message) .replace('__title__', data.results[0].message)
.replace('__url__', pyroutes.url('repo_commit', { .replace('__url__', pyroutes.url('repo_commit', {
@ -389,7 +389,7 @@
})); }));
_html += ' | '; _html += ' | ';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>' _html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
.replace('__branch__', data.results[1].branch) .replace('__branch__', data.results[1].branches.join(","))
.replace('__rev__', 'r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0, 6))) .replace('__rev__', 'r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0, 6)))
.replace('__title__', data.results[1].message) .replace('__title__', data.results[1].message)
.replace('__url__', pyroutes.url('repo_commit', { .replace('__url__', pyroutes.url('repo_commit', {

View file

@ -104,11 +104,11 @@
%endif %endif
## branch ## branch
%if commit.branch: %for branch in commit.branches:
<span class="tag branchtag" title="${h.tooltip(_('Branch %s') % commit.branch)}"> <span class="tag branchtag" title="${h.tooltip(_('Branch %s') % branch)}">
<a href="${h.route_path('repo_commits',repo_name=c.repo_name,_query=dict(branch=commit.branch))}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a> <a href="${h.route_path('repo_commits',repo_name=c.repo_name,_query=dict(branch=branch))}"><i class="icon-code-fork"></i>${h.shorter(branch)}</a>
</span> </span>
%endif %endfor
## bookmarks ## bookmarks
%if h.is_hg(c.rhodecode_repo): %if h.is_hg(c.rhodecode_repo):

View file

@ -5,22 +5,6 @@
fileTreeRefs = {} fileTreeRefs = {}
</script> </script>
% if h.is_svn(c.rhodecode_repo):
## since SVN doesn't have an commit<->refs association, we simply inject it
## based on our at_rev marker
% if at_rev and at_rev.startswith('branches/'):
<%
commit.branch = at_rev
%>
% endif
% if at_rev and at_rev.startswith('tags/'):
<%
commit.tags.append(at_rev)
%>
% endif
% endif
%if commit.merge: %if commit.merge:
<span class="mergetag tag"> <span class="mergetag tag">
<i class="icon-merge">${_('merge')}</i> <i class="icon-merge">${_('merge')}</i>
@ -47,13 +31,13 @@
</script> </script>
%endfor %endfor
%if commit.branch: %for branch in commit.branches:
<span class="branchtag tag" title="${h.tooltip(_('Branch %s') % commit.branch)}"> <span class="branchtag tag" title="${h.tooltip(_('Branch %s') % branch)}">
<a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=commit.raw_id,_query=dict(at=commit.branch))}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a> <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=commit.raw_id,_query=dict(at=branch))}"><i class="icon-code-fork"></i>${h.shorter(branch)}</a>
</span> </span>
<script> <script>
fileTreeRefs["${commit.branch}"] = {raw_id: "${commit.raw_id}", type:"branch", text: "${commit.branch}"}; fileTreeRefs["${branch}"] = {raw_id: "${commit.raw_id}", type:"branch", text: "${branch}"};
</script> </script>
%endif %endfor
</%def> </%def>

View file

@ -25,12 +25,12 @@
<div class="box"> <div class="box">
<div class="edit-file-title"> <div class="edit-file-title">
<span class="title-heading">${_('Add new file')} @ <code>${h.show_id(c.commit)}</code></span> <span class="title-heading">${_('Add new file')}</code></span>
% if c.commit.branch: % for branch in c.commit.branches:
<span class="tag branchtag"> <span class="tag branchtag">
<i class="icon-branch"></i> ${c.commit.branch} <i class="icon-branch"></i> ${branch}
</span> </span>
% endif % endfor
</div> </div>
${h.secure_form(h.route_path('repo_files_create_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)} ${h.secure_form(h.route_path('repo_files_create_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}

View file

@ -22,12 +22,12 @@
<div class="box"> <div class="box">
<div class="edit-file-title"> <div class="edit-file-title">
<span class="title-heading">${_('Delete file')} @ <code>${h.show_id(c.commit)}</code></span> <span class="title-heading">${_('Deleting file')}</span>
% if c.commit.branch: % for branch in c.commit.branches:
<span class="tag branchtag"> <span class="tag branchtag">
<i class="icon-branch"></i> ${c.commit.branch} <i class="icon-branch"></i> ${branch}
</span> </span>
% endif % endfor
</div> </div>
${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)} ${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}

View file

@ -22,12 +22,12 @@
<div class="box"> <div class="box">
<div class="edit-file-title"> <div class="edit-file-title">
<span class="title-heading">${_('Edit file')} @ <code>${h.show_id(c.commit)}</code></span> <span class="title-heading">${_('Editing file')}</code></span>
% if c.commit.branch: % for branch in c.commit.branches:
<span class="tag branchtag"> <span class="tag branchtag">
<i class="icon-branch"></i> ${c.commit.branch} <i class="icon-branch"></i> ${branch}
</span> </span>
% endif % endfor
</div> </div>
${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)} ${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}

View file

@ -27,7 +27,7 @@
</a> </a>
% else: % else:
<a class="btn btn-default" href="${h.route_path('repo_file_download',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}"> <a class="btn btn-default" href="${h.route_path('repo_file_download',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">
${_('Download file')} ${_('Download')}
</a> </a>
% endif % endif
@ -40,9 +40,8 @@
${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query),class_="btn btn-danger")} ${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query),class_="btn btn-danger")}
% else: % else:
<a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}"> <a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}">
${_('Edit on branch: ')}<code>${c.branch_name}</code> ${_('Edit')}
</a> </a>
<a class="btn btn-danger" href="${h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}"> <a class="btn btn-danger" href="${h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}">
${_('Delete')} ${_('Delete')}
</a> </a>

View file

@ -23,15 +23,15 @@
## Template for uploads ## Template for uploads
<div class="edit-file-title"> <div class="edit-file-title">
% if c.replace_binary: % if c.replace_binary:
<span class="title-heading">${_('Replace content of')} <b>${c.f_path}</b> @ <code>${h.show_id(c.commit)}</code></span> <span class="title-heading">${_('Replace content of')} <b>${c.f_path}</b> </code></span>
% else: % else:
<span class="title-heading">${_('Upload new file')} @ <code>${h.show_id(c.commit)}</code></span> <span class="title-heading">${_('Upload new file')} </span>
% endif % endif
% if c.commit.branch: % for branch in c.commit.branches:
<span class="tag branchtag"> <span class="tag branchtag">
<i class="icon-branch"></i> ${c.commit.branch} <i class="icon-branch"></i> ${branch}
</span> </span>
% endif % endfor
</div> </div>
% if not c.replace_binary: % if not c.replace_binary:

View file

@ -73,11 +73,11 @@
%endfor %endfor
## branch ## branch
%if cs.branch: %for branch in cs.branches:
<span class="branchtag tag" title="${h.tooltip(_('Branch %s') % cs.branch)}"> <span class="branchtag tag" title="${h.tooltip(_('Branch %s') % branch)}">
<a href="${h.route_path('repo_commits',repo_name=c.repo_name,_query=dict(branch=cs.branch))}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a> <a href="${h.route_path('repo_commits',repo_name=c.repo_name,_query=dict(branch=branch))}"><i class="icon-code-fork"></i>${h.shorter(branch)}</a>
</span> </span>
%endif %endfor
</div> </div>
</td> </td>
<td class="td-comments"> <td class="td-comments">

View file

@ -1 +1,92 @@
{"results": [{"text": "Changesets", "children": [{"text": "r15:16", "at_rev": "", "type": "sha", "id": "16"}, {"text": "r12:13", "at_rev": "", "type": "sha", "id": "13"}, {"text": "r7:8", "at_rev": "", "type": "sha", "id": "8"}, {"text": "r3:4", "at_rev": "", "type": "sha", "id": "4"}, {"text": "r2:3", "at_rev": "", "type": "sha", "id": "3"}]}, {"text": "Branches", "children": [{"text": "branches/add-docs", "at_rev": "branches/add-docs", "type": "branch", "id": "26"}, {"text": "branches/argparse", "at_rev": "branches/argparse", "type": "branch", "id": "26"}, {"text": "trunk", "at_rev": "trunk", "type": "branch", "id": "26"}]}, {"text": "Tags", "children": [{"text": "tags/v0.1", "at_rev": "tags/v0.1", "type": "tag", "id": "26"}, {"text": "tags/v0.2", "at_rev": "tags/v0.2", "type": "tag", "id": "26"}, {"text": "tags/v0.3", "at_rev": "tags/v0.3", "type": "tag", "id": "26"}, {"text": "tags/v0.5", "at_rev": "tags/v0.5", "type": "tag", "id": "26"}]}], "more": false} {
"results": [
{
"text": "Changesets",
"children": [
{
"text": "r15:16 (trunk)",
"at_rev": "",
"type": "sha",
"id": "16"
},
{
"text": "r12:13 (trunk)",
"at_rev": "",
"type": "sha",
"id": "13"
},
{
"text": "r7:8 (trunk)",
"at_rev": "",
"type": "sha",
"id": "8"
},
{
"text": "r3:4 (trunk)",
"at_rev": "",
"type": "sha",
"id": "4"
},
{
"text": "r2:3 (trunk)",
"at_rev": "",
"type": "sha",
"id": "3"
}
]
},
{
"text": "Branches",
"children": [
{
"text": "branches/add-docs",
"at_rev": "branches/add-docs",
"type": "branch",
"id": "26"
},
{
"text": "branches/argparse",
"at_rev": "branches/argparse",
"type": "branch",
"id": "26"
},
{
"text": "trunk",
"at_rev": "trunk",
"type": "branch",
"id": "26"
}
]
},
{
"text": "Tags",
"children": [
{
"text": "tags/v0.1",
"at_rev": "tags/v0.1",
"type": "tag",
"id": "26"
},
{
"text": "tags/v0.2",
"at_rev": "tags/v0.2",
"type": "tag",
"id": "26"
},
{
"text": "tags/v0.3",
"at_rev": "tags/v0.3",
"type": "tag",
"id": "26"
},
{
"text": "tags/v0.5",
"at_rev": "tags/v0.5",
"type": "tag",
"id": "26"
}
]
}
],
"more": false
}

View file

@ -1 +1,58 @@
{"results": [{"text": "Changesets", "children": [{"text": "r382:383", "at_rev": "", "type": "sha", "id": "383"}, {"text": "r323:324", "at_rev": "", "type": "sha", "id": "324"}, {"text": "r322:323", "at_rev": "", "type": "sha", "id": "323"}, {"text": "r299:300", "at_rev": "", "type": "sha", "id": "300"}, {"text": "r277:278", "at_rev": "", "type": "sha", "id": "278"}, {"text": "r273:274", "at_rev": "", "type": "sha", "id": "274"}, {"text": "r270:271", "at_rev": "", "type": "sha", "id": "271"}, {"text": "r269:270", "at_rev": "", "type": "sha", "id": "270"}, {"text": "r263:264", "at_rev": "", "type": "sha", "id": "264"}, {"text": "r261:262", "at_rev": "", "type": "sha", "id": "262"}, {"text": "r251:252", "at_rev": "", "type": "sha", "id": "252"}, {"text": "r208:209", "at_rev": "", "type": "sha", "id": "209"}, {"text": "r202:203", "at_rev": "", "type": "sha", "id": "203"}, {"text": "r173:174", "at_rev": "", "type": "sha", "id": "174"}, {"text": "r172:173", "at_rev": "", "type": "sha", "id": "173"}, {"text": "r171:172", "at_rev": "", "type": "sha", "id": "172"}, {"text": "r145:146", "at_rev": "", "type": "sha", "id": "146"}, {"text": "r144:145", "at_rev": "", "type": "sha", "id": "145"}, {"text": "r140:141", "at_rev": "", "type": "sha", "id": "141"}, {"text": "r134:135", "at_rev": "", "type": "sha", "id": "135"}, {"text": "r107:108", "at_rev": "", "type": "sha", "id": "108"}, {"text": "r106:107", "at_rev": "", "type": "sha", "id": "107"}, {"text": "r100:101", "at_rev": "", "type": "sha", "id": "101"}, {"text": "r94:95", "at_rev": "", "type": "sha", "id": "95"}, {"text": "r85:86", "at_rev": "", "type": "sha", "id": "86"}, {"text": "r73:74", "at_rev": "", "type": "sha", "id": "74"}, {"text": "r72:73", "at_rev": "", "type": "sha", "id": "73"}, {"text": "r71:72", "at_rev": "", "type": "sha", "id": "72"}, {"text": "r69:70", "at_rev": "", "type": "sha", "id": "70"}, {"text": "r67:68", "at_rev": "", "type": "sha", "id": "68"}, {"text": "r63:64", "at_rev": "", "type": "sha", "id": "64"}, {"text": "r62:63", "at_rev": "", "type": "sha", "id": "63"}, {"text": "r61:62", "at_rev": "", "type": "sha", "id": "62"}, {"text": "r50:51", "at_rev": "", "type": "sha", "id": "51"}, {"text": "r49:50", "at_rev": "", "type": "sha", "id": "50"}, {"text": "r48:49", "at_rev": "", "type": "sha", "id": "49"}, {"text": "r47:48", "at_rev": "", "type": "sha", "id": "48"}, {"text": "r46:47", "at_rev": "", "type": "sha", "id": "47"}, {"text": "r45:46", "at_rev": "", "type": "sha", "id": "46"}, {"text": "r41:42", "at_rev": "", "type": "sha", "id": "42"}, {"text": "r39:40", "at_rev": "", "type": "sha", "id": "40"}, {"text": "r37:38", "at_rev": "", "type": "sha", "id": "38"}, {"text": "r25:26", "at_rev": "", "type": "sha", "id": "26"}, {"text": "r23:24", "at_rev": "", "type": "sha", "id": "24"}, {"text": "r8:9", "at_rev": "", "type": "sha", "id": "9"}, {"text": "r7:8", "at_rev": "", "type": "sha", "id": "8"}]}, {"text": "Branches", "children": []}, {"text": "Tags", "children": []}], "more": false} {
"results": [
{
"text": "Changesets",
"children": [
{ "text": "r382:383 (trunk)", "at_rev": "", "type": "sha", "id": "383" },
{ "text": "r323:324 (trunk)", "at_rev": "", "type": "sha", "id": "324" },
{ "text": "r322:323 (trunk)", "at_rev": "", "type": "sha", "id": "323" },
{ "text": "r299:300 (trunk)", "at_rev": "", "type": "sha", "id": "300" },
{ "text": "r277:278 (trunk)", "at_rev": "", "type": "sha", "id": "278" },
{ "text": "r273:274 (trunk)", "at_rev": "", "type": "sha", "id": "274" },
{ "text": "r270:271 (trunk)", "at_rev": "", "type": "sha", "id": "271" },
{ "text": "r269:270 (trunk)", "at_rev": "", "type": "sha", "id": "270" },
{ "text": "r263:264 (trunk)", "at_rev": "", "type": "sha", "id": "264" },
{ "text": "r261:262 (trunk)", "at_rev": "", "type": "sha", "id": "262" },
{ "text": "r251:252 (trunk)", "at_rev": "", "type": "sha", "id": "252" },
{ "text": "r208:209 (trunk)", "at_rev": "", "type": "sha", "id": "209" },
{ "text": "r202:203 (trunk)", "at_rev": "", "type": "sha", "id": "203" },
{ "text": "r173:174 (trunk)", "at_rev": "", "type": "sha", "id": "174" },
{ "text": "r172:173 (trunk)", "at_rev": "", "type": "sha", "id": "173" },
{ "text": "r171:172 (trunk)", "at_rev": "", "type": "sha", "id": "172" },
{ "text": "r145:146 (trunk)", "at_rev": "", "type": "sha", "id": "146" },
{ "text": "r144:145 (trunk)", "at_rev": "", "type": "sha", "id": "145" },
{ "text": "r140:141 (trunk)", "at_rev": "", "type": "sha", "id": "141" },
{ "text": "r134:135 (trunk)", "at_rev": "", "type": "sha", "id": "135" },
{ "text": "r107:108 (trunk)", "at_rev": "", "type": "sha", "id": "108" },
{ "text": "r106:107 (trunk)", "at_rev": "", "type": "sha", "id": "107" },
{ "text": "r100:101 (trunk)", "at_rev": "", "type": "sha", "id": "101" },
{ "text": "r94:95 (trunk)", "at_rev": "", "type": "sha", "id": "95" },
{ "text": "r85:86 (trunk)", "at_rev": "", "type": "sha", "id": "86" },
{ "text": "r73:74 (trunk)", "at_rev": "", "type": "sha", "id": "74" },
{ "text": "r72:73 (trunk)", "at_rev": "", "type": "sha", "id": "73" },
{ "text": "r71:72 (trunk)", "at_rev": "", "type": "sha", "id": "72" },
{ "text": "r69:70 (trunk)", "at_rev": "", "type": "sha", "id": "70" },
{ "text": "r67:68 (trunk)", "at_rev": "", "type": "sha", "id": "68" },
{ "text": "r63:64 (trunk)", "at_rev": "", "type": "sha", "id": "64" },
{ "text": "r62:63 (trunk)", "at_rev": "", "type": "sha", "id": "63" },
{ "text": "r61:62 (trunk)", "at_rev": "", "type": "sha", "id": "62" },
{ "text": "r50:51 (trunk)", "at_rev": "", "type": "sha", "id": "51" },
{ "text": "r49:50 (trunk)", "at_rev": "", "type": "sha", "id": "50" },
{ "text": "r48:49 (trunk)", "at_rev": "", "type": "sha", "id": "49" },
{ "text": "r47:48 (trunk)", "at_rev": "", "type": "sha", "id": "48" },
{ "text": "r46:47 (trunk)", "at_rev": "", "type": "sha", "id": "47" },
{ "text": "r45:46 (trunk)", "at_rev": "", "type": "sha", "id": "46" },
{ "text": "r41:42 (trunk)", "at_rev": "", "type": "sha", "id": "42" },
{ "text": "r39:40 (trunk)", "at_rev": "", "type": "sha", "id": "40" },
{ "text": "r37:38 (trunk)", "at_rev": "", "type": "sha", "id": "38" },
{ "text": "r25:26 (trunk)", "at_rev": "", "type": "sha", "id": "26" },
{ "text": "r23:24 (trunk)", "at_rev": "", "type": "sha", "id": "24" },
{ "text": "r8:9 (trunk)", "at_rev": "", "type": "sha", "id": "9" },
{ "text": "r7:8 (trunk)", "at_rev": "", "type": "sha", "id": "8" }
]
},
{ "text": "Branches", "children": [] },
{ "text": "Tags", "children": [] }
],
"more": false
}

View file

@ -736,10 +736,17 @@ class VcsBackend(object):
def _next_repo_name(self): def _next_repo_name(self):
return "{}_{}".format(self.invalid_repo_name.sub("_", self._test_name), len(self._cleanup_repos)) return "{}_{}".format(self.invalid_repo_name.sub("_", self._test_name), len(self._cleanup_repos))
def add_file(self, repo, filename, content="Test content\n"): def add_file(
self,
repo,
filename,
content="Test content\n",
msg="Automatic commit from vcsbackend fixture",
author="Automatic <automatic@rhodecode.com>",
):
imc = repo.in_memory_commit imc = repo.in_memory_commit
imc.add(FileNode(safe_bytes(filename), content=safe_bytes(content))) imc.add(FileNode(safe_bytes(filename), content=safe_bytes(content)))
imc.commit(message="Automatic commit from vcsbackend fixture", author="Automatic <automatic@rhodecode.com>") imc.commit(message=msg, author=author)
def ensure_file(self, filename, content="Test content\n"): def ensure_file(self, filename, content="Test content\n"):
assert self._cleanup_repos, "Avoid writing into vcs_test repos" assert self._cleanup_repos, "Avoid writing into vcs_test repos"

View file

@ -144,10 +144,10 @@ def test_webook_parse_url_for_push_event(baseapp, repo_push_event, base_data, te
base_data["push"] = { base_data["push"] = {
"branches": [{"name": "stable"}, {"name": "dev"}], "branches": [{"name": "stable"}, {"name": "dev"}],
"commits": [ "commits": [
{"branch": "stable", "raw_id": "stable-xxx"}, {"branches": ["stable"], "raw_id": "stable-xxx"},
{"branch": "stable", "raw_id": "stable-yyy"}, {"branches": ["stable"], "raw_id": "stable-yyy"},
{"branch": "dev", "raw_id": "dev-xxx"}, {"branches": ["dev"], "raw_id": "dev-xxx"},
{"branch": "dev", "raw_id": "dev-yyy"}, {"branches": ["dev"], "raw_id": "dev-yyy"},
], ],
} }
headers = {"exmaple-header": "header-values"} headers = {"exmaple-header": "header-values"}

View file

@ -25,32 +25,28 @@ from rhodecode.lib.codeblocks import tokenize_string, split_token_stream, rollup
class TestTokenizeString(object): class TestTokenizeString(object):
python_code = """ python_code = """
import this import this
var = 6 var = 6
print("this") print("this")
"""
"""
def test_tokenize_as_python(self): def test_tokenize_as_python(self):
lexer = get_lexer_by_name("python") lexer = get_lexer_by_name("python")
tokens = list(tokenize_string(self.python_code, lexer)) tokens = list(tokenize_string(self.python_code, lexer))
expected_tokens = [ expected_tokens = [
("w", "\n"), ("w", "\n"),
("", " "),
("kn", "import"), ("kn", "import"),
("", " "), ("w", " "),
("nn", "this"), ("nn", "this"),
("w", "\n"), ("w", "\n"),
("w", "\n"), ("w", "\n"),
("", " "),
("n", "var"), ("n", "var"),
("", " "), ("", " "),
("o", "="), ("o", "="),
("", " "), ("", " "),
("mi", "6"), ("mi", "6"),
("w", "\n"), ("w", "\n"),
("", " "),
("nb", "print"), ("nb", "print"),
("p", "("), ("p", "("),
("s2", '"'), ("s2", '"'),
@ -58,8 +54,6 @@ class TestTokenizeString(object):
("s2", '"'), ("s2", '"'),
("p", ")"), ("p", ")"),
("w", "\n"), ("w", "\n"),
("w", "\n"),
("", " "),
] ]
assert tokens == expected_tokens assert tokens == expected_tokens
@ -68,7 +62,7 @@ class TestTokenizeString(object):
lexer = get_lexer_by_name("text") lexer = get_lexer_by_name("text")
tokens = list(tokenize_string(self.python_code, lexer)) tokens = list(tokenize_string(self.python_code, lexer))
assert tokens == [("", '\n import this\n\n var = 6\n print("this")\n\n ')] assert tokens == [("", '\nimport this\n\nvar = 6\nprint("this")\n')]
class TestSplitTokenStream(object): class TestSplitTokenStream(object):

View file

@ -58,7 +58,7 @@ class TestBranches(BackendTestMixin):
branch="foobar", branch="foobar",
) )
assert "foobar" in self.repo.branches assert "foobar" in self.repo.branches
assert foobar_tip.branch == "foobar" assert foobar_tip.branches == ["foobar"]
@pytest.mark.backends("git", "hg") @pytest.mark.backends("git", "hg")
def test_new_head(self): def test_new_head(self):
@ -72,11 +72,11 @@ class TestBranches(BackendTestMixin):
parents=[tip], parents=[tip],
) )
self.imc.change(FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n")) self.imc.change(FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n"))
assert foobar_tip.branch == "foobar" assert foobar_tip.branches == ["foobar"]
newtip = self.imc.commit( newtip = self.imc.commit(
message="At foobar_tip branch", message="At foobar_tip branch",
author="joe <joe@rhodecode.com>", author="joe <joe@rhodecode.com>",
branch=foobar_tip.branch, branch=foobar_tip.branches[0],
parents=[foobar_tip], parents=[foobar_tip],
) )
@ -87,7 +87,7 @@ class TestBranches(BackendTestMixin):
parents=[tip, newtip], parents=[tip, newtip],
) )
assert newest_tip.branch == self.backend_class.DEFAULT_BRANCH_NAME assert newest_tip.branches == [self.backend_class.DEFAULT_BRANCH_NAME]
@pytest.mark.backends("git", "hg") @pytest.mark.backends("git", "hg")
def test_branch_with_slash_in_name(self): def test_branch_with_slash_in_name(self):

View file

@ -45,7 +45,7 @@ class TestBaseChangeset(object):
class TestEmptyCommit(object): class TestEmptyCommit(object):
def test_branch_without_alias_returns_none(self): def test_branch_without_alias_returns_none(self):
commit = EmptyCommit() commit = EmptyCommit()
assert commit.branch is None assert commit.branches == []
@pytest.mark.usefixtures("vcs_repository_support") @pytest.mark.usefixtures("vcs_repository_support")
@ -78,7 +78,7 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin):
branch="foobar", branch="foobar",
) )
assert "foobar" in self.repo.branches 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 # 'foobar' should be the only branch that contains the new commit
branch = list(self.repo.branches.values()) branch = list(self.repo.branches.values())
assert branch[0] != branch[1] assert branch[0] != branch[1]
@ -95,11 +95,11 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin):
parents=[tip], parents=[tip],
) )
self.imc.change(FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n")) self.imc.change(FileNode(b"docs/index.txt", content=b"Documentation\nand more...\n"))
assert foobar_tip.branch == "foobar" assert foobar_tip.branches == ["foobar"]
newtip = self.imc.commit( newtip = self.imc.commit(
message="At foobar_tip branch", message="At foobar_tip branch",
author="joe <joe@rhodecode.com>", author="joe <joe@rhodecode.com>",
branch=foobar_tip.branch, branch=foobar_tip.branches[0],
parents=[foobar_tip], parents=[foobar_tip],
) )
@ -110,7 +110,7 @@ class TestCommitsInNonEmptyRepo(BackendTestMixin):
parents=[tip, newtip], parents=[tip, newtip],
) )
assert newest_tip.branch == self.backend_class.DEFAULT_BRANCH_NAME assert newest_tip.branches == [self.backend_class.DEFAULT_BRANCH_NAME]
@pytest.mark.backends("git", "hg") @pytest.mark.backends("git", "hg")
def test_get_commits_respects_branch_name(self): def test_get_commits_respects_branch_name(self):
@ -226,7 +226,8 @@ class TestCommits(BackendTestMixin):
tip = self.repo.get_commit() tip = self.repo.get_commit()
# json.dumps(tip) uses .__json__() method # json.dumps(tip) uses .__json__() method
data = tip.__json__() data = tip.__json__()
assert "branch" in data assert "branches" in data
assert isinstance(data["branches"], list)
assert data["revision"] assert data["revision"]
def test_retrieve_tip(self): def test_retrieve_tip(self):

View file

@ -555,20 +555,31 @@ TODO: To be written...
def test_merge_target_is_bookmark(self, vcsbackend_hg): def test_merge_target_is_bookmark(self, vcsbackend_hg):
target_repo = vcsbackend_hg.create_repo(number_of_commits=1) target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
source_repo = vcsbackend_hg.clone_repo(target_repo) source_repo = vcsbackend_hg.clone_repo(target_repo)
vcsbackend_hg.add_file(target_repo, "README_MERGE1", "Version 1") vcsbackend_hg.add_file(target_repo, "README_MERGE1", "Version 1")
vcsbackend_hg.add_file(source_repo, "README_MERGE2", "Version 2") vcsbackend_hg.add_file(source_repo, "README_MERGE2", "Version 2")
imc = source_repo.in_memory_commit
imc.add(FileNode(b"file_x", content=source_repo.name)) vcsbackend_hg.add_file(
imc.commit(message="Automatic commit from repo merge test", author="Automatic <automatic@rhodecode.com>") source_repo,
"file_x",
content=source_repo.name,
msg="Automatic commit from repo merge test",
author="Automatic <automatic@rhodecode.com>",
)
target_commit = target_repo.get_commit() target_commit = target_repo.get_commit()
source_commit = source_repo.get_commit() source_commit = source_repo.get_commit()
default_branch = target_repo.DEFAULT_BRANCH_NAME default_branch = target_repo.DEFAULT_BRANCH_NAME
bookmark_name = "bookmark" bookmark_name = "bookmark"
target_repo._update(default_branch) target_repo._update(default_branch)
target_repo.bookmark(bookmark_name) target_repo.bookmark(bookmark_name)
target_ref = Reference("book", bookmark_name, target_commit.raw_id) target_ref = Reference("book", bookmark_name, target_commit.raw_id)
source_ref = Reference("branch", default_branch, source_commit.raw_id) source_ref = Reference("branch", default_branch, source_commit.raw_id)
workspace_id = "test-merge" workspace_id = "test-merge"
repo_id = repo_id_generator(target_repo.path) repo_id = repo_id_generator(target_repo.path)
merge_response = target_repo.merge( merge_response = target_repo.merge(
repo_id, repo_id,
@ -601,18 +612,31 @@ TODO: To be written...
def test_merge_source_is_bookmark(self, vcsbackend_hg): def test_merge_source_is_bookmark(self, vcsbackend_hg):
target_repo = vcsbackend_hg.create_repo(number_of_commits=1) target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
source_repo = vcsbackend_hg.clone_repo(target_repo) source_repo = vcsbackend_hg.clone_repo(target_repo)
imc = source_repo.in_memory_commit
imc.add(FileNode(b"file_x", content=source_repo.name)) vcsbackend_hg.add_file(target_repo, "README_MERGE1", "Version 1")
imc.commit(message="Automatic commit from repo merge test", author="Automatic <automatic@rhodecode.com>") vcsbackend_hg.add_file(source_repo, "README_MERGE2", "Version 2")
vcsbackend_hg.add_file(
source_repo,
"file_x",
content=source_repo.name,
msg="Automatic commit from repo merge test",
author="Automatic <automatic@rhodecode.com>",
)
target_commit = target_repo.get_commit() target_commit = target_repo.get_commit()
source_commit = source_repo.get_commit() source_commit = source_repo.get_commit()
default_branch = target_repo.DEFAULT_BRANCH_NAME default_branch = target_repo.DEFAULT_BRANCH_NAME
bookmark_name = "bookmark" bookmark_name = "bookmark"
target_ref = Reference("branch", default_branch, target_commit.raw_id)
source_repo._update(default_branch) source_repo._update(default_branch)
source_repo.bookmark(bookmark_name) source_repo.bookmark(bookmark_name, source_commit.raw_id)
target_ref = Reference("branch", default_branch, target_commit.raw_id)
source_ref = Reference("book", bookmark_name, source_commit.raw_id) source_ref = Reference("book", bookmark_name, source_commit.raw_id)
workspace_id = "test-merge" workspace_id = "test-merge"
repo_id = repo_id_generator(target_repo.path) repo_id = repo_id_generator(target_repo.path)
merge_response = target_repo.merge( merge_response = target_repo.merge(
repo_id, repo_id,
@ -631,8 +655,8 @@ TODO: To be written...
target_repo = backends.get_backend(vcsbackend_hg.alias)(target_repo.path) target_repo = backends.get_backend(vcsbackend_hg.alias)(target_repo.path)
target_commits = list(target_repo.get_commits()) target_commits = list(target_repo.get_commits())
commit_ids = [c.raw_id for c in target_commits] commit_ids = [c.raw_id for c in target_commits]
assert source_ref.commit_id == commit_ids[-1] assert source_ref.commit_id == commit_ids[-2]
assert target_ref.commit_id == commit_ids[-2] assert target_ref.commit_id == commit_ids[-4]
def test_merge_target_has_multiple_heads(self, vcsbackend_hg): def test_merge_target_has_multiple_heads(self, vcsbackend_hg):
target_repo = vcsbackend_hg.create_repo(number_of_commits=2) target_repo = vcsbackend_hg.create_repo(number_of_commits=2)
@ -819,15 +843,15 @@ class TestMercurialCommit:
def test_branch_and_tags(self): def test_branch_and_tags(self):
commit0 = self.repo.get_commit(commit_idx=0) commit0 = self.repo.get_commit(commit_idx=0)
assert commit0.branch == "default" assert commit0.branches == ["default"]
assert commit0.tags == [] assert commit0.tags == []
commit10 = self.repo.get_commit(commit_idx=10) commit10 = self.repo.get_commit(commit_idx=10)
assert commit10.branch == "default" assert commit10.branches == ["default"]
assert commit10.tags == [] assert commit10.tags == []
commit44 = self.repo.get_commit(commit_idx=44) commit44 = self.repo.get_commit(commit_idx=44)
assert commit44.branch == "web" assert commit44.branches == ["web"]
tip = self.repo.get_commit("tip") tip = self.repo.get_commit("tip")
assert "tip" in tip.tags assert "tip" in tip.tags
@ -1156,18 +1180,18 @@ class TestMercurialCommit:
def test_commit_message_is_unicode(self): def test_commit_message_is_unicode(self):
for cm in self.repo: for cm in self.repo:
assert type(cm.message) == str assert type(cm.message) is str
def test_commit_author_is_unicode(self): def test_commit_author_is_unicode(self):
for cm in self.repo: for cm in self.repo:
assert type(cm.author) == str assert type(cm.author) is str
def test_repo_files_content_type(self): def test_repo_files_content_type(self):
test_commit = self.repo.get_commit(commit_idx=100) test_commit = self.repo.get_commit(commit_idx=100)
for node in test_commit.get_node(b"/"): for node in test_commit.get_node(b"/"):
if node.is_file(): if node.is_file():
assert type(node.content) == bytes assert type(node.content) is bytes
assert type(node.str_content) == str assert type(node.str_content) is str
def test_wrong_path(self): def test_wrong_path(self):
# There is 'setup.py' in the root dir but not there: # There is 'setup.py' in the root dir but not there: