Skip to content

Commit 6ca2711

Browse files
committed
Merge remote-tracking branch 'upstream/main'
* upstream/main: Add pages to view watched repos and subscribed issues/PRs (go-gitea#17156) Fix the hook related FAQ contents (go-gitea#21297) Check if email is used when updating user (go-gitea#21289) Add name field for org api (go-gitea#21270) Add API endpoint to get changed files of a PR (go-gitea#21177)
2 parents 5f9a9b9 + 08609d4 commit 6ca2711

File tree

21 files changed

+790
-24
lines changed

21 files changed

+790
-24
lines changed

docs/content/doc/help/faq.en-us.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,13 @@ Our translations are currently crowd-sourced on our [Crowdin project](https://cr
214214

215215
Whether you want to change a translation or add a new one, it will need to be there as all translations are overwritten in our CI via the Crowdin integration.
216216

217-
## Hooks aren't running
217+
## Push Hook / Webhook aren't running
218218

219-
If Gitea is not running hooks, a common cause is incorrect setup of SSH keys.
219+
If you can push but can't see push activities on the home dashboard, or the push doesn't trigger webhook, there are a few possibilities:
220220

221-
See [SSH Issues](#ssh-issues) for more information.
222-
223-
You can also try logging into the administration panel and running the `Resynchronize pre-receive, update and post-receive hooks of all repositories.` option.
221+
1. The git hooks are out of sync: run "Resynchronize pre-receive, update and post-receive hooks of all repositories" on the site admin panel
222+
2. The git repositories (and hooks) are stored on some filesystems (ex: mounted by NAS) which don't support script execution, make sure the filesystem supports `chmod a+x any-script`
223+
3. If you are using docker, make sure Docker Server (not the client) >= 20.10.6
224224

225225
## SSH issues
226226

models/fixtures/pull_request.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@
6060
head_repo_id: 1
6161
base_repo_id: 1
6262
head_branch: pr-to-update
63-
base_branch: branch1
64-
merge_base: 1234567890abcdef
63+
base_branch: branch2
64+
merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee
6565
has_merged: false
6666

6767
-

models/issues/issue.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,7 @@ type IssuesOptions struct { //nolint
11861186
PosterID int64
11871187
MentionedID int64
11881188
ReviewRequestedID int64
1189+
SubscriberID int64
11891190
MilestoneIDs []int64
11901191
ProjectID int64
11911192
ProjectBoardID int64
@@ -1299,6 +1300,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
12991300
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
13001301
}
13011302

1303+
if opts.SubscriberID > 0 {
1304+
applySubscribedCondition(sess, opts.SubscriberID)
1305+
}
1306+
13021307
if len(opts.MilestoneIDs) > 0 {
13031308
sess.In("issue.milestone_id", opts.MilestoneIDs)
13041309
}
@@ -1463,6 +1468,36 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
14631468
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
14641469
}
14651470

1471+
func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
1472+
return sess.And(
1473+
builder.
1474+
NotIn("issue.id",
1475+
builder.Select("issue_id").
1476+
From("issue_watch").
1477+
Where(builder.Eq{"is_watching": false, "user_id": subscriberID}),
1478+
),
1479+
).And(
1480+
builder.Or(
1481+
builder.In("issue.id", builder.
1482+
Select("issue_id").
1483+
From("issue_watch").
1484+
Where(builder.Eq{"is_watching": true, "user_id": subscriberID}),
1485+
),
1486+
builder.In("issue.id", builder.
1487+
Select("issue_id").
1488+
From("comment").
1489+
Where(builder.Eq{"poster_id": subscriberID}),
1490+
),
1491+
builder.Eq{"issue.poster_id": subscriberID},
1492+
builder.In("issue.repo_id", builder.
1493+
Select("id").
1494+
From("watch").
1495+
Where(builder.Eq{"user_id": subscriberID, "mode": true}),
1496+
),
1497+
),
1498+
)
1499+
}
1500+
14661501
// CountIssuesByRepo map from repoID to number of issues matching the options
14671502
func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
14681503
e := db.GetEngine(db.DefaultContext)

models/user/user.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -893,14 +893,19 @@ func UpdateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...s
893893
if err != nil {
894894
return err
895895
}
896-
if !has {
897-
// 1. Update old primary email
898-
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
899-
IsPrimary: false,
900-
}); err != nil {
901-
return err
896+
if has && emailAddress.UID != u.ID {
897+
return ErrEmailAlreadyUsed{
898+
Email: u.Email,
902899
}
900+
}
901+
// 1. Update old primary email
902+
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
903+
IsPrimary: false,
904+
}); err != nil {
905+
return err
906+
}
903907

