forked from mirrors/gecko-dev
		
	 0c81030974
			
		
	
	
		0c81030974
		
	
	
	
	
		
			
			--HG-- rename : third_party/python/hglib/LICENSE => third_party/python/python-hglib/LICENSE rename : third_party/python/hglib/hglib/__init__.py => third_party/python/python-hglib/hglib/__init__.py rename : third_party/python/hglib/hglib/client.py => third_party/python/python-hglib/hglib/client.py rename : third_party/python/hglib/hglib/context.py => third_party/python/python-hglib/hglib/context.py rename : third_party/python/hglib/hglib/error.py => third_party/python/python-hglib/hglib/error.py rename : third_party/python/hglib/hglib/merge.py => third_party/python/python-hglib/hglib/merge.py rename : third_party/python/hglib/hglib/templates.py => third_party/python/python-hglib/hglib/templates.py rename : third_party/python/hglib/hglib/util.py => third_party/python/python-hglib/hglib/util.py rename : third_party/python/hglib/setup.py => third_party/python/python-hglib/setup.py extra : rebase_source : 552d93c9e90c04171c8b627c8f4f4fa5ec506cc3
		
			
				
	
	
		
			1717 lines
		
	
	
	
		
			64 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1717 lines
		
	
	
	
		
			64 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import struct, re, datetime
 | |
| import hglib
 | |
| from hglib import error, util, templates, merge, context
 | |
| 
 | |
| from hglib.util import b, cmdbuilder, BytesIO, strtobytes
 | |
| 
 | |
| class revision(tuple):
 | |
|     def __new__(cls, rev, node, tags, branch, author, desc, date):
 | |
|         return tuple.__new__(cls, (rev, node, tags, branch, author, desc, date))
 | |
| 
 | |
|     @property
 | |
|     def rev(self):
 | |
|         return self[0]
 | |
| 
 | |
|     @property
 | |
|     def node(self):
 | |
|         return self[1]
 | |
| 
 | |
|     @property
 | |
|     def tags(self):
 | |
|         return self[2]
 | |
| 
 | |
|     @property
 | |
|     def branch(self):
 | |
|         return self[3]
 | |
| 
 | |
|     @property
 | |
|     def author(self):
 | |
|         return self[4]
 | |
| 
 | |
|     @property
 | |
|     def desc(self):
 | |
|         return self[5]
 | |
| 
 | |
|     @property
 | |
|     def date(self):
 | |
|         return self[6]
 | |
| 
 | |
| class hgclient(object):
 | |
|     inputfmt = '>I'
 | |
|     outputfmt = '>cI'
 | |
|     outputfmtsize = struct.calcsize(outputfmt)
 | |
|     retfmt = '>i'
 | |
| 
 | |
|     def __init__(self, path, encoding, configs, connect=True):
 | |
|         self._args = [hglib.HGPATH, 'serve', '--cmdserver', 'pipe',
 | |
|                 '--config', 'ui.interactive=True']
 | |
|         if path:
 | |
|             self._args += ['-R', path]
 | |
|         if configs:
 | |
|             for config in configs:
 | |
|                 self._args += ['--config', config]
 | |
|         self._env = {'HGPLAIN': '1'}
 | |
|         if encoding:
 | |
|             self._env['HGENCODING'] = encoding
 | |
| 
 | |
|         self.server = None
 | |
|         self._version = None
 | |
|         # include the hidden changesets if True
 | |
|         self.hidden = None
 | |
| 
 | |
|         self._cbout = None
 | |
|         self._cberr = None
 | |
|         self._cbprompt = None
 | |
| 
 | |
|         if connect:
 | |
|             self.open()
 | |
| 
 | |
|         self._protocoltracefn = None
 | |
| 
 | |
|     def setcbout(self, cbout):
 | |
|         """
 | |
|         cbout is a function that will be called with the stdout data of
 | |
|         the command as it runs. Call with None to stop getting call backs.
 | |
|         """
 | |
|         self._cbout = cbout
 | |
| 
 | |
|     def setcberr(self, cberr):
 | |
|         """
 | |
|         cberr is a function that will be called with the stderr data of
 | |
|         the command as it runs.Call with None to stop getting call backs.
 | |
|         """
 | |
|         self._cberr = cberr
 | |
| 
 | |
|     def setcbprompt(self, cbprompt):
 | |
|         """
 | |
|         cbprompt is used to reply to prompts by the server
 | |
|         It receives the max number of bytes to return and the
 | |
|         contents of stdout received so far.
 | |
| 
 | |
|         Call with None to stop getting call backs.
 | |
| 
 | |
|         cbprompt is never called from merge() or import_()
 | |
|         which already handle the prompt.
 | |
|         """
 | |
|         self._cbprompt = cbprompt
 | |
| 
 | |
|     def setprotocoltrace(self, tracefn=None):
 | |
|         """
 | |
|         if tracefn is None no trace calls will be made.
 | |
|         Otherwise tracefn is call as tracefn( direction, channel, data )
 | |
|         direction is 'r' for read from server and 'w' for write to server
 | |
|         channel is always None when direction is 'w'
 | |
|         and the channel-identified when the direction is 'r'
 | |
|         """
 | |
|         self._protocoltracefn = tracefn
 | |
| 
 | |
|     def __enter__(self):
 | |
|         if self.server is None:
 | |
|             self.open()
 | |
|         return self
 | |
| 
 | |
|     def __exit__(self, exc_type, exc_val, exc_tb):
 | |
|         self.close()
 | |
| 
 | |
|     def _readhello(self):
 | |
|         """ read the hello message the server sends when started """
 | |
|         ch, msg = self._readchannel()
 | |
|         assert ch == b('o')
 | |
| 
 | |
|         msg = msg.split(b('\n'))
 | |
| 
 | |
|         self.capabilities = msg[0][len(b('capabilities: ')):]
 | |
|         if not self.capabilities:
 | |
|             raise error.ResponseError(
 | |
|                 "bad hello message: expected 'capabilities: '"
 | |
|                 ", got %r" % msg[0])
 | |
| 
 | |
|         self.capabilities = set(self.capabilities.split())
 | |
| 
 | |
|         # at the very least the server should be able to run commands
 | |
|         assert b('runcommand') in self.capabilities
 | |
| 
 | |
|         self._encoding = msg[1][len(b('encoding: ')):]
 | |
|         if not self._encoding:
 | |
|             raise error.ResponseError("bad hello message: expected 'encoding: '"
 | |
|                                       ", got %r" % msg[1])
 | |
| 
 | |
|     def _readchannel(self):
 | |
|         data = self.server.stdout.read(hgclient.outputfmtsize)
 | |
|         if not data:
 | |
|             raise error.ServerError()
 | |
|         channel, length = struct.unpack(hgclient.outputfmt, data)
 | |
|         if channel in b('IL'):
 | |
|             return channel, length
 | |
|         else:
 | |
|             return channel, self.server.stdout.read(length)
 | |
| 
 | |
|     @staticmethod
 | |
|     def _parserevs(splitted):
 | |
|         '''splitted is a list of fields according to our rev.style, where
 | |
|         each 6 fields compose one revision.
 | |
|         '''
 | |
|         revs = []
 | |
|         for rev in util.grouper(7, splitted):
 | |
|             # truncate the timezone and convert to a local datetime
 | |
|             posixtime = float(rev[6].split(b('.'), 1)[0])
 | |
|             dt = datetime.datetime.fromtimestamp(posixtime)
 | |
|             revs.append(revision(rev[0], rev[1], rev[2], rev[3],
 | |
|                                  rev[4], rev[5], dt))
 | |
|         return revs
 | |
| 
 | |
|     def runcommand(self, args, inchannels, outchannels):
 | |
|         def writeblock(data):
 | |
|             if self._protocoltracefn is not None:
 | |
|                 self._protocoltracefn('w', None, data)
 | |
|             self.server.stdin.write(struct.pack(self.inputfmt, len(data)))
 | |
|             self.server.stdin.write(data)
 | |
|             self.server.stdin.flush()
 | |
| 
 | |
|         if not self.server:
 | |
|             raise ValueError("server not connected")
 | |
| 
 | |
|         self.server.stdin.write(b('runcommand\n'))
 | |
|         writeblock(b('\0').join(args))
 | |
| 
 | |
|         while True:
 | |
|             channel, data = self._readchannel()
 | |
|             if self._protocoltracefn is not None:
 | |
|                 self._protocoltracefn('r', channel, data)
 | |
| 
 | |
|             # input channels
 | |
|             if channel in inchannels:
 | |
|                 writeblock(inchannels[channel](data))
 | |
|             # output channels
 | |
|             elif channel in outchannels:
 | |
