bazel_roll: Use git_roll_util rolls

Use fuchsia/git_roll_util Roll objects instead of pigweed/roll_util Roll
objects.

Bug: b/359925419
Change-Id: Ib292fde5f6057b9829f8cfcb037e10e326be8696
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/235015
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
Commit-Queue: Rob Mohr <mohrr@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
diff --git a/recipe_modules/bazel_roll/__init__.py b/recipe_modules/bazel_roll/__init__.py
index 653bbe7..6c14066 100644
--- a/recipe_modules/bazel_roll/__init__.py
+++ b/recipe_modules/bazel_roll/__init__.py
@@ -19,8 +19,6 @@
 DEPS = [
     'fuchsia/git_roll_util',
     'pigweed/bazel',
-    'pigweed/checkout',
-    'pigweed/roll_util',
     'recipe_engine/path',
     'recipe_engine/step',
 ]
diff --git a/recipe_modules/bazel_roll/api.py b/recipe_modules/bazel_roll/api.py
index ec8c439..274bd61 100644
--- a/recipe_modules/bazel_roll/api.py
+++ b/recipe_modules/bazel_roll/api.py
@@ -31,8 +31,8 @@
         GitRepository,
     )
     from recipe_engine import config_types
+    from RECIPE_MODULES.fuchsia.git_roll_util import api as git_roll_util_api
     from RECIPE_MODULES.pigweed.checkout import api as checkout_api
-    from RECIPE_MODULES.pigweed.roll_util import api as roll_util_api
 
 
 class BazelRollApi(recipe_api.RecipeApi):
@@ -99,7 +99,7 @@
         self,
         checkout: checkout_self.m.CheckoutContext,
         git_repository: GitRepository,
-    ) -> list[roll_util_api.Roll]:
+    ) -> list[git_roll_util_api.Roll]:
         workspace_path = self.workspace_path(
             checkout.root,
             git_repository.workspace_path,
@@ -112,45 +112,16 @@
             checkout.remotes_equivalent,
         )
 
-        project_dir = self.m.path.start_dir / 'project'
-
-        project_checkout = self.m.checkout(
-            CheckoutOptions(
-                remote=git_repository.remote,
-                branch=git_repository.branch,
-                use_trigger=False,
-            ),
-            root=project_dir,
-        )
-
-        # In case new_revision is a branch name we need to retrieve the hash it
-        # resolves to.
-        if not re.search(r'^[0-9a-f]{40}$', new_revision):
-            new_revision = self.m.checkout.get_revision(
-                project_dir, 'get new revision', test_data='2' * 40
-            )
-
         update_result = self.m.bazel.update_commit_hash(
             checkout=checkout,
             project_remote=git_repository.remote,
             new_revision=new_revision,
             path=workspace_path,
+            delay_write=True,
         )
         if not update_result:
             self.m.step.empty('failed to update commit hash', status='FAILURE')
 
-        direction = self.m.roll_util.get_roll_direction(
-            project_dir, update_result.old_revision, new_revision
-        )
-
-        # If the primary roll is not necessary or is backwards we can exit
-        # immediately.
-        if not self.m.roll_util.can_roll(direction):
-            self.m.roll_util.skip_roll_step(
-                git_repository.remote, update_result.old_revision, new_revision
-            )
-            return []
-
         project_name = git_repository.name or update_result.project_name
         if not project_name:
             self.m.step.empty(
@@ -158,14 +129,16 @@
                 status='FAILURE',
             )
 
-        return [
-            self.m.roll_util.create_roll(
-                project_name=project_name,
-                old_revision=update_result.old_revision,
-                new_revision=new_revision,
-                proj_dir=project_dir,
-                direction=direction,
-            ),
-        ]
+        try:
+            roll = self.m.git_roll_util.get_roll(
+                repo_url=git_repository.remote,
+                repo_short_name=project_name,
+                old_rev=update_result.old_revision,
+                new_rev=new_revision,
+            )
 
