Maintaining two long-lived variants of React Native and Expo web project - ChatGPT

Given below are edited extracts from an exchange on 20 Nov. 2025 with ChatGPT (CG) titled, "Long lived proj Git repo variants".

Me: For the project mentioned above, I have a release APK which I have shared on google drive and I have the web app deployed on Expo.

I am now considering making a variant of this app which will have a guest feature allowing persons to use the app without having to log in. But I want to keep this variant distinct from the above.

Some points & questions:
1) I plan to use the standard app-release name for the current APK.
2) For the variant, I will use a specific name like guest-release.apk
3) How do Android developers name their APKs to maintain different versions? Do they rename app-release suitably like lms-app-v100.apk?
4) On Expo, to maintain two web app variants, I don't want to have confusion for the eas deploy command with having different expo web app projects. So I was thinking that for main app, I will use production url. But for guest login variant, I will use a development deployment .

What do u think? All suggestions welcome.

CG: ...

So, your plan is perfectly aligned with common practice:

  • Main version → app-release.apk

  • Guest version → guest-release.apk (good choice)

  • If you want to be extra clear → lms-guest-v1-release.apk

...

You can have two versions of the same Expo project, each deployed to its own [EAS] channel:

Channels:

  • production → Main app

  • guest → Guest variant app

...

With channels, you can simply do:

Deploy main app:

eas deploy --channel production

Deploy guest variant:

eas deploy --channel guest

Even for web builds:

Build web:

npx expo export --platform web

Then:

  • Upload production build to production hosting

  • Upload guest build to guest hosting

You can host each variant at different URLs:

https://myapp.web.app/ (main) https://myapp-guest.web.app/ (guest)

Or, if using Expo Hosting:

  • Production → Expo URL assigned to production channel

  • Guest → Expo URL assigned to guest channel

...

Me: Great that Expo offers channels. I will use that for web deployment.
For Android build, I do local builds.
Got the naming APK part. 
Thanks a ton.

Now for the git repo, should I use a separate branch or should I fork my repo?

CG: Both branching and forking will work — but for your specific case (a main app + a guest variant that share 95% of the code), a separate branch is clearly the better choice.

Here’s the reasoning in a clean and practical way:


Use a separate branch (recommended)

Why?

  1. Both variants share the same codebase
    The guest version is not a full rewrite — it’s a configuration variant.
    Branching keeps shared changes easy to merge.

  2. Forking creates unnecessary duplication
    You’ll have two separate repos, two issues lists, two places to maintain.

  3. Branch workflow is simple

    • main → your main app

    • guest → your guest app

    • You can merge fixes from main → guest easily

  4. You can use build-time environment variables to select behavior, so both variants can live in the same project.

Suggested branch layout:

main # main app guest-login # guest variant

If the guest variant is small, this is the simplest, cleanest setup.


❗ When forking is better (not your situation)

Forking makes sense if:

  • The guest app will evolve into a completely different product

  • The UI / features will diverge significantly

  • You plan to show them as two separate repos for clarity to others

  • You want different commit histories

Based on your current plan, none of this is true.


๐Ÿ‘ Best Recommendation for You

Use a branch, not a fork.

...

Me: I don't need to have: const isGuest = process.env.APP_VARIANT === 'guest';

CG: Correct — you don’t need
const isGuest = process.env.APP_VARIANT === 'guest';

because in your workflow, the selected .env file itself determines the variant. That makes your logic far simpler.


Your approach works perfectly without variant flags

Since each build explicitly copies the desired .env.* file into .env, you already achieve:

  • Different API endpoints

  • Different server URLs

  • Different feature toggles

  • Different Clerk configs

  • Different app behavior

simply by having the correct .env file at build time.

So in your case, instead of:

if (isGuest) {
   // guest logic
}

you can configure everything directly in the env files:


⭐ How to structure your env files

.env.main.web

MODE=main
ENABLE_GUEST_LOGIN=false
API_URL=https://main-api.example.com

.env.guest.web

