Plugins/VcsPlugins/vcsMercurial/HgLogDialog.py

changeset 209
94e34c41e823
parent 207
3f889378dede
child 210
b1a204f22291
equal deleted inserted replaced
208:26ed553ad4fb 209:94e34c41e823
62 self.__sourceChanged) 62 self.__sourceChanged)
63 63
64 self.revisions = [] # stack of remembered revisions 64 self.revisions = [] # stack of remembered revisions
65 self.revString = self.trUtf8('Revision') 65 self.revString = self.trUtf8('Revision')
66 66
67 self.buf = [] # buffer for stdout 67 self.logEntries = [] # list of log entries
68 self.lastLogEntry = {}
69 self.fileCopies = {}
70 self.endInitialText = False
71 self.initialText = ""
72
68 self.diff = None 73 self.diff = None
69 74
70 def closeEvent(self, e): 75 def closeEvent(self, e):
71 """ 76 """
72 Private slot implementing a close event handler. 77 Private slot implementing a close event handler.
94 self.intercept = False 99 self.intercept = False
95 self.filename = fn 100 self.filename = fn
96 self.dname, self.fname = self.vcs.splitPath(fn) 101 self.dname, self.fname = self.vcs.splitPath(fn)
97 102
98 # find the root of the repo 103 # find the root of the repo
99 repodir = self.dname 104 self.repodir = self.dname
100 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)): 105 while not os.path.isdir(os.path.join(self.repodir, self.vcs.adminDir)):
101 repodir = os.path.dirname(repodir) 106 self.repodir = os.path.dirname(self.repodir)
102 if repodir == os.sep: 107 if self.repodir == os.sep:
103 return 108 return
104 109
105 self.process.kill() 110 self.process.kill()
106 111
107 self.activateWindow() 112 self.activateWindow()
124 project = e5App().getObject("Project") 129 project = e5App().getObject("Project")
125 self.vcs.bundleFile = os.path.join( 130 self.vcs.bundleFile = os.path.join(
126 project.getProjectManagementDir(), "hg-bundle.hg") 131 project.getProjectManagementDir(), "hg-bundle.hg")
127 args.append('--bundle') 132 args.append('--bundle')
128 args.append(self.vcs.bundleFile) 133 args.append(self.vcs.bundleFile)
129 if self.fname != "." or self.dname != repodir: 134 if self.fname != "." or self.dname != self.repodir:
130 args.append(self.filename) 135 args.append(self.filename)
131 136
132 self.process.setWorkingDirectory(repodir) 137 self.process.setWorkingDirectory(self.repodir)
133 138
134 self.process.start('hg', args) 139 self.process.start('hg', args)
135 procStarted = self.process.waitForStarted() 140 procStarted = self.process.waitForStarted()
136 if not procStarted: 141 if not procStarted:
137 self.inputGroup.setEnabled(False) 142 self.inputGroup.setEnabled(False)
140 self.trUtf8( 145 self.trUtf8(
141 'The process {0} could not be started. ' 146 'The process {0} could not be started. '
142 'Ensure, that it is in the search path.' 147 'Ensure, that it is in the search path.'
143 ).format('hg')) 148 ).format('hg'))
144 149
150 def __getParents(self, rev):
151 """
152 Private method to get the parents of the currently viewed file/directory.
153
154 @param rev revision number to get parents for (string)
155 @return list of parent revisions (list of strings)
156 """
157 errMsg = ""
158 parents = []
159
160 process = QProcess()
161 args = []
162 args.append("parents")
163 if self.commandMode == "incoming" and self.vcs.bundleFile:
164 args.append("--repository")
165 args.append(self.vcs.bundleFile)
166 args.append("--template")
167 args.append("{rev}:{node|short}\n")
168 args.append("-r")
169 args.append(rev)
170 args.append(self.filename)
171
172 process.setWorkingDirectory(self.repodir)
173 process.start('hg', args)
174 procStarted = process.waitForStarted()
175 if procStarted:
176 finished = process.waitForFinished(30000)
177 if finished and process.exitCode() == 0:
178 output = \
179 str(process.readAllStandardOutput(),
180 Preferences.getSystem("IOEncoding"),
181 'replace')
182 parents = [p for p in output.strip().splitlines()]
183 else:
184 if not finished:
185 errMsg = self.trUtf8("The hg process did not finish within 30s.")
186 else:
187 errMsg = self.trUtf8("Could not start the hg executable.")
188
189 if errMsg:
190 QMessageBox.critical(self,
191 self.trUtf8("Mercurial Error"),
192 errMsg)
193
194 return parents
195
145 def __procFinished(self, exitCode, exitStatus): 196 def __procFinished(self, exitCode, exitStatus):
146 """ 197 """
147 Private slot connected to the finished signal. 198 Private slot connected to the finished signal.
148 199
149 @param exitCode exit code of the process (integer) 200 @param exitCode exit code of the process (integer)
152 self.inputGroup.setEnabled(False) 203 self.inputGroup.setEnabled(False)
153 self.inputGroup.hide() 204 self.inputGroup.hide()
154 205
155 self.contents.clear() 206 self.contents.clear()
156 207
157 if not self.buf: 208 if not self.logEntries:
158 self.errors.append(self.trUtf8("No log available for '{0}'")\ 209 self.errors.append(self.trUtf8("No log available for '{0}'")\
159 .format(self.filename)) 210 .format(self.filename))
160 self.errorGroup.show() 211 self.errorGroup.show()
161 return 212 return
162 213
163 hasInitialText = 0 # three states flag (-1, 0, 1) 214 if self.initialText:
164 lvers = 1 215 self.contents.insertHtml(Utilities.html_encode(self.initialText.strip()))
165 fileCopies = {} 216 self.contents.insertHtml('<br />\n')
166 for s in self.buf: 217 self.contents.insertHtml('{0}<br/>\n'.format(80 * "="))
218
219 for entry in self.logEntries:
220 fileCopies = {}
221 for fentry in entry["file_copies"].split(", "):
222 newName, oldName = entry[:-1].split(" (")
223 fileCopies[newName] = oldName
224
225 rev, hexRev = entry["change"].split(":")
226 dstr = '<p><b>{0} {1}</b>'.format(self.revString, entry["change"])
227 parents = self.__getParents(rev)
228 for parent in parents:
229 url = QUrl()
230 url.setScheme("file")
231 url.setPath(self.filename)
232 query = QByteArray()
233 query.append(parent.split(":")[0]).append('_').append(rev)
234 url.setEncodedQuery(query)
235 dstr += ' [<a href="{0}" name="{1}" id="{1}">{2}</a>]'.format(
236 url.toString(),
237 str(query, encoding="ascii"),
238 self.trUtf8('diff to {0}').format(parent),
239 )
240 dstr += '<br />\n'
241 self.contents.insertHtml(dstr)
242
243 self.contents.insertHtml(self.trUtf8("Branches: {0}<br />\n")\
244 .format(entry["branches"]))
245
246 self.contents.insertHtml(self.trUtf8("Tags: {0}<br />\n")\
247 .format(entry["tags"]))
248
249 self.contents.insertHtml(self.trUtf8("Parents: {0}<br />\n")\
250 .format(entry["parents"]))
251
252 self.contents.insertHtml(self.trUtf8('<i>Author: {0}</i><br />\n')\
253 .format(entry["user"]))
254
255 date, time = entry["date"].split()[:2]
256 self.contents.insertHtml(self.trUtf8('<i>Date: {0}, {1}</i><br />\n')\
257 .format(date, time))
258
259 for line in entry["description"]:
260 self.contents.insertHtml(Utilities.html_encode(line.strip()))
261 self.contents.insertHtml('<br />\n')
262
263 if entry["file_adds"]:
264 self.contents.insertHtml('<br />\n')
265 for f in entry["file_adds"].strip().split(", "):
266 if f in fileCopies:
267 self.contents.insertHtml(
268 self.trUtf8('Added {0} (copied from {1})<br />\n')\
269 .format(Utilities.html_encode(f),
270 Utilities.html_encode(fileCopies[f])))
271 else:
272 self.contents.insertHtml(
273 self.trUtf8('Added {0}<br />\n')\
274 .format(Utilities.html_encode(f)))
275
276 if entry["files_mods"]:
277 self.contents.insertHtml('<br />\n')
278 for f in entry["files_mods"].strip().split(", "):
279 self.contents.insertHtml(
280 self.trUtf8('Modified {0}<br />\n')\
281 .format(Utilities.html_encode(f)))
282
283 if entry["file_dels"]:
284 self.contents.insertHtml('<br />\n')
285 for f in entry["file_dels"].strip().split(", "):
286 self.contents.insertHtml(
287 self.trUtf8('Deleted {0}<br />\n')\
288 .format(Utilities.html_encode(f)))
289
290 self.contents.insertHtml('</p>{0}<br/>\n'.format(80 * "="))
291
292 tc = self.contents.textCursor()
293 tc.movePosition(QTextCursor.Start)
294 self.contents.setTextCursor(tc)
295 self.contents.ensureCursorVisible()
296
297 def __readStdout(self):
298 """
299 Private slot to handle the readyReadStandardOutput signal.
300
301 It reads the output of the process and inserts it into a buffer.
302 """
303 self.process.setReadChannel(QProcess.StandardOutput)
304
305 while self.process.canReadLine():
306 s = str(self.process.readLine(),
307 Preferences.getSystem("IOEncoding"),
308 'replace')
309 ## self.buf.append(line)
310 ##
311 ## if line.startswith("change|"):
312 ## ver = line[7:]
313 ## # save revision number for later use
314 ## self.revisions.append(ver)
315
167 if s == "@@@\n": 316 if s == "@@@\n":
168 self.contents.insertHtml('</p>{0}<br/>\n'.format(80 * "=")) 317 self.logEntries.append(self.lastLogEntry)
169 fileCopies = {} 318 self.lastLogEntry = {}
319 self.fileCopies = {}
170 else: 320 else:
171 try: 321 try:
172 key, value = s.split("|", 1) 322 key, value = s.split("|", 1)
173 except ValueError: 323 except ValueError:
174 key = "" 324 key = ""
175 value = s 325 value = s
176 if key == "change": 326 if key == "change":
177 if hasInitialText == 1: 327 self.endInitialText = True
178 self.contents.insertHtml('{0}<br/>\n'.format(80 * "=")) 328 if key in ("change", "branches", "tags", "parents", "user",
179 hasInitialText = -1 329 "date", "file_copies", "file_adds", "files_mods",
180 rev, hexRev = value.split(":") 330 "file_dels"):
181 dstr = '<p><b>{0} {1}</b>'.format(self.revString, value) 331 self.lastLogEntry[key] = value.strip()
182 try:
183 lv = self.revisions[lvers]
184 lvers += 1
185 except IndexError:
186 lv = str(int(rev) - 1)
187 if rev != "0":
188 url = QUrl()
189 url.setScheme("file")
190 url.setPath(self.filename)
191 query = QByteArray()
192 query.append(lv.split(":")[0]).append('_').append(rev)
193 url.setEncodedQuery(query)
194 dstr += ' [<a href="{0}" name="{1}" id="{1}">{2}</a>]'.format(
195 url.toString(),
196 str(query, encoding="ascii"),
197 self.trUtf8('diff to {0}').format(lv),
198 )
199 dstr += '<br />\n'
200 self.contents.insertHtml(dstr)
201 elif key == "branches":
202 if value.strip():
203 self.contents.insertHtml(self.trUtf8("Branches: {0}<br />\n")\
204 .format(value.strip()))
205 elif key == "tags":
206 if value.strip():
207 self.contents.insertHtml(self.trUtf8("Tags: {0}<br />\n")\
208 .format(value.strip()))
209 elif key == "parents":
210 if value.strip():
211 self.contents.insertHtml(self.trUtf8("Parents: {0}<br />\n")\
212 .format(value.strip()))
213 elif key == "user":
214 dstr = self.contents.insertHtml(
215 self.trUtf8('<i>Author: {0}</i><br />\n').format(value.strip()))
216 elif key == "date":
217 date, time = value.strip().split()[:2]
218 dstr = self.contents.insertHtml(
219 self.trUtf8('<i>Date: {0}, {1}</i><br />\n')\
220 .format(date, time))
221 elif key == "description": 332 elif key == "description":
222 self.contents.insertHtml(Utilities.html_encode(value.strip())) 333 self.lastLogEntry[key] = [value.strip()]
223 self.contents.insertHtml('<br />\n')
224 elif key == "file_copies":
225 if value.strip():
226 for entry in value.strip().split(", "):
227 newName, oldName = entry[:-1].split(" (")
228 fileCopies[newName] = oldName
229 elif key == "file_adds":
230 if value.strip():
231 self.contents.insertHtml('<br />\n')
232 for f in value.strip().split(", "):
233 if f in fileCopies:
234 self.contents.insertHtml(
235 self.trUtf8('Added {0} (copied from {1})<br />\n')\
236 .format(Utilities.html_encode(f),
237 Utilities.html_encode(fileCopies[f])))
238 else:
239 self.contents.insertHtml(
240 self.trUtf8('Added {0}<br />\n')\
241 .format(Utilities.html_encode(f)))
242 elif key == "files_mods":
243 if value.strip():
244 self.contents.insertHtml('<br />\n')
245 for f in value.strip().split(", "):
246 self.contents.insertHtml(
247 self.trUtf8('Modified {0}<br />\n')\
248 .format(Utilities.html_encode(f)))
249 elif key == "file_dels":
250 if value.strip():
251 self.contents.insertHtml('<br />\n')
252 for f in value.strip().split(", "):
253 self.contents.insertHtml(
254 self.trUtf8('Deleted {0}<br />\n')\
255 .format(Utilities.html_encode(f)))
256 else: 334 else:
257 if value.strip(): 335 if self.endInitialText:
258 self.contents.insertHtml(Utilities.html_encode(value.strip())) 336 self.lastLogEntry["description"].append(value.strip())
259 self.contents.insertHtml('<br />\n') 337 else:
260 if hasInitialText == 0: 338 self.initialText.append(value)
261 hasInitialText = 1
262
263 tc = self.contents.textCursor()
264 tc.movePosition(QTextCursor.Start)
265 self.contents.setTextCursor(tc)
266 self.contents.ensureCursorVisible()
267
268 def __readStdout(self):
269 """
270 Private slot to handle the readyReadStandardOutput signal.
271
272 It reads the output of the process and inserts it into a buffer.
273 """
274 self.process.setReadChannel(QProcess.StandardOutput)
275
276 while self.process.canReadLine():
277 line = str(self.process.readLine(),
278 Preferences.getSystem("IOEncoding"),
279 'replace')
280 self.buf.append(line)
281
282 if line.startswith("change|"):
283 ver = line[7:]
284 # save revision number for later use
285 self.revisions.append(ver)
286 339
287 def __readStderr(self): 340 def __readStderr(self):
288 """ 341 """
289 Private slot to handle the readyReadStandardError signal. 342 Private slot to handle the readyReadStandardError signal.
290 343

eric ide

mercurial