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