One evening I dropped a fresh export straight on top of my working folder. By morning, a layout fix I had made by hand was back to its original broken state.
Losing the fix stung less than the fact that I went a full day without noticing. I had not looked at a diff. The moment I copied over that folder, the previous state existed nowhere.
I work as an indie developer with a handful of apps that have been in the stores for years, so I thought I was in the habit of watching changes land. But when the other side of the change was generated code, I still slipped into treating it as one big blob to receive and one big blob to replace.
Now I read every export in the same order before I take it in. There is very little to memorize. Four commands carry the whole thing.
Don't let generated code land on the branch you work on
An export is not a patch. It is the entire project as of that moment, which means it arrives with enough force to sweep up whatever you fixed by hand.
So separate the place where code arrives from the place where it grows. Keep one branch that only ever receives exports, and leave the branch you edit untouched.
Generated results arrive on a branch; the merge is mine to approve. That is the one line I hold to even on the days I am in a hurry.
# Move to the receiving branch (create it if it doesn't exist yet)
git switch rork-export 2>/dev/null || git switch -c rork-exportIf you export to GitHub, the branch that output lands on already serves as your receiving branch. If the export comes down as a folder of files, start by creating one of your own, as above.
Delete the tracked files before you copy the new export in
This is the step that pays for itself the fastest.
When you copy a new export over an old folder, additions and modifications show up fine. Deletions do not. A screen or component that was removed on the generating side stays on your disk, and it never appears in the diff.
That is exactly how a settings screen you were sure you had deleted ends up shipping inside a build. So before copying, drop the tracked files.
# Delete only the files Git is currently tracking
# (.git and untracked things like your local .env survive)
git ls-files -z | xargs -0 rm -f
# Copy the new export in; leave .git alone
rsync -a --exclude '.git/' "$EXPORT_DIR"/ .
# Deletions, additions and modifications all land in the index
git add -A
git status --short | head -20You should see something along these lines:
D src/screens/OldSettings.tsx
M package.json
M app.json
A src/screens/Settings.tsx
That leading D is the line a plain copy would never have produced. Since git ls-files only lists tracked files, an .env or a key excluded by .gitignore stays put — which also means that if you have tracked those files, this step takes them with it. I wrote about why the ignore rules belong before the first git init in Set up .gitignore before you run git init on exported Rork code.
Strip the noise before you count the diff
Open git diff as-is and lock files and generated directories fill the screen. Faced with a few thousand lines, most of us stop reading. And a diff you stopped reading is a diff you approved.
So cut it down before counting.
git diff --cached --stat -- . \
':(exclude)package-lock.json' \
':(exclude)yarn.lock' \
':(exclude)ios' \
':(exclude)android'':(exclude)…' is Git's pathspec syntax for leaving things out. Unlike .gitignore, it changes nothing about tracking state — it only changes how you read this one time. What is left tends to look like this:
app.json | 6 ++++--
package.json | 3 ++-
src/screens/Settings.tsx | 84 ++++++++++++++++++++++++++++++---
3 files changed, 79 insertions(+), 14 deletions(-)
What you can safely exclude comes down to one question: do you edit that place by hand?
| Path | Why it's usually noise | When to keep it in |
|---|---|---|
package-lock.json / yarn.lock | Resolution output; the intent shows up in package.json | When a version was pinned deliberately |
ios/ / android/ | Often regenerated by prebuild | When you edit native code by hand |
| Icons and splash images | Binary diffs you can't read; looking is faster | When they changed and you didn't touch them |
If you do keep native folders under your own hand, read them instead of excluding them. The related question of what actually gets uploaded lives in .easignore replaces .gitignore: checking what EAS Build actually uploads.
The three files I open every single time
Once the volume is manageable, order matters. I don't start with the big screen files. I open the small ones first, because config and dependencies can change behavior in three lines.
# Read config and dependencies before anything else
git diff --cached -- app.json app.config.ts package.jsonThree things get my attention.
- Permissions and usage strings. One added camera or location declaration is one more thing to justify in review. If a permission you don't use shows up, take it back out right there.
- Dependency versions. Some versions climb with every generation. The climbing isn't the problem; shipping without knowing it climbed is.
- Inlined values. Keys that belong in environment variables sometimes appear directly in source.
The third one slips past human eyes, so pull the added lines out mechanically.
# Pull key-looking text out of added (+) lines only
# False positives are expected; over-matching is the right setting here
git diff --cached -U0 -- '*.ts' '*.tsx' '*.js' '*.swift' '*.kt' \
| grep '^+' \
| grep -Ei '(api[_-]?key|secret|token)[^a-z0-9]{0,3}[=:]' \
| head-U0 drops the surrounding context lines; without it, unchanged lines mix into the + side and the output gets hard to scan. If nothing comes back, that is your answer for this round.
Stop once before the merge commit lands
Finally, bring the diff you just read into your own branch. Whether or not I pass --no-commit here turned out to be the difference that mattered.
git switch main
git merge --no-commit --no-ff rork-export
# The last look before anything enters your branch
git diff --cached --stat
# Only after confirming your hand-made fixes survived
git commit -m "Take in the latest Rork export"--no-commit leaves the merge sitting in the index instead of committing it. If a fix of yours got wiped, you can recover your side with git checkout HEAD -- <file> and then commit. One place to stop is all it takes to remove the irreversibility I ran into that first evening.
When there's a conflict, I keep my hand-written side without hesitating. Generated code can be asked for again. The reasoning behind a manual fix doesn't come back in the same shape.
What's left after you can read the diff is a separate judgment: which bugs to fix yourself, and which to hand back to Rork. I put that line in Telling apart the bugs Rork can fix from the ones you should: a triage routine for exported code.
Next time you re-export, just create the receiving branch. The reading routine can come later and still arrive in time. That is where I started too.