Repository: pdm-project/pdm

Issue:
pdm doesn't work well when install git repository
### Describe the bug

pdm doesn't work well when install git repository. related issue: #3107 

### To reproduce

```
pdm init
pdm add ibflex@git+https://github.com/csingley/ibflex.git@af6a0c6 -v
```


### Expected Behavior

pdm should success install the git repository

### Environment Information

```
$ pdm info && pdm info --env
PDM version:
  2.23.0
Python Interpreter:
  /tmp/tmp/.venv/bin/python (3.10)
Project Root:
  /tmp/tmp
Local Packages:
  
{
  "implementation_name": "cpython",
  "implementation_version": "3.10.7",
  "os_name": "posix",
  "platform_machine": "x86_64",
  "platform_release": "6.13.6",
  "platform_system": "Linux",
  "platform_version": "#1 SMP PREEMPT_DYNAMIC 1",
  "python_full_version": "3.10.7",
  "platform_python_implementation": "CPython",
  "python_version": "3.10",
  "sys_platform": "linux"
}
```

### Verbose Command Output

```
$ pdm add ibflex@git+https://github.com/csingley/ibflex.git@af6a0c6 -v

Adding packages to default dependencies: ibflex @ git+https://github.com/csingley/ibflex.git@af6a0c6
pdm.termui: ======== Start resolving requirements ========
pdm.termui:   ibflex @ git+https://github.com/csingley/ibflex.git@af6a0c6
pdm.termui:   Adding requirement python==3.10.*
pdm.termui:   Adding requirement ibflex @ git+https://github.com/csingley/ibflex.git@af6a0c6
pdm.termui: ======== Starting round 0 ========
unearth.vcs.git: Cloning https://github.com/csingley/ibflex.git (revision: af6a0c6) to /tmp/pdm-build-qk6r6_nx
Traceback (most recent call last):
  File "/home/dev_1/.local/bin/pdm", line 8, in <module>
    sys.exit(main())
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/core.py", line 392, in main
    return core.main(args or sys.argv[1:])
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/core.py", line 270, in main
    raise cast(Exception, err).with_traceback(traceback) from None
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/core.py", line 265, in main
    self.handle(project, options)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/core.py", line 195, in handle
    command.handle(project, options)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/cli/commands/add.py", line 71, in handle
    self.do_add(
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/cli/commands/add.py", line 157, in do_add
    resolved = do_lock(
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/cli/actions.py", line 134, in do_lock
    resolved, collected_groups = resolver.resolve()
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/resolver/resolvelib.py", line 51, in resolve
    mapping = self._do_resolve()
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/resolver/resolvelib.py", line 89, in _do_resolve
    result = resolver.resolve(requirements, max_rounds)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/resolvelib/resolvers/resolution.py", line 515, in resolve
    state = resolution.resolve(requirements, max_rounds=max_rounds)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/resolvelib/resolvers/resolution.py", line 440, in resolve
    name = min(narrowed_unstatisfied_names, key=self._get_preference)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/resolvelib/resolvers/resolution.py", line 173, in _get_preference
    return self._p.get_preference(
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/resolver/providers.py", line 115, in get_preference
    deps = self.get_dependencies(candidate)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/resolver/providers.py", line 297, in get_dependencies
    deps, requires_python, _ = self.repository.get_dependencies(candidate)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/repositories/base.py", line 102, in get_dependencies
    requirements, requires_python, summary = getter(candidate)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/repositories/base.py", line 41, in wrapper
    result = func(self, candidate)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/repositories/base.py", line 280, in _get_dependencies_from_metadata
    deps = prepared.get_dependencies_from_metadata()
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/candidates.py", line 620, in get_dependencies_from_metadata
    return filter_requirements_with_extras(self.metadata.requires or [], extras)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/candidates.py", line 607, in metadata
    result = self.prepare_metadata()
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/candidates.py", line 498, in prepare_metadata
    self._unpack(validate_hashes=False)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/pdm/models/candidates.py", line 469, in _unpack
    result = finder.download_and_unpack(
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/unearth/finder.py", line 441, in download_and_unpack
    file = unpack_link(
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/unearth/preparer.py", line 322, in unpack_link
    backend.fetch(link, location)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/unearth/vcs/base.py", line 152, in fetch
    return self.fetch_new(location, url, rev, args)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/unearth/vcs/git.py", line 58, in fetch_new
    self.run_command(["fetch", "-q", url, rev], cwd=location)
  File "/home/dev_1/.local/share/pdm/venv/lib/python3.10/site-packages/unearth/vcs/base.py", line 78, in run_command
    raise UnpackError(e.output) from None
unearth.errors.UnpackError: fatal: couldn't find remote ref af6a0c6

```

