##// END OF EJS Templates
outgoing: fix possible filtering crash in outgoing (issue3814)...
Pierre-Yves David -
r18617:227479f6 stable
parent child Browse files
Show More
@@ -1,990 +1,994 b''
1 1 # scmutil.py - Mercurial core utility functions
2 2 #
3 3 # Copyright 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 i18n import _
9 9 from mercurial.node import nullrev
10 10 import util, error, osutil, revset, similar, encoding, phases
11 11 import match as matchmod
12 12 import os, errno, re, stat, sys, glob
13 13
14 14 def nochangesfound(ui, repo, excluded=None):
15 15 '''Report no changes for push/pull, excluded is None or a list of
16 16 nodes excluded from the push/pull.
17 17 '''
18 18 secretlist = []
19 19 if excluded:
20 20 for n in excluded:
21 if n not in repo:
22 # discovery should not have included the filtered revision,
23 # we have to explicitly exclude it until discovery is cleanup.
24 continue
21 25 ctx = repo[n]
22 26 if ctx.phase() >= phases.secret and not ctx.extinct():
23 27 secretlist.append(n)
24 28
25 29 if secretlist:
26 30 ui.status(_("no changes found (ignored %d secret changesets)\n")
27 31 % len(secretlist))
28 32 else:
29 33 ui.status(_("no changes found\n"))
30 34
31 35 def checknewlabel(repo, lbl, kind):
32 36 if lbl in ['tip', '.', 'null']:
33 37 raise util.Abort(_("the name '%s' is reserved") % lbl)
34 38 for c in (':', '\0', '\n', '\r'):
35 39 if c in lbl:
36 40 raise util.Abort(_("%r cannot be used in a name") % c)
37 41
38 42 def checkfilename(f):
39 43 '''Check that the filename f is an acceptable filename for a tracked file'''
40 44 if '\r' in f or '\n' in f:
41 45 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
42 46
43 47 def checkportable(ui, f):
44 48 '''Check if filename f is portable and warn or abort depending on config'''
45 49 checkfilename(f)
46 50 abort, warn = checkportabilityalert(ui)
47 51 if abort or warn:
48 52 msg = util.checkwinfilename(f)
49 53 if msg:
50 54 msg = "%s: %r" % (msg, f)
51 55 if abort:
52 56 raise util.Abort(msg)
53 57 ui.warn(_("warning: %s\n") % msg)
54 58
55 59 def checkportabilityalert(ui):
56 60 '''check if the user's config requests nothing, a warning, or abort for
57 61 non-portable filenames'''
58 62 val = ui.config('ui', 'portablefilenames', 'warn')
59 63 lval = val.lower()
60 64 bval = util.parsebool(val)
61 65 abort = os.name == 'nt' or lval == 'abort'
62 66 warn = bval or lval == 'warn'
63 67 if bval is None and not (warn or abort or lval == 'ignore'):
64 68 raise error.ConfigError(
65 69 _("ui.portablefilenames value is invalid ('%s')") % val)
66 70 return abort, warn
67 71
68 72 class casecollisionauditor(object):
69 73 def __init__(self, ui, abort, dirstate):
70 74 self._ui = ui
71 75 self._abort = abort
72 76 allfiles = '\0'.join(dirstate._map)
73 77 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
74 78 self._dirstate = dirstate
75 79 # The purpose of _newfiles is so that we don't complain about
76 80 # case collisions if someone were to call this object with the
77 81 # same filename twice.
78 82 self._newfiles = set()
79 83
80 84 def __call__(self, f):
81 85 fl = encoding.lower(f)
82 86 if (fl in self._loweredfiles and f not in self._dirstate and
83 87 f not in self._newfiles):
84 88 msg = _('possible case-folding collision for %s') % f
85 89 if self._abort:
86 90 raise util.Abort(msg)
87 91 self._ui.warn(_("warning: %s\n") % msg)
88 92 self._loweredfiles.add(fl)
89 93 self._newfiles.add(f)
90 94
91 95 class pathauditor(object):
92 96 '''ensure that a filesystem path contains no banned components.
93 97 the following properties of a path are checked:
94 98
95 99 - ends with a directory separator
96 100 - under top-level .hg
97 101 - starts at the root of a windows drive
98 102 - contains ".."
99 103 - traverses a symlink (e.g. a/symlink_here/b)
100 104 - inside a nested repository (a callback can be used to approve
101 105 some nested repositories, e.g., subrepositories)
102 106 '''
103 107
104 108 def __init__(self, root, callback=None):
105 109 self.audited = set()
106 110 self.auditeddir = set()
107 111 self.root = root
108 112 self.callback = callback
109 113 if os.path.lexists(root) and not util.checkcase(root):
110 114 self.normcase = util.normcase
111 115 else:
112 116 self.normcase = lambda x: x
113 117
114 118 def __call__(self, path):
115 119 '''Check the relative path.
116 120 path may contain a pattern (e.g. foodir/**.txt)'''
117 121
118 122 path = util.localpath(path)
119 123 normpath = self.normcase(path)
120 124 if normpath in self.audited:
121 125 return
122 126 # AIX ignores "/" at end of path, others raise EISDIR.
123 127 if util.endswithsep(path):
124 128 raise util.Abort(_("path ends in directory separator: %s") % path)
125 129 parts = util.splitpath(path)
126 130 if (os.path.splitdrive(path)[0]
127 131 or parts[0].lower() in ('.hg', '.hg.', '')
128 132 or os.pardir in parts):
129 133 raise util.Abort(_("path contains illegal component: %s") % path)
130 134 if '.hg' in path.lower():
131 135 lparts = [p.lower() for p in parts]
132 136 for p in '.hg', '.hg.':
133 137 if p in lparts[1:]:
134 138 pos = lparts.index(p)
135 139 base = os.path.join(*parts[:pos])
136 140 raise util.Abort(_("path '%s' is inside nested repo %r")
137 141 % (path, base))
138 142
139 143 normparts = util.splitpath(normpath)
140 144 assert len(parts) == len(normparts)
141 145
142 146 parts.pop()
143 147 normparts.pop()
144 148 prefixes = []
145 149 while parts:
146 150 prefix = os.sep.join(parts)
147 151 normprefix = os.sep.join(normparts)
148 152 if normprefix in self.auditeddir:
149 153 break
150 154 curpath = os.path.join(self.root, prefix)
151 155 try:
152 156 st = os.lstat(curpath)
153 157 except OSError, err:
154 158 # EINVAL can be raised as invalid path syntax under win32.
155 159 # They must be ignored for patterns can be checked too.
156 160 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
157 161 raise
158 162 else:
159 163 if stat.S_ISLNK(st.st_mode):
160 164 raise util.Abort(
161 165 _('path %r traverses symbolic link %r')
162 166 % (path, prefix))
163 167 elif (stat.S_ISDIR(st.st_mode) and
164 168 os.path.isdir(os.path.join(curpath, '.hg'))):
165 169 if not self.callback or not self.callback(curpath):
166 170 raise util.Abort(_("path '%s' is inside nested "
167 171 "repo %r")
168 172 % (path, prefix))
169 173 prefixes.append(normprefix)
170 174 parts.pop()
171 175 normparts.pop()
172 176
173 177 self.audited.add(normpath)
174 178 # only add prefixes to the cache after checking everything: we don't
175 179 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
176 180 self.auditeddir.update(prefixes)
177 181
178 182 class abstractvfs(object):
179 183 """Abstract base class; cannot be instantiated"""
180 184
181 185 def __init__(self, *args, **kwargs):
182 186 '''Prevent instantiation; don't call this from subclasses.'''
183 187 raise NotImplementedError('attempted instantiating ' + str(type(self)))
184 188
185 189 def tryread(self, path):
186 190 '''gracefully return an empty string for missing files'''
187 191 try:
188 192 return self.read(path)
189 193 except IOError, inst:
190 194 if inst.errno != errno.ENOENT:
191 195 raise
192 196 return ""
193 197
194 198 def read(self, path):
195 199 fp = self(path, 'rb')
196 200 try:
197 201 return fp.read()
198 202 finally:
199 203 fp.close()
200 204
201 205 def write(self, path, data):
202 206 fp = self(path, 'wb')
203 207 try:
204 208 return fp.write(data)
205 209 finally:
206 210 fp.close()
207 211
208 212 def append(self, path, data):
209 213 fp = self(path, 'ab')
210 214 try:
211 215 return fp.write(data)
212 216 finally:
213 217 fp.close()
214 218
215 219 def exists(self, path=None):
216 220 return os.path.exists(self.join(path))
217 221
218 222 def isdir(self, path=None):
219 223 return os.path.isdir(self.join(path))
220 224
221 225 def makedir(self, path=None, notindexed=True):
222 226 return util.makedir(self.join(path), notindexed)
223 227
224 228 def makedirs(self, path=None, mode=None):
225 229 return util.makedirs(self.join(path), mode)
226 230
227 231 def mkdir(self, path=None):
228 232 return os.mkdir(self.join(path))
229 233
230 234 def readdir(self, path=None, stat=None, skip=None):
231 235 return osutil.listdir(self.join(path), stat, skip)
232 236
233 237 def stat(self, path=None):
234 238 return os.stat(self.join(path))
235 239
236 240 class vfs(abstractvfs):
237 241 '''Operate files relative to a base directory
238 242
239 243 This class is used to hide the details of COW semantics and
240 244 remote file access from higher level code.
241 245 '''
242 246 def __init__(self, base, audit=True, expand=False):
243 247 if expand:
244 248 base = os.path.realpath(util.expandpath(base))
245 249 self.base = base
246 250 self._setmustaudit(audit)
247 251 self.createmode = None
248 252 self._trustnlink = None
249 253
250 254 def _getmustaudit(self):
251 255 return self._audit
252 256
253 257 def _setmustaudit(self, onoff):
254 258 self._audit = onoff
255 259 if onoff:
256 260 self.audit = pathauditor(self.base)
257 261 else:
258 262 self.audit = util.always
259 263
260 264 mustaudit = property(_getmustaudit, _setmustaudit)
261 265
262 266 @util.propertycache
263 267 def _cansymlink(self):
264 268 return util.checklink(self.base)
265 269
266 270 @util.propertycache
267 271 def _chmod(self):
268 272 return util.checkexec(self.base)
269 273
270 274 def _fixfilemode(self, name):
271 275 if self.createmode is None or not self._chmod:
272 276 return
273 277 os.chmod(name, self.createmode & 0666)
274 278
275 279 def __call__(self, path, mode="r", text=False, atomictemp=False):
276 280 if self._audit:
277 281 r = util.checkosfilename(path)
278 282 if r:
279 283 raise util.Abort("%s: %r" % (r, path))
280 284 self.audit(path)
281 285 f = self.join(path)
282 286
283 287 if not text and "b" not in mode:
284 288 mode += "b" # for that other OS
285 289
286 290 nlink = -1
287 291 if mode not in ('r', 'rb'):
288 292 dirname, basename = util.split(f)
289 293 # If basename is empty, then the path is malformed because it points
290 294 # to a directory. Let the posixfile() call below raise IOError.
291 295 if basename:
292 296 if atomictemp:
293 297 if not os.path.isdir(dirname):
294 298 util.makedirs(dirname, self.createmode)
295 299 return util.atomictempfile(f, mode, self.createmode)
296 300 try:
297 301 if 'w' in mode:
298 302 util.unlink(f)
299 303 nlink = 0
300 304 else:
301 305 # nlinks() may behave differently for files on Windows
302 306 # shares if the file is open.
303 307 fd = util.posixfile(f)
304 308 nlink = util.nlinks(f)
305 309 if nlink < 1:
306 310 nlink = 2 # force mktempcopy (issue1922)
307 311 fd.close()
308 312 except (OSError, IOError), e:
309 313 if e.errno != errno.ENOENT:
310 314 raise
311 315 nlink = 0
312 316 if not os.path.isdir(dirname):
313 317 util.makedirs(dirname, self.createmode)
314 318 if nlink > 0:
315 319 if self._trustnlink is None:
316 320 self._trustnlink = nlink > 1 or util.checknlink(f)
317 321 if nlink > 1 or not self._trustnlink:
318 322 util.rename(util.mktempcopy(f), f)
319 323 fp = util.posixfile(f, mode)
320 324 if nlink == 0:
321 325 self._fixfilemode(f)
322 326 return fp
323 327
324 328 def symlink(self, src, dst):
325 329 self.audit(dst)
326 330 linkname = self.join(dst)
327 331 try:
328 332 os.unlink(linkname)
329 333 except OSError:
330 334 pass
331 335
332 336 dirname = os.path.dirname(linkname)
333 337 if not os.path.exists(dirname):
334 338 util.makedirs(dirname, self.createmode)
335 339
336 340 if self._cansymlink:
337 341 try:
338 342 os.symlink(src, linkname)
339 343 except OSError, err:
340 344 raise OSError(err.errno, _('could not symlink to %r: %s') %
341 345 (src, err.strerror), linkname)
342 346 else:
343 347 self.write(dst, src)
344 348
345 349 def join(self, path):
346 350 if path:
347 351 return os.path.join(self.base, path)
348 352 else:
349 353 return self.base
350 354
351 355 opener = vfs
352 356
353 357 class auditvfs(object):
354 358 def __init__(self, vfs):
355 359 self.vfs = vfs
356 360
357 361 def _getmustaudit(self):
358 362 return self.vfs.mustaudit
359 363
360 364 def _setmustaudit(self, onoff):
361 365 self.vfs.mustaudit = onoff
362 366
363 367 mustaudit = property(_getmustaudit, _setmustaudit)
364 368
365 369 class filtervfs(abstractvfs, auditvfs):
366 370 '''Wrapper vfs for filtering filenames with a function.'''
367 371
368 372 def __init__(self, vfs, filter):
369 373 auditvfs.__init__(self, vfs)
370 374 self._filter = filter
371 375
372 376 def __call__(self, path, *args, **kwargs):
373 377 return self.vfs(self._filter(path), *args, **kwargs)
374 378
375 379 def join(self, path):
376 380 if path:
377 381 return self.vfs.join(self._filter(path))
378 382 else:
379 383 return self.vfs.join(path)
380 384
381 385 filteropener = filtervfs
382 386
383 387 class readonlyvfs(abstractvfs, auditvfs):
384 388 '''Wrapper vfs preventing any writing.'''
385 389
386 390 def __init__(self, vfs):
387 391 auditvfs.__init__(self, vfs)
388 392
389 393 def __call__(self, path, mode='r', *args, **kw):
390 394 if mode not in ('r', 'rb'):
391 395 raise util.Abort('this vfs is read only')
392 396 return self.vfs(path, mode, *args, **kw)
393 397
394 398
395 399 def canonpath(root, cwd, myname, auditor=None):
396 400 '''return the canonical path of myname, given cwd and root'''
397 401 if util.endswithsep(root):
398 402 rootsep = root
399 403 else:
400 404 rootsep = root + os.sep
401 405 name = myname
402 406 if not os.path.isabs(name):
403 407 name = os.path.join(root, cwd, name)
404 408 name = os.path.normpath(name)
405 409 if auditor is None:
406 410 auditor = pathauditor(root)
407 411 if name != rootsep and name.startswith(rootsep):
408 412 name = name[len(rootsep):]
409 413 auditor(name)
410 414 return util.pconvert(name)
411 415 elif name == root:
412 416 return ''
413 417 else:
414 418 # Determine whether `name' is in the hierarchy at or beneath `root',
415 419 # by iterating name=dirname(name) until that causes no change (can't
416 420 # check name == '/', because that doesn't work on windows). The list
417 421 # `rel' holds the reversed list of components making up the relative
418 422 # file name we want.
419 423 rel = []
420 424 while True:
421 425 try:
422 426 s = util.samefile(name, root)
423 427 except OSError:
424 428 s = False
425 429 if s:
426 430 if not rel:
427 431 # name was actually the same as root (maybe a symlink)
428 432 return ''
429 433 rel.reverse()
430 434 name = os.path.join(*rel)
431 435 auditor(name)
432 436 return util.pconvert(name)
433 437 dirname, basename = util.split(name)
434 438 rel.append(basename)
435 439 if dirname == name:
436 440 break
437 441 name = dirname
438 442
439 443 raise util.Abort(_("%s not under root '%s'") % (myname, root))
440 444
441 445 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
442 446 '''yield every hg repository under path, always recursively.
443 447 The recurse flag will only control recursion into repo working dirs'''
444 448 def errhandler(err):
445 449 if err.filename == path:
446 450 raise err
447 451 samestat = getattr(os.path, 'samestat', None)
448 452 if followsym and samestat is not None:
449 453 def adddir(dirlst, dirname):
450 454 match = False
451 455 dirstat = os.stat(dirname)
452 456 for lstdirstat in dirlst:
453 457 if samestat(dirstat, lstdirstat):
454 458 match = True
455 459 break
456 460 if not match:
457 461 dirlst.append(dirstat)
458 462 return not match
459 463 else:
460 464 followsym = False
461 465
462 466 if (seen_dirs is None) and followsym:
463 467 seen_dirs = []
464 468 adddir(seen_dirs, path)
465 469 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
466 470 dirs.sort()
467 471 if '.hg' in dirs:
468 472 yield root # found a repository
469 473 qroot = os.path.join(root, '.hg', 'patches')
470 474 if os.path.isdir(os.path.join(qroot, '.hg')):
471 475 yield qroot # we have a patch queue repo here
472 476 if recurse:
473 477 # avoid recursing inside the .hg directory
474 478 dirs.remove('.hg')
475 479 else:
476 480 dirs[:] = [] # don't descend further
477 481 elif followsym:
478 482 newdirs = []
479 483 for d in dirs:
480 484 fname = os.path.join(root, d)
481 485 if adddir(seen_dirs, fname):
482 486 if os.path.islink(fname):
483 487 for hgname in walkrepos(fname, True, seen_dirs):
484 488 yield hgname
485 489 else:
486 490 newdirs.append(d)
487 491 dirs[:] = newdirs
488 492
489 493 def osrcpath():
490 494 '''return default os-specific hgrc search path'''
491 495 path = systemrcpath()
492 496 path.extend(userrcpath())
493 497 path = [os.path.normpath(f) for f in path]
494 498 return path
495 499
496 500 _rcpath = None
497 501
498 502 def rcpath():
499 503 '''return hgrc search path. if env var HGRCPATH is set, use it.
500 504 for each item in path, if directory, use files ending in .rc,
501 505 else use item.
502 506 make HGRCPATH empty to only look in .hg/hgrc of current repo.
503 507 if no HGRCPATH, use default os-specific path.'''
504 508 global _rcpath
505 509 if _rcpath is None:
506 510 if 'HGRCPATH' in os.environ:
507 511 _rcpath = []
508 512 for p in os.environ['HGRCPATH'].split(os.pathsep):
509 513 if not p:
510 514 continue
511 515 p = util.expandpath(p)
512 516 if os.path.isdir(p):
513 517 for f, kind in osutil.listdir(p):
514 518 if f.endswith('.rc'):
515 519 _rcpath.append(os.path.join(p, f))
516 520 else:
517 521 _rcpath.append(p)
518 522 else:
519 523 _rcpath = osrcpath()
520 524 return _rcpath
521 525
522 526 if os.name != 'nt':
523 527
524 528 def rcfiles(path):
525 529 rcs = [os.path.join(path, 'hgrc')]
526 530 rcdir = os.path.join(path, 'hgrc.d')
527 531 try:
528 532 rcs.extend([os.path.join(rcdir, f)
529 533 for f, kind in osutil.listdir(rcdir)
530 534 if f.endswith(".rc")])
531 535 except OSError:
532 536 pass
533 537 return rcs
534 538
535 539 def systemrcpath():
536 540 path = []
537 541 if sys.platform == 'plan9':
538 542 root = 'lib/mercurial'
539 543 else:
540 544 root = 'etc/mercurial'
541 545 # old mod_python does not set sys.argv
542 546 if len(getattr(sys, 'argv', [])) > 0:
543 547 p = os.path.dirname(os.path.dirname(sys.argv[0]))
544 548 path.extend(rcfiles(os.path.join(p, root)))
545 549 path.extend(rcfiles('/' + root))
546 550 return path
547 551
548 552 def userrcpath():
549 553 if sys.platform == 'plan9':
550 554 return [os.environ['home'] + '/lib/hgrc']
551 555 else:
552 556 return [os.path.expanduser('~/.hgrc')]
553 557
554 558 else:
555 559
556 560 import _winreg
557 561
558 562 def systemrcpath():
559 563 '''return default os-specific hgrc search path'''
560 564 rcpath = []
561 565 filename = util.executablepath()
562 566 # Use mercurial.ini found in directory with hg.exe
563 567 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
564 568 if os.path.isfile(progrc):
565 569 rcpath.append(progrc)
566 570 return rcpath
567 571 # Use hgrc.d found in directory with hg.exe
568 572 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
569 573 if os.path.isdir(progrcd):
570 574 for f, kind in osutil.listdir(progrcd):
571 575 if f.endswith('.rc'):
572 576 rcpath.append(os.path.join(progrcd, f))
573 577 return rcpath
574 578 # else look for a system rcpath in the registry
575 579 value = util.lookupreg('SOFTWARE\\Mercurial', None,
576 580 _winreg.HKEY_LOCAL_MACHINE)
577 581 if not isinstance(value, str) or not value:
578 582 return rcpath
579 583 value = util.localpath(value)
580 584 for p in value.split(os.pathsep):
581 585 if p.lower().endswith('mercurial.ini'):
582 586 rcpath.append(p)
583 587 elif os.path.isdir(p):
584 588 for f, kind in osutil.listdir(p):
585 589 if f.endswith('.rc'):
586 590 rcpath.append(os.path.join(p, f))
587 591 return rcpath
588 592
589 593 def userrcpath():
590 594 '''return os-specific hgrc search path to the user dir'''
591 595 home = os.path.expanduser('~')
592 596 path = [os.path.join(home, 'mercurial.ini'),
593 597 os.path.join(home, '.hgrc')]
594 598 userprofile = os.environ.get('USERPROFILE')
595 599 if userprofile:
596 600 path.append(os.path.join(userprofile, 'mercurial.ini'))
597 601 path.append(os.path.join(userprofile, '.hgrc'))
598 602 return path
599 603
600 604 def revsingle(repo, revspec, default='.'):
601 605 if not revspec:
602 606 return repo[default]
603 607
604 608 l = revrange(repo, [revspec])
605 609 if len(l) < 1:
606 610 raise util.Abort(_('empty revision set'))
607 611 return repo[l[-1]]
608 612
609 613 def revpair(repo, revs):
610 614 if not revs:
611 615 return repo.dirstate.p1(), None
612 616
613 617 l = revrange(repo, revs)
614 618
615 619 if len(l) == 0:
616 620 if revs:
617 621 raise util.Abort(_('empty revision range'))
618 622 return repo.dirstate.p1(), None
619 623
620 624 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
621 625 return repo.lookup(l[0]), None
622 626
623 627 return repo.lookup(l[0]), repo.lookup(l[-1])
624 628
625 629 _revrangesep = ':'
626 630
627 631 def revrange(repo, revs):
628 632 """Yield revision as strings from a list of revision specifications."""
629 633
630 634 def revfix(repo, val, defval):
631 635 if not val and val != 0 and defval is not None:
632 636 return defval
633 637 return repo[val].rev()
634 638
635 639 seen, l = set(), []
636 640 for spec in revs:
637 641 if l and not seen:
638 642 seen = set(l)
639 643 # attempt to parse old-style ranges first to deal with
640 644 # things like old-tag which contain query metacharacters
641 645 try:
642 646 if isinstance(spec, int):
643 647 seen.add(spec)
644 648 l.append(spec)
645 649 continue
646 650
647 651 if _revrangesep in spec:
648 652 start, end = spec.split(_revrangesep, 1)
649 653 start = revfix(repo, start, 0)
650 654 end = revfix(repo, end, len(repo) - 1)
651 655 if end == nullrev and start <= 0:
652 656 start = nullrev
653 657 rangeiter = repo.changelog.revs(start, end)
654 658 if not seen and not l:
655 659 # by far the most common case: revs = ["-1:0"]
656 660 l = list(rangeiter)
657 661 # defer syncing seen until next iteration
658 662 continue
659 663 newrevs = set(rangeiter)
660 664 if seen:
661 665 newrevs.difference_update(seen)
662 666 seen.update(newrevs)
663 667 else:
664 668 seen = newrevs
665 669 l.extend(sorted(newrevs, reverse=start > end))
666 670 continue
667 671 elif spec and spec in repo: # single unquoted rev
668 672 rev = revfix(repo, spec, None)
669 673 if rev in seen:
670 674 continue
671 675 seen.add(rev)
672 676 l.append(rev)
673 677 continue
674 678 except error.RepoLookupError:
675 679 pass
676 680
677 681 # fall through to new-style queries if old-style fails
678 682 m = revset.match(repo.ui, spec)
679 683 dl = [r for r in m(repo, list(repo)) if r not in seen]
680 684 l.extend(dl)
681 685 seen.update(dl)
682 686
683 687 return l
684 688
685 689 def expandpats(pats):
686 690 if not util.expandglobs:
687 691 return list(pats)
688 692 ret = []
689 693 for p in pats:
690 694 kind, name = matchmod._patsplit(p, None)
691 695 if kind is None:
692 696 try:
693 697 globbed = glob.glob(name)
694 698 except re.error:
695 699 globbed = [name]
696 700 if globbed:
697 701 ret.extend(globbed)
698 702 continue
699 703 ret.append(p)
700 704 return ret
701 705
702 706 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
703 707 if pats == ("",):
704 708 pats = []
705 709 if not globbed and default == 'relpath':
706 710 pats = expandpats(pats or [])
707 711
708 712 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
709 713 default)
710 714 def badfn(f, msg):
711 715 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
712 716 m.bad = badfn
713 717 return m, pats
714 718
715 719 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
716 720 return matchandpats(ctx, pats, opts, globbed, default)[0]
717 721
718 722 def matchall(repo):
719 723 return matchmod.always(repo.root, repo.getcwd())
720 724
721 725 def matchfiles(repo, files):
722 726 return matchmod.exact(repo.root, repo.getcwd(), files)
723 727
724 728 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
725 729 if dry_run is None:
726 730 dry_run = opts.get('dry_run')
727 731 if similarity is None:
728 732 similarity = float(opts.get('similarity') or 0)
729 733 # we'd use status here, except handling of symlinks and ignore is tricky
730 734 added, unknown, deleted, removed = [], [], [], []
731 735 audit_path = pathauditor(repo.root)
732 736 m = match(repo[None], pats, opts)
733 737 rejected = []
734 738 m.bad = lambda x, y: rejected.append(x)
735 739
736 740 for abs in repo.walk(m):
737 741 target = repo.wjoin(abs)
738 742 good = True
739 743 try:
740 744 audit_path(abs)
741 745 except (OSError, util.Abort):
742 746 good = False
743 747 rel = m.rel(abs)
744 748 exact = m.exact(abs)
745 749 if good and abs not in repo.dirstate:
746 750 unknown.append(abs)
747 751 if repo.ui.verbose or not exact:
748 752 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
749 753 elif (repo.dirstate[abs] != 'r' and
750 754 (not good or not os.path.lexists(target) or
751 755 (os.path.isdir(target) and not os.path.islink(target)))):
752 756 deleted.append(abs)
753 757 if repo.ui.verbose or not exact:
754 758 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
755 759 # for finding renames
756 760 elif repo.dirstate[abs] == 'r':
757 761 removed.append(abs)
758 762 elif repo.dirstate[abs] == 'a':
759 763 added.append(abs)
760 764 copies = {}
761 765 if similarity > 0:
762 766 for old, new, score in similar.findrenames(repo,
763 767 added + unknown, removed + deleted, similarity):
764 768 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
765 769 repo.ui.status(_('recording removal of %s as rename to %s '
766 770 '(%d%% similar)\n') %
767 771 (m.rel(old), m.rel(new), score * 100))
768 772 copies[new] = old
769 773
770 774 if not dry_run:
771 775 wctx = repo[None]
772 776 wlock = repo.wlock()
773 777 try:
774 778 wctx.forget(deleted)
775 779 wctx.add(unknown)
776 780 for new, old in copies.iteritems():
777 781 wctx.copy(old, new)
778 782 finally:
779 783 wlock.release()
780 784
781 785 for f in rejected:
782 786 if f in m.files():
783 787 return 1
784 788 return 0
785 789
786 790 def updatedir(ui, repo, patches, similarity=0):
787 791 '''Update dirstate after patch application according to metadata'''
788 792 if not patches:
789 793 return []
790 794 copies = []
791 795 removes = set()
792 796 cfiles = patches.keys()
793 797 cwd = repo.getcwd()
794 798 if cwd:
795 799 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
796 800 for f in patches:
797 801 gp = patches[f]
798 802 if not gp:
799 803 continue
800 804 if gp.op == 'RENAME':
801 805 copies.append((gp.oldpath, gp.path))
802 806 removes.add(gp.oldpath)
803 807 elif gp.op == 'COPY':
804 808 copies.append((gp.oldpath, gp.path))
805 809 elif gp.op == 'DELETE':
806 810 removes.add(gp.path)
807 811
808 812 wctx = repo[None]
809 813 for src, dst in copies:
810 814 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
811 815 if (not similarity) and removes:
812 816 wctx.remove(sorted(removes), True)
813 817
814 818 for f in patches:
815 819 gp = patches[f]
816 820 if gp and gp.mode:
817 821 islink, isexec = gp.mode
818 822 dst = repo.wjoin(gp.path)
819 823 # patch won't create empty files
820 824 if gp.op == 'ADD' and not os.path.lexists(dst):
821 825 flags = (isexec and 'x' or '') + (islink and 'l' or '')
822 826 repo.wwrite(gp.path, '', flags)
823 827 util.setflags(dst, islink, isexec)
824 828 addremove(repo, cfiles, similarity=similarity)
825 829 files = patches.keys()
826 830 files.extend([r for r in removes if r not in files])
827 831 return sorted(files)
828 832
829 833 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
830 834 """Update the dirstate to reflect the intent of copying src to dst. For
831 835 different reasons it might not end with dst being marked as copied from src.
832 836 """
833 837 origsrc = repo.dirstate.copied(src) or src
834 838 if dst == origsrc: # copying back a copy?
835 839 if repo.dirstate[dst] not in 'mn' and not dryrun:
836 840 repo.dirstate.normallookup(dst)
837 841 else:
838 842 if repo.dirstate[origsrc] == 'a' and origsrc == src:
839 843 if not ui.quiet:
840 844 ui.warn(_("%s has not been committed yet, so no copy "
841 845 "data will be stored for %s.\n")
842 846 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
843 847 if repo.dirstate[dst] in '?r' and not dryrun:
844 848 wctx.add([dst])
845 849 elif not dryrun:
846 850 wctx.copy(origsrc, dst)
847 851
848 852 def readrequires(opener, supported):
849 853 '''Reads and parses .hg/requires and checks if all entries found
850 854 are in the list of supported features.'''
851 855 requirements = set(opener.read("requires").splitlines())
852 856 missings = []
853 857 for r in requirements:
854 858 if r not in supported:
855 859 if not r or not r[0].isalnum():
856 860 raise error.RequirementError(_(".hg/requires file is corrupt"))
857 861 missings.append(r)
858 862 missings.sort()
859 863 if missings:
860 864 raise error.RequirementError(
861 865 _("unknown repository format: requires features '%s' (upgrade "
862 866 "Mercurial)") % "', '".join(missings))
863 867 return requirements
864 868
865 869 class filecacheentry(object):
866 870 def __init__(self, path, stat=True):
867 871 self.path = path
868 872 self.cachestat = None
869 873 self._cacheable = None
870 874
871 875 if stat:
872 876 self.cachestat = filecacheentry.stat(self.path)
873 877
874 878 if self.cachestat:
875 879 self._cacheable = self.cachestat.cacheable()
876 880 else:
877 881 # None means we don't know yet
878 882 self._cacheable = None
879 883
880 884 def refresh(self):
881 885 if self.cacheable():
882 886 self.cachestat = filecacheentry.stat(self.path)
883 887
884 888 def cacheable(self):
885 889 if self._cacheable is not None:
886 890 return self._cacheable
887 891
888 892 # we don't know yet, assume it is for now
889 893 return True
890 894
891 895 def changed(self):
892 896 # no point in going further if we can't cache it
893 897 if not self.cacheable():
894 898 return True
895 899
896 900 newstat = filecacheentry.stat(self.path)
897 901
898 902 # we may not know if it's cacheable yet, check again now
899 903 if newstat and self._cacheable is None:
900 904 self._cacheable = newstat.cacheable()
901 905
902 906 # check again
903 907 if not self._cacheable:
904 908 return True
905 909
906 910 if self.cachestat != newstat:
907 911 self.cachestat = newstat
908 912 return True
909 913 else:
910 914 return False
911 915
912 916 @staticmethod
913 917 def stat(path):
914 918 try:
915 919 return util.cachestat(path)
916 920 except OSError, e:
917 921 if e.errno != errno.ENOENT:
918 922 raise
919 923
920 924 class filecache(object):
921 925 '''A property like decorator that tracks a file under .hg/ for updates.
922 926
923 927 Records stat info when called in _filecache.
924 928
925 929 On subsequent calls, compares old stat info with new info, and recreates
926 930 the object when needed, updating the new stat info in _filecache.
927 931
928 932 Mercurial either atomic renames or appends for files under .hg,
929 933 so to ensure the cache is reliable we need the filesystem to be able
930 934 to tell us if a file has been replaced. If it can't, we fallback to
931 935 recreating the object on every call (essentially the same behaviour as
932 936 propertycache).'''
933 937 def __init__(self, path):
934 938 self.path = path
935 939
936 940 def join(self, obj, fname):
937 941 """Used to compute the runtime path of the cached file.
938 942
939 943 Users should subclass filecache and provide their own version of this
940 944 function to call the appropriate join function on 'obj' (an instance
941 945 of the class that its member function was decorated).
942 946 """
943 947 return obj.join(fname)
944 948
945 949 def __call__(self, func):
946 950 self.func = func
947 951 self.name = func.__name__
948 952 return self
949 953
950 954 def __get__(self, obj, type=None):
951 955 # do we need to check if the file changed?
952 956 if self.name in obj.__dict__:
953 957 assert self.name in obj._filecache, self.name
954 958 return obj.__dict__[self.name]
955 959
956 960 entry = obj._filecache.get(self.name)
957 961
958 962 if entry:
959 963 if entry.changed():
960 964 entry.obj = self.func(obj)
961 965 else:
962 966 path = self.join(obj, self.path)
963 967
964 968 # We stat -before- creating the object so our cache doesn't lie if
965 969 # a writer modified between the time we read and stat
966 970 entry = filecacheentry(path)
967 971 entry.obj = self.func(obj)
968 972
969 973 obj._filecache[self.name] = entry
970 974
971 975 obj.__dict__[self.name] = entry.obj
972 976 return entry.obj
973 977
974 978 def __set__(self, obj, value):
975 979 if self.name not in obj._filecache:
976 980 # we add an entry for the missing value because X in __dict__
977 981 # implies X in _filecache
978 982 ce = filecacheentry(self.join(obj, self.path), False)
979 983 obj._filecache[self.name] = ce
980 984 else:
981 985 ce = obj._filecache[self.name]
982 986
983 987 ce.obj = value # update cached copy
984 988 obj.__dict__[self.name] = value # update copy returned by obj.x
985 989
986 990 def __delete__(self, obj):
987 991 try:
988 992 del obj.__dict__[self.name]
989 993 except KeyError:
990 994 raise AttributeError(self.name)
@@ -1,867 +1,889 b''
1 1 $ cat >> $HGRCPATH << EOF
2 2 > [extensions]
3 3 > graphlog=
4 4 > [phases]
5 5 > # public changeset are not obsolete
6 6 > publish=false
7 7 > EOF
8 8 $ mkcommit() {
9 9 > echo "$1" > "$1"
10 10 > hg add "$1"
11 11 > hg ci -m "add $1"
12 12 > }
13 13 $ getid() {
14 14 > hg id --debug --hidden -ir "desc('$1')"
15 15 > }
16 16
17 17 $ cat > debugkeys.py <<EOF
18 18 > def reposetup(ui, repo):
19 19 > class debugkeysrepo(repo.__class__):
20 20 > def listkeys(self, namespace):
21 21 > ui.write('listkeys %s\n' % (namespace,))
22 22 > return super(debugkeysrepo, self).listkeys(namespace)
23 23 >
24 24 > if repo.local():
25 25 > repo.__class__ = debugkeysrepo
26 26 > EOF
27 27
28 28 $ hg init tmpa
29 29 $ cd tmpa
30 30 $ mkcommit kill_me
31 31
32 32 Checking that the feature is properly disabled
33 33
34 34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
35 35 abort: obsolete feature is not enabled on this repo
36 36 [255]
37 37
38 38 Enabling it
39 39
40 40 $ cat > ../obs.py << EOF
41 41 > import mercurial.obsolete
42 42 > mercurial.obsolete._enabled = True
43 43 > EOF
44 44 $ echo '[extensions]' >> $HGRCPATH
45 45 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
46 46
47 47 Killing a single changeset without replacement
48 48
49 49 $ hg debugobsolete 0
50 50 abort: changeset references must be full hexadecimal node identifiers
51 51 [255]
52 52 $ hg debugobsolete '00'
53 53 abort: changeset references must be full hexadecimal node identifiers
54 54 [255]
55 55 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
56 56 $ hg debugobsolete
57 57 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 {'date': '0 0', 'user': 'babar'}
58 58
59 59 (test that mercurial is not confused)
60 60
61 61 $ hg up null --quiet # having 0 as parent prevents it to be hidden
62 62 $ hg tip
63 63 changeset: -1:000000000000
64 64 tag: tip
65 65 user:
66 66 date: Thu Jan 01 00:00:00 1970 +0000
67 67
68 68 $ hg up --hidden tip --quiet
69 69 $ cd ..
70 70
71 71 Killing a single changeset with replacement
72 72
73 73 $ hg init tmpb
74 74 $ cd tmpb
75 75 $ mkcommit a
76 76 $ mkcommit b
77 77 $ mkcommit original_c
78 78 $ hg up "desc('b')"
79 79 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
80 80 $ mkcommit new_c
81 81 created new head
82 82 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
83 83 $ hg debugobsolete --flag 12 `getid original_c` `getid new_c` -d '56 12'
84 84 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
85 85 2:245bde4270cd add original_c
86 86 $ hg debugobsolete
87 87 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
88 88
89 89 do it again (it read the obsstore before adding new changeset)
90 90
91 91 $ hg up '.^'
92 92 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
93 93 $ mkcommit new_2_c
94 94 created new head
95 95 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
96 96 $ hg debugobsolete
97 97 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
98 98 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
99 99
100 100 Register two markers with a missing node
101 101
102 102 $ hg up '.^'
103 103 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
104 104 $ mkcommit new_3_c
105 105 created new head
106 106 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
107 107 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
108 108 $ hg debugobsolete
109 109 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
110 110 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
111 111 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
112 112 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
113 113
114 114 Refuse pathological nullid successors
115 115 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
116 116 transaction abort!
117 117 rollback completed
118 118 abort: bad obsolescence marker detected: invalid successors nullid
119 119 [255]
120 120
121 121 Check that graphlog detect that a changeset is obsolete:
122 122
123 123 $ hg glog
124 124 @ changeset: 5:5601fb93a350
125 125 | tag: tip
126 126 | parent: 1:7c3bad9141dc
127 127 | user: test
128 128 | date: Thu Jan 01 00:00:00 1970 +0000
129 129 | summary: add new_3_c
130 130 |
131 131 o changeset: 1:7c3bad9141dc
132 132 | user: test
133 133 | date: Thu Jan 01 00:00:00 1970 +0000
134 134 | summary: add b
135 135 |
136 136 o changeset: 0:1f0dee641bb7
137 137 user: test
138 138 date: Thu Jan 01 00:00:00 1970 +0000
139 139 summary: add a
140 140
141 141
142 142 check that heads does not report them
143 143
144 144 $ hg heads
145 145 changeset: 5:5601fb93a350
146 146 tag: tip
147 147 parent: 1:7c3bad9141dc
148 148 user: test
149 149 date: Thu Jan 01 00:00:00 1970 +0000
150 150 summary: add new_3_c
151 151
152 152 $ hg heads --hidden
153 153 changeset: 5:5601fb93a350
154 154 tag: tip
155 155 parent: 1:7c3bad9141dc
156 156 user: test
157 157 date: Thu Jan 01 00:00:00 1970 +0000
158 158 summary: add new_3_c
159 159
160 160 changeset: 4:ca819180edb9
161 161 parent: 1:7c3bad9141dc
162 162 user: test
163 163 date: Thu Jan 01 00:00:00 1970 +0000
164 164 summary: add new_2_c
165 165
166 166 changeset: 3:cdbce2fbb163
167 167 parent: 1:7c3bad9141dc
168 168 user: test
169 169 date: Thu Jan 01 00:00:00 1970 +0000
170 170 summary: add new_c
171 171
172 172 changeset: 2:245bde4270cd
173 173 user: test
174 174 date: Thu Jan 01 00:00:00 1970 +0000
175 175 summary: add original_c
176 176
177 177
178 178
179 179 check that summary does not report them
180 180
181 181 $ hg init ../sink
182 182 $ echo '[paths]' >> .hg/hgrc
183 183 $ echo 'default=../sink' >> .hg/hgrc
184 184 $ hg summary --remote
185 185 parent: 5:5601fb93a350 tip
186 186 add new_3_c
187 187 branch: default
188 188 commit: (clean)
189 189 update: (current)
190 190 remote: 3 outgoing
191 191
192 192 $ hg summary --remote --hidden
193 193 parent: 5:5601fb93a350 tip
194 194 add new_3_c
195 195 branch: default
196 196 commit: (clean)
197 197 update: 3 new changesets, 4 branch heads (merge)
198 198 remote: 3 outgoing
199 199
200 200 check that various commands work well with filtering
201 201
202 202 $ hg tip
203 203 changeset: 5:5601fb93a350
204 204 tag: tip
205 205 parent: 1:7c3bad9141dc
206 206 user: test
207 207 date: Thu Jan 01 00:00:00 1970 +0000
208 208 summary: add new_3_c
209 209
210 210 $ hg log -r 6
211 211 abort: unknown revision '6'!
212 212 [255]
213 213 $ hg log -r 4
214 214 abort: unknown revision '4'!
215 215 [255]
216 216
217 217 Check that public changeset are not accounted as obsolete:
218 218
219 219 $ hg --hidden phase --public 2
220 220 $ hg --config 'extensions.graphlog=' glog
221 221 @ changeset: 5:5601fb93a350
222 222 | tag: tip
223 223 | parent: 1:7c3bad9141dc
224 224 | user: test
225 225 | date: Thu Jan 01 00:00:00 1970 +0000
226 226 | summary: add new_3_c
227 227 |
228 228 | o changeset: 2:245bde4270cd
229 229 |/ user: test
230 230 | date: Thu Jan 01 00:00:00 1970 +0000
231 231 | summary: add original_c
232 232 |
233 233 o changeset: 1:7c3bad9141dc
234 234 | user: test
235 235 | date: Thu Jan 01 00:00:00 1970 +0000
236 236 | summary: add b
237 237 |
238 238 o changeset: 0:1f0dee641bb7
239 239 user: test
240 240 date: Thu Jan 01 00:00:00 1970 +0000
241 241 summary: add a
242 242
243 243
244 244 And that bumped changeset are detected
245 245 --------------------------------------
246 246
247 247 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
248 248 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
249 249 the public changeset
250 250
251 251 $ hg log --hidden -r 'bumped()'
252 252 changeset: 5:5601fb93a350
253 253 tag: tip
254 254 parent: 1:7c3bad9141dc
255 255 user: test
256 256 date: Thu Jan 01 00:00:00 1970 +0000
257 257 summary: add new_3_c
258 258
259 259
260 260 And that we can't push bumped changeset
261 261
262 262 $ hg push ../tmpa -r 0 --force #(make repo related)
263 263 pushing to ../tmpa
264 264 searching for changes
265 265 warning: repository is unrelated
266 266 adding changesets
267 267 adding manifests
268 268 adding file changes
269 269 added 1 changesets with 1 changes to 1 files (+1 heads)
270 270 $ hg push ../tmpa
271 271 pushing to ../tmpa
272 272 searching for changes
273 273 abort: push includes bumped changeset: 5601fb93a350!
274 274 [255]
275 275
276 276 Fixing "bumped" situation
277 277 We need to create a clone of 5 and add a special marker with a flag
278 278
279 279 $ hg up '5^'
280 280 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
281 281 $ hg revert -ar 5
282 282 adding new_3_c
283 283 $ hg ci -m 'add n3w_3_c'
284 284 created new head
285 285 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
286 286 $ hg log -r 'bumped()'
287 287 $ hg log -G
288 288 @ changeset: 6:6f9641995072
289 289 | tag: tip
290 290 | parent: 1:7c3bad9141dc
291 291 | user: test
292 292 | date: Thu Jan 01 00:00:00 1970 +0000
293 293 | summary: add n3w_3_c
294 294 |
295 295 | o changeset: 2:245bde4270cd
296 296 |/ user: test
297 297 | date: Thu Jan 01 00:00:00 1970 +0000
298 298 | summary: add original_c
299 299 |
300 300 o changeset: 1:7c3bad9141dc
301 301 | user: test
302 302 | date: Thu Jan 01 00:00:00 1970 +0000
303 303 | summary: add b
304 304 |
305 305 o changeset: 0:1f0dee641bb7
306 306 user: test
307 307 date: Thu Jan 01 00:00:00 1970 +0000
308 308 summary: add a
309 309
310 310
311 311
312 312
313 313 $ cd ..
314 314
315 315 Exchange Test
316 316 ============================
317 317
318 318 Destination repo does not have any data
319 319 ---------------------------------------
320 320
321 321 Simple incoming test
322 322
323 323 $ hg init tmpc
324 324 $ cd tmpc
325 325 $ hg incoming ../tmpb
326 326 comparing with ../tmpb
327 327 changeset: 0:1f0dee641bb7
328 328 user: test
329 329 date: Thu Jan 01 00:00:00 1970 +0000
330 330 summary: add a
331 331
332 332 changeset: 1:7c3bad9141dc
333 333 user: test
334 334 date: Thu Jan 01 00:00:00 1970 +0000
335 335 summary: add b
336 336
337 337 changeset: 2:245bde4270cd
338 338 user: test
339 339 date: Thu Jan 01 00:00:00 1970 +0000
340 340 summary: add original_c
341 341
342 342 changeset: 6:6f9641995072
343 343 tag: tip
344 344 parent: 1:7c3bad9141dc
345 345 user: test
346 346 date: Thu Jan 01 00:00:00 1970 +0000
347 347 summary: add n3w_3_c
348 348
349 349
350 350 Try to pull markers
351 351 (extinct changeset are excluded but marker are pushed)
352 352
353 353 $ hg pull ../tmpb
354 354 pulling from ../tmpb
355 355 requesting all changes
356 356 adding changesets
357 357 adding manifests
358 358 adding file changes
359 359 added 4 changesets with 4 changes to 4 files (+1 heads)
360 360 (run 'hg heads' to see heads, 'hg merge' to merge)
361 361 $ hg debugobsolete
362 362 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
363 363 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
364 364 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
365 365 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
366 366 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
367 367
368 368 Rollback//Transaction support
369 369
370 370 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
371 371 $ hg debugobsolete
372 372 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
373 373 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
374 374 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
375 375 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
376 376 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
377 377 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 {'date': '1340 0', 'user': 'test'}
378 378 $ hg rollback -n
379 379 repository tip rolled back to revision 3 (undo debugobsolete)
380 380 $ hg rollback
381 381 repository tip rolled back to revision 3 (undo debugobsolete)
382 382 $ hg debugobsolete
383 383 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
384 384 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
385 385 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
386 386 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
387 387 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
388 388
389 389 $ cd ..
390 390
391 391 Try to push markers
392 392
393 393 $ hg init tmpd
394 394 $ hg -R tmpb push tmpd
395 395 pushing to tmpd
396 396 searching for changes
397 397 adding changesets
398 398 adding manifests
399 399 adding file changes
400 400 added 4 changesets with 4 changes to 4 files (+1 heads)
401 401 $ hg -R tmpd debugobsolete
402 402 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
403 403 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
404 404 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
405 405 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
406 406 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
407 407
408 408 Check obsolete keys are exchanged only if source has an obsolete store
409 409
410 410 $ hg init empty
411 411 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
412 412 pushing to tmpd
413 413 no changes found
414 414 listkeys phases
415 415 listkeys bookmarks
416 416 [1]
417 417
418 418 clone support
419 419 (markers are copied and extinct changesets are included to allow hardlinks)
420 420
421 421 $ hg clone tmpb clone-dest
422 422 updating to branch default
423 423 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 424 $ hg -R clone-dest log -G --hidden
425 425 @ changeset: 6:6f9641995072
426 426 | tag: tip
427 427 | parent: 1:7c3bad9141dc
428 428 | user: test
429 429 | date: Thu Jan 01 00:00:00 1970 +0000
430 430 | summary: add n3w_3_c
431 431 |
432 432 | x changeset: 5:5601fb93a350
433 433 |/ parent: 1:7c3bad9141dc
434 434 | user: test
435 435 | date: Thu Jan 01 00:00:00 1970 +0000
436 436 | summary: add new_3_c
437 437 |
438 438 | x changeset: 4:ca819180edb9
439 439 |/ parent: 1:7c3bad9141dc
440 440 | user: test
441 441 | date: Thu Jan 01 00:00:00 1970 +0000
442 442 | summary: add new_2_c
443 443 |
444 444 | x changeset: 3:cdbce2fbb163
445 445 |/ parent: 1:7c3bad9141dc
446 446 | user: test
447 447 | date: Thu Jan 01 00:00:00 1970 +0000
448 448 | summary: add new_c
449 449 |
450 450 | o changeset: 2:245bde4270cd
451 451 |/ user: test
452 452 | date: Thu Jan 01 00:00:00 1970 +0000
453 453 | summary: add original_c
454 454 |
455 455 o changeset: 1:7c3bad9141dc
456 456 | user: test
457 457 | date: Thu Jan 01 00:00:00 1970 +0000
458 458 | summary: add b
459 459 |
460 460 o changeset: 0:1f0dee641bb7
461 461 user: test
462 462 date: Thu Jan 01 00:00:00 1970 +0000
463 463 summary: add a
464 464
465 465 $ hg -R clone-dest debugobsolete
466 466 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
467 467 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
468 468 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
469 469 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
470 470 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
471 471
472 472
473 473 Destination repo have existing data
474 474 ---------------------------------------
475 475
476 476 On pull
477 477
478 478 $ hg init tmpe
479 479 $ cd tmpe
480 480 $ hg debugobsolete -d '1339 0' 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339
481 481 $ hg pull ../tmpb
482 482 pulling from ../tmpb
483 483 requesting all changes
484 484 adding changesets
485 485 adding manifests
486 486 adding file changes
487 487 added 4 changesets with 4 changes to 4 files (+1 heads)
488 488 (run 'hg heads' to see heads, 'hg merge' to merge)
489 489 $ hg debugobsolete
490 490 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
491 491 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
492 492 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
493 493 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
494 494 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
495 495 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
496 496
497 497
498 498 On push
499 499
500 500 $ hg push ../tmpc
501 501 pushing to ../tmpc
502 502 searching for changes
503 503 no changes found
504 504 [1]
505 505 $ hg -R ../tmpc debugobsolete
506 506 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
507 507 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
508 508 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
509 509 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
510 510 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 {'date': '1338 0', 'user': 'test'}
511 511 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
512 512
513 513 detect outgoing obsolete and unstable
514 514 ---------------------------------------
515 515
516 516
517 517 $ hg glog
518 518 o changeset: 3:6f9641995072
519 519 | tag: tip
520 520 | parent: 1:7c3bad9141dc
521 521 | user: test
522 522 | date: Thu Jan 01 00:00:00 1970 +0000
523 523 | summary: add n3w_3_c
524 524 |
525 525 | o changeset: 2:245bde4270cd
526 526 |/ user: test
527 527 | date: Thu Jan 01 00:00:00 1970 +0000
528 528 | summary: add original_c
529 529 |
530 530 o changeset: 1:7c3bad9141dc
531 531 | user: test
532 532 | date: Thu Jan 01 00:00:00 1970 +0000
533 533 | summary: add b
534 534 |
535 535 o changeset: 0:1f0dee641bb7
536 536 user: test
537 537 date: Thu Jan 01 00:00:00 1970 +0000
538 538 summary: add a
539 539
540 540 $ hg up 'desc("n3w_3_c")'
541 541 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
542 542 $ mkcommit original_d
543 543 $ mkcommit original_e
544 544 $ hg debugobsolete `getid original_d` -d '0 0'
545 545 $ hg log -r 'obsolete()'
546 546 changeset: 4:94b33453f93b
547 547 user: test
548 548 date: Thu Jan 01 00:00:00 1970 +0000
549 549 summary: add original_d
550 550
551 551 $ hg glog -r '::unstable()'
552 552 @ changeset: 5:cda648ca50f5
553 553 | tag: tip
554 554 | user: test
555 555 | date: Thu Jan 01 00:00:00 1970 +0000
556 556 | summary: add original_e
557 557 |
558 558 x changeset: 4:94b33453f93b
559 559 | user: test
560 560 | date: Thu Jan 01 00:00:00 1970 +0000
561 561 | summary: add original_d
562 562 |
563 563 o changeset: 3:6f9641995072
564 564 | parent: 1:7c3bad9141dc
565 565 | user: test
566 566 | date: Thu Jan 01 00:00:00 1970 +0000
567 567 | summary: add n3w_3_c
568 568 |
569 569 o changeset: 1:7c3bad9141dc
570 570 | user: test
571 571 | date: Thu Jan 01 00:00:00 1970 +0000
572 572 | summary: add b
573 573 |
574 574 o changeset: 0:1f0dee641bb7
575 575 user: test
576 576 date: Thu Jan 01 00:00:00 1970 +0000
577 577 summary: add a
578 578
579 579
580 580 refuse to push obsolete changeset
581 581
582 582 $ hg push ../tmpc/ -r 'desc("original_d")'
583 583 pushing to ../tmpc/
584 584 searching for changes
585 585 abort: push includes obsolete changeset: 94b33453f93b!
586 586 [255]
587 587
588 588 refuse to push unstable changeset
589 589
590 590 $ hg push ../tmpc/
591 591 pushing to ../tmpc/
592 592 searching for changes
593 593 abort: push includes unstable changeset: cda648ca50f5!
594 594 [255]
595 595
596 596 Test that extinct changeset are properly detected
597 597
598 598 $ hg log -r 'extinct()'
599 599
600 600 Don't try to push extinct changeset
601 601
602 602 $ hg init ../tmpf
603 603 $ hg out ../tmpf
604 604 comparing with ../tmpf
605 605 searching for changes
606 606 changeset: 0:1f0dee641bb7
607 607 user: test
608 608 date: Thu Jan 01 00:00:00 1970 +0000
609 609 summary: add a
610 610
611 611 changeset: 1:7c3bad9141dc
612 612 user: test
613 613 date: Thu Jan 01 00:00:00 1970 +0000
614 614 summary: add b
615 615
616 616 changeset: 2:245bde4270cd
617 617 user: test
618 618 date: Thu Jan 01 00:00:00 1970 +0000
619 619 summary: add original_c
620 620
621 621 changeset: 3:6f9641995072
622 622 parent: 1:7c3bad9141dc
623 623 user: test
624 624 date: Thu Jan 01 00:00:00 1970 +0000
625 625 summary: add n3w_3_c
626 626
627 627 changeset: 4:94b33453f93b
628 628 user: test
629 629 date: Thu Jan 01 00:00:00 1970 +0000
630 630 summary: add original_d
631 631
632 632 changeset: 5:cda648ca50f5
633 633 tag: tip
634 634 user: test
635 635 date: Thu Jan 01 00:00:00 1970 +0000
636 636 summary: add original_e
637 637
638 638 $ hg push ../tmpf -f # -f because be push unstable too
639 639 pushing to ../tmpf
640 640 searching for changes
641 641 adding changesets
642 642 adding manifests
643 643 adding file changes
644 644 added 6 changesets with 6 changes to 6 files (+1 heads)
645 645
646 646 no warning displayed
647 647
648 648 $ hg push ../tmpf
649 649 pushing to ../tmpf
650 650 searching for changes
651 651 no changes found
652 652 [1]
653 653
654 654 Do not warn about new head when the new head is a successors of a remote one
655 655
656 656 $ hg glog
657 657 @ changeset: 5:cda648ca50f5
658 658 | tag: tip
659 659 | user: test
660 660 | date: Thu Jan 01 00:00:00 1970 +0000
661 661 | summary: add original_e
662 662 |
663 663 x changeset: 4:94b33453f93b
664 664 | user: test
665 665 | date: Thu Jan 01 00:00:00 1970 +0000
666 666 | summary: add original_d
667 667 |
668 668 o changeset: 3:6f9641995072
669 669 | parent: 1:7c3bad9141dc
670 670 | user: test
671 671 | date: Thu Jan 01 00:00:00 1970 +0000
672 672 | summary: add n3w_3_c
673 673 |
674 674 | o changeset: 2:245bde4270cd
675 675 |/ user: test
676 676 | date: Thu Jan 01 00:00:00 1970 +0000
677 677 | summary: add original_c
678 678 |
679 679 o changeset: 1:7c3bad9141dc
680 680 | user: test
681 681 | date: Thu Jan 01 00:00:00 1970 +0000
682 682 | summary: add b
683 683 |
684 684 o changeset: 0:1f0dee641bb7
685 685 user: test
686 686 date: Thu Jan 01 00:00:00 1970 +0000
687 687 summary: add a
688 688
689 689 $ hg up -q 'desc(n3w_3_c)'
690 690 $ mkcommit obsolete_e
691 691 created new head
692 692 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
693 693 $ hg outgoing ../tmpf # parasite hg outgoing testin
694 694 comparing with ../tmpf
695 695 searching for changes
696 696 changeset: 6:3de5eca88c00
697 697 tag: tip
698 698 parent: 3:6f9641995072
699 699 user: test
700 700 date: Thu Jan 01 00:00:00 1970 +0000
701 701 summary: add obsolete_e
702 702
703 703 $ hg push ../tmpf
704 704 pushing to ../tmpf
705 705 searching for changes
706 706 adding changesets
707 707 adding manifests
708 708 adding file changes
709 709 added 1 changesets with 1 changes to 1 files (+1 heads)
710 710
711 711 #if serve
712 712
713 713 check hgweb does not explode
714 714 ====================================
715 715
716 716 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
717 717 adding changesets
718 718 adding manifests
719 719 adding file changes
720 720 added 62 changesets with 63 changes to 9 files (+60 heads)
721 721 (run 'hg heads .' to see heads, 'hg merge' to merge)
722 722 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
723 723 > do
724 724 > hg debugobsolete $node
725 725 > done
726 726 $ hg up tip
727 727 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
728 728
729 729 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
730 730 $ cat hg.pid >> $DAEMON_PIDS
731 731
732 732 check changelog view
733 733
734 734 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'shortlog/'
735 735 200 Script output follows
736 736
737 737 check graph view
738 738
739 739 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'graph'
740 740 200 Script output follows
741 741
742 742 check filelog view
743 743
744 744 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'log/'`hg id --debug --id`/'babar'
745 745 200 Script output follows
746 746
747 747 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/68'
748 748 200 Script output follows
749 749 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
750 750 404 Not Found
751 751 [1]
752 752
753 753 check that web.view config option:
754 754
755 755 $ kill `cat hg.pid`
756 756 $ cat >> .hg/hgrc << EOF
757 757 > [web]
758 758 > view=all
759 759 > EOF
760 760 $ wait
761 761 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
762 762 $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
763 763 200 Script output follows
764 764 $ kill `cat hg.pid`
765 765
766 766 Checking _enable=False warning if obsolete marker exists
767 767
768 768 $ echo '[extensions]' >> $HGRCPATH
769 769 $ echo "obs=!" >> $HGRCPATH
770 770 $ hg log -r tip
771 771 obsolete feature not enabled but 68 markers found!
772 772 changeset: 68:c15e9edfca13
773 773 tag: tip
774 774 parent: 7:50c51b361e60
775 775 user: test
776 776 date: Thu Jan 01 00:00:00 1970 +0000
777 777 summary: add celestine
778 778
779 779
780 780 reenable for later test
781 781
782 782 $ echo '[extensions]' >> $HGRCPATH
783 783 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
784 784
785 785 #endif
786 786
787 787 Test incoming/outcoming with changesets obsoleted remotely, known locally
788 788 ===============================================================================
789 789
790 790 This test issue 3805
791 791
792 792 $ hg init repo-issue3805
793 793 $ cd repo-issue3805
794 794 $ echo "foo" > foo
795 795 $ hg ci -Am "A"
796 796 adding foo
797 797 $ hg clone . ../other-issue3805
798 798 updating to branch default
799 799 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
800 800 $ echo "bar" >> foo
801 801 $ hg ci --amend
802 802 $ cd ../other-issue3805
803 803 $ hg log -G
804 804 @ changeset: 0:193e9254ce7e
805 805 tag: tip
806 806 user: test
807 807 date: Thu Jan 01 00:00:00 1970 +0000
808 808 summary: A
809 809
810 810 $ hg log -G -R ../repo-issue3805
811 811 @ changeset: 2:3816541e5485
812 812 tag: tip
813 813 parent: -1:000000000000
814 814 user: test
815 815 date: Thu Jan 01 00:00:00 1970 +0000
816 816 summary: A
817 817
818 818 $ hg incoming
819 819 comparing with $TESTTMP/tmpe/repo-issue3805
820 820 searching for changes
821 821 changeset: 2:3816541e5485
822 822 tag: tip
823 823 parent: -1:000000000000
824 824 user: test
825 825 date: Thu Jan 01 00:00:00 1970 +0000
826 826 summary: A
827 827
828 828 $ hg incoming --bundle ../issue3805.hg
829 829 comparing with $TESTTMP/tmpe/repo-issue3805
830 830 searching for changes
831 831 changeset: 2:3816541e5485
832 832 tag: tip
833 833 parent: -1:000000000000
834 834 user: test
835 835 date: Thu Jan 01 00:00:00 1970 +0000
836 836 summary: A
837 837
838 838 $ hg outgoing
839 839 comparing with $TESTTMP/tmpe/repo-issue3805
840 840 searching for changes
841 841 no changes found
842 842 [1]
843 843
844 844 #if serve
845 845
846 846 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
847 847 $ cat hg.pid >> $DAEMON_PIDS
848 848
849 849 $ hg incoming http://localhost:$HGPORT
850 850 comparing with http://localhost:$HGPORT/
851 851 searching for changes
852 852 changeset: 1:3816541e5485
853 853 tag: tip
854 854 parent: -1:000000000000
855 855 user: test
856 856 date: Thu Jan 01 00:00:00 1970 +0000
857 857 summary: A
858 858
859 859 $ hg outgoing http://localhost:$HGPORT
860 860 comparing with http://localhost:$HGPORT/
861 861 searching for changes
862 862 no changes found
863 863 [1]
864 864
865 865 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
866 866
867 867 #endif
868
869 This test issue 3814
870
871 (nothing to push but locally hidden changeset)
872
873 $ cd ..
874 $ hg init repo-issue3814
875 $ cd repo-issue3805
876 $ hg push -r 3816541e5485 ../repo-issue3814
877 pushing to ../repo-issue3814
878 searching for changes
879 adding changesets
880 adding manifests
881 adding file changes
882 added 1 changesets with 1 changes to 1 files
883 $ hg out ../repo-issue3814
884 comparing with ../repo-issue3814
885 searching for changes
886 no changes found
887 [1]
888
889
General Comments 0
You need to be logged in to leave comments. Login now