Skip to content

Latest commit

 

History

History
380 lines (281 loc) · 8.02 KB

File metadata and controls

380 lines (281 loc) · 8.02 KB

git

Quick-reference cheat-sheet. Validated against git 2.54.0.

Modern commands (switch, restore) are preferred over the older checkout for branch/file operations — since git 2.23 they split checkout's two jobs apart, and git 2.54 updated its own advice messages to point at them.

Sources


1. The 25 commands you actually need

# Get a repo
git clone <url>
git remote add upstream <url>

# Inspect
git status
git log
git diff
git reflog

# Stage + commit
git add <path>            # or  git add .
git commit -m "msg"
git commit -am "msg"      # stage tracked + commit
git commit --amend        # fix last commit

# Sync
git fetch <remote>
git pull --ff-only
git push
git push -u origin <branch>

# Branches
git switch <branch>
git switch -c <branch>    # create + switch
git branch -D <branch>

# Rewrite / integrate
git rebase <base>
git rebase -i HEAD~<N>
git cherry-pick <hash>

# Undo / rescue
git restore <path>            # discard unstaged changes to file
git restore --staged <path>   # unstage
git reset --hard <ref>
git stash / git stash pop / git stash list

# Debug
git bisect

Terminology:

  • origin = your fork (or the repo you cloned)
  • upstream = the canonical repo you track
  • main = the primary branch (was master)

2. Merge vs rebase

merge — record upstream changes into your fork as a merge commit. Adds a noisy commit; prefer rebase for personal branches.

rebase — reapply your commits on top of an updated base. Linear history.

git remote add upstream <url>
git fetch upstream
git switch main
git rebase upstream/main
git switch working
git rebase main

3. Keep a branch in sync with upstream

One canonical recipe — works for any fork (GitHub PR flow, FreeBSD ports, BSDRP, etc.):

git switch main
git pull --rebase upstream main   # fetch + rebase in one step
git push origin main              # update your fork's main

git switch <working-branch>
git rebase main
git push --force-with-lease       # safer than -f: refuses if remote moved

Use --force-with-lease instead of -f — it aborts the push if someone else pushed in the meantime.


4. Diff recipes

git diff                          # unstaged only
git diff --staged                 # staged only
git diff HEAD                     # staged + unstaged  <- use this one
git diff HEAD^ HEAD               # last commit
git diff <hash>^!                 # a specific commit
git diff origin/main origin/feat  # between two branches

Checking a patch is complete before sending it

git diff alone shows only tracked, unstaged changes. It silently omits staged changes and never shows new files, so it can under-report what your patch really is. Two commands, always:

git diff HEAD                     # everything tracked, staged or not
git status --short                # catch ?? files that belong in the patch

Reading git status --short — two columns, staged then unstaged:

 M path    # modified, nothing staged   (git diff shows it)
M  path    # modified and staged        (git diff shows NOTHING)
MM path    # staged, then modified again
?? path    # untracked: git diff never shows this

So a repo with scratch files in its root can look clean to git diff while arc diff or git format-patch pick up something unintended. Keep junk out of the tree, or list it in .git/info/exclude (local-only, unlike .gitignore).

Save a reviewable patch and prove it applies to a pristine tree:

git diff HEAD > /tmp/fix.diff
git stash && git apply --check /tmp/fix.diff && git stash pop

5. Patches (format-patch / am)

Create:

git format-patch HEAD^!           # last commit
git format-patch <hash>^!         # specific commit
git format-patch -N               # last N commits

Apply (preserves author + date):

git am file.patch
git commit --amend                # only if you need to edit the message

6. Squash last N commits

git log --oneline -N              # confirm what you're squashing
git rebase -i HEAD~N

In the editor, keep pick on the first line, change the rest to s (squash). Save; edit the combined message; save.

git push --force-with-lease origin <branch>

7. Undo

Discard unstaged changes in one file:

git restore <path>

Discard all unstaged changes:

git restore .

Unstage a file (keep the edits):

git restore --staged <path>

Undo the last local commit, keep changes staged:

git reset --soft HEAD~1

Undo the last local commit, discard changes:

git reset --hard HEAD~1

Reset a local branch to match its upstream (@{u} = upstream shorthand):

git reset --hard @{u}

Revert a bad rebase (still known via reflog):

git reflog
git reset --hard HEAD@{N}

Force-overwrite a pushed working branch (never on main):

git reset --hard <good-hash>
git push --force-with-lease

8. Branches

Create a branch from a tag:

git switch -c newbranch tags/v1.0

Copy one file from another branch into the current one:

git restore --source=<other-branch> path/to/file

Delete a branch, locally and remotely:

git branch -d <branch>            # -D to force
git push origin -d <branch>

Tag a release:

git tag -a v1.992 -m "Release v1.992"
git push origin v1.992

9. Cherry-pick from another fork

git remote add someone <their-url>
git fetch someone
git switch <target-branch>
git cherry-pick <hash>
git push

10. Inspect history

When was a branch created off main?

git show --summary "$(git merge-base <branch> main)"

When was a file deleted?

git log --all -1 -- path/to/file

Which patchlevel is /usr/src at?

git -C /usr/src rev-list --count --first-parent HEAD

11. Bundle (offline repo transfer)

git bundle create repo.bundle --all
git clone repo.bundle <newrepo>

12. Commit a patch written by someone else

Git records two identities per commit:

  • Author — who wrote the change.
  • Committer — who applied it. Always you, taken from user.name / user.email.

git log prints only the author. Use git log --format=fuller to see both.

Plain diff (patch file, pasted hunk, email body)

git apply patch.diff        # or: patch -p1 < patch.diff
git add -A
git commit --author="Jane Doe <jane@example.org>" -m "msg"

If the author is already in the repo history, a search term is enough — git resolves it against existing commits:

git commit --author="jane" -m "msg"

Patch made with git format-patch

Prefer git am: it takes the author, date, and commit message from the mail headers, so no --author flag is needed.

git am patch.mbox
git am --3way patch.mbox    # fall back to 3-way merge if it does not apply

Fix the author after committing

git commit --amend --author="Jane Doe <jane@example.org>" --no-edit

Reset the author date too

--author sets the name and email only. The author date stays "now". To take the original date as well:

git commit --author="Jane Doe <jane@example.org>" \
           --date="2026-09-14T10:32:00+02:00" -m "msg"

Co-author instead

For a shared change, keep yourself as author and add a trailer. GitHub and GitLab render it as a second contributor.

git commit -m "msg

Co-authored-by: Jane Doe <jane@example.org>"

13. Fork a repo (GitHub-style)

Real example — forking both FreeBSD ports and src. Fork each via the GitHub web UI first, then:

# freebsd-ports
git clone git@github.com:<you>/freebsd-ports.git
cd freebsd-ports
git remote add upstream git@github.com:freebsd/freebsd-ports
cd ..

# freebsd-src
git clone git@github.com:<you>/freebsd-src.git
cd freebsd-src
git remote add upstream git@github.com:freebsd/freebsd-src

After this, each clone has two remotes:

  • origin → your GitHub fork (where git push lands)
  • upstream → the official FreeBSD repo (fetch-only in practice)

Keep either fork current — see section 3.