Collaborating in git — study guide

The concept's fragments, read in order.

One history, many hands

Git on your own machine is already useful: commits record snapshots, branches let separate lines of work sit side by side, and merging folds them back together. But that whole history lives in one place, on one computer, and a project with more than one person needs it somewhere everyone can reach. Git collaboration is what changes when the history stops being yours alone.

The shift is smaller than it sounds and larger than it looks. The commits and branches are the same objects they always were; what is new is a shared copy of the repository that everyone syncs against, and a set of habits for getting work into it without stepping on each other. Instead of committing and moving on, you send your commits up to the shared copy and bring other people's down, and you propose changes for review rather than writing straight into the line everyone depends on.

The rest of this concept is those habits: the shared copy called a remote, the commands that move commits to and from it, the short-lived branches teams work on, the review step that guards the main line, the two ways to integrate divergent work, and what happens when two people edit the same line. None of it replaces what you know about local git. It surrounds it.

A shared copy called origin

your local repository origin the shared copy push: your commits up fetch / pull: commits down fetch downloads only; pull fetches then merges
Your local repository and a remote named origin exchange commits: push sends your commits up to origin, and fetch or pull brings others' commits down.

A remote is a version of the repository hosted somewhere other than your machine — on a server or a hosting service the whole team can reach. Your local repository is complete on its own, with all its own commits and branches, but a remote is the common ground: the copy everyone syncs against so that separate machines end up sharing one history. A remote is like a master copy everyone keeps in one agreed place: you take the latest down from it and post your own changes back up to it, and keeping your work in step with it is most of what collaboration mechanically consists of.

When you clone a repository, git names the remote it came from origin by convention. The name is not special to git — it is just the default label for the server you cloned from — but it is near-universal, so origin is what almost every command and every teammate will mean by "the shared copy." You can see the remotes a repository knows about, and the URLs behind them, with git remote -v.

A local branch can be tied to a branch on the remote, which git calls its upstream. A branch with an upstream is a tracking branch: it knows which remote branch it corresponds to, so git can tell you whether you are ahead of the shared copy, behind it, or both. That link is what makes the next step — moving commits down and up — something git can do with a single word instead of a full address each time.

Down and up

Two directions of traffic keep your local repository and the remote in step: commits come down from the shared copy, and your commits go up to it. Git splits the downward direction into two commands on purpose, because bringing changes in and combining them with your own work are separate acts.

git fetch downloads the latest commits from the remote into your local repository but does not merge them into the branch you are working on. Your files do not change; you simply now have the remote's new commits on hand to inspect before you do anything with them. git pull is the combined move: it fetches and then merges the fetched commits into your current branch in one step. Pull is exactly fetch followed by merge, which is worth holding onto, because it means a pull can produce the same divergence and conflicts any local merge can — it is not a magic "just make me current" button.

The upward direction is git push, which uploads the commits on your local branch to the remote so others can fetch them. When a branch tracks an upstream, git already knows where a push goes and where a pull comes from, so these become one-word commands. The rhythm of collaboration is this loop: pull to take in what changed, do your work and commit it, then push to share it.

A branch per piece of work

Teams do not commit straight to the main line. The common discipline is the feature-branch workflow: for each piece of work — a fix, a feature, one related change — you create a short-lived branch off main, do the work there, and bring it back only when it is ready. A branch is cheap to make and cheap to throw away, so there is little reason not to give every task its own.

The point of the isolation is that unfinished work stays out of everyone's way. Half-written changes, experiments that may not pan out, a fix that still fails its own checks — all of it lives on its own branch, where it cannot break anyone else's day. Meanwhile main stays a line other people can safely branch from and build on, because it only ever receives work that is actually finished.

Short-lived is the operative phrase. A feature branch that lives for weeks drifts far from main while main keeps moving, and the eventual reunion gets harder the longer the two have been apart. The habit that keeps this cheap is to scope branches small, finish them, and fold them back in while the distance is still short.

Proposing a change for review

feature branch pull request the review gate review by teammates plus automated checks main branch propose merge after approval
A feature branch is proposed through a pull request that gates it behind review and automated checks, and only an approved pull request merges into the main branch.