|                 outchannels[channel](data)
 | |
|             # result channel, command finished
 | |
|             elif channel == b('r'):
 | |
|                 return struct.unpack(hgclient.retfmt, data)[0]
 | |
|             # a channel that we don't know and can't ignore
 | |
|             elif channel.isupper():
 | |
|                 raise error.ResponseError(
 | |
|                     "unexpected data on required channel '%s'" % channel)
 | |
|             # optional channel
 | |
|             else:
 | |
|                 pass
 | |
| 
 | |
|     def rawcommand(self, args, eh=None, prompt=None, input=None):
 | |
|         """
 | |
|         args is the cmdline (usually built using util.cmdbuilder)
 | |
| 
 | |
|         eh is an error handler that is passed the return code, stdout and stderr
 | |
|         If no eh is given, we raise a CommandError if ret != 0
 | |
| 
 | |
|         prompt is used to reply to prompts by the server
 | |
|         It receives the max number of bytes to return and the contents of stdout
 | |
|         received so far
 | |
| 
 | |
|         input is used to reply to bulk data requests by the server
 | |
|         It receives the max number of bytes to return
 | |
|         """
 | |
|         out, err = BytesIO(), BytesIO()
 | |
|         outchannels = {}
 | |
|         if self._cbout is None:
 | |
|             outchannels[b('o')] = out.write
 | |
|         else:
 | |
|             def out_handler(data):
 | |
|                 out.write(data)
 | |
|                 self._cbout(data)
 | |
|             outchannels[b('o')] = out_handler
 | |
|         if self._cberr is None:
 | |
|             outchannels[b('e')] = err.write
 | |
|         else:
 | |
|             def err_handler(data):
 | |
|                 err.write(data)
 | |
|                 self._cberr(data)
 | |
|             outchannels[b('e')] = err_handler
 | |
| 
 | |
|         inchannels = {}
 | |
|         if prompt is None:
 | |
|             prompt = self._cbprompt
 | |
|         if prompt is not None:
 | |
|             def func(size):
 | |
|                 reply = prompt(size, out.getvalue())
 | |
|                 return reply
 | |
|             inchannels[b('L')] = func
 | |
|         if input is not None:
 | |
|             inchannels[b('I')] = input
 | |
| 
 | |
|         ret = self.runcommand(args, inchannels, outchannels)
 | |
|         out, err = out.getvalue(), err.getvalue()
 | |
| 
 | |
|         if ret:
 | |
|             if eh is None:
 | |
|                 raise error.CommandError(args, ret, out, err)
 | |
|             else:
 | |
|                 return eh(ret, out, err)
 | |
|         return out
 | |
| 
 | |
|     def open(self):
 | |
|         if self.server is not None:
 | |
|             raise ValueError('server already open')
 | |
| 
 | |
|         self.server = util.popen(self._args, self._env)
 | |
|         try:
 | |
|             self._readhello()
 | |
|         except error.ServerError:
 | |
|             ret, serr = self._close()
 | |
|             raise error.ServerError('server exited with status %d: %s'
 | |
|                                     % (ret, serr.strip()))
 | |
|         return self
 | |
| 
 | |
|     def close(self):
 | |
|         """Closes the command server instance and waits for it to exit,
 | |
|         returns the exit code.
 | |
| 
 | |
|         Attempting to call any function afterwards that needs to
 | |
|         communicate with the server will raise a ValueError.
 | |
|         """
 | |
|         return self._close()[0]
 | |
| 
 | |
|     def _close(self):
 | |
|         _sout, serr = self.server.communicate()
 | |
|         ret = self.server.returncode
 | |
|         self.server = None
 | |
|         return ret, serr
 | |
| 
 | |
|     def add(self, files=[], dryrun=False, subrepos=False, include=None,
 | |
|             exclude=None):
 | |
|         """
 | |
|         Add the specified files on the next commit.
 | |
|         If no files are given, add all files to the repository.
 | |
| 
 | |
|         dryrun - do no perform actions
 | |
|         subrepos - recurse into subrepositories
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         Return whether all given files were added.
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('add'), n=dryrun, S=subrepos, I=include, X=exclude,
 | |
|                           *files)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def addremove(self, files=[], similarity=None, dryrun=False, include=None,
 | |
|                   exclude=None):
 | |
|         """Add all new files and remove all missing files from the repository.
 | |
| 
 | |
|         New files are ignored if they match any of the patterns in
 | |
|         ".hgignore". As with add, these changes take effect at the
 | |
|         next commit.
 | |
| 
 | |
|         similarity - used to detect renamed files. With a parameter
 | |
|         greater than 0, this compares every removed file with every
 | |
|         added file and records those similar enough as renames. This
 | |
|         option takes a percentage between 0 (disabled) and 100 (files
 | |
|         must be identical) as its parameter. Detecting renamed files
 | |
|         this way can be expensive. After using this option, "hg status
 | |
|         -C" can be used to check which files were identified as moved
 | |
|         or renamed.
 | |
| 
 | |
|         dryrun - do no perform actions
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         Return True if all files are successfully added.
 | |
| 
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('addremove'), s=similarity, n=dryrun, I=include,
 | |
|                           X=exclude, *files)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def annotate(self, files, rev=None, nofollow=False, text=False, user=False,
 | |
|                  file=False, date=False, number=False, changeset=False,
 | |
|                  line=False, verbose=False, include=None, exclude=None):
 | |
|         """
 | |
|         Show changeset information by line for each file in files.
 | |
| 
 | |
|         rev - annotate the specified revision
 | |
|         nofollow - don't follow copies and renames
 | |
|         text - treat all files as text
 | |
|         user - list the author (long with -v)
 | |
|         file - list the filename
 | |
|         date - list the date
 | |
|         number - list the revision number (default)
 | |
|         changeset - list the changeset
 | |
|         line - show line number at the first appearance
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         Yields a (info, contents) tuple for each line in a file. Info is a space
 | |
|         separated string according to the given options.
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('annotate'), r=rev, no_follow=nofollow, a=text,
 | |
|                           u=user, f=file, d=date, n=number, c=changeset,
 | |
|                           l=line, v=verbose, I=include, X=exclude,
 | |
|                           hidden=self.hidden, *files)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         for line in out.splitlines():
 | |
|             yield tuple(line.split(b(': '), 1))
 | |
| 
 | |
|     def archive(self, dest, rev=None, nodecode=False, prefix=None, type=None,
 | |
|                 subrepos=False, include=None, exclude=None):
 | |
|         """Create an unversioned archive of a repository revision.
 | |
| 
 | |
|         The exact name of the destination archive or directory is given using a
 | |
|         format string; see export for details.
 | |
| 
 | |
|         Each member added to an archive file has a directory prefix
 | |
|         prepended. Use prefix to specify a format string for the
 | |
|         prefix. The default is the basename of the archive, with
 | |
|         suffixes removed.
 | |
| 
 | |
|         dest - destination path
 | |
|         rev - revision to distribute. The revision used is the parent of the
 | |
|         working directory if one isn't given.
 | |
| 
 | |
|         nodecode - do not pass files through decoders
 | |
|         prefix - directory prefix for files in archive
 | |
|         type - type of distribution to create. The archive type is automatically
 | |
|         detected based on file extension if one isn't given.
 | |
| 
 | |
|         Valid types are:
 | |
| 
 | |
|         "files"  a directory full of files (default)
 | |
|         "tar"    tar archive, uncompressed
 | |
|         "tbz2"   tar archive, compressed using bzip2
 | |
|         "tgz"    tar archive, compressed using gzip
 | |
|         "uzip"   zip archive, uncompressed
 | |
|         "zip"    zip archive, compressed using deflate
 | |
| 
 | |
|         subrepos - recurse into subrepositories
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('archive'), dest, r=rev,
 | |
|                           no_decode=nodecode, p=prefix,
 | |
|                           t=type, S=subrepos, I=include, X=exclude,
 | |
|                           hidden=self.hidden)
 | |
| 
 | |
|         self.rawcommand(args)
 | |
| 
 | |
|     def backout(self, rev, merge=False, parent=None, tool=None, message=None,
 | |
|                 logfile=None, date=None, user=None):
 | |
|         """Prepare a new changeset with the effect of rev undone in the current
 | |
|         working directory.
 | |
| 
 | |
|         If rev is the parent of the working directory, then this new
 | |
|         changeset is committed automatically. Otherwise, hg needs to
 | |
|         merge the changes and the merged result is left uncommitted.
 | |
| 
 | |
|         rev - revision to backout
 | |
|         merge - merge with old dirstate parent after backout
 | |
|         parent - parent to choose when backing out merge
 | |
|         tool - specify merge tool
 | |
|         message - use text as commit message
 | |
|         logfile - read commit message from file
 | |
|         date - record the specified date as commit date
 | |
|         user - record the specified user as committer
 | |
| 
 | |
|         """
 | |