908+
if !has {
904909
emailAddress.Email = u.Email
905910
emailAddress.UID = u.ID
906911
emailAddress.IsActivated = true

models/user/user_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,26 @@ func TestUpdateUser(t *testing.T) {
302302
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
303303
assert.True(t, user.KeepActivityPrivate)
304304

305+
newEmail := "new_" + user.Email
306+
user.Email = newEmail
307+
assert.NoError(t, user_model.UpdateUser(db.DefaultContext, user, true))
308+
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
309+
assert.Equal(t, newEmail, user.Email)
310+
305311
user.Email = "no [email protected]"
306312
assert.Error(t, user_model.UpdateUser(db.DefaultContext, user, true))
307313
}
308314

315+
func TestUpdateUserEmailAlreadyUsed(t *testing.T) {
316+
assert.NoError(t, unittest.PrepareTestDatabase())
317+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
318+
user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
319+
320+
user2.Email = user3.Email
321+
err := user_model.UpdateUser(db.DefaultContext, user2, true)
322+
assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
323+
}
324+
309325
func TestNewUserRedirect(t *testing.T) {
310326
// redirect to a completely new name
311327
assert.NoError(t, unittest.PrepareTestDatabase())

modules/convert/convert.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"code.gitea.io/gitea/modules/log"
2828
api "code.gitea.io/gitea/modules/structs"
2929
"code.gitea.io/gitea/modules/util"
30+
"code.gitea.io/gitea/services/gitdiff"
3031
webhook_service "code.gitea.io/gitea/services/webhook"
3132
)
3233

@@ -295,6 +296,7 @@ func ToOrganization(org *organization.Organization) *api.Organization {
295296
return &api.Organization{
296297
ID: org.ID,
297298
AvatarURL: org.AsUser().AvatarLink(),
299+
Name: org.Name,
298300
UserName: org.Name,
299301
FullName: org.FullName,
300302
Description: org.Description,
@@ -414,3 +416,36 @@ func ToLFSLock(l *git_model.LFSLock) *api.LFSLock {
414416
},
415417
}
416418
}
419+
420+
// ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
421+
func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
422+
status := "changed"
423+
if f.IsDeleted {
424+
status = "deleted"
425+
} else if f.IsCreated {
426+
status = "added"
427+
} else if f.IsRenamed && f.Type == gitdiff.DiffFileCopy {
428+
status = "copied"
429+
} else if f.IsRenamed && f.Type == gitdiff.DiffFileRename {
430+
status = "renamed"
431+
} else if f.Addition == 0 && f.Deletion == 0 {
432+
status = "unchanged"
433+
}
434+
435+
file := &api.ChangedFile{
436+
Filename: f.GetDiffFileName(),
437+
Status: status,
438+
Additions: f.Addition,
439+
Deletions: f.Deletion,
440+
Changes: f.Addition + f.Deletion,
441+
HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
442+
ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
443+
RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
444+
}
445+
446+
if status == "rename" {
447+
file.PreviousFilename = f.OldName
448+
}
449+
450+
return file
451+
}

modules/structs/org.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ package structs
77
// Organization represents an organization
88
type Organization struct {
99
ID int64 `json:"id"`
10-
UserName string `json:"username"`
10+
Name string `json:"name"`
1111
FullName string `json:"full_name"`
1212
AvatarURL string `json:"avatar_url"`
1313
Description string `json:"description"`
1414
Website string `json:"website"`
1515
Location string `json:"location"`
1616
Visibility string `json:"visibility"`
1717
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
18+
// deprecated
19+
UserName string `json:"username"`
1820
}
1921

2022
// OrganizationPermissions list different users permissions on an organization

modules/structs/pull.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,16 @@ type EditPullRequestOption struct {
9595
RemoveDeadline *bool `json:"unset_due_date"`
9696
AllowMaintainerEdit *bool `json:"allow_maintainer_edit"`
9797
}
98+
99+
// ChangedFile store information about files affected by the pull request
100+
type ChangedFile struct {
101+
Filename string `json:"filename"`
102+
PreviousFilename string `json:"previous_filename,omitempty"`
103+
Status string `json:"status"`
104+
Additions int `json:"additions"`
105+
Deletions int `json:"deletions"`
106+
Changes int `json:"changes"`
107+
HTMLURL string `json:"html_url,omitempty"`
108+
ContentsURL string `json:"contents_url,omitempty"`
109+
RawURL string `json:"raw_url,omitempty"`
110+
}

options/locale/locale_en-US.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3034,6 +3034,9 @@ pin = Pin notification
30343034
mark_as_read = Mark as read
30353035
mark_as_unread = Mark as unread
30363036
mark_all_as_read = Mark all as read
3037+
subscriptions = Subscriptions
3038+
watching = Watching
3039+
no_subscriptions = No subscriptions
30373040

30383041
[gpg]
30393042
default_key=Signed with default key

routers/api/v1/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,7 @@ func Routes(ctx gocontext.Context) *web.Route {
10021002
m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch)
10031003
m.Post("/update", reqToken(), repo.UpdatePullRequest)
10041004
m.Get("/commits", repo.GetPullRequestCommits)
1005+
m.Get("/files", repo.GetPullRequestFiles)
10051006
m.Combo("/merge").Get(repo.IsPullRequestMerged).
10061007
Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).
10071008
Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge)

