@@ -12,6 +12,7 @@ import (
1212 "io"
1313 "io/ioutil"
1414 "os"
15+ "path"
1516 "path/filepath"
1617 "sort"
1718 "strconv"
@@ -38,6 +39,7 @@ import (
3839
3940const (
4041 tplSettingsLFS base.TplName = "repo/settings/lfs"
42+ tplSettingsLFSLocks base.TplName = "repo/settings/lfs_locks"
4143 tplSettingsLFSFile base.TplName = "repo/settings/lfs_file"
4244 tplSettingsLFSFileFind base.TplName = "repo/settings/lfs_file_find"
4345 tplSettingsLFSPointers base.TplName = "repo/settings/lfs_pointers"
@@ -58,6 +60,7 @@ func LFSFiles(ctx *context.Context) {
5860 ctx .ServerError ("LFSFiles" , err )
5961 return
6062 }
63+ ctx .Data ["Total" ] = total
6164
6265 pager := context .NewPagination (int (total ), setting .UI .ExplorePagingNum , page , 5 )
6366 ctx .Data ["Title" ] = ctx .Tr ("repo.settings.lfs" )
@@ -72,6 +75,179 @@ func LFSFiles(ctx *context.Context) {
7275 ctx .HTML (200 , tplSettingsLFS )
7376}
7477
78+ // LFSLocks shows a repository's LFS locks
79+ func LFSLocks (ctx * context.Context ) {
80+ if ! setting .LFS .StartServer {
81+ ctx .NotFound ("LFSLocks" , nil )
82+ return
83+ }
84+ ctx .Data ["LFSFilesLink" ] = ctx .Repo .RepoLink + "/settings/lfs"
85+
86+ page := ctx .QueryInt ("page" )
87+ if page <= 1 {
88+ page = 1
89+ }
90+ total , err := models .CountLFSLockByRepoID (ctx .Repo .Repository .ID )
91+ if err != nil {
92+ ctx .ServerError ("LFSLocks" , err )
93+ return
94+ }
95+ ctx .Data ["Total" ] = total
96+
97+ pager := context .NewPagination (int (total ), setting .UI .ExplorePagingNum , page , 5 )
98+ ctx .Data ["Title" ] = ctx .Tr ("repo.settings.lfs_locks" )
99+ ctx .Data ["PageIsSettingsLFS" ] = true
100+ lfsLocks , err := models .GetLFSLockByRepoID (ctx .Repo .Repository .ID , pager .Paginater .Current (), setting .UI .ExplorePagingNum )
101+ if err != nil {
102+ ctx .ServerError ("LFSLocks" , err )
103+ return
104+ }
105+ ctx .Data ["LFSLocks" ] = lfsLocks
106+
107+ if len (lfsLocks ) == 0 {
108+ ctx .Data ["Page" ] = pager
109+ ctx .HTML (200 , tplSettingsLFSLocks )
110+ return
111+ }
112+
113+ // Clone base repo.
114+ tmpBasePath , err := models .CreateTemporaryPath ("locks" )
115+ if err != nil {
116+ log .Error ("Failed to create temporary path: %v" , err )
117+ ctx .ServerError ("LFSLocks" , err )
118+ return
119+ }
120+ defer func () {
121+ if err := models .RemoveTemporaryPath (tmpBasePath ); err != nil {
122+ log .Error ("LFSLocks: RemoveTemporaryPath: %v" , err )
123+ }
124+ }()
125+
126+ if err := git .Clone (ctx .Repo .Repository .RepoPath (), tmpBasePath , git.CloneRepoOptions {
127+ Bare : true ,
128+ Shared : true ,
129+ }); err != nil {
130+ log .Error ("Failed to clone repository: %s (%v)" , ctx .Repo .Repository .FullName (), err )
131+ ctx .ServerError ("LFSLocks" , fmt .Errorf ("Failed to clone repository: %s (%v)" , ctx .Repo .Repository .FullName (), err ))
132+ }
133+
134+ gitRepo , err := git .OpenRepository (tmpBasePath )
135+ if err != nil {
136+ log .Error ("Unable to open temporary repository: %s (%v)" , tmpBasePath , err )
137+ ctx .ServerError ("LFSLocks" , fmt .Errorf ("Failed to open new temporary repository in: %s %v" , tmpBasePath , err ))
138+ }
139+
140+ filenames := make ([]string , len (lfsLocks ))
141+
142+ for i , lock := range lfsLocks {
143+ filenames [i ] = lock .Path
144+ }
145+
146+ if err := gitRepo .ReadTreeToIndex (ctx .Repo .Repository .DefaultBranch ); err != nil {
147+ log .Error ("Unable to read the default branch to the index: %s (%v)" , ctx .Repo .Repository .DefaultBranch , err )
148+ ctx .ServerError ("LFSLocks" , fmt .Errorf ("Unable to read the default branch to the index: %s (%v)" , ctx .Repo .Repository .DefaultBranch , err ))
149+ }
150+
151+ name2attribute2info , err := gitRepo .CheckAttribute (git.CheckAttributeOpts {
152+ Attributes : []string {"lockable" },
153+ Filenames : filenames ,
154+ CachedOnly : true ,
155+ })
156+ if err != nil {
157+ log .Error ("Unable to check attributes in %s (%v)" , tmpBasePath , err )
158+ ctx .ServerError ("LFSLocks" , err )
159+ }
160+
161+ lockables := make ([]bool , len (lfsLocks ))
162+ for i , lock := range lfsLocks {
163+ attribute2info , has := name2attribute2info [lock .Path ]
164+ if ! has {
165+ continue
166+ }
167+ if attribute2info ["lockable" ] != "set" {
168+ continue
169+ }
170+ lockables [i ] = true
171+ }
172+ ctx .Data ["Lockables" ] = lockables
173+
174+ filelist , err := gitRepo .LsFiles (filenames ... )
175+ if err != nil {
176+ log .Error ("Unable to lsfiles in %s (%v)" , tmpBasePath , err )
177+ ctx .ServerError ("LFSLocks" , err )
178+ }
179+
180+ filemap := make (map [string ]bool , len (filelist ))
181+ for _ , name := range filelist {
182+ filemap [name ] = true
183+ }
184+
185+ linkable := make ([]bool , len (lfsLocks ))
186+ for i , lock := range lfsLocks {
187+ linkable [i ] = filemap [lock .Path ]
188+ }
189+ ctx .Data ["Linkable" ] = linkable
190+
191+ ctx .Data ["Page" ] = pager
192+ ctx .HTML (200 , tplSettingsLFSLocks )
193+ }
194+
195+ // LFSLockFile locks a file
196+ func LFSLockFile (ctx * context.Context ) {
197+ if ! setting .LFS .StartServer {
198+ ctx .NotFound ("LFSLocks" , nil )
199+ return
200+ }
201+ originalPath := ctx .Query ("path" )
202+ lockPath := originalPath
203+ if len (lockPath ) == 0 {
204+ ctx .Flash .Error (ctx .Tr ("repo.settings.lfs_invalid_locking_path" , originalPath ))
205+ ctx .Redirect (ctx .Repo .RepoLink + "/settings/lfs/locks" )
206+ return
207+ }
208+ if lockPath [len (lockPath )- 1 ] == '/' {
209+ ctx .Flash .Error (ctx .Tr ("repo.settings.lfs_invalid_lock_directory" , originalPath ))
210+ ctx .Redirect (ctx .Repo .RepoLink + "/settings/lfs/locks" )
211+ return
212+ }
213+ lockPath = path .Clean ("/" + lockPath )[1 :]
214+ if len (lockPath ) == 0 {
215+ ctx .Flash .Error (ctx .Tr ("repo.settings.lfs_invalid_locking_path" , originalPath ))
216+ ctx .Redirect (ctx .Repo .RepoLink + "/settings/lfs/locks" )
217+ return
218+ }
219+
220+ _ , err := models .CreateLFSLock (& models.LFSLock {
221+ Repo : ctx .Repo .Repository ,
222+ Path : lockPath ,
223+ Owner : ctx .User ,
224+ })
225+ if err != nil {
226+ if models .IsErrLFSLockAlreadyExist (err ) {
227+ ctx .Flash .Error (ctx .Tr ("repo.settings.lfs_lock_already_exists" , originalPath ))
228+ ctx .Redirect (ctx .Repo .RepoLink + "/settings/lfs/locks" )
229+ return
230+ }
231+ ctx .ServerError ("LFSLockFile" , err )
232+ return
233+ }
234+ ctx .Redirect (ctx .Repo .RepoLink + "/settings/lfs/locks" )
235+ }
236+
237+ // LFSUnlock forcibly unlocks an LFS lock
238+ func LFSUnlock (ctx * context.Context ) {
239+ if ! setting .LFS .StartServer {
240+ ctx .NotFound ("LFSUnlock" , nil )
241+ return
242+ }
243+ _ , err := models .DeleteLFSLockByID (ctx .ParamsInt64 ("lid" ), ctx .User , true )
244+ if err != nil {
245+ ctx .ServerError ("LFSUnlock" , err )
246+ return
247+ }
248+ ctx .Redirect (ctx .Repo .RepoLink + "/settings/lfs/locks" )
249+ }
250+
75251// LFSFileGet serves a single LFS file
76252func LFSFileGet (ctx * context.Context ) {
77253 if ! setting .LFS .StartServer {
0 commit comments