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 |
7 |
8 import os |
8 import os |
9 from mercurial import util |
9 from mercurial import util, config |
10 from mercurial.node import hex, nullid |
10 from mercurial.node import hex, nullid |
11 from mercurial.i18n import _ |
11 from mercurial.i18n import _ |
12 |
12 |
13 from common import NoRepo, commit, converter_source, checktool |
13 from common import NoRepo, commit, converter_source, checktool |
|
14 |
|
15 class submodule(object): |
|
16 def __init__(self, path, node, url): |
|
17 self.path = path |
|
18 self.node = node |
|
19 self.url = url |
|
20 |
|
21 def hgsub(self): |
|
22 return "%s = [git]%s" % (self.path, self.url) |
|
23 |
|
24 def hgsubstate(self): |
|
25 return "%s %s" % (self.node, self.path) |
14 |
26 |
15 class convert_git(converter_source): |
27 class convert_git(converter_source): |
16 # Windows does not support GIT_DIR= construct while other systems |
28 # Windows does not support GIT_DIR= construct while other systems |
17 # cannot remove environment variable. Just assume none have |
29 # cannot remove environment variable. Just assume none have |
18 # both issues. |
30 # both issues. |
53 raise NoRepo(_("%s does not look like a Git repository") % path) |
65 raise NoRepo(_("%s does not look like a Git repository") % path) |
54 |
66 |
55 checktool('git', 'git') |
67 checktool('git', 'git') |
56 |
68 |
57 self.path = path |
69 self.path = path |
|
70 self.submodules = [] |
58 |
71 |
59 def getheads(self): |
72 def getheads(self): |
60 if not self.rev: |
73 if not self.rev: |
61 heads, ret = self.gitread('git rev-parse --branches --remotes') |
74 heads, ret = self.gitread('git rev-parse --branches --remotes') |
62 heads = heads.splitlines() |
75 heads = heads.splitlines() |
74 if ret: |
87 if ret: |
75 raise util.Abort(_('cannot read %r object at %s') % (type, rev)) |
88 raise util.Abort(_('cannot read %r object at %s') % (type, rev)) |
76 return data |
89 return data |
77 |
90 |
78 def getfile(self, name, rev): |
91 def getfile(self, name, rev): |
79 data = self.catfile(rev, "blob") |
92 if name == '.hgsub': |
80 mode = self.modecache[(name, rev)] |
93 data = '\n'.join([m.hgsub() for m in self.submoditer()]) |
|
94 mode = '' |
|
95 elif name == '.hgsubstate': |
|
96 data = '\n'.join([m.hgsubstate() for m in self.submoditer()]) |
|
97 mode = '' |
|
98 else: |
|
99 data = self.catfile(rev, "blob") |
|
100 mode = self.modecache[(name, rev)] |
81 return data, mode |
101 return data, mode |
|
102 |
|
103 def submoditer(self): |
|
104 null = hex(nullid) |
|
105 for m in sorted(self.submodules, key=lambda p: p.path): |
|
106 if m.node != null: |
|
107 yield m |
|
108 |
|
109 def parsegitmodules(self, content): |
|
110 """Parse the formatted .gitmodules file, example file format: |
|
111 [submodule "sub"]\n |
|
112 \tpath = sub\n |
|
113 \turl = git://giturl\n |
|
114 """ |
|
115 self.submodules = [] |
|
116 c = config.config() |
|
117 # Each item in .gitmodules starts with \t that cant be parsed |
|
118 c.parse('.gitmodules', content.replace('\t','')) |
|
119 for sec in c.sections(): |
|
120 s = c[sec] |
|
121 if 'url' in s and 'path' in s: |
|
122 self.submodules.append(submodule(s['path'], '', s['url'])) |
|
123 |
|
124 def retrievegitmodules(self, version): |
|
125 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules')) |
|
126 if ret: |
|
127 raise util.Abort(_('cannot read submodules config file in %s') % version) |
|
128 self.parsegitmodules(modules) |
|
129 for m in self.submodules: |
|
130 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path)) |
|
131 if ret: |
|
132 continue |
|
133 m.node = node.strip() |
82 |
134 |
83 def getchanges(self, version): |
135 def getchanges(self, version): |
84 self.modecache = {} |
136 self.modecache = {} |
85 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version) |
137 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version) |
86 changes = [] |
138 changes = [] |
87 seen = set() |
139 seen = set() |
88 entry = None |
140 entry = None |
|
141 subexists = False |
89 for l in fh.read().split('\x00'): |
142 for l in fh.read().split('\x00'): |
90 if not entry: |
143 if not entry: |
91 if not l.startswith(':'): |
144 if not l.startswith(':'): |
92 continue |
145 continue |
93 entry = l |
146 entry = l |
95 f = l |
148 f = l |
96 if f not in seen: |
149 if f not in seen: |
97 seen.add(f) |
150 seen.add(f) |
98 entry = entry.split() |
151 entry = entry.split() |
99 h = entry[3] |
152 h = entry[3] |
100 if entry[1] == '160000': |
|
101 raise util.Abort('git submodules are not supported!') |
|
102 p = (entry[1] == "100755") |
153 p = (entry[1] == "100755") |
103 s = (entry[1] == "120000") |
154 s = (entry[1] == "120000") |
104 self.modecache[(f, h)] = (p and "x") or (s and "l") or "" |
155 |
105 changes.append((f, h)) |
156 if f == '.gitmodules': |
|
157 subexists = True |
|
158 changes.append(('.hgsub', '')) |
|
159 elif entry[1] == '160000' or entry[0] == ':160000': |
|
160 subexists = True |
|
161 else: |
|
162 self.modecache[(f, h)] = (p and "x") or (s and "l") or "" |
|
163 changes.append((f, h)) |
106 entry = None |
164 entry = None |
107 if fh.close(): |
165 if fh.close(): |
108 raise util.Abort(_('cannot read changes in %s') % version) |
166 raise util.Abort(_('cannot read changes in %s') % version) |
|
167 |
|
168 if subexists: |
|
169 self.retrievegitmodules(version) |
|
170 changes.append(('.hgsubstate', '')) |
109 return (changes, {}) |
171 return (changes, {}) |
110 |
172 |
111 def getcommit(self, version): |
173 def getcommit(self, version): |
112 c = self.catfile(version, "commit") # read the commit hash |
174 c = self.catfile(version, "commit") # read the commit hash |
113 end = c.find("\n\n") |
175 end = c.find("\n\n") |