routers/api/v1/repo/pull.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ import (
2626
"code.gitea.io/gitea/modules/git"
2727
"code.gitea.io/gitea/modules/log"
2828
"code.gitea.io/gitea/modules/notification"
29+
"code.gitea.io/gitea/modules/setting"
2930
api "code.gitea.io/gitea/modules/structs"
3031
"code.gitea.io/gitea/modules/timeutil"
3132
"code.gitea.io/gitea/modules/web"
3233
"code.gitea.io/gitea/routers/api/v1/utils"
3334
asymkey_service "code.gitea.io/gitea/services/asymkey"
3435
"code.gitea.io/gitea/services/automerge"
3536
"code.gitea.io/gitea/services/forms"
37+
"code.gitea.io/gitea/services/gitdiff"
3638
issue_service "code.gitea.io/gitea/services/issue"
3739
pull_service "code.gitea.io/gitea/services/pull"
3840
repo_service "code.gitea.io/gitea/services/repository"
@@ -1323,3 +1325,137 @@ func GetPullRequestCommits(ctx *context.APIContext) {
13231325

13241326
ctx.JSON(http.StatusOK, &apiCommits)
13251327
}
1328+
1329+
// GetPullRequestFiles gets all changed files associated with a given PR
1330+
func GetPullRequestFiles(ctx *context.APIContext) {
1331+
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/files repository repoGetPullRequestFiles
1332+
// ---
1333+
// summary: Get changed files for a pull request
1334+
// produces:
1335+
// - application/json
1336+
// parameters:
1337+
// - name: owner
1338+
// in: path
1339+
// description: owner of the repo
1340+
// type: string
1341+
// required: true
1342+
// - name: repo
1343+
// in: path
1344+
// description: name of the repo
1345+
// type: string
1346+
// required: true
1347+
// - name: index
1348+
// in: path
1349+
// description: index of the pull request to get
1350+
// type: integer
1351+
// format: int64
1352+
// required: true
1353+
// - name: skip-to
1354+
// in: query
1355+
// description: skip to given file
1356+
// type: string
1357+
// - name: whitespace
1358+
// in: query
1359+
// description: whitespace behavior
1360+
// type: string
1361+
// enum: [ignore-all, ignore-change, ignore-eol, show-all]
1362+
// - name: page
1363+
// in: query
1364+
// description: page number of results to return (1-based)
1365+
// type: integer
1366+
// - name: limit
1367+
// in: query
1368+
// description: page size of results
1369+
// type: integer
1370+
// responses:
1371+
// "200":
1372+
// "$ref": "#/responses/ChangedFileList"
1373+
// "404":
1374+
// "$ref": "#/responses/notFound"
1375+
1376+
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
1377+
if err != nil {
1378+
if issues_model.IsErrPullRequestNotExist(err) {
1379+
ctx.NotFound()
1380+
} else {
1381+
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
1382+
}
1383+
return
1384+
}
1385+
1386+
if err := pr.LoadBaseRepo(); err != nil {
1387+
ctx.InternalServerError(err)
1388+
return
1389+
}
1390+
1391+
if err := pr.LoadHeadRepo(); err != nil {
1392+
ctx.InternalServerError(err)
1393+
return
1394+
}
1395+
1396+
baseGitRepo := ctx.Repo.GitRepo
1397+
1398+
var prInfo *git.CompareInfo
1399+
if pr.HasMerged {
1400+
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true, false)
1401+
} else {
1402+
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false)
1403+
}
1404+
if err != nil {
1405+
ctx.ServerError("GetCompareInfo", err)
1406+
return
1407+
}
1408+
1409+
headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
1410+
if err != nil {
1411+
ctx.ServerError("GetRefCommitID", err)
1412+
return
1413+
}
1414+
1415+
startCommitID := prInfo.MergeBase
1416+
endCommitID := headCommitID
1417+
1418+
maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles
1419+
1420+
diff, err := gitdiff.GetDiff(baseGitRepo,
1421+
&gitdiff.DiffOptions{
1422+
BeforeCommitID: startCommitID,
1423+
AfterCommitID: endCommitID,
1424+
SkipTo: ctx.FormString("skip-to"),
1425+
MaxLines: maxLines,
1426+
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
1427+
MaxFiles: maxFiles,
1428+
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
1429+
})
1430+
if err != nil {
1431+
ctx.ServerError("GetDiff", err)
1432+
return
1433+
}
1434+
1435+
listOptions := utils.GetListOptions(ctx)
1436+
1437+
totalNumberOfFiles := diff.NumFiles
1438+
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
1439+
1440+
start, end := listOptions.GetStartEnd()
1441+
1442+
if end > totalNumberOfFiles {
1443+
end = totalNumberOfFiles
1444+
}
1445+
1446+
apiFiles := make([]*api.ChangedFile, 0, end-start)
1447+
for i := start; i < end; i++ {
1448+
apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.HeadRepo, endCommitID))
1449+
}
1450+
1451+
ctx.SetLinkHeader(totalNumberOfFiles, listOptions.PageSize)
1452+
ctx.SetTotalCountHeader(int64(totalNumberOfFiles))
1453+
1454+
ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
1455+
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
1456+
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
1457+
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
1458+
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")
1459+
1460+
ctx.JSON(http.StatusOK, &apiFiles)
1461+
}

0 commit comments

Comments
 (0)