1
1
mirror of https://github.com/go-gitea/gitea synced 2025-08-23 01:48:27 +00:00

Use configurable remote name for git commands (#35172)

Closes #19403, and makes it possible to use any remote name in code
snippets for an empty repository and pull request.
This change is very helpful to me, because I always use different name
for my gitea remote.

Uses setting config module to store the value. Default is `origin` for
backward compatibility.

### Screenshots
<details>
<summary>Empty repo</summary>
<img width="791" height="398" alt="image"
src="https://github.com/user-attachments/assets/7214053d-a8dd-4e77-8c9d-78936d9859e0"
/>
</details>

<details>
<summary>Pull Request</summary>
<img width="591" height="452" alt="image"
src="https://github.com/user-attachments/assets/ebc3d25c-5d6d-481d-819d-9706af3c5594"
/>
</details>

<details>
<summary>Settings page</summary>
<img width="1438" height="839" alt="image"
src="https://github.com/user-attachments/assets/d92bfa2c-7adc-4efe-95fa-0c55ad13b3f5"
/>
</details>

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Ilya Nurullin
2025-08-21 20:14:35 +03:00
committed by GitHub
parent c0f24bd803
commit e844a41248
7 changed files with 75 additions and 38 deletions

View File

@@ -49,6 +49,7 @@ func DefaultOpenWithEditorApps() OpenWithEditorAppsType {
type RepositoryStruct struct { type RepositoryStruct struct {
OpenWithEditorApps *config.Value[OpenWithEditorAppsType] OpenWithEditorApps *config.Value[OpenWithEditorAppsType]
GitGuideRemoteName *config.Value[string]
} }
type ConfigStruct struct { type ConfigStruct struct {
@@ -70,6 +71,7 @@ func initDefaultConfig() {
}, },
Repository: &RepositoryStruct{ Repository: &RepositoryStruct{
OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"), OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"),
GitGuideRemoteName: config.ValueJSON[string]("repository.git-guide-remote-name").WithDefault("origin"),
}, },
} }
} }

View File

