diff --git a/models/update.go b/models/update.go
index 1492d6c0d3..0883cb0e01 100644
--- a/models/update.go
+++ b/models/update.go
@@ -263,10 +263,6 @@ func pushUpdate(opts PushUpdateOptions) (repo *Repository, err error) {
 		commits = ListToPushCommits(l)
 	}
 
-	if opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
-		UpdateRepoIndexer(repo)
-	}
-
 	if err := CommitRepoAction(CommitRepoActionOptions{
 		PusherName:  opts.PusherName,
 		RepoOwnerID: owner.ID,
diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go
index ccf90f43b3..09a4dbb44c 100644
--- a/modules/repofiles/delete.go
+++ b/modules/repofiles/delete.go
@@ -183,7 +183,8 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo
 	if err = repo.GetOwner(); err != nil {
 		return nil, fmt.Errorf("GetOwner: %v", err)
 	}
-	err = models.PushUpdate(
+	err = PushUpdate(
+		repo,
 		opts.NewBranch,
 		models.PushUpdateOptions{
 			PusherID:     doer.ID,
@@ -199,8 +200,6 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo
 		return nil, fmt.Errorf("PushUpdate: %v", err)
 	}
 
-	// FIXME: Should we UpdateRepoIndexer(repo) here?
-
 	file, err := GetFileResponseFromCommit(repo, commit, opts.NewBranch, treePath)
 	if err != nil {
 		return nil, err
diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go
index 66e3f2babc..569c89ac51 100644
--- a/modules/repofiles/update.go
+++ b/modules/repofiles/update.go
@@ -394,7 +394,8 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 	if err = repo.GetOwner(); err != nil {
 		return nil, fmt.Errorf("GetOwner: %v", err)
 	}
-	err = models.PushUpdate(
+	err = PushUpdate(
+		repo,
 		opts.NewBranch,
 		models.PushUpdateOptions{
 			PusherID:     doer.ID,
@@ -409,7 +410,6 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 	if err != nil {
 		return nil, fmt.Errorf("PushUpdate: %v", err)
 	}
-	models.UpdateRepoIndexer(repo)
 
 	commit, err = t.GetCommit(commitHash)
 	if err != nil {
@@ -422,3 +422,17 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 	}
 	return file, nil
 }
+
+// PushUpdate must be called for any push actions in order to
+// generates necessary push action history feeds and other operations
+func PushUpdate(repo *models.Repository, branch string, opts models.PushUpdateOptions) error {
+	err := models.PushUpdate(branch, opts)
+	if err != nil {
+		return fmt.Errorf("PushUpdate: %v", err)
+	}
+
+	if opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
+		models.UpdateRepoIndexer(repo)
+	}
+	return nil
+}
diff --git a/modules/repofiles/upload.go b/modules/repofiles/upload.go
index ed6a9438c7..5f428c3139 100644
--- a/modules/repofiles/upload.go
+++ b/modules/repofiles/upload.go
@@ -188,7 +188,8 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 	if err = repo.GetOwner(); err != nil {
 		return fmt.Errorf("GetOwner: %v", err)
 	}
-	err = models.PushUpdate(
+	err = PushUpdate(
+		repo,
 		opts.NewBranch,
 		models.PushUpdateOptions{
 			PusherID:     doer.ID,
@@ -203,7 +204,6 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 	if err != nil {
 		return fmt.Errorf("PushUpdate: %v", err)
 	}
-	// FIXME: Should we models.UpdateRepoIndexer(repo) here?
 
 	return models.DeleteUploads(uploads...)
 }
diff --git a/routers/private/hook.go b/routers/private/hook.go
index 700c8bf332..a5985f161e 100644
--- a/routers/private/hook.go
+++ b/routers/private/hook.go
@@ -15,6 +15,7 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/private"
+	"code.gitea.io/gitea/modules/repofiles"
 	"code.gitea.io/gitea/modules/util"
 
 	macaron "gopkg.in/macaron.v1"
@@ -117,7 +118,15 @@ func HookPostReceive(ctx *macaron.Context) {
 	// or other less-standard refs spaces are ignored since there
 	// may be a very large number of them).
 	if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
-		if err := models.PushUpdate(branch, models.PushUpdateOptions{
+		repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
+		if err != nil {
+			log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
+			ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
+				"err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
+			})
+			return
+		}
+		if err := repofiles.PushUpdate(repo, branch, models.PushUpdateOptions{
 			RefFullName:  refFullName,
 			OldCommitID:  oldCommitID,
 			NewCommitID:  newCommitID,
diff --git a/routers/private/push_update.go b/routers/private/push_update.go
new file mode 100644
index 0000000000..733490ce1c
--- /dev/null
+++ b/routers/private/push_update.go
@@ -0,0 +1,56 @@
+// Copyright 2017 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 private
+
+import (
+	"encoding/json"
+	"strings"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/repofiles"
+
+	macaron "gopkg.in/macaron.v1"
+)
+
+// PushUpdate update public key updates
+func PushUpdate(ctx *macaron.Context) {
+	var opt models.PushUpdateOptions
+	if err := json.NewDecoder(ctx.Req.Request.Body).Decode(&opt); err != nil {
+		ctx.JSON(500, map[string]interface{}{
+			"err": err.Error(),
+		})
+		return
+	}
+
+	branch := strings.TrimPrefix(opt.RefFullName, git.BranchPrefix)
+	if len(branch) == 0 || opt.PusherID <= 0 {
+		ctx.Error(404)
+		log.Trace("PushUpdate: branch or secret is empty, or pusher ID is not valid")
+		return
+	}
+
+	repo, err := models.GetRepositoryByOwnerAndName(opt.RepoUserName, opt.RepoName)
+	if err != nil {
+		ctx.JSON(500, map[string]interface{}{
+			"err": err.Error(),
+		})
+		return
+	}
+
+	err = repofiles.PushUpdate(repo, branch, opt)
+	if err != nil {
+		if models.IsErrUserNotExist(err) {
+			ctx.Error(404)
+		} else {
+			ctx.JSON(500, map[string]interface{}{
+				"err": err.Error(),
+			})
+		}
+		return
+	}
+	ctx.Status(202)
+}
diff --git a/routers/repo/branch.go b/routers/repo/branch.go
index ae87aa5b3a..05d64fb4c8 100644
--- a/routers/repo/branch.go
+++ b/routers/repo/branch.go
@@ -131,15 +131,18 @@ func deleteBranch(ctx *context.Context, branchName string) error {
 	}
 
 	// Don't return error below this
-	if err := models.PushUpdate(branchName, models.PushUpdateOptions{
-		RefFullName:  git.BranchPrefix + branchName,
-		OldCommitID:  commit.ID.String(),
-		NewCommitID:  git.EmptySHA,
-		PusherID:     ctx.User.ID,
-		PusherName:   ctx.User.Name,
-		RepoUserName: ctx.Repo.Owner.Name,
-		RepoName:     ctx.Repo.Repository.Name,
-	}); err != nil {
+	if err := repofiles.PushUpdate(
+		ctx.Repo.Repository,
+		branchName,
+		models.PushUpdateOptions{
+			RefFullName:  git.BranchPrefix + branchName,
+			OldCommitID:  commit.ID.String(),
+			NewCommitID:  git.EmptySHA,
+			PusherID:     ctx.User.ID,
+			PusherName:   ctx.User.Name,
+			RepoUserName: ctx.Repo.Owner.Name,
+			RepoName:     ctx.Repo.Repository.Name,
+		}); err != nil {
 		log.Error("Update: %v", err)
 	}