gitbetter.git

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

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

>>> git {command}
def add(self, args: str = '') -> morbin.morbin.Output:
20    def add(self, args: str = "") -> Output:
21        """>>> git add {args}"""
22        return self.git(f"add {args}")
>>> git add {args}
def am(self, args: str = '') -> morbin.morbin.Output:
24    def am(self, args: str = "") -> Output:
25        """>>> git am {args}"""
26        return self.git(f"am {args}")
>>> git am {args}
def annotate(self, args: str = '') -> morbin.morbin.Output:
28    def annotate(self, args: str = "") -> Output:
29        """>>> git annotate {args}"""
30        return self.git(f"annotate {args}")
>>> git annotate {args}
def archive(self, args: str = '') -> morbin.morbin.Output:
32    def archive(self, args: str = "") -> Output:
33        """>>> git archive {args}"""
34        return self.git(f"archive {args}")
>>> git archive {args}
def bisect(self, args: str = '') -> morbin.morbin.Output:
36    def bisect(self, args: str = "") -> Output:
37        """>>> git bisect {args}"""
38        return self.git(f"bisect {args}")
>>> git bisect {args}
def blame(self, args: str = '') -> morbin.morbin.Output:
40    def blame(self, args: str = "") -> Output:
41        """>>> git blame {args}"""
42        return self.git(f"blame {args}")
>>> git blame {args}
def branch(self, args: str = '') -> morbin.morbin.Output:
44    def branch(self, args: str = "") -> Output:
45        """>>> git branch {args}"""
46        return self.git(f"branch {args}")
>>> git branch {args}
def bugreport(self, args: str = '') -> morbin.morbin.Output:
48    def bugreport(self, args: str = "") -> Output:
49        """>>> git bugreport {args}"""
50        return self.git(f"bugreport {args}")
>>> git bugreport {args}
def bundle(self, args: str = '') -> morbin.morbin.Output:
52    def bundle(self, args: str = "") -> Output:
53        """>>> git bundle {args}"""
54        return self.git(f"bundle {args}")
>>> git bundle {args}
def checkout(self, args: str = '') -> morbin.morbin.Output:
56    def checkout(self, args: str = "") -> Output:
57        """>>> git checkout {args}"""
58        return self.git(f"checkout {args}")
>>> git checkout {args}
def cherry_pick(self, args: str = '') -> morbin.morbin.Output:
60    def cherry_pick(self, args: str = "") -> Output:
61        """>>> git cherry-pick {args}"""
62        return self.git(f"cherry-pick {args}")
>>> git cherry-pick {args}
def citool(self, args: str = '') -> morbin.morbin.Output:
64    def citool(self, args: str = "") -> Output:
65        """>>> git citool {args}"""
66        return self.git(f"citool {args}")
>>> git citool {args}
def clean(self, args: str = '') -> morbin.morbin.Output:
68    def clean(self, args: str = "") -> Output:
69        """>>> git clean {args}"""
70        return self.git(f"clean {args}")
>>> git clean {args}
def clone(self, args: str = '') -> morbin.morbin.Output:
72    def clone(self, args: str = "") -> Output:
73        """>>> git clone {args}"""
74        return self.git(f"clone {args}")
>>> git clone {args}
def commit(self, args: str = '') -> morbin.morbin.Output:
76    def commit(self, args: str = "") -> Output:
77        """>>> git commit {args}"""
78        return self.git(f"commit {args}")
>>> git commit {args}
def config(self, args: str = '') -> morbin.morbin.Output:
80    def config(self, args: str = "") -> Output:
81        """>>> git config {args}"""
82        return self.git(f"config {args}")
>>> git config {args}
def count_objects(self, args: str = '') -> morbin.morbin.Output:
84    def count_objects(self, args: str = "") -> Output:
85        """>>> git count-objects {args}"""
86        return self.git(f"count-objects {args}")
>>> git count-objects {args}
def describe(self, args: str = '') -> morbin.morbin.Output:
88    def describe(self, args: str = "") -> Output:
89        """>>> git describe {args}"""
90        return self.git(f"describe {args}")
>>> git describe {args}
def diagnose(self, args: str = '') -> morbin.morbin.Output:
92    def diagnose(self, args: str = "") -> Output:
93        """>>> git diagnose {args}"""
94        return self.git(f"diagnose {args}")
>>> git diagnose {args}
def diff(self, args: str = '') -> morbin.morbin.Output:
96    def diff(self, args: str = "") -> Output:
97        """>>> git diff {args}"""
98        return self.git(f"diff {args}")
>>> git diff {args}
def difftool(self, args: str = '') -> morbin.morbin.Output:
100    def difftool(self, args: str = "") -> Output:
101        """>>> git difftool {args}"""
102        return self.git(f"difftool {args}")
>>> git difftool {args}
def fast_export(self, args: str = '') -> morbin.morbin.Output:
104    def fast_export(self, args: str = "") -> Output:
105        """>>> git fast-export {args}"""
106        return self.git(f"fast-export {args}")
>>> git fast-export {args}
def fast_import(self, args: str = '') -> morbin.morbin.Output:
108    def fast_import(self, args: str = "") -> Output:
109        """>>> git fast-import {args}"""
110        return self.git(f"fast-import {args}")
>>> git fast-import {args}
def fetch(self, args: str = '') -> morbin.morbin.Output:
112    def fetch(self, args: str = "") -> Output:
113        """>>> git fetch {args}"""
114        return self.git(f"fetch {args}")
>>> git fetch {args}
def filter_branch(self, args: str = '') -> morbin.morbin.Output:
116    def filter_branch(self, args: str = "") -> Output:
117        """>>> git filter-branch {args}"""
118        return self.git(f"filter-branch {args}")
>>> git filter-branch {args}
def format_patch(self, args: str = '') -> morbin.morbin.Output:
120    def format_patch(self, args: str = "") -> Output:
121        """>>> git format-patch {args}"""
122        return self.git(f"format-patch {args}")
>>> git format-patch {args}
def fsck(self, args: str = '') -> morbin.morbin.Output:
124    def fsck(self, args: str = "") -> Output:
125        """>>> git fsck {args}"""
126        return self.git(f"fsck {args}")
>>> git fsck {args}
def gc(self, args: str = '') -> morbin.morbin.Output:
128    def gc(self, args: str = "") -> Output:
129        """>>> git gc {args}"""
130        return self.git(f"gc {args}")
>>> git gc {args}
def gitk(self, args: str = '') -> morbin.morbin.Output:
132    def gitk(self, args: str = "") -> Output:
133        """>>> git gitk {args}"""
134        return self.git(f"gitk {args}")
>>> git gitk {args}
def gitweb(self, args: str = '') -> morbin.morbin.Output:
136    def gitweb(self, args: str = "") -> Output:
137        """>>> git gitweb {args}"""
138        return self.git(f"gitweb {args}")
>>> git gitweb {args}
def grep(self, args: str = '') -> morbin.morbin.Output:
140    def grep(self, args: str = "") -> Output:
141        """>>> git grep {args}"""
142        return self.git(f"grep {args}")
>>> git grep {args}
def gui(self, args: str = '') -> morbin.morbin.Output:
144    def gui(self, args: str = "") -> Output:
145        """>>> git gui {args}"""
146        return self.git(f"gui {args}")
>>> git gui {args}
def help(self, args: str = '') -> morbin.morbin.Output:
148    def help(self, args: str = "") -> Output:
149        """>>> git help {args}"""
150        return self.git(f"help {args}")
>>> git help {args}
def init(self, args: str = '') -> morbin.morbin.Output:
152    def init(self, args: str = "") -> Output:
153        """>>> git init {args}"""
154        return self.git(f"init {args}")
>>> git init {args}
def instaweb(self, args: str = '') -> morbin.morbin.Output:
156    def instaweb(self, args: str = "") -> Output:
157        """>>> git instaweb {args}"""
158        return self.git(f"instaweb {args}")
>>> git instaweb {args}
def log(self, args: str = '') -> morbin.morbin.Output:
160    def log(self, args: str = "") -> Output:
161        """>>> git log {args}"""
162        return self.git(f"log {args}")
>>> git log {args}
def maintenance(self, args: str = '') -> morbin.morbin.Output:
164    def maintenance(self, args: str = "") -> Output:
165        """>>> git maintenance {args}"""
166        return self.git(f"maintenance {args}")
>>> git maintenance {args}
def merge(self, args: str = '') -> morbin.morbin.Output:
168    def merge(self, args: str = "") -> Output:
169        """>>> git merge {args}"""
170        return self.git(f"merge {args}")
>>> git merge {args}
def merge_tree(self, args: str = '') -> morbin.morbin.Output:
172    def merge_tree(self, args: str = "") -> Output:
173        """>>> git merge-tree {args}"""
174        return self.git(f"merge-tree {args}")
>>> git merge-tree {args}
def mergetool(self, args: str = '') -> morbin.morbin.Output:
176    def mergetool(self, args: str = "") -> Output:
177        """>>> git mergetool {args}"""
178        return self.git(f"mergetool {args}")
>>> git mergetool {args}
def mv(self, args: str = '') -> morbin.morbin.Output:
180    def mv(self, args: str = "") -> Output:
181        """>>> git mv {args}"""
182        return self.git(f"mv {args}")
>>> git mv {args}
def notes(self, args: str = '') -> morbin.morbin.Output:
184    def notes(self, args: str = "") -> Output:
185        """>>> git notes {args}"""
186        return self.git(f"notes {args}")
>>> git notes {args}
def pack_refs(self, args: str = '') -> morbin.morbin.Output:
188    def pack_refs(self, args: str = "") -> Output:
189        """>>> git pack-refs {args}"""
190        return self.git(f"pack-refs {args}")
>>> git pack-refs {args}
def prune(self, args: str = '') -> morbin.morbin.Output:
192    def prune(self, args: str = "") -> Output:
193        """>>> git prune {args}"""
194        return self.git(f"prune {args}")
>>> git prune {args}
def pull(self, args: str = '') -> morbin.morbin.Output:
196    def pull(self, args: str = "") -> Output:
197        """>>> git pull {args}"""
198        return self.git(f"pull {args}")
>>> git pull {args}
def push(self, args: str = '') -> morbin.morbin.Output:
200    def push(self, args: str = "") -> Output:
201        """>>> git push {args}"""
202        return self.git(f"push {args}")
>>> git push {args}
def range_diff(self, args: str = '') -> morbin.morbin.Output:
204    def range_diff(self, args: str = "") -> Output:
205        """>>> git range-diff {args}"""
206        return self.git(f"range-diff {args}")
>>> git range-diff {args}
def rebase(self, args: str = '') -> morbin.morbin.Output:
208    def rebase(self, args: str = "") -> Output:
209        """>>> git rebase {args}"""
210        return self.git(f"rebase {args}")
>>> git rebase {args}
def reflog(self, args: str = '') -> morbin.morbin.Output:
212    def reflog(self, args: str = "") -> Output:
213        """>>> git reflog {args}"""
214        return self.git(f"reflog {args}")
>>> git reflog {args}
def remote(self, args: str = '') -> morbin.morbin.Output:
216    def remote(self, args: str = "") -> Output:
217        """>>> git remote {args}"""
218        return self.git(f"remote {args}")
>>> git remote {args}
def repack(self, args: str = '') -> morbin.morbin.Output:
220    def repack(self, args: str = "") -> Output:
221        """>>> git repack {args}"""
222        return self.git(f"repack {args}")
>>> git repack {args}
def replace(self, args: str = '') -> morbin.morbin.Output:
224    def replace(self, args: str = "") -> Output:
225        """>>> git replace {args}"""
226        return self.git(f"replace {args}")
>>> git replace {args}
def request_pull(self, args: str = '') -> morbin.morbin.Output:
228    def request_pull(self, args: str = "") -> Output:
229        """>>> git request-pull {args}"""
230        return self.git(f"request-pull {args}")
>>> git request-pull {args}
def rerere(self, args: str = '') -> morbin.morbin.Output:
232    def rerere(self, args: str = "") -> Output:
233        """>>> git rerere {args}"""
234        return self.git(f"rerere {args}")
>>> git rerere {args}
def reset(self, args: str = '') -> morbin.morbin.Output:
236    def reset(self, args: str = "") -> Output:
237        """>>> git reset {args}"""
238        return self.git(f"reset {args}")
>>> git reset {args}
def restore(self, args: str = '') -> morbin.morbin.Output:
240    def restore(self, args: str = "") -> Output:
241        """>>> git restore {args}"""
242        return self.git(f"restore {args}")
>>> git restore {args}
def revert(self, args: str = '') -> morbin.morbin.Output:
244    def revert(self, args: str = "") -> Output:
245        """>>> git revert {args}"""
246        return self.git(f"revert {args}")
>>> git revert {args}
def rm(self, args: str = '') -> morbin.morbin.Output:
248    def rm(self, args: str = "") -> Output:
249        """>>> git rm {args}"""
250        return self.git(f"rm {args}")
>>> git rm {args}
def scalar(self, args: str = '') -> morbin.morbin.Output:
252    def scalar(self, args: str = "") -> Output:
253        """>>> git scalar {args}"""
254        return self.git(f"scalar {args}")
>>> git scalar {args}
def shortlog(self, args: str = '') -> morbin.morbin.Output:
256    def shortlog(self, args: str = "") -> Output:
257        """>>> git shortlog {args}"""
258        return self.git(f"shortlog {args}")
>>> git shortlog {args}
def show(self, args: str = '') -> morbin.morbin.Output:
260    def show(self, args: str = "") -> Output:
261        """>>> git show {args}"""
262        return self.git(f"show {args}")
>>> git show {args}
def show_branch(self, args: str = '') -> morbin.morbin.Output:
264    def show_branch(self, args: str = "") -> Output:
265        """>>> git show-branch {args}"""
266        return self.git(f"show-branch {args}")
>>> git show-branch {args}
def sparse_checkout(self, args: str = '') -> morbin.morbin.Output:
268    def sparse_checkout(self, args: str = "") -> Output:
269        """>>> git sparse-checkout {args}"""
270        return self.git(f"sparse-checkout {args}")
>>> git sparse-checkout {args}
def stash(self, args: str = '') -> morbin.morbin.Output:
272    def stash(self, args: str = "") -> Output:
273        """>>> git stash {args}"""
274        return self.git(f"stash {args}")
>>> git stash {args}
def status(self, args: str = '') -> morbin.morbin.Output:
276    def status(self, args: str = "") -> Output:
277        """>>> git status {args}"""
278        return self.git(f"status {args}")
>>> git status {args}
def submodule(self, args: str = '') -> morbin.morbin.Output:
280    def submodule(self, args: str = "") -> Output:
281        """>>> git submodule {args}"""
282        return self.git(f"submodule {args}")
>>> git submodule {args}
def switch(self, args: str = '') -> morbin.morbin.Output:
284    def switch(self, args: str = "") -> Output:
285        """>>> git switch {args}"""
286        return self.git(f"switch {args}")
>>> git switch {args}
def tag(self, args: str = '') -> morbin.morbin.Output:
288    def tag(self, args: str = "") -> Output:
289        """>>> git tag {args}"""
290        return self.git(f"tag {args}")
>>> git tag {args}
def verify_commit(self, args: str = '') -> morbin.morbin.Output:
292    def verify_commit(self, args: str = "") -> Output:
293        """>>> git verify-commit {args}"""
294        return self.git(f"verify-commit {args}")
>>> git verify-commit {args}
def verify_tag(self, args: str = '') -> morbin.morbin.Output:
296    def verify_tag(self, args: str = "") -> Output:
297        """>>> git verify-tag {args}"""
298        return self.git(f"verify-tag {args}")
>>> git verify-tag {args}
def version(self, args: str = '') -> morbin.morbin.Output:
300    def version(self, args: str = "") -> Output:
301        """>>> git version {args}"""
302        return self.git(f"version {args}")
>>> git version {args}
def whatchanged(self, args: str = '') -> morbin.morbin.Output:
304    def whatchanged(self, args: str = "") -> Output:
305        """>>> git whatchanged {args}"""
306        return self.git(f"whatchanged {args}")
>>> git whatchanged {args}
def worktree(self, args: str = '') -> morbin.morbin.Output:
308    def worktree(self, args: str = "") -> Output:
309        """>>> git worktree {args}"""
310        return self.git(f"worktree {args}")
>>> git worktree {args}
current_branch: str

