Skip to content
Lab

Developer Reference

Version Control & SDLC

A comprehensive reference covering every stage of the software delivery lifecycle — from planning and branching strategy through CI/CD, governance, and post-deployment monitoring.

  • 14 lifecycle stages
  • 6 reference tools
  • 153 git commands, each rated for safety
  • 27 rescue recipes, tested against real git
1Develop

Planning

Turn a request into a work item a developer can start on without asking a question, and a branch name that points back to it.

  • Every change starts as a written work item, not a conversation — so anyone can pick it up without asking what was meant.
  • A story is ready to start when its acceptance criteria are unambiguous, its dependencies are unblocked, and it fits in a sprint.
  • Acceptance criteria are written as Given/When/Then, so "done" is a fact everyone can check, not an opinion.
  • The branch name carries the issue key, so every commit, PR and deploy traces back to the work item that asked for it.
  • A draft PR opened on day one is a better planning signal than a status update: it shows real progress, in review, before the work is finished.

Match the issue type to the work

Five shapes cover almost everything a team tracks. Pick the narrowest one that fits.

Issue types
TypeUse it forTypical fields
StoryUser-visible feature workAs a / I want to / So that, acceptance criteria, story points
BugA defect: something worked and stopped, or never matched the specSteps to reproduce, expected vs. actual, environment
TaskTechnical work with no direct user visibility (upgrades, refactors)Objective, definition of done, dependencies
EpicA body of work spanning multiple sprints, grouping storiesGoal, scope (in/out), success metrics, target quarter
SpikeTime-boxed research to reduce uncertainty before committing to a designQuestion to answer, time box, decision criteria
  1. Meet the Definition of Ready before starting

    A story a developer cannot start without asking a question is not ready. Check, before it enters a sprint:

    • Acceptance criteria are unambiguous and written as Given/When/Then.
    • The team doing the work estimated it, not the person who filed it.
    • Every dependency is identified and unblocked.
    • Designs or wireframes are attached, for UI work.
    • No open question would block a developer from starting today.
    • It fits comfortably inside a single sprint — if not, it is an epic, not a story.
  2. Write acceptance criteria as Given/When/Then

    Behaviour-driven acceptance criteria state a precondition, an action and an observable result, so "done" can be checked mechanically instead of argued about:

    acceptance-criteria.mdmarkdown
    Given a logged-in user on the dashboard
    When they click "Export to CSV"
    Then a CSV file is downloaded containing all visible rows
    And the filename includes today's date
  3. Open a draft PR on day one for anything that will take more than a day

    A draft pull request cannot be merged until it is marked ready for review, but it is visible, diffable and commentable from the first commit GitHub 2026. It replaces a status update with something reviewers can actually look at, and lets a reviewer flag a wrong direction before the work is finished rather than after.

Priority, separately from size

Priority says how soon; story points say how much work. Conflating them means an easy P3 jumps the queue over a hard P1, or a big P0 sits half-estimated while the team argues about its size.

Priority levels
PriorityMeaningResponse
P0 — CriticalProduction is down, or data is being lostFix immediately, all hands
P1 — HighA core feature is broken for usersFix within the current sprint
P2 — MediumA non-critical degradationNext sprint or sooner
P3 — LowA nice-to-have improvementScheduled as capacity allows
  • A story enters the sprint without acceptance criteria

    The team discovers what "done" means during the PR review, which is the most expensive place to discover it — the code already exists and now has to change shape.
  • The branch name has no connection to the issue

    Six months later, nobody can tell why fix-thing exists, whether it shipped, or what it was for. git log --grep on an issue key finds every commit for a feature; a name like fix-thing is unsearchable.
2Develop

Repo Foundations

Create the repository, its default branch, its ignore and attributes files, and choose how it will scale before the first commit.

  • A repository is a directory plus a hidden .git — git init creates the second part; nothing else changes on disk.
  • .gitignore stops git tracking files it should never track; .gitattributes controls how it treats the files it does.
  • Neither file has any effect on a file git already tracks — untrack a mistakenly committed secret with git rm --cached, then rotate it.
  • A monorepo trades independent deploys for atomic cross-project changes; a polyrepo trades that back for isolation. Neither is free.
  • Sparse checkouts, partial clones and shallow clones each cut a different cost — disk, network, or both — at the price of some command no longer working the normal way.

Start the repository

git initReference

This writes .git/ and nothing else: no commit exists yet, and no files are tracked until you add them. The name of the first branch comes from init.defaultBranch if it is set, or git config --global init.defaultBranch main to set it once for every new repository Git project 2026.

git remote add origin <url>Reference
git add . && git commit -m "Initial commit"
git push -u origin mainReference

README, LICENSE, CONTRIBUTING

Three files a reader looks for before anything else. None is required by git; all three are expected by everyone reading the repository on a host.

Root files
FileAnswers
README.mdWhat this is, how to run it, where to start
LICENSEWhat anyone may legally do with the code — required for it to be reused at all outside the org
CONTRIBUTING.mdHow to propose a change: branch naming, commit style, how to run tests, who reviews
  1. Ignore build output, dependencies and secrets before the first commit

    Write .gitignore before adding files, not after — a pattern only stops git tracking a file it has never seen. A file already committed keeps being tracked no matter what you add to .gitignore later; remove it from the index first with git rm --cached <path> Git project 2026.

  2. Fix line endings and diff behaviour with .gitattributes

    * text=auto lets git normalise line endings to LF in the repository and convert them on checkout per core.autocrlf; marking binary files binary stops git trying to diff or merge them as text; marking a lockfile -diff replaces a multi-thousand-line diff with "Binary files differ" without changing how it is stored or merged Git project 2026.

  3. Generate both files from what the project actually uses

    Pick languages, tools, editors and operating systems below; every rule comes with the reason it is there, and the tester checks a path the same way git check-ignore -v would.

Monorepo vs. polyrepo

One repository per project, or one for everything
MonorepoPolyrepo
Cross-project changesOne commit, one PR, reviewed togetherA PR per repository, coordinated by hand or tooling
CINeeds affected-package detection or every push tests everythingEach repository's pipeline only ever sees its own code
Access controlCoarse — usually the whole repository or nothingFine-grained per repository
Shared toolingOne version of every dependency, enforced by the layoutEach repository can pick its own, and drift

Options for a large repository

Each of these makes one specific thing smaller — files on disk, bytes fetched, or history downloaded — none is free, and they compose.

Scale options
OptionWhat it reducesWhat stops working the normal way
git sparse-checkoutFiles written to disk — only the paths you choose appear in the working treeA path outside the cone will not appear until you add it back
git clone --filter=blob:noneBytes fetched — file contents download on demand, the first time each is neededThe first checkout, and the first diff or blame touching an old file, fetches over the network
git clone --depth NHistory downloaded — only the last N commitslog, blame and bisect only see those N commits; pushing new history back can be rejected
Git LFSRepository size for large binaries — the file is stored on an LFS server, a pointer in gitEveryone who clones needs Git LFS installed, or they get pointer files instead of content
git maintenanceTime spent on gc, repacking and the commit-graph, spread out instead of done all at onceNothing — it runs in the background once <code>git maintenance start</code> schedules it

A clone made with --filter or --depth is a partial or shallow clone respectively; both are configured with --sparse, --filter and --depth on git clone itself Git project 2026, and git sparse-checkout can narrow an existing clone's working tree afterwards Git project 2026. git maintenance start schedules background repository maintenance instead of leaving it to an occasional manual git gc Git project 2026.

  • A secret was committed before .gitignore existed

    Adding the pattern afterwards does not remove it from history — every commit that ever included the file still has it.
    Rescue recipe →
  • A lockfile is in .gitignore

    Without the lockfile committed, every install can resolve different transitive versions — the one thing a lockfile exists to prevent.

Sources

3Develop

Branching Strategy

Decide which branches live long, which live for a day, and how work reaches main — trunk-based, GitHub Flow, GitLab Flow or Git Flow.

  • A branching strategy answers one question: how does a change reach main, and how long does a branch live before it does?
  • Trunk-based development and GitHub Flow keep almost everything short-lived; Git Flow adds long-lived develop and release branches for versioned software.
  • Git Flow's own author, ten years after writing it, now recommends most teams use something simpler Driessen 2010.
  • The right choice follows release cadence and CI maturity, not preference — see the simulator below with your own team's numbers.

Four strategies, compared

Branching strategies
StrategyLong-lived branchesRelease modelSuits
Trunk-based developmentNone — everyone commits to main (or trunk) multiple times a day, behind feature flags for anything incompleteContinuous — main is always releasableHigh-velocity teams with strong CI and feature-flag infrastructure
GitHub FlowNone — main, plus a short-lived branch per pull requestContinuous — main deploys as soon as a PR mergesWeb apps deployed continuously to one environment
GitLab FlowEnvironment branches (e.g. staging, production), or a release branch per versionEither continuous (environment branches) or versioned (release branches) — GitLab documents both variantsTeams that need an explicit environment or version boundary GitHub Flow does not give them
Git Flowdevelop (integration) plus one release branch per versionVersioned, scheduled releases, with parallel hotfix supportSoftware distributed as versioned releases, or supporting multiple versions in the wild at once

GitLab documents its own flow as a family of variants built on top of a GitHub-Flow-like base, not one fixed model GitLab 2026; GitHub's own flow is deliberately the simplest of the four GitHub 2026; trunk-based development is the strategy behind continuous integration's original premise — that everyone's changes meet, in one place, at least daily Hammant 2020.

  1. Match the strategy to release cadence and CI maturity, not preference

    Every long-lived branch is a batch of changes that has not yet met the others — the longer it lives, the more expensive the eventual merge. Continuous delivery wants that cost at zero, which is why trunk-based development and GitHub Flow both minimise long-lived branches; software that ships in dated, supported versions needs somewhere to put a fix for a version that already shipped, which is what Git Flow's release branches are for.

  2. Name branches so the type is obvious without opening them

    Branch naming
    PatternUse it for
    feature/PROJ-123-add-oauthNew functionality
    bugfix/PROJ-456-fix-null-pointerA fix for something already merged
    hotfix/v2.1.1-payment-null-fixAn urgent fix cut from a release branch, not from main
    release/v2.2.0A branch that stabilises one version before it ships
    chore/update-dependenciesNo user-visible change
  • Git Flow adopted for a continuously deployed web app

    Every feature waits for develop, then a release branch, before it can ship — for software that could deploy the moment a PR merges, that delay buys nothing.
  • A feature branch lives for weeks

    It drifts further from main every day it is not merged; when it finally does merge, the conflict is bigger than it would have been on day two. Trunk-based development and GitHub Flow both exist specifically to prevent this.

Sources

4Develop

Local Dev

Move changes between the working tree, the index and HEAD; write commits someone can review, revert and bisect.

  • Every file has up to three versions: in HEAD (the last commit), the index (what the next commit will contain) and the working tree (on disk). Add, commit, restore and reset each copy between two of these.
  • A commit is a unit of review, revert and bisection — one logical change, not one save point.
  • Conventional Commits gives a message a type a machine can read (fix:, feat:, a ! or BREAKING CHANGE: footer for a breaking change), which is what makes an automatic changelog and version bump possible.
  • reset --soft only moves HEAD; reset (mixed) also unstages; reset --hard also overwrites the working tree. Each is a strictly bigger hammer than the last.
  • A..B on git log means commits reachable from B but not A; A...B means either but not both — the second is what a PR diff and git log --left-right usually want.

The three trees, live

