gitbetter.git

  1import shlex
  2import subprocess
  3from contextlib import contextmanager
  4from pathlib import Path
  5from urllib.parse import urlparse
  6
  7from pathier import Pathier, Pathish
  8
  9
 10class Git:
 11    def __init__(self, capture_stdout: bool = False):
 12        """If `capture_stdout` is `True`, all functions will return their generated `stdout` as a string.
 13        Otherwise, the functions return the call's exit code."""
 14        self.capture_stdout = capture_stdout
 15
 16    @property
 17    def capture_stdout(self) -> bool:
 18        """If `True`, member functions will return the generated `stdout` as a string,
 19        otherwise they return the command's exit code."""
 20        return self._capture_stdout
 21
 22    @capture_stdout.setter
 23    def capture_stdout(self, should_capture: bool):
 24        self._capture_stdout = should_capture
 25
 26    def _run(self, args: list[str]) -> str | int:
 27        if self._capture_stdout:
 28            return subprocess.run(args, stdout=subprocess.PIPE, text=True).stdout
 29        else:
 30            return subprocess.run(args).returncode
 31
 32    @contextmanager
 33    def capture_output(self):
 34        self.capture_stdout = True
 35        yield self
 36        self.capture_stdout = False
 37
 38    # Seat |===================================================Core===================================================|
 39
 40    def git(self, command: str) -> str | int:
 41        """Base function for executing git commands.
 42        Use this if another function doesn't meet your needs.
 43        >>> git {command}"""
 44        args = ["git"] + shlex.split(command)
 45        return self._run(args)
 46
 47    # Seat
 48
 49    def add(self, args: str = "") -> str | int:
 50        """>>> git add {args}"""
 51        return self.git(f"add {args}")
 52
 53    def am(self, args: str = "") -> str | int:
 54        """>>> git am {args}"""
 55        return self.git(f"am {args}")
 56
 57    def annotate(self, args: str = "") -> str | int:
 58        """>>> git annotate {args}"""
 59        return self.git(f"annotate {args}")
 60
 61    def archive(self, args: str = "") -> str | int:
 62        """>>> git archive {args}"""
 63        return self.git(f"archive {args}")
 64
 65    def bisect(self, args: str = "") -> str | int:
 66        """>>> git bisect {args}"""
 67        return self.git(f"bisect {args}")
 68
 69    def blame(self, args: str = "") -> str | int:
 70        """>>> git blame {args}"""
 71        return self.git(f"blame {args}")
 72
 73    def branch(self, args: str = "") -> str | int:
 74        """>>> git branch {args}"""
 75        return self.git(f"branch {args}")
 76
 77    def bugreport(self, args: str = "") -> str | int:
 78        """>>> git bugreport {args}"""
 79        return self.git(f"bugreport {args}")
 80
 81    def bundle(self, args: str = "") -> str | int:
 82        """>>> git bundle {args}"""
 83        return self.git(f"bundle {args}")
 84
 85    def checkout(self, args: str = "") -> str | int:
 86        """>>> git checkout {args}"""
 87        return self.git(f"checkout {args}")
 88
 89    def cherry_pick(self, args: str = "") -> str | int:
 90        """>>> git cherry-pick {args}"""
 91        return self.git(f"cherry-pick {args}")
 92
 93    def citool(self, args: str = "") -> str | int:
 94        """>>> git citool {args}"""
 95        return self.git(f"citool {args}")
 96
 97    def clean(self, args: str = "") -> str | int:
 98        """>>> git clean {args}"""
 99        return self.git(f"clean {args}")
