1// Copyright 2020 The Gitea Authors. All rights reserved.
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file.
4
5package repository
6
7import (
8	"strings"
9
10	repo_model "code.gitea.io/gitea/models/repo"
11	"code.gitea.io/gitea/modules/git"
12)
13
14// PushUpdateOptions defines the push update options
15type PushUpdateOptions struct {
16	PusherID     int64
17	PusherName   string
18	RepoUserName string
19	RepoName     string
20	RefFullName  string // branch, tag or other name to push
21	OldCommitID  string
22	NewCommitID  string
23}
24
25// IsNewRef return true if it's a first-time push to a branch, tag or etc.
26func (opts *PushUpdateOptions) IsNewRef() bool {
27	return opts.OldCommitID == git.EmptySHA
28}
29
30// IsDelRef return true if it's a deletion to a branch or tag
31func (opts *PushUpdateOptions) IsDelRef() bool {
32	return opts.NewCommitID == git.EmptySHA
33}
34
35// IsUpdateRef return true if it's an update operation
36func (opts *PushUpdateOptions) IsUpdateRef() bool {
37	return !opts.IsNewRef() && !opts.IsDelRef()
38}
39
40// IsTag return true if it's an operation to a tag
41func (opts *PushUpdateOptions) IsTag() bool {
42	return strings.HasPrefix(opts.RefFullName, git.TagPrefix)
43}
44
45// IsNewTag return true if it's a creation to a tag
46func (opts *PushUpdateOptions) IsNewTag() bool {
47	return opts.IsTag() && opts.IsNewRef()
48}
49
50// IsDelTag return true if it's a deletion to a tag
51func (opts *PushUpdateOptions) IsDelTag() bool {
52	return opts.IsTag() && opts.IsDelRef()
53}
54
55// IsBranch return true if it's a push to branch
56func (opts *PushUpdateOptions) IsBranch() bool {
57	return strings.HasPrefix(opts.RefFullName, git.BranchPrefix)
58}
59
60// IsNewBranch return true if it's the first-time push to a branch
61func (opts *PushUpdateOptions) IsNewBranch() bool {
62	return opts.IsBranch() && opts.IsNewRef()
63}
64
65// IsUpdateBranch return true if it's not the first push to a branch
66func (opts *PushUpdateOptions) IsUpdateBranch() bool {
67	return opts.IsBranch() && opts.IsUpdateRef()
68}
69
70// IsDelBranch return true if it's a deletion to a branch
71func (opts *PushUpdateOptions) IsDelBranch() bool {
72	return opts.IsBranch() && opts.IsDelRef()
73}
74
75// TagName returns simple tag name if it's an operation to a tag
76func (opts *PushUpdateOptions) TagName() string {
77	return opts.RefFullName[len(git.TagPrefix):]
78}
79
80// BranchName returns simple branch name if it's an operation to branch
81func (opts *PushUpdateOptions) BranchName() string {
82	return opts.RefFullName[len(git.BranchPrefix):]
83}
84
85// RefName returns simple name for ref
86func (opts *PushUpdateOptions) RefName() string {
87	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
88		return opts.RefFullName[len(git.TagPrefix):]
89	} else if strings.HasPrefix(opts.RefFullName, git.BranchPrefix) {
90		return opts.RefFullName[len(git.BranchPrefix):]
91	}
92	return ""
93}
94
95// RepoFullName returns repo full name
96func (opts *PushUpdateOptions) RepoFullName() string {
97	return opts.RepoUserName + "/" + opts.RepoName
98}
99
100// IsForcePush detect if a push is a force push
101func IsForcePush(opts *PushUpdateOptions) (bool, error) {
102	if !opts.IsUpdateBranch() {
103		return false, nil
104	}
105
106	output, err := git.NewCommand("rev-list", "--max-count=1", opts.OldCommitID, "^"+opts.NewCommitID).
107		RunInDir(repo_model.RepoPath(opts.RepoUserName, opts.RepoName))
108	if err != nil {
109		return false, err
110	} else if len(output) > 0 {
111		return true, nil
112	}
113	return false, nil
114}
115