|         if message and logfile:
 | |
|             raise ValueError("cannot specify both a message and a logfile")
 | |
| 
 | |
|         args = cmdbuilder(b('backout'), r=rev, merge=merge, parent=parent,
 | |
|                           t=tool, m=message, l=logfile, d=date, u=user,
 | |
|                           hidden=self.hidden)
 | |
| 
 | |
|         self.rawcommand(args)
 | |
| 
 | |
|     def bookmark(self, name, rev=None, force=False, delete=False,
 | |
|                  inactive=False, rename=None):
 | |
|         """
 | |
|         Set a bookmark on the working directory's parent revision or rev,
 | |
|         with the given name.
 | |
| 
 | |
|         name - bookmark name
 | |
|         rev - revision to bookmark
 | |
|         force - bookmark even if another bookmark with the same name exists
 | |
|         delete - delete the given bookmark
 | |
|         inactive - do not mark the new bookmark active
 | |
|         rename - rename the bookmark given by rename to name
 | |
|         """
 | |
|         args = cmdbuilder(b('bookmark'), name, r=rev, f=force, d=delete,
 | |
|                           i=inactive, m=rename)
 | |
| 
 | |
|         self.rawcommand(args)
 | |
| 
 | |
|     def bookmarks(self):
 | |
|         """
 | |
|         Return the bookmarks as a list of (name, rev, node) and the index of the
 | |
|         current one.
 | |
| 
 | |
|         If there isn't a current one, -1 is returned as the index.
 | |
|         """
 | |
|         args = cmdbuilder(b('bookmarks'), hidden=self.hidden)
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         bms = []
 | |
|         current = -1
 | |
|         if out.rstrip() != b('no bookmarks set'):
 | |
|             for line in out.splitlines():
 | |
|                 iscurrent, line = line[0:3], line[3:]
 | |
|                 if b('*') in iscurrent:
 | |
|                     current = len(bms)
 | |
|                 name, line = line.split(b(' '), 1)
 | |
|                 rev, node = line.split(b(':'))
 | |
|                 bms.append((name, int(rev), node))
 | |
|         return bms, current
 | |
| 
 | |
|     def branch(self, name=None, clean=False, force=False):
 | |
|         """When name isn't given, return the current branch name. Otherwise
 | |
|         set the working directory branch name (the branch will not
 | |
|         exist in the repository until the next commit). Standard
 | |
|         practice recommends that primary development take place on the
 | |
|         'default' branch.
 | |
| 
 | |
|         When clean is True, reset and return the working directory
 | |
|         branch to that of the parent of the working directory,
 | |
|         negating a previous branch change.
 | |
| 
 | |
|         name - new branch name
 | |
|         clean - reset branch name to parent branch name
 | |
|         force - set branch name even if it shadows an existing branch
 | |
| 
 | |
|         """
 | |
|         if name and clean:
 | |
|             raise ValueError('cannot use both name and clean')
 | |
| 
 | |
|         args = cmdbuilder(b('branch'), name, f=force, C=clean)
 | |
|         out = self.rawcommand(args).rstrip()
 | |
| 
 | |
|         if name:
 | |
|             return name
 | |
|         elif not clean:
 | |
|             return out
 | |
|         else:
 | |
|             # len('reset working directory to branch ') == 34
 | |
|             return out[34:]
 | |
| 
 | |
|     def branches(self, active=False, closed=False):
 | |
|         """
 | |
|         Returns the repository's named branches as a list of (name, rev, node).
 | |
| 
 | |
|         active - show only branches that have unmerged heads
 | |
|         closed - show normal and closed branches
 | |
|         """
 | |
|         args = cmdbuilder(b('branches'), a=active, c=closed, hidden=self.hidden)
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         branches = []
 | |
|         for line in out.rstrip().splitlines():
 | |
|             namerev, node = line.rsplit(b(':'), 1)
 | |
|             name, rev = namerev.rsplit(b(' '), 1)
 | |
|             name = name.rstrip()
 | |
|             node = node.split()[0] # get rid of ' (inactive)'
 | |
|             branches.append((name, int(rev), node))
 | |
|         return branches
 | |
| 
 | |
|     def bundle(self, file, destrepo=None, rev=[], branch=[], base=[], all=False,
 | |
|                force=False, type=None, ssh=None, remotecmd=None,
 | |
|                insecure=False):
 | |
|         """Generate a compressed changegroup file collecting changesets not
 | |
|         known to be in another repository.
 | |
| 
 | |
|         If destrepo isn't given, then hg assumes the destination will have all
 | |
|         the nodes you specify with base. To create a bundle containing all
 | |
|         changesets, use all (or set base to 'null').
 | |
| 
 | |
|         file - destination file name
 | |
|         destrepo - repository to look for changes
 | |
|         rev - a changeset intended to be added to the destination
 | |
|         branch - a specific branch you would like to bundle
 | |
|         base - a base changeset assumed to be available at the destination
 | |
|         all - bundle all changesets in the repository
 | |
|         type - bundle compression type to use, available compression
 | |
|          methods are: none, bzip2, and gzip (default: bzip2)
 | |
| 
 | |
|         force - run even when the destrepo is unrelated
 | |
|         ssh - specify ssh command to use
 | |
|         remotecmd - specify hg command to run on the remote side
 | |
|         insecure - do not verify server certificate (ignoring
 | |
|          web.cacerts config)
 | |
| 
 | |
|         Return True if a bundle was created, False if no changes were found.
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('bundle'), file, destrepo, f=force, r=rev, b=branch,
 | |
|                           base=base, a=all, t=type, e=ssh, remotecmd=remotecmd,
 | |
|                           insecure=insecure, hidden=self.hidden)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def cat(self, files, rev=None, output=None):
 | |
|         """Return a string containing the specified files as they were at the
 | |
|         given revision. If no revision is given, the parent of the working
 | |
|         directory is used, or tip if no revision is checked out.
 | |
| 
 | |
|         If output is given, writes the contents to the specified file.
 | |
|         The name of the file is given using a format string. The
 | |
|         formatting rules are the same as for the export command, with
 | |
|         the following additions:
 | |
| 
 | |
|         "%s"  basename of file being printed
 | |
|         "%d"  dirname of file being printed, or '.' if in repository root
 | |
|         "%p"  root-relative path name of file being printed
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('cat'), r=rev, o=output, hidden=self.hidden, *files)
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         if not output:
 | |
|             return out
 | |
| 
 | |
|     def clone(self, source=b('.'), dest=None, branch=None, updaterev=None,
 | |
|               revrange=None):
 | |
|         """
 | |
|         Create a copy of an existing repository specified by source in a new
 | |
|         directory dest.
 | |
| 
 | |
|         If dest isn't specified, it defaults to the basename of source.
 | |
| 
 | |
|         branch - clone only the specified branch
 | |
|         updaterev - revision, tag or branch to check out
 | |
|         revrange - include the specified changeset
 | |
|         """
 | |
|         args = cmdbuilder(b('clone'), source, dest, b=branch,
 | |
|                           u=updaterev, r=revrange)
 | |
|         self.rawcommand(args)
 | |
| 
 | |
|     def init(self, dest, ssh=None, remotecmd=None, insecure=False):
 | |
|         args = util.cmdbuilder('init', dest, e=ssh, remotecmd=remotecmd,
 | |
|                                insecure=insecure)
 | |
|         self.rawcommand(args)
 | |
| 
 | |
|     def commit(self, message=None, logfile=None, addremove=False,
 | |
|                closebranch=False, date=None, user=None, include=None,
 | |
|                exclude=None, amend=False):
 | |
|         """
 | |
|         Commit changes reported by status into the repository.
 | |
| 
 | |
|         message - the commit message
 | |
|         logfile - read commit message from file
 | |
|         addremove - mark new/missing files as added/removed before committing
 | |
|         closebranch - mark a branch as closed, hiding it from the branch list
 | |
|         date - record the specified date as commit date
 | |
|         user - record the specified user as committer
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
|         amend - amend the parent of the working dir
 | |
|         """
 | |
|         if amend and message is None and logfile is None:
 | |
|             # retrieve current commit message
 | |
|             message = self.log(b('.'))[0][5]
 | |
|         if message is None and logfile is None and not amend:
 | |
|             raise ValueError("must provide at least a message or a logfile")
 | |
|         elif message and logfile:
 | |
|             raise ValueError("cannot specify both a message and a logfile")
 | |
| 
 | |
|         # --debug will print the committed cset
 | |