-        return rolls
+        except self.m.git_roll_util.BackwardsRollError:
+            return []
+
+        update_result.finalize()
+        return [roll]
diff --git a/recipe_modules/bazel_roll/tests/full.expected/backwards.json b/recipe_modules/bazel_roll/tests/full.expected/backwards.json
index b3ba50c..60415f6 100644
--- a/recipe_modules/bazel_roll/tests/full.expected/backwards.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/backwards.json
@@ -15,543 +15,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "checkout pigweed"
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.options with defaults",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@miss@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.ensure git cache dir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.write git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "remote.origin.url",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.remote set-url",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.remove git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copytree",
-      "--symlinks",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000019.0
-      }
-    },
-    "name": "checkout pigweed.git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "add",
-      "origin",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/snapshot"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.submodule-status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "submodule status filler text",
-      "[START_DIR]/snapshot/submodules.log"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.write submodule snapshot",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
-      "@@@STEP_LOG_END@submodules.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[START_DIR]/snapshot/git.log"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--max-count=1",
-      "--pretty=format:%H"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get new revision",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@2222222222222222222222222222222222222222@@@"
-    ]
-  },
-  {
     "cmd": [
       "vpython3",
       "-u",
@@ -733,6 +196,17 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}"
+  },
+  {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "vpython3",
       "-u",
@@ -740,86 +214,102 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/0.\n    commit = \"2222222222222222222222222222222222222222\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
-      "[START_DIR]/checkout/WORKSPACE"
+      "RECIPE_MODULE[fuchsia::gitiles]/resources/cipd.ensure",
+      "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "write new WORKSPACE",
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages.read ensure file",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Warning: this entry is automatically updated.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Last updated 2012-05-14.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: By https://cr-buildbucket.appspot.com/build/0.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_END@WORKSPACE@@@"
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gitiles/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
     ]
   },
   {
     "cmd": [],
-    "name": "get roll direction",
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@backward@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "1111111111111111111111111111111111111111",
-      "2222222222222222222222222222222222222222"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get roll direction.is forward",
-    "timeout": 600.0,
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
   },
   {
     "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "2222222222222222222222222222222222222222",
-      "1111111111111111111111111111111111111111"
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
     ],
-    "cwd": "[START_DIR]/project",
-    "name": "get roll direction.is backward",
-    "timeout": 600.0,
+    "infra_step": true,
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure package directory",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gitiles/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gitiles/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gitiles",
+      "log",
+      "-json-output",
+      "/path/to/tmp/json",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "1111111111111111111111111111111111111111..h3ll0"
+    ],
+    "infra_step": true,
+    "name": "log pigweed",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
     ]
   },
   {
     "cmd": [],
-    "name": "cancelling roll",
+    "name": "detected backwards roll",
     "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@not updating from 1111111 to 2222222 because 1111111 is newer than 2222222@@@",
-      "@@@STEP_LINK@1111111111111111111111111111111111111111@https://pigweed.googlesource.com/pigweed/pigweed/+/1111111111111111111111111111111111111111@@@",
-      "@@@STEP_LINK@2222222222222222222222222222222222222222@https://pigweed.googlesource.com/pigweed/pigweed/+/2222222222222222222222222222222222222222@@@"
+      "@@@STEP_TEXT@Detected backwards roll: expected 1111111 to precede h3ll0 in git history@@@"
     ]
   },
   {
     "cmd": [],
-    "name": "nothing to roll, exiting"
+    "name": "commit message",
+    "~followup_annotations": [
+      "@@@STEP_SUMMARY_TEXT@roll:@@@"
+    ]
   },
   {
     "name": "$result"
diff --git a/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json b/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json
index ccb03d1..0aee7ac 100644
--- a/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json
@@ -27,889 +27,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "checkout foo"
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/foo\"\nbranch: \"main\"\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.options with defaults",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/foo\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@miss@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.ensure git cache dir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.write git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git/pigweed.googlesource.com-foo"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-foo",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "remote.origin.url",
-      "https://pigweed.googlesource.com/foo"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-foo",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.remote set-url",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-foo",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-foo",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-foo",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-foo",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.cache.remove git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copytree",
-      "--symlinks",
-      "[CACHE]/git/pigweed.googlesource.com-foo",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.git checkout.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000019.0
-      },
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "add",
-      "origin",
-      "https://pigweed.googlesource.com/foo"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout foo.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/snapshot"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.submodule-status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "submodule status filler text",
-      "[START_DIR]/snapshot/submodules.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.write submodule snapshot",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
-      "@@@STEP_LOG_END@submodules.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[START_DIR]/snapshot/git.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout foo.write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--max-count=1",
-      "--pretty=format:%H"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "get new revision",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@2222222222222222222222222222222222222222@@@"
-    ]
-  },
-  {
     "cmd": [
       "vpython3",
       "-u",
diff --git a/recipe_modules/bazel_roll/tests/full.expected/no-trigger.json b/recipe_modules/bazel_roll/tests/full.expected/no-trigger.json
index 92b9e48..6cac09c 100644
--- a/recipe_modules/bazel_roll/tests/full.expected/no-trigger.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/no-trigger.json
@@ -15,543 +15,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "checkout pigweed"
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.options with defaults",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@miss@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.ensure git cache dir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.write git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "remote.origin.url",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.remote set-url",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.cache.remove git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copytree",
-      "--symlinks",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000019.0
-      }
-    },
-    "name": "checkout pigweed.git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "add",
-      "origin",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/snapshot"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.submodule-status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "submodule status filler text",
-      "[START_DIR]/snapshot/submodules.log"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.write submodule snapshot",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
-      "@@@STEP_LOG_END@submodules.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed.log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[START_DIR]/snapshot/git.log"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed.write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--max-count=1",
-      "--pretty=format:%H"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get new revision",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@2222222222222222222222222222222222222222@@@"
-    ]
-  },
-  {
     "cmd": [
       "vpython3",
       "-u",
@@ -733,6 +196,17 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}"
+  },
+  {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "vpython3",
       "-u",
@@ -740,133 +214,188 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/0.\n    commit = \"2222222222222222222222222222222222222222\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
-      "[START_DIR]/checkout/WORKSPACE"
+      "RECIPE_MODULE[fuchsia::gitiles]/resources/cipd.ensure",
+      "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "write new WORKSPACE",
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages.read ensure file",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Warning: this entry is automatically updated.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Last updated 2012-05-14.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: By https://cr-buildbucket.appspot.com/build/0.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_END@WORKSPACE@@@"
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gitiles/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
     ]
   },
   {
     "cmd": [],
-    "name": "get roll direction",
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@forward@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "1111111111111111111111111111111111111111",
-      "2222222222222222222222222222222222222222"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get roll direction.is forward",
-    "timeout": 600.0,
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
   },
   {
     "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "2222222222222222222222222222222222222222",
-      "1111111111111111111111111111111111111111"
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
     ],
