{"id":15143715,"url":"https://github.com/contentful/contentful-merge","last_synced_at":"2025-04-13T10:53:06.128Z","repository":{"id":186997760,"uuid":"560290851","full_name":"contentful/contentful-merge","owner":"contentful","description":"CLI to merge entries between environments","archived":false,"fork":false,"pushed_at":"2025-03-25T03:03:04.000Z","size":5896,"stargazers_count":21,"open_issues_count":20,"forks_count":8,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-25T04:19:37.329Z","etag":null,"topics":["cli","contentful"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/contentful.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-01T06:32:40.000Z","updated_at":"2025-02-19T09:04:53.000Z","dependencies_parsed_at":"2023-08-08T15:40:22.858Z","dependency_job_id":"9c179927-83ce-4c24-8397-cf01f08cfd7d","html_url":"https://github.com/contentful/contentful-merge","commit_stats":null,"previous_names":["contentful/contentful-merge"],"tags_count":165,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-merge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-merge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-merge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-merge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/contentful","download_url":"https://codeload.github.com/contentful/contentful-merge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248702999,"owners_count":21148116,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","contentful"],"created_at":"2024-09-26T10:03:04.783Z","updated_at":"2025-04-13T10:53:06.096Z","avatar_url":"https://github.com/contentful.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- header  START --\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.contentful.com/developers/docs/\"\u003e\n    \u003cimg alt=\"Contentful Logo\" title=\"Contentful\" src=\"images/contentful-icon.png\" width=\"150\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align='center'\u003eContentful Merge\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#introduction\"\u003eIntroduction\u003c/a\u003e |\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e |\n  \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e |\n  \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e |\n  \u003ca href=\"#commands\"\u003eCommands\u003c/a\u003e |\n  \u003ca href=\"#data-structure\"\u003eData structure\u003c/a\u003e |\n  \u003ca href=\"#faq\"\u003eFAQ\u003c/a\u003e |\n  \u003ca href=\"#feedback\"\u003eFeedback\u003c/a\u003e |\n  \u003ca href=\"#code-of-conduct\"\u003eCode of Conduct\u003c/a\u003e |\n  \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://oclif.io\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/cli-oclif-brightgreen.svg\" alt=\"oclif\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/contentful-merge\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/contentful-merge.svg\" alt=\"npm\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://circleci.com/gh/contentful/contentful-merge/?branch=main\"\u003e\n    \u003cimg src=\"https://circleci.com/gh/contentful/contentful-merge.svg?style=shield\" alt=\"CircleCI\"\u003e\n  \u003c/a\u003e\n\n\u003c/p\u003e\n\n\u003c!-- header  END --\u003e\n\n## Introduction\n\n**Contentful**\n\n[Contentful](https://www.contentful.com/) provides content infrastructure for digital teams to power websites, apps, and devices. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enables developers and content creators to ship their products faster.\n\n**Contentful Merge**\n\nThe \u003cb\u003econtentful-merge\u003c/b\u003e CLI tool allows you to compare and merge entries across environments in a Contentful space. It can be used to create a changeset of all entry differences between two environments, and to apply this changeset to another environment, thereby effectively syncing the content of two environments.\n\n![](images/recording.gif)\n\n### Tracking\n\n- We want to know how people are using this tool, so that we can prioritize what to focus on next. We therefore collect some [analytics](src/analytics/index.ts) data.\n- \u003cb\u003eWe would love your feedback!\u003c/b\u003e [Here](https://forms.gle/uVj4sG2jKmQHotRd6) is a form where you can tell us about your experience and let us know which additional features you would like.\n\n---\n\n## Features\n\nTakes a space id and two environment ids and creates a changeset which details all entry differences between the two environments.\n\n- It uses the [Contentful Delivery API](https://www.contentful.com/developers/docs/references/content-delivery-api/) (CDA) to fetch all data.\n- A custom CDA client executes requests in the different environments in parallel.\n- All requests are batched.\n- In order to identify \u003cb\u003eadded\u003c/b\u003e and \u003cb\u003edeleted\u003c/b\u003e entries, entry ids are compared in both environments.\n- In order to identify \u003cb\u003eupdated\u003c/b\u003e entries, comparison happens in two steps:\n  The initial step involves identifying potentially diverging entries by examining the `sys.changedAt` property of all entries present in both environments.\n  Subsequently, for all entries with distinct `sys.changedAt` values, a more comprehensive comparison of their payload is performed. If any variations are found, a patch is generated to reflect the differences.\n\n\u003e \u003cb\u003e:bulb: Want to merge content types instead of entries? :bulb:\u003c/b\u003e\n\u003e We got you covered: Take a look at the [Merge App](https://www.contentful.com/marketplace/app/merge/) to your space, or, if you prefer the command line, check out the [Merge CLI](https://github.com/contentful/contentful-cli/tree/master/docs/merge).\n\n## Installation\n\nPrerequisite: node v18\n\n```bash\nnpm install -g contentful-merge\n```\n\n## Usage\n\n```sh-session\n$ npm install -g contentful-merge\n$ contentful-merge COMMAND\nrunning command...\n$ contentful-merge (--version)\ncontentful-merge/0.0.0 darwin-arm64 node-v20.2.0\n$ contentful-merge --help [COMMAND]\nUSAGE\n  $ contentful-merge COMMAND\n...\n```\n\n## Commands\n\n- [`contentful-merge create`](#contentful-merge-create)\n- [`contentful-merge apply`](#contentful-merge-apply)\n- [`contentful-merge help [COMMANDS]`](#contentful-merge-help-commands)\n\n#### `contentful-merge create`\n\n```\nCreate Entries Changeset\n\nUSAGE\n  $ contentful-merge create --space \u003cvalue\u003e --source \u003cvalue\u003e --target \u003cvalue\u003e --cda-token \u003cvalue\u003e [--request-batch-size \u003cvalue\u003e] [--output-file \u003cvalue\u003e] [--query-entries \u003cvalue\u003e] [--allowed-operations \u003cvalue\u003e]\n\nFLAGS\n  --cda-token=\u003cvalue\u003e           (required) CDA token, defaults to env: $CDA_TOKEN\n  --host=\u003cvalue\u003e                [default: api.contentful.com] Contentful API host\n  --query-entries=\u003cvalue\u003e       Query parameters for entries based on CDA. You can pass multiple query-entries flags.\n  --allowed-operations=\u003cvalue\u003e  [default: add,delete,update] Allowed operations for changeset. You can pass multiple allowed-operations flags.\n  --output-file=\u003cvalue\u003e         File path to changeset file\n  --request-batch-size=\u003cvalue\u003e  [default: 1000] Limit for every single request\n  --source=\u003cvalue\u003e              (required) Source environment id\n  --space=\u003cvalue\u003e               (required) Space id\n  --target=\u003cvalue\u003e              (required) Target environment id\n\nDESCRIPTION\n  Create Entries Changeset\n\nEXAMPLES\n  $ contentful-merge create --space \"\u003cspace id\u003e\" --source \"\u003csource environment id\u003e\" --target \"\u003ctarget environment id\u003e\" --cda-token \u003ccda token\u003e --output-file \u003coutput file path\u003e --query-entries \"content_type=\u003ccontent_type_id\u003e\" --query-entries \"sys.id=\u003centry_id\u003e\" --allowed-operations=add --allowed-operations=delete\n```\n\n#### `contentful-merge apply`\n\n```\nApply Changeset\n\nUSAGE\n  $ contentful-merge apply --space \u003cvalue\u003e --environment \u003cvalue\u003e --cma-token \u003cvalue\u003e [--file \u003cvalue\u003e] [--yes]\n\nFLAGS\n  --cma-token=\u003cvalue\u003e    (required) CMA token, defaults to env: $CMA_TOKEN\n  --host=\u003cvalue\u003e                [default: api.contentful.com] Contentful API host\n  --environment=\u003cvalue\u003e  (required) Target environment id\n  --file=\u003cvalue\u003e         (required) File path to changeset file\n  --space=\u003cvalue\u003e        (required) Space id\n  --yes                  Skips any confirmation before applying the changeset\n\nDESCRIPTION\n  Apply Changeset\n\nEXAMPLES\n  $ contentful-merge apply  --space \"\u003cspace-id\u003e\" --environment \"staging\" --file changeset.json\n\n  $ contentful-merge apply  --space \"\u003cspace-id\u003e\" --environment \"staging\" --file changeset.json --yes\n```\n\n#### `contentful-merge help [COMMANDS]`\n\nDisplay help for contentful-merge.\n\n```\nContentful CLI to diff and merge entries across environments\n\nVERSION\n  contentful-merge/0.0.0 darwin-arm64 node-v18.14.0\n\nUSAGE\n  $ contentful-merge [COMMAND]\n\nCOMMANDS\n  apply   Apply Changeset\n  create  Create Entries Changeset\n  help    Display help for contentful-merge.\n```\n\n## Data structure\n\nThe created changeset will be saved in JSON format in a file specified with the output-file flag or if the flag is not provided in a file called `changeset-[DATE]-[SPACE]-[SOURCE]-[TARGET].json`. It has the following basic structure:\n\n```javascript\n{\n  \"sys\": {\n    \"type\": \"Changeset\",\n    \"createdAt\": \"\u003cdate of changeset creation\u003e\",\n    \"space\": {\n      \"sys\": {\n        \"id\": \"\u003cspace id\u003e\",\n        \"linkType\": \"Space\",\n        \"type\": \"Link\"\n      }\n    },\n    \"source\": {\n      \"sys\": {\n        \"id\": \"\u003csource environment id\u003e\",\n        \"linkType\": \"Environment\",\n        \"type\": \"Link\"\n      }\n    },\n    \"target\": {\n      \"sys\": {\n        \"id\": \"\u003ctarget environment id\u003e\",\n        \"linkType\": \"Environment\",\n        \"type\": \"Link\"\n      }\n    }\n  },\n  \"items\": [\n    // \u003cindividual changeset items, see below\u003e\n  ]\n}\n```\n\nThe actual changes are in the `items` array. They have the following structure:\n\n```javascript\n// delete\n{\n  \"changeType\": \"delete\",\n  \"entity\": {\n    \"sys\": {\n      \"type\": \"Link\",\n      \"linkType\": \"Entry\",\n      \"id\": \"5mgMoU9aCWE88SIqSIMGYE\"\n    }\n  }\n}\n\n// add\n{\n  \"changeType\": \"add\",\n  \"entity\": {\n    \"sys\": {\n      \"type\": \"Link\",\n      \"linkType\": \"Entry\",\n      \"id\": \"5mgMoU9aCWE88SIqSIMGYE\"\n    }\n  },\n  \"data\": {\n    // \u003cpayload of added entry\u003e\n  }\n}\n\n// update\n{\n  \"changeType\": \"update\",\n  \"entity\": {\n    \"sys\": {\n      \"type\": \"Link\",\n      \"linkType\": \"Entry\",\n      \"id\": \"5mgMoU9aCWE88SIqSIMGYE\"\n    }\n  },\n  \"patch\": [\n    // \u003cindividual patch operations for each change\u003e\n  ]\n}\n```\n\nThere are three different change types: `add`, `update`, `delete`.\n\n- Changes of type `delete` include `changeType` and `entity`, as seen above.\n\n- Changes of type `update` include an additional property `patch`, with an array of patch operations where content differs between environments.\n\n- Changes of type `add` include an additional property `data` property with the usual Contentful entry payload.\n\nIf you want to see the data structure in practice, run the `create` command and have a look at the generated `changeset.json` file, or look at the [type definitions](src/engine/types.ts).\n\n## Limitations\n\nAt the moment we have a [limit amount](./src/config.base.ts#L2) of entries that can be in the generated changeset\n\n| Change Type | Limit  |\n| ----------- | ------ |\n| Add         | 10 000 |\n| Delete      | 10 000 |\n| Update      | 10 000 |\n|             |        |\n| Total       | 10 000 |\n\nFor apply command one can merge at most 10 000 changes at once.\n\nFurther limitations:\n\n- Tags, Assets, Comments, Workflows and Tasks are not compared and are not copied from one environment to another.\n- We only consider published entries during comparison, thus entries that are in draft state will not be compared.\n- Entries when added are immediately published.\n- Locales must be the same in the source and target environment.\n\n## FAQ\n\n**I have access to the environments I provided, yet the CLI responds with a 404, what could be wrong?**\n\nMake sure your CDA token has access to both environments, otherwise the CDA may respond with a 404.\n\n**I have made draft changes in my environment, but I don't see those in the changeset.**\n\nAs the CDA is used to fetch and compare entries, only published changes will be taken into account. Draft changes are not available via the CDA.\n\n## Feedback\n\nWant to report bugs, give feedback, request features?\n\n- Found some bugs? Head over to https://support.contentful.com and open a support ticket.\n- Want to request a feature or tell us your overall experience with this CLI? Feel free to use [this form](https://forms.gle/uVj4sG2jKmQHotRd6).\n\n## Code of Conduct\n\nWe want to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers. Read our full [Code of Conduct](https://www.contentful.com/developers/code-of-conduct/) here.\n\n## License\n\nThis project is licensed under [MIT license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fcontentful-merge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontentful%2Fcontentful-merge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fcontentful-merge/lists"}