##// END OF EJS Templates
convert: pass absolute paths to git (SEC)...
Blake Burkhart -
r29051:a56296f5 3.8.1 stable
parent child Browse files
Show More
@@ -1,394 +1,398 b''
1 # git.py - git support for the convert extension
1 # git.py - git support for the convert extension
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import os
9 import os
10 from mercurial import (
10 from mercurial import (
11 config,
11 config,
12 error,
12 error,
13 node as nodemod,
13 node as nodemod,
14 )
14 )
15 from mercurial.i18n import _
15 from mercurial.i18n import _
16
16
17 from . import (
17 from . import (
18 common,
18 common,
19 )
19 )
20
20
21 class submodule(object):
21 class submodule(object):
22 def __init__(self, path, node, url):
22 def __init__(self, path, node, url):
23 self.path = path
23 self.path = path
24 self.node = node
24 self.node = node
25 self.url = url
25 self.url = url
26
26
27 def hgsub(self):
27 def hgsub(self):
28 return "%s = [git]%s" % (self.path, self.url)
28 return "%s = [git]%s" % (self.path, self.url)
29
29
30 def hgsubstate(self):
30 def hgsubstate(self):
31 return "%s %s" % (self.node, self.path)
31 return "%s %s" % (self.node, self.path)
32
32
33 class convert_git(common.converter_source, common.commandline):
33 class convert_git(common.converter_source, common.commandline):
34 # Windows does not support GIT_DIR= construct while other systems
34 # Windows does not support GIT_DIR= construct while other systems
35 # cannot remove environment variable. Just assume none have
35 # cannot remove environment variable. Just assume none have
36 # both issues.
36 # both issues.
37
37
38 def _gitcmd(self, cmd, *args, **kwargs):
38 def _gitcmd(self, cmd, *args, **kwargs):
39 return cmd('--git-dir=%s' % self.path, *args, **kwargs)
39 return cmd('--git-dir=%s' % self.path, *args, **kwargs)
40
40
41 def gitrun0(self, *args, **kwargs):
41 def gitrun0(self, *args, **kwargs):
42 return self._gitcmd(self.run0, *args, **kwargs)
42 return self._gitcmd(self.run0, *args, **kwargs)
43
43
44 def gitrun(self, *args, **kwargs):
44 def gitrun(self, *args, **kwargs):
45 return self._gitcmd(self.run, *args, **kwargs)
45 return self._gitcmd(self.run, *args, **kwargs)
46
46
47 def gitrunlines0(self, *args, **kwargs):
47 def gitrunlines0(self, *args, **kwargs):
48 return self._gitcmd(self.runlines0, *args, **kwargs)
48 return self._gitcmd(self.runlines0, *args, **kwargs)
49
49
50 def gitrunlines(self, *args, **kwargs):
50 def gitrunlines(self, *args, **kwargs):
51 return self._gitcmd(self.runlines, *args, **kwargs)
51 return self._gitcmd(self.runlines, *args, **kwargs)
52
52
53 def gitpipe(self, *args, **kwargs):
53 def gitpipe(self, *args, **kwargs):
54 return self._gitcmd(self._run3, *args, **kwargs)
54 return self._gitcmd(self._run3, *args, **kwargs)
55
55
56 def __init__(self, ui, path, revs=None):
56 def __init__(self, ui, path, revs=None):
57 super(convert_git, self).__init__(ui, path, revs=revs)
57 super(convert_git, self).__init__(ui, path, revs=revs)
58 common.commandline.__init__(self, ui, 'git')
58 common.commandline.__init__(self, ui, 'git')
59
59
60 # Pass an absolute path to git to prevent from ever being interpreted
61 # as a URL
62 path = os.path.abspath(path)
63
60 if os.path.isdir(path + "/.git"):
64 if os.path.isdir(path + "/.git"):
61 path += "/.git"
65 path += "/.git"
62 if not os.path.exists(path + "/objects"):
66 if not os.path.exists(path + "/objects"):
63 raise common.NoRepo(_("%s does not look like a Git repository") %
67 raise common.NoRepo(_("%s does not look like a Git repository") %
64 path)
68 path)
65
69
66 # The default value (50) is based on the default for 'git diff'.
70 # The default value (50) is based on the default for 'git diff'.
67 similarity = ui.configint('convert', 'git.similarity', default=50)
71 similarity = ui.configint('convert', 'git.similarity', default=50)
68 if similarity < 0 or similarity > 100:
72 if similarity < 0 or similarity > 100:
69 raise error.Abort(_('similarity must be between 0 and 100'))
73 raise error.Abort(_('similarity must be between 0 and 100'))
70 if similarity > 0:
74 if similarity > 0:
71 self.simopt = ['-C%d%%' % similarity]
75 self.simopt = ['-C%d%%' % similarity]
72 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
76 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
73 False)
77 False)
74 if findcopiesharder:
78 if findcopiesharder:
75 self.simopt.append('--find-copies-harder')
79 self.simopt.append('--find-copies-harder')
76 else:
80 else:
77 self.simopt = []
81 self.simopt = []
78
82
79 common.checktool('git', 'git')
83 common.checktool('git', 'git')
80
84
81 self.path = path
85 self.path = path
82 self.submodules = []
86 self.submodules = []
83
87
84 self.catfilepipe = self.gitpipe('cat-file', '--batch')
88 self.catfilepipe = self.gitpipe('cat-file', '--batch')
85
89
86 def after(self):
90 def after(self):
87 for f in self.catfilepipe:
91 for f in self.catfilepipe:
88 f.close()
92 f.close()
89
93
90 def getheads(self):
94 def getheads(self):
91 if not self.revs:
95 if not self.revs:
92 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
96 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
93 heads = output.splitlines()
97 heads = output.splitlines()
94 if status:
98 if status:
95 raise error.Abort(_('cannot retrieve git heads'))
99 raise error.Abort(_('cannot retrieve git heads'))
96 else:
100 else:
97 heads = []
101 heads = []
98 for rev in self.revs:
102 for rev in self.revs:
99 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
103 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
100 heads.append(rawhead[:-1])
104 heads.append(rawhead[:-1])
101 if ret:
105 if ret:
102 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
106 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
103 return heads
107 return heads
104
108
105 def catfile(self, rev, type):
109 def catfile(self, rev, type):
106 if rev == nodemod.nullhex:
110 if rev == nodemod.nullhex:
107 raise IOError
111 raise IOError
108 self.catfilepipe[0].write(rev+'\n')
112 self.catfilepipe[0].write(rev+'\n')
109 self.catfilepipe[0].flush()
113 self.catfilepipe[0].flush()
110 info = self.catfilepipe[1].readline().split()
114 info = self.catfilepipe[1].readline().split()
111 if info[1] != type:
115 if info[1] != type:
112 raise error.Abort(_('cannot read %r object at %s') % (type, rev))
116 raise error.Abort(_('cannot read %r object at %s') % (type, rev))
113 size = int(info[2])
117 size = int(info[2])
114 data = self.catfilepipe[1].read(size)
118 data = self.catfilepipe[1].read(size)
115 if len(data) < size:
119 if len(data) < size:
116 raise error.Abort(_('cannot read %r object at %s: unexpected size')
120 raise error.Abort(_('cannot read %r object at %s: unexpected size')
117 % (type, rev))
121 % (type, rev))
118 # read the trailing newline
122 # read the trailing newline
119 self.catfilepipe[1].read(1)
123 self.catfilepipe[1].read(1)
120 return data
124 return data
121
125
122 def getfile(self, name, rev):
126 def getfile(self, name, rev):
123 if rev == nodemod.nullhex:
127 if rev == nodemod.nullhex:
124 return None, None
128 return None, None
125 if name == '.hgsub':
129 if name == '.hgsub':
126 data = '\n'.join([m.hgsub() for m in self.submoditer()])
130 data = '\n'.join([m.hgsub() for m in self.submoditer()])
127 mode = ''
131 mode = ''
128 elif name == '.hgsubstate':
132 elif name == '.hgsubstate':
129 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
133 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
130 mode = ''
134 mode = ''
131 else:
135 else:
132 data = self.catfile(rev, "blob")
136 data = self.catfile(rev, "blob")
133 mode = self.modecache[(name, rev)]
137 mode = self.modecache[(name, rev)]
134 return data, mode
138 return data, mode
135
139
136 def submoditer(self):
140 def submoditer(self):
137 null = nodemod.nullhex
141 null = nodemod.nullhex
138 for m in sorted(self.submodules, key=lambda p: p.path):
142 for m in sorted(self.submodules, key=lambda p: p.path):
139 if m.node != null:
143 if m.node != null:
140 yield m
144 yield m
141
145
142 def parsegitmodules(self, content):
146 def parsegitmodules(self, content):
143 """Parse the formatted .gitmodules file, example file format:
147 """Parse the formatted .gitmodules file, example file format:
144 [submodule "sub"]\n
148 [submodule "sub"]\n
145 \tpath = sub\n
149 \tpath = sub\n
146 \turl = git://giturl\n
150 \turl = git://giturl\n
147 """
151 """
148 self.submodules = []
152 self.submodules = []
149 c = config.config()
153 c = config.config()
150 # Each item in .gitmodules starts with whitespace that cant be parsed
154 # Each item in .gitmodules starts with whitespace that cant be parsed
151 c.parse('.gitmodules', '\n'.join(line.strip() for line in
155 c.parse('.gitmodules', '\n'.join(line.strip() for line in
152 content.split('\n')))
156 content.split('\n')))
153 for sec in c.sections():
157 for sec in c.sections():
154 s = c[sec]
158 s = c[sec]
155 if 'url' in s and 'path' in s:
159 if 'url' in s and 'path' in s:
156 self.submodules.append(submodule(s['path'], '', s['url']))
160 self.submodules.append(submodule(s['path'], '', s['url']))
157
161
158 def retrievegitmodules(self, version):
162 def retrievegitmodules(self, version):
159 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
163 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
160 if ret:
164 if ret:
161 # This can happen if a file is in the repo that has permissions
165 # This can happen if a file is in the repo that has permissions
162 # 160000, but there is no .gitmodules file.
166 # 160000, but there is no .gitmodules file.
163 self.ui.warn(_("warning: cannot read submodules config file in "
167 self.ui.warn(_("warning: cannot read submodules config file in "
164 "%s\n") % version)
168 "%s\n") % version)
165 return
169 return
166
170
167 try:
171 try:
168 self.parsegitmodules(modules)
172 self.parsegitmodules(modules)
169 except error.ParseError:
173 except error.ParseError:
170 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
174 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
171 % version)
175 % version)
172 return
176 return
173
177
174 for m in self.submodules:
178 for m in self.submodules:
175 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
179 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
176 if ret:
180 if ret:
177 continue
181 continue
178 m.node = node.strip()
182 m.node = node.strip()
179
183
180 def getchanges(self, version, full):
184 def getchanges(self, version, full):
181 if full:
185 if full:
182 raise error.Abort(_("convert from git does not support --full"))
186 raise error.Abort(_("convert from git does not support --full"))
183 self.modecache = {}
187 self.modecache = {}
184 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
188 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
185 output, status = self.gitrun(*cmd)
189 output, status = self.gitrun(*cmd)
186 if status:
190 if status:
187 raise error.Abort(_('cannot read changes in %s') % version)
191 raise error.Abort(_('cannot read changes in %s') % version)
188 changes = []
192 changes = []
189 copies = {}
193 copies = {}
190 seen = set()
194 seen = set()
191 entry = None
195 entry = None
192 subexists = [False]
196 subexists = [False]
193 subdeleted = [False]
197 subdeleted = [False]
194 difftree = output.split('\x00')
198 difftree = output.split('\x00')
195 lcount = len(difftree)
199 lcount = len(difftree)
196 i = 0
200 i = 0
197
201
198 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
202 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules',
199 False)
203 False)
200 def add(entry, f, isdest):
204 def add(entry, f, isdest):
201 seen.add(f)
205 seen.add(f)
202 h = entry[3]
206 h = entry[3]
203 p = (entry[1] == "100755")
207 p = (entry[1] == "100755")
204 s = (entry[1] == "120000")
208 s = (entry[1] == "120000")
205 renamesource = (not isdest and entry[4][0] == 'R')
209 renamesource = (not isdest and entry[4][0] == 'R')
206
210
207 if f == '.gitmodules':
211 if f == '.gitmodules':
208 if skipsubmodules:
212 if skipsubmodules:
209 return
213 return
210
214
211 subexists[0] = True
215 subexists[0] = True
212 if entry[4] == 'D' or renamesource:
216 if entry[4] == 'D' or renamesource:
213 subdeleted[0] = True
217 subdeleted[0] = True
214 changes.append(('.hgsub', nodemod.nullhex))
218 changes.append(('.hgsub', nodemod.nullhex))
215 else:
219 else:
216 changes.append(('.hgsub', ''))
220 changes.append(('.hgsub', ''))
217 elif entry[1] == '160000' or entry[0] == ':160000':
221 elif entry[1] == '160000' or entry[0] == ':160000':
218 if not skipsubmodules:
222 if not skipsubmodules:
219 subexists[0] = True
223 subexists[0] = True
220 else:
224 else:
221 if renamesource:
225 if renamesource:
222 h = nodemod.nullhex
226 h = nodemod.nullhex
223 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
227 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
224 changes.append((f, h))
228 changes.append((f, h))
225
229
226 while i < lcount:
230 while i < lcount:
227 l = difftree[i]
231 l = difftree[i]
228 i += 1
232 i += 1
229 if not entry:
233 if not entry:
230 if not l.startswith(':'):
234 if not l.startswith(':'):
231 continue
235 continue
232 entry = l.split()
236 entry = l.split()
233 continue
237 continue
234 f = l
238 f = l
235 if entry[4][0] == 'C':
239 if entry[4][0] == 'C':
236 copysrc = f
240 copysrc = f
237 copydest = difftree[i]
241 copydest = difftree[i]
238 i += 1
242 i += 1
239 f = copydest
243 f = copydest
240 copies[copydest] = copysrc
244 copies[copydest] = copysrc
241 if f not in seen:
245 if f not in seen:
242 add(entry, f, False)
246 add(entry, f, False)
243 # A file can be copied multiple times, or modified and copied
247 # A file can be copied multiple times, or modified and copied
244 # simultaneously. So f can be repeated even if fdest isn't.
248 # simultaneously. So f can be repeated even if fdest isn't.
245 if entry[4][0] == 'R':
249 if entry[4][0] == 'R':
246 # rename: next line is the destination
250 # rename: next line is the destination
247 fdest = difftree[i]
251 fdest = difftree[i]
248 i += 1
252 i += 1
249 if fdest not in seen:
253 if fdest not in seen:
250 add(entry, fdest, True)
254 add(entry, fdest, True)
251 # .gitmodules isn't imported at all, so it being copied to
255 # .gitmodules isn't imported at all, so it being copied to
252 # and fro doesn't really make sense
256 # and fro doesn't really make sense
253 if f != '.gitmodules' and fdest != '.gitmodules':
257 if f != '.gitmodules' and fdest != '.gitmodules':
254 copies[fdest] = f
258 copies[fdest] = f
255 entry = None
259 entry = None
256
260
257 if subexists[0]:
261 if subexists[0]:
258 if subdeleted[0]:
262 if subdeleted[0]:
259 changes.append(('.hgsubstate', nodemod.nullhex))
263 changes.append(('.hgsubstate', nodemod.nullhex))
260 else:
264 else:
261 self.retrievegitmodules(version)
265 self.retrievegitmodules(version)
262 changes.append(('.hgsubstate', ''))
266 changes.append(('.hgsubstate', ''))
263 return (changes, copies, set())
267 return (changes, copies, set())
264
268
265 def getcommit(self, version):
269 def getcommit(self, version):
266 c = self.catfile(version, "commit") # read the commit hash
270 c = self.catfile(version, "commit") # read the commit hash
267 end = c.find("\n\n")
271 end = c.find("\n\n")
268 message = c[end + 2:]
272 message = c[end + 2:]
269 message = self.recode(message)
273 message = self.recode(message)
270 l = c[:end].splitlines()
274 l = c[:end].splitlines()
271 parents = []
275 parents = []
272 author = committer = None
276 author = committer = None
273 for e in l[1:]:
277 for e in l[1:]:
274 n, v = e.split(" ", 1)
278 n, v = e.split(" ", 1)
275 if n == "author":
279 if n == "author":
276 p = v.split()
280 p = v.split()
277 tm, tz = p[-2:]
281 tm, tz = p[-2:]
278 author = " ".join(p[:-2])
282 author = " ".join(p[:-2])
279 if author[0] == "<": author = author[1:-1]
283 if author[0] == "<": author = author[1:-1]
280 author = self.recode(author)
284 author = self.recode(author)
281 if n == "committer":
285 if n == "committer":
282 p = v.split()
286 p = v.split()
283 tm, tz = p[-2:]
287 tm, tz = p[-2:]
284 committer = " ".join(p[:-2])
288 committer = " ".join(p[:-2])
285 if committer[0] == "<": committer = committer[1:-1]
289 if committer[0] == "<": committer = committer[1:-1]
286 committer = self.recode(committer)
290 committer = self.recode(committer)
287 if n == "parent":
291 if n == "parent":
288 parents.append(v)
292 parents.append(v)
289
293
290 if committer and committer != author:
294 if committer and committer != author:
291 message += "\ncommitter: %s\n" % committer
295 message += "\ncommitter: %s\n" % committer
292 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
296 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
293 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
297 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
294 date = tm + " " + str(tz)
298 date = tm + " " + str(tz)
295
299
296 c = common.commit(parents=parents, date=date, author=author,
300 c = common.commit(parents=parents, date=date, author=author,
297 desc=message,
301 desc=message,
298 rev=version)
302 rev=version)
299 return c
303 return c
300
304
301 def numcommits(self):
305 def numcommits(self):
302 output, ret = self.gitrunlines('rev-list', '--all')
306 output, ret = self.gitrunlines('rev-list', '--all')
303 if ret:
307 if ret:
304 raise error.Abort(_('cannot retrieve number of commits in %s') \
308 raise error.Abort(_('cannot retrieve number of commits in %s') \
305 % self.path)
309 % self.path)
306 return len(output)
310 return len(output)
307
311
308 def gettags(self):
312 def gettags(self):
309 tags = {}
313 tags = {}
310 alltags = {}
314 alltags = {}
311 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
315 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
312
316
313 if status:
317 if status:
314 raise error.Abort(_('cannot read tags from %s') % self.path)
318 raise error.Abort(_('cannot read tags from %s') % self.path)
315 prefix = 'refs/tags/'
319 prefix = 'refs/tags/'
316
320
317 # Build complete list of tags, both annotated and bare ones
321 # Build complete list of tags, both annotated and bare ones
318 for line in output:
322 for line in output:
319 line = line.strip()
323 line = line.strip()
320 if line.startswith("error:") or line.startswith("fatal:"):
324 if line.startswith("error:") or line.startswith("fatal:"):
321 raise error.Abort(_('cannot read tags from %s') % self.path)
325 raise error.Abort(_('cannot read tags from %s') % self.path)
322 node, tag = line.split(None, 1)
326 node, tag = line.split(None, 1)
323 if not tag.startswith(prefix):
327 if not tag.startswith(prefix):
324 continue
328 continue
325 alltags[tag[len(prefix):]] = node
329 alltags[tag[len(prefix):]] = node
326
330
327 # Filter out tag objects for annotated tag refs
331 # Filter out tag objects for annotated tag refs
328 for tag in alltags:
332 for tag in alltags:
329 if tag.endswith('^{}'):
333 if tag.endswith('^{}'):
330 tags[tag[:-3]] = alltags[tag]
334 tags[tag[:-3]] = alltags[tag]
331 else:
335 else:
332 if tag + '^{}' in alltags:
336 if tag + '^{}' in alltags:
333 continue
337 continue
334 else:
338 else:
335 tags[tag] = alltags[tag]
339 tags[tag] = alltags[tag]
336
340
337 return tags
341 return tags
338
342
339 def getchangedfiles(self, version, i):
343 def getchangedfiles(self, version, i):
340 changes = []
344 changes = []
341 if i is None:
345 if i is None:
342 output, status = self.gitrunlines('diff-tree', '--root', '-m',
346 output, status = self.gitrunlines('diff-tree', '--root', '-m',
343 '-r', version)
347 '-r', version)
344 if status:
348 if status:
345 raise error.Abort(_('cannot read changes in %s') % version)
349 raise error.Abort(_('cannot read changes in %s') % version)
346 for l in output:
350 for l in output:
347 if "\t" not in l:
351 if "\t" not in l:
348 continue
352 continue
349 m, f = l[:-1].split("\t")
353 m, f = l[:-1].split("\t")
350 changes.append(f)
354 changes.append(f)
351 else:
355 else:
352 output, status = self.gitrunlines('diff-tree', '--name-only',
356 output, status = self.gitrunlines('diff-tree', '--name-only',
353 '--root', '-r', version,
357 '--root', '-r', version,
354 '%s^%s' % (version, i + 1), '--')
358 '%s^%s' % (version, i + 1), '--')
355 if status:
359 if status:
356 raise error.Abort(_('cannot read changes in %s') % version)
360 raise error.Abort(_('cannot read changes in %s') % version)
357 changes = [f.rstrip('\n') for f in output]
361 changes = [f.rstrip('\n') for f in output]
358
362
359 return changes
363 return changes
360
364
361 def getbookmarks(self):
365 def getbookmarks(self):
362 bookmarks = {}
366 bookmarks = {}
363
367
364 # Handle local and remote branches
368 # Handle local and remote branches
365 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
369 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
366 reftypes = [
370 reftypes = [
367 # (git prefix, hg prefix)
371 # (git prefix, hg prefix)
368 ('refs/remotes/origin/', remoteprefix + '/'),
372 ('refs/remotes/origin/', remoteprefix + '/'),
369 ('refs/heads/', '')
373 ('refs/heads/', '')
370 ]
374 ]
371
375
372 exclude = set([
376 exclude = set([
373 'refs/remotes/origin/HEAD',
377 'refs/remotes/origin/HEAD',
374 ])
378 ])
375
379
376 try:
380 try:
377 output, status = self.gitrunlines('show-ref')
381 output, status = self.gitrunlines('show-ref')
378 for line in output:
382 for line in output:
379 line = line.strip()
383 line = line.strip()
380 rev, name = line.split(None, 1)
384 rev, name = line.split(None, 1)
381 # Process each type of branch
385 # Process each type of branch
382 for gitprefix, hgprefix in reftypes:
386 for gitprefix, hgprefix in reftypes:
383 if not name.startswith(gitprefix) or name in exclude:
387 if not name.startswith(gitprefix) or name in exclude:
384 continue
388 continue
385 name = '%s%s' % (hgprefix, name[len(gitprefix):])
389 name = '%s%s' % (hgprefix, name[len(gitprefix):])
386 bookmarks[name] = rev
390 bookmarks[name] = rev
387 except Exception:
391 except Exception:
388 pass
392 pass
389
393
390 return bookmarks
394 return bookmarks
391
395
392 def checkrevformat(self, revstr, mapname='splicemap'):
396 def checkrevformat(self, revstr, mapname='splicemap'):
393 """ git revision string is a 40 byte hex """
397 """ git revision string is a 40 byte hex """
394 self.checkhexformat(revstr, mapname)
398 self.checkhexformat(revstr, mapname)
@@ -1,753 +1,770 b''
1 #require git
1 #require git
2
2
3 $ echo "[core]" >> $HOME/.gitconfig
3 $ echo "[core]" >> $HOME/.gitconfig
4 $ echo "autocrlf = false" >> $HOME/.gitconfig
4 $ echo "autocrlf = false" >> $HOME/.gitconfig
5 $ echo "[core]" >> $HOME/.gitconfig
5 $ echo "[core]" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
6 $ echo "autocrlf = false" >> $HOME/.gitconfig
7 $ echo "[extensions]" >> $HGRCPATH
7 $ echo "[extensions]" >> $HGRCPATH
8 $ echo "convert=" >> $HGRCPATH
8 $ echo "convert=" >> $HGRCPATH
9 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
9 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
10 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
10 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
11 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
11 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
12 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
12 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
13 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
13 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
14 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
14 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
15 $ INVALIDID1=afd12345af
15 $ INVALIDID1=afd12345af
16 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
16 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
17 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
17 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
18 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
18 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
19 $ count=10
19 $ count=10
20 $ commit()
20 $ commit()
21 > {
21 > {
22 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
22 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
23 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
23 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
24 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
24 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
25 > count=`expr $count + 1`
25 > count=`expr $count + 1`
26 > }
26 > }
27 $ mkdir git-repo
27 $ mkdir git-repo
28 $ cd git-repo
28 $ cd git-repo
29 $ git init-db >/dev/null 2>/dev/null
29 $ git init-db >/dev/null 2>/dev/null
30 $ echo a > a
30 $ echo a > a
31 $ mkdir d
31 $ mkdir d
32 $ echo b > d/b
32 $ echo b > d/b
33 $ git add a d
33 $ git add a d
34 $ commit -a -m t1
34 $ commit -a -m t1
35
35
36 Remove the directory, then try to replace it with a file (issue754)
36 Remove the directory, then try to replace it with a file (issue754)
37
37
38 $ git rm -f d/b
38 $ git rm -f d/b
39 rm 'd/b'
39 rm 'd/b'
40 $ commit -m t2
40 $ commit -m t2
41 $ echo d > d
41 $ echo d > d
42 $ git add d
42 $ git add d
43 $ commit -m t3
43 $ commit -m t3
44 $ echo b >> a
44 $ echo b >> a
45 $ commit -a -m t4.1
45 $ commit -a -m t4.1
46 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
46 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
47 $ echo c > a
47 $ echo c > a
48 $ echo a >> a
48 $ echo a >> a
49 $ commit -a -m t4.2
49 $ commit -a -m t4.2
50 $ git checkout master >/dev/null 2>/dev/null
50 $ git checkout master >/dev/null 2>/dev/null
51 $ git pull --no-commit . other > /dev/null 2>/dev/null
51 $ git pull --no-commit . other > /dev/null 2>/dev/null
52 $ commit -m 'Merge branch other'
52 $ commit -m 'Merge branch other'
53 $ cd ..
53 $ cd ..
54 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
54 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
55 > --config progress.delay=0 --config progress.changedelay=0 \
55 > --config progress.delay=0 --config progress.changedelay=0 \
56 > --config progress.refresh=0 --config progress.width=60 \
56 > --config progress.refresh=0 --config progress.width=60 \
57 > --config progress.format='topic, bar, number' --datesort git-repo
57 > --config progress.format='topic, bar, number' --datesort git-repo
58 \r (no-eol) (esc)
58 \r (no-eol) (esc)
59 scanning [======> ] 1/6\r (no-eol) (esc)
59 scanning [======> ] 1/6\r (no-eol) (esc)
60 scanning [=============> ] 2/6\r (no-eol) (esc)
60 scanning [=============> ] 2/6\r (no-eol) (esc)
61 scanning [=====================> ] 3/6\r (no-eol) (esc)
61 scanning [=====================> ] 3/6\r (no-eol) (esc)
62 scanning [============================> ] 4/6\r (no-eol) (esc)
62 scanning [============================> ] 4/6\r (no-eol) (esc)
63 scanning [===================================> ] 5/6\r (no-eol) (esc)
63 scanning [===================================> ] 5/6\r (no-eol) (esc)
64 scanning [===========================================>] 6/6\r (no-eol) (esc)
64 scanning [===========================================>] 6/6\r (no-eol) (esc)
65 \r (no-eol) (esc)
65 \r (no-eol) (esc)
66 \r (no-eol) (esc)
66 \r (no-eol) (esc)
67 converting [ ] 0/6\r (no-eol) (esc)
67 converting [ ] 0/6\r (no-eol) (esc)
68 getting files [==================> ] 1/2\r (no-eol) (esc)
68 getting files [==================> ] 1/2\r (no-eol) (esc)
69 getting files [======================================>] 2/2\r (no-eol) (esc)
69 getting files [======================================>] 2/2\r (no-eol) (esc)
70 \r (no-eol) (esc)
70 \r (no-eol) (esc)
71 \r (no-eol) (esc)
71 \r (no-eol) (esc)
72 converting [======> ] 1/6\r (no-eol) (esc)
72 converting [======> ] 1/6\r (no-eol) (esc)
73 getting files [======================================>] 1/1\r (no-eol) (esc)
73 getting files [======================================>] 1/1\r (no-eol) (esc)
74 \r (no-eol) (esc)
74 \r (no-eol) (esc)
75 \r (no-eol) (esc)
75 \r (no-eol) (esc)
76 converting [=============> ] 2/6\r (no-eol) (esc)
76 converting [=============> ] 2/6\r (no-eol) (esc)
77 getting files [======================================>] 1/1\r (no-eol) (esc)
77 getting files [======================================>] 1/1\r (no-eol) (esc)
78 \r (no-eol) (esc)
78 \r (no-eol) (esc)
79 \r (no-eol) (esc)
79 \r (no-eol) (esc)
80 converting [====================> ] 3/6\r (no-eol) (esc)
80 converting [====================> ] 3/6\r (no-eol) (esc)
81 getting files [======================================>] 1/1\r (no-eol) (esc)
81 getting files [======================================>] 1/1\r (no-eol) (esc)
82 \r (no-eol) (esc)
82 \r (no-eol) (esc)
83 \r (no-eol) (esc)
83 \r (no-eol) (esc)
84 converting [===========================> ] 4/6\r (no-eol) (esc)
84 converting [===========================> ] 4/6\r (no-eol) (esc)
85 getting files [======================================>] 1/1\r (no-eol) (esc)
85 getting files [======================================>] 1/1\r (no-eol) (esc)
86 \r (no-eol) (esc)
86 \r (no-eol) (esc)
87 \r (no-eol) (esc)
87 \r (no-eol) (esc)
88 converting [==================================> ] 5/6\r (no-eol) (esc)
88 converting [==================================> ] 5/6\r (no-eol) (esc)
89 getting files [======================================>] 1/1\r (no-eol) (esc)
89 getting files [======================================>] 1/1\r (no-eol) (esc)
90 \r (no-eol) (esc)
90 \r (no-eol) (esc)
91 assuming destination git-repo-hg
91 assuming destination git-repo-hg
92 initializing destination git-repo-hg repository
92 initializing destination git-repo-hg repository
93 scanning source...
93 scanning source...
94 sorting...
94 sorting...
95 converting...
95 converting...
96 5 t1
96 5 t1
97 4 t2
97 4 t2
98 3 t3
98 3 t3
99 2 t4.1
99 2 t4.1
100 1 t4.2
100 1 t4.2
101 0 Merge branch other
101 0 Merge branch other
102 updating bookmarks
102 updating bookmarks
103 $ hg up -q -R git-repo-hg
103 $ hg up -q -R git-repo-hg
104 $ hg -R git-repo-hg tip -v
104 $ hg -R git-repo-hg tip -v
105 changeset: 5:c78094926be2
105 changeset: 5:c78094926be2
106 bookmark: master
106 bookmark: master
107 tag: tip
107 tag: tip
108 parent: 3:f5f5cb45432b
108 parent: 3:f5f5cb45432b
109 parent: 4:4e174f80c67c
109 parent: 4:4e174f80c67c
110 user: test <test@example.org>
110 user: test <test@example.org>
111 date: Mon Jan 01 00:00:15 2007 +0000
111 date: Mon Jan 01 00:00:15 2007 +0000
112 files: a
112 files: a
113 description:
113 description:
114 Merge branch other
114 Merge branch other
115
115
116
116
117 $ count=10
117 $ count=10
118 $ mkdir git-repo2
118 $ mkdir git-repo2
119 $ cd git-repo2
119 $ cd git-repo2
120 $ git init-db >/dev/null 2>/dev/null
120 $ git init-db >/dev/null 2>/dev/null
121 $ echo foo > foo
121 $ echo foo > foo
122 $ git add foo
122 $ git add foo
123 $ commit -a -m 'add foo'
123 $ commit -a -m 'add foo'
124 $ echo >> foo
124 $ echo >> foo
125 $ commit -a -m 'change foo'
125 $ commit -a -m 'change foo'
126 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
126 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
127 $ echo quux >> quux
127 $ echo quux >> quux
128 $ git add quux
128 $ git add quux
129 $ commit -a -m 'add quux'
129 $ commit -a -m 'add quux'
130 $ echo bar > bar
130 $ echo bar > bar
131 $ git add bar
131 $ git add bar
132 $ commit -a -m 'add bar'
132 $ commit -a -m 'add bar'
133 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
133 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
134 $ echo baz > baz
134 $ echo baz > baz
135 $ git add baz
135 $ git add baz
136 $ commit -a -m 'add baz'
136 $ commit -a -m 'add baz'
137 $ git checkout master >/dev/null 2>/dev/null
137 $ git checkout master >/dev/null 2>/dev/null
138 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
138 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
139 $ commit -m 'Octopus merge'
139 $ commit -m 'Octopus merge'
140 $ echo bar >> bar
140 $ echo bar >> bar
141 $ commit -a -m 'change bar'
141 $ commit -a -m 'change bar'
142 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
142 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
143 $ echo >> foo
143 $ echo >> foo
144 $ commit -a -m 'change foo'
144 $ commit -a -m 'change foo'
145 $ git checkout master >/dev/null 2>/dev/null
145 $ git checkout master >/dev/null 2>/dev/null
146 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
146 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
147 $ commit -m 'Discard change to foo'
147 $ commit -m 'Discard change to foo'
148 $ cd ..
148 $ cd ..
149 $ glog()
149 $ glog()
150 > {
150 > {
151 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
151 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
152 > }
152 > }
153 $ splitrepo()
153 $ splitrepo()
154 > {
154 > {
155 > msg="$1"
155 > msg="$1"
156 > files="$2"
156 > files="$2"
157 > opts=$3
157 > opts=$3
158 > echo "% $files: $msg"
158 > echo "% $files: $msg"
159 > prefix=`echo "$files" | sed -e 's/ /-/g'`
159 > prefix=`echo "$files" | sed -e 's/ /-/g'`
160 > fmap="$prefix.fmap"
160 > fmap="$prefix.fmap"
161 > repo="$prefix.repo"
161 > repo="$prefix.repo"
162 > for i in $files; do
162 > for i in $files; do
163 > echo "include $i" >> "$fmap"
163 > echo "include $i" >> "$fmap"
164 > done
164 > done
165 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
165 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
166 > hg up -q -R "$repo"
166 > hg up -q -R "$repo"
167 > glog -R "$repo"
167 > glog -R "$repo"
168 > hg -R "$repo" manifest --debug
168 > hg -R "$repo" manifest --debug
169 > }
169 > }
170
170
171 full conversion
171 full conversion
172
172
173 $ hg convert --datesort git-repo2 fullrepo \
173 $ hg convert --datesort git-repo2 fullrepo \
174 > --config extensions.progress= --config progress.assume-tty=1 \
174 > --config extensions.progress= --config progress.assume-tty=1 \
175 > --config progress.delay=0 --config progress.changedelay=0 \
175 > --config progress.delay=0 --config progress.changedelay=0 \
176 > --config progress.refresh=0 --config progress.width=60 \
176 > --config progress.refresh=0 --config progress.width=60 \
177 > --config progress.format='topic, bar, number'
177 > --config progress.format='topic, bar, number'
178 \r (no-eol) (esc)
178 \r (no-eol) (esc)
179 scanning [===> ] 1/9\r (no-eol) (esc)
179 scanning [===> ] 1/9\r (no-eol) (esc)
180 scanning [========> ] 2/9\r (no-eol) (esc)
180 scanning [========> ] 2/9\r (no-eol) (esc)
181 scanning [=============> ] 3/9\r (no-eol) (esc)
181 scanning [=============> ] 3/9\r (no-eol) (esc)
182 scanning [==================> ] 4/9\r (no-eol) (esc)
182 scanning [==================> ] 4/9\r (no-eol) (esc)
183 scanning [=======================> ] 5/9\r (no-eol) (esc)
183 scanning [=======================> ] 5/9\r (no-eol) (esc)
184 scanning [============================> ] 6/9\r (no-eol) (esc)
184 scanning [============================> ] 6/9\r (no-eol) (esc)
185 scanning [=================================> ] 7/9\r (no-eol) (esc)
185 scanning [=================================> ] 7/9\r (no-eol) (esc)
186 scanning [======================================> ] 8/9\r (no-eol) (esc)
186 scanning [======================================> ] 8/9\r (no-eol) (esc)
187 scanning [===========================================>] 9/9\r (no-eol) (esc)
187 scanning [===========================================>] 9/9\r (no-eol) (esc)
188 \r (no-eol) (esc)
188 \r (no-eol) (esc)
189 \r (no-eol) (esc)
189 \r (no-eol) (esc)
190 converting [ ] 0/9\r (no-eol) (esc)
190 converting [ ] 0/9\r (no-eol) (esc)
191 getting files [======================================>] 1/1\r (no-eol) (esc)
191 getting files [======================================>] 1/1\r (no-eol) (esc)
192 \r (no-eol) (esc)
192 \r (no-eol) (esc)
193 \r (no-eol) (esc)
193 \r (no-eol) (esc)
194 converting [===> ] 1/9\r (no-eol) (esc)
194 converting [===> ] 1/9\r (no-eol) (esc)
195 getting files [======================================>] 1/1\r (no-eol) (esc)
195 getting files [======================================>] 1/1\r (no-eol) (esc)
196 \r (no-eol) (esc)
196 \r (no-eol) (esc)
197 \r (no-eol) (esc)
197 \r (no-eol) (esc)
198 converting [========> ] 2/9\r (no-eol) (esc)
198 converting [========> ] 2/9\r (no-eol) (esc)
199 getting files [======================================>] 1/1\r (no-eol) (esc)
199 getting files [======================================>] 1/1\r (no-eol) (esc)
200 \r (no-eol) (esc)
200 \r (no-eol) (esc)
201 \r (no-eol) (esc)
201 \r (no-eol) (esc)
202 converting [=============> ] 3/9\r (no-eol) (esc)
202 converting [=============> ] 3/9\r (no-eol) (esc)
203 getting files [======================================>] 1/1\r (no-eol) (esc)
203 getting files [======================================>] 1/1\r (no-eol) (esc)
204 \r (no-eol) (esc)
204 \r (no-eol) (esc)
205 \r (no-eol) (esc)
205 \r (no-eol) (esc)
206 converting [=================> ] 4/9\r (no-eol) (esc)
206 converting [=================> ] 4/9\r (no-eol) (esc)
207 getting files [======================================>] 1/1\r (no-eol) (esc)
207 getting files [======================================>] 1/1\r (no-eol) (esc)
208 \r (no-eol) (esc)
208 \r (no-eol) (esc)
209 \r (no-eol) (esc)
209 \r (no-eol) (esc)
210 converting [======================> ] 5/9\r (no-eol) (esc)
210 converting [======================> ] 5/9\r (no-eol) (esc)
211 getting files [===> ] 1/8\r (no-eol) (esc)
211 getting files [===> ] 1/8\r (no-eol) (esc)
212 getting files [========> ] 2/8\r (no-eol) (esc)
212 getting files [========> ] 2/8\r (no-eol) (esc)
213 getting files [=============> ] 3/8\r (no-eol) (esc)
213 getting files [=============> ] 3/8\r (no-eol) (esc)
214 getting files [==================> ] 4/8\r (no-eol) (esc)
214 getting files [==================> ] 4/8\r (no-eol) (esc)
215 getting files [=======================> ] 5/8\r (no-eol) (esc)
215 getting files [=======================> ] 5/8\r (no-eol) (esc)
216 getting files [============================> ] 6/8\r (no-eol) (esc)
216 getting files [============================> ] 6/8\r (no-eol) (esc)
217 getting files [=================================> ] 7/8\r (no-eol) (esc)
217 getting files [=================================> ] 7/8\r (no-eol) (esc)
218 getting files [======================================>] 8/8\r (no-eol) (esc)
218 getting files [======================================>] 8/8\r (no-eol) (esc)
219 \r (no-eol) (esc)
219 \r (no-eol) (esc)
220 \r (no-eol) (esc)
220 \r (no-eol) (esc)
221 converting [===========================> ] 6/9\r (no-eol) (esc)
221 converting [===========================> ] 6/9\r (no-eol) (esc)
222 getting files [======================================>] 1/1\r (no-eol) (esc)
222 getting files [======================================>] 1/1\r (no-eol) (esc)
223 \r (no-eol) (esc)
223 \r (no-eol) (esc)
224 \r (no-eol) (esc)
224 \r (no-eol) (esc)
225 converting [===============================> ] 7/9\r (no-eol) (esc)
225 converting [===============================> ] 7/9\r (no-eol) (esc)
226 getting files [======================================>] 1/1\r (no-eol) (esc)
226 getting files [======================================>] 1/1\r (no-eol) (esc)
227 \r (no-eol) (esc)
227 \r (no-eol) (esc)
228 \r (no-eol) (esc)
228 \r (no-eol) (esc)
229 converting [====================================> ] 8/9\r (no-eol) (esc)
229 converting [====================================> ] 8/9\r (no-eol) (esc)
230 getting files [==================> ] 1/2\r (no-eol) (esc)
230 getting files [==================> ] 1/2\r (no-eol) (esc)
231 getting files [======================================>] 2/2\r (no-eol) (esc)
231 getting files [======================================>] 2/2\r (no-eol) (esc)
232 \r (no-eol) (esc)
232 \r (no-eol) (esc)
233 initializing destination fullrepo repository
233 initializing destination fullrepo repository
234 scanning source...
234 scanning source...
235 sorting...
235 sorting...
236 converting...
236 converting...
237 8 add foo
237 8 add foo
238 7 change foo
238 7 change foo
239 6 add quux
239 6 add quux
240 5 add bar
240 5 add bar
241 4 add baz
241 4 add baz
242 3 Octopus merge
242 3 Octopus merge
243 2 change bar
243 2 change bar
244 1 change foo
244 1 change foo
245 0 Discard change to foo
245 0 Discard change to foo
246 updating bookmarks
246 updating bookmarks
247 $ hg up -q -R fullrepo
247 $ hg up -q -R fullrepo
248 $ glog -R fullrepo
248 $ glog -R fullrepo
249 @ 9 "Discard change to foo" files: foo
249 @ 9 "Discard change to foo" files: foo
250 |\
250 |\
251 | o 8 "change foo" files: foo
251 | o 8 "change foo" files: foo
252 | |
252 | |
253 o | 7 "change bar" files: bar
253 o | 7 "change bar" files: bar
254 |/
254 |/
255 o 6 "(octopus merge fixup)" files:
255 o 6 "(octopus merge fixup)" files:
256 |\
256 |\
257 | o 5 "Octopus merge" files: baz
257 | o 5 "Octopus merge" files: baz
258 | |\
258 | |\
259 o | | 4 "add baz" files: baz
259 o | | 4 "add baz" files: baz
260 | | |
260 | | |
261 +---o 3 "add bar" files: bar
261 +---o 3 "add bar" files: bar
262 | |
262 | |
263 o | 2 "add quux" files: quux
263 o | 2 "add quux" files: quux
264 | |
264 | |
265 | o 1 "change foo" files: foo
265 | o 1 "change foo" files: foo
266 |/
266 |/
267 o 0 "add foo" files: foo
267 o 0 "add foo" files: foo
268
268
269 $ hg -R fullrepo manifest --debug
269 $ hg -R fullrepo manifest --debug
270 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
270 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
271 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
271 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
272 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
272 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
273 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
273 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
274 $ splitrepo 'octopus merge' 'foo bar baz'
274 $ splitrepo 'octopus merge' 'foo bar baz'
275 % foo bar baz: octopus merge
275 % foo bar baz: octopus merge
276 @ 8 "Discard change to foo" files: foo
276 @ 8 "Discard change to foo" files: foo
277 |\
277 |\
278 | o 7 "change foo" files: foo
278 | o 7 "change foo" files: foo
279 | |
279 | |
280 o | 6 "change bar" files: bar
280 o | 6 "change bar" files: bar
281 |/
281 |/
282 o 5 "(octopus merge fixup)" files:
282 o 5 "(octopus merge fixup)" files:
283 |\
283 |\
284 | o 4 "Octopus merge" files: baz
284 | o 4 "Octopus merge" files: baz
285 | |\
285 | |\
286 o | | 3 "add baz" files: baz
286 o | | 3 "add baz" files: baz
287 | | |
287 | | |
288 +---o 2 "add bar" files: bar
288 +---o 2 "add bar" files: bar
289 | |
289 | |
290 | o 1 "change foo" files: foo
290 | o 1 "change foo" files: foo
291 |/
291 |/
292 o 0 "add foo" files: foo
292 o 0 "add foo" files: foo
293
293
294 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
294 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
295 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
295 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
296 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
296 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
297 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
297 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
298 % foo baz quux: only some parents of an octopus merge; "discard" a head
298 % foo baz quux: only some parents of an octopus merge; "discard" a head
299 @ 6 "Discard change to foo" files: foo
299 @ 6 "Discard change to foo" files: foo
300 |
300 |
301 o 5 "change foo" files: foo
301 o 5 "change foo" files: foo
302 |
302 |
303 o 4 "Octopus merge" files:
303 o 4 "Octopus merge" files:
304 |\
304 |\
305 | o 3 "add baz" files: baz
305 | o 3 "add baz" files: baz
306 | |
306 | |
307 | o 2 "add quux" files: quux
307 | o 2 "add quux" files: quux
308 | |
308 | |
309 o | 1 "change foo" files: foo
309 o | 1 "change foo" files: foo
310 |/
310 |/
311 o 0 "add foo" files: foo
311 o 0 "add foo" files: foo
312
312
313 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
313 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
314 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
314 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
315 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
315 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
316
316
317 test importing git renames and copies
317 test importing git renames and copies
318
318
319 $ cd git-repo2
319 $ cd git-repo2
320 $ git mv foo foo-renamed
320 $ git mv foo foo-renamed
321 since bar is not touched in this commit, this copy will not be detected
321 since bar is not touched in this commit, this copy will not be detected
322 $ cp bar bar-copied
322 $ cp bar bar-copied
323 $ cp baz baz-copied
323 $ cp baz baz-copied
324 $ cp baz baz-copied2
324 $ cp baz baz-copied2
325 $ cp baz ba-copy
325 $ cp baz ba-copy
326 $ echo baz2 >> baz
326 $ echo baz2 >> baz
327 $ git add bar-copied baz-copied baz-copied2 ba-copy
327 $ git add bar-copied baz-copied baz-copied2 ba-copy
328 $ commit -a -m 'rename and copy'
328 $ commit -a -m 'rename and copy'
329 $ cd ..
329 $ cd ..
330
330
331 input validation
331 input validation
332 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
332 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
333 abort: convert.git.similarity is not an integer ('foo')
333 abort: convert.git.similarity is not an integer ('foo')
334 [255]
334 [255]
335 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
335 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
336 abort: similarity must be between 0 and 100
336 abort: similarity must be between 0 and 100
337 [255]
337 [255]
338 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
338 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
339 abort: similarity must be between 0 and 100
339 abort: similarity must be between 0 and 100
340 [255]
340 [255]
341
341
342 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
342 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
343 $ hg -R fullrepo status -C --change master
343 $ hg -R fullrepo status -C --change master
344 M baz
344 M baz
345 A ba-copy
345 A ba-copy
346 baz
346 baz
347 A bar-copied
347 A bar-copied
348 A baz-copied
348 A baz-copied
349 baz
349 baz
350 A baz-copied2
350 A baz-copied2
351 baz
351 baz
352 A foo-renamed
352 A foo-renamed
353 foo
353 foo
354 R foo
354 R foo
355
355
356 Ensure that the modification to the copy source was preserved
356 Ensure that the modification to the copy source was preserved
357 (there was a bug where if the copy dest was alphabetically prior to the copy
357 (there was a bug where if the copy dest was alphabetically prior to the copy
358 source, the copy source took the contents of the copy dest)
358 source, the copy source took the contents of the copy dest)
359 $ hg cat -r tip fullrepo/baz
359 $ hg cat -r tip fullrepo/baz
360 baz
360 baz
361 baz2
361 baz2
362
362
363 $ cd git-repo2
363 $ cd git-repo2
364 $ echo bar2 >> bar
364 $ echo bar2 >> bar
365 $ commit -a -m 'change bar'
365 $ commit -a -m 'change bar'
366 $ cp bar bar-copied2
366 $ cp bar bar-copied2
367 $ git add bar-copied2
367 $ git add bar-copied2
368 $ commit -a -m 'copy with no changes'
368 $ commit -a -m 'copy with no changes'
369 $ cd ..
369 $ cd ..
370
370
371 $ hg -q convert --config convert.git.similarity=100 \
371 $ hg -q convert --config convert.git.similarity=100 \
372 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
372 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
373 $ hg -R fullrepo status -C --change master
373 $ hg -R fullrepo status -C --change master
374 A bar-copied2
374 A bar-copied2
375 bar
375 bar
376
376
377 test binary conversion (issue1359)
377 test binary conversion (issue1359)
378
378
379 $ count=19
379 $ count=19
380 $ mkdir git-repo3
380 $ mkdir git-repo3
381 $ cd git-repo3
381 $ cd git-repo3
382 $ git init-db >/dev/null 2>/dev/null
382 $ git init-db >/dev/null 2>/dev/null
383 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
383 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
384 $ git add b
384 $ git add b
385 $ commit -a -m addbinary
385 $ commit -a -m addbinary
386 $ cd ..
386 $ cd ..
387
387
388 convert binary file
388 convert binary file
389
389
390 $ hg convert git-repo3 git-repo3-hg
390 $ hg convert git-repo3 git-repo3-hg
391 initializing destination git-repo3-hg repository
391 initializing destination git-repo3-hg repository
392 scanning source...
392 scanning source...
393 sorting...
393 sorting...
394 converting...
394 converting...
395 0 addbinary
395 0 addbinary
396 updating bookmarks
396 updating bookmarks
397 $ cd git-repo3-hg
397 $ cd git-repo3-hg
398 $ hg up -C
398 $ hg up -C
399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 $ $PYTHON -c 'print len(file("b", "rb").read())'
400 $ $PYTHON -c 'print len(file("b", "rb").read())'
401 4096
401 4096
402 $ cd ..
402 $ cd ..
403
403
404 test author vs committer
404 test author vs committer
405
405
406 $ mkdir git-repo4
406 $ mkdir git-repo4
407 $ cd git-repo4
407 $ cd git-repo4
408 $ git init-db >/dev/null 2>/dev/null
408 $ git init-db >/dev/null 2>/dev/null
409 $ echo >> foo
409 $ echo >> foo
410 $ git add foo
410 $ git add foo
411 $ commit -a -m addfoo
411 $ commit -a -m addfoo
412 $ echo >> foo
412 $ echo >> foo
413 $ GIT_AUTHOR_NAME="nottest"
413 $ GIT_AUTHOR_NAME="nottest"
414 $ commit -a -m addfoo2
414 $ commit -a -m addfoo2
415 $ cd ..
415 $ cd ..
416
416
417 convert author committer
417 convert author committer
418
418
419 $ hg convert git-repo4 git-repo4-hg
419 $ hg convert git-repo4 git-repo4-hg
420 initializing destination git-repo4-hg repository
420 initializing destination git-repo4-hg repository
421 scanning source...
421 scanning source...
422 sorting...
422 sorting...
423 converting...
423 converting...
424 1 addfoo
424 1 addfoo
425 0 addfoo2
425 0 addfoo2
426 updating bookmarks
426 updating bookmarks
427 $ hg -R git-repo4-hg log -v
427 $ hg -R git-repo4-hg log -v
428 changeset: 1:d63e967f93da
428 changeset: 1:d63e967f93da
429 bookmark: master
429 bookmark: master
430 tag: tip
430 tag: tip
431 user: nottest <test@example.org>
431 user: nottest <test@example.org>
432 date: Mon Jan 01 00:00:21 2007 +0000
432 date: Mon Jan 01 00:00:21 2007 +0000
433 files: foo
433 files: foo
434 description:
434 description:
435 addfoo2
435 addfoo2
436
436
437 committer: test <test@example.org>
437 committer: test <test@example.org>
438
438
439
439
440 changeset: 0:0735477b0224
440 changeset: 0:0735477b0224
441 user: test <test@example.org>
441 user: test <test@example.org>
442 date: Mon Jan 01 00:00:20 2007 +0000
442 date: Mon Jan 01 00:00:20 2007 +0000
443 files: foo
443 files: foo
444 description:
444 description:
445 addfoo
445 addfoo
446
446
447
447
448
448
449 --sourceorder should fail
449 --sourceorder should fail
450
450
451 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
451 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
452 initializing destination git-repo4-sourcesort-hg repository
452 initializing destination git-repo4-sourcesort-hg repository
453 abort: --sourcesort is not supported by this data source
453 abort: --sourcesort is not supported by this data source
454 [255]
454 [255]
455
455
456 test converting certain branches
456 test converting certain branches
457
457
458 $ mkdir git-testrevs
458 $ mkdir git-testrevs
459 $ cd git-testrevs
459 $ cd git-testrevs
460 $ git init
460 $ git init
461 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
461 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
462 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
462 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
463 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
463 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
464 $ git checkout -b goodbranch 'HEAD^'
464 $ git checkout -b goodbranch 'HEAD^'
465 Switched to a new branch 'goodbranch'
465 Switched to a new branch 'goodbranch'
466 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
466 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
467 $ git checkout -b badbranch 'HEAD^'
467 $ git checkout -b badbranch 'HEAD^'
468 Switched to a new branch 'badbranch'
468 Switched to a new branch 'badbranch'
469 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
469 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
470 $ cd ..
470 $ cd ..
471 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
471 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
472 initializing destination hg-testrevs repository
472 initializing destination hg-testrevs repository
473 scanning source...
473 scanning source...
474 sorting...
474 sorting...
475 converting...
475 converting...
476 2 first
476 2 first
477 1 good branch commit
477 1 good branch commit
478 0 master commit
478 0 master commit
479 updating bookmarks
479 updating bookmarks
480 $ cd hg-testrevs
480 $ cd hg-testrevs
481 $ hg log -G -T '{rev} {bookmarks}'
481 $ hg log -G -T '{rev} {bookmarks}'
482 o 2 master
482 o 2 master
483 |
483 |
484 | o 1 goodbranch
484 | o 1 goodbranch
485 |/
485 |/
486 o 0
486 o 0
487
487
488 $ cd ..
488 $ cd ..
489
489
490 test sub modules
490 test sub modules
491
491
492 $ mkdir git-repo5
492 $ mkdir git-repo5
493 $ cd git-repo5
493 $ cd git-repo5
494 $ git init-db >/dev/null 2>/dev/null
494 $ git init-db >/dev/null 2>/dev/null
495 $ echo 'sub' >> foo
495 $ echo 'sub' >> foo
496 $ git add foo
496 $ git add foo
497 $ commit -a -m 'addfoo'
497 $ commit -a -m 'addfoo'
498 $ BASE=`pwd`
498 $ BASE=`pwd`
499 $ cd ..
499 $ cd ..
500 $ mkdir git-repo6
500 $ mkdir git-repo6
501 $ cd git-repo6
501 $ cd git-repo6
502 $ git init-db >/dev/null 2>/dev/null
502 $ git init-db >/dev/null 2>/dev/null
503 $ git submodule add ${BASE} >/dev/null 2>/dev/null
503 $ git submodule add ${BASE} >/dev/null 2>/dev/null
504 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
504 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
505
505
506 test non-tab whitespace .gitmodules
506 test non-tab whitespace .gitmodules
507
507
508 $ cat >> .gitmodules <<EOF
508 $ cat >> .gitmodules <<EOF
509 > [submodule "git-repo5"]
509 > [submodule "git-repo5"]
510 > path = git-repo5
510 > path = git-repo5
511 > url = git-repo5
511 > url = git-repo5
512 > EOF
512 > EOF
513 $ git commit -q -a -m "weird white space submodule"
513 $ git commit -q -a -m "weird white space submodule"
514 $ cd ..
514 $ cd ..
515 $ hg convert git-repo6 hg-repo6
515 $ hg convert git-repo6 hg-repo6
516 initializing destination hg-repo6 repository
516 initializing destination hg-repo6 repository
517 scanning source...
517 scanning source...
518 sorting...
518 sorting...
519 converting...
519 converting...
520 1 addsubmodule
520 1 addsubmodule
521 0 weird white space submodule
521 0 weird white space submodule
522 updating bookmarks
522 updating bookmarks
523
523
524 $ rm -rf hg-repo6
524 $ rm -rf hg-repo6
525 $ cd git-repo6
525 $ cd git-repo6
526 $ git reset --hard 'HEAD^' > /dev/null
526 $ git reset --hard 'HEAD^' > /dev/null
527
527
528 test missing .gitmodules
528 test missing .gitmodules
529
529
530 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
530 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
531 $ git checkout HEAD .gitmodules
531 $ git checkout HEAD .gitmodules
532 $ git rm .gitmodules
532 $ git rm .gitmodules
533 rm '.gitmodules'
533 rm '.gitmodules'
534 $ git commit -q -m "remove .gitmodules" .gitmodules
534 $ git commit -q -m "remove .gitmodules" .gitmodules
535 $ git commit -q -m "missing .gitmodules"
535 $ git commit -q -m "missing .gitmodules"
536 $ cd ..
536 $ cd ..
537 $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
537 $ hg convert git-repo6 hg-repo6 --traceback 2>&1 | grep -v "fatal: Path '.gitmodules' does not exist"
538 initializing destination hg-repo6 repository
538 initializing destination hg-repo6 repository
539 scanning source...
539 scanning source...
540 sorting...
540 sorting...
541 converting...
541 converting...
542 2 addsubmodule
542 2 addsubmodule
543 1 remove .gitmodules
543 1 remove .gitmodules
544 0 missing .gitmodules
544 0 missing .gitmodules
545 warning: cannot read submodules config file in * (glob)
545 warning: cannot read submodules config file in * (glob)
546 updating bookmarks
546 updating bookmarks
547 $ rm -rf hg-repo6
547 $ rm -rf hg-repo6
548 $ cd git-repo6
548 $ cd git-repo6
549 $ rm -rf git-repo4
549 $ rm -rf git-repo4
550 $ git reset --hard 'HEAD^^' > /dev/null
550 $ git reset --hard 'HEAD^^' > /dev/null
551 $ cd ..
551 $ cd ..
552
552
553 test invalid splicemap1
553 test invalid splicemap1
554
554
555 $ cat > splicemap <<EOF
555 $ cat > splicemap <<EOF
556 > $VALIDID1
556 > $VALIDID1
557 > EOF
557 > EOF
558 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
558 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
559 initializing destination git-repo2-splicemap1-hg repository
559 initializing destination git-repo2-splicemap1-hg repository
560 abort: syntax error in splicemap(1): child parent1[,parent2] expected
560 abort: syntax error in splicemap(1): child parent1[,parent2] expected
561 [255]
561 [255]
562
562
563 test invalid splicemap2
563 test invalid splicemap2
564
564
565 $ cat > splicemap <<EOF
565 $ cat > splicemap <<EOF
566 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
566 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
567 > EOF
567 > EOF
568 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
568 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
569 initializing destination git-repo2-splicemap2-hg repository
569 initializing destination git-repo2-splicemap2-hg repository
570 abort: syntax error in splicemap(1): child parent1[,parent2] expected
570 abort: syntax error in splicemap(1): child parent1[,parent2] expected
571 [255]
571 [255]
572
572
573 test invalid splicemap3
573 test invalid splicemap3
574
574
575 $ cat > splicemap <<EOF
575 $ cat > splicemap <<EOF
576 > $INVALIDID1 $INVALIDID2
576 > $INVALIDID1 $INVALIDID2
577 > EOF
577 > EOF
578 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
578 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
579 initializing destination git-repo2-splicemap3-hg repository
579 initializing destination git-repo2-splicemap3-hg repository
580 abort: splicemap entry afd12345af is not a valid revision identifier
580 abort: splicemap entry afd12345af is not a valid revision identifier
581 [255]
581 [255]
582
582
583 convert sub modules
583 convert sub modules
584 $ hg convert git-repo6 git-repo6-hg
584 $ hg convert git-repo6 git-repo6-hg
585 initializing destination git-repo6-hg repository
585 initializing destination git-repo6-hg repository
586 scanning source...
586 scanning source...
587 sorting...
587 sorting...
588 converting...
588 converting...
589 0 addsubmodule
589 0 addsubmodule
590 updating bookmarks
590 updating bookmarks
591 $ hg -R git-repo6-hg log -v
591 $ hg -R git-repo6-hg log -v
592 changeset: 0:* (glob)
592 changeset: 0:* (glob)
593 bookmark: master
593 bookmark: master
594 tag: tip
594 tag: tip
595 user: nottest <test@example.org>
595 user: nottest <test@example.org>
596 date: Mon Jan 01 00:00:23 2007 +0000
596 date: Mon Jan 01 00:00:23 2007 +0000
597 files: .hgsub .hgsubstate
597 files: .hgsub .hgsubstate
598 description:
598 description:
599 addsubmodule
599 addsubmodule
600
600
601 committer: test <test@example.org>
601 committer: test <test@example.org>
602
602
603
603
604
604
605 $ cd git-repo6-hg
605 $ cd git-repo6-hg
606 $ hg up >/dev/null 2>/dev/null
606 $ hg up >/dev/null 2>/dev/null
607 $ cat .hgsubstate
607 $ cat .hgsubstate
608 * git-repo5 (glob)
608 * git-repo5 (glob)
609 $ cd git-repo5
609 $ cd git-repo5
610 $ cat foo
610 $ cat foo
611 sub
611 sub
612
612
613 $ cd ../..
613 $ cd ../..
614
614
615 make sure rename detection doesn't break removing and adding gitmodules
615 make sure rename detection doesn't break removing and adding gitmodules
616
616
617 $ cd git-repo6
617 $ cd git-repo6
618 $ git mv .gitmodules .gitmodules-renamed
618 $ git mv .gitmodules .gitmodules-renamed
619 $ commit -a -m 'rename .gitmodules'
619 $ commit -a -m 'rename .gitmodules'
620 $ git mv .gitmodules-renamed .gitmodules
620 $ git mv .gitmodules-renamed .gitmodules
621 $ commit -a -m 'rename .gitmodules back'
621 $ commit -a -m 'rename .gitmodules back'
622 $ cd ..
622 $ cd ..
623
623
624 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
624 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
625 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
625 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
626 rename .gitmodules
626 rename .gitmodules
627 $ hg -R git-repo6-hg status -C --change 'tip^'
627 $ hg -R git-repo6-hg status -C --change 'tip^'
628 A .gitmodules-renamed
628 A .gitmodules-renamed
629 R .hgsub
629 R .hgsub
630 R .hgsubstate
630 R .hgsubstate
631 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
631 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
632 rename .gitmodules back
632 rename .gitmodules back
633 $ hg -R git-repo6-hg status -C --change tip
633 $ hg -R git-repo6-hg status -C --change tip
634 A .hgsub
634 A .hgsub
635 A .hgsubstate
635 A .hgsubstate
636 R .gitmodules-renamed
636 R .gitmodules-renamed
637
637
638 convert the revision removing '.gitmodules' itself (and related
638 convert the revision removing '.gitmodules' itself (and related
639 submodules)
639 submodules)
640
640
641 $ cd git-repo6
641 $ cd git-repo6
642 $ git rm .gitmodules
642 $ git rm .gitmodules
643 rm '.gitmodules'
643 rm '.gitmodules'
644 $ git rm --cached git-repo5
644 $ git rm --cached git-repo5
645 rm 'git-repo5'
645 rm 'git-repo5'
646 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
646 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
647 $ cd ..
647 $ cd ..
648
648
649 $ hg convert -q git-repo6 git-repo6-hg
649 $ hg convert -q git-repo6 git-repo6-hg
650 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
650 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
651 remove .gitmodules and submodule git-repo5
651 remove .gitmodules and submodule git-repo5
652 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
652 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
653 .hgsub .hgsubstate
653 .hgsub .hgsubstate
654
654
655 skip submodules in the conversion
655 skip submodules in the conversion
656
656
657 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
657 $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True
658 $ hg -R no-submodules manifest --all
658 $ hg -R no-submodules manifest --all
659 .gitmodules-renamed
659 .gitmodules-renamed
660
660
661 convert using a different remote prefix
661 convert using a different remote prefix
662 $ git init git-repo7
662 $ git init git-repo7
663 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
663 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
664 $ cd git-repo7
664 $ cd git-repo7
665 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
665 TODO: it'd be nice to use (?) lines instead of grep -v to handle the
666 git output variance, but that doesn't currently work in the middle of
666 git output variance, but that doesn't currently work in the middle of
667 a block, so do this for now.
667 a block, so do this for now.
668 $ touch a && git add a && git commit -am "commit a" | grep -v changed
668 $ touch a && git add a && git commit -am "commit a" | grep -v changed
669 [master (root-commit) 8ae5f69] commit a
669 [master (root-commit) 8ae5f69] commit a
670 Author: nottest <test@example.org>
670 Author: nottest <test@example.org>
671 create mode 100644 a
671 create mode 100644 a
672 $ cd ..
672 $ cd ..
673 $ git clone git-repo7 git-repo7-client
673 $ git clone git-repo7 git-repo7-client
674 Cloning into 'git-repo7-client'...
674 Cloning into 'git-repo7-client'...
675 done.
675 done.
676 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
676 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
677 initializing destination hg-repo7 repository
677 initializing destination hg-repo7 repository
678 scanning source...
678 scanning source...
679 sorting...
679 sorting...
680 converting...
680 converting...
681 0 commit a
681 0 commit a
682 updating bookmarks
682 updating bookmarks
683 $ hg -R hg-repo7 bookmarks
683 $ hg -R hg-repo7 bookmarks
684 master 0:03bf38caa4c6
684 master 0:03bf38caa4c6
685 origin/master 0:03bf38caa4c6
685 origin/master 0:03bf38caa4c6
686
686
687 Run convert when the remote branches have changed
687 Run convert when the remote branches have changed
688 (there was an old bug where the local convert read branches from the server)
688 (there was an old bug where the local convert read branches from the server)
689
689
690 $ cd git-repo7
690 $ cd git-repo7
691 $ echo a >> a
691 $ echo a >> a
692 $ git commit -q -am "move master forward"
692 $ git commit -q -am "move master forward"
693 $ cd ..
693 $ cd ..
694 $ rm -rf hg-repo7
694 $ rm -rf hg-repo7
695 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
695 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
696 initializing destination hg-repo7 repository
696 initializing destination hg-repo7 repository
697 scanning source...
697 scanning source...
698 sorting...
698 sorting...
699 converting...
699 converting...
700 0 commit a
700 0 commit a
701 updating bookmarks
701 updating bookmarks
702 $ hg -R hg-repo7 bookmarks
702 $ hg -R hg-repo7 bookmarks
703 master 0:03bf38caa4c6
703 master 0:03bf38caa4c6
704 origin/master 0:03bf38caa4c6
704 origin/master 0:03bf38caa4c6
705
705
706 damaged git repository tests:
706 damaged git repository tests:
707 In case the hard-coded hashes change, the following commands can be used to
707 In case the hard-coded hashes change, the following commands can be used to
708 list the hashes and their corresponding types in the repository:
708 list the hashes and their corresponding types in the repository:
709 cd git-repo4/.git/objects
709 cd git-repo4/.git/objects
710 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
710 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
711 cd ../../..
711 cd ../../..
712
712
713 damage git repository by renaming a commit object
713 damage git repository by renaming a commit object
714 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
714 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
715 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
715 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
716 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
716 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
717 abort: cannot retrieve number of commits in git-repo4/.git
717 abort: cannot retrieve number of commits in $TESTTMP/git-repo4/.git
718 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
718 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
719 damage git repository by renaming a blob object
719 damage git repository by renaming a blob object
720
720
721 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
721 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
722 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
722 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
723 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
723 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
724 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
724 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
725 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
725 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
726 damage git repository by renaming a tree object
726 damage git repository by renaming a tree object
727
727
728 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
728 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
729 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
729 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
730 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
730 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
731 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
731 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
732
732
733 #if no-windows
733 #if no-windows
734
734
735 test for escaping the repo name (CVE-2016-3069)
735 test for escaping the repo name (CVE-2016-3069)
736
736
737 $ git init '`echo pwned >COMMAND-INJECTION`'
737 $ git init '`echo pwned >COMMAND-INJECTION`'
738 Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
738 Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
739 $ cd '`echo pwned >COMMAND-INJECTION`'
739 $ cd '`echo pwned >COMMAND-INJECTION`'
740 $ git commit -q --allow-empty -m 'empty'
740 $ git commit -q --allow-empty -m 'empty'
741 $ cd ..
741 $ cd ..
742 $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
742 $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
743 initializing destination converted repository
743 initializing destination converted repository
744 scanning source...
744 scanning source...
745 sorting...
745 sorting...
746 converting...
746 converting...
747 0 empty
747 0 empty
748 updating bookmarks
748 updating bookmarks
749 $ test -f COMMAND-INJECTION
749 $ test -f COMMAND-INJECTION
750 [1]
750 [1]
751
751
752 test for safely passing paths to git (CVE-2016-3105)
753
754 $ git init 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
755 Initialized empty Git repository in $TESTTMP/ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #/.git/
756 $ cd 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #'
757 $ git commit -q --allow-empty -m 'empty'
758 $ cd ..
759 $ hg convert 'ext::sh -c echo% pwned% >GIT-EXT-COMMAND-INJECTION% #' 'converted-git-ext'
760 initializing destination converted-git-ext repository
761 scanning source...
762 sorting...
763 converting...
764 0 empty
765 updating bookmarks
766 $ test -f GIT-EXT-COMMAND-INJECTION
767 [1]
768
752 #endif
769 #endif
753
770
@@ -1,535 +1,535 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > convert=
3 > convert=
4 > [convert]
4 > [convert]
5 > hg.saverev=False
5 > hg.saverev=False
6 > EOF
6 > EOF
7 $ hg help convert
7 $ hg help convert
8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
9
9
10 convert a foreign SCM repository to a Mercurial one.
10 convert a foreign SCM repository to a Mercurial one.
11
11
12 Accepted source formats [identifiers]:
12 Accepted source formats [identifiers]:
13
13
14 - Mercurial [hg]
14 - Mercurial [hg]
15 - CVS [cvs]
15 - CVS [cvs]
16 - Darcs [darcs]
16 - Darcs [darcs]
17 - git [git]
17 - git [git]
18 - Subversion [svn]
18 - Subversion [svn]
19 - Monotone [mtn]
19 - Monotone [mtn]
20 - GNU Arch [gnuarch]
20 - GNU Arch [gnuarch]
21 - Bazaar [bzr]
21 - Bazaar [bzr]
22 - Perforce [p4]
22 - Perforce [p4]
23
23
24 Accepted destination formats [identifiers]:
24 Accepted destination formats [identifiers]:
25
25
26 - Mercurial [hg]
26 - Mercurial [hg]
27 - Subversion [svn] (history on branches is not preserved)
27 - Subversion [svn] (history on branches is not preserved)
28
28
29 If no revision is given, all revisions will be converted. Otherwise,
29 If no revision is given, all revisions will be converted. Otherwise,
30 convert will only import up to the named revision (given in a format
30 convert will only import up to the named revision (given in a format
31 understood by the source).
31 understood by the source).
32
32
33 If no destination directory name is specified, it defaults to the basename
33 If no destination directory name is specified, it defaults to the basename
34 of the source with "-hg" appended. If the destination repository doesn't
34 of the source with "-hg" appended. If the destination repository doesn't
35 exist, it will be created.
35 exist, it will be created.
36
36
37 By default, all sources except Mercurial will use --branchsort. Mercurial
37 By default, all sources except Mercurial will use --branchsort. Mercurial
38 uses --sourcesort to preserve original revision numbers order. Sort modes
38 uses --sourcesort to preserve original revision numbers order. Sort modes
39 have the following effects:
39 have the following effects:
40
40
41 --branchsort convert from parent to child revision when possible, which
41 --branchsort convert from parent to child revision when possible, which
42 means branches are usually converted one after the other.
42 means branches are usually converted one after the other.
43 It generates more compact repositories.
43 It generates more compact repositories.
44 --datesort sort revisions by date. Converted repositories have good-
44 --datesort sort revisions by date. Converted repositories have good-
45 looking changelogs but are often an order of magnitude
45 looking changelogs but are often an order of magnitude
46 larger than the same ones generated by --branchsort.
46 larger than the same ones generated by --branchsort.
47 --sourcesort try to preserve source revisions order, only supported by
47 --sourcesort try to preserve source revisions order, only supported by
48 Mercurial sources.
48 Mercurial sources.
49 --closesort try to move closed revisions as close as possible to parent
49 --closesort try to move closed revisions as close as possible to parent
50 branches, only supported by Mercurial sources.
50 branches, only supported by Mercurial sources.
51
51
52 If "REVMAP" isn't given, it will be put in a default location
52 If "REVMAP" isn't given, it will be put in a default location
53 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
53 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
54 maps each source commit ID to the destination ID for that revision, like
54 maps each source commit ID to the destination ID for that revision, like
55 so:
55 so:
56
56
57 <source ID> <destination ID>
57 <source ID> <destination ID>
58
58
59 If the file doesn't exist, it's automatically created. It's updated on
59 If the file doesn't exist, it's automatically created. It's updated on
60 each commit copied, so 'hg convert' can be interrupted and can be run
60 each commit copied, so 'hg convert' can be interrupted and can be run
61 repeatedly to copy new commits.
61 repeatedly to copy new commits.
62
62
63 The authormap is a simple text file that maps each source commit author to
63 The authormap is a simple text file that maps each source commit author to
64 a destination commit author. It is handy for source SCMs that use unix
64 a destination commit author. It is handy for source SCMs that use unix
65 logins to identify authors (e.g.: CVS). One line per author mapping and
65 logins to identify authors (e.g.: CVS). One line per author mapping and
66 the line format is:
66 the line format is:
67
67
68 source author = destination author
68 source author = destination author
69
69
70 Empty lines and lines starting with a "#" are ignored.
70 Empty lines and lines starting with a "#" are ignored.
71
71
72 The filemap is a file that allows filtering and remapping of files and
72 The filemap is a file that allows filtering and remapping of files and
73 directories. Each line can contain one of the following directives:
73 directories. Each line can contain one of the following directives:
74
74
75 include path/to/file-or-dir
75 include path/to/file-or-dir
76
76
77 exclude path/to/file-or-dir
77 exclude path/to/file-or-dir
78
78
79 rename path/to/source path/to/destination
79 rename path/to/source path/to/destination
80
80
81 Comment lines start with "#". A specified path matches if it equals the
81 Comment lines start with "#". A specified path matches if it equals the
82 full relative name of a file or one of its parent directories. The
82 full relative name of a file or one of its parent directories. The
83 "include" or "exclude" directive with the longest matching path applies,
83 "include" or "exclude" directive with the longest matching path applies,
84 so line order does not matter.
84 so line order does not matter.
85
85
86 The "include" directive causes a file, or all files under a directory, to
86 The "include" directive causes a file, or all files under a directory, to
87 be included in the destination repository. The default if there are no
87 be included in the destination repository. The default if there are no
88 "include" statements is to include everything. If there are any "include"
88 "include" statements is to include everything. If there are any "include"
89 statements, nothing else is included. The "exclude" directive causes files
89 statements, nothing else is included. The "exclude" directive causes files
90 or directories to be omitted. The "rename" directive renames a file or
90 or directories to be omitted. The "rename" directive renames a file or
91 directory if it is converted. To rename from a subdirectory into the root
91 directory if it is converted. To rename from a subdirectory into the root
92 of the repository, use "." as the path to rename to.
92 of the repository, use "." as the path to rename to.
93
93
94 "--full" will make sure the converted changesets contain exactly the right
94 "--full" will make sure the converted changesets contain exactly the right
95 files with the right content. It will make a full conversion of all files,
95 files with the right content. It will make a full conversion of all files,
96 not just the ones that have changed. Files that already are correct will
96 not just the ones that have changed. Files that already are correct will
97 not be changed. This can be used to apply filemap changes when converting
97 not be changed. This can be used to apply filemap changes when converting
98 incrementally. This is currently only supported for Mercurial and
98 incrementally. This is currently only supported for Mercurial and
99 Subversion.
99 Subversion.
100
100
101 The splicemap is a file that allows insertion of synthetic history,
101 The splicemap is a file that allows insertion of synthetic history,
102 letting you specify the parents of a revision. This is useful if you want
102 letting you specify the parents of a revision. This is useful if you want
103 to e.g. give a Subversion merge two parents, or graft two disconnected
103 to e.g. give a Subversion merge two parents, or graft two disconnected
104 series of history together. Each entry contains a key, followed by a
104 series of history together. Each entry contains a key, followed by a
105 space, followed by one or two comma-separated values:
105 space, followed by one or two comma-separated values:
106
106
107 key parent1, parent2
107 key parent1, parent2
108
108
109 The key is the revision ID in the source revision control system whose
109 The key is the revision ID in the source revision control system whose
110 parents should be modified (same format as a key in .hg/shamap). The
110 parents should be modified (same format as a key in .hg/shamap). The
111 values are the revision IDs (in either the source or destination revision
111 values are the revision IDs (in either the source or destination revision
112 control system) that should be used as the new parents for that node. For
112 control system) that should be used as the new parents for that node. For
113 example, if you have merged "release-1.0" into "trunk", then you should
113 example, if you have merged "release-1.0" into "trunk", then you should
114 specify the revision on "trunk" as the first parent and the one on the
114 specify the revision on "trunk" as the first parent and the one on the
115 "release-1.0" branch as the second.
115 "release-1.0" branch as the second.
116
116
117 The branchmap is a file that allows you to rename a branch when it is
117 The branchmap is a file that allows you to rename a branch when it is
118 being brought in from whatever external repository. When used in
118 being brought in from whatever external repository. When used in
119 conjunction with a splicemap, it allows for a powerful combination to help
119 conjunction with a splicemap, it allows for a powerful combination to help
120 fix even the most badly mismanaged repositories and turn them into nicely
120 fix even the most badly mismanaged repositories and turn them into nicely
121 structured Mercurial repositories. The branchmap contains lines of the
121 structured Mercurial repositories. The branchmap contains lines of the
122 form:
122 form:
123
123
124 original_branch_name new_branch_name
124 original_branch_name new_branch_name
125
125
126 where "original_branch_name" is the name of the branch in the source
126 where "original_branch_name" is the name of the branch in the source
127 repository, and "new_branch_name" is the name of the branch is the
127 repository, and "new_branch_name" is the name of the branch is the
128 destination repository. No whitespace is allowed in the branch names. This
128 destination repository. No whitespace is allowed in the branch names. This
129 can be used to (for instance) move code in one repository from "default"
129 can be used to (for instance) move code in one repository from "default"
130 to a named branch.
130 to a named branch.
131
131
132 Mercurial Source
132 Mercurial Source
133 ################
133 ################
134
134
135 The Mercurial source recognizes the following configuration options, which
135 The Mercurial source recognizes the following configuration options, which
136 you can set on the command line with "--config":
136 you can set on the command line with "--config":
137
137
138 convert.hg.ignoreerrors
138 convert.hg.ignoreerrors
139 ignore integrity errors when reading. Use it to fix
139 ignore integrity errors when reading. Use it to fix
140 Mercurial repositories with missing revlogs, by converting
140 Mercurial repositories with missing revlogs, by converting
141 from and to Mercurial. Default is False.
141 from and to Mercurial. Default is False.
142 convert.hg.saverev
142 convert.hg.saverev
143 store original revision ID in changeset (forces target IDs
143 store original revision ID in changeset (forces target IDs
144 to change). It takes a boolean argument and defaults to
144 to change). It takes a boolean argument and defaults to
145 False.
145 False.
146 convert.hg.startrev
146 convert.hg.startrev
147 specify the initial Mercurial revision. The default is 0.
147 specify the initial Mercurial revision. The default is 0.
148 convert.hg.revs
148 convert.hg.revs
149 revset specifying the source revisions to convert.
149 revset specifying the source revisions to convert.
150
150
151 CVS Source
151 CVS Source
152 ##########
152 ##########
153
153
154 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
154 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
155 indicate the starting point of what will be converted. Direct access to
155 indicate the starting point of what will be converted. Direct access to
156 the repository files is not needed, unless of course the repository is
156 the repository files is not needed, unless of course the repository is
157 ":local:". The conversion uses the top level directory in the sandbox to
157 ":local:". The conversion uses the top level directory in the sandbox to
158 find the CVS repository, and then uses CVS rlog commands to find files to
158 find the CVS repository, and then uses CVS rlog commands to find files to
159 convert. This means that unless a filemap is given, all files under the
159 convert. This means that unless a filemap is given, all files under the
160 starting directory will be converted, and that any directory
160 starting directory will be converted, and that any directory
161 reorganization in the CVS sandbox is ignored.
161 reorganization in the CVS sandbox is ignored.
162
162
163 The following options can be used with "--config":
163 The following options can be used with "--config":
164
164
165 convert.cvsps.cache
165 convert.cvsps.cache
166 Set to False to disable remote log caching, for testing and
166 Set to False to disable remote log caching, for testing and
167 debugging purposes. Default is True.
167 debugging purposes. Default is True.
168 convert.cvsps.fuzz
168 convert.cvsps.fuzz
169 Specify the maximum time (in seconds) that is allowed
169 Specify the maximum time (in seconds) that is allowed
170 between commits with identical user and log message in a
170 between commits with identical user and log message in a
171 single changeset. When very large files were checked in as
171 single changeset. When very large files were checked in as
172 part of a changeset then the default may not be long enough.
172 part of a changeset then the default may not be long enough.
173 The default is 60.
173 The default is 60.
174 convert.cvsps.mergeto
174 convert.cvsps.mergeto
175 Specify a regular expression to which commit log messages
175 Specify a regular expression to which commit log messages
176 are matched. If a match occurs, then the conversion process
176 are matched. If a match occurs, then the conversion process
177 will insert a dummy revision merging the branch on which
177 will insert a dummy revision merging the branch on which
178 this log message occurs to the branch indicated in the
178 this log message occurs to the branch indicated in the
179 regex. Default is "{{mergetobranch ([-\w]+)}}"
179 regex. Default is "{{mergetobranch ([-\w]+)}}"
180 convert.cvsps.mergefrom
180 convert.cvsps.mergefrom
181 Specify a regular expression to which commit log messages
181 Specify a regular expression to which commit log messages
182 are matched. If a match occurs, then the conversion process
182 are matched. If a match occurs, then the conversion process
183 will add the most recent revision on the branch indicated in
183 will add the most recent revision on the branch indicated in
184 the regex as the second parent of the changeset. Default is
184 the regex as the second parent of the changeset. Default is
185 "{{mergefrombranch ([-\w]+)}}"
185 "{{mergefrombranch ([-\w]+)}}"
186 convert.localtimezone
186 convert.localtimezone
187 use local time (as determined by the TZ environment
187 use local time (as determined by the TZ environment
188 variable) for changeset date/times. The default is False
188 variable) for changeset date/times. The default is False
189 (use UTC).
189 (use UTC).
190 hooks.cvslog Specify a Python function to be called at the end of
190 hooks.cvslog Specify a Python function to be called at the end of
191 gathering the CVS log. The function is passed a list with
191 gathering the CVS log. The function is passed a list with
192 the log entries, and can modify the entries in-place, or add
192 the log entries, and can modify the entries in-place, or add
193 or delete them.
193 or delete them.
194 hooks.cvschangesets
194 hooks.cvschangesets
195 Specify a Python function to be called after the changesets
195 Specify a Python function to be called after the changesets
196 are calculated from the CVS log. The function is passed a
196 are calculated from the CVS log. The function is passed a
197 list with the changeset entries, and can modify the
197 list with the changeset entries, and can modify the
198 changesets in-place, or add or delete them.
198 changesets in-place, or add or delete them.
199
199
200 An additional "debugcvsps" Mercurial command allows the builtin changeset
200 An additional "debugcvsps" Mercurial command allows the builtin changeset
201 merging code to be run without doing a conversion. Its parameters and
201 merging code to be run without doing a conversion. Its parameters and
202 output are similar to that of cvsps 2.1. Please see the command help for
202 output are similar to that of cvsps 2.1. Please see the command help for
203 more details.
203 more details.
204
204
205 Subversion Source
205 Subversion Source
206 #################
206 #################
207
207
208 Subversion source detects classical trunk/branches/tags layouts. By
208 Subversion source detects classical trunk/branches/tags layouts. By
209 default, the supplied "svn://repo/path/" source URL is converted as a
209 default, the supplied "svn://repo/path/" source URL is converted as a
210 single branch. If "svn://repo/path/trunk" exists it replaces the default
210 single branch. If "svn://repo/path/trunk" exists it replaces the default
211 branch. If "svn://repo/path/branches" exists, its subdirectories are
211 branch. If "svn://repo/path/branches" exists, its subdirectories are
212 listed as possible branches. If "svn://repo/path/tags" exists, it is
212 listed as possible branches. If "svn://repo/path/tags" exists, it is
213 looked for tags referencing converted branches. Default "trunk",
213 looked for tags referencing converted branches. Default "trunk",
214 "branches" and "tags" values can be overridden with following options. Set
214 "branches" and "tags" values can be overridden with following options. Set
215 them to paths relative to the source URL, or leave them blank to disable
215 them to paths relative to the source URL, or leave them blank to disable
216 auto detection.
216 auto detection.
217
217
218 The following options can be set with "--config":
218 The following options can be set with "--config":
219
219
220 convert.svn.branches
220 convert.svn.branches
221 specify the directory containing branches. The default is
221 specify the directory containing branches. The default is
222 "branches".
222 "branches".
223 convert.svn.tags
223 convert.svn.tags
224 specify the directory containing tags. The default is
224 specify the directory containing tags. The default is
225 "tags".
225 "tags".
226 convert.svn.trunk
226 convert.svn.trunk
227 specify the name of the trunk branch. The default is
227 specify the name of the trunk branch. The default is
228 "trunk".
228 "trunk".
229 convert.localtimezone
229 convert.localtimezone
230 use local time (as determined by the TZ environment
230 use local time (as determined by the TZ environment
231 variable) for changeset date/times. The default is False
231 variable) for changeset date/times. The default is False
232 (use UTC).
232 (use UTC).
233
233
234 Source history can be retrieved starting at a specific revision, instead
234 Source history can be retrieved starting at a specific revision, instead
235 of being integrally converted. Only single branch conversions are
235 of being integrally converted. Only single branch conversions are
236 supported.
236 supported.
237
237
238 convert.svn.startrev
238 convert.svn.startrev
239 specify start Subversion revision number. The default is 0.
239 specify start Subversion revision number. The default is 0.
240
240
241 Git Source
241 Git Source
242 ##########
242 ##########
243
243
244 The Git importer converts commits from all reachable branches (refs in
244 The Git importer converts commits from all reachable branches (refs in
245 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
245 refs/heads) and remotes (refs in refs/remotes) to Mercurial. Branches are
246 converted to bookmarks with the same name, with the leading 'refs/heads'
246 converted to bookmarks with the same name, with the leading 'refs/heads'
247 stripped. Git submodules are converted to Git subrepos in Mercurial.
247 stripped. Git submodules are converted to Git subrepos in Mercurial.
248
248
249 The following options can be set with "--config":
249 The following options can be set with "--config":
250
250
251 convert.git.similarity
251 convert.git.similarity
252 specify how similar files modified in a commit must be to be
252 specify how similar files modified in a commit must be to be
253 imported as renames or copies, as a percentage between "0"
253 imported as renames or copies, as a percentage between "0"
254 (disabled) and "100" (files must be identical). For example,
254 (disabled) and "100" (files must be identical). For example,
255 "90" means that a delete/add pair will be imported as a
255 "90" means that a delete/add pair will be imported as a
256 rename if more than 90% of the file hasn't changed. The
256 rename if more than 90% of the file hasn't changed. The
257 default is "50".
257 default is "50".
258 convert.git.findcopiesharder
258 convert.git.findcopiesharder
259 while detecting copies, look at all files in the working
259 while detecting copies, look at all files in the working
260 copy instead of just changed ones. This is very expensive
260 copy instead of just changed ones. This is very expensive
261 for large projects, and is only effective when
261 for large projects, and is only effective when
262 "convert.git.similarity" is greater than 0. The default is
262 "convert.git.similarity" is greater than 0. The default is
263 False.
263 False.
264 convert.git.remoteprefix
264 convert.git.remoteprefix
265 remote refs are converted as bookmarks with
265 remote refs are converted as bookmarks with
266 "convert.git.remoteprefix" as a prefix followed by a /. The
266 "convert.git.remoteprefix" as a prefix followed by a /. The
267 default is 'remote'.
267 default is 'remote'.
268 convert.git.skipsubmodules
268 convert.git.skipsubmodules
269 does not convert root level .gitmodules files or files with
269 does not convert root level .gitmodules files or files with
270 160000 mode indicating a submodule. Default is False.
270 160000 mode indicating a submodule. Default is False.
271
271
272 Perforce Source
272 Perforce Source
273 ###############
273 ###############
274
274
275 The Perforce (P4) importer can be given a p4 depot path or a client
275 The Perforce (P4) importer can be given a p4 depot path or a client
276 specification as source. It will convert all files in the source to a flat
276 specification as source. It will convert all files in the source to a flat
277 Mercurial repository, ignoring labels, branches and integrations. Note
277 Mercurial repository, ignoring labels, branches and integrations. Note
278 that when a depot path is given you then usually should specify a target
278 that when a depot path is given you then usually should specify a target
279 directory, because otherwise the target may be named "...-hg".
279 directory, because otherwise the target may be named "...-hg".
280
280
281 The following options can be set with "--config":
281 The following options can be set with "--config":
282
282
283 convert.p4.encoding
283 convert.p4.encoding
284 specify the encoding to use when decoding standard output of
284 specify the encoding to use when decoding standard output of
285 the Perforce command line tool. The default is default
285 the Perforce command line tool. The default is default
286 system encoding.
286 system encoding.
287 convert.p4.startrev
287 convert.p4.startrev
288 specify initial Perforce revision (a Perforce changelist
288 specify initial Perforce revision (a Perforce changelist
289 number).
289 number).
290
290
291 Mercurial Destination
291 Mercurial Destination
292 #####################
292 #####################
293
293
294 The Mercurial destination will recognize Mercurial subrepositories in the
294 The Mercurial destination will recognize Mercurial subrepositories in the
295 destination directory, and update the .hgsubstate file automatically if
295 destination directory, and update the .hgsubstate file automatically if
296 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
296 the destination subrepositories contain the <dest>/<sub>/.hg/shamap file.
297 Converting a repository with subrepositories requires converting a single
297 Converting a repository with subrepositories requires converting a single
298 repository at a time, from the bottom up.
298 repository at a time, from the bottom up.
299
299
300 The following options are supported:
300 The following options are supported:
301
301
302 convert.hg.clonebranches
302 convert.hg.clonebranches
303 dispatch source branches in separate clones. The default is
303 dispatch source branches in separate clones. The default is
304 False.
304 False.
305 convert.hg.tagsbranch
305 convert.hg.tagsbranch
306 branch name for tag revisions, defaults to "default".
306 branch name for tag revisions, defaults to "default".
307 convert.hg.usebranchnames
307 convert.hg.usebranchnames
308 preserve branch names. The default is True.
308 preserve branch names. The default is True.
309 convert.hg.sourcename
309 convert.hg.sourcename
310 records the given string as a 'convert_source' extra value
310 records the given string as a 'convert_source' extra value
311 on each commit made in the target repository. The default is
311 on each commit made in the target repository. The default is
312 None.
312 None.
313
313
314 All Destinations
314 All Destinations
315 ################
315 ################
316
316
317 All destination types accept the following options:
317 All destination types accept the following options:
318
318
319 convert.skiptags
319 convert.skiptags
320 does not convert tags from the source repo to the target
320 does not convert tags from the source repo to the target
321 repo. The default is False.
321 repo. The default is False.
322
322
323 options ([+] can be repeated):
323 options ([+] can be repeated):
324
324
325 -s --source-type TYPE source repository type
325 -s --source-type TYPE source repository type
326 -d --dest-type TYPE destination repository type
326 -d --dest-type TYPE destination repository type
327 -r --rev REV [+] import up to source revision REV
327 -r --rev REV [+] import up to source revision REV
328 -A --authormap FILE remap usernames using this file
328 -A --authormap FILE remap usernames using this file
329 --filemap FILE remap file names using contents of file
329 --filemap FILE remap file names using contents of file
330 --full apply filemap changes by converting all files again
330 --full apply filemap changes by converting all files again
331 --splicemap FILE splice synthesized history into place
331 --splicemap FILE splice synthesized history into place
332 --branchmap FILE change branch names while converting
332 --branchmap FILE change branch names while converting
333 --branchsort try to sort changesets by branches
333 --branchsort try to sort changesets by branches
334 --datesort try to sort changesets by date
334 --datesort try to sort changesets by date
335 --sourcesort preserve source changesets order
335 --sourcesort preserve source changesets order
336 --closesort try to reorder closed revisions
336 --closesort try to reorder closed revisions
337
337
338 (some details hidden, use --verbose to show complete help)
338 (some details hidden, use --verbose to show complete help)
339 $ hg init a
339 $ hg init a
340 $ cd a
340 $ cd a
341 $ echo a > a
341 $ echo a > a
342 $ hg ci -d'0 0' -Ama
342 $ hg ci -d'0 0' -Ama
343 adding a
343 adding a
344 $ hg cp a b
344 $ hg cp a b
345 $ hg ci -d'1 0' -mb
345 $ hg ci -d'1 0' -mb
346 $ hg rm a
346 $ hg rm a
347 $ hg ci -d'2 0' -mc
347 $ hg ci -d'2 0' -mc
348 $ hg mv b a
348 $ hg mv b a
349 $ hg ci -d'3 0' -md
349 $ hg ci -d'3 0' -md
350 $ echo a >> a
350 $ echo a >> a
351 $ hg ci -d'4 0' -me
351 $ hg ci -d'4 0' -me
352 $ cd ..
352 $ cd ..
353 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
353 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
354 assuming destination a-hg
354 assuming destination a-hg
355 initializing destination a-hg repository
355 initializing destination a-hg repository
356 scanning source...
356 scanning source...
357 sorting...
357 sorting...
358 converting...
358 converting...
359 4 a
359 4 a
360 3 b
360 3 b
361 2 c
361 2 c
362 1 d
362 1 d
363 0 e
363 0 e
364 $ hg --cwd a-hg pull ../a
364 $ hg --cwd a-hg pull ../a
365 pulling from ../a
365 pulling from ../a
366 searching for changes
366 searching for changes
367 no changes found
367 no changes found
368
368
369 conversion to existing file should fail
369 conversion to existing file should fail
370
370
371 $ touch bogusfile
371 $ touch bogusfile
372 $ hg convert a bogusfile
372 $ hg convert a bogusfile
373 initializing destination bogusfile repository
373 initializing destination bogusfile repository
374 abort: cannot create new bundle repository
374 abort: cannot create new bundle repository
375 [255]
375 [255]
376
376
377 #if unix-permissions no-root
377 #if unix-permissions no-root
378
378
379 conversion to dir without permissions should fail
379 conversion to dir without permissions should fail
380
380
381 $ mkdir bogusdir
381 $ mkdir bogusdir
382 $ chmod 000 bogusdir
382 $ chmod 000 bogusdir
383
383
384 $ hg convert a bogusdir
384 $ hg convert a bogusdir
385 abort: Permission denied: 'bogusdir'
385 abort: Permission denied: 'bogusdir'
386 [255]
386 [255]
387
387
388 user permissions should succeed
388 user permissions should succeed
389
389
390 $ chmod 700 bogusdir
390 $ chmod 700 bogusdir
391 $ hg convert a bogusdir
391 $ hg convert a bogusdir
392 initializing destination bogusdir repository
392 initializing destination bogusdir repository
393 scanning source...
393 scanning source...
394 sorting...
394 sorting...
395 converting...
395 converting...
396 4 a
396 4 a
397 3 b
397 3 b
398 2 c
398 2 c
399 1 d
399 1 d
400 0 e
400 0 e
401
401
402 #endif
402 #endif
403
403
404 test pre and post conversion actions
404 test pre and post conversion actions
405
405
406 $ echo 'include b' > filemap
406 $ echo 'include b' > filemap
407 $ hg convert --debug --filemap filemap a partialb | \
407 $ hg convert --debug --filemap filemap a partialb | \
408 > grep 'run hg'
408 > grep 'run hg'
409 run hg source pre-conversion action
409 run hg source pre-conversion action
410 run hg sink pre-conversion action
410 run hg sink pre-conversion action
411 run hg sink post-conversion action
411 run hg sink post-conversion action
412 run hg source post-conversion action
412 run hg source post-conversion action
413
413
414 converting empty dir should fail "nicely
414 converting empty dir should fail "nicely
415
415
416 $ mkdir emptydir
416 $ mkdir emptydir
417
417
418 override $PATH to ensure p4 not visible; use $PYTHON in case we're
418 override $PATH to ensure p4 not visible; use $PYTHON in case we're
419 running from a devel copy, not a temp installation
419 running from a devel copy, not a temp installation
420
420
421 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
421 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
422 assuming destination emptydir-hg
422 assuming destination emptydir-hg
423 initializing destination emptydir-hg repository
423 initializing destination emptydir-hg repository
424 emptydir does not look like a CVS checkout
424 emptydir does not look like a CVS checkout
425 emptydir does not look like a Git repository
425 $TESTTMP/emptydir does not look like a Git repository
426 emptydir does not look like a Subversion repository
426 emptydir does not look like a Subversion repository
427 emptydir is not a local Mercurial repository
427 emptydir is not a local Mercurial repository
428 emptydir does not look like a darcs repository
428 emptydir does not look like a darcs repository
429 emptydir does not look like a monotone repository
429 emptydir does not look like a monotone repository
430 emptydir does not look like a GNU Arch repository
430 emptydir does not look like a GNU Arch repository
431 emptydir does not look like a Bazaar repository
431 emptydir does not look like a Bazaar repository
432 cannot find required "p4" tool
432 cannot find required "p4" tool
433 abort: emptydir: missing or unsupported repository
433 abort: emptydir: missing or unsupported repository
434 [255]
434 [255]
435
435
436 convert with imaginary source type
436 convert with imaginary source type
437
437
438 $ hg convert --source-type foo a a-foo
438 $ hg convert --source-type foo a a-foo
439 initializing destination a-foo repository
439 initializing destination a-foo repository
440 abort: foo: invalid source repository type
440 abort: foo: invalid source repository type
441 [255]
441 [255]
442
442
443 convert with imaginary sink type
443 convert with imaginary sink type
444
444
445 $ hg convert --dest-type foo a a-foo
445 $ hg convert --dest-type foo a a-foo
446 abort: foo: invalid destination repository type
446 abort: foo: invalid destination repository type
447 [255]
447 [255]
448
448
449 testing: convert must not produce duplicate entries in fncache
449 testing: convert must not produce duplicate entries in fncache
450
450
451 $ hg convert a b
451 $ hg convert a b
452 initializing destination b repository
452 initializing destination b repository
453 scanning source...
453 scanning source...
454 sorting...
454 sorting...
455 converting...
455 converting...
456 4 a
456 4 a
457 3 b
457 3 b
458 2 c
458 2 c
459 1 d
459 1 d
460 0 e
460 0 e
461
461
462 contents of fncache file:
462 contents of fncache file:
463
463
464 $ cat b/.hg/store/fncache | sort
464 $ cat b/.hg/store/fncache | sort
465 data/a.i
465 data/a.i
466 data/b.i
466 data/b.i
467
467
468 test bogus URL
468 test bogus URL
469
469
470 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
470 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
471 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
471 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
472 [255]
472 [255]
473
473
474 test revset converted() lookup
474 test revset converted() lookup
475
475
476 $ hg --config convert.hg.saverev=True convert a c
476 $ hg --config convert.hg.saverev=True convert a c
477 initializing destination c repository
477 initializing destination c repository
478 scanning source...
478 scanning source...
479 sorting...
479 sorting...
480 converting...
480 converting...
481 4 a
481 4 a
482 3 b
482 3 b
483 2 c
483 2 c
484 1 d
484 1 d
485 0 e
485 0 e
486 $ echo f > c/f
486 $ echo f > c/f
487 $ hg -R c ci -d'0 0' -Amf
487 $ hg -R c ci -d'0 0' -Amf
488 adding f
488 adding f
489 created new head
489 created new head
490 $ hg -R c log -r "converted(09d945a62ce6)"
490 $ hg -R c log -r "converted(09d945a62ce6)"
491 changeset: 1:98c3dd46a874
491 changeset: 1:98c3dd46a874
492 user: test
492 user: test
493 date: Thu Jan 01 00:00:01 1970 +0000
493 date: Thu Jan 01 00:00:01 1970 +0000
494 summary: b
494 summary: b
495
495
496 $ hg -R c log -r "converted()"
496 $ hg -R c log -r "converted()"
497 changeset: 0:31ed57b2037c
497 changeset: 0:31ed57b2037c
498 user: test
498 user: test
499 date: Thu Jan 01 00:00:00 1970 +0000
499 date: Thu Jan 01 00:00:00 1970 +0000
500 summary: a
500 summary: a
501
501
502 changeset: 1:98c3dd46a874
502 changeset: 1:98c3dd46a874
503 user: test
503 user: test
504 date: Thu Jan 01 00:00:01 1970 +0000
504 date: Thu Jan 01 00:00:01 1970 +0000
505 summary: b
505 summary: b
506
506
507 changeset: 2:3b9ca06ef716
507 changeset: 2:3b9ca06ef716
508 user: test
508 user: test
509 date: Thu Jan 01 00:00:02 1970 +0000
509 date: Thu Jan 01 00:00:02 1970 +0000
510 summary: c
510 summary: c
511
511
512 changeset: 3:4e0debd37cf2
512 changeset: 3:4e0debd37cf2
513 user: test
513 user: test
514 date: Thu Jan 01 00:00:03 1970 +0000
514 date: Thu Jan 01 00:00:03 1970 +0000
515 summary: d
515 summary: d
516
516
517 changeset: 4:9de3bc9349c5
517 changeset: 4:9de3bc9349c5
518 user: test
518 user: test
519 date: Thu Jan 01 00:00:04 1970 +0000
519 date: Thu Jan 01 00:00:04 1970 +0000
520 summary: e
520 summary: e
521
521
522
522
523 test specifying a sourcename
523 test specifying a sourcename
524 $ echo g > a/g
524 $ echo g > a/g
525 $ hg -R a ci -d'0 0' -Amg
525 $ hg -R a ci -d'0 0' -Amg
526 adding g
526 adding g
527 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
527 $ hg --config convert.hg.sourcename=mysource --config convert.hg.saverev=True convert a c
528 scanning source...
528 scanning source...
529 sorting...
529 sorting...
530 converting...
530 converting...
531 0 g
531 0 g
532 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
532 $ hg -R c log -r tip --template '{extras % "{extra}\n"}'
533 branch=default
533 branch=default
534 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
534 convert_revision=a3bc6100aa8ec03e00aaf271f1f50046fb432072
535 convert_source=mysource
535 convert_source=mysource
General Comments 0
You need to be logged in to leave comments. Login now