diff --git a/rhodecode/api/tests/test_get_repo_changeset.py b/rhodecode/api/tests/test_get_repo_changeset.py index 464c538d..c8d1d9c0 100644 --- a/rhodecode/api/tests/test_get_repo_changeset.py +++ b/rhodecode/api/tests/test_get_repo_changeset.py @@ -41,8 +41,7 @@ class TestGetRepoChangeset(object): if details == "full": assert result["refs"]["bookmarks"] == getattr(commit, "bookmarks", []) - branches = [commit.branch] if commit.branch else [] - assert result["refs"]["branches"] == branches + assert result["refs"]["branches"] == commit.branches assert result["refs"]["tags"] == commit.tags @pytest.mark.parametrize("details", ["basic", "extended", "full"]) diff --git a/rhodecode/api/views/repo_api.py b/rhodecode/api/views/repo_api.py index b4398312..55ef2e90 100644 --- a/rhodecode/api/views/repo_api.py +++ b/rhodecode/api/views/repo_api.py @@ -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))) vcs_repo = repo.scm_instance() - pre_load = ["author", "branch", "date", "message", "parents", "status", "_commit"] + pre_load = ["author", "branches", "date", "message", "parents", "status", "_commit"] try: 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))) 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() # 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) if changes_details == "full": _cs_json["refs"] = { - "branches": [commit.branch], + "branches": commit.branches, "bookmarks": getattr(commit, "bookmarks", []), "tags": commit.tags, } diff --git a/rhodecode/apps/_base/__init__.py b/rhodecode/apps/_base/__init__.py index 938d9b9e..ffdf7a61 100644 --- a/rhodecode/apps/_base/__init__.py +++ b/rhodecode/apps/_base/__init__.py @@ -444,7 +444,7 @@ class RepoAppView(BaseAppView): def get_commit_preload_attrs(cls): pre_load = [ "author", - "branch", + "branches", "date", "message", "parents", diff --git a/rhodecode/apps/admin/tests/test_admin_settings.py b/rhodecode/apps/admin/tests/test_admin_settings.py index e5dbf606..dd56a341 100644 --- a/rhodecode/apps/admin/tests/test_admin_settings.py +++ b/rhodecode/apps/admin/tests/test_admin_settings.py @@ -24,11 +24,10 @@ from rhodecode.apps._base import ADMIN_PREFIX from rhodecode.lib.hash_utils import md5_safe from rhodecode.model.db import RhodeCodeUi 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.routes import route_path - 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 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("vcs_svn_tag", "/test") + settings_util.create_rhodecode_ui(VcsSettingsModel.SVN_BRANCH_SECTION, "/test") + settings_util.create_rhodecode_ui(VcsSettingsModel.SVN_TAG_SECTION, "/test") response = self.app.post( route_path("admin_settings_vcs_update"), @@ -227,8 +226,8 @@ class TestAdminSettingsVcs(object): @pytest.mark.parametrize( "section", [ - "vcs_svn_branch", - "vcs_svn_tag", + VcsSettingsModel.SVN_BRANCH_SECTION, + VcsSettingsModel.SVN_TAG_SECTION, ], ) def test_delete_svn_patterns(self, section, csrf_token, settings_util): @@ -243,8 +242,8 @@ class TestAdminSettingsVcs(object): @pytest.mark.parametrize( "section", [ - "vcs_svn_branch", - "vcs_svn_tag", + VcsSettingsModel.SVN_BRANCH_SECTION, + VcsSettingsModel.SVN_TAG_SECTION, ], ) def test_delete_svn_patterns_raises_404_when_no_xhr(self, section, csrf_token, settings_util): diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py index 28c02bad..0645cc44 100644 --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -66,6 +66,8 @@ class AdminSettingsView(BaseAppView): c = self._get_local_tmpl_context() c.labs_active = str2bool(rhodecode.CONFIG.get("labs_settings_active", "true")) c.navlist = navigation_list(self.request) + c.svn_default_branches = RhodeCodeUi.SVN_BRANCHES_PATTERNS + c.svn_default_tags = RhodeCodeUi.SVN_TAGS_PATTERNS return c @classmethod @@ -127,6 +129,7 @@ class AdminSettingsView(BaseAppView): c.svn_tag_patterns = model.get_global_svn_tag_patterns() 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) + defaults = self._form_defaults() data = render("rhodecode:templates/admin/settings/settings.mako", self._get_template_context(c), self.request) diff --git a/rhodecode/apps/hovercards/views.py b/rhodecode/apps/hovercards/views.py index f433beab..af1108e3 100644 --- a/rhodecode/apps/hovercards/views.py +++ b/rhodecode/apps/hovercards/views.py @@ -107,7 +107,7 @@ class HoverCardsRepoView(RepoAppView): def hovercard_repo_commit(self): c = self.load_default_context() commit_id = self.request.matchdict["commit_id"] - pre_load = ["author", "branch", "date", "message"] + pre_load = ["author", "branches", "date", "message"] try: c.commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id, pre_load=pre_load) except (CommitDoesNotExistError, EmptyRepositoryError): diff --git a/rhodecode/apps/repository/tests/test_repo_files.py b/rhodecode/apps/repository/tests/test_repo_files.py index 9208a182..dd45d667 100644 --- a/rhodecode/apps/repository/tests/test_repo_files.py +++ b/rhodecode/apps/repository/tests/test_repo_files.py @@ -29,7 +29,7 @@ from rhodecode.lib.ext_json import json from rhodecode.lib.str_utils import safe_str from rhodecode.lib.vcs import nodes 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.fixtures.rc_fixture import Fixture @@ -181,8 +181,9 @@ class TestFilesViews(object): response.mustcontain(msgbox % (commit.message,)) assert_response = response.assert_response() - if commit.branch: - assert_response.element_contains(".tags.tags-main .branchtag", commit.branch) + if commit.branches: + for branch in commit.branches: + assert_response.element_contains(".tags.tags-main .branchtag", branch) if commit.tags: for tag in commit.tags: assert_response.element_contains(".tags.tags-main .tagtag", tag) @@ -230,15 +231,32 @@ class TestFilesViews(object): for author in expected_authors[backend.alias]: 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( route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"), extra_environ=xhr_header, ) 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"] + + # 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( route_path( "repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py" diff --git a/rhodecode/apps/repository/tests/test_repo_summary.py b/rhodecode/apps/repository/tests/test_repo_summary.py index fc5bc457..9db750de 100644 --- a/rhodecode/apps/repository/tests/test_repo_summary.py +++ b/rhodecode/apps/repository/tests/test_repo_summary.py @@ -124,7 +124,7 @@ class TestSummaryView(object): "sh": {"count": 2, "desc": ["Bash"]}, "bat": {"count": 1, "desc": ["Batch"]}, "cfg": {"count": 1, "desc": ["Ini"]}, - "html": {"count": 1, "desc": ["EvoqueHtml", "Html"]}, + "html": {"count": 1, "desc": ["Html"]}, "ini": {"count": 1, "desc": ["Ini"]}, "js": {"count": 1, "desc": ["Javascript"]}, "makefile": {"count": 1, "desc": ["Makefile", "Makefile"]}, @@ -136,7 +136,7 @@ class TestSummaryView(object): "sh": {"count": 2, "desc": ["Bash"]}, "bat": {"count": 1, "desc": ["Batch"]}, "cfg": {"count": 1, "desc": ["Ini"]}, - "html": {"count": 1, "desc": ["EvoqueHtml", "Html"]}, + "html": {"count": 1, "desc": ["Html"]}, "ini": {"count": 1, "desc": ["Ini"]}, "js": {"count": 1, "desc": ["Javascript"]}, "makefile": {"count": 1, "desc": ["Makefile", "Makefile"]}, @@ -144,7 +144,7 @@ class TestSummaryView(object): "svn": { "py": {"count": 75, "desc": ["Python"]}, "rst": {"count": 16, "desc": ["Rst"]}, - "html": {"count": 11, "desc": ["EvoqueHtml", "Html"]}, + "html": {"count": 11, "desc": ["Html"]}, "css": {"count": 2, "desc": ["Css"]}, "bat": {"count": 1, "desc": ["Batch"]}, "cfg": {"count": 1, "desc": ["Ini"]}, diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py index d6b5e087..ce8e2fc1 100644 --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -88,7 +88,7 @@ class RepoCommitsView(RepoAppView): commit_range = commit_id_range.split("...")[:2] try: - pre_load = ["author", "branch", "date", "message", "parents"] + pre_load = ["author", "branches", "date", "message", "parents"] if self.rhodecode_vcs_repo.alias == "hg": pre_load += ["hidden", "obsolete", "phase"] diff --git a/rhodecode/apps/repository/views/repo_compare.py b/rhodecode/apps/repository/views/repo_compare.py index 11945816..db43d81c 100644 --- a/rhodecode/apps/repository/views/repo_compare.py +++ b/rhodecode/apps/repository/views/repo_compare.py @@ -192,7 +192,7 @@ class RepoCompareView(RepoAppView): c.source_ref_type = source_ref_type c.target_ref_type = target_ref_type - pre_load = ["author", "date", "message", "branch"] + pre_load = ["author", "date", "message", "branches"] c.ancestor = None try: diff --git a/rhodecode/apps/repository/views/repo_feed.py b/rhodecode/apps/repository/views/repo_feed.py index 4e0eee55..ed66de25 100644 --- a/rhodecode/apps/repository/views/repo_feed.py +++ b/rhodecode/apps/repository/views/repo_feed.py @@ -99,7 +99,7 @@ class RepoFeedView(RepoAppView): return date 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(): return [] diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py index 76178e35..18d3dac0 100644 --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -289,7 +289,7 @@ class RepoFilesView(RepoAppView): else: commit = repo.get_commit(commit_id=commit_id) if commit: - branch_name = commit.branch + branch_name = commit.last_branch sha_commit_id = commit.raw_id return branch_name, sha_commit_id, is_head @@ -715,9 +715,9 @@ class RepoFilesView(RepoAppView): ) 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_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 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 tip = self.rhodecode_vcs_repo.get_commit() if commits is None: - pre_load = ["author", "branch"] + pre_load = ["author", "branches"] try: commits = tip.get_path_history(safe_bytes(f_path), pre_load=pre_load) except (NodeDoesNotExistError, CommitError): @@ -983,7 +983,7 @@ class RepoFilesView(RepoAppView): history = [] commits_group = ([], _("Changesets")) 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}" commits_group[0].append((commit.raw_id, n_desc, "sha")) 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) if self.rhodecode_vcs_repo.is_empty(): - # for empty repository we cannot check for current branch, we rely on - # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True + # for empty repository we cannot check for current branch, we rely on default landing ref + _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True else: _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 @@ -1338,9 +1337,8 @@ class RepoFilesView(RepoAppView): default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") if self.rhodecode_vcs_repo.is_empty(): - # for empty repository we cannot check for current branch, we rely on - # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True + # for empty repository we cannot check for current branch, we rely on default landing ref + _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True else: _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 @@ -1424,9 +1422,8 @@ class RepoFilesView(RepoAppView): default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") if self.rhodecode_vcs_repo.is_empty(): - # for empty repository we cannot check for current branch, we rely on - # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True + # for empty repository we cannot check for current branch, we rely on default landing ref + _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True else: _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 @@ -1529,9 +1526,8 @@ class RepoFilesView(RepoAppView): default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip") if self.rhodecode_vcs_repo.is_empty(): - # for empty repository we cannot check for current branch, we rely on - # c.commit.branch instead - _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True + # for empty repository we cannot check for current branch, we rely on default landing ref + _branch_name, _sha_commit_id, is_head = self.db_repo.landing_ref_name, "", True else: _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 diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py index c105ca39..e6374acf 100644 --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -858,7 +858,7 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView): missing_requirements = False try: - pre_load = ["author", "date", "message", "branch", "parents"] + pre_load = ["author", "date", "message", "branches", "parents"] pull_request_commits = pull_request_at_ver.revisions log.debug("Loading %s commits from %s", len(pull_request_commits), commits_source_repo) diff --git a/rhodecode/apps/repository/views/repo_settings_vcs.py b/rhodecode/apps/repository/views/repo_settings_vcs.py index a2c2f289..25cd59bd 100644 --- a/rhodecode/apps/repository/views/repo_settings_vcs.py +++ b/rhodecode/apps/repository/views/repo_settings_vcs.py @@ -29,6 +29,7 @@ from rhodecode.apps._base import RepoAppView from rhodecode.apps.svn_support import config_keys from rhodecode.lib import helpers as h from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired +from rhodecode.model.db import RhodeCodeUi from rhodecode.model.forms import RepoVcsSettingsForm from rhodecode.model.meta import Session from rhodecode.model.settings import VcsSettingsModel, SettingNotFound @@ -39,6 +40,8 @@ log = logging.getLogger(__name__) class RepoSettingsVcsView(RepoAppView): def load_default_context(self): c = self._get_local_tmpl_context() + c.svn_default_branches = RhodeCodeUi.SVN_BRANCHES_PATTERNS + c.svn_default_tags = RhodeCodeUi.SVN_TAGS_PATTERNS return c def _vcs_form_defaults(self, repo_name): diff --git a/rhodecode/apps/ssh_support/tests/test_server_svn.py b/rhodecode/apps/ssh_support/tests/test_server_svn.py index 10b1d0dd..37e46e60 100644 --- a/rhodecode/apps/ssh_support/tests/test_server_svn.py +++ b/rhodecode/apps/ssh_support/tests/test_server_svn.py @@ -260,11 +260,11 @@ class TestSubversionServer(object): "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, ), ( - 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, ), ], diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py index c0310439..10277140 100644 --- a/rhodecode/events/repo.py +++ b/rhodecode/events/repo.py @@ -67,7 +67,7 @@ def _commits_as_dict(event, commit_ids, repos): cs_data = { "raw_id": commit_id, "short_id": commit_id, - "branch": None, + "branches": [], "git_ref_change": "tag_add", "message": f"Added new tag {raw_id}", "author": event.actor.full_contact, @@ -81,7 +81,7 @@ def _commits_as_dict(event, commit_ids, repos): cs_data = { "raw_id": commit_id, "short_id": commit_id, - "branch": None, + "branches": [], "git_ref_change": "branch_delete", "message": f"Deleted branch {raw_id}", "author": event.actor.full_contact, @@ -95,7 +95,6 @@ def _commits_as_dict(event, commit_ids, repos): cs = vcs_repo.get_commit(commit_id) except CommitDoesNotExistError: continue # maybe its in next repo - cs_data = cs.__json__() cs_data["refs"] = cs._get_refs() @@ -380,17 +379,19 @@ class RepoPushEvent(RepoVCSEvent): data = super().as_dict() 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): - 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]) - last_branch = None + # fill branches for every single commit + last_branch = [] for commit in reversed(commits): - commit["branch"] = commit["branch"] or last_branch - last_branch = commit["branch"] + commit["branches"] = commit["branches"] or last_branch + last_branch = commit["branches"] + issues = _issues_as_dict(commits) branches = set() @@ -399,8 +400,9 @@ class RepoPushEvent(RepoVCSEvent): if commit["refs"]["tags"]: for tag in commit["refs"]["tags"]: tags.add(tag) - if commit["branch"]: - branches.add(commit["branch"]) + if commit["branches"]: + for branch in commit["branches"]: + branches.add(branch) # maybe we have branches in new_refs ? try: diff --git a/rhodecode/integrations/types/base.py b/rhodecode/integrations/types/base.py index b12e9fb8..f16bcc91 100644 --- a/rhodecode/integrations/types/base.py +++ b/rhodecode/integrations/types/base.py @@ -230,16 +230,18 @@ class CommitParsingDataHandler(object): # special case for GIT that allows creating tags, # deleting branches without associated commit continue - commit_branch = commit["branch"] - if commit_branch not in branches_commits: - _branch = branch_data[commit_branch] if commit_branch else commit_branch - branch_commits = {"branch": _branch, "branch_head": "", "commits": []} - branches_commits[commit_branch] = branch_commits + maybe_commit_branches = commit["branches"] - branch_commits = branches_commits[commit_branch] - branch_commits["commits"].append(commit) - branch_commits["branch_head"] = commit["raw_id"] + for commit_branch in maybe_commit_branches: + if commit_branch not in branches_commits: + _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 diff --git a/rhodecode/lib/celerylib/loader.py b/rhodecode/lib/celerylib/loader.py index e26f18aa..a1289581 100644 --- a/rhodecode/lib/celerylib/loader.py +++ b/rhodecode/lib/celerylib/loader.py @@ -106,6 +106,7 @@ def get_logger(obj): # init main celery app celery_app = Celery() + celery_app.user_options["preload"].add(preload_option_ini) celery_app.user_options["preload"].add(preload_option_ini_var) @@ -215,6 +216,7 @@ def task_success_signal(result, **kwargs): @signals.task_retry.connect def task_retry_signal(request, reason, einfo, **kwargs): + log.warning("Task: %s failed !! reason: %s", request, reason) meta.Session.remove() closer = celery_app.conf["PYRAMID_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") statsd = StatsdClient.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)]) closer = celery_app.conf["PYRAMID_CLOSER"] diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py index 3e1abef1..1bfa2b41 100644 --- a/rhodecode/lib/celerylib/tasks.py +++ b/rhodecode/lib/celerylib/tasks.py @@ -750,7 +750,7 @@ def _get_diffset( source_ref_id = pull_request.source_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) source_commit_final = source_repo.get_commit(commit_id=safe_str(source_ref_id), pre_load=pre_load) diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py index 60c36519..566d687b 100644 --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -391,13 +391,10 @@ class DbManage(object): self.sa.add(hggit) # set svn branch defaults - branches = ["/branches/*", "/trunk"] - tags = ["/tags/*"] - - for branch in branches: + for branch in RhodeCodeUi.SVN_BRANCHES_PATTERNS: 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) def create_auth_plugin_options(self, skip_existing=False): diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py index 30a7cbe4..288145d6 100644 --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -694,6 +694,7 @@ def get_lexer_safe(mimetype=None, filepath=None): defaulting to plain text if none could be found """ lexer = None + try: if mimetype: lexer = get_lexer_for_mimetype(mimetype) diff --git a/rhodecode/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py index 0072280a..fe87cfaa 100644 --- a/rhodecode/lib/vcs/backends/base.py +++ b/rhodecode/lib/vcs/backends/base.py @@ -76,11 +76,13 @@ class Reference: def branch(self): if self.type == "branch": return self.name + return None @property def bookmark(self): if self.type == "book": return self.name + return None @property def to_str(self): @@ -334,7 +336,8 @@ class BaseCommit: """ 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 @@ -375,7 +378,7 @@ class BaseCommit: "date": self.date, "author": self.author, "parents": parents, - "branch": self.branch, + "branches": self.branches, } def __getstate__(self): @@ -392,9 +395,9 @@ class BaseCommit: def _get_refs(self): 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", []), - "tags": self.tags, } @LazyProperty @@ -698,12 +701,12 @@ class BaseCommit: return self._find_next(indexes, branch) 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") for next_idx in indexes: commit = self.repository.get_commit(commit_idx=next_idx) - if branch and branch != commit.branch: + if branch and branch not in commit.branches: continue return commit raise CommitDoesNotExistError @@ -725,7 +728,7 @@ class BaseCommit: 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 (top_node, dirnodes, filenodes). """ @@ -796,6 +799,10 @@ class BaseCommit: warnings.warn("Use get_path_commit instead", DeprecationWarning) 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): """ @@ -1057,7 +1064,7 @@ class BaseRepository(object): """ 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): return self._get_range(key, pre_load) return self.get_commit(commit_idx=key, pre_load=pre_load) @@ -1672,7 +1679,15 @@ class EmptyCommit(BaseCommit): """ 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 # 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.repository = repo self.alias = alias - self._branch = branch + if branch and not isinstance(branch, list): + branch = [branch] + self._branches = branch or [] @LazyProperty def raw_id(self): @@ -1696,14 +1713,23 @@ class EmptyCommit(BaseCommit): @LazyProperty def branch(self): + raise ValueError("Deprecated usage of .branch property") + return self._branches[self.idx] # If branch was explicitly set, return it - if self._branch: - return self._branch - - if self.alias: + branches = self.branches + if branches: + return branches[:1] + else: 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 def short_id(self): diff --git a/rhodecode/lib/vcs/backends/git/commit.py b/rhodecode/lib/vcs/backends/git/commit.py index 9ace9b3f..80a00afd 100644 --- a/rhodecode/lib/vcs/backends/git/commit.py +++ b/rhodecode/lib/vcs/backends/git/commit.py @@ -96,8 +96,8 @@ class GitCommit(base.BaseCommit): value = utcdate_fromtimestamp(*value) elif attr == "parents": value = self._make_commits(value) - elif attr == "branch": - value = self._set_branch(value) + elif attr == "branches": + value = value self.__dict__[attr] = value @LazyProperty @@ -154,15 +154,10 @@ class GitCommit(base.BaseCommit): branches.append(name) return branches - def _set_branch(self, branches): - if branches: - # actually commit can have multiple branches in git - return safe_str(branches[0]) - @LazyProperty - def branch(self): - branches = self._remote.branch(self.raw_id) - return self._set_branch(branches) + def branches(self) -> list[str]: + branches = self._remote.branches(self.raw_id) + return branches def _get_path_tree_id_and_type(self, path: bytes): if path in self._path_type_cache: diff --git a/rhodecode/lib/vcs/backends/hg/commit.py b/rhodecode/lib/vcs/backends/hg/commit.py index af908f96..0a285af9 100644 --- a/rhodecode/lib/vcs/backends/hg/commit.py +++ b/rhodecode/lib/vcs/backends/hg/commit.py @@ -78,8 +78,10 @@ class MercurialCommit(base.BaseCommit): result = self._remote.bulk_request(self.raw_id, pre_load) for attr, value in result.items(): - if attr in ["author", "branch", "message"]: + if attr in ["author", "message"]: value = safe_str(value) + elif attr in ["branches"]: + value = list(value) elif attr == "affected_files": value = list(map(safe_str, value)) elif attr == "date": @@ -96,8 +98,8 @@ class MercurialCommit(base.BaseCommit): return tags @LazyProperty - def branch(self): - return safe_str(self._remote.ctx_branch(self.raw_id)) + def branches(self) -> list[str]: + return self._remote.ctx_branch(self.raw_id) @LazyProperty def bookmarks(self): diff --git a/rhodecode/lib/vcs/backends/svn/commit.py b/rhodecode/lib/vcs/backends/svn/commit.py index dfbfe1fd..81f81656 100644 --- a/rhodecode/lib/vcs/backends/svn/commit.py +++ b/rhodecode/lib/vcs/backends/svn/commit.py @@ -78,6 +78,10 @@ class SubversionCommit(base.BaseCommit): def _properties(self): return self._remote.revision_properties(self._svn_rev) + @LazyProperty + def branches(self) -> list[str]: + return self._remote.revision_branches(self._svn_rev) + @LazyProperty def parents(self): parent_idx = self.idx - 1 diff --git a/rhodecode/lib/vcs/backends/svn/repository.py b/rhodecode/lib/vcs/backends/svn/repository.py index 101cb8da..86129f60 100644 --- a/rhodecode/lib/vcs/backends/svn/repository.py +++ b/rhodecode/lib/vcs/backends/svn/repository.py @@ -46,7 +46,6 @@ from rhodecode.lib.vcs.exceptions import ( NodeDoesNotExistError, ) - log = logging.getLogger(__name__) @@ -127,7 +126,9 @@ class SubversionRepository(base.BaseRepository): @LazyProperty 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 def branches_closed(self): @@ -147,7 +148,9 @@ class SubversionRepository(base.BaseRepository): @LazyProperty 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): found_items = {} diff --git a/rhodecode/lib/vcs/client_http.py b/rhodecode/lib/vcs/client_http.py index 4dec389d..4e91cfb1 100644 --- a/rhodecode/lib/vcs/client_http.py +++ b/rhodecode/lib/vcs/client_http.py @@ -54,6 +54,8 @@ EXCEPTIONS_MAP = { def _remote_call(url, payload, exceptions_map, session, retries=3): + response = None + for attempt in range(retries): try: response = session.post(url, data=msgpack.packb(payload)) @@ -73,6 +75,11 @@ def _remote_call(url, payload, exceptions_map, session, retries=3): else: 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: content_type = response.content_type log.error("Call to %s returned non 200 HTTP code: %s [%s]", url, response.status_code, content_type) diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py index 375b6efe..32627a72 100644 --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -501,7 +501,10 @@ class RhodeCodeUi(Base, BaseModel): # SVN PATTERNS SVN_BRANCH_ID = "vcs_svn_branch" + SVN_BRANCHES_PATTERNS = ["/branches/*", "/trunk"] + 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_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() 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() else: cs_cache = EmptyCommit() diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py index 2dc35b1c..afcb55ba 100644 --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -52,6 +52,7 @@ from rhodecode.lib.pyramid_utils import get_current_request from rhodecode import BACKENDS from rhodecode.lib import helpers from rhodecode.model import validators as v +from rhodecode.model.settings import VcsSettingsModel log = logging.getLogger(__name__) @@ -477,8 +478,8 @@ def ApplicationUiSettingsForm(localizer): class _ApplicationUiSettingsForm(_BaseVcsSettingsForm): extensions_hggit = v.StringBoolean(if_missing=False) - new_svn_branch = v.ValidSvnPattern(localizer, section="vcs_svn_branch") - new_svn_tag = v.ValidSvnPattern(localizer, section="vcs_svn_tag") + new_svn_branch = v.ValidSvnPattern(localizer, section=VcsSettingsModel.SVN_BRANCH_SECTION) + new_svn_tag = v.ValidSvnPattern(localizer, section=VcsSettingsModel.SVN_TAG_SECTION) return _ApplicationUiSettingsForm @@ -488,8 +489,8 @@ def RepoVcsSettingsForm(localizer, repo_name): class _RepoVcsSettingsForm(_BaseVcsSettingsForm): inherit_global_settings = v.StringBoolean(if_missing=False) - new_svn_branch = v.ValidSvnPattern(localizer, section="vcs_svn_branch", repo_name=repo_name) - new_svn_tag = v.ValidSvnPattern(localizer, section="vcs_svn_tag", 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=VcsSettingsModel.SVN_TAG_SECTION, repo_name=repo_name) return _RepoVcsSettingsForm diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py index 7066c75f..7d96d240 100644 --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -1221,7 +1221,7 @@ class PullRequestModel(BaseModel): # re-compute commit ids old_commit_ids = pull_request.revisions - pre_load = ["author", "date", "message", "branch"] + pre_load = ["author", "date", "message", "branches"] commit_ranges = target_repo.compare( 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 # in case of bookmark 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) if branch_perm and branch_perm == "branch.none": diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py index 04a55fca..9a5588fd 100644 --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -489,7 +489,7 @@ class ScmModel(BaseModel): imc.change(FileNode(f_path, content, mode=commit.get_file_mode(f_path))) try: # 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: log.error(traceback.format_exc()) raise IMCCommitError(str(e)) @@ -831,7 +831,7 @@ class ScmModel(BaseModel): imc.add(FileNode(path, content=content)) # 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) if trigger_push_hook: @@ -874,7 +874,7 @@ class ScmModel(BaseModel): try: # 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: raise except Exception as e: @@ -910,7 +910,7 @@ class ScmModel(BaseModel): imc.change(file_node) 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: raise except Exception as e: @@ -963,7 +963,7 @@ class ScmModel(BaseModel): imc.remove(FileNode(path, content=content)) # 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) if trigger_push_hook: diff --git a/rhodecode/model/settings.py b/rhodecode/model/settings.py index 51d57f29..43aad79b 100644 --- a/rhodecode/model/settings.py +++ b/rhodecode/model/settings.py @@ -337,7 +337,7 @@ def assert_repo_settings(func): return _wrapper -class IssueTrackerSettingsModel(object): +class IssueTrackerSettingsModel: INHERIT_SETTINGS = "inherit_issue_tracker_settings" SETTINGS_PREFIX = "issuetracker_" @@ -450,7 +450,7 @@ class IssueTrackerSettingsModel(object): return setting -class VcsSettingsModel(object): +class VcsSettingsModel: INHERIT_SETTINGS = "inherit_vcs_settings" GENERAL_SETTINGS = ( "use_outdated_comments", diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js index 364eeeec..519bfcf1 100644 --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -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_update', '/_admin/security/audit/update', []); 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_update', '/_admin/settings/email/update', []); 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_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_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_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']); pyroutes.register('register', '/_admin/register', []); diff --git a/rhodecode/templates/base/vcs_settings.mako b/rhodecode/templates/base/vcs_settings.mako index 8ad99b35..58d58645 100644 --- a/rhodecode/templates/base/vcs_settings.mako +++ b/rhodecode/templates/base/vcs_settings.mako @@ -150,7 +150,12 @@
${h.show_id(c.commit)}
- % if c.commit.branch:
+ ${_('Add new file')}
+ % for branch in c.commit.branches:
- ${c.commit.branch}
+ ${branch}
- % endif
+ % endfor
${h.show_id(c.commit)}
- % if c.commit.branch:
+ ${_('Deleting file')}
+ % for branch in c.commit.branches:
- ${c.commit.branch}
+ ${branch}
- % endif
+ % endfor
${h.show_id(c.commit)}
- % if c.commit.branch:
+ ${_('Editing file')}
+ % for branch in c.commit.branches:
- ${c.commit.branch}
+ ${branch}
- % endif
+ % endfor
${c.branch_name}
+ ${_('Edit')}
-
${_('Delete')}
diff --git a/rhodecode/templates/files/files_upload.mako b/rhodecode/templates/files/files_upload.mako
index 4c9b797a..8937cad1 100644
--- a/rhodecode/templates/files/files_upload.mako
+++ b/rhodecode/templates/files/files_upload.mako
@@ -23,15 +23,15 @@
## Template for uploads
${h.show_id(c.commit)}
+ ${_('Replace content of')} ${c.f_path}
% else:
- ${_('Upload new file')} @ ${h.show_id(c.commit)}
+ ${_('Upload new file')}
% endif
- % if c.commit.branch:
+ % for branch in c.commit.branches:
- ${c.commit.branch}
+ ${branch}
- % endif
+ % endfor