@@ -46,7 +46,7 @@ func (value *Value[T]) Value(ctx context.Context) (v T) {
rev := dg.GetRevision(ctx) rev := dg.GetRevision(ctx)
// if the revision in database doesn't change, use the last value // if the revision in the database doesn't change, use the last value
value.mu.RLock() value.mu.RLock()
if rev == value.revision { if rev == value.revision {
v = value.value v = value.value
@@ -84,6 +84,10 @@ func (value *Value[T]) WithDefault(def T) *Value[T] {
return value return value
} }
func (value *Value[T]) DefaultValue() T {
return value.def
}
func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] { func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] {
value.cfgSecKey = cfgSecKey value.cfgSecKey = cfgSecKey
return value return value

View File

@@ -3425,6 +3425,7 @@ config.picture_service = Picture Service
config.disable_gravatar = Disable Gravatar config.disable_gravatar = Disable Gravatar
config.enable_federated_avatar = Enable Federated Avatars config.enable_federated_avatar = Enable Federated Avatars
config.open_with_editor_app_help = The "Open with" editors for the clone menu. If left empty, the default will be used. Expand to see the default. config.open_with_editor_app_help = The "Open with" editors for the clone menu. If left empty, the default will be used. Expand to see the default.
config.git_guide_remote_name = Repository remote name for git commands in the guide
config.git_config = Git Configuration config.git_config = Git Configuration
config.git_disable_diff_highlight = Disable Diff Syntax Highlight config.git_disable_diff_highlight = Disable Diff Syntax Highlight

View File

@@ -196,17 +196,21 @@ func ConfigSettings(ctx *context.Context) {
} }
func ChangeConfig(ctx *context.Context) { func ChangeConfig(ctx *context.Context) {
key := strings.TrimSpace(ctx.FormString("key"))
value := ctx.FormString("value")
cfg := setting.Config() cfg := setting.Config()
marshalBool := func(v string) (string, error) { //nolint:unparam // error is always nil marshalBool := func(v string) ([]byte, error) {
if b, _ := strconv.ParseBool(v); b { b, _ := strconv.ParseBool(v)
return "true", nil return json.Marshal(b)
}
return "false", nil
} }
marshalOpenWithApps := func(value string) (string, error) {
marshalString := func(emptyDefault string) func(v string) ([]byte, error) {
return func(v string) ([]byte, error) {
return json.Marshal(util.IfZero(v, emptyDefault))
}
}
marshalOpenWithApps := func(value string) ([]byte, error) {
// TODO: move the block alongside OpenWithEditorAppsType.ToTextareaString
lines := strings.Split(value, "\n") lines := strings.Split(value, "\n")
var openWithEditorApps setting.OpenWithEditorAppsType var openWithEditorApps setting.OpenWithEditorAppsType
for _, line := range lines { for _, line := range lines {
@@ -224,32 +228,47 @@ func ChangeConfig(ctx *context.Context) {
OpenURL: strings.TrimSpace(openURL), OpenURL: strings.TrimSpace(openURL),
}) })
} }
b, err := json.Marshal(openWithEditorApps) return json.Marshal(openWithEditorApps)
if err != nil {
return "", err
}
return string(b), nil
} }
marshallers := map[string]func(string) (string, error){ marshallers := map[string]func(string) ([]byte, error){
cfg.Picture.DisableGravatar.DynKey(): marshalBool, cfg.Picture.DisableGravatar.DynKey(): marshalBool,
cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool, cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool,
cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps, cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps,
} cfg.Repository.GitGuideRemoteName.DynKey(): marshalString(cfg.Repository.GitGuideRemoteName.DefaultValue()),
marshaller, hasMarshaller := marshallers[key]
if !hasMarshaller {
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
return
}
marshaledValue, err := marshaller(value)
if err != nil {
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
return
}
if err = system_model.SetSettings(ctx, map[string]string{key: marshaledValue}); err != nil {
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
return
} }
_ = ctx.Req.ParseForm()
configKeys := ctx.Req.Form["key"]
configValues := ctx.Req.Form["value"]
configSettings := map[string]string{}
loop:
for i, key := range configKeys {
if i >= len(configValues) {
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
break loop
}
value := configValues[i]
marshaller, hasMarshaller := marshallers[key]
if !hasMarshaller {
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
break loop
}
marshaledValue, err := marshaller(value)
if err != nil {
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
break loop
}
configSettings[key] = string(marshaledValue)
}
if ctx.Written() {
return
}
if err := system_model.SetSettings(ctx, configSettings); err != nil {
ctx.ServerError("SetSettings", err)
return
}
config.GetDynGetter().InvalidateCache() config.GetDynGetter().InvalidateCache()
ctx.JSONOK() ctx.JSONOK()
} }

View File

@@ -24,7 +24,7 @@
{{ctx.Locale.Tr "repository"}} {{ctx.Locale.Tr "repository"}}
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">
<form class="ui form form-fetch-action" method="post" action="{{AppSubUrl}}/-/admin/config?key={{.SystemConfig.Repository.OpenWithEditorApps.DynKey}}"> <form class="ui form form-fetch-action" method="post" action="{{AppSubUrl}}/-/admin/config">
<div class="field"> <div class="field">
<details> <details>
<summary>{{ctx.Locale.Tr "admin.config.open_with_editor_app_help"}}</summary> <summary>{{ctx.Locale.Tr "admin.config.open_with_editor_app_help"}}</summary>
@@ -32,7 +32,16 @@
</details> </details>
</div> </div>
<div class="field"> <div class="field">
<textarea name="value">{{(.SystemConfig.Repository.OpenWithEditorApps.Value ctx).ToTextareaString}}</textarea> {{$cfg := .SystemConfig.Repository.OpenWithEditorApps}}
<input type="hidden" name="key" value="{{$cfg.DynKey}}">
<textarea name="value">{{($cfg.Value ctx).ToTextareaString}}</textarea>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "admin.config.git_guide_remote_name"}}</label>
{{$cfg = .SystemConfig.Repository.GitGuideRemoteName}}
<input type="hidden" name="key" value="{{$cfg.DynKey}}">
<input name="value" value="{{$cfg.Value ctx}}" placeholder="{{$cfg.DefaultValue}}" maxlength="100" dir="auto" required pattern="^[A-Za-z0-9][\-_A-Za-z0-9]*$">
</div> </div>
<div class="field"> <div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "save"}}</button> <button class="ui primary button">{{ctx.Locale.Tr "save"}}</button>

View File

