gitbetter.git

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

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:
19    def add(self, args: str = "") -> Output:
20        """>>> git add {args}"""
21        return self.git(f"add {args}")
>>> git add {args}
def am(self, args: str = '') -> morbin.morbin.Output:
23    def am(self, args: str = "") -> Output:
24        """>>> git am {args}"""
25        return self.git(f"am {args}")
>>> git am {args}
def annotate(self, args: str = '') -> morbin.morbin.Output:
27    def annotate(self, args: str = "") -> Output:
28        """>>> git annotate {args}"""
29        return self.git(f"annotate {args}")
>>> git annotate {args}
def archive(self, args: str = '') -> morbin.morbin.Output:
31    def archive(self, args: str = "") -> Output:
32        """>>> git archive {args}"""
33        return self.git(f"archive {args}")
>>> git archive {args}
def bisect(self, args: str = '') -> morbin.morbin.Output:
35    def bisect(self, args: str = "") -> Output:
36        """>>> git bisect {args}"""
37        return self.git(f"bisect {args}")
>>> git bisect {args}
def blame(self, args: str = '') -> morbin.morbin.Output:
39    def blame(self, args: str = "") -> Output:
40        """>>> git blame {args}"""
41        return self.git(f"blame {args}")
>>> git blame {args}
def branch(self, args: str = '') -> morbin.morbin.Output:
43    def branch(self, args: str = "") -> Output:
44        """>>> git branch {args}"""
45        return self.git(f"branch {args}")
>>> git branch {args}
def bugreport(self, args: str = '') -> morbin.morbin.Output:
47    def bugreport(self, args: str = "") -> Output:
48        """>>> git bugreport {args}"""
49        return self.git(f"bugreport {args}")
>>> git bugreport {args}
def bundle(self, args: str = '') -> morbin.morbin.Output:
51    def bundle(self, args: str = "") -> Output:
52        """>>> git bundle {args}"""
53        return self.git(f"bundle {args}")
>>> git bundle {args}
def checkout(self, args: str = '') -> morbin.morbin.Output:
55    def checkout(self, args: str = "") -> Output:
56        """>>> git checkout {args}"""
57        return self.git(f"checkout {args}")
>>> git checkout {args}
def cherry_pick(self, args: str = '') -> morbin.morbin.Output:
59    def cherry_pick(self, args: str = "") -> Output:
60        """>>> git cherry-pick {args}"""
61        return self.git(f"cherry-pick {args}")
>>> git cherry-pick {args}
def citool(self, args: str = '') -> morbin.morbin.Output:
63    def citool(self, args: str = "") -> Output:
64        """>>> git citool {args}"""
65        return self.git(f"citool {args}")
>>> git citool {args}
def clean(self, args: str = '') -> morbin.morbin.Output:
67    def clean(self, args: str = "") -> Output:
68        """>>> git clean {args}"""
69        return self.git(f"clean {args}")
>>> git clean {args}
def clone(self, args: str = '') -> morbin.morbin.Output:
71    def clone(self, args: str = "") -> Output:
72        """>>> git clone {args}"""
73        return self.git(f"clone {args}")
>>> git clone {args}
def commit(self, args: str = '') -> morbin.morbin.Output:
75    def commit(self, args: str = "") -> Output:
76        """>>> git commit {args}"""
77        return self.git(f"commit {args}")
>>> git commit {args}
def config(self, args: str = '') -> morbin.morbin.Output:
79    def config(self, args: str = "") -> Output:
80        """>>> git config {args}"""
81        return self.git(f"config {args}")
>>> git config {args}
def count_objects(self, args: str = '') -> morbin.morbin.Output:
83    def count_objects(self, args: str = "") -> Output:
84        """>>> git count-objects {args}"""
85        return self.git(f"count-objects {args}")
>>> git count-objects {args}
def describe(self, args: str = '') -> morbin.morbin.Output:
87    def describe(self, args: str = "") -> Output:
88        """>>> git describe {args}"""
89        return self.git(f"describe {args}")
>>> git describe {args}
def diagnose(self, args: str = '') -> morbin.morbin.Output:
91    def diagnose(self, args: str = "") -> Output:
92        """>>> git diagnose {args}"""
93        return self.git(f"diagnose {args}")
>>> git diagnose {args}
def diff(self, args: str = '') -> morbin.morbin.Output:
95    def diff(self, args: str = "") -> Output:
96        """>>> git diff {args}"""
97        return self.git(f"diff {args}")
>>> git diff {args}
def difftool(self, args: str = '') -> morbin.morbin.Output:
 99    def difftool(self, args: str = "") -> Output:
