Skip to content

Commit

Permalink
Add branch overiew page (#2108)
Browse files Browse the repository at this point in the history
* Add branch overiew page

* fix changed method name on sub menu

* remove unused code
  • Loading branch information
Bwko authored and lunny committed Oct 26, 2017
1 parent e86a0bf commit 3ab580c
Show file tree
Hide file tree
Showing 21 changed files with 700 additions and 51 deletions.
79 changes: 79 additions & 0 deletions integrations/branches_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package integrations

import (
"net/http"
"net/url"
"testing"

"github.com/PuerkitoBio/goquery"
"github.com/Unknwon/i18n"
"github.com/stretchr/testify/assert"
)

func TestViewBranches(t *testing.T) {
prepareTestEnv(t)

req := NewRequest(t, "GET", "/user2/repo1/branches")
resp := MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
_, exists := htmlDoc.doc.Find(".delete-branch-button").Attr("data-url")
assert.False(t, exists, "The template has changed")
}

func TestDeleteBranch(t *testing.T) {
prepareTestEnv(t)

deleteBranch(t)
}

func TestUndoDeleteBranch(t *testing.T) {
prepareTestEnv(t)

deleteBranch(t)
htmlDoc, name := branchAction(t, ".undo-button")
assert.Contains(t,
htmlDoc.doc.Find(".ui.positive.message").Text(),
i18n.Tr("en", "repo.branch.restore_success", name),
)
}

func deleteBranch(t *testing.T) {
htmlDoc, name := branchAction(t, ".delete-branch-button")
assert.Contains(t,
htmlDoc.doc.Find(".ui.positive.message").Text(),
i18n.Tr("en", "repo.branch.deletion_success", name),
)
}

func branchAction(t *testing.T, button string) (*HTMLDoc, string) {
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user2/repo1/branches")
resp := session.MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find(button).Attr("data-url")
assert.True(t, exists, "The template has changed")

htmlDoc = NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": getCsrf(htmlDoc.doc),
})
resp = session.MakeRequest(t, req, http.StatusOK)

url, err := url.Parse(link)
assert.NoError(t, err)
req = NewRequest(t, "GET", "/user2/repo1/branches")
resp = session.MakeRequest(t, req, http.StatusOK)

return NewHTMLParser(t, resp.Body), url.Query()["name"][0]
}

func getCsrf(doc *goquery.Document) string {
csrf, _ := doc.Find("meta[name=\"_csrf\"]").Attr("content")
return csrf
}
107 changes: 107 additions & 0 deletions models/branches.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"

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

return sess.Commit()
}

