What is Git?
What does Distributed mean?
What is Version Control?
Git's Tracking Phases
Working Tree
Staging
Commit History
What is a Repo?
Local Repo
Remote Repo
Git repos over LAN
Git Internals
Git Objects
References
HEAD
Working within Branches
Inspecting History
Undoing and Recovering from Mistakes
Project Hygiene & Secrets Management
How is GitHub different from Git?
Advanced Local Workflow
The commit cycle
FINAL git commands cheat-sheet
When you initiate git with git init in your project, it creates a .git directory which will contain git's local repo data like commits, history, branches, configurations and more. The "." hides the folder by-default on many OSes. This initiation gives us the capability of version control .
git init # initiates git in your current project folderIt's a distributed version control system.
It's a time machine for your projects.
“Distributed” means that each Git repository contains its own copy of the project's history, rather than relying on one central server to provide information.
Version control means keeping a history of changes to your project so you can see the changes, compare them, and return to earlier versions if needed.
Each saved version is called a "commit". We call these 'checkpoints' in video games. It saves the current state of the project with a name.
.
Git tracks your projects through 3 places -
Working Tree ----> Staging Area ----> Commit History
The files you're currently working on! Say I am working on main.py , so when I edit main.py , I am changing the working tree.
git diff shows the changes you've made but haven't staged.
It's not necesary that it's the files on the MAIN branch. It's what is currently being checked out. If you switch to branch-x, Git changes the working tree to reflect branch-x's current commit.
Older commits are not separate working trees. They're stored as history inside .git/.
You can temporarily make an older commit become your working tree with:
git switch --detach <commit>
git switch main # to return back--detach allows moving your working tree to a commit without moving the branch. The HEAD will also change to that commit. It's useful for temporarily inspecting, running, or testing an old version without changing your main branch.
NOTE: A commit is an object in Git history. A branch is simply a movable name pointing to a commit. A commit is not contained or tied to a branch, it can exist independently. Git normally makes HEAD follow a branch. Detached HEAD means HEAD temporarily stops following a branch and points directly at a commit.
Preparation area or waiting area till the next commit. It contains content you have selected for next commit. You can choose to stage everything or specific files only too.
Use git status to get info about files you've staged, git diff --staged to see what you have staged compared to the latest commit,.
The permanent project history with all saved states.
One of Personal Usecases
I have started using commits for tutorials or learning from docs, where I incrementally change the code as I learn more but may want to checkout starting phases when needed. I can document my learning without creating backups of previous states of code files like main.py and main.py.bak . Also, I previously used to create a folder called 'debris' to save unused or discarded code which I may wanna reference later. But, then it struck me that I can just use Git.
.
A Repository is a git-managed project with its history and associated contents.
Here's a detailed breakdown -
project/ ← working tree
├── main.py ← project file
└── .git/ ← Git repository
├── objects/ ← commits and file snapshots
├── refs/ ← branches/tags
├── HEAD ← current location
└── config ← Git configurationThe Git repository that Git is currently operating on directly. It is normally stored on the same machine where Git is running, but it can also be on a mounted/network filesystem.
If your project and .git/ are on a server, you can SSH into that server and run Git there. It would still be called local. The network connection is remote, file access is remote but from git's perspective, the repo is local as you're worikng on it directly.
A Remote is simply another Git repo that your local repo can talk to.
Remote Repo is commonly used for for team collabs, syncing between machines, open-sourcing & contributions or backing up codebases on the web.
A remote repository is normally intended to represent same project as local repo. But they may be on different versions. They can also contain temporary divergence or different branches.
git remote add origin <github-url>
# git remote add origin https://github.com/me/project.git
# origin is a name/alias for the remote repo
# To send commits to remote origin use:
git push origin main
# To get commits from remote origin use:
git pull origin mainOrigin is not fixed. You can use any name to identify remote. You can jokingly even use kiwi - git remote add kiwi <github-url> but we use origin as a convention.
Also you can have multiple remotes. You can add say a backup remote URL with git remote add backup <url> then use git push origin main git push backup main to push updates to those URLs.
.
LAN is local area network. You can connect devices together within a locality's range.
You can connect 2 repos on 2 different PCs and from each other's perspective local and remote repo definition changes -
From Computer A's perspective:
Repository A = local
Repository B = remote
From Computer B's perspective, if it communicates with A:
Repository B = local
Repository A = remote
So, remote doesn't mean “global internet”. It just means “another repository I'm communicating with”.
.
ESSENCE: Git stores objects in .git/objects/. The main object types are blobs, trees, and commits.
Object - An object is a piece of data stored by Git and identified by a hash.
blob → stores file contents
tree → stores directory structure
commit → project snapshot + history informationcommit
├── tree → project's directory structure
├── parent → previous commit
├── author
├── timestamp
└── messageInspect Objects :-
git cat-file -t <hash>
git cat-file -p <hash>.
A reference (ref) is a human-friendly name that points to a Git object. eg. commit, tags, branches.
Instead of remembering or using hashes, we use names.
cat .git/HEAD
# ref: refs/heads/main.
HEAD
↓
main
↓
commit
↓
tree
├── hello.py → blob
└── src → tree
└── app.py → blob.
HEAD tells Git where you currently are in the repository's history.
It usually points to a commit on the current branch.
It can also point to a commit without being attached to a branch. It's called detached HEAD. This is useful for temporary checkout but dangerous if you make edits. Git will eventually garbage-collect (remove) any commit which has no branch pointing to it. To continue development from a detached commit, you can create a branch pointing to it with git switch -c <new-branch-name> .
To see where the HEAD is use git status command.
.
Branches are a tool to isolate parts of your work.
You can use it to build a feature without affecting rest of the code, test it and merge with main if it works out. You also can use it for risky experiments and revert back & delete the branch if it fails.
A branch gives you an independent line of development without creating another copy of your project or files within it.
A branch is a name pointing to a commit.
A → B → C
↑
main
A → B → C → D
↑
main
=> As D got committed, main moved from C to D.
git branch.
git branch <branch-name>.
We used to use git checkout but it also had many ambiguous jobs so git switch was introduced to make branch operations specific, clear and safer.
git switch <some-branch-name>
# assume you wanna move from 'main' to 'some-branch-name'
# branch must already exist
git switch -c <new-branch-name>
# switch from current branch to a new branch git just created
# if it already exists and you use '-c' flag, it will fail
git switch main # switch back to mainDIFFERENCES BETWEEN SWITCH AND CHECKOUT
| Command | Main purpose |
| ------------------------- | ------------------------------- |
| `git switch main` | Switch branches |
| `git switch -c feature` | Create + switch branch |
| `git checkout main` | Switch branches (older command) |
| `git checkout <commit>` | Enter detached HEAD |
| `git checkout -- file.py` | Restore a file |.
Say you have a branch called 'auth' and you finished working on it and wanna merge the changes with main branch. Then first switch to main branch with git switch main then while on main do -
git merge loginOn an IDE like VS Code, this can open the diff view - showing changes or 'conflicts to resolve' if both branches changed the same part of the same file in incompatible ways. Then git will ask you to resolve conflicts manually. Though, It can automatically resolve minor conflicts.
.
git branch -d auth # safely delete branchThis deletes the branch name not the commits as branch doesn't owns commits.
If you try to delete without merging the changes/commits, git will warn you.
⚠️ Deleting a branch and its unique commits -
git branch -D authThis force-deletes the branch even if it hasn't been merged. The commits are then unreferenced and get garbage-collected eventually. ☠️ : once the commits become unreachable, recovery is harder.
-d = delete only if Git considers it safe
-D = delete even if commits may be lostTo clean-up abandoned commits immediately -
git gc.
git log --oneline
git show <commit-id> # see changes in a selected commit
git diff # shows changes between 2 states
git diff <commit-1-id> <commit-2-id>
git diff HEAD # Compare your current files with the latest commit
git blame src/utils.py
# tells which commit performed that specific change in a file
# use `git show <commit-id>` to investigate that commitWhen you have changed a file but haven't committed or staged yet and want to discard all changes to go back to last commit state -
git restore main.py⚠️You cannot recover the discarded changes on you run this, by-the-way.
.
git add main.py
git restore --staged main.pyThis will unstage the staged files without discarding the edits you made. Nothing is lost/deleted.
.
Change commits already committed.
git commit -m 'add auth' # you already ran this
# suppose you wanna add login.py to same commit -
# first stage the changes
git add login.py
git commit --amendGit replaces recent commit with the new commit containing extra changes.
.
Say you started with
print("hello")then commited it with say git commit -m "Create program" ,then changed it to
print("Hello")
print("Welcome")then commited it again with say git commit -m "Add welcome message" ,
But now you think the second commit was a mistake and you wanna go back. Then use :
git revert HEADHEAD was pointing to current commit so you targeted it.
Git creates another commit that reverses the changes from the previous commit.
File becomes again -
print("Hello")but history has all 3 commits -
1 Create program
2 Add welcome message
3 Revert "Add welcome message"So, revert undoes an earlier commit but by creating a new commit with old state.
.
git reset moves a branch's pointer to another commit.
The default reset mode is --mixed .
git reset HEAD~1
# OR
git reset --mixed HEAD~1There are --mixed, --soft, --hard modes.
git reset --soft HEAD~1Soft can make the move from commit 2 to commit 1 but changes introduced in commit 2 remain staged, ready to commit again.
git reset --hard HEAD~1Hard makes the move from commit 2 to commit 1 but changes introduced in commit 2 are REMOVED.
--soft → move commit back, keep changes staged
--mixed → move commit back, keep changes unstaged
--hard → move commit back, discard changesto move main back by one commit -
git reset HEAD~1.
reflog reflog is a local record of where HEAD and branch references have recently moved.
git reflog
# example output
#abc123 HEAD@{0}: switch: moving from auth to main
#def456 HEAD@{1}: commit: Add login
#ghi789 HEAD@{2}: switch: moving from main to auth.
HEAD can move between commits without moving the branch - we already saw this! If you make a commit while being on detached HEAD and move away, that commit may become unreachable. But reflog can help find it.
It can be recovered using
git switch -c recovered <commit>Now the commit is safely referenced again.
.
If you ran by-mistake git branch -D auth the branch would disappear but the commits would be still there if garbage collection hasn't occured.
# find the old position
git reflog
# it will show the commit with an ID like this
abc123 HEAD@{4}: commit: Add authentication
# use that ID to restore the commit by attaching it to a branch
git switch -c auth-recovered abc123 # -c creates new branchDeleting a branch deletes the pointer to commits not any commit itself. These dangling commits can be recovered.
.gitignore tells Git which files/folders it should ignore.
If you accidently commit .env use git rm --cached .env then add .env to .gitignore .
Secrets include passwords, API keys, tokens, private certificates.
If secrets get pushed to remote, they are to be assumed as compromised. Hence, revoke them.
Generated files + Dependencies and build artifacts like .venv/ , __pycache__/ , dist/ , build/ , coverage/ , target/ , node_modules/ must be excluded from commits and gitingored.
GitHub is a hosted platform or service that adds features around Git repositories, such as:
Web-based repository browsing
Contributions via Pull requests and code review
Issues and project management
Access control and permissions
CI/CD through GitHub Actions
Collaboration
Public repository discovery and sharing
Web backup
It's not a part of git but more like an add-on.
Basic : status, diff, branches, commits
Stashing temporarily saves and removes uncommitted changes from the working tree and restores it to the last commit.
So last commit + changes in working tree ----> last commit only + (changes saved in stash)
To stash your changes run -
git stashTo bring back the changes -
git stash popIf any merge conflict happens, stash is not lost. Git stops and marks conflicts to resolve manually.
This is similar to merging branches but in this case we work on the same branch and stashed changed are merged with changes in current working tree.
To see stashes -
git stash list.
Normal adding/staging stages the whole file but you can stage specific parts of a file as well. Git will show each chunk and ask what to stage.
git add utils.py # normal, stages whole file
git add -pWhen to use this -
When two unrelated changes get mixed up in one file
To stage only the bug fix and commit it separately
.
Rewrite local commit history. Git opens editor showing last X commits with options for actions to perform.
git rebase -i HEAD~3pick keep commit
reword change message
edit stop and modify commit
squash combine with previous commit
drop remove commit.
Squashing means combining multiple commits into fewer commits.
As we were using the 'auth' example: each step like adding 'login page', 'signup page', 'validation engine' can be a commit on its own. But let's say I only want 'added auth' I can squash group of commits -
git rebase -i HEAD~4This interactive rebase approach can do this.
Squashing changes history, it not just created an "undo" commit.
You'll have to pick "squash" instead of default "pick"
.
git log --oneline # see the history
git rebase -i HEAD~N # make modifications with rebase
# N is the no. of commits to selectCHANGE FILES
↓
git status # get list of stages changes
↓
git diff # compare changes between versions
↓
git add # adds files to commit to new version
↓
git commit # creates and saves the new version
↓
repeat// Setup & Basic Workflow
---
git init // Initialize Git in the current project
git status // Show current changes and staging state
git diff // Show unstaged changes
git diff --staged // Show staged changes
git add <filename> // Stage a specific file
git add . // Stage all changes
git add -p // Stage selected parts of files
git commit -m "message" // Create a commit
// Branches
---
git branch // List branches
git branch <branch-name> // Create a branch
git switch <branch-name> // Switch to an existing branch
git switch -c <branch-name> // Create and switch to a new branch
git merge <branch-name> // Merge a branch into the current branch
git branch -d <branch-name> // Safely delete a merged branch
git branch -D <branch-name> // Force-delete a branch
// Inspecting History
---
git log --oneline // Show compact commit history
git show <commit-id> // Show a commit and its changes
git diff <commit-1> <commit-2> // Compare two commits
git diff HEAD // Compare working files with latest commit
git diff HEAD~1 // Compare working files with previous commit
git blame <filename> // Show which commit changed each line
// Inspecting Git Internals
---
git cat-file -t <hash> // Show the type of a Git object
git cat-file -p <hash> // Show the contents of a Git object
cat .git/HEAD // Show what HEAD currently points to
// Detached HEAD
---
git switch --detach <commit> // Temporarily inspect a specific commit
git switch main // Return to the main branch
// Undoing Changes
---
git restore <filename> // Discard unstaged changes in a file
git restore --staged <filename> // Unstage a file without discarding changes
git commit --amend // Replace the latest commit with updated changes
git revert <commit> // Undo a commit by creating a new commit
// Resetting History
---
git reset HEAD~1 // Move branch back one commit, keep changes unstaged
git reset --mixed HEAD~1 // Move branch back, keep changes unstaged
git reset --soft HEAD~1 // Move branch back, keep changes staged
git reset --hard HEAD~1 // Move branch back and discard changes
// Recovering from Mistakes
---
git reflog // Show recent movements of HEAD and references
git switch -c <branch> <commit> // Create a branch from a specific commit
git gc // Clean up unreachable Git objects
// Remote Repositories
---
git remote add origin <url> // Add a remote repository
git push origin main // Send local commits to a remote branch
git pull origin main // Fetch and integrate remote changes
// Stashing
---
git stash // Temporarily save uncommitted changes
git stash pop // Restore the latest stash
git stash list // List saved stashes
// Interactive History Editing
---
git rebase -i HEAD~N // Interactively modify the last N commits
// Removing Tracked Files
---
git rm --cached <filename> // Stop tracking a file but keep it locally
That's it for this article.
.
0
0
0