100        """>>> git difftool {args}"""
101        return self.git(f"difftool {args}")
>>> git difftool {args}
def fast_export(self, args: str = '') -> morbin.morbin.Output:
103    def fast_export(self, args: str = "") -> Output:
104        """>>> git fast-export {args}"""
105        return self.git(f"fast-export {args}")
>>> git fast-export {args}
def fast_import(self, args: str = '') -> morbin.morbin.Output:
107    def fast_import(self, args: str = "") -> Output:
108        """>>> git fast-import {args}"""
109        return self.git(f"fast-import {args}")
>>> git fast-import {args}
def fetch(self, args: str = '') -> morbin.morbin.Output:
111    def fetch(self, args: str = "") -> Output:
112        """>>> git fetch {args}"""
113        return self.git(f"fetch {args}")
>>> git fetch {args}
def filter_branch(self, args: str = '') -> morbin.morbin.Output:
115    def filter_branch(self, args: str = "") -> Output:
116        """>>> git filter-branch {args}"""
117        return self.git(f"filter-branch {args}")
>>> git filter-branch {args}
def format_patch(self, args: str = '') -> morbin.morbin.Output:
119    def format_patch(self, args: str = "") -> Output:
120        """>>> git format-patch {args}"""
121        return self.git(f"format-patch {args}")
>>> git format-patch {args}
def fsck(self, args: str = '') -> morbin.morbin.Output:
123    def fsck(self, args: str = "") -> Output:
124        """>>> git fsck {args}"""
125        return self.git(f"fsck {args}")
>>> git fsck {args}
def gc(self, args: str = '') -> morbin.morbin.Output:
127    def gc(self, args: str = "") -> Output:
128        """>>> git gc {args}"""
129        return self.git(f"gc {args}")
>>> git gc {args}
def gitk(self, args: str = '') -> morbin.morbin.Output:
131    def gitk(self, args: str = "") -> Output:
132        """>>> git gitk {args}"""
133        return self.git(f"gitk {args}")
>>> git gitk {args}
def gitweb(self, args: str = '') -> morbin.morbin.Output:
135    def gitweb(self, args: str = "") -> Output:
136        """>>> git gitweb {args}"""
137        return self.git(f"gitweb {args}")
>>> git gitweb {args}
def grep(self, args: str = '') -> morbin.morbin.Output:
139    def grep(self, args: str = "") -> Output:
140        """>>> git grep {args}"""
141        return self.git(f"grep {args}")
>>> git grep {args}
def gui(self, args: str = '') -> morbin.morbin.Output:
143    def gui(self, args: str = "") -> Output:
144        """>>> git gui {args}"""
145        return self.git(f"gui {args}")
>>> git gui {args}
def help(self, args: str = '') -> morbin.morbin.Output:
147    def help(self, args: str = "") -> Output:
148        """>>> git help {args}"""
149        return self.git(f"help {args}")
>>> git help {args}
def init(self, args: str = '') -> morbin.morbin.Output:
151    def init(self, args: str = "") -> Output:
152        """>>> git init {args}"""
153        return self.git(f"init {args}")
>>> git init {args}
def instaweb(self, args: str = '') -> morbin.morbin.Output:
155    def instaweb(self, args: str = "") -> Output:
156        """>>> git instaweb {args}"""
157        return self.git(f"instaweb {args}")
>>> git instaweb {args}
def log(self, args: str = '') -> morbin.morbin.Output:
159    def log(self, args: str = "") -> Output:
160        """>>> git log {args}"""
161        return self.git(f"log {args}")
>>> git log {args}
def maintenance(self, args: str = '') -> morbin.morbin.Output:
163    def maintenance(self, args: str = "") -> Output:
164        """>>> git maintenance {args}"""
165        return self.git(f"maintenance {args}")
>>> git maintenance {args}
def merge(self, args: str = '') -> morbin.morbin.Output:
167    def merge(self, args: str = "") -> Output:
168        """>>> git merge {args}"""
169        return self.git(f"merge {args}")
>>> git merge {args}
def merge_tree(self, args: str = '') -> morbin.morbin.Output:
171    def merge_tree(self, args: str = "") -> Output:
172        """>>> git merge-tree {args}"""
173        return self.git(f"merge-tree {args}")
>>> git merge-tree {args}
def mergetool(self, args: str = '') -> morbin.morbin.Output:
175    def mergetool(self, args: str = "") -> Output:
176        """>>> git mergetool {args}"""
177        return self.git(f"mergetool {args}")
>>> git mergetool {args}
def mv(self, args: str = '') -> morbin.morbin.Output:
179    def mv(self, args: str = "") -> Output:
180        """>>> git mv {args}"""
181        return self.git(f"mv {args}")
>>> git mv {args}
def notes(self, args: str = '') -> morbin.morbin.Output:
183    def notes(self, args: str = "") -> Output:
184        """>>> git notes {args}"""
185        return self.git(f"notes {args}")
>>> git notes {args}
def pack_refs(self, args: str = '') -> morbin.morbin.Output:
187    def pack_refs(self, args: str = "") -> Output:
188        """>>> git pack-refs {args}"""
189        return self.git(f"pack-refs {args}")
>>> git pack-refs {args}
def prune(self, args: str = '') -> morbin.morbin.Output:
191    def prune(self, args: str = "") -> Output:
192        """>>> git prune {args}"""
193        return self.git(f"prune {args}")
>>> git prune {args}
def pull(self, args: str = '') -> morbin.morbin.Output:
195    def pull(self, args: str = "") -> Output:
196        """>>> git pull {args}"""
197        return self.git(f"pull {args}")
>>> git pull {args}
def push(self, args: str = '') -> morbin.morbin.Output:
199    def push(self, args: str = "") -> Output:
200        """>>> git push {args}"""
201        return self.git(f"push {args}")
>>> git push {args}
def range_diff(self, args: str = '') -> morbin.morbin.Output:
203    def range_diff(self, args: str = "") -> Output:
204        """>>> git range-diff {args}"""
205        return self.git(f"range-diff {args}")
>>> git range-diff {args}
def rebase(self, args: str = '') -> morbin.morbin.Output:
207    def rebase(self, args: str = "") -> Output:
208        """>>> git rebase {args}"""
209        return self.git(f"rebase {args}")
>>> git rebase {args}
def reflog(self, args: str = '') -> morbin.morbin.Output:
211    def reflog(self, args: str = "") -> Output:
212        """>>> git reflog {args}"""
213        return self.git(f"reflog {args}")
>>> git reflog {args}
def remote(self, args: str = '') -> morbin.morbin.Output:
215    def remote(self, args: str = "") -> Output:
216        """>>> git remote {args}"""
217        return self.git(f"remote {args}")
>>> git remote {args}
def repack(self, args: str = '') -> morbin.morbin.Output:
219    def repack(self, args: str = "") -> Output:
220        """>>> git repack {args}"""
221        return self.git(f"repack {args}")
>>> git repack {args}
def replace(self, args: str = '') -> morbin.morbin.Output:
223    def replace(self, args: str = "") -> Output:
224        """>>> git replace {args}"""
225        return self.git(f"replace {args}")
>>> git replace {args}
def request_pull(self, args: str = '') -> morbin.morbin.Output:
227    def request_pull(self, args: str = "") -> Output:
228        """>>> git request-pull {args}"""
229        return self.git(f"request-pull {args}")
>>> git request-pull {args}
def rerere(self, args: str = '') -> morbin.morbin.Output:
231    def rerere(self, args: str = "") -> Output:
232        """>>> git rerere {args}"""
233        return self.git(f"rerere {args}")
>>> git rerere {args}
def reset(self, args: str = '') -> morbin.morbin.Output:
235    def reset(self, args: str = "") -> Output:
236        """>>> git reset {args}"""
237        return self.git(f"reset {args}")
>>> git reset {args}
def restore(self, args: str = '') -> morbin.morbin.Output:
239    def restore(self, args: str = "") -> Output:
240        """>>> git restore {args}"""
241        return self.git(f"restore {args}")
>>> git restore {args}
def revert(self, args: str = '') -> morbin.morbin.Output:
243    def revert(self, args: str = "") -> Output:
244        """>>> git revert {args}"""
245        return self.git(f"revert {args}")
>>> git revert {args}
def rm(self, args: str = '') -> morbin.morbin.Output:
247    def rm(self, args: str = "") -> Output:
248        """>>> git rm {args}"""
249        return self.git(f"rm {args}")
>>> git rm {args}
def scalar(self, args: str = '') -> morbin.morbin.Output:
251    def scalar(self, args: str = "") -> Output:
252        """>>> git scalar {args}"""
253        return self.git(f"scalar {args}")
>>> git scalar {args}
def shortlog(self, args: str = '') -> morbin.morbin.Output:
255    def shortlog(self, args: str = "") -> Output:
256        """>>> git shortlog {args}"""
257        return self.git(f"shortlog {args}")
>>> git shortlog {args}
def show(self, args: str = '') -> morbin.morbin.Output:
259    def show(self, args: str = "") -> Output:
260        """>>> git show {args}"""
261        return self.git(f"show {args}")
>>> git show {args}
def show_branch(self, args: str = '') -> morbin.morbin.Output:
263    def show_branch(self, args: str = "") -> Output:
264        """>>> git show-branch {args}"""
265        return self.git(f"show-branch {args}")
>>> git show-branch {args}
def sparse_checkout(self, args: str = '') -> morbin.morbin.Output:
267    def sparse_checkout(self, args: str = "") -> Output:
268        """>>> git sparse-checkout {args}"""
269        return self.git(f"sparse-checkout {args}")
>>> git sparse-checkout {args}
def stash(self, args: str = '') -> morbin.morbin.Output:
271    def stash(self, args: str = "") -> Output:
272        """>>> git stash {args}"""
273        return self.git(f"stash {args}")
>>> git stash {args}
def status(self, args: str = '') -> morbin.morbin.Output:
275    def status(self, args: str = "") -> Output:
276        """>>> git status {args}"""
277        return self.git(f"status {args}")
>>> git status {args}
def submodule(self, args: str = '') -> morbin.morbin.Output:
279    def submodule(self, args: str = "") -> Output:
280        """>>> git submodule {args}"""
281        return self.git(f"submodule {args}")
>>> git submodule {args}
def switch(self, args: str = '') -> morbin.morbin.Output:
283    def switch(self, args: str = "") -> Output:
284        """>>> git switch {args}"""
285        return self.git(f"switch {args}")
>>> git switch {args}
def tag(self, args: str = '') -> morbin.morbin.Output:
287    def tag(self, args: str = "") -> Output:
288        """>>> git tag {args}"""
289        return self.git(f"tag {args}")
>>> git tag {args}
def verify_commit(self, args: str = '') -> morbin.morbin.Output:
291    def verify_commit(self, args: str = "") -> Output:
292        """>>> git verify-commit {args}"""
293        return self.git(f"verify-commit {args}")
>>> git verify-commit {args}
def verify_tag(self, args: str = '') -> morbin.morbin.Output:
295    def verify_tag(self, args: str = "") -> Output:
296        """>>> git verify-tag {args}"""
297        return self.git(f"verify-tag {args}")
>>> git verify-tag {args}
def version(self, args: str = '') -> morbin.morbin.Output:
299    def version(self, args: str = "") -> Output:
300        """>>> git version {args}"""
301        return self.git(f"version {args}")
>>> git version {args}
def whatchanged(self, args: str = '') -> morbin.morbin.Output:
303    def whatchanged(self, args: str = "") -> Output:
304        """>>> git whatchanged {args}"""
305        return self.git(f"whatchanged {args}")
>>> git whatchanged {args}
def worktree(self, args: str = '') -> morbin.morbin.Output:
307    def worktree(self, args: str = "") -> Output:
308        """>>> git worktree {args}"""
309        return self.git(f"worktree {args}")
>>> git worktree {args}
current_branch: str

