Skip to main content

Patch Operations

Patch operations provide fine-grained control over structured files (JSON, JSONC, YAML, TOML) in overlays. Instead of replacing a file entirely (override) or deep-merging it (overlay), a .patch file applies targeted modifications using declarative markers.

File Naming Convention

Patch files use the naming pattern:

<basename>.patch.<ext>

The .patch suffix is removed when determining the target file. For example, settings.patch.yaml applies to settings.yaml.

The format of the patch file may differ from the target. For example, tsconfig.patch.yaml can patch tsconfig.json -- the patch is parsed as YAML, and the result is serialized as JSON.

Suffixes .patch and .override are mutually exclusive. A file with both suffixes (e.g., settings.patch.override.json) is an error.

Operations

$set

Replaces the value of a key entirely. Creates the key if it does not exist.

Value type: any

Input:

{ "editor": { "fontSize": 14, "tabSize": 2 } }

Patch:

editor:
fontSize:
$set: 16

Result:

{ "editor": { "fontSize": 16, "tabSize": 2 } }

$merge

Deep merges an object into the target. If the target does not exist, it is created as an empty object first.

Value type: object

Input:

{ "editor": { "fontSize": 14, "tabSize": 2, "rulers": [80] } }

Patch:

editor:
$merge:
fontSize: 16
tabSize: 4

Result:

{ "editor": { "fontSize": 16, "tabSize": 4, "rulers": [80] } }

$mergeBy

Merges an array of objects by a key field (similar to Kustomize strategic merge patch).

Value type: object with fields:

FieldTypeDescription
keystringField name used for matching.
itemsarray<object>Objects to merge. Each must contain the key field.

Target: must be an array of objects.

Behavior:

  1. For each item in items, find an element in the target array where the key field matches.
  2. If found, deep merge the target element with the incoming item.
  3. If not found, append the incoming item to the end.

Input:

{
"tasks": [
{ "name": "build", "command": "tsc", "args": ["--strict"] },
{ "name": "test", "command": "vitest" }
]
}

Patch:

tasks:
$mergeBy:
key: name
items:
- name: build
args: ["--strict", "--noEmit"]
- name: lint
command: eslint

Result:

{
"tasks": [
{ "name": "build", "command": "tsc", "args": ["--strict", "--noEmit"] },
{ "name": "test", "command": "vitest" },
{ "name": "lint", "command": "eslint" }
]
}

$append

Adds elements to the end of an array.

Value type: array

Target: must be an array.

Input:

{ "editor": { "rulers": [80, 120] } }

Patch:

editor:
rulers:
$append: [140]

Result:

{ "editor": { "rulers": [80, 120, 140] } }

$prepend

Adds elements to the beginning of an array. The order of elements in $prepend is preserved.

Value type: array

Target: must be an array.

Input:

{ "plugins": ["existing-plugin"] }

Patch:

plugins:
$prepend: [first-plugin, second-plugin]

Result:

{ "plugins": ["first-plugin", "second-plugin", "existing-plugin"] }

$remove

Removes elements from an array by value. Uses strict equality (===) for scalars and deep equality for objects/arrays. Missing elements are silently skipped.

Value type: array

Target: must be an array.

Input:

{ "files": { "exclude": ["node_modules", "dist", ".cache"] } }

Patch:

files:
exclude:
$remove: [node_modules, .cache]

Result:

{ "files": { "exclude": ["dist"] } }

$unset

Removes keys from an object. Missing keys are silently skipped.

Value type: array<string> (list of key names to remove)

Target: must be an object.

Input:

{ "editor": { "fontSize": 14, "wordWrap": "on", "minimap": true } }

Patch:

editor:
$unset: [wordWrap, minimap]

Result:

{ "editor": { "fontSize": 14 } }

$insertAt

Inserts elements at a specific index in an array.

Value type: object with fields:

FieldTypeDescription
indexinteger0-based insertion position. Negative values count from the end (-1 = before last element). Clamped to [0, length].
itemsarrayElements to insert.

Target: must be an array.

Input:

{ "plugins": ["a", "b", "c"] }

Patch:

plugins:
$insertAt:
index: 1
items: [x, y]

Result:

{ "plugins": ["a", "x", "y", "b", "c"] }

Execution Order

When multiple markers appear in the same node, they are applied in this fixed order:

  1. $unset -- remove keys first.
  2. $merge -- deep merge objects.
  3. $mergeBy -- merge array by key.
  4. $set -- set value (overwrites previous operations).
  5. $remove -- remove array elements.
  6. $insertAt -- insert at position.
  7. $prepend -- add to beginning.
  8. $append -- add to end.

Combination Restrictions

CombinationAllowed?
$set + $mergeNo -- error.
$set + $mergeByNo -- error.
$merge + $mergeByYes
$insertAt + $append + $prependYes
$append + $prepend + $removeYes

Non-marker keys in a patch file navigate the object hierarchy. To set a value, use $set; plain non-object values without markers have no effect.

# This navigates to editor.fontSize, then sets it to 16
editor:
fontSize:
$set: 16

# This navigates but has no effect (no marker)
editor:
fontSize: 16

Non-Existent Target Fields

OperationBehavior when target field does not exist
$setCreates the key with the given value.
$mergeCreates an empty object {}, then merges.
$mergeByCreates an empty array [], all items are appended.
$appendWarning in errors, operation skipped.
$prependWarning in errors, operation skipped.
$insertAtWarning in errors, operation skipped.
$removeSilent no-op.
$unsetSilent no-op.

Error Handling

Patch operations use a tolerant strategy: errors in individual files are collected in an errors array, and processing continues with remaining files.

ConditionBehavior
Invalid patch file (parse error)Error message, skip file.
$append/$prepend/$remove/$insertAt on non-arrayError message, skip file.
$unset/$merge on non-objectError message, skip file.
$mergeBy on non-arrayError message, skip file.
$mergeBy item missing key fieldError message, skip file.
$mergeBy item not an objectError message, skip file.
Invalid marker value typeError message, skip file.
Unknown marker (key starts with $)Error message, skip file.
Both $set and $merge in same nodeError message, skip file.
Both .patch and .override suffixesError message, skip file.