@@ -30,8 +30,11 @@ import (
30
30
"code.gitea.io/gitea/modules/setting"
31
31
"code.gitea.io/gitea/modules/templates"
32
32
"code.gitea.io/gitea/modules/util"
33
+ "code.gitea.io/gitea/modules/web"
33
34
asymkey_service "code.gitea.io/gitea/services/asymkey"
34
35
"code.gitea.io/gitea/services/context"
36
+ "code.gitea.io/gitea/services/context/upload"
37
+ "code.gitea.io/gitea/services/forms"
35
38
git_service "code.gitea.io/gitea/services/git"
36
39
"code.gitea.io/gitea/services/gitdiff"
37
40
repo_service "code.gitea.io/gitea/services/repository"
@@ -417,6 +420,25 @@ func Diff(ctx *context.Context) {
417
420
ctx .Data ["MergedPRIssueNumber" ] = pr .Index
418
421
}
419
422
423
+ // Load commit comments for inline display
424
+ comments , err := issues_model .FindCommitComments (ctx , ctx .Repo .Repository .ID , commitID )
425
+ if err != nil {
426
+ log .Error ("FindCommitComments: %v" , err )
427
+ } else {
428
+ if err := comments .LoadPosters (ctx ); err != nil {
429
+ log .Error ("LoadPosters: %v" , err )
430
+ }
431
+ if err := comments .LoadAttachments (ctx ); err != nil {
432
+ log .Error ("LoadAttachments: %v" , err )
433
+ }
434
+ ctx .Data ["CommitComments" ] = comments
435
+ }
436
+
437
+ // Mark this as a commit page to enable comment UI
438
+ ctx .Data ["PageIsCommit" ] = true
439
+ ctx .Data ["IsAttachmentEnabled" ] = setting .Attachment .Enabled
440
+ upload .AddUploadContext (ctx , "comment" )
441
+
420
442
ctx .HTML (http .StatusOK , tplCommitPage )
421
443
}
422
444
@@ -469,3 +491,135 @@ func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) ([]*git_m
469
491
}
470
492
return commits , nil
471
493
}
494
+
495
+ // RenderNewCommitCodeCommentForm renders the form for creating a new commit code comment
496
+ func RenderNewCommitCodeCommentForm (ctx * context.Context ) {
497
+ ctx .Data ["PageIsCommit" ] = true
498
+ ctx .Data ["AfterCommitID" ] = ctx .PathParam ("sha" )
499
+ ctx .Data ["IsAttachmentEnabled" ] = setting .Attachment .Enabled
500
+ upload .AddUploadContext (ctx , "comment" )
501
+ // Use the same template as PR new comments (defined in pull_review.go)
502
+ ctx .HTML (http .StatusOK , "repo/diff/new_comment" )
503
+ }
504
+
505
+ // CreateCommitCodeComment creates an inline comment on a commit
506
+ func CreateCommitCodeComment (ctx * context.Context ) {
507
+ form := web .GetForm (ctx ).(* forms.CodeCommentForm )
508
+ commitSHA := ctx .PathParam ("sha" )
509
+
510
+ if ctx .Written () {
511
+ return
512
+ }
513
+
514
+ if ctx .HasError () {
515
+ ctx .Flash .Error (ctx .Data ["ErrorMsg" ].(string ))
516
+ ctx .Redirect (fmt .Sprintf ("%s/commit/%s" , ctx .Repo .RepoLink , commitSHA ))
517
+ return
518
+ }
519
+
520
+ // Convert line to signed line (negative for previous side)
521
+ signedLine := form .Line
522
+ if form .Side == "previous" {
523
+ signedLine *= - 1
524
+ }
525
+
526
+ var attachments []string
527
+ if setting .Attachment .Enabled {
528
+ attachments = form .Files
529
+ }
530
+
531
+ // Create the comment using the service layer
532
+ comment , err := repo_service .CreateCommitCodeComment (
533
+ ctx ,
534
+ ctx .Doer ,
535
+ ctx .Repo .Repository ,
536
+ ctx .Repo .GitRepo ,
537
+ commitSHA ,
538
+ signedLine ,
539
+ form .Content ,
540
+ form .TreePath ,
541
+ attachments ,
542
+ )
543
+ if err != nil {
544
+ ctx .ServerError ("CreateCommitCodeComment" , err )
545
+ return
546
+ }
547
+
548
+ log .Trace ("Commit comment created: %d for commit %s in %-v" , comment .ID , commitSHA , ctx .Repo .Repository )
549
+
550
+ // Render the comment
551
+ ctx .Data ["Comment" ] = comment
552
+ ctx .Data ["IsAttachmentEnabled" ] = setting .Attachment .Enabled
553
+ upload .AddUploadContext (ctx , "comment" )
554
+
555
+ ctx .JSON (http .StatusOK , map [string ]any {
556
+ "ok" : true ,
557
+ "comment" : comment ,
558
+ })
559
+ }
560
+
561
+ // UpdateCommitCodeComment updates an existing commit inline comment
562
+ func UpdateCommitCodeComment (ctx * context.Context ) {
563
+ form := web .GetForm (ctx ).(* forms.CodeCommentForm )
564
+ commentID := ctx .PathParamInt64 (":id" )
565
+
566
+ comment , err := issues_model .GetCommentByID (ctx , commentID )
567
+ if err != nil {
568
+ ctx .ServerError ("GetCommentByID" , err )
569
+ return
570
+ }
571
+
572
+ // Verify this is a commit comment
573
+ if comment .Type != issues_model .CommentTypeCode || comment .CommitSHA == "" {
574
+ ctx .NotFound (errors .New ("not a commit code comment" ))
575
+ return
576
+ }
577
+
578
+ // Verify the comment belongs to this repository
579
+ if comment .PosterID != ctx .Doer .ID {
580
+ ctx .HTTPError (http .StatusForbidden )
581
+ return
582
+ }
583
+
584
+ var attachments []string
585
+ if setting .Attachment .Enabled {
586
+ attachments = form .Files
587
+ }
588
+
589
+ // Update the comment
590
+ if err := repo_service .UpdateCommitCodeComment (ctx , ctx .Doer , comment , form .Content , attachments ); err != nil {
591
+ ctx .ServerError ("UpdateCommitCodeComment" , err )
592
+ return
593
+ }
594
+
595
+ ctx .JSON (http .StatusOK , map [string ]any {
596
+ "ok" : true ,
597
+ })
598
+ }
599
+
600
+ // DeleteCommitCodeComment deletes a commit inline comment
601
+ func DeleteCommitCodeComment (ctx * context.Context ) {
602
+ commentID := ctx .PathParamInt64 (":id" )
603
+
604
+ comment , err := issues_model .GetCommentByID (ctx , commentID )
605
+ if err != nil {
606
+ ctx .ServerError ("GetCommentByID" , err )
607
+ return
608
+ }
609
+
610
+ // Verify this is a commit comment
611
+ if comment .Type != issues_model .CommentTypeCode || comment .CommitSHA == "" {
612
+ ctx .NotFound (errors .New ("not a commit code comment" ))
613
+ return
614
+ }
615
+
616
+ // Delete the comment
617
+ if err := repo_service .DeleteCommitCodeComment (ctx , ctx .Doer , comment ); err != nil {
618
+ ctx .ServerError ("DeleteCommitCodeComment" , err )
619
+ return
620
+ }
621
+
622
+ ctx .JSON (http .StatusOK , map [string ]any {
623
+ "ok" : true ,
624
+ })
625
+ }
0 commit comments