Show More
@@ -0,0 +1,46 b'' | |||
|
1 | # Extension dedicated to test patch.diff() upgrade modes | |
|
2 | # | |
|
3 | # | |
|
4 | from mercurial import cmdutil, patch, util | |
|
5 | ||
|
6 | def autodiff(ui, repo, *pats, **opts): | |
|
7 | diffopts = patch.diffopts(ui, opts) | |
|
8 | git = opts.get('git', 'no') | |
|
9 | brokenfiles = set() | |
|
10 | losedatafn = None | |
|
11 | if git in ('yes', 'no'): | |
|
12 | diffopts.git = git == 'yes' | |
|
13 | diffopts.upgrade = False | |
|
14 | elif git == 'auto': | |
|
15 | diffopts.git = False | |
|
16 | diffopts.upgrade = True | |
|
17 | elif git == 'warn': | |
|
18 | diffopts.git = False | |
|
19 | diffopts.upgrade = True | |
|
20 | def losedatafn(fn=None, **kwargs): | |
|
21 | brokenfiles.add(fn) | |
|
22 | return True | |
|
23 | elif git == 'abort': | |
|
24 | diffopts.git = False | |
|
25 | diffopts.upgrade = True | |
|
26 | def losedatafn(fn=None, **kwargs): | |
|
27 | raise util.Abort('losing data for %s' % fn) | |
|
28 | else: | |
|
29 | raise util.Abort('--git must be yes, no or auto') | |
|
30 | ||
|
31 | node1, node2 = cmdutil.revpair(repo, []) | |
|
32 | m = cmdutil.match(repo, pats, opts) | |
|
33 | it = patch.diff(repo, node1, node2, match=m, opts=diffopts, | |
|
34 | losedatafn=losedatafn) | |
|
35 | for chunk in it: | |
|
36 | ui.write(chunk) | |
|
37 | for fn in sorted(brokenfiles): | |
|
38 | ui.write('data lost for: %s\n' % fn) | |
|
39 | ||
|
40 | cmdtable = { | |
|
41 | "autodiff": | |
|
42 | (autodiff, | |
|
43 | [('', 'git', '', 'git upgrade mode (yes/no/auto/warn/abort)'), | |
|
44 | ], | |
|
45 | '[OPTION]... [FILE]...'), | |
|
46 | } |
@@ -0,0 +1,63 b'' | |||
|
1 | #!/bin/sh | |
|
2 | ||
|
3 | echo "[extensions]" >> $HGRCPATH | |
|
4 | echo "autodiff=$TESTDIR/autodiff.py" >> $HGRCPATH | |
|
5 | echo "[diff]" >> $HGRCPATH | |
|
6 | echo "nodates=1" >> $HGRCPATH | |
|
7 | ||
|
8 | hg init repo | |
|
9 | cd repo | |
|
10 | echo '% make a combination of new, changed and deleted file' | |
|
11 | echo regular > regular | |
|
12 | echo rmregular > rmregular | |
|
13 | touch rmempty | |
|
14 | echo exec > exec | |
|
15 | chmod +x exec | |
|
16 | echo rmexec > rmexec | |
|
17 | chmod +x rmexec | |
|
18 | echo setexec > setexec | |
|
19 | echo unsetexec > unsetexec | |
|
20 | chmod +x unsetexec | |
|
21 | echo binary > binary | |
|
22 | python -c "file('rmbinary', 'wb').write('\0')" | |
|
23 | hg ci -Am addfiles | |
|
24 | echo regular >> regular | |
|
25 | echo newregular >> newregular | |
|
26 | rm rmempty | |
|
27 | touch newempty | |
|
28 | rm rmregular | |
|
29 | echo exec >> exec | |
|
30 | echo newexec > newexec | |
|
31 | chmod +x newexec | |
|
32 | rm rmexec | |
|
33 | chmod +x setexec | |
|
34 | chmod -x unsetexec | |
|
35 | python -c "file('binary', 'wb').write('\0\0')" | |
|
36 | python -c "file('newbinary', 'wb').write('\0')" | |
|
37 | rm rmbinary | |
|
38 | hg addremove | |
|
39 | ||
|
40 | echo '% git=no: regular diff for all files' | |
|
41 | hg autodiff --git=no | |
|
42 | ||
|
43 | echo '% git=no: git diff for single regular file' | |
|
44 | hg autodiff --git=yes regular | |
|
45 | ||
|
46 | echo '% git=auto: regular diff for regular files and removals' | |
|
47 | hg autodiff --git=auto regular newregular rmregular rmbinary rmexec | |
|
48 | ||
|
49 | for f in exec newexec setexec unsetexec binary newbinary newempty rmempty; do | |
|
50 | echo '% git=auto: git diff for' $f | |
|
51 | hg autodiff --git=auto $f | |
|
52 | done | |
|
53 | ||
|
54 | echo '% git=warn: regular diff with data loss warnings' | |
|
55 | hg autodiff --git=warn | |
|
56 | ||
|
57 | echo '% git=abort: fail on execute bit change' | |
|
58 | hg autodiff --git=abort regular setexec | |
|
59 | ||
|
60 | echo '% git=abort: succeed on regular file' | |
|
61 | hg autodiff --git=abort regular | |
|
62 | ||
|
63 | cd .. |
@@ -0,0 +1,186 b'' | |||
|
1 | % make a combination of new, changed and deleted file | |
|
2 | adding binary | |
|
3 | adding exec | |
|
4 | adding regular | |
|
5 | adding rmbinary | |
|
6 | adding rmempty | |
|
7 | adding rmexec | |
|
8 | adding rmregular | |
|
9 | adding setexec | |
|
10 | adding unsetexec | |
|
11 | adding newbinary | |
|
12 | adding newempty | |
|
13 | adding newexec | |
|
14 | adding newregular | |
|
15 | removing rmbinary | |
|
16 | removing rmempty | |
|
17 | removing rmexec | |
|
18 | removing rmregular | |
|
19 | % git=no: regular diff for all files | |
|
20 | diff -r b3f053cd7c7f binary | |
|
21 | Binary file binary has changed | |
|
22 | diff -r b3f053cd7c7f exec | |
|
23 | --- a/exec | |
|
24 | +++ b/exec | |
|
25 | @@ -1,1 +1,2 @@ | |
|
26 | exec | |
|
27 | +exec | |
|
28 | diff -r b3f053cd7c7f newbinary | |
|
29 | Binary file newbinary has changed | |
|
30 | diff -r b3f053cd7c7f newexec | |
|
31 | --- /dev/null | |
|
32 | +++ b/newexec | |
|
33 | @@ -0,0 +1,1 @@ | |
|
34 | +newexec | |
|
35 | diff -r b3f053cd7c7f newregular | |
|
36 | --- /dev/null | |
|
37 | +++ b/newregular | |
|
38 | @@ -0,0 +1,1 @@ | |
|
39 | +newregular | |
|
40 | diff -r b3f053cd7c7f regular | |
|
41 | --- a/regular | |
|
42 | +++ b/regular | |
|
43 | @@ -1,1 +1,2 @@ | |
|
44 | regular | |
|
45 | +regular | |
|
46 | diff -r b3f053cd7c7f rmbinary | |
|
47 | Binary file rmbinary has changed | |
|
48 | diff -r b3f053cd7c7f rmexec | |
|
49 | --- a/rmexec | |
|
50 | +++ /dev/null | |
|
51 | @@ -1,1 +0,0 @@ | |
|
52 | -rmexec | |
|
53 | diff -r b3f053cd7c7f rmregular | |
|
54 | --- a/rmregular | |
|
55 | +++ /dev/null | |
|
56 | @@ -1,1 +0,0 @@ | |
|
57 | -rmregular | |
|
58 | % git=no: git diff for single regular file | |
|
59 | diff --git a/regular b/regular | |
|
60 | --- a/regular | |
|
61 | +++ b/regular | |
|
62 | @@ -1,1 +1,2 @@ | |
|
63 | regular | |
|
64 | +regular | |
|
65 | % git=auto: regular diff for regular files and removals | |
|
66 | diff -r b3f053cd7c7f newregular | |
|
67 | --- /dev/null | |
|
68 | +++ b/newregular | |
|
69 | @@ -0,0 +1,1 @@ | |
|
70 | +newregular | |
|
71 | diff -r b3f053cd7c7f regular | |
|
72 | --- a/regular | |
|
73 | +++ b/regular | |
|
74 | @@ -1,1 +1,2 @@ | |
|
75 | regular | |
|
76 | +regular | |
|
77 | diff -r b3f053cd7c7f rmbinary | |
|
78 | Binary file rmbinary has changed | |
|
79 | diff -r b3f053cd7c7f rmexec | |
|
80 | --- a/rmexec | |
|
81 | +++ /dev/null | |
|
82 | @@ -1,1 +0,0 @@ | |
|
83 | -rmexec | |
|
84 | diff -r b3f053cd7c7f rmregular | |
|
85 | --- a/rmregular | |
|
86 | +++ /dev/null | |
|
87 | @@ -1,1 +0,0 @@ | |
|
88 | -rmregular | |
|
89 | % git=auto: git diff for exec | |
|
90 | diff -r b3f053cd7c7f exec | |
|
91 | --- a/exec | |
|
92 | +++ b/exec | |
|
93 | @@ -1,1 +1,2 @@ | |
|
94 | exec | |
|
95 | +exec | |
|
96 | % git=auto: git diff for newexec | |
|
97 | diff --git a/newexec b/newexec | |
|
98 | new file mode 100755 | |
|
99 | --- /dev/null | |
|
100 | +++ b/newexec | |
|
101 | @@ -0,0 +1,1 @@ | |
|
102 | +newexec | |
|
103 | % git=auto: git diff for setexec | |
|
104 | diff --git a/setexec b/setexec | |
|
105 | old mode 100644 | |
|
106 | new mode 100755 | |
|
107 | % git=auto: git diff for unsetexec | |
|
108 | diff --git a/unsetexec b/unsetexec | |
|
109 | old mode 100755 | |
|
110 | new mode 100644 | |
|
111 | % git=auto: git diff for binary | |
|
112 | diff --git a/binary b/binary | |
|
113 | index a9128c283485202893f5af379dd9beccb6e79486..09f370e38f498a462e1ca0faa724559b6630c04f | |
|
114 | GIT binary patch | |
|
115 | literal 2 | |
|
116 | Jc${Nk0000200961 | |
|
117 | ||
|
118 | % git=auto: git diff for newbinary | |
|
119 | diff --git a/newbinary b/newbinary | |
|
120 | new file mode 100644 | |
|
121 | index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d | |
|
122 | GIT binary patch | |
|
123 | literal 1 | |
|
124 | Ic${MZ000310RR91 | |
|
125 | ||
|
126 | % git=auto: git diff for newempty | |
|
127 | diff --git a/newempty b/newempty | |
|
128 | new file mode 100644 | |
|
129 | % git=auto: git diff for rmempty | |
|
130 | diff --git a/rmempty b/rmempty | |
|
131 | deleted file mode 100644 | |
|
132 | % git=warn: regular diff with data loss warnings | |
|
133 | diff -r b3f053cd7c7f binary | |
|
134 | Binary file binary has changed | |
|
135 | diff -r b3f053cd7c7f exec | |
|
136 | --- a/exec | |
|
137 | +++ b/exec | |
|
138 | @@ -1,1 +1,2 @@ | |
|
139 | exec | |
|
140 | +exec | |
|
141 | diff -r b3f053cd7c7f newbinary | |
|
142 | Binary file newbinary has changed | |
|
143 | diff -r b3f053cd7c7f newexec | |
|
144 | --- /dev/null | |
|
145 | +++ b/newexec | |
|
146 | @@ -0,0 +1,1 @@ | |
|
147 | +newexec | |
|
148 | diff -r b3f053cd7c7f newregular | |
|
149 | --- /dev/null | |
|
150 | +++ b/newregular | |
|
151 | @@ -0,0 +1,1 @@ | |
|
152 | +newregular | |
|
153 | diff -r b3f053cd7c7f regular | |
|
154 | --- a/regular | |
|
155 | +++ b/regular | |
|
156 | @@ -1,1 +1,2 @@ | |
|
157 | regular | |
|
158 | +regular | |
|
159 | diff -r b3f053cd7c7f rmbinary | |
|
160 | Binary file rmbinary has changed | |
|
161 | diff -r b3f053cd7c7f rmexec | |
|
162 | --- a/rmexec | |
|
163 | +++ /dev/null | |
|
164 | @@ -1,1 +0,0 @@ | |
|
165 | -rmexec | |
|
166 | diff -r b3f053cd7c7f rmregular | |
|
167 | --- a/rmregular | |
|
168 | +++ /dev/null | |
|
169 | @@ -1,1 +0,0 @@ | |
|
170 | -rmregular | |
|
171 | data lost for: binary | |
|
172 | data lost for: newbinary | |
|
173 | data lost for: newempty | |
|
174 | data lost for: newexec | |
|
175 | data lost for: rmempty | |
|
176 | data lost for: setexec | |
|
177 | data lost for: unsetexec | |
|
178 | % git=abort: fail on execute bit change | |
|
179 | abort: losing data for setexec | |
|
180 | % git=abort: succeed on regular file | |
|
181 | diff -r b3f053cd7c7f regular | |
|
182 | --- a/regular | |
|
183 | +++ b/regular | |
|
184 | @@ -1,1 +1,2 @@ | |
|
185 | regular | |
|
186 | +regular |
@@ -27,7 +27,9 b' class diffopts(object):' | |||
|
27 | 27 | nodates removes dates from diff headers |
|
28 | 28 | ignorews ignores all whitespace changes in the diff |
|
29 | 29 | ignorewsamount ignores changes in the amount of whitespace |
|
30 |
ignoreblanklines ignores changes whose lines are all blank |
|
|
30 | ignoreblanklines ignores changes whose lines are all blank | |
|
31 | upgrade generates git diffs to avoid data loss | |
|
32 | ''' | |
|
31 | 33 | |
|
32 | 34 | defaults = { |
|
33 | 35 | 'context': 3, |
@@ -38,6 +40,7 b' class diffopts(object):' | |||
|
38 | 40 | 'ignorews': False, |
|
39 | 41 | 'ignorewsamount': False, |
|
40 | 42 | 'ignoreblanklines': False, |
|
43 | 'upgrade': False, | |
|
41 | 44 | } |
|
42 | 45 | |
|
43 | 46 | __slots__ = defaults.keys() |
@@ -1246,17 +1246,25 b' def b85diff(to, tn):' | |||
|
1246 | 1246 | ret.append('\n') |
|
1247 | 1247 | return ''.join(ret) |
|
1248 | 1248 | |
|
1249 | def _addmodehdr(header, omode, nmode): | |
|
1250 | if omode != nmode: | |
|
1251 | header.append('old mode %s\n' % omode) | |
|
1252 | header.append('new mode %s\n' % nmode) | |
|
1249 | class GitDiffRequired(Exception): | |
|
1250 | pass | |
|
1253 | 1251 | |
|
1254 |
def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None |
|
|
1252 | def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None, | |
|
1253 | losedatafn=None): | |
|
1255 | 1254 | '''yields diff of changes to files between two nodes, or node and |
|
1256 | 1255 | working directory. |
|
1257 | 1256 | |
|
1258 | 1257 | if node1 is None, use first dirstate parent instead. |
|
1259 |
if node2 is None, compare node1 with working directory. |
|
|
1258 | if node2 is None, compare node1 with working directory. | |
|
1259 | ||
|
1260 | losedatafn(**kwarg) is a callable run when opts.upgrade=True and | |
|
1261 | every time some change cannot be represented with the current | |
|
1262 | patch format. Return False to upgrade to git patch format, True to | |
|
1263 | accept the loss or raise an exception to abort the diff. It is | |
|
1264 | called with the name of current file being diffed as 'fn'. If set | |
|
1265 | to None, patches will always be upgraded to git format when | |
|
1266 | necessary. | |
|
1267 | ''' | |
|
1260 | 1268 | |
|
1261 | 1269 | if opts is None: |
|
1262 | 1270 | opts = mdiff.defaultopts |
@@ -1288,24 +1296,50 b' def diff(repo, node1=None, node2=None, m' | |||
|
1288 | 1296 | modified, added, removed = changes[:3] |
|
1289 | 1297 | |
|
1290 | 1298 | if not modified and not added and not removed: |
|
1291 | return | |
|
1299 | return [] | |
|
1300 | ||
|
1301 | revs = None | |
|
1302 | if not repo.ui.quiet: | |
|
1303 | hexfunc = repo.ui.debugflag and hex or short | |
|
1304 | revs = [hexfunc(node) for node in [node1, node2] if node] | |
|
1305 | ||
|
1306 | copy = {} | |
|
1307 | if opts.git or opts.upgrade: | |
|
1308 | copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0] | |
|
1309 | copy = copy.copy() | |
|
1310 | for k, v in copy.items(): | |
|
1311 | copy[v] = k | |
|
1312 | ||
|
1313 | difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2, | |
|
1314 | modified, added, removed, copy, getfilectx, opts, losedata) | |
|
1315 | if opts.upgrade and not opts.git: | |
|
1316 | try: | |
|
1317 | def losedata(fn): | |
|
1318 | if not losedatafn or not losedatafn(fn=fn): | |
|
1319 | raise GitDiffRequired() | |
|
1320 | # Buffer the whole output until we are sure it can be generated | |
|
1321 | return list(difffn(opts.copy(git=False), losedata)) | |
|
1322 | except GitDiffRequired: | |
|
1323 | return difffn(opts.copy(git=True), None) | |
|
1324 | else: | |
|
1325 | return difffn(opts, None) | |
|
1326 | ||
|
1327 | def _addmodehdr(header, omode, nmode): | |
|
1328 | if omode != nmode: | |
|
1329 | header.append('old mode %s\n' % omode) | |
|
1330 | header.append('new mode %s\n' % nmode) | |
|
1331 | ||
|
1332 | def trydiff(repo, revs, ctx1, ctx2, modified, added, removed, | |
|
1333 | copy, getfilectx, opts, losedatafn): | |
|
1292 | 1334 | |
|
1293 | 1335 | date1 = util.datestr(ctx1.date()) |
|
1294 | 1336 | man1 = ctx1.manifest() |
|
1295 | 1337 | |
|
1296 | revs = None | |
|
1297 | if not repo.ui.quiet and not opts.git: | |
|
1298 | hexfunc = repo.ui.debugflag and hex or short | |
|
1299 | revs = [hexfunc(node) for node in [node1, node2] if node] | |
|
1338 | gone = set() | |
|
1339 | gitmode = {'l': '120000', 'x': '100755', '': '100644'} | |
|
1300 | 1340 | |
|
1301 | 1341 | if opts.git: |
|
1302 | copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid]) | |
|
1303 | copy = copy.copy() | |
|
1304 | for k, v in copy.items(): | |
|
1305 | copy[v] = k | |
|
1306 | ||
|
1307 | gone = set() | |
|
1308 | gitmode = {'l': '120000', 'x': '100755', '': '100644'} | |
|
1342 | revs = None | |
|
1309 | 1343 | |
|
1310 | 1344 | for f in sorted(modified + added + removed): |
|
1311 | 1345 | to = None |
@@ -1317,39 +1351,61 b' def diff(repo, node1=None, node2=None, m' | |||
|
1317 | 1351 | if f not in removed: |
|
1318 | 1352 | tn = getfilectx(f, ctx2).data() |
|
1319 | 1353 | a, b = f, f |
|
1320 | if opts.git: | |
|
1354 | if opts.git or losedatafn: | |
|
1321 | 1355 | if f in added: |
|
1322 | 1356 | mode = gitmode[ctx2.flags(f)] |
|
1323 | 1357 | if f in copy: |
|
1324 |
|
|
|
1325 |
|
|
|
1326 |
|
|
|
1327 | if a in removed and a not in gone: | |
|
1328 |
|
|
|
1329 |
|
|
|
1358 | if opts.git: | |
|
1359 | a = copy[f] | |
|
1360 | omode = gitmode[man1.flags(a)] | |
|
1361 | _addmodehdr(header, omode, mode) | |
|
1362 | if a in removed and a not in gone: | |
|
1363 | op = 'rename' | |
|
1364 | gone.add(a) | |
|
1365 | else: | |
|
1366 | op = 'copy' | |
|
1367 | header.append('%s from %s\n' % (op, a)) | |
|
1368 | header.append('%s to %s\n' % (op, f)) | |
|
1369 | to = getfilectx(a, ctx1).data() | |
|
1330 | 1370 | else: |
|
1331 |
|
|
|
1332 | header.append('%s from %s\n' % (op, a)) | |
|
1333 | header.append('%s to %s\n' % (op, f)) | |
|
1334 | to = getfilectx(a, ctx1).data() | |
|
1371 | losedatafn(f) | |
|
1335 | 1372 | else: |
|
1336 | header.append('new file mode %s\n' % mode) | |
|
1373 | if opts.git: | |
|
1374 | header.append('new file mode %s\n' % mode) | |
|
1375 | elif ctx2.flags(f): | |
|
1376 | losedatafn(f) | |
|
1337 | 1377 | if util.binary(tn): |
|
1338 |
|
|
|
1378 | if opts.git: | |
|
1379 | dodiff = 'binary' | |
|
1380 | else: | |
|
1381 | losedatafn(f) | |
|
1382 | if not opts.git and not tn: | |
|
1383 | # regular diffs cannot represent new empty file | |
|
1384 | losedatafn(f) | |
|
1339 | 1385 | elif f in removed: |
|
1340 | # have we already reported a copy above? | |
|
1341 | if f in copy and copy[f] in added and copy[copy[f]] == f: | |
|
1342 | dodiff = False | |
|
1343 |
|
|
|
1344 | header.append('deleted file mode %s\n' % | |
|
1345 | gitmode[man1.flags(f)]) | |
|
1386 | if opts.git: | |
|
1387 | # have we already reported a copy above? | |
|
1388 | if f in copy and copy[f] in added and copy[copy[f]] == f: | |
|
1389 | dodiff = False | |
|
1390 | else: | |
|
1391 | header.append('deleted file mode %s\n' % | |
|
1392 | gitmode[man1.flags(f)]) | |
|
1393 | elif not to: | |
|
1394 | # regular diffs cannot represent empty file deletion | |
|
1395 | losedatafn(f) | |
|
1346 | 1396 | else: |
|
1347 |
o |
|
|
1348 |
n |
|
|
1349 | _addmodehdr(header, omode, nmode) | |
|
1350 | if util.binary(to) or util.binary(tn): | |
|
1351 | dodiff = 'binary' | |
|
1352 | header.insert(0, mdiff.diffline(revs, a, b, opts)) | |
|
1397 | oflag = man1.flags(f) | |
|
1398 | nflag = ctx2.flags(f) | |
|
1399 | binary = util.binary(to) or util.binary(tn) | |
|
1400 | if opts.git: | |
|
1401 | _addmodehdr(header, gitmode[oflag], gitmode[nflag]) | |
|
1402 | if binary: | |
|
1403 | dodiff = 'binary' | |
|
1404 | elif binary or nflag != oflag: | |
|
1405 | losedatafn(f) | |
|
1406 | if opts.git: | |
|
1407 | header.insert(0, mdiff.diffline(revs, a, b, opts)) | |
|
1408 | ||
|
1353 | 1409 | if dodiff: |
|
1354 | 1410 | if dodiff == 'binary': |
|
1355 | 1411 | text = b85diff(to, tn) |
General Comments 0
You need to be logged in to leave comments.
Login now