Version control is not optional in professional development โ it is as fundamental as saving your work. Git tracks every change to your codebase, lets you experiment safely on branches, collaborate with teammates without overwriting each other’s work, and roll back to any previous state. For a MEAN Stack project where frontend and backend are developed in parallel, a clear Git workflow prevents the two tracks from colliding and ensures the main branch always contains working code. In this lesson you will set up Git for the monorepo, understand branching strategy, write meaningful commit messages, configure a proper .gitignore, and establish the workflow that will serve you throughout the rest of this course and in real team environments.
Git Branching Strategies
| Strategy | Branches | Best For |
|---|---|---|
| Git Flow | main, develop, feature/*, release/*, hotfix/* | Software with scheduled releases |
| GitHub Flow | main + feature branches โ PR โ merge | Continuous deployment โ most web apps |
| Trunk-Based Development | main only + short-lived branches | CI/CD teams with feature flags |
Branch Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Feature | feature/description |
feature/task-crud-api |
| Bug fix | fix/description |
fix/login-token-expiry |
| Chore | chore/description |
chore/update-dependencies |
| Documentation | docs/description |
docs/api-readme |
| Release | release/version |
release/v1.2.0 |
Conventional Commits โ Commit Message Format
| Type | When to Use | Example |
|---|---|---|
feat |
New feature | feat(tasks): add CRUD API endpoints |
fix |
Bug fix | fix(auth): correct JWT expiry handling |
chore |
Maintenance, dependencies | chore: update mongoose to v8 |
docs |
Documentation only | docs: add API endpoint documentation |
refactor |
Code change without behaviour change | refactor(tasks): extract service layer |
test |
Adding or updating tests | test(auth): add login integration test |
style |
Formatting only โ no logic change | style: apply prettier formatting |
perf |
Performance improvement | perf(tasks): add MongoDB index on userId |
type(scope): description. This format enables automated tooling โ CHANGELOG generation, semantic versioning, and release automation. Even if you work alone, adopting this format now builds a habit that every professional team expects.feat(auth): add bcrypt password hashing tells you exactly what changed and why, even six months later. “various fixes” tells you nothing.main branch, even on a solo project. Always create a feature branch, make your changes, then merge back to main. This discipline means main always contains known-working code, and you have a clean history that shows when each feature was added. It also prepares you for team workflows where direct commits to main are blocked.Setup โ Git for the MEAN Stack Project
# โโ Initialize Git for the monorepo โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
cd task-manager
git init
git branch -M main # rename default branch to main
# โโ Configure Git (if not done globally) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git config --global user.name "Your Name"
git config --global user.email "your@email.com"
git config --global init.defaultBranch main
git config --global core.autocrlf input # macOS/Linux โ normalise line endings
# โโ Create the .gitignore โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
.npm/
# Environment variables โ NEVER commit these
.env
.env.local
.env.*.local
# Angular build output
frontend/dist/
frontend/.angular/
# IDE and OS files
.vscode/settings.json
.idea/
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
# Test coverage
coverage/
# MongoDB local data
data/db/
EOF
# โโ First commit โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git add .
git commit -m "chore: initial MEAN Stack project scaffold
- Add monorepo structure with frontend/ and backend/
- Configure .gitignore for Node, Angular, and environment files
- Add root package.json with concurrently dev scripts"
# โโ Connect to GitHub โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Create a new repo on GitHub first, then:
git remote add origin https://github.com/yourusername/task-manager.git
git push -u origin main
Daily Development Workflow โ GitHub Flow
# โโ Start a new feature โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git checkout main
git pull origin main # always pull latest first
git checkout -b feature/task-crud-api # create and switch to feature branch
# โโ Develop the feature โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ... write code ...
git status # see what changed
git diff # see exact changes
git add backend/src/routes/task.routes.js
git add backend/src/controllers/task.controller.js
git commit -m "feat(tasks): add GET and POST API endpoints"
# ... write more code ...
git add .
git commit -m "feat(tasks): add PUT and DELETE endpoints with auth protection"
# โโ Push and open a Pull Request โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git push origin feature/task-crud-api
# Go to GitHub โ New Pull Request โ Merge into main
# โโ After PR is merged โ clean up โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git checkout main
git pull origin main
git branch -d feature/task-crud-api # delete local branch
git push origin --delete feature/task-crud-api # delete remote branch
# โโ Useful Git commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
git log --oneline --graph --decorate # visual branch history
git stash # temporarily save uncommitted changes
git stash pop # restore stashed changes
git diff HEAD~1 # compare with previous commit
git show abc1234 # show a specific commit
git restore filename.js # discard changes to a file
git reset HEAD~1 --soft # undo last commit, keep changes staged
Example Commit History โ Well-Structured Project
* a1b2c3d (HEAD -> main) feat(frontend): add task list component with async pipe
* d4e5f6a feat(frontend): add task service with HttpClient
* b7c8d9e feat(tasks): add Mongoose Task model and schema
* e1f2g3h feat(tasks): add CRUD REST endpoints with validation
* h4i5j6k feat(auth): add JWT authentication middleware
* k7l8m9n feat(auth): add login and register endpoints
* n1o2p3q feat(auth): add bcrypt password hashing in User model
* q4r5s6t feat(auth): add User Mongoose model and schema
* t7u8v9w chore: configure MongoDB connection and reconnect logic
* w1x2y3z chore: add express app setup with cors, helmet, body parser
* z4a5b6c chore: initial MEAN Stack project scaffold
How It Works
Step 1 โ Git Tracks the Entire Project as One History
A single Git repository at the root of your monorepo tracks changes across both frontend/ and backend/. Git records every change as a commit โ a snapshot of all modified files at a specific point in time. You can jump back to any commit: git checkout abc1234 restores the entire project (both frontend and backend) to exactly that state.
Step 2 โ Branches Are Cheap โ Use Them Freely
A Git branch is just a pointer to a commit โ creating one takes milliseconds and uses negligible disk space. Branch for every feature, no matter how small. This means you can always switch back to main and start a different task without losing your work. Use git stash when you need to switch contexts before a feature is commit-ready.
Step 3 โ .gitignore Prevents Disasters
The .gitignore file tells Git which files and folders to never track. node_modules/ can contain hundreds of thousands of files โ committing them would make the repo enormous and slow. .env contains secrets โ committing it exposes credentials publicly if the repo is ever made public. The Angular dist/ folder is generated from source โ committing build artifacts creates unnecessary merge conflicts.
Step 4 โ Conventional Commits Enable Automated Tooling
The feat, fix, chore prefixes are not just cosmetic. Tools like standard-version and release-please read your commit history and automatically determine the next semantic version number (major/minor/patch) and generate a CHANGELOG. A commit starting with feat: triggers a minor version bump. A fix: triggers a patch. A commit with BREAKING CHANGE: in the body triggers a major version bump.
Step 5 โ Pull Before You Push โ Always
git pull origin main before creating a new branch ensures your branch starts from the latest code. Pulling before pushing prevents rejected push errors when teammates have pushed in the meantime. In team workflows, always rebase or merge the latest main into your feature branch before opening a Pull Request โ this makes the PR diff clean and reduces the chance of merge conflicts during review.
Real-World Example: Pre-commit Hooks with Husky
# Install Husky โ runs scripts before every commit
cd task-manager
npm install -D husky lint-staged
# Initialize Husky
npx husky init
echo "npx lint-staged" > .husky/pre-commit
# Configure lint-staged in root package.json
# Add:
# "lint-staged": {
# "backend/src/**/*.js": ["eslint --fix", "git add"],
# "frontend/src/**/*.ts": ["eslint --fix", "prettier --write", "git add"],
# "frontend/src/**/*.html": ["prettier --write", "git add"]
# }
# Now every git commit automatically:
# 1. Runs ESLint and fixes auto-fixable issues
# 2. Runs Prettier to format the code
# 3. Adds the formatted files back to the commit
# If ESLint finds unfixable errors โ the commit is ABORTED
Common Mistakes
Mistake 1 โ Giant commits with vague messages
โ Wrong โ impossible to understand or revert selectively:
git add .
git commit -m "stuff" # what stuff?
git commit -m "wip" # work in progress โ but what?
git commit -m "fix bug" # which bug? where? how?
โ Correct โ small, focused commits with descriptive messages:
git commit -m "fix(auth): handle expired JWT with 401 response instead of 500"
Mistake 2 โ Committing node_modules
โ Wrong โ node_modules committed: repo is gigabytes, Git is unusable:
git add . # if .gitignore is missing or wrong โ adds node_modules!
# 200,000+ files added โ git log, git status are now extremely slow
โ Correct โ verify .gitignore before first commit:
git status # check what will be committed before git add
# If node_modules appears โ fix .gitignore first
echo "node_modules/" >> .gitignore
Mistake 3 โ Never pulling โ diverged branches
โ Wrong โ working on stale code for days:
git checkout -b feature/my-feature # branched from week-old main
# ... 3 days of work ...
git push # rejected โ main has moved ahead 20 commits
git merge main # massive conflict โ painful resolution
โ Correct โ regularly sync with main:
git fetch origin
git rebase origin/main # keep feature branch up to date daily
Quick Reference โ Git Commands for MEAN Stack Development
| Task | Command |
|---|---|
| Start new feature | git checkout main && git pull && git checkout -b feature/name |
| Stage all changes | git add . |
| Commit with message | git commit -m "feat(scope): description" |
| Push branch | git push origin feature/name |
| See history | git log --oneline --graph |
| Discard file changes | git restore filename |
| Stash work in progress | git stash / git stash pop |
| Undo last commit (keep changes) | git reset HEAD~1 --soft |
| Delete merged branch | git branch -d feature/name |