|         args = cmdbuilder(b('commit'), debug=True, m=message, A=addremove,
 | |
|                           close_branch=closebranch, d=date, u=user, l=logfile,
 | |
|                           I=include, X=exclude, amend=amend)
 | |
|         out = self.rawcommand(args)
 | |
|         m = re.search(b(r'^committed changeset (\d+):([0-9a-f]+)'), out,
 | |
|                       re.MULTILINE)
 | |
|         if not m:
 | |
|             raise ValueError('revision and node not found in hg output: %r'
 | |
|                              % out)
 | |
|         rev, node = m.groups()
 | |
|         return int(rev), node
 | |
| 
 | |
|     def config(self, names=[], untrusted=False, showsource=False):
 | |
|         """Return a list of (section, key, value) config settings from all
 | |
|         hgrc files
 | |
| 
 | |
|         When showsource is specified, return (source, section, key, value) where
 | |
|         source is of the form filename:[line]
 | |
| 
 | |
|         """
 | |
|         def splitline(s):
 | |
|             k, value = s.rstrip().split(b('='), 1)
 | |
|             section, key = k.split(b('.'), 1)
 | |
|             return section, key, value
 | |
| 
 | |
|         if not isinstance(names, list):
 | |
|             names = [names]
 | |
| 
 | |
|         args = cmdbuilder(b('showconfig'), u=untrusted, debug=showsource,
 | |
|                           *names)
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         conf = []
 | |
|         if showsource:
 | |
|             out = util.skiplines(out, b('read config from: '))
 | |
|             for line in out.splitlines():
 | |
|                 m = re.match(b(r"(.+?:(?:\d+:)?) (.*)"), line)
 | |
|                 t = splitline(m.group(2))
 | |
|                 conf.append((m.group(1)[:-1], t[0], t[1], t[2]))
 | |
|         else:
 | |
|             for line in out.splitlines():
 | |
|                 conf.append(splitline(line))
 | |
| 
 | |
|         return conf
 | |
| 
 | |
|     @property
 | |
|     def encoding(self):
 | |
|         """
 | |
|         Return the server's encoding (as reported in the hello message).
 | |
|         """
 | |
|         if not b('getencoding') in self.capabilities:
 | |
|             raise CapabilityError('getencoding')
 | |
| 
 | |
|         if not self._encoding:
 | |
|             self.server.stdin.write(b('getencoding\n'))
 | |
|             self._encoding = self._readfromchannel('r')
 | |
| 
 | |
|         return self._encoding
 | |
| 
 | |
|     def copy(self, source, dest, after=False, force=False, dryrun=False,
 | |
|              include=None, exclude=None):
 | |
|         """Mark dest as having copies of source files. If dest is a
 | |
|         directory, copies are put in that directory. If dest is a
 | |
|         file, then source must be a string.
 | |
| 
 | |
|         Returns True on success, False if errors are encountered.
 | |
| 
 | |
|         source - a file or a list of files
 | |
|         dest - a destination file or directory
 | |
|         after - record a copy that has already occurred
 | |
|         force - forcibly copy over an existing managed file
 | |
|         dryrun - do not perform actions, just print output
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         if not isinstance(source, list):
 | |
|             source = [source]
 | |
| 
 | |
|         source.append(dest)
 | |
|         args = cmdbuilder(b('copy'), A=after, f=force, n=dryrun,
 | |
|                           I=include, X=exclude, *source)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def diff(self, files=[], revs=[], change=None, text=False,
 | |
|              git=False, nodates=False, showfunction=False,
 | |
|              reverse=False, ignoreallspace=False,
 | |
|              ignorespacechange=False, ignoreblanklines=False,
 | |
|              unified=None, stat=False, subrepos=False, include=None,
 | |
|              exclude=None):
 | |
|         """
 | |
|         Return differences between revisions for the specified files.
 | |
| 
 | |
|         revs - a revision or a list of two revisions to diff
 | |
|         change - change made by revision
 | |
|         text - treat all files as text
 | |
|         git - use git extended diff format
 | |
|         nodates - omit dates from diff headers
 | |
|         showfunction - show which function each change is in
 | |
|         reverse - produce a diff that undoes the changes
 | |
|         ignoreallspace - ignore white space when comparing lines
 | |
|         ignorespacechange - ignore changes in the amount of white space
 | |
|         ignoreblanklines - ignore changes whose lines are all blank
 | |
|         unified - number of lines of context to show
 | |
|         stat - output diffstat-style summary of changes
 | |
|         subrepos - recurse into subrepositories
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
|         """
 | |
|         if change and revs:
 | |
|             raise ValueError('cannot specify both change and rev')
 | |
| 
 | |
|         args = cmdbuilder(b('diff'), r=list(map(strtobytes, revs)), c=change,
 | |
|                           a=text, g=git, nodates=nodates,
 | |
|                           p=showfunction, reverse=reverse,
 | |
|                           w=ignoreallspace, b=ignorespacechange,
 | |
|                           B=ignoreblanklines, U=unified, stat=stat,
 | |
|                           S=subrepos, I=include, X=exclude, hidden=self.hidden,
 | |
|                           *files)
 | |
| 
 | |
|         return self.rawcommand(args)
 | |
| 
 | |
|     def export(self, revs, output=None, switchparent=False,
 | |
|                text=False, git=False, nodates=False):
 | |
|         """Return the header and diffs for one or more changesets. When
 | |
|         output is given, dumps to file. The name of the file is given
 | |
|         using a format string. The formatting rules are as follows:
 | |
| 
 | |
|         "%%"  literal "%" character
 | |
|         "%H"  changeset hash (40 hexadecimal digits)
 | |
|         "%N"  number of patches being generated
 | |
|         "%R"  changeset revision number
 | |
|         "%b"  basename of the exporting repository
 | |
|         "%h"  short-form changeset hash (12 hexadecimal digits)
 | |
|         "%n"  zero-padded sequence number, starting at 1
 | |
|         "%r"  zero-padded changeset revision number
 | |
| 
 | |
|         output - print output to file with formatted name
 | |
|         switchparent - diff against the second parent
 | |
|         rev - a revision or list of revisions to export
 | |
|         text - treat all files as text
 | |
|         git - use git extended diff format
 | |
|         nodates - omit dates from diff headers
 | |
| 
 | |
|         """
 | |
|         if not isinstance(revs, list):
 | |
|             revs = [revs]
 | |
|         args = cmdbuilder(b('export'), o=output, switch_parent=switchparent,
 | |
|                           a=text, g=git, nodates=nodates, hidden=self.hidden,
 | |
|                           *revs)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         if output is None:
 | |
|             return out
 | |
| 
 | |
|     def forget(self, files, include=None, exclude=None):
 | |
|         """Mark the specified files so they will no longer be tracked after
 | |
|         the next commit.
 | |
| 
 | |
|         This only removes files from the current branch, not from the entire
 | |
|         project history, and it does not delete them from the working directory.
 | |
| 
 | |
|         Returns True on success.
 | |
| 
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('forget'), I=include, X=exclude, *files)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def grep(self, pattern, files=[], all=False, text=False, follow=False,
 | |
|              ignorecase=False, fileswithmatches=False, line=False, user=False,
 | |
|              date=False, include=None, exclude=None):
 | |
|         """Search for a pattern in specified files and revisions.
 | |
| 
 | |
|         This behaves differently than Unix grep. It only accepts Python/Perl
 | |
|         regexps. It searches repository history, not the working directory.
 | |
|         It always prints the revision number in which a match appears.
 | |
| 
 | |
|         Yields (filename, revision, [line, [match status, [user,
 | |
|         [date, [match]]]]]) per match depending on the given options.
 | |
| 
 | |
|         all - print all revisions that match
 | |
|         text - treat all files as text
 | |
|         follow - follow changeset history, or file history across
 | |
|          copies and renames
 | |
|         ignorecase - ignore case when matching
 | |
|         fileswithmatches - return only filenames and revisions that match
 | |
|         line - return line numbers in the result tuple
 | |
|         user - return the author in the result tuple
 | |
|         date - return the date in the result tuple
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('grep'), all=all, a=text, f=follow, i=ignorecase,
 | |
|                           l=fileswithmatches, n=line, u=user, d=date,
 | |
|                           I=include, X=exclude, hidden=self.hidden,
 | |
|                           *[pattern] + files)
 | |
|         args.append(b('-0'))
 | |
| 
 | |
|         def eh(ret, out, err):
 | |
|             if ret != 1:
 | |
|                 raise error.CommandError(args, ret, out, err)
 | |
|             return b('')
 | |
| 
 | |
|         out = self.rawcommand(args, eh=eh).split(b('\0'))
 | |
| 
 | |
|         fieldcount = 3
 | |
|         if user:
 | |
|             fieldcount += 1
 | |
|         if date:
 | |
|             fieldcount += 1
 | |
|         if line:
 | |
|             fieldcount += 1
 | |
|         if all:
 | |
|             fieldcount += 1
 | |
|         if fileswithmatches:
 | |
|             fieldcount -= 1
 | |
| 
 | |
|         return util.grouper(fieldcount, out)
 | |
| 
 | |
|     def heads(self, rev=[], startrev=[], topological=False, closed=False):
 | |
|         """Return a list of current repository heads or branch heads.
 | |
