gitbetter.git

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

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):
32    @contextmanager
33    def capture_output(self):
34        self.capture_stdout = True
35        yield self
36        self.capture_stdout = False
def git(self, command: str) -> str | int:
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)

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:
49    def add(self, args: str = "") -> str | int:
50        """>>> git add {args}"""
51        return self.git(f"add {args}")
>>> git add {args}
def am(self, args: str = '') -> str | int:
53    def am(self, args: str = "") -> str | int:
54        """>>> git am {args}"""
55        return self.git(f"am {args}")
>>> git am {args}
def annotate(self, args: str = '') -> str | int:
57    def annotate(self, args: str = "") -> str | int:
58        """>>> git annotate {args}"""
59        return self.git(f"annotate {args}")
>>> git annotate {args}
def archive(self, args: str = '') -> str | int:
61    def archive(self, args: str = "") -> str | int:
62        """>>> git archive {args}"""
63        return self.git(f"archive {args}")
>>> git archive {args}
def bisect(self, args: str = '') -> str | int:
65    def bisect(self, args: str = "") -> str | int:
66        """>>> git bisect {args}"""
67        return self.git(f"bisect {args}")
>>> git bisect {args}
def blame(self, args: str = '') -> str | int:
69    def blame(self, args: str = "") -> str | int:
70        """>>> git blame {args}"""
71        return self.git(f"blame {args}")
>>> git blame {args}
def branch(self, args: str = '') -> str | int:
73    def branch(self, args: str = "") -> str | int:
74        """>>> git branch {args}"""
75        return self.git(f"branch {args}")
>>> git branch {args}
def bugreport(self, args: str = '') -> str | int:
77    def bugreport(self, args: str = "") -> str | int:
78        """>>> git bugreport {args}"""
79        return self.git(f"bugreport {args}")
>>> git bugreport {args}
def bundle(self, args: str = '') -> str | int:
81    def bundle(self, args: str = "") -> str | int:
82        """>>> git bundle {args}"""
83        return self.git(f"bundle {args}")
>>> git bundle {args}
def checkout(self, args: str = '') -> str | int:
85    def checkout(self, args: str = "") -> str | int:
86        """>>> git checkout {args}"""
87        return self.git(f"checkout {args}")
>>> git checkout {args}
def cherry_pick(self, args: str = '') -> str | int:
89    def cherry_pick(self, args: str = "") -> str | int:
90        """>>> git cherry-pick {args}"""
91        return self.git(f"cherry-pick {args}")
>>> git cherry-pick {args}
def citool(self, args: str = '') -> str | int:
93    def citool(self, args: str = "") -> str | int:
94        """>>> git citool {args}"""
95        return self.git(f"citool {args}")
>>> git citool {args}
def clean(self, args: str = '') -> str | int:
97    def clean(self, args: str = "") -> str | int:
98        """>>> git clean {args}"""
99        return self.git(f"clean {args}")
>>> git clean {args}
def clone(self, args: str = '') -> str | int:
101    def clone(self, args: str = "") -> str | int:
102        """>>> git clone {args}"""
103        return self.git(f"clone {args}")
>>> git clone {args}
def commit(self, args: str = '') -> str | int:
105    def commit(self, args: str = "") -> str | int:
106        """>>> git commit {args}"""
107        return self.git(f"commit {args}")
>>> git commit {args}
def config(self, args: str = '') -> str | int:
109    def config(self, args: str = "") -> str | int:
110        """>>> git config {args}"""
111        return self.git(f"config {args}")
>>> git config {args}
def count_objects(self, args: str = '') -> str | int:
113    def count_objects(self, args: str = "") -> str | int:
114        """>>> git count-objects {args}"""
115        return self.git(f"count-objects {args}")
>>> git count-objects {args}
def describe(self, args: str = '') -> str | int:
117    def describe(self, args: str = "") -> str | int:
118        """>>> git describe {args}"""
119        return self.git(f"describe {args}")
>>> git describe {args}
def diagnose(self, args: str = '') -> str | int:
121    def diagnose(self, args: str = "") -> str | int:
122        """>>> git diagnose {args}"""
123        return self.git(f"diagnose {args}")
>>> git diagnose {args}
def diff(self, args: str = '') -> str | int:
125    def diff(self, args: str = "") -> str | int:
126        """>>> git diff {args}"""
127        return self.git(f"diff {args}")
>>> git diff {args}
def difftool(self, args: str = '') -> str | int:
129    def difftool(self, args: str = "") -> str | int:
130        """>>> git difftool {args}"""
131        return self.git(f"difftool {args}")
>>> git difftool {args}
def fast_export(self, args: str = '') -> str | int:
133    def fast_export(self, args: str = "") -> str | int:
134        """>>> git fast-export {args}"""
135        return self.git(f"fast-export {args}")
>>> git fast-export {args}
def fast_import(self, args: str = '') -> str | int:
137    def fast_import(self, args: str = "") -> str | int:
138        """>>> git fast-import {args}"""
139        return self.git(f"fast-import {args}")
>>> git fast-import {args}
def fetch(self, args: str = '') -> str | int:
141    def fetch(self, args: str = "") -> str | int:
142        """>>> git fetch {args}"""
143        return self.git(f"fetch {args}")
>>> git fetch {args}
def filter_branch(self, args: str = '') -> str | int:
145    def filter_branch(self, args: str = "") -> str | int:
146        """>>> git filter-branch {args}"""
147        return self.git(f"filter-branch {args}")
>>> git filter-branch {args}
def format_patch(self, args: str = '') -> str | int:
149    def format_patch(self, args: str = "") -> str | int:
150        """>>> git format-patch {args}"""
151        return self.git(f"format-patch {args}")
>>> git format-patch {args}
def fsck(self, args: str = '') -> str | int:
153    def fsck(self, args: str = "") -> str | int:
154        """>>> git fsck {args}"""
155        return self.git(f"fsck {args}")
>>> git fsck {args}
def gc(self, args: str = '') -> str | int:
157    def gc(self, args: str = "") -> str | int:
158        """>>> git gc {args}"""
159        return self.git(f"gc {args}")
>>> git gc {args}
def gitk(self, args: str = '') -> str | int:
161    def gitk(self, args: str = "") -> str | int:
162        """>>> git gitk {args}"""
163        return self.git(f"gitk {args}")
>>> git gitk {args}
def gitweb(self, args: str = '') -> str | int:
165    def gitweb(self, args: str = "") -> str | int:
166        """>>> git gitweb {args}"""
167        return self.git(f"gitweb {args}")
>>> git gitweb {args}
def grep(self, args: str = '') -> str | int:
169    def grep(self, args: str = "") -> str | int:
170        """>>> git grep {args}"""
171        return self.git(f"grep {args}")
>>> git grep {args}
def gui(self, args: str = '') -> str | int:
173    def gui(self, args: str = "") -> str | int:
174        """>>> git gui {args}"""
175        return self.git(f"gui {args}")
>>> git gui {args}
def help(self, args: str = '') -> str | int:
177    def help(self, args: str = "") -> str | int:
178        """>>> git help {args}"""
179        return self.git(f"help {args}")
>>> git help {args}
def init(self, args: str = '') -> str | int:
181    def init(self, args: str = "") -> str | int:
182        """>>> git init {args}"""
183        return self.git(f"init {args}")
>>> git init {args}
def instaweb(self, args: str = '') -> str | int:
185    def instaweb(self, args: str = "") -> str | int:
186        """>>> git instaweb {args}"""
187        return self.git(f"instaweb {args}")
>>> git instaweb {args}
def log(self, args: str = '') -> str | int:
189    def log(self, args: str = "") -> str | int:
190        """>>> git log {args}"""
191        return self.git(f"log {args}")
>>> git log {args}
def maintenance(self, args: str = '') -> str | int:
193    def maintenance(self, args: str = "") -> str | int:
194        """>>> git maintenance {args}"""
195        return self.git(f"maintenance {args}")
>>> git maintenance {args}
def merge(self, args: str = '') -> str | int:
197    def merge(self, args: str = "") -> str | int:
198        """>>> git merge {args}"""
199        return self.git(f"merge {args}")
>>> git merge {args}
def merge_tree(self, args: str = '') -> str | int:
201    def merge_tree(self, args: str = "") -> str | int:
202        """>>> git merge-tree {args}"""
203        return self.git(f"merge-tree {args}")
>>> git merge-tree {args}
def mergetool(self, args: str = '') -> str | int:
205    def mergetool(self, args: str = "") -> str | int:
206        """>>> git mergetool {args}"""
207        return self.git(f"mergetool {args}")
>>> git mergetool {args}
def mv(self, args: str = '') -> str | int:
209    def mv(self, args: str = "") -> str | int:
210        """>>> git mv {args}"""
211        return self.git(f"mv {args}")
>>> git mv {args}
def notes(self, args: str = '') -> str | int:
213    def notes(self, args: str = "") -> str | int:
214        """>>> git notes {args}"""
215        return self.git(f"notes {args}")
>>> git notes {args}
def pack_refs(self, args: str = '') -> str | int:
217    def pack_refs(self, args: str = "") -> str | int:
218        """>>> git pack-refs {args}"""
219        return self.git(f"pack-refs {args}")
>>> git pack-refs {args}
def prune(self, args: str = '') -> str | int:
221    def prune(self, args: str = "") -> str | int:
222        """>>> git prune {args}"""
223        return self.git(f"prune {args}")
>>> git prune {args}
def pull(self, args: str = '') -> str | int:
225    def pull(self, args: str = "") -> str | int:
226        """>>> git pull {args}"""
227        return self.git(f"pull {args}")
>>> git pull {args}
def push(self, args: str = '') -> str | int:
229    def push(self, args: str = "") -> str | int:
230        """>>> git push {args}"""
231        return self.git(f"push {args}")
>>> git push {args}
def range_diff(self, args: str = '') -> str | int:
233    def range_diff(self, args: str = "") -> str | int:
234        """>>> git range-diff {args}"""
235        return self.git(f"range-diff {args}")
>>> git range-diff {args}
def rebase(self, args: str = '') -> str | int:
237    def rebase(self, args: str = "") -> str | int:
238        """>>> git rebase {args}"""
239        return self.git(f"rebase {args}")
>>> git rebase {args}
def reflog(self, args: str = '') -> str | int:
241    def reflog(self, args: str = "") -> str | int:
242        """>>> git reflog {args}"""
243        return self.git(f"reflog {args}")
>>> git reflog {args}
def remote(self, args: str = '') -> str | int:
245    def remote(self, args: str = "") -> str | int:
246        """>>> git remote {args}"""
247        return self.git(f"remote {args}")
>>> git remote {args}
def repack(self, args: str = '') -> str | int:
249    def repack(self, args: str = "") -> str | int:
250        """>>> git repack {args}"""
251        return self.git(f"repack {args}")
>>> git repack {args}
def replace(self, args: str = '') -> str | int:
253    def replace(self, args: str = "") -> str | int:
254        """>>> git replace {args}"""
255        return self.git(f"replace {args}")
>>> git replace {args}
def request_pull(self, args: str = '') -> str | int:
257    def request_pull(self, args: str = "") -> str | int:
258        """>>> git request-pull {args}"""
259        return self.git(f"request-pull {args}")
>>> git request-pull {args}
def rerere(self, args: str = '') -> str | int:
261    def rerere(self, args: str = "") -> str | int:
262        """>>> git rerere {args}"""
263        return self.git(f"rerere {args}")
>>> git rerere {args}
def reset(self, args: str = '') -> str | int:
265    def reset(self, args: str = "") -> str | int:
266        """>>> git reset {args}"""
267        return self.git(f"reset {args}")
>>> git reset {args}
def restore(self, args: str = '') -> str | int:
269    def restore(self, args: str = "") -> str | int:
270        """>>> git restore {args}"""
271        return self.git(f"restore {args}")
>>> git restore {args}
def revert(self, args: str = '') -> str | int:
273    def revert(self, args: str = "") -> str | int:
274        """>>> git revert {args}"""
275        return self.git(f"revert {args}")
>>> git revert {args}
def rm(self, args: str = '') -> str | int:
277    def rm(self, args: str = "") -> str | int:
278        """>>> git rm {args}"""
279        return self.git(f"rm {args}")
>>> git rm {args}
def scalar(self, args: str = '') -> str | int:
281    def scalar(self, args: str = "") -> str | int:
282        """>>> git scalar {args}"""
283        return self.git(f"scalar {args}")
>>> git scalar {args}
def shortlog(self, args: str = '') -> str | int:
285    def shortlog(self, args: str = "") -> str | int:
286        """>>> git shortlog {args}"""
287        return self.git(f"shortlog {args}")
>>> git shortlog {args}
def show(self, args: str = '') -> str | int:
289    def show(self, args: str = "") -> str | int:
290        """>>> git show {args}"""
291        return self.git(f"show {args}")
>>> git show {args}
def show_branch(self, args: str = '') -> str | int:
293    def show_branch(self, args: str = "") -> str | int:
294        """>>> git show-branch {args}"""
295        return self.git(f"show-branch {args}")
>>> git show-branch {args}
def sparse_checkout(self, args: str = '') -> str | int:
297    def sparse_checkout(self, args: str = "") -> str | int:
298        """>>> git sparse-checkout {args}"""
299        return self.git(f"sparse-checkout {args}")
>>> git sparse-checkout {args}
def stash(self, args: str = '') -> str | int:
301    def stash(self, args: str = "") -> str | int:
302        """>>> git stash {args}"""
303        return self.git(f"stash {args}")
>>> git stash {args}
def status(self, args: str = '') -> str | int:
305    def status(self, args: str = "") -> str | int:
306        """>>> git status {args}"""
307        return self.git(f"status {args}")
>>> git status {args}
def submodule(self, args: str = '') -> str | int:
309    def submodule(self, args: str = "") -> str | int:
310        """>>> git submodule {args}"""
311        return self.git(f"submodule {args}")
>>> git submodule {args}
def switch(self, args: str = '') -> str | int:
313    def switch(self, args: str = "") -> str | int:
314        """>>> git switch {args}"""
315        return self.git(f"switch {args}")
>>> git switch {args}
def tag(self, args: str = '') -> str | int:
317    def tag(self, args: str = "") -> str | int:
318        """>>> git tag {args}"""
319        return self.git(f"tag {args}")
>>> git tag {args}
def verify_commit(self, args: str = '') -> str | int:
321    def verify_commit(self, args: str = "") -> str | int:
322        """>>> git verify-commit {args}"""
323        return self.git(f"verify-commit {args}")
>>> git verify-commit {args}
def verify_tag(self, args: str = '') -> str | int:
325    def verify_tag(self, args: str = "") -> str | int:
326        """>>> git verify-tag {args}"""
327        return self.git(f"verify-tag {args}")
>>> git verify-tag {args}
def version(self, args: str = '') -> str | int:
329    def version(self, args: str = "") -> str | int:
330        """>>> git version {args}"""
331        return self.git(f"version {args}")
>>> git version {args}
def whatchanged(self, args: str = '') -> str | int:
333    def whatchanged(self, args: str = "") -> str | int:
334        """>>> git whatchanged {args}"""
335        return self.git(f"whatchanged {args}")
>>> git whatchanged {args}
def worktree(self, args: str = '') -> str | int:
337    def worktree(self, args: str = "") -> str | int:
338        """>>> git worktree {args}"""
339        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:
363    def add_all(self) -> str | int:
364        """Stage all modified and untracked files.
365        >>> git add ."""
366        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:
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)

