|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the VCS status monitor thread class for Mercurial. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 try: |
|
12 str = unicode |
|
13 except NameError: |
|
14 pass |
|
15 |
|
16 from PyQt5.QtCore import QProcess |
|
17 |
|
18 from VCS.StatusMonitorThread import VcsStatusMonitorThread |
|
19 |
|
20 |
|
21 class HgStatusMonitorThread(VcsStatusMonitorThread): |
|
22 """ |
|
23 Class implementing the VCS status monitor thread class for Mercurial. |
|
24 """ |
|
25 def __init__(self, interval, project, vcs, parent=None): |
|
26 """ |
|
27 Constructor |
|
28 |
|
29 @param interval new interval in seconds (integer) |
|
30 @param project reference to the project object (Project) |
|
31 @param vcs reference to the version control object |
|
32 @param parent reference to the parent object (QObject) |
|
33 """ |
|
34 VcsStatusMonitorThread.__init__(self, interval, project, vcs, parent) |
|
35 |
|
36 self.__client = None |
|
37 self.__useCommandLine = False |
|
38 |
|
39 def _performMonitor(self): |
|
40 """ |
|
41 Protected method implementing the monitoring action. |
|
42 |
|
43 This method populates the statusList member variable |
|
44 with a list of strings giving the status in the first column and the |
|
45 path relative to the project directory starting with the third column. |
|
46 The allowed status flags are: |
|
47 <ul> |
|
48 <li>"A" path was added but not yet comitted</li> |
|
49 <li>"M" path has local changes</li> |
|
50 <li>"O" path was removed</li> |
|
51 <li>"R" path was deleted and then re-added</li> |
|
52 <li>"U" path needs an update</li> |
|
53 <li>"Z" path contains a conflict</li> |
|
54 <li>" " path is back at normal</li> |
|
55 </ul> |
|
56 |
|
57 @return tuple of flag indicating successful operation (boolean) and |
|
58 a status message in case of non successful operation (string) |
|
59 """ |
|
60 self.shouldUpdate = False |
|
61 |
|
62 self.__initClient() |
|
63 |
|
64 # step 1: get overall status |
|
65 args = self.vcs.initCommand("status") |
|
66 args.append('--noninteractive') |
|
67 args.append('--all') |
|
68 |
|
69 output = "" |
|
70 error = "" |
|
71 if self.__client: |
|
72 output, error = self.__client.runcommand(args) |
|
73 else: |
|
74 process = QProcess() |
|
75 process.setWorkingDirectory(self.projectDir) |
|
76 process.start('hg', args) |
|
77 procStarted = process.waitForStarted(5000) |
|
78 if procStarted: |
|
79 finished = process.waitForFinished(300000) |
|
80 if finished and process.exitCode() == 0: |
|
81 output = str(process.readAllStandardOutput(), |
|
82 self.vcs.getEncoding(), 'replace') |
|
83 else: |
|
84 process.kill() |
|
85 process.waitForFinished() |
|
86 error = str(process.readAllStandardError(), |
|
87 self.vcs.getEncoding(), 'replace') |
|
88 else: |
|
89 process.kill() |
|
90 process.waitForFinished() |
|
91 error = self.tr("Could not start the Mercurial process.") |
|
92 |
|
93 if error: |
|
94 return False, error |
|
95 |
|
96 states = {} |
|
97 for line in output.splitlines(): |
|
98 if not line.startswith(" "): |
|
99 flag, name = line.split(" ", 1) |
|
100 if flag in "AMR": |
|
101 if flag == "R": |
|
102 status = "O" |
|
103 else: |
|
104 status = flag |
|
105 states[name] = status |
|
106 |
|
107 # step 2: get conflicting changes |
|
108 args = self.vcs.initCommand("resolve") |
|
109 args.append('--list') |
|
110 |
|
111 output = "" |
|
112 error = "" |
|
113 if self.__client: |
|
114 output, error = self.__client.runcommand(args) |
|
115 else: |
|
116 process.setWorkingDirectory(self.projectDir) |
|
117 process.start('hg', args) |
|
118 procStarted = process.waitForStarted(5000) |
|
119 if procStarted: |
|
120 finished = process.waitForFinished(300000) |
|
121 if finished and process.exitCode() == 0: |
|
122 output = str(process.readAllStandardOutput(), |
|
123 self.vcs.getEncoding(), 'replace') |
|
124 |
|
125 for line in output.splitlines(): |
|
126 flag, name = line.split(" ", 1) |
|
127 if flag == "U": |
|
128 states[name] = "Z" # conflict |
|
129 |
|
130 # step 3: collect the status to be reported back |
|
131 for name in states: |
|
132 try: |
|
133 if self.reportedStates[name] != states[name]: |
|
134 self.statusList.append( |
|
135 "{0} {1}".format(states[name], name)) |
|
136 except KeyError: |
|
137 self.statusList.append("{0} {1}".format(states[name], name)) |
|
138 for name in self.reportedStates.keys(): |
|
139 if name not in states: |
|
140 self.statusList.append(" {0}".format(name)) |
|
141 self.reportedStates = states |
|
142 |
|
143 return True, \ |
|
144 self.tr("Mercurial status checked successfully") |
|
145 |
|
146 def _getInfo(self): |
|
147 """ |
|
148 Protected method implementing the real info action. |
|
149 |
|
150 This method should be overridden and create a short info message to be |
|
151 shown in the main window status bar right next to the status indicator. |
|
152 |
|
153 @return short info message |
|
154 @rtype str |
|
155 """ |
|
156 self.__initClient() |
|
157 |
|
158 args = self.vcs.initCommand("identify") |
|
159 args.append('--num') |
|
160 args.append('--id') |
|
161 args.append('--branch') |
|
162 |
|
163 output = "" |
|
164 error = "" |
|
165 if self.__client: |
|
166 output, error = self.__client.runcommand(args) |
|
167 else: |
|
168 process = QProcess() |
|
169 process.setWorkingDirectory(self.projectDir) |
|
170 process.start('hg', args) |
|
171 procStarted = process.waitForStarted(5000) |
|
172 if procStarted: |
|
173 finished = process.waitForFinished(300000) |
|
174 if finished and process.exitCode() == 0: |
|
175 output = str(process.readAllStandardOutput(), |
|
176 self.vcs.getEncoding(), 'replace') |
|
177 else: |
|
178 process.kill() |
|
179 process.waitForFinished() |
|
180 error = str(process.readAllStandardError(), |
|
181 self.vcs.getEncoding(), 'replace') |
|
182 else: |
|
183 process.kill() |
|
184 process.waitForFinished() |
|
185 error = self.tr("Could not start the Mercurial process.") |
|
186 |
|
187 if error: |
|
188 # ignore errors |
|
189 return "" |
|
190 |
|
191 globalRev, localRev, branch = output.splitlines()[0].split() |
|
192 if globalRev.endswith("+"): |
|
193 globalRev = globalRev[:-1] |
|
194 if localRev.endswith("+"): |
|
195 localRev = localRev[:-1] |
|
196 |
|
197 return self.tr("{0} / {1}:{2}", "branch, local id, global id").format( |
|
198 branch, localRev, globalRev) |
|
199 |
|
200 def _shutdown(self): |
|
201 """ |
|
202 Protected method performing shutdown actions. |
|
203 """ |
|
204 if self.__client: |
|
205 self.__client.stopServer() |
|
206 |
|
207 def __initClient(self): |
|
208 """ |
|
209 Private method to initialize the Mercurial client. |
|
210 """ |
|
211 if self.__client is None and not self.__useCommandLine: |
|
212 from .HgClient import HgClient |
|
213 client = HgClient(self.projectDir, "utf-8", self.vcs) |
|
214 ok, err = client.startServer() |
|
215 if ok: |
|
216 self.__client = client |
|
217 else: |
|
218 self.__useCommandLine = True |