diff --git a/cmd/serv.go b/cmd/serv.go
index 21a69b24d6..ca0354d06c 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -234,19 +234,20 @@ func runServ(c *cli.Context) error {
 
 		// Check deploy key or user key.
 		if key.Type == models.KeyTypeDeploy {
-			if key.Mode < requestedMode {
-				fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
-			}
-
-			// Check if this deploy key belongs to current repository.
-			has, err := private.HasDeployKey(key.ID, repo.ID)
+			// Now we have to get the deploy key for this repo
+			deployKey, err := private.GetDeployKey(key.ID, repo.ID)
 			if err != nil {
 				fail("Key access denied", "Failed to access internal api: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
 			}
-			if !has {
+
+			if deployKey == nil {
 				fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
 			}
 
+			if deployKey.Mode < requestedMode {
+				fail("Key permission denied", "Cannot push with read-only deployment key: %d to repo_id: %d", key.ID, repo.ID)
+			}
+
 			// Update deploy key activity.
 			if err = private.UpdateDeployKeyUpdated(key.ID, repo.ID); err != nil {
 				fail("Internal error", "UpdateDeployKey: %v", err)
diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go
new file mode 100644
index 0000000000..32a4ce8047
--- /dev/null
+++ b/integrations/api_helper_for_declarative_test.go
@@ -0,0 +1,152 @@
+// 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"
+	"io/ioutil"
+	"net/http"
+	"testing"
+
+	api "code.gitea.io/sdk/gitea"
+	"github.com/stretchr/testify/assert"
+)
+
+type APITestContext struct {
+	Reponame     string
+	Session      *TestSession
+	Token        string
+	Username     string
+	ExpectedCode int
+}
+
+func NewAPITestContext(t *testing.T, username, reponame string) APITestContext {
+	session := loginUser(t, username)
+	token := getTokenForLoggedInUser(t, session)
+	return APITestContext{
+		Session:  session,
+		Token:    token,
+		Username: username,
+		Reponame: reponame,
+	}
+}
+
+func (ctx APITestContext) GitPath() string {
+	return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame)
+}
+
+func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
+	return func(t *testing.T) {
+		createRepoOption := &api.CreateRepoOption{
+			AutoInit:    !empty,
+			Description: "Temporary repo",
+			Name:        ctx.Reponame,
+			Private:     true,
+			Gitignores:  "",
+			License:     "WTFPL",
+			Readme:      "Default",
+		}
+		req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+ctx.Token, createRepoOption)
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
+
+		var repository api.Repository
+		DecodeJSON(t, resp, &repository)
+		if len(callback) > 0 {
+			callback[0](t, repository)
+		}
+	}
+}
+
+func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
+	return func(t *testing.T) {
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
+
+		req := NewRequest(t, "GET", urlStr)
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
+
+		var repository api.Repository
+		DecodeJSON(t, resp, &repository)
+		if len(callback) > 0 {
+			callback[0](t, repository)
+		}
+	}
+}
+
+func doAPIDeleteRepository(ctx APITestContext) func(*testing.T) {
+	return func(t *testing.T) {
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
+
+		req := NewRequest(t, "DELETE", urlStr)
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
+	}
+}
+
+func doAPICreateUserKey(ctx APITestContext, keyname, keyFile string, callback ...func(*testing.T, api.PublicKey)) func(*testing.T) {
+	return func(t *testing.T) {
+		urlStr := fmt.Sprintf("/api/v1/user/keys?token=%s", ctx.Token)
+
+		dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
+		assert.NoError(t, err)
+		req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateKeyOption{
+			Title: keyname,
+			Key:   string(dataPubKey),
+		})
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
+		var publicKey api.PublicKey
+		DecodeJSON(t, resp, &publicKey)
+		if len(callback) > 0 {
+			callback[0](t, publicKey)
+		}
+	}
+}
+
+func doAPIDeleteUserKey(ctx APITestContext, keyID int64) func(*testing.T) {
+	return func(t *testing.T) {
+		urlStr := fmt.Sprintf("/api/v1/user/keys/%d?token=%s", keyID, ctx.Token)
+
+		req := NewRequest(t, "DELETE", urlStr)
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
+	}
+}
+
+func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly bool) func(*testing.T) {
+	return func(t *testing.T) {
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
+
+		dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
+		assert.NoError(t, err)
+		req := NewRequestWithJSON(t, "POST", urlStr, api.CreateKeyOption{
+			Title:    keyname,
+			Key:      string(dataPubKey),
+			ReadOnly: readOnly,
+		})
+
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		ctx.Session.MakeRequest(t, req, http.StatusCreated)
+	}
+}
diff --git a/integrations/deploy_key_push_test.go b/integrations/deploy_key_push_test.go
deleted file mode 100644
index 8b3d665629..0000000000
--- a/integrations/deploy_key_push_test.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// 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"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"net/url"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"testing"
-	"time"
-
-	"code.gitea.io/git"
-
-	"code.gitea.io/gitea/modules/setting"
-	api "code.gitea.io/sdk/gitea"
-	"github.com/stretchr/testify/assert"
-)
-
-func createEmptyRepository(username, reponame string) func(*testing.T) {
-	return func(t *testing.T) {
-		session := loginUser(t, username)
-		token := getTokenForLoggedInUser(t, session)
-		req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+token, &api.CreateRepoOption{
-			AutoInit:    false,
-			Description: "Temporary empty repo",
-			Name:        reponame,
-			Private:     false,
-		})
-		session.MakeRequest(t, req, http.StatusCreated)
-	}
-}
-
-func createDeployKey(username, reponame, keyname, keyFile string, readOnly bool) func(*testing.T) {
-	return func(t *testing.T) {
-		session := loginUser(t, username)
-		token := getTokenForLoggedInUser(t, session)
-		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", username, reponame, token)
-
-		dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
-		assert.NoError(t, err)
-		req := NewRequestWithJSON(t, "POST", urlStr, api.CreateKeyOption{
-			Title:    keyname,
-			Key:      string(dataPubKey),
-			ReadOnly: readOnly,
-		})
-		session.MakeRequest(t, req, http.StatusCreated)
-	}
-}
-
-func initTestRepository(dstPath string) func(*testing.T) {
-	return func(t *testing.T) {
-		// Init repository in dstPath
-		assert.NoError(t, git.InitRepository(dstPath, false))
-		assert.NoError(t, ioutil.WriteFile(filepath.Join(dstPath, "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", dstPath)), 0644))
-		assert.NoError(t, git.AddChanges(dstPath, true))
-		signature := git.Signature{
-			Email: "test@example.com",
-			Name:  "test",
-			When:  time.Now(),
-		}
-		assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
-			Committer: &signature,
-			Author:    &signature,
-			Message:   "Initial Commit",
-		}))
-	}
-}
-
-func pushTestRepository(dstPath, username, reponame string, u url.URL, keyFile string) func(*testing.T) {
-	return func(t *testing.T) {
-		//Setup remote link
-		u.Scheme = "ssh"
-		u.User = url.User("git")
-		u.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
-
-		//Setup ssh wrapper
-		os.Setenv("GIT_SSH_COMMAND",
-			"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
-				filepath.Join(setting.AppWorkPath, keyFile))
-		os.Setenv("GIT_SSH_VARIANT", "ssh")
-
-		log.Printf("Adding remote: %s\n", u.String())
-		_, err := git.NewCommand("remote", "add", "origin", u.String()).RunInDir(dstPath)
-		assert.NoError(t, err)
-
-		log.Printf("Pushing to: %s\n", u.String())
-		_, err = git.NewCommand("push", "-u", "origin", "master").RunInDir(dstPath)
-		assert.NoError(t, err)
-	}
-}
-
-func checkRepositoryEmptyStatus(username, reponame string, isEmpty bool) func(*testing.T) {
-	return func(t *testing.T) {
-		session := loginUser(t, username)
-		token := getTokenForLoggedInUser(t, session)
-		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", username, reponame, token)
-
-		req := NewRequest(t, "GET", urlStr)
-		resp := session.MakeRequest(t, req, http.StatusOK)
-
-		var repository api.Repository
-		DecodeJSON(t, resp, &repository)
-
-		assert.Equal(t, isEmpty, repository.Empty)
-	}
-}
-
-func deleteRepository(username, reponame string) func(*testing.T) {
-	return func(t *testing.T) {
-		session := loginUser(t, username)
-		token := getTokenForLoggedInUser(t, session)
-		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", username, reponame, token)
-
-		req := NewRequest(t, "DELETE", urlStr)
-		session.MakeRequest(t, req, http.StatusNoContent)
-	}
-}
-
-func TestPushDeployKeyOnEmptyRepo(t *testing.T) {
-	onGiteaRun(t, testPushDeployKeyOnEmptyRepo)
-}
-
-func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) {
-	reponame := "deploy-key-empty-repo-1"
-	username := "user2"
-	u.Path = fmt.Sprintf("%s/%s.git", username, reponame)
-	keyname := fmt.Sprintf("%s-push", reponame)
-
-	t.Run("CreateEmptyRepository", createEmptyRepository(username, reponame))
-	t.Run("CheckIsEmpty", checkRepositoryEmptyStatus(username, reponame, true))
-
-	//Setup the push deploy key file
-	keyFile := filepath.Join(setting.AppDataPath, keyname)
-	err := exec.Command("ssh-keygen", "-f", keyFile, "-t", "rsa", "-N", "").Run()
-	assert.NoError(t, err)
-	defer os.RemoveAll(keyFile)
-	defer os.RemoveAll(keyFile + ".pub")
-
-	t.Run("CreatePushDeployKey", createDeployKey(username, reponame, keyname, keyFile, false))
-
-	// Setup the testing repository
-	dstPath, err := ioutil.TempDir("", "repo-tmp-deploy-key-empty-repo-1")
-	assert.NoError(t, err)
-	defer os.RemoveAll(dstPath)
-
-	t.Run("InitTestRepository", initTestRepository(dstPath))
-	t.Run("SSHPushTestRepository", pushTestRepository(dstPath, username, reponame, *u, keyFile))
-
-	log.Println("Done Push")
-	t.Run("CheckIsNotEmpty", checkRepositoryEmptyStatus(username, reponame, false))
-
-	t.Run("DeleteRepository", deleteRepository(username, reponame))
-}
diff --git a/integrations/git_helper_for_declarative_test.go b/integrations/git_helper_for_declarative_test.go
new file mode 100644
index 0000000000..572abe95a2
--- /dev/null
+++ b/integrations/git_helper_for_declarative_test.go
@@ -0,0 +1,127 @@
+// 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 (
+	"context"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"code.gitea.io/git"
+	"code.gitea.io/gitea/modules/setting"
+	"github.com/Unknwon/com"
+	"github.com/stretchr/testify/assert"
+)
+
+func withKeyFile(t *testing.T, keyname string, callback func(string)) {
+	keyFile := filepath.Join(setting.AppDataPath, keyname)
+	err := exec.Command("ssh-keygen", "-f", keyFile, "-t", "rsa", "-N", "").Run()
+	assert.NoError(t, err)
+
+	//Setup ssh wrapper
+	os.Setenv("GIT_SSH_COMMAND",
+		"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
+			filepath.Join(setting.AppWorkPath, keyFile))
+	os.Setenv("GIT_SSH_VARIANT", "ssh")
+
+	callback(keyFile)
+
+	defer os.RemoveAll(keyFile)
+	defer os.RemoveAll(keyFile + ".pub")
+}
+
+func createSSHUrl(gitPath string, u *url.URL) *url.URL {
+	u2 := *u
+	u2.Scheme = "ssh"
+	u2.User = url.User("git")
+	u2.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
+	u2.Path = gitPath
+	return &u2
+}
+
+func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
+	prepareTestEnv(t)
+	s := http.Server{
+		Handler: mac,
+	}
+
+	u, err := url.Parse(setting.AppURL)
+	assert.NoError(t, err)
+	listener, err := net.Listen("tcp", u.Host)
+	assert.NoError(t, err)
+
+	defer func() {
+		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+		s.Shutdown(ctx)
+		cancel()
+	}()
+
+	go s.Serve(listener)
+	//Started by config go ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
+
+	callback(t, u)
+}
+
+func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
+	return func(t *testing.T) {
+		assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
+		assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
+	}
+}
+
+func doGitCloneFail(dstLocalPath string, u *url.URL) func(*testing.T) {
+	return func(t *testing.T) {
+		assert.Error(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
+		assert.False(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
+	}
+}
+
+func doGitInitTestRepository(dstPath string) func(*testing.T) {
+	return func(t *testing.T) {
+		// Init repository in dstPath
+		assert.NoError(t, git.InitRepository(dstPath, false))
+		assert.NoError(t, ioutil.WriteFile(filepath.Join(dstPath, "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", dstPath)), 0644))
+		assert.NoError(t, git.AddChanges(dstPath, true))
+		signature := git.Signature{
+			Email: "test@example.com",
+			Name:  "test",
+			When:  time.Now(),
+		}
+		assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
+			Committer: &signature,
+			Author:    &signature,
+			Message:   "Initial Commit",
+		}))
+	}
+}
+
+func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand("remote", "add", remoteName, u.String()).RunInDir(dstPath)
+		assert.NoError(t, err)
+	}
+}
+
+func doGitPushTestRepository(dstPath, remoteName, branch string) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
+		assert.NoError(t, err)
+	}
+}
+
+func doGitPushTestRepositoryFail(dstPath, remoteName, branch string) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
+		assert.Error(t, err)
+	}
+}
diff --git a/integrations/git_test.go b/integrations/git_test.go
index 96d39e0519..a372525864 100644
--- a/integrations/git_test.go
+++ b/integrations/git_test.go
@@ -5,25 +5,17 @@
 package integrations
 
 import (
-	"context"
 	"crypto/rand"
 	"fmt"
 	"io/ioutil"
-	"net"
-	"net/http"
 	"net/url"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"testing"
 	"time"
 
 	"code.gitea.io/git"
-	"code.gitea.io/gitea/models"
-	"code.gitea.io/gitea/modules/setting"
-	api "code.gitea.io/sdk/gitea"
 
-	"github.com/Unknwon/com"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -32,160 +24,86 @@ const (
 	bigSize    = 128 * 1024 * 1024 //128Mo
 )
 
-func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
-	prepareTestEnv(t)
-	s := http.Server{
-		Handler: mac,
-	}
-
-	u, err := url.Parse(setting.AppURL)
-	assert.NoError(t, err)
-	listener, err := net.Listen("tcp", u.Host)
-	assert.NoError(t, err)
-
-	defer func() {
-		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
-		s.Shutdown(ctx)
-		cancel()
-	}()
-
-	go s.Serve(listener)
-	//Started by config go ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
-
-	callback(t, u)
+func TestGit(t *testing.T) {
+	onGiteaRun(t, testGit)
 }
 
-func TestGit(t *testing.T) {
-	onGiteaRun(t, func(t *testing.T, u *url.URL) {
-		u.Path = "user2/repo1.git"
+func testGit(t *testing.T, u *url.URL) {
+	username := "user2"
+	baseAPITestContext := NewAPITestContext(t, username, "repo1")
 
-		t.Run("HTTP", func(t *testing.T) {
-			dstPath, err := ioutil.TempDir("", "repo-tmp-17")
-			assert.NoError(t, err)
-			defer os.RemoveAll(dstPath)
-			t.Run("Standard", func(t *testing.T) {
-				t.Run("CloneNoLogin", func(t *testing.T) {
-					dstLocalPath, err := ioutil.TempDir("", "repo1")
-					assert.NoError(t, err)
-					defer os.RemoveAll(dstLocalPath)
-					err = git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{})
-					assert.NoError(t, err)
-					assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
-				})
+	u.Path = baseAPITestContext.GitPath()
 
-				t.Run("CreateRepo", func(t *testing.T) {
-					session := loginUser(t, "user2")
-					token := getTokenForLoggedInUser(t, session)
-					req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+token, &api.CreateRepoOption{
-						AutoInit:    true,
-						Description: "Temporary repo",
-						Name:        "repo-tmp-17",
-						Private:     false,
-						Gitignores:  "",
-						License:     "WTFPL",
-						Readme:      "Default",
-					})
-					session.MakeRequest(t, req, http.StatusCreated)
-				})
+	t.Run("HTTP", func(t *testing.T) {
+		httpContext := baseAPITestContext
+		httpContext.Reponame = "repo-tmp-17"
 
-				u.Path = "user2/repo-tmp-17.git"
-				u.User = url.UserPassword("user2", userPassword)
-				t.Run("Clone", func(t *testing.T) {
-					err = git.Clone(u.String(), dstPath, git.CloneRepoOptions{})
-					assert.NoError(t, err)
-					assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
-				})
+		dstPath, err := ioutil.TempDir("", httpContext.Reponame)
+		assert.NoError(t, err)
+		defer os.RemoveAll(dstPath)
+		t.Run("Standard", func(t *testing.T) {
+			ensureAnonymousClone(t, u)
 
-				t.Run("PushCommit", func(t *testing.T) {
-					t.Run("Little", func(t *testing.T) {
-						commitAndPush(t, littleSize, dstPath)
-					})
-					t.Run("Big", func(t *testing.T) {
-						commitAndPush(t, bigSize, dstPath)
-					})
-				})
-			})
-			t.Run("LFS", func(t *testing.T) {
-				t.Run("PushCommit", func(t *testing.T) {
-					//Setup git LFS
-					_, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
-					assert.NoError(t, err)
-					_, err = git.NewCommand("lfs").AddArguments("track", "data-file-*").RunInDir(dstPath)
-					assert.NoError(t, err)
-					err = git.AddChanges(dstPath, false, ".gitattributes")
-					assert.NoError(t, err)
+			t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
 
-					t.Run("Little", func(t *testing.T) {
-						commitAndPush(t, littleSize, dstPath)
-					})
-					t.Run("Big", func(t *testing.T) {
-						commitAndPush(t, bigSize, dstPath)
-					})
+			u.Path = httpContext.GitPath()
+			u.User = url.UserPassword(username, userPassword)
+
+			t.Run("Clone", doGitClone(dstPath, u))
+
+			t.Run("PushCommit", func(t *testing.T) {
+				t.Run("Little", func(t *testing.T) {
+					commitAndPush(t, littleSize, dstPath)
 				})
-				t.Run("Locks", func(t *testing.T) {
-					lockTest(t, u.String(), dstPath)
+				t.Run("Big", func(t *testing.T) {
+					commitAndPush(t, bigSize, dstPath)
 				})
 			})
 		})
-		t.Run("SSH", func(t *testing.T) {
-			//Setup remote link
-			u.Scheme = "ssh"
-			u.User = url.User("git")
-			u.Host = fmt.Sprintf("%s:%d", setting.SSH.ListenHost, setting.SSH.ListenPort)
-			u.Path = "user2/repo-tmp-18.git"
+		t.Run("LFS", func(t *testing.T) {
+			t.Run("PushCommit", func(t *testing.T) {
+				//Setup git LFS
+				_, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
+				assert.NoError(t, err)
+				_, err = git.NewCommand("lfs").AddArguments("track", "data-file-*").RunInDir(dstPath)
+				assert.NoError(t, err)
+				err = git.AddChanges(dstPath, false, ".gitattributes")
+				assert.NoError(t, err)
 
-			//Setup key
-			keyFile := filepath.Join(setting.AppDataPath, "my-testing-key")
-			err := exec.Command("ssh-keygen", "-f", keyFile, "-t", "rsa", "-N", "").Run()
-			assert.NoError(t, err)
-			defer os.RemoveAll(keyFile)
-			defer os.RemoveAll(keyFile + ".pub")
-
-			session := loginUser(t, "user1")
-			keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
-			token := getTokenForLoggedInUser(t, session)
-			urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token)
-
-			dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
-			assert.NoError(t, err)
-			req := NewRequestWithValues(t, "POST", urlStr, map[string]string{
-				"key":   string(dataPubKey),
-				"title": "test-key",
+				t.Run("Little", func(t *testing.T) {
+					commitAndPush(t, littleSize, dstPath)
+				})
+				t.Run("Big", func(t *testing.T) {
+					commitAndPush(t, bigSize, dstPath)
+				})
 			})
-			session.MakeRequest(t, req, http.StatusCreated)
+			t.Run("Locks", func(t *testing.T) {
+				lockTest(t, u.String(), dstPath)
+			})
+		})
+	})
+	t.Run("SSH", func(t *testing.T) {
+		sshContext := baseAPITestContext
+		sshContext.Reponame = "repo-tmp-18"
+		keyname := "my-testing-key"
+		//Setup key the user ssh key
+		withKeyFile(t, keyname, func(keyFile string) {
+			t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
 
-			//Setup ssh wrapper
-			os.Setenv("GIT_SSH_COMMAND",
-				"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "+
-					filepath.Join(setting.AppWorkPath, keyFile))
-			os.Setenv("GIT_SSH_VARIANT", "ssh")
+			//Setup remote link
+			sshURL := createSSHUrl(sshContext.GitPath(), u)
 
 			//Setup clone folder
-			dstPath, err := ioutil.TempDir("", "repo-tmp-18")
+			dstPath, err := ioutil.TempDir("", sshContext.Reponame)
 			assert.NoError(t, err)
 			defer os.RemoveAll(dstPath)
 
 			t.Run("Standard", func(t *testing.T) {
-				t.Run("CreateRepo", func(t *testing.T) {
-					session := loginUser(t, "user2")
-					token := getTokenForLoggedInUser(t, session)
-					req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+token, &api.CreateRepoOption{
-						AutoInit:    true,
-						Description: "Temporary repo",
-						Name:        "repo-tmp-18",
-						Private:     false,
-						Gitignores:  "",
-						License:     "WTFPL",
-						Readme:      "Default",
-					})
-					session.MakeRequest(t, req, http.StatusCreated)
-				})
+				t.Run("CreateRepo", doAPICreateRepository(sshContext, false))
+
 				//TODO get url from api
-				t.Run("Clone", func(t *testing.T) {
-					_, err = git.NewCommand("clone").AddArguments(u.String(), dstPath).Run()
-					assert.NoError(t, err)
-					assert.True(t, com.IsExist(filepath.Join(dstPath, "README.md")))
-				})
+				t.Run("Clone", doGitClone(dstPath, sshURL))
+
 				//time.Sleep(5 * time.Minute)
 				t.Run("PushCommit", func(t *testing.T) {
 					t.Run("Little", func(t *testing.T) {
@@ -217,10 +135,20 @@ func TestGit(t *testing.T) {
 					lockTest(t, u.String(), dstPath)
 				})
 			})
+
 		})
+
 	})
 }
 
+func ensureAnonymousClone(t *testing.T, u *url.URL) {
+	dstLocalPath, err := ioutil.TempDir("", "repo1")
+	assert.NoError(t, err)
+	defer os.RemoveAll(dstLocalPath)
+	t.Run("CloneAnonymous", doGitClone(dstLocalPath, u))
+
+}
+
 func lockTest(t *testing.T, remote, repoPath string) {
 	_, err := git.NewCommand("remote").AddArguments("set-url", "origin", remote).RunInDir(repoPath) //TODO add test ssh git-lfs-creds
 	assert.NoError(t, err)
diff --git a/integrations/ssh_key_test.go b/integrations/ssh_key_test.go
new file mode 100644
index 0000000000..9ad43ae226
--- /dev/null
+++ b/integrations/ssh_key_test.go
@@ -0,0 +1,217 @@
+// 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"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"code.gitea.io/git"
+	api "code.gitea.io/sdk/gitea"
+	"github.com/stretchr/testify/assert"
+)
+
+func doCheckRepositoryEmptyStatus(ctx APITestContext, isEmpty bool) func(*testing.T) {
+	return doAPIGetRepository(ctx, func(t *testing.T, repository api.Repository) {
+		assert.Equal(t, isEmpty, repository.Empty)
+	})
+}
+
+func doAddChangesToCheckout(dstPath, filename string) func(*testing.T) {
+	return func(t *testing.T) {
+		assert.NoError(t, ioutil.WriteFile(filepath.Join(dstPath, filename), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s at time: %v", dstPath, time.Now())), 0644))
+		assert.NoError(t, git.AddChanges(dstPath, true))
+		signature := git.Signature{
+			Email: "test@example.com",
+			Name:  "test",
+			When:  time.Now(),
+		}
+		assert.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
+			Committer: &signature,
+			Author:    &signature,
+			Message:   "Initial Commit",
+		}))
+	}
+}
+
+func TestPushDeployKeyOnEmptyRepo(t *testing.T) {
+	onGiteaRun(t, testPushDeployKeyOnEmptyRepo)
+}
+
+func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) {
+	// OK login
+	ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1")
+	keyname := fmt.Sprintf("%s-push", ctx.Reponame)
+	u.Path = ctx.GitPath()
+
+	t.Run("CreateEmptyRepository", doAPICreateRepository(ctx, true))
+
+	t.Run("CheckIsEmpty", doCheckRepositoryEmptyStatus(ctx, true))
+
+	withKeyFile(t, keyname, func(keyFile string) {
+		t.Run("CreatePushDeployKey", doAPICreateDeployKey(ctx, keyname, keyFile, false))
+
+		// Setup the testing repository
+		dstPath, err := ioutil.TempDir("", "repo-tmp-deploy-key-empty-repo-1")
+		assert.NoError(t, err)
+		defer os.RemoveAll(dstPath)
+
+		t.Run("InitTestRepository", doGitInitTestRepository(dstPath))
+
+		//Setup remote link
+		sshURL := createSSHUrl(ctx.GitPath(), u)
+
+		t.Run("AddRemote", doGitAddRemote(dstPath, "origin", sshURL))
+
+		t.Run("SSHPushTestRepository", doGitPushTestRepository(dstPath, "origin", "master"))
+
+		t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false))
+
+		t.Run("DeleteRepository", doAPIDeleteRepository(ctx))
+	})
+}
+
+func TestKeyOnlyOneType(t *testing.T) {
+	onGiteaRun(t, testKeyOnlyOneType)
+}
+
+func testKeyOnlyOneType(t *testing.T, u *url.URL) {
+	// Once a key is a user key we cannot use it as a deploy key
+	// If we delete it from the user we should be able to use it as a deploy key
+	reponame := "ssh-key-test-repo"
+	username := "user2"
+	u.Path = fmt.Sprintf("%s/%s.git", username, reponame)
+	keyname := fmt.Sprintf("%s-push", reponame)
+
+	// OK login
+	ctx := NewAPITestContext(t, username, reponame)
+
+	otherCtx := ctx
+	otherCtx.Reponame = "ssh-key-test-repo-2"
+
+	failCtx := ctx
+	failCtx.ExpectedCode = http.StatusUnprocessableEntity
+
+	t.Run("CreateRepository", doAPICreateRepository(ctx, false))
+	t.Run("CreateOtherRepository", doAPICreateRepository(otherCtx, false))
+
+	withKeyFile(t, keyname, func(keyFile string) {
+		var userKeyPublicKeyID int64
+		t.Run("KeyCanOnlyBeUser", func(t *testing.T) {
+			dstPath, err := ioutil.TempDir("", ctx.Reponame)
+			assert.NoError(t, err)
+			defer os.RemoveAll(dstPath)
+
+			sshURL := createSSHUrl(ctx.GitPath(), u)
+
+			t.Run("FailToClone", doGitCloneFail(dstPath, sshURL))
+
+			t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) {
+				userKeyPublicKeyID = publicKey.ID
+			}))
+
+			t.Run("FailToAddReadOnlyDeployKey", doAPICreateDeployKey(failCtx, keyname, keyFile, true))
+
+			t.Run("FailToAddDeployKey", doAPICreateDeployKey(failCtx, keyname, keyFile, false))
+
+			t.Run("Clone", doGitClone(dstPath, sshURL))
+
+			t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES1.md"))
+
+			t.Run("Push", doGitPushTestRepository(dstPath, "origin", "master"))
+
+			t.Run("DeleteUserKey", doAPIDeleteUserKey(ctx, userKeyPublicKeyID))
+		})
+
+		t.Run("KeyCanBeAnyDeployButNotUserAswell", func(t *testing.T) {
+			dstPath, err := ioutil.TempDir("", ctx.Reponame)
+			assert.NoError(t, err)
+			defer os.RemoveAll(dstPath)
+
+			sshURL := createSSHUrl(ctx.GitPath(), u)
+
+			t.Run("FailToClone", doGitCloneFail(dstPath, sshURL))
+
+			// Should now be able to add...
+			t.Run("AddReadOnlyDeployKey", doAPICreateDeployKey(ctx, keyname, keyFile, true))
+
+			t.Run("Clone", doGitClone(dstPath, sshURL))
+
+			t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES2.md"))
+
+			t.Run("FailToPush", doGitPushTestRepositoryFail(dstPath, "origin", "master"))
+
+			otherSSHURL := createSSHUrl(otherCtx.GitPath(), u)
+			dstOtherPath, err := ioutil.TempDir("", otherCtx.Reponame)
+			assert.NoError(t, err)
+			defer os.RemoveAll(dstOtherPath)
+
+			t.Run("AddWriterDeployKeyToOther", doAPICreateDeployKey(otherCtx, keyname, keyFile, false))
+
+			t.Run("CloneOther", doGitClone(dstOtherPath, otherSSHURL))
+
+			t.Run("AddChangesToOther", doAddChangesToCheckout(dstOtherPath, "CHANGES3.md"))
+
+			t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master"))
+
+			t.Run("FailToCreateUserKey", doAPICreateUserKey(failCtx, keyname, keyFile))
+		})
+
+		t.Run("DeleteRepositoryShouldReleaseKey", func(t *testing.T) {
+			otherSSHURL := createSSHUrl(otherCtx.GitPath(), u)
+			dstOtherPath, err := ioutil.TempDir("", otherCtx.Reponame)
+			assert.NoError(t, err)
+			defer os.RemoveAll(dstOtherPath)
+
+			t.Run("DeleteRepository", doAPIDeleteRepository(ctx))
+
+			t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile))
+
+			t.Run("MakeSureCloneOtherStillWorks", doGitClone(dstOtherPath, otherSSHURL))
+
+			t.Run("AddChangesToOther", doAddChangesToCheckout(dstOtherPath, "CHANGES3.md"))
+
+			t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master"))
+
+			t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtx))
+
+			t.Run("RecreateRepository", doAPICreateRepository(ctx, false))
+
+			t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) {
+				userKeyPublicKeyID = publicKey.ID
+			}))
+
+			dstPath, err := ioutil.TempDir("", ctx.Reponame)
+			assert.NoError(t, err)
+			defer os.RemoveAll(dstPath)
+
+			sshURL := createSSHUrl(ctx.GitPath(), u)
+
+			t.Run("Clone", doGitClone(dstPath, sshURL))
+
+			t.Run("AddChanges", doAddChangesToCheckout(dstPath, "CHANGES1.md"))
+
+			t.Run("Push", doGitPushTestRepository(dstPath, "origin", "master"))
+		})
+
+		t.Run("DeleteUserKeyShouldRemoveAbilityToClone", func(t *testing.T) {
+			dstPath, err := ioutil.TempDir("", ctx.Reponame)
+			assert.NoError(t, err)
+			defer os.RemoveAll(dstPath)
+
+			sshURL := createSSHUrl(ctx.GitPath(), u)
+
+			t.Run("DeleteUserKey", doAPIDeleteUserKey(ctx, userKeyPublicKeyID))
+
+			t.Run("FailToClone", doGitCloneFail(dstPath, sshURL))
+		})
+	})
+}
diff --git a/models/repo.go b/models/repo.go
index 5e96a4e931..873fd407fb 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1756,6 +1756,17 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 		return ErrRepoNotExist{repoID, uid, "", ""}
 	}
 
