Skip to content

Commit 3ab580c

Browse files
Bwkolunny
authored andcommitted
Add branch overiew page (#2108)
* Add branch overiew page * fix changed method name on sub menu * remove unused code
1 parent e86a0bf commit 3ab580c

File tree

21 files changed

+700
-51
lines changed

21 files changed

+700
-51
lines changed

integrations/branches_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"net/http"
9+
"net/url"
10+
"testing"
11+
12+
"github.com/PuerkitoBio/goquery"
13+
"github.com/Unknwon/i18n"
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestViewBranches(t *testing.T) {
18+
prepareTestEnv(t)
19+
20+
req := NewRequest(t, "GET", "/user2/repo1/branches")
21+
resp := MakeRequest(t, req, http.StatusOK)
22+
23+
htmlDoc := NewHTMLParser(t, resp.Body)
24+
_, exists := htmlDoc.doc.Find(".delete-branch-button").Attr("data-url")
25+
assert.False(t, exists, "The template has changed")
26+
}
27+
28+
func TestDeleteBranch(t *testing.T) {
29+
prepareTestEnv(t)
30+
31+
deleteBranch(t)
32+
}
33+
34+
func TestUndoDeleteBranch(t *testing.T) {
35+
prepareTestEnv(t)
36+
37+
deleteBranch(t)
38+
htmlDoc, name := branchAction(t, ".undo-button")
39+
assert.Contains(t,
40+
htmlDoc.doc.Find(".ui.positive.message").Text(),
41+
i18n.Tr("en", "repo.branch.restore_success", name),
42+
)
43+
}
44+
45+
func deleteBranch(t *testing.T) {
46+
htmlDoc, name := branchAction(t, ".delete-branch-button")
47+
assert.Contains(t,
48+
htmlDoc.doc.Find(".ui.positive.message").Text(),
49+
i18n.Tr("en", "repo.branch.deletion_success", name),
50+
)
51+
}
52+
53+
func branchAction(t *testing.T, button string) (*HTMLDoc, string) {
54+
session := loginUser(t, "user2")
55+
req := NewRequest(t, "GET", "/user2/repo1/branches")
56+
resp := session.MakeRequest(t, req, http.StatusOK)
57+
58+
htmlDoc := NewHTMLParser(t, resp.Body)
59+
link, exists := htmlDoc.doc.Find(button).Attr("data-url")
60+
assert.True(t, exists, "The template has changed")
61+
62+
htmlDoc = NewHTMLParser(t, resp.Body)
63+
req = NewRequestWithValues(t, "POST", link, map[string]string{
64+
"_csrf": getCsrf(htmlDoc.doc),
65+
})
66+
resp = session.MakeRequest(t, req, http.StatusOK)
67+
68+
url, err := url.Parse(link)
69+
assert.NoError(t, err)
70+
req = NewRequest(t, "GET", "/user2/repo1/branches")
71+
resp = session.MakeRequest(t, req, http.StatusOK)
72+
73+
return NewHTMLParser(t, resp.Body), url.Query()["name"][0]
74+
}
75+
76+
func getCsrf(doc *goquery.Document) string {
77+
csrf, _ := doc.Find("meta[name=\"_csrf\"]").Attr("content")
78+
return csrf
79+
}

models/branches.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"code.gitea.io/gitea/modules/base"
1313
"code.gitea.io/gitea/modules/log"
14+
"code.gitea.io/gitea/modules/setting"
1415
"code.gitea.io/gitea/modules/util"
1516

1617
"github.com/Unknwon/com"
@@ -193,3 +194,109 @@ func (repo *Repository) DeleteProtectedBranch(id int64) (err error) {
193194

194195
return sess.Commit()
195196
}
197+
198+
// DeletedBranch struct
199+
type DeletedBranch struct {
200+
ID int64 `xorm:"pk autoincr"`
201+
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
202+
Name string `xorm:"UNIQUE(s) NOT NULL"`
203+
Commit string `xorm:"UNIQUE(s) NOT NULL"`
204+
DeletedByID int64 `xorm:"INDEX"`
205+
DeletedBy *User `xorm:"-"`
206+
Deleted time.Time `xorm:"-"`
207+
DeletedUnix int64 `xorm:"INDEX created"`
208+
}
209+
210+
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
211+
func (deletedBranch *DeletedBranch) AfterLoad() {
212+
deletedBranch.Deleted = time.Unix(deletedBranch.DeletedUnix, 0).Local()
213+
}
214+
215+
// AddDeletedBranch adds a deleted branch to the database
216+
func (repo *Repository) AddDeletedBranch(branchName, commit string, deletedByID int64) error {
217+
deletedBranch := &DeletedBranch{
218+
RepoID: repo.ID,
219+
Name: branchName,
220+
Commit: commit,
221+
DeletedByID: deletedByID,
222+
}
223+
224+
sess := x.NewSession()
225+
defer sess.Close()
226+
if err := sess.Begin(); err != nil {
227+
return err
228+
}
229+
230+
if _, err := sess.InsertOne(deletedBranch); err != nil {
231+
return err
232+
}
233+
234+
return sess.Commit()
235+
}
236+
237+
// GetDeletedBranches returns all the deleted branches
238+
func (repo *Repository) GetDeletedBranches() ([]*DeletedBranch, error) {
239+
deletedBranches := make([]*DeletedBranch, 0)
240+
return deletedBranches, x.Where("repo_id = ?", repo.ID).Desc("deleted_unix").Find(&deletedBranches)
241+
}
242+
243+
// GetDeletedBranchByID get a deleted branch by its ID
244+
func (repo *Repository) GetDeletedBranchByID(ID int64) (*DeletedBranch, error) {
245+
deletedBranch := &DeletedBranch{ID: ID}
246+
has, err := x.Get(deletedBranch)
247+
if err != nil {
248+
return nil, err
249+
}
250+
if !has {
251+
return nil, nil
252+
}
253+
return deletedBranch, nil
254+
}
255+
256+
// RemoveDeletedBranch removes a deleted branch from the database
257+
func (repo *Repository) RemoveDeletedBranch(id int64) (err error) {
258+
deletedBranch := &DeletedBranch{
259+
RepoID: repo.ID,
260+
ID: id,
261+
}
262+
263+
sess := x.NewSession()
264+
defer sess.Close()
265+
if err = sess.Begin(); err != nil {
266+
return err
267+
}
268+
269+
if affected, err := sess.Delete(deletedBranch); err != nil {
270+
return err
271+
} else if affected != 1 {
272+
return fmt.Errorf("remove deleted branch ID(%v) failed", id)
273+
}
274+
275+
return sess.Commit()
276+
}
277+
278+
// LoadUser loads the user that deleted the branch
279+
// When there's no user found it returns a NewGhostUser
280+
func (deletedBranch *DeletedBranch) LoadUser() {
281+
user, err := GetUserByID(deletedBranch.DeletedByID)
282+
if err != nil {
283+
user = NewGhostUser()
284+
}
285+
deletedBranch.DeletedBy = user
286+
}
287+
288+
// RemoveOldDeletedBranches removes old deleted branches
289+
func RemoveOldDeletedBranches() {
290+
if !taskStatusTable.StartIfNotRunning(`deleted_branches_cleanup`) {
291+
return
292+
}
293+
defer taskStatusTable.Stop(`deleted_branches_cleanup`)
294+
295+
log.Trace("Doing: DeletedBranchesCleanup")
296+
297+
deleteBefore := time.Now().Add(-setting.Cron.DeletedBranchesCleanup.OlderThan)
298+
_, err := x.Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch))
299+
if err != nil {
300+
log.Error(4, "DeletedBranchesCleanup: %v", err)
301+
}
302+
}

