Skip to content

Commit 79794fe

Browse files
authored
Merge pull request #166 from mikegehard/feature/git-checkout-command
Allow to check out branches
2 parents 854f48e + 98e78c3 commit 79794fe

File tree

5 files changed

+151
-3
lines changed

5 files changed

+151
-3
lines changed

src/git/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ Please note that mcp-server-git is currently in early development. The functiona
6767
- `branch_name` (string): Name of the new branch
6868
- `start_point` (string, optional): Starting point for the new branch
6969
- Returns: Confirmation of branch creation
70+
8. `git_checkout`
71+
- Switches branches
72+
- Inputs:
73+
- `repo_path` (string): Path to Git repository
74+
- `branch_name` (string): Name of branch to checkout
75+
- Returns: Confirmation of branch switch
7076

7177
## Installation
7278

src/git/pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@ requires = ["hatchling"]
3030
build-backend = "hatchling.build"
3131

3232
[tool.uv]
33-
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.3"]
33+
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.3", "pytest>=8.0.0"]
34+
35+
[tool.pytest.ini_options]
36+
testpaths = ["tests"]
37+
python_files = "test_*.py"
38+
python_classes = "Test*"
39+
python_functions = "test_*"

src/git/src/mcp_server_git/server.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class GitCreateBranch(BaseModel):
4848
branch_name: str
4949
base_branch: str | None = None
5050

51+
class GitCheckout(BaseModel):
52+
repo_path: str
53+
branch_name: str
54+
5155
class GitTools(str, Enum):
5256
STATUS = "git_status"
5357
DIFF_UNSTAGED = "git_diff_unstaged"
@@ -58,6 +62,7 @@ class GitTools(str, Enum):
5862
RESET = "git_reset"
5963
LOG = "git_log"
6064
CREATE_BRANCH = "git_create_branch"
65+
CHECKOUT = "git_checkout"
6166

6267
def git_status(repo: git.Repo) -> str:
6368
return repo.git.status()
@@ -104,6 +109,10 @@ def git_create_branch(repo: git.Repo, branch_name: str, base_branch: str | None
104109
repo.create_head(branch_name, base)
105110
return f"Created branch '{branch_name}' from '{base.name}'"
106111

112+
def git_checkout(repo: git.Repo, branch_name: str) -> str:
113+
repo.git.checkout(branch_name)
114+
return f"Switched to branch '{branch_name}'"
115+
107116
async def serve(repository: Path | None) -> None:
108117
logger = logging.getLogger(__name__)
109118

@@ -165,6 +174,11 @@ async def list_tools() -> list[Tool]:
165174
description="Creates a new branch from an optional base branch",
166175
inputSchema=GitCreateBranch.schema(),
167176
),
177+
Tool(
178+
name=GitTools.CHECKOUT,
179+
description="Switches branches",
180+
inputSchema=GitCheckout.schema(),
181+
),
168182
]
169183

170184
async def list_repos() -> Sequence[str]:
@@ -269,6 +283,13 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
269283
text=result
270284
)]
271285

286+
case GitTools.CHECKOUT:
287+
result = git_checkout(repo, arguments["branch_name"])
288+
return [TextContent(
289+
type="text",
290+
text=result
291+
)]
292+
272293
case _:
273294
raise ValueError(f"Unknown tool: {name}")
274295

src/git/tests/test_server.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
from pathlib import Path
3+
import git
4+
from mcp_server_git.server import git_checkout
5+
import shutil
6+
7+
@pytest.fixture
8+
def test_repository(tmp_path: Path):
9+
repo_path = tmp_path / "temp_test_repo"
10+
test_repo = git.Repo.init(repo_path)
11+
12+
Path(repo_path / "test.txt").write_text("test")
13+
test_repo.index.add(["test.txt"])
14+
test_repo.index.commit("initial commit")
15+
16+
yield test_repo
17+
18+
shutil.rmtree(repo_path)
19+
20+
def test_git_checkout_existing_branch(test_repository):
21+
test_repository.git.branch("test-branch")
22+
result = git_checkout(test_repository, "test-branch")
23+
24+
assert "Switched to branch 'test-branch'" in result
25+
assert test_repository.active_branch.name == "test-branch"
26+
27+
def test_git_checkout_nonexistent_branch(test_repository):
28+
29+
with pytest.raises(git.GitCommandError):
30+
git_checkout(test_repository, "nonexistent-branch")

src/git/uv.lock

Lines changed: 87 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)