Repository Basics
Before you start
You need: basic file and terminal usage. Start this track with your first project, not after — every article you write is worth not losing.
Time: about 50 minutes, at the keyboard.
Learning objective
Create a repository, stage changes deliberately, and write commits a colleague can read six months later.
Topics
- What version control gives you
- Installing and configuring
- The three areas
init,status,add,commit- Reading a diff
- Commit messages
.gitignore- Undoing before a commit
What version control gives you
| Without | With |
|---|---|
StudentService_final_v2_USE_THIS.cs | One file, full history |
| "Who changed this and why?" | git blame |
| Overwriting a colleague's work | Merging |
| No way back from a bad change | Every commit is a restore point |
The property that matters most is the last one: every commit is a point you can return to. That changes how you work — experimenting freely is safe when reverting costs nothing.
Installing and configuring
git --version
git config --global user.name "Ravi Kumar"
git config --global user.email "ravi.kumar@nexcoding.in"
git config --global init.defaultBranch main
git config --global core.autocrlf true # Windows
git config --global core.autocrlf input # macOS and Linux
git config --global pull.rebase false
git config --list
Use the email your commits should be attributed to. Commits are matched to a profile by email, so a mismatch leaves work unattributed — and on a company repository it creates confusion in audits. Changing it later does not fix existing commits.
core.autocrlf prevents a whole-file diff the first time a Windows developer commits to a repository created on Linux. Without it, every line appears changed because the line endings differ.
pull.rebase false sets an explicit strategy. Without one, newer Git warns on every pull.
The three areas
Working directory → Staging area → Repository
(your files) (git add) (git commit)
| Area | Contains |
|---|---|
| Working directory | Your files as they are now |
| Staging area | Changes marked for the next commit |
| Repository | Committed history |
The staging area is what makes small commits possible. You can commit two of five changed files and leave the rest pending — so one afternoon's work becomes three focused commits rather than one unreviewable dump.
That is not a detail. A commit doing one thing can be reviewed, reverted and bisected; a commit doing eight cannot.
init, status, add, commit
git init
git clone https://github.com/TeamSahasra/school-portal.git
git status
git status -s # short form
M src/Services/StudentService.cs modified, not staged
M src/Data/StudentRepository.cs modified, staged
?? src/Services/FeeService.cs untracked
A database/01-schema.sql added
D src/Old/Legacy.cs deleted
The left column is the staging area; the right is the working directory. MM means staged changes and further unstaged edits to the same file.
git status before every commit. It is the cheapest habit in this article and it prevents most accidental commits.
git add src/Services/StudentService.cs
git add src/Services/
git add . # everything, including untracked
git add -u # tracked files only
git add -p # interactively, hunk by hunk
git add -p is the tool that turns one messy change into two clean commits. It walks each hunk and asks whether to stage it: y, n, s to split a hunk further, q to stop.
git commit -m "Fix fee calculation to exclude cancelled payments"
git commit # opens an editor for a full message
git commit -am "..." # stage tracked files and commit
git commit -am skips the staging step, which also skips the review. Use it only for a change you have already read.
Reading a diff
git diff # working directory vs staging
git diff --staged # staging vs last commit
git diff HEAD # working directory vs last commit
git diff main feature/x # between branches
git diff --stat # summary of files and line counts
diff --git a/src/Data/FeeRepository.cs b/src/Data/FeeRepository.cs
index 3a4f2b1..8c9d0e2 100644
--- a/src/Data/FeeRepository.cs
+++ b/src/Data/FeeRepository.cs
@@ -42,7 +42,8 @@ public class FeeRepository
public decimal GetTotalPaid(int feeAccountId)
{
- const string sql = "SELECT SUM(Amount) FROM FeePayment WHERE FeeAccountId = @Id";
+ const string sql = @"SELECT ISNULL(SUM(Amount), 0) FROM FeePayment
+ WHERE FeeAccountId = @Id AND IsCancelled = 0";
- is removed, + is added. @@ -42,7 +42,8 @@ means the hunk starts at line 42, was 7 lines, is now 8.
git diff --staged before every commit. You are checking four specific things:
| Check | Why |
|---|---|
| Debug leftovers | Console.WriteLine, a hardcoded id used while testing |
| Files you did not mean to touch | appsettings.json, .csproj, a bin/ folder |
| Secrets | Connection strings, API keys, passwords |
| Whitespace-only churn | A reformat burying the real change |
A committed secret is a leaked secret. Deleting it in a later commit does not remove it — it stays in history, readable by anyone who clones the repository. The only real fix is rotating the credential.
Five seconds reading the diff prevents it.
Commit messages
Fix fee calculation to exclude cancelled payments
FeeAccount.PaidAmount summed every FeePayment row, including
reversed ones, so students with a cancelled payment showed as
Paid while still owing money.
Filter to IsCancelled = 0 in the repository query.
Reported by the accounts team for Ravi Kumar (NCA-2024-0012).
Fixes #142
Subject line: under 50 characters, imperative mood, no full stop.
Add student search by roll number
Fix absent students being counted as failed
Remove unused FeeCalculator class
Update Dapper to 2.1.35
Imperative because it completes the sentence "this commit will…" — which is also how Git's own generated messages read.
Body: the why.
The diff already shows what changed. What it cannot show is why you made that choice, what was broken, and what you considered. Updated StudentController.cs is worthless — the filename is already in the commit.
You are writing for the person who runs git blame on this line in eighteen months. That person is usually you.
If the subject needs "and", it is probably two commits:
Bad: Fixed fee calculation and added student search and updated README
Good: Fix fee calculation to exclude cancelled payments
Add student search by roll number
Update README with new setup steps
Conventional Commits is a common convention worth knowing:
feat(students): add search by roll number
fix(fees): exclude cancelled payments from the total
docs(readme): document local setup
refactor(data): extract StudentMapper
test(fees): cover the cancelled-payment case
chore(deps): update Dapper to 2.1.35
Machine-readable, so tooling can generate changelogs and infer version bumps. Adopt it if the team does.
.gitignore
# .NET
bin/
obj/
*.user
*.suo
.vs/
# Configuration with secrets
appsettings.Development.json
appsettings.Production.json
.env
*.pfx
# Node
node_modules/
dist/
.env.local
# Python
__pycache__/
*.pyc
.venv/
venv/
# Databases and logs
*.mdf
*.ldf
*.log
# OS
.DS_Store
Thumbs.db
Create it before the first commit. Adding it later does not untrack files already committed.
git rm --cached appsettings.Development.json
git rm -r --cached bin/
--cached removes from tracking and keeps the local file. Without it, the file is deleted.
git check-ignore -v src/bin/Debug/app.dll
That reports which rule is ignoring a file — the answer to "why will Git not add this?".
If bin/ keeps appearing in git status, the .gitignore is wrong or the files are already tracked. Unticking them by hand every day is not the fix.
Start from a template: gitignore.io generates one for your stack.
Git in Visual Studio and VS Code
Most teams use a mixture of the command line and an editor's Git tools. This track teaches the command line because it is identical everywhere and it is what error messages, documentation and colleagues assume. But you will work in an editor daily, so know what each button actually runs.
Visual Studio — View → Git Changes:
| In the window | Runs |
|---|---|
| A file under Changes, with + | git add <file> |
| Stage All | git add . |
| Message box plus Commit Staged | git commit -m "..." |
| Commit All | git commit -am "..." — skips your review |
| Double-clicking a file | git diff for that file |
| Push / Pull / Fetch | The same commands |
Git → Manage Branches | git branch, git switch |
| Right-click a commit → Revert | git revert <hash> |
VS Code — the Source Control panel (Ctrl+Shift+G):
| In the panel | Runs |
|---|---|
| + beside a file | git add <file> |
| + beside "Changes" | git add . |
| Clicking a file | Opens a side-by-side git diff |
| Selecting lines → Stage Selected Ranges | The equivalent of git add -p |
| The ✓ with a message | git commit |
| The branch name, bottom left | git switch |
| The sync arrows | git pull then git push |
Two of these are worth using over the command line. VS Code's side-by-side diff is easier to read carefully than terminal output, and Stage Selected Ranges does hunk-by-hunk staging with a mouse — the same job as git add -p, with less friction.
GitHub Desktop is a standalone client covering commit, branch, push, pull and pull requests, with a clear diff view. It is a reasonable way to start and it does not cover the recovery commands in article 07 — so learn the command line alongside it, not instead of it.
Two cautions apply to every GUI:
"Commit All" stages and commits in one click, skipping the review that catches a debug line or a connection string. Stage deliberately, then read the diff, then commit.
A GUI hides which command it ran. When something goes wrong — a rejected push, a detached HEAD, a conflict — the error message is in command-line vocabulary, and the fix usually is too. That is why this track teaches the commands: not because the buttons are wrong, but because you cannot diagnose a button.
Undoing before a commit
git restore src/Services/StudentService.cs # discard working changes
git restore . # discard everything unstaged
git restore --staged src/Data/Repo.cs # unstage, keep the change
git restore --source=HEAD~2 src/Config.cs # restore from an older commit
Older syntax you will still meet:
git checkout -- file # same as git restore
git reset HEAD file # same as git restore --staged
git restore <file> is unrecoverable. The change was never committed, so Git has no copy. There is no undo — which is why git status and git diff come first.
git stash # shelve everything
git stash -u # include untracked files
git stash push -m "wip search" # named
git stash list
git stash pop # restore and remove
git stash apply # restore and keep
git stash drop
git stash without -u leaves untracked files behind, which is a frequent surprise — you stash, switch branch, and your new file is still there.
Stash is for switching branches with unfinished work. It is not storage: a stash from three weeks ago is a commit you should have made on a branch.
Amending
git commit --amend # edit the last message
git commit --amend --no-edit # add staged changes to the last commit
Right for a typo in a message or a file you forgot, before pushing.
Amending rewrites history. After pushing, the amended commit has a different hash, so everyone else's history diverges — and the fix is a force push, which is a conversation with the team, not a solo decision.
Rule: amend freely before pushing, never after.
History
git log
git log --oneline
git log --oneline --graph --all
git log -5
git log --author="Ravi"
git log --since="2 weeks ago"
git log -- src/Data/FeeRepository.cs # commits touching one file
git log -p -- src/Data/FeeRepository.cs # with diffs
git log -S "IsCancelled" # commits adding or removing that string
git show a3f9c1
git show a3f9c1 --stat
git show a3f9c1:src/Data/FeeRepository.cs
git log -S "text" searches the content of changes, not commit messages. It answers "when did this magic number appear?" and "who wrote this connection string?" — often the fastest route to a cause.
Diagnosing
| Symptom | Cause |
|---|---|
| A file will not be ignored | Already tracked — git rm --cached |
bin/ in every commit | Missing or wrong .gitignore |
| Every line shows as changed | Line endings — set core.autocrlf |
| Commits show the wrong author | Email mismatch in config |
| "nothing to commit" after editing | Not staged — run git add |
| A lost uncommitted change | git restore discarded it — unrecoverable |
| A stashed file is missing | Untracked, stashed without -u |
git config user.email
git check-ignore -v <path>
git status
Those three answer most of the table.
Errors you will hit
| Message | Cause | Fix |
|---|---|---|
fatal: not a git repository | Not inside one | git init, or cd into it |
nothing to commit, working tree clean | Changes not staged, or nothing changed | git status |
bin/ keeps appearing | Missing .gitignore, or already tracked | git rm --cached |
| Every line shows as changed | Line endings | Set core.autocrlf |
| Commits show the wrong author | Email mismatch | git config user.email |
| A change vanished | git restore discarded it | Unrecoverable — it was never committed |
git restore on an uncommitted change is unrecoverable. That is why git status and git diff come first.
Common mistakes
- No
.gitignorebefore the first commit - Committing
bin/,obj/,node_modules/ - Committing a connection string or
.env git add .without readinggit status- Committing without reading
git diff --staged - Messages that restate the filename
- Giant end-of-day commits
git add -Awhen the change should be two commits- Amending after pushing
- Using stash as long-term storage
- The wrong email in config
Practice
The course exercise is create meaningful commits.
- Create a repository and configure name, email and
core.autocrlf. - Write a
.gitignorefor .NET before the first commit. - Make two unrelated changes in two files. Stage one, commit it, and confirm the other is still pending.
- Make two unrelated changes in one file. Use
git add -pto commit them separately. - Read
git diff --stagedbefore a commit. Add aConsole.WriteLine, stage it, and catch it in the diff. - Write a message with a body explaining why. Then write one saying
Updated fileand compare what each tells a reader. - Commit a file, then add it to
.gitignore. Confirm it is still tracked, thengit rm --cachedit. - Run
git check-ignore -von a file Git will not add. - Edit a file and
git restoreit. Confirm the change is gone permanently. - Stage a change, then
git restore --stagedit. Confirm the change survives. - Stash tracked and untracked changes without
-u. Switch branch and find the untracked file still present. - Amend a commit message, then amend to add a forgotten file.
- Run
git log --oneline --graph --allon a repository with several branches. - Use
git log -Sto find when a specific string was introduced. - Commit a fake connection string, then look for it with
git log -p | grep. Confirm deleting it in a later commit does not remove it from history.
Exercise 15 is the one to do once, deliberately, on a throwaway repository.
You can now
- Create a repository and configure your identity
- Write a
.gitignorebefore the first commit - Stage deliberately with
git add -p - Read
git diff --stagedbefore committing - Write a commit message that explains why
- Use Git from Visual Studio and VS Code as well as the command line
Review questions
- What does the staging area let you do that committing everything does not?
- What four things are you looking for in
git diff --staged? - Why does deleting a committed secret not remove it?
- When is
git commit --amendsafe?
Next: Remote repositories