@@ -12,6 +12,7 @@ import (
12
12
"io"
13
13
"io/ioutil"
14
14
"os"
15
+ "path"
15
16
"path/filepath"
16
17
"sort"
17
18
"strconv"
@@ -38,6 +39,7 @@ import (
38
39
39
40
const (
40
41
tplSettingsLFS base.TplName = "repo/settings/lfs"
42
+ tplSettingsLFSLocks base.TplName = "repo/settings/lfs_locks"
41
43
tplSettingsLFSFile base.TplName = "repo/settings/lfs_file"
42
44
tplSettingsLFSFileFind base.TplName = "repo/settings/lfs_file_find"
43
45
tplSettingsLFSPointers base.TplName = "repo/settings/lfs_pointers"
@@ -58,6 +60,7 @@ func LFSFiles(ctx *context.Context) {
58
60
ctx .ServerError ("LFSFiles" , err )
59
61
return
60
62
}
63
+ ctx .Data ["Total" ] = total
61
64
62
65
pager := context .NewPagination (int (total ), setting .UI .ExplorePagingNum , page , 5 )
63
66
ctx .Data ["Title" ] = ctx .Tr ("repo.settings.lfs" )
@@ -72,6 +75,179 @@ func LFSFiles(ctx *context.Context) {
72
75
ctx .HTML (200 , tplSettingsLFS )
73
76
}
74
77
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
+
75
251
// LFSFileGet serves a single LFS file
76
252
func LFSFileGet (ctx * context.Context ) {
77
253
if ! setting .LFS .StartServer {
0 commit comments