Stage a list of files.

def add_remote_url(self, url: str, name: str = 'origin') -> str | int:
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}")

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:
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(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:
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

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:
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

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:
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")

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:
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

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 ignore(self, patterns: list[str]):
421    def ignore(self, patterns: list[str]):
422        """Add `patterns` to `.gitignore`."""
423        gitignore = Pathier(".gitignore")
424        if not gitignore.exists():
425            gitignore.touch()
426        ignores = gitignore.split()
427        ignores += [pattern for pattern in patterns if pattern not in ignores]
428        gitignore.join(ignores)

Add patterns to .gitignore.

def initcommit( self, files: list[pathier.pathier.Pathier | pathlib.Path | str] | None = None) -> str | int:
430    def initcommit(self, files: list[Pathish] | None = None) -> str | int:
431        """Stage and commit `files` with the message `Initial commit`.
432
433        If `files` is not given, all files will be added and committed.
434        >>> git add {files} or git add .
435        >>> git commit -m "Initial commit" """
436        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:
438    def list_branches(self) -> str | int:
439        """>>> git branch -vva"""
440        return self.branch("-vva")
>>> git branch -vva
def loggy(self) -> str | int:
442    def loggy(self) -> str | int:
443        """>>> git log --oneline --name-only --abbrev-commit --graph"""
444        return self.log("--oneline --name-only --abbrev-commit --graph")
>>> git log --oneline --name-only --abbrev-commit --graph
def new_repo(self) -> str | int:
446    def new_repo(self) -> str | int:
447        """Initialize a new repo in current directory.
448        >>> git init -b main"""
449        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:
451    def push_new_branch(self, branch: str) -> str | int:
452        """Push a new branch to origin with tracking.
453        >>> git push -u origin {branch}"""
454        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:
456    def switch_branch(self, branch_name: str) -> str | int:
457        """Switch to the branch specified by `branch_name`.
458        >>> git checkout {branch_name}"""
459        return self.checkout(branch_name)

Switch to the branch specified by branch_name.

>>> git checkout {branch_name}
def undo(self) -> str | int:
461    def undo(self) -> str | int:
462        """Undo uncommitted changes.
463        >>> git checkout ."""
464        return self.checkout(".")

Undo uncommitted changes.

>>> git checkout .
def create_remote(self, name: str, public: bool = False) -> str | int:
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])

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:
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        )

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:
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        )

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

def make_private(self) -> str | int:
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")

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

def make_public(self) -> str | int:
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")

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