##// END OF EJS Templates
largefiles: update standins only at the 1st commit of "hg rebase --continue"...
FUJIWARA Katsunori -
r23187:f726b05e default
parent child Browse files
Show More
@@ -1,539 +1,559
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 elif not getattr(repo, "_isconverting", False):
207 elif not getattr(repo, "_isconverting", False):
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 lfdirstate = openlfdirstate(repo.ui, repo)
395 lfdirstate = openlfdirstate(repo.ui, repo)
396 for f in ctx.files():
396 for f in ctx.files():
397 if isstandin(f):
397 if isstandin(f):
398 lfile = splitstandin(f)
398 lfile = splitstandin(f)
399 synclfdirstate(repo, lfdirstate, lfile, False)
399 synclfdirstate(repo, lfdirstate, lfile, False)
400 lfdirstate.write()
400 lfdirstate.write()
401
401
402 def getlfilestoupdate(oldstandins, newstandins):
402 def getlfilestoupdate(oldstandins, newstandins):
403 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
403 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
404 filelist = []
404 filelist = []
405 for f in changedstandins:
405 for f in changedstandins:
406 if f[0] not in filelist:
406 if f[0] not in filelist:
407 filelist.append(f[0])
407 filelist.append(f[0])
408 return filelist
408 return filelist
409
409
410 def getlfilestoupload(repo, missing, addfunc):
410 def getlfilestoupload(repo, missing, addfunc):
411 for n in missing:
411 for n in missing:
412 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
412 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
413 ctx = repo[n]
413 ctx = repo[n]
414 files = set(ctx.files())
414 files = set(ctx.files())
415 if len(parents) == 2:
415 if len(parents) == 2:
416 mc = ctx.manifest()
416 mc = ctx.manifest()
417 mp1 = ctx.parents()[0].manifest()
417 mp1 = ctx.parents()[0].manifest()
418 mp2 = ctx.parents()[1].manifest()
418 mp2 = ctx.parents()[1].manifest()
419 for f in mp1:
419 for f in mp1:
420 if f not in mc:
420 if f not in mc:
421 files.add(f)
421 files.add(f)
422 for f in mp2:
422 for f in mp2:
423 if f not in mc:
423 if f not in mc:
424 files.add(f)
424 files.add(f)
425 for f in mc:
425 for f in mc:
426 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
426 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
427 files.add(f)
427 files.add(f)
428 for fn in files:
428 for fn in files:
429 if isstandin(fn) and fn in ctx:
429 if isstandin(fn) and fn in ctx:
430 addfunc(fn, ctx[fn].data().strip())
430 addfunc(fn, ctx[fn].data().strip())
431
431
432 def updatestandinsbymatch(repo, match):
432 def updatestandinsbymatch(repo, match):
433 '''Update standins in the working directory according to specified match
433 '''Update standins in the working directory according to specified match
434
434
435 This returns (possibly modified) ``match`` object to be used for
435 This returns (possibly modified) ``match`` object to be used for
436 subsequent commit process.
436 subsequent commit process.
437 '''
437 '''
438
438
439 ui = repo.ui
439 ui = repo.ui
440
440
441 # Case 0: Automated committing
441 # Case 0: Automated committing
442 #
442 #
443 # While automated committing (like rebase, transplant
443 # While automated committing (like rebase, transplant
444 # and so on), this code path is used to avoid:
444 # and so on), this code path is used to avoid:
445 # (1) updating standins, because standins should
445 # (1) updating standins, because standins should
446 # be already updated at this point
446 # be already updated at this point
447 # (2) aborting when standins are matched by "match",
447 # (2) aborting when standins are matched by "match",
448 # because automated committing may specify them directly
448 # because automated committing may specify them directly
449 #
449 #
450 if getattr(repo, "_isrebasing", False) or \
450 if getattr(repo, "_istransplanting", False):
451 getattr(repo, "_istransplanting", False):
452 return match
451 return match
453
452
454 # Case 1: user calls commit with no specific files or
453 # Case 1: user calls commit with no specific files or
455 # include/exclude patterns: refresh and commit all files that
454 # include/exclude patterns: refresh and commit all files that
456 # are "dirty".
455 # are "dirty".
457 if match is None or match.always():
456 if match is None or match.always():
458 # Spend a bit of time here to get a list of files we know
457 # Spend a bit of time here to get a list of files we know
459 # are modified so we can compare only against those.
458 # are modified so we can compare only against those.
460 # It can cost a lot of time (several seconds)
459 # It can cost a lot of time (several seconds)
461 # otherwise to update all standins if the largefiles are
460 # otherwise to update all standins if the largefiles are
462 # large.
461 # large.
463 lfdirstate = openlfdirstate(ui, repo)
462 lfdirstate = openlfdirstate(ui, repo)
464 dirtymatch = match_.always(repo.root, repo.getcwd())
463 dirtymatch = match_.always(repo.root, repo.getcwd())
465 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
464 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
466 False)
465 False)
467 modifiedfiles = unsure + s.modified + s.added + s.removed
466 modifiedfiles = unsure + s.modified + s.added + s.removed
468 lfiles = listlfiles(repo)
467 lfiles = listlfiles(repo)
469 # this only loops through largefiles that exist (not
468 # this only loops through largefiles that exist (not
470 # removed/renamed)
469 # removed/renamed)
471 for lfile in lfiles:
470 for lfile in lfiles:
472 if lfile in modifiedfiles:
471 if lfile in modifiedfiles:
473 if os.path.exists(
472 if os.path.exists(
474 repo.wjoin(standin(lfile))):
473 repo.wjoin(standin(lfile))):
475 # this handles the case where a rebase is being
474 # this handles the case where a rebase is being
476 # performed and the working copy is not updated
475 # performed and the working copy is not updated
477 # yet.
476 # yet.
478 if os.path.exists(repo.wjoin(lfile)):
477 if os.path.exists(repo.wjoin(lfile)):
479 updatestandin(repo,
478 updatestandin(repo,
480 standin(lfile))
479 standin(lfile))
481
480
482 return match
481 return match
483
482
484 lfiles = listlfiles(repo)
483 lfiles = listlfiles(repo)
485 match._files = repo._subdirlfs(match.files(), lfiles)
484 match._files = repo._subdirlfs(match.files(), lfiles)
486
485
487 # Case 2: user calls commit with specified patterns: refresh
486 # Case 2: user calls commit with specified patterns: refresh
488 # any matching big files.
487 # any matching big files.
489 smatcher = composestandinmatcher(repo, match)
488 smatcher = composestandinmatcher(repo, match)
490 standins = repo.dirstate.walk(smatcher, [], False, False)
489 standins = repo.dirstate.walk(smatcher, [], False, False)
491
490
492 # No matching big files: get out of the way and pass control to
491 # No matching big files: get out of the way and pass control to
493 # the usual commit() method.
492 # the usual commit() method.
494 if not standins:
493 if not standins:
495 return match
494 return match
496
495
497 # Refresh all matching big files. It's possible that the
496 # Refresh all matching big files. It's possible that the
498 # commit will end up failing, in which case the big files will
497 # commit will end up failing, in which case the big files will
499 # stay refreshed. No harm done: the user modified them and
498 # stay refreshed. No harm done: the user modified them and
500 # asked to commit them, so sooner or later we're going to
499 # asked to commit them, so sooner or later we're going to
501 # refresh the standins. Might as well leave them refreshed.
500 # refresh the standins. Might as well leave them refreshed.
502 lfdirstate = openlfdirstate(ui, repo)
501 lfdirstate = openlfdirstate(ui, repo)
503 for fstandin in standins:
502 for fstandin in standins:
504 lfile = splitstandin(fstandin)
503 lfile = splitstandin(fstandin)
505 if lfdirstate[lfile] != 'r':
504 if lfdirstate[lfile] != 'r':
506 updatestandin(repo, fstandin)
505 updatestandin(repo, fstandin)
507
506
508 # Cook up a new matcher that only matches regular files or
507 # Cook up a new matcher that only matches regular files or
509 # standins corresponding to the big files requested by the
508 # standins corresponding to the big files requested by the
510 # user. Have to modify _files to prevent commit() from
509 # user. Have to modify _files to prevent commit() from
511 # complaining "not tracked" for big files.
510 # complaining "not tracked" for big files.
512 match = copy.copy(match)
511 match = copy.copy(match)
513 origmatchfn = match.matchfn
512 origmatchfn = match.matchfn
514
513
515 # Check both the list of largefiles and the list of
514 # Check both the list of largefiles and the list of
516 # standins because if a largefile was removed, it
515 # standins because if a largefile was removed, it
517 # won't be in the list of largefiles at this point
516 # won't be in the list of largefiles at this point
518 match._files += sorted(standins)
517 match._files += sorted(standins)
519
518
520 actualfiles = []
519 actualfiles = []
521 for f in match._files:
520 for f in match._files:
522 fstandin = standin(f)
521 fstandin = standin(f)
523
522
524 # ignore known largefiles and standins
523 # ignore known largefiles and standins
525 if f in lfiles or fstandin in standins:
524 if f in lfiles or fstandin in standins:
526 continue
525 continue
527
526
528 actualfiles.append(f)
527 actualfiles.append(f)
529 match._files = actualfiles
528 match._files = actualfiles
530
529
531 def matchfn(f):
530 def matchfn(f):
532 if origmatchfn(f):
531 if origmatchfn(f):
533 return f not in lfiles
532 return f not in lfiles
534 else:
533 else:
535 return f in standins
534 return f in standins
536
535
537 match.matchfn = matchfn
536 match.matchfn = matchfn
538
537
539 return match
538 return match
539
540 class automatedcommithook(object):
541 '''Statefull hook to update standins at the 1st commit of resuming
542
543 For efficiency, updating standins in the working directory should
544 be avoided while automated committing (like rebase, transplant and
545 so on), because they should be updated before committing.
546
547 But the 1st commit of resuming automated committing (e.g. ``rebase
548 --continue``) should update them, because largefiles may be
549 modified manually.
550 '''
551 def __init__(self, resuming):
552 self.resuming = resuming
553
554 def __call__(self, repo, match):
555 if self.resuming:
556 self.resuming = False # avoids updating at subsequent commits
557 return updatestandinsbymatch(repo, match)
558 else:
559 return match
@@ -1,1298 +1,1301
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 installnormalfilesmatchfn(manifest):
25 def installnormalfilesmatchfn(manifest):
26 '''installmatchfn with a matchfn that ignores all largefiles'''
26 '''installmatchfn with a matchfn that ignores all largefiles'''
27 def overridematch(ctx, pats=[], opts={}, globbed=False,
27 def overridematch(ctx, pats=[], opts={}, globbed=False,
28 default='relpath'):
28 default='relpath'):
29 match = oldmatch(ctx, pats, opts, globbed, default)
29 match = oldmatch(ctx, pats, opts, globbed, default)
30 m = copy.copy(match)
30 m = copy.copy(match)
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 manifest)
32 manifest)
33 m._files = filter(notlfile, m._files)
33 m._files = filter(notlfile, m._files)
34 m._fmap = set(m._files)
34 m._fmap = set(m._files)
35 m._always = False
35 m._always = False
36 origmatchfn = m.matchfn
36 origmatchfn = m.matchfn
37 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
37 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
38 return m
38 return m
39 oldmatch = installmatchfn(overridematch)
39 oldmatch = installmatchfn(overridematch)
40
40
41 def installmatchfn(f):
41 def installmatchfn(f):
42 '''monkey patch the scmutil module with a custom match function.
42 '''monkey patch the scmutil module with a custom match function.
43 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
43 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
44 oldmatch = scmutil.match
44 oldmatch = scmutil.match
45 setattr(f, 'oldmatch', oldmatch)
45 setattr(f, 'oldmatch', oldmatch)
46 scmutil.match = f
46 scmutil.match = f
47 return oldmatch
47 return oldmatch
48
48
49 def restorematchfn():
49 def restorematchfn():
50 '''restores scmutil.match to what it was before installmatchfn
50 '''restores scmutil.match to what it was before installmatchfn
51 was called. no-op if scmutil.match is its original function.
51 was called. no-op if scmutil.match is its original function.
52
52
53 Note that n calls to installmatchfn will require n calls to
53 Note that n calls to installmatchfn will require n calls to
54 restore matchfn to reverse'''
54 restore matchfn to reverse'''
55 scmutil.match = getattr(scmutil.match, 'oldmatch')
55 scmutil.match = getattr(scmutil.match, 'oldmatch')
56
56
57 def installmatchandpatsfn(f):
57 def installmatchandpatsfn(f):
58 oldmatchandpats = scmutil.matchandpats
58 oldmatchandpats = scmutil.matchandpats
59 setattr(f, 'oldmatchandpats', oldmatchandpats)
59 setattr(f, 'oldmatchandpats', oldmatchandpats)
60 scmutil.matchandpats = f
60 scmutil.matchandpats = f
61 return oldmatchandpats
61 return oldmatchandpats
62
62
63 def restorematchandpatsfn():
63 def restorematchandpatsfn():
64 '''restores scmutil.matchandpats to what it was before
64 '''restores scmutil.matchandpats to what it was before
65 installmatchandpatsfn was called. No-op if scmutil.matchandpats
65 installmatchandpatsfn was called. No-op if scmutil.matchandpats
66 is its original function.
66 is its original function.
67
67
68 Note that n calls to installmatchandpatsfn will require n calls
68 Note that n calls to installmatchandpatsfn will require n calls
69 to restore matchfn to reverse'''
69 to restore matchfn to reverse'''
70 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
70 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
71 scmutil.matchandpats)
71 scmutil.matchandpats)
72
72
73 def addlargefiles(ui, repo, *pats, **opts):
73 def addlargefiles(ui, repo, *pats, **opts):
74 large = opts.pop('large', None)
74 large = opts.pop('large', None)
75 lfsize = lfutil.getminsize(
75 lfsize = lfutil.getminsize(
76 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
76 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
77
77
78 lfmatcher = None
78 lfmatcher = None
79 if lfutil.islfilesrepo(repo):
79 if lfutil.islfilesrepo(repo):
80 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
80 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
81 if lfpats:
81 if lfpats:
82 lfmatcher = match_.match(repo.root, '', list(lfpats))
82 lfmatcher = match_.match(repo.root, '', list(lfpats))
83
83
84 lfnames = []
84 lfnames = []
85 m = scmutil.match(repo[None], pats, opts)
85 m = scmutil.match(repo[None], pats, opts)
86 m.bad = lambda x, y: None
86 m.bad = lambda x, y: None
87 wctx = repo[None]
87 wctx = repo[None]
88 for f in repo.walk(m):
88 for f in repo.walk(m):
89 exact = m.exact(f)
89 exact = m.exact(f)
90 lfile = lfutil.standin(f) in wctx
90 lfile = lfutil.standin(f) in wctx
91 nfile = f in wctx
91 nfile = f in wctx
92 exists = lfile or nfile
92 exists = lfile or nfile
93
93
94 # Don't warn the user when they attempt to add a normal tracked file.
94 # Don't warn the user when they attempt to add a normal tracked file.
95 # The normal add code will do that for us.
95 # The normal add code will do that for us.
96 if exact and exists:
96 if exact and exists:
97 if lfile:
97 if lfile:
98 ui.warn(_('%s already a largefile\n') % f)
98 ui.warn(_('%s already a largefile\n') % f)
99 continue
99 continue
100
100
101 if (exact or not exists) and not lfutil.isstandin(f):
101 if (exact or not exists) and not lfutil.isstandin(f):
102 wfile = repo.wjoin(f)
102 wfile = repo.wjoin(f)
103
103
104 # In case the file was removed previously, but not committed
104 # In case the file was removed previously, but not committed
105 # (issue3507)
105 # (issue3507)
106 if not os.path.exists(wfile):
106 if not os.path.exists(wfile):
107 continue
107 continue
108
108
109 abovemin = (lfsize and
109 abovemin = (lfsize and
110 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
110 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
111 if large or abovemin or (lfmatcher and lfmatcher(f)):
111 if large or abovemin or (lfmatcher and lfmatcher(f)):
112 lfnames.append(f)
112 lfnames.append(f)
113 if ui.verbose or not exact:
113 if ui.verbose or not exact:
114 ui.status(_('adding %s as a largefile\n') % m.rel(f))
114 ui.status(_('adding %s as a largefile\n') % m.rel(f))
115
115
116 bad = []
116 bad = []
117
117
118 # Need to lock, otherwise there could be a race condition between
118 # Need to lock, otherwise there could be a race condition between
119 # when standins are created and added to the repo.
119 # when standins are created and added to the repo.
120 wlock = repo.wlock()
120 wlock = repo.wlock()
121 try:
121 try:
122 if not opts.get('dry_run'):
122 if not opts.get('dry_run'):
123 standins = []
123 standins = []
124 lfdirstate = lfutil.openlfdirstate(ui, repo)
124 lfdirstate = lfutil.openlfdirstate(ui, repo)
125 for f in lfnames:
125 for f in lfnames:
126 standinname = lfutil.standin(f)
126 standinname = lfutil.standin(f)
127 lfutil.writestandin(repo, standinname, hash='',
127 lfutil.writestandin(repo, standinname, hash='',
128 executable=lfutil.getexecutable(repo.wjoin(f)))
128 executable=lfutil.getexecutable(repo.wjoin(f)))
129 standins.append(standinname)
129 standins.append(standinname)
130 if lfdirstate[f] == 'r':
130 if lfdirstate[f] == 'r':
131 lfdirstate.normallookup(f)
131 lfdirstate.normallookup(f)
132 else:
132 else:
133 lfdirstate.add(f)
133 lfdirstate.add(f)
134 lfdirstate.write()
134 lfdirstate.write()
135 bad += [lfutil.splitstandin(f)
135 bad += [lfutil.splitstandin(f)
136 for f in repo[None].add(standins)
136 for f in repo[None].add(standins)
137 if f in m.files()]
137 if f in m.files()]
138 finally:
138 finally:
139 wlock.release()
139 wlock.release()
140 return bad
140 return bad
141
141
142 def removelargefiles(ui, repo, isaddremove, *pats, **opts):
142 def removelargefiles(ui, repo, isaddremove, *pats, **opts):
143 after = opts.get('after')
143 after = opts.get('after')
144 if not pats and not after:
144 if not pats and not after:
145 raise util.Abort(_('no files specified'))
145 raise util.Abort(_('no files specified'))
146 m = scmutil.match(repo[None], pats, opts)
146 m = scmutil.match(repo[None], pats, opts)
147 try:
147 try:
148 repo.lfstatus = True
148 repo.lfstatus = True
149 s = repo.status(match=m, clean=True)
149 s = repo.status(match=m, clean=True)
150 finally:
150 finally:
151 repo.lfstatus = False
151 repo.lfstatus = False
152 manifest = repo[None].manifest()
152 manifest = repo[None].manifest()
153 modified, added, deleted, clean = [[f for f in list
153 modified, added, deleted, clean = [[f for f in list
154 if lfutil.standin(f) in manifest]
154 if lfutil.standin(f) in manifest]
155 for list in (s.modified, s.added,
155 for list in (s.modified, s.added,
156 s.deleted, s.clean)]
156 s.deleted, s.clean)]
157
157
158 def warn(files, msg):
158 def warn(files, msg):
159 for f in files:
159 for f in files:
160 ui.warn(msg % m.rel(f))
160 ui.warn(msg % m.rel(f))
161 return int(len(files) > 0)
161 return int(len(files) > 0)
162
162
163 result = 0
163 result = 0
164
164
165 if after:
165 if after:
166 remove = deleted
166 remove = deleted
167 result = warn(modified + added + clean,
167 result = warn(modified + added + clean,
168 _('not removing %s: file still exists\n'))
168 _('not removing %s: file still exists\n'))
169 else:
169 else:
170 remove = deleted + clean
170 remove = deleted + clean
171 result = warn(modified, _('not removing %s: file is modified (use -f'
171 result = warn(modified, _('not removing %s: file is modified (use -f'
172 ' to force removal)\n'))
172 ' to force removal)\n'))
173 result = warn(added, _('not removing %s: file has been marked for add'
173 result = warn(added, _('not removing %s: file has been marked for add'
174 ' (use forget to undo)\n')) or result
174 ' (use forget to undo)\n')) or result
175
175
176 for f in sorted(remove):
176 for f in sorted(remove):
177 if ui.verbose or not m.exact(f):
177 if ui.verbose or not m.exact(f):
178 ui.status(_('removing %s\n') % m.rel(f))
178 ui.status(_('removing %s\n') % m.rel(f))
179
179
180 # Need to lock because standin files are deleted then removed from the
180 # Need to lock because standin files are deleted then removed from the
181 # repository and we could race in-between.
181 # repository and we could race in-between.
182 wlock = repo.wlock()
182 wlock = repo.wlock()
183 try:
183 try:
184 lfdirstate = lfutil.openlfdirstate(ui, repo)
184 lfdirstate = lfutil.openlfdirstate(ui, repo)
185 for f in remove:
185 for f in remove:
186 if not after:
186 if not after:
187 # If this is being called by addremove, notify the user that we
187 # If this is being called by addremove, notify the user that we
188 # are removing the file.
188 # are removing the file.
189 if isaddremove:
189 if isaddremove:
190 ui.status(_('removing %s\n') % f)
190 ui.status(_('removing %s\n') % f)
191 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
191 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
192 lfdirstate.remove(f)
192 lfdirstate.remove(f)
193 lfdirstate.write()
193 lfdirstate.write()
194 remove = [lfutil.standin(f) for f in remove]
194 remove = [lfutil.standin(f) for f in remove]
195 # If this is being called by addremove, let the original addremove
195 # If this is being called by addremove, let the original addremove
196 # function handle this.
196 # function handle this.
197 if not isaddremove:
197 if not isaddremove:
198 for f in remove:
198 for f in remove:
199 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
199 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
200 repo[None].forget(remove)
200 repo[None].forget(remove)
201 finally:
201 finally:
202 wlock.release()
202 wlock.release()
203
203
204 return result
204 return result
205
205
206 # For overriding mercurial.hgweb.webcommands so that largefiles will
206 # For overriding mercurial.hgweb.webcommands so that largefiles will
207 # appear at their right place in the manifests.
207 # appear at their right place in the manifests.
208 def decodepath(orig, path):
208 def decodepath(orig, path):
209 return lfutil.splitstandin(path) or path
209 return lfutil.splitstandin(path) or path
210
210
211 # -- Wrappers: modify existing commands --------------------------------
211 # -- Wrappers: modify existing commands --------------------------------
212
212
213 # Add works by going through the files that the user wanted to add and
213 # Add works by going through the files that the user wanted to add and
214 # checking if they should be added as largefiles. Then it makes a new
214 # checking if they should be added as largefiles. Then it makes a new
215 # matcher which matches only the normal files and runs the original
215 # matcher which matches only the normal files and runs the original
216 # version of add.
216 # version of add.
217 def overrideadd(orig, ui, repo, *pats, **opts):
217 def overrideadd(orig, ui, repo, *pats, **opts):
218 normal = opts.pop('normal')
218 normal = opts.pop('normal')
219 if normal:
219 if normal:
220 if opts.get('large'):
220 if opts.get('large'):
221 raise util.Abort(_('--normal cannot be used with --large'))
221 raise util.Abort(_('--normal cannot be used with --large'))
222 return orig(ui, repo, *pats, **opts)
222 return orig(ui, repo, *pats, **opts)
223 bad = addlargefiles(ui, repo, *pats, **opts)
223 bad = addlargefiles(ui, repo, *pats, **opts)
224 installnormalfilesmatchfn(repo[None].manifest())
224 installnormalfilesmatchfn(repo[None].manifest())
225 result = orig(ui, repo, *pats, **opts)
225 result = orig(ui, repo, *pats, **opts)
226 restorematchfn()
226 restorematchfn()
227
227
228 return (result == 1 or bad) and 1 or 0
228 return (result == 1 or bad) and 1 or 0
229
229
230 def overrideremove(orig, ui, repo, *pats, **opts):
230 def overrideremove(orig, ui, repo, *pats, **opts):
231 installnormalfilesmatchfn(repo[None].manifest())
231 installnormalfilesmatchfn(repo[None].manifest())
232 result = orig(ui, repo, *pats, **opts)
232 result = orig(ui, repo, *pats, **opts)
233 restorematchfn()
233 restorematchfn()
234 return removelargefiles(ui, repo, False, *pats, **opts) or result
234 return removelargefiles(ui, repo, False, *pats, **opts) or result
235
235
236 def overridestatusfn(orig, repo, rev2, **opts):
236 def overridestatusfn(orig, repo, rev2, **opts):
237 try:
237 try:
238 repo._repo.lfstatus = True
238 repo._repo.lfstatus = True
239 return orig(repo, rev2, **opts)
239 return orig(repo, rev2, **opts)
240 finally:
240 finally:
241 repo._repo.lfstatus = False
241 repo._repo.lfstatus = False
242
242
243 def overridestatus(orig, ui, repo, *pats, **opts):
243 def overridestatus(orig, ui, repo, *pats, **opts):
244 try:
244 try:
245 repo.lfstatus = True
245 repo.lfstatus = True
246 return orig(ui, repo, *pats, **opts)
246 return orig(ui, repo, *pats, **opts)
247 finally:
247 finally:
248 repo.lfstatus = False
248 repo.lfstatus = False
249
249
250 def overridedirty(orig, repo, ignoreupdate=False):
250 def overridedirty(orig, repo, ignoreupdate=False):
251 try:
251 try:
252 repo._repo.lfstatus = True
252 repo._repo.lfstatus = True
253 return orig(repo, ignoreupdate)
253 return orig(repo, ignoreupdate)
254 finally:
254 finally:
255 repo._repo.lfstatus = False
255 repo._repo.lfstatus = False
256
256
257 def overridelog(orig, ui, repo, *pats, **opts):
257 def overridelog(orig, ui, repo, *pats, **opts):
258 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
258 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
259 default='relpath'):
259 default='relpath'):
260 """Matcher that merges root directory with .hglf, suitable for log.
260 """Matcher that merges root directory with .hglf, suitable for log.
261 It is still possible to match .hglf directly.
261 It is still possible to match .hglf directly.
262 For any listed files run log on the standin too.
262 For any listed files run log on the standin too.
263 matchfn tries both the given filename and with .hglf stripped.
263 matchfn tries both the given filename and with .hglf stripped.
264 """
264 """
265 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
265 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
266 m, p = copy.copy(matchandpats)
266 m, p = copy.copy(matchandpats)
267
267
268 if m.always():
268 if m.always():
269 # We want to match everything anyway, so there's no benefit trying
269 # We want to match everything anyway, so there's no benefit trying
270 # to add standins.
270 # to add standins.
271 return matchandpats
271 return matchandpats
272
272
273 pats = set(p)
273 pats = set(p)
274 # TODO: handling of patterns in both cases below
274 # TODO: handling of patterns in both cases below
275 if m._cwd:
275 if m._cwd:
276 if os.path.isabs(m._cwd):
276 if os.path.isabs(m._cwd):
277 # TODO: handle largefile magic when invoked from other cwd
277 # TODO: handle largefile magic when invoked from other cwd
278 return matchandpats
278 return matchandpats
279 back = (m._cwd.count('/') + 1) * '../'
279 back = (m._cwd.count('/') + 1) * '../'
280 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
280 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
281 else:
281 else:
282 pats.update(lfutil.standin(f) for f in p)
282 pats.update(lfutil.standin(f) for f in p)
283
283
284 for i in range(0, len(m._files)):
284 for i in range(0, len(m._files)):
285 standin = lfutil.standin(m._files[i])
285 standin = lfutil.standin(m._files[i])
286 if standin in repo[ctx.node()]:
286 if standin in repo[ctx.node()]:
287 m._files[i] = standin
287 m._files[i] = standin
288 elif m._files[i] not in repo[ctx.node()]:
288 elif m._files[i] not in repo[ctx.node()]:
289 m._files.append(standin)
289 m._files.append(standin)
290 pats.add(standin)
290 pats.add(standin)
291
291
292 m._fmap = set(m._files)
292 m._fmap = set(m._files)
293 m._always = False
293 m._always = False
294 origmatchfn = m.matchfn
294 origmatchfn = m.matchfn
295 def lfmatchfn(f):
295 def lfmatchfn(f):
296 lf = lfutil.splitstandin(f)
296 lf = lfutil.splitstandin(f)
297 if lf is not None and origmatchfn(lf):
297 if lf is not None and origmatchfn(lf):
298 return True
298 return True
299 r = origmatchfn(f)
299 r = origmatchfn(f)
300 return r
300 return r
301 m.matchfn = lfmatchfn
301 m.matchfn = lfmatchfn
302
302
303 return m, pats
303 return m, pats
304
304
305 # For hg log --patch, the match object is used in two different senses:
305 # For hg log --patch, the match object is used in two different senses:
306 # (1) to determine what revisions should be printed out, and
306 # (1) to determine what revisions should be printed out, and
307 # (2) to determine what files to print out diffs for.
307 # (2) to determine what files to print out diffs for.
308 # The magic matchandpats override should be used for case (1) but not for
308 # The magic matchandpats override should be used for case (1) but not for
309 # case (2).
309 # case (2).
310 def overridemakelogfilematcher(repo, pats, opts):
310 def overridemakelogfilematcher(repo, pats, opts):
311 pctx = repo[None]
311 pctx = repo[None]
312 match, pats = oldmatchandpats(pctx, pats, opts)
312 match, pats = oldmatchandpats(pctx, pats, opts)
313 return lambda rev: match
313 return lambda rev: match
314
314
315 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
315 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
316 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
316 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
317 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
317 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
318
318
319 try:
319 try:
320 return orig(ui, repo, *pats, **opts)
320 return orig(ui, repo, *pats, **opts)
321 finally:
321 finally:
322 restorematchandpatsfn()
322 restorematchandpatsfn()
323 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
323 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
324
324
325 def overrideverify(orig, ui, repo, *pats, **opts):
325 def overrideverify(orig, ui, repo, *pats, **opts):
326 large = opts.pop('large', False)
326 large = opts.pop('large', False)
327 all = opts.pop('lfa', False)
327 all = opts.pop('lfa', False)
328 contents = opts.pop('lfc', False)
328 contents = opts.pop('lfc', False)
329
329
330 result = orig(ui, repo, *pats, **opts)
330 result = orig(ui, repo, *pats, **opts)
331 if large or all or contents:
331 if large or all or contents:
332 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
332 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
333 return result
333 return result
334
334
335 def overridedebugstate(orig, ui, repo, *pats, **opts):
335 def overridedebugstate(orig, ui, repo, *pats, **opts):
336 large = opts.pop('large', False)
336 large = opts.pop('large', False)
337 if large:
337 if large:
338 class fakerepo(object):
338 class fakerepo(object):
339 dirstate = lfutil.openlfdirstate(ui, repo)
339 dirstate = lfutil.openlfdirstate(ui, repo)
340 orig(ui, fakerepo, *pats, **opts)
340 orig(ui, fakerepo, *pats, **opts)
341 else:
341 else:
342 orig(ui, repo, *pats, **opts)
342 orig(ui, repo, *pats, **opts)
343
343
344 # Override needs to refresh standins so that update's normal merge
344 # Override needs to refresh standins so that update's normal merge
345 # will go through properly. Then the other update hook (overriding repo.update)
345 # will go through properly. Then the other update hook (overriding repo.update)
346 # will get the new files. Filemerge is also overridden so that the merge
346 # will get the new files. Filemerge is also overridden so that the merge
347 # will merge standins correctly.
347 # will merge standins correctly.
348 def overrideupdate(orig, ui, repo, *pats, **opts):
348 def overrideupdate(orig, ui, repo, *pats, **opts):
349 # Need to lock between the standins getting updated and their
349 # Need to lock between the standins getting updated and their
350 # largefiles getting updated
350 # largefiles getting updated
351 wlock = repo.wlock()
351 wlock = repo.wlock()
352 try:
352 try:
353 if opts['check']:
353 if opts['check']:
354 lfdirstate = lfutil.openlfdirstate(ui, repo)
354 lfdirstate = lfutil.openlfdirstate(ui, repo)
355 unsure, s = lfdirstate.status(
355 unsure, s = lfdirstate.status(
356 match_.always(repo.root, repo.getcwd()),
356 match_.always(repo.root, repo.getcwd()),
357 [], False, False, False)
357 [], False, False, False)
358
358
359 mod = len(s.modified) > 0
359 mod = len(s.modified) > 0
360 for lfile in unsure:
360 for lfile in unsure:
361 standin = lfutil.standin(lfile)
361 standin = lfutil.standin(lfile)
362 if repo['.'][standin].data().strip() != \
362 if repo['.'][standin].data().strip() != \
363 lfutil.hashfile(repo.wjoin(lfile)):
363 lfutil.hashfile(repo.wjoin(lfile)):
364 mod = True
364 mod = True
365 else:
365 else:
366 lfdirstate.normal(lfile)
366 lfdirstate.normal(lfile)
367 lfdirstate.write()
367 lfdirstate.write()
368 if mod:
368 if mod:
369 raise util.Abort(_('uncommitted changes'))
369 raise util.Abort(_('uncommitted changes'))
370 return orig(ui, repo, *pats, **opts)
370 return orig(ui, repo, *pats, **opts)
371 finally:
371 finally:
372 wlock.release()
372 wlock.release()
373
373
374 # Before starting the manifest merge, merge.updates will call
374 # Before starting the manifest merge, merge.updates will call
375 # _checkunknown to check if there are any files in the merged-in
375 # _checkunknown to check if there are any files in the merged-in
376 # changeset that collide with unknown files in the working copy.
376 # changeset that collide with unknown files in the working copy.
377 #
377 #
378 # The largefiles are seen as unknown, so this prevents us from merging
378 # The largefiles are seen as unknown, so this prevents us from merging
379 # in a file 'foo' if we already have a largefile with the same name.
379 # in a file 'foo' if we already have a largefile with the same name.
380 #
380 #
381 # The overridden function filters the unknown files by removing any
381 # The overridden function filters the unknown files by removing any
382 # largefiles. This makes the merge proceed and we can then handle this
382 # largefiles. This makes the merge proceed and we can then handle this
383 # case further in the overridden manifestmerge function below.
383 # case further in the overridden manifestmerge function below.
384 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
384 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
385 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
385 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
386 return False
386 return False
387 return origfn(repo, wctx, mctx, f)
387 return origfn(repo, wctx, mctx, f)
388
388
389 # The manifest merge handles conflicts on the manifest level. We want
389 # The manifest merge handles conflicts on the manifest level. We want
390 # to handle changes in largefile-ness of files at this level too.
390 # to handle changes in largefile-ness of files at this level too.
391 #
391 #
392 # The strategy is to run the original manifestmerge and then process
392 # The strategy is to run the original manifestmerge and then process
393 # the action list it outputs. There are two cases we need to deal with:
393 # the action list it outputs. There are two cases we need to deal with:
394 #
394 #
395 # 1. Normal file in p1, largefile in p2. Here the largefile is
395 # 1. Normal file in p1, largefile in p2. Here the largefile is
396 # detected via its standin file, which will enter the working copy
396 # detected via its standin file, which will enter the working copy
397 # with a "get" action. It is not "merge" since the standin is all
397 # with a "get" action. It is not "merge" since the standin is all
398 # Mercurial is concerned with at this level -- the link to the
398 # Mercurial is concerned with at this level -- the link to the
399 # existing normal file is not relevant here.
399 # existing normal file is not relevant here.
400 #
400 #
401 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
401 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
402 # since the largefile will be present in the working copy and
402 # since the largefile will be present in the working copy and
403 # different from the normal file in p2. Mercurial therefore
403 # different from the normal file in p2. Mercurial therefore
404 # triggers a merge action.
404 # triggers a merge action.
405 #
405 #
406 # In both cases, we prompt the user and emit new actions to either
406 # In both cases, we prompt the user and emit new actions to either
407 # remove the standin (if the normal file was kept) or to remove the
407 # remove the standin (if the normal file was kept) or to remove the
408 # normal file and get the standin (if the largefile was kept). The
408 # normal file and get the standin (if the largefile was kept). The
409 # default prompt answer is to use the largefile version since it was
409 # default prompt answer is to use the largefile version since it was
410 # presumably changed on purpose.
410 # presumably changed on purpose.
411 #
411 #
412 # Finally, the merge.applyupdates function will then take care of
412 # Finally, the merge.applyupdates function will then take care of
413 # writing the files into the working copy and lfcommands.updatelfiles
413 # writing the files into the working copy and lfcommands.updatelfiles
414 # will update the largefiles.
414 # will update the largefiles.
415 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
415 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
416 partial, acceptremote, followcopies):
416 partial, acceptremote, followcopies):
417 overwrite = force and not branchmerge
417 overwrite = force and not branchmerge
418 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
418 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
419 acceptremote, followcopies)
419 acceptremote, followcopies)
420
420
421 if overwrite:
421 if overwrite:
422 return actions
422 return actions
423
423
424 removes = set(a[0] for a in actions['r'])
424 removes = set(a[0] for a in actions['r'])
425
425
426 newglist = []
426 newglist = []
427 lfmr = [] # LargeFiles: Mark as Removed
427 lfmr = [] # LargeFiles: Mark as Removed
428 for action in actions['g']:
428 for action in actions['g']:
429 f, args, msg = action
429 f, args, msg = action
430 splitstandin = f and lfutil.splitstandin(f)
430 splitstandin = f and lfutil.splitstandin(f)
431 if (splitstandin is not None and
431 if (splitstandin is not None and
432 splitstandin in p1 and splitstandin not in removes):
432 splitstandin in p1 and splitstandin not in removes):
433 # Case 1: normal file in the working copy, largefile in
433 # Case 1: normal file in the working copy, largefile in
434 # the second parent
434 # the second parent
435 lfile = splitstandin
435 lfile = splitstandin
436 standin = f
436 standin = f
437 msg = _('remote turned local normal file %s into a largefile\n'
437 msg = _('remote turned local normal file %s into a largefile\n'
438 'use (l)argefile or keep (n)ormal file?'
438 'use (l)argefile or keep (n)ormal file?'
439 '$$ &Largefile $$ &Normal file') % lfile
439 '$$ &Largefile $$ &Normal file') % lfile
440 if repo.ui.promptchoice(msg, 0) == 0:
440 if repo.ui.promptchoice(msg, 0) == 0:
441 actions['r'].append((lfile, None, msg))
441 actions['r'].append((lfile, None, msg))
442 newglist.append((standin, (p2.flags(standin),), msg))
442 newglist.append((standin, (p2.flags(standin),), msg))
443 else:
443 else:
444 actions['r'].append((standin, None, msg))
444 actions['r'].append((standin, None, msg))
445 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
445 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
446 # Case 2: largefile in the working copy, normal file in
446 # Case 2: largefile in the working copy, normal file in
447 # the second parent
447 # the second parent
448 standin = lfutil.standin(f)
448 standin = lfutil.standin(f)
449 lfile = f
449 lfile = f
450 msg = _('remote turned local largefile %s into a normal file\n'
450 msg = _('remote turned local largefile %s into a normal file\n'
451 'keep (l)argefile or use (n)ormal file?'
451 'keep (l)argefile or use (n)ormal file?'
452 '$$ &Largefile $$ &Normal file') % lfile
452 '$$ &Largefile $$ &Normal file') % lfile
453 if repo.ui.promptchoice(msg, 0) == 0:
453 if repo.ui.promptchoice(msg, 0) == 0:
454 if branchmerge:
454 if branchmerge:
455 # largefile can be restored from standin safely
455 # largefile can be restored from standin safely
456 actions['r'].append((lfile, None, msg))
456 actions['r'].append((lfile, None, msg))
457 else:
457 else:
458 # "lfile" should be marked as "removed" without
458 # "lfile" should be marked as "removed" without
459 # removal of itself
459 # removal of itself
460 lfmr.append((lfile, None, msg))
460 lfmr.append((lfile, None, msg))
461
461
462 # linear-merge should treat this largefile as 're-added'
462 # linear-merge should treat this largefile as 're-added'
463 actions['a'].append((standin, None, msg))
463 actions['a'].append((standin, None, msg))
464 else:
464 else:
465 actions['r'].append((standin, None, msg))
465 actions['r'].append((standin, None, msg))
466 newglist.append((lfile, (p2.flags(lfile),), msg))
466 newglist.append((lfile, (p2.flags(lfile),), msg))
467 else:
467 else:
468 newglist.append(action)
468 newglist.append(action)
469
469
470 newglist.sort()
470 newglist.sort()
471 actions['g'] = newglist
471 actions['g'] = newglist
472 if lfmr:
472 if lfmr:
473 lfmr.sort()
473 lfmr.sort()
474 actions['lfmr'] = lfmr
474 actions['lfmr'] = lfmr
475
475
476 return actions
476 return actions
477
477
478 def mergerecordupdates(orig, repo, actions, branchmerge):
478 def mergerecordupdates(orig, repo, actions, branchmerge):
479 if 'lfmr' in actions:
479 if 'lfmr' in actions:
480 # this should be executed before 'orig', to execute 'remove'
480 # this should be executed before 'orig', to execute 'remove'
481 # before all other actions
481 # before all other actions
482 for lfile, args, msg in actions['lfmr']:
482 for lfile, args, msg in actions['lfmr']:
483 repo.dirstate.remove(lfile)
483 repo.dirstate.remove(lfile)
484
484
485 return orig(repo, actions, branchmerge)
485 return orig(repo, actions, branchmerge)
486
486
487
487
488 # Override filemerge to prompt the user about how they wish to merge
488 # Override filemerge to prompt the user about how they wish to merge
489 # largefiles. This will handle identical edits without prompting the user.
489 # largefiles. This will handle identical edits without prompting the user.
490 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
490 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
491 if not lfutil.isstandin(orig):
491 if not lfutil.isstandin(orig):
492 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
492 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
493
493
494 ahash = fca.data().strip().lower()
494 ahash = fca.data().strip().lower()
495 dhash = fcd.data().strip().lower()
495 dhash = fcd.data().strip().lower()
496 ohash = fco.data().strip().lower()
496 ohash = fco.data().strip().lower()
497 if (ohash != ahash and
497 if (ohash != ahash and
498 ohash != dhash and
498 ohash != dhash and
499 (dhash == ahash or
499 (dhash == ahash or
500 repo.ui.promptchoice(
500 repo.ui.promptchoice(
501 _('largefile %s has a merge conflict\nancestor was %s\n'
501 _('largefile %s has a merge conflict\nancestor was %s\n'
502 'keep (l)ocal %s or\ntake (o)ther %s?'
502 'keep (l)ocal %s or\ntake (o)ther %s?'
503 '$$ &Local $$ &Other') %
503 '$$ &Local $$ &Other') %
504 (lfutil.splitstandin(orig), ahash, dhash, ohash),
504 (lfutil.splitstandin(orig), ahash, dhash, ohash),
505 0) == 1)):
505 0) == 1)):
506 repo.wwrite(fcd.path(), fco.data(), fco.flags())
506 repo.wwrite(fcd.path(), fco.data(), fco.flags())
507 return 0
507 return 0
508
508
509 # Copy first changes the matchers to match standins instead of
509 # Copy first changes the matchers to match standins instead of
510 # largefiles. Then it overrides util.copyfile in that function it
510 # largefiles. Then it overrides util.copyfile in that function it
511 # checks if the destination largefile already exists. It also keeps a
511 # checks if the destination largefile already exists. It also keeps a
512 # list of copied files so that the largefiles can be copied and the
512 # list of copied files so that the largefiles can be copied and the
513 # dirstate updated.
513 # dirstate updated.
514 def overridecopy(orig, ui, repo, pats, opts, rename=False):
514 def overridecopy(orig, ui, repo, pats, opts, rename=False):
515 # doesn't remove largefile on rename
515 # doesn't remove largefile on rename
516 if len(pats) < 2:
516 if len(pats) < 2:
517 # this isn't legal, let the original function deal with it
517 # this isn't legal, let the original function deal with it
518 return orig(ui, repo, pats, opts, rename)
518 return orig(ui, repo, pats, opts, rename)
519
519
520 def makestandin(relpath):
520 def makestandin(relpath):
521 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
521 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
522 return os.path.join(repo.wjoin(lfutil.standin(path)))
522 return os.path.join(repo.wjoin(lfutil.standin(path)))
523
523
524 fullpats = scmutil.expandpats(pats)
524 fullpats = scmutil.expandpats(pats)
525 dest = fullpats[-1]
525 dest = fullpats[-1]
526
526
527 if os.path.isdir(dest):
527 if os.path.isdir(dest):
528 if not os.path.isdir(makestandin(dest)):
528 if not os.path.isdir(makestandin(dest)):
529 os.makedirs(makestandin(dest))
529 os.makedirs(makestandin(dest))
530 # This could copy both lfiles and normal files in one command,
530 # This could copy both lfiles and normal files in one command,
531 # but we don't want to do that. First replace their matcher to
531 # but we don't want to do that. First replace their matcher to
532 # only match normal files and run it, then replace it to just
532 # only match normal files and run it, then replace it to just
533 # match largefiles and run it again.
533 # match largefiles and run it again.
534 nonormalfiles = False
534 nonormalfiles = False
535 nolfiles = False
535 nolfiles = False
536 installnormalfilesmatchfn(repo[None].manifest())
536 installnormalfilesmatchfn(repo[None].manifest())
537 try:
537 try:
538 try:
538 try:
539 result = orig(ui, repo, pats, opts, rename)
539 result = orig(ui, repo, pats, opts, rename)
540 except util.Abort, e:
540 except util.Abort, e:
541 if str(e) != _('no files to copy'):
541 if str(e) != _('no files to copy'):
542 raise e
542 raise e
543 else:
543 else:
544 nonormalfiles = True
544 nonormalfiles = True
545 result = 0
545 result = 0
546 finally:
546 finally:
547 restorematchfn()
547 restorematchfn()
548
548
549 # The first rename can cause our current working directory to be removed.
549 # The first rename can cause our current working directory to be removed.
550 # In that case there is nothing left to copy/rename so just quit.
550 # In that case there is nothing left to copy/rename so just quit.
551 try:
551 try:
552 repo.getcwd()
552 repo.getcwd()
553 except OSError:
553 except OSError:
554 return result
554 return result
555
555
556 try:
556 try:
557 try:
557 try:
558 # When we call orig below it creates the standins but we don't add
558 # When we call orig below it creates the standins but we don't add
559 # them to the dir state until later so lock during that time.
559 # them to the dir state until later so lock during that time.
560 wlock = repo.wlock()
560 wlock = repo.wlock()
561
561
562 manifest = repo[None].manifest()
562 manifest = repo[None].manifest()
563 def overridematch(ctx, pats=[], opts={}, globbed=False,
563 def overridematch(ctx, pats=[], opts={}, globbed=False,
564 default='relpath'):
564 default='relpath'):
565 newpats = []
565 newpats = []
566 # The patterns were previously mangled to add the standin
566 # The patterns were previously mangled to add the standin
567 # directory; we need to remove that now
567 # directory; we need to remove that now
568 for pat in pats:
568 for pat in pats:
569 if match_.patkind(pat) is None and lfutil.shortname in pat:
569 if match_.patkind(pat) is None and lfutil.shortname in pat:
570 newpats.append(pat.replace(lfutil.shortname, ''))
570 newpats.append(pat.replace(lfutil.shortname, ''))
571 else:
571 else:
572 newpats.append(pat)
572 newpats.append(pat)
573 match = oldmatch(ctx, newpats, opts, globbed, default)
573 match = oldmatch(ctx, newpats, opts, globbed, default)
574 m = copy.copy(match)
574 m = copy.copy(match)
575 lfile = lambda f: lfutil.standin(f) in manifest
575 lfile = lambda f: lfutil.standin(f) in manifest
576 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
576 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
577 m._fmap = set(m._files)
577 m._fmap = set(m._files)
578 origmatchfn = m.matchfn
578 origmatchfn = m.matchfn
579 m.matchfn = lambda f: (lfutil.isstandin(f) and
579 m.matchfn = lambda f: (lfutil.isstandin(f) and
580 (f in manifest) and
580 (f in manifest) and
581 origmatchfn(lfutil.splitstandin(f)) or
581 origmatchfn(lfutil.splitstandin(f)) or
582 None)
582 None)
583 return m
583 return m
584 oldmatch = installmatchfn(overridematch)
584 oldmatch = installmatchfn(overridematch)
585 listpats = []
585 listpats = []
586 for pat in pats:
586 for pat in pats:
587 if match_.patkind(pat) is not None:
587 if match_.patkind(pat) is not None:
588 listpats.append(pat)
588 listpats.append(pat)
589 else:
589 else:
590 listpats.append(makestandin(pat))
590 listpats.append(makestandin(pat))
591
591
592 try:
592 try:
593 origcopyfile = util.copyfile
593 origcopyfile = util.copyfile
594 copiedfiles = []
594 copiedfiles = []
595 def overridecopyfile(src, dest):
595 def overridecopyfile(src, dest):
596 if (lfutil.shortname in src and
596 if (lfutil.shortname in src and
597 dest.startswith(repo.wjoin(lfutil.shortname))):
597 dest.startswith(repo.wjoin(lfutil.shortname))):
598 destlfile = dest.replace(lfutil.shortname, '')
598 destlfile = dest.replace(lfutil.shortname, '')
599 if not opts['force'] and os.path.exists(destlfile):
599 if not opts['force'] and os.path.exists(destlfile):
600 raise IOError('',
600 raise IOError('',
601 _('destination largefile already exists'))
601 _('destination largefile already exists'))
602 copiedfiles.append((src, dest))
602 copiedfiles.append((src, dest))
603 origcopyfile(src, dest)
603 origcopyfile(src, dest)
604
604
605 util.copyfile = overridecopyfile
605 util.copyfile = overridecopyfile
606 result += orig(ui, repo, listpats, opts, rename)
606 result += orig(ui, repo, listpats, opts, rename)
607 finally:
607 finally:
608 util.copyfile = origcopyfile
608 util.copyfile = origcopyfile
609
609
610 lfdirstate = lfutil.openlfdirstate(ui, repo)
610 lfdirstate = lfutil.openlfdirstate(ui, repo)
611 for (src, dest) in copiedfiles:
611 for (src, dest) in copiedfiles:
612 if (lfutil.shortname in src and
612 if (lfutil.shortname in src and
613 dest.startswith(repo.wjoin(lfutil.shortname))):
613 dest.startswith(repo.wjoin(lfutil.shortname))):
614 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
614 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
615 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
615 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
616 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
616 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
617 if not os.path.isdir(destlfiledir):
617 if not os.path.isdir(destlfiledir):
618 os.makedirs(destlfiledir)
618 os.makedirs(destlfiledir)
619 if rename:
619 if rename:
620 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
620 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
621
621
622 # The file is gone, but this deletes any empty parent
622 # The file is gone, but this deletes any empty parent
623 # directories as a side-effect.
623 # directories as a side-effect.
624 util.unlinkpath(repo.wjoin(srclfile), True)
624 util.unlinkpath(repo.wjoin(srclfile), True)
625 lfdirstate.remove(srclfile)
625 lfdirstate.remove(srclfile)
626 else:
626 else:
627 util.copyfile(repo.wjoin(srclfile),
627 util.copyfile(repo.wjoin(srclfile),
628 repo.wjoin(destlfile))
628 repo.wjoin(destlfile))
629
629
630 lfdirstate.add(destlfile)
630 lfdirstate.add(destlfile)
631 lfdirstate.write()
631 lfdirstate.write()
632 except util.Abort, e:
632 except util.Abort, e:
633 if str(e) != _('no files to copy'):
633 if str(e) != _('no files to copy'):
634 raise e
634 raise e
635 else:
635 else:
636 nolfiles = True
636 nolfiles = True
637 finally:
637 finally:
638 restorematchfn()
638 restorematchfn()
639 wlock.release()
639 wlock.release()
640
640
641 if nolfiles and nonormalfiles:
641 if nolfiles and nonormalfiles:
642 raise util.Abort(_('no files to copy'))
642 raise util.Abort(_('no files to copy'))
643
643
644 return result
644 return result
645
645
646 # When the user calls revert, we have to be careful to not revert any
646 # When the user calls revert, we have to be careful to not revert any
647 # changes to other largefiles accidentally. This means we have to keep
647 # changes to other largefiles accidentally. This means we have to keep
648 # track of the largefiles that are being reverted so we only pull down
648 # track of the largefiles that are being reverted so we only pull down
649 # the necessary largefiles.
649 # the necessary largefiles.
650 #
650 #
651 # Standins are only updated (to match the hash of largefiles) before
651 # Standins are only updated (to match the hash of largefiles) before
652 # commits. Update the standins then run the original revert, changing
652 # commits. Update the standins then run the original revert, changing
653 # the matcher to hit standins instead of largefiles. Based on the
653 # the matcher to hit standins instead of largefiles. Based on the
654 # resulting standins update the largefiles.
654 # resulting standins update the largefiles.
655 def overriderevert(orig, ui, repo, *pats, **opts):
655 def overriderevert(orig, ui, repo, *pats, **opts):
656 # Because we put the standins in a bad state (by updating them)
656 # Because we put the standins in a bad state (by updating them)
657 # and then return them to a correct state we need to lock to
657 # and then return them to a correct state we need to lock to
658 # prevent others from changing them in their incorrect state.
658 # prevent others from changing them in their incorrect state.
659 wlock = repo.wlock()
659 wlock = repo.wlock()
660 try:
660 try:
661 lfdirstate = lfutil.openlfdirstate(ui, repo)
661 lfdirstate = lfutil.openlfdirstate(ui, repo)
662 s = lfutil.lfdirstatestatus(lfdirstate, repo)
662 s = lfutil.lfdirstatestatus(lfdirstate, repo)
663 lfdirstate.write()
663 lfdirstate.write()
664 for lfile in s.modified:
664 for lfile in s.modified:
665 lfutil.updatestandin(repo, lfutil.standin(lfile))
665 lfutil.updatestandin(repo, lfutil.standin(lfile))
666 for lfile in s.deleted:
666 for lfile in s.deleted:
667 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
667 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
668 os.unlink(repo.wjoin(lfutil.standin(lfile)))
668 os.unlink(repo.wjoin(lfutil.standin(lfile)))
669
669
670 oldstandins = lfutil.getstandinsstate(repo)
670 oldstandins = lfutil.getstandinsstate(repo)
671
671
672 def overridematch(ctx, pats=[], opts={}, globbed=False,
672 def overridematch(ctx, pats=[], opts={}, globbed=False,
673 default='relpath'):
673 default='relpath'):
674 match = oldmatch(ctx, pats, opts, globbed, default)
674 match = oldmatch(ctx, pats, opts, globbed, default)
675 m = copy.copy(match)
675 m = copy.copy(match)
676 def tostandin(f):
676 def tostandin(f):
677 if lfutil.standin(f) in ctx:
677 if lfutil.standin(f) in ctx:
678 return lfutil.standin(f)
678 return lfutil.standin(f)
679 elif lfutil.standin(f) in repo[None]:
679 elif lfutil.standin(f) in repo[None]:
680 return None
680 return None
681 return f
681 return f
682 m._files = [tostandin(f) for f in m._files]
682 m._files = [tostandin(f) for f in m._files]
683 m._files = [f for f in m._files if f is not None]
683 m._files = [f for f in m._files if f is not None]
684 m._fmap = set(m._files)
684 m._fmap = set(m._files)
685 origmatchfn = m.matchfn
685 origmatchfn = m.matchfn
686 def matchfn(f):
686 def matchfn(f):
687 if lfutil.isstandin(f):
687 if lfutil.isstandin(f):
688 return (origmatchfn(lfutil.splitstandin(f)) and
688 return (origmatchfn(lfutil.splitstandin(f)) and
689 (f in repo[None] or f in ctx))
689 (f in repo[None] or f in ctx))
690 return origmatchfn(f)
690 return origmatchfn(f)
691 m.matchfn = matchfn
691 m.matchfn = matchfn
692 return m
692 return m
693 oldmatch = installmatchfn(overridematch)
693 oldmatch = installmatchfn(overridematch)
694 try:
694 try:
695 orig(ui, repo, *pats, **opts)
695 orig(ui, repo, *pats, **opts)
696 finally:
696 finally:
697 restorematchfn()
697 restorematchfn()
698
698
699 newstandins = lfutil.getstandinsstate(repo)
699 newstandins = lfutil.getstandinsstate(repo)
700 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
700 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
701 # lfdirstate should be 'normallookup'-ed for updated files,
701 # lfdirstate should be 'normallookup'-ed for updated files,
702 # because reverting doesn't touch dirstate for 'normal' files
702 # because reverting doesn't touch dirstate for 'normal' files
703 # when target revision is explicitly specified: in such case,
703 # when target revision is explicitly specified: in such case,
704 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
704 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
705 # of target (standin) file.
705 # of target (standin) file.
706 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
706 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
707 normallookup=True)
707 normallookup=True)
708
708
709 finally:
709 finally:
710 wlock.release()
710 wlock.release()
711
711
712 # after pulling changesets, we need to take some extra care to get
712 # after pulling changesets, we need to take some extra care to get
713 # largefiles updated remotely
713 # largefiles updated remotely
714 def overridepull(orig, ui, repo, source=None, **opts):
714 def overridepull(orig, ui, repo, source=None, **opts):
715 revsprepull = len(repo)
715 revsprepull = len(repo)
716 if not source:
716 if not source:
717 source = 'default'
717 source = 'default'
718 repo.lfpullsource = source
718 repo.lfpullsource = source
719 result = orig(ui, repo, source, **opts)
719 result = orig(ui, repo, source, **opts)
720 revspostpull = len(repo)
720 revspostpull = len(repo)
721 lfrevs = opts.get('lfrev', [])
721 lfrevs = opts.get('lfrev', [])
722 if opts.get('all_largefiles'):
722 if opts.get('all_largefiles'):
723 lfrevs.append('pulled()')
723 lfrevs.append('pulled()')
724 if lfrevs and revspostpull > revsprepull:
724 if lfrevs and revspostpull > revsprepull:
725 numcached = 0
725 numcached = 0
726 repo.firstpulled = revsprepull # for pulled() revset expression
726 repo.firstpulled = revsprepull # for pulled() revset expression
727 try:
727 try:
728 for rev in scmutil.revrange(repo, lfrevs):
728 for rev in scmutil.revrange(repo, lfrevs):
729 ui.note(_('pulling largefiles for revision %s\n') % rev)
729 ui.note(_('pulling largefiles for revision %s\n') % rev)
730 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
730 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
731 numcached += len(cached)
731 numcached += len(cached)
732 finally:
732 finally:
733 del repo.firstpulled
733 del repo.firstpulled
734 ui.status(_("%d largefiles cached\n") % numcached)
734 ui.status(_("%d largefiles cached\n") % numcached)
735 return result
735 return result
736
736
737 def pulledrevsetsymbol(repo, subset, x):
737 def pulledrevsetsymbol(repo, subset, x):
738 """``pulled()``
738 """``pulled()``
739 Changesets that just has been pulled.
739 Changesets that just has been pulled.
740
740
741 Only available with largefiles from pull --lfrev expressions.
741 Only available with largefiles from pull --lfrev expressions.
742
742
743 .. container:: verbose
743 .. container:: verbose
744
744
745 Some examples:
745 Some examples:
746
746
747 - pull largefiles for all new changesets::
747 - pull largefiles for all new changesets::
748
748
749 hg pull -lfrev "pulled()"
749 hg pull -lfrev "pulled()"
750
750
751 - pull largefiles for all new branch heads::
751 - pull largefiles for all new branch heads::
752
752
753 hg pull -lfrev "head(pulled()) and not closed()"
753 hg pull -lfrev "head(pulled()) and not closed()"
754
754
755 """
755 """
756
756
757 try:
757 try:
758 firstpulled = repo.firstpulled
758 firstpulled = repo.firstpulled
759 except AttributeError:
759 except AttributeError:
760 raise util.Abort(_("pulled() only available in --lfrev"))
760 raise util.Abort(_("pulled() only available in --lfrev"))
761 return revset.baseset([r for r in subset if r >= firstpulled])
761 return revset.baseset([r for r in subset if r >= firstpulled])
762
762
763 def overrideclone(orig, ui, source, dest=None, **opts):
763 def overrideclone(orig, ui, source, dest=None, **opts):
764 d = dest
764 d = dest
765 if d is None:
765 if d is None:
766 d = hg.defaultdest(source)
766 d = hg.defaultdest(source)
767 if opts.get('all_largefiles') and not hg.islocal(d):
767 if opts.get('all_largefiles') and not hg.islocal(d):
768 raise util.Abort(_(
768 raise util.Abort(_(
769 '--all-largefiles is incompatible with non-local destination %s') %
769 '--all-largefiles is incompatible with non-local destination %s') %
770 d)
770 d)
771
771
772 return orig(ui, source, dest, **opts)
772 return orig(ui, source, dest, **opts)
773
773
774 def hgclone(orig, ui, opts, *args, **kwargs):
774 def hgclone(orig, ui, opts, *args, **kwargs):
775 result = orig(ui, opts, *args, **kwargs)
775 result = orig(ui, opts, *args, **kwargs)
776
776
777 if result is not None:
777 if result is not None:
778 sourcerepo, destrepo = result
778 sourcerepo, destrepo = result
779 repo = destrepo.local()
779 repo = destrepo.local()
780
780
781 # Caching is implicitly limited to 'rev' option, since the dest repo was
781 # Caching is implicitly limited to 'rev' option, since the dest repo was
782 # truncated at that point. The user may expect a download count with
782 # truncated at that point. The user may expect a download count with
783 # this option, so attempt whether or not this is a largefile repo.
783 # this option, so attempt whether or not this is a largefile repo.
784 if opts.get('all_largefiles'):
784 if opts.get('all_largefiles'):
785 success, missing = lfcommands.downloadlfiles(ui, repo, None)
785 success, missing = lfcommands.downloadlfiles(ui, repo, None)
786
786
787 if missing != 0:
787 if missing != 0:
788 return None
788 return None
789
789
790 return result
790 return result
791
791
792 def overriderebase(orig, ui, repo, **opts):
792 def overriderebase(orig, ui, repo, **opts):
793 resuming = opts.get('continue')
794 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
793 repo._isrebasing = True
795 repo._isrebasing = True
794 try:
796 try:
795 return orig(ui, repo, **opts)
797 return orig(ui, repo, **opts)
796 finally:
798 finally:
797 repo._isrebasing = False
799 repo._isrebasing = False
800 repo._lfcommithooks.pop()
798
801
799 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
802 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
800 prefix=None, mtime=None, subrepos=None):
803 prefix=None, mtime=None, subrepos=None):
801 # No need to lock because we are only reading history and
804 # No need to lock because we are only reading history and
802 # largefile caches, neither of which are modified.
805 # largefile caches, neither of which are modified.
803 lfcommands.cachelfiles(repo.ui, repo, node)
806 lfcommands.cachelfiles(repo.ui, repo, node)
804
807
805 if kind not in archival.archivers:
808 if kind not in archival.archivers:
806 raise util.Abort(_("unknown archive type '%s'") % kind)
809 raise util.Abort(_("unknown archive type '%s'") % kind)
807
810
808 ctx = repo[node]
811 ctx = repo[node]
809
812
810 if kind == 'files':
813 if kind == 'files':
811 if prefix:
814 if prefix:
812 raise util.Abort(
815 raise util.Abort(
813 _('cannot give prefix when archiving to files'))
816 _('cannot give prefix when archiving to files'))
814 else:
817 else:
815 prefix = archival.tidyprefix(dest, kind, prefix)
818 prefix = archival.tidyprefix(dest, kind, prefix)
816
819
817 def write(name, mode, islink, getdata):
820 def write(name, mode, islink, getdata):
818 if matchfn and not matchfn(name):
821 if matchfn and not matchfn(name):
819 return
822 return
820 data = getdata()
823 data = getdata()
821 if decode:
824 if decode:
822 data = repo.wwritedata(name, data)
825 data = repo.wwritedata(name, data)
823 archiver.addfile(prefix + name, mode, islink, data)
826 archiver.addfile(prefix + name, mode, islink, data)
824
827
825 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
828 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
826
829
827 if repo.ui.configbool("ui", "archivemeta", True):
830 if repo.ui.configbool("ui", "archivemeta", True):
828 def metadata():
831 def metadata():
829 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
832 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
830 hex(repo.changelog.node(0)), hex(node), ctx.branch())
833 hex(repo.changelog.node(0)), hex(node), ctx.branch())
831
834
832 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
835 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
833 if repo.tagtype(t) == 'global')
836 if repo.tagtype(t) == 'global')
834 if not tags:
837 if not tags:
835 repo.ui.pushbuffer()
838 repo.ui.pushbuffer()
836 opts = {'template': '{latesttag}\n{latesttagdistance}',
839 opts = {'template': '{latesttag}\n{latesttagdistance}',
837 'style': '', 'patch': None, 'git': None}
840 'style': '', 'patch': None, 'git': None}
838 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
841 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
839 ltags, dist = repo.ui.popbuffer().split('\n')
842 ltags, dist = repo.ui.popbuffer().split('\n')
840 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
843 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
841 tags += 'latesttagdistance: %s\n' % dist
844 tags += 'latesttagdistance: %s\n' % dist
842
845
843 return base + tags
846 return base + tags
844
847
845 write('.hg_archival.txt', 0644, False, metadata)
848 write('.hg_archival.txt', 0644, False, metadata)
846
849
847 for f in ctx:
850 for f in ctx:
848 ff = ctx.flags(f)
851 ff = ctx.flags(f)
849 getdata = ctx[f].data
852 getdata = ctx[f].data
850 if lfutil.isstandin(f):
853 if lfutil.isstandin(f):
851 path = lfutil.findfile(repo, getdata().strip())
854 path = lfutil.findfile(repo, getdata().strip())
852 if path is None:
855 if path is None:
853 raise util.Abort(
856 raise util.Abort(
854 _('largefile %s not found in repo store or system cache')
857 _('largefile %s not found in repo store or system cache')
855 % lfutil.splitstandin(f))
858 % lfutil.splitstandin(f))
856 f = lfutil.splitstandin(f)
859 f = lfutil.splitstandin(f)
857
860
858 def getdatafn():
861 def getdatafn():
859 fd = None
862 fd = None
860 try:
863 try:
861 fd = open(path, 'rb')
864 fd = open(path, 'rb')
862 return fd.read()
865 return fd.read()
863 finally:
866 finally:
864 if fd:
867 if fd:
865 fd.close()
868 fd.close()
866
869
867 getdata = getdatafn
870 getdata = getdatafn
868 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
871 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
869
872
870 if subrepos:
873 if subrepos:
871 for subpath in sorted(ctx.substate):
874 for subpath in sorted(ctx.substate):
872 sub = ctx.sub(subpath)
875 sub = ctx.sub(subpath)
873 submatch = match_.narrowmatcher(subpath, matchfn)
876 submatch = match_.narrowmatcher(subpath, matchfn)
874 sub.archive(repo.ui, archiver, prefix, submatch)
877 sub.archive(repo.ui, archiver, prefix, submatch)
875
878
876 archiver.done()
879 archiver.done()
877
880
878 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
881 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
879 repo._get(repo._state + ('hg',))
882 repo._get(repo._state + ('hg',))
880 rev = repo._state[1]
883 rev = repo._state[1]
881 ctx = repo._repo[rev]
884 ctx = repo._repo[rev]
882
885
883 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
886 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
884
887
885 def write(name, mode, islink, getdata):
888 def write(name, mode, islink, getdata):
886 # At this point, the standin has been replaced with the largefile name,
889 # At this point, the standin has been replaced with the largefile name,
887 # so the normal matcher works here without the lfutil variants.
890 # so the normal matcher works here without the lfutil variants.
888 if match and not match(f):
891 if match and not match(f):
889 return
892 return
890 data = getdata()
893 data = getdata()
891
894
892 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
895 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
893
896
894 for f in ctx:
897 for f in ctx:
895 ff = ctx.flags(f)
898 ff = ctx.flags(f)
896 getdata = ctx[f].data
899 getdata = ctx[f].data
897 if lfutil.isstandin(f):
900 if lfutil.isstandin(f):
898 path = lfutil.findfile(repo._repo, getdata().strip())
901 path = lfutil.findfile(repo._repo, getdata().strip())
899 if path is None:
902 if path is None:
900 raise util.Abort(
903 raise util.Abort(
901 _('largefile %s not found in repo store or system cache')
904 _('largefile %s not found in repo store or system cache')
902 % lfutil.splitstandin(f))
905 % lfutil.splitstandin(f))
903 f = lfutil.splitstandin(f)
906 f = lfutil.splitstandin(f)
904
907
905 def getdatafn():
908 def getdatafn():
906 fd = None
909 fd = None
907 try:
910 try:
908 fd = open(os.path.join(prefix, path), 'rb')
911 fd = open(os.path.join(prefix, path), 'rb')
909 return fd.read()
912 return fd.read()
910 finally:
913 finally:
911 if fd:
914 if fd:
912 fd.close()
915 fd.close()
913
916
914 getdata = getdatafn
917 getdata = getdatafn
915
918
916 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
919 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
917
920
918 for subpath in sorted(ctx.substate):
921 for subpath in sorted(ctx.substate):
919 sub = ctx.sub(subpath)
922 sub = ctx.sub(subpath)
920 submatch = match_.narrowmatcher(subpath, match)
923 submatch = match_.narrowmatcher(subpath, match)
921 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
924 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
922 submatch)
925 submatch)
923
926
924 # If a largefile is modified, the change is not reflected in its
927 # If a largefile is modified, the change is not reflected in its
925 # standin until a commit. cmdutil.bailifchanged() raises an exception
928 # standin until a commit. cmdutil.bailifchanged() raises an exception
926 # if the repo has uncommitted changes. Wrap it to also check if
929 # if the repo has uncommitted changes. Wrap it to also check if
927 # largefiles were changed. This is used by bisect and backout.
930 # largefiles were changed. This is used by bisect and backout.
928 def overridebailifchanged(orig, repo):
931 def overridebailifchanged(orig, repo):
929 orig(repo)
932 orig(repo)
930 repo.lfstatus = True
933 repo.lfstatus = True
931 s = repo.status()
934 s = repo.status()
932 repo.lfstatus = False
935 repo.lfstatus = False
933 if s.modified or s.added or s.removed or s.deleted:
936 if s.modified or s.added or s.removed or s.deleted:
934 raise util.Abort(_('uncommitted changes'))
937 raise util.Abort(_('uncommitted changes'))
935
938
936 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
939 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
937 def overridefetch(orig, ui, repo, *pats, **opts):
940 def overridefetch(orig, ui, repo, *pats, **opts):
938 repo.lfstatus = True
941 repo.lfstatus = True
939 s = repo.status()
942 s = repo.status()
940 repo.lfstatus = False
943 repo.lfstatus = False
941 if s.modified or s.added or s.removed or s.deleted:
944 if s.modified or s.added or s.removed or s.deleted:
942 raise util.Abort(_('uncommitted changes'))
945 raise util.Abort(_('uncommitted changes'))
943 return orig(ui, repo, *pats, **opts)
946 return orig(ui, repo, *pats, **opts)
944
947
945 def overrideforget(orig, ui, repo, *pats, **opts):
948 def overrideforget(orig, ui, repo, *pats, **opts):
946 installnormalfilesmatchfn(repo[None].manifest())
949 installnormalfilesmatchfn(repo[None].manifest())
947 result = orig(ui, repo, *pats, **opts)
950 result = orig(ui, repo, *pats, **opts)
948 restorematchfn()
951 restorematchfn()
949 m = scmutil.match(repo[None], pats, opts)
952 m = scmutil.match(repo[None], pats, opts)
950
953
951 try:
954 try:
952 repo.lfstatus = True
955 repo.lfstatus = True
953 s = repo.status(match=m, clean=True)
956 s = repo.status(match=m, clean=True)
954 finally:
957 finally:
955 repo.lfstatus = False
958 repo.lfstatus = False
956 forget = sorted(s.modified + s.added + s.deleted + s.clean)
959 forget = sorted(s.modified + s.added + s.deleted + s.clean)
957 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
960 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
958
961
959 for f in forget:
962 for f in forget:
960 if lfutil.standin(f) not in repo.dirstate and not \
963 if lfutil.standin(f) not in repo.dirstate and not \
961 os.path.isdir(m.rel(lfutil.standin(f))):
964 os.path.isdir(m.rel(lfutil.standin(f))):
962 ui.warn(_('not removing %s: file is already untracked\n')
965 ui.warn(_('not removing %s: file is already untracked\n')
963 % m.rel(f))
966 % m.rel(f))
964 result = 1
967 result = 1
965
968
966 for f in forget:
969 for f in forget:
967 if ui.verbose or not m.exact(f):
970 if ui.verbose or not m.exact(f):
968 ui.status(_('removing %s\n') % m.rel(f))
971 ui.status(_('removing %s\n') % m.rel(f))
969
972
970 # Need to lock because standin files are deleted then removed from the
973 # Need to lock because standin files are deleted then removed from the
971 # repository and we could race in-between.
974 # repository and we could race in-between.
972 wlock = repo.wlock()
975 wlock = repo.wlock()
973 try:
976 try:
974 lfdirstate = lfutil.openlfdirstate(ui, repo)
977 lfdirstate = lfutil.openlfdirstate(ui, repo)
975 for f in forget:
978 for f in forget:
976 if lfdirstate[f] == 'a':
979 if lfdirstate[f] == 'a':
977 lfdirstate.drop(f)
980 lfdirstate.drop(f)
978 else:
981 else:
979 lfdirstate.remove(f)
982 lfdirstate.remove(f)
980 lfdirstate.write()
983 lfdirstate.write()
981 standins = [lfutil.standin(f) for f in forget]
984 standins = [lfutil.standin(f) for f in forget]
982 for f in standins:
985 for f in standins:
983 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
986 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
984 repo[None].forget(standins)
987 repo[None].forget(standins)
985 finally:
988 finally:
986 wlock.release()
989 wlock.release()
987
990
988 return result
991 return result
989
992
990 def _getoutgoings(repo, other, missing, addfunc):
993 def _getoutgoings(repo, other, missing, addfunc):
991 """get pairs of filename and largefile hash in outgoing revisions
994 """get pairs of filename and largefile hash in outgoing revisions
992 in 'missing'.
995 in 'missing'.
993
996
994 largefiles already existing on 'other' repository are ignored.
997 largefiles already existing on 'other' repository are ignored.
995
998
996 'addfunc' is invoked with each unique pairs of filename and
999 'addfunc' is invoked with each unique pairs of filename and
997 largefile hash value.
1000 largefile hash value.
998 """
1001 """
999 knowns = set()
1002 knowns = set()
1000 lfhashes = set()
1003 lfhashes = set()
1001 def dedup(fn, lfhash):
1004 def dedup(fn, lfhash):
1002 k = (fn, lfhash)
1005 k = (fn, lfhash)
1003 if k not in knowns:
1006 if k not in knowns:
1004 knowns.add(k)
1007 knowns.add(k)
1005 lfhashes.add(lfhash)
1008 lfhashes.add(lfhash)
1006 lfutil.getlfilestoupload(repo, missing, dedup)
1009 lfutil.getlfilestoupload(repo, missing, dedup)
1007 if lfhashes:
1010 if lfhashes:
1008 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1011 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1009 for fn, lfhash in knowns:
1012 for fn, lfhash in knowns:
1010 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1013 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1011 addfunc(fn, lfhash)
1014 addfunc(fn, lfhash)
1012
1015
1013 def outgoinghook(ui, repo, other, opts, missing):
1016 def outgoinghook(ui, repo, other, opts, missing):
1014 if opts.pop('large', None):
1017 if opts.pop('large', None):
1015 lfhashes = set()
1018 lfhashes = set()
1016 if ui.debugflag:
1019 if ui.debugflag:
1017 toupload = {}
1020 toupload = {}
1018 def addfunc(fn, lfhash):
1021 def addfunc(fn, lfhash):
1019 if fn not in toupload:
1022 if fn not in toupload:
1020 toupload[fn] = []
1023 toupload[fn] = []
1021 toupload[fn].append(lfhash)
1024 toupload[fn].append(lfhash)
1022 lfhashes.add(lfhash)
1025 lfhashes.add(lfhash)
1023 def showhashes(fn):
1026 def showhashes(fn):
1024 for lfhash in sorted(toupload[fn]):
1027 for lfhash in sorted(toupload[fn]):
1025 ui.debug(' %s\n' % (lfhash))
1028 ui.debug(' %s\n' % (lfhash))
1026 else:
1029 else:
1027 toupload = set()
1030 toupload = set()
1028 def addfunc(fn, lfhash):
1031 def addfunc(fn, lfhash):
1029 toupload.add(fn)
1032 toupload.add(fn)
1030 lfhashes.add(lfhash)
1033 lfhashes.add(lfhash)
1031 def showhashes(fn):
1034 def showhashes(fn):
1032 pass
1035 pass
1033 _getoutgoings(repo, other, missing, addfunc)
1036 _getoutgoings(repo, other, missing, addfunc)
1034
1037
1035 if not toupload:
1038 if not toupload:
1036 ui.status(_('largefiles: no files to upload\n'))
1039 ui.status(_('largefiles: no files to upload\n'))
1037 else:
1040 else:
1038 ui.status(_('largefiles to upload (%d entities):\n')
1041 ui.status(_('largefiles to upload (%d entities):\n')
1039 % (len(lfhashes)))
1042 % (len(lfhashes)))
1040 for file in sorted(toupload):
1043 for file in sorted(toupload):
1041 ui.status(lfutil.splitstandin(file) + '\n')
1044 ui.status(lfutil.splitstandin(file) + '\n')
1042 showhashes(file)
1045 showhashes(file)
1043 ui.status('\n')
1046 ui.status('\n')
1044
1047
1045 def summaryremotehook(ui, repo, opts, changes):
1048 def summaryremotehook(ui, repo, opts, changes):
1046 largeopt = opts.get('large', False)
1049 largeopt = opts.get('large', False)
1047 if changes is None:
1050 if changes is None:
1048 if largeopt:
1051 if largeopt:
1049 return (False, True) # only outgoing check is needed
1052 return (False, True) # only outgoing check is needed
1050 else:
1053 else:
1051 return (False, False)
1054 return (False, False)
1052 elif largeopt:
1055 elif largeopt:
1053 url, branch, peer, outgoing = changes[1]
1056 url, branch, peer, outgoing = changes[1]
1054 if peer is None:
1057 if peer is None:
1055 # i18n: column positioning for "hg summary"
1058 # i18n: column positioning for "hg summary"
1056 ui.status(_('largefiles: (no remote repo)\n'))
1059 ui.status(_('largefiles: (no remote repo)\n'))
1057 return
1060 return
1058
1061
1059 toupload = set()
1062 toupload = set()
1060 lfhashes = set()
1063 lfhashes = set()
1061 def addfunc(fn, lfhash):
1064 def addfunc(fn, lfhash):
1062 toupload.add(fn)
1065 toupload.add(fn)
1063 lfhashes.add(lfhash)
1066 lfhashes.add(lfhash)
1064 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1067 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1065
1068
1066 if not toupload:
1069 if not toupload:
1067 # i18n: column positioning for "hg summary"
1070 # i18n: column positioning for "hg summary"
1068 ui.status(_('largefiles: (no files to upload)\n'))
1071 ui.status(_('largefiles: (no files to upload)\n'))
1069 else:
1072 else:
1070 # i18n: column positioning for "hg summary"
1073 # i18n: column positioning for "hg summary"
1071 ui.status(_('largefiles: %d entities for %d files to upload\n')
1074 ui.status(_('largefiles: %d entities for %d files to upload\n')
1072 % (len(lfhashes), len(toupload)))
1075 % (len(lfhashes), len(toupload)))
1073
1076
1074 def overridesummary(orig, ui, repo, *pats, **opts):
1077 def overridesummary(orig, ui, repo, *pats, **opts):
1075 try:
1078 try:
1076 repo.lfstatus = True
1079 repo.lfstatus = True
1077 orig(ui, repo, *pats, **opts)
1080 orig(ui, repo, *pats, **opts)
1078 finally:
1081 finally:
1079 repo.lfstatus = False
1082 repo.lfstatus = False
1080
1083
1081 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1084 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1082 similarity=None):
1085 similarity=None):
1083 if not lfutil.islfilesrepo(repo):
1086 if not lfutil.islfilesrepo(repo):
1084 return orig(repo, pats, opts, dry_run, similarity)
1087 return orig(repo, pats, opts, dry_run, similarity)
1085 # Get the list of missing largefiles so we can remove them
1088 # Get the list of missing largefiles so we can remove them
1086 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1089 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1087 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1090 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1088 False, False, False)
1091 False, False, False)
1089
1092
1090 # Call into the normal remove code, but the removing of the standin, we want
1093 # Call into the normal remove code, but the removing of the standin, we want
1091 # to have handled by original addremove. Monkey patching here makes sure
1094 # to have handled by original addremove. Monkey patching here makes sure
1092 # we don't remove the standin in the largefiles code, preventing a very
1095 # we don't remove the standin in the largefiles code, preventing a very
1093 # confused state later.
1096 # confused state later.
1094 if s.deleted:
1097 if s.deleted:
1095 m = [repo.wjoin(f) for f in s.deleted]
1098 m = [repo.wjoin(f) for f in s.deleted]
1096 removelargefiles(repo.ui, repo, True, *m, **opts)
1099 removelargefiles(repo.ui, repo, True, *m, **opts)
1097 # Call into the normal add code, and any files that *should* be added as
1100 # Call into the normal add code, and any files that *should* be added as
1098 # largefiles will be
1101 # largefiles will be
1099 addlargefiles(repo.ui, repo, *pats, **opts)
1102 addlargefiles(repo.ui, repo, *pats, **opts)
1100 # Now that we've handled largefiles, hand off to the original addremove
1103 # Now that we've handled largefiles, hand off to the original addremove
1101 # function to take care of the rest. Make sure it doesn't do anything with
1104 # function to take care of the rest. Make sure it doesn't do anything with
1102 # largefiles by installing a matcher that will ignore them.
1105 # largefiles by installing a matcher that will ignore them.
1103 installnormalfilesmatchfn(repo[None].manifest())
1106 installnormalfilesmatchfn(repo[None].manifest())
1104 result = orig(repo, pats, opts, dry_run, similarity)
1107 result = orig(repo, pats, opts, dry_run, similarity)
1105 restorematchfn()
1108 restorematchfn()
1106 return result
1109 return result
1107
1110
1108 # Calling purge with --all will cause the largefiles to be deleted.
1111 # Calling purge with --all will cause the largefiles to be deleted.
1109 # Override repo.status to prevent this from happening.
1112 # Override repo.status to prevent this from happening.
1110 def overridepurge(orig, ui, repo, *dirs, **opts):
1113 def overridepurge(orig, ui, repo, *dirs, **opts):
1111 # XXX large file status is buggy when used on repo proxy.
1114 # XXX large file status is buggy when used on repo proxy.
1112 # XXX this needs to be investigate.
1115 # XXX this needs to be investigate.
1113 repo = repo.unfiltered()
1116 repo = repo.unfiltered()
1114 oldstatus = repo.status
1117 oldstatus = repo.status
1115 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1118 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1116 clean=False, unknown=False, listsubrepos=False):
1119 clean=False, unknown=False, listsubrepos=False):
1117 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1120 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1118 listsubrepos)
1121 listsubrepos)
1119 lfdirstate = lfutil.openlfdirstate(ui, repo)
1122 lfdirstate = lfutil.openlfdirstate(ui, repo)
1120 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1123 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1121 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1124 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1122 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1125 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1123 unknown, ignored, r.clean)
1126 unknown, ignored, r.clean)
1124 repo.status = overridestatus
1127 repo.status = overridestatus
1125 orig(ui, repo, *dirs, **opts)
1128 orig(ui, repo, *dirs, **opts)
1126 repo.status = oldstatus
1129 repo.status = oldstatus
1127 def overriderollback(orig, ui, repo, **opts):
1130 def overriderollback(orig, ui, repo, **opts):
1128 wlock = repo.wlock()
1131 wlock = repo.wlock()
1129 try:
1132 try:
1130 before = repo.dirstate.parents()
1133 before = repo.dirstate.parents()
1131 orphans = set(f for f in repo.dirstate
1134 orphans = set(f for f in repo.dirstate
1132 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1135 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1133 result = orig(ui, repo, **opts)
1136 result = orig(ui, repo, **opts)
1134 after = repo.dirstate.parents()
1137 after = repo.dirstate.parents()
1135 if before == after:
1138 if before == after:
1136 return result # no need to restore standins
1139 return result # no need to restore standins
1137
1140
1138 pctx = repo['.']
1141 pctx = repo['.']
1139 for f in repo.dirstate:
1142 for f in repo.dirstate:
1140 if lfutil.isstandin(f):
1143 if lfutil.isstandin(f):
1141 orphans.discard(f)
1144 orphans.discard(f)
1142 if repo.dirstate[f] == 'r':
1145 if repo.dirstate[f] == 'r':
1143 repo.wvfs.unlinkpath(f, ignoremissing=True)
1146 repo.wvfs.unlinkpath(f, ignoremissing=True)
1144 elif f in pctx:
1147 elif f in pctx:
1145 fctx = pctx[f]
1148 fctx = pctx[f]
1146 repo.wwrite(f, fctx.data(), fctx.flags())
1149 repo.wwrite(f, fctx.data(), fctx.flags())
1147 else:
1150 else:
1148 # content of standin is not so important in 'a',
1151 # content of standin is not so important in 'a',
1149 # 'm' or 'n' (coming from the 2nd parent) cases
1152 # 'm' or 'n' (coming from the 2nd parent) cases
1150 lfutil.writestandin(repo, f, '', False)
1153 lfutil.writestandin(repo, f, '', False)
1151 for standin in orphans:
1154 for standin in orphans:
1152 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1155 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1153
1156
1154 lfdirstate = lfutil.openlfdirstate(ui, repo)
1157 lfdirstate = lfutil.openlfdirstate(ui, repo)
1155 orphans = set(lfdirstate)
1158 orphans = set(lfdirstate)
1156 lfiles = lfutil.listlfiles(repo)
1159 lfiles = lfutil.listlfiles(repo)
1157 for file in lfiles:
1160 for file in lfiles:
1158 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1161 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1159 orphans.discard(file)
1162 orphans.discard(file)
1160 for lfile in orphans:
1163 for lfile in orphans:
1161 lfdirstate.drop(lfile)
1164 lfdirstate.drop(lfile)
1162 lfdirstate.write()
1165 lfdirstate.write()
1163 finally:
1166 finally:
1164 wlock.release()
1167 wlock.release()
1165 return result
1168 return result
1166
1169
1167 def overridetransplant(orig, ui, repo, *revs, **opts):
1170 def overridetransplant(orig, ui, repo, *revs, **opts):
1168 try:
1171 try:
1169 oldstandins = lfutil.getstandinsstate(repo)
1172 oldstandins = lfutil.getstandinsstate(repo)
1170 repo._istransplanting = True
1173 repo._istransplanting = True
1171 result = orig(ui, repo, *revs, **opts)
1174 result = orig(ui, repo, *revs, **opts)
1172 newstandins = lfutil.getstandinsstate(repo)
1175 newstandins = lfutil.getstandinsstate(repo)
1173 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1176 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1174 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1177 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1175 printmessage=True)
1178 printmessage=True)
1176 finally:
1179 finally:
1177 repo._istransplanting = False
1180 repo._istransplanting = False
1178 return result
1181 return result
1179
1182
1180 def overridecat(orig, ui, repo, file1, *pats, **opts):
1183 def overridecat(orig, ui, repo, file1, *pats, **opts):
1181 ctx = scmutil.revsingle(repo, opts.get('rev'))
1184 ctx = scmutil.revsingle(repo, opts.get('rev'))
1182 err = 1
1185 err = 1
1183 notbad = set()
1186 notbad = set()
1184 m = scmutil.match(ctx, (file1,) + pats, opts)
1187 m = scmutil.match(ctx, (file1,) + pats, opts)
1185 origmatchfn = m.matchfn
1188 origmatchfn = m.matchfn
1186 def lfmatchfn(f):
1189 def lfmatchfn(f):
1187 if origmatchfn(f):
1190 if origmatchfn(f):
1188 return True
1191 return True
1189 lf = lfutil.splitstandin(f)
1192 lf = lfutil.splitstandin(f)
1190 if lf is None:
1193 if lf is None:
1191 return False
1194 return False
1192 notbad.add(lf)
1195 notbad.add(lf)
1193 return origmatchfn(lf)
1196 return origmatchfn(lf)
1194 m.matchfn = lfmatchfn
1197 m.matchfn = lfmatchfn
1195 origbadfn = m.bad
1198 origbadfn = m.bad
1196 def lfbadfn(f, msg):
1199 def lfbadfn(f, msg):
1197 if not f in notbad:
1200 if not f in notbad:
1198 origbadfn(f, msg)
1201 origbadfn(f, msg)
1199 m.bad = lfbadfn
1202 m.bad = lfbadfn
1200 for f in ctx.walk(m):
1203 for f in ctx.walk(m):
1201 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1204 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1202 pathname=f)
1205 pathname=f)
1203 lf = lfutil.splitstandin(f)
1206 lf = lfutil.splitstandin(f)
1204 if lf is None or origmatchfn(f):
1207 if lf is None or origmatchfn(f):
1205 # duplicating unreachable code from commands.cat
1208 # duplicating unreachable code from commands.cat
1206 data = ctx[f].data()
1209 data = ctx[f].data()
1207 if opts.get('decode'):
1210 if opts.get('decode'):
1208 data = repo.wwritedata(f, data)
1211 data = repo.wwritedata(f, data)
1209 fp.write(data)
1212 fp.write(data)
1210 else:
1213 else:
1211 hash = lfutil.readstandin(repo, lf, ctx.rev())
1214 hash = lfutil.readstandin(repo, lf, ctx.rev())
1212 if not lfutil.inusercache(repo.ui, hash):
1215 if not lfutil.inusercache(repo.ui, hash):
1213 store = basestore._openstore(repo)
1216 store = basestore._openstore(repo)
1214 success, missing = store.get([(lf, hash)])
1217 success, missing = store.get([(lf, hash)])
1215 if len(success) != 1:
1218 if len(success) != 1:
1216 raise util.Abort(
1219 raise util.Abort(
1217 _('largefile %s is not in cache and could not be '
1220 _('largefile %s is not in cache and could not be '
1218 'downloaded') % lf)
1221 'downloaded') % lf)
1219 path = lfutil.usercachepath(repo.ui, hash)
1222 path = lfutil.usercachepath(repo.ui, hash)
1220 fpin = open(path, "rb")
1223 fpin = open(path, "rb")
1221 for chunk in util.filechunkiter(fpin, 128 * 1024):
1224 for chunk in util.filechunkiter(fpin, 128 * 1024):
1222 fp.write(chunk)
1225 fp.write(chunk)
1223 fpin.close()
1226 fpin.close()
1224 fp.close()
1227 fp.close()
1225 err = 0
1228 err = 0
1226 return err
1229 return err
1227
1230
1228 def mercurialsinkbefore(orig, sink):
1231 def mercurialsinkbefore(orig, sink):
1229 sink.repo._isconverting = True
1232 sink.repo._isconverting = True
1230 orig(sink)
1233 orig(sink)
1231
1234
1232 def mercurialsinkafter(orig, sink):
1235 def mercurialsinkafter(orig, sink):
1233 sink.repo._isconverting = False
1236 sink.repo._isconverting = False
1234 orig(sink)
1237 orig(sink)
1235
1238
1236 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1239 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1237 *args, **kwargs):
1240 *args, **kwargs):
1238 wlock = repo.wlock()
1241 wlock = repo.wlock()
1239 try:
1242 try:
1240 # branch | | |
1243 # branch | | |
1241 # merge | force | partial | action
1244 # merge | force | partial | action
1242 # -------+-------+---------+--------------
1245 # -------+-------+---------+--------------
1243 # x | x | x | linear-merge
1246 # x | x | x | linear-merge
1244 # o | x | x | branch-merge
1247 # o | x | x | branch-merge
1245 # x | o | x | overwrite (as clean update)
1248 # x | o | x | overwrite (as clean update)
1246 # o | o | x | force-branch-merge (*1)
1249 # o | o | x | force-branch-merge (*1)
1247 # x | x | o | (*)
1250 # x | x | o | (*)
1248 # o | x | o | (*)
1251 # o | x | o | (*)
1249 # x | o | o | overwrite (as revert)
1252 # x | o | o | overwrite (as revert)
1250 # o | o | o | (*)
1253 # o | o | o | (*)
1251 #
1254 #
1252 # (*) don't care
1255 # (*) don't care
1253 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1256 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1254
1257
1255 linearmerge = not branchmerge and not force and not partial
1258 linearmerge = not branchmerge and not force and not partial
1256
1259
1257 if linearmerge or (branchmerge and force and not partial):
1260 if linearmerge or (branchmerge and force and not partial):
1258 # update standins for linear-merge or force-branch-merge,
1261 # update standins for linear-merge or force-branch-merge,
1259 # because largefiles in the working directory may be modified
1262 # because largefiles in the working directory may be modified
1260 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1263 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1261 unsure, s = lfdirstate.status(match_.always(repo.root,
1264 unsure, s = lfdirstate.status(match_.always(repo.root,
1262 repo.getcwd()),
1265 repo.getcwd()),
1263 [], False, False, False)
1266 [], False, False, False)
1264 for lfile in unsure + s.modified + s.added:
1267 for lfile in unsure + s.modified + s.added:
1265 lfutil.updatestandin(repo, lfutil.standin(lfile))
1268 lfutil.updatestandin(repo, lfutil.standin(lfile))
1266
1269
1267 if linearmerge:
1270 if linearmerge:
1268 # Only call updatelfiles on the standins that have changed
1271 # Only call updatelfiles on the standins that have changed
1269 # to save time
1272 # to save time
1270 oldstandins = lfutil.getstandinsstate(repo)
1273 oldstandins = lfutil.getstandinsstate(repo)
1271
1274
1272 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1275 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1273
1276
1274 filelist = None
1277 filelist = None
1275 if linearmerge:
1278 if linearmerge:
1276 newstandins = lfutil.getstandinsstate(repo)
1279 newstandins = lfutil.getstandinsstate(repo)
1277 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1280 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1278
1281
1279 # suppress status message while automated committing
1282 # suppress status message while automated committing
1280 printmessage = not (getattr(repo, "_isrebasing", False) or
1283 printmessage = not (getattr(repo, "_isrebasing", False) or
1281 getattr(repo, "_istransplanting", False))
1284 getattr(repo, "_istransplanting", False))
1282 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1285 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1283 printmessage=printmessage,
1286 printmessage=printmessage,
1284 normallookup=partial)
1287 normallookup=partial)
1285
1288
1286 return result
1289 return result
1287 finally:
1290 finally:
1288 wlock.release()
1291 wlock.release()
1289
1292
1290 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1293 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1291 result = orig(repo, files, *args, **kwargs)
1294 result = orig(repo, files, *args, **kwargs)
1292
1295
1293 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1296 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1294 if filelist:
1297 if filelist:
1295 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1298 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1296 printmessage=False, normallookup=True)
1299 printmessage=False, normallookup=True)
1297
1300
1298 return result
1301 return result
@@ -1,582 +1,599
1 This file focuses mainly on updating largefiles in the working
1 This file focuses mainly on updating largefiles in the working
2 directory (and ".hg/largefiles/dirstate")
2 directory (and ".hg/largefiles/dirstate")
3
3
4 $ cat >> $HGRCPATH <<EOF
4 $ cat >> $HGRCPATH <<EOF
5 > [ui]
5 > [ui]
6 > merge = internal:fail
6 > merge = internal:fail
7 > [extensions]
7 > [extensions]
8 > largefiles =
8 > largefiles =
9 > EOF
9 > EOF
10
10
11 $ hg init repo
11 $ hg init repo
12 $ cd repo
12 $ cd repo
13
13
14 $ echo large1 > large1
14 $ echo large1 > large1
15 $ echo large2 > large2
15 $ echo large2 > large2
16 $ hg add --large large1 large2
16 $ hg add --large large1 large2
17 $ echo normal1 > normal1
17 $ echo normal1 > normal1
18 $ hg add normal1
18 $ hg add normal1
19 $ hg commit -m '#0'
19 $ hg commit -m '#0'
20 $ echo 'large1 in #1' > large1
20 $ echo 'large1 in #1' > large1
21 $ echo 'normal1 in #1' > normal1
21 $ echo 'normal1 in #1' > normal1
22 $ hg commit -m '#1'
22 $ hg commit -m '#1'
23 $ hg update -q -C 0
23 $ hg update -q -C 0
24 $ echo 'large2 in #2' > large2
24 $ echo 'large2 in #2' > large2
25 $ hg commit -m '#2'
25 $ hg commit -m '#2'
26 created new head
26 created new head
27
27
28 Test that "hg merge" updates largefiles from "other" correctly
28 Test that "hg merge" updates largefiles from "other" correctly
29
29
30 (getting largefiles from "other" normally)
30 (getting largefiles from "other" normally)
31
31
32 $ hg status -A large1
32 $ hg status -A large1
33 C large1
33 C large1
34 $ cat large1
34 $ cat large1
35 large1
35 large1
36 $ cat .hglf/large1
36 $ cat .hglf/large1
37 4669e532d5b2c093a78eca010077e708a071bb64
37 4669e532d5b2c093a78eca010077e708a071bb64
38 $ hg merge --config debug.dirstate.delaywrite=2
38 $ hg merge --config debug.dirstate.delaywrite=2
39 getting changed largefiles
39 getting changed largefiles
40 1 largefiles updated, 0 removed
40 1 largefiles updated, 0 removed
41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 (branch merge, don't forget to commit)
42 (branch merge, don't forget to commit)
43 $ hg status -A large1
43 $ hg status -A large1
44 M large1
44 M large1
45 $ cat large1
45 $ cat large1
46 large1 in #1
46 large1 in #1
47 $ cat .hglf/large1
47 $ cat .hglf/large1
48 58e24f733a964da346e2407a2bee99d9001184f5
48 58e24f733a964da346e2407a2bee99d9001184f5
49 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
49 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
50 -4669e532d5b2c093a78eca010077e708a071bb64
50 -4669e532d5b2c093a78eca010077e708a071bb64
51 +58e24f733a964da346e2407a2bee99d9001184f5
51 +58e24f733a964da346e2407a2bee99d9001184f5
52
52
53 (getting largefiles from "other" via conflict prompt)
53 (getting largefiles from "other" via conflict prompt)
54
54
55 $ hg update -q -C 2
55 $ hg update -q -C 2
56 $ echo 'large1 in #3' > large1
56 $ echo 'large1 in #3' > large1
57 $ echo 'normal1 in #3' > normal1
57 $ echo 'normal1 in #3' > normal1
58 $ hg commit -m '#3'
58 $ hg commit -m '#3'
59 $ cat .hglf/large1
59 $ cat .hglf/large1
60 e5bb990443d6a92aaf7223813720f7566c9dd05b
60 e5bb990443d6a92aaf7223813720f7566c9dd05b
61 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
61 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
62 > o
62 > o
63 > EOF
63 > EOF
64 largefile large1 has a merge conflict
64 largefile large1 has a merge conflict
65 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
65 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
66 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
66 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
67 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
67 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
68 merging normal1
68 merging normal1
69 warning: conflicts during merge.
69 warning: conflicts during merge.
70 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
70 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
71 getting changed largefiles
71 getting changed largefiles
72 1 largefiles updated, 0 removed
72 1 largefiles updated, 0 removed
73 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
73 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
74 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
74 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
75 [1]
75 [1]
76 $ hg status -A large1
76 $ hg status -A large1
77 M large1
77 M large1
78 $ cat large1
78 $ cat large1
79 large1 in #1
79 large1 in #1
80 $ cat .hglf/large1
80 $ cat .hglf/large1
81 58e24f733a964da346e2407a2bee99d9001184f5
81 58e24f733a964da346e2407a2bee99d9001184f5
82
82
83 Test that "hg revert -r REV" updates largefiles from "REV" correctly
83 Test that "hg revert -r REV" updates largefiles from "REV" correctly
84
84
85 $ hg update -q -C 3
85 $ hg update -q -C 3
86 $ hg status -A large1
86 $ hg status -A large1
87 C large1
87 C large1
88 $ cat large1
88 $ cat large1
89 large1 in #3
89 large1 in #3
90 $ cat .hglf/large1
90 $ cat .hglf/large1
91 e5bb990443d6a92aaf7223813720f7566c9dd05b
91 e5bb990443d6a92aaf7223813720f7566c9dd05b
92 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
92 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
93 -4669e532d5b2c093a78eca010077e708a071bb64
93 -4669e532d5b2c093a78eca010077e708a071bb64
94 +58e24f733a964da346e2407a2bee99d9001184f5
94 +58e24f733a964da346e2407a2bee99d9001184f5
95 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
95 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
96 $ hg status -A large1
96 $ hg status -A large1
97 M large1
97 M large1
98 $ cat large1
98 $ cat large1
99 large1 in #1
99 large1 in #1
100 $ cat .hglf/large1
100 $ cat .hglf/large1
101 58e24f733a964da346e2407a2bee99d9001184f5
101 58e24f733a964da346e2407a2bee99d9001184f5
102
102
103 Test that "hg rollback" restores status of largefiles correctly
103 Test that "hg rollback" restores status of largefiles correctly
104
104
105 $ hg update -C -q
105 $ hg update -C -q
106 $ hg remove large1
106 $ hg remove large1
107 $ test -f .hglf/large1
107 $ test -f .hglf/large1
108 [1]
108 [1]
109 $ hg forget large2
109 $ hg forget large2
110 $ test -f .hglf/large2
110 $ test -f .hglf/large2
111 [1]
111 [1]
112 $ echo largeX > largeX
112 $ echo largeX > largeX
113 $ hg add --large largeX
113 $ hg add --large largeX
114 $ cat .hglf/largeX
114 $ cat .hglf/largeX
115
115
116 $ hg commit -m 'will be rollback-ed soon'
116 $ hg commit -m 'will be rollback-ed soon'
117 $ echo largeY > largeY
117 $ echo largeY > largeY
118 $ hg add --large largeY
118 $ hg add --large largeY
119 #if windows
119 #if windows
120 $ hg status -A large1
120 $ hg status -A large1
121 large1: * (glob)
121 large1: * (glob)
122 #else
122 #else
123 $ hg status -A large1
123 $ hg status -A large1
124 large1: No such file or directory
124 large1: No such file or directory
125 #endif
125 #endif
126 $ hg status -A large2
126 $ hg status -A large2
127 ? large2
127 ? large2
128 $ hg status -A largeX
128 $ hg status -A largeX
129 C largeX
129 C largeX
130 $ hg status -A largeY
130 $ hg status -A largeY
131 A largeY
131 A largeY
132 $ hg rollback
132 $ hg rollback
133 repository tip rolled back to revision 3 (undo commit)
133 repository tip rolled back to revision 3 (undo commit)
134 working directory now based on revision 3
134 working directory now based on revision 3
135 $ hg status -A large1
135 $ hg status -A large1
136 R large1
136 R large1
137 $ test -f .hglf/large1
137 $ test -f .hglf/large1
138 [1]
138 [1]
139 $ hg status -A large2
139 $ hg status -A large2
140 R large2
140 R large2
141 $ test -f .hglf/large2
141 $ test -f .hglf/large2
142 [1]
142 [1]
143 $ hg status -A largeX
143 $ hg status -A largeX
144 A largeX
144 A largeX
145 $ cat .hglf/largeX
145 $ cat .hglf/largeX
146
146
147 $ hg status -A largeY
147 $ hg status -A largeY
148 ? largeY
148 ? largeY
149 $ test -f .hglf/largeY
149 $ test -f .hglf/largeY
150 [1]
150 [1]
151
151
152 Test that "hg rollback" restores standins correctly
152 Test that "hg rollback" restores standins correctly
153
153
154 $ hg commit -m 'will be rollback-ed soon'
154 $ hg commit -m 'will be rollback-ed soon'
155 $ hg update -q -C 2
155 $ hg update -q -C 2
156 $ cat large1
156 $ cat large1
157 large1
157 large1
158 $ cat .hglf/large1
158 $ cat .hglf/large1
159 4669e532d5b2c093a78eca010077e708a071bb64
159 4669e532d5b2c093a78eca010077e708a071bb64
160 $ cat large2
160 $ cat large2
161 large2 in #2
161 large2 in #2
162 $ cat .hglf/large2
162 $ cat .hglf/large2
163 3cfce6277e7668985707b6887ce56f9f62f6ccd9
163 3cfce6277e7668985707b6887ce56f9f62f6ccd9
164
164
165 $ hg rollback -q -f
165 $ hg rollback -q -f
166 $ cat large1
166 $ cat large1
167 large1
167 large1
168 $ cat .hglf/large1
168 $ cat .hglf/large1
169 4669e532d5b2c093a78eca010077e708a071bb64
169 4669e532d5b2c093a78eca010077e708a071bb64
170 $ cat large2
170 $ cat large2
171 large2 in #2
171 large2 in #2
172 $ cat .hglf/large2
172 $ cat .hglf/large2
173 3cfce6277e7668985707b6887ce56f9f62f6ccd9
173 3cfce6277e7668985707b6887ce56f9f62f6ccd9
174
174
175 (rollback the parent of the working directory, when the parent of it
175 (rollback the parent of the working directory, when the parent of it
176 is not branch-tip)
176 is not branch-tip)
177
177
178 $ hg update -q -C 1
178 $ hg update -q -C 1
179 $ cat .hglf/large1
179 $ cat .hglf/large1
180 58e24f733a964da346e2407a2bee99d9001184f5
180 58e24f733a964da346e2407a2bee99d9001184f5
181 $ cat .hglf/large2
181 $ cat .hglf/large2
182 1deebade43c8c498a3c8daddac0244dc55d1331d
182 1deebade43c8c498a3c8daddac0244dc55d1331d
183
183
184 $ echo normalX > normalX
184 $ echo normalX > normalX
185 $ hg add normalX
185 $ hg add normalX
186 $ hg commit -m 'will be rollback-ed soon'
186 $ hg commit -m 'will be rollback-ed soon'
187 $ hg rollback -q
187 $ hg rollback -q
188
188
189 $ cat .hglf/large1
189 $ cat .hglf/large1
190 58e24f733a964da346e2407a2bee99d9001184f5
190 58e24f733a964da346e2407a2bee99d9001184f5
191 $ cat .hglf/large2
191 $ cat .hglf/large2
192 1deebade43c8c498a3c8daddac0244dc55d1331d
192 1deebade43c8c498a3c8daddac0244dc55d1331d
193
193
194 Test that "hg status" shows status of largefiles correctly just after
194 Test that "hg status" shows status of largefiles correctly just after
195 automated commit like rebase/transplant
195 automated commit like rebase/transplant
196
196
197 $ cat >> .hg/hgrc <<EOF
197 $ cat >> .hg/hgrc <<EOF
198 > [extensions]
198 > [extensions]
199 > rebase =
199 > rebase =
200 > strip =
200 > strip =
201 > transplant =
201 > transplant =
202 > EOF
202 > EOF
203 $ hg update -q -C 1
203 $ hg update -q -C 1
204 $ hg remove large1
204 $ hg remove large1
205 $ echo largeX > largeX
205 $ echo largeX > largeX
206 $ hg add --large largeX
206 $ hg add --large largeX
207 $ hg commit -m '#4'
207 $ hg commit -m '#4'
208
208
209 $ hg rebase -s 1 -d 2 --keep
209 $ hg rebase -s 1 -d 2 --keep
210 #if windows
210 #if windows
211 $ hg status -A large1
211 $ hg status -A large1
212 large1: * (glob)
212 large1: * (glob)
213 #else
213 #else
214 $ hg status -A large1
214 $ hg status -A large1
215 large1: No such file or directory
215 large1: No such file or directory
216 #endif
216 #endif
217 $ hg status -A largeX
217 $ hg status -A largeX
218 C largeX
218 C largeX
219 $ hg strip -q 5
219 $ hg strip -q 5
220
220
221 $ hg update -q -C 2
221 $ hg update -q -C 2
222 $ hg transplant -q 1 4
222 $ hg transplant -q 1 4
223 #if windows
223 #if windows
224 $ hg status -A large1
224 $ hg status -A large1
225 large1: * (glob)
225 large1: * (glob)
226 #else
226 #else
227 $ hg status -A large1
227 $ hg status -A large1
228 large1: No such file or directory
228 large1: No such file or directory
229 #endif
229 #endif
230 $ hg status -A largeX
230 $ hg status -A largeX
231 C largeX
231 C largeX
232 $ hg strip -q 5
232 $ hg strip -q 5
233
233
234 $ hg update -q -C 2
234 $ hg update -q -C 2
235 $ hg transplant -q --merge 1 --merge 4
235 $ hg transplant -q --merge 1 --merge 4
236 #if windows
236 #if windows
237 $ hg status -A large1
237 $ hg status -A large1
238 large1: * (glob)
238 large1: * (glob)
239 #else
239 #else
240 $ hg status -A large1
240 $ hg status -A large1
241 large1: No such file or directory
241 large1: No such file or directory
242 #endif
242 #endif
243 $ hg status -A largeX
243 $ hg status -A largeX
244 C largeX
244 C largeX
245 $ hg strip -q 5
245 $ hg strip -q 5
246
246
247 Test that linear merge can detect modification (and conflict) correctly
247 Test that linear merge can detect modification (and conflict) correctly
248
248
249 (linear merge without conflict)
249 (linear merge without conflict)
250
250
251 $ echo 'large2 for linear merge (no conflict)' > large2
251 $ echo 'large2 for linear merge (no conflict)' > large2
252 $ hg update 3 --config debug.dirstate.delaywrite=2
252 $ hg update 3 --config debug.dirstate.delaywrite=2
253 getting changed largefiles
253 getting changed largefiles
254 1 largefiles updated, 0 removed
254 1 largefiles updated, 0 removed
255 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 $ hg status -A large2
256 $ hg status -A large2
257 M large2
257 M large2
258 $ cat large2
258 $ cat large2
259 large2 for linear merge (no conflict)
259 large2 for linear merge (no conflict)
260 $ cat .hglf/large2
260 $ cat .hglf/large2
261 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
261 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
262
262
263 (linear merge with conflict, choosing "other")
263 (linear merge with conflict, choosing "other")
264
264
265 $ hg update -q -C 2
265 $ hg update -q -C 2
266 $ echo 'large1 for linear merge (conflict)' > large1
266 $ echo 'large1 for linear merge (conflict)' > large1
267 $ hg update 3 --config ui.interactive=True <<EOF
267 $ hg update 3 --config ui.interactive=True <<EOF
268 > o
268 > o
269 > EOF
269 > EOF
270 largefile large1 has a merge conflict
270 largefile large1 has a merge conflict
271 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
271 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
272 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
272 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
273 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
273 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
274 getting changed largefiles
274 getting changed largefiles
275 1 largefiles updated, 0 removed
275 1 largefiles updated, 0 removed
276 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
276 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
277 $ hg status -A large1
277 $ hg status -A large1
278 C large1
278 C large1
279 $ cat large1
279 $ cat large1
280 large1 in #3
280 large1 in #3
281 $ cat .hglf/large1
281 $ cat .hglf/large1
282 e5bb990443d6a92aaf7223813720f7566c9dd05b
282 e5bb990443d6a92aaf7223813720f7566c9dd05b
283
283
284 (linear merge with conflict, choosing "local")
284 (linear merge with conflict, choosing "local")
285
285
286 $ hg update -q -C 2
286 $ hg update -q -C 2
287 $ echo 'large1 for linear merge (conflict)' > large1
287 $ echo 'large1 for linear merge (conflict)' > large1
288 $ hg update 3 --config debug.dirstate.delaywrite=2
288 $ hg update 3 --config debug.dirstate.delaywrite=2
289 largefile large1 has a merge conflict
289 largefile large1 has a merge conflict
290 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
290 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
291 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
291 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
292 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
292 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
293 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
293 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
294 $ hg status -A large1
294 $ hg status -A large1
295 M large1
295 M large1
296 $ cat large1
296 $ cat large1
297 large1 for linear merge (conflict)
297 large1 for linear merge (conflict)
298 $ cat .hglf/large1
298 $ cat .hglf/large1
299 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
299 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
300
300
301 Test a linear merge to a revision containing same-name normal file
301 Test a linear merge to a revision containing same-name normal file
302
302
303 $ hg update -q -C 3
303 $ hg update -q -C 3
304 $ hg remove large2
304 $ hg remove large2
305 $ echo 'large2 as normal file' > large2
305 $ echo 'large2 as normal file' > large2
306 $ hg add large2
306 $ hg add large2
307 $ echo 'large3 as normal file' > large3
307 $ echo 'large3 as normal file' > large3
308 $ hg add large3
308 $ hg add large3
309 $ hg commit -m '#5'
309 $ hg commit -m '#5'
310 $ hg manifest
310 $ hg manifest
311 .hglf/large1
311 .hglf/large1
312 large2
312 large2
313 large3
313 large3
314 normal1
314 normal1
315
315
316 (modified largefile is already switched to normal)
316 (modified largefile is already switched to normal)
317
317
318 $ hg update -q -C 2
318 $ hg update -q -C 2
319 $ echo 'modified large2 for linear merge' > large2
319 $ echo 'modified large2 for linear merge' > large2
320 $ hg update -q 5
320 $ hg update -q 5
321 local changed .hglf/large2 which remote deleted
321 local changed .hglf/large2 which remote deleted
322 use (c)hanged version or (d)elete? c
322 use (c)hanged version or (d)elete? c
323 remote turned local largefile large2 into a normal file
323 remote turned local largefile large2 into a normal file
324 keep (l)argefile or use (n)ormal file? l
324 keep (l)argefile or use (n)ormal file? l
325 $ hg debugdirstate --nodates | grep large2
325 $ hg debugdirstate --nodates | grep large2
326 a 0 -1 .hglf/large2
326 a 0 -1 .hglf/large2
327 r 0 0 large2
327 r 0 0 large2
328 $ hg status -A large2
328 $ hg status -A large2
329 A large2
329 A large2
330 $ cat large2
330 $ cat large2
331 modified large2 for linear merge
331 modified large2 for linear merge
332
332
333 (added largefile is already committed as normal)
333 (added largefile is already committed as normal)
334
334
335 $ hg update -q -C 2
335 $ hg update -q -C 2
336 $ echo 'large3 as large file for linear merge' > large3
336 $ echo 'large3 as large file for linear merge' > large3
337 $ hg add --large large3
337 $ hg add --large large3
338 $ hg update -q 5
338 $ hg update -q 5
339 remote turned local largefile large3 into a normal file
339 remote turned local largefile large3 into a normal file
340 keep (l)argefile or use (n)ormal file? l
340 keep (l)argefile or use (n)ormal file? l
341 $ hg debugdirstate --nodates | grep large3
341 $ hg debugdirstate --nodates | grep large3
342 a 0 -1 .hglf/large3
342 a 0 -1 .hglf/large3
343 r 0 0 large3
343 r 0 0 large3
344 $ hg status -A large3
344 $ hg status -A large3
345 A large3
345 A large3
346 $ cat large3
346 $ cat large3
347 large3 as large file for linear merge
347 large3 as large file for linear merge
348 $ rm -f large3 .hglf/large3
348 $ rm -f large3 .hglf/large3
349
349
350 Test that the internal linear merging works correctly
350 Test that the internal linear merging works correctly
351 (both heads are stripped to keep pairing of revision number and commit log)
351 (both heads are stripped to keep pairing of revision number and commit log)
352
352
353 $ hg update -q -C 2
353 $ hg update -q -C 2
354 $ hg strip 3 4
354 $ hg strip 3 4
355 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-backup.hg (glob)
355 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-backup.hg (glob)
356 $ mv .hg/strip-backup/9530e27857f7-backup.hg $TESTTMP
356 $ mv .hg/strip-backup/9530e27857f7-backup.hg $TESTTMP
357
357
358 (internal linear merging at "hg pull --update")
358 (internal linear merging at "hg pull --update")
359
359
360 $ echo 'large1 for linear merge (conflict)' > large1
360 $ echo 'large1 for linear merge (conflict)' > large1
361 $ echo 'large2 for linear merge (conflict with normal file)' > large2
361 $ echo 'large2 for linear merge (conflict with normal file)' > large2
362 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
362 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
363 pulling from $TESTTMP/9530e27857f7-backup.hg (glob)
363 pulling from $TESTTMP/9530e27857f7-backup.hg (glob)
364 searching for changes
364 searching for changes
365 adding changesets
365 adding changesets
366 adding manifests
366 adding manifests
367 adding file changes
367 adding file changes
368 added 3 changesets with 5 changes to 5 files
368 added 3 changesets with 5 changes to 5 files
369 local changed .hglf/large2 which remote deleted
369 local changed .hglf/large2 which remote deleted
370 use (c)hanged version or (d)elete? c
370 use (c)hanged version or (d)elete? c
371 remote turned local largefile large2 into a normal file
371 remote turned local largefile large2 into a normal file
372 keep (l)argefile or use (n)ormal file? l
372 keep (l)argefile or use (n)ormal file? l
373 largefile large1 has a merge conflict
373 largefile large1 has a merge conflict
374 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
374 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
375 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
375 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
376 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
376 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
377 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
377 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
378
378
379 $ hg status -A large1
379 $ hg status -A large1
380 M large1
380 M large1
381 $ cat large1
381 $ cat large1
382 large1 for linear merge (conflict)
382 large1 for linear merge (conflict)
383 $ cat .hglf/large1
383 $ cat .hglf/large1
384 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
384 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
385 $ hg status -A large2
385 $ hg status -A large2
386 A large2
386 A large2
387 $ cat large2
387 $ cat large2
388 large2 for linear merge (conflict with normal file)
388 large2 for linear merge (conflict with normal file)
389 $ cat .hglf/large2
389 $ cat .hglf/large2
390 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
390 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
391
391
392 (internal linear merging at "hg unbundle --update")
392 (internal linear merging at "hg unbundle --update")
393
393
394 $ hg update -q -C 2
394 $ hg update -q -C 2
395 $ hg rollback -q
395 $ hg rollback -q
396
396
397 $ echo 'large1 for linear merge (conflict)' > large1
397 $ echo 'large1 for linear merge (conflict)' > large1
398 $ echo 'large2 for linear merge (conflict with normal file)' > large2
398 $ echo 'large2 for linear merge (conflict with normal file)' > large2
399 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
399 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
400 adding changesets
400 adding changesets
401 adding manifests
401 adding manifests
402 adding file changes
402 adding file changes
403 added 3 changesets with 5 changes to 5 files
403 added 3 changesets with 5 changes to 5 files
404 local changed .hglf/large2 which remote deleted
404 local changed .hglf/large2 which remote deleted
405 use (c)hanged version or (d)elete? c
405 use (c)hanged version or (d)elete? c
406 remote turned local largefile large2 into a normal file
406 remote turned local largefile large2 into a normal file
407 keep (l)argefile or use (n)ormal file? l
407 keep (l)argefile or use (n)ormal file? l
408 largefile large1 has a merge conflict
408 largefile large1 has a merge conflict
409 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
409 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
410 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
410 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
411 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
411 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
412 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
412 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
413
413
414 $ hg status -A large1
414 $ hg status -A large1
415 M large1
415 M large1
416 $ cat large1
416 $ cat large1
417 large1 for linear merge (conflict)
417 large1 for linear merge (conflict)
418 $ cat .hglf/large1
418 $ cat .hglf/large1
419 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
419 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
420 $ hg status -A large2
420 $ hg status -A large2
421 A large2
421 A large2
422 $ cat large2
422 $ cat large2
423 large2 for linear merge (conflict with normal file)
423 large2 for linear merge (conflict with normal file)
424 $ cat .hglf/large2
424 $ cat .hglf/large2
425 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
425 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
426
426
427 (internal linear merging in subrepo at "hg update")
427 (internal linear merging in subrepo at "hg update")
428
428
429 $ cd ..
429 $ cd ..
430 $ hg init subparent
430 $ hg init subparent
431 $ cd subparent
431 $ cd subparent
432
432
433 $ hg clone -q -u 2 ../repo sub
433 $ hg clone -q -u 2 ../repo sub
434 $ cat > .hgsub <<EOF
434 $ cat > .hgsub <<EOF
435 > sub = sub
435 > sub = sub
436 > EOF
436 > EOF
437 $ hg add .hgsub
437 $ hg add .hgsub
438 $ hg commit -m '#0@parent'
438 $ hg commit -m '#0@parent'
439 $ cat .hgsubstate
439 $ cat .hgsubstate
440 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
440 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
441 $ hg -R sub update -q
441 $ hg -R sub update -q
442 $ hg commit -m '#1@parent'
442 $ hg commit -m '#1@parent'
443 $ cat .hgsubstate
443 $ cat .hgsubstate
444 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
444 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
445 $ hg update -q 0
445 $ hg update -q 0
446
446
447 $ echo 'large1 for linear merge (conflict)' > sub/large1
447 $ echo 'large1 for linear merge (conflict)' > sub/large1
448 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
448 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
449 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
449 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
450 > m
450 > m
451 > r
451 > r
452 > c
452 > c
453 > l
453 > l
454 > l
454 > l
455 > EOF
455 > EOF
456 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
456 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
457 (M)erge, keep (l)ocal or keep (r)emote? m
457 (M)erge, keep (l)ocal or keep (r)emote? m
458 subrepository sources for sub differ (in checked out version)
458 subrepository sources for sub differ (in checked out version)
459 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
459 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
460 local changed .hglf/large2 which remote deleted
460 local changed .hglf/large2 which remote deleted
461 use (c)hanged version or (d)elete? c
461 use (c)hanged version or (d)elete? c
462 remote turned local largefile large2 into a normal file
462 remote turned local largefile large2 into a normal file
463 keep (l)argefile or use (n)ormal file? l
463 keep (l)argefile or use (n)ormal file? l
464 largefile large1 has a merge conflict
464 largefile large1 has a merge conflict
465 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
465 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
466 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
466 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
467 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
467 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
468 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
468 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
469 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
470
470
471 $ hg -R sub status -A sub/large1
471 $ hg -R sub status -A sub/large1
472 M sub/large1
472 M sub/large1
473 $ cat sub/large1
473 $ cat sub/large1
474 large1 for linear merge (conflict)
474 large1 for linear merge (conflict)
475 $ cat sub/.hglf/large1
475 $ cat sub/.hglf/large1
476 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
476 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
477 $ hg -R sub status -A sub/large2
477 $ hg -R sub status -A sub/large2
478 A sub/large2
478 A sub/large2
479 $ cat sub/large2
479 $ cat sub/large2
480 large2 for linear merge (conflict with normal file)
480 large2 for linear merge (conflict with normal file)
481 $ cat sub/.hglf/large2
481 $ cat sub/.hglf/large2
482 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
482 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
483
483
484 $ cd ..
484 $ cd ..
485 $ cd repo
485 $ cd repo
486
486
487 Test that rebase updates largefiles in the working directory even if
487 Test that rebase updates largefiles in the working directory even if
488 it is aborted by conflict.
488 it is aborted by conflict.
489
489
490 $ hg update -q -C 3
490 $ hg update -q -C 3
491 $ cat .hglf/large1
491 $ cat .hglf/large1
492 e5bb990443d6a92aaf7223813720f7566c9dd05b
492 e5bb990443d6a92aaf7223813720f7566c9dd05b
493 $ cat large1
493 $ cat large1
494 large1 in #3
494 large1 in #3
495 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
495 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
496 > o
496 > o
497 > EOF
497 > EOF
498 largefile large1 has a merge conflict
498 largefile large1 has a merge conflict
499 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
499 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
500 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
500 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
501 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
501 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
502 merging normal1
502 merging normal1
503 warning: conflicts during merge.
503 warning: conflicts during merge.
504 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
504 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
505 unresolved conflicts (see hg resolve, then hg rebase --continue)
505 unresolved conflicts (see hg resolve, then hg rebase --continue)
506 [1]
506 [1]
507 $ cat .hglf/large1
507 $ cat .hglf/large1
508 58e24f733a964da346e2407a2bee99d9001184f5
508 58e24f733a964da346e2407a2bee99d9001184f5
509 $ cat large1
509 $ cat large1
510 large1 in #1
510 large1 in #1
511
511
512 $ hg rebase -q --abort
512 Test that rebase updates standins for manually modified largefiles at
513 rebase aborted
513 the 1st commit of resuming.
514
515 $ echo "manually modified before 'hg rebase --continue'" > large1
516 $ hg resolve -m normal1
517 (no more unresolved files)
518 $ hg rebase --continue --config ui.interactive=True <<EOF
519 > c
520 > EOF
521 local changed .hglf/large1 which remote deleted
522 use (c)hanged version or (d)elete? c
523
524 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
525 -e5bb990443d6a92aaf7223813720f7566c9dd05b
526 +8a4f783556e7dea21139ca0466eafce954c75c13
527 $ rm -f large1
528 $ hg update -q -C tip
529 $ cat large1
530 manually modified before 'hg rebase --continue'
514
531
515 Test that transplant updates largefiles, of which standins are safely
532 Test that transplant updates largefiles, of which standins are safely
516 changed, even if it is aborted by conflict of other.
533 changed, even if it is aborted by conflict of other.
517
534
518 $ hg update -q -C 5
535 $ hg update -q -C 5
519 $ cat .hglf/large1
536 $ cat .hglf/large1
520 e5bb990443d6a92aaf7223813720f7566c9dd05b
537 e5bb990443d6a92aaf7223813720f7566c9dd05b
521 $ cat large1
538 $ cat large1
522 large1 in #3
539 large1 in #3
523 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
540 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
524 +fa44618ea25181aff4f48b70428294790cec9f61
541 +fa44618ea25181aff4f48b70428294790cec9f61
525 $ hg transplant 4
542 $ hg transplant 4
526 applying 07d6153b5c04
543 applying 07d6153b5c04
527 patching file .hglf/large1
544 patching file .hglf/large1
528 Hunk #1 FAILED at 0
545 Hunk #1 FAILED at 0
529 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
546 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
530 patch failed to apply
547 patch failed to apply
531 abort: fix up the merge and run hg transplant --continue
548 abort: fix up the merge and run hg transplant --continue
532 [255]
549 [255]
533 $ hg status -A large1
550 $ hg status -A large1
534 C large1
551 C large1
535 $ cat .hglf/large1
552 $ cat .hglf/large1
536 e5bb990443d6a92aaf7223813720f7566c9dd05b
553 e5bb990443d6a92aaf7223813720f7566c9dd05b
537 $ cat large1
554 $ cat large1
538 large1 in #3
555 large1 in #3
539 $ hg status -A largeX
556 $ hg status -A largeX
540 A largeX
557 A largeX
541 $ cat .hglf/largeX
558 $ cat .hglf/largeX
542 fa44618ea25181aff4f48b70428294790cec9f61
559 fa44618ea25181aff4f48b70428294790cec9f61
543 $ cat largeX
560 $ cat largeX
544 largeX
561 largeX
545
562
546 Test that "hg status" doesn't show removal of largefiles not managed
563 Test that "hg status" doesn't show removal of largefiles not managed
547 in the target context.
564 in the target context.
548
565
549 $ hg update -q -C 4
566 $ hg update -q -C 4
550 $ hg remove largeX
567 $ hg remove largeX
551 $ hg status -A largeX
568 $ hg status -A largeX
552 R largeX
569 R largeX
553 $ hg status -A --rev '.^1' largeX
570 $ hg status -A --rev '.^1' largeX
554
571
555 #if execbit
572 #if execbit
556
573
557 Test that "hg status" against revisions other than parent notices exec
574 Test that "hg status" against revisions other than parent notices exec
558 bit changes of largefiles.
575 bit changes of largefiles.
559
576
560 $ hg update -q -C 4
577 $ hg update -q -C 4
561
578
562 (the case that large2 doesn't have exec bit in the target context but
579 (the case that large2 doesn't have exec bit in the target context but
563 in the working context)
580 in the working context)
564
581
565 $ chmod +x large2
582 $ chmod +x large2
566 $ hg status -A --rev 0 large2
583 $ hg status -A --rev 0 large2
567 M large2
584 M large2
568 $ hg commit -m 'chmod +x large2'
585 $ hg commit -m 'chmod +x large2'
569
586
570 (the case that large2 has exec bit in the target context but not in
587 (the case that large2 has exec bit in the target context but not in
571 the working context)
588 the working context)
572
589
573 $ echo dummy > dummy
590 $ echo dummy > dummy
574 $ hg add dummy
591 $ hg add dummy
575 $ hg commit -m 'revision for separation'
592 $ hg commit -m 'revision for separation'
576 $ chmod -x large2
593 $ chmod -x large2
577 $ hg status -A --rev '.^1' large2
594 $ hg status -A --rev '.^1' large2
578 M large2
595 M large2
579
596
580 #endif
597 #endif
581
598
582 $ cd ..
599 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now