Ignore non-standard refs in git push (#6758)

When replicating to gitea from a remote system which makes use of
git refs to store extra data (for example, gerrit), pushing a lot
of refs to gitea can cause problems due to the extra processing
that the pre and post receive hooks perform.  But it's still
useful for gitea to be able to serve those refs.  This change
skips unecessary processing of refs other than branches or tags.

We don't need to check any ref that isn't a branch for branch
protection (protection will never be enabled).  So in the
pre-receive hook, we wrap that check in a test for whether the
ref is a branch.

We also don't need to add information to the activity stream about
pushes to non-standard refs, so we skip that step in the
post-receive hook for refs which are not branches or tags.

For some concrete examples, gerrit maintains a ref for every
patchset of every change in the form refs/changes/XX/YYYY/Z.
Many systems use refs/notes to store additonal data about commits.
This change allows these and other schemes to be used without
affecting gitea.
This commit is contained in:
James E. Blair 2019-05-14 07:40:27 -07:00 committed by techknowlogick
parent 24a536d145
commit 488d34691a
1 changed files with 46 additions and 37 deletions

View File

@ -89,34 +89,37 @@ func runHookPreReceive(c *cli.Context) error {
newCommitID := string(fields[1]) newCommitID := string(fields[1])
refFullName := string(fields[2]) refFullName := string(fields[2])
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix) // If the ref is a branch, check if it's protected
protectBranch, err := private.GetProtectedBranchBy(repoID, branchName) if strings.HasPrefix(refFullName, git.BranchPrefix) {
if err != nil { branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
fail("Internal error", fmt.Sprintf("retrieve protected branches information failed: %v", err)) protectBranch, err := private.GetProtectedBranchBy(repoID, branchName)
}
if protectBranch != nil && protectBranch.IsProtected() {
// check and deletion
if newCommitID == git.EmptySHA {
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
}
// detect force push
if git.EmptySHA != oldCommitID {
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
if err != nil {
fail("Internal error", "Fail to detect force push: %v", err)
} else if len(output) > 0 {
fail(fmt.Sprintf("branch %s is protected from force push", branchName), "")
}
}
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
canPush, err := private.CanUserPush(protectBranch.ID, userID)
if err != nil { if err != nil {
fail("Internal error", "Fail to detect user can push: %v", err) fail("Internal error", fmt.Sprintf("retrieve protected branches information failed: %v", err))
} else if !canPush { }
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
if protectBranch != nil && protectBranch.IsProtected() {
// check and deletion
if newCommitID == git.EmptySHA {
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
}
// detect force push
if git.EmptySHA != oldCommitID {
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
if err != nil {
fail("Internal error", "Fail to detect force push: %v", err)
} else if len(output) > 0 {
fail(fmt.Sprintf("branch %s is protected from force push", branchName), "")
}
}
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
canPush, err := private.CanUserPush(protectBranch.ID, userID)
if err != nil {
fail("Internal error", "Fail to detect user can push: %v", err)
} else if !canPush {
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
}
} }
} }
} }
@ -169,16 +172,22 @@ func runHookPostReceive(c *cli.Context) error {
newCommitID := string(fields[1]) newCommitID := string(fields[1])
refFullName := string(fields[2]) refFullName := string(fields[2])
if err := private.PushUpdate(models.PushUpdateOptions{ // Only trigger activity updates for changes to branches or
RefFullName: refFullName, // tags. Updates to other refs (eg, refs/notes, refs/changes,
OldCommitID: oldCommitID, // or other less-standard refs spaces are ignored since there
NewCommitID: newCommitID, // may be a very large number of them).
PusherID: pusherID, if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
PusherName: pusherName, if err := private.PushUpdate(models.PushUpdateOptions{
RepoUserName: repoUser, RefFullName: refFullName,
RepoName: repoName, OldCommitID: oldCommitID,
}); err != nil { NewCommitID: newCommitID,
log.GitLogger.Error("Update: %v", err) PusherID: pusherID,
PusherName: pusherName,
RepoUserName: repoUser,
RepoName: repoName,
}); err != nil {
log.GitLogger.Error("Update: %v", err)
}
} }
if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) { if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {