Setting for default branch and creating new repo+commits on empty repo
This commit is contained in:
parent
7d2a9d1d48
commit
c67bb94291
9 changed files with 195 additions and 16 deletions
|
|
@ -1293,7 +1293,7 @@ class RepoFilesView(RepoAppView):
|
|||
|
||||
c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
|
||||
if c.commit is None:
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
|
||||
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
|
||||
|
|
@ -1329,7 +1329,7 @@ class RepoFilesView(RepoAppView):
|
|||
|
||||
c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
|
||||
if c.commit is None:
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias, branch=self.db_repo.landing_ref_name)
|
||||
|
||||
# calculate redirect URL
|
||||
if self.rhodecode_vcs_repo.is_empty():
|
||||
|
|
@ -1415,7 +1415,7 @@ class RepoFilesView(RepoAppView):
|
|||
|
||||
c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
|
||||
if c.commit is None:
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias, branch=self.db_repo.landing_ref_name)
|
||||
|
||||
# calculate redirect URL
|
||||
if self.rhodecode_vcs_repo.is_empty():
|
||||
|
|
@ -1517,7 +1517,7 @@ class RepoFilesView(RepoAppView):
|
|||
|
||||
c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
|
||||
if c.commit is None:
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
|
||||
c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias, branch=self.db_repo.landing_ref_name)
|
||||
|
||||
if self.rhodecode_vcs_repo.is_empty():
|
||||
default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
|
||||
|
|
|
|||
|
|
@ -187,7 +187,16 @@ def create_repo(form_data, cur_user):
|
|||
enable_downloads = form_data.get("enable_downloads", defs.get("repo_enable_downloads"))
|
||||
|
||||
# set landing rev based on default branches for SCM
|
||||
landing_ref, _label = ScmModel.backend_landing_ref(repo_type)
|
||||
if repo_type == "git":
|
||||
# Get default branch from global Git settings
|
||||
from rhodecode.model.settings import VcsSettingsModel
|
||||
|
||||
settings_model = VcsSettingsModel()
|
||||
global_settings = settings_model.get_global_settings()
|
||||
default_branch = global_settings.get("vcs_git_default_branch", "master")
|
||||
landing_ref = f"branch:{default_branch}"
|
||||
else:
|
||||
landing_ref, _label = ScmModel.backend_landing_ref(repo_type)
|
||||
|
||||
try:
|
||||
RepoModel()._create_repo(
|
||||
|
|
|
|||
53
rhodecode/lib/dbmigrate/versions/120_version_5_8_1.py
Normal file
53
rhodecode/lib/dbmigrate/versions/120_version_5_8_1.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from rhodecode.lib.dbmigrate.versions import _reset_base
|
||||
from rhodecode.model import meta, init_model_encryption
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
"""
|
||||
Add Git default branch configuration
|
||||
"""
|
||||
_reset_base(migrate_engine)
|
||||
|
||||
from rhodecode.lib.dbmigrate.schema import db_4_20_0_1 as db
|
||||
|
||||
init_model_encryption(db)
|
||||
|
||||
from rhodecode.model.db import RhodeCodeUi
|
||||
|
||||
session = meta.Session()
|
||||
|
||||
# Check if setting already exists
|
||||
existing = (
|
||||
session.query(RhodeCodeUi)
|
||||
.filter(RhodeCodeUi.ui_section == "vcs_git", RhodeCodeUi.ui_key == "default_branch")
|
||||
.first()
|
||||
)
|
||||
|
||||
if not existing:
|
||||
# Add default branch setting with 'master' as default
|
||||
setting = RhodeCodeUi()
|
||||
setting.ui_section = "vcs_git"
|
||||
setting.ui_key = "default_branch"
|
||||
setting.ui_value = "master"
|
||||
setting.ui_active = True
|
||||
session.add(setting)
|
||||
session.commit()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
"""
|
||||
Remove Git default branch configuration
|
||||
"""
|
||||
_reset_base(migrate_engine)
|
||||
|
||||
from rhodecode.lib.dbmigrate.schema import db_4_20_0_1 as db
|
||||
|
||||
init_model_encryption(db)
|
||||
|
||||
from rhodecode.model.db import RhodeCodeUi
|
||||
|
||||
session = meta.Session()
|
||||
session.query(RhodeCodeUi).filter(
|
||||
RhodeCodeUi.ui_section == "vcs_git", RhodeCodeUi.ui_key == "default_branch"
|
||||
).delete()
|
||||
session.commit()
|
||||
|
|
@ -1671,7 +1671,9 @@ class EmptyCommit(BaseCommit):
|
|||
an EmptyCommit
|
||||
"""
|
||||
|
||||
def __init__(self, commit_id=EMPTY_COMMIT_ID, repo=None, alias=None, idx=-1, message="", author="", date=None):
|
||||
def __init__(
|
||||
self, commit_id=EMPTY_COMMIT_ID, repo=None, alias=None, idx=-1, message="", author="", date=None, branch=None
|
||||
):
|
||||
self._empty_commit_id = commit_id
|
||||
# TODO: johbo: Solve idx parameter, default value does not make
|
||||
# too much sense
|
||||
|
|
@ -1681,6 +1683,7 @@ class EmptyCommit(BaseCommit):
|
|||
self.date = date or datetime.datetime.fromtimestamp(0)
|
||||
self.repository = repo
|
||||
self.alias = alias
|
||||
self._branch = branch
|
||||
|
||||
@LazyProperty
|
||||
def raw_id(self):
|
||||
|
|
@ -1693,6 +1696,10 @@ class EmptyCommit(BaseCommit):
|
|||
|
||||
@LazyProperty
|
||||
def branch(self):
|
||||
# If branch was explicitly set, return it
|
||||
if self._branch:
|
||||
return self._branch
|
||||
|
||||
if self.alias:
|
||||
from rhodecode.lib.vcs.backends import get_backend
|
||||
|
||||
|
|
|
|||
|
|
@ -452,6 +452,15 @@ class _BaseVcsSettingsForm(formencode.Schema):
|
|||
|
||||
# git
|
||||
vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
|
||||
vcs_git_default_branch = v.Regex(
|
||||
r"^(?!\.)(?!.*\.\.)(?!.*[~^:?*\[\\\s])(?!.*//)(?!.*/$)(?!.*\.lock$).+$",
|
||||
not_empty=False,
|
||||
if_missing="master",
|
||||
messages={
|
||||
"invalid": "Invalid branch name. Branch names cannot: start with '.', contain '..', "
|
||||
"contain special characters (~^:?*[\\), end with '/', contain '//', or end with '.lock'."
|
||||
},
|
||||
)
|
||||
rhodecode_git_use_rebase_for_merging = v.StringBoolean(if_missing=False)
|
||||
rhodecode_git_close_branch_before_merging = v.StringBoolean(if_missing=False)
|
||||
rhodecode_git_merge_strategy_selector = v.StringBoolean(if_missing=False)
|
||||
|
|
|
|||
|
|
@ -562,8 +562,19 @@ class RepoModel(BaseModel):
|
|||
owner = self._get_user(owner)
|
||||
fork_of = self._get_repo(fork_of)
|
||||
repo_group = self._get_repo_group(safe_int(repo_group))
|
||||
default_landing_ref, _lbl = ScmModel.backend_landing_ref(repo_type)
|
||||
landing_rev = landing_rev or default_landing_ref
|
||||
if repo_type == "git" and not landing_rev:
|
||||
# Get default branch from global Git settings
|
||||
from rhodecode.model.settings import VcsSettingsModel
|
||||
|
||||
settings_model = VcsSettingsModel()
|
||||
global_settings = settings_model.get_global_settings()
|
||||
|
||||
default_branch = global_settings.get("vcs_git_default_branch", "master")
|
||||
landing_rev = f"branch:{default_branch}"
|
||||
else:
|
||||
# Other VCS types or explicit landing_rev provided
|
||||
default_landing_ref, _lbl = ScmModel.backend_landing_ref(repo_type)
|
||||
landing_rev = landing_rev or default_landing_ref
|
||||
|
||||
try:
|
||||
repo_name = safe_str(repo_name)
|
||||
|
|
|
|||
|
|
@ -479,7 +479,10 @@ class VcsSettingsModel(object):
|
|||
("experimental", "evolution"),
|
||||
("experimental", "evolution.exchange"),
|
||||
)
|
||||
GIT_SETTINGS = (("vcs_git_lfs", "enabled"),)
|
||||
GIT_SETTINGS = (
|
||||
("vcs_git_lfs", "enabled"),
|
||||
("vcs_git", "default_branch"),
|
||||
)
|
||||
GLOBAL_HG_SETTINGS = (
|
||||
("extensions", "largefiles"),
|
||||
("phases", "publish"),
|
||||
|
|
@ -489,7 +492,10 @@ class VcsSettingsModel(object):
|
|||
("experimental", "evolution.exchange"),
|
||||
)
|
||||
|
||||
GLOBAL_GIT_SETTINGS = (("vcs_git_lfs", "enabled"),)
|
||||
GLOBAL_GIT_SETTINGS = (
|
||||
("vcs_git_lfs", "enabled"),
|
||||
("vcs_git", "default_branch"),
|
||||
)
|
||||
|
||||
SVN_BRANCH_SECTION = "vcs_svn_branch"
|
||||
SVN_TAG_SECTION = "vcs_svn_tag"
|
||||
|
|
@ -637,23 +643,29 @@ class VcsSettingsModel(object):
|
|||
self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
|
||||
|
||||
def create_or_update_repo_git_settings(self, data):
|
||||
# NOTE(marcink): # comma makes unpack work properly
|
||||
(lfs_enabled,) = self.GIT_SETTINGS
|
||||
|
||||
(lfs_enabled_key,) = self._get_settings_keys(self.GIT_SETTINGS, data)
|
||||
lfs_enabled, default_branch = self.GIT_SETTINGS
|
||||
lfs_enabled_key, default_branch_key = self._get_settings_keys(self.GIT_SETTINGS, data)
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key], active=data[lfs_enabled_key]
|
||||
)
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.repo_settings, *default_branch, value=data[default_branch_key] or "master", active=True
|
||||
)
|
||||
|
||||
def create_or_update_global_git_settings(self, data):
|
||||
lfs_enabled = self.GLOBAL_GIT_SETTINGS[0]
|
||||
lfs_enabled_key = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)[0]
|
||||
lfs_enabled, default_branch = self.GLOBAL_GIT_SETTINGS
|
||||
lfs_enabled_key, default_branch_key = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.global_settings, *lfs_enabled, value=data[lfs_enabled_key], active=data[lfs_enabled_key]
|
||||
)
|
||||
|
||||
self._create_or_update_ui(
|
||||
self.global_settings, *default_branch, value=data[default_branch_key] or "master", active=True
|
||||
)
|
||||
|
||||
def create_or_update_global_svn_settings(self, data):
|
||||
# branch/tags patterns
|
||||
self._create_svn_settings(self.global_settings, data)
|
||||
|
|
|
|||
|
|
@ -101,6 +101,20 @@
|
|||
<span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
|
||||
% endif
|
||||
</div>
|
||||
|
||||
% if display_globals:
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
<label for="vcs_git_default_branch${suffix}">${_('Default branch name')}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
${h.text('vcs_git_default_branch' + suffix, size=30, **kwargs)}
|
||||
</div>
|
||||
<div class="label">
|
||||
<span class="help-block">${_('Default branch name for new Git repositories. Common values: "main", "master", "develop". Defaults to "master" if empty.')}</span>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
|
|
|||
|
|
@ -561,6 +561,7 @@ class TestCreateOrUpdateGlobalHgSettings(object):
|
|||
class TestCreateOrUpdateGlobalGitSettings(object):
|
||||
FORM_DATA = {
|
||||
"vcs_git_lfs_enabled": False,
|
||||
"vcs_git_default_branch": "main",
|
||||
}
|
||||
|
||||
def test_creates_repo_hg_settings_when_data_is_correct(self):
|
||||
|
|
@ -571,9 +572,72 @@ class TestCreateOrUpdateGlobalGitSettings(object):
|
|||
|
||||
expected_calls = [
|
||||
mock.call(model.global_settings, "vcs_git_lfs", "enabled", active=False, value=False),
|
||||
mock.call(model.global_settings, "vcs_git", "default_branch", active=True, value="main"),
|
||||
]
|
||||
assert expected_calls == create_mock.call_args_list
|
||||
|
||||
def test_default_branch_falls_back_to_master_when_empty(self):
|
||||
model = VcsSettingsModel()
|
||||
form_data = {
|
||||
"vcs_git_lfs_enabled": False,
|
||||
"vcs_git_default_branch": "",
|
||||
}
|
||||
with mock.patch.object(model, "_create_or_update_ui") as create_mock:
|
||||
model.create_or_update_global_git_settings(form_data)
|
||||
Session().commit()
|
||||
|
||||
expected_calls = [
|
||||
mock.call(model.global_settings, "vcs_git_lfs", "enabled", active=False, value=False),
|
||||
mock.call(model.global_settings, "vcs_git", "default_branch", active=True, value="master"),
|
||||
]
|
||||
assert expected_calls == create_mock.call_args_list
|
||||
|
||||
def test_valid_branch_names_are_accepted(self):
|
||||
"""Test that valid Git branch names pass validation"""
|
||||
import formencode
|
||||
from rhodecode.model.forms import ApplicationSettingsForm
|
||||
|
||||
valid_names = ["main", "develop", "feature/my-feature", "release-1.0", "hotfix_urgent"]
|
||||
|
||||
for branch_name in valid_names:
|
||||
form_data = {
|
||||
"vcs_git_lfs_enabled": False,
|
||||
"vcs_git_default_branch": branch_name,
|
||||
}
|
||||
schema = ApplicationSettingsForm(lambda s: s)()
|
||||
result = schema.to_python(form_data)
|
||||
assert result["vcs_git_default_branch"] == branch_name
|
||||
|
||||
def test_invalid_branch_names_are_rejected(self):
|
||||
"""Test that invalid Git branch names fail validation"""
|
||||
import formencode
|
||||
from rhodecode.model.forms import ApplicationSettingsForm
|
||||
|
||||
invalid_names = [
|
||||
".hidden", # starts with dot
|
||||
"feature..bug", # contains double dots
|
||||
"feature~1", # contains tilde
|
||||
"branch:name", # contains colon
|
||||
"what?", # contains question mark
|
||||
"wild*card", # contains asterisk
|
||||
"feature[test]", # contains brackets
|
||||
"trailing/", # ends with slash
|
||||
"double//slash", # contains double slash
|
||||
"feature.lock", # ends with .lock
|
||||
]
|
||||
|
||||
schema = ApplicationSettingsForm(lambda s: s)()
|
||||
for branch_name in invalid_names:
|
||||
form_data = {
|
||||
"vcs_git_lfs_enabled": False,
|
||||
"vcs_git_default_branch": branch_name,
|
||||
}
|
||||
try:
|
||||
schema.to_python(form_data)
|
||||
assert False, f"Expected validation error for branch name: {branch_name}"
|
||||
except formencode.Invalid:
|
||||
pass # Expected
|
||||
|
||||
|
||||
class TestDeleteRepoSvnPattern(object):
|
||||
def test_success_when_repo_is_set(self, backend_svn, settings_util):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue