4 from PyQt4
import QtGui
5 from PyQt4
import QtCore
6 from PyQt4
.QtGui
import QDialog
7 from PyQt4
.QtGui
import QMessageBox
8 from PyQt4
.QtGui
import QMenu
9 from qobserver
import QObserver
14 from views
import GitPushDialog
15 from views
import GitBranchDialog
16 from views
import GitCreateBranchDialog
17 from views
import GitCommitBrowser
18 from repobrowsercontroller
import GitRepoBrowserController
19 from createbranchcontroller
import GitCreateBranchController
20 from pushcontroller
import GitPushController
22 class GitController(QObserver
):
23 '''The controller is a mediator between the model and view.
24 It allows for a clean decoupling between view and model classes.'''
26 def __init__(self
, model
, view
):
27 QObserver
.__init
__(self
, model
, view
)
29 # The diff-display context menu
31 self
.__staged
_diff
_in
_view
= True
33 # Diff display context menu
34 view
.displayText
.controller
= self
35 view
.displayText
.contextMenuEvent
= self
.__menu
_event
37 # Default to creating a new commit(i.e. not an amend commit)
38 view
.newCommitRadio
.setChecked(True)
40 # Binds a specific model attribute to a view widget,
42 self
.model_to_view('commitmsg', 'commitText')
43 self
.model_to_view('staged', 'stagedList')
44 self
.model_to_view('all_unstaged', 'unstagedList')
46 # When a model attribute changes, this runs a specific action
47 self
.add_actions('staged', self
.action_staged
)
48 self
.add_actions('all_unstaged', self
.action_all_unstaged
)
50 # Routes signals for multiple widgets to our callbacks
52 self
.add_signals('textChanged()', view
.commitText
)
53 self
.add_signals('stateChanged(int)', view
.untrackedCheckBox
)
55 self
.add_signals('released()',
56 view
.stageButton
, view
.commitButton
,
57 view
.pushButton
, view
.signOffButton
,)
59 self
.add_signals('triggered()',
61 view
.createBranch
, view
.checkoutBranch
,
62 view
.rebaseBranch
, view
.deleteBranch
,
63 view
.setCommitMessage
, view
.commit
,
64 view
.stageChanged
, view
.stageUntracked
,
65 view
.stageSelected
, view
.unstageAll
,
68 view
.browseBranch
, view
.browseOtherBranch
,
69 view
.visualizeAll
, view
.visualizeCurrent
,
70 view
.exportPatches
, view
.cherryPick
,
72 view
.cut
, view
.copy
, view
.paste
, view
.delete
,
73 view
.selectAll
, view
.undo
, view
.redo
,)
75 self
.add_signals('itemClicked(QListWidgetItem *)',
76 view
.stagedList
, view
.unstagedList
,)
78 self
.add_signals('itemSelectionChanged()',
79 view
.stagedList
, view
.unstagedList
,)
81 self
.add_signals('splitterMoved(int,int)',
82 view
.splitter_top
, view
.splitter_bottom
)
85 self
.connect(QtGui
.qApp
, 'lastWindowClosed()',
86 self
.last_window_closed
)
88 # These callbacks are called in response to the signals
89 # defined above. One property of the QObserver callback
90 # mechanism is that the model is passed in as the first
91 # argument to the callback. This allows for a single
92 # controller to manage multiple models, though this
93 # isn't used at the moment.
95 # Actions that delegate directly to the model
96 'signOffButton': model
.add_signoff
,
97 'setCommitMessage': model
.get_prev_commitmsg
,
99 'stageButton': self
.stage_selected
,
100 'commitButton': self
.commit
,
101 'pushButton': self
.push
,
103 'stagedList': self
.diff_staged
,
104 'unstagedList': self
.diff_unstaged
,
106 'untrackedCheckBox': self
.rescan
,
108 'rescan': self
.rescan
,
109 'createBranch': self
.branch_create
,
110 'deleteBranch': self
.branch_delete
,
111 'checkoutBranch': self
.checkout_branch
,
112 'rebaseBranch': self
.rebase
,
113 'commit': self
.commit
,
114 'stageChanged': self
.stage_changed
,
115 'stageUntracked': self
.stage_untracked
,
116 'stageSelected': self
.stage_selected
,
117 'unstageAll': self
.unstage_all
,
118 'unstageSelected': self
.unstage_selected
,
119 'showDiffstat': self
.show_diffstat
,
120 'browseBranch': self
.browse_current
,
121 'browseOtherBranch': self
.browse_other
,
122 'visualizeCurrent': self
.viz_current
,
123 'visualizeAll': self
.viz_all
,
124 'exportPatches': self
.export_patches
,
125 'cherryPick': self
.cherry_pick
,
126 'loadCommitMsg': self
.load_commitmsg
,
130 'delete': self
.delete
,
131 'selectAll': self
.select_all
,
132 'undo': self
.view
.commitText
.undo
,
135 'splitter_top': self
.splitter_top_event
,
136 'splitter_bottom': self
.splitter_bottom_event
,
139 # Handle double-clicks in the staged/unstaged lists.
140 # These are vanilla signal/slots since the qobserver
141 # signal routing is already handling these lists' signals.
142 self
.connect(view
.unstagedList
,
143 'itemDoubleClicked(QListWidgetItem*)',
146 self
.connect(view
.stagedList
,
147 'itemDoubleClicked(QListWidgetItem*)',
148 self
.unstage_selected
)
150 # Delegate window move events here
151 self
.view
.moveEvent
= self
.move_event
152 self
.view
.resizeEvent
= self
.resize_event
155 self
.__read
_config
_settings
()
158 # Setup the inotify watchdog
159 self
.__start
_inotify
_thread
()
161 #####################################################################
162 # Actions triggered during model updates
164 def action_staged(self
, widget
):
165 self
.__update
_listwidget
(widget
,
166 self
.model
.get_staged(), staged
=True)
168 def action_all_unstaged(self
, widget
):
169 self
.__update
_listwidget
(widget
,
170 self
.model
.get_unstaged(), staged
=False)
172 if self
.view
.untrackedCheckBox
.isChecked():
173 self
.__update
_listwidget
(widget
,
174 self
.model
.get_untracked(),
179 #####################################################################
182 def branch_create(self
):
183 view
= GitCreateBranchDialog(self
.view
)
184 controller
= GitCreateBranchController(self
.model
, view
)
186 result
= view
.exec_()
187 if result
== QDialog
.Accepted
:
190 def branch_delete(self
):
191 dlg
= GitBranchDialog(self
.view
, branches
=cmds
.git_branch())
192 branch
= dlg
.getSelectedBranch()
193 if not branch
: return
194 qtutils
.show_command(self
.view
,
195 cmds
.git_branch(name
=branch
, delete
=True))
197 def browse_current(self
):
198 self
.__browse
_branch
(cmds
.git_current_branch())
200 def browse_other(self
):
201 # Prompt for a branch to browse
202 branches
= self
.model
.all_branches()
203 dialog
= GitBranchDialog(self
.view
, branches
=branches
)
205 # Launch the repobrowser
206 self
.__browse
_branch
(dialog
.getSelectedBranch())
208 def checkout_branch(self
):
209 dlg
= GitBranchDialog(self
.view
, cmds
.git_branch())
210 branch
= dlg
.getSelectedBranch()
211 if not branch
: return
212 qtutils
.show_command(self
.view
, cmds
.git_checkout(branch
))
215 def cherry_pick(self
):
216 '''Starts a cherry-picking session.'''
217 (revs
, summaries
) = cmds
.git_log(all
=True)
218 selection
, idxs
= self
.__select
_commits
(revs
, summaries
)
219 if not selection
: return
220 output
= cmds
.git_cherry_pick(selection
)
221 self
.__show
_command
(self
.tr(output
))
224 '''Sets up data and calls cmds.commit.'''
225 msg
= self
.model
.get_commitmsg()
227 error_msg
= self
.tr(""
228 + "Please supply a commit message.\n"
230 + "A good commit message has the following format:\n"
232 + "- First line: Describe in one sentence what you did.\n"
233 + "- Second line: Blank\n"
234 + "- Remaining lines: Describe why this change is good.\n")
236 self
.__show
_command
(error_msg
)
239 files
= self
.model
.get_staged()
242 + "No changes to commit.\n"
244 + "You must stage at least 1 file before you can commit.\n")
245 self
.__show
_command
(errmsg
)
249 output
= cmds
.git_commit(msg
,
250 amend
=self
.view
.amendRadio
.isChecked())
253 self
.view
.newCommitRadio
.setChecked(True)
254 self
.view
.amendRadio
.setChecked(False)
255 self
.model
.set_commitmsg('')
256 self
.__show
_command
(output
)
258 def commit_sha1_selected(self
, browser
, revs
):
259 '''This callback is called when a commit browser's
260 item is selected. This callback puts the current
261 revision sha1 into the commitText field.
262 This callback also puts shows the commit in the
263 browser's commit textedit and copies it into
264 the global clipboard/selection.'''
265 current
= browser
.commitList
.currentRow()
266 item
= browser
.commitList
.item(current
)
267 if not item
.isSelected():
268 browser
.commitText
.setText('')
269 browser
.revisionLine
.setText('')
272 # Get the sha1 and put it in the revision line
274 browser
.revisionLine
.setText(sha1
)
275 browser
.revisionLine
.selectAll()
277 # Lookup the sha1's commit
278 commit_diff
= cmds
.git_diff(commit
=sha1
,cached
=False)
279 browser
.commitText
.setText(commit_diff
)
281 # Copy the sha1 into the clipboard
282 qtutils
.set_clipboard(sha1
)
284 # use *rest to handle being called from different signals
285 def diff_staged(self
, *rest
):
286 self
.__staged
_diff
_in
_view
= True
287 widget
= self
.view
.stagedList
288 row
, selected
= qtutils
.get_selected_row(widget
)
291 self
.__reset
_display
()
294 filename
= self
.model
.get_staged()[row
]
295 diff
= cmds
.git_diff(filename
=filename
, cached
=True)
297 if os
.path
.exists(filename
):
298 self
.__set
_info
(self
.tr('Staged for commit'))
300 self
.__set
_info
(self
.tr('Staged for removal'))
302 self
.view
.displayText
.setText(diff
)
304 # use *rest to handle being called from different signals
305 def diff_unstaged(self
,*rest
):
306 self
.__staged
_diff
_in
_view
= False
307 widget
= self
.view
.unstagedList
309 row
, selected
= qtutils
.get_selected_row(widget
)
311 self
.__reset
_display
()
314 filename
=(self
.model
.get_unstaged()
315 + self
.model
.get_untracked())[row
]
316 if os
.path
.isdir(filename
):
317 self
.__set
_info
(self
.tr('Untracked directory'))
318 output
= os
.linesep
.join(os
.listdir(filename
))
319 self
.view
.displayText
.setText(output
)
322 if filename
in self
.model
.get_unstaged():
323 diff
= cmds
.git_diff(filename
=filename
, cached
=False)
325 self
.__set
_info
(self
.tr('Modified, not staged'))
328 cmd
= 'file -b %s' % utils
.shell_quote(filename
)
329 file_type
= commands
.getoutput(cmd
)
331 if 'binary' in file_type
or 'data' in file_type
:
332 sq_filename
= utils
.shell_quote(filename
)
333 cmd
= 'hexdump -C %s' % sq_filename
334 contents
= commands
.getoutput(cmd
)
336 if os
.path
.exists(filename
):
337 file = open(filename
, 'r')
338 contents
= file.read()
343 self
.__set
_info
(self
.tr('Untracked, not staged')
347 self
.view
.displayText
.setText(msg
)
349 def copy_display(self
):
350 cursor
= self
.view
.displayText
.textCursor()
351 selection
= cursor
.selection().toPlainText()
352 qtutils
.set_clipboard(selection
)
354 def export_patches(self
):
355 '''Launches the commit browser and exports the selected
358 (revs
, summaries
) = cmds
.git_log()
359 selection
, idxs
= self
.__select
_commits
(revs
, summaries
)
360 if not selection
: return
362 # now get the selected indices to determine whether
363 # a range of consecutive commits were selected
364 selected_range
= range(idxs
[0], idxs
[-1] + 1)
365 export_range
= len(idxs
) > 1 and idxs
== selected_range
367 output
= cmds
.git_format_patch(selection
, export_range
)
368 self
.__show
_command
(output
)
370 def get_commit_msg(self
):
371 self
.model
.retrieve_latest_commitmsg()
373 def last_window_closed(self
):
374 '''Save config settings and cleanup the any inotify threads.'''
376 self
.__save
_config
_settings
()
378 if not self
.inotify_thread
: return
379 if not self
.inotify_thread
.isRunning(): return
381 self
.inotify_thread
.abort
= True
382 self
.inotify_thread
.quit()
383 self
.inotify_thread
.wait()
385 def load_commitmsg(self
):
386 file = qtutils
.open_dialog(self
.view
,
387 self
.tr('Load Commit Message...'),
391 defaults
.DIRECTORY
= os
.path
.dirname(file)
392 slushy
= utils
.slurp(file)
393 self
.model
.set_commitmsg(slushy
)
396 dlg
= GitBranchDialog(self
.view
, cmds
.git_branch())
397 dlg
.setWindowTitle("Select the current branch's new root")
398 branch
= dlg
.getSelectedBranch()
399 if not branch
: return
400 qtutils
.show_command(self
.view
, cmds
.git_rebase(branch
))
402 # use *rest to handle being called from the checkbox signal
403 def rescan(self
, *rest
):
404 '''Populates view widgets with results from "git status."'''
406 self
.view
.statusBar().showMessage(
407 self
.tr('Scanning for modified files ...'))
409 # Rescan for repo updates
410 self
.model
.update_status()
412 # Scan for branch changes
413 self
.__set
_branch
_ui
_items
()
415 if not self
.model
.has_squash_msg(): return
417 if self
.model
.get_commitmsg():
418 answer
= qtutils
.question(self
.view
,
419 self
.tr('Import Commit Message?'),
420 self
.tr('A commit message from an in-progress'
421 + ' merge was found.\nImport it?'))
423 if not answer
: return
425 # Set the new commit message
426 self
.model
.set_commitmsg(self
.model
.get_squash_msg())
429 model
= self
.model
.clone()
430 view
= GitPushDialog(self
.view
)
431 controller
= GitPushController(model
,view
)
440 cursor
= self
.view
.commitText
.textCursor()
441 selection
= cursor
.selection().toPlainText()
442 qtutils
.set_clipboard(selection
)
444 def paste(self
): self
.view
.commitText
.paste()
445 def undo(self
): self
.view
.commitText
.undo()
446 def redo(self
): self
.view
.commitText
.redo()
447 def select_all(self
): self
.view
.commitText
.selectAll()
449 self
.view
.commitText
.textCursor().removeSelectedText()
451 def show_diffstat(self
):
452 '''Show the diffstat from the latest commit.'''
453 self
.__show
_command
(cmds
.git_diff_stat(), rescan
=False)
456 #####################################################################
459 def __diff_selection(self
):
460 cursor
= self
.view
.displayText
.textCursor()
461 offset
= cursor
.position()
462 selection
= cursor
.selection().toPlainText()
463 num_selected_lines
= selection
.count(os
.linesep
)
464 return offset
, selection
466 def process_diff_selection(self
,
472 filename
= qtutils
.get_selected_item(widget
, items
)
473 if not filename
: return
475 parser
= utils
.DiffParser(
476 *cmds
.git_diff(filename
=filename
, with_diff_header
=True,
477 cached
=cached
, reverse
=cached
))
479 # Always index into the non-reversed diff
481 cmds
.git_diff(filename
=filename
, with_diff_header
=True,
482 cached
=cached
, reverse
=False)
484 offset
, selection
= self
.__diff
_selection
()
486 start
= diff
.index(selection
)
487 end
= start
+ len(selection
)
488 parser
.set_diffs_to_range(start
, end
)
490 parser
.set_diff_to_offset(offset
)
493 if not parser
.diffs
: return
495 # Process diff selection only
497 for idx
in parser
.selected
:
498 contents
= parser
.get_diff_subset(idx
, start
, end
)
500 tmpfile
= utils
.get_tmp_filename()
501 utils
.write(tmpfile
, contents
)
502 self
.model
.apply_diff(tmpfile
)
505 # Process a complete hunk
507 for idx
, diff
in enumerate(parser
.diffs
):
508 tmpfile
= utils
.get_tmp_filename()
509 if parser
.write_diff(tmpfile
,idx
):
510 self
.model
.apply_diff(tmpfile
)
514 def stage_hunk(self
):
515 self
.process_diff_selection(
516 self
.model
.get_unstaged(),
517 self
.view
.unstagedList
,
520 def stage_hunks(self
):
521 self
.process_diff_selection(
522 self
.model
.get_unstaged(),
523 self
.view
.unstagedList
,
527 def unstage_hunk(self
, cached
=True):
528 self
.process_diff_selection(
529 self
.model
.get_staged(),
530 self
.view
.stagedList
,
533 def unstage_hunks(self
):
534 self
.process_diff_selection(
535 self
.model
.get_staged(),
536 self
.view
.stagedList
,
540 # #######################################################################
543 def stage_changed(self
):
544 '''Stage all changed files for commit.'''
545 output
= cmds
.git_add(self
.model
.get_unstaged())
546 self
.__show
_command
(output
)
548 def stage_untracked(self
):
549 '''Stage all untracked files for commit.'''
550 output
= cmds
.git_add(self
.model
.get_untracked())
551 self
.__show
_command
(output
)
553 # use *rest to handle being called from different signals
554 def stage_selected(self
,*rest
):
555 '''Use "git add" to add items to the git index.
556 This is a thin wrapper around __apply_to_list.'''
557 command
= cmds
.git_add_or_remove
558 widget
= self
.view
.unstagedList
559 items
= self
.model
.get_all_unstaged()
560 self
.__show
_command
(self
.__apply
_to
_list
(command
,widget
,items
))
562 # use *rest to handle being called from different signals
563 def unstage_selected(self
, *rest
):
564 '''Use "git reset" to remove items from the git index.
565 This is a thin wrapper around __apply_to_list.'''
566 command
= cmds
.git_reset
567 widget
= self
.view
.stagedList
568 items
= self
.model
.get_staged()
569 self
.__apply
_to
_list
(command
, widget
, items
)
571 def unstage_all(self
):
572 '''Use "git reset" to remove all items from the git index.'''
573 cmds
.git_reset(self
.model
.get_staged())
577 '''Visualizes the entire git history using gitk.'''
578 utils
.fork('gitk','--all')
580 def viz_current(self
):
581 '''Visualizes the current branch's history using gitk.'''
582 utils
.fork('gitk', cmds
.git_current_branch())
584 # These actions monitor window resizes, splitter changes, etc.
585 def move_event(self
, event
):
586 defaults
.X
= event
.pos().x()
587 defaults
.Y
= event
.pos().y()
589 def resize_event(self
, event
):
590 defaults
.WIDTH
= event
.size().width()
591 defaults
.HEIGHT
= event
.size().height()
593 def splitter_top_event(self
,*rest
):
594 sizes
= self
.view
.splitter_top
.sizes()
595 defaults
.SPLITTER_TOP_0
= sizes
[0]
596 defaults
.SPLITTER_TOP_1
= sizes
[1]
598 def splitter_bottom_event(self
,*rest
):
599 sizes
= self
.view
.splitter_bottom
.sizes()
600 defaults
.SPLITTER_BOTTOM_0
= sizes
[0]
601 defaults
.SPLITTER_BOTTOM_1
= sizes
[1]
603 #####################################################################
606 def __apply_to_list(self
, command
, widget
, items
):
607 '''This is a helper method that retrieves the current
608 selection list, applies a command to that list,
609 displays a dialog showing the output of that command,
610 and calls rescan to pickup changes.'''
611 apply_items
= qtutils
.get_selection_list(widget
, items
)
612 output
= command(apply_items
)
616 def __browse_branch(self
, branch
):
617 if not branch
: return
618 # Clone the model to allow opening multiple browsers
619 # with different sets of data
620 model
= self
.model
.clone()
621 model
.set_branch(branch
)
622 view
= GitCommitBrowser()
623 controller
= GitRepoBrowserController(model
, view
)
627 def __menu_about_to_show(self
):
629 unstaged_item
= qtutils
.get_selected_item(
630 self
.view
.unstagedList
,
631 self
.model
.get_all_unstaged())
633 is_tracked
= unstaged_item
not in self
.model
.get_untracked()
637 and not self
.__staged
_diff
_in
_view
641 self
.__staged
_diff
_in
_view
642 and qtutils
.get_selected_item(
643 self
.view
.stagedList
,
644 self
.model
.get_staged()))
646 self
.__stage
_hunk
_action
.setEnabled(bool(enable_staged
))
647 self
.__stage
_hunks
_action
.setEnabled(bool(enable_staged
))
649 self
.__unstage
_hunk
_action
.setEnabled(bool(enable_unstaged
))
650 self
.__unstage
_hunks
_action
.setEnabled(bool(enable_unstaged
))
652 def __menu_event(self
, event
):
654 textedit
= self
.view
.displayText
655 self
.__menu
.exec_(textedit
.mapToGlobal(event
.pos()))
657 def __menu_setup(self
):
658 if self
.__menu
: return
660 menu
= self
.__menu
= QMenu(self
.view
)
661 self
.__stage
_hunk
_action
= menu
.addAction(
662 self
.tr('Stage Hunk For Commit'),
665 self
.__stage
_hunks
_action
= menu
.addAction(
666 self
.tr('Stage Selected Lines'),
669 self
.__unstage
_hunk
_action
= menu
.addAction(
670 self
.tr('Unstage Hunk From Commit'),
673 self
.__unstage
_hunks
_action
= menu
.addAction(
674 self
.tr('Unstage Selected Lines'),
677 self
.__copy
_action
= menu
.addAction(
681 self
.connect(self
.__menu
, 'aboutToShow()', self
.__menu
_about
_to
_show
)
683 def __file_to_widget_item(self
, filename
, staged
, untracked
=False):
684 '''Given a filename, return a QListWidgetItem suitable
685 for adding to a QListWidget. "staged" controls whether
686 to use icons for the staged or unstaged list widget.'''
688 icon_file
= utils
.get_staged_icon(filename
)
690 icon_file
= utils
.get_untracked_icon()
692 icon_file
= utils
.get_icon(filename
)
694 return qtutils
.create_listwidget_item(filename
, icon_file
)
696 def __read_config_settings(self
):
699 sb0
,sb1
) = utils
.parse_geom(cmds
.git_config('ugit.geometry'))
700 self
.view
.resize(w
,h
)
702 self
.view
.splitter_top
.setSizes([st0
,st1
])
703 self
.view
.splitter_bottom
.setSizes([sb0
,sb1
])
705 def __save_config_settings(self
):
706 cmds
.git_config('ugit.geometry', utils
.get_geom())
708 def __select_commits(self
, revs
, summaries
):
709 '''Use the GitCommitBrowser to select commits from a list.'''
711 msg
= self
.tr('ERROR: No commits exist in this branch.')
712 self
.__show
_command
(msg
)
715 browser
= GitCommitBrowser(self
.view
)
716 self
.connect(browser
.commitList
,
717 'itemSelectionChanged()',
718 lambda: self
.commit_sha1_selected(
721 qtutils
.set_items(browser
.commitList
, summaries
)
724 result
= browser
.exec_()
725 if result
!= QDialog
.Accepted
:
728 list_widget
= browser
.commitList
729 selection
= qtutils
.get_selection_list(list_widget
, revs
)
730 if not selection
: return([],[])
732 # also return the selected index numbers
733 index_nums
= range(len(revs
))
734 idxs
= qtutils
.get_selection_list(list_widget
, index_nums
)
736 return(selection
, idxs
)
738 def __set_branch_ui_items(self
):
739 '''Sets up items that mention the current branch name.'''
740 branch
= cmds
.git_current_branch()
742 status_text
= self
.tr('Current Branch:') + ' ' + branch
743 self
.view
.statusBar().showMessage(status_text
)
745 project
= self
.model
.get_project()
746 title
= '%s [%s]' % ( project
, branch
)
748 self
.view
.setWindowTitle(title
)
750 def __reset_display(self
):
751 self
.view
.displayText
.setText('')
754 def __set_info(self
,text
):
755 self
.view
.displayLabel
.setText(text
)
757 def __start_inotify_thread(self
):
758 # Do we have inotify? If not, return.
759 # Recommend installing inotify if we're on Linux.
760 self
.inotify_thread
= None
762 from inotify
import GitNotifier
765 if platform
.system() == 'Linux':
766 msg
=(self
.tr('Unable import pyinotify.\n'
767 + 'inotify support has been'
771 plat
= platform
.platform().lower()
772 if 'debian' in plat
or 'ubuntu' in plat
:
773 msg
+= (self
.tr('Hint:')
774 + 'sudo apt-get install'
775 + ' python-pyinotify')
777 qtutils
.information(self
.view
,
778 self
.tr('inotify disabled'), msg
)
781 self
.inotify_thread
= GitNotifier(os
.getcwd())
782 self
.connect(self
.inotify_thread
,
783 'timeForRescan()', self
.rescan
)
785 # Start the notification thread
786 self
.inotify_thread
.start()
788 def __show_command(self
, output
, rescan
=True):
789 '''Shows output and optionally rescans for changes.'''
790 qtutils
.show_command(self
.view
, output
)
791 if rescan
: self
.rescan()
793 def __update_listwidget(self
, widget
, items
,
794 staged
, untracked
=False, append
=False):
795 '''Populate a QListWidget with the custom icon items.'''
796 if not append
: widget
.clear()
797 qtutils
.add_items( widget
,
798 [ self
.__file
_to
_widget
_item
(i
, staged
, untracked
)