The Only Git Article You'd Ever Need - 2/4
Deeper Into GitHub Collaboration
Now that we’ve covered the foundations of Git and have a basic idea of how we can collaborate with others in a common repository, let’s dive deeper into this “collaboration” aspect.
Professional Git Workflows: Collaboration Done Right
This is where Git transforms from a personal productivity tool into a professional collaboration system. These workflows separate developers who can work effectively in teams from those who create merge conflicts and break production deployments.
Branching: The Most Important Thing You Will Ever Learn
If you take one thing, and only one thing, from these 3 Git articles, let it be this: Branching. This isn’t optional. This isn’t a “nice-to-have.” This is how actual, sane people work.
Why Branch?
Typically, ‘main’/‘master’/‘production’ branch is the one that’s taken as the deployable, stable version of your application. The output of the code in this branch is what your end users interact with. Working directly on this branch is dangerous and can lead to unstable releases and broken features reaching users.
Actual developers never commit incomplete or experimental work directly to main. Instead, they use feature branches to isolate development work, ensuring the main branch remains stable and deployable at all times.
Every new feature, bug fix, experiment, or idea gets its own dedicated branch. This isolation allows you to make radical changes, test different approaches, and work through complex problems without affecting the stable main codebase. If your experimental work doesn’t pan out, you can simply delete the branch without any impact on production code.
git branch <name> & git switch <name> (or git checkout -b <name>): Creating and Switching Branches
Think of your local repository as a branching tree structure. The main branch represents the trunk, and when you create a new branch, you’re creating a new development path that diverges from that main line.
git branch <name>: This command creates a new branch. It’s like telling Git, “Hey, make me a new timeline called<name>, based on where I currently am.” It doesn’t switch you to it.# You're on main (check with git branch) git branch feature/user-profile # Now if you run 'git branch', you'll see both main and feature/user-profile, but you would still be on the main branchgit switch <name>: This command switches your working directory and Git’sHEADpointer to that specified branch. It’s like jumping into that new parallel universe. Your files will literally change to reflect the state of that branch.git switch feature/user-profile # You'll see: "Switched to branch 'feature/user-profile'"git checkout -b <name>: This is the lazy (efficient actually) way to do both in one go. It creates a new branch and immediately switches you to it. Use this because we both know we’re lazy.git checkout -b bugfix/login-page-ui # You'll see: "Switched to a new branch 'bugfix/login-page-ui'"Now, you can happily code away on your new branch, making commits, breaking things, and no one on
mainwill be any the wiser. Unless you push it and they see it, but we’ll get to that.
How Many Branches? As Many as You Need
Seriously. A branch is cheap. Make one for everything.
- Feature Branches:
feature/new-dashboard-widget,feature/oauth-integration - Bugfix Branches:
bugfix/critical-crash-on-prod,bugfix/typo-in-footer - Chore Branches:
chore/update-dependencies,chore/refactor-old-module - Experimental Branches:
experimental/ai-powered-toaster-oven(just don’t merge this one intomainafter your all-nighter).
Each branch should ideally represent a single, isolated piece of work. If you find yourself working on three unrelated things on one branch, you’re doing it wrong. Stop. Go back and branch again.
When to Delete These Branches?
Once a branch’s changes are successfully integrated into the main branch (via a merge, which we’ll cover next), that feature branch has served its purpose. There’s almost never a point in keeping a cluttered list of these old branches. It adds overhead and makes git branch output look like a tangled mess. Clean up after yourself.
git branch -d <name>(Safe Delete Local Branch): This command deletes a local branch only if it has been fully merged into your current branch. Git won’t let you accidentally lose work.# switch back to main git checkout main git branch -d feature/user-profile # If not fully merged, Git will complain: "The branch 'feature/user-profile' is not fully merged. If you still want to delete it, read below."git branch -D <name>(Force Delete Local Branch): Use the uppercase-Dto force delete a branch, even if it hasn’t been merged. Only use this if you are absolutely, 100%, undeniably sure you don’t need the changes on that branch. This is the “I know what I’m doing and I accept the consequences” flag.git branch -D experimental/trash-fire-idea # poof. gone. like a dad gone out to buy some milk.git push origin --delete <name>(Delete Remote Branch): Deleting a local branch doesn’t delete it from GitHub. You need to explicitly tell the remote to remove it.git push origin --delete feature/user-profile # or the shorter syntax: git push origin :feature/user-profileThis ensures your remote repository stays clean and manageable.
The Unification: merge and Pull Requests (PRs)
You’ve spent days (or minutes, you sloppy vibecoder) playing around on your feature branch, making buggy, broken commits. Now what? You are an intern. You’re destined to break things in production. So, naturally, you want those buggy commits/changes in the main branch so they can eventually be deployed and seen by the your users. This is where Pull Requests (PRs) come into play.
What’s a Pull Request (PR)?
A Pull Request isn’t a Git command; it’s a GitHub (or GitLab/Bitbucket) feature. It’s you submitting your work for review. It’s you begging like a slave to your team (or whoever reviews it) saying:
“Hey, I’ve done some work on feature/broken-thing. It works (I am lying), pwease accept it into the main branch 👉🏻👈🏿.r”
This is the core of modern, sane collaboration:
- Code Review: Others examine your changes. They check for bugs, performance issues, general sanity, AI slop, yada yada.
- Discussion: Comments, suggestions, and nitpicks fly back and forth. You fix things, they approve.
- CI/CD Integration: Automated tests run. Linters check your code. Build pipelines kick off. If everything passes, green checkmarks. If not, red X’s and your destiny changes from making buggy changes to the inevitable public execution.
- Merge: Once approved and all checks pass, your branch is merged.
A PR is not just about merging code; it’s about quality control, knowledge sharing, and avoiding disasters.
git pull / git fetch: How to Get the Latest Changes Without Screwing Up
Before you even think about starting new work or merging your branch, you need to update your local repository with the latest changes from the remote. Someone might have pushed new stuff to main while you were taking your beauty nap.
git fetch: This command downloads new commits and branches from the remote repository but does not integrate them into your current working branch. It merely updates your local copy of the remote’s branches (e.g.,origin/main). It’s like checking the mail without opening any letters.git fetch origin # You'll see: "remote: Counting objects... Unpacking objects... From github.com:username/repo-name * [new branch] main -> origin/main" # Your local 'main' branch is still behind, but your 'origin/main' reference is up-to-date.You can then use
git log origin/mainto see what changes are waiting for you, orgit diff main origin/mainto see the exact differences.git pull: This command is essentiallygit fetchfollowed bygit merge. It downloads the latest changes from the remote and immediately tries to merge them into your current branch. This is the common shortcut, but it can lead to immediate merge conflicts if you’re not careful.# Make sure you're on the branch you want to update (e.g., main) git checkout main git pull origin main # Or, if you used 'git push -u origin main' before: git pullBest Practice:
Start new work by fetching and pulling
maininto your localmainbranch:git checkout main git pull origin mainThen, create your new feature branch from the updated
main:git checkout -b feature/my-new-thingBefore submitting a PR for your feature branch, update it with the latest
mainchanges:# from your feature or whatever other branch git pull origin main # This pulls origin/main into your feature branch, often resulting in a merge
This prevents you from building a feature on stale code, minimizing painful conflicts later.
git merge <branch-name>: The Command That Joins Two Timelines
This is where the parallel universe you created comes crashing back into the main timeline. git merge takes all the commits from the specified branch and integrates them into your current branch.
How It Works
- Fast-Forward Merge: If your current branch (
main) hasn’t had any new commits since you created your feature branch, Git just “fast-forwards” themainbranch pointer to the latest commit of your feature branch. It’s a clean, linear history. No new commit is created. - Three-Way Merge (Merge Commit): If both your current branch (
main) and your feature branch have diverged (i.e., new commits have been added tomainsince you created your feature branch, and you have commits on your feature branch), Git creates a new commit called a “merge commit.” This merge commit has two parents (the latest commit frommainand the latest commit from your feature branch), effectively bringing both histories together.
Usage:
Let’s say you’re on main and want to bring in changes from feature/user-profile:
# First, ensure your main branch is up-to-date
git switch main
git pull origin main
# then, merge your feature branch into main
git merge feature/user-profile
Output (successful merge):
Updating a0b1c2d..f1d3a5b
Fast-forward
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
# Or for a three-way merge:
Merge branch 'feature/user-profile'
# Conflicts might happen here!
The Inevitable Pain: Merge Conflicts
This section is dedicated to the moment Git throws its hands up in the air, looks you dead in the eye, and screams, “YOU and someone ELSE changed the SAME LINE in the SAME FILE. I’m not resolving this. YOU FIX IT!”
A merge conflict occurs when Git cannot automatically reconcile differences between two converging branches. This usually happens when:
- Two different branches modify the exact same lines in the same file.
- One branch deletes a file, and another branch modifies it.
- Two branches add a file with the same name, but different content, in the same location.
When a merge conflict happens, Git pauses the merge process. Your terminal will look something like this:
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
And if you run git status, you’ll see:
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")
Now, go open the conflicted file (index.html in this example). It will look like a crime scene.
The Ugly Markers of Doom: Git inserts special markers into the file to show you where the conflict is:
<<<<<<< HEAD
<p>This is my useless new paragraph.</p>
=======
<p>This is someone else's better paragraph.</p>
>>>>>>> feature/user-profile
<<<<<<< HEAD: Marks the beginning of the conflicting change from your current branch (whereHEADis pointing, usuallymain).=======: This is the separator. Everything above it is fromHEAD, everything below it is from the other branch.>>>>>>> feature/user-profile: Marks the end of the conflicting change from the other branch you’re trying to merge (feature/user-profile).
How to Resolve This Hellish Mess
Manually edit the file(s): Delete the
<<<<<<<,=======, and>>>>>>>markers. Then, combine or choose the lines of code that you actually want to keep.Option 1: Keep only your changes:
<p>This is my useless new paragraph.</p>Option 2: Keep only their changes:
<p>This is someone else's better paragraph.</p>Option 3: Combine both:
<p> This is my useless new paragraph and their better different paragraph combined, making it all useless now. </p>
Tell Git you’ve resolved the conflict:
git add index.htmlThis stages the merged content. Try
git statusand read what it is displaying now.Commit the resolution:
git commit -m "Merge branch 'feature/user-profile' into main"This creates the merge commit and finalises the integration of the two branches.
This is a rite of passage. Every developer, at some point, will face the <<<<<<< HEAD monster. Learn from it. And for the love of all, test your code after resolving conflicts. You don’t want to merge a broken mess into main.
You’ve mastered the fundamentals. You can add, commit, push, and resolve merge conflicts. This completes all your foundation for Git/GitHub and ways to collaborate with people.
Go forth. Branch. Merge. And may your conflicts be minimal, and your resolutions swift. Try things out yourself, not just when I mention them. Learn to break stuff. Make yourself uncomfortable, that’s how you learn.
See you in the next part, stranger :)