##// END OF EJS Templates
convert: Perforce source for conversion to Mercurial
Frank Kingswood -
r7823:11efa410 1.2 default
parent child Browse files
Show More
@@ -0,0 +1,176 b''
1 #
2 # Perforce source for convert extension.
3 #
4 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
5 #
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
8 #
9
10 from mercurial import util
11 from mercurial.i18n import _
12
13 from common import commit, converter_source, checktool
14 import marshal
15
16 def loaditer(f):
17 "Yield the dictionary objects generated by p4"
18 try:
19 while True:
20 d = marshal.load(f)
21 if not d:
22 break
23 yield d
24 except EOFError:
25 pass
26
27 class p4_source(converter_source):
28 def __init__(self, ui, path, rev=None):
29 super(p4_source, self).__init__(ui, path, rev=rev)
30
31 checktool('p4')
32
33 self.p4changes = {}
34 self.heads = {}
35 self.changeset = {}
36 self.files = {}
37 self.tags = {}
38 self.lastbranch = {}
39 self.parent = {}
40 self.encoding = "latin_1"
41 self.depotname = {} # mapping from local name to depot name
42 self.modecache = {}
43
44 self._parse(ui, path)
45
46 def _parse_view(self, path):
47 "Read changes affecting the path"
48 cmd = "p4 -G changes -s submitted '%s'" % path
49 stdout = util.popen(cmd)
50 for d in loaditer(stdout):
51 c = d.get("change", None)
52 if c:
53 self.p4changes[c] = True
54
55 def _parse(self, ui, path):
56 "Prepare list of P4 filenames and revisions to import"
57 ui.status(_('reading p4 views\n'))
58
59 # read client spec or view
60 if "/" in path:
61 self._parse_view(path)
62 if path.startswith("//") and path.endswith("/..."):
63 views = {path[:-3]:""}
64 else:
65 views = {"//": ""}
66 else:
67 cmd = "p4 -G client -o '%s'" % path
68 clientspec = marshal.load(util.popen(cmd))
69
70 views = {}
71 for client in clientspec:
72 if client.startswith("View"):
73 sview, cview = clientspec[client].split()
74 self._parse_view(sview)
75 if sview.endswith("...") and cview.endswith("..."):
76 sview = sview[:-3]
77 cview = cview[:-3]
78 cview = cview[2:]
79 cview = cview[cview.find("/") + 1:]
80 views[sview] = cview
81
82 # list of changes that affect our source files
83 self.p4changes = self.p4changes.keys()
84 self.p4changes.sort(key=int)
85
86 # list with depot pathnames, longest first
87 vieworder = views.keys()
88 vieworder.sort(key=lambda x: -len(x))
89
90 # handle revision limiting
91 startrev = self.ui.config('convert', 'p4.startrev', default=0)
92 self.p4changes = [x for x in self.p4changes
93 if ((not startrev or int(x) >= int(startrev)) and
94 (not self.rev or int(x) <= int(self.rev)))]
95
96 # now read the full changelists to get the list of file revisions
97 ui.status(_('collecting p4 changelists\n'))
98 lastid = None
99 for change in self.p4changes:
100 cmd = "p4 -G describe %s" % change
101 stdout = util.popen(cmd)
102 d = marshal.load(stdout)
103
104 desc = self.recode(d["desc"])
105 shortdesc = desc.split("\n", 1)[0]
106 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
107 ui.status(util.ellipsis(t, 80) + '\n')
108
109 if lastid:
110 parents = [lastid]
111 else:
112 parents = []
113
114 date = (int(d["time"]), 0) # timezone not set
115 c = commit(author=self.recode(d["user"]), date=util.datestr(date),
116 parents=parents, desc=desc, branch='', extra={"p4": change})
117
118 files = []
119 i = 0
120 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
121 oldname = d["depotFile%d" % i]
122 filename = None
123 for v in vieworder:
124 if oldname.startswith(v):
125 filename = views[v] + oldname[len(v):]
126 break
127 if filename:
128 files.append((filename, d["rev%d" % i]))
129 self.depotname[filename] = oldname
130 i += 1
131 self.changeset[change] = c
132 self.files[change] = files
133 lastid = change
134
135 if lastid:
136 self.heads = [lastid]
137
138 def getheads(self):
139 return self.heads
140
141 def getfile(self, name, rev):
142 cmd = "p4 -G print '%s#%s'" % (self.depotname[name], rev)
143 stdout = util.popen(cmd)
144
145 mode = None
146 data = ""
147
148 for d in loaditer(stdout):
149 if d["code"] == "stat":
150 if "+x" in d["type"]:
151 mode = "x"
152 else:
153 mode = ""
154 elif d["code"] == "text":
155 data += d["data"]
156
157 if mode is None:
158 raise IOError()
159
160 self.modecache[(name, rev)] = mode
161 return data
162
163 def getmode(self, name, rev):
164 return self.modecache[(name, rev)]
165
166 def getchanges(self, rev):
167 return self.files[rev], {}
168
169 def getcommit(self, rev):
170 return self.changeset[rev]
171
172 def gettags(self):
173 return self.tags
174
175 def getchangedfiles(self, rev, i):
176 return util.sort([x[0] for x in self.files[rev]])
@@ -0,0 +1,75 b''
1 #!/bin/sh
2
3 "$TESTDIR/hghave" p4 || exit 80
4
5 echo "[extensions]" >> $HGRCPATH
6 echo "convert = " >> $HGRCPATH
7
8 echo % create p4 depot
9 export P4ROOT=$PWD/depot
10 export P4AUDIT=$P4ROOT/audit
11 export P4JOURNAL=$P4ROOT/journal
12 export P4LOG=$P4ROOT/log
13 export P4PORT=localhost:16661
14 export P4DEBUG=1
15
16 echo % start the p4 server
17 [ ! -d $P4ROOT ] && mkdir $P4ROOT
18 p4d -f -J off >$P4ROOT/stdout 2>$P4ROOT/stderr &
19 trap "echo % stop the p4 server ; p4 admin stop" EXIT
20
21 # wait for the server to initialize
22 while ! p4 ; do
23 sleep 1
24 done >/dev/null 2>/dev/null
25
26 echo % create a client spec
27 export P4CLIENT=hg-p4-import
28 DEPOTPATH=//depot/test-mercurial-import/...
29 p4 client -o | sed '/^View:/,$ d' >p4client
30 echo View: >>p4client
31 echo " $DEPOTPATH //$P4CLIENT/..." >>p4client
32 p4 client -i <p4client
33
34 echo % populate the depot
35 echo a > a
36 mkdir b
37 echo c > b/c
38 p4 add a b/c
39 p4 submit -d initial
40
41 echo % change some files
42 p4 edit a
43 echo aa >> a
44 p4 submit -d "change a"
45
46 p4 edit b/c
47 echo cc >> b/c
48 p4 submit -d "change b/c"
49
50 echo % convert
51 hg convert -s p4 $DEPOTPATH dst
52 hg -R dst log --template 'rev=#rev# desc="#desc#" tags="#tags#" files="#files#"\n'
53
54 echo % change some files
55 p4 edit a b/c
56 echo aaa >> a
57 echo ccc >> b/c
58 p4 submit -d "change a b/c"
59
60 echo % convert again
61 hg convert -s p4 $DEPOTPATH dst
62 hg -R dst log --template 'rev=#rev# desc="#desc#" tags="#tags#" files="#files#"\n'
63
64 echo % interesting names
65 echo dddd > "d d"
66 mkdir " e "
67 echo fff >" e /f "
68 p4 add "d d" " e /f "
69 p4 submit -d "add d e f"
70
71 echo % convert again
72 hg convert -s p4 $DEPOTPATH dst
73 hg -R dst log --template 'rev=#rev# desc="#desc#" tags="#tags#" files="#files#"\n'
74
75
@@ -0,0 +1,88 b''
1 % create p4 depot
2 % start the p4 server
3 % create a client spec
4 Client hg-p4-import saved.
5 % populate the depot
6 //depot/test-mercurial-import/a#1 - opened for add
7 //depot/test-mercurial-import/b/c#1 - opened for add
8 Submitting change 1.
9 Locking 2 files ...
10 add //depot/test-mercurial-import/a#1
11 add //depot/test-mercurial-import/b/c#1
12 Change 1 submitted.
13 % change some files
14 //depot/test-mercurial-import/a#1 - opened for edit
15 Submitting change 2.
16 Locking 1 files ...
17 edit //depot/test-mercurial-import/a#2
18 Change 2 submitted.
19 //depot/test-mercurial-import/b/c#1 - opened for edit
20 Submitting change 3.
21 Locking 1 files ...
22 edit //depot/test-mercurial-import/b/c#2
23 Change 3 submitted.
24 % convert
25 initializing destination dst repository
26 reading p4 views
27 collecting p4 changelists
28 1 initial
29 2 change a
30 3 change b/c
31 scanning source...
32 sorting...
33 converting...
34 2 initial
35 1 change a
36 0 change b/c
37 rev=2 desc="change b/c" tags="tip" files="b/c"
38 rev=1 desc="change a" tags="" files="a"
39 rev=0 desc="initial" tags="" files="a b/c"
40 % change some files
41 //depot/test-mercurial-import/a#2 - opened for edit
42 //depot/test-mercurial-import/b/c#2 - opened for edit
43 Submitting change 4.
44 Locking 2 files ...
45 edit //depot/test-mercurial-import/a#3
46 edit //depot/test-mercurial-import/b/c#3
47 Change 4 submitted.
48 % convert again
49 reading p4 views
50 collecting p4 changelists
51 1 initial
52 2 change a
53 3 change b/c
54 4 change a b/c
55 scanning source...
56 sorting...
57 converting...
58 0 change a b/c
59 rev=3 desc="change a b/c" tags="tip" files="a b/c"
60 rev=2 desc="change b/c" tags="" files="b/c"
61 rev=1 desc="change a" tags="" files="a"
62 rev=0 desc="initial" tags="" files="a b/c"
63 % interesting names
64 //depot/test-mercurial-import/d d#1 - opened for add
65 //depot/test-mercurial-import/ e /f #1 - opened for add
66 Submitting change 5.
67 Locking 2 files ...
68 add //depot/test-mercurial-import/ e /f #1
69 add //depot/test-mercurial-import/d d#1
70 Change 5 submitted.
71 % convert again
72 reading p4 views
73 collecting p4 changelists
74 1 initial
75 2 change a
76 3 change b/c
77 4 change a b/c
78 5 add d e f
79 scanning source...
80 sorting...
81 converting...
82 0 add d e f
83 rev=4 desc="add d e f" tags="tip" files=" e /f d d"
84 rev=3 desc="change a b/c" tags="" files="a b/c"
85 rev=2 desc="change b/c" tags="" files="b/c"
86 rev=1 desc="change a" tags="" files="a"
87 rev=0 desc="initial" tags="" files="a b/c"
88 % stop the p4 server
@@ -25,6 +25,7 b' def convert(ui, src, dest=None, revmapfi'
25 - Monotone [mtn]
25 - Monotone [mtn]
26 - GNU Arch [gnuarch]
26 - GNU Arch [gnuarch]
27 - Bazaar [bzr]
27 - Bazaar [bzr]
28 - Perforce [p4]
28
29
29 Accepted destination formats [identifiers]:
30 Accepted destination formats [identifiers]:
30 - Mercurial [hg]
31 - Mercurial [hg]
@@ -168,6 +169,22 b' def convert(ui, src, dest=None, revmapfi'
168 --config convert.svn.startrev=0 (svn revision number)
169 --config convert.svn.startrev=0 (svn revision number)
169 specify start Subversion revision.
170 specify start Subversion revision.
170
171
172 Perforce Source
173 ---------------
174
175 The Perforce (P4) importer can be given a p4 depot path or a client
176 specification as source. It will convert all files in the source to
177 a flat Mercurial repository, ignoring labels, branches and integrations.
178 Note that when a depot path is given you then usually should specify a
179 target directory, because otherwise the target may be named ...-hg.
180
181 It is possible to limit the amount of source history to be converted
182 by specifying an initial Perforce revision.
183
184 --config convert.p4.startrev=0 (perforce changelist number)
185 specify initial Perforce revision.
186
187
171 Mercurial Destination
188 Mercurial Destination
172 ---------------------
189 ---------------------
173
190
@@ -14,6 +14,7 b' from subversion import debugsvnlog, svn_'
14 from monotone import monotone_source
14 from monotone import monotone_source
15 from gnuarch import gnuarch_source
15 from gnuarch import gnuarch_source
16 from bzr import bzr_source
16 from bzr import bzr_source
17 from p4 import p4_source
17 import filemap
18 import filemap
18
19
19 import os, shutil
20 import os, shutil
@@ -37,6 +38,7 b' source_converters = ['
37 ('mtn', monotone_source),
38 ('mtn', monotone_source),
38 ('gnuarch', gnuarch_source),
39 ('gnuarch', gnuarch_source),
39 ('bzr', bzr_source),
40 ('bzr', bzr_source),
41 ('p4', p4_source),
40 ]
42 ]
41
43
42 sink_converters = [
44 sink_converters = [
@@ -125,6 +125,9 b' def has_svn_bindings():'
125 except ImportError:
125 except ImportError:
126 return False
126 return False
127
127
128 def has_p4():
129 return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/')
130
128 def has_symlink():
131 def has_symlink():
129 return hasattr(os, "symlink")
132 return hasattr(os, "symlink")
130
133
@@ -173,6 +176,7 b' checks = {'
173 "lsprof": (has_lsprof, "python lsprof module"),
176 "lsprof": (has_lsprof, "python lsprof module"),
174 "mtn": (has_mtn, "monotone client (> 0.31)"),
177 "mtn": (has_mtn, "monotone client (> 0.31)"),
175 "outer-repo": (has_outer_repo, "outer repo"),
178 "outer-repo": (has_outer_repo, "outer repo"),
179 "p4": (has_p4, "Perforce server and client"),
176 "pygments": (has_pygments, "Pygments source highlighting library"),
180 "pygments": (has_pygments, "Pygments source highlighting library"),
177 "svn": (has_svn, "subversion client and admin tools"),
181 "svn": (has_svn, "subversion client and admin tools"),
178 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
182 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
@@ -11,6 +11,7 b' convert a foreign SCM repository to a Me'
11 - Monotone [mtn]
11 - Monotone [mtn]
12 - GNU Arch [gnuarch]
12 - GNU Arch [gnuarch]
13 - Bazaar [bzr]
13 - Bazaar [bzr]
14 - Perforce [p4]
14
15
15 Accepted destination formats [identifiers]:
16 Accepted destination formats [identifiers]:
16 - Mercurial [hg]
17 - Mercurial [hg]
@@ -154,6 +155,22 b' convert a foreign SCM repository to a Me'
154 --config convert.svn.startrev=0 (svn revision number)
155 --config convert.svn.startrev=0 (svn revision number)
155 specify start Subversion revision.
156 specify start Subversion revision.
156
157
158 Perforce Source
159 ---------------
160
161 The Perforce (P4) importer can be given a p4 depot path or a client
162 specification as source. It will convert all files in the source to
163 a flat Mercurial repository, ignoring labels, branches and integrations.
164 Note that when a depot path is given you then usually should specify a
165 target directory, because otherwise the target may be named ...-hg.
166
167 It is possible to limit the amount of source history to be converted
168 by specifying an initial Perforce revision.
169
170 --config convert.p4.startrev=0 (perforce changelist number)
171 specify initial Perforce revision.
172
173
157 Mercurial Destination
174 Mercurial Destination
158 ---------------------
175 ---------------------
159
176
General Comments 0
You need to be logged in to leave comments. Login now