Skip to content

Commit

Permalink
Implement ui to support multiple remotes (#1146)
Browse files Browse the repository at this point in the history
* implement name-url pair for adding remote repo

* implement name-url pair for adding remote repo

* Style Add Remote form

* show existing remote on add remote form

* Implement backend api to show remote url info

* Refactor remote_show function to handle verbose case

* Implement remote dialog box using React

* Style remote dialog

* Add remove remote button

* Implement backend to remove remote

* Document codes

* Show push remote url only

* Implement pushing options for multiple remotes

* Change GitRemoteDetailsShowHandler successful code

Co-authored-by: Frédéric Collonval <[email protected]>

* Attempting to implement the DELETE method for removing a remote

* style existing remote list with the grid api

* Fix git remote remove route bug

* Implement advanced push dialog box

* Show remote url in advanced push dialog and increase text font size

* Move dialog action buttons to just below input fields and display message when loading remotes

* Display loading message when getting remote information and handle no remote case

* Add tests for remote_show and remote_remove

* Remove cancel button from add remote dialog

* Change command gitAddRemote to gitManageRemote
Disable arguments for the command

* Rename files to reflect command name 'ManageRemote'

* Refactor manageRemote command to let the dialog handle the adding and removing of remtoes

* Comment out tests for addRemote command

* Remove test for git:add-remote command

* Add tests for component 'ManageRemoteDialogue'

Co-authored-by: Frédéric Collonval <[email protected]>
  • Loading branch information
BoscoCHW and fcollonval authored Aug 11, 2022
1 parent 6854bb4 commit 2fc7423
Show file tree
Hide file tree
Showing 14 changed files with 898 additions and 114 deletions.
40 changes: 35 additions & 5 deletions jupyterlab_git/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -1494,24 +1494,54 @@ async def remote_add(self, path, url, name=DEFAULT_REMOTE_NAME):

return response

async def remote_show(self, path):
async def remote_show(self, path, verbose=False):
"""Handle call to `git remote show` command.
Args:
path (str): Git repository path
verbose (bool): true if details are needed, otherwise, false
Returns:
List[str]: Known remotes
if not verbose: List[str]: Known remotes
if verbose: List[ { name: str, url: str } ]: Known remotes
"""
command = ["git", "remote", "show"]
command = ["git", "remote"]
if verbose:
command.extend(["-v", "show"])
else:
command.append("show")

code, output, error = await execute(command, cwd=path)
response = {"code": code, "command": " ".join(command)}

if code == 0:
response["remotes"] = [r.strip() for r in output.splitlines()]
if verbose:
response["remotes"] = [
{"name": r.split("\t")[0], "url": r.split("\t")[1][:-7]}
for r in output.splitlines()
if "(push)" in r
]
else:
response["remotes"] = [r.strip() for r in output.splitlines()]
else:
response["message"] = error

return response

async def remote_remove(self, path, name):
"""Handle call to `git remote remove <name>` command.
Args:
path (str): Git repository path
name (str): Remote name
"""
command = ["git", "remote", "remove", name]

code, _, error = await execute(command, cwd=path)
response = {"code": code, "command": " ".join(command)}

if code != 0:
response["message"] = error

return response

async def ensure_gitignore(self, path):
"""Handle call to ensure .gitignore file exists and the
next append will be on a new line (this means an empty file
Expand Down
57 changes: 50 additions & 7 deletions jupyterlab_git/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,37 @@ async def post(self, path: str = ""):
self.finish(json.dumps(output))


class GitRemoteDetailsShowHandler(GitHandler):
"""Handler for 'git remote -v'."""

@tornado.web.authenticated
async def get(self, path: str = ""):
"""GET request handler to retrieve existing remotes."""
local_path = self.url2localpath(path)
output = await self.git.remote_show(local_path, verbose=True)
if output["code"] == 0:
self.set_status(200)
else:
self.set_status(500)
self.finish(json.dumps(output))


class GitRemoteRemoveHandler(GitHandler):
"""Handler for 'git remote remove <name>'."""

@tornado.web.authenticated
async def delete(self, path: str = "", name: str = ""):
"""DELETE request handler to remove a remote."""
local_path = self.url2localpath(path)

output = await self.git.remote_remove(local_path, name)
if output["code"] == 0:
self.set_status(204)
else:
self.set_status(500)
self.finish(json.dumps(output))


class GitResetHandler(GitHandler):
"""
Handler for 'git reset <filename>'.
Expand Down Expand Up @@ -871,6 +902,7 @@ def setup_handlers(web_app):
("/push", GitPushHandler),
("/remote/add", GitRemoteAddHandler),
("/remote/fetch", GitFetchHandler),
("/remote/show", GitRemoteDetailsShowHandler),
("/reset", GitResetHandler),
("/reset_to_commit", GitResetToCommitHandler),
("/show_prefix", GitShowPrefixHandler),
Expand All @@ -890,12 +922,23 @@ def setup_handlers(web_app):

# add the baseurl to our paths
base_url = web_app.settings["base_url"]
git_handlers = [
(url_path_join(base_url, NAMESPACE + path_regex + endpoint), handler)
for endpoint, handler in handlers_with_path
] + [
(url_path_join(base_url, NAMESPACE + endpoint), handler)
for endpoint, handler in handlers
]
git_handlers = (
[
(url_path_join(base_url, NAMESPACE + path_regex + endpoint), handler)
for endpoint, handler in handlers_with_path
]
+ [
(url_path_join(base_url, NAMESPACE + endpoint), handler)
for endpoint, handler in handlers
]
+ [
(
url_path_join(
base_url, NAMESPACE + path_regex + r"/remote/(?P<name>\w+)"
),
GitRemoteRemoveHandler,
)
]
)

web_app.add_handlers(".*", git_handlers)
82 changes: 80 additions & 2 deletions jupyterlab_git/tests/test_remote.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import json
from unittest.mock import patch

import os
import pytest
import tornado

from jupyterlab_git.git import Git
from jupyterlab_git.handlers import NAMESPACE

from .testutils import assert_http_error, maybe_future


Expand Down Expand Up @@ -101,3 +101,81 @@ async def test_git_add_remote_failure(mock_execute, jp_fetch, jp_root_dir):
mock_execute.assert_called_once_with(
["git", "remote", "add", "origin", url], cwd=str(local_path)
)


@patch("jupyterlab_git.git.execute")
async def test_git_remote_show(mock_execute, jp_root_dir):
# Given
local_path = jp_root_dir / "test_path"
mock_execute.return_value = maybe_future(
(0, os.linesep.join(["origin", "test"]), "")
)

# When
output = await Git().remote_show(str(local_path), False)

# Then
command = ["git", "remote", "show"]
mock_execute.assert_called_once_with(command, cwd=str(local_path))
assert output == {
"code": 0,
"command": " ".join(command),
"remotes": ["origin", "test"],
}


@patch("jupyterlab_git.git.execute")
async def test_git_remote_show_verbose(mock_execute, jp_fetch, jp_root_dir):
# Given
local_path = jp_root_dir / "test_path"
url = "http://github.com/myid/myrepository.git"
process_output = os.linesep.join(
[f"origin\t{url} (fetch)", f"origin\t{url} (push)"]
)
mock_execute.return_value = maybe_future((0, process_output, ""))

# When
response = await jp_fetch(
NAMESPACE,
local_path.name,
"remote",
"show",
method="GET",
)

# Then
command = ["git", "remote", "-v", "show"]
mock_execute.assert_called_once_with(command, cwd=str(local_path))

assert response.code == 200
payload = json.loads(response.body)
assert payload == {
"code": 0,
"command": " ".join(command),
"remotes": [
{"name": "origin", "url": "http://github.com/myid/myrepository.git"}
],
}


@patch("jupyterlab_git.git.execute")
async def test_git_remote_remove(mock_execute, jp_fetch, jp_root_dir):
# Given
local_path = jp_root_dir / "test_path"
mock_execute.return_value = maybe_future((0, "", ""))

# When
name = "origin"
response = await jp_fetch(
NAMESPACE,
local_path.name,
"remote",
name,
method="DELETE",
)

# Then
command = ["git", "remote", "remove", name]
mock_execute.assert_called_once_with(command, cwd=str(local_path))

assert response.code == 204
4 changes: 2 additions & 2 deletions schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
},
{
"command": "git:push",
"args": { "force": true }
"args": { "advanced": true }
},
{
"command": "git:pull"
Expand All @@ -113,7 +113,7 @@
"command": "git:reset-to-remote"
},
{
"command": "git:add-remote"
"command": "git:manage-remote"
},
{
"command": "git:terminal-command"
Expand Down
Loading

0 comments on commit 2fc7423

Please sign in to comment.