@@ -46,13 +46,14 @@
<div class="item"> <div class="item">
<h3>{{ctx.Locale.Tr "repo.create_new_repo_command"}}</h3> <h3>{{ctx.Locale.Tr "repo.create_new_repo_command"}}</h3>
<div class="markup"> <div class="markup">
{{$gitRemoteName := $.SystemConfig.Repository.GitGuideRemoteName.Value ctx}}
<pre><code>touch README.md <pre><code>touch README.md
git init{{if ne .Repository.ObjectFormatName "sha1"}} --object-format={{.Repository.ObjectFormatName}}{{end}}{{/* for sha256 repo, it needs to set "object-format" explicitly*/}} git init{{if ne .Repository.ObjectFormatName "sha1"}} --object-format={{.Repository.ObjectFormatName}}{{end}}{{/* for sha256 repo, it needs to set "object-format" explicitly*/}}
{{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}} {{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}}
git add README.md git add README.md
git commit -m "first commit" git commit -m "first commit"
git remote add origin <span class="js-clone-url">{{$.CloneButtonOriginLink.HTTPS}}</span> git remote add {{$gitRemoteName}} <span class="js-clone-url">{{$.CloneButtonOriginLink.HTTPS}}</span>
git push -u origin {{.Repository.DefaultBranch}}</code></pre> git push -u {{$gitRemoteName}} {{.Repository.DefaultBranch}}</code></pre>
</div> </div>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
@@ -60,8 +61,8 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre>
<div class="item"> <div class="item">
<h3>{{ctx.Locale.Tr "repo.push_exist_repo"}}</h3> <h3>{{ctx.Locale.Tr "repo.push_exist_repo"}}</h3>
<div class="markup"> <div class="markup">
<pre><code>git remote add origin <span class="js-clone-url">{{$.CloneButtonOriginLink.HTTPS}}</span> <pre><code>git remote add {{$gitRemoteName}} <span class="js-clone-url">{{$.CloneButtonOriginLink.HTTPS}}</span>
git push -u origin {{.Repository.DefaultBranch}}</code></pre> git push -u {{$gitRemoteName}} {{.Repository.DefaultBranch}}</code></pre>
</div> </div>
</div> </div>
{{end}} {{end}}

View File

@@ -8,10 +8,11 @@
{{$localBranch = print .PullRequest.HeadRepo.OwnerName "-" .PullRequest.HeadBranch}} {{$localBranch = print .PullRequest.HeadRepo.OwnerName "-" .PullRequest.HeadBranch}}
{{end}} {{end}}
<div class="ui secondary segment tw-font-mono"> <div class="ui secondary segment tw-font-mono">
{{$gitRemoteName := ctx.RootData.SystemConfig.Repository.GitGuideRemoteName.Value ctx}}
{{if eq .PullRequest.Flow 0}} {{if eq .PullRequest.Flow 0}}
<div>git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}<origin-url data-url="{{.PullRequest.HeadRepo.Link}}"></origin-url>{{else}}origin{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}</div> <div>git fetch -u {{if ne .PullRequest.HeadRepo.ID .PullRequest.BaseRepo.ID}}<origin-url data-url="{{.PullRequest.HeadRepo.Link}}"></origin-url>{{else}}{{$gitRemoteName}}{{end}} {{.PullRequest.HeadBranch}}:{{$localBranch}}</div>
{{else}} {{else}}
<div>git fetch -u origin {{.PullRequest.GetGitHeadRefName}}:{{$localBranch}}</div> <div>git fetch -u {{$gitRemoteName}} {{.PullRequest.GetGitHeadRefName}}:{{$localBranch}}</div>
{{end}} {{end}}
<div>git checkout {{$localBranch}}</div> <div>git checkout {{$localBranch}}</div>
</div> </div>
@@ -50,7 +51,7 @@
<div>git checkout {{.PullRequest.BaseBranch}}</div> <div>git checkout {{.PullRequest.BaseBranch}}</div>
<div>git merge {{$localBranch}}</div> <div>git merge {{$localBranch}}</div>
</div> </div>
<div>git push origin {{.PullRequest.BaseBranch}}</div> <div>git push {{$gitRemoteName}} {{.PullRequest.BaseBranch}}</div>
</div> </div>
{{end}} {{end}}
</div> </div>