-    "cwd": "[START_DIR]/project",
-    "name": "get roll direction.is backward",
-    "timeout": 600.0,
+    "infra_step": true,
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure package directory",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "remote"
-  },
-  {
-    "cmd": [
-      "git",
-      "remote"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "remote.name",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@2@@@"
     ]
   },
   {
     "cmd": [
-      "git",
-      "remote",
-      "get-url",
-      "origin"
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gitiles/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
     ],
-    "cwd": "[START_DIR]/project",
-    "name": "remote.url",
-    "timeout": 600.0,
+    "infra_step": true,
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure_installed",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gitiles/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
     ]
   },
   {
-    "cmd": [],
-    "name": "pigweed"
-  },
-  {
     "cmd": [
-      "git",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gitiles",
       "log",
-      "--pretty=format:%H\n%an\n%ae\n%B",
-      "-z",
-      "1111111111111111111111111111111111111111..2222222222222222222222222222222222222222"
+      "-json-output",
+      "/path/to/tmp/json",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "1111111111111111111111111111111111111111..h3ll0"
     ],
-    "cwd": "[START_DIR]/project",
-    "name": "pigweed.git log",
-    "timeout": 600.0,
+    "infra_step": true,
+    "name": "log pigweed",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 0\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"83a7614b3b60951511be50db1b9561daff4bb447\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"1b6412b24ec3add84836c8fdd1af5ac8e35b61d9\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"8bea05ad53680fce6937543f0d98cd48e295b8ff\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"a.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"8675a52c73c701cb0b2c48f5ed4a9058c624e6cd\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"a1b1e6aa501915989b45a95e1224ec2a88655eb3\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"06bc4c79002f278528aaddae4e056a11f58c19ad\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"b.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"363caa907186de786cb5292cd1ab7245da954815\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 2\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"255c6325c4c654e17e6b35142e3912c86f1718f2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"e84d4ad259e69da73d2b842e2b9709f08e8b22bd\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"d7f478bf423219f2f47c1a6ed344fc597a8bf18f\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"c.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers"
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
   },
   {
     "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.get packages",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@"
     ]
@@ -882,9 +411,8 @@
       "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
       "/path/to/tmp/"
     ],
