1 # Copyright (C) 2009, 2010, 2011, 2012, 2013
2 # David Aguilar <davvid@gmail.com>
3 """Provides the main() routine and ColaApplicaiton"""
10 # Make homebrew work by default
11 if sys
.platform
== 'darwin':
12 from distutils
import sysconfig
13 python_version
= sysconfig
.get_python_version()
14 homebrew_mods
= '/usr/local/lib/python%s/site-packages' % python_version
15 if os
.path
.isdir(homebrew_mods
):
16 sys
.path
.append(homebrew_mods
)
19 from PyQt4
import QtGui
20 from PyQt4
import QtCore
21 from PyQt4
.QtCore
import SIGNAL
23 sys
.stderr
.write('Sorry, you do not seem to have PyQt4 installed.\n')
24 sys
.stderr
.write('Please install it before using git-cola.\n')
25 sys
.stderr
.write('e.g.: sudo apt-get install python-qt4\n')
32 from cola
import compat
34 from cola
import inotify
36 from cola
import qtcompat
37 from cola
import qtutils
38 from cola
import resources
39 from cola
import utils
40 from cola
import version
41 from cola
.decorators
import memoize
42 from cola
.interaction
import Interaction
43 from cola
.widgets
import cfgactions
44 from cola
.widgets
import startup
47 def setup_environment():
48 # Spoof an X11 display for SSH
49 os
.environ
.setdefault('DISPLAY', ':0')
51 # Setup the path so that git finds us when we run 'git cola'
52 path_entries
= core
.getenv('PATH', '').split(os
.pathsep
)
53 bindir
= os
.path
.dirname(core
.abspath(__file__
))
54 path_entries
.insert(0, bindir
)
55 path
= os
.pathsep
.join(path_entries
)
56 compat
.setenv('PATH', path
)
58 # We don't ever want a pager
59 compat
.setenv('GIT_PAGER', '')
62 git_askpass
= core
.getenv('GIT_ASKPASS')
63 ssh_askpass
= core
.getenv('SSH_ASKPASS')
68 elif sys
.platform
== 'darwin':
69 askpass
= resources
.share('bin', 'ssh-askpass-darwin')
71 askpass
= resources
.share('bin', 'ssh-askpass')
73 compat
.setenv('GIT_ASKPASS', askpass
)
74 compat
.setenv('SSH_ASKPASS', askpass
)
77 # Git v1.7.10 Release Notes
78 # =========================
83 # * From this release on, the "git merge" command in an interactive
84 # session will start an editor when it automatically resolves the
85 # merge for the user to explain the resulting commit, just like the
86 # "git commit" command does when it wasn't given a commit message.
88 # If you have a script that runs "git merge" and keeps its standard
89 # input and output attached to the user's terminal, and if you do not
90 # want the user to explain the resulting merge commits, you can
91 # export GIT_MERGE_AUTOEDIT environment variable set to "no", like
95 # GIT_MERGE_AUTOEDIT=no
96 # export GIT_MERGE_AUTOEDIT
98 # to disable this behavior (if you want your users to explain their
99 # merge commits, you do not have to do anything). Alternatively, you
100 # can give the "--no-edit" option to individual invocations of the
101 # "git merge" command if you know everybody who uses your script has
102 # Git v1.7.8 or newer.
104 # Longer-term: Use `git merge --no-commit` so that we always
105 # have a chance to explain our merges.
106 compat
.setenv('GIT_MERGE_AUTOEDIT', 'no')
111 return QtGui
.QApplication(list(argv
))
114 # style note: we use camelCase here since we're masquerading a Qt class
115 class ColaApplication(object):
116 """The main cola application
118 ColaApplication handles i18n of user-visible data
121 def __init__(self
, argv
, locale
=None, gui
=True):
127 # Add the default style dir so that we find our icons
128 icon_dir
= resources
.icon_dir()
129 qtcompat
.add_search_path(os
.path
.basename(icon_dir
), icon_dir
)
132 self
._app
= instance(tuple(argv
))
133 self
._app
.setWindowIcon(qtutils
.git_icon())
135 self
._app
= QtCore
.QCoreApplication(argv
)
137 self
._app
.setStyleSheet("""
138 QMainWindow::separator {
142 QMainWindow::separator:hover {
147 def activeWindow(self
):
148 """Wrap activeWindow()"""
149 return self
._app
.activeWindow()
152 return self
._app
.desktop()
156 return self
._app
.exec_()
159 def process_args(args
):
161 # Accept 'git cola --version' or 'git cola version'
162 version
.print_version()
166 # Adds git to the PATH. This is needed on Windows.
167 path_entries
= core
.getenv('PATH', '').split(os
.pathsep
)
168 path_entries
.insert(0, os
.path
.dirname(core
.decode(args
.git_path
)))
169 compat
.setenv('PATH', os
.pathsep
.join(path_entries
))
171 # Bail out if --repo is not a directory
172 repo
= core
.decode(args
.repo
)
173 if repo
.startswith('file:'):
174 repo
= repo
[len('file:'):]
175 repo
= core
.realpath(repo
)
176 if not core
.isdir(repo
):
177 sys
.stderr
.write("fatal: '%s' is not a directory. "
178 'Consider supplying -r <path>.\n' % repo
)
181 # We do everything relative to the repo root
186 def application_init(args
, update
=False):
187 """Parses the command-line arguments and starts git-cola
192 # Ensure that we're working in a valid git repository.
193 # If not, try to find one. When found, chdir there.
194 app
= new_application()
195 model
= new_model(app
, args
.repo
, prompt
=args
.prompt
)
197 model
.update_status()
199 return ApplicationContext(args
, app
, model
)
202 def application_start(context
, view
):
203 # Make sure that we start out on top
207 # Scan for the first time
208 task
= _start_update_thread(context
.model
)
210 # Start the inotify thread
213 msg_timer
= QtCore
.QTimer()
214 msg_timer
.setSingleShot(True)
215 msg_timer
.connect(msg_timer
, SIGNAL('timeout()'), _send_msg
)
218 # Start the event loop
219 result
= context
.app
.exec_()
223 QtCore
.QThreadPool
.globalInstance().waitForDone()
226 pattern
= utils
.tmp_file_pattern()
227 for filename
in glob
.glob(pattern
):
233 def add_common_arguments(parser
):
234 # We also accept 'git cola version'
235 parser
.add_argument('--version', default
=False, action
='store_true',
236 help='prints the version')
238 # Specifies a git repository to open
239 parser
.add_argument('-r', '--repo', metavar
='<repo>', default
=os
.getcwd(),
240 help='specifies the path to a git repository')
242 # Specifies that we should prompt for a repository at startup
243 parser
.add_argument('--prompt', action
='store_true', default
=False,
244 help='prompts for a repository')
246 # Used on Windows for adding 'git' to the path
247 parser
.add_argument('-g', '--git-path', metavar
='<path>', default
=None,
248 help='specifies the path to the git binary')
251 def new_application():
252 # Allow Ctrl-C to exit
253 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)
256 return ColaApplication(sys
.argv
)
259 def new_model(app
, repo
, prompt
=False):
261 valid
= model
.set_worktree(repo
) and not prompt
263 startup_dlg
= startup
.StartupDialog(app
.activeWindow())
264 gitdir
= startup_dlg
.find_git_repo()
267 valid
= model
.set_worktree(gitdir
)
269 # Finally, go to the root of the git repo
270 os
.chdir(model
.git
.worktree())
274 def _start_update_thread(model
):
275 """Update the model in the background
277 git-cola should startup as quickly as possible.
280 class UpdateTask(QtCore
.QRunnable
):
282 model
.update_status(update_index
=True)
284 # Hold onto a reference to prevent PyQt from dereferencing
286 QtCore
.QThreadPool
.globalInstance().start(task
)
292 if git
.GIT_COLA_TRACE
== 'trace':
293 msg
= ('info: Trace enabled. '
294 'Many of commands reported with "trace" use git\'s stable '
295 '"plumbing" API and are not intended for typical '
296 'day-to-day use. Here be dragons')
300 class ApplicationContext(object):
302 def __init__(self
, args
, app
, model
):