##// END OF EJS Templates
merge: don't call copies.mergecopies unless we need to...
Bryan O'Sullivan -
r18612:0b6e6eac default
parent child Browse files
Show More
@@ -1,665 +1,667 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 node import nullid, nullrev, hex, bin
9 9 from i18n import _
10 10 import error, util, filemerge, copies, subrepo
11 11 import errno, os, shutil
12 12
13 13 class mergestate(object):
14 14 '''track 3-way merge state of individual files'''
15 15 def __init__(self, repo):
16 16 self._repo = repo
17 17 self._dirty = False
18 18 self._read()
19 19 def reset(self, node=None):
20 20 self._state = {}
21 21 if node:
22 22 self._local = node
23 23 shutil.rmtree(self._repo.join("merge"), True)
24 24 self._dirty = False
25 25 def _read(self):
26 26 self._state = {}
27 27 try:
28 28 f = self._repo.opener("merge/state")
29 29 for i, l in enumerate(f):
30 30 if i == 0:
31 31 self._local = bin(l[:-1])
32 32 else:
33 33 bits = l[:-1].split("\0")
34 34 self._state[bits[0]] = bits[1:]
35 35 f.close()
36 36 except IOError, err:
37 37 if err.errno != errno.ENOENT:
38 38 raise
39 39 self._dirty = False
40 40 def commit(self):
41 41 if self._dirty:
42 42 f = self._repo.opener("merge/state", "w")
43 43 f.write(hex(self._local) + "\n")
44 44 for d, v in self._state.iteritems():
45 45 f.write("\0".join([d] + v) + "\n")
46 46 f.close()
47 47 self._dirty = False
48 48 def add(self, fcl, fco, fca, fd):
49 49 hash = util.sha1(fcl.path()).hexdigest()
50 50 self._repo.opener.write("merge/" + hash, fcl.data())
51 51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 52 hex(fca.filenode()), fco.path(), fcl.flags()]
53 53 self._dirty = True
54 54 def __contains__(self, dfile):
55 55 return dfile in self._state
56 56 def __getitem__(self, dfile):
57 57 return self._state[dfile][0]
58 58 def __iter__(self):
59 59 l = self._state.keys()
60 60 l.sort()
61 61 for f in l:
62 62 yield f
63 63 def mark(self, dfile, state):
64 64 self._state[dfile][0] = state
65 65 self._dirty = True
66 66 def resolve(self, dfile, wctx, octx):
67 67 if self[dfile] == 'r':
68 68 return 0
69 69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
70 70 fcd = wctx[dfile]
71 71 fco = octx[ofile]
72 72 fca = self._repo.filectx(afile, fileid=anode)
73 73 # "premerge" x flags
74 74 flo = fco.flags()
75 75 fla = fca.flags()
76 76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
77 77 if fca.node() == nullid:
78 78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
79 79 afile)
80 80 elif flags == fla:
81 81 flags = flo
82 82 # restore local
83 83 f = self._repo.opener("merge/" + hash)
84 84 self._repo.wwrite(dfile, f.read(), flags)
85 85 f.close()
86 86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
87 87 if r is None:
88 88 # no real conflict
89 89 del self._state[dfile]
90 90 elif not r:
91 91 self.mark(dfile, 'r')
92 92 return r
93 93
94 94 def _checkunknownfile(repo, wctx, mctx, f):
95 95 return (not repo.dirstate._ignore(f)
96 96 and os.path.isfile(repo.wjoin(f))
97 97 and repo.dirstate.normalize(f) not in repo.dirstate
98 98 and mctx[f].cmp(wctx[f]))
99 99
100 100 def _checkunknown(repo, wctx, mctx):
101 101 "check for collisions between unknown files and files in mctx"
102 102
103 103 error = False
104 104 for f in mctx:
105 105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
106 106 error = True
107 107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
108 108 if error:
109 109 raise util.Abort(_("untracked files in working directory differ "
110 110 "from files in requested revision"))
111 111
112 112 def _remains(f, m, ma, workingctx=False):
113 113 """check whether specified file remains after merge.
114 114
115 115 It is assumed that specified file is not contained in the manifest
116 116 of the other context.
117 117 """
118 118 if f in ma:
119 119 n = m[f]
120 120 if n != ma[f]:
121 121 return True # because it is changed locally
122 122 # even though it doesn't remain, if "remote deleted" is
123 123 # chosen in manifestmerge()
124 124 elif workingctx and n[20:] == "a":
125 125 return True # because it is added locally (linear merge specific)
126 126 else:
127 127 return False # because it is removed remotely
128 128 else:
129 129 return True # because it is added locally
130 130
131 131 def _checkcollision(mctx, extractxs):
132 132 "check for case folding collisions in the destination context"
133 133 folded = {}
134 134 for fn in mctx:
135 135 fold = util.normcase(fn)
136 136 if fold in folded:
137 137 raise util.Abort(_("case-folding collision between %s and %s")
138 138 % (fn, folded[fold]))
139 139 folded[fold] = fn
140 140
141 141 if extractxs:
142 142 wctx, actx = extractxs
143 143 # class to delay looking up copy mapping
144 144 class pathcopies(object):
145 145 @util.propertycache
146 146 def map(self):
147 147 # {dst@mctx: src@wctx} copy mapping
148 148 return copies.pathcopies(wctx, mctx)
149 149 pc = pathcopies()
150 150
151 151 for fn in wctx:
152 152 fold = util.normcase(fn)
153 153 mfn = folded.get(fold, None)
154 154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
155 155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
156 156 _remains(mfn, mctx.manifest(), actx.manifest())):
157 157 raise util.Abort(_("case-folding collision between %s and %s")
158 158 % (mfn, fn))
159 159
160 160 def _forgetremoved(wctx, mctx, branchmerge):
161 161 """
162 162 Forget removed files
163 163
164 164 If we're jumping between revisions (as opposed to merging), and if
165 165 neither the working directory nor the target rev has the file,
166 166 then we need to remove it from the dirstate, to prevent the
167 167 dirstate from listing the file when it is no longer in the
168 168 manifest.
169 169
170 170 If we're merging, and the other revision has removed a file
171 171 that is not present in the working directory, we need to mark it
172 172 as removed.
173 173 """
174 174
175 175 actions = []
176 176 state = branchmerge and 'r' or 'f'
177 177 for f in wctx.deleted():
178 178 if f not in mctx:
179 179 actions.append((f, state, None, "forget deleted"))
180 180
181 181 if not branchmerge:
182 182 for f in wctx.removed():
183 183 if f not in mctx:
184 184 actions.append((f, "f", None, "forget removed"))
185 185
186 186 return actions
187 187
188 188 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial):
189 189 """
190 190 Merge p1 and p2 with ancestor pa and generate merge action list
191 191
192 192 branchmerge and force are as passed in to update
193 193 partial = function to filter file lists
194 194 """
195 195
196 196 overwrite = force and not branchmerge
197 197 actions, copy, movewithdir = [], {}, {}
198 198
199 199 if overwrite:
200 200 pa = wctx
201 201 elif pa == p2: # backwards
202 202 pa = wctx.p1()
203 elif not branchmerge and not wctx.dirty(missing=True):
204 pass
203 205 elif pa and repo.ui.configbool("merge", "followcopies", True):
204 206 ret = copies.mergecopies(repo, wctx, p2, pa)
205 207 copy, movewithdir, diverge, renamedelete = ret
206 208 for of, fl in diverge.iteritems():
207 209 actions.append((of, "dr", (fl,), "divergent renames"))
208 210 for of, fl in renamedelete.iteritems():
209 211 actions.append((of, "rd", (fl,), "rename and delete"))
210 212
211 213 repo.ui.note(_("resolving manifests\n"))
212 214 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
213 215 % (bool(branchmerge), bool(force), bool(partial)))
214 216 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
215 217
216 218 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
217 219 copied = set(copy.values())
218 220 copied.update(movewithdir.values())
219 221
220 222 if '.hgsubstate' in m1:
221 223 # check whether sub state is modified
222 224 for s in sorted(wctx.substate):
223 225 if wctx.sub(s).dirty():
224 226 m1['.hgsubstate'] += "+"
225 227 break
226 228
227 229 aborts, prompts = [], []
228 230 # Compare manifests
229 231 for f, n in m1.iteritems():
230 232 if partial and not partial(f):
231 233 continue
232 234 if f in m2:
233 235 n2 = m2[f]
234 236 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
235 237 nol = 'l' not in fl1 + fl2 + fla
236 238 a = ma.get(f, nullid)
237 239 if n == n2 and fl1 == fl2:
238 240 pass # same - keep local
239 241 elif n2 == a and fl2 == fla:
240 242 pass # remote unchanged - keep local
241 243 elif n == a and fl1 == fla: # local unchanged - use remote
242 244 if n == n2: # optimization: keep local content
243 245 actions.append((f, "e", (fl2,), "update permissions"))
244 246 else:
245 247 actions.append((f, "g", (fl2,), "remote is newer"))
246 248 elif nol and n2 == a: # remote only changed 'x'
247 249 actions.append((f, "e", (fl2,), "update permissions"))
248 250 elif nol and n == a: # local only changed 'x'
249 251 actions.append((f, "g", (fl1,), "remote is newer"))
250 252 else: # both changed something
251 253 actions.append((f, "m", (f, f, False), "versions differ"))
252 254 elif f in copied: # files we'll deal with on m2 side
253 255 pass
254 256 elif f in movewithdir: # directory rename
255 257 f2 = movewithdir[f]
256 258 actions.append((f, "d", (None, f2, m1.flags(f)),
257 259 "remote renamed directory to " + f2))
258 260 elif f in copy:
259 261 f2 = copy[f]
260 262 actions.append((f, "m", (f2, f, False),
261 263 "local copied/moved to " + f2))
262 264 elif f in ma: # clean, a different, no remote
263 265 if n != ma[f]:
264 266 prompts.append((f, "cd")) # prompt changed/deleted
265 267 elif n[20:] == "a": # added, no remote
266 268 actions.append((f, "f", None, "remote deleted"))
267 269 else:
268 270 actions.append((f, "r", None, "other deleted"))
269 271
270 272 for f, n in m2.iteritems():
271 273 if partial and not partial(f):
272 274 continue
273 275 if f in m1 or f in copied: # files already visited
274 276 continue
275 277 if f in movewithdir:
276 278 f2 = movewithdir[f]
277 279 actions.append((None, "d", (f, f2, m2.flags(f)),
278 280 "local renamed directory to " + f2))
279 281 elif f in copy:
280 282 f2 = copy[f]
281 283 if f2 in m2:
282 284 actions.append((f2, "m", (f, f, False),
283 285 "remote copied to " + f))
284 286 else:
285 287 actions.append((f2, "m", (f, f, True),
286 288 "remote moved to " + f))
287 289 elif f not in ma:
288 290 # local unknown, remote created: the logic is described by the
289 291 # following table:
290 292 #
291 293 # force branchmerge different | action
292 294 # n * n | get
293 295 # n * y | abort
294 296 # y n * | get
295 297 # y y n | get
296 298 # y y y | merge
297 299 #
298 300 # Checking whether the files are different is expensive, so we
299 301 # don't do that when we can avoid it.
300 302 if force and not branchmerge:
301 303 actions.append((f, "g", (m2.flags(f),), "remote created"))
302 304 else:
303 305 different = _checkunknownfile(repo, wctx, p2, f)
304 306 if force and branchmerge and different:
305 307 actions.append((f, "m", (f, f, False),
306 308 "remote differs from untracked local"))
307 309 elif not force and different:
308 310 aborts.append((f, "ud"))
309 311 else:
310 312 actions.append((f, "g", (m2.flags(f),), "remote created"))
311 313 elif n != ma[f]:
312 314 prompts.append((f, "dc")) # prompt deleted/changed
313 315
314 316 for f, m in sorted(aborts):
315 317 if m == "ud":
316 318 repo.ui.warn(_("%s: untracked file differs\n") % f)
317 319 else: assert False, m
318 320 if aborts:
319 321 raise util.Abort(_("untracked files in working directory differ "
320 322 "from files in requested revision"))
321 323
322 324 for f, m in sorted(prompts):
323 325 if m == "cd":
324 326 if repo.ui.promptchoice(
325 327 _("local changed %s which remote deleted\n"
326 328 "use (c)hanged version or (d)elete?") % f,
327 329 (_("&Changed"), _("&Delete")), 0):
328 330 actions.append((f, "r", None, "prompt delete"))
329 331 else:
330 332 actions.append((f, "a", None, "prompt keep"))
331 333 elif m == "dc":
332 334 if repo.ui.promptchoice(
333 335 _("remote changed %s which local deleted\n"
334 336 "use (c)hanged version or leave (d)eleted?") % f,
335 337 (_("&Changed"), _("&Deleted")), 0) == 0:
336 338 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
337 339 else: assert False, m
338 340 return actions
339 341
340 342 def actionkey(a):
341 343 return a[1] == "r" and -1 or 0, a
342 344
343 345 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
344 346 """apply the merge action list to the working directory
345 347
346 348 wctx is the working copy context
347 349 mctx is the context to be merged into the working copy
348 350 actx is the context of the common ancestor
349 351
350 352 Return a tuple of counts (updated, merged, removed, unresolved) that
351 353 describes how many files were affected by the update.
352 354 """
353 355
354 356 updated, merged, removed, unresolved = 0, 0, 0, 0
355 357 ms = mergestate(repo)
356 358 ms.reset(wctx.p1().node())
357 359 moves = []
358 360 actions.sort(key=actionkey)
359 361
360 362 # prescan for merges
361 363 for a in actions:
362 364 f, m, args, msg = a
363 365 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
364 366 if m == "m": # merge
365 367 f2, fd, move = args
366 368 if fd == '.hgsubstate': # merged internally
367 369 continue
368 370 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
369 371 fcl = wctx[f]
370 372 fco = mctx[f2]
371 373 if mctx == actx: # backwards, use working dir parent as ancestor
372 374 if fcl.parents():
373 375 fca = fcl.p1()
374 376 else:
375 377 fca = repo.filectx(f, fileid=nullrev)
376 378 else:
377 379 fca = fcl.ancestor(fco, actx)
378 380 if not fca:
379 381 fca = repo.filectx(f, fileid=nullrev)
380 382 ms.add(fcl, fco, fca, fd)
381 383 if f != fd and move:
382 384 moves.append(f)
383 385
384 386 audit = repo.wopener.audit
385 387
386 388 # remove renamed files after safely stored
387 389 for f in moves:
388 390 if os.path.lexists(repo.wjoin(f)):
389 391 repo.ui.debug("removing %s\n" % f)
390 392 audit(f)
391 393 util.unlinkpath(repo.wjoin(f))
392 394
393 395 numupdates = len(actions)
394 396 for i, a in enumerate(actions):
395 397 f, m, args, msg = a
396 398 repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
397 399 unit=_('files'))
398 400 if m == "r": # remove
399 401 repo.ui.note(_("removing %s\n") % f)
400 402 audit(f)
401 403 if f == '.hgsubstate': # subrepo states need updating
402 404 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
403 405 try:
404 406 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
405 407 except OSError, inst:
406 408 repo.ui.warn(_("update failed to remove %s: %s!\n") %
407 409 (f, inst.strerror))
408 410 removed += 1
409 411 elif m == "m": # merge
410 412 if fd == '.hgsubstate': # subrepo states need updating
411 413 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
412 414 overwrite)
413 415 continue
414 416 f2, fd, move = args
415 417 audit(fd)
416 418 r = ms.resolve(fd, wctx, mctx)
417 419 if r is not None and r > 0:
418 420 unresolved += 1
419 421 else:
420 422 if r is None:
421 423 updated += 1
422 424 else:
423 425 merged += 1
424 426 elif m == "g": # get
425 427 flags, = args
426 428 repo.ui.note(_("getting %s\n") % f)
427 429 repo.wwrite(f, mctx.filectx(f).data(), flags)
428 430 updated += 1
429 431 if f == '.hgsubstate': # subrepo states need updating
430 432 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
431 433 elif m == "d": # directory rename
432 434 f2, fd, flags = args
433 435 if f:
434 436 repo.ui.note(_("moving %s to %s\n") % (f, fd))
435 437 audit(f)
436 438 repo.wwrite(fd, wctx.filectx(f).data(), flags)
437 439 util.unlinkpath(repo.wjoin(f))
438 440 if f2:
439 441 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
440 442 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
441 443 updated += 1
442 444 elif m == "dr": # divergent renames
443 445 fl, = args
444 446 repo.ui.warn(_("note: possible conflict - %s was renamed "
445 447 "multiple times to:\n") % f)
446 448 for nf in fl:
447 449 repo.ui.warn(" %s\n" % nf)
448 450 elif m == "rd": # rename and delete
449 451 fl, = args
450 452 repo.ui.warn(_("note: possible conflict - %s was deleted "
451 453 "and renamed to:\n") % f)
452 454 for nf in fl:
453 455 repo.ui.warn(" %s\n" % nf)
454 456 elif m == "e": # exec
455 457 flags, = args
456 458 audit(f)
457 459 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
458 460 updated += 1
459 461 ms.commit()
460 462 repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
461 463
462 464 return updated, merged, removed, unresolved
463 465
464 466 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
465 467 "Calculate the actions needed to merge mctx into tctx"
466 468 actions = []
467 469 folding = not util.checkcase(repo.path)
468 470 if folding:
469 471 # collision check is not needed for clean update
470 472 if (not branchmerge and
471 473 (force or not tctx.dirty(missing=True, branch=False))):
472 474 _checkcollision(mctx, None)
473 475 else:
474 476 _checkcollision(mctx, (tctx, ancestor))
475 477 if tctx.rev() is None:
476 478 actions += _forgetremoved(tctx, mctx, branchmerge)
477 479 actions += manifestmerge(repo, tctx, mctx,
478 480 ancestor,
479 481 branchmerge, force,
480 482 partial)
481 483 return actions
482 484
483 485 def recordupdates(repo, actions, branchmerge):
484 486 "record merge actions to the dirstate"
485 487
486 488 for a in actions:
487 489 f, m, args, msg = a
488 490 if m == "r": # remove
489 491 if branchmerge:
490 492 repo.dirstate.remove(f)
491 493 else:
492 494 repo.dirstate.drop(f)
493 495 elif m == "a": # re-add
494 496 if not branchmerge:
495 497 repo.dirstate.add(f)
496 498 elif m == "f": # forget
497 499 repo.dirstate.drop(f)
498 500 elif m == "e": # exec change
499 501 repo.dirstate.normallookup(f)
500 502 elif m == "g": # get
501 503 if branchmerge:
502 504 repo.dirstate.otherparent(f)
503 505 else:
504 506 repo.dirstate.normal(f)
505 507 elif m == "m": # merge
506 508 f2, fd, move = args
507 509 if branchmerge:
508 510 # We've done a branch merge, mark this file as merged
509 511 # so that we properly record the merger later
510 512 repo.dirstate.merge(fd)
511 513 if f != f2: # copy/rename
512 514 if move:
513 515 repo.dirstate.remove(f)
514 516 if f != fd:
515 517 repo.dirstate.copy(f, fd)
516 518 else:
517 519 repo.dirstate.copy(f2, fd)
518 520 else:
519 521 # We've update-merged a locally modified file, so
520 522 # we set the dirstate to emulate a normal checkout
521 523 # of that file some time in the past. Thus our
522 524 # merge will appear as a normal local file
523 525 # modification.
524 526 if f2 == fd: # file not locally copied/moved
525 527 repo.dirstate.normallookup(fd)
526 528 if move:
527 529 repo.dirstate.drop(f)
528 530 elif m == "d": # directory rename
529 531 f2, fd, flag = args
530 532 if not f2 and f not in repo.dirstate:
531 533 # untracked file moved
532 534 continue
533 535 if branchmerge:
534 536 repo.dirstate.add(fd)
535 537 if f:
536 538 repo.dirstate.remove(f)
537 539 repo.dirstate.copy(f, fd)
538 540 if f2:
539 541 repo.dirstate.copy(f2, fd)
540 542 else:
541 543 repo.dirstate.normal(fd)
542 544 if f:
543 545 repo.dirstate.drop(f)
544 546
545 547 def update(repo, node, branchmerge, force, partial, ancestor=None,
546 548 mergeancestor=False):
547 549 """
548 550 Perform a merge between the working directory and the given node
549 551
550 552 node = the node to update to, or None if unspecified
551 553 branchmerge = whether to merge between branches
552 554 force = whether to force branch merging or file overwriting
553 555 partial = a function to filter file lists (dirstate not updated)
554 556 mergeancestor = if false, merging with an ancestor (fast-forward)
555 557 is only allowed between different named branches. This flag
556 558 is used by rebase extension as a temporary fix and should be
557 559 avoided in general.
558 560
559 561 The table below shows all the behaviors of the update command
560 562 given the -c and -C or no options, whether the working directory
561 563 is dirty, whether a revision is specified, and the relationship of
562 564 the parent rev to the target rev (linear, on the same named
563 565 branch, or on another named branch).
564 566
565 567 This logic is tested by test-update-branches.t.
566 568
567 569 -c -C dirty rev | linear same cross
568 570 n n n n | ok (1) x
569 571 n n n y | ok ok ok
570 572 n n y * | merge (2) (2)
571 573 n y * * | --- discard ---
572 574 y n y * | --- (3) ---
573 575 y n n * | --- ok ---
574 576 y y * * | --- (4) ---
575 577
576 578 x = can't happen
577 579 * = don't-care
578 580 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
579 581 2 = abort: crosses branches (use 'hg merge' to merge or
580 582 use 'hg update -C' to discard changes)
581 583 3 = abort: uncommitted local changes
582 584 4 = incompatible options (checked in commands.py)
583 585
584 586 Return the same tuple as applyupdates().
585 587 """
586 588
587 589 onode = node
588 590 wlock = repo.wlock()
589 591 try:
590 592 wc = repo[None]
591 593 if node is None:
592 594 # tip of current branch
593 595 try:
594 596 node = repo.branchtip(wc.branch())
595 597 except error.RepoLookupError:
596 598 if wc.branch() == "default": # no default branch!
597 599 node = repo.lookup("tip") # update to tip
598 600 else:
599 601 raise util.Abort(_("branch %s not found") % wc.branch())
600 602 overwrite = force and not branchmerge
601 603 pl = wc.parents()
602 604 p1, p2 = pl[0], repo[node]
603 605 if ancestor:
604 606 pa = repo[ancestor]
605 607 else:
606 608 pa = p1.ancestor(p2)
607 609
608 610 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
609 611
610 612 ### check phase
611 613 if not overwrite and len(pl) > 1:
612 614 raise util.Abort(_("outstanding uncommitted merges"))
613 615 if branchmerge:
614 616 if pa == p2:
615 617 raise util.Abort(_("merging with a working directory ancestor"
616 618 " has no effect"))
617 619 elif pa == p1:
618 620 if not mergeancestor and p1.branch() == p2.branch():
619 621 raise util.Abort(_("nothing to merge"),
620 622 hint=_("use 'hg update' "
621 623 "or check 'hg heads'"))
622 624 if not force and (wc.files() or wc.deleted()):
623 625 raise util.Abort(_("outstanding uncommitted changes"),
624 626 hint=_("use 'hg status' to list changes"))
625 627 for s in sorted(wc.substate):
626 628 if wc.sub(s).dirty():
627 629 raise util.Abort(_("outstanding uncommitted changes in "
628 630 "subrepository '%s'") % s)
629 631
630 632 elif not overwrite:
631 633 if pa == p1 or pa == p2: # linear
632 634 pass # all good
633 635 elif wc.dirty(missing=True):
634 636 raise util.Abort(_("crosses branches (merge branches or use"
635 637 " --clean to discard changes)"))
636 638 elif onode is None:
637 639 raise util.Abort(_("crosses branches (merge branches or update"
638 640 " --check to force update)"))
639 641 else:
640 642 # Allow jumping branches if clean and specific rev given
641 643 pa = p1
642 644
643 645 ### calculate phase
644 646 actions = calculateupdates(repo, wc, p2, pa,
645 647 branchmerge, force, partial)
646 648
647 649 ### apply phase
648 650 if not branchmerge: # just jump to the new rev
649 651 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
650 652 if not partial:
651 653 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
652 654
653 655 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
654 656
655 657 if not partial:
656 658 repo.setparents(fp1, fp2)
657 659 recordupdates(repo, actions, branchmerge)
658 660 if not branchmerge:
659 661 repo.dirstate.setbranch(p2.branch())
660 662 finally:
661 663 wlock.release()
662 664
663 665 if not partial:
664 666 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
665 667 return stats
@@ -1,1088 +1,1087 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 Revert subrepo and test subrepo fileset keyword:
39 39
40 40 $ echo b > s/a
41 41 $ hg revert "set:subrepo('glob:s*')"
42 42 reverting subrepo s
43 43 reverting s/a (glob)
44 44 $ rm s/a.orig
45 45
46 46 Revert subrepo with no backup. The "reverting s/a" line is gone since
47 47 we're really running 'hg update' in the subrepo:
48 48
49 49 $ echo b > s/a
50 50 $ hg revert --no-backup s
51 51 reverting subrepo s
52 52
53 53 Issue2022: update -C
54 54
55 55 $ echo b > s/a
56 56 $ hg sum
57 57 parent: 1:7cf8cfea66e4 tip
58 58 1
59 59 branch: default
60 60 commit: 1 subrepos
61 61 update: (current)
62 62 $ hg co -C 1
63 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 $ hg sum
65 65 parent: 1:7cf8cfea66e4 tip
66 66 1
67 67 branch: default
68 68 commit: (clean)
69 69 update: (current)
70 70
71 71 commands that require a clean repo should respect subrepos
72 72
73 73 $ echo b >> s/a
74 74 $ hg backout tip
75 75 abort: uncommitted changes in subrepo s
76 76 [255]
77 77 $ hg revert -C -R s s/a
78 78
79 79 add sub sub
80 80
81 81 $ echo ss = ss > s/.hgsub
82 82 $ hg init s/ss
83 83 $ echo a > s/ss/a
84 84 $ hg -R s add s/.hgsub
85 85 $ hg -R s/ss add s/ss/a
86 86 $ hg sum
87 87 parent: 1:7cf8cfea66e4 tip
88 88 1
89 89 branch: default
90 90 commit: 1 subrepos
91 91 update: (current)
92 92 $ hg ci -m2
93 93 committing subrepository s
94 94 committing subrepository s/ss (glob)
95 95 $ hg sum
96 96 parent: 2:df30734270ae tip
97 97 2
98 98 branch: default
99 99 commit: (clean)
100 100 update: (current)
101 101
102 102 bump sub rev (and check it is ignored by ui.commitsubrepos)
103 103
104 104 $ echo b > s/a
105 105 $ hg -R s ci -ms1
106 106 $ hg --config ui.commitsubrepos=no ci -m3
107 107
108 108 leave sub dirty (and check ui.commitsubrepos=no aborts the commit)
109 109
110 110 $ echo c > s/a
111 111 $ hg --config ui.commitsubrepos=no ci -m4
112 112 abort: uncommitted changes in subrepo s
113 113 (use --subrepos for recursive commit)
114 114 [255]
115 115 $ hg id
116 116 f6affe3fbfaa+ tip
117 117 $ hg -R s ci -mc
118 118 $ hg id
119 119 f6affe3fbfaa+ tip
120 120 $ echo d > s/a
121 121 $ hg ci -m4
122 122 committing subrepository s
123 123 $ hg tip -R s
124 124 changeset: 4:02dcf1d70411
125 125 tag: tip
126 126 user: test
127 127 date: Thu Jan 01 00:00:00 1970 +0000
128 128 summary: 4
129 129
130 130
131 131 check caching
132 132
133 133 $ hg co 0
134 134 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
135 135 $ hg debugsub
136 136
137 137 restore
138 138
139 139 $ hg co
140 140 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 141 $ hg debugsub
142 142 path s
143 143 source s
144 144 revision 02dcf1d704118aee3ee306ccfa1910850d5b05ef
145 145
146 146 new branch for merge tests
147 147
148 148 $ hg co 1
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 $ echo t = t >> .hgsub
151 151 $ hg init t
152 152 $ echo t > t/t
153 153 $ hg -R t add t
154 154 adding t/t (glob)
155 155
156 156 5
157 157
158 158 $ hg ci -m5 # add sub
159 159 committing subrepository t
160 160 created new head
161 161 $ echo t2 > t/t
162 162
163 163 6
164 164
165 165 $ hg st -R s
166 166 $ hg ci -m6 # change sub
167 167 committing subrepository t
168 168 $ hg debugsub
169 169 path s
170 170 source s
171 171 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
172 172 path t
173 173 source t
174 174 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
175 175 $ echo t3 > t/t
176 176
177 177 7
178 178
179 179 $ hg ci -m7 # change sub again for conflict test
180 180 committing subrepository t
181 181 $ hg rm .hgsub
182 182
183 183 8
184 184
185 185 $ hg ci -m8 # remove sub
186 186
187 187 merge tests
188 188
189 189 $ hg co -C 3
190 190 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 191 $ hg merge 5 # test adding
192 192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 (branch merge, don't forget to commit)
194 194 $ hg debugsub
195 195 path s
196 196 source s
197 197 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
198 198 path t
199 199 source t
200 200 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
201 201 $ hg ci -m9
202 202 created new head
203 203 $ hg merge 6 --debug # test change
204 204 searching for copies back to rev 2
205 205 resolving manifests
206 206 branchmerge: True, force: False, partial: False
207 207 ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
208 208 .hgsubstate: versions differ -> m
209 209 updating: .hgsubstate 1/1 files (100.00%)
210 210 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
211 211 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
212 212 getting subrepo t
213 searching for copies back to rev 1
214 213 resolving manifests
215 214 branchmerge: False, force: False, partial: False
216 215 ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
217 216 t: remote is newer -> g
218 217 updating: t 1/1 files (100.00%)
219 218 getting t
220 219 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 220 (branch merge, don't forget to commit)
222 221 $ hg debugsub
223 222 path s
224 223 source s
225 224 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
226 225 path t
227 226 source t
228 227 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
229 228 $ echo conflict > t/t
230 229 $ hg ci -m10
231 230 committing subrepository t
232 231 $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
233 232 searching for copies back to rev 2
234 233 resolving manifests
235 234 branchmerge: True, force: False, partial: False
236 235 ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
237 236 .hgsubstate: versions differ -> m
238 237 updating: .hgsubstate 1/1 files (100.00%)
239 238 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
240 239 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
241 240 merging subrepo t
242 241 searching for copies back to rev 2
243 242 resolving manifests
244 243 branchmerge: True, force: False, partial: False
245 244 ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
246 245 t: versions differ -> m
247 246 preserving t for resolve of t
248 247 updating: t 1/1 files (100.00%)
249 248 picked tool 'internal:merge' for t (binary False symlink False)
250 249 merging t
251 250 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
252 251 warning: conflicts during merge.
253 252 merging t incomplete! (edit conflicts, then use 'hg resolve --mark')
254 253 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
255 254 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
256 255 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 256 (branch merge, don't forget to commit)
258 257
259 258 should conflict
260 259
261 260 $ cat t/t
262 261 <<<<<<< local
263 262 conflict
264 263 =======
265 264 t3
266 265 >>>>>>> other
267 266
268 267 clone
269 268
270 269 $ cd ..
271 270 $ hg clone t tc
272 271 updating to branch default
273 272 cloning subrepo s from $TESTTMP/t/s (glob)
274 273 cloning subrepo s/ss from $TESTTMP/t/s/ss (glob)
275 274 cloning subrepo t from $TESTTMP/t/t (glob)
276 275 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 276 $ cd tc
278 277 $ hg debugsub
279 278 path s
280 279 source s
281 280 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
282 281 path t
283 282 source t
284 283 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
285 284
286 285 push
287 286
288 287 $ echo bah > t/t
289 288 $ hg ci -m11
290 289 committing subrepository t
291 290 $ hg push
292 291 pushing to $TESTTMP/t (glob)
293 292 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
294 293 searching for changes
295 294 no changes found
296 295 pushing subrepo s to $TESTTMP/t/s (glob)
297 296 searching for changes
298 297 no changes found
299 298 pushing subrepo t to $TESTTMP/t/t (glob)
300 299 searching for changes
301 300 adding changesets
302 301 adding manifests
303 302 adding file changes
304 303 added 1 changesets with 1 changes to 1 files
305 304 searching for changes
306 305 adding changesets
307 306 adding manifests
308 307 adding file changes
309 308 added 1 changesets with 1 changes to 1 files
310 309
311 310 push -f
312 311
313 312 $ echo bah > s/a
314 313 $ hg ci -m12
315 314 committing subrepository s
316 315 $ hg push
317 316 pushing to $TESTTMP/t (glob)
318 317 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
319 318 searching for changes
320 319 no changes found
321 320 pushing subrepo s to $TESTTMP/t/s (glob)
322 321 searching for changes
323 322 abort: push creates new remote head 12a213df6fa9! (in subrepo s)
324 323 (did you forget to merge? use push -f to force)
325 324 [255]
326 325 $ hg push -f
327 326 pushing to $TESTTMP/t (glob)
328 327 pushing subrepo s/ss to $TESTTMP/t/s/ss (glob)
329 328 searching for changes
330 329 no changes found
331 330 pushing subrepo s to $TESTTMP/t/s (glob)
332 331 searching for changes
333 332 adding changesets
334 333 adding manifests
335 334 adding file changes
336 335 added 1 changesets with 1 changes to 1 files (+1 heads)
337 336 pushing subrepo t to $TESTTMP/t/t (glob)
338 337 searching for changes
339 338 no changes found
340 339 searching for changes
341 340 adding changesets
342 341 adding manifests
343 342 adding file changes
344 343 added 1 changesets with 1 changes to 1 files
345 344
346 345 update
347 346
348 347 $ cd ../t
349 348 $ hg up -C # discard our earlier merge
350 349 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 350 $ echo blah > t/t
352 351 $ hg ci -m13
353 352 committing subrepository t
354 353
355 354 pull
356 355
357 356 $ cd ../tc
358 357 $ hg pull
359 358 pulling from $TESTTMP/t (glob)
360 359 searching for changes
361 360 adding changesets
362 361 adding manifests
363 362 adding file changes
364 363 added 1 changesets with 1 changes to 1 files
365 364 (run 'hg update' to get a working copy)
366 365
367 366 should pull t
368 367
369 368 $ hg up
370 369 pulling subrepo t from $TESTTMP/t/t (glob)
371 370 searching for changes
372 371 adding changesets
373 372 adding manifests
374 373 adding file changes
375 374 added 1 changesets with 1 changes to 1 files
376 375 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 376 $ cat t/t
378 377 blah
379 378
380 379 bogus subrepo path aborts
381 380
382 381 $ echo 'bogus=[boguspath' >> .hgsub
383 382 $ hg ci -m 'bogus subrepo path'
384 383 abort: missing ] in subrepo source
385 384 [255]
386 385
387 386 Issue1986: merge aborts when trying to merge a subrepo that
388 387 shouldn't need merging
389 388
390 389 # subrepo layout
391 390 #
392 391 # o 5 br
393 392 # /|
394 393 # o | 4 default
395 394 # | |
396 395 # | o 3 br
397 396 # |/|
398 397 # o | 2 default
399 398 # | |
400 399 # | o 1 br
401 400 # |/
402 401 # o 0 default
403 402
404 403 $ cd ..
405 404 $ rm -rf sub
406 405 $ hg init main
407 406 $ cd main
408 407 $ hg init s
409 408 $ cd s
410 409 $ echo a > a
411 410 $ hg ci -Am1
412 411 adding a
413 412 $ hg branch br
414 413 marked working directory as branch br
415 414 (branches are permanent and global, did you want a bookmark?)
416 415 $ echo a >> a
417 416 $ hg ci -m1
418 417 $ hg up default
419 418 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
420 419 $ echo b > b
421 420 $ hg ci -Am1
422 421 adding b
423 422 $ hg up br
424 423 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
425 424 $ hg merge tip
426 425 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
427 426 (branch merge, don't forget to commit)
428 427 $ hg ci -m1
429 428 $ hg up 2
430 429 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 430 $ echo c > c
432 431 $ hg ci -Am1
433 432 adding c
434 433 $ hg up 3
435 434 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 435 $ hg merge 4
437 436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 437 (branch merge, don't forget to commit)
439 438 $ hg ci -m1
440 439
441 440 # main repo layout:
442 441 #
443 442 # * <-- try to merge default into br again
444 443 # .`|
445 444 # . o 5 br --> substate = 5
446 445 # . |
447 446 # o | 4 default --> substate = 4
448 447 # | |
449 448 # | o 3 br --> substate = 2
450 449 # |/|
451 450 # o | 2 default --> substate = 2
452 451 # | |
453 452 # | o 1 br --> substate = 3
454 453 # |/
455 454 # o 0 default --> substate = 2
456 455
457 456 $ cd ..
458 457 $ echo 's = s' > .hgsub
459 458 $ hg -R s up 2
460 459 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
461 460 $ hg ci -Am1
462 461 adding .hgsub
463 462 $ hg branch br
464 463 marked working directory as branch br
465 464 (branches are permanent and global, did you want a bookmark?)
466 465 $ echo b > b
467 466 $ hg -R s up 3
468 467 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 468 $ hg ci -Am1
470 469 adding b
471 470 $ hg up default
472 471 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
473 472 $ echo c > c
474 473 $ hg ci -Am1
475 474 adding c
476 475 $ hg up 1
477 476 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
478 477 $ hg merge 2
479 478 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 479 (branch merge, don't forget to commit)
481 480 $ hg ci -m1
482 481 $ hg up 2
483 482 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
484 483 $ hg -R s up 4
485 484 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 485 $ echo d > d
487 486 $ hg ci -Am1
488 487 adding d
489 488 $ hg up 3
490 489 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
491 490 $ hg -R s up 5
492 491 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
493 492 $ echo e > e
494 493 $ hg ci -Am1
495 494 adding e
496 495
497 496 $ hg up 5
498 497 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
499 498 $ hg merge 4 # try to merge default into br again
500 499 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
501 500 (branch merge, don't forget to commit)
502 501 $ cd ..
503 502
504 503 test subrepo delete from .hgsubstate
505 504
506 505 $ hg init testdelete
507 506 $ mkdir testdelete/nested testdelete/nested2
508 507 $ hg init testdelete/nested
509 508 $ hg init testdelete/nested2
510 509 $ echo test > testdelete/nested/foo
511 510 $ echo test > testdelete/nested2/foo
512 511 $ hg -R testdelete/nested add
513 512 adding testdelete/nested/foo (glob)
514 513 $ hg -R testdelete/nested2 add
515 514 adding testdelete/nested2/foo (glob)
516 515 $ hg -R testdelete/nested ci -m test
517 516 $ hg -R testdelete/nested2 ci -m test
518 517 $ echo nested = nested > testdelete/.hgsub
519 518 $ echo nested2 = nested2 >> testdelete/.hgsub
520 519 $ hg -R testdelete add
521 520 adding testdelete/.hgsub (glob)
522 521 $ hg -R testdelete ci -m "nested 1 & 2 added"
523 522 $ echo nested = nested > testdelete/.hgsub
524 523 $ hg -R testdelete ci -m "nested 2 deleted"
525 524 $ cat testdelete/.hgsubstate
526 525 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
527 526 $ hg -R testdelete remove testdelete/.hgsub
528 527 $ hg -R testdelete ci -m ".hgsub deleted"
529 528 $ cat testdelete/.hgsubstate
530 529 bdf5c9a3103743d900b12ae0db3ffdcfd7b0d878 nested
531 530
532 531 test repository cloning
533 532
534 533 $ mkdir mercurial mercurial2
535 534 $ hg init nested_absolute
536 535 $ echo test > nested_absolute/foo
537 536 $ hg -R nested_absolute add
538 537 adding nested_absolute/foo (glob)
539 538 $ hg -R nested_absolute ci -mtest
540 539 $ cd mercurial
541 540 $ hg init nested_relative
542 541 $ echo test2 > nested_relative/foo2
543 542 $ hg -R nested_relative add
544 543 adding nested_relative/foo2 (glob)
545 544 $ hg -R nested_relative ci -mtest2
546 545 $ hg init main
547 546 $ echo "nested_relative = ../nested_relative" > main/.hgsub
548 547 $ echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
549 548 $ hg -R main add
550 549 adding main/.hgsub (glob)
551 550 $ hg -R main ci -m "add subrepos"
552 551 $ cd ..
553 552 $ hg clone mercurial/main mercurial2/main
554 553 updating to branch default
555 554 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 555 $ cat mercurial2/main/nested_absolute/.hg/hgrc \
557 556 > mercurial2/main/nested_relative/.hg/hgrc
558 557 [paths]
559 558 default = $TESTTMP/mercurial/nested_absolute
560 559 [paths]
561 560 default = $TESTTMP/mercurial/nested_relative
562 561 $ rm -rf mercurial mercurial2
563 562
564 563 Issue1977: multirepo push should fail if subrepo push fails
565 564
566 565 $ hg init repo
567 566 $ hg init repo/s
568 567 $ echo a > repo/s/a
569 568 $ hg -R repo/s ci -Am0
570 569 adding a
571 570 $ echo s = s > repo/.hgsub
572 571 $ hg -R repo ci -Am1
573 572 adding .hgsub
574 573 $ hg clone repo repo2
575 574 updating to branch default
576 575 cloning subrepo s from $TESTTMP/repo/s (glob)
577 576 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
578 577 $ hg -q -R repo2 pull -u
579 578 $ echo 1 > repo2/s/a
580 579 $ hg -R repo2/s ci -m2
581 580 $ hg -q -R repo2/s push
582 581 $ hg -R repo2/s up -C 0
583 582 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 583 $ echo 2 > repo2/s/b
585 584 $ hg -R repo2/s ci -m3 -A
586 585 adding b
587 586 created new head
588 587 $ hg -R repo2 ci -m3
589 588 $ hg -q -R repo2 push
590 589 abort: push creates new remote head cc505f09a8b2! (in subrepo s)
591 590 (did you forget to merge? use push -f to force)
592 591 [255]
593 592 $ hg -R repo update
594 593 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 594
596 595 test if untracked file is not overwritten
597 596
598 597 $ echo issue3276_ok > repo/s/b
599 598 $ hg -R repo2 push -f -q
600 599 $ hg -R repo update
601 600 b: untracked file differs
602 601 abort: untracked files in working directory differ from files in requested revision (in subrepo s)
603 602 [255]
604 603
605 604 $ cat repo/s/b
606 605 issue3276_ok
607 606 $ rm repo/s/b
608 607 $ hg -R repo revert --all
609 608 reverting repo/.hgsubstate (glob)
610 609 reverting subrepo s
611 610 $ hg -R repo update
612 611 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
613 612 $ cat repo/s/b
614 613 2
615 614 $ rm -rf repo2 repo
616 615
617 616
618 617 Issue1852 subrepos with relative paths always push/pull relative to default
619 618
620 619 Prepare a repo with subrepo
621 620
622 621 $ hg init issue1852a
623 622 $ cd issue1852a
624 623 $ hg init sub/repo
625 624 $ echo test > sub/repo/foo
626 625 $ hg -R sub/repo add sub/repo/foo
627 626 $ echo sub/repo = sub/repo > .hgsub
628 627 $ hg add .hgsub
629 628 $ hg ci -mtest
630 629 committing subrepository sub/repo (glob)
631 630 $ echo test >> sub/repo/foo
632 631 $ hg ci -mtest
633 632 committing subrepository sub/repo (glob)
634 633 $ cd ..
635 634
636 635 Create repo without default path, pull top repo, and see what happens on update
637 636
638 637 $ hg init issue1852b
639 638 $ hg -R issue1852b pull issue1852a
640 639 pulling from issue1852a
641 640 requesting all changes
642 641 adding changesets
643 642 adding manifests
644 643 adding file changes
645 644 added 2 changesets with 3 changes to 2 files
646 645 (run 'hg update' to get a working copy)
647 646 $ hg -R issue1852b update
648 647 abort: default path for subrepository not found (in subrepo sub/repo) (glob)
649 648 [255]
650 649
651 650 Pull -u now doesn't help
652 651
653 652 $ hg -R issue1852b pull -u issue1852a
654 653 pulling from issue1852a
655 654 searching for changes
656 655 no changes found
657 656
658 657 Try the same, but with pull -u
659 658
660 659 $ hg init issue1852c
661 660 $ hg -R issue1852c pull -r0 -u issue1852a
662 661 pulling from issue1852a
663 662 adding changesets
664 663 adding manifests
665 664 adding file changes
666 665 added 1 changesets with 2 changes to 2 files
667 666 cloning subrepo sub/repo from issue1852a/sub/repo (glob)
668 667 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
669 668
670 669 Try to push from the other side
671 670
672 671 $ hg -R issue1852a push `pwd`/issue1852c
673 672 pushing to $TESTTMP/issue1852c
674 673 pushing subrepo sub/repo to $TESTTMP/issue1852c/sub/repo (glob)
675 674 searching for changes
676 675 no changes found
677 676 searching for changes
678 677 adding changesets
679 678 adding manifests
680 679 adding file changes
681 680 added 1 changesets with 1 changes to 1 files
682 681
683 682 Incoming and outgoing should not use the default path:
684 683
685 684 $ hg clone -q issue1852a issue1852d
686 685 $ hg -R issue1852d outgoing --subrepos issue1852c
687 686 comparing with issue1852c
688 687 searching for changes
689 688 no changes found
690 689 comparing with issue1852c/sub/repo
691 690 searching for changes
692 691 no changes found
693 692 [1]
694 693 $ hg -R issue1852d incoming --subrepos issue1852c
695 694 comparing with issue1852c
696 695 searching for changes
697 696 no changes found
698 697 comparing with issue1852c/sub/repo
699 698 searching for changes
700 699 no changes found
701 700 [1]
702 701
703 702 Check status of files when none of them belong to the first
704 703 subrepository:
705 704
706 705 $ hg init subrepo-status
707 706 $ cd subrepo-status
708 707 $ hg init subrepo-1
709 708 $ hg init subrepo-2
710 709 $ cd subrepo-2
711 710 $ touch file
712 711 $ hg add file
713 712 $ cd ..
714 713 $ echo subrepo-1 = subrepo-1 > .hgsub
715 714 $ echo subrepo-2 = subrepo-2 >> .hgsub
716 715 $ hg add .hgsub
717 716 $ hg ci -m 'Added subrepos'
718 717 committing subrepository subrepo-2
719 718 $ hg st subrepo-2/file
720 719
721 720 Check that share works with subrepo
722 721 $ hg --config extensions.share= share . ../shared
723 722 updating working directory
724 723 cloning subrepo subrepo-2 from $TESTTMP/subrepo-status/subrepo-2
725 724 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
726 725 $ test -f ../shared/subrepo-1/.hg/sharedpath
727 726 [1]
728 727 $ hg -R ../shared in
729 728 abort: repository default not found!
730 729 [255]
731 730 $ hg -R ../shared/subrepo-2 showconfig paths
732 731 paths.default=$TESTTMP/subrepo-status/subrepo-2
733 732 $ hg -R ../shared/subrepo-1 sum --remote
734 733 parent: -1:000000000000 tip (empty repository)
735 734 branch: default
736 735 commit: (clean)
737 736 update: (current)
738 737 remote: (synced)
739 738
740 739 Check hg update --clean
741 740 $ cd $TESTTMP/t
742 741 $ rm -r t/t.orig
743 742 $ hg status -S --all
744 743 C .hgsub
745 744 C .hgsubstate
746 745 C a
747 746 C s/.hgsub
748 747 C s/.hgsubstate
749 748 C s/a
750 749 C s/ss/a
751 750 C t/t
752 751 $ echo c1 > s/a
753 752 $ cd s
754 753 $ echo c1 > b
755 754 $ echo c1 > c
756 755 $ hg add b
757 756 $ cd ..
758 757 $ hg status -S
759 758 M s/a
760 759 A s/b
761 760 ? s/c
762 761 $ hg update -C
763 762 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
764 763 $ hg status -S
765 764 ? s/b
766 765 ? s/c
767 766
768 767 Sticky subrepositories, no changes
769 768 $ cd $TESTTMP/t
770 769 $ hg id
771 770 925c17564ef8 tip
772 771 $ hg -R s id
773 772 12a213df6fa9 tip
774 773 $ hg -R t id
775 774 52c0adc0515a tip
776 775 $ hg update 11
777 776 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
778 777 $ hg id
779 778 365661e5936a
780 779 $ hg -R s id
781 780 fc627a69481f
782 781 $ hg -R t id
783 782 e95bcfa18a35
784 783
785 784 Sticky subrepositorys, file changes
786 785 $ touch s/f1
787 786 $ touch t/f1
788 787 $ hg add -S s/f1
789 788 $ hg add -S t/f1
790 789 $ hg id
791 790 365661e5936a+
792 791 $ hg -R s id
793 792 fc627a69481f+
794 793 $ hg -R t id
795 794 e95bcfa18a35+
796 795 $ hg update tip
797 796 subrepository sources for s differ
798 797 use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)?
799 798 l
800 799 subrepository sources for t differ
801 800 use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)?
802 801 l
803 802 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
804 803 $ hg id
805 804 925c17564ef8+ tip
806 805 $ hg -R s id
807 806 fc627a69481f+
808 807 $ hg -R t id
809 808 e95bcfa18a35+
810 809 $ hg update --clean tip
811 810 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
812 811
813 812 Sticky subrepository, revision updates
814 813 $ hg id
815 814 925c17564ef8 tip
816 815 $ hg -R s id
817 816 12a213df6fa9 tip
818 817 $ hg -R t id
819 818 52c0adc0515a tip
820 819 $ cd s
821 820 $ hg update -r -2
822 821 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
823 822 $ cd ../t
824 823 $ hg update -r 2
825 824 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
826 825 $ cd ..
827 826 $ hg update 10
828 827 subrepository sources for t differ (in checked out version)
829 828 use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)?
830 829 l
831 830 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
832 831 $ hg id
833 832 e45c8b14af55+
834 833 $ hg -R s id
835 834 02dcf1d70411
836 835 $ hg -R t id
837 836 7af322bc1198
838 837
839 838 Sticky subrepository, file changes and revision updates
840 839 $ touch s/f1
841 840 $ touch t/f1
842 841 $ hg add -S s/f1
843 842 $ hg add -S t/f1
844 843 $ hg id
845 844 e45c8b14af55+
846 845 $ hg -R s id
847 846 02dcf1d70411+
848 847 $ hg -R t id
849 848 7af322bc1198+
850 849 $ hg update tip
851 850 subrepository sources for s differ
852 851 use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)?
853 852 l
854 853 subrepository sources for t differ
855 854 use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)?
856 855 l
857 856 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
858 857 $ hg id
859 858 925c17564ef8+ tip
860 859 $ hg -R s id
861 860 02dcf1d70411+
862 861 $ hg -R t id
863 862 7af322bc1198+
864 863
865 864 Sticky repository, update --clean
866 865 $ hg update --clean tip
867 866 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
868 867 $ hg id
869 868 925c17564ef8 tip
870 869 $ hg -R s id
871 870 12a213df6fa9 tip
872 871 $ hg -R t id
873 872 52c0adc0515a tip
874 873
875 874 Test subrepo already at intended revision:
876 875 $ cd s
877 876 $ hg update fc627a69481f
878 877 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
879 878 $ cd ..
880 879 $ hg update 11
881 880 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 881 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 882 $ hg id -n
884 883 11+
885 884 $ hg -R s id
886 885 fc627a69481f
887 886 $ hg -R t id
888 887 e95bcfa18a35
889 888
890 889 Test that removing .hgsubstate doesn't break anything:
891 890
892 891 $ hg rm -f .hgsubstate
893 892 $ hg ci -mrm
894 893 nothing changed
895 894 [1]
896 895 $ hg log -vr tip
897 896 changeset: 13:925c17564ef8
898 897 tag: tip
899 898 user: test
900 899 date: Thu Jan 01 00:00:00 1970 +0000
901 900 files: .hgsubstate
902 901 description:
903 902 13
904 903
905 904
906 905
907 906 Test that removing .hgsub removes .hgsubstate:
908 907
909 908 $ hg rm .hgsub
910 909 $ hg ci -mrm2
911 910 created new head
912 911 $ hg log -vr tip
913 912 changeset: 14:2400bccd50af
914 913 tag: tip
915 914 parent: 11:365661e5936a
916 915 user: test
917 916 date: Thu Jan 01 00:00:00 1970 +0000
918 917 files: .hgsub .hgsubstate
919 918 description:
920 919 rm2
921 920
922 921
923 922 Test issue3153: diff -S with deleted subrepos
924 923
925 924 $ hg diff --nodates -S -c .
926 925 diff -r 365661e5936a -r 2400bccd50af .hgsub
927 926 --- a/.hgsub
928 927 +++ /dev/null
929 928 @@ -1,2 +0,0 @@
930 929 -s = s
931 930 -t = t
932 931 diff -r 365661e5936a -r 2400bccd50af .hgsubstate
933 932 --- a/.hgsubstate
934 933 +++ /dev/null
935 934 @@ -1,2 +0,0 @@
936 935 -fc627a69481fcbe5f1135069e8a3881c023e4cf5 s
937 936 -e95bcfa18a358dc4936da981ebf4147b4cad1362 t
938 937
939 938 Test behavior of add for explicit path in subrepo:
940 939 $ cd ..
941 940 $ hg init explicit
942 941 $ cd explicit
943 942 $ echo s = s > .hgsub
944 943 $ hg add .hgsub
945 944 $ hg init s
946 945 $ hg ci -m0
947 946 Adding with an explicit path in a subrepo adds the file
948 947 $ echo c1 > f1
949 948 $ echo c2 > s/f2
950 949 $ hg st -S
951 950 ? f1
952 951 ? s/f2
953 952 $ hg add s/f2
954 953 $ hg st -S
955 954 A s/f2
956 955 ? f1
957 956 $ hg ci -R s -m0
958 957 $ hg ci -Am1
959 958 adding f1
960 959 Adding with an explicit path in a subrepo with -S has the same behavior
961 960 $ echo c3 > f3
962 961 $ echo c4 > s/f4
963 962 $ hg st -S
964 963 ? f3
965 964 ? s/f4
966 965 $ hg add -S s/f4
967 966 $ hg st -S
968 967 A s/f4
969 968 ? f3
970 969 $ hg ci -R s -m1
971 970 $ hg ci -Ama2
972 971 adding f3
973 972 Adding without a path or pattern silently ignores subrepos
974 973 $ echo c5 > f5
975 974 $ echo c6 > s/f6
976 975 $ echo c7 > s/f7
977 976 $ hg st -S
978 977 ? f5
979 978 ? s/f6
980 979 ? s/f7
981 980 $ hg add
982 981 adding f5
983 982 $ hg st -S
984 983 A f5
985 984 ? s/f6
986 985 ? s/f7
987 986 $ hg ci -R s -Am2
988 987 adding f6
989 988 adding f7
990 989 $ hg ci -m3
991 990 Adding without a path or pattern with -S also adds files in subrepos
992 991 $ echo c8 > f8
993 992 $ echo c9 > s/f9
994 993 $ echo c10 > s/f10
995 994 $ hg st -S
996 995 ? f8
997 996 ? s/f10
998 997 ? s/f9
999 998 $ hg add -S
1000 999 adding f8
1001 1000 adding s/f10 (glob)
1002 1001 adding s/f9 (glob)
1003 1002 $ hg st -S
1004 1003 A f8
1005 1004 A s/f10
1006 1005 A s/f9
1007 1006 $ hg ci -R s -m3
1008 1007 $ hg ci -m4
1009 1008 Adding with a pattern silently ignores subrepos
1010 1009 $ echo c11 > fm11
1011 1010 $ echo c12 > fn12
1012 1011 $ echo c13 > s/fm13
1013 1012 $ echo c14 > s/fn14
1014 1013 $ hg st -S
1015 1014 ? fm11
1016 1015 ? fn12
1017 1016 ? s/fm13
1018 1017 ? s/fn14
1019 1018 $ hg add 'glob:**fm*'
1020 1019 adding fm11
1021 1020 $ hg st -S
1022 1021 A fm11
1023 1022 ? fn12
1024 1023 ? s/fm13
1025 1024 ? s/fn14
1026 1025 $ hg ci -R s -Am4
1027 1026 adding fm13
1028 1027 adding fn14
1029 1028 $ hg ci -Am5
1030 1029 adding fn12
1031 1030 Adding with a pattern with -S also adds matches in subrepos
1032 1031 $ echo c15 > fm15
1033 1032 $ echo c16 > fn16
1034 1033 $ echo c17 > s/fm17
1035 1034 $ echo c18 > s/fn18
1036 1035 $ hg st -S
1037 1036 ? fm15
1038 1037 ? fn16
1039 1038 ? s/fm17
1040 1039 ? s/fn18
1041 1040 $ hg add -S 'glob:**fm*'
1042 1041 adding fm15
1043 1042 adding s/fm17 (glob)
1044 1043 $ hg st -S
1045 1044 A fm15
1046 1045 A s/fm17
1047 1046 ? fn16
1048 1047 ? s/fn18
1049 1048 $ hg ci -R s -Am5
1050 1049 adding fn18
1051 1050 $ hg ci -Am6
1052 1051 adding fn16
1053 1052
1054 1053 Test behavior of forget for explicit path in subrepo:
1055 1054 Forgetting an explicit path in a subrepo untracks the file
1056 1055 $ echo c19 > s/f19
1057 1056 $ hg add s/f19
1058 1057 $ hg st -S
1059 1058 A s/f19
1060 1059 $ hg forget s/f19
1061 1060 $ hg st -S
1062 1061 ? s/f19
1063 1062 $ rm s/f19
1064 1063 $ cd ..
1065 1064
1066 1065 Courtesy phases synchronisation to publishing server does not block the push
1067 1066 (issue3781)
1068 1067
1069 1068 $ cp -r main issue3781
1070 1069 $ cp -r main issue3781-dest
1071 1070 $ cd issue3781-dest/s
1072 1071 $ hg phase tip # show we have draft changeset
1073 1072 5: draft
1074 1073 $ chmod a-w .hg/store/phaseroots # prevent phase push
1075 1074 $ cd ../../issue3781
1076 1075 $ cat >> .hg/hgrc << EOF
1077 1076 > [paths]
1078 1077 > default=../issue3781-dest/
1079 1078 > EOF
1080 1079 $ hg push
1081 1080 pushing to $TESTTMP/issue3781-dest (glob)
1082 1081 pushing subrepo s to $TESTTMP/issue3781-dest/s
1083 1082 searching for changes
1084 1083 no changes found
1085 1084 searching for changes
1086 1085 no changes found
1087 1086 [1]
1088 1087
General Comments 0
You need to be logged in to leave comments. Login now