Add `strip_prefix` arg to bzlmod's `git_override`

Add `strip_prefix` arg go bzlmod's `git_override` and pass it down to `git_repository`.

This allows users to work with git repositories that have other Bazel modules in subdirectories.
A prime example of such is the `rules_python`](https://github.com/bazelbuild/rules_python) repo:

```
./rules_python
├── MODULE.bazel       # available on BCR as "rules_python"
└── gazelle
    └── MODULE.bazel   # available on BCR as "rules_python_gazelle_plugin"
```

Fixes #22076.

RELNOTES:
bzlmod `git_repository` now accepts the `strip_prefix` arg and passes it to the underlying `git_repository` call.

Closes #22077.

PiperOrigin-RevId: 628059135
Change-Id: I8b9df3a5d41924302475fd9495aff6cd21ce6db2
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GitOverride.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GitOverride.java
index 1c209f1..7256d2c 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GitOverride.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GitOverride.java
@@ -28,9 +28,10 @@
       ImmutableList<Object> patches,
       ImmutableList<String> patchCmds,
       int patchStrip,
-      boolean initSubmodules) {
+      boolean initSubmodules,
+      String stripPrefix) {
     return new AutoValue_GitOverride(
-        remote, commit, patches, patchCmds, patchStrip, initSubmodules);
+        remote, commit, patches, patchCmds, patchStrip, initSubmodules, stripPrefix);
   }
 
   /** The URL pointing to the git repository. */
@@ -51,6 +52,9 @@
   /** Whether submodules in the fetched repo should be recursively initialized. */
   public abstract boolean getInitSubmodules();
 
+  /** The directory prefix to strip from the extracted files. */
+  public abstract String getStripPrefix();
+
   /** Returns the {@link RepoSpec} that defines this repository. */
   @Override
   public RepoSpec getRepoSpec() {
@@ -61,6 +65,7 @@
         .setPatchCmds(getPatchCmds())
         .setPatchArgs(ImmutableList.of("-p" + getPatchStrip()))
         .setInitSubmodules(getInitSubmodules())
+        .setStripPrefix(getStripPrefix())
         .build();
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java
index 1f367f2..ae0eae0 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java
@@ -1015,10 +1015,20 @@
             defaultValue = "0"),
         @Param(
             name = "init_submodules",
-            doc = "Whether submodules in the fetched repo should be recursively initialized.",
+            doc = "Whether git submodules in the fetched repo should be recursively initialized.",
             named = true,
             positional = false,
             defaultValue = "False"),
