gitbetter.gitbetter

  1import os
  2
  3from argshell import ArgShell, ArgShellParser, Namespace, with_parser
  4from pathier import Pathier
  5
  6from gitbetter import git
  7
  8
  9def new_remote_parser() -> ArgShellParser:
 10    parser = ArgShellParser()
 11    parser.add_argument(
 12        "--public",
 13        action="store_true",
 14        help=""" Set the new remote visibility as public. Defaults to private. """,
 15    )
 16    return parser
 17
 18
 19def commit_files_parser() -> ArgShellParser:
 20    parser = ArgShellParser()
 21    parser.add_argument(
 22        "files", type=str, nargs="*", help=""" List of files to stage and commit. """
 23    )
 24    parser.add_argument(
 25        "-m",
 26        "--message",
 27        type=str,
 28        required=True,
 29        help=""" The commit message to use. """,
 30    )
 31    parser.add_argument(
 32        "-r",
 33        "--recursive",
 34        action="store_true",
 35        help=""" If a file name is not found in the current working directory,
 36        search for it in subfolders. This avoids having to type paths to files in subfolders,
 37        but if you have multiple files in different subfolders with the same name that have changes they
 38        will all be staged and committed.""",
 39    )
 40    return parser
 41
 42
 43def amend_files_parser() -> ArgShellParser:
 44    parser = ArgShellParser()
 45    parser.add_argument(
 46        "-f",
 47        "--files",
 48        type=str,
 49        nargs="*",
 50        help=""" List of files to stage and commit. """,
 51    )
 52    parser.add_argument(
 53        "-r",
 54        "--recursive",
 55        action="store_true",
 56        help=""" If a file name is not found in the current working directory,
 57        search for it in subfolders. This avoids having to type paths to files in subfolders,
 58        but if you have multiple files in different subfolders with the same name that have changes they
 59        will all be staged and committed.""",
 60    )
 61    return parser
 62
 63
 64def delete_branch_parser() -> ArgShellParser:
 65    parser = ArgShellParser()
 66    parser.add_argument(
 67        "branch", type=str, help=""" The name of the branch to delete. """
 68    )
 69    parser.add_argument(
 70        "-r",
 71        "--remote",
 72        action="store_true",
 73        help=""" Delete the remote and remote-tracking branches along with the local branch.
 74        By default only the local branch is deleted.""",
 75    )
 76    return parser
 77
 78
 79def recurse_files(filenames: list[str]) -> list[str]:
 80    files = []
 81    for filename in filenames:
 82        if not Pathier(filename).exists():
 83            results = list(Pathier.cwd().rglob(f"{filename}"))
 84            if not results:
 85                print(f"WARNING: Could not find any files with name {filename}")
 86            else:
 87                files.extend([str(result) for result in results])
 88        else:
 89            files.append(filename)
 90    return files
 91
 92
 93def files_postparser(args: Namespace) -> Namespace:
 94    if args.recursive:
 95        args.files = recurse_files(args.files)
 96    return args
 97
 98
 99class GitBetter(ArgShell):
