|
16 | 16 | from pathlib import Path
|
17 | 17 | from typing import TYPE_CHECKING, Generator, Optional, Set, Type
|
18 | 18 |
|
19 |
| -from ._util import GIT_EXE, HG_EXE, PIJUL_EXE, StrPath, execute_command |
| 19 | +from ._util import GIT_EXE, HG_EXE, PIJUL_EXE, SL_EXE, StrPath, execute_command |
20 | 20 |
|
21 | 21 | if TYPE_CHECKING:
|
22 | 22 | from .project import Project
|
@@ -249,6 +249,74 @@ def find_root(cls, cwd: Optional[StrPath] = None) -> Optional[Path]:
|
249 | 249 | return None
|
250 | 250 |
|
251 | 251 |
|
| 252 | +class VSStrategySapling(VCSStrategy): |
| 253 | + EXE = SL_EXE |
| 254 | + |
| 255 | + def __init__(self, project: Project): |
| 256 | + super().__init__(project) |
| 257 | + if not self.EXE: |
| 258 | + raise FileNotFoundError("Could not find binary for Sapling") |
| 259 | + self._all_ignored_files = self._find_all_ignored_files() |
| 260 | + |
| 261 | + def _find_all_ignored_files(self) -> Set[Path]: |
| 262 | + """Return a set of all files ignored by sapling. If a whole directory |
| 263 | + is ignored, don't return all files inside of it. |
| 264 | + """ |
| 265 | + command = [ |
| 266 | + str(self.EXE), |
| 267 | + "status", |
| 268 | + "--ignored", |
| 269 | + # terse is marked 'experimental' in the hg help but is documented |
| 270 | + # in the man page. It collapses the output of a dir containing only |
| 271 | + # ignored files to the ignored name like the git command does. |
| 272 | + # TODO: Re-enable this flag in the future. |
| 273 | + # "--terse=i", |
| 274 | + "--no-status", |
| 275 | + "--print0", |
| 276 | + ] |
| 277 | + result = execute_command(command, _LOGGER, cwd=self.project.root) |
| 278 | + all_files = result.stdout.decode("utf-8").split("\0") |
| 279 | + return {Path(file_) for file_ in all_files} |
| 280 | + |
| 281 | + def is_ignored(self, path: StrPath) -> bool: |
| 282 | + path = self.project.relative_from_root(path) |
| 283 | + return path in self._all_ignored_files |
| 284 | + |
| 285 | + def is_submodule(self, path: StrPath) -> bool: |
| 286 | + # TODO: Implement me. |
| 287 | + return False |
| 288 | + |
| 289 | + @classmethod |
| 290 | + def in_repo(cls, directory: StrPath) -> bool: |
| 291 | + if directory is None: |
| 292 | + directory = Path.cwd() |
| 293 | + |
| 294 | + if not Path(directory).is_dir(): |
| 295 | + raise NotADirectoryError() |
| 296 | + |
| 297 | + command = [str(cls.EXE), "root"] |
| 298 | + result = execute_command(command, _LOGGER, cwd=directory) |
| 299 | + |
| 300 | + return not result.returncode |
| 301 | + |
| 302 | + @classmethod |
| 303 | + def find_root(cls, cwd: Optional[StrPath] = None) -> Optional[Path]: |
| 304 | + if cwd is None: |
| 305 | + cwd = Path.cwd() |
| 306 | + |
| 307 | + if not Path(cwd).is_dir(): |
| 308 | + raise NotADirectoryError() |
| 309 | + |
| 310 | + command = [str(cls.EXE), "root"] |
| 311 | + result = execute_command(command, _LOGGER, cwd=cwd) |
| 312 | + |
| 313 | + if not result.returncode: |
| 314 | + path = result.stdout.decode("utf-8")[:-1] |
| 315 | + return Path(os.path.relpath(path, cwd)) |
| 316 | + |
| 317 | + return None |
| 318 | + |
| 319 | + |
252 | 320 | class VCSStrategyPijul(VCSStrategy):
|
253 | 321 | """Strategy that is used for Pijul."""
|
254 | 322 |
|
|
0 commit comments