100
101    def clone(self, args: str = "") -> str | int:
102        """>>> git clone {args}"""
103        return self.git(f"clone {args}")
104
105    def commit(self, args: str = "") -> str | int:
106        """>>> git commit {args}"""
107        return self.git(f"commit {args}")
108
109    def config(self, args: str = "") -> str | int:
110        """>>> git config {args}"""
111        return self.git(f"config {args}")
112
113    def count_objects(self, args: str = "") -> str | int:
114        """>>> git count-objects {args}"""
115        return self.git(f"count-objects {args}")
116
117    def describe(self, args: str = "") -> str | int:
118        """>>> git describe {args}"""
119        return self.git(f"describe {args}")
120
121    def diagnose(self, args: str = "") -> str | int:
122        """>>> git diagnose {args}"""
123        return self.git(f"diagnose {args}")
124
125    def diff(self, args: str = "") -> str | int:
126        """>>> git diff {args}"""
127        return self.git(f"diff {args}")
128
129    def difftool(self, args: str = "") -> str | int:
130        """>>> git difftool {args}"""
131        return self.git(f"difftool {args}")
132
133    def fast_export(self, args: str = "") -> str | int:
134        """>>> git fast-export {args}"""
135        return self.git(f"fast-export {args}")
136
137    def fast_import(self, args: str = "") -> str | int:
138        """>>> git fast-import {args}"""
139        return self.git(f"fast-import {args}")
140
141    def fetch(self, args: str = "") -> str | int:
142        """>>> git fetch {args}"""
143        return self.git(f"fetch {args}")
144
145    def filter_branch(self, args: str = "") -> str | int:
146        """>>> git filter-branch {args}"""
147        return self.git(f"filter-branch {args}")
148
149    def format_patch(self, args: str = "") -> str | int:
150        """>>> git format-patch {args}"""
151        return self.git(f"format-patch {args}")
152
153    def fsck(self, args: str = "") -> str | int:
154        """>>> git fsck {args}"""
155        return self.git(f"fsck {args}")
156
157    def gc(self, args: str = "") -> str | int:
158        """>>> git gc {args}"""
159        return self.git(f"gc {args}")
160
161    def gitk(self, args: str = "") -> str | int:
162        """>>> git gitk {args}"""
163        return self.git(f"gitk {args}")
164
165    def gitweb(self, args: str = "") -> str | int:
166        """>>> git gitweb {args}"""
167        return self.git(f"gitweb {args}")
168
169    def grep(self, args: str = "") -> str | int:
170        """>>> git grep {args}"""
171        return self.git(f"grep {args}")
172
173    def gui(self, args: str = "") -> str | int:
174        """>>> git gui {args}"""
175        return self.git(f"gui {args}")
176
177    def help(self, args: str = "") -> str | int:
178        """>>> git help {args}"""
179        return self.git(f"help {args}")
180
181    def init(self, args: str = "") -> str | int:
182        """>>> git init {args}"""
183        return self.git(f"init {args}")
184
185    def instaweb(self, args: str = "") -> str | int:
186        """>>> git instaweb {args}"""
187        return self.git(f"instaweb {args}")
188
189    def log(self, args: str = "") -> str | int:
190        """>>> git log {args}"""
191        return self.git(f"log {args}")
192
193    def maintenance(self, args: str = "") -> str | int:
194        """>>> git maintenance {args}"""
195        return self.git(f"maintenance {args}")
196
197    def merge(self, args: str = "") -> str | int:
198        """>>> git merge {args}"""
199        return self.git(f"merge {args}")
200
201    def merge_tree(self, args: str = "") -> str | int:
202        """>>> git merge-tree {args}"""
203        return self.git(f"merge-tree {args}")
204
205    def mergetool(self, args: str = "") -> str | int:
206        """>>> git mergetool {args}"""
207        return self.git(f"mergetool {args}")
208
209    def mv(self, args: str = "") -> str | int:
210        """>>> git mv {args}"""
211        return self.git(f"mv {args}")
212
213    def notes(self, args: str = "") -> str | int:
214        """>>> git notes {args}"""
215        return self.git(f"notes {args}")
216
217    def pack_refs(self, args: str = "") -> str | int:
218        """>>> git pack-refs {args}"""
219        return self.git(f"pack-refs {args}")
220
221    def prune(self, args: str = "") -> str | int:
222        """>>> git prune {args}"""
223        return self.git(f"prune {args}")
224
225    def pull(self, args: str = "") -> str | int:
226        """>>> git pull {args}"""
227        return self.git(f"pull {args}")
228
229    def push(self, args: str = "") -> str | int:
230        """>>> git push {args}"""
231        return self.git(f"push {args}")
232
233    def range_diff(self, args: str = "") -> str | int:
234        """>>> git range-diff {args}"""
235        return self.git(f"range-diff {args}")
236
237    def rebase(self, args: str = "") -> str | int:
238        """>>> git rebase {args}"""
239        return self.git(f"rebase {args}")
240
241    def reflog(self, args: str = "") -> str | int:
242        """>>> git reflog {args}"""
243        return self.git(f"reflog {args}")
244
245    def remote(self, args: str = "") -> str | int:
246        """>>> git remote {args}"""
247        return self.git(f"remote {args}")
248
249    def repack(self, args: str = "") -> str | int:
250        """>>> git repack {args}"""
251        return self.git(f"repack {args}")
252
253    def replace(self, args: str = "") -> str | int:
254        """>>> git replace {args}"""
255        return self.git(f"replace {args}")
256
257    def request_pull(self, args: str = "") -> str | int:
258        """>>> git request-pull {args}"""
259        return self.git(f"request-pull {args}")
260
261    def rerere(self, args: str = "") -> str | int:
262        """>>> git rerere {args}"""
263        return self.git(f"rerere {args}")
264
265    def reset(self, args: str = "") -> str | int:
266        """>>> git reset {args}"""
267        return self.git(f"reset {args}")
268
269    def restore(self, args: str = "") -> str | int:
270        """>>> git restore {args}"""
271        return self.git(f"restore {args}")
272
273    def revert(self, args: str = "") -> str | int:
274        """>>> git revert {args}"""
275        return self.git(f"revert {args}")
276
277    def rm(self, args: str = "") -> str | int:
278        """>>> git rm {args}"""
279        return self.git(f"rm {args}")
280
281    def scalar(self, args: str = "") -> str | int:
282        """>>> git scalar {args}"""
283        return self.git(f"scalar {args}")
284
285    def shortlog(self, args: str = "") -> str | int:
286        """>>> git shortlog {args}"""
287        return self.git(f"shortlog {args}")
288
289    def show(self, args: str = "") -> str | int:
290        """>>> git show {args}"""
291        return self.git(f"show {args}")
292
293    def show_branch(self, args: str = "") -> str | int:
294        """>>> git show-branch {args}"""
295        return self.git(f"show-branch {args}")
296
297    def sparse_checkout(self, args: str = "") -> str | int:
298        """>>> git sparse-checkout {args}"""
299        return self.git(f"sparse-checkout {args}")
300
301    def stash(self, args: str = "") -> str | int:
302        """>>> git stash {args}"""
303        return self.git(f"stash {args}")
304
305    def status(self, args: str = "") -> str | int:
306        """>>> git status {args}"""
307        return self.git(f"status {args}")
308
309    def submodule(self, args: str = "") -> str | int:
310        """>>> git submodule {args}"""
311        return self.git(f"submodule {args}")
312
313    def switch(self, args: str = "") -> str | int:
314        """>>> git switch {args}"""
315        return self.git(f"switch {args}")
316
317    def tag(self, args: str = "") -> str | int:
318        """>>> git tag {args}"""
319        return self.git(f"tag {args}")
320
321    def verify_commit(self, args: str = "") -> str | int:
322        """>>> git verify-commit {args}"""
323        return self.git(f"verify-commit {args}")
324
325    def verify_tag(self, args: str = "") -> str | int:
326        """>>> git verify-tag {args}"""
327        return self.git(f"verify-tag {args}")
328
329    def version(self, args: str = "") -> str | int:
330        """>>> git version {args}"""
331        return self.git(f"version {args}")
332
333    def whatchanged(self, args: str = "") -> str | int:
334        """>>> git whatchanged {args}"""
335        return self.git(f"whatchanged {args}")
336
337    def worktree(self, args: str = "") -> str | int:
338        """>>> git worktree {args}"""
339        return self.git(f"worktree {args}")
340
341    # Seat |=================================================Convenience=================================================|
342
343    @property
344    def current_branch(self) -> str:
345        """Returns the name of the currently active branch."""
346        capturing_output = self.capture_stdout
347        current_branch = ""
348        with self.capture_output():
349            branches = self.branch().splitlines()  # type: ignore
350            for branch in branches:
351                if branch.startswith("*"):
352                    current_branch = branch[2:]
353                    break
354        self.capture_stdout = capturing_output
355        return current_branch
356
357    @property
358    def origin_url(self) -> str | int:
359        """The remote origin url for this repo
360        >>> git remote get-url origin"""
361        return self.remote("get-url origin")
362
363    def add_all(self) -> str | int:
364        """Stage all modified and untracked files.
365        >>> git add ."""
366        return self.add(".")
367
368    def add_files(self, files: list[Pathish]) -> str | int:
369        """Stage a list of files."""
370        args = " ".join([str(file).replace("\\", "/") for file in files])
371        return self.add(args)
372
373    def add_remote_url(self, url: str, name: str = "origin") -> str | int:
374        """Add remote url to repo.
375        >>> git remote add {name} {url}"""
376        return self.remote(f"add {name} {url}")
377
378    def amend(self, files: list[Pathish] | None = None) -> str | int:
379        """Stage and commit changes to the previous commit.
380
381        If `files` is `None`, all files will be staged.
382
383        >>> git add {files} or git add .
384        >>> git commit --amend --no-edit
385        """
386        return (self.add(files) if files else self.add_all()) + self.commit("--amend --no-edit")  # type: ignore
387
388    def commit_all(self, message: str) -> str | int:
389        """Stage and commit all files with `message`.
390        >>> git add .
391        >>> git commit -m \"{message}\" """
392        return self.add_all() + self.commit(f'-m "{message}"')  # type: ignore
393
394    def commit_files(self, files: list[Pathish], message: str) -> str | int:
395        """Stage and commit a list of files with commit message `message`.
396        >>> git add {files}
397        >>> git commit -m \"{message}\" """
398        return self.add_files(files) + self.commit(f'-m "{message}"')  # type: ignore
399
400    def create_new_branch(self, branch_name: str) -> str | int:
401        """Create and switch to a new branch named with `branch_name`.
402        >>> git checkout -b {branch_name} --track"""
403        return self.checkout(f"-b {branch_name} --track")
404
405    def delete_branch(self, branch_name: str, local_only: bool = True) -> str | int:
406        """Delete `branch_name` from repo.
407
408        #### :params:
409
410        `local_only`: Only delete the local copy of `branch`, otherwise also delete the remote branch on origin and remote-tracking branch.
411        >>> git branch --delete {branch_name}
412
413        Then if not `local_only`:
414        >>> git push origin --delete {branch_name}
415        """
416        output = self.branch(f"--delete {branch_name}")
417        if not local_only:
418            return output + self.push(f"origin --delete {branch_name}")  # type: ignore
419        return output
420
421    def initcommit(self, files: list[Pathish] | None = None) -> str | int:
422        """Stage and commit `files` with the message `Initial commit`.
423
424        If `files` is not given, all files will be added and committed.
425        >>> git add {files} or git add .
426        >>> git commit -m "Initial commit" """
427        return (self.add_files(files) if files else self.add_all()) + self.commit('-m "Initial commit"')  # type: ignore
428
429    def list_branches(self) -> str | int:
430        """>>> git branch -vva"""
431        return self.branch("-vva")
432
433    def loggy(self) -> str | int:
434        """>>> git log --oneline --name-only --abbrev-commit --graph"""
435        return self.log("--oneline --name-only --abbrev-commit --graph")
436
437    def new_repo(self) -> str | int:
438        """Initialize a new repo in current directory.
439        >>> git init -b main"""
440        return self.init("-b main")
441
442    def push_new_branch(self, branch: str) -> str | int:
443        """Push a new branch to origin with tracking.
444        >>> git push -u origin {branch}"""
445        return self.push(f"-u origin {branch}")
446
447    def switch_branch(self, branch_name: str) -> str | int:
448        """Switch to the branch specified by `branch_name`.
449        >>> git checkout {branch_name}"""
450        return self.checkout(branch_name)
451
452    def undo(self) -> str | int:
453        """Undo uncommitted changes.
454        >>> git checkout ."""
455        return self.checkout(".")
456
457    def ignore(self, patterns: list[str]):
458        """Add `patterns` to `.gitignore`."""
459        gitignore = Pathier(".gitignore")
460        if not gitignore.exists():
461            gitignore.touch()
462        ignores = gitignore.split()
463        ignores += [pattern for pattern in patterns if pattern not in ignores]
464        gitignore.join(ignores)
465
466    # Seat |===============================Requires GitHub CLI to be installed and configured===============================|
467
468    @property
469    def owner(self) -> str:
470        return self._owner_reponame().split("/")[0]
471
472    @property
473    def repo_name(self) -> str:
474        return self._owner_reponame().split("/")[1]
475
476    def _change_visibility(self, visibility: str) -> str | int:
477        return self._run(
478            [
479                "gh",
480                "repo",
481                "edit",
482                f"{self.owner}/{self.repo_name}",
483                "--visibility",
484                visibility,
485            ]
486        )
487
488    def _owner_reponame(self) -> str:
489        """Returns "owner/repo-name", assuming there's one remote origin url and it's for github."""
490        with self.capture_output():
491            return urlparse(self.origin_url().strip("\n")).path.strip("/")  # type: ignore
492
493    def create_remote(self, name: str, public: bool = False) -> str | int:
494        """Uses GitHub CLI (must be installed and configured) to create a remote GitHub repo.
495
496        #### :params:
497
498        `name`: The name for the repo.
499
500        `public`: Set to `True` to create the repo as public, otherwise it'll be created as private.
501        """
502        visibility = "--public" if public else "--private"
503        return self._run(["gh", "repo", "create", name, visibility])
504
505    def create_remote_from_cwd(self, public: bool = False) -> str | int:
506        """Use GitHub CLI (must be installed and configured) to create a remote GitHub repo from
507        the current working directory repo and add its url as this repo's remote origin.
508
509        #### :params:
510
511        `public`: Create the GitHub repo as a public repo, default is to create it as private.
512        """
513        visibility = "public" if public else "private"
514        return self._run(
515            ["gh", "repo", "create", "--source", ".", f"--{visibility}", "--push"]
516        )
517
518    def delete_remote(self) -> str | int:
519        """Uses GitHub CLI (must be isntalled and configured) to delete the remote for this repo."""
520        return self._run(
521            ["gh", "repo", "delete", f"{self.owner}/{self.repo_name}", "--yes"]
522        )
523
524    def make_private(self) -> str | int:
525        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to private."""
526        return self._change_visibility("private")
527
528    def make_public(self) -> str | int:
529        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to public."""
530        return self._change_visibility("public")
class Git:
 11class Git:
 12    def __init__(self, capture_stdout: bool = False):
 13        """If `capture_stdout` is `True`, all functions will return their generated `stdout` as a string.
 14        Otherwise, the functions return the call's exit code."""
 15        self.capture_stdout = capture_stdout
 16
 17    @property
 18    def capture_stdout(self) -> bool:
 19        """If `True`, member functions will return the generated `stdout` as a string,
 20        otherwise they return the command's exit code."""
 21        return self._capture_stdout
 22
 23    @capture_stdout.setter
 24    def capture_stdout(self, should_capture: bool):
 25        self._capture_stdout = should_capture
 26
 27    def _run(self, args: list[str]) -> str | int:
 28        if self._capture_stdout:
 29            return subprocess.run(args, stdout=subprocess.PIPE, text=True).stdout
 30        else:
 31            return subprocess.run(args).returncode
 32
 33    @contextmanager
 34    def capture_output(self):
 35        self.capture_stdout = True
 36        yield self
 37        self.capture_stdout = False
 38
 39    # Seat |===================================================Core===================================================|
 40
 41    def git(self, command: str) -> str | int:
 42        """Base function for executing git commands.
 43        Use this if another function doesn't meet your needs.
 44        >>> git {command}"""
 45        args = ["git"] + shlex.split(command)
 46        return self._run(args)
 47
 48    # Seat
 49
 50    def add(self, args: str = "") -> str | int:
 51        """>>> git add {args}"""
 52        return self.git(f"add {args}")
 53
 54    def am(self, args: str = "") -> str | int:
 55        """>>> git am {args}"""
 56        return self.git(f"am {args}")
 57
 58    def annotate(self, args: str = "") -> str | int:
 59        """>>> git annotate {args}"""
 60        return self.git(f"annotate {args}")
 61
 62    def archive(self, args: str = "") -> str | int:
 63        """>>> git archive {args}"""
 64        return self.git(f"archive {args}")
 65
 66    def bisect(self, args: str = "") -> str | int:
 67        """>>> git bisect {args}"""
 68        return self.git(f"bisect {args}")
 69
 70    def blame(self, args: str = "") -> str | int:
 71        """>>> git blame {args}"""
 72        return self.git(f"blame {args}")
 73
 74    def branch(self, args: str = "") -> str | int:
 75        """>>> git branch {args}"""
 76        return self.git(f"branch {args}")
 77
 78    def bugreport(self, args: str = "") -> str | int:
 79        """>>> git bugreport {args}"""
 80        return self.git(f"bugreport {args}")
 81
 82    def bundle(self, args: str = "") -> str | int:
 83        """>>> git bundle {args}"""
 84        return self.git(f"bundle {args}")
 85
 86    def checkout(self, args: str = "") -> str | int:
 87        """>>> git checkout {args}"""
 88        return self.git(f"checkout {args}")
 89
 90    def cherry_pick(self, args: str = "") -> str | int:
 91        """>>> git cherry-pick {args}"""
 92        return self.git(f"cherry-pick {args}")
 93
 94    def citool(self, args: str = "") -> str | int:
 95        """>>> git citool {args}"""
 96        return self.git(f"citool {args}")
 97
 98    def clean(self, args: str = "") -> str | int:
 99        """>>> git clean {args}"""