| 
 | |
|         rev - return only branch heads on the branches associated with
 | |
|         the specified changesets.
 | |
| 
 | |
|         startrev - return only heads which are descendants of the given revs.
 | |
|         topological - named branch mechanics will be ignored and only changesets
 | |
|         without children will be shown.
 | |
| 
 | |
|         closed - normal and closed branch heads.
 | |
| 
 | |
|         """
 | |
|         if not isinstance(rev, list):
 | |
|             rev = [rev]
 | |
| 
 | |
|         args = cmdbuilder(b('heads'), r=startrev, t=topological, c=closed,
 | |
|                           template=templates.changeset, hidden=self.hidden,
 | |
|                           *rev)
 | |
| 
 | |
|         def eh(ret, out, err):
 | |
|             if ret != 1:
 | |
|                 raise error.CommandError(args, ret, out, err)
 | |
|             return b('')
 | |
| 
 | |
|         out = self.rawcommand(args, eh=eh).split(b('\0'))[:-1]
 | |
|         return self._parserevs(out)
 | |
| 
 | |
|     def identify(self, rev=None, source=None, num=False, id=False, branch=False,
 | |
|                  tags=False, bookmarks=False):
 | |
|         """Return a summary string identifying the repository state at rev
 | |
|         using one or two parent hash identifiers, followed by a "+" if
 | |
|         the working directory has uncommitted changes, the branch name
 | |
|         (if not default), a list of tags, and a list of bookmarks.
 | |
| 
 | |
|         When rev is not given, return a summary string of the current
 | |
|         state of the repository.
 | |
| 
 | |
|         Specifying source as a repository root or Mercurial bundle will cause
 | |
|         lookup to operate on that repository/bundle.
 | |
| 
 | |
|         num - show local revision number
 | |
|         id - show global revision id
 | |
|         branch - show branch
 | |
|         tags - show tags
 | |
|         bookmarks - show bookmarks
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('identify'), source, r=rev, n=num, i=id,
 | |
|                           b=branch, t=tags, B=bookmarks,
 | |
|                           hidden=self.hidden)
 | |
| 
 | |
|         return self.rawcommand(args)
 | |
| 
 | |
|     def import_(self, patches, strip=None, force=False, nocommit=False,
 | |
|                 bypass=False, exact=False, importbranch=False, message=None,
 | |
|                 date=None, user=None, similarity=None):
 | |
|         """Import the specified patches which can be a list of file names or a
 | |
|         file-like object and commit them individually (unless nocommit is
 | |
|         specified).
 | |
| 
 | |
|         strip - directory strip option for patch. This has the same
 | |
|         meaning as the corresponding patch option (default: 1)
 | |
| 
 | |
|         force - skip check for outstanding uncommitted changes
 | |
|         nocommit - don't commit, just update the working directory
 | |
|         bypass - apply patch without touching the working directory
 | |
|         exact - apply patch to the nodes from which it was generated
 | |
|         importbranch - use any branch information in patch (implied by exact)
 | |
|         message - the commit message
 | |
|         date - record the specified date as commit date
 | |
|         user - record the specified user as committer
 | |
|         similarity - guess renamed files by similarity (0<=s<=100)
 | |
| 
 | |
|         """
 | |
|         if hasattr(patches, 'read') and hasattr(patches, 'readline'):
 | |
|             patch = patches
 | |
| 
 | |
|             def readline(size, output):
 | |
|                 return patch.readline(size)
 | |
| 
 | |
|             stdin = True
 | |
|             patches = ()
 | |
|             prompt = readline
 | |
|             input = patch.read
 | |
|         else:
 | |
|             stdin = False
 | |
|             prompt = None
 | |
|             input = None
 | |
| 
 | |
|         args = cmdbuilder(b('import'), strip=strip, force=force,
 | |
|                           no_commit=nocommit, bypass=bypass, exact=exact,
 | |
|                           import_branch=importbranch, message=message,
 | |
|                           date=date, user=user, similarity=similarity, _=stdin,
 | |
|                           *patches)
 | |
| 
 | |
|         self.rawcommand(args, prompt=prompt, input=input)
 | |
| 
 | |
|     def incoming(self, revrange=None, path=None, force=False, newest=False,
 | |
|                  bundle=None, bookmarks=False, branch=None, limit=None,
 | |
|                  nomerges=False, subrepos=False):
 | |
|         """Return new changesets found in the specified path or the default pull
 | |
|         location.
 | |
| 
 | |
|         When bookmarks=True, return a list of (name, node) of incoming
 | |
|         bookmarks.
 | |
| 
 | |
|         revrange - a remote changeset or list of changesets intended to be added
 | |
|         force - run even if remote repository is unrelated
 | |
|         newest - show newest record first
 | |
|         bundle - avoid downloading the changesets twice and store the
 | |
|          bundles into the specified file.
 | |
| 
 | |
|         bookmarks - compare bookmarks (this changes the return value)
 | |
|         branch - a specific branch you would like to pull
 | |
|         limit - limit number of changes returned
 | |
|         nomerges - do not show merges
 | |
|         ssh - specify ssh command to use
 | |
|         remotecmd - specify hg command to run on the remote side
 | |
|         insecure- do not verify server certificate (ignoring web.cacerts config)
 | |
|         subrepos - recurse into subrepositories
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('incoming'), path,
 | |
|                           template=templates.changeset, r=revrange,
 | |
|                           f=force, n=newest, bundle=bundle,
 | |
|                           B=bookmarks, b=branch, l=limit, M=nomerges,
 | |
|                           S=subrepos)
 | |
| 
 | |
|         def eh(ret, out, err):
 | |
|             if ret != 1:
 | |
|                 raise error.CommandError(args, ret, out, err)
 | |
| 
 | |
|         out = self.rawcommand(args, eh=eh)
 | |
|         if not out:
 | |
|             return []
 | |
| 
 | |
|         out = util.eatlines(out, 2)
 | |
|         if bookmarks:
 | |
|             bms = []
 | |
|             for line in out.splitlines():
 | |
|                 bms.append(tuple(line.split()))
 | |
|             return bms
 | |
|         else:
 | |
|             out = out.split(b('\0'))[:-1]
 | |
|             return self._parserevs(out)
 | |
| 
 | |
|     def log(self, revrange=None, files=[], follow=False,
 | |
|             followfirst=False, date=None, copies=False, keyword=None,
 | |
|             removed=False, onlymerges=False, user=None, branch=None,
 | |
|             prune=None, hidden=None, limit=None, nomerges=False,
 | |
|             include=None, exclude=None):
 | |
|         """Return the revision history of the specified files or the entire
 | |
|         project.
 | |
| 
 | |
|         File history is shown without following rename or copy history of files.
 | |
|         Use follow with a filename to follow history across renames and copies.
 | |
|         follow without a filename will only show ancestors or descendants of the
 | |
|         starting revision. followfirst only follows the first parent of merge
 | |
|         revisions.
 | |
| 
 | |
|         If revrange isn't specified, the default is "tip:0" unless
 | |
|         follow is set, in which case the working directory parent is
 | |
|         used as the starting revision.
 | |
| 
 | |
|         The returned changeset is a named tuple with the following
 | |
|         string fields:
 | |
| 
 | |
|             - rev
 | |
|             - node
 | |
|             - tags (space delimited)
 | |
|             - branch
 | |
|             - author
 | |
|             - desc
 | |
| 
 | |
|         follow - follow changeset history, or file history across
 | |
|          copies and renames
 | |
|         followfirst - only follow the first parent of merge changesets
 | |
|         date - show revisions matching date spec
 | |
|         copies - show copied files
 | |
|         keyword - do case-insensitive search for a given text
 | |
|         removed - include revisions where files were removed
 | |
|         onlymerges - show only merges
 | |
|         user - revisions committed by user
 | |
|         branch - show changesets within the given named branch
 | |
|         prune - do not display revision or any of its ancestors
 | |
|         hidden - show hidden changesets
 | |
|         limit - limit number of changes displayed
 | |
|         nomerges - do not show merges
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         if hidden is None:
 | |