Returns the name of the currently active branch.

dob: datetime.datetime

Date of this repo's first commit.

origin_url: morbin.morbin.Output

The remote origin url for this repo

>>> git remote get-url origin
def add_all(self) -> morbin.morbin.Output:
339    def add_all(self) -> Output:
340        """Stage all modified and untracked files.
341        >>> git add ."""
342        return self.add(".")

Stage all modified and untracked files.

>>> git add .
def add_files( self, files: list[pathier.pathier.Pathier | pathlib.Path | str]) -> morbin.morbin.Output:
344    def add_files(self, files: list[Pathish]) -> Output:
345        """Stage a list of files."""
346        args = " ".join([str(file) for file in files])
347        return self.add(args)

Stage a list of files.

def add_remote_url(self, url: str, name: str = 'origin') -> morbin.morbin.Output:
349    def add_remote_url(self, url: str, name: str = "origin") -> Output:
350        """Add remote url to repo.
351        >>> git remote add {name} {url}"""
352        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) -> morbin.morbin.Output:
354    def amend(self, files: list[Pathish] | None = None) -> Output:
355        """Stage and commit changes to the previous commit.
356
357        If `files` is `None`, all files will be staged.
358
359        >>> git add {files} or git add .
360        >>> git commit --amend --no-edit
361        """
362        return (self.add_files(files) if files else self.add_all()) + self.commit(
363            "--amend --no-edit"
364        )

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) -> morbin.morbin.Output:
366    def commit_all(self, message: str) -> Output:
367        """Stage and commit all files with `message`.
368        >>> git add .
369        >>> git commit -m "{message}" """
370        return self.add_all() + self.commit(f'-m "{message}"')

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) -> morbin.morbin.Output:
372    def commit_files(self, files: list[Pathish], message: str) -> Output:
373        """Commit a list of files or file patterns with commit message `message`.
374        >>> git commit {files} -m "{message}" """
375        files_arg = " ".join(str(file) for file in files)
376        return self.commit(f'{files_arg} -m "{message}"')

