From 40855aefa1cc364c4d12266591ac473937e648e8 Mon Sep 17 00:00:00 2001 From: dualc Date: Tue, 30 Sep 2025 16:49:30 +0800 Subject: [PATCH 1/3] fix checkout remote branch when branch name contains slash --- jupyterlab_git/git.py | 2 +- jupyterlab_git/tests/test_branch.py | 41 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/jupyterlab_git/git.py b/jupyterlab_git/git.py index a4ae5e9e8..ae6dc2651 100644 --- a/jupyterlab_git/git.py +++ b/jupyterlab_git/git.py @@ -1137,7 +1137,7 @@ async def checkout_branch(self, branchname, path): is_remote_branch = self._is_remote_branch(reference_name) if is_remote_branch: - local_branchname = branchname.split("/")[-1] + local_branchname = "/".join(branchname.split("/")[1:]) cmd = ["git", "checkout", "-B", local_branchname, branchname] else: cmd = ["git", "checkout", branchname] diff --git a/jupyterlab_git/tests/test_branch.py b/jupyterlab_git/tests/test_branch.py index c78d62e66..14acc1783 100644 --- a/jupyterlab_git/tests/test_branch.py +++ b/jupyterlab_git/tests/test_branch.py @@ -174,6 +174,47 @@ async def test_checkout_branch_remoteref_success(): assert {"code": rc, "message": stdout_message} == actual_response +@pytest.mark.asyncio +async def test_checkout_branch_remoteref_success_when_has_slash(): + branch = "origin/test-branch/test" + local_branch = "test-branch/test" + curr_path = str(Path("/bin/test_curr_path")) + stdout_message = "checkout output from git" + stderr_message = "" + rc = 0 + + with patch("jupyterlab_git.git.execute") as mock_execute: + with patch.object( + Git, + "_get_branch_reference", + return_value=maybe_future("refs/remotes/remote_branch"), + ) as mock__get_branch_reference: + # Given + mock_execute.return_value = maybe_future( + (rc, stdout_message, stderr_message) + ) + + # When + actual_response = await Git().checkout_branch( + branchname=branch, path=curr_path + ) + + # Then + mock__get_branch_reference.assert_has_calls([call(branch, curr_path)]) + + cmd = ["git", "checkout", "-B", local_branch, branch] + mock_execute.assert_called_once_with( + cmd, + cwd=str(Path("/bin") / "test_curr_path"), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ) + + assert {"code": rc, "message": stdout_message} == actual_response + @pytest.mark.asyncio async def test_checkout_branch_headsref_failure(): branch = "test-branch" From 81d90f1940d666cef2450adcffe2f5264461feb7 Mon Sep 17 00:00:00 2001 From: dualc Date: Tue, 30 Sep 2025 22:17:13 +0800 Subject: [PATCH 2/3] reformat code --- jupyterlab_git/tests/test_branch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterlab_git/tests/test_branch.py b/jupyterlab_git/tests/test_branch.py index 14acc1783..fbb048602 100644 --- a/jupyterlab_git/tests/test_branch.py +++ b/jupyterlab_git/tests/test_branch.py @@ -215,6 +215,7 @@ async def test_checkout_branch_remoteref_success_when_has_slash(): assert {"code": rc, "message": stdout_message} == actual_response + @pytest.mark.asyncio async def test_checkout_branch_headsref_failure(): branch = "test-branch" From b5954dc51993590ea6b599ba4d1ff6da8b85d762 Mon Sep 17 00:00:00 2001 From: dualc Date: Tue, 11 Nov 2025 09:49:07 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BC=98=E5=8C=96git=20status=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jupyterlab_git/git.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/jupyterlab_git/git.py b/jupyterlab_git/git.py index ae6dc2651..d0b7369b4 100644 --- a/jupyterlab_git/git.py +++ b/jupyterlab_git/git.py @@ -486,6 +486,13 @@ def remove_cell_ids(nb): return {"base": prev_nb, "diff": thediff} + async def _is_first_commit(self, path: str) -> bool: + """Return True if repo has no commits yet.""" + code, _, _ = await self.__execute( + ["git", "rev-parse", "--verify", "HEAD"], cwd=path + ) + return code != 0 + async def status(self, path: str) -> dict: """ Execute git status command & return the result. @@ -501,14 +508,19 @@ async def status(self, path: str) -> dict: } # Add attribute `is_binary` - command = [ # Compare stage to an empty tree see `_is_binary` - "git", - "diff", - "--numstat", - "-z", - "--cached", - "4b825dc642cb6eb9a060e54bf8d69288fbee4904", - ] + first_commit = await self._is_first_commit(path) + if first_commit: + # only first commit has to compare to an empty tree + command = [ # Compare stage to an empty tree see `_is_binary` + "git", + "diff", + "--numstat", + "-z", + "--cached", + "4b825dc642cb6eb9a060e54bf8d69288fbee4904", + ] + else: + command = ["git", "diff", "--numstat", "-z", "--cached"] text_code, text_output, _ = await self.__execute(command, cwd=path) are_binary = dict()