+        @Param(
+            name = "strip_prefix",
+            doc =
+                "A directory prefix to strip from the extracted files. This can be used to target"
+                    + " a subdirectory of the git repo. Note that the subdirectory must have its"
+                    + " own `MODULE.bazel` file with a module name that is the same as the"
+                    + " `module_name` arg passed to this `git_override`.",
+            named = true,
+            positional = false,
+            defaultValue = "''"),
       },
       useStarlarkThread = true)
   public void gitOverride(
@@ -1029,6 +1039,7 @@
       Iterable<?> patchCmds,
       StarlarkInt patchStrip,
       boolean initSubmodules,
+      String stripPrefix,
       StarlarkThread thread)
       throws EvalException {
     ModuleThreadContext context = ModuleThreadContext.fromOrFail(thread, "git_override()");
@@ -1044,7 +1055,8 @@
                 .collect(toImmutableList()),
             Sequence.cast(patchCmds, String.class, "patchCmds").getImmutableList(),
             patchStrip.toInt("git_override.patch_strip"),
-            initSubmodules));
+            initSubmodules,
+            stripPrefix));
   }
 
   @StarlarkMethod(
diff --git a/src/test/py/bazel/bzlmod/bazel_overrides_test.py b/src/test/py/bazel/bzlmod/bazel_overrides_test.py
index 9236094..45c3f4d 100644
--- a/src/test/py/bazel/bzlmod/bazel_overrides_test.py
+++ b/src/test/py/bazel/bzlmod/bazel_overrides_test.py
@@ -15,6 +15,7 @@
 # pylint: disable=g-long-ternary
 
 import os
+import shutil
 import tempfile
 from absl.testing import absltest
 from src.test.py.bazel import test_base
@@ -36,6 +37,8 @@
         'bbb', '1.1', {'aaa': '1.1'}
     ).createCcModule(
         'ccc', '1.1', {'aaa': '1.1', 'bbb': '1.1'}
+    ).createCcModule(
+        'ddd', '1.0'
     )
     self.ScratchFile(
         '.bazelrc',
@@ -272,6 +275,104 @@
     self.assertIn('main function => bbb@1.1', stdout)
     self.assertIn('bbb@1.1 => aaa@1.0 (locally patched)', stdout)
 
+  def testGitOverrideStripPrefix(self):
+    self.writeMainProjectFiles()
+
+    # Update BUILD and main.cc to also call `ddd`.
+    self.ScratchFile(
+        'BUILD',
+        [
+            'cc_binary(',
+            '  name = "main",',
+            '  srcs = ["main.cc"],',
+            '  deps = [',
+            '    "@aaa//:lib_aaa",',
+            '    "@bbb//:lib_bbb",',
+            '    "@ddd//:lib_ddd",',
+            '  ],',
+            ')',
+        ],
+    )
+    self.ScratchFile(
+        'main.cc',
+        [
+            '#include "aaa.h"',
+            '#include "bbb.h"',
+            '#include "ddd.h"',
+            'int main() {',
+            '    hello_aaa("main function");',
+            '    hello_bbb("main function");',
+            '    hello_ddd("main function");',
+            '}',
+        ],
+    )
+    src_aaa_1_0 = self.main_registry.projects.joinpath('aaa', '1.0')
+    src_ddd_1_0 = self.main_registry.projects.joinpath('ddd', '1.0')
+    self.RunProgram(['git', 'init'], cwd=src_aaa_1_0)
+    self.RunProgram(
+        ['git', 'config', 'user.name', 'tester'],
+        cwd=src_aaa_1_0,
+    )
+    self.RunProgram(
+        ['git', 'config', 'user.email', 'tester@foo.com'],
+        cwd=src_aaa_1_0,
+    )
+
+    # Make a subdirectory that itself is the published module 'ddd'.
+    subdir_name = 'subdir_containing_ddd'
+    shutil.copytree(src=src_ddd_1_0, dst=src_aaa_1_0 / subdir_name)
+
+    # Edit the code in 'subdir_containing_ddd/ddd.cc' so that we can assert
+    # that we're using it.
+    src_aaa_relpath = src_aaa_1_0.relative_to(self._test_cwd)
+    self.ScratchFile(
+        str(src_aaa_relpath / subdir_name / 'ddd.cc'),
+        [
+            '#include <stdio.h>',
+            '#include "ddd.h"',
+            'void hello_ddd(const std::string& caller) {',
+            '    std::string lib_name = "ddd@1.0";',
+            (
+                '    printf("%s => %s from subdir\\n", caller.c_str(),'
+                ' lib_name.c_str());'
+            ),
+            '}',
+        ],
+    )
+
+    self.RunProgram(['git', 'add', './'], cwd=src_aaa_1_0)
+    self.RunProgram(
+        ['git', 'commit', '-m', 'Initial commit.'],
+        cwd=src_aaa_1_0,
+    )
+
+    _, stdout, _ = self.RunProgram(
+        ['git', 'rev-parse', 'HEAD'], cwd=src_aaa_1_0
+    )
+
+    commit = stdout[0].strip()
+
+    self.ScratchFile(
+        'MODULE.bazel',
+        [
+            'bazel_dep(name = "aaa", version = "1.1")',
+            'bazel_dep(name = "bbb", version = "1.1")',
+            'bazel_dep(name = "ddd", version = "1.0")',
+            'git_override(',
+            '  module_name = "ddd",',
+            '  remote = "%s",' % src_aaa_1_0.as_uri(),
+            '  commit = "%s",' % commit,
+            '  strip_prefix = "%s",' % subdir_name,
+            ')',
+        ],
+    )
+
+    _, stdout, _ = self.RunBazel(['run', '//:main'])
+    self.assertIn('main function => aaa@1.1', stdout)
+    self.assertIn('main function => bbb@1.1', stdout)
+    self.assertIn('bbb@1.1 => aaa@1.1', stdout)
+    self.assertIn('main function => ddd@1.0 from subdir', stdout)
+
   def testLocalPathOverride(self):
     src_aaa_1_0 = self.main_registry.projects.joinpath('aaa', '1.0')
     self.writeMainProjectFiles()