100    """GitBetter Shell."""
101
102    intro = "Starting gitbetter...\nEnter 'help' or '?' for command help."
103    prompt = "gitbetter>"
104
105    def do_cd(self, path: str):
106        """Change current working directory to `path`."""
107        os.chdir(path)
108
109    def do_cwd(self, _: str):
110        """Print the current working directory."""
111        print(Pathier.cwd())
112
113    def do_cmd(self, command: str):
114        """Execute arbitrary command in the terminal so you don't have to quit gitbetter to run other programs/commands."""
115        os.system(command)
116
117    def do_git(self, arg: str):
118        """Directly execute `git {arg}`.
119
120        i.e. You can still do everything directly invoking git can do."""
121        git.execute(arg)
122
123    def do_new_repo(self, _: str):
124        """Create a new git repo in this directory."""
125        git.new_repo()
126
127    def do_new_branch(self, name: str):
128        """Create and switch to a new branch named after the supplied arg."""
129        git.create_new_branch(name)
130
131    @with_parser(new_remote_parser)
132    def do_new_gh_remote(self, args: Namespace):
133        """Create a remote GitHub repository for this repo.
134
135        GitHub CLI must be installed and configured for this to work."""
136        name = Pathier.cwd().stem
137        git.create_remote(name, args.public)
138
139    def do_initcommit(self, _: str):
140        """Stage and commit all files with message "Initial Commit"."""
141        git.initcommit()
142
143    def do_undo(self, _: str):
144        """Undo all uncommitted changes."""
145        git.undo()
146
147    @with_parser(amend_files_parser, [files_postparser])
148    def do_add(self, args: Namespace):
149        """Stage a list of files.
150        If no files are given, all files will be added."""
151        git.add(None if not args.files else args.files)
152
153    def do_commit(self, message: str):
154        """Commit staged files with this message."""
155        if not message.startswith('"'):
156            message = '"' + message
157        if not message.endswith('"'):
158            message += '"'
159        git.commit(f"-m {message}")
160
161    @with_parser(commit_files_parser, [files_postparser])
162    def do_commitf(self, args: Namespace):
163        """Stage and commit a list of files."""
164        git.commit_files(args.files, args.message)
165
166    def do_commitall(self, message: str):
167        """Stage and commit all files with this message."""
168        if not message.startswith('"'):
169            message = '"' + message
170        if not message.endswith('"'):
171            message += '"'
172        git.add()
173        git.commit(f"-m {message}")
174
175    def do_switch(self, branch_name: str):
176        """Switch to this branch."""
177        git.switch_branch(branch_name)
178
179    def do_add_url(self, url: str):
180        """Add remote url for repo and push repo."""
181        git.add_remote_url(url)
182        git.push("-u origin main")
183
184    def do_push_new(self, branch_name: str):
185        """Push this new branch to origin with -u flag."""
186        git.push_new_branch(branch_name)
187
188    def do_push(self, args: str):
189        """Execute `git push`.
190
191        `args` can be any additional args that `git push` accepts."""
192        git.push(args)
193
194    def do_pull(self, args: str):
195        """Execute `git pull`.
196
197        `args` can be any additional args that `git pull` accepts."""
198        git.pull(args)
199
200    def do_list_branches(self, _: str):
201        """Show local and remote branches."""
202        git.list_branches()
203
204    def do_loggy(self, _: str):
205        """Execute `git --oneline --name-only --abbrev-commit --graph`."""
206        git.loggy()
207
208    def do_merge(self, branch_name: str):
209        """Merge supplied `branch_name` with the currently active branch."""
210        git.merge(branch_name)
211
212    def do_tag(self, tag_id: str):
213        """Tag current commit with `tag_id`."""
214        git.tag(tag_id)
215
216    @with_parser(amend_files_parser, [files_postparser])
217    def do_amend(self, args: Namespace):
218        """Stage files and add to previous commit."""
219        git.amend(args.files)
220
221    @with_parser(delete_branch_parser)
222    def do_delete_branch(self, args: Namespace):
223        """Delete branch."""
224        git.delete_branch(args.branch, not args.remote)
225
226    def do_pull_branch(self, branch: str):
227        """Pull this branch from the origin."""
228        git.pull_branch(branch)
229
230    def do_ignore(self, patterns: str):
231        """Add the list of patterns to `.gitignore`."""
232        patterns = "\n".join(patterns.split())
233        path = Pathier(".gitignore")
234        path.append("\n")
235        path.append(patterns, False)
236
237    def do_make_private(self, owner: str):
238        """Make the GitHub remote for this repo private.
239
240        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
241
242        This repo must exist and GitHub CLI must be installed and configured."""
243        git.make_private(owner, Pathier.cwd().stem)
244
245    def do_make_public(self, owner: str):
246        """Make the GitHub remote for this repo public.
247
248        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
249
250        This repo must exist and GitHub CLI must be installed and configured."""
251        git.make_public(owner, Pathier.cwd().stem)
252
253    def do_delete_gh_repo(self, owner: str):
254        """Delete this repo from GitHub.
255
256        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
257
258        GitHub CLI must be installed and configured.
259
260        May require you to reauthorize and rerun command."""
261        git.delete_remote(owner, Pathier.cwd().stem)
262
263
264def main():
265    GitBetter().cmdloop()
266
267
268if __name__ == "__main__":
269    main()
def new_remote_parser() -> argshell.argshell.ArgShellParser:
10def new_remote_parser() -> ArgShellParser:
11    parser = ArgShellParser()
12    parser.add_argument(
13        "--public",
14        action="store_true",
15        help=""" Set the new remote visibility as public. Defaults to private. """,
16    )
17    return parser
def commit_files_parser() -> argshell.argshell.ArgShellParser:
20def commit_files_parser() -> ArgShellParser:
21    parser = ArgShellParser()
22    parser.add_argument(
23        "files", type=str, nargs="*", help=""" List of files to stage and commit. """
24    )
25    parser.add_argument(
26        "-m",
27        "--message",
28        type=str,
29        required=True,
30        help=""" The commit message to use. """,
31    )
32    parser.add_argument(
33        "-r",
34        "--recursive",
35        action="store_true",
36        help=""" If a file name is not found in the current working directory,
37        search for it in subfolders. This avoids having to type paths to files in subfolders,
38        but if you have multiple files in different subfolders with the same name that have changes they
39        will all be staged and committed.""",
40    )
41    return parser
def amend_files_parser() -> argshell.argshell.ArgShellParser:
44def amend_files_parser() -> ArgShellParser:
45    parser = ArgShellParser()
46    parser.add_argument(
47        "-f",
48        "--files",
49        type=str,
50        nargs="*",
51        help=""" List of files to stage and commit. """,
52    )
53    parser.add_argument(
54        "-r",
55        "--recursive",
56        action="store_true",
57        help=""" If a file name is not found in the current working directory,
58        search for it in subfolders. This avoids having to type paths to files in subfolders,
59        but if you have multiple files in different subfolders with the same name that have changes they
60        will all be staged and committed.""",
61    )
62    return parser
def delete_branch_parser() -> argshell.argshell.ArgShellParser:
65def delete_branch_parser() -> ArgShellParser:
66    parser = ArgShellParser()
67    parser.add_argument(
68        "branch", type=str, help=""" The name of the branch to delete. """
69    )
70    parser.add_argument(
71        "-r",
72        "--remote",
73        action="store_true",
74        help=""" Delete the remote and remote-tracking branches along with the local branch.
75        By default only the local branch is deleted.""",
76    )
77    return parser
def recurse_files(filenames: list[str]) -> list[str]:
80def recurse_files(filenames: list[str]) -> list[str]:
81    files = []
82    for filename in filenames:
83        if not Pathier(filename).exists():
84            results = list(Pathier.cwd().rglob(f"{filename}"))
85            if not results:
86                print(f"WARNING: Could not find any files with name {filename}")
87            else:
88                files.extend([str(result) for result in results])
89        else:
90            files.append(filename)
91    return files
def files_postparser(args: argshell.argshell.Namespace) -> argshell.argshell.Namespace:
94def files_postparser(args: Namespace) -> Namespace:
95    if args.recursive:
96        args.files = recurse_files(args.files)
97    return args
class GitBetter(argshell.argshell.ArgShell):
100class GitBetter(ArgShell):
101    """GitBetter Shell."""
102
103    intro = "Starting gitbetter...\nEnter 'help' or '?' for command help."
104    prompt = "gitbetter>"
105
106    def do_cd(self, path: str):
107        """Change current working directory to `path`."""
108        os.chdir(path)
109
110    def do_cwd(self, _: str):
111        """Print the current working directory."""
112        print(Pathier.cwd())
113
114    def do_cmd(self, command: str):
115        """Execute arbitrary command in the terminal so you don't have to quit gitbetter to run other programs/commands."""
116        os.system(command)
117
118    def do_git(self, arg: str):
119        """Directly execute `git {arg}`.
120
121        i.e. You can still do everything directly invoking git can do."""
122        git.execute(arg)
123
124    def do_new_repo(self, _: str):
125        """Create a new git repo in this directory."""
126        git.new_repo()
127
128    def do_new_branch(self, name: str):
129        """Create and switch to a new branch named after the supplied arg."""
130        git.create_new_branch(name)
131
132    @with_parser(new_remote_parser)
133    def do_new_gh_remote(self, args: Namespace):
134        """Create a remote GitHub repository for this repo.
135
136        GitHub CLI must be installed and configured for this to work."""
137        name = Pathier.cwd().stem
138        git.create_remote(name, args.public)
139
140    def do_initcommit(self, _: str):
141        """Stage and commit all files with message "Initial Commit"."""
142        git.initcommit()
143
144    def do_undo(self, _: str):
145        """Undo all uncommitted changes."""
146        git.undo()
147
148    @with_parser(amend_files_parser, [files_postparser])
149    def do_add(self, args: Namespace):
150        """Stage a list of files.
151        If no files are given, all files will be added."""
152        git.add(None if not args.files else args.files)
153
154    def do_commit(self, message: str):
155        """Commit staged files with this message."""
156        if not message.startswith('"'):
157            message = '"' + message
158        if not message.endswith('"'):
159            message += '"'
160        git.commit(f"-m {message}")
161
162    @with_parser(commit_files_parser, [files_postparser])
163    def do_commitf(self, args: Namespace):
164        """Stage and commit a list of files."""
165        git.commit_files(args.files, args.message)
166
167    def do_commitall(self, message: str):
168        """Stage and commit all files with this message."""
169        if not message.startswith('"'):
170            message = '"' + message
171        if not message.endswith('"'):
172            message += '"'
173        git.add()
174        git.commit(f"-m {message}")
175
176    def do_switch(self, branch_name: str):
177        """Switch to this branch."""
178        git.switch_branch(branch_name)
179
180    def do_add_url(self, url: str):
181        """Add remote url for repo and push repo."""
182        git.add_remote_url(url)
183        git.push("-u origin main")
184
185    def do_push_new(self, branch_name: str):
186        """Push this new branch to origin with -u flag."""
187        git.push_new_branch(branch_name)
188
189    def do_push(self, args: str):
190        """Execute `git push`.
191
192        `args` can be any additional args that `git push` accepts."""
193        git.push(args)
194
195    def do_pull(self, args: str):
196        """Execute `git pull`.
197
198        `args` can be any additional args that `git pull` accepts."""
199        git.pull(args)
200
201    def do_list_branches(self, _: str):
202        """Show local and remote branches."""
203        git.list_branches()
204
205    def do_loggy(self, _: str):
206        """Execute `git --oneline --name-only --abbrev-commit --graph`."""
207        git.loggy()
208
209    def do_merge(self, branch_name: str):
210        """Merge supplied `branch_name` with the currently active branch."""
211        git.merge(branch_name)
212
213    def do_tag(self, tag_id: str):
214        """Tag current commit with `tag_id`."""
215        git.tag(tag_id)
216
217    @with_parser(amend_files_parser, [files_postparser])
218    def do_amend(self, args: Namespace):
219        """Stage files and add to previous commit."""
220        git.amend(args.files)
221
222    @with_parser(delete_branch_parser)
223    def do_delete_branch(self, args: Namespace):
224        """Delete branch."""
225        git.delete_branch(args.branch, not args.remote)
226
227    def do_pull_branch(self, branch: str):
228        """Pull this branch from the origin."""
229        git.pull_branch(branch)
230
231    def do_ignore(self, patterns: str):
232        """Add the list of patterns to `.gitignore`."""
233        patterns = "\n".join(patterns.split())
234        path = Pathier(".gitignore")
235        path.append("\n")
236        path.append(patterns, False)
237
238    def do_make_private(self, owner: str):
239        """Make the GitHub remote for this repo private.
240
241        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
242
243        This repo must exist and GitHub CLI must be installed and configured."""
244        git.make_private(owner, Pathier.cwd().stem)
245
246    def do_make_public(self, owner: str):
247        """Make the GitHub remote for this repo public.
248
249        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
250
251        This repo must exist and GitHub CLI must be installed and configured."""
252        git.make_public(owner, Pathier.cwd().stem)
253
254    def do_delete_gh_repo(self, owner: str):
255        """Delete this repo from GitHub.
256
257        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
258
259        GitHub CLI must be installed and configured.
260
261        May require you to reauthorize and rerun command."""
262        git.delete_remote(owner, Pathier.cwd().stem)