Commit a list of files or file patterns with commit message message.

>>> git commit {files} -m "{message}"
def create_new_branch(self, branch_name: str) -> morbin.morbin.Output:
378    def create_new_branch(self, branch_name: str) -> Output:
379        """Create and switch to a new branch named with `branch_name`.
380        >>> git checkout -b {branch_name} --track"""
381        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) -> morbin.morbin.Output:
383    def delete_branch(self, branch_name: str, local_only: bool = True) -> Output:
384        """Delete `branch_name` from repo.
385
386        #### :params:
387
388        `local_only`: Only delete the local copy of `branch`, otherwise also delete the remote branch on origin and remote-tracking branch.
389        >>> git branch --delete {branch_name}
390
391        Then if not `local_only`:
392        >>> git push origin --delete {branch_name}
393        """
394        output = self.branch(f"--delete {branch_name}")
395        if not local_only:
396            return output + self.push(f"origin --delete {branch_name}")
397        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]):
399    def ignore(self, patterns: list[str]):
400        """Add `patterns` to `.gitignore`."""
401        gitignore = Pathier(".gitignore")
402        if not gitignore.exists():
403            gitignore.touch()
404        ignores = gitignore.split()
405        ignores += [pattern for pattern in patterns if pattern not in ignores]
406        gitignore.join(ignores)

