diff mbox series

[v2,1/2] devtool: standard: guess srcrev update mode for gitsm:// recipes too

Message ID 20260723233043.7-1-bbnpreetsingh@gmail.com
State New
Headers show
Series [v2,1/2] devtool: standard: guess srcrev update mode for gitsm:// recipes too | expand

Commit Message

Babanpreet Singh July 23, 2026, 11:30 p.m. UTC
'devtool update-recipe' and 'devtool finish' default to guessing the
recipe update mode: 'srcrev' when the source tree HEAD sits on the
upstream branch (the "checked out another upstream revision" workflow),
'patch' otherwise. The guesser matches SRC_URI entries against a
literal 'git://' prefix, so a gitsm:// recipe never has any git URIs
from its point of view and always falls into patch mode.

In that case there are no local commits to export either, so checking
out a different upstream revision and running update-recipe reports
"No patches or files need updating" and silently leaves the recipe's
SRCREV untouched, while the same operation on a git:// recipe updates
SRCREV. Forcing -m srcrev works, since _update_recipe_srcrev() is
scheme-agnostic; only the guess is broken.

Accept gitsm:// URIs the same way e7076f1742 ("devtool: gitsm://
should be handled same as git:// in upgrades") did for the upgrade
path, where the same omission was fixed; the guesser itself dates from
9b9733b7d7 (2015), before gitsm handling was a consideration.

Unlike git://, a gitsm:// tree can carry exportable changes that do
not move the parent HEAD off the upstream branch: local commits in a
submodule only show up as an out-of-sync gitlink in the parent.
Guessing srcrev there would silently drop them, because
_update_recipe_srcrev() only rewrites the parent SRCREV — this is
exactly the scenario test_devtool_git_submodules exercises (commit
inside a submodule, then devtool finish), which caught v1 of this
patch on the autobuilder. So only guess srcrev when every submodule
checkout matches the revision its parent records: any '+' (checkout
differs) or 'U' (merge conflicts) entry in 'git submodule status
--recursive' output keeps the current patch mode. This also covers a
tree where another parent revision was checked out without a
following 'git submodule update': srcrev must never be guessed when
it could lose submodule content, and a fully synced tree is the only
state where it provably cannot.

Note this changes the guessed mode for existing gitsm:// recipes from
'patch' to 'srcrev' when HEAD is on the upstream branch and the
submodules are in sync — the behavior git:// recipes have had since
2015. Trees carrying local commits still guess 'patch', because their
HEAD is not reachable from the upstream branch.

AI-Generated: Uses Claude (claude-sonnet-5)
Signed-off-by: Babanpreet Singh <bbnpreetsingh@gmail.com>
---
v1 -> v2:
- Use the startswith(('git://', 'gitsm://')) tuple form (Richard Purdie)
- Only guess srcrev when 'git submodule status --recursive' reports
  every submodule in sync: local commits in a submodule do not move the
  parent HEAD off the upstream branch, and v1 silently dropped them by
  guessing srcrev — caught by test_devtool_git_submodules on the
  autobuilder (reported by Mathieu Dubois-Briand). Of the three
  selftest failures reported against v1, this one is the only one this
  series causes: test_devtool_modify_nested_gitsm fails during
  'devtool modify' source extraction before the guesser can run, and
  test_devtool_finish_update_patch exercises a recipe with only git://
  URIs, for which this change is inert; both pass on master with only
  this series applied (analysis in the reply on the v1 thread)

 scripts/lib/devtool/standard.py | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py
index b2b27c7ced..9b2643604d 100644
--- a/scripts/lib/devtool/standard.py
+++ b/scripts/lib/devtool/standard.py
@@ -1816,7 +1816,7 @@  def _guess_recipe_update_mode(srctree, rdata):
     """Guess the recipe update mode to use"""
     import bb.process
     src_uri = (rdata.getVar('SRC_URI') or '').split()
-    git_uris = [uri for uri in src_uri if uri.startswith('git://')]
+    git_uris = [uri for uri in src_uri if uri.startswith(('git://', 'gitsm://'))]
     if not git_uris:
         return 'patch'
     # Just use the first URI for now
@@ -1831,7 +1831,14 @@  def _guess_recipe_update_mode(srctree, rdata):
                                cwd=srctree)
     remote_brs = [branch.strip() for branch in stdout.splitlines()]
     if 'origin/' + upstr_branch in remote_brs:
-        return 'srcrev'
+        # Local commits in a submodule need exporting as patches, but do not
+        # move the parent HEAD off the upstream branch, so only guess srcrev
+        # if every submodule checkout matches the revision its parent records
+        # ('+') and none has merge conflicts ('U')
+        stdout, _ = bb.process.run('git submodule status --recursive',
+                                   cwd=srctree)
+        if not any(line.startswith(('+', 'U')) for line in stdout.splitlines()):
+            return 'srcrev'
 
     return 'patch'