comparison hgext/evolve.py @ 1357:3bb7a080da4d

evolve: add ordering of the revisions for evolve --rev When running evolve --rev we want to process the revisions in an optimal fashion to solve the maximum amount of trouble in the minimum number of steps. This patch adds a step to evolve --rev to order the revision before solving the troubles. A simple test is added to cover a basic case.
author Laurent Charignon <lcharignon@fb.com>
date Thu, 04 Jun 2015 13:35:12 -0700
parents b4a62d6f0353
children 043e5ca9322f
comparison
equal deleted inserted replaced
1356:aff6bc2a6b2d 1357:3bb7a080da4d
26 import sys, os 26 import sys, os
27 import random 27 import random
28 from StringIO import StringIO 28 from StringIO import StringIO
29 import struct 29 import struct
30 import re 30 import re
31 import collections
31 import socket 32 import socket
32 import errno 33 import errno
33 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b') 34 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
34 35
35 import mercurial 36 import mercurial
1230 if showprogress: 1231 if showprogress:
1231 ui.progress('evolve', None) 1232 ui.progress('evolve', None)
1232 if repo['.'] != startnode: 1233 if repo['.'] != startnode:
1233 ui.status(_('working directory is now at %s\n') % repo['.']) 1234 ui.status(_('working directory is now at %s\n') % repo['.'])
1234 1235
1236 def _singlesuccessor(repo, p):
1237 """returns p if not obsolete or its unique latest successors
1238
1239 fail if there are no such successor"""
1240
1241 if not p.obsolete():
1242 return p
1243 obs = repo[p]
1244 ui = repo.ui
1245 newer = obsolete.successorssets(repo, obs.node())
1246 # search of a parent which is not killed
1247 while not newer or newer == [()]:
1248 ui.debug("stabilize target %s is plain dead,"
1249 " trying to stabilize on its parent\n" %
1250 obs)
1251 obs = obs.parents()[0]
1252 newer = obsolete.successorssets(repo, obs.node())
1253 if len(newer) > 1:
1254 raise util.Abort(_("conflict rewriting. can't choose destination\n"))
1255 return repo[newer[0][0]].rev()
1256
1257 def orderrevs(repo, revs):
1258 """ Compute an ordering to solve instability for the given revs
1259
1260 - Takes revs a list of instable revisions
1261
1262 - Returns the same revisions ordered to solve their instability from the
1263 bottom to the top of the stack that the stabilization process will produce
1264 eventually.
1265
1266 This ensure the minimal number of stabilization as we can stabilize each
1267 revision on its final, stabilized, destination.
1268 """
1269
1270 # Step 1: Build the dependency graph
1271 # For each troubled revision we keep track of what instability if any should
1272 # be resolved in order to resolve it. Example:
1273 # dependencies = {3: [6], 6:[]}
1274 # Means that: 6 has no dependency, 3 depends on 6 to be solved
1275 dependencies = {}
1276 # rdependencies is the inverted dict of dependencies
1277 rdependencies = collections.defaultdict(set)
1278
1279 for r in revs:
1280 dependencies[r] = set()
1281 for p in repo[r].parents():
1282 succ = _singlesuccessor(repo, p)
1283 if succ in revs:
1284 dependencies[r].add(succ)
1285 rdependencies[succ].add(r)
1286
1287 # Step 2: Build the ordering
1288 # Remove the revisions with no dependency(A) and add them to the ordering.
1289 # Removing these revisions leads to new revisions with no dependency (the
1290 # one depending on A) that we can remove from the dependency graph and add
1291 # to the ordering. We progress in a similar fashion until the ordering is
1292 # built
1293 solvablerevs = collections.deque([r for r in sorted(dependencies.keys())
1294 if not dependencies[r]])
1295 ordering = []
1296 while solvablerevs:
1297 rev = solvablerevs.popleft()
1298 for dependent in rdependencies[rev]:
1299 dependencies[dependent].remove(rev)
1300 if not dependencies[dependent]:
1301 solvablerevs.append(dependent)
1302 ordering.append(rev)
1303
1304 return ordering
1305
1235 @command('^evolve|stabilize|solve', 1306 @command('^evolve|stabilize|solve',
1236 [('n', 'dry-run', False, 1307 [('n', 'dry-run', False,
1237 'do not perform actions, just print what would be done'), 1308 'do not perform actions, just print what would be done'),
1238 ('', 'confirm', False, 1309 ('', 'confirm', False,
1239 'ask for confirmation before performing the action'), 1310 'ask for confirmation before performing the action'),
1305 if not _revs: 1376 if not _revs:
1306 ui.write_err("No troubled changes in the specified revisions\n") 1377 ui.write_err("No troubled changes in the specified revisions\n")
1307 else: 1378 else:
1308 # For the progress bar to show 1379 # For the progress bar to show
1309 count = len(_revs) 1380 count = len(_revs)
1381 # Order the revisions
1382 _revs = orderrevs(repo, _revs)
1310 for rev in _revs: 1383 for rev in _revs:
1311 progresscb() 1384 progresscb()
1312 _solveone(ui, repo, repo[rev], dryrunopt, confirmopt, 1385 _solveone(ui, repo, repo[rev], dryrunopt, confirmopt,
1313 progresscb) 1386 progresscb)
1314 seen += 1 1387 seen += 1