@@ -7,7 +7,6 @@ package models
7
7
8
8
import (
9
9
"bufio"
10
- "bytes"
11
10
"fmt"
12
11
"io/ioutil"
13
12
"os"
@@ -18,7 +17,6 @@ import (
18
17
"time"
19
18
20
19
"code.gitea.io/gitea/modules/base"
21
- "code.gitea.io/gitea/modules/cache"
22
20
"code.gitea.io/gitea/modules/git"
23
21
"code.gitea.io/gitea/modules/log"
24
22
"code.gitea.io/gitea/modules/process"
@@ -361,284 +359,8 @@ func (pr *PullRequest) CheckUserAllowedToMerge(doer *User) (err error) {
361
359
return nil
362
360
}
363
361
364
- func getDiffTree (repoPath , baseBranch , headBranch string ) (string , error ) {
365
- getDiffTreeFromBranch := func (repoPath , baseBranch , headBranch string ) (string , error ) {
366
- var outbuf , errbuf strings.Builder
367
- // Compute the diff-tree for sparse-checkout
368
- // The branch argument must be enclosed with double-quotes ("") in case it contains slashes (e.g "feature/test")
369
- if err := git .NewCommand ("diff-tree" , "--no-commit-id" , "--name-only" , "-r" , "--root" , baseBranch , headBranch ).RunInDirPipeline (repoPath , & outbuf , & errbuf ); err != nil {
370
- return "" , fmt .Errorf ("git diff-tree [%s base:%s head:%s]: %s" , repoPath , baseBranch , headBranch , errbuf .String ())
371
- }
372
- return outbuf .String (), nil
373
- }
374
-
375
- list , err := getDiffTreeFromBranch (repoPath , baseBranch , headBranch )
376
- if err != nil {
377
- return "" , err
378
- }
379
-
380
- // Prefixing '/' for each entry, otherwise all files with the same name in subdirectories would be matched.
381
- out := bytes.Buffer {}
382
- scanner := bufio .NewScanner (strings .NewReader (list ))
383
- for scanner .Scan () {
384
- fmt .Fprintf (& out , "/%s\n " , scanner .Text ())
385
- }
386
- return out .String (), nil
387
- }
388
-
389
- // Merge merges pull request to base repository.
390
- // FIXME: add repoWorkingPull make sure two merges does not happen at same time.
391
- func (pr * PullRequest ) Merge (doer * User , baseGitRepo * git.Repository , mergeStyle MergeStyle , message string ) (err error ) {
392
- if err = pr .GetHeadRepo (); err != nil {
393
- return fmt .Errorf ("GetHeadRepo: %v" , err )
394
- } else if err = pr .GetBaseRepo (); err != nil {
395
- return fmt .Errorf ("GetBaseRepo: %v" , err )
396
- }
397
-
398
- prUnit , err := pr .BaseRepo .GetUnit (UnitTypePullRequests )
399
- if err != nil {
400
- return err
401
- }
402
- prConfig := prUnit .PullRequestsConfig ()
403
-
404
- if err := pr .CheckUserAllowedToMerge (doer ); err != nil {
405
- return fmt .Errorf ("CheckUserAllowedToMerge: %v" , err )
406
- }
407
-
408
- // Check if merge style is correct and allowed
409
- if ! prConfig .IsMergeStyleAllowed (mergeStyle ) {
410
- return ErrInvalidMergeStyle {pr .BaseRepo .ID , mergeStyle }
411
- }
412
-
413
- defer func () {
414
- go HookQueue .Add (pr .BaseRepo .ID )
415
- go AddTestPullRequestTask (doer , pr .BaseRepo .ID , pr .BaseBranch , false )
416
- }()
417
-
418
- // Clone base repo.
419
- tmpBasePath , err := CreateTemporaryPath ("merge" )
420
- if err != nil {
421
- return err
422
- }
423
- defer RemoveTemporaryPath (tmpBasePath )
424
-
425
- headRepoPath := RepoPath (pr .HeadUserName , pr .HeadRepo .Name )
426
-
427
- if err := git .Clone (baseGitRepo .Path , tmpBasePath , git.CloneRepoOptions {
428
- Shared : true ,
429
- NoCheckout : true ,
430
- Branch : pr .BaseBranch ,
431
- }); err != nil {
432
- return fmt .Errorf ("git clone: %v" , err )
433
- }
434
-
435
- remoteRepoName := "head_repo"
436
-
437
- // Add head repo remote.
438
- addCacheRepo := func (staging , cache string ) error {
439
- p := filepath .Join (staging , ".git" , "objects" , "info" , "alternates" )
440
- f , err := os .OpenFile (p , os .O_APPEND | os .O_WRONLY , 0600 )
441
- if err != nil {
442
- return err
443
- }
444
- defer f .Close ()
445
- data := filepath .Join (cache , "objects" )
446
- if _ , err := fmt .Fprintln (f , data ); err != nil {
447
- return err
448
- }
449
- return nil
450
- }
451
-
452
- if err := addCacheRepo (tmpBasePath , headRepoPath ); err != nil {
453
- return fmt .Errorf ("addCacheRepo [%s -> %s]: %v" , headRepoPath , tmpBasePath , err )
454
- }
455
-
456
- var errbuf strings.Builder
457
- if err := git .NewCommand ("remote" , "add" , remoteRepoName , headRepoPath ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
458
- return fmt .Errorf ("git remote add [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
459
- }
460
-
461
- // Fetch head branch
462
- if err := git .NewCommand ("fetch" , remoteRepoName ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
463
- return fmt .Errorf ("git fetch [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
464
- }
465
-
466
- trackingBranch := path .Join (remoteRepoName , pr .HeadBranch )
467
- stagingBranch := fmt .Sprintf ("%s_%s" , remoteRepoName , pr .HeadBranch )
468
-
469
- // Enable sparse-checkout
470
- sparseCheckoutList , err := getDiffTree (tmpBasePath , pr .BaseBranch , trackingBranch )
471
- if err != nil {
472
- return fmt .Errorf ("getDiffTree: %v" , err )
473
- }
474
-
475
- infoPath := filepath .Join (tmpBasePath , ".git" , "info" )
476
- if err := os .MkdirAll (infoPath , 0700 ); err != nil {
477
- return fmt .Errorf ("creating directory failed [%s]: %v" , infoPath , err )
478
- }
479
- sparseCheckoutListPath := filepath .Join (infoPath , "sparse-checkout" )
480
- if err := ioutil .WriteFile (sparseCheckoutListPath , []byte (sparseCheckoutList ), 0600 ); err != nil {
481
- return fmt .Errorf ("Writing sparse-checkout file to %s: %v" , sparseCheckoutListPath , err )
482
- }
483
-
484
- if err := git .NewCommand ("config" , "--local" , "core.sparseCheckout" , "true" ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
485
- return fmt .Errorf ("git config [core.sparsecheckout -> true]: %v" , errbuf .String ())
486
- }
487
-
488
- // Read base branch index
489
- if err := git .NewCommand ("read-tree" , "HEAD" ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
490
- return fmt .Errorf ("git read-tree HEAD: %s" , errbuf .String ())
491
- }
492
-
493
- // Merge commits.
494
- switch mergeStyle {
495
- case MergeStyleMerge :
496
- if err := git .NewCommand ("merge" , "--no-ff" , "--no-commit" , trackingBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
497
- return fmt .Errorf ("git merge --no-ff --no-commit [%s]: %v - %s" , tmpBasePath , err , errbuf .String ())
498
- }
499
-
500
- sig := doer .NewGitSig ()
501
- if err := git .NewCommand ("commit" , fmt .Sprintf ("--author='%s <%s>'" , sig .Name , sig .Email ), "-m" , message ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
502
- return fmt .Errorf ("git commit [%s]: %v - %s" , tmpBasePath , err , errbuf .String ())
503
- }
504
- case MergeStyleRebase :
505
- // Checkout head branch
506
- if err := git .NewCommand ("checkout" , "-b" , stagingBranch , trackingBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
507
- return fmt .Errorf ("git checkout: %s" , errbuf .String ())
508
- }
509
- // Rebase before merging
510
- if err := git .NewCommand ("rebase" , "-q" , pr .BaseBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
511
- return fmt .Errorf ("git rebase [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
512
- }
513
- // Checkout base branch again
514
- if err := git .NewCommand ("checkout" , pr .BaseBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
515
- return fmt .Errorf ("git checkout: %s" , errbuf .String ())
516
- }
517
- // Merge fast forward
518
- if err := git .NewCommand ("merge" , "--ff-only" , "-q" , stagingBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
519
- return fmt .Errorf ("git merge --ff-only [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
520
- }
521
- case MergeStyleRebaseMerge :
522
- // Checkout head branch
523
- if err := git .NewCommand ("checkout" , "-b" , stagingBranch , trackingBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
524
- return fmt .Errorf ("git checkout: %s" , errbuf .String ())
525
- }
526
- // Rebase before merging
527
- if err := git .NewCommand ("rebase" , "-q" , pr .BaseBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
528
- return fmt .Errorf ("git rebase [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
529
- }
530
- // Checkout base branch again
531
- if err := git .NewCommand ("checkout" , pr .BaseBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
532
- return fmt .Errorf ("git checkout: %s" , errbuf .String ())
533
- }
534
- // Prepare merge with commit
535
- if err := git .NewCommand ("merge" , "--no-ff" , "--no-commit" , "-q" , stagingBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
536
- return fmt .Errorf ("git merge --no-ff [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
537
- }
538
-
539
- // Set custom message and author and create merge commit
540
- sig := doer .NewGitSig ()
541
- if err := git .NewCommand ("commit" , fmt .Sprintf ("--author='%s <%s>'" , sig .Name , sig .Email ), "-m" , message ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
542
- return fmt .Errorf ("git commit [%s]: %v - %s" , tmpBasePath , err , errbuf .String ())
543
- }
544
-
545
- case MergeStyleSquash :
546
- // Merge with squash
547
- if err := git .NewCommand ("merge" , "-q" , "--squash" , trackingBranch ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
548
- return fmt .Errorf ("git merge --squash [%s -> %s]: %s" , headRepoPath , tmpBasePath , errbuf .String ())
549
- }
550
- sig := pr .Issue .Poster .NewGitSig ()
551
- if err := git .NewCommand ("commit" , fmt .Sprintf ("--author='%s <%s>'" , sig .Name , sig .Email ), "-m" , message ).RunInDirPipeline (tmpBasePath , nil , & errbuf ); err != nil {
552
- return fmt .Errorf ("git commit [%s]: %v - %s" , tmpBasePath , err , errbuf .String ())
553
- }
554
- default :
555
- return ErrInvalidMergeStyle {pr .BaseRepo .ID , mergeStyle }
556
- }
557
-
558
- env := PushingEnvironment (doer , pr .BaseRepo )
559
-
560
- // Push back to upstream.
561
- if err := git .NewCommand ("push" , baseGitRepo .Path , pr .BaseBranch ).RunInDirTimeoutEnvPipeline (env , - 1 , tmpBasePath , nil , & errbuf ); err != nil {
562
- return fmt .Errorf ("git push: %s" , errbuf .String ())
563
- }
564
-
565
- pr .MergedCommitID , err = baseGitRepo .GetBranchCommitID (pr .BaseBranch )
566
- if err != nil {
567
- return fmt .Errorf ("GetBranchCommit: %v" , err )
568
- }
569
-
570
- pr .MergedUnix = util .TimeStampNow ()
571
- pr .Merger = doer
572
- pr .MergerID = doer .ID
573
-
574
- if err = pr .setMerged (); err != nil {
575
- log .Error ("setMerged [%d]: %v" , pr .ID , err )
576
- }
577
-
578
- if err = MergePullRequestAction (doer , pr .Issue .Repo , pr .Issue ); err != nil {
579
- log .Error ("MergePullRequestAction [%d]: %v" , pr .ID , err )
580
- }
581
-
582
- // Reset cached commit count
583
- cache .Remove (pr .Issue .Repo .GetCommitsCountCacheKey (pr .BaseBranch , true ))
584
-
585
- // Reload pull request information.
586
- if err = pr .LoadAttributes (); err != nil {
587
- log .Error ("LoadAttributes: %v" , err )
588
- return nil
589
- }
590
-
591
- mode , _ := AccessLevel (doer , pr .Issue .Repo )
592
- if err = PrepareWebhooks (pr .Issue .Repo , HookEventPullRequest , & api.PullRequestPayload {
593
- Action : api .HookIssueClosed ,
594
- Index : pr .Index ,
595
- PullRequest : pr .APIFormat (),
596
- Repository : pr .Issue .Repo .APIFormat (mode ),
597
- Sender : doer .APIFormat (),
598
- }); err != nil {
599
- log .Error ("PrepareWebhooks: %v" , err )
600
- } else {
601
- go HookQueue .Add (pr .Issue .Repo .ID )
602
- }
603
-
604
- l , err := baseGitRepo .CommitsBetweenIDs (pr .MergedCommitID , pr .MergeBase )
605
- if err != nil {
606
- log .Error ("CommitsBetweenIDs: %v" , err )
607
- return nil
608
- }
609
-
610
- // It is possible that head branch is not fully sync with base branch for merge commits,
611
- // so we need to get latest head commit and append merge commit manually
612
- // to avoid strange diff commits produced.
613
- mergeCommit , err := baseGitRepo .GetBranchCommit (pr .BaseBranch )
614
- if err != nil {
615
- log .Error ("GetBranchCommit: %v" , err )
616
- return nil
617
- }
618
- if mergeStyle == MergeStyleMerge {
619
- l .PushFront (mergeCommit )
620
- }
621
-
622
- p := & api.PushPayload {
623
- Ref : git .BranchPrefix + pr .BaseBranch ,
624
- Before : pr .MergeBase ,
625
- After : mergeCommit .ID .String (),
626
- CompareURL : setting .AppURL + pr .BaseRepo .ComposeCompareURL (pr .MergeBase , pr .MergedCommitID ),
627
- Commits : ListToPushCommits (l ).ToAPIPayloadCommits (pr .BaseRepo .HTMLURL ()),
628
- Repo : pr .BaseRepo .APIFormat (mode ),
629
- Pusher : pr .HeadRepo .MustOwner ().APIFormat (),
630
- Sender : doer .APIFormat (),
631
- }
632
- if err = PrepareWebhooks (pr .BaseRepo , HookEventPush , p ); err != nil {
633
- log .Error ("PrepareWebhooks: %v" , err )
634
- } else {
635
- go HookQueue .Add (pr .BaseRepo .ID )
636
- }
637
- return nil
638
- }
639
-
640
- // setMerged sets a pull request to merged and closes the corresponding issue
641
- func (pr * PullRequest ) setMerged () (err error ) {
362
+ // SetMerged sets a pull request to merged and closes the corresponding issue
363
+ func (pr * PullRequest ) SetMerged () (err error ) {
642
364
if pr .HasMerged {
643
365
return fmt .Errorf ("PullRequest[%d] already merged" , pr .Index )
644
366
}
@@ -705,7 +427,7 @@ func (pr *PullRequest) manuallyMerged() bool {
705
427
pr .Merger = merger
706
428
pr .MergerID = merger .ID
707
429
708
- if err = pr .setMerged (); err != nil {
430
+ if err = pr .SetMerged (); err != nil {
709
431
log .Error ("PullRequest[%d].setMerged : %v" , pr .ID , err )
710
432
return false
711
433
}
0 commit comments