Skip to content
StarLive demo →
GuideChecked against main · 31 Oct 202610 min read

Take updates from upstream

AppointMe has no update command. Merge release tags if you cloned, or merge a regenerated copy if you used dotnet new, and keep your .NET code where upstream merges don't reach.

You'll get
An upstream release merged on a branch
Time
Minutes to hours, depending on divergence
Touches
Shared files, migrations, lockfile, API client
Before you start
A clean working tree and passing tests

There’s no update command, script or bot. You merge upstream yourself, and how you do it depends on how you started.

Dependabot (.github/dependabot.yml) ships in every copy, but it only bumps NuGet, npm and GitHub Actions versions. It never brings in upstream code.

You started by Your names How you update
Cloning or forking the repo AppointMe.* Merge an upstream release tag. Git has a common ancestor to work from.
dotnet new appointme -n … Your own Regenerate the new version and merge it. Upstream’s diff uses AppointMe.* names, so it won’t apply to your files.
Copying the files into a new repository Either Record the version you started from once (shown below), then follow the matching path

CHANGELOG.md follows Keep a Changelog and Semantic Versioning:

CHANGELOG.mdView source ↗
# Changelog
All notable changes to AppointMe are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
  • One section per release, newest first: ## [1.2.1] — 2026-09-27, with ### Added, ### Changed and ### Fixed groups. ## [Unreleased] collects what’s on main but not tagged yet.
  • A release is a v* tag, and the template package has the same version. A release can be packaging-only: 1.2.1 changed the nuget.org page and generates exactly the same project as 1.2.0.
  • The links at the bottom open GitHub’s compare view, for example v1.2.0…v1.2.1. Read the real diff there before you merge. For unreleased work, compare v1.2.1...main.

Add upstream as a remote, fetch its tags under their own prefix, and merge a tag on a branch.

  1. Add the upstream remote. If origin already points at bravodev-hub/appointme, run git remote rename origin upstream instead, then add your own repository as origin.

    Terminal window
    git remote add upstream https://github.com/bravodev-hub/appointme.git
  2. Fetch upstream’s tags under an upstream/ prefix. Your copy of devtest.yml deploys on every pushed v* tag, so keep upstream’s v1.2.1 out of your own tag names.

    Terminal window
    git fetch --no-tags upstream 'refs/tags/v*:refs/tags/upstream/v*'
    git fetch --no-tags upstream main
    git tag -l 'upstream/*'
  3. Merge the tag on a branch. Merge release tags rather than upstream main, which is the development branch.

    Terminal window
    git switch -c update/appointme-1.2.1
    git merge upstream/v1.2.1
  4. Resolve conflicts by file type, using the table in the next section. Then run the checks in Verify it works.

  5. Merge the branch into your main through a pull request, so your own CI runs on it.

If git answers refusing to merge unrelated histories, your repository doesn’t share upstream’s history, because you copied the files rather than cloning. Record the version you started from once. This merge changes no files:

Terminal window
git merge -s ours --allow-unrelated-histories -m "Record AppointMe v1.2.1 as the base" upstream/v1.2.1

From then on, git merge upstream/<newer tag> brings in only what changed after that version. Name the version you really started from: name a later one and its changes are silently skipped.

Most conflicts land in a few kinds of file, and each kind has one right way to resolve. In a renamed project, read Contoso.Booking for AppointMe in the paths.