Returns the name of the currently active branch.

origin_url: morbin.morbin.Output

The remote origin url for this repo

>>> git remote get-url origin
def add_all(self) -> morbin.morbin.Output:
331    def add_all(self) -> Output:
332        """Stage all modified and untracked files.
333        >>> git add ."""
334        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:
336    def add_files(self, files: list[Pathish]) -> Output:
337        """Stage a list of files."""
338        args = " ".join([str(file) for file in files])
339        return self.add(args)

Stage a list of files.

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

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:
358    def commit_all(self, message: str) -> Output:
359        """Stage and commit all files with `message`.
360        >>> git add .
361        >>> git commit -m "{message}" """
362        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:
364    def commit_files(self, files: list[Pathish], message: str) -> Output:
365        """Commit a list of files or file patterns with commit message `message`.
366        >>> git commit {files} -m "{message}" """
367        files_arg = " ".join(str(file) for file in files)
368        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:
370    def create_new_branch(self, branch_name: str) -> Output:
371        """Create and switch to a new branch named with `branch_name`.
372        >>> git checkout -b {branch_name} --track"""
373        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:
375    def delete_branch(self, branch_name: str, local_only: bool = True) -> Output:
376        """Delete `branch_name` from repo.
377
378        #### :params:
379
380        `local_only`: Only delete the local copy of `branch`, otherwise also delete the remote branch on origin and remote-tracking branch.
381        >>> git branch --delete {branch_name}
382
383        Then if not `local_only`:
384        >>> git push origin --delete {branch_name}
385        """
386        output = self.branch(f"--delete {branch_name}")
387        if not local_only:
388            return output + self.push(f"origin --delete {branch_name}")
389        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]):
391    def ignore(self, patterns: list[str]):
392        """Add `patterns` to `.gitignore`."""
393        gitignore = Pathier(".gitignore")
394        if not gitignore.exists():
395            gitignore.touch()
396        ignores = gitignore.split()
397        ignores += [pattern for pattern in patterns if pattern not in ignores]
398        gitignore.join(ignores)

