Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- **Internal Utils:** Centralized browser opening logic and updated the auth manager to support repo creation tokens.
- `gitgo new` token prompt now opens the classic PAT page by default, making it easier to create non-expiring tokens.
- **Python Scaffolding:** `gitgo init <name> python` now generates a modern `pyproject.toml` and `.python-version` file instead of `requirements.txt`.
- **Template URLs:** `gitgo init --template` now supports full GitHub URLs (e.g., `https://github.com/owner/repo` or `.git` extensions) in addition to the short `owner/repo` format.

### Fixed
- Fixed `gitgo new` opening the browser on every call. It now saves the token to git config (`gitgo.github-token`) after the first paste.
Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,13 @@ src/pygitgo/
│ ├── git_branch.py # Branch queries: get current branch, check existence, create
│ ├── git_core.py # Core write operations: commit, init, push
│ ├── git_remote.py # Remote sync, rebase recovery, connection checks
│ ├── init.py # gitgo init handler (local project scaffolding)
│ ├── jump.py # Safe branch switching with Try-and-Revert engine
│ ├── link.py # Init, link remote, and push a new repo
│ ├── new.py # gitgo new handler (quickstart: init + repo + link)
│ ├── pull.py # Safe pull with auto-stash and rebase
│ ├── push.py # Stage, commit, push (with selective staging support)
│ ├── repo.py # gitgo repo handler (remote GitHub repo creation)
│ ├── staging.py # Interactive file picker for selective commits
│ ├── stash.py # Low-level git stash wrappers (push/pop/apply/drop/list/clear)
│ ├── state.py # Named stash interface (save/load/delete/list)
Expand Down
96 changes: 86 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ If GitGo saves you time, give it a star. If you want to go further, sponsoring h

---

GitGo wraps your most-typed git commands into shorter ones. It covers init, add, commit, push, branch, and stash. It also includes features most wrappers leave out: SSH key setup, HTTPS-to-SSH conversion, and a named stash interface called state management.
GitGo wraps your most-typed git commands into shorter ones. It covers init, scaffold, add, commit, push, branch, and stash. It also includes features most wrappers leave out: SSH key setup, HTTPS-to-SSH conversion, a named stash interface called state management, and a one-shot quickstart command that takes you from nothing to a live GitHub repo in seconds.

```bash
# Instead of this:
git init && git add . && git commit -m "init" && git remote add origin <url> && git push -u origin main
mkdir my-app && cd my-app && git init && git add . && git commit -m "init" && git remote add origin <url> && git push -u origin main

# Run this:
gitgo link https://github.com/username/repo.git "init"
gitgo new my-app python
```

---
Expand Down Expand Up @@ -63,6 +63,9 @@ gitgo link https://github.com/username/repo.git "init"
## Features

- **Single commands for linking, pushing, and stashing.** No more chaining five commands together.
- **Quickstart with `new`:** One command to scaffold your project, create the GitHub repo, and push it. No switching tabs, no manual steps.
- **Project scaffolding with `init`:** Generates a language-specific project structure with a `.gitignore` from GitHub's official templates. Supports Python, Node, Rust, Go, C#, and more.
- **Remote repo creation with `repo`:** Creates a GitHub repo directly from the terminal without touching a browser.
- **Undo:** Roll back commits, unstage files, discard local changes, or revert pushes. The subcommands say what they do: `undo commit`, `undo add`, `undo changes`, `undo link`, `undo push`.
- **Branch switching with `jump`:** Stashes your uncommitted work, moves to the target branch, syncs with main, and pops the stash. If a merge conflict occurs, the Try-and-Revert engine offers to roll the whole operation back.
- **State management:** Named, indexed stash. Run `state list` to see what you saved. No more `stash@{2}` archaeology.
Expand Down Expand Up @@ -138,15 +141,39 @@ gitgo user login

For a full walkthrough with screenshots, see the [Login Guide](docs/login-guide.md).

### 2. Link a New Project to GitHub
### 2. Start a New Project From Scratch

Point GitGo at an existing empty GitHub repo. It initializes Git, stages everything, commits, and pushes — including pulling unrelated histories if the remote isn't empty.
One command scaffolds the local project, creates the GitHub repo, and pushes it. No tab switching.

```bash
gitgo new my-app python
gitgo new my-app rust --private
gitgo new my-app # no scaffold, just the repo and push
```

Or use the individual steps if you want more control:

```bash
# Step 1: scaffold the project locally
gitgo init my-app python

# Step 2: create the remote GitHub repo
cd my-app
gitgo repo my-app --private

# Step 3: connect and push
gitgo link https://github.com/username/my-app.git
```

### 3. Link an Existing Project to GitHub

Point GitGo at an existing empty GitHub repo. It initializes Git, stages everything, commits, and pushes, including pulling unrelated histories if the remote isn't empty.

```bash
gitgo link https://github.com/username/repo.git "Initial commit"
```

### 3. Push Changes
### 4. Push Changes

```bash
# Push to an existing branch
Expand All @@ -156,15 +183,15 @@ gitgo push main "Fix auth bug"
gitgo push -n feat/login "Add login flow"
```