+	// Delete Deploy Keys
+	deployKeys, err := listDeployKeys(sess, repo.ID)
+	if err != nil {
+		return fmt.Errorf("listDeployKeys: %v", err)
+	}
+	for _, dKey := range deployKeys {
+		if err := deleteDeployKey(sess, doer, dKey.ID); err != nil {
+			return fmt.Errorf("deleteDeployKeys: %v", err)
+		}
+	}
+
 	if cnt, err := sess.ID(repoID).Delete(&Repository{}); err != nil {
 		return err
 	} else if cnt != 1 {
@@ -1898,6 +1909,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 	}
 
 	if err = sess.Commit(); err != nil {
+		if len(deployKeys) > 0 {
+			// We need to rewrite the public keys because the commit failed
+			if err2 := RewriteAllPublicKeys(); err2 != nil {
+				return fmt.Errorf("Commit: %v SSH Keys: %v", err, err2)
+			}
+		}
 		return fmt.Errorf("Commit: %v", err)
 	}
 
diff --git a/models/ssh_key.go b/models/ssh_key.go
index 90c0f04b78..a7dced841d 100644
--- a/models/ssh_key.go
+++ b/models/ssh_key.go
@@ -51,7 +51,7 @@ type PublicKey struct {
 	ID            int64      `xorm:"pk autoincr"`
 	OwnerID       int64      `xorm:"INDEX NOT NULL"`
 	Name          string     `xorm:"NOT NULL"`
-	Fingerprint   string     `xorm:"NOT NULL"`
+	Fingerprint   string     `xorm:"INDEX NOT NULL"`
 	Content       string     `xorm:"TEXT NOT NULL"`
 	Mode          AccessMode `xorm:"NOT NULL DEFAULT 2"`
 	Type          KeyType    `xorm:"NOT NULL DEFAULT 1"`
@@ -350,7 +350,6 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
 func checkKeyFingerprint(e Engine, fingerprint string) error {
 	has, err := e.Get(&PublicKey{
 		Fingerprint: fingerprint,
-		Type:        KeyTypeUser,
 	})
 	if err != nil {
 		return err
@@ -401,12 +400,18 @@ func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*Pu
 		return nil, err
 	}
 
-	if err := checkKeyFingerprint(x, fingerprint); err != nil {
+	sess := x.NewSession()
+	defer sess.Close()
+	if err = sess.Begin(); err != nil {
+		return nil, err
+	}
+
+	if err := checkKeyFingerprint(sess, fingerprint); err != nil {
 		return nil, err
 	}
 
 	// Key name of same user cannot be duplicated.
-	has, err := x.
+	has, err := sess.
 		Where("owner_id = ? AND name = ?", ownerID, name).
 		Get(new(PublicKey))
 	if err != nil {
@@ -415,12 +420,6 @@ func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*Pu
 		return nil, ErrKeyNameAlreadyUsed{ownerID, name}
 	}
 
-	sess := x.NewSession()
-	defer sess.Close()
-	if err = sess.Begin(); err != nil {
-		return nil, err
-	}
-
 	key := &PublicKey{
 		OwnerID:       ownerID,
 		Name:          name,
@@ -519,7 +518,7 @@ func UpdatePublicKeyUpdated(id int64) error {
 }
 
 // deletePublicKeys does the actual key deletion but does not update authorized_keys file.
-func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
+func deletePublicKeys(e Engine, keyIDs ...int64) error {
 	if len(keyIDs) == 0 {
 		return nil
 	}
@@ -728,24 +727,28 @@ func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey
 		accessMode = AccessModeWrite
 	}
 
-	pkey := &PublicKey{
-		Fingerprint: fingerprint,
-		Mode:        accessMode,
-		Type:        KeyTypeDeploy,
-	}
-	has, err := x.Get(pkey)
-	if err != nil {
-		return nil, err
-	}
-
 	sess := x.NewSession()
 	defer sess.Close()
 	if err = sess.Begin(); err != nil {
 		return nil, err
 	}
 
-	// First time use this deploy key.
-	if !has {
+	pkey := &PublicKey{
+		Fingerprint: fingerprint,
+	}
+	has, err := sess.Get(pkey)
+	if err != nil {
+		return nil, err
+	}
+
+	if has {
+		if pkey.Type != KeyTypeDeploy {
+			return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
+		}
+	} else {
+		// First time use this deploy key.
+		pkey.Mode = accessMode
+		pkey.Type = KeyTypeDeploy
 		pkey.Content = content
 		pkey.Name = name
 		if err = addKey(sess, pkey); err != nil {
@@ -763,8 +766,12 @@ func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey
 
 // GetDeployKeyByID returns deploy key by given ID.
 func GetDeployKeyByID(id int64) (*DeployKey, error) {
+	return getDeployKeyByID(x, id)
+}
+
+func getDeployKeyByID(e Engine, id int64) (*DeployKey, error) {
 	key := new(DeployKey)
-	has, err := x.ID(id).Get(key)
+	has, err := e.ID(id).Get(key)
 	if err != nil {
 		return nil, err
 	} else if !has {
@@ -775,11 +782,15 @@ func GetDeployKeyByID(id int64) (*DeployKey, error) {
 
 // GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
 func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
+	return getDeployKeyByRepo(x, keyID, repoID)
+}
+
+func getDeployKeyByRepo(e Engine, keyID, repoID int64) (*DeployKey, error) {
 	key := &DeployKey{
 		KeyID:  keyID,
 		RepoID: repoID,
 	}
-	has, err := x.Get(key)
+	has, err := e.Get(key)
 	if err != nil {
 		return nil, err
 	} else if !has {
@@ -802,7 +813,19 @@ func UpdateDeployKey(key *DeployKey) error {
 
 // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
 func DeleteDeployKey(doer *User, id int64) error {
-	key, err := GetDeployKeyByID(id)
+	sess := x.NewSession()
+	defer sess.Close()
+	if err := sess.Begin(); err != nil {
+		return err
+	}
+	if err := deleteDeployKey(sess, doer, id); err != nil {
+		return err
+	}
+	return sess.Commit()
+}
+
+func deleteDeployKey(sess Engine, doer *User, id int64) error {
+	key, err := getDeployKeyByID(sess, id)
 	if err != nil {
 		if IsErrDeployKeyNotExist(err) {
 			return nil
@@ -812,11 +835,11 @@ func DeleteDeployKey(doer *User, id int64) error {
 
 	// Check if user has access to delete this key.
 	if !doer.IsAdmin {
-		repo, err := GetRepositoryByID(key.RepoID)
+		repo, err := getRepositoryByID(sess, key.RepoID)
 		if err != nil {
 			return fmt.Errorf("GetRepositoryByID: %v", err)
 		}
-		has, err := IsUserRepoAdmin(repo, doer)
+		has, err := isUserRepoAdmin(sess, repo, doer)
 		if err != nil {
 			return fmt.Errorf("GetUserRepoPermission: %v", err)
 		} else if !has {
@@ -824,12 +847,6 @@ func DeleteDeployKey(doer *User, id int64) error {
 		}
 	}
 
-	sess := x.NewSession()
-	defer sess.Close()
-	if err = sess.Begin(); err != nil {
-		return err
-	}
-
 	if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
 		return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
 	}
@@ -851,13 +868,17 @@ func DeleteDeployKey(doer *User, id int64) error {
 		}
 	}
 
-	return sess.Commit()
+	return nil
 }
 
 // ListDeployKeys returns all deploy keys by given repository ID.
 func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
+	return listDeployKeys(x, repoID)
+}
+
+func listDeployKeys(e Engine, repoID int64) ([]*DeployKey, error) {
 	keys := make([]*DeployKey, 0, 5)
-	return keys, x.
+	return keys, e.
 		Where("repo_id = ?", repoID).
 		Find(&keys)
 }
diff --git a/modules/private/key.go b/modules/private/key.go
index 86d0a730d1..1c6511846b 100644
--- a/modules/private/key.go
+++ b/modules/private/key.go
@@ -32,6 +32,31 @@ func UpdateDeployKeyUpdated(keyID int64, repoID int64) error {
 	return nil
 }
 
+// GetDeployKey check if repo has deploy key
+func GetDeployKey(keyID, repoID int64) (*models.DeployKey, error) {
+	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/keys/%d", repoID, keyID)
+	log.GitLogger.Trace("GetDeployKey: %s", reqURL)
+
+	resp, err := newInternalRequest(reqURL, "GET").Response()
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	switch resp.StatusCode {
+	case 404:
+		return nil, nil
+	case 200:
+		var dKey models.DeployKey
+		if err := json.NewDecoder(resp.Body).Decode(&dKey); err != nil {
+			return nil, err
+		}
+		return &dKey, nil
+	default:
+		return nil, fmt.Errorf("Failed to get deploy key: %s", decodeJSONError(resp).Err)
+	}
+}
+
 // HasDeployKey check if repo has deploy key
 func HasDeployKey(keyID, repoID int64) (bool, error) {
 	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/has-keys/%d", repoID, keyID)
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 653d34c931..2d32fac9c7 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -419,7 +419,7 @@ ssh_helper = <strong>Need help?</strong> Have a look at GitHub's guide to <a hre
 gpg_helper = <strong>Need help?</strong> Have a look at GitHub's guide <a href="%s">about GPG</a>.
 add_new_key = Add SSH Key
 add_new_gpg_key = Add GPG Key
-ssh_key_been_used = This SSH key is already added to your account.
+ssh_key_been_used = This SSH key has already been added to the server.
 ssh_key_name_used = An SSH key with same name is already added to your account.
 gpg_key_id_used = A public GPG key with same ID already exists.
 gpg_no_key_email_found = This GPG key is not usable with any email address associated with your account.
diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go
index 2caca887aa..2ee1ce0098 100644
--- a/routers/api/v1/repo/key.go
+++ b/routers/api/v1/repo/key.go
@@ -159,6 +159,8 @@ func HandleCheckKeyStringError(ctx *context.APIContext, err error) {
 // HandleAddKeyError handle add key error
 func HandleAddKeyError(ctx *context.APIContext, err error) {
 	switch {
+	case models.IsErrDeployKeyAlreadyExist(err):
+		ctx.Error(422, "", "This key has already been added to this repository")
 	case models.IsErrKeyAlreadyExist(err):
 		ctx.Error(422, "", "Key content has been used as non-deploy key")
 	case models.IsErrKeyNameAlreadyUsed(err):
diff --git a/routers/private/internal.go b/routers/private/internal.go
index ec2281c5c5..ee6e1274c3 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -82,6 +82,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 		m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey)
 		m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser)
 		m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey)
+		m.Get("/repositories/:repoid/keys/:keyid", GetDeployKey)
 		m.Get("/repositories/:repoid/wiki/init", InitWiki)
 		m.Post("/push/update", PushUpdate)
 		m.Get("/protectedbranch/:pbid/:userid", CanUserPush)
diff --git a/routers/private/key.go b/routers/private/key.go
index 9cc1165780..ee22f6ac48 100644
--- a/routers/private/key.go
+++ b/routers/private/key.go
@@ -72,6 +72,24 @@ func GetUserByKeyID(ctx *macaron.Context) {
 	ctx.JSON(200, user)
 }
 
+//GetDeployKey chainload to models.GetDeployKey
+func GetDeployKey(ctx *macaron.Context) {
+	repoID := ctx.ParamsInt64(":repoid")
+	keyID := ctx.ParamsInt64(":keyid")
+	dKey, err := models.GetDeployKeyByRepo(keyID, repoID)
+	if err != nil {
+		if models.IsErrDeployKeyNotExist(err) {
+			ctx.JSON(404, []byte("not found"))
+			return
+		}
+		ctx.JSON(500, map[string]interface{}{
+			"err": err.Error(),
+		})
+		return
+	}
+	ctx.JSON(200, dKey)
+}
+
 //HasDeployKey chainload to models.HasDeployKey
 func HasDeployKey(ctx *macaron.Context) {
 	repoID := ctx.ParamsInt64(":repoid")
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 5e9e24f9f2..4fb74f6cfa 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -622,6 +622,9 @@ func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) {
 		case models.IsErrDeployKeyAlreadyExist(err):
 			ctx.Data["Err_Content"] = true
 			ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form)
+		case models.IsErrKeyAlreadyExist(err):
+			ctx.Data["Err_Content"] = true
+			ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplDeployKeys, &form)
 		case models.IsErrKeyNameAlreadyUsed(err):
 			ctx.Data["Err_Title"] = true
 			ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form)