MODE=guest
ENABLE_GUEST_LOGIN=true
API_URL=https://guest-api.example.com

Then in React:

const ENABLE_GUEST = process.env.ENABLE_GUEST_LOGIN === "true";

or

const MODE = process.env.MODE;

No need for APP_VARIANT.


๐Ÿ‘ Your build scripts already enforce the variant

Examples:

Build main web version:

npm run export-web:main

Build guest web version:

npm run export-web:guest

After the copy, Expo sees the final .env and that’s all it needs.


⭐ Benefits of your approach

✔ No extra packages
✔ No extra global env variables
✔ No config logic needed inside app.config.js
✔ Completely deterministic builds
✔ Works the same for Android + Web

This is actually the cleanest setup for a 2-variant app like yours.


...

Me: For the APK variants and their local builds, I want to keep config changes to a minimum. So I think I will use same app.json. After the build is done, I will rename the app-release.APK file suitably.

CG: That is exactly the simplest and cleanest approach for your situation.

For local Android builds, keeping one app.json and then renaming the generated APK is completely fine — and many solo developers do exactly this.

Here’s a clear breakdown so you feel confident about this choice:


✅ Your plan is correct and practical

1. Keep a single app.json

This avoids:

  • changing icons

  • changing app names

  • maintaining separate configs

  • risk of mismatched bundle identifiers

The local build process remains stable and predictable.


2. Build once, then rename the APK manually

Expo local Android builds always output:

./android/app/build/outputs/apk/release/app-release.apk

You can simply rename it after the build:

Example:

For main app:

app-release.apk → lms-app-main-release.apk

For guest app:

app-release.apk → lms-app-guest-release.apk

This is perfectly valid because the APK filename has no effect on:

  • signing

  • package name

  • updates

  • Android installation behavior

Only the internal manifest matters, and you’re keeping that the same.


๐Ÿ‘ Advantages of your approach

  • Zero extra configuration

  • Zero risk from modifying app.json

  • One codebase and one build process

  • Guaranteed consistency between variants

  • Easy to maintain in Git because the app.json never changes

And since these are local builds, you are in full control.


๐Ÿ”ง Optional — If ever needed later

If you want the guest version to:

  • have a different app name, or

  • appear separately in the app drawer,

then you’d need different android.package values or a dynamic app.config.js.

But for your current requirement (“keep config changes to a minimum”), this is not required.


⭐ Final confirmation

Your chosen workflow is good:

  1. Same codebase

  2. Same app.json

  3. Same local build command

  4. Rename the resulting APK file to reflect the variant

  5. Use different .env files to control behavior internally

This keeps the system simple, robust, and totally under your control.


...

Me: I will go with new git branch for guest-login.
But u said something about sharing future fixes/mods in main branch. 
So I will be having two long-lived branches (it is not a temporary dev branch which is later merged into main and deleted).
How can I then share updates to main branch with guest-login branch.

CG: Great question — and yes, when you have two long-lived branches (a “multi-variant” repository), you absolutely can and should share fixes from main into guest-login.

This is standard practice in many projects.
Here is the cleanest and safest workflow.


Recommended workflow: Keep guest-login rebased or merged onto main regularly

You have two stable branches:

main
guest-login

The idea is simple:

  • All core fixes, UI improvements, bug fixes, and shared code changes go into main.

  • The guest-login branch periodically pulls in updates from main.

You have two equally valid ways to do this:


๐Ÿ…ฐ️ Option 1 (recommended): Merge mainguest-login regularly

This is the safer and simpler method.

Steps:

  1. Checkout guest-login

git checkout guest-login
  1. Pull latest main

git pull origin main
  1. Merge main into guest-login

git merge main
  1. Resolve conflicts if any

  2. Commit and push

git push origin guest-login

Benefits:

  • Very stable

  • Good for long-lived branches

  • Easy conflict resolution

  • History remains simple and chronological

Most teams with multi-variant apps use merges.


๐Ÿ…ฑ️ Option 2 (more advanced): Rebase guest-login on main

