1244 for l in chunk(zlib.compress(tn)): |
1244 for l in chunk(zlib.compress(tn)): |
1245 ret.append(fmtline(l)) |
1245 ret.append(fmtline(l)) |
1246 ret.append('\n') |
1246 ret.append('\n') |
1247 return ''.join(ret) |
1247 return ''.join(ret) |
1248 |
1248 |
1249 def _addmodehdr(header, omode, nmode): |
1249 class GitDiffRequired(Exception): |
1250 if omode != nmode: |
1250 pass |
1251 header.append('old mode %s\n' % omode) |
1251 |
1252 header.append('new mode %s\n' % nmode) |
1252 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None, |
1253 |
1253 losedatafn=None): |
1254 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None): |
|
1255 '''yields diff of changes to files between two nodes, or node and |
1254 '''yields diff of changes to files between two nodes, or node and |
1256 working directory. |
1255 working directory. |
1257 |
1256 |
1258 if node1 is None, use first dirstate parent instead. |
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 if opts is None: |
1269 if opts is None: |
1262 opts = mdiff.defaultopts |
1270 opts = mdiff.defaultopts |
1263 |
1271 |
1264 if not node1 and not node2: |
1272 if not node1 and not node2: |
1286 if not changes: |
1294 if not changes: |
1287 changes = repo.status(ctx1, ctx2, match=match) |
1295 changes = repo.status(ctx1, ctx2, match=match) |
1288 modified, added, removed = changes[:3] |
1296 modified, added, removed = changes[:3] |
1289 |
1297 |
1290 if not modified and not added and not removed: |
1298 if not modified and not added and not removed: |
1291 return |
1299 return [] |
1292 |
|
1293 date1 = util.datestr(ctx1.date()) |
|
1294 man1 = ctx1.manifest() |
|
1295 |
1300 |
1296 revs = None |
1301 revs = None |
1297 if not repo.ui.quiet and not opts.git: |
1302 if not repo.ui.quiet: |
1298 hexfunc = repo.ui.debugflag and hex or short |
1303 hexfunc = repo.ui.debugflag and hex or short |
1299 revs = [hexfunc(node) for node in [node1, node2] if node] |
1304 revs = [hexfunc(node) for node in [node1, node2] if node] |
1300 |
1305 |
1301 if opts.git: |
1306 copy = {} |
1302 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid]) |
1307 if opts.git or opts.upgrade: |
|
1308 copy = copies.copies(repo, ctx1, ctx2, repo[nullid])[0] |
1303 copy = copy.copy() |
1309 copy = copy.copy() |
1304 for k, v in copy.items(): |
1310 for k, v in copy.items(): |
1305 copy[v] = k |
1311 copy[v] = k |
1306 |
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): |
|
1334 |
|
1335 date1 = util.datestr(ctx1.date()) |
|
1336 man1 = ctx1.manifest() |
|
1337 |
1307 gone = set() |
1338 gone = set() |
1308 gitmode = {'l': '120000', 'x': '100755', '': '100644'} |
1339 gitmode = {'l': '120000', 'x': '100755', '': '100644'} |
|
1340 |
|
1341 if opts.git: |
|
1342 revs = None |
1309 |
1343 |
1310 for f in sorted(modified + added + removed): |
1344 for f in sorted(modified + added + removed): |
1311 to = None |
1345 to = None |
1312 tn = None |
1346 tn = None |
1313 dodiff = True |
1347 dodiff = True |
1315 if f in man1: |
1349 if f in man1: |
1316 to = getfilectx(f, ctx1).data() |
1350 to = getfilectx(f, ctx1).data() |
1317 if f not in removed: |
1351 if f not in removed: |
1318 tn = getfilectx(f, ctx2).data() |
1352 tn = getfilectx(f, ctx2).data() |
1319 a, b = f, f |
1353 a, b = f, f |
1320 if opts.git: |
1354 if opts.git or losedatafn: |
1321 if f in added: |
1355 if f in added: |
1322 mode = gitmode[ctx2.flags(f)] |
1356 mode = gitmode[ctx2.flags(f)] |
1323 if f in copy: |
1357 if f in copy: |
1324 a = copy[f] |
1358 if opts.git: |
1325 omode = gitmode[man1.flags(a)] |
1359 a = copy[f] |
1326 _addmodehdr(header, omode, mode) |
1360 omode = gitmode[man1.flags(a)] |
1327 if a in removed and a not in gone: |
1361 _addmodehdr(header, omode, mode) |
1328 op = 'rename' |
1362 if a in removed and a not in gone: |
1329 gone.add(a) |
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 else: |
1370 else: |
1331 op = 'copy' |
1371 losedatafn(f) |
1332 header.append('%s from %s\n' % (op, a)) |
|
1333 header.append('%s to %s\n' % (op, f)) |
|
1334 to = getfilectx(a, ctx1).data() |
|
1335 else: |
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 if util.binary(tn): |
1377 if util.binary(tn): |
1338 dodiff = 'binary' |
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 elif f in removed: |
1385 elif f in removed: |
1340 # have we already reported a copy above? |
1386 if opts.git: |
1341 if f in copy and copy[f] in added and copy[copy[f]] == f: |
1387 # have we already reported a copy above? |
1342 dodiff = False |
1388 if f in copy and copy[f] in added and copy[copy[f]] == f: |
1343 else: |
1389 dodiff = False |
1344 header.append('deleted file mode %s\n' % |
1390 else: |
1345 gitmode[man1.flags(f)]) |
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 else: |
1396 else: |
1347 omode = gitmode[man1.flags(f)] |
1397 oflag = man1.flags(f) |
1348 nmode = gitmode[ctx2.flags(f)] |
1398 nflag = ctx2.flags(f) |
1349 _addmodehdr(header, omode, nmode) |
1399 binary = util.binary(to) or util.binary(tn) |
1350 if util.binary(to) or util.binary(tn): |
1400 if opts.git: |
1351 dodiff = 'binary' |
1401 _addmodehdr(header, gitmode[oflag], gitmode[nflag]) |
1352 header.insert(0, mdiff.diffline(revs, a, b, opts)) |
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 if dodiff: |
1409 if dodiff: |
1354 if dodiff == 'binary': |
1410 if dodiff == 'binary': |
1355 text = b85diff(to, tn) |
1411 text = b85diff(to, tn) |
1356 else: |
1412 else: |
1357 text = mdiff.unidiff(to, date1, |
1413 text = mdiff.unidiff(to, date1, |