1 from __future__
import division
, absolute_import
, unicode_literals
6 from .interaction
import Interaction
7 from .models
import main
8 from .widgets
import completion
9 from .widgets
.browse
import BrowseBranch
10 from .widgets
.selectcommits
import select_commits
13 from . import difftool
22 """Launch the 'Delete Branch' dialog."""
23 icon
= icons
.discard()
24 branch
= choose_branch(N_('Delete Branch'), N_('Delete'), icon
=icon
)
27 cmds
.do(cmds
.DeleteBranch
, branch
)
30 def delete_remote_branch():
31 """Launch the 'Delete Remote Branch' dialog."""
32 branch
= choose_remote_branch(N_('Delete Remote Branch'), N_('Delete'),
36 rgx
= re
.compile(r
'^(?P<remote>[^/]+)/(?P<branch>.+)$')
37 match
= rgx
.match(branch
)
39 remote
= match
.group('remote')
40 branch
= match
.group('branch')
41 cmds
.do(cmds
.DeleteRemoteBranch
, remote
, branch
)
45 """Launch the 'Browse Current Branch' dialog."""
46 branch
= gitcmds
.current_branch()
47 BrowseBranch
.browse(branch
)
51 """Prompt for a branch and inspect content at that point in time."""
52 # Prompt for a branch to browse
53 branch
= choose_ref(N_('Browse Commits...'), N_('Browse'))
56 BrowseBranch
.browse(branch
)
59 def checkout_branch():
60 """Launch the 'Checkout Branch' dialog."""
61 branch
= choose_potential_branch(N_('Checkout Branch'), N_('Checkout'))
64 cmds
.do(cmds
.CheckoutBranch
, branch
)
68 """Launch the 'Cherry-Pick' dialog."""
69 revs
, summaries
= gitcmds
.log_helper(all
=True)
70 commits
= select_commits(N_('Cherry-Pick Commit'),
71 revs
, summaries
, multiselect
=False)
74 cmds
.do(cmds
.CherryPick
, commits
)
78 """Prompt for a new directory and create a new Git repository
80 :returns str: repository path or None if no repository was created.
83 path
= qtutils
.opendir_dialog(N_('New Repository...'), core
.getcwd())
86 # Avoid needlessly calling `git init`.
87 if git
.is_git_worktree(path
) or git
.is_git_dir(path
):
88 # We could prompt here and confirm that they really didn't
89 # mean to open an existing repository, but I think
90 # treating it like an "Open" is a sensible DWIM answer.
93 status
, out
, err
= core
.run_command(['git', 'init', path
])
97 title
= N_('Error Creating Repository')
98 msg
= (N_('"%(command)s" returned exit status %(status)d') %
99 dict(command
='git init %s' % path
, status
=status
))
100 details
= N_('Output:\n%s') % out
103 details
+= N_('Errors: %s') % err
104 qtutils
.critical(title
, msg
, details
)
112 cmds
.do(cmds
.OpenRepo
, dirname
)
115 def prompt_for_clone():
117 Present a GUI for cloning a repository.
119 Returns the target directory and URL
122 url
, ok
= qtutils
.prompt(N_('Path or URL to clone (Env. $VARS okay)'))
123 url
= utils
.expandpath(url
)
124 if not ok
or not url
:
127 # Pick a suitable basename by parsing the URL
128 newurl
= url
.replace('\\', '/').rstrip('/')
129 default
= newurl
.rsplit('/', 1)[-1]
130 if default
== '.git':
131 # The end of the URL is /.git, so assume it's a file path
132 default
= os
.path
.basename(os
.path
.dirname(newurl
))
133 if default
.endswith('.git'):
134 # The URL points to a bare repo
135 default
= default
[:-4]
137 # The URL is the current repo
138 default
= os
.path
.basename(core
.getcwd())
142 Interaction
.information(
144 N_('Could not parse Git URL: "%s"') % url
)
145 Interaction
.log(N_('Could not parse Git URL: "%s"') % url
)
148 # Prompt the user for a directory to use as the parent directory
149 msg
= N_('Select a parent directory for the new clone')
150 dirname
= qtutils
.opendir_dialog(msg
, main
.model().getcwd())
154 destdir
= os
.path
.join(dirname
, default
)
156 if core
.exists(destdir
):
157 # An existing path can be specified
158 msg
= (N_('"%s" already exists, cola will create a new directory') %
160 Interaction
.information(N_('Directory Exists'), msg
)
162 # Make sure the new destdir doesn't exist
163 while core
.exists(destdir
):
164 destdir
= olddestdir
+ str(count
)
170 def export_patches():
171 """Run 'git format-patch' on a list of commits."""
172 revs
, summaries
= gitcmds
.log_helper()
173 to_export
= select_commits(N_('Export Patches'), revs
, summaries
)
176 cmds
.do(cmds
.FormatPatch
, reversed(to_export
), reversed(revs
))
179 def diff_expression():
180 """Diff using an arbitrary expression."""
181 tracked
= gitcmds
.tracked_branch()
182 current
= gitcmds
.current_branch()
183 if tracked
and current
:
184 ref
= tracked
+ '..' + current
186 ref
= 'origin/master..'
187 difftool
.diff_expression(qtutils
.active_window(), ref
)
191 dirname
= qtutils
.opendir_dialog(N_('Open Git Repository...'),
192 main
.model().getcwd())
195 cmds
.do(cmds
.OpenRepo
, dirname
)
198 def open_repo_in_new_window():
199 """Spawn a new cola session."""
200 dirname
= qtutils
.opendir_dialog(N_('Open Git Repository...'),
201 main
.model().getcwd())
204 cmds
.do(cmds
.OpenNewRepo
, dirname
)
207 def load_commitmsg():
208 """Load a commit message from a file."""
209 filename
= qtutils
.open_file(N_('Load Commit Message'),
210 directory
=main
.model().getcwd())
212 cmds
.do(cmds
.LoadCommitMessageFromFile
, filename
)
215 def choose_from_dialog(get
, title
, button_text
, default
, icon
=None):
216 parent
= qtutils
.active_window()
217 return get(title
, button_text
, parent
, default
=default
, icon
=icon
)
220 def choose_ref(title
, button_text
, default
=None, icon
=None):
221 return choose_from_dialog(completion
.GitRefDialog
.get
,
222 title
, button_text
, default
, icon
=icon
)
225 def choose_branch(title
, button_text
, default
=None, icon
=None):
226 return choose_from_dialog(completion
.GitBranchDialog
.get
,
227 title
, button_text
, default
, icon
=icon
)
230 def choose_potential_branch(title
, button_text
, default
=None, icon
=None):
231 return choose_from_dialog(completion
.GitPotentialBranchDialog
.get
,
232 title
, button_text
, default
, icon
=icon
)
235 def choose_remote_branch(title
, button_text
, default
=None, icon
=None):
236 return choose_from_dialog(completion
.GitRemoteBranchDialog
.get
,
237 title
, button_text
, default
, icon
=icon
)
241 """Diff against an arbitrary revision, branch, tag, etc."""
242 branch
= choose_ref(N_('Select Branch to Review'), N_('Review'))
245 merge_base
= gitcmds
.merge_base_parent(branch
)
246 difftool
.diff_commits(qtutils
.active_window(), merge_base
, branch
)
249 class CloneTask(qtutils
.Task
):
250 """Clones a Git repository"""
252 def __init__(self
, url
, destdir
, spawn
, parent
):
253 qtutils
.Task
.__init
__(self
, parent
)
255 self
.destdir
= destdir
260 """Runs the model action and captures the result"""
261 self
.cmd
= cmds
.do(cmds
.Clone
, self
.url
, self
.destdir
, spawn
=self
.spawn
)
265 def clone_repo(parent
, runtask
, progress
, finish
, spawn
):
266 """Clone a repository asynchronously with progress animation"""
267 result
= prompt_for_clone()
270 # Use a thread to update in the background
271 url
, destdir
= result
272 progress
.set_details(N_('Clone Repository'),
273 N_('Cloning repository at %s') % url
)
274 task
= CloneTask(url
, destdir
, spawn
, parent
)
275 runtask
.start(task
, finish
=finish
, progress
=progress
)
278 def report_clone_repo_errors(task
):
279 """Report errors from the clone task if they exist"""
280 if task
.cmd
is None or task
.cmd
.ok
:
282 Interaction
.critical(task
.cmd
.error_message
,
283 message
=task
.cmd
.error_message
,
284 details
=task
.cmd
.error_details
)
288 """Launch the 'Rename Branch' dialogs."""
289 branch
= choose_branch(N_('Rename Existing Branch'), N_('Select'))
292 new_branch
= choose_branch(N_('Enter New Branch Name'), N_('Rename'))
295 cmds
.do(cmds
.RenameBranch
, branch
, new_branch
)
298 def reset_branch_head():
299 ref
= choose_ref(N_('Reset Branch Head'), N_('Reset'), default
='HEAD^')
301 cmds
.do(cmds
.ResetBranchHead
, ref
)
304 def reset_worktree():
305 ref
= choose_ref(N_('Reset Worktree'), N_('Reset'))
307 cmds
.do(cmds
.ResetWorktree
, ref
)