diff --git a/.CodeQL.yml b/.CodeQL.yml
new file mode 100644
index 000000000..f1d7cb04a
--- /dev/null
+++ b/.CodeQL.yml
@@ -0,0 +1,4 @@
+path_classifiers:
+ library:
+ - "src/debugpy/_vendored/pydevd"
+
\ No newline at end of file
diff --git a/.coveragerc b/.coveragerc
index e8542738f..ef72dd31a 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -11,16 +11,19 @@ omit =
data_file = coverage/.coverage
[report]
-exclude_lines =
- # Have to re-enable the standard pragma.
- pragma: no cover
-
+exclude_also =
# __repr__ is mostly used for error messages.
def __repr__
# Asserts and error conditions.
raise AssertionError
raise NotImplementedError
+ raise TypeError
+ raise ValueError
+ raise AttributeError
+ raise RuntimeError
+ raise ComponentNotAvailable
+ raise messaging.MessageHandlingError
\.isnt_valid\(
\.cant_handle\(
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 000000000..31d6c5200
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,11 @@
+FROM mcr.microsoft.com/devcontainers/python:3.14
+
+# Install GDB
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends gdb \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/*
+
+# Switch to non-root user and install Python packages
+USER vscode
+RUN python -m pip install black flake8 tox
\ No newline at end of file
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..114c0b7c4
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,12 @@
+{
+ "name": "debugpy Development",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+
+ "features": {
+ "ghcr.io/devcontainers/features/git:1": {}
+ },
+
+ "remoteUser": "vscode"
+}
diff --git a/.flake8 b/.flake8
index dace0c832..cb12c455c 100644
--- a/.flake8
+++ b/.flake8
@@ -11,3 +11,4 @@ exclude =
dist,
versioneer.py,
src/debugpy/_vendored/pydevd
+
diff --git a/.gdn/.gdnsuppress b/.gdn/.gdnsuppress
new file mode 100644
index 000000000..c7c146e0e
--- /dev/null
+++ b/.gdn/.gdnsuppress
@@ -0,0 +1,42 @@
+{
+ "hydrated": true,
+ "properties": {
+ "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/suppressions"
+ },
+ "version": "1.0.0",
+ "suppressionSets": {
+ "default": {
+ "name": "default",
+ "createdDate": "2026-01-28 00:33:53Z",
+ "lastUpdatedDate": "2026-01-28 00:33:53Z"
+ }
+ },
+ "results": {
+ "58a87fd4d7371ece41c06d9d8ef960a90be375cf25621fdddc0f69ed70a8b6f4": {
+ "signature": "58a87fd4d7371ece41c06d9d8ef960a90be375cf25621fdddc0f69ed70a8b6f4",
+ "alternativeSignatures": [
+ "86f6ccbed316821fbcca7e2f0daafcc164b6df645a491d850e1ac16b39eb747e"
+ ],
+ "target": "pydevd/inject_dll_amd64.exe",
+ "memberOf": [
+ "default"
+ ],
+ "tool": "binskim",
+ "ruleId": "BA2007",
+ "createdDate": "2026-01-28 00:33:53Z"
+ },
+ "2b82071ec3bb1a8e80b1615d79febb69084008a4b5a19e66dbeac590b39d4c55": {
+ "signature": "2b82071ec3bb1a8e80b1615d79febb69084008a4b5a19e66dbeac590b39d4c55",
+ "alternativeSignatures": [
+ "3e5a1d789a42983ae1541ba91e20f10f388886a5ba62833ec66e0a358adc1174"
+ ],
+ "target": "pydevd/inject_dll_x86.exe",
+ "memberOf": [
+ "default"
+ ],
+ "tool": "binskim",
+ "ruleId": "BA2007",
+ "createdDate": "2026-01-28 00:33:53Z"
+ }
+ }
+}
\ No newline at end of file
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6c724a5e0..ee161875e 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -4,7 +4,4 @@
# in the repository, i.e. bar/baz will match /bar/baz and /foo/bar/baz.
# The default owners for everything that is not overridden by specific patterns below.
-* @int19h @karthiknadig
-
-# Order is important: the last matching pattern overrides all the preceding ones.
-/src/ptvsd/_vendored/pydevd/ @fabioz
+* @microsoft/pyrx-admins
diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml
new file mode 100644
index 000000000..975fc7b09
--- /dev/null
+++ b/.github/workflows/auto-label.yml
@@ -0,0 +1,39 @@
+name: Auto-label when user responds
+permissions:
+ issues: write
+
+on:
+ issue_comment:
+ types: [created]
+
+env:
+ TRIAGERS: '["int19h","karthiknadig","ericsnowcurrently","fabioz", "gramster", "StellaHuang95", "AdamYoblick"]'
+
+jobs:
+ run-check:
+ runs-on: ubuntu-latest
+ if: contains(github.event.issue.labels.*.name, 'waiting for response') && !contains(github.event.issue.labels.*.name, 'user responded')
+ steps:
+ - name: Add/Remove labels when user responds
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const payload = context.payload;
+ const commentAuthor = payload.comment.user.login;
+ const isTeamMember = ${{ env.TRIAGERS }}.includes(commentAuthor);
+ if (!isTeamMember) {
+ const issue_number = payload.issue.number;
+ await github.rest.issues.removeLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issue_number,
+ name: 'waiting for response'
+ });
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issue_number,
+ labels: ['user responded']
+ });
+ }
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 8604f9d59..0adf64394 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -5,6 +5,9 @@ on:
pull_request:
schedule:
- cron: "0 19 * * 0"
+
+permissions:
+ security-events: write
jobs:
CodeQL-Build:
@@ -13,40 +16,18 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v2
- # with:
- # # We must fetch at least the immediate parents so that if this is
- # # a pull request then we can checkout the head.
- # fetch-depth: 2
-
- # # If this run was triggered by a pull request event, then checkout
- # # the head of the pull request instead of the merge commit.
- # - run: git checkout HEAD^2
- # if: ${{ github.event_name == 'pull_request' }}
+ uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
- # Override language selection by uncommenting this and choosing your languages
- # Supported languages: go, javascript, csharp, python, cpp, java
+ uses: github/codeql-action/init@v2
with:
languages: python
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v1
-
- # βΉοΈ Command-line programs to run using the OS shell.
- # π https://git.io/JvXDl
-
- # βοΈ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
+ uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/round-robin.yml b/.github/workflows/round-robin.yml
new file mode 100644
index 000000000..f85b75f2e
--- /dev/null
+++ b/.github/workflows/round-robin.yml
@@ -0,0 +1,79 @@
+name: round-robin
+permissions:
+ issues: write
+
+on:
+ issues:
+ types: [opened]
+
+jobs:
+ assignIssue:
+ name: Assign Issue to Someone
+ runs-on: ubuntu-latest
+ if: github.repository == 'microsoft/debugpy' && github.event.action == 'opened' && !contains( github.event.issue.labels.*.name, 'skip-reassign')
+ steps:
+ - name: Find last assigned
+ id: assigned
+ uses: actions/github-script@v6.3.3
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ result-encoding: string
+ script: |
+ const issue = await github.rest.issues.get({
+ issue_number: 1649,
+ owner: context.repo.owner,
+ repo: context.repo.repo
+ });
+ return (issue.data.assignees && issue.data.assignees[0].login) || '';
+ - name: Dump last assigned
+ env:
+ LAST_ASSIGNED: ${{ steps.assigned.outputs.result }}
+ run: echo "$LAST_ASSIGNED"
+ - uses: lee-dohm/team-rotation@v1
+ with:
+ last: ${{ steps.assigned.outputs.result }}
+ include: bschnurr heejaechang StellaHuang95 rchiodo gramster
+ id: rotation
+ - name: Dump next in rotation
+ env:
+ NEXT_ROTATION: ${{ steps.rotation.outputs.next }}
+ run: echo "$NEXT_ROTATION"
+ - name: Assign to next person
+ uses: actions/github-script@v6.3.3
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ github.rest.issues.addAssignees({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ assignees: ['${{ steps.rotation.outputs.next }}']
+ })
+ - name: Give it the label 'needs repro'
+ uses: actions/github-script@v6.3.3
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: ['needs repro']
+ })
+ - name: Save assignment to state
+ uses: actions/github-script@v6.3.3
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ await github.rest.issues.removeAssignees({
+ issue_number: 1649,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ assignees: ['${{ steps.assigned.outputs.result }}']
+ });
+ github.rest.issues.addAssignees({
+ issue_number: 1649,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ assignees: ['${{ steps.rotation.outputs.next }}']
+ })
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..e891e752d
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,34 @@
+# This workflow closes issues that have been labeled as 'waiting for response' and had no activity for 30 days.
+#
+# You can adjust the behavior by modifying this file.
+# For more information, see:
+# https://github.com/actions/stale
+name: Close stale 'waiting for response' issues
+
+on:
+ schedule:
+ # ββββββββββββββ minute (0 - 59)
+ # β ββββββββββββββ hour (0 - 23)
+ # β β ββββββββββββββ day of the month (1 - 31)
+ # β β β ββββββββββββββ month (1 - 12 or JAN-DEC)
+ # β β β β ββββββββββββββ day of the week (0 - 6 or SUN-SAT)
+ # β β β β β
+ # β β β β β
+ # β β β β β
+ # * * * * *
+ - cron: "0 0 * * *"
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+
+ steps:
+ - uses: actions/stale@v5
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ days-before-issue-stale: 30
+ days-before-issue-close: 0
+ close-issue-message: "This issue was closed because it has been stalled for 30 days with no activity. If the issue still persists, please reopen with the information requested. Thanks."
+ any-of-labels: "waiting for response"
diff --git a/.gitignore b/.gitignore
index 6ff7595b5..fac40c8d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,11 +5,14 @@ __pycache__/
# C extensions
*.so
+*.dylib
+*.dll
+*.pdb
+*.exe
# Distribution / packaging
.Python
env/
-build/
develop-eggs/
dist/
downloads/
@@ -24,6 +27,7 @@ var/
wheels/
wheelhouse/
*.egg-info/
+*.dist-info/
.installed.cfg
*.egg
@@ -85,7 +89,7 @@ celerybeat-schedule
*.sage.py
# virtualenv
-.venv
+.venv*
venv/
ENV/
@@ -106,6 +110,7 @@ ENV/
.vs/
.vscode/*
!.vscode/launch.json
+!.vscode/extensions.json
# PyDev
.project
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 000000000..7c0f031c5
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "ms-python.python",
+ "ms-python.vscode-pylance",
+ "ms-python.flake8",
+ "ms-python.black-formatter"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 3d891db70..43e7322c9 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -52,6 +52,7 @@
"host": "127.0.0.1"
},
"logToFile": true,
+ //"restart": true,
"debugAdapterPath": "${workspaceFolder}/src/debugpy/adapter"
},
{
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 41b0fd781..49a30c7a4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,7 +24,7 @@ The following tools are required to work on debugpy:
- [Black](https://black.readthedocs.io/en/stable/)
- [tox](https://tox.readthedocs.io/en/latest/)
-We recommend using [Visual Studio Code](https://code.visualstudio.com/) with the (Python extension)[https://marketplace.visualstudio.com/items?itemName=ms-python.python] to work on debugpy, but it's not a requirement. A workspace file, [debugpy.code-workspace], is provided for the convenience of VSCode users, and sets it up to use the other tools listed above properly.
+We recommend using [Visual Studio Code](https://code.visualstudio.com/) with the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) to work on debugpy, but it's not a requirement. A workspace file, [debugpy.code-workspace], is provided for the convenience of VSCode users, and sets it up to use the other tools listed above properly.
Tools that are Python packages should be installed via pip corresponding to the Python 3 installation. On Windows:
```
@@ -65,7 +65,7 @@ On Linux or macOS:
```
.../debugpy$ python3 -m tox
```
-This will perform a full run with the default settings. A full run will run tests on Python 2.7 and 3.5-3.8, and requires all of those to be installed. If some versions are missing, or it is desired to skip them for a particular run, tox can be directed to only run tests on specific versions with `-e`. In addition, the `--developer` option can be used to skip the packaging step, running tests directly against the source code in `src/debugpy`. This should only be used when iterating on the code, and a proper run should be performed before submitting a PR. On Windows:
+This will perform a full run with the default settings. A full run will run tests on Python 2.7 and 3.5-3.8, and requires all of those to be installed. If some versions are missing, or it is desired to skip them for a particular run, tox can be directed to only run tests on specific versions with `-e`. In addition, the `--develop` option can be used to skip the packaging step, running tests directly against the source code in `src/debugpy`. This should only be used when iterating on the code, and a proper run should be performed before submitting a PR. On Windows:
```
...\debugpy> py -m tox -e py27,py37 --develop
```
@@ -74,9 +74,173 @@ On Linux or macOS:
.../debugpy$ python3 -m tox -e py27,py37 --develop
```
+You can run all tests in a single file using a specified python version, like this:
+```
+...\debugpy> py -m tox --develop -e py312 -- ".\tests\debugpy\server\test_cli.py"
+```
+
+You can also specify a single test, like this:
+```
+...\debugpy> py -m tox --develop -e py312 -- ".\tests\debugpy\server\test_cli.py::test_duplicate_switch"
+```
+
+The tests are run concurrently, and the default number of workers is 8. You can force a single worker by using the `-n0` flag, like this:
+```
+...\debugpy> py -m tox --develop -e py312 -- -n0 ".\tests\debugpy\server\test_cli.py"
+```
+
### Running tests without tox
-While tox is the recommended way to run the test suite, pytest can also be invoked directly from the root of the repository. This requires packages in tests/test_requirements.txt to be installed first.
+While tox is the recommended way to run the test suite, pytest can also be invoked directly from the root of the repository. This requires packages in tests/requirements.txt to be installed first.
+
+Using a venv created by tox in the '.tox' folder can make it easier to get the pytest configuration correct. Debugpy needs to be installed into the venv for the tests to run, so using the tox generated .venv makes that easier.
+
+#### Keeping logs on test success
+
+There's an internal setting `debugpy_log_passed` that if set to true will not erase the logs after a successful test run. Just search for this in the code and remove the code that deletes the logs on success.
+
+#### Adding logging
+
+Using `pydevd_log.debug` you can add logging just about anywhere in the pydevd code. However this code won't be called if CYTHON support is enabled without recreating the Cython output. To temporarily disable CYTHON support, look for `CYTHON_SUPPORTED` and make sure it's set to False
+
+## Updating pydevd
+
+Pydevd (at src/debugpy/_vendored/pydevd) is a subrepo of https://github.com/fabioz/PyDev.Debugger. We use the [subrepo](https://github.com/ingydotnet/git-subrepo) to have a copy of pydevd inside of debugpy
+
+In order to update the source, you would:
+- git checkout -b "branch name"
+- python subrepo.py pull
+- git push
+- Fix any debugpy tests that are failing as a result of the pull
+- Create a PR from your branch
+
+You might need to regenerate the Cython modules after any changes. This can be done by:
+
+- Install Python latest (3.14 as of this writing)
+- pip install cython 'django>=1.9' 'setuptools>=0.9' 'wheel>0.21' twine
+- On a windows machine:
+ - set FORCE_PYDEVD_VC_VARS=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat
+ - in the pydevd folder: python .\build_tools\build.py
+
+## Pushing pydevd back to PyDev.Debugger
+
+If you've made changes to pydevd (at src/debugpy/_vendored/pydevd), you'll want to push back changes to pydevd so as Fabio makes changes to pydevd we can continue to share updates.
+
+To do this, you would:
+
+- Create a fork of https://github.com/fabioz/PyDev.Debugger
+- Switch back to your debugpy clone
+- python subrepo.py branch -m "pydevd branch you want to create"
+- git push -f https://github.com/"your fork"/PyDev.Debugger subrepo/src/debugpy/_vendored/pydevd:$(pydevd branch you want to create)
+- Create a PR from that branch
+- Get Fabio's buyoff on the changes
+
+### Setting up pydevd to be testable
+
+Follow these steps to get pydevd testable:
+
+- create an environment to test. The list of stuff in your environment is outlined [here](https://github.com/fabioz/PyDev.Debugger/blob/6cd4d431e6a794448f33a73857d479149041500a/.github/workflows/pydevd-tests-python.yml#L83).
+- set PYTHONPATH=. (make sure you don't forget this part, otherwise a lot of tests will fail)
+
+### Testing pydevd and fixing test failures
+
+Pydevd has a lot more tests on execution than debugpy. They reside in all of the `test` folders under the root. The majority of the execution tests are in the `tests_python` folder.
+
+You run all of the tests with (from the root folder):
+
+- python -m pytest -n auto -rfE
+
+That will run all of the tests in parallel and output any failures.
+
+If you want to just see failures you can do this:
+
+- python -m pytest -n auto -q
+
+That should generate output that just lists the tests which failed.
+
+```
+=============================================== short test summary info ===============================================
+FAILED tests_python/test_debugger.py::test_path_translation[True] - AssertionError: TimeoutError (note: error trying to dump threads on timeout).
+FAILED tests_python/test_debugger.py::test_remote_debugger_multi_proc[False] - AssertionError: TimeoutError
+FAILED tests_python/test_debugger.py::test_path_translation[False] - AssertionError: TimeoutError (note: error trying to dump threads on timeout).
+======================== 3 failed, 661 passed, 169 skipped, 77 warnings in 319.05s (0:05:19) =========================
+```
+With that you can then run individual tests like so:
+
+- python -m pytest -n auto tests_python/test_debugger.py::test_path_translation[False]
+
+That will generate a log from the test run.
+
+Logging the test output can be tricky so here's some information on how to debug the tests.
+
+#### Running pydevd tests inside of VS code
+
+You can also run the pydevd tests inside of VS code using the test explorer (and debug the pytest code). To do so, set PYTHONPATH=. and open the `src/debugpy/_vendored/pydevd` folder in VS code. The test explorer should find all of the pydevd tests.
+
+#### How to add more logging
+
+The pydevd tests log everything to the console and to a text file during the test. If you scroll up in the console, it should show the log file it read the logs from:
+
+```
+Log on failure:
+-------------------- C:\Users\rchiodo\AppData\Local\Temp\pytest-of-rchiodo\pytest-77\popen-gw3\test_path_translation_and_sour0\pydevd_debug_file_23524.32540.txt ------------------
+```
+
+If you want to add more logging in order to investigate something that isn't working, you simply add a line like so in the code:
+
+```python
+ pydevd_log.debug("Some test logging", frame, etc)
+```
+
+Make sure if you add this in a module that gets `cythonized`, that you turn off `Cython` support as listed above. Otherwise you'll have to regen the C code or you won't actually see your new log output.
+
+#### How to use logs to debug failures
+
+Investigating log failures can be done in multiple ways.
+
+If you have an existing test failing, you can investigate it by running the test with the main branch and comparing the results. To do so you would:
+
+- Clone the repo a second time
+- Change the code in `tests_python/debugger_unittest.py` so that the test prints out logs on success too (by default it only logs the output on a failure)
+- Run the failing test in the second clone
+- Run the failing test in your original clone (with the --capture=tee-sys so that it outputs the log)
+- Diff the results by finding the log file name in the output and diffing those two files
+- Add more logging around where the differences first appear
+- Repeat running and diffing
+
+If you're adding a new test or just trying to figure out what the expected log output is, you would look at the failing test to see what steps are expected in the output. Here's an example:
+
+```python
+def test_case_double_remove_breakpoint(case_setup):
+ with case_setup.test_file("_debugger_case_remove_breakpoint.py") as writer:
+ breakpoint_id = writer.write_add_breakpoint(writer.get_line_index_with_content("break here"))
+ writer.write_make_initial_run()
+
+ hit = writer.wait_for_breakpoint_hit()
+ writer.write_remove_breakpoint(breakpoint_id)
+ writer.write_remove_breakpoint(breakpoint_id) # Double-remove (just check that we don't have an error).
+ writer.write_run_thread(hit.thread_id)
+
+ writer.finished_ok = True
+```
+
+That test would have events correlating to:
+
+- Initialization (all debug sessions have this)
+- Setting breakpoints on a specific line
+- Breakpoint event being hit
+- Setting breakpoints to empty
+- Setting breakpoints to empty
+- Continue event
+
+Those would show up in the log like so:
+
+Breakpoint command
+```
+0.00s - Received command: CMD_SET_BREAK 111 3 1 python-line C:\Users\rchiodo\source\repos\PyDev.Debugger\tests_python\resources\_debugger_case_remove_breakpoint.py 7 None None None
+```
+
+In order to investigate a failure you'd look for the CMDs you expect and then see where the CMDs deviate. At that point you'd add logging around what might have happened next.
## Using modified debugpy in Visual Studio Code
To test integration between debugpy and Visual Studio Code, the latter can be directed to use a custom version of debugpy in lieu of the one bundled with the Python extension. This is done by specifying `"debugAdapterPath"` in `launch.json` - it must point at the root directory of the *package*, which is `src/debugpy` inside the repository:
@@ -88,3 +252,28 @@ To test integration between debugpy and Visual Studio Code, the latter can be di
...
}
```
+
+## Enabling logging in VS code
+See the directions here:
+https://github.com/microsoft/debugpy/wiki/Enable-debugger-logs
+
+## Debugging native code (Windows)
+
+To debug the native components of `debugpy`, such as `attach.cpp`, you can use Visual Studio's native debugging feature.
+
+Follow these steps to set up native debugging in Visual Studio:
+
+1. Open Visual Studio and go to `Debug` > `Options` > `Symbols`.
+2. Check the option **Search for all module symbols unless excluded**. This ensures that Visual Studio loads the necessary symbols (PDB files) for all modules, including dynamically loaded ones.
+3. Click **OK** to close the options dialog.
+4. Run your Python script from the command line, for example: `python ./main.py`
+5. In Visual Studio, go to `Debug` > `Attach to Process`.
+6. From the list of processes, select the appropriate Python process. Be sure to choose the correct process, especially if you're using a virtual environment. You can verify this by checking the command line associated with each process in the **Task Manager**.
+7. Under **Attach to**, choose either **Automatic: Native code** or explicitly select **Native** to attach as a native debugger.
+8. Click **Attach**.
+9. Open the native source file you want to debug, such as `attach.cpp`, and set breakpoints where necessary (e.g., at `DoAttach`).
+10. Trigger the loading of the DLL, such as by attaching `debugpy` to the Python process (refer to `Attach: PID` in `debugpy`'s `launch.json` for more details on attaching to the process).
+11. Once the DLL is loaded, Visual Studio will automatically load the associated PDB files, and your breakpoints should become active.
+12. When the breakpoint is hit, you can debug the native code as you would in any debug session.
+
+If you need to step into the Python code during the debug session, you can download the Python source code from [python.org](https://www.python.org/downloads/source/). Unzip it to a folder, and when Visual Studio prompts for the source location, point it to the folder where you extracted the Python source. Ensure that the Python version matches the interpreter used to run your script (e.g., `python ./main.py`).
\ No newline at end of file
diff --git a/README.md b/README.md
index 514e90218..e6c292f3a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# debugpy - a debugger for Python
+An implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/) for Python 3.
+
[](https://dev.azure.com/debugpy/debugpy/_build/latest?definitionId=1&branchName=main)
[](https://raw.githubusercontent.com/microsoft/debugpy/main/LICENSE)
[](https://pypi.org/project/debugpy/)
@@ -13,9 +15,10 @@
| Linux |  |
| Mac |  |
-This debugger implements the Debug Adapter Protocol: [debugProtocol.json](https://github.com/microsoft/vscode-debugadapter-node/blob/master/debugProtocol.json)
-
## `debugpy` CLI Usage
+
+For full details, see the [Command Line Reference](https://github.com/microsoft/debugpy/wiki/Command-Line-Reference).
+
### Debugging a script file
To run a script file with debugging enabled, but without waiting for the client to attach (i.e. code starts executing immediately):
```console
@@ -55,11 +58,14 @@ The following command injects the debugger into a process with a given PID that
### Ignoring subprocesses
The following command will ignore subprocesses started by the debugged process.
```console
--m debugpy --listen localhost:5678 --pid 12345 --config-subProcess False
+-m debugpy --listen localhost:5678 --pid 12345 --configure-subProcess False
```
## `debugpy` Import usage
+
+For full details, see the [API reference](https://github.com/microsoft/debugpy/wiki/API-Reference).
+
### Enabling debugging
At the beginning of your script, import debugpy, and call `debugpy.listen()` to start the debug adapter, passing a `(host, port)` tuple as the first argument.
```python
diff --git a/azure-pipelines/linux-coverage.yaml b/azure-pipelines/linux-coverage.yaml
index f07cb5b11..52d76dc3d 100644
--- a/azure-pipelines/linux-coverage.yaml
+++ b/azure-pipelines/linux-coverage.yaml
@@ -1,19 +1,37 @@
+# This pipeline is used to run unit test code coverage against debugpy public repo.
+
+# Trigger ci builds for commits into master and any release branches
+trigger:
+ branches:
+ include:
+ - main
+ - release/*
+
+# Trigger pr builds for commits into master and any release branches
+# Ignore draft PR's
+pr:
+ branches:
+ include:
+ - main
+ - release/*
+ drafts: "false"
+
variables:
- architecture: "x64"
- python.version: "3.8"
+ architecture: "x64"
+ python.version: "3.11"
jobs:
- - job: "Coverage"
- timeoutInMinutes: 15
- pool:
- name: Azure Pipelines
- demands: java
- vmImage: "ubuntu-latest"
- steps:
- - script: |
- sudo apt-get update
- sudo apt-get --yes install gdb
- sudo sysctl kernel.yama.ptrace_scope=0
- displayName: "Install gdb"
+ - job: "Coverage"
+ timeoutInMinutes: "60"
+ pool:
+ name: Azure Pipelines
+ demands: java
+ vmImage: "ubuntu-latest"
+ steps:
+ - script: |
+ sudo apt-get update
+ sudo apt-get --yes install gdb
+ sudo sysctl kernel.yama.ptrace_scope=0
+ displayName: "Install gdb"
- - template: "templates/coverage.yml"
+ - template: "templates/coverage.yml"
diff --git a/azure-pipelines/mac-coverage.yaml b/azure-pipelines/mac-coverage.yaml
index bb0dedbe0..c9a34f75e 100644
--- a/azure-pipelines/mac-coverage.yaml
+++ b/azure-pipelines/mac-coverage.yaml
@@ -1,17 +1,34 @@
+# This pipeline is used to run unit test code coverage against debugpy public repo.
+
+# Trigger ci builds for commits into master and any release branches
+trigger:
+ branches:
+ include:
+ - main
+ - release/*
+
+# Trigger pr builds for commits into master and any release branches
+# Ignore draft PR's
+pr:
+ branches:
+ include:
+ - main
+ - release/*
+ drafts: "false"
+
variables:
- architecture: "x64"
- python.version: "3.8"
+ architecture: "x64"
+ python.version: "3.11"
jobs:
- - job: "Coverage"
- timeoutInMinutes: 15
- pool:
- name: Azure Pipelines
- demands: java
- vmImage: "macOS-latest"
- steps:
- - script: |
- ulimit -Sn 8192
- displayName: "Increase file descriptor limit"
+ - job: "Coverage"
+ timeoutInMinutes: "60"
+ pool:
+ name: Azure Pipelines
+ demands: java
+ vmImage: "macOS-latest"
+ steps:
+ - script: "ulimit -Sn 8192"
+ displayName: "Increase file descriptor limit"
- - template: "templates/coverage.yml"
+ - template: "templates/coverage.yml"
diff --git a/azure-pipelines/pipelines.yaml b/azure-pipelines/pipelines.yaml
index cfe4425f8..f041cf236 100644
--- a/azure-pipelines/pipelines.yaml
+++ b/azure-pipelines/pipelines.yaml
@@ -1,111 +1,250 @@
+# This pipeline is used to run PR validation and CI builds against the debugpy public repo.
+# Seperate internal pipelines are used for generating wheels, signing, and releasing to pypi.org
+
+# Trigger ci builds for commits into master and any release branches
+trigger:
+ branches:
+ include:
+ - main
+ - release/*
+
+# Trigger pr builds for commits into master and any release branches
+pr:
+ branches:
+ include:
+ - '*'
+
variables:
- architecture: "x64"
+ architecture: x64
+ PYDEVD_ATTACH_TO_PROCESS: src/debugpy/_vendored/pydevd/pydevd_attach_to_process
-jobs:
- - job: "Lint"
- timeoutInMinutes: 15
- displayName: "Lint"
- pool: { vmImage: "ubuntu-latest" }
+stages:
- variables:
- python.version: "3.8"
+ - stage: Lint
+ jobs:
+ - job: Lint
+ timeoutInMinutes: 5
- steps:
- - template: "templates/use_python.yml"
+ pool:
+ vmImage: ubuntu-latest
+
+ variables:
+ python.version: "3.9"
- - script: |
- python3 -m pip install -U flake8
- python3 -m pip install -U git+https://github.com/karthiknadig/flake8reports
- displayName: "Setup flake8"
+ steps:
- - script: |
- python3 -m flake8 --format=junit --output-file=$(Build.ArtifactStagingDirectory)/lint-flake8.xml
- displayName: "Run flake8"
+ - template: templates/use_python.yml
- - task: "PublishTestResults@2"
- condition: "always()"
+ # Install and run ruff
+ # See https://github.com/astral-sh/ruff and https://beta.ruff.rs/docs/
+ - script: python3 -m pip install -U ruff
+ displayName: Install ruff
+
+ # Run linter
+ - script: python3 -m ruff check --output-format=junit --output-file=$(Build.ArtifactStagingDirectory)/lint-ruff.xml .
+ displayName: Run ruff
+
+ # Publish results, even on failure
+ - task: PublishTestResults@2
+ displayName: Publish linting results
inputs:
- testRunTitle: "$(Agent.JobName)"
- testResultsFiles: "lint-*.xml"
- searchFolder: "$(Build.ArtifactStagingDirectory)"
- displayName: "Publish linting results"
+ testRunTitle: $(Agent.JobName)
+ testResultsFiles: lint-*.xml
+ searchFolder: $(Build.ArtifactStagingDirectory)
+ condition: always()
+
+ - stage: Pydevd
+ jobs:
+ - job: Build
+ displayName: Build
- - job: "Test_Linux"
+ strategy:
+ matrix:
+ Windows:
+ image: windows-latest
+ contents: |
+ *.exe
+ *.dll
+ *.pdb
+ script: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)/windows/compile_windows.bat
+ workingDirectory: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)/windows
+ macOS:
+ image: macOS-latest
+ contents: |
+ *.dylib
+ script: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)/linux_and_mac/compile_mac.sh
+ workingDirectory: $(System.DefaultWorkingDirectory)
+ Linux:
+ image: ubuntu-latest
+ contents: |
+ *.so
+ script: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)/linux_and_mac/compile_linux.sh
+ workingDirectory: $(System.DefaultWorkingDirectory)
+
+ pool:
+ vmImage: $(image)
+
+ steps:
+
+ # Clean up old binaries
+ - task: DeleteFiles@1
+ displayName: Clean up old binaries
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)
+ Contents: $(contents)
+
+ # Build pydevd binaries
+ - script: $(script)
+ displayName: Build pydevd binaries
+ workingDirectory: $(workingDirectory)
+
+ # copy pydevd binaries
+ - task: CopyFiles@2
+ displayName: Copy pydevd binaries
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)
+ Contents: $(contents)
+ TargetFolder: $(Build.ArtifactStagingDirectory)
+
+ # Publish pydevd binaries
+ - task: PublishBuildArtifacts@1
+ displayName: Publish pydevd binaries
+ inputs:
+ artifactName: pydevd binaries
+ pathToPublish: $(Build.ArtifactStagingDirectory)
+
+ - stage: Test
+ dependsOn: Pydevd
+ jobs:
+
+ - job: Tests_Linux
+ displayName: Tests - Linux
timeoutInMinutes: 30
- displayName: "Tests - Linux"
- pool: { vmImage: "ubuntu-latest" }
+ pool:
+ vmImage: ubuntu-latest
strategy:
- matrix:
- py37:
- python.version: "3.7"
- py38:
- python.version: "3.8"
- py39:
- python.version: "3.9"
- py310:
- python.version: "3.10"
+ matrix:
+ py39:
+ python.version: 3.9
+ py310:
+ python.version: 3.10
+ py311:
+ python.version: 3.11
+ py312:
+ python.version: 3.12
+ py313:
+ python.version: 3.13
+ py314:
+ python.version: 3.14.0-rc.2
steps:
- - script: |
- sudo apt-get update
- sudo apt-get --yes install gdb
- sudo sysctl kernel.yama.ptrace_scope=0
- displayName: "Setup gdb"
- - template: "templates/use_python.yml"
+ - script: |
+ sudo apt-get update
+ sudo apt-get --yes install gdb
+ sudo sysctl kernel.yama.ptrace_scope=0
+ displayName: Setup gdb
+
+ - template: templates/use_python.yml
+
+ # download pydevd binaries
+ - download: current
+ displayName: Download pydevd binaries
+ artifact: pydevd binaries
- - template: "templates/run_tests.yml"
+ # copy pydevd binaries
+ - task: CopyFiles@2
+ displayName: Copy pydevd binaries
+ inputs:
+ SourceFolder: $(Pipeline.Workspace)/pydevd binaries
+ TargetFolder: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)
- - job: "Test_MacOS"
+ - template: templates/run_tests.yml
+
+ - job: Tests_Mac
timeoutInMinutes: 30
- displayName: "Tests - macOS"
- pool: { vmImage: "macOS-11" }
+ displayName: Tests - macOS
+ pool:
+ vmImage: macOS-latest
strategy:
- matrix:
- py37:
- python.version: "3.7"
- py38:
- python.version: "3.8"
- py39:
- python.version: "3.9"
- py310:
- python.version: "3.10"
+ matrix:
+ py39:
+ python.version: 3.9
+ py310:
+ python.version: 3.10
+ py311:
+ python.version: 3.11
+ py312:
+ python.version: 3.12
+ py313:
+ python.version: 3.13
+ py314:
+ python.version: 3.14.0-rc.2
steps:
- - script: |
- ulimit -Sn 8192
- displayName: "Increase file descriptor limit"
- - template: "templates/use_python.yml"
+ - script: ulimit -Sn 8192
+ displayName: Increase file descriptor limit
- - script: |
- python -m ensurepip --user
- displayName: "Bootstrap pip"
+ - template: templates/use_python.yml
- - template: "templates/run_tests.yml"
+ - script: python -m ensurepip --user
+ displayName: Bootstrap pip
- - job: "Test_Windows"
+ # download pydevd binaries
+ - download: current
+ displayName: Download pydevd binaries
+ artifact: pydevd binaries
+
+ # copy pydevd binaries
+ - task: CopyFiles@2
+ displayName: Copy pydevd binaries
+ inputs:
+ SourceFolder: $(Pipeline.Workspace)/pydevd binaries
+ TargetFolder: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)
+
+ - template: templates/run_tests.yml
+
+ - job: Test_Windows
timeoutInMinutes: 40
- displayName: "Tests - Windows"
- pool: { vmImage: "windows-latest" }
+ displayName: Tests - Windows
+ pool:
+ vmImage: windows-latest
strategy:
- matrix:
- py37:
- python.version: "3.7"
- py38:
- python.version: "3.8"
- py39:
- python.version: "3.9"
- py310:
- python.version: "3.10"
- py39_32:
- python.version: "3.9"
- architecture: "x86"
+ matrix:
+ py39:
+ python.version: 3.9
+ py39_32:
+ python.version: 3.9
+ architecture: x86
+ py310:
+ python.version: 3.10
+ py311:
+ python.version: 3.11
+ py312:
+ python.version: 3.12
+ py313:
+ python.version: 3.13
+ py314:
+ python.version: 3.14.0-rc.2
steps:
- - template: "templates/use_python.yml"
- - template: "templates/run_tests.yml"
+ - template: templates/use_python.yml
+
+ # download pydevd binaries
+ - download: current
+ displayName: Download pydevd binaries
+ artifact: pydevd binaries
+
+ # copy pydevd binaries
+ - task: CopyFiles@2
+ displayName: Copy pydevd binaries
+ inputs:
+ SourceFolder: $(Pipeline.Workspace)/pydevd binaries
+ TargetFolder: $(Build.SourcesDirectory)/$(PYDEVD_ATTACH_TO_PROCESS)
+
+ - template: templates/run_tests.yml
diff --git a/azure-pipelines/exceptions.mdb b/azure-pipelines/policheck/exceptions.mdb
similarity index 99%
rename from azure-pipelines/exceptions.mdb
rename to azure-pipelines/policheck/exceptions.mdb
index ae74474f5..2168c8608 100644
Binary files a/azure-pipelines/exceptions.mdb and b/azure-pipelines/policheck/exceptions.mdb differ
diff --git a/azure-pipelines/policheck/exceptions.xml b/azure-pipelines/policheck/exceptions.xml
new file mode 100644
index 000000000..f158a9126
--- /dev/null
+++ b/azure-pipelines/policheck/exceptions.xml
@@ -0,0 +1,3 @@
+
+ _vendored\pydevd
+
diff --git a/azure-pipelines/templates/coverage.yml b/azure-pipelines/templates/coverage.yml
index 5c638bb80..47ab7e714 100644
--- a/azure-pipelines/templates/coverage.yml
+++ b/azure-pipelines/templates/coverage.yml
@@ -1,37 +1,35 @@
steps:
- - template: "use_python.yml"
+ - template: "use_python.yml"
- - script: |
- python -m pip install -U pip setuptools tox
- displayName: "Setup Python packages"
+ - script: "python -m pip install tox"
+ displayName: "Setup Python packages"
- - task: SonarCloudPrepare@1
- displayName: "Prepare analysis on SonarCloud"
- inputs:
- SonarCloud: "SonarCloud-debugpy"
- organization: microsoft
- scannerMode: CLI
- extraProperties: |
- sonar.python.coverage.reportPaths=$(Build.SourcesDirectory)/coverage/coverage.xml
- sonar.projectKey=microsoft_debugpy
+ - task: SonarCloudPrepare@1
+ displayName: "Prepare analysis on SonarCloud"
+ inputs:
+ SonarCloud: "SonarCloud-debugpy"
+ organization: microsoft
+ scannerMode: CLI
+ extraProperties: |
+ sonar.python.coverage.reportPaths=$(Build.SourcesDirectory)/coverage/coverage.xml
+ sonar.projectKey=microsoft_debugpy
- - script: |
- python -m tox --develop -e py38-cov -- --cov-report=html --cov-report=xml -vv
- displayName: "Coverage Run"
+ - script: "python -m tox --develop -e py311-cov -- --cov-report=html --cov-report=xml -vv"
+ displayName: "Coverage Run"
- - task: SonarCloudAnalyze@1
- displayName: "Run Code Analysis"
+ - task: SonarCloudAnalyze@1
+ displayName: "Run Code Analysis"
- - task: SonarCloudPublish@1
- displayName: "Publish Quality Gate Result"
- inputs:
- pollingTimeoutSec: "300"
+ - task: SonarCloudPublish@1
+ displayName: "Publish Quality Gate Result"
+ inputs:
+ pollingTimeoutSec: "300"
- - task: PublishCodeCoverageResults@1
- displayName: "Publish Coverage Report"
- inputs:
- codeCoverageTool: Cobertura
- summaryFileLocation: "$(Build.SourcesDirectory)/coverage/coverage.xml"
- reportDirectory: "$(Build.SourcesDirectory)/coverage/html"
- failIfCoverageEmpty: true
- condition: always()
+ - task: PublishCodeCoverageResults@1
+ displayName: "Publish Coverage Report"
+ inputs:
+ codeCoverageTool: Cobertura
+ summaryFileLocation: "$(Build.SourcesDirectory)/coverage/coverage.xml"
+ reportDirectory: "$(Build.SourcesDirectory)/coverage/html"
+ failIfCoverageEmpty: true
+ condition: always()
diff --git a/azure-pipelines/templates/run_tests.yml b/azure-pipelines/templates/run_tests.yml
index cf94dcf12..475b6ff91 100644
--- a/azure-pipelines/templates/run_tests.yml
+++ b/azure-pipelines/templates/run_tests.yml
@@ -1,28 +1,39 @@
steps:
- - script: |
- python -m pip install -U pip setuptools tox
- displayName: "Setup Python packages"
+ - script: python -m pip install tox
+ displayName: Setup Python packages
- - pwsh: |
- $toxEnv = '$(python.version)'
- if (-not $toxEnv.startsWith('pypy')) {
- $toxEnv = 'py' + $toxEnv.Replace('.', '')
- }
- echo 'tox environment: $toxEnv'
- python -m tox -e $toxEnv -- -vv --junitxml=$(Build.ArtifactStagingDirectory)/tests.xml --debugpy-log-dir=$(Build.ArtifactStagingDirectory)/logs tests
- displayName: "Run tests using tox"
+ - pwsh: |
+ $raw = '$(python.version)'
+ if ($raw.StartsWith('pypy')) {
+ # For PyPy keep original pattern stripping dots after first two numeric components if needed later.
+ $toxEnv = 'py' + ($raw -replace '^pypy(\d+)\.(\d+).*$','$1$2')
+ }
+ else {
+ # Extract major.minor even from prerelease like 3.14.0-rc.2 -> 3.14
+ $mm = [regex]::Match($raw,'^(\d+)\.(\d+)')
+ if (-not $mm.Success) { throw "Unable to parse python.version '$raw'" }
+ $toxEnv = 'py' + $mm.Groups[1].Value + $mm.Groups[2].Value
+ }
+ Write-Host "python.version raw: $raw"
+ Write-Host "Derived tox environment: $toxEnv"
+ python -m tox -e $toxEnv -- --junitxml=$(Build.ArtifactStagingDirectory)/tests.xml --debugpy-log-dir=$(Build.ArtifactStagingDirectory)/logs tests
+ displayName: Run tests using tox
+ env:
+ DEBUGPY_PROCESS_SPAWN_TIMEOUT: 60
+ DEBUGPY_LAUNCH_TIMEOUT: 60
- - task: "PublishBuildArtifacts@1"
- condition: "failed()"
- inputs:
- artifactName: "Test logs"
- pathToPublish: "$(Build.ArtifactStagingDirectory)/logs"
- displayName: "Publish test logs"
+ - task: PublishBuildArtifacts@1
+ displayName: Publish test logs
+ inputs:
+ artifactName: Test logs
+ pathToPublish: $(Build.ArtifactStagingDirectory)/logs
+ condition: failed()
+
+ - task: PublishTestResults@2
+ displayName: Publish test results
+ inputs:
+ testRunTitle: $(Agent.JobName)
+ testResultsFiles: tests.xml
+ searchFolder: $(Build.ArtifactStagingDirectory)
+ condition: always()
- - task: "PublishTestResults@2"
- condition: "always()"
- inputs:
- testRunTitle: "$(Agent.JobName)"
- testResultsFiles: "tests.xml"
- searchFolder: "$(Build.ArtifactStagingDirectory)"
- displayName: "Publish test results"
diff --git a/azure-pipelines/templates/use_python.yml b/azure-pipelines/templates/use_python.yml
index ef4c20f8a..ecb69656c 100644
--- a/azure-pipelines/templates/use_python.yml
+++ b/azure-pipelines/templates/use_python.yml
@@ -1,6 +1,7 @@
steps:
- - task: UsePythonVersion@0
- inputs:
- versionSpec: "$(python.version)"
- architecture: "$(architecture)"
- displayName: "Use Python $(python.version) $(architecture)"
+ - task: UsePythonVersion@0
+ inputs:
+ versionSpec: $(python.version)
+ architecture: $(architecture)
+ allowUnstable: true
+ displayName: Use Python $(python.version) $(architecture)
diff --git a/azure-pipelines/win-coverage.yaml b/azure-pipelines/win-coverage.yaml
index 682a4ec2f..ffeb23ba3 100644
--- a/azure-pipelines/win-coverage.yaml
+++ b/azure-pipelines/win-coverage.yaml
@@ -1,13 +1,31 @@
+# This pipeline is used to run unit test code coverage against debugpy public repo.
+
+# Trigger ci builds for commits into master and any release branches
+trigger:
+ branches:
+ include:
+ - main
+ - release/*
+
+# Trigger pr builds for commits into master and any release branches
+# Ignore draft PR's
+pr:
+ branches:
+ include:
+ - main
+ - release/*
+ drafts: "false"
+
variables:
- architecture: "x64"
- python.version: "3.8"
+ architecture: "x64"
+ python.version: "3.11"
jobs:
- - job: "Coverage"
- timeoutInMinutes: 25
- pool:
- name: Azure Pipelines
- demands: java
- vmImage: "windows-latest"
- steps:
- - template: "templates/coverage.yml"
+ - job: "Coverage"
+ timeoutInMinutes: "60"
+ pool:
+ name: Azure Pipelines
+ demands: java
+ vmImage: "windows-latest"
+ steps:
+ - template: "templates/coverage.yml"
diff --git a/build/git-subrepo/.gitattributes b/build/git-subrepo/.gitattributes
new file mode 100644
index 000000000..fcadb2cf9
--- /dev/null
+++ b/build/git-subrepo/.gitattributes
@@ -0,0 +1 @@
+* text eol=lf
diff --git a/build/git-subrepo/.gitrepo b/build/git-subrepo/.gitrepo
new file mode 100644
index 000000000..c35f6cfbb
--- /dev/null
+++ b/build/git-subrepo/.gitrepo
@@ -0,0 +1,12 @@
+; DO NOT EDIT (unless you know what you are doing)
+;
+; This subdirectory is a git "subrepo", and this file is maintained by the
+; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
+;
+[subrepo]
+ remote = https://github.com/ingydotnet/git-subrepo.git
+ branch = 0.4.1
+ commit = a04d8c2e55c31931d66b5c92ef6d4fe4c59e3226
+ parent = b341532bdd2eacaddca2d9c63421ef3ac9d3d239
+ method = merge
+ cmdver = 0.4.1
diff --git a/build/git-subrepo/.rc b/build/git-subrepo/.rc
new file mode 100644
index 000000000..2cb9eed3f
--- /dev/null
+++ b/build/git-subrepo/.rc
@@ -0,0 +1,38 @@
+#!bash
+
+#------------------------------------------------------------------------------
+#
+# This is the `git-subrepo` initialization script.
+#
+# This script turns on the `git-subrepo` Git subcommand, its manpages and TAB
+# completion for the *Bash* and *zsh* shells.
+#
+# Just add a line like this to your shell startup configuration:
+#
+# source /path/to/git-subrepo/.rc
+#
+#------------------------------------------------------------------------------
+
+if [[ ${BASH_VERSION} ]]; then
+ if [[ ${BASH_VERSINFO[0]} -lt 4 ]] ; then
+ echo "The git-subrepo command requires that 'Bash 4+' is installed."
+ echo "It doesn't need to be your shell, but it must be in your PATH."
+ if [[ $OSTYPE == darwin* ]]; then
+ echo "You appear to be on macOS."
+ echo "Try: 'brew install bash'."
+ echo "This will not change your user shell, it just installs 'Bash 5.x'."
+ fi
+ return
+ fi
+fi
+
+[[ -n ${ZSH_VERSION-} ]] &&
+ GIT_SUBREPO_ROOT=$0 ||
+ GIT_SUBREPO_ROOT=$BASH_SOURCE
+[[ $GIT_SUBREPO_ROOT =~ ^/ ]] ||
+ GIT_SUBREPO_ROOT=$PWD/$GIT_SUBREPO_ROOT
+export GIT_SUBREPO_ROOT=$(cd "$(dirname "$GIT_SUBREPO_ROOT")"; pwd)
+
+export PATH=$GIT_SUBREPO_ROOT/lib:$PATH
+export MANPATH=$GIT_SUBREPO_ROOT/man:$MANPATH
+source "$GIT_SUBREPO_ROOT/share/enable-completion.sh"
diff --git a/build/git-subrepo/.travis.yml b/build/git-subrepo/.travis.yml
new file mode 100644
index 000000000..41a305d25
--- /dev/null
+++ b/build/git-subrepo/.travis.yml
@@ -0,0 +1,31 @@
+language: shell
+
+matrix:
+ include:
+ - name: Git 2.21 on Ubuntu Xenial
+ dist: xenial
+ language: shell
+
+ # === This will need to removed when Travis drops support ===
+ - name: Git 2.15.1 on Ubuntu Trusty
+ dist: trusty
+ language: shell
+
+ - name: Git on Mac
+ os: osx
+ osx_image: xcode8.3
+ addons:
+ homebrew:
+ packages:
+ - bash
+ update: true
+
+script:
+# NOTE: we have to make sure we're on a branch (rather than on a detached HEAD)
+# because tests rely on it.
+- git checkout -b travis-ci-dummy-branch-name
+- GIT_COMMITTER_NAME=Bob
+ GIT_COMMITTER_EMAIL=bob@blob.net
+ GIT_AUTHOR_NAME='Bob Blob'
+ GIT_AUTHOR_EMAIL=bob@blob.net
+ PROVEOPT=-v make test
diff --git a/build/git-subrepo/Changes b/build/git-subrepo/Changes
new file mode 100644
index 000000000..18c4f7409
--- /dev/null
+++ b/build/git-subrepo/Changes
@@ -0,0 +1,110 @@
+---
+version: 0.4.1
+date: Thu Jan 9 17:11:21 CST 2020
+- Fix Bash version error messages and add to .rc
+- Nicer YAML formatting in .travis.yml
+- Wrap a long line
+- Update the docs
+- Force `make update` to always update docs
+- Don't use XXX in perl stuff
+- Add testing on MacOS
+- Remove conflicting -C from install -d commands.
+- Update version requirement documentation
+- Correct error message in branch
+- Use topo-order in subrepo branch
+- Make βgit subrepo clean -f ...β delete refs correctly
+- Fix #410 Push empty repositories with recent git versions
+- Make subrepo work when run in a worktree
+- Simplify finding subrepos
+- Ask git to find the .gitrepo files
+- Doc: fix sentence repetition
+- Fix typos
+- Fixed typo
+- Travis CI not checking out a branch.
+---
+version: 0.4.0
+date: Thu Nov 8 12:26:38 CET 2018
+changes:
+- Fix #325 Do not squash main repo commits
+- Improve error message for worktree handling
+- Make version checking portable. #307
+- #307, improve version check
+- #307, update version requirement
+- Fix part #308, Add stderr output if commands fail
+- Fix #306: Add check to prevent following commits with no .gitrepo
+- Remove dry-run flag as it's not implemented. Make sure branch --force delete worktree
+- Fix #296, Replace --first-parent with --ancestry-path
+- Fix #291, specify Trusty host to get new 2.x git
+- Fix #258, add --no-tags to git fetch
+- Test that no remotes are created during clone
+- #257 Remove remote creation to avoid problems with fetch --all
+- (origin/issue/150_to_0.4.0) Fix remove-worktree, remove unused parameters
+- Regenerate completion files
+- filter-branch operation should not be done on HEAD
+- Cleanup push and add hint to push directly after pull
+- Simplify cleanup and add worktree to status
+- Add --method option to init/clone, add a 'config' command
+- Updated unit tests to support the new logic
+- Use 'git worktree' for merge/rebase
+- Update docs to reflect how things should work
+- Make it possible to specify commit messages
+- Redesign, trash the tree hash approach and use merges instead
+- Add release branches to travis-ci
+- Add --method option to init/clone, add a 'config' command
+- Detect multiple pulls, use -u flag to decide
+- Don't reuse previous commit message when using --all
+- Update the docs for pull and push
+- Update error messages when failing merge/rebase
+- Fix env var bug in test/push.t
+- Do not overwrite author information
+---
+version: 0.3.1
+date: Tue Jan 3 23:08:56 PST 2017
+changes:
+- Updated release for homebrew
+- Fix #192
+---
+version: 0.3.0
+date: Wed Dec 2 19:19:43 PST 2015
+changes:
+- Fix issue #98 and host of others (89, 91, 95, 96)
+- Adds support for the merge-base command
+- Adds stability to many commands
+- Command completion updates
+- Rename `init` to `.rc`
+- @grimmySwe++ @dzzh++ @jrosdahl++ @perlpunk++
+---
+version: 0.2.3
+date: Sun Aug 9 13:44:22 PDT 2015
+changes:
+- Fix issues #75 and #76
+---
+version: 0.2.2
+date: Wed Jul 22 09:45:13 PDT 2015
+changes:
+- Added the `init` subcommand
+- Applied doc fixes
+---
+version: 0.2.1
+date: Sat Mar 28 07:52:22 PDT 2015
+changes:
+- Allows subrepo clone to clone to an empty branch; fixes #26.
+- Refs in status
+- Empty parent set to 'none' in .gitrepo file.
+- Bug fixes
+---
+version: 0.2.0
+date: Sat Jan 24 06:22:05 PST 2015
+changes:
+- Massive overhaul
+- .gitrepo files remain the same so backwards compatible
+- Introduce the branch and commit subcommands
+- The checkout subcommand goes away
+- Operations work much smoother like normal Git flow
+- Much more testing
+- Better doc
+---
+version: 0.1.0
+date: Fri Feb 21 12:25:53 2014 -0800
+changes:
+- First version
diff --git a/build/git-subrepo/Intro.pod b/build/git-subrepo/Intro.pod
new file mode 100644
index 000000000..d60469d6b
--- /dev/null
+++ b/build/git-subrepo/Intro.pod
@@ -0,0 +1,509 @@
+=pod
+
+=for comment
+DO NOT EDIT. This Pod was generated by Swim v0.1.48.
+See http://github.com/ingydotnet/swim-pm#readme
+
+=encoding utf8
+
+=head1 Introducing Git Subrepos
+
+There is a new git command called C that is meant to be a solid
+alternative to the C and C commands. All 3 of these
+commands allow you to include external repositories (pinned to specific
+commits) in your main repository. This is an often needed feature for project
+development under a source control system like Git. Unfortunately, the
+C command is severely lacking, and the C command (an
+attempt to make things better) is also very flawed. Fortunately, the
+C command is here to save the day.
+
+This article will discuss how the previous commands work, and where they go
+wrong, while explaining how the new C command fixes the issues.
+
+It should be noted that there are 3 distinct roles (ways people use repos)
+involved in discussing this topic:
+
+=over
+
+=item * B β The primary author and repo owner
+
+=item * B β Other developers who contribute to the repo
+
+=item * B β People who simply use the repo software
+
+=back
+
+=head2 Introducing C
+
+While the main point is to show how subrepo addresses the shortcomings
+of submodule and subtree, I'll start by giving a quick intro to the
+subrepo command.
+
+Let's say that you have a project repo called 'freebird' and you want to have
+it include 2 other external repos, 'lynyrd' and 'skynyrd'. You would do the
+following:
+
+ git clone git@github.com/you/freebird
+ cd freebird
+ git subrepo clone git@github.com/you/lynyrd ext/lynyrd
+ git subrepo clone git@github.com/you/skynyrd ext/skynyrd --branch=1975
+
+What these commands do (at a high level) should be obvious. They "clone" (add)
+the repos content into the subdirectories you told them to. The details of
+what is happening to your repo will be discussed later, but adding new
+subrepos is easy. If you need to update the subrepos later:
+
+ git subrepo pull ext/lynyrd
+ git subrepo pull ext/skynyrd --branch=1976
+
+The lynyrd repo is tracking the upstream master branch, and you've changed the
+skynyrd subrepo to the 1976 branch. Since these subrepos are owned by 'you',
+you might want to change them in the context of your freebird repo. When
+things are working, you can push the subrepo changes back:
+
+ git subrepo push ext/lynyrd
+ git subrepo push ext/skynyrd
+
+Looks simple right? It's supposed to be. The intent of C is to do the
+right things, and to not cause problems.
+
+Of course there's more to it under the hood, and that's what the rest of this
+article is about.
+
+=head2 Git Submodules
+
+Submodules tend to receive a lot of bad press. Here's some of it:
+
+=over
+
+=item * L
+
+=item * L
+
+=item * L
+
+=back
+
+A quick recap of some of the good and bad things about submodules:
+
+Good:
+
+=over
+
+=item * Use an external repo in a dedicated subdir of your project.
+
+=item * Pin the external repo to a specific commit.
+
+=item * The C command is a core part of the Git project.
+
+=back
+
+Bad:
+
+=over
+
+=item * Users have to know a repo has submodules.
+
+=item * Users have to get the subrepos manually.
+
+=item * Pulling a repo with submodules won't pull in the new submodule changes.
+
+=item * A submodule will break if the referenced repo goes away.
+
+=item * A submodule will break if a forced push removes the referenced commit.
+
+=item * Can't use different submodules/commits per main project branch.
+
+=item * Can't "try out" a submodule on alternate branch.
+
+=item * Main repo can be pushed upstream pointing to unpushed submod commits.
+
+=item * Command capability differs across Git versions.
+
+=item * Often need to change remote url, to push submodule changes upstream.
+
+=item * Removing or renaming a submodule requires many steps.
+
+=back
+
+Internally, submodules are a real mess. They give the strong impression of
+being bolted on, well after Git was designed. Some commands are aware of the
+existence of submodules (although usually half-heartedly), and many commands
+are oblivious. For instance the git-clone command has a C<--recursive> option
+to clone all subrepos, but it's not a default, so you still need to be aware
+of the need. The git-checkout command does nothing with the submodules, even
+if they are intended to differ across branches.
+
+Let's talk a bit about how submodules are implemented in Git. Information
+about them is stored in 3 different places (in the top level repo directory):
+
+=over
+
+=item * C<.gitmodules>
+
+=item * C<.git/config>
+
+=item * C<.git/modules> β The submodule repo's meta data (refs/objects)
+
+=back
+
+So some of the information lives in the repo history (.gitmodules), but other
+info (.git/) is only known to the local repo.
+
+In addition, the submodule introduces a new low level concept, to the
+commitIblob graph. Normally a git tree object points to blob (file)
+objects and more tree (directory) objects. Submodules have tree objects point
+to B objects. While this seems clever and somewhat reasonable, it also
+means that every other git command (which was built on the super clean Git
+data model) has to be aware of this new possibility (and deal with it
+appropriately).
+
+The point is that, while submodules are a real need, and a lot of work has
+gone into making them work decently, they are essentially a kludge to the Git
+model, and it is quite understandable why they haven't worked out as well as
+people would expect.
+
+NOTE: Submodules I getting better with each release of Git, but it's
+ still an endless catch up game.
+
+=head2 Git Subtrees
+
+One day, someone decided to think different. Instead of pointing to external
+repos, why not just include them into the main repo (but also allow them to be
+pulled and pushed separately as needed)?
+
+At first this may feel like a wasteful approach. Why keep other repos
+physically inside your main one? But if you think about it abstractly, what's
+the difference? You want your users and collaborators to have all this code
+because your project needs it. So why worry about how it happens? In the end,
+the choice is yours, but I've grown very comfortable with this concept and
+I'll try to justify it well. I should note that the first paragraph of the
+C doc suggests considering this alternative.
+
+The big win here is that you can do this using the existing git model. Nothing
+new is added. You are just adding commits to a history. You can do it
+different on every branch. You can merge branches sensibly.
+
+The git-subtree command seems to have been inspired by Git's subtree merge
+strategy, which it uses internally, and possibly got its name from. A subtree
+merge allows you to take a completely separate Git history and make it be a
+subdirectory of your repo.
+
+Adding a subtree was the easy part. All that needed to be done after that was
+to figure out a way to pull upstream changes and push local ones back
+upstream. And that's what the C command does.
+
+So what's the problem with git-subtree then?
+
+Well unfortunately, it drops a few balls. The main problems come down to an
+overly complicated commandline UX, poor collaborator awareness, and a fragile
+and messy implementation.
+
+Good:
+
+=over
+
+=item * Use an external repo in a dedicated subdir of your project.
+
+=item * Pin the external repo to a specific commit.
+
+=item * Users get everything with a normal clone command.
+
+=item * Users don't need to know that subtrees are involved.
+
+=item * Can use different submodules/commits per main project branch.
+
+=item * Users don't need the subtree command. Only owners and collaborators.
+
+=back
+
+Bad:
+
+=over
+
+=item * The remote url and branch info is not saved (except in the history).
+
+=item * Owners and collaborators have to enter the remote for every command.
+
+=item * Collaborators aren't made aware that subtrees are involved.
+
+=item * Pulled history is not squashed by default.
+
+=item * Creates a messy historical view. (See below)
+
+=item * Bash code is complicated.
+
+=item * Only one test file. Currently is failing.
+
+=back
+
+As you can see, subtree makes quite a few things better, but after trying it
+for a while, the experience was more annoying than submodules. For example,
+consider this usage:
+
+ $ git subtree add --squash --prefix=foo git@github.com:my/thing mybranch
+ # weeks go byβ¦
+ $ git subtree pull --squash --prefix=foo git@github.com:my/thing mybranch
+ # time to push local subtree changes back upstream
+ $ git subtree push --prefix=foo git@github.com:my/thing mybranch
+
+The first thing you notice is the overly verbose syntax. It's justified in the
+first command, but in the other 2 commands I really don't want to have to
+remember what the remote and branch are that I'm using.
+
+Moreover, my collaborators have no idea that subtrees are involved, let alone
+where they came from.
+
+Consider the equivalent subrepo commands:
+
+ $ git subrepo clone git@github.com:my/thing foo -b mybranch
+ $ git subrepo pull foo
+ $ git subrepo push foo
+
+Collaborators see a file called 'foo/.gitrepo', and know that the subdir is a
+subrepo. The file contains all the information needed by future commands
+applied to that subrepo.
+
+=head2 Git Subrepos
+
+Now is a good time to dive into the techinical aspects of the C
+command, but first let me explain how it came about.
+
+As you may have surmised by now, I am the author of git-subrepo. I'd used
+submodules on and off for years, and when I became aware of subtree I gave it
+a try, but I quickly realized its problems. I decided maybe it could be
+improved. I decided to write down my expected commandline usage and my ideals
+of what it would and would not do. Then I set off to implement it. It's been a
+long road, but what I ended up with was even better than what I wanted from
+the start.
+
+Let's review the Goods and Bads:
+
+Good:
+
+=over
+
+=item * Use an external repo in a dedicated subdir of your project.
+
+=item * Pin the external repo to a specific commit.
+
+=item * Users get everything with a normal clone command.
+
+=item * Users don't need to know that subrepos are involved.
+
+=item * Can use different submodules/commits per main project branch.
+
+=item * Meta info is kept in an obvious place.
+
+=item * Everyone knows when a subdir is a subrepo.
+
+=item * Commandline UX is minimal and intuitive.
+
+=item * Pulled history is always squashed out locally.
+
+=item * Pushed history is kept intact.
+
+=item * Creates a clean historical view. (See below)
+
+=item * Bash code is very simple and easy to follow.
+
+=item * Comprehensive test suite. Currently passing on travis:
+
+=back
+
+=for html
+
+
+Bad:
+
+=over
+
+=item * --Subrepo is very new.-- (no longer true)
+
+=item * --Not well tested in the wild.-- (no longer true)
+
+=back
+
+This review may seem somewhat slanted, but I honestly am not aware of any
+"bad" points that I'm not disclosing. That said, I am sure time will reveal
+bugs and shortcomings. Those can usually be fixed. Hopefully the B is
+correct, because that's harder to fix down the road.
+
+OK. So how does it all work?
+
+There are 3 main commands: cloneIpush. Let's start with the clone
+command. This is the easiest part. You give it a remote url, possibly a new
+subdir to put it, and possibly a remote branch to use. I say possibly, because
+the command can guess the subdir name (just like the git-clone command does),
+and the branch can be the upstream default branch.
+
+Given this we do the following steps internally:
+
+=over
+
+=item * Fetch the remote content (for a specific refspec)
+
+=item * Read the remote head tree into the index
+
+=item * Checkout the index into the new subdir
+
+=item * Create a new subrepo commit object for the subdir content
+
+=item * Add a state file called .gitrepo to the new subrepo/subdir
+
+=item * Amend the merge commit with this new file
+
+=back
+
+This process adds something like this to the top of your history:
+
+ * 9b6ddc9 git subrepo clone git@github.com:you/foo.git foo/
+ * 37c61a5 Previous head commit of your repo
+
+The entire history has been squashed down into one commit, and placed on
+top of your history. This is important as it keeps your history as clean
+as possible. You don't need to have the subrepo history in your main
+project, since it is immutably available elsewhere, and you have a pointer
+to that place.
+
+The new foo/.gitrepo file looks like this:
+
+ [subrepo]
+ remote = git@github.com:you/foo.git
+ branch = master
+ commit = 14c96c6931b41257b2d42b2edc67ddc659325823
+ parent = 37c61a5a234f5dd6f5c2aec037509f50d3a79b8f
+ cmdver = 0.1.0
+
+It contains all the info needed now and later. Note that the repo url is the
+generally pushable form, rather than the publically readable (L
+form. This is the best practice. Users of your repo don't need access to this
+url, because the content is already in your repo. Only you and your
+collaborators need this url to pull/push in the future.
+
+The next command is the pull command. Normally you just give it the subrepo's
+subdir path (although you can change the branch with -b), and it will get the
+other info from the subdir/.gitrepo file.
+
+The pull command does these steps:
+
+=over
+
+=item * Fetch the upstream content
+
+=item * Check if anything needs pulling
+
+=item * Create a branch of local subrepo commits since last pull
+
+=item * Rebase this branch onto the upstream commits
+
+=item * Commit the HEAD of the rebased content
+
+=item * Update/amend the .gitrepo file
+
+=back
+
+=head3 Clean History
+
+I've talked a bit about clean history but let me show you a comparison between
+subrepo and subtree. Let's run this command sequence using both methods. Note
+the differences between I the command syntax required, and the branch
+history produced.
+
+Subrepo first:
+
+ $ git subrepo clone git@github.com:user/abc
+ $ git subrepo clone git@github.com:user/def xyz
+ $ git subrepo pull abc
+ $ git subrepo pull xyz
+
+The resulting history is:
+
+ * b1f60cc subrepo pull xyz
+ * 4fb0276 subrepo pull abc
+ * bcef2a0 subrepo clone git@github.com:user/def xyz
+ * bebf0db subrepo clone git@github.com:user/abc
+ * 64eeaa6 (origin/master, origin/HEAD) O HAI FREND
+
+Compare that to B. This:
+
+ $ git subtree add abc git@github.com:user/abc master
+ $ git subtree add xyz git@github.com:user/def master
+ $ git subtree pull abc git@github.com:user/abc master
+ $ git subtree pull xyz git@github.com:user/def master
+
+Produces this:
+
+ * 739e45a (HEAD, master) Merge commit '5f563469d886d53e19cb908b3a64e4229f88a2d1'
+ |\
+ | * 5f56346 Squashed 'xyz/' changes from 08c7421..365409f
+ * | 641f5e5 Merge commit '8d88e90ce5f653ed2e7608a71b8693a2174ea62a'
+ |\ \
+ | * | 8d88e90 Squashed 'abc/' changes from 08c7421..365409f
+ * | | 1703ed2 Merge commit '0e091b672c4bbbbf6bc4f6694c475d127ffa21eb' as 'xyz'
+ |\ \ \
+ | | |/
+ | |/|
+ | * | 0e091b6 Squashed 'xyz/' content from commit 08c7421
+ | /
+ * | 07b77e7 Merge commit 'cd2b30a0229d931979ed4436b995875ec563faea' as 'abc'
+ |\ \
+ | |/
+ | * cd2b30a Squashed 'abc/' content from commit 08c7421
+ * 64eeaa6 (origin/master, origin/HEAD) O HAI FREND
+
+This was from a minimal case. Subtree history (when viewed this way at least)
+gets unreasonably ugly fast. Subrepo history, by contrast, always looks as
+clean as shown.
+
+The final command, push, bascially just does the pull/rebase dance above
+described, and pushes the resulting history back. It does not squash the
+commits made locally, because it assumed that when you changed the local
+subrepo, you made messages that were intended to eventually be published
+back upstream.
+
+=head2 Conflict Resolution
+
+The commands described above can also be done "by hand". If something fails
+during a pull or push (generally in the rebasing) then the command will tell
+you what to do to finish up.
+
+You might choose to do everything by hand, and do your own merging strategies.
+This is perfectly reasonable. The C command offers a few other helper
+commands to help you get the job done:
+
+=over
+
+=item * C - Fetch the upstream and create a C<< subrepo/remote/ >> ref.
+
+=item * C - Create a branch of local subdir commits since the last pull, called C<< subrepo/ >>.
+
+=item * C - Commit a merged branch's HEAD back into your repo.
+
+=item * C - Show lots of useful info about the current state of the subrepos.
+
+=item * C - Remove branches, ref and remotes created by subrepo commands.
+
+=item * C - Read the complete documentation!
+
+=back
+
+=head2 Conclusion
+
+Hopefully by now, you see that submodules are a painful choice with a dubious
+future, and that subtree, while a solid idea has many usage issues.
+
+Give C a try. It's painless, easily revertable and just might be what
+the doctor ordered.
+
+=head2 Reference Links
+
+=over
+
+=item * L
+
+=item * L
+
+=back
+
+=cut
diff --git a/build/git-subrepo/License b/build/git-subrepo/License
new file mode 100644
index 000000000..8cf5dea0a
--- /dev/null
+++ b/build/git-subrepo/License
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2020 Ingy dΓΆt Net
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/build/git-subrepo/Makefile b/build/git-subrepo/Makefile
new file mode 100644
index 000000000..989f7a09d
--- /dev/null
+++ b/build/git-subrepo/Makefile
@@ -0,0 +1,82 @@
+# Make sure we have 'git' and it works OK:
+ifeq ($(shell which git),)
+ $(error 'git' is not installed on this system)
+endif
+
+# Set variables:
+NAME := git-subrepo
+LIB := lib/$(NAME)
+DOC := doc/$(NAME).swim
+MAN1 := man/man1
+EXT := $(LIB).d
+EXTS := $(shell find $(EXT) -type f) \
+ $(shell find $(EXT) -type l)
+SHARE = share
+
+# Install variables:
+PREFIX ?= /usr/local
+INSTALL_LIB ?= $(DESTDIR)$(shell git --exec-path)
+INSTALL_EXT ?= $(INSTALL_LIB)/$(NAME).d
+INSTALL_MAN1 ?= $(DESTDIR)$(PREFIX)/share/man/man1
+
+# Basic targets:
+default: help
+
+help:
+ @echo 'Makefile rules:'
+ @echo ''
+ @echo 'test Run all tests'
+ @echo 'install Install $(NAME)'
+ @echo 'uninstall Uninstall $(NAME)'
+ @echo 'env Show environment variables to set'
+
+.PHONY: test
+test:
+ prove $(PROVEOPT:%=% )test/
+
+# Install support:
+install:
+ install -d -m 0755 $(INSTALL_LIB)/
+ install -C -m 0755 $(LIB) $(INSTALL_LIB)/
+ install -d -m 0755 $(INSTALL_EXT)/
+ install -C -m 0755 $(EXTS) $(INSTALL_EXT)/
+ install -d -m 0755 $(INSTALL_MAN1)/
+ install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/
+
+# Uninstall support:
+uninstall:
+ rm -f $(INSTALL_LIB)/$(NAME)
+ rm -fr $(INSTALL_EXT)
+ rm -f $(INSTALL_MAN1)/$(NAME).1
+
+env:
+ @echo "export PATH=\"$$PWD/lib:\$$PATH\""
+ @echo "export MANPATH=\"$$PWD/man:\$$MANPATH\""
+
+# Doc rules:
+.PHONY: doc
+update: doc compgen
+
+force:
+
+doc: ReadMe.pod Intro.pod $(MAN1)/$(NAME).1
+ perl pkg/bin/generate-help-functions.pl $(DOC) > \
+ $(EXT)/help-functions.bash
+
+ReadMe.pod: $(DOC) force
+ swim --to=pod --wrap --complete $< > $@
+
+Intro.pod: doc/intro-to-subrepo.swim force
+ swim --to=pod --wrap --complete $< > $@
+
+$(MAN1)/%.1: doc/%.swim Makefile force
+ swim --to=man --wrap $< > $@
+
+compgen: force
+ perl pkg/bin/generate-completion.pl bash $(DOC) $(LIB) > \
+ $(SHARE)/completion.bash
+ perl pkg/bin/generate-completion.pl zsh $(DOC) $(LIB) > \
+ $(SHARE)/zsh-completion/_git-subrepo
+
+clean purge:
+ rm -fr tmp
diff --git a/build/git-subrepo/Meta b/build/git-subrepo/Meta
new file mode 100644
index 000000000..73b7d997f
--- /dev/null
+++ b/build/git-subrepo/Meta
@@ -0,0 +1,28 @@
+=meta: 0.0.2
+
+name: git-subrepo
+version: 0.4.1
+abstract: Git Submodule Alternative
+homepage: https://github.com/ingydotnet/git-subrepo#readme
+license: MIT
+copyright: 2013-2020
+
+author:
+ name: Ingy dΓΆt Net
+ email: ingy@ingy.net
+ github: ingydotnet
+ twitter: ingydotnet
+ freenode: ingy
+ homepage: http://ingy.net
+
+requires:
+ bash: 4.0.0
+ git: 2.7.0
+test:
+ cmd: make test
+install: make install
+
+devel:
+ git: git@github.org:ingydotnet/git-subrepo.git
+ irc: irc.freenode.net/gitcommands
+ bug: https://github.com/ingydotnet/git-subrepo/issues/
diff --git a/build/git-subrepo/ReadMe.pod b/build/git-subrepo/ReadMe.pod
new file mode 100644
index 000000000..c850a5c16
--- /dev/null
+++ b/build/git-subrepo/ReadMe.pod
@@ -0,0 +1,698 @@
+=pod
+
+=for comment
+DO NOT EDIT. This Pod was generated by Swim v0.1.48.
+See http://github.com/ingydotnet/swim-pm#readme
+
+=encoding utf8
+
+=head1 Name
+
+git-subrepo - Git Submodule Alternative
+
+=for html
+
+
+=head1 Synopsis
+
+ git subrepo -h # Help Overview
+
+ git subrepo clone []
+ git subrepo init
+ git subrepo pull
+ git subrepo push
+
+ git subrepo fetch
+ git subrepo branch
+ git subrepo commit
+ git subrepo config
+
+ git subrepo status []
+ git subrepo clean
+
+ git subrepo help [ | --all]
+ git subrepo version
+ git subrepo upgrade
+
+=head1 Description
+
+This git command "clones" an external git repo into a subdirectory of your
+repo. Later on, upstream changes can be pulled in, and local changes can be
+pushed back. Simple.
+
+=head1 Benefits
+
+This command is an improvement from C and C; two
+other git commands with similar goals, but various problems.
+
+It assumes there are 3 main roles of people interacting with a repo, and
+attempts to serve them all well:
+
+=over
+
+=item * B - The person who authors/owns/maintains a repo.
+
+=item * B - People who are just using/installing the repo.
+
+=item * B - People who commit code to the repo and subrepos.
+
+=back
+
+The C command benefits these roles in the following ways:
+
+=over
+
+=item * Simple and intuitive commandline usage (with tab completion).
+
+=item * Users get your repo and all your subrepos just by cloning your repo.
+
+=item * Users do not need to install C, ever.
+
+=item * Collaborators do not need to install unless they want to push/pull.
+
+=item * Collaborators know when a subdir is a subrepo (it has a C<.gitrepo> file).
+
+=item * The C<.gitrepo> file never gets pushed back to the subrepo upstream.
+
+=item * Well named branches and remotes are generated for manual operations.
+
+=item * Owners do not deal with the complications of keeping submodules in sync.
+
+=item * Subrepo repositories can contain subrepos themselves.
+
+=item * Branching with subrepos JustWorksβ’.
+
+=item * Different branches can have different subrepos in different states, etc.
+
+=item * Moving/renaming/deleting a subrepo subdir JustWorksβ’.
+
+=item * You can C an existing subdirectory into a subrepo.
+
+=item * Your git history is kept squeaky clean.
+
+=item * Upstream history (clone/pull) is condensed into a single commit.
+
+=item * Pulls can use a C, C or C strategies.
+
+=item * You can see the subrepo history with C<< git log subrepo//fetch >>.
+
+=item * Commits pushed back upstream are B condensed (by default).
+
+=item * Trivial to try any subrepo operations and then reset back.
+
+=item * No configuration required.
+
+=item * Does not introduce history that messes up other git commands.
+
+=item * Fixes known rebase failures with C.
+
+=back
+
+=head1 Installation
+
+The best short answer is:
+
+ git clone https://github.com/ingydotnet/git-subrepo /path/to/git-subrepo
+ echo 'source /path/to/git-subrepo/.rc' >> ~/.bashrc
+
+The complete "Installation Instructions" can be found below.
+
+Note: git-subrepo needs a git version (> 2.7) that supports worktree:s.
+
+=head1 Commands
+
+All the B commands use names of actual Git commands and try to do
+operations that are similar to their Git counterparts. They also attempt to
+give similar output in an attempt to make the subrepo usage intuitive to
+experienced Git users.
+
+Please note that the commands are I exact equivalents, and do not take
+all the same arguments. Keep readingβ¦
+
+=over
+
+=item C<< git subrepo clone [] [-b ] [-f] [-m ] [-e] [--method ] >>
+
+Add a repository as a subrepo in a subdir of your repository.
+
+This is similar in feel to C. You just specify the remote repo url,
+and optionally a sub-directory and/or branch name. The repo will be fetched
+and merged into the subdir.
+
+The subrepo history is I into a single commit that contains the
+reference information. This information is also stored in a special file
+called C<< /.gitrepo >>. The presence of this file indicates that the
+directory is a subrepo.
+
+All subsequent commands refer to the subrepo by the name of the
+I. From the subdir, all the current information about the subrepo
+can be obtained.
+
+The C<--force> option will "reclone" (completely replace) an existing subdir.
+
+The C<--method> option will decide how the join process between branches are
+performed. The default option is merge.
+
+The C command accepts the C<--branch=> C<--edit>, C<--force> and C<--
+message=> options.
+
+=item C<< git subrepo init [-r ] [-b ] [--method ] >>
+
+Turn an existing subdirectory into a subrepo.
+
+If you want to expose a subdirectory of your project as a published subrepo,
+this command will do that. It will split out the content of a normal
+subdirectory into a branch and start tracking it as a subrepo. Afterwards your
+original repo will look exactly the same except that there will be a C<<
+/.gitrepo >> file.
+
+If you specify the C<--remote> (and optionally the C<--branch>) option, the
+values will be added to the C<< /.gitrepo >> file. The C<--remote>
+option is the upstream URL, and the C<--branch> option is the upstream branch
+to push to. These values will be needed to do a C command,
+but they can be provided later on the C command (and saved to C<<
+/.gitrepo >> if you also specify the C<--update> option).
+
+Note: You will need to create the empty upstream repo and push to it on your
+ own, using C<< git subrepo push >>.
+
+The C<--method> option will decide how the join process between branches are
+performed. The default option is merge.
+
+The C command accepts the C<--branch=> and C<--remote=> options.
+
+=item C<< git subrepo pull |--all [-M|-R|-f] [-m ] [-e] [-b ] [-r ] [-u] >>
+
+Update the subrepo subdir with the latest upstream changes.
+
+The C command fetches the latest content from the remote branch pointed
+to by the subrepo's C<.gitrepo> file, and then tries to merge the changes into
+the corresponding subdir. It does this by making a branch of the local commits
+to the subdir and then merging or rebasing (see below) it with the fetched
+upstream content. After the merge, the content of the new branch replaces your
+subdir, the C<.gitrepo> file is updated and a single 'pull' commit is added to
+your mainline history.
+
+The C command will attempt to do the following commands in one go:
+
+ git subrepo fetch
+ git subrepo branch
+ git merge/rebase subrepo//fetch subrepo/
+ git subrepo commit
+ # Only needed for a consequential push:
+ git update-ref refs/subrepo//pull subrepo/
+
+In other words, you could do all the above commands yourself, for the same
+effect. If any of the commands fail, subrepo will stop and tell you to finish
+this by hand. Generally a failure would be in the merge or rebase part, where
+conflicts can happen. Since Git has lots of ways to resolve conflicts to your
+personal tastes, the subrepo command defers to letting you do this by hand.
+
+When pulling new data, the method selected in clone/init is used. This has no
+effect on the final result of the pull, since it becomes a single commit. But
+it does affect the resulting C<< subrepo/ >> branch, which is often
+used for a subrepo C command. See 'push' below for more information. If
+you want to change the method you can use the C command for this.
+
+When you pull you can assume a fast-forward strategy (default) or you can
+specify a C<--rebase>, C<--merge> or C<--force> strategy. The latter is the
+same as a C operation, using the current remote and branch.
+
+Like the C command, C will squash all the changes (since the last
+pull or clone) into one commit. This keeps your mainline history nice and
+clean. You can easily see the subrepo's history with the C command:
+
+ git log refs/subrepo//fetch
+
+The set of commands used above are described in detail below.
+
+The C command accepts the C<--all>, C<--branch=>, C<--edit>, C<--force>,
+C<--message=>, C<--remote=> and C<--update> options.
+
+=item C<< git subrepo push |--all [] [-r ] [-b ] [-M|-R] [-u] [-f] [-s] [-N] >>
+
+Push a properly merged subrepo branch back upstream.
+
+This command takes the subrepo branch from a successful pull command and
+pushes the history back to its designated remote and branch. You can also use
+the C command and merge things yourself before pushing if you want to
+(although that is probably a rare use case).
+
+The C command requires a branch that has been properly merged/rebased
+with the upstream HEAD (unless the upstream HEAD is empty, which is common
+when doing a first C after an C). That means the upstream HEAD is
+one of the commits in the branch.
+
+By default the branch ref C<< refs/subrepo//pull >> will be pushed,
+but you can specify a (properly merged) branch to push.
+
+After that, the C command just checks that the branch contains the
+upstream HEAD and then pushes it upstream.
+
+The C<--force> option will do a force push. Force pushes are typically
+discouraged. Only use this option if you fully understand it. (The C<--force>
+option will NOT check for a proper merge. ANY branch will be force pushed!)
+
+The C command accepts the C<--all>, C<--branch=>, C<--dry-run>, C<--
+force>, C<--merge>, C<--rebase>, C<--remote=>, C<--squash> and C<--
+update> options.
+
+=item C<< git subrepo fetch |--all [-r ] [-b ] >>
+
+Fetch the remote/upstream content for a subrepo.
+
+It will create a Git reference called C<< subrepo//fetch >> that
+points at the same commit as C. It will also create a remote
+called C<< subrepo/ >>. These are temporary and you can easily remove
+them with the subrepo C command.
+
+The C command accepts the C<--all>, C<--branch=> and C<--
+remote=> options.
+
+=item C<< git subrepo branch |--all [-f] [-F] >>
+
+Create a branch with local subrepo commits.
+
+Scan the history of the mainline for all the commits that affect the C
+and create a new branch from them called C<< subrepo/ >>.
+
+This is useful for doing C and C commands by hand.
+
+Use the C<--force> option to write over an existing C<< subrepo/
+>> branch.
+
+The C command accepts the C<--all>, C<--fetch> and C<--force> options.
+
+=item C<< git subrepo commit [] [-m ] [-e] [-f] [-F] >>
+
+Add subrepo branch to current history as a single commit.
+
+This command is generally used after a hand-merge. You have done a C and merged (rebased) it with the upstream. This command takes the HEAD
+of that branch, puts its content into the subrepo subdir and adds a new commit
+for it to the top of your mainline history.
+
+This command requires that the upstream HEAD be in the C<< subrepo/ >>
+branch history. That way the same branch can push upstream. Use the C<--force>
+option to commit anyway.
+
+The C command accepts the C<--edit>, C<--fetch>, C<--force> and C<--
+message=> options.
+
+=item C<< git subrepo status [|--all|--ALL] [-F] [-q|-v] >>
+
+Get the status of a subrepo. Uses the C<--all> option by default. If the C<--
+quiet> flag is used, just print the subrepo names, one per line.
+
+The C<--verbose> option will show all the recent local and upstream commits.
+
+Use C<--ALL> to show the subrepos of the subrepos (ie the
+"subsubrepos"), if any.
+
+The C command accepts the C<--all>, C<--ALL>, C<--fetch>, C<--quiet>
+and C<--verbose> options.
+
+=item C<< git subrepo clean |--all|--ALL [-f] >>
+
+Remove artifacts created by C and C commands.
+
+The C and C operations (and other commands that call them)
+create temporary things like refs, branches and remotes. This command removes
+all those things.
+
+Use C<--force> to remove refs. Refs are not removed by default because they
+are sometimes needed between commands.
+
+Use C<--all> to clean up after all the current subrepos. Sometimes you might
+change to a branch where a subrepo doesn't exist, and then C<--all> won't find
+it. Use C<--ALL> to remove any artifacts that were ever created by subrepo.
+
+To remove ALL subrepo artifacts:
+
+ git subrepo clean --ALL --force
+
+The C command accepts the C<--all>, C<--ALL>, and C<--force> options.
+
+=item C<< git subrepo config