Git 102 Collaboration

Introduction

BLAST! I didn’t see you there! You know…you’re interrupting me from very important business.

What’d you say?

Oh. You got a little taste of Sith power and you’re back for more Git brain dumping? Fine. You came to the right place, because I have a LOT more to say.

Darth Awesome

You probably have the mind of a sieve, so I’ll give you a little recap. Here goes: Git is a way to manage commits. A commit is a snapshot of your files at that point in time. Branches are pointers to commits. HEAD tells Git where you are. If you mess up, reflog is your friend. Reset is useful too.

But here’s the thing. You’ve been playing in a sandbox. Alone. Like a loser hermit on Dagobah. Real development means working with other people. Other messy, chaotic, “I accidentally pushed code to main” people. That means remotes, pulling, fetching, stashing, merge conflicts, pull requests, and diffs.

Mando Castle

This course is about surviving collaboration. If the [Git Primer course]( was about learning to swing a lightsaber, this course is about learning to fight in freaking formation without slicing off your buddy’s nipples.

Here’s how this all plays out

I’m going to school you on:

  1. Remotes
  2. Fetching
  3. Pulling
  4. Diffs
  5. Stashing
  6. Merge conflicts
  7. Pull Requests on GitHub
  8. More cheat sheet shenanigans
  9. A picture of a tortoise

Remotes

In the Git Primer, everything was local. Your commits lived on your machine. A remote is a copy of your repository that lives somewhere else—usually on GitHub, GitLab, Bitbucket, or someplace like that. Think of it like a library. You check out a book from the library (clone a repo) and can scribble notes in your book like a little villain all you want. When you’re done, you return it to the library (push to the remote) so others can see your pen-wrought phalluses. The remote is just the library’s address.

Darth Awesome Rules

When you clone a repository, Git automatically creates a remote called origin. That’s just a nickname for the URL of the remote repo. You can have multiple remotes, but most of you will only ever deal with origin.

git remote -v

That command shows you the remotes you have configured. You’ll see something like:

origin https://github.com/darth-awesome/sexy-hutt.git (fetch) origin https://github.com/darth-awesome/sexy-hutt.git (push)

That’s the remote name (origin), the URL to the remote repository (note the .git extension), and what the remote is used for. Fetch, indicates the URL you fetch code from. Push is where your code goes when you send it to that remote.

Your local branches have something called remote-tracking branches that act as bookmarks for where the remote’s branches were the last time you communicated with it. Let’s say you have a branch called mynock-panties. If that branch was fetched from the remote, you would also have origin/mynock-panties. That prefixed branch is the remote-tracking branch. You cannot directly commit to those—they’re read-only references that get updated when you fetch or pull.

Mynock Panties

Fetching

Let’s say your teammate Darth Incompetent pushed some commits to the remote. Those commits exist on GitHub but your local machine doesn’t know about them yet. Your local repo is blissfully ignorant.

‘git fetch’ goes to the remote, downloads any new commits, branches, and tags that you don’t have yet, and updates your remote-tracking branches. That’s it. It does not touch your working directory. It does not change your local branches. It does not merge anything.

git fetch origin

After fetching, your origin/mynock-panties might be 3 commits ahead of your local mynock-panties. You can inspect what changed before deciding to do anything about it. This is the cautious, “I want to see what those idiots did before I let it anywhere near my code” approach.

Fetch is always safe. It never modifies your precious local work. You can fetch as often as you want and nothing bad will happen. I mean…you might choke on a chicken wing or something, but your code is safe.

Here are some useful commands after a fetch:

  • ‘git log origin/mynock-panties’ — see what commits are on the remote’s mynock-panties branch.
  • ‘git diff mynock-panties origin/mynock-panties’ — compare your local branch with the remote’s branch.
  • ‘git merge origin/mynock-panties’ — merge the remote changes into your local branch.

Darth Kiddie Pool

Pulling

If git fetch is sissy cautious carefulness, git pull is kicking down the door. A pull is literally just a fetch followed immediately by a merge. Nothing really magical about it. It downloads the remote changes AND merges them into your current branches in one command.

git pull origin mynock-panties

This fetches from origin and merges origin/mynock-panties into whatever branch you currently have checked out. If there are no conflicts, Git handles it automatically and life is good.

If there ARE conflicts…well, I’ll show you how to solve that horror show in the Merge Conflicts section.

Now, I specified mynock-panties in that command. That’s the branch you are fetching from the remote. You might be working on your local mynock-panties branch or you could be working on your hutt-dimples branch. It doesn’t matter. If you do that command above, it is going to fetch mynock-panties and merge it into whatever branch you are working on in your working directory.

Hutt Panties

pull --rebase

This is a variation worth knowing. git pull --rebase is helpful but can be a bit problematic at the same time if you don’t know what you’re doing—which you probably don’t. Basically, this fetches a branch, shoves your changes off to the side like an unwanted child, puts the changes from the fetched branch in place, and then sprinkles your commits on top of it.

The result is a cleaner, linear history without merge commits.

git pull --rebase origin mynock-panties

But the simplicity can breed chaos. Rebasing rewrites commit history. You know all of those fancy hashes associated with commits? Rebasing causes hashes to change because where commits point is different and the snapshot is different. If you absorb anything into that tiny mind of yours, remember this: Never rebase commits that you have already pushed to a remote. That path leads to suffering.

Diffs

A diff shows you what changed between two states. It’s Git’s way of saying “SEE! This is what is different.” You’ll see lines prefixed with + (added) and - (removed). Understanding diffs is critical because they’re the language of the code review.

Common Dif commands

Changes in your working directory (not yet staged):

git diff

Changes that are staged (ready to commit):

git diff --staged

Differences between two branches:

git diff hutt-dimples mynock-panties

Differences between two specific commits:

git diff a12cf93e c7af36de

See what changed in a single file:

git diff -- path/to/file.txt

Reading a diff

Hahaha if you read diff really fast it looks like DILF.

Darth Dilf

This is what a diff can look like:

git diff HEAD~1 HEAD
diff --git a/app/assets/stylesheets/themed/components/_fixes.scss b/app/assets/stylesheets/themed/components/_fixes.scss
index 33cf7224..2e49e9be 100644
--- a/app/assets/stylesheets/themed/components/_fixes.scss
+++ b/app/assets/stylesheets/themed/components/_fixes.scss
@@ -9,6 +9,16 @@
     border-bottom: 0;
 }

