PushPull
PushPull stores selected WordPress content in a Git repository using a canonical JSON representation instead of raw database dumps.
Beta notice
This is a beta plugin. It is still under active development, has limited functionality, and currently supports only a narrow subset of the intended PushPull feature set.
The current release supports these managed domains:
- Primary domains: generateblocks_global_styles generateblocks_conditions wordpress_block_patterns wordpress_menus wordpress_pages wordpress_posts wordpress_custom_css generatepress_elements wordpress_attachments (explicit opt-in only)
- Config domains: wordpress_core_configuration
- Overlay domains: translation_management (WPML-backed) media_organization (Real Media Library-backed)
PushPull keeps a local Git-like repository inside WordPress database tables and supports the following workflow directly from WordPress admin:
- Test the remote GitHub or GitLab connection
- Commit live managed content into the local repository
- Initialize an empty remote repository
- Fetch remote commits into a local tracking ref
- Diff live, local, and remote states
- Pull remote changes through fetch + merge
- Merge remote changes into the local branch
- Resolve conflicts when needed
- Apply repository content back into WordPress
- Push local commits to GitHub or GitLab
The plugin also includes:
- An audit log screen
- Local repository reset tooling
- Remote branch reset tooling that creates one commit removing all tracked files from the branch
- Global and per-domain managed-content views in the admin UI
- Primary, config, and overlay domain separation in settings and Managed Content
- A high-level PushPull status dropdown in the WordPress admin bar
- Menu structure export and apply with hierarchy and theme location assignment
Current scope
This is an early, focused release. At the moment, PushPull is intentionally limited to:
- GitHub and GitLab as implemented remote providers
- Managed domains across three families: generateblocks/global-styles/ generateblocks/conditions/ wordpress/block-patterns/ wordpress/menus/ wordpress/pages/ wordpress/posts/ wordpress/custom-css/ wordpress/attachments/ wordpress/configuration/ wordpress/generatepress-elements/ translations/management/ media/organization/
- Canonical JSON storage with one file per managed item for manifest-backed sets, plus directory-backed storage for attachments using
attachment.jsonand the binary file - Explicit opt-in attachment sync through a media-library checkbox
- Overlay domains that scope themselves to enabled compatible base domains rather than exporting every backend row blindly
It does not yet manage forms, arbitrary wp_options, or arbitrary plugin data.
How PushPull represents content
PushPull does not use WordPress post IDs as repository identity.
For the currently supported managed sets it stores:
- One canonical JSON file per managed item
- One separate
manifest.jsonfile for manifest-backed sets that preserves logical ordering - One directory per attachment for the attachments set, containing
attachment.jsonand the binary file - Stable logical keys instead of environment-specific database IDs
- Canonical logical-key references for cross-domain relationships such as reading settings, translation groups, media folders, GeneratePress condition targets, and menu object references
- Recursive placeholder normalization for current-site absolute URLs in post-type-backed content
Configuration
PushPull currently supports GitHub and GitLab repositories.
For GitHub, grant:
- Repository metadata read access
- Repository contents read and write access
For GitLab fine-grained personal access tokens, grant:
Project: ReadBranch: ReadCommit: ReadCommit: CreateRepository: Read
In PushPull > Settings:
- Select
GitHuborGitLabas the provider - Enter the repository owner and repository name
- Enter the target branch
- Enter the API token
- Enable one or more managed content domains in the managed content settings
- Click
Test connection - Save the settings
Workflow
The normal workflow is:
Committo snapshot the current live managed-set content into the local repositoryFetchto import the current remote branch intorefs/remotes/origin/<branch>- Inspect the live/local and local/remote diff views if needed
Pullfor the common fetch + merge flow, orMergemanually after fetch when you want review firstApply repo to WordPresswhen you want the local branch state written back into WordPressPushwhen you want local commits published to GitHub or GitLab
If both local and remote changed, PushPull can persist conflicts, let you resolve them in the admin UI, and then finalize a merge commit.
When pushing to GitLab, PushPull currently linearizes local merge results into a normal commit on the remote branch instead of preserving merge topology. The merged tree content is preserved; only the remote Git history shape is flattened.
Empty repositories
If the configured GitHub or GitLab repository exists but has no commits yet, Test connection will report that the repository is reachable but empty.
In that case, click Initialize remote repository. PushPull will:
- create the first commit on the configured branch
- fetch that initial commit into the local remote-tracking ref
- make the repository ready for normal commit, fetch, merge, apply, and push workflows
You do not need to create the first commit manually on the provider before using PushPull.
TODO
- Cache the admin-bar PushPull status summary so the high-level live/local and local/remote aggregation is not recomputed on every page view.
- Move chunked async provider resumability fully into the provider layer so
AsyncBranchOperationRunnerno longer needs provider-specific GitLab staging rehydration logic. - Improve push progress and recap reporting to distinguish newly uploaded objects from objects reused from the remote history.
- Surface unresolved logical-reference mapping issues, such as GeneratePress condition IDs that could not be converted to logical placeholders, instead of silently leaving mixed raw IDs and canonical refs.
External services
PushPull connects to the GitHub or GitLab API for the repository you configure in the plugin settings.
The plugin uses the provider REST API to:
- Read repository metadata and the default branch
- Read and update branch refs
- Read and create Git objects or provider-equivalent commit actions
- Test repository access before sync operations
PushPull sends the following information to the configured provider over HTTPS:
- The repository owner, repository name, branch, and API base URL
- Your configured API token in the provider-specific authentication header
- Canonical JSON representations of the managed content you choose to commit and push
- Commit metadata such as commit messages and, if configured, author name and email
In the current release, the managed content sent to the provider is limited to the enabled supported domains: GenerateBlocks Global Styles, GenerateBlocks Conditions, WordPress Block Patterns, WordPress Menus, WordPress Pages, WordPress Posts, WordPress Custom CSS, GeneratePress Elements, explicitly opted-in WordPress Attachments, WordPress core configuration, WPML-backed translation management, and Real Media Library-backed media organization.
PushPull does not send your whole WordPress database to the provider. It only sends the managed content represented by the enabled adapters.
GitHub terms of service: https://docs.github.com/en/site-policy/github-terms/github-terms-of-service GitHub privacy statement: https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement GitLab terms: https://about.gitlab.com/terms/ GitLab privacy statement: https://about.gitlab.com/privacy/