Mercurial > hg > mercurial-crew-with-dirclash
comparison mercurial/mdiff.py @ 3044:fcadf7a32425
Merge with mpm
author | Josef "Jeff" Sipek <jeffpc@josefsipek.net> |
---|---|
date | Sun, 03 Sep 2006 06:06:02 -0400 |
parents | 8b02af865990 |
children | d838bfac668d |
comparison
equal
deleted
inserted
replaced
3043:2a4d4aecb2b4 | 3044:fcadf7a32425 |
---|---|
1 # mdiff.py - diff and patch routines for mercurial | 1 # mdiff.py - diff and patch routines for mercurial |
2 # | 2 # |
3 # Copyright 2005 Matt Mackall <mpm@selenic.com> | 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> |
4 # | 4 # |
5 # This software may be used and distributed according to the terms | 5 # This software may be used and distributed according to the terms |
6 # of the GNU General Public License, incorporated herein by reference. | 6 # of the GNU General Public License, incorporated herein by reference. |
7 | 7 |
8 from demandload import demandload | 8 from demandload import demandload |
17 lines.pop() | 17 lines.pop() |
18 else: | 18 else: |
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 | 25 showfunc enables diff -p output |
26 git enables the git extended patch format | |
27 ignorews ignores all whitespace changes in the diff | |
28 ignorewsamount ignores changes in the amount of whitespace | |
29 ignoreblanklines ignores changes whose lines are all blank''' | |
30 | |
31 defaults = { | |
32 'context': 3, | |
33 'text': False, | |
34 'showfunc': True, | |
35 'git': False, | |
36 'ignorews': False, | |
37 'ignorewsamount': False, | |
38 'ignoreblanklines': False, | |
39 } | |
40 | |
41 __slots__ = defaults.keys() | |
42 | |
43 def __init__(self, **opts): | |
44 for k in self.__slots__: | |
45 v = opts.get(k) | |
46 if v is None: | |
47 v = self.defaults[k] | |
48 setattr(self, k, v) | |
49 | |
50 defaultopts = diffopts() | |
51 | |
52 def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts): | |
26 if not a and not b: return "" | 53 if not a and not b: return "" |
27 epoch = util.datestr((0, 0)) | 54 epoch = util.datestr((0, 0)) |
28 | 55 |
29 if not text and (util.binary(a) or util.binary(b)): | 56 if not opts.text and (util.binary(a) or util.binary(b)): |
30 l = ['Binary file %s has changed\n' % fn] | 57 l = ['Binary file %s has changed\n' % fn] |
31 elif not a: | 58 elif not a: |
32 b = splitnewlines(b) | 59 b = splitnewlines(b) |
33 if a is None: | 60 if a is None: |
34 l1 = "--- %s\t%s\n" % ("/dev/null", epoch) | 61 l1 = "--- %s\t%s\n" % ("/dev/null", epoch) |
47 l3 = "@@ -1,%d +0,0 @@\n" % len(a) | 74 l3 = "@@ -1,%d +0,0 @@\n" % len(a) |
48 l = [l1, l2, l3] + ["-" + e for e in a] | 75 l = [l1, l2, l3] + ["-" + e for e in a] |
49 else: | 76 else: |
50 al = splitnewlines(a) | 77 al = splitnewlines(a) |
51 bl = splitnewlines(b) | 78 bl = splitnewlines(b) |
52 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, | 79 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 "" | 80 if not l: return "" |
57 # difflib uses a space, rather than a tab | 81 # difflib uses a space, rather than a tab |
58 l[0] = "%s\t%s\n" % (l[0][:-2], ad) | 82 l[0] = "%s\t%s\n" % (l[0][:-2], ad) |
59 l[1] = "%s\t%s\n" % (l[1][:-2], bd) | 83 l[1] = "%s\t%s\n" % (l[1][:-2], bd) |
60 | 84 |
70 | 94 |
71 # somewhat self contained replacement for difflib.unified_diff | 95 # somewhat self contained replacement for difflib.unified_diff |
72 # t1 and t2 are the text to be diffed | 96 # t1 and t2 are the text to be diffed |
73 # l1 and l2 are the text broken up into lines | 97 # l1 and l2 are the text broken up into lines |
74 # header1 and header2 are the filenames for the diff output | 98 # header1 and header2 are the filenames for the diff output |
75 # context is the number of context lines | 99 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): | 100 def contextend(l, len): |
83 ret = l + context | 101 ret = l + opts.context |
84 if ret > len: | 102 if ret > len: |
85 ret = len | 103 ret = len |
86 return ret | 104 return ret |
87 | 105 |
88 def contextstart(l): | 106 def contextstart(l): |
89 ret = l - context | 107 ret = l - opts.context |
90 if ret < 0: | 108 if ret < 0: |
91 return 0 | 109 return 0 |
92 return ret | 110 return ret |
93 | 111 |
94 def yieldhunk(hunk, header): | 112 def yieldhunk(hunk, header): |
99 aend = contextend(a2, len(l1)) | 117 aend = contextend(a2, len(l1)) |
100 alen = aend - astart | 118 alen = aend - astart |
101 blen = b2 - bstart + aend - a2 | 119 blen = b2 - bstart + aend - a2 |
102 | 120 |
103 func = "" | 121 func = "" |
104 if showfunc: | 122 if opts.showfunc: |
105 # walk backwards from the start of the context | 123 # walk backwards from the start of the context |
106 # to find a line starting with an alphanumeric char. | 124 # to find a line starting with an alphanumeric char. |
107 for x in xrange(astart, -1, -1): | 125 for x in xrange(astart, -1, -1): |
108 t = l1[x].rstrip() | 126 t = l1[x].rstrip() |
109 if funcre.match(t): | 127 if funcre.match(t): |
117 for x in xrange(a2, aend): | 135 for x in xrange(a2, aend): |
118 yield ' ' + l1[x] | 136 yield ' ' + l1[x] |
119 | 137 |
120 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ] | 138 header = [ "--- %s\t\n" % header1, "+++ %s\t\n" % header2 ] |
121 | 139 |
122 if showfunc: | 140 if opts.showfunc: |
123 funcre = re.compile('\w') | 141 funcre = re.compile('\w') |
124 if ignorewsamount: | 142 if opts.ignorewsamount: |
125 wsamountre = re.compile('[ \t]+') | 143 wsamountre = re.compile('[ \t]+') |
126 wsappendedre = re.compile(' \n') | 144 wsappendedre = re.compile(' \n') |
127 if ignoreblanklines: | 145 if opts.ignoreblanklines: |
128 wsblanklinesre = re.compile('\n') | 146 wsblanklinesre = re.compile('\n') |
129 if ignorews: | 147 if opts.ignorews: |
130 wsre = re.compile('[ \t]') | 148 wsre = re.compile('[ \t]') |
131 | 149 |
132 # bdiff.blocks gives us the matching sequences in the files. The loop | 150 # bdiff.blocks gives us the matching sequences in the files. The loop |
133 # below finds the spaces between those matching sequences and translates | 151 # below finds the spaces between those matching sequences and translates |
134 # them into diff output. | 152 # them into diff output. |
157 # bdiff sometimes gives huge matches past eof, this check eats them, | 175 # bdiff sometimes gives huge matches past eof, this check eats them, |
158 # and deals with the special first match case described above | 176 # and deals with the special first match case described above |
159 if not old and not new: | 177 if not old and not new: |
160 continue | 178 continue |
161 | 179 |
162 if ignoreblanklines: | 180 if opts.ignoreblanklines: |
163 wsold = wsblanklinesre.sub('', "".join(old)) | 181 wsold = wsblanklinesre.sub('', "".join(old)) |
164 wsnew = wsblanklinesre.sub('', "".join(new)) | 182 wsnew = wsblanklinesre.sub('', "".join(new)) |
165 if wsold == wsnew: | 183 if wsold == wsnew: |
166 continue | 184 continue |
167 | 185 |
168 if ignorewsamount: | 186 if opts.ignorewsamount: |
169 wsold = wsamountre.sub(' ', "".join(old)) | 187 wsold = wsamountre.sub(' ', "".join(old)) |
170 wsold = wsappendedre.sub('\n', wsold) | 188 wsold = wsappendedre.sub('\n', wsold) |
171 wsnew = wsamountre.sub(' ', "".join(new)) | 189 wsnew = wsamountre.sub(' ', "".join(new)) |
172 wsnew = wsappendedre.sub('\n', wsnew) | 190 wsnew = wsappendedre.sub('\n', wsnew) |
173 if wsold == wsnew: | 191 if wsold == wsnew: |
174 continue | 192 continue |
175 | 193 |
176 if ignorews: | 194 if opts.ignorews: |
177 wsold = wsre.sub('', "".join(old)) | 195 wsold = wsre.sub('', "".join(old)) |
178 wsnew = wsre.sub('', "".join(new)) | 196 wsnew = wsre.sub('', "".join(new)) |
179 if wsold == wsnew: | 197 if wsold == wsnew: |
180 continue | 198 continue |
181 | 199 |
182 astart = contextstart(a1) | 200 astart = contextstart(a1) |
183 bstart = contextstart(b1) | 201 bstart = contextstart(b1) |
184 prev = None | 202 prev = None |
185 if hunk: | 203 if hunk: |
186 # join with the previous hunk if it falls inside the context | 204 # join with the previous hunk if it falls inside the context |
187 if astart < hunk[1] + context + 1: | 205 if astart < hunk[1] + opts.context + 1: |
188 prev = hunk | 206 prev = hunk |
189 astart = hunk[1] | 207 astart = hunk[1] |
190 bstart = hunk[3] | 208 bstart = hunk[3] |
191 else: | 209 else: |
192 for x in yieldhunk(hunk, header): | 210 for x in yieldhunk(hunk, header): |