Add basic repository lfs management (#7199)
This PR adds basic repository LFS management UI including the ability to find all possible pointers within the repository. Locks are not managed at present but would be addable through some simple additions. * Add basic repository lfs management * add auto-associate function * Add functionality to find commits with this lfs file * Add link to find commits on the lfs file view * Adjust commit view to state the likely branch causing the commit * Only read Oid from database
This commit is contained in:
parent
af8957bc4c
commit
5e6a008fba
20 changed files with 1151 additions and 137 deletions
|
@ -8,6 +8,8 @@ import (
|
|||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// LFSMetaObject stores metadata for LFS tracked files.
|
||||
|
@ -106,19 +108,91 @@ func (repo *Repository) GetLFSMetaObjectByOid(oid string) (*LFSMetaObject, error
|
|||
|
||||
// RemoveLFSMetaObjectByOid removes a LFSMetaObject entry from database by its OID.
|
||||
// It may return ErrLFSObjectNotExist or a database error.
|
||||
func (repo *Repository) RemoveLFSMetaObjectByOid(oid string) error {
|
||||
func (repo *Repository) RemoveLFSMetaObjectByOid(oid string) (int64, error) {
|
||||
if len(oid) == 0 {
|
||||
return ErrLFSObjectNotExist
|
||||
return 0, ErrLFSObjectNotExist
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
m := &LFSMetaObject{Oid: oid, RepositoryID: repo.ID}
|
||||
if _, err := sess.Delete(m); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
count, err := sess.Count(&LFSMetaObject{Oid: oid})
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
|
||||
return count, sess.Commit()
|
||||
}
|
||||
|
||||
// GetLFSMetaObjects returns all LFSMetaObjects associated with a repository
|
||||
func (repo *Repository) GetLFSMetaObjects(page, pageSize int) ([]*LFSMetaObject, error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if page >= 0 && pageSize > 0 {
|
||||
start := 0
|
||||
if page > 0 {
|
||||
start = (page - 1) * pageSize
|
||||
}
|
||||
sess.Limit(pageSize, start)
|
||||
}
|
||||
lfsObjects := make([]*LFSMetaObject, 0, pageSize)
|
||||
return lfsObjects, sess.Find(&lfsObjects, &LFSMetaObject{RepositoryID: repo.ID})
|
||||
}
|
||||
|
||||
// CountLFSMetaObjects returns a count of all LFSMetaObjects associated with a repository
|
||||
func (repo *Repository) CountLFSMetaObjects() (int64, error) {
|
||||
return x.Count(&LFSMetaObject{RepositoryID: repo.ID})
|
||||
}
|
||||
|
||||
// LFSObjectAccessible checks if a provided Oid is accessible to the user
|
||||
func LFSObjectAccessible(user *User, oid string) (bool, error) {
|
||||
if user.IsAdmin {
|
||||
count, err := x.Count(&LFSMetaObject{Oid: oid})
|
||||
return (count > 0), err
|
||||
}
|
||||
cond := accessibleRepositoryCondition(user.ID)
|
||||
count, err := x.Where(cond).Join("INNER", "repository", "`lfs_meta_object`.repository_id = `repository`.id").Count(&LFSMetaObject{Oid: oid})
|
||||
return (count > 0), err
|
||||
}
|
||||
|
||||
// LFSAutoAssociate auto associates accessible LFSMetaObjects
|
||||
func LFSAutoAssociate(metas []*LFSMetaObject, user *User, repoID int64) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := &LFSMetaObject{Oid: oid, RepositoryID: repo.ID}
|
||||
if _, err := sess.Delete(m); err != nil {
|
||||
oids := make([]interface{}, len(metas))
|
||||
oidMap := make(map[string]*LFSMetaObject, len(metas))
|
||||
for i, meta := range metas {
|
||||
oids[i] = meta.Oid
|
||||
oidMap[meta.Oid] = meta
|
||||
}
|
||||
|
||||
cond := builder.NewCond()
|
||||
if !user.IsAdmin {
|
||||
cond = builder.In("`lfs_meta_object`.repository_id",
|
||||
builder.Select("`repository`.id").From("repository").Where(accessibleRepositoryCondition(user.ID)))
|
||||
}
|
||||
newMetas := make([]*LFSMetaObject, 0, len(metas))
|
||||
if err := sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range newMetas {
|
||||
newMetas[i].Size = oidMap[newMetas[i].Oid].Size
|
||||
newMetas[i].RepositoryID = repoID
|
||||
}
|
||||
if _, err := sess.InsertMulti(newMetas); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -176,28 +176,7 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
|||
if opts.Private {
|
||||
if !opts.UserIsAdmin && opts.UserID != 0 && opts.UserID != opts.OwnerID {
|
||||
// OK we're in the context of a User
|
||||
// We should be Either
|
||||
cond = cond.And(builder.Or(
|
||||
// 1. Be able to see all non-private repositories that either:
|
||||
cond.And(
|
||||
builder.Eq{"is_private": false},
|
||||
builder.Or(
|
||||
// A. Aren't in organisations __OR__
|
||||
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
|
||||
// B. Isn't a private organisation. (Limited is OK because we're logged in)
|
||||
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePrivate}))),
|
||||
),
|
||||
// 2. Be able to see all repositories that we have access to
|
||||
builder.In("id", builder.Select("repo_id").
|
||||
From("`access`").
|
||||
Where(builder.And(
|
||||
builder.Eq{"user_id": opts.UserID},
|
||||
builder.Gt{"mode": int(AccessModeNone)}))),
|
||||
// 3. Be able to see all repositories that we are in a team
|
||||
builder.In("id", builder.Select("`team_repo`.repo_id").
|
||||
From("team_repo").
|
||||
Where(builder.Eq{"`team_user`.uid": opts.UserID}).
|
||||
Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id"))))
|
||||
cond = cond.And(accessibleRepositoryCondition(opts.UserID))
|
||||
}
|
||||
} else {
|
||||
// Not looking at private organisations
|
||||
|
@ -316,6 +295,31 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
|||
return repos, count, nil
|
||||
}
|
||||
|
||||
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
|
||||
func accessibleRepositoryCondition(userID int64) builder.Cond {
|
||||
return builder.Or(
|
||||
// 1. Be able to see all non-private repositories that either:
|
||||
builder.And(
|
||||
builder.Eq{"`repository`.is_private": false},
|
||||
builder.Or(
|
||||
// A. Aren't in organisations __OR__
|
||||
builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
|
||||
// B. Isn't a private organisation. (Limited is OK because we're logged in)
|
||||
builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePrivate}))),
|
||||
),
|
||||
// 2. Be able to see all repositories that we have access to
|
||||
builder.In("`repository`.id", builder.Select("repo_id").
|
||||
From("`access`").
|
||||
Where(builder.And(
|
||||
builder.Eq{"user_id": userID},
|
||||
builder.Gt{"mode": int(AccessModeNone)}))),
|
||||
// 3. Be able to see all repositories that we are in a team
|
||||
builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").
|
||||
From("team_repo").
|
||||
Where(builder.Eq{"`team_user`.uid": userID}).
|
||||
Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id")))
|
||||
}
|
||||
|
||||
// SearchRepositoryByName takes keyword and part of repository name to search,
|
||||
// it returns results in given range and number of total results.
|
||||
func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue