@@ -60,6 +60,7 @@ type ChangeRepoFilesOptions struct {
6060 Committer * IdentityOptions
6161 Dates * CommitDateOptions
6262 Signoff bool
63+ Force bool
6364}
6465
6566type RepoFileOptions struct {
@@ -168,19 +169,30 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
168169 }
169170
170171 // A NewBranch can be specified for the file to be created/updated in a new branch.
171- // Check to make sure the branch does not already exist, otherwise we can't proceed.
172- // If we aren't branching to a new branch, make sure user can commit to the given branch
172+ // Check to to see if the branch already exist. If it does, ensure Force option is set
173+ // and VerifyBranchProtection passes
173174 if opts .NewBranch != opts .OldBranch {
174175 exist , err := git_model .IsBranchExist (ctx , repo .ID , opts .NewBranch )
175176 if err != nil {
176177 return nil , err
177178 }
179+
178180 if exist {
179- return nil , git_model.ErrBranchAlreadyExists {
180- BranchName : opts .NewBranch ,
181+ if opts .Force {
182+ // ensure branch can be force pushed
183+ if err := VerifyBranchProtection (ctx , repo , doer , opts .NewBranch , treePaths , opts .Force ); err != nil {
184+ return nil , git_model.ErrBranchProtected {
185+ BranchName : opts .NewBranch ,
186+ }
187+ }
188+ } else {
189+ // branch exists but force option not set
190+ return nil , git_model.ErrBranchAlreadyExists {
191+ BranchName : opts .NewBranch ,
192+ }
181193 }
182194 }
183- } else if err := VerifyBranchProtection (ctx , repo , doer , opts .OldBranch , treePaths ); err != nil {
195+ } else if err := VerifyBranchProtection (ctx , repo , doer , opts .OldBranch , treePaths , opts . Force ); err != nil {
184196 return nil , err
185197 }
186198
@@ -303,7 +315,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
303315 }
304316
305317 // Then push this tree to NewBranch
306- if err := t .Push (ctx , doer , commitHash , opts .NewBranch ); err != nil {
318+ if err := t .Push (ctx , doer , commitHash , opts .NewBranch , opts . Force ); err != nil {
307319 log .Error ("%T %v" , err , err )
308320 return nil , err
309321 }
@@ -685,7 +697,7 @@ func writeRepoObjectForRename(ctx context.Context, t *TemporaryUploadRepository,
685697}
686698
687699// VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
688- func VerifyBranchProtection (ctx context.Context , repo * repo_model.Repository , doer * user_model.User , branchName string , treePaths []string ) error {
700+ func VerifyBranchProtection (ctx context.Context , repo * repo_model.Repository , doer * user_model.User , branchName string , treePaths []string , force bool ) error {
689701 protectedBranch , err := git_model .GetFirstMatchProtectedBranchRule (ctx , repo .ID , branchName )
690702 if err != nil {
691703 return err
@@ -695,6 +707,7 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do
695707 globUnprotected := protectedBranch .GetUnprotectedFilePatterns ()
696708 globProtected := protectedBranch .GetProtectedFilePatterns ()
697709 canUserPush := protectedBranch .CanUserPush (ctx , doer )
710+ canUserForcePush := protectedBranch .CanUserForcePush (ctx , doer )
698711 for _ , treePath := range treePaths {
699712 isUnprotectedFile := false
700713 if len (globUnprotected ) != 0 {
@@ -705,6 +718,11 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do
705718 UserName : doer .LowerName ,
706719 }
707720 }
721+ if force && ! canUserForcePush && ! isUnprotectedFile {
722+ return ErrUserCannotCommit {
723+ UserName : doer .LowerName ,
724+ }
725+ }
708726 if protectedBranch .IsProtectedFile (globProtected , treePath ) {
709727 return pull_service.ErrFilePathProtected {
710728 Path : treePath ,
0 commit comments