roll_util: Update to support rolls of rebases
Update roll_util, submodule_roller, and repo_roller to work when the
pinned repository has been rebased. This happens occasionally with
dogfood branches on some projects. In those cases we can't find the
commits between the previous pinned version and the version we're
updating to, so we just grab the top five commits on the branch and list
them.
Change-Id: Ib237109acccb88f823a15602d88cd0a622a8ff1d
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/25025
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Oliver Newman <olivernewman@google.com>
diff --git a/recipe_modules/roll_util/api.py b/recipe_modules/roll_util/api.py
index 27d10b7..3ece600 100644
--- a/recipe_modules/roll_util/api.py
+++ b/recipe_modules/roll_util/api.py
@@ -17,6 +17,7 @@
import re
import attr
+import enum
from recipe_engine import recipe_api
# If we're embedding the original commit message, prepend 'Original-' to lines
@@ -59,6 +60,13 @@
)
+class _Direction(enum.Enum):
+ CURRENT = 'CURRENT'
+ FORWARD = 'FORWARD'
+ BACKWARD = 'BACKWARD'
+ REBASE = 'REBASE'
+
+
@attr.s
class Commit(object):
hash = attr.ib()
@@ -72,8 +80,16 @@
old_revision = attr.ib(type=str)
new_revision = attr.ib(type=str)
proj_dir = attr.ib(type=str)
+ direction = attr.ib(type=str)
_commit_data = attr.ib(default=None)
+ @direction.validator
+ def check(self, _, value): # pragma: no cover
+ if value not in _Direction:
+ raise ValueError('invalid direction: {}'.format(value))
+ if value == _Direction.CURRENT:
+ raise ValueError('attempt to do a no-op roll')
+
@property
def commits(self):
if self._commit_data:
@@ -82,7 +98,10 @@
with self._api.context(cwd=self.proj_dir):
with self._api.step.nest(self.project_name):
commits = []
- if _is_hash(self.old_revision):
+ if (
+ _is_hash(self.old_revision) and
+ self.direction == _Direction.FORWARD
+ ):
base = self.old_revision
else:
base = '{}~5'.format(self.new_revision)
@@ -216,7 +235,6 @@
return self._multiple_commits_roll_message(roll)
return self._single_commit_roll_message(roll)
-
def _multiple_rolls_message(self, *rolls):
rolls = sorted(rolls, key=lambda x: x.project_name)
@@ -245,27 +263,61 @@
return self._multiple_rolls_message(*rolls)
return self._single_roll_message(*rolls).render()
- def check_roll_direction(
- self, git_dir, old, new, name='check roll direction'
+ Direction = _Direction
+
+ def get_roll_direction(
+ self, git_dir, old, new, name='get roll direction'
):
- """Return if old is an ancestor of new (i.e., a "forward" roll)."""
+ """Return Direction of roll."""
if old == new:
with self.m.step.nest(name) as pres:
pres.step_summary_text = 'up-to-date'
- return False
+ return self.Direction.CURRENT
with self.m.context(git_dir):
- step = self.m.git(
- name, 'merge-base', '--is-ancestor', old, new, ok_ret=(0, 1),
- )
+ with self.m.step.nest(name) as pres:
+ forward = self.m.git(
+ 'is forward',
+ 'merge-base',
+ '--is-ancestor',
+ old,
+ new,
+ ok_ret=(0, 1),
+ )
- step.presentation.step_summary_text = 'backward'
- if step.exc_result.retcode == 0:
- step.presentation.step_summary_text = 'forward'
+ backward = self.m.git(
+ 'is backward',
+ 'merge-base',
+ '--is-ancestor',
+ new,
+ old,
+ ok_ret=(0, 1),
+ )
- return step.exc_result.retcode == 0
+ if (
+ forward.exc_result.retcode == 0
+ and backward.exc_result.retcode != 0
+ ):
+ pres.step_summary_text = 'forward'
+ return self.Direction.FORWARD
- def backwards_roll_step(self, remote, old_revision, new_revision):
+ if (
+ forward.exc_result.retcode != 0
+ and backward.exc_result.retcode == 0
+ ):
+ pres.step_summary_text = 'backward'
+ return self.Direction.BACKWARD
+
+ # If old is not an ancestor of new and new is not an ancestor
+ # of old then history was rewritten in some manner but we still
+ # need to update the pin.
+ pres.step_summary_text = 'rebase'
+ return self.Direction.REBASE
+
+ def can_roll(self, direction):
+ return direction in (self.Direction.FORWARD, self.Direction.REBASE)
+
+ def skip_roll_step(self, remote, old_revision, new_revision):
with self.m.step.nest('cancelling roll') as pres:
fmt = (
'not updating from {old} to {new} because {old} is newer '