##// END OF EJS Templates
Merge with crew-stable
Patrick Mezard -
r8127:17ab4dab merge 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 if not os.path.exists(os.path.join(path, '.bzr')):
32 if not os.path.exists(os.path.join(path, '.bzr')):
33 raise NoRepo('%s does not look like a Bazaar repo' % path)
33 raise NoRepo('%s does not look like a Bazaar repo' % path)
34
34
35 try:
35 try:
36 # access bzrlib stuff
36 # access bzrlib stuff
37 branch
37 branch
38 except NameError:
38 except NameError:
39 raise NoRepo('Bazaar modules could not be loaded')
39 raise NoRepo('Bazaar modules could not be loaded')
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,216 +1,222 b''
1 # monotone support for the convert extension
1 # monotone support for the convert extension
2
2
3 import os, re
3 import os, re
4 from mercurial import util
4 from mercurial import util
5 from common import NoRepo, commit, converter_source, checktool
5 from common import NoRepo, commit, converter_source, checktool
6 from common import commandline
6 from common import commandline
7 from mercurial.i18n import _
7 from mercurial.i18n import _
8
8
9 class monotone_source(converter_source, commandline):
9 class monotone_source(converter_source, commandline):
10 def __init__(self, ui, path=None, rev=None):
10 def __init__(self, ui, path=None, rev=None):
11 converter_source.__init__(self, ui, path, rev)
11 converter_source.__init__(self, ui, path, rev)
12 commandline.__init__(self, ui, 'mtn')
12 commandline.__init__(self, ui, 'mtn')
13
13
14 self.ui = ui
14 self.ui = ui
15 self.path = path
15 self.path = path
16
16
17 norepo = NoRepo (_("%s does not look like a monotone repo") % path)
17 norepo = NoRepo (_("%s does not look like a monotone repo") % path)
18 if not os.path.exists(os.path.join(path, '_MTN')):
18 if not os.path.exists(os.path.join(path, '_MTN')):
19 # Could be a monotone repository (SQLite db file)
19 # Could be a monotone repository (SQLite db file)
20 try:
20 try:
21 header = file(path, 'rb').read(16)
21 header = file(path, 'rb').read(16)
22 except:
22 except:
23 header = ''
23 header = ''
24 if header != 'SQLite format 3\x00':
24 if header != 'SQLite format 3\x00':
25 raise norepo
25 raise norepo
26
26
27 # regular expressions for parsing monotone output
27 # regular expressions for parsing monotone output
28 space = r'\s*'
28 space = r'\s*'
29 name = r'\s+"((?:\\"|[^"])*)"\s*'
29 name = r'\s+"((?:\\"|[^"])*)"\s*'
30 value = name
30 value = name
31 revision = r'\s+\[(\w+)\]\s*'
31 revision = r'\s+\[(\w+)\]\s*'
32 lines = r'(?:.|\n)+'
32 lines = r'(?:.|\n)+'
33
33
34 self.dir_re = re.compile(space + "dir" + name)
34 self.dir_re = re.compile(space + "dir" + name)
35 self.file_re = re.compile(space + "file" + name + "content" + revision)
35 self.file_re = re.compile(space + "file" + name + "content" + revision)
36 self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
36 self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
37 self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
37 self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
38 self.rename_re = re.compile(space + "rename" + name + "to" + name)
38 self.rename_re = re.compile(space + "rename" + name + "to" + name)
39 self.delete_re = re.compile(space + "delete" + name)
39 self.delete_re = re.compile(space + "delete" + name)
40 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
40 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
41 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
41 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
42
42
43 attr = space + "file" + lines + space + "attr" + space
43 attr = space + "file" + lines + space + "attr" + space
44 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
44 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
45
45
46 # cached data
46 # cached data
47 self.manifest_rev = None
47 self.manifest_rev = None
48 self.manifest = None
48 self.manifest = None
49 self.files = None
49 self.files = None
50 self.dirs = None
50 self.dirs = None
51
51
52 checktool('mtn', abort=False)
52 checktool('mtn', abort=False)
53
53
54 # test if there are any revisions
54 # test if there are any revisions
55 self.rev = None
55 self.rev = None
56 try:
56 try:
57 self.getheads()
57 self.getheads()
58 except:
58 except:
59 raise norepo
59 raise norepo
60 self.rev = rev
60 self.rev = rev
61
61
62 def mtnrun(self, *args, **kwargs):
62 def mtnrun(self, *args, **kwargs):
63 kwargs['d'] = self.path
63 kwargs['d'] = self.path
64 return self.run0('automate', *args, **kwargs)
64 return self.run0('automate', *args, **kwargs)
65
65
66 def mtnloadmanifest(self, rev):
66 def mtnloadmanifest(self, rev):
67 if self.manifest_rev == rev:
67 if self.manifest_rev == rev:
68 return
68 return
69 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
69 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
70 self.manifest_rev = rev
70 self.manifest_rev = rev
71 self.files = {}
71 self.files = {}
72 self.dirs = {}
72 self.dirs = {}
73
73
74 for e in self.manifest:
74 for e in self.manifest:
75 m = self.file_re.match(e)
75 m = self.file_re.match(e)
76 if m:
76 if m:
77 attr = ""
77 attr = ""
78 name = m.group(1)
78 name = m.group(1)
79 node = m.group(2)
79 node = m.group(2)
80 if self.attr_execute_re.match(e):
80 if self.attr_execute_re.match(e):
81 attr += "x"
81 attr += "x"
82 self.files[name] = (node, attr)
82 self.files[name] = (node, attr)
83 m = self.dir_re.match(e)
83 m = self.dir_re.match(e)
84 if m:
84 if m:
85 self.dirs[m.group(1)] = True
85 self.dirs[m.group(1)] = True
86
86
87 def mtnisfile(self, name, rev):
87 def mtnisfile(self, name, rev):
88 # a non-file could be a directory or a deleted or renamed file
88 # a non-file could be a directory or a deleted or renamed file
89 self.mtnloadmanifest(rev)
89 self.mtnloadmanifest(rev)
90 try:
90 try:
91 self.files[name]
91 self.files[name]
92 return True
92 return True
93 except KeyError:
93 except KeyError:
94 return False
94 return False
95
95
96 def mtnisdir(self, name, rev):
96 def mtnisdir(self, name, rev):
97 self.mtnloadmanifest(rev)
97 self.mtnloadmanifest(rev)
98 try:
98 try:
99 self.dirs[name]
99 self.dirs[name]
100 return True
100 return True
101 except KeyError:
101 except KeyError:
102 return False
102 return False
103
103
104 def mtngetcerts(self, rev):
104 def mtngetcerts(self, rev):
105 certs = {"author":"<missing>", "date":"<missing>",
105 certs = {"author":"<missing>", "date":"<missing>",
106 "changelog":"<missing>", "branch":"<missing>"}
106 "changelog":"<missing>", "branch":"<missing>"}
107 cert_list = self.mtnrun("certs", rev).split('\n\n key "')
107 cert_list = self.mtnrun("certs", rev).split('\n\n key "')
108 for e in cert_list:
108 for e in cert_list:
109 m = self.cert_re.match(e)
109 m = self.cert_re.match(e)
110 if m:
110 if m:
111 name, value = m.groups()
111 name, value = m.groups()
112 value = value.replace(r'\"', '"')
112 value = value.replace(r'\"', '"')
113 value = value.replace(r'\\', '\\')
113 value = value.replace(r'\\', '\\')
114 certs[name] = value
114 certs[name] = value
115 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
115 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
116 certs["date"] = certs["date"].split('.')[0]
116 # and all times are stored in UTC
117 certs["date"] = certs["date"].split('.')[0] + " UTC"
117 return certs
118 return certs
118
119
119 # implement the converter_source interface:
120 # implement the converter_source interface:
120
121
121 def getheads(self):
122 def getheads(self):
122 if not self.rev:
123 if not self.rev:
123 return self.mtnrun("leaves").splitlines()
124 return self.mtnrun("leaves").splitlines()
124 else:
125 else:
125 return [self.rev]
126 return [self.rev]
126
127
127 def getchanges(self, rev):
128 def getchanges(self, rev):
128 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
129 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
129 revision = self.mtnrun("get_revision", rev).split("\n\n")
130 revision = self.mtnrun("get_revision", rev).split("\n\n")
130 files = {}
131 files = {}
131 addedfiles = {}
132 ignoremove = {}
132 renameddirs = []
133 renameddirs = []
133 copies = {}
134 copies = {}
134 for e in revision:
135 for e in revision:
135 m = self.add_file_re.match(e)
136 m = self.add_file_re.match(e)
136 if m:
137 if m:
137 files[m.group(1)] = rev
138 files[m.group(1)] = rev
138 addedfiles[m.group(1)] = rev
139 ignoremove[m.group(1)] = rev
139 m = self.patch_re.match(e)
140 m = self.patch_re.match(e)
140 if m:
141 if m:
141 files[m.group(1)] = rev
142 files[m.group(1)] = rev
142 # Delete/rename is handled later when the convert engine
143 # Delete/rename is handled later when the convert engine
143 # discovers an IOError exception from getfile,
144 # discovers an IOError exception from getfile,
144 # but only if we add the "from" file to the list of changes.
145 # but only if we add the "from" file to the list of changes.
145 m = self.delete_re.match(e)
146 m = self.delete_re.match(e)
146 if m:
147 if m:
147 files[m.group(1)] = rev
148 files[m.group(1)] = rev
148 m = self.rename_re.match(e)
149 m = self.rename_re.match(e)
149 if m:
150 if m:
150 toname = m.group(2)
151 toname = m.group(2)
151 fromname = m.group(1)
152 fromname = m.group(1)
152 if self.mtnisfile(toname, rev):
153 if self.mtnisfile(toname, rev):
154 ignoremove[toname] = 1
153 copies[toname] = fromname
155 copies[toname] = fromname
154 files[toname] = rev
156 files[toname] = rev
155 files[fromname] = rev
157 files[fromname] = rev
156 elif self.mtnisdir(toname, rev):
158 elif self.mtnisdir(toname, rev):
157 renameddirs.append((fromname, toname))
159 renameddirs.append((fromname, toname))
158
160
159 # Directory renames can be handled only once we have recorded
161 # Directory renames can be handled only once we have recorded
160 # all new files
162 # all new files
161 for fromdir, todir in renameddirs:
163 for fromdir, todir in renameddirs:
162 renamed = {}
164 renamed = {}
163 for tofile in self.files:
165 for tofile in self.files:
164 if tofile in addedfiles:
166 if tofile in ignoremove:
165 continue
167 continue
166 if tofile.startswith(todir + '/'):
168 if tofile.startswith(todir + '/'):
167 renamed[tofile] = fromdir + tofile[len(todir):]
169 renamed[tofile] = fromdir + tofile[len(todir):]
170 # Avoid chained moves like:
171 # d1(/a) => d3/d1(/a)
172 # d2 => d3
173 ignoremove[tofile] = 1
168 for tofile, fromfile in renamed.items():
174 for tofile, fromfile in renamed.items():
169 self.ui.debug (_("copying file in renamed directory "
175 self.ui.debug (_("copying file in renamed directory "
170 "from '%s' to '%s'")
176 "from '%s' to '%s'")
171 % (fromfile, tofile), '\n')
177 % (fromfile, tofile), '\n')
172 files[tofile] = rev
178 files[tofile] = rev
173 copies[tofile] = fromfile
179 copies[tofile] = fromfile
174 for fromfile in renamed.values():
180 for fromfile in renamed.values():
175 files[fromfile] = rev
181 files[fromfile] = rev
176
182
177 return (files.items(), copies)
183 return (files.items(), copies)
178
184
179 def getmode(self, name, rev):
185 def getmode(self, name, rev):
180 self.mtnloadmanifest(rev)
186 self.mtnloadmanifest(rev)
181 try:
187 try:
182 node, attr = self.files[name]
188 node, attr = self.files[name]
183 return attr
189 return attr
184 except KeyError:
190 except KeyError:
185 return ""
191 return ""
186
192
187 def getfile(self, name, rev):
193 def getfile(self, name, rev):
188 if not self.mtnisfile(name, rev):
194 if not self.mtnisfile(name, rev):
189 raise IOError() # file was deleted or renamed
195 raise IOError() # file was deleted or renamed
190 try:
196 try:
191 return self.mtnrun("get_file_of", name, r=rev)
197 return self.mtnrun("get_file_of", name, r=rev)
192 except:
198 except:
193 raise IOError() # file was deleted or renamed
199 raise IOError() # file was deleted or renamed
194
200
195 def getcommit(self, rev):
201 def getcommit(self, rev):
196 certs = self.mtngetcerts(rev)
202 certs = self.mtngetcerts(rev)
197 return commit(
203 return commit(
198 author=certs["author"],
204 author=certs["author"],
199 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
205 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
200 desc=certs["changelog"],
206 desc=certs["changelog"],
201 rev=rev,
207 rev=rev,
202 parents=self.mtnrun("parents", rev).splitlines(),
208 parents=self.mtnrun("parents", rev).splitlines(),
203 branch=certs["branch"])
209 branch=certs["branch"])
204
210
205 def gettags(self):
211 def gettags(self):
206 tags = {}
212 tags = {}
207 for e in self.mtnrun("tags").split("\n\n"):
213 for e in self.mtnrun("tags").split("\n\n"):
208 m = self.tag_re.match(e)
214 m = self.tag_re.match(e)
209 if m:
215 if m:
210 tags[m.group(1)] = m.group(2)
216 tags[m.group(1)] = m.group(2)
211 return tags
217 return tags
212
218
213 def getchangedfiles(self, rev, i):
219 def getchangedfiles(self, rev, i):
214 # This function is only needed to support --filemap
220 # This function is only needed to support --filemap
215 # ... and we don't support that
221 # ... and we don't support that
216 raise NotImplementedError()
222 raise NotImplementedError()
@@ -1,255 +1,264 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 def test_features():
202 def test_features():
194 failed = 0
203 failed = 0
195 for name, feature in checks.iteritems():
204 for name, feature in checks.iteritems():
196 check, _ = feature
205 check, _ = feature
197 try:
206 try:
198 check()
207 check()
199 except Exception, e:
208 except Exception, e:
200 print "feature %s failed: %s" % (name, e)
209 print "feature %s failed: %s" % (name, e)
201 failed += 1
210 failed += 1
202 return failed
211 return failed
203
212
204 parser = optparse.OptionParser("%prog [options] [features]")
213 parser = optparse.OptionParser("%prog [options] [features]")
205 parser.add_option("--test-features", action="store_true",
214 parser.add_option("--test-features", action="store_true",
206 help="test available features")
215 help="test available features")
207 parser.add_option("--list-features", action="store_true",
216 parser.add_option("--list-features", action="store_true",
208 help="list available features")
217 help="list available features")
209 parser.add_option("-q", "--quiet", action="store_true",
218 parser.add_option("-q", "--quiet", action="store_true",
210 help="check features silently")
219 help="check features silently")
211
220
212 if __name__ == '__main__':
221 if __name__ == '__main__':
213 options, args = parser.parse_args()
222 options, args = parser.parse_args()
214 if options.list_features:
223 if options.list_features:
215 list_features()
224 list_features()
216 sys.exit(0)
225 sys.exit(0)
217
226
218 if options.test_features:
227 if options.test_features:
219 sys.exit(test_features())
228 sys.exit(test_features())
220
229
221 quiet = options.quiet
230 quiet = options.quiet
222
231
223 failures = 0
232 failures = 0
224
233
225 def error(msg):
234 def error(msg):
226 global failures
235 global failures
227 if not quiet:
236 if not quiet:
228 sys.stderr.write(msg + '\n')
237 sys.stderr.write(msg + '\n')
229 failures += 1
238 failures += 1
230
239
231 for feature in args:
240 for feature in args:
232 negate = feature.startswith('no-')
241 negate = feature.startswith('no-')
233 if negate:
242 if negate:
234 feature = feature[3:]
243 feature = feature[3:]
235
244
236 if feature not in checks:
245 if feature not in checks:
237 error('skipped: unknown feature: ' + feature)
246 error('skipped: unknown feature: ' + feature)
238 continue
247 continue
239
248
240 check, desc = checks[feature]
249 check, desc = checks[feature]
241 try:
250 try:
242 available = check()
251 available = check()
243 except Exception, e:
252 except Exception, e:
244 error('hghave check failed: ' + feature)
253 error('hghave check failed: ' + feature)
245 continue
254 continue
246
255
247 if not negate and not available:
256 if not negate and not available:
248 error('skipped: missing feature: ' + desc)
257 error('skipped: missing feature: ' + desc)
249 elif negate and available:
258 elif negate and available:
250 error('skipped: system supports %s' % desc)
259 error('skipped: system supports %s' % desc)
251
260
252 if failures != 0:
261 if failures != 0:
253 sys.exit(1)
262 sys.exit(1)
254
263
255
264
@@ -1,112 +1,146 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 "$TESTDIR/hghave" mtn || exit 80
3 "$TESTDIR/hghave" mtn || exit 80
4
4
5 # Monotone directory is called .monotone on *nix and monotone
5 # Monotone directory is called .monotone on *nix and monotone
6 # on Windows. Having a variable here ease test patching.
6 # on Windows. Having a variable here ease test patching.
7 mtndir=.monotone
7 mtndir=.monotone
8 echo "[extensions]" >> $HGRCPATH
8 echo "[extensions]" >> $HGRCPATH
9 echo "convert=" >> $HGRCPATH
9 echo "convert=" >> $HGRCPATH
10 echo 'hgext.graphlog =' >> $HGRCPATH
10 echo 'hgext.graphlog =' >> $HGRCPATH
11
11
12 HOME=`pwd`/do_not_use_HOME_mtn; export HOME
12 HOME=`pwd`/do_not_use_HOME_mtn; export HOME
13 # Windows version of monotone home
13 # Windows version of monotone home
14 APPDATA=$HOME; export APPDATA
14 APPDATA=$HOME; export APPDATA
15
15
16 echo % tedious monotone keys configuration
16 echo % tedious monotone keys configuration
17 # The /dev/null redirection is necessary under Windows, or
17 # The /dev/null redirection is necessary under Windows, or
18 # it complains about home directory permissions
18 # it complains about home directory permissions
19 mtn --quiet genkey test@selenic.com 1>/dev/null 2>&1 <<EOF
19 mtn --quiet genkey test@selenic.com 1>/dev/null 2>&1 <<EOF
20 passphrase
20 passphrase
21 passphrase
21 passphrase
22 EOF
22 EOF
23 cat >> $HOME/$mtndir/monotonerc <<EOF
23 cat >> $HOME/$mtndir/monotonerc <<EOF
24 function get_passphrase(keypair_id)
24 function get_passphrase(keypair_id)
25 return "passphrase"
25 return "passphrase"
26 end
26 end
27 EOF
27 EOF
28
28
29 echo % create monotone repository
29 echo % create monotone repository
30 mtn db init --db=repo.mtn
30 mtn db init --db=repo.mtn
31 mtn --db=repo.mtn --branch=com.selenic.test setup workingdir
31 mtn --db=repo.mtn --branch=com.selenic.test setup workingdir
32 cd workingdir
32 cd workingdir
33 echo a > a
33 echo a > a
34 mkdir dir
34 mkdir dir
35 echo b > dir/b
35 echo b > dir/b
36 echo d > dir/d
36 echo d > dir/d
37 python -c 'file("bin", "wb").write("a\\x00b")'
37 python -c 'file("bin", "wb").write("a\\x00b")'
38 echo c > c
38 echo c > c
39 mtn add a dir/b dir/d c bin
39 mtn add a dir/b dir/d c bin
40 mtn ci -m initialize
40 mtn ci -m initialize
41 echo % update monotone working directory
41 echo % update monotone working directory
42 mtn mv a dir/a
42 mtn mv a dir/a
43 echo a >> dir/a
43 echo a >> dir/a
44 echo b >> dir/b
44 echo b >> dir/b
45 mtn drop c
45 mtn drop c
46 python -c 'file("bin", "wb").write("b\\x00c")'
46 python -c 'file("bin", "wb").write("b\\x00c")'
47 mtn ci -m update1
47 mtn ci -m update1
48 cd ..
48 cd ..
49
49
50 echo % convert once
50 echo % convert once
51 hg convert -s mtn repo.mtn
51 hg convert -s mtn repo.mtn
52
52
53 cd workingdir
53 cd workingdir
54 echo e > e
54 echo e > e
55 mtn add e
55 mtn add e
56 mtn drop dir/b
56 mtn drop dir/b
57 mtn mv bin bin2
57 mtn mv bin bin2
58 mtn ci -m 'update2 "with" quotes'
58 mtn ci -m 'update2 "with" quotes'
59 echo '% test directory move'
59 echo '% test directory move'
60 mkdir -p dir1/subdir1
60 mkdir -p dir1/subdir1
61 mkdir -p dir1/subdir2_other
61 mkdir -p dir1/subdir2_other
62 echo file1 > dir1/subdir1/file1
62 echo file1 > dir1/subdir1/file1
63 echo file2 > dir1/subdir2_other/file1
63 echo file2 > dir1/subdir2_other/file1
64 mtn add dir1/subdir1/file1 dir1/subdir2_other/file1
64 mtn add dir1/subdir1/file1 dir1/subdir2_other/file1
65 mtn ci -m createdir1
65 mtn ci -m createdir1
66 mtn rename dir1/subdir1 dir1/subdir2
66 mtn rename dir1/subdir1 dir1/subdir2
67 mtn ci -m movedir1
67 mtn ci -m movedir1
68 echo '% test subdirectory move'
68 echo '% test subdirectory move'
69 mtn mv dir dir2
69 mtn mv dir dir2
70 echo newfile > dir2/newfile
70 echo newfile > dir2/newfile
71 mtn drop dir2/d
71 mtn drop dir2/d
72 mtn add dir2/newfile
72 mtn add dir2/newfile
73 mtn ci -m movedir
73 mtn ci -m movedir
74 # Test directory removal with empty directory
74 # Test directory removal with empty directory
75 mkdir dir2/dir
75 mkdir dir2/dir
76 mkdir dir2/dir/subdir
76 mkdir dir2/dir/subdir
77 echo f > dir2/dir/subdir/f
77 echo f > dir2/dir/subdir/f
78 mkdir dir2/dir/emptydir
78 mkdir dir2/dir/emptydir
79 mtn add --quiet -R dir2/dir
79 mtn add --quiet -R dir2/dir
80 mtn ci -m emptydir
80 mtn ci -m emptydir
81 mtn drop -R dir2/dir
81 mtn drop -R dir2/dir
82 mtn ci -m dropdirectory
82 mtn ci -m dropdirectory
83 echo '% test directory and file move'
84 mkdir -p dir3/d1
85 echo a > dir3/a
86 mtn add dir3/a dir3/d1
87 mtn ci -m dirfilemove
88 mtn mv dir3/a dir3/d1/a
89 mtn mv dir3/d1 dir3/d2
90 mtn ci -m dirfilemove2
91 echo '% test directory move into another directory move'
92 mkdir dir4
93 mkdir dir5
94 echo a > dir4/a
95 mtn add dir4/a dir5
96 mtn ci -m dirdirmove
97 mtn mv dir5 dir6
98 mtn mv dir4 dir6/dir4
99 mtn ci -m dirdirmove2
100 echo '% test diverging directory moves'
101 mkdir -p dir7/dir9/dir8
102 echo a > dir7/dir9/dir8/a
103 echo b > dir7/dir9/b
104 echo c > dir7/c
105 mtn add -R dir7
106 mtn ci -m divergentdirmove
107 mtn mv dir7 dir7-2
108 mtn mv dir7-2/dir9 dir9-2
109 mtn mv dir9-2/dir8 dir8-2
110 mtn ci -m divergentdirmove2
83 cd ..
111 cd ..
84
112
85 echo % convert incrementally
113 echo % convert incrementally
86 hg convert -s mtn repo.mtn
114 hg convert -s mtn repo.mtn
87
115
88 glog()
116 glog()
89 {
117 {
90 hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@"
118 hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@"
91 }
119 }
92
120
93 cd repo.mtn-hg
121 cd repo.mtn-hg
94 hg up -C
122 hg up -C
95 glog
123 glog
96 echo % manifest
124 echo % manifest
97 hg manifest
125 hg manifest
98 echo % contents
126 echo % contents
99 cat dir2/a
127 cat dir2/a
100 test -d dir2/dir && echo 'removed dir2/dir is still there!'
128 test -d dir2/dir && echo 'removed dir2/dir is still there!'
101
129
102 echo % file move
130 echo % file move
103 hg log -v -C -r 1 | grep copies
131 hg log -v -C -r 1 | grep copies
104 echo % check directory move
132 echo % check directory move
105 hg manifest -r 4
133 hg manifest -r 4
106 test -d dir1/subdir2 || echo 'new dir1/subdir2 does not exist!'
134 test -d dir1/subdir2 || echo 'new dir1/subdir2 does not exist!'
107 test -d dir1/subdir1 && echo 'renamed dir1/subdir1 is still there!'
135 test -d dir1/subdir1 && echo 'renamed dir1/subdir1 is still there!'
108 hg log -v -C -r 4 | grep copies
136 hg log -v -C -r 4 | grep copies
109 echo % check file remove with directory move
137 echo % check file remove with directory move
110 hg manifest -r 5
138 hg manifest -r 5
139 echo % check file move with directory move
140 hg manifest -r 9
141 echo % check file directory directory move
142 hg manifest -r 11
143 echo % check divergent directory moves
144 hg manifest -r 13
111 exit 0
145 exit 0
112
146
@@ -1,110 +1,199 b''
1 % tedious monotone keys configuration
1 % tedious monotone keys configuration
2 % create monotone repository
2 % create monotone repository
3 mtn: adding a to workspace manifest
3 mtn: adding a to workspace manifest
4 mtn: adding bin to workspace manifest
4 mtn: adding bin to workspace manifest
5 mtn: adding c to workspace manifest
5 mtn: adding c to workspace manifest
6 mtn: adding dir to workspace manifest
6 mtn: adding dir to workspace manifest
7 mtn: adding dir/b to workspace manifest
7 mtn: adding dir/b to workspace manifest
8 mtn: adding dir/d to workspace manifest
8 mtn: adding dir/d to workspace manifest
9 mtn: beginning commit on branch 'com.selenic.test'
9 mtn: beginning commit on branch 'com.selenic.test'
10 mtn: committed revision 0f6e5e4f2e7d2a8ef312408f57618abf026afd90
10 mtn: committed revision 0f6e5e4f2e7d2a8ef312408f57618abf026afd90
11 % update monotone working directory
11 % update monotone working directory
12 mtn: skipping dir, already accounted for in workspace
12 mtn: skipping dir, already accounted for in workspace
13 mtn: renaming a to dir/a in workspace manifest
13 mtn: renaming a to dir/a in workspace manifest
14 mtn: dropping c from workspace manifest
14 mtn: dropping c from workspace manifest
15 mtn: beginning commit on branch 'com.selenic.test'
15 mtn: beginning commit on branch 'com.selenic.test'
16 mtn: committed revision 51d0a982464573a2a2cf5ee2c9219c652aaebeff
16 mtn: committed revision 51d0a982464573a2a2cf5ee2c9219c652aaebeff
17 % convert once
17 % convert once
18 assuming destination repo.mtn-hg
18 assuming destination repo.mtn-hg
19 initializing destination repo.mtn-hg repository
19 initializing destination repo.mtn-hg repository
20 scanning source...
20 scanning source...
21 sorting...
21 sorting...
22 converting...
22 converting...
23 1 initialize
23 1 initialize
24 0 update1
24 0 update1
25 mtn: adding e to workspace manifest
25 mtn: adding e to workspace manifest
26 mtn: dropping dir/b from workspace manifest
26 mtn: dropping dir/b from workspace manifest
27 mtn: renaming bin to bin2 in workspace manifest
27 mtn: renaming bin to bin2 in workspace manifest
28 mtn: beginning commit on branch 'com.selenic.test'
28 mtn: beginning commit on branch 'com.selenic.test'
29 mtn: committed revision ebe58335d85d8cb176b6d0a12be04f5314b998da
29 mtn: committed revision ebe58335d85d8cb176b6d0a12be04f5314b998da
30 % test directory move
30 % test directory move
31 mtn: adding dir1 to workspace manifest
31 mtn: adding dir1 to workspace manifest
32 mtn: adding dir1/subdir1 to workspace manifest
32 mtn: adding dir1/subdir1 to workspace manifest
33 mtn: adding dir1/subdir1/file1 to workspace manifest
33 mtn: adding dir1/subdir1/file1 to workspace manifest
34 mtn: adding dir1/subdir2_other to workspace manifest
34 mtn: adding dir1/subdir2_other to workspace manifest
35 mtn: adding dir1/subdir2_other/file1 to workspace manifest
35 mtn: adding dir1/subdir2_other/file1 to workspace manifest
36 mtn: beginning commit on branch 'com.selenic.test'
36 mtn: beginning commit on branch 'com.selenic.test'
37 mtn: committed revision a8d62bc04fee4d2936d28e98bbcc81686dd74306
37 mtn: committed revision a8d62bc04fee4d2936d28e98bbcc81686dd74306
38 mtn: skipping dir1, already accounted for in workspace
38 mtn: skipping dir1, already accounted for in workspace
39 mtn: renaming dir1/subdir1 to dir1/subdir2 in workspace manifest
39 mtn: renaming dir1/subdir1 to dir1/subdir2 in workspace manifest
40 mtn: beginning commit on branch 'com.selenic.test'
40 mtn: beginning commit on branch 'com.selenic.test'
41 mtn: committed revision 2c3d241bbbfe538b1b51d910f5676407e3f4d3a6
41 mtn: committed revision 2c3d241bbbfe538b1b51d910f5676407e3f4d3a6
42 % test subdirectory move
42 % test subdirectory move
43 mtn: renaming dir to dir2 in workspace manifest
43 mtn: renaming dir to dir2 in workspace manifest
44 mtn: dropping dir2/d from workspace manifest
44 mtn: dropping dir2/d from workspace manifest
45 mtn: adding dir2/newfile to workspace manifest
45 mtn: adding dir2/newfile to workspace manifest
46 mtn: beginning commit on branch 'com.selenic.test'
46 mtn: beginning commit on branch 'com.selenic.test'
47 mtn: committed revision fdb5a02dae8bfce3a79b3393680af471016e1b4c
47 mtn: committed revision fdb5a02dae8bfce3a79b3393680af471016e1b4c
48 mtn: beginning commit on branch 'com.selenic.test'
48 mtn: beginning commit on branch 'com.selenic.test'
49 mtn: committed revision 8bbf76d717001d24964e4604739fdcd0f539fc88
49 mtn: committed revision 8bbf76d717001d24964e4604739fdcd0f539fc88
50 mtn: dropping dir2/dir/subdir/f from workspace manifest
50 mtn: dropping dir2/dir/subdir/f from workspace manifest
51 mtn: dropping dir2/dir/subdir from workspace manifest
51 mtn: dropping dir2/dir/subdir from workspace manifest
52 mtn: dropping dir2/dir/emptydir from workspace manifest
52 mtn: dropping dir2/dir/emptydir from workspace manifest
53 mtn: dropping dir2/dir from workspace manifest
53 mtn: dropping dir2/dir from workspace manifest
54 mtn: beginning commit on branch 'com.selenic.test'
54 mtn: beginning commit on branch 'com.selenic.test'
55 mtn: committed revision 2323d4bc324e6c82628dc04d47a9fd32ad24e322
55 mtn: committed revision 2323d4bc324e6c82628dc04d47a9fd32ad24e322
56 % test directory and file move
57 mtn: adding dir3 to workspace manifest
58 mtn: adding dir3/a to workspace manifest
59 mtn: adding dir3/d1 to workspace manifest
60 mtn: beginning commit on branch 'com.selenic.test'
61 mtn: committed revision 47b192f720faa622f48c68d1eb075b26d405aa8b
62 mtn: skipping dir3/d1, already accounted for in workspace
63 mtn: renaming dir3/a to dir3/d1/a in workspace manifest
64 mtn: skipping dir3, already accounted for in workspace
65 mtn: renaming dir3/d1 to dir3/d2 in workspace manifest
66 mtn: beginning commit on branch 'com.selenic.test'
67 mtn: committed revision 8b543a400d3ee7f6d4bb1835b9b9e3747c8cb632
68 % test directory move into another directory move
69 mtn: adding dir4 to workspace manifest
70 mtn: adding dir4/a to workspace manifest
71 mtn: adding dir5 to workspace manifest
72 mtn: beginning commit on branch 'com.selenic.test'
73 mtn: committed revision 466e0b2afc7a55aa2b4ab2f57cb240bb6cd66fc7
74 mtn: renaming dir5 to dir6 in workspace manifest
75 mtn: skipping dir6, already accounted for in workspace
76 mtn: renaming dir4 to dir6/dir4 in workspace manifest
77 mtn: beginning commit on branch 'com.selenic.test'
78 mtn: committed revision 3d1f77ebad0c23a5d14911be3b670f990991b749
79 % test diverging directory moves
80 mtn: adding dir7 to workspace manifest
81 mtn: adding dir7/c to workspace manifest
82 mtn: adding dir7/dir9 to workspace manifest
83 mtn: adding dir7/dir9/b to workspace manifest
84 mtn: adding dir7/dir9/dir8 to workspace manifest
85 mtn: adding dir7/dir9/dir8/a to workspace manifest
86 mtn: beginning commit on branch 'com.selenic.test'
87 mtn: committed revision 08a08511f18b428d840199b062de90d0396bc2ed
88 mtn: renaming dir7 to dir7-2 in workspace manifest
89 mtn: renaming dir7-2/dir9 to dir9-2 in workspace manifest
90 mtn: renaming dir9-2/dir8 to dir8-2 in workspace manifest
91 mtn: beginning commit on branch 'com.selenic.test'
92 mtn: committed revision 4a736634505795f17786fffdf2c9cbf5b11df6f6
56 % convert incrementally
93 % convert incrementally
57 assuming destination repo.mtn-hg
94 assuming destination repo.mtn-hg
58 scanning source...
95 scanning source...
59 sorting...
96 sorting...
60 converting...
97 converting...
61 5 update2 "with" quotes
98 11 update2 "with" quotes
62 4 createdir1
99 10 createdir1
63 3 movedir1
100 9 movedir1
64 2 movedir
101 8 movedir
65 1 emptydir
102 7 emptydir
66 0 dropdirectory
103 6 dropdirectory
67 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 5 dirfilemove
68 @ 7 "dropdirectory" files: dir2/dir/subdir/f
105 4 dirfilemove2
106 3 dirdirmove
107 2 dirdirmove2
108 1 divergentdirmove
109 0 divergentdirmove2
110 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 @ 13 "divergentdirmove2" files: dir7-2/c dir7/c dir7/dir9/b dir7/dir9/dir8/a dir8-2/a dir9-2/b
112 |
113 o 12 "divergentdirmove" files: dir7/c dir7/dir9/b dir7/dir9/dir8/a
114 |
115 o 11 "dirdirmove2" files: dir4/a dir6/dir4/a
116 |
117 o 10 "dirdirmove" files: dir4/a
118 |
119 o 9 "dirfilemove2" files: dir3/a dir3/d2/a
120 |
121 o 8 "dirfilemove" files: dir3/a
122 |
123 o 7 "dropdirectory" files: dir2/dir/subdir/f
69 |
124 |
70 o 6 "emptydir" files: dir2/dir/subdir/f
125 o 6 "emptydir" files: dir2/dir/subdir/f
71 |
126 |
72 o 5 "movedir" files: dir/a dir/d dir2/a dir2/newfile
127 o 5 "movedir" files: dir/a dir/d dir2/a dir2/newfile
73 |
128 |
74 o 4 "movedir1" files: dir1/subdir1/file1 dir1/subdir2/file1
129 o 4 "movedir1" files: dir1/subdir1/file1 dir1/subdir2/file1
75 |
130 |
76 o 3 "createdir1" files: dir1/subdir1/file1 dir1/subdir2_other/file1
131 o 3 "createdir1" files: dir1/subdir1/file1 dir1/subdir2_other/file1
77 |
132 |
78 o 2 "update2 "with" quotes" files: bin bin2 dir/b e
133 o 2 "update2 "with" quotes" files: bin bin2 dir/b e
79 |
134 |
80 o 1 "update1" files: a bin c dir/a dir/b
135 o 1 "update1" files: a bin c dir/a dir/b
81 |
136 |
82 o 0 "initialize" files: a bin c dir/b dir/d
137 o 0 "initialize" files: a bin c dir/b dir/d
83
138
84 % manifest
139 % manifest
85 bin2
140 bin2
86 dir1/subdir2/file1
141 dir1/subdir2/file1
87 dir1/subdir2_other/file1
142 dir1/subdir2_other/file1
88 dir2/a
143 dir2/a
89 dir2/newfile
144 dir2/newfile
145 dir3/d2/a
146 dir6/dir4/a
147 dir7-2/c
148 dir8-2/a
149 dir9-2/b
90 e
150 e
91 % contents
151 % contents
92 a
152 a
93 a
153 a
94 % file move
154 % file move
95 copies: dir/a (a)
155 copies: dir/a (a)
96 % check directory move
156 % check directory move
97 bin2
157 bin2
98 dir/a
158 dir/a
99 dir/d
159 dir/d
100 dir1/subdir2/file1
160 dir1/subdir2/file1
101 dir1/subdir2_other/file1
161 dir1/subdir2_other/file1
102 e
162 e
103 copies: dir1/subdir2/file1 (dir1/subdir1/file1)
163 copies: dir1/subdir2/file1 (dir1/subdir1/file1)
104 % check file remove with directory move
164 % check file remove with directory move
105 bin2
165 bin2
106 dir1/subdir2/file1
166 dir1/subdir2/file1
107 dir1/subdir2_other/file1
167 dir1/subdir2_other/file1
108 dir2/a
168 dir2/a
109 dir2/newfile
169 dir2/newfile
110 e
170 e
171 % check file move with directory move
172 bin2
173 dir1/subdir2/file1
174 dir1/subdir2_other/file1
175 dir2/a
176 dir2/newfile
177 dir3/d2/a
178 e
179 % check file directory directory move
180 bin2
181 dir1/subdir2/file1
182 dir1/subdir2_other/file1
183 dir2/a
184 dir2/newfile
185 dir3/d2/a
186 dir6/dir4/a
187 e
188 % check divergent directory moves
189 bin2
190 dir1/subdir2/file1
191 dir1/subdir2_other/file1
192 dir2/a
193 dir2/newfile
194 dir3/d2/a
195 dir6/dir4/a
196 dir7-2/c
197 dir8-2/a
198 dir9-2/b
199 e
General Comments 0
You need to be logged in to leave comments. Login now