I submitted a couple of small fixes to OpenClawa Mattermost threading bug (2 lines) and a chatmode mention issue (1 line). The PRs were in review, but I wanted to test them on my deployment now. Maintaining a full fork for 3 lines felt absurd.

The trick

The upstream image ships TypeScript source and uses jiti for runtime transpilation. So you can patch .ts files directly — no build step. And git is already in the image.

Store .patch files in the repo, apply them during the Docker build:

COPY patches/ /tmp/patches/
RUN cd /app && \
    for p in /tmp/patches/*.patch; do \
      [ -e "$p" ] || continue; \
      echo "Applying patch: $(basename $p)"; \
      git apply "$p" || { echo "FAILED: $(basename $p)"; exit 1; }; \
    done && \
    rm -rf /tmp/patches

git apply fails loudly if a patch doesn't apply cleanly — which is what you want.

Generating patches

This is where I initially got tripped up. If you just git diff main...my-branch, you'll get all the upstream changes between the fork point and your branch tip, not just your fix. The branch includes dozens of unrelated upstream commits.

Instead, extract just your commit:

git show <your-commit-sha> -- path/to/file.ts \
  > patches/12345-short-description.patch

Then build the image. If git apply fails, the build fails. That's your test.

Naming convention

I name patches <PR-number>-<short-description>.patch. The PR number is key — when bumping the upstream version, you check:

gh pr view 27744 --repo openclaw/openclaw --json state -q '.state'
# MERGED → delete the patch
# OPEN → keep it

Version bumps

When you bump the upstream version, patches may not apply if the context changed. The first time I did this (v2026.2.25 → v2026.2.26), both patches needed line number updates because upstream refactored the file. The surrounding code was identical though — regenerated both in 2 minutes.

The workflow is: bump version, build, fix whatever breaks. If a patch fails, check whether the PR merged (delete the patch) or just regenerate it against the new version.

Why not sed / file overlay / submodule / fork build?

git apply hits a sweet spot that the alternatives don't:

ApproachProblem
sed in DockerfileFragile, no context-awareness, unreadable
Copy entire patched filesLose track of what changed; version bumps require full file regeneration
Git submodule of forkOverkill; submodules are painful
Clone fork in DockerfileSlow, caching issues, complex for tiny changes

git apply is contextual (fails if surrounding code changed), reviewable (standard diff format), and needs no extra tooling. The failure mode is the right one: a broken build tells you something changed upstream and you need to look.