[PATCH] convert: extend Perforce converter to support (unofficial) p4 URI scheme
Eugene Baranov
eug.baranov at gmail.com
Thu Jul 16 17:34:45 UTC 2015
# HG changeset patch
# User Eugene Baranov <eug.baranov at gmail.com>
# Date 1437065858 -3600
# Thu Jul 16 17:57:38 2015 +0100
# Node ID bcb96f98d9cfade5098a9a12b49714d218441f83
# Parent 7f1fc20a2a2c5003e8f973248b8049c6f6c9c859
convert: extend Perforce converter to support (unofficial) p4 URI scheme
Before this patch converting from Perforce repository would require you to set
up quite a few environment variables before 'p4' tool would work correctly as
well as explicitly passing source type of 'p4' to 'hg convert'. Which is not
a big deal but can get messy especially if you need to deal with different
Perforce servers.
Introducing support for p4 URI scheme allows to simplify:
export P4PORT=p4.local:1234
export P4USER=admin
export P4PASSWD=xyz
hg convert -s p4 //Depot/Test/... Test-hg
into:
hg convert p4://admin:xyz at p4.local:1234/Depot/Test
diff -r 7f1fc20a2a2c -r bcb96f98d9cf hgext/convert/p4.py
--- a/hgext/convert/p4.py Tue Jul 14 14:40:56 2015 +0100
+++ b/hgext/convert/p4.py Thu Jul 16 17:57:38 2015 +0100
@@ -37,12 +37,97 @@
filename = filename.replace(k, v)
return filename
+class p4connection:
+ def __init__(self, path):
+ '''
+ >>> tests = ['p4://admin:xyz at test.local:4321/Depot/Test',
+ ... 'p4://user:password@/Development/Branch/',
+ ... 'p4://power at abc.com/Something/Somewhere/...',
+ ... 'p4://127.0.0.1/local',
+ ... 'p4:///Repo',
+ ... '//Depot/Main',
+ ... 'Client']
+ >>> for t in tests:
+ ... p4 = p4connection(t)
+ ... p4.username, p4.password, p4.port, p4.path
+ ('admin', 'xyz', 'test.local:4321', '//Depot/Test/...')
+ ('user', 'password', '', '//Development/Branch/...')
+ ('power', None, 'abc.com', '//Something/Somewhere/...')
+ (None, None, '127.0.0.1', '//local/...')
+ (None, None, '', '//Repo/...')
+ (None, None, None, '//Depot/Main')
+ (None, None, None, 'Client')
+ '''
+ self.username = None
+ self.password = None
+ self.port = None
+ # Additional behaviour to recognize Perforce URL in a format:
+ # p4://[username[:password]@]/Depot/Name[/[...]]
+ # (it will make sure there is a trailing '/...')
+ scheme = 'p4://'
+ if path.startswith(scheme):
+ path = path[len(scheme):]
+ netloc = None
+ if '/' in path:
+ netloc, path = path.split('/', 1)
+ else:
+ netloc, path = path, ''
+ if '@' in netloc:
+ userinfo, self.port = netloc.split('@', 1)
+ if ':' in userinfo:
+ self.username, self.password = userinfo.split(':', 1)
+ else:
+ self.username = userinfo
+ else:
+ self.port = netloc
+
+ # Make sure path starts with '//'
+ while (not path.startswith('//')):
+ path = '/' + path
+ # and ends with '/...'
+ if path.endswith('/'):
+ path += '...'
+ elif not path.endswith('/...'):
+ path += '/...'
+ self.path = path
+
+ def _getcommand(self, p4cmd):
+ '''
+ >>> tests = ['p4:///Depot/Main',
+ ... 'p4:///Repo',
+ ... '//Depot/Main',
+ ... 'Client']
+ >>> for t in tests:
+ ... p4 = p4connection(t)
+ ... p4._getcommand('test')
+ 'p4 -G test'
+ 'p4 -G test'
+ 'p4 -G test'
+ 'p4 -G test'
+ '''
+ p4opts = ''
+ opts = [('-p', self.port),
+ ('-u', self.username),
+ ('-P', self.password)]
+ for k, v in opts:
+ if v:
+ p4opts += k + ' ' + util.shellquote(v) + ' '
+ cmd = 'p4 -G ' + p4opts + p4cmd
+ return cmd
+
+ def runcommand(self, cmd):
+ cmd = self._getcommand(cmd)
+ stdout = util.popen(cmd, mode='rb')
+ return stdout
+
class p4_source(converter_source):
def __init__(self, ui, path, revs=None):
super(p4_source, self).__init__(ui, path, revs=revs)
- if "/" in path and not path.startswith('//'):
+ if ("/" in path and not path.startswith('//')
+ and not path.startswith('p4://')):
raise NoRepo(_('%s does not look like a P4 repository') % path)
+ self.p4 = p4connection(path)
checktool('p4', abort=False)
@@ -68,12 +153,12 @@
if revs and len(revs) > 1:
raise util.Abort(_("p4 source does not support specifying "
"multiple revisions"))
- self._parse(ui, path)
+ self._parse(ui, self.p4.path)
def _parse_view(self, path):
"Read changes affecting the path"
- cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
- stdout = util.popen(cmd, mode='rb')
+ cmd = 'changes -s submitted %s' % util.shellquote(path)
+ stdout = self.p4.runcommand(cmd)
for d in loaditer(stdout):
c = d.get("change", None)
if c:
@@ -91,8 +176,8 @@
else:
views = {"//": ""}
else:
- cmd = 'p4 -G client -o %s' % util.shellquote(path)
- clientspec = marshal.load(util.popen(cmd, mode='rb'))
+ cmd = 'client -o %s' % util.shellquote(path)
+ clientspec = marshal.load(self.p4.runcommand(cmd))
views = {}
for client in clientspec:
@@ -124,8 +209,7 @@
ui.status(_('collecting p4 changelists\n'))
lastid = None
for change in self.p4changes:
- cmd = "p4 -G describe -s %s" % change
- stdout = util.popen(cmd, mode='rb')
+ stdout = self.p4.runcommand('describe -s %s' % change)
d = marshal.load(stdout)
desc = self.recode(d.get("desc", ""))
shortdesc = desc.split("\n", 1)[0]
@@ -166,9 +250,8 @@
for filename in copiedfiles:
oldname = self.depotname[filename]
- flcmd = 'p4 -G filelog %s' \
- % util.shellquote(oldname)
- flstdout = util.popen(flcmd, mode='rb')
+ flcmd = 'filelog %s' % util.shellquote(oldname)
+ flstdout = self.p4.runcommand(flcmd)
copiedfilename = None
for d in loaditer(flstdout):
@@ -208,12 +291,12 @@
return self.heads
def getfile(self, name, rev):
- cmd = 'p4 -G print %s' \
+ cmd = 'print %s' \
% util.shellquote("%s#%s" % (self.depotname[name], rev))
lasterror = None
while True:
- stdout = util.popen(cmd, mode='rb')
+ stdout = self.p4.runcommand(cmd)
mode = None
contents = ""
More information about the Mercurial-devel
mailing list