Support patching of git repositories as well

By simply sharing the utility function. In this way, we get
feature parity between git_repository and http_archive.

Fixes #4676.

Change-Id: I99b300e42b2f267d8d04fd965f09c24f3ae54f10
PiperOrigin-RevId: 187450644
diff --git a/src/test/shell/bazel/external_patching_test.sh b/src/test/shell/bazel/external_patching_test.sh
index fe5d4f2..820b859 100755
--- a/src/test/shell/bazel/external_patching_test.sh
+++ b/src/test/shell/bazel/external_patching_test.sh
@@ -110,6 +110,92 @@
   grep -q 'env' $foopath || fail "expected unpatched file"
 }
 
+test_patch_git() {
+  EXTREPODIR=`pwd`
+  export GIT_CONFIG_NOSYSTEM=YES
+
+  mkdir extgit
+  (cd extgit && git init \
+       && git config user.email 'me@example.com' \
+       && git config user.name 'E X Ample' )
+  cat > extgit/foo.sh <<'EOF'
+#!/usr/bin/env sh
+
+echo Here be dragons...
+EOF
+  (cd extgit
+   git add .
+   git commit --author="A U Thor <author@example.com>" -m 'initial commit'
+   git tag mytag)
+
+  # Test that the patches attribute of git_repository is honored
+  mkdir main
+  cd main
+  cat > patch_foo.sh <<'EOF'
+--- foo.sh.orig	2018-01-15 10:39:20.183909147 +0100
++++ foo.sh	2018-01-15 10:43:35.331566052 +0100
+@@ -1,3 +1,3 @@
+ #!/usr/bin/env sh
+
+-echo Here be dragons...
++echo There are dragons...
+EOF
+  cat > WORKSPACE <<EOF
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
+new_git_repository(
+  name="ext",
+  remote="file://${EXTREPODIR}/extgit/.git",
+  tag="mytag",
+  build_file_content="exports_files([\"foo.sh\"])",
+  patches = ["//:patch_foo.sh"],
+  patch_cmds = ["find . -name '*.sh' -exec sed -i.orig '1s|#!/usr/bin/env sh\$|/bin/sh\$|' {} +"],
+)
+EOF
+  cat > BUILD <<'EOF'
+genrule(
+  name = "foo",
+  outs = ["foo.sh"],
+  srcs = ["@ext//:foo.sh"],
+  cmd = "cp $< $@; chmod u+x $@",
+  executable = True,
+)
+EOF
+  bazel build :foo.sh
+  foopath=`bazel info bazel-genfiles`/foo.sh
+  grep -q 'There are' $foopath || fail "expected patch to be applied"
+  grep env $foopath && fail "expected patch commands to be executed" || :
+
+  # Verify that changes to the patch files trigger enough rebuilding
+  cat > patch_foo.sh <<'EOF'
+--- foo.sh.orig	2018-01-15 10:39:20.183909147 +0100
++++ foo.sh	2018-01-15 10:43:35.331566052 +0100
+@@ -1,3 +1,3 @@
+ #!/usr/bin/env sh
+
+-echo Here be dragons...
++echo completely differently patched
+EOF
+  bazel build :foo.sh
+  foopath=`bazel info bazel-genfiles`/foo.sh
+  grep -q 'differently patched' $foopath \
+      || fail "expected the new patch to be applied"
+
+  # Verify that changes to the patches attribute trigger enough rebuilding
+  cat > WORKSPACE <<EOF
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
+new_git_repository(
+  name="ext",
+  remote="file://${EXTREPODIR}/extgit/.git",
+  tag="mytag",
+  build_file_content="exports_files([\"foo.sh\"])",
+)
+EOF
+  bazel build :foo.sh
+  foopath=`bazel info bazel-genfiles`/foo.sh
+  grep -q 'Here be' $foopath || fail "expected unpatched file"
+  grep -q 'env' $foopath || fail "expected unpatched file"
+}
+
 test_override_buildfile() {
   ## Verify that the BUILD file of an external repository can be overriden
   ## via the http_archive rule.
diff --git a/tools/build_defs/repo/git.bzl b/tools/build_defs/repo/git.bzl
index f146ef6..d9cf660 100644
--- a/tools/build_defs/repo/git.bzl
+++ b/tools/build_defs/repo/git.bzl
@@ -13,7 +13,8 @@
 # limitations under the License.
 """Rules for cloning external git repositories."""
 
-load("@bazel_tools//tools/build_defs/repo:utils.bzl", "workspace_and_buildfile")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "workspace_and_buildfile", "patch")
+
 
 def _clone_or_update(ctx):
   if ((not ctx.attr.tag and not ctx.attr.commit) or
@@ -86,9 +87,11 @@
     fail('Exactly one of build_file and build_file_content must be provided.')
   _clone_or_update(ctx)
   workspace_and_buildfile(ctx)
+  patch(ctx)
 
 def _git_repository_implementation(ctx):
   _clone_or_update(ctx)
+  patch(ctx)
 
 
 _common_attrs = {
@@ -98,7 +101,10 @@
     'tag': attr.string(default=''),
     'init_submodules': attr.bool(default=False),
     'verbose': attr.bool(default=False),
-    'strip_prefix': attr.string(default='')
+    'strip_prefix': attr.string(default=''),
+    'patches': attr.label_list(default=[]),
+    'patch_tool': attr.string(default="patch"),
+    'patch_cmds': attr.string_list(default=[]),
 }
 
 
@@ -144,6 +150,11 @@
   remote: The URI of the remote Git repository.
 
   strip_prefix: A directory prefix to strip from the extracted files.
+
+  patches: A list of files that are to be applied as patches after extracting
+    the archive.
+  patch_tool: the patch(1) utility to use.
+  patch_cmds: sequence of commands to be applied after patches are applied.
 """
 
 git_repository = repository_rule(
@@ -174,4 +185,9 @@
     wall-clock time.
 
   strip_prefix: A directory prefix to strip from the extracted files.
+
+  patches: A list of files that are to be applied as patches after extracting
+    the archive.
+  patch_tool: the patch(1) utility to use.
+  patch_cmds: sequence of commands to be applied after patches are applied.
 """
diff --git a/tools/build_defs/repo/http.bzl b/tools/build_defs/repo/http.bzl
index 2247708..e396914 100644
--- a/tools/build_defs/repo/http.bzl
+++ b/tools/build_defs/repo/http.bzl
@@ -29,23 +29,7 @@
 replace the native rules.
 """
 
-load("@bazel_tools//tools/build_defs/repo:utils.bzl", "workspace_and_buildfile")
-
-def _patch(ctx):
-  """Implementation of patching an already extracted repository"""
-  bash_exe = ctx.os.environ["BAZEL_SH"] if "BAZEL_SH" in ctx.os.environ else "bash"
-  for patchfile in ctx.attr.patches:
-    command = "{patchtool} -p0 < {patchfile}".format(
-      patchtool=ctx.attr.patch_tool,
-      patchfile=ctx.path(patchfile))
-    st = ctx.execute([bash_exe, "-c", command])
-    if st.return_code:
-      fail("Error applying patch %s:\n%s" % (str(patchfile), st.stderr))
-  for cmd in ctx.attr.patch_cmds:
-    st = ctx.execute([bash_exe, "-c", cmd])
-    if st.return_code:
-      fail("Error applying patch command %s:\n%s%s"
-           % (cmd, st.stdout, st.stderr))
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "workspace_and_buildfile", "patch")
 
 def _http_archive_impl(ctx):
   """Implementation of the http_archive rule."""
@@ -74,7 +58,7 @@
 
   ctx.download_and_extract(all_urls, "", ctx.attr.sha256, ctx.attr.type,
                            ctx.attr.strip_prefix)
-  _patch(ctx)
+  patch(ctx)
   workspace_and_buildfile(ctx)
 
 _HTTP_FILE_BUILD = """
diff --git a/tools/build_defs/repo/utils.bzl b/tools/build_defs/repo/utils.bzl
index 5f72f64..b326a82 100644
--- a/tools/build_defs/repo/utils.bzl
+++ b/tools/build_defs/repo/utils.bzl
@@ -50,4 +50,20 @@
     ctx.execute([bash_exe, "-c", "rm -f BUILD.bazel"])
     ctx.file("BUILD", ctx.attr.build_file_content)
 
+def patch(ctx):
+  """Implementation of patching an already extracted repository"""
+  bash_exe = ctx.os.environ["BAZEL_SH"] if "BAZEL_SH" in ctx.os.environ else "bash"
+  for patchfile in ctx.attr.patches:
+    command = "{patchtool} -p0 < {patchfile}".format(
+      patchtool=ctx.attr.patch_tool,
+      patchfile=ctx.path(patchfile))
+    st = ctx.execute([bash_exe, "-c", command])
+    if st.return_code:
+      fail("Error applying patch %s:\n%s" % (str(patchfile), st.stderr))
+  for cmd in ctx.attr.patch_cmds:
+    st = ctx.execute([bash_exe, "-c", cmd])
+    if st.return_code:
+      fail("Error applying patch command %s:\n%s%s"
+           % (cmd, st.stdout, st.stderr))
+