1 # Copyright (C) 2009, David Aguilar <davvid@gmail.com>
2 """Provides the main() routine and ColaApplicaiton"""
11 from PyQt4
import QtGui
12 from PyQt4
import QtCore
13 from PyQt4
.QtCore
import SIGNAL
15 print >> sys
.stderr
, 'Sorry, you do not seem to have PyQt4 installed.'
16 print >> sys
.stderr
, 'Please install it before using git-cola.'
17 print >> sys
.stderr
, 'e.g.: sudo apt-get install python-qt4'
25 from cola
import guicmds
26 from cola
import inotify
28 from cola
import qtcompat
29 from cola
import qtutils
30 from cola
import resources
31 from cola
import signals
32 from cola
import utils
33 from cola
import version
34 from cola
.classic
import cola_classic
35 from cola
.dag
import git_dag
36 from cola
.stash
import stash
37 from cola
.decorators
import memoize
38 from cola
.main
.view
import MainView
39 from cola
.main
.controller
import MainController
40 from cola
.widgets
import remote
41 from cola
.widgets
import cfgactions
42 from cola
.widgets
import startup
43 from cola
.widgets
.createtag
import create_tag
44 from cola
.widgets
.createbranch
import create_new_branch
45 from cola
.widgets
.search
import search
48 def setup_environment():
49 # Spoof an X11 display for SSH
50 os
.environ
.setdefault('DISPLAY', ':0')
52 # Provide an SSH_ASKPASS fallback
53 if sys
.platform
== 'darwin':
54 os
.environ
.setdefault('SSH_ASKPASS',
55 resources
.share('bin', 'ssh-askpass-darwin'))
57 os
.environ
.setdefault('SSH_ASKPASS',
58 resources
.share('bin', 'ssh-askpass'))
60 # Setup the path so that git finds us when we run 'git cola'
61 path_entries
= os
.environ
.get('PATH').split(os
.pathsep
)
62 bindir
= os
.path
.dirname(os
.path
.abspath(__file__
))
63 path_entries
.insert(0, bindir
)
64 path
= os
.pathsep
.join(path_entries
)
65 os
.environ
['PATH'] = path
66 os
.putenv('PATH', path
)
71 return QtGui
.QApplication(list(argv
))
74 # style note: we use camelCase here since we're masquerading a Qt class
75 class ColaApplication(object):
76 """The main cola application
78 ColaApplication handles i18n of user-visible data
81 def __init__(self
, argv
, locale
=None, gui
=True):
82 """Initialize our QApplication for translation
87 # Add the default style dir so that we find our icons
88 icon_dir
= resources
.icon_dir()
89 qtcompat
.add_search_path(os
.path
.basename(icon_dir
), icon_dir
)
91 # monkey-patch Qt's translate() to use our translate()
93 self
._app
= instance(tuple(argv
))
94 self
._app
.setWindowIcon(qtutils
.git_icon())
95 self
._translate
_base
= QtGui
.QApplication
.translate
96 QtGui
.QApplication
.translate
= self
.translate
98 self
._app
= QtCore
.QCoreApplication(argv
)
99 self
._translate
_base
= QtCore
.QCoreApplication
.translate
100 QtCore
.QCoreApplication
.translate
= self
.translate
102 # Register model commands
105 # Make file descriptors binary for win32
106 utils
.set_binary(sys
.stdin
)
107 utils
.set_binary(sys
.stdout
)
108 utils
.set_binary(sys
.stderr
)
110 def translate(self
, domain
, txt
):
112 Translate strings with gettext
114 Supports @@noun/@@verb specifiers.
117 trtxt
= i18n
.gettext(txt
)
118 if trtxt
[-6:-4] == '@@': # handle @@verb / @@noun
122 def activeWindow(self
):
123 """Wrap activeWindow()"""
124 return self
._app
.activeWindow()
128 return self
._app
.exec_()
131 def parse_args(context
):
133 builtins
= set(('branch',
143 if context
== 'git-dag':
145 elif args
and args
[0] in builtins
:
146 context
= args
.pop(0)
147 sys
.argv
= sys
.argv
[0:1] + args
149 parser
= optparse
.OptionParser(usage
='%prog [options]')
151 # We also accept 'git cola version'
152 parser
.add_option('-v', '--version',
153 help='Show cola version',
158 # Specifies a git repository to open
159 parser
.add_option('-r', '--repo',
160 help='Specifies the path to a git repository.',
165 # Specifies that we should prompt for a repository at startup
166 parser
.add_option('--prompt',
167 help='Prompt for a repository before starting the main GUI.',
172 # Used on Windows for adding 'git' to the path
173 parser
.add_option('-g', '--git-path',
174 help='Specifies the path to the git binary',
180 parser
.add_option('-c', '--count',
181 help='Number of commits to display.',
186 opts
, args
= parser
.parse_args()
187 return opts
, args
, context
190 def process_args(opts
, args
):
191 if opts
.version
or (args
and args
[0] == 'version'):
192 # Accept 'git cola --version' or 'git cola version'
193 print 'cola version', version
.version()
197 # Adds git to the PATH. This is needed on Windows.
198 path_entries
= os
.environ
.get('PATH', '').split(os
.pathsep
)
199 path_entries
.insert(0, os
.path
.dirname(opts
.git
))
200 os
.environ
['PATH'] = os
.pathsep
.join(path_entries
)
202 # Bail out if --repo is not a directory
203 repo
= os
.path
.realpath(opts
.repo
)
204 if not os
.path
.isdir(repo
):
205 print >> sys
.stderr
, "fatal: '%s' is not a directory. Consider supplying -r <path>.\n" % repo
208 # We do everything relative to the repo root
215 """Parses the command-line arguments and starts git-cola
218 opts
, args
, context
= parse_args(context
)
219 repo
= process_args(opts
, args
)
221 # Allow Ctrl-C to exit
222 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)
225 app
= ColaApplication(sys
.argv
)
227 # Ensure that we're working in a valid git repository.
228 # If not, try to find one. When found, chdir there.
230 valid
= model
.set_worktree(repo
) and not opts
.prompt
232 startup_dlg
= startup
.StartupDialog(app
.activeWindow())
233 gitdir
= startup_dlg
.find_git_repo()
236 valid
= model
.set_worktree(gitdir
)
238 # Finally, go to the root of the git repo
239 os
.chdir(model
.git
.worktree())
242 if context
== 'branch':
243 view
= create_new_branch()
244 elif context
in ('git-dag', 'dag'):
245 ctl
= git_dag(model
, opts
=opts
, args
=args
)
247 elif context
in ('classic', 'browse'):
248 view
= cola_classic(update
=False)
249 # TODO: the calls to update_status() can be done asynchronously
250 # by hooking into the message_updated notification.
251 elif context
== 'fetch':
252 model
.update_status()
253 view
= remote
.fetch()
254 elif context
== 'pull':
255 model
.update_status()
257 elif context
== 'push':
258 model
.update_status()
260 elif context
== 'search':
262 elif context
== 'stash':
263 model
.update_status()
265 elif context
== 'tag':
268 view
= MainView(model
, qtutils
.active_window())
269 ctl
= MainController(model
, view
)
271 # Install UI wrappers for command objects
272 cfgactions
.install_command_wrapper()
273 guicmds
.install_command_wrapper()
275 # Make sure that we start out on top
279 # Scan for the first time
280 task
= _start_update_thread(model
)
282 # Start the inotify thread
285 msg_timer
= QtCore
.QTimer()
286 msg_timer
.setSingleShot(True)
287 msg_timer
.connect(msg_timer
, SIGNAL('timeout()'), _send_msg
)
290 # Start the event loop
295 QtCore
.QThreadPool
.globalInstance().waitForDone()
297 pattern
= utils
.tmp_file_pattern()
298 for filename
in glob
.glob(pattern
):
305 def _start_update_thread(model
):
306 """Update the model in the background
308 git-cola should startup as quickly as possible.
311 class UpdateTask(QtCore
.QRunnable
):
313 model
.update_status(update_index
=True)
315 # Hold onto a reference to prevent PyQt from dereferencing
317 QtCore
.QThreadPool
.globalInstance().start(task
)
323 if git
.GIT_COLA_TRACE
== 'trace':
324 msg
= ('info: Trace enabled. '
325 'Many of commands reported with "trace" use git\'s stable '
326 '"plumbing" API and are not intended for typical '
327 'day-to-day use. Here be dragons')
328 cola
.notifier().broadcast(signals
.log_cmd
, 0, msg
)