Git Mastery: Permanent Token Auth, Branching, and Pro Commands
The Problem: Typing Credentials Every Time
If you're working with private repositories on GitHub, GitLab, or Bitbucket, you've probably been asked for a username and password (token) every time you git pull, git push, or git clone. This gets old fast.
In this guide, we'll cover how to permanently set up a Git Personal Access Token so you never have to type it again, and then deep-dive into how Git actually works under the hood — branches, commands, and workflows that separate beginners from professionals.
Part 1: Setting Up a Permanent Git Personal Access Token
Step 1: Generate a Personal Access Token (PAT)
Head to your Git provider's settings:
- GitHub:
Settings→Developer settings→Personal access tokens - GitLab:
Settings→Access Tokens - Bitbucket:
Personal settings→App passwords
On GitHub, you'll see two options:
- Fine-grained tokens (recommended by GitHub — the new default)
- Tokens (classic) (simpler but broader permissions)
Both work. The permissions are different for each, so read the section below that matches the token type you're creating.
Step 2: Token Permissions
This is the part most people get wrong. The permissions are completely different depending on which token type you chose.
Option A: Fine-grained Token Permissions (New Default)
When creating a fine-grained token, you'll see two permission categories: Repository permissions and Account permissions. Each one has a dropdown with options like No access, Read-only, and Read and write.
Here's what you need:
Repository permissions (click + Add permissions under Repository):
| Permission | Access Level | Why |
|---|---|---|
| Contents | ✅ Read-only (minimum) or Read and write (if you need to push) | This is the main one — controls access to repo files, commits, and branches |
| Metadata | ✅ Read-only | Mandatory — GitHub auto-selects this. It gives basic repo info |
| Pull requests | ⚠️ Optional: Read-only | Only if you want to create or read PRs via CLI/API |
| Actions | ⚠️ Optional: Read-only | Only if you interact with GitHub Actions |
| Workflows | ⚠️ Optional: Read and write | Only for triggering or modifying workflow files |
| Commit statuses | ❌ Not needed | For CI/CD status checks only |
| Deployments | ❌ Not needed | For deployment API only |
| Environments | ❌ Not needed | For deployment environments only |
| Administration | ❌ Never | Way too powerful — can delete repos |
Account permissions — you likely need none of these for clone/pull:
| Permission | Required? | Why |
|---|---|---|
| GPG keys | ❌ No | Only for managing GPG signing keys |
| Git SSH keys | ❌ No | Only for managing SSH keys via API |
| Gists | ❌ No | Only for GitHub Gists |
| Interaction limits | ❌ No | For moderating interactions |
| Plan | ❌ No | Reads your billing plan |
| Private repository invitations | ❌ No | Only if accepting repo invites via API |
TL;DR for Fine-grained: Under Repository permissions, set Contents to Read and write (or Read-only if you only pull). That's it — Metadata is auto-included. Leave Account permissions empty.
Also, make sure you select the right Repository access:
- All repositories — token works with all your repos
- Only select repositories — pick specific repos (more secure)
Option B: Classic Token Permissions (Scopes)
Classic tokens use a simpler checkbox system. You'll see a list of scopes — just tick the ones you need:
| Scope | Required? | Why |
|---|---|---|
repo (Full control of private repositories) |
✅ Yes | This is the main one — grants read/write to all your private repos. Sub-scopes like repo:status and public_repo are auto-included |
read:org |
⚠️ Recommended | If you work with organization repos (not just personal) |
workflow |
⚠️ Recommended | If your repos use GitHub Actions |
write:packages |
❌ Optional | Only for GitHub Packages |
delete_repo |
❌ Never | Can delete repos — don't enable this |
admin:org |
❌ Never | Way too much power for clone/pull |
TL;DR for Classic: Just check the
repocheckbox. If you work with organizations, also checkread:org. Done.
Step 3: Copy & Save Your Token
Once generated, copy the token immediately. GitHub will never show it again.
# Fine-grained tokens start with:
github_pat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Classic tokens start with:
ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Save it somewhere secure (password manager, encrypted notes, etc).
Step 4: Permanently Store the Token on Your Machine
Now the important part — making Git remember this token forever.
Method 1: Git Credential Store (Simple, stores in plain text)
# Tell Git to store credentials permanently
git config --global credential.helper store
# Now do any git operation that requires auth:
git clone https://github.com/your-username/private-repo.git
# Enter your username and paste the token as the password
# Git will never ask again!
The credentials are saved to ~/.git-credentials in this format:
https://your-username:[email protected]
Method 2: Manually Write the Credentials File (Skip the prompt)
If you want to skip the interactive prompt entirely:
# Set the credential helper
git config --global credential.helper store
# Write credentials directly
echo "https://YOUR_USERNAME:[email protected]" >> ~/.git-credentials
# For GitLab:
echo "https://YOUR_USERNAME:[email protected]" >> ~/.git-credentials
Method 3: Git Credential Cache (Temporary, more secure)
If you don't want credentials stored permanently on disk:
# Cache credentials for 8 hours (28800 seconds)
git config --global credential.helper 'cache --timeout=28800'
Method 4: Using the URL Directly (Quick clone)
For a one-off clone without configuring anything:
git clone https://YOUR_USERNAME:[email protected]/your-username/private-repo.git
⚠️ Security Note: Method 1 and 2 store credentials in plain text on your disk. If you're on a shared machine, use Method 3 or a system keychain manager instead.
Step 5: Verify It Works
# Test the connection
git ls-remote https://github.com/your-username/private-repo.git
# If you see refs listed, you're good!
# If you get a 403, your token doesn't have the right permissions.
Part 2: How Git Actually Works
Understanding Git's internals will transform you from someone who just memorizes commands to someone who truly understands what's happening.
The Three Trees of Git
Git manages your code through three main "trees" (areas):
graph LR
A["📁 Working Directory<br/><i>Your actual files</i>"] -->|git add| B["📋 Staging Area<br/><i>Index / ready to commit</i>"]
B -->|git commit| C["🗄️ Local Repository<br/><i>.git directory</i>"]
C -->|git push| D["☁️ Remote Repository<br/><i>GitHub / GitLab</i>"]
D -->|git pull| A
D -->|git fetch| C
D -->|git clone| A
style A fill:#0d1117,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9
style B fill:#0d1117,stroke:#39ff14,stroke-width:2px,color:#c9d1d9
style C fill:#0d1117,stroke:#bc8cff,stroke-width:2px,color:#c9d1d9
style D fill:#0d1117,stroke:#ff7b72,stroke-width:2px,color:#c9d1d9
- Working Directory — The actual files you see and edit on your machine.
- Staging Area (Index) — A "preparation zone" where you select which changes to include in the next commit.
- Repository (.git) — The full history of your project, stored in
.git/.
The Complete Git Lifecycle
flowchart TD
Start["🆕 Start Project"] --> Init["git init / git clone"]
Init --> Edit["✏️ Edit Files"]
Edit --> Status["git status<br/><i>Check what changed</i>"]
Status --> Stage["git add .<br/><i>Stage changes</i>"]
Stage --> Commit["git commit -m 'message'<br/><i>Save snapshot</i>"]
Commit --> Push["git push origin main<br/><i>Upload to remote</i>"]
Push --> Edit
Push --> PR["🔀 Create Pull Request"]
PR --> Review["👀 Code Review"]
Review --> Merge["✅ Merge to main"]
Merge --> Pull["git pull origin main<br/><i>Sync local</i>"]
Pull --> Edit
style Start fill:#0d1117,stroke:#39ff14,stroke-width:2px,color:#c9d1d9
style Init fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
style Edit fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
style Status fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#c9d1d9
style Stage fill:#161b22,stroke:#39ff14,stroke-width:1px,color:#c9d1d9
style Commit fill:#161b22,stroke:#39ff14,stroke-width:1px,color:#c9d1d9
style Push fill:#161b22,stroke:#bc8cff,stroke-width:1px,color:#c9d1d9
style PR fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#c9d1d9
style Review fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#c9d1d9
style Merge fill:#161b22,stroke:#39ff14,stroke-width:1px,color:#c9d1d9
style Pull fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
Part 3: Branching — The Most Powerful Feature
Branches are what make Git truly powerful. They let you work on features, fixes, and experiments without touching the main codebase.
How Branches Work
Think of branches as parallel timelines. The main (or master) branch is your production-ready code. Feature branches are where you experiment.
gitGraph
commit id: "Initial"
commit id: "Setup"
branch feature/login
commit id: "Add login UI"
commit id: "Add auth logic"
checkout main
commit id: "Hotfix: typo"
branch feature/dashboard
commit id: "Dashboard layout"
commit id: "Add charts"
checkout main
merge feature/login id: "Merge login" tag: "v1.1"
checkout feature/dashboard
commit id: "Polish UI"
checkout main
merge feature/dashboard id: "Merge dashboard" tag: "v1.2"
commit id: "Release prep"
Branch Naming Conventions
Use a consistent naming pattern. Here's the professional standard:
| Branch Type | Pattern | Example |
|---|---|---|
| Feature | feature/description |
feature/user-auth |
| Bug Fix | fix/description |
fix/login-crash |
| Hotfix | hotfix/description |
hotfix/security-patch |
| Release | release/version |
release/v2.0.0 |
| Experiment | experiment/description |
experiment/new-ui |
Essential Branch Commands
# Create a new branch and switch to it
git checkout -b feature/my-feature
# Or using the newer command (Git 2.23+)
git switch -c feature/my-feature
# List all branches (* = current branch)
git branch -a
# Switch to an existing branch
git checkout main
# or
git switch main
# Delete a branch (local)
git branch -d feature/my-feature
# Delete a branch (remote)
git push origin --delete feature/my-feature
# Rename current branch
git branch -m new-branch-name
Merging vs Rebasing
Two ways to integrate changes from one branch to another:
graph LR
subgraph Merge ["🔀 Merge (preserves history)"]
direction TB
M1["main"] --- M2["commit A"]
M2 --- M3["commit B"]
M3 --- M4["merge commit ✅"]
F1["feature"] --- F2["commit X"]
F2 --- F3["commit Y"]
F3 --- M4
end
subgraph Rebase ["♻️ Rebase (linear history)"]
direction TB
R1["main"] --- R2["commit A"]
R2 --- R3["commit B"]
R3 --- R4["commit X'"]
R4 --- R5["commit Y'"]
end
style Merge fill:#0d1117,stroke:#39ff14,stroke-width:2px,color:#c9d1d9
style Rebase fill:#0d1117,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9
# Merge: keeps all history, creates a merge commit
git checkout main
git merge feature/my-feature
# Rebase: replays your commits on top of main (cleaner history)
git checkout feature/my-feature
git rebase main
Rule of thumb: Use merge for shared branches. Use rebase for your personal feature branches before merging.
Part 4: Professional Git Commands
Daily Workflow Commands
# Check status (what changed?)
git status
# See exactly what changed (line by line)
git diff
# See staged changes
git diff --staged
# Add specific files
git add src/app.js src/utils.js
# Add all changes
git add .
# Commit with a message
git commit -m "feat: add user authentication"
# Push to remote
git push origin main
# Pull latest changes (fetch + merge)
git pull origin main
Commit Message Convention
Follow the Conventional Commits standard for professional-grade messages:
<type>(<scope>): <description>
[optional body]
[optional footer]
| Type | When to Use |
|---|---|
feat |
A new feature |
fix |
A bug fix |
docs |
Documentation changes |
style |
Formatting, no code change |
refactor |
Code restructuring |
test |
Adding tests |
chore |
Maintenance tasks |
Examples:
git commit -m "feat(auth): add Google OAuth login"
git commit -m "fix(api): handle null response from server"
git commit -m "docs: update README with setup instructions"
git commit -m "refactor(utils): simplify date formatting logic"
Stashing — Saving Work Without Committing
# Save your current changes temporarily
git stash
# Save with a description
git stash push -m "WIP: dashboard layout changes"
# List all stashes
git stash list
# Apply the latest stash (keeps it in the list)
git stash apply
# Apply and remove from the list
git stash pop
# Apply a specific stash
git stash apply stash@{2}
# Drop a specific stash
git stash drop stash@{0}
Undoing Mistakes
# Undo the last commit but keep changes staged
git reset --soft HEAD~1
# Undo the last commit and unstage changes
git reset --mixed HEAD~1
# ⚠️ Undo the last commit and DELETE all changes
git reset --hard HEAD~1
# Undo a specific file change
git checkout -- path/to/file.js
# Create a NEW commit that reverses a previous commit (safe for shared branches)
git revert <commit-hash>
flowchart LR
A["git reset --soft"] -->|"Undo commit<br/>Keep staged"| B["Staging Area"]
C["git reset --mixed"] -->|"Undo commit<br/>Unstage files"| D["Working Directory"]
E["git reset --hard"] -->|"⚠️ Undo commit<br/>DELETE everything"| F["💀 Gone"]
G["git revert"] -->|"New commit<br/>Safe undo"| H["✅ History preserved"]
style A fill:#161b22,stroke:#39ff14,stroke-width:1px,color:#c9d1d9
style B fill:#161b22,stroke:#39ff14,stroke-width:1px,color:#c9d1d9
style C fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#c9d1d9
style D fill:#161b22,stroke:#f0883e,stroke-width:1px,color:#c9d1d9
style E fill:#161b22,stroke:#ff7b72,stroke-width:1px,color:#c9d1d9
style F fill:#161b22,stroke:#ff7b72,stroke-width:1px,color:#c9d1d9
style G fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
style H fill:#161b22,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
Log & History (Like a Pro)
# Beautiful one-line log
git log --oneline --graph --all --decorate
# See last 10 commits
git log -n 10
# Search commits by message
git log --grep="auth"
# See who changed what (blame)
git blame src/app.js
# See changes in a specific commit
git show <commit-hash>
# Compare two branches
git diff main..feature/my-feature
Remote Management
# See all remotes
git remote -v
# Add a new remote
git remote add upstream https://github.com/original-owner/repo.git
# Fetch from all remotes
git fetch --all
# Prune deleted remote branches
git fetch --prune
# Change remote URL (useful when switching from HTTPS to SSH)
git remote set-url origin [email protected]:username/repo.git
Cherry-Pick — Steal a Single Commit
# Apply a specific commit from another branch to your current branch
git cherry-pick <commit-hash>
# Cherry-pick without auto-committing (to review first)
git cherry-pick --no-commit <commit-hash>
Part 5: Quick Reference Cheat Sheet
mindmap
root((Git Commands))
Setup
git init
git clone
git config
Basics
git add
git commit
git status
git diff
Branching
git branch
git checkout
git switch
git merge
git rebase
Remote
git push
git pull
git fetch
git remote
Undo
git reset
git revert
git stash
git checkout --
Advanced
git cherry-pick
git blame
git log
git tag
Power Aliases
Add these to your ~/.gitconfig for turbocharged Git:
[alias]
s = status
co = checkout
br = branch
ci = commit
lg = log --oneline --graph --all --decorate
last = log -1 HEAD
unstage = reset HEAD --
amend = commit --amend --no-edit
yolo = push --force-with-lease
wip = stash push -m "WIP"
unwip = stash pop
Now you can use:
git s # instead of git status
git co main # instead of git checkout main
git lg # beautiful git log
git amend # amend last commit silently
git yolo # force push (safely)
Conclusion
You should now have:
- ✅ A permanent Git token that never asks for credentials again
- ✅ A solid understanding of how Git works internally
- ✅ Branching strategies used by professional teams
- ✅ A toolkit of pro commands for your daily workflow
Git is one of those tools where investing time to learn it deeply pays off every single day. Stop googling the same commands — internalize them, make them muscle memory, and watch your productivity skyrocket.
"Git is the duct tape of the internet." — Nobody, but it should be someone.