|             hidden = self.hidden
 | |
|         args = cmdbuilder(b('log'), template=templates.changeset,
 | |
|                           r=revrange, f=follow, follow_first=followfirst,
 | |
|                           d=date, C=copies, k=keyword, removed=removed,
 | |
|                           m=onlymerges, u=user, b=branch, P=prune,
 | |
|                           l=limit, M=nomerges, I=include, X=exclude,
 | |
|                           hidden=hidden, *files)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
|         out = out.split(b('\0'))[:-1]
 | |
| 
 | |
|         return self._parserevs(out)
 | |
| 
 | |
|     def manifest(self, rev=None, all=False):
 | |
|         """Yields (nodeid, permission, executable, symlink, file path) tuples
 | |
|         for version controlled files for the given revision. If no
 | |
|         revision is given, the first parent of the working directory
 | |
|         is used, or the null revision if no revision is checked out.
 | |
| 
 | |
|         When all is True, all files from all revisions are yielded
 | |
|         (just the name). This includes deleted and renamed files.
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('manifest'), r=rev, all=all, debug=True,
 | |
|                           hidden=self.hidden)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         if all:
 | |
|             for line in out.splitlines():
 | |
|                 yield line
 | |
|         else:
 | |
|             for line in out.splitlines():
 | |
|                 node = line[0:40]
 | |
|                 perm = line[41:44]
 | |
|                 symlink = line[45:46] == b('@')
 | |
|                 executable = line[45:46] == b('*')
 | |
|                 yield node, perm, executable, symlink, line[47:]
 | |
| 
 | |
|     def merge(self, rev=None, force=False, tool=None, cb=merge.handlers.abort):
 | |
|         """Merge working directory with rev. If no revision is specified, the
 | |
|         working directory's parent is a head revision, and the current
 | |
|         branch contains exactly one other head, the other head is
 | |
|         merged with by default.
 | |
| 
 | |
|         The current working directory is updated with all changes made in the
 | |
|         requested revision since the last common predecessor revision.
 | |
| 
 | |
|         Files that changed between either parent are marked as changed for the
 | |
|         next commit and a commit must be performed before any further updates to
 | |
|         the repository are allowed. The next commit will have two parents.
 | |
| 
 | |
|         force - force a merge with outstanding changes
 | |
|         tool - can be used to specify the merge tool used for file merges. It
 | |
|         overrides the HGMERGE environment variable and your configuration files.
 | |
| 
 | |
|         cb - controls the behaviour when Mercurial prompts what to do
 | |
|         with regard to a specific file, e.g. when one parent modified
 | |
|         a file and the other removed it. It can be one of
 | |
|         merge.handlers, or a function that gets a single argument
 | |
|         which are the contents of stdout. It should return one of the
 | |
|         expected choices (a single character).
 | |
| 
 | |
|         """
 | |
|         # we can't really use --preview since merge doesn't support --template
 | |
|         args = cmdbuilder(b('merge'), r=rev, f=force, t=tool)
 | |
| 
 | |
|         prompt = None
 | |
|         if cb is merge.handlers.abort:
 | |
|             prompt = cb
 | |
|         elif cb is merge.handlers.noninteractive:
 | |
|             args.append(b('-y'))
 | |
|         else:
 | |
|             prompt = lambda size, output: cb(output) + b('\n')
 | |
| 
 | |
|         self.rawcommand(args, prompt=prompt)
 | |
| 
 | |
|     def move(self, source, dest, after=False, force=False, dryrun=False,
 | |
|              include=None, exclude=None):
 | |
|         """Mark dest as copies of source; mark source for deletion. If dest
 | |
|         is a directory, copies are put in that directory. If dest is a
 | |
|         file, then source must be a string.
 | |
| 
 | |
|         Returns True on success, False if errors are encountered.
 | |
| 
 | |
|         source - a file or a list of files
 | |
|         dest - a destination file or directory
 | |
|         after - record a rename that has already occurred
 | |
|         force - forcibly copy over an existing managed file
 | |
|         dryrun - do not perform actions, just print output
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         if not isinstance(source, list):
 | |
|             source = [source]
 | |
| 
 | |
|         source.append(dest)
 | |
|         args = cmdbuilder(b('move'), A=after, f=force, n=dryrun,
 | |
|                           I=include, X=exclude, *source)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def outgoing(self, revrange=None, path=None, force=False, newest=False,
 | |
|                  bookmarks=False, branch=None, limit=None, nomerges=False,
 | |
|                  subrepos=False):
 | |
|         """Return changesets not found in the specified path or the default push
 | |
|         location.
 | |
| 
 | |
|         When bookmarks=True, return a list of (name, node) of
 | |
|         bookmarks that will be pushed.
 | |
| 
 | |
|         revrange - a (list of) changeset intended to be included in
 | |
|         the destination force - run even when the destination is
 | |
|         unrelated newest - show newest record first branch - a
 | |
|         specific branch you would like to push limit - limit number of
 | |
|         changes displayed nomerges - do not show merges ssh - specify
 | |
|         ssh command to use remotecmd - specify hg command to run on
 | |
|         the remote side insecure - do not verify server certificate
 | |
|         (ignoring web.cacerts config) subrepos - recurse into
 | |
|         subrepositories
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('outgoing'),
 | |
|                           path,
 | |
|                           template=templates.changeset, r=revrange,
 | |
|                           f=force, n=newest, B=bookmarks,
 | |
|                           b=branch, S=subrepos)
 | |
| 
 | |
|         def eh(ret, out, err):
 | |
|             if ret != 1:
 | |
|                 raise error.CommandError(args, ret, out, err)
 | |
| 
 | |
|         out = self.rawcommand(args, eh=eh)
 | |
|         if not out:
 | |
|             return []
 | |
| 
 | |
|         out = util.eatlines(out, 2)
 | |
|         if bookmarks:
 | |
|             bms = []
 | |
|             for line in out.splitlines():
 | |
|                 bms.append(tuple(line.split()))
 | |
|             return bms
 | |
|         else:
 | |
|             out = out.split(b('\0'))[:-1]
 | |
|             return self._parserevs(out)
 | |
| 
 | |
|     def parents(self, rev=None, file=None):
 | |
|         """Return the working directory's parent revisions. If rev is given,
 | |
|         the parent of that revision will be printed. If file is given,
 | |
|         the revision in which the file was last changed (before the
 | |
|         working directory revision or the revision specified by rev)
 | |
|         is returned.
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('parents'), file, template=templates.changeset,
 | |
|                           r=rev, hidden=self.hidden)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
|         if not out:
 | |
|             return
 | |
| 
 | |
|         out = out.split(b('\0'))[:-1]
 | |
| 
 | |
|         return self._parserevs(out)
 | |
| 
 | |
|     def paths(self, name=None):
 | |
|         """
 | |
|         Return the definition of given symbolic path name. If no name is given,
 | |
|         return a dictionary of pathname : url of all available names.
 | |
| 
 | |
|         Path names are defined in the [paths] section of your configuration file
 | |
|         and in "/etc/mercurial/hgrc". If run inside a repository, ".hg/hgrc" is
 | |
|         used, too.
 | |
|         """
 | |
|         if not name:
 | |
|             out = self.rawcommand([b('paths')])
 | |
|             if not out:
 | |
|                 return {}
 | |
| 
 | |
|             return dict([s.split(b(' = '))
 | |
|                          for s in out.rstrip().split(b('\n'))])
 | |
|         else:
 | |
|             args = cmdbuilder(b('paths'), name)
 | |
|             out = self.rawcommand(args)
 | |
|             return out.rstrip()
 | |
| 
 | |
|     def pull(self, source=None, rev=None, update=False, force=False,
 | |
|              bookmark=None, branch=None, ssh=None, remotecmd=None,
 | |
|              insecure=False, tool=None):
 | |
|         """Pull changes from a remote repository.
 | |
| 
 | |
|         This finds all changes from the repository specified by source
 | |
|         and adds them to this repository. If source is omitted, the
 | |
|         'default' path will be used. By default, this does not update
 | |
|         the copy of the project in the working directory.
 | |
| 
 | |
|         Returns True on success, False if update was given and there were
 | |
|         unresolved files.
 | |
| 
 | |
|         update - update to new branch head if changesets were pulled
 | |
|         force - run even when remote repository is unrelated
 | |
|         rev - a (list of) remote changeset intended to be added
 | |
|         bookmark - (list of) bookmark to pull
 | |
|         branch - a (list of) specific branch you would like to pull
 | |
|         ssh - specify ssh command to use
 | |
|         remotecmd - specify hg command to run on the remote side
 | |
|         insecure - do not verify server certificate (ignoring
 | |
|          web.cacerts config)
 | |
|         tool - specify merge tool for rebase
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('pull'), source, r=rev, u=update, f=force,
 | |
|                           B=bookmark, b=branch, e=ssh,
 | |
|                           remotecmd=remotecmd, insecure=insecure,
 | |
|                           t=tool)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def push(self, dest=None, rev=None, force=False, bookmark=None, branch=None,
 | |
|              newbranch=False, ssh=None, remotecmd=None, insecure=False):
 | |
|         """Push changesets from this repository to the specified destination.
 | |
