KSKS Security Research
Home / Git & collaboration

Git collaboration: branches, forks & conflicts

Working with Git· part 1 of 2
GitGitHubpull requestsmerge conflicts

A repository is a shared space, but git is built so many people can work in it at once without stepping on each other. The trick is branches — and understanding exactly what happens when that discipline slips and two people land on the same one.

TL;DR
A contribution is merged changes; a contributor is anyone with merged commits. You contribute either by pushing branches to a repo you can write to, or by forking one you can't. The norm is one branch per task. Put two people on one branch and the second to push must pull first — and resolve a conflict if they touched the same lines.

What a contributor and contribution are

A contribution is any change that gets merged into a repository — usually a set of commits delivered as a pull request. A contributor is anyone whose changes have been merged. The "Contributors" list on a GitHub repo is literally everyone who has commits in the project's history.

Two ways to contribute

Which model you use depends on whether you have write access to the repo. This is what the "contribute" on a public project actually means:

Shared-repo modelyou have write access:push branches to the repoFork & pull modelno write access:push to your fork, PR backPull request → review → merge to mainthe common path for both models
Both models end the same way — a reviewed pull request merged to main. They differ only in where you push.
Shared-repo modelFork & pull model
Whenyour own repo, or a team you're a collaborator onan open-source repo you don't own
You clonethe repo itselfyour fork (a copy on your account)
You push toa branch on the repoa branch on your fork
The PR goesyour branch → main, same repoyour fork → the original repo

The contribution flow

The loop is the same in both models — the key detail is that you push your branch, never main (which is protected):

the contribution loop
git clone <repo>                    # or fork first, then clone your fork
git checkout -b feature/my-change   # your own branch
# ...edit...
git add .
git commit -m "describe the change"
git push -u origin feature/my-change   # push YOUR BRANCH, never main
# open a Pull Request in the web UI → reviewer approves → merge to main

The norm: one branch per task

In a healthy team, people do not share a working branch. Each person creates their own for their own task, so two engineers editing the same repo never collide:

  • Engineer A → feature/rule-brute-force
  • Engineer B → feature/rule-impossible-travel

Separate branches → separate PRs → reviewed and merged independently. This is why feature branches exist: the only place changes meet is the controlled merge into main.

What if two people are on the same branch

Git allows it, but it creates a race. Here is the exact sequence:

Both pull the brancheveryone starts at commit C0A and B commit locallyA makes C1, B makes C2A pushes first — succeedsremote branch advances to C1B pushes — rejectedremote has C1 that B doesn'tB runs git pullsame lines changed → merge conflictB resolves, then pushes — succeedsremote now holds C1 + C2
Git refuses to let the second pusher overwrite the first — the rejection is a safety feature, not an error.

The two rules that fall out of this:

  • Whoever pushes second must pull first. Git won't let B overwrite A's commit.
  • The pull's outcome depends on what each touched — different files/lines merge automatically; the same lines produce a conflict.

Resolving a conflict

When the same lines changed, git marks the spot and asks you to choose. You edit the file, keep what's correct, delete the markers, then commit the resolution:

a conflict in the file
<<<<<<< HEAD (the version already on the remote)
    | where FailedAttempts >= 10
=======
    | where FailedAttempts >= 5
>>>>>>> your changes
Same-branch isn't broken — it just doesn't scale
Two people can pair on one branch for an afternoon. But it forces constant pull-before-push and invites conflicts, which is why branch-per-task is the default: the collisions move to one controlled place — the merge into main, where a reviewer handles them deliberately.
Runnable companion example

Clone the repo and open examples/git-collaboration — the analytic rule (ARM + Terraform), the validation scripts, and the GitHub Actions pipeline from this article, ready to run in your own lab. Try it now: python3 scripts/lint_rule.py rules/*.json