254 lines
7.8 KiB
Go
254 lines
7.8 KiB
Go
package repo
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
git_model "code.gitea.io/gitea/models/git"
|
|
"code.gitea.io/gitea/modules/context"
|
|
gitea_git "code.gitea.io/gitea/modules/git"
|
|
repo_module "code.gitea.io/gitea/modules/repository"
|
|
"code.gitea.io/gitea/modules/util"
|
|
"code.gitea.io/gitea/modules/web"
|
|
"code.gitea.io/gitea/services/convert"
|
|
repo_service "code.gitea.io/gitea/services/repository"
|
|
hat_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert"
|
|
hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
|
|
hat_branch_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/services/convert"
|
|
|
|
"code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git"
|
|
|
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
|
)
|
|
|
|
func ListBranches(ctx *context.APIContext) {
|
|
var totalNumOfBranches int64
|
|
var apiBranches []*hat_api.Branch
|
|
|
|
searchName := ctx.FormString("name")
|
|
listOptions := utils.GetListOptions(ctx)
|
|
if !ctx.Repo.Repository.IsEmpty {
|
|
if ctx.Repo.GitRepo == nil {
|
|
ctx.Error(http.StatusInternalServerError, "Load git repository failed", nil)
|
|
return
|
|
}
|
|
var isDeleted util.OptionalBool
|
|
switch ctx.FormString("state") {
|
|
case "all":
|
|
isDeleted = util.OptionalBoolNone
|
|
case "deleted":
|
|
isDeleted = util.OptionalBoolTrue
|
|
default:
|
|
isDeleted = util.OptionalBoolFalse
|
|
}
|
|
|
|
branchOpts := git_model.FindBranchOptions{
|
|
ListOptions: listOptions,
|
|
RepoID: ctx.Repo.Repository.ID,
|
|
IsDeletedBranch: isDeleted,
|
|
Keyword: searchName,
|
|
}
|
|
|
|
var err error
|
|
totalNumOfBranches, err = git_model.CountBranches(ctx, branchOpts)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "CountBranches", err)
|
|
return
|
|
}
|
|
if totalNumOfBranches < 2 { // sync branches immediately because non-empty repository should have at least 1 branch
|
|
_, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
|
if err != nil {
|
|
ctx.ServerError("SyncRepoBranches", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
|
|
return
|
|
}
|
|
|
|
branches, err := git_model.FindBranches(ctx, branchOpts)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
|
return
|
|
}
|
|
|
|
apiBranches = make([]*hat_api.Branch, 0, len(branches))
|
|
for i := range branches {
|
|
err := branches[i].LoadDeletedBy(ctx)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "LoadDeletedBy", err)
|
|
return
|
|
}
|
|
c, err := ctx.Repo.GitRepo.GetCommit(branches[i].CommitID)
|
|
if err != nil {
|
|
// Skip if this branch doesn't exist anymore.
|
|
if gitea_git.IsErrNotExist(err) {
|
|
totalNumOfBranches--
|
|
continue
|
|
}
|
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
|
return
|
|
}
|
|
|
|
branchProtection := rules.GetFirstMatched(branches[i].Name)
|
|
apiBranch, err := hat_branch_convert.ToBranch(ctx, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
|
return
|
|
}
|
|
apiBranches = append(apiBranches, apiBranch)
|
|
}
|
|
}
|
|
|
|
ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
|
|
ctx.SetTotalCountHeader(totalNumOfBranches)
|
|
ctx.RespHeader().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
|
|
ctx.JSON(http.StatusOK, &apiBranches)
|
|
}
|
|
|
|
func ListBranchesSlice(ctx *context.APIContext) {
|
|
listOptions := utils.GetListOptions(ctx)
|
|
|
|
branches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(0, 0)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
|
return
|
|
}
|
|
|
|
apiBranches := make([]*git.Branch, len(branches))
|
|
for i, branch := range branches {
|
|
c, err := branch.GetCommit()
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
|
return
|
|
}
|
|
branchProtection, err := git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, branch.Name)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
|
|
return
|
|
}
|
|
sBranch, err := convert.ToBranch(ctx, ctx.Repo.Repository, branch.Name, c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
|
return
|
|
}
|
|
gitBranch := &git.Branch{}
|
|
gitBranch.Branch = sBranch
|
|
gitBranch.CommitID = c.ID.String()
|
|
gitBranch.CommitTime = c.Author.When.Format(time.RFC3339)
|
|
gitBranch.DefaultBranch = ctx.Repo.Repository.DefaultBranch
|
|
gitBranch.BranchKind = int(git.OtherBranch)
|
|
apiBranches[i] = gitBranch
|
|
if branchProtection != nil {
|
|
apiBranches[i].BranchKind = int(git.ProtectedBranch)
|
|
}
|
|
if branch.Name == ctx.Repo.Repository.DefaultBranch {
|
|
apiBranches[i].BranchKind = int(git.DefaultBranch)
|
|
}
|
|
|
|
}
|
|
|
|
sort.Sort(git.SortBranch(apiBranches))
|
|
BranchesSlice := BranchesSliceByProtection(ctx, apiBranches)
|
|
|
|
ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
|
|
ctx.RespHeader().Set("X-Total-Count", fmt.Sprintf("%d", totalNumOfBranches))
|
|
ctx.RespHeader().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
|
|
ctx.JSON(http.StatusOK, &BranchesSlice)
|
|
}
|
|
|
|
func paginate(branchSlice []*git.Branch, skip, pageSize int) []*git.Branch {
|
|
limit := func() int {
|
|
if skip+pageSize > len(branchSlice) {
|
|
return len(branchSlice)
|
|
} else {
|
|
return skip + pageSize
|
|
}
|
|
}
|
|
start := func() int {
|
|
if skip > len(branchSlice) {
|
|
return len(branchSlice)
|
|
} else {
|
|
return skip
|
|
}
|
|
}
|
|
return branchSlice[start():limit()]
|
|
|
|
}
|
|
|
|
func BranchesSliceByProtection(ctx *context.APIContext, branchList []*git.Branch) []git.BranchesSlice {
|
|
// group by protection
|
|
sort.Sort(git.SortBranch(branchList))
|
|
branchSlice := make([]git.BranchesSlice, 0)
|
|
i := 0
|
|
var j int
|
|
for {
|
|
if i >= len(branchList) {
|
|
break
|
|
}
|
|
for j = i + 1; j < len(branchList) && (branchList[i].BranchKind == branchList[j].BranchKind); j++ {
|
|
}
|
|
// get the same branches
|
|
sameBranchSlice := branchList[i:j]
|
|
// sort by time
|
|
sort.Sort(git.SortBranchTime(sameBranchSlice))
|
|
branchSlice = append(branchSlice, git.BranchesSlice{
|
|
BranchName: hat_convert.ToBranchType(branchList[i].BranchKind).Name(),
|
|
Branches: sameBranchSlice,
|
|
})
|
|
i = j
|
|
}
|
|
return branchSlice
|
|
}
|
|
|
|
func RestoreBranch(ctx *context.APIContext) {
|
|
|
|
form := web.GetForm(ctx).(*hat_api.RestoreBranchOption)
|
|
branchID := form.BranchID
|
|
branchName := form.BranchName
|
|
|
|
deletedBranch, err := git_model.GetDeletedBranchByID(ctx, ctx.Repo.Repository.ID, branchID)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "GetDeletedBranchByID: %v", err)
|
|
return
|
|
} else if deletedBranch == nil {
|
|
ctx.Error(http.StatusInternalServerError, "RestoreBranch: Can't restore branch '%s', as it does not exist", branchName)
|
|
return
|
|
}
|
|
|
|
if err := gitea_git.Push(ctx, ctx.Repo.Repository.RepoPath(), gitea_git.PushOptions{
|
|
Remote: ctx.Repo.Repository.RepoPath(),
|
|
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.CommitID, gitea_git.BranchPrefix, deletedBranch.Name),
|
|
Env: repo_module.PushingEnvironment(ctx.Doer, ctx.Repo.Repository),
|
|
}); err != nil {
|
|
if strings.Contains(err.Error(), "already exists") {
|
|
ctx.Error(http.StatusInternalServerError, "RestoreBranch: Can't restore branch '%s', since one with same name already exist", deletedBranch.Name)
|
|
return
|
|
}
|
|
ctx.Error(http.StatusInternalServerError, "RestoreBranch: CreateBranch: %v", err)
|
|
return
|
|
}
|
|
|
|
if err := repo_service.PushUpdate(
|
|
&repo_module.PushUpdateOptions{
|
|
RefFullName: gitea_git.RefNameFromBranch(deletedBranch.Name),
|
|
OldCommitID: gitea_git.EmptySHA,
|
|
NewCommitID: deletedBranch.CommitID,
|
|
PusherID: ctx.Doer.ID,
|
|
PusherName: ctx.Doer.Name,
|
|
RepoUserName: ctx.Repo.Owner.Name,
|
|
RepoName: ctx.Repo.Repository.Name,
|
|
}); err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "RestoreBranch: Update: %v", err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusCreated)
|
|
}
|