Skip to content

Commit 69c6122

Browse files
authored
Add submodules clone checkbox (#1188)
* Add submodules clone checkbox * Reorder getValue() type hint * Fix test_clone.py formatting with Black
1 parent da6ed22 commit 69c6122

File tree

4 files changed

+60
-12
lines changed

4 files changed

+60
-12
lines changed

jupyterlab_git/git.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ async def changed_files(self, path, base=None, remote=None, single_commit=None):
272272

273273
return response
274274

275-
async def clone(self, path, repo_url, auth=None, versioning=True):
275+
async def clone(self, path, repo_url, auth=None, versioning=True, submodules=False):
276276
"""
277277
Execute `git clone`.
278278
When no auth is provided, disables prompts for the password to avoid the terminal hanging.
@@ -281,13 +281,16 @@ async def clone(self, path, repo_url, auth=None, versioning=True):
281281
:param repo_url: the URL of the repository to be cloned.
282282
:param auth: OPTIONAL dictionary with 'username' and 'password' fields
283283
:param versioning: OPTIONAL whether to clone or download a snapshot of the remote repository; default clone
284+
:param submodules: OPTIONAL whether to clone submodules content; default False
284285
:return: response with status code and error message.
285286
"""
286287
env = os.environ.copy()
287288
cmd = ["git", "clone"]
288289
if not versioning:
289290
cmd.append("--depth=1")
290291
current_content = set(os.listdir(path))
292+
if submodules:
293+
cmd.append("--recurse-submodules")
291294
cmd.append(unquote(repo_url))
292295

293296
if auth:

jupyterlab_git/handlers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ async def post(self, path: str = ""):
8989
data["clone_url"],
9090
data.get("auth", None),
9191
data["versioning"],
92+
data["submodules"],
9293
)
9394

9495
if response["code"] != 0:

jupyterlab_git/tests/test_clone.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,30 @@ def create_fake_git_repo(*args, **kwargs):
6464
assert not git_folder.exists()
6565

6666

67+
@pytest.mark.asyncio
68+
async def test_git_submodules_success(tmp_path):
69+
with patch("os.environ", {"TEST": "test"}):
70+
with patch("jupyterlab_git.git.execute") as mock_execute:
71+
# Given
72+
output = "output"
73+
mock_execute.return_value = maybe_future((0, output, "error"))
74+
75+
# When
76+
actual_response = await Git().clone(
77+
path=str(Path("/bin/test_curr_path")),
78+
repo_url="ghjkhjkl",
79+
submodules=True,
80+
)
81+
82+
# Then
83+
mock_execute.assert_called_once_with(
84+
["git", "clone", "--recurse-submodules", "ghjkhjkl"],
85+
cwd=str(Path("/bin") / "test_curr_path"),
86+
env={"TEST": "test", "GIT_TERMINAL_PROMPT": "0"},
87+
)
88+
assert {"code": 0, "message": output} == actual_response
89+
90+
6791
@pytest.mark.asyncio
6892
async def test_git_clone_failure_from_git():
6993
"""

src/widgets/GitCloneForm.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class GitCloneForm extends Widget {
1616
/**
1717
* Returns the input value.
1818
*/
19-
getValue(): { url: string; versioning: boolean } {
19+
getValue(): { url: string; versioning: boolean; submodules: boolean } {
2020
return {
2121
url: encodeURIComponent(
2222
(
@@ -25,7 +25,12 @@ export class GitCloneForm extends Widget {
2525
),
2626
versioning: Boolean(
2727
encodeURIComponent(
28-
(this.node.querySelector('#checkbox') as HTMLInputElement).checked
28+
(this.node.querySelector('#download') as HTMLInputElement).checked
29+
)
30+
),
31+
submodules: Boolean(
32+
encodeURIComponent(
33+
(this.node.querySelector('#submodules') as HTMLInputElement).checked
2934
)
3035
)
3136
};
@@ -38,33 +43,48 @@ export class GitCloneForm extends Widget {
3843
const inputLink = document.createElement('input');
3944
const linkText = document.createElement('span');
4045
const checkboxWrapper = document.createElement('div');
41-
const checkboxLabel = document.createElement('label');
42-
const checkbox = document.createElement('input');
46+
const subModulesLabel = document.createElement('label');
47+
const subModules = document.createElement('input');
48+
const downloadLabel = document.createElement('label');
49+
const download = document.createElement('input');
4350

4451
node.className = 'jp-CredentialsBox';
4552
inputWrapper.className = 'jp-RedirectForm';
4653
checkboxWrapper.className = 'jp-CredentialsBox-wrapper';
47-
checkboxLabel.className = 'jp-CredentialsBox-label-checkbox';
48-
checkbox.id = 'checkbox';
54+
subModulesLabel.className = 'jp-CredentialsBox-label-checkbox';
55+
downloadLabel.className = 'jp-CredentialsBox-label-checkbox';
56+
subModules.id = 'submodules';
57+
download.id = 'download';
4958
inputLink.id = 'input-link';
5059

5160
linkText.textContent = trans.__(
5261
'Enter the URI of the remote Git repository'
5362
);
5463
inputLink.placeholder = 'https://host.com/org/repo.git';
55-
checkboxLabel.textContent = trans.__('Download the repository');
56-
checkboxLabel.title = trans.__(
64+
65+
subModulesLabel.textContent = trans.__('Include submodules');
66+
subModulesLabel.title = trans.__(
67+
'If checked, the remote submodules in the repository will be cloned recursively'
68+
);
69+
subModules.setAttribute('type', 'checkbox');
70+
subModules.setAttribute('checked', 'checked');
71+
72+
downloadLabel.textContent = trans.__('Download the repository');
73+
downloadLabel.title = trans.__(
5774
'If checked, the remote repository default branch will be downloaded instead of cloned'
5875
);
59-
checkbox.setAttribute('type', 'checkbox');
76+
download.setAttribute('type', 'checkbox');
6077

6178
inputLinkLabel.appendChild(linkText);
6279
inputLinkLabel.appendChild(inputLink);
6380

6481
inputWrapper.append(inputLinkLabel);
6582

66-
checkboxLabel.prepend(checkbox);
67-
checkboxWrapper.appendChild(checkboxLabel);
83+
subModulesLabel.prepend(subModules);
84+
checkboxWrapper.appendChild(subModulesLabel);
85+
86+
downloadLabel.prepend(download);
87+
checkboxWrapper.appendChild(downloadLabel);
6888

6989
node.appendChild(inputWrapper);
7090
node.appendChild(checkboxWrapper);

0 commit comments

Comments
 (0)