-
Notifications
You must be signed in to change notification settings - Fork 0
2 githelp explain command #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
87135f5
4df0403
8e4b03f
0c3dadc
b9adbbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| recursive-include src/githelp/data/overlays *.yml |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #how the project is built | ||
| [build-system] | ||
| requires = ["setuptools>=61.0"] | ||
| build-backend = "setuptools.build_meta" | ||
|
|
||
| #project's metadata | ||
| [project] | ||
| name = "githelp" | ||
| version = "0.1.0" | ||
| description = "example CLI using Click." | ||
| authors = [{ name = "Jimmy Zhang", email = "jimmy_zhang@uri.edu" }] | ||
| readme = "README.md" | ||
| requires-python = ">=3.9" | ||
| dependencies = ["click>=8.1", "pyyaml>=6.0"] | ||
|
|
||
| #this creates the 'githelp' command that point to the click group | ||
| [project.scripts] | ||
| githelp = "githelp.cli:githelp_cli" | ||
|
|
||
| #tell the setuptool to include non py file | ||
| [tool.setuptools] | ||
| include-package-data = true | ||
|
|
||
| #include the yml file inside the install so that it can be found in the runtime | ||
| [tool.setuptools.package-data] | ||
| githelp = ["data/overlays/*.yml"] | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| __all__ = ["__version__"] | ||
| __version__ = "0.1.0" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import click | ||
| from .overlays import load_overlay, render_overlay, render_menu | ||
|
|
||
| @click.group( | ||
| help="githelp terminal first Git helper.", | ||
| invoke_without_command=True, | ||
| add_help_option=False, | ||
| ) | ||
| @click.option("-h", "--help", "show_help", is_flag=True, help="Provides option menu") | ||
| @click.pass_context | ||
| def githelp_cli(context, show_help): | ||
| if show_help or context.invoked_subcommand is None: | ||
| click.echo(context.get_help()) | ||
| click.echo() | ||
| click.echo(render_menu()) | ||
| if context.invoked_subcommand is None: | ||
| context.exit(0) | ||
|
|
||
| @githelp_cli.command(name="list", help="List of available githelp tip pages.") | ||
| def list_cmd(): | ||
| '''List all available githelp tip.''' | ||
| click.echo(render_menu()) | ||
|
|
||
| @githelp_cli.command(name="explain", help="Show githelp overlay tips for a git subcommand.") | ||
| @click.argument("cmd") | ||
| def explain_cmd(cmd): | ||
| '''Show tips for a specific git subcommand''' | ||
| tips = load_overlay(cmd) | ||
| if tips: | ||
| click.echo(render_overlay(tips)) | ||
| else: | ||
| click.echo(f"githelp tips\n\nNo tips found for '{cmd}'.") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| pull: | ||
| command: pull | ||
| summary: "A git command that fetches from a remote repository and merges into the current branch (fetch + merge)." | ||
| when_to_use: | ||
| - "You want to get the latest updates into your local branch." | ||
| examples: | ||
| - cmd: "git pull" | ||
| say: "Fetches from your branch's tracked remote (usually 'origin') and merges into your current branch." | ||
|
|
||
| commit: | ||
| command: commit | ||
| summary: "Create a snapshot of staged changes; keep messages short and imperative." | ||
| when_to_use: | ||
| - "You have staged changes and want to record them." | ||
| examples: | ||
| - cmd: "git commit -m \"messages\"" | ||
| say: "Short, informative summary." | ||
|
|
||
| add: | ||
| command: add | ||
| summary: "Stages changes like new, modified, or deleted files to be included in the next commit. This also moves files from working directory to the staging area." | ||
| when_to_use: | ||
| - "When you want to stage changes this could include new, modified, or deleted files to the repo." | ||
| examples: | ||
| - cmd: "git add ." | ||
| say: "add all file" | ||
| - cmd: "git add <name of the changed file>" | ||
| say: "add a file" | ||
|
|
||
| status: | ||
| command: status | ||
| summary: "command provides an overview of the current state of your Git repository. this is done by checking pointer and compare them to the current directory" | ||
| when_to_use: | ||
| - "when you want to check the differences/changes of current vs before" | ||
| examples: | ||
| - cmd: "git status" | ||
| say: "overview changes" | ||
|
|
||
| clone: | ||
| command: clone | ||
| summary: "Creates a copy of a remote repository on your local machine." | ||
| when_to_use: | ||
| - "you want to use this to create a local copy of an existing Git repository" | ||
| examples: | ||
| - cmd: "git clone <github repository link>" | ||
| - say: "Downloads the remote repo into a new local folder." | ||
|
|
||
| checkout: | ||
| command: checkout | ||
| summary: "switch between branch" | ||
| when_to_use: | ||
| - "you want to use this to navigate between difference branches" | ||
| examples: | ||
| - cmd: "git checkout <branch name>" | ||
| say: "Switches your working directory to the specified branch." | ||
|
|
||
| branch: | ||
| command: branch | ||
| summary: "Lists all branches in your repository" | ||
| when_to_use: | ||
| - "you use this to check what branch you're on" | ||
| examples: | ||
| - cmd: "git branch" | ||
| say: "Shows all local branches and highlights the one you're on." | ||
| - cmd: "git branch -m <new-branch-name>" | ||
| say: "Renames the current branch to the new name." | ||
|
|
||
| logs: | ||
| command: logs | ||
| summary: "Displays the commit history of the repository. " | ||
| when_to_use: | ||
| - "you want to use this to see commit hashes, authors, dates, and messages." | ||
| examples: | ||
| - cmd: git logs | ||
| say: "information of commit history" | ||
|
|
||
| tag: | ||
| command: tag | ||
| summary: "command in Git is used to mark specific points in a repository's history as important." | ||
| when_to_use: | ||
| - "use this to mark a particular point in the commit ancestry chain." | ||
| examples: | ||
| - cmd: git tag | ||
| say: "Lists all tags that mark important commits in the repo." | ||
|
|
||
| rebase: | ||
| command: rebase | ||
| summary: "Moves or reapplies commits from one branch on top of another to keep history linear." | ||
| when_to_use: | ||
| - "You want to update your feature branch on top of the latest main or another branch." | ||
| - "You want a cleaner, straight line commit history instead of a lot of merge commits." | ||
| examples: | ||
| - cmd: "git rebase main" | ||
| say: "Replay your current branch’s commits on top of the latest 'main' branch." | ||
| - cmd: "git rebase origin/main" | ||
| say: "Rebase your work on top of the 'origin/main' branch from the remote." | ||
|
|
||
| fetch: | ||
| command: fetch | ||
| summary: "Downloads new commits, branches, and tags from the remote without changing local branches or files." | ||
| when_to_use: | ||
| - "You want to see what changed on the remote before merging or rebasing." | ||
| - "You want to update remote-tracking branches like origin/main without touching your current branch." | ||
| examples: | ||
| - cmd: "git fetch" | ||
| say: "Gets all new data from the default remote (usually 'origin') without modifying your current branch." | ||
| - cmd: "git fetch origin main" | ||
| say: "Fetches only updates for the 'main' branch from 'origin'." | ||
|
|
||
| push: | ||
| command: push | ||
| summary: "Sends your local commits to a remote repository branch." | ||
| when_to_use: | ||
| - "You want to upload your local commits so they appear on GitHub or another remote." | ||
| - "You want to share your changes with others or back them up remotely." | ||
| examples: | ||
| - cmd: "git push" | ||
| say: "Pushes your current branch to its tracked remote branch." | ||
| - cmd: "git push origin <branch-name>" | ||
| say: "Pushes the specified local branch to the 'origin' remote." | ||
|
|
||
| merge: | ||
| command: merge | ||
| summary: "Combines another branch into the current branch by creating a merge commit." | ||
| when_to_use: | ||
| - "You want to bring changes from one branch into another and keep full history from both." | ||
| - "You want to integrate a completed feature branch into main (or another base branch)." | ||
| examples: | ||
| - cmd: "git merge <branch-name>" | ||
| say: "Merges the named branch into your current branch, creating a merge commit." | ||
| - cmd: "git merge main" | ||
| say: "Brings the latest changes from 'main' into your current branch." | ||
|
|
||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,177 @@ | ||||||
| import os | ||||||
| import yaml | ||||||
|
|
||||||
| #build absolute path to githelp/data/overlays | ||||||
| THIS_DIR = os.path.dirname(os.path.abspath(__file__)) | ||||||
| DATA_DIR = os.path.join(THIS_DIR, "data", "overlays") | ||||||
| #master dictionary file: to /src/githelp/data/overlays/tips.yml | ||||||
| MASTER_FILE = os.path.join(DATA_DIR, "tips.yml") | ||||||
|
Comment on lines
+7
to
+8
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's not use the term |
||||||
|
|
||||||
|
|
||||||
| def read_yaml(path: str): | ||||||
| ''' | ||||||
| Read a YAML file into a Python object. | ||||||
|
|
||||||
| Parameters | ||||||
| ---------- | ||||||
| path : str | ||||||
| Path to the YAML file to read. | ||||||
|
|
||||||
| Returns | ||||||
| ------- | ||||||
| dict | ||||||
| Parsed YAML contents. Returns an empty dict if the file is empty | ||||||
| or if any error occurs while reading/parsing. | ||||||
| ''' | ||||||
| try: | ||||||
| #r opens the file for reading in text mode, and encoding="utf-8" tells Python to decode the file's bytes | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| #as UTF-8 text into normal Python strings | ||||||
| with open(path, "r", encoding="utf-8") as f: | ||||||
| #convert YAML content to Python object or empty dict if file is empty | ||||||
| data = yaml.safe_load(f) or {} | ||||||
| #return the data of whatever was loaded | ||||||
| return data | ||||||
| except Exception: | ||||||
| #if error occurs return empty dict | ||||||
| return {} | ||||||
|
|
||||||
| def helper(): | ||||||
|
|
||||||
| data = read_yaml(MASTER_FILE) | ||||||
| return data | ||||||
|
Comment on lines
+38
to
+41
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the point of this function |
||||||
|
|
||||||
| def load_overlay(command: str): | ||||||
| ''' | ||||||
| Get the tips for one git subcommand. | ||||||
|
|
||||||
| This first looks in the master file tips.yml under the given | ||||||
| command name (ex "pull"). If found, it returns the corresponding | ||||||
| dictionary of tips. | ||||||
|
|
||||||
| Parameters | ||||||
| ---------- | ||||||
| command : str | ||||||
| Name of the git subcommand whose overlay should be loaded. | ||||||
|
|
||||||
| Returns | ||||||
| ------- | ||||||
| dict or None | ||||||
| The overlay dictionary for the given command, with a ``"command"`` key | ||||||
| ensured, or ``None`` if no overlay is defined. | ||||||
| ''' | ||||||
| #set data to the master dictionary file | ||||||
| data = helper() | ||||||
| #check to see if the master is a dict and not empty | ||||||
|
Comment on lines
+62
to
+64
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again, remove references to master and why not jsut use the read function directly? |
||||||
| if isinstance(data, dict): | ||||||
| #get the section for this specific command and store it in section | ||||||
| section = data.get(command) | ||||||
| #check if section is a dict | ||||||
| if isinstance(section, dict): | ||||||
|
Comment on lines
+65
to
+69
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if no else, then no need for if |
||||||
| # make sure the section has a command key | ||||||
| # does nothing if "command" already exists | ||||||
| section.setdefault("command", command) | ||||||
| #return the section | ||||||
| return section | ||||||
|
|
||||||
|
|
||||||
| def list_overlay_names(): | ||||||
| ''' | ||||||
| List available subcommand names from tips.yml. | ||||||
|
|
||||||
| Returns | ||||||
| ------- | ||||||
| list of str | ||||||
| Sorted list of subcommand names found in the master tips mapping. | ||||||
| ''' | ||||||
| names = [] | ||||||
| data = helper() | ||||||
| #check if data is a dict and not empty | ||||||
| if not isinstance(data, dict): | ||||||
| return names | ||||||
|
|
||||||
| for key, value in data.items(): | ||||||
| if isinstance(value, dict): | ||||||
| names.append(key) | ||||||
|
Comment on lines
+92
to
+94
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do the values inside need to also be a dict? |
||||||
|
|
||||||
| names.sort() | ||||||
| return names | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wouldn't it be equivalent to just return |
||||||
| def render_overlay(d: dict) -> str: | ||||||
| ''' | ||||||
| Render a single overlay dictionary into a human-readable text block. | ||||||
|
|
||||||
| Parameters | ||||||
| ---------- | ||||||
| d : dict | ||||||
| Overlay dictionary containing keys such as ``"summary"``, | ||||||
| ``"when_to_use"``, and ``"examples"`` (any of which may be absent). | ||||||
|
|
||||||
| Returns | ||||||
| ------- | ||||||
| str | ||||||
| A formatted multi-line string | ||||||
| ''' | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this could be a lot simpler, using fstrings. |
||||||
| #line variable that stores a list of outputs | ||||||
| lines = [] | ||||||
| #append header | ||||||
| lines.append("\ngithelp tips") | ||||||
|
|
||||||
| #get the summary, when to use, and example in the dictionary | ||||||
| summary = d.get("summary") | ||||||
| when_to_use = d.get("when_to_use") | ||||||
| examples = d.get("examples") | ||||||
|
|
||||||
| #summary = true then append the summary text to line list | ||||||
| if summary: | ||||||
| lines.append("") | ||||||
| lines.append(summary) | ||||||
| #when to use = true then append the item in the when to use to line list | ||||||
| if when_to_use: | ||||||
| lines.append("\nWhen to use") | ||||||
| for item in d["when_to_use"]: | ||||||
| lines.append(f" - {item}") | ||||||
| #example = true then append new line and example into line | ||||||
| if examples: | ||||||
| lines.append("\nExamples") | ||||||
| #for each example in d["examples"] | ||||||
| for ex in d["examples"]: | ||||||
| #get the cmd and say | ||||||
| cmd = ex.get("cmd", "") | ||||||
| say = ex.get("say", "") | ||||||
| lines.append(f" $ {cmd}") | ||||||
| # If say exists, indent it by 3 spaces for formatting | ||||||
| if say: | ||||||
| lines.append(f" {say}") | ||||||
| #append a new line at the end | ||||||
| lines.append("") | ||||||
| #join all lines into a single string and return it | ||||||
| return "\n".join(lines) | ||||||
|
|
||||||
| def render_menu(): | ||||||
| ''' | ||||||
| Render the main menu text for githelp. | ||||||
|
|
||||||
| Returns | ||||||
| ------- | ||||||
| str | ||||||
| A formatted multi-line string describing available commands. | ||||||
| ''' | ||||||
| #declare an empty list called lines | ||||||
| lines = [] | ||||||
| #display commands | ||||||
| lines.append("Commands:") | ||||||
| lines.append(" - explain <subcommand> Show an explaination for a git subcommand") | ||||||
| lines.append(" - list List available tip pages\n") | ||||||
|
|
||||||
| #name variable that stores the list of overlay names | ||||||
| names = list_overlay_names() | ||||||
| #if name is true then append the available tip pages to line list | ||||||
| if names: | ||||||
| lines.append("Available tip pages:") | ||||||
| #for each name in names | ||||||
| for n in names: | ||||||
| #append each name with a dash and space before it for formatting | ||||||
| lines.append(f" - {n}") | ||||||
| else: | ||||||
| #if no names found append no tip pages found to line list | ||||||
| lines.append("No tip pages found.") | ||||||
| return "\n".join(lines) | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is equivalent but slightly more compact