##// END OF EJS Templates
convert/mtn: handle subsecond commit dates (issue1616)
Paul Aurich -
r8101:d8229670 default
parent child Browse files
Show More
@@ -1,207 +1,209
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.delete_re = re.compile(space + "delete" + name)
30 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
30 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
31 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
31 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
32
32
33 attr = space + "file" + lines + space + "attr" + space
33 attr = space + "file" + lines + space + "attr" + space
34 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
34 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
35
35
36 # cached data
36 # cached data
37 self.manifest_rev = None
37 self.manifest_rev = None
38 self.manifest = None
38 self.manifest = None
39 self.files = None
39 self.files = None
40 self.dirs = None
40 self.dirs = None
41
41
42 norepo = NoRepo (_("%s does not look like a monotone repo") % path)
42 norepo = NoRepo (_("%s does not look like a monotone repo") % path)
43 if not os.path.exists(path):
43 if not os.path.exists(path):
44 raise norepo
44 raise norepo
45
45
46 checktool('mtn', abort=False)
46 checktool('mtn', abort=False)
47
47
48 # test if there are any revisions
48 # test if there are any revisions
49 self.rev = None
49 self.rev = None
50 try:
50 try:
51 self.getheads()
51 self.getheads()
52 except:
52 except:
53 raise norepo
53 raise norepo
54 self.rev = rev
54 self.rev = rev
55
55
56 def mtnrun(self, *args, **kwargs):
56 def mtnrun(self, *args, **kwargs):
57 kwargs['d'] = self.path
57 kwargs['d'] = self.path
58 return self.run0('automate', *args, **kwargs)
58 return self.run0('automate', *args, **kwargs)
59
59
60 def mtnloadmanifest(self, rev):
60 def mtnloadmanifest(self, rev):
61 if self.manifest_rev == rev:
61 if self.manifest_rev == rev:
62 return
62 return
63 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
63 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
64 self.manifest_rev = rev
64 self.manifest_rev = rev
65 self.files = {}
65 self.files = {}
66 self.dirs = {}
66 self.dirs = {}
67
67
68 for e in self.manifest:
68 for e in self.manifest:
69 m = self.file_re.match(e)
69 m = self.file_re.match(e)
70 if m:
70 if m:
71 attr = ""
71 attr = ""
72 name = m.group(1)
72 name = m.group(1)
73 node = m.group(2)
73 node = m.group(2)
74 if self.attr_execute_re.match(e):
74 if self.attr_execute_re.match(e):
75 attr += "x"
75 attr += "x"
76 self.files[name] = (node, attr)
76 self.files[name] = (node, attr)
77 m = self.dir_re.match(e)
77 m = self.dir_re.match(e)
78 if m:
78 if m:
79 self.dirs[m.group(1)] = True
79 self.dirs[m.group(1)] = True
80
80
81 def mtnisfile(self, name, rev):
81 def mtnisfile(self, name, rev):
82 # 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
83 self.mtnloadmanifest(rev)
83 self.mtnloadmanifest(rev)
84 try:
84 try:
85 self.files[name]
85 self.files[name]
86 return True
86 return True
87 except KeyError:
87 except KeyError:
88 return False
88 return False
89
89
90 def mtnisdir(self, name, rev):
90 def mtnisdir(self, name, rev):
91 self.mtnloadmanifest(rev)
91 self.mtnloadmanifest(rev)
92 try:
92 try:
93 self.dirs[name]
93 self.dirs[name]
94 return True
94 return True
95 except KeyError:
95 except KeyError:
96 return False
96 return False
97
97
98 def mtngetcerts(self, rev):
98 def mtngetcerts(self, rev):
99 certs = {"author":"<missing>", "date":"<missing>",
99 certs = {"author":"<missing>", "date":"<missing>",
100 "changelog":"<missing>", "branch":"<missing>"}
100 "changelog":"<missing>", "branch":"<missing>"}
101 cert_list = self.mtnrun("certs", rev).split('\n\n key "')
101 cert_list = self.mtnrun("certs", rev).split('\n\n key "')
102 for e in cert_list:
102 for e in cert_list:
103 m = self.cert_re.match(e)
103 m = self.cert_re.match(e)
104 if m:
104 if m:
105 name, value = m.groups()
105 name, value = m.groups()
106 value = value.replace(r'\"', '"')
106 value = value.replace(r'\"', '"')
107 value = value.replace(r'\\', '\\')
107 value = value.replace(r'\\', '\\')
108 certs[name] = value
108 certs[name] = value
109 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
110 certs["date"] = certs["date"].split('.')[0]
109 return certs
111 return certs
110
112
111 # implement the converter_source interface:
113 # implement the converter_source interface:
112
114
113 def getheads(self):
115 def getheads(self):
114 if not self.rev:
116 if not self.rev:
115 return self.mtnrun("leaves").splitlines()
117 return self.mtnrun("leaves").splitlines()
116 else:
118 else:
117 return [self.rev]
119 return [self.rev]
118
120
119 def getchanges(self, rev):
121 def getchanges(self, rev):
120 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
122 #revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
121 revision = self.mtnrun("get_revision", rev).split("\n\n")
123 revision = self.mtnrun("get_revision", rev).split("\n\n")
122 files = {}
124 files = {}
123 addedfiles = {}
125 addedfiles = {}
124 renameddirs = []
126 renameddirs = []
125 copies = {}
127 copies = {}
126 for e in revision:
128 for e in revision:
127 m = self.add_file_re.match(e)
129 m = self.add_file_re.match(e)
128 if m:
130 if m:
129 files[m.group(1)] = rev
131 files[m.group(1)] = rev
130 addedfiles[m.group(1)] = rev
132 addedfiles[m.group(1)] = rev
131 m = self.patch_re.match(e)
133 m = self.patch_re.match(e)
132 if m:
134 if m:
133 files[m.group(1)] = rev
135 files[m.group(1)] = rev
134 # Delete/rename is handled later when the convert engine
136 # Delete/rename is handled later when the convert engine
135 # discovers an IOError exception from getfile,
137 # discovers an IOError exception from getfile,
136 # but only if we add the "from" file to the list of changes.
138 # but only if we add the "from" file to the list of changes.
137 m = self.delete_re.match(e)
139 m = self.delete_re.match(e)
138 if m:
140 if m:
139 files[m.group(1)] = rev
141 files[m.group(1)] = rev
140 m = self.rename_re.match(e)
142 m = self.rename_re.match(e)
141 if m:
143 if m:
142 toname = m.group(2)
144 toname = m.group(2)
143 fromname = m.group(1)
145 fromname = m.group(1)
144 if self.mtnisfile(toname, rev):
146 if self.mtnisfile(toname, rev):
145 copies[toname] = fromname
147 copies[toname] = fromname
146 files[toname] = rev
148 files[toname] = rev
147 files[fromname] = rev
149 files[fromname] = rev
148 elif self.mtnisdir(toname, rev):
150 elif self.mtnisdir(toname, rev):
149 renameddirs.append((fromname, toname))
151 renameddirs.append((fromname, toname))
150
152
151 # Directory renames can be handled only once we have recorded
153 # Directory renames can be handled only once we have recorded
152 # all new files
154 # all new files
153 for fromdir, todir in renameddirs:
155 for fromdir, todir in renameddirs:
154 renamed = {}
156 renamed = {}
155 for tofile in self.files:
157 for tofile in self.files:
156 if tofile in addedfiles:
158 if tofile in addedfiles:
157 continue
159 continue
158 if tofile.startswith(todir + '/'):
160 if tofile.startswith(todir + '/'):
159 renamed[tofile] = fromdir + tofile[len(todir):]
161 renamed[tofile] = fromdir + tofile[len(todir):]
160 for tofile, fromfile in renamed.items():
162 for tofile, fromfile in renamed.items():
161 self.ui.debug (_("copying file in renamed dir from '%s' to '%s'")
163 self.ui.debug (_("copying file in renamed dir from '%s' to '%s'")
162 % (fromfile, tofile), '\n')
164 % (fromfile, tofile), '\n')
163 files[tofile] = rev
165 files[tofile] = rev
164 copies[tofile] = fromfile
166 copies[tofile] = fromfile
165 for fromfile in renamed.values():
167 for fromfile in renamed.values():
166 files[fromfile] = rev
168 files[fromfile] = rev
167
169
168 return (files.items(), copies)
170 return (files.items(), copies)
169
171
170 def getmode(self, name, rev):
172 def getmode(self, name, rev):
171 self.mtnloadmanifest(rev)
173 self.mtnloadmanifest(rev)
172 try:
174 try:
173 node, attr = self.files[name]
175 node, attr = self.files[name]
174 return attr
176 return attr
175 except KeyError:
177 except KeyError:
176 return ""
178 return ""
177
179
178 def getfile(self, name, rev):
180 def getfile(self, name, rev):
179 if not self.mtnisfile(name, rev):
181 if not self.mtnisfile(name, rev):
180 raise IOError() # file was deleted or renamed
182 raise IOError() # file was deleted or renamed
181 try:
183 try:
182 return self.mtnrun("get_file_of", name, r=rev)
184 return self.mtnrun("get_file_of", name, r=rev)
183 except:
185 except:
184 raise IOError() # file was deleted or renamed
186 raise IOError() # file was deleted or renamed
185
187
186 def getcommit(self, rev):
188 def getcommit(self, rev):
187 certs = self.mtngetcerts(rev)
189 certs = self.mtngetcerts(rev)
188 return commit(
190 return commit(
189 author=certs["author"],
191 author=certs["author"],
190 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
192 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
191 desc=certs["changelog"],
193 desc=certs["changelog"],
192 rev=rev,
194 rev=rev,
193 parents=self.mtnrun("parents", rev).splitlines(),
195 parents=self.mtnrun("parents", rev).splitlines(),
194 branch=certs["branch"])
196 branch=certs["branch"])
195
197
196 def gettags(self):
198 def gettags(self):
197 tags = {}
199 tags = {}
198 for e in self.mtnrun("tags").split("\n\n"):
200 for e in self.mtnrun("tags").split("\n\n"):
199 m = self.tag_re.match(e)
201 m = self.tag_re.match(e)
200 if m:
202 if m:
201 tags[m.group(1)] = m.group(2)
203 tags[m.group(1)] = m.group(2)
202 return tags
204 return tags
203
205
204 def getchangedfiles(self, rev, i):
206 def getchangedfiles(self, rev, i):
205 # This function is only needed to support --filemap
207 # This function is only needed to support --filemap
206 # ... and we don't support that
208 # ... and we don't support that
207 raise NotImplementedError()
209 raise NotImplementedError()
General Comments 0
You need to be logged in to leave comments. Login now