-.list-group-item-action a {
-}
+.list-group-item-action > a {
+    color: var(--bs-list-group-action-color);
+    text-decoration: none;
+
+    &:hover, &:focus {
+        color: var(--bs-list-group-action-hover-color);
+        text-decoration: none;
+    }
+}
+
 .card dd {
     color: $dim-text-color;

Anyway, a diff output looks intimidating if you are a pansy, but it’s really not. Here’s what the pieces mean:

  • ---a/file.txt and +++b/file.txt tell you which file is being compared.
  • @@ lines (called “hunks”) tell you where in the file the changes are. The numbers indicate line ranges.
  • Lines starting with - (usually shown in red) are removed.
  • Lines starting with + (usually shown in green) are added.
  • Lines with no prefix are unchanged context lines to help you orient yourself.

If you find the terminal diff hard to read, VS Code and GitHub both show diffs with nice color-coding and side-by-side views. As much of a bada** that I am, I prefer those views.

Stashing

Picture this: you’re halfway through writing what you think is a good feature. Files are changed. Things are messy. Then your Sith Master yells at you to fix a critical bug on mynock-panties RIGHT NOW. You can’t switch branches because Git will get all whiny about your uncommitted changes. You don’t want to commit half-finished garbage. What do you do?

You stash it.

git stash

This takes all of your lousy uncommitted changes (both staged and unstaged) and saves them on a stack. Your working directory is now clean, as if you never made those changes. You can switch branches, do your emergency fix, and come back.

git stash

When you are ready to get your changes back:

git stash pop

This reapplies your stashed changes and removes the stash from the stack. If you want to apply the stash but keep it on the stack (in case you want to apply it elsewhere too), use git stash apply instead.

Stash Management

gith stash

You can have multiple stashes. They stack up like my trophies.

Command Description
git stash list Shows all your stashes.
git stash show Shows a summary of the most recent stash’s changes.
git stash show -p Shows the full diff of the stash.
git stash drop Deletes the most recent stash without applying it.
git stash clear Nukes ALL stashes. Gone. Obliterated.
git stash push -m “description” Gives your stash a name so you remember what it is.

If you want to be smart like me, you should give your stashes a message. Future you will think past you is a mighty fine being when future you types git stash list and sees how you’ve described all of your lost code children.

Merge conflicts

Ah, merge conflicts. The thing that makes grown developers cry into their keyboards. Let me demystify this for you so you aren’t one of those losers because it's really not that scary once you understand what's happening. A merge conflict happens when Git tries to combine two sets of changes and can't figure out what to do because both sides modified the same part of the same file. Git is smart, but it's not me. It doesn't know if you want your version, their version, or some combination. So it stops and asks you to decide.

When do conflicts happen?

Conflicts can happen during:

  • git merge — merging one branch into another

  • git pull — because pull does a merge under the hood

  • git rebase — replaying commits on a new base

  • git stash pop — if your stash conflicts with changes made while you were away

  • git cherry-pick — applying a specific commit to a different branch

What a conflict looks like

When a conflict occurs, Git marks up the conflicted file with conflict markers. Open the file and you'll see something like this:

<<<<<<< HEAD
const greeting = "Hello from the Dark Side";
=======
const greeting = "Hello from the Light Side";
>>>>>>> feature/jedi-nonsense

Here's what those markers mean:

  • <<<<<<< HEAD — this is the start of YOUR changes (the branch you're merging INTO)

  • ======= — the divider between the two versions

  • >>>>>>> feature/jedi-nonsense — this is the end of THEIR changes (the branch being merged)

How to resolve a conflict

Resolving a conflict is a three-step process:

  1. Edit the file. Remove the conflict markers and decide what the final code should look like. Maybe you keep your version. Maybe you keep theirs. Maybe you combine both. Maybe you rewrite it entirely. You're the boss.

  2. Stage the resolved file. Run git add <file> to tell Git you've resolved the conflict in that file.

  3. Complete the merge. Run git commit to finish the merge. Git will create a merge commit with a default message about the merge.

If you're using VS Code, it has a fantastic built-in merge conflict editor. It highlights the conflicts and gives you buttons to accept the current change, incoming change, both, or compare them side by side. I won't judge you for using it. -> If you panic during a merge conflict, you can always abort: git merge --abort This resets everything to before the merge started. Breathe.) <-

Pull Requests on GitHub

git hub

Now we get to the big one. Pull Requests (PRs) are not a Git feature. They're a GitHub feature (and GitLab calls them Merge Requests, because they like being different). But they are absolutely central to how modern teams collaborate, so you need to understand them. A Pull Request is a formal way of saying: "Hey, I've got some changes on my branch and I'd like to merge them into another branch. Come look at my code before I break everything!"

The typical PR workflow

Here's how it usually goes, step by step:

  1. Create a branch from main (or whatever your team's base branch is).
git checkout -b feature/orv-is-so-sexy
  1. Do your work. Make commits. Be a good developer and write meaningful commit messages, not "fixed stuff" or "asdfgh".
git add .
git commit -m "Added some fancy hats for Orv’s perfect head"
  1. Push your branch to the remote.
git push origin feature/orv-is-so-sexy
  1. Open a Pull Request on GitHub. Go to your repo on github.com. GitHub usually shows a banner saying "You recently pushed a branch. Want to create a pull request?" Click the button.

  2. Fill out the PR. Give it a clear title and description. Explain WHAT you changed and WHY. Link any relevant issues. Assign reviewers.

  3. Code review happens. Your teammates review your diff (see, diffs matter!), leave comments, request changes, or approve.

  4. Address feedback. Make more commits on your branch to fix whatever they complained about. Push again. The PR updates automatically.

  5. Merge the PR. Once approved, someone clicks the merge button on GitHub. Your changes are now part of the target branch.

  6. Delete the branch. Clean up after yourself. GitHub offers a button to delete the branch after merging. Use it.

Merge strategies on GitHub

When you hit that merge button on a PR, GitHub gives you three options: * Create a merge commit — the default. Creates a merge commit that joins both branches. Preserves full history. The graph shows the branch and merge points. * Squash and merge — takes all of your branch's commits and squashes them into a single commit on the target branch. Great if your branch has a lot of messy "WIP" and "fix typo" commits you don't want in the main history. * Rebase and merge — replays your commits one by one on top of the target branch. Creates a linear history with no merge commit. Each of your commits is preserved individually. Which one should you use? It depends on your team's conventions. Many teams use squash-and-merge for feature branches so that main has one clean commit per feature. Ask your team. If nobody has an opinion, squash-and-merge is a safe default.

Keeping your branch up to date

While you're working on your feature branch, other people are merging things into main. Your branch falls behind. Before merging your PR, you often need to update your branch with the latest changes.

Option A: Merge main into your branch

git checkout feature/orv-is-so-sexy
git pull origin main

Option B: Rebase your branch onto main

git checkout feature/orv-is-so-sexy
git fetch origin
git rebase origin/main

Option A is simpler and safer. Option B gives a cleaner history but requires a force push (git push --force-with-lease) since you're rewriting your branch's history. If other people are also working on your branch, rebase can cause problems. Communicate with your team.

git hub

More cheat sheet shenanigans

Here are the most common commands for working with others. Some of these were covered in this course, some are extras for you to explore on your own. I'm not your mother.

Command What it does
git remote -v Lists your configured remotes and their URLs.
git fetch origin Downloads new commits from the remote without changing your local branches. Safe.
git pull origin main Fetches and merges remote main into your current branch.
git pull --rebase origin main Fetches and rebases your commits on top of remote main. Cleaner history.
git push origin <branch> Uploads your local commits to the remote branch.
git push --force-with-lease Force pushes safely — only if no one else has pushed since you last fetched.
git diff Shows unstaged changes in your working directory.
git diff --staged Shows changes in the staging area (ready to commit).
git diff main feature/x Shows differences between two branches.
git stash Saves uncommitted changes to a stack and cleans your working directory.
git stash pop Reapplies the most recent stash and removes it from the stack.
git stash list Lists all stashes.
git stash push -m "msg" Stashes with a descriptive message.
git stash drop Deletes the most recent stash.
git merge <branch> Merges the target branch into your current branch.
git merge --abort Cancels a merge in progress. Returns to pre-merge state.
git rebase <branch> Replays your commits on top of the target branch.
git rebase --abort Cancels a rebase in progress.
git cherry-pick <hash> Applies a specific commit to your current branch.
git log --oneline --graph Shows a compact visual history of your branch.

A picture of a tortoise

git hub

Take the exam

Please log in to take this course's exam