diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go
index 943981ead..85f0ab621 100644
--- a/integrations/api_helper_for_declarative_test.go
+++ b/integrations/api_helper_for_declarative_test.go
@@ -5,11 +5,14 @@
 package integrations
 
 import (
+	"encoding/json"
 	"fmt"
 	"io/ioutil"
 	"net/http"
 	"testing"
 
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/auth"
 	api "code.gitea.io/gitea/modules/structs"
 	"github.com/stretchr/testify/assert"
 )
@@ -150,3 +153,42 @@ func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly
 		ctx.Session.MakeRequest(t, req, http.StatusCreated)
 	}
 }
+
+func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBranch string) func(*testing.T) (api.PullRequest, error) {
+	return func(t *testing.T) (api.PullRequest, error) {
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s",
+			owner, repo, ctx.Token)
+		req := NewRequestWithJSON(t, http.MethodPost, urlStr, &api.CreatePullRequestOption{
+			Head:  headBranch,
+			Base:  baseBranch,
+			Title: fmt.Sprintf("create a pr from %s to %s", headBranch, baseBranch),
+		})
+
+		expected := 201
+		if ctx.ExpectedCode != 0 {
+			expected = ctx.ExpectedCode
+		}
+		resp := ctx.Session.MakeRequest(t, req, expected)
+		decoder := json.NewDecoder(resp.Body)
+		pr := api.PullRequest{}
+		err := decoder.Decode(&pr)
+		return pr, err
+	}
+}
+
+func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
+	return func(t *testing.T) {
+		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
+			owner, repo, index, ctx.Token)
+		req := NewRequestWithJSON(t, http.MethodPost, urlStr, &auth.MergePullRequestForm{
+			MergeMessageField: "doAPIMergePullRequest Merge",
+			Do:                string(models.MergeStyleMerge),
+		})
+
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		ctx.Session.MakeRequest(t, req, 200)
+	}
+}
diff --git a/integrations/git_helper_for_declarative_test.go b/integrations/git_helper_for_declarative_test.go
index b4fead662..235f4b4a9 100644
--- a/integrations/git_helper_for_declarative_test.go
+++ b/integrations/git_helper_for_declarative_test.go
@@ -112,16 +112,44 @@ func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) {
 	}
 }
 
