8
8
"fmt"
9
9
"io/ioutil"
10
10
"path"
11
+ "path/filepath"
11
12
"strings"
12
13
13
14
"code.gitea.io/gitea/models"
@@ -137,7 +138,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
137
138
} else {
138
139
ctx .Data ["commit_choice" ] = frmCommitChoiceNewBranch
139
140
}
140
- ctx .Data ["new_branch_name" ] = ""
141
+ ctx .Data ["new_branch_name" ] = GetUniquePatchBranchName ( ctx )
141
142
ctx .Data ["last_commit" ] = ctx .Repo .CommitID
142
143
ctx .Data ["MarkdownFileExts" ] = strings .Join (setting .Markdown .FileExtensions , "," )
143
144
ctx .Data ["LineWrapExtensions" ] = strings .Join (setting .Repository .Editor .LineWrapExtensions , "," )
@@ -266,6 +267,10 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
266
267
} else {
267
268
ctx .RenderWithErr (ctx .Tr ("repo.editor.fail_to_update_file" , form .TreePath , err ), tplEditFile , & form )
268
269
}
270
+ }
271
+
272
+ if form .CommitChoice == frmCommitChoiceNewBranch {
273
+ ctx .Redirect (ctx .Repo .RepoLink + "/compare/" + ctx .Repo .BranchName + "..." + form .NewBranchName )
269
274
} else {
270
275
ctx .Redirect (ctx .Repo .RepoLink + "/src/branch/" + util .PathEscapeSegments (branchName ) + "/" + util .PathEscapeSegments (form .TreePath ))
271
276
}
@@ -335,7 +340,7 @@ func DeleteFile(ctx *context.Context) {
335
340
} else {
336
341
ctx .Data ["commit_choice" ] = frmCommitChoiceNewBranch
337
342
}
338
- ctx .Data ["new_branch_name" ] = ""
343
+ ctx .Data ["new_branch_name" ] = GetUniquePatchBranchName ( ctx )
339
344
340
345
ctx .HTML (200 , tplDeleteFile )
341
346
}
@@ -362,7 +367,7 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
362
367
return
363
368
}
364
369
365
- if branchName ! = ctx .Repo .BranchName && ! canCommit {
370
+ if branchName = = ctx .Repo .BranchName && ! canCommit {
366
371
ctx .Data ["Err_NewBranchName" ] = true
367
372
ctx .Data ["commit_choice" ] = frmCommitChoiceNewBranch
368
373
ctx .RenderWithErr (ctx .Tr ("repo.editor.cannot_commit_to_protected_branch" , branchName ), tplDeleteFile , & form )
@@ -387,20 +392,20 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
387
392
}); err != nil {
388
393
// This is where we handle all the errors thrown by repofiles.DeleteRepoFile
389
394
if git .IsErrNotExist (err ) || models .IsErrRepoFileDoesNotExist (err ) {
390
- ctx .RenderWithErr (ctx .Tr ("repo.editor.file_deleting_no_longer_exists" , ctx .Repo .TreePath ), tplEditFile , & form )
395
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.file_deleting_no_longer_exists" , ctx .Repo .TreePath ), tplDeleteFile , & form )
391
396
} else if models .IsErrFilenameInvalid (err ) {
392
397
ctx .Data ["Err_TreePath" ] = true
393
- ctx .RenderWithErr (ctx .Tr ("repo.editor.filename_is_invalid" , ctx .Repo .TreePath ), tplEditFile , & form )
398
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.filename_is_invalid" , ctx .Repo .TreePath ), tplDeleteFile , & form )
394
399
} else if models .IsErrFilePathInvalid (err ) {
395
400
ctx .Data ["Err_TreePath" ] = true
396
401
if fileErr , ok := err .(models.ErrFilePathInvalid ); ok {
397
402
switch fileErr .Type {
398
403
case git .EntryModeSymlink :
399
- ctx .RenderWithErr (ctx .Tr ("repo.editor.file_is_a_symlink" , fileErr .Path ), tplEditFile , & form )
404
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.file_is_a_symlink" , fileErr .Path ), tplDeleteFile , & form )
400
405
case git .EntryModeTree :
401
- ctx .RenderWithErr (ctx .Tr ("repo.editor.filename_is_a_directory" , fileErr .Path ), tplEditFile , & form )
406
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.filename_is_a_directory" , fileErr .Path ), tplDeleteFile , & form )
402
407
case git .EntryModeBlob :
403
- ctx .RenderWithErr (ctx .Tr ("repo.editor.directory_is_a_file" , fileErr .Path ), tplEditFile , & form )
408
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.directory_is_a_file" , fileErr .Path ), tplDeleteFile , & form )
404
409
default :
405
410
ctx .ServerError ("DeleteRepoFile" , err )
406
411
}
@@ -410,25 +415,44 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
410
415
} else if git .IsErrBranchNotExist (err ) {
411
416
// For when a user deletes a file to a branch that no longer exists
412
417
if branchErr , ok := err .(git.ErrBranchNotExist ); ok {
413
- ctx .RenderWithErr (ctx .Tr ("repo.editor.branch_does_not_exist" , branchErr .Name ), tplEditFile , & form )
418
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.branch_does_not_exist" , branchErr .Name ), tplDeleteFile , & form )
414
419
} else {
415
420
ctx .Error (500 , err .Error ())
416
421
}
417
422
} else if models .IsErrBranchAlreadyExists (err ) {
418
423
// For when a user specifies a new branch that already exists
419
424
if branchErr , ok := err .(models.ErrBranchAlreadyExists ); ok {
420
- ctx .RenderWithErr (ctx .Tr ("repo.editor.branch_already_exists" , branchErr .BranchName ), tplEditFile , & form )
425
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.branch_already_exists" , branchErr .BranchName ), tplDeleteFile , & form )
421
426
} else {
422
427
ctx .Error (500 , err .Error ())
423
428
}
424
429
} else if models .IsErrCommitIDDoesNotMatch (err ) {
425
- ctx .RenderWithErr (ctx .Tr ("repo.editor.file_changed_while_editing " , ctx .Repo .RepoLink + "/compare/" + form .LastCommit + "..." + ctx .Repo .CommitID ), tplEditFile , & form )
430
+ ctx .RenderWithErr (ctx .Tr ("repo.editor.file_changed_while_deleting " , ctx .Repo .RepoLink + "/compare/" + form .LastCommit + "..." + ctx .Repo .CommitID ), tplDeleteFile , & form )
426
431
} else {
427
432
ctx .ServerError ("DeleteRepoFile" , err )
428
433
}
434
+ }
435
+
436
+ ctx .Flash .Success (ctx .Tr ("repo.editor.file_delete_success" , ctx .Repo .TreePath ))
437
+ if form .CommitChoice == frmCommitChoiceNewBranch {
438
+ ctx .Redirect (ctx .Repo .RepoLink + "/compare/" + ctx .Repo .BranchName + "..." + form .NewBranchName )
429
439
} else {
430
- ctx .Flash .Success (ctx .Tr ("repo.editor.file_delete_success" , ctx .Repo .TreePath ))
431
- ctx .Redirect (ctx .Repo .RepoLink + "/src/branch/" + util .PathEscapeSegments (branchName ))
440
+ treePath := filepath .Dir (ctx .Repo .TreePath )
441
+ if treePath == "." {
442
+ treePath = "" // the file deleted was in the root, so we return the user to the root directory
443
+ }
444
+ if len (treePath ) > 0 {
445
+ // Need to get the latest commit since it changed
446
+ commit , err := ctx .Repo .GitRepo .GetBranchCommit (ctx .Repo .BranchName )
447
+ if err == nil && commit != nil {
448
+ // We have the comment, now find what directory we can return the user to
449
+ // (must have entries)
450
+ treePath = GetClosestParentWithFiles (treePath , commit )
451
+ } else {
452
+ treePath = "" // otherwise return them to the root of the repo
453
+ }
454
+ }
455
+ ctx .Redirect (ctx .Repo .RepoLink + "/src/branch/" + util .PathEscapeSegments (branchName ) + "/" + util .PathEscapeSegments (treePath ))
432
456
}
433
457
}
434
458
@@ -467,7 +491,7 @@ func UploadFile(ctx *context.Context) {
467
491
} else {
468
492
ctx .Data ["commit_choice" ] = frmCommitChoiceNewBranch
469
493
}
470
- ctx .Data ["new_branch_name" ] = ""
494
+ ctx .Data ["new_branch_name" ] = GetUniquePatchBranchName ( ctx )
471
495
472
496
ctx .HTML (200 , tplUploadFile )
473
497
}
@@ -565,7 +589,11 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
565
589
return
566
590
}
567
591
568
- ctx .Redirect (ctx .Repo .RepoLink + "/src/branch/" + util .PathEscapeSegments (branchName ) + "/" + util .PathEscapeSegments (form .TreePath ))
592
+ if form .CommitChoice == frmCommitChoiceNewBranch {
593
+ ctx .Redirect (ctx .Repo .RepoLink + "/compare/" + ctx .Repo .BranchName + "..." + form .NewBranchName )
594
+ } else {
595
+ ctx .Redirect (ctx .Repo .RepoLink + "/src/branch/" + util .PathEscapeSegments (branchName ) + "/" + util .PathEscapeSegments (form .TreePath ))
596
+ }
569
597
}
570
598
571
599
func cleanUploadFileName (name string ) string {
@@ -636,3 +664,40 @@ func RemoveUploadFileFromServer(ctx *context.Context, form auth.RemoveUploadFile
636
664
log .Trace ("Upload file removed: %s" , form .File )
637
665
ctx .Status (204 )
638
666
}
667
+
668
+ // GetUniquePatchBranchName Gets a unique branch name for a new patch branch
669
+ // It will be in the form of <username>-patch-<num> where <num> is the first branch of this format
670
+ // that doesn't already exist. If we exceed 1000 tries or an error is thrown, we just return "" so the user has to
671
+ // type in the branch name themselves (will be an empty field)
672
+ func GetUniquePatchBranchName (ctx * context.Context ) string {
673
+ prefix := ctx .User .LowerName + "-patch-"
674
+ for i := 1 ; i <= 1000 ; i ++ {
675
+ branchName := fmt .Sprintf ("%s%d" , prefix , i )
676
+ if _ , err := ctx .Repo .Repository .GetBranch (branchName ); err != nil {
677
+ if git .IsErrBranchNotExist (err ) {
678
+ return branchName
679
+ }
680
+ log .Error ("GetUniquePatchBranchName: %v" , err )
681
+ return ""
682
+ }
683
+ }
684
+ return ""
685
+ }
686
+
687
+ // GetClosestParentWithFiles Recursively gets the path of parent in a tree that has files (used when file in a tree is
688
+ // deleted). Returns "" for the root if no parents other than the root have files. If the given treePath isn't a
689
+ // SubTree or it has no entries, we go up one dir and see if we can return the user to that listing.
690
+ func GetClosestParentWithFiles (treePath string , commit * git.Commit ) string {
691
+ if len (treePath ) == 0 || treePath == "." {
692
+ return ""
693
+ }
694
+ // see if the tree has entries
695
+ if tree , err := commit .SubTree (treePath ); err != nil {
696
+ // failed to get tree, going up a dir
697
+ return GetClosestParentWithFiles (filepath .Dir (treePath ), commit )
698
+ } else if entries , err := tree .ListEntries (); err != nil || len (entries ) == 0 {
699
+ // no files in this dir, going up a dir
700
+ return GetClosestParentWithFiles (filepath .Dir (treePath ), commit )
701
+ }
702
+ return treePath
703
+ }
0 commit comments