##// END OF EJS Templates
bundlerepo: reintroduce dirstate
Matt Mackall -
r7853:af062a9f default
parent child Browse files
Show More
@@ -1,532 +1,536 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an audience
15 15 # not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Setup in hgrc:
25 25 #
26 26 # [extensions]
27 27 # # enable extension
28 28 # hgext.keyword =
29 29 #
30 30 # Files to act upon/ignore are specified in the [keyword] section.
31 31 # Customized keyword template mappings in the [keywordmaps] section.
32 32 #
33 33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34 34
35 35 '''keyword expansion in local repositories
36 36
37 37 This extension expands RCS/CVS-like or self-customized $Keywords$
38 38 in tracked text files selected by your configuration.
39 39
40 40 Keywords are only expanded in local repositories and not stored in
41 41 the change history. The mechanism can be regarded as a convenience
42 42 for the current user or for archive distribution.
43 43
44 44 Configuration is done in the [keyword] and [keywordmaps] sections
45 45 of hgrc files.
46 46
47 47 Example:
48 48
49 49 [keyword]
50 50 # expand keywords in every python file except those matching "x*"
51 51 **.py =
52 52 x* = ignore
53 53
54 54 Note: the more specific you are in your filename patterns
55 55 the less you lose speed in huge repos.
56 56
57 57 For [keywordmaps] template mapping and expansion demonstration and
58 58 control run "hg kwdemo".
59 59
60 60 An additional date template filter {date|utcdate} is provided.
61 61
62 62 The default template mappings (view with "hg kwdemo -d") can be replaced
63 63 with customized keywords and templates.
64 64 Again, run "hg kwdemo" to control the results of your config changes.
65 65
66 66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 67 the risk of inadvertedly storing expanded keywords in the change history.
68 68
69 69 To force expansion after enabling it, or a configuration change, run
70 70 "hg kwexpand".
71 71
72 72 Also, when committing with the record extension or using mq's qrecord, be aware
73 73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
74 74 question to update keyword expansions after all changes have been checked in.
75 75
76 76 Expansions spanning more than one line and incremental expansions,
77 77 like CVS' $Log$, are not supported. A keyword template map
78 78 "Log = {desc}" expands to the first line of the changeset description.
79 79 '''
80 80
81 81 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
82 82 from mercurial import patch, localrepo, templater, templatefilters, util
83 83 from mercurial.hgweb import webcommands
84 84 from mercurial.node import nullid, hex
85 85 from mercurial.i18n import _
86 86 import re, shutil, tempfile, time
87 87
88 88 commands.optionalrepo += ' kwdemo'
89 89
90 90 # hg commands that do not act on keywords
91 91 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
92 92 ' log outgoing push rename rollback tip verify'
93 93 ' convert email glog')
94 94
95 95 # hg commands that trigger expansion only when writing to working dir,
96 96 # not when reading filelog, and unexpand when reading from working dir
97 97 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
98 98
99 99 def utcdate(date):
100 100 '''Returns hgdate in cvs-like UTC format.'''
101 101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102 102
103 103 # make keyword tools accessible
104 104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
105 105
106 106
107 107 class kwtemplater(object):
108 108 '''
109 109 Sets up keyword templates, corresponding keyword regex, and
110 110 provides keyword substitution functions.
111 111 '''
112 112 templates = {
113 113 'Revision': '{node|short}',
114 114 'Author': '{author|user}',
115 115 'Date': '{date|utcdate}',
116 116 'RCSFile': '{file|basename},v',
117 117 'Source': '{root}/{file},v',
118 118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 120 }
121 121
122 122 def __init__(self, ui, repo):
123 123 self.ui = ui
124 124 self.repo = repo
125 125 self.matcher = util.matcher(repo.root,
126 126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
127 127 self.restrict = kwtools['hgcmd'] in restricted.split()
128 128
129 129 kwmaps = self.ui.configitems('keywordmaps')
130 130 if kwmaps: # override default templates
131 131 kwmaps = [(k, templater.parsestring(v, False))
132 132 for (k, v) in kwmaps]
133 133 self.templates = dict(kwmaps)
134 134 escaped = map(re.escape, self.templates.keys())
135 135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 136 self.re_kw = re.compile(kwpat)
137 137
138 138 templatefilters.filters['utcdate'] = utcdate
139 139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 140 False, None, '', False)
141 141
142 142 def substitute(self, data, path, ctx, subfunc):
143 143 '''Replaces keywords in data with expanded template.'''
144 144 def kwsub(mobj):
145 145 kw = mobj.group(1)
146 146 self.ct.use_template(self.templates[kw])
147 147 self.ui.pushbuffer()
148 148 self.ct.show(ctx, root=self.repo.root, file=path)
149 149 ekw = templatefilters.firstline(self.ui.popbuffer())
150 150 return '$%s: %s $' % (kw, ekw)
151 151 return subfunc(kwsub, data)
152 152
153 153 def expand(self, path, node, data):
154 154 '''Returns data with keywords expanded.'''
155 155 if not self.restrict and self.matcher(path) and not util.binary(data):
156 156 ctx = self.repo.filectx(path, fileid=node).changectx()
157 157 return self.substitute(data, path, ctx, self.re_kw.sub)
158 158 return data
159 159
160 160 def iskwfile(self, path, flagfunc):
161 161 '''Returns true if path matches [keyword] pattern
162 162 and is not a symbolic link.
163 163 Caveat: localrepository._link fails on Windows.'''
164 164 return self.matcher(path) and not 'l' in flagfunc(path)
165 165
166 166 def overwrite(self, node, expand, files):
167 167 '''Overwrites selected files expanding/shrinking keywords.'''
168 168 ctx = self.repo[node]
169 169 mf = ctx.manifest()
170 170 if node is not None: # commit
171 171 files = [f for f in ctx.files() if f in mf]
172 172 notify = self.ui.debug
173 173 else: # kwexpand/kwshrink
174 174 notify = self.ui.note
175 175 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
176 176 if candidates:
177 177 self.restrict = True # do not expand when reading
178 178 action = expand and 'expanding' or 'shrinking'
179 179 for f in candidates:
180 180 fp = self.repo.file(f)
181 181 data = fp.read(mf[f])
182 182 if util.binary(data):
183 183 continue
184 184 if expand:
185 185 if node is None:
186 186 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
187 187 data, found = self.substitute(data, f, ctx,
188 188 self.re_kw.subn)
189 189 else:
190 190 found = self.re_kw.search(data)
191 191 if found:
192 192 notify(_('overwriting %s %s keywords\n') % (f, action))
193 193 self.repo.wwrite(f, data, mf.flags(f))
194 194 self.repo.dirstate.normal(f)
195 195 self.restrict = False
196 196
197 197 def shrinktext(self, text):
198 198 '''Unconditionally removes all keyword substitutions from text.'''
199 199 return self.re_kw.sub(r'$\1$', text)
200 200
201 201 def shrink(self, fname, text):
202 202 '''Returns text with all keyword substitutions removed.'''
203 203 if self.matcher(fname) and not util.binary(text):
204 204 return self.shrinktext(text)
205 205 return text
206 206
207 207 def shrinklines(self, fname, lines):
208 208 '''Returns lines with keyword substitutions removed.'''
209 209 if self.matcher(fname):
210 210 text = ''.join(lines)
211 211 if not util.binary(text):
212 212 return self.shrinktext(text).splitlines(True)
213 213 return lines
214 214
215 215 def wread(self, fname, data):
216 216 '''If in restricted mode returns data read from wdir with
217 217 keyword substitutions removed.'''
218 218 return self.restrict and self.shrink(fname, data) or data
219 219
220 220 class kwfilelog(filelog.filelog):
221 221 '''
222 222 Subclass of filelog to hook into its read, add, cmp methods.
223 223 Keywords are "stored" unexpanded, and processed on reading.
224 224 '''
225 225 def __init__(self, opener, kwt, path):
226 226 super(kwfilelog, self).__init__(opener, path)
227 227 self.kwt = kwt
228 228 self.path = path
229 229
230 230 def read(self, node):
231 231 '''Expands keywords when reading filelog.'''
232 232 data = super(kwfilelog, self).read(node)
233 233 return self.kwt.expand(self.path, node, data)
234 234
235 235 def add(self, text, meta, tr, link, p1=None, p2=None):
236 236 '''Removes keyword substitutions when adding to filelog.'''
237 237 text = self.kwt.shrink(self.path, text)
238 238 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
239 239
240 240 def cmp(self, node, text):
241 241 '''Removes keyword substitutions for comparison.'''
242 242 text = self.kwt.shrink(self.path, text)
243 243 if self.renamed(node):
244 244 t2 = super(kwfilelog, self).read(node)
245 245 return t2 != text
246 246 return revlog.revlog.cmp(self, node, text)
247 247
248 248 def _status(ui, repo, kwt, unknown, *pats, **opts):
249 249 '''Bails out if [keyword] configuration is not active.
250 250 Returns status of working directory.'''
251 251 if kwt:
252 252 matcher = cmdutil.match(repo, pats, opts)
253 253 return repo.status(match=matcher, unknown=unknown, clean=True)
254 254 if ui.configitems('keyword'):
255 255 raise util.Abort(_('[keyword] patterns cannot match'))
256 256 raise util.Abort(_('no [keyword] patterns configured'))
257 257
258 258 def _kwfwrite(ui, repo, expand, *pats, **opts):
259 259 '''Selects files and passes them to kwtemplater.overwrite.'''
260 260 if repo.dirstate.parents()[1] != nullid:
261 261 raise util.Abort(_('outstanding uncommitted merge'))
262 262 kwt = kwtools['templater']
263 263 status = _status(ui, repo, kwt, False, *pats, **opts)
264 264 modified, added, removed, deleted = status[:4]
265 265 if modified or added or removed or deleted:
266 266 raise util.Abort(_('outstanding uncommitted changes'))
267 267 wlock = lock = None
268 268 try:
269 269 wlock = repo.wlock()
270 270 lock = repo.lock()
271 271 kwt.overwrite(None, expand, status[6])
272 272 finally:
273 273 del wlock, lock
274 274
275 275
276 276 def demo(ui, repo, *args, **opts):
277 277 '''print [keywordmaps] configuration and an expansion example
278 278
279 279 Show current, custom, or default keyword template maps
280 280 and their expansion.
281 281
282 282 Extend current configuration by specifying maps as arguments
283 283 and optionally by reading from an additional hgrc file.
284 284
285 285 Override current keyword template maps with "default" option.
286 286 '''
287 287 def demostatus(stat):
288 288 ui.status(_('\n\t%s\n') % stat)
289 289
290 290 def demoitems(section, items):
291 291 ui.write('[%s]\n' % section)
292 292 for k, v in items:
293 293 ui.write('%s = %s\n' % (k, v))
294 294
295 295 msg = 'hg keyword config and expansion example'
296 296 kwstatus = 'current'
297 297 fn = 'demo.txt'
298 298 branchname = 'demobranch'
299 299 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
300 300 ui.note(_('creating temporary repo at %s\n') % tmpdir)
301 301 repo = localrepo.localrepository(ui, tmpdir, True)
302 302 ui.setconfig('keyword', fn, '')
303 303 if args or opts.get('rcfile'):
304 304 kwstatus = 'custom'
305 305 if opts.get('rcfile'):
306 306 ui.readconfig(opts.get('rcfile'))
307 307 if opts.get('default'):
308 308 kwstatus = 'default'
309 309 kwmaps = kwtemplater.templates
310 310 if ui.configitems('keywordmaps'):
311 311 # override maps from optional rcfile
312 312 for k, v in kwmaps.iteritems():
313 313 ui.setconfig('keywordmaps', k, v)
314 314 elif args:
315 315 # simulate hgrc parsing
316 316 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
317 317 fp = repo.opener('hgrc', 'w')
318 318 fp.writelines(rcmaps)
319 319 fp.close()
320 320 ui.readconfig(repo.join('hgrc'))
321 321 if not opts.get('default'):
322 322 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
323 323 uisetup(ui)
324 324 reposetup(ui, repo)
325 325 for k, v in ui.configitems('extensions'):
326 326 if k.endswith('keyword'):
327 327 extension = '%s = %s' % (k, v)
328 328 break
329 329 demostatus('config using %s keyword template maps' % kwstatus)
330 330 ui.write('[extensions]\n%s\n' % extension)
331 331 demoitems('keyword', ui.configitems('keyword'))
332 332 demoitems('keywordmaps', kwmaps.iteritems())
333 333 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
334 334 repo.wopener(fn, 'w').write(keywords)
335 335 repo.add([fn])
336 336 path = repo.wjoin(fn)
337 337 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
338 338 ui.note(keywords)
339 339 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
340 340 # silence branch command if not verbose
341 341 quiet = ui.quiet
342 342 ui.quiet = not ui.verbose
343 343 commands.branch(ui, repo, branchname)
344 344 ui.quiet = quiet
345 345 for name, cmd in ui.configitems('hooks'):
346 346 if name.split('.', 1)[0].find('commit') > -1:
347 347 repo.ui.setconfig('hooks', name, '')
348 348 ui.note(_('unhooked all commit hooks\n'))
349 349 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
350 350 repo.commit(text=msg)
351 351 fmt = ui.verbose and ' in %s' % path or ''
352 352 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
353 353 ui.write(repo.wread(fn))
354 354 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
355 355 shutil.rmtree(tmpdir, ignore_errors=True)
356 356
357 357 def expand(ui, repo, *pats, **opts):
358 358 '''expand keywords in working directory
359 359
360 360 Run after (re)enabling keyword expansion.
361 361
362 362 kwexpand refuses to run if given files contain local changes.
363 363 '''
364 364 # 3rd argument sets expansion to True
365 365 _kwfwrite(ui, repo, True, *pats, **opts)
366 366
367 367 def files(ui, repo, *pats, **opts):
368 368 '''print files currently configured for keyword expansion
369 369
370 370 Crosscheck which files in working directory are potential targets for
371 371 keyword expansion.
372 372 That is, files matched by [keyword] config patterns but not symlinks.
373 373 '''
374 374 kwt = kwtools['templater']
375 375 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
376 376 modified, added, removed, deleted, unknown, ignored, clean = status
377 377 files = util.sort(modified + added + clean + unknown)
378 378 wctx = repo[None]
379 379 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
380 380 cwd = pats and repo.getcwd() or ''
381 381 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
382 382 if opts.get('all') or opts.get('ignore'):
383 383 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
384 384 for char, filenames in kwfstats:
385 385 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
386 386 for f in filenames:
387 387 ui.write(fmt % repo.pathto(f, cwd))
388 388
389 389 def shrink(ui, repo, *pats, **opts):
390 390 '''revert expanded keywords in working directory
391 391
392 392 Run before changing/disabling active keywords
393 393 or if you experience problems with "hg import" or "hg merge".
394 394
395 395 kwshrink refuses to run if given files contain local changes.
396 396 '''
397 397 # 3rd argument sets expansion to False
398 398 _kwfwrite(ui, repo, False, *pats, **opts)
399 399
400 400
401 401 def uisetup(ui):
402 402 '''Collects [keyword] config in kwtools.
403 403 Monkeypatches dispatch._parse if needed.'''
404 404
405 405 for pat, opt in ui.configitems('keyword'):
406 406 if opt != 'ignore':
407 407 kwtools['inc'].append(pat)
408 408 else:
409 409 kwtools['exc'].append(pat)
410 410
411 411 if kwtools['inc']:
412 412 def kwdispatch_parse(orig, ui, args):
413 413 '''Monkeypatch dispatch._parse to obtain running hg command.'''
414 414 cmd, func, args, options, cmdoptions = orig(ui, args)
415 415 kwtools['hgcmd'] = cmd
416 416 return cmd, func, args, options, cmdoptions
417 417
418 418 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
419 419
420 420 def reposetup(ui, repo):
421 421 '''Sets up repo as kwrepo for keyword substitution.
422 422 Overrides file method to return kwfilelog instead of filelog
423 423 if file matches user configuration.
424 424 Wraps commit to overwrite configured files with updated
425 425 keyword substitutions.
426 426 Monkeypatches patch and webcommands.'''
427 427
428 if (not hasattr(repo, 'dirstate') or not kwtools['inc']
429 or kwtools['hgcmd'] in nokwcommands.split()
430 or '.hg' in util.splitpath(repo.root)):
431 return
428 try:
429 if (not repo.local() or not kwtools['inc']
430 or kwtools['hgcmd'] in nokwcommands.split()
431 or '.hg' in util.splitpath(repo.root)
432 or repo._url.startswith('bundle:')):
433 return
434 except AttributeError:
435 pass
432 436
433 437 kwtools['templater'] = kwt = kwtemplater(ui, repo)
434 438
435 439 class kwrepo(repo.__class__):
436 440 def file(self, f):
437 441 if f[0] == '/':
438 442 f = f[1:]
439 443 return kwfilelog(self.sopener, kwt, f)
440 444
441 445 def wread(self, filename):
442 446 data = super(kwrepo, self).wread(filename)
443 447 return kwt.wread(filename, data)
444 448
445 449 def commit(self, files=None, text='', user=None, date=None,
446 450 match=None, force=False, force_editor=False,
447 451 p1=None, p2=None, extra={}, empty_ok=False):
448 452 wlock = lock = None
449 453 _p1 = _p2 = None
450 454 try:
451 455 wlock = self.wlock()
452 456 lock = self.lock()
453 457 # store and postpone commit hooks
454 458 commithooks = {}
455 459 for name, cmd in ui.configitems('hooks'):
456 460 if name.split('.', 1)[0] == 'commit':
457 461 commithooks[name] = cmd
458 462 ui.setconfig('hooks', name, None)
459 463 if commithooks:
460 464 # store parents for commit hook environment
461 465 if p1 is None:
462 466 _p1, _p2 = repo.dirstate.parents()
463 467 else:
464 468 _p1, _p2 = p1, p2 or nullid
465 469 _p1 = hex(_p1)
466 470 if _p2 == nullid:
467 471 _p2 = ''
468 472 else:
469 473 _p2 = hex(_p2)
470 474
471 475 n = super(kwrepo, self).commit(files, text, user, date, match,
472 476 force, force_editor, p1, p2,
473 477 extra, empty_ok)
474 478
475 479 # restore commit hooks
476 480 for name, cmd in commithooks.iteritems():
477 481 ui.setconfig('hooks', name, cmd)
478 482 if n is not None:
479 483 kwt.overwrite(n, True, None)
480 484 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
481 485 return n
482 486 finally:
483 487 del wlock, lock
484 488
485 489 # monkeypatches
486 490 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False):
487 491 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
488 492 rejects or conflicts due to expanded keywords in working dir.'''
489 493 orig(self, ui, fname, opener, missing)
490 494 # shrink keywords read from working dir
491 495 self.lines = kwt.shrinklines(self.fname, self.lines)
492 496
493 497 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
494 498 opts=None):
495 499 '''Monkeypatch patch.diff to avoid expansion except when
496 500 comparing against working dir.'''
497 501 if node2 is not None:
498 502 kwt.matcher = util.never
499 503 elif node1 is not None and node1 != repo['.'].node():
500 504 kwt.restrict = True
501 505 return orig(repo, node1, node2, match, changes, opts)
502 506
503 507 def kwweb_skip(orig, web, req, tmpl):
504 508 '''Wraps webcommands.x turning off keyword expansion.'''
505 509 kwt.matcher = util.never
506 510 return orig(web, req, tmpl)
507 511
508 512 repo.__class__ = kwrepo
509 513
510 514 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
511 515 extensions.wrapfunction(patch, 'diff', kw_diff)
512 516 for c in 'annotate changeset rev filediff diff'.split():
513 517 extensions.wrapfunction(webcommands, c, kwweb_skip)
514 518
515 519 cmdtable = {
516 520 'kwdemo':
517 521 (demo,
518 522 [('d', 'default', None, _('show default keyword template maps')),
519 523 ('f', 'rcfile', [], _('read maps from rcfile'))],
520 524 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
521 525 'kwexpand': (expand, commands.walkopts,
522 526 _('hg kwexpand [OPTION]... [FILE]...')),
523 527 'kwfiles':
524 528 (files,
525 529 [('a', 'all', None, _('show keyword status flags of all files')),
526 530 ('i', 'ignore', None, _('show files excluded from expansion')),
527 531 ('u', 'untracked', None, _('additionally show untracked files')),
528 532 ] + commands.walkopts,
529 533 _('hg kwfiles [OPTION]... [FILE]...')),
530 534 'kwshrink': (shrink, commands.walkopts,
531 535 _('hg kwshrink [OPTION]... [FILE]...')),
532 536 }
@@ -1,298 +1,298 b''
1 1 """
2 2 bundlerepo.py - repository class for viewing uncompressed bundles
3 3
4 4 This provides a read-only repository interface to bundles as if
5 5 they were part of the actual repository.
6 6
7 7 Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
8 8
9 9 This software may be used and distributed according to the terms
10 10 of the GNU General Public License, incorporated herein by reference.
11 11 """
12 12
13 13 from node import hex, nullid, short
14 14 from i18n import _
15 15 import changegroup, util, os, struct, bz2, zlib, tempfile, shutil, mdiff
16 16 import repo, localrepo, changelog, manifest, filelog, revlog, context, error
17 17
18 18 class bundlerevlog(revlog.revlog):
19 19 def __init__(self, opener, indexfile, bundlefile,
20 20 linkmapper=None):
21 21 # How it works:
22 22 # to retrieve a revision, we need to know the offset of
23 23 # the revision in the bundlefile (an opened file).
24 24 #
25 25 # We store this offset in the index (start), to differentiate a
26 26 # rev in the bundle and from a rev in the revlog, we check
27 27 # len(index[r]). If the tuple is bigger than 7, it is a bundle
28 28 # (it is bigger since we store the node to which the delta is)
29 29 #
30 30 revlog.revlog.__init__(self, opener, indexfile)
31 31 self.bundlefile = bundlefile
32 32 self.basemap = {}
33 33 def chunkpositer():
34 34 for chunk in changegroup.chunkiter(bundlefile):
35 35 pos = bundlefile.tell()
36 36 yield chunk, pos - len(chunk)
37 37 n = len(self)
38 38 prev = None
39 39 for chunk, start in chunkpositer():
40 40 size = len(chunk)
41 41 if size < 80:
42 42 raise util.Abort(_("invalid changegroup"))
43 43 start += 80
44 44 size -= 80
45 45 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
46 46 if node in self.nodemap:
47 47 prev = node
48 48 continue
49 49 for p in (p1, p2):
50 50 if not p in self.nodemap:
51 51 raise error.LookupError(p1, self.indexfile,
52 52 _("unknown parent"))
53 53 if linkmapper is None:
54 54 link = n
55 55 else:
56 56 link = linkmapper(cs)
57 57
58 58 if not prev:
59 59 prev = p1
60 60 # start, size, full unc. size, base (unused), link, p1, p2, node
61 61 e = (revlog.offset_type(start, 0), size, -1, -1, link,
62 62 self.rev(p1), self.rev(p2), node)
63 63 self.basemap[n] = prev
64 64 self.index.insert(-1, e)
65 65 self.nodemap[node] = n
66 66 prev = node
67 67 n += 1
68 68
69 69 def bundle(self, rev):
70 70 """is rev from the bundle"""
71 71 if rev < 0:
72 72 return False
73 73 return rev in self.basemap
74 74 def bundlebase(self, rev): return self.basemap[rev]
75 75 def chunk(self, rev, df=None, cachelen=4096):
76 76 # Warning: in case of bundle, the diff is against bundlebase,
77 77 # not against rev - 1
78 78 # XXX: could use some caching
79 79 if not self.bundle(rev):
80 80 return revlog.revlog.chunk(self, rev, df)
81 81 self.bundlefile.seek(self.start(rev))
82 82 return self.bundlefile.read(self.length(rev))
83 83
84 84 def revdiff(self, rev1, rev2):
85 85 """return or calculate a delta between two revisions"""
86 86 if self.bundle(rev1) and self.bundle(rev2):
87 87 # hot path for bundle
88 88 revb = self.rev(self.bundlebase(rev2))
89 89 if revb == rev1:
90 90 return self.chunk(rev2)
91 91 elif not self.bundle(rev1) and not self.bundle(rev2):
92 92 return revlog.revlog.revdiff(self, rev1, rev2)
93 93
94 94 return mdiff.textdiff(self.revision(self.node(rev1)),
95 95 self.revision(self.node(rev2)))
96 96
97 97 def revision(self, node):
98 98 """return an uncompressed revision of a given"""
99 99 if node == nullid: return ""
100 100
101 101 text = None
102 102 chain = []
103 103 iter_node = node
104 104 rev = self.rev(iter_node)
105 105 # reconstruct the revision if it is from a changegroup
106 106 while self.bundle(rev):
107 107 if self._cache and self._cache[0] == iter_node:
108 108 text = self._cache[2]
109 109 break
110 110 chain.append(rev)
111 111 iter_node = self.bundlebase(rev)
112 112 rev = self.rev(iter_node)
113 113 if text is None:
114 114 text = revlog.revlog.revision(self, iter_node)
115 115
116 116 while chain:
117 117 delta = self.chunk(chain.pop())
118 118 text = mdiff.patches(text, [delta])
119 119
120 120 p1, p2 = self.parents(node)
121 121 if node != revlog.hash(text, p1, p2):
122 122 raise error.RevlogError(_("integrity check failed on %s:%d")
123 123 % (self.datafile, self.rev(node)))
124 124
125 125 self._cache = (node, self.rev(node), text)
126 126 return text
127 127
128 128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
129 129 raise NotImplementedError
130 130 def addgroup(self, revs, linkmapper, transaction):
131 131 raise NotImplementedError
132 132 def strip(self, rev, minlink):
133 133 raise NotImplementedError
134 134 def checksize(self):
135 135 raise NotImplementedError
136 136
137 137 class bundlechangelog(bundlerevlog, changelog.changelog):
138 138 def __init__(self, opener, bundlefile):
139 139 changelog.changelog.__init__(self, opener)
140 140 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile)
141 141
142 142 class bundlemanifest(bundlerevlog, manifest.manifest):
143 143 def __init__(self, opener, bundlefile, linkmapper):
144 144 manifest.manifest.__init__(self, opener)
145 145 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
146 146 linkmapper)
147 147
148 148 class bundlefilelog(bundlerevlog, filelog.filelog):
149 149 def __init__(self, opener, path, bundlefile, linkmapper):
150 150 filelog.filelog.__init__(self, opener, path)
151 151 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile,
152 152 linkmapper)
153 153
154 154 class bundlerepository(localrepo.localrepository):
155 155 def __init__(self, ui, path, bundlename):
156 156 self._tempparent = None
157 157 try:
158 158 localrepo.localrepository.__init__(self, ui, path)
159 159 except error.RepoError:
160 160 self._tempparent = tempfile.mkdtemp()
161 161 tmprepo = localrepo.instance(ui,self._tempparent,1)
162 162 localrepo.localrepository.__init__(self, ui, self._tempparent)
163 163
164 164 if path:
165 165 self._url = 'bundle:' + path + '+' + bundlename
166 166 else:
167 167 self._url = 'bundle:' + bundlename
168 168
169 169 self.tempfile = None
170 170 self.bundlefile = open(bundlename, "rb")
171 171 header = self.bundlefile.read(6)
172 172 if not header.startswith("HG"):
173 173 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
174 174 elif not header.startswith("HG10"):
175 175 raise util.Abort(_("%s: unknown bundle version") % bundlename)
176 176 elif (header == "HG10BZ") or (header == "HG10GZ"):
177 177 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
178 178 suffix=".hg10un", dir=self.path)
179 179 self.tempfile = temp
180 180 fptemp = os.fdopen(fdtemp, 'wb')
181 181 def generator(f):
182 182 if header == "HG10BZ":
183 183 zd = bz2.BZ2Decompressor()
184 184 zd.decompress("BZ")
185 185 elif header == "HG10GZ":
186 186 zd = zlib.decompressobj()
187 187 for chunk in f:
188 188 yield zd.decompress(chunk)
189 189 gen = generator(util.filechunkiter(self.bundlefile, 4096))
190 190
191 191 try:
192 192 fptemp.write("HG10UN")
193 193 for chunk in gen:
194 194 fptemp.write(chunk)
195 195 finally:
196 196 fptemp.close()
197 197 self.bundlefile.close()
198 198
199 199 self.bundlefile = open(self.tempfile, "rb")
200 200 # seek right after the header
201 201 self.bundlefile.seek(6)
202 202 elif header == "HG10UN":
203 203 # nothing to do
204 204 pass
205 205 else:
206 206 raise util.Abort(_("%s: unknown bundle compression type")
207 207 % bundlename)
208 208 # dict with the mapping 'filename' -> position in the bundle
209 209 self.bundlefilespos = {}
210 210
211 211 def __getattr__(self, name):
212 212 if name == 'changelog':
213 213 self.changelog = bundlechangelog(self.sopener, self.bundlefile)
214 214 self.manstart = self.bundlefile.tell()
215 215 return self.changelog
216 216 elif name == 'manifest':
217 217 self.bundlefile.seek(self.manstart)
218 218 self.manifest = bundlemanifest(self.sopener, self.bundlefile,
219 219 self.changelog.rev)
220 220 self.filestart = self.bundlefile.tell()
221 221 return self.manifest
222 222 elif name == 'manstart':
223 223 self.changelog
224 224 return self.manstart
225 225 elif name == 'filestart':
226 226 self.manifest
227 227 return self.filestart
228 228 else:
229 raise AttributeError(name)
229 return localrepo.localrepository.__getattr__(self, name)
230 230
231 231 def url(self):
232 232 return self._url
233 233
234 234 def file(self, f):
235 235 if not self.bundlefilespos:
236 236 self.bundlefile.seek(self.filestart)
237 237 while 1:
238 238 chunk = changegroup.getchunk(self.bundlefile)
239 239 if not chunk:
240 240 break
241 241 self.bundlefilespos[chunk] = self.bundlefile.tell()
242 242 for c in changegroup.chunkiter(self.bundlefile):
243 243 pass
244 244
245 245 if f[0] == '/':
246 246 f = f[1:]
247 247 if f in self.bundlefilespos:
248 248 self.bundlefile.seek(self.bundlefilespos[f])
249 249 return bundlefilelog(self.sopener, f, self.bundlefile,
250 250 self.changelog.rev)
251 251 else:
252 252 return filelog.filelog(self.sopener, f)
253 253
254 254 def close(self):
255 255 """Close assigned bundle file immediately."""
256 256 self.bundlefile.close()
257 257
258 258 def __del__(self):
259 259 bundlefile = getattr(self, 'bundlefile', None)
260 260 if bundlefile and not bundlefile.closed:
261 261 bundlefile.close()
262 262 tempfile = getattr(self, 'tempfile', None)
263 263 if tempfile is not None:
264 264 os.unlink(tempfile)
265 265 if self._tempparent:
266 266 shutil.rmtree(self._tempparent, True)
267 267
268 268 def cancopy(self):
269 269 return False
270 270
271 271 def getcwd(self):
272 272 return os.getcwd() # always outside the repo
273 273
274 274 def instance(ui, path, create):
275 275 if create:
276 276 raise util.Abort(_('cannot create new bundle repository'))
277 277 parentpath = ui.config("bundle", "mainreporoot", "")
278 278 if parentpath:
279 279 # Try to make the full path relative so we get a nice, short URL.
280 280 # In particular, we don't want temp dir names in test outputs.
281 281 cwd = os.getcwd()
282 282 if parentpath == cwd:
283 283 parentpath = ''
284 284 else:
285 285 cwd = os.path.join(cwd,'')
286 286 if parentpath.startswith(cwd):
287 287 parentpath = parentpath[len(cwd):]
288 288 path = util.drop_scheme('file', path)
289 289 if path.startswith('bundle:'):
290 290 path = util.drop_scheme('bundle', path)
291 291 s = path.split("+", 1)
292 292 if len(s) == 1:
293 293 repopath, bundlename = parentpath, s[0]
294 294 else:
295 295 repopath, bundlename = s
296 296 else:
297 297 repopath, bundlename = parentpath, path
298 298 return bundlerepository(ui, repopath, bundlename)
@@ -1,142 +1,149 b''
1 1 #!/bin/sh
2 2
3 3 cp "$TESTDIR"/printenv.py .
4 4
5 5 echo "====== Setting up test"
6 6 hg init test
7 7 cd test
8 8 echo 0 > afile
9 9 hg add afile
10 10 hg commit -m "0.0" -d "1000000 0"
11 11 echo 1 >> afile
12 12 hg commit -m "0.1" -d "1000000 0"
13 13 echo 2 >> afile
14 14 hg commit -m "0.2" -d "1000000 0"
15 15 echo 3 >> afile
16 16 hg commit -m "0.3" -d "1000000 0"
17 17 hg update -C 0
18 18 echo 1 >> afile
19 19 hg commit -m "1.1" -d "1000000 0"
20 20 echo 2 >> afile
21 21 hg commit -m "1.2" -d "1000000 0"
22 22 echo "a line" > fred
23 23 echo 3 >> afile
24 24 hg add fred
25 25 hg commit -m "1.3" -d "1000000 0"
26 26 hg mv afile adifferentfile
27 27 hg commit -m "1.3m" -d "1000000 0"
28 28 hg update -C 3
29 29 hg mv afile anotherfile
30 30 hg commit -m "0.3m" -d "1000000 0"
31 31 hg verify
32 32 cd ..
33 33 hg init empty
34 34
35 35 echo "====== Bundle --all"
36 36 hg -R test bundle --all all.hg
37
37 38 echo "====== Bundle test to full.hg"
38 39 hg -R test bundle full.hg empty
39 40 echo "====== Unbundle full.hg in test"
40 41 hg -R test unbundle full.hg
41 42 echo "====== Verify empty"
42 43 hg -R empty heads
43 44 hg -R empty verify
44 45
45 46 echo "====== Pull full.hg into test (using --cwd)"
46 47 hg --cwd test pull ../full.hg
47 48 echo "====== Pull full.hg into empty (using --cwd)"
48 49 hg --cwd empty pull ../full.hg
49 50 echo "====== Rollback empty"
50 51 hg -R empty rollback
51 52 echo "====== Pull full.hg into empty again (using --cwd)"
52 53 hg --cwd empty pull ../full.hg
53 54
54 55 echo "====== Pull full.hg into test (using -R)"
55 56 hg -R test pull full.hg
56 57 echo "====== Pull full.hg into empty (using -R)"
57 58 hg -R empty pull full.hg
58 59 echo "====== Rollback empty"
59 60 hg -R empty rollback
60 61 echo "====== Pull full.hg into empty again (using -R)"
61 62 hg -R empty pull full.hg
62 63
63 64 echo "====== Log -R full.hg in fresh empty"
64 65 rm -r empty
65 66 hg init empty
66 67 cd empty
67 68 hg -R bundle://../full.hg log
68 69
69 70 echo "====== Pull ../full.hg into empty (with hook)"
70 71 echo '[hooks]' >> .hg/hgrc
71 72 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
72 73 #doesn't work (yet ?)
73 74 #hg -R bundle://../full.hg verify
74 75 hg pull bundle://../full.hg
75 76 echo "====== Rollback empty"
76 77 hg rollback
77 78 cd ..
78 79 echo "====== Log -R bundle:empty+full.hg"
79 80 hg -R bundle:empty+full.hg log --template="{rev} "
80 81 echo ""
81 82 echo "====== Pull full.hg into empty again (using -R; with hook)"
82 83 hg -R empty pull full.hg
83 84
84 85 echo "====== Create partial clones"
85 86 rm -r empty
86 87 hg init empty
87 88 hg clone -r 3 test partial
88 89 hg clone partial partial2
89 90 cd partial
90 91 echo "====== Log -R full.hg in partial"
91 92 hg -R bundle://../full.hg log
92 93 echo "====== Incoming full.hg in partial"
93 94 hg incoming bundle://../full.hg
94 95 echo "====== Outgoing -R full.hg vs partial2 in partial"
95 96 hg -R bundle://../full.hg outgoing ../partial2
96 97 echo "====== Outgoing -R does-not-exist.hg vs partial2 in partial"
97 98 hg -R bundle://../does-not-exist.hg outgoing ../partial2
98 99 cd ..
99 100
100 101 echo "====== Direct clone from bundle (all-history)"
101 102 hg clone full.hg full-clone
102 103 hg -R full-clone heads
103 104 rm -r full-clone
104 105
105 106 # test for http://www.selenic.com/mercurial/bts/issue216
106 107 echo "====== Unbundle incremental bundles into fresh empty in one go"
107 108 rm -r empty
108 109 hg init empty
109 110 hg -R test bundle --base null -r 0 ../0.hg
110 111 hg -R test bundle --base 0 -r 1 ../1.hg
111 112 hg -R empty unbundle -u ../0.hg ../1.hg
112 113
113 114 # test for 540d1059c802
114 115 echo "====== test for 540d1059c802"
115 116 hg init orig
116 117 cd orig
117 118 echo foo > foo
118 119 hg add foo
119 120 hg ci -m 'add foo' -d '0 0'
120 121
121 122 hg clone . ../copy
122 123 hg tag -d '0 0' foo
123 124
124 125 cd ../copy
125 126 echo >> foo
126 127 hg ci -m 'change foo' -d '0 0'
127 128 hg bundle ../bundle.hg ../orig
128 129
129 130 cd ../orig
130 131 hg incoming ../bundle.hg
131 132 cd ..
132 133
133 134 # test for http://www.selenic.com/mercurial/bts/issue1144
134 135 echo "===== test that verify bundle does not traceback"
135 136 # partial history bundle, fails w/ unkown parent
136 137 hg -R bundle.hg verify
137 138 # full history bundle, refuses to verify non-local repo
138 139 hg -R all.hg verify
139 140 # but, regular verify must continue to work
140 141 hg -R orig verify
141 142
143 echo "====== diff against bundle"
144 hg init b
145 cd b
146 hg -R ../all.hg diff -r tip
147 cd ..
142 148
149
@@ -1,319 +1,328 b''
1 1 ====== Setting up test
2 2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 3 created new head
4 4 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
5 5 checking changesets
6 6 checking manifests
7 7 crosschecking files in changesets and manifests
8 8 checking files
9 9 4 files, 9 changesets, 7 total revisions
10 10 ====== Bundle --all
11 11 9 changesets found
12 12 ====== Bundle test to full.hg
13 13 searching for changes
14 14 9 changesets found
15 15 ====== Unbundle full.hg in test
16 16 adding changesets
17 17 adding manifests
18 18 adding file changes
19 19 added 0 changesets with 0 changes to 4 files
20 20 (run 'hg update' to get a working copy)
21 21 ====== Verify empty
22 22 changeset: -1:000000000000
23 23 tag: tip
24 24 user:
25 25 date: Thu Jan 01 00:00:00 1970 +0000
26 26
27 27 checking changesets
28 28 checking manifests
29 29 crosschecking files in changesets and manifests
30 30 checking files
31 31 0 files, 0 changesets, 0 total revisions
32 32 ====== Pull full.hg into test (using --cwd)
33 33 pulling from ../full.hg
34 34 searching for changes
35 35 no changes found
36 36 ====== Pull full.hg into empty (using --cwd)
37 37 pulling from ../full.hg
38 38 requesting all changes
39 39 adding changesets
40 40 adding manifests
41 41 adding file changes
42 42 added 9 changesets with 7 changes to 4 files (+1 heads)
43 43 (run 'hg heads' to see heads, 'hg merge' to merge)
44 44 ====== Rollback empty
45 45 rolling back last transaction
46 46 ====== Pull full.hg into empty again (using --cwd)
47 47 pulling from ../full.hg
48 48 requesting all changes
49 49 adding changesets
50 50 adding manifests
51 51 adding file changes
52 52 added 9 changesets with 7 changes to 4 files (+1 heads)
53 53 (run 'hg heads' to see heads, 'hg merge' to merge)
54 54 ====== Pull full.hg into test (using -R)
55 55 pulling from full.hg
56 56 searching for changes
57 57 no changes found
58 58 ====== Pull full.hg into empty (using -R)
59 59 pulling from full.hg
60 60 searching for changes
61 61 no changes found
62 62 ====== Rollback empty
63 63 rolling back last transaction
64 64 ====== Pull full.hg into empty again (using -R)
65 65 pulling from full.hg
66 66 requesting all changes
67 67 adding changesets
68 68 adding manifests
69 69 adding file changes
70 70 added 9 changesets with 7 changes to 4 files (+1 heads)
71 71 (run 'hg heads' to see heads, 'hg merge' to merge)
72 72 ====== Log -R full.hg in fresh empty
73 73 changeset: 8:836ac62537ab
74 74 tag: tip
75 75 parent: 3:ac69c658229d
76 76 user: test
77 77 date: Mon Jan 12 13:46:40 1970 +0000
78 78 summary: 0.3m
79 79
80 80 changeset: 7:80fe151401c2
81 81 user: test
82 82 date: Mon Jan 12 13:46:40 1970 +0000
83 83 summary: 1.3m
84 84
85 85 changeset: 6:1e3f6b843bd6
86 86 user: test
87 87 date: Mon Jan 12 13:46:40 1970 +0000
88 88 summary: 1.3
89 89
90 90 changeset: 5:024e4e7df376
91 91 user: test
92 92 date: Mon Jan 12 13:46:40 1970 +0000
93 93 summary: 1.2
94 94
95 95 changeset: 4:5f4f3ceb285e
96 96 parent: 0:5649c9d34dd8
97 97 user: test
98 98 date: Mon Jan 12 13:46:40 1970 +0000
99 99 summary: 1.1
100 100
101 101 changeset: 3:ac69c658229d
102 102 user: test
103 103 date: Mon Jan 12 13:46:40 1970 +0000
104 104 summary: 0.3
105 105
106 106 changeset: 2:d62976ca1e50
107 107 user: test
108 108 date: Mon Jan 12 13:46:40 1970 +0000
109 109 summary: 0.2
110 110
111 111 changeset: 1:10b2180f755b
112 112 user: test
113 113 date: Mon Jan 12 13:46:40 1970 +0000
114 114 summary: 0.1
115 115
116 116 changeset: 0:5649c9d34dd8
117 117 user: test
118 118 date: Mon Jan 12 13:46:40 1970 +0000
119 119 summary: 0.0
120 120
121 121 ====== Pull ../full.hg into empty (with hook)
122 122 changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:../full.hg
123 123 pulling from bundle://../full.hg
124 124 requesting all changes
125 125 adding changesets
126 126 adding manifests
127 127 adding file changes
128 128 added 9 changesets with 7 changes to 4 files (+1 heads)
129 129 (run 'hg heads' to see heads, 'hg merge' to merge)
130 130 ====== Rollback empty
131 131 rolling back last transaction
132 132 ====== Log -R bundle:empty+full.hg
133 133 8 7 6 5 4 3 2 1 0
134 134 ====== Pull full.hg into empty again (using -R; with hook)
135 135 changegroup hook: HG_NODE=5649c9d34dd87d0ecb5fd39672128376e83b22e1 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
136 136 pulling from full.hg
137 137 requesting all changes
138 138 adding changesets
139 139 adding manifests
140 140 adding file changes
141 141 added 9 changesets with 7 changes to 4 files (+1 heads)
142 142 (run 'hg heads' to see heads, 'hg merge' to merge)
143 143 ====== Create partial clones
144 144 requesting all changes
145 145 adding changesets
146 146 adding manifests
147 147 adding file changes
148 148 added 4 changesets with 4 changes to 1 files
149 149 updating working directory
150 150 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 151 updating working directory
152 152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 153 ====== Log -R full.hg in partial
154 154 changeset: 8:836ac62537ab
155 155 tag: tip
156 156 parent: 3:ac69c658229d
157 157 user: test
158 158 date: Mon Jan 12 13:46:40 1970 +0000
159 159 summary: 0.3m
160 160
161 161 changeset: 7:80fe151401c2
162 162 user: test
163 163 date: Mon Jan 12 13:46:40 1970 +0000
164 164 summary: 1.3m
165 165
166 166 changeset: 6:1e3f6b843bd6
167 167 user: test
168 168 date: Mon Jan 12 13:46:40 1970 +0000
169 169 summary: 1.3
170 170
171 171 changeset: 5:024e4e7df376
172 172 user: test
173 173 date: Mon Jan 12 13:46:40 1970 +0000
174 174 summary: 1.2
175 175
176 176 changeset: 4:5f4f3ceb285e
177 177 parent: 0:5649c9d34dd8
178 178 user: test
179 179 date: Mon Jan 12 13:46:40 1970 +0000
180 180 summary: 1.1
181 181
182 182 changeset: 3:ac69c658229d
183 183 user: test
184 184 date: Mon Jan 12 13:46:40 1970 +0000
185 185 summary: 0.3
186 186
187 187 changeset: 2:d62976ca1e50
188 188 user: test
189 189 date: Mon Jan 12 13:46:40 1970 +0000
190 190 summary: 0.2
191 191
192 192 changeset: 1:10b2180f755b
193 193 user: test
194 194 date: Mon Jan 12 13:46:40 1970 +0000
195 195 summary: 0.1
196 196
197 197 changeset: 0:5649c9d34dd8
198 198 user: test
199 199 date: Mon Jan 12 13:46:40 1970 +0000
200 200 summary: 0.0
201 201
202 202 ====== Incoming full.hg in partial
203 203 comparing with bundle://../full.hg
204 204 searching for changes
205 205 changeset: 4:5f4f3ceb285e
206 206 parent: 0:5649c9d34dd8
207 207 user: test
208 208 date: Mon Jan 12 13:46:40 1970 +0000
209 209 summary: 1.1
210 210
211 211 changeset: 5:024e4e7df376
212 212 user: test
213 213 date: Mon Jan 12 13:46:40 1970 +0000
214 214 summary: 1.2
215 215
216 216 changeset: 6:1e3f6b843bd6
217 217 user: test
218 218 date: Mon Jan 12 13:46:40 1970 +0000
219 219 summary: 1.3
220 220
221 221 changeset: 7:80fe151401c2
222 222 user: test
223 223 date: Mon Jan 12 13:46:40 1970 +0000
224 224 summary: 1.3m
225 225
226 226 changeset: 8:836ac62537ab
227 227 tag: tip
228 228 parent: 3:ac69c658229d
229 229 user: test
230 230 date: Mon Jan 12 13:46:40 1970 +0000
231 231 summary: 0.3m
232 232
233 233 ====== Outgoing -R full.hg vs partial2 in partial
234 234 comparing with ../partial2
235 235 searching for changes
236 236 changeset: 4:5f4f3ceb285e
237 237 parent: 0:5649c9d34dd8
238 238 user: test
239 239 date: Mon Jan 12 13:46:40 1970 +0000
240 240 summary: 1.1
241 241
242 242 changeset: 5:024e4e7df376
243 243 user: test
244 244 date: Mon Jan 12 13:46:40 1970 +0000
245 245 summary: 1.2
246 246
247 247 changeset: 6:1e3f6b843bd6
248 248 user: test
249 249 date: Mon Jan 12 13:46:40 1970 +0000
250 250 summary: 1.3
251 251
252 252 changeset: 7:80fe151401c2
253 253 user: test
254 254 date: Mon Jan 12 13:46:40 1970 +0000
255 255 summary: 1.3m
256 256
257 257 changeset: 8:836ac62537ab
258 258 tag: tip
259 259 parent: 3:ac69c658229d
260 260 user: test
261 261 date: Mon Jan 12 13:46:40 1970 +0000
262 262 summary: 0.3m
263 263
264 264 ====== Outgoing -R does-not-exist.hg vs partial2 in partial
265 265 abort: No such file or directory: ../does-not-exist.hg
266 266 ====== Direct clone from bundle (all-history)
267 267 requesting all changes
268 268 adding changesets
269 269 adding manifests
270 270 adding file changes
271 271 added 9 changesets with 7 changes to 4 files (+1 heads)
272 272 updating working directory
273 273 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 274 changeset: 8:836ac62537ab
275 275 tag: tip
276 276 parent: 3:ac69c658229d
277 277 user: test
278 278 date: Mon Jan 12 13:46:40 1970 +0000
279 279 summary: 0.3m
280 280
281 281 changeset: 7:80fe151401c2
282 282 user: test
283 283 date: Mon Jan 12 13:46:40 1970 +0000
284 284 summary: 1.3m
285 285
286 286 ====== Unbundle incremental bundles into fresh empty in one go
287 287 1 changesets found
288 288 1 changesets found
289 289 adding changesets
290 290 adding manifests
291 291 adding file changes
292 292 added 1 changesets with 1 changes to 1 files
293 293 adding changesets
294 294 adding manifests
295 295 adding file changes
296 296 added 1 changesets with 1 changes to 1 files
297 297 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 298 ====== test for 540d1059c802
299 299 updating working directory
300 300 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 301 searching for changes
302 302 1 changesets found
303 303 comparing with ../bundle.hg
304 304 searching for changes
305 305 changeset: 2:ed1b79f46b9a
306 306 tag: tip
307 307 parent: 0:bbd179dfa0a7
308 308 user: test
309 309 date: Thu Jan 01 00:00:00 1970 +0000
310 310 summary: change foo
311 311
312 312 ===== test that verify bundle does not traceback
313 313 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
314 314 abort: cannot verify bundle or remote repos
315 315 checking changesets
316 316 checking manifests
317 317 crosschecking files in changesets and manifests
318 318 checking files
319 319 2 files, 2 changesets, 2 total revisions
320 ====== diff against bundle
321 diff -r 836ac62537ab anotherfile
322 --- a/anotherfile Mon Jan 12 13:46:40 1970 +0000
323 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
324 @@ -1,4 +0,0 @@
325 -0
326 -1
327 -2
328 -3
General Comments 0
You need to be logged in to leave comments. Login now