diff --git a/cmd/doctor.go b/cmd/doctor.go
index 20c1904afa..2a93db27da 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -127,6 +127,12 @@ var checklist = []check{
 		isDefault: false,
 		f:         runDoctorUserStarNum,
 	},
+	{
+		title:     "Enable push options",
+		name:      "enable-push-options",
+		isDefault: false,
+		f:         runDoctorEnablePushOptions,
+	},
 	// more checks please append here
 }
 
@@ -605,3 +611,28 @@ func runDoctorCheckDBConsistency(ctx *cli.Context) ([]string, error) {
 
 	return results, nil
 }
+
+func runDoctorEnablePushOptions(ctx *cli.Context) ([]string, error) {
+	numRepos := 0
+	_, err := iterateRepositories(func(repo *models.Repository) ([]string, error) {
+		numRepos++
+		r, err := git.OpenRepository(repo.RepoPath())
+		if err != nil {
+			return nil, err
+		}
+		defer r.Close()
+
+		if ctx.Bool("fix") {
+			_, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunInDir(r.Path)
+			return nil, err
+		}
+
+		return nil, nil
+	})
+
+	var prefix string
+	if !ctx.Bool("fix") {
+		prefix = "DRY RUN: "
+	}
+	return []string{fmt.Sprintf("%sEnabled push options for %d repositories.", prefix, numRepos)}, err
+}
diff --git a/cmd/hook.go b/cmd/hook.go
index f5658de7cd..863ed832a9 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -178,6 +178,7 @@ Gitea or set your environment appropriately.`, "")
 		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
+		GitPushOptions:                  pushOptions(),
 		ProtectedBranchID:               prID,
 		IsDeployKey:                     isDeployKey,
 	}
@@ -326,6 +327,7 @@ Gitea or set your environment appropriately.`, "")
 		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
+		GitPushOptions:                  pushOptions(),
 	}
 	oldCommitIDs := make([]string, hookBatchSize)
 	newCommitIDs := make([]string, hookBatchSize)
@@ -438,3 +440,17 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) {
 		os.Stderr.Sync()
 	}
 }
+
+func pushOptions() map[string]string {
+	opts := make(map[string]string)
+	if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
+		for idx := 0; idx < pushCount; idx++ {
+			opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
+			kv := strings.SplitN(opt, "=", 2)
+			if len(kv) == 2 {
+				opts[kv[0]] = kv[1]
+			}
+		}
+	}
+	return opts
+}
diff --git a/docs/content/doc/usage/push-options.en-us.md b/docs/content/doc/usage/push-options.en-us.md
new file mode 100644
index 0000000000..439d13b42f
--- /dev/null
+++ b/docs/content/doc/usage/push-options.en-us.md
@@ -0,0 +1,31 @@
+---
+date: "2020-07-06T16:00:00+02:00"
+title: "Usage: Push Options"
+slug: "push-options"
+weight: 15
+toc: true
+draft: false
+menu:
+  sidebar:
+    parent: "usage"
+    name: "Push Options"
+    weight: 15
+    identifier: "push-options"
+---
+
+# Push Options
+
+In Gitea `1.13`, support for some [push options](https://git-scm.com/docs/git-push#Documentation/git-push.txt--oltoptiongt)
+were added.
+
+
+## Supported Options
+
+- `repo.private` (true|false) - Change the repository's visibility.  
+This is particularly useful when combined with push-to-create.
+- `repo.template` (true|false) - Change whether the repository is a template.
+
+Example of changing a repository's visibility to public:  
+```shell
+git push -o repo.private=false -u origin master
+```
diff --git a/modules/git/git.go b/modules/git/git.go
index 6f231cee7a..1061bdb0d5 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -120,6 +120,12 @@ func Init(ctx context.Context) error {
 		return err
 	}
 
+	if version.Compare(gitVersion, "2.10", ">=") {
+		if err := checkAndSetConfig("receive.advertisePushOptions", "true", true); err != nil {
+			return err
+		}
+	}
+
 	if version.Compare(gitVersion, "2.18", ">=") {
 		if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil {
 			return err
diff --git a/modules/private/hook.go b/modules/private/hook.go
index 010fc4d724..84d66943ba 100644
--- a/modules/private/hook.go
+++ b/modules/private/hook.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 	"net/http"
 	"net/url"
+	"strconv"
 	"time"
 
 	"code.gitea.io/gitea/modules/setting"
@@ -19,8 +20,28 @@ const (
 	GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 	GitObjectDirectory              = "GIT_OBJECT_DIRECTORY"
 	GitQuarantinePath               = "GIT_QUARANTINE_PATH"
+	GitPushOptionCount              = "GIT_PUSH_OPTION_COUNT"
 )
 
+// GitPushOptions is a wrapper around a map[string]string
+type GitPushOptions map[string]string
+
+// GitPushOptions keys
+const (
+	GitPushOptionRepoPrivate  = "repo.private"
+	GitPushOptionRepoTemplate = "repo.template"
+)
+
+// Bool checks for a key in the map and parses as a boolean
+func (g GitPushOptions) Bool(key string, def bool) bool {
+	if val, ok := g[key]; ok {
+		if b, err := strconv.ParseBool(val); err == nil {
+			return b
+		}
+	}
+	return def
+}
+
 // HookOptions represents the options for the Hook calls
 type HookOptions struct {
 	OldCommitIDs                    []string
@@ -31,6 +52,7 @@ type HookOptions struct {
 	GitObjectDirectory              string
 	GitAlternativeObjectDirectories string
 	GitQuarantinePath               string
+	GitPushOptions                  GitPushOptions
 	ProtectedBranchID               int64
 	IsDeployKey                     bool
 }
diff --git a/routers/private/hook.go b/routers/private/hook.go
index 6cdc5393f4..2bccca3e3e 100644
--- a/routers/private/hook.go
+++ b/routers/private/hook.go
@@ -436,6 +436,18 @@ func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) {
 		}
 	}
 
+	// Push Options
+	if repo != nil && len(opts.GitPushOptions) > 0 {
+		repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate)
+		repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate)
+		if err := models.UpdateRepositoryCols(repo, "is_private", "is_template"); err != nil {
+			log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
+			ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
+				Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
+			})
+		}
+	}
+
 	results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs))
 
 	// We have to reload the repo in case its state is changed above