A pull request proposes merging one branch into another — typically your feature branch into main. It is not the merge itself. It is a proposal: an invitation for the rest of the team to look at the change, discuss it, and sign off before it becomes part of the shared line. Some hosting services call the same thing a merge request; the mechanism is identical.

The proposal opens a gate between finished work and the main branch. Teammates review the change, leave comments, and ask for revisions, and automated checks usually run against it as well. A pull request is like submitting a draft for sign-off before it goes into the shared version, rather than editing the shared version directly, so nothing lands on main on one person's say-so alone — the branch merges only once the review is satisfied. When the reviewers approve, the pull request is merged and the feature branch's commits finally join main.

This is the step that makes a shared main branch trustworthy. Because every change passes through review before it lands, main stays something the whole team can rely on rather than a place where anyone's untested work might appear without warning. The review gate is where collaboration stops being about commands and starts being about people agreeing on what ships.

Stitch, or replay

merge: keep both histories main feature merge commit joins two parents rebase: replay onto main's tip main new id new id feature commits replayed as new commits
Merge joins the feature and main lines with a merge commit that keeps both histories, while rebase replays the feature commits onto the tip of main as one straight line of new commits.

When your branch and main have both moved on since they parted, you have two ways to bring them together, and they produce different-shaped histories. A merge joins the two lines with a merge commit — a commit with two parents that records exactly where the histories came back together, keeping both lines visible in the graph. A rebase instead replays your commits one by one onto the current tip of main, as if you had started your work from there all along, producing new commits and a single straight line with no join to show for it. It is the difference between like redoing your steps cleanly onto the newest version versus stitching two versions together with a seam that records they met.

Neither is more correct. A merge preserves the true story, including the fact that two lines developed in parallel, at the cost of a busier graph. A rebase gives you a clean, linear history that reads like one thing happened after another, at the cost of rewriting your commits — because replaying them creates new commits with new identities, even though the changes inside are the same.

That rewriting is the whole reason for the one rule you must not break: do not rebase commits that have already been shared. Once your commits have been pushed and other people may have based work on them, rebasing replaces them with new ones, and everyone who had the originals now disagrees with you about what history is. Rebase your own local, unpushed work freely to tidy it up; leave shared history alone.

When two people edit the same line

Most of the time git combines two people's changes without anyone noticing, because they touched different parts of different files and there is no ambiguity about the result. A conflict happens only in the narrow case where the same lines were changed on both sides. Then git cannot know which version you meant, so it stops, marks the clashing region in the file, and hands the decision to a human.

Resolving a conflict is not a git trick; it is a judgment. Git shows you both versions of the disputed lines, and you either choose one side, choose the other, or write the combination that is actually right, then tell git the clash is settled. The work is understanding what each change was trying to do, which is why a conflict between two people is as much a communication problem as a technical one — the fastest fix is often a short conversation with whoever wrote the other side.

Conflicts are a normal cost of several people editing one project, not a sign anything went wrong. They cluster where work overlaps, so the habits that reduce them are social as much as technical: small changes, short-lived branches, and syncing often so two people rarely drift far apart on the same lines before either notices.

Keeping main shippable

The point of all this machinery is one property: a main branch that is always ready to ship. When every change reaches main through a reviewed pull request off a short-lived branch, the shared line only ever accumulates work that someone else has looked at and that passed its checks. That is what lets a team deploy from main at any moment without holding its breath.

This is how more than one person builds a single thing without chaos. A group putting up a website or a shared tool is not really coordinating files; they are coordinating a history, and the workflow — remotes to sync against, branches to isolate work, pull requests to guard the main line, a clear choice between merging and rebasing to integrate — is the protocol that keeps that history coherent while many hands write into it at once.

It scales down as well as up. The same loop that lets a large team keep main deployable lets you collaborate with one other person, or with a version of the project that a service is watching, or with your future self returning to a repository months later. Learn the loop once and it is the same loop everywhere work is shared: pull, branch, propose, review, integrate, keep main clean.