models/branches_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package models
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
var firstBranch = DeletedBranch{
14+
ID: 1,
15+
Name: "foo",
16+
Commit: "1213212312313213213132131",
17+
DeletedByID: int64(1),
18+
}
19+
20+
var secondBranch = DeletedBranch{
21+
ID: 2,
22+
Name: "bar",
23+
Commit: "5655464564554545466464655",
24+
DeletedByID: int64(99),
25+
}
26+
27+
func TestAddDeletedBranch(t *testing.T) {
28+
assert.NoError(t, PrepareTestDatabase())
29+
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
30+
assert.NoError(t, repo.AddDeletedBranch(firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
31+
assert.Error(t, repo.AddDeletedBranch(firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
32+
assert.NoError(t, repo.AddDeletedBranch(secondBranch.Name, secondBranch.Commit, secondBranch.DeletedByID))
33+
}
34+
func TestGetDeletedBranches(t *testing.T) {
35+
assert.NoError(t, PrepareTestDatabase())
36+
AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1})
37+
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
38+
39+
branches, err := repo.GetDeletedBranches()
40+
assert.NoError(t, err)
41+
assert.Len(t, branches, 2)
42+
}
43+
44+
func TestGetDeletedBranch(t *testing.T) {
45+
assert.NoError(t, PrepareTestDatabase())
46+
assert.NotNil(t, getDeletedBranch(t, firstBranch))
47+
}
48+
49+
func TestDeletedBranchLoadUser(t *testing.T) {
50+
assert.NoError(t, PrepareTestDatabase())
51+
branch := getDeletedBranch(t, firstBranch)
52+
assert.Nil(t, branch.DeletedBy)
53+
branch.LoadUser()
54+
assert.NotNil(t, branch.DeletedBy)
55+
assert.Equal(t, "user1", branch.DeletedBy.Name)
56+
57+
branch = getDeletedBranch(t, secondBranch)
58+
assert.Nil(t, branch.DeletedBy)
59+
branch.LoadUser()
60+
assert.NotNil(t, branch.DeletedBy)
61+
assert.Equal(t, "Ghost", branch.DeletedBy.Name)
62+
}
63+
64+
func TestRemoveDeletedBranch(t *testing.T) {
65+
assert.NoError(t, PrepareTestDatabase())
66+
67+
branch := DeletedBranch{ID: 1}
68+
AssertExistsAndLoadBean(t, &branch)
69+
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
70+
71+
err := repo.RemoveDeletedBranch(1)
72+
assert.NoError(t, err)
73+
AssertNotExistsBean(t, &branch)
74+
AssertExistsAndLoadBean(t, &DeletedBranch{ID: 2})
75+
}
76+
77+
func getDeletedBranch(t *testing.T, branch DeletedBranch) *DeletedBranch {
78+
AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1})
79+
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
80+
81+
deletedBranch, err := repo.GetDeletedBranchByID(branch.ID)
82+
assert.NoError(t, err)
83+
assert.Equal(t, branch.ID, deletedBranch.ID)
84+
assert.Equal(t, branch.Name, deletedBranch.Name)
85+
assert.Equal(t, branch.Commit, deletedBranch.Commit)
86+
assert.Equal(t, branch.DeletedByID, deletedBranch.DeletedByID)
87+
88+
return deletedBranch
89+
}

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ var migrations = []Migration{
142142
NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable),
143143
// v46 -> v47
144144
NewMigration("remove organization watch repositories", removeOrganizationWatchRepo),
145+
// v47 -> v48
146+
NewMigration("add deleted branches", addDeletedBranch),
145147
}
146148

