##// END OF EJS Templates
convert: fix git convert using servers branches...
Durham Goode -
r25905:80149d0b default
parent child Browse files
Show More
@@ -1,401 +1,404 b''
1 1 # git.py - git support for the convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import os
9 9 import subprocess
10 10 from mercurial import util, config, error
11 11 from mercurial.node import hex, nullid
12 12 from mercurial.i18n import _
13 13
14 14 from common import NoRepo, commit, converter_source, checktool
15 15
16 16 class submodule(object):
17 17 def __init__(self, path, node, url):
18 18 self.path = path
19 19 self.node = node
20 20 self.url = url
21 21
22 22 def hgsub(self):
23 23 return "%s = [git]%s" % (self.path, self.url)
24 24
25 25 def hgsubstate(self):
26 26 return "%s %s" % (self.node, self.path)
27 27
28 28 class convert_git(converter_source):
29 29 # Windows does not support GIT_DIR= construct while other systems
30 30 # cannot remove environment variable. Just assume none have
31 31 # both issues.
32 32 if util.safehasattr(os, 'unsetenv'):
33 33 def gitopen(self, s, err=None):
34 34 prevgitdir = os.environ.get('GIT_DIR')
35 35 os.environ['GIT_DIR'] = self.path
36 36 try:
37 37 if err == subprocess.PIPE:
38 38 (stdin, stdout, stderr) = util.popen3(s)
39 39 return stdout
40 40 elif err == subprocess.STDOUT:
41 41 return self.popen_with_stderr(s)
42 42 else:
43 43 return util.popen(s, 'rb')
44 44 finally:
45 45 if prevgitdir is None:
46 46 del os.environ['GIT_DIR']
47 47 else:
48 48 os.environ['GIT_DIR'] = prevgitdir
49 49
50 50 def gitpipe(self, s):
51 51 prevgitdir = os.environ.get('GIT_DIR')
52 52 os.environ['GIT_DIR'] = self.path
53 53 try:
54 54 return util.popen3(s)
55 55 finally:
56 56 if prevgitdir is None:
57 57 del os.environ['GIT_DIR']
58 58 else:
59 59 os.environ['GIT_DIR'] = prevgitdir
60 60
61 61 else:
62 62 def gitopen(self, s, err=None):
63 63 if err == subprocess.PIPE:
64 64 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
65 65 return so
66 66 elif err == subprocess.STDOUT:
67 67 return self.popen_with_stderr(s)
68 68 else:
69 69 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
70 70
71 71 def gitpipe(self, s):
72 72 return util.popen3('GIT_DIR=%s %s' % (self.path, s))
73 73
74 74 def popen_with_stderr(self, s):
75 75 p = subprocess.Popen(s, shell=True, bufsize=-1,
76 76 close_fds=util.closefds,
77 77 stdin=subprocess.PIPE,
78 78 stdout=subprocess.PIPE,
79 79 stderr=subprocess.STDOUT,
80 80 universal_newlines=False,
81 81 env=None)
82 82 return p.stdout
83 83
84 84 def gitread(self, s):
85 85 fh = self.gitopen(s)
86 86 data = fh.read()
87 87 return data, fh.close()
88 88
89 89 def __init__(self, ui, path, revs=None):
90 90 super(convert_git, self).__init__(ui, path, revs=revs)
91 91
92 92 if os.path.isdir(path + "/.git"):
93 93 path += "/.git"
94 94 if not os.path.exists(path + "/objects"):
95 95 raise NoRepo(_("%s does not look like a Git repository") % path)
96 96
97 97 # The default value (50) is based on the default for 'git diff'.
98 98 similarity = ui.configint('convert', 'git.similarity', default=50)
99 99 if similarity < 0 or similarity > 100:
100 100 raise util.Abort(_('similarity must be between 0 and 100'))
101 101 if similarity > 0:
102 102 self.simopt = '-C%d%%' % similarity
103 103 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
104 104 False)
105 105 if findcopiesharder:
106 106 self.simopt += ' --find-copies-harder'
107 107 else:
108 108 self.simopt = ''
109 109
110 110 checktool('git', 'git')
111 111
112 112 self.path = path
113 113 self.submodules = []
114 114
115 115 self.catfilepipe = self.gitpipe('git cat-file --batch')
116 116
117 117 def after(self):
118 118 for f in self.catfilepipe:
119 119 f.close()
120 120
121 121 def getheads(self):
122 122 if not self.revs:
123 123 heads, ret = self.gitread('git rev-parse --branches --remotes')
124 124 heads = heads.splitlines()
125 125 if ret:
126 126 raise util.Abort(_('cannot retrieve git heads'))
127 127 else:
128 128 heads = []
129 129 for rev in self.revs:
130 130 rawhead, ret = self.gitread("git rev-parse --verify %s" % rev)
131 131 heads.append(rawhead[:-1])
132 132 if ret:
133 133 raise util.Abort(_('cannot retrieve git head "%s"') % rev)
134 134 return heads
135 135
136 136 def catfile(self, rev, type):
137 137 if rev == hex(nullid):
138 138 raise IOError
139 139 self.catfilepipe[0].write(rev+'\n')
140 140 self.catfilepipe[0].flush()
141 141 info = self.catfilepipe[1].readline().split()
142 142 if info[1] != type:
143 143 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
144 144 size = int(info[2])
145 145 data = self.catfilepipe[1].read(size)
146 146 if len(data) < size:
147 147 raise util.Abort(_('cannot read %r object at %s: unexpected size')
148 148 % (type, rev))
149 149 # read the trailing newline
150 150 self.catfilepipe[1].read(1)
151 151 return data
152 152
153 153 def getfile(self, name, rev):
154 154 if rev == hex(nullid):
155 155 return None, None
156 156 if name == '.hgsub':
157 157 data = '\n'.join([m.hgsub() for m in self.submoditer()])
158 158 mode = ''
159 159 elif name == '.hgsubstate':
160 160 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
161 161 mode = ''
162 162 else:
163 163 data = self.catfile(rev, "blob")
164 164 mode = self.modecache[(name, rev)]
165 165 return data, mode
166 166
167 167 def submoditer(self):
168 168 null = hex(nullid)
169 169 for m in sorted(self.submodules, key=lambda p: p.path):
170 170 if m.node != null:
171 171 yield m
172 172
173 173 def parsegitmodules(self, content):
174 174 """Parse the formatted .gitmodules file, example file format:
175 175 [submodule "sub"]\n
176 176 \tpath = sub\n
177 177 \turl = git://giturl\n
178 178 """
179 179 self.submodules = []
180 180 c = config.config()
181 181 # Each item in .gitmodules starts with whitespace that cant be parsed
182 182 c.parse('.gitmodules', '\n'.join(line.strip() for line in
183 183 content.split('\n')))
184 184 for sec in c.sections():
185 185 s = c[sec]
186 186 if 'url' in s and 'path' in s:
187 187 self.submodules.append(submodule(s['path'], '', s['url']))
188 188
189 189 def retrievegitmodules(self, version):
190 190 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
191 191 if ret:
192 192 # This can happen if a file is in the repo that has permissions
193 193 # 160000, but there is no .gitmodules file.
194 194 self.ui.warn(_("warning: cannot read submodules config file in "
195 195 "%s\n") % version)
196 196 return
197 197
198 198 try:
199 199 self.parsegitmodules(modules)
200 200 except error.ParseError:
201 201 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
202 202 % version)
203 203 return
204 204
205 205 for m in self.submodules:
206 206 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
207 207 if ret:
208 208 continue
209 209 m.node = node.strip()
210 210
211 211 def getchanges(self, version, full):
212 212 if full:
213 213 raise util.Abort(_("convert from git do not support --full"))
214 214 self.modecache = {}
215 215 fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
216 216 self.simopt, version))
217 217 changes = []
218 218 copies = {}
219 219 seen = set()
220 220 entry = None
221 221 subexists = [False]
222 222 subdeleted = [False]
223 223 difftree = fh.read().split('\x00')
224 224 lcount = len(difftree)
225 225 i = 0
226 226
227 227 def add(entry, f, isdest):
228 228 seen.add(f)
229 229 h = entry[3]
230 230 p = (entry[1] == "100755")
231 231 s = (entry[1] == "120000")
232 232 renamesource = (not isdest and entry[4][0] == 'R')
233 233
234 234 if f == '.gitmodules':
235 235 subexists[0] = True
236 236 if entry[4] == 'D' or renamesource:
237 237 subdeleted[0] = True
238 238 changes.append(('.hgsub', hex(nullid)))
239 239 else:
240 240 changes.append(('.hgsub', ''))
241 241 elif entry[1] == '160000' or entry[0] == ':160000':
242 242 subexists[0] = True
243 243 else:
244 244 if renamesource:
245 245 h = hex(nullid)
246 246 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
247 247 changes.append((f, h))
248 248
249 249 while i < lcount:
250 250 l = difftree[i]
251 251 i += 1
252 252 if not entry:
253 253 if not l.startswith(':'):
254 254 continue
255 255 entry = l.split()
256 256 continue
257 257 f = l
258 258 if f not in seen:
259 259 add(entry, f, False)
260 260 # A file can be copied multiple times, or modified and copied
261 261 # simultaneously. So f can be repeated even if fdest isn't.
262 262 if entry[4][0] in 'RC':
263 263 # rename or copy: next line is the destination
264 264 fdest = difftree[i]
265 265 i += 1
266 266 if fdest not in seen:
267 267 add(entry, fdest, True)
268 268 # .gitmodules isn't imported at all, so it being copied to
269 269 # and fro doesn't really make sense
270 270 if f != '.gitmodules' and fdest != '.gitmodules':
271 271 copies[fdest] = f
272 272 entry = None
273 273 if fh.close():
274 274 raise util.Abort(_('cannot read changes in %s') % version)
275 275
276 276 if subexists[0]:
277 277 if subdeleted[0]:
278 278 changes.append(('.hgsubstate', hex(nullid)))
279 279 else:
280 280 self.retrievegitmodules(version)
281 281 changes.append(('.hgsubstate', ''))
282 282 return (changes, copies, set())
283 283
284 284 def getcommit(self, version):
285 285 c = self.catfile(version, "commit") # read the commit hash
286 286 end = c.find("\n\n")
287 287 message = c[end + 2:]
288 288 message = self.recode(message)
289 289 l = c[:end].splitlines()
290 290 parents = []
291 291 author = committer = None
292 292 for e in l[1:]:
293 293 n, v = e.split(" ", 1)
294 294 if n == "author":
295 295 p = v.split()
296 296 tm, tz = p[-2:]
297 297 author = " ".join(p[:-2])
298 298 if author[0] == "<": author = author[1:-1]
299 299 author = self.recode(author)
300 300 if n == "committer":
301 301 p = v.split()
302 302 tm, tz = p[-2:]
303 303 committer = " ".join(p[:-2])
304 304 if committer[0] == "<": committer = committer[1:-1]
305 305 committer = self.recode(committer)
306 306 if n == "parent":
307 307 parents.append(v)
308 308
309 309 if committer and committer != author:
310 310 message += "\ncommitter: %s\n" % committer
311 311 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
312 312 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
313 313 date = tm + " " + str(tz)
314 314
315 315 c = commit(parents=parents, date=date, author=author, desc=message,
316 316 rev=version)
317 317 return c
318 318
319 319 def numcommits(self):
320 320 return len([None for _ in self.gitopen('git rev-list --all')])
321 321
322 322 def gettags(self):
323 323 tags = {}
324 324 alltags = {}
325 325 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
326 326 err=subprocess.STDOUT)
327 327 prefix = 'refs/tags/'
328 328
329 329 # Build complete list of tags, both annotated and bare ones
330 330 for line in fh:
331 331 line = line.strip()
332 332 if line.startswith("error:") or line.startswith("fatal:"):
333 333 raise util.Abort(_('cannot read tags from %s') % self.path)
334 334 node, tag = line.split(None, 1)
335 335 if not tag.startswith(prefix):
336 336 continue
337 337 alltags[tag[len(prefix):]] = node
338 338 if fh.close():
339 339 raise util.Abort(_('cannot read tags from %s') % self.path)
340 340
341 341 # Filter out tag objects for annotated tag refs
342 342 for tag in alltags:
343 343 if tag.endswith('^{}'):
344 344 tags[tag[:-3]] = alltags[tag]
345 345 else:
346 346 if tag + '^{}' in alltags:
347 347 continue
348 348 else:
349 349 tags[tag] = alltags[tag]
350 350
351 351 return tags
352 352
353 353 def getchangedfiles(self, version, i):
354 354 changes = []
355 355 if i is None:
356 356 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
357 357 for l in fh:
358 358 if "\t" not in l:
359 359 continue
360 360 m, f = l[:-1].split("\t")
361 361 changes.append(f)
362 362 else:
363 363 fh = self.gitopen('git diff-tree --name-only --root -r %s '
364 364 '"%s^%s" --' % (version, version, i + 1))
365 365 changes = [f.rstrip('\n') for f in fh]
366 366 if fh.close():
367 367 raise util.Abort(_('cannot read changes in %s') % version)
368 368
369 369 return changes
370 370
371 371 def getbookmarks(self):
372 372 bookmarks = {}
373 373
374 # Interesting references in git are prefixed
375 prefix = 'refs/heads/'
376 prefixlen = len(prefix)
374 # Handle local and remote branches
375 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
376 reftypes = [
377 # (git prefix, hg prefix)
378 ('refs/remotes/origin/', remoteprefix + '/'),
379 ('refs/heads/', '')
380 ]
377 381
378 # factor two commands
379 remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote')
380 gitcmd = { remoteprefix + '/': 'git ls-remote --heads origin',
381 '': 'git show-ref'}
382 exclude = set([
383 'refs/remotes/origin/HEAD',
384 ])
382 385
383 # Origin heads
384 for reftype in gitcmd:
385 try:
386 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
387 for line in fh:
388 line = line.strip()
389 rev, name = line.split(None, 1)
390 if not name.startswith(prefix):
386 try:
387 fh = self.gitopen('git show-ref', err=subprocess.PIPE)
388 for line in fh:
389 line = line.strip()
390 rev, name = line.split(None, 1)
391 # Process each type of branch
392 for gitprefix, hgprefix in reftypes:
393 if not name.startswith(gitprefix) or name in exclude:
391 394 continue
392 name = '%s%s' % (reftype, name[prefixlen:])
395 name = '%s%s' % (hgprefix, name[len(gitprefix):])
393 396 bookmarks[name] = rev
394 except Exception:
395 pass
397 except Exception:
398 pass
396 399
397 400 return bookmarks
398 401
399 402 def checkrevformat(self, revstr, mapname='splicemap'):
400 403 """ git revision string is a 40 byte hex """
401 404 self.checkhexformat(revstr, mapname)
@@ -1,694 +1,716 b''
1 1 #require git
2 2
3 3 $ echo "[core]" >> $HOME/.gitconfig
4 4 $ echo "autocrlf = false" >> $HOME/.gitconfig
5 5 $ echo "[core]" >> $HOME/.gitconfig
6 6 $ echo "autocrlf = false" >> $HOME/.gitconfig
7 7 $ echo "[extensions]" >> $HGRCPATH
8 8 $ echo "convert=" >> $HGRCPATH
9 9 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
10 10 $ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
11 11 $ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
12 12 $ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
13 13 $ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
14 14 $ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
15 15 $ INVALIDID1=afd12345af
16 16 $ INVALIDID2=28173x36ddd1e67bf7098d541130558ef5534a86
17 17 $ VALIDID1=39b3d83f9a69a9ba4ebb111461071a0af0027357
18 18 $ VALIDID2=8dd6476bd09d9c7776355dc454dafe38efaec5da
19 19 $ count=10
20 20 $ commit()
21 21 > {
22 22 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
23 23 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
24 24 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
25 25 > count=`expr $count + 1`
26 26 > }
27 27 $ mkdir git-repo
28 28 $ cd git-repo
29 29 $ git init-db >/dev/null 2>/dev/null
30 30 $ echo a > a
31 31 $ mkdir d
32 32 $ echo b > d/b
33 33 $ git add a d
34 34 $ commit -a -m t1
35 35
36 36 Remove the directory, then try to replace it with a file (issue754)
37 37
38 38 $ git rm -f d/b
39 39 rm 'd/b'
40 40 $ commit -m t2
41 41 $ echo d > d
42 42 $ git add d
43 43 $ commit -m t3
44 44 $ echo b >> a
45 45 $ commit -a -m t4.1
46 46 $ git checkout -b other HEAD~ >/dev/null 2>/dev/null
47 47 $ echo c > a
48 48 $ echo a >> a
49 49 $ commit -a -m t4.2
50 50 $ git checkout master >/dev/null 2>/dev/null
51 51 $ git pull --no-commit . other > /dev/null 2>/dev/null
52 52 $ commit -m 'Merge branch other'
53 53 $ cd ..
54 54 $ hg convert --config extensions.progress= --config progress.assume-tty=1 \
55 55 > --config progress.delay=0 --config progress.changedelay=0 \
56 56 > --config progress.refresh=0 --config progress.width=60 \
57 57 > --datesort git-repo
58 58 \r (no-eol) (esc)
59 59 scanning [======> ] 1/6\r (no-eol) (esc)
60 60 scanning [=============> ] 2/6\r (no-eol) (esc)
61 61 scanning [=====================> ] 3/6\r (no-eol) (esc)
62 62 scanning [============================> ] 4/6\r (no-eol) (esc)
63 63 scanning [===================================> ] 5/6\r (no-eol) (esc)
64 64 scanning [===========================================>] 6/6\r (no-eol) (esc)
65 65 \r (no-eol) (esc)
66 66 \r (no-eol) (esc)
67 67 converting [ ] 0/6\r (no-eol) (esc)
68 68 getting files [==================> ] 1/2\r (no-eol) (esc)
69 69 getting files [======================================>] 2/2\r (no-eol) (esc)
70 70 \r (no-eol) (esc)
71 71 \r (no-eol) (esc)
72 72 converting [======> ] 1/6\r (no-eol) (esc)
73 73 getting files [======================================>] 1/1\r (no-eol) (esc)
74 74 \r (no-eol) (esc)
75 75 \r (no-eol) (esc)
76 76 converting [=============> ] 2/6\r (no-eol) (esc)
77 77 getting files [======================================>] 1/1\r (no-eol) (esc)
78 78 \r (no-eol) (esc)
79 79 \r (no-eol) (esc)
80 80 converting [====================> ] 3/6\r (no-eol) (esc)
81 81 getting files [======================================>] 1/1\r (no-eol) (esc)
82 82 \r (no-eol) (esc)
83 83 \r (no-eol) (esc)
84 84 converting [===========================> ] 4/6\r (no-eol) (esc)
85 85 getting files [======================================>] 1/1\r (no-eol) (esc)
86 86 \r (no-eol) (esc)
87 87 \r (no-eol) (esc)
88 88 converting [==================================> ] 5/6\r (no-eol) (esc)
89 89 getting files [======================================>] 1/1\r (no-eol) (esc)
90 90 \r (no-eol) (esc)
91 91 assuming destination git-repo-hg
92 92 initializing destination git-repo-hg repository
93 93 scanning source...
94 94 sorting...
95 95 converting...
96 96 5 t1
97 97 4 t2
98 98 3 t3
99 99 2 t4.1
100 100 1 t4.2
101 101 0 Merge branch other
102 102 updating bookmarks
103 103 $ hg up -q -R git-repo-hg
104 104 $ hg -R git-repo-hg tip -v
105 105 changeset: 5:c78094926be2
106 106 bookmark: master
107 107 tag: tip
108 108 parent: 3:f5f5cb45432b
109 109 parent: 4:4e174f80c67c
110 110 user: test <test@example.org>
111 111 date: Mon Jan 01 00:00:15 2007 +0000
112 112 files: a
113 113 description:
114 114 Merge branch other
115 115
116 116
117 117 $ count=10
118 118 $ mkdir git-repo2
119 119 $ cd git-repo2
120 120 $ git init-db >/dev/null 2>/dev/null
121 121 $ echo foo > foo
122 122 $ git add foo
123 123 $ commit -a -m 'add foo'
124 124 $ echo >> foo
125 125 $ commit -a -m 'change foo'
126 126 $ git checkout -b Bar HEAD~ >/dev/null 2>/dev/null
127 127 $ echo quux >> quux
128 128 $ git add quux
129 129 $ commit -a -m 'add quux'
130 130 $ echo bar > bar
131 131 $ git add bar
132 132 $ commit -a -m 'add bar'
133 133 $ git checkout -b Baz HEAD~ >/dev/null 2>/dev/null
134 134 $ echo baz > baz
135 135 $ git add baz
136 136 $ commit -a -m 'add baz'
137 137 $ git checkout master >/dev/null 2>/dev/null
138 138 $ git pull --no-commit . Bar Baz > /dev/null 2>/dev/null
139 139 $ commit -m 'Octopus merge'
140 140 $ echo bar >> bar
141 141 $ commit -a -m 'change bar'
142 142 $ git checkout -b Foo HEAD~ >/dev/null 2>/dev/null
143 143 $ echo >> foo
144 144 $ commit -a -m 'change foo'
145 145 $ git checkout master >/dev/null 2>/dev/null
146 146 $ git pull --no-commit -s ours . Foo > /dev/null 2>/dev/null
147 147 $ commit -m 'Discard change to foo'
148 148 $ cd ..
149 149 $ glog()
150 150 > {
151 151 > hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
152 152 > }
153 153 $ splitrepo()
154 154 > {
155 155 > msg="$1"
156 156 > files="$2"
157 157 > opts=$3
158 158 > echo "% $files: $msg"
159 159 > prefix=`echo "$files" | sed -e 's/ /-/g'`
160 160 > fmap="$prefix.fmap"
161 161 > repo="$prefix.repo"
162 162 > for i in $files; do
163 163 > echo "include $i" >> "$fmap"
164 164 > done
165 165 > hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
166 166 > hg up -q -R "$repo"
167 167 > glog -R "$repo"
168 168 > hg -R "$repo" manifest --debug
169 169 > }
170 170
171 171 full conversion
172 172
173 173 $ hg convert --datesort git-repo2 fullrepo \
174 174 > --config extensions.progress= --config progress.assume-tty=1 \
175 175 > --config progress.delay=0 --config progress.changedelay=0 \
176 176 > --config progress.refresh=0 --config progress.width=60
177 177 \r (no-eol) (esc)
178 178 scanning [===> ] 1/9\r (no-eol) (esc)
179 179 scanning [========> ] 2/9\r (no-eol) (esc)
180 180 scanning [=============> ] 3/9\r (no-eol) (esc)
181 181 scanning [==================> ] 4/9\r (no-eol) (esc)
182 182 scanning [=======================> ] 5/9\r (no-eol) (esc)
183 183 scanning [============================> ] 6/9\r (no-eol) (esc)
184 184 scanning [=================================> ] 7/9\r (no-eol) (esc)
185 185 scanning [======================================> ] 8/9\r (no-eol) (esc)
186 186 scanning [===========================================>] 9/9\r (no-eol) (esc)
187 187 \r (no-eol) (esc)
188 188 \r (no-eol) (esc)
189 189 converting [ ] 0/9\r (no-eol) (esc)
190 190 getting files [======================================>] 1/1\r (no-eol) (esc)
191 191 \r (no-eol) (esc)
192 192 \r (no-eol) (esc)
193 193 converting [===> ] 1/9\r (no-eol) (esc)
194 194 getting files [======================================>] 1/1\r (no-eol) (esc)
195 195 \r (no-eol) (esc)
196 196 \r (no-eol) (esc)
197 197 converting [========> ] 2/9\r (no-eol) (esc)
198 198 getting files [======================================>] 1/1\r (no-eol) (esc)
199 199 \r (no-eol) (esc)
200 200 \r (no-eol) (esc)
201 201 converting [=============> ] 3/9\r (no-eol) (esc)
202 202 getting files [======================================>] 1/1\r (no-eol) (esc)
203 203 \r (no-eol) (esc)
204 204 \r (no-eol) (esc)
205 205 converting [=================> ] 4/9\r (no-eol) (esc)
206 206 getting files [======================================>] 1/1\r (no-eol) (esc)
207 207 \r (no-eol) (esc)
208 208 \r (no-eol) (esc)
209 209 converting [======================> ] 5/9\r (no-eol) (esc)
210 210 getting files [===> ] 1/8\r (no-eol) (esc)
211 211 getting files [========> ] 2/8\r (no-eol) (esc)
212 212 getting files [=============> ] 3/8\r (no-eol) (esc)
213 213 getting files [==================> ] 4/8\r (no-eol) (esc)
214 214 getting files [=======================> ] 5/8\r (no-eol) (esc)
215 215 getting files [============================> ] 6/8\r (no-eol) (esc)
216 216 getting files [=================================> ] 7/8\r (no-eol) (esc)
217 217 getting files [======================================>] 8/8\r (no-eol) (esc)
218 218 \r (no-eol) (esc)
219 219 \r (no-eol) (esc)
220 220 converting [===========================> ] 6/9\r (no-eol) (esc)
221 221 getting files [======================================>] 1/1\r (no-eol) (esc)
222 222 \r (no-eol) (esc)
223 223 \r (no-eol) (esc)
224 224 converting [===============================> ] 7/9\r (no-eol) (esc)
225 225 getting files [======================================>] 1/1\r (no-eol) (esc)
226 226 \r (no-eol) (esc)
227 227 \r (no-eol) (esc)
228 228 converting [====================================> ] 8/9\r (no-eol) (esc)
229 229 getting files [==================> ] 1/2\r (no-eol) (esc)
230 230 getting files [======================================>] 2/2\r (no-eol) (esc)
231 231 \r (no-eol) (esc)
232 232 initializing destination fullrepo repository
233 233 scanning source...
234 234 sorting...
235 235 converting...
236 236 8 add foo
237 237 7 change foo
238 238 6 add quux
239 239 5 add bar
240 240 4 add baz
241 241 3 Octopus merge
242 242 2 change bar
243 243 1 change foo
244 244 0 Discard change to foo
245 245 updating bookmarks
246 246 $ hg up -q -R fullrepo
247 247 $ glog -R fullrepo
248 248 @ 9 "Discard change to foo" files: foo
249 249 |\
250 250 | o 8 "change foo" files: foo
251 251 | |
252 252 o | 7 "change bar" files: bar
253 253 |/
254 254 o 6 "(octopus merge fixup)" files:
255 255 |\
256 256 | o 5 "Octopus merge" files: baz
257 257 | |\
258 258 o | | 4 "add baz" files: baz
259 259 | | |
260 260 +---o 3 "add bar" files: bar
261 261 | |
262 262 o | 2 "add quux" files: quux
263 263 | |
264 264 | o 1 "change foo" files: foo
265 265 |/
266 266 o 0 "add foo" files: foo
267 267
268 268 $ hg -R fullrepo manifest --debug
269 269 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
270 270 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
271 271 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
272 272 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
273 273 $ splitrepo 'octopus merge' 'foo bar baz'
274 274 % foo bar baz: octopus merge
275 275 @ 8 "Discard change to foo" files: foo
276 276 |\
277 277 | o 7 "change foo" files: foo
278 278 | |
279 279 o | 6 "change bar" files: bar
280 280 |/
281 281 o 5 "(octopus merge fixup)" files:
282 282 |\
283 283 | o 4 "Octopus merge" files: baz
284 284 | |\
285 285 o | | 3 "add baz" files: baz
286 286 | | |
287 287 +---o 2 "add bar" files: bar
288 288 | |
289 289 | o 1 "change foo" files: foo
290 290 |/
291 291 o 0 "add foo" files: foo
292 292
293 293 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
294 294 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
295 295 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
296 296 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
297 297 % foo baz quux: only some parents of an octopus merge; "discard" a head
298 298 @ 6 "Discard change to foo" files: foo
299 299 |
300 300 o 5 "change foo" files: foo
301 301 |
302 302 o 4 "Octopus merge" files:
303 303 |\
304 304 | o 3 "add baz" files: baz
305 305 | |
306 306 | o 2 "add quux" files: quux
307 307 | |
308 308 o | 1 "change foo" files: foo
309 309 |/
310 310 o 0 "add foo" files: foo
311 311
312 312 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
313 313 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
314 314 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
315 315
316 316 test importing git renames and copies
317 317
318 318 $ cd git-repo2
319 319 $ git mv foo foo-renamed
320 320 since bar is not touched in this commit, this copy will not be detected
321 321 $ cp bar bar-copied
322 322 $ cp baz baz-copied
323 323 $ cp baz baz-copied2
324 324 $ echo baz2 >> baz
325 325 $ git add bar-copied baz-copied baz-copied2
326 326 $ commit -a -m 'rename and copy'
327 327 $ cd ..
328 328
329 329 input validation
330 330 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
331 331 abort: convert.git.similarity is not an integer ('foo')
332 332 [255]
333 333 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
334 334 abort: similarity must be between 0 and 100
335 335 [255]
336 336 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
337 337 abort: similarity must be between 0 and 100
338 338 [255]
339 339
340 340 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
341 341 $ hg -R fullrepo status -C --change master
342 342 M baz
343 343 A bar-copied
344 344 A baz-copied
345 345 baz
346 346 A baz-copied2
347 347 baz
348 348 A foo-renamed
349 349 foo
350 350 R foo
351 351
352 352 $ cd git-repo2
353 353 $ echo bar2 >> bar
354 354 $ commit -a -m 'change bar'
355 355 $ cp bar bar-copied2
356 356 $ git add bar-copied2
357 357 $ commit -a -m 'copy with no changes'
358 358 $ cd ..
359 359
360 360 $ hg -q convert --config convert.git.similarity=100 \
361 361 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
362 362 $ hg -R fullrepo status -C --change master
363 363 A bar-copied2
364 364 bar
365 365
366 366 test binary conversion (issue1359)
367 367
368 368 $ count=19
369 369 $ mkdir git-repo3
370 370 $ cd git-repo3
371 371 $ git init-db >/dev/null 2>/dev/null
372 372 $ $PYTHON -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
373 373 $ git add b
374 374 $ commit -a -m addbinary
375 375 $ cd ..
376 376
377 377 convert binary file
378 378
379 379 $ hg convert git-repo3 git-repo3-hg
380 380 initializing destination git-repo3-hg repository
381 381 scanning source...
382 382 sorting...
383 383 converting...
384 384 0 addbinary
385 385 updating bookmarks
386 386 $ cd git-repo3-hg
387 387 $ hg up -C
388 388 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
389 389 $ $PYTHON -c 'print len(file("b", "rb").read())'
390 390 4096
391 391 $ cd ..
392 392
393 393 test author vs committer
394 394
395 395 $ mkdir git-repo4
396 396 $ cd git-repo4
397 397 $ git init-db >/dev/null 2>/dev/null
398 398 $ echo >> foo
399 399 $ git add foo
400 400 $ commit -a -m addfoo
401 401 $ echo >> foo
402 402 $ GIT_AUTHOR_NAME="nottest"
403 403 $ commit -a -m addfoo2
404 404 $ cd ..
405 405
406 406 convert author committer
407 407
408 408 $ hg convert git-repo4 git-repo4-hg
409 409 initializing destination git-repo4-hg repository
410 410 scanning source...
411 411 sorting...
412 412 converting...
413 413 1 addfoo
414 414 0 addfoo2
415 415 updating bookmarks
416 416 $ hg -R git-repo4-hg log -v
417 417 changeset: 1:d63e967f93da
418 418 bookmark: master
419 419 tag: tip
420 420 user: nottest <test@example.org>
421 421 date: Mon Jan 01 00:00:21 2007 +0000
422 422 files: foo
423 423 description:
424 424 addfoo2
425 425
426 426 committer: test <test@example.org>
427 427
428 428
429 429 changeset: 0:0735477b0224
430 430 user: test <test@example.org>
431 431 date: Mon Jan 01 00:00:20 2007 +0000
432 432 files: foo
433 433 description:
434 434 addfoo
435 435
436 436
437 437
438 438 --sourceorder should fail
439 439
440 440 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
441 441 initializing destination git-repo4-sourcesort-hg repository
442 442 abort: --sourcesort is not supported by this data source
443 443 [255]
444 444
445 445 test converting certain branches
446 446
447 447 $ mkdir git-testrevs
448 448 $ cd git-testrevs
449 449 $ git init
450 450 Initialized empty Git repository in $TESTTMP/git-testrevs/.git/
451 451 $ echo a >> a ; git add a > /dev/null; git commit -m 'first' > /dev/null
452 452 $ echo a >> a ; git add a > /dev/null; git commit -m 'master commit' > /dev/null
453 453 $ git checkout -b goodbranch 'HEAD^'
454 454 Switched to a new branch 'goodbranch'
455 455 $ echo a >> b ; git add b > /dev/null; git commit -m 'good branch commit' > /dev/null
456 456 $ git checkout -b badbranch 'HEAD^'
457 457 Switched to a new branch 'badbranch'
458 458 $ echo a >> c ; git add c > /dev/null; git commit -m 'bad branch commit' > /dev/null
459 459 $ cd ..
460 460 $ hg convert git-testrevs hg-testrevs --rev master --rev goodbranch
461 461 initializing destination hg-testrevs repository
462 462 scanning source...
463 463 sorting...
464 464 converting...
465 465 2 first
466 466 1 good branch commit
467 467 0 master commit
468 468 updating bookmarks
469 469 $ cd hg-testrevs
470 470 $ hg log -G -T '{rev} {bookmarks}'
471 471 o 2 master
472 472 |
473 473 | o 1 goodbranch
474 474 |/
475 475 o 0
476 476
477 477 $ cd ..
478 478
479 479 test sub modules
480 480
481 481 $ mkdir git-repo5
482 482 $ cd git-repo5
483 483 $ git init-db >/dev/null 2>/dev/null
484 484 $ echo 'sub' >> foo
485 485 $ git add foo
486 486 $ commit -a -m 'addfoo'
487 487 $ BASE=`pwd`
488 488 $ cd ..
489 489 $ mkdir git-repo6
490 490 $ cd git-repo6
491 491 $ git init-db >/dev/null 2>/dev/null
492 492 $ git submodule add ${BASE} >/dev/null 2>/dev/null
493 493 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
494 494
495 495 test non-tab whitespace .gitmodules
496 496
497 497 $ cat >> .gitmodules <<EOF
498 498 > [submodule "git-repo5"]
499 499 > path = git-repo5
500 500 > url = git-repo5
501 501 > EOF
502 502 $ git commit -q -a -m "weird white space submodule"
503 503 $ cd ..
504 504 $ hg convert git-repo6 hg-repo6
505 505 initializing destination hg-repo6 repository
506 506 scanning source...
507 507 sorting...
508 508 converting...
509 509 1 addsubmodule
510 510 0 weird white space submodule
511 511 updating bookmarks
512 512
513 513 $ rm -rf hg-repo6
514 514 $ cd git-repo6
515 515 $ git reset --hard 'HEAD^' > /dev/null
516 516
517 517 test missing .gitmodules
518 518
519 519 $ git submodule add ../git-repo4 >/dev/null 2>/dev/null
520 520 $ git checkout HEAD .gitmodules
521 521 $ git rm .gitmodules
522 522 rm '.gitmodules'
523 523 $ git commit -q -m "remove .gitmodules" .gitmodules
524 524 $ git commit -q -m "missing .gitmodules"
525 525 $ cd ..
526 526 $ hg convert git-repo6 hg-repo6 --traceback
527 527 fatal: Path '.gitmodules' does not exist in '*' (glob)
528 528 initializing destination hg-repo6 repository
529 529 scanning source...
530 530 sorting...
531 531 converting...
532 532 2 addsubmodule
533 533 1 remove .gitmodules
534 534 0 missing .gitmodules
535 535 warning: cannot read submodules config file in * (glob)
536 536 updating bookmarks
537 537 $ rm -rf hg-repo6
538 538 $ cd git-repo6
539 539 $ rm -rf git-repo4
540 540 $ git reset --hard 'HEAD^^' > /dev/null
541 541 $ cd ..
542 542
543 543 test invalid splicemap1
544 544
545 545 $ cat > splicemap <<EOF
546 546 > $VALIDID1
547 547 > EOF
548 548 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
549 549 initializing destination git-repo2-splicemap1-hg repository
550 550 abort: syntax error in splicemap(1): child parent1[,parent2] expected
551 551 [255]
552 552
553 553 test invalid splicemap2
554 554
555 555 $ cat > splicemap <<EOF
556 556 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
557 557 > EOF
558 558 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
559 559 initializing destination git-repo2-splicemap2-hg repository
560 560 abort: syntax error in splicemap(1): child parent1[,parent2] expected
561 561 [255]
562 562
563 563 test invalid splicemap3
564 564
565 565 $ cat > splicemap <<EOF
566 566 > $INVALIDID1 $INVALIDID2
567 567 > EOF
568 568 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
569 569 initializing destination git-repo2-splicemap3-hg repository
570 570 abort: splicemap entry afd12345af is not a valid revision identifier
571 571 [255]
572 572
573 573 convert sub modules
574 574 $ hg convert git-repo6 git-repo6-hg
575 575 initializing destination git-repo6-hg repository
576 576 scanning source...
577 577 sorting...
578 578 converting...
579 579 0 addsubmodule
580 580 updating bookmarks
581 581 $ hg -R git-repo6-hg log -v
582 582 changeset: 0:* (glob)
583 583 bookmark: master
584 584 tag: tip
585 585 user: nottest <test@example.org>
586 586 date: Mon Jan 01 00:00:23 2007 +0000
587 587 files: .hgsub .hgsubstate
588 588 description:
589 589 addsubmodule
590 590
591 591 committer: test <test@example.org>
592 592
593 593
594 594
595 595 $ cd git-repo6-hg
596 596 $ hg up >/dev/null 2>/dev/null
597 597 $ cat .hgsubstate
598 598 * git-repo5 (glob)
599 599 $ cd git-repo5
600 600 $ cat foo
601 601 sub
602 602
603 603 $ cd ../..
604 604
605 605 make sure rename detection doesn't break removing and adding gitmodules
606 606
607 607 $ cd git-repo6
608 608 $ git mv .gitmodules .gitmodules-renamed
609 609 $ commit -a -m 'rename .gitmodules'
610 610 $ git mv .gitmodules-renamed .gitmodules
611 611 $ commit -a -m 'rename .gitmodules back'
612 612 $ cd ..
613 613
614 614 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
615 615 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
616 616 rename .gitmodules
617 617 $ hg -R git-repo6-hg status -C --change 'tip^'
618 618 A .gitmodules-renamed
619 619 R .hgsub
620 620 R .hgsubstate
621 621 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
622 622 rename .gitmodules back
623 623 $ hg -R git-repo6-hg status -C --change tip
624 624 A .hgsub
625 625 A .hgsubstate
626 626 R .gitmodules-renamed
627 627
628 628 convert the revision removing '.gitmodules' itself (and related
629 629 submodules)
630 630
631 631 $ cd git-repo6
632 632 $ git rm .gitmodules
633 633 rm '.gitmodules'
634 634 $ git rm --cached git-repo5
635 635 rm 'git-repo5'
636 636 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
637 637 $ cd ..
638 638
639 639 $ hg convert -q git-repo6 git-repo6-hg
640 640 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
641 641 remove .gitmodules and submodule git-repo5
642 642 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
643 643 .hgsub .hgsubstate
644 644
645 645 convert using a different remote prefix
646 646 $ git init git-repo7
647 647 Initialized empty Git repository in $TESTTMP/git-repo7/.git/
648 648 $ cd git-repo7
649 649 $ touch a && git add a && git commit -am "commit a"
650 650 [master (root-commit) 8ae5f69] commit a
651 651 Author: nottest <test@example.org>
652 652 1 file changed, 0 insertions(+), 0 deletions(-)
653 653 create mode 100644 a
654 654 $ cd ..
655 655 $ git clone git-repo7 git-repo7-client
656 656 Cloning into 'git-repo7-client'...
657 657 done.
658 658 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
659 659 initializing destination hg-repo7 repository
660 660 scanning source...
661 661 sorting...
662 662 converting...
663 663 0 commit a
664 664 updating bookmarks
665 665 $ hg -R hg-repo7 bookmarks
666 666 master 0:03bf38caa4c6
667 667 origin/master 0:03bf38caa4c6
668 668
669 Run convert when the remote branches have changed
670 (there was an old bug where the local convert read branches from the server)
671
672 $ cd git-repo7
673 $ echo a >> a
674 $ git commit -am "move master forward"
675 [master 0c81947] move master forward
676 Author: nottest <test@example.org>
677 1 file changed, 1 insertion(+)
678 $ cd ..
679 $ rm -rf hg-repo7
680 $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7
681 initializing destination hg-repo7 repository
682 scanning source...
683 sorting...
684 converting...
685 0 commit a
686 updating bookmarks
687 $ hg -R hg-repo7 bookmarks
688 master 0:03bf38caa4c6
689 origin/master 0:03bf38caa4c6
690
669 691 damaged git repository tests:
670 692 In case the hard-coded hashes change, the following commands can be used to
671 693 list the hashes and their corresponding types in the repository:
672 694 cd git-repo4/.git/objects
673 695 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
674 696 cd ../../..
675 697
676 698 damage git repository by renaming a commit object
677 699 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
678 700 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
679 701 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
680 702 abort: cannot read tags from git-repo4/.git
681 703 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
682 704 damage git repository by renaming a blob object
683 705
684 706 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
685 707 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
686 708 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
687 709 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
688 710 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
689 711 damage git repository by renaming a tree object
690 712
691 713 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
692 714 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
693 715 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
694 716 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
General Comments 0
You need to be logged in to leave comments. Login now