-    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@3@@@",
       "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
@@ -893,7 +421,7 @@
   },
   {
     "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@2@@@"
     ]
@@ -910,9 +438,8 @@
       "0o777",
       "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
     ],
-    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@3@@@"
     ]
@@ -930,9 +457,8 @@
       "-json-output",
       "/path/to/tmp/json"
     ],
-    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@3@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
@@ -951,32 +477,28 @@
   {
     "cmd": [
       "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
-      "change-query",
+      "change-detail",
       "-host",
       "https://pigweed-review.googlesource.com",
       "-input",
-      "{\"params\": {\"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}",
+      "{\"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"}",
       "-output",
       "/path/to/tmp/json"
     ],
-    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "pigweed.get change-id",
+    "name": "get gerrit change numbers.change details for 3e30158",
     "timeout": 600,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"_number\": 12345@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
       "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
-      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"@@@",
-      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"@@@",
       "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@"
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/3e30158f2a7caccb7a9f6632a60011e7a44e1e5c@@@"
     ]
   },
   {
@@ -986,44 +508,97 @@
       "-host",
       "https://pigweed-review.googlesource.com",
       "-input",
-      "{\"change_id\": \"12345\"}",
+      "{\"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"}",
       "-output",
       "/path/to/tmp/json"
     ],
-    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "pigweed.get 12345",
+    "name": "get gerrit change numbers.change details for 3380b83",
     "timeout": 600,
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"owner\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"email\": \"author@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"name\": \"author\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  },@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"reviewers\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"REVIEWER\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"reviewer@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"reviewer\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"nobody@google.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"nobody\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"robot@gserviceaccount.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"robot\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
       "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"12345\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"@@@",
       "@@@STEP_LOG_LINE@json.input@}@@@",
       "@@@STEP_LOG_END@json.input@@@",
