##// END OF EJS Templates
largefiles: avoid unnecessary creation of .hg/largefiles when opening lfdirstate...
Matt Harbison -
r21917:ac3b3a2d default
parent child Browse files
Show More
@@ -1,390 +1,394 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''largefiles utility code: must not import other modules in this package.'''
9 '''largefiles utility code: must not import other modules in this package.'''
10
10
11 import os
11 import os
12 import platform
12 import platform
13 import shutil
13 import shutil
14 import stat
14 import stat
15
15
16 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
16 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial import node
18 from mercurial import node
19
19
20 shortname = '.hglf'
20 shortname = '.hglf'
21 shortnameslash = shortname + '/'
21 shortnameslash = shortname + '/'
22 longname = 'largefiles'
22 longname = 'largefiles'
23
23
24
24
25 # -- Private worker functions ------------------------------------------
25 # -- Private worker functions ------------------------------------------
26
26
27 def getminsize(ui, assumelfiles, opt, default=10):
27 def getminsize(ui, assumelfiles, opt, default=10):
28 lfsize = opt
28 lfsize = opt
29 if not lfsize and assumelfiles:
29 if not lfsize and assumelfiles:
30 lfsize = ui.config(longname, 'minsize', default=default)
30 lfsize = ui.config(longname, 'minsize', default=default)
31 if lfsize:
31 if lfsize:
32 try:
32 try:
33 lfsize = float(lfsize)
33 lfsize = float(lfsize)
34 except ValueError:
34 except ValueError:
35 raise util.Abort(_('largefiles: size must be number (not %s)\n')
35 raise util.Abort(_('largefiles: size must be number (not %s)\n')
36 % lfsize)
36 % lfsize)
37 if lfsize is None:
37 if lfsize is None:
38 raise util.Abort(_('minimum size for largefiles must be specified'))
38 raise util.Abort(_('minimum size for largefiles must be specified'))
39 return lfsize
39 return lfsize
40
40
41 def link(src, dest):
41 def link(src, dest):
42 util.makedirs(os.path.dirname(dest))
42 util.makedirs(os.path.dirname(dest))
43 try:
43 try:
44 util.oslink(src, dest)
44 util.oslink(src, dest)
45 except OSError:
45 except OSError:
46 # if hardlinks fail, fallback on atomic copy
46 # if hardlinks fail, fallback on atomic copy
47 dst = util.atomictempfile(dest)
47 dst = util.atomictempfile(dest)
48 for chunk in util.filechunkiter(open(src, 'rb')):
48 for chunk in util.filechunkiter(open(src, 'rb')):
49 dst.write(chunk)
49 dst.write(chunk)
50 dst.close()
50 dst.close()
51 os.chmod(dest, os.stat(src).st_mode)
51 os.chmod(dest, os.stat(src).st_mode)
52
52
53 def usercachepath(ui, hash):
53 def usercachepath(ui, hash):
54 path = ui.configpath(longname, 'usercache', None)
54 path = ui.configpath(longname, 'usercache', None)
55 if path:
55 if path:
56 path = os.path.join(path, hash)
56 path = os.path.join(path, hash)
57 else:
57 else:
58 if os.name == 'nt':
58 if os.name == 'nt':
59 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
59 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
60 if appdata:
60 if appdata:
61 path = os.path.join(appdata, longname, hash)
61 path = os.path.join(appdata, longname, hash)
62 elif platform.system() == 'Darwin':
62 elif platform.system() == 'Darwin':
63 home = os.getenv('HOME')
63 home = os.getenv('HOME')
64 if home:
64 if home:
65 path = os.path.join(home, 'Library', 'Caches',
65 path = os.path.join(home, 'Library', 'Caches',
66 longname, hash)
66 longname, hash)
67 elif os.name == 'posix':
67 elif os.name == 'posix':
68 path = os.getenv('XDG_CACHE_HOME')
68 path = os.getenv('XDG_CACHE_HOME')
69 if path:
69 if path:
70 path = os.path.join(path, longname, hash)
70 path = os.path.join(path, longname, hash)
71 else:
71 else:
72 home = os.getenv('HOME')
72 home = os.getenv('HOME')
73 if home:
73 if home:
74 path = os.path.join(home, '.cache', longname, hash)
74 path = os.path.join(home, '.cache', longname, hash)
75 else:
75 else:
76 raise util.Abort(_('unknown operating system: %s\n') % os.name)
76 raise util.Abort(_('unknown operating system: %s\n') % os.name)
77 return path
77 return path
78
78
79 def inusercache(ui, hash):
79 def inusercache(ui, hash):
80 path = usercachepath(ui, hash)
80 path = usercachepath(ui, hash)
81 return path and os.path.exists(path)
81 return path and os.path.exists(path)
82
82
83 def findfile(repo, hash):
83 def findfile(repo, hash):
84 if instore(repo, hash):
84 if instore(repo, hash):
85 repo.ui.note(_('found %s in store\n') % hash)
85 repo.ui.note(_('found %s in store\n') % hash)
86 return storepath(repo, hash)
86 return storepath(repo, hash)
87 elif inusercache(repo.ui, hash):
87 elif inusercache(repo.ui, hash):
88 repo.ui.note(_('found %s in system cache\n') % hash)
88 repo.ui.note(_('found %s in system cache\n') % hash)
89 path = storepath(repo, hash)
89 path = storepath(repo, hash)
90 link(usercachepath(repo.ui, hash), path)
90 link(usercachepath(repo.ui, hash), path)
91 return path
91 return path
92 return None
92 return None
93
93
94 class largefilesdirstate(dirstate.dirstate):
94 class largefilesdirstate(dirstate.dirstate):
95 def __getitem__(self, key):
95 def __getitem__(self, key):
96 return super(largefilesdirstate, self).__getitem__(unixpath(key))
96 return super(largefilesdirstate, self).__getitem__(unixpath(key))
97 def normal(self, f):
97 def normal(self, f):
98 return super(largefilesdirstate, self).normal(unixpath(f))
98 return super(largefilesdirstate, self).normal(unixpath(f))
99 def remove(self, f):
99 def remove(self, f):
100 return super(largefilesdirstate, self).remove(unixpath(f))
100 return super(largefilesdirstate, self).remove(unixpath(f))
101 def add(self, f):
101 def add(self, f):
102 return super(largefilesdirstate, self).add(unixpath(f))
102 return super(largefilesdirstate, self).add(unixpath(f))
103 def drop(self, f):
103 def drop(self, f):
104 return super(largefilesdirstate, self).drop(unixpath(f))
104 return super(largefilesdirstate, self).drop(unixpath(f))
105 def forget(self, f):
105 def forget(self, f):
106 return super(largefilesdirstate, self).forget(unixpath(f))
106 return super(largefilesdirstate, self).forget(unixpath(f))
107 def normallookup(self, f):
107 def normallookup(self, f):
108 return super(largefilesdirstate, self).normallookup(unixpath(f))
108 return super(largefilesdirstate, self).normallookup(unixpath(f))
109 def _ignore(self, f):
109 def _ignore(self, f):
110 return False
110 return False
111
111
112 def openlfdirstate(ui, repo, create=True):
112 def openlfdirstate(ui, repo, create=True):
113 '''
113 '''
114 Return a dirstate object that tracks largefiles: i.e. its root is
114 Return a dirstate object that tracks largefiles: i.e. its root is
115 the repo root, but it is saved in .hg/largefiles/dirstate.
115 the repo root, but it is saved in .hg/largefiles/dirstate.
116 '''
116 '''
117 lfstoredir = repo.join(longname)
117 lfstoredir = repo.join(longname)
118 opener = scmutil.opener(lfstoredir)
118 opener = scmutil.opener(lfstoredir)
119 lfdirstate = largefilesdirstate(opener, ui, repo.root,
119 lfdirstate = largefilesdirstate(opener, ui, repo.root,
120 repo.dirstate._validate)
120 repo.dirstate._validate)
121
121
122 # If the largefiles dirstate does not exist, populate and create
122 # If the largefiles dirstate does not exist, populate and create
123 # it. This ensures that we create it on the first meaningful
123 # it. This ensures that we create it on the first meaningful
124 # largefiles operation in a new clone.
124 # largefiles operation in a new clone.
125 if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
125 if create and not os.path.exists(os.path.join(lfstoredir, 'dirstate')):
126 util.makedirs(lfstoredir)
127 matcher = getstandinmatcher(repo)
126 matcher = getstandinmatcher(repo)
128 for standin in repo.dirstate.walk(matcher, [], False, False):
127 standins = repo.dirstate.walk(matcher, [], False, False)
128
129 if len(standins) > 0:
130 util.makedirs(lfstoredir)
131
132 for standin in standins:
129 lfile = splitstandin(standin)
133 lfile = splitstandin(standin)
130 lfdirstate.normallookup(lfile)
134 lfdirstate.normallookup(lfile)
131 return lfdirstate
135 return lfdirstate
132
136
133 def lfdirstatestatus(lfdirstate, repo, rev):
137 def lfdirstatestatus(lfdirstate, repo, rev):
134 match = match_.always(repo.root, repo.getcwd())
138 match = match_.always(repo.root, repo.getcwd())
135 s = lfdirstate.status(match, [], False, False, False)
139 s = lfdirstate.status(match, [], False, False, False)
136 unsure, modified, added, removed, missing, unknown, ignored, clean = s
140 unsure, modified, added, removed, missing, unknown, ignored, clean = s
137 for lfile in unsure:
141 for lfile in unsure:
138 try:
142 try:
139 fctx = repo[rev][standin(lfile)]
143 fctx = repo[rev][standin(lfile)]
140 except LookupError:
144 except LookupError:
141 fctx = None
145 fctx = None
142 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
146 if not fctx or fctx.data().strip() != hashfile(repo.wjoin(lfile)):
143 modified.append(lfile)
147 modified.append(lfile)
144 else:
148 else:
145 clean.append(lfile)
149 clean.append(lfile)
146 lfdirstate.normal(lfile)
150 lfdirstate.normal(lfile)
147 return (modified, added, removed, missing, unknown, ignored, clean)
151 return (modified, added, removed, missing, unknown, ignored, clean)
148
152
149 def listlfiles(repo, rev=None, matcher=None):
153 def listlfiles(repo, rev=None, matcher=None):
150 '''return a list of largefiles in the working copy or the
154 '''return a list of largefiles in the working copy or the
151 specified changeset'''
155 specified changeset'''
152
156
153 if matcher is None:
157 if matcher is None:
154 matcher = getstandinmatcher(repo)
158 matcher = getstandinmatcher(repo)
155
159
156 # ignore unknown files in working directory
160 # ignore unknown files in working directory
157 return [splitstandin(f)
161 return [splitstandin(f)
158 for f in repo[rev].walk(matcher)
162 for f in repo[rev].walk(matcher)
159 if rev is not None or repo.dirstate[f] != '?']
163 if rev is not None or repo.dirstate[f] != '?']
160
164
161 def instore(repo, hash):
165 def instore(repo, hash):
162 return os.path.exists(storepath(repo, hash))
166 return os.path.exists(storepath(repo, hash))
163
167
164 def storepath(repo, hash):
168 def storepath(repo, hash):
165 return repo.join(os.path.join(longname, hash))
169 return repo.join(os.path.join(longname, hash))
166
170
167 def copyfromcache(repo, hash, filename):
171 def copyfromcache(repo, hash, filename):
168 '''Copy the specified largefile from the repo or system cache to
172 '''Copy the specified largefile from the repo or system cache to
169 filename in the repository. Return true on success or false if the
173 filename in the repository. Return true on success or false if the
170 file was not found in either cache (which should not happened:
174 file was not found in either cache (which should not happened:
171 this is meant to be called only after ensuring that the needed
175 this is meant to be called only after ensuring that the needed
172 largefile exists in the cache).'''
176 largefile exists in the cache).'''
173 path = findfile(repo, hash)
177 path = findfile(repo, hash)
174 if path is None:
178 if path is None:
175 return False
179 return False
176 util.makedirs(os.path.dirname(repo.wjoin(filename)))
180 util.makedirs(os.path.dirname(repo.wjoin(filename)))
177 # The write may fail before the file is fully written, but we
181 # The write may fail before the file is fully written, but we
178 # don't use atomic writes in the working copy.
182 # don't use atomic writes in the working copy.
179 shutil.copy(path, repo.wjoin(filename))
183 shutil.copy(path, repo.wjoin(filename))
180 return True
184 return True
181
185
182 def copytostore(repo, rev, file, uploaded=False):
186 def copytostore(repo, rev, file, uploaded=False):
183 hash = readstandin(repo, file, rev)
187 hash = readstandin(repo, file, rev)
184 if instore(repo, hash):
188 if instore(repo, hash):
185 return
189 return
186 copytostoreabsolute(repo, repo.wjoin(file), hash)
190 copytostoreabsolute(repo, repo.wjoin(file), hash)
187
191
188 def copyalltostore(repo, node):
192 def copyalltostore(repo, node):
189 '''Copy all largefiles in a given revision to the store'''
193 '''Copy all largefiles in a given revision to the store'''
190
194
191 ctx = repo[node]
195 ctx = repo[node]
192 for filename in ctx.files():
196 for filename in ctx.files():
193 if isstandin(filename) and filename in ctx.manifest():
197 if isstandin(filename) and filename in ctx.manifest():
194 realfile = splitstandin(filename)
198 realfile = splitstandin(filename)
195 copytostore(repo, ctx.node(), realfile)
199 copytostore(repo, ctx.node(), realfile)
196
200
197
201
198 def copytostoreabsolute(repo, file, hash):
202 def copytostoreabsolute(repo, file, hash):
199 if inusercache(repo.ui, hash):
203 if inusercache(repo.ui, hash):
200 link(usercachepath(repo.ui, hash), storepath(repo, hash))
204 link(usercachepath(repo.ui, hash), storepath(repo, hash))
201 elif not getattr(repo, "_isconverting", False):
205 elif not getattr(repo, "_isconverting", False):
202 util.makedirs(os.path.dirname(storepath(repo, hash)))
206 util.makedirs(os.path.dirname(storepath(repo, hash)))
203 dst = util.atomictempfile(storepath(repo, hash),
207 dst = util.atomictempfile(storepath(repo, hash),
204 createmode=repo.store.createmode)
208 createmode=repo.store.createmode)
205 for chunk in util.filechunkiter(open(file, 'rb')):
209 for chunk in util.filechunkiter(open(file, 'rb')):
206 dst.write(chunk)
210 dst.write(chunk)
207 dst.close()
211 dst.close()
208 linktousercache(repo, hash)
212 linktousercache(repo, hash)
209
213
210 def linktousercache(repo, hash):
214 def linktousercache(repo, hash):
211 path = usercachepath(repo.ui, hash)
215 path = usercachepath(repo.ui, hash)
212 if path:
216 if path:
213 link(storepath(repo, hash), path)
217 link(storepath(repo, hash), path)
214
218
215 def getstandinmatcher(repo, pats=[], opts={}):
219 def getstandinmatcher(repo, pats=[], opts={}):
216 '''Return a match object that applies pats to the standin directory'''
220 '''Return a match object that applies pats to the standin directory'''
217 standindir = repo.wjoin(shortname)
221 standindir = repo.wjoin(shortname)
218 if pats:
222 if pats:
219 pats = [os.path.join(standindir, pat) for pat in pats]
223 pats = [os.path.join(standindir, pat) for pat in pats]
220 else:
224 else:
221 # no patterns: relative to repo root
225 # no patterns: relative to repo root
222 pats = [standindir]
226 pats = [standindir]
223 # no warnings about missing files or directories
227 # no warnings about missing files or directories
224 match = scmutil.match(repo[None], pats, opts)
228 match = scmutil.match(repo[None], pats, opts)
225 match.bad = lambda f, msg: None
229 match.bad = lambda f, msg: None
226 return match
230 return match
227
231
228 def composestandinmatcher(repo, rmatcher):
232 def composestandinmatcher(repo, rmatcher):
229 '''Return a matcher that accepts standins corresponding to the
233 '''Return a matcher that accepts standins corresponding to the
230 files accepted by rmatcher. Pass the list of files in the matcher
234 files accepted by rmatcher. Pass the list of files in the matcher
231 as the paths specified by the user.'''
235 as the paths specified by the user.'''
232 smatcher = getstandinmatcher(repo, rmatcher.files())
236 smatcher = getstandinmatcher(repo, rmatcher.files())
233 isstandin = smatcher.matchfn
237 isstandin = smatcher.matchfn
234 def composedmatchfn(f):
238 def composedmatchfn(f):
235 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
239 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
236 smatcher.matchfn = composedmatchfn
240 smatcher.matchfn = composedmatchfn
237
241
238 return smatcher
242 return smatcher
239
243
240 def standin(filename):
244 def standin(filename):
241 '''Return the repo-relative path to the standin for the specified big
245 '''Return the repo-relative path to the standin for the specified big
242 file.'''
246 file.'''
243 # Notes:
247 # Notes:
244 # 1) Some callers want an absolute path, but for instance addlargefiles
248 # 1) Some callers want an absolute path, but for instance addlargefiles
245 # needs it repo-relative so it can be passed to repo[None].add(). So
249 # needs it repo-relative so it can be passed to repo[None].add(). So
246 # leave it up to the caller to use repo.wjoin() to get an absolute path.
250 # leave it up to the caller to use repo.wjoin() to get an absolute path.
247 # 2) Join with '/' because that's what dirstate always uses, even on
251 # 2) Join with '/' because that's what dirstate always uses, even on
248 # Windows. Change existing separator to '/' first in case we are
252 # Windows. Change existing separator to '/' first in case we are
249 # passed filenames from an external source (like the command line).
253 # passed filenames from an external source (like the command line).
250 return shortnameslash + util.pconvert(filename)
254 return shortnameslash + util.pconvert(filename)
251
255
252 def isstandin(filename):
256 def isstandin(filename):
253 '''Return true if filename is a big file standin. filename must be
257 '''Return true if filename is a big file standin. filename must be
254 in Mercurial's internal form (slash-separated).'''
258 in Mercurial's internal form (slash-separated).'''
255 return filename.startswith(shortnameslash)
259 return filename.startswith(shortnameslash)
256
260
257 def splitstandin(filename):
261 def splitstandin(filename):
258 # Split on / because that's what dirstate always uses, even on Windows.
262 # Split on / because that's what dirstate always uses, even on Windows.
259 # Change local separator to / first just in case we are passed filenames
263 # Change local separator to / first just in case we are passed filenames
260 # from an external source (like the command line).
264 # from an external source (like the command line).
261 bits = util.pconvert(filename).split('/', 1)
265 bits = util.pconvert(filename).split('/', 1)
262 if len(bits) == 2 and bits[0] == shortname:
266 if len(bits) == 2 and bits[0] == shortname:
263 return bits[1]
267 return bits[1]
264 else:
268 else:
265 return None
269 return None
266
270
267 def updatestandin(repo, standin):
271 def updatestandin(repo, standin):
268 file = repo.wjoin(splitstandin(standin))
272 file = repo.wjoin(splitstandin(standin))
269 if os.path.exists(file):
273 if os.path.exists(file):
270 hash = hashfile(file)
274 hash = hashfile(file)
271 executable = getexecutable(file)
275 executable = getexecutable(file)
272 writestandin(repo, standin, hash, executable)
276 writestandin(repo, standin, hash, executable)
273
277
274 def readstandin(repo, filename, node=None):
278 def readstandin(repo, filename, node=None):
275 '''read hex hash from standin for filename at given node, or working
279 '''read hex hash from standin for filename at given node, or working
276 directory if no node is given'''
280 directory if no node is given'''
277 return repo[node][standin(filename)].data().strip()
281 return repo[node][standin(filename)].data().strip()
278
282
279 def writestandin(repo, standin, hash, executable):
283 def writestandin(repo, standin, hash, executable):
280 '''write hash to <repo.root>/<standin>'''
284 '''write hash to <repo.root>/<standin>'''
281 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
285 repo.wwrite(standin, hash + '\n', executable and 'x' or '')
282
286
283 def copyandhash(instream, outfile):
287 def copyandhash(instream, outfile):
284 '''Read bytes from instream (iterable) and write them to outfile,
288 '''Read bytes from instream (iterable) and write them to outfile,
285 computing the SHA-1 hash of the data along the way. Return the hash.'''
289 computing the SHA-1 hash of the data along the way. Return the hash.'''
286 hasher = util.sha1('')
290 hasher = util.sha1('')
287 for data in instream:
291 for data in instream:
288 hasher.update(data)
292 hasher.update(data)
289 outfile.write(data)
293 outfile.write(data)
290 return hasher.hexdigest()
294 return hasher.hexdigest()
291
295
292 def hashrepofile(repo, file):
296 def hashrepofile(repo, file):
293 return hashfile(repo.wjoin(file))
297 return hashfile(repo.wjoin(file))
294
298
295 def hashfile(file):
299 def hashfile(file):
296 if not os.path.exists(file):
300 if not os.path.exists(file):
297 return ''
301 return ''
298 hasher = util.sha1('')
302 hasher = util.sha1('')
299 fd = open(file, 'rb')
303 fd = open(file, 'rb')
300 for data in util.filechunkiter(fd, 128 * 1024):
304 for data in util.filechunkiter(fd, 128 * 1024):
301 hasher.update(data)
305 hasher.update(data)
302 fd.close()
306 fd.close()
303 return hasher.hexdigest()
307 return hasher.hexdigest()
304
308
305 def getexecutable(filename):
309 def getexecutable(filename):
306 mode = os.stat(filename).st_mode
310 mode = os.stat(filename).st_mode
307 return ((mode & stat.S_IXUSR) and
311 return ((mode & stat.S_IXUSR) and
308 (mode & stat.S_IXGRP) and
312 (mode & stat.S_IXGRP) and
309 (mode & stat.S_IXOTH))
313 (mode & stat.S_IXOTH))
310
314
311 def urljoin(first, second, *arg):
315 def urljoin(first, second, *arg):
312 def join(left, right):
316 def join(left, right):
313 if not left.endswith('/'):
317 if not left.endswith('/'):
314 left += '/'
318 left += '/'
315 if right.startswith('/'):
319 if right.startswith('/'):
316 right = right[1:]
320 right = right[1:]
317 return left + right
321 return left + right
318
322
319 url = join(first, second)
323 url = join(first, second)
320 for a in arg:
324 for a in arg:
321 url = join(url, a)
325 url = join(url, a)
322 return url
326 return url
323
327
324 def hexsha1(data):
328 def hexsha1(data):
325 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
329 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
326 object data"""
330 object data"""
327 h = util.sha1()
331 h = util.sha1()
328 for chunk in util.filechunkiter(data):
332 for chunk in util.filechunkiter(data):
329 h.update(chunk)
333 h.update(chunk)
330 return h.hexdigest()
334 return h.hexdigest()
331
335
332 def httpsendfile(ui, filename):
336 def httpsendfile(ui, filename):
333 return httpconnection.httpsendfile(ui, filename, 'rb')
337 return httpconnection.httpsendfile(ui, filename, 'rb')
334
338
335 def unixpath(path):
339 def unixpath(path):
336 '''Return a version of path normalized for use with the lfdirstate.'''
340 '''Return a version of path normalized for use with the lfdirstate.'''
337 return util.pconvert(os.path.normpath(path))
341 return util.pconvert(os.path.normpath(path))
338
342
339 def islfilesrepo(repo):
343 def islfilesrepo(repo):
340 if ('largefiles' in repo.requirements and
344 if ('largefiles' in repo.requirements and
341 util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
345 util.any(shortnameslash in f[0] for f in repo.store.datafiles())):
342 return True
346 return True
343
347
344 return util.any(openlfdirstate(repo.ui, repo, False))
348 return util.any(openlfdirstate(repo.ui, repo, False))
345
349
346 class storeprotonotcapable(Exception):
350 class storeprotonotcapable(Exception):
347 def __init__(self, storetypes):
351 def __init__(self, storetypes):
348 self.storetypes = storetypes
352 self.storetypes = storetypes
349
353
350 def getstandinsstate(repo):
354 def getstandinsstate(repo):
351 standins = []
355 standins = []
352 matcher = getstandinmatcher(repo)
356 matcher = getstandinmatcher(repo)
353 for standin in repo.dirstate.walk(matcher, [], False, False):
357 for standin in repo.dirstate.walk(matcher, [], False, False):
354 lfile = splitstandin(standin)
358 lfile = splitstandin(standin)
355 try:
359 try:
356 hash = readstandin(repo, lfile)
360 hash = readstandin(repo, lfile)
357 except IOError:
361 except IOError:
358 hash = None
362 hash = None
359 standins.append((lfile, hash))
363 standins.append((lfile, hash))
360 return standins
364 return standins
361
365
362 def getlfilestoupdate(oldstandins, newstandins):
366 def getlfilestoupdate(oldstandins, newstandins):
363 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
367 changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
364 filelist = []
368 filelist = []
365 for f in changedstandins:
369 for f in changedstandins:
366 if f[0] not in filelist:
370 if f[0] not in filelist:
367 filelist.append(f[0])
371 filelist.append(f[0])
368 return filelist
372 return filelist
369
373
370 def getlfilestoupload(repo, missing, addfunc):
374 def getlfilestoupload(repo, missing, addfunc):
371 for n in missing:
375 for n in missing:
372 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
376 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
373 ctx = repo[n]
377 ctx = repo[n]
374 files = set(ctx.files())
378 files = set(ctx.files())
375 if len(parents) == 2:
379 if len(parents) == 2:
376 mc = ctx.manifest()
380 mc = ctx.manifest()
377 mp1 = ctx.parents()[0].manifest()
381 mp1 = ctx.parents()[0].manifest()
378 mp2 = ctx.parents()[1].manifest()
382 mp2 = ctx.parents()[1].manifest()
379 for f in mp1:
383 for f in mp1:
380 if f not in mc:
384 if f not in mc:
381 files.add(f)
385 files.add(f)
382 for f in mp2:
386 for f in mp2:
383 if f not in mc:
387 if f not in mc:
384 files.add(f)
388 files.add(f)
385 for f in mc:
389 for f in mc:
386 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
390 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
387 files.add(f)
391 files.add(f)
388 for fn in files:
392 for fn in files:
389 if isstandin(fn) and fn in ctx:
393 if isstandin(fn) and fn in ctx:
390 addfunc(fn, ctx[fn].data().strip())
394 addfunc(fn, ctx[fn].data().strip())
@@ -1,357 +1,370 b''
1
1
2 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "[extensions]" >> $HGRCPATH
3 $ echo "largefiles =" >> $HGRCPATH
3 $ echo "largefiles =" >> $HGRCPATH
4
4
5 Create the repository outside $HOME since largefiles write to
5 Create the repository outside $HOME since largefiles write to
6 $HOME/.cache/largefiles.
6 $HOME/.cache/largefiles.
7
7
8 $ hg init test
8 $ hg init test
9 $ cd test
9 $ cd test
10 $ echo "root" > root
10 $ echo "root" > root
11 $ hg add root
11 $ hg add root
12 $ hg commit -m "Root commit"
12 $ hg commit -m "Root commit"
13
13
14 $ echo "large" > foo
14 $ echo "large" > foo
15 $ hg add --large foo
15 $ hg add --large foo
16 $ hg commit -m "Add foo as a largefile"
16 $ hg commit -m "Add foo as a largefile"
17
17
18 $ hg update -r 0
18 $ hg update -r 0
19 getting changed largefiles
19 getting changed largefiles
20 0 largefiles updated, 1 removed
20 0 largefiles updated, 1 removed
21 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
21 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
22
22
23 $ echo "normal" > foo
23 $ echo "normal" > foo
24 $ hg add foo
24 $ hg add foo
25 $ hg commit -m "Add foo as normal file"
25 $ hg commit -m "Add foo as normal file"
26 created new head
26 created new head
27
27
28 Normal file in the working copy, keeping the normal version:
28 Normal file in the working copy, keeping the normal version:
29
29
30 $ echo "n" | hg merge --config ui.interactive=Yes
30 $ echo "n" | hg merge --config ui.interactive=Yes
31 remote turned local normal file foo into a largefile
31 remote turned local normal file foo into a largefile
32 use (l)argefile or keep (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
32 use (l)argefile or keep (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 (branch merge, don't forget to commit)
33 (branch merge, don't forget to commit)
34 getting changed largefiles
34 getting changed largefiles
35 0 largefiles updated, 0 removed
35 0 largefiles updated, 0 removed
36
36
37 $ hg status
37 $ hg status
38 $ cat foo
38 $ cat foo
39 normal
39 normal
40
40
41 Normal file in the working copy, keeping the largefile version:
41 Normal file in the working copy, keeping the largefile version:
42
42
43 $ hg update -q -C
43 $ hg update -q -C
44 $ echo "l" | hg merge --config ui.interactive=Yes
44 $ echo "l" | hg merge --config ui.interactive=Yes
45 remote turned local normal file foo into a largefile
45 remote turned local normal file foo into a largefile
46 use (l)argefile or keep (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
46 use (l)argefile or keep (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
47 (branch merge, don't forget to commit)
47 (branch merge, don't forget to commit)
48 getting changed largefiles
48 getting changed largefiles
49 1 largefiles updated, 0 removed
49 1 largefiles updated, 0 removed
50
50
51 $ hg status
51 $ hg status
52 M foo
52 M foo
53
53
54 $ hg diff --nodates
54 $ hg diff --nodates
55 diff -r fa129ab6b5a7 .hglf/foo
55 diff -r fa129ab6b5a7 .hglf/foo
56 --- /dev/null
56 --- /dev/null
57 +++ b/.hglf/foo
57 +++ b/.hglf/foo
58 @@ -0,0 +1,1 @@
58 @@ -0,0 +1,1 @@
59 +7f7097b041ccf68cc5561e9600da4655d21c6d18
59 +7f7097b041ccf68cc5561e9600da4655d21c6d18
60 diff -r fa129ab6b5a7 foo
60 diff -r fa129ab6b5a7 foo
61 --- a/foo
61 --- a/foo
62 +++ /dev/null
62 +++ /dev/null
63 @@ -1,1 +0,0 @@
63 @@ -1,1 +0,0 @@
64 -normal
64 -normal
65
65
66 $ cat foo
66 $ cat foo
67 large
67 large
68
68
69 Largefile in the working copy, keeping the normal version:
69 Largefile in the working copy, keeping the normal version:
70
70
71 $ hg update -q -C -r 1
71 $ hg update -q -C -r 1
72 $ echo "n" | hg merge --config ui.interactive=Yes
72 $ echo "n" | hg merge --config ui.interactive=Yes
73 remote turned local largefile foo into a normal file
73 remote turned local largefile foo into a normal file
74 keep (l)argefile or use (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
74 keep (l)argefile or use (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
75 (branch merge, don't forget to commit)
75 (branch merge, don't forget to commit)
76 getting changed largefiles
76 getting changed largefiles
77 0 largefiles updated, 0 removed
77 0 largefiles updated, 0 removed
78
78
79 $ hg status
79 $ hg status
80 M foo
80 M foo
81
81
82 $ hg diff --nodates
82 $ hg diff --nodates
83 diff -r ff521236428a .hglf/foo
83 diff -r ff521236428a .hglf/foo
84 --- a/.hglf/foo
84 --- a/.hglf/foo
85 +++ /dev/null
85 +++ /dev/null
86 @@ -1,1 +0,0 @@
86 @@ -1,1 +0,0 @@
87 -7f7097b041ccf68cc5561e9600da4655d21c6d18
87 -7f7097b041ccf68cc5561e9600da4655d21c6d18
88 diff -r ff521236428a foo
88 diff -r ff521236428a foo
89 --- /dev/null
89 --- /dev/null
90 +++ b/foo
90 +++ b/foo
91 @@ -0,0 +1,1 @@
91 @@ -0,0 +1,1 @@
92 +normal
92 +normal
93
93
94 $ cat foo
94 $ cat foo
95 normal
95 normal
96
96
97 Largefile in the working copy, keeping the largefile version:
97 Largefile in the working copy, keeping the largefile version:
98
98
99 $ hg update -q -C -r 1
99 $ hg update -q -C -r 1
100 $ echo "l" | hg merge --config ui.interactive=Yes
100 $ echo "l" | hg merge --config ui.interactive=Yes
101 remote turned local largefile foo into a normal file
101 remote turned local largefile foo into a normal file
102 keep (l)argefile or use (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
102 keep (l)argefile or use (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
103 (branch merge, don't forget to commit)
103 (branch merge, don't forget to commit)
104 getting changed largefiles
104 getting changed largefiles
105 1 largefiles updated, 0 removed
105 1 largefiles updated, 0 removed
106
106
107 $ hg status
107 $ hg status
108
108
109 $ cat foo
109 $ cat foo
110 large
110 large
111
111
112 Whatever ... commit something so we can invoke merge when updating
112 Whatever ... commit something so we can invoke merge when updating
113
113
114 $ hg commit -m '3: Merge'
114 $ hg commit -m '3: Merge'
115
115
116 Updating from largefile to normal - no reason to prompt
116 Updating from largefile to normal - no reason to prompt
117
117
118 $ hg up -r 2
118 $ hg up -r 2
119 getting changed largefiles
119 getting changed largefiles
120 0 largefiles updated, 0 removed
120 0 largefiles updated, 0 removed
121 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
122 $ cat foo
122 $ cat foo
123 normal
123 normal
124
124
125 (the update above used to leave the working dir in a very weird state - clean it
125 (the update above used to leave the working dir in a very weird state - clean it
126 $ hg up -qr null
126 $ hg up -qr null
127 $ hg up -qr 2
127 $ hg up -qr 2
128 )
128 )
129
129
130 Updating from normal to largefile - no reason to prompt
130 Updating from normal to largefile - no reason to prompt
131
131
132 $ hg up -r 3
132 $ hg up -r 3
133 getting changed largefiles
133 getting changed largefiles
134 1 largefiles updated, 0 removed
134 1 largefiles updated, 0 removed
135 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
135 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
136 $ cat foo
136 $ cat foo
137 large
137 large
138
138
139 $ cd ..
139 $ cd ..
140
140
141
141
142 Systematic testing of merges involving largefiles:
142 Systematic testing of merges involving largefiles:
143
143
144 Ancestor: normal Parent: normal= Parent: large result: large
144 Ancestor: normal Parent: normal= Parent: large result: large
145 Ancestor: normal Parent: normal2 Parent: large result: ?
145 Ancestor: normal Parent: normal2 Parent: large result: ?
146 Ancestor: large Parent: large= Parent: normal result: normal
146 Ancestor: large Parent: large= Parent: normal result: normal
147 Ancestor: large Parent: large2 Parent: normal result: ?
147 Ancestor: large Parent: large2 Parent: normal result: ?
148
148
149 All cases should try merging both ways.
149 All cases should try merging both ways.
150 "=" means same file content.
150 "=" means same file content.
151
151
152 Prepare test repo:
152 Prepare test repo:
153
153
154 $ hg init merges
154 $ hg init merges
155 $ cd merges
155 $ cd merges
156 $ touch f1
156 $ touch f1
157 $ hg ci -Aqm "0-root"
157 $ hg ci -Aqm "0-root" --config extensions.largefiles=!
158
159 Ensure that .hg/largefiles isn't created before largefiles are added
160 #if unix-permissions
161 $ chmod 555 .hg
162 #endif
163 $ hg status
164 #if unix-permissions
165 $ chmod 755 .hg
166 #endif
167
168 $ find .hg/largefiles
169 find: `.hg/largefiles': No such file or directory
170 [1]
158
171
159 ancestor is "normal":
172 ancestor is "normal":
160 $ echo normal > f
173 $ echo normal > f
161 $ hg ci -Aqm "1-normal-ancestor"
174 $ hg ci -Aqm "1-normal-ancestor"
162 $ touch f2
175 $ touch f2
163 $ hg ci -Aqm "2-normal-unchanged"
176 $ hg ci -Aqm "2-normal-unchanged"
164 $ hg tag -l "normal="
177 $ hg tag -l "normal="
165 $ echo normal2 > f
178 $ echo normal2 > f
166 $ hg ci -m "3-normal2"
179 $ hg ci -m "3-normal2"
167 $ hg tag -l "normal2"
180 $ hg tag -l "normal2"
168 $ hg up -qr 1
181 $ hg up -qr 1
169 $ hg rm f
182 $ hg rm f
170 $ echo large > f
183 $ echo large > f
171 $ hg add --large f
184 $ hg add --large f
172 $ hg ci -qm "4-normal-to-large"
185 $ hg ci -qm "4-normal-to-large"
173 $ hg tag -l "large"
186 $ hg tag -l "large"
174
187
175 $ hg up -qr null
188 $ hg up -qr null
176
189
177 ancestor is "large":
190 ancestor is "large":
178 $ echo large > f
191 $ echo large > f
179 $ hg add --large f
192 $ hg add --large f
180 $ hg ci -qm "5-large-ancestor"
193 $ hg ci -qm "5-large-ancestor"
181 $ touch f2
194 $ touch f2
182 $ hg ci -Aqm "6-large-unchanged"
195 $ hg ci -Aqm "6-large-unchanged"
183 $ hg tag -l "large="
196 $ hg tag -l "large="
184 $ echo large2 > f
197 $ echo large2 > f
185 $ hg ci -m "7-large2"
198 $ hg ci -m "7-large2"
186 $ hg tag -l "large2"
199 $ hg tag -l "large2"
187 $ hg up -qr 5
200 $ hg up -qr 5
188 $ hg rm f
201 $ hg rm f
189 $ echo normal > f
202 $ echo normal > f
190 $ hg ci -qAm "8-large-to-normal"
203 $ hg ci -qAm "8-large-to-normal"
191 $ hg tag -l "normal"
204 $ hg tag -l "normal"
192
205
193 Ancestor: normal Parent: normal= Parent: large result: large
206 Ancestor: normal Parent: normal= Parent: large result: large
194
207
195 $ hg up -Cqr normal=
208 $ hg up -Cqr normal=
196 $ hg merge -r large
209 $ hg merge -r large
197 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
210 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
198 (branch merge, don't forget to commit)
211 (branch merge, don't forget to commit)
199 getting changed largefiles
212 getting changed largefiles
200 1 largefiles updated, 0 removed
213 1 largefiles updated, 0 removed
201 $ cat f
214 $ cat f
202 large
215 large
203
216
204 swap
217 swap
205
218
206 $ hg up -Cqr large
219 $ hg up -Cqr large
207 $ hg merge -r normal=
220 $ hg merge -r normal=
208 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 (branch merge, don't forget to commit)
222 (branch merge, don't forget to commit)
210 getting changed largefiles
223 getting changed largefiles
211 0 largefiles updated, 0 removed
224 0 largefiles updated, 0 removed
212 $ cat f
225 $ cat f
213 large
226 large
214
227
215 Ancestor: normal Parent: normal2 Parent: large result: ?
228 Ancestor: normal Parent: normal2 Parent: large result: ?
216 (annoying extra prompt ... but it do not do any serious harm)
229 (annoying extra prompt ... but it do not do any serious harm)
217
230
218 $ hg up -Cqr normal2
231 $ hg up -Cqr normal2
219 $ hg merge -r large
232 $ hg merge -r large
220 local changed f which remote deleted
233 local changed f which remote deleted
221 use (c)hanged version or (d)elete? c
234 use (c)hanged version or (d)elete? c
222 remote turned local normal file f into a largefile
235 remote turned local normal file f into a largefile
223 use (l)argefile or keep (n)ormal file? l
236 use (l)argefile or keep (n)ormal file? l
224 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
237 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
225 (branch merge, don't forget to commit)
238 (branch merge, don't forget to commit)
226 getting changed largefiles
239 getting changed largefiles
227 1 largefiles updated, 0 removed
240 1 largefiles updated, 0 removed
228 $ cat f
241 $ cat f
229 large
242 large
230
243
231 $ hg up -Cqr normal2
244 $ hg up -Cqr normal2
232 $ ( echo c; echo n ) | hg merge -r large --config ui.interactive=Yes
245 $ ( echo c; echo n ) | hg merge -r large --config ui.interactive=Yes
233 local changed f which remote deleted
246 local changed f which remote deleted
234 use (c)hanged version or (d)elete? remote turned local normal file f into a largefile
247 use (c)hanged version or (d)elete? remote turned local normal file f into a largefile
235 use (l)argefile or keep (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
248 use (l)argefile or keep (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
236 (branch merge, don't forget to commit)
249 (branch merge, don't forget to commit)
237 getting changed largefiles
250 getting changed largefiles
238 0 largefiles updated, 0 removed
251 0 largefiles updated, 0 removed
239 $ cat f
252 $ cat f
240 normal2
253 normal2
241
254
242 $ hg up -Cqr normal2
255 $ hg up -Cqr normal2
243 $ echo d | hg merge -r large --config ui.interactive=Yes
256 $ echo d | hg merge -r large --config ui.interactive=Yes
244 local changed f which remote deleted
257 local changed f which remote deleted
245 use (c)hanged version or (d)elete? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
258 use (c)hanged version or (d)elete? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
246 (branch merge, don't forget to commit)
259 (branch merge, don't forget to commit)
247 getting changed largefiles
260 getting changed largefiles
248 1 largefiles updated, 0 removed
261 1 largefiles updated, 0 removed
249 $ cat f
262 $ cat f
250 large
263 large
251
264
252 swap
265 swap
253
266
254 $ hg up -Cqr large
267 $ hg up -Cqr large
255 $ hg merge -r normal2
268 $ hg merge -r normal2
256 remote changed f which local deleted
269 remote changed f which local deleted
257 use (c)hanged version or leave (d)eleted? c
270 use (c)hanged version or leave (d)eleted? c
258 remote turned local largefile f into a normal file
271 remote turned local largefile f into a normal file
259 keep (l)argefile or use (n)ormal file? l
272 keep (l)argefile or use (n)ormal file? l
260 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
273 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
261 (branch merge, don't forget to commit)
274 (branch merge, don't forget to commit)
262 getting changed largefiles
275 getting changed largefiles
263 1 largefiles updated, 0 removed
276 1 largefiles updated, 0 removed
264 $ cat f
277 $ cat f
265 large
278 large
266
279
267 $ hg up -Cqr large
280 $ hg up -Cqr large
268 $ ( echo c; echo n ) | hg merge -r normal2 --config ui.interactive=Yes
281 $ ( echo c; echo n ) | hg merge -r normal2 --config ui.interactive=Yes
269 remote changed f which local deleted
282 remote changed f which local deleted
270 use (c)hanged version or leave (d)eleted? remote turned local largefile f into a normal file
283 use (c)hanged version or leave (d)eleted? remote turned local largefile f into a normal file
271 keep (l)argefile or use (n)ormal file? 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
284 keep (l)argefile or use (n)ormal file? 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
272 (branch merge, don't forget to commit)
285 (branch merge, don't forget to commit)
273 getting changed largefiles
286 getting changed largefiles
274 0 largefiles updated, 0 removed
287 0 largefiles updated, 0 removed
275 $ cat f
288 $ cat f
276 normal2
289 normal2
277
290
278 $ hg up -Cqr large
291 $ hg up -Cqr large
279 $ echo d | hg merge -r normal2 --config ui.interactive=Yes
292 $ echo d | hg merge -r normal2 --config ui.interactive=Yes
280 remote changed f which local deleted
293 remote changed f which local deleted
281 use (c)hanged version or leave (d)eleted? 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 use (c)hanged version or leave (d)eleted? 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
282 (branch merge, don't forget to commit)
295 (branch merge, don't forget to commit)
283 getting changed largefiles
296 getting changed largefiles
284 0 largefiles updated, 0 removed
297 0 largefiles updated, 0 removed
285 $ cat f
298 $ cat f
286 large
299 large
287
300
288 Ancestor: large Parent: large= Parent: normal result: normal
301 Ancestor: large Parent: large= Parent: normal result: normal
289
302
290 $ hg up -Cqr large=
303 $ hg up -Cqr large=
291 $ hg merge -r normal
304 $ hg merge -r normal
292 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
305 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
293 (branch merge, don't forget to commit)
306 (branch merge, don't forget to commit)
294 getting changed largefiles
307 getting changed largefiles
295 0 largefiles updated, 0 removed
308 0 largefiles updated, 0 removed
296 $ cat f
309 $ cat f
297 normal
310 normal
298
311
299 swap
312 swap
300
313
301 $ hg up -Cqr normal
314 $ hg up -Cqr normal
302 $ hg merge -r large=
315 $ hg merge -r large=
303 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 (branch merge, don't forget to commit)
317 (branch merge, don't forget to commit)
305 $ cat f
318 $ cat f
306 normal
319 normal
307
320
308 Ancestor: large Parent: large2 Parent: normal result: ?
321 Ancestor: large Parent: large2 Parent: normal result: ?
309 (annoying extra prompt ... but it do not do any serious harm)
322 (annoying extra prompt ... but it do not do any serious harm)
310
323
311 $ hg up -Cqr large2
324 $ hg up -Cqr large2
312 $ hg merge -r normal
325 $ hg merge -r normal
313 local changed .hglf/f which remote deleted
326 local changed .hglf/f which remote deleted
314 use (c)hanged version or (d)elete? c
327 use (c)hanged version or (d)elete? c
315 remote turned local largefile f into a normal file
328 remote turned local largefile f into a normal file
316 keep (l)argefile or use (n)ormal file? l
329 keep (l)argefile or use (n)ormal file? l
317 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
330 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
318 (branch merge, don't forget to commit)
331 (branch merge, don't forget to commit)
319 getting changed largefiles
332 getting changed largefiles
320 1 largefiles updated, 0 removed
333 1 largefiles updated, 0 removed
321 $ cat f
334 $ cat f
322 large2
335 large2
323
336
324 $ hg up -Cqr large2
337 $ hg up -Cqr large2
325 $ echo d | hg merge -r normal --config ui.interactive=Yes
338 $ echo d | hg merge -r normal --config ui.interactive=Yes
326 local changed .hglf/f which remote deleted
339 local changed .hglf/f which remote deleted
327 use (c)hanged version or (d)elete? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
340 use (c)hanged version or (d)elete? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
328 (branch merge, don't forget to commit)
341 (branch merge, don't forget to commit)
329 getting changed largefiles
342 getting changed largefiles
330 0 largefiles updated, 0 removed
343 0 largefiles updated, 0 removed
331 $ cat f
344 $ cat f
332 normal
345 normal
333
346
334 swap
347 swap
335
348
336 $ hg up -Cqr normal
349 $ hg up -Cqr normal
337 $ hg merge -r large2
350 $ hg merge -r large2
338 remote changed .hglf/f which local deleted
351 remote changed .hglf/f which local deleted
339 use (c)hanged version or leave (d)eleted? c
352 use (c)hanged version or leave (d)eleted? c
340 remote turned local normal file f into a largefile
353 remote turned local normal file f into a largefile
341 use (l)argefile or keep (n)ormal file? l
354 use (l)argefile or keep (n)ormal file? l
342 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
355 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
343 (branch merge, don't forget to commit)
356 (branch merge, don't forget to commit)
344 getting changed largefiles
357 getting changed largefiles
345 1 largefiles updated, 0 removed
358 1 largefiles updated, 0 removed
346 $ cat f
359 $ cat f
347 large2
360 large2
348
361
349 $ hg up -Cqr normal
362 $ hg up -Cqr normal
350 $ echo d | hg merge -r large2 --config ui.interactive=Yes
363 $ echo d | hg merge -r large2 --config ui.interactive=Yes
351 remote changed .hglf/f which local deleted
364 remote changed .hglf/f which local deleted
352 use (c)hanged version or leave (d)eleted? 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 use (c)hanged version or leave (d)eleted? 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
353 (branch merge, don't forget to commit)
366 (branch merge, don't forget to commit)
354 $ cat f
367 $ cat f
355 normal
368 normal
356
369
357 $ cd ..
370 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now