tree-wide: spelling corrections
[git-cola.git] / cola / widgets / clone.py
blob78262e8c10b65ed504252d3371d9bb663531c97d
1 import os
2 from functools import partial
4 from qtpy import QtCore
5 from qtpy import QtWidgets
6 from qtpy.QtCore import Qt
8 from .. import cmds
9 from .. import core
10 from .. import icons
11 from .. import utils
12 from .. import qtutils
13 from ..i18n import N_
14 from ..interaction import Interaction
15 from ..qtutils import get
16 from . import defs
17 from . import standard
18 from . import text
21 def clone(context, spawn=True, show=True):
22 """Clone a repository and spawn a new git-cola instance"""
23 parent = qtutils.active_window()
24 progress = standard.progress('', '', parent)
25 return clone_repo(context, show, progress, task_finished, spawn)
28 def clone_repo(context, show, progress, finish, spawn):
29 """Clone a repository asynchronously with progress animation"""
30 func = partial(start_clone_task, context, progress, finish, spawn)
31 prompt = prompt_for_clone(context, show=show)
32 prompt.result.connect(func)
33 return prompt
36 def prompt_for_clone(context, show=True):
37 """Presents a GUI for cloning a repository"""
38 view = Clone(context, parent=qtutils.active_window())
39 if show:
40 view.show()
41 return view
44 def task_finished(task):
45 """Report errors from the clone task if they exist"""
46 cmd = task.cmd
47 if cmd is None:
48 return
49 status = cmd.status
50 out = cmd.out
51 err = cmd.err
52 title = N_('Error: could not clone "%s"') % cmd.url
53 Interaction.command(title, 'git clone', status, out, err)
56 def start_clone_task(
57 context, progress, finish, spawn, url, destdir, submodules, shallow
59 # Use a thread to update in the background
60 runtask = context.runtask
61 progress.set_details(N_('Clone Repository'), N_('Cloning repository at %s') % url)
62 task = CloneTask(context, url, destdir, submodules, shallow, spawn)
63 runtask.start(task, finish=finish, progress=progress)
66 class CloneTask(qtutils.Task):
67 """Clones a Git repository"""
69 def __init__(self, context, url, destdir, submodules, shallow, spawn):
70 qtutils.Task.__init__(self)
71 self.context = context
72 self.url = url
73 self.destdir = destdir
74 self.submodules = submodules
75 self.shallow = shallow
76 self.spawn = spawn
77 self.cmd = None
79 def task(self):
80 """Runs the model action and captures the result"""
81 self.cmd = cmds.do(
82 cmds.Clone,
83 self.context,
84 self.url,
85 self.destdir,
86 self.submodules,
87 self.shallow,
88 spawn=self.spawn,
90 return self.cmd
93 class Clone(standard.Dialog):
94 # Signal binding for returning the input data
95 result = QtCore.Signal(object, object, bool, bool)
97 def __init__(self, context, parent=None):
98 standard.Dialog.__init__(self, parent=parent)
99 self.context = context
100 self.model = context.model
102 self.setWindowTitle(N_('Clone Repository'))
103 if parent is not None:
104 self.setWindowModality(Qt.WindowModal)
106 # Repository location
107 self.url_label = QtWidgets.QLabel(N_('URL'))
108 hint = 'git://git.example.com/repo.git'
109 self.url = text.HintedLineEdit(context, hint, parent=self)
110 self.url.setToolTip(N_('Path or URL to clone (Env. $VARS okay)'))
112 # Initialize submodules
113 self.submodules = qtutils.checkbox(
114 text=N_('Inititalize submodules'), checked=False
117 # Reduce commit history
118 self.shallow = qtutils.checkbox(
119 text=N_('Reduce commit history to minimum'), checked=False
122 # Buttons
123 self.ok_button = qtutils.create_button(
124 text=N_('Clone'), icon=icons.ok(), default=True
126 self.close_button = qtutils.close_button()
128 # Form layout for inputs
129 self.input_layout = qtutils.form(
130 defs.no_margin, defs.button_spacing, (self.url_label, self.url)
133 self.button_layout = qtutils.hbox(
134 defs.margin,
135 defs.spacing,
136 self.submodules,
137 defs.button_spacing,
138 self.shallow,
139 qtutils.STRETCH,
140 self.close_button,
141 self.ok_button,
144 self.main_layout = qtutils.vbox(
145 defs.margin, defs.spacing, self.input_layout, self.button_layout
147 self.setLayout(self.main_layout)
149 qtutils.connect_button(self.close_button, self.close)
150 qtutils.connect_button(self.ok_button, self.prepare_to_clone)
151 # pylint: disable=no-member
152 self.url.textChanged.connect(lambda x: self.update_actions())
154 self.init_state(context.settings, self.resize, 720, 200)
155 self.update_actions()
157 def update_actions(self):
158 url = get(self.url).strip()
159 enabled = bool(url)
160 self.ok_button.setEnabled(enabled)
162 def prepare_to_clone(self):
163 """Grabs and validates the input data"""
165 submodules = get(self.submodules)
166 shallow = get(self.shallow)
168 url = get(self.url)
169 url = utils.expandpath(url)
170 if not url:
171 return
172 # Pick a suitable basename by parsing the URL
173 newurl = url.replace('\\', '/').rstrip('/')
174 try:
175 default = newurl.rsplit('/', 1)[-1]
176 except IndexError:
177 default = ''
178 if default == '.git':
179 # The end of the URL is /.git, so assume it's a file path
180 default = os.path.basename(os.path.dirname(newurl))
181 if default.endswith('.git'):
182 # The URL points to a bare repo
183 default = default[:-4]
184 if url == '.':
185 # The URL is the current repo
186 default = os.path.basename(core.getcwd())
187 if not default:
188 Interaction.information(
189 N_('Error Cloning'), N_('Could not parse Git URL: "%s"') % url
191 Interaction.log(N_('Could not parse Git URL: "%s"') % url)
192 return
194 # Prompt the user for a directory to use as the parent directory
195 msg = N_('Select a parent directory for the new clone')
196 dirname = qtutils.opendir_dialog(msg, self.model.getcwd())
197 if not dirname:
198 return
199 count = 1
200 destdir = os.path.join(dirname, default)
201 olddestdir = destdir
202 if core.exists(destdir):
203 # An existing path can be specified
204 msg = N_('"%s" already exists, cola will create a new directory') % destdir
205 Interaction.information(N_('Directory Exists'), msg)
207 # Make sure the directory doesn't exist
208 while core.exists(destdir):
209 destdir = olddestdir + str(count)
210 count += 1
212 # Return the input data and close the dialog
213 self.result.emit(url, destdir, submodules, shallow)
214 self.close()