main: Simplify the "Diff->SHA-1" action
[git-cola.git] / cola / guicmds.py
blob6d841c74f12471691ae5f92ea9666fa2b9b47d15
1 import os
3 import cola
4 from cola import core
5 from cola import difftool
6 from cola import gitcmds
7 from cola import qt
8 from cola import qtutils
9 from cola import signals
10 from cola.git import git
11 from cola.widgets.browse import BrowseDialog
12 from cola.widgets.combodlg import ComboDialog
13 from cola.widgets.selectcommits import select_commits
16 def install_command_wrapper():
17 wrapper = CommandWrapper()
18 cola.factory().add_command_wrapper(wrapper)
21 class CommandWrapper(object):
22 def __init__(self):
23 self.callbacks = {
24 signals.confirm: qtutils.confirm,
25 signals.critical: qtutils.critical,
26 signals.information: qtutils.information,
27 signals.question: qtutils.question,
31 def choose_from_combo(title, items):
32 """Quickly choose an item from a list using a combo box"""
33 return ComboDialog(qtutils.active_window(), title=title, items=items).selected()
36 def slot_with_parent(fn, parent):
37 """Return an argument-less method for calling fn(parent=parent)
39 :param fn: - Function reference, must accept 'parent' as a keyword
40 :param parent: - Qt parent widget
42 """
43 def slot():
44 fn(parent=parent)
45 return slot
48 def branch_delete():
49 """Launch the 'Delete Branch' dialog."""
50 branch = choose_from_combo('Delete Branch',
51 cola.model().local_branches)
52 if not branch:
53 return
54 cola.notifier().broadcast(signals.delete_branch, branch)
57 def diff_revision():
58 """Diff an arbitrary revision against the worktree"""
59 ref = choose_ref('Select Revision to Diff', 'Diff', pre=['HEAD^'])
60 if not ref:
61 return
62 difftool.diff_commits(qtutils.active_window(), ref, None)
65 def browse_current():
66 """Launch the 'Browse Current Branch' dialog."""
67 branch = gitcmds.current_branch()
68 BrowseDialog.browse(branch)
71 def browse_other():
72 """Prompt for a branch and inspect content at that point in time."""
73 # Prompt for a branch to browse
74 branch = choose_from_combo('Browse Revision...', gitcmds.all_refs())
75 if not branch:
76 return
77 BrowseDialog.browse(branch)
80 def checkout_branch():
81 """Launch the 'Checkout Branch' dialog."""
82 branch = choose_from_combo('Checkout Branch',
83 cola.model().local_branches)
84 if not branch:
85 return
86 cola.notifier().broadcast(signals.checkout_branch, branch)
89 def cherry_pick():
90 """Launch the 'Cherry-Pick' dialog."""
91 revs, summaries = gitcmds.log_helper(all=True)
92 commits = select_commits('Cherry-Pick Commit',
93 revs, summaries, multiselect=False)
94 if not commits:
95 return
96 cola.notifier().broadcast(signals.cherry_pick, commits)
99 def clone_repo(spawn=True):
101 Present GUI controls for cloning a repository
103 A new cola session is invoked when 'spawn' is True.
106 url, ok = qtutils.prompt('Path or URL to clone (Env. $VARS okay)')
107 url = os.path.expandvars(core.encode(url))
108 if not ok or not url:
109 return None
110 try:
111 # Pick a suitable basename by parsing the URL
112 newurl = url.replace('\\', '/')
113 default = newurl.rsplit('/', 1)[-1]
114 if default == '.git':
115 # The end of the URL is /.git, so assume it's a file path
116 default = os.path.basename(os.path.dirname(newurl))
117 if default.endswith('.git'):
118 # The URL points to a bare repo
119 default = default[:-4]
120 if url == '.':
121 # The URL is the current repo
122 default = os.path.basename(os.getcwd())
123 if not default:
124 raise
125 except:
126 cola.notifier().broadcast(signals.information,
127 'Error Cloning',
128 'Could not parse: "%s"' % url)
129 qtutils.log(1, 'Oops, could not parse git url: "%s"' % url)
130 return None
132 # Prompt the user for a directory to use as the parent directory
133 msg = 'Select a parent directory for the new clone'
134 dirname = qtutils.opendir_dialog(msg, cola.model().getcwd())
135 if not dirname:
136 return None
137 count = 1
138 dirname = core.decode(dirname)
139 destdir = os.path.join(dirname, core.decode(default))
140 olddestdir = destdir
141 if os.path.exists(destdir):
142 # An existing path can be specified
143 msg = ('"%s" already exists, cola will create a new directory' %
144 destdir)
145 cola.notifier().broadcast(signals.information,
146 'Directory Exists', msg)
148 # Make sure the new destdir doesn't exist
149 while os.path.exists(destdir):
150 destdir = olddestdir + str(count)
151 count += 1
152 cola.notifier().broadcast(signals.clone, core.decode(url), destdir,
153 spawn=spawn)
154 return destdir
157 def export_patches():
158 """Run 'git format-patch' on a list of commits."""
159 revs, summaries = gitcmds.log_helper()
160 to_export = select_commits('Export Patches', revs, summaries)
161 if not to_export:
162 return
163 to_export.reverse()
164 revs.reverse()
165 cola.notifier().broadcast(signals.format_patch, to_export, revs)
168 def diff_expression():
169 """Diff using an arbitrary expression."""
170 expr = choose_from_combo('Enter Diff Expression',
171 cola.model().all_branches() +
172 cola.model().tags)
173 if not expr:
174 return
175 cola.notifier().broadcast(signals.diff_expr_mode, expr)
178 def goto_grep(line):
179 """Called when Search -> Grep's right-click 'goto' action."""
180 filename, line_number, contents = line.split(':', 2)
181 filename = core.encode(filename)
182 cola.notifier().broadcast(signals.edit, [filename], line_number=line_number)
185 def grep():
186 """Prompt and use 'git grep' to find the content."""
187 # This should be a command in cola.cmds.
188 txt, ok = qtutils.prompt('grep')
189 if not ok:
190 return
191 cola.notifier().broadcast(signals.grep, txt)
194 def open_repo():
195 """Spawn a new cola session."""
196 dirname = qtutils.opendir_dialog('Open Git Repository...',
197 cola.model().getcwd())
198 if not dirname:
199 return
200 cola.notifier().broadcast(signals.open_repo, dirname)
203 def load_commitmsg():
204 """Load a commit message from a file."""
205 filename = qtutils.open_dialog('Load Commit Message...',
206 cola.model().getcwd())
207 if filename:
208 cola.notifier().broadcast(signals.load_commit_message, filename)
211 def rebase():
212 """Rebase onto a branch."""
213 branch = choose_from_combo('Rebase Branch',
214 cola.model().all_branches())
215 if not branch:
216 return
217 #TODO cmd
218 status, output = git.rebase(branch, with_stderr=True, with_status=True)
219 qtutils.log(status, output)
222 def choose_ref(title, button_text, pre=None):
223 provider = None
224 if pre:
225 provider = qt.GitRefProvider(pre=pre)
226 parent = qtutils.active_window()
227 return qt.GitRefDialog.ref(title, button_text, parent, provider=provider)
230 def review_branch():
231 """Diff against an arbitrary revision, branch, tag, etc."""
232 branch = choose_ref('Select Branch to Review', 'Review')
233 if not branch:
234 return
235 merge_base = gitcmds.merge_base_parent(branch)
236 difftool.diff_commits(qtutils.active_window(), merge_base, branch)