##// END OF EJS Templates
convert: simplify git.similarity parsing
Siddharth Agarwal -
r22511:b1ec65b3 default
parent child Browse files
Show More
@@ -1,387 +1,384 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
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, rev=None):
90 90 super(convert_git, self).__init__(ui, path, rev=rev)
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 try:
98 similarity = int(ui.config('convert', 'git.similarity') or 0)
99 except ValueError:
100 raise util.Abort('convert.git.similarity must be a number')
97 similarity = ui.configint('convert', 'git.similarity', default=0)
101 98 if similarity < 0 or similarity > 100:
102 99 raise util.Abort(_('similarity must be between 0 and 100'))
103 100 if similarity > 0:
104 101 self.simopt = '--find-copies=%d%%' % similarity
105 102 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
106 103 False)
107 104 if findcopiesharder:
108 105 self.simopt += ' --find-copies-harder'
109 106 else:
110 107 self.simopt = ''
111 108
112 109 checktool('git', 'git')
113 110
114 111 self.path = path
115 112 self.submodules = []
116 113
117 114 self.catfilepipe = self.gitpipe('git cat-file --batch')
118 115
119 116 def after(self):
120 117 for f in self.catfilepipe:
121 118 f.close()
122 119
123 120 def getheads(self):
124 121 if not self.rev:
125 122 heads, ret = self.gitread('git rev-parse --branches --remotes')
126 123 heads = heads.splitlines()
127 124 else:
128 125 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
129 126 heads = [heads[:-1]]
130 127 if ret:
131 128 raise util.Abort(_('cannot retrieve git heads'))
132 129 return heads
133 130
134 131 def catfile(self, rev, type):
135 132 if rev == hex(nullid):
136 133 raise IOError
137 134 self.catfilepipe[0].write(rev+'\n')
138 135 self.catfilepipe[0].flush()
139 136 info = self.catfilepipe[1].readline().split()
140 137 if info[1] != type:
141 138 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
142 139 size = int(info[2])
143 140 data = self.catfilepipe[1].read(size)
144 141 if len(data) < size:
145 142 raise util.Abort(_('cannot read %r object at %s: unexpected size')
146 143 % (type, rev))
147 144 # read the trailing newline
148 145 self.catfilepipe[1].read(1)
149 146 return data
150 147
151 148 def getfile(self, name, rev):
152 149 if rev == hex(nullid):
153 150 return None, None
154 151 if name == '.hgsub':
155 152 data = '\n'.join([m.hgsub() for m in self.submoditer()])
156 153 mode = ''
157 154 elif name == '.hgsubstate':
158 155 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
159 156 mode = ''
160 157 else:
161 158 data = self.catfile(rev, "blob")
162 159 mode = self.modecache[(name, rev)]
163 160 return data, mode
164 161
165 162 def submoditer(self):
166 163 null = hex(nullid)
167 164 for m in sorted(self.submodules, key=lambda p: p.path):
168 165 if m.node != null:
169 166 yield m
170 167
171 168 def parsegitmodules(self, content):
172 169 """Parse the formatted .gitmodules file, example file format:
173 170 [submodule "sub"]\n
174 171 \tpath = sub\n
175 172 \turl = git://giturl\n
176 173 """
177 174 self.submodules = []
178 175 c = config.config()
179 176 # Each item in .gitmodules starts with \t that cant be parsed
180 177 c.parse('.gitmodules', content.replace('\t',''))
181 178 for sec in c.sections():
182 179 s = c[sec]
183 180 if 'url' in s and 'path' in s:
184 181 self.submodules.append(submodule(s['path'], '', s['url']))
185 182
186 183 def retrievegitmodules(self, version):
187 184 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
188 185 if ret:
189 186 raise util.Abort(_('cannot read submodules config file in %s') %
190 187 version)
191 188 self.parsegitmodules(modules)
192 189 for m in self.submodules:
193 190 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
194 191 if ret:
195 192 continue
196 193 m.node = node.strip()
197 194
198 195 def getchanges(self, version, full):
199 196 if full:
200 197 raise util.Abort(_("convert from git do not support --full"))
201 198 self.modecache = {}
202 199 fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
203 200 self.simopt, version))
204 201 changes = []
205 202 copies = {}
206 203 seen = set()
207 204 entry = None
208 205 subexists = [False]
209 206 subdeleted = [False]
210 207 difftree = fh.read().split('\x00')
211 208 lcount = len(difftree)
212 209 i = 0
213 210
214 211 def add(entry, f, isdest):
215 212 seen.add(f)
216 213 h = entry[3]
217 214 p = (entry[1] == "100755")
218 215 s = (entry[1] == "120000")
219 216 renamesource = (not isdest and entry[4][0] == 'R')
220 217
221 218 if f == '.gitmodules':
222 219 subexists[0] = True
223 220 if entry[4] == 'D' or renamesource:
224 221 subdeleted[0] = True
225 222 changes.append(('.hgsub', hex(nullid)))
226 223 else:
227 224 changes.append(('.hgsub', ''))
228 225 elif entry[1] == '160000' or entry[0] == ':160000':
229 226 subexists[0] = True
230 227 else:
231 228 if renamesource:
232 229 h = hex(nullid)
233 230 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
234 231 changes.append((f, h))
235 232
236 233 while i < lcount:
237 234 l = difftree[i]
238 235 i += 1
239 236 if not entry:
240 237 if not l.startswith(':'):
241 238 continue
242 239 entry = l.split()
243 240 continue
244 241 f = l
245 242 if f not in seen:
246 243 add(entry, f, False)
247 244 # A file can be copied multiple times, or modified and copied
248 245 # simultaneously. So f can be repeated even if fdest isn't.
249 246 if entry[4][0] in 'RC':
250 247 # rename or copy: next line is the destination
251 248 fdest = difftree[i]
252 249 i += 1
253 250 if fdest not in seen:
254 251 add(entry, fdest, True)
255 252 # .gitmodules isn't imported at all, so it being copied to
256 253 # and fro doesn't really make sense
257 254 if f != '.gitmodules' and fdest != '.gitmodules':
258 255 copies[fdest] = f
259 256 entry = None
260 257 if fh.close():
261 258 raise util.Abort(_('cannot read changes in %s') % version)
262 259
263 260 if subexists[0]:
264 261 if subdeleted[0]:
265 262 changes.append(('.hgsubstate', hex(nullid)))
266 263 else:
267 264 self.retrievegitmodules(version)
268 265 changes.append(('.hgsubstate', ''))
269 266 return (changes, copies)
270 267
271 268 def getcommit(self, version):
272 269 c = self.catfile(version, "commit") # read the commit hash
273 270 end = c.find("\n\n")
274 271 message = c[end + 2:]
275 272 message = self.recode(message)
276 273 l = c[:end].splitlines()
277 274 parents = []
278 275 author = committer = None
279 276 for e in l[1:]:
280 277 n, v = e.split(" ", 1)
281 278 if n == "author":
282 279 p = v.split()
283 280 tm, tz = p[-2:]
284 281 author = " ".join(p[:-2])
285 282 if author[0] == "<": author = author[1:-1]
286 283 author = self.recode(author)
287 284 if n == "committer":
288 285 p = v.split()
289 286 tm, tz = p[-2:]
290 287 committer = " ".join(p[:-2])
291 288 if committer[0] == "<": committer = committer[1:-1]
292 289 committer = self.recode(committer)
293 290 if n == "parent":
294 291 parents.append(v)
295 292
296 293 if committer and committer != author:
297 294 message += "\ncommitter: %s\n" % committer
298 295 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
299 296 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
300 297 date = tm + " " + str(tz)
301 298
302 299 c = commit(parents=parents, date=date, author=author, desc=message,
303 300 rev=version)
304 301 return c
305 302
306 303 def numcommits(self):
307 304 return len([None for _ in self.gitopen('git rev-list --all')])
308 305
309 306 def gettags(self):
310 307 tags = {}
311 308 alltags = {}
312 309 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
313 310 err=subprocess.STDOUT)
314 311 prefix = 'refs/tags/'
315 312
316 313 # Build complete list of tags, both annotated and bare ones
317 314 for line in fh:
318 315 line = line.strip()
319 316 if line.startswith("error:") or line.startswith("fatal:"):
320 317 raise util.Abort(_('cannot read tags from %s') % self.path)
321 318 node, tag = line.split(None, 1)
322 319 if not tag.startswith(prefix):
323 320 continue
324 321 alltags[tag[len(prefix):]] = node
325 322 if fh.close():
326 323 raise util.Abort(_('cannot read tags from %s') % self.path)
327 324
328 325 # Filter out tag objects for annotated tag refs
329 326 for tag in alltags:
330 327 if tag.endswith('^{}'):
331 328 tags[tag[:-3]] = alltags[tag]
332 329 else:
333 330 if tag + '^{}' in alltags:
334 331 continue
335 332 else:
336 333 tags[tag] = alltags[tag]
337 334
338 335 return tags
339 336
340 337 def getchangedfiles(self, version, i):
341 338 changes = []
342 339 if i is None:
343 340 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
344 341 for l in fh:
345 342 if "\t" not in l:
346 343 continue
347 344 m, f = l[:-1].split("\t")
348 345 changes.append(f)
349 346 else:
350 347 fh = self.gitopen('git diff-tree --name-only --root -r %s '
351 348 '"%s^%s" --' % (version, version, i + 1))
352 349 changes = [f.rstrip('\n') for f in fh]
353 350 if fh.close():
354 351 raise util.Abort(_('cannot read changes in %s') % version)
355 352
356 353 return changes
357 354
358 355 def getbookmarks(self):
359 356 bookmarks = {}
360 357
361 358 # Interesting references in git are prefixed
362 359 prefix = 'refs/heads/'
363 360 prefixlen = len(prefix)
364 361
365 362 # factor two commands
366 363 gitcmd = { 'remote/': 'git ls-remote --heads origin',
367 364 '': 'git show-ref'}
368 365
369 366 # Origin heads
370 367 for reftype in gitcmd:
371 368 try:
372 369 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
373 370 for line in fh:
374 371 line = line.strip()
375 372 rev, name = line.split(None, 1)
376 373 if not name.startswith(prefix):
377 374 continue
378 375 name = '%s%s' % (reftype, name[prefixlen:])
379 376 bookmarks[name] = rev
380 377 except Exception:
381 378 pass
382 379
383 380 return bookmarks
384 381
385 382 def checkrevformat(self, revstr, mapname='splicemap'):
386 383 """ git revision string is a 40 byte hex """
387 384 self.checkhexformat(revstr, mapname)
@@ -1,515 +1,515 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 -q convert --datesort git-repo2 fullrepo
174 174 $ hg up -q -R fullrepo
175 175 $ glog -R fullrepo
176 176 @ 9 "Discard change to foo" files: foo
177 177 |\
178 178 | o 8 "change foo" files: foo
179 179 | |
180 180 o | 7 "change bar" files: bar
181 181 |/
182 182 o 6 "(octopus merge fixup)" files:
183 183 |\
184 184 | o 5 "Octopus merge" files: baz
185 185 | |\
186 186 o | | 4 "add baz" files: baz
187 187 | | |
188 188 +---o 3 "add bar" files: bar
189 189 | |
190 190 o | 2 "add quux" files: quux
191 191 | |
192 192 | o 1 "change foo" files: foo
193 193 |/
194 194 o 0 "add foo" files: foo
195 195
196 196 $ hg -R fullrepo manifest --debug
197 197 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
198 198 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
199 199 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
200 200 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
201 201 $ splitrepo 'octopus merge' 'foo bar baz'
202 202 % foo bar baz: octopus merge
203 203 @ 8 "Discard change to foo" files: foo
204 204 |\
205 205 | o 7 "change foo" files: foo
206 206 | |
207 207 o | 6 "change bar" files: bar
208 208 |/
209 209 o 5 "(octopus merge fixup)" files:
210 210 |\
211 211 | o 4 "Octopus merge" files: baz
212 212 | |\
213 213 o | | 3 "add baz" files: baz
214 214 | | |
215 215 +---o 2 "add bar" files: bar
216 216 | |
217 217 | o 1 "change foo" files: foo
218 218 |/
219 219 o 0 "add foo" files: foo
220 220
221 221 245a3b8bc653999c2b22cdabd517ccb47aecafdf 644 bar
222 222 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
223 223 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
224 224 $ splitrepo 'only some parents of an octopus merge; "discard" a head' 'foo baz quux'
225 225 % foo baz quux: only some parents of an octopus merge; "discard" a head
226 226 @ 6 "Discard change to foo" files: foo
227 227 |
228 228 o 5 "change foo" files: foo
229 229 |
230 230 o 4 "Octopus merge" files:
231 231 |\
232 232 | o 3 "add baz" files: baz
233 233 | |
234 234 | o 2 "add quux" files: quux
235 235 | |
236 236 o | 1 "change foo" files: foo
237 237 |/
238 238 o 0 "add foo" files: foo
239 239
240 240 354ae8da6e890359ef49ade27b68bbc361f3ca88 644 baz
241 241 9277c9cc8dd4576fc01a17939b4351e5ada93466 644 foo
242 242 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644 quux
243 243
244 244 test importing git renames and copies
245 245
246 246 $ cd git-repo2
247 247 $ git mv foo foo-renamed
248 248 since bar is not touched in this commit, this copy will not be detected
249 249 $ cp bar bar-copied
250 250 $ cp baz baz-copied
251 251 $ cp baz baz-copied2
252 252 $ echo baz2 >> baz
253 253 $ git add bar-copied baz-copied baz-copied2
254 254 $ commit -a -m 'rename and copy'
255 255 $ cd ..
256 256
257 257 input validation
258 258 $ hg convert --config convert.git.similarity=foo --datesort git-repo2 fullrepo
259 abort: convert.git.similarity must be a number
259 abort: convert.git.similarity is not an integer ('foo')
260 260 [255]
261 261 $ hg convert --config convert.git.similarity=-1 --datesort git-repo2 fullrepo
262 262 abort: similarity must be between 0 and 100
263 263 [255]
264 264 $ hg convert --config convert.git.similarity=101 --datesort git-repo2 fullrepo
265 265 abort: similarity must be between 0 and 100
266 266 [255]
267 267
268 268 $ hg -q convert --config convert.git.similarity=100 --datesort git-repo2 fullrepo
269 269 $ hg -R fullrepo status -C --change master
270 270 M baz
271 271 A bar-copied
272 272 A baz-copied
273 273 baz
274 274 A baz-copied2
275 275 baz
276 276 A foo-renamed
277 277 foo
278 278 R foo
279 279
280 280 $ cd git-repo2
281 281 $ cp bar bar-copied2
282 282 $ git add bar-copied2
283 283 $ commit -a -m 'copy with no changes'
284 284 $ cd ..
285 285
286 286 $ hg -q convert --config convert.git.similarity=100 \
287 287 > --config convert.git.findcopiesharder=1 --datesort git-repo2 fullrepo
288 288 $ hg -R fullrepo status -C --change master
289 289 A bar-copied2
290 290 bar
291 291
292 292 test binary conversion (issue1359)
293 293
294 294 $ count=19
295 295 $ mkdir git-repo3
296 296 $ cd git-repo3
297 297 $ git init-db >/dev/null 2>/dev/null
298 298 $ python -c 'file("b", "wb").write("".join([chr(i) for i in range(256)])*16)'
299 299 $ git add b
300 300 $ commit -a -m addbinary
301 301 $ cd ..
302 302
303 303 convert binary file
304 304
305 305 $ hg convert git-repo3 git-repo3-hg
306 306 initializing destination git-repo3-hg repository
307 307 scanning source...
308 308 sorting...
309 309 converting...
310 310 0 addbinary
311 311 updating bookmarks
312 312 $ cd git-repo3-hg
313 313 $ hg up -C
314 314 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
315 315 $ python -c 'print len(file("b", "rb").read())'
316 316 4096
317 317 $ cd ..
318 318
319 319 test author vs committer
320 320
321 321 $ mkdir git-repo4
322 322 $ cd git-repo4
323 323 $ git init-db >/dev/null 2>/dev/null
324 324 $ echo >> foo
325 325 $ git add foo
326 326 $ commit -a -m addfoo
327 327 $ echo >> foo
328 328 $ GIT_AUTHOR_NAME="nottest"
329 329 $ commit -a -m addfoo2
330 330 $ cd ..
331 331
332 332 convert author committer
333 333
334 334 $ hg convert git-repo4 git-repo4-hg
335 335 initializing destination git-repo4-hg repository
336 336 scanning source...
337 337 sorting...
338 338 converting...
339 339 1 addfoo
340 340 0 addfoo2
341 341 updating bookmarks
342 342 $ hg -R git-repo4-hg log -v
343 343 changeset: 1:d63e967f93da
344 344 bookmark: master
345 345 tag: tip
346 346 user: nottest <test@example.org>
347 347 date: Mon Jan 01 00:00:21 2007 +0000
348 348 files: foo
349 349 description:
350 350 addfoo2
351 351
352 352 committer: test <test@example.org>
353 353
354 354
355 355 changeset: 0:0735477b0224
356 356 user: test <test@example.org>
357 357 date: Mon Jan 01 00:00:20 2007 +0000
358 358 files: foo
359 359 description:
360 360 addfoo
361 361
362 362
363 363
364 364 --sourceorder should fail
365 365
366 366 $ hg convert --sourcesort git-repo4 git-repo4-sourcesort-hg
367 367 initializing destination git-repo4-sourcesort-hg repository
368 368 abort: --sourcesort is not supported by this data source
369 369 [255]
370 370
371 371 test sub modules
372 372
373 373 $ mkdir git-repo5
374 374 $ cd git-repo5
375 375 $ git init-db >/dev/null 2>/dev/null
376 376 $ echo 'sub' >> foo
377 377 $ git add foo
378 378 $ commit -a -m 'addfoo'
379 379 $ BASE=`pwd`
380 380 $ cd ..
381 381 $ mkdir git-repo6
382 382 $ cd git-repo6
383 383 $ git init-db >/dev/null 2>/dev/null
384 384 $ git submodule add ${BASE} >/dev/null 2>/dev/null
385 385 $ commit -a -m 'addsubmodule' >/dev/null 2>/dev/null
386 386 $ cd ..
387 387
388 388 test invalid splicemap1
389 389
390 390 $ cat > splicemap <<EOF
391 391 > $VALIDID1
392 392 > EOF
393 393 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap1-hg
394 394 initializing destination git-repo2-splicemap1-hg repository
395 395 abort: syntax error in splicemap(1): child parent1[,parent2] expected
396 396 [255]
397 397
398 398 test invalid splicemap2
399 399
400 400 $ cat > splicemap <<EOF
401 401 > $VALIDID1 $VALIDID2, $VALIDID2, $VALIDID2
402 402 > EOF
403 403 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap2-hg
404 404 initializing destination git-repo2-splicemap2-hg repository
405 405 abort: syntax error in splicemap(1): child parent1[,parent2] expected
406 406 [255]
407 407
408 408 test invalid splicemap3
409 409
410 410 $ cat > splicemap <<EOF
411 411 > $INVALIDID1 $INVALIDID2
412 412 > EOF
413 413 $ hg convert --splicemap splicemap git-repo2 git-repo2-splicemap3-hg
414 414 initializing destination git-repo2-splicemap3-hg repository
415 415 abort: splicemap entry afd12345af is not a valid revision identifier
416 416 [255]
417 417
418 418 convert sub modules
419 419 $ hg convert git-repo6 git-repo6-hg
420 420 initializing destination git-repo6-hg repository
421 421 scanning source...
422 422 sorting...
423 423 converting...
424 424 0 addsubmodule
425 425 updating bookmarks
426 426 $ hg -R git-repo6-hg log -v
427 427 changeset: 0:* (glob)
428 428 bookmark: master
429 429 tag: tip
430 430 user: nottest <test@example.org>
431 431 date: Mon Jan 01 00:00:23 2007 +0000
432 432 files: .hgsub .hgsubstate
433 433 description:
434 434 addsubmodule
435 435
436 436 committer: test <test@example.org>
437 437
438 438
439 439
440 440 $ cd git-repo6-hg
441 441 $ hg up >/dev/null 2>/dev/null
442 442 $ cat .hgsubstate
443 443 * git-repo5 (glob)
444 444 $ cd git-repo5
445 445 $ cat foo
446 446 sub
447 447
448 448 $ cd ../..
449 449
450 450 make sure rename detection doesn't break removing and adding gitmodules
451 451
452 452 $ cd git-repo6
453 453 $ git mv .gitmodules .gitmodules-renamed
454 454 $ commit -a -m 'rename .gitmodules'
455 455 $ git mv .gitmodules-renamed .gitmodules
456 456 $ commit -a -m 'rename .gitmodules back'
457 457 $ cd ..
458 458
459 459 $ hg --config convert.git.similarity=100 convert -q git-repo6 git-repo6-hg
460 460 $ hg -R git-repo6-hg log -r 'tip^' -T "{desc|firstline}\n"
461 461 rename .gitmodules
462 462 $ hg -R git-repo6-hg status -C --change 'tip^'
463 463 A .gitmodules-renamed
464 464 R .hgsub
465 465 R .hgsubstate
466 466 $ hg -R git-repo6-hg log -r tip -T "{desc|firstline}\n"
467 467 rename .gitmodules back
468 468 $ hg -R git-repo6-hg status -C --change tip
469 469 A .hgsub
470 470 A .hgsubstate
471 471 R .gitmodules-renamed
472 472
473 473 convert the revision removing '.gitmodules' itself (and related
474 474 submodules)
475 475
476 476 $ cd git-repo6
477 477 $ git rm .gitmodules
478 478 rm '.gitmodules'
479 479 $ git rm --cached git-repo5
480 480 rm 'git-repo5'
481 481 $ commit -a -m 'remove .gitmodules and submodule git-repo5'
482 482 $ cd ..
483 483
484 484 $ hg convert -q git-repo6 git-repo6-hg
485 485 $ hg -R git-repo6-hg tip -T "{desc|firstline}\n"
486 486 remove .gitmodules and submodule git-repo5
487 487 $ hg -R git-repo6-hg tip -T "{file_dels}\n"
488 488 .hgsub .hgsubstate
489 489
490 490 damaged git repository tests:
491 491 In case the hard-coded hashes change, the following commands can be used to
492 492 list the hashes and their corresponding types in the repository:
493 493 cd git-repo4/.git/objects
494 494 find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
495 495 cd ../../..
496 496
497 497 damage git repository by renaming a commit object
498 498 $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
499 499 $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
500 500 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
501 501 abort: cannot read tags from git-repo4/.git
502 502 $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
503 503 damage git repository by renaming a blob object
504 504
505 505 $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
506 506 $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
507 507 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
508 508 abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
509 509 $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
510 510 damage git repository by renaming a tree object
511 511
512 512 $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
513 513 $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
514 514 $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
515 515 abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
General Comments 0
You need to be logged in to leave comments. Login now