##// END OF EJS Templates
convert/bzr: handle files replaced by directories (issue1623)
Patrick Mezard -
r8126:13b36eb1 default
parent child Browse files
Show More
@@ -0,0 +1,30 b''
1 #!/bin/sh
2
3 "$TESTDIR/hghave" bzr114 || exit 80
4
5 . "$TESTDIR/bzr-definitions"
6
7 # The file/directory replacement can only be reproduced on
8 # bzr >= 1.4. Merge it back in test-convert-bzr-directories once
9 # this version becomes mainstream.
10 echo % replace file with dir
11 mkdir test-replace-file-with-dir
12 cd test-replace-file-with-dir
13 bzr init -q source
14 cd source
15 echo d > d
16 bzr add -q d
17 bzr commit -q -m 'add d file'
18 rm d
19 mkdir d
20 bzr add -q d
21 bzr commit -q -m 'replace with d dir'
22 echo a > d/a
23 bzr add -q d/a
24 bzr commit -q -m 'add d/a'
25 cd ..
26 hg convert source source-hg
27 manifest source-hg tip
28 cd source-hg
29 hg update
30 cd ../..
@@ -0,0 +1,11 b''
1 % replace file with dir
2 initializing destination source-hg repository
3 scanning source...
4 sorting...
5 converting...
6 2 add d file
7 1 replace with d dir
8 0 add d/a
9 % manifest of tip
10 644 d/a
11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -1,219 +1,224 b''
1 # bzr support for the convert extension
1 # bzr support for the convert extension
2 # This module is for handling 'bzr', that was formerly known as Bazaar-NG;
2 # This module is for handling 'bzr', that was formerly known as Bazaar-NG;
3 # it cannot access 'bar' repositories, but they were never used very much
3 # it cannot access 'bar' repositories, but they were never used very much
4
4
5 import os
5 import os
6 from mercurial import demandimport
6 from mercurial import demandimport
7 # these do not work with demandimport, blacklist
7 # these do not work with demandimport, blacklist
8 demandimport.ignore.extend([
8 demandimport.ignore.extend([
9 'bzrlib.transactions',
9 'bzrlib.transactions',
10 'bzrlib.urlutils',
10 'bzrlib.urlutils',
11 ])
11 ])
12
12
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial import util
14 from mercurial import util
15 from common import NoRepo, commit, converter_source
15 from common import NoRepo, commit, converter_source
16
16
17 try:
17 try:
18 # bazaar imports
18 # bazaar imports
19 from bzrlib import branch, revision, errors
19 from bzrlib import branch, revision, errors
20 from bzrlib.revisionspec import RevisionSpec
20 from bzrlib.revisionspec import RevisionSpec
21 except ImportError:
21 except ImportError:
22 pass
22 pass
23
23
24 supportedkinds = ('file', 'symlink')
24 supportedkinds = ('file', 'symlink')
25
25
26 class bzr_source(converter_source):
26 class bzr_source(converter_source):
27 """Reads Bazaar repositories by using the Bazaar Python libraries"""
27 """Reads Bazaar repositories by using the Bazaar Python libraries"""
28
28
29 def __init__(self, ui, path, rev=None):
29 def __init__(self, ui, path, rev=None):
30 super(bzr_source, self).__init__(ui, path, rev=rev)
30 super(bzr_source, self).__init__(ui, path, rev=rev)
31
31
32 try:
32 try:
33 # access bzrlib stuff
33 # access bzrlib stuff
34 branch
34 branch
35 except NameError:
35 except NameError:
36 raise NoRepo('Bazaar modules could not be loaded')
36 raise NoRepo('Bazaar modules could not be loaded')
37
37
38 if not os.path.exists(os.path.join(path, '.bzr')):
38 if not os.path.exists(os.path.join(path, '.bzr')):
39 raise NoRepo('%s does not look like a Bazaar repo' % path)
39 raise NoRepo('%s does not look like a Bazaar repo' % path)
40
40
41 path = os.path.abspath(path)
41 path = os.path.abspath(path)
42 self.branch = branch.Branch.open(path)
42 self.branch = branch.Branch.open(path)
43 self.sourcerepo = self.branch.repository
43 self.sourcerepo = self.branch.repository
44 self._parentids = {}
44 self._parentids = {}
45
45
46 def before(self):
46 def before(self):
47 """Before the conversion begins, acquire a read lock
47 """Before the conversion begins, acquire a read lock
48 for all the operations that might need it. Fortunately
48 for all the operations that might need it. Fortunately
49 read locks don't block other reads or writes to the
49 read locks don't block other reads or writes to the
50 repository, so this shouldn't have any impact on the usage of
50 repository, so this shouldn't have any impact on the usage of
51 the source repository.
51 the source repository.
52
52
53 The alternative would be locking on every operation that
53 The alternative would be locking on every operation that
54 needs locks (there are currently two: getting the file and
54 needs locks (there are currently two: getting the file and
55 getting the parent map) and releasing immediately after,
55 getting the parent map) and releasing immediately after,
56 but this approach can take even 40% longer."""
56 but this approach can take even 40% longer."""
57 self.sourcerepo.lock_read()
57 self.sourcerepo.lock_read()
58
58
59 def after(self):
59 def after(self):
60 self.sourcerepo.unlock()
60 self.sourcerepo.unlock()
61
61
62 def getheads(self):
62 def getheads(self):
63 if not self.rev:
63 if not self.rev:
64 return [self.branch.last_revision()]
64 return [self.branch.last_revision()]
65 try:
65 try:
66 r = RevisionSpec.from_string(self.rev)
66 r = RevisionSpec.from_string(self.rev)
67 info = r.in_history(self.branch)
67 info = r.in_history(self.branch)
68 except errors.BzrError:
68 except errors.BzrError:
69 raise util.Abort(_('%s is not a valid revision in current branch')
69 raise util.Abort(_('%s is not a valid revision in current branch')
70 % self.rev)
70 % self.rev)
71 return [info.rev_id]
71 return [info.rev_id]
72
72
73 def getfile(self, name, rev):
73 def getfile(self, name, rev):
74 revtree = self.sourcerepo.revision_tree(rev)
74 revtree = self.sourcerepo.revision_tree(rev)
75 fileid = revtree.path2id(name)
75 fileid = revtree.path2id(name)
76 if fileid is None or revtree.kind(fileid) not in supportedkinds:
76 if fileid is None or revtree.kind(fileid) not in supportedkinds:
77 # the file is not available anymore - was deleted
77 # the file is not available anymore - was deleted
78 raise IOError(_('%s is not available in %s anymore') %
78 raise IOError(_('%s is not available in %s anymore') %
79 (name, rev))
79 (name, rev))
80 sio = revtree.get_file(fileid)
80 sio = revtree.get_file(fileid)
81 return sio.read()
81 return sio.read()
82
82
83 def getmode(self, name, rev):
83 def getmode(self, name, rev):
84 return self._modecache[(name, rev)]
84 return self._modecache[(name, rev)]
85
85
86 def getchanges(self, version):
86 def getchanges(self, version):
87 # set up caches: modecache and revtree
87 # set up caches: modecache and revtree
88 self._modecache = {}
88 self._modecache = {}
89 self._revtree = self.sourcerepo.revision_tree(version)
89 self._revtree = self.sourcerepo.revision_tree(version)
90 # get the parentids from the cache
90 # get the parentids from the cache
91 parentids = self._parentids.pop(version)
91 parentids = self._parentids.pop(version)
92 # only diff against first parent id
92 # only diff against first parent id
93 prevtree = self.sourcerepo.revision_tree(parentids[0])
93 prevtree = self.sourcerepo.revision_tree(parentids[0])
94 return self._gettreechanges(self._revtree, prevtree)
94 return self._gettreechanges(self._revtree, prevtree)
95
95
96 def getcommit(self, version):
96 def getcommit(self, version):
97 rev = self.sourcerepo.get_revision(version)
97 rev = self.sourcerepo.get_revision(version)
98 # populate parent id cache
98 # populate parent id cache
99 if not rev.parent_ids:
99 if not rev.parent_ids:
100 parents = []
100 parents = []
101 self._parentids[version] = (revision.NULL_REVISION,)
101 self._parentids[version] = (revision.NULL_REVISION,)
102 else:
102 else:
103 parents = self._filterghosts(rev.parent_ids)
103 parents = self._filterghosts(rev.parent_ids)
104 self._parentids[version] = parents
104 self._parentids[version] = parents
105
105
106 return commit(parents=parents,
106 return commit(parents=parents,
107 # bzr uses 1 second timezone precision
107 # bzr uses 1 second timezone precision
108 date='%d %d' % (rev.timestamp, rev.timezone / 3600),
108 date='%d %d' % (rev.timestamp, rev.timezone / 3600),
109 author=self.recode(rev.committer),
109 author=self.recode(rev.committer),
110 # bzr returns bytestrings or unicode, depending on the content
110 # bzr returns bytestrings or unicode, depending on the content
111 desc=self.recode(rev.message),
111 desc=self.recode(rev.message),
112 rev=version)
112 rev=version)
113
113
114 def gettags(self):
114 def gettags(self):
115 if not self.branch.supports_tags():
115 if not self.branch.supports_tags():
116 return {}
116 return {}
117 tagdict = self.branch.tags.get_tag_dict()
117 tagdict = self.branch.tags.get_tag_dict()
118 bytetags = {}
118 bytetags = {}
119 for name, rev in tagdict.iteritems():
119 for name, rev in tagdict.iteritems():
120 bytetags[self.recode(name)] = rev
120 bytetags[self.recode(name)] = rev
121 return bytetags
121 return bytetags
122
122
123 def getchangedfiles(self, rev, i):
123 def getchangedfiles(self, rev, i):
124 self._modecache = {}
124 self._modecache = {}
125 curtree = self.sourcerepo.revision_tree(rev)
125 curtree = self.sourcerepo.revision_tree(rev)
126 parentids = self._parentids.pop(rev)
126 parentids = self._parentids.pop(rev)
127 if i is not None:
127 if i is not None:
128 parentid = parentids[i]
128 parentid = parentids[i]
129 else:
129 else:
130 # no parent id, get the empty revision
130 # no parent id, get the empty revision
131 parentid = revision.NULL_REVISION
131 parentid = revision.NULL_REVISION
132
132
133 prevtree = self.sourcerepo.revision_tree(parentid)
133 prevtree = self.sourcerepo.revision_tree(parentid)
134 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
134 changes = [e[0] for e in self._gettreechanges(curtree, prevtree)[0]]
135 return changes
135 return changes
136
136
137 def _gettreechanges(self, current, origin):
137 def _gettreechanges(self, current, origin):
138 revid = current._revision_id;
138 revid = current._revision_id;
139 changes = []
139 changes = []
140 renames = {}
140 renames = {}
141 for (fileid, paths, changed_content, versioned, parent, name,
141 for (fileid, paths, changed_content, versioned, parent, name,
142 kind, executable) in current.iter_changes(origin):
142 kind, executable) in current.iter_changes(origin):
143
143
144 if paths[0] == u'' or paths[1] == u'':
144 if paths[0] == u'' or paths[1] == u'':
145 # ignore changes to tree root
145 # ignore changes to tree root
146 continue
146 continue
147
147
148 # bazaar tracks directories, mercurial does not, so
148 # bazaar tracks directories, mercurial does not, so
149 # we have to rename the directory contents
149 # we have to rename the directory contents
150 if kind[1] == 'directory':
150 if kind[1] == 'directory':
151 if kind[0] not in (None, 'directory'):
152 # Replacing 'something' with a directory, record it
153 # so it can be removed.
154 changes.append((self.recode(paths[0]), revid))
155
151 if None not in paths and paths[0] != paths[1]:
156 if None not in paths and paths[0] != paths[1]:
152 # neither an add nor an delete - a move
157 # neither an add nor an delete - a move
153 # rename all directory contents manually
158 # rename all directory contents manually
154 subdir = origin.inventory.path2id(paths[0])
159 subdir = origin.inventory.path2id(paths[0])
155 # get all child-entries of the directory
160 # get all child-entries of the directory
156 for name, entry in origin.inventory.iter_entries(subdir):
161 for name, entry in origin.inventory.iter_entries(subdir):
157 # hg does not track directory renames
162 # hg does not track directory renames
158 if entry.kind == 'directory':
163 if entry.kind == 'directory':
159 continue
164 continue
160 frompath = self.recode(paths[0] + '/' + name)
165 frompath = self.recode(paths[0] + '/' + name)
161 topath = self.recode(paths[1] + '/' + name)
166 topath = self.recode(paths[1] + '/' + name)
162 # register the files as changed
167 # register the files as changed
163 changes.append((frompath, revid))
168 changes.append((frompath, revid))
164 changes.append((topath, revid))
169 changes.append((topath, revid))
165 # add to mode cache
170 # add to mode cache
166 mode = ((entry.executable and 'x') or (entry.kind == 'symlink' and 's')
171 mode = ((entry.executable and 'x') or (entry.kind == 'symlink' and 's')
167 or '')
172 or '')
168 self._modecache[(topath, revid)] = mode
173 self._modecache[(topath, revid)] = mode
169 # register the change as move
174 # register the change as move
170 renames[topath] = frompath
175 renames[topath] = frompath
171
176
172 # no futher changes, go to the next change
177 # no futher changes, go to the next change
173 continue
178 continue
174
179
175 # we got unicode paths, need to convert them
180 # we got unicode paths, need to convert them
176 path, topath = [self.recode(part) for part in paths]
181 path, topath = [self.recode(part) for part in paths]
177
182
178 if topath is None:
183 if topath is None:
179 # file deleted
184 # file deleted
180 changes.append((path, revid))
185 changes.append((path, revid))
181 continue
186 continue
182
187
183 # renamed
188 # renamed
184 if path and path != topath:
189 if path and path != topath:
185 renames[topath] = path
190 renames[topath] = path
186 changes.append((path, revid))
191 changes.append((path, revid))
187
192
188 # populate the mode cache
193 # populate the mode cache
189 kind, executable = [e[1] for e in (kind, executable)]
194 kind, executable = [e[1] for e in (kind, executable)]
190 mode = ((executable and 'x') or (kind == 'symlink' and 's')
195 mode = ((executable and 'x') or (kind == 'symlink' and 's')
191 or '')
196 or '')
192 self._modecache[(topath, revid)] = mode
197 self._modecache[(topath, revid)] = mode
193 changes.append((topath, revid))
198 changes.append((topath, revid))
194
199
195 return changes, renames
200 return changes, renames
196
201
197 def _filterghosts(self, ids):
202 def _filterghosts(self, ids):
198 """Filters out ghost revisions which hg does not support, see
203 """Filters out ghost revisions which hg does not support, see
199 <http://bazaar-vcs.org/GhostRevision>
204 <http://bazaar-vcs.org/GhostRevision>
200 """
205 """
201 parentmap = self.sourcerepo.get_parent_map(ids)
206 parentmap = self.sourcerepo.get_parent_map(ids)
202 parents = tuple([parent for parent in ids if parent in parentmap])
207 parents = tuple([parent for parent in ids if parent in parentmap])
203 return parents
208 return parents
204
209
205 def recode(self, s, encoding=None):
210 def recode(self, s, encoding=None):
206 """This version of recode tries to encode unicode to bytecode,
211 """This version of recode tries to encode unicode to bytecode,
207 and preferably using the UTF-8 codec.
212 and preferably using the UTF-8 codec.
208 Other types than Unicode are silently returned, this is by
213 Other types than Unicode are silently returned, this is by
209 intention, e.g. the None-type is not going to be encoded but instead
214 intention, e.g. the None-type is not going to be encoded but instead
210 just passed through
215 just passed through
211 """
216 """
212 if not encoding:
217 if not encoding:
213 encoding = self.encoding or 'utf-8'
218 encoding = self.encoding or 'utf-8'
214
219
215 if isinstance(s, unicode):
220 if isinstance(s, unicode):
216 return s.encode(encoding)
221 return s.encode(encoding)
217 else:
222 else:
218 # leave it alone
223 # leave it alone
219 return s
224 return s
@@ -1,233 +1,242 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Test the running system for features availability. Exit with zero
2 """Test the running system for features availability. Exit with zero
3 if all features are there, non-zero otherwise. If a feature name is
3 if all features are there, non-zero otherwise. If a feature name is
4 prefixed with "no-", the absence of feature is tested.
4 prefixed with "no-", the absence of feature is tested.
5 """
5 """
6 import optparse
6 import optparse
7 import os
7 import os
8 import re
8 import re
9 import sys
9 import sys
10 import tempfile
10 import tempfile
11
11
12 tempprefix = 'hg-hghave-'
12 tempprefix = 'hg-hghave-'
13
13
14 def matchoutput(cmd, regexp, ignorestatus=False):
14 def matchoutput(cmd, regexp, ignorestatus=False):
15 """Return True if cmd executes successfully and its output
15 """Return True if cmd executes successfully and its output
16 is matched by the supplied regular expression.
16 is matched by the supplied regular expression.
17 """
17 """
18 r = re.compile(regexp)
18 r = re.compile(regexp)
19 fh = os.popen(cmd)
19 fh = os.popen(cmd)
20 s = fh.read()
20 s = fh.read()
21 ret = fh.close()
21 ret = fh.close()
22 return (ignorestatus or ret is None) and r.search(s)
22 return (ignorestatus or ret is None) and r.search(s)
23
23
24 def has_baz():
24 def has_baz():
25 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
25 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
26
26
27 def has_bzr():
27 def has_bzr():
28 try:
28 try:
29 import bzrlib
29 import bzrlib
30 return bzrlib.__doc__ != None
30 return bzrlib.__doc__ != None
31 except ImportError:
31 except ImportError:
32 return False
32 return False
33
33
34 def has_bzr114():
35 try:
36 import bzrlib
37 return (bzrlib.__doc__ != None
38 and bzrlib.version_info[:2] == (1, 14))
39 except ImportError:
40 return False
41
34 def has_cvs():
42 def has_cvs():
35 re = r'Concurrent Versions System.*?server'
43 re = r'Concurrent Versions System.*?server'
36 return matchoutput('cvs --version 2>&1', re)
44 return matchoutput('cvs --version 2>&1', re)
37
45
38 def has_cvsps():
46 def has_cvsps():
39 return matchoutput('cvsps -h -q 2>&1', r'cvsps version', True)
47 return matchoutput('cvsps -h -q 2>&1', r'cvsps version', True)
40
48
41 def has_darcs():
49 def has_darcs():
42 return matchoutput('darcs', r'darcs version', True)
50 return matchoutput('darcs', r'darcs version', True)
43
51
44 def has_mtn():
52 def has_mtn():
45 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
53 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
46 'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
54 'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
47
55
48 def has_eol_in_paths():
56 def has_eol_in_paths():
49 try:
57 try:
50 fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r')
58 fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r')
51 os.close(fd)
59 os.close(fd)
52 os.remove(path)
60 os.remove(path)
53 return True
61 return True
54 except:
62 except:
55 return False
63 return False
56
64
57 def has_executablebit():
65 def has_executablebit():
58 fd, path = tempfile.mkstemp(prefix=tempprefix)
66 fd, path = tempfile.mkstemp(prefix=tempprefix)
59 os.close(fd)
67 os.close(fd)
60 try:
68 try:
61 s = os.lstat(path).st_mode
69 s = os.lstat(path).st_mode
62 os.chmod(path, s | 0100)
70 os.chmod(path, s | 0100)
63 return (os.lstat(path).st_mode & 0100 != 0)
71 return (os.lstat(path).st_mode & 0100 != 0)
64 finally:
72 finally:
65 os.remove(path)
73 os.remove(path)
66
74
67 def has_icasefs():
75 def has_icasefs():
68 # Stolen from mercurial.util
76 # Stolen from mercurial.util
69 fd, path = tempfile.mkstemp(prefix=tempprefix)
77 fd, path = tempfile.mkstemp(prefix=tempprefix)
70 os.close(fd)
78 os.close(fd)
71 try:
79 try:
72 s1 = os.stat(path)
80 s1 = os.stat(path)
73 d, b = os.path.split(path)
81 d, b = os.path.split(path)
74 p2 = os.path.join(d, b.upper())
82 p2 = os.path.join(d, b.upper())
75 if path == p2:
83 if path == p2:
76 p2 = os.path.join(d, b.lower())
84 p2 = os.path.join(d, b.lower())
77 try:
85 try:
78 s2 = os.stat(p2)
86 s2 = os.stat(p2)
79 return s2 == s1
87 return s2 == s1
80 except:
88 except:
81 return False
89 return False
82 finally:
90 finally:
83 os.remove(path)
91 os.remove(path)
84
92
85 def has_inotify():
93 def has_inotify():
86 try:
94 try:
87 import hgext.inotify.linux.watcher
95 import hgext.inotify.linux.watcher
88 return True
96 return True
89 except ImportError:
97 except ImportError:
90 return False
98 return False
91
99
92 def has_fifo():
100 def has_fifo():
93 return hasattr(os, "mkfifo")
101 return hasattr(os, "mkfifo")
94
102
95 def has_hotshot():
103 def has_hotshot():
96 try:
104 try:
97 # hotshot.stats tests hotshot and many problematic dependencies
105 # hotshot.stats tests hotshot and many problematic dependencies
98 # like profile.
106 # like profile.
99 import hotshot.stats
107 import hotshot.stats
100 return True
108 return True
101 except ImportError:
109 except ImportError:
102 return False
110 return False
103
111
104 def has_lsprof():
112 def has_lsprof():
105 try:
113 try:
106 import _lsprof
114 import _lsprof
107 return True
115 return True
108 except ImportError:
116 except ImportError:
109 return False
117 return False
110
118
111 def has_git():
119 def has_git():
112 return matchoutput('git --version 2>&1', r'^git version')
120 return matchoutput('git --version 2>&1', r'^git version')
113
121
114 def has_svn():
122 def has_svn():
115 return matchoutput('svn --version 2>&1', r'^svn, version') and \
123 return matchoutput('svn --version 2>&1', r'^svn, version') and \
116 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
124 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
117
125
118 def has_svn_bindings():
126 def has_svn_bindings():
119 try:
127 try:
120 import svn.core
128 import svn.core
121 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
129 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
122 if version < (1, 4):
130 if version < (1, 4):
123 return False
131 return False
124 return True
132 return True
125 except ImportError:
133 except ImportError:
126 return False
134 return False
127
135
128 def has_p4():
136 def has_p4():
129 return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/')
137 return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/')
130
138
131 def has_symlink():
139 def has_symlink():
132 return hasattr(os, "symlink")
140 return hasattr(os, "symlink")
133
141
134 def has_tla():
142 def has_tla():
135 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
143 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
136
144
137 def has_unix_permissions():
145 def has_unix_permissions():
138 d = tempfile.mkdtemp(prefix=tempprefix, dir=".")
146 d = tempfile.mkdtemp(prefix=tempprefix, dir=".")
139 try:
147 try:
140 fname = os.path.join(d, 'foo')
148 fname = os.path.join(d, 'foo')
141 for umask in (077, 007, 022):
149 for umask in (077, 007, 022):
142 os.umask(umask)
150 os.umask(umask)
143 f = open(fname, 'w')
151 f = open(fname, 'w')
144 f.close()
152 f.close()
145 mode = os.stat(fname).st_mode
153 mode = os.stat(fname).st_mode
146 os.unlink(fname)
154 os.unlink(fname)
147 if mode & 0777 != ~umask & 0666:
155 if mode & 0777 != ~umask & 0666:
148 return False
156 return False
149 return True
157 return True
150 finally:
158 finally:
151 os.rmdir(d)
159 os.rmdir(d)
152
160
153 def has_pygments():
161 def has_pygments():
154 try:
162 try:
155 import pygments
163 import pygments
156 return True
164 return True
157 except ImportError:
165 except ImportError:
158 return False
166 return False
159
167
160 def has_outer_repo():
168 def has_outer_repo():
161 return matchoutput('hg root 2>&1', r'')
169 return matchoutput('hg root 2>&1', r'')
162
170
163 checks = {
171 checks = {
164 "baz": (has_baz, "GNU Arch baz client"),
172 "baz": (has_baz, "GNU Arch baz client"),
165 "bzr": (has_bzr, "Canonical's Bazaar client"),
173 "bzr": (has_bzr, "Canonical's Bazaar client"),
174 "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
166 "cvs": (has_cvs, "cvs client/server"),
175 "cvs": (has_cvs, "cvs client/server"),
167 "cvsps": (has_cvsps, "cvsps utility"),
176 "cvsps": (has_cvsps, "cvsps utility"),
168 "darcs": (has_darcs, "darcs client"),
177 "darcs": (has_darcs, "darcs client"),
169 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
178 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
170 "execbit": (has_executablebit, "executable bit"),
179 "execbit": (has_executablebit, "executable bit"),
171 "fifo": (has_fifo, "named pipes"),
180 "fifo": (has_fifo, "named pipes"),
172 "git": (has_git, "git command line client"),
181 "git": (has_git, "git command line client"),
173 "hotshot": (has_hotshot, "python hotshot module"),
182 "hotshot": (has_hotshot, "python hotshot module"),
174 "icasefs": (has_icasefs, "case insensitive file system"),
183 "icasefs": (has_icasefs, "case insensitive file system"),
175 "inotify": (has_inotify, "inotify extension support"),
184 "inotify": (has_inotify, "inotify extension support"),
176 "lsprof": (has_lsprof, "python lsprof module"),
185 "lsprof": (has_lsprof, "python lsprof module"),
177 "mtn": (has_mtn, "monotone client (> 0.31)"),
186 "mtn": (has_mtn, "monotone client (> 0.31)"),
178 "outer-repo": (has_outer_repo, "outer repo"),
187 "outer-repo": (has_outer_repo, "outer repo"),
179 "p4": (has_p4, "Perforce server and client"),
188 "p4": (has_p4, "Perforce server and client"),
180 "pygments": (has_pygments, "Pygments source highlighting library"),
189 "pygments": (has_pygments, "Pygments source highlighting library"),
181 "svn": (has_svn, "subversion client and admin tools"),
190 "svn": (has_svn, "subversion client and admin tools"),
182 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
191 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
183 "symlink": (has_symlink, "symbolic links"),
192 "symlink": (has_symlink, "symbolic links"),
184 "tla": (has_tla, "GNU Arch tla client"),
193 "tla": (has_tla, "GNU Arch tla client"),
185 "unix-permissions": (has_unix_permissions, "unix-style permissions"),
194 "unix-permissions": (has_unix_permissions, "unix-style permissions"),
186 }
195 }
187
196
188 def list_features():
197 def list_features():
189 for name, feature in checks.iteritems():
198 for name, feature in checks.iteritems():
190 desc = feature[1]
199 desc = feature[1]
191 print name + ':', desc
200 print name + ':', desc
192
201
193 parser = optparse.OptionParser("%prog [options] [features]")
202 parser = optparse.OptionParser("%prog [options] [features]")
194 parser.add_option("--list-features", action="store_true",
203 parser.add_option("--list-features", action="store_true",
195 help="list available features")
204 help="list available features")
196 parser.add_option("-q", "--quiet", action="store_true",
205 parser.add_option("-q", "--quiet", action="store_true",
197 help="check features silently")
206 help="check features silently")
198
207
199 if __name__ == '__main__':
208 if __name__ == '__main__':
200 options, args = parser.parse_args()
209 options, args = parser.parse_args()
201 if options.list_features:
210 if options.list_features:
202 list_features()
211 list_features()
203 sys.exit(0)
212 sys.exit(0)
204
213
205 quiet = options.quiet
214 quiet = options.quiet
206
215
207 failures = 0
216 failures = 0
208
217
209 def error(msg):
218 def error(msg):
210 global failures
219 global failures
211 if not quiet:
220 if not quiet:
212 sys.stderr.write(msg + '\n')
221 sys.stderr.write(msg + '\n')
213 failures += 1
222 failures += 1
214
223
215 for feature in args:
224 for feature in args:
216 negate = feature.startswith('no-')
225 negate = feature.startswith('no-')
217 if negate:
226 if negate:
218 feature = feature[3:]
227 feature = feature[3:]
219
228
220 if feature not in checks:
229 if feature not in checks:
221 error('skipped: unknown feature: ' + feature)
230 error('skipped: unknown feature: ' + feature)
222 continue
231 continue
223
232
224 check, desc = checks[feature]
233 check, desc = checks[feature]
225 if not negate and not check():
234 if not negate and not check():
226 error('skipped: missing feature: ' + desc)
235 error('skipped: missing feature: ' + desc)
227 elif negate and check():
236 elif negate and check():
228 error('skipped: system supports %s' % desc)
237 error('skipped: system supports %s' % desc)
229
238
230 if failures != 0:
239 if failures != 0:
231 sys.exit(1)
240 sys.exit(1)
232
241
233
242
General Comments 0
You need to be logged in to leave comments. Login now