100        return self.git(f"clean {args}")
101
102    def clone(self, args: str = "") -> str | int:
103        """>>> git clone {args}"""
104        return self.git(f"clone {args}")
105
106    def commit(self, args: str = "") -> str | int:
107        """>>> git commit {args}"""
108        return self.git(f"commit {args}")
109
110    def config(self, args: str = "") -> str | int:
111        """>>> git config {args}"""
112        return self.git(f"config {args}")
113
114    def count_objects(self, args: str = "") -> str | int:
115        """>>> git count-objects {args}"""
116        return self.git(f"count-objects {args}")
117
118    def describe(self, args: str = "") -> str | int:
119        """>>> git describe {args}"""
120        return self.git(f"describe {args}")
121
122    def diagnose(self, args: str = "") -> str | int:
123        """>>> git diagnose {args}"""
124        return self.git(f"diagnose {args}")
125
126    def diff(self, args: str = "") -> str | int:
127        """>>> git diff {args}"""
128        return self.git(f"diff {args}")
129
130    def difftool(self, args: str = "") -> str | int:
131        """>>> git difftool {args}"""
132        return self.git(f"difftool {args}")
133
134    def fast_export(self, args: str = "") -> str | int:
135        """>>> git fast-export {args}"""
136        return self.git(f"fast-export {args}")
137
138    def fast_import(self, args: str = "") -> str | int:
139        """>>> git fast-import {args}"""
140        return self.git(f"fast-import {args}")
141
142    def fetch(self, args: str = "") -> str | int:
143        """>>> git fetch {args}"""
144        return self.git(f"fetch {args}")
145
146    def filter_branch(self, args: str = "") -> str | int:
147        """>>> git filter-branch {args}"""
148        return self.git(f"filter-branch {args}")
149
150    def format_patch(self, args: str = "") -> str | int:
151        """>>> git format-patch {args}"""
152        return self.git(f"format-patch {args}")
153
154    def fsck(self, args: str = "") -> str | int:
155        """>>> git fsck {args}"""
156        return self.git(f"fsck {args}")
157
158    def gc(self, args: str = "") -> str | int:
159        """>>> git gc {args}"""
160        return self.git(f"gc {args}")
161
162    def gitk(self, args: str = "") -> str | int:
163        """>>> git gitk {args}"""
164        return self.git(f"gitk {args}")
165
166    def gitweb(self, args: str = "") -> str | int:
167        """>>> git gitweb {args}"""
168        return self.git(f"gitweb {args}")
169
170    def grep(self, args: str = "") -> str | int:
171        """>>> git grep {args}"""
172        return self.git(f"grep {args}")
173
174    def gui(self, args: str = "") -> str | int:
175        """>>> git gui {args}"""
176        return self.git(f"gui {args}")
177
178    def help(self, args: str = "") -> str | int:
179        """>>> git help {args}"""
180        return self.git(f"help {args}")
181
182    def init(self, args: str = "") -> str | int:
183        """>>> git init {args}"""
184        return self.git(f"init {args}")
185
186    def instaweb(self, args: str = "") -> str | int:
187        """>>> git instaweb {args}"""
188        return self.git(f"instaweb {args}")
189
190    def log(self, args: str = "") -> str | int:
191        """>>> git log {args}"""
192        return self.git(f"log {args}")
193
194    def maintenance(self, args: str = "") -> str | int:
195        """>>> git maintenance {args}"""
196        return self.git(f"maintenance {args}")
197
198    def merge(self, args: str = "") -> str | int:
199        """>>> git merge {args}"""
200        return self.git(f"merge {args}")
201
202    def merge_tree(self, args: str = "") -> str | int:
203        """>>> git merge-tree {args}"""
204        return self.git(f"merge-tree {args}")
205
206    def mergetool(self, args: str = "") -> str | int:
207        """>>> git mergetool {args}"""
208        return self.git(f"mergetool {args}")
209
210    def mv(self, args: str = "") -> str | int:
211        """>>> git mv {args}"""
212        return self.git(f"mv {args}")
213
214    def notes(self, args: str = "") -> str | int:
215        """>>> git notes {args}"""
216        return self.git(f"notes {args}")
217
218    def pack_refs(self, args: str = "") -> str | int:
219        """>>> git pack-refs {args}"""
220        return self.git(f"pack-refs {args}")
221
222    def prune(self, args: str = "") -> str | int:
223        """>>> git prune {args}"""
224        return self.git(f"prune {args}")
225
226    def pull(self, args: str = "") -> str | int:
227        """>>> git pull {args}"""
228        return self.git(f"pull {args}")
229
230    def push(self, args: str = "") -> str | int:
231        """>>> git push {args}"""
232        return self.git(f"push {args}")
233
234    def range_diff(self, args: str = "") -> str | int:
235        """>>> git range-diff {args}"""
236        return self.git(f"range-diff {args}")
237
238    def rebase(self, args: str = "") -> str | int:
239        """>>> git rebase {args}"""
240        return self.git(f"rebase {args}")
241
242    def reflog(self, args: str = "") -> str | int:
243        """>>> git reflog {args}"""
244        return self.git(f"reflog {args}")
245
246    def remote(self, args: str = "") -> str | int:
247        """>>> git remote {args}"""
248        return self.git(f"remote {args}")
249
250    def repack(self, args: str = "") -> str | int:
251        """>>> git repack {args}"""
252        return self.git(f"repack {args}")
253
254    def replace(self, args: str = "") -> str | int:
255        """>>> git replace {args}"""
256        return self.git(f"replace {args}")
257
258    def request_pull(self, args: str = "") -> str | int:
259        """>>> git request-pull {args}"""
260        return self.git(f"request-pull {args}")
261
262    def rerere(self, args: str = "") -> str | int:
263        """>>> git rerere {args}"""
264        return self.git(f"rerere {args}")
265
266    def reset(self, args: str = "") -> str | int:
267        """>>> git reset {args}"""
268        return self.git(f"reset {args}")
269
270    def restore(self, args: str = "") -> str | int:
271        """>>> git restore {args}"""
272        return self.git(f"restore {args}")
273
274    def revert(self, args: str = "") -> str | int:
275        """>>> git revert {args}"""
276        return self.git(f"revert {args}")
277
278    def rm(self, args: str = "") -> str | int:
279        """>>> git rm {args}"""
280        return self.git(f"rm {args}")
281
282    def scalar(self, args: str = "") -> str | int:
283        """>>> git scalar {args}"""
284        return self.git(f"scalar {args}")
285
286    def shortlog(self, args: str = "") -> str | int:
287        """>>> git shortlog {args}"""
288        return self.git(f"shortlog {args}")
289
290    def show(self, args: str = "") -> str | int:
291        """>>> git show {args}"""
292        return self.git(f"show {args}")
293
294    def show_branch(self, args: str = "") -> str | int:
295        """>>> git show-branch {args}"""
296        return self.git(f"show-branch {args}")
297
298    def sparse_checkout(self, args: str = "") -> str | int:
299        """>>> git sparse-checkout {args}"""
300        return self.git(f"sparse-checkout {args}")
301
302    def stash(self, args: str = "") -> str | int:
303        """>>> git stash {args}"""
304        return self.git(f"stash {args}")
305
306    def status(self, args: str = "") -> str | int:
307        """>>> git status {args}"""
308        return self.git(f"status {args}")
309
310    def submodule(self, args: str = "") -> str | int:
311        """>>> git submodule {args}"""
312        return self.git(f"submodule {args}")
313
314    def switch(self, args: str = "") -> str | int:
315        """>>> git switch {args}"""
316        return self.git(f"switch {args}")
317
318    def tag(self, args: str = "") -> str | int:
319        """>>> git tag {args}"""
320        return self.git(f"tag {args}")
321
322    def verify_commit(self, args: str = "") -> str | int:
323        """>>> git verify-commit {args}"""
324        return self.git(f"verify-commit {args}")
325
326    def verify_tag(self, args: str = "") -> str | int:
327        """>>> git verify-tag {args}"""
328        return self.git(f"verify-tag {args}")
329
330    def version(self, args: str = "") -> str | int:
331        """>>> git version {args}"""
332        return self.git(f"version {args}")
333
334    def whatchanged(self, args: str = "") -> str | int:
335        """>>> git whatchanged {args}"""
336        return self.git(f"whatchanged {args}")
337
338    def worktree(self, args: str = "") -> str | int:
339        """>>> git worktree {args}"""
340        return self.git(f"worktree {args}")
341
342    # Seat |=================================================Convenience=================================================|
343
344    @property
345    def current_branch(self) -> str:
346        """Returns the name of the currently active branch."""
347        capturing_output = self.capture_stdout
348        current_branch = ""
349        with self.capture_output():
350            branches = self.branch().splitlines()  # type: ignore
351            for branch in branches:
352                if branch.startswith("*"):
353                    current_branch = branch[2:]
354                    break
355        self.capture_stdout = capturing_output
356        return current_branch
357
358    @property
359    def origin_url(self) -> str | int:
360        """The remote origin url for this repo
361        >>> git remote get-url origin"""
362        return self.remote("get-url origin")
363
364    def add_all(self) -> str | int:
365        """Stage all modified and untracked files.
366        >>> git add ."""
367        return self.add(".")
368
369    def add_files(self, files: list[Pathish]) -> str | int:
370        """Stage a list of files."""
371        args = " ".join([str(file).replace("\\", "/") for file in files])
372        return self.add(args)
373
374    def add_remote_url(self, url: str, name: str = "origin") -> str | int:
375        """Add remote url to repo.
376        >>> git remote add {name} {url}"""
377        return self.remote(f"add {name} {url}")
378
379    def amend(self, files: list[Pathish] | None = None) -> str | int:
380        """Stage and commit changes to the previous commit.
381
382        If `files` is `None`, all files will be staged.
383
384        >>> git add {files} or git add .
385        >>> git commit --amend --no-edit
386        """
387        return (self.add(files) if files else self.add_all()) + self.commit("--amend --no-edit")  # type: ignore
388
389    def commit_all(self, message: str) -> str | int:
390        """Stage and commit all files with `message`.
391        >>> git add .
392        >>> git commit -m \"{message}\" """
393        return self.add_all() + self.commit(f'-m "{message}"')  # type: ignore
394
395    def commit_files(self, files: list[Pathish], message: str) -> str | int:
396        """Stage and commit a list of files with commit message `message`.
397        >>> git add {files}
398        >>> git commit -m \"{message}\" """
399        return self.add_files(files) + self.commit(f'-m "{message}"')  # type: ignore
400
401    def create_new_branch(self, branch_name: str) -> str | int:
402        """Create and switch to a new branch named with `branch_name`.
403        >>> git checkout -b {branch_name} --track"""
404        return self.checkout(f"-b {branch_name} --track")
405
406    def delete_branch(self, branch_name: str, local_only: bool = True) -> str | int:
407        """Delete `branch_name` from repo.
408
409        #### :params:
410
411        `local_only`: Only delete the local copy of `branch`, otherwise also delete the remote branch on origin and remote-tracking branch.
412        >>> git branch --delete {branch_name}
413
414        Then if not `local_only`:
415        >>> git push origin --delete {branch_name}
416        """
417        output = self.branch(f"--delete {branch_name}")
418        if not local_only:
419            return output + self.push(f"origin --delete {branch_name}")  # type: ignore
420        return output
421
422    def initcommit(self, files: list[Pathish] | None = None) -> str | int:
423        """Stage and commit `files` with the message `Initial commit`.
424
425        If `files` is not given, all files will be added and committed.
426        >>> git add {files} or git add .
427        >>> git commit -m "Initial commit" """
428        return (self.add_files(files) if files else self.add_all()) + self.commit('-m "Initial commit"')  # type: ignore
429
430    def list_branches(self) -> str | int:
431        """>>> git branch -vva"""
432        return self.branch("-vva")
433
434    def loggy(self) -> str | int:
435        """>>> git log --oneline --name-only --abbrev-commit --graph"""
436        return self.log("--oneline --name-only --abbrev-commit --graph")
437
438    def new_repo(self) -> str | int:
439        """Initialize a new repo in current directory.
440        >>> git init -b main"""
441        return self.init("-b main")
442
443    def push_new_branch(self, branch: str) -> str | int:
444        """Push a new branch to origin with tracking.
445        >>> git push -u origin {branch}"""
446        return self.push(f"-u origin {branch}")
447
448    def switch_branch(self, branch_name: str) -> str | int:
449        """Switch to the branch specified by `branch_name`.
450        >>> git checkout {branch_name}"""
451        return self.checkout(branch_name)
452
453    def undo(self) -> str | int:
454        """Undo uncommitted changes.
455        >>> git checkout ."""
456        return self.checkout(".")
457
458    def ignore(self, patterns: list[str]):
459        """Add `patterns` to `.gitignore`."""
460        gitignore = Pathier(".gitignore")
461        if not gitignore.exists():
462            gitignore.touch()
463        ignores = gitignore.split()
464        ignores += [pattern for pattern in patterns if pattern not in ignores]
465        gitignore.join(ignores)
466
467    # Seat |===============================Requires GitHub CLI to be installed and configured===============================|
468
469    @property
470    def owner(self) -> str:
471        return self._owner_reponame().split("/")[0]
472
473    @property
474    def repo_name(self) -> str:
475        return self._owner_reponame().split("/")[1]
476
477    def _change_visibility(self, visibility: str) -> str | int:
478        return self._run(
479            [
480                "gh",
481                "repo",
482                "edit",
483                f"{self.owner}/{self.repo_name}",
484                "--visibility",
485                visibility,
486            ]
487        )
488
489    def _owner_reponame(self) -> str:
490        """Returns "owner/repo-name", assuming there's one remote origin url and it's for github."""
491        with self.capture_output():
492            return urlparse(self.origin_url().strip("\n")).path.strip("/")  # type: ignore
493
494    def create_remote(self, name: str, public: bool = False) -> str | int:
495        """Uses GitHub CLI (must be installed and configured) to create a remote GitHub repo.
496
497        #### :params:
498
499        `name`: The name for the repo.
500
501        `public`: Set to `True` to create the repo as public, otherwise it'll be created as private.
502        """
503        visibility = "--public" if public else "--private"
504        return self._run(["gh", "repo", "create", name, visibility])
505
506    def create_remote_from_cwd(self, public: bool = False) -> str | int:
507        """Use GitHub CLI (must be installed and configured) to create a remote GitHub repo from
508        the current working directory repo and add its url as this repo's remote origin.
509
510        #### :params:
511
512        `public`: Create the GitHub repo as a public repo, default is to create it as private.
513        """
514        visibility = "public" if public else "private"
515        return self._run(
516            ["gh", "repo", "create", "--source", ".", f"--{visibility}", "--push"]
517        )
518
519    def delete_remote(self) -> str | int:
520        """Uses GitHub CLI (must be isntalled and configured) to delete the remote for this repo."""
521        return self._run(
522            ["gh", "repo", "delete", f"{self.owner}/{self.repo_name}", "--yes"]
523        )
524
525    def make_private(self) -> str | int:
526        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to private."""
527        return self._change_visibility("private")
528
529    def make_public(self) -> str | int:
530        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to public."""
531        return self._change_visibility("public")
Git(capture_stdout: bool = False)
12    def __init__(self, capture_stdout: bool = False):
13        """If `capture_stdout` is `True`, all functions will return their generated `stdout` as a string.
14        Otherwise, the functions return the call's exit code."""
15        self.capture_stdout = capture_stdout

