Add the gifn-apply test tool

Add a tool that checks out the repositories mentioned in
a git-if-needed patch series, runs gif-it-needed, and performs
some checks on its operation. This tool will eventually be used in
a Zuul test job for this repository.

Change-Id: Id02fb7c21f5ab34d9639bf845fcc3961d929b13b
diff --git a/tools/git-if-needed/python/gifn_apply/gifn.py b/tools/git-if-needed/python/gifn_apply/gifn.py
new file mode 100644
index 0000000..3e313a8
--- /dev/null
+++ b/tools/git-if-needed/python/gifn_apply/gifn.py
@@ -0,0 +1,26 @@
+# SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
+# SPDX-License-Identifier: BSD-2-Clause
+"""Run git-if-needed."""
+
+from __future__ import annotations
+
+import shlex
+import subprocess
+
+from typing import TYPE_CHECKING
+
+from . import defs
+
+if TYPE_CHECKING:
+    import pathlib
+
+
+def apply_series(cfg: defs.Config, tempd: pathlib.Path) -> None:
+    """Run git-if-needed to apply all the patches in a series file."""
+    cmd: list[str | pathlib.Path] = [cfg.program, "-s", cfg.series, "--", "am"]
+    cmdstr = shlex.join(str(arg) for arg in cmd)
+    cfg.log.debug("Running `%(cmdstr)s`", {"cmdstr": cmdstr})
+    try:
+        subprocess.run(cmd, check=True, cwd=tempd, shell=False)
+    except (OSError, subprocess.CalledProcessError) as err:
+        raise defs.GApplyError(f"Could not run `{cmdstr}` in {tempd}: {err}") from err