##// END OF EJS Templates
largefiles: add examination of exec bit in "hg status --rev REV" case...
FUJIWARA Katsunori -
r23090:24600c9d stable
parent child Browse files
Show More
@@ -1,477 +1,480 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 '''setup for largefiles repositories: reposetup'''
10 10 import copy
11 11 import os
12 12
13 13 from mercurial import error, manifest, match as match_, util
14 14 from mercurial.i18n import _
15 15 from mercurial import localrepo, scmutil
16 16
17 17 import lfcommands
18 18 import lfutil
19 19
20 20 def reposetup(ui, repo):
21 21 # wire repositories should be given new wireproto functions
22 22 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
23 23 if not repo.local():
24 24 return
25 25
26 26 class lfilesrepo(repo.__class__):
27 27 lfstatus = False
28 28 def status_nolfiles(self, *args, **kwargs):
29 29 return super(lfilesrepo, self).status(*args, **kwargs)
30 30
31 31 # When lfstatus is set, return a context that gives the names
32 32 # of largefiles instead of their corresponding standins and
33 33 # identifies the largefiles as always binary, regardless of
34 34 # their actual contents.
35 35 def __getitem__(self, changeid):
36 36 ctx = super(lfilesrepo, self).__getitem__(changeid)
37 37 if self.lfstatus:
38 38 class lfilesmanifestdict(manifest.manifestdict):
39 39 def __contains__(self, filename):
40 40 orig = super(lfilesmanifestdict, self).__contains__
41 41 return orig(filename) or orig(lfutil.standin(filename))
42 42 class lfilesctx(ctx.__class__):
43 43 def files(self):
44 44 filenames = super(lfilesctx, self).files()
45 45 return [lfutil.splitstandin(f) or f for f in filenames]
46 46 def manifest(self):
47 47 man1 = super(lfilesctx, self).manifest()
48 48 man1.__class__ = lfilesmanifestdict
49 49 return man1
50 50 def filectx(self, path, fileid=None, filelog=None):
51 51 orig = super(lfilesctx, self).filectx
52 52 try:
53 53 if filelog is not None:
54 54 result = orig(path, fileid, filelog)
55 55 else:
56 56 result = orig(path, fileid)
57 57 except error.LookupError:
58 58 # Adding a null character will cause Mercurial to
59 59 # identify this as a binary file.
60 60 if filelog is not None:
61 61 result = orig(lfutil.standin(path), fileid,
62 62 filelog)
63 63 else:
64 64 result = orig(lfutil.standin(path), fileid)
65 65 olddata = result.data
66 66 result.data = lambda: olddata() + '\0'
67 67 return result
68 68 ctx.__class__ = lfilesctx
69 69 return ctx
70 70
71 71 # Figure out the status of big files and insert them into the
72 72 # appropriate list in the result. Also removes standin files
73 73 # from the listing. Revert to the original status if
74 74 # self.lfstatus is False.
75 75 # XXX large file status is buggy when used on repo proxy.
76 76 # XXX this needs to be investigated.
77 77 @localrepo.unfilteredmethod
78 78 def status(self, node1='.', node2=None, match=None, ignored=False,
79 79 clean=False, unknown=False, listsubrepos=False):
80 80 listignored, listclean, listunknown = ignored, clean, unknown
81 81 orig = super(lfilesrepo, self).status
82 82 if not self.lfstatus:
83 83 return orig(node1, node2, match, listignored, listclean,
84 84 listunknown, listsubrepos)
85 85
86 86 # some calls in this function rely on the old version of status
87 87 self.lfstatus = False
88 88 ctx1 = self[node1]
89 89 ctx2 = self[node2]
90 90 working = ctx2.rev() is None
91 91 parentworking = working and ctx1 == self['.']
92 92
93 93 if match is None:
94 94 match = match_.always(self.root, self.getcwd())
95 95
96 96 wlock = None
97 97 try:
98 98 try:
99 99 # updating the dirstate is optional
100 100 # so we don't wait on the lock
101 101 wlock = self.wlock(False)
102 102 except error.LockError:
103 103 pass
104 104
105 105 # First check if there were files specified on the
106 106 # command line. If there were, and none of them were
107 107 # largefiles, we should just bail here and let super
108 108 # handle it -- thus gaining a big performance boost.
109 109 lfdirstate = lfutil.openlfdirstate(ui, self)
110 110 if match.files() and not match.anypats():
111 111 for f in lfdirstate:
112 112 if match(f):
113 113 break
114 114 else:
115 115 return orig(node1, node2, match, listignored, listclean,
116 116 listunknown, listsubrepos)
117 117
118 118 # Create a copy of match that matches standins instead
119 119 # of largefiles.
120 120 def tostandins(files):
121 121 if not working:
122 122 return files
123 123 newfiles = []
124 124 dirstate = self.dirstate
125 125 for f in files:
126 126 sf = lfutil.standin(f)
127 127 if sf in dirstate:
128 128 newfiles.append(sf)
129 129 elif sf in dirstate.dirs():
130 130 # Directory entries could be regular or
131 131 # standin, check both
132 132 newfiles.extend((f, sf))
133 133 else:
134 134 newfiles.append(f)
135 135 return newfiles
136 136
137 137 m = copy.copy(match)
138 138 m._files = tostandins(m._files)
139 139
140 140 result = orig(node1, node2, m, ignored, clean, unknown,
141 141 listsubrepos)
142 142 if working:
143 143
144 144 def sfindirstate(f):
145 145 sf = lfutil.standin(f)
146 146 dirstate = self.dirstate
147 147 return sf in dirstate or sf in dirstate.dirs()
148 148
149 149 match._files = [f for f in match._files
150 150 if sfindirstate(f)]
151 151 # Don't waste time getting the ignored and unknown
152 152 # files from lfdirstate
153 153 unsure, s = lfdirstate.status(match, [], False, listclean,
154 154 False)
155 155 (modified, added, removed, clean) = (s.modified, s.added,
156 156 s.removed, s.clean)
157 157 if parentworking:
158 158 for lfile in unsure:
159 159 standin = lfutil.standin(lfile)
160 160 if standin not in ctx1:
161 161 # from second parent
162 162 modified.append(lfile)
163 163 elif ctx1[standin].data().strip() \
164 164 != lfutil.hashfile(self.wjoin(lfile)):
165 165 modified.append(lfile)
166 166 else:
167 167 if listclean:
168 168 clean.append(lfile)
169 169 lfdirstate.normal(lfile)
170 170 else:
171 171 tocheck = unsure + modified + added + clean
172 172 modified, added, clean = [], [], []
173 173
174 174 for lfile in tocheck:
175 175 standin = lfutil.standin(lfile)
176 176 if standin in ctx1:
177 if ctx1[standin].data().strip() != \
178 lfutil.hashfile(self.wjoin(lfile)):
177 abslfile = self.wjoin(lfile)
178 if ((ctx1[standin].data().strip() !=
179 lfutil.hashfile(abslfile)) or
180 (('x' in ctx1.flags(standin)) !=
181 bool(lfutil.getexecutable(abslfile)))):
179 182 modified.append(lfile)
180 183 elif listclean:
181 184 clean.append(lfile)
182 185 else:
183 186 added.append(lfile)
184 187
185 188 # at this point, 'removed' contains largefiles
186 189 # marked as 'R' in the working context.
187 190 # then, largefiles not managed also in the target
188 191 # context should be excluded from 'removed'.
189 192 removed = [lfile for lfile in removed
190 193 if lfutil.standin(lfile) in ctx1]
191 194
192 195 # Standins no longer found in lfdirstate has been
193 196 # removed
194 197 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
195 198 lfile = lfutil.splitstandin(standin)
196 199 if not match(lfile):
197 200 continue
198 201 if lfile not in lfdirstate:
199 202 removed.append(lfile)
200 203
201 204 # Filter result lists
202 205 result = list(result)
203 206
204 207 # Largefiles are not really removed when they're
205 208 # still in the normal dirstate. Likewise, normal
206 209 # files are not really removed if they are still in
207 210 # lfdirstate. This happens in merges where files
208 211 # change type.
209 212 removed = [f for f in removed
210 213 if f not in self.dirstate]
211 214 result[2] = [f for f in result[2]
212 215 if f not in lfdirstate]
213 216
214 217 lfiles = set(lfdirstate._map)
215 218 # Unknown files
216 219 result[4] = set(result[4]).difference(lfiles)
217 220 # Ignored files
218 221 result[5] = set(result[5]).difference(lfiles)
219 222 # combine normal files and largefiles
220 223 normals = [[fn for fn in filelist
221 224 if not lfutil.isstandin(fn)]
222 225 for filelist in result]
223 226 lfstatus = (modified, added, removed, s.deleted, [], [],
224 227 clean)
225 228 result = [sorted(list1 + list2)
226 229 for (list1, list2) in zip(normals, lfstatus)]
227 230 else: # not against working directory
228 231 result = [[lfutil.splitstandin(f) or f for f in items]
229 232 for items in result]
230 233
231 234 if wlock:
232 235 lfdirstate.write()
233 236
234 237 finally:
235 238 if wlock:
236 239 wlock.release()
237 240
238 241 self.lfstatus = True
239 242 return scmutil.status(*result)
240 243
241 244 # As part of committing, copy all of the largefiles into the
242 245 # cache.
243 246 def commitctx(self, *args, **kwargs):
244 247 node = super(lfilesrepo, self).commitctx(*args, **kwargs)
245 248 lfutil.copyalltostore(self, node)
246 249 return node
247 250
248 251 # Before commit, largefile standins have not had their
249 252 # contents updated to reflect the hash of their largefile.
250 253 # Do that here.
251 254 def commit(self, text="", user=None, date=None, match=None,
252 255 force=False, editor=False, extra={}):
253 256 orig = super(lfilesrepo, self).commit
254 257
255 258 wlock = self.wlock()
256 259 try:
257 260 # Case 0: Automated committing
258 261 #
259 262 # While automated committing (like rebase, transplant
260 263 # and so on), this code path is used to avoid:
261 264 # (1) updating standins, because standins should
262 265 # be already updated at this point
263 266 # (2) aborting when stadnins are matched by "match",
264 267 # because automated committing may specify them directly
265 268 #
266 269 if getattr(self, "_isrebasing", False) or \
267 270 getattr(self, "_istransplanting", False):
268 271 result = orig(text=text, user=user, date=date, match=match,
269 272 force=force, editor=editor, extra=extra)
270 273
271 274 if result:
272 275 lfdirstate = lfutil.openlfdirstate(ui, self)
273 276 for f in self[result].files():
274 277 if lfutil.isstandin(f):
275 278 lfile = lfutil.splitstandin(f)
276 279 lfutil.synclfdirstate(self, lfdirstate, lfile,
277 280 False)
278 281 lfdirstate.write()
279 282
280 283 return result
281 284 # Case 1: user calls commit with no specific files or
282 285 # include/exclude patterns: refresh and commit all files that
283 286 # are "dirty".
284 287 if ((match is None) or
285 288 (not match.anypats() and not match.files())):
286 289 # Spend a bit of time here to get a list of files we know
287 290 # are modified so we can compare only against those.
288 291 # It can cost a lot of time (several seconds)
289 292 # otherwise to update all standins if the largefiles are
290 293 # large.
291 294 lfdirstate = lfutil.openlfdirstate(ui, self)
292 295 dirtymatch = match_.always(self.root, self.getcwd())
293 296 unsure, s = lfdirstate.status(dirtymatch, [], False, False,
294 297 False)
295 298 modifiedfiles = unsure + s.modified + s.added + s.removed
296 299 lfiles = lfutil.listlfiles(self)
297 300 # this only loops through largefiles that exist (not
298 301 # removed/renamed)
299 302 for lfile in lfiles:
300 303 if lfile in modifiedfiles:
301 304 if os.path.exists(
302 305 self.wjoin(lfutil.standin(lfile))):
303 306 # this handles the case where a rebase is being
304 307 # performed and the working copy is not updated
305 308 # yet.
306 309 if os.path.exists(self.wjoin(lfile)):
307 310 lfutil.updatestandin(self,
308 311 lfutil.standin(lfile))
309 312 lfdirstate.normal(lfile)
310 313
311 314 result = orig(text=text, user=user, date=date, match=match,
312 315 force=force, editor=editor, extra=extra)
313 316
314 317 if result is not None:
315 318 for lfile in lfdirstate:
316 319 if lfile in modifiedfiles:
317 320 if (not os.path.exists(self.wjoin(
318 321 lfutil.standin(lfile)))) or \
319 322 (not os.path.exists(self.wjoin(lfile))):
320 323 lfdirstate.drop(lfile)
321 324
322 325 # This needs to be after commit; otherwise precommit hooks
323 326 # get the wrong status
324 327 lfdirstate.write()
325 328 return result
326 329
327 330 lfiles = lfutil.listlfiles(self)
328 331 match._files = self._subdirlfs(match.files(), lfiles)
329 332
330 333 # Case 2: user calls commit with specified patterns: refresh
331 334 # any matching big files.
332 335 smatcher = lfutil.composestandinmatcher(self, match)
333 336 standins = self.dirstate.walk(smatcher, [], False, False)
334 337
335 338 # No matching big files: get out of the way and pass control to
336 339 # the usual commit() method.
337 340 if not standins:
338 341 return orig(text=text, user=user, date=date, match=match,
339 342 force=force, editor=editor, extra=extra)
340 343
341 344 # Refresh all matching big files. It's possible that the
342 345 # commit will end up failing, in which case the big files will
343 346 # stay refreshed. No harm done: the user modified them and
344 347 # asked to commit them, so sooner or later we're going to
345 348 # refresh the standins. Might as well leave them refreshed.
346 349 lfdirstate = lfutil.openlfdirstate(ui, self)
347 350 for standin in standins:
348 351 lfile = lfutil.splitstandin(standin)
349 352 if lfdirstate[lfile] != 'r':
350 353 lfutil.updatestandin(self, standin)
351 354 lfdirstate.normal(lfile)
352 355 else:
353 356 lfdirstate.drop(lfile)
354 357
355 358 # Cook up a new matcher that only matches regular files or
356 359 # standins corresponding to the big files requested by the
357 360 # user. Have to modify _files to prevent commit() from
358 361 # complaining "not tracked" for big files.
359 362 match = copy.copy(match)
360 363 origmatchfn = match.matchfn
361 364
362 365 # Check both the list of largefiles and the list of
363 366 # standins because if a largefile was removed, it
364 367 # won't be in the list of largefiles at this point
365 368 match._files += sorted(standins)
366 369
367 370 actualfiles = []
368 371 for f in match._files:
369 372 fstandin = lfutil.standin(f)
370 373
371 374 # ignore known largefiles and standins
372 375 if f in lfiles or fstandin in standins:
373 376 continue
374 377
375 378 actualfiles.append(f)
376 379 match._files = actualfiles
377 380
378 381 def matchfn(f):
379 382 if origmatchfn(f):
380 383 return f not in lfiles
381 384 else:
382 385 return f in standins
383 386
384 387 match.matchfn = matchfn
385 388 result = orig(text=text, user=user, date=date, match=match,
386 389 force=force, editor=editor, extra=extra)
387 390 # This needs to be after commit; otherwise precommit hooks
388 391 # get the wrong status
389 392 lfdirstate.write()
390 393 return result
391 394 finally:
392 395 wlock.release()
393 396
394 397 def push(self, remote, force=False, revs=None, newbranch=False):
395 398 if remote.local():
396 399 missing = set(self.requirements) - remote.local().supported
397 400 if missing:
398 401 msg = _("required features are not"
399 402 " supported in the destination:"
400 403 " %s") % (', '.join(sorted(missing)))
401 404 raise util.Abort(msg)
402 405 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
403 406 newbranch=newbranch)
404 407
405 408 def _subdirlfs(self, files, lfiles):
406 409 '''
407 410 Adjust matched file list
408 411 If we pass a directory to commit whose only commitable files
409 412 are largefiles, the core commit code aborts before finding
410 413 the largefiles.
411 414 So we do the following:
412 415 For directories that only have largefiles as matches,
413 416 we explicitly add the largefiles to the match list and remove
414 417 the directory.
415 418 In other cases, we leave the match list unmodified.
416 419 '''
417 420 actualfiles = []
418 421 dirs = []
419 422 regulars = []
420 423
421 424 for f in files:
422 425 if lfutil.isstandin(f + '/'):
423 426 raise util.Abort(
424 427 _('file "%s" is a largefile standin') % f,
425 428 hint=('commit the largefile itself instead'))
426 429 # Scan directories
427 430 if os.path.isdir(self.wjoin(f)):
428 431 dirs.append(f)
429 432 else:
430 433 regulars.append(f)
431 434
432 435 for f in dirs:
433 436 matcheddir = False
434 437 d = self.dirstate.normalize(f) + '/'
435 438 # Check for matched normal files
436 439 for mf in regulars:
437 440 if self.dirstate.normalize(mf).startswith(d):
438 441 actualfiles.append(f)
439 442 matcheddir = True
440 443 break
441 444 if not matcheddir:
442 445 # If no normal match, manually append
443 446 # any matching largefiles
444 447 for lf in lfiles:
445 448 if self.dirstate.normalize(lf).startswith(d):
446 449 actualfiles.append(lf)
447 450 if not matcheddir:
448 451 actualfiles.append(lfutil.standin(f))
449 452 matcheddir = True
450 453 # Nothing in dir, so readd it
451 454 # and let commit reject it
452 455 if not matcheddir:
453 456 actualfiles.append(f)
454 457
455 458 # Always add normal files
456 459 actualfiles += regulars
457 460 return actualfiles
458 461
459 462 repo.__class__ = lfilesrepo
460 463
461 464 def prepushoutgoinghook(local, remote, outgoing):
462 465 if outgoing.missing:
463 466 toupload = set()
464 467 addfunc = lambda fn, lfhash: toupload.add(lfhash)
465 468 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
466 469 lfcommands.uploadlfiles(ui, local, remote, toupload)
467 470 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
468 471
469 472 def checkrequireslfiles(ui, repo, **kwargs):
470 473 if 'largefiles' not in repo.requirements and util.any(
471 474 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
472 475 repo.requirements.add('largefiles')
473 476 repo._writerequirements()
474 477
475 478 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
476 479 'largefiles')
477 480 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
@@ -1,555 +1,582 b''
1 1 This file focuses mainly on updating largefiles in the working
2 2 directory (and ".hg/largefiles/dirstate")
3 3
4 4 $ cat >> $HGRCPATH <<EOF
5 5 > [ui]
6 6 > merge = internal:fail
7 7 > [extensions]
8 8 > largefiles =
9 9 > EOF
10 10
11 11 $ hg init repo
12 12 $ cd repo
13 13
14 14 $ echo large1 > large1
15 15 $ echo large2 > large2
16 16 $ hg add --large large1 large2
17 17 $ echo normal1 > normal1
18 18 $ hg add normal1
19 19 $ hg commit -m '#0'
20 20 $ echo 'large1 in #1' > large1
21 21 $ echo 'normal1 in #1' > normal1
22 22 $ hg commit -m '#1'
23 23 $ hg update -q -C 0
24 24 $ echo 'large2 in #2' > large2
25 25 $ hg commit -m '#2'
26 26 created new head
27 27
28 28 Test that "hg merge" updates largefiles from "other" correctly
29 29
30 30 (getting largefiles from "other" normally)
31 31
32 32 $ hg status -A large1
33 33 C large1
34 34 $ cat large1
35 35 large1
36 36 $ cat .hglf/large1
37 37 4669e532d5b2c093a78eca010077e708a071bb64
38 38 $ hg merge --config debug.dirstate.delaywrite=2
39 39 getting changed largefiles
40 40 1 largefiles updated, 0 removed
41 41 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 42 (branch merge, don't forget to commit)
43 43 $ hg status -A large1
44 44 M large1
45 45 $ cat large1
46 46 large1 in #1
47 47 $ cat .hglf/large1
48 48 58e24f733a964da346e2407a2bee99d9001184f5
49 49 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
50 50 -4669e532d5b2c093a78eca010077e708a071bb64
51 51 +58e24f733a964da346e2407a2bee99d9001184f5
52 52
53 53 (getting largefiles from "other" via conflict prompt)
54 54
55 55 $ hg update -q -C 2
56 56 $ echo 'large1 in #3' > large1
57 57 $ echo 'normal1 in #3' > normal1
58 58 $ hg commit -m '#3'
59 59 $ cat .hglf/large1
60 60 e5bb990443d6a92aaf7223813720f7566c9dd05b
61 61 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
62 62 > o
63 63 > EOF
64 64 largefile large1 has a merge conflict
65 65 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
66 66 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
67 67 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
68 68 merging normal1
69 69 warning: conflicts during merge.
70 70 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
71 71 getting changed largefiles
72 72 1 largefiles updated, 0 removed
73 73 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
74 74 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
75 75 [1]
76 76 $ hg status -A large1
77 77 M large1
78 78 $ cat large1
79 79 large1 in #1
80 80 $ cat .hglf/large1
81 81 58e24f733a964da346e2407a2bee99d9001184f5
82 82
83 83 Test that "hg revert -r REV" updates largefiles from "REV" correctly
84 84
85 85 $ hg update -q -C 3
86 86 $ hg status -A large1
87 87 C large1
88 88 $ cat large1
89 89 large1 in #3
90 90 $ cat .hglf/large1
91 91 e5bb990443d6a92aaf7223813720f7566c9dd05b
92 92 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
93 93 -4669e532d5b2c093a78eca010077e708a071bb64
94 94 +58e24f733a964da346e2407a2bee99d9001184f5
95 95 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
96 96 $ hg status -A large1
97 97 M large1
98 98 $ cat large1
99 99 large1 in #1
100 100 $ cat .hglf/large1
101 101 58e24f733a964da346e2407a2bee99d9001184f5
102 102
103 103 Test that "hg rollback" restores status of largefiles correctly
104 104
105 105 $ hg update -C -q
106 106 $ hg remove large1
107 107 $ test -f .hglf/large1
108 108 [1]
109 109 $ hg forget large2
110 110 $ test -f .hglf/large2
111 111 [1]
112 112 $ echo largeX > largeX
113 113 $ hg add --large largeX
114 114 $ cat .hglf/largeX
115 115
116 116 $ hg commit -m 'will be rollback-ed soon'
117 117 $ echo largeY > largeY
118 118 $ hg add --large largeY
119 119 #if windows
120 120 $ hg status -A large1
121 121 large1: * (glob)
122 122 #else
123 123 $ hg status -A large1
124 124 large1: No such file or directory
125 125 #endif
126 126 $ hg status -A large2
127 127 ? large2
128 128 $ hg status -A largeX
129 129 C largeX
130 130 $ hg status -A largeY
131 131 A largeY
132 132 $ hg rollback
133 133 repository tip rolled back to revision 3 (undo commit)
134 134 working directory now based on revision 3
135 135 $ hg status -A large1
136 136 R large1
137 137 $ test -f .hglf/large1
138 138 [1]
139 139 $ hg status -A large2
140 140 R large2
141 141 $ test -f .hglf/large2
142 142 [1]
143 143 $ hg status -A largeX
144 144 A largeX
145 145 $ cat .hglf/largeX
146 146
147 147 $ hg status -A largeY
148 148 ? largeY
149 149 $ test -f .hglf/largeY
150 150 [1]
151 151
152 152 Test that "hg rollback" restores standins correctly
153 153
154 154 $ hg commit -m 'will be rollback-ed soon'
155 155 $ hg update -q -C 2
156 156 $ cat large1
157 157 large1
158 158 $ cat .hglf/large1
159 159 4669e532d5b2c093a78eca010077e708a071bb64
160 160 $ cat large2
161 161 large2 in #2
162 162 $ cat .hglf/large2
163 163 3cfce6277e7668985707b6887ce56f9f62f6ccd9
164 164
165 165 $ hg rollback -q -f
166 166 $ cat large1
167 167 large1
168 168 $ cat .hglf/large1
169 169 4669e532d5b2c093a78eca010077e708a071bb64
170 170 $ cat large2
171 171 large2 in #2
172 172 $ cat .hglf/large2
173 173 3cfce6277e7668985707b6887ce56f9f62f6ccd9
174 174
175 175 (rollback the parent of the working directory, when the parent of it
176 176 is not branch-tip)
177 177
178 178 $ hg update -q -C 1
179 179 $ cat .hglf/large1
180 180 58e24f733a964da346e2407a2bee99d9001184f5
181 181 $ cat .hglf/large2
182 182 1deebade43c8c498a3c8daddac0244dc55d1331d
183 183
184 184 $ echo normalX > normalX
185 185 $ hg add normalX
186 186 $ hg commit -m 'will be rollback-ed soon'
187 187 $ hg rollback -q
188 188
189 189 $ cat .hglf/large1
190 190 58e24f733a964da346e2407a2bee99d9001184f5
191 191 $ cat .hglf/large2
192 192 1deebade43c8c498a3c8daddac0244dc55d1331d
193 193
194 194 Test that "hg status" shows status of largefiles correctly just after
195 195 automated commit like rebase/transplant
196 196
197 197 $ cat >> .hg/hgrc <<EOF
198 198 > [extensions]
199 199 > rebase =
200 200 > strip =
201 201 > transplant =
202 202 > EOF
203 203 $ hg update -q -C 1
204 204 $ hg remove large1
205 205 $ echo largeX > largeX
206 206 $ hg add --large largeX
207 207 $ hg commit -m '#4'
208 208
209 209 $ hg rebase -s 1 -d 2 --keep
210 210 #if windows
211 211 $ hg status -A large1
212 212 large1: * (glob)
213 213 #else
214 214 $ hg status -A large1
215 215 large1: No such file or directory
216 216 #endif
217 217 $ hg status -A largeX
218 218 C largeX
219 219 $ hg strip -q 5
220 220
221 221 $ hg update -q -C 2
222 222 $ hg transplant -q 1 4
223 223 #if windows
224 224 $ hg status -A large1
225 225 large1: * (glob)
226 226 #else
227 227 $ hg status -A large1
228 228 large1: No such file or directory
229 229 #endif
230 230 $ hg status -A largeX
231 231 C largeX
232 232 $ hg strip -q 5
233 233
234 234 $ hg update -q -C 2
235 235 $ hg transplant -q --merge 1 --merge 4
236 236 #if windows
237 237 $ hg status -A large1
238 238 large1: * (glob)
239 239 #else
240 240 $ hg status -A large1
241 241 large1: No such file or directory
242 242 #endif
243 243 $ hg status -A largeX
244 244 C largeX
245 245 $ hg strip -q 5
246 246
247 247 Test that linear merge can detect modification (and conflict) correctly
248 248
249 249 (linear merge without conflict)
250 250
251 251 $ echo 'large2 for linear merge (no conflict)' > large2
252 252 $ hg update 3 --config debug.dirstate.delaywrite=2
253 253 getting changed largefiles
254 254 1 largefiles updated, 0 removed
255 255 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 256 $ hg status -A large2
257 257 M large2
258 258 $ cat large2
259 259 large2 for linear merge (no conflict)
260 260 $ cat .hglf/large2
261 261 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
262 262
263 263 (linear merge with conflict, choosing "other")
264 264
265 265 $ hg update -q -C 2
266 266 $ echo 'large1 for linear merge (conflict)' > large1
267 267 $ hg update 3 --config ui.interactive=True <<EOF
268 268 > o
269 269 > EOF
270 270 largefile large1 has a merge conflict
271 271 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
272 272 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
273 273 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
274 274 getting changed largefiles
275 275 1 largefiles updated, 0 removed
276 276 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
277 277 $ hg status -A large1
278 278 C large1
279 279 $ cat large1
280 280 large1 in #3
281 281 $ cat .hglf/large1
282 282 e5bb990443d6a92aaf7223813720f7566c9dd05b
283 283
284 284 (linear merge with conflict, choosing "local")
285 285
286 286 $ hg update -q -C 2
287 287 $ echo 'large1 for linear merge (conflict)' > large1
288 288 $ hg update 3 --config debug.dirstate.delaywrite=2
289 289 largefile large1 has a merge conflict
290 290 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
291 291 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
292 292 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
293 293 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
294 294 $ hg status -A large1
295 295 M large1
296 296 $ cat large1
297 297 large1 for linear merge (conflict)
298 298 $ cat .hglf/large1
299 299 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
300 300
301 301 Test a linear merge to a revision containing same-name normal file
302 302
303 303 $ hg update -q -C 3
304 304 $ hg remove large2
305 305 $ echo 'large2 as normal file' > large2
306 306 $ hg add large2
307 307 $ echo 'large3 as normal file' > large3
308 308 $ hg add large3
309 309 $ hg commit -m '#5'
310 310 $ hg manifest
311 311 .hglf/large1
312 312 large2
313 313 large3
314 314 normal1
315 315
316 316 (modified largefile is already switched to normal)
317 317
318 318 $ hg update -q -C 2
319 319 $ echo 'modified large2 for linear merge' > large2
320 320 $ hg update -q 5
321 321 local changed .hglf/large2 which remote deleted
322 322 use (c)hanged version or (d)elete? c
323 323 remote turned local largefile large2 into a normal file
324 324 keep (l)argefile or use (n)ormal file? l
325 325 $ hg debugdirstate --nodates | grep large2
326 326 a 0 -1 .hglf/large2
327 327 r 0 0 large2
328 328 $ hg status -A large2
329 329 A large2
330 330 $ cat large2
331 331 modified large2 for linear merge
332 332
333 333 (added largefile is already committed as normal)
334 334
335 335 $ hg update -q -C 2
336 336 $ echo 'large3 as large file for linear merge' > large3
337 337 $ hg add --large large3
338 338 $ hg update -q 5
339 339 remote turned local largefile large3 into a normal file
340 340 keep (l)argefile or use (n)ormal file? l
341 341 $ hg debugdirstate --nodates | grep large3
342 342 a 0 -1 .hglf/large3
343 343 r 0 0 large3
344 344 $ hg status -A large3
345 345 A large3
346 346 $ cat large3
347 347 large3 as large file for linear merge
348 348 $ rm -f large3 .hglf/large3
349 349
350 350 Test that the internal linear merging works correctly
351 351 (both heads are stripped to keep pairing of revision number and commit log)
352 352
353 353 $ hg update -q -C 2
354 354 $ hg strip 3 4
355 355 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-backup.hg (glob)
356 356 $ mv .hg/strip-backup/9530e27857f7-backup.hg $TESTTMP
357 357
358 358 (internal linear merging at "hg pull --update")
359 359
360 360 $ echo 'large1 for linear merge (conflict)' > large1
361 361 $ echo 'large2 for linear merge (conflict with normal file)' > large2
362 362 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
363 363 pulling from $TESTTMP/9530e27857f7-backup.hg (glob)
364 364 searching for changes
365 365 adding changesets
366 366 adding manifests
367 367 adding file changes
368 368 added 3 changesets with 5 changes to 5 files
369 369 local changed .hglf/large2 which remote deleted
370 370 use (c)hanged version or (d)elete? c
371 371 remote turned local largefile large2 into a normal file
372 372 keep (l)argefile or use (n)ormal file? l
373 373 largefile large1 has a merge conflict
374 374 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
375 375 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
376 376 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
377 377 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
378 378
379 379 $ hg status -A large1
380 380 M large1
381 381 $ cat large1
382 382 large1 for linear merge (conflict)
383 383 $ cat .hglf/large1
384 384 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
385 385 $ hg status -A large2
386 386 A large2
387 387 $ cat large2
388 388 large2 for linear merge (conflict with normal file)
389 389 $ cat .hglf/large2
390 390 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
391 391
392 392 (internal linear merging at "hg unbundle --update")
393 393
394 394 $ hg update -q -C 2
395 395 $ hg rollback -q
396 396
397 397 $ echo 'large1 for linear merge (conflict)' > large1
398 398 $ echo 'large2 for linear merge (conflict with normal file)' > large2
399 399 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-backup.hg
400 400 adding changesets
401 401 adding manifests
402 402 adding file changes
403 403 added 3 changesets with 5 changes to 5 files
404 404 local changed .hglf/large2 which remote deleted
405 405 use (c)hanged version or (d)elete? c
406 406 remote turned local largefile large2 into a normal file
407 407 keep (l)argefile or use (n)ormal file? l
408 408 largefile large1 has a merge conflict
409 409 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
410 410 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
411 411 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
412 412 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
413 413
414 414 $ hg status -A large1
415 415 M large1
416 416 $ cat large1
417 417 large1 for linear merge (conflict)
418 418 $ cat .hglf/large1
419 419 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
420 420 $ hg status -A large2
421 421 A large2
422 422 $ cat large2
423 423 large2 for linear merge (conflict with normal file)
424 424 $ cat .hglf/large2
425 425 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
426 426
427 427 (internal linear merging in subrepo at "hg update")
428 428
429 429 $ cd ..
430 430 $ hg init subparent
431 431 $ cd subparent
432 432
433 433 $ hg clone -q -u 2 ../repo sub
434 434 $ cat > .hgsub <<EOF
435 435 > sub = sub
436 436 > EOF
437 437 $ hg add .hgsub
438 438 $ hg commit -m '#0@parent'
439 439 $ cat .hgsubstate
440 440 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
441 441 $ hg -R sub update -q
442 442 $ hg commit -m '#1@parent'
443 443 $ cat .hgsubstate
444 444 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
445 445 $ hg update -q 0
446 446
447 447 $ echo 'large1 for linear merge (conflict)' > sub/large1
448 448 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
449 449 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
450 450 > m
451 451 > r
452 452 > c
453 453 > l
454 454 > l
455 455 > EOF
456 456 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
457 457 (M)erge, keep (l)ocal or keep (r)emote? m
458 458 subrepository sources for sub differ (in checked out version)
459 459 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
460 460 local changed .hglf/large2 which remote deleted
461 461 use (c)hanged version or (d)elete? c
462 462 remote turned local largefile large2 into a normal file
463 463 keep (l)argefile or use (n)ormal file? l
464 464 largefile large1 has a merge conflict
465 465 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
466 466 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
467 467 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
468 468 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
469 469 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 470
471 471 $ hg -R sub status -A sub/large1
472 472 M sub/large1
473 473 $ cat sub/large1
474 474 large1 for linear merge (conflict)
475 475 $ cat sub/.hglf/large1
476 476 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
477 477 $ hg -R sub status -A sub/large2
478 478 A sub/large2
479 479 $ cat sub/large2
480 480 large2 for linear merge (conflict with normal file)
481 481 $ cat sub/.hglf/large2
482 482 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
483 483
484 484 $ cd ..
485 485 $ cd repo
486 486
487 487 Test that rebase updates largefiles in the working directory even if
488 488 it is aborted by conflict.
489 489
490 490 $ hg update -q -C 3
491 491 $ cat .hglf/large1
492 492 e5bb990443d6a92aaf7223813720f7566c9dd05b
493 493 $ cat large1
494 494 large1 in #3
495 495 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
496 496 > o
497 497 > EOF
498 498 largefile large1 has a merge conflict
499 499 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
500 500 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
501 501 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
502 502 merging normal1
503 503 warning: conflicts during merge.
504 504 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
505 505 unresolved conflicts (see hg resolve, then hg rebase --continue)
506 506 [1]
507 507 $ cat .hglf/large1
508 508 58e24f733a964da346e2407a2bee99d9001184f5
509 509 $ cat large1
510 510 large1 in #1
511 511
512 512 $ hg rebase -q --abort
513 513 rebase aborted
514 514
515 515 Test that transplant updates largefiles, of which standins are safely
516 516 changed, even if it is aborted by conflict of other.
517 517
518 518 $ hg update -q -C 5
519 519 $ cat .hglf/large1
520 520 e5bb990443d6a92aaf7223813720f7566c9dd05b
521 521 $ cat large1
522 522 large1 in #3
523 523 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
524 524 +fa44618ea25181aff4f48b70428294790cec9f61
525 525 $ hg transplant 4
526 526 applying 07d6153b5c04
527 527 patching file .hglf/large1
528 528 Hunk #1 FAILED at 0
529 529 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
530 530 patch failed to apply
531 531 abort: fix up the merge and run hg transplant --continue
532 532 [255]
533 533 $ hg status -A large1
534 534 C large1
535 535 $ cat .hglf/large1
536 536 e5bb990443d6a92aaf7223813720f7566c9dd05b
537 537 $ cat large1
538 538 large1 in #3
539 539 $ hg status -A largeX
540 540 A largeX
541 541 $ cat .hglf/largeX
542 542 fa44618ea25181aff4f48b70428294790cec9f61
543 543 $ cat largeX
544 544 largeX
545 545
546 546 Test that "hg status" doesn't show removal of largefiles not managed
547 547 in the target context.
548 548
549 549 $ hg update -q -C 4
550 550 $ hg remove largeX
551 551 $ hg status -A largeX
552 552 R largeX
553 553 $ hg status -A --rev '.^1' largeX
554 554
555 #if execbit
556
557 Test that "hg status" against revisions other than parent notices exec
558 bit changes of largefiles.
559
560 $ hg update -q -C 4
561
562 (the case that large2 doesn't have exec bit in the target context but
563 in the working context)
564
565 $ chmod +x large2
566 $ hg status -A --rev 0 large2
567 M large2
568 $ hg commit -m 'chmod +x large2'
569
570 (the case that large2 has exec bit in the target context but not in
571 the working context)
572
573 $ echo dummy > dummy
574 $ hg add dummy
575 $ hg commit -m 'revision for separation'
576 $ chmod -x large2
577 $ hg status -A --rev '.^1' large2
578 M large2
579
580 #endif
581
555 582 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now