(Corrections Appreciated)
Here's a step-by-step for GitHub Desktop instead — no terminal needed:
1. Open GitHub Desktop and select the repository
If it's already listed on the left sidebar under "Current repository," click it. If not, add it first: File → Add Local Repository (if it's already on their computer) or File → Clone Repository (if they need to get it for the first time).
2. Check for uncommitted changes
Look at the Changes tab (left panel). If files are listed there, they have local edits. Best to commit those first:
- Type a short summary in the box at the bottom left
- Click Commit to [branch name]
If they'd rather not commit yet, they can stash instead via Branch → Stash all changes (Mac) or the equivalent under the Branch menu on Windows.
3. Make sure they're on the right branch
The current branch name shows at the top of the window. Click it to switch branches if needed — pulling only updates whatever branch is currently checked out.
4. Pull the latest changes
Look at the top bar — there's a button that says either:
- Fetch origin (if it's not sure yet whether anything's changed), or
- Pull origin with a number badge (if it already knows there are new commits waiting)
Click it. GitHub Desktop fetches and merges in one click — same result as git pull in the terminal.
5. Handle a merge conflict, if one comes up
GitHub Desktop will show a banner listing the conflicting files. For each one:
- Click Open in [their editor] (or open it manually)
- Look for <<<<<<< / ======= / >>>>>>> markers, decide what to keep, delete the markers
- Back in GitHub Desktop, check the box next to that file once resolved
- Click Continue Merge (or Commit merge) once all conflicts are checked off
6. Confirm it worked
Click the History tab (next to Changes) — the newest commits from the remote should now appear at the top of the list.
One thing worth pointing out to whoever you're guiding: the Fetch origin button turning into Pull origin (n) is how GitHub Desktop signals "there's something new" — so if they're ever unsure whether they're up to date, that button is the tell.
— pulling code with GitHub Desktop never touches the database. It only updates files on disk. If the new commits expect a table or column that doesn't exist yet, the app will just start throwing errors the moment it runs.
General safe order of operations, regardless of project:
- Back up the database before pulling anything
- Check the changelog/release notes for the commits you're about to pull — look for words like "migration," "schema," or "run this after upgrading"
- Pull the code
- Apply whatever database change process the project uses
- Restart the app/service if it's long-running
- specific to your actual TicketsCAD setup, since you've got both versions — they handle this very differently, and neither is something GitHub Desktop does for you:
v3 (classic): it's a web-based upgrade. After pulling, you preserve your incs/mysql.inc.php config and uploads//_tile_cache/folders, then visit http://your-server/tickets/install.php in a browser and select Upgrade mode → Do It. The installer itself detects when your database version doesn't match the code version and prompts you.
v4 (NewUI): this one's actually built for exactly your situation. There's a CLI migration runner:
bash
php sql/run_migrations.php --list # preview what's pending, changes nothing
php sql/run_migrations.php # apply pending migrations
It's idempotent (tracks what's already applied via a hash in a _migrations table) and halts on the first failure rather than leaving things half-applied. There's also a read-only admin dashboard (Settings → Migrations, super-admin only) that flags pending migrations after you pull new code — but it deliberately has no Run button, since triggering schema changes from a web request is its own security risk. It exists to tell you something needs running, not to run it. So you'll still need terminal/SSH access to the server itself for that step — GitHub Desktop gets the code there, but someone still has to run that command by hand.
**One habit worth building into your process either way: don't pull v4 and restart the app before running migrations — if the new code expects a column that isn't there yet, you'll get errors between those two steps**
**the only part that will need a command line**
(newui is the database name; --single-transaction gets a consistent snapshot without locking the table while people are using it.) There's also an in-app option — Settings → System → Backup → Backup now — as a quicker ad-hoc snapshot before this kind of change.
2. Get a terminal on the actual server
This is the part GitHub Desktop can't do — you need SSH access (PuTTY, Windows Terminal, macOS's Terminal.app, whatever you normally use to reach the box).
3a. If it's a traditional (non-Docker) install:
bash
cd /var/www/newui # or wherever it's actually deployed
php sql/run_migrations.php --list # preview only, changes nothing
php sql/run_migrations.php # actually applies them
3b. If it's the Docker setup:
bash
docker compose exec app php sql/run_migrations.php --list
docker compose exec app php sql/run_migrations.php
(app is the service name in the compose file — db is the database container, don't run it there.)
4. Read the output
It prints a summary — how many migrations were already applied vs. pending, then runs each pending one in order, and it's idempotent (safe to run more than once; it hashes each script and skips ones already applied). If something fails partway through, it halts immediately rather than continuing on a half-migrated schema — fix whatever the error says, then just run the same command again; everything before the failure point gets skipped since it's already recorded.
One nice thing this particular runner does: it doesn't just trust its own bookkeeping. Before running, it double-checks the actual database against what the code expects, and if something's missing — say a table got dropped during a crash or a restore from an older backup — it'll notice the mismatch and self-correct even if its tracking table claims everything's already applied.
5. Restart the app afterward if it's a long-running process (Docker: docker compose restart app), so nothing's holding old schema assumptions in memory.