GitBetter Shell.

def do_cd(self, path: str):
106    def do_cd(self, path: str):
107        """Change current working directory to `path`."""
108        os.chdir(path)

Change current working directory to path.

def do_cwd(self, _: str):
110    def do_cwd(self, _: str):
111        """Print the current working directory."""
112        print(Pathier.cwd())

Print the current working directory.

def do_cmd(self, command: str):
114    def do_cmd(self, command: str):
115        """Execute arbitrary command in the terminal so you don't have to quit gitbetter to run other programs/commands."""
116        os.system(command)

Execute arbitrary command in the terminal so you don't have to quit gitbetter to run other programs/commands.

def do_git(self, arg: str):
118    def do_git(self, arg: str):
119        """Directly execute `git {arg}`.
120
121        i.e. You can still do everything directly invoking git can do."""
122        git.execute(arg)

Directly execute git {arg}.

i.e. You can still do everything directly invoking git can do.

def do_new_repo(self, _: str):
124    def do_new_repo(self, _: str):
125        """Create a new git repo in this directory."""
126        git.new_repo()

Create a new git repo in this directory.

def do_new_branch(self, name: str):
128    def do_new_branch(self, name: str):
129        """Create and switch to a new branch named after the supplied arg."""
130        git.create_new_branch(name)

