1 """Launcher and command line interface to git-cola"""
15 # we're using argparse with subparsers, 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
:
26 args
= parse_args(argv
)
27 return args
.func(args
)
31 parser
= argparse
.ArgumentParser()
32 # Newer versions of argparse (Python 3.6+) emit an error message for
33 # "--help-commands" unless we register the flag on the main parser.
34 if compat
.PY_VERSION
>= (3, 6):
35 add_help_options(parser
)
36 parser
.set_defaults(func
=lambda _
: parser
.print_help())
38 subparser
= parser
.add_subparsers(title
='valid commands')
39 add_cola_command(subparser
)
40 add_about_command(subparser
)
41 add_am_command(subparser
)
42 add_archive_command(subparser
)
43 add_branch_command(subparser
)
44 add_browse_command(subparser
)
45 add_clone_command(subparser
)
46 add_config_command(subparser
)
47 add_dag_command(subparser
)
48 add_diff_command(subparser
)
49 add_fetch_command(subparser
)
50 add_find_command(subparser
)
51 add_grep_command(subparser
)
52 add_merge_command(subparser
)
53 add_pull_command(subparser
)
54 add_push_command(subparser
)
55 add_rebase_command(subparser
)
56 add_recent_command(subparser
)
57 add_remote_command(subparser
)
58 add_search_command(subparser
)
59 add_stash_command(subparser
)
60 add_tag_command(subparser
)
61 add_version_command(subparser
)
63 return parser
.parse_args(argv
)
66 def add_help_options(parser
):
67 """Add the --help-commands flag to the parser"""
72 help='show available commands',
76 def add_command(parent
, name
, description
, func
):
77 """Add a "git cola" command with common arguments"""
78 parser
= parent
.add_parser(str(name
), help=description
)
79 parser
.set_defaults(func
=func
)
80 app
.add_common_arguments(parser
)
84 def add_cola_command(subparser
):
85 """Add the main "git cola" command. "git cola cola" is valid"""
86 parser
= add_command(subparser
, 'cola', 'launch git-cola', cmd_cola
)
88 '--amend', default
=False, action
='store_true', help='start in amend mode'
90 add_help_options(parser
)
92 '--status-filter', '-s', metavar
='<path>', default
='', help='status path filter'
96 def add_about_command(parent
):
97 """Add the "git cola about" documentation command"""
98 add_command(parent
, 'about', 'about git-cola', cmd_about
)
101 def add_am_command(parent
):
102 """Add the "git cola am" command for applying patches"""
103 parser
= add_command(parent
, 'am', 'apply patches using "git am"', cmd_am
)
105 'patches', metavar
='<patches>', nargs
='*', help='patches to apply'
109 def add_archive_command(parent
):
110 """Add the "git cola archive" tarball export command"""
111 parser
= add_command(parent
, 'archive', 'save an archive', cmd_archive
)
113 'ref', metavar
='<ref>', nargs
='?', default
=None, help='commit to archive'
117 def add_branch_command(subparser
):
118 """Add the "git cola branch" branch creation command"""
119 add_command(subparser
, 'branch', 'create a branch', cmd_branch
)
122 def add_browse_command(subparser
):
123 """Add the "git cola browse" repository browser command"""
124 add_command(subparser
, 'browse', 'browse repository', cmd_browse
)
127 def add_clone_command(subparser
):
128 """Add the "git cola clone" command for cloning repositories"""
129 add_command(subparser
, 'clone', 'clone repository', cmd_clone
)
132 def add_config_command(subparser
):
133 """Add the "git cola config" command for editing preferences"""
134 add_command(subparser
, 'config', 'edit configuration', cmd_config
)
137 def add_dag_command(subparser
):
138 """Add the "git cola dag" command for visualizing history"""
139 parser
= add_command(subparser
, 'dag', 'start git-dag', cmd_dag
)
146 help='number of commits to display',
152 help='visualize all branches',
156 'args', nargs
=argparse
.REMAINDER
, metavar
='<args>', help='git log arguments'
160 def add_diff_command(subparser
):
161 """Add the "git cola diff" command for diffing changes"""
162 parser
= add_command(subparser
, 'diff', 'view diffs', cmd_diff
)
164 'args', nargs
=argparse
.REMAINDER
, metavar
='<args>', help='git diff arguments'
168 def add_fetch_command(subparser
):
169 """Add the "git cola fetch" command for fetching repositories"""
170 add_command(subparser
, 'fetch', 'fetch remotes', cmd_fetch
)
173 def add_find_command(subparser
):
174 """Add the "git cola find" command for finding files"""
175 parser
= add_command(subparser
, 'find', 'find files', cmd_find
)
176 parser
.add_argument('paths', nargs
='*', metavar
='<path>', help='filter by path')
179 def add_grep_command(subparser
):
180 """Add the "git cola grep" command for searching files"""
181 parser
= add_command(subparser
, 'grep', 'grep source', cmd_grep
)
182 parser
.add_argument('args', nargs
='*', metavar
='<args>', help='git grep arguments')
185 def add_merge_command(subparser
):
186 """Add the "git cola merge" command for merging branches"""
187 parser
= add_command(subparser
, 'merge', 'merge branches', cmd_merge
)
189 'ref', nargs
='?', metavar
='<ref>', help='branch, tag, or commit to merge'
193 def add_pull_command(subparser
):
194 """Add the "git cola pull" command for pulling changes from remotes"""
195 parser
= add_command(subparser
, 'pull', 'pull remote branches', cmd_pull
)
200 help='rebase local branch when pulling',
204 def add_push_command(subparser
):
205 """Add the "git cola push" command for pushing branches to remotes"""
206 add_command(subparser
, 'push', 'push remote branches', cmd_push
)
209 def add_rebase_command(subparser
):
210 """Add the "git cola rebase" command for rebasing the current branch"""
211 parser
= add_command(subparser
, 'rebase', 'interactive rebase', cmd_rebase
)
217 help='display a diffstat of what changed upstream',
224 help='be quiet. implies --no-stat',
227 '-i', '--interactive', default
=True, action
='store_true', help=argparse
.SUPPRESS
233 help='automatically stash/stash pop before and after',
239 help="use 'merge-base --fork-point' to refine upstream",
245 help='rebase onto given branch instead of upstream',
252 help='try to recreate merges instead of ignoring them',
258 metavar
='<strategy>',
259 help='use the given merge strategy',
265 help='cherry-pick all commits, even if unchanged',
272 help='use merging strategies to rebase',
278 help='add exec lines after each commit of ' 'the editable list',
285 help='preserve empty commits during rebase',
292 help='force rebase even if branch is up to date',
299 help='pass the argument through to the merge strategy',
305 help='display a diffstat of what changed upstream',
312 help='do not show diffstat of what changed upstream',
318 help='allow pre-rebase hook to run',
321 '--rerere-autoupdate',
324 help='allow rerere to update index with ' 'resolved conflicts',
330 help='rebase all reachable commits up to the root(s)',
336 help='move commits that begin with ' 'squash!/fixup! under -i',
341 action
='store_false',
343 help='do not move commits that begin with ' 'squash!/fixup! under -i',
346 '--committer-date-is-author-date',
349 help="passed to 'git am' by 'git rebase'",
355 help="passed to 'git am' by 'git rebase'",
361 help="passed to 'git apply' by 'git rebase'",
364 '--ignore-whitespace',
367 help="passed to 'git apply' by 'git rebase'",
373 help='update branches that point to commits that are being rebased',
377 dest
='context_lines',
380 help="passed to 'git apply' by 'git rebase'",
383 actions
= parser
.add_argument_group('actions')
384 actions
.add_argument(
385 '--continue', default
=False, action
='store_true', help='continue'
387 actions
.add_argument(
391 help='abort and check out the original branch',
393 actions
.add_argument(
397 help='skip current patch and continue',
399 actions
.add_argument(
403 help='edit the todo list during an interactive rebase',
410 metavar
='<upstream>',
411 help='the upstream configured in branch.<name>.remote '
412 'and branch.<name>.merge options will be used '
413 'when <upstream> is omitted; see git-rebase(1) '
414 'for details. If you are currently not on any '
415 'branch or if the current branch does not have '
416 'a configured upstream, the rebase will abort',
423 help='git rebase will perform an automatic '
424 '"git checkout <branch>" before doing anything '
425 'else when <branch> is specified',
429 def add_recent_command(subparser
):
430 """Add the "git cola recent" command for opening recently edited files"""
431 add_command(subparser
, 'recent', 'edit recent files', cmd_recent
)
434 def add_remote_command(subparser
):
435 """Add the "git cola remote" command for editing remotes"""
436 add_command(subparser
, 'remote', 'edit remotes', cmd_remote
)
439 def add_search_command(subparser
):
440 """Add the "git cola search" command for searching over commits"""
441 add_command(subparser
, 'search', 'search commits', cmd_search
)
444 def add_stash_command(subparser
):
445 """Add the "git cola stash" command for creating and applying stashes"""
446 add_command(subparser
, 'stash', 'stash and unstash changes', cmd_stash
)
449 def add_tag_command(subparser
):
450 """Add the "git cola tag" command for creating tags"""
451 parser
= add_command(subparser
, 'tag', 'create tags', cmd_tag
)
453 'name', metavar
='<name>', nargs
='?', default
=None, help='tag name'
456 'ref', metavar
='<ref>', nargs
='?', default
=None, help='commit to tag'
463 help='annotated and GPG-signed tag',
467 def add_version_command(subparser
):
468 """Add the "git cola version" command for displaying Git Cola's version"""
469 parser
= add_command(subparser
, 'version', 'print the version', cmd_version
)
474 help=argparse
.SUPPRESS
,
480 help='print the version number only',
486 """The "git cola" entry point"""
487 from .widgets
.main
import MainView
489 status_filter
= args
.status_filter
491 status_filter
= core
.abspath(status_filter
)
493 context
= app
.application_init(args
)
495 context
.timer
.start('view')
496 view
= MainView(context
)
498 cmds
.do(cmds
.AmendMode
, context
, amend
=True)
501 view
.set_filter(core
.relpath(status_filter
))
503 context
.timer
.stop('view')
505 context
.timer
.display('view')
507 return app
.application_run(context
, view
, start
=start_cola
, stop
=app
.default_stop
)
510 def start_cola(context
, view
):
511 app
.default_start(context
, view
)
516 from .widgets
import about
518 context
= app
.application_init(args
)
519 view
= about
.about_dialog(context
)
520 return app
.application_start(context
, view
)
524 from .widgets
.patch
import new_apply_patches
526 context
= app
.application_init(args
)
527 view
= new_apply_patches(context
, patches
=args
.patches
)
528 return app
.application_start(context
, view
)
531 def cmd_archive(args
):
532 from .widgets
import archive
534 context
= app
.application_init(args
, update
=True)
536 args
.ref
= context
.model
.currentbranch
537 view
= archive
.Archive(context
, args
.ref
)
538 return app
.application_start(context
, view
)
541 def cmd_branch(args
):
542 from .widgets
.createbranch
import create_new_branch
544 context
= app
.application_init(args
, update
=True)
545 view
= create_new_branch(context
)
546 return app
.application_start(context
, view
)
549 def cmd_browse(args
):
550 from .widgets
.browse
import worktree_browser
552 context
= app
.application_init(args
)
553 view
= worktree_browser(context
, show
=False, update
=False)
554 return app
.application_start(context
, view
)
558 from .widgets
import clone
560 context
= app
.application_init(args
)
561 view
= clone
.clone(context
)
562 context
.set_view(view
)
563 result
= 0 if view
.exec_() == view
.Accepted
else 1
564 app
.default_stop(context
, view
)
568 def cmd_config(args
):
569 from .widgets
.prefs
import preferences
571 context
= app
.application_init(args
)
572 view
= preferences(context
)
573 return app
.application_start(context
, view
)
577 from .widgets
import dag
579 context
= app
.application_init(args
)
580 # cola.main() uses parse_args(), unlike dag.main() which uses
581 # parse_known_args(), thus we aren't able to automatically forward
582 # all unknown arguments. Special-case support for "--all" since it's
583 # used by the history viewer command on Windows.
585 args
.args
.insert(0, '--all')
586 view
= dag
.git_dag(context
, args
=args
, show
=False)
587 return app
.application_start(context
, view
)
591 from .difftool
import diff_expression
593 context
= app
.application_init(args
)
594 expr
= core
.list2cmdline(args
.args
)
595 view
= diff_expression(context
, None, expr
, create_widget
=True)
596 return app
.application_start(context
, view
)
600 # TODO: the calls to update_status() can be done asynchronously
601 # by hooking into the message_updated notification.
602 from .widgets
import remote
604 context
= app
.application_init(args
)
605 context
.model
.update_status()
606 view
= remote
.fetch(context
)
607 return app
.application_start(context
, view
)
611 from .widgets
import finder
613 context
= app
.application_init(args
)
614 paths
= core
.list2cmdline(args
.paths
)
615 view
= finder
.finder(context
, paths
=paths
)
616 return app
.application_start(context
, view
)
620 from .widgets
import grep
622 context
= app
.application_init(args
)
623 text
= core
.list2cmdline(args
.args
)
624 view
= grep
.new_grep(context
, text
=text
, parent
=None)
625 return app
.application_start(context
, view
)
629 from .widgets
.merge
import Merge
631 context
= app
.application_init(args
, update
=True)
632 view
= Merge(context
, parent
=None, ref
=args
.ref
)
633 return app
.application_start(context
, view
)
636 def cmd_version(args
):
637 from . import version
639 version
.print_version(builtin
=args
.builtin
, brief
=args
.brief
)
644 from .widgets
import remote
646 context
= app
.application_init(args
, update
=True)
647 view
= remote
.pull(context
)
649 view
.set_rebase(True)
650 return app
.application_start(context
, view
)
654 from .widgets
import remote
656 context
= app
.application_init(args
, update
=True)
657 view
= remote
.push(context
)
658 return app
.application_start(context
, view
)
661 def cmd_rebase(args
):
663 'verbose': args
.verbose
,
665 'autostash': args
.autostash
,
666 'fork_point': args
.fork_point
,
668 'preserve_merges': args
.preserve_merges
,
669 'strategy': args
.strategy
,
672 'exec': getattr(args
, 'exec', None), # python keyword
673 'keep_empty': args
.keep_empty
,
674 'force_rebase': args
.force_rebase
,
675 'strategy_option': args
.strategy_option
,
677 'no_stat': args
.no_stat
,
678 'verify': args
.verify
,
679 'rerere_autoupdate': args
.rerere_autoupdate
,
681 'autosquash': args
.autosquash
,
682 'committer_date_is_author_date': args
.committer_date_is_author_date
,
683 'ignore_date': args
.ignore_date
,
684 'whitespace': args
.whitespace
,
685 'ignore_whitespace': args
.ignore_whitespace
,
686 'C': args
.context_lines
,
687 'continue': getattr(args
, 'continue', False), # python keyword
690 'edit_todo': args
.edit_todo
,
691 'update_refs': args
.update_refs
,
692 'upstream': args
.upstream
,
693 'branch': args
.branch
,
695 context
= app
.application_init(args
)
696 context
.model
.update_refs()
697 status
, _
, _
= cmds
.do(cmds
.Rebase
, context
, **kwargs
)
701 def cmd_recent(args
):
702 from .widgets
import recent
704 context
= app
.application_init(args
)
705 view
= recent
.browse_recent_files(context
)
706 return app
.application_start(context
, view
)
709 def cmd_remote(args
):
710 from .widgets
import editremotes
712 context
= app
.application_init(args
)
713 view
= editremotes
.editor(context
, run
=False)
714 return app
.application_start(context
, view
)
717 def cmd_search(args
):
718 from .widgets
.search
import search
720 context
= app
.application_init(args
)
721 view
= search(context
)
722 return app
.application_start(context
, view
)
726 from .widgets
import stash
728 context
= app
.application_init(args
)
729 view
= stash
.view(context
, show
=False)
730 return app
.application_start(context
, view
)
734 from .widgets
.createtag
import new_create_tag
736 context
= app
.application_init(args
)
737 context
.model
.update_status()
738 view
= new_create_tag(context
, name
=args
.name
, ref
=args
.ref
, sign
=args
.sign
)
739 return app
.application_start(context
, view
)
742 # Windows shortcut launch features:
743 def shortcut_launch():
744 """Launch from a shortcut
746 Prompt for the repository by default.
751 argv
= ['cola', '--prompt']
752 return main(argv
=argv
)