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