Git
11 min read
Git is the version control system (VCS) designed and developed by Linus Torvalds, the creator of the Linux kernel. Git is now used to maintain AUR packages, as well as many other projects, including sources for the Linux kernel. There is a commit naming scheme called gitmoji and Conventional Commits.
To control a git repo graphically GitHub Desktop can be used.
Configuration
Git can be configured with a .gitconfig file or via the command line. In order to use Git you need to set at least a name and email:
shell
git config --global user.name "John Doe"
git config --global user.email "johndoe@example.com"
Usage
A Git repository is contained in a .git directory, which holds the revision history and other metadata. The directory tracked by the repository, by default the parent directory, is called the working directory. Changes in the working tree need to be staged before they can be recorded (committed) to the repository. Git also lets you restore, previously committed, working tree files.
Git repository
Initialize a repository
shell
git init
git init --bare
git init -b main
Clone an existing repository
shell
git clone repository
git clone repository folder
git clone --recursive repository
git clone --bare repository
git clone --sparse repository
git clone -b master repository
See status of tracked and untracked files:
shell
git status
Rename a file or folder:
shell
git mv old new
See Commits:
shell
git log
git log --graph --oneline --decorate
# Graph of all branches
git log --graph --all
# Extended graph
git log --graph
Commits
See how a file come together over commits and authors:
shell
git blame file
# Ignore code movements
git blame -C -C -C file
Add files to version control:
shell
git add file
# Add everything
git add -A
Commit changes:
shell
git commit -m "message"
# Signed commit
git commit -s -m "message"
# Fix last commit with new changes
git commit --amend
See commit changes:
shell
git show commit
# See only changed filenames or status
git show --name-only commit
git show --name-status commit
# Show commit statistics
git show --stat commit
# Show patch of commit
git show -p commit
git show --patch commit
Revert changes:
shell
git revert <commit>
# Signed revert commit
git revert -s <commit>
# Revert to the fourth last commit from HEAD
git revert HEAD~3
Bring back old revisions of files:
shell
git restore file
# Restore state before two commits from master
git restore --source master~2 Makefile
Commit Signing
Commit signing is a way to verify the identity of the person who made a Git commit by attaching a digital signature. This helps ensure that commits are authentic and haven’t been tampered with.
Configuring Commit Signing with PGP (GPG) via .gitconfig:
ini
[user]
signingkey = YOUR_KEY_ID
[commit]
gpgsign = true
• Replace YOUR_KEY_ID with your GPG key ID (found via gpg --list-secret-keys --keyid-format LONG).
Configuring Commit Signing with SSH via .gitconfig:
ini
[gpg]
format = ssh
[user]
signingkey = ~/.ssh/id_ed25519.pub
[commit]
gpgsign = true
Make sure your SSH public key path is correct (~/.ssh/id_ed25519.pub or your actual key file).
This config ensures all commits are automatically signed using your chosen key.
Stash
git stash is used to temporarily shelve (or "stash") changes in your working directory that are not yet ready to be committed. This is especially helpful when you need to switch branches quickly but want to keep your current work intact and return to it later.
bash
git stash
This will:
• Save your tracked (i.e., staged and unstaged) changes
• Revert your working directory to match the HEAD commit
Note: Untracked and ignored files are not stashed by default.
bash
git stash apply
• Re-applies the most recent stash to your working directory.
• Keeps the stash in the stash list (does not remove it).
To apply a specific stash:
bash
git stash apply stash@{2}
bash
git stash pop
• Re-applies the most recent stash and removes it from the stash list.
To view a list of all stashed changes:
bash
git stash list
Example output:
stash@{0}: WIP on feature/login: abc1234 Add login page
stash@{1}: WIP on main: def5678 Fix typo
You can provide a custom message to identify your stash:
bash
git stash push -m "WIP: Update footer styles"
To include untracked files:
bash
git stash push -u
To include ignored files as well:
bash
git stash push -a
To delete a specific stash:
bash
git stash drop stash@{1}
To clear all stashes:
bash
git stash clear
Branching
Fixes and new features are usually tested in branches. When changes are satisfactory they can merged back into the default (master) branch.
Create a branch, whose name accurately reflects its purpose:
shell
git branch help-section-addition
List branches:
shell
git branch
git branch --all
git branch --remotes
Switch branches:
shell
git checkout <ref>
# Switch to ref and create new branch
git checkout -B <branch> <ref>
Merge a branch back to the master branch:
shell
git checkout master
git merge branch
The changes will be merged if they do not conflict. Otherwise, Git will print an error message, and annotate files in the working tree to record the conflicts. The annotations can be displayed with git diff. Conflicts are resolved by editing the files to remove the annotations, and committing the final version.
Any conflicts will look something like this:
Here are lines that are either unchanged from the common
ancestor, or cleanly resolved because only one side changed,
or cleanly resolved because both sides changed the same way.
<<<<<<< yours:sample.txt
Conflict resolution is hard;
let's go shopping.
=======
Git makes conflict resolution easy.
>>>>>>> theirs:sample.txt
And here is another line that is cleanly resolved or unmodified.
Continue the merge after resolving merge conflicts:
shell
git merge --continue
When done with a branch, delete it with:
shell
git branch -d branch
# Also delete on remote
git push -d <remote_name> <branchname>
Worktrees
Git worktrees allow you to have multiple working directories (checkouts) of the same repository at once.
They’re perfect when you need to:
• Work on multiple branches simultaneously
• Test or build a branch without switching your main checkout
• Keep temporary workspaces for experimentation
A worktree is essentially a linked working directory that shares the same .git repository data. Instead of cloning the repo again, Git just references the existing object store.
Each worktree has its own:
• Working directory
• Checked-out branch (or commit)
• Index (staging area)
But all share the same .git data in the main repo.
shell
# Create a New Worktree
git worktree add ../feature-x feature-x
git worktree add <path> <branch>
# Add a worktree AND create a new branch from main
git worktree add -b feature/new-thing ../my-repo-feature main
# Prune stale worktree refs (if you deleted the dir manually)
git worktree prune
# List All Worktrees
git worktree list
# Remove a Worktree
git worktree remove ../feature-x
Tip: Bare Repositories can conveniently have multiple worktrees under their them.
Remotes
Working with remotes:
shell
git remote [-v | --verbose]
git remote add [-t <branch>] [-m <master>] [-f] <name> <URL>
git remote rename <old> <new>
git remote remove <name>
git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
git remote set-branches [--add] <name> <branch>...
git remote get-url [--push] [--all] <name>
git remote set-url [--push] <name> <newurl> [<oldurl>]
git remote set-url --add [--push] <name> <newurl>
git remote set-url --delete [--push] <name> <URL>
git remote [-v | --verbose] show [-n] <name>...
git remote prune [-n | --dry-run] <name>...
git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]
Pull from a reposity:
shell
git pull
# Rebase instead of merge (append your commit if remote is newer)
git pull --rebase
Push to a repository:
shell
git push
git push --force
git push remote branch
Tagging
Tag commits for versioning:
shell
git tag 2.14 <commit>
# Signed Tag
git tag -s 2.14
# Tag with message
git tag -a 2.14 -m "Version 2.14"
List tags:
shell
git tag -l
Delete a tag:
shell
git tag -d 2.08
Update remote tags:
shell
git push --tags
Notes
Git notes (git notes) let you attach extra information to an existing object (usually a commit) without rewriting it. Because a commit's hash is computed from its content, you normally cannot add anything to a commit after the fact without changing its ID. Notes solve this by storing the additional text separately and binding it to the object, so the commit stays untouched while extra metadata travels alongside it.
This is useful for adding things like review comments, bug-tracker references, build results, or release annotations to commits long after they were made. Notes are stored in their own ref (by default refs/notes/commits) rather than as part of the commit, and git log will display them automatically beneath the commit they belong to.
Add or edit a note on a commit:
shell
# Open an editor to write the note for the given commit (defaults to HEAD)
git notes add <commit>
# Provide the note message inline
git notes add -m "Reviewed by Alice" <commit>
# Append to an existing note instead of failing
git notes append -m "Also passed CI" <commit>
# Overwrite an existing note
git notes add -f -m "new message" <commit>
Show and list notes:
shell
# Notes are shown automatically in the log
git log
# Show the note for a specific object
git notes show <commit>
# List all noted objects
git notes list
Remove a note:
shell
git notes remove <commit>
You can keep multiple independent sets of notes by using different refs (notes "namespaces"):
shell
# Work in a custom notes ref instead of the default refs/notes/commits
git notes --ref=builds add -m "build #42 OK" <commit>
git config notes.displayRef "refs/notes/*" # show all notes refs in the log
Notes are not pushed or fetched by default. To share them you must explicitly transfer their ref:
shell
git push origin refs/notes/commits
git fetch origin refs/notes/commits:refs/notes/commits
Patches & Diffs
Show differences:
shell
# Show unstaged changes
git diff
# Show changes since last commit
git diff --staged
# Output a summary of file creations, renames and mode changes since a given commit:
git diff --summary commit
# Output a difference by word instead of line
git diff --word-diff
# Output a statistic of changes
git diff --stat commit
# Compare a single file between two branches or commits:
git diff branch_1..branch_2 [--] path/to/file
# Compare changes between commits
git range-diff <commit> <commit>
Generate a patch file:
shell
git format-patch <revision range>
Apply patch files to repository:
shell
git apply patch
# Revert patch
git apply --reverse patch
# Another way to apply patches
git am < patch
git am --signoff < patch
git am --continue < patch
git am --abort < patch
.gitignore
A .gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected. This file contains pattern on each line which exclude files from git versioning.
Git Hooks
Git hooks are custom scripts that run automatically in response to certain Git events or actions. These hooks are useful for automating tasks like code quality checks, running tests, enforcing commit message conventions, and more. Git hooks can be executed at different points in the Git workflow, such as before or after a commit, push, or merge.
Git hooks are stored in the .git/hooks directory of your repository. By default, this directory contains example scripts with the .sample extension. You can customize these scripts by removing the .sample extension and editing them as needed.
Hooks only apply to your local repository. If a hook script fails it prevents the associated action as well.
Common Git Hooks
• pre-commit
• prepare-commit-msg
• commit-msg
• post-commit
• post-checkout
• pre-rebase
Extending
Git can be extended via custom commands. For every unrecognized command git will try to call a binary in $PATH with the naming scheme git-<command>. So, for example, if you want to have a command for git customized, create a binary (or shell script) named git-customized with your logic and place it in $PATH.
One of such extensions is git-extras.