##// END OF EJS Templates
subrepo: correctly handle update -C with modified subrepos (issue2022)...
Matt Mackall -
r11470:34e33d50 stable
parent child Browse files
Show More
@@ -1,527 +1,527
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 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._read()
18 18 def reset(self, node=None):
19 19 self._state = {}
20 20 if node:
21 21 self._local = node
22 22 shutil.rmtree(self._repo.join("merge"), True)
23 23 def _read(self):
24 24 self._state = {}
25 25 try:
26 26 f = self._repo.opener("merge/state")
27 27 for i, l in enumerate(f):
28 28 if i == 0:
29 29 self._local = bin(l[:-1])
30 30 else:
31 31 bits = l[:-1].split("\0")
32 32 self._state[bits[0]] = bits[1:]
33 33 except IOError, err:
34 34 if err.errno != errno.ENOENT:
35 35 raise
36 36 def _write(self):
37 37 f = self._repo.opener("merge/state", "w")
38 38 f.write(hex(self._local) + "\n")
39 39 for d, v in self._state.iteritems():
40 40 f.write("\0".join([d] + v) + "\n")
41 41 def add(self, fcl, fco, fca, fd, flags):
42 42 hash = util.sha1(fcl.path()).hexdigest()
43 43 self._repo.opener("merge/" + hash, "w").write(fcl.data())
44 44 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
45 45 hex(fca.filenode()), fco.path(), flags]
46 46 self._write()
47 47 def __contains__(self, dfile):
48 48 return dfile in self._state
49 49 def __getitem__(self, dfile):
50 50 return self._state[dfile][0]
51 51 def __iter__(self):
52 52 l = self._state.keys()
53 53 l.sort()
54 54 for f in l:
55 55 yield f
56 56 def mark(self, dfile, state):
57 57 self._state[dfile][0] = state
58 58 self._write()
59 59 def resolve(self, dfile, wctx, octx):
60 60 if self[dfile] == 'r':
61 61 return 0
62 62 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
63 63 f = self._repo.opener("merge/" + hash)
64 64 self._repo.wwrite(dfile, f.read(), flags)
65 65 fcd = wctx[dfile]
66 66 fco = octx[ofile]
67 67 fca = self._repo.filectx(afile, fileid=anode)
68 68 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
69 69 if not r:
70 70 self.mark(dfile, 'r')
71 71 return r
72 72
73 73 def _checkunknown(wctx, mctx):
74 74 "check for collisions between unknown files and files in mctx"
75 75 for f in wctx.unknown():
76 76 if f in mctx and mctx[f].cmp(wctx[f].data()):
77 77 raise util.Abort(_("untracked file in working directory differs"
78 78 " from file in requested revision: '%s'") % f)
79 79
80 80 def _checkcollision(mctx):
81 81 "check for case folding collisions in the destination context"
82 82 folded = {}
83 83 for fn in mctx:
84 84 fold = fn.lower()
85 85 if fold in folded:
86 86 raise util.Abort(_("case-folding collision between %s and %s")
87 87 % (fn, folded[fold]))
88 88 folded[fold] = fn
89 89
90 90 def _forgetremoved(wctx, mctx, branchmerge):
91 91 """
92 92 Forget removed files
93 93
94 94 If we're jumping between revisions (as opposed to merging), and if
95 95 neither the working directory nor the target rev has the file,
96 96 then we need to remove it from the dirstate, to prevent the
97 97 dirstate from listing the file when it is no longer in the
98 98 manifest.
99 99
100 100 If we're merging, and the other revision has removed a file
101 101 that is not present in the working directory, we need to mark it
102 102 as removed.
103 103 """
104 104
105 105 action = []
106 106 state = branchmerge and 'r' or 'f'
107 107 for f in wctx.deleted():
108 108 if f not in mctx:
109 109 action.append((f, state))
110 110
111 111 if not branchmerge:
112 112 for f in wctx.removed():
113 113 if f not in mctx:
114 114 action.append((f, "f"))
115 115
116 116 return action
117 117
118 118 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
119 119 """
120 120 Merge p1 and p2 with ancestor ma and generate merge action list
121 121
122 122 overwrite = whether we clobber working files
123 123 partial = function to filter file lists
124 124 """
125 125
126 126 def fmerge(f, f2, fa):
127 127 """merge flags"""
128 128 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
129 129 if m == n: # flags agree
130 130 return m # unchanged
131 131 if m and n and not a: # flags set, don't agree, differ from parent
132 132 r = repo.ui.promptchoice(
133 133 _(" conflicting flags for %s\n"
134 134 "(n)one, e(x)ec or sym(l)ink?") % f,
135 135 (_("&None"), _("E&xec"), _("Sym&link")), 0)
136 136 if r == 1:
137 137 return "x" # Exec
138 138 if r == 2:
139 139 return "l" # Symlink
140 140 return ""
141 141 if m and m != a: # changed from a to m
142 142 return m
143 143 if n and n != a: # changed from a to n
144 144 return n
145 145 return '' # flag was cleared
146 146
147 147 def act(msg, m, f, *args):
148 148 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
149 149 action.append((f, m) + args)
150 150
151 151 action, copy = [], {}
152 152
153 153 if overwrite:
154 154 pa = p1
155 155 elif pa == p2: # backwards
156 156 pa = p1.p1()
157 157 elif pa and repo.ui.configbool("merge", "followcopies", True):
158 158 dirs = repo.ui.configbool("merge", "followdirs", True)
159 159 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
160 160 for of, fl in diverge.iteritems():
161 161 act("divergent renames", "dr", of, fl)
162 162
163 163 repo.ui.note(_("resolving manifests\n"))
164 164 repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
165 165 repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))
166 166
167 167 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
168 168 copied = set(copy.values())
169 169
170 if not overwrite and '.hgsubstate' in m1:
170 if '.hgsubstate' in m1:
171 171 # check whether sub state is modified
172 172 for s in p1.substate:
173 173 if p1.sub(s).dirty():
174 174 m1['.hgsubstate'] += "+"
175 175 break
176 176
177 177 # Compare manifests
178 178 for f, n in m1.iteritems():
179 179 if partial and not partial(f):
180 180 continue
181 181 if f in m2:
182 182 rflags = fmerge(f, f, f)
183 183 a = ma.get(f, nullid)
184 184 if n == m2[f] or m2[f] == a: # same or local newer
185 185 # is file locally modified or flags need changing?
186 186 # dirstate flags may need to be made current
187 187 if m1.flags(f) != rflags or n[20:]:
188 188 act("update permissions", "e", f, rflags)
189 189 elif n == a: # remote newer
190 190 act("remote is newer", "g", f, rflags)
191 191 else: # both changed
192 192 act("versions differ", "m", f, f, f, rflags, False)
193 193 elif f in copied: # files we'll deal with on m2 side
194 194 pass
195 195 elif f in copy:
196 196 f2 = copy[f]
197 197 if f2 not in m2: # directory rename
198 198 act("remote renamed directory to " + f2, "d",
199 199 f, None, f2, m1.flags(f))
200 200 else: # case 2 A,B/B/B or case 4,21 A/B/B
201 201 act("local copied/moved to " + f2, "m",
202 202 f, f2, f, fmerge(f, f2, f2), False)
203 203 elif f in ma: # clean, a different, no remote
204 204 if n != ma[f]:
205 205 if repo.ui.promptchoice(
206 206 _(" local changed %s which remote deleted\n"
207 207 "use (c)hanged version or (d)elete?") % f,
208 208 (_("&Changed"), _("&Delete")), 0):
209 209 act("prompt delete", "r", f)
210 210 else:
211 211 act("prompt keep", "a", f)
212 212 elif n[20:] == "a": # added, no remote
213 213 act("remote deleted", "f", f)
214 214 elif n[20:] != "u":
215 215 act("other deleted", "r", f)
216 216
217 217 for f, n in m2.iteritems():
218 218 if partial and not partial(f):
219 219 continue
220 220 if f in m1 or f in copied: # files already visited
221 221 continue
222 222 if f in copy:
223 223 f2 = copy[f]
224 224 if f2 not in m1: # directory rename
225 225 act("local renamed directory to " + f2, "d",
226 226 None, f, f2, m2.flags(f))
227 227 elif f2 in m2: # rename case 1, A/A,B/A
228 228 act("remote copied to " + f, "m",
229 229 f2, f, f, fmerge(f2, f, f2), False)
230 230 else: # case 3,20 A/B/A
231 231 act("remote moved to " + f, "m",
232 232 f2, f, f, fmerge(f2, f, f2), True)
233 233 elif f not in ma:
234 234 act("remote created", "g", f, m2.flags(f))
235 235 elif n != ma[f]:
236 236 if repo.ui.promptchoice(
237 237 _("remote changed %s which local deleted\n"
238 238 "use (c)hanged version or leave (d)eleted?") % f,
239 239 (_("&Changed"), _("&Deleted")), 0) == 0:
240 240 act("prompt recreating", "g", f, m2.flags(f))
241 241
242 242 return action
243 243
244 244 def actionkey(a):
245 245 return a[1] == 'r' and -1 or 0, a
246 246
247 247 def applyupdates(repo, action, wctx, mctx, actx):
248 248 """apply the merge action list to the working directory
249 249
250 250 wctx is the working copy context
251 251 mctx is the context to be merged into the working copy
252 252 actx is the context of the common ancestor
253 253 """
254 254
255 255 updated, merged, removed, unresolved = 0, 0, 0, 0
256 256 ms = mergestate(repo)
257 257 ms.reset(wctx.parents()[0].node())
258 258 moves = []
259 259 action.sort(key=actionkey)
260 260 substate = wctx.substate # prime
261 261
262 262 # prescan for merges
263 263 u = repo.ui
264 264 for a in action:
265 265 f, m = a[:2]
266 266 if m == 'm': # merge
267 267 f2, fd, flags, move = a[2:]
268 268 if f == '.hgsubstate': # merged internally
269 269 continue
270 270 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
271 271 fcl = wctx[f]
272 272 fco = mctx[f2]
273 273 fca = fcl.ancestor(fco, actx) or repo.filectx(f, fileid=nullrev)
274 274 ms.add(fcl, fco, fca, fd, flags)
275 275 if f != fd and move:
276 276 moves.append(f)
277 277
278 278 # remove renamed files after safely stored
279 279 for f in moves:
280 280 if util.lexists(repo.wjoin(f)):
281 281 repo.ui.debug("removing %s\n" % f)
282 282 os.unlink(repo.wjoin(f))
283 283
284 284 audit_path = util.path_auditor(repo.root)
285 285
286 286 numupdates = len(action)
287 287 for i, a in enumerate(action):
288 288 f, m = a[:2]
289 289 u.progress('update', i + 1, item=f, total=numupdates, unit='files')
290 290 if f and f[0] == "/":
291 291 continue
292 292 if m == "r": # remove
293 293 repo.ui.note(_("removing %s\n") % f)
294 294 audit_path(f)
295 295 if f == '.hgsubstate': # subrepo states need updating
296 296 subrepo.submerge(repo, wctx, mctx, wctx)
297 297 try:
298 298 util.unlink(repo.wjoin(f))
299 299 except OSError, inst:
300 300 if inst.errno != errno.ENOENT:
301 301 repo.ui.warn(_("update failed to remove %s: %s!\n") %
302 302 (f, inst.strerror))
303 303 removed += 1
304 304 elif m == "m": # merge
305 305 if f == '.hgsubstate': # subrepo states need updating
306 306 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
307 307 continue
308 308 f2, fd, flags, move = a[2:]
309 309 r = ms.resolve(fd, wctx, mctx)
310 310 if r is not None and r > 0:
311 311 unresolved += 1
312 312 else:
313 313 if r is None:
314 314 updated += 1
315 315 else:
316 316 merged += 1
317 317 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
318 318 if f != fd and move and util.lexists(repo.wjoin(f)):
319 319 repo.ui.debug("removing %s\n" % f)
320 320 os.unlink(repo.wjoin(f))
321 321 elif m == "g": # get
322 322 flags = a[2]
323 323 repo.ui.note(_("getting %s\n") % f)
324 324 t = mctx.filectx(f).data()
325 325 repo.wwrite(f, t, flags)
326 326 updated += 1
327 327 if f == '.hgsubstate': # subrepo states need updating
328 328 subrepo.submerge(repo, wctx, mctx, wctx)
329 329 elif m == "d": # directory rename
330 330 f2, fd, flags = a[2:]
331 331 if f:
332 332 repo.ui.note(_("moving %s to %s\n") % (f, fd))
333 333 t = wctx.filectx(f).data()
334 334 repo.wwrite(fd, t, flags)
335 335 util.unlink(repo.wjoin(f))
336 336 if f2:
337 337 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
338 338 t = mctx.filectx(f2).data()
339 339 repo.wwrite(fd, t, flags)
340 340 updated += 1
341 341 elif m == "dr": # divergent renames
342 342 fl = a[2]
343 343 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
344 344 for nf in fl:
345 345 repo.ui.warn(" %s\n" % nf)
346 346 elif m == "e": # exec
347 347 flags = a[2]
348 348 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
349 349 u.progress('update', None, total=numupdates, unit='files')
350 350
351 351 return updated, merged, removed, unresolved
352 352
353 353 def recordupdates(repo, action, branchmerge):
354 354 "record merge actions to the dirstate"
355 355
356 356 for a in action:
357 357 f, m = a[:2]
358 358 if m == "r": # remove
359 359 if branchmerge:
360 360 repo.dirstate.remove(f)
361 361 else:
362 362 repo.dirstate.forget(f)
363 363 elif m == "a": # re-add
364 364 if not branchmerge:
365 365 repo.dirstate.add(f)
366 366 elif m == "f": # forget
367 367 repo.dirstate.forget(f)
368 368 elif m == "e": # exec change
369 369 repo.dirstate.normallookup(f)
370 370 elif m == "g": # get
371 371 if branchmerge:
372 372 repo.dirstate.otherparent(f)
373 373 else:
374 374 repo.dirstate.normal(f)
375 375 elif m == "m": # merge
376 376 f2, fd, flag, move = a[2:]
377 377 if branchmerge:
378 378 # We've done a branch merge, mark this file as merged
379 379 # so that we properly record the merger later
380 380 repo.dirstate.merge(fd)
381 381 if f != f2: # copy/rename
382 382 if move:
383 383 repo.dirstate.remove(f)
384 384 if f != fd:
385 385 repo.dirstate.copy(f, fd)
386 386 else:
387 387 repo.dirstate.copy(f2, fd)
388 388 else:
389 389 # We've update-merged a locally modified file, so
390 390 # we set the dirstate to emulate a normal checkout
391 391 # of that file some time in the past. Thus our
392 392 # merge will appear as a normal local file
393 393 # modification.
394 394 if f2 == fd: # file not locally copied/moved
395 395 repo.dirstate.normallookup(fd)
396 396 if move:
397 397 repo.dirstate.forget(f)
398 398 elif m == "d": # directory rename
399 399 f2, fd, flag = a[2:]
400 400 if not f2 and f not in repo.dirstate:
401 401 # untracked file moved
402 402 continue
403 403 if branchmerge:
404 404 repo.dirstate.add(fd)
405 405 if f:
406 406 repo.dirstate.remove(f)
407 407 repo.dirstate.copy(f, fd)
408 408 if f2:
409 409 repo.dirstate.copy(f2, fd)
410 410 else:
411 411 repo.dirstate.normal(fd)
412 412 if f:
413 413 repo.dirstate.forget(f)
414 414
415 415 def update(repo, node, branchmerge, force, partial):
416 416 """
417 417 Perform a merge between the working directory and the given node
418 418
419 419 node = the node to update to, or None if unspecified
420 420 branchmerge = whether to merge between branches
421 421 force = whether to force branch merging or file overwriting
422 422 partial = a function to filter file lists (dirstate not updated)
423 423
424 424 The table below shows all the behaviors of the update command
425 425 given the -c and -C or no options, whether the working directory
426 426 is dirty, whether a revision is specified, and the relationship of
427 427 the parent rev to the target rev (linear, on the same named
428 428 branch, or on another named branch).
429 429
430 430 This logic is tested by test-update-branches.
431 431
432 432 -c -C dirty rev | linear same cross
433 433 n n n n | ok (1) x
434 434 n n n y | ok ok ok
435 435 n n y * | merge (2) (2)
436 436 n y * * | --- discard ---
437 437 y n y * | --- (3) ---
438 438 y n n * | --- ok ---
439 439 y y * * | --- (4) ---
440 440
441 441 x = can't happen
442 442 * = don't-care
443 443 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
444 444 2 = abort: crosses branches (use 'hg merge' to merge or
445 445 use 'hg update -C' to discard changes)
446 446 3 = abort: uncommitted local changes
447 447 4 = incompatible options (checked in commands.py)
448 448 """
449 449
450 450 onode = node
451 451 wlock = repo.wlock()
452 452 try:
453 453 wc = repo[None]
454 454 if node is None:
455 455 # tip of current branch
456 456 try:
457 457 node = repo.branchtags()[wc.branch()]
458 458 except KeyError:
459 459 if wc.branch() == "default": # no default branch!
460 460 node = repo.lookup("tip") # update to tip
461 461 else:
462 462 raise util.Abort(_("branch %s not found") % wc.branch())
463 463 overwrite = force and not branchmerge
464 464 pl = wc.parents()
465 465 p1, p2 = pl[0], repo[node]
466 466 pa = p1.ancestor(p2)
467 467 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
468 468 fastforward = False
469 469
470 470 ### check phase
471 471 if not overwrite and len(pl) > 1:
472 472 raise util.Abort(_("outstanding uncommitted merges"))
473 473 if branchmerge:
474 474 if pa == p2:
475 475 raise util.Abort(_("merging with a working directory ancestor"
476 476 " has no effect"))
477 477 elif pa == p1:
478 478 if p1.branch() != p2.branch():
479 479 fastforward = True
480 480 else:
481 481 raise util.Abort(_("nothing to merge (use 'hg update'"
482 482 " or check 'hg heads')"))
483 483 if not force and (wc.files() or wc.deleted()):
484 484 raise util.Abort(_("outstanding uncommitted changes "
485 485 "(use 'hg status' to list changes)"))
486 486 elif not overwrite:
487 487 if pa == p1 or pa == p2: # linear
488 488 pass # all good
489 489 elif wc.files() or wc.deleted():
490 490 raise util.Abort(_("crosses branches (use 'hg merge' to merge "
491 491 "or use 'hg update -C' to discard changes)"))
492 492 elif onode is None:
493 493 raise util.Abort(_("crosses branches (use 'hg merge' or use "
494 494 "'hg update -c')"))
495 495 else:
496 496 # Allow jumping branches if clean and specific rev given
497 497 overwrite = True
498 498
499 499 ### calculate phase
500 500 action = []
501 501 wc.status(unknown=True) # prime cache
502 502 if not force:
503 503 _checkunknown(wc, p2)
504 504 if not util.checkcase(repo.path):
505 505 _checkcollision(p2)
506 506 action += _forgetremoved(wc, p2, branchmerge)
507 507 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
508 508
509 509 ### apply phase
510 510 if not branchmerge: # just jump to the new rev
511 511 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
512 512 if not partial:
513 513 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
514 514
515 515 stats = applyupdates(repo, action, wc, p2, pa)
516 516
517 517 if not partial:
518 518 repo.dirstate.setparents(fp1, fp2)
519 519 recordupdates(repo, action, branchmerge)
520 520 if not branchmerge and not fastforward:
521 521 repo.dirstate.setbranch(p2.branch())
522 522 finally:
523 523 wlock.release()
524 524
525 525 if not partial:
526 526 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
527 527 return stats
@@ -1,390 +1,392
1 1 # subrepo.py - sub-repository handling for Mercurial
2 2 #
3 3 # Copyright 2009-2010 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 errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
9 9 from i18n import _
10 10 import config, util, node, error
11 11 hg = None
12 12
13 13 nullstate = ('', '', 'empty')
14 14
15 15 def state(ctx):
16 16 p = config.config()
17 17 def read(f, sections=None, remap=None):
18 18 if f in ctx:
19 19 p.parse(f, ctx[f].data(), sections, remap, read)
20 20 else:
21 21 raise util.Abort(_("subrepo spec file %s not found") % f)
22 22
23 23 if '.hgsub' in ctx:
24 24 read('.hgsub')
25 25
26 26 rev = {}
27 27 if '.hgsubstate' in ctx:
28 28 try:
29 29 for l in ctx['.hgsubstate'].data().splitlines():
30 30 revision, path = l.split(" ", 1)
31 31 rev[path] = revision
32 32 except IOError, err:
33 33 if err.errno != errno.ENOENT:
34 34 raise
35 35
36 36 state = {}
37 37 for path, src in p[''].items():
38 38 kind = 'hg'
39 39 if src.startswith('['):
40 40 if ']' not in src:
41 41 raise util.Abort(_('missing ] in subrepo source'))
42 42 kind, src = src.split(']', 1)
43 43 kind = kind[1:]
44 44 state[path] = (src.strip(), rev.get(path, ''), kind)
45 45
46 46 return state
47 47
48 48 def writestate(repo, state):
49 49 repo.wwrite('.hgsubstate',
50 50 ''.join(['%s %s\n' % (state[s][1], s)
51 51 for s in sorted(state)]), '')
52 52
53 53 def submerge(repo, wctx, mctx, actx):
54 54 # working context, merging context, ancestor context
55 55 if mctx == actx: # backwards?
56 56 actx = wctx.p1()
57 57 s1 = wctx.substate
58 58 s2 = mctx.substate
59 59 sa = actx.substate
60 60 sm = {}
61 61
62 62 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
63 63
64 64 def debug(s, msg, r=""):
65 65 if r:
66 66 r = "%s:%s:%s" % r
67 67 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
68 68
69 69 for s, l in s1.items():
70 a = sa.get(s, nullstate)
70 71 ld = l # local state with possible dirty flag for compares
71 if wctx != actx and wctx.sub(s).dirty():
72 if wctx.sub(s).dirty():
72 73 ld = (l[0], l[1] + "+")
74 if wctx == actx: # overwrite
75 a = ld
73 76
74 a = sa.get(s, nullstate)
75 77 if s in s2:
76 78 r = s2[s]
77 79 if ld == r or r == a: # no change or local is newer
78 80 sm[s] = l
79 81 continue
80 82 elif ld == a: # other side changed
81 83 debug(s, "other changed, get", r)
82 84 wctx.sub(s).get(r)
83 85 sm[s] = r
84 86 elif ld[0] != r[0]: # sources differ
85 87 if repo.ui.promptchoice(
86 88 _(' subrepository sources for %s differ\n'
87 89 'use (l)ocal source (%s) or (r)emote source (%s)?')
88 90 % (s, l[0], r[0]),
89 91 (_('&Local'), _('&Remote')), 0):
90 92 debug(s, "prompt changed, get", r)
91 93 wctx.sub(s).get(r)
92 94 sm[s] = r
93 95 elif ld[1] == a[1]: # local side is unchanged
94 96 debug(s, "other side changed, get", r)
95 97 wctx.sub(s).get(r)
96 98 sm[s] = r
97 99 else:
98 100 debug(s, "both sides changed, merge with", r)
99 101 wctx.sub(s).merge(r)
100 102 sm[s] = l
101 103 elif ld == a: # remote removed, local unchanged
102 104 debug(s, "remote removed, remove")
103 105 wctx.sub(s).remove()
104 106 else:
105 107 if repo.ui.promptchoice(
106 108 _(' local changed subrepository %s which remote removed\n'
107 109 'use (c)hanged version or (d)elete?') % s,
108 110 (_('&Changed'), _('&Delete')), 0):
109 111 debug(s, "prompt remove")
110 112 wctx.sub(s).remove()
111 113
112 114 for s, r in s2.items():
113 115 if s in s1:
114 116 continue
115 117 elif s not in sa:
116 118 debug(s, "remote added, get", r)
117 119 mctx.sub(s).get(r)
118 120 sm[s] = r
119 121 elif r != sa[s]:
120 122 if repo.ui.promptchoice(
121 123 _(' remote changed subrepository %s which local removed\n'
122 124 'use (c)hanged version or (d)elete?') % s,
123 125 (_('&Changed'), _('&Delete')), 0) == 0:
124 126 debug(s, "prompt recreate", r)
125 127 wctx.sub(s).get(r)
126 128 sm[s] = r
127 129
128 130 # record merged .hgsubstate
129 131 writestate(repo, sm)
130 132
131 133 def relpath(sub):
132 134 if not hasattr(sub, '_repo'):
133 135 return sub._path
134 136 parent = sub._repo
135 137 while hasattr(parent, '_subparent'):
136 138 parent = parent._subparent
137 139 return sub._repo.root[len(parent.root)+1:]
138 140
139 141 def _abssource(repo, push=False):
140 142 if hasattr(repo, '_subparent'):
141 143 source = repo._subsource
142 144 if source.startswith('/') or '://' in source:
143 145 return source
144 146 parent = _abssource(repo._subparent, push)
145 147 if '://' in parent:
146 148 if parent[-1] == '/':
147 149 parent = parent[:-1]
148 150 r = urlparse.urlparse(parent + '/' + source)
149 151 r = urlparse.urlunparse((r[0], r[1],
150 152 posixpath.normpath(r[2]),
151 153 r[3], r[4], r[5]))
152 154 return r
153 155 return posixpath.normpath(os.path.join(parent, repo._subsource))
154 156 if push and repo.ui.config('paths', 'default-push'):
155 157 return repo.ui.config('paths', 'default-push', repo.root)
156 158 return repo.ui.config('paths', 'default', repo.root)
157 159
158 160 def subrepo(ctx, path):
159 161 # subrepo inherently violates our import layering rules
160 162 # because it wants to make repo objects from deep inside the stack
161 163 # so we manually delay the circular imports to not break
162 164 # scripts that don't use our demand-loading
163 165 global hg
164 166 import hg as h
165 167 hg = h
166 168
167 169 util.path_auditor(ctx._repo.root)(path)
168 170 state = ctx.substate.get(path, nullstate)
169 171 if state[2] not in types:
170 172 raise util.Abort(_('unknown subrepo type %s') % state[2])
171 173 return types[state[2]](ctx, path, state[:2])
172 174
173 175 # subrepo classes need to implement the following methods:
174 176 # __init__(self, ctx, path, state)
175 177 # dirty(self): returns true if the dirstate of the subrepo
176 178 # does not match current stored state
177 179 # commit(self, text, user, date): commit the current changes
178 180 # to the subrepo with the given log message. Use given
179 181 # user and date if possible. Return the new state of the subrepo.
180 182 # remove(self): remove the subrepo (should verify the dirstate
181 183 # is not dirty first)
182 184 # get(self, state): run whatever commands are needed to put the
183 185 # subrepo into this state
184 186 # merge(self, state): merge currently-saved state with the new state.
185 187 # push(self, force): perform whatever action is analagous to 'hg push'
186 188 # This may be a no-op on some systems.
187 189
188 190 class hgsubrepo(object):
189 191 def __init__(self, ctx, path, state):
190 192 self._path = path
191 193 self._state = state
192 194 r = ctx._repo
193 195 root = r.wjoin(path)
194 196 create = False
195 197 if not os.path.exists(os.path.join(root, '.hg')):
196 198 create = True
197 199 util.makedirs(root)
198 200 self._repo = hg.repository(r.ui, root, create=create)
199 201 self._repo._subparent = r
200 202 self._repo._subsource = state[0]
201 203
202 204 if create:
203 205 fp = self._repo.opener("hgrc", "w", text=True)
204 206 fp.write('[paths]\n')
205 207
206 208 def addpathconfig(key, value):
207 209 fp.write('%s = %s\n' % (key, value))
208 210 self._repo.ui.setconfig('paths', key, value)
209 211
210 212 defpath = _abssource(self._repo)
211 213 defpushpath = _abssource(self._repo, True)
212 214 addpathconfig('default', defpath)
213 215 if defpath != defpushpath:
214 216 addpathconfig('default-push', defpushpath)
215 217 fp.close()
216 218
217 219 def dirty(self):
218 220 r = self._state[1]
219 221 if r == '':
220 222 return True
221 223 w = self._repo[None]
222 224 if w.p1() != self._repo[r]: # version checked out change
223 225 return True
224 226 return w.dirty() # working directory changed
225 227
226 228 def commit(self, text, user, date):
227 229 self._repo.ui.debug("committing subrepo %s\n" % relpath(self))
228 230 n = self._repo.commit(text, user, date)
229 231 if not n:
230 232 return self._repo['.'].hex() # different version checked out
231 233 return node.hex(n)
232 234
233 235 def remove(self):
234 236 # we can't fully delete the repository as it may contain
235 237 # local-only history
236 238 self._repo.ui.note(_('removing subrepo %s\n') % relpath(self))
237 239 hg.clean(self._repo, node.nullid, False)
238 240
239 241 def _get(self, state):
240 242 source, revision, kind = state
241 243 try:
242 244 self._repo.lookup(revision)
243 245 except error.RepoError:
244 246 self._repo._subsource = source
245 247 srcurl = _abssource(self._repo)
246 248 self._repo.ui.status(_('pulling subrepo %s from %s\n')
247 249 % (relpath(self), srcurl))
248 250 other = hg.repository(self._repo.ui, srcurl)
249 251 self._repo.pull(other)
250 252
251 253 def get(self, state):
252 254 self._get(state)
253 255 source, revision, kind = state
254 256 self._repo.ui.debug("getting subrepo %s\n" % self._path)
255 257 hg.clean(self._repo, revision, False)
256 258
257 259 def merge(self, state):
258 260 self._get(state)
259 261 cur = self._repo['.']
260 262 dst = self._repo[state[1]]
261 263 anc = dst.ancestor(cur)
262 264 if anc == cur:
263 265 self._repo.ui.debug("updating subrepo %s\n" % relpath(self))
264 266 hg.update(self._repo, state[1])
265 267 elif anc == dst:
266 268 self._repo.ui.debug("skipping subrepo %s\n" % relpath(self))
267 269 else:
268 270 self._repo.ui.debug("merging subrepo %s\n" % relpath(self))
269 271 hg.merge(self._repo, state[1], remind=False)
270 272
271 273 def push(self, force):
272 274 # push subrepos depth-first for coherent ordering
273 275 c = self._repo['']
274 276 subs = c.substate # only repos that are committed
275 277 for s in sorted(subs):
276 278 if not c.sub(s).push(force):
277 279 return False
278 280
279 281 dsturl = _abssource(self._repo, True)
280 282 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
281 283 (relpath(self), dsturl))
282 284 other = hg.repository(self._repo.ui, dsturl)
283 285 return self._repo.push(other, force)
284 286
285 287 class svnsubrepo(object):
286 288 def __init__(self, ctx, path, state):
287 289 self._path = path
288 290 self._state = state
289 291 self._ctx = ctx
290 292 self._ui = ctx._repo.ui
291 293
292 294 def _svncommand(self, commands):
293 295 path = os.path.join(self._ctx._repo.origroot, self._path)
294 296 cmd = ['svn'] + commands + [path]
295 297 cmd = [util.shellquote(arg) for arg in cmd]
296 298 cmd = util.quotecommand(' '.join(cmd))
297 299 env = dict(os.environ)
298 300 # Avoid localized output, preserve current locale for everything else.
299 301 env['LC_MESSAGES'] = 'C'
300 302 write, read, err = util.popen3(cmd, env=env, newlines=True)
301 303 retdata = read.read()
302 304 err = err.read().strip()
303 305 if err:
304 306 raise util.Abort(err)
305 307 return retdata
306 308
307 309 def _wcrev(self):
308 310 output = self._svncommand(['info', '--xml'])
309 311 doc = xml.dom.minidom.parseString(output)
310 312 entries = doc.getElementsByTagName('entry')
311 313 if not entries:
312 314 return 0
313 315 return int(entries[0].getAttribute('revision') or 0)
314 316
315 317 def _wcchanged(self):
316 318 """Return (changes, extchanges) where changes is True
317 319 if the working directory was changed, and extchanges is
318 320 True if any of these changes concern an external entry.
319 321 """
320 322 output = self._svncommand(['status', '--xml'])
321 323 externals, changes = [], []
322 324 doc = xml.dom.minidom.parseString(output)
323 325 for e in doc.getElementsByTagName('entry'):
324 326 s = e.getElementsByTagName('wc-status')
325 327 if not s:
326 328 continue
327 329 item = s[0].getAttribute('item')
328 330 props = s[0].getAttribute('props')
329 331 path = e.getAttribute('path')
330 332 if item == 'external':
331 333 externals.append(path)
332 334 if (item not in ('', 'normal', 'unversioned', 'external')
333 335 or props not in ('', 'none')):
334 336 changes.append(path)
335 337 for path in changes:
336 338 for ext in externals:
337 339 if path == ext or path.startswith(ext + os.sep):
338 340 return True, True
339 341 return bool(changes), False
340 342
341 343 def dirty(self):
342 344 if self._wcrev() == self._state[1] and not self._wcchanged()[0]:
343 345 return False
344 346 return True
345 347
346 348 def commit(self, text, user, date):
347 349 # user and date are out of our hands since svn is centralized
348 350 changed, extchanged = self._wcchanged()
349 351 if not changed:
350 352 return self._wcrev()
351 353 if extchanged:
352 354 # Do not try to commit externals
353 355 raise util.Abort(_('cannot commit svn externals'))
354 356 commitinfo = self._svncommand(['commit', '-m', text])
355 357 self._ui.status(commitinfo)
356 358 newrev = re.search('Committed revision ([\d]+).', commitinfo)
357 359 if not newrev:
358 360 raise util.Abort(commitinfo.splitlines()[-1])
359 361 newrev = newrev.groups()[0]
360 362 self._ui.status(self._svncommand(['update', '-r', newrev]))
361 363 return newrev
362 364
363 365 def remove(self):
364 366 if self.dirty():
365 367 self._ui.warn(_('not removing repo %s because '
366 368 'it has changes.\n' % self._path))
367 369 return
368 370 self._ui.note(_('removing subrepo %s\n') % self._path)
369 371 shutil.rmtree(self._ctx.repo.join(self._path))
370 372
371 373 def get(self, state):
372 374 status = self._svncommand(['checkout', state[0], '--revision', state[1]])
373 375 if not re.search('Checked out revision [\d]+.', status):
374 376 raise util.Abort(status.splitlines()[-1])
375 377 self._ui.status(status)
376 378
377 379 def merge(self, state):
378 380 old = int(self._state[1])
379 381 new = int(state[1])
380 382 if new > old:
381 383 self.get(state)
382 384
383 385 def push(self, force):
384 386 # push is a no-op for SVN
385 387 return True
386 388
387 389 types = {
388 390 'hg': hgsubrepo,
389 391 'svn': svnsubrepo,
390 392 }
@@ -1,234 +1,241
1 1 #!/bin/sh
2 2
3 3 rm -rf sub
4 4 mkdir sub
5 5 cd sub
6 6 hg init t
7 7 cd t
8 8
9 9 echo % first revision, no sub
10 10 echo a > a
11 11 hg ci -Am0
12 12
13 13 echo % add first sub
14 14 echo s = s > .hgsub
15 15 hg add .hgsub
16 16 hg init s
17 17 echo a > s/a
18 18 hg -R s ci -Ams0
19 19 hg sum
20 20 hg ci -m1
21 21
22 # issue 2022 - update -C
23
24 echo b > s/a
25 hg sum
26 hg co -C 1
27 hg sum
28
22 29 echo % add sub sub
23 30 echo ss = ss > s/.hgsub
24 31 hg init s/ss
25 32 echo a > s/ss/a
26 33 hg -R s add s/.hgsub
27 34 hg -R s/ss add s/ss/a
28 35 hg sum
29 36 hg ci -m2
30 37 hg sum
31 38
32 39 echo % bump sub rev
33 40 echo b > s/a
34 41 hg -R s ci -ms1
35 42 hg ci -m3
36 43
37 44 echo % leave sub dirty
38 45 echo c > s/a
39 46 hg ci -m4
40 47 hg tip -R s
41 48
42 49 echo % check caching
43 50 hg co 0
44 51 hg debugsub
45 52 echo % restore
46 53 hg co
47 54 hg debugsub
48 55
49 56 echo % new branch for merge tests
50 57 hg co 1
51 58 echo t = t >> .hgsub
52 59 hg init t
53 60 echo t > t/t
54 61 hg -R t add t
55 62 echo % 5
56 63 hg ci -m5 # add sub
57 64 echo t2 > t/t
58 65 echo % 6
59 66 hg st -R s
60 67 hg ci -m6 # change sub
61 68 hg debugsub
62 69 echo t3 > t/t
63 70 echo % 7
64 71 hg ci -m7 # change sub again for conflict test
65 72 hg rm .hgsub
66 73 echo % 8
67 74 hg ci -m8 # remove sub
68 75
69 76 echo % merge tests
70 77 hg co -C 3
71 78 hg merge 5 # test adding
72 79 hg debugsub
73 80 hg ci -m9
74 81 hg merge 6 --debug # test change
75 82 hg debugsub
76 83 echo conflict > t/t
77 84 hg ci -m10
78 85 HGMERGE=internal:merge hg merge --debug 7 # test conflict
79 86 echo % should conflict
80 87 cat t/t
81 88
82 89 echo % clone
83 90 cd ..
84 91 hg clone t tc | sed 's|from .*/sub|from .../sub|g'
85 92 cd tc
86 93 hg debugsub
87 94
88 95 echo % push
89 96 echo bah > t/t
90 97 hg ci -m11
91 98 hg push | sed 's/ .*sub/ ...sub/g'
92 99
93 100 echo % push -f
94 101 echo bah > s/a
95 102 hg ci -m12
96 103 hg push | sed 's/ .*sub/ ...sub/g'
97 104 hg push -f | sed 's/ .*sub/ ...sub/g'
98 105
99 106 echo % update
100 107 cd ../t
101 108 hg up -C # discard our earlier merge
102 109 echo blah > t/t
103 110 hg ci -m13
104 111
105 112 echo % pull
106 113 cd ../tc
107 114 hg pull | sed 's/ .*sub/ ...sub/g'
108 115 # should pull t
109 116 hg up | sed 's|from .*/sub|from .../sub|g'
110 117 cat t/t
111 118
112 119 echo % bogus subrepo path aborts
113 120 echo 'bogus=[boguspath' >> .hgsub
114 121 hg ci -m 'bogus subrepo path'
115 122
116 123 echo % issue 1986
117 124 cd ..
118 125 rm -rf sub
119 126 hg init main
120 127 cd main
121 128
122 129 hg init s # subrepo layout
123 130 cd s #
124 131 echo a > a # o 5 br
125 132 hg ci -Am1 # /|
126 133 hg branch br # o | 4 default
127 134 echo a >> a # | |
128 135 hg ci -m1 # | o 3 br
129 136 hg up default # |/|
130 137 echo b > b # o | 2 default
131 138 hg ci -Am1 # | |
132 139 hg up br # | o 1 br
133 140 hg merge tip # |/
134 141 hg ci -m1 # o 0 default
135 142 hg up 2
136 143 echo c > c
137 144 hg ci -Am1
138 145 hg up 3
139 146 hg merge 4
140 147 hg ci -m1
141 148
142 149 cd .. # main repo layout:
143 150 echo 's = s' > .hgsub #
144 151 hg -R s up 2 # * <-- try to merge default into br again
145 152 hg ci -Am1 # .`|
146 153 hg branch br # . o 5 br --> substate = 5
147 154 echo b > b # . |
148 155 hg -R s up 3 # o | 4 default --> substate = 4
149 156 hg ci -Am1 # | |
150 157 hg up default # | o 3 br --> substate = 2
151 158 echo c > c # |/|
152 159 hg ci -Am1 # o | 2 default --> substate = 2
153 160 hg up 1 # | |
154 161 hg merge 2 # | o 1 br --> substate = 3
155 162 hg ci -m1 # |/
156 163 hg up 2 # o 0 default --> substate = 2
157 164 hg -R s up 4
158 165 echo d > d
159 166 hg ci -Am1
160 167 hg up 3
161 168 hg -R s up 5
162 169 echo e > e
163 170 hg ci -Am1
164 171
165 172 hg up 5
166 173 hg merge 4 # try to merge default into br again
167 174 cd ..
168 175
169 176 echo % test subrepo delete from .hgsubstate
170 177 hg init testdelete
171 178 mkdir testdelete/nested testdelete/nested2
172 179 hg init testdelete/nested
173 180 hg init testdelete/nested2
174 181 echo test > testdelete/nested/foo
175 182 echo test > testdelete/nested2/foo
176 183 hg -R testdelete/nested add
177 184 hg -R testdelete/nested2 add
178 185 hg -R testdelete/nested ci -m test
179 186 hg -R testdelete/nested2 ci -m test
180 187 echo nested = nested > testdelete/.hgsub
181 188 echo nested2 = nested2 >> testdelete/.hgsub
182 189 hg -R testdelete add
183 190 hg -R testdelete ci -m "nested 1 & 2 added"
184 191 echo nested = nested > testdelete/.hgsub
185 192 hg -R testdelete ci -m "nested 2 deleted"
186 193 cat testdelete/.hgsubstate | sed "s:.* ::"
187 194 hg -R testdelete remove testdelete/.hgsub
188 195 hg -R testdelete ci -m ".hgsub deleted"
189 196 cat testdelete/.hgsubstate
190 197
191 198 echo % test repository cloning
192 199 mkdir mercurial mercurial2
193 200 hg init nested_absolute
194 201 echo test > nested_absolute/foo
195 202 hg -R nested_absolute add
196 203 hg -R nested_absolute ci -mtest
197 204 cd mercurial
198 205 hg init nested_relative
199 206 echo test2 > nested_relative/foo2
200 207 hg -R nested_relative add
201 208 hg -R nested_relative ci -mtest2
202 209 hg init main
203 210 echo "nested_relative = ../nested_relative" > main/.hgsub
204 211 echo "nested_absolute = `pwd`/nested_absolute" >> main/.hgsub
205 212 hg -R main add
206 213 hg -R main ci -m "add subrepos"
207 214 cd ..
208 215 hg clone mercurial/main mercurial2/main
209 216 cat mercurial2/main/nested_absolute/.hg/hgrc \
210 217 mercurial2/main/nested_relative/.hg/hgrc \
211 218 | "$TESTDIR/filtertmp.py"
212 219 rm -rf mercurial mercurial2
213 220
214 221 echo % issue 1977
215 222 hg init repo
216 223 hg init repo/s
217 224 echo a > repo/s/a
218 225 hg -R repo/s ci -Am0
219 226 echo s = s > repo/.hgsub
220 227 hg -R repo ci -Am1
221 228 hg clone repo repo2 | sed 's|from .*/sub|from .../sub|g'
222 229 hg -q -R repo2 pull -u
223 230 echo 1 > repo2/s/a
224 231 hg -R repo2/s ci -m2
225 232 hg -q -R repo2/s push
226 233 hg -R repo2/s up -C 0
227 234 echo 2 > repo2/s/a
228 235 hg -R repo2/s ci -m3
229 236 hg -R repo2 ci -m3
230 237 hg -q -R repo2 push
231 238 hg -R repo update
232 239 rm -rf repo2 repo
233 240
234 241 exit 0
@@ -1,291 +1,302
1 1 % first revision, no sub
2 2 adding a
3 3 % add first sub
4 4 adding a
5 5 parent: 0:f7b1eb17ad24 tip
6 6 0
7 7 branch: default
8 8 commit: 1 added, 1 subrepos
9 9 update: (current)
10 10 committing subrepository s
11 parent: 1:7cf8cfea66e4 tip
12 1
13 branch: default
14 commit: 1 subrepos
15 update: (current)
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 parent: 1:7cf8cfea66e4 tip
18 1
19 branch: default
20 commit: (clean)
21 update: (current)
11 22 % add sub sub
12 23 parent: 1:7cf8cfea66e4 tip
13 24 1
14 25 branch: default
15 26 commit: 1 subrepos
16 27 update: (current)
17 28 committing subrepository s
18 29 committing subrepository s/ss
19 30 parent: 2:df30734270ae tip
20 31 2
21 32 branch: default
22 33 commit: (clean)
23 34 update: (current)
24 35 % bump sub rev
25 36 committing subrepository s
26 37 % leave sub dirty
27 38 committing subrepository s
28 39 changeset: 3:1c833a7a9e3a
29 40 tag: tip
30 41 user: test
31 42 date: Thu Jan 01 00:00:00 1970 +0000
32 43 summary: 4
33 44
34 45 % check caching
35 46 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
36 47 % restore
37 48 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 49 path s
39 50 source s
40 51 revision 1c833a7a9e3a4445c711aaf0f012379cd0d4034e
41 52 % new branch for merge tests
42 53 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 54 adding t/t
44 55 % 5
45 56 committing subrepository t
46 57 created new head
47 58 % 6
48 59 committing subrepository t
49 60 path s
50 61 source s
51 62 revision e4ece1bf43360ddc8f6a96432201a37b7cd27ae4
52 63 path t
53 64 source t
54 65 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
55 66 % 7
56 67 committing subrepository t
57 68 % 8
58 69 % merge tests
59 70 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 72 (branch merge, don't forget to commit)
62 73 path s
63 74 source s
64 75 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
65 76 path t
66 77 source t
67 78 revision 60ca1237c19474e7a3978b0dc1ca4e6f36d51382
68 79 created new head
69 80 searching for copies back to rev 2
70 81 resolving manifests
71 82 overwrite None partial False
72 83 ancestor 1f14a2e2d3ec local f0d2028bf86d+ remote 1831e14459c4
73 84 .hgsubstate: versions differ -> m
74 85 update: .hgsubstate 1/1 files (100.00%)
75 86 subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
76 87 subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
77 88 getting subrepo t
78 89 resolving manifests
79 90 overwrite True partial False
80 91 ancestor 60ca1237c194+ local 60ca1237c194+ remote 6747d179aa9a
81 92 t: remote is newer -> g
82 93 update: t 1/1 files (100.00%)
83 94 getting t
84 95 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 96 (branch merge, don't forget to commit)
86 97 path s
87 98 source s
88 99 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
89 100 path t
90 101 source t
91 102 revision 6747d179aa9a688023c4b0cad32e4c92bb7f34ad
92 103 committing subrepository t
93 104 searching for copies back to rev 2
94 105 resolving manifests
95 106 overwrite None partial False
96 107 ancestor 1831e14459c4 local e45c8b14af55+ remote f94576341bcf
97 108 .hgsubstate: versions differ -> m
98 109 update: .hgsubstate 1/1 files (100.00%)
99 110 subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4
100 111 subrepo t: both sides changed, merge with t:7af322bc1198a32402fe903e0b7ebcfc5c9bf8f4:hg
101 112 merging subrepo t
102 113 searching for copies back to rev 2
103 114 resolving manifests
104 115 overwrite None partial False
105 116 ancestor 6747d179aa9a local 20a0db6fbf6c+ remote 7af322bc1198
106 117 t: versions differ -> m
107 118 preserving t for resolve of t
108 119 update: t 1/1 files (100.00%)
109 120 picked tool 'internal:merge' for t (binary False symlink False)
110 121 merging t
111 122 my t@20a0db6fbf6c+ other t@7af322bc1198 ancestor t@6747d179aa9a
112 123 warning: conflicts during merge.
113 124 merging t failed!
114 125 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
115 126 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
116 127 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 128 (branch merge, don't forget to commit)
118 129 % should conflict
119 130 <<<<<<< local
120 131 conflict
121 132 =======
122 133 t3
123 134 >>>>>>> other
124 135 % clone
125 136 updating to branch default
126 137 pulling subrepo s from .../sub/t/s
127 138 requesting all changes
128 139 adding changesets
129 140 adding manifests
130 141 adding file changes
131 142 added 4 changesets with 5 changes to 3 files
132 143 pulling subrepo s/ss from .../sub/t/s/ss
133 144 requesting all changes
134 145 adding changesets
135 146 adding manifests
136 147 adding file changes
137 148 added 1 changesets with 1 changes to 1 files
138 149 pulling subrepo t from .../sub/t/t
139 150 requesting all changes
140 151 adding changesets
141 152 adding manifests
142 153 adding file changes
143 154 added 4 changesets with 4 changes to 1 files (+1 heads)
144 155 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 156 path s
146 157 source s
147 158 revision fc627a69481fcbe5f1135069e8a3881c023e4cf5
148 159 path t
149 160 source t
150 161 revision 20a0db6fbf6c3d2836e6519a642ae929bfc67c0e
151 162 % push
152 163 committing subrepository t
153 164 pushing ...sub/t
154 165 pushing ...sub/t/s/ss
155 166 searching for changes
156 167 no changes found
157 168 pushing ...sub/t/s
158 169 searching for changes
159 170 no changes found
160 171 pushing ...sub/t/t
161 172 searching for changes
162 173 adding changesets
163 174 adding manifests
164 175 adding file changes
165 176 added 1 changesets with 1 changes to 1 files
166 177 searching for changes
167 178 adding changesets
168 179 adding manifests
169 180 adding file changes
170 181 added 1 changesets with 1 changes to 1 files
171 182 % push -f
172 183 committing subrepository s
173 184 abort: push creates new remote heads on branch 'default'!
174 185 pushing ...sub/t
175 186 pushing ...sub/t/s/ss
176 187 searching for changes
177 188 no changes found
178 189 pushing ...sub/t/s
179 190 searching for changes
180 191 (did you forget to merge? use push -f to force)
181 192 pushing ...sub/t
182 193 pushing ...sub/t/s/ss
183 194 searching for changes
184 195 no changes found
185 196 pushing ...sub/t/s
186 197 searching for changes
187 198 adding changesets
188 199 adding manifests
189 200 adding file changes
190 201 added 1 changesets with 1 changes to 1 files (+1 heads)
191 202 pushing ...sub/t/t
192 203 searching for changes
193 204 no changes found
194 205 searching for changes
195 206 adding changesets
196 207 adding manifests
197 208 adding file changes
198 209 added 1 changesets with 1 changes to 1 files
199 210 % update
200 211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 212 committing subrepository t
202 213 % pull
203 214 pulling ...sub/t
204 215 searching for changes
205 216 adding changesets
206 217 adding manifests
207 218 adding file changes
208 219 added 1 changesets with 1 changes to 1 files
209 220 (run 'hg update' to get a working copy)
210 221 pulling subrepo t from .../sub/t/t
211 222 searching for changes
212 223 adding changesets
213 224 adding manifests
214 225 adding file changes
215 226 added 1 changesets with 1 changes to 1 files
216 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
217 228 blah
218 229 % bogus subrepo path aborts
219 230 abort: missing ] in subrepo source
220 231 % issue 1986
221 232 adding a
222 233 marked working directory as branch br
223 234 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 235 adding b
225 236 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
226 237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 238 (branch merge, don't forget to commit)
228 239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 240 adding c
230 241 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
231 242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 243 (branch merge, don't forget to commit)
233 244 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
234 245 adding .hgsub
235 246 committing subrepository s
236 247 marked working directory as branch br
237 248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 249 adding b
239 250 committing subrepository s
240 251 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
241 252 adding c
242 253 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
243 254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 255 (branch merge, don't forget to commit)
245 256 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
246 257 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 258 adding d
248 259 committing subrepository s
249 260 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
250 261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
251 262 adding e
252 263 committing subrepository s
253 264 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 266 (branch merge, don't forget to commit)
256 267 % test subrepo delete from .hgsubstate
257 268 adding testdelete/nested/foo
258 269 adding testdelete/nested2/foo
259 270 adding testdelete/.hgsub
260 271 committing subrepository nested2
261 272 committing subrepository nested
262 273 nested
263 274 % test repository cloning
264 275 adding nested_absolute/foo
265 276 adding nested_relative/foo2
266 277 adding main/.hgsub
267 278 committing subrepository nested_relative
268 279 committing subrepository nested_absolute
269 280 updating to branch default
270 281 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 282 [paths]
272 283 default = $HGTMP/test-subrepo/sub/mercurial/nested_absolute
273 284 [paths]
274 285 default = $HGTMP/test-subrepo/sub/mercurial/nested_relative
275 286 % issue 1977
276 287 adding a
277 288 adding .hgsub
278 289 committing subrepository s
279 290 updating to branch default
280 291 pulling subrepo s from .../sub/repo/s
281 292 requesting all changes
282 293 adding changesets
283 294 adding manifests
284 295 adding file changes
285 296 added 1 changesets with 1 changes to 1 files
286 297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 298 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 299 created new head
289 300 committing subrepository s
290 301 abort: push creates new remote heads on branch 'default'!
291 302 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now