Create and switch to a new branch named after the supplied arg.

@with_parser(new_remote_parser)
def do_new_gh_remote(self, args: argshell.argshell.Namespace):
132    @with_parser(new_remote_parser)
133    def do_new_gh_remote(self, args: Namespace):
134        """Create a remote GitHub repository for this repo.
135
136        GitHub CLI must be installed and configured for this to work."""
137        name = Pathier.cwd().stem
138        git.create_remote(name, args.public)

Create a remote GitHub repository for this repo.

GitHub CLI must be installed and configured for this to work.

def do_initcommit(self, _: str):
140    def do_initcommit(self, _: str):
141        """Stage and commit all files with message "Initial Commit"."""
142        git.initcommit()

Stage and commit all files with message "Initial Commit".

def do_undo(self, _: str):
144    def do_undo(self, _: str):
145        """Undo all uncommitted changes."""
146        git.undo()

Undo all uncommitted changes.

@with_parser(amend_files_parser, [files_postparser])
def do_add(self, args: argshell.argshell.Namespace):
148    @with_parser(amend_files_parser, [files_postparser])
149    def do_add(self, args: Namespace):
150        """Stage a list of files.
151        If no files are given, all files will be added."""
152        git.add(None if not args.files else args.files)

Stage a list of files. If no files are given, all files will be added.