-func doGitPushTestRepository(dstPath, remoteName, branch string) func(*testing.T) {
+func doGitPushTestRepository(dstPath string, args ...string) func(*testing.T) {
 	return func(t *testing.T) {
-		_, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
+		_, err := git.NewCommand(append([]string{"push", "-u"}, args...)...).RunInDir(dstPath)
 		assert.NoError(t, err)
 	}
 }
 
-func doGitPushTestRepositoryFail(dstPath, remoteName, branch string) func(*testing.T) {
+func doGitPushTestRepositoryFail(dstPath string, args ...string) func(*testing.T) {
 	return func(t *testing.T) {
-		_, err := git.NewCommand("push", "-u", remoteName, branch).RunInDir(dstPath)
+		_, err := git.NewCommand(append([]string{"push"}, args...)...).RunInDir(dstPath)
 		assert.Error(t, err)
 	}
 }
+
+func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand("checkout", "-b", branch).RunInDir(dstPath)
+		assert.NoError(t, err)
+	}
+}
+
+func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
+		assert.NoError(t, err)
+	}
+}
+
+func doGitMerge(dstPath string, args ...string) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand(append([]string{"merge"}, args...)...).RunInDir(dstPath)
+		assert.NoError(t, err)
+	}
+}
+
+func doGitPull(dstPath string, args ...string) func(*testing.T) {
+	return func(t *testing.T) {
+		_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
+		assert.NoError(t, err)
+	}
+}
diff --git a/integrations/git_test.go b/integrations/git_test.go
index 0554f9a5a..ce5aee493 100644
--- a/integrations/git_test.go
+++ b/integrations/git_test.go
@@ -13,11 +13,13 @@ import (
 	"os"
 	"path"
 	"path/filepath"
+	"strconv"
 	"testing"
 	"time"
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/git"
+	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -43,119 +45,23 @@ func testGit(t *testing.T, u *url.URL) {
 		httpContext.Reponame = "repo-tmp-17"
 
 		dstPath, err := ioutil.TempDir("", httpContext.Reponame)
-		var little, big, littleLFS, bigLFS string
-
 		assert.NoError(t, err)
 		defer os.RemoveAll(dstPath)
-		t.Run("Standard", func(t *testing.T) {
-			PrintCurrentTest(t)
-			ensureAnonymousClone(t, u)
 
-			t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
+		t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
+		ensureAnonymousClone(t, u)
 
-			u.Path = httpContext.GitPath()
-			u.User = url.UserPassword(username, userPassword)
+		u.Path = httpContext.GitPath()
+		u.User = url.UserPassword(username, userPassword)
 
-			t.Run("Clone", doGitClone(dstPath, u))
+		t.Run("Clone", doGitClone(dstPath, u))
 
-			t.Run("PushCommit", func(t *testing.T) {
-				PrintCurrentTest(t)
-				t.Run("Little", func(t *testing.T) {
-					PrintCurrentTest(t)
-					little = commitAndPush(t, littleSize, dstPath)
-				})
-				t.Run("Big", func(t *testing.T) {
-					if testing.Short() {
-						t.Skip("skipping test in short mode.")
-						return
-					}
-					PrintCurrentTest(t)
-					big = commitAndPush(t, bigSize, dstPath)
-				})
-			})
-		})
-		t.Run("LFS", func(t *testing.T) {
-			PrintCurrentTest(t)
-			t.Run("PushCommit", func(t *testing.T) {
-				PrintCurrentTest(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("Little", func(t *testing.T) {
-					PrintCurrentTest(t)
-					littleLFS = commitAndPush(t, littleSize, dstPath)
-					lockFileTest(t, littleLFS, dstPath)
-				})
-				t.Run("Big", func(t *testing.T) {
-					if testing.Short() {
-						t.Skip("skipping test in short mode.")
-						return
-					}
-					PrintCurrentTest(t)
-					bigLFS = commitAndPush(t, bigSize, dstPath)
-					lockFileTest(t, bigLFS, dstPath)
-				})
-			})
-			t.Run("Locks", func(t *testing.T) {
-				PrintCurrentTest(t)
-				lockTest(t, u.String(), dstPath)
-			})
-		})
-		t.Run("Raw", func(t *testing.T) {
-			PrintCurrentTest(t)
-			session := loginUser(t, "user2")
-
-			// Request raw paths
-			req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", little))
-			resp := session.MakeRequest(t, req, http.StatusOK)
-			assert.Equal(t, littleSize, resp.Body.Len())
-
-			req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", littleLFS))
-			resp = session.MakeRequest(t, req, http.StatusOK)
-			assert.NotEqual(t, littleSize, resp.Body.Len())
-			assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
-
-			if !testing.Short() {
-				req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", big))
-				nilResp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-				assert.Equal(t, bigSize, nilResp.Length)
-
-				req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/raw/branch/master/", bigLFS))
-				resp = session.MakeRequest(t, req, http.StatusOK)
-				assert.NotEqual(t, bigSize, resp.Body.Len())
-				assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
-			}
-
-		})
-		t.Run("Media", func(t *testing.T) {
-			PrintCurrentTest(t)
-			session := loginUser(t, "user2")
-
-			// Request media paths
-			req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", little))
-			resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-			assert.Equal(t, littleSize, resp.Length)
-
-			req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", littleLFS))
-			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-			assert.Equal(t, littleSize, resp.Length)
-
-			if !testing.Short() {
-				req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", big))
-				resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-				assert.Equal(t, bigSize, resp.Length)
-
-				req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-17/media/branch/master/", bigLFS))
-				resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-				assert.Equal(t, bigSize, resp.Length)
-			}
-		})
+		little, big := standardCommitAndPushTest(t, dstPath)
+		littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
+		rawTest(t, &httpContext, little, big, littleLFS, bigLFS)
+		mediaTest(t, &httpContext, little, big, littleLFS, bigLFS)
 
+		t.Run("BranchProtectMerge", doBranchProtectPRMerge(&httpContext, dstPath))
 	})
 	t.Run("SSH", func(t *testing.T) {
 		PrintCurrentTest(t)
@@ -165,123 +71,26 @@ func testGit(t *testing.T, u *url.URL) {
 		//Setup key the user ssh key
 		withKeyFile(t, keyname, func(keyFile string) {
 			t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile))
-			PrintCurrentTest(t)
 
 			//Setup remote link
+			//TODO: get url from api
 			sshURL := createSSHUrl(sshContext.GitPath(), u)
 
 			//Setup clone folder
 			dstPath, err := ioutil.TempDir("", sshContext.Reponame)
 			assert.NoError(t, err)
 			defer os.RemoveAll(dstPath)
-			var little, big, littleLFS, bigLFS string
 
-			t.Run("Standard", func(t *testing.T) {
-				PrintCurrentTest(t)
-				t.Run("CreateRepo", doAPICreateRepository(sshContext, false))
+			t.Run("CreateRepo", doAPICreateRepository(sshContext, false))
 
-				//TODO get url from api
-				t.Run("Clone", doGitClone(dstPath, sshURL))
+			t.Run("Clone", doGitClone(dstPath, sshURL))
 
-				//time.Sleep(5 * time.Minute)
-				t.Run("PushCommit", func(t *testing.T) {
-					PrintCurrentTest(t)
-					t.Run("Little", func(t *testing.T) {
-						PrintCurrentTest(t)
-						little = commitAndPush(t, littleSize, dstPath)
-					})
-					t.Run("Big", func(t *testing.T) {
-						if testing.Short() {
-							t.Skip("skipping test in short mode.")
-							return
-						}
-						PrintCurrentTest(t)
-						big = commitAndPush(t, bigSize, dstPath)
-					})
-				})
-			})
-			t.Run("LFS", func(t *testing.T) {
-				PrintCurrentTest(t)
-				t.Run("PushCommit", func(t *testing.T) {
-					PrintCurrentTest(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("Little", func(t *testing.T) {
-						PrintCurrentTest(t)
-						littleLFS = commitAndPush(t, littleSize, dstPath)
-						lockFileTest(t, littleLFS, dstPath)
-
-					})
-					t.Run("Big", func(t *testing.T) {
-						if testing.Short() {
-							return
-						}
-						PrintCurrentTest(t)
-						bigLFS = commitAndPush(t, bigSize, dstPath)
-						lockFileTest(t, bigLFS, dstPath)
-
-					})
-				})
-				t.Run("Locks", func(t *testing.T) {
-					PrintCurrentTest(t)
-					lockTest(t, u.String(), dstPath)
-				})
-			})
-			t.Run("Raw", func(t *testing.T) {
-				PrintCurrentTest(t)
-				session := loginUser(t, "user2")
-
-				// Request raw paths
-				req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", little))
-				resp := session.MakeRequest(t, req, http.StatusOK)
-				assert.Equal(t, littleSize, resp.Body.Len())
-
-				req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", littleLFS))
-				resp = session.MakeRequest(t, req, http.StatusOK)
-				assert.NotEqual(t, littleSize, resp.Body.Len())
-				assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
-
-				if !testing.Short() {
-					req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", big))
-					resp = session.MakeRequest(t, req, http.StatusOK)
-					assert.Equal(t, bigSize, resp.Body.Len())
-
-					req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/raw/branch/master/", bigLFS))
-					resp = session.MakeRequest(t, req, http.StatusOK)
-					assert.NotEqual(t, bigSize, resp.Body.Len())
-					assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
-				}
-			})
-			t.Run("Media", func(t *testing.T) {
-				PrintCurrentTest(t)
-				session := loginUser(t, "user2")
-
-				// Request media paths
-				req := NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", little))
-				resp := session.MakeRequest(t, req, http.StatusOK)
-				assert.Equal(t, littleSize, resp.Body.Len())
-
-				req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", littleLFS))
-				resp = session.MakeRequest(t, req, http.StatusOK)
-				assert.Equal(t, littleSize, resp.Body.Len())
-
-				if !testing.Short() {
-					req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", big))
-					resp = session.MakeRequest(t, req, http.StatusOK)
-					assert.Equal(t, bigSize, resp.Body.Len())
-
-					req = NewRequest(t, "GET", path.Join("/user2/repo-tmp-18/media/branch/master/", bigLFS))
-					resp = session.MakeRequest(t, req, http.StatusOK)
-					assert.Equal(t, bigSize, resp.Body.Len())
-				}
-			})
+			little, big := standardCommitAndPushTest(t, dstPath)
+			littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
+			rawTest(t, &sshContext, little, big, littleLFS, bigLFS)
+			mediaTest(t, &sshContext, little, big, littleLFS, bigLFS)
 
+			t.Run("BranchProtectMerge", doBranchProtectPRMerge(&sshContext, dstPath))
 		})
 
 	})
@@ -295,7 +104,116 @@ func ensureAnonymousClone(t *testing.T, u *url.URL) {
 
 }
 
-func lockTest(t *testing.T, remote, repoPath string) {
+func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string) {
+	t.Run("Standard", func(t *testing.T) {
+		PrintCurrentTest(t)
+		little, big = commitAndPushTest(t, dstPath, "data-file-")
+	})
+	return
+}
+
+func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
+	t.Run("LFS", func(t *testing.T) {
+		PrintCurrentTest(t)
+		prefix := "lfs-data-file-"
+		_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
+		assert.NoError(t, err)
+		_, err = git.NewCommand("lfs").AddArguments("track", prefix+"*").RunInDir(dstPath)
+		assert.NoError(t, err)
+		err = git.AddChanges(dstPath, false, ".gitattributes")
+		assert.NoError(t, err)
+
+		littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
+
+		t.Run("Locks", func(t *testing.T) {
+			PrintCurrentTest(t)
+			lockTest(t, dstPath)
+		})
+	})
+	return
+}
+
+func commitAndPushTest(t *testing.T, dstPath, prefix string) (little, big string) {
+	t.Run("PushCommit", func(t *testing.T) {
+		PrintCurrentTest(t)
+		t.Run("Little", func(t *testing.T) {
+			PrintCurrentTest(t)
+			little = doCommitAndPush(t, littleSize, dstPath, prefix)
+		})
+		t.Run("Big", func(t *testing.T) {
+			if testing.Short() {
+				t.Skip("Skipping test in short mode.")
+				return
+			}
+			PrintCurrentTest(t)
+			big = doCommitAndPush(t, bigSize, dstPath, prefix)
+		})
+	})
+	return
+}
+
+func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) {
+	t.Run("Raw", func(t *testing.T) {
+		PrintCurrentTest(t)
+		username := ctx.Username
+		reponame := ctx.Reponame
+
+		session := loginUser(t, username)
+
+		// Request raw paths
+		req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little))
+		resp := session.MakeRequest(t, req, http.StatusOK)
+		assert.Equal(t, littleSize, resp.Body.Len())
+
+		req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
+		resp = session.MakeRequest(t, req, http.StatusOK)
+		assert.NotEqual(t, littleSize, resp.Body.Len())
+		assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
+
+		if !testing.Short() {
+			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
+			resp = session.MakeRequest(t, req, http.StatusOK)
+			assert.Equal(t, bigSize, resp.Body.Len())
+
+			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
+			resp = session.MakeRequest(t, req, http.StatusOK)
+			assert.NotEqual(t, bigSize, resp.Body.Len())
+			assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
+		}
+	})
+}
+
+func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS string) {
+	t.Run("Media", func(t *testing.T) {
+		PrintCurrentTest(t)
+
+		username := ctx.Username
+		reponame := ctx.Reponame
+
+		session := loginUser(t, username)
+
+		// Request media paths
+		req := NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", little))
+		resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
+		assert.Equal(t, littleSize, resp.Length)
+
+		req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
+		resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
+		assert.Equal(t, littleSize, resp.Length)
+
+		if !testing.Short() {
+			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
+			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
+			assert.Equal(t, bigSize, resp.Length)
+
+			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
+			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
+			assert.Equal(t, bigSize, resp.Length)
+		}
+	})
+}
+
+func lockTest(t *testing.T, repoPath string) {
 	lockFileTest(t, "README.md", repoPath)
 }
 
