Git & Version Control
A practical, beginner-to-advanced guide to tracking code changes, working with branches, merging and rebasing, resolving conflicts, and collaborating through GitHub pull requests and team workflows.
01. Introduction to Version Control
Software changes constantly. Features are added, defects are fixed, dependencies are upgraded, and several developers may work on the same codebase at the same time. Version control records how files change so a team can understand, compare, restore, and collaborate on work safely.
Git records project history locally; a hosting platform such as GitHub adds remote collaboration features.
This guide focuses on Git fundamentals and professional team workflows, with examples drawn from an ASP.NET Core Web API solution. The same practices apply to frontend, mobile, data, and other software projects.
02. What Is Git?
Git is a distributed version control system. Each clone contains repository history and can create commits locally without needing a central server. Developers later exchange changes with remote repositories.
- Distributed: a clone contains the project's Git history, subject to clone options.
- Snapshot-oriented: commits represent project snapshots, while Git efficiently reuses stored data.
- Branch-friendly: branches are lightweight references that make parallel work practical.
- Integrity-aware: Git identifies objects using cryptographic hashes.
03. Why Version Control Matters
Without version control, teams often rely on copied folders, filenames such as final-v2-really-final, and manual coordination. Git replaces that fragile process with an auditable history.
- Understand who changed a file and when.
- Compare current code with an earlier version.
- Work on features independently using branches.
- Review changes before they reach the shared branch.
- Restore a previous state or revert an unwanted commit.
- Connect source changes with issues, releases, and automated builds.
04. Git vs GitHub vs GitLab vs Azure Repos
| Tool | What it provides | Typical use |
|---|---|---|
| Git | Distributed version control engine and command-line tools | Track source changes locally and exchange commits |
| GitHub | Git hosting, pull requests, issues, Actions, security and collaboration | Open-source and enterprise team workflows |
| GitLab | Git hosting with integrated planning, CI/CD and security capabilities | Integrated DevSecOps platform |
| Azure Repos | Git repositories and pull requests within Azure DevOps | Teams using Azure Boards and Pipelines |
The Git commands in this guide are largely portable across these hosting services. The web interface, policy controls, and automation integrations differ.
05. How Git Stores Changes
Git's object model is built around blobs (file content), trees (directory structure), commits (snapshots plus metadata and parent links), and annotated tags (named release references). A branch is a movable reference to a commit.
A simplified view: a branch name points to a commit, and the commit references a tree describing the snapshot.
You usually interact with these internals through commands such as git log, git show, and git cat-file; memorizing object internals is not required for everyday work.
06. Install Git and Verify the Setup
Install Git using the official Git installation page. Git for Windows includes Git Bash; macOS and Linux also provide package-manager options.
git --version
git --helpGit releases evolve independently of .NET. At the time this guide was prepared, Git 2.56.1 was the current stable release. Install the latest security-patched release available for your operating system.
07. Configure Your Git Identity
Git stores author and committer identity in commit metadata. Configure a name and an email address that you intend to use for your work.
git config --global user.name "Developer Name"
git config --global user.email "developer@example.com"
git config --global --listUse --global for your default identity. A repository-specific configuration (without --global) overrides it for that repository. For privacy, GitHub also supports a noreply email address.
08. Create Your First Repository
Start with a small .NET API project and initialize Git in its root directory.
dotnet new webapi -n InventoryApi
cd InventoryApi
git init
git statusgit init creates the local .git database. It does not upload your code anywhere. The working directory contains your project files; Git status tells you which changes are untracked, staged, or modified.
09. The Three Main Areas of Git
Everyday Git work becomes easier when you understand three areas: the working tree, the staging area (index), and the repository history.
- Working tree: files you are currently editing.
- Staging area: the exact changes selected for the next commit.
- Repository: committed snapshots and their history.
10. Check Status and Inspect Changes
Use status and diff frequently. They are safe inspection commands and help prevent accidental commits.
git status
git diff
git diff --staged
git diff -- Program.csgit diff shows unstaged changes. git diff --staged shows what is currently selected for the next commit. Review both before committing.
11. Stage Files with git add
Staging is an intentional selection step. You can stage one file, several files, or all current changes.
git add Program.cs
git add Controllers/
git add .
git statusFor a large or mixed change, consider git add -p to stage individual hunks. This lets one commit contain one coherent logical change even when several files have unrelated edits.
12. Create Meaningful Commits
A commit records the staged snapshot with a message. Prefer small, focused commits that explain why the change exists.
git commit -m "Add product search endpoint"
git log --oneline --decorate -5A useful message is specific and action-oriented. Avoid vague messages such as changes, update, or final. A commit should ideally represent one logical unit of work.
13. Read and Explore Commit History
Git history helps you understand how a project reached its current state.
git log
git log --oneline --graph --decorate --all
git show HEAD
git show <commit-id>
git blame Controllers/ProductsController.csgit blame identifies the commit that last changed each line; it is a navigation aid, not proof of who is responsible for a defect.
14. Understand HEAD and References
HEAD identifies the current checkout position, usually the current branch. A branch is a movable name for a commit. As you create commits on that branch, the branch reference advances.
git status
git branch --show-current
git rev-parse HEADDetached HEAD means you checked out a commit directly rather than a branch. It is useful for inspection, but create or switch to a branch before making work you want to keep.
15. Create and Switch Branches
Branches isolate work so a feature or fix can progress without changing the main development line.
git branch
git switch -c feature/product-search
# make changes and commit
git switch mainModern Git recommends git switch for switching branches. The older git checkout command still exists and performs several different tasks.
16. Branch Naming Conventions
Use short, descriptive branch names that make the purpose obvious. Agree on a convention with the team.
| Prefix | Example | Purpose |
|---|---|---|
| feature/ | feature/customer-export | New capability |
| bugfix/ | bugfix/order-total-rounding | Defect correction |
| hotfix/ | hotfix/payment-timeout | Urgent production fix |
| chore/ | chore/update-packages | Maintenance task |
| docs/ | docs/api-readme | Documentation-only work |
Branch naming is a team convention, not a Git requirement.
17. Branching Strategies
A branching strategy defines how a team creates, integrates, and releases work. Choose the simplest strategy that fits release frequency and team coordination.
| Strategy | Shape | Useful when |
|---|---|---|
| Trunk-based development | Short-lived branches integrate frequently into main | Teams release often and rely on automated tests and feature flags |
| GitHub Flow | Branch from main, open PR, review, merge | Web applications and continuous delivery |
| Git Flow | main, develop, feature, release and hotfix branches | Teams with planned release trains and parallel maintenance |
No strategy is universally best. Long-lived branches increase the time changes remain apart and can make integration harder.
18. GitHub Flow in Practice
GitHub Flow is a lightweight workflow centered on a deployable main branch and short-lived topic branches.
A feature branch is created from main, reviewed through a pull request, and merged after required checks pass.
- Pull the latest main branch.
- Create a focused branch.
- Commit and push your changes.
- Open a pull request and explain the change.
- Address review feedback and automated checks.
- Merge using the team's chosen method and delete the branch when appropriate.
19. Merge: Combine Branch Histories
git merge integrates changes from another branch into the currently checked-out branch. A fast-forward merge moves the branch pointer when no divergent history exists. A three-way merge creates a merge commit when histories have diverged and Git can combine them.
git switch main
git merge feature/product-searchMerge preserves the original commits. A merge commit records the integration point and can make the history of parallel work visible.
20. Resolve Merge Conflicts
A conflict occurs when Git cannot automatically determine how to combine overlapping changes. Git pauses the merge and marks the affected files.
git status
# Open each conflicted file and decide the correct result
git add Controllers/ProductsController.cs
git commitConflict markers commonly look like <<<<<<<, =======, and >>>>>>>. Remove the markers, preserve the intended code, run tests, then stage the resolved file.
git merge --abort when supported by the current state.21. Rebase: Replay Commits on a New Base
git rebase moves or replays commits so your branch is based on a newer commit. It can create a linear history, but the replayed commits receive new identities.
git switch feature/product-search
git fetch origin
git rebase origin/mainConceptual illustration: feature commits are replayed after the updated main commit.
Rebase is useful for updating a private feature branch before review. Avoid rewriting commits that teammates have already based work on unless the team explicitly coordinates it.
22. Merge vs Rebase
| Aspect | Merge | Rebase |
|---|---|---|
| History | Preserves branch topology; may add merge commit | Replays commits and produces new commit IDs |
| Traceability | Shows integration points | Often creates a linear sequence |
| Shared commits | Safe way to integrate shared branch history | Rewriting shared history can disrupt collaborators |
| Typical use | Integrate completed work | Refresh a private feature branch |
Both are valid. Agree on repository policy and understand whether history is being rewritten before choosing rebase.
23. Interactive Rebase and Squashing
Interactive rebase lets you edit, reorder, reword, combine, or remove commits in a selected range.
git rebase -i HEAD~3Common actions include pick, reword, squash, fixup, and drop. Use it to clean up your own unpublished work before sharing. Do not use it casually on commits already consumed by others.
24. Undo Changes Safely
Different commands undo different kinds of work. Check git status first and understand whether the change is uncommitted or already shared.
| Command | Purpose | Caution |
|---|---|---|
git restore file | Discard unstaged working-tree edits in a file | Local edits can be lost |
git restore --staged file | Unstage a file while keeping its working-tree edits | Does not discard the edits |
git revert commit | Create a new commit that reverses an earlier commit | Review the inverse change |
git reset | Move the current branch reference and optionally alter index/working tree | Can discard work or rewrite local history |
For shared branches, git revert is usually safer than rewriting published history with reset.
25. Reset: Soft, Mixed and Hard
git reset moves the current branch to a selected commit. Its mode controls what happens to the staging area and working tree.
git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1- --soft: move HEAD; keep changes staged.
- --mixed: move HEAD; unstage changes but keep working-tree content (the default mode).
- --hard: move HEAD and overwrite the index and working tree.
--hard can permanently discard uncommitted changes. Confirm the target commit and backup important work before using it.26. Stash Temporary Work
Stash saves selected working-tree changes temporarily so you can switch context without creating a commit.
git stash push -m "WIP product validation"
git stash list
git stash show -p
git stash apply
git stash popapply keeps the stash entry; pop applies it and removes the entry when successful. Stash is a short-term shelf, not a substitute for committing valuable work.
27. Tags and Releases
Tags identify important points in history, commonly release versions. Lightweight tags are simple references; annotated tags store metadata and can be signed.
git tag v1.0.0
git tag -a v1.1.0 -m "Inventory API 1.1.0"
git push origin v1.1.0
git tag --listUse a consistent versioning policy such as Semantic Versioning where it fits the product. A tag does not automatically create a GitHub Release, though GitHub can create a release from a tag.
28. Remote Repositories
A remote is a named reference to another repository, often hosted on GitHub, GitLab, or Azure Repos.
git remote -v
git remote add origin https://github.com/org/inventory-api.git
git remote set-url origin <new-url>
git fetch originorigin is only a conventional remote name. A repository can have multiple remotes, for example an upstream project and your own fork.
29. Clone, Fetch, Pull and Push
git clone https://github.com/org/inventory-api.git
git fetch origin
git pull --ff-only
git push -u origin feature/product-search- clone: create a local copy of a remote repository.
- fetch: download remote objects and update remote-tracking references without integrating them into your current branch.
- pull: fetch and then integrate changes according to configuration/options.
- push: publish local commits to a remote branch.
Use git pull --ff-only when you want pull to refuse an implicit merge if a fast-forward is not possible. Teams may choose another explicit pull strategy.
30. Track Remote Branches
Remote-tracking branches such as origin/main represent the last fetched state of branches on a remote. They are updated by fetch and are not normally edited directly.
git branch -a
git switch --track origin/feature/customer-export
git branch -vvAfter fetching, compare your local branch with its upstream to see whether you are ahead or behind.
31. Forks and Upstream Workflows
A fork is your own server-side copy of another repository. It is common in open-source collaboration and organizations that restrict direct writes to the main repository.
git remote add upstream https://github.com/original/project.git
git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin mainUse a pull request from your fork to the original repository. Follow the project's contribution guide, license, code of conduct, and security policy.
32. GitHub Repository Essentials
A GitHub repository combines Git history with collaboration features. Common repository elements include README, license, contribution guidelines, issue templates, pull request templates, branch rules, releases, and Actions workflows.
For a professional .NET repository, document prerequisites, how to restore/build/test, configuration requirements, database setup, and how to run the application locally. Never put secrets or production connection strings in the README.
33. Pull Requests: Purpose and Lifecycle
A pull request (PR) proposes a set of changes for review before merging them into a target branch. It provides a shared place to discuss design, inspect diffs, run checks, and record approval.
Keep PRs focused, explain the reason for the change, link the related issue, describe testing, and call out risks or deployment steps.
34. Write Effective Pull Request Descriptions
A reviewer should quickly understand the problem, the approach, and how to verify the result. A practical PR description can include:
- Summary: what changed and why.
- Implementation: important design decisions.
- Testing: commands or test cases executed.
- Risk: migration, configuration, compatibility, or rollout concerns.
- Evidence: screenshots or API examples where useful.
Keep the diff reviewable. Split unrelated refactoring from a feature change when doing so makes review safer.
35. Code Review Best Practices
Good code review improves correctness and shared understanding. Review behavior, edge cases, readability, security, performance, tests, and maintainability—not personal style preferences that are already handled by formatters or analyzers.
- Ask specific, respectful questions.
- Explain why a requested change matters.
- Distinguish blocking issues from optional suggestions.
- Review the full diff, not only the changed lines.
- Authors should respond to every actionable comment and rerun relevant checks.
36. Choose a Pull Request Merge Strategy
| Method | History result | Use when |
|---|---|---|
| Merge commit | Preserves branch commits and adds an integration commit | Team wants visible branch topology |
| Squash and merge | Combines PR commits into one commit on the base branch | One logical PR should become one commit |
| Rebase and merge | Replays commits onto base without a merge commit | Team prefers linear history and commits are meaningful |
GitHub repository settings can enable or disable available merge methods. Pick a policy that matches your team's history and release practices.
37. Protect Important Branches
Protect the default and release branches with repository rules or branch protection settings. Depending on the hosting plan and repository configuration, policies can require pull requests, approvals, status checks, signed commits, linear history, or restricted pushes.
- Require CI checks to pass before merging.
- Require at least one approval for sensitive repositories.
- Dismiss stale approvals when new commits change the reviewed code, if appropriate.
- Restrict force pushes and deletion of protected branches.
- Use CODEOWNERS to request review from responsible teams.
38. Git Ignore and .gitignore
.gitignore tells Git which untracked files and paths to ignore. A .NET repository commonly excludes build output, IDE user state, local secrets, and temporary files.
**/bin/
**/obj/
.vs/
*.user
*.suo
.env
appsettings.Development.local.jsonDo not ignore files that the team needs to build the project. A file already tracked by Git remains tracked even if it is later added to .gitignore; remove it from the index with git rm --cached when appropriate.
39. Git Attributes, Line Endings and File Types
.gitattributes controls attributes such as text normalization, diff behavior, and merge handling. Consistent line endings reduce noisy diffs when developers use different operating systems.
* text=auto
*.cs text eol=lf
*.sln text eol=crlfChoose a repository policy that matches your toolchain and team. Do not copy line-ending rules blindly; validate them on Windows, macOS, and Linux environments used by the project.
40. Secrets and Repository Security
Never commit passwords, API keys, private certificates, access tokens, or production secrets. Add local secret files to .gitignore, use environment variables or a secret manager, and configure CI secrets through the hosting platform's protected secret store.
- Enable secret scanning and push protection where available.
- Use least-privilege tokens and short-lived credentials.
- Rotate a secret immediately if it is committed or exposed.
- Removing a secret in a later commit does not remove it from earlier history.
- Use signed commits or verified identities when required by policy.
41. Git LFS and Large Files
Git is optimized for source files and text diffs. Large binary assets such as media, datasets, or design files can make a repository grow quickly. Git Large File Storage (LFS) stores pointer files in Git while the large content is stored separately.
git lfs install
git lfs track "*.psd"
git add .gitattributes
git add design/mockup.psdReview hosting quotas, bandwidth, retention, and team needs before adopting LFS. Do not use it as a replacement for a proper artifact or data-storage system.
42. Submodules and Subtrees
Submodules and subtrees let one repository include content maintained in another repository. They solve different dependency and source-sharing needs.
git submodule add https://github.com/org/shared-library.git libs/shared
git submodule update --init --recursiveSubmodules pin a specific commit of another repository and require developers to initialize/update them. Subtrees copy or merge another project's history into a subdirectory. For .NET libraries, a package feed is often simpler than embedding another repository.
43. Git Worktrees
Worktrees let you check out multiple branches from one local repository into separate working directories. This is useful when you need to inspect a hotfix while keeping a feature branch open.
git worktree add ../InventoryApi-hotfix hotfix/payment-timeout
git worktree list
git worktree remove ../InventoryApi-hotfixWorktrees share the repository's object database but have separate working directories and indexes. A branch generally cannot be checked out in two worktrees simultaneously.
44. Useful Git Inspection and Recovery Tools
git reflog
git fsck --lost-found
git diff main...feature/product-search
git log --all --oneline --graph
git show --stat HEADgit reflog records recent movements of local references and can help recover a commit after a mistaken reset or rebase. Reflog entries expire, so it is not a permanent backup. Learn these tools before you need emergency recovery.
45. Git Hooks and Automation
Git hooks are scripts triggered by repository events such as commit or push. They can run formatters, linters, or checks locally, but local hooks can be bypassed and are not a substitute for server-side CI requirements.
For a .NET solution, a pre-commit hook might run formatting checks, while CI should perform authoritative restore, build, test, and security checks on pull requests.
46. GitHub Actions: CI for a .NET Project
GitHub Actions runs workflows defined as YAML files in .github/workflows. A workflow can run on pull requests, pushes, manual dispatch, or schedules. This simple example restores, builds, and tests a .NET 10 solution.
name: .NET CI
on:
pull_request:
push:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- run: dotnet restore
- run: dotnet build --no-restore --configuration Release
- run: dotnet test --no-build --configuration Release47. A Practical Team Workflow for an ASP.NET Core API
- Clone the repository and install the required .NET SDK.
- Read README, contribution guidance, and branch policy.
- Fetch the latest remote state and update main.
- Create a branch such as
feature/customer-search. - Implement the change and add or update tests.
- Run
dotnet format,dotnet build, anddotnet testas appropriate. - Review staged changes and create focused commits.
- Push the branch and open a pull request.
- Address review comments and wait for required CI checks.
- Merge using the repository policy and verify the deployed result.
git switch main
git pull --ff-only
git switch -c feature/customer-search
# edit, test, inspect
git status
git diff
git add .
git commit -m "Add customer search endpoint"
git push -u origin feature/customer-search48. Common Git Mistakes
| Mistake | Better practice |
|---|---|
| Committing generated output | Use a reviewed .gitignore and inspect status |
| One huge commit for unrelated work | Stage and commit coherent changes |
| Rebasing shared commits without coordination | Preserve published history or coordinate a rewrite |
| Force-pushing main | Protect important branches and use reviewed PRs |
| Committing secrets | Use secret stores and scanning; rotate exposed credentials |
| Ignoring conflicts without testing | Understand both sides and run relevant tests |
| Pulling without understanding strategy | Use explicit fetch/integration behavior agreed by the team |
49. Troubleshooting Everyday Git Problems
- “Nothing to commit”: check whether files changed, are ignored, or are already staged.
- Push rejected: fetch remote changes, integrate them safely, then push again.
- Authentication failed: verify HTTPS credential manager or SSH key setup and account permissions.
- Wrong branch: inspect status and branch before committing; move uncommitted work carefully.
- Merge conflict: resolve each file, stage the result, and complete or abort the merge.
- Accidental commit: decide whether it is local-only or shared; choose reset, amend, or revert accordingly.
When in doubt, stop and inspect git status, git log, and git reflog before running destructive commands.
50. Mini Project: Collaborate on an Inventory API
Practice the full workflow with a small ASP.NET Core API repository.
- Create an
InventoryApirepository and add a README, license if appropriate, and .gitignore. - Create a main branch with a working baseline and a CI workflow.
- Developer A creates
feature/product-crud; Developer B createsfeature/stock-validation. - Each developer commits focused changes and pushes their branch.
- Open separate pull requests and review each other's code.
- Intentionally create an overlapping edit to practice conflict resolution.
- Merge using the team's chosen strategy and verify CI.
- Tag a sample release such as
v1.0.0.
Deliverable: a repository with a clear commit history, reviewed pull requests, a repeatable build/test workflow, and documented setup instructions.
51. Interview Questions and Answers
1. What is Git?
Git is a distributed version control system that records project history and supports parallel development.
2. How is Git different from GitHub?
Git is the version control tool; GitHub hosts Git repositories and adds collaboration, review, automation, and project features.
3. What are the working tree, index, and repository?
The working tree contains current files, the index stages the next snapshot, and the repository stores committed history.
4. What is a commit?
A commit is a recorded snapshot with metadata and parent commit references.
5. What is a branch?
A branch is a movable reference to a commit, commonly used to isolate a line of development.
6. What is the difference between fetch and pull?
Fetch downloads remote updates without integrating them; pull fetches and then integrates according to the configured or specified strategy.
7. What is the difference between merge and rebase?
Merge integrates histories and may create a merge commit; rebase replays commits onto a new base and changes their identities.
8. What is a merge conflict?
A conflict occurs when Git cannot automatically combine overlapping changes. A developer must resolve the intended result.
9. When should you use revert instead of reset?
Revert is generally safer for shared history because it adds a new inverse commit. Reset moves a branch reference and can rewrite or discard local work.
10. What does git stash do?
It temporarily shelves selected uncommitted changes so you can switch context.
11. What is a pull request?
A pull request proposes changes for discussion, review, automated checks, and eventual integration.
12. What are squash, merge commit, and rebase merge?
They are PR integration methods that respectively combine changes into one commit, preserve branch commits with a merge point, or replay commits linearly.
13. Why should secrets never be committed?
Secrets in Git history may remain accessible even after a later deletion. Exposed credentials must be revoked or rotated.
14. What is .gitignore?
A file of patterns that tells Git which untracked files and paths to ignore.
15. What is GitHub Actions?
A GitHub automation platform that runs workflows defined in YAML, commonly for CI, testing, and deployment.
52. Official Documentation and Further Learning
- Git official documentation
- Pro Git book
- GitHub getting started
- GitHub pull requests documentation
- GitHub Actions documentation
- Azure Repos Git documentation
- Git LFS
- .NET support policy
Use the official documentation for current command options, hosting features, security guidance, and release details.
53. Final Thought
Git is more than a set of commands. It is a shared discipline for making software changes understandable, reviewable, and recoverable. Learn to inspect before changing history, commit focused work, collaborate through short-lived branches and pull requests, and protect shared repositories with automated checks.
Start with status, diff, add, commit, branch, switch, fetch, pull, and push. Then practice merge, rebase, conflict resolution, revert, and recovery in a safe repository. Strong Git habits make every software project easier to maintain.
Skills Tutorials – Learn • Build • Grow Together