def do_commit(self, message: str):
154    def do_commit(self, message: str):
155        """Commit staged files with this message."""
156        if not message.startswith('"'):
157            message = '"' + message
158        if not message.endswith('"'):
159            message += '"'
160        git.commit(f"-m {message}")

Commit staged files with this message.

@with_parser(commit_files_parser, [files_postparser])
def do_commitf(self, args: argshell.argshell.Namespace):
162    @with_parser(commit_files_parser, [files_postparser])
163    def do_commitf(self, args: Namespace):
164        """Stage and commit a list of files."""
165        git.commit_files(args.files, args.message)

Stage and commit a list of files.

def do_commitall(self, message: str):
167    def do_commitall(self, message: str):
168        """Stage and commit all files with this message."""
169        if not message.startswith('"'):
170            message = '"' + message
171        if not message.endswith('"'):
172            message += '"'
173        git.add()
174        git.commit(f"-m {message}")

Stage and commit all files with this message.

def do_switch(self, branch_name: str):
176    def do_switch(self, branch_name: str):
177        """Switch to this branch."""
178        git.switch_branch(branch_name)

Switch to this branch.

def do_add_url(self, url: str):
180    def do_add_url(self, url: str):
181        """Add remote url for repo and push repo."""
182        git.add_remote_url(url)
183        git.push("-u origin main")

Add remote url for repo and push repo.

def do_push_new(self, branch_name: str):
185    def do_push_new(self, branch_name: str):
186        """Push this new branch to origin with -u flag."""
187        git.push_new_branch(branch_name)

Push this new branch to origin with -u flag.

def do_push(self, args: str):
189    def do_push(self, args: str):
190        """Execute `git push`.
191
192        `args` can be any additional args that `git push` accepts."""
193        git.push(args)

Execute git push.

