##// END OF EJS Templates
largefiles: access to specific fields only if largefiles enabled (issue4547)...
FUJIWARA Katsunori -
r24158:d414c28d stable
parent child Browse files
Show More
@@ -1,586 +1,586 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''largefiles utility code: must not import other modules in this package.'''
9 '''largefiles utility code: must not import other modules in this package.'''
10
10
11 import os
11 import os
12 import platform
12 import platform
13 import shutil
13 import shutil
14 import stat
14 import stat
15 import copy
15 import copy
16
16
17 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
17 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
18 from mercurial.i18n import _
18 from mercurial.i18n import _
19 from mercurial import node
19 from mercurial import node
20
20
21 shortname = '.hglf'
21 shortname = '.hglf'
22 shortnameslash = shortname + '/'
22 shortnameslash = shortname + '/'
23 longname = 'largefiles'
23 longname = 'largefiles'
24
24
25
25
26 # -- Private worker functions ------------------------------------------
26 # -- Private worker functions ------------------------------------------
27
27
28 def getminsize(ui, assumelfiles, opt, default=10):
28 def getminsize(ui, assumelfiles, opt, default=10):
29 lfsize = opt
29 lfsize = opt
30 if not lfsize and assumelfiles:
30 if not lfsize and assumelfiles:
31 lfsize = ui.config(longname, 'minsize', default=default)
31 lfsize = ui.config(longname, 'minsize', default=default)
32 if lfsize:
32 if lfsize:
33 try:
33 try:
34 lfsize = float(lfsize)
34 lfsize = float(lfsize)
35 except ValueError:
35 except ValueError:
36 raise util.Abort(_('largefiles: size must be number (not %s)\n')
36 raise util.Abort(_('largefiles: size must be number (not %s)\n')
37 % lfsize)
37 % lfsize)
38 if lfsize is None:
38 if lfsize is None:
39 raise util.Abort(_('minimum size for largefiles must be specified'))
39 raise util.Abort(_('minimum size for largefiles must be specified'))
40 return lfsize
40 return lfsize
41
41
42 def link(src, dest):
42 def link(src, dest):
43 util.makedirs(os.path.dirname(dest))
43 util.makedirs(os.path.dirname(dest))
44 try:
44 try:
45 util.oslink(src, dest)
45 util.oslink(src, dest)
46 except OSError:
46 except OSError:
47 # if hardlinks fail, fallback on atomic copy
47 # if hardlinks fail, fallback on atomic copy
48 dst = util.atomictempfile(dest)
48 dst = util.atomictempfile(dest)
49 for chunk in util.filechunkiter(open(src, 'rb')):
49 for chunk in util.filechunkiter(open(src, 'rb')):
50 dst.write(chunk)
50 dst.write(chunk)
51 dst.close()
51 dst.close()
52 os.chmod(dest, os.stat(src).st_mode)
52 os.chmod(dest, os.stat(src).st_mode)
53
53
54 def usercachepath(ui, hash):
54 def usercachepath(ui, hash):
55 path = ui.configpath(longname, 'usercache', None)
55 path = ui.configpath(longname, 'usercache', None)
56 if path:
56 if path:
57 path = os.path.join(path, hash)
57 path = os.path.join(path, hash)
58 else:
58 else:
59 if os.name == 'nt':
59 if os.name == 'nt':
60 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
60 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
61 if appdata:
61 if appdata:
62 path = os.path.join(appdata, longname, hash)
62 path = os.path.join(appdata, longname, hash)
63 elif platform.system() == 'Darwin':
63 elif platform.system() == 'Darwin':
64 home = os.getenv('HOME')
64 home = os.getenv('HOME')
65 if home:
65 if home:
66 path = os.path.join(home, 'Library', 'Caches',
66 path = os.path.join(home, 'Library', 'Caches',
67 longname, hash)
67 longname, hash)
68 elif os.name == 'posix':
68 elif os.name == 'posix':
69 path = os.getenv('XDG_CACHE_HOME')
69 path = os.getenv('XDG_CACHE_HOME')
70 if path:
70 if path:
71 path = os.path.join(path, longname, hash)
71 path = os.path.join(path, longname, hash)
72 else:
72 else:
73 home = os.getenv('HOME')
73 home = os.getenv('HOME')
74 if home:
74 if home:
75 path = os.path.join(home, '.cache', longname, hash)
75 path = os.path.join(home, '.cache', longname, hash)
76 else:
76 else:
77 raise util.Abort(_('unknown operating system: %s\n') % os.name)
77 raise util.Abort(_('unknown operating system: %s\n') % os.name)
78 return path
78 return path
79
79
80 def inusercache(ui, hash):
80 def inusercache(ui, hash):
81 path = usercachepath(ui, hash)
81 path = usercachepath(ui, hash)
82 return path and os.path.exists(path)
82 return path and os.path.exists(path)
83
83
84 def findfile(repo, hash):
84 def findfile(repo, hash):
85 if instore(repo, hash):
85 if instore(repo, hash):
86 repo.ui.note(_('found %s in store\n') % hash)
86 repo.ui.note(_('found %s in store\n') % hash)
87 return storepath(repo, hash)
87 return storepath(repo, hash)
88 elif inusercache(repo.ui, hash):
88 elif inusercache(repo.ui, hash):
89 repo.ui.note(_('found %s in system cache\n') % hash)
89 repo.ui.note(_('found %s in system cache\n') % hash)
90 path = storepath(repo, hash)
90 path = storepath(repo, hash)
91 link(usercachepath(repo.ui, hash), path)
91 link(usercachepath(repo.ui, hash), path)
92 return path
92 return path
93 return None
93 return None
94
94
95 class largefilesdirstate(dirstate.dirstate):
95 class largefilesdirstate(dirstate.dirstate):
96 def __getitem__(self, key):
96 def __getitem__(self, key):
97 return super(largefilesdirstate, self).__getitem__(unixpath(key))
97 return super(largefilesdirstate, self).__getitem__(unixpath(key))
98 def normal(self, f):
98 def normal(self, f):
99 return super(largefilesdirstate, self).normal(unixpath(f))
99 return super(largefilesdirstate, self).normal(unixpath(f))
100 def remove(self, f):
100 def remove(self, f):
101 return super(largefilesdirstate, self).remove(unixpath(f))
101 return super(largefilesdirstate, self).remove(unixpath(f))
102 def add(self, f):
102 def add(self, f):
103 return super(largefilesdirstate, self).add(unixpath(f))
103 return super(largefilesdirstate, self).add(unixpath(f))
104 def drop(self, f):
104 def drop(self, f):
105 return super(largefilesdirstate, self).drop(unixpath(f))
105 return super(largefilesdirstate, self).drop(unixpath(f))
106 def forget(self, f):
106 def forget(self, f):
107 return super(largefilesdirstate, self).forget(unixpath(f))
107 return super(largefilesdirstate, self).forget(unixpath(f))
108 def normallookup(self, f):
108 def normallookup(self, f):
109 return super(largefilesdirstate, self).normallookup(unixpath(f))
109 return super(largefilesdirstate, self).normallookup(unixpath(f))
110 def _ignore(self, f):
110 def _ignore(self, f):
111 return False
111 return False
112
112
113 def openlfdirstate(ui, repo, create=True):
113 def openlfdirstate(ui, repo, create=True):
114 '''
114 '''
115 Return a dirstate object that tracks largefiles: i.e. its root is
115 Return a dirstate object that tracks largefiles: i.e. its root is
116 the repo root, but it is saved in .hg/largefiles/dirstate.
116 the repo root, but it is saved in .hg/largefiles/dirstate.
117 '''
117 '''
118 lfstoredir = repo.join(longname)
118 lfstoredir = repo.join(longname)
119 opener = scmutil.opener(lfstoredir)
119 opener = scmutil.opener(lfstoredir)
120 lfdirstate = largefilesdirstate(opener, ui, repo.root,
120 lfdirstate = largefilesdirstate(opener, ui, repo.root,
121 repo.dirstate._validate)
121 repo.dirstate._validate)
122
122
123 # If the largefiles dirstate does not exist, populate and create
123 # If the largefiles dirstate does not exist, populate and create
124 # it. This ensures that we create it on the first meaningful
124 # it. This ensures that we create it on the first meaningful
125 # largefiles operation in a new clone.
125 # largefiles operation in a new clone.
126 if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
126 if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
127 matcher = getstandinmatcher(repo)
127 matcher = getstandinmatcher(repo)
128 standins = repo.dirstate.walk(matcher, [], False, False)
128 standins = repo.dirstate.walk(matcher, [], False, False)
129
129
130 if len(standins) > 0:
130 if len(standins) > 0:
131 util.makedirs(lfstoredir)
131 util.makedirs(lfstoredir)
132
132
133 for standin in standins:
133 for standin in standins:
134 lfile = splitstandin(standin)
134 lfile = splitstandin(standin)
135 lfdirstate.normallookup(lfile)
135 lfdirstate.normallookup(lfile)
136 return lfdirstate
136 return lfdirstate
137
137
138 def lfdirstatestatus(lfdirstate, repo):
138 def lfdirstatestatus(lfdirstate, repo):
139 wctx = repo['.']
139 wctx = repo['.']
140 match = match_.always(repo.root, repo.getcwd())
140 match = match_.always(repo.root, repo.getcwd())
141 unsure, s = lfdirstate.status(match, [], False, False, False)
141 unsure, s = lfdirstate.status(match, [], False, False, False)
142 modified, clean = s.modified, s.clean
142 modified, clean = s.modified, s.clean
143 for lfile in unsure:
143 for lfile in unsure:
144 try:
144 try:
145 fctx = wctx[standin(lfile)]
145 fctx = wctx[standin(lfile)]
146 except LookupError:
146 except LookupError:
147 fctx = None
147 fctx = None
148 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
148 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
149 modified.append(lfile)
149 modified.append(lfile)
150 else:
150 else:
151 clean.append(lfile)
151 clean.append(lfile)
152 lfdirstate.normal(lfile)
152 lfdirstate.normal(lfile)
153 return s
153 return s
154
154
155 def listlfiles(repo, rev=None, matcher=None):
155 def listlfiles(repo, rev=None, matcher=None):
156 '''return a list of largefiles in the working copy or the
156 '''return a list of largefiles in the working copy or the
157 specified changeset'''
157 specified changeset'''
158
158
159 if matcher is None:
159 if matcher is None:
160 matcher = getstandinmatcher(repo)
160 matcher = getstandinmatcher(repo)
161
161
162 # ignore unknown files in working directory
162 # ignore unknown files in working directory
163 return [splitstandin(f)
163 return [splitstandin(f)
164 for f in repo[rev].walk(matcher)
164 for f in repo[rev].walk(matcher)
165 if rev is not None or repo.dirstate[f] != '?']
165 if rev is not None or repo.dirstate[f] != '?']
166
166
167 def instore(repo, hash):
167 def instore(repo, hash):
168 return os.path.exists(storepath(repo, hash))
168 return os.path.exists(storepath(repo, hash))
169
169
170 def storepath(repo, hash):
170 def storepath(repo, hash):
171 return repo.join(os.path.join(longname, hash))
171 return repo.join(os.path.join(longname, hash))
172
172
173 def copyfromcache(repo, hash, filename):
173 def copyfromcache(repo, hash, filename):
174 '''Copy the specified largefile from the repo or system cache to
174 '''Copy the specified largefile from the repo or system cache to
175 filename in the repository. Return true on success or false if the
175 filename in the repository. Return true on success or false if the
176 file was not found in either cache (which should not happened:
176 file was not found in either cache (which should not happened:
177 this is meant to be called only after ensuring that the needed
177 this is meant to be called only after ensuring that the needed
178 largefile exists in the cache).'''
178 largefile exists in the cache).'''
179 path = findfile(repo, hash)
179 path = findfile(repo, hash)
180 if path is None:
180 if path is None:
181 return False
181 return False
182 util.makedirs(os.path.dirname(repo.wjoin(filename)))
182 util.makedirs(os.path.dirname(repo.wjoin(filename)))
183 # The write may fail before the file is fully written, but we
183 # The write may fail before the file is fully written, but we
184 # don't use atomic writes in the working copy.
184 # don't use atomic writes in the working copy.
185 shutil.copy(path, repo.wjoin(filename))
185 shutil.copy(path, repo.wjoin(filename))
186 return True
186 return True
187
187
188 def copytostore(repo, rev, file, uploaded=False):
188 def copytostore(repo, rev, file, uploaded=False):
189 hash = readstandin(repo, file, rev)
189 hash = readstandin(repo, file, rev)
190 if instore(repo, hash):
190 if instore(repo, hash):
191 return
191 return
192 copytostoreabsolute(repo, repo.wjoin(file), hash)
192 copytostoreabsolute(repo, repo.wjoin(file), hash)
193
193
194 def copyalltostore(repo, node):
194 def copyalltostore(repo, node):
195 '''Copy all largefiles in a given revision to the store'''
195 '''Copy all largefiles in a given revision to the store'''
196
196
197 ctx = repo[node]
197 ctx = repo[node]
198 for filename in ctx.files():
198 for filename in ctx.files():
199 if isstandin(filename) and filename in ctx.manifest():
199 if isstandin(filename) and filename in ctx.manifest():
200 realfile = splitstandin(filename)
200 realfile = splitstandin(filename)
201 copytostore(repo, ctx.node(), realfile)
201 copytostore(repo, ctx.node(), realfile)
202
202
203
203
204 def copytostoreabsolute(repo, file, hash):
204 def copytostoreabsolute(repo, file, hash):
205 if inusercache(repo.ui, hash):
205 if inusercache(repo.ui, hash):
206 link(usercachepath(repo.ui, hash), storepath(repo, hash))
206 link(usercachepath(repo.ui, hash), storepath(repo, hash))
207 else:
207 else:
208 util.makedirs(os.path.dirname(storepath(repo, hash)))
208 util.makedirs(os.path.dirname(storepath(repo, hash)))
209 dst = util.atomictempfile(storepath(repo, hash),
209 dst = util.atomictempfile(storepath(repo, hash),
210 createmode=repo.store.createmode)
210 createmode=repo.store.createmode)
211 for chunk in util.filechunkiter(open(file, 'rb')):
211 for chunk in util.filechunkiter(open(file, 'rb')):
212 dst.write(chunk)
212 dst.write(chunk)
213 dst.close()
213 dst.close()
214 linktousercache(repo, hash)
214 linktousercache(repo, hash)
215
215
216 def linktousercache(repo, hash):
216 def linktousercache(repo, hash):
217 path = usercachepath(repo.ui, hash)
217 path = usercachepath(repo.ui, hash)
218 if path:
218 if path:
219 link(storepath(repo, hash), path)
219 link(storepath(repo, hash), path)
220
220
221 def getstandinmatcher(repo, pats=[], opts={}):
221 def getstandinmatcher(repo, pats=[], opts={}):
222 '''Return a match object that applies pats to the standin directory'''
222 '''Return a match object that applies pats to the standin directory'''
223 standindir = repo.wjoin(shortname)
223 standindir = repo.wjoin(shortname)
224 if pats:
224 if pats:
225 pats = [os.path.join(standindir, pat) for pat in pats]
225 pats = [os.path.join(standindir, pat) for pat in pats]
226 else:
226 else:
227 # no patterns: relative to repo root
227 # no patterns: relative to repo root
228 pats = [standindir]
228 pats = [standindir]
229 # no warnings about missing files or directories
229 # no warnings about missing files or directories
230 match = scmutil.match(repo[None], pats, opts)
230 match = scmutil.match(repo[None], pats, opts)
231 match.bad = lambda f, msg: None
231 match.bad = lambda f, msg: None
232 return match
232 return match
233
233
234 def composestandinmatcher(repo, rmatcher):
234 def composestandinmatcher(repo, rmatcher):
235 '''Return a matcher that accepts standins corresponding to the
235 '''Return a matcher that accepts standins corresponding to the
236 files accepted by rmatcher. Pass the list of files in the matcher
236 files accepted by rmatcher. Pass the list of files in the matcher
237 as the paths specified by the user.'''
237 as the paths specified by the user.'''
238 smatcher = getstandinmatcher(repo, rmatcher.files())
238 smatcher = getstandinmatcher(repo, rmatcher.files())
239 isstandin = smatcher.matchfn
239 isstandin = smatcher.matchfn
240 def composedmatchfn(f):
240 def composedmatchfn(f):
241 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
241 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
242 smatcher.matchfn = composedmatchfn
242 smatcher.matchfn = composedmatchfn
243
243
244 return smatcher
244 return smatcher
245
245
246 def standin(filename):
246 def standin(filename):
247 '''Return the repo-relative path to the standin for the specified big
247 '''Return the repo-relative path to the standin for the specified big
248 file.'''
248 file.'''
249 # Notes:
249 # Notes:
250 # 1) Some callers want an absolute path, but for instance addlargefiles
250 # 1) Some callers want an absolute path, but for instance addlargefiles
251 # needs it repo-relative so it can be passed to repo[None].add(). So
251 # needs it repo-relative so it can be passed to repo[None].add(). So
252 # leave it up to the caller to use repo.wjoin() to get an absolute path.
252 # leave it up to the caller to use repo.wjoin() to get an absolute path.
253 # 2) Join with '/' because that's what dirstate always uses, even on
253 # 2) Join with '/' because that's what dirstate always uses, even on
254 # Windows. Change existing separator to '/' first in case we are
254 # Windows. Change existing separator to '/' first in case we are
255 # passed filenames from an external source (like the command line).
255 # passed filenames from an external source (like the command line).
256 return shortnameslash + util.pconvert(filename)
256 return shortnameslash + util.pconvert(filename)
257
257
258 def isstandin(filename):
258 def isstandin(filename):
259 '''Return true if filename is a big file standin. filename must be
259 '''Return true if filename is a big file standin. filename must be
260 in Mercurial's internal form (slash-separated).'''
260 in Mercurial's internal form (slash-separated).'''
261 return filename.startswith(shortnameslash)
261 return filename.startswith(shortnameslash)
262
262
263 def splitstandin(filename):
263 def splitstandin(filename):
264 # Split on / because that's what dirstate always uses, even on Windows.
264 # Split on / because that's what dirstate always uses, even on Windows.
265 # Change local separator to / first just in case we are passed filenames
265 # Change local separator to / first just in case we are passed filenames
266 # from an external source (like the command line).
266 # from an external source (like the command line).
267 bits = util.pconvert(filename).split('/', 1)
267 bits = util.pconvert(filename).split('/', 1)
268 if len(bits) == 2 and bits[0] == shortname:
268 if len(bits) == 2 and bits[0] == shortname:
269 return bits[1]
269 return bits[1]
270 else:
270 else:
271 return None
271 return None
272
272
273 def updatestandin(repo, standin):
273 def updatestandin(repo, standin):
274 file = repo.wjoin(splitstandin(standin))
274 file = repo.wjoin(splitstandin(standin))
275 if os.path.exists(file):
275 if os.path.exists(file):
276 hash = hashfile(file)
276 hash = hashfile(file)
277 executable = getexecutable(file)
277 executable = getexecutable(file)
278 writestandin(repo, standin, hash, executable)
278 writestandin(repo, standin, hash, executable)
279
279
280 def readstandin(repo, filename, node=None):
280 def readstandin(repo, filename, node=None):
281 '''read hex hash from standin for filename at given node, or working
281 '''read hex hash from standin for filename at given node, or working
282 directory if no node is given'''
282 directory if no node is given'''
283 return repo[node][standin(filename)].data().strip()
283 return repo[node][standin(filename)].data().strip()
284
284
285 def writestandin(repo, standin, hash, executable):
285 def writestandin(repo, standin, hash, executable):
286 '''write hash to <repo.root>/<standin>'''
286 '''write hash to <repo.root>/<standin>'''
287 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
287 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
288
288
289 def copyandhash(instream, outfile):
289 def copyandhash(instream, outfile):
290 '''Read bytes from instream (iterable) and write them to outfile,
290 '''Read bytes from instream (iterable) and write them to outfile,
291 computing the SHA-1 hash of the data along the way. Return the hash.'''
291 computing the SHA-1 hash of the data along the way. Return the hash.'''
292 hasher = util.sha1('')
292 hasher = util.sha1('')
293 for data in instream:
293 for data in instream:
294 hasher.update(data)
294 hasher.update(data)
295 outfile.write(data)
295 outfile.write(data)
296 return hasher.hexdigest()
296 return hasher.hexdigest()
297
297
298 def hashrepofile(repo, file):
298 def hashrepofile(repo, file):
299 return hashfile(repo.wjoin(file))
299 return hashfile(repo.wjoin(file))
300
300
301 def hashfile(file):
301 def hashfile(file):
302 if not os.path.exists(file):
302 if not os.path.exists(file):
303 return ''
303 return ''
304 hasher = util.sha1('')
304 hasher = util.sha1('')
305 fd = open(file, 'rb')
305 fd = open(file, 'rb')
306 for data in util.filechunkiter(fd, 128 * 1024):
306 for data in util.filechunkiter(fd, 128 * 1024):
307 hasher.update(data)
307 hasher.update(data)
308 fd.close()
308 fd.close()
309 return hasher.hexdigest()
309 return hasher.hexdigest()
310
310
311 def getexecutable(filename):
311 def getexecutable(filename):
312 mode = os.stat(filename).st_mode
312 mode = os.stat(filename).st_mode
313 return ((mode & stat.S_IXUSR) and
313 return ((mode & stat.S_IXUSR) and
314 (mode & stat.S_IXGRP) and
314 (mode & stat.S_IXGRP) and
315 (mode & stat.S_IXOTH))
315 (mode & stat.S_IXOTH))
316
316
317 def urljoin(first, second, *arg):
317 def urljoin(first, second, *arg):
318 def join(left, right):
318 def join(left, right):
319 if not left.endswith('/'):
319 if not left.endswith('/'):
320 left += '/'
320 left += '/'
321 if right.startswith('/'):
321 if right.startswith('/'):
322 right = right[1:]
322 right = right[1:]
323 return left + right
323 return left + right
324
324
325 url = join(first, second)
325 url = join(first, second)
326 for a in arg:
326 for a in arg:
327 url = join(url, a)
327 url = join(url, a)
328 return url
328 return url
329
329
330 def hexsha1(data):
330 def hexsha1(data):
331 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
331 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
332 object data"""
332 object data"""
333 h = util.sha1()
333 h = util.sha1()
334 for chunk in util.filechunkiter(data):
334 for chunk in util.filechunkiter(data):
335 h.update(chunk)
335 h.update(chunk)
336 return h.hexdigest()
336 return h.hexdigest()
337
337
338 def httpsendfile(ui, filename):
338 def httpsendfile(ui, filename):
339 return httpconnection.httpsendfile(ui, filename, 'rb')
339 return httpconnection.httpsendfile(ui, filename, 'rb')
340
340
341 def unixpath(path):
341 def unixpath(path):
342 '''Return a version of path normalized for use with the lfdirstate.'''
342 '''Return a version of path normalized for use with the lfdirstate.'''
343 return util.pconvert(os.path.normpath(path))
343 return util.pconvert(os.path.normpath(path))
344
344
345 def islfilesrepo(repo):
345 def islfilesrepo(repo):
346 if ('largefiles' in repo.requirements and
346 if ('largefiles' in repo.requirements and
347 util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
347 util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
348 return True
348 return True
349
349
350 return util.any(openlfdirstate(repo.ui, repo, False))
350 return util.any(openlfdirstate(repo.ui, repo, False))
351
351
352 class storeprotonotcapable(Exception):
352 class storeprotonotcapable(Exception):
353 def __init__(self, storetypes):
353 def __init__(self, storetypes):
354 self.storetypes = storetypes
354 self.storetypes = storetypes
355
355
356 def getstandinsstate(repo):
356 def getstandinsstate(repo):
357 standins = []
357 standins = []
358 matcher = getstandinmatcher(repo)
358 matcher = getstandinmatcher(repo)
359 for standin in repo.dirstate.walk(matcher, [], False, False):
359 for standin in repo.dirstate.walk(matcher, [], False, False):
360 lfile = splitstandin(standin)
360 lfile = splitstandin(standin)
361 try:
361 try:
362 hash = readstandin(repo, lfile)
362 hash = readstandin(repo, lfile)
363 except IOError:
363 except IOError:
364 hash = None
364 hash = None
365 standins.append((lfile, hash))
365 standins.append((lfile, hash))
366 return standins
366 return standins
367
367
368 def synclfdirstate(repo, lfdirstate, lfile, normallookup):
368 def synclfdirstate(repo, lfdirstate, lfile, normallookup):
369 lfstandin = standin(lfile)
369 lfstandin = standin(lfile)
370 if lfstandin in repo.dirstate:
370 if lfstandin in repo.dirstate:
371 stat = repo.dirstate._map[lfstandin]
371 stat = repo.dirstate._map[lfstandin]
372 state, mtime = stat[0], stat[3]
372 state, mtime = stat[0], stat[3]
373 else:
373 else:
374 state, mtime = '?', -1
374 state, mtime = '?', -1
375 if state == 'n':
375 if state == 'n':
376 if normallookup or mtime < 0:
376 if normallookup or mtime < 0:
377 # state 'n' doesn't ensure 'clean' in this case
377 # state 'n' doesn't ensure 'clean' in this case
378 lfdirstate.normallookup(lfile)
378 lfdirstate.normallookup(lfile)
379 else:
379 else:
380 lfdirstate.normal(lfile)
380 lfdirstate.normal(lfile)
381 elif state == 'm':
381 elif state == 'm':
382 lfdirstate.normallookup(lfile)
382 lfdirstate.normallookup(lfile)
383 elif state == 'r':
383 elif state == 'r':
384 lfdirstate.remove(lfile)
384 lfdirstate.remove(lfile)
385 elif state == 'a':
385 elif state == 'a':
386 lfdirstate.add(lfile)
386 lfdirstate.add(lfile)
387 elif state == '?':
387 elif state == '?':
388 lfdirstate.drop(lfile)
388 lfdirstate.drop(lfile)
389
389
390 def markcommitted(orig, ctx, node):
390 def markcommitted(orig, ctx, node):
391 repo = ctx._repo
391 repo = ctx._repo
392
392
393 orig(node)
393 orig(node)
394
394
395 # ATTENTION: "ctx.files()" may differ from "repo[node].files()"
395 # ATTENTION: "ctx.files()" may differ from "repo[node].files()"
396 # because files coming from the 2nd parent are omitted in the latter.
396 # because files coming from the 2nd parent are omitted in the latter.
397 #
397 #
398 # The former should be used to get targets of "synclfdirstate",
398 # The former should be used to get targets of "synclfdirstate",
399 # because such files:
399 # because such files:
400 # - are marked as "a" by "patch.patch()" (e.g. via transplant), and
400 # - are marked as "a" by "patch.patch()" (e.g. via transplant), and
401 # - have to be marked as "n" after commit, but
401 # - have to be marked as "n" after commit, but
402 # - aren't listed in "repo[node].files()"
402 # - aren't listed in "repo[node].files()"
403
403
404 lfdirstate = openlfdirstate(repo.ui, repo)
404 lfdirstate = openlfdirstate(repo.ui, repo)
405 for f in ctx.files():
405 for f in ctx.files():
406 if isstandin(f):
406 if isstandin(f):
407 lfile = splitstandin(f)
407 lfile = splitstandin(f)
408 synclfdirstate(repo, lfdirstate, lfile, False)
408 synclfdirstate(repo, lfdirstate, lfile, False)
409 lfdirstate.write()
409 lfdirstate.write()
410
410
411 # As part of committing, copy all of the largefiles into the cache.
411 # As part of committing, copy all of the largefiles into the cache.
412 copyalltostore(repo, node)
412 copyalltostore(repo, node)
413
413
414 def getlfilestoupdate(oldstandins, newstandins):
414 def getlfilestoupdate(oldstandins, newstandins):
415 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
415 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
416 filelist = []
416 filelist = []
417 for f in changedstandins:
417 for f in changedstandins:
418 if f[0] not in filelist:
418 if f[0] not in filelist:
419 filelist.append(f[0])
419 filelist.append(f[0])
420 return filelist
420 return filelist
421
421
422 def getlfilestoupload(repo, missing, addfunc):
422 def getlfilestoupload(repo, missing, addfunc):
423 for i, n in enumerate(missing):
423 for i, n in enumerate(missing):
424 repo.ui.progress(_('finding outgoing largefiles'), i,
424 repo.ui.progress(_('finding outgoing largefiles'), i,
425 unit=_('revision'), total=len(missing))
425 unit=_('revision'), total=len(missing))
426 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
426 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
427
427
428 oldlfstatus = repo.lfstatus
428 oldlfstatus = repo.lfstatus
429 repo.lfstatus = False
429 repo.lfstatus = False
430 try:
430 try:
431 ctx = repo[n]
431 ctx = repo[n]
432 finally:
432 finally:
433 repo.lfstatus = oldlfstatus
433 repo.lfstatus = oldlfstatus
434
434
435 files = set(ctx.files())
435 files = set(ctx.files())
436 if len(parents) == 2:
436 if len(parents) == 2:
437 mc = ctx.manifest()
437 mc = ctx.manifest()
438 mp1 = ctx.parents()[0].manifest()
438 mp1 = ctx.parents()[0].manifest()
439 mp2 = ctx.parents()[1].manifest()
439 mp2 = ctx.parents()[1].manifest()
440 for f in mp1:
440 for f in mp1:
441 if f not in mc:
441 if f not in mc:
442 files.add(f)
442 files.add(f)
443 for f in mp2:
443 for f in mp2:
444 if f not in mc:
444 if f not in mc:
445 files.add(f)
445 files.add(f)
446 for f in mc:
446 for f in mc:
447 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
447 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
448 files.add(f)
448 files.add(f)
449 for fn in files:
449 for fn in files:
450 if isstandin(fn) and fn in ctx:
450 if isstandin(fn) and fn in ctx:
451 addfunc(fn, ctx[fn].data().strip())
451 addfunc(fn, ctx[fn].data().strip())
452 repo.ui.progress(_('finding outgoing largefiles'), None)
452 repo.ui.progress(_('finding outgoing largefiles'), None)
453
453
454 def updatestandinsbymatch(repo, match):
454 def updatestandinsbymatch(repo, match):
455 '''Update standins in the working directory according to specified match
455 '''Update standins in the working directory according to specified match
456
456
457 This returns (possibly modified) ``match`` object to be used for
457 This returns (possibly modified) ``match`` object to be used for
458 subsequent commit process.
458 subsequent commit process.
459 '''
459 '''
460
460
461 ui = repo.ui
461 ui = repo.ui
462
462
463 # Case 1: user calls commit with no specific files or
463 # Case 1: user calls commit with no specific files or
464 # include/exclude patterns: refresh and commit all files that
464 # include/exclude patterns: refresh and commit all files that
465 # are "dirty".
465 # are "dirty".
466 if match is None or match.always():
466 if match is None or match.always():
467 # Spend a bit of time here to get a list of files we know
467 # Spend a bit of time here to get a list of files we know
468 # are modified so we can compare only against those.
468 # are modified so we can compare only against those.
469 # It can cost a lot of time (several seconds)
469 # It can cost a lot of time (several seconds)
470 # otherwise to update all standins if the largefiles are
470 # otherwise to update all standins if the largefiles are
471 # large.
471 # large.
472 lfdirstate = openlfdirstate(ui, repo)
472 lfdirstate = openlfdirstate(ui, repo)
473 dirtymatch = match_.always(repo.root, repo.getcwd())
473 dirtymatch = match_.always(repo.root, repo.getcwd())
474 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
474 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
475 False)
475 False)
476 modifiedfiles = unsure + s.modified + s.added + s.removed
476 modifiedfiles = unsure + s.modified + s.added + s.removed
477 lfiles = listlfiles(repo)
477 lfiles = listlfiles(repo)
478 # this only loops through largefiles that exist (not
478 # this only loops through largefiles that exist (not
479 # removed/renamed)
479 # removed/renamed)
480 for lfile in lfiles:
480 for lfile in lfiles:
481 if lfile in modifiedfiles:
481 if lfile in modifiedfiles:
482 if os.path.exists(
482 if os.path.exists(
483 repo.wjoin(standin(lfile))):
483 repo.wjoin(standin(lfile))):
484 # this handles the case where a rebase is being
484 # this handles the case where a rebase is being
485 # performed and the working copy is not updated
485 # performed and the working copy is not updated
486 # yet.
486 # yet.
487 if os.path.exists(repo.wjoin(lfile)):
487 if os.path.exists(repo.wjoin(lfile)):
488 updatestandin(repo,
488 updatestandin(repo,
489 standin(lfile))
489 standin(lfile))
490
490
491 return match
491 return match
492
492
493 lfiles = listlfiles(repo)
493 lfiles = listlfiles(repo)
494 match._files = repo._subdirlfs(match.files(), lfiles)
494 match._files = repo._subdirlfs(match.files(), lfiles)
495
495
496 # Case 2: user calls commit with specified patterns: refresh
496 # Case 2: user calls commit with specified patterns: refresh
497 # any matching big files.
497 # any matching big files.
498 smatcher = composestandinmatcher(repo, match)
498 smatcher = composestandinmatcher(repo, match)
499 standins = repo.dirstate.walk(smatcher, [], False, False)
499 standins = repo.dirstate.walk(smatcher, [], False, False)
500
500
501 # No matching big files: get out of the way and pass control to
501 # No matching big files: get out of the way and pass control to
502 # the usual commit() method.
502 # the usual commit() method.
503 if not standins:
503 if not standins:
504 return match
504 return match
505
505
506 # Refresh all matching big files. It's possible that the
506 # Refresh all matching big files. It's possible that the
507 # commit will end up failing, in which case the big files will
507 # commit will end up failing, in which case the big files will
508 # stay refreshed. No harm done: the user modified them and
508 # stay refreshed. No harm done: the user modified them and
509 # asked to commit them, so sooner or later we're going to
509 # asked to commit them, so sooner or later we're going to
510 # refresh the standins. Might as well leave them refreshed.
510 # refresh the standins. Might as well leave them refreshed.
511 lfdirstate = openlfdirstate(ui, repo)
511 lfdirstate = openlfdirstate(ui, repo)
512 for fstandin in standins:
512 for fstandin in standins:
513 lfile = splitstandin(fstandin)
513 lfile = splitstandin(fstandin)
514 if lfdirstate[lfile] != 'r':
514 if lfdirstate[lfile] != 'r':
515 updatestandin(repo, fstandin)
515 updatestandin(repo, fstandin)
516
516
517 # Cook up a new matcher that only matches regular files or
517 # Cook up a new matcher that only matches regular files or
518 # standins corresponding to the big files requested by the
518 # standins corresponding to the big files requested by the
519 # user. Have to modify _files to prevent commit() from
519 # user. Have to modify _files to prevent commit() from
520 # complaining "not tracked" for big files.
520 # complaining "not tracked" for big files.
521 match = copy.copy(match)
521 match = copy.copy(match)
522 origmatchfn = match.matchfn
522 origmatchfn = match.matchfn
523
523
524 # Check both the list of largefiles and the list of
524 # Check both the list of largefiles and the list of
525 # standins because if a largefile was removed, it
525 # standins because if a largefile was removed, it
526 # won't be in the list of largefiles at this point
526 # won't be in the list of largefiles at this point
527 match._files += sorted(standins)
527 match._files += sorted(standins)
528
528
529 actualfiles = []
529 actualfiles = []
530 for f in match._files:
530 for f in match._files:
531 fstandin = standin(f)
531 fstandin = standin(f)
532
532
533 # ignore known largefiles and standins
533 # ignore known largefiles and standins
534 if f in lfiles or fstandin in standins:
534 if f in lfiles or fstandin in standins:
535 continue
535 continue
536
536
537 actualfiles.append(f)
537 actualfiles.append(f)
538 match._files = actualfiles
538 match._files = actualfiles
539
539
540 def matchfn(f):
540 def matchfn(f):
541 if origmatchfn(f):
541 if origmatchfn(f):
542 return f not in lfiles
542 return f not in lfiles
543 else:
543 else:
544 return f in standins
544 return f in standins
545
545
546 match.matchfn = matchfn
546 match.matchfn = matchfn
547
547
548 return match
548 return match
549
549
550 class automatedcommithook(object):
550 class automatedcommithook(object):
551 '''Stateful hook to update standins at the 1st commit of resuming
551 '''Stateful hook to update standins at the 1st commit of resuming
552
552
553 For efficiency, updating standins in the working directory should
553 For efficiency, updating standins in the working directory should
554 be avoided while automated committing (like rebase, transplant and
554 be avoided while automated committing (like rebase, transplant and
555 so on), because they should be updated before committing.
555 so on), because they should be updated before committing.
556
556
557 But the 1st commit of resuming automated committing (e.g. ``rebase
557 But the 1st commit of resuming automated committing (e.g. ``rebase
558 --continue``) should update them, because largefiles may be
558 --continue``) should update them, because largefiles may be
559 modified manually.
559 modified manually.
560 '''
560 '''
561 def __init__(self, resuming):
561 def __init__(self, resuming):
562 self.resuming = resuming
562 self.resuming = resuming
563
563
564 def __call__(self, repo, match):
564 def __call__(self, repo, match):
565 if self.resuming:
565 if self.resuming:
566 self.resuming = False # avoids updating at subsequent commits
566 self.resuming = False # avoids updating at subsequent commits
567 return updatestandinsbymatch(repo, match)
567 return updatestandinsbymatch(repo, match)
568 else:
568 else:
569 return match
569 return match
570
570
571 def getstatuswriter(ui, repo, forcibly=None):
571 def getstatuswriter(ui, repo, forcibly=None):
572 '''Return the function to write largefiles specific status out
572 '''Return the function to write largefiles specific status out
573
573
574 If ``forcibly`` is ``None``, this returns the last element of
574 If ``forcibly`` is ``None``, this returns the last element of
575 ``repo._lfstatuswriters`` as "default" writer function.
575 ``repo._lfstatuswriters`` as "default" writer function.
576
576
577 Otherwise, this returns the function to always write out (or
577 Otherwise, this returns the function to always write out (or
578 ignore if ``not forcibly``) status.
578 ignore if ``not forcibly``) status.
579 '''
579 '''
580 if forcibly is None:
580 if forcibly is None and util.safehasattr(repo, '_largefilesenabled'):
581 return repo._lfstatuswriters[-1]
581 return repo._lfstatuswriters[-1]
582 else:
582 else:
583 if forcibly:
583 if forcibly:
584 return ui.status # forcibly WRITE OUT
584 return ui.status # forcibly WRITE OUT
585 else:
585 else:
586 return lambda *msg, **opts: None # forcibly IGNORE
586 return lambda *msg, **opts: None # forcibly IGNORE
@@ -1,1346 +1,1349 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
15 archival, pathutil, revset
15 archival, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18
18
19 import lfutil
19 import lfutil
20 import lfcommands
20 import lfcommands
21 import basestore
21 import basestore
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def composelargefilematcher(match, manifest):
25 def composelargefilematcher(match, manifest):
26 '''create a matcher that matches only the largefiles in the original
26 '''create a matcher that matches only the largefiles in the original
27 matcher'''
27 matcher'''
28 m = copy.copy(match)
28 m = copy.copy(match)
29 lfile = lambda f: lfutil.standin(f) in manifest
29 lfile = lambda f: lfutil.standin(f) in manifest
30 m._files = filter(lfile, m._files)
30 m._files = filter(lfile, m._files)
31 m._fmap = set(m._files)
31 m._fmap = set(m._files)
32 m._always = False
32 m._always = False
33 origmatchfn = m.matchfn
33 origmatchfn = m.matchfn
34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
35 return m
35 return m
36
36
37 def composenormalfilematcher(match, manifest, exclude=None):
37 def composenormalfilematcher(match, manifest, exclude=None):
38 excluded = set()
38 excluded = set()
39 if exclude is not None:
39 if exclude is not None:
40 excluded.update(exclude)
40 excluded.update(exclude)
41
41
42 m = copy.copy(match)
42 m = copy.copy(match)
43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
44 manifest or f in excluded)
44 manifest or f in excluded)
45 m._files = filter(notlfile, m._files)
45 m._files = filter(notlfile, m._files)
46 m._fmap = set(m._files)
46 m._fmap = set(m._files)
47 m._always = False
47 m._always = False
48 origmatchfn = m.matchfn
48 origmatchfn = m.matchfn
49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
50 return m
50 return m
51
51
52 def installnormalfilesmatchfn(manifest):
52 def installnormalfilesmatchfn(manifest):
53 '''installmatchfn with a matchfn that ignores all largefiles'''
53 '''installmatchfn with a matchfn that ignores all largefiles'''
54 def overridematch(ctx, pats=[], opts={}, globbed=False,
54 def overridematch(ctx, pats=[], opts={}, globbed=False,
55 default='relpath'):
55 default='relpath'):
56 match = oldmatch(ctx, pats, opts, globbed, default)
56 match = oldmatch(ctx, pats, opts, globbed, default)
57 return composenormalfilematcher(match, manifest)
57 return composenormalfilematcher(match, manifest)
58 oldmatch = installmatchfn(overridematch)
58 oldmatch = installmatchfn(overridematch)
59
59
60 def installmatchfn(f):
60 def installmatchfn(f):
61 '''monkey patch the scmutil module with a custom match function.
61 '''monkey patch the scmutil module with a custom match function.
62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
63 oldmatch = scmutil.match
63 oldmatch = scmutil.match
64 setattr(f, 'oldmatch', oldmatch)
64 setattr(f, 'oldmatch', oldmatch)
65 scmutil.match = f
65 scmutil.match = f
66 return oldmatch
66 return oldmatch
67
67
68 def restorematchfn():
68 def restorematchfn():
69 '''restores scmutil.match to what it was before installmatchfn
69 '''restores scmutil.match to what it was before installmatchfn
70 was called. no-op if scmutil.match is its original function.
70 was called. no-op if scmutil.match is its original function.
71
71
72 Note that n calls to installmatchfn will require n calls to
72 Note that n calls to installmatchfn will require n calls to
73 restore the original matchfn.'''
73 restore the original matchfn.'''
74 scmutil.match = getattr(scmutil.match, 'oldmatch')
74 scmutil.match = getattr(scmutil.match, 'oldmatch')
75
75
76 def installmatchandpatsfn(f):
76 def installmatchandpatsfn(f):
77 oldmatchandpats = scmutil.matchandpats
77 oldmatchandpats = scmutil.matchandpats
78 setattr(f, 'oldmatchandpats', oldmatchandpats)
78 setattr(f, 'oldmatchandpats', oldmatchandpats)
79 scmutil.matchandpats = f
79 scmutil.matchandpats = f
80 return oldmatchandpats
80 return oldmatchandpats
81
81
82 def restorematchandpatsfn():
82 def restorematchandpatsfn():
83 '''restores scmutil.matchandpats to what it was before
83 '''restores scmutil.matchandpats to what it was before
84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
85 is its original function.
85 is its original function.
86
86
87 Note that n calls to installmatchandpatsfn will require n calls
87 Note that n calls to installmatchandpatsfn will require n calls
88 to restore the original matchfn.'''
88 to restore the original matchfn.'''
89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
90 scmutil.matchandpats)
90 scmutil.matchandpats)
91
91
92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
93 large = opts.get('large')
93 large = opts.get('large')
94 lfsize = lfutil.getminsize(
94 lfsize = lfutil.getminsize(
95 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
95 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
96
96
97 lfmatcher = None
97 lfmatcher = None
98 if lfutil.islfilesrepo(repo):
98 if lfutil.islfilesrepo(repo):
99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
100 if lfpats:
100 if lfpats:
101 lfmatcher = match_.match(repo.root, '', list(lfpats))
101 lfmatcher = match_.match(repo.root, '', list(lfpats))
102
102
103 lfnames = []
103 lfnames = []
104 m = copy.copy(matcher)
104 m = copy.copy(matcher)
105 m.bad = lambda x, y: None
105 m.bad = lambda x, y: None
106 wctx = repo[None]
106 wctx = repo[None]
107 for f in repo.walk(m):
107 for f in repo.walk(m):
108 exact = m.exact(f)
108 exact = m.exact(f)
109 lfile = lfutil.standin(f) in wctx
109 lfile = lfutil.standin(f) in wctx
110 nfile = f in wctx
110 nfile = f in wctx
111 exists = lfile or nfile
111 exists = lfile or nfile
112
112
113 # addremove in core gets fancy with the name, add doesn't
113 # addremove in core gets fancy with the name, add doesn't
114 if isaddremove:
114 if isaddremove:
115 name = m.uipath(f)
115 name = m.uipath(f)
116 else:
116 else:
117 name = m.rel(f)
117 name = m.rel(f)
118
118
119 # Don't warn the user when they attempt to add a normal tracked file.
119 # Don't warn the user when they attempt to add a normal tracked file.
120 # The normal add code will do that for us.
120 # The normal add code will do that for us.
121 if exact and exists:
121 if exact and exists:
122 if lfile:
122 if lfile:
123 ui.warn(_('%s already a largefile\n') % name)
123 ui.warn(_('%s already a largefile\n') % name)
124 continue
124 continue
125
125
126 if (exact or not exists) and not lfutil.isstandin(f):
126 if (exact or not exists) and not lfutil.isstandin(f):
127 # In case the file was removed previously, but not committed
127 # In case the file was removed previously, but not committed
128 # (issue3507)
128 # (issue3507)
129 if not repo.wvfs.exists(f):
129 if not repo.wvfs.exists(f):
130 continue
130 continue
131
131
132 abovemin = (lfsize and
132 abovemin = (lfsize and
133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
134 if large or abovemin or (lfmatcher and lfmatcher(f)):
134 if large or abovemin or (lfmatcher and lfmatcher(f)):
135 lfnames.append(f)
135 lfnames.append(f)
136 if ui.verbose or not exact:
136 if ui.verbose or not exact:
137 ui.status(_('adding %s as a largefile\n') % name)
137 ui.status(_('adding %s as a largefile\n') % name)
138
138
139 bad = []
139 bad = []
140
140
141 # Need to lock, otherwise there could be a race condition between
141 # Need to lock, otherwise there could be a race condition between
142 # when standins are created and added to the repo.
142 # when standins are created and added to the repo.
143 wlock = repo.wlock()
143 wlock = repo.wlock()
144 try:
144 try:
145 if not opts.get('dry_run'):
145 if not opts.get('dry_run'):
146 standins = []
146 standins = []
147 lfdirstate = lfutil.openlfdirstate(ui, repo)
147 lfdirstate = lfutil.openlfdirstate(ui, repo)
148 for f in lfnames:
148 for f in lfnames:
149 standinname = lfutil.standin(f)
149 standinname = lfutil.standin(f)
150 lfutil.writestandin(repo, standinname, hash='',
150 lfutil.writestandin(repo, standinname, hash='',
151 executable=lfutil.getexecutable(repo.wjoin(f)))
151 executable=lfutil.getexecutable(repo.wjoin(f)))
152 standins.append(standinname)
152 standins.append(standinname)
153 if lfdirstate[f] == 'r':
153 if lfdirstate[f] == 'r':
154 lfdirstate.normallookup(f)
154 lfdirstate.normallookup(f)
155 else:
155 else:
156 lfdirstate.add(f)
156 lfdirstate.add(f)
157 lfdirstate.write()
157 lfdirstate.write()
158 bad += [lfutil.splitstandin(f)
158 bad += [lfutil.splitstandin(f)
159 for f in repo[None].add(standins)
159 for f in repo[None].add(standins)
160 if f in m.files()]
160 if f in m.files()]
161
161
162 added = [f for f in lfnames if f not in bad]
162 added = [f for f in lfnames if f not in bad]
163 finally:
163 finally:
164 wlock.release()
164 wlock.release()
165 return added, bad
165 return added, bad
166
166
167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
168 after = opts.get('after')
168 after = opts.get('after')
169 m = composelargefilematcher(matcher, repo[None].manifest())
169 m = composelargefilematcher(matcher, repo[None].manifest())
170 try:
170 try:
171 repo.lfstatus = True
171 repo.lfstatus = True
172 s = repo.status(match=m, clean=not isaddremove)
172 s = repo.status(match=m, clean=not isaddremove)
173 finally:
173 finally:
174 repo.lfstatus = False
174 repo.lfstatus = False
175 manifest = repo[None].manifest()
175 manifest = repo[None].manifest()
176 modified, added, deleted, clean = [[f for f in list
176 modified, added, deleted, clean = [[f for f in list
177 if lfutil.standin(f) in manifest]
177 if lfutil.standin(f) in manifest]
178 for list in (s.modified, s.added,
178 for list in (s.modified, s.added,
179 s.deleted, s.clean)]
179 s.deleted, s.clean)]
180
180
181 def warn(files, msg):
181 def warn(files, msg):
182 for f in files:
182 for f in files:
183 ui.warn(msg % m.rel(f))
183 ui.warn(msg % m.rel(f))
184 return int(len(files) > 0)
184 return int(len(files) > 0)
185
185
186 result = 0
186 result = 0
187
187
188 if after:
188 if after:
189 remove = deleted
189 remove = deleted
190 result = warn(modified + added + clean,
190 result = warn(modified + added + clean,
191 _('not removing %s: file still exists\n'))
191 _('not removing %s: file still exists\n'))
192 else:
192 else:
193 remove = deleted + clean
193 remove = deleted + clean
194 result = warn(modified, _('not removing %s: file is modified (use -f'
194 result = warn(modified, _('not removing %s: file is modified (use -f'
195 ' to force removal)\n'))
195 ' to force removal)\n'))
196 result = warn(added, _('not removing %s: file has been marked for add'
196 result = warn(added, _('not removing %s: file has been marked for add'
197 ' (use forget to undo)\n')) or result
197 ' (use forget to undo)\n')) or result
198
198
199 # Need to lock because standin files are deleted then removed from the
199 # Need to lock because standin files are deleted then removed from the
200 # repository and we could race in-between.
200 # repository and we could race in-between.
201 wlock = repo.wlock()
201 wlock = repo.wlock()
202 try:
202 try:
203 lfdirstate = lfutil.openlfdirstate(ui, repo)
203 lfdirstate = lfutil.openlfdirstate(ui, repo)
204 for f in sorted(remove):
204 for f in sorted(remove):
205 if ui.verbose or not m.exact(f):
205 if ui.verbose or not m.exact(f):
206 # addremove in core gets fancy with the name, remove doesn't
206 # addremove in core gets fancy with the name, remove doesn't
207 if isaddremove:
207 if isaddremove:
208 name = m.uipath(f)
208 name = m.uipath(f)
209 else:
209 else:
210 name = m.rel(f)
210 name = m.rel(f)
211 ui.status(_('removing %s\n') % name)
211 ui.status(_('removing %s\n') % name)
212
212
213 if not opts.get('dry_run'):
213 if not opts.get('dry_run'):
214 if not after:
214 if not after:
215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
216
216
217 if opts.get('dry_run'):
217 if opts.get('dry_run'):
218 return result
218 return result
219
219
220 remove = [lfutil.standin(f) for f in remove]
220 remove = [lfutil.standin(f) for f in remove]
221 # If this is being called by addremove, let the original addremove
221 # If this is being called by addremove, let the original addremove
222 # function handle this.
222 # function handle this.
223 if not isaddremove:
223 if not isaddremove:
224 for f in remove:
224 for f in remove:
225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
226 repo[None].forget(remove)
226 repo[None].forget(remove)
227
227
228 for f in remove:
228 for f in remove:
229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
230 False)
230 False)
231
231
232 lfdirstate.write()
232 lfdirstate.write()
233 finally:
233 finally:
234 wlock.release()
234 wlock.release()
235
235
236 return result
236 return result
237
237
238 # For overriding mercurial.hgweb.webcommands so that largefiles will
238 # For overriding mercurial.hgweb.webcommands so that largefiles will
239 # appear at their right place in the manifests.
239 # appear at their right place in the manifests.
240 def decodepath(orig, path):
240 def decodepath(orig, path):
241 return lfutil.splitstandin(path) or path
241 return lfutil.splitstandin(path) or path
242
242
243 # -- Wrappers: modify existing commands --------------------------------
243 # -- Wrappers: modify existing commands --------------------------------
244
244
245 def overrideadd(orig, ui, repo, *pats, **opts):
245 def overrideadd(orig, ui, repo, *pats, **opts):
246 if opts.get('normal') and opts.get('large'):
246 if opts.get('normal') and opts.get('large'):
247 raise util.Abort(_('--normal cannot be used with --large'))
247 raise util.Abort(_('--normal cannot be used with --large'))
248 return orig(ui, repo, *pats, **opts)
248 return orig(ui, repo, *pats, **opts)
249
249
250 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
250 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
251 # The --normal flag short circuits this override
251 # The --normal flag short circuits this override
252 if opts.get('normal'):
252 if opts.get('normal'):
253 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
253 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
254
254
255 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
255 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
256 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
256 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
257 ladded)
257 ladded)
258 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
258 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
259
259
260 bad.extend(f for f in lbad)
260 bad.extend(f for f in lbad)
261 return bad
261 return bad
262
262
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
266 return removelargefiles(ui, repo, False, matcher, after=after,
266 return removelargefiles(ui, repo, False, matcher, after=after,
267 force=force) or result
267 force=force) or result
268
268
269 def overridestatusfn(orig, repo, rev2, **opts):
269 def overridestatusfn(orig, repo, rev2, **opts):
270 try:
270 try:
271 repo._repo.lfstatus = True
271 repo._repo.lfstatus = True
272 return orig(repo, rev2, **opts)
272 return orig(repo, rev2, **opts)
273 finally:
273 finally:
274 repo._repo.lfstatus = False
274 repo._repo.lfstatus = False
275
275
276 def overridestatus(orig, ui, repo, *pats, **opts):
276 def overridestatus(orig, ui, repo, *pats, **opts):
277 try:
277 try:
278 repo.lfstatus = True
278 repo.lfstatus = True
279 return orig(ui, repo, *pats, **opts)
279 return orig(ui, repo, *pats, **opts)
280 finally:
280 finally:
281 repo.lfstatus = False
281 repo.lfstatus = False
282
282
283 def overridedirty(orig, repo, ignoreupdate=False):
283 def overridedirty(orig, repo, ignoreupdate=False):
284 try:
284 try:
285 repo._repo.lfstatus = True
285 repo._repo.lfstatus = True
286 return orig(repo, ignoreupdate)
286 return orig(repo, ignoreupdate)
287 finally:
287 finally:
288 repo._repo.lfstatus = False
288 repo._repo.lfstatus = False
289
289
290 def overridelog(orig, ui, repo, *pats, **opts):
290 def overridelog(orig, ui, repo, *pats, **opts):
291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
292 default='relpath'):
292 default='relpath'):
293 """Matcher that merges root directory with .hglf, suitable for log.
293 """Matcher that merges root directory with .hglf, suitable for log.
294 It is still possible to match .hglf directly.
294 It is still possible to match .hglf directly.
295 For any listed files run log on the standin too.
295 For any listed files run log on the standin too.
296 matchfn tries both the given filename and with .hglf stripped.
296 matchfn tries both the given filename and with .hglf stripped.
297 """
297 """
298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
299 m, p = copy.copy(matchandpats)
299 m, p = copy.copy(matchandpats)
300
300
301 if m.always():
301 if m.always():
302 # We want to match everything anyway, so there's no benefit trying
302 # We want to match everything anyway, so there's no benefit trying
303 # to add standins.
303 # to add standins.
304 return matchandpats
304 return matchandpats
305
305
306 pats = set(p)
306 pats = set(p)
307 # TODO: handling of patterns in both cases below
307 # TODO: handling of patterns in both cases below
308 if m._cwd:
308 if m._cwd:
309 if os.path.isabs(m._cwd):
309 if os.path.isabs(m._cwd):
310 # TODO: handle largefile magic when invoked from other cwd
310 # TODO: handle largefile magic when invoked from other cwd
311 return matchandpats
311 return matchandpats
312 back = (m._cwd.count('/') + 1) * '../'
312 back = (m._cwd.count('/') + 1) * '../'
313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
314 else:
314 else:
315 pats.update(lfutil.standin(f) for f in p)
315 pats.update(lfutil.standin(f) for f in p)
316
316
317 for i in range(0, len(m._files)):
317 for i in range(0, len(m._files)):
318 standin = lfutil.standin(m._files[i])
318 standin = lfutil.standin(m._files[i])
319 # If the "standin" is a directory, append instead of replace to
319 # If the "standin" is a directory, append instead of replace to
320 # support naming a directory on the command line with only
320 # support naming a directory on the command line with only
321 # largefiles. The original directory is kept to support normal
321 # largefiles. The original directory is kept to support normal
322 # files.
322 # files.
323 if standin in repo[ctx.node()]:
323 if standin in repo[ctx.node()]:
324 m._files[i] = standin
324 m._files[i] = standin
325 elif m._files[i] not in repo[ctx.node()] \
325 elif m._files[i] not in repo[ctx.node()] \
326 and repo.wvfs.isdir(standin):
326 and repo.wvfs.isdir(standin):
327 m._files.append(standin)
327 m._files.append(standin)
328 pats.add(standin)
328 pats.add(standin)
329
329
330 m._fmap = set(m._files)
330 m._fmap = set(m._files)
331 m._always = False
331 m._always = False
332 origmatchfn = m.matchfn
332 origmatchfn = m.matchfn
333 def lfmatchfn(f):
333 def lfmatchfn(f):
334 lf = lfutil.splitstandin(f)
334 lf = lfutil.splitstandin(f)
335 if lf is not None and origmatchfn(lf):
335 if lf is not None and origmatchfn(lf):
336 return True
336 return True
337 r = origmatchfn(f)
337 r = origmatchfn(f)
338 return r
338 return r
339 m.matchfn = lfmatchfn
339 m.matchfn = lfmatchfn
340
340
341 return m, pats
341 return m, pats
342
342
343 # For hg log --patch, the match object is used in two different senses:
343 # For hg log --patch, the match object is used in two different senses:
344 # (1) to determine what revisions should be printed out, and
344 # (1) to determine what revisions should be printed out, and
345 # (2) to determine what files to print out diffs for.
345 # (2) to determine what files to print out diffs for.
346 # The magic matchandpats override should be used for case (1) but not for
346 # The magic matchandpats override should be used for case (1) but not for
347 # case (2).
347 # case (2).
348 def overridemakelogfilematcher(repo, pats, opts):
348 def overridemakelogfilematcher(repo, pats, opts):
349 pctx = repo[None]
349 pctx = repo[None]
350 match, pats = oldmatchandpats(pctx, pats, opts)
350 match, pats = oldmatchandpats(pctx, pats, opts)
351 return lambda rev: match
351 return lambda rev: match
352
352
353 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
353 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
354 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
354 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
355 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
355 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
356
356
357 try:
357 try:
358 return orig(ui, repo, *pats, **opts)
358 return orig(ui, repo, *pats, **opts)
359 finally:
359 finally:
360 restorematchandpatsfn()
360 restorematchandpatsfn()
361 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
361 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
362
362
363 def overrideverify(orig, ui, repo, *pats, **opts):
363 def overrideverify(orig, ui, repo, *pats, **opts):
364 large = opts.pop('large', False)
364 large = opts.pop('large', False)
365 all = opts.pop('lfa', False)
365 all = opts.pop('lfa', False)
366 contents = opts.pop('lfc', False)
366 contents = opts.pop('lfc', False)
367
367
368 result = orig(ui, repo, *pats, **opts)
368 result = orig(ui, repo, *pats, **opts)
369 if large or all or contents:
369 if large or all or contents:
370 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
370 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
371 return result
371 return result
372
372
373 def overridedebugstate(orig, ui, repo, *pats, **opts):
373 def overridedebugstate(orig, ui, repo, *pats, **opts):
374 large = opts.pop('large', False)
374 large = opts.pop('large', False)
375 if large:
375 if large:
376 class fakerepo(object):
376 class fakerepo(object):
377 dirstate = lfutil.openlfdirstate(ui, repo)
377 dirstate = lfutil.openlfdirstate(ui, repo)
378 orig(ui, fakerepo, *pats, **opts)
378 orig(ui, fakerepo, *pats, **opts)
379 else:
379 else:
380 orig(ui, repo, *pats, **opts)
380 orig(ui, repo, *pats, **opts)
381
381
382 # Override needs to refresh standins so that update's normal merge
382 # Override needs to refresh standins so that update's normal merge
383 # will go through properly. Then the other update hook (overriding repo.update)
383 # will go through properly. Then the other update hook (overriding repo.update)
384 # will get the new files. Filemerge is also overridden so that the merge
384 # will get the new files. Filemerge is also overridden so that the merge
385 # will merge standins correctly.
385 # will merge standins correctly.
386 def overrideupdate(orig, ui, repo, *pats, **opts):
386 def overrideupdate(orig, ui, repo, *pats, **opts):
387 # Need to lock between the standins getting updated and their
387 # Need to lock between the standins getting updated and their
388 # largefiles getting updated
388 # largefiles getting updated
389 wlock = repo.wlock()
389 wlock = repo.wlock()
390 try:
390 try:
391 if opts['check']:
391 if opts['check']:
392 lfdirstate = lfutil.openlfdirstate(ui, repo)
392 lfdirstate = lfutil.openlfdirstate(ui, repo)
393 unsure, s = lfdirstate.status(
393 unsure, s = lfdirstate.status(
394 match_.always(repo.root, repo.getcwd()),
394 match_.always(repo.root, repo.getcwd()),
395 [], False, False, False)
395 [], False, False, False)
396
396
397 mod = len(s.modified) > 0
397 mod = len(s.modified) > 0
398 for lfile in unsure:
398 for lfile in unsure:
399 standin = lfutil.standin(lfile)
399 standin = lfutil.standin(lfile)
400 if repo['.'][standin].data().strip() != \
400 if repo['.'][standin].data().strip() != \
401 lfutil.hashfile(repo.wjoin(lfile)):
401 lfutil.hashfile(repo.wjoin(lfile)):
402 mod = True
402 mod = True
403 else:
403 else:
404 lfdirstate.normal(lfile)
404 lfdirstate.normal(lfile)
405 lfdirstate.write()
405 lfdirstate.write()
406 if mod:
406 if mod:
407 raise util.Abort(_('uncommitted changes'))
407 raise util.Abort(_('uncommitted changes'))
408 return orig(ui, repo, *pats, **opts)
408 return orig(ui, repo, *pats, **opts)
409 finally:
409 finally:
410 wlock.release()
410 wlock.release()
411
411
412 # Before starting the manifest merge, merge.updates will call
412 # Before starting the manifest merge, merge.updates will call
413 # _checkunknownfile to check if there are any files in the merged-in
413 # _checkunknownfile to check if there are any files in the merged-in
414 # changeset that collide with unknown files in the working copy.
414 # changeset that collide with unknown files in the working copy.
415 #
415 #
416 # The largefiles are seen as unknown, so this prevents us from merging
416 # The largefiles are seen as unknown, so this prevents us from merging
417 # in a file 'foo' if we already have a largefile with the same name.
417 # in a file 'foo' if we already have a largefile with the same name.
418 #
418 #
419 # The overridden function filters the unknown files by removing any
419 # The overridden function filters the unknown files by removing any
420 # largefiles. This makes the merge proceed and we can then handle this
420 # largefiles. This makes the merge proceed and we can then handle this
421 # case further in the overridden calculateupdates function below.
421 # case further in the overridden calculateupdates function below.
422 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
422 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
423 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
423 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
424 return False
424 return False
425 return origfn(repo, wctx, mctx, f, f2)
425 return origfn(repo, wctx, mctx, f, f2)
426
426
427 # The manifest merge handles conflicts on the manifest level. We want
427 # The manifest merge handles conflicts on the manifest level. We want
428 # to handle changes in largefile-ness of files at this level too.
428 # to handle changes in largefile-ness of files at this level too.
429 #
429 #
430 # The strategy is to run the original calculateupdates and then process
430 # The strategy is to run the original calculateupdates and then process
431 # the action list it outputs. There are two cases we need to deal with:
431 # the action list it outputs. There are two cases we need to deal with:
432 #
432 #
433 # 1. Normal file in p1, largefile in p2. Here the largefile is
433 # 1. Normal file in p1, largefile in p2. Here the largefile is
434 # detected via its standin file, which will enter the working copy
434 # detected via its standin file, which will enter the working copy
435 # with a "get" action. It is not "merge" since the standin is all
435 # with a "get" action. It is not "merge" since the standin is all
436 # Mercurial is concerned with at this level -- the link to the
436 # Mercurial is concerned with at this level -- the link to the
437 # existing normal file is not relevant here.
437 # existing normal file is not relevant here.
438 #
438 #
439 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
439 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
440 # since the largefile will be present in the working copy and
440 # since the largefile will be present in the working copy and
441 # different from the normal file in p2. Mercurial therefore
441 # different from the normal file in p2. Mercurial therefore
442 # triggers a merge action.
442 # triggers a merge action.
443 #
443 #
444 # In both cases, we prompt the user and emit new actions to either
444 # In both cases, we prompt the user and emit new actions to either
445 # remove the standin (if the normal file was kept) or to remove the
445 # remove the standin (if the normal file was kept) or to remove the
446 # normal file and get the standin (if the largefile was kept). The
446 # normal file and get the standin (if the largefile was kept). The
447 # default prompt answer is to use the largefile version since it was
447 # default prompt answer is to use the largefile version since it was
448 # presumably changed on purpose.
448 # presumably changed on purpose.
449 #
449 #
450 # Finally, the merge.applyupdates function will then take care of
450 # Finally, the merge.applyupdates function will then take care of
451 # writing the files into the working copy and lfcommands.updatelfiles
451 # writing the files into the working copy and lfcommands.updatelfiles
452 # will update the largefiles.
452 # will update the largefiles.
453 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
453 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
454 partial, acceptremote, followcopies):
454 partial, acceptremote, followcopies):
455 overwrite = force and not branchmerge
455 overwrite = force and not branchmerge
456 actions, diverge, renamedelete = origfn(
456 actions, diverge, renamedelete = origfn(
457 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
457 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
458 followcopies)
458 followcopies)
459
459
460 if overwrite:
460 if overwrite:
461 return actions, diverge, renamedelete
461 return actions, diverge, renamedelete
462
462
463 # Convert to dictionary with filename as key and action as value.
463 # Convert to dictionary with filename as key and action as value.
464 lfiles = set()
464 lfiles = set()
465 for f in actions:
465 for f in actions:
466 splitstandin = f and lfutil.splitstandin(f)
466 splitstandin = f and lfutil.splitstandin(f)
467 if splitstandin in p1:
467 if splitstandin in p1:
468 lfiles.add(splitstandin)
468 lfiles.add(splitstandin)
469 elif lfutil.standin(f) in p1:
469 elif lfutil.standin(f) in p1:
470 lfiles.add(f)
470 lfiles.add(f)
471
471
472 for lfile in lfiles:
472 for lfile in lfiles:
473 standin = lfutil.standin(lfile)
473 standin = lfutil.standin(lfile)
474 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
474 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
475 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
475 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
476 if sm in ('g', 'dc') and lm != 'r':
476 if sm in ('g', 'dc') and lm != 'r':
477 # Case 1: normal file in the working copy, largefile in
477 # Case 1: normal file in the working copy, largefile in
478 # the second parent
478 # the second parent
479 usermsg = _('remote turned local normal file %s into a largefile\n'
479 usermsg = _('remote turned local normal file %s into a largefile\n'
480 'use (l)argefile or keep (n)ormal file?'
480 'use (l)argefile or keep (n)ormal file?'
481 '$$ &Largefile $$ &Normal file') % lfile
481 '$$ &Largefile $$ &Normal file') % lfile
482 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
482 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
483 actions[lfile] = ('r', None, 'replaced by standin')
483 actions[lfile] = ('r', None, 'replaced by standin')
484 actions[standin] = ('g', sargs, 'replaces standin')
484 actions[standin] = ('g', sargs, 'replaces standin')
485 else: # keep local normal file
485 else: # keep local normal file
486 actions[lfile] = ('k', None, 'replaces standin')
486 actions[lfile] = ('k', None, 'replaces standin')
487 if branchmerge:
487 if branchmerge:
488 actions[standin] = ('k', None, 'replaced by non-standin')
488 actions[standin] = ('k', None, 'replaced by non-standin')
489 else:
489 else:
490 actions[standin] = ('r', None, 'replaced by non-standin')
490 actions[standin] = ('r', None, 'replaced by non-standin')
491 elif lm in ('g', 'dc') and sm != 'r':
491 elif lm in ('g', 'dc') and sm != 'r':
492 # Case 2: largefile in the working copy, normal file in
492 # Case 2: largefile in the working copy, normal file in
493 # the second parent
493 # the second parent
494 usermsg = _('remote turned local largefile %s into a normal file\n'
494 usermsg = _('remote turned local largefile %s into a normal file\n'
495 'keep (l)argefile or use (n)ormal file?'
495 'keep (l)argefile or use (n)ormal file?'
496 '$$ &Largefile $$ &Normal file') % lfile
496 '$$ &Largefile $$ &Normal file') % lfile
497 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
497 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
498 if branchmerge:
498 if branchmerge:
499 # largefile can be restored from standin safely
499 # largefile can be restored from standin safely
500 actions[lfile] = ('k', None, 'replaced by standin')
500 actions[lfile] = ('k', None, 'replaced by standin')
501 actions[standin] = ('k', None, 'replaces standin')
501 actions[standin] = ('k', None, 'replaces standin')
502 else:
502 else:
503 # "lfile" should be marked as "removed" without
503 # "lfile" should be marked as "removed" without
504 # removal of itself
504 # removal of itself
505 actions[lfile] = ('lfmr', None,
505 actions[lfile] = ('lfmr', None,
506 'forget non-standin largefile')
506 'forget non-standin largefile')
507
507
508 # linear-merge should treat this largefile as 're-added'
508 # linear-merge should treat this largefile as 're-added'
509 actions[standin] = ('a', None, 'keep standin')
509 actions[standin] = ('a', None, 'keep standin')
510 else: # pick remote normal file
510 else: # pick remote normal file
511 actions[lfile] = ('g', largs, 'replaces standin')
511 actions[lfile] = ('g', largs, 'replaces standin')
512 actions[standin] = ('r', None, 'replaced by non-standin')
512 actions[standin] = ('r', None, 'replaced by non-standin')
513
513
514 return actions, diverge, renamedelete
514 return actions, diverge, renamedelete
515
515
516 def mergerecordupdates(orig, repo, actions, branchmerge):
516 def mergerecordupdates(orig, repo, actions, branchmerge):
517 if 'lfmr' in actions:
517 if 'lfmr' in actions:
518 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
518 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
519 for lfile, args, msg in actions['lfmr']:
519 for lfile, args, msg in actions['lfmr']:
520 # this should be executed before 'orig', to execute 'remove'
520 # this should be executed before 'orig', to execute 'remove'
521 # before all other actions
521 # before all other actions
522 repo.dirstate.remove(lfile)
522 repo.dirstate.remove(lfile)
523 # make sure lfile doesn't get synclfdirstate'd as normal
523 # make sure lfile doesn't get synclfdirstate'd as normal
524 lfdirstate.add(lfile)
524 lfdirstate.add(lfile)
525 lfdirstate.write()
525 lfdirstate.write()
526
526
527 return orig(repo, actions, branchmerge)
527 return orig(repo, actions, branchmerge)
528
528
529
529
530 # Override filemerge to prompt the user about how they wish to merge
530 # Override filemerge to prompt the user about how they wish to merge
531 # largefiles. This will handle identical edits without prompting the user.
531 # largefiles. This will handle identical edits without prompting the user.
532 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
532 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
533 if not lfutil.isstandin(orig):
533 if not lfutil.isstandin(orig):
534 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
534 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
535
535
536 ahash = fca.data().strip().lower()
536 ahash = fca.data().strip().lower()
537 dhash = fcd.data().strip().lower()
537 dhash = fcd.data().strip().lower()
538 ohash = fco.data().strip().lower()
538 ohash = fco.data().strip().lower()
539 if (ohash != ahash and
539 if (ohash != ahash and
540 ohash != dhash and
540 ohash != dhash and
541 (dhash == ahash or
541 (dhash == ahash or
542 repo.ui.promptchoice(
542 repo.ui.promptchoice(
543 _('largefile %s has a merge conflict\nancestor was %s\n'
543 _('largefile %s has a merge conflict\nancestor was %s\n'
544 'keep (l)ocal %s or\ntake (o)ther %s?'
544 'keep (l)ocal %s or\ntake (o)ther %s?'
545 '$$ &Local $$ &Other') %
545 '$$ &Local $$ &Other') %
546 (lfutil.splitstandin(orig), ahash, dhash, ohash),
546 (lfutil.splitstandin(orig), ahash, dhash, ohash),
547 0) == 1)):
547 0) == 1)):
548 repo.wwrite(fcd.path(), fco.data(), fco.flags())
548 repo.wwrite(fcd.path(), fco.data(), fco.flags())
549 return 0
549 return 0
550
550
551 # Copy first changes the matchers to match standins instead of
551 # Copy first changes the matchers to match standins instead of
552 # largefiles. Then it overrides util.copyfile in that function it
552 # largefiles. Then it overrides util.copyfile in that function it
553 # checks if the destination largefile already exists. It also keeps a
553 # checks if the destination largefile already exists. It also keeps a
554 # list of copied files so that the largefiles can be copied and the
554 # list of copied files so that the largefiles can be copied and the
555 # dirstate updated.
555 # dirstate updated.
556 def overridecopy(orig, ui, repo, pats, opts, rename=False):
556 def overridecopy(orig, ui, repo, pats, opts, rename=False):
557 # doesn't remove largefile on rename
557 # doesn't remove largefile on rename
558 if len(pats) < 2:
558 if len(pats) < 2:
559 # this isn't legal, let the original function deal with it
559 # this isn't legal, let the original function deal with it
560 return orig(ui, repo, pats, opts, rename)
560 return orig(ui, repo, pats, opts, rename)
561
561
562 def makestandin(relpath):
562 def makestandin(relpath):
563 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
563 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
564 return os.path.join(repo.wjoin(lfutil.standin(path)))
564 return os.path.join(repo.wjoin(lfutil.standin(path)))
565
565
566 fullpats = scmutil.expandpats(pats)
566 fullpats = scmutil.expandpats(pats)
567 dest = fullpats[-1]
567 dest = fullpats[-1]
568
568
569 if os.path.isdir(dest):
569 if os.path.isdir(dest):
570 if not os.path.isdir(makestandin(dest)):
570 if not os.path.isdir(makestandin(dest)):
571 os.makedirs(makestandin(dest))
571 os.makedirs(makestandin(dest))
572 # This could copy both lfiles and normal files in one command,
572 # This could copy both lfiles and normal files in one command,
573 # but we don't want to do that. First replace their matcher to
573 # but we don't want to do that. First replace their matcher to
574 # only match normal files and run it, then replace it to just
574 # only match normal files and run it, then replace it to just
575 # match largefiles and run it again.
575 # match largefiles and run it again.
576 nonormalfiles = False
576 nonormalfiles = False
577 nolfiles = False
577 nolfiles = False
578 installnormalfilesmatchfn(repo[None].manifest())
578 installnormalfilesmatchfn(repo[None].manifest())
579 try:
579 try:
580 try:
580 try:
581 result = orig(ui, repo, pats, opts, rename)
581 result = orig(ui, repo, pats, opts, rename)
582 except util.Abort, e:
582 except util.Abort, e:
583 if str(e) != _('no files to copy'):
583 if str(e) != _('no files to copy'):
584 raise e
584 raise e
585 else:
585 else:
586 nonormalfiles = True
586 nonormalfiles = True
587 result = 0
587 result = 0
588 finally:
588 finally:
589 restorematchfn()
589 restorematchfn()
590
590
591 # The first rename can cause our current working directory to be removed.
591 # The first rename can cause our current working directory to be removed.
592 # In that case there is nothing left to copy/rename so just quit.
592 # In that case there is nothing left to copy/rename so just quit.
593 try:
593 try:
594 repo.getcwd()
594 repo.getcwd()
595 except OSError:
595 except OSError:
596 return result
596 return result
597
597
598 try:
598 try:
599 try:
599 try:
600 # When we call orig below it creates the standins but we don't add
600 # When we call orig below it creates the standins but we don't add
601 # them to the dir state until later so lock during that time.
601 # them to the dir state until later so lock during that time.
602 wlock = repo.wlock()
602 wlock = repo.wlock()
603
603
604 manifest = repo[None].manifest()
604 manifest = repo[None].manifest()
605 def overridematch(ctx, pats=[], opts={}, globbed=False,
605 def overridematch(ctx, pats=[], opts={}, globbed=False,
606 default='relpath'):
606 default='relpath'):
607 newpats = []
607 newpats = []
608 # The patterns were previously mangled to add the standin
608 # The patterns were previously mangled to add the standin
609 # directory; we need to remove that now
609 # directory; we need to remove that now
610 for pat in pats:
610 for pat in pats:
611 if match_.patkind(pat) is None and lfutil.shortname in pat:
611 if match_.patkind(pat) is None and lfutil.shortname in pat:
612 newpats.append(pat.replace(lfutil.shortname, ''))
612 newpats.append(pat.replace(lfutil.shortname, ''))
613 else:
613 else:
614 newpats.append(pat)
614 newpats.append(pat)
615 match = oldmatch(ctx, newpats, opts, globbed, default)
615 match = oldmatch(ctx, newpats, opts, globbed, default)
616 m = copy.copy(match)
616 m = copy.copy(match)
617 lfile = lambda f: lfutil.standin(f) in manifest
617 lfile = lambda f: lfutil.standin(f) in manifest
618 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
618 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
619 m._fmap = set(m._files)
619 m._fmap = set(m._files)
620 origmatchfn = m.matchfn
620 origmatchfn = m.matchfn
621 m.matchfn = lambda f: (lfutil.isstandin(f) and
621 m.matchfn = lambda f: (lfutil.isstandin(f) and
622 (f in manifest) and
622 (f in manifest) and
623 origmatchfn(lfutil.splitstandin(f)) or
623 origmatchfn(lfutil.splitstandin(f)) or
624 None)
624 None)
625 return m
625 return m
626 oldmatch = installmatchfn(overridematch)
626 oldmatch = installmatchfn(overridematch)
627 listpats = []
627 listpats = []
628 for pat in pats:
628 for pat in pats:
629 if match_.patkind(pat) is not None:
629 if match_.patkind(pat) is not None:
630 listpats.append(pat)
630 listpats.append(pat)
631 else:
631 else:
632 listpats.append(makestandin(pat))
632 listpats.append(makestandin(pat))
633
633
634 try:
634 try:
635 origcopyfile = util.copyfile
635 origcopyfile = util.copyfile
636 copiedfiles = []
636 copiedfiles = []
637 def overridecopyfile(src, dest):
637 def overridecopyfile(src, dest):
638 if (lfutil.shortname in src and
638 if (lfutil.shortname in src and
639 dest.startswith(repo.wjoin(lfutil.shortname))):
639 dest.startswith(repo.wjoin(lfutil.shortname))):
640 destlfile = dest.replace(lfutil.shortname, '')
640 destlfile = dest.replace(lfutil.shortname, '')
641 if not opts['force'] and os.path.exists(destlfile):
641 if not opts['force'] and os.path.exists(destlfile):
642 raise IOError('',
642 raise IOError('',
643 _('destination largefile already exists'))
643 _('destination largefile already exists'))
644 copiedfiles.append((src, dest))
644 copiedfiles.append((src, dest))
645 origcopyfile(src, dest)
645 origcopyfile(src, dest)
646
646
647 util.copyfile = overridecopyfile
647 util.copyfile = overridecopyfile
648 result += orig(ui, repo, listpats, opts, rename)
648 result += orig(ui, repo, listpats, opts, rename)
649 finally:
649 finally:
650 util.copyfile = origcopyfile
650 util.copyfile = origcopyfile
651
651
652 lfdirstate = lfutil.openlfdirstate(ui, repo)
652 lfdirstate = lfutil.openlfdirstate(ui, repo)
653 for (src, dest) in copiedfiles:
653 for (src, dest) in copiedfiles:
654 if (lfutil.shortname in src and
654 if (lfutil.shortname in src and
655 dest.startswith(repo.wjoin(lfutil.shortname))):
655 dest.startswith(repo.wjoin(lfutil.shortname))):
656 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
656 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
657 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
657 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
658 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
658 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
659 if not os.path.isdir(destlfiledir):
659 if not os.path.isdir(destlfiledir):
660 os.makedirs(destlfiledir)
660 os.makedirs(destlfiledir)
661 if rename:
661 if rename:
662 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
662 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
663
663
664 # The file is gone, but this deletes any empty parent
664 # The file is gone, but this deletes any empty parent
665 # directories as a side-effect.
665 # directories as a side-effect.
666 util.unlinkpath(repo.wjoin(srclfile), True)
666 util.unlinkpath(repo.wjoin(srclfile), True)
667 lfdirstate.remove(srclfile)
667 lfdirstate.remove(srclfile)
668 else:
668 else:
669 util.copyfile(repo.wjoin(srclfile),
669 util.copyfile(repo.wjoin(srclfile),
670 repo.wjoin(destlfile))
670 repo.wjoin(destlfile))
671
671
672 lfdirstate.add(destlfile)
672 lfdirstate.add(destlfile)
673 lfdirstate.write()
673 lfdirstate.write()
674 except util.Abort, e:
674 except util.Abort, e:
675 if str(e) != _('no files to copy'):
675 if str(e) != _('no files to copy'):
676 raise e
676 raise e
677 else:
677 else:
678 nolfiles = True
678 nolfiles = True
679 finally:
679 finally:
680 restorematchfn()
680 restorematchfn()
681 wlock.release()
681 wlock.release()
682
682
683 if nolfiles and nonormalfiles:
683 if nolfiles and nonormalfiles:
684 raise util.Abort(_('no files to copy'))
684 raise util.Abort(_('no files to copy'))
685
685
686 return result
686 return result
687
687
688 # When the user calls revert, we have to be careful to not revert any
688 # When the user calls revert, we have to be careful to not revert any
689 # changes to other largefiles accidentally. This means we have to keep
689 # changes to other largefiles accidentally. This means we have to keep
690 # track of the largefiles that are being reverted so we only pull down
690 # track of the largefiles that are being reverted so we only pull down
691 # the necessary largefiles.
691 # the necessary largefiles.
692 #
692 #
693 # Standins are only updated (to match the hash of largefiles) before
693 # Standins are only updated (to match the hash of largefiles) before
694 # commits. Update the standins then run the original revert, changing
694 # commits. Update the standins then run the original revert, changing
695 # the matcher to hit standins instead of largefiles. Based on the
695 # the matcher to hit standins instead of largefiles. Based on the
696 # resulting standins update the largefiles.
696 # resulting standins update the largefiles.
697 def overriderevert(orig, ui, repo, *pats, **opts):
697 def overriderevert(orig, ui, repo, *pats, **opts):
698 # Because we put the standins in a bad state (by updating them)
698 # Because we put the standins in a bad state (by updating them)
699 # and then return them to a correct state we need to lock to
699 # and then return them to a correct state we need to lock to
700 # prevent others from changing them in their incorrect state.
700 # prevent others from changing them in their incorrect state.
701 wlock = repo.wlock()
701 wlock = repo.wlock()
702 try:
702 try:
703 lfdirstate = lfutil.openlfdirstate(ui, repo)
703 lfdirstate = lfutil.openlfdirstate(ui, repo)
704 s = lfutil.lfdirstatestatus(lfdirstate, repo)
704 s = lfutil.lfdirstatestatus(lfdirstate, repo)
705 lfdirstate.write()
705 lfdirstate.write()
706 for lfile in s.modified:
706 for lfile in s.modified:
707 lfutil.updatestandin(repo, lfutil.standin(lfile))
707 lfutil.updatestandin(repo, lfutil.standin(lfile))
708 for lfile in s.deleted:
708 for lfile in s.deleted:
709 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
709 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
710 os.unlink(repo.wjoin(lfutil.standin(lfile)))
710 os.unlink(repo.wjoin(lfutil.standin(lfile)))
711
711
712 oldstandins = lfutil.getstandinsstate(repo)
712 oldstandins = lfutil.getstandinsstate(repo)
713
713
714 def overridematch(ctx, pats=[], opts={}, globbed=False,
714 def overridematch(ctx, pats=[], opts={}, globbed=False,
715 default='relpath'):
715 default='relpath'):
716 match = oldmatch(ctx, pats, opts, globbed, default)
716 match = oldmatch(ctx, pats, opts, globbed, default)
717 m = copy.copy(match)
717 m = copy.copy(match)
718 def tostandin(f):
718 def tostandin(f):
719 if lfutil.standin(f) in ctx:
719 if lfutil.standin(f) in ctx:
720 return lfutil.standin(f)
720 return lfutil.standin(f)
721 elif lfutil.standin(f) in repo[None]:
721 elif lfutil.standin(f) in repo[None]:
722 return None
722 return None
723 return f
723 return f
724 m._files = [tostandin(f) for f in m._files]
724 m._files = [tostandin(f) for f in m._files]
725 m._files = [f for f in m._files if f is not None]
725 m._files = [f for f in m._files if f is not None]
726 m._fmap = set(m._files)
726 m._fmap = set(m._files)
727 origmatchfn = m.matchfn
727 origmatchfn = m.matchfn
728 def matchfn(f):
728 def matchfn(f):
729 if lfutil.isstandin(f):
729 if lfutil.isstandin(f):
730 return (origmatchfn(lfutil.splitstandin(f)) and
730 return (origmatchfn(lfutil.splitstandin(f)) and
731 (f in repo[None] or f in ctx))
731 (f in repo[None] or f in ctx))
732 return origmatchfn(f)
732 return origmatchfn(f)
733 m.matchfn = matchfn
733 m.matchfn = matchfn
734 return m
734 return m
735 oldmatch = installmatchfn(overridematch)
735 oldmatch = installmatchfn(overridematch)
736 try:
736 try:
737 orig(ui, repo, *pats, **opts)
737 orig(ui, repo, *pats, **opts)
738 finally:
738 finally:
739 restorematchfn()
739 restorematchfn()
740
740
741 newstandins = lfutil.getstandinsstate(repo)
741 newstandins = lfutil.getstandinsstate(repo)
742 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
742 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
743 # lfdirstate should be 'normallookup'-ed for updated files,
743 # lfdirstate should be 'normallookup'-ed for updated files,
744 # because reverting doesn't touch dirstate for 'normal' files
744 # because reverting doesn't touch dirstate for 'normal' files
745 # when target revision is explicitly specified: in such case,
745 # when target revision is explicitly specified: in such case,
746 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
746 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
747 # of target (standin) file.
747 # of target (standin) file.
748 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
748 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
749 normallookup=True)
749 normallookup=True)
750
750
751 finally:
751 finally:
752 wlock.release()
752 wlock.release()
753
753
754 # after pulling changesets, we need to take some extra care to get
754 # after pulling changesets, we need to take some extra care to get
755 # largefiles updated remotely
755 # largefiles updated remotely
756 def overridepull(orig, ui, repo, source=None, **opts):
756 def overridepull(orig, ui, repo, source=None, **opts):
757 revsprepull = len(repo)
757 revsprepull = len(repo)
758 if not source:
758 if not source:
759 source = 'default'
759 source = 'default'
760 repo.lfpullsource = source
760 repo.lfpullsource = source
761 result = orig(ui, repo, source, **opts)
761 result = orig(ui, repo, source, **opts)
762 revspostpull = len(repo)
762 revspostpull = len(repo)
763 lfrevs = opts.get('lfrev', [])
763 lfrevs = opts.get('lfrev', [])
764 if opts.get('all_largefiles'):
764 if opts.get('all_largefiles'):
765 lfrevs.append('pulled()')
765 lfrevs.append('pulled()')
766 if lfrevs and revspostpull > revsprepull:
766 if lfrevs and revspostpull > revsprepull:
767 numcached = 0
767 numcached = 0
768 repo.firstpulled = revsprepull # for pulled() revset expression
768 repo.firstpulled = revsprepull # for pulled() revset expression
769 try:
769 try:
770 for rev in scmutil.revrange(repo, lfrevs):
770 for rev in scmutil.revrange(repo, lfrevs):
771 ui.note(_('pulling largefiles for revision %s\n') % rev)
771 ui.note(_('pulling largefiles for revision %s\n') % rev)
772 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
772 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
773 numcached += len(cached)
773 numcached += len(cached)
774 finally:
774 finally:
775 del repo.firstpulled
775 del repo.firstpulled
776 ui.status(_("%d largefiles cached\n") % numcached)
776 ui.status(_("%d largefiles cached\n") % numcached)
777 return result
777 return result
778
778
779 def pulledrevsetsymbol(repo, subset, x):
779 def pulledrevsetsymbol(repo, subset, x):
780 """``pulled()``
780 """``pulled()``
781 Changesets that just has been pulled.
781 Changesets that just has been pulled.
782
782
783 Only available with largefiles from pull --lfrev expressions.
783 Only available with largefiles from pull --lfrev expressions.
784
784
785 .. container:: verbose
785 .. container:: verbose
786
786
787 Some examples:
787 Some examples:
788
788
789 - pull largefiles for all new changesets::
789 - pull largefiles for all new changesets::
790
790
791 hg pull -lfrev "pulled()"
791 hg pull -lfrev "pulled()"
792
792
793 - pull largefiles for all new branch heads::
793 - pull largefiles for all new branch heads::
794
794
795 hg pull -lfrev "head(pulled()) and not closed()"
795 hg pull -lfrev "head(pulled()) and not closed()"
796
796
797 """
797 """
798
798
799 try:
799 try:
800 firstpulled = repo.firstpulled
800 firstpulled = repo.firstpulled
801 except AttributeError:
801 except AttributeError:
802 raise util.Abort(_("pulled() only available in --lfrev"))
802 raise util.Abort(_("pulled() only available in --lfrev"))
803 return revset.baseset([r for r in subset if r >= firstpulled])
803 return revset.baseset([r for r in subset if r >= firstpulled])
804
804
805 def overrideclone(orig, ui, source, dest=None, **opts):
805 def overrideclone(orig, ui, source, dest=None, **opts):
806 d = dest
806 d = dest
807 if d is None:
807 if d is None:
808 d = hg.defaultdest(source)
808 d = hg.defaultdest(source)
809 if opts.get('all_largefiles') and not hg.islocal(d):
809 if opts.get('all_largefiles') and not hg.islocal(d):
810 raise util.Abort(_(
810 raise util.Abort(_(
811 '--all-largefiles is incompatible with non-local destination %s') %
811 '--all-largefiles is incompatible with non-local destination %s') %
812 d)
812 d)
813
813
814 return orig(ui, source, dest, **opts)
814 return orig(ui, source, dest, **opts)
815
815
816 def hgclone(orig, ui, opts, *args, **kwargs):
816 def hgclone(orig, ui, opts, *args, **kwargs):
817 result = orig(ui, opts, *args, **kwargs)
817 result = orig(ui, opts, *args, **kwargs)
818
818
819 if result is not None:
819 if result is not None:
820 sourcerepo, destrepo = result
820 sourcerepo, destrepo = result
821 repo = destrepo.local()
821 repo = destrepo.local()
822
822
823 # Caching is implicitly limited to 'rev' option, since the dest repo was
823 # Caching is implicitly limited to 'rev' option, since the dest repo was
824 # truncated at that point. The user may expect a download count with
824 # truncated at that point. The user may expect a download count with
825 # this option, so attempt whether or not this is a largefile repo.
825 # this option, so attempt whether or not this is a largefile repo.
826 if opts.get('all_largefiles'):
826 if opts.get('all_largefiles'):
827 success, missing = lfcommands.downloadlfiles(ui, repo, None)
827 success, missing = lfcommands.downloadlfiles(ui, repo, None)
828
828
829 if missing != 0:
829 if missing != 0:
830 return None
830 return None
831
831
832 return result
832 return result
833
833
834 def overriderebase(orig, ui, repo, **opts):
834 def overriderebase(orig, ui, repo, **opts):
835 if not util.safehasattr(repo, '_largefilesenabled'):
836 return orig(ui, repo, **opts)
837
835 resuming = opts.get('continue')
838 resuming = opts.get('continue')
836 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
839 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
837 repo._lfstatuswriters.append(lambda *msg, **opts: None)
840 repo._lfstatuswriters.append(lambda *msg, **opts: None)
838 try:
841 try:
839 return orig(ui, repo, **opts)
842 return orig(ui, repo, **opts)
840 finally:
843 finally:
841 repo._lfstatuswriters.pop()
844 repo._lfstatuswriters.pop()
842 repo._lfcommithooks.pop()
845 repo._lfcommithooks.pop()
843
846
844 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
847 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
845 prefix=None, mtime=None, subrepos=None):
848 prefix=None, mtime=None, subrepos=None):
846 # No need to lock because we are only reading history and
849 # No need to lock because we are only reading history and
847 # largefile caches, neither of which are modified.
850 # largefile caches, neither of which are modified.
848 lfcommands.cachelfiles(repo.ui, repo, node)
851 lfcommands.cachelfiles(repo.ui, repo, node)
849
852
850 if kind not in archival.archivers:
853 if kind not in archival.archivers:
851 raise util.Abort(_("unknown archive type '%s'") % kind)
854 raise util.Abort(_("unknown archive type '%s'") % kind)
852
855
853 ctx = repo[node]
856 ctx = repo[node]
854
857
855 if kind == 'files':
858 if kind == 'files':
856 if prefix:
859 if prefix:
857 raise util.Abort(
860 raise util.Abort(
858 _('cannot give prefix when archiving to files'))
861 _('cannot give prefix when archiving to files'))
859 else:
862 else:
860 prefix = archival.tidyprefix(dest, kind, prefix)
863 prefix = archival.tidyprefix(dest, kind, prefix)
861
864
862 def write(name, mode, islink, getdata):
865 def write(name, mode, islink, getdata):
863 if matchfn and not matchfn(name):
866 if matchfn and not matchfn(name):
864 return
867 return
865 data = getdata()
868 data = getdata()
866 if decode:
869 if decode:
867 data = repo.wwritedata(name, data)
870 data = repo.wwritedata(name, data)
868 archiver.addfile(prefix + name, mode, islink, data)
871 archiver.addfile(prefix + name, mode, islink, data)
869
872
870 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
873 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
871
874
872 if repo.ui.configbool("ui", "archivemeta", True):
875 if repo.ui.configbool("ui", "archivemeta", True):
873 def metadata():
876 def metadata():
874 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
877 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
875 hex(repo.changelog.node(0)), hex(node), ctx.branch())
878 hex(repo.changelog.node(0)), hex(node), ctx.branch())
876
879
877 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
880 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
878 if repo.tagtype(t) == 'global')
881 if repo.tagtype(t) == 'global')
879 if not tags:
882 if not tags:
880 repo.ui.pushbuffer()
883 repo.ui.pushbuffer()
881 opts = {'template': '{latesttag}\n{latesttagdistance}',
884 opts = {'template': '{latesttag}\n{latesttagdistance}',
882 'style': '', 'patch': None, 'git': None}
885 'style': '', 'patch': None, 'git': None}
883 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
886 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
884 ltags, dist = repo.ui.popbuffer().split('\n')
887 ltags, dist = repo.ui.popbuffer().split('\n')
885 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
888 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
886 tags += 'latesttagdistance: %s\n' % dist
889 tags += 'latesttagdistance: %s\n' % dist
887
890
888 return base + tags
891 return base + tags
889
892
890 write('.hg_archival.txt', 0644, False, metadata)
893 write('.hg_archival.txt', 0644, False, metadata)
891
894
892 for f in ctx:
895 for f in ctx:
893 ff = ctx.flags(f)
896 ff = ctx.flags(f)
894 getdata = ctx[f].data
897 getdata = ctx[f].data
895 if lfutil.isstandin(f):
898 if lfutil.isstandin(f):
896 path = lfutil.findfile(repo, getdata().strip())
899 path = lfutil.findfile(repo, getdata().strip())
897 if path is None:
900 if path is None:
898 raise util.Abort(
901 raise util.Abort(
899 _('largefile %s not found in repo store or system cache')
902 _('largefile %s not found in repo store or system cache')
900 % lfutil.splitstandin(f))
903 % lfutil.splitstandin(f))
901 f = lfutil.splitstandin(f)
904 f = lfutil.splitstandin(f)
902
905
903 def getdatafn():
906 def getdatafn():
904 fd = None
907 fd = None
905 try:
908 try:
906 fd = open(path, 'rb')
909 fd = open(path, 'rb')
907 return fd.read()
910 return fd.read()
908 finally:
911 finally:
909 if fd:
912 if fd:
910 fd.close()
913 fd.close()
911
914
912 getdata = getdatafn
915 getdata = getdatafn
913 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
916 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
914
917
915 if subrepos:
918 if subrepos:
916 for subpath in sorted(ctx.substate):
919 for subpath in sorted(ctx.substate):
917 sub = ctx.sub(subpath)
920 sub = ctx.sub(subpath)
918 submatch = match_.narrowmatcher(subpath, matchfn)
921 submatch = match_.narrowmatcher(subpath, matchfn)
919 sub.archive(archiver, prefix, submatch)
922 sub.archive(archiver, prefix, submatch)
920
923
921 archiver.done()
924 archiver.done()
922
925
923 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
926 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
924 repo._get(repo._state + ('hg',))
927 repo._get(repo._state + ('hg',))
925 rev = repo._state[1]
928 rev = repo._state[1]
926 ctx = repo._repo[rev]
929 ctx = repo._repo[rev]
927
930
928 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
931 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
929
932
930 def write(name, mode, islink, getdata):
933 def write(name, mode, islink, getdata):
931 # At this point, the standin has been replaced with the largefile name,
934 # At this point, the standin has been replaced with the largefile name,
932 # so the normal matcher works here without the lfutil variants.
935 # so the normal matcher works here without the lfutil variants.
933 if match and not match(f):
936 if match and not match(f):
934 return
937 return
935 data = getdata()
938 data = getdata()
936
939
937 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
940 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
938
941
939 for f in ctx:
942 for f in ctx:
940 ff = ctx.flags(f)
943 ff = ctx.flags(f)
941 getdata = ctx[f].data
944 getdata = ctx[f].data
942 if lfutil.isstandin(f):
945 if lfutil.isstandin(f):
943 path = lfutil.findfile(repo._repo, getdata().strip())
946 path = lfutil.findfile(repo._repo, getdata().strip())
944 if path is None:
947 if path is None:
945 raise util.Abort(
948 raise util.Abort(
946 _('largefile %s not found in repo store or system cache')
949 _('largefile %s not found in repo store or system cache')
947 % lfutil.splitstandin(f))
950 % lfutil.splitstandin(f))
948 f = lfutil.splitstandin(f)
951 f = lfutil.splitstandin(f)
949
952
950 def getdatafn():
953 def getdatafn():
951 fd = None
954 fd = None
952 try:
955 try:
953 fd = open(os.path.join(prefix, path), 'rb')
956 fd = open(os.path.join(prefix, path), 'rb')
954 return fd.read()
957 return fd.read()
955 finally:
958 finally:
956 if fd:
959 if fd:
957 fd.close()
960 fd.close()
958
961
959 getdata = getdatafn
962 getdata = getdatafn
960
963
961 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
964 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
962
965
963 for subpath in sorted(ctx.substate):
966 for subpath in sorted(ctx.substate):
964 sub = ctx.sub(subpath)
967 sub = ctx.sub(subpath)
965 submatch = match_.narrowmatcher(subpath, match)
968 submatch = match_.narrowmatcher(subpath, match)
966 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
969 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
967
970
968 # If a largefile is modified, the change is not reflected in its
971 # If a largefile is modified, the change is not reflected in its
969 # standin until a commit. cmdutil.bailifchanged() raises an exception
972 # standin until a commit. cmdutil.bailifchanged() raises an exception
970 # if the repo has uncommitted changes. Wrap it to also check if
973 # if the repo has uncommitted changes. Wrap it to also check if
971 # largefiles were changed. This is used by bisect, backout and fetch.
974 # largefiles were changed. This is used by bisect, backout and fetch.
972 def overridebailifchanged(orig, repo):
975 def overridebailifchanged(orig, repo):
973 orig(repo)
976 orig(repo)
974 repo.lfstatus = True
977 repo.lfstatus = True
975 s = repo.status()
978 s = repo.status()
976 repo.lfstatus = False
979 repo.lfstatus = False
977 if s.modified or s.added or s.removed or s.deleted:
980 if s.modified or s.added or s.removed or s.deleted:
978 raise util.Abort(_('uncommitted changes'))
981 raise util.Abort(_('uncommitted changes'))
979
982
980 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
983 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
981 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
984 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
982 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
985 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
983 m = composelargefilematcher(match, repo[None].manifest())
986 m = composelargefilematcher(match, repo[None].manifest())
984
987
985 try:
988 try:
986 repo.lfstatus = True
989 repo.lfstatus = True
987 s = repo.status(match=m, clean=True)
990 s = repo.status(match=m, clean=True)
988 finally:
991 finally:
989 repo.lfstatus = False
992 repo.lfstatus = False
990 forget = sorted(s.modified + s.added + s.deleted + s.clean)
993 forget = sorted(s.modified + s.added + s.deleted + s.clean)
991 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
994 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
992
995
993 for f in forget:
996 for f in forget:
994 if lfutil.standin(f) not in repo.dirstate and not \
997 if lfutil.standin(f) not in repo.dirstate and not \
995 repo.wvfs.isdir(lfutil.standin(f)):
998 repo.wvfs.isdir(lfutil.standin(f)):
996 ui.warn(_('not removing %s: file is already untracked\n')
999 ui.warn(_('not removing %s: file is already untracked\n')
997 % m.rel(f))
1000 % m.rel(f))
998 bad.append(f)
1001 bad.append(f)
999
1002
1000 for f in forget:
1003 for f in forget:
1001 if ui.verbose or not m.exact(f):
1004 if ui.verbose or not m.exact(f):
1002 ui.status(_('removing %s\n') % m.rel(f))
1005 ui.status(_('removing %s\n') % m.rel(f))
1003
1006
1004 # Need to lock because standin files are deleted then removed from the
1007 # Need to lock because standin files are deleted then removed from the
1005 # repository and we could race in-between.
1008 # repository and we could race in-between.
1006 wlock = repo.wlock()
1009 wlock = repo.wlock()
1007 try:
1010 try:
1008 lfdirstate = lfutil.openlfdirstate(ui, repo)
1011 lfdirstate = lfutil.openlfdirstate(ui, repo)
1009 for f in forget:
1012 for f in forget:
1010 if lfdirstate[f] == 'a':
1013 if lfdirstate[f] == 'a':
1011 lfdirstate.drop(f)
1014 lfdirstate.drop(f)
1012 else:
1015 else:
1013 lfdirstate.remove(f)
1016 lfdirstate.remove(f)
1014 lfdirstate.write()
1017 lfdirstate.write()
1015 standins = [lfutil.standin(f) for f in forget]
1018 standins = [lfutil.standin(f) for f in forget]
1016 for f in standins:
1019 for f in standins:
1017 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1020 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1018 rejected = repo[None].forget(standins)
1021 rejected = repo[None].forget(standins)
1019 finally:
1022 finally:
1020 wlock.release()
1023 wlock.release()
1021
1024
1022 bad.extend(f for f in rejected if f in m.files())
1025 bad.extend(f for f in rejected if f in m.files())
1023 forgot.extend(f for f in forget if f not in rejected)
1026 forgot.extend(f for f in forget if f not in rejected)
1024 return bad, forgot
1027 return bad, forgot
1025
1028
1026 def _getoutgoings(repo, other, missing, addfunc):
1029 def _getoutgoings(repo, other, missing, addfunc):
1027 """get pairs of filename and largefile hash in outgoing revisions
1030 """get pairs of filename and largefile hash in outgoing revisions
1028 in 'missing'.
1031 in 'missing'.
1029
1032
1030 largefiles already existing on 'other' repository are ignored.
1033 largefiles already existing on 'other' repository are ignored.
1031
1034
1032 'addfunc' is invoked with each unique pairs of filename and
1035 'addfunc' is invoked with each unique pairs of filename and
1033 largefile hash value.
1036 largefile hash value.
1034 """
1037 """
1035 knowns = set()
1038 knowns = set()
1036 lfhashes = set()
1039 lfhashes = set()
1037 def dedup(fn, lfhash):
1040 def dedup(fn, lfhash):
1038 k = (fn, lfhash)
1041 k = (fn, lfhash)
1039 if k not in knowns:
1042 if k not in knowns:
1040 knowns.add(k)
1043 knowns.add(k)
1041 lfhashes.add(lfhash)
1044 lfhashes.add(lfhash)
1042 lfutil.getlfilestoupload(repo, missing, dedup)
1045 lfutil.getlfilestoupload(repo, missing, dedup)
1043 if lfhashes:
1046 if lfhashes:
1044 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1047 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1045 for fn, lfhash in knowns:
1048 for fn, lfhash in knowns:
1046 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1049 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1047 addfunc(fn, lfhash)
1050 addfunc(fn, lfhash)
1048
1051
1049 def outgoinghook(ui, repo, other, opts, missing):
1052 def outgoinghook(ui, repo, other, opts, missing):
1050 if opts.pop('large', None):
1053 if opts.pop('large', None):
1051 lfhashes = set()
1054 lfhashes = set()
1052 if ui.debugflag:
1055 if ui.debugflag:
1053 toupload = {}
1056 toupload = {}
1054 def addfunc(fn, lfhash):
1057 def addfunc(fn, lfhash):
1055 if fn not in toupload:
1058 if fn not in toupload:
1056 toupload[fn] = []
1059 toupload[fn] = []
1057 toupload[fn].append(lfhash)
1060 toupload[fn].append(lfhash)
1058 lfhashes.add(lfhash)
1061 lfhashes.add(lfhash)
1059 def showhashes(fn):
1062 def showhashes(fn):
1060 for lfhash in sorted(toupload[fn]):
1063 for lfhash in sorted(toupload[fn]):
1061 ui.debug(' %s\n' % (lfhash))
1064 ui.debug(' %s\n' % (lfhash))
1062 else:
1065 else:
1063 toupload = set()
1066 toupload = set()
1064 def addfunc(fn, lfhash):
1067 def addfunc(fn, lfhash):
1065 toupload.add(fn)
1068 toupload.add(fn)
1066 lfhashes.add(lfhash)
1069 lfhashes.add(lfhash)
1067 def showhashes(fn):
1070 def showhashes(fn):
1068 pass
1071 pass
1069 _getoutgoings(repo, other, missing, addfunc)
1072 _getoutgoings(repo, other, missing, addfunc)
1070
1073
1071 if not toupload:
1074 if not toupload:
1072 ui.status(_('largefiles: no files to upload\n'))
1075 ui.status(_('largefiles: no files to upload\n'))
1073 else:
1076 else:
1074 ui.status(_('largefiles to upload (%d entities):\n')
1077 ui.status(_('largefiles to upload (%d entities):\n')
1075 % (len(lfhashes)))
1078 % (len(lfhashes)))
1076 for file in sorted(toupload):
1079 for file in sorted(toupload):
1077 ui.status(lfutil.splitstandin(file) + '\n')
1080 ui.status(lfutil.splitstandin(file) + '\n')
1078 showhashes(file)
1081 showhashes(file)
1079 ui.status('\n')
1082 ui.status('\n')
1080
1083
1081 def summaryremotehook(ui, repo, opts, changes):
1084 def summaryremotehook(ui, repo, opts, changes):
1082 largeopt = opts.get('large', False)
1085 largeopt = opts.get('large', False)
1083 if changes is None:
1086 if changes is None:
1084 if largeopt:
1087 if largeopt:
1085 return (False, True) # only outgoing check is needed
1088 return (False, True) # only outgoing check is needed
1086 else:
1089 else:
1087 return (False, False)
1090 return (False, False)
1088 elif largeopt:
1091 elif largeopt:
1089 url, branch, peer, outgoing = changes[1]
1092 url, branch, peer, outgoing = changes[1]
1090 if peer is None:
1093 if peer is None:
1091 # i18n: column positioning for "hg summary"
1094 # i18n: column positioning for "hg summary"
1092 ui.status(_('largefiles: (no remote repo)\n'))
1095 ui.status(_('largefiles: (no remote repo)\n'))
1093 return
1096 return
1094
1097
1095 toupload = set()
1098 toupload = set()
1096 lfhashes = set()
1099 lfhashes = set()
1097 def addfunc(fn, lfhash):
1100 def addfunc(fn, lfhash):
1098 toupload.add(fn)
1101 toupload.add(fn)
1099 lfhashes.add(lfhash)
1102 lfhashes.add(lfhash)
1100 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1103 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1101
1104
1102 if not toupload:
1105 if not toupload:
1103 # i18n: column positioning for "hg summary"
1106 # i18n: column positioning for "hg summary"
1104 ui.status(_('largefiles: (no files to upload)\n'))
1107 ui.status(_('largefiles: (no files to upload)\n'))
1105 else:
1108 else:
1106 # i18n: column positioning for "hg summary"
1109 # i18n: column positioning for "hg summary"
1107 ui.status(_('largefiles: %d entities for %d files to upload\n')
1110 ui.status(_('largefiles: %d entities for %d files to upload\n')
1108 % (len(lfhashes), len(toupload)))
1111 % (len(lfhashes), len(toupload)))
1109
1112
1110 def overridesummary(orig, ui, repo, *pats, **opts):
1113 def overridesummary(orig, ui, repo, *pats, **opts):
1111 try:
1114 try:
1112 repo.lfstatus = True
1115 repo.lfstatus = True
1113 orig(ui, repo, *pats, **opts)
1116 orig(ui, repo, *pats, **opts)
1114 finally:
1117 finally:
1115 repo.lfstatus = False
1118 repo.lfstatus = False
1116
1119
1117 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1120 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1118 similarity=None):
1121 similarity=None):
1119 if not lfutil.islfilesrepo(repo):
1122 if not lfutil.islfilesrepo(repo):
1120 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1123 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1121 # Get the list of missing largefiles so we can remove them
1124 # Get the list of missing largefiles so we can remove them
1122 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1125 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1123 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1126 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1124 False, False, False)
1127 False, False, False)
1125
1128
1126 # Call into the normal remove code, but the removing of the standin, we want
1129 # Call into the normal remove code, but the removing of the standin, we want
1127 # to have handled by original addremove. Monkey patching here makes sure
1130 # to have handled by original addremove. Monkey patching here makes sure
1128 # we don't remove the standin in the largefiles code, preventing a very
1131 # we don't remove the standin in the largefiles code, preventing a very
1129 # confused state later.
1132 # confused state later.
1130 if s.deleted:
1133 if s.deleted:
1131 m = copy.copy(matcher)
1134 m = copy.copy(matcher)
1132
1135
1133 # The m._files and m._map attributes are not changed to the deleted list
1136 # The m._files and m._map attributes are not changed to the deleted list
1134 # because that affects the m.exact() test, which in turn governs whether
1137 # because that affects the m.exact() test, which in turn governs whether
1135 # or not the file name is printed, and how. Simply limit the original
1138 # or not the file name is printed, and how. Simply limit the original
1136 # matches to those in the deleted status list.
1139 # matches to those in the deleted status list.
1137 matchfn = m.matchfn
1140 matchfn = m.matchfn
1138 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1141 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1139
1142
1140 removelargefiles(repo.ui, repo, True, m, **opts)
1143 removelargefiles(repo.ui, repo, True, m, **opts)
1141 # Call into the normal add code, and any files that *should* be added as
1144 # Call into the normal add code, and any files that *should* be added as
1142 # largefiles will be
1145 # largefiles will be
1143 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1146 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1144 # Now that we've handled largefiles, hand off to the original addremove
1147 # Now that we've handled largefiles, hand off to the original addremove
1145 # function to take care of the rest. Make sure it doesn't do anything with
1148 # function to take care of the rest. Make sure it doesn't do anything with
1146 # largefiles by passing a matcher that will ignore them.
1149 # largefiles by passing a matcher that will ignore them.
1147 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1150 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1148 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1151 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1149
1152
1150 # Calling purge with --all will cause the largefiles to be deleted.
1153 # Calling purge with --all will cause the largefiles to be deleted.
1151 # Override repo.status to prevent this from happening.
1154 # Override repo.status to prevent this from happening.
1152 def overridepurge(orig, ui, repo, *dirs, **opts):
1155 def overridepurge(orig, ui, repo, *dirs, **opts):
1153 # XXX Monkey patching a repoview will not work. The assigned attribute will
1156 # XXX Monkey patching a repoview will not work. The assigned attribute will
1154 # be set on the unfiltered repo, but we will only lookup attributes in the
1157 # be set on the unfiltered repo, but we will only lookup attributes in the
1155 # unfiltered repo if the lookup in the repoview object itself fails. As the
1158 # unfiltered repo if the lookup in the repoview object itself fails. As the
1156 # monkey patched method exists on the repoview class the lookup will not
1159 # monkey patched method exists on the repoview class the lookup will not
1157 # fail. As a result, the original version will shadow the monkey patched
1160 # fail. As a result, the original version will shadow the monkey patched
1158 # one, defeating the monkey patch.
1161 # one, defeating the monkey patch.
1159 #
1162 #
1160 # As a work around we use an unfiltered repo here. We should do something
1163 # As a work around we use an unfiltered repo here. We should do something
1161 # cleaner instead.
1164 # cleaner instead.
1162 repo = repo.unfiltered()
1165 repo = repo.unfiltered()
1163 oldstatus = repo.status
1166 oldstatus = repo.status
1164 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1167 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1165 clean=False, unknown=False, listsubrepos=False):
1168 clean=False, unknown=False, listsubrepos=False):
1166 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1169 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1167 listsubrepos)
1170 listsubrepos)
1168 lfdirstate = lfutil.openlfdirstate(ui, repo)
1171 lfdirstate = lfutil.openlfdirstate(ui, repo)
1169 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1172 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1170 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1173 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1171 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1174 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1172 unknown, ignored, r.clean)
1175 unknown, ignored, r.clean)
1173 repo.status = overridestatus
1176 repo.status = overridestatus
1174 orig(ui, repo, *dirs, **opts)
1177 orig(ui, repo, *dirs, **opts)
1175 repo.status = oldstatus
1178 repo.status = oldstatus
1176 def overriderollback(orig, ui, repo, **opts):
1179 def overriderollback(orig, ui, repo, **opts):
1177 wlock = repo.wlock()
1180 wlock = repo.wlock()
1178 try:
1181 try:
1179 before = repo.dirstate.parents()
1182 before = repo.dirstate.parents()
1180 orphans = set(f for f in repo.dirstate
1183 orphans = set(f for f in repo.dirstate
1181 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1184 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1182 result = orig(ui, repo, **opts)
1185 result = orig(ui, repo, **opts)
1183 after = repo.dirstate.parents()
1186 after = repo.dirstate.parents()
1184 if before == after:
1187 if before == after:
1185 return result # no need to restore standins
1188 return result # no need to restore standins
1186
1189
1187 pctx = repo['.']
1190 pctx = repo['.']
1188 for f in repo.dirstate:
1191 for f in repo.dirstate:
1189 if lfutil.isstandin(f):
1192 if lfutil.isstandin(f):
1190 orphans.discard(f)
1193 orphans.discard(f)
1191 if repo.dirstate[f] == 'r':
1194 if repo.dirstate[f] == 'r':
1192 repo.wvfs.unlinkpath(f, ignoremissing=True)
1195 repo.wvfs.unlinkpath(f, ignoremissing=True)
1193 elif f in pctx:
1196 elif f in pctx:
1194 fctx = pctx[f]
1197 fctx = pctx[f]
1195 repo.wwrite(f, fctx.data(), fctx.flags())
1198 repo.wwrite(f, fctx.data(), fctx.flags())
1196 else:
1199 else:
1197 # content of standin is not so important in 'a',
1200 # content of standin is not so important in 'a',
1198 # 'm' or 'n' (coming from the 2nd parent) cases
1201 # 'm' or 'n' (coming from the 2nd parent) cases
1199 lfutil.writestandin(repo, f, '', False)
1202 lfutil.writestandin(repo, f, '', False)
1200 for standin in orphans:
1203 for standin in orphans:
1201 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1204 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1202
1205
1203 lfdirstate = lfutil.openlfdirstate(ui, repo)
1206 lfdirstate = lfutil.openlfdirstate(ui, repo)
1204 orphans = set(lfdirstate)
1207 orphans = set(lfdirstate)
1205 lfiles = lfutil.listlfiles(repo)
1208 lfiles = lfutil.listlfiles(repo)
1206 for file in lfiles:
1209 for file in lfiles:
1207 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1210 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1208 orphans.discard(file)
1211 orphans.discard(file)
1209 for lfile in orphans:
1212 for lfile in orphans:
1210 lfdirstate.drop(lfile)
1213 lfdirstate.drop(lfile)
1211 lfdirstate.write()
1214 lfdirstate.write()
1212 finally:
1215 finally:
1213 wlock.release()
1216 wlock.release()
1214 return result
1217 return result
1215
1218
1216 def overridetransplant(orig, ui, repo, *revs, **opts):
1219 def overridetransplant(orig, ui, repo, *revs, **opts):
1217 resuming = opts.get('continue')
1220 resuming = opts.get('continue')
1218 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1221 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1219 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1222 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1220 try:
1223 try:
1221 result = orig(ui, repo, *revs, **opts)
1224 result = orig(ui, repo, *revs, **opts)
1222 finally:
1225 finally:
1223 repo._lfstatuswriters.pop()
1226 repo._lfstatuswriters.pop()
1224 repo._lfcommithooks.pop()
1227 repo._lfcommithooks.pop()
1225 return result
1228 return result
1226
1229
1227 def overridecat(orig, ui, repo, file1, *pats, **opts):
1230 def overridecat(orig, ui, repo, file1, *pats, **opts):
1228 ctx = scmutil.revsingle(repo, opts.get('rev'))
1231 ctx = scmutil.revsingle(repo, opts.get('rev'))
1229 err = 1
1232 err = 1
1230 notbad = set()
1233 notbad = set()
1231 m = scmutil.match(ctx, (file1,) + pats, opts)
1234 m = scmutil.match(ctx, (file1,) + pats, opts)
1232 origmatchfn = m.matchfn
1235 origmatchfn = m.matchfn
1233 def lfmatchfn(f):
1236 def lfmatchfn(f):
1234 if origmatchfn(f):
1237 if origmatchfn(f):
1235 return True
1238 return True
1236 lf = lfutil.splitstandin(f)
1239 lf = lfutil.splitstandin(f)
1237 if lf is None:
1240 if lf is None:
1238 return False
1241 return False
1239 notbad.add(lf)
1242 notbad.add(lf)
1240 return origmatchfn(lf)
1243 return origmatchfn(lf)
1241 m.matchfn = lfmatchfn
1244 m.matchfn = lfmatchfn
1242 origbadfn = m.bad
1245 origbadfn = m.bad
1243 def lfbadfn(f, msg):
1246 def lfbadfn(f, msg):
1244 if not f in notbad:
1247 if not f in notbad:
1245 origbadfn(f, msg)
1248 origbadfn(f, msg)
1246 m.bad = lfbadfn
1249 m.bad = lfbadfn
1247 for f in ctx.walk(m):
1250 for f in ctx.walk(m):
1248 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1251 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1249 pathname=f)
1252 pathname=f)
1250 lf = lfutil.splitstandin(f)
1253 lf = lfutil.splitstandin(f)
1251 if lf is None or origmatchfn(f):
1254 if lf is None or origmatchfn(f):
1252 # duplicating unreachable code from commands.cat
1255 # duplicating unreachable code from commands.cat
1253 data = ctx[f].data()
1256 data = ctx[f].data()
1254 if opts.get('decode'):
1257 if opts.get('decode'):
1255 data = repo.wwritedata(f, data)
1258 data = repo.wwritedata(f, data)
1256 fp.write(data)
1259 fp.write(data)
1257 else:
1260 else:
1258 hash = lfutil.readstandin(repo, lf, ctx.rev())
1261 hash = lfutil.readstandin(repo, lf, ctx.rev())
1259 if not lfutil.inusercache(repo.ui, hash):
1262 if not lfutil.inusercache(repo.ui, hash):
1260 store = basestore._openstore(repo)
1263 store = basestore._openstore(repo)
1261 success, missing = store.get([(lf, hash)])
1264 success, missing = store.get([(lf, hash)])
1262 if len(success) != 1:
1265 if len(success) != 1:
1263 raise util.Abort(
1266 raise util.Abort(
1264 _('largefile %s is not in cache and could not be '
1267 _('largefile %s is not in cache and could not be '
1265 'downloaded') % lf)
1268 'downloaded') % lf)
1266 path = lfutil.usercachepath(repo.ui, hash)
1269 path = lfutil.usercachepath(repo.ui, hash)
1267 fpin = open(path, "rb")
1270 fpin = open(path, "rb")
1268 for chunk in util.filechunkiter(fpin, 128 * 1024):
1271 for chunk in util.filechunkiter(fpin, 128 * 1024):
1269 fp.write(chunk)
1272 fp.write(chunk)
1270 fpin.close()
1273 fpin.close()
1271 fp.close()
1274 fp.close()
1272 err = 0
1275 err = 0
1273 return err
1276 return err
1274
1277
1275 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1278 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1276 *args, **kwargs):
1279 *args, **kwargs):
1277 wlock = repo.wlock()
1280 wlock = repo.wlock()
1278 try:
1281 try:
1279 # branch | | |
1282 # branch | | |
1280 # merge | force | partial | action
1283 # merge | force | partial | action
1281 # -------+-------+---------+--------------
1284 # -------+-------+---------+--------------
1282 # x | x | x | linear-merge
1285 # x | x | x | linear-merge
1283 # o | x | x | branch-merge
1286 # o | x | x | branch-merge
1284 # x | o | x | overwrite (as clean update)
1287 # x | o | x | overwrite (as clean update)
1285 # o | o | x | force-branch-merge (*1)
1288 # o | o | x | force-branch-merge (*1)
1286 # x | x | o | (*)
1289 # x | x | o | (*)
1287 # o | x | o | (*)
1290 # o | x | o | (*)
1288 # x | o | o | overwrite (as revert)
1291 # x | o | o | overwrite (as revert)
1289 # o | o | o | (*)
1292 # o | o | o | (*)
1290 #
1293 #
1291 # (*) don't care
1294 # (*) don't care
1292 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1295 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1293
1296
1294 linearmerge = not branchmerge and not force and not partial
1297 linearmerge = not branchmerge and not force and not partial
1295
1298
1296 if linearmerge or (branchmerge and force and not partial):
1299 if linearmerge or (branchmerge and force and not partial):
1297 # update standins for linear-merge or force-branch-merge,
1300 # update standins for linear-merge or force-branch-merge,
1298 # because largefiles in the working directory may be modified
1301 # because largefiles in the working directory may be modified
1299 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1302 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1300 unsure, s = lfdirstate.status(match_.always(repo.root,
1303 unsure, s = lfdirstate.status(match_.always(repo.root,
1301 repo.getcwd()),
1304 repo.getcwd()),
1302 [], False, False, False)
1305 [], False, False, False)
1303 pctx = repo['.']
1306 pctx = repo['.']
1304 for lfile in unsure + s.modified:
1307 for lfile in unsure + s.modified:
1305 lfileabs = repo.wvfs.join(lfile)
1308 lfileabs = repo.wvfs.join(lfile)
1306 if not os.path.exists(lfileabs):
1309 if not os.path.exists(lfileabs):
1307 continue
1310 continue
1308 lfhash = lfutil.hashrepofile(repo, lfile)
1311 lfhash = lfutil.hashrepofile(repo, lfile)
1309 standin = lfutil.standin(lfile)
1312 standin = lfutil.standin(lfile)
1310 lfutil.writestandin(repo, standin, lfhash,
1313 lfutil.writestandin(repo, standin, lfhash,
1311 lfutil.getexecutable(lfileabs))
1314 lfutil.getexecutable(lfileabs))
1312 if (standin in pctx and
1315 if (standin in pctx and
1313 lfhash == lfutil.readstandin(repo, lfile, '.')):
1316 lfhash == lfutil.readstandin(repo, lfile, '.')):
1314 lfdirstate.normal(lfile)
1317 lfdirstate.normal(lfile)
1315 for lfile in s.added:
1318 for lfile in s.added:
1316 lfutil.updatestandin(repo, lfutil.standin(lfile))
1319 lfutil.updatestandin(repo, lfutil.standin(lfile))
1317 lfdirstate.write()
1320 lfdirstate.write()
1318
1321
1319 if linearmerge:
1322 if linearmerge:
1320 # Only call updatelfiles on the standins that have changed
1323 # Only call updatelfiles on the standins that have changed
1321 # to save time
1324 # to save time
1322 oldstandins = lfutil.getstandinsstate(repo)
1325 oldstandins = lfutil.getstandinsstate(repo)
1323
1326
1324 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1327 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1325
1328
1326 filelist = None
1329 filelist = None
1327 if linearmerge:
1330 if linearmerge:
1328 newstandins = lfutil.getstandinsstate(repo)
1331 newstandins = lfutil.getstandinsstate(repo)
1329 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1332 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1330
1333
1331 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1334 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1332 normallookup=partial, checked=linearmerge)
1335 normallookup=partial, checked=linearmerge)
1333
1336
1334 return result
1337 return result
1335 finally:
1338 finally:
1336 wlock.release()
1339 wlock.release()
1337
1340
1338 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1341 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1339 result = orig(repo, files, *args, **kwargs)
1342 result = orig(repo, files, *args, **kwargs)
1340
1343
1341 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1344 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1342 if filelist:
1345 if filelist:
1343 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1346 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1344 printmessage=False, normallookup=True)
1347 printmessage=False, normallookup=True)
1345
1348
1346 return result
1349 return result
@@ -1,371 +1,374 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles repositories: reposetup'''
9 '''setup for largefiles repositories: reposetup'''
10 import copy
10 import copy
11 import os
11 import os
12
12
13 from mercurial import error, manifest, match as match_, util
13 from mercurial import error, manifest, match as match_, util
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15 from mercurial import scmutil, localrepo
15 from mercurial import scmutil, localrepo
16
16
17 import lfcommands
17 import lfcommands
18 import lfutil
18 import lfutil
19
19
20 def reposetup(ui, repo):
20 def reposetup(ui, repo):
21 # wire repositories should be given new wireproto functions
21 # wire repositories should be given new wireproto functions
22 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
22 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
23 if not repo.local():
23 if not repo.local():
24 return
24 return
25
25
26 class lfilesrepo(repo.__class__):
26 class lfilesrepo(repo.__class__):
27 # the mark to examine whether "repo" object enables largefiles or not
28 _largefilesenabled = True
29
27 lfstatus = False
30 lfstatus = False
28 def status_nolfiles(self, *args, **kwargs):
31 def status_nolfiles(self, *args, **kwargs):
29 return super(lfilesrepo, self).status(*args, **kwargs)
32 return super(lfilesrepo, self).status(*args, **kwargs)
30
33
31 # When lfstatus is set, return a context that gives the names
34 # When lfstatus is set, return a context that gives the names
32 # of largefiles instead of their corresponding standins and
35 # of largefiles instead of their corresponding standins and
33 # identifies the largefiles as always binary, regardless of
36 # identifies the largefiles as always binary, regardless of
34 # their actual contents.
37 # their actual contents.
35 def __getitem__(self, changeid):
38 def __getitem__(self, changeid):
36 ctx = super(lfilesrepo, self).__getitem__(changeid)
39 ctx = super(lfilesrepo, self).__getitem__(changeid)
37 if self.lfstatus:
40 if self.lfstatus:
38 class lfilesmanifestdict(manifest.manifestdict):
41 class lfilesmanifestdict(manifest.manifestdict):
39 def __contains__(self, filename):
42 def __contains__(self, filename):
40 orig = super(lfilesmanifestdict, self).__contains__
43 orig = super(lfilesmanifestdict, self).__contains__
41 return orig(filename) or orig(lfutil.standin(filename))
44 return orig(filename) or orig(lfutil.standin(filename))
42 class lfilesctx(ctx.__class__):
45 class lfilesctx(ctx.__class__):
43 def files(self):
46 def files(self):
44 filenames = super(lfilesctx, self).files()
47 filenames = super(lfilesctx, self).files()
45 return [lfutil.splitstandin(f) or f for f in filenames]
48 return [lfutil.splitstandin(f) or f for f in filenames]
46 def manifest(self):
49 def manifest(self):
47 man1 = super(lfilesctx, self).manifest()
50 man1 = super(lfilesctx, self).manifest()
48 man1.__class__ = lfilesmanifestdict
51 man1.__class__ = lfilesmanifestdict
49 return man1
52 return man1
50 def filectx(self, path, fileid=None, filelog=None):
53 def filectx(self, path, fileid=None, filelog=None):
51 orig = super(lfilesctx, self).filectx
54 orig = super(lfilesctx, self).filectx
52 try:
55 try:
53 if filelog is not None:
56 if filelog is not None:
54 result = orig(path, fileid, filelog)
57 result = orig(path, fileid, filelog)
55 else:
58 else:
56 result = orig(path, fileid)
59 result = orig(path, fileid)
57 except error.LookupError:
60 except error.LookupError:
58 # Adding a null character will cause Mercurial to
61 # Adding a null character will cause Mercurial to
59 # identify this as a binary file.
62 # identify this as a binary file.
60 if filelog is not None:
63 if filelog is not None:
61 result = orig(lfutil.standin(path), fileid,
64 result = orig(lfutil.standin(path), fileid,
62 filelog)
65 filelog)
63 else:
66 else:
64 result = orig(lfutil.standin(path), fileid)
67 result = orig(lfutil.standin(path), fileid)
65 olddata = result.data
68 olddata = result.data
66 result.data = lambda: olddata() + '\0'
69 result.data = lambda: olddata() + '\0'
67 return result
70 return result
68 ctx.__class__ = lfilesctx
71 ctx.__class__ = lfilesctx
69 return ctx
72 return ctx
70
73
71 # Figure out the status of big files and insert them into the
74 # Figure out the status of big files and insert them into the
72 # appropriate list in the result. Also removes standin files
75 # appropriate list in the result. Also removes standin files
73 # from the listing. Revert to the original status if
76 # from the listing. Revert to the original status if
74 # self.lfstatus is False.
77 # self.lfstatus is False.
75 # XXX large file status is buggy when used on repo proxy.
78 # XXX large file status is buggy when used on repo proxy.
76 # XXX this needs to be investigated.
79 # XXX this needs to be investigated.
77 @localrepo.unfilteredmethod
80 @localrepo.unfilteredmethod
78 def status(self, node1='.', node2=None, match=None, ignored=False,
81 def status(self, node1='.', node2=None, match=None, ignored=False,
79 clean=False, unknown=False, listsubrepos=False):
82 clean=False, unknown=False, listsubrepos=False):
80 listignored, listclean, listunknown = ignored, clean, unknown
83 listignored, listclean, listunknown = ignored, clean, unknown
81 orig = super(lfilesrepo, self).status
84 orig = super(lfilesrepo, self).status
82 if not self.lfstatus:
85 if not self.lfstatus:
83 return orig(node1, node2, match, listignored, listclean,
86 return orig(node1, node2, match, listignored, listclean,
84 listunknown, listsubrepos)
87 listunknown, listsubrepos)
85
88
86 # some calls in this function rely on the old version of status
89 # some calls in this function rely on the old version of status
87 self.lfstatus = False
90 self.lfstatus = False
88 ctx1 = self[node1]
91 ctx1 = self[node1]
89 ctx2 = self[node2]
92 ctx2 = self[node2]
90 working = ctx2.rev() is None
93 working = ctx2.rev() is None
91 parentworking = working and ctx1 == self['.']
94 parentworking = working and ctx1 == self['.']
92
95
93 if match is None:
96 if match is None:
94 match = match_.always(self.root, self.getcwd())
97 match = match_.always(self.root, self.getcwd())
95
98
96 wlock = None
99 wlock = None
97 try:
100 try:
98 try:
101 try:
99 # updating the dirstate is optional
102 # updating the dirstate is optional
100 # so we don't wait on the lock
103 # so we don't wait on the lock
101 wlock = self.wlock(False)
104 wlock = self.wlock(False)
102 except error.LockError:
105 except error.LockError:
103 pass
106 pass
104
107
105 # First check if paths or patterns were specified on the
108 # First check if paths or patterns were specified on the
106 # command line. If there were, and they don't match any
109 # command line. If there were, and they don't match any
107 # largefiles, we should just bail here and let super
110 # largefiles, we should just bail here and let super
108 # handle it -- thus gaining a big performance boost.
111 # handle it -- thus gaining a big performance boost.
109 lfdirstate = lfutil.openlfdirstate(ui, self)
112 lfdirstate = lfutil.openlfdirstate(ui, self)
110 if not match.always():
113 if not match.always():
111 for f in lfdirstate:
114 for f in lfdirstate:
112 if match(f):
115 if match(f):
113 break
116 break
114 else:
117 else:
115 return orig(node1, node2, match, listignored, listclean,
118 return orig(node1, node2, match, listignored, listclean,
116 listunknown, listsubrepos)
119 listunknown, listsubrepos)
117
120
118 # Create a copy of match that matches standins instead
121 # Create a copy of match that matches standins instead
119 # of largefiles.
122 # of largefiles.
120 def tostandins(files):
123 def tostandins(files):
121 if not working:
124 if not working:
122 return files
125 return files
123 newfiles = []
126 newfiles = []
124 dirstate = self.dirstate
127 dirstate = self.dirstate
125 for f in files:
128 for f in files:
126 sf = lfutil.standin(f)
129 sf = lfutil.standin(f)
127 if sf in dirstate:
130 if sf in dirstate:
128 newfiles.append(sf)
131 newfiles.append(sf)
129 elif sf in dirstate.dirs():
132 elif sf in dirstate.dirs():
130 # Directory entries could be regular or
133 # Directory entries could be regular or
131 # standin, check both
134 # standin, check both
132 newfiles.extend((f, sf))
135 newfiles.extend((f, sf))
133 else:
136 else:
134 newfiles.append(f)
137 newfiles.append(f)
135 return newfiles
138 return newfiles
136
139
137 m = copy.copy(match)
140 m = copy.copy(match)
138 m._files = tostandins(m._files)
141 m._files = tostandins(m._files)
139
142
140 result = orig(node1, node2, m, ignored, clean, unknown,
143 result = orig(node1, node2, m, ignored, clean, unknown,
141 listsubrepos)
144 listsubrepos)
142 if working:
145 if working:
143
146
144 def sfindirstate(f):
147 def sfindirstate(f):
145 sf = lfutil.standin(f)
148 sf = lfutil.standin(f)
146 dirstate = self.dirstate
149 dirstate = self.dirstate
147 return sf in dirstate or sf in dirstate.dirs()
150 return sf in dirstate or sf in dirstate.dirs()
148
151
149 match._files = [f for f in match._files
152 match._files = [f for f in match._files
150 if sfindirstate(f)]
153 if sfindirstate(f)]
151 # Don't waste time getting the ignored and unknown
154 # Don't waste time getting the ignored and unknown
152 # files from lfdirstate
155 # files from lfdirstate
153 unsure, s = lfdirstate.status(match, [], False, listclean,
156 unsure, s = lfdirstate.status(match, [], False, listclean,
154 False)
157 False)
155 (modified, added, removed, clean) = (s.modified, s.added,
158 (modified, added, removed, clean) = (s.modified, s.added,
156 s.removed, s.clean)
159 s.removed, s.clean)
157 if parentworking:
160 if parentworking:
158 for lfile in unsure:
161 for lfile in unsure:
159 standin = lfutil.standin(lfile)
162 standin = lfutil.standin(lfile)
160 if standin not in ctx1:
163 if standin not in ctx1:
161 # from second parent
164 # from second parent
162 modified.append(lfile)
165 modified.append(lfile)
163 elif ctx1[standin].data().strip() \
166 elif ctx1[standin].data().strip() \
164 != lfutil.hashfile(self.wjoin(lfile)):
167 != lfutil.hashfile(self.wjoin(lfile)):
165 modified.append(lfile)
168 modified.append(lfile)
166 else:
169 else:
167 if listclean:
170 if listclean:
168 clean.append(lfile)
171 clean.append(lfile)
169 lfdirstate.normal(lfile)
172 lfdirstate.normal(lfile)
170 else:
173 else:
171 tocheck = unsure + modified + added + clean
174 tocheck = unsure + modified + added + clean
172 modified, added, clean = [], [], []
175 modified, added, clean = [], [], []
173 checkexec = self.dirstate._checkexec
176 checkexec = self.dirstate._checkexec
174
177
175 for lfile in tocheck:
178 for lfile in tocheck:
176 standin = lfutil.standin(lfile)
179 standin = lfutil.standin(lfile)
177 if standin in ctx1:
180 if standin in ctx1:
178 abslfile = self.wjoin(lfile)
181 abslfile = self.wjoin(lfile)
179 if ((ctx1[standin].data().strip() !=
182 if ((ctx1[standin].data().strip() !=
180 lfutil.hashfile(abslfile)) or
183 lfutil.hashfile(abslfile)) or
181 (checkexec and
184 (checkexec and
182 ('x' in ctx1.flags(standin)) !=
185 ('x' in ctx1.flags(standin)) !=
183 bool(lfutil.getexecutable(abslfile)))):
186 bool(lfutil.getexecutable(abslfile)))):
184 modified.append(lfile)
187 modified.append(lfile)
185 elif listclean:
188 elif listclean:
186 clean.append(lfile)
189 clean.append(lfile)
187 else:
190 else:
188 added.append(lfile)
191 added.append(lfile)
189
192
190 # at this point, 'removed' contains largefiles
193 # at this point, 'removed' contains largefiles
191 # marked as 'R' in the working context.
194 # marked as 'R' in the working context.
192 # then, largefiles not managed also in the target
195 # then, largefiles not managed also in the target
193 # context should be excluded from 'removed'.
196 # context should be excluded from 'removed'.
194 removed = [lfile for lfile in removed
197 removed = [lfile for lfile in removed
195 if lfutil.standin(lfile) in ctx1]
198 if lfutil.standin(lfile) in ctx1]
196
199
197 # Standins no longer found in lfdirstate has been
200 # Standins no longer found in lfdirstate has been
198 # removed
201 # removed
199 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
202 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
200 lfile = lfutil.splitstandin(standin)
203 lfile = lfutil.splitstandin(standin)
201 if not match(lfile):
204 if not match(lfile):
202 continue
205 continue
203 if lfile not in lfdirstate:
206 if lfile not in lfdirstate:
204 removed.append(lfile)
207 removed.append(lfile)
205
208
206 # Filter result lists
209 # Filter result lists
207 result = list(result)
210 result = list(result)
208
211
209 # Largefiles are not really removed when they're
212 # Largefiles are not really removed when they're
210 # still in the normal dirstate. Likewise, normal
213 # still in the normal dirstate. Likewise, normal
211 # files are not really removed if they are still in
214 # files are not really removed if they are still in
212 # lfdirstate. This happens in merges where files
215 # lfdirstate. This happens in merges where files
213 # change type.
216 # change type.
214 removed = [f for f in removed
217 removed = [f for f in removed
215 if f not in self.dirstate]
218 if f not in self.dirstate]
216 result[2] = [f for f in result[2]
219 result[2] = [f for f in result[2]
217 if f not in lfdirstate]
220 if f not in lfdirstate]
218
221
219 lfiles = set(lfdirstate._map)
222 lfiles = set(lfdirstate._map)
220 # Unknown files
223 # Unknown files
221 result[4] = set(result[4]).difference(lfiles)
224 result[4] = set(result[4]).difference(lfiles)
222 # Ignored files
225 # Ignored files
223 result[5] = set(result[5]).difference(lfiles)
226 result[5] = set(result[5]).difference(lfiles)
224 # combine normal files and largefiles
227 # combine normal files and largefiles
225 normals = [[fn for fn in filelist
228 normals = [[fn for fn in filelist
226 if not lfutil.isstandin(fn)]
229 if not lfutil.isstandin(fn)]
227 for filelist in result]
230 for filelist in result]
228 lfstatus = (modified, added, removed, s.deleted, [], [],
231 lfstatus = (modified, added, removed, s.deleted, [], [],
229 clean)
232 clean)
230 result = [sorted(list1 + list2)
233 result = [sorted(list1 + list2)
231 for (list1, list2) in zip(normals, lfstatus)]
234 for (list1, list2) in zip(normals, lfstatus)]
232 else: # not against working directory
235 else: # not against working directory
233 result = [[lfutil.splitstandin(f) or f for f in items]
236 result = [[lfutil.splitstandin(f) or f for f in items]
234 for items in result]
237 for items in result]
235
238
236 if wlock:
239 if wlock:
237 lfdirstate.write()
240 lfdirstate.write()
238
241
239 finally:
242 finally:
240 if wlock:
243 if wlock:
241 wlock.release()
244 wlock.release()
242
245
243 self.lfstatus = True
246 self.lfstatus = True
244 return scmutil.status(*result)
247 return scmutil.status(*result)
245
248
246 def commitctx(self, ctx, *args, **kwargs):
249 def commitctx(self, ctx, *args, **kwargs):
247 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
250 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
248 class lfilesctx(ctx.__class__):
251 class lfilesctx(ctx.__class__):
249 def markcommitted(self, node):
252 def markcommitted(self, node):
250 orig = super(lfilesctx, self).markcommitted
253 orig = super(lfilesctx, self).markcommitted
251 return lfutil.markcommitted(orig, self, node)
254 return lfutil.markcommitted(orig, self, node)
252 ctx.__class__ = lfilesctx
255 ctx.__class__ = lfilesctx
253 return node
256 return node
254
257
255 # Before commit, largefile standins have not had their
258 # Before commit, largefile standins have not had their
256 # contents updated to reflect the hash of their largefile.
259 # contents updated to reflect the hash of their largefile.
257 # Do that here.
260 # Do that here.
258 def commit(self, text="", user=None, date=None, match=None,
261 def commit(self, text="", user=None, date=None, match=None,
259 force=False, editor=False, extra={}):
262 force=False, editor=False, extra={}):
260 orig = super(lfilesrepo, self).commit
263 orig = super(lfilesrepo, self).commit
261
264
262 wlock = self.wlock()
265 wlock = self.wlock()
263 try:
266 try:
264 lfcommithook = self._lfcommithooks[-1]
267 lfcommithook = self._lfcommithooks[-1]
265 match = lfcommithook(self, match)
268 match = lfcommithook(self, match)
266 result = orig(text=text, user=user, date=date, match=match,
269 result = orig(text=text, user=user, date=date, match=match,
267 force=force, editor=editor, extra=extra)
270 force=force, editor=editor, extra=extra)
268 return result
271 return result
269 finally:
272 finally:
270 wlock.release()
273 wlock.release()
271
274
272 def push(self, remote, force=False, revs=None, newbranch=False):
275 def push(self, remote, force=False, revs=None, newbranch=False):
273 if remote.local():
276 if remote.local():
274 missing = set(self.requirements) - remote.local().supported
277 missing = set(self.requirements) - remote.local().supported
275 if missing:
278 if missing:
276 msg = _("required features are not"
279 msg = _("required features are not"
277 " supported in the destination:"
280 " supported in the destination:"
278 " %s") % (', '.join(sorted(missing)))
281 " %s") % (', '.join(sorted(missing)))
279 raise util.Abort(msg)
282 raise util.Abort(msg)
280 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
283 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
281 newbranch=newbranch)
284 newbranch=newbranch)
282
285
283 # TODO: _subdirlfs should be moved into "lfutil.py", because
286 # TODO: _subdirlfs should be moved into "lfutil.py", because
284 # it is referred only from "lfutil.updatestandinsbymatch"
287 # it is referred only from "lfutil.updatestandinsbymatch"
285 def _subdirlfs(self, files, lfiles):
288 def _subdirlfs(self, files, lfiles):
286 '''
289 '''
287 Adjust matched file list
290 Adjust matched file list
288 If we pass a directory to commit whose only committable files
291 If we pass a directory to commit whose only committable files
289 are largefiles, the core commit code aborts before finding
292 are largefiles, the core commit code aborts before finding
290 the largefiles.
293 the largefiles.
291 So we do the following:
294 So we do the following:
292 For directories that only have largefiles as matches,
295 For directories that only have largefiles as matches,
293 we explicitly add the largefiles to the match list and remove
296 we explicitly add the largefiles to the match list and remove
294 the directory.
297 the directory.
295 In other cases, we leave the match list unmodified.
298 In other cases, we leave the match list unmodified.
296 '''
299 '''
297 actualfiles = []
300 actualfiles = []
298 dirs = []
301 dirs = []
299 regulars = []
302 regulars = []
300
303
301 for f in files:
304 for f in files:
302 if lfutil.isstandin(f + '/'):
305 if lfutil.isstandin(f + '/'):
303 raise util.Abort(
306 raise util.Abort(
304 _('file "%s" is a largefile standin') % f,
307 _('file "%s" is a largefile standin') % f,
305 hint=('commit the largefile itself instead'))
308 hint=('commit the largefile itself instead'))
306 # Scan directories
309 # Scan directories
307 if os.path.isdir(self.wjoin(f)):
310 if os.path.isdir(self.wjoin(f)):
308 dirs.append(f)
311 dirs.append(f)
309 else:
312 else:
310 regulars.append(f)
313 regulars.append(f)
311
314
312 for f in dirs:
315 for f in dirs:
313 matcheddir = False
316 matcheddir = False
314 d = self.dirstate.normalize(f) + '/'
317 d = self.dirstate.normalize(f) + '/'
315 # Check for matched normal files
318 # Check for matched normal files
316 for mf in regulars:
319 for mf in regulars:
317 if self.dirstate.normalize(mf).startswith(d):
320 if self.dirstate.normalize(mf).startswith(d):
318 actualfiles.append(f)
321 actualfiles.append(f)
319 matcheddir = True
322 matcheddir = True
320 break
323 break
321 if not matcheddir:
324 if not matcheddir:
322 # If no normal match, manually append
325 # If no normal match, manually append
323 # any matching largefiles
326 # any matching largefiles
324 for lf in lfiles:
327 for lf in lfiles:
325 if self.dirstate.normalize(lf).startswith(d):
328 if self.dirstate.normalize(lf).startswith(d):
326 actualfiles.append(lf)
329 actualfiles.append(lf)
327 if not matcheddir:
330 if not matcheddir:
328 # There may still be normal files in the dir, so
331 # There may still be normal files in the dir, so
329 # make sure a directory is in the list, which
332 # make sure a directory is in the list, which
330 # forces status to walk and call the match
333 # forces status to walk and call the match
331 # function on the matcher. Windows does NOT
334 # function on the matcher. Windows does NOT
332 # require this.
335 # require this.
333 actualfiles.append('.')
336 actualfiles.append('.')
334 matcheddir = True
337 matcheddir = True
335 # Nothing in dir, so readd it
338 # Nothing in dir, so readd it
336 # and let commit reject it
339 # and let commit reject it
337 if not matcheddir:
340 if not matcheddir:
338 actualfiles.append(f)
341 actualfiles.append(f)
339
342
340 # Always add normal files
343 # Always add normal files
341 actualfiles += regulars
344 actualfiles += regulars
342 return actualfiles
345 return actualfiles
343
346
344 repo.__class__ = lfilesrepo
347 repo.__class__ = lfilesrepo
345
348
346 # stack of hooks being executed before committing.
349 # stack of hooks being executed before committing.
347 # only last element ("_lfcommithooks[-1]") is used for each committing.
350 # only last element ("_lfcommithooks[-1]") is used for each committing.
348 repo._lfcommithooks = [lfutil.updatestandinsbymatch]
351 repo._lfcommithooks = [lfutil.updatestandinsbymatch]
349
352
350 # Stack of status writer functions taking "*msg, **opts" arguments
353 # Stack of status writer functions taking "*msg, **opts" arguments
351 # like "ui.status()". Only last element ("_lfstatuswriters[-1]")
354 # like "ui.status()". Only last element ("_lfstatuswriters[-1]")
352 # is used to write status out.
355 # is used to write status out.
353 repo._lfstatuswriters = [ui.status]
356 repo._lfstatuswriters = [ui.status]
354
357
355 def prepushoutgoinghook(local, remote, outgoing):
358 def prepushoutgoinghook(local, remote, outgoing):
356 if outgoing.missing:
359 if outgoing.missing:
357 toupload = set()
360 toupload = set()
358 addfunc = lambda fn, lfhash: toupload.add(lfhash)
361 addfunc = lambda fn, lfhash: toupload.add(lfhash)
359 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
362 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
360 lfcommands.uploadlfiles(ui, local, remote, toupload)
363 lfcommands.uploadlfiles(ui, local, remote, toupload)
361 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
364 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
362
365
363 def checkrequireslfiles(ui, repo, **kwargs):
366 def checkrequireslfiles(ui, repo, **kwargs):
364 if 'largefiles' not in repo.requirements and util.any(
367 if 'largefiles' not in repo.requirements and util.any(
365 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
368 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
366 repo.requirements.add('largefiles')
369 repo.requirements.add('largefiles')
367 repo._writerequirements()
370 repo._writerequirements()
368
371
369 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
372 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
370 'largefiles')
373 'largefiles')
371 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
374 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
@@ -1,942 +1,1013 b''
1 This file contains testcases that tend to be related to special cases or less
1 This file contains testcases that tend to be related to special cases or less
2 common commands affecting largefile.
2 common commands affecting largefile.
3
3
4 Each sections should be independent of each others.
4 Each sections should be independent of each others.
5
5
6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 $ mkdir "${USERCACHE}"
7 $ mkdir "${USERCACHE}"
8 $ cat >> $HGRCPATH <<EOF
8 $ cat >> $HGRCPATH <<EOF
9 > [extensions]
9 > [extensions]
10 > largefiles=
10 > largefiles=
11 > purge=
11 > purge=
12 > rebase=
12 > rebase=
13 > transplant=
13 > transplant=
14 > [phases]
14 > [phases]
15 > publish=False
15 > publish=False
16 > [largefiles]
16 > [largefiles]
17 > minsize=2
17 > minsize=2
18 > patterns=glob:**.dat
18 > patterns=glob:**.dat
19 > usercache=${USERCACHE}
19 > usercache=${USERCACHE}
20 > [hooks]
20 > [hooks]
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 > EOF
22 > EOF
23
23
24
24
25
25
26 Test copies and moves from a directory other than root (issue3516)
26 Test copies and moves from a directory other than root (issue3516)
27 =========================================================================
27 =========================================================================
28
28
29 $ hg init lf_cpmv
29 $ hg init lf_cpmv
30 $ cd lf_cpmv
30 $ cd lf_cpmv
31 $ mkdir dira
31 $ mkdir dira
32 $ mkdir dira/dirb
32 $ mkdir dira/dirb
33 $ touch dira/dirb/largefile
33 $ touch dira/dirb/largefile
34 $ hg add --large dira/dirb/largefile
34 $ hg add --large dira/dirb/largefile
35 $ hg commit -m "added"
35 $ hg commit -m "added"
36 Invoking status precommit hook
36 Invoking status precommit hook
37 A dira/dirb/largefile
37 A dira/dirb/largefile
38 $ cd dira
38 $ cd dira
39 $ hg cp dirb/largefile foo/largefile
39 $ hg cp dirb/largefile foo/largefile
40 $ hg ci -m "deep copy"
40 $ hg ci -m "deep copy"
41 Invoking status precommit hook
41 Invoking status precommit hook
42 A dira/foo/largefile
42 A dira/foo/largefile
43 $ find . | sort
43 $ find . | sort
44 .
44 .
45 ./dirb
45 ./dirb
46 ./dirb/largefile
46 ./dirb/largefile
47 ./foo
47 ./foo
48 ./foo/largefile
48 ./foo/largefile
49 $ hg mv foo/largefile baz/largefile
49 $ hg mv foo/largefile baz/largefile
50 $ hg ci -m "moved"
50 $ hg ci -m "moved"
51 Invoking status precommit hook
51 Invoking status precommit hook
52 A dira/baz/largefile
52 A dira/baz/largefile
53 R dira/foo/largefile
53 R dira/foo/largefile
54 $ find . | sort
54 $ find . | sort
55 .
55 .
56 ./baz
56 ./baz
57 ./baz/largefile
57 ./baz/largefile
58 ./dirb
58 ./dirb
59 ./dirb/largefile
59 ./dirb/largefile
60 $ cd ..
60 $ cd ..
61 $ hg mv dira dirc
61 $ hg mv dira dirc
62 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
62 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
63 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
63 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
64 $ find * | sort
64 $ find * | sort
65 dirc
65 dirc
66 dirc/baz
66 dirc/baz
67 dirc/baz/largefile
67 dirc/baz/largefile
68 dirc/dirb
68 dirc/dirb
69 dirc/dirb/largefile
69 dirc/dirb/largefile
70
70
71 $ hg clone -q . ../fetch
71 $ hg clone -q . ../fetch
72 $ hg --config extensions.fetch= fetch ../fetch
72 $ hg --config extensions.fetch= fetch ../fetch
73 abort: uncommitted changes
73 abort: uncommitted changes
74 [255]
74 [255]
75 $ hg up -qC
75 $ hg up -qC
76 $ cd ..
76 $ cd ..
77
77
78 Clone a local repository owned by another user
78 Clone a local repository owned by another user
79 ===================================================
79 ===================================================
80
80
81 #if unix-permissions
81 #if unix-permissions
82
82
83 We have to simulate that here by setting $HOME and removing write permissions
83 We have to simulate that here by setting $HOME and removing write permissions
84 $ ORIGHOME="$HOME"
84 $ ORIGHOME="$HOME"
85 $ mkdir alice
85 $ mkdir alice
86 $ HOME="`pwd`/alice"
86 $ HOME="`pwd`/alice"
87 $ cd alice
87 $ cd alice
88 $ hg init pubrepo
88 $ hg init pubrepo
89 $ cd pubrepo
89 $ cd pubrepo
90 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
90 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
91 $ hg add --large a-large-file
91 $ hg add --large a-large-file
92 $ hg commit -m "Add a large file"
92 $ hg commit -m "Add a large file"
93 Invoking status precommit hook
93 Invoking status precommit hook
94 A a-large-file
94 A a-large-file
95 $ cd ..
95 $ cd ..
96 $ chmod -R a-w pubrepo
96 $ chmod -R a-w pubrepo
97 $ cd ..
97 $ cd ..
98 $ mkdir bob
98 $ mkdir bob
99 $ HOME="`pwd`/bob"
99 $ HOME="`pwd`/bob"
100 $ cd bob
100 $ cd bob
101 $ hg clone --pull ../alice/pubrepo pubrepo
101 $ hg clone --pull ../alice/pubrepo pubrepo
102 requesting all changes
102 requesting all changes
103 adding changesets
103 adding changesets
104 adding manifests
104 adding manifests
105 adding file changes
105 adding file changes
106 added 1 changesets with 1 changes to 1 files
106 added 1 changesets with 1 changes to 1 files
107 updating to branch default
107 updating to branch default
108 getting changed largefiles
108 getting changed largefiles
109 1 largefiles updated, 0 removed
109 1 largefiles updated, 0 removed
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ cd ..
111 $ cd ..
112 $ chmod -R u+w alice/pubrepo
112 $ chmod -R u+w alice/pubrepo
113 $ HOME="$ORIGHOME"
113 $ HOME="$ORIGHOME"
114
114
115 #endif
115 #endif
116
116
117
117
118 Symlink to a large largefile should behave the same as a symlink to a normal file
118 Symlink to a large largefile should behave the same as a symlink to a normal file
119 =====================================================================================
119 =====================================================================================
120
120
121 #if symlink
121 #if symlink
122
122
123 $ hg init largesymlink
123 $ hg init largesymlink
124 $ cd largesymlink
124 $ cd largesymlink
125 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
125 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
126 $ hg add --large largefile
126 $ hg add --large largefile
127 $ hg commit -m "commit a large file"
127 $ hg commit -m "commit a large file"
128 Invoking status precommit hook
128 Invoking status precommit hook
129 A largefile
129 A largefile
130 $ ln -s largefile largelink
130 $ ln -s largefile largelink
131 $ hg add largelink
131 $ hg add largelink
132 $ hg commit -m "commit a large symlink"
132 $ hg commit -m "commit a large symlink"
133 Invoking status precommit hook
133 Invoking status precommit hook
134 A largelink
134 A largelink
135 $ rm -f largelink
135 $ rm -f largelink
136 $ hg up >/dev/null
136 $ hg up >/dev/null
137 $ test -f largelink
137 $ test -f largelink
138 [1]
138 [1]
139 $ test -L largelink
139 $ test -L largelink
140 [1]
140 [1]
141 $ rm -f largelink # make next part of the test independent of the previous
141 $ rm -f largelink # make next part of the test independent of the previous
142 $ hg up -C >/dev/null
142 $ hg up -C >/dev/null
143 $ test -f largelink
143 $ test -f largelink
144 $ test -L largelink
144 $ test -L largelink
145 $ cd ..
145 $ cd ..
146
146
147 #endif
147 #endif
148
148
149
149
150 test for pattern matching on 'hg status':
150 test for pattern matching on 'hg status':
151 ==============================================
151 ==============================================
152
152
153
153
154 to boost performance, largefiles checks whether specified patterns are
154 to boost performance, largefiles checks whether specified patterns are
155 related to largefiles in working directory (NOT to STANDIN) or not.
155 related to largefiles in working directory (NOT to STANDIN) or not.
156
156
157 $ hg init statusmatch
157 $ hg init statusmatch
158 $ cd statusmatch
158 $ cd statusmatch
159
159
160 $ mkdir -p a/b/c/d
160 $ mkdir -p a/b/c/d
161 $ echo normal > a/b/c/d/e.normal.txt
161 $ echo normal > a/b/c/d/e.normal.txt
162 $ hg add a/b/c/d/e.normal.txt
162 $ hg add a/b/c/d/e.normal.txt
163 $ echo large > a/b/c/d/e.large.txt
163 $ echo large > a/b/c/d/e.large.txt
164 $ hg add --large a/b/c/d/e.large.txt
164 $ hg add --large a/b/c/d/e.large.txt
165 $ mkdir -p a/b/c/x
165 $ mkdir -p a/b/c/x
166 $ echo normal > a/b/c/x/y.normal.txt
166 $ echo normal > a/b/c/x/y.normal.txt
167 $ hg add a/b/c/x/y.normal.txt
167 $ hg add a/b/c/x/y.normal.txt
168 $ hg commit -m 'add files'
168 $ hg commit -m 'add files'
169 Invoking status precommit hook
169 Invoking status precommit hook
170 A a/b/c/d/e.large.txt
170 A a/b/c/d/e.large.txt
171 A a/b/c/d/e.normal.txt
171 A a/b/c/d/e.normal.txt
172 A a/b/c/x/y.normal.txt
172 A a/b/c/x/y.normal.txt
173
173
174 (1) no pattern: no performance boost
174 (1) no pattern: no performance boost
175 $ hg status -A
175 $ hg status -A
176 C a/b/c/d/e.large.txt
176 C a/b/c/d/e.large.txt
177 C a/b/c/d/e.normal.txt
177 C a/b/c/d/e.normal.txt
178 C a/b/c/x/y.normal.txt
178 C a/b/c/x/y.normal.txt
179
179
180 (2) pattern not related to largefiles: performance boost
180 (2) pattern not related to largefiles: performance boost
181 $ hg status -A a/b/c/x
181 $ hg status -A a/b/c/x
182 C a/b/c/x/y.normal.txt
182 C a/b/c/x/y.normal.txt
183
183
184 (3) pattern related to largefiles: no performance boost
184 (3) pattern related to largefiles: no performance boost
185 $ hg status -A a/b/c/d
185 $ hg status -A a/b/c/d
186 C a/b/c/d/e.large.txt
186 C a/b/c/d/e.large.txt
187 C a/b/c/d/e.normal.txt
187 C a/b/c/d/e.normal.txt
188
188
189 (4) pattern related to STANDIN (not to largefiles): performance boost
189 (4) pattern related to STANDIN (not to largefiles): performance boost
190 $ hg status -A .hglf/a
190 $ hg status -A .hglf/a
191 C .hglf/a/b/c/d/e.large.txt
191 C .hglf/a/b/c/d/e.large.txt
192
192
193 (5) mixed case: no performance boost
193 (5) mixed case: no performance boost
194 $ hg status -A a/b/c/x a/b/c/d
194 $ hg status -A a/b/c/x a/b/c/d
195 C a/b/c/d/e.large.txt
195 C a/b/c/d/e.large.txt
196 C a/b/c/d/e.normal.txt
196 C a/b/c/d/e.normal.txt
197 C a/b/c/x/y.normal.txt
197 C a/b/c/x/y.normal.txt
198
198
199 verify that largefiles doesn't break filesets
199 verify that largefiles doesn't break filesets
200
200
201 $ hg log --rev . --exclude "set:binary()"
201 $ hg log --rev . --exclude "set:binary()"
202 changeset: 0:41bd42f10efa
202 changeset: 0:41bd42f10efa
203 tag: tip
203 tag: tip
204 user: test
204 user: test
205 date: Thu Jan 01 00:00:00 1970 +0000
205 date: Thu Jan 01 00:00:00 1970 +0000
206 summary: add files
206 summary: add files
207
207
208 verify that large files in subrepos handled properly
208 verify that large files in subrepos handled properly
209 $ hg init subrepo
209 $ hg init subrepo
210 $ echo "subrepo = subrepo" > .hgsub
210 $ echo "subrepo = subrepo" > .hgsub
211 $ hg add .hgsub
211 $ hg add .hgsub
212 $ hg ci -m "add subrepo"
212 $ hg ci -m "add subrepo"
213 Invoking status precommit hook
213 Invoking status precommit hook
214 A .hgsub
214 A .hgsub
215 ? .hgsubstate
215 ? .hgsubstate
216 $ echo "rev 1" > subrepo/large.txt
216 $ echo "rev 1" > subrepo/large.txt
217 $ hg add --large subrepo/large.txt
217 $ hg add --large subrepo/large.txt
218 $ hg sum
218 $ hg sum
219 parent: 1:8ee150ea2e9c tip
219 parent: 1:8ee150ea2e9c tip
220 add subrepo
220 add subrepo
221 branch: default
221 branch: default
222 commit: 1 subrepos
222 commit: 1 subrepos
223 update: (current)
223 update: (current)
224 $ hg st
224 $ hg st
225 $ hg st -S
225 $ hg st -S
226 A subrepo/large.txt
226 A subrepo/large.txt
227 $ hg ci -S -m "commit top repo"
227 $ hg ci -S -m "commit top repo"
228 committing subrepository subrepo
228 committing subrepository subrepo
229 Invoking status precommit hook
229 Invoking status precommit hook
230 A large.txt
230 A large.txt
231 Invoking status precommit hook
231 Invoking status precommit hook
232 M .hgsubstate
232 M .hgsubstate
233 # No differences
233 # No differences
234 $ hg st -S
234 $ hg st -S
235 $ hg sum
235 $ hg sum
236 parent: 2:ce4cd0c527a6 tip
236 parent: 2:ce4cd0c527a6 tip
237 commit top repo
237 commit top repo
238 branch: default
238 branch: default
239 commit: (clean)
239 commit: (clean)
240 update: (current)
240 update: (current)
241 $ echo "rev 2" > subrepo/large.txt
241 $ echo "rev 2" > subrepo/large.txt
242 $ hg st -S
242 $ hg st -S
243 M subrepo/large.txt
243 M subrepo/large.txt
244 $ hg sum
244 $ hg sum
245 parent: 2:ce4cd0c527a6 tip
245 parent: 2:ce4cd0c527a6 tip
246 commit top repo
246 commit top repo
247 branch: default
247 branch: default
248 commit: 1 subrepos
248 commit: 1 subrepos
249 update: (current)
249 update: (current)
250 $ hg ci -m "this commit should fail without -S"
250 $ hg ci -m "this commit should fail without -S"
251 abort: uncommitted changes in subrepo subrepo
251 abort: uncommitted changes in subrepo subrepo
252 (use --subrepos for recursive commit)
252 (use --subrepos for recursive commit)
253 [255]
253 [255]
254
254
255 Add a normal file to the subrepo, then test archiving
255 Add a normal file to the subrepo, then test archiving
256
256
257 $ echo 'normal file' > subrepo/normal.txt
257 $ echo 'normal file' > subrepo/normal.txt
258 $ touch large.dat
258 $ touch large.dat
259 $ mv subrepo/large.txt subrepo/renamed-large.txt
259 $ mv subrepo/large.txt subrepo/renamed-large.txt
260 $ hg addremove -S --dry-run
260 $ hg addremove -S --dry-run
261 adding large.dat as a largefile
261 adding large.dat as a largefile
262 removing subrepo/large.txt
262 removing subrepo/large.txt
263 adding subrepo/normal.txt
263 adding subrepo/normal.txt
264 adding subrepo/renamed-large.txt
264 adding subrepo/renamed-large.txt
265 $ hg status -S
265 $ hg status -S
266 ! subrepo/large.txt
266 ! subrepo/large.txt
267 ? large.dat
267 ? large.dat
268 ? subrepo/normal.txt
268 ? subrepo/normal.txt
269 ? subrepo/renamed-large.txt
269 ? subrepo/renamed-large.txt
270
270
271 $ hg addremove --dry-run subrepo
271 $ hg addremove --dry-run subrepo
272 removing subrepo/large.txt (glob)
272 removing subrepo/large.txt (glob)
273 adding subrepo/normal.txt (glob)
273 adding subrepo/normal.txt (glob)
274 adding subrepo/renamed-large.txt (glob)
274 adding subrepo/renamed-large.txt (glob)
275 $ hg status -S
275 $ hg status -S
276 ! subrepo/large.txt
276 ! subrepo/large.txt
277 ? large.dat
277 ? large.dat
278 ? subrepo/normal.txt
278 ? subrepo/normal.txt
279 ? subrepo/renamed-large.txt
279 ? subrepo/renamed-large.txt
280 $ cd ..
280 $ cd ..
281
281
282 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
282 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
283 removing statusmatch/subrepo/large.txt (glob)
283 removing statusmatch/subrepo/large.txt (glob)
284 adding statusmatch/subrepo/normal.txt (glob)
284 adding statusmatch/subrepo/normal.txt (glob)
285 adding statusmatch/subrepo/renamed-large.txt (glob)
285 adding statusmatch/subrepo/renamed-large.txt (glob)
286 $ hg -R statusmatch status -S
286 $ hg -R statusmatch status -S
287 ! subrepo/large.txt
287 ! subrepo/large.txt
288 ? large.dat
288 ? large.dat
289 ? subrepo/normal.txt
289 ? subrepo/normal.txt
290 ? subrepo/renamed-large.txt
290 ? subrepo/renamed-large.txt
291
291
292 $ hg -R statusmatch addremove --dry-run -S
292 $ hg -R statusmatch addremove --dry-run -S
293 adding large.dat as a largefile
293 adding large.dat as a largefile
294 removing subrepo/large.txt
294 removing subrepo/large.txt
295 adding subrepo/normal.txt
295 adding subrepo/normal.txt
296 adding subrepo/renamed-large.txt
296 adding subrepo/renamed-large.txt
297 $ cd statusmatch
297 $ cd statusmatch
298
298
299 $ mv subrepo/renamed-large.txt subrepo/large.txt
299 $ mv subrepo/renamed-large.txt subrepo/large.txt
300 $ hg addremove subrepo
300 $ hg addremove subrepo
301 adding subrepo/normal.txt (glob)
301 adding subrepo/normal.txt (glob)
302 $ hg forget subrepo/normal.txt
302 $ hg forget subrepo/normal.txt
303
303
304 $ hg addremove -S
304 $ hg addremove -S
305 adding large.dat as a largefile
305 adding large.dat as a largefile
306 adding subrepo/normal.txt
306 adding subrepo/normal.txt
307 $ rm large.dat
307 $ rm large.dat
308
308
309 $ hg addremove subrepo
309 $ hg addremove subrepo
310 $ hg addremove -S
310 $ hg addremove -S
311 removing large.dat
311 removing large.dat
312
312
313 Lock in subrepo, otherwise the change isn't archived
313 Lock in subrepo, otherwise the change isn't archived
314
314
315 $ hg ci -S -m "add normal file to top level"
315 $ hg ci -S -m "add normal file to top level"
316 committing subrepository subrepo
316 committing subrepository subrepo
317 Invoking status precommit hook
317 Invoking status precommit hook
318 M large.txt
318 M large.txt
319 A normal.txt
319 A normal.txt
320 Invoking status precommit hook
320 Invoking status precommit hook
321 M .hgsubstate
321 M .hgsubstate
322 $ hg archive -S ../lf_subrepo_archive
322 $ hg archive -S ../lf_subrepo_archive
323 $ find ../lf_subrepo_archive | sort
323 $ find ../lf_subrepo_archive | sort
324 ../lf_subrepo_archive
324 ../lf_subrepo_archive
325 ../lf_subrepo_archive/.hg_archival.txt
325 ../lf_subrepo_archive/.hg_archival.txt
326 ../lf_subrepo_archive/.hgsub
326 ../lf_subrepo_archive/.hgsub
327 ../lf_subrepo_archive/.hgsubstate
327 ../lf_subrepo_archive/.hgsubstate
328 ../lf_subrepo_archive/a
328 ../lf_subrepo_archive/a
329 ../lf_subrepo_archive/a/b
329 ../lf_subrepo_archive/a/b
330 ../lf_subrepo_archive/a/b/c
330 ../lf_subrepo_archive/a/b/c
331 ../lf_subrepo_archive/a/b/c/d
331 ../lf_subrepo_archive/a/b/c/d
332 ../lf_subrepo_archive/a/b/c/d/e.large.txt
332 ../lf_subrepo_archive/a/b/c/d/e.large.txt
333 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
333 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
334 ../lf_subrepo_archive/a/b/c/x
334 ../lf_subrepo_archive/a/b/c/x
335 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
335 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
336 ../lf_subrepo_archive/subrepo
336 ../lf_subrepo_archive/subrepo
337 ../lf_subrepo_archive/subrepo/large.txt
337 ../lf_subrepo_archive/subrepo/large.txt
338 ../lf_subrepo_archive/subrepo/normal.txt
338 ../lf_subrepo_archive/subrepo/normal.txt
339
339
340 Test update with subrepos.
340 Test update with subrepos.
341
341
342 $ hg update 0
342 $ hg update 0
343 getting changed largefiles
343 getting changed largefiles
344 0 largefiles updated, 1 removed
344 0 largefiles updated, 1 removed
345 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
345 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
346 $ hg status -S
346 $ hg status -S
347 $ hg update tip
347 $ hg update tip
348 getting changed largefiles
348 getting changed largefiles
349 1 largefiles updated, 0 removed
349 1 largefiles updated, 0 removed
350 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
350 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 $ hg status -S
351 $ hg status -S
352 # modify a large file
352 # modify a large file
353 $ echo "modified" > subrepo/large.txt
353 $ echo "modified" > subrepo/large.txt
354 $ hg st -S
354 $ hg st -S
355 M subrepo/large.txt
355 M subrepo/large.txt
356 # update -C should revert the change.
356 # update -C should revert the change.
357 $ hg update -C
357 $ hg update -C
358 getting changed largefiles
358 getting changed largefiles
359 1 largefiles updated, 0 removed
359 1 largefiles updated, 0 removed
360 getting changed largefiles
360 getting changed largefiles
361 0 largefiles updated, 0 removed
361 0 largefiles updated, 0 removed
362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 $ hg status -S
363 $ hg status -S
364
364
365 $ hg rm -v subrepo/large.txt
365 $ hg rm -v subrepo/large.txt
366 removing subrepo/large.txt (glob)
366 removing subrepo/large.txt (glob)
367 $ hg revert -R subrepo subrepo/large.txt
367 $ hg revert -R subrepo subrepo/large.txt
368 $ rm subrepo/large.txt
368 $ rm subrepo/large.txt
369 $ hg addremove -S
369 $ hg addremove -S
370 removing subrepo/large.txt
370 removing subrepo/large.txt
371 $ hg st -S
371 $ hg st -S
372 R subrepo/large.txt
372 R subrepo/large.txt
373
373
374 Test archiving a revision that references a subrepo that is not yet
374 Test archiving a revision that references a subrepo that is not yet
375 cloned (see test-subrepo-recursion.t):
375 cloned (see test-subrepo-recursion.t):
376
376
377 $ hg clone -U . ../empty
377 $ hg clone -U . ../empty
378 $ cd ../empty
378 $ cd ../empty
379 $ hg archive --subrepos -r tip ../archive.tar.gz
379 $ hg archive --subrepos -r tip ../archive.tar.gz
380 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
380 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
381 $ cd ..
381 $ cd ..
382
382
383
383
384
384
385
385
386
386
387
387
388 Test addremove, forget and others
388 Test addremove, forget and others
389 ==============================================
389 ==============================================
390
390
391 Test that addremove picks up largefiles prior to the initial commit (issue3541)
391 Test that addremove picks up largefiles prior to the initial commit (issue3541)
392
392
393 $ hg init addrm2
393 $ hg init addrm2
394 $ cd addrm2
394 $ cd addrm2
395 $ touch large.dat
395 $ touch large.dat
396 $ touch large2.dat
396 $ touch large2.dat
397 $ touch normal
397 $ touch normal
398 $ hg add --large large.dat
398 $ hg add --large large.dat
399 $ hg addremove -v
399 $ hg addremove -v
400 adding large2.dat as a largefile
400 adding large2.dat as a largefile
401 adding normal
401 adding normal
402
402
403 Test that forgetting all largefiles reverts to islfilesrepo() == False
403 Test that forgetting all largefiles reverts to islfilesrepo() == False
404 (addremove will add *.dat as normal files now)
404 (addremove will add *.dat as normal files now)
405 $ hg forget large.dat
405 $ hg forget large.dat
406 $ hg forget large2.dat
406 $ hg forget large2.dat
407 $ hg addremove -v
407 $ hg addremove -v
408 adding large.dat
408 adding large.dat
409 adding large2.dat
409 adding large2.dat
410
410
411 Test commit's addremove option prior to the first commit
411 Test commit's addremove option prior to the first commit
412 $ hg forget large.dat
412 $ hg forget large.dat
413 $ hg forget large2.dat
413 $ hg forget large2.dat
414 $ hg add --large large.dat
414 $ hg add --large large.dat
415 $ hg ci -Am "commit"
415 $ hg ci -Am "commit"
416 adding large2.dat as a largefile
416 adding large2.dat as a largefile
417 Invoking status precommit hook
417 Invoking status precommit hook
418 A large.dat
418 A large.dat
419 A large2.dat
419 A large2.dat
420 A normal
420 A normal
421 $ find .hglf | sort
421 $ find .hglf | sort
422 .hglf
422 .hglf
423 .hglf/large.dat
423 .hglf/large.dat
424 .hglf/large2.dat
424 .hglf/large2.dat
425
425
426 Test actions on largefiles using relative paths from subdir
426 Test actions on largefiles using relative paths from subdir
427
427
428 $ mkdir sub
428 $ mkdir sub
429 $ cd sub
429 $ cd sub
430 $ echo anotherlarge > anotherlarge
430 $ echo anotherlarge > anotherlarge
431 $ hg add --large anotherlarge
431 $ hg add --large anotherlarge
432 $ hg st
432 $ hg st
433 A sub/anotherlarge
433 A sub/anotherlarge
434 $ hg st anotherlarge
434 $ hg st anotherlarge
435 A anotherlarge
435 A anotherlarge
436 $ hg commit -m anotherlarge anotherlarge
436 $ hg commit -m anotherlarge anotherlarge
437 Invoking status precommit hook
437 Invoking status precommit hook
438 A sub/anotherlarge
438 A sub/anotherlarge
439 $ hg log anotherlarge
439 $ hg log anotherlarge
440 changeset: 1:9627a577c5e9
440 changeset: 1:9627a577c5e9
441 tag: tip
441 tag: tip
442 user: test
442 user: test
443 date: Thu Jan 01 00:00:00 1970 +0000
443 date: Thu Jan 01 00:00:00 1970 +0000
444 summary: anotherlarge
444 summary: anotherlarge
445
445
446 $ hg log -G anotherlarge
446 $ hg log -G anotherlarge
447 @ changeset: 1:9627a577c5e9
447 @ changeset: 1:9627a577c5e9
448 | tag: tip
448 | tag: tip
449 | user: test
449 | user: test
450 | date: Thu Jan 01 00:00:00 1970 +0000
450 | date: Thu Jan 01 00:00:00 1970 +0000
451 | summary: anotherlarge
451 | summary: anotherlarge
452 |
452 |
453 $ echo more >> anotherlarge
453 $ echo more >> anotherlarge
454 $ hg st .
454 $ hg st .
455 M anotherlarge
455 M anotherlarge
456 $ hg cat anotherlarge
456 $ hg cat anotherlarge
457 anotherlarge
457 anotherlarge
458 $ hg revert anotherlarge
458 $ hg revert anotherlarge
459 $ hg st
459 $ hg st
460 ? sub/anotherlarge.orig
460 ? sub/anotherlarge.orig
461 $ cd ..
461 $ cd ..
462
462
463 $ cd ..
463 $ cd ..
464
464
465 Check error message while exchange
465 Check error message while exchange
466 =========================================================
466 =========================================================
467
467
468 issue3651: summary/outgoing with largefiles shows "no remote repo"
468 issue3651: summary/outgoing with largefiles shows "no remote repo"
469 unexpectedly
469 unexpectedly
470
470
471 $ mkdir issue3651
471 $ mkdir issue3651
472 $ cd issue3651
472 $ cd issue3651
473
473
474 $ hg init src
474 $ hg init src
475 $ echo a > src/a
475 $ echo a > src/a
476 $ hg -R src add --large src/a
476 $ hg -R src add --large src/a
477 $ hg -R src commit -m '#0'
477 $ hg -R src commit -m '#0'
478 Invoking status precommit hook
478 Invoking status precommit hook
479 A a
479 A a
480
480
481 check messages when no remote repository is specified:
481 check messages when no remote repository is specified:
482 "no remote repo" route for "hg outgoing --large" is not tested here,
482 "no remote repo" route for "hg outgoing --large" is not tested here,
483 because it can't be reproduced easily.
483 because it can't be reproduced easily.
484
484
485 $ hg init clone1
485 $ hg init clone1
486 $ hg -R clone1 -q pull src
486 $ hg -R clone1 -q pull src
487 $ hg -R clone1 -q update
487 $ hg -R clone1 -q update
488 $ hg -R clone1 paths | grep default
488 $ hg -R clone1 paths | grep default
489 [1]
489 [1]
490
490
491 $ hg -R clone1 summary --large
491 $ hg -R clone1 summary --large
492 parent: 0:fc0bd45326d3 tip
492 parent: 0:fc0bd45326d3 tip
493 #0
493 #0
494 branch: default
494 branch: default
495 commit: (clean)
495 commit: (clean)
496 update: (current)
496 update: (current)
497 largefiles: (no remote repo)
497 largefiles: (no remote repo)
498
498
499 check messages when there is no files to upload:
499 check messages when there is no files to upload:
500
500
501 $ hg -q clone src clone2
501 $ hg -q clone src clone2
502 $ hg -R clone2 paths | grep default
502 $ hg -R clone2 paths | grep default
503 default = $TESTTMP/issue3651/src (glob)
503 default = $TESTTMP/issue3651/src (glob)
504
504
505 $ hg -R clone2 summary --large
505 $ hg -R clone2 summary --large
506 parent: 0:fc0bd45326d3 tip
506 parent: 0:fc0bd45326d3 tip
507 #0
507 #0
508 branch: default
508 branch: default
509 commit: (clean)
509 commit: (clean)
510 update: (current)
510 update: (current)
511 largefiles: (no files to upload)
511 largefiles: (no files to upload)
512 $ hg -R clone2 outgoing --large
512 $ hg -R clone2 outgoing --large
513 comparing with $TESTTMP/issue3651/src (glob)
513 comparing with $TESTTMP/issue3651/src (glob)
514 searching for changes
514 searching for changes
515 no changes found
515 no changes found
516 largefiles: no files to upload
516 largefiles: no files to upload
517 [1]
517 [1]
518
518
519 $ hg -R clone2 outgoing --large --graph --template "{rev}"
519 $ hg -R clone2 outgoing --large --graph --template "{rev}"
520 comparing with $TESTTMP/issue3651/src (glob)
520 comparing with $TESTTMP/issue3651/src (glob)
521 searching for changes
521 searching for changes
522 no changes found
522 no changes found
523 largefiles: no files to upload
523 largefiles: no files to upload
524
524
525 check messages when there are files to upload:
525 check messages when there are files to upload:
526
526
527 $ echo b > clone2/b
527 $ echo b > clone2/b
528 $ hg -R clone2 add --large clone2/b
528 $ hg -R clone2 add --large clone2/b
529 $ hg -R clone2 commit -m '#1'
529 $ hg -R clone2 commit -m '#1'
530 Invoking status precommit hook
530 Invoking status precommit hook
531 A b
531 A b
532 $ hg -R clone2 summary --large
532 $ hg -R clone2 summary --large
533 parent: 1:1acbe71ce432 tip
533 parent: 1:1acbe71ce432 tip
534 #1
534 #1
535 branch: default
535 branch: default
536 commit: (clean)
536 commit: (clean)
537 update: (current)
537 update: (current)
538 largefiles: 1 entities for 1 files to upload
538 largefiles: 1 entities for 1 files to upload
539 $ hg -R clone2 outgoing --large
539 $ hg -R clone2 outgoing --large
540 comparing with $TESTTMP/issue3651/src (glob)
540 comparing with $TESTTMP/issue3651/src (glob)
541 searching for changes
541 searching for changes
542 changeset: 1:1acbe71ce432
542 changeset: 1:1acbe71ce432
543 tag: tip
543 tag: tip
544 user: test
544 user: test
545 date: Thu Jan 01 00:00:00 1970 +0000
545 date: Thu Jan 01 00:00:00 1970 +0000
546 summary: #1
546 summary: #1
547
547
548 largefiles to upload (1 entities):
548 largefiles to upload (1 entities):
549 b
549 b
550
550
551 $ hg -R clone2 outgoing --large --graph --template "{rev}"
551 $ hg -R clone2 outgoing --large --graph --template "{rev}"
552 comparing with $TESTTMP/issue3651/src (glob)
552 comparing with $TESTTMP/issue3651/src (glob)
553 searching for changes
553 searching for changes
554 @ 1
554 @ 1
555
555
556 largefiles to upload (1 entities):
556 largefiles to upload (1 entities):
557 b
557 b
558
558
559
559
560 $ cp clone2/b clone2/b1
560 $ cp clone2/b clone2/b1
561 $ cp clone2/b clone2/b2
561 $ cp clone2/b clone2/b2
562 $ hg -R clone2 add --large clone2/b1 clone2/b2
562 $ hg -R clone2 add --large clone2/b1 clone2/b2
563 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
563 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
564 Invoking status precommit hook
564 Invoking status precommit hook
565 A b1
565 A b1
566 A b2
566 A b2
567 $ hg -R clone2 summary --large
567 $ hg -R clone2 summary --large
568 parent: 2:6095d0695d70 tip
568 parent: 2:6095d0695d70 tip
569 #2: add largefiles referring same entity
569 #2: add largefiles referring same entity
570 branch: default
570 branch: default
571 commit: (clean)
571 commit: (clean)
572 update: (current)
572 update: (current)
573 largefiles: 1 entities for 3 files to upload
573 largefiles: 1 entities for 3 files to upload
574 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
574 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
575 comparing with $TESTTMP/issue3651/src (glob)
575 comparing with $TESTTMP/issue3651/src (glob)
576 searching for changes
576 searching for changes
577 1:1acbe71ce432
577 1:1acbe71ce432
578 2:6095d0695d70
578 2:6095d0695d70
579 largefiles to upload (1 entities):
579 largefiles to upload (1 entities):
580 b
580 b
581 b1
581 b1
582 b2
582 b2
583
583
584 $ hg -R clone2 cat -r 1 clone2/.hglf/b
584 $ hg -R clone2 cat -r 1 clone2/.hglf/b
585 89e6c98d92887913cadf06b2adb97f26cde4849b
585 89e6c98d92887913cadf06b2adb97f26cde4849b
586 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
586 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
587 comparing with $TESTTMP/issue3651/src (glob)
587 comparing with $TESTTMP/issue3651/src (glob)
588 query 1; heads
588 query 1; heads
589 searching for changes
589 searching for changes
590 all remote heads known locally
590 all remote heads known locally
591 1:1acbe71ce432
591 1:1acbe71ce432
592 2:6095d0695d70
592 2:6095d0695d70
593 finding outgoing largefiles: 0/2 revision (0.00%)
593 finding outgoing largefiles: 0/2 revision (0.00%)
594 finding outgoing largefiles: 1/2 revision (50.00%)
594 finding outgoing largefiles: 1/2 revision (50.00%)
595 largefiles to upload (1 entities):
595 largefiles to upload (1 entities):
596 b
596 b
597 89e6c98d92887913cadf06b2adb97f26cde4849b
597 89e6c98d92887913cadf06b2adb97f26cde4849b
598 b1
598 b1
599 89e6c98d92887913cadf06b2adb97f26cde4849b
599 89e6c98d92887913cadf06b2adb97f26cde4849b
600 b2
600 b2
601 89e6c98d92887913cadf06b2adb97f26cde4849b
601 89e6c98d92887913cadf06b2adb97f26cde4849b
602
602
603
603
604 $ echo bbb > clone2/b
604 $ echo bbb > clone2/b
605 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
605 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
606 Invoking status precommit hook
606 Invoking status precommit hook
607 M b
607 M b
608 $ echo bbbb > clone2/b
608 $ echo bbbb > clone2/b
609 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
609 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
610 Invoking status precommit hook
610 Invoking status precommit hook
611 M b
611 M b
612 $ cp clone2/b1 clone2/b
612 $ cp clone2/b1 clone2/b
613 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
613 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
614 Invoking status precommit hook
614 Invoking status precommit hook
615 M b
615 M b
616 $ hg -R clone2 summary --large
616 $ hg -R clone2 summary --large
617 parent: 5:036794ea641c tip
617 parent: 5:036794ea641c tip
618 #5: refer existing largefile entity again
618 #5: refer existing largefile entity again
619 branch: default
619 branch: default
620 commit: (clean)
620 commit: (clean)
621 update: (current)
621 update: (current)
622 largefiles: 3 entities for 3 files to upload
622 largefiles: 3 entities for 3 files to upload
623 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
623 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
624 comparing with $TESTTMP/issue3651/src (glob)
624 comparing with $TESTTMP/issue3651/src (glob)
625 searching for changes
625 searching for changes
626 1:1acbe71ce432
626 1:1acbe71ce432
627 2:6095d0695d70
627 2:6095d0695d70
628 3:7983dce246cc
628 3:7983dce246cc
629 4:233f12ada4ae
629 4:233f12ada4ae
630 5:036794ea641c
630 5:036794ea641c
631 largefiles to upload (3 entities):
631 largefiles to upload (3 entities):
632 b
632 b
633 b1
633 b1
634 b2
634 b2
635
635
636 $ hg -R clone2 cat -r 3 clone2/.hglf/b
636 $ hg -R clone2 cat -r 3 clone2/.hglf/b
637 c801c9cfe94400963fcb683246217d5db77f9a9a
637 c801c9cfe94400963fcb683246217d5db77f9a9a
638 $ hg -R clone2 cat -r 4 clone2/.hglf/b
638 $ hg -R clone2 cat -r 4 clone2/.hglf/b
639 13f9ed0898e315bf59dc2973fec52037b6f441a2
639 13f9ed0898e315bf59dc2973fec52037b6f441a2
640 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
640 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
641 comparing with $TESTTMP/issue3651/src (glob)
641 comparing with $TESTTMP/issue3651/src (glob)
642 query 1; heads
642 query 1; heads
643 searching for changes
643 searching for changes
644 all remote heads known locally
644 all remote heads known locally
645 1:1acbe71ce432
645 1:1acbe71ce432
646 2:6095d0695d70
646 2:6095d0695d70
647 3:7983dce246cc
647 3:7983dce246cc
648 4:233f12ada4ae
648 4:233f12ada4ae
649 5:036794ea641c
649 5:036794ea641c
650 finding outgoing largefiles: 0/5 revision (0.00%)
650 finding outgoing largefiles: 0/5 revision (0.00%)
651 finding outgoing largefiles: 1/5 revision (20.00%)
651 finding outgoing largefiles: 1/5 revision (20.00%)
652 finding outgoing largefiles: 2/5 revision (40.00%)
652 finding outgoing largefiles: 2/5 revision (40.00%)
653 finding outgoing largefiles: 3/5 revision (60.00%)
653 finding outgoing largefiles: 3/5 revision (60.00%)
654 finding outgoing largefiles: 4/5 revision (80.00%)
654 finding outgoing largefiles: 4/5 revision (80.00%)
655 largefiles to upload (3 entities):
655 largefiles to upload (3 entities):
656 b
656 b
657 13f9ed0898e315bf59dc2973fec52037b6f441a2
657 13f9ed0898e315bf59dc2973fec52037b6f441a2
658 89e6c98d92887913cadf06b2adb97f26cde4849b
658 89e6c98d92887913cadf06b2adb97f26cde4849b
659 c801c9cfe94400963fcb683246217d5db77f9a9a
659 c801c9cfe94400963fcb683246217d5db77f9a9a
660 b1
660 b1
661 89e6c98d92887913cadf06b2adb97f26cde4849b
661 89e6c98d92887913cadf06b2adb97f26cde4849b
662 b2
662 b2
663 89e6c98d92887913cadf06b2adb97f26cde4849b
663 89e6c98d92887913cadf06b2adb97f26cde4849b
664
664
665
665
666 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
666 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
667 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
667 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
668
668
669 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
669 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
670 summary" and "hg outgoing", even though files in outgoing revision #2
670 summary" and "hg outgoing", even though files in outgoing revision #2
671 and #5 refer it.
671 and #5 refer it.
672
672
673 $ hg -R clone2 push -r 1 -q
673 $ hg -R clone2 push -r 1 -q
674 $ hg -R clone2 summary --large
674 $ hg -R clone2 summary --large
675 parent: 5:036794ea641c tip
675 parent: 5:036794ea641c tip
676 #5: refer existing largefile entity again
676 #5: refer existing largefile entity again
677 branch: default
677 branch: default
678 commit: (clean)
678 commit: (clean)
679 update: (current)
679 update: (current)
680 largefiles: 2 entities for 1 files to upload
680 largefiles: 2 entities for 1 files to upload
681 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
681 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
682 comparing with $TESTTMP/issue3651/src (glob)
682 comparing with $TESTTMP/issue3651/src (glob)
683 searching for changes
683 searching for changes
684 2:6095d0695d70
684 2:6095d0695d70
685 3:7983dce246cc
685 3:7983dce246cc
686 4:233f12ada4ae
686 4:233f12ada4ae
687 5:036794ea641c
687 5:036794ea641c
688 largefiles to upload (2 entities):
688 largefiles to upload (2 entities):
689 b
689 b
690
690
691 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
691 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
692 comparing with $TESTTMP/issue3651/src (glob)
692 comparing with $TESTTMP/issue3651/src (glob)
693 query 1; heads
693 query 1; heads
694 searching for changes
694 searching for changes
695 all remote heads known locally
695 all remote heads known locally
696 2:6095d0695d70
696 2:6095d0695d70
697 3:7983dce246cc
697 3:7983dce246cc
698 4:233f12ada4ae
698 4:233f12ada4ae
699 5:036794ea641c
699 5:036794ea641c
700 finding outgoing largefiles: 0/4 revision (0.00%)
700 finding outgoing largefiles: 0/4 revision (0.00%)
701 finding outgoing largefiles: 1/4 revision (25.00%)
701 finding outgoing largefiles: 1/4 revision (25.00%)
702 finding outgoing largefiles: 2/4 revision (50.00%)
702 finding outgoing largefiles: 2/4 revision (50.00%)
703 finding outgoing largefiles: 3/4 revision (75.00%)
703 finding outgoing largefiles: 3/4 revision (75.00%)
704 largefiles to upload (2 entities):
704 largefiles to upload (2 entities):
705 b
705 b
706 13f9ed0898e315bf59dc2973fec52037b6f441a2
706 13f9ed0898e315bf59dc2973fec52037b6f441a2
707 c801c9cfe94400963fcb683246217d5db77f9a9a
707 c801c9cfe94400963fcb683246217d5db77f9a9a
708
708
709
709
710 $ cd ..
710 $ cd ..
711
711
712 merge action 'd' for 'local renamed directory to d2/g' which has no filename
712 merge action 'd' for 'local renamed directory to d2/g' which has no filename
713 ==================================================================================
713 ==================================================================================
714
714
715 $ hg init merge-action
715 $ hg init merge-action
716 $ cd merge-action
716 $ cd merge-action
717 $ touch l
717 $ touch l
718 $ hg add --large l
718 $ hg add --large l
719 $ mkdir d1
719 $ mkdir d1
720 $ touch d1/f
720 $ touch d1/f
721 $ hg ci -Aqm0
721 $ hg ci -Aqm0
722 Invoking status precommit hook
722 Invoking status precommit hook
723 A d1/f
723 A d1/f
724 A l
724 A l
725 $ echo > d1/f
725 $ echo > d1/f
726 $ touch d1/g
726 $ touch d1/g
727 $ hg ci -Aqm1
727 $ hg ci -Aqm1
728 Invoking status precommit hook
728 Invoking status precommit hook
729 M d1/f
729 M d1/f
730 A d1/g
730 A d1/g
731 $ hg up -qr0
731 $ hg up -qr0
732 $ hg mv d1 d2
732 $ hg mv d1 d2
733 moving d1/f to d2/f (glob)
733 moving d1/f to d2/f (glob)
734 $ hg ci -qm2
734 $ hg ci -qm2
735 Invoking status precommit hook
735 Invoking status precommit hook
736 A d2/f
736 A d2/f
737 R d1/f
737 R d1/f
738 $ hg merge
738 $ hg merge
739 merging d2/f and d1/f to d2/f
739 merging d2/f and d1/f to d2/f
740 getting changed largefiles
740 getting changed largefiles
741 0 largefiles updated, 0 removed
741 0 largefiles updated, 0 removed
742 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
742 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
743 (branch merge, don't forget to commit)
743 (branch merge, don't forget to commit)
744 $ cd ..
744 $ cd ..
745
745
746
746
747 Merge conflicts:
747 Merge conflicts:
748 =====================
748 =====================
749
749
750 $ hg init merge
750 $ hg init merge
751 $ cd merge
751 $ cd merge
752 $ echo 0 > f-different
752 $ echo 0 > f-different
753 $ echo 0 > f-same
753 $ echo 0 > f-same
754 $ echo 0 > f-unchanged-1
754 $ echo 0 > f-unchanged-1
755 $ echo 0 > f-unchanged-2
755 $ echo 0 > f-unchanged-2
756 $ hg add --large *
756 $ hg add --large *
757 $ hg ci -m0
757 $ hg ci -m0
758 Invoking status precommit hook
758 Invoking status precommit hook
759 A f-different
759 A f-different
760 A f-same
760 A f-same
761 A f-unchanged-1
761 A f-unchanged-1
762 A f-unchanged-2
762 A f-unchanged-2
763 $ echo tmp1 > f-unchanged-1
763 $ echo tmp1 > f-unchanged-1
764 $ echo tmp1 > f-unchanged-2
764 $ echo tmp1 > f-unchanged-2
765 $ echo tmp1 > f-same
765 $ echo tmp1 > f-same
766 $ hg ci -m1
766 $ hg ci -m1
767 Invoking status precommit hook
767 Invoking status precommit hook
768 M f-same
768 M f-same
769 M f-unchanged-1
769 M f-unchanged-1
770 M f-unchanged-2
770 M f-unchanged-2
771 $ echo 2 > f-different
771 $ echo 2 > f-different
772 $ echo 0 > f-unchanged-1
772 $ echo 0 > f-unchanged-1
773 $ echo 1 > f-unchanged-2
773 $ echo 1 > f-unchanged-2
774 $ echo 1 > f-same
774 $ echo 1 > f-same
775 $ hg ci -m2
775 $ hg ci -m2
776 Invoking status precommit hook
776 Invoking status precommit hook
777 M f-different
777 M f-different
778 M f-same
778 M f-same
779 M f-unchanged-1
779 M f-unchanged-1
780 M f-unchanged-2
780 M f-unchanged-2
781 $ hg up -qr0
781 $ hg up -qr0
782 $ echo tmp2 > f-unchanged-1
782 $ echo tmp2 > f-unchanged-1
783 $ echo tmp2 > f-unchanged-2
783 $ echo tmp2 > f-unchanged-2
784 $ echo tmp2 > f-same
784 $ echo tmp2 > f-same
785 $ hg ci -m3
785 $ hg ci -m3
786 Invoking status precommit hook
786 Invoking status precommit hook
787 M f-same
787 M f-same
788 M f-unchanged-1
788 M f-unchanged-1
789 M f-unchanged-2
789 M f-unchanged-2
790 created new head
790 created new head
791 $ echo 1 > f-different
791 $ echo 1 > f-different
792 $ echo 1 > f-unchanged-1
792 $ echo 1 > f-unchanged-1
793 $ echo 0 > f-unchanged-2
793 $ echo 0 > f-unchanged-2
794 $ echo 1 > f-same
794 $ echo 1 > f-same
795 $ hg ci -m4
795 $ hg ci -m4
796 Invoking status precommit hook
796 Invoking status precommit hook
797 M f-different
797 M f-different
798 M f-same
798 M f-same
799 M f-unchanged-1
799 M f-unchanged-1
800 M f-unchanged-2
800 M f-unchanged-2
801 $ hg merge
801 $ hg merge
802 largefile f-different has a merge conflict
802 largefile f-different has a merge conflict
803 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
803 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
804 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
804 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
805 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
805 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
806 getting changed largefiles
806 getting changed largefiles
807 1 largefiles updated, 0 removed
807 1 largefiles updated, 0 removed
808 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
808 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
809 (branch merge, don't forget to commit)
809 (branch merge, don't forget to commit)
810 $ cat f-different
810 $ cat f-different
811 1
811 1
812 $ cat f-same
812 $ cat f-same
813 1
813 1
814 $ cat f-unchanged-1
814 $ cat f-unchanged-1
815 1
815 1
816 $ cat f-unchanged-2
816 $ cat f-unchanged-2
817 1
817 1
818 $ cd ..
818 $ cd ..
819
819
820 Test largefile insulation (do not enabled a side effect
820 Test largefile insulation (do not enabled a side effect
821 ========================================================
821 ========================================================
822
822
823 Check whether "largefiles" feature is supported only in repositories
823 Check whether "largefiles" feature is supported only in repositories
824 enabling largefiles extension.
824 enabling largefiles extension.
825
825
826 $ mkdir individualenabling
826 $ mkdir individualenabling
827 $ cd individualenabling
827 $ cd individualenabling
828
828
829 $ hg init enabledlocally
829 $ hg init enabledlocally
830 $ echo large > enabledlocally/large
830 $ echo large > enabledlocally/large
831 $ hg -R enabledlocally add --large enabledlocally/large
831 $ hg -R enabledlocally add --large enabledlocally/large
832 $ hg -R enabledlocally commit -m '#0'
832 $ hg -R enabledlocally commit -m '#0'
833 Invoking status precommit hook
833 Invoking status precommit hook
834 A large
834 A large
835
835
836 $ hg init notenabledlocally
836 $ hg init notenabledlocally
837 $ echo large > notenabledlocally/large
837 $ echo large > notenabledlocally/large
838 $ hg -R notenabledlocally add --large notenabledlocally/large
838 $ hg -R notenabledlocally add --large notenabledlocally/large
839 $ hg -R notenabledlocally commit -m '#0'
839 $ hg -R notenabledlocally commit -m '#0'
840 Invoking status precommit hook
840 Invoking status precommit hook
841 A large
841 A large
842
842
843 $ cat >> $HGRCPATH <<EOF
843 $ cat >> $HGRCPATH <<EOF
844 > [extensions]
844 > [extensions]
845 > # disable globally
845 > # disable globally
846 > largefiles=!
846 > largefiles=!
847 > EOF
847 > EOF
848 $ cat >> enabledlocally/.hg/hgrc <<EOF
848 $ cat >> enabledlocally/.hg/hgrc <<EOF
849 > [extensions]
849 > [extensions]
850 > # enable locally
850 > # enable locally
851 > largefiles=
851 > largefiles=
852 > EOF
852 > EOF
853 $ hg -R enabledlocally root
853 $ hg -R enabledlocally root
854 $TESTTMP/individualenabling/enabledlocally (glob)
854 $TESTTMP/individualenabling/enabledlocally (glob)
855 $ hg -R notenabledlocally root
855 $ hg -R notenabledlocally root
856 abort: repository requires features unknown to this Mercurial: largefiles!
856 abort: repository requires features unknown to this Mercurial: largefiles!
857 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
857 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
858 [255]
858 [255]
859
859
860 $ hg init push-dst
860 $ hg init push-dst
861 $ hg -R enabledlocally push push-dst
861 $ hg -R enabledlocally push push-dst
862 pushing to push-dst
862 pushing to push-dst
863 abort: required features are not supported in the destination: largefiles
863 abort: required features are not supported in the destination: largefiles
864 [255]
864 [255]
865
865
866 $ hg init pull-src
866 $ hg init pull-src
867 $ hg -R pull-src pull enabledlocally
867 $ hg -R pull-src pull enabledlocally
868 pulling from enabledlocally
868 pulling from enabledlocally
869 abort: required features are not supported in the destination: largefiles
869 abort: required features are not supported in the destination: largefiles
870 [255]
870 [255]
871
871
872 $ hg clone enabledlocally clone-dst
872 $ hg clone enabledlocally clone-dst
873 abort: repository requires features unknown to this Mercurial: largefiles!
873 abort: repository requires features unknown to this Mercurial: largefiles!
874 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
874 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
875 [255]
875 [255]
876 $ test -d clone-dst
876 $ test -d clone-dst
877 [1]
877 [1]
878 $ hg clone --pull enabledlocally clone-pull-dst
878 $ hg clone --pull enabledlocally clone-pull-dst
879 abort: required features are not supported in the destination: largefiles
879 abort: required features are not supported in the destination: largefiles
880 [255]
880 [255]
881 $ test -d clone-pull-dst
881 $ test -d clone-pull-dst
882 [1]
882 [1]
883
883
884 #if serve
884 #if serve
885
885
886 Test largefiles specific peer setup, when largefiles is enabled
886 Test largefiles specific peer setup, when largefiles is enabled
887 locally (issue4109)
887 locally (issue4109)
888
888
889 $ hg showconfig extensions | grep largefiles
889 $ hg showconfig extensions | grep largefiles
890 extensions.largefiles=!
890 extensions.largefiles=!
891 $ mkdir -p $TESTTMP/individualenabling/usercache
891 $ mkdir -p $TESTTMP/individualenabling/usercache
892
892
893 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
893 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
894 $ cat hg.pid >> $DAEMON_PIDS
894 $ cat hg.pid >> $DAEMON_PIDS
895
895
896 $ hg init pull-dst
896 $ hg init pull-dst
897 $ cat > pull-dst/.hg/hgrc <<EOF
897 $ cat > pull-dst/.hg/hgrc <<EOF
898 > [extensions]
898 > [extensions]
899 > # enable locally
899 > # enable locally
900 > largefiles=
900 > largefiles=
901 > [largefiles]
901 > [largefiles]
902 > # ignore system cache to force largefiles specific wire proto access
902 > # ignore system cache to force largefiles specific wire proto access
903 > usercache=$TESTTMP/individualenabling/usercache
903 > usercache=$TESTTMP/individualenabling/usercache
904 > EOF
904 > EOF
905 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
905 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
906
906
907 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
907 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
908 #endif
908 #endif
909
909
910 Test overridden functions work correctly even for repos disabling
911 largefiles (issue4547)
912
913 $ hg showconfig extensions | grep largefiles
914 extensions.largefiles=!
915
916 (test updating implied by clone)
917
918 $ hg init enabled-but-no-largefiles
919 $ echo normal1 > enabled-but-no-largefiles/normal1
920 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
921 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
922 Invoking status precommit hook
923 A normal1
924 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
925 > [extensions]
926 > # enable locally
927 > largefiles=
928 > EOF
929 $ hg clone -q enabled-but-no-largefiles no-largefiles
930
931 (test rebasing implied by pull: precommit while rebasing unexpectedly
932 shows "normal3" as "?", because lfdirstate isn't yet written out at
933 that time)
934
935 $ echo normal2 > enabled-but-no-largefiles/normal2
936 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
937 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
938 Invoking status precommit hook
939 A normal2
940
941 $ echo normal3 > no-largefiles/normal3
942 $ hg -R no-largefiles add no-largefiles/normal3
943 $ hg -R no-largefiles commit -m '#1@no-largefiles'
944 Invoking status precommit hook
945 A normal3
946
947 $ hg -R no-largefiles -q pull --rebase
948 Invoking status precommit hook
949 ? normal3
950
951 (test reverting)
952
953 $ hg init subrepo-root
954 $ cat >> subrepo-root/.hg/hgrc <<EOF
955 > [extensions]
956 > # enable locally
957 > largefiles=
958 > EOF
959 $ echo large > subrepo-root/large
960 $ hg -R subrepo-root add --large subrepo-root/large
961 $ hg clone -q no-largefiles subrepo-root/no-largefiles
962 $ cat > subrepo-root/.hgsub <<EOF
963 > no-largefiles = no-largefiles
964 > EOF
965 $ hg -R subrepo-root add subrepo-root/.hgsub
966 $ hg -R subrepo-root commit -m '#0'
967 Invoking status precommit hook
968 A .hgsub
969 A large
970 ? .hgsubstate
971 $ echo dirty >> subrepo-root/large
972 $ echo dirty >> subrepo-root/no-largefiles/normal1
973 $ hg -R subrepo-root status -S
974 M large
975 M no-largefiles/normal1
976 $ hg -R subrepo-root revert --all
977 reverting subrepo-root/.hglf/large (glob)
978 reverting subrepo no-largefiles
979 reverting subrepo-root/no-largefiles/normal1 (glob)
980
910 $ cd ..
981 $ cd ..
911
982
912
983
913 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
984 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
914 =========================================================================
985 =========================================================================
915
986
916 $ hg showconfig extensions | grep largefiles
987 $ hg showconfig extensions | grep largefiles
917 extensions.largefiles=!
988 extensions.largefiles=!
918
989
919 $ mkdir issue3861
990 $ mkdir issue3861
920 $ cd issue3861
991 $ cd issue3861
921 $ hg init src
992 $ hg init src
922 $ hg clone -q src dst
993 $ hg clone -q src dst
923 $ echo a > src/a
994 $ echo a > src/a
924 $ hg -R src commit -Aqm "#0"
995 $ hg -R src commit -Aqm "#0"
925 Invoking status precommit hook
996 Invoking status precommit hook
926 A a
997 A a
927
998
928 $ cat >> dst/.hg/hgrc <<EOF
999 $ cat >> dst/.hg/hgrc <<EOF
929 > [extensions]
1000 > [extensions]
930 > largefiles=
1001 > largefiles=
931 > EOF
1002 > EOF
932 $ hg -R dst pull --rebase
1003 $ hg -R dst pull --rebase
933 pulling from $TESTTMP/issue3861/src (glob)
1004 pulling from $TESTTMP/issue3861/src (glob)
934 requesting all changes
1005 requesting all changes
935 adding changesets
1006 adding changesets
936 adding manifests
1007 adding manifests
937 adding file changes
1008 adding file changes
938 added 1 changesets with 1 changes to 1 files
1009 added 1 changesets with 1 changes to 1 files
939 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
1010 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
940 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1011 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
941
1012
942 $ cd ..
1013 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now