Skip to content

Metadata Module Reference

The metadata module provides commit message and metadata transformations.

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
)

Replace entire commit message:

metadata.replace_message(
"New commit message", # New message
)

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
)

Make label available:

metadata.expose_label(
"Label-Name", # Label to expose
)

Remove label from message:

metadata.remove_label(
"Internal-Label", # Label to remove
)

Save author to label:

metadata.save_author(
label = "ORIGINAL_AUTHOR", # Label name
)

Restore author from label:

metadata.restore_author(
label = "ORIGINAL_AUTHOR", # Label name
search_all_changes = True, # Search all changes
)

Map author emails:

metadata.map_author({
"old@email.com": "new@email.com",
})

Use author from last change:

metadata.use_last_change()
transformations = [
metadata.expose_label("Co-authored-by"),
metadata.expose_label("Signed-off-by"),
]
transformations = [
metadata.squash_notes(
prefix = "Imported changes:\n\n",
show_author = True,
show_description = True,
),
metadata.add_header("Synced-From: internal"),
]
transformations = [
metadata.map_author({
"alice@internal.corp": "alice@public.example.com",
"bob@internal.corp": "bob@public.example.com",
}),
]

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:

ParameterTypeDescription
regexstringPattern to match (multiline mode)
msg_if_no_matchstringDefault message if no match
fail_if_no_matchboolFail if pattern not found
replacementstringReplacement 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> tags
metadata.scrubber(
regex = "^(?:\\n|.)*<public>((?:\\n|.)*)</public>(?:\\n|.)*$",
replacement = "$1",
msg_if_no_match = "Internal change.",
)

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> tags
metadata.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,
)

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]+"},
)
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,
),
]
# Export workflow
export_transforms = [
metadata.save_author("INTERNAL_AUTHOR"),
metadata.add_header("Exported-From: internal"),
]
# Import workflow
import_transforms = [
metadata.restore_author("INTERNAL_AUTHOR"),
metadata.expose_label("GITHUB_PR_NUMBER"),
]
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