mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 07:17:55 +03:00
strip: make --keep option not set all dirstate times to 0
hg strip -k was using dirstate.rebuild() which reset all the dirstate entries timestamps to 0. This meant that the next time hg status was run every file was considered to be 'unsure', which caused it to do expensive read operations on every filelog. On a repo with >150,000 files it took 70 seconds when everything was in memory. From a cold cache it took several minutes. The fix is to only reset files that have changed between the working context and the destination context. For reference, --keep means the working directory is left alone during the strip. We have users wanting to use this operation to store their work-in-progress as a commit on a branch while they go work on another branch, then come back later and be able to uncommit that work and continue working. They currently use 'git reset HARD^' to accomplish this in git.
This commit is contained in:
parent
97bdc357fb
commit
83b3faf2ec
17
hgext/mq.py
17
hgext/mq.py
@ -3037,7 +3037,22 @@ def strip(ui, repo, *revs, **opts):
|
||||
wlock = repo.wlock()
|
||||
try:
|
||||
urev = repo.mq.qparents(repo, revs[0])
|
||||
repo.dirstate.rebuild(urev, repo[urev].manifest())
|
||||
uctx = repo[urev]
|
||||
|
||||
# only reset the dirstate for files that would actually change
|
||||
# between the working context and uctx
|
||||
descendantrevs = repo.revs("%s::." % uctx.rev())
|
||||
changedfiles = []
|
||||
for rev in descendantrevs:
|
||||
# blindy reset the files, regardless of what actually changed
|
||||
changedfiles.extend(repo[rev].files())
|
||||
|
||||
# reset files that only changed in the dirstate too
|
||||
dirstate = repo.dirstate
|
||||
dirchanges = [f for f in dirstate if dirstate[f] != 'n']
|
||||
changedfiles.extend(dirchanges)
|
||||
|
||||
repo.dirstate.rebuild(urev, uctx.manifest(), changedfiles)
|
||||
repo.dirstate.write()
|
||||
update = False
|
||||
finally:
|
||||
|
@ -498,13 +498,18 @@ class dirstate(object):
|
||||
self._lastnormaltime = 0
|
||||
self._dirty = True
|
||||
|
||||
def rebuild(self, parent, files):
|
||||
def rebuild(self, parent, allfiles, changedfiles=None):
|
||||
changedfiles = changedfiles or allfiles
|
||||
oldmap = self._map
|
||||
self.clear()
|
||||
for f in files:
|
||||
if 'x' in files.flags(f):
|
||||
self._map[f] = ('n', 0777, -1, 0)
|
||||
for f in allfiles:
|
||||
if f not in changedfiles:
|
||||
self._map[f] = oldmap[f]
|
||||
else:
|
||||
self._map[f] = ('n', 0666, -1, 0)
|
||||
if 'x' in allfiles.flags(f):
|
||||
self._map[f] = ('n', 0777, -1, 0)
|
||||
else:
|
||||
self._map[f] = ('n', 0666, -1, 0)
|
||||
self._pl = (parent, nullid)
|
||||
self._dirty = True
|
||||
|
||||
|
@ -420,6 +420,25 @@ Verify strip protects against stripping wc parent when there are uncommited mods
|
||||
$ hg status
|
||||
M bar
|
||||
? b
|
||||
|
||||
Strip adds, removes, modifies with --keep
|
||||
|
||||
$ touch b
|
||||
$ hg add b
|
||||
$ hg commit -mb
|
||||
$ touch c
|
||||
$ hg add c
|
||||
$ hg rm bar
|
||||
$ hg commit -mc
|
||||
$ echo b > b
|
||||
$ echo d > d
|
||||
$ hg strip --keep tip
|
||||
saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
|
||||
$ hg status
|
||||
M b
|
||||
! bar
|
||||
? c
|
||||
? d
|
||||
$ cd ..
|
||||
|
||||
stripping many nodes on a complex graph (issue3299)
|
||||
|
Loading…
Reference in New Issue
Block a user