mercurial/patch.py
changeset 10189 e451e599fbcf
parent 10151 c7355a0e1f39
child 10204 3ca8f2ae5fee
equal deleted inserted replaced
10188:fd6e9c7cd98c 10189:e451e599fbcf
  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,