# 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.

Source page: https://appointme.dev/docs/guides/upstream-updates/

* **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

## Pick your path

**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](#if-you-cloned-merge-a-release-tag). Git has a common ancestor to work from.                                                            |
| `dotnet new appointme -n …`             | Your own      | [Regenerate the new version and merge it](#if-you-used-dotnet-new-merge-a-regenerated-copy). 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                                                                                   |

### Read what changed first

`CHANGELOG.md` follows Keep a Changelog and Semantic Versioning:

CHANGELOG.md

```md
# 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](https://github.com/bravodev-hub/appointme/compare/v1.2.0...v1.2.1). Read the real diff there before you merge. For unreleased work, compare `v1.2.1...main`.

## If you cloned: merge a release tag

**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`.

   ```bash
   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.

   ```bash
   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.

   ```bash
   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](#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:

```bash
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.

## Resolve conflicts by file type

**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](/docs/frontend/api-client/), take the incoming side and rebuild it. During a merge, `--theirs` is the branch you’re merging in:

```bash
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.

### EF Core migrations

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](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/teams) 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](/docs/guides/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

**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](/docs/guides/rename-your-project/)), branch from that commit:

   ```bash
   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:

   ```bash
   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.

   ```bash
   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](#verify-it-works).

   ```bash
   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](/docs/guides/rename-your-project/#if-you-cloned-instead)) and install that `.nupkg` in step 2.

Gotcha

`git switch --orphan` removes the tracked files from your working tree, and `git rm -rq .` removes them from the `template` branch. Ignored files such as `bin/`, `obj/` and `node_modules/` stay where they are, and `--force` lets `dotnet new` write around them. Commit or stash your own work before you switch.

### Why a template branch instead of a patch?

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.

## Verify it works

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

```bash
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.

## Keep your code merge-friendly

**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](/docs/guides/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](/docs/guides/ai-assisted-development/).                             |
| 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?

`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.

## Troubleshooting

| 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                         |

## Next

* [Rename your project](/docs/guides/rename-your-project/) (Guide)
* [Database migrations](/docs/guides/database-migrations/) (Guide)
* [Add a module](/docs/guides/add-a-module/) (Guide)