If capture_stdout is True, all functions will return their generated stdout as a string. Otherwise, the functions return the call's exit code.

capture_stdout: bool

If True, member functions will return the generated stdout as a string, otherwise they return the command's exit code.

@contextmanager
def capture_output(self):
33    @contextmanager
34    def capture_output(self):
35        self.capture_stdout = True
36        yield self
37        self.capture_stdout = False
def git(self, command: str) -> str | int:
41    def git(self, command: str) -> str | int:
42        """Base function for executing git commands.
43        Use this if another function doesn't meet your needs.
44        >>> git {command}"""
45        args = ["git"] + shlex.split(command)
46        return self._run(args)

Base function for executing git commands. Use this if another function doesn't meet your needs.

>>> git {command}
def add(self, args: str = '') -> str | int:
50    def add(self, args: str = "") -> str | int:
51        """>>> git add {args}"""
52        return self.git(f"add {args}")
>>> git add {args}
def am(self, args: str = '') -> str | int:
54    def am(self, args: str = "") -> str | int:
55        """>>> git am {args}"""
56        return self.git(f"am {args}")
>>> git am {args}
def annotate(self, args: str = '') -> str | int:
58    def annotate(self, args: str = "") -> str | int:
59        """>>> git annotate {args}"""
60        return self.git(f"annotate {args}")
>>> git annotate {args}
def archive(self, args: str = '') -> str | int:
62    def archive(self, args: str = "") -> str | int:
63        """>>> git archive {args}"""
64        return self.git(f"archive {args}")
>>> git archive {args}
def bisect(self, args: str = '') -> str | int:
66    def bisect(self, args: str = "") -> str | int:
67        """>>> git bisect {args}"""
68        return self.git(f"bisect {args}")
>>> git bisect {args}
def blame(self, args: str = '') -> str | int:
70    def blame(self, args: str = "") -> str | int:
71        """>>> git blame {args}"""
72        return self.git(f"blame {args}")
>>> git blame {args}
def branch(self, args: str = '') -> str | int:
74    def branch(self, args: str = "") -> str | int:
75        """>>> git branch {args}"""
76        return self.git(f"branch {args}")
>>> git branch {args}
def bugreport(self, args: str = '') -> str | int:
78    def bugreport(self, args: str = "") -> str | int:
79        """>>> git bugreport {args}"""
80        return self.git(f"bugreport {args}")
>>> git bugreport {args}
def bundle(self, args: str = '') -> str | int:
82    def bundle(self, args: str = "") -> str | int:
83        """>>> git bundle {args}"""
84        return self.git(f"bundle {args}")
>>> git bundle {args}
def checkout(self, args: str = '') -> str | int:
86    def checkout(self, args: str = "") -> str | int:
87        """>>> git checkout {args}"""
88        return self.git(f"checkout {args}")
>>> git checkout {args}
def cherry_pick(self, args: str = '') -> str | int:
90    def cherry_pick(self, args: str = "") -> str | int:
91        """>>> git cherry-pick {args}"""
92        return self.git(f"cherry-pick {args}")
>>> git cherry-pick {args}
def citool(self, args: str = '') -> str | int:
94    def citool(self, args: str = "") -> str | int:
95        """>>> git citool {args}"""
96        return self.git(f"citool {args}")
>>> git citool {args}
def clean(self, args: str = '') -> str | int:
 98    def clean(self, args: str = "") -> str | int:
 99        """>>> git clean {args}"""
