📂 Real-World Examples
Each example is a scenario-based walkthrough using only Git commands — no framework-specific steps. Click any card to expand it.
Scenario
The repo already exists on GitLab. You need to get it on your machine and start contributing. This is the most common starting point — joining a team whose project is already on GitLab.
Step 1 — Clone the Repository
git clone https://gitlab.company.com/team/MyApp.git
# Or clone into a specific folder name
git clone https://gitlab.company.com/team/MyApp.git my-local-folder
cd MyApp
This creates a full local copy including the entire history. The remote is automatically named origin.
npm install (Node/Angular), dotnet restore (.NET), or pip install -r requirements.txt (Python).Step 2 — Understand What You Cloned
git log --oneline --graph -20 # recent history
git branch -a # all branches (local + remote)
git remote -v # confirm origin points to GitLab
Step 3 — Always Pull Before You Start Work
git switch main
git pull
Step 4 — Make Changes and Push
# Create a branch for your work
git switch -c feature/add-user-endpoint
# ... make your changes ...
git status
git diff
git add src/users/users.controller.js
git commit -m "Add GET /users endpoint"
git push -u origin feature/add-user-endpoint
Then go to GitLab and open a Merge Request from your branch into main.
Step 5 — Stay in Sync While Working
git fetch origin
git rebase origin/main
If You Accidentally Work on Main
git switch -c feature/my-work # create branch at current position
git switch main
git reset --hard origin/main # reset main back to remote state
git switch feature/my-work # your changes are safe on the new branch
Scenario
You're starting a brand new project from scratch and need to get it into GitLab.
Step 1 — Create the GitLab Repository First
- Go to your GitLab instance → New Project → Create blank project
- Set a name (e.g.,
MyApp) - Set visibility (Private/Internal/Public)
- Uncheck "Initialize repository with a README" — you'll push from local
- Copy the Clone with HTTPS URL (e.g.,
https://gitlab.company.com/team/MyApp.git)
Step 2 — Create Your Project Folder
Create the project folder and add your initial files however your stack requires. Once you have something worth tracking, continue below.
Step 3 — Initialize Git and Add .gitignore
git init
Create a .gitignore at the root to stop build output and IDE files from being committed:
# Build output (adjust for your stack)
bin/
obj/
dist/
out/
# IDE / Editor
.vs/
.idea/
*.user
*.suo
.vscode/
# OS
.DS_Store
Thumbs.db
# Environment / secrets
.env
.env.local
*.local
Step 4 — First Commit
git add .
git status # review what's staged — confirm no build output appears
git commit -m "Initial commit: scaffold MyApp"
Step 5 — Connect to GitLab and Push
git remote add origin https://gitlab.company.com/team/MyApp.git
# Rename branch to main if needed
git branch -M main
git push -u origin main
After this, every future push on this branch is just git push.
git push -u origin main sets origin/main as the upstream for your local branch. After that, Git knows where to push/pull without specifying it each time.Scenario
A team shares one GitLab repository. Everyone needs to work on their own features without breaking each other's work.
The Golden Rules
- Never commit directly to
main - Always pull before starting work
- One feature = one branch = one Merge Request
- Small, frequent commits — easier to review and revert
GitHub Flow — Daily Workflow
# 1. Start from latest main
git switch main
git pull
# 2. Create your feature branch
git switch -c feature/user-auth
# 3. Work, commit often
git add -p
git commit -m "Add JWT authentication middleware"
# 4. Keep in sync with main daily
git fetch origin
git rebase origin/main
# 5. Push and open a Merge Request
git push -u origin feature/user-auth
After MR is approved and merged:
git switch main
git pull
git branch -d feature/user-auth # clean up local branch
GitFlow Branch Naming
| Branch type | Format | Example |
|---|---|---|
| Feature | feature/short-description | feature/user-auth |
| Release | release/version | release/1.2.0 |
| Hotfix | hotfix/short-description | hotfix/login-crash |
Common Team Scenarios
"I committed to the wrong branch"
git log --oneline -5
git switch feature/correct-branch
git cherry-pick <commit-hash>
git switch wrong-branch
git reset --hard HEAD~1 # only if not pushed yet
"My branch is 10 commits behind main"
git fetch origin
git rebase origin/main
git push -f origin feature/my-branch
Scenario
Two people edited the same lines in the same file. Git can't auto-merge — it needs you to decide what the final result should be.
Reading Conflict Markers
<<<<<<< HEAD
function getUser(id) {
return db.users.find(id);
}
=======
async function getUser(id) {
return await db.users.findById(id);
}
>>>>>>> feature/async-refactor
| Marker | Meaning |
|---|---|
<<<<<<< HEAD | Start of your version |
======= | Divider between the two versions |
>>>>>>> branch-name | End of the incoming version |
Delete the markers and keep the correct final code — often a combination of both sides.
Resolving a Merge Conflict
git status # shows which files have conflicts
# Edit each conflicted file
git add src/users/service.js # mark as resolved
git merge --continue # or: git commit
To abort and go back: git merge --abort
Resolving a Rebase Conflict
git fetch origin
git rebase origin/main
# Git pauses on each conflicted commit
# Edit the file, then:
git add src/users/service.js
git rebase --continue
After the rebase completes, force-push your branch:
git push -f origin feature/my-branch
main or develop.Conflict in a Lock File (package-lock.json, yarn.lock, etc.)
# Don't manually merge lock files — accept one side then regenerate
git checkout --ours package-lock.json
# or
git checkout --theirs package-lock.json
# Regenerate
npm install
git add package-lock.json
git rebase --continue
Prevention
- Rebase onto
mainfrequently — the longer you diverge, the bigger the conflict - Communicate — if two people are touching the same area, coordinate
- Keep PRs small — large PRs have more surface area for conflicts
Quick Decision Guide
| What happened | Not pushed yet | Already pushed |
|---|---|---|
| Wrong commit message | git commit --amend | Avoid — others may have pulled |
| Committed wrong files | git reset HEAD~1 | git revert <hash> |
| Committed to wrong branch | cherry-pick + reset | git revert + new MR |
| Discard all local changes | git restore . | — |
| Accidentally deleted a file | git restore <file> | — |
Fix the Last Commit Message (Not Pushed)
git commit --amend -m "Correct message here"
Undo the Last Commit — Keep Changes Staged
git reset --soft HEAD~1
Undo the Last Commit — Keep Changes Unstaged
git reset HEAD~1
Undo the Last Commit — Discard Everything
git reset --hard HEAD~1
--hard permanently destroys your changes. Only use it when you're certain.Revert a Commit That's Already Pushed
git log --oneline -10
git revert a1b2c3d # creates a new "Revert..." commit
git push
Discard Uncommitted Changes
git restore src/users/service.js # one file
git restore . # everything
Recover a "Lost" Commit with Reflog
git reflog
# Find the hash of the lost commit
git cherry-pick f6e5d4c # bring it back
Remove a File from Git Without Deleting It Locally
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "Stop tracking .env"
Scenario
You're mid-feature when something urgent comes up. You're not ready to commit, but you can't leave the working directory dirty.
Basic Stash
git stash # save tracked changes
git stash -u # also include untracked (new) files
Give Your Stash a Name
git stash push -m "WIP: token validation logic, halfway done"
Full Workflow
# Mid-feature, urgent task arrives
git stash push -m "WIP: token validation"
git switch main
git pull
git switch -c hotfix/login-crash
# ... fix, commit, push ...
# Return to your feature
git switch feature/user-auth
git stash pop
List, Apply, Drop
git stash list # see all stashes
git stash pop # apply most recent AND remove from list
git stash apply # apply most recent, keep in list
git stash apply stash@{1} # apply a specific stash by index
git stash drop stash@{1} # delete a specific stash
git stash clear # delete ALL stashes
Inspect a Stash Before Applying
git stash show stash@{0} # summary
git stash show -p stash@{0} # full diff
WIP Commit as an Alternative
# Create a temporary commit
git add -A
git commit -m "WIP: do not merge"
# When you come back, undo it
git reset HEAD~1 # keeps your changes unstaged
git log — useful for long-running work-in-progress.Scenario
You're working on feature/user-auth with uncommitted changes. A critical bug is reported in production — it needs to be fixed and deployed immediately.
Step 1 — Stash Your In-Progress Work
Don't commit half-done work. Stash it cleanly so you can come back to it later.
git stash push -m "WIP: user auth - mid refactor"
git status # confirm working directory is clean
Step 2 — Switch to Main and Pull the Latest
git switch main
git pull # make sure you have the latest production code
Step 3 — Create a Hotfix Branch
Always branch off main for a hotfix — not off your feature branch.
git switch -c hotfix/fix-login-crash
Step 4 — Fix, Commit, Push
# Make the fix
git add path/to/fixed-file.cs
git commit -m "fix: prevent null ref crash on login page"
git push -u origin hotfix/fix-login-crash
Open a Pull Request / Merge Request targeting main. Get it reviewed and merged.
Step 5 — Return to Your Feature and Rebase
Once the hotfix is merged, update your feature branch so it's built on top of the fix, not the old code.
git switch feature/user-auth
git stash pop # restore your WIP changes
git rebase origin/main # replay your feature commits on top of the hotfix
stash pop causes conflicts, resolve them the same way as a merge conflict — edit the files, then run git add <file> and git stash drop to finish.main to fix production. A hotfix branch from main is always cleaner and safer.Scenario
You've been working on a feature for a day and your branch has commits like: "WIP", "fix typo", "actually fix it", "add tests", "test fix". Before raising a PR you want these squashed into one clean commit.
Step 1 — Check How Many Commits to Squash
git log --oneline origin/main..HEAD # shows commits on your branch not yet in main
Say you see 5 commits. You'll squash all 5 into 1.
Step 2 — Start Interactive Rebase
git rebase -i HEAD~5 # replace 5 with your actual commit count
Your editor opens with a list like:
pick a1b2c3d feat: add login page skeleton
pick b2c3d4e WIP
pick c3d4e5f fix typo
pick d4e5f6a actually fix it
pick e5f6a7b add tests
Step 3 — Mark Commits to Squash
Keep the first commit as pick. Change the rest to s (squash) — they'll be merged into the first:
pick a1b2c3d feat: add login page skeleton
s b2c3d4e WIP
s c3d4e5f fix typo
s d4e5f6a actually fix it
s e5f6a7b add tests
Save and close the editor. Git opens a second editor to write the final combined commit message — replace everything with one clean message:
feat: add login page with form validation and unit tests
Step 4 — Force Push Your Branch
You've rewritten history on your local branch — you need to force push. This is safe on your own feature branch that no one else is working on.
git push --force-with-lease
--force-with-lease is safer than --force — it refuses to push if someone else has pushed to the branch since you last fetched, preventing you from accidentally overwriting their commits.main or any shared branch — it rewrites history and causes problems for everyone.