toolbar: remove extraneous whitespace
[git-cola.git] / cola / main.py
blobc6322eda66a987acacf2324a120c2f22d6b6d913
1 """Launcher and command line interface to git-cola"""
2 from __future__ import absolute_import, division, unicode_literals
3 import argparse
4 import sys
6 from . import app
7 from . import cmds
8 from . import core
11 def main(argv=None):
12 app.initialize()
13 if argv is None:
14 argv = sys.argv[1:]
15 # we're using argparse with subparser, but argparse
16 # does not allow us to assign a default subparser
17 # when none has been specified. We fake it by injecting
18 # 'cola' into the command-line so that parse_args()
19 # routes them to the 'cola' parser by default.
20 help_commands = core.encode('--help-commands')
21 args = [core.encode(arg) for arg in argv]
22 if not argv or argv[0].startswith('-') and help_commands not in args:
23 argv.insert(0, 'cola')
24 elif help_commands in argv:
25 argv.append('--help')
26 args = parse_args(argv)
27 return args.func(args)
30 def winmain():
31 return app.winmain(main)
34 def parse_args(argv):
35 parser = argparse.ArgumentParser()
36 subparser = parser.add_subparsers(title='valid commands')
38 add_cola_command(subparser)
39 add_about_command(subparser)
40 add_am_command(subparser)
41 add_archive_command(subparser)
42 add_branch_command(subparser)
43 add_browse_command(subparser)
44 add_clone_command(subparser)
45 add_config_command(subparser)
46 add_dag_command(subparser)
47 add_diff_command(subparser)
48 add_fetch_command(subparser)
49 add_find_command(subparser)
50 add_grep_command(subparser)
51 add_merge_command(subparser)
52 add_pull_command(subparser)
53 add_push_command(subparser)
54 add_rebase_command(subparser)
55 add_recent_command(subparser)
56 add_remote_command(subparser)
57 add_search_command(subparser)
58 add_stash_command(subparser)
59 add_tag_command(subparser)
60 add_version_command(subparser)
62 return parser.parse_args(argv)
65 def add_command(parent, name, description, func):
66 parser = parent.add_parser(str(name), help=description)
67 parser.set_defaults(func=func)
68 app.add_common_arguments(parser)
69 return parser
72 def add_cola_command(subparser):
73 parser = add_command(subparser, 'cola', 'start git-cola', cmd_cola)
74 parser.add_argument(
75 '--amend', default=False, action='store_true', help='start in amend mode'
77 parser.add_argument(
78 '--help-commands',
79 default=False,
80 action='store_true',
81 help='show available sub-commands',
83 parser.add_argument(
84 '--status-filter', '-s', metavar='<path>', default='', help='status path filter'
88 def add_about_command(parent):
89 add_command(parent, 'about', 'about git-cola', cmd_about)
92 def add_am_command(parent):
93 parser = add_command(parent, 'am', 'apply patches using "git am"', cmd_am)
94 parser.add_argument(
95 'patches', metavar='<patches>', nargs='*', help='patches to apply'
99 def add_archive_command(parent):
100 parser = add_command(parent, 'archive', 'save an archive', cmd_archive)
101 parser.add_argument(
102 'ref', metavar='<ref>', nargs='?', default=None, help='commit to archive'
106 def add_branch_command(subparser):
107 add_command(subparser, 'branch', 'create a branch', cmd_branch)
110 def add_browse_command(subparser):
111 add_command(subparser, 'browse', 'browse repository', cmd_browse)
114 def add_clone_command(subparser):
115 add_command(subparser, 'clone', 'clone repository', cmd_clone)
118 def add_config_command(subparser):
119 add_command(subparser, 'config', 'edit configuration', cmd_config)
122 def add_dag_command(subparser):
123 parser = add_command(subparser, 'dag', 'start git-dag', cmd_dag)
124 parser.add_argument(
125 '-c',
126 '--count',
127 metavar='<count>',
128 type=int,
129 default=1000,
130 help='number of commits to display',
132 parser.add_argument(
133 '--all',
134 action='store_true',
135 dest='show_all',
136 help='visualize all branches',
137 default=False,
139 parser.add_argument('args', nargs='*', metavar='<args>', help='git log arguments')
142 def add_diff_command(subparser):
143 parser = add_command(subparser, 'diff', 'view diffs', cmd_diff)
144 parser.add_argument('args', nargs='*', metavar='<args>', help='git diff arguments')
147 def add_fetch_command(subparser):
148 add_command(subparser, 'fetch', 'fetch remotes', cmd_fetch)
151 def add_find_command(subparser):
152 parser = add_command(subparser, 'find', 'find files', cmd_find)
153 parser.add_argument('paths', nargs='*', metavar='<path>', help='filter by path')
156 def add_grep_command(subparser):
157 parser = add_command(subparser, 'grep', 'grep source', cmd_grep)
158 parser.add_argument('args', nargs='*', metavar='<args>', help='git grep arguments')
161 def add_merge_command(subparser):
162 parser = add_command(subparser, 'merge', 'merge branches', cmd_merge)
163 parser.add_argument(
164 'ref', nargs='?', metavar='<ref>', help='branch, tag, or commit to merge'
168 def add_pull_command(subparser):
169 parser = add_command(subparser, 'pull', 'pull remote branches', cmd_pull)
170 parser.add_argument(
171 '--rebase',
172 default=False,
173 action='store_true',
174 help='rebase local branch when pulling',
178 def add_push_command(subparser):
179 add_command(subparser, 'push', 'push remote branches', cmd_push)
182 def add_rebase_command(subparser):
183 parser = add_command(subparser, 'rebase', 'interactive rebase', cmd_rebase)
184 parser.add_argument(
185 '-v',
186 '--verbose',
187 default=False,
188 action='store_true',
189 help='display a diffstat of what changed upstream',
191 parser.add_argument(
192 '-q',
193 '--quiet',
194 default=False,
195 action='store_true',
196 help='be quiet. implies --no-stat',
198 parser.add_argument(
199 '-i', '--interactive', default=True, action='store_true', help=argparse.SUPPRESS
201 parser.add_argument(
202 '--autostash',
203 default=False,
204 action='store_true',
205 help='automatically stash/stash pop before and after',
207 parser.add_argument(
208 '--fork-point',
209 default=False,
210 action='store_true',
211 help="use 'merge-base --fork-point' to refine upstream",
213 parser.add_argument(
214 '--onto',
215 default=None,
216 metavar='<newbase>',
217 help='rebase onto given branch instead of upstream',
219 parser.add_argument(
220 '-p',
221 '--preserve-merges',
222 default=False,
223 action='store_true',
224 help='try to recreate merges instead of ignoring them',
226 parser.add_argument(
227 '-s',
228 '--strategy',
229 default=None,
230 metavar='<strategy>',
231 help='use the given merge strategy',
233 parser.add_argument(
234 '--no-ff',
235 default=False,
236 action='store_true',
237 help='cherry-pick all commits, even if unchanged',
239 parser.add_argument(
240 '-m',
241 '--merge',
242 default=False,
243 action='store_true',
244 help='use merging strategies to rebase',
246 parser.add_argument(
247 '-x',
248 '--exec',
249 default=None,
250 help='add exec lines after each commit of ' 'the editable list',
252 parser.add_argument(
253 '-k',
254 '--keep-empty',
255 default=False,
256 action='store_true',
257 help='preserve empty commits during rebase',
259 parser.add_argument(
260 '-f',
261 '--force-rebase',
262 default=False,
263 action='store_true',
264 help='force rebase even if branch is up to date',
266 parser.add_argument(
267 '-X',
268 '--strategy-option',
269 default=None,
270 metavar='<arg>',
271 help='pass the argument through to the merge strategy',
273 parser.add_argument(
274 '--stat',
275 default=False,
276 action='store_true',
277 help='display a diffstat of what changed upstream',
279 parser.add_argument(
280 '-n',
281 '--no-stat',
282 default=False,
283 action='store_true',
284 help='do not show diffstat of what changed upstream',
286 parser.add_argument(
287 '--verify',
288 default=False,
289 action='store_true',
290 help='allow pre-rebase hook to run',
292 parser.add_argument(
293 '--rerere-autoupdate',
294 default=False,
295 action='store_true',
296 help='allow rerere to update index with ' 'resolved conflicts',
298 parser.add_argument(
299 '--root',
300 default=False,
301 action='store_true',
302 help='rebase all reachable commits up to the root(s)',
304 parser.add_argument(
305 '--autosquash',
306 default=True,
307 action='store_true',
308 help='move commits that begin with ' 'squash!/fixup! under -i',
310 parser.add_argument(
311 '--no-autosquash',
312 default=True,
313 action='store_false',
314 dest='autosquash',
315 help='do not move commits that begin with ' 'squash!/fixup! under -i',
317 parser.add_argument(
318 '--committer-date-is-author-date',
319 default=False,
320 action='store_true',
321 help="passed to 'git am' by 'git rebase'",
323 parser.add_argument(
324 '--ignore-date',
325 default=False,
326 action='store_true',
327 help="passed to 'git am' by 'git rebase'",
329 parser.add_argument(
330 '--whitespace',
331 default=False,
332 action='store_true',
333 help="passed to 'git apply' by 'git rebase'",
335 parser.add_argument(
336 '--ignore-whitespace',
337 default=False,
338 action='store_true',
339 help="passed to 'git apply' by 'git rebase'",
341 parser.add_argument(
342 '-C',
343 dest='context_lines',
344 default=None,
345 metavar='<n>',
346 help="passed to 'git apply' by 'git rebase'",
349 actions = parser.add_argument_group('actions')
350 actions.add_argument(
351 '--continue', default=False, action='store_true', help='continue'
353 actions.add_argument(
354 '--abort',
355 default=False,
356 action='store_true',
357 help='abort and check out the original branch',
359 actions.add_argument(
360 '--skip',
361 default=False,
362 action='store_true',
363 help='skip current patch and continue',
365 actions.add_argument(
366 '--edit-todo',
367 default=False,
368 action='store_true',
369 help='edit the todo list during an interactive rebase',
372 parser.add_argument(
373 'upstream',
374 nargs='?',
375 default=None,
376 metavar='<upstream>',
377 help='the upstream configured in branch.<name>.remote '
378 'and branch.<name>.merge options will be used '
379 'when <upstream> is omitted; see git-rebase(1) '
380 'for details. If you are currently not on any '
381 'branch or if the current branch does not have '
382 'a configured upstream, the rebase will abort',
384 parser.add_argument(
385 'branch',
386 nargs='?',
387 default=None,
388 metavar='<branch>',
389 help='git rebase will perform an automatic '
390 '"git checkout <branch>" before doing anything '
391 'else when <branch> is specified',
395 def add_recent_command(subparser):
396 add_command(subparser, 'recent', 'edit recent files', cmd_recent)
399 def add_remote_command(subparser):
400 add_command(subparser, 'remote', 'edit remotes', cmd_remote)
403 def add_search_command(subparser):
404 add_command(subparser, 'search', 'search commits', cmd_search)
407 def add_stash_command(subparser):
408 add_command(subparser, 'stash', 'stash and unstash changes', cmd_stash)
411 def add_tag_command(subparser):
412 parser = add_command(subparser, 'tag', 'create tags', cmd_tag)
413 parser.add_argument(
414 'name', metavar='<name>', nargs='?', default=None, help='tag name'
416 parser.add_argument(
417 'ref', metavar='<ref>', nargs='?', default=None, help='commit to tag'
419 parser.add_argument(
420 '-s',
421 '--sign',
422 default=False,
423 action='store_true',
424 help='annotated and GPG-signed tag',
428 def add_version_command(subparser):
429 parser = add_command(subparser, 'version', 'print the version', cmd_version)
430 parser.add_argument(
431 '--brief',
432 action='store_true',
433 default=False,
434 help='print the version number only',
436 parser.add_argument(
437 '--build', action='store_true', default=False, help='print the build version'
441 # entry points
442 def cmd_cola(args):
443 from .widgets.main import MainView # pylint: disable=all
445 status_filter = args.status_filter
446 if status_filter:
447 status_filter = core.abspath(status_filter)
449 context = app.application_init(args)
451 context.timer.start('view')
452 view = MainView(context)
453 if args.amend:
454 cmds.do(cmds.AmendMode, context, amend=True)
456 if status_filter:
457 view.set_filter(core.relpath(status_filter))
459 context.timer.stop('view')
460 if args.perf:
461 context.timer.display('view')
463 return app.application_run(context, view, start=start_cola, stop=app.default_stop)
466 def start_cola(context, view):
467 app.default_start(context, view)
468 view.start(context)
471 def cmd_about(args):
472 from .widgets import about # pylint: disable=all
474 context = app.application_init(args)
475 view = about.about_dialog(context)
476 return app.application_start(context, view)
479 def cmd_am(args):
480 from .widgets.patch import new_apply_patches # pylint: disable=all
482 context = app.application_init(args)
483 view = new_apply_patches(context, patches=args.patches)
484 return app.application_start(context, view)
487 def cmd_archive(args):
488 from .widgets import archive # pylint: disable=all
490 context = app.application_init(args, update=True)
491 if args.ref is None:
492 args.ref = context.model.currentbranch
493 view = archive.Archive(context, args.ref)
494 return app.application_start(context, view)
497 def cmd_branch(args):
498 from .widgets.createbranch import create_new_branch # pylint: disable=all
500 context = app.application_init(args, update=True)
501 view = create_new_branch(context)
502 return app.application_start(context, view)
505 def cmd_browse(args):
506 from .widgets.browse import worktree_browser # pylint: disable=all
508 context = app.application_init(args)
509 view = worktree_browser(context, show=False, update=False)
510 return app.application_start(context, view)
513 def cmd_clone(args):
514 from .widgets import clone # pylint: disable=all
516 context = app.application_init(args)
517 view = clone.clone(context)
518 context.set_view(view)
519 result = 0 if view.exec_() == view.Accepted else 1
520 app.default_stop(context, view)
521 return result
524 def cmd_config(args):
525 from .widgets.prefs import preferences # pylint: disable=all
527 context = app.application_init(args)
528 view = preferences(context)
529 return app.application_start(context, view)
532 def cmd_dag(args):
533 from .widgets import dag # pylint: disable=all
535 context = app.application_init(args)
536 # cola.main() uses parse_args(), unlike dag.main() which uses
537 # parse_known_args(), thus we aren't able to automatically forward
538 # all unknown arguments. Special-case support for "--all" since it's
539 # used by the history viewer command on Windows.
540 if args.show_all:
541 args.args.insert(0, '--all')
542 view = dag.git_dag(context, args=args, show=False)
543 return app.application_start(context, view)
546 def cmd_diff(args):
547 from .difftool import diff_expression # pylint: disable=all
549 context = app.application_init(args)
550 expr = core.list2cmdline(args.args)
551 view = diff_expression(context, None, expr, create_widget=True)
552 return app.application_start(context, view)
555 def cmd_fetch(args):
556 # TODO: the calls to update_status() can be done asynchronously
557 # by hooking into the message_updated notification.
558 from .widgets import remote # pylint: disable=all
560 context = app.application_init(args)
561 context.model.update_status()
562 view = remote.fetch(context)
563 return app.application_start(context, view)
566 def cmd_find(args):
567 from .widgets import finder # pylint: disable=all
569 context = app.application_init(args)
570 paths = core.list2cmdline(args.paths)
571 view = finder.finder(context, paths=paths)
572 return app.application_start(context, view)
575 def cmd_grep(args):
576 from .widgets import grep # pylint: disable=all
578 context = app.application_init(args)
579 text = core.list2cmdline(args.args)
580 view = grep.new_grep(context, text=text, parent=None)
581 return app.application_start(context, view)
584 def cmd_merge(args):
585 from .widgets.merge import Merge # pylint: disable=all
587 context = app.application_init(args, update=True)
588 view = Merge(context, parent=None, ref=args.ref)
589 return app.application_start(context, view)
592 def cmd_version(args):
593 from . import version # pylint: disable=all
595 version.print_version(brief=args.brief, build=args.build)
596 return 0
599 def cmd_pull(args):
600 from .widgets import remote # pylint: disable=all
602 context = app.application_init(args, update=True)
603 view = remote.pull(context)
604 if args.rebase:
605 view.set_rebase(True)
606 return app.application_start(context, view)
609 def cmd_push(args):
610 from .widgets import remote # pylint: disable=all
612 context = app.application_init(args, update=True)
613 view = remote.push(context)
614 return app.application_start(context, view)
617 def cmd_rebase(args):
618 kwargs = {
619 'verbose': args.verbose,
620 'quiet': args.quiet,
621 'autostash': args.autostash,
622 'fork_point': args.fork_point,
623 'onto': args.onto,
624 'preserve_merges': args.preserve_merges,
625 'strategy': args.strategy,
626 'no_ff': args.no_ff,
627 'merge': args.merge,
628 'exec': getattr(args, 'exec', None), # python keyword
629 'keep_empty': args.keep_empty,
630 'force_rebase': args.force_rebase,
631 'strategy_option': args.strategy_option,
632 'stat': args.stat,
633 'no_stat': args.no_stat,
634 'verify': args.verify,
635 'rerere_autoupdate': args.rerere_autoupdate,
636 'root': args.root,
637 'autosquash': args.autosquash,
638 'committer_date_is_author_date': args.committer_date_is_author_date,
639 'ignore_date': args.ignore_date,
640 'whitespace': args.whitespace,
641 'ignore_whitespace': args.ignore_whitespace,
642 'C': args.context_lines,
643 'continue': getattr(args, 'continue', False), # python keyword
644 'abort': args.abort,
645 'skip': args.skip,
646 'edit_todo': args.edit_todo,
647 'upstream': args.upstream,
648 'branch': args.branch,
650 context = app.application_init(args)
651 status, _, _ = cmds.do(cmds.Rebase, context, **kwargs)
652 return status
655 def cmd_recent(args):
656 from .widgets import recent # pylint: disable=all
658 context = app.application_init(args)
659 view = recent.browse_recent_files(context)
660 return app.application_start(context, view)
663 def cmd_remote(args):
664 from .widgets import editremotes # pylint: disable=all
666 context = app.application_init(args)
667 view = editremotes.editor(context, run=False)
668 return app.application_start(context, view)
671 def cmd_search(args):
672 from .widgets.search import search # pylint: disable=all
674 context = app.application_init(args)
675 view = search(context)
676 return app.application_start(context, view)
679 def cmd_stash(args):
680 from .widgets import stash # pylint: disable=all
682 context = app.application_init(args)
683 view = stash.view(context, show=False)
684 return app.application_start(context, view)
687 def cmd_tag(args):
688 from .widgets.createtag import new_create_tag # pylint: disable=all
690 context = app.application_init(args)
691 view = new_create_tag(context, name=args.name, ref=args.ref, sign=args.sign)
692 return app.application_start(context, view)
695 # Windows shortcut launch features:
696 def shortcut_launch():
697 """Launch from a shortcut
699 Prompt for the repository by default.
702 argv = sys.argv[1:]
703 if not argv:
704 argv = ['cola', '--prompt']
705 return app.winmain(main, argv)