##// END OF EJS Templates
convert: record deleted files in monotone source
Patrick Mezard -
r6376:b40e9034 default
parent child Browse files
Show More
@@ -1,194 +1,198 b''
1 # monotone support for the convert extension
1 # monotone support for the convert extension
2
2
3 import os, re, time
3 import os, re, time
4 from mercurial import util
4 from mercurial import util
5 from common import NoRepo, MissingTool, commit, converter_source, checktool
5 from common import NoRepo, MissingTool, 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 # regular expressions for parsing monotone output
17 # regular expressions for parsing monotone output
18 space = r'\s*'
18 space = r'\s*'
19 name = r'\s+"((?:[^"]|\\")*)"\s*'
19 name = r'\s+"((?:[^"]|\\")*)"\s*'
20 value = name
20 value = name
21 revision = r'\s+\[(\w+)\]\s*'
21 revision = r'\s+\[(\w+)\]\s*'
22 lines = r'(?:.|\n)+'
22 lines = r'(?:.|\n)+'
23
23
24 self.dir_re = re.compile(space + "dir" + name)
24 self.dir_re = re.compile(space + "dir" + name)
25 self.file_re = re.compile(space + "file" + name + "content" + revision)
25 self.file_re = re.compile(space + "file" + name + "content" + revision)
26 self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
26 self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
27 self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
27 self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
28 self.rename_re = re.compile(space + "rename" + name + "to" + name)
28 self.rename_re = re.compile(space + "rename" + name + "to" + name)
29 self.delete_re = re.compile(space + "delete" + name)
29 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
30 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
30 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
31 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
31
32
32 attr = space + "file" + lines + space + "attr" + space
33 attr = space + "file" + lines + space + "attr" + space
33 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
34 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
34
35
35 # cached data
36 # cached data
36 self.manifest_rev = None
37 self.manifest_rev = None
37 self.manifest = None
38 self.manifest = None
38 self.files = None
39 self.files = None
39 self.dirs = None
40 self.dirs = None
40
41
41 norepo = NoRepo (_("%s does not look like a monotone repo") % path)
42 norepo = NoRepo (_("%s does not look like a monotone repo") % path)
42 if not os.path.exists(path):
43 if not os.path.exists(path):
43 raise norepo
44 raise norepo
44
45
45 checktool('mtn', abort=False)
46 checktool('mtn', abort=False)
46
47
47 # test if there are any revisions
48 # test if there are any revisions
48 self.rev = None
49 self.rev = None
49 try:
50 try:
50 self.getheads()
51 self.getheads()
51 except:
52 except:
52 raise norepo
53 raise norepo
53 self.rev = rev
54 self.rev = rev
54
55
55 def mtnrun(self, *args, **kwargs):
56 def mtnrun(self, *args, **kwargs):
56 kwargs['d'] = self.path
57 kwargs['d'] = self.path
57 return self.run0('automate', *args, **kwargs)
58 return self.run0('automate', *args, **kwargs)
58
59
59 def mtnloadmanifest(self, rev):
60 def mtnloadmanifest(self, rev):
60 if self.manifest_rev == rev:
61 if self.manifest_rev == rev:
61 return
62 return
62 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
63 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
63 self.manifest_rev = rev
64 self.manifest_rev = rev
64 self.files = {}
65 self.files = {}
65 self.dirs = {}
66 self.dirs = {}
66
67
67 for e in self.manifest:
68 for e in self.manifest:
68 m = self.file_re.match(e)
69 m = self.file_re.match(e)
69 if m:
70 if m:
70 attr = ""
71 attr = ""
71 name = m.group(1)
72 name = m.group(1)
72 node = m.group(2)
73 node = m.group(2)
73 if self.attr_execute_re.match(e):
74 if self.attr_execute_re.match(e):
74 attr += "x"
75 attr += "x"
75 self.files[name] = (node, attr)
76 self.files[name] = (node, attr)
76 m = self.dir_re.match(e)
77 m = self.dir_re.match(e)
77 if m:
78 if m:
78 self.dirs[m.group(1)] = True
79 self.dirs[m.group(1)] = True
79
80
80 def mtnisfile(self, name, rev):
81 def mtnisfile(self, name, rev):
81 # a non-file could be a directory or a deleted or renamed file
82 # a non-file could be a directory or a deleted or renamed file
82 self.mtnloadmanifest(rev)
83 self.mtnloadmanifest(rev)
83 try:
84 try:
84 self.files[name]
85 self.files[name]
85 return True
86 return True
86 except KeyError:
87 except KeyError:
87 return False
88 return False
88
89
89 def mtnisdir(self, name, rev):
90 def mtnisdir(self, name, rev):
90 self.mtnloadmanifest(rev)
91 self.mtnloadmanifest(rev)
91 try:
92 try:
92 self.dirs[name]
93 self.dirs[name]
93 return True
94 return True
94 except KeyError:
95 except KeyError:
95 return False
96 return False
96
97
97 def mtngetcerts(self, rev):
98 def mtngetcerts(self, rev):
98 certs = {"author":"<missing>", "date":"<missing>",
99 certs = {"author":"<missing>", "date":"<missing>",
99 "changelog":"<missing>", "branch":"<missing>"}
100 "changelog":"<missing>", "branch":"<missing>"}
100 cert_list = self.mtnrun("certs", rev).split("\n\n")
101 cert_list = self.mtnrun("certs", rev).split("\n\n")
101 for e in cert_list:
102 for e in cert_list:
102 m = self.cert_re.match(e)
103 m = self.cert_re.match(e)
103 if m:
104 if m:
104 certs[m.group(1)] = m.group(2)
105 certs[m.group(1)] = m.group(2)
105 return certs
106 return certs
106
107
107 def mtnrenamefiles(self, files, fromdir, todir):
108 def mtnrenamefiles(self, files, fromdir, todir):
108 renamed = {}
109 renamed = {}
109 for tofile in files:
110 for tofile in files:
110 suffix = tofile.lstrip(todir)
111 suffix = tofile.lstrip(todir)
111 if todir + suffix == tofile:
112 if todir + suffix == tofile:
112 renamed[tofile] = (fromdir + suffix).lstrip("/")
113 renamed[tofile] = (fromdir + suffix).lstrip("/")
113 return renamed
114 return renamed
114
115
115
116
116 # implement the converter_source interface:
117 # implement the converter_source interface:
117
118
118 def getheads(self):
119 def getheads(self):
119 if not self.rev:
120 if not self.rev:
120 return self.mtnrun("leaves").splitlines()
121 return self.mtnrun("leaves").splitlines()
121 else:
122 else:
122 return [self.rev]
123 return [self.rev]
123
124
124 def getchanges(self, rev):
125 def getchanges(self, rev):
125 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
126 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
126 revision = self.mtnrun("get_revision", rev).split("\n\n")
127 revision = self.mtnrun("get_revision", rev).split("\n\n")
127 files = {}
128 files = {}
128 copies = {}
129 copies = {}
129 for e in revision:
130 for e in revision:
130 m = self.add_file_re.match(e)
131 m = self.add_file_re.match(e)
131 if m:
132 if m:
132 files[m.group(1)] = rev
133 files[m.group(1)] = rev
133 m = self.patch_re.match(e)
134 m = self.patch_re.match(e)
134 if m:
135 if m:
135 files[m.group(1)] = rev
136 files[m.group(1)] = rev
136
137
137 # Delete/rename is handled later when the convert engine
138 # Delete/rename is handled later when the convert engine
138 # discovers an IOError exception from getfile,
139 # discovers an IOError exception from getfile,
139 # but only if we add the "from" file to the list of changes.
140 # but only if we add the "from" file to the list of changes.
141 m = self.delete_re.match(e)
142 if m:
143 files[m.group(1)] = rev
140 m = self.rename_re.match(e)
144 m = self.rename_re.match(e)
141 if m:
145 if m:
142 toname = m.group(2)
146 toname = m.group(2)
143 fromname = m.group(1)
147 fromname = m.group(1)
144 if self.mtnisfile(toname, rev):
148 if self.mtnisfile(toname, rev):
145 copies[toname] = fromname
149 copies[toname] = fromname
146 files[toname] = rev
150 files[toname] = rev
147 files[fromname] = rev
151 files[fromname] = rev
148 if self.mtnisdir(toname, rev):
152 if self.mtnisdir(toname, rev):
149 renamed = self.mtnrenamefiles(self.files, fromname, toname)
153 renamed = self.mtnrenamefiles(self.files, fromname, toname)
150 for tofile, fromfile in renamed.items():
154 for tofile, fromfile in renamed.items():
151 self.ui.debug (_("copying file in renamed dir from '%s' to '%s'") % (fromfile, tofile), '\n')
155 self.ui.debug (_("copying file in renamed dir from '%s' to '%s'") % (fromfile, tofile), '\n')
152 files[tofile] = rev
156 files[tofile] = rev
153 for fromfile in renamed.values():
157 for fromfile in renamed.values():
154 files[fromfile] = rev
158 files[fromfile] = rev
155 return (files.items(), copies)
159 return (files.items(), copies)
156
160
157 def getmode(self, name, rev):
161 def getmode(self, name, rev):
158 self.mtnloadmanifest(rev)
162 self.mtnloadmanifest(rev)
159 try:
163 try:
160 node, attr = self.files[name]
164 node, attr = self.files[name]
161 return attr
165 return attr
162 except KeyError:
166 except KeyError:
163 return ""
167 return ""
164
168
165 def getfile(self, name, rev):
169 def getfile(self, name, rev):
166 if not self.mtnisfile(name, rev):
170 if not self.mtnisfile(name, rev):
167 raise IOError() # file was deleted or renamed
171 raise IOError() # file was deleted or renamed
168 try:
172 try:
169 return self.mtnrun("get_file_of", name, r=rev)
173 return self.mtnrun("get_file_of", name, r=rev)
170 except:
174 except:
171 raise IOError() # file was deleted or renamed
175 raise IOError() # file was deleted or renamed
172
176
173 def getcommit(self, rev):
177 def getcommit(self, rev):
174 certs = self.mtngetcerts(rev)
178 certs = self.mtngetcerts(rev)
175 return commit(
179 return commit(
176 author=certs["author"],
180 author=certs["author"],
177 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
181 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
178 desc=certs["changelog"],
182 desc=certs["changelog"],
179 rev=rev,
183 rev=rev,
180 parents=self.mtnrun("parents", rev).splitlines(),
184 parents=self.mtnrun("parents", rev).splitlines(),
181 branch=certs["branch"])
185 branch=certs["branch"])
182
186
183 def gettags(self):
187 def gettags(self):
184 tags = {}
188 tags = {}
185 for e in self.mtnrun("tags").split("\n\n"):
189 for e in self.mtnrun("tags").split("\n\n"):
186 m = self.tag_re.match(e)
190 m = self.tag_re.match(e)
187 if m:
191 if m:
188 tags[m.group(1)] = m.group(2)
192 tags[m.group(1)] = m.group(2)
189 return tags
193 return tags
190
194
191 def getchangedfiles(self, rev, i):
195 def getchangedfiles(self, rev, i):
192 # This function is only needed to support --filemap
196 # This function is only needed to support --filemap
193 # ... and we don't support that
197 # ... and we don't support that
194 raise NotImplementedError()
198 raise NotImplementedError()
@@ -1,76 +1,75 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 python -c 'file("bin", "wb").write("a\\x00b")'
36 python -c 'file("bin", "wb").write("a\\x00b")'
37 echo c > c
37 echo c > c
38 mtn add a dir/b c bin
38 mtn add a dir/b c bin
39 mtn ci -m initialize
39 mtn ci -m initialize
40 echo % update monotone working directory
40 echo % update monotone working directory
41 mtn mv a dir/a
41 mtn mv a dir/a
42 echo a >> dir/a
42 echo a >> dir/a
43 echo b >> dir/b
43 echo b >> dir/b
44 mtn drop c
44 mtn drop c
45 python -c 'file("bin", "wb").write("b\\x00c")'
45 python -c 'file("bin", "wb").write("b\\x00c")'
46 mtn ci -m update1
46 mtn ci -m update1
47 cd ..
47 cd ..
48
48
49 echo % convert once
49 echo % convert once
50 hg convert -s mtn repo.mtn
50 hg convert -s mtn repo.mtn
51
51
52 cd workingdir
52 cd workingdir
53 echo e > e
53 echo e > e
54 mtn add e
54 mtn add e
55 mtn drop dir/b
55 mtn drop dir/b
56 mtn mv bin bin2
56 mtn mv bin bin2
57 mtn ci -m update2
57 mtn ci -m update2
58 cd ..
58 cd ..
59
59
60 echo % convert incrementally
60 echo % convert incrementally
61 hg convert -s mtn repo.mtn
61 hg convert -s mtn repo.mtn
62
62
63 glog()
63 glog()
64 {
64 {
65 hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@"
65 hg glog --template '#rev# "#desc|firstline#" files: #files#\n' "$@"
66 }
66 }
67
67
68 cd repo.mtn-hg
68 cd repo.mtn-hg
69 hg up -C
69 hg up -C
70 glog
70 glog
71 echo % manifest
71 echo % manifest
72 # BUG: c and dir/b should not appear here
73 hg manifest
72 hg manifest
74 echo % contents
73 echo % contents
75 cat dir/a
74 cat dir/a
76
75
@@ -1,50 +1,48 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: beginning commit on branch 'com.selenic.test'
8 mtn: beginning commit on branch 'com.selenic.test'
9 mtn: committed revision 803ef0bf815e35b951dbd4310acd1e45e675016e
9 mtn: committed revision 803ef0bf815e35b951dbd4310acd1e45e675016e
10 % update monotone working directory
10 % update monotone working directory
11 mtn: skipping dir, already accounted for in workspace
11 mtn: skipping dir, already accounted for in workspace
12 mtn: renaming a to dir/a in workspace manifest
12 mtn: renaming a to dir/a in workspace manifest
13 mtn: dropping c from workspace manifest
13 mtn: dropping c from workspace manifest
14 mtn: beginning commit on branch 'com.selenic.test'
14 mtn: beginning commit on branch 'com.selenic.test'
15 mtn: committed revision 4daf60753d6fe21a06ce5f716303fe55fd6d3a56
15 mtn: committed revision 4daf60753d6fe21a06ce5f716303fe55fd6d3a56
16 % convert once
16 % convert once
17 assuming destination repo.mtn-hg
17 assuming destination repo.mtn-hg
18 initializing destination repo.mtn-hg repository
18 initializing destination repo.mtn-hg repository
19 scanning source...
19 scanning source...
20 sorting...
20 sorting...
21 converting...
21 converting...
22 1 initialize
22 1 initialize
23 0 update1
23 0 update1
24 mtn: adding e to workspace manifest
24 mtn: adding e to workspace manifest
25 mtn: dropping dir/b from workspace manifest
25 mtn: dropping dir/b from workspace manifest
26 mtn: renaming bin to bin2 in workspace manifest
26 mtn: renaming bin to bin2 in workspace manifest
27 mtn: beginning commit on branch 'com.selenic.test'
27 mtn: beginning commit on branch 'com.selenic.test'
28 mtn: committed revision 6c6977a6ef609ec80e40779f89dbd2772c96de62
28 mtn: committed revision 6c6977a6ef609ec80e40779f89dbd2772c96de62
29 % convert incrementally
29 % convert incrementally
30 assuming destination repo.mtn-hg
30 assuming destination repo.mtn-hg
31 scanning source...
31 scanning source...
32 sorting...
32 sorting...
33 converting...
33 converting...
34 0 update2
34 0 update2
35 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 @ 2 "update2" files: bin bin2 e
36 @ 2 "update2" files: bin bin2 dir/b e
37 |
37 |
38 o 1 "update1" files: a bin dir/a dir/b
38 o 1 "update1" files: a bin c dir/a dir/b
39 |
39 |
40 o 0 "initialize" files: a bin c dir/b
40 o 0 "initialize" files: a bin c dir/b
41
41
42 % manifest
42 % manifest
43 bin2
43 bin2
44 c
45 dir/a
44 dir/a
46 dir/b
47 e
45 e
48 % contents
46 % contents
49 a
47 a
50 a
48 a
General Comments 0
You need to be logged in to leave comments. Login now