The Perfetto team have created several scripts/tools which navigating and working with the Perfetto codebase. This page is mainly targetted towards frequent contributors to Perfetto (e.g. team members or external contribtors sending a lot of PRs).
These tools have a bit of a learning curve to them but can signficantly accelerate the developer experience.
From our days on Android, Perfetto has long worked on a “stacked-diff” model and its one that we like a lot inside the team. However, Github is very much not optimized for stack diffs.
We've explored a bunch of tools in the ecosystem (git-town, Graphite, git-spice, git-stack) and for various reasons, none of them fit all our requirements. These are:
To this end, we have developed a bunch of Python scripts which live in our tools folder and implement the above. You can add them to your git aliases. Modify your .gitconfig to include the following:
[alias]
# Create a new branch based on a parent and set its parent config
# Usage: git new-branch <new_branch_name> [--parent <parent_branch>]
new-branch = "!f() { ./tools/git_new_branch.py \"$@\"; }; f"
# Set the parent branch for the target branch (default: current)
# Usage: git set-parent <parent_branch> [--target <branch>]
set-parent = "!f() { ./tools/git_set_parent.py \"$@\"; }; f"
# Renames local branch and updates children's parent config
# Usage: git rename-branch --new <new_name> [--target <old_name>]
rename-branch = "!f() { ./tools/git_rename_branch.py \"$@\"; }; f"
# Checkout the configured parent of the target branch (default: current)
# Usage: git goto-parent [--target <branch>]
goto-parent = "!f() { ./tools/git_goto_parent.py \"$@\"; }; f"
# Find and checkout a child of the target branch (default: current)
# Usage: git goto-child [--target <branch>]
goto-child = "!f() { ./tools/git_goto_child.py \"$@\"; }; f"
# Update local stack segment (target+ancestors+descendants) via merges
# Usage: git update-stack [--target <branch>]
update-stack = "!f() { ./tools/git_update_stack.py \"$@\"; }; f"
# Update ALL local stacks via merges using topological sort
# Usage: git update-all
update-all = "!f() { ./tools/git_update_all.py \"$@\"; }; f"
# Push full stack segment (target+ancestors+descendants) and sync GitHub PRs
# Usage: git sync-stack [--target <branch>] [--remote <name>] [--draft] [--force]
sync-stack = "!f() { ./tools/git_sync_stack.py \"$@\"; }; f"
# Prune all local branches identical (no diff) to ~their effective parent
# Usage: git prune-all [--dry-run]
prune-all = "!f() { ./tools/git_prune_all.py \"$@\"; }; f"
All of these tools work by adding an entry to the repo's gitconfig called branch.{branch_name}.parent which keeps track of the parent branch. This is then used to figure out what a “stack” is and then perform operations on it.
A normal workflow using these tools might look like ths:
# Create a branch for the feature. git new-branch my-feature # .... hack away, make changes # Commit; will be used as PR title git commit -a -m 'My feature' # Create a new branch for adding something on top of the feature. git new-branch my-feature-2 # ... hack away, make changes # Commit; will be used as PR title git commit -a -m 'My feature changes' # Make GitHub PRs out of the above, correctly setting up base branches and # PR descriptions based on commit messages. git sync-stack # Go to my-feature to respond to review git goto-parent # ... make changes for review git commit -a -m 'Respond to review' # Update the stack so that my-feature-2 also has this commit. git update-stack # Sync to Github. git sync-all # ... my-feature is approved and merged on GitHub. # Do a merge again to make everything up-to-date. git-update-stack # Prune my-feature now it's no longer necessary git prune-all