Add patterns to .gitignore.

def initcommit( self, files: list[pathier.pathier.Pathier | pathlib.Path | str] | None = None) -> morbin.morbin.Output:
408    def initcommit(self, files: list[Pathish] | None = None) -> Output:
409        """Stage and commit `files` with the message `Initial commit`.
410
411        If `files` is not given, all files will be added and committed.
412        >>> git add {files} or git add .
413        >>> git commit -m "Initial commit" """
414        return (self.add_files(files) if files else self.add_all()) + self.commit(
415            '-m "Initial commit"'
416        )

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) -> morbin.morbin.Output:
418    def list_branches(self) -> Output:
419        """>>> git branch -vva"""
420        return self.branch("-vva")
>>> git branch -vva
def loggy(self) -> morbin.morbin.Output:
422    def loggy(self) -> Output:
423        """>>> git log --graph --abbrev-commit --name-only --pretty=tformat:'%C(auto)%h %C(green)(%cs|%cr)%C(auto)%d %C(magenta)%s'"""
424        return self.log(
425            "--graph --abbrev-commit --name-only --pretty=tformat:'%C(auto)%h %C(green)(%cs|%cr)%C(auto)%d %C(magenta)%s'"
426        )
>>> git log --graph --abbrev-commit --name-only --pretty=tformat:'%C(auto)%h %C(green)(%cs|%cr)%C(auto)%d %C(magenta)%s'
def new_repo(self) -> morbin.morbin.Output:
428    def new_repo(self) -> Output:
429        """Initialize a new repo in current directory.
430        >>> git init -b main"""
431        return self.init("-b main")

