Frequently Asked Questions
Answers to frequently asked questions, sourced from the Copybara mailing list and GitHub issues.
General
Section titled “General”Is Copybara stateless?
Section titled “Is Copybara stateless?”Yes, by design. Copybara stores state in the destination repository itself, not in local files.
From Issue #196, a Copybara collaborator confirmed:
“Yes, it is stateless. We designed like that on purpose.”
A contributor clarified the mechanism:
“Copybara is stateless because it relies on the
GitOrigin-RevIdtrailer existing in the destination repository in order to determine revision list that needs to be migrated.”
This means multiple users or CI jobs can run Copybara for the same config and get consistent results. The ~/copybara folder is only used for caching, not persistent state.
See also: State Management Guide
Can I use a custom state label instead of GitOrigin-RevId?
Section titled “Can I use a custom state label instead of GitOrigin-RevId?”Yes. Use the custom_rev_id parameter in core.workflow.
From Custom label for copybara state:
There’s
experimental_custom_rev_idsetting incore.workflowwhich lets you customize the revision ID.
core.workflow( name = "default", # Use custom label instead of GitOrigin-RevId custom_rev_id = "X-Backport-Id", # ...)Is bidirectional sync possible?
Section titled “Is bidirectional sync possible?”Yes, but it requires two separate workflows. Copybara requires one repository to be the source of truth.
From Issue #140:
A user asked about using Copybara for bidirectional sync between external (public) and internal (private) GitHub repos.
The pattern is:
- Push workflow: Internal → External (source of truth is internal)
- Pull workflow: Import external PRs back to internal
See also: Advanced Use Cases
Configuration
Section titled “Configuration”How do I use environment variables in copy.bara.sky?
Section titled “How do I use environment variables in copy.bara.sky?”You cannot directly reference environment variables like ${VAR} in the config file.
From Issue #125, the recommended approaches are:
1. Use Starlark macros to create parameterized workflows:
def make_workflow(name, destination_url): return core.workflow( name = name, destination = git.destination(url = destination_url), # ... )
# Define multiple workflowsmake_workflow("prod", "https://github.com/org/prod")make_workflow("staging", "https://github.com/org/staging")2. Use command-line flags to override values at runtime:
copybara migrate copy.bara.sky \ --git-destination-url "https://x-access-token:${TOKEN}@github.com/org/repo"3. Generate config from template before running Copybara:
envsubst < copy.bara.sky.template > copy.bara.skycopybara migrate copy.bara.skyCan I split configuration across multiple files?
Section titled “Can I split configuration across multiple files?”Yes! Use Starlark’s load() statement to import from other .bara.sky files.
From Issue #268, a Copybara collaborator confirmed:
“Yes, starlark has a
loadstatement to include other files. You will want to write a function that returns a list of transforms.”
Example:
# Define reusable replacementsreplacements = { "old_name": "new_name", "old_import": "new_import",}
def common_transforms(): return [ core.replace(before = k, after = v) for k, v in replacements.items() ]load("common", "common_transforms")
core.workflow( name = "default", transformations = common_transforms() + [ # workflow-specific transforms ], # ...)This pattern is useful for:
- Organizing transforms per directory/module
- Sharing common replacements across workflows
- Keeping the main config file clean
Why does my config file need to be named copy.bara.sky?
Section titled “Why does my config file need to be named copy.bara.sky?”It’s a requirement, not just convention. The filename must be copy.bara.sky, but the path can vary.
From Issue #127, a Copybara maintainer clarified:
“This is expected. Copybara requires that the main config file is called ‘copy.bara.sky’. The path can change, but not the name.”
Valid examples:
# These work - different paths, same filenamecopybara migrate /path/to/copy.bara.skyCOPYBARA_CONFIG=/configs/copy.bara.sky
# This does NOT work - different filenameCOPYBARA_CONFIG=my-config.sky # Error!Docker & CI
Section titled “Docker & CI”Is there an official Docker image?
Section titled “Is there an official Docker image?”No official image is published by Google. You can build from source or use community images.
From Issue #82:
It would be super helpful to have a copy of the latest master or release published as a docker image.
The issue remains open. Google has not published an official image.
Community alternatives:
- Olivr/copybara-action - Builds nightly images
gcr.io/copybara-docker/copybara- Community-maintained image- Build your own from the Dockerfile in the repo
How do I pass command line arguments with Docker?
Section titled “How do I pass command line arguments with Docker?”Use environment variables, as the entrypoint doesn’t pass arguments directly.
From Issue #158:
The Docker entrypoint does not pass command line arguments that are given to docker run.
docker run -e COPYBARA_WORKFLOW=my-workflow \ -e COPYBARA_SOURCEREF=main \ -v $(pwd):/usr/src/app \ copybaraEnvironment variables:
COPYBARA_CONFIG- Config file path (default:copy.bara.sky)COPYBARA_WORKFLOW- Workflow nameCOPYBARA_SOURCEREF- Source referenceCOPYBARA_OPTIONS- Additional optionsCOPYBARA_SUBCOMMAND- Subcommand (migrate, validate, etc.)
Can I run Copybara in GitHub Actions?
Section titled “Can I run Copybara in GitHub Actions?”Yes. Use the community action or Docker directly.
From Issue #123:
Is it possible to run Copybara in GitHub actions?
- uses: olivr/copybara-action@v1 with: ssh_key: ${{ secrets.SSH_KEY }}See also: GitHub Actions Guide
Authentication
Section titled “Authentication”How do I use deploy keys with Copybara?
Section titled “How do I use deploy keys with Copybara?”Challenge: GitHub doesn’t allow the same SSH key as a deploy key in two different repos.
From Issue #131:
Users wanted to give Copybara access to origin with read-only key and destination with read/write key.
Solutions:
- Use a machine user account with access to both repos
- Use GitHub App for authentication (Issue #264)
- Use HTTPS tokens instead of SSH:
Terminal window echo "https://x-access-token:${TOKEN}@github.com" >> ~/.git-credentials
How do I sync between two private repos?
Section titled “How do I sync between two private repos?”Mount your SSH credentials into the Docker container.
From Copybara for 2 private repos:
docker run -v ~/.ssh:/root/.ssh \ -v ~/.gitconfig:/root/.gitconfig \ -v $(pwd):/usr/src/app \ copybara migrate copy.bara.skySee also: Authentication Guide
Workflows & Testing
Section titled “Workflows & Testing”How do I test a Copybara workflow before running it?
Section titled “How do I test a Copybara workflow before running it?”Use --dry-run and folder.destination.
From Recommended process for adding new workflows?:
How does one test a copybara workflow? Testing workflows is difficult.
Approaches:
-
Dry run - Preview without making changes:
Terminal window copybara migrate copy.bara.sky --dry-run -
Folder destination - Output to local folder for review:
core.workflow(destination = folder.destination(),# ...) -
Validate command - Check config syntax:
Terminal window copybara validate copy.bara.sky
From Understanding the workflow:
The workaround was to use folder destination so they could iterate and re-apply copybara multiple times, then push manually.
How do I preview changes before pushing?
Section titled “How do I preview changes before pushing?”Use folder.destination() during development.
From Issue #43:
A user was wondering how to dry run Copybara to preview results.
# Development/testing workflowcore.workflow( name = "test", destination = folder.destination(), # ... same config as production)Then inspect the output folder before switching to git.destination().
Transformations
Section titled “Transformations”How do I overlay/replace specific files?
Section titled “How do I overlay/replace specific files?”From Issue #43, there are two approaches:
Option 1: Glob addition + core.move (recommended for reversibility)
Include the overlay file via glob, then move it into place:
core.workflow( # Include the overlay file explicitly origin_files = glob(["**"], exclude = ["internal/**"]) + glob(["internal/README.md"]), transformations = [ core.move("internal/README.md", "README.md"), ],)Option 2: core.copy with overwrite
Copy a file over an existing one:
transformations = [ core.copy( before = "internal/README.md", after = "README.md", overwrite = True, ),]Where can I find examples of custom transformations?
Section titled “Where can I find examples of custom transformations?”From Issue #254:
A user asked for examples of more complex transformations beyond simple copies and moves.
Resources:
- Custom Transformations Guide
- MongoDB’s copy.bara.sky - Production example
- CUE’s copy.bara.sky - Gerrit integration
Workflow Modes
Section titled “Workflow Modes”When should I use SQUASH vs ITERATIVE mode?
Section titled “When should I use SQUASH vs ITERATIVE mode?”From Issue #265:
SQUASH mode:
- Combines all commits into one
- Better for detecting empty/duplicate changes
- Recommended when you don’t need individual commit history
ITERATIVE mode:
- Preserves individual commits
- Can cause issues with bidirectional sync (commits may duplicate)
- Best when combined with CHANGE_REQUEST for the reverse direction
A maintainer explained:
“Copybara enforces a single source of truth for a set of files.”
Recommended pattern:
# Source of truth → mirror (ITERATIVE)core.workflow( name = "push", mode = "ITERATIVE", # ...)
# External PRs → source of truth (CHANGE_REQUEST)core.workflow( name = "pull", mode = "CHANGE_REQUEST", # ...)Does Copybara preserve merge commits?
Section titled “Does Copybara preserve merge commits?”No. Copybara produces linear history by design.
From Issue #99, a maintainer confirmed:
“Copybara moves commits as linear history.”
Even with first_parent = False, merge commits are treated as regular commits. If you need to preserve merge structure, consider:
- Using
integratesfor external changes - Using
git.github_pr_destinationfor PR-based workflows - Adding
metadata.expose_label("COPYBARA_INTEGRATE_REVIEW")
File Selection
Section titled “File Selection”What’s the difference between origin_files and destination_files?
Section titled “What’s the difference between origin_files and destination_files?”origin_files - Controls which files are read from source:
# Only sync src/ and docs/, exclude secretsorigin_files = glob(["src/**", "docs/**"], exclude = ["**/*.secret"])destination_files - Controls which files can be modified in destination:
# Only touch files in vendor/lib/, leave everything else alonedestination_files = glob(["vendor/lib/**"])Why are excluded files still being copied?
Section titled “Why are excluded files still being copied?”From Issue #56:
Problem: Files added to exclude array still appear in destination.
Solution: Make sure exclusions are in origin_files, not destination_files:
# CORRECT - exclude in origin_filesorigin_files = glob(["**"], exclude = ["secrets/**", "*.key"])
# WRONG - this only prevents writing/deleting, not readingdestination_files = glob(["**"], exclude = ["secrets/**"])Troubleshooting
Section titled “Troubleshooting”Why does Copybara say “No changes to migrate”?
Section titled “Why does Copybara say “No changes to migrate”?”The destination already has all changes from origin.
Copybara reads GitOrigin-RevId from the last destination commit and only syncs newer changes.
Solutions:
- Use
--ignore-noopto not fail on no changes - Use
--forceto re-sync anyway - Use
--last-revto start from a specific commit
See also: Common Issues
What does “No new changes to import for resolved ref” mean?
Section titled “What does “No new changes to import for resolved ref” mean?”From Issue #27:
This warning appears when you specify a --last-rev that’s already the final commit in your source repository.
Solution for importing full history:
# First run - point to earliest commit to importcopybara migrate copy.bara.sky --last-rev <first-commit-sha>
# Subsequent runs - no flag needed, Copybara auto-detectscopybara migrate copy.bara.skyA contributor noted there’s no “zero” SHA-1 reference in Git for importing from the very beginning, so you must specify the first commit you want.
Why is Copybara stuck on the initial run?
Section titled “Why is Copybara stuck on the initial run?”From Copybara is stuck on the initial run:
Common causes:
- Large repository - Initial clone takes time
- Network issues - Slow connection to Git server
- Many commits - Use
--last-revto limit history
# Start from recent history onlycopybara migrate copy.bara.sky --last-rev HEAD~100How do I handle submodules?
Section titled “How do I handle submodules?”From Issue #84 and Issue #216:
Submodule not registered in destination / Cannot find object when running with submodules.
Copybara has limited submodule support. Consider:
- Using
git.origin()withsubmodules = "NO"(default) - Flattening submodules before sync
- Syncing submodule content separately
Feature Requests
Section titled “Feature Requests”Does Copybara support GitLab/Bitbucket natively?
Section titled “Does Copybara support GitLab/Bitbucket natively?”Git operations work, but no native API integration (unlike GitHub).
From Issue #153:
GitLab support is requested.
Workaround: Use git.origin() and git.destination() with HTTPS/SSH URLs.
See also: Unsupported Platforms
Can I force push to destination?
Section titled “Can I force push to destination?”Not recommended, but possible with custom scripts.
From Issue #136:
A user wanted to protect the branch in the destination repo.
Copybara is designed for incremental sync, not force pushes. If you need to rewrite history, consider resetting the destination branch manually first.
Community Resources
Section titled “Community Resources”- Mailing List: groups.google.com/g/copybara-discuss
- GitHub Issues: github.com/google/copybara/issues
- Community Resources: Community & Resources Guide