100        return self.git(f"clean {args}")
>>> git clean {args}
def clone(self, args: str = '') -> str | int:
102    def clone(self, args: str = "") -> str | int:
103        """>>> git clone {args}"""
104        return self.git(f"clone {args}")
>>> git clone {args}
def commit(self, args: str = '') -> str | int:
106    def commit(self, args: str = "") -> str | int:
107        """>>> git commit {args}"""
108        return self.git(f"commit {args}")
>>> git commit {args}
def config(self, args: str = '') -> str | int:
110    def config(self, args: str = "") -> str | int:
111        """>>> git config {args}"""
112        return self.git(f"config {args}")
>>> git config {args}
def count_objects(self, args: str = '') -> str | int:
114    def count_objects(self, args: str = "") -> str | int:
115        """>>> git count-objects {args}"""
116        return self.git(f"count-objects {args}")
>>> git count-objects {args}
def describe(self, args: str = '') -> str | int:
118    def describe(self, args: str = "") -> str | int:
119        """>>> git describe {args}"""
120        return self.git(f"describe {args}")
>>> git describe {args}
def diagnose(self, args: str = '') -> str | int:
122    def diagnose(self, args: str = "") -> str | int:
123        """>>> git diagnose {args}"""
124        return self.git(f"diagnose {args}")
>>> git diagnose {args}
def diff(self, args: str = '') -> str | int:
126    def diff(self, args: str = "") -> str | int:
127        """>>> git diff {args}"""
128        return self.git(f"diff {args}")
>>> git diff {args}
def difftool(self, args: str = '') -> str | int:
130    def difftool(self, args: str = "") -> str | int:
131        """>>> git difftool {args}"""
132        return self.git(f"difftool {args}")
>>> git difftool {args}
def fast_export(self, args: str = '') -> str | int:
134    def fast_export(self, args: str = "") -> str | int:
135        """>>> git fast-export {args}"""
136        return self.git(f"fast-export {args}")
>>> git fast-export {args}
def fast_import(self, args: str = '') -> str | int:
138    def fast_import(self, args: str = "") -> str | int:
139        """>>> git fast-import {args}"""
140        return self.git(f"fast-import {args}")
>>> git fast-import {args}
def fetch(self, args: str = '') -> str | int:
142    def fetch(self, args: str = "") -> str | int:
143        """>>> git fetch {args}"""
144        return self.git(f"fetch {args}")
>>> git fetch {args}
def filter_branch(self, args: str = '') -> str | int:
146    def filter_branch(self, args: str = "") -> str | int:
147        """>>> git filter-branch {args}"""
148        return self.git(f"filter-branch {args}")
>>> git filter-branch {args}
def format_patch(self, args: str = '') -> str | int:
150    def format_patch(self, args: str = "") -> str | int:
151        """>>> git format-patch {args}"""
152        return self.git(f"format-patch {args}")
>>> git format-patch {args}
def fsck(self, args: str = '') -> str | int:
154    def fsck(self, args: str = "") -> str | int:
155        """>>> git fsck {args}"""
156        return self.git(f"fsck {args}")
>>> git fsck {args}
def gc(self, args: str = '') -> str | int:
158    def gc(self, args: str = "") -> str | int:
159        """>>> git gc {args}"""
160        return self.git(f"gc {args}")
>>> git gc {args}
def gitk(self, args: str = '') -> str | int:
162    def gitk(self, args: str = "") -> str | int:
163        """>>> git gitk {args}"""
164        return self.git(f"gitk {args}")
>>> git gitk {args}
def gitweb(self, args: str = '') -> str | int:
166    def gitweb(self, args: str = "") -> str | int:
167        """>>> git gitweb {args}"""
168        return self.git(f"gitweb {args}")
>>> git gitweb {args}
def grep(self, args: str = '') -> str | int:
170    def grep(self, args: str = "") -> str | int:
171        """>>> git grep {args}"""
172        return self.git(f"grep {args}")
>>> git grep {args}
def gui(self, args: str = '') -> str | int:
174    def gui(self, args: str = "") -> str | int:
175        """>>> git gui {args}"""
176        return self.git(f"gui {args}")
>>> git gui {args}
def help(self, args: str = '') -> str | int:
178    def help(self, args: str = "") -> str | int:
179        """>>> git help {args}"""
180        return self.git(f"help {args}")
>>> git help {args}
def init(self, args: str = '') -> str | int:
182    def init(self, args: str = "") -> str | int:
183        """>>> git init {args}"""
184        return self.git(f"init {args}")
>>> git init {args}
def instaweb(self, args: str = '') -> str | int:
186    def instaweb(self, args: str = "") -> str | int:
187        """>>> git instaweb {args}"""
188        return self.git(f"instaweb {args}")
>>> git instaweb {args}
def log(self, args: str = '') -> str | int:
190    def log(self, args: str = "") -> str | int:
191        """>>> git log {args}"""
192        return self.git(f"log {args}")
>>> git log {args}
def maintenance(self, args: str = '') -> str | int:
194    def maintenance(self, args: str = "") -> str | int:
195        """>>> git maintenance {args}"""
196        return self.git(f"maintenance {args}")
>>> git maintenance {args}
def merge(self, args: str = '') -> str | int:
198    def merge(self, args: str = "") -> str | int:
199        """>>> git merge {args}"""
200        return self.git(f"merge {args}")
>>> git merge {args}
def merge_tree(self, args: str = '') -> str | int:
202    def merge_tree(self, args: str = "") -> str | int:
203        """>>> git merge-tree {args}"""
204        return self.git(f"merge-tree {args}")
>>> git merge-tree {args}
def mergetool(self, args: str = '') -> str | int:
206    def mergetool(self, args: str = "") -> str | int:
207        """>>> git mergetool {args}"""
208        return self.git(f"mergetool {args}")
>>> git mergetool {args}
def mv(self, args: str = '') -> str | int:
210    def mv(self, args: str = "") -> str | int:
211        """>>> git mv {args}"""
212        return self.git(f"mv {args}")
>>> git mv {args}
def notes(self, args: str = '') -> str | int:
214    def notes(self, args: str = "") -> str | int:
215        """>>> git notes {args}"""
216        return self.git(f"notes {args}")
>>> git notes {args}
def pack_refs(self, args: str = '') -> str | int:
218    def pack_refs(self, args: str = "") -> str | int:
219        """>>> git pack-refs {args}"""
220        return self.git(f"pack-refs {args}")
>>> git pack-refs {args}
def prune(self, args: str = '') -> str | int:
222    def prune(self, args: str = "") -> str | int:
223        """>>> git prune {args}"""
224        return self.git(f"prune {args}")
>>> git prune {args}
def pull(self, args: str = '') -> str | int:
226    def pull(self, args: str = "") -> str | int:
227        """>>> git pull {args}"""
228        return self.git(f"pull {args}")
>>> git pull {args}
def push(self, args: str = '') -> str | int:
230    def push(self, args: str = "") -> str | int:
231        """>>> git push {args}"""
232        return self.git(f"push {args}")
>>> git push {args}
def range_diff(self, args: str = '') -> str | int:
234    def range_diff(self, args: str = "") -> str | int:
235        """>>> git range-diff {args}"""
236        return self.git(f"range-diff {args}")
>>> git range-diff {args}
def rebase(self, args: str = '') -> str | int:
238    def rebase(self, args: str = "") -> str | int:
239        """>>> git rebase {args}"""
240        return self.git(f"rebase {args}")
>>> git rebase {args}
def reflog(self, args: str = '') -> str | int:
242    def reflog(self, args: str = "") -> str | int:
243        """>>> git reflog {args}"""
244        return self.git(f"reflog {args}")
>>> git reflog {args}
def remote(self, args: str = '') -> str | int:
246    def remote(self, args: str = "") -> str | int:
247        """>>> git remote {args}"""
248        return self.git(f"remote {args}")
>>> git remote {args}
def repack(self, args: str = '') -> str | int:
250    def repack(self, args: str = "") -> str | int:
251        """>>> git repack {args}"""
252        return self.git(f"repack {args}")
>>> git repack {args}
def replace(self, args: str = '') -> str | int:
254    def replace(self, args: str = "") -> str | int:
255        """>>> git replace {args}"""
256        return self.git(f"replace {args}")
>>> git replace {args}
def request_pull(self, args: str = '') -> str | int:
258    def request_pull(self, args: str = "") -> str | int:
259        """>>> git request-pull {args}"""
260        return self.git(f"request-pull {args}")
>>> git request-pull {args}
def rerere(self, args: str = '') -> str | int:
262    def rerere(self, args: str = "") -> str | int:
263        """>>> git rerere {args}"""
264        return self.git(f"rerere {args}")
>>> git rerere {args}
def reset(self, args: str = '') -> str | int:
266    def reset(self, args: str = "") -> str | int:
267        """>>> git reset {args}"""
268        return self.git(f"reset {args}")
>>> git reset {args}
def restore(self, args: str = '') -> str | int:
270    def restore(self, args: str = "") -> str | int:
271        """>>> git restore {args}"""
272        return self.git(f"restore {args}")
>>> git restore {args}
def revert(self, args: str = '') -> str | int:
274    def revert(self, args: str = "") -> str | int:
275        """>>> git revert {args}"""
276        return self.git(f"revert {args}")
>>> git revert {args}
def rm(self, args: str = '') -> str | int:
278    def rm(self, args: str = "") -> str | int:
279        """>>> git rm {args}"""
280        return self.git(f"rm {args}")
>>> git rm {args}
def scalar(self, args: str = '') -> str | int:
282    def scalar(self, args: str = "") -> str | int:
283        """>>> git scalar {args}"""
284        return self.git(f"scalar {args}")
>>> git scalar {args}
def shortlog(self, args: str = '') -> str | int:
286    def shortlog(self, args: str = "") -> str | int:
287        """>>> git shortlog {args}"""
288        return self.git(f"shortlog {args}")
>>> git shortlog {args}
def show(self, args: str = '') -> str | int:
290    def show(self, args: str = "") -> str | int:
291        """>>> git show {args}"""
292        return self.git(f"show {args}")
>>> git show {args}
def show_branch(self, args: str = '') -> str | int:
294    def show_branch(self, args: str = "") -> str | int:
295        """>>> git show-branch {args}"""
296        return self.git(f"show-branch {args}")
>>> git show-branch {args}
def sparse_checkout(self, args: str = '') -> str | int:
298    def sparse_checkout(self, args: str = "") -> str | int:
299        """>>> git sparse-checkout {args}"""
300        return self.git(f"sparse-checkout {args}")
>>> git sparse-checkout {args}
def stash(self, args: str = '') -> str | int:
302    def stash(self, args: str = "") -> str | int:
303        """>>> git stash {args}"""
304        return self.git(f"stash {args}")
>>> git stash {args}
def status(self, args: str = '') -> str | int:
306    def status(self, args: str = "") -> str | int:
307        """>>> git status {args}"""
308        return self.git(f"status {args}")
>>> git status {args}
def submodule(self, args: str = '') -> str | int:
310    def submodule(self, args: str = "") -> str | int:
311        """>>> git submodule {args}"""
312        return self.git(f"submodule {args}")
>>> git submodule {args}
def switch(self, args: str = '') -> str | int:
314    def switch(self, args: str = "") -> str | int:
315        """>>> git switch {args}"""
316        return self.git(f"switch {args}")
>>> git switch {args}
def tag(self, args: str = '') -> str | int:
318    def tag(self, args: str = "") -> str | int:
319        """>>> git tag {args}"""
320        return self.git(f"tag {args}")
>>> git tag {args}
def verify_commit(self, args: str = '') -> str | int:
322    def verify_commit(self, args: str = "") -> str | int:
323        """>>> git verify-commit {args}"""
324        return self.git(f"verify-commit {args}")
>>> git verify-commit {args}
def verify_tag(self, args: str = '') -> str | int:
326    def verify_tag(self, args: str = "") -> str | int:
327        """>>> git verify-tag {args}"""
328        return self.git(f"verify-tag {args}")
>>> git verify-tag {args}
def version(self, args: str = '') -> str | int:
330    def version(self, args: str = "") -> str | int:
331        """>>> git version {args}"""
332        return self.git(f"version {args}")
>>> git version {args}
def whatchanged(self, args: str = '') -> str | int:
334    def whatchanged(self, args: str = "") -> str | int:
335        """>>> git whatchanged {args}"""
336        return self.git(f"whatchanged {args}")
>>> git whatchanged {args}
def worktree(self, args: str = '') -> str | int:
338    def worktree(self, args: str = "") -> str | int:
339        """>>> git worktree {args}"""
340        return self.git(f"worktree {args}")
>>> git worktree {args}
current_branch: str

