Metadata Module Reference
The metadata module provides commit message and metadata transformations.
Commit Message
Section titled “Commit Message”metadata.squash_notes
Section titled “metadata.squash_notes”Customize squash commit message:
metadata.squash_notes( prefix = "Changes:\n", # Text before notes show_author = True, # Include authors show_description = True, # Include descriptions oldest_first = False, # Order by oldest first max = 100, # Max notes to include compact = False, # Compact format)metadata.replace_message
Section titled “metadata.replace_message”Replace entire commit message:
metadata.replace_message( "New commit message", # New message)metadata.add_header
Section titled “metadata.add_header”Add text to commit message:
metadata.add_header( text = "Header text", # Text to add new_line = True, # Add newline before ignore_label_not_found = False, # Don't fail on missing label)Labels
Section titled “Labels”metadata.expose_label
Section titled “metadata.expose_label”Make label available:
metadata.expose_label( "Label-Name", # Label to expose)metadata.remove_label
Section titled “metadata.remove_label”Remove label from message:
metadata.remove_label( "Internal-Label", # Label to remove)metadata.save_author
Section titled “metadata.save_author”Save author to label:
metadata.save_author( label = "ORIGINAL_AUTHOR", # Label name)metadata.restore_author
Section titled “metadata.restore_author”Restore author from label:
metadata.restore_author( label = "ORIGINAL_AUTHOR", # Label name search_all_changes = True, # Search all changes)Author Mapping
Section titled “Author Mapping”metadata.map_author
Section titled “metadata.map_author”Map author emails:
metadata.map_author({ "old@email.com": "new@email.com",})metadata.use_last_change
Section titled “metadata.use_last_change”Use author from last change:
metadata.use_last_change()Examples
Section titled “Examples”Preserve Co-authors
Section titled “Preserve Co-authors”transformations = [ metadata.expose_label("Co-authored-by"), metadata.expose_label("Signed-off-by"),]Custom Squash Message
Section titled “Custom Squash Message”transformations = [ metadata.squash_notes( prefix = "Imported changes:\n\n", show_author = True, show_description = True, ), metadata.add_header("Synced-From: internal"),]Map Internal Emails
Section titled “Map Internal Emails”transformations = [ metadata.map_author({ "alice@internal.corp": "alice@public.example.com", "bob@internal.corp": "bob@public.example.com", }),]Scrubbing
Section titled “Scrubbing”metadata.scrubber
Section titled “metadata.scrubber”Remove or replace parts of commit messages using regex:
metadata.scrubber( regex = "(^|\\n)CONFIDENTIAL:(.|\\n)*", msg_if_no_match = None, fail_if_no_match = False, replacement = "",)Parameters:
| Parameter | Type | Description |
|---|---|---|
regex | string | Pattern to match (multiline mode) |
msg_if_no_match | string | Default message if no match |
fail_if_no_match | bool | Fail if pattern not found |
replacement | string | Replacement text (supports $1, $2 groups) |
Remove confidential sections:
# Message: "Public info\nCONFIDENTIAL:\nSecret stuff"# Result: "Public info"metadata.scrubber("(^|\\n)CONFIDENTIAL:(.|\\n)*")Keep only tagged content:
# Only keep content between <public></public> tagsmetadata.scrubber( regex = "^(?:\\n|.)*<public>((?:\\n|.)*)</public>(?:\\n|.)*$", replacement = "$1", msg_if_no_match = "Internal change.",)metadata.verify_match
Section titled “metadata.verify_match”Verify commit message matches (or doesn’t match) a pattern:
metadata.verify_match( regex = "<public>(.|\\n)*</public>", verify_no_match = False,)Require public description:
# Fail if message doesn't contain <public> tagsmetadata.verify_match("<public>(.|\\n)*</public>")Block sensitive patterns:
# Fail if message contains "SECRET" or "INTERNAL"metadata.verify_match( regex = "(SECRET|INTERNAL)", verify_no_match = True,)Reference Mapping
Section titled “Reference Mapping”metadata.map_references
Section titled “metadata.map_references”Update commit references to match destination format:
metadata.map_references( before = "origin/${reference}", after = "destination/${reference}", regex_groups = { "before_ref": "[0-9a-f]+", "after_ref": "[0-9]+", },)Map GitHub to internal references:
# "Fixes #123" → "Fixes internal/456"metadata.map_references( before = "#${reference}", after = "internal/${reference}", regex_groups = {"before_ref": "[0-9]+"},)Advanced Examples
Section titled “Advanced Examples”Complete Open-Source Pipeline
Section titled “Complete Open-Source Pipeline”transformations = [ # Scrub confidential content metadata.scrubber( regex = "(^|\\n)INTERNAL:(.|\\n)*", ),
# Verify no secrets leaked metadata.verify_match( regex = "(PASSWORD|API_KEY|SECRET)", verify_no_match = True, ),
# Map author emails metadata.map_author({ "dev@internal.corp": "dev@public.example.com", }),
# Add tracking header metadata.add_header( text = "Imported from internal: ${COPYBARA_REVISION}", ignore_label_not_found = True, ),]Bi-directional Sync with Labels
Section titled “Bi-directional Sync with Labels”# Export workflowexport_transforms = [ metadata.save_author("INTERNAL_AUTHOR"), metadata.add_header("Exported-From: internal"),]
# Import workflowimport_transforms = [ metadata.restore_author("INTERNAL_AUTHOR"), metadata.expose_label("GITHUB_PR_NUMBER"),]Squash with Custom Format
Section titled “Squash with Custom Format”metadata.squash_notes( prefix = "## Changes\n\n", compact = False, show_author = True, show_ref = True, oldest_first = True, max = 50,)Output:
## Changes
--abc123 by Alice <alice@example.com>:
First change description
Extended details here--def456 by Bob <bob@example.com>:
Second change description