githelp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- githelp/__init__.py +3 -0
- githelp/cli.py +203 -0
- githelp/gitignore.py +61 -0
- githelp/gitutil.py +71 -0
- githelp-0.1.0.dist-info/METADATA +132 -0
- githelp-0.1.0.dist-info/RECORD +9 -0
- githelp-0.1.0.dist-info/WHEEL +4 -0
- githelp-0.1.0.dist-info/entry_points.txt +3 -0
- githelp-0.1.0.dist-info/licenses/LICENSE +21 -0
githelp/__init__.py
ADDED
githelp/cli.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Command-line entry points for `gg` and `ggi`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import questionary
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from . import gitignore, gitutil
|
|
12
|
+
from .gitutil import GitError
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
err = Console(stderr=True)
|
|
16
|
+
|
|
17
|
+
FILE_WARN = 100 # warn when staging more than this many files
|
|
18
|
+
LINE_WARN = 100_000 # warn when staging more than this many changed lines
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# --------------------------------------------------------------------------- #
|
|
22
|
+
# gg : add . + commit (no quotes needed) + push
|
|
23
|
+
# --------------------------------------------------------------------------- #
|
|
24
|
+
|
|
25
|
+
GG_USAGE = """\
|
|
26
|
+
[bold]gg[/bold] - stage everything, commit, and push in one shot.
|
|
27
|
+
|
|
28
|
+
[bold]Usage:[/bold]
|
|
29
|
+
gg <your commit message with no quotes>
|
|
30
|
+
|
|
31
|
+
[bold]Example:[/bold]
|
|
32
|
+
gg fix the login bug and update readme
|
|
33
|
+
|
|
34
|
+
Runs: git add . -> git commit -m "<your message>" -> git push
|
|
35
|
+
Warns before committing if more than %d files or %s changed lines are staged.\
|
|
36
|
+
""" % (FILE_WARN, f"{LINE_WARN:,}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def gg_main() -> None:
|
|
40
|
+
sys.exit(_gg(sys.argv[1:]))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _gg(args: list[str]) -> int:
|
|
44
|
+
if not args or args[0] in ("-h", "--help"):
|
|
45
|
+
console.print(GG_USAGE)
|
|
46
|
+
return 0 if args else 1
|
|
47
|
+
|
|
48
|
+
message = " ".join(args).strip()
|
|
49
|
+
if not message:
|
|
50
|
+
err.print("[red]A commit message is required.[/red]")
|
|
51
|
+
return 1
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
if not gitutil.is_inside_repo():
|
|
55
|
+
err.print("[red]Not inside a git repository.[/red]")
|
|
56
|
+
return 1
|
|
57
|
+
|
|
58
|
+
gitutil.run_git(["add", "."])
|
|
59
|
+
stats = gitutil.staged_stats()
|
|
60
|
+
|
|
61
|
+
if stats.files == 0:
|
|
62
|
+
console.print("[yellow]Nothing to commit - working tree is clean.[/yellow]")
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
console.print(
|
|
66
|
+
f"Staged [bold]{stats.files}[/bold] file(s), "
|
|
67
|
+
f"[bold]{stats.lines:,}[/bold] changed line(s)."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if stats.files > FILE_WARN and not _confirm(
|
|
71
|
+
f"That is more than {FILE_WARN} files. Are you sure you want to commit?"
|
|
72
|
+
):
|
|
73
|
+
console.print("[yellow]Aborted. (Files are still staged.)[/yellow]")
|
|
74
|
+
return 1
|
|
75
|
+
|
|
76
|
+
if stats.lines > LINE_WARN and not _confirm(
|
|
77
|
+
f"That is more than {LINE_WARN:,} changed lines. Are you sure?"
|
|
78
|
+
):
|
|
79
|
+
console.print("[yellow]Aborted. (Files are still staged.)[/yellow]")
|
|
80
|
+
return 1
|
|
81
|
+
|
|
82
|
+
gitutil.run_git(["commit", "-m", message])
|
|
83
|
+
console.print(f'[green]Committed:[/green] "{message}"')
|
|
84
|
+
|
|
85
|
+
_push()
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
except GitError as exc:
|
|
89
|
+
err.print(f"[red]{exc}[/red]")
|
|
90
|
+
return 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _push() -> None:
|
|
94
|
+
"""Push the current branch, setting upstream automatically if needed."""
|
|
95
|
+
proc = gitutil.run_git(["push"], check=False)
|
|
96
|
+
if proc.returncode == 0:
|
|
97
|
+
console.print("[green]Pushed.[/green]")
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
stderr = (proc.stderr or "").lower()
|
|
101
|
+
branch = gitutil.current_branch()
|
|
102
|
+
if "no upstream" in stderr and branch:
|
|
103
|
+
console.print("[yellow]No upstream set - pushing with --set-upstream origin.[/yellow]")
|
|
104
|
+
up = gitutil.run_git(["push", "--set-upstream", "origin", branch], check=False)
|
|
105
|
+
if up.returncode == 0:
|
|
106
|
+
console.print("[green]Pushed.[/green]")
|
|
107
|
+
return
|
|
108
|
+
raise GitError((up.stderr or up.stdout or "git push failed.").strip())
|
|
109
|
+
|
|
110
|
+
raise GitError((proc.stderr or proc.stdout or "git push failed.").strip())
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# --------------------------------------------------------------------------- #
|
|
114
|
+
# ggi : add a .gitignore template from github/gitignore
|
|
115
|
+
# --------------------------------------------------------------------------- #
|
|
116
|
+
|
|
117
|
+
GGI_USAGE = """\
|
|
118
|
+
[bold]ggi[/bold] - add a .gitignore template (live from github/gitignore).
|
|
119
|
+
|
|
120
|
+
[bold]Usage:[/bold]
|
|
121
|
+
ggi # interactive: pick a template by typing/searching
|
|
122
|
+
ggi Python # add a named template directly
|
|
123
|
+
|
|
124
|
+
Appends the chosen template to your repository's .gitignore.\
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def ggi_main() -> None:
|
|
129
|
+
sys.exit(_ggi(sys.argv[1:]))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _ggi(args: list[str]) -> int:
|
|
133
|
+
if args and args[0] in ("-h", "--help"):
|
|
134
|
+
console.print(GGI_USAGE)
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
available = gitignore.list_templates()
|
|
139
|
+
except gitignore.GitignoreError as exc:
|
|
140
|
+
err.print(f"[red]{exc}[/red]")
|
|
141
|
+
return 1
|
|
142
|
+
|
|
143
|
+
if args:
|
|
144
|
+
chosen = gitignore.resolve_name(args[0], available)
|
|
145
|
+
if chosen is None:
|
|
146
|
+
err.print(
|
|
147
|
+
f"[red]Unknown template {args[0]!r}.[/red] "
|
|
148
|
+
"Run [bold]ggi[/bold] with no arguments to browse the list."
|
|
149
|
+
)
|
|
150
|
+
return 1
|
|
151
|
+
else:
|
|
152
|
+
chosen = questionary.autocomplete(
|
|
153
|
+
"Which .gitignore template do you want to add?",
|
|
154
|
+
choices=available,
|
|
155
|
+
match_middle=True,
|
|
156
|
+
ignore_case=True,
|
|
157
|
+
).ask()
|
|
158
|
+
if not chosen:
|
|
159
|
+
console.print("[yellow]Cancelled.[/yellow]")
|
|
160
|
+
return 1
|
|
161
|
+
chosen = gitignore.resolve_name(chosen, available)
|
|
162
|
+
if chosen is None:
|
|
163
|
+
err.print("[red]That is not a valid template name.[/red]")
|
|
164
|
+
return 1
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
name, source = gitignore.fetch_template(chosen)
|
|
168
|
+
except gitignore.GitignoreError as exc:
|
|
169
|
+
err.print(f"[red]{exc}[/red]")
|
|
170
|
+
return 1
|
|
171
|
+
|
|
172
|
+
target = os.path.join(gitutil.repo_root() or os.getcwd(), ".gitignore")
|
|
173
|
+
marker = f"# >>> {name}.gitignore (added by gg) >>>"
|
|
174
|
+
|
|
175
|
+
existing = ""
|
|
176
|
+
if os.path.exists(target):
|
|
177
|
+
with open(target, "r", encoding="utf-8") as fh:
|
|
178
|
+
existing = fh.read()
|
|
179
|
+
|
|
180
|
+
if marker in existing:
|
|
181
|
+
console.print(
|
|
182
|
+
f"[yellow]{name} is already present in {target} - nothing to do.[/yellow]"
|
|
183
|
+
)
|
|
184
|
+
return 0
|
|
185
|
+
|
|
186
|
+
block = f"{marker}\n{source.rstrip()}\n# <<< {name}.gitignore <<<\n"
|
|
187
|
+
prefix = "" if (not existing or existing.endswith("\n")) else "\n"
|
|
188
|
+
with open(target, "a", encoding="utf-8") as fh:
|
|
189
|
+
if existing and not existing.endswith("\n\n"):
|
|
190
|
+
fh.write(prefix + "\n")
|
|
191
|
+
fh.write(block)
|
|
192
|
+
|
|
193
|
+
console.print(f"[green]Added {name} template to[/green] {target}")
|
|
194
|
+
return 0
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# --------------------------------------------------------------------------- #
|
|
198
|
+
# helpers
|
|
199
|
+
# --------------------------------------------------------------------------- #
|
|
200
|
+
|
|
201
|
+
def _confirm(question: str) -> bool:
|
|
202
|
+
answer = questionary.confirm(question, default=False).ask()
|
|
203
|
+
return bool(answer)
|
githelp/gitignore.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Fetch .gitignore templates live from github/gitignore via the GitHub API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
API_LIST = "https://api.github.com/gitignore/templates"
|
|
10
|
+
API_TEMPLATE = "https://api.github.com/gitignore/templates/{name}"
|
|
11
|
+
TIMEOUT = 15.0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GitignoreError(RuntimeError):
|
|
15
|
+
"""Raised when a template cannot be listed or fetched."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _headers() -> dict[str, str]:
|
|
19
|
+
headers = {
|
|
20
|
+
"Accept": "application/vnd.github+json",
|
|
21
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
22
|
+
}
|
|
23
|
+
# Use a token if present to avoid the unauthenticated rate limit.
|
|
24
|
+
token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
|
|
25
|
+
if token:
|
|
26
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
27
|
+
return headers
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def list_templates() -> list[str]:
|
|
31
|
+
"""Return the list of available template names (e.g. 'Python', 'Node')."""
|
|
32
|
+
try:
|
|
33
|
+
resp = httpx.get(API_LIST, headers=_headers(), timeout=TIMEOUT)
|
|
34
|
+
resp.raise_for_status()
|
|
35
|
+
return list(resp.json())
|
|
36
|
+
except httpx.HTTPError as exc:
|
|
37
|
+
raise GitignoreError(f"Could not fetch the template list: {exc}") from exc
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def fetch_template(name: str) -> tuple[str, str]:
|
|
41
|
+
"""Fetch one template. Returns (canonical_name, source_text)."""
|
|
42
|
+
try:
|
|
43
|
+
resp = httpx.get(
|
|
44
|
+
API_TEMPLATE.format(name=name), headers=_headers(), timeout=TIMEOUT
|
|
45
|
+
)
|
|
46
|
+
if resp.status_code == 404:
|
|
47
|
+
raise GitignoreError(f"No gitignore template named {name!r}.")
|
|
48
|
+
resp.raise_for_status()
|
|
49
|
+
data = resp.json()
|
|
50
|
+
return data["name"], data["source"]
|
|
51
|
+
except httpx.HTTPError as exc:
|
|
52
|
+
raise GitignoreError(f"Could not fetch template {name!r}: {exc}") from exc
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def resolve_name(name: str, available: list[str]) -> str | None:
|
|
56
|
+
"""Match user input to an available template, case-insensitively."""
|
|
57
|
+
lowered = name.strip().lower()
|
|
58
|
+
for candidate in available:
|
|
59
|
+
if candidate.lower() == lowered:
|
|
60
|
+
return candidate
|
|
61
|
+
return None
|
githelp/gitutil.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Thin wrappers around the `git` command-line."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GitError(RuntimeError):
|
|
10
|
+
"""Raised when a git command fails or git is unavailable."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_git(args: list[str], check: bool = True) -> subprocess.CompletedProcess:
|
|
14
|
+
"""Run a git command, capturing stdout/stderr as text."""
|
|
15
|
+
try:
|
|
16
|
+
proc = subprocess.run(
|
|
17
|
+
["git", *args],
|
|
18
|
+
capture_output=True,
|
|
19
|
+
text=True,
|
|
20
|
+
)
|
|
21
|
+
except FileNotFoundError as exc: # git not installed
|
|
22
|
+
raise GitError("git is not installed or not on your PATH.") from exc
|
|
23
|
+
|
|
24
|
+
if check and proc.returncode != 0:
|
|
25
|
+
message = (proc.stderr or proc.stdout or "").strip()
|
|
26
|
+
raise GitError(message or f"git {' '.join(args)} failed.")
|
|
27
|
+
return proc
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_inside_repo() -> bool:
|
|
31
|
+
proc = run_git(["rev-parse", "--is-inside-work-tree"], check=False)
|
|
32
|
+
return proc.returncode == 0 and proc.stdout.strip() == "true"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def repo_root() -> str | None:
|
|
36
|
+
proc = run_git(["rev-parse", "--show-toplevel"], check=False)
|
|
37
|
+
if proc.returncode == 0:
|
|
38
|
+
return proc.stdout.strip()
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def current_branch() -> str | None:
|
|
43
|
+
proc = run_git(["rev-parse", "--abbrev-ref", "HEAD"], check=False)
|
|
44
|
+
if proc.returncode == 0:
|
|
45
|
+
branch = proc.stdout.strip()
|
|
46
|
+
return branch if branch and branch != "HEAD" else None
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class StagedStats:
|
|
52
|
+
files: int
|
|
53
|
+
lines: int # added + removed across all staged files
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def staged_stats() -> StagedStats:
|
|
57
|
+
"""Summarise what is currently staged using `git diff --cached --numstat`."""
|
|
58
|
+
proc = run_git(["diff", "--cached", "--numstat"])
|
|
59
|
+
files = 0
|
|
60
|
+
lines = 0
|
|
61
|
+
for raw in proc.stdout.splitlines():
|
|
62
|
+
parts = raw.split("\t")
|
|
63
|
+
if len(parts) < 3:
|
|
64
|
+
continue
|
|
65
|
+
added, removed = parts[0], parts[1]
|
|
66
|
+
files += 1
|
|
67
|
+
if added.isdigit():
|
|
68
|
+
lines += int(added)
|
|
69
|
+
if removed.isdigit():
|
|
70
|
+
lines += int(removed)
|
|
71
|
+
return StagedStats(files=files, lines=lines)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: githelp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: githelp - a tiny git helper (gg/ggi): commit without quotes, and add .gitignore templates from github/gitignore
|
|
5
|
+
Project-URL: Homepage, https://github.com/rosaboyle/githelp
|
|
6
|
+
Project-URL: Repository, https://github.com/rosaboyle/githelp
|
|
7
|
+
Project-URL: Issues, https://github.com/rosaboyle/githelp/issues
|
|
8
|
+
Author-email: Dheeraj Pai <dheeraj.pai@leanmcp.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,commit,git,gitignore
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Requires-Dist: httpx==0.28.1
|
|
18
|
+
Requires-Dist: questionary==2.1.1
|
|
19
|
+
Requires-Dist: rich==15.0.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# githelp
|
|
23
|
+
|
|
24
|
+
A tiny git helper published as [`githelp`](https://pypi.org/project/githelp/). It
|
|
25
|
+
installs two commands: **`gg`** and **`ggi`**.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
pip install githelp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- Repository: <https://github.com/rosaboyle/githelp>
|
|
32
|
+
- PyPI: <https://pypi.org/project/githelp/>
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## `gg` — commit without quotes
|
|
37
|
+
|
|
38
|
+
Type your message straight after `gg`, no quotes:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
gg fix the login bug and update the readme
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
That runs, in order:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
git add .
|
|
48
|
+
git commit -m "fix the login bug and update the readme"
|
|
49
|
+
git push
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
It **warns you and asks "are you sure?"** before committing when:
|
|
53
|
+
|
|
54
|
+
- more than **100 files** are staged, or
|
|
55
|
+
- more than **100,000 changed lines** are staged.
|
|
56
|
+
|
|
57
|
+
If you decline, it aborts (your files stay staged). If neither threshold is
|
|
58
|
+
crossed, it just commits and pushes. If the current branch has no upstream, it
|
|
59
|
+
pushes with `--set-upstream origin <branch>` automatically.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
gg --help # usage
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## `ggi` — add a .gitignore template
|
|
66
|
+
|
|
67
|
+
Templates are fetched **live** from
|
|
68
|
+
[`github/gitignore`](https://github.com/github/gitignore) through the GitHub API,
|
|
69
|
+
so they are always the current upstream versions (nothing is bundled or stale).
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
ggi # interactive: type to fuzzy-search the full list, then pick one
|
|
73
|
+
ggi Python # add a named template directly
|
|
74
|
+
ggi --help # usage
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The selected template is appended to your repository's `.gitignore` inside a
|
|
78
|
+
clearly marked block, so it is easy to find and is never added twice.
|
|
79
|
+
|
|
80
|
+
> Tip: set `GITHUB_TOKEN` (or `GH_TOKEN`) in your environment to avoid GitHub's
|
|
81
|
+
> unauthenticated API rate limit.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Install from source / develop
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git clone https://github.com/rosaboyle/githelp
|
|
89
|
+
cd githelp
|
|
90
|
+
pip install -e .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Dependencies
|
|
94
|
+
|
|
95
|
+
Pinned to the latest releases:
|
|
96
|
+
|
|
97
|
+
| Package | Version |
|
|
98
|
+
|--------------|---------|
|
|
99
|
+
| httpx | 0.28.1 |
|
|
100
|
+
| questionary | 2.1.1 |
|
|
101
|
+
| rich | 15.0.0 |
|
|
102
|
+
|
|
103
|
+
Requires Python 3.9+.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Publishing
|
|
108
|
+
|
|
109
|
+
A helper script is provided: [`publish.sh`](./publish.sh).
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
./publish.sh # build + upload to PyPI
|
|
113
|
+
./publish.sh --test # upload to TestPyPI instead
|
|
114
|
+
./publish.sh --check # build only, validate with twine, do not upload
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Manual equivalent:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python -m pip install --upgrade build twine
|
|
121
|
+
python -m build
|
|
122
|
+
twine check dist/*
|
|
123
|
+
twine upload dist/*
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Authenticate to PyPI with an API token: create one at
|
|
127
|
+
<https://pypi.org/manage/account/token/> and either set `TWINE_USERNAME=__token__`
|
|
128
|
+
and `TWINE_PASSWORD=<your-token>`, or let twine prompt you.
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
githelp/__init__.py,sha256=R1TFSwPV-RZGfv5x94KmaSbW6-S7wvoxSfj2igibgak,53
|
|
2
|
+
githelp/cli.py,sha256=RJGqDdSs64u3f3CHMdpD91do7r_W51CJdrfimdPl9nY,6370
|
|
3
|
+
githelp/gitignore.py,sha256=Tj-QqbZhYe-1RAjyWIGUkWge2Bd2WBqKlFeXVOXatic,2013
|
|
4
|
+
githelp/gitutil.py,sha256=hzsaW9UAGWMX2vIjkCyR6VScz_dIqXEScG1vOc7DabE,2101
|
|
5
|
+
githelp-0.1.0.dist-info/METADATA,sha256=h-ahn4UEtim0oj9Vtl2vhCJgRioTKljeml0CGt1N0r0,3401
|
|
6
|
+
githelp-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
7
|
+
githelp-0.1.0.dist-info/entry_points.txt,sha256=rHb_oBlXstXQ5QrrvXQikWr7afSLEXBsmHXdLQT7u7A,70
|
|
8
|
+
githelp-0.1.0.dist-info/licenses/LICENSE,sha256=aqonVRTrI_8qReGCiAAL7lAeepyC-BR8kfea7HH4qNY,1068
|
|
9
|
+
githelp-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dheeraj Pai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|