VCS, Direct URL & Local Project Installs
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the Python Reference bibliography. |
Not every requirement comes from an index. pip can install straight from a version-control repository, from an archive URL, or
from a directory on disk, either as a normal install or in editable mode. This page was written against pip 26.2.2 and run on
CPython 3.14.6 on 2026-10-22. The VCS examples use a local Git repository, birdwatch-mono, which holds birdtally under
libs/birdtally and has a tag v0.1.0; it is addressed with git+file:// so that the examples need no network. Scratch
paths are shortened to …. How these installs are recorded is defined by the direct URL specification (PEP 610), introduced on
Packaging Concepts & Standards.
VCS installs
A VCS requirement is a direct reference with a <vcs>+<scheme> URL:
birdtally @ git+https://git.example.org/birdwatch/birdwatch-mono.git@v0.1.0#subdirectory=libs/birdtally
birdtally @ git+ssh://git@git.example.org/birdwatch/birdwatch-mono.git@main
birdtally @ git+file:///srv/git/birdwatch-mono@053e5ca7de7521c66de7bfebb6458f986012e4b8
| VCS | Schemes |
|---|---|
Git |
|
Mercurial |
|
Subversion |
|
Bazaar |
|
-
The ref after
@is a branch, a tag, a commit hash, or for Git any ref such asrefs/pull/123/head. Give a full commit hash when you pin one: it lets pip skip network calls. -
#subdirectory=<path>points at the project inside the repository, for monorepos. -
Partial clones. With Git 2.17 or later pip asks for a partial clone (
--filter=blob:none). If a server claims to support it but does not,PIP_NO_PARTIAL_CLONE_FOR_BROKEN_GIT_SERVER=1(honoured since pip 26.2) forces a full clone. Since 26.2 pip also avoids re-fetching a pinned commit that is already present locally.
Installing birdtally from the tag:
python -m pip install --no-deps "birdtally @ git+file:///…/birdwatch-mono@v0.1.0#subdirectory=libs/birdtally"
Collecting birdtally @ git+file:/…/birdwatch-mono@v0.1.0#subdirectory=libs/birdtally
Cloning file:///…/birdwatch-mono (to revision v0.1.0) to /tmp/pip-install-l2u21f9j/birdtally_70ee860d10004263b02d608981e09a5c
Running command git clone --filter=blob:none --quiet file:///…/birdwatch-mono /tmp/pip-install-l2u21f9j/birdtally_70ee860d10004263b02d608981e09a5c
Resolved file:///…/birdwatch-mono to commit 053e5ca7de7521c66de7bfebb6458f986012e4b8
…
Successfully installed birdtally-0.1.0
pip resolved the tag to a commit, built a wheel from that checkout and installed it. The commit is recorded in
direct_url.json (the commit id is shortened here):
{
"subdirectory": "libs/birdtally",
"url": "file:///…/birdwatch-mono",
"vcs_info": {
"commit_id": "053e5ca7…f986012e4b8",
"requested_revision": "v0.1.0",
"vcs": "git"
}
}
and pip freeze therefore writes the immutable commit rather than the tag:
birdtally @ git+file:///…/birdwatch-mono@053e5ca7de7521c66de7bfebb6458f986012e4b8#subdirectory=libs/birdtally
A VCS requirement is installed only if no satisfactory version is installed yet: pip compares the version in the package’s metadata,
not the commit, so a new commit with an unchanged version number needs --upgrade or --force-reinstall.
Source: pip: Supported VCS, pip: Git, pip: Mercurial, pip: Subversion, pip: Bazaar, pip: URL fragments, PyPA: Direct URL data structure, VCS URLs.
Editable VCS installs
With -e, pip clones the repository into <venv>/src/<name> (or <cwd>/src/<name> outside a venv; --src <dir> changes it) and
installs it in editable mode, so you can work in the checkout. The project name must be given with the name @ URL form, so that pip
knows the clone directory before it has the metadata:
python -m pip install -e "birdtally @ git+file:///…/birdwatch-mono@v0.1.0#subdirectory=libs/birdtally"
ls .venv/src .venv/src/birdtally
python -m pip freeze
Obtaining birdtally from git+file:/…/birdwatch-mono@v0.1.0#subdirectory=libs/birdtally
Cloning file:///…/birdwatch-mono (to revision v0.1.0) to ./.venv/src/birdtally
Resolved file:///…/birdwatch-mono to commit 053e5ca7de7521c66de7bfebb6458f986012e4b8
…
Successfully installed birdtally-0.1.0
.venv/src:
birdtally
.venv/src/birdtally:
libs
-e git+file:///…/birdwatch-mono@053e5ca7de7521c66de7bfebb6458f986012e4b8#egg=birdtally&subdirectory=libs/birdtally
pip freeze still writes the old #egg= fragment for editable VCS installs. The fragment is a legacy way to give the project name:
the documentation discourages it in favour of the direct URL form, it must be a bare project name, and pip 26.0 removed support for
anything else in it (such as #egg=birdtally[pretty] or a version).
Source: pip: Editable VCS installs.
Direct URLs to archives
A wheel or sdist on any web server can be installed with name @ https://…/file, optionally with a #sha256= fragment that pip
checks. direct_url.json then records archive_info with the hash, and pip freeze reproduces the URL. PyPI rejects uploads whose
own dependencies are direct references, so this form is for applications and lock files, not for library metadata.
Local archives and directories
python -m pip install ./dist/birdtally-0.1.0-py3-none-any.whl # a wheel: unpacked, no build
python -m pip install ./dist/birdtally-0.1.0.tar.gz # an sdist: built into a wheel first
python -m pip install ./birdtally # a project directory: built in place
A local directory must contain pyproject.toml or setup.py. Its direct_url.json has dir_info, and since pip 26.2 local
directories are never served from or stored in the persistent wheel cache, so a rebuilt directory is always rebuilt.
Regular and editable installs of a local project
pip install . (regular) |
pip install -e . (editable) |
|
|---|---|---|
What is installed |
A wheel built from the directory: a copy of the files. |
A small editable wheel (PEP 660) that points at the directory. |
Editing the source |
Needs a reinstall. |
Takes effect on the next import (except metadata, entry points and compiled code). |
Backend hooks |
|
|
|
|
|
The editable install of birdtally, with the .pth file hatchling uses, is shown on
Packaging Concepts & Standards.
History that explains old instructions you may find:
-
pip 21.3 added PEP 660 editables and switched to in-tree builds: the project is no longer copied to a temporary directory before the backend runs (the
--use-deprecated=out-of-tree-buildfallback was removed in 22.1). Builds are faster, but backends such as setuptools leave artefacts (build/,*.egg-info) in your tree and may not support parallel builds of the same directory. -
pip 25.3 removed the
setup.py developfallback for projects whose backend does not implementbuild_editable. With setuptools that means version 64 or later is required forpip install -e ..
pip install -e . ignores lock files. Installing the project resolves its [project] dependencies again and may upgrade or
downgrade what a lock or requirements file installed. Install the locked dependencies first and then the project with --no-deps
(pip install -r requirements.lock && pip install --no-deps -e .), or use Poetry, whose poetry install installs the root project
from the lock.
pip and Poetry
| pip | Poetry |
|---|---|
|
|
|
|
|
|
|
|
|
|
Poetry records the resolved commit in poetry.lock; see
Poetry: Dependency Specification.