-      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/12345@@@"
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/3380b83c11e029b7291c83c44e7b1ce09d465fd1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "get gerrit change numbers.change details for 363caa9",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/363caa907186de786cb5292cd1ab7245da954815@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/0.\n    commit = \"h3ll0\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
+      "[START_DIR]/checkout/WORKSPACE"
+    ],
+    "infra_step": true,
+    "name": "write new WORKSPACE",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Warning: this entry is automatically updated.@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Last updated 2012-05-14.@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: By https://cr-buildbucket.appspot.com/build/0.@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"h3ll0\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_END@WORKSPACE@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "commit message",
+    "~followup_annotations": [
+      "@@@STEP_SUMMARY_TEXT@roll: pigweed 1111111..h3ll0 (3 commits)\n\n3e30158:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 0\n3380b83:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 1\n363caa9:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 2\n\n\nRolled-Repo: https://pigweed.googlesource.com/pigweed/pigweed\nRolled-Commits: 11111111111111..h3ll0@@@"
     ]
   },
   {
diff --git a/recipe_modules/bazel_roll/tests/full.expected/success.json b/recipe_modules/bazel_roll/tests/full.expected/success.json
index 1373798..bb70039 100644
--- a/recipe_modules/bazel_roll/tests/full.expected/success.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/success.json
@@ -1,862 +1,5 @@
 [
   {
-    "cmd": [],
-    "name": "checkout pigweed"
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.options with defaults",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@miss@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.ensure git cache dir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.write git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "remote.origin.url",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.remote set-url",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.cache.remove git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copytree",
-      "--symlinks",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000019.0
-      },
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "add",
-      "origin",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/snapshot"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.submodule-status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "submodule status filler text",
-      "[START_DIR]/snapshot/submodules.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.write submodule snapshot",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
-      "@@@STEP_LOG_END@submodules.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[START_DIR]/snapshot/git.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
     "cmd": [
       "vpython3",
       "-u",
@@ -1050,6 +193,486 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}"
+  },
+  {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gitiles]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gitiles/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gitiles/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gitiles/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gitiles",
+      "log",
+      "-json-output",
+      "/path/to/tmp/json",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "1111111111111111111111111111111111111111..2d72510e447ab60a9728aeea2362d8be2cbd7789"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "log pigweed",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 0\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"83a7614b3b60951511be50db1b9561daff4bb447\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"1b6412b24ec3add84836c8fdd1af5ac8e35b61d9\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"8bea05ad53680fce6937543f0d98cd48e295b8ff\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"a.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"8675a52c73c701cb0b2c48f5ed4a9058c624e6cd\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"a1b1e6aa501915989b45a95e1224ec2a88655eb3\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"06bc4c79002f278528aaddae4e056a11f58c19ad\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"b.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"363caa907186de786cb5292cd1ab7245da954815\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 2\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"255c6325c4c654e17e6b35142e3912c86f1718f2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"e84d4ad259e69da73d2b842e2b9709f08e8b22bd\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"d7f478bf423219f2f47c1a6ed344fc597a8bf18f\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"c.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers"
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gerrit/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.change details for 3e30158",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/3e30158f2a7caccb7a9f6632a60011e7a44e1e5c@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.change details for 3380b83",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/3380b83c11e029b7291c83c44e7b1ce09d465fd1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.change details for 363caa9",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/363caa907186de786cb5292cd1ab7245da954815@@@"
+    ]
+  },
+  {
     "cmd": [
       "vpython3",
       "-u",
@@ -1102,377 +725,9 @@
   },
   {
     "cmd": [],
-    "name": "get roll direction",
+    "name": "commit message",
     "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@forward@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "1111111111111111111111111111111111111111",
-      "2d72510e447ab60a9728aeea2362d8be2cbd7789"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "get roll direction.is forward",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "2d72510e447ab60a9728aeea2362d8be2cbd7789",
-      "1111111111111111111111111111111111111111"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "get roll direction.is backward",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "remote"
-  },
-  {
-    "cmd": [
-      "git",
-      "remote"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "remote.name",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "get-url",
-      "origin"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "remote.url",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed"
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--pretty=format:%H\n%an\n%ae\n%B",
-      "-z",
-      "1111111111111111111111111111111111111111..2d72510e447ab60a9728aeea2362d8be2cbd7789"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.git log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
-      "/path/to/tmp/"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@",
-      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
-      "@@@STEP_LOG_END@cipd.ensure@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "cipd",
-      "ensure",
-      "-root",
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
-      "-ensure-file",
-      "infra/tools/luci/gerrit/${platform} version:pinned-version",
-      "-max-threads",
-      "0",
-      "-json-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
-      "change-query",
-      "-host",
-      "https://pigweed-review.googlesource.com",
-      "-input",
-      "{\"params\": {\"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.get change-id",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"_number\": 12345@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
-      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"@@@",
-      "@@@STEP_LOG_LINE@json.input@  }@@@",
-      "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
-      "change-detail",
-      "-host",
-      "https://pigweed-review.googlesource.com",
-      "-input",
-      "{\"change_id\": \"12345\"}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.get 12345",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"owner\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"email\": \"author@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"name\": \"author\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  },@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"reviewers\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"REVIEWER\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"reviewer@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"reviewer\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"nobody@google.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"nobody\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"robot@gserviceaccount.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"robot\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"12345\"@@@",
-      "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@",
-      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/12345@@@"
+      "@@@STEP_SUMMARY_TEXT@roll: pigweed 1111111..2d72510 (3 commits)\n\n3e30158:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 0\n3380b83:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 1\n363caa9:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 2\n\n\nRolled-Repo: https://pigweed.googlesource.com/pigweed/pigweed\nRolled-Commits: 11111111111111..2d72510e447ab6@@@"
     ]
   },
   {
diff --git a/recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json b/recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json
index 150bfc1..83c5a57 100644
--- a/recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json
@@ -1,862 +1,5 @@
 [
   {
-    "cmd": [],
-    "name": "checkout unrecognized-remote"
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/unrecognized-remote\"\nbranch: \"main\"\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.options with defaults",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/unrecognized-remote\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@miss@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.ensure git cache dir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.write git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "remote.origin.url",
-      "https://pigweed.googlesource.com/unrecognized-remote"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.remote set-url",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.cache.remove git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copytree",
-      "--symlinks",
-      "[CACHE]/git/pigweed.googlesource.com-unrecognized--remote",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.git checkout.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000019.0
-      },
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "add",
-      "origin",
-      "https://pigweed.googlesource.com/unrecognized-remote"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout unrecognized-remote.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/snapshot"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.submodule-status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "submodule status filler text",
-      "[START_DIR]/snapshot/submodules.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.write submodule snapshot",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
-      "@@@STEP_LOG_END@submodules.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[START_DIR]/snapshot/git.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout unrecognized-remote.write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
     "cmd": [
       "vpython3",
       "-u",
diff --git a/recipe_modules/bazel_roll/tests/full.py b/recipe_modules/bazel_roll/tests/full.py
index a31a8a3..d01dbb8 100644
--- a/recipe_modules/bazel_roll/tests/full.py
+++ b/recipe_modules/bazel_roll/tests/full.py
@@ -18,6 +18,8 @@
 from recipe_engine import post_process
 
 DEPS = [
+    "fuchsia/git_roll_util",
+    "fuchsia/gitiles",
     "pigweed/bazel_roll",
     "pigweed/checkout",
     "pigweed/roll_util",
@@ -41,9 +43,12 @@
         git_repository=props.git_repositories[0],
     )
 
-    if not rolls:
-        with api.step.nest('nothing to roll, exiting'):
-            return
+    pres = api.step.empty('commit message').presentation
+    pres.step_summary_text = api.git_roll_util.format_commit_message(
+        *rolls,
+        roll_prefix='roll:',
+        send_comment=True,
+    )
 
 
 def GenTests(api) -> Generator[recipe_test_api.TestData, None, None]:
@@ -61,20 +66,12 @@
         props.git_repositories.append(repo)
         return api.properties(props)
 
-    def commit_data(name, **kwargs):
-        return api.roll_util.commit_data(
-            name,
-            api.roll_util.commit('a' * 40, 'foo\nbar\n\nChange-Id: I1111'),
-            **kwargs,
-        )
-
     yield api.test(
         'success',
         properties(api.bazel_roll.git_repo()),
         api.roll_util.properties(commit_divider='--divider--'),
         trigger('pigweed/pigweed'),
-        api.roll_util.forward_roll(),
-        commit_data('pigweed', prefix=''),
+        api.gitiles.log("log pigweed", "A"),
     )
 
     yield api.test(
@@ -102,7 +99,6 @@
         'name-not-found',
         properties(api.bazel_roll.git_repo(remote=_url('third/repo'))),
         trigger('third/repo'),
-        api.roll_util.forward_roll(),
         api.post_process(post_process.MustRunRE, r'could not find name.*'),
         api.post_process(post_process.DropExpectation),
         status='FAILURE',
@@ -111,12 +107,11 @@
     yield api.test(
         'no-trigger',
         properties(api.bazel_roll.git_repo()),
-        api.roll_util.forward_roll(),
-        commit_data('pigweed', prefix=''),
+        api.gitiles.log("log pigweed", "A"),
     )
 
     yield api.test(
         'backwards',
         properties(api.bazel_roll.git_repo()),
-        api.roll_util.backward_roll(),
+        api.gitiles.log("log pigweed", "A", n=0),
     )
diff --git a/recipes/bazel_roller.expected/success.json b/recipes/bazel_roller.expected/success.json
index 526a6c9..4740cd5 100644
--- a/recipes/bazel_roller.expected/success.json
+++ b/recipes/bazel_roller.expected/success.json
@@ -950,833 +950,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "checkout pigweed (2)"
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).options with defaults",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@miss@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.write git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "remote.origin.url",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.remote set-url",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).cache.remove git cache guard file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copytree",
-      "--symlinks",
-      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git checkout.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000028.0
-      },
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.git init",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "add",
-      "origin",
-      "https://pigweed.googlesource.com/pigweed/pigweed"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/snapshot"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).submodule-status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "submodule status filler text",
-      "[START_DIR]/snapshot/submodules.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).write submodule snapshot",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
-      "@@@STEP_LOG_END@submodules.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "",
-      "[START_DIR]/snapshot/git.log"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed (2).write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
     "cmd": [
       "vpython3",
       "-u",
@@ -1970,6 +1143,486 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}"
+  },
+  {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gitiles]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure infra/tools/luci/gitiles/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gitiles/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gitiles/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gitiles/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gitiles",
+      "log",
+      "-json-output",
+      "/path/to/tmp/json",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "1111111111111111111111111111111111111111..2d72510e447ab60a9728aeea2362d8be2cbd7789"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "log pigweed",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 0\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"83a7614b3b60951511be50db1b9561daff4bb447\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"1b6412b24ec3add84836c8fdd1af5ac8e35b61d9\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"8bea05ad53680fce6937543f0d98cd48e295b8ff\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"a.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"8675a52c73c701cb0b2c48f5ed4a9058c624e6cd\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"a1b1e6aa501915989b45a95e1224ec2a88655eb3\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"06bc4c79002f278528aaddae4e056a11f58c19ad\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"b.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"363caa907186de786cb5292cd1ab7245da954815\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 2\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"255c6325c4c654e17e6b35142e3912c86f1718f2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"e84d4ad259e69da73d2b842e2b9709f08e8b22bd\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"d7f478bf423219f2f47c1a6ed344fc597a8bf18f\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"c.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers"
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gerrit/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.change details for 3e30158",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/3e30158f2a7caccb7a9f6632a60011e7a44e1e5c@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.change details for 3380b83",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/3380b83c11e029b7291c83c44e7b1ce09d465fd1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get gerrit change numbers.change details for 363caa9",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/363caa907186de786cb5292cd1ab7245da954815@@@"
+    ]
+  },
+  {
     "cmd": [
       "vpython3",
       "-u",
@@ -2021,411 +1674,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "get roll direction",
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@forward@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "1111111111111111111111111111111111111111",
-      "2d72510e447ab60a9728aeea2362d8be2cbd7789"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "get roll direction.is forward",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "2d72510e447ab60a9728aeea2362d8be2cbd7789",
-      "1111111111111111111111111111111111111111"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "get roll direction.is backward",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "remote"
-  },
-  {
-    "cmd": [
-      "git",
-      "remote"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "remote.name",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "get-url",
-      "origin"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "remote.url",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed"
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--pretty=format:%H\n%an\n%ae\n%B",
-      "-z",
-      "1111111111111111111111111111111111111111..2d72510e447ab60a9728aeea2362d8be2cbd7789"
-    ],
-    "cwd": "[START_DIR]/project",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.git log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
-      "/path/to/tmp/"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@",
-      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
-      "@@@STEP_LOG_END@cipd.ensure@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0o777",
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "cipd",
-      "ensure",
-      "-root",
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
-      "-ensure-file",
-      "infra/tools/luci/gerrit/${platform} version:pinned-version",
-      "-max-threads",
-      "0",
-      "-json-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
-      "change-query",
-      "-host",
-      "https://pigweed-review.googlesource.com",
-      "-input",
-      "{\"params\": {\"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.get change-id",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"_number\": 12345@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@]@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
-      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"@@@",
-      "@@@STEP_LOG_LINE@json.input@  }@@@",
-      "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
-      "change-detail",
-      "-host",
-      "https://pigweed-review.googlesource.com",
-      "-input",
-      "{\"change_id\": \"12345\"}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "pigweed.get 12345",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"owner\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"email\": \"author@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"name\": \"author\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  },@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"reviewers\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"REVIEWER\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"reviewer@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"reviewer\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"nobody@google.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"nobody\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"robot@gserviceaccount.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"robot\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    ]@@@",
-      "@@@STEP_LOG_LINE@json.output@  }@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"12345\"@@@",
-      "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@",
-      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/12345@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "roll message"
-  },
-  {
-    "cmd": [],
-    "name": "roll message.message for pigweed",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@template@roll: {project_name}: {sanitized_message}@@@",
-      "@@@STEP_LOG_LINE@template@@@@",
-      "@@@STEP_LOG_LINE@template@{remote}@@@",
-      "@@@STEP_LOG_LINE@template@{project_name} Rolled-Commits: {old_revision:.15}..{new_revision:.15}@@@",
-      "@@@STEP_LOG_END@template@@@",
-      "@@@STEP_LOG_LINE@kwargs@'new_revision': '2d72510e447ab60a9728aeea2362d8be2cbd7789'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'old_revision': '1111111111111111111111111111111111111111'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'original_message': 'foo\\nbar\\n\\nChange-Id: I1111'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'project_name': 'pigweed'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'remote': 'https://pigweed.googlesource.com/pigweed/pigweed'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'sanitized_message': 'foo\\nbar\\n'@@@",
-      "@@@STEP_LOG_END@kwargs@@@",
-      "@@@STEP_LOG_LINE@message@roll: pigweed: foo@@@",
-      "@@@STEP_LOG_LINE@message@bar@@@",
-      "@@@STEP_LOG_LINE@message@@@@",
-      "@@@STEP_LOG_LINE@message@@@@",
-      "@@@STEP_LOG_LINE@message@https://pigweed.googlesource.com/pigweed/pigweed@@@",
-      "@@@STEP_LOG_LINE@message@pigweed Rolled-Commits: 111111111111111..2d72510e447ab60@@@",
-      "@@@STEP_LOG_END@message@@@"
-    ]
-  },
-  {
     "cmd": [
       "git",
       "ls-files",
@@ -2622,11 +1870,9 @@
       "git",
       "commit",
       "-m",
-      "roll: pigweed: foo\nbar\n\n\nhttps://pigweed.googlesource.com/pigweed/pigweed\npigweed Rolled-Commits: 111111111111111..2d72510e447ab60\n--divider--\nRoller-URL: https://ci.chromium.org/b/8945511751514863184\nCQ-Do-Not-Cancel-Tryjobs: true\nChange-Id: I520324b8ea4bfe51bb4d3690983686816663e896",
+      "roll: pigweed 1111111..2d72510 (3 commits)\n\n3e30158:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 0\n3380b83:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 1\n363caa9:https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/12345 fake A msg 2\n\n\nRolled-Repo: https://pigweed.googlesource.com/pigweed/pigweed\nRolled-Commits: 11111111111111..2d72510e447ab6\nRoller-URL: https://ci.chromium.org/b/8945511751514863184\nCQ-Do-Not-Cancel-Tryjobs: true\nChange-Id: I520324b8ea4bfe51bb4d3690983686816663e896",
       "-a",
-      "--no-verify",
-      "--author",
-      "author <author@pigweed.infra.roller.example.com>"
+      "--no-verify"
     ],
     "cwd": "[START_DIR]/co",
     "infra_step": true,
diff --git a/recipes/bazel_roller.proto b/recipes/bazel_roller.proto
index 45e1ab2..4ab7b4a 100644
--- a/recipes/bazel_roller.proto
+++ b/recipes/bazel_roller.proto
@@ -33,4 +33,7 @@
   // Forge the author so rolls of single commits are attributed to the original
   // commit author.
   bool forge_author = 8;
+
+  // Line of text to divide the commit header and body from the footers.
+  string commit_divider = 9;
 }