Files Don’t Do
src/AppointMe.Frontend/src/api/client.ts, client.schemas.ts Merge hunks by hand Take either side, and regenerate once the backend builds
*/Database/Migrations/*, including <Context>ModelSnapshot.cs Edit upstream’s migrations, or trust a clean snapshot merge Follow the migrations table below
src/AppointMe.Frontend/package-lock.json Merge hunks by hand Take upstream’s, fix package.json, run npm install on Node 24, check with npm ci
Directory.Packages.props Drop a version below upstream’s Keep the higher version of each package, plus your own entries
src/AppointMe.Aspire/AppointMe.Aspire.csproj Leave the SDK version behind Keep Aspire.AppHost.Sdk on the same version as the Aspire.* packages
CLAUDE.md, README.md Merge line by line Take upstream’s, then add your own sections back

To regenerate the typed API client, take the incoming side and rebuild it. During a merge, --theirs is the branch you’re merging in:

Terminal window
git checkout --theirs -- src/AppointMe.Frontend/src/api/
cd src/AppointMe.Frontend
npm run generate:api

For the lockfile, the frontend’s CLAUDE.md asks for the npm major CI uses: Node 24 with npm 11. A lockfile written by a different major churns, and npm ci has rejected one before.

Each module has its own DbContext, schema, migrations folder and snapshot, and DatabaseMigrationService applies all of them when the API starts. Trouble starts only when you and upstream both added a migration to the same context.

Your situation Do this
Your migrations are all in your own module’s context Nothing. Upstream’s files merge in untouched.
You added a migration to an upstream context, and no shared database has it yet Abort the merge, remove your migration with git (keep the model change), merge, then add your migration again
Your migration is already applied to a shared or production database Don’t delete it. Keep both migrations, make sure the snapshot contains both changes, and add a new migration on top

EF Core 10 doesn’t record the latest migration in the snapshot, so git can merge two diverged snapshots without any conflict. Microsoft’s migrations in team environments guide covers each case. Don’t run migrations remove after a bad merge: it restores the previous migration’s model, which can lack upstream’s changes.

To check a snapshot after merging, add a throwaway migration for that context, as in Database migrations. If its Up() is empty, the snapshot matches the model and you can delete it.

If you used dotnet new: merge a regenerated copy

Section titled “If you used dotnet new: merge a regenerated copy”

Keep a template branch that holds nothing but generated output. Regenerate the new version onto it, then let git merge the difference into your code.

  1. Create the template branch, once. If your first commit is the untouched output (step 3 of Rename your project), branch from that commit:

    Terminal window
    git branch template <first-commit-sha>

    If it isn’t, generate the version you started from onto an orphan branch, and record that branch on main as already merged:

    Terminal window
    FROM=1.2.0 # the version you generated from
    git switch --orphan template
    dotnet new install BravoDev.AppointMe.Templates::$FROM
    dotnet new appointme -n Contoso.Booking -o . --force
    git add -A && git commit -m "AppointMe $FROM, generated"
    git switch main
    git merge -s ours --allow-unrelated-histories -m "Record template $FROM as the base" template
  2. Regenerate the new version onto template. Use exactly the -n you used the first time. git rm comes first, so files that upstream deleted show up as deletions.

    Terminal window
    TO=1.2.1 # the version you're moving to
    git switch template
    git rm -rq .
    dotnet new uninstall BravoDev.AppointMe.Templates
    dotnet new install BravoDev.AppointMe.Templates::$TO
    dotnet new appointme -n Contoso.Booking -o . --force
    git add -A && git commit -m "AppointMe $TO, generated"
  3. Read the change in your own names. git diff template~1 template is the upstream compare view, already renamed.

  4. Merge it on a branch. Then resolve conflicts as in the table above, and run Verify it works.

    Terminal window
    git switch main
    git switch -c update/appointme-$TO
    git merge template

For a small release, porting by hand from the compare view is often quicker. To take upstream main before it’s released, pack the template from a clone of upstream (If you cloned instead) and install that .nupkg in step 2.

Why a template branch instead of a patch?2 min · optional

Upstream’s diff names AppointMe.Crm, and your files are Contoso.Booking.Crm, so it doesn’t apply. Regenerating translates the change into your names. But a patch still fails wherever you’ve edited the same file, because it doesn’t know what the file looked like before your edits.

The template branch supplies that. Its previous commit is the output you started from, and that commit is also an ancestor of your main. Git can then run a three-way merge: it keeps your edits, takes upstream’s, and stops only where both changed the same lines.

Run what CI runs before you merge the update branch. These are the checks in devtest.yml and the frontend’s CLAUDE.md:

Terminal window
dotnet build AppointMe.sln
dotnet test AppointMe.sln
dotnet run --project src/AppointMe.Api/AppointMe.Api.csproj --no-launch-profile -- codegen test
cd src/AppointMe.Frontend
npm ci
npm run check
npm run build

codegen test generates and compiles every Wolverine handler in memory, with no database. Then start the stack with Aspire: the API applies upstream’s new migrations to your local database on startup, and a bad snapshot fails at that point.

The fewer of upstream’s files you edit, the less there is to merge.

Do Because
Put product features in your own modules under src/ Upstream never touches them. Registering one adds a single line to Program.cs. See Add a module.
Keep your migrations in your own module’s DbContext Its snapshot belongs to you alone
Add slices next to upstream’s instead of rewriting them Endpoints, permissions and handlers are discovered from each module’s assembly, so a new slice edits no shared file
Put your team’s agent rules in .claude/rules/ or an imported file Upstream edits CLAUDE.md too. See Working with AI agents.
When you must change an upstream file, keep the change small and comment why Small hunks merge; rewrites conflict
Never hand-edit the generated client or reformat upstream code The result is conflicts with no meaning
Merge every release Conflicts grow with the gap
Why regenerate the API client instead of merging it?1 min · optional

client.ts and client.schemas.ts are orval output, and orval rewrites them completely on every run (clean: true). A hand-merged file can mix two backends’ contracts. Regenerating from the merged backend gives you a client that matches the code you’re about to ship, with your endpoints and upstream’s.

Symptom Cause Fix
fatal: refusing to merge unrelated histories Your branch doesn’t share history with upstream or with template Record your starting version once with git merge -s ours --allow-unrelated-histories
Type errors on imports from @/api/appointme or @/api/<your name> after the update Main renamed the generated client to src/api/client.ts Change your own imports to @/api/client and @/api/client.schemas
The API won’t start: The model for context '…' has pending changes A migration snapshot merged wrong. EF Core 9 and later refuse to migrate when it does. Fix the snapshot, then check it with a throwaway migration
npm ci fails after the merge with Missing: … from lock file The lockfile was written by a different npm major Run npm install again on Node 24 (npm 11), then npm ci
Every file changes on template You regenerated with a different -n, or different casing Regenerate with exactly the original name
Pushing tags starts a devtest deploy You pushed upstream’s v* tags to your remote Fetch upstream tags under the upstream/ prefix, as in step 2