##// END OF EJS Templates
merge: mark .hgsubstate as possibly dirty before submerge for consistency...
FUJIWARA Katsunori -
r23971:6becb9db stable
parent child Browse files
Show More
@@ -1,1189 +1,1199 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 import struct
9 9
10 10 from node import nullid, nullrev, hex, bin
11 11 from i18n import _
12 12 from mercurial import obsolete
13 13 import error as errormod, util, filemerge, copies, subrepo, worker
14 14 import errno, os, shutil
15 15
16 16 _pack = struct.pack
17 17 _unpack = struct.unpack
18 18
19 19 def _droponode(data):
20 20 # used for compatibility for v1
21 21 bits = data.split('\0')
22 22 bits = bits[:-2] + bits[-1:]
23 23 return '\0'.join(bits)
24 24
25 25 class mergestate(object):
26 26 '''track 3-way merge state of individual files
27 27
28 28 it is stored on disk when needed. Two file are used, one with an old
29 29 format, one with a new format. Both contains similar data, but the new
30 30 format can store new kind of field.
31 31
32 32 Current new format is a list of arbitrary record of the form:
33 33
34 34 [type][length][content]
35 35
36 36 Type is a single character, length is a 4 bytes integer, content is an
37 37 arbitrary suites of bytes of length `length`.
38 38
39 39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 40 should abort if they are unknown. lower case record can be safely ignored.
41 41
42 42 Currently known record:
43 43
44 44 L: the node of the "local" part of the merge (hexified version)
45 45 O: the node of the "other" part of the merge (hexified version)
46 46 F: a file to be merged entry
47 47 '''
48 48 statepathv1 = 'merge/state'
49 49 statepathv2 = 'merge/state2'
50 50
51 51 def __init__(self, repo):
52 52 self._repo = repo
53 53 self._dirty = False
54 54 self._read()
55 55
56 56 def reset(self, node=None, other=None):
57 57 self._state = {}
58 58 self._local = None
59 59 self._other = None
60 60 if node:
61 61 self._local = node
62 62 self._other = other
63 63 shutil.rmtree(self._repo.join('merge'), True)
64 64 self._dirty = False
65 65
66 66 def _read(self):
67 67 """Analyse each record content to restore a serialized state from disk
68 68
69 69 This function process "record" entry produced by the de-serialization
70 70 of on disk file.
71 71 """
72 72 self._state = {}
73 73 self._local = None
74 74 self._other = None
75 75 records = self._readrecords()
76 76 for rtype, record in records:
77 77 if rtype == 'L':
78 78 self._local = bin(record)
79 79 elif rtype == 'O':
80 80 self._other = bin(record)
81 81 elif rtype == 'F':
82 82 bits = record.split('\0')
83 83 self._state[bits[0]] = bits[1:]
84 84 elif not rtype.islower():
85 85 raise util.Abort(_('unsupported merge state record: %s')
86 86 % rtype)
87 87 self._dirty = False
88 88
89 89 def _readrecords(self):
90 90 """Read merge state from disk and return a list of record (TYPE, data)
91 91
92 92 We read data from both v1 and v2 files and decide which one to use.
93 93
94 94 V1 has been used by version prior to 2.9.1 and contains less data than
95 95 v2. We read both versions and check if no data in v2 contradicts
96 96 v1. If there is not contradiction we can safely assume that both v1
97 97 and v2 were written at the same time and use the extract data in v2. If
98 98 there is contradiction we ignore v2 content as we assume an old version
99 99 of Mercurial has overwritten the mergestate file and left an old v2
100 100 file around.
101 101
102 102 returns list of record [(TYPE, data), ...]"""
103 103 v1records = self._readrecordsv1()
104 104 v2records = self._readrecordsv2()
105 105 oldv2 = set() # old format version of v2 record
106 106 for rec in v2records:
107 107 if rec[0] == 'L':
108 108 oldv2.add(rec)
109 109 elif rec[0] == 'F':
110 110 # drop the onode data (not contained in v1)
111 111 oldv2.add(('F', _droponode(rec[1])))
112 112 for rec in v1records:
113 113 if rec not in oldv2:
114 114 # v1 file is newer than v2 file, use it
115 115 # we have to infer the "other" changeset of the merge
116 116 # we cannot do better than that with v1 of the format
117 117 mctx = self._repo[None].parents()[-1]
118 118 v1records.append(('O', mctx.hex()))
119 119 # add place holder "other" file node information
120 120 # nobody is using it yet so we do no need to fetch the data
121 121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 122 for idx, r in enumerate(v1records):
123 123 if r[0] == 'F':
124 124 bits = r[1].split('\0')
125 125 bits.insert(-2, '')
126 126 v1records[idx] = (r[0], '\0'.join(bits))
127 127 return v1records
128 128 else:
129 129 return v2records
130 130
131 131 def _readrecordsv1(self):
132 132 """read on disk merge state for version 1 file
133 133
134 134 returns list of record [(TYPE, data), ...]
135 135
136 136 Note: the "F" data from this file are one entry short
137 137 (no "other file node" entry)
138 138 """
139 139 records = []
140 140 try:
141 141 f = self._repo.vfs(self.statepathv1)
142 142 for i, l in enumerate(f):
143 143 if i == 0:
144 144 records.append(('L', l[:-1]))
145 145 else:
146 146 records.append(('F', l[:-1]))
147 147 f.close()
148 148 except IOError, err:
149 149 if err.errno != errno.ENOENT:
150 150 raise
151 151 return records
152 152
153 153 def _readrecordsv2(self):
154 154 """read on disk merge state for version 2 file
155 155
156 156 returns list of record [(TYPE, data), ...]
157 157 """
158 158 records = []
159 159 try:
160 160 f = self._repo.vfs(self.statepathv2)
161 161 data = f.read()
162 162 off = 0
163 163 end = len(data)
164 164 while off < end:
165 165 rtype = data[off]
166 166 off += 1
167 167 length = _unpack('>I', data[off:(off + 4)])[0]
168 168 off += 4
169 169 record = data[off:(off + length)]
170 170 off += length
171 171 records.append((rtype, record))
172 172 f.close()
173 173 except IOError, err:
174 174 if err.errno != errno.ENOENT:
175 175 raise
176 176 return records
177 177
178 178 def active(self):
179 179 """Whether mergestate is active.
180 180
181 181 Returns True if there appears to be mergestate. This is a rough proxy
182 182 for "is a merge in progress."
183 183 """
184 184 # Check local variables before looking at filesystem for performance
185 185 # reasons.
186 186 return bool(self._local) or bool(self._state) or \
187 187 self._repo.vfs.exists(self.statepathv1) or \
188 188 self._repo.vfs.exists(self.statepathv2)
189 189
190 190 def commit(self):
191 191 """Write current state on disk (if necessary)"""
192 192 if self._dirty:
193 193 records = []
194 194 records.append(('L', hex(self._local)))
195 195 records.append(('O', hex(self._other)))
196 196 for d, v in self._state.iteritems():
197 197 records.append(('F', '\0'.join([d] + v)))
198 198 self._writerecords(records)
199 199 self._dirty = False
200 200
201 201 def _writerecords(self, records):
202 202 """Write current state on disk (both v1 and v2)"""
203 203 self._writerecordsv1(records)
204 204 self._writerecordsv2(records)
205 205
206 206 def _writerecordsv1(self, records):
207 207 """Write current state on disk in a version 1 file"""
208 208 f = self._repo.vfs(self.statepathv1, 'w')
209 209 irecords = iter(records)
210 210 lrecords = irecords.next()
211 211 assert lrecords[0] == 'L'
212 212 f.write(hex(self._local) + '\n')
213 213 for rtype, data in irecords:
214 214 if rtype == 'F':
215 215 f.write('%s\n' % _droponode(data))
216 216 f.close()
217 217
218 218 def _writerecordsv2(self, records):
219 219 """Write current state on disk in a version 2 file"""
220 220 f = self._repo.vfs(self.statepathv2, 'w')
221 221 for key, data in records:
222 222 assert len(key) == 1
223 223 format = '>sI%is' % len(data)
224 224 f.write(_pack(format, key, len(data), data))
225 225 f.close()
226 226
227 227 def add(self, fcl, fco, fca, fd):
228 228 """add a new (potentially?) conflicting file the merge state
229 229 fcl: file context for local,
230 230 fco: file context for remote,
231 231 fca: file context for ancestors,
232 232 fd: file path of the resulting merge.
233 233
234 234 note: also write the local version to the `.hg/merge` directory.
235 235 """
236 236 hash = util.sha1(fcl.path()).hexdigest()
237 237 self._repo.vfs.write('merge/' + hash, fcl.data())
238 238 self._state[fd] = ['u', hash, fcl.path(),
239 239 fca.path(), hex(fca.filenode()),
240 240 fco.path(), hex(fco.filenode()),
241 241 fcl.flags()]
242 242 self._dirty = True
243 243
244 244 def __contains__(self, dfile):
245 245 return dfile in self._state
246 246
247 247 def __getitem__(self, dfile):
248 248 return self._state[dfile][0]
249 249
250 250 def __iter__(self):
251 251 return iter(sorted(self._state))
252 252
253 253 def files(self):
254 254 return self._state.keys()
255 255
256 256 def mark(self, dfile, state):
257 257 self._state[dfile][0] = state
258 258 self._dirty = True
259 259
260 260 def unresolved(self):
261 261 """Obtain the paths of unresolved files."""
262 262
263 263 for f, entry in self._state.items():
264 264 if entry[0] == 'u':
265 265 yield f
266 266
267 267 def resolve(self, dfile, wctx, labels=None):
268 268 """rerun merge process for file path `dfile`"""
269 269 if self[dfile] == 'r':
270 270 return 0
271 271 stateentry = self._state[dfile]
272 272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 273 octx = self._repo[self._other]
274 274 fcd = wctx[dfile]
275 275 fco = octx[ofile]
276 276 fca = self._repo.filectx(afile, fileid=anode)
277 277 # "premerge" x flags
278 278 flo = fco.flags()
279 279 fla = fca.flags()
280 280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 281 if fca.node() == nullid:
282 282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 283 afile)
284 284 elif flags == fla:
285 285 flags = flo
286 286 # restore local
287 287 f = self._repo.vfs('merge/' + hash)
288 288 self._repo.wwrite(dfile, f.read(), flags)
289 289 f.close()
290 290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
291 291 labels=labels)
292 292 if r is None:
293 293 # no real conflict
294 294 del self._state[dfile]
295 295 self._dirty = True
296 296 elif not r:
297 297 self.mark(dfile, 'r')
298 298 return r
299 299
300 300 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
301 301 if f2 is None:
302 302 f2 = f
303 303 return (os.path.isfile(repo.wjoin(f))
304 304 and repo.wvfs.audit.check(f)
305 305 and repo.dirstate.normalize(f) not in repo.dirstate
306 306 and mctx[f2].cmp(wctx[f]))
307 307
308 308 def _checkunknownfiles(repo, wctx, mctx, force, actions):
309 309 """
310 310 Considers any actions that care about the presence of conflicting unknown
311 311 files. For some actions, the result is to abort; for others, it is to
312 312 choose a different action.
313 313 """
314 314 aborts = []
315 315 if not force:
316 316 for f, (m, args, msg) in actions.iteritems():
317 317 if m in ('c', 'dc'):
318 318 if _checkunknownfile(repo, wctx, mctx, f):
319 319 aborts.append(f)
320 320 elif m == 'dg':
321 321 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
322 322 aborts.append(f)
323 323
324 324 for f in sorted(aborts):
325 325 repo.ui.warn(_("%s: untracked file differs\n") % f)
326 326 if aborts:
327 327 raise util.Abort(_("untracked files in working directory differ "
328 328 "from files in requested revision"))
329 329
330 330 for f, (m, args, msg) in actions.iteritems():
331 331 if m == 'c':
332 332 actions[f] = ('g', args, msg)
333 333 elif m == 'cm':
334 334 fl2, anc = args
335 335 different = _checkunknownfile(repo, wctx, mctx, f)
336 336 if different:
337 337 actions[f] = ('m', (f, f, None, False, anc),
338 338 "remote differs from untracked local")
339 339 else:
340 340 actions[f] = ('g', (fl2,), "remote created")
341 341
342 342 def _forgetremoved(wctx, mctx, branchmerge):
343 343 """
344 344 Forget removed files
345 345
346 346 If we're jumping between revisions (as opposed to merging), and if
347 347 neither the working directory nor the target rev has the file,
348 348 then we need to remove it from the dirstate, to prevent the
349 349 dirstate from listing the file when it is no longer in the
350 350 manifest.
351 351
352 352 If we're merging, and the other revision has removed a file
353 353 that is not present in the working directory, we need to mark it
354 354 as removed.
355 355 """
356 356
357 357 actions = {}
358 358 m = 'f'
359 359 if branchmerge:
360 360 m = 'r'
361 361 for f in wctx.deleted():
362 362 if f not in mctx:
363 363 actions[f] = m, None, "forget deleted"
364 364
365 365 if not branchmerge:
366 366 for f in wctx.removed():
367 367 if f not in mctx:
368 368 actions[f] = 'f', None, "forget removed"
369 369
370 370 return actions
371 371
372 372 def _checkcollision(repo, wmf, actions):
373 373 # build provisional merged manifest up
374 374 pmmf = set(wmf)
375 375
376 376 if actions:
377 377 # k, dr, e and rd are no-op
378 378 for m in 'a', 'f', 'g', 'cd', 'dc':
379 379 for f, args, msg in actions[m]:
380 380 pmmf.add(f)
381 381 for f, args, msg in actions['r']:
382 382 pmmf.discard(f)
383 383 for f, args, msg in actions['dm']:
384 384 f2, flags = args
385 385 pmmf.discard(f2)
386 386 pmmf.add(f)
387 387 for f, args, msg in actions['dg']:
388 388 pmmf.add(f)
389 389 for f, args, msg in actions['m']:
390 390 f1, f2, fa, move, anc = args
391 391 if move:
392 392 pmmf.discard(f1)
393 393 pmmf.add(f)
394 394
395 395 # check case-folding collision in provisional merged manifest
396 396 foldmap = {}
397 397 for f in sorted(pmmf):
398 398 fold = util.normcase(f)
399 399 if fold in foldmap:
400 400 raise util.Abort(_("case-folding collision between %s and %s")
401 401 % (f, foldmap[fold]))
402 402 foldmap[fold] = f
403 403
404 404 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
405 405 acceptremote, followcopies):
406 406 """
407 407 Merge p1 and p2 with ancestor pa and generate merge action list
408 408
409 409 branchmerge and force are as passed in to update
410 410 partial = function to filter file lists
411 411 acceptremote = accept the incoming changes without prompting
412 412 """
413 413
414 414 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
415 415
416 416 # manifests fetched in order are going to be faster, so prime the caches
417 417 [x.manifest() for x in
418 418 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
419 419
420 420 if followcopies:
421 421 ret = copies.mergecopies(repo, wctx, p2, pa)
422 422 copy, movewithdir, diverge, renamedelete = ret
423 423
424 424 repo.ui.note(_("resolving manifests\n"))
425 425 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
426 426 % (bool(branchmerge), bool(force), bool(partial)))
427 427 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
428 428
429 429 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
430 430 copied = set(copy.values())
431 431 copied.update(movewithdir.values())
432 432
433 433 if '.hgsubstate' in m1:
434 434 # check whether sub state is modified
435 435 for s in sorted(wctx.substate):
436 436 if wctx.sub(s).dirty():
437 437 m1['.hgsubstate'] += '+'
438 438 break
439 439
440 440 # Compare manifests
441 441 diff = m1.diff(m2)
442 442
443 443 actions = {}
444 444 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
445 445 if partial and not partial(f):
446 446 continue
447 447 if n1 and n2: # file exists on both local and remote side
448 448 if f not in ma:
449 449 fa = copy.get(f, None)
450 450 if fa is not None:
451 451 actions[f] = ('m', (f, f, fa, False, pa.node()),
452 452 "both renamed from " + fa)
453 453 else:
454 454 actions[f] = ('m', (f, f, None, False, pa.node()),
455 455 "both created")
456 456 else:
457 457 a = ma[f]
458 458 fla = ma.flags(f)
459 459 nol = 'l' not in fl1 + fl2 + fla
460 460 if n2 == a and fl2 == fla:
461 461 actions[f] = ('k' , (), "remote unchanged")
462 462 elif n1 == a and fl1 == fla: # local unchanged - use remote
463 463 if n1 == n2: # optimization: keep local content
464 464 actions[f] = ('e', (fl2,), "update permissions")
465 465 else:
466 466 actions[f] = ('g', (fl2,), "remote is newer")
467 467 elif nol and n2 == a: # remote only changed 'x'
468 468 actions[f] = ('e', (fl2,), "update permissions")
469 469 elif nol and n1 == a: # local only changed 'x'
470 470 actions[f] = ('g', (fl1,), "remote is newer")
471 471 else: # both changed something
472 472 actions[f] = ('m', (f, f, f, False, pa.node()),
473 473 "versions differ")
474 474 elif n1: # file exists only on local side
475 475 if f in copied:
476 476 pass # we'll deal with it on m2 side
477 477 elif f in movewithdir: # directory rename, move local
478 478 f2 = movewithdir[f]
479 479 if f2 in m2:
480 480 actions[f2] = ('m', (f, f2, None, True, pa.node()),
481 481 "remote directory rename, both created")
482 482 else:
483 483 actions[f2] = ('dm', (f, fl1),
484 484 "remote directory rename - move from " + f)
485 485 elif f in copy:
486 486 f2 = copy[f]
487 487 actions[f] = ('m', (f, f2, f2, False, pa.node()),
488 488 "local copied/moved from " + f2)
489 489 elif f in ma: # clean, a different, no remote
490 490 if n1 != ma[f]:
491 491 if acceptremote:
492 492 actions[f] = ('r', None, "remote delete")
493 493 else:
494 494 actions[f] = ('cd', None, "prompt changed/deleted")
495 495 elif n1[20:] == 'a':
496 496 # This extra 'a' is added by working copy manifest to mark
497 497 # the file as locally added. We should forget it instead of
498 498 # deleting it.
499 499 actions[f] = ('f', None, "remote deleted")
500 500 else:
501 501 actions[f] = ('r', None, "other deleted")
502 502 elif n2: # file exists only on remote side
503 503 if f in copied:
504 504 pass # we'll deal with it on m1 side
505 505 elif f in movewithdir:
506 506 f2 = movewithdir[f]
507 507 if f2 in m1:
508 508 actions[f2] = ('m', (f2, f, None, False, pa.node()),
509 509 "local directory rename, both created")
510 510 else:
511 511 actions[f2] = ('dg', (f, fl2),
512 512 "local directory rename - get from " + f)
513 513 elif f in copy:
514 514 f2 = copy[f]
515 515 if f2 in m2:
516 516 actions[f] = ('m', (f2, f, f2, False, pa.node()),
517 517 "remote copied from " + f2)
518 518 else:
519 519 actions[f] = ('m', (f2, f, f2, True, pa.node()),
520 520 "remote moved from " + f2)
521 521 elif f not in ma:
522 522 # local unknown, remote created: the logic is described by the
523 523 # following table:
524 524 #
525 525 # force branchmerge different | action
526 526 # n * * | create
527 527 # y n * | create
528 528 # y y n | create
529 529 # y y y | merge
530 530 #
531 531 # Checking whether the files are different is expensive, so we
532 532 # don't do that when we can avoid it.
533 533 if not force:
534 534 actions[f] = ('c', (fl2,), "remote created")
535 535 elif not branchmerge:
536 536 actions[f] = ('c', (fl2,), "remote created")
537 537 else:
538 538 actions[f] = ('cm', (fl2, pa.node()),
539 539 "remote created, get or merge")
540 540 elif n2 != ma[f]:
541 541 if acceptremote:
542 542 actions[f] = ('c', (fl2,), "remote recreating")
543 543 else:
544 544 actions[f] = ('dc', (fl2,), "prompt deleted/changed")
545 545
546 546 return actions, diverge, renamedelete
547 547
548 548 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
549 549 """Resolves false conflicts where the nodeid changed but the content
550 550 remained the same."""
551 551
552 552 for f, (m, args, msg) in actions.items():
553 553 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
554 554 # local did change but ended up with same content
555 555 actions[f] = 'r', None, "prompt same"
556 556 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
557 557 # remote did change but ended up with same content
558 558 del actions[f] # don't get = keep local deleted
559 559
560 560 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
561 561 acceptremote, followcopies):
562 562 "Calculate the actions needed to merge mctx into wctx using ancestors"
563 563
564 564 if len(ancestors) == 1: # default
565 565 actions, diverge, renamedelete = manifestmerge(
566 566 repo, wctx, mctx, ancestors[0], branchmerge, force, partial,
567 567 acceptremote, followcopies)
568 568 _checkunknownfiles(repo, wctx, mctx, force, actions)
569 569
570 570 else: # only when merge.preferancestor=* - the default
571 571 repo.ui.note(
572 572 _("note: merging %s and %s using bids from ancestors %s\n") %
573 573 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
574 574
575 575 # Call for bids
576 576 fbids = {} # mapping filename to bids (action method to list af actions)
577 577 diverge, renamedelete = None, None
578 578 for ancestor in ancestors:
579 579 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
580 580 actions, diverge1, renamedelete1 = manifestmerge(
581 581 repo, wctx, mctx, ancestor, branchmerge, force, partial,
582 582 acceptremote, followcopies)
583 583 _checkunknownfiles(repo, wctx, mctx, force, actions)
584 584 if diverge is None: # and renamedelete is None.
585 585 # Arbitrarily pick warnings from first iteration
586 586 diverge = diverge1
587 587 renamedelete = renamedelete1
588 588 for f, a in sorted(actions.iteritems()):
589 589 m, args, msg = a
590 590 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
591 591 if f in fbids:
592 592 d = fbids[f]
593 593 if m in d:
594 594 d[m].append(a)
595 595 else:
596 596 d[m] = [a]
597 597 else:
598 598 fbids[f] = {m: [a]}
599 599
600 600 # Pick the best bid for each file
601 601 repo.ui.note(_('\nauction for merging merge bids\n'))
602 602 actions = {}
603 603 for f, bids in sorted(fbids.items()):
604 604 # bids is a mapping from action method to list af actions
605 605 # Consensus?
606 606 if len(bids) == 1: # all bids are the same kind of method
607 607 m, l = bids.items()[0]
608 608 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
609 609 repo.ui.note(" %s: consensus for %s\n" % (f, m))
610 610 actions[f] = l[0]
611 611 continue
612 612 # If keep is an option, just do it.
613 613 if 'k' in bids:
614 614 repo.ui.note(" %s: picking 'keep' action\n" % f)
615 615 actions[f] = bids['k'][0]
616 616 continue
617 617 # If there are gets and they all agree [how could they not?], do it.
618 618 if 'g' in bids:
619 619 ga0 = bids['g'][0]
620 620 if util.all(a == ga0 for a in bids['g'][1:]):
621 621 repo.ui.note(" %s: picking 'get' action\n" % f)
622 622 actions[f] = ga0
623 623 continue
624 624 # TODO: Consider other simple actions such as mode changes
625 625 # Handle inefficient democrazy.
626 626 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
627 627 for m, l in sorted(bids.items()):
628 628 for _f, args, msg in l:
629 629 repo.ui.note(' %s -> %s\n' % (msg, m))
630 630 # Pick random action. TODO: Instead, prompt user when resolving
631 631 m, l = bids.items()[0]
632 632 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
633 633 (f, m))
634 634 actions[f] = l[0]
635 635 continue
636 636 repo.ui.note(_('end of auction\n\n'))
637 637
638 638 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
639 639
640 640 if wctx.rev() is None:
641 641 fractions = _forgetremoved(wctx, mctx, branchmerge)
642 642 actions.update(fractions)
643 643
644 644 return actions, diverge, renamedelete
645 645
646 646 def batchremove(repo, actions):
647 647 """apply removes to the working directory
648 648
649 649 yields tuples for progress updates
650 650 """
651 651 verbose = repo.ui.verbose
652 652 unlink = util.unlinkpath
653 653 wjoin = repo.wjoin
654 654 audit = repo.wvfs.audit
655 655 i = 0
656 656 for f, args, msg in actions:
657 657 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
658 658 if verbose:
659 659 repo.ui.note(_("removing %s\n") % f)
660 660 audit(f)
661 661 try:
662 662 unlink(wjoin(f), ignoremissing=True)
663 663 except OSError, inst:
664 664 repo.ui.warn(_("update failed to remove %s: %s!\n") %
665 665 (f, inst.strerror))
666 666 if i == 100:
667 667 yield i, f
668 668 i = 0
669 669 i += 1
670 670 if i > 0:
671 671 yield i, f
672 672
673 673 def batchget(repo, mctx, actions):
674 674 """apply gets to the working directory
675 675
676 676 mctx is the context to get from
677 677
678 678 yields tuples for progress updates
679 679 """
680 680 verbose = repo.ui.verbose
681 681 fctx = mctx.filectx
682 682 wwrite = repo.wwrite
683 683 i = 0
684 684 for f, args, msg in actions:
685 685 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
686 686 if verbose:
687 687 repo.ui.note(_("getting %s\n") % f)
688 688 wwrite(f, fctx(f).data(), args[0])
689 689 if i == 100:
690 690 yield i, f
691 691 i = 0
692 692 i += 1
693 693 if i > 0:
694 694 yield i, f
695 695
696 696 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
697 697 """apply the merge action list to the working directory
698 698
699 699 wctx is the working copy context
700 700 mctx is the context to be merged into the working copy
701 701
702 702 Return a tuple of counts (updated, merged, removed, unresolved) that
703 703 describes how many files were affected by the update.
704 704 """
705 705
706 706 updated, merged, removed, unresolved = 0, 0, 0, 0
707 707 ms = mergestate(repo)
708 708 ms.reset(wctx.p1().node(), mctx.node())
709 709 moves = []
710 710 for m, l in actions.items():
711 711 l.sort()
712 712
713 713 # prescan for merges
714 714 for f, args, msg in actions['m']:
715 715 f1, f2, fa, move, anc = args
716 716 if f == '.hgsubstate': # merged internally
717 717 continue
718 718 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
719 719 fcl = wctx[f1]
720 720 fco = mctx[f2]
721 721 actx = repo[anc]
722 722 if fa in actx:
723 723 fca = actx[fa]
724 724 else:
725 725 fca = repo.filectx(f1, fileid=nullrev)
726 726 ms.add(fcl, fco, fca, f)
727 727 if f1 != f and move:
728 728 moves.append(f1)
729 729
730 730 audit = repo.wvfs.audit
731 731 _updating = _('updating')
732 732 _files = _('files')
733 733 progress = repo.ui.progress
734 734
735 735 # remove renamed files after safely stored
736 736 for f in moves:
737 737 if os.path.lexists(repo.wjoin(f)):
738 738 repo.ui.debug("removing %s\n" % f)
739 739 audit(f)
740 740 util.unlinkpath(repo.wjoin(f))
741 741
742 742 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
743 743
744 def dirtysubstate():
745 # mark '.hgsubstate' as possibly dirty forcibly, because
746 # modified '.hgsubstate' is misunderstood as clean,
747 # when both st_size/st_mtime of '.hgsubstate' aren't changed,
748 # even if "submerge" fails and '.hgsubstate' is inconsistent
749 repo.dirstate.normallookup('.hgsubstate')
750
744 751 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
752 dirtysubstate()
745 753 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
746 754
747 755 # remove in parallel (must come first)
748 756 z = 0
749 757 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
750 758 for i, item in prog:
751 759 z += i
752 760 progress(_updating, z, item=item, total=numupdates, unit=_files)
753 761 removed = len(actions['r'])
754 762
755 763 # get in parallel
756 764 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
757 765 for i, item in prog:
758 766 z += i
759 767 progress(_updating, z, item=item, total=numupdates, unit=_files)
760 768 updated = len(actions['g'])
761 769
762 770 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
771 dirtysubstate()
763 772 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
764 773
765 774 # forget (manifest only, just log it) (must come first)
766 775 for f, args, msg in actions['f']:
767 776 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
768 777 z += 1
769 778 progress(_updating, z, item=f, total=numupdates, unit=_files)
770 779
771 780 # re-add (manifest only, just log it)
772 781 for f, args, msg in actions['a']:
773 782 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
774 783 z += 1
775 784 progress(_updating, z, item=f, total=numupdates, unit=_files)
776 785
777 786 # keep (noop, just log it)
778 787 for f, args, msg in actions['k']:
779 788 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
780 789 # no progress
781 790
782 791 # merge
783 792 for f, args, msg in actions['m']:
784 793 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
785 794 z += 1
786 795 progress(_updating, z, item=f, total=numupdates, unit=_files)
787 796 if f == '.hgsubstate': # subrepo states need updating
797 dirtysubstate()
788 798 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
789 799 overwrite)
790 800 continue
791 801 audit(f)
792 802 r = ms.resolve(f, wctx, labels=labels)
793 803 if r is not None and r > 0:
794 804 unresolved += 1
795 805 else:
796 806 if r is None:
797 807 updated += 1
798 808 else:
799 809 merged += 1
800 810
801 811 # directory rename, move local
802 812 for f, args, msg in actions['dm']:
803 813 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
804 814 z += 1
805 815 progress(_updating, z, item=f, total=numupdates, unit=_files)
806 816 f0, flags = args
807 817 repo.ui.note(_("moving %s to %s\n") % (f0, f))
808 818 audit(f)
809 819 repo.wwrite(f, wctx.filectx(f0).data(), flags)
810 820 util.unlinkpath(repo.wjoin(f0))
811 821 updated += 1
812 822
813 823 # local directory rename, get
814 824 for f, args, msg in actions['dg']:
815 825 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
816 826 z += 1
817 827 progress(_updating, z, item=f, total=numupdates, unit=_files)
818 828 f0, flags = args
819 829 repo.ui.note(_("getting %s to %s\n") % (f0, f))
820 830 repo.wwrite(f, mctx.filectx(f0).data(), flags)
821 831 updated += 1
822 832
823 833 # exec
824 834 for f, args, msg in actions['e']:
825 835 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
826 836 z += 1
827 837 progress(_updating, z, item=f, total=numupdates, unit=_files)
828 838 flags, = args
829 839 audit(f)
830 840 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
831 841 updated += 1
832 842
833 843 ms.commit()
834 844 progress(_updating, None, total=numupdates, unit=_files)
835 845
836 846 return updated, merged, removed, unresolved
837 847
838 848 def recordupdates(repo, actions, branchmerge):
839 849 "record merge actions to the dirstate"
840 850 # remove (must come first)
841 851 for f, args, msg in actions['r']:
842 852 if branchmerge:
843 853 repo.dirstate.remove(f)
844 854 else:
845 855 repo.dirstate.drop(f)
846 856
847 857 # forget (must come first)
848 858 for f, args, msg in actions['f']:
849 859 repo.dirstate.drop(f)
850 860
851 861 # re-add
852 862 for f, args, msg in actions['a']:
853 863 if not branchmerge:
854 864 repo.dirstate.add(f)
855 865
856 866 # exec change
857 867 for f, args, msg in actions['e']:
858 868 repo.dirstate.normallookup(f)
859 869
860 870 # keep
861 871 for f, args, msg in actions['k']:
862 872 pass
863 873
864 874 # get
865 875 for f, args, msg in actions['g']:
866 876 if branchmerge:
867 877 repo.dirstate.otherparent(f)
868 878 else:
869 879 repo.dirstate.normal(f)
870 880
871 881 # merge
872 882 for f, args, msg in actions['m']:
873 883 f1, f2, fa, move, anc = args
874 884 if branchmerge:
875 885 # We've done a branch merge, mark this file as merged
876 886 # so that we properly record the merger later
877 887 repo.dirstate.merge(f)
878 888 if f1 != f2: # copy/rename
879 889 if move:
880 890 repo.dirstate.remove(f1)
881 891 if f1 != f:
882 892 repo.dirstate.copy(f1, f)
883 893 else:
884 894 repo.dirstate.copy(f2, f)
885 895 else:
886 896 # We've update-merged a locally modified file, so
887 897 # we set the dirstate to emulate a normal checkout
888 898 # of that file some time in the past. Thus our
889 899 # merge will appear as a normal local file
890 900 # modification.
891 901 if f2 == f: # file not locally copied/moved
892 902 repo.dirstate.normallookup(f)
893 903 if move:
894 904 repo.dirstate.drop(f1)
895 905
896 906 # directory rename, move local
897 907 for f, args, msg in actions['dm']:
898 908 f0, flag = args
899 909 if branchmerge:
900 910 repo.dirstate.add(f)
901 911 repo.dirstate.remove(f0)
902 912 repo.dirstate.copy(f0, f)
903 913 else:
904 914 repo.dirstate.normal(f)
905 915 repo.dirstate.drop(f0)
906 916
907 917 # directory rename, get
908 918 for f, args, msg in actions['dg']:
909 919 f0, flag = args
910 920 if branchmerge:
911 921 repo.dirstate.add(f)
912 922 repo.dirstate.copy(f0, f)
913 923 else:
914 924 repo.dirstate.normal(f)
915 925
916 926 def update(repo, node, branchmerge, force, partial, ancestor=None,
917 927 mergeancestor=False, labels=None):
918 928 """
919 929 Perform a merge between the working directory and the given node
920 930
921 931 node = the node to update to, or None if unspecified
922 932 branchmerge = whether to merge between branches
923 933 force = whether to force branch merging or file overwriting
924 934 partial = a function to filter file lists (dirstate not updated)
925 935 mergeancestor = whether it is merging with an ancestor. If true,
926 936 we should accept the incoming changes for any prompts that occur.
927 937 If false, merging with an ancestor (fast-forward) is only allowed
928 938 between different named branches. This flag is used by rebase extension
929 939 as a temporary fix and should be avoided in general.
930 940
931 941 The table below shows all the behaviors of the update command
932 942 given the -c and -C or no options, whether the working directory
933 943 is dirty, whether a revision is specified, and the relationship of
934 944 the parent rev to the target rev (linear, on the same named
935 945 branch, or on another named branch).
936 946
937 947 This logic is tested by test-update-branches.t.
938 948
939 949 -c -C dirty rev | linear same cross
940 950 n n n n | ok (1) x
941 951 n n n y | ok ok ok
942 952 n n y n | merge (2) (2)
943 953 n n y y | merge (3) (3)
944 954 n y * * | --- discard ---
945 955 y n y * | --- (4) ---
946 956 y n n * | --- ok ---
947 957 y y * * | --- (5) ---
948 958
949 959 x = can't happen
950 960 * = don't-care
951 961 1 = abort: not a linear update (merge or update --check to force update)
952 962 2 = abort: uncommitted changes (commit and merge, or update --clean to
953 963 discard changes)
954 964 3 = abort: uncommitted changes (commit or update --clean to discard changes)
955 965 4 = abort: uncommitted changes (checked in commands.py)
956 966 5 = incompatible options (checked in commands.py)
957 967
958 968 Return the same tuple as applyupdates().
959 969 """
960 970
961 971 onode = node
962 972 wlock = repo.wlock()
963 973 try:
964 974 wc = repo[None]
965 975 pl = wc.parents()
966 976 p1 = pl[0]
967 977 pas = [None]
968 978 if ancestor is not None:
969 979 pas = [repo[ancestor]]
970 980
971 981 if node is None:
972 982 # Here is where we should consider bookmarks, divergent bookmarks,
973 983 # foreground changesets (successors), and tip of current branch;
974 984 # but currently we are only checking the branch tips.
975 985 try:
976 986 node = repo.branchtip(wc.branch())
977 987 except errormod.RepoLookupError:
978 988 if wc.branch() == 'default': # no default branch!
979 989 node = repo.lookup('tip') # update to tip
980 990 else:
981 991 raise util.Abort(_("branch %s not found") % wc.branch())
982 992
983 993 if p1.obsolete() and not p1.children():
984 994 # allow updating to successors
985 995 successors = obsolete.successorssets(repo, p1.node())
986 996
987 997 # behavior of certain cases is as follows,
988 998 #
989 999 # divergent changesets: update to highest rev, similar to what
990 1000 # is currently done when there are more than one head
991 1001 # (i.e. 'tip')
992 1002 #
993 1003 # replaced changesets: same as divergent except we know there
994 1004 # is no conflict
995 1005 #
996 1006 # pruned changeset: no update is done; though, we could
997 1007 # consider updating to the first non-obsolete parent,
998 1008 # similar to what is current done for 'hg prune'
999 1009
1000 1010 if successors:
1001 1011 # flatten the list here handles both divergent (len > 1)
1002 1012 # and the usual case (len = 1)
1003 1013 successors = [n for sub in successors for n in sub]
1004 1014
1005 1015 # get the max revision for the given successors set,
1006 1016 # i.e. the 'tip' of a set
1007 1017 node = repo.revs('max(%ln)', successors).first()
1008 1018 pas = [p1]
1009 1019
1010 1020 overwrite = force and not branchmerge
1011 1021
1012 1022 p2 = repo[node]
1013 1023 if pas[0] is None:
1014 1024 if repo.ui.config('merge', 'preferancestor', '*') == '*':
1015 1025 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1016 1026 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1017 1027 else:
1018 1028 pas = [p1.ancestor(p2, warn=branchmerge)]
1019 1029
1020 1030 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1021 1031
1022 1032 ### check phase
1023 1033 if not overwrite and len(pl) > 1:
1024 1034 raise util.Abort(_("outstanding uncommitted merge"))
1025 1035 if branchmerge:
1026 1036 if pas == [p2]:
1027 1037 raise util.Abort(_("merging with a working directory ancestor"
1028 1038 " has no effect"))
1029 1039 elif pas == [p1]:
1030 1040 if not mergeancestor and p1.branch() == p2.branch():
1031 1041 raise util.Abort(_("nothing to merge"),
1032 1042 hint=_("use 'hg update' "
1033 1043 "or check 'hg heads'"))
1034 1044 if not force and (wc.files() or wc.deleted()):
1035 1045 raise util.Abort(_("uncommitted changes"),
1036 1046 hint=_("use 'hg status' to list changes"))
1037 1047 for s in sorted(wc.substate):
1038 1048 if wc.sub(s).dirty():
1039 1049 raise util.Abort(_("uncommitted changes in "
1040 1050 "subrepository '%s'") % s)
1041 1051
1042 1052 elif not overwrite:
1043 1053 if p1 == p2: # no-op update
1044 1054 # call the hooks and exit early
1045 1055 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1046 1056 repo.hook('update', parent1=xp2, parent2='', error=0)
1047 1057 return 0, 0, 0, 0
1048 1058
1049 1059 if pas not in ([p1], [p2]): # nonlinear
1050 1060 dirty = wc.dirty(missing=True)
1051 1061 if dirty or onode is None:
1052 1062 # Branching is a bit strange to ensure we do the minimal
1053 1063 # amount of call to obsolete.background.
1054 1064 foreground = obsolete.foreground(repo, [p1.node()])
1055 1065 # note: the <node> variable contains a random identifier
1056 1066 if repo[node].node() in foreground:
1057 1067 pas = [p1] # allow updating to successors
1058 1068 elif dirty:
1059 1069 msg = _("uncommitted changes")
1060 1070 if onode is None:
1061 1071 hint = _("commit and merge, or update --clean to"
1062 1072 " discard changes")
1063 1073 else:
1064 1074 hint = _("commit or update --clean to discard"
1065 1075 " changes")
1066 1076 raise util.Abort(msg, hint=hint)
1067 1077 else: # node is none
1068 1078 msg = _("not a linear update")
1069 1079 hint = _("merge or update --check to force update")
1070 1080 raise util.Abort(msg, hint=hint)
1071 1081 else:
1072 1082 # Allow jumping branches if clean and specific rev given
1073 1083 pas = [p1]
1074 1084
1075 1085 followcopies = False
1076 1086 if overwrite:
1077 1087 pas = [wc]
1078 1088 elif pas == [p2]: # backwards
1079 1089 pas = [wc.p1()]
1080 1090 elif not branchmerge and not wc.dirty(missing=True):
1081 1091 pass
1082 1092 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
1083 1093 followcopies = True
1084 1094
1085 1095 ### calculate phase
1086 1096 actionbyfile, diverge, renamedelete = calculateupdates(
1087 1097 repo, wc, p2, pas, branchmerge, force, partial, mergeancestor,
1088 1098 followcopies)
1089 1099 # Convert to dictionary-of-lists format
1090 1100 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split())
1091 1101 for f, (m, args, msg) in actionbyfile.iteritems():
1092 1102 if m not in actions:
1093 1103 actions[m] = []
1094 1104 actions[m].append((f, args, msg))
1095 1105
1096 1106 if not util.checkcase(repo.path):
1097 1107 # check collision between files only in p2 for clean update
1098 1108 if (not branchmerge and
1099 1109 (force or not wc.dirty(missing=True, branch=False))):
1100 1110 _checkcollision(repo, p2.manifest(), None)
1101 1111 else:
1102 1112 _checkcollision(repo, wc.manifest(), actions)
1103 1113
1104 1114 # Prompt and create actions. TODO: Move this towards resolve phase.
1105 1115 for f, args, msg in sorted(actions['cd']):
1106 1116 if repo.ui.promptchoice(
1107 1117 _("local changed %s which remote deleted\n"
1108 1118 "use (c)hanged version or (d)elete?"
1109 1119 "$$ &Changed $$ &Delete") % f, 0):
1110 1120 actions['r'].append((f, None, "prompt delete"))
1111 1121 else:
1112 1122 actions['a'].append((f, None, "prompt keep"))
1113 1123 del actions['cd'][:]
1114 1124
1115 1125 for f, args, msg in sorted(actions['dc']):
1116 1126 flags, = args
1117 1127 if repo.ui.promptchoice(
1118 1128 _("remote changed %s which local deleted\n"
1119 1129 "use (c)hanged version or leave (d)eleted?"
1120 1130 "$$ &Changed $$ &Deleted") % f, 0) == 0:
1121 1131 actions['g'].append((f, (flags,), "prompt recreating"))
1122 1132 del actions['dc'][:]
1123 1133
1124 1134 ### apply phase
1125 1135 if not branchmerge: # just jump to the new rev
1126 1136 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1127 1137 if not partial:
1128 1138 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1129 1139 # note that we're in the middle of an update
1130 1140 repo.vfs.write('updatestate', p2.hex())
1131 1141
1132 1142 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1133 1143
1134 1144 # divergent renames
1135 1145 for f, fl in sorted(diverge.iteritems()):
1136 1146 repo.ui.warn(_("note: possible conflict - %s was renamed "
1137 1147 "multiple times to:\n") % f)
1138 1148 for nf in fl:
1139 1149 repo.ui.warn(" %s\n" % nf)
1140 1150
1141 1151 # rename and delete
1142 1152 for f, fl in sorted(renamedelete.iteritems()):
1143 1153 repo.ui.warn(_("note: possible conflict - %s was deleted "
1144 1154 "and renamed to:\n") % f)
1145 1155 for nf in fl:
1146 1156 repo.ui.warn(" %s\n" % nf)
1147 1157
1148 1158 if not partial:
1149 1159 repo.dirstate.beginparentchange()
1150 1160 repo.setparents(fp1, fp2)
1151 1161 recordupdates(repo, actions, branchmerge)
1152 1162 # update completed, clear state
1153 1163 util.unlink(repo.join('updatestate'))
1154 1164
1155 1165 if not branchmerge:
1156 1166 repo.dirstate.setbranch(p2.branch())
1157 1167 repo.dirstate.endparentchange()
1158 1168 finally:
1159 1169 wlock.release()
1160 1170
1161 1171 if not partial:
1162 1172 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1163 1173 return stats
1164 1174
1165 1175 def graft(repo, ctx, pctx, labels):
1166 1176 """Do a graft-like merge.
1167 1177
1168 1178 This is a merge where the merge ancestor is chosen such that one
1169 1179 or more changesets are grafted onto the current changeset. In
1170 1180 addition to the merge, this fixes up the dirstate to include only
1171 1181 a single parent and tries to duplicate any renames/copies
1172 1182 appropriately.
1173 1183
1174 1184 ctx - changeset to rebase
1175 1185 pctx - merge base, usually ctx.p1()
1176 1186 labels - merge labels eg ['local', 'graft']
1177 1187
1178 1188 """
1179 1189
1180 1190 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1181 1191 labels=labels)
1182 1192 # drop the second merge parent
1183 1193 repo.dirstate.beginparentchange()
1184 1194 repo.setparents(repo['.'].node(), nullid)
1185 1195 repo.dirstate.write()
1186 1196 # fix up dirstate for copies and renames
1187 1197 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1188 1198 repo.dirstate.endparentchange()
1189 1199 return stats
@@ -1,1471 +1,1474 b''
1 1 Let commit recurse into subrepos by default to match pre-2.0 behavior:
2 2
3 3 $ echo "[ui]" >> $HGRCPATH
4 4 $ echo "commitsubrepos = Yes" >> $HGRCPATH
5 5
6 6 $ hg init t
7 7 $ cd t
8 8
9 9 first revision, no sub
10 10
11 11 $ echo a > a
12 12 $ hg ci -Am0
13 13 adding a
14 14
15 15 add first sub
16 16
17 17 $ echo s = s > .hgsub
18 18 $ hg add .hgsub
19 19 $ hg init s
20 20 $ echo a > s/a
21 21
22 22 Issue2232: committing a subrepo without .hgsub
23 23
24 24 $ hg ci -mbad s
25 25 abort: can't commit subrepos without .hgsub
26 26 [255]
27 27
28 28 $ hg -R s ci -Ams0
29 29 adding a
30 30 $ hg sum
31 31 parent: 0:f7b1eb17ad24 tip
32 32 0
33 33 branch: default
34 34 commit: 1 added, 1 subrepos
35 35 update: (current)
36 36 $ hg ci -m1
37 37
38 38 test handling .hgsubstate "added" explicitly.
39 39
40 40 $ hg parents --template '{node}\n{files}\n'
41 41 7cf8cfea66e410e8e3336508dfeec07b3192de51
42 42 .hgsub .hgsubstate
43 43 $ hg rollback -q
44 44 $ hg add .hgsubstate
45 45 $ hg ci -m1
46 46 $ hg parents --template '{node}\n{files}\n'
47 47 7cf8cfea66e410e8e3336508dfeec07b3192de51
48 48 .hgsub .hgsubstate
49 49
50 50 Revert subrepo and test subrepo fileset keyword:
51 51
52 52 $ echo b > s/a
53 53 $ hg revert "set:subrepo('glob:s*')"
54 54 reverting subrepo s
55 55 reverting s/a (glob)
56 56 $ rm s/a.orig
57 57
58 58 Revert subrepo with no backup. The "reverting s/a" line is gone since
59 59 we're really running 'hg update' in the subrepo:
60 60
61 61 $ echo b > s/a
62 62 $ hg revert --no-backup s
63 63 reverting subrepo s
64 64
65 65 Issue2022: update -C
66 66
67 67 $ echo b > s/a
68 68 $ hg sum
69 69 parent: 1:7cf8cfea66e4 tip
70 70 1
71 71 branch: default
72 72 commit: 1 subrepos
73 73 update: (current)
74 74 $ hg co -C 1
75 75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76 $ hg sum
77 77 parent: 1:7cf8cfea66e4 tip
78 78 1
79 79 branch: default
80 80 commit: (clean)
81 81 update: (current)
82 82
83 83 commands that require a clean repo should respect subrepos
84 84
85 85 $ echo b >> s/a
86 86 $ hg backout tip
87 87 abort: uncommitted changes in subrepo s
88 88 [255]
89 89 $ hg revert -C -R s s/a
90 90
91 91 add sub sub
92 92
93 93 $ echo ss = ss > s/.hgsub
94 94 $ hg init s/ss
95 95 $ echo a > s/ss/a
96 96 $ hg -R s add s/.hgsub
97 97 $ hg -R s/ss add s/ss/a
98 98 $ hg sum
99 99 parent: 1:7cf8cfea66e4 tip
100 100 1
101 101 branch: default
102 102 commit: 1 subrepos
103 103 update: (current)
104 104 $ hg ci -m2
105 105 committing subrepository s
106 106 committing subrepository s/ss (glob)
107 107 $ hg sum
108 108 parent: 2:df30734270ae tip
109 109 2
110 110 branch: default
111 111 commit: (clean)
112 112 update: (current)
113 113
114 114 test handling .hgsubstate "modified" explicitly.
115 115
116 116 $ hg parents --template '{node}\n{files}\n'
117 117 df30734270ae757feb35e643b7018e818e78a9aa
118 118 .hgsubstate
119 119 $ hg rollback -q
120 120 $ hg status -A .hgsubstate
121 121 M .hgsubstate
122 122 $ hg ci -m2
123 123 $ hg parents --template '{node}\n{files}\n'
124 124 df30734270ae757feb35e643b7018e818e78a9aa
125 125 .hgsubstate
126 126
127 127 bump sub rev (and check it is ignored by ui.commitsubrepos)
128 128
129 129 $ echo b > s/a
130 130 $ hg -R s ci -ms1
131 131 $ hg --config ui.commitsubrepos=no ci -m3
132 132
133 133 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
134 134
135 135 $ echo c > s/a
136 136 $ hg --config ui.commitsubrepos=no ci -m4
137 137 abort: uncommitted changes in subrepo s
138 138 (use --subrepos for recursive commit)
139 139 [255]
140 140 $ hg id
141 141 f6affe3fbfaa+ tip
142 142 $ hg -R s ci -mc
143 143 $ hg id
144 144 f6affe3fbfaa+ tip
145 145 $ echo d > s/a
146 146 $ hg ci -m4
147 147 committing subrepository s
148 148 $ hg tip -R s
149 149 changeset: 4:02dcf1d70411
150 150 tag: tip
151 151 user: test
152 152 date: Thu Jan 01 00:00:00 1970 +0000
153 153 summary: 4
154 154
155 155
156 156 check caching
157 157
158 158 $ hg co 0
159 159 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
160 160 $ hg debugsub
161 161
162 162 restore
163 163
164 164 $ hg co
165 165 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 166 $ hg debugsub
167 167 path s
168 168 source s
169 169 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
170 170
171 171 new branch for merge tests
172 172
173 173 $ hg co 1
174 174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 175 $ echo t = t >> .hgsub
176 176 $ hg init t
177 177 $ echo t > t/t
178 178 $ hg -R t add t
179 179 adding t/t (glob)
180 180
181 181 5
182 182
183 183 $ hg ci -m5 # add sub
184 184 committing subrepository t
185 185 created new head
186 186 $ echo t2 > t/t
187 187
188 188 6
189 189
190 190 $ hg st -R s
191 191 $ hg ci -m6 # change sub
192 192 committing subrepository t
193 193 $ hg debugsub
194 194 path s
195 195 source s
196 196 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
197 197 path t
198 198 source t
199 199 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
200 200 $ echo t3 > t/t
201 201
202 202 7
203 203
204 204 $ hg ci -m7 # change sub again for conflict test
205 205 committing subrepository t
206 206 $ hg rm .hgsub
207 207
208 208 8
209 209
210 210 $ hg ci -m8 # remove sub
211 211
212 212 test handling .hgsubstate "removed" explicitly.
213 213
214 214 $ hg parents --template '{node}\n{files}\n'
215 215 96615c1dad2dc8e3796d7332c77ce69156f7b78e
216 216 .hgsub .hgsubstate
217 217 $ hg rollback -q
218 218 $ hg remove .hgsubstate
219 219 $ hg ci -m8
220 220 $ hg parents --template '{node}\n{files}\n'
221 221 96615c1dad2dc8e3796d7332c77ce69156f7b78e
222 222 .hgsub .hgsubstate
223 223
224 224 merge tests
225 225
226 226 $ hg co -C 3
227 227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228 $ hg merge 5 # test adding
229 229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 230 (branch merge, don't forget to commit)
231 231 $ hg debugsub
232 232 path s
233 233 source s
234 234 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
235 235 path t
236 236 source t
237 237 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
238 238 $ hg ci -m9
239 239 created new head
240 240 $ hg merge 6 --debug # test change
241 241 searching for copies back to rev 2
242 242 resolving manifests
243 243 branchmerge: True, force: False, partial: False
244 244 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
245 245 .hgsubstate: versions differ -> m
246 246 updating: .hgsubstate 1/1 files (100.00%)
247 247 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
248 248 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
249 249 getting subrepo t
250 250 resolving manifests
251 251 branchmerge: False, force: False, partial: False
252 252 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
253 253 t: remote is newer -> g
254 254 getting t
255 255 updating: t 1/1 files (100.00%)
256 256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 257 (branch merge, don't forget to commit)
258 258 $ hg debugsub
259 259 path s
260 260 source s
261 261 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
262 262 path t
263 263 source t
264 264 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
265 265 $ echo conflict > t/t
266 266 $ hg ci -m10
267 267 committing subrepository t
268 268 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
269 269 searching for copies back to rev 2
270 270 resolving manifests
271 271 branchmerge: True, force: False, partial: False
272 272 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
273 273 .hgsubstate: versions differ -> m
274 274 updating: .hgsubstate 1/1 files (100.00%)
275 275 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
276 276 subrepo t: both sides changed
277 277 subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198)
278 278 (M)erge, keep (l)ocal or keep (r)emote? m
279 279 merging subrepo t
280 280 searching for copies back to rev 2
281 281 resolving manifests
282 282 branchmerge: True, force: False, partial: False
283 283 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
284 284 preserving t for resolve of t
285 285 t: versions differ -> m
286 286 updating: t 1/1 files (100.00%)
287 287 picked tool 'internal:merge' for t (binary False symlink False)
288 288 merging t
289 289 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
290 290 warning: conflicts during merge.
291 291 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
292 292 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
293 293 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
294 294 subrepo t: merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
295 295 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 296 (branch merge, don't forget to commit)
297 297
298 298 should conflict
299 299
300 300 $ cat t/t
301 301 <<<<<<< local: 20a0db6fbf6c - test: 10
302 302 conflict
303 303 =======
304 304 t3
305 305 >>>>>>> other: 7af322bc1198 - test: 7
306 306
307 307 clone
308 308
309 309 $ cd ..
310 310 $ hg clone t tc
311 311 updating to branch default
312 312 cloning subrepo s from $TESTTMP/t/s
313 313 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
314 314 cloning subrepo t from $TESTTMP/t/t
315 315 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 316 $ cd tc
317 317 $ hg debugsub
318 318 path s
319 319 source s
320 320 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
321 321 path t
322 322 source t
323 323 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
324 324
325 325 push
326 326
327 327 $ echo bah > t/t
328 328 $ hg ci -m11
329 329 committing subrepository t
330 330 $ hg push
331 331 pushing to $TESTTMP/t (glob)
332 332 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
333 333 no changes made to subrepo s since last push to $TESTTMP/t/s
334 334 pushing subrepo t to $TESTTMP/t/t
335 335 searching for changes
336 336 adding changesets
337 337 adding manifests
338 338 adding file changes
339 339 added 1 changesets with 1 changes to 1 files
340 340 searching for changes
341 341 adding changesets
342 342 adding manifests
343 343 adding file changes
344 344 added 1 changesets with 1 changes to 1 files
345 345
346 346 push -f
347 347
348 348 $ echo bah > s/a
349 349 $ hg ci -m12
350 350 committing subrepository s
351 351 $ hg push
352 352 pushing to $TESTTMP/t (glob)
353 353 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
354 354 pushing subrepo s to $TESTTMP/t/s
355 355 searching for changes
356 356 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
357 357 (merge or see "hg help push" for details about pushing new heads)
358 358 [255]
359 359 $ hg push -f
360 360 pushing to $TESTTMP/t (glob)
361 361 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
362 362 searching for changes
363 363 no changes found
364 364 pushing subrepo s to $TESTTMP/t/s
365 365 searching for changes
366 366 adding changesets
367 367 adding manifests
368 368 adding file changes
369 369 added 1 changesets with 1 changes to 1 files (+1 heads)
370 370 pushing subrepo t to $TESTTMP/t/t
371 371 searching for changes
372 372 no changes found
373 373 searching for changes
374 374 adding changesets
375 375 adding manifests
376 376 adding file changes
377 377 added 1 changesets with 1 changes to 1 files
378 378
379 379 check that unmodified subrepos are not pushed
380 380
381 381 $ hg clone . ../tcc
382 382 updating to branch default
383 383 cloning subrepo s from $TESTTMP/tc/s
384 384 cloning subrepo s/ss from $TESTTMP/tc/s/ss (glob)
385 385 cloning subrepo t from $TESTTMP/tc/t
386 386 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 387
388 388 the subrepos on the new clone have nothing to push to its source
389 389
390 390 $ hg push -R ../tcc .
391 391 pushing to .
392 392 no changes made to subrepo s/ss since last push to s/ss (glob)
393 393 no changes made to subrepo s since last push to s
394 394 no changes made to subrepo t since last push to t
395 395 searching for changes
396 396 no changes found
397 397 [1]
398 398
399 399 the subrepos on the source do not have a clean store versus the clone target
400 400 because they were never explicitly pushed to the source
401 401
402 402 $ hg push ../tcc
403 403 pushing to ../tcc
404 404 pushing subrepo s/ss to ../tcc/s/ss (glob)
405 405 searching for changes
406 406 no changes found
407 407 pushing subrepo s to ../tcc/s
408 408 searching for changes
409 409 no changes found
410 410 pushing subrepo t to ../tcc/t
411 411 searching for changes
412 412 no changes found
413 413 searching for changes
414 414 no changes found
415 415 [1]
416 416
417 417 after push their stores become clean
418 418
419 419 $ hg push ../tcc
420 420 pushing to ../tcc
421 421 no changes made to subrepo s/ss since last push to ../tcc/s/ss (glob)
422 422 no changes made to subrepo s since last push to ../tcc/s
423 423 no changes made to subrepo t since last push to ../tcc/t
424 424 searching for changes
425 425 no changes found
426 426 [1]
427 427
428 428 updating a subrepo to a different revision or changing
429 429 its working directory does not make its store dirty
430 430
431 431 $ hg -R s update '.^'
432 432 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
433 433 $ hg push
434 434 pushing to $TESTTMP/t (glob)
435 435 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
436 436 no changes made to subrepo s since last push to $TESTTMP/t/s
437 437 no changes made to subrepo t since last push to $TESTTMP/t/t
438 438 searching for changes
439 439 no changes found
440 440 [1]
441 441 $ echo foo >> s/a
442 442 $ hg push
443 443 pushing to $TESTTMP/t (glob)
444 444 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
445 445 no changes made to subrepo s since last push to $TESTTMP/t/s
446 446 no changes made to subrepo t since last push to $TESTTMP/t/t
447 447 searching for changes
448 448 no changes found
449 449 [1]
450 450 $ hg -R s update -C tip
451 451 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
452 452
453 453 committing into a subrepo makes its store (but not its parent's store) dirty
454 454
455 455 $ echo foo >> s/ss/a
456 456 $ hg -R s/ss commit -m 'test dirty store detection'
457 457 $ hg push
458 458 pushing to $TESTTMP/t (glob)
459 459 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
460 460 searching for changes
461 461 adding changesets
462 462 adding manifests
463 463 adding file changes
464 464 added 1 changesets with 1 changes to 1 files
465 465 no changes made to subrepo s since last push to $TESTTMP/t/s
466 466 no changes made to subrepo t since last push to $TESTTMP/t/t
467 467 searching for changes
468 468 no changes found
469 469 [1]
470 470
471 471 a subrepo store may be clean versus one repo but not versus another
472 472
473 473 $ hg push
474 474 pushing to $TESTTMP/t (glob)
475 475 no changes made to subrepo s/ss since last push to $TESTTMP/t/s/ss (glob)
476 476 no changes made to subrepo s since last push to $TESTTMP/t/s
477 477 no changes made to subrepo t since last push to $TESTTMP/t/t
478 478 searching for changes
479 479 no changes found
480 480 [1]
481 481 $ hg push ../tcc
482 482 pushing to ../tcc
483 483 pushing subrepo s/ss to ../tcc/s/ss (glob)
484 484 searching for changes
485 485 adding changesets
486 486 adding manifests
487 487 adding file changes
488 488 added 1 changesets with 1 changes to 1 files
489 489 no changes made to subrepo s since last push to ../tcc/s
490 490 no changes made to subrepo t since last push to ../tcc/t
491 491 searching for changes
492 492 no changes found
493 493 [1]
494 494
495 495 update
496 496
497 497 $ cd ../t
498 498 $ hg up -C # discard our earlier merge
499 499 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
500 500 $ echo blah > t/t
501 501 $ hg ci -m13
502 502 committing subrepository t
503 503
504 504 backout calls revert internally with minimal opts, which should not raise
505 505 KeyError
506 506
507 507 $ hg backout ".^"
508 508 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
509 509 changeset c373c8102e68 backed out, don't forget to commit.
510 510
511 511 $ hg up -C # discard changes
512 512 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 513
514 514 pull
515 515
516 516 $ cd ../tc
517 517 $ hg pull
518 518 pulling from $TESTTMP/t (glob)
519 519 searching for changes
520 520 adding changesets
521 521 adding manifests
522 522 adding file changes
523 523 added 1 changesets with 1 changes to 1 files
524 524 (run 'hg update' to get a working copy)
525 525
526 526 should pull t
527 527
528 528 $ hg up
529 529 pulling subrepo t from $TESTTMP/t/t
530 530 searching for changes
531 531 adding changesets
532 532 adding manifests
533 533 adding file changes
534 534 added 1 changesets with 1 changes to 1 files
535 535 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
536 536 $ cat t/t
537 537 blah
538 538
539 539 bogus subrepo path aborts
540 540
541 541 $ echo 'bogus=[boguspath' >> .hgsub
542 542 $ hg ci -m 'bogus subrepo path'
543 543 abort: missing ] in subrepo source
544 544 [255]
545 545
546 546 Issue1986: merge aborts when trying to merge a subrepo that
547 547 shouldn't need merging
548 548
549 549 # subrepo layout
550 550 #
551 551 # o 5 br
552 552 # /|
553 553 # o | 4 default
554 554 # | |
555 555 # | o 3 br
556 556 # |/|
557 557 # o | 2 default
558 558 # | |
559 559 # | o 1 br
560 560 # |/
561 561 # o 0 default
562 562
563 563 $ cd ..
564 564 $ rm -rf sub
565 565 $ hg init main
566 566 $ cd main
567 567 $ hg init s
568 568 $ cd s
569 569 $ echo a > a
570 570 $ hg ci -Am1
571 571 adding a
572 572 $ hg branch br
573 573 marked working directory as branch br
574 574 (branches are permanent and global, did you want a bookmark?)
575 575 $ echo a >> a
576 576 $ hg ci -m1
577 577 $ hg up default
578 578 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
579 579 $ echo b > b
580 580 $ hg ci -Am1
581 581 adding b
582 582 $ hg up br
583 583 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
584 584 $ hg merge tip
585 585 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
586 586 (branch merge, don't forget to commit)
587 587 $ hg ci -m1
588 588 $ hg up 2
589 589 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 590 $ echo c > c
591 591 $ hg ci -Am1
592 592 adding c
593 593 $ hg up 3
594 594 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
595 595 $ hg merge 4
596 596 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 597 (branch merge, don't forget to commit)
598 598 $ hg ci -m1
599 599
600 600 # main repo layout:
601 601 #
602 602 # * <-- try to merge default into br again
603 603 # .`|
604 604 # . o 5 br --> substate = 5
605 605 # . |
606 606 # o | 4 default --> substate = 4
607 607 # | |
608 608 # | o 3 br --> substate = 2
609 609 # |/|
610 610 # o | 2 default --> substate = 2
611 611 # | |
612 612 # | o 1 br --> substate = 3
613 613 # |/
614 614 # o 0 default --> substate = 2
615 615
616 616 $ cd ..
617 617 $ echo 's = s' > .hgsub
618 618 $ hg -R s up 2
619 619 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
620 620 $ hg ci -Am1
621 621 adding .hgsub
622 622 $ hg branch br
623 623 marked working directory as branch br
624 624 (branches are permanent and global, did you want a bookmark?)
625 625 $ echo b > b
626 626 $ hg -R s up 3
627 627 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
628 628 $ hg ci -Am1
629 629 adding b
630 630 $ hg up default
631 631 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
632 632 $ echo c > c
633 633 $ hg ci -Am1
634 634 adding c
635 635 $ hg up 1
636 636 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
637 637 $ hg merge 2
638 638 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 639 (branch merge, don't forget to commit)
640 640 $ hg ci -m1
641 641 $ hg up 2
642 642 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
643 643 $ hg -R s up 4
644 644 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 645 $ echo d > d
646 646 $ hg ci -Am1
647 647 adding d
648 648 $ hg up 3
649 649 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
650 650 $ hg -R s up 5
651 651 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
652 652 $ echo e > e
653 653 $ hg ci -Am1
654 654 adding e
655 655
656 656 $ hg up 5
657 657 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
658 658 $ hg merge 4 # try to merge default into br again
659 659 subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88)
660 660 (M)erge, keep (l)ocal or keep (r)emote? m
661 661 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
662 662 (branch merge, don't forget to commit)
663 663 $ cd ..
664 664
665 665 test subrepo delete from .hgsubstate
666 666
667 667 $ hg init testdelete
668 668 $ mkdir testdelete/nested testdelete/nested2
669 669 $ hg init testdelete/nested
670 670 $ hg init testdelete/nested2
671 671 $ echo test > testdelete/nested/foo
672 672 $ echo test > testdelete/nested2/foo
673 673 $ hg -R testdelete/nested add
674 674 adding testdelete/nested/foo (glob)
675 675 $ hg -R testdelete/nested2 add
676 676 adding testdelete/nested2/foo (glob)
677 677 $ hg -R testdelete/nested ci -m test
678 678 $ hg -R testdelete/nested2 ci -m test
679 679 $ echo nested = nested > testdelete/.hgsub
680 680 $ echo nested2 = nested2 >> testdelete/.hgsub
681 681 $ hg -R testdelete add
682 682 adding testdelete/.hgsub (glob)
683 683 $ hg -R testdelete ci -m "nested 1 & 2 added"
684 684 $ echo nested = nested > testdelete/.hgsub
685 685 $ hg -R testdelete ci -m "nested 2 deleted"
686 686 $ cat testdelete/.hgsubstate
687 687 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
688 688 $ hg -R testdelete remove testdelete/.hgsub
689 689 $ hg -R testdelete ci -m ".hgsub deleted"
690 690 $ cat testdelete/.hgsubstate
691 691 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
692 692
693 693 test repository cloning
694 694
695 695 $ mkdir mercurial mercurial2
696 696 $ hg init nested_absolute
697 697 $ echo test > nested_absolute/foo
698 698 $ hg -R nested_absolute add
699 699 adding nested_absolute/foo (glob)
700 700 $ hg -R nested_absolute ci -mtest
701 701 $ cd mercurial
702 702 $ hg init nested_relative
703 703 $ echo test2 > nested_relative/foo2
704 704 $ hg -R nested_relative add
705 705 adding nested_relative/foo2 (glob)
706 706 $ hg -R nested_relative ci -mtest2
707 707 $ hg init main
708 708 $ echo "nested_relative = ../nested_relative" > main/.hgsub
709 709 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
710 710 $ hg -R main add
711 711 adding main/.hgsub (glob)
712 712 $ hg -R main ci -m "add subrepos"
713 713 $ cd ..
714 714 $ hg clone mercurial/main mercurial2/main
715 715 updating to branch default
716 716 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
717 717 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
718 718 > mercurial2/main/nested_relative/.hg/hgrc
719 719 [paths]
720 720 default = $TESTTMP/mercurial/nested_absolute
721 721 [paths]
722 722 default = $TESTTMP/mercurial/nested_relative
723 723 $ rm -rf mercurial mercurial2
724 724
725 725 Issue1977: multirepo push should fail if subrepo push fails
726 726
727 727 $ hg init repo
728 728 $ hg init repo/s
729 729 $ echo a > repo/s/a
730 730 $ hg -R repo/s ci -Am0
731 731 adding a
732 732 $ echo s = s > repo/.hgsub
733 733 $ hg -R repo ci -Am1
734 734 adding .hgsub
735 735 $ hg clone repo repo2
736 736 updating to branch default
737 737 cloning subrepo s from $TESTTMP/repo/s
738 738 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
739 739 $ hg -q -R repo2 pull -u
740 740 $ echo 1 > repo2/s/a
741 741 $ hg -R repo2/s ci -m2
742 742 $ hg -q -R repo2/s push
743 743 $ hg -R repo2/s up -C 0
744 744 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
745 745 $ echo 2 > repo2/s/b
746 746 $ hg -R repo2/s ci -m3 -A
747 747 adding b
748 748 created new head
749 749 $ hg -R repo2 ci -m3
750 750 $ hg -q -R repo2 push
751 751 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
752 752 (merge or see "hg help push" for details about pushing new heads)
753 753 [255]
754 754 $ hg -R repo update
755 755 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
756 756
757 757 test if untracked file is not overwritten
758 758
759 759 $ echo issue3276_ok > repo/s/b
760 760 $ hg -R repo2 push -f -q
761 $ touch -t 200001010000 repo/.hgsubstate
762 $ hg -R repo status --config debug.dirstate.delaywrite=2 repo/.hgsubstate
761 763 $ hg -R repo update
762 764 b: untracked file differs
763 765 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
764 766 [255]
765 767
766 768 $ cat repo/s/b
767 769 issue3276_ok
768 770 $ rm repo/s/b
771 $ touch -t 200001010000 repo/.hgsubstate
769 772 $ hg -R repo revert --all
770 773 reverting repo/.hgsubstate (glob)
771 774 reverting subrepo s
772 775 $ hg -R repo update
773 776 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
774 777 $ cat repo/s/b
775 778 2
776 779 $ rm -rf repo2 repo
777 780
778 781
779 782 Issue1852 subrepos with relative paths always push/pull relative to default
780 783
781 784 Prepare a repo with subrepo
782 785
783 786 $ hg init issue1852a
784 787 $ cd issue1852a
785 788 $ hg init sub/repo
786 789 $ echo test > sub/repo/foo
787 790 $ hg -R sub/repo add sub/repo/foo
788 791 $ echo sub/repo = sub/repo > .hgsub
789 792 $ hg add .hgsub
790 793 $ hg ci -mtest
791 794 committing subrepository sub/repo (glob)
792 795 $ echo test >> sub/repo/foo
793 796 $ hg ci -mtest
794 797 committing subrepository sub/repo (glob)
795 798 $ hg cat sub/repo/foo
796 799 test
797 800 test
798 801 $ mkdir -p tmp/sub/repo
799 802 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
800 803 $ cat tmp/sub/repo/foo_p
801 804 test
802 805 $ mv sub/repo sub_
803 806 $ hg cat sub/repo/baz
804 807 skipping missing subrepository: sub/repo
805 808 [1]
806 809 $ rm -rf sub/repo
807 810 $ mv sub_ sub/repo
808 811 $ cd ..
809 812
810 813 Create repo without default path, pull top repo, and see what happens on update
811 814
812 815 $ hg init issue1852b
813 816 $ hg -R issue1852b pull issue1852a
814 817 pulling from issue1852a
815 818 requesting all changes
816 819 adding changesets
817 820 adding manifests
818 821 adding file changes
819 822 added 2 changesets with 3 changes to 2 files
820 823 (run 'hg update' to get a working copy)
821 824 $ hg -R issue1852b update
822 825 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
823 826 [255]
824 827
825 828 Ensure a full traceback, not just the SubrepoAbort part
826 829
827 830 $ hg -R issue1852b update --traceback 2>&1 | grep 'raise util\.Abort'
828 831 raise util.Abort(_("default path for subrepository not found"))
829 832
830 833 Pull -u now doesn't help
831 834
832 835 $ hg -R issue1852b pull -u issue1852a
833 836 pulling from issue1852a
834 837 searching for changes
835 838 no changes found
836 839
837 840 Try the same, but with pull -u
838 841
839 842 $ hg init issue1852c
840 843 $ hg -R issue1852c pull -r0 -u issue1852a
841 844 pulling from issue1852a
842 845 adding changesets
843 846 adding manifests
844 847 adding file changes
845 848 added 1 changesets with 2 changes to 2 files
846 849 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
847 850 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 851
849 852 Try to push from the other side
850 853
851 854 $ hg -R issue1852a push `pwd`/issue1852c
852 855 pushing to $TESTTMP/issue1852c (glob)
853 856 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
854 857 searching for changes
855 858 no changes found
856 859 searching for changes
857 860 adding changesets
858 861 adding manifests
859 862 adding file changes
860 863 added 1 changesets with 1 changes to 1 files
861 864
862 865 Incoming and outgoing should not use the default path:
863 866
864 867 $ hg clone -q issue1852a issue1852d
865 868 $ hg -R issue1852d outgoing --subrepos issue1852c
866 869 comparing with issue1852c
867 870 searching for changes
868 871 no changes found
869 872 comparing with issue1852c/sub/repo
870 873 searching for changes
871 874 no changes found
872 875 [1]
873 876 $ hg -R issue1852d incoming --subrepos issue1852c
874 877 comparing with issue1852c
875 878 searching for changes
876 879 no changes found
877 880 comparing with issue1852c/sub/repo
878 881 searching for changes
879 882 no changes found
880 883 [1]
881 884
882 885 Check status of files when none of them belong to the first
883 886 subrepository:
884 887
885 888 $ hg init subrepo-status
886 889 $ cd subrepo-status
887 890 $ hg init subrepo-1
888 891 $ hg init subrepo-2
889 892 $ cd subrepo-2
890 893 $ touch file
891 894 $ hg add file
892 895 $ cd ..
893 896 $ echo subrepo-1 = subrepo-1 > .hgsub
894 897 $ echo subrepo-2 = subrepo-2 >> .hgsub
895 898 $ hg add .hgsub
896 899 $ hg ci -m 'Added subrepos'
897 900 committing subrepository subrepo-2
898 901 $ hg st subrepo-2/file
899 902
900 903 Check that share works with subrepo
901 904 $ hg --config extensions.share= share . ../shared
902 905 updating working directory
903 906 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
904 907 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
905 908 $ test -f ../shared/subrepo-1/.hg/sharedpath
906 909 [1]
907 910 $ hg -R ../shared in
908 911 abort: repository default not found!
909 912 [255]
910 913 $ hg -R ../shared/subrepo-2 showconfig paths
911 914 paths.default=$TESTTMP/subrepo-status/subrepo-2
912 915 $ hg -R ../shared/subrepo-1 sum --remote
913 916 parent: -1:000000000000 tip (empty repository)
914 917 branch: default
915 918 commit: (clean)
916 919 update: (current)
917 920 remote: (synced)
918 921
919 922 Check hg update --clean
920 923 $ cd $TESTTMP/t
921 924 $ rm -r t/t.orig
922 925 $ hg status -S --all
923 926 C .hgsub
924 927 C .hgsubstate
925 928 C a
926 929 C s/.hgsub
927 930 C s/.hgsubstate
928 931 C s/a
929 932 C s/ss/a
930 933 C t/t
931 934 $ echo c1 > s/a
932 935 $ cd s
933 936 $ echo c1 > b
934 937 $ echo c1 > c
935 938 $ hg add b
936 939 $ cd ..
937 940 $ hg status -S
938 941 M s/a
939 942 A s/b
940 943 ? s/c
941 944 $ hg update -C
942 945 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
943 946 $ hg status -S
944 947 ? s/b
945 948 ? s/c
946 949
947 950 Sticky subrepositories, no changes
948 951 $ cd $TESTTMP/t
949 952 $ hg id
950 953 925c17564ef8 tip
951 954 $ hg -R s id
952 955 12a213df6fa9 tip
953 956 $ hg -R t id
954 957 52c0adc0515a tip
955 958 $ hg update 11
956 959 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
957 960 $ hg id
958 961 365661e5936a
959 962 $ hg -R s id
960 963 fc627a69481f
961 964 $ hg -R t id
962 965 e95bcfa18a35
963 966
964 967 Sticky subrepositories, file changes
965 968 $ touch s/f1
966 969 $ touch t/f1
967 970 $ hg add -S s/f1
968 971 $ hg add -S t/f1
969 972 $ hg id
970 973 365661e5936a+
971 974 $ hg -R s id
972 975 fc627a69481f+
973 976 $ hg -R t id
974 977 e95bcfa18a35+
975 978 $ hg update tip
976 979 subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9)
977 980 (M)erge, keep (l)ocal or keep (r)emote? m
978 981 subrepository sources for s differ
979 982 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l
980 983 subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a)
981 984 (M)erge, keep (l)ocal or keep (r)emote? m
982 985 subrepository sources for t differ
983 986 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l
984 987 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
985 988 $ hg id
986 989 925c17564ef8+ tip
987 990 $ hg -R s id
988 991 fc627a69481f+
989 992 $ hg -R t id
990 993 e95bcfa18a35+
991 994 $ hg update --clean tip
992 995 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
993 996
994 997 Sticky subrepository, revision updates
995 998 $ hg id
996 999 925c17564ef8 tip
997 1000 $ hg -R s id
998 1001 12a213df6fa9 tip
999 1002 $ hg -R t id
1000 1003 52c0adc0515a tip
1001 1004 $ cd s
1002 1005 $ hg update -r -2
1003 1006 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1004 1007 $ cd ../t
1005 1008 $ hg update -r 2
1006 1009 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1007 1010 $ cd ..
1008 1011 $ hg update 10
1009 1012 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1010 1013 (M)erge, keep (l)ocal or keep (r)emote? m
1011 1014 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c)
1012 1015 (M)erge, keep (l)ocal or keep (r)emote? m
1013 1016 subrepository sources for t differ (in checked out version)
1014 1017 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l
1015 1018 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1016 1019 $ hg id
1017 1020 e45c8b14af55+
1018 1021 $ hg -R s id
1019 1022 02dcf1d70411
1020 1023 $ hg -R t id
1021 1024 7af322bc1198
1022 1025
1023 1026 Sticky subrepository, file changes and revision updates
1024 1027 $ touch s/f1
1025 1028 $ touch t/f1
1026 1029 $ hg add -S s/f1
1027 1030 $ hg add -S t/f1
1028 1031 $ hg id
1029 1032 e45c8b14af55+
1030 1033 $ hg -R s id
1031 1034 02dcf1d70411+
1032 1035 $ hg -R t id
1033 1036 7af322bc1198+
1034 1037 $ hg update tip
1035 1038 subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9)
1036 1039 (M)erge, keep (l)ocal or keep (r)emote? m
1037 1040 subrepository sources for s differ
1038 1041 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l
1039 1042 subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a)
1040 1043 (M)erge, keep (l)ocal or keep (r)emote? m
1041 1044 subrepository sources for t differ
1042 1045 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l
1043 1046 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1044 1047 $ hg id
1045 1048 925c17564ef8+ tip
1046 1049 $ hg -R s id
1047 1050 02dcf1d70411+
1048 1051 $ hg -R t id
1049 1052 7af322bc1198+
1050 1053
1051 1054 Sticky repository, update --clean
1052 1055 $ hg update --clean tip
1053 1056 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1054 1057 $ hg id
1055 1058 925c17564ef8 tip
1056 1059 $ hg -R s id
1057 1060 12a213df6fa9 tip
1058 1061 $ hg -R t id
1059 1062 52c0adc0515a tip
1060 1063
1061 1064 Test subrepo already at intended revision:
1062 1065 $ cd s
1063 1066 $ hg update fc627a69481f
1064 1067 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1065 1068 $ cd ..
1066 1069 $ hg update 11
1067 1070 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f)
1068 1071 (M)erge, keep (l)ocal or keep (r)emote? m
1069 1072 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1070 1073 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1071 1074 $ hg id -n
1072 1075 11+
1073 1076 $ hg -R s id
1074 1077 fc627a69481f
1075 1078 $ hg -R t id
1076 1079 e95bcfa18a35
1077 1080
1078 1081 Test that removing .hgsubstate doesn't break anything:
1079 1082
1080 1083 $ hg rm -f .hgsubstate
1081 1084 $ hg ci -mrm
1082 1085 nothing changed
1083 1086 [1]
1084 1087 $ hg log -vr tip
1085 1088 changeset: 13:925c17564ef8
1086 1089 tag: tip
1087 1090 user: test
1088 1091 date: Thu Jan 01 00:00:00 1970 +0000
1089 1092 files: .hgsubstate
1090 1093 description:
1091 1094 13
1092 1095
1093 1096
1094 1097
1095 1098 Test that removing .hgsub removes .hgsubstate:
1096 1099
1097 1100 $ hg rm .hgsub
1098 1101 $ hg ci -mrm2
1099 1102 created new head
1100 1103 $ hg log -vr tip
1101 1104 changeset: 14:2400bccd50af
1102 1105 tag: tip
1103 1106 parent: 11:365661e5936a
1104 1107 user: test
1105 1108 date: Thu Jan 01 00:00:00 1970 +0000
1106 1109 files: .hgsub .hgsubstate
1107 1110 description:
1108 1111 rm2
1109 1112
1110 1113
1111 1114 Test issue3153: diff -S with deleted subrepos
1112 1115
1113 1116 $ hg diff --nodates -S -c .
1114 1117 diff -r 365661e5936a -r 2400bccd50af .hgsub
1115 1118 --- a/.hgsub
1116 1119 +++ /dev/null
1117 1120 @@ -1,2 +0,0 @@
1118 1121 -s = s
1119 1122 -t = t
1120 1123 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
1121 1124 --- a/.hgsubstate
1122 1125 +++ /dev/null
1123 1126 @@ -1,2 +0,0 @@
1124 1127 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1125 1128 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
1126 1129
1127 1130 Test behavior of add for explicit path in subrepo:
1128 1131 $ cd ..
1129 1132 $ hg init explicit
1130 1133 $ cd explicit
1131 1134 $ echo s = s > .hgsub
1132 1135 $ hg add .hgsub
1133 1136 $ hg init s
1134 1137 $ hg ci -m0
1135 1138 Adding with an explicit path in a subrepo adds the file
1136 1139 $ echo c1 > f1
1137 1140 $ echo c2 > s/f2
1138 1141 $ hg st -S
1139 1142 ? f1
1140 1143 ? s/f2
1141 1144 $ hg add s/f2
1142 1145 $ hg st -S
1143 1146 A s/f2
1144 1147 ? f1
1145 1148 $ hg ci -R s -m0
1146 1149 $ hg ci -Am1
1147 1150 adding f1
1148 1151 Adding with an explicit path in a subrepo with -S has the same behavior
1149 1152 $ echo c3 > f3
1150 1153 $ echo c4 > s/f4
1151 1154 $ hg st -S
1152 1155 ? f3
1153 1156 ? s/f4
1154 1157 $ hg add -S s/f4
1155 1158 $ hg st -S
1156 1159 A s/f4
1157 1160 ? f3
1158 1161 $ hg ci -R s -m1
1159 1162 $ hg ci -Ama2
1160 1163 adding f3
1161 1164 Adding without a path or pattern silently ignores subrepos
1162 1165 $ echo c5 > f5
1163 1166 $ echo c6 > s/f6
1164 1167 $ echo c7 > s/f7
1165 1168 $ hg st -S
1166 1169 ? f5
1167 1170 ? s/f6
1168 1171 ? s/f7
1169 1172 $ hg add
1170 1173 adding f5
1171 1174 $ hg st -S
1172 1175 A f5
1173 1176 ? s/f6
1174 1177 ? s/f7
1175 1178 $ hg ci -R s -Am2
1176 1179 adding f6
1177 1180 adding f7
1178 1181 $ hg ci -m3
1179 1182 Adding without a path or pattern with -S also adds files in subrepos
1180 1183 $ echo c8 > f8
1181 1184 $ echo c9 > s/f9
1182 1185 $ echo c10 > s/f10
1183 1186 $ hg st -S
1184 1187 ? f8
1185 1188 ? s/f10
1186 1189 ? s/f9
1187 1190 $ hg add -S
1188 1191 adding f8
1189 1192 adding s/f10 (glob)
1190 1193 adding s/f9 (glob)
1191 1194 $ hg st -S
1192 1195 A f8
1193 1196 A s/f10
1194 1197 A s/f9
1195 1198 $ hg ci -R s -m3
1196 1199 $ hg ci -m4
1197 1200 Adding with a pattern silently ignores subrepos
1198 1201 $ echo c11 > fm11
1199 1202 $ echo c12 > fn12
1200 1203 $ echo c13 > s/fm13
1201 1204 $ echo c14 > s/fn14
1202 1205 $ hg st -S
1203 1206 ? fm11
1204 1207 ? fn12
1205 1208 ? s/fm13
1206 1209 ? s/fn14
1207 1210 $ hg add 'glob:**fm*'
1208 1211 adding fm11
1209 1212 $ hg st -S
1210 1213 A fm11
1211 1214 ? fn12
1212 1215 ? s/fm13
1213 1216 ? s/fn14
1214 1217 $ hg ci -R s -Am4
1215 1218 adding fm13
1216 1219 adding fn14
1217 1220 $ hg ci -Am5
1218 1221 adding fn12
1219 1222 Adding with a pattern with -S also adds matches in subrepos
1220 1223 $ echo c15 > fm15
1221 1224 $ echo c16 > fn16
1222 1225 $ echo c17 > s/fm17
1223 1226 $ echo c18 > s/fn18
1224 1227 $ hg st -S
1225 1228 ? fm15
1226 1229 ? fn16
1227 1230 ? s/fm17
1228 1231 ? s/fn18
1229 1232 $ hg add -S 'glob:**fm*'
1230 1233 adding fm15
1231 1234 adding s/fm17 (glob)
1232 1235 $ hg st -S
1233 1236 A fm15
1234 1237 A s/fm17
1235 1238 ? fn16
1236 1239 ? s/fn18
1237 1240 $ hg ci -R s -Am5
1238 1241 adding fn18
1239 1242 $ hg ci -Am6
1240 1243 adding fn16
1241 1244
1242 1245 Test behavior of forget for explicit path in subrepo:
1243 1246 Forgetting an explicit path in a subrepo untracks the file
1244 1247 $ echo c19 > s/f19
1245 1248 $ hg add s/f19
1246 1249 $ hg st -S
1247 1250 A s/f19
1248 1251 $ hg forget s/f19
1249 1252 $ hg st -S
1250 1253 ? s/f19
1251 1254 $ rm s/f19
1252 1255 $ cd ..
1253 1256
1254 1257 Courtesy phases synchronisation to publishing server does not block the push
1255 1258 (issue3781)
1256 1259
1257 1260 $ cp -r main issue3781
1258 1261 $ cp -r main issue3781-dest
1259 1262 $ cd issue3781-dest/s
1260 1263 $ hg phase tip # show we have draft changeset
1261 1264 5: draft
1262 1265 $ chmod a-w .hg/store/phaseroots # prevent phase push
1263 1266 $ cd ../../issue3781
1264 1267 $ cat >> .hg/hgrc << EOF
1265 1268 > [paths]
1266 1269 > default=../issue3781-dest/
1267 1270 > EOF
1268 1271 $ hg push
1269 1272 pushing to $TESTTMP/issue3781-dest (glob)
1270 1273 pushing subrepo s to $TESTTMP/issue3781-dest/s
1271 1274 searching for changes
1272 1275 no changes found
1273 1276 searching for changes
1274 1277 no changes found
1275 1278 [1]
1276 1279 $ cd ..
1277 1280
1278 1281 Test phase choice for newly created commit with "phases.subrepochecks"
1279 1282 configuration
1280 1283
1281 1284 $ cd t
1282 1285 $ hg update -q -r 12
1283 1286
1284 1287 $ cat >> s/ss/.hg/hgrc <<EOF
1285 1288 > [phases]
1286 1289 > new-commit = secret
1287 1290 > EOF
1288 1291 $ cat >> s/.hg/hgrc <<EOF
1289 1292 > [phases]
1290 1293 > new-commit = draft
1291 1294 > EOF
1292 1295 $ echo phasecheck1 >> s/ss/a
1293 1296 $ hg -R s commit -S --config phases.checksubrepos=abort -m phasecheck1
1294 1297 committing subrepository ss
1295 1298 transaction abort!
1296 1299 rollback completed
1297 1300 abort: can't commit in draft phase conflicting secret from subrepository ss
1298 1301 [255]
1299 1302 $ echo phasecheck2 >> s/ss/a
1300 1303 $ hg -R s commit -S --config phases.checksubrepos=ignore -m phasecheck2
1301 1304 committing subrepository ss
1302 1305 $ hg -R s/ss phase tip
1303 1306 3: secret
1304 1307 $ hg -R s phase tip
1305 1308 6: draft
1306 1309 $ echo phasecheck3 >> s/ss/a
1307 1310 $ hg -R s commit -S -m phasecheck3
1308 1311 committing subrepository ss
1309 1312 warning: changes are committed in secret phase from subrepository ss
1310 1313 $ hg -R s/ss phase tip
1311 1314 4: secret
1312 1315 $ hg -R s phase tip
1313 1316 7: secret
1314 1317
1315 1318 $ cat >> t/.hg/hgrc <<EOF
1316 1319 > [phases]
1317 1320 > new-commit = draft
1318 1321 > EOF
1319 1322 $ cat >> .hg/hgrc <<EOF
1320 1323 > [phases]
1321 1324 > new-commit = public
1322 1325 > EOF
1323 1326 $ echo phasecheck4 >> s/ss/a
1324 1327 $ echo phasecheck4 >> t/t
1325 1328 $ hg commit -S -m phasecheck4
1326 1329 committing subrepository s
1327 1330 committing subrepository s/ss (glob)
1328 1331 warning: changes are committed in secret phase from subrepository ss
1329 1332 committing subrepository t
1330 1333 warning: changes are committed in secret phase from subrepository s
1331 1334 created new head
1332 1335 $ hg -R s/ss phase tip
1333 1336 5: secret
1334 1337 $ hg -R s phase tip
1335 1338 8: secret
1336 1339 $ hg -R t phase tip
1337 1340 6: draft
1338 1341 $ hg phase tip
1339 1342 15: secret
1340 1343
1341 1344 $ cd ..
1342 1345
1343 1346
1344 1347 Test that commit --secret works on both repo and subrepo (issue4182)
1345 1348
1346 1349 $ cd main
1347 1350 $ echo secret >> b
1348 1351 $ echo secret >> s/b
1349 1352 $ hg commit --secret --subrepo -m "secret"
1350 1353 committing subrepository s
1351 1354 $ hg phase -r .
1352 1355 6: secret
1353 1356 $ cd s
1354 1357 $ hg phase -r .
1355 1358 6: secret
1356 1359 $ cd ../../
1357 1360
1358 1361 Test "subrepos" template keyword
1359 1362
1360 1363 $ cd t
1361 1364 $ hg update -q 15
1362 1365 $ cat > .hgsub <<EOF
1363 1366 > s = s
1364 1367 > EOF
1365 1368 $ hg commit -m "16"
1366 1369 warning: changes are committed in secret phase from subrepository s
1367 1370
1368 1371 (addition of ".hgsub" itself)
1369 1372
1370 1373 $ hg diff --nodates -c 1 .hgsubstate
1371 1374 diff -r f7b1eb17ad24 -r 7cf8cfea66e4 .hgsubstate
1372 1375 --- /dev/null
1373 1376 +++ b/.hgsubstate
1374 1377 @@ -0,0 +1,1 @@
1375 1378 +e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1376 1379 $ hg log -r 1 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1377 1380 f7b1eb17ad24 000000000000
1378 1381 s
1379 1382
1380 1383 (modification of existing entry)
1381 1384
1382 1385 $ hg diff --nodates -c 2 .hgsubstate
1383 1386 diff -r 7cf8cfea66e4 -r df30734270ae .hgsubstate
1384 1387 --- a/.hgsubstate
1385 1388 +++ b/.hgsubstate
1386 1389 @@ -1,1 +1,1 @@
1387 1390 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1388 1391 +dc73e2e6d2675eb2e41e33c205f4bdab4ea5111d s
1389 1392 $ hg log -r 2 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1390 1393 7cf8cfea66e4 000000000000
1391 1394 s
1392 1395
1393 1396 (addition of entry)
1394 1397
1395 1398 $ hg diff --nodates -c 5 .hgsubstate
1396 1399 diff -r 7cf8cfea66e4 -r 1f14a2e2d3ec .hgsubstate
1397 1400 --- a/.hgsubstate
1398 1401 +++ b/.hgsubstate
1399 1402 @@ -1,1 +1,2 @@
1400 1403 e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1401 1404 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1402 1405 $ hg log -r 5 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1403 1406 7cf8cfea66e4 000000000000
1404 1407 t
1405 1408
1406 1409 (removal of existing entry)
1407 1410
1408 1411 $ hg diff --nodates -c 16 .hgsubstate
1409 1412 diff -r 8bec38d2bd0b -r f2f70bc3d3c9 .hgsubstate
1410 1413 --- a/.hgsubstate
1411 1414 +++ b/.hgsubstate
1412 1415 @@ -1,2 +1,1 @@
1413 1416 0731af8ca9423976d3743119d0865097c07bdc1b s
1414 1417 -e202dc79b04c88a636ea8913d9182a1346d9b3dc t
1415 1418 $ hg log -r 16 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1416 1419 8bec38d2bd0b 000000000000
1417 1420 t
1418 1421
1419 1422 (merging)
1420 1423
1421 1424 $ hg diff --nodates -c 9 .hgsubstate
1422 1425 diff -r f6affe3fbfaa -r f0d2028bf86d .hgsubstate
1423 1426 --- a/.hgsubstate
1424 1427 +++ b/.hgsubstate
1425 1428 @@ -1,1 +1,2 @@
1426 1429 fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
1427 1430 +60ca1237c19474e7a3978b0dc1ca4e6f36d51382 t
1428 1431 $ hg log -r 9 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1429 1432 f6affe3fbfaa 1f14a2e2d3ec
1430 1433 t
1431 1434
1432 1435 (removal of ".hgsub" itself)
1433 1436
1434 1437 $ hg diff --nodates -c 8 .hgsubstate
1435 1438 diff -r f94576341bcf -r 96615c1dad2d .hgsubstate
1436 1439 --- a/.hgsubstate
1437 1440 +++ /dev/null
1438 1441 @@ -1,2 +0,0 @@
1439 1442 -e4ece1bf43360ddc8f6a96432201a37b7cd27ae4 s
1440 1443 -7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4 t
1441 1444 $ hg log -r 8 --template "{p1node|short} {p2node|short}\n{subrepos % '{subrepo}\n'}"
1442 1445 f94576341bcf 000000000000
1443 1446
1444 1447 Test that '[paths]' is configured correctly at subrepo creation
1445 1448
1446 1449 $ cd $TESTTMP/tc
1447 1450 $ cat > .hgsub <<EOF
1448 1451 > # to clear bogus subrepo path 'bogus=[boguspath'
1449 1452 > s = s
1450 1453 > t = t
1451 1454 > EOF
1452 1455 $ hg update -q --clean null
1453 1456 $ rm -rf s t
1454 1457 $ cat >> .hg/hgrc <<EOF
1455 1458 > [paths]
1456 1459 > default-push = /foo/bar
1457 1460 > EOF
1458 1461 $ hg update -q
1459 1462 $ cat s/.hg/hgrc
1460 1463 [paths]
1461 1464 default = $TESTTMP/t/s
1462 1465 default-push = /foo/bar/s
1463 1466 $ cat s/ss/.hg/hgrc
1464 1467 [paths]
1465 1468 default = $TESTTMP/t/s/ss
1466 1469 default-push = /foo/bar/s/ss
1467 1470 $ cat t/.hg/hgrc
1468 1471 [paths]
1469 1472 default = $TESTTMP/t/t
1470 1473 default-push = /foo/bar/t
1471 1474 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now