diff --git a/jupyterlab_git/git.py b/jupyterlab_git/git.py index a4ae5e9e..ae8f0a58 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,20 @@ 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() diff --git a/jupyterlab_git/tests/test_status.py b/jupyterlab_git/tests/test_status.py index e531b515..d28ad7f0 100644 --- a/jupyterlab_git/tests/test_status.py +++ b/jupyterlab_git/tests/test_status.py @@ -353,9 +353,20 @@ async def test_status(tmp_path, output, diff_output, expected): repository = tmp_path / "test_curr_path" (repository / ".git" / "rebase-merge").mkdir(parents=True) + # Determine if this is a first commit scenario + is_first_commit = expected.get("branch") == "(initial)" + + # Build mock_execute.side_effect list + # First: git status call + # Second: git rev-parse --verify HEAD (for _is_first_commit) + # Third: git diff call (with or without empty tree SHA based on is_first_commit) + # Then: state detection calls (cherry pick, merge, rebase) mock_execute.side_effect = [ - maybe_future((0, "\x00".join(output) + "\x00", "")), - maybe_future((0, "\x00".join(diff_output) + "\x00", "")), + maybe_future((0, "\x00".join(output) + "\x00", "")), # git status + maybe_future( + (128 if is_first_commit else 0, "", "") + ), # git rev-parse --verify HEAD + maybe_future((0, "\x00".join(diff_output) + "\x00", "")), # git diff maybe_future((0 if expected["state"] == 4 else 128, "", "cherry pick")), maybe_future((0 if expected["state"] == 2 else 128, "", "merge")), maybe_future( @@ -381,50 +392,7 @@ async def test_status(tmp_path, output, diff_output, expected): is_binary=False, ), call( - [ - "git", - "diff", - "--numstat", - "-z", - "--cached", - "4b825dc642cb6eb9a060e54bf8d69288fbee4904", - ], - cwd=str(repository), - timeout=20, - env=None, - username=None, - password=None, - is_binary=False, - ), - call( - ["git", "show", "--quiet", "CHERRY_PICK_HEAD"], - cwd=str(repository), - timeout=20, - env=None, - username=None, - password=None, - is_binary=False, - ), - call( - ["git", "show", "--quiet", "MERGE_HEAD"], - cwd=str(repository), - timeout=20, - env=None, - username=None, - password=None, - is_binary=False, - ), - call( - ["git", "rev-parse", "--git-path", "rebase-merge"], - cwd=str(repository), - timeout=20, - env=None, - username=None, - password=None, - is_binary=False, - ), - call( - ["git", "rev-parse", "--git-path", "rebase-apply"], + ["git", "rev-parse", "--verify", "HEAD"], cwd=str(repository), timeout=20, env=None, @@ -434,6 +402,87 @@ async def test_status(tmp_path, output, diff_output, expected): ), ] + # Add diff command based on whether it's first commit + if is_first_commit: + expected_calls.append( + call( + [ + "git", + "diff", + "--numstat", + "-z", + "--cached", + "4b825dc642cb6eb9a060e54bf8d69288fbee4904", + ], + cwd=str(repository), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ) + ) + else: + expected_calls.append( + call( + [ + "git", + "diff", + "--numstat", + "-z", + "--cached", + ], + cwd=str(repository), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ) + ) + + # Add state detection calls + expected_calls.extend( + [ + call( + ["git", "show", "--quiet", "CHERRY_PICK_HEAD"], + cwd=str(repository), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ), + call( + ["git", "show", "--quiet", "MERGE_HEAD"], + cwd=str(repository), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ), + call( + ["git", "rev-parse", "--git-path", "rebase-merge"], + cwd=str(repository), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ), + call( + ["git", "rev-parse", "--git-path", "rebase-apply"], + cwd=str(repository), + timeout=20, + env=None, + username=None, + password=None, + is_binary=False, + ), + ] + ) + if expected["state"] == 4: expected_calls = expected_calls[:-3] elif expected["state"] == 2: