Skip to main content

Documentation Index

Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt

Use this file to discover all available pages before exploring further.

Git Collaboration

Git Distributed Model Git’s distributed nature makes team collaboration seamless. Learn to work with remote repositories, pull requests, and code review workflows.

Remote Repositories

A remote is a version of your repository hosted elsewhere (GitHub, GitLab, Bitbucket). Think of it as a shared bulletin board — everyone on the team can post their changes and read everyone else’s. Your local repo is your private workspace; the remote is where work becomes visible to the team.

Viewing Remotes

# List remotes
git remote

# List with URLs
git remote -v

# Show remote details
git remote show origin

Adding Remotes

# Add a remote
git remote add origin https://github.com/username/repo.git

# Add multiple remotes
git remote add upstream https://github.com/original/repo.git

# Change remote URL
git remote set-url origin git@github.com:username/repo.git

Cloning Repositories

# Clone via HTTPS
git clone https://github.com/username/repo.git

# Clone via SSH
git clone git@github.com:username/repo.git

# Clone specific branch
git clone -b develop https://github.com/username/repo.git

# Shallow clone (faster, less history)
git clone --depth 1 https://github.com/username/repo.git

Pushing Changes

# Push to remote
git push origin main

# Push new branch
git push -u origin feature/new-feature
# -u sets upstream tracking

# Push all branches
git push origin --all

# Push tags
git push origin --tags

# Force push (DANGEROUS!)
git push --force origin main
Never force push to shared branches! It rewrites history and can cause problems for teammates.Safe alternative:
git push --force-with-lease origin main

Pulling Changes

# Fetch + merge
git pull origin main

# Fetch only (no merge)
git fetch origin

# Pull with rebase
git pull --rebase origin main

# Pull all branches
git fetch --all

Pull vs Fetch

CommandWhat it does
git fetchDownloads changes but does not merge — safe, non-destructive
git pullDownloads AND merges (fetch + merge) — convenient but can surprise you
# Recommended workflow for cautious developers: fetch first, review, then merge.
# This gives you a chance to see what changed before integrating.
git fetch origin
git log origin/main  # Review what your teammates pushed
git diff main..origin/main  # See the actual changes
git merge origin/main  # Merge when you're ready
Practical tip: Many experienced developers prefer git pull --rebase over a plain git pull. Instead of creating a merge commit every time you sync with main, rebase replays your local commits on top of the remote changes — keeping a cleaner, linear history. You can make this the default: git config --global pull.rebase true.

Working with Forks

Forking Workflow

1

Fork on GitHub

Click “Fork” button on the repository
2

Clone your fork

git clone https://github.com/YOUR-USERNAME/repo.git
cd repo
3

Add upstream remote

git remote add upstream https://github.com/ORIGINAL-OWNER/repo.git
git remote -v
4

Keep fork updated

git fetch upstream
git checkout main
git merge upstream/main
git push origin main

Pull Requests (PRs)

Pull requests are how you propose changes to a repository.

Creating a Pull Request

# 1. Create feature branch
git checkout -b feature/add-search

# 2. Make changes and commit
git add .
git commit -m "feat: add search functionality"

# 3. Push to your fork
git push origin feature/add-search

# 4. Go to GitHub and click "Create Pull Request"

PR Best Practices

Small PRs

Easier to review, faster to merge

Clear Description

Explain what and why, not just how

Link Issues

Reference related issues: “Fixes #123”

Update Branch

Keep PR branch updated with main

PR Template Example

## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
How to test these changes

## Screenshots
If applicable

## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex code
- [ ] Documentation updated
- [ ] Tests added/updated

Code Review

As a Reviewer

# Fetch PR branch
git fetch origin pull/123/head:pr-123
git checkout pr-123

# Test locally
npm install
npm test

# Leave comments on GitHub
Good Review Comments:
  • ✅ “Consider using a Map here for O(1) lookup”
  • ✅ “This could cause a race condition if…”
  • ✅ “Great solution! Minor: variable name could be more descriptive”
Bad Review Comments:
  • ❌ “This is wrong”
  • ❌ “Why did you do it this way?”
  • ❌ “I would have done it differently”

As an Author

# Address review comments
git add .
git commit -m "refactor: apply review suggestions"
git push origin feature/add-search

# Squash commits before merge (optional)
git rebase -i main

Collaboration Workflows

Centralized Workflow

# Everyone works on main
git clone https://github.com/team/repo.git
git pull origin main
# ... make changes ...
git add .
git commit -m "feat: add feature"
git pull origin main  # Get latest
git push origin main
Best for: Small teams, simple projects

Feature Branch Workflow

# Each feature gets its own branch
git checkout -b feature/user-profile
# ... work ...
git push origin feature/user-profile
# Create PR
# After review and merge
git checkout main
git pull origin main
git branch -d feature/user-profile
Best for: Most teams, medium to large projects

Forking Workflow

# Fork repo on GitHub
git clone https://github.com/YOUR-USERNAME/repo.git
git remote add upstream https://github.com/ORIGINAL/repo.git