@@ -310,22 +228,22 @@ func lockFileTest(t *testing.T, filename, repoPath string) {
 	assert.NoError(t, err)
 }
 
-func commitAndPush(t *testing.T, size int, repoPath string) string {
-	name, err := generateCommitWithNewData(size, repoPath, "user2@example.com", "User Two")
+func doCommitAndPush(t *testing.T, size int, repoPath, prefix string) string {
+	name, err := generateCommitWithNewData(size, repoPath, "user2@example.com", "User Two", prefix)
 	assert.NoError(t, err)
-	_, err = git.NewCommand("push").RunInDir(repoPath) //Push
+	_, err = git.NewCommand("push", "origin", "master").RunInDir(repoPath) //Push
 	assert.NoError(t, err)
 	return name
 }
 
-func generateCommitWithNewData(size int, repoPath, email, fullName string) (string, error) {
+func generateCommitWithNewData(size int, repoPath, email, fullName, prefix string) (string, error) {
 	//Generate random file
 	data := make([]byte, size)
 	_, err := rand.Read(data)
 	if err != nil {
 		return "", err
 	}
-	tmpFile, err := ioutil.TempFile(repoPath, "data-file-")
+	tmpFile, err := ioutil.TempFile(repoPath, prefix)
 	if err != nil {
 		return "", err
 	}
@@ -355,3 +273,71 @@ func generateCommitWithNewData(size int, repoPath, email, fullName string) (stri
 	})
 	return filepath.Base(tmpFile.Name()), err
 }
+
+func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
+	return func(t *testing.T) {
+		PrintCurrentTest(t)
+		t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected"))
+		t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected"))
+
+		ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame)
+		t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", ""))
+		t.Run("GenerateCommit", func(t *testing.T) {
+			_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
+			assert.NoError(t, err)
+		})
+		t.Run("FailToPushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "origin", "protected"))
+		t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected"))
+		var pr api.PullRequest
+		var err error
+		t.Run("CreatePullRequest", func(t *testing.T) {
+			pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t)
+			assert.NoError(t, err)
+		})
+		t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
+		t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
+		t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username))
+
+		t.Run("CheckoutMaster", doGitCheckoutBranch(dstPath, "master"))
+		t.Run("CreateBranchForced", doGitCreateBranch(dstPath, "toforce"))
+		t.Run("GenerateCommit", func(t *testing.T) {
+			_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
+			assert.NoError(t, err)
+		})
+		t.Run("FailToForcePushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected"))
+		t.Run("MergeProtectedToToforce", doGitMerge(dstPath, "protected"))
+		t.Run("PushToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "toforce:protected"))
+		t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master"))
+	}
+}
+
+func doProtectBranch(ctx APITestContext, branch string, userToWhitelist string) func(t *testing.T) {
+	// We are going to just use the owner to set the protection.
+	return func(t *testing.T) {
+		csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
+
+		if userToWhitelist == "" {
+			// Change branch to protected
+			req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
+				"_csrf":     csrf,
+				"protected": "on",
+			})
+			ctx.Session.MakeRequest(t, req, http.StatusFound)
+		} else {
+			user, err := models.GetUserByName(userToWhitelist)
+			assert.NoError(t, err)
+			// Change branch to protected
+			req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
+				"_csrf":            csrf,
+				"protected":        "on",
+				"enable_whitelist": "on",
+				"whitelist_users":  strconv.FormatInt(user.ID, 10),
+			})
+			ctx.Session.MakeRequest(t, req, http.StatusFound)
+		}
+		// Check if master branch has been locked successfully
+		flashCookie := ctx.Session.GetCookie("macaron_flash")
+		assert.NotNil(t, flashCookie)
+		assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Bbranch%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
+	}
+}