mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 03:29:40 +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:
		| @@ -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"), | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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() | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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> | ||||||
|   | |||||||
| @@ -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}} | ||||||
|   | |||||||
| @@ -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> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user