# Create feature
git checkout -b feature/improvement
# ... work ...
git push origin feature/improvement
# Create PR from your fork to original repo
Best for: Open source projects, external contributors

Handling Common Scenarios

Syncing Fork with Upstream

# Fetch upstream changes
git fetch upstream

# Merge into your main
git checkout main
git merge upstream/main

# Push to your fork
git push origin main

Updating PR Branch

# Method 1: Merge
git checkout feature/my-pr
git merge main
git push origin feature/my-pr

# Method 2: Rebase (cleaner history)
git checkout feature/my-pr
git rebase main
git push --force-with-lease origin feature/my-pr

Resolving PR Conflicts

# Update your branch
git checkout feature/my-pr
git fetch origin
git merge origin/main

# Resolve conflicts
# ... edit files ...
git add .
git commit

# Push resolution
git push origin feature/my-pr

SSH vs HTTPS

HTTPS

git clone https://github.com/username/repo.git
Pros: Easy setup, works everywhere
Cons: Need to enter credentials (or use credential helper)

SSH

git clone git@github.com:username/repo.git
Pros: No password needed, more secure
Cons: Requires SSH key setup

Setting Up SSH

# Generate SSH key
ssh-keygen -t ed25519 -C "your.email@example.com"

# Start SSH agent
eval "$(ssh-agent -s)"

# Add key
ssh-add ~/.ssh/id_ed25519

# Copy public key
cat ~/.ssh/id_ed25519.pub
# Add to GitHub: Settings → SSH Keys → New SSH Key

Tags and Releases

Creating Tags

# Lightweight tag
git tag v1.0.0

# Annotated tag (recommended)
git tag -a v1.0.0 -m "Release version 1.0.0"

# Tag specific commit
git tag -a v0.9.0 abc123

# List tags
git tag

# Push tags
git push origin v1.0.0
git push origin --tags  # All tags

Semantic Versioning

Semantic versioning (SemVer) communicates the nature of changes to consumers of your software. It is a contract: “I promise this version is safe to upgrade to (or not) based on the version number.”
v1.2.3
│ │ └─ Patch: Bug fixes (safe to upgrade -- no API changes)
│ └─── Minor: New features (safe to upgrade -- backward compatible, nothing removed)
└───── Major: Breaking changes (consumers must review -- something they depend on changed)
Real-world example: When a library bumps from v2.3.1 to v3.0.0, your automated dependency updater (Dependabot, Renovate) will flag it as a major change requiring manual review. A bump from v2.3.1 to v2.4.0 is usually auto-merged. This works because the community trusts SemVer as a reliable signal — which is why violating it (shipping breaking changes in a minor version) is considered a serious breach of trust in the open source world.

Practical Examples

Example 1: Contributing to Open Source

# 1. Fork repo on GitHub

# 2. Clone your fork
git clone https://github.com/YOUR-USERNAME/awesome-project.git
cd awesome-project

# 3. Add upstream
git remote add upstream https://github.com/ORIGINAL/awesome-project.git

# 4. Create branch
git checkout -b fix/typo-in-readme

# 5. Make changes
echo "Fixed typo" >> README.md
git add README.md
git commit -m "docs: fix typo in README"

# 6. Push to your fork
git push origin fix/typo-in-readme

# 7. Create PR on GitHub

# 8. Keep fork updated
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

Example 2: Team Feature Development

# 1. Get latest code
git checkout main
git pull origin main

# 2. Create feature branch
git checkout -b feature/payment-integration

# 3. Work and commit
git add .
git commit -m "feat: add Stripe payment integration"

# 4. Push and create PR
git push -u origin feature/payment-integration

# 5. Address review comments
git add .
git commit -m "refactor: apply code review suggestions"
git push origin feature/payment-integration

# 6. After merge, clean up
git checkout main
git pull origin main
git branch -d feature/payment-integration

Troubleshooting

”Your branch is behind ‘origin/main’"

git pull origin main

"Your branch has diverged"

# Option 1: Merge
git pull origin main

# Option 2: Rebase
git pull --rebase origin main

"Permission denied (publickey)"

# Check SSH key
ssh -T git@github.com

# If fails, add SSH key to GitHub
cat ~/.ssh/id_ed25519.pub

"Failed to push some refs”

# Someone pushed before you
git pull origin main
git push origin main

Best Practices

Always get latest changes before pushing
git pull origin main
git push origin main
  • One feature/fix per PR
  • Easier to review
  • Faster to merge
  • What changed
  • Why it changed
  • How to test
  • Screenshots if UI changes
  • Be constructive
  • Ask questions
  • Suggest improvements
  • Approve when ready

Key Takeaways

  • Remotes connect local and hosted repositories
  • Pull requests enable code review
  • Forks allow external contributions
  • SSH keys simplify authentication
  • Good collaboration requires communication

Interview Deep-Dive