Add patterns to .gitignore.

def initcommit( self, files: list[pathier.pathier.Pathier | pathlib.Path | str] | None = None) -> morbin.morbin.Output:
400    def initcommit(self, files: list[Pathish] | None = None) -> Output:
401        """Stage and commit `files` with the message `Initial commit`.
402
403        If `files` is not given, all files will be added and committed.
404        >>> git add {files} or git add .
405        >>> git commit -m "Initial commit" """
406        return (self.add_files(files) if files else self.add_all()) + self.commit(
407            '-m "Initial commit"'
408        )

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:
410    def list_branches(self) -> Output:
411        """>>> git branch -vva"""
412        return self.branch("-vva")
>>> git branch -vva
def loggy(self) -> morbin.morbin.Output:
414    def loggy(self) -> Output:
415        """>>> git log --oneline --name-only --abbrev-commit --graph"""
416        return self.log("--oneline --name-only --abbrev-commit --graph")
>>> git log --oneline --name-only --abbrev-commit --graph
def new_repo(self) -> morbin.morbin.Output:
418    def new_repo(self) -> Output:
419        """Initialize a new repo in current directory.
420        >>> git init -b main"""
421        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:
423    def push_new_branch(self, branch: str) -> Output:
424        """Push a new branch to origin with tracking.
425        >>> git push -u origin {branch}"""
426        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:
428    def switch_branch(self, branch_name: str) -> Output:
429        """Switch to the branch specified by `branch_name`.
430        >>> git checkout {branch_name}"""
431        return self.checkout(branch_name)