Returns the name of the currently active branch.

origin_url: str | int

The remote origin url for this repo

>>> git remote get-url origin
def add_all(self) -> str | int:
364    def add_all(self) -> str | int:
365        """Stage all modified and untracked files.
366        >>> git add ."""
367        return self.add(".")

Stage all modified and untracked files.

>>> git add .
def add_files( self, files: list[pathier.pathier.Pathier | pathlib.Path | str]) -> str | int:
369    def add_files(self, files: list[Pathish]) -> str | int:
370        """Stage a list of files."""
371        args = " ".join([str(file).replace("\\", "/") for file in files])
372        return self.add(args)

Stage a list of files.

def add_remote_url(self, url: str, name: str = 'origin') -> str | int:
374    def add_remote_url(self, url: str, name: str = "origin") -> str | int:
375        """Add remote url to repo.
376        >>> git remote add {name} {url}"""
377        return self.remote(f"add {name} {url}")

Add remote url to repo.

>>> git remote add {name} {url}
def amend( self, files: list[pathier.pathier.Pathier | pathlib.Path | str] | None = None) -> str | int:
379    def amend(self, files: list[Pathish] | None = None) -> str | int:
380        """Stage and commit changes to the previous commit.
381
382        If `files` is `None`, all files will be staged.
383
384        >>> git add {files} or git add .
385        >>> git commit --amend --no-edit
386        """
387        return (self.add(files) if files else self.add_all()) + self.commit("--amend --no-edit")  # type: ignore