This keeps the branch history clean but rewrites commits.

Steps:

git checkout guest-login
git fetch
git rebase origin/main
git push --force-with-lease

Use this only if you are fully comfortable with git and you are the only developer.
If you push rebase changes publicly, others get conflicts.


๐ŸŽฏ Which one should you use?

Given your workflow (solo developer, two variants, long-lived branches):

✔️ Use merge, not rebase

It's safer, easier, and perfect for long-lived variant branches.


๐Ÿ” How frequently should you merge?

You can adopt any of these rhythms:

  • After every important fix in main

  • Every time before building a new guest APK

  • Every few days if you're actively working on both

There is no harm in merging often.


๐Ÿ“Œ Important practice

On the guest-login branch:

  • Only put guest-specific logic

  • Accept all incoming changes from main

  • Keep differences minimal (env files + small logic differences)

This keeps merges smooth.


๐Ÿงฑ Example workflow summary

main
  ↑
  |  (merge main → guest-login)
  |
guest-login

You never merge guest-login back into main unless:

  • you added shared fixes that belong in main.

If that happens:

git checkout main
git merge guest-login --no-ff

(But ideally avoid putting shared fixes into the guest branch.)


⭐ Conclusion

Your two-branch structure works perfectly:

  • main → canonical project

  • guest-login → variant, regularly updated from main

  • Use git merge main while on guest-login to keep it updated

  • Very clean, very stable for multi-variant apps


...

Me: Will not git pull origin main
overwrite guest-login files by what main contains

CG: Short answer: No — git pull origin main will not overwrite your guest-login files.

Long answer with clarity below ๐Ÿ‘‡


✅ Why your files will NOT get overwritten

Let’s say you are currently on:

guest-login

and you run:

git pull origin main

What Git actually does is:

  1. Fetch the latest changes from the main branch

  2. Merge those changes into your current branch (guest-login)

  3. Git then shows a merge result:

    • automatic merge if no conflicts

    • conflict markers if both branches modified the same lines

Git will not blindly replace the files in your branch with the files from main.


๐Ÿ“Œ Important: Git merge is not "copy main over guest-login"

During a merge, Git tries to combine changes, not overwrite entire files.

Only lines that changed differently between main and guest-login will cause conflicts.

Example:

guest-login/file.ts

function login() { return 'guest login'; }

main/file.ts

function login() { return 'regular login'; }

After:

git checkout guest-login
git pull origin main

Git will show a conflict like:

<<<<<<< HEAD
guest login line
=======
regular login line
>>>>>>> main

It will not delete the guest logic; it asks you to pick one (or combine them).


๐Ÿงช Example scenario: files unchanged in guest branch

If the file wasn’t touched in guest-login, but changed in main:

  • Git will bring the updated main version into guest-login with no conflict.

  • This is safe and expected.


๐Ÿงฉ Example: guest-login changed lines AND main changed different lines

Git will merge everything cleanly.

Example:

In main you updated a component’s styling.

In guest-login you added a guest feature (ENABLE_GUEST_LOGIN flag).

Git will merge both because they are different areas of the file.


❗ When conflict happens

Only when both branches edited the same lines.

Git will show a conflict block.
You manually resolve it, save, and continue.

This is normal and expected for long-lived branches.


๐ŸŽฏ Bottom line

Merging main → guest-login does NOT overwrite your work.

Instead:

  • Git combines changes where possible

  • Git asks for your help only when both branches edited the same lines

This is exactly how multi-variant projects are maintained.


๐Ÿ‘ Suggested workflow for you (safe)

git checkout guest-login
git fetch origin
git merge origin/main

This is the safest merge sequence.


...

Me: Fascinating! So my guest-login specific code in guest-login branch will not be automatically overwritten with git pull from main branch. If that guest-login specific code has a conflict with some lines in current main branch, I will be prompted for what should be picked. I can ensure that I don't pick anything that overwrites my key guest-login specific code. Did I get that right?

CG: Exactly right — you’ve understood the workflow perfectly.

...


Comments