// DeletedBranch struct
type DeletedBranch struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"UNIQUE(s) NOT NULL"`
Commit string `xorm:"UNIQUE(s) NOT NULL"`
DeletedByID int64 `xorm:"INDEX"`
DeletedBy *User `xorm:"-"`
Deleted time.Time `xorm:"-"`
DeletedUnix int64 `xorm:"INDEX created"`
}

// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func (deletedBranch *DeletedBranch) AfterLoad() {
deletedBranch.Deleted = time.Unix(deletedBranch.DeletedUnix, 0).Local()
}

// AddDeletedBranch adds a deleted branch to the database
func (repo *Repository) AddDeletedBranch(branchName, commit string, deletedByID int64) error {
deletedBranch := &DeletedBranch{
RepoID: repo.ID,
Name: branchName,
Commit: commit,
DeletedByID: deletedByID,
}

sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}

if _, err := sess.InsertOne(deletedBranch); err != nil {
return err
}

return sess.Commit()
}

// GetDeletedBranches returns all the deleted branches
func (repo *Repository) GetDeletedBranches() ([]*DeletedBranch, error) {
deletedBranches := make([]*DeletedBranch, 0)
return deletedBranches, x.Where("repo_id = ?", repo.ID).Desc("deleted_unix").Find(&deletedBranches)
}

// GetDeletedBranchByID get a deleted branch by its ID
func (repo *Repository) GetDeletedBranchByID(ID int64) (*DeletedBranch, error) {
deletedBranch := &DeletedBranch{ID: ID}
has, err := x.Get(deletedBranch)
if err != nil {
return nil, err
}
if !has {
return nil, nil
}
return deletedBranch, nil
}

// RemoveDeletedBranch removes a deleted branch from the database
func (repo *Repository) RemoveDeletedBranch(id int64) (err error) {
deletedBranch := &DeletedBranch{
RepoID: repo.ID,
ID: id,
}

sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}

if affected, err := sess.Delete(deletedBranch); err != nil {
return err
} else if affected != 1 {
return fmt.Errorf("remove deleted branch ID(%v) failed", id)
}

return sess.Commit()
}

// LoadUser loads the user that deleted the branch
// When there's no user found it returns a NewGhostUser
func (deletedBranch *DeletedBranch) LoadUser() {
user, err := GetUserByID(deletedBranch.DeletedByID)
if err != nil {
user = NewGhostUser()
}
deletedBranch.DeletedBy = user
}

// RemoveOldDeletedBranches removes old deleted branches
func RemoveOldDeletedBranches() {
if !taskStatusTable.StartIfNotRunning(`deleted_branches_cleanup`) {
return
}
defer taskStatusTable.Stop(`deleted_branches_cleanup`)

log.Trace("Doing: DeletedBranchesCleanup")

deleteBefore := time.Now().Add(-setting.Cron.DeletedBranchesCleanup.OlderThan)
_, err := x.Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch))
if err != nil {
log.Error(4, "DeletedBranchesCleanup: %v", err)
}
}
89 changes: 89 additions & 0 deletions models/branches_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"testing"

"github.com/stretchr/testify/assert"
)

var firstBranch = DeletedBranch{
ID: 1,
Name: "foo",
Commit: "1213212312313213213132131",
DeletedByID: int64(1),
}

var secondBranch = DeletedBranch{
ID: 2,
Name: "bar",
Commit: "5655464564554545466464655",
DeletedByID: int64(99),
}

func TestAddDeletedBranch(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
assert.NoError(t, repo.AddDeletedBranch(firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
assert.Error(t, repo.AddDeletedBranch(firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
assert.NoError(t, repo.AddDeletedBranch(secondBranch.Name, secondBranch.Commit, secondBranch.DeletedByID))
}
func TestGetDeletedBranches(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1})
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)

branches, err := repo.GetDeletedBranches()
assert.NoError(t, err)
assert.Len(t, branches, 2)
}

func TestGetDeletedBranch(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
assert.NotNil(t, getDeletedBranch(t, firstBranch))
}

func TestDeletedBranchLoadUser(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
branch := getDeletedBranch(t, firstBranch)
assert.Nil(t, branch.DeletedBy)
branch.LoadUser()
assert.NotNil(t, branch.DeletedBy)
assert.Equal(t, "user1", branch.DeletedBy.Name)

branch = getDeletedBranch(t, secondBranch)
assert.Nil(t, branch.DeletedBy)
branch.LoadUser()
assert.NotNil(t, branch.DeletedBy)
assert.Equal(t, "Ghost", branch.DeletedBy.Name)
}

func TestRemoveDeletedBranch(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())

branch := DeletedBranch{ID: 1}
AssertExistsAndLoadBean(t, &branch)
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)

err := repo.RemoveDeletedBranch(1)
assert.NoError(t, err)
AssertNotExistsBean(t, &branch)
AssertExistsAndLoadBean(t, &DeletedBranch{ID: 2})
}

func getDeletedBranch(t *testing.T, branch DeletedBranch) *DeletedBranch {
AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1})
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)

deletedBranch, err := repo.GetDeletedBranchByID(branch.ID)
assert.NoError(t, err)
assert.Equal(t, branch.ID, deletedBranch.ID)
assert.Equal(t, branch.Name, deletedBranch.Name)
assert.Equal(t, branch.Commit, deletedBranch.Commit)
assert.Equal(t, branch.DeletedByID, deletedBranch.DeletedByID)

return deletedBranch
}
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ var migrations = []Migration{
NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable),
// v46 -> v47
NewMigration("remove organization watch repositories", removeOrganizationWatchRepo),
// v47 -> v48
NewMigration("add deleted branches", addDeletedBranch),
}

// Migrate database to current version
Expand Down
29 changes: 29 additions & 0 deletions models/migrations/v47.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"fmt"

"github.com/go-xorm/xorm"
)

func addDeletedBranch(x *xorm.Engine) (err error) {
// DeletedBranch contains the deleted branch information
type DeletedBranch struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
Name string `xorm:"UNIQUE(s) NOT NULL"`
Commit string `xorm:"UNIQUE(s) NOT NULL"`
DeletedByID int64 `xorm:"INDEX NOT NULL"`
DeletedUnix int64 `xorm:"INDEX"`
}

if err = x.Sync2(new(DeletedBranch)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}

return nil
}
1 change: 1 addition & 0 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func init() {
new(CommitStatus),
new(Stopwatch),
new(TrackedTime),
new(DeletedBranch),
)

gonicNames := []string{"SSL", "UID"}
Expand Down
11 changes: 11 additions & 0 deletions modules/cron/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ func NewContext() {
go models.SyncExternalUsers()
}
}
if setting.Cron.DeletedBranchesCleanup.Enabled {
entry, err = c.AddFunc("Remove old deleted branches", setting.Cron.DeletedBranchesCleanup.Schedule, models.RemoveOldDeletedBranches)
if err != nil {
log.Fatal(4, "Cron[Remove old deleted branches]: %v", err)
}
if setting.Cron.DeletedBranchesCleanup.RunAtStart {
entry.Prev = time.Now()
entry.ExecTimes++
go models.RemoveOldDeletedBranches()
}
}
c.Start()
}

Expand Down
17 changes: 17 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,12 @@ var (
Schedule string
UpdateExisting bool
} `ini:"cron.sync_external_users"`
DeletedBranchesCleanup struct {
Enabled bool
RunAtStart bool
Schedule string
OlderThan time.Duration
} `ini:"cron.deleted_branches_cleanup"`
}{
UpdateMirror: struct {
Enabled bool
Expand Down Expand Up @@ -419,6 +425,17 @@ var (
Schedule: "@every 24h",
UpdateExisting: true,
},
DeletedBranchesCleanup: struct {
Enabled bool
RunAtStart bool
Schedule string
OlderThan time.Duration
}{
Enabled: true,
RunAtStart: true,
Schedule: "@every 24h",
OlderThan: 24 * time.Hour,
},
}

// Git settings
Expand Down

0 comments on commit 3ab580c

Please sign in to comment.