Run a command in this stage's terminal — add, commit, restore, reset — and watch which column changes.

  1. Stage what belongs in this commit, not everything you touched

    git add app.js config.jsonReference

    Staging the whole working tree with git add . is fine when every change in it really is one commit; when it is not, stage file by file, or use git add -p to stage individual hunks within a file and leave the rest for a later commit.

  2. One logical change per commit

    The unit of review, revert and bisection is the commit, not the pull request:

    • Revertability — reverting one concern reverts exactly what broke, not unrelated code that happened to ship alongside it.
    • Bisectability — git bisect only finds the right commit if every commit in between is a consistent, buildable state.
    • Review quality — a reviewer reasons about one change at a time; a commit that mixes a rename with a behaviour change hides the behaviour change inside the noise.
  3. Write commit messages a machine and a person can both use

    Conventional Commits gives every message a type, an optional scope, and a description: fix(cart): correct total on discount. fix maps to a patch release and feat to a minor one; a ! before the colon, or a BREAKING CHANGE: footer, means major Conventional Commits contributors 2019. Footers also carry authorship and provenance trailers such as Co-authored-by (a commit with more than one author GitHub 2026) and Signed-off-by (the Developer Certificate of Origin: the signer attests they have the right to submit the change under the project's license Linux Foundation 2004), both parsed the same way by git interpret-trailers Git project 2026.

  4. Fix the last commit or the working tree with the smallest tool that does it

    Undo, from smallest to largest
    CommandUndoesRewrites shared history?
    git commit --amendReplaces the last commit — its message, or its content if you staged more firstYes, if already pushed — don't amend a commit others have based work on
    git restore <path>Discards uncommitted changes to a file, back to what the index hasNo — nothing was committed
    git restore --staged <path>Unstages a file without touching its contentNo
    git reset --soft HEAD~1Moves HEAD back one commit; index and working tree untouched — the commit's changes are now stagedYes, if already pushed
    git reset HEAD~1The same, and also unstages — the changes are now just in the working treeYes, if already pushed
    git reset --hard HEAD~1The same, and also overwrites the working tree — uncommitted changes are goneYes, if already pushed
  5. Set work aside without committing it

    git stash push -m "WIP: add auth"Reference
    git switch main
    git stash popReference

    A stash is a commit outside any branch's history — git stash list shows every one, and popping applies and drops the most recent.

  6. Read history with the flag that answers the actual question

    Reading history
    CommandAnswers
    git log --oneline --graphWhat happened, and in what order, across branches
    git log A..BCommits reachable from B but not A — what B has that A doesn't
    git log A...BCommits reachable from either but not both — the symmetric difference
    git blame <file>Which commit last touched each line
    git log -S"needle"Which commits added or removed an exact string — not just touched the file

    git log --left-right A...B marks each commit with which side of the symmetric difference it is reachable from (< or >) Git project 2026, which is what most "how have these two branches diverged" questions actually want; the range syntax itself — A..B, A...B, and @{upstream} — is gitrevisions' own subject Git project 2026.

Read a diff

Hooks: local checks that run automatically

Common client-side hooks
HookRunsTypical use
pre-commitBefore a commit is createdLint, format, type-check, or scan for secrets
commit-msgAfter the message is written, before the commit is createdEnforce Conventional Commits or a ticket-number prefix
pre-pushBefore git push sends anythingRun the test suite one last time

Hooks live in .git/hooks/, are not cloned with the repository, and must be executable Git project 2026. Most teams install them through a tool (Husky for Node, the Python pre-commit framework) that writes them from a checked-in config, so every clone gets the same ones.

.husky/pre-commitbash

What Husky writes for a Node project; the Python pre-commit framework reads an equivalent .pre-commit-config.yaml instead.

#!/usr/bin/env sh
npx eslint --fix .
npx prettier --write .
git add -u
  1. Work on two branches at once without stashing

    git worktree add ../hotfix hotfix/urgentReference

    This checks out hotfix/urgent into a second directory, sharing the same .git — no stash, no switching back and forth, and both working trees stay usable at the same time.

  • git commit --amend after pushing

    The pushed commit and the amended one are now different objects with the same message — anyone who already pulled the old one now has a divergent history.
    Rescue recipe →
  • A file was forgotten before committing

    Amend it in, rather than adding a second "forgot a file" commit — unless the original commit was already pushed and shared.
    Rescue recipe →
  • git stash pop dropped, and the stash is needed again

    A dropped stash is unreachable from any ref, but its commit is not deleted immediately — the reflog and git fsck --unreachable can often still find it.
    Rescue recipe →

Sources

5Review

Pushing & Collab

Share commits through a remote without losing anyone's work: fetch, pull, push, force-with-lease, merge versus rebase, and conflicts.

  • A remote is just another repository your clone knows the URL of. fetch copies its objects and updates your remote-tracking branches; nothing else changes until you merge or rebase them in.
  • git pull is fetch plus either a merge or a rebase — which one depends on pull.rebase, and git will refuse to guess once your branch has diverged from upstream.
  • A push is rejected when the remote has a commit you don't — fetch and integrate first, or force only if you are certain the remote's version is disposable.
  • --force-with-lease refuses to overwrite a remote-tracking ref that has moved since your last fetch; plain --force has no such check and can silently discard a teammate's push.
  • The rule that makes all of this safe: never rewrite commits other people have already based work on. Rebase and amend freely on a branch that is yours alone; once it's shared, only add to it.

Remotes, fetch and the state that changes

This stage's repository has a teammate who pushed while you were working locally — fetch shows what they did without touching your working tree.

git fetch originReference

Fetch updates origin/main (your remote-tracking branch) to wherever the remote's main now points. It never touches your own main, your index, or your working tree — that is what makes it safe to run at any time, as often as you like.

git pull origin mainReference

Pull is fetch followed by an integration step. Which step depends on pull.rebase: unset or false merges origin/main into your branch; true rebases your branch onto it instead Git project 2026.

  1. Decide merge pull or rebase pull once, in config, not commit by commit

    git pull's two modes
    Merge pull (default)Rebase pull (--rebase, or pull.rebase=true)
    ResultA merge commit joining your local work and the remote'sYour commits replayed on top of the remote's — no merge commit
    History shapeRecords exactly what happened, including the pull itselfLinear, as if you had started from the latest remote
    Safe on a shared branch you also push to?YesOnly if you haven't pushed your local commits yet — rebase rewrites them
    git pull --rebase origin mainRewrites local historyReference
  2. Force only what you are sure is disposable, and check before you overwrite it

    git push --force-with-leaseRewrites shared historyReference

    --force-with-lease fails if the remote-tracking ref has moved since your last fetch — meaning someone pushed something you haven't seen — instead of silently overwriting it. --force-if-includes, used alongside it, additionally verifies that whatever updated the remote-tracking ref in the background is already in your own history before allowing the push Git project 2020. Plain --force has neither check.

    git push --forceRewrites shared historyReference
  3. Choose how a branch rejoins main: merge, --no-ff, squash, or rebase

    Same starting graph, four outcomes — the comparator runs all four through the same commands the terminal uses.

  4. Resolve a conflict, in whichever marker style helps

    git rerere ("reuse recorded resolution") remembers how you resolved a conflict and replays that resolution automatically the next time the same conflict appears — useful when rebasing a long branch keeps hitting the same collision on every replayed commit Git project 2026. git merge --abort (or git rebase --abort) bails out of a merge or rebase in progress and restores the state from immediately before it started.

    git merge --abortDiscards workReference
  • git push rejected: the remote has work you don't

    Fetch and integrate (merge or rebase) before pushing again — the rejection is git refusing to silently drop the remote's commits, not a bug.
    Rescue recipe →
  • Someone force-pushed the branch you're working on

    Your local history and theirs have now diverged in a way a normal pull can't reconcile; check what actually happened before choosing how to reconcile it.
    Rescue recipe →
  • git pull made a merge commit you didn't want

    Set pull.rebase if this branch should always integrate by rebasing — or run git pull --rebase explicitly each time.
    Rescue recipe →

Sources

06

Merge & Pull Request Process

How code moves from a feature branch into the shared trunk safely.

PR Lifecycle

Open: Ready for review. Reviewers can be requested.

PR Template Anatomy

CODEOWNERS Auto-Assignment

# .github/CODEOWNERS
# Global owners — review everything
*                   @team/platform

# Backend — Python services
/src/api/**         @backend-lead @srini

# Frontend — React components
/src/ui/**          @alice @bob

# Infrastructure
/infra/**           @team/devops

# Security-sensitive paths
/src/auth/**        @team/security

Review States

✓
ApprovedLGTM — ready to merge.
✗
Changes RequestedBlocking — must address before merge.
💬
CommentedNon-blocking feedback or questions.

Merge Strategies

Mmerge commit (2 parents)

Creates a merge commit that unites the two histories.

PROFull history preserved
CONHistory can become noisy

PR Size Guide

S< 50 lines

Ideal. Fast review, low risk.

M50–250 lines

Good. Single responsibility, reviewable in one sitting.

L250–500 lines

Acceptable. Consider splitting if possible.

XL500+ lines

Avoid. Approval rubber-stamping risk is high.

07

Code Review Process

How teams verify correctness, share knowledge, and maintain quality standards.

Reviewer Checklist — 8 Dimensions

Comment Types

🚫Blocking

"`user.password` is being logged on line 42 — this is a security incident waiting to happen."

💡Non-blocking

"nit: Could extract this into a `formatDate()` utility. Happy to merge as-is though."

❓Question

"Why are we calling `getUser()` twice here? I may be missing context."

🙌Praise

"Love how you handled the retry logic here — clean and robust."

Review SLA

PriDescriptionSLA
P0Security / Production incident fix< 1 hour
P1Breaking bug, blocking another team< 4 hours
P2Normal feature / bug PR1 business day
P3Docs, chore, low-risk refactor2 business days
Approval Workflow
Minimum 2 approvals required
CODEOWNERS must approve owned paths
Stale reviews dismissed on new push
Branch must be up to date before merge

Review Etiquette

✓ DO
✓Explain the why, not just the what
✓Suggest, don't dictate (use "consider" / "what if")
✓Acknowledge good decisions with praise
✓Separate blocking from non-blocking concerns clearly
✓Review the diff, not the person
✗ DON'T
✗Leave vague comments like "this is wrong"
✗Pile on blocking comments for style nits
✗Ghost a PR — respond even if just "looking at this tomorrow"
✗Approve without reading — rubber-stamping erodes trust
✗Request sweeping refactors unrelated to the PR scope
08

CI/CD Pipeline

Automated verification and delivery — the heartbeat of modern software teams.

Pipeline idle
name: CI/CD Pipeline

on:
  push:
    branches: ['**']
  pull_request:

env:
  IMAGE: ghcr.io/${{ github.repository }}

jobs:
  pipeline:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci
      - run: npm run lint && npm run type-check
      - run: npm test -- --coverage
      - run: npm run test:integration

      - run: npx playwright install --with-deps
      - run: npm run test:e2e
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/

      - uses: github/codeql-action/init@v3
        with:
          languages: javascript, python
      - uses: github/codeql-action/analyze@v3
      - uses: aquasecurity/trivy-action@0.28.0
        with:
          scan-type: fs
          ignore-unfixed: true

      - run: npm run build

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: ${{ env.IMAGE }}:${{ github.sha }}

      - uses: azure/k8s-deploy@v4
        with:
          manifests: k8s/

      - name: Health check
        run: |
          for i in {1..10}; do
            STATUS=$(curl -s -o /dev/null -w "%{http_code}" $URL/health)
            [ "$STATUS" = "200" ] && exit 0
            sleep 3
          done
          exit 1

      - uses: grafana/k6-action@v0.3.0
        with:
          filename: tests/load.js
          flags: --vus 50 --duration 60s
09

Environment Promotion Flow

How a software build travels from laptop to production safely.

Environment Ladder

Click an environment rung to see details.

Blue-Green DeploymentBLUEv1.4.2LIVEGREENv1.5.0IDLELBSwitch to green; reverting to blue is instant
Canary Release5%25%50%100%StableCanary
Feature Flagnew-checkout-flow is OFF — all users see legacy checkout
10

Release Management

Versioning, tagging, changelog generation, and release ceremonies.

Semantic Version Calculator

major
2
.
minor
1
.
patch
0
v2.1.0
MAJOR

Breaking change — incompatible API change. Consumers must update.

MINOR

New feature — backwards-compatible addition. Safe to upgrade.

PATCH

Bug fix — backwards-compatible fix. Always safe to upgrade.

Git Tag Anatomy

git tag -a v2.1.0 -m "Release 2.1.0"
git tagGit tag command
-aAnnotated tag (stored as full Git object with tagger, date, message)
v2.1.0Version string — v prefix is convention, not enforced
-m "…"Tag message — describes the release. Appears in `git show <tag>`

Release Candidates

Click to progress through the RC flow. Each RC gets deployed to staging and tested. Once all blockers are resolved, the final tag is cut.

Changelog from Conventional Commits

Raw commits (git log --oneline)
a1b2c3dfeat:add OAuth login via Google and GitHub
e4f5g6hfix:resolve null pointer in user.getRole()
i7j8k9lfeat!:redesign REST API — v2 endpoints
m1n2o3pdocs:update README with new setup steps
q4r5s6tfix:handle empty cart edge case at checkout
u7v8w9xperf:memoize expensive product catalog query

Hotfix Process

maindevelophotfix/critical-fixv2.0.1
1.Branch off main (not develop)
2.Fix the bug in isolation
3.Merge to main + tag patch release
4.Cherry-pick or merge back to develop

Release Train Calendar (2-week sprints)

D1
D2
D3
D4
D5
D6
D7
D8
D9
D10
D11
D12
D13
D14
Sprint start
Dev freeze
RC.1 cut
QA cycle
🚀 RELEASE
Retro
Sprint start

11

Post-Deployment

Deployment is the beginning, not the end. Observability, incident response, and continuous learning close the loop.

Monitoring Stack

Incident Response Flow

Detect0–2 min

Alert fires in PagerDuty / Slack. On-call engineer acknowledges.

▶ACK alert
▶Check dashboard
▶Assess severity (P1–P4)

SLA / SLO Dashboard

99.97%
Uptime (30d)
42ms
P50 Latency
187ms
P95 Latency
843ms
P99 Latency
0.03%
Error Rate
0.96
Apdex Score

Apdex = (Satisfied + Tolerating ÷ 2) ÷ Total requests, scored 0–1 against a response-time threshold. 0.96 is "Excellent."

A/B Test Result — Checkout Button Colour

Control (Blue)
3.2%
n = 12,400
Variant (Green)
3.9%
n = 12,350
Significance: 99.7%Lift +21.9%

Ship Variant B — statistically significant at p < 0.05

Feature Flag Cleanup Checklist

✓Confirm rollout has held at 100% for a full bake period
✓Remove flag evaluation code from all call sites
✓Remove flag-specific tests and mocks
✓Delete the old code path (the 'off' branch) entirely
✓Delete the flag from LaunchDarkly / ConfigCat
✓Remove the corresponding database column / config key
✓Update changelog and notify stakeholders

12

Documentation & Knowledge

Code that isn't documented is an asset with an expiry date. The best docs live in the same repo as the code they describe.

Documentation Types

Diagram-as-Code (Mermaid)

gitGraph
   commit id: "init"
   branch feature/auth
   checkout feature/auth
   commit id: "add login"
   commit id: "add OAuth"
   checkout main
   branch hotfix/typo
   checkout hotfix/typo
   commit id: "fix typo"
   checkout main
   merge hotfix/typo
   merge feature/auth

Documentation Quality Checklist

◎All examples tested and runnable
◎Covers the most common error cases
◎Has a 'quick start' (< 5 min to first success)
◎Version-tagged and dated
◎Searchable (indexed in Confluence / Notion / Docs site)
◎Ownership clearly attributed
◎Updated as part of the same PR that changes the code
◎Reviewed by a developer who was NOT the author
◎Docs untouched for 6+ months are flagged for re-review against the current codebase

New Team Member — Week 1 Checklist

Day 1

▶Dev environment setup guide
▶Repo access + SSH keys
▶Architecture overview doc
▶Meet the team

Day 2–3

▶First small PR (good-first-issue)
▶Code review process walkthrough
▶CI/CD pipeline tour
▶Codebase walkthrough with lead

Days 4–5

▶Own a small feature end-to-end
▶Runbook for at least one service
▶On-call shadow shift
▶30-day goals set with manager

13

Governance & Compliance

Git is your audit log. Every merge, every deploy, every config change is evidence — for your team and for your auditors.

Audit Trail

TimestampActorActionDetail
2024-03-15 09:12alice@co.com
PR #821 approved
feat: add payment gateway
2024-03-15 09:31ci-bot
Pipeline passed
build #1203 — all 847 tests green
2024-03-15 09:45bob@co.com
Deployed to prod
v2.14.0 → eu-west-1 (canary 5%)
2024-03-15 10:02bob@co.com
Canary promoted
5% → 100% — error rate stable
2024-03-15 14:20carol@co.com
Config changed
PAYMENT_TIMEOUT: 5s → 10s
2024-03-15 16:55alice@co.com
Tag created
v2.14.1 — hotfix: null check
2024-03-22 03:14dave@co.com
Bypassed branch protection
P1 incident — force-merged past required review (logged admin override)

Compliance Evidence Collection

CC6.1 — Logical access controls
✓Required 2 reviewers on main branch
CC7.2 — System monitoring
✓All merges logged with actor + timestamp
CC8.1 — Change management
✓No direct push to main; PRs mandatory
A1.2 — Availability monitoring
✓Deployment gate: staging tests must pass

Access Control Matrix

Developer
Lead
DevOps
Admin
Merge to main
✗
✓
✗
✓
Deploy to prod
✗
✗
✓
✓
Create release tag
✗
✓
✓
✓
Manage secrets
✗
✗
✓
✓
Add CODEOWNER
✗
✓
✗
✓
Delete branch
✓
✓
✓
✓
Bypass branch protection
✗
✗
✗
✓
⚿Signed Commits with GPG
▾

License Compliance

Scanning tools: FOSSABlack DuckTLDR LegalSnyk License
MITLow

Attribution only. Use freely in proprietary software.

Apache 2.0Low

Attribution + patent grant. Notice file required.

GPL v3Low for SaaS

Derivative works must also be GPL. Copyleft triggers on distribution, not network/SaaS use — see AGPL.

LGPLMedium

Can link to LGPL library without GPL propagation.

AGPLVery High

Network use = distribution. Strongest copyleft.

BSD 2/3Low

Attribution. Non-endorsement clause (3-clause).

CI Security Scanning

Secret Scanning

GitHub secret scanning / gitleaks

Blocks pushes containing credentials.

Dependency Scanning (SCA)

Dependabot / Snyk

CVE alerts surfaced on PRs.

14

Team Communication & Notifications

The right signal to the right person at the right time. Too little: surprises. Too much: everyone ignores everything.

Integration Map

GitHubGitLabSlackTeamsPagerDutyOn-callJiraIssuesDashboardGrafana

Notification Preferences Matrix

slack
email
dashboard
PR Opened
Review Requested
PR Merged
Pipeline Failed
Pipeline Passed
Deployed to Staging
Deployed to Prod
Alert Fired

Deployment Announcement Template

*🚀 Deployed to Production*  v2.15.0

*What shipped:*
• Payment gateway v2 (Stripe + Apple Pay)
• Checkout redesign — 22% fewer steps
• Fix: null pointer in order history (hotfix #863)

*Stats:*
Pipeline: ✅ 847 tests passed | Build: 2m 34s
Canary: 5% for 30min, no error rate change
Rollout: 100% of traffic as of 09:45 UTC

*Risk:* Low — canary held at 5% for 30min with no error-rate increase. No DB/schema migration in this release.

*Rollback:* `kubectl set image deploy/api api=app:v2.14.1` — safe, nothing to reverse migration-wise. Faster option: flip the Apple Pay flag off (seconds, no redeploy) if only the new payment path is misbehaving.

*Links:* Runbook | Grafana | Sentry | Release Notes
*On-call:* @alice (primary) @bob (secondary)

On-Call Rotation

A
Alice
Mar 11–17
ACTIVE
B
Bob
Mar 18–24
C
Carol
Mar 25–31
D
Dave
Apr 1–7

Incident Status Update Template

Investigating→
Identified→
Monitoring→
Resolved

We've identified a spike in 5xx errors on checkout tied to the 09:45 UTC deploy. Rolling back now.

Next update ETA15 min, or on state change

Alert Fatigue Prevention

⏱Set minimum duration thresholds — alerts must fire for >5min before paging
◉Group related alerts into a single incident, not separate pages
◈Use severity tiers: P1 pages, P2 Slack, P3 dashboard-only
◎Review and tune alert thresholds monthly — aim for <2 false positives/week
✓Auto-resolve alerts when metrics recover; no manual ACK needed
Reference

Git Commands

153 entries: what each command does, the flags you will use, examples to copy, and four safety answers — does it rewrite history, can it discard uncommitted work, is it safe on a shared branch, and how to get back if it goes wrong.

Setup & config

git configRead and write settings, per repository (.git/config), per user (~/.gitconfig) or for the whole machine.Safe
git config set [--global|--local] <name> <value>
git config get [--global|--local] <name>
git config list [--global|--local|--show-origin]
git config unset [--global|--local] <name>
git config [--global] <name> [<value>]   # the older form, still supported
--global
Use ~/.gitconfig (or $XDG_CONFIG_HOME/git/config): your settings for every repository.
--local
Use .git/config of the current repository (the default when writing).
--system
Use the machine-wide file ($(prefix)/etc/gitconfig).
--show-origin
With list or get: show which file each value came from.
--all
With get or unset: act on every value of a multi-valued key.
--type=<type>
Check and normalise the value: bool, int, path, color, expiry-date.
-e, --edit
Open the chosen file in your editor (git config edit is the subcommand form).
  • git config set --global user.name "Ada Lovelace"

    The name recorded in every commit you make.

  • git config set --global user.email ada@example.com

    The email recorded with it; hosts match it to your account.

  • git config set --global init.defaultBranch main

    Name the first branch of new repositories main (Git 2.28+).

  • git config set --global core.editor "code --wait"

    The editor git opens for messages; --wait makes git wait for the tab to close.

  • git config set --global alias.lg "log --oneline --graph --all"

    git lg now runs that log.

  • git config list --show-origin

    Every setting in effect, with the file it comes from.

  • git config --global user.name "Ada Lovelace"

    The same as the first example, in the form older guides use.

  • git config --list --show-origin

    The older spelling of git config list.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Set the old value again, or unset the key; config changes are not versioned.

The full list of variables is in the git-config documentation; the Appendix has a recommended ~/.gitconfig with every line explained.

Since Git 2.46: The list/get/set/unset subcommands; the older option form keeps working. See also git init, git hook.

git initCreate an empty repository: a .git directory in the current (or named) directory.Safe
git init [-b <branch>] [--bare] [<directory>]
-b <name>, --initial-branch=<name>
Name of the first branch. Without it git uses init.defaultBranch, else master, and prints a hint that the default becomes main in Git 3.0.
--bare
Create a repository with no working tree, as servers and shared remotes use.
--object-format=<format>
sha1 (the default) or sha256. SHA-256 repositories cannot yet exchange objects with SHA-1 ones.
--template=<dir>
Copy hooks and files from this directory into the new .git.
-q, --quiet
Print only errors.
  • git init

    Make the current directory a repository.

  • git init -b main my-app

    Create my-app/ with a repository whose first branch is main.

  • git init --bare shared.git

    A bare repository others can push to.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Running it again in an existing repository is safe: it reinitialises without touching history.

See also git clone, git config.

git cloneCopy a repository: download its objects, create remote-tracking branches for its branches, and check out its default branch.Safe
git clone [--branch <name>] [--depth <n>] [--filter=<spec>] <repository> [<directory>]
-b <name>, --branch <name>
Check out this branch (or tag) instead of the remote's HEAD.
--depth <n>
Shallow clone: only the last n commits. Implies --single-branch.
--filter=blob:none
Partial clone: all commits and trees, file contents fetched when first needed.
--single-branch
Fetch only one branch's history.
--recurse-submodules
Also clone and check out the submodules.
--sparse
Start with a sparse checkout of only the top-level files.
--bare
Clone without a working tree.
--mirror
A bare clone that copies every ref and keeps them in sync on fetch.
-o <name>, --origin <name>
Name the remote something other than origin.
  • git clone https://github.com/git/git.git

    The full history, into ./git.

  • git clone --filter=blob:none https://github.com/org/big.git

    Full history, file contents on demand: much faster for big repositories.

  • git clone --depth 1 https://github.com/org/app.git

    Only the latest commit; for CI and one-off builds.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Delete the new directory; the source is untouched.

A shallow clone cannot run most history commands correctly (log, blame, bisect see only the fetched commits); git fetch --unshallow completes it.

See also git remote, git fetch, git sparse-checkout, git clone --filter.

git helpShow a command's manual page, or list commands and guides.Safe
git help <command>
git help -a
git help -g
git <command> -h
-a, --all
List every command git knows, grouped.
-g, --guides
List the concept guides (gitrevisions, gitignore, gitworkflows…).
-w, --web
Open the page in a browser.
-c, --config
List every configuration variable.
  • git help rebase

    The full manual for git rebase.

  • git rebase -h

    The short usage summary, printed in the terminal.

  • git help gitrevisions

    How to name commits: HEAD~2, main@{yesterday}, A..B, A...B.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git version.

git versionPrint the installed git version. Several options on this page need a minimum version; check yours first.Safe
git version [--build-options]
git --version
--build-options
Also print how this git was built (hash algorithms, curl, compiler).
  • git --version

    For example: git version 2.55.0.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

git hookRun a hook script the way git would, for testing it or calling it from tooling.Safe
git hook run [--ignore-missing] <hook-name> [-- <hook-args>]
--ignore-missing
Exit 0 when the hook does not exist, instead of failing.
  • git hook run pre-commit

    Run .git/hooks/pre-commit (or core.hooksPath's) as git commit would.

  • git config set core.hooksPath .githooks

    Use hooks committed in the repository's .githooks/ directory.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Depends entirely on what the hook script does.

Hooks in .git/hooks are not cloned. Teams share them by committing a directory and setting core.hooksPath, or with a tool such as pre-commit or husky.

See also git config, git commit.

git credentialThe interface to credential helpers, which store HTTPS usernames and tokens so git does not ask every time.Safe
git credential (fill|approve|reject)
git config set --global credential.helper <helper>
credential.helper=cache
Keep credentials in memory for 15 minutes (git-credential-cache).
credential.helper=store
Keep them in plain text in ~/.git-credentials. Avoid on shared machines.
credential.helper=manager
Git Credential Manager: the OS keychain, with OAuth sign-in to the big hosts (installed separately).
  • ssh-keygen -t ed25519 -C "ada@example.com"

    Not git itself: make an SSH key, then add the .pub file to your host and use git@host:org/repo.git URLs.

  • git config set --global credential.helper cache

    Remember HTTPS credentials in memory for a while.

  • git config set --global credential.helper osxkeychain

    macOS: the Keychain (ships with Apple's git).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Not a history operation.
If it goes wrong
Remove the helper setting; delete stored credentials with the helper or your OS keychain.

Use SSH keys or a credential manager rather than a token in the remote URL: a URL with a token in it ends up in .git/config and in shell history.

See also git config.

Staging & committing

git statusShow the branch you are on, how it compares with its upstream, and which files are staged, changed or untracked.Safe
git status [-s] [-b] [--] [<pathspec>…]
-s, --short
Two-column output: the index against HEAD, then the working tree against the index.
-b, --branch
In short format, add the branch and ahead/behind line.
--porcelain[=v2]
A format for scripts that does not change between git versions.
-u[<mode>], --untracked-files[=<mode>]
no, normal (the default) or all (list files inside untracked directories).
--ignored
Also list ignored files.
--show-stash
Say how many stash entries there are.
-v, --verbose
Also show the staged diff (-vv adds the unstaged one).
  • git status

    The long form, with a hint for the next command under each section.

  • git status -sb

    One line per file plus the branch line; the form most people alias.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git diff, git add, git restore.

git addCopy the current content of files into the index (the staging area), so the next commit records it.Safe
git add [-A | -u] [-p] [-n] [--] <pathspec>…
-A, --all
Stage every change in the working tree: new, modified and deleted files.
-u, --update
Stage modifications and deletions of tracked files only; new files stay untracked.
-p, --patch
Choose hunks one at a time (y/n/s to split/e to edit).
-N, --intent-to-add
Record that a new file will be added, so git diff shows it; its content is added later.
-n, --dry-run
Show what would be staged.
-f, --force
Add a file even though .gitignore ignores it.
--renormalize
Re-apply line-ending and filter rules to tracked files (after changing .gitattributes).
  • git add app.js

    Stage one file.

  • git add -p

    Stage part of your changes and leave the rest for another commit.

  • git add -A

    Stage everything, deletions included.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git restore --staged <file> unstages it again; the working tree is not changed.

See also git restore, git status, git commit, git add -p.

git add -pStage selected hunks of a file instead of the whole file, so one commit holds one change.Safe
git add -p [<pathspec>…]
-p, --patch
Walk the hunks: y stage, n skip, s split into smaller hunks, e edit the hunk, q quit, ? help.
-U<n>, --unified=<n>
Show n lines of context around each hunk.
  • git add -p src/app.js

    Stage the fix but not the debugging line next to it.

  • git diff --staged

    Check what you staged before committing.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git restore --staged -p unstages hunks the same way.

The page's engine does not run -p (an interactive, per-hunk prompt); run it in your own terminal.

See also git add, git restore, git commit.

git rmDelete files from the working tree and the index; the next commit records the deletion.Safe
git rm [--cached] [-r] [-f] [-n] [--] <pathspec>…
--cached
Remove from the index only: the file stays on disk, now untracked. The way to stop tracking a file.
-r
Recurse into directories.
-f, --force
Remove even if the file has changes that are not committed.
-n, --dry-run
List what would be removed.
  • git rm old.js

    Delete a file and stage the deletion.

  • git rm --cached .env

    Stop tracking .env but keep it on disk (then add it to .gitignore).

Rewrites history?
No.
Discards uncommitted work?
Only with -f: without it git refuses to remove a file whose changes are not committed.
Safe on a shared branch?
Yes.
If it goes wrong
Committed content is in history: git restore --source=HEAD -- <file>, or git checkout HEAD -- <file>.

Removing a file does not remove it from history. A committed secret must be rotated, then purged with git filter-repo (Stage 13).

See also git mv, git restore, git clean.

git mvRename or move a file and stage the rename in one step.Safe
git mv [-f] [-n] [-k] <source> <destination>
-f, --force
Overwrite an existing destination.
-n, --dry-run
Show what would happen.
-k
Skip moves that would fail instead of stopping.
  • git mv app.js server.js

    Git stores no renames: log --follow and diff detect them from content similarity.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git rm, git log.

git commitRecord the index as a new commit on the current branch, with a message, an author and the time.Safe
git commit [-m <msg>] [-a] [--amend] [--fixup=<commit>] [-s] [-S]
-m <msg>, --message=<msg>
Use this message; several -m become separate paragraphs.
-a, --all
Stage modified and deleted tracked files first (new files are not added).
--amend
Replace the last commit with a new one (new id) containing the index and, unless --no-edit, a new message.
--no-edit
Keep the proposed message (with --amend: the old one) without opening an editor.
--fixup=<commit>
Make a "fixup!" commit for rebase --autosquash to fold into <commit>. amend:<commit> and reword:<commit> also change its message (Git 2.32+).
-s, --signoff
Add a Signed-off-by trailer (the Developer Certificate of Origin).
-S[<keyid>], --gpg-sign[=<keyid>]
Sign the commit (GPG, SSH or X.509, per gpg.format).
--trailer <token>=<value>
Add a trailer such as Co-authored-by (Git 2.32+).
--allow-empty
Record a commit that changes nothing (to trigger CI, for example).
-v, --verbose
Show the diff in the editor while you write the message.
--author=<author>
Record someone else as the author.
--no-verify
Skip the pre-commit and commit-msg hooks.
  • git commit -m "Add login form"

    Commit what is staged.

  • git commit -am "Fix typo in README"

    Stage tracked changes and commit them.

  • git commit --fixup=HEAD~2

    A fix for an earlier commit; git rebase -i --autosquash puts it in place.

  • git commit --trailer "Co-authored-by: Grace Hopper <grace@example.com>"

    Credit a co-author; GitHub and GitLab read this trailer.

Rewrites history?
Only with --amend (see its entry).
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
A new commit can be undone with git reset --soft HEAD~1 (keeps the changes staged).

See also git commit --amend, git add, git status, git interpret-trailers.

git commit --amendReplace the tip commit with a new one: add forgotten changes or fix its message. The old commit is left out of the branch.Rewrites local history
git commit --amend [--no-edit] [-m <msg>] [--reset-author]
--no-edit
Keep the old message; just add what is staged.
-m <msg>
Replace the message.
--reset-author
Record you, now, as the author (instead of keeping the original author and date).
--only
With paths: amend only those paths, ignoring the rest of the index.
  • git commit --amend --no-edit

    Add a staged fix to the last commit.

  • git commit --amend -m "Add login form (with validation)"

    Reword the last commit.

Rewrites history?
Yes: the tip commit gets a new id.
Discards uncommitted work?
No.
Safe on a shared branch?
Not if the commit is pushed: others have the old id, and pushing needs --force-with-lease.
If it goes wrong
git reset --soft HEAD@{1} goes back to the commit before the amend (the reflog names it).

See also git commit, git reflog, git push --force-with-lease.

git restoreReplace files in the working tree and/or the index with another version: undo edits, or unstage.Discards work
git restore [--source=<tree>] [--staged] [--worktree] [--] <pathspec>…
-S, --staged
Restore the index (unstage). The working tree is left alone.
-W, --worktree
Restore the working tree (the default when --staged is not given).
-s <tree>, --source=<tree>
Take the content from this commit instead of the index (or HEAD with --staged).
-p, --patch
Choose hunks to restore.
--ours, --theirs
During a conflict, take one side's version of the file.
-m, --merge
Recreate the conflicted file with markers.
  • git restore --staged app.js

    Unstage app.js; its edits stay in the working tree.

  • git restore config.json

    Throw away unstaged edits to config.json.

  • git restore --source=HEAD~3 -- src/payments.js

    Bring back the file as it was three commits ago.

Rewrites history?
No.
Discards uncommitted work?
Yes, for the working tree: edits that were never staged or committed are gone. --staged alone discards nothing.
Safe on a shared branch?
Yes (it does not touch commits).
If it goes wrong
None for discarded working-tree edits; they were never saved in an object.

Since Git 2.23: The file-restoring half of git checkout. See also git checkout, git reset, git stash.

git diffShow changes: the working tree against the index, the index against HEAD, or any two commits.Safe
git diff                # working tree vs index (unstaged changes)
git diff --staged       # index vs HEAD (what you are about to commit)
git diff <a> <b>        # two commits
git diff <a>...<b>      # b against the merge base of a and b
--staged, --cached
Compare the index with HEAD.
--stat
A per-file summary with +/- bars.
--name-only, --name-status
List only the file names (with A/M/D/R status).
-w, --ignore-all-space
Ignore whitespace changes.
--word-diff
Show changed words inline instead of whole lines.
--color-moved
Colour blocks of lines that moved rather than changed.
--check
Warn about whitespace errors and conflict markers left in the diff.
--merge-base
With commits: diff against their merge base (Git 2.30+).
-U<n>, --unified=<n>
Lines of context around each hunk (default 3).
--no-index
Compare two paths outside a repository.
  • git diff

    What you changed but have not staged.

  • git diff --staged

    What the next commit will contain.

  • git diff main...feature

    What feature changed since it branched from main — what a pull request shows.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git status, git show, git range-diff, git difftool.

git notesAttach notes to commits without changing them (their ids stay the same); notes live in refs/notes/commits.Safe
git notes add [-m <msg>] [<object>]
git notes show [<object>]
git notes remove [<object>]
-m <msg>
The note's text.
-f, --force
Overwrite an existing note.
--ref <ref>
Use a notes ref other than refs/notes/commits.
  • git notes add -m "Deployed to production 2026-03-02" v2.3.0

    Record a fact about a commit after the fact.

  • git push origin refs/notes/*

    Notes are not pushed by default; push the notes refs explicitly.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git log, git show.

git interpret-trailersAdd or parse trailers — the Key: value lines at the end of a commit message (Signed-off-by, Co-authored-by, Fixes).Safe
git interpret-trailers [--trailer <token>=<value>] [--parse] [<file>…]
--trailer <token>=<value>
Add a trailer.
--parse
Print only the trailers found.
--in-place
Edit the files instead of printing.
  • git log -1 --format=%B | git interpret-trailers --parse

    List the trailers of the last commit.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git commit.

git stageAnother name for git add.Safe

Replaced by git add.

git stage <arg>…
  • git stage app.js

    The same as git add app.js.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

Branches & merging

git branchList, create, rename, copy and delete branches. A branch is a movable name for one commit.Rewrites local history
git branch [-a | -r] [-v | -vv] [--merged | --no-merged] [--sort=<key>]
git branch <name> [<start-point>]
git branch (-m | -M) [<old>] <new>
git branch (-d | -D) <name>…
git branch -u <upstream> [<name>]
-a, --all
List local and remote-tracking branches.
-r, --remotes
List remote-tracking branches.
-v, -vv, --verbose
Show each tip's id and subject; -vv adds the upstream and ahead/behind.
--merged [<commit>]
Only branches whose tip is reachable from <commit> (default HEAD): safe to delete.
--no-merged [<commit>]
Only branches with commits <commit> does not have.
--contains <commit>
Only branches that contain <commit>.
-d, --delete
Delete a branch; refuses if it is not merged into its upstream or HEAD.
-D
Delete even if it is not merged (the same as --delete --force).
-m, --move
Rename; -M renames over an existing name.
-c, --copy
Copy a branch with its config and reflog.
-f, --force
Reset an existing branch to <start-point>.
-u <upstream>, --set-upstream-to=<upstream>
Set the branch's upstream.
--show-current
Print the current branch name (Git 2.22+).
--sort=<key>
Sort, for example --sort=-committerdate for most recent first.
  • git branch -vv

    Every branch, its upstream and how far ahead or behind it is.

  • git branch --merged main

    Branches already merged into main.

  • git branch -d feature/login

    Delete a merged branch.

  • git branch -m master main

    Rename a branch.

  • git branch --sort=-committerdate

    Most recently changed branches first.

Rewrites history?
-f moves an existing branch; -D deletes one with unmerged commits.
Discards uncommitted work?
No uncommitted work; -D can leave commits that no branch names.
Safe on a shared branch?
Yes: it only changes your local branch names.
If it goes wrong
A deleted branch's commits: find the tip in git reflog (HEAD's) and run git branch <name> <id>.

See also git switch, git merge, git push --delete.

git switchSwitch to another branch, or create a branch and switch to it; the working tree and index change to match.Safe
git switch <branch>
git switch -c <new-branch> [<start-point>]
git switch --detach <commit>
git switch -
-c <name>, --create <name>
Create the branch at <start-point> (default HEAD) and switch to it.
-C <name>, --force-create <name>
The same, resetting the branch if it exists.
-d, --detach
Check out a commit without a branch (detached HEAD).
--orphan <name>
Start a branch with no history and an empty index.
-t, --track
Set the new branch's upstream to the start point.
--no-guess
Do not create a local branch from a same-named remote-tracking branch.
--discard-changes, -f, --force
Switch even with local changes; they are thrown away.
-m, --merge
Carry local changes across by merging them into the target.
  • git switch main

    Go to main.

  • git switch -c feature/login

    Create feature/login here and switch to it.

  • git switch -

    Back to the previous branch.

  • git switch feature/x

    If only origin/feature/x exists, this creates a local branch that tracks it.

Rewrites history?
No.
Discards uncommitted work?
Only with -f / --discard-changes. Otherwise git refuses when a local change would be overwritten, and carries other changes with you.
Safe on a shared branch?
Yes.
If it goes wrong
git switch - goes back.

Since Git 2.23: The branch-switching half of git checkout. See also git checkout, git branch, git restore.

git checkoutThe older command that both switches branches and restores files. Still supported; git switch and git restore split its two jobs.Discards work

Replaced by git switch, git restore.

git checkout <branch>              # now: git switch <branch>
git checkout -b <new> [<start>]    # now: git switch -c <new> [<start>]
git checkout <commit>              # now: git switch --detach <commit>
git checkout [<commit>] -- <path>  # now: git restore [--source=<commit>] -- <path>
-b <new>
Create a branch and switch to it.
-B <new>
Create or reset a branch and switch to it.
--detach
Detach HEAD at the commit.
-f, --force
Throw away local changes when switching; with paths, ignore unmerged entries.
--ours, --theirs
With paths during a conflict: take one side.
-p, --patch
Pick hunks to restore.
  • git checkout feature/login

    Switch branches (git switch feature/login).

  • git checkout HEAD~3 -- src/payments.js

    Restore a file from three commits ago (git restore --source=HEAD~3 -- src/payments.js).

Rewrites history?
No.
Discards uncommitted work?
Yes with paths: git checkout -- <path> overwrites edits in those paths without asking.
Safe on a shared branch?
Yes.
If it goes wrong
None for overwritten edits; switching back with git checkout - is fine.

One word doing two jobs is why git checkout is easy to misuse: git checkout name switches to a branch if one is called name, and otherwise overwrites a file called name.

See also git switch, git restore.

git mergeJoin another branch's history into the current branch: fast-forward when possible, otherwise a merge commit with two parents.Safe
git merge [--no-ff | --ff-only] [--squash] [-m <msg>] [-X <option>] <commit>…
git merge (--continue | --abort | --quit)
--no-ff
Always create a merge commit, even when a fast-forward is possible: the branch stays visible in history.
--ff-only
Fast-forward or refuse; never create a merge commit.
--squash
Stage the combined changes without committing or recording a merge (see its entry).
--no-commit
Merge but stop before committing, to inspect or adjust.
-m <msg>
The merge commit's message.
-X ours, -X theirs
On conflicting hunks, take our side or theirs (other changes still merge).
-s <strategy>
Merge strategy; ort is the default since Git 2.34 (it replaced recursive).
--abort
Give up and return to the state before the merge.
--continue
Conclude after resolving conflicts (the same as git commit).
--autostash
Stash local changes first and reapply them after (Git 2.27+).
--allow-unrelated-histories
Merge two histories with no common ancestor.
  • git merge feature/login

    Fast-forward if main has not moved, else a merge commit.

  • git merge --no-ff feature/login

    Always a merge commit: the branch's commits stay grouped.

  • git merge --ff-only origin/main

    Update to the remote's main only if nothing diverged.

  • git merge --abort

    Back out of a conflicted merge.

Rewrites history?
No: it adds a commit (or moves the branch forward).
Discards uncommitted work?
No; git refuses to start if local changes would be overwritten. --abort discards conflict resolutions made so far.
Safe on a shared branch?
Yes: the standard way to combine shared branches.
If it goes wrong
Before pushing: git reset --hard ORIG_HEAD undoes the merge. After pushing: git revert -m 1 <merge>.

See also git merge --squash, git rebase, git rerere, git mergetool.

git merge --squashPut all of a branch's changes into the index as one change, for a single new commit; no merge is recorded.Safe
git merge --squash <branch> && git commit
--squash
Stage the result without MERGE_HEAD; the next git commit has one parent.
  • git merge --squash feature/search

    Stage feature/search's changes as one change.

  • git commit -m "Add search"

    Record them as a single commit on main.

Rewrites history?
No, but the branch's commits are not in main's history: git branch -d refuses to delete it later (use -D).
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Before committing: git reset --hard (drops the staged changes).

Hosts' "Squash and merge" button does the same: one commit on the target branch, with the pull request's commits left out of its history.

See also git merge, git rebase.

git rebaseReplay the current branch's commits on top of another commit, giving each a new id; the result is a straight line.Rewrites local history
git rebase [-i] [--onto <newbase>] [<upstream> [<branch>]]
git rebase (--continue | --skip | --abort | --quit)
-i, --interactive
Edit the list of commits first: reorder, reword, squash, fixup, drop (see its entry).
--onto <newbase>
Replay onto <newbase> the commits after <upstream>.
--continue
Resume after resolving a conflict (git add the files first).
--skip
Drop the commit that stopped and carry on.
--abort
Stop and put the branch back where it was.
--autosquash
Move fixup!/squash! commits next to their targets.
--autostash
Stash local changes before and reapply after.
--update-refs
Also move branches that point into the rebased range (stacked branches, Git 2.38+).
--rebase-merges
Keep merge commits instead of flattening them.
--keep-base
Rebase onto the same fork point (to clean up without taking new upstream commits, Git 2.24+).
-x <cmd>, --exec <cmd>
Run a command after each commit (for example the tests).
-X ours, -X theirs
Resolve conflicting hunks automatically (note: during a rebase, ours is the branch you rebase onto).
  • git rebase main

    Move the current branch's commits onto the tip of main.

  • git rebase --onto main feature/base feature/child

    Move only feature/child's own commits from feature/base onto main.

  • git rebase --continue

    After fixing a conflict and staging the file.

  • git rebase --abort

    Give up; everything is as before.

Rewrites history?
Yes: every replayed commit gets a new id.
Discards uncommitted work?
No; it refuses to start with local changes (unless --autostash).
Safe on a shared branch?
No, once others have the commits: they keep the old ids, and pushing needs --force-with-lease.
If it goes wrong
git reset --hard ORIG_HEAD right after, or the branch's reflog: git reset --hard <branch>@{1}.

See also git rebase -i, git merge, git pull, git push --force-with-lease, git range-diff.

git rebase -iRewrite a series of commits by editing its todo list: reorder, reword, squash, fixup, edit or drop them.Rewrites local history
git rebase -i <base>
git rebase -i --autosquash <base>
pick
Use the commit.
reword
Use it, but edit the message.
edit
Use it, then stop so you can amend it.
squash
Fold it into the previous commit and combine the messages.
fixup
Fold it in and keep only the previous message (fixup -C keeps this one's).
drop
Leave it out.
exec <cmd>
Run a shell command at that point.
  • git rebase -i HEAD~4

    Edit the last four commits.

  • git rebase -i --autosquash main

    Fold fixup! commits into their targets before a pull request.

Rewrites history?
Yes: commits from the first changed line onward get new ids.
Discards uncommitted work?
Dropped commits leave the branch (the reflog still has them).
Safe on a shared branch?
No, once the commits are pushed and others use them.
If it goes wrong
git reset --hard ORIG_HEAD, or <branch>@{1} from the reflog.

The todo list is oldest first, the reverse of git log. The page's terminal opens it as an editor with an action menu per commit.

See also git rebase, git commit.

git cherry-pickApply the changes of existing commits as new commits on the current branch.Safe
git cherry-pick [-x] [-n] [-m <parent>] <commit>…
git cherry-pick (--continue | --skip | --abort | --quit)
-x
Add "(cherry picked from commit <id>)" to the message: the way to track backports.
-n, --no-commit
Apply to the index and working tree without committing.
-e, --edit
Edit the message first.
-m <parent>, --mainline <parent>
Pick a merge commit, relative to parent number <parent>.
-s, --signoff
Add Signed-off-by.
--ff
Fast-forward when the picked commit's parent is HEAD.
--continue, --skip, --abort, --quit
Control a stopped sequence of picks.
  • git cherry-pick -x a1b2c3d

    Backport one fix to a release branch, with a trailer naming the original.

  • git cherry-pick main~3..main

    Pick the last three commits of main, oldest first.

Rewrites history?
No: it adds commits.
Discards uncommitted work?
No; it refuses to start with conflicting local changes.
Safe on a shared branch?
Yes.
If it goes wrong
git reset --hard HEAD~1 removes a picked commit before pushing; after pushing, git revert it.

See also git revert, git rebase, git tag.

git worktreeCheck out more than one branch at once, each in its own directory, sharing one repository.Safe
git worktree add [-b <new-branch>] <path> [<commit-ish>]
git worktree list
git worktree remove [-f] <path>
git worktree prune
add <path> [<branch>]
Create a working tree at <path> with <branch> checked out.
-b <new-branch>
With add: create the branch too.
--detach
With add: a detached HEAD instead of a branch.
list
Show every working tree and what it has checked out.
remove [-f]
Delete a working tree; -f even with uncommitted changes.
prune
Forget working trees whose directories were deleted.
lock, unlock
Keep a working tree (on a removable drive, say) from being pruned.
  • git worktree add ../hotfix -b hotfix/login main

    Fix production in ../hotfix without stashing your feature work.

  • git worktree list

    Where every working tree is.

  • git worktree remove ../hotfix

    Delete it when done.

Rewrites history?
No.
Discards uncommitted work?
remove -f deletes that working tree's uncommitted changes.
Safe on a shared branch?
Yes.
If it goes wrong
Committed work is in the shared repository; uncommitted work in a force-removed tree is gone.

A branch can be checked out in only one worktree at a time. One worktree per task (or per AI agent) keeps parallel work from colliding: Stage 13.

Since Git 2.5: Multiple working trees for one repository. See also git switch, git stash.

git rerere"Reuse recorded resolution": remember how you resolved a conflict and apply the same resolution when it recurs.Safe
git config set --global rerere.enabled true
git rerere [status | diff | forget <path>]
status
Paths whose resolution will be recorded.
diff
The resolution recorded so far.
forget <path>
Drop a bad recorded resolution.
  • git config set --global rerere.enabled true

    Turn it on; merges and rebases then record and reuse resolutions.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git rerere forget <path> discards a wrong resolution; check the result before committing.

See also git merge, git rebase.

git mergetoolOpen each conflicted file in a merge tool (vimdiff, meld, VS Code, …) to resolve it.Safe
git mergetool [--tool=<tool>] [<file>…]
-t <tool>, --tool=<tool>
Which tool; merge.tool sets the default.
--tool-help
List the tools git knows.
-y, --no-prompt
Do not ask before each file.
  • git config set --global merge.tool vscode

    Use VS Code; git knows the vscode tool (the code command must be on your PATH).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Resolutions are uncommitted until you commit; git merge --abort starts over.

See also git merge, git difftool.

git range-diffCompare two versions of a series of commits — typically a branch before and after a rebase or force-push.Safe
git range-diff <old-base>..<old-tip> <new-base>..<new-tip>
git range-diff <old-tip>...<new-tip>
git range-diff <base> <old-tip> <new-tip>
--creation-factor=<n>
How different two commits may be and still be matched (default 60).
--no-dual-color
Plain diff colours.
--left-only, --right-only
Show only the commits on one side.
  • git range-diff main feature@{1} feature

    What a force-push changed in feature, commit by commit.

  • git range-diff @{u}...HEAD

    Your rebased branch against what you last pushed.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Reviewers use it to re-review a force-pushed branch: each line pairs an old commit with its new version and shows what changed between them.

Since Git 2.19: A reimplementation of tbdiff. See also git rebase, git diff.

git show-branchShow branches and their commits side by side, with markers for which branch contains which commit.Safe
git show-branch [--all] [<branch>…]
-a, --all
Include remote-tracking branches.
--topics
Only the commits not on the first branch.
  • git show-branch main feature/login

    Where the two branches differ.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log, git branch.

git branch -DDelete a local branch even if its commits are merged nowhere else. -d refuses in that case; -D does not.Rewrites local history
git branch -d <branch>
git branch -D <branch>
-d, --delete
Delete if merged into its upstream (or HEAD).
-D
Delete regardless (--delete --force).
-r
With -d: delete a remote-tracking branch locally.
  • git branch -d feature/login

    The safe form: refuses if the work is not merged.

  • git branch -D spike/old-idea

    Throw away an experiment on purpose.

Rewrites history?
No, but commits only that branch had are no longer named by anything.
Discards uncommitted work?
No uncommitted work.
Safe on a shared branch?
Yes: it only deletes your local name (git push --delete removes the remote one).
If it goes wrong
git prints the tip id on deletion (Deleted branch x (was a1b2c3d)); git branch x a1b2c3d restores it. HEAD's reflog has it too if you had it checked out.

See also git branch, git push --delete, git reflog.

git switch --detachCheck out a commit with no branch (detached HEAD) to look around or build an old version; new commits here belong to no branch.Safe
git switch --detach <commit>
git switch -c <new-branch>   # keep what you made
-d, --detach
Detach HEAD at the commit.
  • git switch --detach v1.0.0

    Build the 1.0.0 release.

  • git switch -c fix-from-old-version

    Commits made while detached get a branch.

  • git switch -

    Back to the branch you came from.

Rewrites history?
No.
Discards uncommitted work?
No, but commits made while detached are left behind when you switch away (git warns and prints their ids).
Safe on a shared branch?
Yes.
If it goes wrong
HEAD@{1} (the reflog) names the commit you left; git branch <name> HEAD@{1}.

See also git switch, git reflog.

git rebase --continueCarry on after resolving a conflict in a rebase: stage the fixed files, then continue with the next commit.Rewrites local history
git add <resolved files> && git rebase --continue
git rebase --skip
git rebase --abort
--continue
Commit the resolution and apply the next commit.
--skip
Drop the commit that stopped.
--abort
Undo the whole rebase.
--edit-todo
Change the remaining todo list.
  • git status

    Which files are in conflict and which commit stopped.

  • git rebase --continue

    After git add.

Rewrites history?
Yes (it is part of a rebase).
Discards uncommitted work?
--skip discards the stopped commit's changes from the result; --abort discards resolutions.
Safe on a shared branch?
As for git rebase.
If it goes wrong
git rebase --abort, or ORIG_HEAD afterwards.

See also git rebase, git merge --abort.

History & inspection

git logList commits reachable from HEAD (or the revisions you name), newest first, with filters and formats.Safe
git log [<options>] [<revision-range>] [[--] <path>…]
--oneline
One line per commit: short id and subject.
--graph
Draw the branch structure in text beside the commits.
--all
Start from every ref, not just HEAD.
-n <n>, --max-count=<n>
Show at most n commits.
-p, --patch
Show each commit's diff.
--stat
Show each commit's per-file summary.
--author=<pattern>
Only commits whose author matches.
--since=<date>, --until=<date>
Only commits in a date range ("2 weeks ago", "2026-01-01").
-S <string>
The pickaxe: commits that change how many times <string> occurs — when a line appeared or vanished.
-G <regex>
Commits whose diff adds or removes a line matching <regex>.
--follow
With one file: continue past renames.
--first-parent
Follow only the first parent of merges: main's own history.
--merges, --no-merges
Only merge commits, or none.
--format=<format>
Custom output: %h %s %an %ar and more (see git help log, PRETTY FORMATS).
-L <start>,<end>:<file>
The history of a range of lines.
  • git log --oneline --graph --all

    The whole repository's shape.

  • git log main..feature

    Commits on feature that main does not have (A..B).

  • git log main...feature

    Commits on either side but not both (A...B), with --left-right to see which.

  • git log -S "apiKey" --oneline

    Which commit added or removed the string apiKey.

  • git log --follow -- src/auth/login.ts

    A file's history across renames.

  • git log --merges --first-parent --oneline main

    The merges into main: one line per merged pull request.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git show, git reflog, git shortlog, git blame.

git showShow one object: a commit's message and diff, an annotated tag, a tree's listing or a file at a revision.Safe
git show [<options>] [<object>…]
git show <commit>:<path>
--stat
The summary instead of the full diff.
--name-only, --name-status
Just the files changed.
-s, --no-patch
The commit header and message only.
--format=<format>
Custom header, as in git log.
  • git show

    The last commit and its diff.

  • git show HEAD~2:src/app.ts

    The file as it was two commits ago.

  • git show v1.0.0

    An annotated tag: the tagger and message, then the commit.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log, git cat-file, git diff.

git blameShow, for each line of a file, the commit and author that last changed it.Safe
git blame [-w] [-M] [-C] [-L <range>] [<rev>] [--] <file>
-L <start>,<end>
Only these lines.
-w
Ignore whitespace changes.
-M
Detect lines moved within the file.
-C
Detect lines moved or copied from other files changed in the same commit; twice, also from the commit that created the file; three times, from any commit.
--ignore-rev <rev>
Skip a commit (a formatting sweep); blame the commit before it.
--ignore-revs-file <file>
Skip every commit listed (conventionally .git-blame-ignore-revs).
  • git blame -w -L 40,60 src/payments.ts

    Who last changed lines 40–60, ignoring whitespace.

  • git config set blame.ignoreRevsFile .git-blame-ignore-revs

    Always skip the commits in that file (GitHub reads it too).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log, git last-modified.

git reflogShow where HEAD (or a branch) has pointed, newest first. It is how you find commits a reset, rebase or amend left behind.Safe
git reflog [show] [<ref>]
git reflog expire [--expire=<time>] [--all]
git reflog delete <ref>@{<n>}
show [<ref>]
The reflog of <ref> (default HEAD); the default subcommand.
-n <n>
Only the last n entries.
expire
Delete old entries (gc does this: 90 days by default, 30 for entries not reachable from the tip).
  • git reflog

    Every recent move of HEAD: HEAD@{0} is now, HEAD@{1} the one before.

  • git reflog show main

    Every value main has had.

  • git reflog --date=iso

    When each move happened, instead of @{n} (reflog show takes git log's options).

  • git reset --hard HEAD@{1}

    Go back to where HEAD was before the last command.

  • git branch rescued HEAD@{3}

    Give a lost commit a branch name again.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
The reflog is the recovery tool. expire and delete remove entries (and with gc, the commits).

Reflogs are local: they are not pushed or cloned. Entries expire after 90 days (gc.reflogExpire), or 30 days for commits no longer on the branch (gc.reflogExpireUnreachable).

See also git reset, git fsck, git log.

git shortlogSummarise history by author: the subjects of each author's commits, or just their counts.Safe
git shortlog [-s] [-n] [-e] [<revision-range>]
-s, --summary
Counts only.
-n, --numbered
Sort by count.
-e, --email
Include email addresses.
--group=<type>
Group by author, committer or a trailer such as co-authored-by.
  • git shortlog -sn --all

    Commit counts per author.

  • git shortlog v1.0.0..v1.1.0

    Who did what between two releases (release notes material).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log, git describe.

git describeName a commit after the nearest tag: v1.2.0-5-g1a2b3c4 is five commits after v1.2.0, at 1a2b3c4.Safe
git describe [--tags] [--always] [--dirty] [<commit>]
--tags
Use lightweight tags too (annotated only by default).
--always
Fall back to the short id when no tag is reachable.
--dirty
Append -dirty when the working tree has changes.
--abbrev=<n>
Length of the id part.
--contains
The tag that comes after the commit instead of before.
  • git describe --tags --always --dirty

    A version string for a build, straight from git.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git tag, git name-rev.

git grepSearch tracked files (or any revision) for a pattern — faster than grep, and it skips untracked and ignored files.Safe
git grep [-n] [-i] [-l] [-w] [-e] <pattern> [<tree>…] [-- <pathspec>…]
-n, --line-number
Show line numbers.
-i, --ignore-case
Case-insensitive.
-l, --files-with-matches
Only file names.
-w, --word-regexp
Match whole words.
-E, --extended-regexp
Extended regular expressions.
--untracked
Also search untracked files.
  • git grep -n TODO

    Every TODO in tracked files.

  • git grep -n apiKey v1.2.0

    Search the tree as it was at v1.2.0.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log.

git bisectBinary-search the history for the commit that introduced a bug: mark a good and a bad commit, then test the commits git checks out.Safe
git bisect start [<bad> [<good>…]]
git bisect good | bad | skip [<rev>]
git bisect run <cmd> [<args>…]
git bisect reset
git bisect log
start [<bad> [<good>…]]
Begin, optionally naming the bad and good commits at once.
good, bad
Mark the checked-out commit (or <rev>); git checks out the next one to test.
skip
This commit cannot be tested; git picks a nearby one.
run <cmd>
Automate: exit 0 = good, 125 = skip, 1–127 (other) = bad.
reset
End the bisection and return to where you started.
--first-parent
With start: follow only first parents (test merges of main, not the commits inside them).
--term-old=<term>, --term-new=<term>
Use other words than good and bad (say, fast and slow).
  • git bisect start HEAD v1.0

    HEAD is bad, v1.0 was good.

  • git bisect run npm test

    Let the tests decide each step.

  • git bisect reset

    Done: back to your branch.

Rewrites history?
No.
Discards uncommitted work?
No; it refuses to check out a commit over conflicting local changes.
Safe on a shared branch?
Yes.
If it goes wrong
git bisect reset returns to the starting branch; git bisect log saves the marks for replay.

A history of n commits takes about log₂ n tests: 1,000 commits in about 10. Stage 8's bisect tool runs it on a 64-commit history.

See also git log, git reflog.

git last-modifiedExperimental: for each file or directory, the commit that last changed it (what code hosts show next to each file).Safe
git last-modified [-r] [<revision-range>] [[--] <pathspec>…]
-r, --recursive
Descend into subdirectories.
-t, --show-trees
Also show the directories when recursing.
  • git last-modified -r src/

    The last commit to touch each file under src/.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Since Git 2.52: Marked experimental; its output may change. See also git blame, git log.

git difftoolShow a diff in an external diff tool instead of the terminal.Safe
git difftool [--tool=<tool>] [-d] [<git diff options>]
-t <tool>, --tool=<tool>
Which tool; diff.tool sets the default.
-d, --dir-diff
Compare whole directories in one go.
-y, --no-prompt
Do not ask before each file.
  • git difftool -d main...feature

    Review a branch in a directory-compare tool.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git diff, git mergetool.

git cherryList commits on your branch whose changes are not yet upstream, matching by content (patch id), not by commit id.Safe
git cherry [-v] [<upstream> [<head>]]
-v
Show the subjects.
  • git cherry -v main

    + lines are still to be merged; - lines have an equivalent (a cherry-pick) on main.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git cherry-pick, git log.

git name-revName a commit relative to a ref: 1a2b3c4 is tags/v1.2.0~3.Safe
git name-rev [--tags] <commit>…
--tags
Use only tags.
--name-only
Print just the name.
  • git name-rev --tags a1b2c3d

    Which release it came three commits before.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git describe.

git whatchangedDeprecated: git log with the raw diff and without merges. Git plans to remove it.Safe

Replaced by git log.

git whatchanged <option>…
  • git log --raw --no-merges

    What to run instead.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Since Git 2.51: Deprecated and scheduled for removal.

git log -SFind the commits that added or removed a string: "when did this line appear, and who removed that function?"Safe
git log -S <string> [--oneline] [-p] [-- <path>]
git log -G <regex>
-S <string>
Commits that change the number of occurrences of <string>.
-G <regex>
Commits whose added or removed lines match <regex> (also catches in-place edits).
--pickaxe-regex
Treat the -S string as a regular expression.
-p
Show the matching diffs.
  • git log -S "STRIPE_KEY" --oneline --all

    Every commit that added or removed that string, on any branch — where a secret entered history.

  • git log -G "timeout\s*=" -p -- config/

    Changes to timeout settings under config/.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log, git grep, git blame.

git log A..B / A...BName sets of commits: A..B is "on B but not A"; A...B is "on either but not both" (the symmetric difference).Safe
git log A..B
git log A...B --left-right
git log ^A B C
--left-right
Mark each commit < (from the left side) or > (right).
--cherry-pick
With A...B: hide commits that have an equivalent on the other side.
--boundary
Also show the commits where the range stops.
  • git log --oneline main..feature

    What merging feature into main would bring.

  • git log --oneline origin/main..main

    What you have not pushed.

  • git log --oneline --left-right main...feature

    How two branches diverged.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

In git diff the dots mean something else: git diff A..B is the same as git diff A B, and git diff A...B compares B with the merge base of A and B.

See also git log, git rev-list, git diff.

git annotateAn older form of git blame with a slightly different output format, kept for compatibility.Safe

Replaced by git blame.

git annotate [<options>] [<rev>] [--] <file>
  • git blame <file>

    What to use instead.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Remotes & sharing

git remoteManage the named remotes (origin, upstream…) a repository fetches from and pushes to.Safe
git remote [-v]
git remote add <name> <url>
git remote rename <old> <new>
git remote remove <name>
git remote set-url <name> <url>
git remote show <name>
git remote prune <name>
-v, --verbose
Show each remote's fetch and push URL.
add <name> <url>
Register a remote; -f fetches it straight away.
set-url <name> <url>
Change a remote's URL (after a repository moves, or to switch HTTPS↔SSH).
show <name>
Branches on the remote, which are tracked, and what push and pull would do.
prune <name>
Delete remote-tracking branches whose branch was deleted on the remote.
rename, remove
Rename a remote (its remote-tracking branches too), or remove it.
  • git remote -v

    Where origin points.

  • git remote add upstream https://github.com/original/repo.git

    Track the project you forked from.

  • git remote set-url origin git@github.com:new-org/repo.git

    The repository moved.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Remote settings live in .git/config; add the remote back to restore it.

See also git fetch, git push, git clone, git ls-remote.

git fetchDownload new commits and refs from a remote and update the remote-tracking branches (origin/main…). Your branches and files do not change.Safe
git fetch [<remote>] [<refspec>…]
git fetch --all
--all
Fetch every remote.
-p, --prune
Delete remote-tracking branches that no longer exist on the remote.
--tags
Fetch all tags as well.
--depth=<n>, --unshallow
Deepen a shallow clone, or make it complete.
--dry-run
Show what would be fetched.
-f, --force
Allow non-fast-forward updates of the refs being fetched into.
  • git fetch

    Update origin/* from origin.

  • git fetch --all --prune

    Every remote, and forget branches deleted there.

  • git log --oneline main..origin/main

    After fetching: what the remote has that you do not.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: only remote-tracking refs move, and their reflogs record it.

See also git pull, git remote, git push.

git pullFetch, then integrate the upstream branch into the current branch — by merging (the default), rebasing or fast-forwarding only.Safe
git pull [--rebase | --no-rebase | --ff-only] [<remote> [<branch>]]
-r, --rebase
Rebase your local commits onto the fetched branch instead of merging.
--no-rebase
Merge (creates a merge commit when the histories diverged).
--ff-only
Update only if it is a fast-forward; otherwise stop.
--autostash
Stash local changes around the pull.
--squash, --no-commit
Passed to the merge.
  • git pull --rebase

    Replay your unpushed commits on top of what others pushed: a straight history.

  • git pull --ff-only

    Update a branch you do not commit to (a local main, say).

  • git config set --global pull.rebase true

    Make --rebase the default.

Rewrites history?
With --rebase: your unpushed commits get new ids. Merging and fast-forwarding rewrite nothing.
Discards uncommitted work?
No; it refuses when local changes conflict (use --autostash).
Safe on a shared branch?
Yes: it only changes your branch.
If it goes wrong
git reset --hard ORIG_HEAD undoes the merge or rebase it just did.

When your branch and the upstream have diverged and you have not chosen how to reconcile them, git pull stops with "Need to specify how to reconcile divergent branches" and names the three settings: pull.rebase false (merge), pull.rebase true (rebase) and pull.ff only.

See also git fetch, git merge, git rebase.

git pushUpload commits and move a remote's branches to match yours. Refused unless the remote branch is an ancestor of what you push.Safe
git push [-u] [<remote> [<refspec>…]]
git push <remote> <local>:<remote-branch>
git push --tags
-u, --set-upstream
Record the remote branch as the upstream, so plain git push and git pull know where to go.
--force-with-lease
Overwrite the remote branch only if it is still where your remote-tracking branch says (see its entry).
--force-if-includes
With --force-with-lease: also require that the remote tip is in your branch's reflog (Git 2.30+).
-f, --force
Overwrite unconditionally (see its entry).
-d, --delete
Delete the named remote branches.
--tags
Push every tag.
--follow-tags
Also push annotated tags that point into the pushed commits.
-n, --dry-run
Show what would be updated.
--atomic
All refs update or none do.
  • git push -u origin feature/login

    Publish a new branch and set its upstream.

  • git push

    Push the current branch to its upstream.

  • git push origin v1.2.0

    Publish one tag.

Rewrites history?
No, without --force or --force-with-lease: a non-fast-forward push is rejected.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
A pushed commit is public; undo it with git revert and push the revert.

See also git push --force-with-lease, git push --force, git push --delete, git fetch, git pull.

git push --force-with-leaseOverwrite a remote branch after rewriting history — but only if nobody pushed to it since you last fetched.Rewrites shared history
git push --force-with-lease [--force-if-includes] [<remote> <branch>]
git push --force-with-lease=<ref>:<expected-id> <remote> <branch>
--force-with-lease
Require each remote branch to equal your remote-tracking branch for it (origin/x).
--force-with-lease=<ref>:<expect>
Require <ref> to be exactly <expect>: the form git documents as not experimental.
--force-if-includes
Also require the remote tip to be in your branch's reflog, which catches a background fetch having updated origin/x (Git 2.30+).
  • git push --force-with-lease

    After rebasing your own branch.

  • git push --force-with-lease --force-if-includes

    The same, safe even if an editor fetches in the background.

  • git config set --global alias.pushf "push --force-with-lease --force-if-includes"

    Make the safe form the easy one.

Rewrites history?
Yes: it replaces the remote branch's history.
Discards uncommitted work?
Only commits you have seen (they were in origin/x); a teammate's newer push makes it fail instead.
Safe on a shared branch?
Only on a branch you alone push to, or by agreement. Never on main.
If it goes wrong
Anyone who had the old tip can push it back; the old commits are in your reflog (<branch>@{1}).

A plain git fetch updates origin/x, and then --force-with-lease alone will overwrite whatever that fetch brought in. --force-if-includes closes that gap.

Since Git 2.30: --force-if-includes. See also git push, git push --force, git rebase, git commit --amend.

git push --forceOverwrite a remote branch with yours unconditionally. Commits others pushed since you last fetched are removed from it.Rewrites shared history

Replaced by git push --force-with-lease.

git push --force <remote> <branch>
git push <remote> +<branch>
-f, --force
Skip the fast-forward check for every ref pushed.
+<refspec>
Force just this ref (git push origin +feature).
  • git push --force-with-lease

    Use this instead: it refuses when the remote has commits you have not seen.

Rewrites history?
Yes.
Discards uncommitted work?
Yes, others' pushed commits, silently.
Safe on a shared branch?
No.
If it goes wrong
Someone who still has the lost commits must push them again; the remote keeps no reflog you can reach.

See also git push --force-with-lease.

git push --deleteDelete a branch or tag on the remote.Rewrites shared history
git push <remote> --delete <branch>
git push <remote> :<branch>
-d, --delete
Delete the named refs on the remote.
  • git push origin --delete feature/old-auth

    Delete a merged branch on the server.

  • git push origin :refs/tags/v1.0.0-rc1

    Delete a tag (the empty source form).

Rewrites history?
No, but the branch is gone for everyone.
Discards uncommitted work?
No local work.
Safe on a shared branch?
Only branches you own or that are merged.
If it goes wrong
Push it again from any clone that has it: git push origin <id>:refs/heads/<branch>.

See also git push, git branch.

git ls-remoteList the refs a remote has, with their commit ids, without fetching anything.Safe
git ls-remote [--branches] [--tags] [<remote>]
-b, --branches
Only branches (refs/heads). --heads is the older spelling, deprecated in Git 2.46.
-t, --tags
Only tags.
--symref
Show what HEAD points to.
  • git ls-remote --tags origin

    Which release tags the server has.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git fetch, git remote.

git clone --filterPartial clone: download every commit and tree but fetch file contents (blobs) only when a command needs them.Safe
git clone --filter=blob:none <url>
git clone --filter=tree:0 <url>
--filter=blob:none
Omit all file contents up front; the usual choice for developers.
--filter=blob:limit=<size>
Omit only files larger than <size> (1m, 10k).
--filter=tree:0
Omit trees too; fastest, but history commands fetch a lot (best for CI builds of one commit).
  • git clone --filter=blob:none https://github.com/org/monorepo.git

    History is complete (log, blame work); contents arrive as you check out.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

Unlike a shallow clone (--depth), a partial clone keeps all history, so log, bisect and merge-base behave normally. The server must support filters (GitHub and GitLab do).

See also git clone, git sparse-checkout, git backfill.

git submoduleEmbed another repository at a path, pinned to a specific commit.Safe
git submodule add <url> [<path>]
git submodule update --init --recursive
git submodule status
git submodule foreach <cmd>
add <url> [<path>]
Add a submodule and record it in .gitmodules.
update --init --recursive
Clone missing submodules and check out the recorded commits (nested ones too).
update --remote
Move submodules to their tracked branch's latest commit.
status
The recorded commit of each submodule, and whether it matches.
foreach <cmd>
Run a command in each submodule.
deinit <path>
Unregister a submodule and remove its working tree.
  • git clone --recurse-submodules https://github.com/org/app.git

    Clone with submodules in one step.

  • git submodule update --init --recursive

    After cloning without them.

Rewrites history?
No.
Discards uncommitted work?
deinit -f removes a submodule's working tree, including its uncommitted changes.
Safe on a shared branch?
Yes.
If it goes wrong
The superproject records only a commit id per submodule; re-run update to restore it.

The page's engine does not run submodules. Many teams prefer a package manager or a monorepo; Stage 2 compares the options.

See also git clone.

git bundlePack commits and refs into a single file that can be cloned or fetched from, for offline transfer.Safe
git bundle create <file> <git-rev-list-args>
git bundle verify <file>
git clone <file> <dir>
create <file> --all
Every ref.
create <file> main ^v1.0
Only commits after v1.0 (an incremental bundle).
verify <file>
Check the bundle and list the commits it needs.
  • git bundle create repo.bundle --all

    The whole repository in one file.

  • git clone repo.bundle repo

    Clone from it.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git clone, git fetch, git archive.

git pull --rebaseFetch, then replay your unpushed commits on top of the upstream instead of merging: no merge commit, a straight history.Rewrites local history
git pull --rebase [<remote> [<branch>]]
git config set pull.rebase true
-r, --rebase[=<mode>]
true, false, merges (keep local merges) or interactive.
--autostash
Stash local changes around it.
  • git pull --rebase

    Update before pushing, keeping your commits on top.

  • git rebase --continue

    If one of your commits conflicts: fix, git add, continue.

Rewrites history?
Your unpushed commits get new ids. Commits already pushed are not replayed: git skips what the upstream has.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes, as long as the replayed commits were never pushed.
If it goes wrong
git reset --hard ORIG_HEAD right after.

See also git pull, git rebase, git fetch.

Undo & recovery

git resetMove the current branch to another commit and, depending on the mode, reset the index and working tree to match. With paths, it only unstages.Discards work
git reset [--soft | --mixed | --hard | --keep | --merge] [<commit>]
git reset [<commit>] -- <path>…     # unstage paths
--soft
Move the branch only. The index and files keep your changes, now staged.
--mixed
The default: move the branch and reset the index. Changes stay in the files, unstaged.
--hard
Move the branch and overwrite the index and working tree. Uncommitted changes to tracked files are lost.
--keep
Like --hard, but refuse if a file with local changes would be overwritten.
--merge
Abort a merge while keeping unrelated local changes.
-q, --quiet
Print only errors.
  • git reset HEAD~1

    Undo the last commit, keep its changes in the files (unstaged). The same as git reset --mixed HEAD~1.

  • git reset app.js

    Unstage app.js (git restore --staged app.js does the same).

  • git reset --keep origin/main

    Reset to the remote, refusing if that would overwrite local edits.

Rewrites history?
Yes when <commit> is not the current one: the branch stops containing the commits after it.
Discards uncommitted work?
--hard does (and --merge for conflicted paths). --soft and --mixed keep every change in the files.
Safe on a shared branch?
Only if the commits leaving the branch were never pushed.
If it goes wrong
Commits: git reset --hard ORIG_HEAD or <branch>@{1}. Discarded uncommitted edits: none.

See also git reset --soft, git reset --hard, git restore, git revert, git reflog.

git reset --softMove the branch back but keep everything staged: the undone commits' changes are ready to commit again.Rewrites local history
git reset --soft <commit>
--soft
Touch neither the index nor the working tree.
  • git reset --soft HEAD~3 && git commit -m "Add search"

    Squash the last three commits into one.

  • git reset --soft HEAD~1

    Undo the last commit; its changes stay staged.

Rewrites history?
Yes: commits leave the branch.
Discards uncommitted work?
No.
Safe on a shared branch?
Only if the commits were never pushed.
If it goes wrong
git reset --soft ORIG_HEAD (or <branch>@{1}) puts the branch back.

See also git reset, git commit --amend.

git reset --hardMake the branch, the index and the working tree exactly match a commit. Uncommitted changes to tracked files are destroyed.Discards work
git reset --hard [<commit>]
--hard
Overwrite the index and working tree. Untracked files are left alone (git clean removes those).
  • git stash

    First: keep your changes somewhere git can give back.

  • git reset --hard origin/main

    Throw away local commits and changes; match the remote.

  • git reset --hard ORIG_HEAD

    Undo a merge, rebase or reset that just happened.

Rewrites history?
Yes, when <commit> is not HEAD.
Discards uncommitted work?
Yes: every uncommitted change to a tracked file, with no way back. Staged content survives only as loose objects (git fsck --lost-found).
Safe on a shared branch?
Only if the commits leaving the branch were never pushed.
If it goes wrong
Commits: the reflog (HEAD@{1}). Uncommitted edits: none.

See also git reset, git stash, git reflog, git clean.

git revertMake a new commit that undoes an earlier commit's changes. History is not rewritten, so it is safe on shared branches.Safe
git revert [--no-edit] [-n] [-m <parent>] <commit>…
git revert (--continue | --skip | --abort | --quit)
--no-edit
Keep the generated message (Revert "…" / This reverts commit …).
-n, --no-commit
Apply the inverse changes without committing (to revert several as one).
-m <parent>, --mainline <parent>
Revert a merge commit, relative to parent <parent> (1 is the branch that was merged into).
--continue, --skip, --abort
Control a revert that stopped on a conflict.
-s, --signoff
Add Signed-off-by.
  • git revert a1b2c3d

    Undo one pushed commit.

  • git revert -m 1 <merge-id>

    Undo a merged pull request.

  • git revert --no-commit HEAD~3..HEAD && git commit -m "Revert the search rollout"

    Undo three commits as one.

Rewrites history?
No: it adds a commit.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes: the standard undo on main.
If it goes wrong
Revert the revert (git revert <revert-id>) to bring the change back.

After reverting a merge, merging the same branch again brings in nothing: git considers its commits already merged. To reapply, revert the revert.

See also git reset, git cherry-pick.

git cleanDelete untracked files (and with -d, directories) from the working tree. Git has no copy of them.Discards work
git clean -n [-d] [-x | -X]
git clean -f [-d] [-x | -X]
-n, --dry-run
List what would be deleted. Run this first.
-f, --force
Actually delete (git refuses without it, by default).
-d
Also untracked directories.
-x
Also ignored files (build output, node_modules, .env).
-X
Only ignored files.
-i, --interactive
Choose what to delete.
  • git clean -nd

    What would go.

  • git clean -fd

    Delete untracked files and directories.

  • git clean -fdX

    Delete only ignored files: a clean build.

Rewrites history?
No.
Discards uncommitted work?
Yes: untracked files are deleted, and git never had a copy.
Safe on a shared branch?
Yes (it touches no commits).
If it goes wrong
None, unless your editor or OS keeps a history of the files.

See also git reset --hard, git stash.

git stashSave uncommitted changes as special commits and clean the working tree, to switch tasks; bring them back later.Safe
git stash [push] [-u] [-k] [-m <message>] [-- <pathspec>…]
git stash list | show [-p] [<stash>]
git stash pop | apply [--index] [<stash>]
git stash drop [<stash>] | clear
git stash branch <name> [<stash>]
-u, --include-untracked
Also stash untracked files.
-k, --keep-index
Leave staged changes in place.
-S, --staged
Stash only what is staged (Git 2.35+).
-m <message>
Name the entry.
-p, --patch
Choose hunks to stash.
--index
With pop or apply: restore the staged/unstaged split as well.
list
The entries: stash@{0} is the newest.
show -p
An entry's diff.
branch <name>
Create a branch at the commit the stash was made on and apply it there (no conflicts).
drop, clear
Delete one entry, or all of them.
  • git stash push -m "half-done auth"

    Put the work away.

  • git stash list

    What is stashed.

  • git stash pop

    Reapply the newest entry and delete it.

  • git stash branch fix/auth stash@{1}

    Turn an old stash into a branch.

Rewrites history?
No.
Discards uncommitted work?
drop and clear delete entries; nothing else does.
Safe on a shared branch?
Yes: stashes are local.
If it goes wrong
A dropped entry: git fsck --unreachable | grep commit, then git stash apply <id>.

See also git stash pop, git worktree, git switch.

git stash popReapply a stash entry to the working tree and remove it from the stash — unless it conflicts, in which case the entry is kept.Safe
git stash pop [--index] [<stash>]
git stash apply [--index] [<stash>]
--index
Also restore which changes were staged.
apply
The same, but keep the entry.
  • git stash pop

    Bring the newest entry back.

  • git stash apply stash@{2}

    Apply an older entry and keep it.

Rewrites history?
No.
Discards uncommitted work?
No; it refuses if the changes would overwrite local edits.
Safe on a shared branch?
Yes.
If it goes wrong
After a conflict the entry is still in git stash list; git reset --hard, then try again elsewhere.

See also git stash.

git fsckCheck the object database, and list objects nothing points to — the last place a lost commit or stash can be found.Safe
git fsck [--unreachable] [--lost-found] [--no-reflogs]
--unreachable
List objects no ref or reflog reaches.
--lost-found
Write dangling commits and blobs to .git/lost-found/.
--no-reflogs
Treat objects reachable only from reflogs as unreachable.
--full
Check packs and alternates too.
  • git fsck --lost-found

    Recover staged content that a reset --hard dropped.

  • git fsck --unreachable | grep commit

    Candidate lost commits, such as a dropped stash.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Unreachable objects are deleted by git gc after gc.pruneExpire (two weeks by default). Look before then.

See also git reflog, git gc.

git restore --stagedUnstage: make the index match HEAD for the paths, leaving the working tree alone. Nothing is lost.Safe
git restore --staged <pathspec>…
git reset -- <pathspec>…   # the older equivalent
-S, --staged
Restore the index only.
-s <tree>, --source=<tree>
Unstage to another commit's version.
  • git restore --staged .

    Unstage everything; your edits stay in the files.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git add the paths again.

See also git restore, git reset, git add.

git merge --abortGive up on a conflicted merge and return to the state before it started.Discards work
git merge --abort
git rebase --abort
git cherry-pick --abort
git revert --abort
--abort
Restore the pre-operation state.
--quit
Forget the operation but keep the working tree as it is.
  • git merge --abort

    Out of a merge whose conflicts you do not want to resolve now.

Rewrites history?
No.
Discards uncommitted work?
Conflict resolutions made so far are lost. Local changes made before the merge are kept (git refuses to start a merge that would overwrite them).
Safe on a shared branch?
Yes.
If it goes wrong
Start the merge again; with rerere on, your resolutions are reused.

See also git merge, git rerere, git reset.

Tags & releases

git tagName a commit permanently: lightweight tags are bare names, annotated tags are objects with a tagger, date, message and optional signature.Safe
git tag [-a | -s] [-m <msg>] <name> [<commit>]
git tag -l [<pattern>] [--contains <commit>] [--sort=<key>]
git tag -d <name>…
-a, --annotate
Create an annotated tag (a tag object). Use these for releases.
-s, --sign
Create a signed annotated tag.
-m <msg>
The tag message.
-l, --list [<pattern>]
List tags, for example -l "v1.*".
--contains <commit>
Only tags whose commit contains <commit>: which releases include a fix.
--sort=<key>
For example --sort=-v:refname for newest version first.
-n<num>
Show the first lines of each annotation.
-d, --delete
Delete tags (locally).
-f, --force
Replace an existing tag.
  • git tag -a v1.2.0 -m "Release 1.2.0"

    An annotated release tag on HEAD.

  • git push origin v1.2.0

    Tags are not pushed with branches; push them by name (or --follow-tags).

  • git tag --contains a1b2c3d

    Every release that includes commit a1b2c3d.

  • git tag -l "v2.*" --sort=-v:refname

    2.x releases, newest first.

Rewrites history?
-f moves a tag, which is a rewrite for anyone who fetched it.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes, until you move or delete a pushed tag; git fetch does not update tags people already have.
If it goes wrong
A deleted local tag: git tag <name> <id> (the id is in the output of -d).

Never move a published release tag; tag a new version instead. Clients that fetched the old tag keep it.

See also git describe, git push, git verify-tag, git shortlog.

git archiveWrite a tar or zip of a commit's files, without the .git directory.Safe
git archive [--format=<fmt>] [--prefix=<dir>/] [-o <file>] <tree-ish> [<path>…]
--format=<fmt>
tar, tgz, tar.gz or zip (inferred from -o's extension).
--prefix=<dir>/
Put every file under <dir>/ in the archive.
-o <file>, --output=<file>
Write to a file.
--remote=<repo>
Ask a remote to produce the archive (if the server allows it).
  • git archive --format=tar.gz --prefix=app-1.2.0/ -o app-1.2.0.tar.gz v1.2.0

    A release tarball from a tag.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Paths marked export-ignore in .gitattributes are left out, and export-subst fills in placeholders such as $Format:%H$.

See also git tag, git bundle.

git verify-tagCheck the signature on a signed tag.Safe
git verify-tag [-v] <tag>…
-v, --verbose
Print the tag's contents too.
--raw
Show the raw gpg or ssh-keygen status.
  • git verify-tag v1.2.0

    Good signature, or an error.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git tag, git verify-commit.

git verify-commitCheck the signature on commits.Safe
git verify-commit [-v] <commit>…
-v, --verbose
Print the commit's contents too.
--raw
Raw status output.
  • git verify-commit HEAD

    Is the last commit signed, and by whom?

  • git log --show-signature -1

    The same, in log form.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

SSH signing (gpg.format=ssh, Git 2.34+) needs gpg.ssh.allowedSignersFile set for verification to say which key is trusted.

See also git verify-tag, git commit.

Rewriting history

git filter-repoRewrite a whole repository's history: remove a file or secret everywhere, move paths, change emails. A separate tool the git project recommends over filter-branch.Rewrites shared history
git filter-repo --invert-paths --path <path>
git filter-repo --replace-text <file>
git filter-repo --mailmap <file>
--path <path> --invert-paths
Remove <path> from every commit.
--replace-text <file>
Replace strings (secrets) listed in <file> in every file of every commit.
--subdirectory-filter <dir>
Make <dir> the new root (split a directory out).
--mailmap <file>
Rewrite author and committer names and emails.
--analyze
Report sizes and paths first, to see what to remove.
  • git filter-repo --invert-paths --path .env

    Remove .env from all history (in a fresh clone).

  • git push --force --all && git push --force --tags

    Publish the rewritten history; every clone must be re-cloned.

Rewrites history?
Yes: every commit from the first changed one onward gets a new id, on every branch.
Discards uncommitted work?
It refuses to run outside a fresh clone unless --force, to avoid losing local work.
Safe on a shared branch?
It rewrites shared history by design: coordinate, and have everyone re-clone.
If it goes wrong
Keep the original clone until the rewrite is verified.

A leaked secret must be rotated first: rewriting history does not remove copies in forks, clones, caches or pull request refs on the host. GitHub's guide to removing sensitive data covers the host side.

See also git filter-branch, git log -S. Not part of git: install git-filter-repo (Elijah Newren) separately.

git filter-branchThe original history-rewriting command. Its own documentation warns against it and points to git filter-repo.Rewrites shared history

Replaced by git filter-repo.

git filter-branch [--index-filter <cmd>] [--tree-filter <cmd>] [--] <rev-list-options>
--index-filter <cmd>
Rewrite the index of each commit (fast).
--tree-filter <cmd>
Check out each commit and run <cmd> in it (slow).
--prune-empty
Drop commits that become empty.
  • git filter-repo --invert-paths --path secrets.txt

    What to run instead.

Rewrites history?
Yes.
Discards uncommitted work?
It keeps the old refs under refs/original/.
Safe on a shared branch?
No.
If it goes wrong
refs/original/ holds the old branch tips until you delete them.

See also git filter-repo.

git replaceMake git read one object in place of another, without rewriting history (for example, to graft an old history onto a new repository).Rewrites local history
git replace <object> <replacement>
git replace --graft <commit> [<parent>…]
git replace -l
git replace -d <object>
--graft <commit> [<parent>…]
Pretend <commit> has these parents.
--edit <object>
Edit an object's content as a replacement.
-l, --list
List replacements.
-d, --delete
Remove replacements.
  • git replace --graft a1b2c3d old-history-tip

    Join an imported old history below the first new commit.

Rewrites history?
No, but every command sees the replaced history while the replacement exists.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git replace -d <object>.

Replacements live in refs/replace/ and are not fetched by default, so others see the original history unless they fetch those refs.

See also git filter-repo.

git historyExperimental: rewrite one commit without an interactive rebase — fix it up with staged changes, reword it, or split it — and move the branches built on it.Rewrites local history
git history fixup <commit> [--dry-run]
git history reword <commit> [--dry-run]
git history split <commit> [--] [<pathspec>…]
fixup <commit>
Fold what is staged into <commit>.
reword <commit>
Change <commit>'s message.
split <commit>
Split <commit> into two (Git 2.54+).
--dry-run
Show what would change.
--update-refs=(branches|head)
Which refs to move: every descendant branch (the default) or only HEAD.
  • git history reword HEAD~3

    Reword a commit three back; the branches on top are rewritten to follow.

Rewrites history?
Yes: the commit and everything after it get new ids.
Discards uncommitted work?
No; it refuses operations that would conflict.
Safe on a shared branch?
No, once pushed.
If it goes wrong
The reflogs of the moved branches.

Since Git 2.54: Marked experimental; it does not yet handle histories with merges. See also git rebase -i, git commit --amend.

git replayExperimental: rebase or cherry-pick without touching the working tree or index — meant for servers and scripts.Rewrites local history
git replay --onto <newbase> <revision-range>
git replay --advance <branch> <revision-range>
--onto <newbase>
Replay the range onto <newbase> (like rebase).
--advance <branch>
Replay onto <branch> and advance it (like cherry-pick).
--contained
Also update branches contained in the range.
  • git replay --onto main topic~3..topic

    Compute a rebase in a bare repository.

Rewrites history?
Yes: it produces new commits and can update refs.
Discards uncommitted work?
No.
Safe on a shared branch?
No, once pushed.
If it goes wrong
The reflogs of the updated refs.

Since Git 2.44: Marked experimental. See also git rebase, git cherry-pick.

git commit --fixupRecord a correction for an earlier commit now, and fold it in later with git rebase -i --autosquash.Safe
git commit --fixup=<commit>
git commit --fixup=amend:<commit>
git rebase -i --autosquash <base>
--fixup=<commit>
A commit titled "fixup! <subject>"; its message is discarded when folded.
--fixup=amend:<commit>
Also replace the target's message (Git 2.32+).
--fixup=reword:<commit>
Only replace the message (an empty change, Git 2.32+).
--squash=<commit>
A "squash!" commit whose message is combined with the target's.
  • git commit --fixup=a1b2c3d

    Stage the fix, then record it against a1b2c3d.

  • git rebase -i --autosquash main

    Fold every fixup! into its target before merging.

  • git config set --global rebase.autoSquash true

    Make --autosquash the default for rebase -i.

Rewrites history?
Not by itself; the later rebase rewrites.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes until you autosquash.
If it goes wrong
Before the rebase it is an ordinary commit.

Since Git 2.32: The amend: and reword: forms. See also git rebase -i, git commit, git commit --amend.

Large repos & maintenance

git sparse-checkoutCheck out only some directories of a large repository; the rest stays in history but not on disk.Safe
git sparse-checkout set <dir>…
git sparse-checkout add <dir>…
git sparse-checkout list
git sparse-checkout disable
set <dir>…
Check out only these directories (plus files at the top level).
add <dir>…
Add directories to the set.
list
The current directories.
disable
Go back to a full checkout.
reapply
Re-apply the rules after a merge brought in files outside them.
--no-cone
Use full gitignore-style patterns instead of directories (deprecated).
  • git clone --filter=blob:none --sparse https://github.com/org/monorepo.git

    Start with almost nothing on disk.

  • git sparse-checkout set services/api packages/shared

    Work on just these.

Rewrites history?
No.
Discards uncommitted work?
No: files with uncommitted changes outside the new set stay on disk, and git warns about them.
Safe on a shared branch?
Yes.
If it goes wrong
git sparse-checkout disable restores every file.

Since Git 2.25: The sparse-checkout command. Cone mode (whole directories) is the default; non-cone patterns are deprecated since 2.37. See also git clone --filter, git worktree, scalar.

git clone --depthShallow clone: only the last n commits. Fast for CI; limited for development, because history commands see only those commits.Safe
git clone --depth <n> <url>
git fetch --unshallow
--depth <n>
Fetch only n commits of each branch fetched (implies --single-branch).
--shallow-since=<date>
Fetch commits after a date.
--no-single-branch
Shallow history for every branch.
  • git clone --depth 1 https://github.com/org/app.git

    What CI checkout steps do by default (actions/checkout fetches depth 1).

  • git fetch --unshallow

    Fetch the rest of the history later.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

git log, blame and bisect stop at the shallow boundary, and merge-base can fail. For developers, prefer a partial clone (--filter=blob:none).

See also git clone --filter, git clone.

git maintenanceRun (or schedule) the housekeeping that keeps a large repository fast: prefetch, commit-graph, loose-object packing, incremental repack.Safe
git maintenance start
git maintenance run [--task=<task>] [--auto]
git maintenance stop
git maintenance register | unregister
start
Register this repository and schedule hourly, daily and weekly runs with the OS scheduler.
run [--task=<task>]
Run now: gc, commit-graph, prefetch, loose-objects, incremental-repack, pack-refs…
--auto
Run only tasks whose thresholds are met.
stop
Remove the scheduled runs.
  • git maintenance start

    Background maintenance for this repository.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
It only repacks and prunes by the usual expiry rules; git maintenance stop turns it off.

Since Git 2.29: Introduced as the successor to running git gc by hand. See also git gc, scalar, git backfill.

git gcClean up: pack loose objects, pack refs, expire old reflog entries and delete unreachable objects past their grace period.Safe
git gc [--aggressive] [--auto] [--prune=<date>]
--auto
Do something only if there is enough to do (commands run this themselves).
--aggressive
Spend much more time for a smaller pack.
--prune=<date>
Delete unreachable objects older than <date> (default: gc.pruneExpire, two weeks).
--prune=now
Delete them all now — including the commits a reflog-less recovery would need.
  • git gc

    Tidy up by hand (rarely needed; git runs gc --auto).

Rewrites history?
No.
Discards uncommitted work?
With --prune=now: unreachable commits and blobs become unrecoverable at once. The defaults keep them two weeks.
Safe on a shared branch?
Yes.
If it goes wrong
None for objects it pruned.

See also git maintenance, git reflog, git fsck, git prune.

git backfillIn a partial clone, download the file contents that history commands will need, in large batches instead of one at a time.Safe
git backfill [--min-batch-size=<n>] [--[no-]sparse]
--min-batch-size=<n>
Objects per request.
--sparse
Only blobs inside the sparse-checkout.
  • git backfill

    Before running blame or log -p across a partial clone's history.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

Since Git 2.49: For partial clones. See also git clone --filter, git sparse-checkout.

git pruneDelete unreachable loose objects. git gc runs it for you, with an expiry.Discards work
git prune [-n] [--expire <time>]
-n, --dry-run
Report what would be removed.
--expire <time>
Only objects older than <time>.
  • git prune -n

    What is unreachable and loose.

Rewrites history?
No.
Discards uncommitted work?
Unreachable objects are deleted; only reflogs protect lost commits.
Safe on a shared branch?
Yes.
If it goes wrong
None for pruned objects.

See also git gc, git fsck.

git repackCombine objects into pack files (delta-compressed); gc and maintenance call it.Safe
git repack [-a] [-d] [--cruft]
-a
Pack everything into one pack.
-d
Delete packs made redundant.
--cruft
Put unreachable objects in a separate cruft pack.
  • git repack -ad

    One pack for the whole repository.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git gc, git count-objects.

git count-objectsReport how many objects the repository has and how much disk they use.Safe
git count-objects [-v] [-H]
-v, --verbose
Loose and packed counts, pack size, garbage.
-H, --human-readable
Sizes in KiB/MiB.
  • git count-objects -vH

    How big the repository really is.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git repack, git gc.

git lfsGit Large File Storage: keep large binaries (media, models, datasets) on a separate server; the repository stores small pointer files.Safe
git lfs install
git lfs track "*.psd"
git lfs ls-files
git lfs migrate import --include="*.zip"
install
Set up the LFS filters in your git config (once per machine).
track <pattern>
Store matching files in LFS; writes the rule to .gitattributes.
ls-files
Files currently stored in LFS.
migrate import --include=<pattern>
Rewrite history so past versions move to LFS too.
  • git lfs track "*.mp4" && git add .gitattributes

    New .mp4 files go to LFS; commit the rule.

Rewrites history?
migrate import rewrites history (shared).
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Pointer files are small text files; git lfs fetch downloads the content again.

Hosts meter LFS storage and bandwidth separately from the repository.

See also git clone --filter, git sparse-checkout. Not part of git: install Git LFS separately.

scalarConfigure a clone for very large repositories in one step: partial clone, sparse checkout, background maintenance and recommended settings.Safe
scalar clone <url> [<dir>]
scalar register [<dir>]
scalar list
scalar unregister [<dir>]
clone <url>
Clone with the large-repository settings (sparse by default; --full-clone for everything).
register
Apply the settings to an existing clone.
--full-clone
With clone: no sparse checkout.
  • scalar clone https://github.com/org/monorepo.git

    Creates monorepo/src with the settings applied.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

Ships with git (the scalar command) since Git 2.38.

See also git maintenance, git sparse-checkout, git clone --filter.

git commit-graphWrite a file that caches the commit graph, making log --graph, merge-base and reachability checks much faster in big histories.Safe
git commit-graph write [--reachable] [--changed-paths]
git commit-graph verify
write --reachable
Include every commit reachable from a ref.
--changed-paths
Add Bloom filters that speed up path-limited log.
verify
Check the file.
  • git commit-graph write --reachable --changed-paths

    What maintenance's commit-graph task does.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git maintenance.

git refsTools for the ref store: migrate between the files and reftable backends, verify, list and test refs.Safe
git refs migrate --ref-format=reftable [--dry-run]
git refs verify
git refs list
git refs exists <ref>
migrate --ref-format=<format>
Convert to reftable (or files).
--dry-run
Report without converting.
verify
Check the refs for consistency.
exists <ref>
Exit 0 if the ref exists (Git 2.52+).
  • git refs migrate --ref-format=reftable

    Move a repository with many refs to the reftable format.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Migrate back with --ref-format=files.

Since Git 2.46: git refs migrate; list and exists came in 2.52. See also git for-each-ref, git show-ref.

git repoReport facts about the repository (object format, ref backend, paths) and its structure (counts and sizes of refs and objects).Safe
git repo info [<key>…] [--all]
git repo structure
info [--all | <key>…]
Key=value facts such as object.format and references.format.
structure
A table of counts and sizes.
-z
NUL-separated output.
  • git repo structure

    How many branches, tags, commits and how much they weigh.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Since Git 2.52: git repo info and structure. See also git count-objects, git rev-parse.

git pack-refsMove loose ref files into .git/packed-refs, which is faster when a repository has thousands of branches or tags.Safe
git pack-refs [--all] [--no-prune]
--all
Pack all refs, not just tags and already-packed ones.
--no-prune
Keep the loose files.
  • git pack-refs --all

    Pack everything (gc does this).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git gc, git refs.

git multi-pack-indexWrite one index across all pack files, so object lookups do not search each pack in turn (maintenance's incremental-repack uses it).Safe
git multi-pack-index write [--bitmap]
git multi-pack-index verify
git multi-pack-index expire
git multi-pack-index repack
write
Write the index.
--bitmap
With write: also a reachability bitmap.
verify
Check it.
expire, repack
Consolidate packs incrementally.
  • git multi-pack-index write --bitmap

    Faster object lookup and counting in a big repository.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git maintenance, git repack.

Patches & email

git request-pullPrint a summary of changes for a maintainer to pull, in the format the Linux kernel's email workflow uses.Safe
git request-pull <start> <url> [<end>]
-p
Include the patch text.
  • git request-pull v1.0 https://example.com/repo.git main

    What main adds since v1.0, and where to pull it from.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git format-patch.

git format-patchWrite commits as email-ready patch files (one per commit), for projects that review by mail or to move commits without a shared remote.Safe
git format-patch [-o <dir>] [--cover-letter] <since>
git format-patch -<n> <commit>
-o <dir>, --output-directory <dir>
Where to write the .patch files.
-<n>
The last n commits.
--cover-letter
Add a 0000 patch to describe the series.
-v <n>, --reroll-count=<n>
Mark a revised series (v2, v3…).
--stdout
Print instead of writing files.
  • git format-patch -o outgoing/ origin/main

    One file per commit you have that origin/main does not.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git am, git send-email, git apply.

git amApply patches from email (mbox or format-patch files) as commits, keeping their authors and messages.Safe
git am [--3way] [-s] <mbox>…
git am (--continue | --skip | --abort)
-3, --3way
Fall back to a three-way merge when a patch does not apply cleanly.
-s, --signoff
Add Signed-off-by.
--continue, --skip, --abort
Control a stopped series.
  • git am -3 outgoing/*.patch

    Apply a series with three-way fallback.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git am --abort returns to where you started.

See also git format-patch, git apply.

git applyApply a diff to the working tree (and optionally the index), without making a commit.Safe
git apply [--check] [--index] [-3] [-R] <patch>…
--check
Test whether it applies; change nothing.
--index
Update the index too.
-3, --3way
Three-way merge on failure.
-R, --reverse
Apply in reverse (undo the patch).
--stat
Summarise the patch.
  • git diff > fix.patch && git apply --check fix.patch

    Save a diff and test it applies elsewhere.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
git apply -R with the same patch reverts it.

See also git am, git diff.

git send-emailSend patch files as emails, threaded as a series (the Linux kernel's and git's own review workflow).Safe
git send-email [--to=<addr>] [--annotate] <files | directory>…
--to=<addr>, --cc=<addr>
Recipients.
--annotate
Edit each email before sending.
--dry-run
Do everything except send.
  • git send-email --to=list@example.org outgoing/

    Mail a series to a list.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git format-patch, git request-pull.

Plumbing

git cat-filePrint an object — blob, tree, commit or tag — by id, or its type and size. The way to look inside the object store.Safe
git cat-file -p <object>
git cat-file -t <object>
git cat-file -s <object>
-p
Pretty-print the content.
-t
The object's type.
-s
Its size in bytes.
-e
Exit 0 if it exists.
--batch
Read ids from stdin, print each object.
  • git cat-file -p HEAD

    The raw commit: tree, parent, author, committer, message.

  • git cat-file -p HEAD^{tree}

    The top-level tree: mode, type, id, name per entry.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

The Git Model reference's object inspector builds these objects in the browser, with the same ids.

See also git hash-object, git ls-tree, git rev-parse.

git hash-objectCompute the id git would give some content (sha1 of "blob <size>\0<content>"), and optionally store it.Safe
git hash-object [-w] [--stdin] [<file>…]
-w
Write the object into the repository.
--stdin
Read content from standard input.
-t <type>
Hash as another type (default blob).
  • printf '' | git hash-object --stdin

    e69de29bb2d1d6434b8b29ae775ad8c2e48c5391: the empty blob, in every repository.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git cat-file.

git rev-parseTurn a revision name (HEAD~2, main@{1}, v1.0^{commit}) into an id, and answer questions about the repository for scripts.Safe
git rev-parse <rev>…
git rev-parse --show-toplevel | --abbrev-ref HEAD | --is-inside-work-tree
--short[=<n>]
An abbreviated id.
--abbrev-ref
The short ref name (git rev-parse --abbrev-ref HEAD prints the branch).
--show-toplevel
The root of the working tree.
--git-dir
The path of .git.
--verify
Exactly one valid object, or fail.
--is-inside-work-tree
true or false.
  • git rev-parse HEAD

    The full id of HEAD.

  • git rev-parse --abbrev-ref @{u}

    The current branch's upstream, e.g. origin/main.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git rev-list, git cat-file.

git rev-listList commit ids reachable from some commits and not others — the engine under git log, and the tool for counting.Safe
git rev-list [--count] [--left-right] <commit>… [^<commit>…]
--count
Just the number.
--left-right
With A...B: mark which side each commit is on.
--objects
Also list trees and blobs.
--max-count=<n>
Stop after n.
  • git rev-list --left-right --count main...feature

    "3 5": main has 3 commits feature lacks, feature has 5 main lacks.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log, git rev-parse, git merge-base.

git ls-filesList files in the index, with filters for modified, deleted, untracked, ignored or conflicted ones.Safe
git ls-files [-s] [-m] [-o] [-i --exclude-standard] [-u]
-s, --stage
Mode, id and stage number for each entry (stages 1–3 during a conflict).
-m, --modified
Modified files.
-o, --others
Untracked files.
-i, --ignored
Ignored files (with --exclude-standard).
-u, --unmerged
Conflicted entries.
  • git ls-files -s

    The index itself: what the next commit will contain.

  • git ls-files -oi --exclude-standard

    Every ignored file.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git ls-tree, git status, git check-ignore.

git ls-treeList a tree object's entries: the files and directories of a commit.Safe
git ls-tree [-r] [--name-only] <tree-ish> [<path>…]
-r
Recurse into subtrees.
--name-only
Just the paths.
-l, --long
Include blob sizes.
  • git ls-tree -r --name-only HEAD

    Every file in the last commit.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git cat-file, git ls-files.

git write-treeWrite the index as a tree object and print its id — the first half of what git commit does.Safe
git write-tree
--missing-ok
Allow entries whose objects are missing.
  • git write-tree

    The tree id the next commit would have.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git commit-tree, git read-tree.

git commit-treeCreate a commit object from a tree and parents; the second half of what git commit does. No ref moves.Safe
git commit-tree <tree> [-p <parent>…] -m <message>
-p <parent>
A parent (repeat for merges).
-m <message>
The message.
-S[<keyid>]
Sign it.
  • git commit-tree $(git write-tree) -p HEAD -m "Built by hand"

    Prints the new commit's id; git update-ref would point a branch at it.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git write-tree, git update-ref.

git read-treeLoad a tree into the index, optionally merging up to three trees; the plumbing under checkout and merge.Discards work
git read-tree [-m] [-u] <tree-ish>…
-m
Merge the trees into the index.
-u
Update the working tree too.
--prefix=<dir>/
Read the tree into a subdirectory.
  • git read-tree --prefix=vendor/lib/ -u lib/main

    Import another branch's files under a directory (a subtree merge).

Rewrites history?
No.
Discards uncommitted work?
With -u it overwrites working-tree files.
Safe on a shared branch?
Yes.
If it goes wrong
git reset --hard to the previous state (uncommitted edits are lost).

See also git write-tree, git checkout.

git update-refSet or delete a ref safely, writing its reflog — how scripts move branches.Rewrites local history
git update-ref [-m <reason>] <ref> <new> [<old>]
git update-ref -d <ref> [<old>]
-m <reason>
The reflog message.
-d
Delete the ref.
<old>
Only update if the ref currently has this value (a compare-and-swap).
--stdin
Many updates in one transaction.
  • git update-ref refs/heads/main a1b2c3d

    Point main at a commit (no checkout happens).

Rewrites history?
It can move a branch anywhere, including backwards.
Discards uncommitted work?
No.
Safe on a shared branch?
Only for your local refs.
If it goes wrong
The ref's reflog.

See also git symbolic-ref, git for-each-ref.

git symbolic-refRead or set a symbolic ref such as HEAD, which names another ref instead of a commit.Safe
git symbolic-ref HEAD
git symbolic-ref HEAD refs/heads/<branch>
--short
Print main instead of refs/heads/main.
-d, --delete
Delete the symbolic ref.
  • git symbolic-ref --short HEAD

    The current branch (fails when HEAD is detached).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git rev-parse, git update-ref.

git for-each-refList refs with custom formatting and sorting — the building block for branch and tag reports.Safe
git for-each-ref [--sort=<key>] [--format=<format>] [<pattern>…]
--sort=<key>
For example -committerdate.
--format=<format>
%(refname:short) %(objectname:short) %(upstream:track) and more.
--merged, --no-merged, --contains
Filters, as in git branch.
--count=<n>
At most n refs.
  • git for-each-ref --sort=-committerdate --format="%(refname:short) %(committerdate:relative)" refs/heads

    Branches by last activity.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git show-ref, git branch, git refs.

git show-refList refs and their ids, or test whether one exists.Safe
git show-ref [--branches] [--tags]
git show-ref --verify <ref>
git show-ref --exists <ref>
--branches
Only branches (--heads is the older spelling).
--tags
Only tags.
--verify
Require an exact ref name.
--exists
Report through the exit status only.
  • git show-ref --verify --quiet refs/heads/main && echo yes

    Does main exist?

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git for-each-ref.

git merge-baseFind the best common ancestor of commits — where two branches forked.Safe
git merge-base <a> <b>
git merge-base --is-ancestor <a> <b>
git merge-base --fork-point <ref> [<commit>]
--all
Every best common ancestor (criss-cross histories have more than one).
--is-ancestor
Exit 0 if a is an ancestor of b.
--fork-point
Use the ref's reflog to find where a branch forked even after upstream was rebased.
  • git merge-base main feature

    The commit a pull request is compared against.

  • git merge-base --is-ancestor v1.0 HEAD && echo contained

    Does HEAD include v1.0?

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git rev-list, git diff.

git update-indexChange index entries directly: add content, set flags such as assume-unchanged or skip-worktree.Safe
git update-index [--add] [--remove] <file>…
git update-index --[no-]skip-worktree <file>…
--add, --remove
Add or remove entries.
--chmod=+x
Set the executable bit in the index.
--skip-worktree
Pretend the file is unchanged (for local-only edits); sparse checkout uses it.
--assume-unchanged
A performance hint, not a way to ignore changes.
--refresh
Re-check stat information.
  • git update-index --chmod=+x deploy.sh

    Make a script executable in the repository (useful on Windows).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git add, git ls-files.

git check-ignoreSay whether paths are ignored, and which rule in which file ignores them.Safe
git check-ignore [-v] [--no-index] <path>…
-v, --verbose
Show the file, line and pattern that matched.
--no-index
Also check paths that are tracked.
-n, --non-matching
With -v, list paths that match nothing too.
  • git check-ignore -v dist/app.js

    .gitignore:3:dist/ dist/app.js

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Stage 2's ignore generator has a path tester that answers the same question in the browser.

See also git status, git check-attr.

git check-attrShow which .gitattributes attributes apply to paths (text, eol, diff, merge, filter, export-ignore…).Safe
git check-attr [-a] <attr>… [--] <path>…
-a, --all
Every attribute set on the paths.
--source=<tree>
Read .gitattributes from a commit.
  • git check-attr -a -- assets/logo.png

    binary: set, diff: unset, merge: unset.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git check-ignore.

git mktreeBuild a tree object from ls-tree-formatted lines on standard input.Safe
git mktree [-z] [--missing] [--batch]
-z
NUL-terminated input.
--missing
Allow entries whose objects are missing.
  • git ls-tree HEAD | git mktree

    Prints the same tree id as HEAD^{tree}.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git write-tree, git ls-tree.

git diff-treeCompare two trees (or a commit with its parent): the plumbing under git show and log -p.Safe
git diff-tree [-r] [-p] [--name-status] <tree-ish> [<tree-ish>]
-r
Recurse into subtrees.
-p
Patch output.
--name-status
Names and statuses.
--no-commit-id
Omit the commit id line.
  • git diff-tree --no-commit-id --name-only -r HEAD

    Files the last commit changed (common in CI scripts).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git diff, git show.

git merge-treeCompute a merge without touching the index or working tree; prints the resulting tree and any conflicts.Safe
git merge-tree [--write-tree] <branch1> <branch2>
--write-tree
Write the merged tree and print its id (the default mode).
--name-only
List conflicted files by name.
--messages
Also print the informational messages.
  • git merge-tree --write-tree main feature

    Would this merge conflict? Exit status 1 if so.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

Code hosts use it to decide whether a pull request can be merged, without a working tree.

See also git merge, git merge-file.

git merge-fileThree-way merge three files (ours, base, theirs) into the first, with conflict markers — git's merge on single files.Safe
git merge-file [-p] [--diff3 | --zdiff3] <current> <base> <other>
-p
Print the result instead of overwriting <current>.
--diff3
Include the base in conflict hunks.
--zdiff3
diff3, with lines both sides share moved out of the conflict (Git 2.35+).
--ours, --theirs, --union
Resolve conflicts to one side, or keep both.
  • git merge-file -p --zdiff3 mine.txt base.txt theirs.txt

    What the conflict looks like in zdiff3 style.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git merge, git merge-tree.

git varPrint git's logical variables: the identity it will use, the editor, the pager.Safe
git var (GIT_AUTHOR_IDENT | GIT_COMMITTER_IDENT | GIT_EDITOR | GIT_PAGER | -l)
-l
List them all, with config.
  • git var GIT_EDITOR

    Which editor git will open.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git config.

git patch-idCompute an id for a patch's content, ignoring line numbers and whitespace — how git recognises the same change under different commit ids.Safe
git show <commit> | git patch-id --stable
--stable
An id that does not depend on file order.
  • git show HEAD | git patch-id --stable

    rebase and cherry use the same idea to skip changes already upstream.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git cherry, git rebase.

git check-ref-formatTest whether a string is a valid ref or branch name (no spaces, no .., no trailing .lock…).Safe
git check-ref-format --branch <name>
git check-ref-format <refname>
--branch
Check a branch name, expanding @{-1}.
--normalize
Print the cleaned-up name.
  • git check-ref-format --branch "feature/PROJ-42-login" && echo ok

    Validate a generated branch name.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git branch.

git check-mailmapShow the canonical name and email .mailmap gives for an identity (after someone changed their name or email).Safe
git check-mailmap "Name <email>"…
--stdin
Read identities from standard input.
  • git check-mailmap "A. Lovelace <ada@old.example>"

    Ada Lovelace <ada@example.com>, if .mailmap maps it.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git shortlog.

git verify-packCheck a pack file, and list its objects with sizes and delta chains.Safe
git verify-pack -v <pack>.idx
-v, --verbose
List every object.
-s, --stat-only
Only the delta-chain histogram.
  • git verify-pack -v .git/objects/pack/*.idx | sort -k3 -n | tail

    The largest objects: what makes a repository big.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git count-objects, git repack.

git stripspaceClean up text the way git cleans commit messages: trailing spaces, extra blank lines, and (with -s) comment lines.Safe
git stripspace [-s | -c] < <text>
-s, --strip-comments
Also remove lines starting with the comment character.
-c, --comment-lines
Prefix every line with the comment character.
  • printf 'Fix\n\n\n# note\n' | git stripspace -s

    Prints just "Fix".

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git commit.

git mktagCreate an annotated tag object from text on standard input (after checking it is well formed).Safe
git mktag < <tag-text>
--strict
Reject any format warnings (the default).
  • git cat-file tag v1.0 | git mktag

    Recreates the same tag object and prints its id.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git tag, git cat-file.

git diff-indexCompare a tree with the working tree or (with --cached) the index; the plumbing under git diff HEAD and git diff --staged.Safe
git diff-index [--cached] [<diff-options>] <tree-ish> [<path>…]
--cached
Compare with the index only.
--merge-base
Use the merge base of the commit and HEAD.
  • git diff-index --quiet HEAD -- || echo dirty

    Exit status 1 if anything differs from HEAD (a script's dirty check).

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git diff, git diff-files.

git diff-filesCompare the working tree with the index: the plumbing under plain git diff.Safe
git diff-files [-q] [<diff-options>] [<path>…]
-q
Stay quiet about files missing from the working tree.
-c, --cc
Combined diff for conflicted paths.
  • git diff-files --name-only

    Files with unstaged changes.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git diff-index, git diff.

git checkout-indexCopy files from the index to the working tree (or elsewhere, with --prefix).Discards work
git checkout-index [-a] [-f] [--prefix=<dir>/] [<file>…]
-a, --all
Every file in the index.
-f, --force
Overwrite existing files.
--prefix=<string>
Write under this directory instead.
  • git checkout-index -a --prefix=/tmp/export/

    Export the index's files to another directory.

Rewrites history?
No.
Discards uncommitted work?
With -f it overwrites working-tree files.
Safe on a shared branch?
Yes.
If it goes wrong
None for overwritten edits.

See also git restore, git read-tree.

git pack-objectsBuild a pack file from a list of objects; the core of repack, gc and every fetch and push.Safe
git rev-list --objects <revs> | git pack-objects <base-name>
--stdout
Write the pack to standard output.
--revs
Read revisions instead of object ids.
--window=<n>, --depth=<n>
How hard to search for deltas.
  • git rev-list --objects --all | git pack-objects .git/objects/pack/pack

    Pack every object by hand.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git repack, git index-pack, git verify-pack.

git index-packBuild the .idx index for a pack file (fetch does this for every pack it receives).Safe
git index-pack [-v] <pack-file>
git index-pack --stdin [--fix-thin]
-v
Report progress.
--stdin
Read the pack from standard input and store it.
--fix-thin
Complete a thin pack with local objects.
  • git index-pack pack-1234.pack

    Recreate a lost .idx.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git pack-objects, git verify-pack.

git fmt-merge-msgProduce the default merge commit message (Merge branch 'x' …) from FETCH_HEAD data, optionally with a one-line log of the merged commits.Safe
git fmt-merge-msg [--log[=<n>]] < .git/FETCH_HEAD
--log[=<n>]
Append up to n one-line summaries of the merged commits.
-m <message>, --message <message>
Use this as the first line.
  • git config set merge.log 20

    Make git merge include up to 20 commit subjects in its message.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git merge.

git for-each-repoRun a git command in every repository listed in a multi-valued config key (how scheduled maintenance reaches each registered repository).Safe
git for-each-repo --config=<key> [--] <git-arguments>
--config=<key>
The config key listing repository paths (maintenance.repo).
--keep-going
Continue after a failure.
  • git for-each-repo --config=maintenance.repo maintenance run --auto

    What the scheduler runs.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git maintenance.

git upload-packThe server side of fetch and clone: it sends the objects a client asks for. Clients run it over SSH; you rarely call it directly.Safe
git-upload-pack [--strict] [--timeout=<n>] <directory>
--strict
Do not look for <directory>/.git if <directory> is not a repository.
--timeout=<n>
Give up after n seconds of inactivity.
  • git clone --upload-pack="git upload-pack" host:repo.git

    Name it explicitly when the remote's PATH lacks git.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git receive-pack, git daemon.

git receive-packThe server side of push: receives objects and updates refs, running the pre-receive, update and post-receive hooks.Safe
git receive-pack <git-dir>
--skip-connectivity-check
Skip checking that received objects are complete (for trusted setups).
  • git config set receive.denyNonFastForwards true

    On a server repository: refuse force pushes.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git upload-pack, git hook.

git unpack-objectsExplode a pack read from standard input into loose objects.Safe
git unpack-objects [-n] [-q] < <pack-file>
-n
Dry run.
-q
No progress.
--strict
Check the objects as they are written.
  • git unpack-objects < pack-1234.pack

    Recover objects from a pack by hand.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git pack-objects.

Other tools & systems

gitkGit's built-in history browser (Tk). git gui is its companion for committing.Safe
gitk [--all] [<revision-range>]
git gui
--all
Every ref.
  • gitk --all &

    Browse the whole graph.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git log.

git bugreportWrite a bug report template with your git version, system details and enabled hooks, for reporting a git bug.Safe
git bugreport [-o <path>]
-o <path>, --output-directory=<path>
Where to write it.
  • git bugreport

    Creates git-bugreport-<date>.txt to fill in and send to the git mailing list.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git version.

git fast-exportDump history as a fast-import stream: for migrations, filtering and moving to other tools.Safe
git fast-export [--all] [--signed-tags=<mode>] > stream
--all
Every ref.
--anonymize
Replace all content and names (for sharing a bug reproduction).
--signed-tags=<mode>
What to do with signatures (strip, warn, verbatim).
  • git fast-export --all | (cd ../copy && git fast-import)

    Copy history through the stream format.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git fast-import, git filter-repo.

git fast-importBuild history from a fast-import stream: what migrations from Subversion, Mercurial or Perforce feed into.Rewrites local history
git fast-import < stream
--force
Allow updates that are not fast-forwards.
--quiet
No statistics.
  • git init new && cd new && git fast-import < ../export.stream

    Load an exported history.

Rewrites history?
With --force it can move existing branches.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
The branches' reflogs.

See also git fast-export.

git svnWork with a Subversion repository through git: clone it, commit locally, and send commits back as svn revisions.Rewrites local history
git svn clone <url> [-s]
git svn rebase
git svn dcommit
-s, --stdlayout
trunk/, branches/, tags/ layout.
rebase
Fetch new svn revisions and rebase onto them.
dcommit
Send local commits to svn.
  • git svn clone -s https://svn.example.com/project

    A git copy of an svn project.

Rewrites history?
dcommit rewrites your local commits to record their svn revisions.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
The reflog.

See also git fast-import.

git p4Import from and submit to Perforce depots.Safe
git p4 clone //depot/path@all
git p4 sync
git p4 submit
clone
Import a depot path.
sync
Fetch new changelists.
submit
Send commits to Perforce.
  • git p4 clone //depot/project@all

    Import the full history.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git fast-import.

git guiGit's built-in graphical tool for staging and committing; gitk is its history viewer.Safe
git gui
git gui blame <file>
git citool
  • git gui

    Stage hunks and write commits in a window.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also gitk, git citool.

git citoolgit gui opened for one commit, then closed.Safe
git citool
  • git citool

    Commit through the GUI.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git gui.

git instawebBrowse the repository in a local web page (gitweb) served by a small web server.Safe
git instaweb [--httpd=<server>] [--port=<port>]
git instaweb --stop
-d <httpd>, --httpd=<httpd>
Which server (lighttpd, apache2, webrick…).
-p <port>, --port=<port>
The port.
--stop
Stop the server.
  • git instaweb --httpd=webrick

    Open the repository in a browser.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

git daemonServe repositories over git:// (unauthenticated, read-only by default): for local networks and mirrors.Safe
git daemon [--export-all] [--base-path=<path>] [<directory>…]
--export-all
Serve every repository, not only those with git-daemon-export-ok.
--base-path=<path>
Map git://host/x to <path>/x.
--enable=receive-pack
Allow pushes (no authentication: only on trusted networks).
  • git daemon --export-all --base-path=/srv/git

    Serve /srv/git read-only.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

git diagnoseCollect diagnostics about a repository (sizes, layout, configuration summary) into a zip for a bug report.Safe
git diagnose [-o <path>] [--mode=(stats|all)]
-o <path>, --output-directory <path>
Where to write the zip.
--mode=<mode>
stats (default) or all (also copies .git contents: may include private data).
  • git diagnose

    A zip for maintainers investigating slowness.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it only reads the repository.

See also git bugreport.

git cvsimportImport a CVS repository's history into git.Safe
git cvsimport -d <CVSROOT> [-C <git-repository>] <CVS-module>
-d <CVSROOT>
The CVS root.
-C <git-repository>
Where to create the git repository.
-A <author-conv-file>
Map CVS user names to Name <email>.
  • git cvsimport -d :pserver:anon@cvs.example.org:/cvs -C project.git project

    One-time migration.

Rewrites history?
No.
Discards uncommitted work?
No.
Safe on a shared branch?
Yes.
If it goes wrong
Nothing to recover: it does not change commits or your files.

See also git svn, git fast-import.

Checked against the Git 2.55.0 documentation: every option listed here appears in that command's manual page, and every “since” version in the release notes. Commands marked Try it run in the page's engine, which is tested line by line against real git.

Reference

Rescue

“I did X — how do I undo it?” 27 situations, each with the exact commands in order, a safety rating, and a Preview that loads the broken repository so you can run the fix and watch the graph first.

Find what happened, check the When line, and run the steps in order. Preview loads a repository in exactly that broken state; each step's Run applies it there first. Every recipe here is tested against real git: the broken state is recorded from git 2.55.0, and the fix is checked to produce the outcome it states.

The last commit

I want to undo my last commit but keep the changes

Rewrites local history

When: The commit is not pushed.

  1. git reset --soft HEAD~1Rewrites local history
    Move the branch back one commit. The changes stay staged, ready to commit again. (git reset HEAD~1 leaves them unstaged instead.)
  2. git status
    The undone commit's files are listed under Changes to be committed.

Fixed when: main is back at Add README, and parser.js is staged with the undone commit's content.

Otherwise: Pushed already? Use git revert HEAD instead, which adds an undoing commit (see "Undo a commit I already pushed").

I made a typo in my last commit message

Rewrites local history

When: The commit is not pushed.

  1. git commit --amend -m "Fix the login bug"Rewrites local history
    Replace the last commit with one that has the new message (and a new id).

Fixed when: The tip commit reads "Fix the login bug" and sits directly on Add README; there is still one commit, not two.

Otherwise: Pushed already? Amending means a force push. On a branch only you use: amend, then git push --force-with-lease. On a shared branch, leave the typo.

Commands: git commit --amend

I forgot to add a file to my last commit

Rewrites local history

When: The commit is not pushed.

  1. git add parser.test.js
    Stage the missing file.
  2. git commit --amend --no-editRewrites local history
    Fold it into the last commit and keep its message.

Fixed when: The Add parser commit now contains both parser.js and parser.test.js.

Commands: git commit --amend, git add

I amended the wrong commit, or amended when I meant to make a new one

Rewrites local history

When: The amended commit is not pushed.

  1. git reset --soft HEAD@{1}Rewrites local history
    HEAD@{1} is where HEAD was before the amend: the original commit. --soft keeps the amend's extra changes staged.
  2. git commit -m "Add lexer"
    Commit them as a separate commit, as intended.

Fixed when: Add parser is back as it was, and the lexer change is its own commit on top.

I want to combine my last few commits into one

Rewrites local history

When: The commits are on your own branch and not yet merged or shared.

  1. git reset --soft HEAD~3Rewrites local history
    Move the branch back three commits; all their changes stay staged.
  2. git commit -m "Add search"
    Record them as one commit.

Fixed when: feature/search has one commit, Add search, containing all three changes. (git rebase -i with squash does the same and lets you keep some commits.)

Otherwise: Already pushed to a branch others use? Squash when merging instead (the host's Squash and merge, or git merge --squash).

My commit has the wrong name or email

Rewrites local history

When: The commit is not pushed.

  1. git config user.name "Ada Lovelace"
    Fix the identity for this repository (add --global for all of them).
  2. git config user.email ada@example.com
    Hosts link commits to your account by this email.
  3. git commit --amend --reset-author --no-editRewrites local history
    Rewrite the last commit with the corrected author.

Fixed when: The last commit's author is Ada Lovelace <ada@example.com>.

Otherwise: Many commits? git rebase --exec "git commit --amend --no-edit --reset-author" <base> rewrites each one. A .mailmap file changes how names display, without rewriting anything.

Commands: git commit --amend, git config

Branches and HEAD

I committed to main instead of a new feature branch

Rewrites local history

When: The commit is not pushed.

  1. git branch feature/login
    Create the branch at the commit you just made, so it keeps the work.
  2. git reset --keep HEAD~1Rewrites local history
    Move main back one commit. --keep refuses if that would overwrite local changes.
  3. git switch feature/login
    Carry on where the commit now lives.

Fixed when: main is back at Add README; feature/login has Add login form, and you are on it.

Otherwise: Pushed main already? Leave main alone: create the branch, then revert the commit on main with git revert.

Commands: git branch, git reset, git switch

I committed to main but it belongs on a branch that already exists

Rewrites local history

When: The commit is not pushed.

  1. git switch feature/search
    Go to the branch the commit belongs on.
  2. git cherry-pick main
    Copy main's tip commit onto it (a new commit with the same changes).
  3. git switch main
    Back to main…
  4. git reset --keep HEAD~1Rewrites local history
    …and remove the commit from it.

Fixed when: feature/search ends with Add search; main is back at Add README.

Commands: git cherry-pick, git reset

I deleted a branch that had work on it

Safe

When: It happened recently (reflog entries last 90 days, or 30 for unreachable commits).

  1. git reflog
    Find the last commit you made or checked out on that branch; git branch -D also printed its id (was …).
  2. git branch feature/export HEAD@{1}
    Here the branch tip is HEAD@{1}, the commit before you switched away. Use the id from the reflog in general.

Fixed when: feature/export exists again, pointing at Add CSV export.

Otherwise: Deleted on the remote too? Restore it locally, then git push -u origin feature/export.

Commands: git reflog, git branch -D

I'm in 'detached HEAD' and made commits

Safe

When: You are still on the detached commits.

  1. git switch -c fix/attempt
    Give the commits a branch; nothing else changes.

Fixed when: fix/attempt holds Try a fix, and HEAD is attached to it.

Otherwise: Already switched away? git warned you with the commit's id; or run git branch fix/attempt HEAD@{1}.

Lost work

I ran git reset --hard and lost commits

Rewrites local history

When: The commits were committed (uncommitted edits cannot come back).

  1. git reflog
    Every place HEAD has been. The line before the reset is the commit you lost.
  2. git reset --hard HEAD@{1}Discards work
    Go back to it. (Or git branch rescued HEAD@{1} to keep both.)

Fixed when: main points at Add payments again.

Commands: git reflog, git reset --hard

I dropped a stash I still needed

Safe

When: You still have the id git printed (Dropped refs/stash@{0} (…)).

  1. git stash apply f3b40664f915ae6eedaa6741a6a231045a6e2a9e
    A stash is a commit; the id from the Dropped line still reaches it until gc prunes it (two weeks by default).

Fixed when: app.js has the work-in-progress line back.

Otherwise: Lost the id? git fsck --unreachable | grep commit lists candidates; git show <id> to find the right one.

Commands: git stash, git fsck

I deleted a file and committed the deletion

Safe

When: Any time: the file is still in history.

  1. git restore --source=HEAD~1 --staged --worktree README.mdDiscards work
    Take the file from the commit before the deletion, into the index and the working tree.
  2. git commit -m "Restore README"
    Record it again.

Fixed when: README.md is back in the working tree and in the latest commit.

Otherwise: Deleted but not committed? git restore README.md is enough.

Commands: git restore, git log

Merges and rebases

I merged the wrong branch (not pushed yet)

Rewrites local history

When: The merge is the last thing you did and is not pushed.

  1. git reset --hard ORIG_HEADDiscards work
    ORIG_HEAD is where main was before the merge. The tree is clean after a merge, so --hard discards nothing.

Fixed when: main is back at Update app, without the merge; feature/beta is untouched.

Otherwise: Pushed? Use git revert -m 1 on the merge commit (next recipe).

Commands: git reset --hard, git merge

I merged and pushed a branch that broke main

Safe

When: The merge is on the shared main.

  1. git revert -m 1 --no-edit HEAD
    A new commit that undoes the merge's changes. -m 1 means "relative to main", the first parent.
  2. git push
    Everyone gets the undo; no history is rewritten.

Fixed when: origin's main ends with a revert commit, and beta.js is gone from the working tree.

Otherwise: To merge the branch again later, revert the revert first: git merges nothing otherwise, because its commits are already in main's history.

Commands: git revert

My merge has conflicts and I want to stop

Discards work

When: The merge is still in progress (git status says You have unmerged paths).

  1. git merge --abortDiscards work
    Put everything back as it was before git merge. Resolutions you made are lost.

Fixed when: No merge in progress, no conflicts, and app.js is main's version.

My rebase finished but the result is wrong

Rewrites local history

When: Right after the rebase (ORIG_HEAD), or any time via the branch's reflog.

  1. git reset --hard ORIG_HEADDiscards work
    ORIG_HEAD is the branch tip before the rebase. Later, use git reflog show feature/profile and reset to feature/profile@{1}.

Fixed when: feature/profile's commit sits on Add README again, as before the rebase.

My rebase stopped with a conflict and I want to give up

Discards work

When: The rebase is in progress (git status says You are currently rebasing).

  1. git rebase --abortDiscards work
    Return the branch and working tree to where they were before git rebase.

Fixed when: No rebase in progress; feature/theme is back on its own commit, unchanged.

Files and staging

I want to throw away my changes to one file

Discards work

When: You are sure: the changes were never committed or stashed, so nothing brings them back.

  1. git restore app.jsDiscards work
    Overwrite the file with its staged (or committed) version.

Fixed when: app.js matches the last commit again.

Otherwise: Not sure? git stash keeps the change where git stash pop can bring it back.

Commands: git restore, git stash

I staged a file I didn't mean to

Safe

When: Before committing.

  1. git restore --staged notes.txt
    Take it out of the index; the file and your other staged changes are untouched.

Fixed when: notes.txt is untracked again (still on disk), and app.js is still staged.

Otherwise: Already committed it? git rm --cached notes.txt, add it to .gitignore, and commit (or amend).

Remotes and pushing

I need to undo a commit I already pushed

Safe

When: The commit is on a shared branch.

  1. git revert --no-edit HEAD
    A new commit that reverses the bad one (use its id when it is not the last commit).
  2. git push
    Publish the undo. History is not rewritten, so nobody else has to do anything.

Fixed when: origin's main ends with Revert "Break the build", and app.js is back to its previous content.

Commands: git revert, git push

git push was rejected: "Updates were rejected because the remote contains work that you do not have"

Rewrites local history

When: Someone pushed to the branch since you last pulled.

  1. git pull --rebaseRewrites local history
    Fetch their commit and replay yours on top of it (your unpushed commit gets a new id).
  2. git push
    Now it is a fast-forward.

Fixed when: origin's main is Move API to /v2 followed by your Add cache flag — nobody's commit lost.

Otherwise: Never answer this rejection with git push --force: that removes the teammate's commit from the remote.

git pull says "Need to specify how to reconcile divergent branches"

Rewrites local history

When: You and the remote both have new commits on the branch.

  1. git config pull.rebase true
    Choose rebase for this repository (add --global for all). Choose pull.rebase false for merge commits, or pull.ff only to refuse.
  2. git pull
    Now pull knows what to do: replay your commit on the remote's.

Fixed when: main is the teammate's commit followed by yours, with no merge commit.

git pull made a merge commit I didn't want

Rewrites local history

When: The merge is not pushed.

  1. git reset --hard ORIG_HEADDiscards work
    Undo the merge the pull just made (the tree is clean, so nothing is discarded).
  2. git pull --rebaseRewrites local history
    Integrate again by replaying your commit on top instead.

Fixed when: main has no merge commit: the teammate's commit, then yours.

Someone force-pushed the branch I'm working on

Rewrites local history

When: You have local commits on top of the old version of the branch.

  1. git pull --rebaseRewrites local history
    git finds where your work forked from the old branch (from the remote-tracking reflog) and replays only your commits onto the new one.

Fixed when: feature/api is the rewritten Add API (reviewed) followed by your Add API docs; the old Add API commit is gone from the branch.

Otherwise: No local commits on the branch? git reset --hard origin/feature/api simply takes the new version.

Secrets

I committed a secret (.env, a key) and haven't pushed

Rewrites local history

When: The commit is only local.

  1. git rm --cached .env
    Stop tracking the file; it stays on disk.
  2. echo .env >> .gitignore
    Keep it from being added again.
  3. git add .gitignore
    Stage the ignore rule.
  4. git commit --amend --no-editRewrites local history
    Rewrite the commit without the secret.

Fixed when: No commit on any branch contains .env; the file is still on disk and now ignored.

Otherwise: The old commit stays in your local reflog until it expires. It was never pushed, so nobody else has it; if the secret was ever pushed, use the next recipe.

I pushed a secret

Rewrites shared history

When: The commit reached a remote. Treat the secret as public.

  1. git rm --cached .env
    First, outside git: revoke and rotate the secret with its provider. Then stop tracking the file.
  2. echo .env >> .gitignore
    Ignore it from now on.
  3. git add .gitignore
    Stage the rule.
  4. git commit -m "Stop tracking .env"
    This removes .env from the latest commit only.
  5. git push
    The earlier commit — and the secret — is still in origin's history. Removing it needs git filter-repo and a force push (below).

Fixed when: The latest commit no longer has .env, but origin's history still contains it — the reason rotating the secret comes first.

Otherwise: To purge history (outside this page's engine): in a fresh clone run git filter-repo --invert-paths --path .env, then git push --force --all and --tags; ask everyone to re-clone; ask the host to purge cached views and pull request refs (GitHub's guide: Removing sensitive data from a repository). Secret scanning with push protection stops the next one.

Commands: git filter-repo, git rm, git log -S
Reference

Git Model

What a repository is made of — objects, ids, refs and HEAD, the three trees, and what the reflog keeps — on one screen, with an inspector that computes real object ids as you type.

  • A repository is a store of four kinds of object — blobs (file contents), trees (directories), commits and annotated tags — each named by a hash of its content.
  • A commit records a whole snapshot (one tree), its parent commits, who made it and when. Diffs are computed when you ask for them.
  • A branch is a file holding one commit id. Committing moves it. HEAD names the branch you are on (or, detached, a commit).
  • Your changes move between three places: the working tree, the index (staging area) and HEAD. Every add, commit, restore and reset is a copy between two of them.
  • Nothing that was committed is lost at once: the reflog remembers where refs pointed for 90 days, and unreachable objects are deleted only after two weeks.

Snapshots, not diffs

Each commit points at a tree describing every file in the project at that moment. Files that did not change are not stored again: the new tree refers to the same blob id as the old one, because the id depends only on the content. git diff, git show and merges compute differences between snapshots when they need them Chacon 2014.

Renames are not recorded either: git detects them by comparing contents (a file that disappears and a similar one that appears), which is why git log --follow and git diff -M can find them and why a rename with heavy edits may show as a delete and an add.

The four objects, and the inspector

Type below. The blob id is the SHA-1 of the header blob <size>\0 followed by the content, so one changed character gives a different id — and a different tree id, and a different commit id.

Author and committer: Ada Lovelace <ada@example.com>, time 1767225600 +0000 (2026-01-01 00:00 UTC). With the defaults, these are the ids git 2.55.0 prints for the same input.

blob82359009487218af7a33dba25d3522d1bf5755fb
# Hello, git

The file's content only — no name, no date. Two files with the same content are one blob.

tree1dd1b4e8b1e2b5771bc74d1231a7ede1460f1af7
100644 blob 82359009487218af7a33dba25d3522d1bf5755fb	README.md

A directory: each entry is a mode, a type, the id of a blob or subtree, and a name.

commit870a871ceb0fdfdc7300b2492d6a6fcf45c2b30e
tree 1dd1b4e8b1e2b5771bc74d1231a7ede1460f1af7
author Ada Lovelace <ada@example.com> 1767225600 +0000
committer Ada Lovelace <ada@example.com> 1767225600 +0000

First commit

A snapshot: the id of the top tree, the parents (none for a first commit), who and when, and the message.

The object types
ObjectHoldsPoints toMade by
blobA file's bytes (no name, no mode)Nothinggit add, git hash-object -w
treeOne directory: mode, type, id and name per entryBlobs and other treesgit commit (via git write-tree)
commitAuthor, committer, dates, messageOne tree and zero or more parent commitsgit commit, merge, rebase, cherry-pick…
tag (annotated)Tagger, date, message, optional signatureOne object, usually a commitgit tag -a

Object ids

Ids are 40 hexadecimal characters of SHA-1. Git has hardened its SHA-1 against the known collision attacks, and supports SHA-256 repositories with git init --object-format=sha256; at present a SHA-256 repository cannot exchange objects with a SHA-1 one git-init. The Git project plans to make SHA-256 the default for new repositories in Git 3.0, along with the reftable ref format and main as the initial branch name Git: breaking changes.

A short id such as 1dd1b4e is any unambiguous prefix; git prints 7 characters or more as the repository grows.

Refs and HEAD

Refs
RefExampleWhat it isMoves when
Branchrefs/heads/mainA name for one commitYou commit, merge, reset or rebase on it
HEADref: refs/heads/mainWhich branch you are on — or, detached, a commit idYou switch, or check out a commit
Remote-tracking branchrefs/remotes/origin/mainWhere main was on origin when you last fetchedgit fetch, pull or push
Tagrefs/tags/v1.0.0A fixed name for a releaseOnly if forced (-f)
Stashrefs/stashThe newest stash entry; older ones are its refloggit stash push, pop, drop
ORIG_HEADORIG_HEADHEAD before the last reset, merge or rebaseThose commands

Refs live as files under .git/refs/, packed into .git/packed-refs, or in the newer reftable format (git init --ref-format=reftable, Git 2.45+) gitrepository-layout Git 2.45.

The three trees

Every file has up to three versions: in HEAD (the last commit), in the index (what the next commit will contain) and in the working tree (on disk). Commands copy between them.

Which command moves content where
CommandFromToHEAD moves?
git add <path>working treeindexNo
git commitindexa new commitYes: the branch moves to it
git restore <path>indexworking treeNo
git restore --staged <path>HEADindexNo
git restore --source=<c> -SW <path>commit <c>index and working treeNo
git reset --soft <c>——Yes, to <c>; index and files unchanged
git reset [--mixed] <c>commit <c>indexYes, to <c>
git reset --hard <c>commit <c>index and working treeYes, to <c>
git switch <branch><branch>'s commitindex and working tree (local changes carried)Yes: HEAD names <branch>
git stashindex and working treea stash commit; both reset to HEADNo

Stage 4 has the three-trees tool, which shows each file's three versions as you run these commands.

Reachability, the reflog and garbage collection

A commit is reachable if a ref, or a commit reachable from one, points at it. git log, clone and push only see reachable commits. After a reset, amend or rebase, the old commits are unreachable from branches — but not gone:

  • The reflog records every value HEAD and each branch has had. git reflog lists them; HEAD@{2} and main@{1} name them. Entries expire after 90 days (gc.reflogExpire), or 30 days for commits no longer on the branch (gc.reflogExpireUnreachable) git-config: gc.*.
  • Objects no ref or reflog reaches are deleted by git gc once they are older than gc.pruneExpire, two weeks by default. git fsck --lost-found finds them before that.
  • Reflogs are local. They are not pushed, fetched or cloned — your teammate's reflog cannot rescue your commit.

Uncommitted changes are the exception: a working-tree edit that was never staged or stashed is in no object, and reset --hard, restore or checkout -- <path> destroy it for good. The terminal's dry run warns about exactly this.

Distributed, not centralised

Every clone is a complete repository: all commits, all history, its own branches. A remote such as origin is another repository your clone knows the URL of; fetch and push copy objects and move refs between the two. Centralised systems (Subversion, Perforce) keep history on a server and give each client one checkout; there, committing means sending to the server, while in git it is a local operation you publish later.

Sources

Interactive Feature

Branch Strategy Comparison

Click any strategy to see the full branching rules, workflow, and commands.

Interactive Feature

Follow the Code

One feature. Eight milestones. From idea to production — every command, every artifact.

◉

Issue Created

Planning
1 / 8

A bug or feature is captured in the backlog with acceptance criteria and priority.

Key Commands

$ gh issue create --title 'Add Apple Pay to checkout'
$ gh issue view 847

Artifacts

◆JIRA ticket
◆Acceptance criteria
◆Priority label
Reference

Lab

A live repository, full width. Pick a starting repository, type git commands, and watch the graph change. Undo and redo move between states; the session exports as a shell script you can run against real git, and a link replays it.

In progress

In progress

vedantsen.com/version-control14 stages · 6 reference tools