### 4. Switch Branches
### 5. Switch Branches

Switch branches with uncommitted work in progress. `jump` stashes your changes, moves to the target branch, syncs with main, and pops the stash. If the pop triggers a conflict, it offers to abort and restore the repo to its prior state.

```bash
gitgo jump feat/new-login
```

### 5. Undo Mistakes
### 6. Undo Mistakes

Undo recent mistakes with commands named for what they undo.

Expand All @@ -176,15 +203,15 @@ gitgo undo link # Remove remote and undo initial commit
gitgo undo push # DANGER: Revert last push with a force-push
```

### 6. Save Your Work-in-Progress
### 7. Save Your Work-in-Progress

```bash
gitgo state save "halfway through refactor"
gitgo state list
gitgo state load 1
```

### 7. Custom Defaults
### 8. Custom Defaults

```bash
gitgo config set default-branch develop
Expand All @@ -196,6 +223,55 @@ gitgo config get default-branch

## Command Reference

### `gitgo new`

One-shot quickstart. Scaffolds a local project, creates the GitHub remote repo, and pushes, all in one command.

```bash
gitgo new <name> [lang]
gitgo new my-app python # scaffold Python project and push
gitgo new my-app rust --private # private Rust project
gitgo new my-app # no scaffold, just create repo and push
```

| Flag | Description |
|------|-------------|
| `lang` | Language to scaffold. Options: `python`, `node`, `rust`, `go`, `cs`, and more |
| `--template OWNER/REPO` | Use a GitHub template repo instead of a language scaffold |
| `-p`, `--private` | Create a private repository |
| `-d`, `--description TEXT` | Short description shown on GitHub |

### `gitgo init`

Scaffolds a project folder locally. Creates a README, `.gitignore` (fetched from GitHub's official templates), and language-specific starter files.

```bash
gitgo init my-app python # generates pyproject.toml and .python-version
gitgo init my-app node # generates package.json and index.js
gitgo init my-app cs # generates .csproj and Program.cs
gitgo init my-app --template owner/repo # slug format
gitgo init my-app --template https://github.com/owner/repo # full URL accepted too
```

Supported languages: `python` (`py`), `node` (`js`, `ts`), `rust` (`rs`), `go` (`golang`), `csharp` (`cs`), and any language with a `.gitignore` template on GitHub.

### `gitgo repo`

Creates a remote GitHub repository without touching your local files.

```bash
gitgo repo [name] # use current directory name if no name given
gitgo repo my-app --private
gitgo repo my-app -d "My project description"
```

| Flag | Description |
|------|-------------|
| `-p`, `--private` | Create a private repository |
| `-d`, `--description TEXT` | Short description shown on GitHub |

On first run, GitGo opens GitHub's PAT page and prompts you to paste a token with `repo` scope. The token is saved to git config for future calls. If the token expires, GitGo detects the 401 and re-prompts automatically.

### `gitgo push`

Stage, commit, and push in one command.
Expand Down
18 changes: 17 additions & 1 deletion src/pygitgo/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import urllib.error
import zipfile
import json
import re
import io
import os

Expand Down Expand Up @@ -156,6 +157,20 @@ def _fetch_gitignore(resolved_lang):
raise GitGoError(f"Network error fetching gitignore: {e}")


def _parse_template_slug(template):
# For github url
match = re.search(r"github\.com[/:]([^/]+/[^/.]+)", template)
if match:
return match.group(1)
# For repo slug
if re.match(r"^[^/]+/[^/]+$", template):
return template
raise GitGoError(
f"Invalid template format: '{template}'.\n"
"Expected: owner/repo, https://github.com/owner/repo, or https://github.com/owner/repo.git"
)


def _download_and_extract_template(template_slug, target_dir):
url = f"https://api.github.com/repos/{template_slug}/zipball"
req = urllib.request.Request(url, headers={"User-Agent": "GitGo-CLI"})
Expand Down Expand Up @@ -273,7 +288,8 @@ def init_operation(args):
orig_cwd = os.getcwd()
try:
if args.template:
_download_and_extract_template(args.template, target_dir)
slug = _parse_template_slug(args.template)
_download_and_extract_template(slug, target_dir)
elif args.lang:
_scaffold_language(args.lang, target_dir, target_dir)

Expand Down
16 changes: 15 additions & 1 deletion tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,18 @@ def test_init_operation_lang(mock_git_init, mock_scaffold, tmp_path):
init_operation(args)

mock_scaffold.assert_called_once_with("python", args.name, args.name)
mock_git_init.assert_called_once()
mock_git_init.assert_called_once()


def test_parse_template_slug():
from pygitgo.commands.init import _parse_template_slug

assert _parse_template_slug("owner/repo") == "owner/repo"
assert _parse_template_slug("https://github.com/owner/repo") == "owner/repo"
assert _parse_template_slug("https://github.com/owner/repo.git") == "owner/repo"
assert _parse_template_slug("git@github.com:owner/repo.git") == "owner/repo"

import pytest
from pygitgo.exceptions import GitGoError
with pytest.raises(GitGoError):
_parse_template_slug("invalid_format")
Loading