src/eric7/Plugins/VcsPlugins/vcsGit/GitDiffGenerator.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
18 18
19 19
20 class GitDiffGenerator(QObject): 20 class GitDiffGenerator(QObject):
21 """ 21 """
22 Class implementing the generation of output of the git diff command. 22 Class implementing the generation of output of the git diff command.
23 23
24 @signal finished() emitted when all processes have finished 24 @signal finished() emitted when all processes have finished
25 """ 25 """
26
26 finished = pyqtSignal() 27 finished = pyqtSignal()
27 28
28 def __init__(self, vcs, parent=None): 29 def __init__(self, vcs, parent=None):
29 """ 30 """
30 Constructor 31 Constructor
31 32
32 @param vcs reference to the vcs object 33 @param vcs reference to the vcs object
33 @param parent parent widget (QWidget) 34 @param parent parent widget (QWidget)
34 """ 35 """
35 super().__init__(parent) 36 super().__init__(parent)
36 37
37 self.vcs = vcs 38 self.vcs = vcs
38 39
39 self.__process = EricOverrideCursorProcess() 40 self.__process = EricOverrideCursorProcess()
40 self.__process.finished.connect(self.__procFinished) 41 self.__process.finished.connect(self.__procFinished)
41 self.__process.readyReadStandardOutput.connect( 42 self.__process.readyReadStandardOutput.connect(
42 lambda: self.__readStdout(self.__process)) 43 lambda: self.__readStdout(self.__process)
44 )
43 self.__process.readyReadStandardError.connect( 45 self.__process.readyReadStandardError.connect(
44 lambda: self.__readStderr(self.__process)) 46 lambda: self.__readStderr(self.__process)
45 47 )
48
46 self.__process2 = EricOverrideCursorProcess() 49 self.__process2 = EricOverrideCursorProcess()
47 self.__process2.finished.connect(self.__procFinished) 50 self.__process2.finished.connect(self.__procFinished)
48 self.__process2.readyReadStandardOutput.connect( 51 self.__process2.readyReadStandardOutput.connect(
49 lambda: self.__readStdout(self.__process2)) 52 lambda: self.__readStdout(self.__process2)
53 )
50 self.__process2.readyReadStandardError.connect( 54 self.__process2.readyReadStandardError.connect(
51 lambda: self.__readStderr(self.__process2)) 55 lambda: self.__readStderr(self.__process2)
52 56 )
57
53 def stopProcesses(self): 58 def stopProcesses(self):
54 """ 59 """
55 Public slot to stop the diff processes. 60 Public slot to stop the diff processes.
56 """ 61 """
57 for process in [self.__process, self.__process2]: 62 for process in [self.__process, self.__process2]:
58 if ( 63 if (
59 process is not None and 64 process is not None
60 process.state() != QProcess.ProcessState.NotRunning 65 and process.state() != QProcess.ProcessState.NotRunning
61 ): 66 ):
62 process.terminate() 67 process.terminate()
63 QTimer.singleShot(2000, process.kill) 68 QTimer.singleShot(2000, process.kill)
64 process.waitForFinished(3000) 69 process.waitForFinished(3000)
65 70
66 def start(self, fn, versions=None, diffMode="work2repo", stashName=""): 71 def start(self, fn, versions=None, diffMode="work2repo", stashName=""):
67 """ 72 """
68 Public slot to start the git diff command. 73 Public slot to start the git diff command.
69 74
70 @param fn filename to be diffed (string) 75 @param fn filename to be diffed (string)
71 @param versions list of versions to be diffed (list of up to 2 strings 76 @param versions list of versions to be diffed (list of up to 2 strings
72 or None) 77 or None)
73 @param diffMode indication for the type of diff to be performed ( 78 @param diffMode indication for the type of diff to be performed (
74 'work2repo' compares the working tree with the HEAD commit, 79 'work2repo' compares the working tree with the HEAD commit,
80 @param stashName name of the stash to show a diff for (string) 85 @param stashName name of the stash to show a diff for (string)
81 @return flag indicating the start status (boolean) 86 @return flag indicating the start status (boolean)
82 @exception ValueError raised to indicate a bad value for the 'diffMode' 87 @exception ValueError raised to indicate a bad value for the 'diffMode'
83 parameter. 88 parameter.
84 """ 89 """
85 if diffMode not in ["work2repo", "work2stage", "stage2repo", 90 if diffMode not in [
86 "work2stage2repo", "stash"]: 91 "work2repo",
92 "work2stage",
93 "stage2repo",
94 "work2stage2repo",
95 "stash",
96 ]:
87 raise ValueError("Bad value for 'diffMode' parameter.") 97 raise ValueError("Bad value for 'diffMode' parameter.")
88 98
89 self.__output1 = [] 99 self.__output1 = []
90 self.__output2 = [] 100 self.__output2 = []
91 self.__errors = [] 101 self.__errors = []
92 self.__fileSeparators = [] 102 self.__fileSeparators = []
93 args2 = [] 103 args2 = []
94 104
95 self.__ioEncoding = Preferences.getSystem("IOEncoding") 105 self.__ioEncoding = Preferences.getSystem("IOEncoding")
96 106
97 if diffMode in ["work2repo", "work2stage", "stage2repo", 107 if diffMode in ["work2repo", "work2stage", "stage2repo", "work2stage2repo"]:
98 "work2stage2repo"]:
99 args = self.vcs.initCommand("diff") 108 args = self.vcs.initCommand("diff")
100 args.append("--patch") 109 args.append("--patch")
101 args.append("--find-copies-harder") 110 args.append("--find-copies-harder")
102 111
103 if versions is not None: 112 if versions is not None:
104 for version in versions: 113 for version in versions:
105 if version: 114 if version:
106 args.append(version) 115 args.append(version)
107 else: 116 else:
111 args2.append("--") 120 args2.append("--")
112 elif diffMode == "stage2repo": 121 elif diffMode == "stage2repo":
113 args.append("--cached") 122 args.append("--cached")
114 elif diffMode == "work2repo": 123 elif diffMode == "work2repo":
115 args.append("HEAD") 124 args.append("HEAD")
116 125
117 args.append("--") 126 args.append("--")
118 if isinstance(fn, list): 127 if isinstance(fn, list):
119 dname, fnames = self.vcs.splitPathList(fn) 128 dname, fnames = self.vcs.splitPathList(fn)
120 self.vcs.addArguments(args, fn) 129 self.vcs.addArguments(args, fn)
121 if args2: 130 if args2:
130 args = self.vcs.initCommand("stash") 139 args = self.vcs.initCommand("stash")
131 args.append("show") 140 args.append("show")
132 args.append("--patch") 141 args.append("--patch")
133 if stashName: 142 if stashName:
134 args.append(stashName) 143 args.append(stashName)
135 144
136 # find the root of the repo 145 # find the root of the repo
137 repodir = dname 146 repodir = dname
138 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)): 147 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
139 repodir = os.path.dirname(repodir) 148 repodir = os.path.dirname(repodir)
140 if os.path.splitdrive(repodir)[1] == os.sep: 149 if os.path.splitdrive(repodir)[1] == os.sep:
141 return False 150 return False
142 151
143 self.__process.kill() 152 self.__process.kill()
144 self.__process.setWorkingDirectory(repodir) 153 self.__process.setWorkingDirectory(repodir)
145 self.__process.start('git', args) 154 self.__process.start("git", args)
146 procStarted = self.__process.waitForStarted(5000) 155 procStarted = self.__process.waitForStarted(5000)
147 if not procStarted: 156 if not procStarted:
148 return False 157 return False
149 158
150 if diffMode == "work2stage2repo": 159 if diffMode == "work2stage2repo":
151 self.__process2.kill() 160 self.__process2.kill()
152 self.__process2.setWorkingDirectory(repodir) 161 self.__process2.setWorkingDirectory(repodir)
153 self.__process2.start('git', args2) 162 self.__process2.start("git", args2)
154 procStarted = self.__process2.waitForStarted(5000) 163 procStarted = self.__process2.waitForStarted(5000)
155 if not procStarted: 164 if not procStarted:
156 return False 165 return False
157 166
158 return True 167 return True
159 168
160 def __procFinished(self, exitCode, exitStatus): 169 def __procFinished(self, exitCode, exitStatus):
161 """ 170 """
162 Private slot connected to the finished signal. 171 Private slot connected to the finished signal.
163 172
164 @param exitCode exit code of the process (integer) 173 @param exitCode exit code of the process (integer)
165 @param exitStatus exit status of the process (QProcess.ExitStatus) 174 @param exitStatus exit status of the process (QProcess.ExitStatus)
166 """ 175 """
167 if ( 176 if (
168 self.__process.state() == QProcess.ProcessState.NotRunning and 177 self.__process.state() == QProcess.ProcessState.NotRunning
169 self.__process2.state() == QProcess.ProcessState.NotRunning 178 and self.__process2.state() == QProcess.ProcessState.NotRunning
170 ): 179 ):
171 self.finished.emit() 180 self.finished.emit()
172 181
173 def getResult(self): 182 def getResult(self):
174 """ 183 """
175 Public method to return the result data. 184 Public method to return the result data.
176 185
177 @return tuple of lists of string containing lines of the diff, the diff 186 @return tuple of lists of string containing lines of the diff, the diff
178 between stage and repo for 'work2stage2repo' mode (empty 187 between stage and repo for 'work2stage2repo' mode (empty
179 otherwise), the list of errors and a list of tuples of filenames 188 otherwise), the list of errors and a list of tuples of filenames
180 and the line into the diff output. 189 and the line into the diff output.
181 """ 190 """
182 return (self.__output1, self.__output2, self.__errors, 191 return (self.__output1, self.__output2, self.__errors, self.__fileSeparators)
183 self.__fileSeparators) 192
184
185 def __processFileLine(self, line, isTopDiff): 193 def __processFileLine(self, line, isTopDiff):
186 """ 194 """
187 Private slot to process a line giving the old/new file. 195 Private slot to process a line giving the old/new file.
188 196
189 @param line line to be processed (string) 197 @param line line to be processed (string)
190 @param isTopDiff flag indicating to show the output in the top 198 @param isTopDiff flag indicating to show the output in the top
191 output widget (boolean) 199 output widget (boolean)
192 """ 200 """
193 prefix, filenames = line.split(" a/", 1) 201 prefix, filenames = line.split(" a/", 1)
194 oldFile, newFile = filenames.split(" b/", 1) 202 oldFile, newFile = filenames.split(" b/", 1)
195 if isTopDiff: 203 if isTopDiff:
196 self.__fileSeparators.append((oldFile.strip(), newFile.strip(), 204 self.__fileSeparators.append(
197 len(self.__output1), -2)) 205 (oldFile.strip(), newFile.strip(), len(self.__output1), -2)
206 )
198 else: 207 else:
199 self.__fileSeparators.append((oldFile.strip(), newFile.strip(), 208 self.__fileSeparators.append(
200 -2, len(self.__output2))) 209 (oldFile.strip(), newFile.strip(), -2, len(self.__output2))
201 210 )
211
202 def __processLine(self, line, isTopDiff): 212 def __processLine(self, line, isTopDiff):
203 """ 213 """
204 Private method to process one line of output. 214 Private method to process one line of output.
205 215
206 @param line output line to process (string) 216 @param line output line to process (string)
207 @param isTopDiff flag indicating to show the output in the top 217 @param isTopDiff flag indicating to show the output in the top
208 output widget (boolean) 218 output widget (boolean)
209 """ 219 """
210 if line.startswith("diff --git"): 220 if line.startswith("diff --git"):
211 self.__processFileLine(line, isTopDiff) 221 self.__processFileLine(line, isTopDiff)
212 222
213 if isTopDiff: 223 if isTopDiff:
214 self.__output1.append(line) 224 self.__output1.append(line)
215 else: 225 else:
216 self.__output2.append(line) 226 self.__output2.append(line)
217 227
218 def __readStdout(self, process): 228 def __readStdout(self, process):
219 """ 229 """
220 Private slot to handle the readyReadStandardOutput signal. 230 Private slot to handle the readyReadStandardOutput signal.
221 231
222 It reads the output of the process, formats it and inserts it into 232 It reads the output of the process, formats it and inserts it into
223 the contents pane. 233 the contents pane.
224 234
225 @param process reference to the process providing output 235 @param process reference to the process providing output
226 @type QProcess 236 @type QProcess
227 """ 237 """
228 process.setReadChannel(QProcess.ProcessChannel.StandardOutput) 238 process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
229 239
230 isTopDiff = process == self.__process 240 isTopDiff = process == self.__process
231 241
232 while process.canReadLine(): 242 while process.canReadLine():
233 line = str(process.readLine(), self.__ioEncoding, 243 line = str(process.readLine(), self.__ioEncoding, "replace")
234 'replace')
235 self.__processLine(line, isTopDiff) 244 self.__processLine(line, isTopDiff)
236 245
237 def __readStderr(self, process): 246 def __readStderr(self, process):
238 """ 247 """
239 Private slot to handle the readyReadStandardError signal. 248 Private slot to handle the readyReadStandardError signal.
240 249
241 It reads the error output of the process and inserts it into the 250 It reads the error output of the process and inserts it into the
242 error pane. 251 error pane.
243 252
244 @param process reference to the process providing error output 253 @param process reference to the process providing error output
245 @type QProcess 254 @type QProcess
246 """ 255 """
247 s = str(process.readAllStandardError(), self.__ioEncoding, 256 s = str(process.readAllStandardError(), self.__ioEncoding, "replace")
248 'replace')
249 self.__errors.append(s) 257 self.__errors.append(s)

eric ide

mercurial