cmds: add SetDefaultRepo command
[git-cola.git] / cola / difftool.py
blob9b41be710744dfe77f78d54f7bad2dce75aed9f8
1 from __future__ import division, absolute_import, unicode_literals
3 from PyQt4 import QtGui
4 from PyQt4 import QtCore
5 from PyQt4.QtCore import Qt
6 from PyQt4.QtCore import SIGNAL
8 from cola import core
9 from cola import gitcmds
10 from cola import hotkeys
11 from cola import icons
12 from cola import qtutils
13 from cola import utils
14 from cola.i18n import N_
15 from cola.interaction import Interaction
16 from cola.models import main
17 from cola.models import selection
18 from cola.widgets import completion
19 from cola.widgets import defs
20 from cola.widgets import filetree
21 from cola.compat import ustr
24 def run():
25 files = selection.selected_group()
26 if not files:
27 return
28 s = selection.selection()
29 model = main.model()
30 launch_with_head(files, bool(s.staged), model.head)
33 def launch_with_head(filenames, staged, head):
34 if head == 'HEAD':
35 left = None
36 else:
37 left = head
38 launch(left=left, staged=staged, paths=filenames)
41 def launch(left=None, right=None, paths=None,
42 left_take_parent=False, staged=False):
43 """Launches 'git difftool' with given parameters"""
45 difftool_args = ['git', 'difftool', '--no-prompt']
46 if staged:
47 difftool_args.append('--cached')
49 if left:
50 if left_take_parent:
51 # Check root commit (no parents and thus cannot execute '~')
52 model = main.model()
53 git = model.git
54 status, out, err = git.rev_list(left, parents=True, n=1)
55 Interaction.log_status(status, out, err)
56 if status:
57 raise StandardError('git rev-list command failed')
59 if len(out.split()) >= 2:
60 # Commit has a parent, so we can take its child as requested
61 left += '~'
62 else:
63 # No parent, assume it's the root commit, so we have to diff
64 # against the empty tree. The empty tree is a built-in
65 # git constant SHA1. The empty tree is a built-in Git SHA1.
66 left = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
67 difftool_args.append(left)
69 if right:
70 difftool_args.append(right)
72 if paths:
73 difftool_args.append('--')
74 difftool_args.extend(paths)
76 core.fork(difftool_args)
79 def diff_commits(parent, a, b):
80 dlg = FileDiffDialog(parent, a=a, b=b)
81 dlg.show()
82 dlg.raise_()
83 return dlg.exec_() == QtGui.QDialog.Accepted
86 def diff_expression(parent, expr,
87 create_widget=False, hide_expr=False):
88 dlg = FileDiffDialog(parent, expr=expr, hide_expr=hide_expr)
89 if create_widget:
90 return dlg
91 dlg.show()
92 dlg.raise_()
93 return dlg.exec_() == QtGui.QDialog.Accepted
96 class FileDiffDialog(QtGui.QDialog):
98 def __init__(self, parent, a=None, b=None, expr=None, title=None,
99 hide_expr=False):
100 QtGui.QDialog.__init__(self, parent)
101 self.setAttribute(Qt.WA_MacMetalStyle)
103 self.a = a
104 self.b = b
105 self.diff_expr = expr
107 if title is None:
108 title = N_('git-cola diff')
110 self.setWindowTitle(title)
111 self.setWindowModality(QtCore.Qt.WindowModal)
113 self.expr = completion.GitRefLineEdit(parent=self)
114 if expr is not None:
115 self.expr.setText(expr)
117 if expr is None or hide_expr:
118 self.expr.hide()
120 self.tree = filetree.FileTree(parent=self)
122 self.diff_button = qtutils.create_button(text=N_('Compare'),
123 icon=icons.diff(),
124 enabled=False)
125 self.close_button = qtutils.close_button()
127 self.button_layout = qtutils.hbox(defs.no_margin, defs.spacing,
128 qtutils.STRETCH,
129 self.diff_button, self.close_button)
131 self.main_layout = qtutils.vbox(defs.margin, defs.spacing,
132 self.expr, self.tree,
133 self.button_layout)
134 self.setLayout(self.main_layout)
136 self.connect(self.tree, SIGNAL('itemSelectionChanged()'),
137 self.tree_selection_changed)
139 self.connect(self.tree,
140 SIGNAL('itemDoubleClicked(QTreeWidgetItem*,int)'),
141 self.tree_double_clicked)
143 self.connect(self.expr, SIGNAL('textChanged(QString)'),
144 self.text_changed)
145 self.connect(self.tree, SIGNAL('up()'), self.focus_input)
147 self.connect(self.expr, SIGNAL('activated()'), self.focus_tree)
148 self.connect(self.expr, SIGNAL('down()'), self.focus_tree)
149 self.connect(self.expr, SIGNAL('enter()'), self.focus_tree)
150 self.connect(self.expr, SIGNAL('return()'), self.focus_tree)
152 qtutils.connect_button(self.diff_button, self.diff)
153 qtutils.connect_button(self.close_button, self.close)
155 qtutils.add_action(self, 'Focus Input', self.focus_input, hotkeys.FOCUS)
156 qtutils.add_close_action(self)
158 self.resize(720, 420)
159 self.refresh()
161 def focus_tree(self):
162 self.tree.setFocus()
164 def focus_input(self):
165 self.expr.setFocus()
167 def text_changed(self, txt):
168 self.diff_expr = ustr(txt)
169 self.refresh()
171 def refresh(self):
172 if self.diff_expr is not None:
173 self.diff_arg = utils.shell_split(self.diff_expr)
174 elif self.b is None:
175 self.diff_arg = [self.a]
176 else:
177 self.diff_arg = [self.a, self.b]
178 self.refresh_filenames()
180 def refresh_filenames(self):
181 if self.a and self.b is None:
182 filenames = gitcmds.diff_index_filenames(self.a)
183 else:
184 filenames = gitcmds.diff(self.diff_arg)
185 self.tree.set_filenames(filenames, select=True)
187 def tree_selection_changed(self):
188 self.diff_button.setEnabled(self.tree.has_selection())
190 def tree_double_clicked(self, item, column):
191 path = self.tree.filename_from_item(item)
192 left, right = self._left_right_args()
193 launch(left=left, right=right, paths=[path])
195 def diff(self):
196 paths = self.tree.selected_filenames()
197 left, right = self._left_right_args()
198 launch(left=left, right=right, paths=paths)
200 def _left_right_args(self):
201 if self.diff_arg:
202 left = self.diff_arg[0]
203 else:
204 left = None
205 if len(self.diff_arg) > 1:
206 right = self.diff_arg[1]
207 else:
208 right = None
209 return (left, right)