Initialize a new repo in current directory.

>>> git init -b main
def push_new_branch(self, branch: str) -> morbin.morbin.Output:
433    def push_new_branch(self, branch: str) -> Output:
434        """Push a new branch to origin with tracking.
435        >>> git push -u origin {branch}"""
436        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) -> morbin.morbin.Output:
438    def switch_branch(self, branch_name: str) -> Output:
439        """Switch to the branch specified by `branch_name`.
440        >>> git checkout {branch_name}"""
441        return self.checkout(branch_name)

Switch to the branch specified by branch_name.

>>> git checkout {branch_name}
def undo(self) -> morbin.morbin.Output:
443    def undo(self) -> Output:
444        """Undo uncommitted changes.
445        >>> git checkout ."""
446        return self.checkout(".")

Undo uncommitted changes.

>>> git checkout .
def untrack( self, *paths: pathier.pathier.Pathier | pathlib.Path | str) -> morbin.morbin.Output:
448    def untrack(self, *paths: Pathish) -> Output:
449        """Remove any number of `paths` from the index.
450
451        Equivalent to
452        >>> git rm --cached {path}
453
454        for each path in `paths`."""
455        paths_ = [str(path) for path in paths]
456        return sum(
457            [self.rm(f"--cached {path}") for path in paths_[1:]],
458            self.rm(f"--cached {paths_[0]}"),
459        )