Strong Answer:
  • The blast radius: the remote main branch now points to an older commit. The 5 overwritten commits are no longer reachable from any remote ref. Developers who already pulled those commits have them locally, but their next git pull will see a diverged history. If anyone had branched off the lost commits, their branches now have orphaned parents that do not exist on the remote.
  • Immediate recovery: any developer who has the original main (before the force push) can restore it. They run git log origin/main to confirm they still have the old tip (they might need to check their reflog with git reflog show origin/main). Then they push the correct history back: git push origin main (or --force-with-lease if the remote has already been updated by others).
  • If no developer has the original commits locally, check the hosting platform. GitHub retains force-pushed commits for at least 90 days — you can find the old tip SHA in the repository’s events audit log or by using the GitHub API to inspect the push event. With the SHA, you can reset main to the correct commit.
  • Downstream cleanup: all three developers should git fetch origin and then git reset --hard origin/main on their local main (or git pull --rebase). Developers with feature branches based on the lost commits should rebase onto the restored main.
  • Prevention: enable branch protection on main. In GitHub, this means requiring PR reviews and disabling force pushes entirely. In GitLab, protect the branch at the project settings level. This is the single most important repository configuration for any team.
Follow-up: How do you educate the team without shaming the junior developer?I frame it as a systems problem, not a people problem. The question is not “why did they force push” but “why does our repository allow force pushes to main?” I enable branch protection, do a brief team demo of --force-with-lease vs --force, and add a hook or CI check that rejects force pushes to protected branches. The incident becomes a learning moment that results in a concrete process improvement, not blame.
Strong Answer:
  • As a maintainer, I have two options. First, I ask the contributor to resolve the conflicts themselves. This is the preferred approach because it keeps the contributor engaged and they understand their code best. I leave a PR comment: “This PR has conflicts with main. Could you rebase onto the latest main?” Most active contributors will do this within a day.
  • Second, if the contributor is unresponsive or unfamiliar with conflict resolution, I can resolve it myself. I fetch their branch: git fetch origin pull/123/head:pr-123, check it out, rebase it onto main (git rebase main), resolve conflicts, and push to the contributor’s fork (if they enabled “allow edits from maintainers”) or to a new branch and update the PR.
  • A third, more common pattern in larger projects: I use GitHub’s “Update branch” button (which merges main into the PR branch) or the CLI equivalent. This creates a merge commit on the PR branch, which is less clean but avoids the contributor needing to force-push.
  • The key consideration is the project’s merge strategy. If the project uses squash merge (most open-source projects do), the intermediate conflict resolution commits do not matter because they will be squashed into one commit on merge. If the project uses rebase-and-merge, the commit history should be clean.
Follow-up: The contributor accidentally committed sensitive credentials in an early commit. The PR has 10 commits. How do you handle this before merging?I immediately ask the contributor to rotate the credential, regardless of whether we can clean the history. Then I ask them to do an interactive rebase (git rebase -i) to remove or amend the commit that introduced the credential, and force-push the cleaned branch. I verify the cleaned PR no longer contains the credential with a search through the diff. If the PR was open for any length of time, I assume the credential was scraped by bots — GitHub public repos are actively scanned. The credential must be rotated regardless of whether we successfully clean the Git history.
Strong Answer:
  • git pull is syntactic sugar for git fetch + git merge (or git fetch + git rebase if configured with pull.rebase=true). Functionally, they produce the same result. The difference is control and visibility.
  • git fetch + manual merge gives you an inspection step. After fetching, you can run git log origin/main to see what your teammates pushed, git diff main..origin/main to review the actual changes, and then decide how to integrate. This is valuable when working on a shared branch where unexpected changes could conflict with your in-progress work.
  • git pull is more convenient for routine syncing. If you are on a personal feature branch and just want to stay up to date with main, git pull --rebase origin main is a single command that fetches and replays your commits on top.
  • In my workflow, I use git fetch + manual inspection when integrating from a branch I do not control (e.g., syncing a fork with upstream, or pulling from a coworker’s branch). I use git pull --rebase for routine updates to my own feature branches from main. I configure pull.rebase=true globally so that accidental git pull without flags does not create unnecessary merge commits.
  • The most important thing is understanding that git pull can create merge commits that pollute your branch history. Teams that care about clean history should default to git pull --rebase and only use merge pulls when they intentionally want to record a merge point.
Follow-up: You run ‘git pull —rebase’ and hit a conflict on every single commit in your branch. What is happening and how do you handle it?During rebase, Git replays each of your commits one at a time on top of the updated base. If the base changed significantly (e.g., a large refactor on main), each replayed commit can conflict with the new base. The worst case is resolving the same conflict repeatedly across commits. The fix is git rerere (reuse recorded resolution): enable it with git config rerere.enabled true. Once you resolve a conflict, rerere records the resolution. If the same conflict pattern appears in subsequent commits, Git applies the recorded resolution automatically. For a branch with many commits, I would also consider squashing into fewer commits before rebasing to reduce the number of potential conflict points.

Next: Git Advanced Topics →