##// END OF EJS Templates
import: import each patch in a file or stream as a separate change...
Brendan Cully -
r10384:832f3538 default
parent child Browse files
Show More
@@ -1,3779 +1,3803 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, difflib, time, tempfile
12 12 import hg, util, revlog, bundlerepo, extensions, copies, error
13 13 import patch, help, mdiff, url, encoding, templatekw
14 14 import archival, changegroup, cmdutil, sshserver, hbisect
15 15 from hgweb import server
16 16 import merge as merge_
17 17 import minirst
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the
25 25 repository.
26 26
27 27 The files will be added to the repository at the next commit. To
28 28 undo an add before that, see hg forget.
29 29
30 30 If no names are given, add all files to the repository.
31 31 """
32 32
33 33 bad = []
34 34 names = []
35 35 m = cmdutil.match(repo, pats, opts)
36 36 oldbad = m.bad
37 37 m.bad = lambda x, y: bad.append(x) or oldbad(x, y)
38 38
39 39 for f in repo.walk(m):
40 40 exact = m.exact(f)
41 41 if exact or f not in repo.dirstate:
42 42 names.append(f)
43 43 if ui.verbose or not exact:
44 44 ui.status(_('adding %s\n') % m.rel(f))
45 45 if not opts.get('dry_run'):
46 46 bad += [f for f in repo.add(names) if f in m.files()]
47 47 return bad and 1 or 0
48 48
49 49 def addremove(ui, repo, *pats, **opts):
50 50 """add all new files, delete all missing files
51 51
52 52 Add all new files and remove all missing files from the
53 53 repository.
54 54
55 55 New files are ignored if they match any of the patterns in
56 56 .hgignore. As with add, these changes take effect at the next
57 57 commit.
58 58
59 59 Use the -s/--similarity option to detect renamed files. With a
60 60 parameter greater than 0, this compares every removed file with
61 61 every added file and records those similar enough as renames. This
62 62 option takes a percentage between 0 (disabled) and 100 (files must
63 63 be identical) as its parameter. Detecting renamed files this way
64 64 can be expensive.
65 65 """
66 66 try:
67 67 sim = float(opts.get('similarity') or 0)
68 68 except ValueError:
69 69 raise util.Abort(_('similarity must be a number'))
70 70 if sim < 0 or sim > 100:
71 71 raise util.Abort(_('similarity must be between 0 and 100'))
72 72 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
73 73
74 74 def annotate(ui, repo, *pats, **opts):
75 75 """show changeset information by line for each file
76 76
77 77 List changes in files, showing the revision id responsible for
78 78 each line
79 79
80 80 This command is useful for discovering when a change was made and
81 81 by whom.
82 82
83 83 Without the -a/--text option, annotate will avoid processing files
84 84 it detects as binary. With -a, annotate will annotate the file
85 85 anyway, although the results will probably be neither useful
86 86 nor desirable.
87 87 """
88 88 datefunc = ui.quiet and util.shortdate or util.datestr
89 89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90 90
91 91 if not pats:
92 92 raise util.Abort(_('at least one filename or pattern is required'))
93 93
94 94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 95 ('number', lambda x: str(x[0].rev())),
96 96 ('changeset', lambda x: short(x[0].node())),
97 97 ('date', getdate),
98 98 ('file', lambda x: x[0].path()),
99 99 ]
100 100
101 101 if (not opts.get('user') and not opts.get('changeset')
102 102 and not opts.get('date') and not opts.get('file')):
103 103 opts['number'] = 1
104 104
105 105 linenumber = opts.get('line_number') is not None
106 106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108 108
109 109 funcmap = [func for op, func in opmap if opts.get(op)]
110 110 if linenumber:
111 111 lastfunc = funcmap[-1]
112 112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113 113
114 114 ctx = repo[opts.get('rev')]
115 115 m = cmdutil.match(repo, pats, opts)
116 116 follow = not opts.get('no_follow')
117 117 for abs in ctx.walk(m):
118 118 fctx = ctx[abs]
119 119 if not opts.get('text') and util.binary(fctx.data()):
120 120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 121 continue
122 122
123 123 lines = fctx.annotate(follow=follow, linenumber=linenumber)
124 124 pieces = []
125 125
126 126 for f in funcmap:
127 127 l = [f(n) for n, dummy in lines]
128 128 if l:
129 129 ml = max(map(len, l))
130 130 pieces.append(["%*s" % (ml, x) for x in l])
131 131
132 132 if pieces:
133 133 for p, l in zip(zip(*pieces), lines):
134 134 ui.write("%s: %s" % (" ".join(p), l[1]))
135 135
136 136 def archive(ui, repo, dest, **opts):
137 137 '''create an unversioned archive of a repository revision
138 138
139 139 By default, the revision used is the parent of the working
140 140 directory; use -r/--rev to specify a different revision.
141 141
142 142 To specify the type of archive to create, use -t/--type. Valid
143 143 types are:
144 144
145 145 :``files``: a directory full of files (default)
146 146 :``tar``: tar archive, uncompressed
147 147 :``tbz2``: tar archive, compressed using bzip2
148 148 :``tgz``: tar archive, compressed using gzip
149 149 :``uzip``: zip archive, uncompressed
150 150 :``zip``: zip archive, compressed using deflate
151 151
152 152 The exact name of the destination archive or directory is given
153 153 using a format string; see 'hg help export' for details.
154 154
155 155 Each member added to an archive file has a directory prefix
156 156 prepended. Use -p/--prefix to specify a format string for the
157 157 prefix. The default is the basename of the archive, with suffixes
158 158 removed.
159 159 '''
160 160
161 161 ctx = repo[opts.get('rev')]
162 162 if not ctx:
163 163 raise util.Abort(_('no working directory: please specify a revision'))
164 164 node = ctx.node()
165 165 dest = cmdutil.make_filename(repo, dest, node)
166 166 if os.path.realpath(dest) == repo.root:
167 167 raise util.Abort(_('repository root cannot be destination'))
168 168 matchfn = cmdutil.match(repo, [], opts)
169 169 kind = opts.get('type') or 'files'
170 170 prefix = opts.get('prefix')
171 171 if dest == '-':
172 172 if kind == 'files':
173 173 raise util.Abort(_('cannot archive plain files to stdout'))
174 174 dest = sys.stdout
175 175 if not prefix:
176 176 prefix = os.path.basename(repo.root) + '-%h'
177 177 prefix = cmdutil.make_filename(repo, prefix, node)
178 178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 179 matchfn, prefix)
180 180
181 181 def backout(ui, repo, node=None, rev=None, **opts):
182 182 '''reverse effect of earlier changeset
183 183
184 184 Commit the backed out changes as a new changeset. The new
185 185 changeset is a child of the backed out changeset.
186 186
187 187 If you backout a changeset other than the tip, a new head is
188 188 created. This head will be the new tip and you should merge this
189 189 backout changeset with another head.
190 190
191 191 The --merge option remembers the parent of the working directory
192 192 before starting the backout, then merges the new head with that
193 193 changeset afterwards. This saves you from doing the merge by hand.
194 194 The result of this merge is not committed, as with a normal merge.
195 195
196 196 See 'hg help dates' for a list of formats valid for -d/--date.
197 197 '''
198 198 if rev and node:
199 199 raise util.Abort(_("please specify just one revision"))
200 200
201 201 if not rev:
202 202 rev = node
203 203
204 204 if not rev:
205 205 raise util.Abort(_("please specify a revision to backout"))
206 206
207 207 date = opts.get('date')
208 208 if date:
209 209 opts['date'] = util.parsedate(date)
210 210
211 211 cmdutil.bail_if_changed(repo)
212 212 node = repo.lookup(rev)
213 213
214 214 op1, op2 = repo.dirstate.parents()
215 215 a = repo.changelog.ancestor(op1, node)
216 216 if a != node:
217 217 raise util.Abort(_('cannot backout change on a different branch'))
218 218
219 219 p1, p2 = repo.changelog.parents(node)
220 220 if p1 == nullid:
221 221 raise util.Abort(_('cannot backout a change with no parents'))
222 222 if p2 != nullid:
223 223 if not opts.get('parent'):
224 224 raise util.Abort(_('cannot backout a merge changeset without '
225 225 '--parent'))
226 226 p = repo.lookup(opts['parent'])
227 227 if p not in (p1, p2):
228 228 raise util.Abort(_('%s is not a parent of %s') %
229 229 (short(p), short(node)))
230 230 parent = p
231 231 else:
232 232 if opts.get('parent'):
233 233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 234 parent = p1
235 235
236 236 # the backout should appear on the same branch
237 237 branch = repo.dirstate.branch()
238 238 hg.clean(repo, node, show_stats=False)
239 239 repo.dirstate.setbranch(branch)
240 240 revert_opts = opts.copy()
241 241 revert_opts['date'] = None
242 242 revert_opts['all'] = True
243 243 revert_opts['rev'] = hex(parent)
244 244 revert_opts['no_backup'] = None
245 245 revert(ui, repo, **revert_opts)
246 246 commit_opts = opts.copy()
247 247 commit_opts['addremove'] = False
248 248 if not commit_opts['message'] and not commit_opts['logfile']:
249 249 # we don't translate commit messages
250 250 commit_opts['message'] = "Backed out changeset %s" % short(node)
251 251 commit_opts['force_editor'] = True
252 252 commit(ui, repo, **commit_opts)
253 253 def nice(node):
254 254 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 255 ui.status(_('changeset %s backs out changeset %s\n') %
256 256 (nice(repo.changelog.tip()), nice(node)))
257 257 if op1 != node:
258 258 hg.clean(repo, op1, show_stats=False)
259 259 if opts.get('merge'):
260 260 ui.status(_('merging with changeset %s\n')
261 261 % nice(repo.changelog.tip()))
262 262 hg.merge(repo, hex(repo.changelog.tip()))
263 263 else:
264 264 ui.status(_('the backout changeset is a new head - '
265 265 'do not forget to merge\n'))
266 266 ui.status(_('(use "backout --merge" '
267 267 'if you want to auto-merge)\n'))
268 268
269 269 def bisect(ui, repo, rev=None, extra=None, command=None,
270 270 reset=None, good=None, bad=None, skip=None, noupdate=None):
271 271 """subdivision search of changesets
272 272
273 273 This command helps to find changesets which introduce problems. To
274 274 use, mark the earliest changeset you know exhibits the problem as
275 275 bad, then mark the latest changeset which is free from the problem
276 276 as good. Bisect will update your working directory to a revision
277 277 for testing (unless the -U/--noupdate option is specified). Once
278 278 you have performed tests, mark the working directory as good or
279 279 bad, and bisect will either update to another candidate changeset
280 280 or announce that it has found the bad revision.
281 281
282 282 As a shortcut, you can also use the revision argument to mark a
283 283 revision as good or bad without checking it out first.
284 284
285 285 If you supply a command, it will be used for automatic bisection.
286 286 Its exit status will be used to mark revisions as good or bad:
287 287 status 0 means good, 125 means to skip the revision, 127
288 288 (command not found) will abort the bisection, and any other
289 289 non-zero exit status means the revision is bad.
290 290 """
291 291 def print_result(nodes, good):
292 292 displayer = cmdutil.show_changeset(ui, repo, {})
293 293 if len(nodes) == 1:
294 294 # narrowed it down to a single revision
295 295 if good:
296 296 ui.write(_("The first good revision is:\n"))
297 297 else:
298 298 ui.write(_("The first bad revision is:\n"))
299 299 displayer.show(repo[nodes[0]])
300 300 else:
301 301 # multiple possible revisions
302 302 if good:
303 303 ui.write(_("Due to skipped revisions, the first "
304 304 "good revision could be any of:\n"))
305 305 else:
306 306 ui.write(_("Due to skipped revisions, the first "
307 307 "bad revision could be any of:\n"))
308 308 for n in nodes:
309 309 displayer.show(repo[n])
310 310 displayer.close()
311 311
312 312 def check_state(state, interactive=True):
313 313 if not state['good'] or not state['bad']:
314 314 if (good or bad or skip or reset) and interactive:
315 315 return
316 316 if not state['good']:
317 317 raise util.Abort(_('cannot bisect (no known good revisions)'))
318 318 else:
319 319 raise util.Abort(_('cannot bisect (no known bad revisions)'))
320 320 return True
321 321
322 322 # backward compatibility
323 323 if rev in "good bad reset init".split():
324 324 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
325 325 cmd, rev, extra = rev, extra, None
326 326 if cmd == "good":
327 327 good = True
328 328 elif cmd == "bad":
329 329 bad = True
330 330 else:
331 331 reset = True
332 332 elif extra or good + bad + skip + reset + bool(command) > 1:
333 333 raise util.Abort(_('incompatible arguments'))
334 334
335 335 if reset:
336 336 p = repo.join("bisect.state")
337 337 if os.path.exists(p):
338 338 os.unlink(p)
339 339 return
340 340
341 341 state = hbisect.load_state(repo)
342 342
343 343 if command:
344 344 changesets = 1
345 345 try:
346 346 while changesets:
347 347 # update state
348 348 status = util.system(command)
349 349 if status == 125:
350 350 transition = "skip"
351 351 elif status == 0:
352 352 transition = "good"
353 353 # status < 0 means process was killed
354 354 elif status == 127:
355 355 raise util.Abort(_("failed to execute %s") % command)
356 356 elif status < 0:
357 357 raise util.Abort(_("%s killed") % command)
358 358 else:
359 359 transition = "bad"
360 360 ctx = repo[rev or '.']
361 361 state[transition].append(ctx.node())
362 362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
363 363 check_state(state, interactive=False)
364 364 # bisect
365 365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
366 366 # update to next check
367 367 cmdutil.bail_if_changed(repo)
368 368 hg.clean(repo, nodes[0], show_stats=False)
369 369 finally:
370 370 hbisect.save_state(repo, state)
371 371 return print_result(nodes, good)
372 372
373 373 # update state
374 374 node = repo.lookup(rev or '.')
375 375 if good or bad or skip:
376 376 if good:
377 377 state['good'].append(node)
378 378 elif bad:
379 379 state['bad'].append(node)
380 380 elif skip:
381 381 state['skip'].append(node)
382 382 hbisect.save_state(repo, state)
383 383
384 384 if not check_state(state):
385 385 return
386 386
387 387 # actually bisect
388 388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
389 389 if changesets == 0:
390 390 print_result(nodes, good)
391 391 else:
392 392 assert len(nodes) == 1 # only a single node can be tested next
393 393 node = nodes[0]
394 394 # compute the approximate number of remaining tests
395 395 tests, size = 0, 2
396 396 while size <= changesets:
397 397 tests, size = tests + 1, size * 2
398 398 rev = repo.changelog.rev(node)
399 399 ui.write(_("Testing changeset %d:%s "
400 400 "(%d changesets remaining, ~%d tests)\n")
401 401 % (rev, short(node), changesets, tests))
402 402 if not noupdate:
403 403 cmdutil.bail_if_changed(repo)
404 404 return hg.clean(repo, node)
405 405
406 406 def branch(ui, repo, label=None, **opts):
407 407 """set or show the current branch name
408 408
409 409 With no argument, show the current branch name. With one argument,
410 410 set the working directory branch name (the branch will not exist
411 411 in the repository until the next commit). Standard practice
412 412 recommends that primary development take place on the 'default'
413 413 branch.
414 414
415 415 Unless -f/--force is specified, branch will not let you set a
416 416 branch name that already exists, even if it's inactive.
417 417
418 418 Use -C/--clean to reset the working directory branch to that of
419 419 the parent of the working directory, negating a previous branch
420 420 change.
421 421
422 422 Use the command 'hg update' to switch to an existing branch. Use
423 423 'hg commit --close-branch' to mark this branch as closed.
424 424 """
425 425
426 426 if opts.get('clean'):
427 427 label = repo[None].parents()[0].branch()
428 428 repo.dirstate.setbranch(label)
429 429 ui.status(_('reset working directory to branch %s\n') % label)
430 430 elif label:
431 431 utflabel = encoding.fromlocal(label)
432 432 if not opts.get('force') and utflabel in repo.branchtags():
433 433 if label not in [p.branch() for p in repo.parents()]:
434 434 raise util.Abort(_('a branch of the same name already exists'
435 435 ' (use --force to override)'))
436 436 repo.dirstate.setbranch(utflabel)
437 437 ui.status(_('marked working directory as branch %s\n') % label)
438 438 else:
439 439 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
440 440
441 441 def branches(ui, repo, active=False, closed=False):
442 442 """list repository named branches
443 443
444 444 List the repository's named branches, indicating which ones are
445 445 inactive. If -c/--closed is specified, also list branches which have
446 446 been marked closed (see hg commit --close-branch).
447 447
448 448 If -a/--active is specified, only show active branches. A branch
449 449 is considered active if it contains repository heads.
450 450
451 451 Use the command 'hg update' to switch to an existing branch.
452 452 """
453 453
454 454 hexfunc = ui.debugflag and hex or short
455 455 activebranches = [repo[n].branch() for n in repo.heads()]
456 456 def testactive(tag, node):
457 457 realhead = tag in activebranches
458 458 open = node in repo.branchheads(tag, closed=False)
459 459 return realhead and open
460 460 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
461 461 for tag, node in repo.branchtags().items()],
462 462 reverse=True)
463 463
464 464 for isactive, node, tag in branches:
465 465 if (not active) or isactive:
466 466 encodedtag = encoding.tolocal(tag)
467 467 if ui.quiet:
468 468 ui.write("%s\n" % encodedtag)
469 469 else:
470 470 hn = repo.lookup(node)
471 471 if isactive:
472 472 notice = ''
473 473 elif hn not in repo.branchheads(tag, closed=False):
474 474 if not closed:
475 475 continue
476 476 notice = _(' (closed)')
477 477 else:
478 478 notice = _(' (inactive)')
479 479 rev = str(node).rjust(31 - encoding.colwidth(encodedtag))
480 480 data = encodedtag, rev, hexfunc(hn), notice
481 481 ui.write("%s %s:%s%s\n" % data)
482 482
483 483 def bundle(ui, repo, fname, dest=None, **opts):
484 484 """create a changegroup file
485 485
486 486 Generate a compressed changegroup file collecting changesets not
487 487 known to be in another repository.
488 488
489 489 If you omit the destination repository, then hg assumes the
490 490 destination will have all the nodes you specify with --base
491 491 parameters. To create a bundle containing all changesets, use
492 492 -a/--all (or --base null).
493 493
494 494 You can change compression method with the -t/--type option.
495 495 The available compression methods are: none, bzip2, and
496 496 gzip (by default, bundles are compressed using bzip2).
497 497
498 498 The bundle file can then be transferred using conventional means
499 499 and applied to another repository with the unbundle or pull
500 500 command. This is useful when direct push and pull are not
501 501 available or when exporting an entire repository is undesirable.
502 502
503 503 Applying bundles preserves all changeset contents including
504 504 permissions, copy/rename information, and revision history.
505 505 """
506 506 revs = opts.get('rev') or None
507 507 if revs:
508 508 revs = [repo.lookup(rev) for rev in revs]
509 509 if opts.get('all'):
510 510 base = ['null']
511 511 else:
512 512 base = opts.get('base')
513 513 if base:
514 514 if dest:
515 515 raise util.Abort(_("--base is incompatible with specifying "
516 516 "a destination"))
517 517 base = [repo.lookup(rev) for rev in base]
518 518 # create the right base
519 519 # XXX: nodesbetween / changegroup* should be "fixed" instead
520 520 o = []
521 521 has = set((nullid,))
522 522 for n in base:
523 523 has.update(repo.changelog.reachable(n))
524 524 if revs:
525 525 visit = list(revs)
526 526 else:
527 527 visit = repo.changelog.heads()
528 528 seen = {}
529 529 while visit:
530 530 n = visit.pop(0)
531 531 parents = [p for p in repo.changelog.parents(n) if p not in has]
532 532 if len(parents) == 0:
533 533 o.insert(0, n)
534 534 else:
535 535 for p in parents:
536 536 if p not in seen:
537 537 seen[p] = 1
538 538 visit.append(p)
539 539 else:
540 540 dest = ui.expandpath(dest or 'default-push', dest or 'default')
541 541 dest, branches = hg.parseurl(dest, opts.get('branch'))
542 542 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
543 543 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
544 544 o = repo.findoutgoing(other, force=opts.get('force'))
545 545
546 546 if revs:
547 547 cg = repo.changegroupsubset(o, revs, 'bundle')
548 548 else:
549 549 cg = repo.changegroup(o, 'bundle')
550 550
551 551 bundletype = opts.get('type', 'bzip2').lower()
552 552 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
553 553 bundletype = btypes.get(bundletype)
554 554 if bundletype not in changegroup.bundletypes:
555 555 raise util.Abort(_('unknown bundle type specified with --type'))
556 556
557 557 changegroup.writebundle(cg, fname, bundletype)
558 558
559 559 def cat(ui, repo, file1, *pats, **opts):
560 560 """output the current or given revision of files
561 561
562 562 Print the specified files as they were at the given revision. If
563 563 no revision is given, the parent of the working directory is used,
564 564 or tip if no revision is checked out.
565 565
566 566 Output may be to a file, in which case the name of the file is
567 567 given using a format string. The formatting rules are the same as
568 568 for the export command, with the following additions:
569 569
570 570 :``%s``: basename of file being printed
571 571 :``%d``: dirname of file being printed, or '.' if in repository root
572 572 :``%p``: root-relative path name of file being printed
573 573 """
574 574 ctx = repo[opts.get('rev')]
575 575 err = 1
576 576 m = cmdutil.match(repo, (file1,) + pats, opts)
577 577 for abs in ctx.walk(m):
578 578 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
579 579 data = ctx[abs].data()
580 580 if opts.get('decode'):
581 581 data = repo.wwritedata(abs, data)
582 582 fp.write(data)
583 583 err = 0
584 584 return err
585 585
586 586 def clone(ui, source, dest=None, **opts):
587 587 """make a copy of an existing repository
588 588
589 589 Create a copy of an existing repository in a new directory.
590 590
591 591 If no destination directory name is specified, it defaults to the
592 592 basename of the source.
593 593
594 594 The location of the source is added to the new repository's
595 595 .hg/hgrc file, as the default to be used for future pulls.
596 596
597 597 See 'hg help urls' for valid source format details.
598 598
599 599 It is possible to specify an ``ssh://`` URL as the destination, but no
600 600 .hg/hgrc and working directory will be created on the remote side.
601 601 Please see 'hg help urls' for important details about ``ssh://`` URLs.
602 602
603 603 If the -U/--noupdate option is specified, the new clone will contain
604 604 only a repository (.hg) and no working copy (the working copy parent
605 605 will be the null changeset). Otherwise, clone will initially check
606 606 out (in order of precedence):
607 607
608 608 a) the changeset, tag or branch specified with -u/--updaterev
609 609 b) the changeset, tag or branch given with the first -r/--rev
610 610 c) the branch given with the first -b/--branch
611 611 d) the branch given with the url#branch source syntax
612 612 e) the head of the default branch
613 613
614 614 Use 'hg clone -u . src dst' to checkout the source repository's
615 615 parent changeset (applicable for local source repositories only).
616 616
617 617 A set of changesets (tags, or branch names) to pull may be specified
618 618 by listing each changeset (tag, or branch name) with -r/--rev.
619 619 If -r/--rev is used, the cloned repository will contain only a subset
620 620 of the changesets of the source repository. Only the set of changesets
621 621 defined by all -r/--rev options (including all their ancestors)
622 622 will be pulled into the destination repository.
623 623 No subsequent changesets (including subsequent tags) will be present
624 624 in the destination.
625 625
626 626 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
627 627 local source repositories.
628 628
629 629 For efficiency, hardlinks are used for cloning whenever the source
630 630 and destination are on the same filesystem (note this applies only
631 631 to the repository data, not to the checked out files). Some
632 632 filesystems, such as AFS, implement hardlinking incorrectly, but
633 633 do not report errors. In these cases, use the --pull option to
634 634 avoid hardlinking.
635 635
636 636 In some cases, you can clone repositories and checked out files
637 637 using full hardlinks with ::
638 638
639 639 $ cp -al REPO REPOCLONE
640 640
641 641 This is the fastest way to clone, but it is not always safe. The
642 642 operation is not atomic (making sure REPO is not modified during
643 643 the operation is up to you) and you have to make sure your editor
644 644 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
645 645 this is not compatible with certain extensions that place their
646 646 metadata under the .hg directory, such as mq.
647 647 """
648 648 if opts.get('noupdate') and opts.get('updaterev'):
649 649 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
650 650
651 651 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
652 652 pull=opts.get('pull'),
653 653 stream=opts.get('uncompressed'),
654 654 rev=opts.get('rev'),
655 655 update=opts.get('updaterev') or not opts.get('noupdate'),
656 656 branch=opts.get('branch'))
657 657
658 658 def commit(ui, repo, *pats, **opts):
659 659 """commit the specified files or all outstanding changes
660 660
661 661 Commit changes to the given files into the repository. Unlike a
662 662 centralized RCS, this operation is a local operation. See hg push
663 663 for a way to actively distribute your changes.
664 664
665 665 If a list of files is omitted, all changes reported by "hg status"
666 666 will be committed.
667 667
668 668 If you are committing the result of a merge, do not provide any
669 669 filenames or -I/-X filters.
670 670
671 671 If no commit message is specified, the configured editor is
672 672 started to prompt you for a message.
673 673
674 674 See 'hg help dates' for a list of formats valid for -d/--date.
675 675 """
676 676 extra = {}
677 677 if opts.get('close_branch'):
678 678 extra['close'] = 1
679 679 e = cmdutil.commiteditor
680 680 if opts.get('force_editor'):
681 681 e = cmdutil.commitforceeditor
682 682
683 683 def commitfunc(ui, repo, message, match, opts):
684 684 return repo.commit(message, opts.get('user'), opts.get('date'), match,
685 685 editor=e, extra=extra)
686 686
687 687 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
688 688 if not node:
689 689 ui.status(_("nothing changed\n"))
690 690 return
691 691 cl = repo.changelog
692 692 rev = cl.rev(node)
693 693 parents = cl.parentrevs(rev)
694 694 if rev - 1 in parents:
695 695 # one of the parents was the old tip
696 696 pass
697 697 elif (parents == (nullrev, nullrev) or
698 698 len(cl.heads(cl.node(parents[0]))) > 1 and
699 699 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
700 700 ui.status(_('created new head\n'))
701 701
702 702 if ui.debugflag:
703 703 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
704 704 elif ui.verbose:
705 705 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
706 706
707 707 def copy(ui, repo, *pats, **opts):
708 708 """mark files as copied for the next commit
709 709
710 710 Mark dest as having copies of source files. If dest is a
711 711 directory, copies are put in that directory. If dest is a file,
712 712 the source must be a single file.
713 713
714 714 By default, this command copies the contents of files as they
715 715 exist in the working directory. If invoked with -A/--after, the
716 716 operation is recorded, but no copying is performed.
717 717
718 718 This command takes effect with the next commit. To undo a copy
719 719 before that, see hg revert.
720 720 """
721 721 wlock = repo.wlock(False)
722 722 try:
723 723 return cmdutil.copy(ui, repo, pats, opts)
724 724 finally:
725 725 wlock.release()
726 726
727 727 def debugancestor(ui, repo, *args):
728 728 """find the ancestor revision of two revisions in a given index"""
729 729 if len(args) == 3:
730 730 index, rev1, rev2 = args
731 731 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
732 732 lookup = r.lookup
733 733 elif len(args) == 2:
734 734 if not repo:
735 735 raise util.Abort(_("There is no Mercurial repository here "
736 736 "(.hg not found)"))
737 737 rev1, rev2 = args
738 738 r = repo.changelog
739 739 lookup = repo.lookup
740 740 else:
741 741 raise util.Abort(_('either two or three arguments required'))
742 742 a = r.ancestor(lookup(rev1), lookup(rev2))
743 743 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
744 744
745 745 def debugcommands(ui, cmd='', *args):
746 746 for cmd, vals in sorted(table.iteritems()):
747 747 cmd = cmd.split('|')[0].strip('^')
748 748 opts = ', '.join([i[1] for i in vals[1]])
749 749 ui.write('%s: %s\n' % (cmd, opts))
750 750
751 751 def debugcomplete(ui, cmd='', **opts):
752 752 """returns the completion list associated with the given command"""
753 753
754 754 if opts.get('options'):
755 755 options = []
756 756 otables = [globalopts]
757 757 if cmd:
758 758 aliases, entry = cmdutil.findcmd(cmd, table, False)
759 759 otables.append(entry[1])
760 760 for t in otables:
761 761 for o in t:
762 762 if o[0]:
763 763 options.append('-%s' % o[0])
764 764 options.append('--%s' % o[1])
765 765 ui.write("%s\n" % "\n".join(options))
766 766 return
767 767
768 768 cmdlist = cmdutil.findpossible(cmd, table)
769 769 if ui.verbose:
770 770 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
771 771 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
772 772
773 773 def debugfsinfo(ui, path = "."):
774 774 open('.debugfsinfo', 'w').write('')
775 775 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
776 776 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
777 777 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
778 778 and 'yes' or 'no'))
779 779 os.unlink('.debugfsinfo')
780 780
781 781 def debugrebuildstate(ui, repo, rev="tip"):
782 782 """rebuild the dirstate as it would look like for the given revision"""
783 783 ctx = repo[rev]
784 784 wlock = repo.wlock()
785 785 try:
786 786 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
787 787 finally:
788 788 wlock.release()
789 789
790 790 def debugcheckstate(ui, repo):
791 791 """validate the correctness of the current dirstate"""
792 792 parent1, parent2 = repo.dirstate.parents()
793 793 m1 = repo[parent1].manifest()
794 794 m2 = repo[parent2].manifest()
795 795 errors = 0
796 796 for f in repo.dirstate:
797 797 state = repo.dirstate[f]
798 798 if state in "nr" and f not in m1:
799 799 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
800 800 errors += 1
801 801 if state in "a" and f in m1:
802 802 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
803 803 errors += 1
804 804 if state in "m" and f not in m1 and f not in m2:
805 805 ui.warn(_("%s in state %s, but not in either manifest\n") %
806 806 (f, state))
807 807 errors += 1
808 808 for f in m1:
809 809 state = repo.dirstate[f]
810 810 if state not in "nrm":
811 811 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
812 812 errors += 1
813 813 if errors:
814 814 error = _(".hg/dirstate inconsistent with current parent's manifest")
815 815 raise util.Abort(error)
816 816
817 817 def showconfig(ui, repo, *values, **opts):
818 818 """show combined config settings from all hgrc files
819 819
820 820 With no arguments, print names and values of all config items.
821 821
822 822 With one argument of the form section.name, print just the value
823 823 of that config item.
824 824
825 825 With multiple arguments, print names and values of all config
826 826 items with matching section names.
827 827
828 828 With --debug, the source (filename and line number) is printed
829 829 for each config item.
830 830 """
831 831
832 832 untrusted = bool(opts.get('untrusted'))
833 833 if values:
834 834 if len([v for v in values if '.' in v]) > 1:
835 835 raise util.Abort(_('only one config item permitted'))
836 836 for section, name, value in ui.walkconfig(untrusted=untrusted):
837 837 sectname = section + '.' + name
838 838 if values:
839 839 for v in values:
840 840 if v == section:
841 841 ui.debug('%s: ' %
842 842 ui.configsource(section, name, untrusted))
843 843 ui.write('%s=%s\n' % (sectname, value))
844 844 elif v == sectname:
845 845 ui.debug('%s: ' %
846 846 ui.configsource(section, name, untrusted))
847 847 ui.write(value, '\n')
848 848 else:
849 849 ui.debug('%s: ' %
850 850 ui.configsource(section, name, untrusted))
851 851 ui.write('%s=%s\n' % (sectname, value))
852 852
853 853 def debugsetparents(ui, repo, rev1, rev2=None):
854 854 """manually set the parents of the current working directory
855 855
856 856 This is useful for writing repository conversion tools, but should
857 857 be used with care.
858 858 """
859 859
860 860 if not rev2:
861 861 rev2 = hex(nullid)
862 862
863 863 wlock = repo.wlock()
864 864 try:
865 865 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
866 866 finally:
867 867 wlock.release()
868 868
869 869 def debugstate(ui, repo, nodates=None):
870 870 """show the contents of the current dirstate"""
871 871 timestr = ""
872 872 showdate = not nodates
873 873 for file_, ent in sorted(repo.dirstate._map.iteritems()):
874 874 if showdate:
875 875 if ent[3] == -1:
876 876 # Pad or slice to locale representation
877 877 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
878 878 time.localtime(0)))
879 879 timestr = 'unset'
880 880 timestr = (timestr[:locale_len] +
881 881 ' ' * (locale_len - len(timestr)))
882 882 else:
883 883 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
884 884 time.localtime(ent[3]))
885 885 if ent[1] & 020000:
886 886 mode = 'lnk'
887 887 else:
888 888 mode = '%3o' % (ent[1] & 0777)
889 889 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
890 890 for f in repo.dirstate.copies():
891 891 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
892 892
893 893 def debugsub(ui, repo, rev=None):
894 894 if rev == '':
895 895 rev = None
896 896 for k, v in sorted(repo[rev].substate.items()):
897 897 ui.write('path %s\n' % k)
898 898 ui.write(' source %s\n' % v[0])
899 899 ui.write(' revision %s\n' % v[1])
900 900
901 901 def debugdata(ui, file_, rev):
902 902 """dump the contents of a data file revision"""
903 903 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
904 904 try:
905 905 ui.write(r.revision(r.lookup(rev)))
906 906 except KeyError:
907 907 raise util.Abort(_('invalid revision identifier %s') % rev)
908 908
909 909 def debugdate(ui, date, range=None, **opts):
910 910 """parse and display a date"""
911 911 if opts["extended"]:
912 912 d = util.parsedate(date, util.extendeddateformats)
913 913 else:
914 914 d = util.parsedate(date)
915 915 ui.write("internal: %s %s\n" % d)
916 916 ui.write("standard: %s\n" % util.datestr(d))
917 917 if range:
918 918 m = util.matchdate(range)
919 919 ui.write("match: %s\n" % m(d[0]))
920 920
921 921 def debugindex(ui, file_):
922 922 """dump the contents of an index file"""
923 923 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
924 924 ui.write(" rev offset length base linkrev"
925 925 " nodeid p1 p2\n")
926 926 for i in r:
927 927 node = r.node(i)
928 928 try:
929 929 pp = r.parents(node)
930 930 except:
931 931 pp = [nullid, nullid]
932 932 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
933 933 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
934 934 short(node), short(pp[0]), short(pp[1])))
935 935
936 936 def debugindexdot(ui, file_):
937 937 """dump an index DAG as a graphviz dot file"""
938 938 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
939 939 ui.write("digraph G {\n")
940 940 for i in r:
941 941 node = r.node(i)
942 942 pp = r.parents(node)
943 943 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
944 944 if pp[1] != nullid:
945 945 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
946 946 ui.write("}\n")
947 947
948 948 def debuginstall(ui):
949 949 '''test Mercurial installation'''
950 950
951 951 def writetemp(contents):
952 952 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
953 953 f = os.fdopen(fd, "wb")
954 954 f.write(contents)
955 955 f.close()
956 956 return name
957 957
958 958 problems = 0
959 959
960 960 # encoding
961 961 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
962 962 try:
963 963 encoding.fromlocal("test")
964 964 except util.Abort, inst:
965 965 ui.write(" %s\n" % inst)
966 966 ui.write(_(" (check that your locale is properly set)\n"))
967 967 problems += 1
968 968
969 969 # compiled modules
970 970 ui.status(_("Checking extensions...\n"))
971 971 try:
972 972 import bdiff, mpatch, base85
973 973 except Exception, inst:
974 974 ui.write(" %s\n" % inst)
975 975 ui.write(_(" One or more extensions could not be found"))
976 976 ui.write(_(" (check that you compiled the extensions)\n"))
977 977 problems += 1
978 978
979 979 # templates
980 980 ui.status(_("Checking templates...\n"))
981 981 try:
982 982 import templater
983 983 templater.templater(templater.templatepath("map-cmdline.default"))
984 984 except Exception, inst:
985 985 ui.write(" %s\n" % inst)
986 986 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
987 987 problems += 1
988 988
989 989 # patch
990 990 ui.status(_("Checking patch...\n"))
991 991 patchproblems = 0
992 992 a = "1\n2\n3\n4\n"
993 993 b = "1\n2\n3\ninsert\n4\n"
994 994 fa = writetemp(a)
995 995 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
996 996 os.path.basename(fa))
997 997 fd = writetemp(d)
998 998
999 999 files = {}
1000 1000 try:
1001 1001 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1002 1002 except util.Abort, e:
1003 1003 ui.write(_(" patch call failed:\n"))
1004 1004 ui.write(" " + str(e) + "\n")
1005 1005 patchproblems += 1
1006 1006 else:
1007 1007 if list(files) != [os.path.basename(fa)]:
1008 1008 ui.write(_(" unexpected patch output!\n"))
1009 1009 patchproblems += 1
1010 1010 a = open(fa).read()
1011 1011 if a != b:
1012 1012 ui.write(_(" patch test failed!\n"))
1013 1013 patchproblems += 1
1014 1014
1015 1015 if patchproblems:
1016 1016 if ui.config('ui', 'patch'):
1017 1017 ui.write(_(" (Current patch tool may be incompatible with patch,"
1018 1018 " or misconfigured. Please check your .hgrc file)\n"))
1019 1019 else:
1020 1020 ui.write(_(" Internal patcher failure, please report this error"
1021 1021 " to http://mercurial.selenic.com/bts/\n"))
1022 1022 problems += patchproblems
1023 1023
1024 1024 os.unlink(fa)
1025 1025 os.unlink(fd)
1026 1026
1027 1027 # editor
1028 1028 ui.status(_("Checking commit editor...\n"))
1029 1029 editor = ui.geteditor()
1030 1030 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1031 1031 if not cmdpath:
1032 1032 if editor == 'vi':
1033 1033 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1034 1034 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1035 1035 else:
1036 1036 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1037 1037 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1038 1038 problems += 1
1039 1039
1040 1040 # check username
1041 1041 ui.status(_("Checking username...\n"))
1042 1042 try:
1043 1043 user = ui.username()
1044 1044 except util.Abort, e:
1045 1045 ui.write(" %s\n" % e)
1046 1046 ui.write(_(" (specify a username in your .hgrc file)\n"))
1047 1047 problems += 1
1048 1048
1049 1049 if not problems:
1050 1050 ui.status(_("No problems detected\n"))
1051 1051 else:
1052 1052 ui.write(_("%s problems detected,"
1053 1053 " please check your install!\n") % problems)
1054 1054
1055 1055 return problems
1056 1056
1057 1057 def debugrename(ui, repo, file1, *pats, **opts):
1058 1058 """dump rename information"""
1059 1059
1060 1060 ctx = repo[opts.get('rev')]
1061 1061 m = cmdutil.match(repo, (file1,) + pats, opts)
1062 1062 for abs in ctx.walk(m):
1063 1063 fctx = ctx[abs]
1064 1064 o = fctx.filelog().renamed(fctx.filenode())
1065 1065 rel = m.rel(abs)
1066 1066 if o:
1067 1067 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1068 1068 else:
1069 1069 ui.write(_("%s not renamed\n") % rel)
1070 1070
1071 1071 def debugwalk(ui, repo, *pats, **opts):
1072 1072 """show how files match on given patterns"""
1073 1073 m = cmdutil.match(repo, pats, opts)
1074 1074 items = list(repo.walk(m))
1075 1075 if not items:
1076 1076 return
1077 1077 fmt = 'f %%-%ds %%-%ds %%s' % (
1078 1078 max([len(abs) for abs in items]),
1079 1079 max([len(m.rel(abs)) for abs in items]))
1080 1080 for abs in items:
1081 1081 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1082 1082 ui.write("%s\n" % line.rstrip())
1083 1083
1084 1084 def diff(ui, repo, *pats, **opts):
1085 1085 """diff repository (or selected files)
1086 1086
1087 1087 Show differences between revisions for the specified files.
1088 1088
1089 1089 Differences between files are shown using the unified diff format.
1090 1090
1091 1091 NOTE: diff may generate unexpected results for merges, as it will
1092 1092 default to comparing against the working directory's first parent
1093 1093 changeset if no revisions are specified.
1094 1094
1095 1095 When two revision arguments are given, then changes are shown
1096 1096 between those revisions. If only one revision is specified then
1097 1097 that revision is compared to the working directory, and, when no
1098 1098 revisions are specified, the working directory files are compared
1099 1099 to its parent.
1100 1100
1101 1101 Without the -a/--text option, diff will avoid generating diffs of
1102 1102 files it detects as binary. With -a, diff will generate a diff
1103 1103 anyway, probably with undesirable results.
1104 1104
1105 1105 Use the -g/--git option to generate diffs in the git extended diff
1106 1106 format. For more information, read 'hg help diffs'.
1107 1107 """
1108 1108
1109 1109 revs = opts.get('rev')
1110 1110 change = opts.get('change')
1111 1111 stat = opts.get('stat')
1112 1112 reverse = opts.get('reverse')
1113 1113
1114 1114 if revs and change:
1115 1115 msg = _('cannot specify --rev and --change at the same time')
1116 1116 raise util.Abort(msg)
1117 1117 elif change:
1118 1118 node2 = repo.lookup(change)
1119 1119 node1 = repo[node2].parents()[0].node()
1120 1120 else:
1121 1121 node1, node2 = cmdutil.revpair(repo, revs)
1122 1122
1123 1123 if reverse:
1124 1124 node1, node2 = node2, node1
1125 1125
1126 1126 if stat:
1127 1127 opts['unified'] = '0'
1128 1128 diffopts = patch.diffopts(ui, opts)
1129 1129
1130 1130 m = cmdutil.match(repo, pats, opts)
1131 1131 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1132 1132 if stat:
1133 1133 width = ui.interactive() and util.termwidth() or 80
1134 1134 ui.write(patch.diffstat(util.iterlines(it), width=width,
1135 1135 git=diffopts.git))
1136 1136 else:
1137 1137 for chunk in it:
1138 1138 ui.write(chunk)
1139 1139
1140 1140 def export(ui, repo, *changesets, **opts):
1141 1141 """dump the header and diffs for one or more changesets
1142 1142
1143 1143 Print the changeset header and diffs for one or more revisions.
1144 1144
1145 1145 The information shown in the changeset header is: author, date,
1146 1146 branch name (if non-default), changeset hash, parent(s) and commit
1147 1147 comment.
1148 1148
1149 1149 NOTE: export may generate unexpected diff output for merge
1150 1150 changesets, as it will compare the merge changeset against its
1151 1151 first parent only.
1152 1152
1153 1153 Output may be to a file, in which case the name of the file is
1154 1154 given using a format string. The formatting rules are as follows:
1155 1155
1156 1156 :``%%``: literal "%" character
1157 1157 :``%H``: changeset hash (40 bytes of hexadecimal)
1158 1158 :``%N``: number of patches being generated
1159 1159 :``%R``: changeset revision number
1160 1160 :``%b``: basename of the exporting repository
1161 1161 :``%h``: short-form changeset hash (12 bytes of hexadecimal)
1162 1162 :``%n``: zero-padded sequence number, starting at 1
1163 1163 :``%r``: zero-padded changeset revision number
1164 1164
1165 1165 Without the -a/--text option, export will avoid generating diffs
1166 1166 of files it detects as binary. With -a, export will generate a
1167 1167 diff anyway, probably with undesirable results.
1168 1168
1169 1169 Use the -g/--git option to generate diffs in the git extended diff
1170 1170 format. See 'hg help diffs' for more information.
1171 1171
1172 1172 With the --switch-parent option, the diff will be against the
1173 1173 second parent. It can be useful to review a merge.
1174 1174 """
1175 1175 changesets += tuple(opts.get('rev', []))
1176 1176 if not changesets:
1177 1177 raise util.Abort(_("export requires at least one changeset"))
1178 1178 revs = cmdutil.revrange(repo, changesets)
1179 1179 if len(revs) > 1:
1180 1180 ui.note(_('exporting patches:\n'))
1181 1181 else:
1182 1182 ui.note(_('exporting patch:\n'))
1183 1183 patch.export(repo, revs, template=opts.get('output'),
1184 1184 switch_parent=opts.get('switch_parent'),
1185 1185 opts=patch.diffopts(ui, opts))
1186 1186
1187 1187 def forget(ui, repo, *pats, **opts):
1188 1188 """forget the specified files on the next commit
1189 1189
1190 1190 Mark the specified files so they will no longer be tracked
1191 1191 after the next commit.
1192 1192
1193 1193 This only removes files from the current branch, not from the
1194 1194 entire project history, and it does not delete them from the
1195 1195 working directory.
1196 1196
1197 1197 To undo a forget before the next commit, see hg add.
1198 1198 """
1199 1199
1200 1200 if not pats:
1201 1201 raise util.Abort(_('no files specified'))
1202 1202
1203 1203 m = cmdutil.match(repo, pats, opts)
1204 1204 s = repo.status(match=m, clean=True)
1205 1205 forget = sorted(s[0] + s[1] + s[3] + s[6])
1206 1206
1207 1207 for f in m.files():
1208 1208 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1209 1209 ui.warn(_('not removing %s: file is already untracked\n')
1210 1210 % m.rel(f))
1211 1211
1212 1212 for f in forget:
1213 1213 if ui.verbose or not m.exact(f):
1214 1214 ui.status(_('removing %s\n') % m.rel(f))
1215 1215
1216 1216 repo.remove(forget, unlink=False)
1217 1217
1218 1218 def grep(ui, repo, pattern, *pats, **opts):
1219 1219 """search for a pattern in specified files and revisions
1220 1220
1221 1221 Search revisions of files for a regular expression.
1222 1222
1223 1223 This command behaves differently than Unix grep. It only accepts
1224 1224 Python/Perl regexps. It searches repository history, not the
1225 1225 working directory. It always prints the revision number in which a
1226 1226 match appears.
1227 1227
1228 1228 By default, grep only prints output for the first revision of a
1229 1229 file in which it finds a match. To get it to print every revision
1230 1230 that contains a change in match status ("-" for a match that
1231 1231 becomes a non-match, or "+" for a non-match that becomes a match),
1232 1232 use the --all flag.
1233 1233 """
1234 1234 reflags = 0
1235 1235 if opts.get('ignore_case'):
1236 1236 reflags |= re.I
1237 1237 try:
1238 1238 regexp = re.compile(pattern, reflags)
1239 1239 except Exception, inst:
1240 1240 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1241 1241 return None
1242 1242 sep, eol = ':', '\n'
1243 1243 if opts.get('print0'):
1244 1244 sep = eol = '\0'
1245 1245
1246 1246 getfile = util.lrucachefunc(repo.file)
1247 1247
1248 1248 def matchlines(body):
1249 1249 begin = 0
1250 1250 linenum = 0
1251 1251 while True:
1252 1252 match = regexp.search(body, begin)
1253 1253 if not match:
1254 1254 break
1255 1255 mstart, mend = match.span()
1256 1256 linenum += body.count('\n', begin, mstart) + 1
1257 1257 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1258 1258 begin = body.find('\n', mend) + 1 or len(body)
1259 1259 lend = begin - 1
1260 1260 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1261 1261
1262 1262 class linestate(object):
1263 1263 def __init__(self, line, linenum, colstart, colend):
1264 1264 self.line = line
1265 1265 self.linenum = linenum
1266 1266 self.colstart = colstart
1267 1267 self.colend = colend
1268 1268
1269 1269 def __hash__(self):
1270 1270 return hash((self.linenum, self.line))
1271 1271
1272 1272 def __eq__(self, other):
1273 1273 return self.line == other.line
1274 1274
1275 1275 matches = {}
1276 1276 copies = {}
1277 1277 def grepbody(fn, rev, body):
1278 1278 matches[rev].setdefault(fn, [])
1279 1279 m = matches[rev][fn]
1280 1280 for lnum, cstart, cend, line in matchlines(body):
1281 1281 s = linestate(line, lnum, cstart, cend)
1282 1282 m.append(s)
1283 1283
1284 1284 def difflinestates(a, b):
1285 1285 sm = difflib.SequenceMatcher(None, a, b)
1286 1286 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1287 1287 if tag == 'insert':
1288 1288 for i in xrange(blo, bhi):
1289 1289 yield ('+', b[i])
1290 1290 elif tag == 'delete':
1291 1291 for i in xrange(alo, ahi):
1292 1292 yield ('-', a[i])
1293 1293 elif tag == 'replace':
1294 1294 for i in xrange(alo, ahi):
1295 1295 yield ('-', a[i])
1296 1296 for i in xrange(blo, bhi):
1297 1297 yield ('+', b[i])
1298 1298
1299 1299 def display(fn, ctx, pstates, states):
1300 1300 rev = ctx.rev()
1301 1301 datefunc = ui.quiet and util.shortdate or util.datestr
1302 1302 found = False
1303 1303 filerevmatches = {}
1304 1304 if opts.get('all'):
1305 1305 iter = difflinestates(pstates, states)
1306 1306 else:
1307 1307 iter = [('', l) for l in states]
1308 1308 for change, l in iter:
1309 1309 cols = [fn, str(rev)]
1310 1310 if opts.get('line_number'):
1311 1311 cols.append(str(l.linenum))
1312 1312 if opts.get('all'):
1313 1313 cols.append(change)
1314 1314 if opts.get('user'):
1315 1315 cols.append(ui.shortuser(ctx.user()))
1316 1316 if opts.get('date'):
1317 1317 cols.append(datefunc(ctx.date()))
1318 1318 if opts.get('files_with_matches'):
1319 1319 c = (fn, rev)
1320 1320 if c in filerevmatches:
1321 1321 continue
1322 1322 filerevmatches[c] = 1
1323 1323 else:
1324 1324 cols.append(l.line)
1325 1325 ui.write(sep.join(cols), eol)
1326 1326 found = True
1327 1327 return found
1328 1328
1329 1329 skip = {}
1330 1330 revfiles = {}
1331 1331 matchfn = cmdutil.match(repo, pats, opts)
1332 1332 found = False
1333 1333 follow = opts.get('follow')
1334 1334
1335 1335 def prep(ctx, fns):
1336 1336 rev = ctx.rev()
1337 1337 pctx = ctx.parents()[0]
1338 1338 parent = pctx.rev()
1339 1339 matches.setdefault(rev, {})
1340 1340 matches.setdefault(parent, {})
1341 1341 files = revfiles.setdefault(rev, [])
1342 1342 for fn in fns:
1343 1343 flog = getfile(fn)
1344 1344 try:
1345 1345 fnode = ctx.filenode(fn)
1346 1346 except error.LookupError:
1347 1347 continue
1348 1348
1349 1349 copied = flog.renamed(fnode)
1350 1350 copy = follow and copied and copied[0]
1351 1351 if copy:
1352 1352 copies.setdefault(rev, {})[fn] = copy
1353 1353 if fn in skip:
1354 1354 if copy:
1355 1355 skip[copy] = True
1356 1356 continue
1357 1357 files.append(fn)
1358 1358
1359 1359 if fn not in matches[rev]:
1360 1360 grepbody(fn, rev, flog.read(fnode))
1361 1361
1362 1362 pfn = copy or fn
1363 1363 if pfn not in matches[parent]:
1364 1364 try:
1365 1365 fnode = pctx.filenode(pfn)
1366 1366 grepbody(pfn, parent, flog.read(fnode))
1367 1367 except error.LookupError:
1368 1368 pass
1369 1369
1370 1370 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1371 1371 rev = ctx.rev()
1372 1372 parent = ctx.parents()[0].rev()
1373 1373 for fn in sorted(revfiles.get(rev, [])):
1374 1374 states = matches[rev][fn]
1375 1375 copy = copies.get(rev, {}).get(fn)
1376 1376 if fn in skip:
1377 1377 if copy:
1378 1378 skip[copy] = True
1379 1379 continue
1380 1380 pstates = matches.get(parent, {}).get(copy or fn, [])
1381 1381 if pstates or states:
1382 1382 r = display(fn, ctx, pstates, states)
1383 1383 found = found or r
1384 1384 if r and not opts.get('all'):
1385 1385 skip[fn] = True
1386 1386 if copy:
1387 1387 skip[copy] = True
1388 1388 del matches[rev]
1389 1389 del revfiles[rev]
1390 1390
1391 1391 def heads(ui, repo, *branchrevs, **opts):
1392 1392 """show current repository heads or show branch heads
1393 1393
1394 1394 With no arguments, show all repository branch heads.
1395 1395
1396 1396 Repository "heads" are changesets with no child changesets. They are
1397 1397 where development generally takes place and are the usual targets
1398 1398 for update and merge operations. Branch heads are changesets that have
1399 1399 no child changeset on the same branch.
1400 1400
1401 1401 If one or more REVs are given, only branch heads on the branches
1402 1402 associated with the specified changesets are shown.
1403 1403
1404 1404 If -c/--closed is specified, also show branch heads marked closed
1405 1405 (see hg commit --close-branch).
1406 1406
1407 1407 If STARTREV is specified, only those heads that are descendants of
1408 1408 STARTREV will be displayed.
1409 1409
1410 1410 If -t/--topo is specified, named branch mechanics will be ignored and only
1411 1411 changesets without children will be shown.
1412 1412 """
1413 1413
1414 1414 if opts.get('rev'):
1415 1415 start = repo.lookup(opts['rev'])
1416 1416 else:
1417 1417 start = None
1418 1418
1419 1419 if opts.get('topo'):
1420 1420 heads = [repo[h] for h in repo.heads(start)]
1421 1421 else:
1422 1422 heads = []
1423 1423 for b, ls in repo.branchmap().iteritems():
1424 1424 if start is None:
1425 1425 heads += [repo[h] for h in ls]
1426 1426 continue
1427 1427 startrev = repo.changelog.rev(start)
1428 1428 descendants = set(repo.changelog.descendants(startrev))
1429 1429 descendants.add(startrev)
1430 1430 rev = repo.changelog.rev
1431 1431 heads += [repo[h] for h in ls if rev(h) in descendants]
1432 1432
1433 1433 if branchrevs:
1434 1434 decode, encode = encoding.fromlocal, encoding.tolocal
1435 1435 branches = set(repo[decode(br)].branch() for br in branchrevs)
1436 1436 heads = [h for h in heads if h.branch() in branches]
1437 1437
1438 1438 if not opts.get('closed'):
1439 1439 heads = [h for h in heads if not h.extra().get('close')]
1440 1440
1441 1441 if opts.get('active') and branchrevs:
1442 1442 dagheads = repo.heads(start)
1443 1443 heads = [h for h in heads if h.node() in dagheads]
1444 1444
1445 1445 if branchrevs:
1446 1446 haveheads = set(h.branch() for h in heads)
1447 1447 if branches - haveheads:
1448 1448 headless = ', '.join(encode(b) for b in branches - haveheads)
1449 1449 msg = _('no open branch heads found on branches %s')
1450 1450 if opts.get('rev'):
1451 1451 msg += _(' (started at %s)' % opts['rev'])
1452 1452 ui.warn((msg + '\n') % headless)
1453 1453
1454 1454 if not heads:
1455 1455 return 1
1456 1456
1457 1457 heads = sorted(heads, key=lambda x: -x.rev())
1458 1458 displayer = cmdutil.show_changeset(ui, repo, opts)
1459 1459 for ctx in heads:
1460 1460 displayer.show(ctx)
1461 1461 displayer.close()
1462 1462
1463 1463 def help_(ui, name=None, with_version=False, unknowncmd=False):
1464 1464 """show help for a given topic or a help overview
1465 1465
1466 1466 With no arguments, print a list of commands with short help messages.
1467 1467
1468 1468 Given a topic, extension, or command name, print help for that
1469 1469 topic."""
1470 1470 option_lists = []
1471 1471 textwidth = util.termwidth() - 2
1472 1472
1473 1473 def addglobalopts(aliases):
1474 1474 if ui.verbose:
1475 1475 option_lists.append((_("global options:"), globalopts))
1476 1476 if name == 'shortlist':
1477 1477 option_lists.append((_('use "hg help" for the full list '
1478 1478 'of commands'), ()))
1479 1479 else:
1480 1480 if name == 'shortlist':
1481 1481 msg = _('use "hg help" for the full list of commands '
1482 1482 'or "hg -v" for details')
1483 1483 elif aliases:
1484 1484 msg = _('use "hg -v help%s" to show aliases and '
1485 1485 'global options') % (name and " " + name or "")
1486 1486 else:
1487 1487 msg = _('use "hg -v help %s" to show global options') % name
1488 1488 option_lists.append((msg, ()))
1489 1489
1490 1490 def helpcmd(name):
1491 1491 if with_version:
1492 1492 version_(ui)
1493 1493 ui.write('\n')
1494 1494
1495 1495 try:
1496 1496 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1497 1497 except error.AmbiguousCommand, inst:
1498 1498 # py3k fix: except vars can't be used outside the scope of the
1499 1499 # except block, nor can be used inside a lambda. python issue4617
1500 1500 prefix = inst.args[0]
1501 1501 select = lambda c: c.lstrip('^').startswith(prefix)
1502 1502 helplist(_('list of commands:\n\n'), select)
1503 1503 return
1504 1504
1505 1505 # check if it's an invalid alias and display its error if it is
1506 1506 if getattr(entry[0], 'badalias', False):
1507 1507 if not unknowncmd:
1508 1508 entry[0](ui)
1509 1509 return
1510 1510
1511 1511 # synopsis
1512 1512 if len(entry) > 2:
1513 1513 if entry[2].startswith('hg'):
1514 1514 ui.write("%s\n" % entry[2])
1515 1515 else:
1516 1516 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1517 1517 else:
1518 1518 ui.write('hg %s\n' % aliases[0])
1519 1519
1520 1520 # aliases
1521 1521 if not ui.quiet and len(aliases) > 1:
1522 1522 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1523 1523
1524 1524 # description
1525 1525 doc = gettext(entry[0].__doc__)
1526 1526 if not doc:
1527 1527 doc = _("(no help text available)")
1528 1528 if ui.quiet:
1529 1529 doc = doc.splitlines()[0]
1530 1530 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1531 1531
1532 1532 if not ui.quiet:
1533 1533 # options
1534 1534 if entry[1]:
1535 1535 option_lists.append((_("options:\n"), entry[1]))
1536 1536
1537 1537 addglobalopts(False)
1538 1538
1539 1539 def helplist(header, select=None):
1540 1540 h = {}
1541 1541 cmds = {}
1542 1542 for c, e in table.iteritems():
1543 1543 f = c.split("|", 1)[0]
1544 1544 if select and not select(f):
1545 1545 continue
1546 1546 if (not select and name != 'shortlist' and
1547 1547 e[0].__module__ != __name__):
1548 1548 continue
1549 1549 if name == "shortlist" and not f.startswith("^"):
1550 1550 continue
1551 1551 f = f.lstrip("^")
1552 1552 if not ui.debugflag and f.startswith("debug"):
1553 1553 continue
1554 1554 doc = e[0].__doc__
1555 1555 if doc and 'DEPRECATED' in doc and not ui.verbose:
1556 1556 continue
1557 1557 doc = gettext(doc)
1558 1558 if not doc:
1559 1559 doc = _("(no help text available)")
1560 1560 h[f] = doc.splitlines()[0].rstrip()
1561 1561 cmds[f] = c.lstrip("^")
1562 1562
1563 1563 if not h:
1564 1564 ui.status(_('no commands defined\n'))
1565 1565 return
1566 1566
1567 1567 ui.status(header)
1568 1568 fns = sorted(h)
1569 1569 m = max(map(len, fns))
1570 1570 for f in fns:
1571 1571 if ui.verbose:
1572 1572 commands = cmds[f].replace("|",", ")
1573 1573 ui.write(" %s:\n %s\n"%(commands, h[f]))
1574 1574 else:
1575 1575 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1576 1576
1577 1577 if not ui.quiet:
1578 1578 addglobalopts(True)
1579 1579
1580 1580 def helptopic(name):
1581 1581 for names, header, doc in help.helptable:
1582 1582 if name in names:
1583 1583 break
1584 1584 else:
1585 1585 raise error.UnknownCommand(name)
1586 1586
1587 1587 # description
1588 1588 if not doc:
1589 1589 doc = _("(no help text available)")
1590 1590 if hasattr(doc, '__call__'):
1591 1591 doc = doc()
1592 1592
1593 1593 ui.write("%s\n\n" % header)
1594 1594 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1595 1595
1596 1596 def helpext(name):
1597 1597 try:
1598 1598 mod = extensions.find(name)
1599 1599 doc = gettext(mod.__doc__) or _('no help text available')
1600 1600 except KeyError:
1601 1601 mod = None
1602 1602 doc = extensions.disabledext(name)
1603 1603 if not doc:
1604 1604 raise error.UnknownCommand(name)
1605 1605
1606 1606 if '\n' not in doc:
1607 1607 head, tail = doc, ""
1608 1608 else:
1609 1609 head, tail = doc.split('\n', 1)
1610 1610 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1611 1611 if tail:
1612 1612 ui.write(minirst.format(tail, textwidth))
1613 1613 ui.status('\n\n')
1614 1614
1615 1615 if mod:
1616 1616 try:
1617 1617 ct = mod.cmdtable
1618 1618 except AttributeError:
1619 1619 ct = {}
1620 1620 modcmds = set([c.split('|', 1)[0] for c in ct])
1621 1621 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1622 1622 else:
1623 1623 ui.write(_('use "hg help extensions" for information on enabling '
1624 1624 'extensions\n'))
1625 1625
1626 1626 def helpextcmd(name):
1627 1627 cmd, ext, mod = extensions.disabledcmd(name, ui.config('ui', 'strict'))
1628 1628 doc = gettext(mod.__doc__).splitlines()[0]
1629 1629
1630 1630 msg = help.listexts(_("'%s' is provided by the following "
1631 1631 "extension:") % cmd, {ext: doc}, len(ext),
1632 1632 indent=4)
1633 1633 ui.write(minirst.format(msg, textwidth))
1634 1634 ui.write('\n\n')
1635 1635 ui.write(_('use "hg help extensions" for information on enabling '
1636 1636 'extensions\n'))
1637 1637
1638 1638 if name and name != 'shortlist':
1639 1639 i = None
1640 1640 if unknowncmd:
1641 1641 queries = (helpextcmd,)
1642 1642 else:
1643 1643 queries = (helptopic, helpcmd, helpext, helpextcmd)
1644 1644 for f in queries:
1645 1645 try:
1646 1646 f(name)
1647 1647 i = None
1648 1648 break
1649 1649 except error.UnknownCommand, inst:
1650 1650 i = inst
1651 1651 if i:
1652 1652 raise i
1653 1653
1654 1654 else:
1655 1655 # program name
1656 1656 if ui.verbose or with_version:
1657 1657 version_(ui)
1658 1658 else:
1659 1659 ui.status(_("Mercurial Distributed SCM\n"))
1660 1660 ui.status('\n')
1661 1661
1662 1662 # list of commands
1663 1663 if name == "shortlist":
1664 1664 header = _('basic commands:\n\n')
1665 1665 else:
1666 1666 header = _('list of commands:\n\n')
1667 1667
1668 1668 helplist(header)
1669 1669 if name != 'shortlist':
1670 1670 exts, maxlength = extensions.enabled()
1671 1671 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1672 1672 if text:
1673 1673 ui.write("\n%s\n" % minirst.format(text, textwidth))
1674 1674
1675 1675 # list all option lists
1676 1676 opt_output = []
1677 1677 for title, options in option_lists:
1678 1678 opt_output.append(("\n%s" % title, None))
1679 1679 for shortopt, longopt, default, desc in options:
1680 1680 if _("DEPRECATED") in desc and not ui.verbose:
1681 1681 continue
1682 1682 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1683 1683 longopt and " --%s" % longopt),
1684 1684 "%s%s" % (desc,
1685 1685 default
1686 1686 and _(" (default: %s)") % default
1687 1687 or "")))
1688 1688
1689 1689 if not name:
1690 1690 ui.write(_("\nadditional help topics:\n\n"))
1691 1691 topics = []
1692 1692 for names, header, doc in help.helptable:
1693 1693 topics.append((sorted(names, key=len, reverse=True)[0], header))
1694 1694 topics_len = max([len(s[0]) for s in topics])
1695 1695 for t, desc in topics:
1696 1696 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1697 1697
1698 1698 if opt_output:
1699 1699 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1700 1700 for first, second in opt_output:
1701 1701 if second:
1702 1702 second = util.wrap(second, opts_len + 3)
1703 1703 ui.write(" %-*s %s\n" % (opts_len, first, second))
1704 1704 else:
1705 1705 ui.write("%s\n" % first)
1706 1706
1707 1707 def identify(ui, repo, source=None,
1708 1708 rev=None, num=None, id=None, branch=None, tags=None):
1709 1709 """identify the working copy or specified revision
1710 1710
1711 1711 With no revision, print a summary of the current state of the
1712 1712 repository.
1713 1713
1714 1714 Specifying a path to a repository root or Mercurial bundle will
1715 1715 cause lookup to operate on that repository/bundle.
1716 1716
1717 1717 This summary identifies the repository state using one or two
1718 1718 parent hash identifiers, followed by a "+" if there are
1719 1719 uncommitted changes in the working directory, a list of tags for
1720 1720 this revision and a branch name for non-default branches.
1721 1721 """
1722 1722
1723 1723 if not repo and not source:
1724 1724 raise util.Abort(_("There is no Mercurial repository here "
1725 1725 "(.hg not found)"))
1726 1726
1727 1727 hexfunc = ui.debugflag and hex or short
1728 1728 default = not (num or id or branch or tags)
1729 1729 output = []
1730 1730
1731 1731 revs = []
1732 1732 if source:
1733 1733 source, branches = hg.parseurl(ui.expandpath(source))
1734 1734 repo = hg.repository(ui, source)
1735 1735 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
1736 1736
1737 1737 if not repo.local():
1738 1738 if not rev and revs:
1739 1739 rev = revs[0]
1740 1740 if not rev:
1741 1741 rev = "tip"
1742 1742 if num or branch or tags:
1743 1743 raise util.Abort(
1744 1744 "can't query remote revision number, branch, or tags")
1745 1745 output = [hexfunc(repo.lookup(rev))]
1746 1746 elif not rev:
1747 1747 ctx = repo[None]
1748 1748 parents = ctx.parents()
1749 1749 changed = False
1750 1750 if default or id or num:
1751 1751 changed = ctx.files() + ctx.deleted()
1752 1752 if default or id:
1753 1753 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1754 1754 (changed) and "+" or "")]
1755 1755 if num:
1756 1756 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1757 1757 (changed) and "+" or ""))
1758 1758 else:
1759 1759 ctx = repo[rev]
1760 1760 if default or id:
1761 1761 output = [hexfunc(ctx.node())]
1762 1762 if num:
1763 1763 output.append(str(ctx.rev()))
1764 1764
1765 1765 if repo.local() and default and not ui.quiet:
1766 1766 b = encoding.tolocal(ctx.branch())
1767 1767 if b != 'default':
1768 1768 output.append("(%s)" % b)
1769 1769
1770 1770 # multiple tags for a single parent separated by '/'
1771 1771 t = "/".join(ctx.tags())
1772 1772 if t:
1773 1773 output.append(t)
1774 1774
1775 1775 if branch:
1776 1776 output.append(encoding.tolocal(ctx.branch()))
1777 1777
1778 1778 if tags:
1779 1779 output.extend(ctx.tags())
1780 1780
1781 1781 ui.write("%s\n" % ' '.join(output))
1782 1782
1783 1783 def import_(ui, repo, patch1, *patches, **opts):
1784 1784 """import an ordered set of patches
1785 1785
1786 1786 Import a list of patches and commit them individually (unless
1787 1787 --no-commit is specified).
1788 1788
1789 1789 If there are outstanding changes in the working directory, import
1790 1790 will abort unless given the -f/--force flag.
1791 1791
1792 1792 You can import a patch straight from a mail message. Even patches
1793 1793 as attachments work (to use the body part, it must have type
1794 1794 text/plain or text/x-patch). From and Subject headers of email
1795 1795 message are used as default committer and commit message. All
1796 1796 text/plain body parts before first diff are added to commit
1797 1797 message.
1798 1798
1799 1799 If the imported patch was generated by hg export, user and
1800 1800 description from patch override values from message headers and
1801 1801 body. Values given on command line with -m/--message and -u/--user
1802 1802 override these.
1803 1803
1804 1804 If --exact is specified, import will set the working directory to
1805 1805 the parent of each patch before applying it, and will abort if the
1806 1806 resulting changeset has a different ID than the one recorded in
1807 1807 the patch. This may happen due to character set problems or other
1808 1808 deficiencies in the text patch format.
1809 1809
1810 1810 With -s/--similarity, hg will attempt to discover renames and
1811 1811 copies in the patch in the same way as 'addremove'.
1812 1812
1813 1813 To read a patch from standard input, use "-" as the patch name. If
1814 1814 a URL is specified, the patch will be downloaded from it.
1815 1815 See 'hg help dates' for a list of formats valid for -d/--date.
1816 1816 """
1817 1817 patches = (patch1,) + patches
1818 1818
1819 1819 date = opts.get('date')
1820 1820 if date:
1821 1821 opts['date'] = util.parsedate(date)
1822 1822
1823 1823 try:
1824 1824 sim = float(opts.get('similarity') or 0)
1825 1825 except ValueError:
1826 1826 raise util.Abort(_('similarity must be a number'))
1827 1827 if sim < 0 or sim > 100:
1828 1828 raise util.Abort(_('similarity must be between 0 and 100'))
1829 1829
1830 1830 if opts.get('exact') or not opts.get('force'):
1831 1831 cmdutil.bail_if_changed(repo)
1832 1832
1833 1833 d = opts["base"]
1834 1834 strip = opts["strip"]
1835 1835 wlock = lock = None
1836
1837 def tryone(ui, hunk):
1838 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, hunk)
1839
1840 if not tmpname:
1841 return None
1842 commitid = _('to working directory')
1843
1844 try:
1845 cmdline_message = cmdutil.logmessage(opts)
1846 if cmdline_message:
1847 # pickup the cmdline msg
1848 message = cmdline_message
1849 elif message:
1850 # pickup the patch msg
1851 message = message.strip()
1852 else:
1853 # launch the editor
1854 message = None
1855 ui.debug('message:\n%s\n' % message)
1856
1857 wp = repo.parents()
1858 if opts.get('exact'):
1859 if not nodeid or not p1:
1860 raise util.Abort(_('not a Mercurial patch'))
1861 p1 = repo.lookup(p1)
1862 p2 = repo.lookup(p2 or hex(nullid))
1863
1864 if p1 != wp[0].node():
1865 hg.clean(repo, p1)
1866 repo.dirstate.setparents(p1, p2)
1867 elif p2:
1868 try:
1869 p1 = repo.lookup(p1)
1870 p2 = repo.lookup(p2)
1871 if p1 == wp[0].node():
1872 repo.dirstate.setparents(p1, p2)
1873 except error.RepoError:
1874 pass
1875 if opts.get('exact') or opts.get('import_branch'):
1876 repo.dirstate.setbranch(branch or 'default')
1877
1878 files = {}
1879 try:
1880 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1881 files=files, eolmode=None)
1882 finally:
1883 files = patch.updatedir(ui, repo, files,
1884 similarity=sim / 100.0)
1885 if not opts.get('no_commit'):
1886 if opts.get('exact'):
1887 m = None
1888 else:
1889 m = cmdutil.matchfiles(repo, files or [])
1890 n = repo.commit(message, opts.get('user') or user,
1891 opts.get('date') or date, match=m,
1892 editor=cmdutil.commiteditor)
1893 if opts.get('exact'):
1894 if hex(n) != nodeid:
1895 repo.rollback()
1896 raise util.Abort(_('patch is damaged'
1897 ' or loses information'))
1898 # Force a dirstate write so that the next transaction
1899 # backups an up-do-date file.
1900 repo.dirstate.write()
1901 if n:
1902 commitid = short(n)
1903
1904 return commitid
1905 finally:
1906 os.unlink(tmpname)
1907
1836 1908 try:
1837 1909 wlock = repo.wlock()
1838 1910 lock = repo.lock()
1911 lastcommit = None
1839 1912 for p in patches:
1840 1913 pf = os.path.join(d, p)
1841 1914
1842 1915 if pf == '-':
1843 1916 ui.status(_("applying patch from stdin\n"))
1844 1917 pf = sys.stdin
1845 1918 else:
1846 1919 ui.status(_("applying %s\n") % p)
1847 1920 pf = url.open(ui, pf)
1848 data = patch.extract(ui, pf)
1849 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1850
1851 if tmpname is None:
1921
1922 haspatch = False
1923 for hunk in patch.split(pf):
1924 commitid = tryone(ui, hunk)
1925 if commitid:
1926 haspatch = True
1927 if lastcommit:
1928 ui.status(_('applied %s\n') % lastcommit)
1929 lastcommit = commitid
1930
1931 if not haspatch:
1852 1932 raise util.Abort(_('no diffs found'))
1853 1933
1854 try:
1855 cmdline_message = cmdutil.logmessage(opts)
1856 if cmdline_message:
1857 # pickup the cmdline msg
1858 message = cmdline_message
1859 elif message:
1860 # pickup the patch msg
1861 message = message.strip()
1862 else:
1863 # launch the editor
1864 message = None
1865 ui.debug('message:\n%s\n' % message)
1866
1867 wp = repo.parents()
1868 if opts.get('exact'):
1869 if not nodeid or not p1:
1870 raise util.Abort(_('not a Mercurial patch'))
1871 p1 = repo.lookup(p1)
1872 p2 = repo.lookup(p2 or hex(nullid))
1873
1874 if p1 != wp[0].node():
1875 hg.clean(repo, p1)
1876 repo.dirstate.setparents(p1, p2)
1877 elif p2:
1878 try:
1879 p1 = repo.lookup(p1)
1880 p2 = repo.lookup(p2)
1881 if p1 == wp[0].node():
1882 repo.dirstate.setparents(p1, p2)
1883 except error.RepoError:
1884 pass
1885 if opts.get('exact') or opts.get('import_branch'):
1886 repo.dirstate.setbranch(branch or 'default')
1887
1888 files = {}
1889 try:
1890 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1891 files=files, eolmode=None)
1892 finally:
1893 files = patch.updatedir(ui, repo, files,
1894 similarity=sim / 100.0)
1895 if not opts.get('no_commit'):
1896 m = cmdutil.matchfiles(repo, files or [])
1897 n = repo.commit(message, opts.get('user') or user,
1898 opts.get('date') or date, match=m,
1899 editor=cmdutil.commiteditor)
1900 if opts.get('exact'):
1901 if hex(n) != nodeid:
1902 repo.rollback()
1903 raise util.Abort(_('patch is damaged'
1904 ' or loses information'))
1905 # Force a dirstate write so that the next transaction
1906 # backups an up-do-date file.
1907 repo.dirstate.write()
1908 finally:
1909 os.unlink(tmpname)
1910 1934 finally:
1911 1935 release(lock, wlock)
1912 1936
1913 1937 def incoming(ui, repo, source="default", **opts):
1914 1938 """show new changesets found in source
1915 1939
1916 1940 Show new changesets found in the specified path/URL or the default
1917 1941 pull location. These are the changesets that would have been pulled
1918 1942 if a pull at the time you issued this command.
1919 1943
1920 1944 For remote repository, using --bundle avoids downloading the
1921 1945 changesets twice if the incoming is followed by a pull.
1922 1946
1923 1947 See pull for valid source format details.
1924 1948 """
1925 1949 limit = cmdutil.loglimit(opts)
1926 1950 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
1927 1951 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1928 1952 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1929 1953 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
1930 1954 if revs:
1931 1955 revs = [other.lookup(rev) for rev in revs]
1932 1956 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1933 1957 force=opts["force"])
1934 1958 if not incoming:
1935 1959 try:
1936 1960 os.unlink(opts["bundle"])
1937 1961 except:
1938 1962 pass
1939 1963 ui.status(_("no changes found\n"))
1940 1964 return 1
1941 1965
1942 1966 cleanup = None
1943 1967 try:
1944 1968 fname = opts["bundle"]
1945 1969 if fname or not other.local():
1946 1970 # create a bundle (uncompressed if other repo is not local)
1947 1971
1948 1972 if revs is None and other.capable('changegroupsubset'):
1949 1973 revs = rheads
1950 1974
1951 1975 if revs is None:
1952 1976 cg = other.changegroup(incoming, "incoming")
1953 1977 else:
1954 1978 cg = other.changegroupsubset(incoming, revs, 'incoming')
1955 1979 bundletype = other.local() and "HG10BZ" or "HG10UN"
1956 1980 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1957 1981 # keep written bundle?
1958 1982 if opts["bundle"]:
1959 1983 cleanup = None
1960 1984 if not other.local():
1961 1985 # use the created uncompressed bundlerepo
1962 1986 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1963 1987
1964 1988 o = other.changelog.nodesbetween(incoming, revs)[0]
1965 1989 if opts.get('newest_first'):
1966 1990 o.reverse()
1967 1991 displayer = cmdutil.show_changeset(ui, other, opts)
1968 1992 count = 0
1969 1993 for n in o:
1970 1994 if limit is not None and count >= limit:
1971 1995 break
1972 1996 parents = [p for p in other.changelog.parents(n) if p != nullid]
1973 1997 if opts.get('no_merges') and len(parents) == 2:
1974 1998 continue
1975 1999 count += 1
1976 2000 displayer.show(other[n])
1977 2001 displayer.close()
1978 2002 finally:
1979 2003 if hasattr(other, 'close'):
1980 2004 other.close()
1981 2005 if cleanup:
1982 2006 os.unlink(cleanup)
1983 2007
1984 2008 def init(ui, dest=".", **opts):
1985 2009 """create a new repository in the given directory
1986 2010
1987 2011 Initialize a new repository in the given directory. If the given
1988 2012 directory does not exist, it will be created.
1989 2013
1990 2014 If no directory is given, the current directory is used.
1991 2015
1992 2016 It is possible to specify an ``ssh://`` URL as the destination.
1993 2017 See 'hg help urls' for more information.
1994 2018 """
1995 2019 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1996 2020
1997 2021 def locate(ui, repo, *pats, **opts):
1998 2022 """locate files matching specific patterns
1999 2023
2000 2024 Print files under Mercurial control in the working directory whose
2001 2025 names match the given patterns.
2002 2026
2003 2027 By default, this command searches all directories in the working
2004 2028 directory. To search just the current directory and its
2005 2029 subdirectories, use "--include .".
2006 2030
2007 2031 If no patterns are given to match, this command prints the names
2008 2032 of all files under Mercurial control in the working directory.
2009 2033
2010 2034 If you want to feed the output of this command into the "xargs"
2011 2035 command, use the -0 option to both this command and "xargs". This
2012 2036 will avoid the problem of "xargs" treating single filenames that
2013 2037 contain whitespace as multiple filenames.
2014 2038 """
2015 2039 end = opts.get('print0') and '\0' or '\n'
2016 2040 rev = opts.get('rev') or None
2017 2041
2018 2042 ret = 1
2019 2043 m = cmdutil.match(repo, pats, opts, default='relglob')
2020 2044 m.bad = lambda x, y: False
2021 2045 for abs in repo[rev].walk(m):
2022 2046 if not rev and abs not in repo.dirstate:
2023 2047 continue
2024 2048 if opts.get('fullpath'):
2025 2049 ui.write(repo.wjoin(abs), end)
2026 2050 else:
2027 2051 ui.write(((pats and m.rel(abs)) or abs), end)
2028 2052 ret = 0
2029 2053
2030 2054 return ret
2031 2055
2032 2056 def log(ui, repo, *pats, **opts):
2033 2057 """show revision history of entire repository or files
2034 2058
2035 2059 Print the revision history of the specified files or the entire
2036 2060 project.
2037 2061
2038 2062 File history is shown without following rename or copy history of
2039 2063 files. Use -f/--follow with a filename to follow history across
2040 2064 renames and copies. --follow without a filename will only show
2041 2065 ancestors or descendants of the starting revision. --follow-first
2042 2066 only follows the first parent of merge revisions.
2043 2067
2044 2068 If no revision range is specified, the default is tip:0 unless
2045 2069 --follow is set, in which case the working directory parent is
2046 2070 used as the starting revision.
2047 2071
2048 2072 See 'hg help dates' for a list of formats valid for -d/--date.
2049 2073
2050 2074 By default this command prints revision number and changeset id,
2051 2075 tags, non-trivial parents, user, date and time, and a summary for
2052 2076 each commit. When the -v/--verbose switch is used, the list of
2053 2077 changed files and full commit message are shown.
2054 2078
2055 2079 NOTE: log -p/--patch may generate unexpected diff output for merge
2056 2080 changesets, as it will only compare the merge changeset against
2057 2081 its first parent. Also, only files different from BOTH parents
2058 2082 will appear in files:.
2059 2083 """
2060 2084
2061 2085 matchfn = cmdutil.match(repo, pats, opts)
2062 2086 limit = cmdutil.loglimit(opts)
2063 2087 count = 0
2064 2088
2065 2089 endrev = None
2066 2090 if opts.get('copies') and opts.get('rev'):
2067 2091 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2068 2092
2069 2093 df = False
2070 2094 if opts["date"]:
2071 2095 df = util.matchdate(opts["date"])
2072 2096
2073 2097 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2074 2098 def prep(ctx, fns):
2075 2099 rev = ctx.rev()
2076 2100 parents = [p for p in repo.changelog.parentrevs(rev)
2077 2101 if p != nullrev]
2078 2102 if opts.get('no_merges') and len(parents) == 2:
2079 2103 return
2080 2104 if opts.get('only_merges') and len(parents) != 2:
2081 2105 return
2082 2106 if opts.get('only_branch') and ctx.branch() not in opts['only_branch']:
2083 2107 return
2084 2108 if df and not df(ctx.date()[0]):
2085 2109 return
2086 2110 if opts['user'] and not [k for k in opts['user'] if k in ctx.user()]:
2087 2111 return
2088 2112 if opts.get('keyword'):
2089 2113 for k in [kw.lower() for kw in opts['keyword']]:
2090 2114 if (k in ctx.user().lower() or
2091 2115 k in ctx.description().lower() or
2092 2116 k in " ".join(ctx.files()).lower()):
2093 2117 break
2094 2118 else:
2095 2119 return
2096 2120
2097 2121 copies = None
2098 2122 if opts.get('copies') and rev:
2099 2123 copies = []
2100 2124 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2101 2125 for fn in ctx.files():
2102 2126 rename = getrenamed(fn, rev)
2103 2127 if rename:
2104 2128 copies.append((fn, rename[0]))
2105 2129
2106 2130 displayer.show(ctx, copies=copies)
2107 2131
2108 2132 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2109 2133 if count == limit:
2110 2134 break
2111 2135 if displayer.flush(ctx.rev()):
2112 2136 count += 1
2113 2137 displayer.close()
2114 2138
2115 2139 def manifest(ui, repo, node=None, rev=None):
2116 2140 """output the current or given revision of the project manifest
2117 2141
2118 2142 Print a list of version controlled files for the given revision.
2119 2143 If no revision is given, the first parent of the working directory
2120 2144 is used, or the null revision if no revision is checked out.
2121 2145
2122 2146 With -v, print file permissions, symlink and executable bits.
2123 2147 With --debug, print file revision hashes.
2124 2148 """
2125 2149
2126 2150 if rev and node:
2127 2151 raise util.Abort(_("please specify just one revision"))
2128 2152
2129 2153 if not node:
2130 2154 node = rev
2131 2155
2132 2156 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2133 2157 ctx = repo[node]
2134 2158 for f in ctx:
2135 2159 if ui.debugflag:
2136 2160 ui.write("%40s " % hex(ctx.manifest()[f]))
2137 2161 if ui.verbose:
2138 2162 ui.write(decor[ctx.flags(f)])
2139 2163 ui.write("%s\n" % f)
2140 2164
2141 2165 def merge(ui, repo, node=None, **opts):
2142 2166 """merge working directory with another revision
2143 2167
2144 2168 The current working directory is updated with all changes made in
2145 2169 the requested revision since the last common predecessor revision.
2146 2170
2147 2171 Files that changed between either parent are marked as changed for
2148 2172 the next commit and a commit must be performed before any further
2149 2173 updates to the repository are allowed. The next commit will have
2150 2174 two parents.
2151 2175
2152 2176 If no revision is specified, the working directory's parent is a
2153 2177 head revision, and the current branch contains exactly one other
2154 2178 head, the other head is merged with by default. Otherwise, an
2155 2179 explicit revision with which to merge with must be provided.
2156 2180 """
2157 2181
2158 2182 if opts.get('rev') and node:
2159 2183 raise util.Abort(_("please specify just one revision"))
2160 2184 if not node:
2161 2185 node = opts.get('rev')
2162 2186
2163 2187 if not node:
2164 2188 branch = repo.changectx(None).branch()
2165 2189 bheads = repo.branchheads(branch)
2166 2190 if len(bheads) > 2:
2167 2191 ui.warn(_("abort: branch '%s' has %d heads - "
2168 2192 "please merge with an explicit rev\n")
2169 2193 % (branch, len(bheads)))
2170 2194 ui.status(_("(run 'hg heads .' to see heads)\n"))
2171 2195 return False
2172 2196
2173 2197 parent = repo.dirstate.parents()[0]
2174 2198 if len(bheads) == 1:
2175 2199 if len(repo.heads()) > 1:
2176 2200 ui.warn(_("abort: branch '%s' has one head - "
2177 2201 "please merge with an explicit rev\n" % branch))
2178 2202 ui.status(_("(run 'hg heads' to see all heads)\n"))
2179 2203 return False
2180 2204 msg = _('there is nothing to merge')
2181 2205 if parent != repo.lookup(repo[None].branch()):
2182 2206 msg = _('%s - use "hg update" instead') % msg
2183 2207 raise util.Abort(msg)
2184 2208
2185 2209 if parent not in bheads:
2186 2210 raise util.Abort(_('working dir not at a head rev - '
2187 2211 'use "hg update" or merge with an explicit rev'))
2188 2212 node = parent == bheads[0] and bheads[-1] or bheads[0]
2189 2213
2190 2214 if opts.get('preview'):
2191 2215 p1 = repo['.']
2192 2216 p2 = repo[node]
2193 2217 common = p1.ancestor(p2)
2194 2218 roots, heads = [common.node()], [p2.node()]
2195 2219 displayer = cmdutil.show_changeset(ui, repo, opts)
2196 2220 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2197 2221 if node not in roots:
2198 2222 displayer.show(repo[node])
2199 2223 displayer.close()
2200 2224 return 0
2201 2225
2202 2226 return hg.merge(repo, node, force=opts.get('force'))
2203 2227
2204 2228 def outgoing(ui, repo, dest=None, **opts):
2205 2229 """show changesets not found in the destination
2206 2230
2207 2231 Show changesets not found in the specified destination repository
2208 2232 or the default push location. These are the changesets that would
2209 2233 be pushed if a push was requested.
2210 2234
2211 2235 See pull for details of valid destination formats.
2212 2236 """
2213 2237 limit = cmdutil.loglimit(opts)
2214 2238 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2215 2239 dest, branches = hg.parseurl(dest, opts.get('branch'))
2216 2240 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2217 2241 if revs:
2218 2242 revs = [repo.lookup(rev) for rev in revs]
2219 2243
2220 2244 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2221 2245 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2222 2246 o = repo.findoutgoing(other, force=opts.get('force'))
2223 2247 if not o:
2224 2248 ui.status(_("no changes found\n"))
2225 2249 return 1
2226 2250 o = repo.changelog.nodesbetween(o, revs)[0]
2227 2251 if opts.get('newest_first'):
2228 2252 o.reverse()
2229 2253 displayer = cmdutil.show_changeset(ui, repo, opts)
2230 2254 count = 0
2231 2255 for n in o:
2232 2256 if limit is not None and count >= limit:
2233 2257 break
2234 2258 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2235 2259 if opts.get('no_merges') and len(parents) == 2:
2236 2260 continue
2237 2261 count += 1
2238 2262 displayer.show(repo[n])
2239 2263 displayer.close()
2240 2264
2241 2265 def parents(ui, repo, file_=None, **opts):
2242 2266 """show the parents of the working directory or revision
2243 2267
2244 2268 Print the working directory's parent revisions. If a revision is
2245 2269 given via -r/--rev, the parent of that revision will be printed.
2246 2270 If a file argument is given, the revision in which the file was
2247 2271 last changed (before the working directory revision or the
2248 2272 argument to --rev if given) is printed.
2249 2273 """
2250 2274 rev = opts.get('rev')
2251 2275 if rev:
2252 2276 ctx = repo[rev]
2253 2277 else:
2254 2278 ctx = repo[None]
2255 2279
2256 2280 if file_:
2257 2281 m = cmdutil.match(repo, (file_,), opts)
2258 2282 if m.anypats() or len(m.files()) != 1:
2259 2283 raise util.Abort(_('can only specify an explicit filename'))
2260 2284 file_ = m.files()[0]
2261 2285 filenodes = []
2262 2286 for cp in ctx.parents():
2263 2287 if not cp:
2264 2288 continue
2265 2289 try:
2266 2290 filenodes.append(cp.filenode(file_))
2267 2291 except error.LookupError:
2268 2292 pass
2269 2293 if not filenodes:
2270 2294 raise util.Abort(_("'%s' not found in manifest!") % file_)
2271 2295 fl = repo.file(file_)
2272 2296 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2273 2297 else:
2274 2298 p = [cp.node() for cp in ctx.parents()]
2275 2299
2276 2300 displayer = cmdutil.show_changeset(ui, repo, opts)
2277 2301 for n in p:
2278 2302 if n != nullid:
2279 2303 displayer.show(repo[n])
2280 2304 displayer.close()
2281 2305
2282 2306 def paths(ui, repo, search=None):
2283 2307 """show aliases for remote repositories
2284 2308
2285 2309 Show definition of symbolic path name NAME. If no name is given,
2286 2310 show definition of all available names.
2287 2311
2288 2312 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2289 2313 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2290 2314
2291 2315 See 'hg help urls' for more information.
2292 2316 """
2293 2317 if search:
2294 2318 for name, path in ui.configitems("paths"):
2295 2319 if name == search:
2296 2320 ui.write("%s\n" % url.hidepassword(path))
2297 2321 return
2298 2322 ui.warn(_("not found!\n"))
2299 2323 return 1
2300 2324 else:
2301 2325 for name, path in ui.configitems("paths"):
2302 2326 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2303 2327
2304 2328 def postincoming(ui, repo, modheads, optupdate, checkout):
2305 2329 if modheads == 0:
2306 2330 return
2307 2331 if optupdate:
2308 2332 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2309 2333 return hg.update(repo, checkout)
2310 2334 else:
2311 2335 ui.status(_("not updating, since new heads added\n"))
2312 2336 if modheads > 1:
2313 2337 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2314 2338 else:
2315 2339 ui.status(_("(run 'hg update' to get a working copy)\n"))
2316 2340
2317 2341 def pull(ui, repo, source="default", **opts):
2318 2342 """pull changes from the specified source
2319 2343
2320 2344 Pull changes from a remote repository to a local one.
2321 2345
2322 2346 This finds all changes from the repository at the specified path
2323 2347 or URL and adds them to a local repository (the current one unless
2324 2348 -R is specified). By default, this does not update the copy of the
2325 2349 project in the working directory.
2326 2350
2327 2351 Use hg incoming if you want to see what would have been added by a
2328 2352 pull at the time you issued this command. If you then decide to
2329 2353 added those changes to the repository, you should use pull -r X
2330 2354 where X is the last changeset listed by hg incoming.
2331 2355
2332 2356 If SOURCE is omitted, the 'default' path will be used.
2333 2357 See 'hg help urls' for more information.
2334 2358 """
2335 2359 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2336 2360 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2337 2361 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2338 2362 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2339 2363 if revs:
2340 2364 try:
2341 2365 revs = [other.lookup(rev) for rev in revs]
2342 2366 except error.CapabilityError:
2343 2367 err = _("Other repository doesn't support revision lookup, "
2344 2368 "so a rev cannot be specified.")
2345 2369 raise util.Abort(err)
2346 2370
2347 2371 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2348 2372 if checkout:
2349 2373 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2350 2374 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2351 2375
2352 2376 def push(ui, repo, dest=None, **opts):
2353 2377 """push changes to the specified destination
2354 2378
2355 2379 Push changes from the local repository to the specified destination.
2356 2380
2357 2381 This is the symmetrical operation for pull. It moves changes from
2358 2382 the current repository to a different one. If the destination is
2359 2383 local this is identical to a pull in that directory from the
2360 2384 current one.
2361 2385
2362 2386 By default, push will refuse to run if it detects the result would
2363 2387 increase the number of remote heads. This generally indicates the
2364 2388 user forgot to pull and merge before pushing.
2365 2389
2366 2390 If -r/--rev is used, the named revision and all its ancestors will
2367 2391 be pushed to the remote repository.
2368 2392
2369 2393 Please see 'hg help urls' for important details about ``ssh://``
2370 2394 URLs. If DESTINATION is omitted, a default path will be used.
2371 2395 """
2372 2396 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2373 2397 dest, branches = hg.parseurl(dest, opts.get('branch'))
2374 2398 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2375 2399 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2376 2400 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2377 2401 if revs:
2378 2402 revs = [repo.lookup(rev) for rev in revs]
2379 2403
2380 2404 # push subrepos depth-first for coherent ordering
2381 2405 c = repo['']
2382 2406 subs = c.substate # only repos that are committed
2383 2407 for s in sorted(subs):
2384 2408 c.sub(s).push(opts.get('force'))
2385 2409
2386 2410 r = repo.push(other, opts.get('force'), revs=revs)
2387 2411 return r == 0
2388 2412
2389 2413 def recover(ui, repo):
2390 2414 """roll back an interrupted transaction
2391 2415
2392 2416 Recover from an interrupted commit or pull.
2393 2417
2394 2418 This command tries to fix the repository status after an
2395 2419 interrupted operation. It should only be necessary when Mercurial
2396 2420 suggests it.
2397 2421 """
2398 2422 if repo.recover():
2399 2423 return hg.verify(repo)
2400 2424 return 1
2401 2425
2402 2426 def remove(ui, repo, *pats, **opts):
2403 2427 """remove the specified files on the next commit
2404 2428
2405 2429 Schedule the indicated files for removal from the repository.
2406 2430
2407 2431 This only removes files from the current branch, not from the
2408 2432 entire project history. -A/--after can be used to remove only
2409 2433 files that have already been deleted, -f/--force can be used to
2410 2434 force deletion, and -Af can be used to remove files from the next
2411 2435 revision without deleting them from the working directory.
2412 2436
2413 2437 The following table details the behavior of remove for different
2414 2438 file states (columns) and option combinations (rows). The file
2415 2439 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2416 2440 reported by hg status). The actions are Warn, Remove (from branch)
2417 2441 and Delete (from disk)::
2418 2442
2419 2443 A C M !
2420 2444 none W RD W R
2421 2445 -f R RD RD R
2422 2446 -A W W W R
2423 2447 -Af R R R R
2424 2448
2425 2449 This command schedules the files to be removed at the next commit.
2426 2450 To undo a remove before that, see hg revert.
2427 2451 """
2428 2452
2429 2453 after, force = opts.get('after'), opts.get('force')
2430 2454 if not pats and not after:
2431 2455 raise util.Abort(_('no files specified'))
2432 2456
2433 2457 m = cmdutil.match(repo, pats, opts)
2434 2458 s = repo.status(match=m, clean=True)
2435 2459 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2436 2460
2437 2461 for f in m.files():
2438 2462 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2439 2463 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2440 2464
2441 2465 def warn(files, reason):
2442 2466 for f in files:
2443 2467 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2444 2468 % (m.rel(f), reason))
2445 2469
2446 2470 if force:
2447 2471 remove, forget = modified + deleted + clean, added
2448 2472 elif after:
2449 2473 remove, forget = deleted, []
2450 2474 warn(modified + added + clean, _('still exists'))
2451 2475 else:
2452 2476 remove, forget = deleted + clean, []
2453 2477 warn(modified, _('is modified'))
2454 2478 warn(added, _('has been marked for add'))
2455 2479
2456 2480 for f in sorted(remove + forget):
2457 2481 if ui.verbose or not m.exact(f):
2458 2482 ui.status(_('removing %s\n') % m.rel(f))
2459 2483
2460 2484 repo.forget(forget)
2461 2485 repo.remove(remove, unlink=not after)
2462 2486
2463 2487 def rename(ui, repo, *pats, **opts):
2464 2488 """rename files; equivalent of copy + remove
2465 2489
2466 2490 Mark dest as copies of sources; mark sources for deletion. If dest
2467 2491 is a directory, copies are put in that directory. If dest is a
2468 2492 file, there can only be one source.
2469 2493
2470 2494 By default, this command copies the contents of files as they
2471 2495 exist in the working directory. If invoked with -A/--after, the
2472 2496 operation is recorded, but no copying is performed.
2473 2497
2474 2498 This command takes effect at the next commit. To undo a rename
2475 2499 before that, see hg revert.
2476 2500 """
2477 2501 wlock = repo.wlock(False)
2478 2502 try:
2479 2503 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2480 2504 finally:
2481 2505 wlock.release()
2482 2506
2483 2507 def resolve(ui, repo, *pats, **opts):
2484 2508 """retry file merges from a merge or update
2485 2509
2486 2510 This command can cleanly retry unresolved file merges using file
2487 2511 revisions preserved from the last update or merge.
2488 2512
2489 2513 If a conflict is resolved manually, please note that the changes
2490 2514 will be overwritten if the merge is retried with resolve. The
2491 2515 -m/--mark switch should be used to mark the file as resolved.
2492 2516
2493 2517 You can specify a set of files to operate on, or use the -a/--all
2494 2518 switch to select all unresolved files.
2495 2519
2496 2520 This command also allows listing resolved files and manually
2497 2521 indicating whether or not files are resolved. All files must be
2498 2522 marked as resolved before a commit is permitted.
2499 2523
2500 2524 The codes used to show the status of files are::
2501 2525
2502 2526 U = unresolved
2503 2527 R = resolved
2504 2528 """
2505 2529
2506 2530 all, mark, unmark, show, nostatus = \
2507 2531 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
2508 2532
2509 2533 if (show and (mark or unmark)) or (mark and unmark):
2510 2534 raise util.Abort(_("too many options specified"))
2511 2535 if pats and all:
2512 2536 raise util.Abort(_("can't specify --all and patterns"))
2513 2537 if not (all or pats or show or mark or unmark):
2514 2538 raise util.Abort(_('no files or directories specified; '
2515 2539 'use --all to remerge all files'))
2516 2540
2517 2541 ms = merge_.mergestate(repo)
2518 2542 m = cmdutil.match(repo, pats, opts)
2519 2543
2520 2544 for f in ms:
2521 2545 if m(f):
2522 2546 if show:
2523 2547 if nostatus:
2524 2548 ui.write("%s\n" % f)
2525 2549 else:
2526 2550 ui.write("%s %s\n" % (ms[f].upper(), f))
2527 2551 elif mark:
2528 2552 ms.mark(f, "r")
2529 2553 elif unmark:
2530 2554 ms.mark(f, "u")
2531 2555 else:
2532 2556 wctx = repo[None]
2533 2557 mctx = wctx.parents()[-1]
2534 2558
2535 2559 # backup pre-resolve (merge uses .orig for its own purposes)
2536 2560 a = repo.wjoin(f)
2537 2561 util.copyfile(a, a + ".resolve")
2538 2562
2539 2563 # resolve file
2540 2564 ms.resolve(f, wctx, mctx)
2541 2565
2542 2566 # replace filemerge's .orig file with our resolve file
2543 2567 util.rename(a + ".resolve", a + ".orig")
2544 2568
2545 2569 def revert(ui, repo, *pats, **opts):
2546 2570 """restore individual files or directories to an earlier state
2547 2571
2548 2572 (Use update -r to check out earlier revisions, revert does not
2549 2573 change the working directory parents.)
2550 2574
2551 2575 With no revision specified, revert the named files or directories
2552 2576 to the contents they had in the parent of the working directory.
2553 2577 This restores the contents of the affected files to an unmodified
2554 2578 state and unschedules adds, removes, copies, and renames. If the
2555 2579 working directory has two parents, you must explicitly specify a
2556 2580 revision.
2557 2581
2558 2582 Using the -r/--rev option, revert the given files or directories
2559 2583 to their contents as of a specific revision. This can be helpful
2560 2584 to "roll back" some or all of an earlier change. See 'hg help
2561 2585 dates' for a list of formats valid for -d/--date.
2562 2586
2563 2587 Revert modifies the working directory. It does not commit any
2564 2588 changes, or change the parent of the working directory. If you
2565 2589 revert to a revision other than the parent of the working
2566 2590 directory, the reverted files will thus appear modified
2567 2591 afterwards.
2568 2592
2569 2593 If a file has been deleted, it is restored. If the executable mode
2570 2594 of a file was changed, it is reset.
2571 2595
2572 2596 If names are given, all files matching the names are reverted.
2573 2597 If no arguments are given, no files are reverted.
2574 2598
2575 2599 Modified files are saved with a .orig suffix before reverting.
2576 2600 To disable these backups, use --no-backup.
2577 2601 """
2578 2602
2579 2603 if opts["date"]:
2580 2604 if opts["rev"]:
2581 2605 raise util.Abort(_("you can't specify a revision and a date"))
2582 2606 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2583 2607
2584 2608 if not pats and not opts.get('all'):
2585 2609 raise util.Abort(_('no files or directories specified; '
2586 2610 'use --all to revert the whole repo'))
2587 2611
2588 2612 parent, p2 = repo.dirstate.parents()
2589 2613 if not opts.get('rev') and p2 != nullid:
2590 2614 raise util.Abort(_('uncommitted merge - please provide a '
2591 2615 'specific revision'))
2592 2616 ctx = repo[opts.get('rev')]
2593 2617 node = ctx.node()
2594 2618 mf = ctx.manifest()
2595 2619 if node == parent:
2596 2620 pmf = mf
2597 2621 else:
2598 2622 pmf = None
2599 2623
2600 2624 # need all matching names in dirstate and manifest of target rev,
2601 2625 # so have to walk both. do not print errors if files exist in one
2602 2626 # but not other.
2603 2627
2604 2628 names = {}
2605 2629
2606 2630 wlock = repo.wlock()
2607 2631 try:
2608 2632 # walk dirstate.
2609 2633
2610 2634 m = cmdutil.match(repo, pats, opts)
2611 2635 m.bad = lambda x, y: False
2612 2636 for abs in repo.walk(m):
2613 2637 names[abs] = m.rel(abs), m.exact(abs)
2614 2638
2615 2639 # walk target manifest.
2616 2640
2617 2641 def badfn(path, msg):
2618 2642 if path in names:
2619 2643 return
2620 2644 path_ = path + '/'
2621 2645 for f in names:
2622 2646 if f.startswith(path_):
2623 2647 return
2624 2648 ui.warn("%s: %s\n" % (m.rel(path), msg))
2625 2649
2626 2650 m = cmdutil.match(repo, pats, opts)
2627 2651 m.bad = badfn
2628 2652 for abs in repo[node].walk(m):
2629 2653 if abs not in names:
2630 2654 names[abs] = m.rel(abs), m.exact(abs)
2631 2655
2632 2656 m = cmdutil.matchfiles(repo, names)
2633 2657 changes = repo.status(match=m)[:4]
2634 2658 modified, added, removed, deleted = map(set, changes)
2635 2659
2636 2660 # if f is a rename, also revert the source
2637 2661 cwd = repo.getcwd()
2638 2662 for f in added:
2639 2663 src = repo.dirstate.copied(f)
2640 2664 if src and src not in names and repo.dirstate[src] == 'r':
2641 2665 removed.add(src)
2642 2666 names[src] = (repo.pathto(src, cwd), True)
2643 2667
2644 2668 def removeforget(abs):
2645 2669 if repo.dirstate[abs] == 'a':
2646 2670 return _('forgetting %s\n')
2647 2671 return _('removing %s\n')
2648 2672
2649 2673 revert = ([], _('reverting %s\n'))
2650 2674 add = ([], _('adding %s\n'))
2651 2675 remove = ([], removeforget)
2652 2676 undelete = ([], _('undeleting %s\n'))
2653 2677
2654 2678 disptable = (
2655 2679 # dispatch table:
2656 2680 # file state
2657 2681 # action if in target manifest
2658 2682 # action if not in target manifest
2659 2683 # make backup if in target manifest
2660 2684 # make backup if not in target manifest
2661 2685 (modified, revert, remove, True, True),
2662 2686 (added, revert, remove, True, False),
2663 2687 (removed, undelete, None, False, False),
2664 2688 (deleted, revert, remove, False, False),
2665 2689 )
2666 2690
2667 2691 for abs, (rel, exact) in sorted(names.items()):
2668 2692 mfentry = mf.get(abs)
2669 2693 target = repo.wjoin(abs)
2670 2694 def handle(xlist, dobackup):
2671 2695 xlist[0].append(abs)
2672 2696 if dobackup and not opts.get('no_backup') and util.lexists(target):
2673 2697 bakname = "%s.orig" % rel
2674 2698 ui.note(_('saving current version of %s as %s\n') %
2675 2699 (rel, bakname))
2676 2700 if not opts.get('dry_run'):
2677 2701 util.copyfile(target, bakname)
2678 2702 if ui.verbose or not exact:
2679 2703 msg = xlist[1]
2680 2704 if not isinstance(msg, basestring):
2681 2705 msg = msg(abs)
2682 2706 ui.status(msg % rel)
2683 2707 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2684 2708 if abs not in table:
2685 2709 continue
2686 2710 # file has changed in dirstate
2687 2711 if mfentry:
2688 2712 handle(hitlist, backuphit)
2689 2713 elif misslist is not None:
2690 2714 handle(misslist, backupmiss)
2691 2715 break
2692 2716 else:
2693 2717 if abs not in repo.dirstate:
2694 2718 if mfentry:
2695 2719 handle(add, True)
2696 2720 elif exact:
2697 2721 ui.warn(_('file not managed: %s\n') % rel)
2698 2722 continue
2699 2723 # file has not changed in dirstate
2700 2724 if node == parent:
2701 2725 if exact:
2702 2726 ui.warn(_('no changes needed to %s\n') % rel)
2703 2727 continue
2704 2728 if pmf is None:
2705 2729 # only need parent manifest in this unlikely case,
2706 2730 # so do not read by default
2707 2731 pmf = repo[parent].manifest()
2708 2732 if abs in pmf:
2709 2733 if mfentry:
2710 2734 # if version of file is same in parent and target
2711 2735 # manifests, do nothing
2712 2736 if (pmf[abs] != mfentry or
2713 2737 pmf.flags(abs) != mf.flags(abs)):
2714 2738 handle(revert, False)
2715 2739 else:
2716 2740 handle(remove, False)
2717 2741
2718 2742 if not opts.get('dry_run'):
2719 2743 def checkout(f):
2720 2744 fc = ctx[f]
2721 2745 repo.wwrite(f, fc.data(), fc.flags())
2722 2746
2723 2747 audit_path = util.path_auditor(repo.root)
2724 2748 for f in remove[0]:
2725 2749 if repo.dirstate[f] == 'a':
2726 2750 repo.dirstate.forget(f)
2727 2751 continue
2728 2752 audit_path(f)
2729 2753 try:
2730 2754 util.unlink(repo.wjoin(f))
2731 2755 except OSError:
2732 2756 pass
2733 2757 repo.dirstate.remove(f)
2734 2758
2735 2759 normal = None
2736 2760 if node == parent:
2737 2761 # We're reverting to our parent. If possible, we'd like status
2738 2762 # to report the file as clean. We have to use normallookup for
2739 2763 # merges to avoid losing information about merged/dirty files.
2740 2764 if p2 != nullid:
2741 2765 normal = repo.dirstate.normallookup
2742 2766 else:
2743 2767 normal = repo.dirstate.normal
2744 2768 for f in revert[0]:
2745 2769 checkout(f)
2746 2770 if normal:
2747 2771 normal(f)
2748 2772
2749 2773 for f in add[0]:
2750 2774 checkout(f)
2751 2775 repo.dirstate.add(f)
2752 2776
2753 2777 normal = repo.dirstate.normallookup
2754 2778 if node == parent and p2 == nullid:
2755 2779 normal = repo.dirstate.normal
2756 2780 for f in undelete[0]:
2757 2781 checkout(f)
2758 2782 normal(f)
2759 2783
2760 2784 finally:
2761 2785 wlock.release()
2762 2786
2763 2787 def rollback(ui, repo):
2764 2788 """roll back the last transaction
2765 2789
2766 2790 This command should be used with care. There is only one level of
2767 2791 rollback, and there is no way to undo a rollback. It will also
2768 2792 restore the dirstate at the time of the last transaction, losing
2769 2793 any dirstate changes since that time. This command does not alter
2770 2794 the working directory.
2771 2795
2772 2796 Transactions are used to encapsulate the effects of all commands
2773 2797 that create new changesets or propagate existing changesets into a
2774 2798 repository. For example, the following commands are transactional,
2775 2799 and their effects can be rolled back:
2776 2800
2777 2801 - commit
2778 2802 - import
2779 2803 - pull
2780 2804 - push (with this repository as the destination)
2781 2805 - unbundle
2782 2806
2783 2807 This command is not intended for use on public repositories. Once
2784 2808 changes are visible for pull by other users, rolling a transaction
2785 2809 back locally is ineffective (someone else may already have pulled
2786 2810 the changes). Furthermore, a race is possible with readers of the
2787 2811 repository; for example an in-progress pull from the repository
2788 2812 may fail if a rollback is performed.
2789 2813 """
2790 2814 repo.rollback()
2791 2815
2792 2816 def root(ui, repo):
2793 2817 """print the root (top) of the current working directory
2794 2818
2795 2819 Print the root directory of the current repository.
2796 2820 """
2797 2821 ui.write(repo.root + "\n")
2798 2822
2799 2823 def serve(ui, repo, **opts):
2800 2824 """export the repository via HTTP
2801 2825
2802 2826 Start a local HTTP repository browser and pull server.
2803 2827
2804 2828 By default, the server logs accesses to stdout and errors to
2805 2829 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2806 2830 files.
2807 2831 """
2808 2832
2809 2833 if opts["stdio"]:
2810 2834 if repo is None:
2811 2835 raise error.RepoError(_("There is no Mercurial repository here"
2812 2836 " (.hg not found)"))
2813 2837 s = sshserver.sshserver(ui, repo)
2814 2838 s.serve_forever()
2815 2839
2816 2840 baseui = repo and repo.baseui or ui
2817 2841 optlist = ("name templates style address port prefix ipv6"
2818 2842 " accesslog errorlog webdir_conf certificate encoding")
2819 2843 for o in optlist.split():
2820 2844 if opts.get(o, None):
2821 2845 baseui.setconfig("web", o, str(opts[o]))
2822 2846 if (repo is not None) and (repo.ui != baseui):
2823 2847 repo.ui.setconfig("web", o, str(opts[o]))
2824 2848
2825 2849 if repo is None and not ui.config("web", "webdir_conf"):
2826 2850 raise error.RepoError(_("There is no Mercurial repository here"
2827 2851 " (.hg not found)"))
2828 2852
2829 2853 class service(object):
2830 2854 def init(self):
2831 2855 util.set_signal_handler()
2832 2856 self.httpd = server.create_server(baseui, repo)
2833 2857
2834 2858 if not ui.verbose:
2835 2859 return
2836 2860
2837 2861 if self.httpd.prefix:
2838 2862 prefix = self.httpd.prefix.strip('/') + '/'
2839 2863 else:
2840 2864 prefix = ''
2841 2865
2842 2866 port = ':%d' % self.httpd.port
2843 2867 if port == ':80':
2844 2868 port = ''
2845 2869
2846 2870 bindaddr = self.httpd.addr
2847 2871 if bindaddr == '0.0.0.0':
2848 2872 bindaddr = '*'
2849 2873 elif ':' in bindaddr: # IPv6
2850 2874 bindaddr = '[%s]' % bindaddr
2851 2875
2852 2876 fqaddr = self.httpd.fqaddr
2853 2877 if ':' in fqaddr:
2854 2878 fqaddr = '[%s]' % fqaddr
2855 2879 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2856 2880 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2857 2881
2858 2882 def run(self):
2859 2883 self.httpd.serve_forever()
2860 2884
2861 2885 service = service()
2862 2886
2863 2887 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2864 2888
2865 2889 def status(ui, repo, *pats, **opts):
2866 2890 """show changed files in the working directory
2867 2891
2868 2892 Show status of files in the repository. If names are given, only
2869 2893 files that match are shown. Files that are clean or ignored or
2870 2894 the source of a copy/move operation, are not listed unless
2871 2895 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2872 2896 Unless options described with "show only ..." are given, the
2873 2897 options -mardu are used.
2874 2898
2875 2899 Option -q/--quiet hides untracked (unknown and ignored) files
2876 2900 unless explicitly requested with -u/--unknown or -i/--ignored.
2877 2901
2878 2902 NOTE: status may appear to disagree with diff if permissions have
2879 2903 changed or a merge has occurred. The standard diff format does not
2880 2904 report permission changes and diff only reports changes relative
2881 2905 to one merge parent.
2882 2906
2883 2907 If one revision is given, it is used as the base revision.
2884 2908 If two revisions are given, the differences between them are
2885 2909 shown. The --change option can also be used as a shortcut to list
2886 2910 the changed files of a revision from its first parent.
2887 2911
2888 2912 The codes used to show the status of files are::
2889 2913
2890 2914 M = modified
2891 2915 A = added
2892 2916 R = removed
2893 2917 C = clean
2894 2918 ! = missing (deleted by non-hg command, but still tracked)
2895 2919 ? = not tracked
2896 2920 I = ignored
2897 2921 = origin of the previous file listed as A (added)
2898 2922 """
2899 2923
2900 2924 revs = opts.get('rev')
2901 2925 change = opts.get('change')
2902 2926
2903 2927 if revs and change:
2904 2928 msg = _('cannot specify --rev and --change at the same time')
2905 2929 raise util.Abort(msg)
2906 2930 elif change:
2907 2931 node2 = repo.lookup(change)
2908 2932 node1 = repo[node2].parents()[0].node()
2909 2933 else:
2910 2934 node1, node2 = cmdutil.revpair(repo, revs)
2911 2935
2912 2936 cwd = (pats and repo.getcwd()) or ''
2913 2937 end = opts.get('print0') and '\0' or '\n'
2914 2938 copy = {}
2915 2939 states = 'modified added removed deleted unknown ignored clean'.split()
2916 2940 show = [k for k in states if opts.get(k)]
2917 2941 if opts.get('all'):
2918 2942 show += ui.quiet and (states[:4] + ['clean']) or states
2919 2943 if not show:
2920 2944 show = ui.quiet and states[:4] or states[:5]
2921 2945
2922 2946 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2923 2947 'ignored' in show, 'clean' in show, 'unknown' in show)
2924 2948 changestates = zip(states, 'MAR!?IC', stat)
2925 2949
2926 2950 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2927 2951 ctxn = repo[nullid]
2928 2952 ctx1 = repo[node1]
2929 2953 ctx2 = repo[node2]
2930 2954 added = stat[1]
2931 2955 if node2 is None:
2932 2956 added = stat[0] + stat[1] # merged?
2933 2957
2934 2958 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2935 2959 if k in added:
2936 2960 copy[k] = v
2937 2961 elif v in added:
2938 2962 copy[v] = k
2939 2963
2940 2964 for state, char, files in changestates:
2941 2965 if state in show:
2942 2966 format = "%s %%s%s" % (char, end)
2943 2967 if opts.get('no_status'):
2944 2968 format = "%%s%s" % end
2945 2969
2946 2970 for f in files:
2947 2971 ui.write(format % repo.pathto(f, cwd))
2948 2972 if f in copy:
2949 2973 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2950 2974
2951 2975 def summary(ui, repo, **opts):
2952 2976 """summarize working directory state
2953 2977
2954 2978 This generates a brief summary of the working directory state,
2955 2979 including parents, branch, commit status, and available updates.
2956 2980
2957 2981 With the --remote option, this will check the default paths for
2958 2982 incoming and outgoing changes. This can be time-consuming.
2959 2983 """
2960 2984
2961 2985 ctx = repo[None]
2962 2986 parents = ctx.parents()
2963 2987 pnode = parents[0].node()
2964 2988 tags = repo.tags()
2965 2989
2966 2990 for p in parents:
2967 2991 t = ' '.join([t for t in tags if tags[t] == p.node()])
2968 2992 if p.rev() == -1:
2969 2993 if not len(repo):
2970 2994 t += _(' (empty repository)')
2971 2995 else:
2972 2996 t += _(' (no revision checked out)')
2973 2997 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2974 2998 if p.description():
2975 2999 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2976 3000
2977 3001 branch = ctx.branch()
2978 3002 bheads = repo.branchheads(branch)
2979 3003 m = _('branch: %s\n') % branch
2980 3004 if branch != 'default':
2981 3005 ui.write(m)
2982 3006 else:
2983 3007 ui.status(m)
2984 3008
2985 3009 st = list(repo.status(unknown=True))[:7]
2986 3010 ms = merge_.mergestate(repo)
2987 3011 st.append([f for f in ms if f == 'u'])
2988 3012 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2989 3013 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2990 3014 _('%d unresolved')]
2991 3015 t = []
2992 3016 for s, l in zip(st, labels):
2993 3017 if s:
2994 3018 t.append(l % len(s))
2995 3019
2996 3020 t = ', '.join(t)
2997 3021 cleanworkdir = False
2998 3022
2999 3023 if len(parents) > 1:
3000 3024 t += _(' (merge)')
3001 3025 elif branch != parents[0].branch():
3002 3026 t += _(' (new branch)')
3003 3027 elif (not st[0] and not st[1] and not st[2]):
3004 3028 t += _(' (clean)')
3005 3029 cleanworkdir = True
3006 3030 elif pnode not in bheads:
3007 3031 t += _(' (new branch head)')
3008 3032
3009 3033 if cleanworkdir:
3010 3034 ui.status(_('commit: %s\n') % t.strip())
3011 3035 else:
3012 3036 ui.write(_('commit: %s\n') % t.strip())
3013 3037
3014 3038 # all ancestors of branch heads - all ancestors of parent = new csets
3015 3039 new = [0] * len(repo)
3016 3040 cl = repo.changelog
3017 3041 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3018 3042 new[a] = 1
3019 3043 for a in cl.ancestors(*[p.rev() for p in parents]):
3020 3044 new[a] = 0
3021 3045 new = sum(new)
3022 3046
3023 3047 if new == 0:
3024 3048 ui.status(_('update: (current)\n'))
3025 3049 elif pnode not in bheads:
3026 3050 ui.write(_('update: %d new changesets (update)\n') % new)
3027 3051 else:
3028 3052 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3029 3053 (new, len(bheads)))
3030 3054
3031 3055 if opts.get('remote'):
3032 3056 t = []
3033 3057 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
3034 3058 opts.get('rev'))
3035 3059 other = hg.repository(cmdutil.remoteui(repo, {}), source)
3036 3060 ui.debug('comparing with %s\n' % url.hidepassword(source))
3037 3061 repo.ui.pushbuffer()
3038 3062 common, incoming, rheads = repo.findcommonincoming(other)
3039 3063 repo.ui.popbuffer()
3040 3064 if incoming:
3041 3065 t.append(_('1 or more incoming'))
3042 3066
3043 3067 dest, revs, checkout = hg.parseurl(
3044 3068 ui.expandpath('default-push', 'default'))
3045 3069 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
3046 3070 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3047 3071 repo.ui.pushbuffer()
3048 3072 o = repo.findoutgoing(other)
3049 3073 repo.ui.popbuffer()
3050 3074 o = repo.changelog.nodesbetween(o, revs)[0]
3051 3075 if o:
3052 3076 t.append(_('%d outgoing') % len(o))
3053 3077
3054 3078 if t:
3055 3079 ui.write(_('remote: %s\n') % (', '.join(t)))
3056 3080 else:
3057 3081 ui.status(_('remote: (synced)\n'))
3058 3082
3059 3083 def tag(ui, repo, name1, *names, **opts):
3060 3084 """add one or more tags for the current or given revision
3061 3085
3062 3086 Name a particular revision using <name>.
3063 3087
3064 3088 Tags are used to name particular revisions of the repository and are
3065 3089 very useful to compare different revisions, to go back to significant
3066 3090 earlier versions or to mark branch points as releases, etc.
3067 3091
3068 3092 If no revision is given, the parent of the working directory is
3069 3093 used, or tip if no revision is checked out.
3070 3094
3071 3095 To facilitate version control, distribution, and merging of tags,
3072 3096 they are stored as a file named ".hgtags" which is managed
3073 3097 similarly to other project files and can be hand-edited if
3074 3098 necessary. The file '.hg/localtags' is used for local tags (not
3075 3099 shared among repositories).
3076 3100
3077 3101 See 'hg help dates' for a list of formats valid for -d/--date.
3078 3102 """
3079 3103
3080 3104 rev_ = "."
3081 3105 names = (name1,) + names
3082 3106 if len(names) != len(set(names)):
3083 3107 raise util.Abort(_('tag names must be unique'))
3084 3108 for n in names:
3085 3109 if n in ['tip', '.', 'null']:
3086 3110 raise util.Abort(_('the name \'%s\' is reserved') % n)
3087 3111 if opts.get('rev') and opts.get('remove'):
3088 3112 raise util.Abort(_("--rev and --remove are incompatible"))
3089 3113 if opts.get('rev'):
3090 3114 rev_ = opts['rev']
3091 3115 message = opts.get('message')
3092 3116 if opts.get('remove'):
3093 3117 expectedtype = opts.get('local') and 'local' or 'global'
3094 3118 for n in names:
3095 3119 if not repo.tagtype(n):
3096 3120 raise util.Abort(_('tag \'%s\' does not exist') % n)
3097 3121 if repo.tagtype(n) != expectedtype:
3098 3122 if expectedtype == 'global':
3099 3123 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3100 3124 else:
3101 3125 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3102 3126 rev_ = nullid
3103 3127 if not message:
3104 3128 # we don't translate commit messages
3105 3129 message = 'Removed tag %s' % ', '.join(names)
3106 3130 elif not opts.get('force'):
3107 3131 for n in names:
3108 3132 if n in repo.tags():
3109 3133 raise util.Abort(_('tag \'%s\' already exists '
3110 3134 '(use -f to force)') % n)
3111 3135 if not rev_ and repo.dirstate.parents()[1] != nullid:
3112 3136 raise util.Abort(_('uncommitted merge - please provide a '
3113 3137 'specific revision'))
3114 3138 r = repo[rev_].node()
3115 3139
3116 3140 if not message:
3117 3141 # we don't translate commit messages
3118 3142 message = ('Added tag %s for changeset %s' %
3119 3143 (', '.join(names), short(r)))
3120 3144
3121 3145 date = opts.get('date')
3122 3146 if date:
3123 3147 date = util.parsedate(date)
3124 3148
3125 3149 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3126 3150
3127 3151 def tags(ui, repo):
3128 3152 """list repository tags
3129 3153
3130 3154 This lists both regular and local tags. When the -v/--verbose
3131 3155 switch is used, a third column "local" is printed for local tags.
3132 3156 """
3133 3157
3134 3158 hexfunc = ui.debugflag and hex or short
3135 3159 tagtype = ""
3136 3160
3137 3161 for t, n in reversed(repo.tagslist()):
3138 3162 if ui.quiet:
3139 3163 ui.write("%s\n" % t)
3140 3164 continue
3141 3165
3142 3166 try:
3143 3167 hn = hexfunc(n)
3144 3168 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3145 3169 except error.LookupError:
3146 3170 r = " ?:%s" % hn
3147 3171 else:
3148 3172 spaces = " " * (30 - encoding.colwidth(t))
3149 3173 if ui.verbose:
3150 3174 if repo.tagtype(t) == 'local':
3151 3175 tagtype = " local"
3152 3176 else:
3153 3177 tagtype = ""
3154 3178 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3155 3179
3156 3180 def tip(ui, repo, **opts):
3157 3181 """show the tip revision
3158 3182
3159 3183 The tip revision (usually just called the tip) is the changeset
3160 3184 most recently added to the repository (and therefore the most
3161 3185 recently changed head).
3162 3186
3163 3187 If you have just made a commit, that commit will be the tip. If
3164 3188 you have just pulled changes from another repository, the tip of
3165 3189 that repository becomes the current tip. The "tip" tag is special
3166 3190 and cannot be renamed or assigned to a different changeset.
3167 3191 """
3168 3192 displayer = cmdutil.show_changeset(ui, repo, opts)
3169 3193 displayer.show(repo[len(repo) - 1])
3170 3194 displayer.close()
3171 3195
3172 3196 def unbundle(ui, repo, fname1, *fnames, **opts):
3173 3197 """apply one or more changegroup files
3174 3198
3175 3199 Apply one or more compressed changegroup files generated by the
3176 3200 bundle command.
3177 3201 """
3178 3202 fnames = (fname1,) + fnames
3179 3203
3180 3204 lock = repo.lock()
3181 3205 try:
3182 3206 for fname in fnames:
3183 3207 f = url.open(ui, fname)
3184 3208 gen = changegroup.readbundle(f, fname)
3185 3209 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3186 3210 finally:
3187 3211 lock.release()
3188 3212
3189 3213 return postincoming(ui, repo, modheads, opts.get('update'), None)
3190 3214
3191 3215 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3192 3216 """update working directory
3193 3217
3194 3218 Update the repository's working directory to the specified
3195 3219 changeset.
3196 3220
3197 3221 If no changeset is specified, attempt to update to the head of the
3198 3222 current branch. If this head is a descendant of the working
3199 3223 directory's parent, update to it, otherwise abort.
3200 3224
3201 3225 The following rules apply when the working directory contains
3202 3226 uncommitted changes:
3203 3227
3204 3228 1. If neither -c/--check nor -C/--clean is specified, and if
3205 3229 the requested changeset is an ancestor or descendant of
3206 3230 the working directory's parent, the uncommitted changes
3207 3231 are merged into the requested changeset and the merged
3208 3232 result is left uncommitted. If the requested changeset is
3209 3233 not an ancestor or descendant (that is, it is on another
3210 3234 branch), the update is aborted and the uncommitted changes
3211 3235 are preserved.
3212 3236
3213 3237 2. With the -c/--check option, the update is aborted and the
3214 3238 uncommitted changes are preserved.
3215 3239
3216 3240 3. With the -C/--clean option, uncommitted changes are discarded and
3217 3241 the working directory is updated to the requested changeset.
3218 3242
3219 3243 Use null as the changeset to remove the working directory (like 'hg
3220 3244 clone -U').
3221 3245
3222 3246 If you want to update just one file to an older changeset, use 'hg revert'.
3223 3247
3224 3248 See 'hg help dates' for a list of formats valid for -d/--date.
3225 3249 """
3226 3250 if rev and node:
3227 3251 raise util.Abort(_("please specify just one revision"))
3228 3252
3229 3253 if not rev:
3230 3254 rev = node
3231 3255
3232 3256 if check and clean:
3233 3257 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3234 3258
3235 3259 if check:
3236 3260 # we could use dirty() but we can ignore merge and branch trivia
3237 3261 c = repo[None]
3238 3262 if c.modified() or c.added() or c.removed():
3239 3263 raise util.Abort(_("uncommitted local changes"))
3240 3264
3241 3265 if date:
3242 3266 if rev:
3243 3267 raise util.Abort(_("you can't specify a revision and a date"))
3244 3268 rev = cmdutil.finddate(ui, repo, date)
3245 3269
3246 3270 if clean or check:
3247 3271 return hg.clean(repo, rev)
3248 3272 else:
3249 3273 return hg.update(repo, rev)
3250 3274
3251 3275 def verify(ui, repo):
3252 3276 """verify the integrity of the repository
3253 3277
3254 3278 Verify the integrity of the current repository.
3255 3279
3256 3280 This will perform an extensive check of the repository's
3257 3281 integrity, validating the hashes and checksums of each entry in
3258 3282 the changelog, manifest, and tracked files, as well as the
3259 3283 integrity of their crosslinks and indices.
3260 3284 """
3261 3285 return hg.verify(repo)
3262 3286
3263 3287 def version_(ui):
3264 3288 """output version and copyright information"""
3265 3289 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3266 3290 % util.version())
3267 3291 ui.status(_(
3268 3292 "\nCopyright (C) 2005-2010 Matt Mackall <mpm@selenic.com> and others\n"
3269 3293 "This is free software; see the source for copying conditions. "
3270 3294 "There is NO\nwarranty; "
3271 3295 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3272 3296 ))
3273 3297
3274 3298 # Command options and aliases are listed here, alphabetically
3275 3299
3276 3300 globalopts = [
3277 3301 ('R', 'repository', '',
3278 3302 _('repository root directory or name of overlay bundle file')),
3279 3303 ('', 'cwd', '', _('change working directory')),
3280 3304 ('y', 'noninteractive', None,
3281 3305 _('do not prompt, assume \'yes\' for any required answers')),
3282 3306 ('q', 'quiet', None, _('suppress output')),
3283 3307 ('v', 'verbose', None, _('enable additional output')),
3284 3308 ('', 'config', [], _('set/override config option')),
3285 3309 ('', 'debug', None, _('enable debugging output')),
3286 3310 ('', 'debugger', None, _('start debugger')),
3287 3311 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3288 3312 ('', 'encodingmode', encoding.encodingmode,
3289 3313 _('set the charset encoding mode')),
3290 3314 ('', 'traceback', None, _('always print a traceback on exception')),
3291 3315 ('', 'time', None, _('time how long the command takes')),
3292 3316 ('', 'profile', None, _('print command execution profile')),
3293 3317 ('', 'version', None, _('output version information and exit')),
3294 3318 ('h', 'help', None, _('display help and exit')),
3295 3319 ]
3296 3320
3297 3321 dryrunopts = [('n', 'dry-run', None,
3298 3322 _('do not perform actions, just print output'))]
3299 3323
3300 3324 remoteopts = [
3301 3325 ('e', 'ssh', '', _('specify ssh command to use')),
3302 3326 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3303 3327 ]
3304 3328
3305 3329 walkopts = [
3306 3330 ('I', 'include', [], _('include names matching the given patterns')),
3307 3331 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3308 3332 ]
3309 3333
3310 3334 commitopts = [
3311 3335 ('m', 'message', '', _('use <text> as commit message')),
3312 3336 ('l', 'logfile', '', _('read commit message from <file>')),
3313 3337 ]
3314 3338
3315 3339 commitopts2 = [
3316 3340 ('d', 'date', '', _('record datecode as commit date')),
3317 3341 ('u', 'user', '', _('record the specified user as committer')),
3318 3342 ]
3319 3343
3320 3344 templateopts = [
3321 3345 ('', 'style', '', _('display using template map file')),
3322 3346 ('', 'template', '', _('display with template')),
3323 3347 ]
3324 3348
3325 3349 logopts = [
3326 3350 ('p', 'patch', None, _('show patch')),
3327 3351 ('g', 'git', None, _('use git extended diff format')),
3328 3352 ('l', 'limit', '', _('limit number of changes displayed')),
3329 3353 ('M', 'no-merges', None, _('do not show merges')),
3330 3354 ] + templateopts
3331 3355
3332 3356 diffopts = [
3333 3357 ('a', 'text', None, _('treat all files as text')),
3334 3358 ('g', 'git', None, _('use git extended diff format')),
3335 3359 ('', 'nodates', None, _('omit dates from diff headers'))
3336 3360 ]
3337 3361
3338 3362 diffopts2 = [
3339 3363 ('p', 'show-function', None, _('show which function each change is in')),
3340 3364 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3341 3365 ('w', 'ignore-all-space', None,
3342 3366 _('ignore white space when comparing lines')),
3343 3367 ('b', 'ignore-space-change', None,
3344 3368 _('ignore changes in the amount of white space')),
3345 3369 ('B', 'ignore-blank-lines', None,
3346 3370 _('ignore changes whose lines are all blank')),
3347 3371 ('U', 'unified', '', _('number of lines of context to show')),
3348 3372 ('', 'stat', None, _('output diffstat-style summary of changes')),
3349 3373 ]
3350 3374
3351 3375 similarityopts = [
3352 3376 ('s', 'similarity', '',
3353 3377 _('guess renamed files by similarity (0<=s<=100)'))
3354 3378 ]
3355 3379
3356 3380 table = {
3357 3381 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3358 3382 "addremove":
3359 3383 (addremove, similarityopts + walkopts + dryrunopts,
3360 3384 _('[OPTION]... [FILE]...')),
3361 3385 "^annotate|blame":
3362 3386 (annotate,
3363 3387 [('r', 'rev', '', _('annotate the specified revision')),
3364 3388 ('', 'follow', None, _('follow copies and renames (DEPRECATED)')),
3365 3389 ('', 'no-follow', None, _("don't follow copies and renames")),
3366 3390 ('a', 'text', None, _('treat all files as text')),
3367 3391 ('u', 'user', None, _('list the author (long with -v)')),
3368 3392 ('f', 'file', None, _('list the filename')),
3369 3393 ('d', 'date', None, _('list the date (short with -q)')),
3370 3394 ('n', 'number', None, _('list the revision number (default)')),
3371 3395 ('c', 'changeset', None, _('list the changeset')),
3372 3396 ('l', 'line-number', None,
3373 3397 _('show line number at the first appearance'))
3374 3398 ] + walkopts,
3375 3399 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3376 3400 "archive":
3377 3401 (archive,
3378 3402 [('', 'no-decode', None, _('do not pass files through decoders')),
3379 3403 ('p', 'prefix', '', _('directory prefix for files in archive')),
3380 3404 ('r', 'rev', '', _('revision to distribute')),
3381 3405 ('t', 'type', '', _('type of distribution to create')),
3382 3406 ] + walkopts,
3383 3407 _('[OPTION]... DEST')),
3384 3408 "backout":
3385 3409 (backout,
3386 3410 [('', 'merge', None,
3387 3411 _('merge with old dirstate parent after backout')),
3388 3412 ('', 'parent', '', _('parent to choose when backing out merge')),
3389 3413 ('r', 'rev', '', _('revision to backout')),
3390 3414 ] + walkopts + commitopts + commitopts2,
3391 3415 _('[OPTION]... [-r] REV')),
3392 3416 "bisect":
3393 3417 (bisect,
3394 3418 [('r', 'reset', False, _('reset bisect state')),
3395 3419 ('g', 'good', False, _('mark changeset good')),
3396 3420 ('b', 'bad', False, _('mark changeset bad')),
3397 3421 ('s', 'skip', False, _('skip testing changeset')),
3398 3422 ('c', 'command', '', _('use command to check changeset state')),
3399 3423 ('U', 'noupdate', False, _('do not update to target'))],
3400 3424 _("[-gbsr] [-U] [-c CMD] [REV]")),
3401 3425 "branch":
3402 3426 (branch,
3403 3427 [('f', 'force', None,
3404 3428 _('set branch name even if it shadows an existing branch')),
3405 3429 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3406 3430 _('[-fC] [NAME]')),
3407 3431 "branches":
3408 3432 (branches,
3409 3433 [('a', 'active', False,
3410 3434 _('show only branches that have unmerged heads')),
3411 3435 ('c', 'closed', False,
3412 3436 _('show normal and closed branches'))],
3413 3437 _('[-ac]')),
3414 3438 "bundle":
3415 3439 (bundle,
3416 3440 [('f', 'force', None,
3417 3441 _('run even when the destination is unrelated')),
3418 3442 ('r', 'rev', [],
3419 3443 _('a changeset intended to be added to the destination')),
3420 3444 ('b', 'branch', [],
3421 3445 _('a specific branch you would like to bundle')),
3422 3446 ('', 'base', [],
3423 3447 _('a base changeset assumed to be available at the destination')),
3424 3448 ('a', 'all', None, _('bundle all changesets in the repository')),
3425 3449 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3426 3450 ] + remoteopts,
3427 3451 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3428 3452 "cat":
3429 3453 (cat,
3430 3454 [('o', 'output', '', _('print output to file with formatted name')),
3431 3455 ('r', 'rev', '', _('print the given revision')),
3432 3456 ('', 'decode', None, _('apply any matching decode filter')),
3433 3457 ] + walkopts,
3434 3458 _('[OPTION]... FILE...')),
3435 3459 "^clone":
3436 3460 (clone,
3437 3461 [('U', 'noupdate', None,
3438 3462 _('the clone will include an empty working copy (only a repository)')),
3439 3463 ('u', 'updaterev', '',
3440 3464 _('revision, tag or branch to check out')),
3441 3465 ('r', 'rev', [],
3442 3466 _('include the specified changeset')),
3443 3467 ('b', 'branch', [],
3444 3468 _('clone only the specified branch')),
3445 3469 ('', 'pull', None, _('use pull protocol to copy metadata')),
3446 3470 ('', 'uncompressed', None,
3447 3471 _('use uncompressed transfer (fast over LAN)')),
3448 3472 ] + remoteopts,
3449 3473 _('[OPTION]... SOURCE [DEST]')),
3450 3474 "^commit|ci":
3451 3475 (commit,
3452 3476 [('A', 'addremove', None,
3453 3477 _('mark new/missing files as added/removed before committing')),
3454 3478 ('', 'close-branch', None,
3455 3479 _('mark a branch as closed, hiding it from the branch list')),
3456 3480 ] + walkopts + commitopts + commitopts2,
3457 3481 _('[OPTION]... [FILE]...')),
3458 3482 "copy|cp":
3459 3483 (copy,
3460 3484 [('A', 'after', None, _('record a copy that has already occurred')),
3461 3485 ('f', 'force', None,
3462 3486 _('forcibly copy over an existing managed file')),
3463 3487 ] + walkopts + dryrunopts,
3464 3488 _('[OPTION]... [SOURCE]... DEST')),
3465 3489 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3466 3490 "debugcheckstate": (debugcheckstate, [], ''),
3467 3491 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3468 3492 "debugcomplete":
3469 3493 (debugcomplete,
3470 3494 [('o', 'options', None, _('show the command options'))],
3471 3495 _('[-o] CMD')),
3472 3496 "debugdate":
3473 3497 (debugdate,
3474 3498 [('e', 'extended', None, _('try extended date formats'))],
3475 3499 _('[-e] DATE [RANGE]')),
3476 3500 "debugdata": (debugdata, [], _('FILE REV')),
3477 3501 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3478 3502 "debugindex": (debugindex, [], _('FILE')),
3479 3503 "debugindexdot": (debugindexdot, [], _('FILE')),
3480 3504 "debuginstall": (debuginstall, [], ''),
3481 3505 "debugrebuildstate":
3482 3506 (debugrebuildstate,
3483 3507 [('r', 'rev', '', _('revision to rebuild to'))],
3484 3508 _('[-r REV] [REV]')),
3485 3509 "debugrename":
3486 3510 (debugrename,
3487 3511 [('r', 'rev', '', _('revision to debug'))],
3488 3512 _('[-r REV] FILE')),
3489 3513 "debugsetparents":
3490 3514 (debugsetparents, [], _('REV1 [REV2]')),
3491 3515 "debugstate":
3492 3516 (debugstate,
3493 3517 [('', 'nodates', None, _('do not display the saved mtime'))],
3494 3518 _('[OPTION]...')),
3495 3519 "debugsub":
3496 3520 (debugsub,
3497 3521 [('r', 'rev', '', _('revision to check'))],
3498 3522 _('[-r REV] [REV]')),
3499 3523 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3500 3524 "^diff":
3501 3525 (diff,
3502 3526 [('r', 'rev', [], _('revision')),
3503 3527 ('c', 'change', '', _('change made by revision'))
3504 3528 ] + diffopts + diffopts2 + walkopts,
3505 3529 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3506 3530 "^export":
3507 3531 (export,
3508 3532 [('o', 'output', '', _('print output to file with formatted name')),
3509 3533 ('', 'switch-parent', None, _('diff against the second parent')),
3510 3534 ('r', 'rev', [], _('revisions to export')),
3511 3535 ] + diffopts,
3512 3536 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3513 3537 "^forget":
3514 3538 (forget,
3515 3539 [] + walkopts,
3516 3540 _('[OPTION]... FILE...')),
3517 3541 "grep":
3518 3542 (grep,
3519 3543 [('0', 'print0', None, _('end fields with NUL')),
3520 3544 ('', 'all', None, _('print all revisions that match')),
3521 3545 ('f', 'follow', None,
3522 3546 _('follow changeset history,'
3523 3547 ' or file history across copies and renames')),
3524 3548 ('i', 'ignore-case', None, _('ignore case when matching')),
3525 3549 ('l', 'files-with-matches', None,
3526 3550 _('print only filenames and revisions that match')),
3527 3551 ('n', 'line-number', None, _('print matching line numbers')),
3528 3552 ('r', 'rev', [], _('search in given revision range')),
3529 3553 ('u', 'user', None, _('list the author (long with -v)')),
3530 3554 ('d', 'date', None, _('list the date (short with -q)')),
3531 3555 ] + walkopts,
3532 3556 _('[OPTION]... PATTERN [FILE]...')),
3533 3557 "heads":
3534 3558 (heads,
3535 3559 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3536 3560 ('t', 'topo', False, _('show topological heads only')),
3537 3561 ('a', 'active', False,
3538 3562 _('show active branchheads only [DEPRECATED]')),
3539 3563 ('c', 'closed', False,
3540 3564 _('show normal and closed branch heads')),
3541 3565 ] + templateopts,
3542 3566 _('[-ac] [-r STARTREV] [REV]...')),
3543 3567 "help": (help_, [], _('[TOPIC]')),
3544 3568 "identify|id":
3545 3569 (identify,
3546 3570 [('r', 'rev', '', _('identify the specified revision')),
3547 3571 ('n', 'num', None, _('show local revision number')),
3548 3572 ('i', 'id', None, _('show global revision id')),
3549 3573 ('b', 'branch', None, _('show branch')),
3550 3574 ('t', 'tags', None, _('show tags'))],
3551 3575 _('[-nibt] [-r REV] [SOURCE]')),
3552 3576 "import|patch":
3553 3577 (import_,
3554 3578 [('p', 'strip', 1,
3555 3579 _('directory strip option for patch. This has the same '
3556 3580 'meaning as the corresponding patch option')),
3557 3581 ('b', 'base', '', _('base path')),
3558 3582 ('f', 'force', None,
3559 3583 _('skip check for outstanding uncommitted changes')),
3560 3584 ('', 'no-commit', None,
3561 3585 _("don't commit, just update the working directory")),
3562 3586 ('', 'exact', None,
3563 3587 _('apply patch to the nodes from which it was generated')),
3564 3588 ('', 'import-branch', None,
3565 3589 _('use any branch information in patch (implied by --exact)'))] +
3566 3590 commitopts + commitopts2 + similarityopts,
3567 3591 _('[OPTION]... PATCH...')),
3568 3592 "incoming|in":
3569 3593 (incoming,
3570 3594 [('f', 'force', None,
3571 3595 _('run even if remote repository is unrelated')),
3572 3596 ('n', 'newest-first', None, _('show newest record first')),
3573 3597 ('', 'bundle', '', _('file to store the bundles into')),
3574 3598 ('r', 'rev', [],
3575 3599 _('a remote changeset intended to be added')),
3576 3600 ('b', 'branch', [],
3577 3601 _('a specific branch you would like to pull')),
3578 3602 ] + logopts + remoteopts,
3579 3603 _('[-p] [-n] [-M] [-f] [-r REV]...'
3580 3604 ' [--bundle FILENAME] [SOURCE]')),
3581 3605 "^init":
3582 3606 (init,
3583 3607 remoteopts,
3584 3608 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3585 3609 "locate":
3586 3610 (locate,
3587 3611 [('r', 'rev', '', _('search the repository as it is in REV')),
3588 3612 ('0', 'print0', None,
3589 3613 _('end filenames with NUL, for use with xargs')),
3590 3614 ('f', 'fullpath', None,
3591 3615 _('print complete paths from the filesystem root')),
3592 3616 ] + walkopts,
3593 3617 _('[OPTION]... [PATTERN]...')),
3594 3618 "^log|history":
3595 3619 (log,
3596 3620 [('f', 'follow', None,
3597 3621 _('follow changeset history,'
3598 3622 ' or file history across copies and renames')),
3599 3623 ('', 'follow-first', None,
3600 3624 _('only follow the first parent of merge changesets')),
3601 3625 ('d', 'date', '', _('show revisions matching date spec')),
3602 3626 ('C', 'copies', None, _('show copied files')),
3603 3627 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3604 3628 ('r', 'rev', [], _('show the specified revision or range')),
3605 3629 ('', 'removed', None, _('include revisions where files were removed')),
3606 3630 ('m', 'only-merges', None, _('show only merges')),
3607 3631 ('u', 'user', [], _('revisions committed by user')),
3608 3632 ('b', 'only-branch', [],
3609 3633 _('show only changesets within the given named branch')),
3610 3634 ('P', 'prune', [],
3611 3635 _('do not display revision or any of its ancestors')),
3612 3636 ] + logopts + walkopts,
3613 3637 _('[OPTION]... [FILE]')),
3614 3638 "manifest":
3615 3639 (manifest,
3616 3640 [('r', 'rev', '', _('revision to display'))],
3617 3641 _('[-r REV]')),
3618 3642 "^merge":
3619 3643 (merge,
3620 3644 [('f', 'force', None, _('force a merge with outstanding changes')),
3621 3645 ('r', 'rev', '', _('revision to merge')),
3622 3646 ('P', 'preview', None,
3623 3647 _('review revisions to merge (no merge is performed)'))],
3624 3648 _('[-P] [-f] [[-r] REV]')),
3625 3649 "outgoing|out":
3626 3650 (outgoing,
3627 3651 [('f', 'force', None,
3628 3652 _('run even when the destination is unrelated')),
3629 3653 ('r', 'rev', [],
3630 3654 _('a changeset intended to be included in the destination')),
3631 3655 ('n', 'newest-first', None, _('show newest record first')),
3632 3656 ('b', 'branch', [],
3633 3657 _('a specific branch you would like to push')),
3634 3658 ] + logopts + remoteopts,
3635 3659 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3636 3660 "parents":
3637 3661 (parents,
3638 3662 [('r', 'rev', '', _('show parents of the specified revision')),
3639 3663 ] + templateopts,
3640 3664 _('[-r REV] [FILE]')),
3641 3665 "paths": (paths, [], _('[NAME]')),
3642 3666 "^pull":
3643 3667 (pull,
3644 3668 [('u', 'update', None,
3645 3669 _('update to new branch head if changesets were pulled')),
3646 3670 ('f', 'force', None,
3647 3671 _('run even when remote repository is unrelated')),
3648 3672 ('r', 'rev', [],
3649 3673 _('a remote changeset intended to be added')),
3650 3674 ('b', 'branch', [],
3651 3675 _('a specific branch you would like to pull')),
3652 3676 ] + remoteopts,
3653 3677 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3654 3678 "^push":
3655 3679 (push,
3656 3680 [('f', 'force', None, _('force push')),
3657 3681 ('r', 'rev', [],
3658 3682 _('a changeset intended to be included in the destination')),
3659 3683 ('b', 'branch', [],
3660 3684 _('a specific branch you would like to push')),
3661 3685 ] + remoteopts,
3662 3686 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3663 3687 "recover": (recover, []),
3664 3688 "^remove|rm":
3665 3689 (remove,
3666 3690 [('A', 'after', None, _('record delete for missing files')),
3667 3691 ('f', 'force', None,
3668 3692 _('remove (and delete) file even if added or modified')),
3669 3693 ] + walkopts,
3670 3694 _('[OPTION]... FILE...')),
3671 3695 "rename|mv":
3672 3696 (rename,
3673 3697 [('A', 'after', None, _('record a rename that has already occurred')),
3674 3698 ('f', 'force', None,
3675 3699 _('forcibly copy over an existing managed file')),
3676 3700 ] + walkopts + dryrunopts,
3677 3701 _('[OPTION]... SOURCE... DEST')),
3678 3702 "resolve":
3679 3703 (resolve,
3680 3704 [('a', 'all', None, _('select all unresolved files')),
3681 3705 ('l', 'list', None, _('list state of files needing merge')),
3682 3706 ('m', 'mark', None, _('mark files as resolved')),
3683 3707 ('u', 'unmark', None, _('unmark files as resolved')),
3684 3708 ('n', 'no-status', None, _('hide status prefix'))]
3685 3709 + walkopts,
3686 3710 _('[OPTION]... [FILE]...')),
3687 3711 "revert":
3688 3712 (revert,
3689 3713 [('a', 'all', None, _('revert all changes when no arguments given')),
3690 3714 ('d', 'date', '', _('tipmost revision matching date')),
3691 3715 ('r', 'rev', '', _('revert to the specified revision')),
3692 3716 ('', 'no-backup', None, _('do not save backup copies of files')),
3693 3717 ] + walkopts + dryrunopts,
3694 3718 _('[OPTION]... [-r REV] [NAME]...')),
3695 3719 "rollback": (rollback, []),
3696 3720 "root": (root, []),
3697 3721 "^serve":
3698 3722 (serve,
3699 3723 [('A', 'accesslog', '', _('name of access log file to write to')),
3700 3724 ('d', 'daemon', None, _('run server in background')),
3701 3725 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3702 3726 ('E', 'errorlog', '', _('name of error log file to write to')),
3703 3727 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3704 3728 ('a', 'address', '',
3705 3729 _('address to listen on (default: all interfaces)')),
3706 3730 ('', 'prefix', '',
3707 3731 _('prefix path to serve from (default: server root)')),
3708 3732 ('n', 'name', '',
3709 3733 _('name to show in web pages (default: working directory)')),
3710 3734 ('', 'webdir-conf', '', _('name of the webdir config file'
3711 3735 ' (serve more than one repository)')),
3712 3736 ('', 'pid-file', '', _('name of file to write process ID to')),
3713 3737 ('', 'stdio', None, _('for remote clients')),
3714 3738 ('t', 'templates', '', _('web templates to use')),
3715 3739 ('', 'style', '', _('template style to use')),
3716 3740 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3717 3741 ('', 'certificate', '', _('SSL certificate file'))],
3718 3742 _('[OPTION]...')),
3719 3743 "showconfig|debugconfig":
3720 3744 (showconfig,
3721 3745 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3722 3746 _('[-u] [NAME]...')),
3723 3747 "^summary|sum":
3724 3748 (summary,
3725 3749 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3726 3750 "^status|st":
3727 3751 (status,
3728 3752 [('A', 'all', None, _('show status of all files')),
3729 3753 ('m', 'modified', None, _('show only modified files')),
3730 3754 ('a', 'added', None, _('show only added files')),
3731 3755 ('r', 'removed', None, _('show only removed files')),
3732 3756 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3733 3757 ('c', 'clean', None, _('show only files without changes')),
3734 3758 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3735 3759 ('i', 'ignored', None, _('show only ignored files')),
3736 3760 ('n', 'no-status', None, _('hide status prefix')),
3737 3761 ('C', 'copies', None, _('show source of copied files')),
3738 3762 ('0', 'print0', None,
3739 3763 _('end filenames with NUL, for use with xargs')),
3740 3764 ('', 'rev', [], _('show difference from revision')),
3741 3765 ('', 'change', '', _('list the changed files of a revision')),
3742 3766 ] + walkopts,
3743 3767 _('[OPTION]... [FILE]...')),
3744 3768 "tag":
3745 3769 (tag,
3746 3770 [('f', 'force', None, _('replace existing tag')),
3747 3771 ('l', 'local', None, _('make the tag local')),
3748 3772 ('r', 'rev', '', _('revision to tag')),
3749 3773 ('', 'remove', None, _('remove a tag')),
3750 3774 # -l/--local is already there, commitopts cannot be used
3751 3775 ('m', 'message', '', _('use <text> as commit message')),
3752 3776 ] + commitopts2,
3753 3777 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3754 3778 "tags": (tags, [], ''),
3755 3779 "tip":
3756 3780 (tip,
3757 3781 [('p', 'patch', None, _('show patch')),
3758 3782 ('g', 'git', None, _('use git extended diff format')),
3759 3783 ] + templateopts,
3760 3784 _('[-p] [-g]')),
3761 3785 "unbundle":
3762 3786 (unbundle,
3763 3787 [('u', 'update', None,
3764 3788 _('update to new branch head if changesets were unbundled'))],
3765 3789 _('[-u] FILE...')),
3766 3790 "^update|up|checkout|co":
3767 3791 (update,
3768 3792 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
3769 3793 ('c', 'check', None, _('check for uncommitted changes')),
3770 3794 ('d', 'date', '', _('tipmost revision matching date')),
3771 3795 ('r', 'rev', '', _('revision'))],
3772 3796 _('[-c] [-C] [-d DATE] [[-r] REV]')),
3773 3797 "verify": (verify, []),
3774 3798 "version": (version_, []),
3775 3799 }
3776 3800
3777 3801 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3778 3802 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3779 3803 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1534 +1,1658 b''
1 1 # patch.py - patch file parsing routines
2 2 #
3 3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from i18n import _
10 10 from node import hex, nullid, short
11 11 import base85, cmdutil, mdiff, util, diffhelpers, copies
12 12 import cStringIO, email.Parser, os, re
13 13 import sys, tempfile, zlib
14 14
15 15 gitre = re.compile('diff --git a/(.*) b/(.*)')
16 16
17 17 class PatchError(Exception):
18 18 pass
19 19
20 20 class NoHunks(PatchError):
21 21 pass
22 22
23 23 # helper functions
24 24
25 25 def copyfile(src, dst, basedir):
26 26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 27 if os.path.exists(absdst):
28 28 raise util.Abort(_("cannot create %s: destination already exists") %
29 29 dst)
30 30
31 31 dstdir = os.path.dirname(absdst)
32 32 if dstdir and not os.path.isdir(dstdir):
33 33 try:
34 34 os.makedirs(dstdir)
35 35 except IOError:
36 36 raise util.Abort(
37 37 _("cannot create %s: unable to create destination directory")
38 38 % dst)
39 39
40 40 util.copyfile(abssrc, absdst)
41 41
42 42 # public functions
43 43
44 def split(stream):
45 '''return an iterator of individual patches from a stream'''
46 def isheader(line, inheader):
47 if inheader and line[0] in (' ', '\t'):
48 # continuation
49 return True
50 l = line.split(': ', 1)
51 return len(l) == 2 and ' ' not in l[0]
52
53 def chunk(lines):
54 return cStringIO.StringIO(''.join(lines))
55
56 def hgsplit(stream, cur):
57 inheader = True
58
59 for line in stream:
60 if not line.strip():
61 inheader = False
62 if not inheader and line.startswith('# HG changeset patch'):
63 yield chunk(cur)
64 cur = []
65 inheader = True
66
67 cur.append(line)
68
69 if cur:
70 yield chunk(cur)
71
72 def mboxsplit(stream, cur):
73 for line in stream:
74 if line.startswith('From '):
75 for c in split(chunk(cur[1:])):
76 yield c
77 cur = []
78
79 cur.append(line)
80
81 if cur:
82 for c in split(chunk(cur[1:])):
83 yield c
84
85 def mimesplit(stream, cur):
86 def msgfp(m):
87 fp = cStringIO.StringIO()
88 g = email.Generator.Generator(fp, mangle_from_=False)
89 g.flatten(m)
90 fp.seek(0)
91 return fp
92
93 for line in stream:
94 cur.append(line)
95 c = chunk(cur)
96
97 m = email.Parser.Parser().parse(c)
98 if not m.is_multipart():
99 yield msgfp(m)
100 else:
101 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
102 for part in m.walk():
103 ct = part.get_content_type()
104 if ct not in ok_types:
105 continue
106 yield msgfp(part)
107
108 def headersplit(stream, cur):
109 inheader = False
110
111 for line in stream:
112 if not inheader and isheader(line, inheader):
113 yield chunk(cur)
114 cur = []
115 inheader = True
116 if inheader and not isheader(line, inheader):
117 inheader = False
118
119 cur.append(line)
120
121 if cur:
122 yield chunk(cur)
123
124 def remainder(cur):
125 yield chunk(cur)
126
127 class fiter(object):
128 def __init__(self, fp):
129 self.fp = fp
130
131 def __iter__(self):
132 return self
133
134 def next(self):
135 l = self.fp.readline()
136 if not l:
137 raise StopIteration
138 return l
139
140 inheader = False
141 cur = []
142
143 mimeheaders = ['content-type']
144
145 if not hasattr(stream, 'next'):
146 # http responses, for example, have readline but not next
147 stream = fiter(stream)
148
149 for line in stream:
150 cur.append(line)
151 if line.startswith('# HG changeset patch'):
152 return hgsplit(stream, cur)
153 elif line.startswith('From '):
154 return mboxsplit(stream, cur)
155 elif isheader(line, inheader):
156 inheader = True
157 if line.split(':', 1)[0].lower() in mimeheaders:
158 # let email parser handle this
159 return mimesplit(stream, cur)
160 elif inheader:
161 # No evil headers seen, split by hand
162 return headersplit(stream, cur)
163 # Not enough info, keep reading
164
165 # if we are here, we have a very plain patch
166 return remainder(cur)
167
44 168 def extract(ui, fileobj):
45 169 '''extract patch from data read from fileobj.
46 170
47 171 patch can be a normal patch or contained in an email message.
48 172
49 173 return tuple (filename, message, user, date, node, p1, p2).
50 174 Any item in the returned tuple can be None. If filename is None,
51 175 fileobj did not contain a patch. Caller must unlink filename when done.'''
52 176
53 177 # attempt to detect the start of a patch
54 178 # (this heuristic is borrowed from quilt)
55 179 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
56 180 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
57 181 r'(---|\*\*\*)[ \t])', re.MULTILINE)
58 182
59 183 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
60 184 tmpfp = os.fdopen(fd, 'w')
61 185 try:
62 186 msg = email.Parser.Parser().parse(fileobj)
63 187
64 188 subject = msg['Subject']
65 189 user = msg['From']
66 190 if not subject and not user:
67 191 # Not an email, restore parsed headers if any
68 192 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
69 193
70 194 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
71 195 # should try to parse msg['Date']
72 196 date = None
73 197 nodeid = None
74 198 branch = None
75 199 parents = []
76 200
77 201 if subject:
78 202 if subject.startswith('[PATCH'):
79 203 pend = subject.find(']')
80 204 if pend >= 0:
81 205 subject = subject[pend + 1:].lstrip()
82 206 subject = subject.replace('\n\t', ' ')
83 207 ui.debug('Subject: %s\n' % subject)
84 208 if user:
85 209 ui.debug('From: %s\n' % user)
86 210 diffs_seen = 0
87 211 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
88 212 message = ''
89 213 for part in msg.walk():
90 214 content_type = part.get_content_type()
91 215 ui.debug('Content-Type: %s\n' % content_type)
92 216 if content_type not in ok_types:
93 217 continue
94 218 payload = part.get_payload(decode=True)
95 219 m = diffre.search(payload)
96 220 if m:
97 221 hgpatch = False
98 222 ignoretext = False
99 223
100 224 ui.debug('found patch at byte %d\n' % m.start(0))
101 225 diffs_seen += 1
102 226 cfp = cStringIO.StringIO()
103 227 for line in payload[:m.start(0)].splitlines():
104 228 if line.startswith('# HG changeset patch'):
105 229 ui.debug('patch generated by hg export\n')
106 230 hgpatch = True
107 231 # drop earlier commit message content
108 232 cfp.seek(0)
109 233 cfp.truncate()
110 234 subject = None
111 235 elif hgpatch:
112 236 if line.startswith('# User '):
113 237 user = line[7:]
114 238 ui.debug('From: %s\n' % user)
115 239 elif line.startswith("# Date "):
116 240 date = line[7:]
117 241 elif line.startswith("# Branch "):
118 242 branch = line[9:]
119 243 elif line.startswith("# Node ID "):
120 244 nodeid = line[10:]
121 245 elif line.startswith("# Parent "):
122 246 parents.append(line[10:])
123 247 elif line == '---' and gitsendmail:
124 248 ignoretext = True
125 249 if not line.startswith('# ') and not ignoretext:
126 250 cfp.write(line)
127 251 cfp.write('\n')
128 252 message = cfp.getvalue()
129 253 if tmpfp:
130 254 tmpfp.write(payload)
131 255 if not payload.endswith('\n'):
132 256 tmpfp.write('\n')
133 257 elif not diffs_seen and message and content_type == 'text/plain':
134 258 message += '\n' + payload
135 259 except:
136 260 tmpfp.close()
137 261 os.unlink(tmpname)
138 262 raise
139 263
140 264 if subject and not message.startswith(subject):
141 265 message = '%s\n%s' % (subject, message)
142 266 tmpfp.close()
143 267 if not diffs_seen:
144 268 os.unlink(tmpname)
145 269 return None, message, user, date, branch, None, None, None
146 270 p1 = parents and parents.pop(0) or None
147 271 p2 = parents and parents.pop(0) or None
148 272 return tmpname, message, user, date, branch, nodeid, p1, p2
149 273
150 274 GP_PATCH = 1 << 0 # we have to run patch
151 275 GP_FILTER = 1 << 1 # there's some copy/rename operation
152 276 GP_BINARY = 1 << 2 # there's a binary patch
153 277
154 278 class patchmeta(object):
155 279 """Patched file metadata
156 280
157 281 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
158 282 or COPY. 'path' is patched file path. 'oldpath' is set to the
159 283 origin file when 'op' is either COPY or RENAME, None otherwise. If
160 284 file mode is changed, 'mode' is a tuple (islink, isexec) where
161 285 'islink' is True if the file is a symlink and 'isexec' is True if
162 286 the file is executable. Otherwise, 'mode' is None.
163 287 """
164 288 def __init__(self, path):
165 289 self.path = path
166 290 self.oldpath = None
167 291 self.mode = None
168 292 self.op = 'MODIFY'
169 293 self.lineno = 0
170 294 self.binary = False
171 295
172 296 def setmode(self, mode):
173 297 islink = mode & 020000
174 298 isexec = mode & 0100
175 299 self.mode = (islink, isexec)
176 300
177 301 def readgitpatch(lr):
178 302 """extract git-style metadata about patches from <patchname>"""
179 303
180 304 # Filter patch for git information
181 305 gp = None
182 306 gitpatches = []
183 307 # Can have a git patch with only metadata, causing patch to complain
184 308 dopatch = 0
185 309
186 310 lineno = 0
187 311 for line in lr:
188 312 lineno += 1
189 313 line = line.rstrip(' \r\n')
190 314 if line.startswith('diff --git'):
191 315 m = gitre.match(line)
192 316 if m:
193 317 if gp:
194 318 gitpatches.append(gp)
195 319 dst = m.group(2)
196 320 gp = patchmeta(dst)
197 321 gp.lineno = lineno
198 322 elif gp:
199 323 if line.startswith('--- '):
200 324 if gp.op in ('COPY', 'RENAME'):
201 325 dopatch |= GP_FILTER
202 326 gitpatches.append(gp)
203 327 gp = None
204 328 dopatch |= GP_PATCH
205 329 continue
206 330 if line.startswith('rename from '):
207 331 gp.op = 'RENAME'
208 332 gp.oldpath = line[12:]
209 333 elif line.startswith('rename to '):
210 334 gp.path = line[10:]
211 335 elif line.startswith('copy from '):
212 336 gp.op = 'COPY'
213 337 gp.oldpath = line[10:]
214 338 elif line.startswith('copy to '):
215 339 gp.path = line[8:]
216 340 elif line.startswith('deleted file'):
217 341 gp.op = 'DELETE'
218 342 # is the deleted file a symlink?
219 343 gp.setmode(int(line[-6:], 8))
220 344 elif line.startswith('new file mode '):
221 345 gp.op = 'ADD'
222 346 gp.setmode(int(line[-6:], 8))
223 347 elif line.startswith('new mode '):
224 348 gp.setmode(int(line[-6:], 8))
225 349 elif line.startswith('GIT binary patch'):
226 350 dopatch |= GP_BINARY
227 351 gp.binary = True
228 352 if gp:
229 353 gitpatches.append(gp)
230 354
231 355 if not gitpatches:
232 356 dopatch = GP_PATCH
233 357
234 358 return (dopatch, gitpatches)
235 359
236 360 class linereader(object):
237 361 # simple class to allow pushing lines back into the input stream
238 362 def __init__(self, fp, textmode=False):
239 363 self.fp = fp
240 364 self.buf = []
241 365 self.textmode = textmode
242 366 self.eol = None
243 367
244 368 def push(self, line):
245 369 if line is not None:
246 370 self.buf.append(line)
247 371
248 372 def readline(self):
249 373 if self.buf:
250 374 l = self.buf[0]
251 375 del self.buf[0]
252 376 return l
253 377 l = self.fp.readline()
254 378 if not self.eol:
255 379 if l.endswith('\r\n'):
256 380 self.eol = '\r\n'
257 381 elif l.endswith('\n'):
258 382 self.eol = '\n'
259 383 if self.textmode and l.endswith('\r\n'):
260 384 l = l[:-2] + '\n'
261 385 return l
262 386
263 387 def __iter__(self):
264 388 while 1:
265 389 l = self.readline()
266 390 if not l:
267 391 break
268 392 yield l
269 393
270 394 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
271 395 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
272 396 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
273 397 eolmodes = ['strict', 'crlf', 'lf', 'auto']
274 398
275 399 class patchfile(object):
276 400 def __init__(self, ui, fname, opener, missing=False, eolmode='strict'):
277 401 self.fname = fname
278 402 self.eolmode = eolmode
279 403 self.eol = None
280 404 self.opener = opener
281 405 self.ui = ui
282 406 self.lines = []
283 407 self.exists = False
284 408 self.missing = missing
285 409 if not missing:
286 410 try:
287 411 self.lines = self.readlines(fname)
288 412 self.exists = True
289 413 except IOError:
290 414 pass
291 415 else:
292 416 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
293 417
294 418 self.hash = {}
295 419 self.dirty = 0
296 420 self.offset = 0
297 421 self.skew = 0
298 422 self.rej = []
299 423 self.fileprinted = False
300 424 self.printfile(False)
301 425 self.hunks = 0
302 426
303 427 def readlines(self, fname):
304 428 if os.path.islink(fname):
305 429 return [os.readlink(fname)]
306 430 fp = self.opener(fname, 'r')
307 431 try:
308 432 lr = linereader(fp, self.eolmode != 'strict')
309 433 lines = list(lr)
310 434 self.eol = lr.eol
311 435 return lines
312 436 finally:
313 437 fp.close()
314 438
315 439 def writelines(self, fname, lines):
316 440 # Ensure supplied data ends in fname, being a regular file or
317 441 # a symlink. updatedir() will -too magically- take care of
318 442 # setting it to the proper type afterwards.
319 443 islink = os.path.islink(fname)
320 444 if islink:
321 445 fp = cStringIO.StringIO()
322 446 else:
323 447 fp = self.opener(fname, 'w')
324 448 try:
325 449 if self.eolmode == 'auto':
326 450 eol = self.eol
327 451 elif self.eolmode == 'crlf':
328 452 eol = '\r\n'
329 453 else:
330 454 eol = '\n'
331 455
332 456 if self.eolmode != 'strict' and eol and eol != '\n':
333 457 for l in lines:
334 458 if l and l[-1] == '\n':
335 459 l = l[:-1] + eol
336 460 fp.write(l)
337 461 else:
338 462 fp.writelines(lines)
339 463 if islink:
340 464 self.opener.symlink(fp.getvalue(), fname)
341 465 finally:
342 466 fp.close()
343 467
344 468 def unlink(self, fname):
345 469 os.unlink(fname)
346 470
347 471 def printfile(self, warn):
348 472 if self.fileprinted:
349 473 return
350 474 if warn or self.ui.verbose:
351 475 self.fileprinted = True
352 476 s = _("patching file %s\n") % self.fname
353 477 if warn:
354 478 self.ui.warn(s)
355 479 else:
356 480 self.ui.note(s)
357 481
358 482
359 483 def findlines(self, l, linenum):
360 484 # looks through the hash and finds candidate lines. The
361 485 # result is a list of line numbers sorted based on distance
362 486 # from linenum
363 487
364 488 cand = self.hash.get(l, [])
365 489 if len(cand) > 1:
366 490 # resort our list of potentials forward then back.
367 491 cand.sort(key=lambda x: abs(x - linenum))
368 492 return cand
369 493
370 494 def hashlines(self):
371 495 self.hash = {}
372 496 for x, s in enumerate(self.lines):
373 497 self.hash.setdefault(s, []).append(x)
374 498
375 499 def write_rej(self):
376 500 # our rejects are a little different from patch(1). This always
377 501 # creates rejects in the same form as the original patch. A file
378 502 # header is inserted so that you can run the reject through patch again
379 503 # without having to type the filename.
380 504
381 505 if not self.rej:
382 506 return
383 507
384 508 fname = self.fname + ".rej"
385 509 self.ui.warn(
386 510 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
387 511 (len(self.rej), self.hunks, fname))
388 512
389 513 def rejlines():
390 514 base = os.path.basename(self.fname)
391 515 yield "--- %s\n+++ %s\n" % (base, base)
392 516 for x in self.rej:
393 517 for l in x.hunk:
394 518 yield l
395 519 if l[-1] != '\n':
396 520 yield "\n\ No newline at end of file\n"
397 521
398 522 self.writelines(fname, rejlines())
399 523
400 524 def write(self, dest=None):
401 525 if not self.dirty:
402 526 return
403 527 if not dest:
404 528 dest = self.fname
405 529 self.writelines(dest, self.lines)
406 530
407 531 def close(self):
408 532 self.write()
409 533 self.write_rej()
410 534
411 535 def apply(self, h):
412 536 if not h.complete():
413 537 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
414 538 (h.number, h.desc, len(h.a), h.lena, len(h.b),
415 539 h.lenb))
416 540
417 541 self.hunks += 1
418 542
419 543 if self.missing:
420 544 self.rej.append(h)
421 545 return -1
422 546
423 547 if self.exists and h.createfile():
424 548 self.ui.warn(_("file %s already exists\n") % self.fname)
425 549 self.rej.append(h)
426 550 return -1
427 551
428 552 if isinstance(h, binhunk):
429 553 if h.rmfile():
430 554 self.unlink(self.fname)
431 555 else:
432 556 self.lines[:] = h.new()
433 557 self.offset += len(h.new())
434 558 self.dirty = 1
435 559 return 0
436 560
437 561 horig = h
438 562 if (self.eolmode in ('crlf', 'lf')
439 563 or self.eolmode == 'auto' and self.eol):
440 564 # If new eols are going to be normalized, then normalize
441 565 # hunk data before patching. Otherwise, preserve input
442 566 # line-endings.
443 567 h = h.getnormalized()
444 568
445 569 # fast case first, no offsets, no fuzz
446 570 old = h.old()
447 571 # patch starts counting at 1 unless we are adding the file
448 572 if h.starta == 0:
449 573 start = 0
450 574 else:
451 575 start = h.starta + self.offset - 1
452 576 orig_start = start
453 577 # if there's skew we want to emit the "(offset %d lines)" even
454 578 # when the hunk cleanly applies at start + skew, so skip the
455 579 # fast case code
456 580 if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0:
457 581 if h.rmfile():
458 582 self.unlink(self.fname)
459 583 else:
460 584 self.lines[start : start + h.lena] = h.new()
461 585 self.offset += h.lenb - h.lena
462 586 self.dirty = 1
463 587 return 0
464 588
465 589 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
466 590 self.hashlines()
467 591 if h.hunk[-1][0] != ' ':
468 592 # if the hunk tried to put something at the bottom of the file
469 593 # override the start line and use eof here
470 594 search_start = len(self.lines)
471 595 else:
472 596 search_start = orig_start + self.skew
473 597
474 598 for fuzzlen in xrange(3):
475 599 for toponly in [True, False]:
476 600 old = h.old(fuzzlen, toponly)
477 601
478 602 cand = self.findlines(old[0][1:], search_start)
479 603 for l in cand:
480 604 if diffhelpers.testhunk(old, self.lines, l) == 0:
481 605 newlines = h.new(fuzzlen, toponly)
482 606 self.lines[l : l + len(old)] = newlines
483 607 self.offset += len(newlines) - len(old)
484 608 self.skew = l - orig_start
485 609 self.dirty = 1
486 610 if fuzzlen:
487 611 fuzzstr = "with fuzz %d " % fuzzlen
488 612 f = self.ui.warn
489 613 self.printfile(True)
490 614 else:
491 615 fuzzstr = ""
492 616 f = self.ui.note
493 617 offset = l - orig_start - fuzzlen
494 618 if offset == 1:
495 619 msg = _("Hunk #%d succeeded at %d %s"
496 620 "(offset %d line).\n")
497 621 else:
498 622 msg = _("Hunk #%d succeeded at %d %s"
499 623 "(offset %d lines).\n")
500 624 f(msg % (h.number, l + 1, fuzzstr, offset))
501 625 return fuzzlen
502 626 self.printfile(True)
503 627 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
504 628 self.rej.append(horig)
505 629 return -1
506 630
507 631 class hunk(object):
508 632 def __init__(self, desc, num, lr, context, create=False, remove=False):
509 633 self.number = num
510 634 self.desc = desc
511 635 self.hunk = [desc]
512 636 self.a = []
513 637 self.b = []
514 638 self.starta = self.lena = None
515 639 self.startb = self.lenb = None
516 640 if lr is not None:
517 641 if context:
518 642 self.read_context_hunk(lr)
519 643 else:
520 644 self.read_unified_hunk(lr)
521 645 self.create = create
522 646 self.remove = remove and not create
523 647
524 648 def getnormalized(self):
525 649 """Return a copy with line endings normalized to LF."""
526 650
527 651 def normalize(lines):
528 652 nlines = []
529 653 for line in lines:
530 654 if line.endswith('\r\n'):
531 655 line = line[:-2] + '\n'
532 656 nlines.append(line)
533 657 return nlines
534 658
535 659 # Dummy object, it is rebuilt manually
536 660 nh = hunk(self.desc, self.number, None, None, False, False)
537 661 nh.number = self.number
538 662 nh.desc = self.desc
539 663 nh.a = normalize(self.a)
540 664 nh.b = normalize(self.b)
541 665 nh.starta = self.starta
542 666 nh.startb = self.startb
543 667 nh.lena = self.lena
544 668 nh.lenb = self.lenb
545 669 nh.create = self.create
546 670 nh.remove = self.remove
547 671 return nh
548 672
549 673 def read_unified_hunk(self, lr):
550 674 m = unidesc.match(self.desc)
551 675 if not m:
552 676 raise PatchError(_("bad hunk #%d") % self.number)
553 677 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
554 678 if self.lena is None:
555 679 self.lena = 1
556 680 else:
557 681 self.lena = int(self.lena)
558 682 if self.lenb is None:
559 683 self.lenb = 1
560 684 else:
561 685 self.lenb = int(self.lenb)
562 686 self.starta = int(self.starta)
563 687 self.startb = int(self.startb)
564 688 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
565 689 # if we hit eof before finishing out the hunk, the last line will
566 690 # be zero length. Lets try to fix it up.
567 691 while len(self.hunk[-1]) == 0:
568 692 del self.hunk[-1]
569 693 del self.a[-1]
570 694 del self.b[-1]
571 695 self.lena -= 1
572 696 self.lenb -= 1
573 697
574 698 def read_context_hunk(self, lr):
575 699 self.desc = lr.readline()
576 700 m = contextdesc.match(self.desc)
577 701 if not m:
578 702 raise PatchError(_("bad hunk #%d") % self.number)
579 703 foo, self.starta, foo2, aend, foo3 = m.groups()
580 704 self.starta = int(self.starta)
581 705 if aend is None:
582 706 aend = self.starta
583 707 self.lena = int(aend) - self.starta
584 708 if self.starta:
585 709 self.lena += 1
586 710 for x in xrange(self.lena):
587 711 l = lr.readline()
588 712 if l.startswith('---'):
589 713 lr.push(l)
590 714 break
591 715 s = l[2:]
592 716 if l.startswith('- ') or l.startswith('! '):
593 717 u = '-' + s
594 718 elif l.startswith(' '):
595 719 u = ' ' + s
596 720 else:
597 721 raise PatchError(_("bad hunk #%d old text line %d") %
598 722 (self.number, x))
599 723 self.a.append(u)
600 724 self.hunk.append(u)
601 725
602 726 l = lr.readline()
603 727 if l.startswith('\ '):
604 728 s = self.a[-1][:-1]
605 729 self.a[-1] = s
606 730 self.hunk[-1] = s
607 731 l = lr.readline()
608 732 m = contextdesc.match(l)
609 733 if not m:
610 734 raise PatchError(_("bad hunk #%d") % self.number)
611 735 foo, self.startb, foo2, bend, foo3 = m.groups()
612 736 self.startb = int(self.startb)
613 737 if bend is None:
614 738 bend = self.startb
615 739 self.lenb = int(bend) - self.startb
616 740 if self.startb:
617 741 self.lenb += 1
618 742 hunki = 1
619 743 for x in xrange(self.lenb):
620 744 l = lr.readline()
621 745 if l.startswith('\ '):
622 746 s = self.b[-1][:-1]
623 747 self.b[-1] = s
624 748 self.hunk[hunki - 1] = s
625 749 continue
626 750 if not l:
627 751 lr.push(l)
628 752 break
629 753 s = l[2:]
630 754 if l.startswith('+ ') or l.startswith('! '):
631 755 u = '+' + s
632 756 elif l.startswith(' '):
633 757 u = ' ' + s
634 758 elif len(self.b) == 0:
635 759 # this can happen when the hunk does not add any lines
636 760 lr.push(l)
637 761 break
638 762 else:
639 763 raise PatchError(_("bad hunk #%d old text line %d") %
640 764 (self.number, x))
641 765 self.b.append(s)
642 766 while True:
643 767 if hunki >= len(self.hunk):
644 768 h = ""
645 769 else:
646 770 h = self.hunk[hunki]
647 771 hunki += 1
648 772 if h == u:
649 773 break
650 774 elif h.startswith('-'):
651 775 continue
652 776 else:
653 777 self.hunk.insert(hunki - 1, u)
654 778 break
655 779
656 780 if not self.a:
657 781 # this happens when lines were only added to the hunk
658 782 for x in self.hunk:
659 783 if x.startswith('-') or x.startswith(' '):
660 784 self.a.append(x)
661 785 if not self.b:
662 786 # this happens when lines were only deleted from the hunk
663 787 for x in self.hunk:
664 788 if x.startswith('+') or x.startswith(' '):
665 789 self.b.append(x[1:])
666 790 # @@ -start,len +start,len @@
667 791 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
668 792 self.startb, self.lenb)
669 793 self.hunk[0] = self.desc
670 794
671 795 def fix_newline(self):
672 796 diffhelpers.fix_newline(self.hunk, self.a, self.b)
673 797
674 798 def complete(self):
675 799 return len(self.a) == self.lena and len(self.b) == self.lenb
676 800
677 801 def createfile(self):
678 802 return self.starta == 0 and self.lena == 0 and self.create
679 803
680 804 def rmfile(self):
681 805 return self.startb == 0 and self.lenb == 0 and self.remove
682 806
683 807 def fuzzit(self, l, fuzz, toponly):
684 808 # this removes context lines from the top and bottom of list 'l'. It
685 809 # checks the hunk to make sure only context lines are removed, and then
686 810 # returns a new shortened list of lines.
687 811 fuzz = min(fuzz, len(l)-1)
688 812 if fuzz:
689 813 top = 0
690 814 bot = 0
691 815 hlen = len(self.hunk)
692 816 for x in xrange(hlen - 1):
693 817 # the hunk starts with the @@ line, so use x+1
694 818 if self.hunk[x + 1][0] == ' ':
695 819 top += 1
696 820 else:
697 821 break
698 822 if not toponly:
699 823 for x in xrange(hlen - 1):
700 824 if self.hunk[hlen - bot - 1][0] == ' ':
701 825 bot += 1
702 826 else:
703 827 break
704 828
705 829 # top and bot now count context in the hunk
706 830 # adjust them if either one is short
707 831 context = max(top, bot, 3)
708 832 if bot < context:
709 833 bot = max(0, fuzz - (context - bot))
710 834 else:
711 835 bot = min(fuzz, bot)
712 836 if top < context:
713 837 top = max(0, fuzz - (context - top))
714 838 else:
715 839 top = min(fuzz, top)
716 840
717 841 return l[top:len(l)-bot]
718 842 return l
719 843
720 844 def old(self, fuzz=0, toponly=False):
721 845 return self.fuzzit(self.a, fuzz, toponly)
722 846
723 847 def new(self, fuzz=0, toponly=False):
724 848 return self.fuzzit(self.b, fuzz, toponly)
725 849
726 850 class binhunk:
727 851 'A binary patch file. Only understands literals so far.'
728 852 def __init__(self, gitpatch):
729 853 self.gitpatch = gitpatch
730 854 self.text = None
731 855 self.hunk = ['GIT binary patch\n']
732 856
733 857 def createfile(self):
734 858 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
735 859
736 860 def rmfile(self):
737 861 return self.gitpatch.op == 'DELETE'
738 862
739 863 def complete(self):
740 864 return self.text is not None
741 865
742 866 def new(self):
743 867 return [self.text]
744 868
745 869 def extract(self, lr):
746 870 line = lr.readline()
747 871 self.hunk.append(line)
748 872 while line and not line.startswith('literal '):
749 873 line = lr.readline()
750 874 self.hunk.append(line)
751 875 if not line:
752 876 raise PatchError(_('could not extract binary patch'))
753 877 size = int(line[8:].rstrip())
754 878 dec = []
755 879 line = lr.readline()
756 880 self.hunk.append(line)
757 881 while len(line) > 1:
758 882 l = line[0]
759 883 if l <= 'Z' and l >= 'A':
760 884 l = ord(l) - ord('A') + 1
761 885 else:
762 886 l = ord(l) - ord('a') + 27
763 887 dec.append(base85.b85decode(line[1:-1])[:l])
764 888 line = lr.readline()
765 889 self.hunk.append(line)
766 890 text = zlib.decompress(''.join(dec))
767 891 if len(text) != size:
768 892 raise PatchError(_('binary patch is %d bytes, not %d') %
769 893 len(text), size)
770 894 self.text = text
771 895
772 896 def parsefilename(str):
773 897 # --- filename \t|space stuff
774 898 s = str[4:].rstrip('\r\n')
775 899 i = s.find('\t')
776 900 if i < 0:
777 901 i = s.find(' ')
778 902 if i < 0:
779 903 return s
780 904 return s[:i]
781 905
782 906 def selectfile(afile_orig, bfile_orig, hunk, strip):
783 907 def pathstrip(path, count=1):
784 908 pathlen = len(path)
785 909 i = 0
786 910 if count == 0:
787 911 return '', path.rstrip()
788 912 while count > 0:
789 913 i = path.find('/', i)
790 914 if i == -1:
791 915 raise PatchError(_("unable to strip away %d dirs from %s") %
792 916 (count, path))
793 917 i += 1
794 918 # consume '//' in the path
795 919 while i < pathlen - 1 and path[i] == '/':
796 920 i += 1
797 921 count -= 1
798 922 return path[:i].lstrip(), path[i:].rstrip()
799 923
800 924 nulla = afile_orig == "/dev/null"
801 925 nullb = bfile_orig == "/dev/null"
802 926 abase, afile = pathstrip(afile_orig, strip)
803 927 gooda = not nulla and util.lexists(afile)
804 928 bbase, bfile = pathstrip(bfile_orig, strip)
805 929 if afile == bfile:
806 930 goodb = gooda
807 931 else:
808 932 goodb = not nullb and os.path.exists(bfile)
809 933 createfunc = hunk.createfile
810 934 missing = not goodb and not gooda and not createfunc()
811 935
812 936 # some diff programs apparently produce create patches where the
813 937 # afile is not /dev/null, but rather the same name as the bfile
814 938 if missing and afile == bfile:
815 939 # this isn't very pretty
816 940 hunk.create = True
817 941 if createfunc():
818 942 missing = False
819 943 else:
820 944 hunk.create = False
821 945
822 946 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
823 947 # diff is between a file and its backup. In this case, the original
824 948 # file should be patched (see original mpatch code).
825 949 isbackup = (abase == bbase and bfile.startswith(afile))
826 950 fname = None
827 951 if not missing:
828 952 if gooda and goodb:
829 953 fname = isbackup and afile or bfile
830 954 elif gooda:
831 955 fname = afile
832 956
833 957 if not fname:
834 958 if not nullb:
835 959 fname = isbackup and afile or bfile
836 960 elif not nulla:
837 961 fname = afile
838 962 else:
839 963 raise PatchError(_("undefined source and destination files"))
840 964
841 965 return fname, missing
842 966
843 967 def scangitpatch(lr, firstline):
844 968 """
845 969 Git patches can emit:
846 970 - rename a to b
847 971 - change b
848 972 - copy a to c
849 973 - change c
850 974
851 975 We cannot apply this sequence as-is, the renamed 'a' could not be
852 976 found for it would have been renamed already. And we cannot copy
853 977 from 'b' instead because 'b' would have been changed already. So
854 978 we scan the git patch for copy and rename commands so we can
855 979 perform the copies ahead of time.
856 980 """
857 981 pos = 0
858 982 try:
859 983 pos = lr.fp.tell()
860 984 fp = lr.fp
861 985 except IOError:
862 986 fp = cStringIO.StringIO(lr.fp.read())
863 987 gitlr = linereader(fp, lr.textmode)
864 988 gitlr.push(firstline)
865 989 (dopatch, gitpatches) = readgitpatch(gitlr)
866 990 fp.seek(pos)
867 991 return dopatch, gitpatches
868 992
869 993 def iterhunks(ui, fp, sourcefile=None):
870 994 """Read a patch and yield the following events:
871 995 - ("file", afile, bfile, firsthunk): select a new target file.
872 996 - ("hunk", hunk): a new hunk is ready to be applied, follows a
873 997 "file" event.
874 998 - ("git", gitchanges): current diff is in git format, gitchanges
875 999 maps filenames to gitpatch records. Unique event.
876 1000 """
877 1001 changed = {}
878 1002 current_hunk = None
879 1003 afile = ""
880 1004 bfile = ""
881 1005 state = None
882 1006 hunknum = 0
883 1007 emitfile = False
884 1008 git = False
885 1009
886 1010 # our states
887 1011 BFILE = 1
888 1012 context = None
889 1013 lr = linereader(fp)
890 1014 dopatch = True
891 1015 # gitworkdone is True if a git operation (copy, rename, ...) was
892 1016 # performed already for the current file. Useful when the file
893 1017 # section may have no hunk.
894 1018 gitworkdone = False
895 1019
896 1020 while True:
897 1021 newfile = False
898 1022 x = lr.readline()
899 1023 if not x:
900 1024 break
901 1025 if current_hunk:
902 1026 if x.startswith('\ '):
903 1027 current_hunk.fix_newline()
904 1028 yield 'hunk', current_hunk
905 1029 current_hunk = None
906 1030 gitworkdone = False
907 1031 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
908 1032 ((context is not False) and x.startswith('***************')))):
909 1033 try:
910 1034 if context is None and x.startswith('***************'):
911 1035 context = True
912 1036 gpatch = changed.get(bfile)
913 1037 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
914 1038 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
915 1039 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
916 1040 except PatchError, err:
917 1041 ui.debug(err)
918 1042 current_hunk = None
919 1043 continue
920 1044 hunknum += 1
921 1045 if emitfile:
922 1046 emitfile = False
923 1047 yield 'file', (afile, bfile, current_hunk)
924 1048 elif state == BFILE and x.startswith('GIT binary patch'):
925 1049 current_hunk = binhunk(changed[bfile])
926 1050 hunknum += 1
927 1051 if emitfile:
928 1052 emitfile = False
929 1053 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
930 1054 current_hunk.extract(lr)
931 1055 elif x.startswith('diff --git'):
932 1056 # check for git diff, scanning the whole patch file if needed
933 1057 m = gitre.match(x)
934 1058 if m:
935 1059 afile, bfile = m.group(1, 2)
936 1060 if not git:
937 1061 git = True
938 1062 dopatch, gitpatches = scangitpatch(lr, x)
939 1063 yield 'git', gitpatches
940 1064 for gp in gitpatches:
941 1065 changed[gp.path] = gp
942 1066 # else error?
943 1067 # copy/rename + modify should modify target, not source
944 1068 gp = changed.get(bfile)
945 1069 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
946 1070 afile = bfile
947 1071 gitworkdone = True
948 1072 newfile = True
949 1073 elif x.startswith('---'):
950 1074 # check for a unified diff
951 1075 l2 = lr.readline()
952 1076 if not l2.startswith('+++'):
953 1077 lr.push(l2)
954 1078 continue
955 1079 newfile = True
956 1080 context = False
957 1081 afile = parsefilename(x)
958 1082 bfile = parsefilename(l2)
959 1083 elif x.startswith('***'):
960 1084 # check for a context diff
961 1085 l2 = lr.readline()
962 1086 if not l2.startswith('---'):
963 1087 lr.push(l2)
964 1088 continue
965 1089 l3 = lr.readline()
966 1090 lr.push(l3)
967 1091 if not l3.startswith("***************"):
968 1092 lr.push(l2)
969 1093 continue
970 1094 newfile = True
971 1095 context = True
972 1096 afile = parsefilename(x)
973 1097 bfile = parsefilename(l2)
974 1098
975 1099 if newfile:
976 1100 emitfile = True
977 1101 state = BFILE
978 1102 hunknum = 0
979 1103 if current_hunk:
980 1104 if current_hunk.complete():
981 1105 yield 'hunk', current_hunk
982 1106 else:
983 1107 raise PatchError(_("malformed patch %s %s") % (afile,
984 1108 current_hunk.desc))
985 1109
986 1110 if hunknum == 0 and dopatch and not gitworkdone:
987 1111 raise NoHunks
988 1112
989 1113 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eolmode='strict'):
990 1114 """
991 1115 Reads a patch from fp and tries to apply it.
992 1116
993 1117 The dict 'changed' is filled in with all of the filenames changed
994 1118 by the patch. Returns 0 for a clean patch, -1 if any rejects were
995 1119 found and 1 if there was any fuzz.
996 1120
997 1121 If 'eolmode' is 'strict', the patch content and patched file are
998 1122 read in binary mode. Otherwise, line endings are ignored when
999 1123 patching then normalized according to 'eolmode'.
1000 1124 """
1001 1125 rejects = 0
1002 1126 err = 0
1003 1127 current_file = None
1004 1128 gitpatches = None
1005 1129 opener = util.opener(os.getcwd())
1006 1130
1007 1131 def closefile():
1008 1132 if not current_file:
1009 1133 return 0
1010 1134 current_file.close()
1011 1135 return len(current_file.rej)
1012 1136
1013 1137 for state, values in iterhunks(ui, fp, sourcefile):
1014 1138 if state == 'hunk':
1015 1139 if not current_file:
1016 1140 continue
1017 1141 current_hunk = values
1018 1142 ret = current_file.apply(current_hunk)
1019 1143 if ret >= 0:
1020 1144 changed.setdefault(current_file.fname, None)
1021 1145 if ret > 0:
1022 1146 err = 1
1023 1147 elif state == 'file':
1024 1148 rejects += closefile()
1025 1149 afile, bfile, first_hunk = values
1026 1150 try:
1027 1151 if sourcefile:
1028 1152 current_file = patchfile(ui, sourcefile, opener,
1029 1153 eolmode=eolmode)
1030 1154 else:
1031 1155 current_file, missing = selectfile(afile, bfile,
1032 1156 first_hunk, strip)
1033 1157 current_file = patchfile(ui, current_file, opener,
1034 1158 missing, eolmode)
1035 1159 except PatchError, err:
1036 1160 ui.warn(str(err) + '\n')
1037 1161 current_file, current_hunk = None, None
1038 1162 rejects += 1
1039 1163 continue
1040 1164 elif state == 'git':
1041 1165 gitpatches = values
1042 1166 cwd = os.getcwd()
1043 1167 for gp in gitpatches:
1044 1168 if gp.op in ('COPY', 'RENAME'):
1045 1169 copyfile(gp.oldpath, gp.path, cwd)
1046 1170 changed[gp.path] = gp
1047 1171 else:
1048 1172 raise util.Abort(_('unsupported parser state: %s') % state)
1049 1173
1050 1174 rejects += closefile()
1051 1175
1052 1176 if rejects:
1053 1177 return -1
1054 1178 return err
1055 1179
1056 1180 def diffopts(ui, opts=None, untrusted=False):
1057 1181 def get(key, name=None, getter=ui.configbool):
1058 1182 return ((opts and opts.get(key)) or
1059 1183 getter('diff', name or key, None, untrusted=untrusted))
1060 1184 return mdiff.diffopts(
1061 1185 text=opts and opts.get('text'),
1062 1186 git=get('git'),
1063 1187 nodates=get('nodates'),
1064 1188 showfunc=get('show_function', 'showfunc'),
1065 1189 ignorews=get('ignore_all_space', 'ignorews'),
1066 1190 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1067 1191 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1068 1192 context=get('unified', getter=ui.config))
1069 1193
1070 1194 def updatedir(ui, repo, patches, similarity=0):
1071 1195 '''Update dirstate after patch application according to metadata'''
1072 1196 if not patches:
1073 1197 return
1074 1198 copies = []
1075 1199 removes = set()
1076 1200 cfiles = patches.keys()
1077 1201 cwd = repo.getcwd()
1078 1202 if cwd:
1079 1203 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1080 1204 for f in patches:
1081 1205 gp = patches[f]
1082 1206 if not gp:
1083 1207 continue
1084 1208 if gp.op == 'RENAME':
1085 1209 copies.append((gp.oldpath, gp.path))
1086 1210 removes.add(gp.oldpath)
1087 1211 elif gp.op == 'COPY':
1088 1212 copies.append((gp.oldpath, gp.path))
1089 1213 elif gp.op == 'DELETE':
1090 1214 removes.add(gp.path)
1091 1215 for src, dst in copies:
1092 1216 repo.copy(src, dst)
1093 1217 if (not similarity) and removes:
1094 1218 repo.remove(sorted(removes), True)
1095 1219 for f in patches:
1096 1220 gp = patches[f]
1097 1221 if gp and gp.mode:
1098 1222 islink, isexec = gp.mode
1099 1223 dst = repo.wjoin(gp.path)
1100 1224 # patch won't create empty files
1101 1225 if gp.op == 'ADD' and not os.path.exists(dst):
1102 1226 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1103 1227 repo.wwrite(gp.path, '', flags)
1104 1228 elif gp.op != 'DELETE':
1105 1229 util.set_flags(dst, islink, isexec)
1106 1230 cmdutil.addremove(repo, cfiles, similarity=similarity)
1107 1231 files = patches.keys()
1108 1232 files.extend([r for r in removes if r not in files])
1109 1233 return sorted(files)
1110 1234
1111 1235 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1112 1236 """use <patcher> to apply <patchname> to the working directory.
1113 1237 returns whether patch was applied with fuzz factor."""
1114 1238
1115 1239 fuzz = False
1116 1240 if cwd:
1117 1241 args.append('-d %s' % util.shellquote(cwd))
1118 1242 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1119 1243 util.shellquote(patchname)))
1120 1244
1121 1245 for line in fp:
1122 1246 line = line.rstrip()
1123 1247 ui.note(line + '\n')
1124 1248 if line.startswith('patching file '):
1125 1249 pf = util.parse_patch_output(line)
1126 1250 printed_file = False
1127 1251 files.setdefault(pf, None)
1128 1252 elif line.find('with fuzz') >= 0:
1129 1253 fuzz = True
1130 1254 if not printed_file:
1131 1255 ui.warn(pf + '\n')
1132 1256 printed_file = True
1133 1257 ui.warn(line + '\n')
1134 1258 elif line.find('saving rejects to file') >= 0:
1135 1259 ui.warn(line + '\n')
1136 1260 elif line.find('FAILED') >= 0:
1137 1261 if not printed_file:
1138 1262 ui.warn(pf + '\n')
1139 1263 printed_file = True
1140 1264 ui.warn(line + '\n')
1141 1265 code = fp.close()
1142 1266 if code:
1143 1267 raise PatchError(_("patch command failed: %s") %
1144 1268 util.explain_exit(code)[0])
1145 1269 return fuzz
1146 1270
1147 1271 def internalpatch(patchobj, ui, strip, cwd, files=None, eolmode='strict'):
1148 1272 """use builtin patch to apply <patchobj> to the working directory.
1149 1273 returns whether patch was applied with fuzz factor."""
1150 1274
1151 1275 if files is None:
1152 1276 files = {}
1153 1277 if eolmode is None:
1154 1278 eolmode = ui.config('patch', 'eol', 'strict')
1155 1279 if eolmode.lower() not in eolmodes:
1156 1280 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1157 1281 eolmode = eolmode.lower()
1158 1282
1159 1283 try:
1160 1284 fp = open(patchobj, 'rb')
1161 1285 except TypeError:
1162 1286 fp = patchobj
1163 1287 if cwd:
1164 1288 curdir = os.getcwd()
1165 1289 os.chdir(cwd)
1166 1290 try:
1167 1291 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode)
1168 1292 finally:
1169 1293 if cwd:
1170 1294 os.chdir(curdir)
1171 1295 if fp != patchobj:
1172 1296 fp.close()
1173 1297 if ret < 0:
1174 1298 raise PatchError
1175 1299 return ret > 0
1176 1300
1177 1301 def patch(patchname, ui, strip=1, cwd=None, files=None, eolmode='strict'):
1178 1302 """Apply <patchname> to the working directory.
1179 1303
1180 1304 'eolmode' specifies how end of lines should be handled. It can be:
1181 1305 - 'strict': inputs are read in binary mode, EOLs are preserved
1182 1306 - 'crlf': EOLs are ignored when patching and reset to CRLF
1183 1307 - 'lf': EOLs are ignored when patching and reset to LF
1184 1308 - None: get it from user settings, default to 'strict'
1185 1309 'eolmode' is ignored when using an external patcher program.
1186 1310
1187 1311 Returns whether patch was applied with fuzz factor.
1188 1312 """
1189 1313 patcher = ui.config('ui', 'patch')
1190 1314 args = []
1191 1315 if files is None:
1192 1316 files = {}
1193 1317 try:
1194 1318 if patcher:
1195 1319 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1196 1320 files)
1197 1321 else:
1198 1322 try:
1199 1323 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1200 1324 except NoHunks:
1201 1325 patcher = (util.find_exe('gpatch') or util.find_exe('patch')
1202 1326 or 'patch')
1203 1327 ui.debug('no valid hunks found; trying with %r instead\n' %
1204 1328 patcher)
1205 1329 if util.needbinarypatch():
1206 1330 args.append('--binary')
1207 1331 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1208 1332 files)
1209 1333 except PatchError, err:
1210 1334 s = str(err)
1211 1335 if s:
1212 1336 raise util.Abort(s)
1213 1337 else:
1214 1338 raise util.Abort(_('patch failed to apply'))
1215 1339
1216 1340 def b85diff(to, tn):
1217 1341 '''print base85-encoded binary diff'''
1218 1342 def gitindex(text):
1219 1343 if not text:
1220 1344 return '0' * 40
1221 1345 l = len(text)
1222 1346 s = util.sha1('blob %d\0' % l)
1223 1347 s.update(text)
1224 1348 return s.hexdigest()
1225 1349
1226 1350 def fmtline(line):
1227 1351 l = len(line)
1228 1352 if l <= 26:
1229 1353 l = chr(ord('A') + l - 1)
1230 1354 else:
1231 1355 l = chr(l - 26 + ord('a') - 1)
1232 1356 return '%c%s\n' % (l, base85.b85encode(line, True))
1233 1357
1234 1358 def chunk(text, csize=52):
1235 1359 l = len(text)
1236 1360 i = 0
1237 1361 while i < l:
1238 1362 yield text[i:i + csize]
1239 1363 i += csize
1240 1364
1241 1365 tohash = gitindex(to)
1242 1366 tnhash = gitindex(tn)
1243 1367 if tohash == tnhash:
1244 1368 return ""
1245 1369
1246 1370 # TODO: deltas
1247 1371 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1248 1372 (tohash, tnhash, len(tn))]
1249 1373 for l in chunk(zlib.compress(tn)):
1250 1374 ret.append(fmtline(l))
1251 1375 ret.append('\n')
1252 1376 return ''.join(ret)
1253 1377
1254 1378 class GitDiffRequired(Exception):
1255 1379 pass
1256 1380
1257 1381 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
1258 1382 losedatafn=None):
1259 1383 '''yields diff of changes to files between two nodes, or node and
1260 1384 working directory.
1261 1385
1262 1386 if node1 is None, use first dirstate parent instead.
1263 1387 if node2 is None, compare node1 with working directory.
1264 1388
1265 1389 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
1266 1390 every time some change cannot be represented with the current
1267 1391 patch format. Return False to upgrade to git patch format, True to
1268 1392 accept the loss or raise an exception to abort the diff. It is
1269 1393 called with the name of current file being diffed as 'fn'. If set
1270 1394 to None, patches will always be upgraded to git format when
1271 1395 necessary.
1272 1396 '''
1273 1397
1274 1398 if opts is None:
1275 1399 opts = mdiff.defaultopts
1276 1400
1277 1401 if not node1 and not node2:
1278 1402 node1 = repo.dirstate.parents()[0]
1279 1403
1280 1404 def lrugetfilectx():
1281 1405 cache = {}
1282 1406 order = []
1283 1407 def getfilectx(f, ctx):
1284 1408 fctx = ctx.filectx(f, filelog=cache.get(f))
1285 1409 if f not in cache:
1286 1410 if len(cache) > 20:
1287 1411 del cache[order.pop(0)]
1288 1412 cache[f] = fctx.filelog()
1289 1413 else:
1290 1414 order.remove(f)
1291 1415 order.append(f)
1292 1416 return fctx
1293 1417 return getfilectx
1294 1418 getfilectx = lrugetfilectx()
1295 1419
1296 1420 ctx1 = repo[node1]
1297 1421 ctx2 = repo[node2]
1298 1422
1299 1423 if not changes:
1300 1424 changes = repo.status(ctx1, ctx2, match=match)
1301 1425 modified, added, removed = changes[:3]
1302 1426
1303 1427 if not modified and not added and not removed:
1304 1428 return []
1305 1429
1306 1430 revs = None
1307 1431 if not repo.ui.quiet:
1308 1432 hexfunc = repo.ui.debugflag and hex or short
1309 1433 revs = [hexfunc(node) for node in [node1, node2] if node]
1310 1434
1311 1435 copy = {}
1312 1436 if opts.git or opts.upgrade:
1313 1437 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0]
1314 1438 copy = copy.copy()
1315 1439 for k, v in copy.items():
1316 1440 copy[v] = k
1317 1441
1318 1442 difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2,
1319 1443 modified, added, removed, copy, getfilectx, opts, losedata)
1320 1444 if opts.upgrade and not opts.git:
1321 1445 try:
1322 1446 def losedata(fn):
1323 1447 if not losedatafn or not losedatafn(fn=fn):
1324 1448 raise GitDiffRequired()
1325 1449 # Buffer the whole output until we are sure it can be generated
1326 1450 return list(difffn(opts.copy(git=False), losedata))
1327 1451 except GitDiffRequired:
1328 1452 return difffn(opts.copy(git=True), None)
1329 1453 else:
1330 1454 return difffn(opts, None)
1331 1455
1332 1456 def _addmodehdr(header, omode, nmode):
1333 1457 if omode != nmode:
1334 1458 header.append('old mode %s\n' % omode)
1335 1459 header.append('new mode %s\n' % nmode)
1336 1460
1337 1461 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
1338 1462 copy, getfilectx, opts, losedatafn):
1339 1463
1340 1464 date1 = util.datestr(ctx1.date())
1341 1465 man1 = ctx1.manifest()
1342 1466
1343 1467 gone = set()
1344 1468 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1345 1469
1346 1470 if opts.git:
1347 1471 revs = None
1348 1472
1349 1473 for f in sorted(modified + added + removed):
1350 1474 to = None
1351 1475 tn = None
1352 1476 dodiff = True
1353 1477 header = []
1354 1478 if f in man1:
1355 1479 to = getfilectx(f, ctx1).data()
1356 1480 if f not in removed:
1357 1481 tn = getfilectx(f, ctx2).data()
1358 1482 a, b = f, f
1359 1483 if opts.git or losedatafn:
1360 1484 if f in added:
1361 1485 mode = gitmode[ctx2.flags(f)]
1362 1486 if f in copy:
1363 1487 if opts.git:
1364 1488 a = copy[f]
1365 1489 omode = gitmode[man1.flags(a)]
1366 1490 _addmodehdr(header, omode, mode)
1367 1491 if a in removed and a not in gone:
1368 1492 op = 'rename'
1369 1493 gone.add(a)
1370 1494 else:
1371 1495 op = 'copy'
1372 1496 header.append('%s from %s\n' % (op, a))
1373 1497 header.append('%s to %s\n' % (op, f))
1374 1498 to = getfilectx(a, ctx1).data()
1375 1499 else:
1376 1500 losedatafn(f)
1377 1501 else:
1378 1502 if opts.git:
1379 1503 header.append('new file mode %s\n' % mode)
1380 1504 elif ctx2.flags(f):
1381 1505 losedatafn(f)
1382 1506 if util.binary(tn):
1383 1507 if opts.git:
1384 1508 dodiff = 'binary'
1385 1509 else:
1386 1510 losedatafn(f)
1387 1511 if not opts.git and not tn:
1388 1512 # regular diffs cannot represent new empty file
1389 1513 losedatafn(f)
1390 1514 elif f in removed:
1391 1515 if opts.git:
1392 1516 # have we already reported a copy above?
1393 1517 if f in copy and copy[f] in added and copy[copy[f]] == f:
1394 1518 dodiff = False
1395 1519 else:
1396 1520 header.append('deleted file mode %s\n' %
1397 1521 gitmode[man1.flags(f)])
1398 1522 elif not to:
1399 1523 # regular diffs cannot represent empty file deletion
1400 1524 losedatafn(f)
1401 1525 else:
1402 1526 oflag = man1.flags(f)
1403 1527 nflag = ctx2.flags(f)
1404 1528 binary = util.binary(to) or util.binary(tn)
1405 1529 if opts.git:
1406 1530 _addmodehdr(header, gitmode[oflag], gitmode[nflag])
1407 1531 if binary:
1408 1532 dodiff = 'binary'
1409 1533 elif binary or nflag != oflag:
1410 1534 losedatafn(f)
1411 1535 if opts.git:
1412 1536 header.insert(0, mdiff.diffline(revs, a, b, opts))
1413 1537
1414 1538 if dodiff:
1415 1539 if dodiff == 'binary':
1416 1540 text = b85diff(to, tn)
1417 1541 else:
1418 1542 text = mdiff.unidiff(to, date1,
1419 1543 # ctx2 date may be dynamic
1420 1544 tn, util.datestr(ctx2.date()),
1421 1545 a, b, revs, opts=opts)
1422 1546 if header and (text or len(header) > 1):
1423 1547 yield ''.join(header)
1424 1548 if text:
1425 1549 yield text
1426 1550
1427 1551 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1428 1552 opts=None):
1429 1553 '''export changesets as hg patches.'''
1430 1554
1431 1555 total = len(revs)
1432 1556 revwidth = max([len(str(rev)) for rev in revs])
1433 1557
1434 1558 def single(rev, seqno, fp):
1435 1559 ctx = repo[rev]
1436 1560 node = ctx.node()
1437 1561 parents = [p.node() for p in ctx.parents() if p]
1438 1562 branch = ctx.branch()
1439 1563 if switch_parent:
1440 1564 parents.reverse()
1441 1565 prev = (parents and parents[0]) or nullid
1442 1566
1443 1567 if not fp:
1444 1568 fp = cmdutil.make_file(repo, template, node, total=total,
1445 1569 seqno=seqno, revwidth=revwidth,
1446 1570 mode='ab')
1447 1571 if fp != sys.stdout and hasattr(fp, 'name'):
1448 1572 repo.ui.note("%s\n" % fp.name)
1449 1573
1450 1574 fp.write("# HG changeset patch\n")
1451 1575 fp.write("# User %s\n" % ctx.user())
1452 1576 fp.write("# Date %d %d\n" % ctx.date())
1453 1577 if branch and (branch != 'default'):
1454 1578 fp.write("# Branch %s\n" % branch)
1455 1579 fp.write("# Node ID %s\n" % hex(node))
1456 1580 fp.write("# Parent %s\n" % hex(prev))
1457 1581 if len(parents) > 1:
1458 1582 fp.write("# Parent %s\n" % hex(parents[1]))
1459 1583 fp.write(ctx.description().rstrip())
1460 1584 fp.write("\n\n")
1461 1585
1462 1586 for chunk in diff(repo, prev, node, opts=opts):
1463 1587 fp.write(chunk)
1464 1588
1465 1589 for seqno, rev in enumerate(revs):
1466 1590 single(rev, seqno + 1, fp)
1467 1591
1468 1592 def diffstatdata(lines):
1469 1593 filename, adds, removes = None, 0, 0
1470 1594 for line in lines:
1471 1595 if line.startswith('diff'):
1472 1596 if filename:
1473 1597 isbinary = adds == 0 and removes == 0
1474 1598 yield (filename, adds, removes, isbinary)
1475 1599 # set numbers to 0 anyway when starting new file
1476 1600 adds, removes = 0, 0
1477 1601 if line.startswith('diff --git'):
1478 1602 filename = gitre.search(line).group(1)
1479 1603 else:
1480 1604 # format: "diff -r ... -r ... filename"
1481 1605 filename = line.split(None, 5)[-1]
1482 1606 elif line.startswith('+') and not line.startswith('+++'):
1483 1607 adds += 1
1484 1608 elif line.startswith('-') and not line.startswith('---'):
1485 1609 removes += 1
1486 1610 if filename:
1487 1611 isbinary = adds == 0 and removes == 0
1488 1612 yield (filename, adds, removes, isbinary)
1489 1613
1490 1614 def diffstat(lines, width=80, git=False):
1491 1615 output = []
1492 1616 stats = list(diffstatdata(lines))
1493 1617
1494 1618 maxtotal, maxname = 0, 0
1495 1619 totaladds, totalremoves = 0, 0
1496 1620 hasbinary = False
1497 1621 for filename, adds, removes, isbinary in stats:
1498 1622 totaladds += adds
1499 1623 totalremoves += removes
1500 1624 maxname = max(maxname, len(filename))
1501 1625 maxtotal = max(maxtotal, adds + removes)
1502 1626 if isbinary:
1503 1627 hasbinary = True
1504 1628
1505 1629 countwidth = len(str(maxtotal))
1506 1630 if hasbinary and countwidth < 3:
1507 1631 countwidth = 3
1508 1632 graphwidth = width - countwidth - maxname - 6
1509 1633 if graphwidth < 10:
1510 1634 graphwidth = 10
1511 1635
1512 1636 def scale(i):
1513 1637 if maxtotal <= graphwidth:
1514 1638 return i
1515 1639 # If diffstat runs out of room it doesn't print anything,
1516 1640 # which isn't very useful, so always print at least one + or -
1517 1641 # if there were at least some changes.
1518 1642 return max(i * graphwidth // maxtotal, int(bool(i)))
1519 1643
1520 1644 for filename, adds, removes, isbinary in stats:
1521 1645 if git and isbinary:
1522 1646 count = 'Bin'
1523 1647 else:
1524 1648 count = adds + removes
1525 1649 pluses = '+' * scale(adds)
1526 1650 minuses = '-' * scale(removes)
1527 1651 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1528 1652 count, pluses, minuses))
1529 1653
1530 1654 if stats:
1531 1655 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1532 1656 % (len(stats), totaladds, totalremoves))
1533 1657
1534 1658 return ''.join(output)
@@ -1,367 +1,374 b''
1 1 #!/bin/sh
2 2
3 3 hg init a
4 4 mkdir a/d1
5 5 mkdir a/d1/d2
6 6 echo line 1 > a/a
7 7 echo line 1 > a/d1/d2/a
8 8 hg --cwd a ci -Ama
9 9
10 10 echo line 2 >> a/a
11 11 hg --cwd a ci -u someone -d '1 0' -m'second change'
12 12
13 13 echo % import exported patch
14 14 hg clone -r0 a b
15 15 hg --cwd a export tip > tip.patch
16 16 hg --cwd b import ../tip.patch
17 17 echo % message should be same
18 18 hg --cwd b tip | grep 'second change'
19 19 echo % committer should be same
20 20 hg --cwd b tip | grep someone
21 21 rm -r b
22 22
23 23 echo % import exported patch with external patcher
24 24 cat > dummypatch.py <<EOF
25 25 print 'patching file a'
26 26 file('a', 'wb').write('line2\n')
27 27 EOF
28 28 chmod +x dummypatch.py
29 29 hg clone -r0 a b
30 30 hg --cwd a export tip > tip.patch
31 31 hg --config ui.patch='python ../dummypatch.py' --cwd b import ../tip.patch
32 32 cat b/a
33 33 rm -r b
34 34
35 35 echo % import of plain diff should fail without message
36 36 hg clone -r0 a b
37 37 hg --cwd a diff -r0:1 > tip.patch
38 38 hg --cwd b import ../tip.patch
39 39 rm -r b
40 40
41 41 echo % import of plain diff should be ok with message
42 42 hg clone -r0 a b
43 43 hg --cwd a diff -r0:1 > tip.patch
44 44 hg --cwd b import -mpatch ../tip.patch
45 45 rm -r b
46 46
47 47 echo % import of plain diff with specific date and user
48 48 hg clone -r0 a b
49 49 hg --cwd a diff -r0:1 > tip.patch
50 50 hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../tip.patch
51 51 hg -R b tip -pv
52 52 rm -r b
53 53
54 54 echo % import of plain diff should be ok with --no-commit
55 55 hg clone -r0 a b
56 56 hg --cwd a diff -r0:1 > tip.patch
57 57 hg --cwd b import --no-commit ../tip.patch
58 58 hg --cwd b diff --nodates
59 59 rm -r b
60 60
61 61 echo % hg -R repo import
62 62 # put the clone in a subdir - having a directory named "a"
63 63 # used to hide a bug.
64 64 mkdir dir
65 65 hg clone -r0 a dir/b
66 66 hg --cwd a export tip > dir/tip.patch
67 67 cd dir
68 68 hg -R b import tip.patch
69 69 cd ..
70 70 rm -r dir
71 71
72 72 echo % import from stdin
73 73 hg clone -r0 a b
74 74 hg --cwd a export tip | hg --cwd b import -
75 75 rm -r b
76 76
77 echo % import two patches in one stream
78 hg init b
79 hg --cwd a export 0:tip | hg --cwd b import -
80 hg --cwd a id
81 hg --cwd b id
82 rm -r b
83
77 84 echo % override commit message
78 85 hg clone -r0 a b
79 86 hg --cwd a export tip | hg --cwd b import -m 'override' -
80 87 hg --cwd b tip | grep override
81 88 rm -r b
82 89
83 90 cat > mkmsg.py <<EOF
84 91 import email.Message, sys
85 92 msg = email.Message.Message()
86 93 msg.set_payload('email commit message\n' + open('tip.patch', 'rb').read())
87 94 msg['Subject'] = 'email patch'
88 95 msg['From'] = 'email patcher'
89 96 sys.stdout.write(msg.as_string())
90 97 EOF
91 98
92 99 echo % plain diff in email, subject, message body
93 100 hg clone -r0 a b
94 101 hg --cwd a diff -r0:1 > tip.patch
95 102 python mkmsg.py > msg.patch
96 103 hg --cwd b import ../msg.patch
97 104 hg --cwd b tip | grep email
98 105 rm -r b
99 106
100 107 echo % plain diff in email, no subject, message body
101 108 hg clone -r0 a b
102 109 grep -v '^Subject:' msg.patch | hg --cwd b import -
103 110 rm -r b
104 111
105 112 echo % plain diff in email, subject, no message body
106 113 hg clone -r0 a b
107 114 grep -v '^email ' msg.patch | hg --cwd b import -
108 115 rm -r b
109 116
110 117 echo % plain diff in email, no subject, no message body, should fail
111 118 hg clone -r0 a b
112 119 egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
113 120 rm -r b
114 121
115 122 echo % hg export in email, should use patch header
116 123 hg clone -r0 a b
117 124 hg --cwd a export tip > tip.patch
118 125 python mkmsg.py | hg --cwd b import -
119 126 hg --cwd b tip | grep second
120 127 rm -r b
121 128
122 129 # subject: duplicate detection, removal of [PATCH]
123 130 # The '---' tests the gitsendmail handling without proper mail headers
124 131 cat > mkmsg2.py <<EOF
125 132 import email.Message, sys
126 133 msg = email.Message.Message()
127 134 msg.set_payload('email patch\n\nnext line\n---\n' + open('tip.patch').read())
128 135 msg['Subject'] = '[PATCH] email patch'
129 136 msg['From'] = 'email patcher'
130 137 sys.stdout.write(msg.as_string())
131 138 EOF
132 139
133 140 echo '% plain diff in email, [PATCH] subject, message body with subject'
134 141 hg clone -r0 a b
135 142 hg --cwd a diff -r0:1 > tip.patch
136 143 python mkmsg2.py | hg --cwd b import -
137 144 hg --cwd b tip --template '{desc}\n'
138 145 rm -r b
139 146
140 147 # We weren't backing up the correct dirstate file when importing many patches
141 148 # (issue963)
142 149 echo '% import patch1 patch2; rollback'
143 150 echo line 3 >> a/a
144 151 hg --cwd a ci -m'third change'
145 152 hg --cwd a export -o '../patch%R' 1 2
146 153 hg clone -qr0 a b
147 154 hg --cwd b parents --template 'parent: {rev}\n'
148 155 hg --cwd b import ../patch1 ../patch2
149 156 hg --cwd b rollback
150 157 hg --cwd b parents --template 'parent: {rev}\n'
151 158 rm -r b
152 159
153 160 # bug non regression test
154 161 # importing a patch in a subdirectory failed at the commit stage
155 162 echo line 2 >> a/d1/d2/a
156 163 hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
157 164 echo % hg import in a subdirectory
158 165 hg clone -r0 a b
159 166 hg --cwd a export tip | sed -e 's/d1\/d2\///' > tip.patch
160 167 dir=`pwd`
161 168 cd b/d1/d2 2>&1 > /dev/null
162 169 hg import ../../../tip.patch
163 170 cd $dir
164 171 echo "% message should be 'subdir change'"
165 172 hg --cwd b tip | grep 'subdir change'
166 173 echo "% committer should be 'someoneelse'"
167 174 hg --cwd b tip | grep someoneelse
168 175 echo "% should be empty"
169 176 hg --cwd b status
170 177
171 178
172 179 # Test fuzziness (ambiguous patch location, fuzz=2)
173 180 echo % test fuzziness
174 181 hg init fuzzy
175 182 cd fuzzy
176 183 echo line1 > a
177 184 echo line0 >> a
178 185 echo line3 >> a
179 186 hg ci -Am adda
180 187 echo line1 > a
181 188 echo line2 >> a
182 189 echo line0 >> a
183 190 echo line3 >> a
184 191 hg ci -m change a
185 192 hg export tip > tip.patch
186 193 hg up -C 0
187 194 echo line1 > a
188 195 echo line0 >> a
189 196 echo line1 >> a
190 197 echo line0 >> a
191 198 hg ci -m brancha
192 199 hg import -v tip.patch
193 200 cd ..
194 201
195 202 # Test hunk touching empty files (issue906)
196 203 hg init empty
197 204 cd empty
198 205 touch a
199 206 touch b1
200 207 touch c1
201 208 echo d > d
202 209 hg ci -Am init
203 210 echo a > a
204 211 echo b > b1
205 212 hg mv b1 b2
206 213 echo c > c1
207 214 hg copy c1 c2
208 215 rm d
209 216 touch d
210 217 hg diff --git
211 218 hg ci -m empty
212 219 hg export --git tip > empty.diff
213 220 hg up -C 0
214 221 hg import empty.diff
215 222 for name in a b1 b2 c1 c2 d;
216 223 do
217 224 echo % $name file
218 225 test -f $name && cat $name
219 226 done
220 227 cd ..
221 228
222 229 # Test importing a patch ending with a binary file removal
223 230 echo % test trailing binary removal
224 231 hg init binaryremoval
225 232 cd binaryremoval
226 233 echo a > a
227 234 python -c "file('b', 'wb').write('a\x00b')"
228 235 hg ci -Am addall
229 236 hg rm a
230 237 hg rm b
231 238 hg st
232 239 hg ci -m remove
233 240 hg export --git . > remove.diff
234 241 cat remove.diff | grep git
235 242 hg up -C 0
236 243 hg import remove.diff
237 244 hg manifest
238 245 cd ..
239 246
240 247 echo % 'test update+rename with common name (issue 927)'
241 248 hg init t
242 249 cd t
243 250 touch a
244 251 hg ci -Am t
245 252 echo a > a
246 253 # Here, bfile.startswith(afile)
247 254 hg copy a a2
248 255 hg ci -m copya
249 256 hg export --git tip > copy.diff
250 257 hg up -C 0
251 258 hg import copy.diff
252 259 echo % view a
253 260 # a should contain an 'a'
254 261 cat a
255 262 echo % view a2
256 263 # and a2 should have duplicated it
257 264 cat a2
258 265 cd ..
259 266
260 267 echo % 'test -p0'
261 268 hg init p0
262 269 cd p0
263 270 echo a > a
264 271 hg ci -Am t
265 272 hg import -p0 - << EOF
266 273 foobar
267 274 --- a Sat Apr 12 22:43:58 2008 -0400
268 275 +++ a Sat Apr 12 22:44:05 2008 -0400
269 276 @@ -1,1 +1,1 @@
270 277 -a
271 278 +bb
272 279 EOF
273 280 hg status
274 281 cat a
275 282 cd ..
276 283
277 284 echo % 'test paths outside repo root'
278 285 mkdir outside
279 286 touch outside/foo
280 287 hg init inside
281 288 cd inside
282 289 hg import - <<EOF
283 290 diff --git a/a b/b
284 291 rename from ../outside/foo
285 292 rename to bar
286 293 EOF
287 294 cd ..
288 295
289 296 echo '% test import with similarity (issue295)'
290 297 hg init sim
291 298 cd sim
292 299 echo 'this is a test' > a
293 300 hg ci -Ama
294 301 cat > ../rename.diff <<EOF
295 302 diff --git a/a b/a
296 303 deleted file mode 100644
297 304 --- a/a
298 305 +++ /dev/null
299 306 @@ -1,1 +0,0 @@
300 307 -this is a test
301 308 diff --git a/b b/b
302 309 new file mode 100644
303 310 --- /dev/null
304 311 +++ b/b
305 312 @@ -0,0 +1,2 @@
306 313 +this is a test
307 314 +foo
308 315 EOF
309 316 hg import --no-commit -v -s 1 ../rename.diff
310 317 hg st -C
311 318 hg revert -a
312 319 rm b
313 320 hg import --no-commit -v -s 100 ../rename.diff
314 321 hg st -C
315 322 cd ..
316 323
317 324
318 325 echo '% add empty file from the end of patch (issue 1495)'
319 326 hg init addemptyend
320 327 cd addemptyend
321 328 touch a
322 329 hg addremove
323 330 hg ci -m "commit"
324 331 cat > a.patch <<EOF
325 332 diff --git a/a b/a
326 333 --- a/a
327 334 +++ b/a
328 335 @@ -0,0 +1,1 @@
329 336 +a
330 337 diff --git a/b b/b
331 338 new file mode 100644
332 339 EOF
333 340 hg import --no-commit a.patch
334 341 cd ..
335 342
336 343 echo '% create file when source is not /dev/null'
337 344 cat > create.patch <<EOF
338 345 diff -Naur proj-orig/foo proj-new/foo
339 346 --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
340 347 +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
341 348 @@ -0,0 +1,1 @@
342 349 +a
343 350 EOF
344 351 hg init oddcreate
345 352 cd oddcreate
346 353 hg import --no-commit ../create.patch
347 354 cat foo
348 355
349 356 echo % 'first line mistaken for email headers (issue 1859)'
350 357 hg init emailconfusion
351 358 cd emailconfusion
352 359 cat > a.patch <<EOF
353 360 module: summary
354 361
355 362 description
356 363
357 364
358 365 diff -r 000000000000 -r 9b4c1e343b55 test.txt
359 366 --- /dev/null
360 367 +++ b/a
361 368 @@ -0,0 +1,1 @@
362 369 +a
363 370 EOF
364 371 hg import -d '0 0' a.patch
365 372 hg parents -v
366 373 cd ..
367 374
@@ -1,310 +1,316 b''
1 1 adding a
2 2 adding d1/d2/a
3 3 % import exported patch
4 4 requesting all changes
5 5 adding changesets
6 6 adding manifests
7 7 adding file changes
8 8 added 1 changesets with 2 changes to 2 files
9 9 updating to branch default
10 10 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 11 applying ../tip.patch
12 12 % message should be same
13 13 summary: second change
14 14 % committer should be same
15 15 user: someone
16 16 % import exported patch with external patcher
17 17 requesting all changes
18 18 adding changesets
19 19 adding manifests
20 20 adding file changes
21 21 added 1 changesets with 2 changes to 2 files
22 22 updating to branch default
23 23 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 24 applying ../tip.patch
25 25 line2
26 26 % import of plain diff should fail without message
27 27 requesting all changes
28 28 adding changesets
29 29 adding manifests
30 30 adding file changes
31 31 added 1 changesets with 2 changes to 2 files
32 32 updating to branch default
33 33 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 34 applying ../tip.patch
35 35 abort: empty commit message
36 36 % import of plain diff should be ok with message
37 37 requesting all changes
38 38 adding changesets
39 39 adding manifests
40 40 adding file changes
41 41 added 1 changesets with 2 changes to 2 files
42 42 updating to branch default
43 43 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 44 applying ../tip.patch
45 45 % import of plain diff with specific date and user
46 46 requesting all changes
47 47 adding changesets
48 48 adding manifests
49 49 adding file changes
50 50 added 1 changesets with 2 changes to 2 files
51 51 updating to branch default
52 52 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 53 applying ../tip.patch
54 54 changeset: 1:ca68f19f3a40
55 55 tag: tip
56 56 user: user@nowhere.net
57 57 date: Thu Jan 01 00:00:01 1970 +0000
58 58 files: a
59 59 description:
60 60 patch
61 61
62 62
63 63 diff -r 80971e65b431 -r ca68f19f3a40 a
64 64 --- a/a Thu Jan 01 00:00:00 1970 +0000
65 65 +++ b/a Thu Jan 01 00:00:01 1970 +0000
66 66 @@ -1,1 +1,2 @@
67 67 line 1
68 68 +line 2
69 69
70 70 % import of plain diff should be ok with --no-commit
71 71 requesting all changes
72 72 adding changesets
73 73 adding manifests
74 74 adding file changes
75 75 added 1 changesets with 2 changes to 2 files
76 76 updating to branch default
77 77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 78 applying ../tip.patch
79 79 diff -r 80971e65b431 a
80 80 --- a/a
81 81 +++ b/a
82 82 @@ -1,1 +1,2 @@
83 83 line 1
84 84 +line 2
85 85 % hg -R repo import
86 86 requesting all changes
87 87 adding changesets
88 88 adding manifests
89 89 adding file changes
90 90 added 1 changesets with 2 changes to 2 files
91 91 updating to branch default
92 92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 93 applying tip.patch
94 94 % import from stdin
95 95 requesting all changes
96 96 adding changesets
97 97 adding manifests
98 98 adding file changes
99 99 added 1 changesets with 2 changes to 2 files
100 100 updating to branch default
101 101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 102 applying patch from stdin
103 % import two patches in one stream
104 applying patch from stdin
105 applied 80971e65b431
106 1d4bd90af0e4 tip
107 1d4bd90af0e4 tip
103 108 % override commit message
104 109 requesting all changes
105 110 adding changesets
106 111 adding manifests
107 112 adding file changes
108 113 added 1 changesets with 2 changes to 2 files
109 114 updating to branch default
110 115 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 116 applying patch from stdin
112 117 summary: override
113 118 % plain diff in email, subject, message body
114 119 requesting all changes
115 120 adding changesets
116 121 adding manifests
117 122 adding file changes
118 123 added 1 changesets with 2 changes to 2 files
119 124 updating to branch default
120 125 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 126 applying ../msg.patch
122 127 user: email patcher
123 128 summary: email patch
124 129 % plain diff in email, no subject, message body
125 130 requesting all changes
126 131 adding changesets
127 132 adding manifests
128 133 adding file changes
129 134 added 1 changesets with 2 changes to 2 files
130 135 updating to branch default
131 136 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 137 applying patch from stdin
133 138 % plain diff in email, subject, no message body
134 139 requesting all changes
135 140 adding changesets
136 141 adding manifests
137 142 adding file changes
138 143 added 1 changesets with 2 changes to 2 files
139 144 updating to branch default
140 145 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 146 applying patch from stdin
142 147 % plain diff in email, no subject, no message body, should fail
143 148 requesting all changes
144 149 adding changesets
145 150 adding manifests
146 151 adding file changes
147 152 added 1 changesets with 2 changes to 2 files
148 153 updating to branch default
149 154 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 155 applying patch from stdin
151 156 abort: empty commit message
152 157 % hg export in email, should use patch header
153 158 requesting all changes
154 159 adding changesets
155 160 adding manifests
156 161 adding file changes
157 162 added 1 changesets with 2 changes to 2 files
158 163 updating to branch default
159 164 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 165 applying patch from stdin
161 166 summary: second change
162 167 % plain diff in email, [PATCH] subject, message body with subject
163 168 requesting all changes
164 169 adding changesets
165 170 adding manifests
166 171 adding file changes
167 172 added 1 changesets with 2 changes to 2 files
168 173 updating to branch default
169 174 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 175 applying patch from stdin
171 176 email patch
172 177
173 178 next line
174 179 ---
175 180 % import patch1 patch2; rollback
176 181 parent: 0
177 182 applying ../patch1
178 183 applying ../patch2
184 applied 1d4bd90af0e4
179 185 rolling back last transaction
180 186 parent: 1
181 187 % hg import in a subdirectory
182 188 requesting all changes
183 189 adding changesets
184 190 adding manifests
185 191 adding file changes
186 192 added 1 changesets with 2 changes to 2 files
187 193 updating to branch default
188 194 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 195 applying ../../../tip.patch
190 196 % message should be 'subdir change'
191 197 summary: subdir change
192 198 % committer should be 'someoneelse'
193 199 user: someoneelse
194 200 % should be empty
195 201 % test fuzziness
196 202 adding a
197 203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 204 created new head
199 205 applying tip.patch
200 206 patching file a
201 207 Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines).
202 208 a
203 209 adding a
204 210 adding b1
205 211 adding c1
206 212 adding d
207 213 diff --git a/a b/a
208 214 --- a/a
209 215 +++ b/a
210 216 @@ -0,0 +1,1 @@
211 217 +a
212 218 diff --git a/b1 b/b2
213 219 rename from b1
214 220 rename to b2
215 221 --- a/b1
216 222 +++ b/b2
217 223 @@ -0,0 +1,1 @@
218 224 +b
219 225 diff --git a/c1 b/c1
220 226 --- a/c1
221 227 +++ b/c1
222 228 @@ -0,0 +1,1 @@
223 229 +c
224 230 diff --git a/c1 b/c2
225 231 copy from c1
226 232 copy to c2
227 233 --- a/c1
228 234 +++ b/c2
229 235 @@ -0,0 +1,1 @@
230 236 +c
231 237 diff --git a/d b/d
232 238 --- a/d
233 239 +++ b/d
234 240 @@ -1,1 +0,0 @@
235 241 -d
236 242 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
237 243 applying empty.diff
238 244 % a file
239 245 a
240 246 % b1 file
241 247 % b2 file
242 248 b
243 249 % c1 file
244 250 c
245 251 % c2 file
246 252 c
247 253 % d file
248 254 % test trailing binary removal
249 255 adding a
250 256 adding b
251 257 R a
252 258 R b
253 259 diff --git a/a b/a
254 260 diff --git a/b b/b
255 261 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 262 applying remove.diff
257 263 % test update+rename with common name (issue 927)
258 264 adding a
259 265 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
260 266 applying copy.diff
261 267 % view a
262 268 a
263 269 % view a2
264 270 a
265 271 % test -p0
266 272 adding a
267 273 applying patch from stdin
268 274 bb
269 275 % test paths outside repo root
270 276 applying patch from stdin
271 277 abort: ../outside/foo not under root
272 278 % test import with similarity (issue295)
273 279 adding a
274 280 applying ../rename.diff
275 281 patching file a
276 282 patching file b
277 283 removing a
278 284 adding b
279 285 recording removal of a as rename to b (88% similar)
280 286 A b
281 287 a
282 288 R a
283 289 undeleting a
284 290 forgetting b
285 291 applying ../rename.diff
286 292 patching file a
287 293 patching file b
288 294 removing a
289 295 adding b
290 296 A b
291 297 R a
292 298 % add empty file from the end of patch (issue 1495)
293 299 adding a
294 300 applying a.patch
295 301 % create file when source is not /dev/null
296 302 applying ../create.patch
297 303 a
298 304 % first line mistaken for email headers (issue 1859)
299 305 applying a.patch
300 306 changeset: 0:5a681217c0ad
301 307 tag: tip
302 308 user: test
303 309 date: Thu Jan 01 00:00:00 1970 +0000
304 310 files: a
305 311 description:
306 312 module: summary
307 313
308 314 description
309 315
310 316
General Comments 0
You need to be logged in to leave comments. Login now