> ## 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

> Work with teams using remote repositories

# Git Collaboration

<img src="https://mintcdn.com/devweeekends/emzPt-9B_R8UKdqm/images/courses/devops-tools/git-distributed.svg?fit=max&auto=format&n=emzPt-9B_R8UKdqm&q=85&s=693ae06ed73ae6da30e21df2e7726b75" alt="Git Distributed Model" width="1080" height="1080" data-path="images/courses/devops-tools/git-distributed.svg" />

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

```bash theme={null}
# List remotes
git remote

# List with URLs
git remote -v

# Show remote details
git remote show origin
```

### Adding Remotes

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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
```

<Warning>
  **Never force push to shared branches!** It rewrites history and can cause problems for teammates.

  Safe alternative:

  ```bash theme={null}
  git push --force-with-lease origin main
  ```
</Warning>

***

## Pulling Changes

```bash theme={null}
# 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

| Command     | What it does                                                                |
| ----------- | --------------------------------------------------------------------------- |
| `git fetch` | Downloads changes but does not merge -- safe, non-destructive               |
| `git pull`  | Downloads AND merges (`fetch` + `merge`) -- convenient but can surprise you |

```bash theme={null}
# 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
```

<Tip>
  **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`.
</Tip>

***

## Working with Forks

### Forking Workflow

<Steps>
  <Step title="Fork on GitHub">
    Click "Fork" button on the repository
  </Step>

  <Step title="Clone your fork">
    ```bash theme={null}
    git clone https://github.com/YOUR-USERNAME/repo.git
    cd repo
    ```
  </Step>

  <Step title="Add upstream remote">
    ```bash theme={null}
    git remote add upstream https://github.com/ORIGINAL-OWNER/repo.git
    git remote -v
    ```
  </Step>

  <Step title="Keep fork updated">
    ```bash theme={null}
    git fetch upstream
    git checkout main
    git merge upstream/main
    git push origin main
    ```
  </Step>
</Steps>

***

## Pull Requests (PRs)

Pull requests are how you propose changes to a repository.

### Creating a Pull Request

```bash theme={null}
# 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

<CardGroup cols={2}>
  <Card title="Small PRs" icon="minimize">
    Easier to review, faster to merge
  </Card>

  <Card title="Clear Description" icon="file-lines">
    Explain what and why, not just how
  </Card>

  <Card title="Link Issues" icon="link">
    Reference related issues: "Fixes #123"
  </Card>

  <Card title="Update Branch" icon="arrows-rotate">
    Keep PR branch updated with main
  </Card>
</CardGroup>

### PR Template Example

```markdown theme={null}
## 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
# 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

```bash theme={null}
git clone https://github.com/username/repo.git
```

**Pros**: Easy setup, works everywhere\
**Cons**: Need to enter credentials (or use credential helper)

### SSH

```bash theme={null}
git clone git@github.com:username/repo.git
```

**Pros**: No password needed, more secure\
**Cons**: Requires SSH key setup

### Setting Up SSH

```bash theme={null}
# 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

```bash theme={null}
# 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)
```

<Tip>
  **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.
</Tip>

***

## Practical Examples

### Example 1: Contributing to Open Source

```bash theme={null}
# 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

```bash theme={null}
# 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'"

```bash theme={null}
git pull origin main
```

### "Your branch has diverged"

```bash theme={null}
# Option 1: Merge
git pull origin main

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

### "Permission denied (publickey)"

```bash theme={null}
# 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"

```bash theme={null}
# Someone pushed before you
git pull origin main
git push origin main
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Pull Before Push" icon="download">
    Always get latest changes before pushing

    ```bash theme={null}
    git pull origin main
    git push origin main
    ```
  </Accordion>

  <Accordion title="Small, Focused PRs" icon="minimize">
    * One feature/fix per PR
    * Easier to review
    * Faster to merge
  </Accordion>

  <Accordion title="Write Good PR Descriptions" icon="file-lines">
    * What changed
    * Why it changed
    * How to test
    * Screenshots if UI changes
  </Accordion>

  <Accordion title="Review Code Thoughtfully" icon="magnifying-glass">
    * Be constructive
    * Ask questions
    * Suggest improvements
    * Approve when ready
  </Accordion>
</AccordionGroup>

***

## 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

<AccordionGroup>
  <Accordion title="A junior developer runs 'git push --force origin main' and overwrites the last 5 commits that three other developers had already pulled. Describe the blast radius and your recovery plan." icon="circle-question">
    **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.
  </Accordion>

  <Accordion title="Your company uses a fork-based workflow for an open-source project. An external contributor's PR has merge conflicts with main. Walk me through the correct way to resolve this -- from your perspective as the maintainer." icon="circle-question">
    **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.
  </Accordion>

  <Accordion title="Explain the difference between 'git pull' and 'git fetch' followed by 'git merge'. When would you prefer one over the other?" icon="circle-question">
    **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.
  </Accordion>
</AccordionGroup>

***

Next: [Git Advanced Topics →](/courses/devops-tools/git-advanced)
