Version Control with Git
This document covers the Git Version Control System.
Git is a distributed Version Control System (VCS). In a DVCS (such as Git, Mercurial or Bazaar), clients don’t just check out the latest snapshot of the files; rather, they fully mirror the repository, including its full history. Thus, if any server dies, and these systems were collaborating via that server, any of the client repositories can be copied back up to the server to restore it. Every clone is really a full backup of all the data.
Installing Git
Git can be installed in the following way, on the following systems.
Using a package manager
Arch Linux
Git on Arch Linux or derivative distributions (such as Manjaro, Arco, Endeavour, etc) can be installed simply via pacman.
sudo pacman -S git
Debian/Ubuntu
Git on Debian or it's derivative distributions (such as Ubuntu, Mint, Zorin, etc) can be installed via apt
sudo apt install git-all
RHEL/Fedora
Git on Red Hat Enterprise Linux or Fedora & it's derivatives (Rocky, CentOS 7, Oracle Linux, etc) can be installed via dnf
sudo dnf install git-all
Gentoo
Git on Gentoo and derivatives (Sabayon, Funtoo, Redcore Linux) can be installed via the portage package manager
emerge --ask dev-vcs/git
MacOS X
Git on apple MacOS X can be installed via homebrew.
brew install git
or you can also use MacPorts
sudo port install git
Installing from Source Code
Some people may instead find it useful to install Git from source, because you’ll get the most recent version. If you do want to install Git from source, you need to have the following libraries that Git depends on: autotools, curl, zlib, openssl, expat, and libiconv.
On Fedora/RHEL based systems, you can use DNF to install them
sudo dnf install dh-autoreconf curl-devel expat-devel gettext-devel openssl-devel perl-devel zlib-devel
On Debian/Ubuntu based systems, you can use APT,
sudo apt install dh-autoreconf libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
On Arch based systems, you can use pacman to install the dependencies
sudo pacman -Sy curl expat perl perl-error perl-mailtools openssl pcre2 grep shadow zlib
When you have all the necessary dependencies, you can go ahead and grab the latest tagged release tarball from the mirror on the GitHub website, at https://github.com/git/git/releases.
You can then compile & install:
$ tar -zxf git-2.8.0.tar.gz
$ cd git-2.8.0
$ make configure
$ ./configure --prefix=/usr
$ make all doc info
$ sudo make install install-doc install-html install-info
Setting up Git
Before using git, the first thing that needs to be done is to set up your username and email address. This is important because every Git commit uses this information, and it’s immutably baked into the commits you start creating. You can set these global configuration variables like this:
git config --global user.name "DemonKiller"
git config --global user.email demonkiller@example.com
You can check you Git global configuration values with
git config --list
Getting help with Git (man pages)
You can launch Git command man pages with --help flag. The syntax is
git <command> --help
For example, if you want help with the git add command, you can type
git add --help
Git Usecases
There are two common scenarios where you would want to use Git as a VCS.
- Start tracking existing local project with Git
- Contribute/develop a remote existing project
Track a local project
Start tracking project with git
Tracking a local project is extremely simple. You just have to initialise git in the project folder. Navigate to the project folder, and open a terminal in that folder. You can start tracking with
git init
Running this will create a .git folder in that directory. Any file or folder that starts with a dot/period is hidden. You can access this folder by pressing Control + H in most major file managers like Dolphin (KDE Plasma), Nautilus (GNOME), Thunar (XFCE), Caja (MATE), etc. In the command line, you can see this file using the -a (all) flag.
ls -la
Stop tracking project
To stop tracking the said folder or project, simply remove the .git folder.
rm -rvf .git
The -r flag is compulsary as it will recursively delete contents of the .git folder.
The -v flag is optional, yet recommended. It spits out verbose output on what is being deleted.
The -f flag is optional, yet recommended too. It forces deletion of any nonexistent files and arguments.
Know status of project files
In order to know status of project files, we can use
git status
Ignore files
Usually, there are files that we want to ignore in the project. These could be personal config files for example. We can create a gitingore file and list such files and folders in this file. The gitignore is a simple text file. This file is hidden and starts with a dot/period.
*.conf
test.py
myfolder
You can check the status of files that are stated in gitignore with the the status command.
Recording changes to the repository
Each file in your working directory can be in one of two states: tracked or untracked. Tracked files are files that were in the last snapshot, as well as any newly staged files; they can be unmodified, modified, or staged. In short, tracked files are files that Git knows about.
Untracked files are everything else — any files in your working directory that were not in your last snapshot and are not in your staging area. When you first clone a repository, all of your files will be tracked and unmodified because Git just checked them out and you haven’t edited anything.
As you edit files, Git sees them as modified, because you’ve changed them since your last commit. As you work, you selectively stage these modified files and then commit all those staged changes, and the cycle repeats.
Add files to staging area
In order to begin tracking a new file, you use the command git add.
$ git add <filename>
$ git add .
$ git add -A
You can add individual files or you can add all the files in the said directory.
A dot or period means to add all the files in the folder/directory.
You can check the status of the files with Git Status.
Remove files from the staging area
To remove files from the staging area, you can use
git reset
Again, you can check status of the file with Git Status.
Commit Files
You can commit files with git commit.
git commit -m "Commit Message"
You can find information on past commits using
git log
Track a remote project
A remote project is a one that is hosted on some online code repository hosting provider like Github/Gitlab.
Clone a project repository
In order to work locally on a remote project, you need to clone it. A project can be cloned in the following ways
$ git clone <url> /dir/where/repo/will/be/cloned
$ git clone https://github.com/demonkillerr/example.git .
$ git clone /path/tp/directory/remote-repo.git .
The dot/period indicates that you want to clone the repository in the current directory you are in. If you have a specified, you need to specify it instead of the period. For ex
git clone https://github.com/demonkillerr/example.git ~/Documents
Viewing information regarding remote repository
You can view information regarding the remote repository, such as it's origins like this
git remote -v
Push changes to remote remote repository
In order to push changes to remote repository we need to
- Commit changes locally as discussed above.
- Then Push to remote
$ git add <filename>
$ git commit -m "Commit Message"
then
$ git pull origin master
$ git push origin master
Common Workflow
What we have discussed till now is the basic of how to use Git in the two most common scenarios. Let's take a look at common workflow most open source projects follow.
Working with Branches
Nearly every VCS has some form of branching support. Branching means you diverge from the main line of development and continue to do work without messing with that main line.
Creating and Switching Branches
In Git, you can create a branch like this
git branch <branch-name>
You can move to this branch from Master by
git checkout <branch-name>
Or create and switch to a new branch in one command:
git checkout -b <branch-name>
Modern Git (2.23+) introduced the switch command for clearer semantics:
# Create and switch to new branch
git switch -c <branch-name>
# Switch to existing branch
git switch <branch-name>
# Switch to previous branch
git switch -
Listing Branches
View all local branches:
git branch
View all branches (local and remote):
git branch -a
View remote branches only:
git branch -r
View branches with last commit information:
git branch -v
View merged branches:
git branch --merged
View branches not yet merged:
git branch --no-merged
Renaming Branches
Rename the current branch:
git branch -m <new-branch-name>
Rename a different branch:
git branch -m <old-branch-name> <new-branch-name>
Comparing Branches
See differences between branches:
# See all different commits
git log main..feature-branch
# See commits in feature-branch not in main
git log main..feature-branch --oneline
# See commits in both branches
git log main...feature-branch
# See file differences
git diff main..feature-branch
# See which files changed
git diff --name-only main..feature-branch
Pushing new branch to remote
Till now the new branch that is made is created locally. In order to push the branch to remote repository we can
git push -u origin <branch-name>
You can check the branches locally and remotely using
git branch -a
Merge Branch to Master
Once we are done with the development of a said feature on a said branch, we want to merge the changes of this branch with master. We can merge a branch with master like this
$ git checkout master #checkout to master branch
$ git pull origin master #pull in changes that might have been made in master
$ git branch --merged #show previously merged branches
$ git merge <branch-name> #merge branch
$ git push origin master #push to remote
The third step to see merged branches using --merged is optional, but recommended.
Delete merged branch
Once a branch is merged, it is not required, hence best deleted. You can delete a branch like this
$ git branch --merged #check if branch has been merged
$ git branch -d <branch-name> #delete branch locally
$ git branch -a #look for branch in remote
$ git push origin --delete <branch-name> #delete branch from remote repository
Force delete a branch (even if not merged):
git branch -D <branch-name>
Use -D carefully as it will delete the branch even if it contains unmerged changes.
Rebasing
Rebasing is an alternative to merging that rewrites commit history. Instead of creating a merge commit, rebase moves or combines a sequence of commits to a new base commit.
Basic Rebase
# While on feature branch
git rebase main
This replays your feature branch commits on top of the main branch.
Rebase vs Merge
Merge creates a merge commit and preserves history:
git checkout main
git merge feature-branch
Rebase creates a linear history:
git checkout feature-branch
git rebase main
git checkout main
git merge feature-branch # Fast-forward merge
When to use Rebase:
- To maintain a clean, linear project history
- Before merging your feature branch to main
- To incorporate upstream changes into your feature branch
When to use Merge:
- For public/shared branches
- When you want to preserve exact history
- For integrating completed features
Handling Rebase Conflicts
When conflicts occur during rebase:
# Fix conflicts in files, then
git add <conflicted-files>
git rebase --continue
# Or skip the conflicting commit
git rebase --skip
# Or abort the rebase
git rebase --abort
Interactive Rebase
Interactive rebase allows you to modify commits as they're being replayed. It's one of Git's most powerful features.
# Rebase last 5 commits
git rebase -i HEAD~5
# Rebase from a specific commit
git rebase -i <commit-hash>
# Rebase from the beginning of the branch
git rebase -i --root
When you run interactive rebase, Git opens an editor with a list of commits:
pick a1b2c3d Add feature X
pick e4f5g6h Fix typo
pick i7j8k9l Update documentation
pick m0n1o2p Refactor code
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# d, drop = remove commit
Common Interactive Rebase Operations:
Squash multiple commits:
pick a1b2c3d Add feature X
squash e4f5g6h Fix typo
squash i7j8k9l Update documentation
pick m0n1o2p Refactor code
Reword commit message:
pick a1b2c3d Add feature X
reword e4f5g6h Fix typo
Reorder commits:
pick i7j8k9l Update documentation
pick a1b2c3d Add feature X
pick e4f5g6h Fix typo
Edit a commit:
pick a1b2c3d Add feature X
edit e4f5g6h Fix typo
When rebase stops at an edit commit:
# Make your changes
git add <files>
git commit --amend
git rebase --continue
Split a commit:
edit a1b2c3d Large commit with multiple changes
Then:
git reset HEAD~
git add <file1>
git commit -m "First part"
git add <file2>
git commit -m "Second part"
git rebase --continue
Autosquash
Git can automatically organize fixup commits:
# Create a fixup commit for a previous commit
git commit --fixup <commit-hash>
# When rebasing, automatically squash fixup commits
git rebase -i --autosquash HEAD~10
Advanced Cherry-Picking
Cherry-picking allows you to apply specific commits from one branch to another.
Basic Cherry-Pick
# Apply a single commit
git cherry-pick <commit-hash>
# Apply multiple commits
git cherry-pick <commit1> <commit2> <commit3>
# Apply a range of commits
git cherry-pick <start-commit>..<end-commit>
Cherry-Pick Options
# Cherry-pick without committing (stage changes only)
git cherry-pick -n <commit-hash>
git cherry-pick --no-commit <commit-hash>
# Edit commit message while cherry-picking
git cherry-pick -e <commit-hash>
git cherry-pick --edit <commit-hash>
# Keep original author
git cherry-pick -x <commit-hash>
Handling Cherry-Pick Conflicts
# After resolving conflicts
git add <resolved-files>
git cherry-pick --continue
# Skip the current commit
git cherry-pick --skip
# Abort cherry-pick
git cherry-pick --abort
Cherry-Pick from Another Repository
# Add another repo as a remote
git remote add other-repo https://github.com/user/repo.git
git fetch other-repo
# Cherry-pick from the other repo
git cherry-pick other-repo/main~3
Understanding Git Reset
Git reset moves the HEAD pointer and optionally modifies the staging area and working directory.
The Three Trees
Git manages three trees:
- HEAD - Last commit snapshot, next parent
- Index - Proposed next commit (staging area)
- Working Directory - Sandbox where you make changes
Reset Modes
Soft Reset - Moves HEAD only:
git reset --soft HEAD~1
- Moves HEAD to previous commit
- Keeps staging area and working directory unchanged
- Previous commit's changes remain staged
Mixed Reset (default) - Moves HEAD and resets staging:
git reset HEAD~1
git reset --mixed HEAD~1
- Moves HEAD to previous commit
- Resets staging area to match HEAD
- Keeps working directory unchanged
- Previous commit's changes become unstaged
Hard Reset - Moves HEAD, resets staging and working directory:
git reset --hard HEAD~1
- Moves HEAD to previous commit
- Resets staging area to match HEAD
- Resets working directory to match HEAD
- ⚠️ Destroys uncommitted changes
git reset --hard permanently discards changes. Make sure you want to lose those changes!
Reset Use Cases
Undo last commit but keep changes:
git reset --soft HEAD~1
Unstage all files:
git reset
Unstage specific file:
git reset <file>
Move branch pointer:
git reset --hard <commit-hash>
Undo multiple commits:
git reset --hard HEAD~3
Reset vs Revert
- Reset rewrites history (don't use on public branches)
- Revert creates a new commit that undoes changes (safe for public branches)
# Reset (changes history)
git reset --hard HEAD~1
# Revert (preserves history)
git revert HEAD
Fixing common mistakes
We as humans are prone to make errors. In this section we will see how to rectify them.
Undo recent changes made to a file
Undo changes to a file with checkout
git checkout <filename>
Undoing things
At any stage, you may want to undo something. One of the common undos takes place when you commit too early and possibly forget to add some
files, or you mess up your commit message. If you want to redo that commit, make the additional changes you forgot, stage them, and commit again using the --amend option.
git commit -amend -m "Commit Message"
Changing the commit message will change it's hash as well. Each commit message is part of the commit. Changing it will change the hash as well. This means that the Git history has been modified.
Move commit made in master to another branch
Sometimes we are working on a feature that was meant to be commited to the feature's branch. But accidently it gets commited to master. To move the commit to the said branch, we can use the cherry-pick command.
Cherry-pick creates a new commit in the said branch based off the original commit. It does not delete the original commit.
$ git log #grab the hash (6-7 characters is fine)
$ git checkout <branch-name>
$ git cherry-pick <hash>
$ git log #check new commit (new commit has new hash)
The new feature will be in the said branch. But it is still on the master branch as well. We don't want that, it was never meant to be there. In order to remove the commit from master, we can use the Git reset command.
There are 3 types of reset.
- Git reset soft (remove last commit but keeps changes in the staging directory)
- Git reset mixed - default (remove last commit, and remove files from the staging area. Files are back in the working directory. Changes are kept.)
- Git reset hard (Get rid of all changes till specified commit)
$ git checkout master #move to master branch
$ git reset --soft <hash of last desired commit>
$ git reset <hash of last desired commit>
$ git reset --hard <hash of last desired commit>
The reset command will handle tracked files.
Getting rid of untracked files
If we want to clear untracked files, we can use the clean command
git clean -df
The -d flag removes untracked directories.
The -f flag removes untracked files.
Undoing a commit without disturbing Git history
If a commit that has been pushed to a remote repository, needs to be undone, we can use the Git revert command. The revert command helps in keeping the Git history intact.
git revert <hash of commit>
This will create a new commit based of the previous one with the revert commit message.
Git Stash
Stashing takes your modified tracked files and staged changes and saves them on a stack of unfinished changes that you can reapply at any time.
Basic Stashing
# Stash current changes
git stash
# Stash with a message
git stash save "WIP: working on feature X"
# Stash including untracked files
git stash -u
git stash --include-untracked
# Stash including untracked and ignored files
git stash -a
git stash --all
Viewing Stashes
# List all stashes
git stash list
# Show stash contents
git stash show
# Show detailed stash contents
git stash show -p
# Show specific stash
git stash show stash@{2}
Applying Stashes
# Apply most recent stash
git stash apply
# Apply specific stash
git stash apply stash@{2}
# Apply and remove from stash list (pop)
git stash pop
# Apply specific stash and pop
git stash pop stash@{2}
Managing Stashes
# Create a branch from stash
git stash branch <branch-name>
# Drop a specific stash
git stash drop stash@{1}
# Clear all stashes
git stash clear
Partial Stashing
# Interactively choose what to stash
git stash -p
git stash --patch
# Stash only staged changes
git stash --staged
Git Reflog
The reflog records when the tips of branches and other references were updated in your local repository. It's your safety net for recovering "lost" commits.
Viewing Reflog
# Show reflog for HEAD
git reflog
# Show reflog for specific branch
git reflog <branch-name>
# Show reflog with dates
git reflog --relative-date
# Show reflog entries from last 2 weeks
git reflog --since="2 weeks ago"
Recovering Lost Commits
# Find the commit in reflog
git reflog
# Create a new branch from lost commit
git branch <branch-name> <commit-hash>
# Or reset to lost commit
git reset --hard <commit-hash>
# Or cherry-pick the lost commit
git cherry-pick <commit-hash>
Recovering Deleted Branch
# Find the branch tip in reflog
git reflog
# Recreate the branch
git branch <branch-name> <commit-hash>
Reflog Expiration
Reflog entries expire after 90 days by default (30 days for unreachable commits).
# Expire reflog now
git reflog expire --expire=now --all
# Expire unreachable reflog entries older than 2 weeks
git reflog expire --expire-unreachable=2.weeks.ago --all
Git Bisect
Git bisect uses binary search to find which commit introduced a bug. It's incredibly powerful for debugging.
Basic Bisect Workflow
# Start bisecting
git bisect start
# Mark current commit as bad
git bisect bad
# Mark a known good commit (e.g., a tag or commit hash)
git bisect good v1.0
# Git checks out a commit in the middle
# Test if the bug exists, then mark it
git bisect good # if no bug
git bisect bad # if bug exists
# Continue until Git finds the first bad commit
# Git will output: "abc123 is the first bad commit"
# End bisect session
git bisect reset
Start Bisect with Commits
# Start and mark in one command
git bisect start HEAD v1.0
Automated Bisect
Run a script automatically:
# Start bisect
git bisect start HEAD v1.0
# Run automated test
git bisect run ./test.sh
# Git will automatically find the bad commit
The script should:
- Exit with 0 if commit is good
- Exit with 1-127 (except 125) if commit is bad
- Exit with 125 to skip commit
Bisect Visualize
# Visualize remaining commits to test
git bisect visualize
git bisect view
Skip Commits
# Skip current commit (e.g., won't compile)
git bisect skip
# Skip a range of commits
git bisect skip v1.0..v1.5
Bisect Log
# View bisect log
git bisect log
# Replay bisect from log
git bisect replay <logfile>
Git Tags
Tags are references to specific points in Git history, commonly used to mark release versions.
Creating Tags
Lightweight tags (just a pointer to commit):
git tag v1.0
git tag v1.0 <commit-hash>
Annotated tags (full objects with metadata):
git tag -a v1.0 -m "Version 1.0 release"
git tag -a v1.0 <commit-hash> -m "Version 1.0"
Listing Tags
# List all tags
git tag
# List tags matching pattern
git tag -l "v1.8.*"
# Show tag information
git show v1.0
Pushing Tags
# Push specific tag
git push origin v1.0
# Push all tags
git push origin --tags
# Push annotated tags only
git push origin --follow-tags
Deleting Tags
# Delete local tag
git tag -d v1.0
# Delete remote tag
git push origin --delete v1.0
git push origin :refs/tags/v1.0
Checking Out Tags
# View files at tag (detached HEAD)
git checkout v1.0
# Create branch from tag
git checkout -b version1 v1.0
Git Log Advanced
Formatting Log Output
# One line per commit
git log --oneline
# Show graph
git log --graph --oneline --all
# Show with dates
git log --pretty=format:"%h - %an, %ar : %s"
# Show commits with patches
git log -p
# Show stats
git log --stat
# Show specific number of commits
git log -5
# Custom format
git log --pretty=format:"%C(yellow)%h%C(reset) - %C(green)%an%C(reset), %C(blue)%ar%C(reset) : %s"
Filtering Log
# By author
git log --author="DemonKiller"
# By date
git log --since="2 weeks ago"
git log --after="2024-01-01"
git log --before="2024-12-31"
# By commit message
git log --grep="bug fix"
# By file
git log -- <file>
# Commits that added or removed a string
git log -S "function_name"
# Commits that added or removed a string (regex)
git log -G "regex_pattern"
# Show only merge commits
git log --merges
# Show only non-merge commits
git log --no-merges
Log Ranges
# Commits in branch2 not in branch1
git log branch1..branch2
# Commits in either branch but not both
git log branch1...branch2
# Show which branch each commit came from
git log --left-right branch1...branch2
Git Diff Advanced
Diff Variations
# Diff working directory vs staging
git diff
# Diff staging vs last commit
git diff --staged
git diff --cached
# Diff working directory vs last commit
git diff HEAD
# Diff between commits
git diff <commit1> <commit2>
# Diff between branches
git diff branch1..branch2
# Diff specific file
git diff <file>
# Diff with word-level granularity
git diff --word-diff
# Show only changed file names
git diff --name-only
# Show file names with status
git diff --name-status
Diff Tools
# Use external diff tool
git difftool
# Configure diff tool
git config --global diff.tool vimdiff
git config --global difftool.prompt false
Git Worktrees
Worktrees allow you to have multiple working directories attached to the same repository.
Creating Worktrees
# Create new worktree
git worktree add <path> <branch>
# Create new worktree with new branch
git worktree add <path> -b <new-branch>
# Create worktree from detached HEAD
git worktree add <path> <commit-hash>
Managing Worktrees
# List all worktrees
git worktree list
# Remove worktree
git worktree remove <path>
# Prune worktree records
git worktree prune
Use cases:
- Work on multiple branches simultaneously
- Test changes without switching branches
- Run long-running processes on one branch while working on another
Git Submodules
Submodules allow you to keep a Git repository as a subdirectory of another Git repository.
Adding Submodules
# Add submodule
git submodule add <repository-url> <path>
# Add submodule to specific branch
git submodule add -b <branch> <repository-url> <path>
Cloning Repository with Submodules
# Clone and initialize submodules
git clone --recurse-submodules <repository-url>
# Or initialize after cloning
git clone <repository-url>
git submodule init
git submodule update
Updating Submodules
# Update all submodules
git submodule update --remote
# Update specific submodule
git submodule update --remote <submodule-path>
# Pull latest changes in all submodules
git submodule foreach git pull origin main
Removing Submodules
# Remove submodule
git submodule deinit <path>
git rm <path>
rm -rf .git/modules/<path>
Git Aliases
Create shortcuts for common commands.
Setting Up Aliases
# Simple aliases
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
# Complex aliases
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual 'log --graph --oneline --all'
# Alias with parameters
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
Now you can use:
git co main
git br -a
git st
git lg
Git Hooks
Git hooks are scripts that run automatically on certain Git events.
Common Hooks
Located in .git/hooks/:
pre-commit - Runs before commit is created:
#!/bin/bash
# .git/hooks/pre-commit
npm test
prepare-commit-msg - Runs before commit message editor opens:
#!/bin/bash
# .git/hooks/prepare-commit-msg
echo "# Please include ticket number" >> $1
commit-msg - Validate commit message:
#!/bin/bash
# .git/hooks/commit-msg
if ! grep -qE "^(feat|fix|docs|style|refactor|test|chore):" "$1"; then
echo "Error: Commit message must start with type (feat|fix|docs|...)"
exit 1
fi
pre-push - Runs before push:
#!/bin/bash
# .git/hooks/pre-push
npm run lint && npm test
post-merge - Runs after successful merge:
#!/bin/bash
# .git/hooks/post-merge
npm install
Make hooks executable:
chmod +x .git/hooks/pre-commit
Git Configuration
Configuration Levels
# System-wide configuration
git config --system
# User-level configuration
git config --global
# Repository-level configuration
git config --local
Useful Configurations
# Set default branch name
git config --global init.defaultBranch main
# Set default editor
git config --global core.editor vim
# Enable color output
git config --global color.ui auto
# Set diff tool
git config --global diff.tool vimdiff
# Set merge tool
git config --global merge.tool vimdiff
# Auto-correct mistyped commands
git config --global help.autocorrect 20
# Cache credentials for 1 hour
git config --global credential.helper 'cache --timeout=3600'
# Always rebase instead of merge when pulling
git config --global pull.rebase true
# Automatically prune deleted remote branches
git config --global fetch.prune true
View Configuration
# View all configuration
git config --list
# View specific setting
git config user.name
# View with source file
git config --list --show-origin
Git Best Practices
Commit Messages
Follow conventional commits format:
<type>(<scope>): <subject>
<body>
<footer>
Types: feat, fix, docs, style, refactor, test, chore
Example:
feat(auth): add password reset functionality
Implement password reset via email token.
Token expires after 1 hour.
Closes #123
Branching Strategy
Git Flow:
main- production-ready codedevelop- integration branchfeature/*- new featuresrelease/*- release preparationhotfix/*- emergency fixes
GitHub Flow:
main- always deployablefeature/*- short-lived feature branches- Deploy from
mainafter PR merge
Trunk-Based Development:
main- single long-lived branch- Short-lived feature branches (< 2 days)
- Feature flags for incomplete features
Do's and Don'ts
Do:
- Commit early and often
- Write meaningful commit messages
- Keep commits focused and atomic
- Use branches for features
- Pull before you push
- Review your changes before committing
Don't:
- Rewrite public history
- Commit large binary files
- Commit sensitive information
- Have huge commits with unrelated changes
- Force push to shared branches
- Commit commented-out code
Advanced Workflows
Feature Branch Workflow
# Start new feature
git checkout -b feature/awesome-feature
# Make changes and commit
git add .
git commit -m "feat: add awesome feature"
# Keep feature branch updated with main
git checkout main
git pull origin main
git checkout feature/awesome-feature
git rebase main
# Push feature branch
git push -u origin feature/awesome-feature
# After PR approval, merge via GitHub/GitLab
# Or merge locally
git checkout main
git merge --no-ff feature/awesome-feature
git push origin main
git branch -d feature/awesome-feature
git push origin --delete feature/awesome-feature
Hotfix Workflow
# Create hotfix branch from main
git checkout -b hotfix/critical-bug main
# Fix the bug
git commit -am "fix: resolve critical bug"
# Merge to main
git checkout main
git merge --no-ff hotfix/critical-bug
git tag -a v1.0.1 -m "Hotfix release 1.0.1"
# Merge to develop
git checkout develop
git merge --no-ff hotfix/critical-bug
# Clean up
git branch -d hotfix/critical-bug
Resolving Merge Conflicts
# When merge conflicts occur
git status # See conflicted files
# Open and edit conflicted files
# Look for conflict markers:
# <<<<<<< HEAD
# your changes
# =======
# their changes
# >>>>>>> branch-name
# After resolving
git add <resolved-files>
git commit # or git merge --continue
Squash Commits Before Merge
# Squash last 3 commits
git rebase -i HEAD~3
# In editor, change 'pick' to 'squash' for commits to merge
# Save and edit the combined commit message
# Force push if already pushed
git push --force-with-lease
Split Repository History
If you need to split a subdirectory into its own repository:
# Filter branch to keep only specific directory
git filter-branch --subdirectory-filter <directory> -- --all
# Or use git filter-repo (recommended)
git filter-repo --path <directory>
Troubleshooting
Undo Committed Changes
# Undo last commit, keep changes
git reset --soft HEAD~1
# Undo last commit, unstage changes
git reset HEAD~1
# Undo last commit, discard changes
git reset --hard HEAD~1
# Undo pushed commit (creates new commit)
git revert HEAD
git push origin main
Fix Wrong Commit Message
# Fix last commit message (not pushed)
git commit --amend -m "New message"
# Fix last commit message (already pushed)
git commit --amend -m "New message"
git push --force-with-lease
# Fix older commit message
git rebase -i HEAD~3 # Change 'pick' to 'reword'
Recover Deleted Files
# Find commit where file was deleted
git log --all --full-history -- <file>
# Restore file from before deletion
git checkout <commit-hash>~1 -- <file>
# Or use reflog
git reflog
git checkout <commit-hash> -- <file>
Fix Detached HEAD
# Create branch from detached HEAD
git branch temp-branch
# Or checkout existing branch
git checkout main
Remove File from Git History
# Remove file from all commits (use with caution!)
git filter-branch --tree-filter 'rm -f <file>' HEAD
# Better: use git filter-repo
git filter-repo --invert-paths --path <file>
# After removing, force push
git push --force --all
Unstage Accidentally Staged File
# Unstage specific file
git reset HEAD <file>
# Unstage all files
git reset
Discard Local Changes
# Discard changes in specific file
git checkout -- <file>
git restore <file>
# Discard all local changes
git reset --hard HEAD
# Discard all changes including untracked files
git clean -fd
git reset --hard HEAD
Sync Forked Repository
# Add upstream remote
git remote add upstream https://github.com/original/repo.git
# Fetch upstream changes
git fetch upstream
# Merge upstream changes
git checkout main
git merge upstream/main
# Or rebase
git rebase upstream/main
# Push to your fork
git push origin main
Fix "Diverged Branches" Error
# When local and remote have diverged
# Option 1: Merge
git pull origin main
# Option 2: Rebase
git pull --rebase origin main
# Option 3: Force push (only if you're sure!)
git push --force-with-lease origin main
Resolve "Refusing to Merge Unrelated Histories"
# When merging repos with no common ancestor
git pull origin main --allow-unrelated-histories
Large File Issues
# Remove large file from last commit
git rm --cached <large-file>
git commit --amend --no-edit
# Use Git LFS for large files
git lfs install
git lfs track "*.psd"
git add .gitattributes
Performance Optimization
Shallow Clone
# Clone only recent history
git clone --depth 1 <repository-url>
# Fetch more history later
git fetch --depth=100
Partial Clone
# Clone without files (blob-less)
git clone --filter=blob:none <repository-url>
# Clone without any objects
git clone --filter=tree:0 <repository-url>
Garbage Collection
# Run garbage collection
git gc
# Aggressive garbage collection
git gc --aggressive --prune=now
Optimize Repository
# Verify integrity
git fsck
# Count objects
git count-objects -vH
# Prune old objects
git prune
# Cleanup unnecessary files
git clean -fdx
Git Maintenance
Regular Maintenance Tasks
# Enable automatic maintenance
git maintenance start
# Run maintenance manually
git maintenance run
# Configure maintenance schedule
git config --global maintenance.auto true
Repository Backup
# Create bundle (complete backup)
git bundle create repo.bundle --all
# Restore from bundle
git clone repo.bundle restored-repo
Clean Up Old Branches
# Delete merged local branches
git branch --merged main | grep -v "main" | xargs git branch -d
# Remove remote-tracking branches that no longer exist
git fetch --prune
# Show stale branches
git branch -vv | grep ': gone]'
Git Security
Signing Commits
# Generate GPG key
gpg --gen-key
# List GPG keys
gpg --list-secret-keys --keyid-format=long
# Configure Git to use GPG key
git config --global user.signingkey <GPG-key-ID>
# Sign commits
git commit -S -m "Signed commit"
# Always sign commits
git config --global commit.gpgsign true
# Verify signatures
git log --show-signature
Credential Management
# Cache credentials
git config --global credential.helper cache
# Store credentials (less secure)
git config --global credential.helper store
# Use OS keychain (macOS)
git config --global credential.helper osxkeychain
# Use credential manager (Windows)
git config --global credential.helper manager
Quick Reference
Essential Commands Cheat Sheet
| Command | Description |
|---|---|
git init | Initialize repository |
git clone <url> | Clone repository |
git status | Check status |
git add <file> | Stage file |
git commit -m "msg" | Commit changes |
git push | Push to remote |
git pull | Pull from remote |
git branch | List branches |
git checkout <branch> | Switch branch |
git merge <branch> | Merge branch |
git rebase <branch> | Rebase branch |
git log | View history |
git diff | View changes |
git stash | Stash changes |
git tag | Manage tags |
git reset | Undo changes |
git revert | Revert commit |
Common Scenarios Quick Guide
Start new feature:
git checkout -b feature/name
Update feature with latest main:
git checkout main && git pull
git checkout feature/name
git rebase main
Undo last commit (not pushed):
git reset --soft HEAD~1
Change last commit message:
git commit --amend
Discard local changes:
git restore <file>
View changes not staged:
git diff
View changes staged:
git diff --staged
Temporarily save changes:
git stash
git stash pop
Find a bug with binary search:
git bisect start
git bisect bad
git bisect good <commit>
Clean up merged branches:
git branch --merged | xargs git branch -d
This comprehensive guide should serve as a solid reference for Git operations. Remember: practice makes perfect, and don't be afraid to experiment in a test repository!