##// END OF EJS Templates
merge: make in-memory changes visible to external update hooks...
FUJIWARA Katsunori -
r26752:949e8c62 default
parent child Browse files
Show More
@@ -1,1284 +1,1282 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import os
12 12 import shutil
13 13 import struct
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 bin,
18 18 hex,
19 19 nullid,
20 20 nullrev,
21 21 )
22 22 from . import (
23 23 copies,
24 24 destutil,
25 25 error,
26 26 filemerge,
27 27 obsolete,
28 28 subrepo,
29 29 util,
30 30 worker,
31 31 )
32 32
33 33 _pack = struct.pack
34 34 _unpack = struct.unpack
35 35
36 36 def _droponode(data):
37 37 # used for compatibility for v1
38 38 bits = data.split('\0')
39 39 bits = bits[:-2] + bits[-1:]
40 40 return '\0'.join(bits)
41 41
42 42 class mergestate(object):
43 43 '''track 3-way merge state of individual files
44 44
45 45 it is stored on disk when needed. Two file are used, one with an old
46 46 format, one with a new format. Both contains similar data, but the new
47 47 format can store new kinds of field.
48 48
49 49 Current new format is a list of arbitrary record of the form:
50 50
51 51 [type][length][content]
52 52
53 53 Type is a single character, length is a 4 bytes integer, content is an
54 54 arbitrary suites of bytes of length `length`.
55 55
56 56 Type should be a letter. Capital letter are mandatory record, Mercurial
57 57 should abort if they are unknown. lower case record can be safely ignored.
58 58
59 59 Currently known record:
60 60
61 61 L: the node of the "local" part of the merge (hexified version)
62 62 O: the node of the "other" part of the merge (hexified version)
63 63 F: a file to be merged entry
64 64 D: a file that the external merge driver will merge internally
65 65 (experimental)
66 66 m: the external merge driver defined for this merge plus its run state
67 67 (experimental)
68 68
69 69 Merge driver run states (experimental):
70 70 u: driver-resolved files unmarked -- needs to be run next time we're about
71 71 to resolve or commit
72 72 m: driver-resolved files marked -- only needs to be run before commit
73 73 s: success/skipped -- does not need to be run any more
74 74 '''
75 75 statepathv1 = 'merge/state'
76 76 statepathv2 = 'merge/state2'
77 77
78 78 def __init__(self, repo):
79 79 self._repo = repo
80 80 self._dirty = False
81 81 self._read()
82 82
83 83 def reset(self, node=None, other=None):
84 84 self._state = {}
85 85 self._local = None
86 86 self._other = None
87 87 if node:
88 88 self._local = node
89 89 self._other = other
90 90 self._mdstate = 'u'
91 91 shutil.rmtree(self._repo.join('merge'), True)
92 92 self._dirty = False
93 93
94 94 def _read(self):
95 95 """Analyse each record content to restore a serialized state from disk
96 96
97 97 This function process "record" entry produced by the de-serialization
98 98 of on disk file.
99 99 """
100 100 self._state = {}
101 101 self._local = None
102 102 self._other = None
103 103 self._mdstate = 'u'
104 104 records = self._readrecords()
105 105 for rtype, record in records:
106 106 if rtype == 'L':
107 107 self._local = bin(record)
108 108 elif rtype == 'O':
109 109 self._other = bin(record)
110 110 elif rtype == 'm':
111 111 bits = record.split('\0', 1)
112 112 mdstate = bits[1]
113 113 if len(mdstate) != 1 or mdstate not in 'ums':
114 114 # the merge driver should be idempotent, so just rerun it
115 115 mdstate = 'u'
116 116
117 117 # protect against the following:
118 118 # - A configures a malicious merge driver in their hgrc, then
119 119 # pauses the merge
120 120 # - A edits their hgrc to remove references to the merge driver
121 121 # - A gives a copy of their entire repo, including .hg, to B
122 122 # - B inspects .hgrc and finds it to be clean
123 123 # - B then continues the merge and the malicious merge driver
124 124 # gets invoked
125 125 if self.mergedriver != bits[0]:
126 126 raise error.ConfigError(
127 127 _("merge driver changed since merge started"),
128 128 hint=_("revert merge driver change or abort merge"))
129 129 self._mdstate = mdstate
130 130 elif rtype in 'FD':
131 131 bits = record.split('\0')
132 132 self._state[bits[0]] = bits[1:]
133 133 elif not rtype.islower():
134 134 raise error.Abort(_('unsupported merge state record: %s')
135 135 % rtype)
136 136 self._dirty = False
137 137
138 138 def _readrecords(self):
139 139 """Read merge state from disk and return a list of record (TYPE, data)
140 140
141 141 We read data from both v1 and v2 files and decide which one to use.
142 142
143 143 V1 has been used by version prior to 2.9.1 and contains less data than
144 144 v2. We read both versions and check if no data in v2 contradicts
145 145 v1. If there is not contradiction we can safely assume that both v1
146 146 and v2 were written at the same time and use the extract data in v2. If
147 147 there is contradiction we ignore v2 content as we assume an old version
148 148 of Mercurial has overwritten the mergestate file and left an old v2
149 149 file around.
150 150
151 151 returns list of record [(TYPE, data), ...]"""
152 152 v1records = self._readrecordsv1()
153 153 v2records = self._readrecordsv2()
154 154 if self._v1v2match(v1records, v2records):
155 155 return v2records
156 156 else:
157 157 # v1 file is newer than v2 file, use it
158 158 # we have to infer the "other" changeset of the merge
159 159 # we cannot do better than that with v1 of the format
160 160 mctx = self._repo[None].parents()[-1]
161 161 v1records.append(('O', mctx.hex()))
162 162 # add place holder "other" file node information
163 163 # nobody is using it yet so we do no need to fetch the data
164 164 # if mctx was wrong `mctx[bits[-2]]` may fails.
165 165 for idx, r in enumerate(v1records):
166 166 if r[0] == 'F':
167 167 bits = r[1].split('\0')
168 168 bits.insert(-2, '')
169 169 v1records[idx] = (r[0], '\0'.join(bits))
170 170 return v1records
171 171
172 172 def _v1v2match(self, v1records, v2records):
173 173 oldv2 = set() # old format version of v2 record
174 174 for rec in v2records:
175 175 if rec[0] == 'L':
176 176 oldv2.add(rec)
177 177 elif rec[0] == 'F':
178 178 # drop the onode data (not contained in v1)
179 179 oldv2.add(('F', _droponode(rec[1])))
180 180 for rec in v1records:
181 181 if rec not in oldv2:
182 182 return False
183 183 else:
184 184 return True
185 185
186 186 def _readrecordsv1(self):
187 187 """read on disk merge state for version 1 file
188 188
189 189 returns list of record [(TYPE, data), ...]
190 190
191 191 Note: the "F" data from this file are one entry short
192 192 (no "other file node" entry)
193 193 """
194 194 records = []
195 195 try:
196 196 f = self._repo.vfs(self.statepathv1)
197 197 for i, l in enumerate(f):
198 198 if i == 0:
199 199 records.append(('L', l[:-1]))
200 200 else:
201 201 records.append(('F', l[:-1]))
202 202 f.close()
203 203 except IOError as err:
204 204 if err.errno != errno.ENOENT:
205 205 raise
206 206 return records
207 207
208 208 def _readrecordsv2(self):
209 209 """read on disk merge state for version 2 file
210 210
211 211 returns list of record [(TYPE, data), ...]
212 212 """
213 213 records = []
214 214 try:
215 215 f = self._repo.vfs(self.statepathv2)
216 216 data = f.read()
217 217 off = 0
218 218 end = len(data)
219 219 while off < end:
220 220 rtype = data[off]
221 221 off += 1
222 222 length = _unpack('>I', data[off:(off + 4)])[0]
223 223 off += 4
224 224 record = data[off:(off + length)]
225 225 off += length
226 226 records.append((rtype, record))
227 227 f.close()
228 228 except IOError as err:
229 229 if err.errno != errno.ENOENT:
230 230 raise
231 231 return records
232 232
233 233 @util.propertycache
234 234 def mergedriver(self):
235 235 return self._repo.ui.config('experimental', 'mergedriver')
236 236
237 237 def active(self):
238 238 """Whether mergestate is active.
239 239
240 240 Returns True if there appears to be mergestate. This is a rough proxy
241 241 for "is a merge in progress."
242 242 """
243 243 # Check local variables before looking at filesystem for performance
244 244 # reasons.
245 245 return bool(self._local) or bool(self._state) or \
246 246 self._repo.vfs.exists(self.statepathv1) or \
247 247 self._repo.vfs.exists(self.statepathv2)
248 248
249 249 def commit(self):
250 250 """Write current state on disk (if necessary)"""
251 251 if self._dirty:
252 252 records = []
253 253 records.append(('L', hex(self._local)))
254 254 records.append(('O', hex(self._other)))
255 255 if self.mergedriver:
256 256 records.append(('m', '\0'.join([
257 257 self.mergedriver, self._mdstate])))
258 258 for d, v in self._state.iteritems():
259 259 if v[0] == 'd':
260 260 records.append(('D', '\0'.join([d] + v)))
261 261 else:
262 262 records.append(('F', '\0'.join([d] + v)))
263 263 self._writerecords(records)
264 264 self._dirty = False
265 265
266 266 def _writerecords(self, records):
267 267 """Write current state on disk (both v1 and v2)"""
268 268 self._writerecordsv1(records)
269 269 self._writerecordsv2(records)
270 270
271 271 def _writerecordsv1(self, records):
272 272 """Write current state on disk in a version 1 file"""
273 273 f = self._repo.vfs(self.statepathv1, 'w')
274 274 irecords = iter(records)
275 275 lrecords = irecords.next()
276 276 assert lrecords[0] == 'L'
277 277 f.write(hex(self._local) + '\n')
278 278 for rtype, data in irecords:
279 279 if rtype == 'F':
280 280 f.write('%s\n' % _droponode(data))
281 281 f.close()
282 282
283 283 def _writerecordsv2(self, records):
284 284 """Write current state on disk in a version 2 file"""
285 285 f = self._repo.vfs(self.statepathv2, 'w')
286 286 for key, data in records:
287 287 assert len(key) == 1
288 288 format = '>sI%is' % len(data)
289 289 f.write(_pack(format, key, len(data), data))
290 290 f.close()
291 291
292 292 def add(self, fcl, fco, fca, fd):
293 293 """add a new (potentially?) conflicting file the merge state
294 294 fcl: file context for local,
295 295 fco: file context for remote,
296 296 fca: file context for ancestors,
297 297 fd: file path of the resulting merge.
298 298
299 299 note: also write the local version to the `.hg/merge` directory.
300 300 """
301 301 hash = util.sha1(fcl.path()).hexdigest()
302 302 self._repo.vfs.write('merge/' + hash, fcl.data())
303 303 self._state[fd] = ['u', hash, fcl.path(),
304 304 fca.path(), hex(fca.filenode()),
305 305 fco.path(), hex(fco.filenode()),
306 306 fcl.flags()]
307 307 self._dirty = True
308 308
309 309 def __contains__(self, dfile):
310 310 return dfile in self._state
311 311
312 312 def __getitem__(self, dfile):
313 313 return self._state[dfile][0]
314 314
315 315 def __iter__(self):
316 316 return iter(sorted(self._state))
317 317
318 318 def files(self):
319 319 return self._state.keys()
320 320
321 321 def mark(self, dfile, state):
322 322 self._state[dfile][0] = state
323 323 self._dirty = True
324 324
325 325 def unresolved(self):
326 326 """Obtain the paths of unresolved files."""
327 327
328 328 for f, entry in self._state.items():
329 329 if entry[0] == 'u':
330 330 yield f
331 331
332 332 def driverresolved(self):
333 333 """Obtain the paths of driver-resolved files."""
334 334
335 335 for f, entry in self._state.items():
336 336 if entry[0] == 'd':
337 337 yield f
338 338
339 339 def _resolve(self, preresolve, dfile, wctx, labels=None):
340 340 """rerun merge process for file path `dfile`"""
341 341 if self[dfile] in 'rd':
342 342 return True, 0
343 343 stateentry = self._state[dfile]
344 344 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
345 345 octx = self._repo[self._other]
346 346 fcd = wctx[dfile]
347 347 fco = octx[ofile]
348 348 fca = self._repo.filectx(afile, fileid=anode)
349 349 # "premerge" x flags
350 350 flo = fco.flags()
351 351 fla = fca.flags()
352 352 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
353 353 if fca.node() == nullid:
354 354 if preresolve:
355 355 self._repo.ui.warn(
356 356 _('warning: cannot merge flags for %s\n') % afile)
357 357 elif flags == fla:
358 358 flags = flo
359 359 if preresolve:
360 360 # restore local
361 361 f = self._repo.vfs('merge/' + hash)
362 362 self._repo.wwrite(dfile, f.read(), flags)
363 363 f.close()
364 364 complete, r = filemerge.premerge(self._repo, self._local, lfile,
365 365 fcd, fco, fca, labels=labels)
366 366 else:
367 367 complete, r = filemerge.filemerge(self._repo, self._local, lfile,
368 368 fcd, fco, fca, labels=labels)
369 369 if r is None:
370 370 # no real conflict
371 371 del self._state[dfile]
372 372 self._dirty = True
373 373 elif not r:
374 374 self.mark(dfile, 'r')
375 375 return complete, r
376 376
377 377 def preresolve(self, dfile, wctx, labels=None):
378 378 return self._resolve(True, dfile, wctx, labels=labels)
379 379
380 380 def resolve(self, dfile, wctx, labels=None):
381 381 """rerun merge process for file path `dfile`"""
382 382 return self._resolve(False, dfile, wctx, labels=labels)[1]
383 383
384 384 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
385 385 if f2 is None:
386 386 f2 = f
387 387 return (os.path.isfile(repo.wjoin(f))
388 388 and repo.wvfs.audit.check(f)
389 389 and repo.dirstate.normalize(f) not in repo.dirstate
390 390 and mctx[f2].cmp(wctx[f]))
391 391
392 392 def _checkunknownfiles(repo, wctx, mctx, force, actions):
393 393 """
394 394 Considers any actions that care about the presence of conflicting unknown
395 395 files. For some actions, the result is to abort; for others, it is to
396 396 choose a different action.
397 397 """
398 398 aborts = []
399 399 if not force:
400 400 for f, (m, args, msg) in actions.iteritems():
401 401 if m in ('c', 'dc'):
402 402 if _checkunknownfile(repo, wctx, mctx, f):
403 403 aborts.append(f)
404 404 elif m == 'dg':
405 405 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
406 406 aborts.append(f)
407 407
408 408 for f in sorted(aborts):
409 409 repo.ui.warn(_("%s: untracked file differs\n") % f)
410 410 if aborts:
411 411 raise error.Abort(_("untracked files in working directory differ "
412 412 "from files in requested revision"))
413 413
414 414 for f, (m, args, msg) in actions.iteritems():
415 415 if m == 'c':
416 416 actions[f] = ('g', args, msg)
417 417 elif m == 'cm':
418 418 fl2, anc = args
419 419 different = _checkunknownfile(repo, wctx, mctx, f)
420 420 if different:
421 421 actions[f] = ('m', (f, f, None, False, anc),
422 422 "remote differs from untracked local")
423 423 else:
424 424 actions[f] = ('g', (fl2,), "remote created")
425 425
426 426 def _forgetremoved(wctx, mctx, branchmerge):
427 427 """
428 428 Forget removed files
429 429
430 430 If we're jumping between revisions (as opposed to merging), and if
431 431 neither the working directory nor the target rev has the file,
432 432 then we need to remove it from the dirstate, to prevent the
433 433 dirstate from listing the file when it is no longer in the
434 434 manifest.
435 435
436 436 If we're merging, and the other revision has removed a file
437 437 that is not present in the working directory, we need to mark it
438 438 as removed.
439 439 """
440 440
441 441 actions = {}
442 442 m = 'f'
443 443 if branchmerge:
444 444 m = 'r'
445 445 for f in wctx.deleted():
446 446 if f not in mctx:
447 447 actions[f] = m, None, "forget deleted"
448 448
449 449 if not branchmerge:
450 450 for f in wctx.removed():
451 451 if f not in mctx:
452 452 actions[f] = 'f', None, "forget removed"
453 453
454 454 return actions
455 455
456 456 def _checkcollision(repo, wmf, actions):
457 457 # build provisional merged manifest up
458 458 pmmf = set(wmf)
459 459
460 460 if actions:
461 461 # k, dr, e and rd are no-op
462 462 for m in 'a', 'f', 'g', 'cd', 'dc':
463 463 for f, args, msg in actions[m]:
464 464 pmmf.add(f)
465 465 for f, args, msg in actions['r']:
466 466 pmmf.discard(f)
467 467 for f, args, msg in actions['dm']:
468 468 f2, flags = args
469 469 pmmf.discard(f2)
470 470 pmmf.add(f)
471 471 for f, args, msg in actions['dg']:
472 472 pmmf.add(f)
473 473 for f, args, msg in actions['m']:
474 474 f1, f2, fa, move, anc = args
475 475 if move:
476 476 pmmf.discard(f1)
477 477 pmmf.add(f)
478 478
479 479 # check case-folding collision in provisional merged manifest
480 480 foldmap = {}
481 481 for f in sorted(pmmf):
482 482 fold = util.normcase(f)
483 483 if fold in foldmap:
484 484 raise error.Abort(_("case-folding collision between %s and %s")
485 485 % (f, foldmap[fold]))
486 486 foldmap[fold] = f
487 487
488 488 # check case-folding of directories
489 489 foldprefix = unfoldprefix = lastfull = ''
490 490 for fold, f in sorted(foldmap.items()):
491 491 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
492 492 # the folded prefix matches but actual casing is different
493 493 raise error.Abort(_("case-folding collision between "
494 494 "%s and directory of %s") % (lastfull, f))
495 495 foldprefix = fold + '/'
496 496 unfoldprefix = f + '/'
497 497 lastfull = f
498 498
499 499 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
500 500 acceptremote, followcopies):
501 501 """
502 502 Merge p1 and p2 with ancestor pa and generate merge action list
503 503
504 504 branchmerge and force are as passed in to update
505 505 partial = function to filter file lists
506 506 acceptremote = accept the incoming changes without prompting
507 507 """
508 508
509 509 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
510 510
511 511 # manifests fetched in order are going to be faster, so prime the caches
512 512 [x.manifest() for x in
513 513 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
514 514
515 515 if followcopies:
516 516 ret = copies.mergecopies(repo, wctx, p2, pa)
517 517 copy, movewithdir, diverge, renamedelete = ret
518 518
519 519 repo.ui.note(_("resolving manifests\n"))
520 520 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
521 521 % (bool(branchmerge), bool(force), bool(partial)))
522 522 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
523 523
524 524 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
525 525 copied = set(copy.values())
526 526 copied.update(movewithdir.values())
527 527
528 528 if '.hgsubstate' in m1:
529 529 # check whether sub state is modified
530 530 for s in sorted(wctx.substate):
531 531 if wctx.sub(s).dirty():
532 532 m1['.hgsubstate'] += '+'
533 533 break
534 534
535 535 # Compare manifests
536 536 diff = m1.diff(m2)
537 537
538 538 actions = {}
539 539 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
540 540 if partial and not partial(f):
541 541 continue
542 542 if n1 and n2: # file exists on both local and remote side
543 543 if f not in ma:
544 544 fa = copy.get(f, None)
545 545 if fa is not None:
546 546 actions[f] = ('m', (f, f, fa, False, pa.node()),
547 547 "both renamed from " + fa)
548 548 else:
549 549 actions[f] = ('m', (f, f, None, False, pa.node()),
550 550 "both created")
551 551 else:
552 552 a = ma[f]
553 553 fla = ma.flags(f)
554 554 nol = 'l' not in fl1 + fl2 + fla
555 555 if n2 == a and fl2 == fla:
556 556 actions[f] = ('k' , (), "remote unchanged")
557 557 elif n1 == a and fl1 == fla: # local unchanged - use remote
558 558 if n1 == n2: # optimization: keep local content
559 559 actions[f] = ('e', (fl2,), "update permissions")
560 560 else:
561 561 actions[f] = ('g', (fl2,), "remote is newer")
562 562 elif nol and n2 == a: # remote only changed 'x'
563 563 actions[f] = ('e', (fl2,), "update permissions")
564 564 elif nol and n1 == a: # local only changed 'x'
565 565 actions[f] = ('g', (fl1,), "remote is newer")
566 566 else: # both changed something
567 567 actions[f] = ('m', (f, f, f, False, pa.node()),
568 568 "versions differ")
569 569 elif n1: # file exists only on local side
570 570 if f in copied:
571 571 pass # we'll deal with it on m2 side
572 572 elif f in movewithdir: # directory rename, move local
573 573 f2 = movewithdir[f]
574 574 if f2 in m2:
575 575 actions[f2] = ('m', (f, f2, None, True, pa.node()),
576 576 "remote directory rename, both created")
577 577 else:
578 578 actions[f2] = ('dm', (f, fl1),
579 579 "remote directory rename - move from " + f)
580 580 elif f in copy:
581 581 f2 = copy[f]
582 582 actions[f] = ('m', (f, f2, f2, False, pa.node()),
583 583 "local copied/moved from " + f2)
584 584 elif f in ma: # clean, a different, no remote
585 585 if n1 != ma[f]:
586 586 if acceptremote:
587 587 actions[f] = ('r', None, "remote delete")
588 588 else:
589 589 actions[f] = ('cd', None, "prompt changed/deleted")
590 590 elif n1[20:] == 'a':
591 591 # This extra 'a' is added by working copy manifest to mark
592 592 # the file as locally added. We should forget it instead of
593 593 # deleting it.
594 594 actions[f] = ('f', None, "remote deleted")
595 595 else:
596 596 actions[f] = ('r', None, "other deleted")
597 597 elif n2: # file exists only on remote side
598 598 if f in copied:
599 599 pass # we'll deal with it on m1 side
600 600 elif f in movewithdir:
601 601 f2 = movewithdir[f]
602 602 if f2 in m1:
603 603 actions[f2] = ('m', (f2, f, None, False, pa.node()),
604 604 "local directory rename, both created")
605 605 else:
606 606 actions[f2] = ('dg', (f, fl2),
607 607 "local directory rename - get from " + f)
608 608 elif f in copy:
609 609 f2 = copy[f]
610 610 if f2 in m2:
611 611 actions[f] = ('m', (f2, f, f2, False, pa.node()),
612 612 "remote copied from " + f2)
613 613 else:
614 614 actions[f] = ('m', (f2, f, f2, True, pa.node()),
615 615 "remote moved from " + f2)
616 616 elif f not in ma:
617 617 # local unknown, remote created: the logic is described by the
618 618 # following table:
619 619 #
620 620 # force branchmerge different | action
621 621 # n * * | create
622 622 # y n * | create
623 623 # y y n | create
624 624 # y y y | merge
625 625 #
626 626 # Checking whether the files are different is expensive, so we
627 627 # don't do that when we can avoid it.
628 628 if not force:
629 629 actions[f] = ('c', (fl2,), "remote created")
630 630 elif not branchmerge:
631 631 actions[f] = ('c', (fl2,), "remote created")
632 632 else:
633 633 actions[f] = ('cm', (fl2, pa.node()),
634 634 "remote created, get or merge")
635 635 elif n2 != ma[f]:
636 636 if acceptremote:
637 637 actions[f] = ('c', (fl2,), "remote recreating")
638 638 else:
639 639 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
640 640
641 641 return actions, diverge, renamedelete
642 642
643 643 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
644 644 """Resolves false conflicts where the nodeid changed but the content
645 645 remained the same."""
646 646
647 647 for f, (m, args, msg) in actions.items():
648 648 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
649 649 # local did change but ended up with same content
650 650 actions[f] = 'r', None, "prompt same"
651 651 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
652 652 # remote did change but ended up with same content
653 653 del actions[f] # don't get = keep local deleted
654 654
655 655 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
656 656 acceptremote, followcopies):
657 657 "Calculate the actions needed to merge mctx into wctx using ancestors"
658 658
659 659 if len(ancestors) == 1: # default
660 660 actions, diverge, renamedelete = manifestmerge(
661 661 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
662 662 acceptremote, followcopies)
663 663 _checkunknownfiles(repo, wctx, mctx, force, actions)
664 664
665 665 else: # only when merge.preferancestor=* - the default
666 666 repo.ui.note(
667 667 _("note: merging %s and %s using bids from ancestors %s\n") %
668 668 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
669 669
670 670 # Call for bids
671 671 fbids = {} # mapping filename to bids (action method to list af actions)
672 672 diverge, renamedelete = None, None
673 673 for ancestor in ancestors:
674 674 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
675 675 actions, diverge1, renamedelete1 = manifestmerge(
676 676 repo, wctx, mctx, ancestor, branchmerge, force, partial,
677 677 acceptremote, followcopies)
678 678 _checkunknownfiles(repo, wctx, mctx, force, actions)
679 679
680 680 # Track the shortest set of warning on the theory that bid
681 681 # merge will correctly incorporate more information
682 682 if diverge is None or len(diverge1) < len(diverge):
683 683 diverge = diverge1
684 684 if renamedelete is None or len(renamedelete) < len(renamedelete1):
685 685 renamedelete = renamedelete1
686 686
687 687 for f, a in sorted(actions.iteritems()):
688 688 m, args, msg = a
689 689 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
690 690 if f in fbids:
691 691 d = fbids[f]
692 692 if m in d:
693 693 d[m].append(a)
694 694 else:
695 695 d[m] = [a]
696 696 else:
697 697 fbids[f] = {m: [a]}
698 698
699 699 # Pick the best bid for each file
700 700 repo.ui.note(_('\nauction for merging merge bids\n'))
701 701 actions = {}
702 702 for f, bids in sorted(fbids.items()):
703 703 # bids is a mapping from action method to list af actions
704 704 # Consensus?
705 705 if len(bids) == 1: # all bids are the same kind of method
706 706 m, l = bids.items()[0]
707 707 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
708 708 repo.ui.note(" %s: consensus for %s\n" % (f, m))
709 709 actions[f] = l[0]
710 710 continue
711 711 # If keep is an option, just do it.
712 712 if 'k' in bids:
713 713 repo.ui.note(" %s: picking 'keep' action\n" % f)
714 714 actions[f] = bids['k'][0]
715 715 continue
716 716 # If there are gets and they all agree [how could they not?], do it.
717 717 if 'g' in bids:
718 718 ga0 = bids['g'][0]
719 719 if all(a == ga0 for a in bids['g'][1:]):
720 720 repo.ui.note(" %s: picking 'get' action\n" % f)
721 721 actions[f] = ga0
722 722 continue
723 723 # TODO: Consider other simple actions such as mode changes
724 724 # Handle inefficient democrazy.
725 725 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
726 726 for m, l in sorted(bids.items()):
727 727 for _f, args, msg in l:
728 728 repo.ui.note(' %s -> %s\n' % (msg, m))
729 729 # Pick random action. TODO: Instead, prompt user when resolving
730 730 m, l = bids.items()[0]
731 731 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
732 732 (f, m))
733 733 actions[f] = l[0]
734 734 continue
735 735 repo.ui.note(_('end of auction\n\n'))
736 736
737 737 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
738 738
739 739 if wctx.rev() is None:
740 740 fractions = _forgetremoved(wctx, mctx, branchmerge)
741 741 actions.update(fractions)
742 742
743 743 return actions, diverge, renamedelete
744 744
745 745 def batchremove(repo, actions):
746 746 """apply removes to the working directory
747 747
748 748 yields tuples for progress updates
749 749 """
750 750 verbose = repo.ui.verbose
751 751 unlink = util.unlinkpath
752 752 wjoin = repo.wjoin
753 753 audit = repo.wvfs.audit
754 754 i = 0
755 755 for f, args, msg in actions:
756 756 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
757 757 if verbose:
758 758 repo.ui.note(_("removing %s\n") % f)
759 759 audit(f)
760 760 try:
761 761 unlink(wjoin(f), ignoremissing=True)
762 762 except OSError as inst:
763 763 repo.ui.warn(_("update failed to remove %s: %s!\n") %
764 764 (f, inst.strerror))
765 765 if i == 100:
766 766 yield i, f
767 767 i = 0
768 768 i += 1
769 769 if i > 0:
770 770 yield i, f
771 771
772 772 def batchget(repo, mctx, actions):
773 773 """apply gets to the working directory
774 774
775 775 mctx is the context to get from
776 776
777 777 yields tuples for progress updates
778 778 """
779 779 verbose = repo.ui.verbose
780 780 fctx = mctx.filectx
781 781 wwrite = repo.wwrite
782 782 i = 0
783 783 for f, args, msg in actions:
784 784 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
785 785 if verbose:
786 786 repo.ui.note(_("getting %s\n") % f)
787 787 wwrite(f, fctx(f).data(), args[0])
788 788 if i == 100:
789 789 yield i, f
790 790 i = 0
791 791 i += 1
792 792 if i > 0:
793 793 yield i, f
794 794
795 795 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
796 796 """apply the merge action list to the working directory
797 797
798 798 wctx is the working copy context
799 799 mctx is the context to be merged into the working copy
800 800
801 801 Return a tuple of counts (updated, merged, removed, unresolved) that
802 802 describes how many files were affected by the update.
803 803 """
804 804
805 805 updated, merged, removed, unresolved = 0, 0, 0, 0
806 806 ms = mergestate(repo)
807 807 ms.reset(wctx.p1().node(), mctx.node())
808 808 moves = []
809 809 for m, l in actions.items():
810 810 l.sort()
811 811
812 812 # prescan for merges
813 813 for f, args, msg in actions['m']:
814 814 f1, f2, fa, move, anc = args
815 815 if f == '.hgsubstate': # merged internally
816 816 continue
817 817 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
818 818 fcl = wctx[f1]
819 819 fco = mctx[f2]
820 820 actx = repo[anc]
821 821 if fa in actx:
822 822 fca = actx[fa]
823 823 else:
824 824 fca = repo.filectx(f1, fileid=nullrev)
825 825 ms.add(fcl, fco, fca, f)
826 826 if f1 != f and move:
827 827 moves.append(f1)
828 828
829 829 audit = repo.wvfs.audit
830 830 _updating = _('updating')
831 831 _files = _('files')
832 832 progress = repo.ui.progress
833 833
834 834 # remove renamed files after safely stored
835 835 for f in moves:
836 836 if os.path.lexists(repo.wjoin(f)):
837 837 repo.ui.debug("removing %s\n" % f)
838 838 audit(f)
839 839 util.unlinkpath(repo.wjoin(f))
840 840
841 841 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
842 842
843 843 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
844 844 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
845 845
846 846 # remove in parallel (must come first)
847 847 z = 0
848 848 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
849 849 for i, item in prog:
850 850 z += i
851 851 progress(_updating, z, item=item, total=numupdates, unit=_files)
852 852 removed = len(actions['r'])
853 853
854 854 # get in parallel
855 855 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
856 856 for i, item in prog:
857 857 z += i
858 858 progress(_updating, z, item=item, total=numupdates, unit=_files)
859 859 updated = len(actions['g'])
860 860
861 861 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
862 862 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
863 863
864 864 # forget (manifest only, just log it) (must come first)
865 865 for f, args, msg in actions['f']:
866 866 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
867 867 z += 1
868 868 progress(_updating, z, item=f, total=numupdates, unit=_files)
869 869
870 870 # re-add (manifest only, just log it)
871 871 for f, args, msg in actions['a']:
872 872 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
873 873 z += 1
874 874 progress(_updating, z, item=f, total=numupdates, unit=_files)
875 875
876 876 # keep (noop, just log it)
877 877 for f, args, msg in actions['k']:
878 878 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
879 879 # no progress
880 880
881 881 # directory rename, move local
882 882 for f, args, msg in actions['dm']:
883 883 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
884 884 z += 1
885 885 progress(_updating, z, item=f, total=numupdates, unit=_files)
886 886 f0, flags = args
887 887 repo.ui.note(_("moving %s to %s\n") % (f0, f))
888 888 audit(f)
889 889 repo.wwrite(f, wctx.filectx(f0).data(), flags)
890 890 util.unlinkpath(repo.wjoin(f0))
891 891 updated += 1
892 892
893 893 # local directory rename, get
894 894 for f, args, msg in actions['dg']:
895 895 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
896 896 z += 1
897 897 progress(_updating, z, item=f, total=numupdates, unit=_files)
898 898 f0, flags = args
899 899 repo.ui.note(_("getting %s to %s\n") % (f0, f))
900 900 repo.wwrite(f, mctx.filectx(f0).data(), flags)
901 901 updated += 1
902 902
903 903 # exec
904 904 for f, args, msg in actions['e']:
905 905 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
906 906 z += 1
907 907 progress(_updating, z, item=f, total=numupdates, unit=_files)
908 908 flags, = args
909 909 audit(f)
910 910 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
911 911 updated += 1
912 912
913 913 # premerge
914 914 tocomplete = []
915 915 for f, args, msg in actions['m']:
916 916 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
917 917 z += 1
918 918 progress(_updating, z, item=f, total=numupdates, unit=_files)
919 919 if f == '.hgsubstate': # subrepo states need updating
920 920 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
921 921 overwrite)
922 922 continue
923 923 audit(f)
924 924 complete, r = ms.preresolve(f, wctx, labels=labels)
925 925 if complete:
926 926 if r is not None and r > 0:
927 927 unresolved += 1
928 928 else:
929 929 if r is None:
930 930 updated += 1
931 931 else:
932 932 merged += 1
933 933 else:
934 934 numupdates += 1
935 935 tocomplete.append((f, args, msg))
936 936
937 937 # merge
938 938 for f, args, msg in tocomplete:
939 939 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
940 940 z += 1
941 941 progress(_updating, z, item=f, total=numupdates, unit=_files)
942 942 r = ms.resolve(f, wctx, labels=labels)
943 943 if r is not None and r > 0:
944 944 unresolved += 1
945 945 else:
946 946 if r is None:
947 947 updated += 1
948 948 else:
949 949 merged += 1
950 950
951 951 ms.commit()
952 952 progress(_updating, None, total=numupdates, unit=_files)
953 953
954 954 return updated, merged, removed, unresolved
955 955
956 956 def recordupdates(repo, actions, branchmerge):
957 957 "record merge actions to the dirstate"
958 958 # remove (must come first)
959 959 for f, args, msg in actions['r']:
960 960 if branchmerge:
961 961 repo.dirstate.remove(f)
962 962 else:
963 963 repo.dirstate.drop(f)
964 964
965 965 # forget (must come first)
966 966 for f, args, msg in actions['f']:
967 967 repo.dirstate.drop(f)
968 968
969 969 # re-add
970 970 for f, args, msg in actions['a']:
971 971 if not branchmerge:
972 972 repo.dirstate.add(f)
973 973
974 974 # exec change
975 975 for f, args, msg in actions['e']:
976 976 repo.dirstate.normallookup(f)
977 977
978 978 # keep
979 979 for f, args, msg in actions['k']:
980 980 pass
981 981
982 982 # get
983 983 for f, args, msg in actions['g']:
984 984 if branchmerge:
985 985 repo.dirstate.otherparent(f)
986 986 else:
987 987 repo.dirstate.normal(f)
988 988
989 989 # merge
990 990 for f, args, msg in actions['m']:
991 991 f1, f2, fa, move, anc = args
992 992 if branchmerge:
993 993 # We've done a branch merge, mark this file as merged
994 994 # so that we properly record the merger later
995 995 repo.dirstate.merge(f)
996 996 if f1 != f2: # copy/rename
997 997 if move:
998 998 repo.dirstate.remove(f1)
999 999 if f1 != f:
1000 1000 repo.dirstate.copy(f1, f)
1001 1001 else:
1002 1002 repo.dirstate.copy(f2, f)
1003 1003 else:
1004 1004 # We've update-merged a locally modified file, so
1005 1005 # we set the dirstate to emulate a normal checkout
1006 1006 # of that file some time in the past. Thus our
1007 1007 # merge will appear as a normal local file
1008 1008 # modification.
1009 1009 if f2 == f: # file not locally copied/moved
1010 1010 repo.dirstate.normallookup(f)
1011 1011 if move:
1012 1012 repo.dirstate.drop(f1)
1013 1013
1014 1014 # directory rename, move local
1015 1015 for f, args, msg in actions['dm']:
1016 1016 f0, flag = args
1017 1017 if branchmerge:
1018 1018 repo.dirstate.add(f)
1019 1019 repo.dirstate.remove(f0)
1020 1020 repo.dirstate.copy(f0, f)
1021 1021 else:
1022 1022 repo.dirstate.normal(f)
1023 1023 repo.dirstate.drop(f0)
1024 1024
1025 1025 # directory rename, get
1026 1026 for f, args, msg in actions['dg']:
1027 1027 f0, flag = args
1028 1028 if branchmerge:
1029 1029 repo.dirstate.add(f)
1030 1030 repo.dirstate.copy(f0, f)
1031 1031 else:
1032 1032 repo.dirstate.normal(f)
1033 1033
1034 1034 def update(repo, node, branchmerge, force, partial, ancestor=None,
1035 1035 mergeancestor=False, labels=None):
1036 1036 """
1037 1037 Perform a merge between the working directory and the given node
1038 1038
1039 1039 node = the node to update to, or None if unspecified
1040 1040 branchmerge = whether to merge between branches
1041 1041 force = whether to force branch merging or file overwriting
1042 1042 partial = a function to filter file lists (dirstate not updated)
1043 1043 mergeancestor = whether it is merging with an ancestor. If true,
1044 1044 we should accept the incoming changes for any prompts that occur.
1045 1045 If false, merging with an ancestor (fast-forward) is only allowed
1046 1046 between different named branches. This flag is used by rebase extension
1047 1047 as a temporary fix and should be avoided in general.
1048 1048
1049 1049 The table below shows all the behaviors of the update command
1050 1050 given the -c and -C or no options, whether the working directory
1051 1051 is dirty, whether a revision is specified, and the relationship of
1052 1052 the parent rev to the target rev (linear, on the same named
1053 1053 branch, or on another named branch).
1054 1054
1055 1055 This logic is tested by test-update-branches.t.
1056 1056
1057 1057 -c -C dirty rev | linear same cross
1058 1058 n n n n | ok (1) x
1059 1059 n n n y | ok ok ok
1060 1060 n n y n | merge (2) (2)
1061 1061 n n y y | merge (3) (3)
1062 1062 n y * * | --- discard ---
1063 1063 y n y * | --- (4) ---
1064 1064 y n n * | --- ok ---
1065 1065 y y * * | --- (5) ---
1066 1066
1067 1067 x = can't happen
1068 1068 * = don't-care
1069 1069 1 = abort: not a linear update (merge or update --check to force update)
1070 1070 2 = abort: uncommitted changes (commit and merge, or update --clean to
1071 1071 discard changes)
1072 1072 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1073 1073 4 = abort: uncommitted changes (checked in commands.py)
1074 1074 5 = incompatible options (checked in commands.py)
1075 1075
1076 1076 Return the same tuple as applyupdates().
1077 1077 """
1078 1078
1079 1079 onode = node
1080 1080 wlock = repo.wlock()
1081 1081 try:
1082 1082 wc = repo[None]
1083 1083 pl = wc.parents()
1084 1084 p1 = pl[0]
1085 1085 pas = [None]
1086 1086 if ancestor is not None:
1087 1087 pas = [repo[ancestor]]
1088 1088
1089 1089 if node is None:
1090 1090 if (repo.ui.configbool('devel', 'all-warnings')
1091 1091 or repo.ui.configbool('devel', 'oldapi')):
1092 1092 repo.ui.develwarn('update with no target')
1093 1093 rev, _mark, _act = destutil.destupdate(repo)
1094 1094 node = repo[rev].node()
1095 1095
1096 1096 overwrite = force and not branchmerge
1097 1097
1098 1098 p2 = repo[node]
1099 1099 if pas[0] is None:
1100 1100 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1101 1101 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1102 1102 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1103 1103 else:
1104 1104 pas = [p1.ancestor(p2, warn=branchmerge)]
1105 1105
1106 1106 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1107 1107
1108 1108 ### check phase
1109 1109 if not overwrite and len(pl) > 1:
1110 1110 raise error.Abort(_("outstanding uncommitted merge"))
1111 1111 if branchmerge:
1112 1112 if pas == [p2]:
1113 1113 raise error.Abort(_("merging with a working directory ancestor"
1114 1114 " has no effect"))
1115 1115 elif pas == [p1]:
1116 1116 if not mergeancestor and p1.branch() == p2.branch():
1117 1117 raise error.Abort(_("nothing to merge"),
1118 1118 hint=_("use 'hg update' "
1119 1119 "or check 'hg heads'"))
1120 1120 if not force and (wc.files() or wc.deleted()):
1121 1121 raise error.Abort(_("uncommitted changes"),
1122 1122 hint=_("use 'hg status' to list changes"))
1123 1123 for s in sorted(wc.substate):
1124 1124 wc.sub(s).bailifchanged()
1125 1125
1126 1126 elif not overwrite:
1127 1127 if p1 == p2: # no-op update
1128 1128 # call the hooks and exit early
1129 1129 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1130 1130 repo.hook('update', parent1=xp2, parent2='', error=0)
1131 1131 return 0, 0, 0, 0
1132 1132
1133 1133 if pas not in ([p1], [p2]): # nonlinear
1134 1134 dirty = wc.dirty(missing=True)
1135 1135 if dirty or onode is None:
1136 1136 # Branching is a bit strange to ensure we do the minimal
1137 1137 # amount of call to obsolete.background.
1138 1138 foreground = obsolete.foreground(repo, [p1.node()])
1139 1139 # note: the <node> variable contains a random identifier
1140 1140 if repo[node].node() in foreground:
1141 1141 pas = [p1] # allow updating to successors
1142 1142 elif dirty:
1143 1143 msg = _("uncommitted changes")
1144 1144 if onode is None:
1145 1145 hint = _("commit and merge, or update --clean to"
1146 1146 " discard changes")
1147 1147 else:
1148 1148 hint = _("commit or update --clean to discard"
1149 1149 " changes")
1150 1150 raise error.Abort(msg, hint=hint)
1151 1151 else: # node is none
1152 1152 msg = _("not a linear update")
1153 1153 hint = _("merge or update --check to force update")
1154 1154 raise error.Abort(msg, hint=hint)
1155 1155 else:
1156 1156 # Allow jumping branches if clean and specific rev given
1157 1157 pas = [p1]
1158 1158
1159 1159 # deprecated config: merge.followcopies
1160 1160 followcopies = False
1161 1161 if overwrite:
1162 1162 pas = [wc]
1163 1163 elif pas == [p2]: # backwards
1164 1164 pas = [wc.p1()]
1165 1165 elif not branchmerge and not wc.dirty(missing=True):
1166 1166 pass
1167 1167 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1168 1168 followcopies = True
1169 1169
1170 1170 ### calculate phase
1171 1171 actionbyfile, diverge, renamedelete = calculateupdates(
1172 1172 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1173 1173 followcopies)
1174 1174 # Convert to dictionary-of-lists format
1175 1175 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1176 1176 for f, (m, args, msg) in actionbyfile.iteritems():
1177 1177 if m not in actions:
1178 1178 actions[m] = []
1179 1179 actions[m].append((f, args, msg))
1180 1180
1181 1181 if not util.checkcase(repo.path):
1182 1182 # check collision between files only in p2 for clean update
1183 1183 if (not branchmerge and
1184 1184 (force or not wc.dirty(missing=True, branch=False))):
1185 1185 _checkcollision(repo, p2.manifest(), None)
1186 1186 else:
1187 1187 _checkcollision(repo, wc.manifest(), actions)
1188 1188
1189 1189 # Prompt and create actions. TODO: Move this towards resolve phase.
1190 1190 for f, args, msg in sorted(actions['cd']):
1191 1191 if repo.ui.promptchoice(
1192 1192 _("local changed %s which remote deleted\n"
1193 1193 "use (c)hanged version or (d)elete?"
1194 1194 "$$ &Changed $$ &Delete") % f, 0):
1195 1195 actions['r'].append((f, None, "prompt delete"))
1196 1196 else:
1197 1197 actions['a'].append((f, None, "prompt keep"))
1198 1198 del actions['cd'][:]
1199 1199
1200 1200 for f, args, msg in sorted(actions['dc']):
1201 1201 flags, = args
1202 1202 if repo.ui.promptchoice(
1203 1203 _("remote changed %s which local deleted\n"
1204 1204 "use (c)hanged version or leave (d)eleted?"
1205 1205 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1206 1206 actions['g'].append((f, (flags,), "prompt recreating"))
1207 1207 del actions['dc'][:]
1208 1208
1209 1209 ### apply phase
1210 1210 if not branchmerge: # just jump to the new rev
1211 1211 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1212 1212 if not partial:
1213 1213 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1214 1214 # note that we're in the middle of an update
1215 1215 repo.vfs.write('updatestate', p2.hex())
1216 1216
1217 1217 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1218 1218
1219 1219 # divergent renames
1220 1220 for f, fl in sorted(diverge.iteritems()):
1221 1221 repo.ui.warn(_("note: possible conflict - %s was renamed "
1222 1222 "multiple times to:\n") % f)
1223 1223 for nf in fl:
1224 1224 repo.ui.warn(" %s\n" % nf)
1225 1225
1226 1226 # rename and delete
1227 1227 for f, fl in sorted(renamedelete.iteritems()):
1228 1228 repo.ui.warn(_("note: possible conflict - %s was deleted "
1229 1229 "and renamed to:\n") % f)
1230 1230 for nf in fl:
1231 1231 repo.ui.warn(" %s\n" % nf)
1232 1232
1233 1233 if not partial:
1234 1234 repo.dirstate.beginparentchange()
1235 1235 repo.setparents(fp1, fp2)
1236 1236 recordupdates(repo, actions, branchmerge)
1237 1237 # update completed, clear state
1238 1238 util.unlink(repo.join('updatestate'))
1239 1239
1240 1240 if not branchmerge:
1241 1241 repo.dirstate.setbranch(p2.branch())
1242 1242 repo.dirstate.endparentchange()
1243 1243 finally:
1244 1244 wlock.release()
1245 1245
1246 1246 if not partial:
1247 def updatehook(parent1=xp1, parent2=xp2, error=stats[3]):
1248 repo.hook('update', parent1=parent1, parent2=parent2, error=error)
1249 repo._afterlock(updatehook)
1247 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1250 1248 return stats
1251 1249
1252 1250 def graft(repo, ctx, pctx, labels):
1253 1251 """Do a graft-like merge.
1254 1252
1255 1253 This is a merge where the merge ancestor is chosen such that one
1256 1254 or more changesets are grafted onto the current changeset. In
1257 1255 addition to the merge, this fixes up the dirstate to include only
1258 1256 a single parent and tries to duplicate any renames/copies
1259 1257 appropriately.
1260 1258
1261 1259 ctx - changeset to rebase
1262 1260 pctx - merge base, usually ctx.p1()
1263 1261 labels - merge labels eg ['local', 'graft']
1264 1262
1265 1263 """
1266 1264 # If we're grafting a descendant onto an ancestor, be sure to pass
1267 1265 # mergeancestor=True to update. This does two things: 1) allows the merge if
1268 1266 # the destination is the same as the parent of the ctx (so we can use graft
1269 1267 # to copy commits), and 2) informs update that the incoming changes are
1270 1268 # newer than the destination so it doesn't prompt about "remote changed foo
1271 1269 # which local deleted".
1272 1270 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1273 1271
1274 1272 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1275 1273 mergeancestor=mergeancestor, labels=labels)
1276 1274
1277 1275 # drop the second merge parent
1278 1276 repo.dirstate.beginparentchange()
1279 1277 repo.setparents(repo['.'].node(), nullid)
1280 1278 repo.dirstate.write(repo.currenttransaction())
1281 1279 # fix up dirstate for copies and renames
1282 1280 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1283 1281 repo.dirstate.endparentchange()
1284 1282 return stats
@@ -1,706 +1,741 b''
1 1 $ hg init basic
2 2 $ cd basic
3 3
4 4 should complain
5 5
6 6 $ hg backout
7 7 abort: please specify a revision to backout
8 8 [255]
9 9 $ hg backout -r 0 0
10 10 abort: please specify just one revision
11 11 [255]
12 12
13 13 basic operation
14 14 (this also tests that editor is invoked if the commit message is not
15 15 specified explicitly)
16 16
17 17 $ echo a > a
18 18 $ hg commit -d '0 0' -A -m a
19 19 adding a
20 20 $ echo b >> a
21 21 $ hg commit -d '1 0' -m b
22 22
23 23 $ hg status --rev tip --rev "tip^1"
24 24 M a
25 25 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true
26 26 reverting a
27 27 Backed out changeset a820f4f40a57
28 28
29 29
30 30 HG: Enter commit message. Lines beginning with 'HG:' are removed.
31 31 HG: Leave message empty to abort commit.
32 32 HG: --
33 33 HG: user: test
34 34 HG: branch 'default'
35 35 HG: changed a
36 36 changeset 2:2929462c3dff backs out changeset 1:a820f4f40a57
37 37 $ cat a
38 38 a
39 39 $ hg summary
40 40 parent: 2:2929462c3dff tip
41 41 Backed out changeset a820f4f40a57
42 42 branch: default
43 43 commit: (clean)
44 44 update: (current)
45 45 phases: 3 draft
46 46
47 47 commit option
48 48
49 49 $ cd ..
50 50 $ hg init commit
51 51 $ cd commit
52 52
53 53 $ echo tomatoes > a
54 54 $ hg add a
55 55 $ hg commit -d '0 0' -m tomatoes
56 56
57 57 $ echo chair > b
58 58 $ hg add b
59 59 $ hg commit -d '1 0' -m chair
60 60
61 61 $ echo grapes >> a
62 62 $ hg commit -d '2 0' -m grapes
63 63
64 64 $ hg backout --commit -d '4 0' 1 --tool=:fail
65 65 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 66 changeset 3:1c2161e97c0a backs out changeset 1:22cb4f70d813
67 67 $ hg summary
68 68 parent: 3:1c2161e97c0a tip
69 69 Backed out changeset 22cb4f70d813
70 70 branch: default
71 71 commit: (clean)
72 72 update: (current)
73 73 phases: 4 draft
74 74
75 75 $ echo ypples > a
76 76 $ hg commit -d '5 0' -m ypples
77 77
78 78 $ hg backout --commit -d '6 0' 2 --tool=:fail
79 79 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
80 80 use 'hg resolve' to retry unresolved file merges
81 81 [1]
82 82 $ hg summary
83 83 parent: 4:ed99997b793d tip
84 84 ypples
85 85 branch: default
86 86 commit: 1 unresolved (clean)
87 87 update: (current)
88 88 phases: 5 draft
89 89
90 90 file that was removed is recreated
91 91 (this also tests that editor is not invoked if the commit message is
92 92 specified explicitly)
93 93
94 94 $ cd ..
95 95 $ hg init remove
96 96 $ cd remove
97 97
98 98 $ echo content > a
99 99 $ hg commit -d '0 0' -A -m a
100 100 adding a
101 101
102 102 $ hg rm a
103 103 $ hg commit -d '1 0' -m b
104 104
105 105 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true -m "Backed out changeset 76862dcce372"
106 106 adding a
107 107 changeset 2:de31bdc76c0d backs out changeset 1:76862dcce372
108 108 $ cat a
109 109 content
110 110 $ hg summary
111 111 parent: 2:de31bdc76c0d tip
112 112 Backed out changeset 76862dcce372
113 113 branch: default
114 114 commit: (clean)
115 115 update: (current)
116 116 phases: 3 draft
117 117
118 118 backout of backout is as if nothing happened
119 119
120 120 $ hg backout -d '3 0' --merge tip --tool=true
121 121 removing a
122 122 changeset 3:7f6d0f120113 backs out changeset 2:de31bdc76c0d
123 123 $ test -f a
124 124 [1]
125 125 $ hg summary
126 126 parent: 3:7f6d0f120113 tip
127 127 Backed out changeset de31bdc76c0d
128 128 branch: default
129 129 commit: (clean)
130 130 update: (current)
131 131 phases: 4 draft
132 132
133 133 Test that 'hg rollback' restores dirstate just before opening
134 134 transaction: in-memory dirstate changes should be written into
135 135 '.hg/journal.dirstate' as expected.
136 136
137 137 $ echo 'removed soon' > b
138 138 $ hg commit -A -d '4 0' -m 'prepare for subsequent removing'
139 139 adding b
140 140 $ echo 'newly added' > c
141 141 $ hg add c
142 142 $ hg remove b
143 143 $ hg commit -d '5 0' -m 'prepare for subsequent backout'
144 144 $ touch -t 200001010000 c
145 145 $ hg status -A
146 146 C c
147 147 $ hg debugstate --nodates
148 148 n 644 12 set c
149 149 $ hg backout -d '6 0' -m 'to be rollback-ed soon' -r .
150 150 adding b
151 151 removing c
152 152 changeset 6:4bfec048029d backs out changeset 5:fac0b729a654
153 153 $ hg rollback -q
154 154 $ hg status -A
155 155 A b
156 156 R c
157 157 $ hg debugstate --nodates
158 158 a 0 -1 unset b
159 159 r 0 0 set c
160 160
161 161 across branch
162 162
163 163 $ cd ..
164 164 $ hg init branch
165 165 $ cd branch
166 166 $ echo a > a
167 167 $ hg ci -Am0
168 168 adding a
169 169 $ echo b > b
170 170 $ hg ci -Am1
171 171 adding b
172 172 $ hg co -C 0
173 173 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
174 174 $ hg summary
175 175 parent: 0:f7b1eb17ad24
176 176 0
177 177 branch: default
178 178 commit: (clean)
179 179 update: 1 new changesets (update)
180 180 phases: 2 draft
181 181
182 182 should fail
183 183
184 184 $ hg backout 1
185 185 abort: cannot backout change that is not an ancestor
186 186 [255]
187 187 $ echo c > c
188 188 $ hg ci -Am2
189 189 adding c
190 190 created new head
191 191 $ hg summary
192 192 parent: 2:db815d6d32e6 tip
193 193 2
194 194 branch: default
195 195 commit: (clean)
196 196 update: 1 new changesets, 2 branch heads (merge)
197 197 phases: 3 draft
198 198
199 199 should fail
200 200
201 201 $ hg backout 1
202 202 abort: cannot backout change that is not an ancestor
203 203 [255]
204 204 $ hg summary
205 205 parent: 2:db815d6d32e6 tip
206 206 2
207 207 branch: default
208 208 commit: (clean)
209 209 update: 1 new changesets, 2 branch heads (merge)
210 210 phases: 3 draft
211 211
212 212 backout with merge
213 213
214 214 $ cd ..
215 215 $ hg init merge
216 216 $ cd merge
217 217
218 218 $ echo line 1 > a
219 219 $ echo line 2 >> a
220 220 $ hg commit -d '0 0' -A -m a
221 221 adding a
222 222 $ hg summary
223 223 parent: 0:59395513a13a tip
224 224 a
225 225 branch: default
226 226 commit: (clean)
227 227 update: (current)
228 228 phases: 1 draft
229 229
230 230 remove line 1
231 231
232 232 $ echo line 2 > a
233 233 $ hg commit -d '1 0' -m b
234 234
235 235 $ echo line 3 >> a
236 236 $ hg commit -d '2 0' -m c
237 237
238 238 $ hg backout --merge -d '3 0' 1 --tool=true
239 239 reverting a
240 240 created new head
241 241 changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182
242 242 merging with changeset 3:26b8ccb9ad91
243 243 merging a
244 244 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
245 245 (branch merge, don't forget to commit)
246 246 $ hg commit -d '4 0' -m d
247 247 $ hg summary
248 248 parent: 4:c7df5e0b9c09 tip
249 249 d
250 250 branch: default
251 251 commit: (clean)
252 252 update: (current)
253 253 phases: 5 draft
254 254
255 255 check line 1 is back
256 256
257 257 $ cat a
258 258 line 1
259 259 line 2
260 260 line 3
261 261
262 262 Test visibility of in-memory dirstate changes outside transaction to
263 263 external hook process
264 264
265 265 $ cat > $TESTTMP/checkvisibility.sh <<EOF
266 266 > echo "==== \$1:"
267 267 > hg parents --template "{rev}:{node|short}\n"
268 268 > echo "===="
269 269 > EOF
270 270
271 271 "hg backout --merge REV1" at REV2 below implies steps below:
272 272
273 273 (1) update to REV1 (REV2 => REV1)
274 274 (2) revert by REV1^1
275 275 (3) commit backnig out revision (REV3)
276 276 (4) update to REV2 (REV3 => REV2)
277 277 (5) merge with REV3 (REV2 => REV2, REV3)
278 278
279 279 == test visibility to external preupdate hook
280 280
281 281 $ hg update -q -C 2
282 282 $ hg --config extensions.strip= strip 3
283 283 saved backup bundle to * (glob)
284 284
285 285 $ cat >> .hg/hgrc <<EOF
286 286 > [hooks]
287 287 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
288 288 > EOF
289 289
290 290 ("-m" is needed to avoid writing dirstte changes out at other than
291 291 invocation of the hook to be examined)
292 292
293 293 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
294 294 ==== preupdate:
295 295 2:6ea3f2a197a2
296 296 ====
297 297 reverting a
298 298 created new head
299 299 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
300 300 ==== preupdate:
301 301 3:d92a3f57f067
302 302 ====
303 303 merging with changeset 3:d92a3f57f067
304 304 ==== preupdate:
305 305 2:6ea3f2a197a2
306 306 ====
307 307 merging a
308 308 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
309 309 (branch merge, don't forget to commit)
310 310
311 311 $ cat >> .hg/hgrc <<EOF
312 312 > [hooks]
313 313 > preupdate.visibility =
314 314 > EOF
315 315
316 == test visibility to external update hook
317
318 $ hg update -q -C 2
319 $ hg --config extensions.strip= strip 3
320 saved backup bundle to * (glob)
321
322 $ cat >> .hg/hgrc <<EOF
323 > [hooks]
324 > update.visibility = sh $TESTTMP/checkvisibility.sh update
325 > EOF
326
327 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
328 ==== update:
329 1:5a50a024c182
330 ====
331 reverting a
332 created new head
333 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
334 ==== update:
335 2:6ea3f2a197a2
336 ====
337 merging with changeset 3:d92a3f57f067
338 merging a
339 ==== update:
340 2:6ea3f2a197a2
341 3:d92a3f57f067
342 ====
343 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
344 (branch merge, don't forget to commit)
345
346 $ cat >> .hg/hgrc <<EOF
347 > [hooks]
348 > update.visibility =
349 > EOF
350
316 351 $ cd ..
317 352
318 353 backout should not back out subsequent changesets
319 354
320 355 $ hg init onecs
321 356 $ cd onecs
322 357 $ echo 1 > a
323 358 $ hg commit -d '0 0' -A -m a
324 359 adding a
325 360 $ echo 2 >> a
326 361 $ hg commit -d '1 0' -m b
327 362 $ echo 1 > b
328 363 $ hg commit -d '2 0' -A -m c
329 364 adding b
330 365 $ hg summary
331 366 parent: 2:882396649954 tip
332 367 c
333 368 branch: default
334 369 commit: (clean)
335 370 update: (current)
336 371 phases: 3 draft
337 372
338 373 without --merge
339 374 $ hg backout -d '3 0' 1 --tool=true
340 375 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 376 changeset 22bca4c721e5 backed out, don't forget to commit.
342 377 $ hg locate b
343 378 b
344 379 $ hg update -C tip
345 380 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
346 381 $ hg locate b
347 382 b
348 383 $ hg summary
349 384 parent: 2:882396649954 tip
350 385 c
351 386 branch: default
352 387 commit: (clean)
353 388 update: (current)
354 389 phases: 3 draft
355 390
356 391 with --merge
357 392 $ hg backout --merge -d '3 0' 1 --tool=true
358 393 reverting a
359 394 created new head
360 395 changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5
361 396 merging with changeset 3:3202beb76721
362 397 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 398 (branch merge, don't forget to commit)
364 399 $ hg locate b
365 400 b
366 401 $ hg update -C tip
367 402 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
368 403 $ hg locate b
369 404 [1]
370 405
371 406 $ cd ..
372 407 $ hg init m
373 408 $ cd m
374 409 $ echo a > a
375 410 $ hg commit -d '0 0' -A -m a
376 411 adding a
377 412 $ echo b > b
378 413 $ hg commit -d '1 0' -A -m b
379 414 adding b
380 415 $ echo c > c
381 416 $ hg commit -d '2 0' -A -m b
382 417 adding c
383 418 $ hg update 1
384 419 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
385 420 $ echo d > d
386 421 $ hg commit -d '3 0' -A -m c
387 422 adding d
388 423 created new head
389 424 $ hg merge 2
390 425 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 426 (branch merge, don't forget to commit)
392 427 $ hg commit -d '4 0' -A -m d
393 428 $ hg summary
394 429 parent: 4:b2f3bb92043e tip
395 430 d
396 431 branch: default
397 432 commit: (clean)
398 433 update: (current)
399 434 phases: 5 draft
400 435
401 436 backout of merge should fail
402 437
403 438 $ hg backout 4
404 439 abort: cannot backout a merge changeset
405 440 [255]
406 441
407 442 backout of merge with bad parent should fail
408 443
409 444 $ hg backout --parent 0 4
410 445 abort: cb9a9f314b8b is not a parent of b2f3bb92043e
411 446 [255]
412 447
413 448 backout of non-merge with parent should fail
414 449
415 450 $ hg backout --parent 0 3
416 451 abort: cannot use --parent on non-merge changeset
417 452 [255]
418 453
419 454 backout with valid parent should be ok
420 455
421 456 $ hg backout -d '5 0' --parent 2 4 --tool=true
422 457 removing d
423 458 changeset 5:10e5328c8435 backs out changeset 4:b2f3bb92043e
424 459 $ hg summary
425 460 parent: 5:10e5328c8435 tip
426 461 Backed out changeset b2f3bb92043e
427 462 branch: default
428 463 commit: (clean)
429 464 update: (current)
430 465 phases: 6 draft
431 466
432 467 $ hg rollback
433 468 repository tip rolled back to revision 4 (undo commit)
434 469 working directory now based on revision 4
435 470 $ hg update -C
436 471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437 472 $ hg summary
438 473 parent: 4:b2f3bb92043e tip
439 474 d
440 475 branch: default
441 476 commit: (clean)
442 477 update: (current)
443 478 phases: 5 draft
444 479
445 480 $ hg backout -d '6 0' --parent 3 4 --tool=true
446 481 removing c
447 482 changeset 5:033590168430 backs out changeset 4:b2f3bb92043e
448 483 $ hg summary
449 484 parent: 5:033590168430 tip
450 485 Backed out changeset b2f3bb92043e
451 486 branch: default
452 487 commit: (clean)
453 488 update: (current)
454 489 phases: 6 draft
455 490
456 491 $ cd ..
457 492
458 493 named branches
459 494
460 495 $ hg init named_branches
461 496 $ cd named_branches
462 497
463 498 $ echo default > default
464 499 $ hg ci -d '0 0' -Am default
465 500 adding default
466 501 $ hg branch branch1
467 502 marked working directory as branch branch1
468 503 (branches are permanent and global, did you want a bookmark?)
469 504 $ echo branch1 > file1
470 505 $ hg ci -d '1 0' -Am file1
471 506 adding file1
472 507 $ hg branch branch2
473 508 marked working directory as branch branch2
474 509 $ echo branch2 > file2
475 510 $ hg ci -d '2 0' -Am file2
476 511 adding file2
477 512
478 513 without --merge
479 514 $ hg backout -r 1 --tool=true
480 515 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
481 516 changeset bf1602f437f3 backed out, don't forget to commit.
482 517 $ hg branch
483 518 branch2
484 519 $ hg status -A
485 520 R file1
486 521 C default
487 522 C file2
488 523 $ hg summary
489 524 parent: 2:45bbcd363bf0 tip
490 525 file2
491 526 branch: branch2
492 527 commit: 1 removed
493 528 update: (current)
494 529 phases: 3 draft
495 530
496 531 with --merge
497 532 (this also tests that editor is invoked if '--edit' is specified
498 533 explicitly regardless of '--message')
499 534
500 535 $ hg update -qC
501 536 $ HGEDITOR=cat hg backout --merge -d '3 0' -r 1 -m 'backout on branch1' --tool=true --edit
502 537 removing file1
503 538 backout on branch1
504 539
505 540
506 541 HG: Enter commit message. Lines beginning with 'HG:' are removed.
507 542 HG: Leave message empty to abort commit.
508 543 HG: --
509 544 HG: user: test
510 545 HG: branch 'branch2'
511 546 HG: removed file1
512 547 created new head
513 548 changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3
514 549 merging with changeset 3:d4e8f6db59fb
515 550 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
516 551 (branch merge, don't forget to commit)
517 552 $ hg summary
518 553 parent: 2:45bbcd363bf0
519 554 file2
520 555 parent: 3:d4e8f6db59fb tip
521 556 backout on branch1
522 557 branch: branch2
523 558 commit: 1 removed (merge)
524 559 update: (current)
525 560 phases: 4 draft
526 561 $ hg update -q -C 2
527 562
528 563 on branch2 with branch1 not merged, so file1 should still exist:
529 564
530 565 $ hg id
531 566 45bbcd363bf0 (branch2)
532 567 $ hg st -A
533 568 C default
534 569 C file1
535 570 C file2
536 571 $ hg summary
537 572 parent: 2:45bbcd363bf0
538 573 file2
539 574 branch: branch2
540 575 commit: (clean)
541 576 update: 1 new changesets, 2 branch heads (merge)
542 577 phases: 4 draft
543 578
544 579 on branch2 with branch1 merged, so file1 should be gone:
545 580
546 581 $ hg merge
547 582 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
548 583 (branch merge, don't forget to commit)
549 584 $ hg ci -d '4 0' -m 'merge backout of branch1'
550 585 $ hg id
551 586 22149cdde76d (branch2) tip
552 587 $ hg st -A
553 588 C default
554 589 C file2
555 590 $ hg summary
556 591 parent: 4:22149cdde76d tip
557 592 merge backout of branch1
558 593 branch: branch2
559 594 commit: (clean)
560 595 update: (current)
561 596 phases: 5 draft
562 597
563 598 on branch1, so no file1 and file2:
564 599
565 600 $ hg co -C branch1
566 601 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
567 602 $ hg id
568 603 bf1602f437f3 (branch1)
569 604 $ hg st -A
570 605 C default
571 606 C file1
572 607 $ hg summary
573 608 parent: 1:bf1602f437f3
574 609 file1
575 610 branch: branch1
576 611 commit: (clean)
577 612 update: (current)
578 613 phases: 5 draft
579 614
580 615 $ cd ..
581 616
582 617 backout of empty changeset (issue4190)
583 618
584 619 $ hg init emptycommit
585 620 $ cd emptycommit
586 621
587 622 $ touch file1
588 623 $ hg ci -Aqm file1
589 624 $ hg branch -q branch1
590 625 $ hg ci -qm branch1
591 626 $ hg backout -v 1
592 627 resolving manifests
593 628 nothing changed
594 629 [1]
595 630
596 631 $ cd ..
597 632
598 633
599 634 Test usage of `hg resolve` in case of conflict
600 635 (issue4163)
601 636
602 637 $ hg init issue4163
603 638 $ cd issue4163
604 639 $ touch foo
605 640 $ hg add foo
606 641 $ cat > foo << EOF
607 642 > one
608 643 > two
609 644 > three
610 645 > four
611 646 > five
612 647 > six
613 648 > seven
614 649 > height
615 650 > nine
616 651 > ten
617 652 > EOF
618 653 $ hg ci -m 'initial'
619 654 $ cat > foo << EOF
620 655 > one
621 656 > two
622 657 > THREE
623 658 > four
624 659 > five
625 660 > six
626 661 > seven
627 662 > height
628 663 > nine
629 664 > ten
630 665 > EOF
631 666 $ hg ci -m 'capital three'
632 667 $ cat > foo << EOF
633 668 > one
634 669 > two
635 670 > THREE
636 671 > four
637 672 > five
638 673 > six
639 674 > seven
640 675 > height
641 676 > nine
642 677 > TEN
643 678 > EOF
644 679 $ hg ci -m 'capital ten'
645 680 $ hg backout -r 'desc("capital three")' --tool internal:fail
646 681 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
647 682 use 'hg resolve' to retry unresolved file merges
648 683 [1]
649 684 $ hg status
650 685 $ hg debugmergestate
651 686 * version 2 records
652 687 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
653 688 other: a30dd8addae3ce71b8667868478542bc417439e6
654 689 file: foo (state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
655 690 local path: foo (flags "")
656 691 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
657 692 other path: foo (node f50039b486d6fa1a90ae51778388cad161f425ee)
658 693 $ mv .hg/merge/state2 .hg/merge/state2-moved
659 694 $ hg debugmergestate
660 695 * version 1 records
661 696 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
662 697 file: foo (state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
663 698 local path: foo (flags "")
664 699 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
665 700 other path: foo (node not stored in v1 format)
666 701 $ mv .hg/merge/state2-moved .hg/merge/state2
667 702 $ hg resolve -l # still unresolved
668 703 U foo
669 704 $ hg summary
670 705 parent: 2:b71750c4b0fd tip
671 706 capital ten
672 707 branch: default
673 708 commit: 1 unresolved (clean)
674 709 update: (current)
675 710 phases: 3 draft
676 711 $ hg resolve --all --debug
677 712 picked tool ':merge' for foo (binary False symlink False)
678 713 merging foo
679 714 my foo@b71750c4b0fd+ other foo@a30dd8addae3 ancestor foo@913609522437
680 715 premerge successful
681 716 (no more unresolved files)
682 717 $ hg status
683 718 M foo
684 719 ? foo.orig
685 720 $ hg resolve -l
686 721 R foo
687 722 $ hg summary
688 723 parent: 2:b71750c4b0fd tip
689 724 capital ten
690 725 branch: default
691 726 commit: 1 modified, 1 unknown
692 727 update: (current)
693 728 phases: 3 draft
694 729 $ cat foo
695 730 one
696 731 two
697 732 three
698 733 four
699 734 five
700 735 six
701 736 seven
702 737 height
703 738 nine
704 739 TEN
705 740
706 741
@@ -1,147 +1,147 b''
1 1 setup
2 2 $ cat >> $HGRCPATH <<EOF
3 3 > [extensions]
4 4 > blackbox=
5 5 > mock=$TESTDIR/mockblackbox.py
6 6 > mq=
7 7 > EOF
8 8 $ hg init blackboxtest
9 9 $ cd blackboxtest
10 10
11 11 command, exit codes, and duration
12 12
13 13 $ echo a > a
14 14 $ hg add a
15 15 $ hg blackbox
16 16 1970/01/01 00:00:00 bob (*)> add a (glob)
17 17 1970/01/01 00:00:00 bob (*)> add a exited 0 after * seconds (glob)
18 18
19 19 incoming change tracking
20 20
21 21 create two heads to verify that we only see one change in the log later
22 22 $ hg commit -ma
23 23 $ hg up null
24 24 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
25 25 $ echo b > b
26 26 $ hg commit -Amb
27 27 adding b
28 28 created new head
29 29
30 30 clone, commit, pull
31 31 $ hg clone . ../blackboxtest2
32 32 updating to branch default
33 33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 34 $ echo c > c
35 35 $ hg commit -Amc
36 36 adding c
37 37 $ cd ../blackboxtest2
38 38 $ hg pull
39 39 pulling from $TESTTMP/blackboxtest (glob)
40 40 searching for changes
41 41 adding changesets
42 42 adding manifests
43 43 adding file changes
44 44 added 1 changesets with 1 changes to 1 files
45 45 (run 'hg update' to get a working copy)
46 46 $ hg blackbox -l 5
47 47 1970/01/01 00:00:00 bob (*)> pull (glob)
48 48 1970/01/01 00:00:00 bob (*)> updated served branch cache in ?.???? seconds (glob)
49 49 1970/01/01 00:00:00 bob (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
50 50 1970/01/01 00:00:00 bob (*)> 1 incoming changes - new heads: d02f48003e62 (glob)
51 51 1970/01/01 00:00:00 bob (*)> pull exited 0 after * seconds (glob)
52 52
53 53 we must not cause a failure if we cannot write to the log
54 54
55 55 $ hg rollback
56 56 repository tip rolled back to revision 1 (undo pull)
57 57
58 58 #if unix-permissions no-root
59 59 $ chmod 000 .hg/blackbox.log
60 60 $ hg --debug incoming
61 61 warning: cannot write to blackbox.log: Permission denied
62 62 comparing with $TESTTMP/blackboxtest (glob)
63 63 query 1; heads
64 64 searching for changes
65 65 all local heads known remotely
66 66 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
67 67 tag: tip
68 68 phase: draft
69 69 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
70 70 parent: -1:0000000000000000000000000000000000000000
71 71 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
72 72 user: test
73 73 date: Thu Jan 01 00:00:00 1970 +0000
74 74 files+: c
75 75 extra: branch=default
76 76 description:
77 77 c
78 78
79 79
80 80 #endif
81 81 $ hg pull
82 82 pulling from $TESTTMP/blackboxtest (glob)
83 83 searching for changes
84 84 adding changesets
85 85 adding manifests
86 86 adding file changes
87 87 added 1 changesets with 1 changes to 1 files
88 88 (run 'hg update' to get a working copy)
89 89
90 90 a failure reading from the log is fine
91 91 #if unix-permissions no-root
92 92 $ hg blackbox -l 3
93 93 abort: Permission denied: $TESTTMP/blackboxtest2/.hg/blackbox.log
94 94 [255]
95 95
96 96 $ chmod 600 .hg/blackbox.log
97 97 #endif
98 98
99 99 backup bundles get logged
100 100
101 101 $ touch d
102 102 $ hg commit -Amd
103 103 adding d
104 104 created new head
105 105 $ hg strip tip
106 106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
107 107 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
108 108 $ hg blackbox -l 5
109 109 1970/01/01 00:00:00 bob (*)> strip tip (glob)
110 110 1970/01/01 00:00:00 bob (*)> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
111 111 1970/01/01 00:00:00 bob (*)> updated base branch cache in ?.???? seconds (glob)
112 112 1970/01/01 00:00:00 bob (*)> wrote base branch cache with 1 labels and 2 nodes (glob)
113 113 1970/01/01 00:00:00 bob (*)> strip tip exited 0 after * seconds (glob)
114 114
115 115 extension and python hooks - use the eol extension for a pythonhook
116 116
117 117 $ echo '[extensions]' >> .hg/hgrc
118 118 $ echo 'eol=' >> .hg/hgrc
119 119 $ echo '[hooks]' >> .hg/hgrc
120 120 $ echo 'update = echo hooked' >> .hg/hgrc
121 121 $ hg update
122 hooked
122 123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 hooked
124 124 $ hg blackbox -l 5
125 125 1970/01/01 00:00:00 bob (*)> update (glob)
126 126 1970/01/01 00:00:00 bob (*)> writing .hg/cache/tags2-visible with 0 tags (glob)
127 127 1970/01/01 00:00:00 bob (*)> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
128 128 1970/01/01 00:00:00 bob (*)> exthook-update: echo hooked finished in * seconds (glob)
129 129 1970/01/01 00:00:00 bob (*)> update exited 0 after * seconds (glob)
130 130
131 131 log rotation
132 132
133 133 $ echo '[blackbox]' >> .hg/hgrc
134 134 $ echo 'maxsize = 20 b' >> .hg/hgrc
135 135 $ echo 'maxfiles = 3' >> .hg/hgrc
136 136 $ hg status
137 137 $ hg status
138 138 $ hg status
139 139 $ hg tip -q
140 140 2:d02f48003e62
141 141 $ ls .hg/blackbox.log*
142 142 .hg/blackbox.log
143 143 .hg/blackbox.log.1
144 144 .hg/blackbox.log.2
145 145
146 146 cleanup
147 147 $ cd ..
@@ -1,710 +1,710 b''
1 1 commit hooks can see env vars
2 2 (and post-transaction one are run unlocked)
3 3
4 4 $ cat << EOF >> $HGRCPATH
5 5 > [experimental]
6 6 > # drop me once bundle2 is the default,
7 7 > # added to get test change early.
8 8 > bundle2-exp = True
9 9 > EOF
10 10
11 11 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
12 12 > def showargs(ui, repo, hooktype, **kwargs):
13 13 > ui.write('%s python hook: %s\n' % (hooktype, ','.join(sorted(kwargs))))
14 14 > EOF
15 15
16 16 $ hg init a
17 17 $ cd a
18 18 $ cat > .hg/hgrc <<EOF
19 19 > [hooks]
20 20 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py commit"
21 21 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py commit.b"
22 22 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py precommit"
23 23 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxncommit"
24 24 > pretxncommit.tip = hg -q tip
25 25 > pre-identify = printenv.py pre-identify 1
26 26 > pre-cat = printenv.py pre-cat
27 27 > post-cat = printenv.py post-cat
28 28 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnopen"
29 29 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnclose"
30 30 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py txnclose"
31 31 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
32 32 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py txnabort"
33 33 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
34 34 > EOF
35 35 $ echo a > a
36 36 $ hg add a
37 37 $ hg commit -m a
38 38 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
39 39 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
40 40 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
41 41 0:cb9a9f314b8b
42 42 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
43 43 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
44 44 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
45 45 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
46 46
47 47 $ hg clone . ../b
48 48 updating to branch default
49 49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 50 $ cd ../b
51 51
52 52 changegroup hooks can see env vars
53 53
54 54 $ cat > .hg/hgrc <<EOF
55 55 > [hooks]
56 56 > prechangegroup = printenv.py prechangegroup
57 57 > changegroup = printenv.py changegroup
58 58 > incoming = printenv.py incoming
59 59 > EOF
60 60
61 61 pretxncommit and commit hooks can see both parents of merge
62 62
63 63 $ cd ../a
64 64 $ echo b >> a
65 65 $ hg commit -m a1 -d "1 0"
66 66 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 67 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
68 68 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
69 69 1:ab228980c14d
70 70 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
71 71 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
72 72 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
73 73 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
74 74 $ hg update -C 0
75 75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76 $ echo b > b
77 77 $ hg add b
78 78 $ hg commit -m b -d '1 0'
79 79 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
80 80 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
81 81 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
82 82 2:ee9deb46ab31
83 83 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
84 84 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
85 85 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
86 86 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
87 87 created new head
88 88 $ hg merge 1
89 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 (branch merge, don't forget to commit)
91 91 $ hg commit -m merge -d '2 0'
92 92 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
93 93 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
94 94 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
95 95 3:07f3376c1e65
96 96 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
97 97 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
98 98 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
99 99 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
100 100
101 101 test generic hooks
102 102
103 103 $ hg id
104 104 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[]
105 105 abort: pre-identify hook exited with status 1
106 106 [255]
107 107 $ hg cat b
108 108 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
109 109 b
110 110 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
111 111
112 112 $ cd ../b
113 113 $ hg pull ../a
114 114 pulling from ../a
115 115 searching for changes
116 116 prechangegroup hook: HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
117 117 adding changesets
118 118 adding manifests
119 119 adding file changes
120 120 added 3 changesets with 2 changes to 2 files
121 121 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
122 122 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
123 123 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
124 124 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
125 125 (run 'hg update' to get a working copy)
126 126
127 127 tag hooks can see env vars
128 128
129 129 $ cd ../a
130 130 $ cat >> .hg/hgrc <<EOF
131 131 > pretag = printenv.py pretag
132 132 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py tag"
133 133 > EOF
134 134 $ hg tag -d '3 0' a
135 135 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
136 136 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
137 137 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
138 138 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
139 139 4:539e4b31b6dc
140 140 pretxnclose hook: HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
141 141 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
142 142 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
143 143 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
144 144 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
145 145 $ hg tag -l la
146 146 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
147 147 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
148 148
149 149 pretag hook can forbid tagging
150 150
151 151 $ echo "pretag.forbid = printenv.py pretag.forbid 1" >> .hg/hgrc
152 152 $ hg tag -d '4 0' fa
153 153 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
154 154 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
155 155 abort: pretag.forbid hook exited with status 1
156 156 [255]
157 157 $ hg tag -l fla
158 158 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
159 159 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
160 160 abort: pretag.forbid hook exited with status 1
161 161 [255]
162 162
163 163 pretxncommit hook can see changeset, can roll back txn, changeset no
164 164 more there after
165 165
166 166 $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc
167 167 $ echo "pretxncommit.forbid1 = printenv.py pretxncommit.forbid 1" >> .hg/hgrc
168 168 $ echo z > z
169 169 $ hg add z
170 170 $ hg -q tip
171 171 4:539e4b31b6dc
172 172 $ hg commit -m 'fail' -d '4 0'
173 173 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
174 174 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
175 175 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
176 176 5:6f611f8018c1
177 177 5:6f611f8018c1
178 178 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
179 179 transaction abort!
180 180 txnabort python hook: txnid,txnname
181 181 txnabort hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
182 182 rollback completed
183 183 abort: pretxncommit.forbid1 hook exited with status 1
184 184 [255]
185 185 $ hg -q tip
186 186 4:539e4b31b6dc
187 187
188 188 (Check that no 'changelog.i.a' file were left behind)
189 189
190 190 $ ls -1 .hg/store/
191 191 00changelog.i
192 192 00manifest.i
193 193 data
194 194 fncache
195 195 journal.phaseroots
196 196 phaseroots
197 197 undo
198 198 undo.backup.fncache
199 199 undo.backupfiles
200 200 undo.phaseroots
201 201
202 202
203 203 precommit hook can prevent commit
204 204
205 205 $ echo "precommit.forbid = printenv.py precommit.forbid 1" >> .hg/hgrc
206 206 $ hg commit -m 'fail' -d '4 0'
207 207 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
208 208 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
209 209 abort: precommit.forbid hook exited with status 1
210 210 [255]
211 211 $ hg -q tip
212 212 4:539e4b31b6dc
213 213
214 214 preupdate hook can prevent update
215 215
216 216 $ echo "preupdate = printenv.py preupdate" >> .hg/hgrc
217 217 $ hg update 1
218 218 preupdate hook: HG_PARENT1=ab228980c14d
219 219 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
220 220
221 221 update hook
222 222
223 223 $ echo "update = printenv.py update" >> .hg/hgrc
224 224 $ hg update
225 225 preupdate hook: HG_PARENT1=539e4b31b6dc
226 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
226 227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
228 228
229 229 pushkey hook
230 230
231 231 $ echo "pushkey = printenv.py pushkey" >> .hg/hgrc
232 232 $ cd ../b
233 233 $ hg bookmark -r null foo
234 234 $ hg push -B foo ../a
235 235 pushing to ../a
236 236 searching for changes
237 237 no changes found
238 238 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=push (glob)
239 239 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_PENDING=$TESTTMP/a HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
240 240 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
241 241 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
242 242 exporting bookmark foo
243 243 [1]
244 244 $ cd ../a
245 245
246 246 listkeys hook
247 247
248 248 $ echo "listkeys = printenv.py listkeys" >> .hg/hgrc
249 249 $ hg bookmark -r null bar
250 250 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
251 251 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
252 252 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
253 253 $ cd ../b
254 254 $ hg pull -B bar ../a
255 255 pulling from ../a
256 256 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
257 257 no changes found
258 258 listkeys hook: HG_NAMESPACE=phase HG_VALUES={}
259 259 adding remote bookmark bar
260 260 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
261 261 $ cd ../a
262 262
263 263 test that prepushkey can prevent incoming keys
264 264
265 265 $ echo "prepushkey = printenv.py prepushkey.forbid 1" >> .hg/hgrc
266 266 $ cd ../b
267 267 $ hg bookmark -r null baz
268 268 $ hg push -B baz ../a
269 269 pushing to ../a
270 270 searching for changes
271 271 listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
272 272 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
273 273 no changes found
274 274 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=push (glob)
275 275 prepushkey.forbid hook: HG_BUNDLE2=1 HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_SOURCE=push HG_TXNID=TXN:* HG_URL=push (glob)
276 276 pushkey-abort: prepushkey hook exited with status 1
277 277 abort: exporting bookmark baz failed!
278 278 [255]
279 279 $ cd ../a
280 280
281 281 test that prelistkeys can prevent listing keys
282 282
283 283 $ echo "prelistkeys = printenv.py prelistkeys.forbid 1" >> .hg/hgrc
284 284 $ hg bookmark -r null quux
285 285 pretxnopen hook: HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
286 286 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_PENDING=$TESTTMP/a HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
287 287 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
288 288 $ cd ../b
289 289 $ hg pull -B quux ../a
290 290 pulling from ../a
291 291 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
292 292 abort: prelistkeys hook exited with status 1
293 293 [255]
294 294 $ cd ../a
295 295 $ rm .hg/hgrc
296 296
297 297 prechangegroup hook can prevent incoming changes
298 298
299 299 $ cd ../b
300 300 $ hg -q tip
301 301 3:07f3376c1e65
302 302 $ cat > .hg/hgrc <<EOF
303 303 > [hooks]
304 304 > prechangegroup.forbid = printenv.py prechangegroup.forbid 1
305 305 > EOF
306 306 $ hg pull ../a
307 307 pulling from ../a
308 308 searching for changes
309 309 prechangegroup.forbid hook: HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
310 310 abort: prechangegroup.forbid hook exited with status 1
311 311 [255]
312 312
313 313 pretxnchangegroup hook can see incoming changes, can roll back txn,
314 314 incoming changes no longer there after
315 315
316 316 $ cat > .hg/hgrc <<EOF
317 317 > [hooks]
318 318 > pretxnchangegroup.forbid0 = hg tip -q
319 319 > pretxnchangegroup.forbid1 = printenv.py pretxnchangegroup.forbid 1
320 320 > EOF
321 321 $ hg pull ../a
322 322 pulling from ../a
323 323 searching for changes
324 324 adding changesets
325 325 adding manifests
326 326 adding file changes
327 327 added 1 changesets with 1 changes to 1 files
328 328 4:539e4b31b6dc
329 329 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=file:$TESTTMP/a (glob)
330 330 transaction abort!
331 331 rollback completed
332 332 abort: pretxnchangegroup.forbid1 hook exited with status 1
333 333 [255]
334 334 $ hg -q tip
335 335 3:07f3376c1e65
336 336
337 337 outgoing hooks can see env vars
338 338
339 339 $ rm .hg/hgrc
340 340 $ cat > ../a/.hg/hgrc <<EOF
341 341 > [hooks]
342 342 > preoutgoing = printenv.py preoutgoing
343 343 > outgoing = printenv.py outgoing
344 344 > EOF
345 345 $ hg pull ../a
346 346 pulling from ../a
347 347 searching for changes
348 348 preoutgoing hook: HG_SOURCE=pull
349 349 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
350 350 adding changesets
351 351 adding manifests
352 352 adding file changes
353 353 added 1 changesets with 1 changes to 1 files
354 354 adding remote bookmark quux
355 355 (run 'hg update' to get a working copy)
356 356 $ hg rollback
357 357 repository tip rolled back to revision 3 (undo pull)
358 358
359 359 preoutgoing hook can prevent outgoing changes
360 360
361 361 $ echo "preoutgoing.forbid = printenv.py preoutgoing.forbid 1" >> ../a/.hg/hgrc
362 362 $ hg pull ../a
363 363 pulling from ../a
364 364 searching for changes
365 365 preoutgoing hook: HG_SOURCE=pull
366 366 preoutgoing.forbid hook: HG_SOURCE=pull
367 367 abort: preoutgoing.forbid hook exited with status 1
368 368 [255]
369 369
370 370 outgoing hooks work for local clones
371 371
372 372 $ cd ..
373 373 $ cat > a/.hg/hgrc <<EOF
374 374 > [hooks]
375 375 > preoutgoing = printenv.py preoutgoing
376 376 > outgoing = printenv.py outgoing
377 377 > EOF
378 378 $ hg clone a c
379 379 preoutgoing hook: HG_SOURCE=clone
380 380 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
381 381 updating to branch default
382 382 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 383 $ rm -rf c
384 384
385 385 preoutgoing hook can prevent outgoing changes for local clones
386 386
387 387 $ echo "preoutgoing.forbid = printenv.py preoutgoing.forbid 1" >> a/.hg/hgrc
388 388 $ hg clone a zzz
389 389 preoutgoing hook: HG_SOURCE=clone
390 390 preoutgoing.forbid hook: HG_SOURCE=clone
391 391 abort: preoutgoing.forbid hook exited with status 1
392 392 [255]
393 393
394 394 $ cd "$TESTTMP/b"
395 395
396 396 $ cat > hooktests.py <<EOF
397 397 > from mercurial import error
398 398 >
399 399 > uncallable = 0
400 400 >
401 401 > def printargs(args):
402 402 > args.pop('ui', None)
403 403 > args.pop('repo', None)
404 404 > a = list(args.items())
405 405 > a.sort()
406 406 > print 'hook args:'
407 407 > for k, v in a:
408 408 > print ' ', k, v
409 409 >
410 410 > def passhook(**args):
411 411 > printargs(args)
412 412 >
413 413 > def failhook(**args):
414 414 > printargs(args)
415 415 > return True
416 416 >
417 417 > class LocalException(Exception):
418 418 > pass
419 419 >
420 420 > def raisehook(**args):
421 421 > raise LocalException('exception from hook')
422 422 >
423 423 > def aborthook(**args):
424 424 > raise error.Abort('raise abort from hook')
425 425 >
426 426 > def brokenhook(**args):
427 427 > return 1 + {}
428 428 >
429 429 > def verbosehook(ui, **args):
430 430 > ui.note('verbose output from hook\n')
431 431 >
432 432 > def printtags(ui, repo, **args):
433 433 > print sorted(repo.tags())
434 434 >
435 435 > class container:
436 436 > unreachable = 1
437 437 > EOF
438 438
439 439 test python hooks
440 440
441 441 #if windows
442 442 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
443 443 #else
444 444 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
445 445 #endif
446 446 $ export PYTHONPATH
447 447
448 448 $ echo '[hooks]' > ../a/.hg/hgrc
449 449 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
450 450 $ hg pull ../a 2>&1 | grep 'raised an exception'
451 451 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
452 452
453 453 $ echo '[hooks]' > ../a/.hg/hgrc
454 454 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
455 455 $ hg pull ../a 2>&1 | grep 'raised an exception'
456 456 error: preoutgoing.raise hook raised an exception: exception from hook
457 457
458 458 $ echo '[hooks]' > ../a/.hg/hgrc
459 459 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
460 460 $ hg pull ../a
461 461 pulling from ../a
462 462 searching for changes
463 463 error: preoutgoing.abort hook failed: raise abort from hook
464 464 abort: raise abort from hook
465 465 [255]
466 466
467 467 $ echo '[hooks]' > ../a/.hg/hgrc
468 468 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
469 469 $ hg pull ../a
470 470 pulling from ../a
471 471 searching for changes
472 472 hook args:
473 473 hooktype preoutgoing
474 474 source pull
475 475 abort: preoutgoing.fail hook failed
476 476 [255]
477 477
478 478 $ echo '[hooks]' > ../a/.hg/hgrc
479 479 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
480 480 $ hg pull ../a
481 481 pulling from ../a
482 482 searching for changes
483 483 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
484 484 [255]
485 485
486 486 $ echo '[hooks]' > ../a/.hg/hgrc
487 487 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
488 488 $ hg pull ../a
489 489 pulling from ../a
490 490 searching for changes
491 491 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
492 492 [255]
493 493
494 494 $ echo '[hooks]' > ../a/.hg/hgrc
495 495 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
496 496 $ hg pull ../a
497 497 pulling from ../a
498 498 searching for changes
499 499 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
500 500 [255]
501 501
502 502 $ echo '[hooks]' > ../a/.hg/hgrc
503 503 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
504 504 $ hg pull ../a
505 505 pulling from ../a
506 506 searching for changes
507 507 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
508 508 [255]
509 509
510 510 $ echo '[hooks]' > ../a/.hg/hgrc
511 511 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
512 512 $ hg pull ../a
513 513 pulling from ../a
514 514 searching for changes
515 515 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
516 516 [255]
517 517
518 518 $ echo '[hooks]' > ../a/.hg/hgrc
519 519 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
520 520 $ hg pull ../a
521 521 pulling from ../a
522 522 searching for changes
523 523 hook args:
524 524 hooktype preoutgoing
525 525 source pull
526 526 adding changesets
527 527 adding manifests
528 528 adding file changes
529 529 added 1 changesets with 1 changes to 1 files
530 530 adding remote bookmark quux
531 531 (run 'hg update' to get a working copy)
532 532
533 533 make sure --traceback works
534 534
535 535 $ echo '[hooks]' > .hg/hgrc
536 536 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
537 537
538 538 $ echo aa > a
539 539 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
540 540 Traceback (most recent call last):
541 541
542 542 $ cd ..
543 543 $ hg init c
544 544 $ cd c
545 545
546 546 $ cat > hookext.py <<EOF
547 547 > def autohook(**args):
548 548 > print "Automatically installed hook"
549 549 >
550 550 > def reposetup(ui, repo):
551 551 > repo.ui.setconfig("hooks", "commit.auto", autohook)
552 552 > EOF
553 553 $ echo '[extensions]' >> .hg/hgrc
554 554 $ echo 'hookext = hookext.py' >> .hg/hgrc
555 555
556 556 $ touch foo
557 557 $ hg add foo
558 558 $ hg ci -d '0 0' -m 'add foo'
559 559 Automatically installed hook
560 560 $ echo >> foo
561 561 $ hg ci --debug -d '0 0' -m 'change foo'
562 562 committing files:
563 563 foo
564 564 committing manifest
565 565 committing changelog
566 566 calling hook commit.auto: hgext_hookext.autohook
567 567 Automatically installed hook
568 568 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
569 569
570 570 $ hg showconfig hooks
571 571 hooks.commit.auto=<function autohook at *> (glob)
572 572
573 573 test python hook configured with python:[file]:[hook] syntax
574 574
575 575 $ cd ..
576 576 $ mkdir d
577 577 $ cd d
578 578 $ hg init repo
579 579 $ mkdir hooks
580 580
581 581 $ cd hooks
582 582 $ cat > testhooks.py <<EOF
583 583 > def testhook(**args):
584 584 > print 'hook works'
585 585 > EOF
586 586 $ echo '[hooks]' > ../repo/.hg/hgrc
587 587 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
588 588
589 589 $ cd ../repo
590 590 $ hg commit -d '0 0'
591 591 hook works
592 592 nothing changed
593 593 [1]
594 594
595 595 $ echo '[hooks]' > .hg/hgrc
596 596 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
597 597 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
598 598
599 599 $ hg up null
600 600 loading update.ne hook failed:
601 601 abort: No such file or directory: $TESTTMP/d/repo/nonexistent.py
602 602 [255]
603 603
604 604 $ hg id
605 605 loading pre-identify.npmd hook failed:
606 606 abort: No module named repo!
607 607 [255]
608 608
609 609 $ cd ../../b
610 610
611 611 make sure --traceback works on hook import failure
612 612
613 613 $ cat > importfail.py <<EOF
614 614 > import somebogusmodule
615 615 > # dereference something in the module to force demandimport to load it
616 616 > somebogusmodule.whatever
617 617 > EOF
618 618
619 619 $ echo '[hooks]' > .hg/hgrc
620 620 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
621 621
622 622 $ echo a >> a
623 623 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
624 624 exception from first failed import attempt:
625 625 Traceback (most recent call last):
626 626 ImportError: No module named somebogusmodule
627 627 exception from second failed import attempt:
628 628 Traceback (most recent call last):
629 629 ImportError: No module named hgext_importfail
630 630 Traceback (most recent call last):
631 631 HookLoadError: precommit.importfail hook is invalid (import of "importfail" failed)
632 632 abort: precommit.importfail hook is invalid (import of "importfail" failed)
633 633
634 634 Issue1827: Hooks Update & Commit not completely post operation
635 635
636 636 commit and update hooks should run after command completion. The largefiles
637 637 use demonstrates a recursive wlock, showing the hook doesn't run until the
638 638 final release (and dirstate flush).
639 639
640 640 $ echo '[hooks]' > .hg/hgrc
641 641 $ echo 'commit = hg id' >> .hg/hgrc
642 642 $ echo 'update = hg id' >> .hg/hgrc
643 643 $ echo bb > a
644 644 $ hg ci -ma
645 645 223eafe2750c tip
646 646 $ hg up 0 --config extensions.largefiles=
647 cb9a9f314b8b
647 648 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 cb9a9f314b8b
649 649
650 650 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
651 651 that is passed to pre/post hooks
652 652
653 653 $ echo '[hooks]' > .hg/hgrc
654 654 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
655 655 $ hg id
656 656 cb9a9f314b8b
657 657 $ hg id --verbose
658 658 calling hook pre-identify: hooktests.verbosehook
659 659 verbose output from hook
660 660 cb9a9f314b8b
661 661
662 662 Ensure hooks can be prioritized
663 663
664 664 $ echo '[hooks]' > .hg/hgrc
665 665 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
666 666 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
667 667 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
668 668 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
669 669 $ hg id --verbose
670 670 calling hook pre-identify.b: hooktests.verbosehook
671 671 verbose output from hook
672 672 calling hook pre-identify.a: hooktests.verbosehook
673 673 verbose output from hook
674 674 calling hook pre-identify.c: hooktests.verbosehook
675 675 verbose output from hook
676 676 cb9a9f314b8b
677 677
678 678 new tags must be visible in pretxncommit (issue3210)
679 679
680 680 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
681 681 $ hg tag -f foo
682 682 ['a', 'foo', 'tip']
683 683
684 684 new commits must be visible in pretxnchangegroup (issue3428)
685 685
686 686 $ cd ..
687 687 $ hg init to
688 688 $ echo '[hooks]' >> to/.hg/hgrc
689 689 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
690 690 $ echo a >> to/a
691 691 $ hg --cwd to ci -Ama
692 692 adding a
693 693 $ hg clone to from
694 694 updating to branch default
695 695 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
696 696 $ echo aa >> from/a
697 697 $ hg --cwd from ci -mb
698 698 $ hg --cwd from push
699 699 pushing to $TESTTMP/to (glob)
700 700 searching for changes
701 701 adding changesets
702 702 adding manifests
703 703 adding file changes
704 704 added 1 changesets with 1 changes to 1 files
705 705 changeset: 1:9836a07b9b9d
706 706 tag: tip
707 707 user: test
708 708 date: Thu Jan 01 00:00:00 1970 +0000
709 709 summary: b
710 710
@@ -1,1130 +1,1176 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > mq =
4 4 > shelve =
5 5 > [defaults]
6 6 > diff = --nodates --git
7 7 > qnew = --date '0 0'
8 8 > [shelve]
9 9 > maxbackups = 2
10 10 > EOF
11 11
12 12 $ hg init repo
13 13 $ cd repo
14 14 $ mkdir a b
15 15 $ echo a > a/a
16 16 $ echo b > b/b
17 17 $ echo c > c
18 18 $ echo d > d
19 19 $ echo x > x
20 20 $ hg addremove -q
21 21
22 22 shelve has a help message
23 23 $ hg shelve -h
24 24 hg shelve [OPTION]... [FILE]...
25 25
26 26 save and set aside changes from the working directory
27 27
28 28 Shelving takes files that "hg status" reports as not clean, saves the
29 29 modifications to a bundle (a shelved change), and reverts the files so
30 30 that their state in the working directory becomes clean.
31 31
32 32 To restore these changes to the working directory, using "hg unshelve";
33 33 this will work even if you switch to a different commit.
34 34
35 35 When no files are specified, "hg shelve" saves all not-clean files. If
36 36 specific files or directories are named, only changes to those files are
37 37 shelved.
38 38
39 39 Each shelved change has a name that makes it easier to find later. The
40 40 name of a shelved change defaults to being based on the active bookmark,
41 41 or if there is no active bookmark, the current named branch. To specify a
42 42 different name, use "--name".
43 43
44 44 To see a list of existing shelved changes, use the "--list" option. For
45 45 each shelved change, this will print its name, age, and description; use "
46 46 --patch" or "--stat" for more details.
47 47
48 48 To delete specific shelved changes, use "--delete". To delete all shelved
49 49 changes, use "--cleanup".
50 50
51 51 (use "hg help -e shelve" to show help for the shelve extension)
52 52
53 53 options ([+] can be repeated):
54 54
55 55 -A --addremove mark new/missing files as added/removed before
56 56 shelving
57 57 --cleanup delete all shelved changes
58 58 --date DATE shelve with the specified commit date
59 59 -d --delete delete the named shelved change(s)
60 60 -e --edit invoke editor on commit messages
61 61 -l --list list current shelves
62 62 -m --message TEXT use text as shelve message
63 63 -n --name NAME use the given name for the shelved commit
64 64 -p --patch show patch
65 65 -i --interactive interactive mode, only works while creating a shelve
66 66 --stat output diffstat-style summary of changes
67 67 -I --include PATTERN [+] include names matching the given patterns
68 68 -X --exclude PATTERN [+] exclude names matching the given patterns
69 69 --mq operate on patch repository
70 70
71 71 (some details hidden, use --verbose to show complete help)
72 72
73 73 shelving in an empty repo should be possible
74 74 (this tests also that editor is not invoked, if '--edit' is not
75 75 specified)
76 76
77 77 $ HGEDITOR=cat hg shelve
78 78 shelved as default
79 79 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
80 80
81 81 $ hg unshelve
82 82 unshelving change 'default'
83 83
84 84 $ hg commit -q -m 'initial commit'
85 85
86 86 $ hg shelve
87 87 nothing changed
88 88 [1]
89 89
90 90 make sure shelve files were backed up
91 91
92 92 $ ls .hg/shelve-backup
93 93 default.hg
94 94 default.patch
95 95
96 96 create an mq patch - shelving should work fine with a patch applied
97 97
98 98 $ echo n > n
99 99 $ hg add n
100 100 $ hg commit n -m second
101 101 $ hg qnew second.patch
102 102
103 103 shelve a change that we will delete later
104 104
105 105 $ echo a >> a/a
106 106 $ hg shelve
107 107 shelved as default
108 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109
110 110 set up some more complex changes to shelve
111 111
112 112 $ echo a >> a/a
113 113 $ hg mv b b.rename
114 114 moving b/b to b.rename/b (glob)
115 115 $ hg cp c c.copy
116 116 $ hg status -C
117 117 M a/a
118 118 A b.rename/b
119 119 b/b
120 120 A c.copy
121 121 c
122 122 R b/b
123 123
124 124 prevent some foot-shooting
125 125
126 126 $ hg shelve -n foo/bar
127 127 abort: shelved change names may not contain slashes
128 128 [255]
129 129 $ hg shelve -n .baz
130 130 abort: shelved change names may not start with '.'
131 131 [255]
132 132
133 133 the common case - no options or filenames
134 134
135 135 $ hg shelve
136 136 shelved as default-01
137 137 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
138 138 $ hg status -C
139 139
140 140 ensure that our shelved changes exist
141 141
142 142 $ hg shelve -l
143 143 default-01 (*)* changes to '[mq]: second.patch' (glob)
144 144 default (*)* changes to '[mq]: second.patch' (glob)
145 145
146 146 $ hg shelve -l -p default
147 147 default (*)* changes to '[mq]: second.patch' (glob)
148 148
149 149 diff --git a/a/a b/a/a
150 150 --- a/a/a
151 151 +++ b/a/a
152 152 @@ -1,1 +1,2 @@
153 153 a
154 154 +a
155 155
156 156 $ hg shelve --list --addremove
157 157 abort: options '--list' and '--addremove' may not be used together
158 158 [255]
159 159
160 160 delete our older shelved change
161 161
162 162 $ hg shelve -d default
163 163 $ hg qfinish -a -q
164 164
165 165 ensure shelve backups aren't overwritten
166 166
167 167 $ ls .hg/shelve-backup/
168 168 default-1.hg
169 169 default-1.patch
170 170 default.hg
171 171 default.patch
172 172
173 173 local edits should not prevent a shelved change from applying
174 174
175 175 $ printf "z\na\n" > a/a
176 176 $ hg unshelve --keep
177 177 unshelving change 'default-01'
178 178 temporarily committing pending changes (restore with 'hg unshelve --abort')
179 179 rebasing shelved changes
180 180 rebasing 4:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
181 181 merging a/a
182 182
183 183 $ hg revert --all -q
184 184 $ rm a/a.orig b.rename/b c.copy
185 185
186 186 apply it and make sure our state is as expected
187 187
188 188 (this also tests that same timestamp prevents backups from being
189 189 removed, even though there are more than 'maxbackups' backups)
190 190
191 191 $ f -t .hg/shelve-backup/default.hg
192 192 .hg/shelve-backup/default.hg: file
193 193 $ touch -t 200001010000 .hg/shelve-backup/default.hg
194 194 $ f -t .hg/shelve-backup/default-1.hg
195 195 .hg/shelve-backup/default-1.hg: file
196 196 $ touch -t 200001010000 .hg/shelve-backup/default-1.hg
197 197
198 198 $ hg unshelve
199 199 unshelving change 'default-01'
200 200 $ hg status -C
201 201 M a/a
202 202 A b.rename/b
203 203 b/b
204 204 A c.copy
205 205 c
206 206 R b/b
207 207 $ hg shelve -l
208 208
209 209 (both of default.hg and default-1.hg should be still kept, because it
210 210 is difficult to decide actual order of them from same timestamp)
211 211
212 212 $ ls .hg/shelve-backup/
213 213 default-01.hg
214 214 default-01.patch
215 215 default-1.hg
216 216 default-1.patch
217 217 default.hg
218 218 default.patch
219 219
220 220 $ hg unshelve
221 221 abort: no shelved changes to apply!
222 222 [255]
223 223 $ hg unshelve foo
224 224 abort: shelved change 'foo' not found
225 225 [255]
226 226
227 227 named shelves, specific filenames, and "commit messages" should all work
228 228 (this tests also that editor is invoked, if '--edit' is specified)
229 229
230 230 $ hg status -C
231 231 M a/a
232 232 A b.rename/b
233 233 b/b
234 234 A c.copy
235 235 c
236 236 R b/b
237 237 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
238 238 wat
239 239
240 240
241 241 HG: Enter commit message. Lines beginning with 'HG:' are removed.
242 242 HG: Leave message empty to abort commit.
243 243 HG: --
244 244 HG: user: shelve@localhost
245 245 HG: branch 'default'
246 246 HG: changed a/a
247 247
248 248 expect "a" to no longer be present, but status otherwise unchanged
249 249
250 250 $ hg status -C
251 251 A b.rename/b
252 252 b/b
253 253 A c.copy
254 254 c
255 255 R b/b
256 256 $ hg shelve -l --stat
257 257 wibble (*) wat (glob)
258 258 a/a | 1 +
259 259 1 files changed, 1 insertions(+), 0 deletions(-)
260 260
261 261 and now "a/a" should reappear
262 262
263 263 $ cd a
264 264 $ hg unshelve -q wibble
265 265 $ cd ..
266 266 $ hg status -C
267 267 M a/a
268 268 A b.rename/b
269 269 b/b
270 270 A c.copy
271 271 c
272 272 R b/b
273 273
274 274 ensure old shelve backups are being deleted automatically
275 275
276 276 $ ls .hg/shelve-backup/
277 277 default-01.hg
278 278 default-01.patch
279 279 wibble.hg
280 280 wibble.patch
281 281
282 282 cause unshelving to result in a merge with 'a' conflicting
283 283
284 284 $ hg shelve -q
285 285 $ echo c>>a/a
286 286 $ hg commit -m second
287 287 $ hg tip --template '{files}\n'
288 288 a/a
289 289
290 290 add an unrelated change that should be preserved
291 291
292 292 $ mkdir foo
293 293 $ echo foo > foo/foo
294 294 $ hg add foo/foo
295 295
296 296 force a conflicted merge to occur
297 297
298 298 $ hg unshelve
299 299 unshelving change 'default'
300 300 temporarily committing pending changes (restore with 'hg unshelve --abort')
301 301 rebasing shelved changes
302 302 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
303 303 merging a/a
304 304 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
305 305 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
306 306 [1]
307 307
308 308 ensure that we have a merge with unresolved conflicts
309 309
310 310 $ hg heads -q --template '{rev}\n'
311 311 5
312 312 4
313 313 $ hg parents -q --template '{rev}\n'
314 314 4
315 315 5
316 316 $ hg status
317 317 M a/a
318 318 M b.rename/b
319 319 M c.copy
320 320 R b/b
321 321 ? a/a.orig
322 322 $ hg diff
323 323 diff --git a/a/a b/a/a
324 324 --- a/a/a
325 325 +++ b/a/a
326 326 @@ -1,2 +1,6 @@
327 327 a
328 328 +<<<<<<< dest: * - shelve: pending changes temporary commit (glob)
329 329 c
330 330 +=======
331 331 +a
332 332 +>>>>>>> source: 4702e8911fe0 - shelve: changes to '[mq]: second.patch'
333 333 diff --git a/b/b b/b.rename/b
334 334 rename from b/b
335 335 rename to b.rename/b
336 336 diff --git a/c b/c.copy
337 337 copy from c
338 338 copy to c.copy
339 339 $ hg resolve -l
340 340 U a/a
341 341
342 342 $ hg shelve
343 343 abort: unshelve already in progress
344 344 (use 'hg unshelve --continue' or 'hg unshelve --abort')
345 345 [255]
346 346
347 347 abort the unshelve and be happy
348 348
349 349 $ hg status
350 350 M a/a
351 351 M b.rename/b
352 352 M c.copy
353 353 R b/b
354 354 ? a/a.orig
355 355 $ hg unshelve -a
356 356 rebase aborted
357 357 unshelve of 'default' aborted
358 358 $ hg heads -q
359 359 3:2e69b451d1ea
360 360 $ hg parents
361 361 changeset: 3:2e69b451d1ea
362 362 tag: tip
363 363 user: test
364 364 date: Thu Jan 01 00:00:00 1970 +0000
365 365 summary: second
366 366
367 367 $ hg resolve -l
368 368 $ hg status
369 369 A foo/foo
370 370 ? a/a.orig
371 371
372 372 try to continue with no unshelve underway
373 373
374 374 $ hg unshelve -c
375 375 abort: no unshelve operation underway
376 376 [255]
377 377 $ hg status
378 378 A foo/foo
379 379 ? a/a.orig
380 380
381 381 redo the unshelve to get a conflict
382 382
383 383 $ hg unshelve -q
384 384 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
385 385 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
386 386 [1]
387 387
388 388 attempt to continue
389 389
390 390 $ hg unshelve -c
391 391 abort: unresolved conflicts, can't continue
392 392 (see 'hg resolve', then 'hg unshelve --continue')
393 393 [255]
394 394
395 395 $ hg revert -r . a/a
396 396 $ hg resolve -m a/a
397 397 (no more unresolved files)
398 398
399 399 $ hg commit -m 'commit while unshelve in progress'
400 400 abort: unshelve already in progress
401 401 (use 'hg unshelve --continue' or 'hg unshelve --abort')
402 402 [255]
403 403
404 404 $ hg unshelve -c
405 405 rebasing 5:4702e8911fe0 "changes to '[mq]: second.patch'" (tip)
406 406 unshelve of 'default' complete
407 407
408 408 ensure the repo is as we hope
409 409
410 410 $ hg parents
411 411 changeset: 3:2e69b451d1ea
412 412 tag: tip
413 413 user: test
414 414 date: Thu Jan 01 00:00:00 1970 +0000
415 415 summary: second
416 416
417 417 $ hg heads -q
418 418 3:2e69b451d1ea
419 419
420 420 $ hg status -C
421 421 A b.rename/b
422 422 b/b
423 423 A c.copy
424 424 c
425 425 A foo/foo
426 426 R b/b
427 427 ? a/a.orig
428 428
429 429 there should be no shelves left
430 430
431 431 $ hg shelve -l
432 432
433 433 #if execbit
434 434
435 435 ensure that metadata-only changes are shelved
436 436
437 437 $ chmod +x a/a
438 438 $ hg shelve -q -n execbit a/a
439 439 $ hg status a/a
440 440 $ hg unshelve -q execbit
441 441 $ hg status a/a
442 442 M a/a
443 443 $ hg revert a/a
444 444
445 445 #endif
446 446
447 447 #if symlink
448 448
449 449 $ rm a/a
450 450 $ ln -s foo a/a
451 451 $ hg shelve -q -n symlink a/a
452 452 $ hg status a/a
453 453 $ hg unshelve -q symlink
454 454 $ hg status a/a
455 455 M a/a
456 456 $ hg revert a/a
457 457
458 458 #endif
459 459
460 460 set up another conflict between a commit and a shelved change
461 461
462 462 $ hg revert -q -C -a
463 463 $ rm a/a.orig b.rename/b c.copy
464 464 $ echo a >> a/a
465 465 $ hg shelve -q
466 466 $ echo x >> a/a
467 467 $ hg ci -m 'create conflict'
468 468 $ hg add foo/foo
469 469
470 470 if we resolve a conflict while unshelving, the unshelve should succeed
471 471
472 472 $ HGMERGE=true hg unshelve
473 473 unshelving change 'default'
474 474 temporarily committing pending changes (restore with 'hg unshelve --abort')
475 475 rebasing shelved changes
476 476 rebasing 6:c5e6910e7601 "changes to 'second'" (tip)
477 477 merging a/a
478 478 note: rebase of 6:c5e6910e7601 created no changes to commit
479 479 $ hg parents -q
480 480 4:33f7f61e6c5e
481 481 $ hg shelve -l
482 482 $ hg status
483 483 A foo/foo
484 484 $ cat a/a
485 485 a
486 486 c
487 487 x
488 488
489 489 test keep and cleanup
490 490
491 491 $ hg shelve
492 492 shelved as default
493 493 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
494 494 $ hg shelve --list
495 495 default (*) changes to 'create conflict' (glob)
496 496 $ hg unshelve --keep
497 497 unshelving change 'default'
498 498 $ hg shelve --list
499 499 default (*) changes to 'create conflict' (glob)
500 500 $ hg shelve --cleanup
501 501 $ hg shelve --list
502 502
503 503 $ hg shelve --cleanup --delete
504 504 abort: options '--cleanup' and '--delete' may not be used together
505 505 [255]
506 506 $ hg shelve --cleanup --patch
507 507 abort: options '--cleanup' and '--patch' may not be used together
508 508 [255]
509 509 $ hg shelve --cleanup --message MESSAGE
510 510 abort: options '--cleanup' and '--message' may not be used together
511 511 [255]
512 512
513 513 test bookmarks
514 514
515 515 $ hg bookmark test
516 516 $ hg bookmark
517 517 * test 4:33f7f61e6c5e
518 518 $ hg shelve
519 519 shelved as test
520 520 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
521 521 $ hg bookmark
522 522 * test 4:33f7f61e6c5e
523 523 $ hg unshelve
524 524 unshelving change 'test'
525 525 $ hg bookmark
526 526 * test 4:33f7f61e6c5e
527 527
528 528 shelve should still work even if mq is disabled
529 529
530 530 $ hg --config extensions.mq=! shelve
531 531 shelved as test
532 532 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
533 533 $ hg --config extensions.mq=! shelve --list
534 534 test (*) changes to 'create conflict' (glob)
535 535 $ hg bookmark
536 536 * test 4:33f7f61e6c5e
537 537 $ hg --config extensions.mq=! unshelve
538 538 unshelving change 'test'
539 539 $ hg bookmark
540 540 * test 4:33f7f61e6c5e
541 541
542 542 shelve should leave dirstate clean (issue4055)
543 543
544 544 $ cd ..
545 545 $ hg init shelverebase
546 546 $ cd shelverebase
547 547 $ printf 'x\ny\n' > x
548 548 $ echo z > z
549 549 $ hg commit -Aqm xy
550 550 $ echo z >> x
551 551 $ hg commit -Aqm z
552 552 $ hg up 0
553 553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 554 $ printf 'a\nx\ny\nz\n' > x
555 555 $ hg commit -Aqm xyz
556 556 $ echo c >> z
557 557 $ hg shelve
558 558 shelved as default
559 559 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
560 560 $ hg rebase -d 1 --config extensions.rebase=
561 561 rebasing 2:323bfa07f744 "xyz" (tip)
562 562 merging x
563 563 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-backup.hg (glob)
564 564 $ hg unshelve
565 565 unshelving change 'default'
566 566 rebasing shelved changes
567 567 rebasing 4:b8fefe789ed0 "changes to 'xyz'" (tip)
568 568 $ hg status
569 569 M z
570 570
571 571 $ cd ..
572 572
573 573 shelve should only unshelve pending changes (issue4068)
574 574
575 575 $ hg init onlypendingchanges
576 576 $ cd onlypendingchanges
577 577 $ touch a
578 578 $ hg ci -Aqm a
579 579 $ touch b
580 580 $ hg ci -Aqm b
581 581 $ hg up -q 0
582 582 $ touch c
583 583 $ hg ci -Aqm c
584 584
585 585 $ touch d
586 586 $ hg add d
587 587 $ hg shelve
588 588 shelved as default
589 589 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
590 590 $ hg up -q 1
591 591 $ hg unshelve
592 592 unshelving change 'default'
593 593 rebasing shelved changes
594 594 rebasing 3:0cae6656c016 "changes to 'c'" (tip)
595 595 $ hg status
596 596 A d
597 597
598 598 unshelve should work on an ancestor of the original commit
599 599
600 600 $ hg shelve
601 601 shelved as default
602 602 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
603 603 $ hg up 0
604 604 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
605 605 $ hg unshelve
606 606 unshelving change 'default'
607 607 rebasing shelved changes
608 608 rebasing 3:be58f65f55fb "changes to 'b'" (tip)
609 609 $ hg status
610 610 A d
611 611
612 612 test bug 4073 we need to enable obsolete markers for it
613 613
614 614 $ cat >> $HGRCPATH << EOF
615 615 > [experimental]
616 616 > evolution=createmarkers
617 617 > EOF
618 618 $ hg shelve
619 619 shelved as default
620 620 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
621 621 $ hg debugobsolete `hg --debug id -i -r 1`
622 622 $ hg unshelve
623 623 unshelving change 'default'
624 624
625 625 unshelve should leave unknown files alone (issue4113)
626 626
627 627 $ echo e > e
628 628 $ hg shelve
629 629 shelved as default
630 630 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
631 631 $ hg status
632 632 ? e
633 633 $ hg unshelve
634 634 unshelving change 'default'
635 635 $ hg status
636 636 A d
637 637 ? e
638 638 $ cat e
639 639 e
640 640
641 641 unshelve should keep a copy of unknown files
642 642
643 643 $ hg add e
644 644 $ hg shelve
645 645 shelved as default
646 646 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
647 647 $ echo z > e
648 648 $ hg unshelve
649 649 unshelving change 'default'
650 650 $ cat e
651 651 e
652 652 $ cat e.orig
653 653 z
654 654
655 655
656 656 unshelve and conflicts with tracked and untracked files
657 657
658 658 preparing:
659 659
660 660 $ rm *.orig
661 661 $ hg ci -qm 'commit stuff'
662 662 $ hg phase -p null:
663 663
664 664 no other changes - no merge:
665 665
666 666 $ echo f > f
667 667 $ hg add f
668 668 $ hg shelve
669 669 shelved as default
670 670 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
671 671 $ echo g > f
672 672 $ hg unshelve
673 673 unshelving change 'default'
674 674 $ hg st
675 675 A f
676 676 ? f.orig
677 677 $ cat f
678 678 f
679 679 $ cat f.orig
680 680 g
681 681
682 682 other uncommitted changes - merge:
683 683
684 684 $ hg st
685 685 A f
686 686 ? f.orig
687 687 $ hg shelve
688 688 shelved as default
689 689 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
690 690 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
691 691 o 4 changes to 'commit stuff' shelve@localhost
692 692 |
693 693 $ hg log -G --template '{rev} {desc|firstline} {author}'
694 694 @ 3 commit stuff test
695 695 |
696 696 | o 2 c test
697 697 |/
698 698 o 0 a test
699 699
700 700 $ mv f.orig f
701 701 $ echo 1 > a
702 702 $ hg unshelve --date '1073741824 0'
703 703 unshelving change 'default'
704 704 temporarily committing pending changes (restore with 'hg unshelve --abort')
705 705 rebasing shelved changes
706 706 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
707 707 merging f
708 708 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
709 709 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
710 710 [1]
711 711 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
712 712 @ 5 changes to 'commit stuff' shelve@localhost 1970-01-01 00:00 +0000
713 713 |
714 714 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
715 715 |/
716 716 o 3 commit stuff test 1970-01-01 00:00 +0000
717 717 |
718 718 | o 2 c test 1970-01-01 00:00 +0000
719 719 |/
720 720 o 0 a test 1970-01-01 00:00 +0000
721 721
722 722 $ hg st
723 723 M f
724 724 ? f.orig
725 725 $ cat f
726 726 <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit
727 727 g
728 728 =======
729 729 f
730 730 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
731 731 $ cat f.orig
732 732 g
733 733 $ hg unshelve --abort
734 734 rebase aborted
735 735 unshelve of 'default' aborted
736 736 $ hg st
737 737 M a
738 738 ? f.orig
739 739 $ cat f.orig
740 740 g
741 741 $ hg unshelve
742 742 unshelving change 'default'
743 743 temporarily committing pending changes (restore with 'hg unshelve --abort')
744 744 rebasing shelved changes
745 745 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
746 746 $ hg st
747 747 M a
748 748 A f
749 749 ? f.orig
750 750
751 751 other committed changes - merge:
752 752
753 753 $ hg shelve f
754 754 shelved as default
755 755 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
756 756 $ hg ci a -m 'intermediate other change'
757 757 $ mv f.orig f
758 758 $ hg unshelve
759 759 unshelving change 'default'
760 760 rebasing shelved changes
761 761 rebasing 5:23b29cada8ba "changes to 'commit stuff'" (tip)
762 762 merging f
763 763 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
764 764 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
765 765 [1]
766 766 $ hg st
767 767 M f
768 768 ? f.orig
769 769 $ cat f
770 770 <<<<<<< dest: * - test: intermediate other change (glob)
771 771 g
772 772 =======
773 773 f
774 774 >>>>>>> source: 23b29cada8ba - shelve: changes to 'commit stuff'
775 775 $ cat f.orig
776 776 g
777 777 $ hg unshelve --abort
778 778 rebase aborted
779 779 unshelve of 'default' aborted
780 780 $ hg st
781 781 ? f.orig
782 782 $ cat f.orig
783 783 g
784 784 $ hg shelve --delete default
785 785
786 786 Recreate some conflict again
787 787
788 788 $ cd ../repo
789 789 $ hg up -C -r 3
790 790 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
791 791 (leaving bookmark test)
792 792 $ echo y >> a/a
793 793 $ hg shelve
794 794 shelved as default
795 795 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 796 $ hg up test
797 797 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
798 798 (activating bookmark test)
799 799 $ hg bookmark
800 800 * test 4:33f7f61e6c5e
801 801 $ hg unshelve
802 802 unshelving change 'default'
803 803 rebasing shelved changes
804 804 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
805 805 merging a/a
806 806 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
807 807 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
808 808 [1]
809 809 $ hg bookmark
810 810 test 4:33f7f61e6c5e
811 811
812 812 Test that resolving all conflicts in one direction (so that the rebase
813 813 is a no-op), works (issue4398)
814 814
815 815 $ hg revert -a -r .
816 816 reverting a/a (glob)
817 817 $ hg resolve -m a/a
818 818 (no more unresolved files)
819 819 $ hg unshelve -c
820 820 rebasing 5:4b555fdb4e96 "changes to 'second'" (tip)
821 821 note: rebase of 5:4b555fdb4e96 created no changes to commit
822 822 unshelve of 'default' complete
823 823 $ hg bookmark
824 824 * test 4:33f7f61e6c5e
825 825 $ hg diff
826 826 $ hg status
827 827 ? a/a.orig
828 828 ? foo/foo
829 829 $ hg summary
830 830 parent: 4:33f7f61e6c5e tip
831 831 create conflict
832 832 branch: default
833 833 bookmarks: *test
834 834 commit: 2 unknown (clean)
835 835 update: (current)
836 836 phases: 5 draft
837 837
838 838 $ hg shelve --delete --stat
839 839 abort: options '--delete' and '--stat' may not be used together
840 840 [255]
841 841 $ hg shelve --delete --name NAME
842 842 abort: options '--delete' and '--name' may not be used together
843 843 [255]
844 844
845 845 Test interactive shelve
846 846 $ cat <<EOF >> $HGRCPATH
847 847 > [ui]
848 848 > interactive = true
849 849 > EOF
850 850 $ echo 'a' >> a/b
851 851 $ cat a/a >> a/b
852 852 $ echo 'x' >> a/b
853 853 $ mv a/b a/a
854 854 $ echo 'a' >> foo/foo
855 855 $ hg st
856 856 M a/a
857 857 ? a/a.orig
858 858 ? foo/foo
859 859 $ cat a/a
860 860 a
861 861 a
862 862 c
863 863 x
864 864 x
865 865 $ cat foo/foo
866 866 foo
867 867 a
868 868 $ hg shelve --interactive --config ui.interactive=false
869 869 abort: running non-interactively
870 870 [255]
871 871 $ hg shelve --interactive << EOF
872 872 > y
873 873 > y
874 874 > n
875 875 > EOF
876 876 diff --git a/a/a b/a/a
877 877 2 hunks, 2 lines changed
878 878 examine changes to 'a/a'? [Ynesfdaq?] y
879 879
880 880 @@ -1,3 +1,4 @@
881 881 +a
882 882 a
883 883 c
884 884 x
885 885 record change 1/2 to 'a/a'? [Ynesfdaq?] y
886 886
887 887 @@ -1,3 +2,4 @@
888 888 a
889 889 c
890 890 x
891 891 +x
892 892 record change 2/2 to 'a/a'? [Ynesfdaq?] n
893 893
894 894 shelved as test
895 895 merging a/a
896 896 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
897 897 $ cat a/a
898 898 a
899 899 c
900 900 x
901 901 x
902 902 $ cat foo/foo
903 903 foo
904 904 a
905 905 $ hg st
906 906 M a/a
907 907 ? foo/foo
908 908 $ hg bookmark
909 909 * test 4:33f7f61e6c5e
910 910 $ hg unshelve
911 911 unshelving change 'test'
912 912 temporarily committing pending changes (restore with 'hg unshelve --abort')
913 913 rebasing shelved changes
914 914 rebasing 6:65b5d1c34c34 "changes to 'create conflict'" (tip)
915 915 merging a/a
916 916 $ hg bookmark
917 917 * test 4:33f7f61e6c5e
918 918 $ cat a/a
919 919 a
920 920 a
921 921 c
922 922 x
923 923 x
924 924
925 925 shelve --patch and shelve --stat should work with a single valid shelfname
926 926
927 927 $ hg up --clean .
928 928 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
929 929 (leaving bookmark test)
930 930 $ hg shelve --list
931 931 $ echo 'patch a' > shelf-patch-a
932 932 $ hg add shelf-patch-a
933 933 $ hg shelve
934 934 shelved as default
935 935 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
936 936 $ echo 'patch b' > shelf-patch-b
937 937 $ hg add shelf-patch-b
938 938 $ hg shelve
939 939 shelved as default-01
940 940 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
941 941 $ hg shelve --patch default default-01
942 942 abort: --patch expects a single shelf
943 943 [255]
944 944 $ hg shelve --stat default default-01
945 945 abort: --stat expects a single shelf
946 946 [255]
947 947 $ hg shelve --patch default
948 948 default (* ago) changes to 'create conflict' (glob)
949 949
950 950 diff --git a/shelf-patch-a b/shelf-patch-a
951 951 new file mode 100644
952 952 --- /dev/null
953 953 +++ b/shelf-patch-a
954 954 @@ -0,0 +1,1 @@
955 955 +patch a
956 956 $ hg shelve --stat default
957 957 default (* ago) changes to 'create conflict' (glob)
958 958 shelf-patch-a | 1 +
959 959 1 files changed, 1 insertions(+), 0 deletions(-)
960 960 $ hg shelve --patch nonexistentshelf
961 961 abort: cannot find shelf nonexistentshelf
962 962 [255]
963 963 $ hg shelve --stat nonexistentshelf
964 964 abort: cannot find shelf nonexistentshelf
965 965 [255]
966 966
967 967 $ cd ..
968 968
969 969 Shelve from general delta repo uses bundle2 on disk
970 970 --------------------------------------------------
971 971
972 972 no general delta
973 973
974 974 $ hg clone --pull repo bundle1 --config format.generaldelta=0
975 975 requesting all changes
976 976 adding changesets
977 977 adding manifests
978 978 adding file changes
979 979 added 5 changesets with 8 changes to 6 files
980 980 updating to branch default
981 981 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
982 982 $ cd bundle1
983 983 $ echo babar > jungle
984 984 $ hg add jungle
985 985 $ hg shelve
986 986 shelved as default
987 987 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
988 988 $ hg debugbundle .hg/shelved/*.hg
989 989 7e30d8ac6f23cfc84330fd7e698730374615d21a
990 990 $ cd ..
991 991
992 992 with general delta
993 993
994 994 $ hg clone --pull repo bundle2 --config format.generaldelta=1
995 995 requesting all changes
996 996 adding changesets
997 997 adding manifests
998 998 adding file changes
999 999 added 5 changesets with 8 changes to 6 files
1000 1000 updating to branch default
1001 1001 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1002 1002 $ cd bundle2
1003 1003 $ echo babar > jungle
1004 1004 $ hg add jungle
1005 1005 $ hg shelve
1006 1006 shelved as default
1007 1007 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1008 1008 $ hg debugbundle .hg/shelved/*.hg
1009 1009 Stream params: {'Compression': 'BZ'}
1010 1010 changegroup -- "{'version': '02'}"
1011 1011 7e30d8ac6f23cfc84330fd7e698730374615d21a
1012 1012 $ cd ..
1013 1013
1014 1014 Test visibility of in-memory changes inside transaction to external hook
1015 1015 ------------------------------------------------------------------------
1016 1016
1017 1017 $ cd repo
1018 1018
1019 1019 $ echo xxxx >> x
1020 1020 $ hg commit -m "#5: changes to invoke rebase"
1021 1021
1022 1022 $ cat > $TESTTMP/checkvisibility.sh <<EOF
1023 1023 > echo "==== \$1:"
1024 1024 > hg parents --template "VISIBLE {rev}:{node|short}\n"
1025 1025 > # test that pending changes are hidden
1026 1026 > unset HG_PENDING
1027 1027 > hg parents --template "ACTUAL {rev}:{node|short}\n"
1028 1028 > echo "===="
1029 1029 > EOF
1030 1030
1031 1031 $ cat >> .hg/hgrc <<EOF
1032 1032 > [defaults]
1033 1033 > # to fix hash id of temporary revisions
1034 1034 > unshelve = --date '0 0'
1035 1035 > EOF
1036 1036
1037 1037 "hg unshelve" at REV5 implies steps below:
1038 1038
1039 1039 (1) commit changes in the working directory (REV6)
1040 1040 (2) unbundle shelved revision (REV7)
1041 1041 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
1042 1042 (4) rebase: commit merged revision (REV8)
1043 1043 (5) rebase: update to REV6 (REV8 => REV6)
1044 1044 (6) update to REV5 (REV6 => REV5)
1045 1045 (7) abort transaction
1046 1046
1047 1047 == test visibility to external preupdate hook
1048 1048
1049 1049 $ cat >> .hg/hgrc <<EOF
1050 1050 > [hooks]
1051 1051 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
1052 1052 > EOF
1053 1053
1054 1054 $ echo nnnn >> n
1055 1055
1056 1056 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1057 1057 ==== before-unshelving:
1058 1058 VISIBLE 5:703117a2acfb
1059 1059 ACTUAL 5:703117a2acfb
1060 1060 ====
1061 1061
1062 1062 $ hg unshelve --keep default
1063 1063 temporarily committing pending changes (restore with 'hg unshelve --abort')
1064 1064 rebasing shelved changes
1065 1065 rebasing 7:fcbb97608399 "changes to 'create conflict'" (tip)
1066 1066 ==== preupdate:
1067 1067 VISIBLE 6:66b86db80ee4
1068 1068 ACTUAL 5:703117a2acfb
1069 1069 ====
1070 1070 ==== preupdate:
1071 1071 VISIBLE 8:cb2a4e59c2d5
1072 1072 ACTUAL 5:703117a2acfb
1073 1073 ====
1074 1074 ==== preupdate:
1075 1075 VISIBLE 6:66b86db80ee4
1076 1076 ACTUAL 5:703117a2acfb
1077 1077 ====
1078 1078
1079 1079 $ cat >> .hg/hgrc <<EOF
1080 1080 > [hooks]
1081 1081 > preupdate.visibility =
1082 1082 > EOF
1083 1083
1084 1084 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1085 1085 ==== after-unshelving:
1086 1086 VISIBLE 5:703117a2acfb
1087 1087 ACTUAL 5:703117a2acfb
1088 1088 ====
1089 1089
1090 == test visibility to external update hook
1091
1092 $ hg update -q -C 5
1093
1094 $ cat >> .hg/hgrc <<EOF
1095 > [hooks]
1096 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1097 > EOF
1098
1099 $ echo nnnn >> n
1100
1101 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1102 ==== before-unshelving:
1103 VISIBLE 5:703117a2acfb
1104 ACTUAL 5:703117a2acfb
1105 ====
1106
1107 $ hg unshelve --keep default
1108 temporarily committing pending changes (restore with 'hg unshelve --abort')
1109 rebasing shelved changes
1110 rebasing 7:fcbb97608399 "changes to 'create conflict'" (tip)
1111 ==== update:
1112 VISIBLE 6:66b86db80ee4
1113 VISIBLE 7:fcbb97608399
1114 ACTUAL 5:703117a2acfb
1115 ====
1116 ==== update:
1117 VISIBLE 6:66b86db80ee4
1118 ACTUAL 5:703117a2acfb
1119 ====
1120 ==== update:
1121 VISIBLE 5:703117a2acfb
1122 ACTUAL 5:703117a2acfb
1123 ====
1124
1125 $ cat >> .hg/hgrc <<EOF
1126 > [hooks]
1127 > update.visibility =
1128 > EOF
1129
1130 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1131 ==== after-unshelving:
1132 VISIBLE 5:703117a2acfb
1133 ACTUAL 5:703117a2acfb
1134 ====
1135
1090 1136 $ cd ..
1091 1137
1092 1138 test Abort unshelve always gets user out of the unshelved state
1093 1139 ---------------------------------------------------------------
1094 1140 $ hg init salvage
1095 1141 $ cd salvage
1096 1142 $ echo 'content' > root
1097 1143 $ hg commit -A -m 'root' -q
1098 1144 $ echo '' > root
1099 1145 $ hg shelve -q
1100 1146 $ echo 'contADDent' > root
1101 1147 $ hg unshelve -q
1102 1148 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
1103 1149 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1104 1150 [1]
1105 1151 Wreak havoc on the unshelve process
1106 1152 $ rm .hg/unshelverebasestate
1107 1153 $ hg unshelve --abort
1108 1154 unshelve of 'default' aborted
1109 1155 abort: No such file or directory
1110 1156 [255]
1111 1157 Can the user leave the current state?
1112 1158 $ hg up -C .
1113 1159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1114 1160
1115 1161 Try again but with a corrupted shelve state file
1116 1162 $ hg strip -r 2 -r 1 -q
1117 1163 $ hg up -r 0 -q
1118 1164 $ echo '' > root
1119 1165 $ hg shelve -q
1120 1166 $ echo 'contADDent' > root
1121 1167 $ hg unshelve -q
1122 1168 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
1123 1169 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1124 1170 [1]
1125 1171 $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate
1126 1172 $ mv ../corrupt-shelvedstate .hg/histedit-state
1127 1173 $ hg unshelve --abort |& grep 'rebase aborted'
1128 1174 rebase aborted
1129 1175 $ hg up -C .
1130 1176 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now