@@ -60,6 +60,7 @@ type ChangeRepoFilesOptions struct {
60
60
Committer * IdentityOptions
61
61
Dates * CommitDateOptions
62
62
Signoff bool
63
+ Force bool
63
64
}
64
65
65
66
type RepoFileOptions struct {
@@ -168,19 +169,30 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
168
169
}
169
170
170
171
// 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
173
174
if opts .NewBranch != opts .OldBranch {
174
175
exist , err := git_model .IsBranchExist (ctx , repo .ID , opts .NewBranch )
175
176
if err != nil {
176
177
return nil , err
177
178
}
179
+
178
180
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
+ }
181
193
}
182
194
}
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 {
184
196
return nil , err
185
197
}
186
198
@@ -303,7 +315,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
303
315
}
304
316
305
317
// 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 {
307
319
log .Error ("%T %v" , err , err )
308
320
return nil , err
309
321
}
@@ -685,7 +697,7 @@ func writeRepoObjectForRename(ctx context.Context, t *TemporaryUploadRepository,
685
697
}
686
698
687
699
// 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 {
689
701
protectedBranch , err := git_model .GetFirstMatchProtectedBranchRule (ctx , repo .ID , branchName )
690
702
if err != nil {
691
703
return err
@@ -695,6 +707,7 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do
695
707
globUnprotected := protectedBranch .GetUnprotectedFilePatterns ()
696
708
globProtected := protectedBranch .GetProtectedFilePatterns ()
697
709
canUserPush := protectedBranch .CanUserPush (ctx , doer )
710
+ canUserForcePush := protectedBranch .CanUserForcePush (ctx , doer )
698
711
for _ , treePath := range treePaths {
699
712
isUnprotectedFile := false
700
713
if len (globUnprotected ) != 0 {
@@ -705,6 +718,11 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do
705
718
UserName : doer .LowerName ,
706
719
}
707
720
}
721
+ if force && ! canUserForcePush && ! isUnprotectedFile {
722
+ return ErrUserCannotCommit {
723
+ UserName : doer .LowerName ,
724
+ }
725
+ }
708
726
if protectedBranch .IsProtectedFile (globProtected , treePath ) {
709
727
return pull_service.ErrFilePathProtected {
710
728
Path : treePath ,
0 commit comments