Stage and commit changes to the previous commit.

If files is None, all files will be staged.

>>> git add {files} or git add .
>>> git commit --amend --no-edit
def commit_all(self, message: str) -> str | int:
389    def commit_all(self, message: str) -> str | int:
390        """Stage and commit all files with `message`.
391        >>> git add .
392        >>> git commit -m \"{message}\" """
393        return self.add_all() + self.commit(f'-m "{message}"')  # type: ignore

Stage and commit all files with message.

>>> git add .
>>> git commit -m "{message}"
def commit_files( self, files: list[pathier.pathier.Pathier | pathlib.Path | str], message: str) -> str | int:
395    def commit_files(self, files: list[Pathish], message: str) -> str | int:
396        """Stage and commit a list of files with commit message `message`.
397        >>> git add {files}
398        >>> git commit -m \"{message}\" """
399        return self.add_files(files) + self.commit(f'-m "{message}"')  # type: ignore

Stage and commit a list of files with commit message message.

>>> git add {files}
>>> git commit -m "{message}"
def create_new_branch(self, branch_name: str) -> str | int:
401    def create_new_branch(self, branch_name: str) -> str | int:
402        """Create and switch to a new branch named with `branch_name`.
403        >>> git checkout -b {branch_name} --track"""
404        return self.checkout(f"-b {branch_name} --track")

