Version control tracks changes to code over time. Git, created by Linus Torvalds for Linux kernel development, has become universal standard. Understanding Git is essential for individual developers and absolutely critical for teams. Without version control, collaboration would be chaotic and history would disappear.
Version Control with Git

Git is distributed version control system. Every developer has complete copy of repository with full history. This differs from centralized systems where single server stores history. Distribution enables working offline, fast operations, and multiple backup copies. No single point of failure.
Repositories store project files and history. Local repository resides on developer’s machine. Remote repository (GitHub, GitLab, Bitbucket) enables collaboration. Developers clone remote repository, work locally, push changes back. This workflow underlies most modern development.
Commits capture snapshots of project at specific times. Each commit has unique hash (long hexadecimal string), author, timestamp, message describing changes. Commits form directed acyclic graph: each commit points to parent(s). Branches are lightweight pointers to specific commits.
Staging area (index) lets developers prepare commits selectively. git add stages specific changes; git commit creates commit from staged content. This separation enables grouping related changes together, keeping commits focused and atomic. “Commit early, commit often” with meaningful messages.
Branches enable parallel development. Default branch usually named main or master. Feature branches isolate work on new features or bug fixes. Multiple developers can work simultaneously without interfering. Branches cheap in Git—creating and switching fast.
Merging integrates changes from different branches. Fast-forward merge simply moves branch pointer forward when no divergent changes. Three-way merge creates new commit combining changes when branches have diverged. Merge commits preserve history showing where branches joined.
Merge conflicts occur when same file sections modified differently in branches being merged. Git cannot automatically decide which changes to keep. Developers must manually resolve conflicts, choosing desired content, removing conflict markers, then commit resolution. Conflicts intimidating initially but manageable with practice.
Pull requests (or merge requests) facilitate code review. Developer pushes feature branch to remote, opens pull request requesting merge into main branch. Team reviews changes, discusses, requests modifications. CI runs tests. After approval, pull request merges. This workflow improves code quality and spreads knowledge.
Remote repositories enable collaboration. git push sends local commits to remote. git pull fetches remote changes and merges into local branch. git fetch retrieves changes without merging, letting developers review before integrating. Multiple remotes possible—common to have origin (primary) and upstream (original forked repository).
Stashing temporarily saves uncommitted changes. git stash shelves changes, cleaning working directory. Later git stash pop reapplies them. Useful when needing to switch branches quickly without committing half-finished work.
Rebasing rewrites history by applying commits onto different base. git rebase main while on feature branch applies feature branch commits after latest main commits, creating linear history. Rebasing makes history cleaner but should never rebase commits already pushed and shared—rewriting public history causes problems.
Cherry-picking applies specific commits to current branch. git cherry-pick <hash> takes changes from one commit and applies them elsewhere. Useful for selectively porting fixes between branches without merging everything.
Tags mark specific points in history, usually releases. git tag v1.0.0 creates lightweight tag; annotated tags store additional metadata. Tags unlike branches don’t move. Essential for marking production releases.
.gitignore specifies intentionally untracked files. Dependencies, build artifacts, environment files, IDE configuration—files generated or local—should never be committed. Proper .gitignore prevents clutter and security issues.
Git hooks automate actions at Git lifecycle points. Pre-commit hooks can lint code, run tests. Post-receive hooks can deploy applications. Hooks stored in .git/hooks but not version-controlled. Tools like Husky manage hooks across team.
UNDO operations save mistakes. git commit --amend modifies last commit. git reset moves branch pointer, optionally unstages changes or discards them entirely. git revert creates new commit undoing previous changes. git reflog logs where HEAD pointed, enabling recovery of “lost” commits.
Learning Git means understanding commits, branches, merging, remotes. It means developing workflow habits preventing disasters. Git is developer’s safety net, enabling experimentation without fear, collaboration without chaos, and history without loss.