Repository: mesonbuild/meson

Issue:
Git wrap errors incorrectly with shallow clones against HEAD
**Describe the bug**

The documentation states that Git wraps can be pointed to `revision = 'head'` to make them fetch the latest tip of the default branch.

https://github.com/mesonbuild/meson/blob/2761131ff94489842da3988792c4c2b0d25a850c/docs/markdown/Wrap-dependency-system-manual.md?plain=1#L112-L123

However, if one combines `revision = 'head'` with `depth = 1`, the following error occurs:

```
warning: Could not find remote branch HEAD to clone.
fatal: Remote branch HEAD not found in upstream origin

..\meson.build:1864:4: ERROR: Git command failed: ['C:\\Program Files\\Git\\cmd\\git.EXE', '-c', 'advice.detachedHead=false', 'clone', '--depth', '1', '--branch', 'HEAD', 'https://github.com/FFmpeg/gas-preprocessor.git', 'win-gas']
```

This bug was introduced by https://github.com/mesonbuild/meson/commit/507cf479411050b76dc8724de1b3b464b8f965e3, where having `depth = 1`  hardcodes the usage of `--branch`, ignoring if `revision` is also `head`:

https://github.com/mesonbuild/meson/blob/9a57608cac4a422ffa450bcf75c0609639035e11/mesonbuild/wrap/wrap.py#L578-L586

**To Reproduce**

meson.build:
```meson
  project('test', 'c')
  gas = find_program('gas-preprocessor.pl', required: false)

  if not gas.found()
    subproject('win-gas')
  endif
```

subprojects/win-gas.wrap:
```ini
[wrap-git]
url = https://github.com/FFmpeg/gas-preprocessor.git
revision = head
depth = 1

[provide]
program_names = gas-preprocessor.pl
```

**Expected behavior**
Git clone must succeed, and the resulting clone must be shallow and point to the tip of the `master` branch.

**system parameters**
* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)? **No**
* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.) **Windows 10 21H2**
* what Python version are you using e.g. 3.8.0 **3.10.7**
* what `meson --version` **0.63.2**
* what `ninja --version` if it's a Ninja build **1.10.2**

Git wrap errors incorrectly with shallow clones against HEAD
**Describe the bug**

The documentation states that Git wraps can be pointed to `revision = 'head'` to make them fetch the latest tip of the default branch.

https://github.com/mesonbuild/meson/blob/2761131ff94489842da3988792c4c2b0d25a850c/docs/markdown/Wrap-dependency-system-manual.md?plain=1#L112-L123

However, if one combines `revision = 'head'` with `depth = 1`, the following error occurs:

```
warning: Could not find remote branch HEAD to clone.
fatal: Remote branch HEAD not found in upstream origin

..\meson.build:1864:4: ERROR: Git command failed: ['C:\\Program Files\\Git\\cmd\\git.EXE', '-c', 'advice.detachedHead=false', 'clone', '--depth', '1', '--branch', 'HEAD', 'https://github.com/FFmpeg/gas-preprocessor.git', 'win-gas']
```

This bug was introduced by https://github.com/mesonbuild/meson/commit/507cf479411050b76dc8724de1b3b464b8f965e3, where having `depth = 1`  hardcodes the usage of `--branch`, ignoring if `revision` is also `head`:

https://github.com/mesonbuild/meson/blob/9a57608cac4a422ffa450bcf75c0609639035e11/mesonbuild/wrap/wrap.py#L578-L586

**To Reproduce**

meson.build:
```meson
  project('test', 'c')
  gas = find_program('gas-preprocessor.pl', required: false)

  if not gas.found()
    subproject('win-gas')
  endif
```

subprojects/win-gas.wrap:
```ini
[wrap-git]
url = https://github.com/FFmpeg/gas-preprocessor.git
revision = head
depth = 1

[provide]
program_names = gas-preprocessor.pl
```

**Expected behavior**
Git clone must succeed, and the resulting clone must be shallow and point to the tip of the `master` branch.

**system parameters**
* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)? **No**
* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.) **Windows 10 21H2**
* what Python version are you using e.g. 3.8.0 **3.10.7**
* what `meson --version` **0.63.2**
* what `ninja --version` if it's a Ninja build **1.10.2**



Discussion:

Resolving patch:
diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py
--- a/mesonbuild/utils/universal.py
+++ b/mesonbuild/utils/universal.py
@@ -1446,7 +1446,7 @@ def Popen_safe_legacy(args: T.List[str], write: T.Optional[str] = None,
         input_ = write.encode('utf-8')
     o, e = p.communicate(input_)
     if o is not None:
-        if sys.stdout.encoding:
+        if sys.stdout.encoding is not None:
             o = o.decode(encoding=sys.stdout.encoding, errors='replace').replace('\r\n', '\n')
         else:
             o = o.decode(errors='replace').replace('\r\n', '\n')
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -582,8 +582,11 @@ def get_git(self) -> None:
                         verbose_git(['fetch', self.wrap.get('url'), revno], self.dirname, check=True)
                         verbose_git(checkout_cmd, self.dirname, check=True)
             else:
-                verbose_git(['-c', 'advice.detachedHead=false', 'clone', *depth_option, '--branch', revno, self.wrap.get('url'),
-                             self.directory], self.subdir_root, check=True)
+                args = ['-c', 'advice.detachedHead=false', 'clone', *depth_option]
+                if revno.lower() != 'head':
+                    args += ['--branch', revno]
+                args += [self.wrap.get('url'), self.directory]
+                verbose_git(args, self.subdir_root, check=True)
             if self.wrap.values.get('clone-recursive', '').lower() == 'true':
                 verbose_git(['submodule', 'update', '--init', '--checkout', '--recursive', *depth_option],
                             self.dirname, check=True)
diff --git a/unittests/subprojectscommandtests.py b/unittests/subprojectscommandtests.py
--- a/unittests/subprojectscommandtests.py
+++ b/unittests/subprojectscommandtests.py
@@ -103,15 +103,20 @@ def _git_create_remote_tag(self, name, tag):
         self._git_remote(['commit', '--no-gpg-sign', '--allow-empty', '-m', f'tag {tag} commit'], name)
         self._git_remote(['tag', '--no-sign', tag], name)
 
-    def _wrap_create_git(self, name, revision='master'):
+    def _wrap_create_git(self, name, revision='master', depth=None):
         path = self.root_dir / name
         with open(str((self.subprojects_dir / name).with_suffix('.wrap')), 'w', encoding='utf-8') as f:
+            if depth is None:
+                depth_line = ''
+            else:
+                depth_line = 'depth = {}'.format(depth)
             f.write(textwrap.dedent(
                 '''
                 [wrap-git]
                 url={}
                 revision={}
-                '''.format(os.path.abspath(str(path)), revision)))
+                {}
+                '''.format(os.path.abspath(str(path)), revision, depth_line)))
 
     def _wrap_create_file(self, name, tarball='dummy.tar.gz'):
         path = self.root_dir / tarball
@@ -205,6 +210,15 @@ def test_git_update(self):
         self._subprojects_cmd(['update', '--reset'])
         self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name))
 
+        # Create a fake remote git repository and a wrap file targeting
+        # HEAD and depth = 1. Checks that "meson subprojects download" works.
+        subp_name = 'sub3'
+        self._git_create_remote_repo(subp_name)
+        self._wrap_create_git(subp_name, revision='head', depth='1')
+        self._subprojects_cmd(['download'])
+        self.assertPathExists(str(self.subprojects_dir / subp_name))
+        self._git_config(self.subprojects_dir / subp_name)
+
     @skipIfNoExecutable('true')
     def test_foreach(self):
         self._create_project(self.subprojects_dir / 'sub_file')


Test patch: