controllers.remote: Filter remote branches when selecting remotes
[git-cola.git] / cola / controllers / remote.py
blob9e0c2997ec80221472189518c0eb570cbeb847a0
1 """This controller handles the remote dialog."""
4 import os
5 from PyQt4.QtGui import QDialog
7 import cola
8 from cola import serializer
9 from cola import utils
10 from cola import qtutils
11 from cola.views import remote
12 from cola.qobserver import QObserver
14 def remote_action(parent, action):
15 """Launches fetch/push/pull dialogs."""
16 # TODO: subclass model
17 model = serializer.clone(cola.model())
18 model.remotename = ''
19 model.tags_checkbox = False
20 model.rebase_checkbox = False
21 model.ffwd_only_checkbox = True
23 view = remote.RemoteView(parent, action)
24 controller = RemoteController(model, view, action)
25 view.show()
27 class RemoteController(QObserver):
28 """Provides control for the remote-action dialog."""
29 def __init__(self, model, view, action):
30 QObserver.__init__(self, model, view)
31 self.add_observables('remotename',
32 'remotes',
33 'local_branch',
34 'local_branches',
35 'remote_branch',
36 'remote_branches',
37 'tags_checkbox',
38 'rebase_checkbox',
39 'ffwd_only_checkbox')
40 self.action = action
41 """The current mode; one of fetch/push/pull"""
43 self.action_method = {
44 'fetch': self.gen_remote_callback(self.model.fetch_helper),
45 'push': self.gen_remote_callback(self.model.push_helper),
46 'pull': self.gen_remote_callback(self.model.pull_helper),
47 } [action]
48 """Callbacks corresponding to the 3 (fetch/push/pull) modes"""
50 self.add_actions(remotes = self.display_remotes)
51 self.add_callbacks(action_button = self.action_method,
52 remotes = self.update_remotes,
53 local_branches = self.update_local_branches,
54 remote_branches = self.update_remote_branches)
55 self.refresh_view()
56 remotes = self.model.remotes
57 if 'origin' in remotes:
58 idx = remotes.index('origin')
59 if self.view.select_remote(idx):
60 self.model.set_remotename('origin')
61 else:
62 if self.view.select_first_remote():
63 self.model.set_remotename(remotes[0])
65 # Select the current branch by default for push
66 if action != 'push':
67 return
68 branches = self.model.local_branches
69 branch = self.model.currentbranch
70 if branch not in branches:
71 return
72 idx = branches.index(branch)
73 if self.view.select_local_branch(idx):
74 self.model.set_local_branch(branch)
76 def display_remotes(self, widget):
77 """Display the available remotes in a listwidget"""
78 displayed = []
79 for remotename in self.model.remotes:
80 url = self.model.remote_url(remotename)
81 display = ('%s\t(%s %s)'
82 % (remotename, unicode(self.tr('URL:')), url))
83 displayed.append(display)
84 qtutils.set_items(widget,displayed)
86 def update_remotes(self,*rest):
87 """Update the remote name when a remote from the list is selected"""
88 widget = self.view.remotes
89 remotes = self.model.remotes
90 selection = qtutils.selected_item(widget, remotes)
91 if not selection:
92 return
93 self.model.set_remotename(selection)
94 self.view.remotename.selectAll()
96 if self.action != 'pull':
97 pass
98 all_branches = gitcmds.branch_list(remote=True)
99 branches = []
100 pat = selection + '/*'
101 for branch in all_branches:
102 if fnmatch.fnmatch(branch, pat):
103 branches.append(branch)
104 if branches:
105 self.model.set_remote_branches(branches)
106 else:
107 self.model.set_remote_branches(all_branches)
108 self.model.set_remote_branch('')
110 def update_local_branches(self,*rest):
111 """Update the local/remote branch names when a branch is selected"""
112 branches = self.model.local_branches
113 widget = self.view.local_branches
114 selection = qtutils.selected_item(widget, branches)
115 if not selection:
116 return
118 self.model.set_local_branch(selection)
119 self.model.set_remote_branch(selection)
121 self.view.local_branch.selectAll()
122 self.view.remote_branch.selectAll()
124 def update_remote_branches(self,*rest):
125 """Update the remote branch name when a branch is selected"""
126 widget = self.view.remote_branches
127 branches = self.model.remote_branches
128 selection = qtutils.selected_item(widget,branches)
129 if not selection:
130 return
131 branch = utils.basename(selection)
132 if branch == 'HEAD':
133 return
134 self.model.set_remote_branch(branch)
135 self.view.remote_branch.selectAll()
137 def common_args(self):
138 """Returns git arguments common to fetch/push/pulll"""
139 # TODO move to model
140 return (self.model.remotename,
142 'local_branch': self.model.local_branch,
143 'remote_branch': self.model.remote_branch,
144 'ffwd': self.model.ffwd_only_checkbox,
145 'tags': self.model.tags_checkbox,
146 'rebase': self.model.rebase_checkbox,
149 #+-------------------------------------------------------------
150 #+ Actions
151 def gen_remote_callback(self, modelaction):
152 """Generates a Qt callback for fetch/push/pull.
154 def remote_callback():
155 if not self.model.remotename:
156 errmsg = self.tr('No repository selected.')
157 qtutils.log(1, errmsg)
158 return
159 remote, kwargs = self.common_args()
160 action = self.action
162 # Check if we're about to create a new branch and warn.
163 if action == 'push' and not self.model.remote_branch:
164 branch = self.model.local_branch
165 candidate = '%s/%s' % (remote, branch)
166 if candidate not in self.model.remote_branches:
167 msg = ('Branch "' + branch + '" does not exist in ' +
168 remote + '.\n\nCreate a new branch?')
169 if not qtutils.question(self.view, 'Create New Branch?',
170 msg, default=False):
171 return
173 if not self.model.ffwd_only_checkbox:
174 if action == 'fetch':
175 msg = ('Non-fast-forward fetch overwrites local '
176 'history!\n\tContinue?')
177 elif action == 'push':
178 msg = ('Non-fast-forward push overwrites published '
179 'history!\nAre you sure you want to do this? '
180 '(Did you pull first?)\n\tContinue?')
181 else: # pull: shouldn't happen since the controls are hidden
182 msg = "You probably don't want to do this.\n\tContinue?"
183 if not qtutils.question(self.view,
184 'Force %s?' % action.title(), msg, default=False):
185 return
186 status, output = modelaction(remote, **kwargs)
187 if not output: # git fetch --tags --verbose doesn't print anything...
188 output = self.tr('Already up-to-date.')
189 # Force the status to 1 so that we always display the log
190 qtutils.log(1, output)
191 self.view.accept()
192 return remote_callback