args can be any additional args that git push accepts.

def do_pull(self, args: str):
195    def do_pull(self, args: str):
196        """Execute `git pull`.
197
198        `args` can be any additional args that `git pull` accepts."""
199        git.pull(args)

Execute git pull.

args can be any additional args that git pull accepts.

def do_list_branches(self, _: str):
201    def do_list_branches(self, _: str):
202        """Show local and remote branches."""
203        git.list_branches()

Show local and remote branches.

def do_loggy(self, _: str):
205    def do_loggy(self, _: str):
206        """Execute `git --oneline --name-only --abbrev-commit --graph`."""
207        git.loggy()

Execute git --oneline --name-only --abbrev-commit --graph.

def do_merge(self, branch_name: str):
209    def do_merge(self, branch_name: str):
210        """Merge supplied `branch_name` with the currently active branch."""
211        git.merge(branch_name)

Merge supplied branch_name with the currently active branch.

def do_tag(self, tag_id: str):
213    def do_tag(self, tag_id: str):
214        """Tag current commit with `tag_id`."""
215        git.tag(tag_id)

Tag current commit with tag_id.

@with_parser(amend_files_parser, [files_postparser])
def do_amend(self, args: argshell.argshell.Namespace):
217    @with_parser(amend_files_parser, [files_postparser])
218    def do_amend(self, args: Namespace):
219        """Stage files and add to previous commit."""
220        git.amend(args.files)

Stage files and add to previous commit.

@with_parser(delete_branch_parser)
def do_delete_branch(self, args: argshell.argshell.Namespace):
222    @with_parser(delete_branch_parser)
223    def do_delete_branch(self, args: Namespace):
224        """Delete branch."""
225        git.delete_branch(args.branch, not args.remote)

Delete branch.

def do_pull_branch(self, branch: str):
227    def do_pull_branch(self, branch: str):
228        """Pull this branch from the origin."""
229        git.pull_branch(branch)

Pull this branch from the origin.

def do_ignore(self, patterns: str):
231    def do_ignore(self, patterns: str):
232        """Add the list of patterns to `.gitignore`."""
233        patterns = "\n".join(patterns.split())
234        path = Pathier(".gitignore")
235        path.append("\n")
236        path.append(patterns, False)

Add the list of patterns to .gitignore.

def do_make_private(self, owner: str):
238    def do_make_private(self, owner: str):
239        """Make the GitHub remote for this repo private.
240
241        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
242
243        This repo must exist and GitHub CLI must be installed and configured."""
244        git.make_private(owner, Pathier.cwd().stem)

Make the GitHub remote for this repo private.

Expects an argument for the repo owner, i.e. the OWNER in github.com/{OWNER}/{repo-name}

This repo must exist and GitHub CLI must be installed and configured.

def do_make_public(self, owner: str):
246    def do_make_public(self, owner: str):
247        """Make the GitHub remote for this repo public.
248
249        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
250
251        This repo must exist and GitHub CLI must be installed and configured."""
252        git.make_public(owner, Pathier.cwd().stem)

Make the GitHub remote for this repo public.

Expects an argument for the repo owner, i.e. the OWNER in github.com/{OWNER}/{repo-name}

This repo must exist and GitHub CLI must be installed and configured.

def do_delete_gh_repo(self, owner: str):
254    def do_delete_gh_repo(self, owner: str):
255        """Delete this repo from GitHub.
256
257        Expects an argument for the repo owner, i.e. the `OWNER` in `github.com/{OWNER}/{repo-name}`
258
259        GitHub CLI must be installed and configured.
260
261        May require you to reauthorize and rerun command."""
262        git.delete_remote(owner, Pathier.cwd().stem)

Delete this repo from GitHub.

Expects an argument for the repo owner, i.e. the OWNER in github.com/{OWNER}/{repo-name}

GitHub CLI must be installed and configured.

May require you to reauthorize and rerun command.

Inherited Members
cmd.Cmd
Cmd
precmd
postcmd
preloop
postloop
parseline
onecmd
emptyline
default
completedefault
completenames
complete
get_names
complete_help
print_topics
columnize
argshell.argshell.ArgShell
do_quit
do_help
cmdloop
def main():
265def main():
266    GitBetter().cmdloop()