Jelmer Vernooij 1 месяц назад
Родитель
Сommit
a2c703455c
2 измененных файлов с 218 добавлено и 170 удалено
  1. 10 170
      dulwich/porcelain/__init__.py
  2. 208 0
      dulwich/porcelain/worktree.py

+ 10 - 170
dulwich/porcelain/__init__.py

@@ -428,6 +428,16 @@ from .submodule import (
     submodule_update,
 )
 from .tag import tag_create, tag_delete, tag_list, verify_tag
+from .worktree import (
+    worktree_add,
+    worktree_list,
+    worktree_lock,
+    worktree_move,
+    worktree_prune,
+    worktree_remove,
+    worktree_repair,
+    worktree_unlock,
+)
 
 # Module level tuple definition for status output
 GitStatus = namedtuple("GitStatus", "staged unstaged untracked")
@@ -7705,176 +7715,6 @@ def reflog_delete(
             drop_reflog_entry(f, index, rewrite=rewrite)
 
 
-def worktree_list(repo: RepoPath = ".") -> list[Any]:
-    """List all worktrees for a repository.
-
-    Args:
-        repo: Path to repository
-
-    Returns:
-        List of WorkTreeInfo objects
-    """
-    from ..worktree import list_worktrees
-
-    with open_repo_closing(repo) as r:
-        return list_worktrees(r)
-
-
-def worktree_add(
-    repo: RepoPath = ".",
-    path: str | os.PathLike[str] | None = None,
-    branch: str | bytes | None = None,
-    commit: str | bytes | None = None,
-    detach: bool = False,
-    force: bool = False,
-) -> str:
-    """Add a new worktree.
-
-    Args:
-        repo: Path to repository
-        path: Path for new worktree
-        branch: Branch to checkout (creates if doesn't exist)
-        commit: Specific commit to checkout
-        detach: Create with detached HEAD
-        force: Force creation even if branch is already checked out
-
-    Returns:
-        Path to the newly created worktree
-    """
-    from ..worktree import add_worktree
-
-    if path is None:
-        raise ValueError("Path is required for worktree add")
-
-    with open_repo_closing(repo) as r:
-        commit_bytes = commit.encode() if isinstance(commit, str) else commit
-        commit_id = ObjectID(commit_bytes) if commit_bytes is not None else None
-        wt_repo = add_worktree(
-            r, path, branch=branch, commit=commit_id, detach=detach, force=force
-        )
-        return wt_repo.path
-
-
-def worktree_remove(
-    repo: RepoPath = ".",
-    path: str | os.PathLike[str] | None = None,
-    force: bool = False,
-) -> None:
-    """Remove a worktree.
-
-    Args:
-        repo: Path to repository
-        path: Path to worktree to remove
-        force: Force removal even if there are local changes
-    """
-    from ..worktree import remove_worktree
-
-    if path is None:
-        raise ValueError("Path is required for worktree remove")
-
-    with open_repo_closing(repo) as r:
-        remove_worktree(r, path, force=force)
-
-
-def worktree_prune(
-    repo: RepoPath = ".", dry_run: bool = False, expire: int | None = None
-) -> list[str]:
-    """Prune worktree administrative files.
-
-    Args:
-        repo: Path to repository
-        dry_run: Only show what would be removed
-        expire: Only prune worktrees older than this many seconds
-
-    Returns:
-        List of pruned worktree names
-    """
-    from ..worktree import prune_worktrees
-
-    with open_repo_closing(repo) as r:
-        return prune_worktrees(r, expire=expire, dry_run=dry_run)
-
-
-def worktree_lock(
-    repo: RepoPath = ".",
-    path: str | os.PathLike[str] | None = None,
-    reason: str | None = None,
-) -> None:
-    """Lock a worktree to prevent it from being pruned.
-
-    Args:
-        repo: Path to repository
-        path: Path to worktree to lock
-        reason: Optional reason for locking
-    """
-    from ..worktree import lock_worktree
-
-    if path is None:
-        raise ValueError("Path is required for worktree lock")
-
-    with open_repo_closing(repo) as r:
-        lock_worktree(r, path, reason=reason)
-
-
-def worktree_unlock(
-    repo: RepoPath = ".", path: str | os.PathLike[str] | None = None
-) -> None:
-    """Unlock a worktree.
-
-    Args:
-        repo: Path to repository
-        path: Path to worktree to unlock
-    """
-    from ..worktree import unlock_worktree
-
-    if path is None:
-        raise ValueError("Path is required for worktree unlock")
-
-    with open_repo_closing(repo) as r:
-        unlock_worktree(r, path)
-
-
-def worktree_move(
-    repo: RepoPath = ".",
-    old_path: str | os.PathLike[str] | None = None,
-    new_path: str | os.PathLike[str] | None = None,
-) -> None:
-    """Move a worktree to a new location.
-
-    Args:
-        repo: Path to repository
-        old_path: Current path of worktree
-        new_path: New path for worktree
-    """
-    from ..worktree import move_worktree
-
-    if old_path is None or new_path is None:
-        raise ValueError("Both old_path and new_path are required for worktree move")
-
-    with open_repo_closing(repo) as r:
-        move_worktree(r, old_path, new_path)
-
-
-def worktree_repair(
-    repo: RepoPath = ".",
-    paths: list[str | os.PathLike[str]] | None = None,
-) -> list[str]:
-    """Repair worktree administrative files.
-
-    Args:
-        repo: Path to repository
-        paths: Optional list of worktree paths to repair. If None, repairs
-               connections from the main repository to all linked worktrees.
-
-    Returns:
-        List of repaired worktree paths
-    """
-    from ..worktree import repair_worktree
-
-    with open_repo_closing(repo) as r:
-        return repair_worktree(r, paths=paths)
-
-
 def merge_base(
     repo: RepoPath = ".",
     committishes: Sequence[str | bytes] | None = None,

+ 208 - 0
dulwich/porcelain/worktree.py

@@ -0,0 +1,208 @@
+# worktree.py -- Porcelain-like interface for Git worktrees
+# Copyright (C) 2013 Jelmer Vernooij <jelmer@jelmer.uk>
+#
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
+# General Public License as published by the Free Software Foundation; version 2.0
+# or (at your option) any later version. You can redistribute it and/or
+# modify it under the terms of either of these two licenses.
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# You should have received a copy of the licenses; if not, see
+# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
+# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
+# License, Version 2.0.
+#
+
+"""Porcelain-like interface for Git worktrees."""
+
+import os
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from ..worktree import WorkTreeInfo
+    from . import RepoPath
+
+
+def worktree_list(repo: "RepoPath" = ".") -> list["WorkTreeInfo"]:
+    """List all worktrees for a repository.
+
+    Args:
+        repo: Path to repository
+
+    Returns:
+        List of WorkTreeInfo objects
+    """
+    from ..worktree import list_worktrees
+    from . import open_repo_closing
+
+    with open_repo_closing(repo) as r:
+        return list_worktrees(r)
+
+
+def worktree_add(
+    repo: "RepoPath" = ".",
+    path: str | os.PathLike[str] | None = None,
+    branch: str | bytes | None = None,
+    commit: str | bytes | None = None,
+    detach: bool = False,
+    force: bool = False,
+) -> str:
+    """Add a new worktree.
+
+    Args:
+        repo: Path to repository
+        path: Path for new worktree
+        branch: Branch to checkout (creates if doesn't exist)
+        commit: Specific commit to checkout
+        detach: Create with detached HEAD
+        force: Force creation even if branch is already checked out
+
+    Returns:
+        Path to the newly created worktree
+    """
+    from ..objects import ObjectID
+    from ..worktree import add_worktree
+    from . import open_repo_closing
+
+    if path is None:
+        raise ValueError("Path is required for worktree add")
+
+    with open_repo_closing(repo) as r:
+        commit_bytes = commit.encode() if isinstance(commit, str) else commit
+        commit_id = ObjectID(commit_bytes) if commit_bytes is not None else None
+        wt_repo = add_worktree(
+            r, path, branch=branch, commit=commit_id, detach=detach, force=force
+        )
+        return wt_repo.path
+
+
+def worktree_remove(
+    repo: "RepoPath" = ".",
+    path: str | os.PathLike[str] | None = None,
+    force: bool = False,
+) -> None:
+    """Remove a worktree.
+
+    Args:
+        repo: Path to repository
+        path: Path to worktree to remove
+        force: Force removal even if there are local changes
+    """
+    from ..worktree import remove_worktree
+    from . import open_repo_closing
+
+    if path is None:
+        raise ValueError("Path is required for worktree remove")
+
+    with open_repo_closing(repo) as r:
+        remove_worktree(r, path, force=force)
+
+
+def worktree_prune(
+    repo: "RepoPath" = ".", dry_run: bool = False, expire: int | None = None
+) -> list[str]:
+    """Prune worktree administrative files.
+
+    Args:
+        repo: Path to repository
+        dry_run: Only show what would be removed
+        expire: Only prune worktrees older than this many seconds
+
+    Returns:
+        List of pruned worktree names
+    """
+    from ..worktree import prune_worktrees
+    from . import open_repo_closing
+
+    with open_repo_closing(repo) as r:
+        return prune_worktrees(r, expire=expire, dry_run=dry_run)
+
+
+def worktree_lock(
+    repo: "RepoPath" = ".",
+    path: str | os.PathLike[str] | None = None,
+    reason: str | None = None,
+) -> None:
+    """Lock a worktree to prevent it from being pruned.
+
+    Args:
+        repo: Path to repository
+        path: Path to worktree to lock
+        reason: Optional reason for locking
+    """
+    from ..worktree import lock_worktree
+    from . import open_repo_closing
+
+    if path is None:
+        raise ValueError("Path is required for worktree lock")
+
+    with open_repo_closing(repo) as r:
+        lock_worktree(r, path, reason=reason)
+
+
+def worktree_unlock(
+    repo: "RepoPath" = ".", path: str | os.PathLike[str] | None = None
+) -> None:
+    """Unlock a worktree.
+
+    Args:
+        repo: Path to repository
+        path: Path to worktree to unlock
+    """
+    from ..worktree import unlock_worktree
+    from . import open_repo_closing
+
+    if path is None:
+        raise ValueError("Path is required for worktree unlock")
+
+    with open_repo_closing(repo) as r:
+        unlock_worktree(r, path)
+
+
+def worktree_move(
+    repo: "RepoPath" = ".",
+    old_path: str | os.PathLike[str] | None = None,
+    new_path: str | os.PathLike[str] | None = None,
+) -> None:
+    """Move a worktree to a new location.
+
+    Args:
+        repo: Path to repository
+        old_path: Current path of worktree
+        new_path: New path for worktree
+    """
+    from ..worktree import move_worktree
+    from . import open_repo_closing
+
+    if old_path is None or new_path is None:
+        raise ValueError("Both old_path and new_path are required for worktree move")
+
+    with open_repo_closing(repo) as r:
+        move_worktree(r, old_path, new_path)
+
+
+def worktree_repair(
+    repo: "RepoPath" = ".",
+    paths: list[str | os.PathLike[str]] | None = None,
+) -> list[str]:
+    """Repair worktree administrative files.
+
+    Args:
+        repo: Path to repository
+        paths: Optional list of worktree paths to repair. If None, repairs
+               connections from the main repository to all linked worktrees.
+
+    Returns:
+        List of repaired worktree paths
+    """
+    from ..worktree import repair_worktree
+    from . import open_repo_closing
+
+    with open_repo_closing(repo) as r:
+        return repair_worktree(r, paths=paths)