diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go
new file mode 100644
index 0000000000..3b2c916ab0
--- /dev/null
+++ b/integrations/api_repo_edit_test.go
@@ -0,0 +1,225 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"testing"
+
+	"code.gitea.io/gitea/models"
+	api "code.gitea.io/gitea/modules/structs"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// getRepoEditOptionFromRepo gets the options for an existing repo exactly as is
+func getRepoEditOptionFromRepo(repo *models.Repository) *api.EditRepoOption {
+	name := repo.Name
+	description := repo.Description
+	website := repo.Website
+	private := repo.IsPrivate
+	hasIssues := false
+	if _, err := repo.GetUnit(models.UnitTypeIssues); err == nil {
+		hasIssues = true
+	}
+	hasWiki := false
+	if _, err := repo.GetUnit(models.UnitTypeWiki); err == nil {
+		hasWiki = true
+	}
+	defaultBranch := repo.DefaultBranch
+	hasPullRequests := false
+	ignoreWhitespaceConflicts := false
+	allowMerge := false
+	allowRebase := false
+	allowRebaseMerge := false
+	allowSquash := false
+	if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil {
+		config := unit.PullRequestsConfig()
+		hasPullRequests = true
+		ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
+		allowMerge = config.AllowMerge
+		allowRebase = config.AllowRebase
+		allowRebaseMerge = config.AllowRebaseMerge
+		allowSquash = config.AllowSquash
+	}
+	archived := repo.IsArchived
+	return &api.EditRepoOption{
+		Name:                      &name,
+		Description:               &description,
+		Website:                   &website,
+		Private:                   &private,
+		HasIssues:                 &hasIssues,
+		HasWiki:                   &hasWiki,
+		DefaultBranch:             &defaultBranch,
+		HasPullRequests:           &hasPullRequests,
+		IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
+		AllowMerge:                &allowMerge,
+		AllowRebase:               &allowRebase,
+		AllowRebaseMerge:          &allowRebaseMerge,
+		AllowSquash:               &allowSquash,
+		Archived:                  &archived,
+	}
+}
+
+// getNewRepoEditOption Gets the options to change everything about an existing repo by adding to strings or changing
+// the boolean
+func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption {
+	// Gives a new property to everything
+	name := *opts.Name + "renamed"
+	description := "new description"
+	website := "http://wwww.newwebsite.com"
+	private := !*opts.Private
+	hasIssues := !*opts.HasIssues
+	hasWiki := !*opts.HasWiki
+	defaultBranch := "master"
+	hasPullRequests := !*opts.HasPullRequests
+	ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts
+	allowMerge := !*opts.AllowMerge
+	allowRebase := !*opts.AllowRebase
+	allowRebaseMerge := !*opts.AllowRebaseMerge
+	allowSquash := !*opts.AllowSquash
+	archived := !*opts.Archived
+
+	return &api.EditRepoOption{
+		Name:                      &name,
+		Description:               &description,
+		Website:                   &website,
+		Private:                   &private,
+		DefaultBranch:             &defaultBranch,
+		HasIssues:                 &hasIssues,
+		HasWiki:                   &hasWiki,
+		HasPullRequests:           &hasPullRequests,
+		IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
+		AllowMerge:                &allowMerge,
+		AllowRebase:               &allowRebase,
+		AllowRebaseMerge:          &allowRebaseMerge,
+		AllowSquash:               &allowSquash,
+		Archived:                  &archived,
+	}
+}
+
+func TestAPIRepoEdit(t *testing.T) {
+	onGiteaRun(t, func(t *testing.T, u *url.URL) {
+		user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
+		user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
+		user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+		repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
+		repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
+		repo16 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
+
+		// Get user2's token
+		session := loginUser(t, user2.Name)
+		token2 := getTokenForLoggedInUser(t, session)
+		session = emptyTestSession(t)
+		// Get user4's token
+		session = loginUser(t, user4.Name)
+		token4 := getTokenForLoggedInUser(t, session)
+		session = emptyTestSession(t)
+
+		// Test editing a repo1 which user2 owns, changing name and many properties
+		origRepoEditOption := getRepoEditOptionFromRepo(repo1)
+		repoEditOption := getNewRepoEditOption(origRepoEditOption)
+		url := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo1.Name, token2)
+		req := NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		resp := session.MakeRequest(t, req, http.StatusOK)
+		var repo api.Repository
+		DecodeJSON(t, resp, &repo)
+		assert.NotNil(t, repo)
+		// check response
+		assert.Equal(t, *repoEditOption.Name, repo.Name)
+		assert.Equal(t, *repoEditOption.Description, repo.Description)
+		assert.Equal(t, *repoEditOption.Website, repo.Website)
+		assert.Equal(t, *repoEditOption.Archived, repo.Archived)
+		// check repo1 from database
+		repo1edited := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
+		repo1editedOption := getRepoEditOptionFromRepo(repo1edited)
+		assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name)
+		assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description)
+		assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website)
+		assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived)
+		assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private)
+		assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki)
+		// reset repo in db
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, *repoEditOption.Name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusOK)
+
+		// Test editing a non-existing repo
+		name := "repodoesnotexist"
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{Name: &name})
+		resp = session.MakeRequest(t, req, http.StatusNotFound)
+
+		// Test editing repo16 by user4 who does not have write access
+		origRepoEditOption = getRepoEditOptionFromRepo(repo16)
+		repoEditOption = getNewRepoEditOption(origRepoEditOption)
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token4)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		session.MakeRequest(t, req, http.StatusNotFound)
+
+		// Tests a repo with no token given so will fail
+		origRepoEditOption = getRepoEditOptionFromRepo(repo16)
+		repoEditOption = getNewRepoEditOption(origRepoEditOption)
+		url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusNotFound)
+
+		// Test using access token for a private repo that the user of the token owns
+		origRepoEditOption = getRepoEditOptionFromRepo(repo16)
+		repoEditOption = getNewRepoEditOption(origRepoEditOption)
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusOK)
+		// reset repo in db
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, *repoEditOption.Name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusOK)
+
+		// Test making a repo public that is private
+		repo16 = models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
+		assert.True(t, repo16.IsPrivate)
+		private := false
+		repoEditOption = &api.EditRepoOption{
+			Private: &private,
+		}
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo16.Name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusOK)
+		repo16 = models.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository)
+		assert.False(t, repo16.IsPrivate)
+		// Make it private again
+		private = true
+		repoEditOption.Private = &private
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusOK)
+
+		// Test using org repo "user3/repo3" where user2 is a collaborator
+		origRepoEditOption = getRepoEditOptionFromRepo(repo3)
+		repoEditOption = getNewRepoEditOption(origRepoEditOption)
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user3.Name, repo3.Name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		session.MakeRequest(t, req, http.StatusOK)
+		// reset repo in db
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user3.Name, *repoEditOption.Name, token2)
+		req = NewRequestWithJSON(t, "PATCH", url, &origRepoEditOption)
+		resp = session.MakeRequest(t, req, http.StatusOK)
+
+		// Test using org repo "user3/repo3" with no user token
+		origRepoEditOption = getRepoEditOptionFromRepo(repo3)
+		repoEditOption = getNewRepoEditOption(origRepoEditOption)
+		url = fmt.Sprintf("/api/v1/repos/%s/%s", user3.Name, repo3.Name)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		session.MakeRequest(t, req, http.StatusNotFound)
+
+		// Test using repo "user2/repo1" where user4 is a NOT collaborator
+		origRepoEditOption = getRepoEditOptionFromRepo(repo1)
+		repoEditOption = getNewRepoEditOption(origRepoEditOption)
+		url = fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", user2.Name, repo1.Name, token4)
+		req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption)
+		session.MakeRequest(t, req, http.StatusForbidden)
+	})
+}
diff --git a/integrations/api_repo_file_delete_test.go b/integrations/api_repo_file_delete_test.go
index 57e2539e19..e9029a669b 100644
--- a/integrations/api_repo_file_delete_test.go
+++ b/integrations/api_repo_file_delete_test.go
@@ -108,7 +108,7 @@ func TestAPIDeleteFile(t *testing.T) {
 		DecodeJSON(t, resp, &apiError)
 		assert.Equal(t, expectedAPIError, apiError)
 
-		// Test creating a file in repo1 by user4 who does not have write access
+		// Test creating a file in repo16 by user4 who does not have write access
 		fileID++
 		treePath = fmt.Sprintf("delete/file%d.txt", fileID)
 		createFile(user2, repo16, treePath)
diff --git a/models/org.go b/models/org.go
index b7db32ef16..6511072e2b 100644
--- a/models/org.go
+++ b/models/org.go
@@ -162,8 +162,8 @@ func CreateOrganization(org, owner *User) (err error) {
 	}
 
 	// insert units for team
-	var units = make([]TeamUnit, 0, len(allRepUnitTypes))
-	for _, tp := range allRepUnitTypes {
+	var units = make([]TeamUnit, 0, len(AllRepoUnitTypes))
+	for _, tp := range AllRepoUnitTypes {
 		units = append(units, TeamUnit{
 			OrgID:  org.ID,
 			TeamID: t.ID,
diff --git a/models/repo.go b/models/repo.go
index b8a3714abf..16684bdeef 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -274,32 +274,64 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
 			parent = repo.BaseRepo.innerAPIFormat(e, mode, true)
 		}
 	}
+	hasIssues := false
+	if _, err := repo.getUnit(e, UnitTypeIssues); err == nil {
+		hasIssues = true
+	}
+	hasWiki := false
+	if _, err := repo.getUnit(e, UnitTypeWiki); err == nil {
+		hasWiki = true
+	}
+	hasPullRequests := false
+	ignoreWhitespaceConflicts := false
+	allowMerge := false
+	allowRebase := false
+	allowRebaseMerge := false
+	allowSquash := false
+	if unit, err := repo.getUnit(e, UnitTypePullRequests); err == nil {
+		config := unit.PullRequestsConfig()
+		hasPullRequests = true
+		ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
+		allowMerge = config.AllowMerge
+		allowRebase = config.AllowRebase
+		allowRebaseMerge = config.AllowRebaseMerge
+		allowSquash = config.AllowSquash
+	}
+
 	return &api.Repository{
-		ID:            repo.ID,
-		Owner:         repo.Owner.APIFormat(),
-		Name:          repo.Name,
-		FullName:      repo.FullName(),
-		Description:   repo.Description,
-		Private:       repo.IsPrivate,
-		Empty:         repo.IsEmpty,
-		Archived:      repo.IsArchived,
-		Size:          int(repo.Size / 1024),
-		Fork:          repo.IsFork,
-		Parent:        parent,
-		Mirror:        repo.IsMirror,
-		HTMLURL:       repo.HTMLURL(),
-		SSHURL:        cloneLink.SSH,
-		CloneURL:      cloneLink.HTTPS,
-		Website:       repo.Website,
-		Stars:         repo.NumStars,
-		Forks:         repo.NumForks,
-		Watchers:      repo.NumWatches,
-		OpenIssues:    repo.NumOpenIssues,
-		DefaultBranch: repo.DefaultBranch,
-		Created:       repo.CreatedUnix.AsTime(),
-		Updated:       repo.UpdatedUnix.AsTime(),
-		Permissions:   permission,
-		AvatarURL:     repo.AvatarLink(),
+		ID:                        repo.ID,
+		Owner:                     repo.Owner.APIFormat(),
+		Name:                      repo.Name,
+		FullName:                  repo.FullName(),
+		Description:               repo.Description,
+		Private:                   repo.IsPrivate,
+		Empty:                     repo.IsEmpty,
+		Archived:                  repo.IsArchived,
+		Size:                      int(repo.Size / 1024),
+		Fork:                      repo.IsFork,
+		Parent:                    parent,
+		Mirror:                    repo.IsMirror,
+		HTMLURL:                   repo.HTMLURL(),
+		SSHURL:                    cloneLink.SSH,
+		CloneURL:                  cloneLink.HTTPS,
+		Website:                   repo.Website,
+		Stars:                     repo.NumStars,
+		Forks:                     repo.NumForks,
+		Watchers:                  repo.NumWatches,
+		OpenIssues:                repo.NumOpenIssues,
+		DefaultBranch:             repo.DefaultBranch,
+		Created:                   repo.CreatedUnix.AsTime(),
+		Updated:                   repo.UpdatedUnix.AsTime(),
+		Permissions:               permission,
+		HasIssues:                 hasIssues,
+		HasWiki:                   hasWiki,
+		HasPullRequests:           hasPullRequests,
+		IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts,
+		AllowMerge:                allowMerge,
+		AllowRebase:               allowRebase,
+		AllowRebaseMerge:          allowRebaseMerge,
+		AllowSquash:               allowSquash,
+		AvatarURL:                 repo.AvatarLink(),
 	}
 }
 
@@ -346,10 +378,20 @@ func (repo *Repository) UnitEnabled(tp UnitType) bool {
 	return false
 }
 
-var (
-	// ErrUnitNotExist organization does not exist
-	ErrUnitNotExist = errors.New("Unit does not exist")
-)
+// ErrUnitTypeNotExist represents a "UnitTypeNotExist" kind of error.
+type ErrUnitTypeNotExist struct {
+	UT UnitType
+}
+
+// IsErrUnitTypeNotExist checks if an error is a ErrUnitNotExist.
+func IsErrUnitTypeNotExist(err error) bool {
+	_, ok := err.(ErrUnitTypeNotExist)
+	return ok
+}
+
+func (err ErrUnitTypeNotExist) Error() string {
+	return fmt.Sprintf("Unit type does not exist: %s", err.UT.String())
+}
 
 // MustGetUnit always returns a RepoUnit object
 func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit {
@@ -373,6 +415,11 @@ func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit {
 			Type:   tp,
 			Config: new(PullRequestsConfig),
 		}
+	} else if tp == UnitTypeIssues {
+		return &RepoUnit{
+			Type:   tp,
+			Config: new(IssuesConfig),
+		}
 	}
 	return &RepoUnit{
 		Type:   tp,
@@ -394,7 +441,7 @@ func (repo *Repository) getUnit(e Engine, tp UnitType) (*RepoUnit, error) {
 			return unit, nil
 		}
 	}
-	return nil, ErrUnitNotExist
+	return nil, ErrUnitTypeNotExist{tp}
 }
 
 func (repo *Repository) getOwner(e Engine) (err error) {
@@ -1232,8 +1279,8 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
 	}
 
 	// insert units for repo
-	var units = make([]RepoUnit, 0, len(defaultRepoUnits))
-	for _, tp := range defaultRepoUnits {
+	var units = make([]RepoUnit, 0, len(DefaultRepoUnits))
+	for _, tp := range DefaultRepoUnits {
 		if tp == UnitTypeIssues {
 			units = append(units, RepoUnit{
 				RepoID: repo.ID,
diff --git a/models/unit.go b/models/unit.go
index 697df696bc..9f5c8d3cbb 100644
--- a/models/unit.go
+++ b/models/unit.go
@@ -58,8 +58,8 @@ func (u UnitType) ColorFormat(s fmt.State) {
 }
 
 var (
-	// allRepUnitTypes contains all the unit types
-	allRepUnitTypes = []UnitType{
+	// AllRepoUnitTypes contains all the unit types
+	AllRepoUnitTypes = []UnitType{
 		UnitTypeCode,
 		UnitTypeIssues,
 		UnitTypePullRequests,
@@ -69,8 +69,8 @@ var (
 		UnitTypeExternalTracker,
 	}
 
-	// defaultRepoUnits contains the default unit types
-	defaultRepoUnits = []UnitType{
+	// DefaultRepoUnits contains the default unit types
+	DefaultRepoUnits = []UnitType{
 		UnitTypeCode,
 		UnitTypeIssues,
 		UnitTypePullRequests,
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 19f5ff8afe..b4d162b776 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -41,9 +41,17 @@ type Repository struct {
 	// swagger:strfmt date-time
 	Created time.Time `json:"created_at"`
 	// swagger:strfmt date-time
-	Updated     time.Time   `json:"updated_at"`
-	Permissions *Permission `json:"permissions,omitempty"`
-	AvatarURL   string      `json:"avatar_url"`
+	Updated                   time.Time   `json:"updated_at"`
+	Permissions               *Permission `json:"permissions,omitempty"`
+	HasIssues                 bool        `json:"has_issues"`
+	HasWiki                   bool        `json:"has_wiki"`
+	HasPullRequests           bool        `json:"has_pull_requests"`
+	IgnoreWhitespaceConflicts bool        `json:"ignore_whitespace_conflicts"`
+	AllowMerge                bool        `json:"allow_merge_commits"`
+	AllowRebase               bool        `json:"allow_rebase"`
+	AllowRebaseMerge          bool        `json:"allow_rebase_explicit"`
+	AllowSquash               bool        `json:"allow_squash_merge"`
+	AvatarURL                 string      `json:"avatar_url"`
 }
 
 // CreateRepoOption options when creating repository
@@ -71,38 +79,36 @@ type CreateRepoOption struct {
 // EditRepoOption options when editing a repository's properties
 // swagger:model
 type EditRepoOption struct {
-	// Name of the repository
-	//
-	// required: true
+	// name of the repository
 	// unique: true
-	Name *string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
-	// A short description of the repository.
+	Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"`
+	// a short description of the repository.
 	Description *string `json:"description,omitempty" binding:"MaxSize(255)"`
-	// A URL with more information about the repository.
+	// a URL with more information about the repository.
 	Website *string `json:"website,omitempty" binding:"MaxSize(255)"`
-	// Either `true` to make the repository private or `false` to make it public.
-	// Note: You will get a 422 error if the organization restricts changing repository visibility to organization
+	// either `true` to make the repository private or `false` to make it public.
+	// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
 	// owners and a non-owner tries to change the value of private.
 	Private *bool `json:"private,omitempty"`
-	// Either `true` to enable issues for this repository or `false` to disable them.
-	EnableIssues *bool `json:"enable_issues,omitempty"`
-	// Either `true` to enable the wiki for this repository or `false` to disable it.
-	EnableWiki *bool `json:"enable_wiki,omitempty"`
-	// Updates the default branch for this repository.
+	// either `true` to enable issues for this repository or `false` to disable them.
+	HasIssues *bool `json:"has_issues,omitempty"`
+	// either `true` to enable the wiki for this repository or `false` to disable it.
+	HasWiki *bool `json:"has_wiki,omitempty"`
+	// sets the default branch for this repository.
 	DefaultBranch *string `json:"default_branch,omitempty"`
-	// Either `true` to allow pull requests, or `false` to prevent pull request.
-	EnablePullRequests *bool `json:"enable_pull_requests,omitempty"`
-	// Either `true` to ignore whitepace for conflicts, or `false` to not ignore whitespace. `enabled_pull_requests` must be `true`.
-	IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace,omitempty"`
-	// Either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `enabled_pull_requests` must be `true`.
+	// either `true` to allow pull requests, or `false` to prevent pull request.
+	HasPullRequests *bool `json:"has_pull_requests,omitempty"`
+	// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
+	IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
+	// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
 	AllowMerge *bool `json:"allow_merge_commits,omitempty"`
-	// Either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `enabled_pull_requests` must be `true`.
+	// either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
 	AllowRebase *bool `json:"allow_rebase,omitempty"`
-	// Either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `enabled_pull_requests` must be `true`.
+	// either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
 	AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
-	// Either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `enabled_pull_requests` must be `true`.
-	AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
-	// `true` to archive this repository. Note: You cannot unarchive repositories through the API.
+	// either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
+	AllowSquash *bool `json:"allow_squash_merge,omitempty"`
+	// set to `true` to archive this repository.
 	Archived *bool `json:"archived,omitempty"`
 }
 
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index ae64e887ca..c1561200cd 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -608,7 +608,8 @@ func RegisterRoutes(m *macaron.Macaron) {
 
 			m.Group("/:username/:reponame", func() {
 				m.Combo("").Get(reqAnyRepoReader(), repo.Get).
-					Delete(reqToken(), reqOwner(), repo.Delete)
+					Delete(reqToken(), reqOwner(), repo.Delete).
+					Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)
 				m.Group("/hooks", func() {
 					m.Combo("").Get(repo.ListHooks).
 						Post(bind(api.CreateHookOption{}), repo.CreateHook)
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 62153893a6..f8df3e9fa1 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -240,6 +240,10 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
 	// responses:
 	//   "201":
 	//     "$ref": "#/responses/Repository"
+	//   "409":
+	//     description: The repository with the same name already exists.
+	//   "422":
+	//     "$ref": "#/responses/validationError"
 	if ctx.User.IsOrganization() {
 		// Shouldn't reach this condition, but just in case.
 		ctx.Error(422, "", "not allowed creating repository for organization")
@@ -500,6 +504,280 @@ func GetByID(ctx *context.APIContext) {
 	ctx.JSON(200, repo.APIFormat(perm.AccessMode))
 }
 
+// Edit edit repository properties
+func Edit(ctx *context.APIContext, opts api.EditRepoOption) {
+	// swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit
+	// ---
+	// summary: Edit a repository's properties. Only fields that are set will be changed.
+	// produces:
+	// - application/json
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo to edit
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo to edit
+	//   type: string
+	//   required: true
+	//   required: true
+	// - name: body
+	//   in: body
+	//   description: "Properties of a repo that you can edit"
+	//   schema:
+	//     "$ref": "#/definitions/EditRepoOption"
+	// responses:
+	//   "200":
+	//     "$ref": "#/responses/Repository"
+	//   "403":
+	//     "$ref": "#/responses/forbidden"
+	//   "422":
+	//     "$ref": "#/responses/validationError"
+	if err := updateBasicProperties(ctx, opts); err != nil {
+		return
+	}
+
+	if err := updateRepoUnits(ctx, opts); err != nil {
+		return
+	}
+
+	if opts.Archived != nil {
+		if err := updateRepoArchivedState(ctx, opts); err != nil {
+			return
+		}
+	}
+
+	ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
+}
+
+// updateBasicProperties updates the basic properties of a repo: Name, Description, Website and Visibility
+func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) error {
+	owner := ctx.Repo.Owner
+	repo := ctx.Repo.Repository
+
+	oldRepoName := repo.Name
+	newRepoName := repo.Name
+	if opts.Name != nil {
+		newRepoName = *opts.Name
+	}
+	// Check if repository name has been changed and not just a case change
+	if repo.LowerName != strings.ToLower(newRepoName) {
+		if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
+			switch {
+			case models.IsErrRepoAlreadyExist(err):
+				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
+			case models.IsErrNameReserved(err):
+				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
+			case models.IsErrNamePatternNotAllowed(err):
+				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(models.ErrNamePatternNotAllowed).Pattern), err)
+			default:
+				ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
+			}
+			return err
+		}
+
+		err := models.NewRepoRedirect(ctx.Repo.Owner.ID, repo.ID, repo.Name, newRepoName)
+		if err != nil {
+			ctx.Error(http.StatusUnprocessableEntity, "NewRepoRedirect", err)
+			return err
+		}
+
+		if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
+			log.Error("RenameRepoAction: %v", err)
+			ctx.Error(http.StatusInternalServerError, "RenameRepoActions", err)
+			return err
+		}
+
+		log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
+	}
+	// Update the name in the repo object for the response
+	repo.Name = newRepoName
+	repo.LowerName = strings.ToLower(newRepoName)
+
+	if opts.Description != nil {
+		repo.Description = *opts.Description
+	}
+
+	if opts.Website != nil {
+		repo.Website = *opts.Website
+	}
+
+	visibilityChanged := false
+	if opts.Private != nil {
+		// Visibility of forked repository is forced sync with base repository.
+		if repo.IsFork {
+			*opts.Private = repo.BaseRepo.IsPrivate
+		}
+
+		visibilityChanged = repo.IsPrivate != *opts.Private
+		// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
+		if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.User.IsAdmin {
+			err := fmt.Errorf("cannot change private repository to public")
+			ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
+			return err
+		}
+
+		repo.IsPrivate = *opts.Private
+	}
+
+	if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
+		ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
+		return err
+	}
+
+	log.Trace("Repository basic settings updated: %s/%s", owner.Name, repo.Name)
+	return nil
+}
+
+func unitTypeInTypes(unitType models.UnitType, unitTypes []models.UnitType) bool {
+	for _, tp := range unitTypes {
+		if unitType == tp {
+			return true
+		}
+	}
+	return false
+}
+
+// updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
+func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
+	owner := ctx.Repo.Owner
+	repo := ctx.Repo.Repository
+
+	var units []models.RepoUnit
+
+	for _, tp := range models.MustRepoUnits {
+		units = append(units, models.RepoUnit{
+			RepoID: repo.ID,
+			Type:   tp,
+			Config: new(models.UnitConfig),
+		})
+	}
+
+	if opts.HasIssues != nil {
+		if *opts.HasIssues {
+			// We don't currently allow setting individual issue settings through the API,
+			// only can enable/disable issues, so when enabling issues,
+			// we either get the existing config which means it was already enabled,
+			// or create a new config since it doesn't exist.
+			unit, err := repo.GetUnit(models.UnitTypeIssues)
+			var config *models.IssuesConfig
+			if err != nil {
+				// Unit type doesn't exist so we make a new config file with default values
+				config = &models.IssuesConfig{
+					EnableTimetracker:                true,
+					AllowOnlyContributorsToTrackTime: true,
+					EnableDependencies:               true,
+				}
+			} else {
+				config = unit.IssuesConfig()
+			}
+			units = append(units, models.RepoUnit{
+				RepoID: repo.ID,
+				Type:   models.UnitTypeIssues,
+				Config: config,
+			})
+		}
+	}
+
+	if opts.HasWiki != nil {
+		if *opts.HasWiki {
+			// We don't currently allow setting individual wiki settings through the API,
+			// only can enable/disable the wiki, so when enabling the wiki,
+			// we either get the existing config which means it was already enabled,
+			// or create a new config since it doesn't exist.
+			config := &models.UnitConfig{}
+			units = append(units, models.RepoUnit{
+				RepoID: repo.ID,
+				Type:   models.UnitTypeWiki,
+				Config: config,
+			})
+		}
+	}
+
+	if opts.HasPullRequests != nil {
+		if *opts.HasPullRequests {
+			// We do allow setting individual PR settings through the API, so
+			// we get the config settings and then set them
+			// if those settings were provided in the opts.
+			unit, err := repo.GetUnit(models.UnitTypePullRequests)
+			var config *models.PullRequestsConfig
+			if err != nil {
+				// Unit type doesn't exist so we make a new config file with default values
+				config = &models.PullRequestsConfig{
+					IgnoreWhitespaceConflicts: false,
+					AllowMerge:                true,
+					AllowRebase:               true,
+					AllowRebaseMerge:          true,
+					AllowSquash:               true,
+				}
+			} else {
+				config = unit.PullRequestsConfig()
+			}
+
+			if opts.IgnoreWhitespaceConflicts != nil {
+				config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts
+			}
+			if opts.AllowMerge != nil {
+				config.AllowMerge = *opts.AllowMerge
+			}
+			if opts.AllowRebase != nil {
+				config.AllowRebase = *opts.AllowRebase
+			}
+			if opts.AllowRebaseMerge != nil {
+				config.AllowRebaseMerge = *opts.AllowRebaseMerge
+			}
+			if opts.AllowSquash != nil {
+				config.AllowSquash = *opts.AllowSquash
+			}
+
+			units = append(units, models.RepoUnit{
+				RepoID: repo.ID,
+				Type:   models.UnitTypePullRequests,
+				Config: config,
+			})
+		}
+	}
+
+	if err := models.UpdateRepositoryUnits(repo, units); err != nil {
+		ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
+		return err
+	}
+
+	log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name)
+	return nil
+}
+
+// updateRepoArchivedState updates repo's archive state
+func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) error {
+	repo := ctx.Repo.Repository
+	// archive / un-archive
+	if opts.Archived != nil {
+		if repo.IsMirror {
+			err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
+			ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
+			return err
+		}
+		if *opts.Archived {
+			if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
+				log.Error("Tried to archive a repo: %s", err)
+				ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
+				return err
+			}
+			log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+		} else {
+			if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
+				log.Error("Tried to un-archive a repo: %s", err)
+				ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
+				return err
+			}
+			log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+		}
+	}
+	return nil
+}
+
 // Delete one repository
 func Delete(ctx *context.APIContext) {
 	// swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go
new file mode 100644
index 0000000000..053134ec61
--- /dev/null
+++ b/routers/api/v1/repo/repo_test.go
@@ -0,0 +1,82 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+	"net/http"
+	"testing"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/context"
+	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/test"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRepoEdit(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1")
+	test.LoadRepo(t, ctx, 1)
+	test.LoadUser(t, ctx, 2)
+	ctx.Repo.Owner = ctx.User
+	description := "new description"
+	website := "http://wwww.newwebsite.com"
+	private := true
+	hasIssues := false
+	hasWiki := false
+	defaultBranch := "master"
+	hasPullRequests := true
+	ignoreWhitespaceConflicts := true
+	allowMerge := false
+	allowRebase := false
+	allowRebaseMerge := false
+	allowSquashMerge := false
+	archived := true
+	opts := api.EditRepoOption{
+		Name:                      &ctx.Repo.Repository.Name,
+		Description:               &description,
+		Website:                   &website,
+		Private:                   &private,
+		HasIssues:                 &hasIssues,
+		HasWiki:                   &hasWiki,
+		DefaultBranch:             &defaultBranch,
+		HasPullRequests:           &hasPullRequests,
+		IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
+		AllowMerge:                &allowMerge,
+		AllowRebase:               &allowRebase,
+		AllowRebaseMerge:          &allowRebaseMerge,
+		AllowSquash:               &allowSquashMerge,
+		Archived:                  &archived,
+	}
+
+	Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
+
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+	models.AssertExistsAndLoadBean(t, &models.Repository{
+		ID: 1,
+	}, models.Cond("name = ? AND is_archived = 1", *opts.Name))
+}
+
+func TestRepoEditNameChange(t *testing.T) {
+	models.PrepareTestEnv(t)
+
+	ctx := test.MockContext(t, "user2/repo1")
+	test.LoadRepo(t, ctx, 1)
+	test.LoadUser(t, ctx, 2)
+	ctx.Repo.Owner = ctx.User
+	name := "newname"
+	opts := api.EditRepoOption{
+		Name: &name,
+	}
+
+	Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
+	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
+
+	models.AssertExistsAndLoadBean(t, &models.Repository{
+		ID: 1,
+	}, models.Cond("name = ?", opts.Name))
+}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 2df97304aa..c1196eeb71 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -82,6 +82,8 @@ type swaggerParameterBodies struct {
 	// in:body
 	CreateRepoOption api.CreateRepoOption
 	// in:body
+	EditRepoOption api.EditRepoOption
+	// in:body
 	CreateForkOption api.CreateForkOption
 
 	// in:body
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 7307d1284b..a3090d1d52 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -1210,6 +1210,51 @@
             "$ref": "#/responses/forbidden"
           }
         }
+      },
+      "patch": {
+        "produces": [
+          "application/json"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Edit a repository's properties. Only fields that are set will be changed.",
+        "operationId": "repoEdit",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo to edit",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo to edit",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          },
+          {
+            "description": "Properties of a repo that you can edit",
+            "name": "body",
+            "in": "body",
+            "schema": {
+              "$ref": "#/definitions/EditRepoOption"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "$ref": "#/responses/Repository"
+          },
+          "403": {
+            "$ref": "#/responses/forbidden"
+          },
+          "422": {
+            "$ref": "#/responses/validationError"
+          }
+        }
       }
     },
     "/repos/{owner}/{repo}/archive/{archive}": {
@@ -6037,6 +6082,12 @@
         "responses": {
           "201": {
             "$ref": "#/responses/Repository"
+          },
+          "409": {
+            "description": "The repository with the same name already exists."
+          },
+          "422": {
+            "$ref": "#/responses/validationError"
           }
         }
       }
@@ -7738,6 +7789,84 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "EditRepoOption": {
+      "description": "EditRepoOption options when editing a repository's properties",
+      "type": "object",
+      "properties": {
+        "allow_merge_commits": {
+          "description": "either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.",
+          "type": "boolean",
+          "x-go-name": "AllowMerge"
+        },
+        "allow_rebase": {
+          "description": "either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.",
+          "type": "boolean",
+          "x-go-name": "AllowRebase"
+        },
+        "allow_rebase_explicit": {
+          "description": "either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.",
+          "type": "boolean",
+          "x-go-name": "AllowRebaseMerge"
+        },
+        "allow_squash_merge": {
+          "description": "either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.",
+          "type": "boolean",
+          "x-go-name": "AllowSquash"
+        },
+        "archived": {
+          "description": "set to `true` to archive this repository.",
+          "type": "boolean",
+          "x-go-name": "Archived"
+        },
+        "default_branch": {
+          "description": "sets the default branch for this repository.",
+          "type": "string",
+          "x-go-name": "DefaultBranch"
+        },
+        "description": {
+          "description": "a short description of the repository.",
+          "type": "string",
+          "x-go-name": "Description"
+        },
+        "has_issues": {
+          "description": "either `true` to enable issues for this repository or `false` to disable them.",
+          "type": "boolean",
+          "x-go-name": "HasIssues"
+        },
+        "has_pull_requests": {
+          "description": "either `true` to allow pull requests, or `false` to prevent pull request.",
+          "type": "boolean",
+          "x-go-name": "HasPullRequests"
+        },
+        "has_wiki": {
+          "description": "either `true` to enable the wiki for this repository or `false` to disable it.",
+          "type": "boolean",
+          "x-go-name": "HasWiki"
+        },
+        "ignore_whitespace_conflicts": {
+          "description": "either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.",
+          "type": "boolean",
+          "x-go-name": "IgnoreWhitespaceConflicts"
+        },
+        "name": {
+          "description": "name of the repository",
+          "type": "string",
+          "uniqueItems": true,
+          "x-go-name": "Name"
+        },
+        "private": {
+          "description": "either `true` to make the repository private or `false` to make it public.\nNote: you will get a 422 error if the organization restricts changing repository visibility to organization\nowners and a non-owner tries to change the value of private.",
+          "type": "boolean",
+          "x-go-name": "Private"
+        },
+        "website": {
+          "description": "a URL with more information about the repository.",
+          "type": "string",
+          "x-go-name": "Website"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "EditTeamOption": {
       "description": "EditTeamOption options for editing a team",
       "type": "object",
@@ -9062,6 +9191,22 @@
       "description": "Repository represents a repository",
       "type": "object",
       "properties": {
+        "allow_merge_commits": {
+          "type": "boolean",
+          "x-go-name": "AllowMerge"
+        },
+        "allow_rebase": {
+          "type": "boolean",
+          "x-go-name": "AllowRebase"
+        },
+        "allow_rebase_explicit": {
+          "type": "boolean",
+          "x-go-name": "AllowRebaseMerge"
+        },
+        "allow_squash_merge": {
+          "type": "boolean",
+          "x-go-name": "AllowSquash"
+        },
         "archived": {
           "type": "boolean",
           "x-go-name": "Archived"
@@ -9104,6 +9249,18 @@
           "type": "string",
           "x-go-name": "FullName"
         },
+        "has_issues": {
+          "type": "boolean",
+          "x-go-name": "HasIssues"
+        },
+        "has_pull_requests": {
+          "type": "boolean",
+          "x-go-name": "HasPullRequests"
+        },
+        "has_wiki": {
+          "type": "boolean",
+          "x-go-name": "HasWiki"
+        },
         "html_url": {
           "type": "string",
           "x-go-name": "HTMLURL"
@@ -9113,6 +9270,10 @@
           "format": "int64",
           "x-go-name": "ID"
         },
+        "ignore_whitespace_conflicts": {
+          "type": "boolean",
+          "x-go-name": "IgnoreWhitespaceConflicts"
+        },
         "mirror": {
           "type": "boolean",
           "x-go-name": "Mirror"