diff --git a/recipes/bazel_roller.py b/recipes/bazel_roller.py
index 0ae0ea6..ccef042 100644
--- a/recipes/bazel_roller.py
+++ b/recipes/bazel_roller.py
@@ -50,9 +50,10 @@
 
 DEPS = [
     'fuchsia/auto_roller',
+    'fuchsia/git_roll_util',
+    'fuchsia/gitiles',
     'pigweed/bazel_roll',
     'pigweed/checkout',
-    'pigweed/roll_util',
     'recipe_engine/properties',
 ]
 
@@ -80,18 +81,19 @@
     if not rolls:
         return  # pragma: no cover
 
-    authors = api.roll_util.authors(*rolls)
-
-    author_override: Optional[api.roll_util.Account] = None
-    if len(authors) == 1 and props.forge_author:
-        author_override = attrs.asdict(
-            api.roll_util.fake_author(next(iter(authors)))
-        )
+    author_override: api.git_roll_util.Author | None = None
+    if props.forge_author:
+        author_override = api.git_roll_util.get_author_override(*rolls)
 
     change: api.auto_roller.GerritChange = api.auto_roller.attempt_roll(
         props.auto_roller_options,
         repo_dir=checkout.root,
-        commit_message=api.roll_util.message(*rolls),
+        commit_message=api.git_roll_util.format_commit_message(
+            *rolls,
+            roll_prefix='roll:',
+            send_comment=True,
+            commit_divider=props.commit_divider,
+        ),
         author_override=author_override,
     )
 
@@ -118,19 +120,10 @@
         )
         return api.properties(props)
 
-    def commit_data(name, **kwargs):
-        return api.roll_util.commit_data(
-            name,
-            api.roll_util.commit('a' * 40, 'foo\nbar\n\nChange-Id: I1111'),
-            **kwargs,
-        )
-
     yield api.test(
         'success',
         properties(GitRepository(remote=_url('pigweed/pigweed'))),
-        api.roll_util.properties(commit_divider='--divider--'),
         trigger('pigweed/pigweed'),
-        api.roll_util.forward_roll(),
-        commit_data('pigweed', prefix=''),
+        api.gitiles.log("log pigweed", "A"),
         api.auto_roller.success(),
     )