Two Tools, One Job#

git and gh are different tools that work on the same codebase. Understanding the split helps you use them together effectively.

git talks to your local repository and git remotes. It manages commits, branches, merges, stashes, history. Everything git does is about the version control state of your code.

gh talks to the GitHub API. It manages pull requests, issues, GitHub Actions runs, releases, and repository settings. It’s essentially a CLI wrapper around GitHub’s web interface.

Used together, they let you complete most software development workflows entirely from the terminal. This post walks through those workflows in order from simplest to most powerful.

Getting Started: Install Both#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# git — you probably already have it
git --version

# gh — GitHub CLI
# Arch / CachyOS:
sudo pacman -S github-cli

# Debian/Ubuntu:
sudo apt install gh

# macOS:
brew install gh

# After installing, authenticate:
gh auth login
# → follow the prompts, choose browser or token auth

The Daily Development Loop#

Start from a Clean Main#

1
2
git checkout main
git pull --rebase   # rebase keeps history clean vs merge

Always start feature work from an updated main. --rebase replays your local commits on top of the remote changes instead of creating a merge commit.

Create a Branch from an Issue#

1
2
3
4
5
6
7
8
# List open issues to find what to work on
gh issue list

# Create a branch linked to issue #42 and check it out
gh issue develop 42 --checkout
# → creates branch: 42-fix-gap-detection-on-restart
# → checks it out
# → the branch is now linked to the issue on GitHub

gh issue develop is underused. The branch name is derived from the issue title, so you don’t name it manually. When the PR for this branch merges, GitHub automatically closes issue #42.

Stage Changes Carefully#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# See what changed
git status
git diff

# Stage specific files (safer than git add .)
git add internal/backfill/sync.go

# Stage specific hunks interactively (best practice)
git add -p internal/backfill/sync.go
# → shows each changed section one by one
# → y to stage, n to skip, s to split into smaller hunks

git add -p (patch mode) is the habit that prevents accidental commits of debug code, TODO comments, or unrelated changes. You review every change before it goes into the commit.

Commit#

1
2
3
4
5
6
git commit -m "fix: detect gaps across process restarts

Sequence checker previously reset to zero on restart, allowing
the first incoming trade to pass the check regardless of missed
trades during downtime. Now initialises from the last persisted
ID in Redis on startup."

Conventional Commits format: type: short description. Common types: feat (new feature), fix (bug fix), docs, refactor, test, chore. This format lets tools auto-generate changelogs and makes git log readable at a glance.

Push and Open a PR#

1
2
3
4
5
6
7
8
# Push to origin with tracking (first time for this branch)
git push -u origin HEAD

# Open a PR — --fill uses branch name + commits to prefill title/body
gh pr create --fill

# Or open as draft while still working
gh pr create --fill --draft

--fill reads your commit messages and prefills the PR title and body. For well-written commits, this is often good enough as-is. You can also pass --body for a custom description or omit --fill to write the PR description interactively.

Monitoring CI Without Leaving the Terminal#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# See CI status on the current PR
gh pr checks

# Watch a run in real-time (streaming output)
gh run watch

# List recent workflow runs
gh run list --limit 10

# See the logs for a failed run without opening GitHub
gh run view --log-failed

# Re-run a failed job
gh run rerun --failed

gh run watch is the best one. Instead of: open browser → navigate to repo → click Actions → click on run → click on job → wait for it to update — you run gh run watch in a terminal tab and see the streaming output directly. Much faster feedback loop.

Code Review in the Terminal#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Checkout someone else's PR to test it locally
gh pr checkout 47

# See the full diff
gh pr diff 47

# View PR status, reviewers, and check results
gh pr view 47

# Leave a review comment
gh pr review 47 --request-changes \
  --body "The nil check on line 84 is missing — this will panic if Redis is unavailable"

# Approve
gh pr review 47 --approve --body "LGTM, tested locally"

# Add a comment without reviewing
gh pr comment 47 --body "This fixes the issue I reported last week"
1
2
3
# Merge when ready (squash = one commit per PR, clean history)
gh pr merge 47 --squash --delete-branch
# --delete-branch removes the remote branch after merge

After merging, clean up your local branches:

1
2
3
git fetch --prune  # remove remote-tracking refs that are gone
# Delete local branches whose remote is gone
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -d

Issue Management#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Create an issue from the terminal
gh issue create \
  --title "gap detection misses restart gaps" \
  --body "Steps to reproduce: restart the collector mid-session..." \
  --label "bug" \
  --assignee @me

# List issues by label
gh issue list --label "bug"
gh issue list --label "enhancement" --assignee @me

# Close with a comment
gh issue close 42 --comment "Fixed in #47 — merged to main"
1
2
3
# Search across all issues and PRs
gh search issues "backfill gap" --repo owner/repo --state open
gh search prs "schema sync" --state merged

Releases and Changelogs#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Create a release with auto-generated changelog from PR titles
gh release create v1.2.0 --generate-notes

# The changelog groups PRs by label:
# ## What's Changed
# ### Features
# - feat: add SLT candle alignment #43
# ### Bug Fixes
# - fix: detect gaps on restart #47

# Upload build binaries to the release
gh release upload v1.2.0 \
  build/theron-linux-amd64 \
  build/theron-darwin-arm64

--generate-notes works best when your PR titles follow Conventional Commits. GitHub groups feat: PRs under “Features” and fix: PRs under “Bug Fixes” automatically.

Useful git Aliases#

Add these to ~/.gitconfig under [alias]:

1
2
3
4
5
6
[alias]
    lg    = log --oneline --graph --decorate --all
    last  = show --stat HEAD
    undo  = reset --soft HEAD~1          # undo last commit, keep changes staged
    ri    = rebase -i HEAD~              # interactive rebase
    recent = branch --sort=-committerdate --format='%(refname:short) %(committerdate:relative)'

git lg gives you a visual ASCII graph of your branch history. git undo is the safe “I committed too early” escape hatch — it keeps your changes staged, just removes the commit. git recent shows branches sorted by when you last touched them.

The Safety Net: git reflog#

Before we close: the most important recovery tool in git.

1
git reflog

Every time HEAD moves — commit, checkout, reset, rebase — git records it in the reflog. Even after git reset --hard, an accidental branch delete, or a bad rebase, the reflog shows you where HEAD was before:

abc1234 HEAD@{0}: reset: moving to HEAD~3
def5678 HEAD@{1}: commit: fix: detect gaps on restart  ← your lost commit

Recover it:

1
2
3
git checkout -b recovery-branch def5678
# or
git cherry-pick def5678

The reflog is local only (90 day expiry by default). It doesn’t sync to GitHub. But within that window, very few git operations are truly irreversible — most disasters are recoverable from reflog.

The Complete No-Browser Workflow#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
gh issue develop 42 --checkout      # start work
# ... write code ...
git add -p                           # review every change
git commit -m "fix: description"     # commit
git push -u origin HEAD              # push
gh pr create --fill --draft          # open draft PR
gh run watch                         # watch CI
gh pr ready                          # mark ready for review
gh pr merge --squash --delete-branch # merge
git fetch --prune                    # clean up

From issue to merged PR without opening a browser once. Not because browser is bad — but because staying in the terminal means less context switching, and less context switching means more focus.