Remove any number of paths from the index.

Equivalent to

>>> git rm --cached {path}

for each path in paths.

def rename_file( self, file: pathier.pathier.Pathier | pathlib.Path | str, new_name: str) -> morbin.morbin.Output:
461    def rename_file(self, file: Pathish, new_name: str) -> Output:
462        """Rename `file` to `new_name` and add renaming to staging index.
463
464        `new_name` should include the file suffix.
465
466        Equivalent to renaming `old_file.py` to `new_file.py` then executing
467        >>> git add new_file.py
468        >>> git rm old_file.py"""
469        file = Pathier(file)
470        new_file = file.replace(file.with_name(new_name))
471        return self.add_files([new_file]) + self.rm(str(file))

Rename file to new_name and add renaming to staging index.

new_name should include the file suffix.

Equivalent to renaming old_file.py to new_file.py then executing

>>> git add new_file.py
>>> git rm old_file.py
def create_remote(self, name: str, public: bool = False) -> morbin.morbin.Output:
493    def create_remote(self, name: str, public: bool = False) -> Output:
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.execute("gh", f"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) -> morbin.morbin.Output:
505    def create_remote_from_cwd(self, public: bool = False) -> Output:
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.execute("gh", f"repo create --source . {visibility} --push")

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) -> morbin.morbin.Output:
516    def delete_remote(self) -> Output:
517        """Uses GitHub CLI (must be isntalled and configured) to delete the remote for this repo."""
518        return self.execute("gh", f"repo delete {self.owner}/{self.repo_name} --yes")

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

def make_private(self) -> morbin.morbin.Output:
520    def make_private(self) -> Output:
521        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to private."""
522        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) -> morbin.morbin.Output:
524    def make_public(self) -> Output:
525        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to public."""
526        return self._change_visibility("public")

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

Inherited Members
morbin.morbin.Morbin
Morbin
capture_output
shell
capturing_output
execute