Same task — "show me what this branch changed" — but the two tools take opposite dot conventions. Get it backwards and your review fills with commits the author never touched.
The diff: use three dots
git diff origin/main...HEAD
Three-dot diff is git diff $(git merge-base origin/main HEAD) HEAD — it diffs against the branch point, not the tip of main. That's exactly what you want: the author's delta and nothing else.
The nice property: it's immune to origin/main moving forward. New commits landing on main after the branch point aren't ancestors of HEAD, so they don't shift the merge-base. The diff stays clean.
The trap is a stale base, not a newer one. If your local main is older than the branch point, the merge-base slides back to an older ancestor and the diff swallows unrelated upstream commits — making the author look like they changed far more than they did. So fetch first:
git fetch origin
git diff origin/main...HEAD
Want to see the branch point itself? git merge-base origin/main HEAD.
The log: use two dots
git log origin/main..HEAD
Two-dot log = commits reachable from HEAD but not from origin/main = the branch's own commits, exactly.
Don't reach for three dots here out of habit — git log A...B is the symmetric difference, so it also lists the commits main picked up that HEAD doesn't have. That's the noise you were trying to avoid.
So: diff three-dot, log two-dot. Different tools, opposite defaults, same job.
VARCHAR2(4000) means 4000 bytes, not characters. Most people know this. What's less obvious: VARCHAR2(4000 CHAR) doesn't guarantee 4000 characters either.
Under the default MAX_STRING_SIZE=STANDARD, the hard column cap is 4000 bytes regardless of whether you declared BYTE or CHAR. In AL32UTF8, a Chinese character takes ~3 bytes, so VARCHAR2(4000 CHAR) on a column storing CJK text will fail once the actual byte count exceeds 4000 — around ~1333 characters in.
To actually store 4000 CJK characters in a single VARCHAR2, the instance needs MAX_STRING_SIZE=EXTENDED (12c+), which raises the limit to 32767 bytes. This is not the default — not even in 19c — and it's a one-way migration that requires running utl32k.sql in upgrade mode. Oracle keeps it off by default precisely because it changes data dictionary behavior and breaks compatibility.
Quick check for your instance:
SELECT value FROM v$parameter WHERE name = 'max_string_size';
STANDARD = 4000-byte ceiling. EXTENDED = 32767-byte ceiling.
In development, your frontend runs on localhost:5173 and your API server on localhost:3000. The browser blocks cross-origin requests — that's CORS. Vite's dev proxy solves this by forwarding /api/* requests to the backend, making them look same-origin to the browser:
// vite.config.ts
server: {
proxy: {
'/api': 'http://localhost:3000'
}
}
In production this proxy disappears. The built frontend is just static files (HTML/JS/CSS) — no port, no process. Nginx or a CDN serves them, and reverse-proxies /api/* to the backend the same way Vite did in dev:
user → Nginx :80
├── /api/* → backend :3000
└── /* → dist/ static files
One port from the user's perspective, no CORS issue. The backend port is always real and needed; the frontend "port" only exists during development because Vite's dev server is a live process.