diff --git a/hgext3rd/perftweaks.py b/hgext3rd/perftweaks.py --- a/hgext3rd/perftweaks.py +++ b/hgext3rd/perftweaks.py @@ -257,15 +257,11 @@ if repo is not None and repo.local(): dirstate = repo.dirstate # if the _map attribute is missing on the map, the dirstate was not - # loaded. If present, it *could* be a sqldirstate map. + # loaded. if '_map' in vars(dirstate): - map_ = dirstate._map._map - # check for a sqldirstate, only load length if cache was activated - sqldirstate = util.safehasattr(map_, '_lookupcache') - logsize = map_._lookupcache is not None if sqldirstate else True - if logsize: - lui.log('dirstate_size', '', - dirstate_size=len(map_)) + map_ = dirstate._map.map + lui.log('dirstate_size', '', + dirstate_size=len(map_)) return res def _tracksparseprofiles(runcommand, lui, repo, *args): diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -199,7 +199,6 @@ availablepackages = hgext3rdpkgs + [ 'infinitepush', 'phabricator', - 'sqldirstate', 'remotefilelog', ] diff --git a/sqldirstate/__init__.py b/sqldirstate/__init__.py deleted file mode 100644 --- a/sqldirstate/__init__.py +++ /dev/null @@ -1,133 +0,0 @@ -# __init__.py - sqldirstate extension -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -testedwith = 'ships-with-fb-hgext' - -from sqldirstate import makedirstate, toflat, tosql, writefakedirstate - -from mercurial import commands, error, extensions, localrepo, util -from mercurial import registrar -from mercurial.i18n import _ -from mercurial.extensions import wrapfunction - -def issqldirstate(repo): - return util.safehasattr(repo, 'requirements') and \ - 'sqldirstate' in repo.requirements - -def wrapjournalfiles(orig, self): - if issqldirstate(self): - files = () - for vfs, filename in orig(self): - if filename != 'journal.dirstate': - files += ((vfs, filename),) - - if not self.ui.configbool('sqldirstate', 'skipbackups', True): - files += ((self.vfs, 'journal.dirstate.sqlite3'),) - else: - files = orig(self) - - return files - -def wrapdirstate(orig, self): - ds = orig(self) - if issqldirstate(self): - ds.__class__ = makedirstate(ds.__class__) - ds._sqlinit() - return ds - -def wrapnewreporequirements(orig, repo): - reqs = orig(repo) - if repo.ui.configbool('format', 'sqldirstate', False): - reqs.add('sqldirstate') - return reqs - -def wrapshelveaborttransaction(orig, repo): - if issqldirstate(repo): - tr = repo.currenttransaction() - repo.dirstate._writesqldirstate() - tr.abort() - else: - return orig(repo) - -def upgrade(ui, repo): - if issqldirstate(repo): - raise error.Abort('repo already has sqldirstate') - wlock = repo.wlock() - try: - repo.dirstate._read() - tosql(repo.dirstate) - repo.requirements.add('sqldirstate') - repo._writerequirements() - writefakedirstate(repo.dirstate) - del repo.dirstate - - finally: - wlock.release() - -def downgrade(ui, repo): - if not issqldirstate(repo): - raise error.Abort('repo doesn\'t have sqldirstate') - wlock = repo.lock() - try: - toflat(repo.dirstate) - repo.requirements.remove('sqldirstate') - repo._writerequirements() - repo.dirstate._opener.unlink('dirstate.sqlite3') - del repo.dirstate - finally: - wlock.release() - -def wrappull(orig, ui, repo, *args, **kwargs): - if ui.configbool('sqldirstate', 'downgrade', False) and \ - issqldirstate(repo): - ui.status(_('disabling sqldirstate...\n')) - downgrade(ui, repo) - elif ui.configbool('sqldirstate', 'upgrade', False) and \ - not issqldirstate(repo): - ui.status(_('migrating your repo to sqldirstate which will make your ' - 'hg commands faster...\n')) - upgrade(ui, repo) - - return orig(ui, repo, *args, **kwargs) - -def featuresetup(ui, supported): - # don't die on seeing a repo with the sqldirstate requirement - supported |= set(['sqldirstate']) - -def extsetup(ui): - localrepo.localrepository.featuresetupfuncs.add(featuresetup) - wrapfunction(localrepo, 'newreporequirements', - wrapnewreporequirements) - wrapfunction(localrepo.localrepository, '_journalfiles', - wrapjournalfiles) - fcdescr = localrepo.localrepository.dirstate - wrapfunction(fcdescr, 'func', wrapdirstate) - - try: - shelve = extensions.find('shelve') - wrapfunction(shelve, '_aborttransaction', wrapshelveaborttransaction) - except KeyError: - pass - extensions.wrapcommand(commands.table, 'pull', wrappull) - -def reposetup(ui, repo): - ui.setconfig('sqldirstate', 'enabled', issqldirstate(repo)) - -# debug commands -cmdtable = {} -command = registrar.command(cmdtable) - -@command('debugsqldirstate', [], 'hg debugsqldirstate [on|off]') -def debugsqldirstate(ui, repo, cmd, **opts): - """ migrate to sqldirstate """ - - if cmd == "on": - upgrade(ui, repo) - elif cmd == "off": - downgrade(ui, repo) - else: - raise error.Abort("bad command") diff --git a/sqldirstate/sqldirstate.py b/sqldirstate/sqldirstate.py deleted file mode 100644 --- a/sqldirstate/sqldirstate.py +++ /dev/null @@ -1,659 +0,0 @@ -# dirstate.py - sqlite backed dirstate -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -""" -dirstate class replacement with sqlite backed storage - -This allows us to make incremental changes to we don't have to read the whole -dirstate on every operation. This makes sense only when fsmonitor is on so we -don't iterate through whole dirstate. - -sqldirstate stores the data in unnormalized form to avoid reading whole dirstate -to generate data like flat dirstate does. - -It'is using the sqlite transactions to handle dirstate transactions instead of -copying db around. As a result of that "hg rollback" doesn't work anymore. You -can fall back to copying things by setting sqldirstate.skipbackups to False. - -We commit sql transaction only when normal dirstate write would happen. -""" - -from __future__ import absolute_import - -from exceptions import RuntimeError -import os -import sqlite3 - -from mercurial import ( - dirstate, - policy, - util, -) - -from mercurial.node import nullid, hex, bin -from mercurial.util import propertycache - -from .sqlmap import sqlmap -from .sqltrace import tracewrapsqlconn - -parsers = policy.importmod(r'parsers') - -dirstatetuple = parsers.dirstatetuple - -DBFILE = "dirstate.sqlite3" -FAKEDIRSTATE = "dirstate" -SQLITE_CACHE_SIZE = -100000 # 100MB -SQL_SCHEMA_VERSION = 2 - -class SQLSchemaVersionUnsupported(RuntimeError): - def __init__(self, version): - self._version = version - - def __str__(self): - return "sqldirstate schema version not supported (%s)" % self._version - -def createotherschema(sqlconn): - """ The storage for all misc small key value data """ - sqlconn.execute('''CREATE TABLE IF NOT EXISTS other ( - key BLOB PRIMARY KEY, - value BLOB NOT NULL) - ''') - sqlconn.commit() - -def setversion(sqlconn, version): - sqlconn.execute('''INSERT OR REPLACE INTO other (key, value) VALUES - ("schema_version", ?)''', str(version)) - sqlconn.commit() - -def getversion(sqlconn): - cur = sqlconn.cursor() - cur.execute('''SELECT value FROM other - WHERE key = "schema_version"''') - row = cur.fetchone() - cur.close() - if row is None: - setversion(sqlconn, SQL_SCHEMA_VERSION) - return SQL_SCHEMA_VERSION - else: - return int(row[0]) - -def dropotherschema(sqlconn): - cur = sqlconn.cursor() - cur.execute('''DROP TABLE IF EXISTS other''') - cur.close() - sqlconn.commit() - -class sqldirstatemap(sqlmap): - """ the main map - reflects the original dirstate file contents """ - _tablename = 'files' - _keyname = 'filename' - _valuenames = ['status', 'mode', 'size', 'mtime'] - - def createschema(self): - cur = self._sqlconn.cursor() - cur.execute('''CREATE TABLE IF NOT EXISTS files ( - filename BLOB PRIMARY KEY, - status BLOB NOT NULL, - mode INTEGER NOT NULL, - size INTEGER NOT NULL, - mtime INTEGER NOT NULL) - ''') - - # The low cardinality of the status column makes it basically - # non-indexable. - # There is a feature we could use available sqlite 3.9 called partial - # indexes. Using it we could index only the files that have non-normal - # statuses. - # The sqlite 3.6 that we are using on centos6 unfortunately doesn't - # support it. So we are maintaining separate table with nonnormalfiles. - cur.execute('''CREATE TABLE IF NOT EXISTS nonnormalfiles ( - filename BLOB PRIMARY KEY, - status BLOB NOT NULL, - mode INTEGER NOT NULL, - size INTEGER NOT NULL, - mtime INTEGER NOT NULL) - ''') - - cur.execute('''CREATE INDEX IF NOT EXISTS - files_mtime ON files(mtime);''') - - cur.close() - self._sqlconn.commit() - - def dropschema(self): - cur = self._sqlconn.cursor() - cur.execute('''DROP TABLE IF EXISTS files''') - cur.execute('''DROP TABLE IF EXISTS nonnormalfiles''') - cur.close() - self._sqlconn.commit() - - def _rowtovalue(self, row): - return dirstatetuple(*row) - - def _valuetorow(self, value): - return (value[0], value[1], value[2], value[3]) - - def nonnormalentries(self): - cur = self._sqlconn.cursor() - # -1 means that we should check the file on next status - cur.execute('''SELECT filename FROM files - WHERE mtime = -1''') - rows = cur.fetchall() - cur.execute('''SELECT filename FROM nonnormalfiles''') - rows += cur.fetchall() - cur.close() - return set(row[0] for row in rows) - - - def otherparententries(self): - cur = self._sqlconn.cursor() - # -2 means that file is comming from the other parent of the merge - # it's always dirty - cur.execute('''SELECT filename, status, mode, size, mtime FROM files ''' - '''WHERE status = 'n' and size = -2;''') - for r in cur: - yield (r[0], self._rowtovalue(r[1:])) - cur.close() - - def modifiedentries(self): - cur = self._sqlconn.cursor() - cur.execute('''SELECT filename, status, mode, size, mtime FROM ''' - '''nonnormalfiles WHERE status = 'm';''') - for r in cur: - yield (r[0], self._rowtovalue(r[1:])) - cur.close() - - def resetnow(self, now, nonnormalset=None): - cur = self._sqlconn.cursor() - cur.execute('''UPDATE files SET mtime = -1 - WHERE mtime = ? and status = 'n' ''', (now,)) - if self._lookupcache is not None: - for k, v in self._lookupcache.iteritems(): - status, mode, size, mtime = v - if status == 'n' and mtime == now: - mtime = -1 - self._lookupcache[k] = status, mode, size, mtime - if nonnormalset is not None: - nonnormalset.add(k) - cur.close() - - def __setitem__(self, key, item): - super(sqldirstatemap, self).__setitem__(key, item) - - status = item[0] - cur = self._sqlconn.cursor() - if status != 'n': - item = self._valuetorow(item) - cur.execute('''INSERT OR REPLACE INTO nonnormalfiles ({keyname}, - {valuenames}) VALUES ({placeholders})'''.format( - **self._querytemplateargs), (key,) + item) - else: - cur.execute('''DELETE FROM nonnormalfiles - WHERE {keyname}=?'''.format( - **self._querytemplateargs), (key,)) - cur.close() - - def _update(self, otherdict): - super(sqldirstatemap, self)._update(otherdict) - - updatelist = list() - deletelist = list() - for key, item in otherdict.iteritems(): - if item[0] != 'n': - updatelist.append((key,) + self._valuetorow(item)) - else: - deletelist.append((key,)) - cur = self._sqlconn.cursor() - cur.executemany('''INSERT OR REPLACE INTO nonnormalfiles - ({keyname}, {valuenames}) VALUES ({placeholders})'''.format( - **self._querytemplateargs), updatelist) - - cur.executemany('''DELETE FROM nonnormalfiles - WHERE {keyname}=?'''.format( - **self._querytemplateargs), deletelist) - cur.close() - - -class sqlcopymap(sqlmap): - """ all copy informations in dirstate """ - _tablename = 'copymap' - _keyname = 'dest' - _valuenames = ['source'] - def createschema(self): - cur = self._sqlconn.cursor() - cur.execute('''CREATE TABLE IF NOT EXISTS copymap( - dest BLOB PRIMARY KEY, - source BLOB NOT NULL) - ''') - cur.close() - self._sqlconn.commit() - - def dropschema(self): - cur = self._sqlconn.cursor() - cur.execute('''DROP TABLE IF EXISTS copymap''') - cur.close() - self._sqlconn.commit() - - -class sqlfilefoldmap(sqlmap): - """ in normal dirstate this map is generated on-the-fly from - the dirstate. We are opting for persistent foldmap so we don't - have read the whole dirstate """ - - _tablename = 'filefoldmap' - _keyname = 'normed' - _valuenames = ['real'] - def createschema(self): - cur = self._sqlconn.cursor() - cur.execute('''CREATE TABLE IF NOT EXISTS filefoldmap ( - normed BLOB PRIMARY KEY, - real BLOB NOT NULL) - ''') - cur.close() - self._sqlconn.commit() - - def dropschema(self): - cur = self._sqlconn.cursor() - cur.execute('''DROP TABLE IF EXISTS filefoldmap''') - cur.close() - self._sqlconn.commit() - -class sqldirfoldmap(sqlmap): - """ in normal dirstate this map is generated on-the-fly from - the dirstate. We are opting for persistent foldmap so we don't - have read the whole dirstate """ - - _tablename = 'dirfoldmap' - _keyname = 'normed' - _valuenames = ['real'] - def createschema(self): - cur = self._sqlconn.cursor() - cur.execute('''CREATE TABLE IF NOT EXISTS dirfoldmap ( - normed BLOB PRIMARY KEY, - real BLOB NOT NULL) - ''') - cur.close() - self._sqlconn.commit() - - def dropschema(self): - cur = self._sqlconn.cursor() - cur.execute('''DROP TABLE IF EXISTS dirfoldmap''') - cur.close() - self._sqlconn.commit() - - -class sqldirsdict(sqlmap): - """ in normal dirstate this map is generated on-the-fly from - the dirstate. We are opting for persistent foldmap so we don't - have read the whole dirstate """ - - _tablename = 'dirs' - _keyname = 'dir' - _valuenames = ['count'] - def createschema(self): - cur = self._sqlconn.cursor() - cur.execute('''CREATE TABLE IF NOT EXISTS dirs( - dir BLOB PRIMARY KEY, - count INT NOT NULL) - ''') - cur.close() - self._sqlconn.commit() - - def dropschema(self): - cur = self._sqlconn.cursor() - cur.execute('''DROP TABLE IF EXISTS dirs''') - cur.close() - self._sqlconn.commit() - -class sqldirs(object): - """ Reimplementaion of util.dirs which is not resuseable because it's - replaced by c code if available. Probably with a small upstream - change we could reuse it """ - def __init__(self, sqlconn, skip=None, filemap=None, dirsdict=None, - **mapkwargs): - self._dirs = dirsdict - if self._dirs is None: - self._dirs = sqldirsdict(sqlconn, **mapkwargs) - if filemap: - for f, s in filemap.iteritems(): - self.addpath(f) - - # copied from util.py - def addpath(self, path): - dirs = self._dirs - for base in util.finddirs(path): - if base in dirs: - dirs[base] += 1 - return - dirs[base] = 1 - - def delpath(self, path): - dirs = self._dirs - for base in util.finddirs(path): - if dirs[base] > 1: - dirs[base] -= 1 - return - del dirs[base] - - def __iter__(self): - return self._dirs.iterkeys() - - def __contains__(self, d): - return d in self._dirs - - def clear(self): - self._dirs.clear() - - @property - def dirsdict(self): - return self._dirs - -def makedirstate(cls): - class sqldirstate(cls): - def _sqlinit(self): - '''Create a new dirstate object. - - opener is an open()-like callable that can be used to open the - dirstate file; root is the root of the directory tracked by - the dirstate. - ''' - self._sqlfilename = self._opener.join(DBFILE) - self._sqlconn = sqlite3.connect(self._sqlfilename) - self._sqlconn.text_factory = str - if self._ui.config('sqldirstate', 'tracefile', False): - self._sqlconn = tracewrapsqlconn(self._sqlconn, - self._ui.config('sqldirstate', 'tracefile')) - - self._sqlconn.execute("PRAGMA cache_size = %d" % SQLITE_CACHE_SIZE) - self._sqlconn.execute("PRAGMA synchronous = OFF") - createotherschema(self._sqlconn) - self._sqlschemaversion = getversion(self._sqlconn) - - cachebuildtreshold = self._ui.config('sqldirstate', - 'cachebuildtreshold', 10000) - mapkwargs = {'cachebuildtreshold': cachebuildtreshold } - self._map = sqldirstatemap(self._sqlconn, **mapkwargs) - self._dirs = sqldirs(self._sqlconn, **mapkwargs) - self._copymap = sqlcopymap(self._sqlconn, **mapkwargs) - self._filefoldmap = sqlfilefoldmap(self._sqlconn, **mapkwargs) - self._dirfoldmap = sqldirfoldmap(self._sqlconn, **mapkwargs) - self.skipbackups = self._ui.configbool('sqldirstate', 'skipbackups', - True) - - if self._sqlschemaversion > SQL_SCHEMA_VERSION: - # TODO: add recovery mechanism - raise SQLSchemaVersionUnsupported(self._sqlschemaversion) - - self._sqlmigration() - - def _read(self): - self._identity = util.filestat.frompath( - self._opener.join(self._filename)) - - @propertycache - def _pl(self): - p1 = p2 = hex(nullid) - cur = self._sqlconn.cursor() - cur.execute('''SELECT key, value FROM other - WHERE key='p1' or key='p2' ''') - rows = cur.fetchall() - for r in rows: - if r[0] == 'p1': - p1 = r[1] - if r[0] == 'p2': - p2 = r[1] - - cur.close() - return [bin(p1), bin(p2)] - - def __setattr__(self, key, value): - if key == '_pl': - # because other methods in dirstate are setting it directly - # instead of using setparents - p1 = value[0] - p2 = value[1] - cur = self._sqlconn.cursor() - cur.executemany('''INSERT OR REPLACE INTO - other (key, value) VALUES (?, ?)''', - [('p1', hex(p1)), ('p2', hex(p2))]) - cur.close() - self.__dict__['_pl'] = value - else: - return super(sqldirstate, self).__setattr__(key, value) - - def savebackup(self, tr, suffix="", prefix=""): - if self.skipbackups: - return - self._writesqldirstate() - util.copyfile(self._opener.join(DBFILE), - self._opener.join(prefix + DBFILE + suffix)) - - def restorebackup(self, tr, suffix="", prefix=""): - if self.skipbackups: - return - self._opener.rename(prefix + DBFILE + suffix, DBFILE) - self.invalidate() - - def clearbackup(self, tr, suffix="", prefix=""): - if self.skipbackups: - return - self._opener.unlink(prefix + DBFILE + suffix) - - @propertycache - def _nonnormalset(self): - return self._map.nonnormalentries() - - @propertycache - def _otherparentset(self): - result = set() - for f, s in self._map.otherparententries(): - result.add(f) - return result - - def invalidate(self): - # Transaction will be rolled back on the next open of the file. - # The close is faster in the case where we replace the whole - # db file as we don't need to rollback in such case. - self._sqlconn.close() - for a in ("_branch", "_pl", "_ignore", "_nonnormalset"): - if a in self.__dict__: - delattr(self, a) - self._lastnormaltime = 0 - self._dirty = False - self._dirtypl = False - self._origpl = None - self._parentwriters = 0 - self._sqlinit() - if util.safehasattr(self, '_fsmonitorstate'): - self._fsmonitorstate.invalidate() - - def write(self, tr=False): - # if dirty dump to disk (db transaction commit) - if not self._dirty: - return - now = dirstate._getfsnow(self._opener) - - self._map.resetnow(now, self._nonnormalset) - if tr: - tr.addfinalize("sqldirstate.write", self._backupandwrite) - return - self._writesqldirstate() - - def _writedirstate(self, st): - self._writesqldirstate() - - def _writesqldirstate(self): - # notify callbacks about parents change - if self._origpl is not None: - if self._origpl != self._pl: - for c, callback in sorted( - self._plchangecallbacks.iteritems()): - callback(self, self._origpl, self._pl) - self._origpl = None - # if dirty dump to disk (db transaction commit) - now = dirstate._getfsnow(self._opener) - - self._map.resetnow(now, self._nonnormalset) - self._sqlconn.commit() - self._lastnormaltime = 0 - self._dirty = self._dirtypl = False - if '_nonnormalset' in self.__dict__: - delattr(self, '_nonnormalset') - - writefakedirstate(self) - - def _backupandwrite(self, tr): - if not self.skipbackups: - backuppath = self._opener.join('%s.%s' % (tr.journal, DBFILE)) - util.copyfile(self._sqlfilename, backuppath) - tr._addbackupentry(('plain', self._sqlfilename, - backuppath, False)) - self._writesqldirstate() - - def clear(self): - self._map.clear() - if '_nonnormalset' in self.__dict__: - delattr(self, '_nonnormalset') - self._dirs.clear() - self._copymap.clear() - self._filefoldmap.clear() - self._dirfoldmap.clear() - self._pl = [nullid, nullid] - self._lastnormaltime = 0 - self._dirty = True - - def setparents(self, p1, p2=nullid): - """Set dirstate parents to p1 and p2. - - When moving from two parents to one, 'm' merged entries a - adjusted to normal and previous copy records discarded and - returned by the call. - - See localrepo.setparents() - """ - if self._parentwriters == 0: - raise ValueError("cannot set dirstate parent without " - "calling dirstate.beginparentchange") - - self._dirty = self._dirtypl = True - oldp2 = self._pl[1] - if self._origpl is None: - self._origpl = self._pl - self._pl = p1, p2 - copies = {} - if oldp2 != nullid and p2 == nullid: - # Discard 'm' markers when moving away from a merge state - for f, s in self._map.modifiedentries(): - if f in self._copymap: - copies[f] = self._copymap[f] - self.normallookup(f) - # Also fix up otherparent markers - for f, s in self._map.otherparententries(): - if f in self._copymap: - copies[f] = self._copymap[f] - self.add(f) - return copies - - def walk(self, match, subrepos, unknown, ignored, full=True): - self._map.enablelookupcache() - self._copymap.enablelookupcache() - return super(sqldirstate, self).walk( - match, subrepos, unknown, ignored, full) - - def _sqlmigration(self): - if self._sqlschemaversion == 1: - cur = self._sqlconn.cursor() - cur.execute('''SELECT filename, status, mode, size, mtime - FROM files WHERE status != 'n' or mtime = -1''') - rows = cur.fetchall() - nonnormalfiles = dict() - for r in rows: - nonnormalfiles[r[0]] = self._map._rowtovalue(r[1:]) - self._map.update(nonnormalfiles) - setversion(self._sqlconn, 2) - self._sqlschemaversion = 2 - - return sqldirstate - - -def writefakedirstate(dirstate): - st = dirstate._opener(FAKEDIRSTATE, "w", atomictemp=True, checkambig=True) - st.write("".join(dirstate._pl)) - st.write("\nThis is fake dirstate put here by the sqldirsate.") - st.write("\nIt contains only working copy parents info.") - st.write("\nThe real dirstate is in dirstate.sqlite3 file.") - st.close() - -def tosql(dirstate): - """" converts flat dirstate to sqldirstate - - note: the sole responsibility of this function is to write the new dirstate - it's not touching anything but the DBFILE - """ - sqlfilename = dirstate._opener.join(DBFILE) - try: - os.unlink(sqlfilename) - except OSError: - pass - - sqlconn = sqlite3.connect(sqlfilename) - - sqlconn.text_factory = str - - # Those two pragmas are dangerous and may corrupt db if interrupted but we - # are populating db now so we are not afraid to lose it and we want it to - # be fast. - sqlconn.execute("PRAGMA synchronous = OFF") - sqlconn.execute("PRAGMA journal_mode = OFF") - sqlconn.execute("PRAGMA cache_size = %d" % SQLITE_CACHE_SIZE) - - createotherschema(sqlconn) - getversion(sqlconn) - sqlmap = sqldirstatemap(sqlconn) - copymap = sqlcopymap(sqlconn) - filefoldmap = sqlfilefoldmap(sqlconn) - dirfoldmap = sqldirfoldmap(sqlconn) - dirsdict = sqldirsdict(sqlconn) - - sqldirs(sqlconn, dirsdict=dirstate._map) - sqlmap.update(dirstate._map) - copymap.update(dirstate._copymap) - filefoldmap.update(dirstate._filefoldmap) - dirfoldmap.update(dirstate._dirfoldmap) - dirs = sqldirs(sqlconn, filemap=dirstate._map, dirsdict={}) - dirsdict.update(dirs.dirsdict) - - cur = sqlconn.cursor() - cur.executemany('''INSERT OR REPLACE INTO - other (key, value) - VALUES (?, ?)''', - [('p1', hex(dirstate.p1())), ('p2', hex(dirstate.p2()))] - ) - cur.close() - sqlconn.commit() - writefakedirstate(dirstate) - - sqlconn.execute("PRAGMA synchronous = ON") - sqlconn.execute("PRAGMA journal_mode = DELETE") - -def toflat(sqldirstate): - """" converts sqldirstate to flat dirstate - - note: the sole responsibility of this function is to write the new dirstate - it's not touching anything but the dirstate file - """ - # converts a sqldirstate to a flat one - st = sqldirstate._opener("dirstate", "w", atomictemp=True, checkambig=True) - newmap = {} - for k, v in sqldirstate._map.iteritems(): - newmap[k] = v - newcopymap = {} - for k, v in sqldirstate._copymap.iteritems(): - newcopymap[k] = v - - st.write(parsers.pack_dirstate(newmap, newcopymap, sqldirstate._pl, - dirstate._getfsnow(sqldirstate._opener))) - st.close() diff --git a/sqldirstate/sqlmap.py b/sqldirstate/sqlmap.py deleted file mode 100644 --- a/sqldirstate/sqlmap.py +++ /dev/null @@ -1,206 +0,0 @@ -# sqlmap.py - sql backed dictionary -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -from __future__ import absolute_import - -from abc import abstractmethod, ABCMeta -import collections, inspect - -def allowcachelookup(f): - """Decorator that will delegate the function call to the lookupcache instead - of executing the function and querying the SQLite database - For example with: - - @allowcachelookup - def __len__(self): - .... - - It will return self._lookupcache.__len__() iff self._lookupcache is - not None, otherwise it will call the original function - """ - # generator need a generator wrapper - if inspect.isgenerator(f) or inspect.isgeneratorfunction(f): - def withcachelookup(self, *args, **kwargs): - if self._lookupcache is not None: - for i in getattr(self._lookupcache, f.__name__)(*args, - **kwargs): - yield i - else: - for i in f(self, *args, **kwargs): - yield i - else: - def withcachelookup(self, *args, **kwargs): - if self._lookupcache is not None: - return getattr(self._lookupcache, f.__name__)(*args, **kwargs) - else: - return f(self, *args, **kwargs) - return withcachelookup - -class sqlmap(collections.MutableMapping): - """ a dictionary-like object backed by sqllite db.""" - __metaclass__ = ABCMeta - - def __init__(self, sqlconn, cachebuildtreshold=10000): - self._sqlconn = sqlconn - self._lookupcache = None - self.createschema() - self._readcount = 0 - self._cachebuildtreshold = cachebuildtreshold - - def enablelookupcache(self): - if self._lookupcache is None: - self._lookupcache = dict(self.iteritems()) - - def invalidatelookupcache(self): - self._lookupcache = None - - @abstractmethod - def createschema(self): - """ create db table if doesn't exist """ - pass - - @abstractmethod - def dropschema(self): - """ drop db table """ - pass - - def _rowtovalue(self, row): - """ converts row of db to a value format """ - return row[0] - - def _valuetorow(self, value): - """ convers provided value to db row format """ - return (value,) - - @property - def _numcols(self): - return 1 + len(self._valuenames) - - @property - def _valuenamesstr(self): - return ', '.join(self._valuenames) - - @property - def _querytemplateargs(self): - return {'table': self._tablename, - 'keyname': self._keyname, - 'valuenames': self._valuenamesstr, - 'placeholders': ', '.join(['?'] * self._numcols)} - - def __setitem__(self, key, item): - if self._lookupcache is not None: - self._lookupcache[key] = item - - cur = self._sqlconn.cursor() - - item = self._valuetorow(item) - - - cur.execute('''INSERT OR REPLACE INTO {table} ({keyname}, {valuenames}) - VALUES ({placeholders})'''.format(**self._querytemplateargs), - (key,) + item) - cur.close() - - @allowcachelookup - def __getitem__(self, key): - cur = self._sqlconn.cursor() - self._readcount += 1 - - if self._cachebuildtreshold and\ - self._readcount > self._cachebuildtreshold: - self.enablelookupcache() - self._readcount = 0 - return self[key] - - cur.execute('''SELECT {valuenames} FROM {table} - WHERE {keyname}=?'''.format(**self._querytemplateargs), - (key,)) - row = cur.fetchone() - cur.close() - - if row is None: - raise KeyError("key %s not found" % key) - return self._rowtovalue(row) - - def __delitem__(self, key): - if self._lookupcache is not None: - del self._lookupcache[key] - - cur = self._sqlconn.cursor() - cur.execute('''DELETE FROM {table} WHERE {keyname}=?'''.format( - **self._querytemplateargs), (key,)) - - if cur.rowcount == 0: - raise KeyError("key %s not found" % key) - cur.close() - - @allowcachelookup - def __len__(self): - cur = self._sqlconn.cursor() - cur.execute('''SELECT COUNT(*) FROM {table}'''.format( - **self._querytemplateargs)) - res = cur.fetchone() - cur.close() - return res[0] - - def clear(self): - self.invalidatelookupcache() - cur = self._sqlconn.cursor() - cur.execute('''DELETE FROM {table}'''.format(**self._querytemplateargs)) - cur.close() - - @allowcachelookup - def copy(self): - return dict(self.iteritems()) - - def _update(self, otherdict): - if self._lookupcache is not None: - self._lookupcache.update(otherdict) - - tuplelist = [(k,) + self._valuetorow(v) - for k, v in otherdict.iteritems()] - - cur = self._sqlconn.cursor() - cur.executemany('''INSERT OR REPLACE INTO {table} - ({keyname}, {valuenames}) VALUES ({placeholders})'''.format( - **self._querytemplateargs), tuplelist) - cur.close() - - def update(self, *args, **kwargs): - assert len(args) == 1 or kwargs - if args: - self._update(args[0]) - if kwargs: - self._update(kwargs) - - @allowcachelookup - def keys(self): - cur = self._sqlconn.cursor() - cur.execute('''SELECT {keyname} FROM {table}'''.format( - **self._querytemplateargs)) - keys = cur.fetchall() - cur.close() - return [k[0] for k in keys] - - @allowcachelookup - def __iter__(self): - cur = self._sqlconn.cursor() - cur.execute('''SELECT {keyname} FROM {table}'''.format( - **self._querytemplateargs)) - for r in cur: - yield r[0] - cur.close() - - @allowcachelookup - def iteritems(self): - cur = self._sqlconn.cursor() - cur.execute('''SELECT {keyname}, {valuenames} - FROM {table}'''.format( - **self._querytemplateargs)) - for r in cur: - yield (r[0], self._rowtovalue(r[1:])) - cur.close() diff --git a/sqldirstate/sqltrace.py b/sqldirstate/sqltrace.py deleted file mode 100644 --- a/sqldirstate/sqltrace.py +++ /dev/null @@ -1,75 +0,0 @@ -# sqltrace.py - sqlite command tracing for logging purposes -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -import sys - -def tracewrapsqlconn(sqlconn, tracefile): - if tracefile == '-': - f = sys.stderr - else: - f = open(tracefile, 'a+') - - def logquery(q, paramtuple=()): - # This is module for escaping the sql queries imported from postressql - # as sqllite doesn't provide one. Still better than pretending to - # escape it manually over here. - # - # We are importing it here as it's only used for debugging purposes so - # it's not considered as dependency for prod system. - from psycopg2.extensions import adapt - out = q.replace("?", "%s") % tuple(map(adapt, paramtuple)) - f.write(out) - f.write(';\n') - - class CursorWrapper(object): - def __init__(self, cursor): - self._cursor = cursor - - def __getattr__(self, attr): - return self._cursor.__getattribute__(attr) - - def __iter__(self, *args, **kwargs): - return self._cursor.__iter__(*args, **kwargs) - - def iter(self, *args, **kwargs): - return self._cursor.iter(*args, **kwargs) - - def execute(self, sql, *params): - logquery(sql, *params) - return self._cursor.execute(sql, *params) - - def executemany(self, sql, paramslist): - for params in paramslist: - logquery(sql, params) - return self._cursor.executemany(sql, paramslist) - - def executescript(self, sql, *params): - logquery(sql) - return self._cursor.executescript(sql, *params) - - class ConnWrapper(object): - def __init__(self, wrapped): - self._sqlconn = wrapped - - def __getattr__(self, attr): - return self._sqlconn.__getattribute__(attr) - - def cursor(self, *args, **kwargs): - return CursorWrapper(self._sqlconn.cursor()) - - def execute(self, sql, *params): - logquery(sql, *params) - return self._sqlconn.execute(sql, *params) - - def executemany(self, sql, paramslist): - for params in paramslist: - logquery(sql, params) - return self._sqlconn.executemany(sql, paramslist) - - def executescript(self, sql, *params): - logquery(sql) - return self._sqlconn.executescript(sql, *params) - return ConnWrapper(sqlconn) diff --git a/tests/run-sqlitedirstate-test.py b/tests/run-sqlitedirstate-test.py deleted file mode 100755 --- a/tests/run-sqlitedirstate-test.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - -import optparse -import os -import subprocess -import sys - -# PassThroughOptionParse is from the Optik source distribution, (c) 2001-2006 -# Gregory P. Ward. Used under the BSD license. -class PassThroughOptionParser(optparse.OptionParser): - def _process_long_opt(self, rargs, values): - try: - optparse.OptionParser._process_long_opt(self, rargs, values) - except optparse.BadOptionError as err: - self.largs.append(err.opt_str) - - def _process_short_opts(self, rargs, values): - try: - optparse.OptionParser._process_short_opts(self, rargs, values) - except optparse.BadOptionError as err: - self.largs.append(err.opt_str) - -def parseargs(argv): - parser = PassThroughOptionParser(usage='%prog [options]', - epilog='Any additional options and arguments are passed through to ' - 'REPO/tests/run-tests.py.') - - parser.add_option('--hg', type='string', - metavar='REPO', - help='Mercurial repository to run tests against') - parser.add_option('--disable-blacklist', action='store_true', - default=False, - help='disable default test blacklist') - - options, args = parser.parse_args(argv) - if not options.hg: - parser.error('Mercurial repository not specified') - - return options, args - -def main(argv): - options, args = parseargs(argv) - - thisdir = os.path.dirname(os.path.realpath(__file__)) - extroot = os.path.join(os.path.dirname(thisdir), 'sqldirstate') - extopts = ['--extra-config-opt', 'extensions.sqldirstate=%s' % extroot, - '--extra-config-opt', 'sqldirstate.skipbackups=False', - '--extra-config-opt', 'format.sqldirstate=True'] - if not options.disable_blacklist: - extopts += ['--blacklist', - os.path.join(thisdir, 'blacklist-sqldirstate')] - - cwd = os.path.expanduser(os.path.join(options.hg, 'tests')) - cmd = [os.path.join(cwd, 'run-tests.py')] + extopts + args - - return subprocess.call(cmd, cwd=cwd) - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) diff --git a/tests/test-sqldirstate-fbhistedit.t b/tests/test-sqldirstate-fbhistedit.t deleted file mode 100644 --- a/tests/test-sqldirstate-fbhistedit.t +++ /dev/null @@ -1,434 +0,0 @@ - $ . "$TESTDIR/histedit-helpers.sh" - - $ cat <> $HGRCPATH - > [extensions] - > histedit= - > sqldirstate=$TESTDIR/../sqldirstate - > fbhistedit=$TESTDIR/../hgext3rd/fbhistedit.py - > [shelve] - > EOF - - $ initrepo () - > { - > hg init --config format.sqldirstate=True r - > cd r - > for x in a b c d e f ; do - > echo $x > $x - > hg add $x - > hg ci -m $x - > done - > } - - $ initrepo - -log before edit - - $ hg log --graph - @ changeset: 5:652413bf663e - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: f - | - o changeset: 4:e860deea161a - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: e - | - o changeset: 3:055a42cdd887 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: d - | - o changeset: 2:177f92b77385 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: c - | - o changeset: 1:d2ae7f538514 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: b - | - o changeset: 0:cb9a9f314b8b - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: a - - -exec & continue should not preserve hashes - - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > pick e860deea161a e - > exec echo "this should be printed to stdout" - > exec echo "this should be printed to stderr" >&2 - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - this should be printed to stdout - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - this should be printed to stderr - - $ hg log --graph - @ changeset: 5:652413bf663e - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: f - | - o changeset: 4:e860deea161a - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: e - | - o changeset: 3:055a42cdd887 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: d - | - o changeset: 2:177f92b77385 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: c - | - o changeset: 1:d2ae7f538514 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: b - | - o changeset: 0:cb9a9f314b8b - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: a - -ensure we are properly executed in a shell - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > pick e860deea161a e - > exec echo "foo" >/dev/null && exit 0 - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - -a failing command should drop us into the shell - - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > pick e860deea161a e - > exec exit 1 - > exec exit 1 - > pick 652413bf663e f - > exec exit 1 - > EOF - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - Command 'exit 1' failed with exit status 1 - -continue should work - - $ hg histedit --continue - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - Command 'exit 1' failed with exit status 1 - [1] - -continue after consecutive failed execs - - $ hg histedit --continue - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - Command 'exit 1' failed with exit status 1 - [1] - -continue after the last entry - - $ hg histedit --continue - - $ hg log --template '{node|short} {desc}' --graph - @ 652413bf663e f - | - o e860deea161a e - | - o 055a42cdd887 d - | - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - -abort should work - - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > pick e860deea161a e - > exec exit 1 - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - Command 'exit 1' failed with exit status 1 - - $ hg histedit --abort - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - - $ hg log --template '{node|short} {desc}' --graph - @ 652413bf663e f - | - o e860deea161a e - | - o 055a42cdd887 d - | - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - -Multiple exec commands must work - - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > exec exit 0 - > pick e860deea161a e - > exec exit 0 - > exec exit 0 - > exec exit 0 - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 2 files removed, 0 files unresolved - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - - $ hg log --template '{node|short} {desc}' --graph - @ 652413bf663e f - | - o e860deea161a e - | - o 055a42cdd887 d - | - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - -abort on a failing command, e.g when we have children - - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > pick e860deea161a e - > exec echo "added" > added && hg add added && hg commit --amend - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - abort: cannot amend changeset with children - - $ hg histedit --abort - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - -test 'execr' executing in the current directory - - $ hg up 5 - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ mkdir subdir - $ cd subdir - $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle - > pick 177f92b77385 c - > pick 055a42cdd887 d - > execr echo "added long" >> subfile - > pick e860deea161a e - > execr echo "added short" >> subfile - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 2 files removed, 0 files unresolved - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ ls - subfile - $ cat subfile - added long - added short - $ cd .. - -Test that we can recover exec with fbamend on - $ cat >> .hg/hgrc < [extensions] - > fbamend=$TESTDIR/../hgext3rd/fbamend - > [experimental] - > evolution=createmarkers, allowunstable - > EOF - - $ hg up -q tip - -Test continue a stopped fbamend histedit - - $ hg log -G --hidden -T '{node|short} {desc|firstline}\n' - @ 652413bf663e f - | - o e860deea161a e - | - o 055a42cdd887 d - | - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - $ hg histedit 055a42cdd887 --commands - 2>&1 << EOF - > pick e860deea161a e - > pick 055a42cdd887 d - > exec false - > pick 652413bf663e f - > EOF - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - Command 'false' failed with exit status 1 - [1] - $ echo d >> d - $ hg log -G --hidden -T '{node|short} {desc|firstline}\n' - @ 883a5225844a d - | - o d8249471110a e - | - | o 652413bf663e f - | | - | o e860deea161a e - | | - | o 055a42cdd887 d - |/ - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - $ hg commit --amend -m d - $ hg log -G --hidden -T '{node|short} {desc|firstline}\n' - @ 8800a5180f91 d - | - | x 883a5225844a d - |/ - o d8249471110a e - | - | o 652413bf663e f - | | - | o e860deea161a e - | | - | o 055a42cdd887 d - |/ - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - $ hg histedit --continue --traceback - $ hg log -G --hidden -T '{node|short} {desc|firstline}\n' - @ 0d9a4961b100 f - | - o 8800a5180f91 d - | - | x 883a5225844a d - |/ - o d8249471110a e - | - | x 652413bf663e f - | | - | x e860deea161a e - | | - | x 055a42cdd887 d - |/ - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - -Test abort a stopped histedit with obsmarkers - - $ hg histedit d8249471110a --commands - 2>&1 << EOF - > pick 8800a5180f91 d - > stop d8249471110a e - > exec false - > pick 0d9a4961b100 f - > EOF - Changes committed as 1d7ed205640e. You may amend the changeset now. - When you are done, run hg histedit --continue to resume - [1] - $ hg histedit --edit-plan - $ hg histedit --continue - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - Command 'false' failed with exit status 1 - [1] - $ echo e >> e - $ hg commit --amend -m e - $ hg histedit --abort --traceback - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - saved backup bundle to $TESTTMP/r/.hg/strip-backup/abce911bf7cf-fc24e0a4-backup.hg (glob) - $ hg log -G --hidden -T '{node|short} {desc|firstline}\n' - @ 0d9a4961b100 f - | - o 8800a5180f91 d - | - | x 883a5225844a d - |/ - o d8249471110a e - | - | x 652413bf663e f - | | - | x e860deea161a e - | | - | x 055a42cdd887 d - |/ - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - -Test amend inside exec rule: - - $ hg log -G -T '{node|short} {desc|firstline}\n' - @ 0d9a4961b100 f - | - o 8800a5180f91 d - | - o d8249471110a e - | - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - - - $ hg histedit 8800a5180f91 --commands - 2>&1 << EOF - > pick 8800a5180f91 d - > exec hg commit --amend -m "d (amended)" - > pick 0d9a4961b100 f - > EOF - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - warning: the changeset's children were left behind - (this is okay since a histedit is in progress) - - $ hg log -G -T '{node|short} {desc|firstline}\n' - @ 5aeafddb5246 f - | - o 6bd17118649c d (amended) - | - o d8249471110a e - | - o 177f92b77385 c - | - o d2ae7f538514 b - | - o cb9a9f314b8b a - diff --git a/tests/test-sqldirstate-shelve.t b/tests/test-sqldirstate-shelve.t deleted file mode 100644 --- a/tests/test-sqldirstate-shelve.t +++ /dev/null @@ -1,1524 +0,0 @@ - $ cat <> $HGRCPATH - > [extensions] - > mq = - > shelve = - > sqldirstate=$TESTDIR/../sqldirstate - > [defaults] - > diff = --nodates --git - > qnew = --date '0 0' - > [shelve] - > maxbackups = 2 - > EOF - - $ hg init --config format.sqldirstate=True repo - $ cd repo - $ mkdir a b - $ echo a > a/a - $ echo b > b/b - $ echo c > c - $ echo d > d - $ echo x > x - $ hg addremove -q - -shelve has a help message - $ hg shelve -h - hg shelve [OPTION]... [FILE]... - - save and set aside changes from the working directory - - Shelving takes files that "hg status" reports as not clean, saves the - modifications to a bundle (a shelved change), and reverts the files so - that their state in the working directory becomes clean. - - To restore these changes to the working directory, using "hg unshelve"; - this will work even if you switch to a different commit. - - When no files are specified, "hg shelve" saves all not-clean files. If - specific files or directories are named, only changes to those files are - shelved. - - In bare shelve (when no files are specified, without interactive, include - and exclude option), shelving remembers information if the working - directory was on newly created branch, in other words working directory - was on different branch than its first parent. In this situation - unshelving restores branch information to the working directory. - - Each shelved change has a name that makes it easier to find later. The - name of a shelved change defaults to being based on the active bookmark, - or if there is no active bookmark, the current named branch. To specify a - different name, use "--name". - - To see a list of existing shelved changes, use the "--list" option. For - each shelved change, this will print its name, age, and description; use " - --patch" or "--stat" for more details. - - To delete specific shelved changes, use "--delete". To delete all shelved - changes, use "--cleanup". - - (use 'hg help -e shelve' to show help for the shelve extension) - - options ([+] can be repeated): - - -A --addremove mark new/missing files as added/removed before - shelving - -u --unknown store unknown files in the shelve - --cleanup delete all shelved changes - --date DATE shelve with the specified commit date - -d --delete delete the named shelved change(s) - -e --edit invoke editor on commit messages - -l --list list current shelves - -m --message TEXT use text as shelve message - -n --name NAME use the given name for the shelved commit - -p --patch show patch - -i --interactive interactive mode, only works while creating a shelve - --stat output diffstat-style summary of changes - -I --include PATTERN [+] include names matching the given patterns - -X --exclude PATTERN [+] exclude names matching the given patterns - --mq operate on patch repository - - (some details hidden, use --verbose to show complete help) - -shelving in an empty repo should be possible -(this tests also that editor is not invoked, if '--edit' is not -specified) - - $ HGEDITOR=cat hg shelve - shelved as default - 0 files updated, 0 files merged, 5 files removed, 0 files unresolved - - $ hg unshelve - unshelving change 'default' - - $ hg commit -q -m 'initial commit' - - $ hg shelve - nothing changed - [1] - -make sure shelve files were backed up - - $ ls .hg/shelve-backup - default.hg - default.patch - -create an mq patch - shelving should work fine with a patch applied - - $ echo n > n - $ hg add n - $ hg commit n -m second - $ hg qnew second.patch - -shelve a change that we will delete later - - $ echo a >> a/a - $ hg shelve - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - -set up some more complex changes to shelve - - $ echo a >> a/a - $ hg mv b b.rename - moving b/b to b.rename/b (glob) - $ hg cp c c.copy - $ hg status -C - M a/a - A b.rename/b - b/b - A c.copy - c - R b/b - -prevent some foot-shooting - - $ hg shelve -n foo/bar - abort: shelved change names can not contain slashes - [255] - $ hg shelve -n .baz - abort: shelved change names can not start with '.' - [255] - -the common case - no options or filenames - - $ hg shelve - shelved as default-01 - 2 files updated, 0 files merged, 2 files removed, 0 files unresolved - $ hg status -C - -ensure that our shelved changes exist - - $ hg shelve -l - default-01 * changes to: [mq]: second.patch (glob) - default * changes to: [mq]: second.patch (glob) - - $ hg shelve -l -p default - default * changes to: [mq]: second.patch (glob) - - diff --git a/a/a b/a/a - --- a/a/a - +++ b/a/a - @@ -1,1 +1,2 @@ - a - +a - - $ hg shelve --list --addremove - abort: options '--list' and '--addremove' may not be used together - [255] - -delete our older shelved change - - $ hg shelve -d default - $ hg qfinish -a -q - -ensure shelve backups aren't overwritten - - $ ls .hg/shelve-backup/ - default-1.hg - default-1.patch - default.hg - default.patch - -local edits should not prevent a shelved change from applying - - $ printf "z\na\n" > a/a - $ hg unshelve --keep - unshelving change 'default-01' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 4:32c69314e062 "changes to: [mq]: second.patch" (tip) - merging a/a - - $ hg revert --all -q - $ rm a/a.orig b.rename/b c.copy - -apply it and make sure our state is as expected - -(this also tests that same timestamp prevents backups from being -removed, even though there are more than 'maxbackups' backups) - - $ f -t .hg/shelve-backup/default.patch - .hg/shelve-backup/default.patch: file - $ touch -t 200001010000 .hg/shelve-backup/default.patch - $ f -t .hg/shelve-backup/default-1.patch - .hg/shelve-backup/default-1.patch: file - $ touch -t 200001010000 .hg/shelve-backup/default-1.patch - - $ hg unshelve - unshelving change 'default-01' - $ hg status -C - M a/a - A b.rename/b - b/b - A c.copy - c - R b/b - $ hg shelve -l - -(both of default.hg and default-1.hg should be still kept, because it -is difficult to decide actual order of them from same timestamp) - - $ ls .hg/shelve-backup/ - default-01.hg - default-01.patch - default-1.hg - default-1.patch - default.hg - default.patch - - $ hg unshelve - abort: no shelved changes to apply! - [255] - $ hg unshelve foo - abort: shelved change 'foo' not found - [255] - -named shelves, specific filenames, and "commit messages" should all work -(this tests also that editor is invoked, if '--edit' is specified) - - $ hg status -C - M a/a - A b.rename/b - b/b - A c.copy - c - R b/b - $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a - wat - - - HG: Enter commit message. Lines beginning with 'HG:' are removed. - HG: Leave message empty to abort commit. - HG: -- - HG: user: shelve@localhost - HG: branch 'default' - HG: changed a/a - -expect "a" to no longer be present, but status otherwise unchanged - - $ hg status -C - A b.rename/b - b/b - A c.copy - c - R b/b - $ hg shelve -l --stat - wibble * wat (glob) - a/a | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - -and now "a/a" should reappear - - $ cd a - $ hg unshelve -q wibble - $ cd .. - $ hg status -C - M a/a - A b.rename/b - b/b - A c.copy - c - R b/b - -ensure old shelve backups are being deleted automatically - - $ ls .hg/shelve-backup/ - default-01.hg - default-01.patch - wibble.hg - wibble.patch - -cause unshelving to result in a merge with 'a' conflicting - - $ hg shelve -q - $ echo c>>a/a - $ hg commit -m second - $ hg tip --template '{files}\n' - a/a - -add an unrelated change that should be preserved - - $ mkdir foo - $ echo foo > foo/foo - $ hg add foo/foo - -force a conflicted merge to occur - - $ hg unshelve - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip) - merging a/a - warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - -ensure that we have a merge with unresolved conflicts - - $ hg heads -q --template '{rev}\n' - 5 - 4 - $ hg parents -q --template '{rev}\n' - 4 - 5 - $ hg status - M a/a - M b.rename/b - M c.copy - R b/b - ? a/a.orig - $ hg diff - diff --git a/a/a b/a/a - --- a/a/a - +++ b/a/a - @@ -1,2 +1,6 @@ - a - +<<<<<<< dest: * - shelve: pending changes temporary commit (glob) - c - +======= - +a - +>>>>>>> source: 32c69314e062 - shelve: changes to: [mq]: second.patch - diff --git a/b/b b/b.rename/b - rename from b/b - rename to b.rename/b - diff --git a/c b/c.copy - copy from c - copy to c.copy - $ hg resolve -l - U a/a - - $ hg shelve - abort: unshelve already in progress - (use 'hg unshelve --continue' or 'hg unshelve --abort') - [255] - -abort the unshelve and be happy - - $ hg status - M a/a - M b.rename/b - M c.copy - R b/b - ? a/a.orig - $ hg unshelve -a - rebase aborted - unshelve of 'default' aborted - $ hg heads -q - 3:2e69b451d1ea - $ hg parents - changeset: 3:2e69b451d1ea - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: second - - $ hg resolve -l - $ hg status - A foo/foo - ? a/a.orig - -try to continue with no unshelve underway - - $ hg unshelve -c - abort: no unshelve in progress - [255] - $ hg status - A foo/foo - ? a/a.orig - -redo the unshelve to get a conflict - - $ hg unshelve -q - warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - -attempt to continue - - $ hg unshelve -c - abort: unresolved conflicts, can't continue - (see 'hg resolve', then 'hg unshelve --continue') - [255] - - $ hg revert -r . a/a - $ hg resolve -m a/a - (no more unresolved files) - continue: hg unshelve --continue - - $ hg commit -m 'commit while unshelve in progress' - abort: unshelve already in progress - (use 'hg unshelve --continue' or 'hg unshelve --abort') - [255] - - $ hg graft --continue - abort: no graft in progress - (continue: hg unshelve --continue) - [255] - $ hg unshelve -c - rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip) - unshelve of 'default' complete - -ensure the repo is as we hope - - $ hg parents - changeset: 3:2e69b451d1ea - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: second - - $ hg heads -q - 3:2e69b451d1ea - - $ hg status -C - A b.rename/b - b/b - A c.copy - c - A foo/foo - R b/b - ? a/a.orig - -there should be no shelves left - - $ hg shelve -l - -#if execbit - -ensure that metadata-only changes are shelved - - $ chmod +x a/a - $ hg shelve -q -n execbit a/a - $ hg status a/a - $ hg unshelve -q execbit - $ hg status a/a - M a/a - $ hg revert a/a - -#endif - -#if symlink - - $ rm a/a - $ ln -s foo a/a - $ hg shelve -q -n symlink a/a - $ hg status a/a - $ hg unshelve -q symlink - $ hg status a/a - M a/a - $ hg revert a/a - -#endif - -set up another conflict between a commit and a shelved change - - $ hg revert -q -C -a - $ rm a/a.orig b.rename/b c.copy - $ echo a >> a/a - $ hg shelve -q - $ echo x >> a/a - $ hg ci -m 'create conflict' - $ hg add foo/foo - -if we resolve a conflict while unshelving, the unshelve should succeed - - $ hg unshelve --tool :merge-other --keep - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 6:2f694dd83a13 "changes to: second" (tip) - merging a/a - $ hg parents -q - 4:33f7f61e6c5e - $ hg shelve -l - default * changes to: second (glob) - $ hg status - M a/a - A foo/foo - $ cat a/a - a - c - a - $ cat > a/a << EOF - > a - > c - > x - > EOF - - $ HGMERGE=true hg unshelve - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 6:2f694dd83a13 "changes to: second" (tip) - merging a/a - note: rebase of 6:2f694dd83a13 created no changes to commit - $ hg parents -q - 4:33f7f61e6c5e - $ hg shelve -l - $ hg status - A foo/foo - $ cat a/a - a - c - x - -test keep and cleanup - - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg shelve --list - default * changes to: create conflict (glob) - $ hg unshelve -k - unshelving change 'default' - $ hg shelve --list - default * changes to: create conflict (glob) - $ hg shelve --cleanup - $ hg shelve --list - - $ hg shelve --cleanup --delete - abort: options '--cleanup' and '--delete' may not be used together - [255] - $ hg shelve --cleanup --patch - abort: options '--cleanup' and '--patch' may not be used together - [255] - $ hg shelve --cleanup --message MESSAGE - abort: options '--cleanup' and '--message' may not be used together - [255] - -test bookmarks - - $ hg bookmark test - $ hg bookmark - * test 4:33f7f61e6c5e - $ hg shelve - shelved as test - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg bookmark - * test 4:33f7f61e6c5e - $ hg unshelve - unshelving change 'test' - $ hg bookmark - * test 4:33f7f61e6c5e - -shelve should still work even if mq is disabled - - $ hg --config extensions.mq=! shelve - shelved as test - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg --config extensions.mq=! shelve --list - test * changes to: create conflict (glob) - $ hg bookmark - * test 4:33f7f61e6c5e - $ hg --config extensions.mq=! unshelve - unshelving change 'test' - $ hg bookmark - * test 4:33f7f61e6c5e - -shelve should leave dirstate clean (issue4055) - - $ cd .. - $ hg init shelverebase - $ cd shelverebase - $ printf 'x\ny\n' > x - $ echo z > z - $ hg commit -Aqm xy - $ echo z >> x - $ hg commit -Aqm z - $ hg up 0 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ printf 'a\nx\ny\nz\n' > x - $ hg commit -Aqm xyz - $ echo c >> z - $ hg shelve - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg rebase -d 1 --config extensions.rebase= - rebasing 2:323bfa07f744 "xyz" (tip) - merging x - saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-rebase.hg (glob) - $ hg unshelve - unshelving change 'default' - rebasing shelved changes - rebasing 4:82a0d7d6ba61 "changes to: xyz" (tip) - $ hg status - M z - - $ cd .. - -shelve should only unshelve pending changes (issue4068) - - $ hg init onlypendingchanges - $ cd onlypendingchanges - $ touch a - $ hg ci -Aqm a - $ touch b - $ hg ci -Aqm b - $ hg up -q 0 - $ touch c - $ hg ci -Aqm c - - $ touch d - $ hg add d - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg up -q 1 - $ hg unshelve - unshelving change 'default' - rebasing shelved changes - rebasing 3:958bcbd1776e "changes to: c" (tip) - $ hg status - A d - -unshelve should work on an ancestor of the original commit - - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg unshelve - unshelving change 'default' - rebasing shelved changes - rebasing 3:013284d9655e "changes to: b" (tip) - $ hg status - A d - -test bug 4073 we need to enable obsolete markers for it - - $ cat >> $HGRCPATH << EOF - > [experimental] - > evolution=createmarkers - > EOF - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg debugobsolete `hg --debug id -i -r 1` - obsoleted 1 changesets - $ hg unshelve - unshelving change 'default' - -unshelve should leave unknown files alone (issue4113) - - $ echo e > e - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg status - ? e - $ hg unshelve - unshelving change 'default' - $ hg status - A d - ? e - $ cat e - e - -unshelve should keep a copy of unknown files - - $ hg add e - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 2 files removed, 0 files unresolved - $ echo z > e - $ hg unshelve - unshelving change 'default' - $ cat e - e - $ cat e.orig - z - - -unshelve and conflicts with tracked and untracked files - - preparing: - - $ rm *.orig - $ hg ci -qm 'commit stuff' - $ hg phase -p null: - - no other changes - no merge: - - $ echo f > f - $ hg add f - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ echo g > f - $ hg unshelve - unshelving change 'default' - $ hg st - A f - ? f.orig - $ cat f - f - $ cat f.orig - g - - other uncommitted changes - merge: - - $ hg st - A f - ? f.orig - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()' - o 4 changes to: commit stuff shelve@localhost - | - ~ - $ hg log -G --template '{rev} {desc|firstline} {author}' - @ 3 commit stuff test - | - | o 2 c test - |/ - o 0 a test - - $ mv f.orig f - $ echo 1 > a - $ hg unshelve --date '1073741824 0' - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 5:81152db69da7 "changes to: commit stuff" (tip) - merging f - warning: conflicts while merging f! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}' - @ 5 changes to: commit stuff shelve@localhost 1970-01-01 00:00 +0000 - | - | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000 - |/ - o 3 commit stuff test 1970-01-01 00:00 +0000 - | - | o 2 c test 1970-01-01 00:00 +0000 - |/ - o 0 a test 1970-01-01 00:00 +0000 - - $ hg st - M f - ? f.orig - $ cat f - <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit - g - ======= - f - >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff - $ cat f.orig - g - $ hg unshelve --abort -t false - tool option will be ignored - rebase aborted - unshelve of 'default' aborted - $ hg st - M a - ? f.orig - $ cat f.orig - g - $ hg unshelve - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 5:81152db69da7 "changes to: commit stuff" (tip) - $ hg st - M a - A f - ? f.orig - - other committed changes - merge: - - $ hg shelve f - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg ci a -m 'intermediate other change' - $ mv f.orig f - $ hg unshelve - unshelving change 'default' - rebasing shelved changes - rebasing 5:81152db69da7 "changes to: commit stuff" (tip) - merging f - warning: conflicts while merging f! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ hg st - M f - ? f.orig - $ cat f - <<<<<<< dest: * - test: intermediate other change (glob) - g - ======= - f - >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff - $ cat f.orig - g - $ hg unshelve --abort - rebase aborted - unshelve of 'default' aborted - $ hg st - ? f.orig - $ cat f.orig - g - $ hg shelve --delete default - -Recreate some conflict again - - $ cd ../repo - $ hg up -C -r 3 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (leaving bookmark test) - $ echo y >> a/a - $ hg shelve - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg up test - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (activating bookmark test) - $ hg bookmark - * test 4:33f7f61e6c5e - $ hg unshelve - unshelving change 'default' - rebasing shelved changes - rebasing 5:e42a7da90865 "changes to: second" (tip) - merging a/a - warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ hg bookmark - test 4:33f7f61e6c5e - -Test that resolving all conflicts in one direction (so that the rebase -is a no-op), works (issue4398) - - $ hg revert -a -r . - reverting a/a (glob) - $ hg resolve -m a/a - (no more unresolved files) - continue: hg unshelve --continue - $ hg unshelve -c - rebasing 5:e42a7da90865 "changes to: second" (tip) - note: rebase of 5:e42a7da90865 created no changes to commit - unshelve of 'default' complete - $ hg bookmark - * test 4:33f7f61e6c5e - $ hg diff - $ hg status -C - ? a/a.orig - ? foo/foo - $ hg summary - parent: 4:33f7f61e6c5e tip - create conflict - branch: default - bookmarks: *test - commit: 2 unknown (clean) - update: (current) - phases: 5 draft - - $ hg shelve --delete --stat - abort: options '--delete' and '--stat' may not be used together - [255] - $ hg shelve --delete --name NAME - abort: options '--delete' and '--name' may not be used together - [255] - -Test interactive shelve - $ cat <> $HGRCPATH - > [ui] - > interactive = true - > EOF - $ echo 'a' >> a/b - $ cat a/a >> a/b - $ echo 'x' >> a/b - $ mv a/b a/a - $ echo 'a' >> foo/foo - $ hg st - M a/a - ? a/a.orig - ? foo/foo - $ cat a/a - a - a - c - x - x - $ cat foo/foo - foo - a - $ hg shelve --interactive --config ui.interactive=false - abort: running non-interactively - [255] - $ hg shelve --interactive << EOF - > y - > y - > n - > EOF - diff --git a/a/a b/a/a - 2 hunks, 2 lines changed - examine changes to 'a/a'? [Ynesfdaq?] y - - @@ -1,3 +1,4 @@ - +a - a - c - x - record change 1/2 to 'a/a'? [Ynesfdaq?] y - - @@ -1,3 +2,4 @@ - a - c - x - +x - record change 2/2 to 'a/a'? [Ynesfdaq?] n - - shelved as test - merging a/a - 0 files updated, 1 files merged, 0 files removed, 0 files unresolved - $ cat a/a - a - c - x - x - $ cat foo/foo - foo - a - $ hg st - M a/a - ? foo/foo - $ hg bookmark - * test 4:33f7f61e6c5e - $ hg unshelve - unshelving change 'test' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 6:96a1354f65f6 "changes to: create conflict" (tip) - merging a/a - $ hg bookmark - * test 4:33f7f61e6c5e - $ cat a/a - a - a - c - x - x - -shelve --patch and shelve --stat should work with a single valid shelfname - - $ hg up --clean . - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (leaving bookmark test) - $ hg shelve --list - $ echo 'patch a' > shelf-patch-a - $ hg add shelf-patch-a - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ echo 'patch b' > shelf-patch-b - $ hg add shelf-patch-b - $ hg shelve - shelved as default-01 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg shelve --patch default default-01 - default-01 (* ago) changes to: create conflict (glob) - - diff --git a/shelf-patch-b b/shelf-patch-b - new file mode 100644 - --- /dev/null - +++ b/shelf-patch-b - @@ -0,0 +1,1 @@ - +patch b - default * changes to: create conflict (glob) - - diff --git a/shelf-patch-a b/shelf-patch-a - new file mode 100644 - --- /dev/null - +++ b/shelf-patch-a - @@ -0,0 +1,1 @@ - +patch a - $ hg shelve --stat default default-01 - default-01 * changes to: create conflict (glob) - shelf-patch-b | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - default * changes to: create conflict (glob) - shelf-patch-a | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - $ hg shelve --patch default - default * changes to: create conflict (glob) - - diff --git a/shelf-patch-a b/shelf-patch-a - new file mode 100644 - --- /dev/null - +++ b/shelf-patch-a - @@ -0,0 +1,1 @@ - +patch a - $ hg shelve --stat default - default * changes to: create conflict (glob) - shelf-patch-a | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - $ hg shelve --patch nonexistentshelf - abort: cannot find shelf nonexistentshelf - [255] - $ hg shelve --stat nonexistentshelf - abort: cannot find shelf nonexistentshelf - [255] - - $ cd .. - -Shelve from general delta repo uses bundle2 on disk --------------------------------------------------- - -no general delta - - $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0 - requesting all changes - adding changesets - adding manifests - adding file changes - added 5 changesets with 8 changes to 6 files - updating to branch default - 6 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd bundle1 - $ echo babar > jungle - $ hg add jungle - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg debugbundle .hg/shelved/*.hg - 45993d65fe9dc3c6d8764b9c3b07fa831ee7d92d - $ cd .. - -with general delta - - $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1 - requesting all changes - adding changesets - adding manifests - adding file changes - added 5 changesets with 8 changes to 6 files - updating to branch default - 6 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd bundle2 - $ echo babar > jungle - $ hg add jungle - $ hg shelve - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg debugbundle .hg/shelved/*.hg - Stream params: {Compression: BZ} - changegroup -- {nbchanges: 1, version: 02} - 45993d65fe9dc3c6d8764b9c3b07fa831ee7d92d - $ cd .. - -Test visibility of in-memory changes inside transaction to external hook ------------------------------------------------------------------------- - - $ cd repo - - $ echo xxxx >> x - $ hg commit -m "#5: changes to invoke rebase" - - $ cat >> .hg/hgrc < [defaults] - > # to fix hash id of temporary revisions - > unshelve = --date '0 0' - > EOF - -"hg unshelve" at REV5 implies steps below: - -(1) commit changes in the working directory (REV6) -(2) unbundle shelved revision (REV7) -(3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7) -(4) rebase: commit merged revision (REV8) -(5) rebase: update to REV6 (REV8 => REV6) -(6) update to REV5 (REV6 => REV5) -(7) abort transaction - - $ echo nnnn >> n - - $ hg unshelve --keep default - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 7:206bf5d4f922 "changes to: create conflict" (tip) - - $ hg update -q -C 5 - - - $ echo nnnn >> n - - $ hg unshelve --keep default - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 7:206bf5d4f922 "changes to: create conflict" (tip) - $ cd .. - -test .orig files go where the user wants them to ---------------------------------------------------------------- - $ hg init salvage - $ cd salvage - $ echo 'content' > root - $ hg commit -A -m 'root' -q - $ echo '' > root - $ hg shelve -q - $ echo 'contADDent' > root - $ hg unshelve -q --config 'ui.origbackuppath=.hg/origbackups' - warning: conflicts while merging root! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ ls .hg/origbackups - root - $ rm -rf .hg/origbackups - -test Abort unshelve always gets user out of the unshelved state ---------------------------------------------------------------- -Wreak havoc on the unshelve process - $ rm .hg/unshelverebasestate - $ hg unshelve --abort - unshelve of 'default' aborted - abort: (No such file or directory|The system cannot find the file specified) (re) - [255] -Can the user leave the current state? - $ hg up -C . - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - -Try again but with a corrupted shelve state file - $ hg strip -r 2 -r 1 -q - $ hg up -r 0 -q - $ echo '' > root - $ hg shelve -q - $ echo 'contADDent' > root - $ hg unshelve -q - warning: conflicts while merging root! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate - $ mv ../corrupt-shelvedstate .hg/histedit-state - $ hg unshelve --abort 2>&1 | grep 'rebase aborted' - rebase aborted - $ hg up -C . - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - - $ cd .. - -Keep active bookmark while (un)shelving even on shared repo (issue4940) ------------------------------------------------------------------------ - - $ cat <> $HGRCPATH - > [extensions] - > share = - > EOF - - $ hg bookmarks -R repo - test 4:33f7f61e6c5e - $ hg share -B repo share - updating working directory - 6 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd share - - $ hg bookmarks - test 4:33f7f61e6c5e - $ hg bookmarks foo - $ hg bookmarks - * foo 5:703117a2acfb - test 4:33f7f61e6c5e - $ echo x >> x - $ hg shelve - shelved as foo - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg bookmarks - * foo 5:703117a2acfb - test 4:33f7f61e6c5e - - $ hg unshelve - unshelving change 'foo' - $ hg bookmarks - * foo 5:703117a2acfb - test 4:33f7f61e6c5e - - $ cd .. - -Shelve and unshelve unknown files. For the purposes of unshelve, a shelved -unknown file is the same as a shelved added file, except that it will be in -unknown state after unshelve if and only if it was either absent or unknown -before the unshelve operation. - - $ hg init unknowns - $ cd unknowns - -The simplest case is if I simply have an unknown file that I shelve and unshelve - - $ echo unknown > unknown - $ hg status - ? unknown - $ hg shelve --unknown - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg status - $ hg unshelve - unshelving change 'default' - $ hg status - ? unknown - $ rm unknown - -If I shelve, add the file, and unshelve, does it stay added? - - $ echo unknown > unknown - $ hg shelve -u - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg status - $ touch unknown - $ hg add unknown - $ hg status - A unknown - $ hg unshelve - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 1:098df96e7410 "(changes in empty repository)" (tip) - merging unknown - $ hg status - A unknown - $ hg forget unknown - $ rm unknown - -And if I shelve, commit, then unshelve, does it become modified? - - $ echo unknown > unknown - $ hg shelve -u - shelved as default - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg status - $ touch unknown - $ hg add unknown - $ hg commit -qm "Add unknown" - $ hg status - $ hg unshelve - unshelving change 'default' - rebasing shelved changes - rebasing 1:098df96e7410 "(changes in empty repository)" (tip) - merging unknown - $ hg status - M unknown - $ hg remove --force unknown - $ hg commit -qm "Remove unknown" - - $ cd .. - -We expects that non-bare shelve keeps newly created branch in -working directory. - - $ hg init shelve-preserve-new-branch - $ cd shelve-preserve-new-branch - $ echo "a" >> a - $ hg add a - $ echo "b" >> b - $ hg add b - $ hg commit -m "ab" - $ echo "aa" >> a - $ echo "bb" >> b - $ hg branch new-branch - marked working directory as branch new-branch - (branches are permanent and global, did you want a bookmark?) - $ hg status - M a - M b - $ hg branch - new-branch - $ hg shelve a - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg branch - new-branch - $ hg status - M b - $ touch "c" >> c - $ hg add c - $ hg status - M b - A c - $ hg shelve --exclude c - shelved as default-01 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg branch - new-branch - $ hg status - A c - $ hg shelve --include c - shelved as default-02 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg branch - new-branch - $ hg status - $ echo "d" >> d - $ hg add d - $ hg status - A d - -We expect that bare-shelve will not keep branch in current working directory. - - $ hg shelve - shelved as default-03 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg branch - default - -When i shelve commit on newly created branch i expect -that after unshelve newly created branch will be preserved. - - $ hg init shelve_on_new_branch_simple - $ cd shelve_on_new_branch_simple - $ echo "aaa" >> a - $ hg commit -A -m "a" - adding a - $ hg branch - default - $ hg branch test - marked working directory as branch test - (branches are permanent and global, did you want a bookmark?) - $ echo "bbb" >> a - $ hg status - M a - $ hg shelve - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg branch - default - $ echo "bbb" >> b - $ hg status - ? b - $ hg unshelve - unshelving change 'default' - marked working directory as branch test - $ hg status - M a - ? b - $ hg branch - test - -When i shelve commit on newly created branch, make -some changes, unshelve it and running into merge -conflicts i expect that after fixing them and -running unshelve --continue newly created branch -will be preserved. - - $ hg init shelve_on_new_branch_conflict - $ cd shelve_on_new_branch_conflict - $ echo "aaa" >> a - $ hg commit -A -m "a" - adding a - $ hg branch - default - $ hg branch test - marked working directory as branch test - (branches are permanent and global, did you want a bookmark?) - $ echo "bbb" >> a - $ hg status - M a - $ hg shelve - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg branch - default - $ echo "ccc" >> a - $ hg status - M a - $ hg unshelve - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 2:425c97ef07f3 "changes to: a" (tip) - merging a - warning: conflicts while merging a! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - $ echo "aaabbbccc" > a - $ rm a.orig - $ hg resolve --mark a - (no more unresolved files) - continue: hg unshelve --continue - $ hg unshelve --continue - rebasing 2:425c97ef07f3 "changes to: a" (tip) - marked working directory as branch test - unshelve of 'default' complete - $ cat a - aaabbbccc - $ hg status - M a - $ hg branch - test - $ hg commit -m "test-commit" - -When i shelve on test branch, update to default branch -and unshelve i expect that it will not preserve previous -test branch. - - $ echo "xxx" > b - $ hg add b - $ hg shelve - shelved as test - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ hg update -r default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg unshelve - unshelving change 'test' - rebasing shelved changes - rebasing 2:357525f34729 "changes to: test-commit" (tip) - $ hg status - A b - $ hg branch - default - -When i unshelve resulting in merge conflicts and makes saved -file shelvedstate looks like in previous versions in -mercurial(without restore branch information in 7th line) i -expect that after resolving conflicts and succesfully -running 'shelve --continue' the branch information won't be -restored and branch will be unchanged. - -shelve on new branch, conflict with previous shelvedstate - - $ hg init conflict - $ cd conflict - $ echo "aaa" >> a - $ hg commit -A -m "a" - adding a - $ hg branch - default - $ hg branch test - marked working directory as branch test - (branches are permanent and global, did you want a bookmark?) - $ echo "bbb" >> a - $ hg status - M a - $ hg shelve - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg branch - default - $ echo "ccc" >> a - $ hg status - M a - $ hg unshelve - unshelving change 'default' - temporarily committing pending changes (restore with 'hg unshelve --abort') - rebasing shelved changes - rebasing 2:425c97ef07f3 "changes to: a" (tip) - merging a - warning: conflicts while merging a! (edit, then use 'hg resolve --mark') - unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue') - [1] - -Removing restore branch information from shelvedstate file(making it looks like -in previous versions) and running unshelve --continue - - $ cat .hg/shelvedstate | grep -v "branchtorestore" > .hg/shelvedstate_old - $ mv .hg/shelvedstate_old .hg/shelvedstate - - $ echo "aaabbbccc" > a - $ rm a.orig - $ hg resolve --mark a - (no more unresolved files) - continue: hg unshelve --continue - $ hg unshelve --continue - rebasing 2:425c97ef07f3 "changes to: a" (tip) - unshelve of 'default' complete - $ cat a - aaabbbccc - $ hg status - M a - $ hg branch - default - -On non bare shelve the branch information shouldn't be restored - - $ hg init bare_shelve_on_new_branch - $ cd bare_shelve_on_new_branch - $ echo "aaa" >> a - $ hg commit -A -m "a" - adding a - $ hg branch - default - $ hg branch test - marked working directory as branch test - (branches are permanent and global, did you want a bookmark?) - $ echo "bbb" >> a - $ hg status - M a - $ hg shelve a - shelved as default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg branch - test - $ hg branch default - marked working directory as branch default - (branches are permanent and global, did you want a bookmark?) - $ echo "bbb" >> b - $ hg status - ? b - $ hg unshelve - unshelving change 'default' - $ hg status - M a - ? b - $ hg branch - default diff --git a/tests/test-sqldirstate-upgrade.t b/tests/test-sqldirstate-upgrade.t deleted file mode 100644 --- a/tests/test-sqldirstate-upgrade.t +++ /dev/null @@ -1,59 +0,0 @@ -Init repo - - $ cat <> $HGRCPATH - > [extensions] - > sqldirstate=$TESTDIR/../sqldirstate - > EOF - $ hg init repo - $ cd repo - $ mkdir a b - $ echo a > a/a - $ echo b > b/b - $ echo c > c - $ echo d > d - $ echo x > x - $ hg addremove -q - $ hg st - A a/a - A b/b - A c - A d - A x - -Test automatic upgrade on pull - - $ cat <> $HGRCPATH - > [sqldirstate] - > upgrade = True - > EOF - $ hg pull - migrating your repo to sqldirstate which will make your hg commands faster... - pulling from default - abort: repository default not found! - [255] - $ ls .hg/dirstate* - .hg/dirstate - .hg/dirstate.sqlite3 - $ hg st - A a/a - A b/b - A c - A d - A x - $ hg pull - pulling from default - abort: repository default not found! - [255] - -Test conversions using debugcommands - - $ hg commit -m a - $ hg st - $ hg debugsqldirstate off - $ hg st - $ hg debugsqldirstate on - $ hg st - $ hg debugsqldirstate on - abort: repo already has sqldirstate - [255] -