| 
 | |
|         This operation is symmetrical to pull: it is identical to a pull in the
 | |
|         destination repository from the current one.
 | |
| 
 | |
|         Returns True if push was successful, False if nothing to push.
 | |
| 
 | |
|         rev - the (list of) specified revision and all its ancestors
 | |
|         will be pushed to the remote repository.
 | |
| 
 | |
|         force - override the default behavior and push all changesets on all
 | |
|         branches.
 | |
| 
 | |
|         bookmark - (list of) bookmark to push
 | |
|         branch - a (list of) specific branch you would like to push
 | |
|         newbranch - allows push to create a new named branch that is
 | |
|          not present at the destination. This allows you to only create
 | |
|          a new branch without forcing other changes.
 | |
| 
 | |
|         ssh - specify ssh command to use
 | |
|         remotecmd - specify hg command to run on the remote side
 | |
|         insecure - do not verify server certificate (ignoring
 | |
|          web.cacerts config)
 | |
| 
 | |
|         """
 | |
|         args = cmdbuilder(b('push'), dest, r=rev, f=force, B=bookmark, b=branch,
 | |
|                           new_branch=newbranch, e=ssh, remotecmd=remotecmd,
 | |
|                           insecure=insecure)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def remove(self, files, after=False, force=False, include=None,
 | |
|                exclude=None):
 | |
|         """Schedule the indicated files for removal from the repository. This
 | |
|         only removes files from the current branch, not from the
 | |
|         entire project history.
 | |
| 
 | |
|         Returns True on success, False if any warnings encountered.
 | |
| 
 | |
|         after - used to remove only files that have already been deleted
 | |
|         force - remove (and delete) file even if added or modified
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
| 
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('remove'), A=after, f=force, I=include, X=exclude,
 | |
|                           *files)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def resolve(self, file=[], all=False, listfiles=False, mark=False,
 | |
|                 unmark=False, tool=None, include=None, exclude=None):
 | |
|         """
 | |
|         Redo merges or set/view the merge status of given files.
 | |
| 
 | |
|         Returns True on success, False if any files fail a resolve attempt.
 | |
| 
 | |
|         When listfiles is True, returns a list of (code, file path) of resolved
 | |
|         and unresolved files. Code will be 'R' or 'U' accordingly.
 | |
| 
 | |
|         all - select all unresolved files
 | |
|         mark - mark files as resolved
 | |
|         unmark - mark files as unresolved
 | |
|         tool - specify merge tool
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
|         """
 | |
|         if not isinstance(file, list):
 | |
|             file = [file]
 | |
| 
 | |
|         args = cmdbuilder(b('resolve'), a=all, l=listfiles, m=mark, u=unmark,
 | |
|                           t=tool, I=include, X=exclude, *file)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         if listfiles:
 | |
|             l = []
 | |
|             for line in out.splitlines():
 | |
|                 l.append(tuple(line.split(b(' '), 1)))
 | |
|             return l
 | |
| 
 | |
|     def revert(self, files, rev=None, all=False, date=None, nobackup=False,
 | |
|                dryrun=False, include=None, exclude=None):
 | |
|         """With no revision specified, revert the specified files or
 | |
|         directories to the contents they had in the parent of the
 | |
|         working directory. This restores the contents of files to an
 | |
|         unmodified state and unschedules adds, removes, copies, and
 | |
|         renames. If the working directory has two parents, you must
 | |
|         explicitly specify a revision.
 | |
| 
 | |
|         Specifying rev or date will revert the given files or
 | |
|         directories to their states as of a specific revision. Because
 | |
|         revert does not change the working directory parents, this
 | |
|         will cause these files to appear modified. This can be helpful
 | |
|         to "back out" some or all of an earlier change.
 | |
| 
 | |
|         Modified files are saved with a .orig suffix before reverting.
 | |
|         To disable these backups, use nobackup.
 | |
| 
 | |
|         Returns True on success.
 | |
| 
 | |
|         all - revert all changes when no arguments given
 | |
|         date - tipmost revision matching date
 | |
|         rev - revert to the specified revision
 | |
|         nobackup - do not save backup copies of files
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
|         dryrun - do not perform actions, just print output
 | |
| 
 | |
|         """
 | |
|         if not isinstance(files, list):
 | |
|             files = [files]
 | |
| 
 | |
|         args = cmdbuilder(b('revert'), r=rev, a=all, d=date,
 | |
|                           no_backup=nobackup, n=dryrun, I=include, X=exclude,
 | |
|                           hidden=self.hidden, *files)
 | |
| 
 | |
|         eh = util.reterrorhandler(args)
 | |
|         self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         return bool(eh)
 | |
| 
 | |
|     def root(self):
 | |
|         """
 | |
|         Return the root directory of the current repository.
 | |
|         """
 | |
|         return self.rawcommand([b('root')]).rstrip()
 | |
| 
 | |
|     def status(self, rev=None, change=None, all=False, modified=False,
 | |
|                added=False, removed=False, deleted=False, clean=False,
 | |
|                unknown=False, ignored=False, copies=False,
 | |
|                subrepos=False, include=None, exclude=None):
 | |
|         """
 | |
|         Return status of files in the repository as a list of (code, file path)
 | |
|         where code can be:
 | |
| 
 | |
|                 M = modified
 | |
|                 A = added
 | |
|                 R = removed
 | |
|                 C = clean
 | |
|                 ! = missing (deleted by non-hg command, but still tracked)
 | |
|                 ? = untracked
 | |
|                 I = ignored
 | |
|                   = origin of the previous file listed as A (added)
 | |
| 
 | |
|         rev - show difference from (list of) revision
 | |
|         change - list the changed files of a revision
 | |
|         all - show status of all files
 | |
|         modified - show only modified files
 | |
|         added - show only added files
 | |
|         removed - show only removed files
 | |
|         deleted - show only deleted (but tracked) files
 | |
|         clean - show only files without changes
 | |
|         unknown - show only unknown (not tracked) files
 | |
|         ignored - show only ignored files
 | |
|         copies - show source of copied files
 | |
|         subrepos - recurse into subrepositories
 | |
|         include - include names matching the given patterns
 | |
|         exclude - exclude names matching the given patterns
 | |
|         """
 | |
|         if rev and change:
 | |
|             raise ValueError('cannot specify both rev and change')
 | |
| 
 | |
|         args = cmdbuilder(b('status'), rev=rev, change=change, A=all,
 | |
|                           m=modified, a=added, r=removed, d=deleted, c=clean,
 | |
|                           u=unknown, i=ignored, C=copies, S=subrepos, I=include,
 | |
|                           X=exclude, hidden=self.hidden)
 | |
| 
 | |
|         args.append(b('-0'))
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
|         l = []
 | |
| 
 | |
|         for entry in out.split(b('\0')):
 | |
|             if entry:
 | |
|                 if entry[0:1] == b(' '):
 | |
|                     l.append((b(' '), entry[2:]))
 | |
|                 else:
 | |
|                     l.append(tuple(entry.split(b(' '), 1)))
 | |
| 
 | |
|         return l
 | |
| 
 | |
|     def tag(self, names, rev=None, message=None, force=False, local=False,
 | |
|             remove=False, date=None, user=None):
 | |
|         """Add one or more tags specified by names for the current or given
 | |
|         revision.
 | |
| 
 | |
|         Changing an existing tag is normally disallowed; use force to override.
 | |
| 
 | |
|         Tag commits are usually made at the head of a branch. If the
 | |
|         parent of the working directory is not a branch head, a
 | |
|         CommandError will be raised. force can be specified to force
 | |
|         the tag commit to be based on a non-head changeset.
 | |
| 
 | |
|         local - make the tag local
 | |
|         rev - revision to tag
 | |
|         remove - remove a tag
 | |
|         message - set commit message
 | |
|         date - record the specified date as commit date
 | |
|         user - record the specified user as committer
 | |
| 
 | |
|         """
 | |
|         if not isinstance(names, list):
 | |
|             names = [names]
 | |
| 
 | |
|         args = cmdbuilder(b('tag'), r=rev, m=message, f=force, l=local,
 | |
|                           remove=remove, d=date, u=user, hidden=self.hidden,
 | |
|                           *names)
 | |
