##// END OF EJS Templates
refactor text diff/patch code....
Vadim Gelfer -
r2874:4ec58b15 default
parent child Browse files
Show More
@@ -0,0 +1,68 b''
1 # commands.py - command processing for mercurial
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 from demandload import demandload
9 from node import *
10 from i18n import gettext as _
11 demandload(globals(), 'os sys')
12
13 def make_filename(repo, pat, node,
14 total=None, seqno=None, revwidth=None, pathname=None):
15 node_expander = {
16 'H': lambda: hex(node),
17 'R': lambda: str(repo.changelog.rev(node)),
18 'h': lambda: short(node),
19 }
20 expander = {
21 '%': lambda: '%',
22 'b': lambda: os.path.basename(repo.root),
23 }
24
25 try:
26 if node:
27 expander.update(node_expander)
28 if node and revwidth is not None:
29 expander['r'] = (lambda:
30 str(repo.changelog.rev(node)).zfill(revwidth))
31 if total is not None:
32 expander['N'] = lambda: str(total)
33 if seqno is not None:
34 expander['n'] = lambda: str(seqno)
35 if total is not None and seqno is not None:
36 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
37 if pathname is not None:
38 expander['s'] = lambda: os.path.basename(pathname)
39 expander['d'] = lambda: os.path.dirname(pathname) or '.'
40 expander['p'] = lambda: pathname
41
42 newname = []
43 patlen = len(pat)
44 i = 0
45 while i < patlen:
46 c = pat[i]
47 if c == '%':
48 i += 1
49 c = pat[i]
50 c = expander[c]()
51 newname.append(c)
52 i += 1
53 return ''.join(newname)
54 except KeyError, inst:
55 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
56 inst.args[0])
57
58 def make_file(repo, pat, node=None,
59 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
60 if not pat or pat == '-':
61 return 'w' in mode and sys.stdout or sys.stdin
62 if hasattr(pat, 'write') and 'w' in mode:
63 return pat
64 if hasattr(pat, 'read') and 'r' in mode:
65 return pat
66 return open(make_filename(repo, pat, node, total, seqno, revwidth,
67 pathname),
68 mode)
@@ -32,7 +32,7 b' refresh contents of top applied patch '
32 from mercurial.demandload import *
32 from mercurial.demandload import *
33 from mercurial.i18n import gettext as _
33 from mercurial.i18n import gettext as _
34 demandload(globals(), "os sys re struct traceback errno bz2")
34 demandload(globals(), "os sys re struct traceback errno bz2")
35 demandload(globals(), "mercurial:commands,hg,revlog,ui,util")
35 demandload(globals(), "mercurial:commands,hg,patch,revlog,ui,util")
36
36
37 commands.norepo += " qclone qversion"
37 commands.norepo += " qclone qversion"
38
38
@@ -65,6 +65,7 b' class queue:'
65 self.guards_path = "guards"
65 self.guards_path = "guards"
66 self.active_guards = None
66 self.active_guards = None
67 self.guards_dirty = False
67 self.guards_dirty = False
68 self._diffopts = None
68
69
69 if os.path.exists(self.join(self.series_path)):
70 if os.path.exists(self.join(self.series_path)):
70 self.full_series = self.opener(self.series_path).read().splitlines()
71 self.full_series = self.opener(self.series_path).read().splitlines()
@@ -74,6 +75,11 b' class queue:'
74 lines = self.opener(self.status_path).read().splitlines()
75 lines = self.opener(self.status_path).read().splitlines()
75 self.applied = [statusentry(l) for l in lines]
76 self.applied = [statusentry(l) for l in lines]
76
77
78 def diffopts(self):
79 if self._diffopts is None:
80 self._diffopts = self.ui.diffopts()
81 return self._diffopts
82
77 def join(self, *p):
83 def join(self, *p):
78 return os.path.join(self.path, *p)
84 return os.path.join(self.path, *p)
79
85
@@ -291,6 +297,11 b' class queue:'
291 message.insert(0, subject)
297 message.insert(0, subject)
292 return (message, comments, user, date, diffstart > 1)
298 return (message, comments, user, date, diffstart > 1)
293
299
300 def printdiff(self, repo, node1, node2=None, files=None,
301 fp=None, changes=None, opts=None):
302 patch.diff(repo, node1, node2, files,
303 fp=fp, changes=changes, opts=self.diffopts())
304
294 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
305 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
295 # first try just applying the patch
306 # first try just applying the patch
296 (err, n) = self.apply(repo, [ patch ], update_status=False,
307 (err, n) = self.apply(repo, [ patch ], update_status=False,
@@ -324,7 +335,7 b' class queue:'
324 if comments:
335 if comments:
325 comments = "\n".join(comments) + '\n\n'
336 comments = "\n".join(comments) + '\n\n'
326 patchf.write(comments)
337 patchf.write(comments)
327 commands.dodiff(patchf, self.ui, repo, head, n)
338 self.printdiff(repo, head, n, fp=patchf)
328 patchf.close()
339 patchf.close()
329 return (0, n)
340 return (0, n)
330
341
@@ -918,7 +929,7 b' class queue:'
918 self.ui.write("No patches applied\n")
929 self.ui.write("No patches applied\n")
919 return
930 return
920 qp = self.qparents(repo, top)
931 qp = self.qparents(repo, top)
921 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
932 self.printdiff(repo, qp, files=files)
922
933
923 def refresh(self, repo, msg='', short=False):
934 def refresh(self, repo, msg='', short=False):
924 if len(self.applied) == 0:
935 if len(self.applied) == 0:
@@ -1001,8 +1012,8 b' class queue:'
1001 r = list(util.unique(dd))
1012 r = list(util.unique(dd))
1002 a = list(util.unique(aa))
1013 a = list(util.unique(aa))
1003 filelist = list(util.unique(c + r + a ))
1014 filelist = list(util.unique(c + r + a ))
1004 commands.dodiff(patchf, self.ui, repo, patchparent, None,
1015 self.printdiff(repo, patchparent, files=filelist,
1005 filelist, changes=(c, a, r, [], u))
1016 changes=(c, a, r, [], u), fp=patchf)
1006 patchf.close()
1017 patchf.close()
1007
1018
1008 changes = repo.changelog.read(tip)
1019 changes = repo.changelog.read(tip)
@@ -1025,7 +1036,7 b' class queue:'
1025 self.applied[-1] = statusentry(revlog.hex(n), patch)
1036 self.applied[-1] = statusentry(revlog.hex(n), patch)
1026 self.applied_dirty = 1
1037 self.applied_dirty = 1
1027 else:
1038 else:
1028 commands.dodiff(patchf, self.ui, repo, patchparent, None)
1039 self.printdiff(repo, patchparent, fp=patchf)
1029 patchf.close()
1040 patchf.close()
1030 self.pop(repo, force=True, wlock=wlock)
1041 self.pop(repo, force=True, wlock=wlock)
1031 self.push(repo, force=True, wlock=wlock)
1042 self.push(repo, force=True, wlock=wlock)
@@ -67,7 +67,7 b''
67 from mercurial.demandload import *
67 from mercurial.demandload import *
68 from mercurial.i18n import gettext as _
68 from mercurial.i18n import gettext as _
69 from mercurial.node import *
69 from mercurial.node import *
70 demandload(globals(), 'email.Parser mercurial:commands,templater,util')
70 demandload(globals(), 'email.Parser mercurial:commands,patch,templater,util')
71 demandload(globals(), 'fnmatch socket time')
71 demandload(globals(), 'fnmatch socket time')
72
72
73 # template for single changeset can include email headers.
73 # template for single changeset can include email headers.
@@ -238,7 +238,7 b' class notifier(object):'
238 return
238 return
239 fp = templater.stringio()
239 fp = templater.stringio()
240 prev = self.repo.changelog.parents(node)[0]
240 prev = self.repo.changelog.parents(node)[0]
241 commands.dodiff(fp, self.ui, self.repo, prev, ref)
241 patch.diff(self.repo, fp, prev, ref)
242 difflines = fp.getvalue().splitlines(1)
242 difflines = fp.getvalue().splitlines(1)
243 if maxdiff > 0 and len(difflines) > maxdiff:
243 if maxdiff > 0 and len(difflines) > maxdiff:
244 self.sio.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
244 self.sio.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
@@ -10,10 +10,10 b' from node import *'
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff difflib patch random signal tempfile time")
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival cStringIO changegroup")
15 demandload(globals(), "archival cStringIO changegroup")
16 demandload(globals(), "hgweb.server sshserver")
16 demandload(globals(), "cmdutil hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table."""
19 """Exception raised if command is not in the command table."""
@@ -25,15 +25,6 b' def bail_if_changed(repo):'
25 if modified or added or removed or deleted:
25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes"))
26 raise util.Abort(_("outstanding uncommitted changes"))
27
27
28 def filterfiles(filters, files):
29 l = [x for x in files if x in filters]
30
31 for t in filters:
32 if t and t[-1] != "/":
33 t += "/"
34 l += [x for x in files if x.startswith(t)]
35 return l
36
37 def relpath(repo, args):
28 def relpath(repo, args):
38 cwd = repo.getcwd()
29 cwd = repo.getcwd()
39 if cwd:
30 if cwd:
@@ -344,63 +335,6 b' def revrange(ui, repo, revs):'
344 seen[rev] = 1
335 seen[rev] = 1
345 yield str(rev)
336 yield str(rev)
346
337
347 def make_filename(repo, pat, node,
348 total=None, seqno=None, revwidth=None, pathname=None):
349 node_expander = {
350 'H': lambda: hex(node),
351 'R': lambda: str(repo.changelog.rev(node)),
352 'h': lambda: short(node),
353 }
354 expander = {
355 '%': lambda: '%',
356 'b': lambda: os.path.basename(repo.root),
357 }
358
359 try:
360 if node:
361 expander.update(node_expander)
362 if node and revwidth is not None:
363 expander['r'] = (lambda:
364 str(repo.changelog.rev(node)).zfill(revwidth))
365 if total is not None:
366 expander['N'] = lambda: str(total)
367 if seqno is not None:
368 expander['n'] = lambda: str(seqno)
369 if total is not None and seqno is not None:
370 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
371 if pathname is not None:
372 expander['s'] = lambda: os.path.basename(pathname)
373 expander['d'] = lambda: os.path.dirname(pathname) or '.'
374 expander['p'] = lambda: pathname
375
376 newname = []
377 patlen = len(pat)
378 i = 0
379 while i < patlen:
380 c = pat[i]
381 if c == '%':
382 i += 1
383 c = pat[i]
384 c = expander[c]()
385 newname.append(c)
386 i += 1
387 return ''.join(newname)
388 except KeyError, inst:
389 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
390 inst.args[0])
391
392 def make_file(repo, pat, node=None,
393 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
394 if not pat or pat == '-':
395 return 'w' in mode and sys.stdout or sys.stdin
396 if hasattr(pat, 'write') and 'w' in mode:
397 return pat
398 if hasattr(pat, 'read') and 'r' in mode:
399 return pat
400 return open(make_filename(repo, pat, node, total, seqno, revwidth,
401 pathname),
402 mode)
403
404 def write_bundle(cg, filename=None, compress=True):
338 def write_bundle(cg, filename=None, compress=True):
405 """Write a bundle file and return its filename.
339 """Write a bundle file and return its filename.
406
340
@@ -453,74 +387,6 b' def write_bundle(cg, filename=None, comp'
453 if cleanup is not None:
387 if cleanup is not None:
454 os.unlink(cleanup)
388 os.unlink(cleanup)
455
389
456 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
457 changes=None, text=False, opts={}):
458 if not node1:
459 node1 = repo.dirstate.parents()[0]
460 # reading the data for node1 early allows it to play nicely
461 # with repo.changes and the revlog cache.
462 change = repo.changelog.read(node1)
463 mmap = repo.manifest.read(change[0])
464 date1 = util.datestr(change[2])
465
466 if not changes:
467 changes = repo.changes(node1, node2, files, match=match)
468 modified, added, removed, deleted, unknown = changes
469 if files:
470 modified, added, removed = map(lambda x: filterfiles(files, x),
471 (modified, added, removed))
472
473 if not modified and not added and not removed:
474 return
475
476 if node2:
477 change = repo.changelog.read(node2)
478 mmap2 = repo.manifest.read(change[0])
479 _date2 = util.datestr(change[2])
480 def date2(f):
481 return _date2
482 def read(f):
483 return repo.file(f).read(mmap2[f])
484 else:
485 tz = util.makedate()[1]
486 _date2 = util.datestr()
487 def date2(f):
488 try:
489 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
490 except OSError, err:
491 if err.errno != errno.ENOENT: raise
492 return _date2
493 def read(f):
494 return repo.wread(f)
495
496 if ui.quiet:
497 r = None
498 else:
499 hexfunc = ui.verbose and hex or short
500 r = [hexfunc(node) for node in [node1, node2] if node]
501
502 diffopts = ui.diffopts()
503 showfunc = opts.get('show_function') or diffopts['showfunc']
504 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
505 ignorewsamount = opts.get('ignore_space_change') or \
506 diffopts['ignorewsamount']
507 ignoreblanklines = opts.get('ignore_blank_lines') or \
508 diffopts['ignoreblanklines']
509
510 all = modified + added + removed
511 all.sort()
512 for f in all:
513 to = None
514 tn = None
515 if f in mmap:
516 to = repo.file(f).read(mmap[f])
517 if f not in removed:
518 tn = read(f)
519 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
520 showfunc=showfunc, ignorews=ignorews,
521 ignorewsamount=ignorewsamount,
522 ignoreblanklines=ignoreblanklines))
523
524 def trimuser(ui, name, rev, revcache):
390 def trimuser(ui, name, rev, revcache):
525 """trim the name of the user who committed a change"""
391 """trim the name of the user who committed a change"""
526 user = revcache.get(rev)
392 user = revcache.get(rev)
@@ -922,7 +788,7 b' def archive(ui, repo, dest, **opts):'
922 raise util.Abort(_('uncommitted merge - please provide a '
788 raise util.Abort(_('uncommitted merge - please provide a '
923 'specific revision'))
789 'specific revision'))
924
790
925 dest = make_filename(repo, dest, node)
791 dest = cmdutil.make_filename(repo, dest, node)
926 if os.path.realpath(dest) == repo.root:
792 if os.path.realpath(dest) == repo.root:
927 raise util.Abort(_('repository root cannot be destination'))
793 raise util.Abort(_('repository root cannot be destination'))
928 dummy, matchfn, dummy = matchpats(repo, [], opts)
794 dummy, matchfn, dummy = matchpats(repo, [], opts)
@@ -933,7 +799,7 b' def archive(ui, repo, dest, **opts):'
933 raise util.Abort(_('cannot archive plain files to stdout'))
799 raise util.Abort(_('cannot archive plain files to stdout'))
934 dest = sys.stdout
800 dest = sys.stdout
935 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
801 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
936 prefix = make_filename(repo, prefix, node)
802 prefix = cmdutil.make_filename(repo, prefix, node)
937 archival.archive(repo, dest, node, kind, not opts['no_decode'],
803 archival.archive(repo, dest, node, kind, not opts['no_decode'],
938 matchfn, prefix)
804 matchfn, prefix)
939
805
@@ -1038,7 +904,7 b' def cat(ui, repo, file1, *pats, **opts):'
1038 """
904 """
1039 ctx = repo.changectx(opts['rev'] or "-1")
905 ctx = repo.changectx(opts['rev'] or "-1")
1040 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
906 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
1041 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
907 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
1042 fp.write(ctx.filectx(abs).data())
908 fp.write(ctx.filectx(abs).data())
1043
909
1044 def clone(ui, source, dest=None, **opts):
910 def clone(ui, source, dest=None, **opts):
@@ -1507,35 +1373,8 b' def diff(ui, repo, *pats, **opts):'
1507
1373
1508 fns, matchfn, anypats = matchpats(repo, pats, opts)
1374 fns, matchfn, anypats = matchpats(repo, pats, opts)
1509
1375
1510 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1376 patch.diff(repo, node1, node2, fns, match=matchfn,
1511 text=opts['text'], opts=opts)
1377 opts=ui.diffopts(opts))
1512
1513 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1514 node = repo.lookup(changeset)
1515 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1516 if opts['switch_parent']:
1517 parents.reverse()
1518 prev = (parents and parents[0]) or nullid
1519 change = repo.changelog.read(node)
1520
1521 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1522 revwidth=revwidth)
1523 if fp != sys.stdout:
1524 ui.note("%s\n" % fp.name)
1525
1526 fp.write("# HG changeset patch\n")
1527 fp.write("# User %s\n" % change[1])
1528 fp.write("# Date %d %d\n" % change[2])
1529 fp.write("# Node ID %s\n" % hex(node))
1530 fp.write("# Parent %s\n" % hex(prev))
1531 if len(parents) > 1:
1532 fp.write("# Parent %s\n" % hex(parents[1]))
1533 fp.write(change[4].rstrip())
1534 fp.write("\n\n")
1535
1536 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1537 if fp != sys.stdout:
1538 fp.close()
1539
1378
1540 def export(ui, repo, *changesets, **opts):
1379 def export(ui, repo, *changesets, **opts):
1541 """dump the header and diffs for one or more changesets
1380 """dump the header and diffs for one or more changesets
@@ -1566,15 +1405,13 b' def export(ui, repo, *changesets, **opts'
1566 """
1405 """
1567 if not changesets:
1406 if not changesets:
1568 raise util.Abort(_("export requires at least one changeset"))
1407 raise util.Abort(_("export requires at least one changeset"))
1569 seqno = 0
1570 revs = list(revrange(ui, repo, changesets))
1408 revs = list(revrange(ui, repo, changesets))
1571 total = len(revs)
1409 if len(revs) > 1:
1572 revwidth = max(map(len, revs))
1410 ui.note(_('exporting patches:\n'))
1573 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1411 else:
1574 ui.note(msg)
1412 ui.note(_('exporting patch:\n'))
1575 for cset in revs:
1413 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1576 seqno += 1
1414 switch_parent=opts['switch_parent'], opts=ui.diffopts(opts))
1577 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1578
1415
1579 def forget(ui, repo, *pats, **opts):
1416 def forget(ui, repo, *pats, **opts):
1580 """don't add the specified files on the next commit (DEPRECATED)
1417 """don't add the specified files on the next commit (DEPRECATED)
@@ -1963,7 +1800,7 b' def incoming(ui, repo, source="default",'
1963 displayer.show(changenode=n)
1800 displayer.show(changenode=n)
1964 if opts['patch']:
1801 if opts['patch']:
1965 prev = (parents and parents[0]) or nullid
1802 prev = (parents and parents[0]) or nullid
1966 dodiff(ui, ui, other, prev, n)
1803 patch.diff(repo, other, prev, n)
1967 ui.write("\n")
1804 ui.write("\n")
1968 finally:
1805 finally:
1969 if hasattr(other, 'close'):
1806 if hasattr(other, 'close'):
@@ -2114,7 +1951,7 b' def log(ui, repo, *pats, **opts):'
2114 displayer.show(rev, brinfo=br)
1951 displayer.show(rev, brinfo=br)
2115 if opts['patch']:
1952 if opts['patch']:
2116 prev = (parents and parents[0]) or nullid
1953 prev = (parents and parents[0]) or nullid
2117 dodiff(du, du, repo, prev, changenode, match=matchfn)
1954 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
2118 du.write("\n\n")
1955 du.write("\n\n")
2119 elif st == 'iter':
1956 elif st == 'iter':
2120 if count == limit: break
1957 if count == limit: break
@@ -2195,7 +2032,7 b' def outgoing(ui, repo, dest=None, **opts'
2195 displayer.show(changenode=n)
2032 displayer.show(changenode=n)
2196 if opts['patch']:
2033 if opts['patch']:
2197 prev = (parents and parents[0]) or nullid
2034 prev = (parents and parents[0]) or nullid
2198 dodiff(ui, ui, repo, prev, n)
2035 patch.diff(repo, prev, n)
2199 ui.write("\n")
2036 ui.write("\n")
2200
2037
2201 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2038 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
@@ -2843,7 +2680,7 b' def tip(ui, repo, **opts):'
2843 br = repo.branchlookup([n])
2680 br = repo.branchlookup([n])
2844 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2681 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2845 if opts['patch']:
2682 if opts['patch']:
2846 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2683 patch.diff(repo, repo.changelog.parents(n)[0], n)
2847
2684
2848 def unbundle(ui, repo, fname, **opts):
2685 def unbundle(ui, repo, fname, **opts):
2849 """apply a changegroup file
2686 """apply a changegroup file
@@ -134,32 +134,22 b' class hgweb(object):'
134 modified, added, removed = map(lambda x: filterfiles(files, x),
134 modified, added, removed = map(lambda x: filterfiles(files, x),
135 (modified, added, removed))
135 (modified, added, removed))
136
136
137 diffopts = self.repo.ui.diffopts()
137 diffopts = ui.diffopts()
138 showfunc = diffopts['showfunc']
139 ignorews = diffopts['ignorews']
140 ignorewsamount = diffopts['ignorewsamount']
141 ignoreblanklines = diffopts['ignoreblanklines']
142 for f in modified:
138 for f in modified:
143 to = r.file(f).read(mmap1[f])
139 to = r.file(f).read(mmap1[f])
144 tn = r.file(f).read(mmap2[f])
140 tn = r.file(f).read(mmap2[f])
145 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
141 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
146 showfunc=showfunc, ignorews=ignorews,
142 opts=diffopts), f, tn)
147 ignorewsamount=ignorewsamount,
148 ignoreblanklines=ignoreblanklines), f, tn)
149 for f in added:
143 for f in added:
150 to = None
144 to = None
151 tn = r.file(f).read(mmap2[f])
145 tn = r.file(f).read(mmap2[f])
152 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
146 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
153 showfunc=showfunc, ignorews=ignorews,
147 opts=diffopts), f, tn)
154 ignorewsamount=ignorewsamount,
155 ignoreblanklines=ignoreblanklines), f, tn)
156 for f in removed:
148 for f in removed:
157 to = r.file(f).read(mmap1[f])
149 to = r.file(f).read(mmap1[f])
158 tn = None
150 tn = None
159 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
151 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
160 showfunc=showfunc, ignorews=ignorews,
152 opts=diffopts), f, tn)
161 ignorewsamount=ignorewsamount,
162 ignoreblanklines=ignoreblanklines), f, tn)
163
153
164 def changelog(self, pos, shortlog=False):
154 def changelog(self, pos, shortlog=False):
165 def changenav(**map):
155 def changenav(**map):
@@ -19,14 +19,39 b' def splitnewlines(text):'
19 lines[-1] = lines[-1][:-1]
19 lines[-1] = lines[-1][:-1]
20 return lines
20 return lines
21
21
22 def unidiff(a, ad, b, bd, fn, r=None, text=False,
22 class diffopts(object):
23 showfunc=False, ignorews=False, ignorewsamount=False,
23 '''context is the number of context lines
24 ignoreblanklines=False):
24 text treats all files as text
25 showfunc enables diff -p output
26 ignorews ignores all whitespace changes in the diff
27 ignorewsamount ignores changes in the amount of whitespace
28 ignoreblanklines ignores changes whose lines are all blank'''
25
29
30 defaults = {
31 'context': 3,
32 'text': False,
33 'showfunc': True,
34 'ignorews': False,
35 'ignorewsamount': False,
36 'ignoreblanklines': False,
37 }
38
39 __slots__ = defaults.keys()
40
41 def __init__(self, **opts):
42 for k in self.__slots__:
43 v = opts.get(k)
44 if v is None:
45 v = self.defaults[k]
46 setattr(self, k, v)
47
48 defaultopts = diffopts()
49
50 def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts):
26 if not a and not b: return ""
51 if not a and not b: return ""
27 epoch = util.datestr((0, 0))
52 epoch = util.datestr((0, 0))
28
53
29 if not text and (util.binary(a) or util.binary(b)):
54 if not opts.text and (util.binary(a) or util.binary(b)):
30 l = ['Binary file %s has changed\n' % fn]
55 l = ['Binary file %s has changed\n' % fn]
31 elif not a:
56 elif not a:
32 b = splitnewlines(b)
57 b = splitnewlines(b)
@@ -49,10 +74,7 b' def unidiff(a, ad, b, bd, fn, r=None, te'
49 else:
74 else:
50 al = splitnewlines(a)
75 al = splitnewlines(a)
51 bl = splitnewlines(b)
76 bl = splitnewlines(b)
52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn,
77 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, opts=opts))
53 showfunc=showfunc, ignorews=ignorews,
54 ignorewsamount=ignorewsamount,
55 ignoreblanklines=ignoreblanklines))
56 if not l: return ""
78 if not l: return ""
57 # difflib uses a space, rather than a tab
79 # difflib uses a space, rather than a tab
58 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
80 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
@@ -72,21 +94,15 b' def unidiff(a, ad, b, bd, fn, r=None, te'
72 # t1 and t2 are the text to be diffed
94 # t1 and t2 are the text to be diffed
73 # l1 and l2 are the text broken up into lines
95 # l1 and l2 are the text broken up into lines
74 # header1 and header2 are the filenames for the diff output
96 # header1 and header2 are the filenames for the diff output
75 # context is the number of context lines
97 def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
76 # showfunc enables diff -p output
77 # ignorews ignores all whitespace changes in the diff
78 # ignorewsamount ignores changes in the amount of whitespace
79 # ignoreblanklines ignores changes whose lines are all blank
80 def bunidiff(t1, t2, l1, l2, header1, header2, context=3, showfunc=False,
81 ignorews=False, ignorewsamount=False, ignoreblanklines=False):
82 def contextend(l, len):
98 def contextend(l, len):
83 ret = l + context
99 ret = l + opts.context
84 if ret > len:
100 if ret > len:
85 ret = len
101 ret = len
86 return ret
102 return ret
87
103
88 def contextstart(l):
104 def contextstart(l):
89 ret = l - context
105 ret = l - opts.context
90 if ret < 0:
106 if ret < 0:
91 return 0
107 return 0
92 return ret
108 return ret
@@ -101,7 +117,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
101 blen = b2 - bstart + aend - a2
117 blen = b2 - bstart + aend - a2
102
118
103 func = ""
119 func = ""
104 if showfunc:
120 if opts.showfunc:
105 # walk backwards from the start of the context
121 # walk backwards from the start of the context
106 # to find a line starting with an alphanumeric char.
122 # to find a line starting with an alphanumeric char.
107 for x in xrange(astart, -1, -1):
123 for x in xrange(astart, -1, -1):
@@ -119,14 +135,14 b' def bunidiff(t1, t2, l1, l2, header1, he'
119
135
120 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
136 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ]
121
137
122 if showfunc:
138 if opts.showfunc:
123 funcre = re.compile('\w')
139 funcre = re.compile('\w')
124 if ignorewsamount:
140 if opts.ignorewsamount:
125 wsamountre = re.compile('[ \t]+')
141 wsamountre = re.compile('[ \t]+')
126 wsappendedre = re.compile(' \n')
142 wsappendedre = re.compile(' \n')
127 if ignoreblanklines:
143 if opts.ignoreblanklines:
128 wsblanklinesre = re.compile('\n')
144 wsblanklinesre = re.compile('\n')
129 if ignorews:
145 if opts.ignorews:
130 wsre = re.compile('[ \t]')
146 wsre = re.compile('[ \t]')
131
147
132 # bdiff.blocks gives us the matching sequences in the files. The loop
148 # bdiff.blocks gives us the matching sequences in the files. The loop
@@ -159,13 +175,13 b' def bunidiff(t1, t2, l1, l2, header1, he'
159 if not old and not new:
175 if not old and not new:
160 continue
176 continue
161
177
162 if ignoreblanklines:
178 if opts.ignoreblanklines:
163 wsold = wsblanklinesre.sub('', "".join(old))
179 wsold = wsblanklinesre.sub('', "".join(old))
164 wsnew = wsblanklinesre.sub('', "".join(new))
180 wsnew = wsblanklinesre.sub('', "".join(new))
165 if wsold == wsnew:
181 if wsold == wsnew:
166 continue
182 continue
167
183
168 if ignorewsamount:
184 if opts.ignorewsamount:
169 wsold = wsamountre.sub(' ', "".join(old))
185 wsold = wsamountre.sub(' ', "".join(old))
170 wsold = wsappendedre.sub('\n', wsold)
186 wsold = wsappendedre.sub('\n', wsold)
171 wsnew = wsamountre.sub(' ', "".join(new))
187 wsnew = wsamountre.sub(' ', "".join(new))
@@ -173,7 +189,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
173 if wsold == wsnew:
189 if wsold == wsnew:
174 continue
190 continue
175
191
176 if ignorews:
192 if opts.ignorews:
177 wsold = wsre.sub('', "".join(old))
193 wsold = wsre.sub('', "".join(old))
178 wsnew = wsre.sub('', "".join(new))
194 wsnew = wsre.sub('', "".join(new))
179 if wsold == wsnew:
195 if wsold == wsnew:
@@ -184,7 +200,7 b' def bunidiff(t1, t2, l1, l2, header1, he'
184 prev = None
200 prev = None
185 if hunk:
201 if hunk:
186 # join with the previous hunk if it falls inside the context
202 # join with the previous hunk if it falls inside the context
187 if astart < hunk[1] + context + 1:
203 if astart < hunk[1] + opts.context + 1:
188 prev = hunk
204 prev = hunk
189 astart = hunk[1]
205 astart = hunk[1]
190 bstart = hunk[3]
206 bstart = hunk[3]
@@ -7,8 +7,9 b''
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from i18n import gettext as _
9 from i18n import gettext as _
10 demandload(globals(), "util")
10 from node import *
11 demandload(globals(), "cStringIO email.Parser os re shutil tempfile")
11 demandload(globals(), "cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser os re shutil sys tempfile")
12
13
13 def extract(ui, fileobj):
14 def extract(ui, fileobj):
14 '''extract patch from data read from fileobj.
15 '''extract patch from data read from fileobj.
@@ -249,3 +250,117 b' def patch(strip, patchname, ui, cwd=None'
249 files[gp.path] = (gp.op, gp)
250 files[gp.path] = (gp.op, gp)
250
251
251 return files
252 return files
253
254 def diff(repo, node1=None, node2=None, files=None, match=util.always,
255 fp=None, changes=None, opts=None):
256 '''print diff of changes to files between two nodes, or node and
257 working directory.
258
259 if node1 is None, use first dirstate parent instead.
260 if node2 is None, compare node1 with working directory.'''
261
262 if opts is None:
263 opts = mdiff.defaultopts
264 if fp is None:
265 fp = repo.ui
266
267 if not node1:
268 node1 = repo.dirstate.parents()[0]
269 # reading the data for node1 early allows it to play nicely
270 # with repo.changes and the revlog cache.
271 change = repo.changelog.read(node1)
272 mmap = repo.manifest.read(change[0])
273 date1 = util.datestr(change[2])
274
275 if not changes:
276 changes = repo.changes(node1, node2, files, match=match)
277 modified, added, removed, deleted, unknown = changes
278 if files:
279 def filterfiles(filters):
280 l = [x for x in files if x in filters]
281
282 for t in filters:
283 if t and t[-1] != "/":
284 t += "/"
285 l += [x for x in files if x.startswith(t)]
286 return l
287
288 modified, added, removed = map(lambda x: filterfiles(x),
289 (modified, added, removed))
290
291 if not modified and not added and not removed:
292 return
293
294 if node2:
295 change = repo.changelog.read(node2)
296 mmap2 = repo.manifest.read(change[0])
297 _date2 = util.datestr(change[2])
298 def date2(f):
299 return _date2
300 def read(f):
301 return repo.file(f).read(mmap2[f])
302 else:
303 tz = util.makedate()[1]
304 _date2 = util.datestr()
305 def date2(f):
306 try:
307 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
308 except OSError, err:
309 if err.errno != errno.ENOENT: raise
310 return _date2
311 def read(f):
312 return repo.wread(f)
313
314 if repo.ui.quiet:
315 r = None
316 else:
317 hexfunc = repo.ui.verbose and hex or short
318 r = [hexfunc(node) for node in [node1, node2] if node]
319
320 all = modified + added + removed
321 all.sort()
322 for f in all:
323 to = None
324 tn = None
325 if f in mmap:
326 to = repo.file(f).read(mmap[f])
327 if f not in removed:
328 tn = read(f)
329 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
330
331 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
332 opts=None):
333 '''export changesets as hg patches.'''
334
335 total = len(revs)
336 revwidth = max(map(len, revs))
337
338 def single(node, seqno, fp):
339 parents = [p for p in repo.changelog.parents(node) if p != nullid]
340 if switch_parent:
341 parents.reverse()
342 prev = (parents and parents[0]) or nullid
343 change = repo.changelog.read(node)
344
345 if not fp:
346 fp = cmdutil.make_file(repo, template, node, total=total,
347 seqno=seqno, revwidth=revwidth)
348 if fp not in (sys.stdout, repo.ui):
349 repo.ui.note("%s\n" % fp.name)
350
351 fp.write("# HG changeset patch\n")
352 fp.write("# User %s\n" % change[1])
353 fp.write("# Date %d %d\n" % change[2])
354 fp.write("# Node ID %s\n" % hex(node))
355 fp.write("# Parent %s\n" % hex(prev))
356 if len(parents) > 1:
357 fp.write("# Parent %s\n" % hex(parents[1]))
358 fp.write(change[4].rstrip())
359 fp.write("\n\n")
360
361 diff(repo, prev, node, fp=fp, opts=opts)
362 if fp not in (sys.stdout, repo.ui):
363 fp.close()
364
365 for seqno, cset in enumerate(revs):
366 single(cset, seqno, fp)
@@ -8,7 +8,7 b''
8 from i18n import gettext as _
8 from i18n import gettext as _
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
11 demandload(globals(), "ConfigParser templater traceback util")
11 demandload(globals(), "ConfigParser mdiff templater traceback util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
@@ -169,16 +169,17 b' class ui(object):'
169 result[key.lower()] = value
169 result[key.lower()] = value
170 return result
170 return result
171
171
172 def diffopts(self):
172 def diffopts(self, opts={}):
173 if self.diffcache:
173 return mdiff.diffopts(
174 return self.diffcache
174 text=opts.get('text'),
175 result = {'showfunc': True, 'ignorews': False,
175 showfunc=(opts.get('show_function') or
176 'ignorewsamount': False, 'ignoreblanklines': False}
176 self.configbool('diff', 'showfunc', None)),
177 for key, value in self.configitems("diff"):
177 ignorews=(opts.get('ignore_all_space') or
178 if value:
178 self.configbool('diff', 'ignorews', None)),
179 result[key.lower()] = (value.lower() == 'true')
179 ignorewsamount=(opts.get('ignore_space_change') or
180 self.diffcache = result
180 self.configbool('diff', 'ignorewsamount', None)),
181 return result
181 ignoreblanklines=(opts.get('ignore_blank_lines') or
182 self.configbool('diff', 'ignoreblanklines', None)))
182
183
183 def username(self):
184 def username(self):
184 """Return default username to be used in commits.
185 """Return default username to be used in commits.
General Comments 0
You need to be logged in to leave comments. Login now