Create and switch to a new branch named with branch_name.

>>> git checkout -b {branch_name} --track
def delete_branch(self, branch_name: str, local_only: bool = True) -> str | int:
406    def delete_branch(self, branch_name: str, local_only: bool = True) -> str | int:
407        """Delete `branch_name` from repo.
408
409        #### :params:
410
411        `local_only`: Only delete the local copy of `branch`, otherwise also delete the remote branch on origin and remote-tracking branch.
412        >>> git branch --delete {branch_name}
413
414        Then if not `local_only`:
415        >>> git push origin --delete {branch_name}
416        """
417        output = self.branch(f"--delete {branch_name}")
418        if not local_only:
419            return output + self.push(f"origin --delete {branch_name}")  # type: ignore
420        return output

Delete branch_name from repo.

:params:

local_only: Only delete the local copy of branch, otherwise also delete the remote branch on origin and remote-tracking branch.

>>> git branch --delete {branch_name}

Then if not local_only:

>>> git push origin --delete {branch_name}
def initcommit( self, files: list[pathier.pathier.Pathier | pathlib.Path | str] | None = None) -> str | int:
422    def initcommit(self, files: list[Pathish] | None = None) -> str | int:
423        """Stage and commit `files` with the message `Initial commit`.
424
425        If `files` is not given, all files will be added and committed.
426        >>> git add {files} or git add .
427        >>> git commit -m "Initial commit" """
428        return (self.add_files(files) if files else self.add_all()) + self.commit('-m "Initial commit"')  # type: ignore

Stage and commit files with the message Initial commit.

If files is not given, all files will be added and committed.

>>> git add {files} or git add .
>>> git commit -m "Initial commit"
def list_branches(self) -> str | int:
430    def list_branches(self) -> str | int:
431        """>>> git branch -vva"""
432        return self.branch("-vva")
>>> git branch -vva
def loggy(self) -> str | int:
434    def loggy(self) -> str | int:
435        """>>> git log --oneline --name-only --abbrev-commit --graph"""
436        return self.log("--oneline --name-only --abbrev-commit --graph")
>>> git log --oneline --name-only --abbrev-commit --graph
def new_repo(self) -> str | int:
438    def new_repo(self) -> str | int:
439        """Initialize a new repo in current directory.
440        >>> git init -b main"""
441        return self.init("-b main")

Initialize a new repo in current directory.

>>> git init -b main
def push_new_branch(self, branch: str) -> str | int:
443    def push_new_branch(self, branch: str) -> str | int:
444        """Push a new branch to origin with tracking.
445        >>> git push -u origin {branch}"""
446        return self.push(f"-u origin {branch}")

Push a new branch to origin with tracking.

>>> git push -u origin {branch}
def switch_branch(self, branch_name: str) -> str | int:
448    def switch_branch(self, branch_name: str) -> str | int:
449        """Switch to the branch specified by `branch_name`.
450        >>> git checkout {branch_name}"""
451        return self.checkout(branch_name)

Switch to the branch specified by branch_name.

>>> git checkout {branch_name}
def undo(self) -> str | int:
453    def undo(self) -> str | int:
454        """Undo uncommitted changes.
455        >>> git checkout ."""
456        return self.checkout(".")

Undo uncommitted changes.

>>> git checkout .
def ignore(self, patterns: list[str]):
458    def ignore(self, patterns: list[str]):
459        """Add `patterns` to `.gitignore`."""
460        gitignore = Pathier(".gitignore")
461        if not gitignore.exists():
462            gitignore.touch()
463        ignores = gitignore.split()
464        ignores += [pattern for pattern in patterns if pattern not in ignores]
465        gitignore.join(ignores)

Add patterns to .gitignore.

def create_remote(self, name: str, public: bool = False) -> str | int:
494    def create_remote(self, name: str, public: bool = False) -> str | int:
495        """Uses GitHub CLI (must be installed and configured) to create a remote GitHub repo.
496
497        #### :params:
498
499        `name`: The name for the repo.
500
501        `public`: Set to `True` to create the repo as public, otherwise it'll be created as private.
502        """
503        visibility = "--public" if public else "--private"
504        return self._run(["gh", "repo", "create", name, visibility])

Uses GitHub CLI (must be installed and configured) to create a remote GitHub repo.

:params:

name: The name for the repo.

public: Set to True to create the repo as public, otherwise it'll be created as private.

def create_remote_from_cwd(self, public: bool = False) -> str | int:
506    def create_remote_from_cwd(self, public: bool = False) -> str | int:
507        """Use GitHub CLI (must be installed and configured) to create a remote GitHub repo from
508        the current working directory repo and add its url as this repo's remote origin.
509
510        #### :params:
511
512        `public`: Create the GitHub repo as a public repo, default is to create it as private.
513        """
514        visibility = "public" if public else "private"
515        return self._run(
516            ["gh", "repo", "create", "--source", ".", f"--{visibility}", "--push"]
517        )

Use GitHub CLI (must be installed and configured) to create a remote GitHub repo from the current working directory repo and add its url as this repo's remote origin.

:params:

public: Create the GitHub repo as a public repo, default is to create it as private.

def delete_remote(self) -> str | int:
519    def delete_remote(self) -> str | int:
520        """Uses GitHub CLI (must be isntalled and configured) to delete the remote for this repo."""
521        return self._run(
522            ["gh", "repo", "delete", f"{self.owner}/{self.repo_name}", "--yes"]
523        )

Uses GitHub CLI (must be isntalled and configured) to delete the remote for this repo.

def make_private(self) -> str | int:
525    def make_private(self) -> str | int:
526        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to private."""
527        return self._change_visibility("private")

Uses GitHub CLI (must be installed and configured) to set the repo's visibility to private.

def make_public(self) -> str | int:
529    def make_public(self) -> str | int:
530        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to public."""
531        return self._change_visibility("public")

Uses GitHub CLI (must be installed and configured) to set the repo's visibility to public.