| 
 | |
|         self.rawcommand(args)
 | |
| 
 | |
|     def tags(self):
 | |
|         """
 | |
|         Return a list of repository tags as: (name, rev, node, islocal)
 | |
|         """
 | |
|         args = cmdbuilder(b('tags'), v=True)
 | |
| 
 | |
|         out = self.rawcommand(args)
 | |
| 
 | |
|         t = []
 | |
|         for line in out.splitlines():
 | |
|             taglocal = line.endswith(b(' local'))
 | |
|             if taglocal:
 | |
|                 line = line[:-6]
 | |
|             name, rev = line.rsplit(b(' '), 1)
 | |
|             rev, node = rev.split(b(':'))
 | |
|             t.append((name.rstrip(), int(rev), node, taglocal))
 | |
|         return t
 | |
| 
 | |
|     def phase(self, revs=(), secret=False, draft=False, public=False,
 | |
|               force=False):
 | |
|         '''Set or show the current phase name.
 | |
| 
 | |
|         revs - target revision(s)
 | |
|         public - set changeset phase to public
 | |
|         draft - set changeset phase to draft
 | |
|         secret - set changeset phase to secret
 | |
|         force - allow to move boundary backward
 | |
| 
 | |
|         output format: [(id, phase) ...] for each changeset
 | |
| 
 | |
|         The arguments match the mercurial API.
 | |
|         '''
 | |
|         if not isinstance(revs, (list, tuple)):
 | |
|             revs = [revs]
 | |
|         args = util.cmdbuilder(b('phase'), secret=secret, draft=draft,
 | |
|                                public=public, force=force,
 | |
|                                hidden=self.hidden, *revs)
 | |
|         out = self.rawcommand(args)
 | |
|         if draft or public or secret:
 | |
|             return
 | |
|         else:
 | |
|             output = [i.split(b(': '))for i in out.strip().split(b('\n'))]
 | |
|             return [(int(num), phase) for (num, phase) in output]
 | |
| 
 | |
|     def summary(self, remote=False):
 | |
|         """
 | |
|         Return a dictionary with a brief summary of the working directory state,
 | |
|         including parents, branch, commit status, and available updates.
 | |
| 
 | |
|             'parent' : a list of (rev, node, tags, message)
 | |
|             'branch' : the current branch
 | |
|             'commit' : True if the working directory is clean, False otherwise
 | |
|             'update' : number of available updates,
 | |
|             ['remote' : (in, in bookmarks, out, out bookmarks),]
 | |
|             ['mq': (applied, unapplied) mq patches,]
 | |
| 
 | |
|             unparsed entries will be of them form key : value
 | |
|         """
 | |
|         args = cmdbuilder(b('summary'), remote=remote, hidden=self.hidden)
 | |
| 
 | |
|         out = self.rawcommand(args).splitlines()
 | |
| 
 | |
|         d = {}
 | |
|         while out:
 | |
|             line = out.pop(0)
 | |
|             name, value = line.split(b(': '), 1)
 | |
| 
 | |
|             if name == b('parent'):
 | |
|                 parent, tags = value.split(b(' '), 1)
 | |
|                 rev, node = parent.split(b(':'))
 | |
| 
 | |
|                 if tags:
 | |
|                     tags = tags.replace(b(' (empty repository)'), b(''))
 | |
|                 else:
 | |
|                     tags = None
 | |
| 
 | |
|                 value = d.get(name, [])
 | |
| 
 | |
|                 if rev == b('-1'):
 | |
|                     value.append((int(rev), node, tags, None))
 | |
|                 else:
 | |
|                     message = out.pop(0)[1:]
 | |
|                     value.append((int(rev), node, tags, message))
 | |
|             elif name == b('branch'):
 | |
|                 pass
 | |
|             elif name == b('commit'):
 | |
|                 value = value == b('(clean)')
 | |
|             elif name == b('update'):
 | |
|                 if value == b('(current)'):
 | |
|                     value = 0
 | |
|                 else:
 | |
|                     value = int(value.split(b(' '), 1)[0])
 | |
|             elif remote and name == b('remote'):
 | |
|                 if value == b('(synced)'):
 | |
|                     value = 0, 0, 0, 0
 | |
|                 else:
 | |
|                     inc = incb = out_ = outb = 0
 | |
| 
 | |
|                     for v in value.split(b(', ')):
 | |
|                         count, v = v.split(b(' '), 1)
 | |
|                         if v == b('outgoing'):
 | |
|                             out_ = int(count)
 | |
|                         elif v.endswith(b('incoming')):
 | |
|                             inc = int(count)
 | |
|                         elif v == b('incoming bookmarks'):
 | |
|                             incb = int(count)
 | |
|                         elif v == b('outgoing bookmarks'):
 | |
|                             outb = int(count)
 | |
| 
 | |
|                     value = inc, incb, out_, outb
 | |
|             elif name == b('mq'):
 | |
|                 applied = unapplied = 0
 | |
|                 for v in value.split(b(', ')):
 | |
|                     count, v = v.split(b(' '), 1)
 | |
|                     if v == b('applied'):
 | |
|                         applied = int(count)
 | |
|                     elif v == b('unapplied'):
 | |
|                         unapplied = int(count)
 | |
|                 value = applied, unapplied
 | |
| 
 | |
|             d[name] = value
 | |
| 
 | |
|         return d
 | |
| 
 | |
|     def tip(self):
 | |
|         """
 | |
|         Return the tip revision (usually just called the tip) which is the
 | |
|         changeset most recently added to the repository (and therefore the most
 | |
|         recently changed head).
 | |
|         """
 | |
|         args = cmdbuilder(b('tip'), template=templates.changeset,
 | |
|                           hidden=self.hidden)
 | |
|         out = self.rawcommand(args)
 | |
|         out = out.split(b('\0'))
 | |
| 
 | |
|         return self._parserevs(out)[0]
 | |
| 
 | |
|     def update(self, rev=None, clean=False, check=False, date=None):
 | |
|         """
 | |
|         Update the repository's working directory to changeset specified by rev.
 | |
|         If rev isn't specified, update to the tip of the current named branch.
 | |
| 
 | |
|         Return the number of files (updated, merged, removed, unresolved)
 | |
| 
 | |
|         clean - discard uncommitted changes (no backup)
 | |
|         check - update across branches if no uncommitted changes
 | |
|         date - tipmost revision matching date
 | |
|         """
 | |
|         if clean and check:
 | |
|             raise ValueError('clean and check cannot both be True')
 | |
| 
 | |
|         args = cmdbuilder(b('update'), r=rev, C=clean, c=check, d=date,
 | |
|                           hidden=self.hidden)
 | |
| 
 | |
|         def eh(ret, out, err):
 | |
|             if ret == 1:
 | |
|                 return out
 | |
| 
 | |
|             raise error.CommandError(args, ret, out, err)
 | |
| 
 | |
| 
 | |
|         out = self.rawcommand(args, eh=eh)
 | |
| 
 | |
|         m = re.search(b(r'^(\d+).+, (\d+).+, (\d+).+, (\d+)'), out,
 | |
|                       re.MULTILINE)
 | |
|         return tuple(map(int, list(m.groups())))
 | |
| 
 | |
|     @property
 | |
|     def version(self):
 | |
|         """Return hg version that runs the command server as a 4 fielded
 | |
|         tuple: major, minor, micro and local build info. e.g. (1, 9,
 | |
|         1, '+4-3095db9f5c2c')
 | |
|         """
 | |
|         if self._version is None:
 | |
|             v = self.rawcommand(cmdbuilder(b('version'), q=True))
 | |
|             v = list(re.match(b(r'.*?(\d+)\.(\d+)\.?(\d+)?(\+[0-9a-f-]+)?'),
 | |
|                               v).groups())
 | |
| 
 | |
|             for i in range(3):
 | |
|                 try:
 | |
|                     v[i] = int(v[i])
 | |
|                 except TypeError:
 | |
|                     v[i] = 0
 | |
| 
 | |
|             self._version = tuple(v)
 | |
| 
 | |
|         return self._version
 | |
| 
 | |
|     def __getitem__(self, changeid):
 | |
|         try:
 | |
|             return context.changectx(self, changeid)
 | |
|         except ValueError as e:
 | |
|             raise KeyError(*e.args)
 | |
| 
 | |
|     def __contains__(self, changeid):
 | |
|         """
 | |
|         check if changeid, which can be either a local revision number or a
 | |
|         changeset id, matches a changeset in the client.
 | |
|         """
 | |
|         try:
 | |
|             context.changectx(self, changeid)
 | |
|             return True
 | |
|         except ValueError:
 | |
|             return False
 |