38 class GitStatusDialog(QWidget, Ui_GitStatusDialog): |
43 class GitStatusDialog(QWidget, Ui_GitStatusDialog): |
39 """ |
44 """ |
40 Class implementing a dialog to show the output of the git status command |
45 Class implementing a dialog to show the output of the git status command |
41 process. |
46 process. |
42 """ |
47 """ |
|
48 |
43 ConflictStates = ["AA", "AU", "DD", "DU", "UA", "UD", "UU"] |
49 ConflictStates = ["AA", "AU", "DD", "DU", "UA", "UD", "UU"] |
44 |
50 |
45 ConflictRole = Qt.ItemDataRole.UserRole |
51 ConflictRole = Qt.ItemDataRole.UserRole |
46 |
52 |
47 def __init__(self, vcs, parent=None): |
53 def __init__(self, vcs, parent=None): |
48 """ |
54 """ |
49 Constructor |
55 Constructor |
50 |
56 |
51 @param vcs reference to the vcs object |
57 @param vcs reference to the vcs object |
52 @param parent parent widget (QWidget) |
58 @param parent parent widget (QWidget) |
53 """ |
59 """ |
54 super().__init__(parent) |
60 super().__init__(parent) |
55 self.setupUi(self) |
61 self.setupUi(self) |
56 |
62 |
57 self.__toBeCommittedColumn = 0 |
63 self.__toBeCommittedColumn = 0 |
58 self.__statusWorkColumn = 1 |
64 self.__statusWorkColumn = 1 |
59 self.__statusIndexColumn = 2 |
65 self.__statusIndexColumn = 2 |
60 self.__pathColumn = 3 |
66 self.__pathColumn = 3 |
61 self.__lastColumn = self.statusList.columnCount() |
67 self.__lastColumn = self.statusList.columnCount() |
62 |
68 |
63 self.refreshButton = self.buttonBox.addButton( |
69 self.refreshButton = self.buttonBox.addButton( |
64 self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole) |
70 self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole |
65 self.refreshButton.setToolTip( |
71 ) |
66 self.tr("Press to refresh the status display")) |
72 self.refreshButton.setToolTip(self.tr("Press to refresh the status display")) |
67 self.refreshButton.setEnabled(False) |
73 self.refreshButton.setEnabled(False) |
68 self.buttonBox.button( |
74 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
69 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
75 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
70 self.buttonBox.button( |
76 |
71 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
72 |
|
73 self.diff = None |
77 self.diff = None |
74 self.vcs = vcs |
78 self.vcs = vcs |
75 self.vcs.committed.connect(self.__committed) |
79 self.vcs.committed.connect(self.__committed) |
76 self.process = QProcess() |
80 self.process = QProcess() |
77 self.process.finished.connect(self.__procFinished) |
81 self.process.finished.connect(self.__procFinished) |
78 self.process.readyReadStandardOutput.connect(self.__readStdout) |
82 self.process.readyReadStandardOutput.connect(self.__readStdout) |
79 self.process.readyReadStandardError.connect(self.__readStderr) |
83 self.process.readyReadStandardError.connect(self.__readStderr) |
80 |
84 |
81 self.errorGroup.hide() |
85 self.errorGroup.hide() |
82 self.inputGroup.hide() |
86 self.inputGroup.hide() |
83 |
87 |
84 self.vDiffSplitter.setStretchFactor(0, 2) |
88 self.vDiffSplitter.setStretchFactor(0, 2) |
85 self.vDiffSplitter.setStretchFactor(0, 2) |
89 self.vDiffSplitter.setStretchFactor(0, 2) |
86 self.vDiffSplitter.setSizes([400, 400]) |
90 self.vDiffSplitter.setSizes([400, 400]) |
87 self.__hDiffSplitterState = None |
91 self.__hDiffSplitterState = None |
88 self.__vDiffSplitterState = None |
92 self.__vDiffSplitterState = None |
89 |
93 |
90 self.statusList.headerItem().setText(self.__lastColumn, "") |
94 self.statusList.headerItem().setText(self.__lastColumn, "") |
91 self.statusList.header().setSortIndicator( |
95 self.statusList.header().setSortIndicator( |
92 self.__pathColumn, Qt.SortOrder.AscendingOrder) |
96 self.__pathColumn, Qt.SortOrder.AscendingOrder |
93 |
97 ) |
|
98 |
94 font = Preferences.getEditorOtherFonts("MonospacedFont") |
99 font = Preferences.getEditorOtherFonts("MonospacedFont") |
95 self.lDiffEdit.document().setDefaultFont(font) |
100 self.lDiffEdit.document().setDefaultFont(font) |
96 self.rDiffEdit.document().setDefaultFont(font) |
101 self.rDiffEdit.document().setDefaultFont(font) |
97 self.lDiffEdit.customContextMenuRequested.connect( |
102 self.lDiffEdit.customContextMenuRequested.connect(self.__showLDiffContextMenu) |
98 self.__showLDiffContextMenu) |
103 self.rDiffEdit.customContextMenuRequested.connect(self.__showRDiffContextMenu) |
99 self.rDiffEdit.customContextMenuRequested.connect( |
104 |
100 self.__showRDiffContextMenu) |
|
101 |
|
102 self.__lDiffMenu = QMenu() |
105 self.__lDiffMenu = QMenu() |
103 self.__stageLinesAct = self.__lDiffMenu.addAction( |
106 self.__stageLinesAct = self.__lDiffMenu.addAction( |
104 UI.PixmapCache.getIcon("vcsAdd"), |
107 UI.PixmapCache.getIcon("vcsAdd"), |
105 self.tr("Stage Selected Lines"), |
108 self.tr("Stage Selected Lines"), |
106 self.__stageHunkOrLines) |
109 self.__stageHunkOrLines, |
|
110 ) |
107 self.__revertLinesAct = self.__lDiffMenu.addAction( |
111 self.__revertLinesAct = self.__lDiffMenu.addAction( |
108 UI.PixmapCache.getIcon("vcsRevert"), |
112 UI.PixmapCache.getIcon("vcsRevert"), |
109 self.tr("Revert Selected Lines"), |
113 self.tr("Revert Selected Lines"), |
110 self.__revertHunkOrLines) |
114 self.__revertHunkOrLines, |
|
115 ) |
111 self.__stageHunkAct = self.__lDiffMenu.addAction( |
116 self.__stageHunkAct = self.__lDiffMenu.addAction( |
112 UI.PixmapCache.getIcon("vcsAdd"), |
117 UI.PixmapCache.getIcon("vcsAdd"), |
113 self.tr("Stage Hunk"), |
118 self.tr("Stage Hunk"), |
114 self.__stageHunkOrLines) |
119 self.__stageHunkOrLines, |
|
120 ) |
115 self.__revertHunkAct = self.__lDiffMenu.addAction( |
121 self.__revertHunkAct = self.__lDiffMenu.addAction( |
116 UI.PixmapCache.getIcon("vcsRevert"), |
122 UI.PixmapCache.getIcon("vcsRevert"), |
117 self.tr("Revert Hunk"), |
123 self.tr("Revert Hunk"), |
118 self.__revertHunkOrLines) |
124 self.__revertHunkOrLines, |
119 |
125 ) |
|
126 |
120 self.__rDiffMenu = QMenu() |
127 self.__rDiffMenu = QMenu() |
121 self.__unstageLinesAct = self.__rDiffMenu.addAction( |
128 self.__unstageLinesAct = self.__rDiffMenu.addAction( |
122 UI.PixmapCache.getIcon("vcsRemove"), |
129 UI.PixmapCache.getIcon("vcsRemove"), |
123 self.tr("Unstage Selected Lines"), |
130 self.tr("Unstage Selected Lines"), |
124 self.__unstageHunkOrLines) |
131 self.__unstageHunkOrLines, |
|
132 ) |
125 self.__unstageHunkAct = self.__rDiffMenu.addAction( |
133 self.__unstageHunkAct = self.__rDiffMenu.addAction( |
126 UI.PixmapCache.getIcon("vcsRemove"), |
134 UI.PixmapCache.getIcon("vcsRemove"), |
127 self.tr("Unstage Hunk"), |
135 self.tr("Unstage Hunk"), |
128 self.__unstageHunkOrLines) |
136 self.__unstageHunkOrLines, |
129 |
137 ) |
|
138 |
130 self.lDiffHighlighter = GitDiffHighlighter(self.lDiffEdit.document()) |
139 self.lDiffHighlighter = GitDiffHighlighter(self.lDiffEdit.document()) |
131 self.rDiffHighlighter = GitDiffHighlighter(self.rDiffEdit.document()) |
140 self.rDiffHighlighter = GitDiffHighlighter(self.rDiffEdit.document()) |
132 |
141 |
133 self.lDiffParser = None |
142 self.lDiffParser = None |
134 self.rDiffParser = None |
143 self.rDiffParser = None |
135 |
144 |
136 self.__selectedName = "" |
145 self.__selectedName = "" |
137 |
146 |
138 self.__diffGenerator = GitDiffGenerator(vcs, self) |
147 self.__diffGenerator = GitDiffGenerator(vcs, self) |
139 self.__diffGenerator.finished.connect(self.__generatorFinished) |
148 self.__diffGenerator.finished.connect(self.__generatorFinished) |
140 |
149 |
141 self.modifiedIndicators = [ |
150 self.modifiedIndicators = [ |
142 self.tr('added'), |
151 self.tr("added"), |
143 self.tr('copied'), |
152 self.tr("copied"), |
144 self.tr('deleted'), |
153 self.tr("deleted"), |
145 self.tr('modified'), |
154 self.tr("modified"), |
146 self.tr('renamed'), |
155 self.tr("renamed"), |
147 ] |
156 ] |
148 self.modifiedOnlyIndicators = [ |
157 self.modifiedOnlyIndicators = [ |
149 self.tr('modified'), |
158 self.tr("modified"), |
150 ] |
159 ] |
151 |
160 |
152 self.unversionedIndicators = [ |
161 self.unversionedIndicators = [ |
153 self.tr('not tracked'), |
162 self.tr("not tracked"), |
154 ] |
163 ] |
155 |
164 |
156 self.missingIndicators = [ |
165 self.missingIndicators = [ |
157 self.tr('deleted'), |
166 self.tr("deleted"), |
158 ] |
167 ] |
159 |
168 |
160 self.unmergedIndicators = [ |
169 self.unmergedIndicators = [ |
161 self.tr('unmerged'), |
170 self.tr("unmerged"), |
162 ] |
171 ] |
163 |
172 |
164 self.status = { |
173 self.status = { |
165 ' ': self.tr("unmodified"), |
174 " ": self.tr("unmodified"), |
166 'A': self.tr('added'), |
175 "A": self.tr("added"), |
167 'C': self.tr('copied'), |
176 "C": self.tr("copied"), |
168 'D': self.tr('deleted'), |
177 "D": self.tr("deleted"), |
169 'M': self.tr('modified'), |
178 "M": self.tr("modified"), |
170 'R': self.tr('renamed'), |
179 "R": self.tr("renamed"), |
171 'U': self.tr('unmerged'), |
180 "U": self.tr("unmerged"), |
172 '?': self.tr('not tracked'), |
181 "?": self.tr("not tracked"), |
173 '!': self.tr('ignored'), |
182 "!": self.tr("ignored"), |
174 } |
183 } |
175 |
184 |
176 self.__ioEncoding = Preferences.getSystem("IOEncoding") |
185 self.__ioEncoding = Preferences.getSystem("IOEncoding") |
177 |
186 |
178 self.__initActionsMenu() |
187 self.__initActionsMenu() |
179 |
188 |
180 def __initActionsMenu(self): |
189 def __initActionsMenu(self): |
181 """ |
190 """ |
182 Private method to initialize the actions menu. |
191 Private method to initialize the actions menu. |
183 """ |
192 """ |
184 self.__actionsMenu = QMenu() |
193 self.__actionsMenu = QMenu() |
185 self.__actionsMenu.setTearOffEnabled(True) |
194 self.__actionsMenu.setTearOffEnabled(True) |
186 self.__actionsMenu.setToolTipsVisible(True) |
195 self.__actionsMenu.setToolTipsVisible(True) |
187 self.__actionsMenu.aboutToShow.connect(self.__showActionsMenu) |
196 self.__actionsMenu.aboutToShow.connect(self.__showActionsMenu) |
188 |
197 |
189 self.__commitAct = self.__actionsMenu.addAction( |
198 self.__commitAct = self.__actionsMenu.addAction( |
190 self.tr("Commit"), self.__commit) |
199 self.tr("Commit"), self.__commit |
|
200 ) |
191 self.__commitAct.setToolTip(self.tr("Commit the selected changes")) |
201 self.__commitAct.setToolTip(self.tr("Commit the selected changes")) |
192 self.__amendAct = self.__actionsMenu.addAction( |
202 self.__amendAct = self.__actionsMenu.addAction(self.tr("Amend"), self.__amend) |
193 self.tr("Amend"), self.__amend) |
203 self.__amendAct.setToolTip( |
194 self.__amendAct.setToolTip(self.tr( |
204 self.tr("Amend the latest commit with the selected changes") |
195 "Amend the latest commit with the selected changes")) |
205 ) |
196 self.__commitSelectAct = self.__actionsMenu.addAction( |
206 self.__commitSelectAct = self.__actionsMenu.addAction( |
197 self.tr("Select all for commit"), self.__commitSelectAll) |
207 self.tr("Select all for commit"), self.__commitSelectAll |
|
208 ) |
198 self.__commitDeselectAct = self.__actionsMenu.addAction( |
209 self.__commitDeselectAct = self.__actionsMenu.addAction( |
199 self.tr("Unselect all from commit"), self.__commitDeselectAll) |
210 self.tr("Unselect all from commit"), self.__commitDeselectAll |
200 |
211 ) |
|
212 |
201 self.__actionsMenu.addSeparator() |
213 self.__actionsMenu.addSeparator() |
202 self.__addAct = self.__actionsMenu.addAction( |
214 self.__addAct = self.__actionsMenu.addAction(self.tr("Add"), self.__add) |
203 self.tr("Add"), self.__add) |
|
204 self.__addAct.setToolTip(self.tr("Add the selected files")) |
215 self.__addAct.setToolTip(self.tr("Add the selected files")) |
205 self.__stageAct = self.__actionsMenu.addAction( |
216 self.__stageAct = self.__actionsMenu.addAction( |
206 self.tr("Stage changes"), self.__stage) |
217 self.tr("Stage changes"), self.__stage |
207 self.__stageAct.setToolTip(self.tr( |
218 ) |
208 "Stages all changes of the selected files")) |
219 self.__stageAct.setToolTip(self.tr("Stages all changes of the selected files")) |
209 self.__unstageAct = self.__actionsMenu.addAction( |
220 self.__unstageAct = self.__actionsMenu.addAction( |
210 self.tr("Unstage changes"), self.__unstage) |
221 self.tr("Unstage changes"), self.__unstage |
211 self.__unstageAct.setToolTip(self.tr( |
222 ) |
212 "Unstages all changes of the selected files")) |
223 self.__unstageAct.setToolTip( |
213 |
224 self.tr("Unstages all changes of the selected files") |
|
225 ) |
|
226 |
214 self.__actionsMenu.addSeparator() |
227 self.__actionsMenu.addSeparator() |
215 |
228 |
216 self.__diffAct = self.__actionsMenu.addAction( |
229 self.__diffAct = self.__actionsMenu.addAction( |
217 self.tr("Differences"), self.__diff) |
230 self.tr("Differences"), self.__diff |
218 self.__diffAct.setToolTip(self.tr( |
231 ) |
219 "Shows the differences of the selected entry in a" |
232 self.__diffAct.setToolTip( |
220 " separate dialog")) |
233 self.tr( |
|
234 "Shows the differences of the selected entry in a" " separate dialog" |
|
235 ) |
|
236 ) |
221 self.__sbsDiffAct = self.__actionsMenu.addAction( |
237 self.__sbsDiffAct = self.__actionsMenu.addAction( |
222 self.tr("Differences Side-By-Side"), self.__sbsDiff) |
238 self.tr("Differences Side-By-Side"), self.__sbsDiff |
223 self.__sbsDiffAct.setToolTip(self.tr( |
239 ) |
224 "Shows the differences of the selected entry side-by-side in" |
240 self.__sbsDiffAct.setToolTip( |
225 " a separate dialog")) |
241 self.tr( |
226 |
242 "Shows the differences of the selected entry side-by-side in" |
|
243 " a separate dialog" |
|
244 ) |
|
245 ) |
|
246 |
227 self.__actionsMenu.addSeparator() |
247 self.__actionsMenu.addSeparator() |
228 |
248 |
229 self.__revertAct = self.__actionsMenu.addAction( |
249 self.__revertAct = self.__actionsMenu.addAction( |
230 self.tr("Revert"), self.__revert) |
250 self.tr("Revert"), self.__revert |
231 self.__revertAct.setToolTip(self.tr( |
251 ) |
232 "Reverts the changes of the selected files")) |
252 self.__revertAct.setToolTip( |
233 |
253 self.tr("Reverts the changes of the selected files") |
|
254 ) |
|
255 |
234 self.__actionsMenu.addSeparator() |
256 self.__actionsMenu.addSeparator() |
235 |
257 |
236 self.__forgetAct = self.__actionsMenu.addAction( |
258 self.__forgetAct = self.__actionsMenu.addAction( |
237 self.tr("Forget Missing"), self.__forget) |
259 self.tr("Forget Missing"), self.__forget |
238 self.__forgetAct.setToolTip(self.tr( |
260 ) |
239 "Forgets about the selected missing files")) |
261 self.__forgetAct.setToolTip(self.tr("Forgets about the selected missing files")) |
240 self.__restoreAct = self.__actionsMenu.addAction( |
262 self.__restoreAct = self.__actionsMenu.addAction( |
241 self.tr("Restore Missing"), self.__restoreMissing) |
263 self.tr("Restore Missing"), self.__restoreMissing |
242 self.__restoreAct.setToolTip(self.tr( |
264 ) |
243 "Restores the selected missing files")) |
265 self.__restoreAct.setToolTip(self.tr("Restores the selected missing files")) |
244 |
266 |
245 self.__actionsMenu.addSeparator() |
267 self.__actionsMenu.addSeparator() |
246 |
268 |
247 self.__editAct = self.__actionsMenu.addAction( |
269 self.__editAct = self.__actionsMenu.addAction( |
248 self.tr("Edit Conflict"), self.__editConflict) |
270 self.tr("Edit Conflict"), self.__editConflict |
249 self.__editAct.setToolTip(self.tr( |
271 ) |
250 "Edit the selected conflicting file")) |
272 self.__editAct.setToolTip(self.tr("Edit the selected conflicting file")) |
251 |
273 |
252 self.__actionsMenu.addSeparator() |
274 self.__actionsMenu.addSeparator() |
253 |
275 |
254 act = self.__actionsMenu.addAction( |
276 act = self.__actionsMenu.addAction( |
255 self.tr("Adjust column sizes"), self.__resizeColumns) |
277 self.tr("Adjust column sizes"), self.__resizeColumns |
256 act.setToolTip(self.tr( |
278 ) |
257 "Adjusts the width of all columns to their contents")) |
279 act.setToolTip(self.tr("Adjusts the width of all columns to their contents")) |
258 |
280 |
259 self.actionsButton.setIcon( |
281 self.actionsButton.setIcon(UI.PixmapCache.getIcon("actionsToolButton")) |
260 UI.PixmapCache.getIcon("actionsToolButton")) |
|
261 self.actionsButton.setMenu(self.__actionsMenu) |
282 self.actionsButton.setMenu(self.__actionsMenu) |
262 |
283 |
263 def closeEvent(self, e): |
284 def closeEvent(self, e): |
264 """ |
285 """ |
265 Protected slot implementing a close event handler. |
286 Protected slot implementing a close event handler. |
266 |
287 |
267 @param e close event (QCloseEvent) |
288 @param e close event (QCloseEvent) |
268 """ |
289 """ |
269 if ( |
290 if ( |
270 self.process is not None and |
291 self.process is not None |
271 self.process.state() != QProcess.ProcessState.NotRunning |
292 and self.process.state() != QProcess.ProcessState.NotRunning |
272 ): |
293 ): |
273 self.process.terminate() |
294 self.process.terminate() |
274 QTimer.singleShot(2000, self.process.kill) |
295 QTimer.singleShot(2000, self.process.kill) |
275 self.process.waitForFinished(3000) |
296 self.process.waitForFinished(3000) |
276 |
297 |
|
298 self.vcs.getPlugin().setPreferences("StatusDialogGeometry", self.saveGeometry()) |
277 self.vcs.getPlugin().setPreferences( |
299 self.vcs.getPlugin().setPreferences( |
278 "StatusDialogGeometry", self.saveGeometry()) |
300 "StatusDialogSplitterStates", |
279 self.vcs.getPlugin().setPreferences( |
301 [self.vDiffSplitter.saveState(), self.hDiffSplitter.saveState()], |
280 "StatusDialogSplitterStates", [ |
302 ) |
281 self.vDiffSplitter.saveState(), |
303 |
282 self.hDiffSplitter.saveState() |
|
283 ] |
|
284 ) |
|
285 |
|
286 e.accept() |
304 e.accept() |
287 |
305 |
288 def show(self): |
306 def show(self): |
289 """ |
307 """ |
290 Public slot to show the dialog. |
308 Public slot to show the dialog. |
291 """ |
309 """ |
292 super().show() |
310 super().show() |
293 |
311 |
294 geom = self.vcs.getPlugin().getPreferences( |
312 geom = self.vcs.getPlugin().getPreferences("StatusDialogGeometry") |
295 "StatusDialogGeometry") |
|
296 if geom.isEmpty(): |
313 if geom.isEmpty(): |
297 s = QSize(900, 600) |
314 s = QSize(900, 600) |
298 self.resize(s) |
315 self.resize(s) |
299 else: |
316 else: |
300 self.restoreGeometry(geom) |
317 self.restoreGeometry(geom) |
301 |
318 |
302 states = self.vcs.getPlugin().getPreferences( |
319 states = self.vcs.getPlugin().getPreferences("StatusDialogSplitterStates") |
303 "StatusDialogSplitterStates") |
|
304 if len(states) == 2: |
320 if len(states) == 2: |
305 # we have two splitters |
321 # we have two splitters |
306 self.vDiffSplitter.restoreState(states[0]) |
322 self.vDiffSplitter.restoreState(states[0]) |
307 self.hDiffSplitter.restoreState(states[1]) |
323 self.hDiffSplitter.restoreState(states[1]) |
308 |
324 |
309 def __resort(self): |
325 def __resort(self): |
310 """ |
326 """ |
311 Private method to resort the tree. |
327 Private method to resort the tree. |
312 """ |
328 """ |
313 self.statusList.sortItems( |
329 self.statusList.sortItems( |
314 self.statusList.sortColumn(), |
330 self.statusList.sortColumn(), self.statusList.header().sortIndicatorOrder() |
315 self.statusList.header().sortIndicatorOrder()) |
331 ) |
316 |
332 |
317 def __resizeColumns(self): |
333 def __resizeColumns(self): |
318 """ |
334 """ |
319 Private method to resize the list columns. |
335 Private method to resize the list columns. |
320 """ |
336 """ |
321 self.statusList.header().resizeSections( |
337 self.statusList.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) |
322 QHeaderView.ResizeMode.ResizeToContents) |
|
323 self.statusList.header().setStretchLastSection(True) |
338 self.statusList.header().setStretchLastSection(True) |
324 |
339 |
325 def __generateItem(self, status, path): |
340 def __generateItem(self, status, path): |
326 """ |
341 """ |
327 Private method to generate a status item in the status list. |
342 Private method to generate a status item in the status list. |
328 |
343 |
329 @param status status indicator (string) |
344 @param status status indicator (string) |
330 @param path path of the file or directory (string) |
345 @param path path of the file or directory (string) |
331 """ |
346 """ |
332 statusWorkText = self.status[status[1]] |
347 statusWorkText = self.status[status[1]] |
333 statusIndexText = self.status[status[0]] |
348 statusIndexText = self.status[status[0]] |
334 itm = QTreeWidgetItem(self.statusList, [ |
349 itm = QTreeWidgetItem( |
335 "", |
350 self.statusList, |
336 statusWorkText, |
351 [ |
337 statusIndexText, |
352 "", |
338 path, |
353 statusWorkText, |
339 ]) |
354 statusIndexText, |
340 |
355 path, |
341 itm.setTextAlignment(self.__statusWorkColumn, |
356 ], |
342 Qt.AlignmentFlag.AlignHCenter) |
357 ) |
343 itm.setTextAlignment(self.__statusIndexColumn, |
358 |
344 Qt.AlignmentFlag.AlignHCenter) |
359 itm.setTextAlignment(self.__statusWorkColumn, Qt.AlignmentFlag.AlignHCenter) |
345 itm.setTextAlignment(self.__pathColumn, |
360 itm.setTextAlignment(self.__statusIndexColumn, Qt.AlignmentFlag.AlignHCenter) |
346 Qt.AlignmentFlag.AlignLeft) |
361 itm.setTextAlignment(self.__pathColumn, Qt.AlignmentFlag.AlignLeft) |
347 |
362 |
348 if ( |
363 if ( |
349 status not in self.ConflictStates + ["??", "!!"] and |
364 status not in self.ConflictStates + ["??", "!!"] |
350 statusIndexText in self.modifiedIndicators |
365 and statusIndexText in self.modifiedIndicators |
351 ): |
366 ): |
352 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) |
367 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) |
353 itm.setCheckState(self.__toBeCommittedColumn, |
368 itm.setCheckState(self.__toBeCommittedColumn, Qt.CheckState.Checked) |
354 Qt.CheckState.Checked) |
|
355 else: |
369 else: |
356 itm.setFlags(itm.flags() & ~Qt.ItemFlag.ItemIsUserCheckable) |
370 itm.setFlags(itm.flags() & ~Qt.ItemFlag.ItemIsUserCheckable) |
357 |
371 |
358 if statusWorkText not in self.__statusFilters: |
372 if statusWorkText not in self.__statusFilters: |
359 self.__statusFilters.append(statusWorkText) |
373 self.__statusFilters.append(statusWorkText) |
360 if statusIndexText not in self.__statusFilters: |
374 if statusIndexText not in self.__statusFilters: |
361 self.__statusFilters.append(statusIndexText) |
375 self.__statusFilters.append(statusIndexText) |
362 |
376 |
363 if status in self.ConflictStates: |
377 if status in self.ConflictStates: |
364 itm.setIcon(self.__statusWorkColumn, |
378 itm.setIcon( |
365 UI.PixmapCache.getIcon( |
379 self.__statusWorkColumn, |
366 os.path.join("VcsPlugins", "vcsGit", "icons", |
380 UI.PixmapCache.getIcon( |
367 "conflict.svg"))) |
381 os.path.join("VcsPlugins", "vcsGit", "icons", "conflict.svg") |
|
382 ), |
|
383 ) |
368 itm.setData(0, self.ConflictRole, status in self.ConflictStates) |
384 itm.setData(0, self.ConflictRole, status in self.ConflictStates) |
369 |
385 |
370 def start(self, fn): |
386 def start(self, fn): |
371 """ |
387 """ |
372 Public slot to start the git status command. |
388 Public slot to start the git status command. |
373 |
389 |
374 @param fn filename(s)/directoryname(s) to show the status of |
390 @param fn filename(s)/directoryname(s) to show the status of |
375 (string or list of strings) |
391 (string or list of strings) |
376 """ |
392 """ |
377 self.errorGroup.hide() |
393 self.errorGroup.hide() |
378 self.intercept = False |
394 self.intercept = False |
379 self.args = fn |
395 self.args = fn |
380 |
396 |
381 self.__ioEncoding = Preferences.getSystem("IOEncoding") |
397 self.__ioEncoding = Preferences.getSystem("IOEncoding") |
382 |
398 |
383 self.statusFilterCombo.clear() |
399 self.statusFilterCombo.clear() |
384 self.__statusFilters = [] |
400 self.__statusFilters = [] |
385 self.statusList.clear() |
401 self.statusList.clear() |
386 |
402 |
387 self.setWindowTitle(self.tr('Git Status')) |
403 self.setWindowTitle(self.tr("Git Status")) |
388 |
404 |
389 args = self.vcs.initCommand("status") |
405 args = self.vcs.initCommand("status") |
390 args.append('--porcelain') |
406 args.append("--porcelain") |
391 args.append("--") |
407 args.append("--") |
392 if isinstance(fn, list): |
408 if isinstance(fn, list): |
393 self.dname, fnames = self.vcs.splitPathList(fn) |
409 self.dname, fnames = self.vcs.splitPathList(fn) |
394 self.vcs.addArguments(args, fn) |
410 self.vcs.addArguments(args, fn) |
395 else: |
411 else: |
396 self.dname, fname = self.vcs.splitPath(fn) |
412 self.dname, fname = self.vcs.splitPath(fn) |
397 args.append(fn) |
413 args.append(fn) |
398 |
414 |
399 # find the root of the repo |
415 # find the root of the repo |
400 self.__repodir = self.dname |
416 self.__repodir = self.dname |
401 while not os.path.isdir( |
417 while not os.path.isdir(os.path.join(self.__repodir, self.vcs.adminDir)): |
402 os.path.join(self.__repodir, self.vcs.adminDir)): |
|
403 self.__repodir = os.path.dirname(self.__repodir) |
418 self.__repodir = os.path.dirname(self.__repodir) |
404 if os.path.splitdrive(self.__repodir)[1] == os.sep: |
419 if os.path.splitdrive(self.__repodir)[1] == os.sep: |
405 return |
420 return |
406 |
421 |
407 self.process.kill() |
422 self.process.kill() |
408 self.process.setWorkingDirectory(self.__repodir) |
423 self.process.setWorkingDirectory(self.__repodir) |
409 |
424 |
410 self.process.start('git', args) |
425 self.process.start("git", args) |
411 procStarted = self.process.waitForStarted(5000) |
426 procStarted = self.process.waitForStarted(5000) |
412 if not procStarted: |
427 if not procStarted: |
413 self.inputGroup.setEnabled(False) |
428 self.inputGroup.setEnabled(False) |
414 self.inputGroup.hide() |
429 self.inputGroup.hide() |
415 EricMessageBox.critical( |
430 EricMessageBox.critical( |
416 self, |
431 self, |
417 self.tr('Process Generation Error'), |
432 self.tr("Process Generation Error"), |
418 self.tr( |
433 self.tr( |
419 'The process {0} could not be started. ' |
434 "The process {0} could not be started. " |
420 'Ensure, that it is in the search path.' |
435 "Ensure, that it is in the search path." |
421 ).format('git')) |
436 ).format("git"), |
|
437 ) |
422 else: |
438 else: |
423 self.buttonBox.button( |
439 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( |
424 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
440 False |
425 self.buttonBox.button( |
441 ) |
426 QDialogButtonBox.StandardButton.Cancel).setEnabled(True) |
442 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled( |
427 self.buttonBox.button( |
443 True |
428 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
444 ) |
429 |
445 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault( |
|
446 True |
|
447 ) |
|
448 |
430 self.refreshButton.setEnabled(False) |
449 self.refreshButton.setEnabled(False) |
431 |
450 |
432 def __finish(self): |
451 def __finish(self): |
433 """ |
452 """ |
434 Private slot called when the process finished or the user pressed |
453 Private slot called when the process finished or the user pressed |
435 the button. |
454 the button. |
436 """ |
455 """ |
437 if ( |
456 if ( |
438 self.process is not None and |
457 self.process is not None |
439 self.process.state() != QProcess.ProcessState.NotRunning |
458 and self.process.state() != QProcess.ProcessState.NotRunning |
440 ): |
459 ): |
441 self.process.terminate() |
460 self.process.terminate() |
442 QTimer.singleShot(2000, self.process.kill) |
461 QTimer.singleShot(2000, self.process.kill) |
443 self.process.waitForFinished(3000) |
462 self.process.waitForFinished(3000) |
444 |
463 |
445 self.inputGroup.setEnabled(False) |
464 self.inputGroup.setEnabled(False) |
446 self.inputGroup.hide() |
465 self.inputGroup.hide() |
447 self.refreshButton.setEnabled(True) |
466 self.refreshButton.setEnabled(True) |
448 |
467 |
449 self.buttonBox.button( |
468 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
450 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
469 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
451 self.buttonBox.button( |
470 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
452 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
471 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setFocus( |
453 self.buttonBox.button( |
472 Qt.FocusReason.OtherFocusReason |
454 QDialogButtonBox.StandardButton.Close).setDefault(True) |
473 ) |
455 self.buttonBox.button( |
474 |
456 QDialogButtonBox.StandardButton.Close).setFocus( |
|
457 Qt.FocusReason.OtherFocusReason) |
|
458 |
|
459 self.__statusFilters.sort() |
475 self.__statusFilters.sort() |
460 self.__statusFilters.insert(0, "<{0}>".format(self.tr("all"))) |
476 self.__statusFilters.insert(0, "<{0}>".format(self.tr("all"))) |
461 self.statusFilterCombo.addItems(self.__statusFilters) |
477 self.statusFilterCombo.addItems(self.__statusFilters) |
462 |
478 |
463 self.__resort() |
479 self.__resort() |
464 self.__resizeColumns() |
480 self.__resizeColumns() |
465 |
481 |
466 self.__refreshDiff() |
482 self.__refreshDiff() |
467 |
483 |
468 def on_buttonBox_clicked(self, button): |
484 def on_buttonBox_clicked(self, button): |
469 """ |
485 """ |
470 Private slot called by a button of the button box clicked. |
486 Private slot called by a button of the button box clicked. |
471 |
487 |
472 @param button button that was clicked (QAbstractButton) |
488 @param button button that was clicked (QAbstractButton) |
473 """ |
489 """ |
474 if button == self.buttonBox.button( |
490 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
475 QDialogButtonBox.StandardButton.Close |
|
476 ): |
|
477 self.close() |
491 self.close() |
478 elif button == self.buttonBox.button( |
492 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): |
479 QDialogButtonBox.StandardButton.Cancel |
|
480 ): |
|
481 self.__finish() |
493 self.__finish() |
482 elif button == self.refreshButton: |
494 elif button == self.refreshButton: |
483 self.on_refreshButton_clicked() |
495 self.on_refreshButton_clicked() |
484 |
496 |
485 def __procFinished(self, exitCode, exitStatus): |
497 def __procFinished(self, exitCode, exitStatus): |
486 """ |
498 """ |
487 Private slot connected to the finished signal. |
499 Private slot connected to the finished signal. |
488 |
500 |
489 @param exitCode exit code of the process (integer) |
501 @param exitCode exit code of the process (integer) |
490 @param exitStatus exit status of the process (QProcess.ExitStatus) |
502 @param exitStatus exit status of the process (QProcess.ExitStatus) |
491 """ |
503 """ |
492 self.__finish() |
504 self.__finish() |
493 |
505 |
494 def __readStdout(self): |
506 def __readStdout(self): |
495 """ |
507 """ |
496 Private slot to handle the readyReadStandardOutput signal. |
508 Private slot to handle the readyReadStandardOutput signal. |
497 |
509 |
498 It reads the output of the process, formats it and inserts it into |
510 It reads the output of the process, formats it and inserts it into |
499 the contents pane. |
511 the contents pane. |
500 """ |
512 """ |
501 if self.process is not None: |
513 if self.process is not None: |
502 self.process.setReadChannel(QProcess.ProcessChannel.StandardOutput) |
514 self.process.setReadChannel(QProcess.ProcessChannel.StandardOutput) |
503 |
515 |
504 while self.process.canReadLine(): |
516 while self.process.canReadLine(): |
505 line = str(self.process.readLine(), self.__ioEncoding, |
517 line = str(self.process.readLine(), self.__ioEncoding, "replace") |
506 'replace') |
518 |
507 |
|
508 status = line[:2] |
519 status = line[:2] |
509 path = line[3:].strip().split(" -> ")[-1].strip('"') |
520 path = line[3:].strip().split(" -> ")[-1].strip('"') |
510 self.__generateItem(status, path) |
521 self.__generateItem(status, path) |
511 |
522 |
512 def __readStderr(self): |
523 def __readStderr(self): |
513 """ |
524 """ |
514 Private slot to handle the readyReadStandardError signal. |
525 Private slot to handle the readyReadStandardError signal. |
515 |
526 |
516 It reads the error output of the process and inserts it into the |
527 It reads the error output of the process and inserts it into the |
517 error pane. |
528 error pane. |
518 """ |
529 """ |
519 if self.process is not None: |
530 if self.process is not None: |
520 s = str(self.process.readAllStandardError(), |
531 s = str(self.process.readAllStandardError(), self.__ioEncoding, "replace") |
521 self.__ioEncoding, 'replace') |
|
522 self.errorGroup.show() |
532 self.errorGroup.show() |
523 self.errors.insertPlainText(s) |
533 self.errors.insertPlainText(s) |
524 self.errors.ensureCursorVisible() |
534 self.errors.ensureCursorVisible() |
525 |
535 |
526 # show input in case the process asked for some input |
536 # show input in case the process asked for some input |
527 self.inputGroup.setEnabled(True) |
537 self.inputGroup.setEnabled(True) |
528 self.inputGroup.show() |
538 self.inputGroup.show() |
529 |
539 |
530 def on_passwordCheckBox_toggled(self, isOn): |
540 def on_passwordCheckBox_toggled(self, isOn): |
531 """ |
541 """ |
532 Private slot to handle the password checkbox toggled. |
542 Private slot to handle the password checkbox toggled. |
533 |
543 |
534 @param isOn flag indicating the status of the check box (boolean) |
544 @param isOn flag indicating the status of the check box (boolean) |
535 """ |
545 """ |
536 if isOn: |
546 if isOn: |
537 self.input.setEchoMode(QLineEdit.EchoMode.Password) |
547 self.input.setEchoMode(QLineEdit.EchoMode.Password) |
538 else: |
548 else: |
539 self.input.setEchoMode(QLineEdit.EchoMode.Normal) |
549 self.input.setEchoMode(QLineEdit.EchoMode.Normal) |
540 |
550 |
541 @pyqtSlot() |
551 @pyqtSlot() |
542 def on_sendButton_clicked(self): |
552 def on_sendButton_clicked(self): |
543 """ |
553 """ |
544 Private slot to send the input to the git process. |
554 Private slot to send the input to the git process. |
545 """ |
555 """ |
546 inputTxt = self.input.text() |
556 inputTxt = self.input.text() |
547 inputTxt += os.linesep |
557 inputTxt += os.linesep |
548 |
558 |
549 if self.passwordCheckBox.isChecked(): |
559 if self.passwordCheckBox.isChecked(): |
550 self.errors.insertPlainText(os.linesep) |
560 self.errors.insertPlainText(os.linesep) |
551 self.errors.ensureCursorVisible() |
561 self.errors.ensureCursorVisible() |
552 else: |
562 else: |
553 self.errors.insertPlainText(inputTxt) |
563 self.errors.insertPlainText(inputTxt) |
554 self.errors.ensureCursorVisible() |
564 self.errors.ensureCursorVisible() |
555 |
565 |
556 self.process.write(strToQByteArray(inputTxt)) |
566 self.process.write(strToQByteArray(inputTxt)) |
557 |
567 |
558 self.passwordCheckBox.setChecked(False) |
568 self.passwordCheckBox.setChecked(False) |
559 self.input.clear() |
569 self.input.clear() |
560 |
570 |
561 def on_input_returnPressed(self): |
571 def on_input_returnPressed(self): |
562 """ |
572 """ |
563 Private slot to handle the press of the return key in the input field. |
573 Private slot to handle the press of the return key in the input field. |
564 """ |
574 """ |
565 self.intercept = True |
575 self.intercept = True |
566 self.on_sendButton_clicked() |
576 self.on_sendButton_clicked() |
567 |
577 |
568 def keyPressEvent(self, evt): |
578 def keyPressEvent(self, evt): |
569 """ |
579 """ |
570 Protected slot to handle a key press event. |
580 Protected slot to handle a key press event. |
571 |
581 |
572 @param evt the key press event (QKeyEvent) |
582 @param evt the key press event (QKeyEvent) |
573 """ |
583 """ |
574 if self.intercept: |
584 if self.intercept: |
575 self.intercept = False |
585 self.intercept = False |
576 evt.accept() |
586 evt.accept() |
577 return |
587 return |
578 super().keyPressEvent(evt) |
588 super().keyPressEvent(evt) |
579 |
589 |
580 @pyqtSlot() |
590 @pyqtSlot() |
581 def on_refreshButton_clicked(self): |
591 def on_refreshButton_clicked(self): |
582 """ |
592 """ |
583 Private slot to refresh the status display. |
593 Private slot to refresh the status display. |
584 """ |
594 """ |
585 selectedItems = self.statusList.selectedItems() |
595 selectedItems = self.statusList.selectedItems() |
586 if len(selectedItems) == 1: |
596 if len(selectedItems) == 1: |
587 self.__selectedName = selectedItems[0].text(self.__pathColumn) |
597 self.__selectedName = selectedItems[0].text(self.__pathColumn) |
588 else: |
598 else: |
589 self.__selectedName = "" |
599 self.__selectedName = "" |
590 |
600 |
591 self.start(self.args) |
601 self.start(self.args) |
592 |
602 |
593 @pyqtSlot(int) |
603 @pyqtSlot(int) |
594 def on_statusFilterCombo_activated(self, index): |
604 def on_statusFilterCombo_activated(self, index): |
595 """ |
605 """ |
596 Private slot to react to the selection of a status filter. |
606 Private slot to react to the selection of a status filter. |
597 |
607 |
598 @param index index of the selected entry |
608 @param index index of the selected entry |
599 @type int |
609 @type int |
600 """ |
610 """ |
601 txt = self.statusFilterCombo.itemText(index) |
611 txt = self.statusFilterCombo.itemText(index) |
602 if txt == "<{0}>".format(self.tr("all")): |
612 if txt == "<{0}>".format(self.tr("all")): |
647 self.__sbsDiffAct.setEnabled(modifiedOnly == 1) |
657 self.__sbsDiffAct.setEnabled(modifiedOnly == 1) |
648 self.__revertAct.setEnabled(stageable) |
658 self.__revertAct.setEnabled(stageable) |
649 self.__forgetAct.setEnabled(missing) |
659 self.__forgetAct.setEnabled(missing) |
650 self.__restoreAct.setEnabled(missing) |
660 self.__restoreAct.setEnabled(missing) |
651 self.__editAct.setEnabled(conflicting == 1) |
661 self.__editAct.setEnabled(conflicting == 1) |
652 |
662 |
653 def __amend(self): |
663 def __amend(self): |
654 """ |
664 """ |
655 Private slot to handle the Amend context menu entry. |
665 Private slot to handle the Amend context menu entry. |
656 """ |
666 """ |
657 self.__commit(amend=True) |
667 self.__commit(amend=True) |
658 |
668 |
659 def __commit(self, amend=False): |
669 def __commit(self, amend=False): |
660 """ |
670 """ |
661 Private slot to handle the Commit context menu entry. |
671 Private slot to handle the Commit context menu entry. |
662 |
672 |
663 @param amend flag indicating to perform an amend operation (boolean) |
673 @param amend flag indicating to perform an amend operation (boolean) |
664 """ |
674 """ |
665 names = [os.path.join(self.dname, itm.text(self.__pathColumn)) |
675 names = [ |
666 for itm in self.__getCommitableItems()] |
676 os.path.join(self.dname, itm.text(self.__pathColumn)) |
|
677 for itm in self.__getCommitableItems() |
|
678 ] |
667 if not names: |
679 if not names: |
668 EricMessageBox.information( |
680 EricMessageBox.information( |
669 self, |
681 self, |
670 self.tr("Commit"), |
682 self.tr("Commit"), |
671 self.tr("""There are no entries selected to be""" |
683 self.tr("""There are no entries selected to be""" """ committed."""), |
672 """ committed.""")) |
684 ) |
673 return |
685 return |
674 |
686 |
675 if Preferences.getVCS("AutoSaveFiles"): |
687 if Preferences.getVCS("AutoSaveFiles"): |
676 vm = ericApp().getObject("ViewManager") |
688 vm = ericApp().getObject("ViewManager") |
677 for name in names: |
689 for name in names: |
678 vm.saveEditor(name) |
690 vm.saveEditor(name) |
679 self.vcs.vcsCommit(names, commitAll=False, amend=amend) |
691 self.vcs.vcsCommit(names, commitAll=False, amend=amend) |
680 # staged changes |
692 # staged changes |
681 |
693 |
682 def __committed(self): |
694 def __committed(self): |
683 """ |
695 """ |
684 Private slot called after the commit has finished. |
696 Private slot called after the commit has finished. |
685 """ |
697 """ |
686 if self.isVisible(): |
698 if self.isVisible(): |
687 self.on_refreshButton_clicked() |
699 self.on_refreshButton_clicked() |
688 self.vcs.checkVCSStatus() |
700 self.vcs.checkVCSStatus() |
689 |
701 |
690 def __commitSelectAll(self): |
702 def __commitSelectAll(self): |
691 """ |
703 """ |
692 Private slot to select all entries for commit. |
704 Private slot to select all entries for commit. |
693 """ |
705 """ |
694 self.__commitSelect(True) |
706 self.__commitSelect(True) |
695 |
707 |
696 def __commitDeselectAll(self): |
708 def __commitDeselectAll(self): |
697 """ |
709 """ |
698 Private slot to deselect all entries from commit. |
710 Private slot to deselect all entries from commit. |
699 """ |
711 """ |
700 self.__commitSelect(False) |
712 self.__commitSelect(False) |
701 |
713 |
702 def __add(self): |
714 def __add(self): |
703 """ |
715 """ |
704 Private slot to handle the Add context menu entry. |
716 Private slot to handle the Add context menu entry. |
705 """ |
717 """ |
706 names = [os.path.join(self.dname, itm.text(self.__pathColumn)) |
718 names = [ |
707 for itm in self.__getUnversionedItems()] |
719 os.path.join(self.dname, itm.text(self.__pathColumn)) |
|
720 for itm in self.__getUnversionedItems() |
|
721 ] |
708 if not names: |
722 if not names: |
709 EricMessageBox.information( |
723 EricMessageBox.information( |
710 self, |
724 self, |
711 self.tr("Add"), |
725 self.tr("Add"), |
712 self.tr("""There are no unversioned entries""" |
726 self.tr( |
713 """ available/selected.""")) |
727 """There are no unversioned entries""" """ available/selected.""" |
|
728 ), |
|
729 ) |
714 return |
730 return |
715 |
731 |
716 self.vcs.vcsAdd(names) |
732 self.vcs.vcsAdd(names) |
717 self.on_refreshButton_clicked() |
733 self.on_refreshButton_clicked() |
718 |
734 |
719 project = ericApp().getObject("Project") |
735 project = ericApp().getObject("Project") |
720 for name in names: |
736 for name in names: |
721 project.getModel().updateVCSStatus(name) |
737 project.getModel().updateVCSStatus(name) |
722 self.vcs.checkVCSStatus() |
738 self.vcs.checkVCSStatus() |
723 |
739 |
724 def __stage(self): |
740 def __stage(self): |
725 """ |
741 """ |
726 Private slot to handle the Stage context menu entry. |
742 Private slot to handle the Stage context menu entry. |
727 """ |
743 """ |
728 names = [os.path.join(self.dname, itm.text(self.__pathColumn)) |
744 names = [ |
729 for itm in self.__getStageableItems()] |
745 os.path.join(self.dname, itm.text(self.__pathColumn)) |
|
746 for itm in self.__getStageableItems() |
|
747 ] |
730 if not names: |
748 if not names: |
731 EricMessageBox.information( |
749 EricMessageBox.information( |
732 self, |
750 self, |
733 self.tr("Stage"), |
751 self.tr("Stage"), |
734 self.tr("""There are no stageable entries""" |
752 self.tr( |
735 """ available/selected.""")) |
753 """There are no stageable entries""" """ available/selected.""" |
|
754 ), |
|
755 ) |
736 return |
756 return |
737 |
757 |
738 self.vcs.vcsAdd(names) |
758 self.vcs.vcsAdd(names) |
739 self.on_refreshButton_clicked() |
759 self.on_refreshButton_clicked() |
740 |
760 |
741 project = ericApp().getObject("Project") |
761 project = ericApp().getObject("Project") |
742 for name in names: |
762 for name in names: |
743 project.getModel().updateVCSStatus(name) |
763 project.getModel().updateVCSStatus(name) |
744 self.vcs.checkVCSStatus() |
764 self.vcs.checkVCSStatus() |
745 |
765 |
746 def __unstage(self): |
766 def __unstage(self): |
747 """ |
767 """ |
748 Private slot to handle the Unstage context menu entry. |
768 Private slot to handle the Unstage context menu entry. |
749 """ |
769 """ |
750 names = [os.path.join(self.dname, itm.text(self.__pathColumn)) |
770 names = [ |
751 for itm in self.__getUnstageableItems()] |
771 os.path.join(self.dname, itm.text(self.__pathColumn)) |
|
772 for itm in self.__getUnstageableItems() |
|
773 ] |
752 if not names: |
774 if not names: |
753 EricMessageBox.information( |
775 EricMessageBox.information( |
754 self, |
776 self, |
755 self.tr("Unstage"), |
777 self.tr("Unstage"), |
756 self.tr("""There are no unstageable entries""" |
778 self.tr( |
757 """ available/selected.""")) |
779 """There are no unstageable entries""" """ available/selected.""" |
|
780 ), |
|
781 ) |
758 return |
782 return |
759 |
783 |
760 self.vcs.gitUnstage(names) |
784 self.vcs.gitUnstage(names) |
761 self.on_refreshButton_clicked() |
785 self.on_refreshButton_clicked() |
762 |
786 |
763 project = ericApp().getObject("Project") |
787 project = ericApp().getObject("Project") |
764 for name in names: |
788 for name in names: |
765 project.getModel().updateVCSStatus(name) |
789 project.getModel().updateVCSStatus(name) |
766 self.vcs.checkVCSStatus() |
790 self.vcs.checkVCSStatus() |
767 |
791 |
768 def __forget(self): |
792 def __forget(self): |
769 """ |
793 """ |
770 Private slot to handle the Forget Missing context menu entry. |
794 Private slot to handle the Forget Missing context menu entry. |
771 """ |
795 """ |
772 names = [os.path.join(self.dname, itm.text(self.__pathColumn)) |
796 names = [ |
773 for itm in self.__getMissingItems()] |
797 os.path.join(self.dname, itm.text(self.__pathColumn)) |
|
798 for itm in self.__getMissingItems() |
|
799 ] |
774 if not names: |
800 if not names: |
775 EricMessageBox.information( |
801 EricMessageBox.information( |
776 self, |
802 self, |
777 self.tr("Forget Missing"), |
803 self.tr("Forget Missing"), |
778 self.tr("""There are no missing entries""" |
804 self.tr("""There are no missing entries""" """ available/selected."""), |
779 """ available/selected.""")) |
805 ) |
780 return |
806 return |
781 |
807 |
782 self.vcs.vcsRemove(names, stageOnly=True) |
808 self.vcs.vcsRemove(names, stageOnly=True) |
783 self.on_refreshButton_clicked() |
809 self.on_refreshButton_clicked() |
784 |
810 |
785 def __revert(self): |
811 def __revert(self): |
786 """ |
812 """ |
787 Private slot to handle the Revert context menu entry. |
813 Private slot to handle the Revert context menu entry. |
788 """ |
814 """ |
789 names = [os.path.join(self.dname, itm.text(self.__pathColumn)) |
815 names = [ |
790 for itm in self.__getStageableItems()] |
816 os.path.join(self.dname, itm.text(self.__pathColumn)) |
|
817 for itm in self.__getStageableItems() |
|
818 ] |
791 if not names: |
819 if not names: |
792 EricMessageBox.information( |
820 EricMessageBox.information( |
793 self, |
821 self, |
794 self.tr("Revert"), |
822 self.tr("Revert"), |
795 self.tr("""There are no uncommitted, unstaged changes""" |
823 self.tr( |
796 """ available/selected.""")) |
824 """There are no uncommitted, unstaged changes""" |
|
825 """ available/selected.""" |
|
826 ), |
|
827 ) |
797 return |
828 return |
798 |
829 |
799 self.vcs.vcsRevert(names) |
830 self.vcs.vcsRevert(names) |
800 self.raise_() |
831 self.raise_() |
801 self.activateWindow() |
832 self.activateWindow() |
900 result, ok = QInputDialog.getItem( |
942 result, ok = QInputDialog.getItem( |
901 None, |
943 None, |
902 self.tr("Differences Side-by-Side"), |
944 self.tr("Differences Side-by-Side"), |
903 self.tr("Select the compare method."), |
945 self.tr("Select the compare method."), |
904 messages, |
946 messages, |
905 0, False) |
947 0, |
|
948 False, |
|
949 ) |
906 if not ok: |
950 if not ok: |
907 return |
951 return |
908 |
952 |
909 if result == messages[0]: |
953 if result == messages[0]: |
910 revisions = ["", ""] |
954 revisions = ["", ""] |
911 else: |
955 else: |
912 revisions = ["HEAD", ""] |
956 revisions = ["HEAD", ""] |
913 else: |
957 else: |
914 revisions = ["HEAD", "Stage"] |
958 revisions = ["HEAD", "Stage"] |
915 |
959 |
916 self.vcs.vcsSbsDiff(names[0], revisions=revisions) |
960 self.vcs.vcsSbsDiff(names[0], revisions=revisions) |
917 |
961 |
918 def __getCommitableItems(self): |
962 def __getCommitableItems(self): |
919 """ |
963 """ |
920 Private method to retrieve all entries the user wants to commit. |
964 Private method to retrieve all entries the user wants to commit. |
921 |
965 |
922 @return list of all items, the user has checked |
966 @return list of all items, the user has checked |
923 """ |
967 """ |
924 commitableItems = [] |
968 commitableItems = [] |
925 for index in range(self.statusList.topLevelItemCount()): |
969 for index in range(self.statusList.topLevelItemCount()): |
926 itm = self.statusList.topLevelItem(index) |
970 itm = self.statusList.topLevelItem(index) |
927 if ( |
971 if itm.checkState(self.__toBeCommittedColumn) == Qt.CheckState.Checked: |
928 itm.checkState(self.__toBeCommittedColumn) == |
|
929 Qt.CheckState.Checked |
|
930 ): |
|
931 commitableItems.append(itm) |
972 commitableItems.append(itm) |
932 return commitableItems |
973 return commitableItems |
933 |
974 |
934 def __getCommitableUnselectedItems(self): |
975 def __getCommitableUnselectedItems(self): |
935 """ |
976 """ |
936 Private method to retrieve all entries the user may commit but hasn't |
977 Private method to retrieve all entries the user may commit but hasn't |
937 selected. |
978 selected. |
938 |
979 |
939 @return list of all items, the user has not checked |
980 @return list of all items, the user has not checked |
940 """ |
981 """ |
941 items = [] |
982 items = [] |
942 for index in range(self.statusList.topLevelItemCount()): |
983 for index in range(self.statusList.topLevelItemCount()): |
943 itm = self.statusList.topLevelItem(index) |
984 itm = self.statusList.topLevelItem(index) |
944 if ( |
985 if ( |
945 (itm.flags() & Qt.ItemFlag.ItemIsUserCheckable == |
986 itm.flags() & Qt.ItemFlag.ItemIsUserCheckable |
946 Qt.ItemFlag.ItemIsUserCheckable) and |
987 == Qt.ItemFlag.ItemIsUserCheckable |
947 itm.checkState(self.__toBeCommittedColumn) == |
988 ) and itm.checkState(self.__toBeCommittedColumn) == Qt.CheckState.Unchecked: |
948 Qt.CheckState.Unchecked |
|
949 ): |
|
950 items.append(itm) |
989 items.append(itm) |
951 return items |
990 return items |
952 |
991 |
953 def __getModifiedItems(self): |
992 def __getModifiedItems(self): |
954 """ |
993 """ |
955 Private method to retrieve all entries, that have a modified status. |
994 Private method to retrieve all entries, that have a modified status. |
956 |
995 |
957 @return list of all items with a modified status |
996 @return list of all items with a modified status |
958 """ |
997 """ |
959 modifiedItems = [] |
998 modifiedItems = [] |
960 for itm in self.statusList.selectedItems(): |
999 for itm in self.statusList.selectedItems(): |
961 if (itm.text(self.__statusWorkColumn) in |
1000 if ( |
962 self.modifiedIndicators or |
1001 itm.text(self.__statusWorkColumn) in self.modifiedIndicators |
963 itm.text(self.__statusIndexColumn) in |
1002 or itm.text(self.__statusIndexColumn) in self.modifiedIndicators |
964 self.modifiedIndicators): |
1003 ): |
965 modifiedItems.append(itm) |
1004 modifiedItems.append(itm) |
966 return modifiedItems |
1005 return modifiedItems |
967 |
1006 |
968 def __getModifiedOnlyItems(self): |
1007 def __getModifiedOnlyItems(self): |
969 """ |
1008 """ |
970 Private method to retrieve all entries, that have a modified status. |
1009 Private method to retrieve all entries, that have a modified status. |
971 |
1010 |
972 @return list of all items with a modified status |
1011 @return list of all items with a modified status |
973 """ |
1012 """ |
974 modifiedItems = [] |
1013 modifiedItems = [] |
975 for itm in self.statusList.selectedItems(): |
1014 for itm in self.statusList.selectedItems(): |
976 if (itm.text(self.__statusWorkColumn) in |
1015 if ( |
977 self.modifiedOnlyIndicators or |
1016 itm.text(self.__statusWorkColumn) in self.modifiedOnlyIndicators |
978 itm.text(self.__statusIndexColumn) in |
1017 or itm.text(self.__statusIndexColumn) in self.modifiedOnlyIndicators |
979 self.modifiedOnlyIndicators): |
1018 ): |
980 modifiedItems.append(itm) |
1019 modifiedItems.append(itm) |
981 return modifiedItems |
1020 return modifiedItems |
982 |
1021 |
983 def __getUnversionedItems(self): |
1022 def __getUnversionedItems(self): |
984 """ |
1023 """ |
985 Private method to retrieve all entries, that have an unversioned |
1024 Private method to retrieve all entries, that have an unversioned |
986 status. |
1025 status. |
987 |
1026 |
988 @return list of all items with an unversioned status |
1027 @return list of all items with an unversioned status |
989 """ |
1028 """ |
990 unversionedItems = [] |
1029 unversionedItems = [] |
991 for itm in self.statusList.selectedItems(): |
1030 for itm in self.statusList.selectedItems(): |
992 if itm.text(self.__statusWorkColumn) in self.unversionedIndicators: |
1031 if itm.text(self.__statusWorkColumn) in self.unversionedIndicators: |
993 unversionedItems.append(itm) |
1032 unversionedItems.append(itm) |
994 return unversionedItems |
1033 return unversionedItems |
995 |
1034 |
996 def __getStageableItems(self): |
1035 def __getStageableItems(self): |
997 """ |
1036 """ |
998 Private method to retrieve all entries, that have a stageable |
1037 Private method to retrieve all entries, that have a stageable |
999 status. |
1038 status. |
1000 |
1039 |
1001 @return list of all items with a stageable status |
1040 @return list of all items with a stageable status |
1002 """ |
1041 """ |
1003 stageableItems = [] |
1042 stageableItems = [] |
1004 for itm in self.statusList.selectedItems(): |
1043 for itm in self.statusList.selectedItems(): |
1005 if ( |
1044 if ( |
1006 itm.text(self.__statusWorkColumn) in |
1045 itm.text(self.__statusWorkColumn) |
1007 self.modifiedIndicators + self.unmergedIndicators |
1046 in self.modifiedIndicators + self.unmergedIndicators |
1008 ): |
1047 ): |
1009 stageableItems.append(itm) |
1048 stageableItems.append(itm) |
1010 return stageableItems |
1049 return stageableItems |
1011 |
1050 |
1012 def __getUnstageableItems(self): |
1051 def __getUnstageableItems(self): |
1013 """ |
1052 """ |
1014 Private method to retrieve all entries, that have an unstageable |
1053 Private method to retrieve all entries, that have an unstageable |
1015 status. |
1054 status. |
1016 |
1055 |
1017 @return list of all items with an unstageable status |
1056 @return list of all items with an unstageable status |
1018 """ |
1057 """ |
1019 unstageableItems = [] |
1058 unstageableItems = [] |
1020 for itm in self.statusList.selectedItems(): |
1059 for itm in self.statusList.selectedItems(): |
1021 if itm.text(self.__statusIndexColumn) in self.modifiedIndicators: |
1060 if itm.text(self.__statusIndexColumn) in self.modifiedIndicators: |
1022 unstageableItems.append(itm) |
1061 unstageableItems.append(itm) |
1023 return unstageableItems |
1062 return unstageableItems |
1024 |
1063 |
1025 def __getMissingItems(self): |
1064 def __getMissingItems(self): |
1026 """ |
1065 """ |
1027 Private method to retrieve all entries, that have a missing status. |
1066 Private method to retrieve all entries, that have a missing status. |
1028 |
1067 |
1029 @return list of all items with a missing status |
1068 @return list of all items with a missing status |
1030 """ |
1069 """ |
1031 missingItems = [] |
1070 missingItems = [] |
1032 for itm in self.statusList.selectedItems(): |
1071 for itm in self.statusList.selectedItems(): |
1033 if itm.text(self.__statusWorkColumn) in self.missingIndicators: |
1072 if itm.text(self.__statusWorkColumn) in self.missingIndicators: |
1034 missingItems.append(itm) |
1073 missingItems.append(itm) |
1035 return missingItems |
1074 return missingItems |
1036 |
1075 |
1037 def __getConflictingItems(self): |
1076 def __getConflictingItems(self): |
1038 """ |
1077 """ |
1039 Private method to retrieve all entries, that have a conflict status. |
1078 Private method to retrieve all entries, that have a conflict status. |
1040 |
1079 |
1041 @return list of all items with a conflict status |
1080 @return list of all items with a conflict status |
1042 """ |
1081 """ |
1043 conflictingItems = [] |
1082 conflictingItems = [] |
1044 for itm in self.statusList.selectedItems(): |
1083 for itm in self.statusList.selectedItems(): |
1045 if itm.data(0, self.ConflictRole): |
1084 if itm.data(0, self.ConflictRole): |
1046 conflictingItems.append(itm) |
1085 conflictingItems.append(itm) |
1047 return conflictingItems |
1086 return conflictingItems |
1048 |
1087 |
1049 def __commitSelect(self, selected): |
1088 def __commitSelect(self, selected): |
1050 """ |
1089 """ |
1051 Private slot to select or deselect all entries. |
1090 Private slot to select or deselect all entries. |
1052 |
1091 |
1053 @param selected commit selection state to be set (boolean) |
1092 @param selected commit selection state to be set (boolean) |
1054 """ |
1093 """ |
1055 for index in range(self.statusList.topLevelItemCount()): |
1094 for index in range(self.statusList.topLevelItemCount()): |
1056 itm = self.statusList.topLevelItem(index) |
1095 itm = self.statusList.topLevelItem(index) |
1057 if ( |
1096 if ( |
1058 itm.flags() & Qt.ItemFlag.ItemIsUserCheckable == |
1097 itm.flags() & Qt.ItemFlag.ItemIsUserCheckable |
1059 Qt.ItemFlag.ItemIsUserCheckable |
1098 == Qt.ItemFlag.ItemIsUserCheckable |
1060 ): |
1099 ): |
1061 if selected: |
1100 if selected: |
1062 itm.setCheckState(self.__toBeCommittedColumn, |
1101 itm.setCheckState(self.__toBeCommittedColumn, Qt.CheckState.Checked) |
1063 Qt.CheckState.Checked) |
|
1064 else: |
1102 else: |
1065 itm.setCheckState(self.__toBeCommittedColumn, |
1103 itm.setCheckState( |
1066 Qt.CheckState.Unchecked) |
1104 self.__toBeCommittedColumn, Qt.CheckState.Unchecked |
1067 |
1105 ) |
|
1106 |
1068 ########################################################################### |
1107 ########################################################################### |
1069 ## Diff handling methods below |
1108 ## Diff handling methods below |
1070 ########################################################################### |
1109 ########################################################################### |
1071 |
1110 |
1072 def __generateDiffs(self): |
1111 def __generateDiffs(self): |
1073 """ |
1112 """ |
1074 Private slot to generate diff outputs for the selected item. |
1113 Private slot to generate diff outputs for the selected item. |
1075 """ |
1114 """ |
1076 self.lDiffEdit.clear() |
1115 self.lDiffEdit.clear() |
1077 self.rDiffEdit.clear() |
1116 self.rDiffEdit.clear() |
1078 with contextlib.suppress(AttributeError): |
1117 with contextlib.suppress(AttributeError): |
1079 self.lDiffHighlighter.regenerateRules() |
1118 self.lDiffHighlighter.regenerateRules() |
1080 self.rDiffHighlighter.regenerateRules() |
1119 self.rDiffHighlighter.regenerateRules() |
1081 |
1120 |
1082 selectedItems = self.statusList.selectedItems() |
1121 selectedItems = self.statusList.selectedItems() |
1083 if len(selectedItems) == 1: |
1122 if len(selectedItems) == 1: |
1084 fn = os.path.join(self.dname, |
1123 fn = os.path.join(self.dname, selectedItems[0].text(self.__pathColumn)) |
1085 selectedItems[0].text(self.__pathColumn)) |
|
1086 self.__diffGenerator.start(fn, diffMode="work2stage2repo") |
1124 self.__diffGenerator.start(fn, diffMode="work2stage2repo") |
1087 |
1125 |
1088 def __generatorFinished(self): |
1126 def __generatorFinished(self): |
1089 """ |
1127 """ |
1090 Private slot connected to the finished signal of the diff generator. |
1128 Private slot connected to the finished signal of the diff generator. |
1091 """ |
1129 """ |
1092 diff1, diff2 = self.__diffGenerator.getResult()[:2] |
1130 diff1, diff2 = self.__diffGenerator.getResult()[:2] |
1093 |
1131 |
1094 if diff1: |
1132 if diff1: |
1095 self.lDiffParser = GitDiffParser(diff1) |
1133 self.lDiffParser = GitDiffParser(diff1) |
1096 for line in diff1[:]: |
1134 for line in diff1[:]: |
1097 if line.startswith("@@ "): |
1135 if line.startswith("@@ "): |
1098 break |
1136 break |
1099 else: |
1137 else: |
1100 diff1.pop(0) |
1138 diff1.pop(0) |
1101 self.lDiffEdit.setPlainText("".join(diff1)) |
1139 self.lDiffEdit.setPlainText("".join(diff1)) |
1102 else: |
1140 else: |
1103 self.lDiffParser = None |
1141 self.lDiffParser = None |
1104 |
1142 |
1105 if diff2: |
1143 if diff2: |
1106 self.rDiffParser = GitDiffParser(diff2) |
1144 self.rDiffParser = GitDiffParser(diff2) |
1107 for line in diff2[:]: |
1145 for line in diff2[:]: |
1108 if line.startswith("@@ "): |
1146 if line.startswith("@@ "): |
1109 break |
1147 break |
1110 else: |
1148 else: |
1111 diff2.pop(0) |
1149 diff2.pop(0) |
1112 self.rDiffEdit.setPlainText("".join(diff2)) |
1150 self.rDiffEdit.setPlainText("".join(diff2)) |
1113 else: |
1151 else: |
1114 self.rDiffParser = None |
1152 self.rDiffParser = None |
1115 |
1153 |
1116 for diffEdit in [self.lDiffEdit, self.rDiffEdit]: |
1154 for diffEdit in [self.lDiffEdit, self.rDiffEdit]: |
1117 tc = diffEdit.textCursor() |
1155 tc = diffEdit.textCursor() |
1118 tc.movePosition(QTextCursor.MoveOperation.Start) |
1156 tc.movePosition(QTextCursor.MoveOperation.Start) |
1119 diffEdit.setTextCursor(tc) |
1157 diffEdit.setTextCursor(tc) |
1120 diffEdit.ensureCursorVisible() |
1158 diffEdit.ensureCursorVisible() |
1121 |
1159 |
1122 def __showLDiffContextMenu(self, coord): |
1160 def __showLDiffContextMenu(self, coord): |
1123 """ |
1161 """ |
1124 Private slot to show the context menu of the status list. |
1162 Private slot to show the context menu of the status list. |
1125 |
1163 |
1126 @param coord position of the mouse pointer (QPoint) |
1164 @param coord position of the mouse pointer (QPoint) |
1127 """ |
1165 """ |
1128 if bool(self.lDiffEdit.toPlainText()): |
1166 if bool(self.lDiffEdit.toPlainText()): |
1129 cursor = self.lDiffEdit.textCursor() |
1167 cursor = self.lDiffEdit.textCursor() |
1130 if cursor.hasSelection(): |
1168 if cursor.hasSelection(): |
1135 else: |
1173 else: |
1136 self.__stageLinesAct.setEnabled(False) |
1174 self.__stageLinesAct.setEnabled(False) |
1137 self.__revertLinesAct.setEnabled(False) |
1175 self.__revertLinesAct.setEnabled(False) |
1138 self.__stageHunkAct.setEnabled(True) |
1176 self.__stageHunkAct.setEnabled(True) |
1139 self.__revertHunkAct.setEnabled(True) |
1177 self.__revertHunkAct.setEnabled(True) |
1140 |
1178 |
1141 cursor = self.lDiffEdit.cursorForPosition(coord) |
1179 cursor = self.lDiffEdit.cursorForPosition(coord) |
1142 self.lDiffEdit.setTextCursor(cursor) |
1180 self.lDiffEdit.setTextCursor(cursor) |
1143 |
1181 |
1144 self.__lDiffMenu.popup(self.lDiffEdit.mapToGlobal(coord)) |
1182 self.__lDiffMenu.popup(self.lDiffEdit.mapToGlobal(coord)) |
1145 |
1183 |
1146 def __showRDiffContextMenu(self, coord): |
1184 def __showRDiffContextMenu(self, coord): |
1147 """ |
1185 """ |
1148 Private slot to show the context menu of the status list. |
1186 Private slot to show the context menu of the status list. |
1149 |
1187 |
1150 @param coord position of the mouse pointer (QPoint) |
1188 @param coord position of the mouse pointer (QPoint) |
1151 """ |
1189 """ |
1152 if bool(self.rDiffEdit.toPlainText()): |
1190 if bool(self.rDiffEdit.toPlainText()): |
1153 cursor = self.rDiffEdit.textCursor() |
1191 cursor = self.rDiffEdit.textCursor() |
1154 if cursor.hasSelection(): |
1192 if cursor.hasSelection(): |
1155 self.__unstageLinesAct.setEnabled(True) |
1193 self.__unstageLinesAct.setEnabled(True) |
1156 self.__unstageHunkAct.setEnabled(False) |
1194 self.__unstageHunkAct.setEnabled(False) |
1157 else: |
1195 else: |
1158 self.__unstageLinesAct.setEnabled(False) |
1196 self.__unstageLinesAct.setEnabled(False) |
1159 self.__unstageHunkAct.setEnabled(True) |
1197 self.__unstageHunkAct.setEnabled(True) |
1160 |
1198 |
1161 cursor = self.rDiffEdit.cursorForPosition(coord) |
1199 cursor = self.rDiffEdit.cursorForPosition(coord) |
1162 self.rDiffEdit.setTextCursor(cursor) |
1200 self.rDiffEdit.setTextCursor(cursor) |
1163 |
1201 |
1164 self.__rDiffMenu.popup(self.rDiffEdit.mapToGlobal(coord)) |
1202 self.__rDiffMenu.popup(self.rDiffEdit.mapToGlobal(coord)) |
1165 |
1203 |
1166 def __stageHunkOrLines(self): |
1204 def __stageHunkOrLines(self): |
1167 """ |
1205 """ |
1168 Private method to stage the selected lines or hunk. |
1206 Private method to stage the selected lines or hunk. |
1169 """ |
1207 """ |
1170 cursor = self.lDiffEdit.textCursor() |
1208 cursor = self.lDiffEdit.textCursor() |
1171 startIndex, endIndex = self.__selectedLinesIndexes(self.lDiffEdit) |
1209 startIndex, endIndex = self.__selectedLinesIndexes(self.lDiffEdit) |
1172 patch = ( |
1210 patch = ( |
1173 self.lDiffParser.createLinesPatch(startIndex, endIndex) |
1211 self.lDiffParser.createLinesPatch(startIndex, endIndex) |
1174 if cursor.hasSelection() else |
1212 if cursor.hasSelection() |
1175 self.lDiffParser.createHunkPatch(startIndex) |
1213 else self.lDiffParser.createHunkPatch(startIndex) |
1176 ) |
1214 ) |
1177 if patch: |
1215 if patch: |
1178 patchFile = self.__tmpPatchFileName() |
1216 patchFile = self.__tmpPatchFileName() |
1179 try: |
1217 try: |
1180 with open(patchFile, "w") as f: |
1218 with open(patchFile, "w") as f: |
1181 f.write(patch) |
1219 f.write(patch) |
1182 self.vcs.gitApply(self.dname, patchFile, cached=True, |
1220 self.vcs.gitApply(self.dname, patchFile, cached=True, noDialog=True) |
1183 noDialog=True) |
|
1184 self.on_refreshButton_clicked() |
1221 self.on_refreshButton_clicked() |
1185 finally: |
1222 finally: |
1186 os.remove(patchFile) |
1223 os.remove(patchFile) |
1187 |
1224 |
1188 def __unstageHunkOrLines(self): |
1225 def __unstageHunkOrLines(self): |
1189 """ |
1226 """ |
1190 Private method to unstage the selected lines or hunk. |
1227 Private method to unstage the selected lines or hunk. |
1191 """ |
1228 """ |
1192 cursor = self.rDiffEdit.textCursor() |
1229 cursor = self.rDiffEdit.textCursor() |
1193 startIndex, endIndex = self.__selectedLinesIndexes(self.rDiffEdit) |
1230 startIndex, endIndex = self.__selectedLinesIndexes(self.rDiffEdit) |
1194 patch = ( |
1231 patch = ( |
1195 self.rDiffParser.createLinesPatch(startIndex, endIndex, |
1232 self.rDiffParser.createLinesPatch(startIndex, endIndex, reverse=True) |
1196 reverse=True) |
1233 if cursor.hasSelection() |
1197 if cursor.hasSelection() else |
1234 else self.rDiffParser.createHunkPatch(startIndex) |
1198 self.rDiffParser.createHunkPatch(startIndex) |
|
1199 ) |
1235 ) |
1200 if patch: |
1236 if patch: |
1201 patchFile = self.__tmpPatchFileName() |
1237 patchFile = self.__tmpPatchFileName() |
1202 try: |
1238 try: |
1203 with open(patchFile, "w") as f: |
1239 with open(patchFile, "w") as f: |
1204 f.write(patch) |
1240 f.write(patch) |
1205 self.vcs.gitApply(self.dname, patchFile, cached=True, |
1241 self.vcs.gitApply( |
1206 reverse=True, noDialog=True) |
1242 self.dname, patchFile, cached=True, reverse=True, noDialog=True |
|
1243 ) |
1207 self.on_refreshButton_clicked() |
1244 self.on_refreshButton_clicked() |
1208 finally: |
1245 finally: |
1209 os.remove(patchFile) |
1246 os.remove(patchFile) |
1210 |
1247 |
1211 def __revertHunkOrLines(self): |
1248 def __revertHunkOrLines(self): |
1212 """ |
1249 """ |
1213 Private method to revert the selected lines or hunk. |
1250 Private method to revert the selected lines or hunk. |
1214 """ |
1251 """ |
1215 cursor = self.lDiffEdit.textCursor() |
1252 cursor = self.lDiffEdit.textCursor() |
1216 startIndex, endIndex = self.__selectedLinesIndexes(self.lDiffEdit) |
1253 startIndex, endIndex = self.__selectedLinesIndexes(self.lDiffEdit) |
1217 title = ( |
1254 title = ( |
1218 self.tr("Revert selected lines") |
1255 self.tr("Revert selected lines") |
1219 if cursor.hasSelection() else |
1256 if cursor.hasSelection() |
1220 self.tr("Revert hunk") |
1257 else self.tr("Revert hunk") |
1221 ) |
1258 ) |
1222 res = EricMessageBox.yesNo( |
1259 res = EricMessageBox.yesNo( |
1223 self, |
1260 self, |
1224 title, |
1261 title, |
1225 self.tr("""Are you sure you want to revert the selected""" |
1262 self.tr("""Are you sure you want to revert the selected""" """ changes?"""), |
1226 """ changes?""")) |
1263 ) |
1227 if res: |
1264 if res: |
1228 if cursor.hasSelection(): |
1265 if cursor.hasSelection(): |
1229 patch = self.lDiffParser.createLinesPatch(startIndex, endIndex, |
1266 patch = self.lDiffParser.createLinesPatch( |
1230 reverse=True) |
1267 startIndex, endIndex, reverse=True |
|
1268 ) |
1231 else: |
1269 else: |
1232 patch = self.lDiffParser.createHunkPatch(startIndex) |
1270 patch = self.lDiffParser.createHunkPatch(startIndex) |
1233 if patch: |
1271 if patch: |
1234 patchFile = self.__tmpPatchFileName() |
1272 patchFile = self.__tmpPatchFileName() |
1235 try: |
1273 try: |
1236 with open(patchFile, "w") as f: |
1274 with open(patchFile, "w") as f: |
1237 f.write(patch) |
1275 f.write(patch) |
1238 self.vcs.gitApply(self.dname, patchFile, reverse=True, |
1276 self.vcs.gitApply( |
1239 noDialog=True) |
1277 self.dname, patchFile, reverse=True, noDialog=True |
|
1278 ) |
1240 self.on_refreshButton_clicked() |
1279 self.on_refreshButton_clicked() |
1241 finally: |
1280 finally: |
1242 os.remove(patchFile) |
1281 os.remove(patchFile) |
1243 |
1282 |
1244 def __selectedLinesIndexes(self, diffEdit): |
1283 def __selectedLinesIndexes(self, diffEdit): |
1245 """ |
1284 """ |
1246 Private method to extract the indexes of the selected lines. |
1285 Private method to extract the indexes of the selected lines. |
1247 |
1286 |
1248 @param diffEdit reference to the edit widget (QTextEdit) |
1287 @param diffEdit reference to the edit widget (QTextEdit) |
1249 @return tuple of start and end indexes (integer, integer) |
1288 @return tuple of start and end indexes (integer, integer) |
1250 """ |
1289 """ |
1251 cursor = diffEdit.textCursor() |
1290 cursor = diffEdit.textCursor() |
1252 selectionStart = cursor.selectionStart() |
1291 selectionStart = cursor.selectionStart() |
1253 selectionEnd = cursor.selectionEnd() |
1292 selectionEnd = cursor.selectionEnd() |
1254 |
1293 |
1255 startIndex = -1 |
1294 startIndex = -1 |
1256 |
1295 |
1257 lineStart = 0 |
1296 lineStart = 0 |
1258 for lineIdx, line in enumerate(diffEdit.toPlainText().splitlines()): |
1297 for lineIdx, line in enumerate(diffEdit.toPlainText().splitlines()): |
1259 lineEnd = lineStart + len(line) |
1298 lineEnd = lineStart + len(line) |
1260 if lineStart <= selectionStart <= lineEnd: |
1299 if lineStart <= selectionStart <= lineEnd: |
1261 startIndex = lineIdx |
1300 startIndex = lineIdx |