Git Basics: From First Commit to Collaboration
Git is the most widely used version control system, tracking changes in files across a distributed network of repositories. Unlike centralized systems (SVN, CVS), Git stores the complete history locally, enabling offline work, fast operations, and flexible branching models. This article covers the essential Git commands and concepts every developer must know.
The Three States and Basic Workflow
Git has three main states for files: modified (changed but not staged), staged (marked for the next commit), and committed (saved to the local repository). The working directory holds modified files, the staging area (index) holds staged changes, and the .git directory stores committed history. The basic cycle is: edit files in the working directory, use git add to stage changes, and git commit to save them to history. git status shows the current state, and git diff shows unstaged changes.
# Initialize a new repository
git init my-project
cd my-project
# Create and commit a file
echo "# My Project" > README.md
git status # Shows README.md as untracked
git add README.md # Stage the file
git commit -m "Initial commit with README"
git log --oneline # View commit history
Branching and Merging
Branches are lightweight pointers to specific commits. Creating a branch is instantaneous because Git simply creates a new pointer (41 bytes) rather than copying files. The default branch is named main (or master in older repositories). Feature branches isolate work until it is ready. Merging integrates changes from one branch into another—Git either fast-forwards (if there is no divergent work) or creates a merge commit (if branches have diverged). Merge conflicts occur when the same part of a file was modified in both branches and must be resolved manually.
# Branch workflow
git checkout -b feature/login # Create and switch to new branch
# ... make changes, commit ...
git add . && git commit -m "Add login form"
git checkout main # Switch back to main
git merge feature/login # Merge feature into main
git branch -d feature/login # Delete the feature branch
# Handle a merge conflict
# Edit the conflicted file to resolve
git add resolved-file.txt
git commit -m "Merge feature/login: resolved conflict"
Remote Repositories and Collaboration
Remote repositories (on GitHub, GitLab, Bitbucket) enable collaboration. git clone downloads a remote repository. git push uploads local commits, and git pull fetches and merges remote changes. git fetch downloads remote data without merging, giving you a chance to review changes before integrating. The origin remote is created automatically when cloning. Pull requests (GitHub) or merge requests (GitLab) are code review mechanisms built on top of Git’s branch model—they propose merging a feature branch into main after review and CI validation.
# Working with remotes
git clone https://github.com/user/repo.git
cd repo
git remote -v # List remotes
git pull origin main # Fetch and merge remote changes
git push origin feature-branch # Push branch to remote
# Undo and amend
git commit --amend -m "Better message" # Fix last commit message
git reset HEAD~1 # Uncommit last commit (keep changes)
git reset --hard HEAD~1 # Discard last commit and changes
Ignoring Files and .gitignore
Not all files should be committed—build artifacts (node_modules, target, build/), environment files (.env), IDE settings (.vscode/), and operating system files (.DS_Store) should be excluded via .gitignore. GitHub provides templates for different languages and frameworks. Once a file is tracked by Git, adding it to .gitignore does not stop tracking—you must use git rm –cached to untrack it. Git hooks (pre-commit, pre-push) automate checks like linting, formatting, and running tests before commits or pushes, enforcing code quality standards across the team.
Git Internals: Objects and References
Understanding Git’s internal data model demystifies many Git behaviors. Git stores everything as objects in .git/objects/: blobs (file contents), trees (directory listings mapping filenames to blobs or sub-trees), commits (snapshot pointers with metadata), and annotated tags (named commit references with messages). Each object is identified by its SHA-1 hash (40 hex characters). Branches are simple files in .git/refs/heads/ containing a commit hash—creating a branch is literally writing 41 bytes to a file. The HEAD file points to the current branch or directly to a commit (detached HEAD). When you run git add, Git creates blob objects for the file contents and updates the index (staging area). When you run git commit, Git creates a tree object from the index and a commit object pointing to that tree. Understanding this object model explains why git operations are so fast—they are just file operations on hashed content.
# Exploring Git internals
git cat-file -p HEAD # Show the current commit object
git ls-tree HEAD # Show the tree at HEAD
git cat-file -p $(git ls-tree HEAD | grep README | awk '{print $3}')
# This shows the blob content for README at HEAD

