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
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
25 files
= selection
.selected_group()
28 s
= selection
.selection()
30 launch_with_head(files
, bool(s
.staged
), model
.head
)
33 def launch_with_head(filenames
, staged
, 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']
47 difftool_args
.append('--cached')
51 # Check root commit (no parents and thus cannot execute '~')
54 status
, out
, err
= git
.rev_list(left
, parents
=True, n
=1)
55 Interaction
.log_status(status
, out
, err
)
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
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
)
70 difftool_args
.append(right
)
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
)
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
)
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,
100 QtGui
.QDialog
.__init
__(self
, parent
)
101 self
.setAttribute(Qt
.WA_MacMetalStyle
)
105 self
.diff_expr
= expr
108 title
= N_('git-cola diff')
110 self
.setWindowTitle(title
)
111 self
.setWindowModality(QtCore
.Qt
.WindowModal
)
113 self
.expr
= completion
.GitRefLineEdit(parent
=self
)
115 self
.expr
.setText(expr
)
117 if expr
is None or hide_expr
:
120 self
.tree
= filetree
.FileTree(parent
=self
)
122 self
.diff_button
= qtutils
.create_button(text
=N_('Compare'),
125 self
.close_button
= qtutils
.close_button()
127 self
.button_layout
= qtutils
.hbox(defs
.no_margin
, defs
.spacing
,
129 self
.diff_button
, self
.close_button
)
131 self
.main_layout
= qtutils
.vbox(defs
.margin
, defs
.spacing
,
132 self
.expr
, self
.tree
,
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)'),
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)
161 def focus_tree(self
):
164 def focus_input(self
):
167 def text_changed(self
, txt
):
168 self
.diff_expr
= ustr(txt
)
172 if self
.diff_expr
is not None:
173 self
.diff_arg
= utils
.shell_split(self
.diff_expr
)
175 self
.diff_arg
= [self
.a
]
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
)
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
])
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
):
202 left
= self
.diff_arg
[0]
205 if len(self
.diff_arg
) > 1:
206 right
= self
.diff_arg
[1]