Repository Layout vs. com.mojang Folders
A common mistake when starting out is initializing a Git repository directly inside the game's development_behavior_packs folder. Because Bedrock requires behavior packs and resource packs to live in separate directories under com.mojang, putting a repository in one leaves the other untracked. Maintaining two separate repositories for a single project makes atomic commits impossible when a feature requires both a behavior definition and a client-side texture or geometry file.
Instead, place your Git repository in a dedicated workspace folder outside the game's storage directory. Inside this root folder, create a BP directory for the behavior pack and an RP directory for the resource pack. If you use the Script API with TypeScript, your package.json, tsconfig.json, and raw .ts source files also live in the workspace root, keeping development tooling completely out of the final packaged add-on.
To get your files into the game for testing, either use a build tool or script that copies the contents of BP and RP into development_behavior_packs and development_resource_packs, or create directory junctions (symlinks on macOS/Linux, mklink /J on Windows) pointing from the com.mojang folders to your repository's BP and RP subdirectories. Symlinks allow you to edit files inside your tracked repository while using the /reload command in-game immediately, without waiting for a copy step.
What to Track and What to Ignore
Bedrock add-on repositories mix plain text JSON and lang files with binary assets like PNG textures, OGG audio files, and Blockbench project files (.bbmodel). All source JSON, scripts, localization files, and final assets inside BP and RP must be tracked. Whether you track .bbmodel files is a team preference, though storing them in a top-level src/models folder outside the RP prevents the game from shipping heavy source files inside the final .mcaddon archive.
If you compile TypeScript into JavaScript for the Script API, only track the .ts source files. The compiled output inside BP/scripts/ should be ignored by Git and generated fresh during your build or watch step. Committing compiled JavaScript alongside TypeScript guarantees noisy merge conflicts whenever two people touch the same script.
You should also ignore OS-specific metadata files, Node.js dependency folders, and packaged archive builds (.mcpack, .mcaddon). While there does not have to be a lang file for configuration, Git uses a standard plain-text dotfile at the root of the workspace to filter out these artifacts.
# Node and TypeScript build tooling
node_modules/
dist/
*.tsbuildinfo
# Compiled Script API output (if compiling from TypeScript in src/)
BP/scripts/**/*.js
BP/scripts/**/*.js.map
# Packaged archives
*.mcpack
*.mcaddon
*.mcworld
*.zip
# Editor and OS files
.DS_Store
Thumbs.db
.vscode/
*.bakSemantic Versioning in manifest.json
Every behavior and resource pack requires a manifest.json at its root. Aside from the Git history, Minecraft itself performs version control on your packs using the version arrays inside the manifest header, modules, and dependencies blocks. Each version is a three-integer array representing [major, minor, patch], or a semantic version string for certain script modules.
When you load a world with an add-on attached, the world stores the pack's UUID and its exact version array. If you distribute an update to players or testers without incrementing the version array in manifest.json, Minecraft may continue using cached versions of the pack stored inside the world save or global storage, ignoring your new files. Note that packs placed in development_behavior_packs and development_resource_packs bypass version caching so you do not have to bump the version on every test edit, but imported .mcpack files strictly obey it.
When your behavior pack depends on your resource pack, the dependencies array in the BP manifest must match both the RP's header.uuid and its header.version. Bumping the version in the header while forgetting to update the matching dependency entry in the partner pack is one of the most frequent causes of broken pack links after a release tag.
| Field | What it does | Notes |
|---|---|---|
| format_version | Sets the schema version for the manifest parser itself. | Always use 2 for modern packs. |
| header.version | Identifies the release version of the pack as [major, minor, patch]. | Must be incremented when distributing updated .mcpack or .mcaddon files. |
| header.min_engine_version | Locks the minimum Bedrock client version required to load the pack, and dictates base game behavior overrides. | Never bump this blindly; raising it can subtly alter how older entities or blocks in your pack behave. |
| dependencies[].version | Specifies the required version of a linked pack or native script module. | Uses [major, minor, patch] for pack UUIDs, and a string like "1.11.0" for Script API modules. |
{
"format_version": 2,
"header": {
"name": "pack.name",
"description": "pack.description",
"uuid": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
"version": [1, 2, 0],
"min_engine_version": [1, 20, 80]
},
"modules": [
{
"type": "data",
"uuid": "b2c3d4e5-f6a7-4b5c-9d0e-1f2a3b4c5d6e",
"version": [1, 2, 0]
},
{
"type": "script",
"language": "javascript",
"uuid": "c3d4e5f6-a7b8-4c5d-8e9f-2a3b4c5d6e7f",
"entry": "scripts/main.js",
"version": [1, 2, 0]
}
],
"dependencies": [
{
"uuid": "d4e5f6a7-b8c9-4d5e-9f0a-3b4c5d6e7f8a",
"version": [1, 2, 0]
},
{
"module_name": "@minecraft/server",
"version": "1.11.0"
}
]
}Managing Schema Evolution with format_version
Inside individual JSON files—such as entities, blocks, items, recipes, and client geometries—the format_version field acts as a per-file version control switch. It does not need to match your manifest's min_engine_version, nor do all files in your repository need to share the same format_version. Instead, it tells Minecraft's JSON parser which schema rules and component syntax apply to that specific file.
When updating a project in Git for a newer Minecraft release, avoid running a global find-and-replace on format_version across your repository. A block written for 1.20.20 might use components that have changed structure or validation rules in a later version, while an entity file from 1.16.0 might still work without issue. Only update a file's format_version when you actively need a component or feature introduced in that newer schema, and test that specific asset immediately after changing it.
{
"format_version": "1.20.60",
"minecraft:block": {
"description": {
"identifier": "wiki:reinforced_slate",
"menu_category": {
"category": "construction"
}
},
"components": {
"minecraft:destructible_by_mining": {
"seconds_to_destroy": 3.0
},
"minecraft:destructible_by_explosion": {
"explosion_resistance": 12.0
},
"minecraft:geometry": "minecraft:geometry.full_block"
}
}
}Preventing Merge Conflicts in Shared Index Files
Most behavior pack assets are isolated one-per-file: adding a new loot table or spawn rule only creates a new file, which Git merges cleanly across branches. Resource packs, however, rely heavily on shared registry files where every asset in the pack must be listed inside a single JSON object or lang file. The biggest culprits for Git merge conflicts are RP/textures/terrain_texture.json, RP/textures/item_texture.json, RP/sounds/sound_definitions.json, RP/blocks.json, and RP/texts/en_US.lang.
When two developers on separate Git branches both add new custom blocks at the bottom of terrain_texture.json, the first branch to merge succeeds, and the second branch hits a conflict on the closing braces and commas of the JSON object. In JSON, a careless conflict resolution frequently leaves behind a missing comma or a duplicate closing brace, causing the entire texture registry to fail to parse.
To minimize these conflicts, establish two strict formatting rules for your team. First, sort keys alphabetically inside terrain_texture.json, item_texture.json, blocks.json, and sound_definitions.json rather than appending new entries to the very bottom of the file. Two features named wiki:amber_ore and wiki:zinc_ore will edit completely different lines of the file, allowing Git to auto-merge them without human intervention. Second, group localization strings in en_US.lang by feature or category with clear comment headers, and ensure your code editor is configured to use consistent line endings (LF via a .gitattributes file) so Git does not flag the entire file as modified when someone on Windows edits a file created on macOS.
{
"resource_pack_name": "wiki_rp",
"texture_name": "atlas.terrain",
"padding": 8,
"num_mip_levels": 4,
"texture_data": {
"wiki_amber_ore": {
"textures": "textures/blocks/amber_ore"
},
"wiki_reinforced_slate": {
"textures": "textures/blocks/reinforced_slate"
},
"wiki_zinc_ore": {
"textures": "textures/blocks/zinc_ore"
}
}
}Handling Test Worlds and Binary Assets
Avoid committing active test worlds from the minecraftWorlds directory directly into your primary code branch. Minecraft worlds use a LevelDB key-value store located in the db/ subfolder, which generates dozens of small binary .ldb and .log files that change names and contents every time the world is opened and closed. Tracking an active world bloats the Git history rapidly and produces unresolvable binary conflicts if two developers open the test world on their respective machines.
If your add-on is built specifically for a custom adventure map or requires a standardized testing arena, export the world as an .mcworld archive at major milestones and attach it to a Git release, or store unpacked template worlds in a separate branch using Git LFS (Large File Storage). For regular add-on development, keep worlds out of Git entirely and use a setup function or script event to build your test structures dynamically inside a fresh flat world.
Which versions this applies to
Manifest format_version 2 and semantic versioning rules apply to all modern Bedrock versions (1.16.0 through 1.21+). Stable; no experimental toggles are required for version control, manifest dependency linking, or standard Script API modules.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Content Log shows a syntax error in a JSON file immediately after pulling or merging a branch: "<<<<<< HEAD" or "Failed to parse JSON". | A Git merge conflict occurred in a shared file like RP/textures/terrain_texture.json or BP/manifest.json, and the file was saved or loaded into the game while still containing Git's conflict marker lines (<<<<<<<, =======, >>>>>>>), or a comma was accidentally omitted when combining the two branches. | Search your workspace for "<<<<<<<" to locate unresolved conflict markers, choose the correct JSON blocks from both branches, and validate the file with a JSON linter before running /reload. |
| World settings or Content Log displays "Missing dependency with ID '...' and version [1, 2, 0]" when activating the behavior pack. | You incremented the header.version in BP/manifest.json and updated its dependencies block to require [1, 2, 0] of the resource pack, but forgot to increment header.version in RP/manifest.json to match. | Open RP/manifest.json and update both header.version and modules[].version to [1, 2, 0] so they match the version array requested in the behavior pack's dependencies array. |
| Importing a newly packaged .mcpack or .mcaddon fails with "Duplicate pack detected" or loads into the game without showing any of your recent changes. | The pack was imported over an existing installation in global resources or a world save, and because the header.version in manifest.json was not increased since the last import, Minecraft refused to overwrite the cached version. | Increment the patch or minor number in header.version and modules[].version inside manifest.json before packaging a release build, or delete the old cached pack from Settings > Storage before importing. |
| Content Log reports "Script module '@minecraft/server' version '1.11.0' is not supported by this version of the game" for one developer on the team. | One developer updated the @minecraft/server dependency in BP/manifest.json and package.json to match a newer Minecraft update or Preview build, while another team member pulled the commit on an older retail client. | Agree on a target Minecraft client version across the team, or lower the @minecraft/server module version in BP/manifest.json to the highest version supported by the oldest client on your team. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