### Additional Context

_No response_

### Are you willing to submit a PR to fix this bug?

- [ ] Yes, I would like to submit a PR.


Discussion:

Patch:
diff --git a/news/3455.bugfix.md b/news/3455.bugfix.md
new file mode 100644
index 0000000000..45a11baf72
--- /dev/null
+++ b/news/3455.bugfix.md
@@ -0,0 +1,1 @@
+Update `unearth` to address an issue downloading git repos with short commit hash.
diff --git a/pdm.lock b/pdm.lock
index 7d351005aa..1232849d24 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -5,7 +5,7 @@
 groups = ["default", "all", "cookiecutter", "copier", "doc", "keyring", "pytest", "template", "test", "tox", "workflow"]
 strategy = ["inherit_metadata"]
 lock_version = "4.5.0"
-content_hash = "sha256:c01f9550e0e9f04d9bcd5b910568afbbea1e15ca484f29acd43fa986c43edf0e"
+content_hash = "sha256:5c004ad629e014130dcd0a71ab839c12f525c8b91246ac988b5018b0e88127d7"
 
 [[metadata.targets]]
 requires_python = ">=3.9"
@@ -124,7 +124,7 @@ name = "blinker"
 version = "1.8.2"
 requires_python = ">=3.8"
 summary = "Fast, simple object-to-object and broadcast signaling"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"},
     {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"},
@@ -213,7 +213,7 @@ name = "charset-normalizer"
 version = "3.3.2"
 requires_python = ">=3.7.0"
 summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-groups = ["default", "all", "cookiecutter", "doc", "template", "test"]
+groups = ["default", "all", "cookiecutter", "doc", "template"]
 files = [
     {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
     {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
@@ -491,7 +491,7 @@ name = "dep-logic"
 version = "0.4.11"
 requires_python = ">=3.8"
 summary = "Python dependency specifications supporting logical operations"
-groups = ["default", "test"]
+groups = ["default"]
 dependencies = [
     "packaging>=22",
 ]
@@ -504,7 +504,7 @@ files = [
 name = "distlib"
 version = "0.3.8"
 summary = "Distribution utilities"
-groups = ["default", "test", "tox"]
+groups = ["default", "tox"]
 files = [
     {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
     {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
@@ -565,7 +565,7 @@ name = "filelock"
 version = "3.16.1"
 requires_python = ">=3.8"
 summary = "A platform independent file lock."
-groups = ["default", "test", "tox"]
+groups = ["default", "tox"]
 files = [
     {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
     {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
@@ -576,7 +576,7 @@ name = "findpython"
 version = "0.6.3"
 requires_python = ">=3.8"
 summary = "A utility to find python versions on your system"
-groups = ["default", "test"]
+groups = ["default"]
 dependencies = [
     "packaging>=20",
 ]
@@ -642,7 +642,7 @@ name = "hishel"
 version = "0.0.33"
 requires_python = ">=3.8"
 summary = "Persistent cache implementation for httpx and httpcore"
-groups = ["default", "test"]
+groups = ["default"]
 dependencies = [
     "httpx>=0.22.0",
     "typing-extensions>=4.8.0",
@@ -691,7 +691,7 @@ version = "0.27.2"
 extras = ["socks"]
 requires_python = ">=3.8"
 summary = "The next generation HTTP client."
-groups = ["default", "test"]
+groups = ["default"]
 dependencies = [
     "httpx==0.27.2",
     "socksio==1.*",
@@ -706,7 +706,7 @@ name = "id"
 version = "1.5.0"
 requires_python = ">=3.8"
 summary = "A tool for generating OIDC identities"
-groups = ["default", "test"]
+groups = ["default"]
 dependencies = [
     "requests",
 ]
@@ -731,7 +731,7 @@ name = "importlib-metadata"
 version = "8.5.0"
 requires_python = ">=3.8"
 summary = "Read metadata from Python packages"
-groups = ["default", "all", "doc", "keyring", "test", "workflow"]
+groups = ["default", "all", "doc", "keyring", "workflow"]
 marker = "python_version < \"3.12\""
 dependencies = [
     "typing-extensions>=3.6.4; python_version < \"3.8\"",
@@ -773,7 +773,7 @@ name = "installer"
 version = "0.7.0"
 requires_python = ">=3.7"
 summary = "A library for installing Python wheels."
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"},
     {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"},
@@ -915,7 +915,7 @@ name = "markdown-it-py"
 version = "3.0.0"
 requires_python = ">=3.8"
 summary = "Python port of markdown-it. Markdown parsing, done right!"
-groups = ["default", "all", "cookiecutter", "template", "test"]
+groups = ["default", "all", "cookiecutter", "template"]
 dependencies = [
     "mdurl~=0.1",
 ]
@@ -979,7 +979,7 @@ name = "mdurl"
 version = "0.1.2"
 requires_python = ">=3.7"
 summary = "Markdown URL utilities"
-groups = ["default", "all", "cookiecutter", "template", "test"]
+groups = ["default", "all", "cookiecutter", "template"]
 files = [
     {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
     {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
@@ -1186,7 +1186,7 @@ name = "msgpack"
 version = "1.1.0"
 requires_python = ">=3.8"
 summary = "MessagePack serializer"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"},
     {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"},
@@ -1298,7 +1298,7 @@ name = "pbs-installer"
 version = "2025.2.12"
 requires_python = ">=3.8"
 summary = "Installer for Python Build Standalone"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "pbs_installer-2025.2.12-py3-none-any.whl", hash = "sha256:3d9034047945b2d5f169cd9bb324f1f28c37d0ec120d6110ddb10aa07016fb79"},
     {file = "pbs_installer-2025.2.12.tar.gz", hash = "sha256:c6815165babf312c90d27ccd16afe598de641d616860f88e1855f183b0253b39"},
@@ -1309,7 +1309,7 @@ name = "platformdirs"
 version = "4.3.6"
 requires_python = ">=3.8"
 summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
-groups = ["default", "doc", "test", "tox"]
+groups = ["default", "doc", "tox"]
 files = [
     {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
     {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
@@ -1488,7 +1488,7 @@ name = "pygments"
 version = "2.17.2"
 requires_python = ">=3.7"
 summary = "Pygments is a syntax highlighting package written in Python."
-groups = ["default", "all", "cookiecutter", "copier", "doc", "template", "test"]
+groups = ["default", "all", "cookiecutter", "copier", "doc", "template"]
 files = [
     {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
     {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
@@ -1529,7 +1529,7 @@ name = "pyproject-hooks"
 version = "1.2.0"
 requires_python = ">=3.7"
 summary = "Wrappers to call pyproject.toml-based build backend hooks."
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"},
     {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"},
@@ -1661,7 +1661,7 @@ name = "python-dotenv"
 version = "1.0.1"
 requires_python = ">=3.8"
 summary = "Read key-value pairs from a .env file and set them as environment variables"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
     {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
@@ -1858,7 +1858,7 @@ name = "requests"
 version = "2.31.0"
 requires_python = ">=3.7"
 summary = "Python HTTP for Humans."
-groups = ["default", "all", "cookiecutter", "doc", "template", "test"]
+groups = ["default", "all", "cookiecutter", "doc", "template"]
 dependencies = [
     "certifi>=2017.4.17",
     "charset-normalizer<4,>=2",
@@ -1875,7 +1875,7 @@ name = "resolvelib"
 version = "1.1.0"
 requires_python = ">=3.7"
 summary = "Resolve abstract dependencies into concrete ones"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "resolvelib-1.1.0-py2.py3-none-any.whl", hash = "sha256:f80de38ae744bcf4e918e27a681a5c6cb63a08d9a926c0989c0730bcdd089049"},
     {file = "resolvelib-1.1.0.tar.gz", hash = "sha256:b68591ef748f58c1e2a2ac28d0961b3586ae8b25f60b0ba9a5e4f3d87c1d6a79"},
@@ -1886,7 +1886,7 @@ name = "rich"
 version = "13.9.2"
 requires_python = ">=3.8.0"
 summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-groups = ["default", "all", "cookiecutter", "template", "test"]
+groups = ["default", "all", "cookiecutter", "template"]
 dependencies = [
     "markdown-it-py>=2.2.0",
     "pygments<3.0.0,>=2.13.0",
@@ -1929,7 +1929,7 @@ name = "shellingham"
 version = "1.5.4"
 requires_python = ">=3.7"
 summary = "Tool to Detect Surrounding Shell"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
     {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
@@ -1962,7 +1962,7 @@ name = "socksio"
 version = "1.0.0"
 requires_python = ">=3.6"
 summary = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5."
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"},
     {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"},
@@ -1995,7 +1995,7 @@ name = "tomlkit"
 version = "0.13.2"
 requires_python = ">=3.8"
 summary = "Style preserving TOML library"
-groups = ["default", "test"]
+groups = ["default"]
 files = [
     {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"},
     {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"},
@@ -2063,7 +2063,7 @@ name = "truststore"
 version = "0.9.2"
 requires_python = ">=3.10"
 summary = "Verify certificates using native system trust stores"
-groups = ["default", "test"]
+groups = ["default"]
 marker = "python_version >= \"3.10\""
 files = [
     {file = "truststore-0.9.2-py3-none-any.whl", hash = "sha256:04559916f8810cc1a5ecc41f215eddc988746067b754fc0995da7a2ceaf54735"},
@@ -2083,17 +2083,17 @@ files = [
 
 [[package]]
 name = "unearth"
-version = "0.17.2"
+version = "0.17.4"
 requires_python = ">=3.8"
 summary = "A utility to fetch and download python packages"
-groups = ["default", "test"]
+groups = ["default"]
 dependencies = [
     "httpx<1,>=0.27.0",
     "packaging>=20",
 ]
 files = [
-    {file = "unearth-0.17.2-py3-none-any.whl", hash = "sha256:4d21af1238a583835fca156322f7225382e718cdcc42d6278050a88e605c4ad5"},
-    {file = "unearth-0.17.2.tar.gz", hash = "sha256:0b8a2afd3476f1ab6155fc579501ac47fffe43547d88a70e5a5b76a7fe6caa2c"},
+    {file = "unearth-0.17.4-py3-none-any.whl", hash = "sha256:5b0eb24bfd479642ac5dc73a9123fc5f8660f1c2e5672c5fd54b1731c3ac6165"},
+    {file = "unearth-0.17.4.tar.gz", hash = "sha256:ac881e9a39de4654c1c23872b15988dcca4745ef5cdfc9579e85c00c70f99f18"},
 ]
 
 [[package]]
@@ -2101,7 +2101,7 @@ name = "urllib3"
 version = "2.2.1"
 requires_python = ">=3.8"
 summary = "HTTP library with thread-safe connection pooling, file post, and more."
-groups = ["default", "all", "cookiecutter", "doc", "template", "test"]
+groups = ["default", "all", "cookiecutter", "doc", "template"]
 files = [
     {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
     {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
@@ -2112,7 +2112,7 @@ name = "virtualenv"
 version = "20.27.0"
 requires_python = ">=3.8"
 summary = "Virtual Python Environment builder"
-groups = ["default", "test", "tox"]
+groups = ["default", "tox"]
 dependencies = [
     "distlib<1,>=0.3.7",
     "filelock<4,>=3.12.2",
@@ -2190,7 +2190,7 @@ name = "zipp"
 version = "3.20.2"
 requires_python = ">=3.8"
 summary = "Backport of pathlib-compatible object wrapper for zip files"
-groups = ["default", "all", "doc", "keyring", "test", "workflow"]
+groups = ["default", "all", "doc", "keyring", "workflow"]
 marker = "python_version < \"3.12\""
 files = [
     {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"},
diff --git a/pyproject.toml b/pyproject.toml
index 11ef50adaa..89194484b3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,7 +20,7 @@ dependencies = [
     "rich>=12.3.0",
     "virtualenv>=20",
     "pyproject-hooks",
-    "unearth>=0.17.0",
+    "unearth>=0.17.4",
     "dep-logic>=0.4.4",
     "findpython>=0.6.0,<1.0.0a0",
     "tomlkit>=0.11.1,<1",
diff --git a/src/pdm/models/candidates.py b/src/pdm/models/candidates.py
index 53b386ed52..13e9a71245 100644
--- a/src/pdm/models/candidates.py
+++ b/src/pdm/models/candidates.py
@@ -8,7 +8,7 @@
 from functools import cached_property
 from pathlib import Path
 from tempfile import TemporaryDirectory
-from typing import TYPE_CHECKING, Any, cast, no_type_check
+from typing import TYPE_CHECKING, Any, ClassVar, cast, no_type_check
 from zipfile import ZipFile
 
 from packaging.version import InvalidVersion
@@ -306,6 +306,8 @@ class PreparedCandidate:
     The metadata and built wheel are available.
     """
 
+    _build_dir_cache: ClassVar[dict[Link, str]] = {}
+
     candidate: Candidate
     environment: BaseEnvironment
     reporter: CandidateReporter = dataclasses.field(default_factory=CandidateReporter)
@@ -678,7 +680,9 @@ def _get_build_dir(self) -> str:
                 dirname, _ = os.path.splitext(self.link.filename)
             return str(src_dir / str(dirname))
         # Otherwise, for source dists, they will be unpacked into a *temp* directory.
-        return self.environment.project.core.create_temp_dir(prefix="pdm-build-")
+        if self.link not in self._build_dir_cache:
+            self._build_dir_cache[self.link] = self.environment.project.core.create_temp_dir(prefix="pdm-build-")
+        return self._build_dir_cache[self.link]
 
     def _wheel_compatible(self, wheel_file: str, allow_all: bool = False) -> bool:
         env_spec = self.environment.allow_all_spec if allow_all else self.environment.spec


Test patch:
diff --git a/src/pdm/pytest.py b/src/pdm/pytest.py
index 0bfded6446..d23ad4b9d4 100644
--- a/src/pdm/pytest.py
+++ b/src/pdm/pytest.py
@@ -304,8 +304,9 @@ def build_env_wheels() -> Iterable[Path]:
 
 
 @pytest.fixture(autouse=True)
-def temp_env() -> Generator[MutableMapping[str, str]]:
+def temp_env(monkeypatch: pytest.MonkeyPatch) -> Generator[MutableMapping[str, str]]:
     old_env = os.environ.copy()
+    monkeypatch.setattr("pdm.models.candidates.PreparedCandidate._build_dir_cache", {})
     try:
         yield os.environ
     finally: