When your project isn’t just a temporary contribution but has its own identity (e.g., a “mod,” a “custom flavor,” or a “branded fork”), the workflow shifts from “temporary branch” to “long-lived integration.”
You are essentially maintaining a parallel product while siphoning updates from the original source.
The Branching Strategy
In this scenario, you need two permanent branches that never die:
upstream-main(orvendor): A clean, untouched mirror of the original project. You only ever pull into this branch.
prod(orcustom-main): Your project’s identity. This contains your unique branding, features, and configurations.
The Mental Model
Think of the upstream project as the engine and your project as the car body. You want to be able to swap out the engine for a newer model without denting the car.
The Long-Term Integration Workflow
Step A: Keeping the “Engine” Current
Periodically update your mirror branch so you have a clean base:
git checkout upstream-main
git pull upstream main
Step B: The “Integration” Merge
Instead of rebasing (which is for short-lived features), you will Merge the upstream changes into your custom project. This creates a “Merge Commit” that acts as a historical marker of when you synced.
git checkout prod
git merge upstream-main
Step C: Resolving “Identity” Conflicts
Because your project has its own identity, you will likely have permanent conflicts (e.g., you changed the README.md or the logo.png).
- Keep your identity: Always favor your custom files during the merge.
- Refactor for compatibility: If the upstream project changed a core function that your custom code relies on, you must update your custom code to match the new API.
- 3. 3. Architecture for Easier Maintenance
If you find yourself resolving the same conflicts every month, your project is too “intertwined.” Aim for decoupling:
| Strategy | Action |
|---|---|
| Configuration Over Modification | Use .env files or config objects rather than hardcoding changes into the upstream source code. |
| The “Plugin” Pattern | Keep your personal code in separate directories (e.g., /custom) and hook into the main app via imports, rather than editing the original files. |
| Wrapper Scripts | If you need to change how the app starts, write a wrapper script (start-my-flavor.sh) instead of modifying the upstream Makefile or package.json. |
- 4. 4. Handling Contributions Back
If you develop a bug fix in your “Identity” project that would benefit the “Upstream” project:
- Cherry-pick that specific commit:
git checkout -b fix-upstream-bug
git cherry-pick <commit-hash-from-your-prod-branch>
- Push this clean, isolated branch to your fork and open a PR.
Summary of the “Identity” Workflow
- Mirror the upstream in a dedicated local branch.
- Merge (don’t rebase) that branch into your custom branch.
- Isolate your unique code into separate files/folders to minimize merge conflicts.
- Cherry-pick fixes back to upstream when they are generic enough to share.