147149
// Migrate database to current version

models/migrations/v47.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"fmt"
9+
10+
"github.com/go-xorm/xorm"
11+
)
12+
13+
func addDeletedBranch(x *xorm.Engine) (err error) {
14+
// DeletedBranch contains the deleted branch information
15+
type DeletedBranch struct {
16+
ID int64 `xorm:"pk autoincr"`
17+
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
18+
Name string `xorm:"UNIQUE(s) NOT NULL"`
19+
Commit string `xorm:"UNIQUE(s) NOT NULL"`
20+
DeletedByID int64 `xorm:"INDEX NOT NULL"`
21+
DeletedUnix int64 `xorm:"INDEX"`
22+
}
23+
24+
if err = x.Sync2(new(DeletedBranch)); err != nil {
25+
return fmt.Errorf("Sync2: %v", err)
26+
}
27+
28+
return nil
29+
}

models/models.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func init() {
114114
new(CommitStatus),
115115
new(Stopwatch),
116116
new(TrackedTime),
117+
new(DeletedBranch),
117118
)
118119

119120
gonicNames := []string{"SSL", "UID"}

modules/cron/cron.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ func NewContext() {
7777
go models.SyncExternalUsers()
7878
}
7979
}
80+
if setting.Cron.DeletedBranchesCleanup.Enabled {
81+
entry, err = c.AddFunc("Remove old deleted branches", setting.Cron.DeletedBranchesCleanup.Schedule, models.RemoveOldDeletedBranches)
82+
if err != nil {
83+
log.Fatal(4, "Cron[Remove old deleted branches]: %v", err)
84+
}
85+
if setting.Cron.DeletedBranchesCleanup.RunAtStart {
86+
entry.Prev = time.Now()
87+
entry.ExecTimes++
88+
go models.RemoveOldDeletedBranches()
89+
}
90+
}
8091
c.Start()
8192
}
8293

modules/setting/setting.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ var (
365365
Schedule string
366366
UpdateExisting bool
367367
} `ini:"cron.sync_external_users"`
368+
DeletedBranchesCleanup struct {
369+
Enabled bool
370+
RunAtStart bool
371+
Schedule string
372+
OlderThan time.Duration
373+
} `ini:"cron.deleted_branches_cleanup"`
368374
}{
369375
UpdateMirror: struct {
370376
Enabled bool
@@ -419,6 +425,17 @@ var (
419425
Schedule: "@every 24h",
420426
UpdateExisting: true,
421427
},
428+
DeletedBranchesCleanup: struct {
429+
Enabled bool
430+
RunAtStart bool
431+
Schedule string
432+
OlderThan time.Duration
433+
}{
434+
Enabled: true,
435+
RunAtStart: true,
436+
Schedule: "@every 24h",
437+
OlderThan: 24 * time.Hour,
438+
},
422439
}
423440

424441
// Git settings

0 commit comments

Comments
 (0)