Switch to the branch specified by branch_name.

>>> git checkout {branch_name}
def undo(self) -> morbin.morbin.Output:
433    def undo(self) -> Output:
434        """Undo uncommitted changes.
435        >>> git checkout ."""
436        return self.checkout(".")

Undo uncommitted changes.

>>> git checkout .
def untrack( self, *paths: pathier.pathier.Pathier | pathlib.Path | str) -> morbin.morbin.Output:
438    def untrack(self, *paths: Pathish) -> Output:
439        """Remove any number of `paths` from the index.
440
441        Equivalent to
442        >>> git rm --cached {path}
443
444        for each path in `paths`."""
445        paths_ = [str(path) for path in paths]
446        return sum(
447            [self.rm(f"--cached {path}") for path in paths_[1:]],
448            self.rm(f"--cached {paths_[0]}"),
449        )

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:
451    def rename_file(self, file: Pathish, new_name: str) -> Output:
452        """Rename `file` to `new_name` and add renaming to staging index.
453
454        `new_name` should include the file suffix.
455
456        Equivalent to renaming `old_file.py` to `new_file.py` then executing
457        >>> git add new_file.py
458        >>> git rm old_file.py"""
459        file = Pathier(file)
460        new_file = file.replace(file.with_name(new_name))
461        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:
483    def create_remote(self, name: str, public: bool = False) -> Output:
484        """Uses GitHub CLI (must be installed and configured) to create a remote GitHub repo.
485
486        #### :params:
487
488        `name`: The name for the repo.
489
490        `public`: Set to `True` to create the repo as public, otherwise it'll be created as private.
491        """
492        visibility = "--public" if public else "--private"
493        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:
495    def create_remote_from_cwd(self, public: bool = False) -> Output:
496        """Use GitHub CLI (must be installed and configured) to create a remote GitHub repo from
497        the current working directory repo and add its url as this repo's remote origin.
498
499        #### :params:
500
501        `public`: Create the GitHub repo as a public repo, default is to create it as private.
502        """
503        visibility = "--public" if public else "--private"
504        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:
506    def delete_remote(self) -> Output:
507        """Uses GitHub CLI (must be isntalled and configured) to delete the remote for this repo."""
508        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:
510    def make_private(self) -> Output:
511        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to private."""
512        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:
514    def make_public(self) -> Output:
515        """Uses GitHub CLI (must be installed and configured) to set the repo's visibility to public."""
516        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