pax_global_header00006660000000000000000000000064152131721650014515gustar00rootroot0000000000000052 comment=d86662e5e517a7f5c90dddfd9931daefbd37e522 libgit2-pygit2-d86662e/000077500000000000000000000000001521317216500146335ustar00rootroot00000000000000libgit2-pygit2-d86662e/.gitattributes000066400000000000000000000000201521317216500175160ustar00rootroot00000000000000*.h text eol=lf libgit2-pygit2-d86662e/.github/000077500000000000000000000000001521317216500161735ustar00rootroot00000000000000libgit2-pygit2-d86662e/.github/FUNDING.yml000066400000000000000000000000171521317216500200060ustar00rootroot00000000000000github: jdavid libgit2-pygit2-d86662e/.github/dependabot.yml000066400000000000000000000012661521317216500210300ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: "monthly" commit-message: prefix: "chore(CI):" groups: actions: patterns: - "*" - package-ecosystem: pip directory: .github/ schedule: interval: "monthly" groups: pip: patterns: - "*" libgit2-pygit2-d86662e/.github/workflows/000077500000000000000000000000001521317216500202305ustar00rootroot00000000000000libgit2-pygit2-d86662e/.github/workflows/codespell.yml000066400000000000000000000007771521317216500227400ustar00rootroot00000000000000# Codespell configuration is within pyproject.toml --- name: Codespell on: pull_request: branches: [master] push: branches: [master] permissions: contents: read jobs: codespell: name: Check for spelling errors runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Annotate locations with typos uses: codespell-project/codespell-problem-matcher@v1 - name: Codespell uses: codespell-project/actions-codespell@v2 libgit2-pygit2-d86662e/.github/workflows/lint.yml000066400000000000000000000011721521317216500217220ustar00rootroot00000000000000name: Lints on: pull_request: branches: [master] push: branches: [master] paths-ignore: - '**.rst' jobs: ruff: runs-on: ubuntu-24.04 steps: - name: Checkout pygit2 uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: python-version: '3.13' - name: Install ruff run: pip install ruff - name: Format code with ruff run: ruff format --diff - name: Check code style with ruff run: ruff check - name: Check typing with mypy run: LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.4 /bin/sh build.sh mypy libgit2-pygit2-d86662e/.github/workflows/parse_release_notes.py000066400000000000000000000055611521317216500246330ustar00rootroot00000000000000"""Parse the latest release notes from CHANGELOG.md. If running in GitHub Actions, set the `release_title` output variable for use in subsequent step(s). If running in CI, write the release notes to ReleaseNotes.md for upload as an artifact. Otherwise, print the release title and notes to stdout. """ import re import subprocess from os import environ from pathlib import Path class ChangesEntry: def __init__(self, version: str, notes: str) -> None: self.version = version title = notes.splitlines()[0] self.title = f'{version} {title}' self.notes = notes[len(title) :].strip() H1 = re.compile(r'^# (\d+\.\d+\.\d+)', re.MULTILINE) def parse_changelog() -> list[ChangesEntry]: changelog = Path('CHANGELOG.md').read_text(encoding='utf-8') parsed = H1.split(changelog) # may result in a blank line at index 0 if not parsed[0]: # leading entry is a blank line due to re.split() implementation parsed = parsed[1:] assert len(parsed) % 2 == 0, ( 'Malformed CHANGELOG.md; Entries expected to start with "# x.y.x"' ) changes: list[ChangesEntry] = [] for i in range(0, len(parsed), 2): version = parsed[i] notes = parsed[i + 1].strip() changes.append(ChangesEntry(version, notes)) return changes def get_version_tag() -> str | None: if 'GITHUB_REF' in environ: # for use in GitHub Actions git_ref = environ['GITHUB_REF'] else: # for local use git_out = subprocess.run( ['git', 'rev-parse', '--symbolic-full-name', 'HEAD'], capture_output=True, text=True, check=True, ) git_ref = git_out.stdout.strip() version: str | None = None if git_ref and git_ref.startswith('refs/tags/'): version = git_ref[len('refs/tags/') :].lstrip('v') else: print( f"Using latest CHANGELOG.md entry because the git ref '{git_ref}' is not a tag." ) return version def get_entry(changes: list[ChangesEntry], version: str | None) -> ChangesEntry: latest = changes[0] if version is not None: for entry in changes: if entry.version == version: latest = entry break else: raise ValueError(f'No changelog entry found for version {version}') return latest def main() -> None: changes = parse_changelog() version = get_version_tag() latest = get_entry(changes=changes, version=version) if 'GITHUB_OUTPUT' in environ: with Path(environ['GITHUB_OUTPUT']).open('a') as gh_out: print(f'release_title={latest.title}', file=gh_out) if environ.get('CI', 'false') == 'true': Path('ReleaseNotes.md').write_text(latest.notes, encoding='utf-8') else: print('Release notes:') print(f'# {latest.title}\n{latest.notes}') if __name__ == '__main__': main() libgit2-pygit2-d86662e/.github/workflows/tests.yml000066400000000000000000000013011521317216500221100ustar00rootroot00000000000000name: Tests (s390x) on: pull_request: branches: [master] push: branches: [master] paths-ignore: - '**.rst' jobs: linux-s390x: runs-on: ubuntu-24.04 if: github.ref == 'refs/heads/master' steps: - name: Checkout uses: actions/checkout@v6 - name: Build & test uses: uraimo/run-on-arch-action@v3 with: arch: s390x distro: ubuntu22.04 install: | apt-get update -q -y apt-get install -q -y cmake libssl-dev python3-dev python3-venv wget run: | LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.4 /bin/sh build.sh test continue-on-error: true # Tests are expected to fail, see issue #812 libgit2-pygit2-d86662e/.github/workflows/wheels.yml000066400000000000000000000116531521317216500222500ustar00rootroot00000000000000name: Wheels concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref_name != 'master' }} on: push: branches: [master] tags: - 'v*' pull_request: branches: [master] paths-ignore: - 'docs/**' jobs: sdist: runs-on: ubuntu-latest outputs: release_title: ${{ steps.parse_changelog.outputs.release_title }} steps: - uses: actions/checkout@v6 with: # avoid leaking credentials in uploaded artifacts persist-credentials: false - uses: actions/setup-python@v6 with: python-version: '3.13' - name: Build sdist run: pipx run build --sdist --outdir dist - uses: actions/upload-artifact@v7 with: name: wheels-sdist path: dist/* - name: parse CHANGELOG for release notes id: parse_changelog run: python .github/workflows/parse_release_notes.py - name: Upload Release Notes uses: actions/upload-artifact@v7 with: name: release-notes path: ReleaseNotes.md build_wheels: name: Wheels for ${{ matrix.name }} runs-on: ${{ matrix.os }} needs: sdist strategy: # let other jobs in matrix complete if one fails fail-fast: false matrix: include: - name: linux-amd os: ubuntu-24.04 - name: linux-arm os: ubuntu-24.04-arm - name: macos-intel os: macos-15-intel - name: macos-arm os: macos-15 - name: windows-x64 os: windows-latest - name: windows-x86 os: windows-latest - name: windows-arm64 # https://github.com/actions/partner-runner-images#available-images os: windows-11-arm steps: - uses: actions/setup-python@v6 with: python-version: '3.13' - name: Download sdist uses: actions/download-artifact@v8 with: name: wheels-sdist path: dist - name: Install cibuildwheel run: python -m pip install cibuildwheel~=3.3 - name: Build wheels shell: bash env: CIBW_ARCHS_WINDOWS: ${{ matrix.name == 'windows-x86' && 'auto32' || 'native' }} run: python -m cibuildwheel dist/*.tar.gz --output-dir wheelhouse - uses: actions/upload-artifact@v7 with: name: wheels-${{ matrix.name }} path: ./wheelhouse/*.whl build_wheels_ppc: name: Wheels for linux-ppc runs-on: ubuntu-24.04 needs: sdist steps: - uses: actions/setup-python@v6 with: python-version: '3.13' - uses: docker/setup-qemu-action@v4 with: platforms: linux/ppc64le - name: Download sdist uses: actions/download-artifact@v8 with: name: wheels-sdist path: dist - name: Install cibuildwheel run: python -m pip install cibuildwheel~=3.3 - name: Build wheels shell: bash run: python -m cibuildwheel dist/*.tar.gz --output-dir wheelhouse env: CIBW_ARCHS: ppc64le CIBW_ENVIRONMENT: LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.4 LIBGIT2=/project/ci - uses: actions/upload-artifact@v7 with: name: wheels-linux-ppc path: ./wheelhouse/*.whl twine-check: name: Twine check # It is good to do this check on non-tagged commits. # Note, pypa/gh-action-pypi-publish (see job below) does this automatically. if: ${{ !startsWith(github.ref, 'refs/tags/v') }} needs: [build_wheels, build_wheels_ppc, sdist] runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v8 with: path: dist pattern: wheels-* merge-multiple: true - name: check distribution files run: pipx run twine check dist/* pypi: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') needs: [build_wheels, build_wheels_ppc, sdist] permissions: contents: write # to create GitHub Release runs-on: ubuntu-24.04 steps: - uses: actions/download-artifact@v8 with: path: dist pattern: wheels-* merge-multiple: true - name: Display structure of downloaded files run: ls -lh dist - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} skip-existing: true - uses: actions/download-artifact@v8 with: name: release-notes - name: Create GitHub Release env: GITHUB_TOKEN: ${{ github.token }} TAG: ${{ github.ref_name }} REPO: ${{ github.repository }} TITLE: ${{ needs.sdist.outputs.release_title }} # https://cli.github.com/manual/gh_release_create run: >- gh release create ${TAG} --verify-tag --repo ${REPO} --title "${TITLE}" --notes-file ReleaseNotes.md libgit2-pygit2-d86662e/.gitignore000066400000000000000000000073321521317216500166300ustar00rootroot00000000000000# Created by https://www.toptal.com/developers/gitignore/api/python,c # Edit at https://www.toptal.com/developers/gitignore?templates=python,c ### C ### # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf ### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions # Distribution / packaging .Python build/ develop-eggs/ /dist/ downloads/ eggs/ /.eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ wheelhouse/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg /MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ /.coverage .coverage.* /.cache/ nosetests.xml coverage.xml lcov.info *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ ### Python Patch ### # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration poetry.toml # ruff .ruff_cache/ # LSP config files pyrightconfig.json # End of https://www.toptal.com/developers/gitignore/api/python,c # PyCharm (IntelliJ JetBrains) .idea/ *.iml *.iws *.ipr .idea_modules/ # for VSCode .vscode/ # for Eclipse .settings/ # custom ignore paths /.envrc /venv* /.docenv /ci/ *.swp /pygit2/_libgit2.c /pygit2/_libgit2.o libgit2-pygit2-d86662e/.mailmap000066400000000000000000000044201521317216500162540ustar00rootroot00000000000000Alexander Bayandin Alexander Linne Anatoly Techtonik Bob Carroll Brandon Milton Brendan Doherty <2bndy5@gmail.com> CJ Steiner <47841949+clintonsteiner@users.noreply.github.com> Carlos Martín Nieto Christian Boos Grégory Herrero Guillermo Pérez Gustavo Di Pietro J. David Ibáñez Jeremy Westwood Jose Plana Kaarel Kitsemets Karl Malmros <44969574+ktpa@users.noreply.github.com> Konstantin Baikov Lukas Fleischer Martin Lenders Matthew Duggan Matthew Gamble Matthias Bartelmeß Mikhail Yushkovskiy Mukunda Rao Katta Nabijacz Leweli Nicolas Rybowski Óscar San José Petr Hosek Phil Schleihauf Raphael Medaer Richo Healey Robert Hölzl Saugat Pachhai Sriram Raghu Sukhman Bhuller Tamir Bahar Tamir Bahar Victor Florea Victor Garcia Vlad Temian William Schueller Wim Jeantine-Glenn Xavier Delannoy Xu Tao Xu Tao libgit2-pygit2-d86662e/.vimrc000066400000000000000000000014461521317216500157610ustar00rootroot00000000000000" pygit2 local vimrc - C extension configuration " Get Python include path dynamically let s:python_include = system('python3 -c "import sysconfig; print(sysconfig.get_path(''include''))"')[:-2] " Configure ALE C linters with proper includes let g:ale_c_cc_options = '-std=c11 -Wall -I' . s:python_include . ' -I/usr/local/include' let g:ale_c_gcc_options = '-std=c11 -Wall -I' . s:python_include . ' -I/usr/local/include' let g:ale_c_clang_options = '-std=c11 -Wall -I' . s:python_include . ' -I/usr/local/include' " If you have libgit2 in a non-standard location, add it: " let g:ale_c_cc_options .= ' -I/usr/local/include/git2' " Optional: Explicitly set which linters to use for C let g:ale_linters = get(g:, 'ale_linters', {}) let g:ale_linters.c = ['cc', 'clangtidy'] " or ['gcc'] if you prefer libgit2-pygit2-d86662e/AGENTS.md000066400000000000000000000253701521317216500161450ustar00rootroot00000000000000# pygit2 Agent Guide ## Project Overview **pygit2** is a Python library that provides bindings to [libgit2](https://libgit2.org/), the shared C library that implements Git plumbing operations. It exposes both a low-level API (direct libgit2 wrappers) and a high-level, Pythonic API for repository manipulation. - **Version**: 1.19.3 (canonical version is defined in `pygit2/_build.py`) - **License**: GPLv2 with linking exception (see `COPYING`) - **Maintainer**: J. David Ibáñez - **Python Support**: 3.11 – 3.14 and PyPy3 7.3+ - **libgit2 Version**: 1.9.4 - **Homepage**: - **Repository**: ## Architecture The project uses a **hybrid C/Python** architecture with two compiled extension modules: - **`src/`** — C11 source and header files that compile into the `_pygit2` C extension module. Each file generally maps to a libgit2 concept: - Core objects: `blob.c`, `commit.c`, `object.c`, `tag.c`, `tree.c` - Repository, refs and branches: `repository.c`, `branch.c`, `reference.c`, `refdb.c`, `refdb_backend.c`, `revspec.c`, `worktree.c` - Diff and patch: `diff.c`, `patch.c` - ODB and backends: `odb.c`, `odb_backend.c` - Index, walking and helpers: `treebuilder.c`, `walker.c`, `oid.c`, `note.c`, `signature.c`, `mailmap.c`, `stash.c` - Filters: `filter.c` - Module infrastructure: `pygit2.c`, `error.c`, `utils.c`, `wildmatch.c` - Headers: `*.h` files mirroring the C sources (e.g. `repository.h`, `diff.h`, `types.h`, `error.h`) - **`pygit2/`** — The main Python package. - **`_pygit2*.so`** — Compiled C extension built from `src/`. - **`_libgit2.abi3.so`** — CFFI-generated ABI module built from `pygit2/_run.py`. - **`decl/`** — C header stub files used by CFFI to define the libgit2 API surface (e.g. `types.h`, `repository.h`, `callbacks.h`, `diff.h`, `remote.h`). `pygit2/_run.py` concatenates these stubs in a specific order before passing them to CFFI. - **`_build.py`** — Build-time helpers and the canonical `__version__` string. Also used at runtime to locate libgit2. It must remain importable without the rest of the package being built because `setup.py` imports it. - **`_run.py`** — CFFI build script that aggregates `decl/*.h` and compiles `pygit2._libgit2`. - **`ffi.py`** — Runtime import of the CFFI `ffi` and `lib` (`C`) objects. - **`_pygit2.pyi`** — Type stubs for the C extension. Keep it in sync when adding or changing low-level APIs. - **`py.typed`** — PEP 561 marker indicating the package is typed. - **High-level modules** — Pure-Python wrappers that sit on top of the C extension: `repository.py`, `callbacks.py`, `config.py`, `index.py`, `remotes.py`, `settings.py`, `submodules.py`, `transaction.py`, `filter.py`, `blob.py`, `blame.py`, `branches.py`, `credentials.py`, `errors.py`, `options.py`, `packbuilder.py`, `references.py`, `refspec.py`, `utils.py`, `enums.py`, `legacyenums.py`. - **`test/`** — pytest suite with fixture-based repository handling. - **`docs/`** — Sphinx documentation (RTD theme). ## Key Configuration Files - **`setup.py`** — setuptools entry point. Builds both the C extension (`src/*.c`) and the CFFI extension (`pygit2/_run.py:ffi`). - **`pyproject.toml`** — Build-system requirements, `cibuildwheel` configuration, `ruff` settings, and `codespell` settings. - **`setup.cfg`** — Legacy pycodestyle configuration. - **`pytest.ini`** — pytest configuration (`--capture=no -ra --verbose`, `testpaths = test/`). - **`mypy.ini`** — mypy configuration with strict settings. - **`mypy-stubtest.ini`** — mypy configuration for `stubtest` against `_pygit2.pyi`. - **`requirements.txt`** — Runtime/build requirements (`cffi>=2.0`, `setuptools` for Python >= 3.12). - **`requirements-test.txt`** — Test requirements (`pytest`, `pytest-cov`). - **`requirements-typing.txt`** — Typing requirements (`mypy`, `types-cffi`). - **`Makefile`** — Convenience targets: `make` builds dependencies + extension inplace; `make html` builds docs. - **`.vimrc`** — Local editor configuration for C development with ALE (`-std=c11 -Wall`, Python include path, `/usr/local/include`). ## Build and Test Commands ### Quick Development Build (inplace) Requires libgit2 development headers and library to be installed on the system or pointed to via the `LIBGIT2` environment variable. ```bash python setup.py build_ext --inplace pytest ``` ### Full Build with Dependencies The `build.sh` script can download, compile, and bundle libgit2 (and optionally libssh2, OpenSSL, and zlib) into a local prefix. On Windows, `build.ps1` handles libgit2 compilation via CMake. ```bash # Build inplace with bundled libgit2/libssh2/OpenSSL make # Or manually: LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.4 sh build.sh # Build inplace and run the tests sh build.sh test # Build a wheel, install it, and run the tests sh build.sh wheel # Run tests with coverage sh build.sh test # build.sh adds --cov=pygit2 # Run mypy type checking sh build.sh mypy # Run stubtest against the .pyi file sh build.sh stubtest ``` `build.sh` creates a virtual environment under `ci//` by default, where `` is computed by `build_tag.py`. Use the `PYTHON` environment variable to select a different interpreter (default: `python3`). ### Environment Variables Variables consumed by `setup.py` / `pygit2/_build.py`: - `LIBGIT2` — Base path where libgit2 is installed (default: `/usr/local` or `%ProgramFiles%\libgit2` on Windows). - `LIBGIT2_LIB` — Override the library directory specifically. Variables consumed by `build.sh`: - `LIBGIT2_VERSION` — If set, download and build this libgit2 version. - `LIBSSH2_VERSION` — If set, download and build libssh2 with SSH support. - `OPENSSL_VERSION` — If set, download and build OpenSSL (mainly used for macOS universal builds on CI). - `ZLIB_VERSION` — If set, download and build zlib. - `BUILD_TYPE` — CMake build type (default: `Debug`). - `PYTHON` — Python interpreter to use (default: `python3`). - `PREFIX` — Installation prefix (default: `$(pwd)/ci/$PYTHON_TAG`). - `CIBUILDWHEEL` — Set to `1` when invoked by cibuildwheel; changes package manager and directory layout. - `AUDITWHEEL_PLAT` — Linux platform for auditwheel repair. - `LIBSSH2_OPENSSL` — Where to find OpenSSL when building libssh2. ### Documentation Build ```bash # Build the extension first, then docs make # builds deps + extension make -C docs html # requires sphinx-rtd-theme ``` ## Code Style Guidelines ### Python - **Formatter / Linter**: [ruff](https://docs.astral.sh/ruff/) - Target Python: 3.11+ - Quote style: single quotes - Selected rules: `E4`, `E7`, `E9`, `F`, `I`, `UP035`, `UP007` - **Type checker**: mypy (strict settings enabled; see `mypy.ini`) - All Python source files must include the standard GPLv2 copyright header. - `pygit2/__init__.py` is large because it re-exports a large surface of constants and classes; follow existing patterns when adding new public symbols. ### C - Standard: C11 - All C source files must include the standard GPLv2 copyright header. - The `.vimrc` at repo root configures ALE with `-std=c11 -Wall` and includes the Python headers and `/usr/local/include`. ### Docstrings Use the following style (from `docs/development.rst`): ```python def f(a, b): """ The general description goes here. Returns: bla bla. Parameters: a : Bla bla. b : Bla bla. Examples:: >>> f(...) """ ``` ## Testing Instructions - **Runner**: pytest - **Configuration**: `pytest.ini` ```ini [pytest] addopts = --capture=no -ra --verbose testpaths = test/ ``` - **Fixtures**: Defined in `test/conftest.py`. They yield `pygit2.Repository` instances extracted from zipped sample repos in `test/data/` (e.g. `testrepo.zip`, `barerepo.zip`). Named fixtures include `testrepo`, `testrepo_path`, `barerepo`, `barerepo_path`, `emptyrepo`, `dirtyrepo`, `mergerepo`, `encodingrepo`, `testrepopacked`, `gpgsigned`, `blameflagsrepo`, and `pygit2_empty_key`. - **Test utilities**: `test/utils.py` provides helpers such as `TemporaryRepository`, `gen_blob_sha1`, `rmtree`, `diff_safeiter`, and markers like `requires_network`, `requires_proxy`, `requires_ssh`, `requires_refcount`, `fails_in_macos`, and `requires_future_libgit2`. - **Isolation**: The session-scoped `global_git_config` fixture clears `GLOBAL`, `XDG`, and `SYSTEM` config search paths to ensure reproducibility. - **Coverage**: `pytest-cov` is used; run via `sh build.sh test`. ## CI / Deployment GitHub Actions workflows live in `.github/workflows/`: - **`tests.yml`** — Runs on s390x via QEMU (`uraimo/run-on-arch-action`). Allowed to fail; see issue #812. - **`lint.yml`** — Runs `ruff format --diff`, `ruff check`, and `sh build.sh mypy`. - **`wheels.yml`** — Uses `cibuildwheel` to build wheels for Linux (amd64, arm64, ppc64le, musl), macOS (intel, arm64, PyPy), and Windows (x64, x86, arm64). It also builds an sdist, runs a `twine check`, publishes to PyPI, and creates a GitHub Release on version tags (`v*`). - **`codespell.yml`** — Spell checking with the codespell action. The `cibuildwheel` configuration in `pyproject.toml` pins: - `LIBGIT2_VERSION="1.9.4"` - `LIBSSH2_VERSION="1.11.1"` - `OPENSSL_VERSION="3.5.4"` and skips `*musllinux_ppc64le` plus testing on `*-*linux_ppc64le` and `pp*-macosx_arm64`. ## Security Considerations - The project links against OpenSSL and libssh2. CI pins specific versions of these libraries when building wheels. - Wheel repair commands (`auditwheel`, `delocate-wheel`) bundle shared libraries so wheels are self-contained. - Credentials callbacks (`RemoteCallbacks`, `get_credentials`) are the primary interface for supplying secrets; never hardcode credentials in tests. - Valgrind support: see `docs/development.rst` and `misc/valgrind-python.supp` for memory-leak debugging instructions. ## Useful Notes for Agents - **Do not assume libgit2 is installed globally.** Check for `LIBGIT2` or use `build.sh` / `make`. - **`pygit2/_build.py`** is imported by `setup.py`; it must remain importable without the rest of the package being built. - **CFFI and setuptools extensions are both built from `setup.py`.** `ext_modules` builds the C extension from `src/*.c`; `cffi_modules` triggers the CFFI build via `pygit2/_run.py:ffi`. - **`.pyi` stub file**: `pygit2/_pygit2.pyi` provides type stubs for the C extension. Keep it in sync when adding or changing low-level APIs. - **Header stub order matters**: `pygit2/_run.py` concatenates `decl/*.h` in a fixed list; add new stubs in the correct position if dependencies require it. - Run the full test suite and type checks before considering a change complete: `sh build.sh test` and `sh build.sh mypy`. libgit2-pygit2-d86662e/AUTHORS.md000066400000000000000000000111101521317216500162740ustar00rootroot00000000000000Authors: J. David Ibáñez Carlos Martín Nieto Nico von Geyso Iliyas Jorio Benedikt Seidl Sviatoslav Sydorenko Matthias Bartelmeß Robert Coup W. Trevor King Drew DeVault Dave Borowitz Brandon Milton Daniel RodriÌ�guez Troitiño Brendan Doherty Peter Rowlands Richo Healey Christian Boos Julien Miotte Nick Hynes Richard Möhn Xu Tao Konstantin Baikov Matthew Duggan Matthew Gamble Jeremy Westwood Jose Plana Martin Lenders Sriram Raghu Victor Garcia Yonggang Luo Å�ukasz Langa Patrick Steinhardt Petr Hosek Tamir Bahar Valentin Haenel Xavier Delannoy Michael Jones Saugat Pachhai Andrej730 Bernardo Heynemann John Szakmeister Nabijacz Leweli Simon Cozens Vlad Temian Brodie Rao Chad Dombrova Lukas Fleischer Mathias Leppich Mathieu Parent MichaÅ‚ KÄ™pieÅ„ Nicolas Dandrimont Raphael Medaer Yaroslav Halchenko Anatoly Techtonik Andrew Olsen Dan Sully David Versmisse Grégory Herrero Mikhail Yushkovskiy Robin Stocker Rohit Sanjay Rémi Duraffort Santiago Perez De Rosso Sebastian Thiel Thom Wiggers WANG Xuerui William Manley Alexander Linne Alok Singhal Assaf Nativ Bob Carroll Christian Häggström Edmundo Carmona Antoranz Erik Johnson Ethan Meng Filip Rindler Fraser Tweedale Grégoire ROCHER Han-Wen Nienhuys Helio Machado Jason Ziglar Leonardo Rhodes Mark Adams Nika Layzell Peter-Yi Zhang Petr Viktorin Robert Hölzl Ron Cohen Sebastian Böhm Sukhman Bhuller Thomas Kluyver Tyler Cipriani Alex Chamberlain Alexander Bayandin Amit Bakshi Andrey Devyatkin Arno van Lumig Ben Davis CJ Steiner Colin Watson Craig de Stigter Dan Yeaw Dustin Raimondi Eric Schrijver Greg Fitzgerald Guillermo Pérez Hervé Cauwelier Hong Minhee Huang Huang Ian P. McCullough Igor Gnatenko Insomnia Jack O'Connor Jared Flatow Jeremy Heiner Jesse P. Johnson Jiunn Haur Lim Jorge C. Leitao Jun Omae Kaarel Kitsemets Ken Dreyer Kevin KIN-FOO Kyle Gottfried Marcel Waldvogel Masud Rahman Michael Sondergaard Natanael Arndt OndÅ™ej Nový Sarath Lakshman Steve Kieffer Szucs Krisztian Vicent Marti Zbigniew JÄ™drzejewski-Szmek Zoran Zaric nikitalita Adam Gausmann Adam Spiers Adrien Nader Albin Söderström Alexander Shadchin Alexandru Fikl Andrew Chin Andrew McNulty Andrey Trubachev András Veres-Szentkirályi Ash Berlin Benjamin Kircher Benjamin Pollack Benjamin Wohlwend Bogdan Stoicescu Bogdan Vasilescu Bryan O'Sullivan CJ Harries Cam Cope Chad Birch Chason Chaffin Chris Jerdonek Chris Rebert Christopher Hunt Claudio Jolowicz Cristian Hotea Cyril Jouve Dan Cecile Daniel Bruce Daniele Esposti Daniele Trifirò David Black David Fischer David Sanders David Six Dennis Schwertel Devaev Maxim Eric Davis Erik Meusel Erik van Zijst Fabrice Salvaire Ferengee Florian Weimer Frazer McLean Gustavo Di Pietro Holger Frey Hugh Cole-Baker Isabella Stephens Jacob Swanson Jah-yee Jasper Lievisse Adriaanse Jimisola Laursen Jiri Benc Johann Miller Jonathan Robson Josh Bleecher Snyder Julia Evans Justin Clift Karl Malmros Kevin Valk Konstantinos Smanis Kyriakos Oikonomakos Lance Eftink Legorooj Lukas Berk Martin von Zweigbergk Mathieu Bridon Mathieu Pillard Matthaus Woolard MatÄ›j Cepl Maxwell G MichaÅ‚ Górny Mukunda Rao Katta Na'aman Hirschfeld Nicolas Rybowski Nicolás Sanguinetti Nikita Kartashov Nikolai Zujev Nils Philippsen Noah Fontes Or Hayat Óscar San José Patrick Lühne Paul Wagland Peter Dave Hello Phil Schleihauf Philippe Ombredanne Ram Rachum Remy Suen Ridge Kennedy Rodrigo Bistolfi Ross Nicoll Rui Abreu Ferreira Rui Chen Sandro Jäckel Saul Pwanson Sebastian Hamann Shane Turner Sheeo Simone Mosciatti Soasme Steven Winfield Tad Hardesty Timo Röhling Victor Florea Vladimir Rutsky Vruyr Gyolchanyan William Schueller Wim Jeantine-Glenn Yu Jianjian buhl chengyuhang earl odidev libgit2-pygit2-d86662e/CHANGELOG.md000066400000000000000000002165571521317216500164640ustar00rootroot00000000000000# 1.19.3 (2026-06-13) - Memory fixes [#1368](https://github.com/libgit2/pygit2/issues/1368) [#1417](https://github.com/libgit2/pygit2/issues/1417) [#1443](https://github.com/libgit2/pygit2/issues/1443) - Fix `Repository.ident` [#1461](https://github.com/libgit2/pygit2/pull/1461) - Build/CI fixes and updates [#1454](https://github.com/libgit2/pygit2/issues/1454) [#1459](https://github.com/libgit2/pygit2/pull/1459) - Documentation and annotation fixes [#410](https://github.com/libgit2/pygit2/issues/410) [#1289](https://github.com/libgit2/pygit2/issues/1289) [#1323](https://github.com/libgit2/pygit2/issues/1323) [#1333](https://github.com/libgit2/pygit2/issues/1333) [#1458](https://github.com/libgit2/pygit2/issues/1458) [#1460](https://github.com/libgit2/pygit2/pull/1460) - Add `AGENTS.md` file generated by Kimi-k2.6 # 1.19.2 (2026-03-29) - Fix refcount and error handling issues in `filter_register(...)` - Fix config with valueless keys [#1457](https://github.com/libgit2/pygit2/pull/1457) - New `Repository.load_filter_list(...)` and `FilterList` [#1444](https://github.com/libgit2/pygit2/pull/1444) - New `Odb.read_header(...)` and now `Odb.read(...)` returns `enums.ObjectType` instead of int [#1450](https://github.com/libgit2/pygit2/pull/1450) - Build and CI fixes [#1446](https://github.com/libgit2/pygit2/pull/1446) [#1448](https://github.com/libgit2/pygit2/pull/1448) [#1455](https://github.com/libgit2/pygit2/pull/1455) # 1.19.1 (2025-12-29) - Update wheels to libgit2 1.9.2 and OpenSSL 3.5 - Fix: now diff's getitem/iter returns `None` for unchanged or binary files [#1412](https://github.com/libgit2/pygit2/pull/1412) - CI (macOS): arm, intel and pypy wheels (instead of universal) [#1441](https://github.com/libgit2/pygit2/pull/1441) - CI (pypy): fix tests [#1437](https://github.com/libgit2/pygit2/pull/1437) # 1.19.0 (2025-10-23) - Add support for Python 3.14 and drop 3.10 - Support threaded builds (experimental) [#1430](https://github.com/libgit2/pygit2/pull/1430) [#1435](https://github.com/libgit2/pygit2/pull/1435) - Add Linux musl wheels for AArch64 - Add Windows wheels for AArch64; CI: build Windows wheels with cibuildwheel on GitHub [#1423](https://github.com/libgit2/pygit2/pull/1423) - New `Repository.transaction()` context manager, returns new `ReferenceTransaction` [#1420](https://github.com/libgit2/pygit2/pull/1420) - CI: add GitHub releases and other improvements [#1433](https://github.com/libgit2/pygit2/pull/1433) [#1432](https://github.com/libgit2/pygit2/pull/1432) [#1425](https://github.com/libgit2/pygit2/pull/1425) [#1431](https://github.com/libgit2/pygit2/pull/1431) - Documentation improvements and other changes [#1426](https://github.com/libgit2/pygit2/pull/1426) [#1424](https://github.com/libgit2/pygit2/pull/1424) Breaking changes: - Remove deprecated `IndexEntry.hex`, use `str(entry.id)` instead of `entry.hex` Deprecations: - Deprecate `IndexEntry.oid`, use `entry.id` instead of `entry.oid` # 1.18.2 (2025-08-16) - Add support for almost all global options [#1409](https://github.com/libgit2/pygit2/pull/1409) - Now it's possible to set `Submodule.url = url` [#1395](https://github.com/libgit2/pygit2/pull/1395) - New `RemoteCallbacks.push_negotiation(...)` [#1396](https://github.com/libgit2/pygit2/pull/1396) - New optional boolean argument `connect` in `Remote.ls_remotes(...)` [#1396](https://github.com/libgit2/pygit2/pull/1396) - New `Remote.list_heads(...)` returns a list of `RemoteHead` objects [#1397](https://github.com/libgit2/pygit2/pull/1397) [#1410](https://github.com/libgit2/pygit2/pull/1410) - Documentation fixes [#1388](https://github.com/libgit2/pygit2/pull/1388) - Typing improvements [#1387](https://github.com/libgit2/pygit2/pull/1387) [#1389](https://github.com/libgit2/pygit2/pull/1389) [#1390](https://github.com/libgit2/pygit2/pull/1390) [#1391](https://github.com/libgit2/pygit2/pull/1391) [#1392](https://github.com/libgit2/pygit2/pull/1392) [#1393](https://github.com/libgit2/pygit2/pull/1393) [#1394](https://github.com/libgit2/pygit2/pull/1394) [#1398](https://github.com/libgit2/pygit2/pull/1398) [#1399](https://github.com/libgit2/pygit2/pull/1399) [#1400](https://github.com/libgit2/pygit2/pull/1400) [#1402](https://github.com/libgit2/pygit2/pull/1402) [#1403](https://github.com/libgit2/pygit2/pull/1403) [#1406](https://github.com/libgit2/pygit2/pull/1406) [#1407](https://github.com/libgit2/pygit2/pull/1407) [#1408](https://github.com/libgit2/pygit2/pull/1408) Deprecations: - `Remote.ls_remotes(...)` is deprecated, use `Remote.list_heads(...)`: # Before for head in remote.ls_remotes(): head['name'] head['oid'] head['loid'] # None when local is False head['local'] head['symref_target'] # Now for head in remote.list_heads(): head.name head.oid head.loid # The zero oid when local is False head.local head.symref_target # 1.18.1 (2025-07-26) - Update wheels to libgit2 1.9.1 and OpenSSL 3.3 - New `Index.remove_directory(...)` [#1377](https://github.com/libgit2/pygit2/pull/1377) - New `Index.add_conflict(...)` [#1382](https://github.com/libgit2/pygit2/pull/1382) - Now `Repository.merge_file_from_index(...)` returns a `MergeFileResult` object when called with `use_deprecated=False` [#1376](https://github.com/libgit2/pygit2/pull/1376) - Typing improvements [#1369](https://github.com/libgit2/pygit2/pull/1369) [#1370](https://github.com/libgit2/pygit2/pull/1370) [#1371](https://github.com/libgit2/pygit2/pull/1371) [#1373](https://github.com/libgit2/pygit2/pull/1373) [#1384](https://github.com/libgit2/pygit2/pull/1384) [#1386](https://github.com/libgit2/pygit2/pull/1386) Deprecations: - Update your code: # Before contents = Repository.merge_file_from_index(...) # Now result = Repository.merge_file_from_index(..., use_deprecated=False) contents = result.contents At some point in the future `use_deprecated=False` will be the default. # 1.18.0 (2025-04-24) - Upgrade Linux Glibc wheels to `manylinux_2_28` - Add `RemoteCallbacks.push_transfer_progress(...)` callback [#1345](https://github.com/libgit2/pygit2/pull/1345) - New `bool(oid)` [#1347](https://github.com/libgit2/pygit2/pull/1347) - Now `Repository.merge(...)` accepts a commit or reference object [#1348](https://github.com/libgit2/pygit2/pull/1348) - New `threads` optional argument in `Remote.push(...)` [#1352](https://github.com/libgit2/pygit2/pull/1352) - New `proxy` optional argument in `clone_repository(...)` [#1354](https://github.com/libgit2/pygit2/pull/1354) - New optional arguments `context_lines` and `interhunk_lines` in `Blob.diff(...)` ; and now `Repository.diff(...)` honors these two arguments when the objects diffed are blobs. [#1360](https://github.com/libgit2/pygit2/pull/1360) - Now `Tree.diff_to_workdir(...)` accepts keyword arguments, not just positional. - Fix when a reference name has non UTF-8 chars [#1329](https://github.com/libgit2/pygit2/pull/1329) - Fix condition check in `Repository.remotes.rename(...)` [#1342](https://github.com/libgit2/pygit2/pull/1342) - Add codespell workflow, fix a number of typos [#1344](https://github.com/libgit2/pygit2/pull/1344) - Documentation and typing [#1343](https://github.com/libgit2/pygit2/pull/1343) [#1347](https://github.com/libgit2/pygit2/pull/1347) [#1356](https://github.com/libgit2/pygit2/pull/1356) - CI: Use ARM runner for tests and wheels [#1346](https://github.com/libgit2/pygit2/pull/1346) - Build and CI updates [#1363](https://github.com/libgit2/pygit2/pull/1363) [#1365](https://github.com/libgit2/pygit2/pull/1365) Deprecations: - Passing str to `Repository.merge(...)` is deprecated, instead pass an oid object (or a commit, or a reference) [#1349](https://github.com/libgit2/pygit2/pull/1349) Breaking changes: - Keyword argument `flag` has been renamed to `flags` in `Blob.diff(...)` and `Blob.diff_to_buffer(...)` # 1.17.0 (2025-01-08) - Upgrade to libgit2 1.9 - Add `certificate_check` callback to `Remote.ls_remotes(...)` [#1326](https://github.com/libgit2/pygit2/pull/1326) - Fix build with GCC 14 [#1324](https://github.com/libgit2/pygit2/pull/1324) - Release wheels for PyPy [#1336](https://github.com/libgit2/pygit2/pull/1336) [#1339](https://github.com/libgit2/pygit2/pull/1339) - CI: update tests for macOS to use OpenSSL 3 [#1335](https://github.com/libgit2/pygit2/pull/1335) - Documentation: fix typo in `Repository.status(...)` docstring [#1327](https://github.com/libgit2/pygit2/pull/1327) # 1.16.0 (2024-10-11) - Add support for Python 3.13 - Drop support for Python 3.9 - New `Repository.hashfile(...)` [#1298](https://github.com/libgit2/pygit2/pull/1298) - New `Option.GET_MWINDOW_FILE_LIMIT` and `Option.SET_MWINDOW_FILE_LIMIT` [#1312](https://github.com/libgit2/pygit2/pull/1312) - Fix overriding `certificate_check(...)` callback via argument to `RemoteCallbacks(...)` [#1321](https://github.com/libgit2/pygit2/pull/1321) - Add py.typed [#1310](https://github.com/libgit2/pygit2/pull/1310) - Fix `discover_repository(...)` annotation [#1313](https://github.com/libgit2/pygit2/pull/1313) # 1.15.1 (2024-07-07) - New `Repository.revert(...)` [#1297](https://github.com/libgit2/pygit2/pull/1297) - New optional `depth` argument in submodules `add()` and `update()` methods [#1296](https://github.com/libgit2/pygit2/pull/1296) - Now `Submodule.url` returns `None` when the submodule does not have a url [#1294](https://github.com/libgit2/pygit2/pull/1294) - Fix use after free bug in error reporting [#1299](https://github.com/libgit2/pygit2/pull/1299) - Fix `Submodule.head_id` when the submodule is not in the current HEAD tree [#1300](https://github.com/libgit2/pygit2/pull/1300) - Fix `Submodule.open()` when subclassing `Repository` [#1295](https://github.com/libgit2/pygit2/pull/1295) - Fix error in the test suite when running with address sanitizer [#1304](https://github.com/libgit2/pygit2/pull/1304) [#1301](https://github.com/libgit2/pygit2/issues/1301) - Annotations and documentation fixes [#1293](https://github.com/libgit2/pygit2/pull/1293) # 1.15.0 (2024-05-18) - Many deprecated features have been removed, see below - Upgrade to libgit2 v1.8.1 - New `push_options` optional argument in `Repository.push(...)` [#1282](https://github.com/libgit2/pygit2/pull/1282) - New support comparison of `Oid` with text string - Fix `CheckoutNotify.IGNORED` [#1288](https://github.com/libgit2/pygit2/issues/1288) - Use default error handler when decoding/encoding paths [#537](https://github.com/libgit2/pygit2/issues/537) - Remove setuptools runtime dependency [#1281](https://github.com/libgit2/pygit2/pull/1281) - Coding style with ruff [#1280](https://github.com/libgit2/pygit2/pull/1280) - Add wheels for ppc64le [#1279](https://github.com/libgit2/pygit2/pull/1279) - Fix tests on EPEL8 builds for s390x [#1283](https://github.com/libgit2/pygit2/pull/1283) Deprecations: - Deprecate `IndexEntry.hex`, use `str(IndexEntry.id)` Breaking changes: - Remove deprecated `oid.hex`, use `str(oid)` - Remove deprecated `object.hex`, use `str(object.id)` - Remove deprecated `object.oid`, use `object.id` - Remove deprecated `Repository.add_submodule(...)`, use `Repository.submodules.add(...)` - Remove deprecated `Repository.lookup_submodule(...)`, use `Repository.submodules[...]` - Remove deprecated `Repository.init_submodules(...)`, use `Repository.submodules.init(...)` - Remove deprecated `Repository.update_submodule(...)`, use `Repository.submodules.update(...)` - Remove deprecated constants `GIT_OBJ_XXX`, use `ObjectType` - Remove deprecated constants `GIT_REVPARSE_XXX`, use `RevSpecFlag` - Remove deprecated constants `GIT_REF_XXX`, use `ReferenceType` - Remove deprecated `ReferenceType.OID`, use instead `ReferenceType.DIRECT` - Remove deprecated `ReferenceType.LISTALL`, use instead `ReferenceType.ALL` - Remove deprecated support for passing dicts to repository\'s `merge(...)`, `merge_commits(...)` and `merge_trees(...)`. Instead pass `MergeFlag` for `flags`, and `MergeFileFlag` for `file_flags`. - Remove deprecated support for passing a string for the favor argument to repository\'s `merge(...)`, `merge_commits(...)` and `merge_trees(...)`. Instead pass `MergeFavor`. # 1.14.1 (2024-02-10) - Update wheels to libgit2 v1.7.2 - Now `Object.filemode` returns `enums.FileMode` and `Reference.type` returns `enums.ReferenceType` [#1273](https://github.com/libgit2/pygit2/pull/1273) - Fix tests on Fedora 40 [#1275](https://github.com/libgit2/pygit2/pull/1275) Deprecations: - Deprecate `ReferenceType.OID`, use `ReferenceType.DIRECT` - Deprecate `ReferenceType.LISTALL`, use `ReferenceType.ALL` # 1.14.0 (2024-01-26) - Drop support for Python 3.8 - Add Linux wheels for musl on x86\_64 [#1266](https://github.com/libgit2/pygit2/pull/1266) - New `Repository.submodules` namespace [#1250](https://github.com/libgit2/pygit2/pull/1250) - New `Repository.listall_mergeheads()`, `Repository.message`, `Repository.raw_message` and `Repository.remove_message()` [#1261](https://github.com/libgit2/pygit2/pull/1261) - New `pygit2.enums` supersedes the `GIT_` constants [#1251](https://github.com/libgit2/pygit2/pull/1251) - Now `Repository.status()`, `Repository.status_file()`, `Repository.merge_analysis()`, `DiffFile.flags`, `DiffFile.mode`, `DiffDelta.flags` and `DiffDelta.status` return enums [#1263](https://github.com/libgit2/pygit2/pull/1263) - Now repository\'s `merge()`, `merge_commits()` and `merge_trees()` take enums/flags for their `favor`, `flags` and `file_flags` arguments. [#1271](https://github.com/libgit2/pygit2/pull/1271) [#1272](https://github.com/libgit2/pygit2/pull/1272) - Fix crash in filter cleanup [#1259](https://github.com/libgit2/pygit2/pull/1259) - Documentation fixes [#1255](https://github.com/libgit2/pygit2/pull/1255) [#1258](https://github.com/libgit2/pygit2/pull/1258) [#1268](https://github.com/libgit2/pygit2/pull/1268) [#1270](https://github.com/libgit2/pygit2/pull/1270) Breaking changes: - Remove deprecated `Repository.create_remote(...)` function, use instead `Repository.remotes.create(...)` Deprecations: - Deprecate `Repository.add_submodule(...)`, use `Repository.submodules.add(...)` - Deprecate `Repository.lookup_submodule(...)`, use `Repository.submodules[...]` - Deprecate `Repository.init_submodules(...)`, use `Repository.submodules.init(...)` - Deprecate `Repository.update_submodule(...)`, use `Repository.submodules.update(...)` - Deprecate `GIT_*` constants, use `pygit2.enums` - Passing dicts to repository\'s `merge(...)`, `merge_commits(...)` and `merge_trees(...)` is deprecated. Instead pass `MergeFlag` for the `flags` argument, and `MergeFileFlag` for `file_flags`. - Passing a string for the favor argument to repository\'s `merge(...)`, `merge_commits(...)` and `merge_trees(...)` is deprecated. Instead pass `MergeFavor`. # 1.13.3 (2023-11-21) - New API for filters in Python [#1237](https://github.com/libgit2/pygit2/pull/1237) [#1244](https://github.com/libgit2/pygit2/pull/1244) - Shallow repositories: New `depth` optional argument for `clone_repository(...)` and `Remote.fetch(...)` [#1245](https://github.com/libgit2/pygit2/pull/1245) [#1246](https://github.com/libgit2/pygit2/pull/1246) - New submodule `init(...)`, `update(...)` and `reload(...)` functions [#1248](https://github.com/libgit2/pygit2/pull/1248) - Release GIL in `Walker.__next__` [#1249](https://github.com/libgit2/pygit2/pull/1249) - Type hints for submodule functions in `Repository` [#1247](https://github.com/libgit2/pygit2/pull/1247) # 1.13.2 (2023-10-30) - Support Python 3.12 - Documentation updates [#1242](https://github.com/libgit2/pygit2/pull/1242) # 1.13.1 (2023-09-24) - Fix crash in reference rename [#1233](https://github.com/libgit2/pygit2/issues/1233) # 1.13.0 (2023-09-07) - Upgrade to libgit2 v1.7.1 - Don\'t distribute wheels for pypy, only universal wheels for macOS - New `Repository.remotes.create_anonymous(url)` [#1229](https://github.com/libgit2/pygit2/pull/1229) - docs: update links to pypi, pygit2.org [#1228](https://github.com/libgit2/pygit2/pull/1228) - Prep work for Python 3.12 (not yet supported) [#1223](https://github.com/libgit2/pygit2/pull/1223) # 1.12.2 (2023-06-25) - Update wheels to bundle libssh2 1.11.0 and OpenSSL 3.0.9 - Remove obsolete `Remote.save()` [#1219](https://github.com/libgit2/pygit2/issues/1219) # 1.12.1 (2023-05-07) - Fix segfault in signature when encoding is incorrect [#1210](https://github.com/libgit2/pygit2/pull/1210) - Typing improvements [#1212](https://github.com/libgit2/pygit2/pull/1212) [#1214](https://github.com/libgit2/pygit2/pull/1214) - Update wheels to libgit2 v1.6.4 # 1.12.0 (2023-04-01) - Upgrade to libgit2 v1.6.3 - Update Linux wheels to bundle OpenSSL 3.0.8 - Downgrade Linux wheels to manylinux2014 - New `ConflictCollection.__contains__` [#1181](https://github.com/libgit2/pygit2/pull/1181) - New `Repository.references.iterator(...)` [#1191](https://github.com/libgit2/pygit2/pull/1191) - New `favor`, `flags` and `file_flags` optional arguments for `Repository.merge(...)` [#1192](https://github.com/libgit2/pygit2/pull/1192) - New `keep_all` and `paths` optional arguments for `Repository.stash(...)` [#1202](https://github.com/libgit2/pygit2/pull/1202) - New `Repository.state()` [#1204](https://github.com/libgit2/pygit2/pull/1204) - Improve `Repository.write_archive(...)` performance [#1183](https://github.com/libgit2/pygit2/pull/1183) - Sync type annotations [#1203](https://github.com/libgit2/pygit2/pull/1203) # 1.11.1 (2022-11-09) - Fix Linux wheels, downgrade to manylinux 2_24 [#1176](https://github.com/libgit2/pygit2/issues/1176) - Windows wheels for Python 3.11 [#1177](https://github.com/libgit2/pygit2/pull/1177) - CI: Use 3.11 final release for testing [#1178](https://github.com/libgit2/pygit2/pull/1178) # 1.11.0 (2022-11-06) - Drop support for Python 3.7 - Update Linux wheels to manylinux 2_28 [#1136](https://github.com/libgit2/pygit2/issues/1136) - Fix crash in signature representation [#1162](https://github.com/libgit2/pygit2/pull/1162) - Fix memory leak in `Signature` [#1173](https://github.com/libgit2/pygit2/pull/1173) - New optional argument `raise_error` in `Repository.applies(...)` [#1166](https://github.com/libgit2/pygit2/pull/1166) - New notify/progress callbacks for checkout and stash [#1167](https://github.com/libgit2/pygit2/pull/1167) [#1169](https://github.com/libgit2/pygit2/pull/1169) - New `Repository.remotes.names()` [#1159](https://github.com/libgit2/pygit2/pull/1159) - Now `refname` argument in `RemoteCallbacks.push_update_reference(...)` is a string, not bytes [#1168](https://github.com/libgit2/pygit2/pull/1168) - Add missing newline at end of `pygit2/decl/pack.h` [#1163](https://github.com/libgit2/pygit2/pull/1163) # 1.10.1 (2022-08-28) - Fix segfault in `Signature` repr [#1155](https://github.com/libgit2/pygit2/pull/1155) - Linux and macOS wheels for Python 3.11 [#1154](https://github.com/libgit2/pygit2/pull/1154) # 1.10.0 (2022-07-24) - Upgrade to libgit2 1.5 - Add support for `GIT_OPT_GET_OWNER_VALIDATION` and `GIT_OPT_SET_OWNER_VALIDATION` [#1150](https://github.com/libgit2/pygit2/pull/1150) - New `untracked_files` and `ignored` optional arguments for `Repository.status(...)` [#1151](https://github.com/libgit2/pygit2/pull/1151) # 1.9.2 (2022-05-24) - New `Repository.create_commit_string(...)` and `Repository.create_commit_with_signature(...)` [#1142](https://github.com/libgit2/pygit2/pull/1142) - Linux and macOS wheels updated to libgit2 v1.4.3 - Remove redundant line [#1139](https://github.com/libgit2/pygit2/pull/1139) # 1.9.1 (2022-03-22) - Type hints: added to C code and Branches/References [#1121](https://github.com/libgit2/pygit2/pull/1121) [#1132](https://github.com/libgit2/pygit2/pull/1132) - New `Signature` supports `str()` and `repr()` [#1135](https://github.com/libgit2/pygit2/pull/1135) - Fix ODB backend\'s read in big endian architectures [#1130](https://github.com/libgit2/pygit2/pull/1130) - Fix install with poetry [#1129](https://github.com/libgit2/pygit2/pull/1129) [#1128](https://github.com/libgit2/pygit2/issues/1128) - Wheels: update to libgit2 v1.4.2 - Tests: fix testing `parse_diff` [#1131](https://github.com/libgit2/pygit2/pull/1131) - CI: various fixes after migration to libgit2 v1.4 # 1.9.0 (2022-02-22) - Upgrade to libgit2 v1.4 - Documentation, new recipes for committing and cloning [#1125](https://github.com/libgit2/pygit2/pull/1125) # 1.8.0 (2022-02-04) - Rename `RemoteCallbacks.progress(...)` callback to `.sideband_progress(...)` [#1120](https://github.com/libgit2/pygit2/pull/1120) - New `Repository.merge_base_many(...)` and `Repository.merge_base_octopus(...)` [#1112](https://github.com/libgit2/pygit2/pull/1112) - New `Repository.listall_stashes()` [#1117](https://github.com/libgit2/pygit2/pull/1117) - Code cleanup [#1118](https://github.com/libgit2/pygit2/pull/1118) Backward incompatible changes: - The `RemoteCallbacks.progress(...)` callback has been renamed to `RemoteCallbacks.sideband_progress(...)`. This matches the documentation, but may break existing code that still uses the old name. # 1.7.2 (2021-12-06) - Universal wheels for macOS [#1109](https://github.com/libgit2/pygit2/pull/1109) # 1.7.1 (2021-11-19) - New `Repository.amend_commit(...)` [#1098](https://github.com/libgit2/pygit2/pull/1098) - New `Commit.message_trailers` [#1101](https://github.com/libgit2/pygit2/pull/1101) - Windows wheels for Python 3.10 [#1103](https://github.com/libgit2/pygit2/pull/1103) - Changed: now `DiffDelta.is_binary` returns `None` if the file data has not yet been loaded, cf. [#962](https://github.com/libgit2/pygit2/issues/962) - Document `Repository.get_attr(...)` and update theme [#1017](https://github.com/libgit2/pygit2/issues/1017) [#1105](https://github.com/libgit2/pygit2/pull/1105) # 1.7.0 (2021-10-08) - Upgrade to libgit2 1.3.0 [#1089](https://github.com/libgit2/pygit2/pull/1089) - Linux wheels now bundled with libssh2 1.10.0 (instead of 1.9.0) - macOS wheels now include libssh2 - Add support for Python 3.10 [#1092](https://github.com/libgit2/pygit2/pull/1092) [#1093](https://github.com/libgit2/pygit2/pull/1093) - Drop support for Python 3.6 - New [pygit2.GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES]{.title-ref} [#1087](https://github.com/libgit2/pygit2/pull/1087) - New optional argument `location` in `Repository.applies(..)` and `Repository.apply(..)` [#1091](https://github.com/libgit2/pygit2/pull/1091) - Fix: Now the [flags]{.title-ref} argument in [Repository.blame()]{.title-ref} is passed through [#1083](https://github.com/libgit2/pygit2/pull/1083) - CI: Stop using Travis, move to GitHub actions Caveats: - Windows wheels for Python 3.10 not yet available. # 1.6.1 (2021-06-19) - Fix a number of reference leaks - Review custom object backends Breaking changes: - In custom backends the callbacks have been renamed from `read` to `read_cb`, `write` to `write_cb`, and so on. # 1.6.0 (2021-06-01) - New optional `proxy` argument in `Remote` methods [#642](https://github.com/libgit2/pygit2/issues/642) [#1063](https://github.com/libgit2/pygit2/pull/1063) [#1069](https://github.com/libgit2/pygit2/issues/1069) - New GIT_MERGE_PREFERENCE constants [#1071](https://github.com/libgit2/pygit2/pull/1071) - Don\'t require cached-property with Python 3.8 or later [#1066](https://github.com/libgit2/pygit2/pull/1066) - Add wheels for aarch64 [#1077](https://github.com/libgit2/pygit2/issues/1077) [#1078](https://github.com/libgit2/pygit2/pull/1078) - Documentation fixes [#1068](https://github.com/libgit2/pygit2/pull/1068) [#1072](https://github.com/libgit2/pygit2/pull/1072) - Refactored build and CI, new `build.sh` script Breaking changes: - Remove deprecated `GIT_CREDTYPE_XXX` constants, use `GIT_CREDENTIAL_XXX` instead. - Remove deprecated `Patch.patch` getter, use `Patch.text` instead. # 1.5.0 (2021-01-23) - New `PackBuilder` class and `Repository.pack(...)` [#1048](https://github.com/libgit2/pygit2/pull/1048) - New `Config.delete_multivar(...)` [#1056](https://github.com/libgit2/pygit2/pull/1056) - New `Repository.is_shallow` [#1058](https://github.com/libgit2/pygit2/pull/1058) - New optional `message` argument in `Repository.create_reference(...)` [#1061](https://github.com/libgit2/pygit2/issues/1061) [#1062](https://github.com/libgit2/pygit2/pull/1062) - Fix truncated diff when there are nulls [#1047](https://github.com/libgit2/pygit2/pull/1047) [#1043](https://github.com/libgit2/pygit2/issues/1043) - Unit tests & Continuous integration [#1039](https://github.com/libgit2/pygit2/issues/1039) [#1052](https://github.com/libgit2/pygit2/pull/1052) Breaking changes: - Fix `Index.add(...)` raise `TypeError` instead of `AttributeError` when arguments are of unexpected type # 1.4.0 (2020-11-06) - Upgrade to libgit2 1.1, new `GIT_BLAME_IGNORE_WHITESPACE` constant [#1040](https://github.com/libgit2/pygit2/issues/1040) - Add wheels for Python 3.9 [#1038](https://github.com/libgit2/pygit2/issues/1038) - Drop support for PyPy3 7.2 - New optional `flags` argument in `Repository.__init__(...)`, new `GIT_REPOSITORY_OPEN_*` constants [#1044](https://github.com/libgit2/pygit2/pull/1044) - Documentation [#509](https://github.com/libgit2/pygit2/issues/509) [#752](https://github.com/libgit2/pygit2/issues/752) [#1037](https://github.com/libgit2/pygit2/issues/1037) [#1045](https://github.com/libgit2/pygit2/issues/1045) # 1.3.0 (2020-09-18) - New `Repository.add_submodule(...)` [#1011](https://github.com/libgit2/pygit2/pull/1011) - New `Repository.applies(...)` [#1019](https://github.com/libgit2/pygit2/pull/1019) - New `Repository.revparse(...)` and `Repository.revparse_ext(...)` [#1022](https://github.com/libgit2/pygit2/pull/1022) - New optional `flags` and `file_flags` arguments in `Repository.merge_commits` and `Repository.merge_trees` [#1008](https://github.com/libgit2/pygit2/pull/1008) - New `Reference.raw_target`, `Repository.raw_listall_branches(...)` and `Repository.raw_listall_references()`; allow bytes in `Repository.lookup_branch(...)` and `Repository.diff(...)` [#1029](https://github.com/libgit2/pygit2/pull/1029) - New `GIT_BLAME_FIRST_PARENT` and `GIT_BLAME_USE_MAILMAP` constants [#1031](https://github.com/libgit2/pygit2/pull/1031) - New `IndexEntry` supports `repr()`, `str()`, `==` and `!=` [#1009](https://github.com/libgit2/pygit2/pull/1009) - New `Object` supports `repr()` [#1022](https://github.com/libgit2/pygit2/pull/1022) - New accept tuples of strings (not only lists) in a number of places [#1025](https://github.com/libgit2/pygit2/pull/1025) - Fix compatibility with old macOS 10.9 [#1026](https://github.com/libgit2/pygit2/issues/1026) [#1027](https://github.com/libgit2/pygit2/pull/1027) - Fix check argument type in `Repository.apply(...)` [#1033](https://github.com/libgit2/pygit2/issues/1033) - Fix raise exception if error in `Repository.listall_submodules()` commit 32133974 - Fix a couple of refcount errors in `OdbBackend.refresh()` and `Worktree_is_prunable` commit fed0c19c - Unit tests [#800](https://github.com/libgit2/pygit2/issues/800) [#1015](https://github.com/libgit2/pygit2/pull/1015) - Documentation [#705](https://github.com/libgit2/pygit2/pull/705) # 1.2.1 (2020-05-01) - Fix segfault in `Object.raw_name` when not reached through a tree [#1002](https://github.com/libgit2/pygit2/pull/1002) - Internal: Use \@ffi.def_extern instead of \@ffi.callback [#899](https://github.com/libgit2/pygit2/issues/899) - Internal: callbacks code refactored - Test suite completely switched to pytest [#824](https://github.com/libgit2/pygit2/issues/824) - New unit tests [#538](https://github.com/libgit2/pygit2/pull/538) [#996](https://github.com/libgit2/pygit2/issues/996) - Documentation changes [#999](https://github.com/libgit2/pygit2/issues/999) Deprecations: - Deprecate `Repository.create_remote(...)`, use instead `Repository.remotes.create(...)` - Deprecate `GIT_CREDTYPE_XXX` constants, use `GIT_CREDENTIAL_XXX` instead. # 1.2.0 (2020-04-05) - Drop support for Python 3.5 [#991](https://github.com/libgit2/pygit2/issues/991) - Upgrade to libgit2 1.0 [#982](https://github.com/libgit2/pygit2/pull/982) - New support for custom reference database backends [#982](https://github.com/libgit2/pygit2/pull/982) - New support for path objects [#990](https://github.com/libgit2/pygit2/pull/990) [#955](https://github.com/libgit2/pygit2/issues/955) - New `index` optional parameter in `Repository.checkout_index` [#987](https://github.com/libgit2/pygit2/pull/987) - New MacOS wheels [#988](https://github.com/libgit2/pygit2/pull/988) - Fix re-raise exception from credentials callback in clone_repository [#996](https://github.com/libgit2/pygit2/issues/996) - Fix warning with `pip install pygit2` [#986](https://github.com/libgit2/pygit2/issues/986) - Tests: disable global Git config [#989](https://github.com/libgit2/pygit2/issues/989) # 1.1.1 (2020-03-06) - Fix crash in tree iteration [#984](https://github.com/libgit2/pygit2/pull/984) [#980](https://github.com/libgit2/pygit2/issues/980) - Do not include the docs in dist files, so they\'re much smaller now # 1.1.0 (2020-03-01) - Upgrade to libgit2 0.99 [#959](https://github.com/libgit2/pygit2/pull/959) - Continued work on custom odb backends [#948](https://github.com/libgit2/pygit2/pull/948) - New `Diff.patchid` getter [#960](https://github.com/libgit2/pygit2/pull/960) [#877](https://github.com/libgit2/pygit2/issues/877) - New `settings.disable_pack_keep_file_checks(...)` [#908](https://github.com/libgit2/pygit2/pull/908) - New `GIT_DIFF_` and `GIT_DELTA_` constants [#738](https://github.com/libgit2/pygit2/issues/738) - Fix crash in iteration of config entries [#970](https://github.com/libgit2/pygit2/issues/970) - Travis: fix printing features when building Linux wheels [#977](https://github.com/libgit2/pygit2/pull/977) - Move `_pygit2` to `pygit2._pygit2` [#978](https://github.com/libgit2/pygit2/pull/978) Requirements changes: - Now libgit2 0.99 is required - New requirement: cached-property Breaking changes: - In the rare case you\'re directly importing the low level `_pygit2`, the import has changed: # Before import _pygit2 # Now from pygit2 import _pygit2 # 1.0.3 (2020-01-31) - Fix memory leak in DiffFile [#943](https://github.com/libgit2/pygit2/issues/943) # 1.0.2 (2020-01-11) - Fix enumerating tree entries with submodules [#967](https://github.com/libgit2/pygit2/issues/967) # 1.0.1 (2019-12-21) - Fix build in Mac OS [#963](https://github.com/libgit2/pygit2/issues/963) # 1.0.0 (2019-12-06) - Drop Python 2.7 and 3.4 support, six no longer required [#941](https://github.com/libgit2/pygit2/issues/941) - Add Python 3.8 support [#918](https://github.com/libgit2/pygit2/issues/918) - New support for `/` operator to traverse trees [#903](https://github.com/libgit2/pygit2/pull/903) [#924](https://github.com/libgit2/pygit2/issues/924) - New `Branch.raw_branch_name` [#954](https://github.com/libgit2/pygit2/pull/954) - New `Index.remove_all()` [#920](https://github.com/libgit2/pygit2/pull/920) - New `Remote.ls_remotes(..)` [#935](https://github.com/libgit2/pygit2/pull/935) [#936](https://github.com/libgit2/pygit2/issues/936) - New `Repository.lookup_reference_dwim(..)` and `Repository.resolve_refish(..)` [#922](https://github.com/libgit2/pygit2/issues/922) [#923](https://github.com/libgit2/pygit2/pull/923) - New `Repository.odb` returns new `Odb` type instance. And new `OdbBackend` type. [#940](https://github.com/libgit2/pygit2/pull/940) [#942](https://github.com/libgit2/pygit2/pull/942) - New `Repository.references.compress()` [#961](https://github.com/libgit2/pygit2/pull/961) - Optimization: Load notes lazily [#958](https://github.com/libgit2/pygit2/pull/958) - Fix spurious exception in config [#916](https://github.com/libgit2/pygit2/issues/916) [#917](https://github.com/libgit2/pygit2/pull/917) - Minor documentation and cosmetic changes [#919](https://github.com/libgit2/pygit2/pull/919) [#921](https://github.com/libgit2/pygit2/pull/921) [#946](https://github.com/libgit2/pygit2/pull/946) [#950](https://github.com/libgit2/pygit2/pull/950) Breaking changes: - Now the Repository has a new attribute `odb` for object database: # Before repository.read(...) repository.write(...) # Now repository.odb.read(...) repository.odb.write(...) - Now `Tree[x]` returns a `Object` instance instead of a `TreeEntry`; `Object.type` returns an integer while `TreeEntry.type` returned a string: # Before if tree[x].type == 'tree': # Now if tree[x].type == GIT_OBJ_TREE: if tree[x].type_str == 'tree': - Renamed `TreeEntry._name` to `Object.raw_name`: # Before tree[x]._name # Now tree[x].raw_name - Object comparison is done by id. In the rare case you need to do tree-entry comparison or sorting: # Before tree[x] < tree[y] sorted(list(tree)) # Now pygit2.tree_entry_cmp(x, y) < 0 sorted(list(tree), key=pygit2.tree_entry_key) # 0.28.2 (2019-05-26) - Fix crash in reflog iteration [#901](https://github.com/libgit2/pygit2/issues/901) - Support symbolic references in `branches.with_commit(..)` [#910](https://github.com/libgit2/pygit2/issues/910) - Documentation updates [#909](https://github.com/libgit2/pygit2/pull/909) - Test updates [#911](https://github.com/libgit2/pygit2/pull/911) # 0.28.1 (2019-04-19) - Now works with pycparser 2.18 and above [#846](https://github.com/libgit2/pygit2/issues/846) - Now `Repository.write_archive(..)` keeps the file mode [#616](https://github.com/libgit2/pygit2/issues/616) [#898](https://github.com/libgit2/pygit2/pull/898) - New `Patch.data` returns the raw contents of the patch as a byte string [#790](https://github.com/libgit2/pygit2/pull/790) [#893](https://github.com/libgit2/pygit2/pull/893) - New `Patch.text` returns the contents of the patch as a text string, deprecates [Patch.patch]{.title-ref} [#790](https://github.com/libgit2/pygit2/pull/790) [#893](https://github.com/libgit2/pygit2/pull/893) Deprecations: - `Patch.patch` is deprecated, use `Patch.text` instead # 0.28.0 (2019-03-19) - Upgrade to libgit2 0.28 [#878](https://github.com/libgit2/pygit2/issues/878) - Add binary wheels for Linux [#793](https://github.com/libgit2/pygit2/issues/793) [#869](https://github.com/libgit2/pygit2/pull/869) [#874](https://github.com/libgit2/pygit2/pull/874) [#875](https://github.com/libgit2/pygit2/pull/875) [#883](https://github.com/libgit2/pygit2/pull/883) - New `pygit2.Mailmap`, see documentation [#804](https://github.com/libgit2/pygit2/pull/804) - New `Repository.apply(...)` wraps `git_apply(..)` [#841](https://github.com/libgit2/pygit2/issues/841) [#843](https://github.com/libgit2/pygit2/pull/843) - Now `Repository.merge_analysis(...)` accepts an optional reference parameter [#888](https://github.com/libgit2/pygit2/pull/888) [#891](https://github.com/libgit2/pygit2/pull/891) - Now `Repository.add_worktree(...)` accepts an optional reference parameter [#814](https://github.com/libgit2/pygit2/issues/814) [#889](https://github.com/libgit2/pygit2/pull/889) - Now it\'s possible to set SSL certificate locations [#876](https://github.com/libgit2/pygit2/issues/876) [#879](https://github.com/libgit2/pygit2/pull/879) [#884](https://github.com/libgit2/pygit2/pull/884) [#886](https://github.com/libgit2/pygit2/pull/886) - Test and documentation improvements [#873](https://github.com/libgit2/pygit2/pull/873) [#887](https://github.com/libgit2/pygit2/pull/887) Breaking changes: - Now `worktree.path` returns the path to the worktree directory, not to the [.git]{.title-ref} file within [#803](https://github.com/libgit2/pygit2/issues/803) - Remove undocumented `worktree.git_path` [#803](https://github.com/libgit2/pygit2/issues/803) # 0.27.4 (2019-01-19) - New `pygit2.LIBGIT2_VER` tuple [#845](https://github.com/libgit2/pygit2/issues/845) [#848](https://github.com/libgit2/pygit2/pull/848) - New objects now support (in)equality comparison and hash [#852](https://github.com/libgit2/pygit2/issues/852) [#853](https://github.com/libgit2/pygit2/pull/853) - New references now support (in)equality comparison [#860](https://github.com/libgit2/pygit2/issues/860) [#862](https://github.com/libgit2/pygit2/pull/862) - New `paths` optional argument in `Repository.checkout()` [#858](https://github.com/libgit2/pygit2/issues/858) [#859](https://github.com/libgit2/pygit2/pull/859) - Fix speed and windows package regression [#849](https://github.com/libgit2/pygit2/issues/849) [#857](https://github.com/libgit2/pygit2/issues/857) [#851](https://github.com/libgit2/pygit2/pull/851) - Fix deprecation warning [#850](https://github.com/libgit2/pygit2/pull/850) - Documentation fixes [#855](https://github.com/libgit2/pygit2/pull/855) - Add Python classifiers to setup.py [#861](https://github.com/libgit2/pygit2/pull/861) - Speeding up tests in Travis [#854](https://github.com/libgit2/pygit2/pull/854) Breaking changes: - Remove deprecated [Reference.get_object()]{.title-ref}, use [Reference.peel()]{.title-ref} instead # 0.27.3 (2018-12-15) - Move to pytest, drop support for Python 3.3 and cffi 0.x [#824](https://github.com/libgit2/pygit2/issues/824) [#826](https://github.com/libgit2/pygit2/pull/826) [#833](https://github.com/libgit2/pygit2/pull/833) [#834](https://github.com/libgit2/pygit2/pull/834) - New support comparing signatures for (in)equality - New `Submodule.head_id` [#817](https://github.com/libgit2/pygit2/pull/817) - New `Remote.prune(...)` [#825](https://github.com/libgit2/pygit2/pull/825) - New `pygit2.reference_is_valid_name(...)` [#827](https://github.com/libgit2/pygit2/pull/827) - New `AlreadyExistsError` and `InvalidSpecError` [#828](https://github.com/libgit2/pygit2/issues/828) [#829](https://github.com/libgit2/pygit2/pull/829) - New `Reference.raw_name`, `Reference.raw_shorthand`, `Tag.raw_name`, `Tag.raw_message` and `DiffFile.raw_path` [#840](https://github.com/libgit2/pygit2/pull/840) - Fix decode error in commit messages and signatures [#839](https://github.com/libgit2/pygit2/issues/839) - Fix, raise error in `Repository.descendant_of(...)` if commit doesn\'t exist [#822](https://github.com/libgit2/pygit2/issues/822) [#842](https://github.com/libgit2/pygit2/pull/842) - Documentation fixes [#821](https://github.com/libgit2/pygit2/pull/821) Breaking changes: - Remove undocumented `Tag._message`, replaced by `Tag.raw_message` # 0.27.2 (2018-09-16) - Add support for Python 3.7 [#809](https://github.com/libgit2/pygit2/issues/809) - New `Object.short_id` [#799](https://github.com/libgit2/pygit2/issues/799) [#806](https://github.com/libgit2/pygit2/pull/806) [#807](https://github.com/libgit2/pygit2/pull/807) - New `Repository.descendant_of` and `Repository.branches.with_commit` [#815](https://github.com/libgit2/pygit2/issues/815) [#816](https://github.com/libgit2/pygit2/pull/816) - Fix repository initialization in `clone_repository(...)` [#818](https://github.com/libgit2/pygit2/issues/818) - Fix several warnings and errors, commits [cd896ddc](https://github.com/libgit2/pygit2/commit/cd896ddc) and [dfa536a3](https://github.com/libgit2/pygit2/commit/dfa536a3) - Documentation fixes and improvements [#805](https://github.com/libgit2/pygit2/pull/805) [#808](https://github.com/libgit2/pygit2/pull/808) # 0.27.1 (2018-06-02) Breaking changes: - Now `discover_repository` returns `None` if repository not found, instead of raising `KeyError` [#531](https://github.com/libgit2/pygit2/issues/531) Other changes: - New `DiffLine.raw_content` [#610](https://github.com/libgit2/pygit2/issues/610) - Fix tests failing in some cases [#795](https://github.com/libgit2/pygit2/issues/795) - Automate wheels upload to pypi [#563](https://github.com/libgit2/pygit2/issues/563) # 0.27.0 (2018-03-30) - Update to libgit2 v0.27 [#783](https://github.com/libgit2/pygit2/pull/783) - Fix for GCC 4 [#786](https://github.com/libgit2/pygit2/pull/786) # 0.26.4 (2018-03-23) Backward incompatible changes: - Now iterating over a configuration returns `ConfigEntry` objects [#778](https://github.com/libgit2/pygit2/pull/778) # Before for name in config: value = config[name] # Now for entry in config: name = entry.name value = entry.value Other changes: - Added support for worktrees [#779](https://github.com/libgit2/pygit2/pull/779) - New `Commit.gpg_signature` [#766](https://github.com/libgit2/pygit2/pull/766) - New static `Diff.parse_diff(...)` [#774](https://github.com/libgit2/pygit2/pull/774) - New optional argument `callbacks` in `Repository.update_submodules(...)` [#763](https://github.com/libgit2/pygit2/pull/763) - New `KeypairFromMemory` credentials [#771](https://github.com/libgit2/pygit2/pull/771) - Add missing status constants [#781](https://github.com/libgit2/pygit2/issues/781) - Fix segfault [#775](https://github.com/libgit2/pygit2/issues/775) - Fix some unicode decode errors with Python 2 [#767](https://github.com/libgit2/pygit2/pull/767) [#768](https://github.com/libgit2/pygit2/pull/768) - Documentation improvements [#721](https://github.com/libgit2/pygit2/pull/721) [#769](https://github.com/libgit2/pygit2/pull/769) [#770](https://github.com/libgit2/pygit2/pull/770) # 0.26.3 (2017-12-24) - New `Diff.deltas` [#736](https://github.com/libgit2/pygit2/issues/736) - Improvements to `Patch.create_from` [#753](https://github.com/libgit2/pygit2/pull/753) [#756](https://github.com/libgit2/pygit2/pull/756) [#759](https://github.com/libgit2/pygit2/pull/759) - Fix build and tests in Windows, broken in the previous release [#749](https://github.com/libgit2/pygit2/pull/749) [#751](https://github.com/libgit2/pygit2/pull/751) - Review `Patch.patch` [#757](https://github.com/libgit2/pygit2/issues/757) - Workaround bug [#4442](https://github.com/libgit2/libgit2/issues/4442) in libgit2, and improve unit tests [#748](https://github.com/libgit2/pygit2/issues/748) [#754](https://github.com/libgit2/pygit2/issues/754) [#758](https://github.com/libgit2/pygit2/pull/758) [#761](https://github.com/libgit2/pygit2/pull/761) # 0.26.2 (2017-12-01) - New property `Patch.patch` [#739](https://github.com/libgit2/pygit2/issues/739) [#741](https://github.com/libgit2/pygit2/pull/741) - New static method `Patch.create_from` [#742](https://github.com/libgit2/pygit2/issues/742) [#744](https://github.com/libgit2/pygit2/pull/744) - New parameter `prune` in `Remote.fetch` [#743](https://github.com/libgit2/pygit2/pull/743) - Tests: skip tests that require network when there is not [#737](https://github.com/libgit2/pygit2/issues/737) - Tests: other improvements [#740](https://github.com/libgit2/pygit2/pull/740) - Documentation improvements # 0.26.1 (2017-11-19) - New `Repository.free()` [#730](https://github.com/libgit2/pygit2/pull/730) - Improve credentials handling for ssh cloning [#718](https://github.com/libgit2/pygit2/pull/718) - Documentation improvements [#714](https://github.com/libgit2/pygit2/pull/714) [#715](https://github.com/libgit2/pygit2/pull/715) [#728](https://github.com/libgit2/pygit2/pull/728) [#733](https://github.com/libgit2/pygit2/pull/733) [#734](https://github.com/libgit2/pygit2/pull/734) [#735](https://github.com/libgit2/pygit2/pull/735) # 0.26.0 (2017-07-06) - Update to libgit2 v0.26 [#713](https://github.com/libgit2/pygit2/pull/713) - Drop support for Python 3.2, add support for cffi 1.10 [#706](https://github.com/libgit2/pygit2/pull/706) [#694](https://github.com/libgit2/pygit2/issues/694) - New `Repository.revert_commit(...)` [#711](https://github.com/libgit2/pygit2/pull/711) [#710](https://github.com/libgit2/pygit2/issues/710) - New `Branch.is_checked_out()` [#696](https://github.com/libgit2/pygit2/pull/696) - Various fixes [#706](https://github.com/libgit2/pygit2/pull/706) [#707](https://github.com/libgit2/pygit2/pull/707) [#708](https://github.com/libgit2/pygit2/pull/708) # 0.25.1 (2017-04-25) - Add support for Python 3.6 - New support for stash: repository methods `stash`, `stash_apply`, `stash_drop` and `stash_pop` [#695](https://github.com/libgit2/pygit2/pull/695) - Improved support for submodules: new repository methods `init_submodules` and `update_submodules` [#692](https://github.com/libgit2/pygit2/pull/692) - New friendlier API for branches & references: `Repository.branches` and `Repository.references` [#700](https://github.com/libgit2/pygit2/pull/700) [#701](https://github.com/libgit2/pygit2/pull/701) - New support for custom backends [#690](https://github.com/libgit2/pygit2/pull/690) - Fix `init_repository` crash on None input [#688](https://github.com/libgit2/pygit2/issues/688) [#697](https://github.com/libgit2/pygit2/pull/697) - Fix checkout with an orphan master branch [#669](https://github.com/libgit2/pygit2/issues/669) [#685](https://github.com/libgit2/pygit2/pull/685) - Better error messages for opening repositories [#645](https://github.com/libgit2/pygit2/issues/645) [#698](https://github.com/libgit2/pygit2/pull/698) # 0.25.0 (2016-12-26) - Upgrade to libgit2 0.25 [#670](https://github.com/libgit2/pygit2/pull/670) - Now Commit.tree raises an error if tree is not found [#682](https://github.com/libgit2/pygit2/pull/682) - New settings.mwindow_mapped_limit, cached_memory, enable_caching, cache_max_size and cache_object_limit [#677](https://github.com/libgit2/pygit2/pull/677) # 0.24.2 (2016-11-01) - Unit tests pass on Windows, integration with AppVeyor [#641](https://github.com/libgit2/pygit2/pull/641) [#655](https://github.com/libgit2/pygit2/issues/655) [#657](https://github.com/libgit2/pygit2/pull/657) [#659](https://github.com/libgit2/pygit2/pull/659) [#660](https://github.com/libgit2/pygit2/pull/660) [#661](https://github.com/libgit2/pygit2/pull/661) [#667](https://github.com/libgit2/pygit2/pull/667) - Fix when libgit2 error messages have non-ascii chars [#651](https://github.com/libgit2/pygit2/pull/651) - Documentation improvements [#643](https://github.com/libgit2/pygit2/pull/643) [#653](https://github.com/libgit2/pygit2/pull/653) [#663](https://github.com/libgit2/pygit2/pull/663) # 0.24.1 (2016-06-21) - New `Repository.listall_reference_objects()` [#634](https://github.com/libgit2/pygit2/pull/634) - Fix `Repository.write_archive(...)` [#619](https://github.com/libgit2/pygit2/pull/619) [#621](https://github.com/libgit2/pygit2/pull/621) - Reproducible builds [#636](https://github.com/libgit2/pygit2/pull/636) - Documentation fixes [#606](https://github.com/libgit2/pygit2/pull/606) [#607](https://github.com/libgit2/pygit2/pull/607) [#609](https://github.com/libgit2/pygit2/pull/609) [#623](https://github.com/libgit2/pygit2/pull/623) - Test updates [#629](https://github.com/libgit2/pygit2/pull/629) # 0.24.0 (2016-03-05) - Update to libgit2 v0.24 [#594](https://github.com/libgit2/pygit2/pull/594) - Support Python 3.5 - New dependency, [six](https://pypi.org/project/six/) - New `Repository.path_is_ignored(path)` [#589](https://github.com/libgit2/pygit2/pull/589) - Fix error in `Repository(path)` when path is a bytes string [#588](https://github.com/libgit2/pygit2/issues/588) [#593](https://github.com/libgit2/pygit2/pull/593) - Fix memory issue in `Repository.describe(...)` [#592](https://github.com/libgit2/pygit2/issues/592) [#597](https://github.com/libgit2/pygit2/issues/597) [#599](https://github.com/libgit2/pygit2/pull/599) - Allow testing with [tox](https://pypi.org/project/tox/) [#600](https://github.com/libgit2/pygit2/pull/600) # 0.23.3 (2016-01-01) - New `Repository.create_blob_fromiobase(...)` [#490](https://github.com/libgit2/pygit2/pull/490) [#577](https://github.com/libgit2/pygit2/pull/577) - New `Repository.describe(...)` [#585](https://github.com/libgit2/pygit2/pull/585) - Fix `Signature` default encoding, UTF-8 now [#581](https://github.com/libgit2/pygit2/issues/581) - Fixing `pip install pygit2`, should install cffi first - Unit tests, fix binary diff test [#586](https://github.com/libgit2/pygit2/pull/586) - Document that `Diff.patch` can be `None` [#587](https://github.com/libgit2/pygit2/pull/587) # 0.23.2 (2015-10-11) - Unify callbacks system for remotes and clone [#568](https://github.com/libgit2/pygit2/pull/568) - New `TreeEntry._name` [#570](https://github.com/libgit2/pygit2/pull/570) - Fix segfault in `Tag._message` [#572](https://github.com/libgit2/pygit2/pull/572) - Documentation improvements [#569](https://github.com/libgit2/pygit2/pull/569) [#574](https://github.com/libgit2/pygit2/pull/574) API changes to clone: # Before clone_repository(..., credentials, certificate) # Now callbacks = RemoteCallbacks(credentials, certificate) clone_repository(..., callbacks) API changes to remote: # Before def transfer_progress(stats): ... remote.credentials = credentials remote.transfer_progress = transfer_progress remote.fetch() remote.push(specs) # Now class MyCallbacks(RemoteCallbacks): def transfer_progress(self, stats): ... callbacks = MyCallbacks(credentials) remote.fetch(callbacks=callbacks) remote.push(specs, callbacks=callbacks) # 0.23.1 (2015-09-26) - Improve support for cffi 1.0+ [#529](https://github.com/libgit2/pygit2/pull/529) [#561](https://github.com/libgit2/pygit2/pull/561) - Fix `Remote.push` [#557](https://github.com/libgit2/pygit2/pull/557) - New `TreeEntry.type` [#560](https://github.com/libgit2/pygit2/pull/560) - New `pygit2.GIT_DIFF_SHOW_BINARY` [#566](https://github.com/libgit2/pygit2/pull/566) # 0.23.0 (2015-08-14) - Update to libgit2 v0.23 [#540](https://github.com/libgit2/pygit2/pull/540) - Now `Repository.merge_base(...)` returns `None` if no merge base is found [#550](https://github.com/libgit2/pygit2/pull/550) - Documentation updates [#547](https://github.com/libgit2/pygit2/pull/547) API changes: - How to set identity (aka signature) in a reflog has changed: # Before signature = Signature('foo', 'bar') ... reference.set_target(target, signature=signature, message=message) repo.set_head(target, signature=signature) remote.fetch(signature=signature) remote.push(signature=signature) # Now repo.set_ident('foo', 'bar') ... reference.set_target(target, message=message) repo.set_head(target) remote.push() # The current identity can be get with repo.ident - Some remote setters have been replaced by methods: # Before # Now Remote.url = url Repository.remotes.set_url(name, url) Remote.push_url = url Repository.remotes.set_push_url(name, url) Remote.add_fetch(refspec) Repository.remotes.add_fetch(name, refspec) Remote.add_push(refspec) Repository.remotes.add_push(name, refspec) Remote.fetch_refspecs = [...] removed, use the config API instead Remote.push_refspecs = [...] removed, use the config API instead # 0.22.1 (2015-07-12) Diff interface refactoring [#346](https://github.com/libgit2/pygit2/pull/346) (in progress): - New `iter(pygit2.Blame)` - New `pygit2.DiffDelta`, `pygit2.DiffFile` and `pygit.DiffLine` - API changes, translation table: Hunk => DiffHunk Patch.old_file_path => Patch.delta.old_file.path Patch.new_file_path => Patch.delta.new_file.path Patch.old_id => Patch.delta.old_file.id Patch.new_id => Patch.delta.new_file.id Patch.status => Patch.delta.status Patch.similarity => Patch.delta.similarity Patch.is_binary => Patch.delta.is_binary Patch.additions => Patch.line_stats[1] Patch.deletions => Patch.line_stats[2] - `DiffHunk.lines` is now a list of `DiffLine` objects, not tuples New features: - New `Repository.expand_id(...)` and `Repository.ahead_behind(...)` [#448](https://github.com/libgit2/pygit2/pull/448) - New `prefix` parameter in `Repository.write_archive` [#481](https://github.com/libgit2/pygit2/pull/481) - New `Repository.merge_trees(...)` [#489](https://github.com/libgit2/pygit2/pull/489) - New `Repository.cherrypick(...)` [#436](https://github.com/libgit2/pygit2/issues/436) [#492](https://github.com/libgit2/pygit2/pull/492) - New support for submodules [#499](https://github.com/libgit2/pygit2/pull/499) [#514](https://github.com/libgit2/pygit2/pull/514) - New `Repository.merge_file_from_index(...)` [#503](https://github.com/libgit2/pygit2/pull/503) - Now `Repository.diff` supports diffing two blobs [#508](https://github.com/libgit2/pygit2/pull/508) - New optional `fetch` parameter in `Remote.create` [#526](https://github.com/libgit2/pygit2/pull/526) - New `pygit2.DiffStats` [#406](https://github.com/libgit2/pygit2/issues/406) [#525](https://github.com/libgit2/pygit2/pull/525) - New `Repository.get_attr(...)` [#528](https://github.com/libgit2/pygit2/pull/528) - New `level` optional parameter in `Index.remove` [#533](https://github.com/libgit2/pygit2/pull/533) - New `repr(TreeEntry)` [#543](https://github.com/libgit2/pygit2/pull/543) Build and install improvements: - Make pygit work in a frozen environment [#453](https://github.com/libgit2/pygit2/pull/453) - Make pygit2 work with pyinstaller [#510](https://github.com/libgit2/pygit2/pull/510) Bugs fixed: - Fix memory issues [#477](https://github.com/libgit2/pygit2/issues/477) [#487](https://github.com/libgit2/pygit2/pull/487) [#520](https://github.com/libgit2/pygit2/pull/520) - Fix TreeEntry equality testing [#458](https://github.com/libgit2/pygit2/issues/458) [#488](https://github.com/libgit2/pygit2/pull/488) - `Repository.write_archive` fix handling of symlinks [#480](https://github.com/libgit2/pygit2/pull/480) - Fix type check in `Diff[...]` [#495](https://github.com/libgit2/pygit2/issues/495) - Fix error when merging files with unicode content [#505](https://github.com/libgit2/pygit2/pull/505) Other: - Documentation improvements and fixes [#448](https://github.com/libgit2/pygit2/pull/448) [#491](https://github.com/libgit2/pygit2/pull/491) [#497](https://github.com/libgit2/pygit2/pull/497) [#507](https://github.com/libgit2/pygit2/pull/507) [#517](https://github.com/libgit2/pygit2/pull/517) [#518](https://github.com/libgit2/pygit2/pull/518) [#519](https://github.com/libgit2/pygit2/pull/519) [#521](https://github.com/libgit2/pygit2/pull/521) [#523](https://github.com/libgit2/pygit2/pull/523) [#527](https://github.com/libgit2/pygit2/pull/527) [#536](https://github.com/libgit2/pygit2/pull/536) - Expose the `pygit2.GIT_REPOSITORY_INIT_*` constants [#483](https://github.com/libgit2/pygit2/issues/483) # 0.22.0 (2015-01-16) New: - Update to libgit2 v0.22 [#459](https://github.com/libgit2/pygit2/pull/459) - Add support for libgit2 feature detection (new `pygit2.features` and `pygit2.GIT_FEATURE_*`) [#475](https://github.com/libgit2/pygit2/pull/475) - New `Repository.remotes` (`RemoteCollection`) [#447](https://github.com/libgit2/pygit2/pull/447) API Changes: - Prototype of `clone_repository` changed, check documentation - Removed `clone_into`, use `clone_repository` with callbacks instead - Use `Repository.remotes.rename(name, new_name)` instead of `Remote.rename(new_name)` - Use `Repository.remotes.delete(name)` instead of `Remote.delete()` - Now `Remote.push(...)` takes a list of refspecs instead of just one - Change `Patch.old_id`, `Patch.new_id`, `Note.annotated_id`, `RefLogEntry.oid_old` and `RefLogEntry.oid_new` to be `Oid` objects instead of strings [#449](https://github.com/libgit2/pygit2/pull/449) Other: - Fix `init_repository` when passing optional parameters `workdir_path`, `description`, `template_path`, `initial_head` or `origin_url` [#466](https://github.com/libgit2/pygit2/issues/466) [#471](https://github.com/libgit2/pygit2/pull/471) - Fix use-after-free when patch outlives diff [#457](https://github.com/libgit2/pygit2/issues/457) [#461](https://github.com/libgit2/pygit2/pull/461) [#474](https://github.com/libgit2/pygit2/pull/474) - Documentation improvements [#456](https://github.com/libgit2/pygit2/issues/456) [#462](https://github.com/libgit2/pygit2/pull/462) [#465](https://github.com/libgit2/pygit2/pull/465) [#472](https://github.com/libgit2/pygit2/pull/472) [#473](https://github.com/libgit2/pygit2/pull/473) - Make the GPL exception explicit in setup.py [#450](https://github.com/libgit2/pygit2/pull/450) # 0.21.4 (2014-11-04) - Fix credentials callback not set when pushing [#431](https://github.com/libgit2/pygit2/pull/431) [#435](https://github.com/libgit2/pygit2/issues/435) [#437](https://github.com/libgit2/pygit2/issues/437) [#438](https://github.com/libgit2/pygit2/pull/438) - Fix `Repository.diff(...)` when treeish is \"empty\" [#432](https://github.com/libgit2/pygit2/issues/432) - New `Reference.peel(...)` renders `Reference.get_object()` obsolete [#434](https://github.com/libgit2/pygit2/pull/434) - New, authenticate using ssh agent [#424](https://github.com/libgit2/pygit2/pull/424) - New `Repository.merge_commits(...)` [#445](https://github.com/libgit2/pygit2/pull/445) - Make it easier to run when libgit2 not in a standard location [#441](https://github.com/libgit2/pygit2/issues/441) - Documentation: review install chapter - Documentation: many corrections [#427](https://github.com/libgit2/pygit2/pull/427) [#429](https://github.com/libgit2/pygit2/pull/429) [#439](https://github.com/libgit2/pygit2/pull/439) [#440](https://github.com/libgit2/pygit2/pull/440) [#442](https://github.com/libgit2/pygit2/pull/442) [#443](https://github.com/libgit2/pygit2/pull/443) [#444](https://github.com/libgit2/pygit2/pull/444) # 0.21.3 (2014-09-15) Breaking changes: - Now `Repository.blame(...)` returns `Oid` instead of string [#413](https://github.com/libgit2/pygit2/pull/413) - New `Reference.set_target(...)` replaces the `Reference.target` setter and `Reference.log_append(...)` [#414](https://github.com/libgit2/pygit2/pull/414) - New `Repository.set_head(...)` replaces the `Repository.head` setter [#414](https://github.com/libgit2/pygit2/pull/414) - `Repository.merge(...)` now uses the `SAFE_CREATE` strategy by default [#417](https://github.com/libgit2/pygit2/pull/417) Other changes: - New `Remote.delete()` [#418](https://github.com/libgit2/pygit2/issues/418) [#420](https://github.com/libgit2/pygit2/pull/420) - New `Repository.write_archive(...)` [#421](https://github.com/libgit2/pygit2/pull/421) - Now `Repository.checkout(...)` accepts branch objects [#408](https://github.com/libgit2/pygit2/pull/408) - Fix refcount leak in remotes [#403](https://github.com/libgit2/pygit2/issues/403) [#404](https://github.com/libgit2/pygit2/pull/404) [#419](https://github.com/libgit2/pygit2/pull/419) - Various fixes to `clone_repository(...)` [#399](https://github.com/libgit2/pygit2/issues/399) [#411](https://github.com/libgit2/pygit2/pull/411) [#425](https://github.com/libgit2/pygit2/issues/425) [#426](https://github.com/libgit2/pygit2/pull/426) - Fix build error in Python 3 [#401](https://github.com/libgit2/pygit2/pull/401) - Now `pip install pygit2` installs cffi first [#380](https://github.com/libgit2/pygit2/issues/380) [#407](https://github.com/libgit2/pygit2/pull/407) - Add support for PyPy3 [#422](https://github.com/libgit2/pygit2/pull/422) - Documentation improvements [#398](https://github.com/libgit2/pygit2/pull/398) [#409](https://github.com/libgit2/pygit2/pull/409) # 0.21.2 (2014-08-09) - Fix regression with Python 2, `IndexEntry.path` returns str (bytes in Python 2 and unicode in Python 3) - Get back `IndexEntry.oid` for backwards compatibility - Config, iterate over the keys (instead of the key/value pairs) [#395](https://github.com/libgit2/pygit2/pull/395) - `Diff.find_similar` supports new threshold arguments [#396](https://github.com/libgit2/pygit2/pull/396) - Optimization, do not load the object when expanding an oid prefix [#397](https://github.com/libgit2/pygit2/pull/397) # 0.21.1 (2014-07-22) - Install fix [#382](https://github.com/libgit2/pygit2/pull/382) - Documentation improved, including [#383](https://github.com/libgit2/pygit2/pull/383) [#385](https://github.com/libgit2/pygit2/pull/385) [#388](https://github.com/libgit2/pygit2/pull/388) - Documentation, use the read-the-docs theme [#387](https://github.com/libgit2/pygit2/pull/387) - Coding style improvements [#392](https://github.com/libgit2/pygit2/pull/392) - New `Repository.state_cleanup()` [#386](https://github.com/libgit2/pygit2/pull/386) - New `Index.conflicts` [#345](https://github.com/libgit2/pygit2/issues/345) [#389](https://github.com/libgit2/pygit2/pull/389) - New checkout option to define the target directory [#390](https://github.com/libgit2/pygit2/pull/390) Backward incompatible changes: - Now the checkout strategy must be a keyword argument. Change `Repository.checkout(refname, strategy)` to `Repository.checkout(refname, strategy=strategy)` Idem for `checkout_head`, `checkout_index` and `checkout_tree` # 0.21.0 (2014-06-27) Highlights: - Drop official support for Python 2.6, and add support for Python 3.4 [#376](https://github.com/libgit2/pygit2/pull/376) - Upgrade to libgit2 v0.21.0 [#374](https://github.com/libgit2/pygit2/pull/374) - Start using cffi [#360](https://github.com/libgit2/pygit2/pull/360) [#361](https://github.com/libgit2/pygit2/pull/361) Backward incompatible changes: - Replace `oid` by `id` through the API to follow libgit2 conventions. - Merge API overhaul following changes in libgit2. - New `Remote.rename(...)` replaces `Remote.name = ...` - Now `Remote.fetch()` returns a `TransferProgress` object. - Now `Config.get_multivar(...)` returns an iterator instead of a list. New features: - New `Config.snapshot()` and `Repository.config_snapshot()` - New `Config` methods: `get_bool(...)`, `get_int(...)`, `parse_bool(...)` and `parse_int(...)` [#357](https://github.com/libgit2/pygit2/pull/357) - Blob: implement the memory buffer interface [#362](https://github.com/libgit2/pygit2/pull/362) - New `clone_into(...)` function [#368](https://github.com/libgit2/pygit2/pull/368) - Now `Index` can be used alone, without a repository [#372](https://github.com/libgit2/pygit2/pull/372) - Add more options to `init_repository` [#347](https://github.com/libgit2/pygit2/pull/347) - Support `Repository.workdir = ...` and support setting detached heads `Repository.head = ` [#377](https://github.com/libgit2/pygit2/pull/377) Other: - Fix again build with VS2008 [#364](https://github.com/libgit2/pygit2/pull/364) - Fix `Blob.diff(...)` and `Blob.diff_to_buffer(...)` arguments passing [#366](https://github.com/libgit2/pygit2/pull/366) - Fail gracefully when compiling against the wrong version of libgit2 [#365](https://github.com/libgit2/pygit2/pull/365) - Several documentation improvements and updates [#359](https://github.com/libgit2/pygit2/pull/359) [#375](https://github.com/libgit2/pygit2/pull/375) [#378](https://github.com/libgit2/pygit2/pull/378) # 0.20.3 (2014-04-02) - A number of memory issues fixed [#328](https://github.com/libgit2/pygit2/pull/328) [#348](https://github.com/libgit2/pygit2/pull/348) [#353](https://github.com/libgit2/pygit2/pull/353) [#355](https://github.com/libgit2/pygit2/pull/355) [#356](https://github.com/libgit2/pygit2/pull/356) - Compatibility fixes for PyPy ([#338](https://github.com/libgit2/pygit2/pull/338)), Visual Studio 2008 ([#343](https://github.com/libgit2/pygit2/pull/343)) and Python 3.3 ([#351](https://github.com/libgit2/pygit2/pull/351)) - Make the sort mode parameter in `Repository.walk(...)` optional [#337](https://github.com/libgit2/pygit2/pull/337) - New `Object.peel(...)` [#342](https://github.com/libgit2/pygit2/pull/342) - New `Index.add_all(...)` [#344](https://github.com/libgit2/pygit2/pull/344) - Introduce support for libgit2 options [#350](https://github.com/libgit2/pygit2/pull/350) - More informative repr for `Repository` objects [#352](https://github.com/libgit2/pygit2/pull/352) - Introduce support for credentials [#354](https://github.com/libgit2/pygit2/pull/354) - Several documentation fixes [#302](https://github.com/libgit2/pygit2/issues/302) [#336](https://github.com/libgit2/pygit2/issues/336) - Tests, remove temporary files [#341](https://github.com/libgit2/pygit2/pull/341) # 0.20.2 (2014-02-04) - Support PyPy [#209](https://github.com/libgit2/pygit2/issues/209) [#327](https://github.com/libgit2/pygit2/pull/327) [#333](https://github.com/libgit2/pygit2/pull/333) Repository: - New `Repository.default_signature` [#310](https://github.com/libgit2/pygit2/pull/310) Oid: - New `str(Oid)` deprecates `Oid.hex` [#322](https://github.com/libgit2/pygit2/pull/322) Object: - New `Object.id` deprecates `Object.oid` [#322](https://github.com/libgit2/pygit2/pull/322) - New `TreeEntry.id` deprecates `TreeEntry.oid` [#322](https://github.com/libgit2/pygit2/pull/322) - New `Blob.diff(...)` and `Blob.diff_to_buffer(...)` [#307](https://github.com/libgit2/pygit2/pull/307) - New `Commit.tree_id` and `Commit.parent_ids` [#73](https://github.com/libgit2/pygit2/issues/73) [#311](https://github.com/libgit2/pygit2/pull/311) - New rich comparison between tree entries [#305](https://github.com/libgit2/pygit2/issues/305) [#313](https://github.com/libgit2/pygit2/pull/313) - Now `Tree.__contains__(key)` supports paths [#306](https://github.com/libgit2/pygit2/issues/306) [#316](https://github.com/libgit2/pygit2/pull/316) Index: - Now possible to create `IndexEntry(...)` [#325](https://github.com/libgit2/pygit2/pull/325) - Now `IndexEntry.path`, `IndexEntry.oid` and `IndexEntry.mode` are writable [#325](https://github.com/libgit2/pygit2/pull/325) - Now `Index.add(...)` accepts an `IndexEntry` too [#325](https://github.com/libgit2/pygit2/pull/325) - Now `Index.write_tree(...)` is able to write to a different repository [#325](https://github.com/libgit2/pygit2/pull/325) - Fix memory leak in `IndexEntry.path` setter [#335](https://github.com/libgit2/pygit2/pull/335) Config: - New `Config` iterator replaces `Config.foreach` [#183](https://github.com/libgit2/pygit2/issues/183) [#312](https://github.com/libgit2/pygit2/pull/312) Remote: - New type `Refspec` [#314](https://github.com/libgit2/pygit2/pull/314) - New `Remote.push_url` [#315](https://github.com/libgit2/pygit2/pull/314) - New `Remote.add_push` and `Remote.add_fetch` [#255](https://github.com/libgit2/pygit2/issues/255) [#318](https://github.com/libgit2/pygit2/pull/318) - New `Remote.fetch_refspecs` replaces `Remote.get_fetch_refspecs()` and `Remote.set_fetch_refspecs(...)` [#319](https://github.com/libgit2/pygit2/pull/319) - New `Remote.push_refspecs` replaces `Remote.get_push_refspecs()` and `Remote.set_push_refspecs(...)` [#319](https://github.com/libgit2/pygit2/pull/319) - New `Remote.progress`, `Remote.transfer_progress` and `Remote.update_tips` [#274](https://github.com/libgit2/pygit2/issues/274) [#324](https://github.com/libgit2/pygit2/pull/324) - New type `TransferProgress` [#274](https://github.com/libgit2/pygit2/issues/274) [#324](https://github.com/libgit2/pygit2/pull/324) - Fix refcount leak in `Repository.remotes` [#321](https://github.com/libgit2/pygit2/issues/321) [#332](https://github.com/libgit2/pygit2/pull/332) Other: [#331](https://github.com/libgit2/pygit2/pull/331) # 0.20.1 (2013-12-24) - New remote ref-specs API: [#290](https://github.com/libgit2/pygit2/pull/290) - New `Repository.reset(...)`: [#292](https://github.com/libgit2/pygit2/pull/292), [#294](https://github.com/libgit2/pygit2/pull/294) - Export `GIT_DIFF_MINIMAL`: [#293](https://github.com/libgit2/pygit2/pull/293) - New `Repository.merge(...)`: [#295](https://github.com/libgit2/pygit2/pull/295) - Fix `Repository.blame` argument handling: [#297](https://github.com/libgit2/pygit2/pull/297) - Fix build error on Windows: [#298](https://github.com/libgit2/pygit2/pull/298) - Fix typo in the README file, Blog → Blob: [#301](https://github.com/libgit2/pygit2/pull/301) - Now `Diff.patch` returns `None` if no patch: [#232](https://github.com/libgit2/pygit2/pull/232), [#303](https://github.com/libgit2/pygit2/pull/303) - New `Walker.simplify_first_parent()`: [#304](https://github.com/libgit2/pygit2/pull/304) # 0.20.0 (2013-11-24) - Upgrade to libgit2 v0.20.0: [#288](https://github.com/libgit2/pygit2/pull/288) - New `Repository.head_is_unborn` replaces `Repository.head_is_orphaned` - Changed `pygit2.clone_repository(...)`. Drop `push_url`, `fetch_spec` and `push_spec` parameters. Add `ignore_cert_errors`. - New `Patch.additions` and `Patch.deletions`: [#275](https://github.com/libgit2/pygit2/pull/275) - New `Patch.is_binary`: [#276](https://github.com/libgit2/pygit2/pull/276) - New `Reference.log_append(...)`: [#277](https://github.com/libgit2/pygit2/pull/277) - New `Blob.is_binary`: [#278](https://github.com/libgit2/pygit2/pull/278) - New `len(Diff)` shows the number of patches: [#281](https://github.com/libgit2/pygit2/pull/281) - Rewrite `Repository.status()`: [#283](https://github.com/libgit2/pygit2/pull/283) - New `Reference.shorthand`: [#284](https://github.com/libgit2/pygit2/pull/284) - New `Repository.blame(...)`: [#285](https://github.com/libgit2/pygit2/pull/285) - Now `Repository.listall_references()` and `Repository.listall_branches()` return a list, not a tuple: [#289](https://github.com/libgit2/pygit2/pull/289) libgit2-pygit2-d86662e/CONTRIBUTING.md000066400000000000000000000062431521317216500170710ustar00rootroot00000000000000# Contributing to pygit2 Thank you for your interest in improving pygit2! This document covers how to submit pull requests and help others in the community. --- ## Pull Requests We welcome pull requests that fix bugs, add features, improve documentation, or clean up code. To ensure a smooth review process, please follow the steps below. ### Development Setup See the [install documentation](https://www.pygit2.org/install.html) for instructions on building pygit2 and its libgit2 dependency. ### Making Changes 1. **Fork the repository** and create a feature branch. 2. **Follow the existing code style** (see below). 3. **Add or update tests** for any new or changed behavior. Tests live in `test/` and are run with `pytest`. 4. **Update type stubs** (`pygit2/_pygit2.pyi`) if you modify the C extension's public API. 5. **Update `pygit2/__init__.py`** if you add new public symbols that should be re-exported. 6. **Ensure the test suite passes**: ```bash pytest ``` 7. **Run the linters and type checker**: ```bash ruff format --diff ruff check sh build.sh mypy # or: mypy sh build.sh stubtest # validate .pyi stubs ``` 8. **Build the documentation** if you changed it (requires `sphinx-rtd-theme`): ```bash make -C docs html ``` 9. **Write a clear commit message** explaining the *what* and *why*. ### Code Style - **Python:** We target Python 3.11+. Use single quotes. Run `ruff format` and `ruff check` before submitting. - **C:** We use C11. Follow `-std=c11 -Wall`. Match the style of the surrounding code in `src/`. - **Copyright headers:** All source files must include the standard GPLv2 copyright header. Copy it from an existing file. - **Docstrings:** Use the style shown in `docs/development.rst`: ```python def f(a, b): """ The general description goes here. Returns: bla bla. Parameters: a : Bla bla. b : Bla bla. """ ``` ### Pull Request Review - All PRs require review from a maintainer. - CI will run tests, linting, and type checks automatically. - Be responsive to feedback and willing to iterate. - Keep PRs focused. A pull request that does one thing well is easier to review than a large, mixed one. --- ## Helping Others You do not need to write code to contribute. Helping others is valuable: - **Answer questions** in open issues and pull requests. If you know the answer, share it. - **Review PRs.** Even if you are not a maintainer, constructive reviews from the community are welcome. - **Improve documentation.** Doc fixes, clarifications, and typo corrections can be submitted as PRs just like code. - **Reproduce reported issues.** Confirming a bug on your system helps maintainers prioritize fixes. --- ## Commit Messages - Use the present tense and imperative mood (e.g., "Add support for…", not "Added support for…"). - Keep the subject line under 72 characters. - Reference related issues with `Fixes #123` or `Closes #456` when applicable. - If you used AI assistance while preparing the change, mention it in the commit message with a tag such as `Assisted-by: Kimi-k2.6` (or the appropriate model name). --- Thank you for contributing! libgit2-pygit2-d86662e/COPYING000066400000000000000000000450551521317216500156770ustar00rootroot00000000000000 pygit2 is Copyright (C) the pygit2 contributors, unless otherwise stated. See the AUTHORS.md file for details. Note that the only valid version of the GPL as far as this project is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. ---------------------------------------------------------------------- LINKING EXCEPTION In addition to the permissions in the GNU General Public License, the authors give you unlimited permission to link the compiled version of this library into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combined executable.) ---------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. libgit2-pygit2-d86662e/Makefile000066400000000000000000000002131521317216500162670ustar00rootroot00000000000000.PHONY: build html build: OPENSSL_VERSION=3.5.4 LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.4 sh build.sh html: build make -C docs html libgit2-pygit2-d86662e/README.md000066400000000000000000000042351521317216500161160ustar00rootroot00000000000000# pygit2 - libgit2 bindings in Python Bindings to the libgit2 shared library, implements Git plumbing. Supports Python 3.11 to 3.14 and PyPy3 7.3+ [![test-ci-badge][test-ci-badge]][test-ci-link] [![deploy-ci-badge][deploy-ci-badge]][deploy-ci-link] [deploy-ci-badge]: https://github.com/libgit2/pygit2/actions/workflows/wheels.yml/badge.svg [deploy-ci-link]: https://github.com/libgit2/pygit2/actions/workflows/wheels.yml [test-ci-badge]: https://github.com/libgit2/pygit2/actions/workflows/tests.yml/badge.svg [test-ci-link]: https://github.com/libgit2/pygit2/actions/workflows/tests.yml ## Links - Documentation - - Install - - Download - - Source code and issue tracker - - Changelog - - Authors - ## Sponsors Add your name and link here, [become a sponsor](https://github.com/sponsors/jdavid). ## License: GPLv2 with linking exception This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. In addition to the permissions in the GNU General Public License, the authors give you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combined executable.) This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. libgit2-pygit2-d86662e/SPONSORS.md000066400000000000000000000005131521317216500164420ustar00rootroot00000000000000Friends of pygit2: - Add your name to the list, [become a friend of pygit2](https://github.com/sponsors/jdavid). Past sponsors: - [Microsoft](https://github.com/microsoft) - [Iterative](https://iterative.ai/) - [SourceHut](https://sourcehut.org) - [GitHub](https://github.com/github) - [omniproc](https://github.com/omniproc) libgit2-pygit2-d86662e/build.ps1000066400000000000000000000015471521317216500163660ustar00rootroot00000000000000$ErrorActionPreference = 'Stop' if (!(Test-Path -Path "build")) { # in case the pygit2 package build/ workspace has not been created by cibuildwheel yet mkdir build } if (Test-Path -Path "$env:LIBGIT2_SRC") { Set-Location "$env:LIBGIT2_SRC" # for local runs, reuse build/libgit_src if it exists if (Test-Path -Path build) { # purge previous build env (likely for a different arch type) Remove-Item -Recurse -Force build } # ensure we are checked out to the right version git fetch --depth=1 --tags git checkout "v$env:LIBGIT2_VERSION" } else { # from a fresh run (like in CI) git clone --depth=1 -b "v$env:LIBGIT2_VERSION" https://github.com/libgit2/libgit2.git $env:LIBGIT2_SRC Set-Location "$env:LIBGIT2_SRC" } cmake -B build -S . -DBUILD_TESTS=OFF cmake --build build/ --config=Release --target install libgit2-pygit2-d86662e/build.sh000066400000000000000000000204541521317216500162730ustar00rootroot00000000000000#!/bin/sh # # Synopsis: # # sh build.sh - Build inplace # sh build.sh test - Build inplace, and run the tests # sh build.sh wheel - Build a wheel, install, and run the tests # # Environment variables: # # AUDITWHEEL_PLAT - Linux platform for auditwheel repair # LIBSSH2_OPENSSL - Where to find openssl # LIBSSH2_VERSION= - Build the given version of libssh2 # LIBGIT2_VERSION= - Build the given version of libgit2 # OPENSSL_VERSION= - Build the given version of OpenSSL # (only needed for Mac universal on CI) # # Examples. # # Build inplace, libgit2 must be available in the path: # # sh build.sh # # Build libgit2 1.9.4 (will use libssh2 if available), then build pygit2 # inplace: # # LIBGIT2_VERSION=1.9.4 sh build.sh # # Build libssh2 1.11.1 and libgit2 1.9.4, then build pygit2 inplace: # # LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.4 sh build.sh # # Build inplace and run the tests: # # sh build.sh test # # Build a wheel: # # sh build.sh wheel # set -x # Print every command and variable set -e # Fail fast # Variables ARCH=`uname -m` KERNEL=`uname -s` BUILD_TYPE=${BUILD_TYPE:-Debug} PYTHON=${PYTHON:-python3} if [ "$CIBUILDWHEEL" != "1" ]; then PYTHON_TAG=$($PYTHON build_tag.py) fi PREFIX="${PREFIX:-$(pwd)/ci/$PYTHON_TAG}" export LDFLAGS="-Wl,-rpath,$PREFIX/lib" if [ "$CIBUILDWHEEL" = "1" ]; then if [ -f /usr/bin/apt-get ]; then apt-get update apt-get install wget -y if [ -z "$OPENSSL_VERSION" ]; then apt-get install libssl-dev -y else apt-get install libtime-piece-perl -y fi elif [ -f /usr/bin/yum ]; then yum install wget zlib-devel -y if [ -z "$OPENSSL_VERSION" ]; then yum install openssl-devel -y else yum install perl-IPC-Cmd -y yum install perl-Pod-Html -y yum install perl-Time-Piece -y fi elif [ -f /sbin/apk ]; then apk add wget if [ -z "$OPENSSL_VERSION" ]; then apk add --no-cache openssl-dev else apk add --no-cache perl fi fi rm -rf ci mkdir ci || true cd ci else # Create a virtual environment $PYTHON -m venv $PREFIX cd ci fi # Install zlib # XXX Build libgit2 with USE_BUNDLED_ZLIB instead? if [ -n "$ZLIB_VERSION" ]; then FILENAME=zlib-$ZLIB_VERSION wget https://www.zlib.net/$FILENAME.tar.gz -N tar xf $FILENAME.tar.gz cd $FILENAME ./configure --prefix=$PREFIX make make install cd .. fi # Install openssl if [ -n "$OPENSSL_VERSION" ]; then FILENAME=openssl-$OPENSSL_VERSION wget https://www.openssl.org/source/$FILENAME.tar.gz -N --no-check-certificate if [ "$KERNEL" = "Darwin" ]; then tar xf $FILENAME.tar.gz mv $FILENAME openssl-x86 tar xf $FILENAME.tar.gz mv $FILENAME openssl-arm cd openssl-x86 ./Configure darwin64-x86_64-cc shared make cd ../openssl-arm ./Configure enable-rc5 zlib darwin64-arm64-cc no-asm make cd .. mkdir openssl-universal LIBSSL=$(basename openssl-x86/libssl.*.dylib) lipo -create openssl-x86/libssl.*.dylib openssl-arm/libssl.*.dylib -output openssl-universal/$LIBSSL LIBCRYPTO=$(basename openssl-x86/libcrypto.*.dylib) lipo -create openssl-x86/libcrypto.*.dylib openssl-arm/libcrypto.*.dylib -output openssl-universal/$LIBCRYPTO cd openssl-universal install_name_tool -id "@rpath/$LIBSSL" $LIBSSL install_name_tool -id "@rpath/$LIBCRYPTO" $LIBCRYPTO OPENSSL_PREFIX=$(pwd) cd .. else # Linux tar xf $FILENAME.tar.gz cd $FILENAME ./Configure shared no-apps no-docs no-tests --prefix=$PREFIX --libdir=$PREFIX/lib make make install OPENSSL_PREFIX=$(pwd) cd .. fi fi # Install libssh2 if [ -n "$LIBSSH2_VERSION" ]; then FILENAME=libssh2-$LIBSSH2_VERSION wget https://www.libssh2.org/download/$FILENAME.tar.gz -N --no-check-certificate tar xf $FILENAME.tar.gz cd $FILENAME if [ "$KERNEL" = "Darwin" ] && [ "$CIBUILDWHEEL" = "1" ]; then cmake . \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_EXAMPLES=OFF \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DOPENSSL_CRYPTO_LIBRARY="../openssl-universal/$LIBCRYPTO" \ -DOPENSSL_SSL_LIBRARY="../openssl-universal/$LIBSSL" \ -DOPENSSL_INCLUDE_DIR="../openssl-x86/include" \ -DBUILD_TESTING=OFF else cmake . \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTING=OFF fi cmake --build . --target install cd .. USE_SSH=ON else USE_SSH=OFF fi # Install libgit2 if [ -n "$LIBGIT2_VERSION" ]; then FILENAME=libgit2-$LIBGIT2_VERSION wget https://github.com/libgit2/libgit2/archive/refs/tags/v$LIBGIT2_VERSION.tar.gz -N -O $FILENAME.tar.gz tar xf $FILENAME.tar.gz cd $FILENAME mkdir -p build cd build if [ "$KERNEL" = "Darwin" ] && [ "$CIBUILDWHEEL" = "1" ]; then CMAKE_PREFIX_PATH=$OPENSSL_PREFIX:$PREFIX cmake .. \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_TESTS=OFF \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DOPENSSL_CRYPTO_LIBRARY="../openssl-universal/$LIBCRYPTO" \ -DOPENSSL_SSL_LIBRARY="../openssl-universal/$LIBSSL" \ -DOPENSSL_INCLUDE_DIR="../openssl-x86/include" \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DUSE_SSH=$USE_SSH else export CFLAGS=-I$PREFIX/include CMAKE_PREFIX_PATH=$OPENSSL_PREFIX:$PREFIX cmake .. \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_TESTS=OFF \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DUSE_SSH=$USE_SSH fi cmake --build . --target install cd .. cd .. export LIBGIT2=$PREFIX fi if [ "$CIBUILDWHEEL" = "1" ]; then if [ "$KERNEL" = "Darwin" ]; then cp $OPENSSL_PREFIX/*.dylib $PREFIX/lib/ cp $OPENSSL_PREFIX/*.dylib $PREFIX/lib/ echo "PREFIX " $PREFIX echo "OPENSSL_PREFIX" $OPENSSL_PREFIX ls -l $PREFIX ls -l $PREFIX/lib fi # we're done building dependencies, cibuildwheel action will take over exit 0 fi # Build pygit2 cd .. $PREFIX/bin/pip install -U pip wheel if [ "$1" = "wheel" ]; then shift $PREFIX/bin/pip install wheel $PREFIX/bin/python setup.py bdist_wheel WHEELDIR=dist else # Install Python requirements & build inplace $PREFIX/bin/pip install -r requirements.txt $PREFIX/bin/python setup.py build_ext --inplace fi # Bundle libraries if [ "$1" = "bundle" ]; then shift WHEELDIR=wheelhouse case "${KERNEL}" in Darwin*) $PREFIX/bin/pip install delocate $PREFIX/bin/delocate-listdeps dist/pygit2-*macosx*.whl $PREFIX/bin/delocate-wheel -v -w $WHEELDIR dist/pygit2-*macosx*.whl $PREFIX/bin/delocate-listdeps $WHEELDIR/pygit2-*macosx*.whl ;; *) # LINUX $PREFIX/bin/pip install auditwheel $PREFIX/bin/auditwheel repair dist/pygit2*-$PYTHON_TAG-*_$ARCH.whl $PREFIX/bin/auditwheel show $WHEELDIR/pygit2*-$PYTHON_TAG-*_$ARCH.whl ;; esac fi # Tests if [ "$1" = "test" ]; then shift if [ -n "$WHEELDIR" ]; then $PREFIX/bin/pip install $WHEELDIR/pygit2*-$PYTHON_TAG-*.whl fi $PREFIX/bin/pip install -r requirements-test.txt $PREFIX/bin/pytest --cov=pygit2 fi # Type checking if [ "$1" = "mypy" ]; then shift if [ -n "$WHEELDIR" ]; then $PREFIX/bin/pip install $WHEELDIR/pygit2*-$PYTHON_TAG-*.whl fi $PREFIX/bin/pip install -r requirements-test.txt -r requirements-typing.txt $PREFIX/bin/mypy pygit2 test fi # Test .pyi stub file if [ "$1" = "stubtest" ]; then shift $PREFIX/bin/pip install mypy PYTHONPATH=. $PREFIX/bin/stubtest --mypy-config-file mypy-stubtest.ini pygit2._pygit2 [ $? == 0 ] && echo "stubtest OK" fi libgit2-pygit2-d86662e/build_tag.py000066400000000000000000000002421521317216500171350ustar00rootroot00000000000000import platform import sys py = {'CPython': 'cp', 'PyPy': 'pp'}[platform.python_implementation()] print(f'{py}{sys.version_info.major}{sys.version_info.minor}') libgit2-pygit2-d86662e/docs/000077500000000000000000000000001521317216500155635ustar00rootroot00000000000000libgit2-pygit2-d86662e/docs/Makefile000066400000000000000000000011041521317216500172170ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)libgit2-pygit2-d86662e/docs/_static/000077500000000000000000000000001521317216500172115ustar00rootroot00000000000000libgit2-pygit2-d86662e/docs/_static/.keep000066400000000000000000000000001521317216500201240ustar00rootroot00000000000000libgit2-pygit2-d86662e/docs/_templates/000077500000000000000000000000001521317216500177205ustar00rootroot00000000000000libgit2-pygit2-d86662e/docs/_templates/.keep000066400000000000000000000000001521317216500206330ustar00rootroot00000000000000libgit2-pygit2-d86662e/docs/backends.rst000066400000000000000000000017551521317216500200770ustar00rootroot00000000000000********************************************************************** Backends ********************************************************************** The use of custom backends for the git object database (odb) and reference database (refdb) are supported by pygit2. .. contents:: Contents :local: The OdbBackend class =================================== The OdbBackend class is subclassable and can be used to build a custom object database. .. autoclass:: pygit2.OdbBackend :members: Built-in OdbBackend implementations =================================== .. autoclass:: pygit2.OdbBackendLoose :members: .. autoclass:: pygit2.OdbBackendPack :members: The RefdbBackend class =================================== The RefdbBackend class is subclassable and can be used to build a custom reference database. .. autoclass:: pygit2.RefdbBackend :members: Built-in RefdbBackend implementations ===================================== .. autoclass:: pygit2.RefdbFsBackend :members: libgit2-pygit2-d86662e/docs/blame.rst000066400000000000000000000024471521317216500174040ustar00rootroot00000000000000********************************************************************** Blame ********************************************************************** .. contents:: .. automethod:: pygit2.Repository.blame The Blame type ============== .. automethod:: pygit2.Blame.for_line .. method:: Blame.__iter__() .. method:: Blame.__len__() .. method:: Blame.__getitem__(n) The BlameHunk type ================== Attributes: .. autoattribute:: pygit2.BlameHunk.lines_in_hunk .. autoattribute:: pygit2.BlameHunk.final_commit_id .. autoattribute:: pygit2.BlameHunk.final_start_line_number .. autoattribute:: pygit2.BlameHunk.orig_commit_id .. autoattribute:: pygit2.BlameHunk.orig_path .. autoattribute:: pygit2.BlameHunk.orig_start_line_number .. autoattribute:: pygit2.BlameHunk.boundary Getters: .. autoattribute:: pygit2.BlameHunk.final_committer .. autoattribute:: pygit2.BlameHunk.orig_committer Constants ========= .. py:data:: enums.BlameFlag.NORMAL .. py:data:: enums.BlameFlag.TRACK_COPIES_SAME_FILE .. py:data:: enums.BlameFlag.TRACK_COPIES_SAME_COMMIT_MOVES .. py:data:: enums.BlameFlag.TRACK_COPIES_SAME_COMMIT_COPIES .. py:data:: enums.BlameFlag.TRACK_COPIES_ANY_COMMIT_COPIES .. py:data:: enums.BlameFlag.FIRST_PARENT .. py:data:: enums.BlameFlag.USE_MAILMAP .. py:data:: enums.BlameFlag.IGNORE_WHITESPACE libgit2-pygit2-d86662e/docs/branches.rst000066400000000000000000000024721521317216500201070ustar00rootroot00000000000000********************************************************************** Branches ********************************************************************** .. autoclass:: pygit2.Repository :members: lookup_branch, raw_listall_branches :noindex: .. attribute:: branches Branches inherit from References, and additionally provide specialized accessors for some unique features. .. autoclass:: pygit2.repository.Branches :members: :undoc-members: :special-members: __getitem__, __iter__, __contains__ Example:: >>> # Listing all branches >>> branches_list = list(repo.branches) >>> # Local only >>> local_branches = list(repo.branches.local) >>> # Remote only >>> remote_branches = list(repo.branches.remote) >>> # Get a branch >>> master_branch = repo.branches['master'] >>> other_branch = repo.branches['does-not-exist'] # Will raise a KeyError >>> other_branch = repo.branches.get('does-not-exist') # Returns None >>> remote_branch = repo.branches.remote['upstream/feature'] >>> # Create a local branch, branching from master >>> new_branch = repo.branches.local.create('new-branch', repo[master_branch.target]) >>> # And delete it >>> repo.branches.delete('new-branch') The Branch type ==================== .. autoclass:: pygit2.Branch :members: libgit2-pygit2-d86662e/docs/callbacks.rst000066400000000000000000000017271521317216500202430ustar00rootroot00000000000000********************************************************************** Callbacks ********************************************************************** Many pygit2 operations accept callback objects. The callbacks module provides base classes that you can subclass to customize behavior such as progress reporting, credential lookup, or checkout notifications. .. contents:: Contents :local: Remote callbacks ================ .. autoclass:: pygit2.RemoteCallbacks :members: Checkout callbacks ================== .. autoclass:: pygit2.CheckoutCallbacks :members: Stash apply callbacks ===================== .. autoclass:: pygit2.StashApplyCallbacks :members: Passthrough =========== Callbacks may raise :exc:`pygit2.Passthrough` to tell libgit2 to behave as if the callback had not been set. This is useful when a callback only wants to handle some cases and let libgit2 use its default behavior for the rest. See :doc:`general` for the exception reference. libgit2-pygit2-d86662e/docs/commit_log.rst000066400000000000000000000006041521317216500204460ustar00rootroot00000000000000********************************************************************** Commit log ********************************************************************** .. automethod:: pygit2.Repository.walk .. automethod:: pygit2.Walker.hide .. automethod:: pygit2.Walker.push .. automethod:: pygit2.Walker.reset .. automethod:: pygit2.Walker.sort .. automethod:: pygit2.Walker.simplify_first_parent libgit2-pygit2-d86662e/docs/conf.py000066400000000000000000000036621521317216500170710ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # http://www.sphinx-doc.org/en/master/config import os import sys # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('..')) # -- Project information ----------------------------------------------------- project = 'pygit2' copyright = '2010-2025 The pygit2 contributors' # author = '' # The full version, including alpha/beta/rc tags release = '1.19.3' # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' html_theme_path = ['_themes'] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] libgit2-pygit2-d86662e/docs/config.rst000066400000000000000000000010171521317216500175610ustar00rootroot00000000000000********************************************************************** Configuration files ********************************************************************** .. autoclass:: pygit2.Repository :members: config :noindex: The Config type ================ .. autoclass:: pygit2.Config :members: :undoc-members: :special-members: __contains__, __delitem__, __getitem__, __iter__, __setitem__ The ConfigEntry type ==================== .. autoclass:: pygit2.config.ConfigEntry :members: name, value, level libgit2-pygit2-d86662e/docs/development.rst000066400000000000000000000037231521317216500206440ustar00rootroot00000000000000********************************************************************** The development version ********************************************************************** .. image:: https://github.com/libgit2/pygit2/actions/workflows/tests.yml/badge.svg :target: https://github.com/libgit2/pygit2/actions/workflows/tests.yml .. image:: https://github.com/libgit2/pygit2/actions/workflows/wheels.yml/badge.svg :target: https://github.com/libgit2/pygit2/actions/workflows/wheels.yml .. contents:: Contents :local: Unit tests ========== .. code-block:: sh $ git clone git://github.com/libgit2/pygit2.git $ cd pygit2 $ python setup.py build_ext --inplace $ pytest test Coding style: documentation strings =================================== Example:: def f(a, b): """ The general description goes here. Returns: bla bla. Parameters: a : Bla bla. b : Bla bla. Examples:: >>> f(...) """ Building the docs =================================== To build the documentation first you need to install sphinx-rtd-theme:: $ pip install sphinx-rtd-theme Then you have to build pygit2 inplace:: $ make And finally you can build the documentation:: $ make -C docs html Running Valgrind =================================== Step 1. Build libc and libgit2 with debug symbols. See your distribution documentation. Step 2. Build Python to be used with valgrind, e.g.:: $ ./configure --prefix=~/Python-3.9.18 --without-pymalloc --with-pydebug --with-valgrind $ make $ make install $ export PYTHONBIN=~/Python-3.9.18/bin Step 3. Build pygit2 with debug symbols:: $ rm build -rf && $PYTHONBIN/python3 setup.py build_ext --inplace -g Step 4. Install requirements:: $ $PYTHONBIN/python3 setup.py install $ pip install pytest Step 4. Run valgrind:: $ valgrind -v --leak-check=full --suppressions=misc/valgrind-python.supp $PYTHONBIN/pytest &> valgrind.txt libgit2-pygit2-d86662e/docs/diff.rst000066400000000000000000000034741521317216500172350ustar00rootroot00000000000000********************************************************************** Diff ********************************************************************** .. contents:: A diff shows the changes between trees, an index or the working dir. .. automethod:: pygit2.Repository.diff Examples .. code-block:: python # Changes between commits >>> t0 = revparse_single('HEAD') >>> t1 = revparse_single('HEAD^') >>> repo.diff(t0, t1) >>> t0.diff(t1) # equivalent >>> repo.diff('HEAD', 'HEAD^') # equivalent # Get all patches for a diff >>> diff = repo.diff('HEAD^', 'HEAD~3') >>> patches = [p for p in diff] # Get the stats for a diff >>> diff = repo.diff('HEAD^', 'HEAD~3') >>> diff.stats # Diffing the empty tree >>> tree = revparse_single('HEAD').tree >>> tree.diff_to_tree() # Diff empty tree to a tree >>> tree = revparse_single('HEAD').tree >>> tree.diff_to_tree(swap=True) The Diff type ==================== .. autoclass:: pygit2.Diff :members: deltas, find_similar, merge, parse_diff, patch, patchid, stats .. method:: Diff.__iter__() Returns an iterator over the deltas/patches in this diff. .. method:: Diff.__len__() Returns the number of deltas/patches in this diff. The Patch type ==================== Attributes: .. autoclass:: pygit2.Patch :members: create_from, data, delta, hunks, line_stats, text The DiffDelta type ==================== .. autoclass:: pygit2.DiffDelta :members: The DiffFile type ==================== .. autoclass:: pygit2.DiffFile :members: The DiffHunk type ==================== .. autoclass:: pygit2.DiffHunk :members: The DiffStats type ==================== .. autoclass:: pygit2.DiffStats :members: The DiffLine type ==================== .. autoclass:: pygit2.DiffLine :members: libgit2-pygit2-d86662e/docs/enums.rst000066400000000000000000000054411521317216500174500ustar00rootroot00000000000000********************************************************************** Enums ********************************************************************** pygit2 exposes libgit2 constants as Python enums in the :mod:`pygit2.enums` module. They are preferred over the top-level ``GIT_*`` integer constants. .. contents:: Contents :local: Repository ========== .. autoclass:: pygit2.enums.RepositoryInitFlag :members: .. autoclass:: pygit2.enums.RepositoryInitMode :members: .. autoclass:: pygit2.enums.RepositoryOpenFlag :members: .. autoclass:: pygit2.enums.RepositoryState :members: References and branches ======================= .. autoclass:: pygit2.enums.BranchType :members: .. autoclass:: pygit2.enums.ReferenceFilter :members: .. autoclass:: pygit2.enums.ReferenceType :members: .. autoclass:: pygit2.enums.ResetMode :members: .. autoclass:: pygit2.enums.RevSpecFlag :members: Objects ======= .. autoclass:: pygit2.enums.ObjectType :members: .. autoclass:: pygit2.enums.FileMode :members: Diff ==== .. autoclass:: pygit2.enums.DeltaStatus :members: .. autoclass:: pygit2.enums.DiffFind :members: .. autoclass:: pygit2.enums.DiffFlag :members: .. autoclass:: pygit2.enums.DiffOption :members: .. autoclass:: pygit2.enums.DiffStatsFormat :members: Status ====== .. autoclass:: pygit2.enums.FileStatus :members: Checkout ======== .. autoclass:: pygit2.enums.CheckoutNotify :members: .. autoclass:: pygit2.enums.CheckoutStrategy :members: Merge ===== .. autoclass:: pygit2.enums.MergeAnalysis :members: .. autoclass:: pygit2.enums.MergeFavor :members: .. autoclass:: pygit2.enums.MergeFileFlag :members: .. autoclass:: pygit2.enums.MergeFlag :members: .. autoclass:: pygit2.enums.MergePreference :members: Blame ===== .. autoclass:: pygit2.enums.BlameFlag :members: Filters ======= .. autoclass:: pygit2.enums.FilterMode :members: .. autoclass:: pygit2.enums.FilterFlag :members: .. autoclass:: pygit2.enums.BlobFilter :members: Attributes ========== .. autoclass:: pygit2.enums.AttrCheck :members: Remotes ======= .. autoclass:: pygit2.enums.CredentialType :members: .. autoclass:: pygit2.enums.FetchPrune :members: Submodules ========== .. autoclass:: pygit2.enums.SubmoduleIgnore :members: .. autoclass:: pygit2.enums.SubmoduleStatus :members: Stash ===== .. autoclass:: pygit2.enums.StashApplyProgress :members: Revwalk ======= .. autoclass:: pygit2.enums.SortMode :members: .. autoclass:: pygit2.enums.DescribeStrategy :members: Apply ===== .. autoclass:: pygit2.enums.ApplyLocation :members: Library ======= .. autoclass:: pygit2.enums.Feature :members: .. autoclass:: pygit2.enums.Option :members: .. autoclass:: pygit2.enums.ConfigLevel :members: libgit2-pygit2-d86662e/docs/features.rst000066400000000000000000000005061521317216500201340ustar00rootroot00000000000000********************************************************************** Feature detection ********************************************************************** .. py:data:: pygit2.features This variable contains a combination of `enums.Feature` flags, indicating which features a particular build of libgit2 supports. libgit2-pygit2-d86662e/docs/filters.rst000066400000000000000000000042631521317216500177720ustar00rootroot00000000000000********************************************************************** Filters ********************************************************************** pygit2 supports defining and registering libgit2 blob filters implemented in Python. The Filter type =============== .. autoclass:: pygit2.Filter :members: .. autoclass:: pygit2.FilterSource Registering filters =================== .. autofunction:: pygit2.filter_register .. autofunction:: pygit2.filter_unregister Loading filters =============== .. automethod:: pygit2.Repository.load_filter_list The FilterList type ------------------- .. autoclass:: pygit2.filter.FilterList :members: Example ======= The following example is a simple Python implementation of a filter which enforces that blobs are stored with unix LF line-endings in the ODB, and checked out with line-endings in accordance with the .gitattributes ``eol`` setting. .. code-block:: python class CRLFFilter(pygit2.Filter): attributes = "text eol=*" def __init__(self): super().__init__() self.linesep = b'\r\n' if os.name == 'nt' else b'\n' self.buffer = io.BytesIO() def check(self, src, attr_values): if src.mode == pygit2.enums.FilterMode.SMUDGE: # attr_values contains the values of the 'text' and 'eol' # attributes in that order (as they are defined in # CRLFFilter.attributes eol = attr_values[1] if eol == 'crlf': self.linesep = b'\r\n' elif eol == 'lf': self.linesep = b'\n' else: # src.mode == pygit2.enums.FilterMode.CLEAN # always use LF line-endings when writing to the ODB self.linesep = b'\n' def write(data, src, write_next): # buffer input data in case line-ending sequences span chunk boundaries self.buffer.write(data) def close(self, write_next): # apply line-ending conversion to our buffered input and write all # of our output data self.buffer.seek(0) write_next(self.linesep.join(self.buffer.read().splitlines())) libgit2-pygit2-d86662e/docs/general.rst000066400000000000000000000037461521317216500177440ustar00rootroot00000000000000********************************************************************** General ********************************************************************** .. contents:: Contents :local: Top level constants and exceptions from the library. Version ========= The following constants provide information about the version of the libgit2 library that has been built against. The version number has a ``MAJOR.MINOR.REVISION`` format. .. py:data:: LIBGIT2_VER_MAJOR Integer value of the major version number. For example, for the version ``1.9.4``:: >>> print(pygit2.LIBGIT2_VER_MAJOR) 1 .. py:data:: LIBGIT2_VER_MINOR Integer value of the minor version number. For example, for the version ``1.9.4``:: >>> print(pygit2.LIBGIT2_VER_MINOR) 9 .. py:data:: LIBGIT2_VER_REVISION Integer value of the revision version number. For example, for the version ``1.9.4``:: >>> print(pygit2.LIBGIT2_VER_REVISION) 4 .. py:data:: LIBGIT2_VER Tuple value of the revision version numbers. For example, for the version ``1.9.4``:: >>> print(pygit2.LIBGIT2_VER) (1, 9, 4) .. py:data:: LIBGIT2_VERSION The libgit2 version number as a string:: >>> print(pygit2.LIBGIT2_VERSION) '1.9.4' Options ========= .. autofunction:: pygit2.option Exceptions ========== .. autoexception:: pygit2.GitError :members: :show-inheritance: :undoc-members: .. autoexception:: pygit2.AlreadyExistsError :members: :show-inheritance: :undoc-members: Exception when trying to create an object (reference, etc) that already exists. .. autoexception:: pygit2.InvalidSpecError :members: :show-inheritance: :undoc-members: Exception when an input specification such as a reference name is invalid. .. autoexception:: pygit2.Passthrough :members: :show-inheritance: :undoc-members: Exception that can be raised from a callback to tell libgit2 to behave as if that callback had not been set. See :doc:`callbacks` for details. libgit2-pygit2-d86662e/docs/index.rst000066400000000000000000000046761521317216500174410ustar00rootroot00000000000000###################################################################### pygit2 - libgit2 bindings in Python ###################################################################### Bindings to the libgit2 shared library, implements Git plumbing. Supports Python 3.11 to 3.14 and PyPy3 7.3+ Links ===================================== - Documentation - https://www.pygit2.org/ - Install - https://www.pygit2.org/install.html - Download - https://pypi.org/project/pygit2/ - Source code and issue tracker - https://github.com/libgit2/pygit2 - Changelog - https://github.com/libgit2/pygit2/blob/master/CHANGELOG.md - Authors - https://github.com/libgit2/pygit2/blob/master/AUTHORS.md Sponsors ===================================== Add your name and link here, `become a sponsor `_. License: GPLv2 with linking exception ===================================== This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. In addition to the permissions in the GNU General Public License, the authors give you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combined executable.) This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Table of Contents ===================================== .. toctree:: :maxdepth: 1 install recipes general backends blame branches callbacks commit_log config diff enums features filters index_file mailmap merge objects oid packing references transactions remotes repository revparse settings submodule worktree development Indices and tables ===================================== * :ref:`genindex` libgit2-pygit2-d86662e/docs/index_file.rst000066400000000000000000000065441521317216500204340ustar00rootroot00000000000000********************************************************************** Index file & Working copy ********************************************************************** .. autoattribute:: pygit2.Repository.index Index read:: >>> index = repo.index >>> index.read() >>> id = index['path/to/file'].id # from path to object id >>> blob = repo[id] # from object id to object Iterate over all entries of the index:: >>> for entry in index: ... print(entry.path, entry.id) Index write:: >>> index.add('path/to/file') # git add >>> index.remove('path/to/file') # git rm >>> index.remove_directory('path/to/directory/') # git rm -r >>> index.write() # don't forget to save the changes Custom entries:: >>> entry = pygit2.IndexEntry('README.md', blob_id, blob_filemode) >>> repo.index.add(entry) The index fulfills a dual role as the in-memory representation of the index file and data structure which represents a flat list of a tree. You can use it independently of the index file, e.g. >>> index = pygit2.Index() >>> entry = pygit2.IndexEntry('README.md', blob_id, blob_filemode) >>> index.add(entry) The Index type ==================== .. autoclass:: pygit2.Index :members: The IndexEntry type ==================== .. autoclass:: pygit2.IndexEntry :members: .. automethod:: __eq__ .. automethod:: __ne__ .. automethod:: __repr__ .. automethod:: __str__ The Stash type ==================== .. autoclass:: pygit2.Stash :members: commit_id, message Status ==================== .. autoclass:: pygit2.Repository :members: status_file :noindex: .. automethod:: Repository.status Example, inspect the status of the repository:: from pygit2.enums import FileStatus status = repo.status() for filepath, flags in status.items(): if flags != FileStatus.CURRENT: print(f"Filepath {filepath} isn't clean") This is the list of status flags for a single file:: enums.FileStatus.CURRENT enums.FileStatus.INDEX_NEW enums.FileStatus.INDEX_MODIFIED enums.FileStatus.INDEX_DELETED enums.FileStatus.INDEX_RENAMED enums.FileStatus.INDEX_TYPECHANGE enums.FileStatus.WT_NEW enums.FileStatus.WT_MODIFIED enums.FileStatus.WT_DELETED enums.FileStatus.WT_TYPECHANGE enums.FileStatus.WT_RENAMED enums.FileStatus.WT_UNREADABLE enums.FileStatus.IGNORED enums.FileStatus.CONFLICTED A combination of these values will be returned to indicate the status of a file. Status compares the working directory, the index, and the current HEAD of the repository. The `INDEX_...` set of flags represents the status of file in the index relative to the HEAD, and the `WT_...` set of flags represents the status of the file in the working directory relative to the index. Checkout ==================== .. automethod:: pygit2.Repository.checkout Lower level API: .. automethod:: pygit2.Repository.checkout_head .. automethod:: pygit2.Repository.checkout_tree .. automethod:: pygit2.Repository.checkout_index Stash ==================== .. automethod:: pygit2.Repository.stash .. automethod:: pygit2.Repository.stash_apply .. automethod:: pygit2.Repository.stash_drop .. automethod:: pygit2.Repository.stash_pop .. automethod:: pygit2.Repository.listall_stashes libgit2-pygit2-d86662e/docs/install.rst000066400000000000000000000247601521317216500177740ustar00rootroot00000000000000********************************************************************** Installation ********************************************************************** .. contents:: Contents :local: Quick install ============= Install pygit2: .. code-block:: sh pip install -U pip pip install pygit2 The line above will install binary wheels if available in your platform. .. note:: It is recommended to first update the version of pip, as it will increase the chances for it to install a binary wheel instead of the source distribution. At least version 19.3 of pip is required. If you get the error:: fatal error: git2.h: No such file or directory It means that pip did not find a binary wheel for your platform, so it tried to build from source. It failed to build because it could not find the libgit2 headers. Then: - Verify pip is updated - Verify there is a binary wheel of pygit2 for your platform - Otherwise `install from the source distribution`_ Caveats: - Binary wheels for Windows are available, but they don't have support for ssh. Requirements ============ Supported versions of Python: - Python 3.11 to 3.14 - PyPy3 7.3+ Python requirements (these are specified in ``setup.py``): - cffi 2.0 or later Libgit2 **v1.9.x**; binary wheels already include libgit2, so you only need to worry about this if you `install from the source distribution`_. Optional libgit2 dependencies to support ssh and https: - https: WinHTTP (Windows), SecureTransport (OS X) or OpenSSL. - ssh: libssh2 1.10.0 or later, pkg-config To run the tests: - pytest Version numbers =============== The version number of pygit2 is composed of three numbers separated by dots:: .. - *major* will always be 1 (until we release 2.0 in a far undefined future) - *medium* will increase whenever we make breaking changes, or upgrade to new versions of libgit2. - *minor* will increase for bug fixes. The table below summarizes the latest pygit2 versions with the supported versions of Python and the required libgit2 version. +-------------+----------------+------------+ | pygit2 | Python | libgit2 | +-------------+----------------+------------+ | 1.19 | 3.11 - 3.14(t) | 1.9 | +-------------+----------------+------------+ | 1.17 - 1.18 | 3.10 - 3.13 | 1.9 | +-------------+----------------+------------+ | 1.16 | 3.10 - 3.13 | 1.8 | +-------------+----------------+------------+ | 1.15 | 3.9 - 3.12 | 1.8 | +-------------+----------------+------------+ | 1.14 | 3.9 - 3.12 | 1.7 | +-------------+----------------+------------+ | 1.13 | 3.8 - 3.12 | 1.7 | +-------------+----------------+------------+ | 1.12 | 3.8 - 3.11 | 1.6 | +-------------+----------------+------------+ | 1.11 | 3.8 - 3.11 | 1.5 | +-------------+----------------+------------+ | 1.10 | 3.7 - 3.10 | 1.5 | +-------------+----------------+------------+ | 1.9 | 3.7 - 3.10 | 1.4 | +-------------+----------------+------------+ | 1.7 - 1.8 | 3.7 - 3.10 | 1.3 | +-------------+----------------+------------+ | 1.4 - 1.6 | 3.6 - 3.9 | 1.1 | +-------------+----------------+------------+ | 1.2 - 1.3 | 3.6 - 3.8 | 1.0 | +-------------+----------------+------------+ | 1.1 | 3.5 - 3.8 | 0.99 - 1.0 | +-------------+----------------+------------+ | 1.0 | 3.5 - 3.8 | 0.28 | +-------------+----------------+------------+ | 0.28.2 | 2.7, 3.4 - 3.7 | 0.28 | +-------------+----------------+------------+ .. warning:: It is recommended to use the latest 1.x.y release. Because only the latest is supported. .. warning:: Backwards compatibility is not guaranteed in minor releases. Please check the release notes for incompatible changes before upgrading to a new release. .. warning:: Threaded builds are experimental, do not use them in production. History: the 0.x series ----------------------- The development of pygit2 started in October 2010, the release of 1.0.0 happened in December 2019. In the 0.x series the version numbering was lockstep with libgit2, e.g. pygit2 0.28.x worked with libgit2 0.28.x Advanced =========================== .. _install from the source distribution: Install libgit2 from source --------------------------- Installing from source requires * a C compiler (such as gcc) * the CPython API headers (typically in an ``apt`` package named ``python3-dev``) To install the latest version of libgit2 system wide, in the ``/usr/local`` directory, do: .. code-block:: sh :caption: On Linux using bash wget https://github.com/libgit2/libgit2/archive/refs/tags/v1.9.4.tar.gz -O libgit2-1.9.4.tar.gz tar -xzf libgit2-1.9.4.tar.gz cd libgit2-1.9.4/ cmake . make sudo make install .. seealso:: For detailed instructions on building libgit2 check https://libgit2.org/docs/guides/build-and-link/ Now install pygit2, and then verify it is correctly installed: .. code-block:: sh pip install pygit2 # ... python -c 'import pygit2' Troubleshooting --------------------------- The verification step may fail if the dynamic linker does not find the libgit2 library: .. code-block:: text python -c 'import pygit2' Traceback (most recent call last): File "", line 1, in File "pygit2/__init__.py", line 29, in from ._pygit2 import * ImportError: libgit2.so.0: cannot open shared object file: No such file or directory This happens for instance in Ubuntu, the libgit2 library is installed within the ``/usr/local/lib`` directory, but the linker does not look for it there. To fix this call ``ldconfig``: .. code-block:: sh :caption: On Linux using bash sudo ldconfig python -c 'import pygit2' If it still does not work, please open an issue at https://github.com/libgit2/pygit2/issues Build options --------------------------- ``LIBGIT2`` -- If you install libgit2 in an unusual place, you will need to set the ``LIBGIT2`` environment variable before installing pygit2. This variable tells pygit2 where libgit2 is installed. We will see a concrete example later, when explaining how to install libgit2 within a virtual environment. ``LIBGIT2_LIB`` -- This is a more rarely used build option, it allows to override the library directory where libgit2 is installed, useful if different from ``$LIBGIT2/lib`` and ``$LIBGIT2/lib64``. libgit2 within a virtual environment ------------------------------------ This is how to install both libgit2 and pygit2 within a virtual environment. This is useful if you don't have root access to install libgit2 system wide. Or if you wish to have different versions of libgit2/pygit2 installed in different virtual environments, isolated from each other. Create the virtualenv, activate it, and set the ``LIBGIT2`` environment variable: .. code-block:: sh :caption: On Linux using bash virtualenv venv source venv/bin/activate export LIBGIT2=$VIRTUAL_ENV Install libgit2 (see we define the installation prefix): .. code-block:: sh :caption: On Linux using bash wget https://github.com/libgit2/libgit2/archive/refs/tags/v1.9.4.tar.gz -O libgit2-1.9.4.tar.gz tar xzf libgit2-1.9.4.tar.gz cd libgit2-1.9.4/ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2 cmake --build . --target install Install pygit2: .. code-block:: sh :caption: On Linux using bash export LDFLAGS="-Wl,-rpath,'$LIBGIT2/lib',--enable-new-dtags $LDFLAGS" # on OSX: export LDFLAGS="-Wl,-rpath,'$LIBGIT2/lib' $LDFLAGS" pip install pygit2 python -c 'import pygit2' The run-path ------------------------------------------ Did you notice we set the `rpath `_ before installing pygit2? Since libgit2 is installed in a non standard location, the dynamic linker will not find it at run-time, and ``lddconfig`` will not help this time. So you need to either set ``LD_LIBRARY_PATH`` before using pygit2, like: .. code-block:: sh :caption: On Linux using bash export LD_LIBRARY_PATH=$LIBGIT2/lib python -c 'import pygit2' Or, like we have done in the instructions above, use the `rpath `_, it hard-codes extra search paths within the pygit2 extension modules, so you don't need to set ``LD_LIBRARY_PATH`` every time. Verify yourself if curious: .. code-block:: sh :caption: On Linux using bash readelf --dynamic lib/python2.7/site-packages/pygit2-0.27.0-py2.7-linux-x86_64.egg/pygit2/_pygit2.so | grep PATH 0x000000000000001d (RUNPATH) Library runpath: [/tmp/venv/lib] Installing on Windows =================================== ``pygit2`` for Windows is packaged into wheels and can be easily installed with ``pip``: .. code-block:: console pip install pygit2 For development it is also possible to build ``pygit2`` with ``libgit2`` from sources. ``libgit2`` location is specified by the ``LIBGIT2`` environment variable. The following recipe shows you how to do it: .. code-block:: pwsh :caption: On Windows using PowerShell (and CMake v3.21 or newer) git clone --depth=1 -b v1.9.4 https://github.com/libgit2/libgit2.git $env:CMAKE_INSTALL_PREFIX = "C:/Dev/libgit2" $env:CMAKE_GENERATOR = "Visual Studio 17 2022" # or "Visual Studio 18 2026" $env:CMAKE_GENERATOR_PLATFORM = "x64" # or "Win32" or "ARM64" cmake -B libgit2/build -S libgit2 cmake --build libgit2/build --config release --target install # let pip know where to find libgit2 when building pygit2 $env:LIBGIT2 = "$env:CMAKE_INSTALL_PREFIX" At this point, you're ready to execute the generic `pygit2` installation steps described at the start of this page. Installing on OS X =================================== There are not binary wheels available for OS X, so you will need to install the source package. .. note:: You will need the `XCode `_ Developer Tools from Apple. This free download from the Mac App Store will provide the clang compiler needed for the installation of pygit2. This section was tested on OS X 10.9 Mavericks and OS X 10.10 Yosemite with Python 3.3 in a virtual environment. The easiest way is to first install libgit2 with the `Homebrew `_ package manager and then use pip3 for pygit2. The following example assumes that XCode and Homebrew are already installed. .. code-block:: sh brew update brew install libgit2 pip3 install pygit2 To build from a non-Homebrew libgit2 follow the guide in `libgit2 within a virtual environment`_. libgit2-pygit2-d86662e/docs/mailmap.rst000066400000000000000000000003021521317216500177300ustar00rootroot00000000000000********************************************************************** Mailmap ********************************************************************** .. autoclass:: pygit2.Mailmap :members: libgit2-pygit2-d86662e/docs/make.bat000066400000000000000000000014231521317216500171700ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd libgit2-pygit2-d86662e/docs/merge.rst000066400000000000000000000045561521317216500174260ustar00rootroot00000000000000********************************************************************** Merge & Cherrypick ********************************************************************** .. contents:: .. automethod:: pygit2.Repository.merge_base .. automethod:: pygit2.Repository.merge .. automethod:: pygit2.Repository.merge_analysis The merge method ================= The method does a merge over the current working copy. It gets an Oid object as a parameter. As its name says, it only does the merge, does not commit nor update the branch reference in the case of a fastforward. Example:: >>> from pygit2.enums import MergeFavor, MergeFlag >>> other_branch_tip = '5ebeeebb320790caf276b9fc8b24546d63316533' >>> repo.merge(other_branch_tip) >>> repo.merge(other_branch_tip, favor=MergeFavor.OURS) >>> repo.merge(other_branch_tip, flags=MergeFlag.FIND_RENAMES | MergeFlag.NO_RECURSIVE) >>> repo.merge(other_branch_tip, flags=0) # turn off FIND_RENAMES (on by default if flags omitted) You can now inspect the index file for conflicts and get back to the user to resolve if there are. Once there are no conflicts left, you can create a commit with these two parents. >>> user = repo.default_signature >>> tree = repo.index.write_tree() >>> message = "Merging branches" >>> new_commit = repo.create_commit('HEAD', user, user, message, tree, [repo.head.target, other_branch_tip]) Cherrypick =================== .. automethod:: pygit2.Repository.cherrypick Note that after a successful cherrypick you have to run :py:meth:`.Repository.state_cleanup` in order to get the repository out of cherrypicking mode. Lower-level methods =================== These methods allow more direct control over how to perform the merging. They do not modify the working directory and return an in-memory Index representing the result of the merge. .. automethod:: pygit2.Repository.merge_commits .. automethod:: pygit2.Repository.merge_trees N-way merges ============ The following methods perform the calculation for a base to an n-way merge. .. automethod:: pygit2.Repository.merge_base_many .. automethod:: pygit2.Repository.merge_base_octopus With this base at hand one can do repeated invocations of :py:meth:`.Repository.merge_commits` and :py:meth:`.Repository.merge_trees` to perform the actual merge into one tree (and deal with conflicts along the way).libgit2-pygit2-d86662e/docs/objects.rst000066400000000000000000000173601521317216500177550ustar00rootroot00000000000000********************************************************************** Objects ********************************************************************** There are four types of Git objects: blobs, trees, commits and tags. For each one pygit2 has a type, and all four types inherit from the base ``Object`` type. .. contents:: Contents :local: Object lookup ================= In the previous chapter we learnt about Object IDs. With an Oid we can ask the repository to get the associated object. To do that the ``Repository`` class implements a subset of the mapping interface. .. autoclass:: pygit2.Repository :noindex: .. automethod:: Repository.get Return the Git object for the given *id*, returns the *default* value if there's no object in the repository with that id. The id can be an Oid object, or an hexadecimal string. Example:: >>> from pygit2 import Repository >>> repo = Repository('path/to/pygit2') >>> obj = repo.get("101715bf37440d32291bde4f58c3142bcf7d8adb") >>> obj .. method:: Repository.__getitem__(id) Return the Git object for the given id, raise ``KeyError`` if there's no object in the repository with that id. The id can be an Oid object, or an hexadecimal string. .. method:: Repository.__contains__(id) Returns True if there is an object in the Repository with that id, False if there is not. The id can be an Oid object, or an hexadecimal string. The Object base type ==================== The Object type is a base type, it is not possible to make instances of it, in any way. It is the base type of the ``Blob``, ``Tree``, ``Commit`` and ``Tag`` types, so it is possible to check whether a Python value is an Object or not:: >>> from pygit2 import Object >>> commit = repository.revparse_single('HEAD') >>> print(isinstance(commit, Object)) True All Objects are immutable, they cannot be modified once they are created:: >>> commit.message = u"foobar" Traceback (most recent call last): File "", line 1, in AttributeError: attribute 'message' of '_pygit2.Commit' objects is not writable Derived types (blobs, trees, etc.) don't have a constructor, this means they cannot be created with the common idiom:: >>> from pygit2 import Blob >>> blob = Blob("data") Traceback (most recent call last): File "", line 1, in TypeError: cannot create '_pygit2.Blob' instances New objects are created using an specific API we will see later. This is the common interface for all Git objects: .. autoclass:: pygit2.Object :members: id, type, type_str, short_id, read_raw, peel, name, filemode :special-members: __eq__, __ne__, __hash__, __repr__ Blobs ================= A blob is just a raw byte string. They are the Git equivalent to files in a filesystem. This is their API: .. autoclass:: pygit2.Blob :members: Creating blobs -------------- There are a number of methods in the repository to create new blobs, and add them to the Git object database: .. autoclass:: pygit2.Repository :members: create_blob_fromworkdir, create_blob_fromdisk, create_blob_fromiobase :noindex: .. automethod:: Repository.create_blob Example: >>> id = repo.create_blob(b'foo bar') # Creates blob from a byte string >>> blob = repo[id] >>> blob.data b'foo bar' There are also some functions to calculate the id for a byte string without creating the blob object: .. autofunction:: pygit2.hash .. autofunction:: pygit2.hashfile To calculate the hash of a file using the repository's filtering rules (e.g. ``core.safecrlf``), use the repository's instance method: .. automethod:: pygit2.Repository.hashfile :noindex: Streaming blob content ---------------------- `pygit2.Blob.data` and `pygit2.Blob.read_raw()` read the full contents of the blob into memory and return Python ``bytes``. They also return the raw contents of the blob, and do not apply any filters which would be applied upon checkout to the working directory. Raw and filtered blob data can be accessed as a Python Binary I/O stream (i.e. a file-like object): .. autoclass:: pygit2.BlobIO :members: Trees ================= At the low level (libgit2) a tree is a sorted collection of tree entries. In pygit2 accessing an entry directly returns the object. A tree can be iterated, and partially implements the sequence and mapping interfaces. .. autoclass:: pygit2.Tree :members: diff_to_tree, diff_to_workdir, diff_to_index .. method:: Tree.__getitem__(name) ``Tree[name]`` Return the Object subclass instance for the given *name*. Raise ``KeyError`` if there is not a tree entry with that name. .. method:: Tree.__truediv__(name) ``Tree / name`` Return the Object subclass instance for the given *name*. Raise ``KeyError`` if there is not a tree entry with that name. This allows navigating the tree similarly to Pathlib using the slash operator via. Example:: >>> entry = tree / 'path' / 'deeper' / 'some.file' .. method:: Tree.__contains__(name) ``name in Tree`` Return True if there is a tree entry with the given name, False otherwise. .. method:: Tree.__len__() ``len(Tree)`` Return the number of objects in the tree. .. method:: Tree.__iter__() ``for object in Tree`` Return an iterator over the objects in the tree. Example:: >>> tree = commit.tree >>> len(tree) # Number of entries 6 >>> for obj in tree: # Iteration ... print(obj.id, obj.type_str, obj.name) ... 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa blob .gitignore c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 blob COPYING 32b30b90b062f66957d6790c3c155c289c34424e blob README.md c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 blob pygit2.c 85a67270a49ef16cdd3d328f06a3e4b459f09b27 blob setup.py 3d8985bbec338eb4d47c5b01b863ee89d044bd53 tree test >>> obj = tree / 'pygit2.c' # Get an object by name >>> obj <_pygit2.Blob at 0x7f08a70acc10> Creating trees -------------------- .. autoclass:: pygit2.Repository :members: TreeBuilder :noindex: .. autoclass:: pygit2.TreeBuilder :members: Commits ================= A commit is a snapshot of the working dir with meta information like author, committer and others. .. autoclass:: pygit2.Commit :members: Signatures ------------- The author and committer attributes of commit objects are ``Signature`` objects:: >>> commit.author pygit2.Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60, 'utf-8') Signatures can be compared for (in)equality. .. autoclass:: pygit2.Signature :members: Creating commits ---------------- .. autoclass:: pygit2.Repository :members: create_commit :noindex: Commits can be created by calling the ``create_commit`` method of the repository with the following parameters:: >>> author = Signature('Alice Author', 'alice@authors.tld') >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') >>> tree = repo.TreeBuilder().write() >>> repo.create_commit( ... 'refs/heads/master', # the name of the reference to update ... author, committer, 'one line commit message\n\ndetailed commit message', ... tree, # binary string representing the tree object ID ... [] # list of binary strings representing parents of the new commit ... ) '#\xe4`_ hash of an object. It is 20 bytes long. These are the three forms of an oid in pygit2: Raw oid A raw oid is represented as a Python byte string of 20 bytes length. This form can only be used to create an Oid object. Hex oid A hex oid is represented as a Python string of 40 hexadecimal chars. This form can be used to create Oid objects, just like raw oids. Also, the pygit2 API directly accepts hex oids everywhere. Oid object An ``Oid`` object can be built from the raw or hexadecimal representations (see below). The pygit2 API always returns, and accepts, ``Oid`` objects. This is the preferred way to represent an Oid, with the hexadecimal form being used for interaction with the user. The Oid type ============ .. c:type:: pygit2.Oid(raw=None, hex=None) The constructor expects either a raw or a hex oid, but not both. An Oid object is created from the hexadecimal form this way:: >>> from pygit2 import Oid >>> hex = "cff3ceaefc955f0dbe1957017db181bc49913781" >>> oid1 = Oid(hex=hex) An Oid object is created from the raw form this way:: >>> from binascii import unhexlify >>> from pygit2 import Oid >>> raw = unhexlify("cff3ceaefc955f0dbe1957017db181bc49913781") >>> oid2 = Oid(raw=raw) And the other way around, from an Oid object we can get the raw form via `oid.raw`. You can use `str(oid)` to get the hexadecimal representation of the Oid. .. method:: Oid.__str__() .. autoattribute:: pygit2.Oid.raw The Oid type supports: - rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, etc. - `hash(oid)`, so Oid objects can be used as keys in a dictionary. - `bool(oid)`, returning False if the Oid is a null SHA-1 (all zeros). - `str(oid)`, returning the hexadecimal representation of the Oid. Constants ========= .. py:data:: GIT_OID_RAWSZ .. py:data:: GIT_OID_HEXSZ .. py:data:: GIT_OID_HEX_ZERO .. py:data:: GIT_OID_MINPREFIXLEN libgit2-pygit2-d86662e/docs/packing.rst000066400000000000000000000005321521317216500177310ustar00rootroot00000000000000********************************************************************** Packing ********************************************************************** .. autoclass:: pygit2.Repository :members: pack :noindex: The PackBuilder ================ .. autoclass:: pygit2.PackBuilder :members: :undoc-members: :special-members: __len__ libgit2-pygit2-d86662e/docs/recipes.rst000066400000000000000000000030301521317216500177430ustar00rootroot00000000000000********************************************************************** Recipes ********************************************************************** A list of some standard git commands and their pygit2 equivalents. This document is a work in progress, and is organized according to the `git man page`_. ---------------------------------------------------------------------- High Level Commands ---------------------------------------------------------------------- ====================================================================== Main porcelain commands ====================================================================== .. toctree:: :maxdepth: 1 git-cherry-pick (Apply the changes introduced by some existing commits.) git-init (Create an empty git repository or reinitialize an existing one.) git-log (Show commit logs.) git-show (Show various types of objects.) git-tag (Create, list, delete or verify a tag object signed with GPG.) git clone (Clone with progress monitor) git clone --mirror (Clone with a mirroring configuration) git clone username@hostname (Clone over ssh) git-add / git-reset HEAD (Add file contents to the index / Unstage) git commit (Make an initial commit, and a subsequent commit) .. _git man page: https://www.kernel.org/pub/software/scm/git/docs/git.html libgit2-pygit2-d86662e/docs/recipes/000077500000000000000000000000001521317216500172155ustar00rootroot00000000000000libgit2-pygit2-d86662e/docs/recipes/git-add-reset.rst000066400000000000000000000035371521317216500224100ustar00rootroot00000000000000********************************************************************** git-add / git-reset ********************************************************************** ---------------------------------------------------------------------- Add file contents to the index / Stage ---------------------------------------------------------------------- We can add a new (untracked) file or a modified file to the index. .. code-block:: bash $ git add foo.txt .. code-block:: python >>> index = repo.index >>> index.add(path) >>> index.write() ---------------------------------------------------------------------- Restore the entry in the index / Unstage ---------------------------------------------------------------------- .. code-block:: bash $ git reset HEAD src/tree.c .. code-block:: python >>> index = repo.index # Remove path from the index >>> path = 'src/tree.c' >>> index.remove(path) # Restore object from db >>> obj = repo.revparse_single('HEAD').tree[path] # Get object from db >>> index.add(pygit2.IndexEntry(path, obj.id, obj.filemode)) # Add to index # Write index >>> index.write() ---------------------------------------------------------------------- Query the index state / Is file staged ? ---------------------------------------------------------------------- .. code-block:: bash $ git status foo.txt .. code-block:: python # Return True is the file is modified in the working tree >>> repo.status_file(path) & pygit2.enums.FileStatus.WT_MODIFIED ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-add_. .. _git-add: https://www.kernel.org/pub/software/scm/git/docs/git-add.html - git-reset_. .. _git-reset: https://www.kernel.org/pub/software/scm/git/docs/git-reset.html libgit2-pygit2-d86662e/docs/recipes/git-cherry-pick.rst000066400000000000000000000045551521317216500227610ustar00rootroot00000000000000********************************************************************** git-cherry-pick ********************************************************************** The convenient way to cherry-pick a commit is to use :py:meth:`.Repository.cherrypick()`. It is limited to cherry-picking with a working copy and on-disk index. .. code-block:: bash $ cd /path/to/repo $ git checkout basket $ git cherry-pick 9e044d03c .. code-block:: python repo = pygit2.Repository('/path/to/repo') repo.checkout('basket') cherry_id = pygit2.Oid('9e044d03c') repo.cherrypick(cherry_id) if repo.index.conflicts is None: tree_id = repo.index.write_tree() cherry = repo.get(cherry_id) committer = pygit2.Signature('Archimedes', 'archy@jpl-classics.org') repo.create_commit(basket.name, cherry.author, committer, cherry.message, tree_id, [basket.target]) del basket # outdated, prevent from accidentally using it repo.state_cleanup() ---------------------------------------------------------------------- Cherry-picking a commit without a working copy ---------------------------------------------------------------------- This way of cherry-picking gives you more control over the process and works on bare repositories as well as repositories with a working copy. :py:meth:`~.Repository.merge_trees()` can also be used for other tasks, for example `three-argument rebases`_. .. _`three-argument rebases`: https://www.kernel.org/pub/software/scm/git/docs/git-rebase.html .. code-block:: python repo = pygit2.Repository('/path/to/repo') cherry = repo.revparse_single('9e044d03c') basket = repo.branches.get('basket') base_tree = cherry.parents[0].tree index = repo.merge_trees(base_tree, basket, cherry) tree_id = index.write_tree(repo) author = cherry.author committer = pygit2.Signature('Archimedes', 'archy@jpl-classics.org') repo.create_commit(basket.name, author, committer, cherry.message, tree_id, [basket.target]) del None # outdated, prevent from accidentally using it ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-cherry-pick_. .. _git-cherry-pick: https://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html libgit2-pygit2-d86662e/docs/recipes/git-clone-mirror.rst000066400000000000000000000020041521317216500231340ustar00rootroot00000000000000********************************************************************** git-clone --mirror ********************************************************************** git provides an argument to set up the repository as a mirror, which involves setting the refspec to one which copies all refs and a mirror option for push in the remote. .. code-block:: bash $ git clone --mirror https://github.com/libgit2/pygit2 .. code-block:: python def init_remote(repo, name, url): # Create the remote with a mirroring url remote = repo.remotes.create(name, url, "+refs/*:refs/*") # And set the configuration option to true for the push command mirror_var = f"remote.{name.decode()}.mirror" repo.config[mirror_var] = True # Return the remote, which pygit2 will use to perform the clone return remote print("Cloning pygit2 as mirror") pygit2.clone_repository("https://github.com/libgit2/pygit2", "pygit2.git", bare=True, remote=init_remote) libgit2-pygit2-d86662e/docs/recipes/git-clone-progress.rst000066400000000000000000000012341521317216500234720ustar00rootroot00000000000000********************************************************************** git-clone with progress monitor ********************************************************************** Example for cloning a git repository with progress monitoring: .. code-block:: bash $ git clone https://github.com/libgit2/pygit2 .. code-block:: python class MyRemoteCallbacks(pygit2.RemoteCallbacks): def transfer_progress(self, stats): print(f'{stats.indexed_objects}/{stats.total_objects}') print("Cloning pygit2") pygit2.clone_repository("https://github.com/libgit2/pygit2", "pygit2.git", callbacks=MyRemoteCallbacks()) libgit2-pygit2-d86662e/docs/recipes/git-clone-ssh.rst000066400000000000000000000023101521317216500224170ustar00rootroot00000000000000********************************************************************** git-clone ssh://git@example.com ********************************************************************** Example for cloning a git repository over ssh. .. code-block:: bash $ git clone git@example.com .. code-block:: python class MyRemoteCallbacks(pygit2.RemoteCallbacks): def credentials(self, url, username_from_url, allowed_types): if allowed_types & pygit2.enums.CredentialType.USERNAME: return pygit2.Username("git") elif allowed_types & pygit2.enums.CredentialType.SSH_KEY: return pygit2.Keypair("git", "id_rsa.pub", "id_rsa", "") else: return None print("Cloning pygit2 over ssh") pygit2.clone_repository("ssh://github.com/libgit2/pygit2", "pygit2.git", callbacks=MyRemoteCallbacks()) print("Cloning pygit2 over ssh with the username in the URL") keypair = pygit2.Keypair("git", "id_rsa.pub", "id_rsa", "") callbacks = pygit2.RemoteCallbacks(credentials=keypair) pygit2.clone_repository("ssh://git@github.com/libgit2/pygit2", "pygit2.git", callbacks=callbacks) libgit2-pygit2-d86662e/docs/recipes/git-commit.rst000066400000000000000000000064171521317216500220300ustar00rootroot00000000000000********************************************************************** git-commit ********************************************************************** ---------------------------------------------------------------------- Initial commit ---------------------------------------------------------------------- Add everything, and make an initial commit: .. code-block:: bash $ git add . $ git commit -m "Initial commit" .. code-block:: python >>> index = repo.index >>> index.add_all() >>> index.write() >>> ref = "HEAD" >>> author = Signature('Alice Author', 'alice@authors.tld') >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') >>> message = "Initial commit" >>> tree = index.write_tree() >>> parents = [] >>> repo.create_commit(ref, author, committer, message, tree, parents) ---------------------------------------------------------------------- Subsequent commit ---------------------------------------------------------------------- Once ``HEAD`` has a commit to point to, you can use ``repo.head.name`` as the reference to be updated by the commit, and you should name parents: .. code-block:: python >>> ref = repo.head.name >>> parents = [repo.head.target] The rest is the same: .. code-block:: python >>> index = repo.index >>> index.add_all() >>> index.write() >>> author = Signature('Alice Author', 'alice@authors.tld') >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') >>> message = "Initial commit" >>> tree = index.write_tree() >>> repo.create_commit(ref, author, committer, message, tree, parents) ---------------------------------------------------------------------- Signing a commit ---------------------------------------------------------------------- Add everything, and commit with a GPG signature: .. code-block:: bash $ git add . $ git commit -S -m "Signed commit" .. code-block:: python >>> index = repo.index >>> index.add_all() >>> index.write() >>> author = Signature('Alice Author', 'alice@authors.tld') >>> committer = Signature('Cecil Committer', 'cecil@committers.tld') >>> message = "Signed commit" >>> tree = index.write_tree() >>> parents = [] >>> commit_string = repo.create_commit_string( >>> author, committer, message, tree, parents >>> ) The ``commit_string`` can then be signed by a third party library: .. code-block:: python >>> gpg = YourGPGToolHere() >>> signed_commit = gpg.sign( >>> commit_string, >>> passphrase='secret', >>> detach=True, >>> ) .. note:: The commit signature should resemble: .. code-block:: none >>> -----BEGIN PGP SIGNATURE----- >>> >>> < base64 encoded hash here > >>> -----END PGP SIGNATURE----- The signed commit can then be added to the branch: .. code-block:: python >>> commit = repo.create_commit_with_signature( >>> commit_string, signed_commit.data.decode('utf-8') >>> ) >>> repo.head.set_target(commit) ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-commit_. .. _git-commit: https://www.kernel.org/pub/software/scm/git/docs/git-commit.html libgit2-pygit2-d86662e/docs/recipes/git-init.rst000066400000000000000000000023751521317216500215020ustar00rootroot00000000000000********************************************************************** git-init ********************************************************************** ---------------------------------------------------------------------- Creating a new repository ---------------------------------------------------------------------- ====================================================================== Create bare repository ====================================================================== .. code-block:: bash $ git init --bare path/to/git .. code-block:: python >>> pygit2.init_repository('path/to/git', True) ====================================================================== Create standard repository ====================================================================== .. code-block:: bash $ git init path/to/git .. code-block:: python >>> pygit2.init_repository('path/to/git', False) ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-init_ .. _git-init: https://www.kernel.org/pub/software/scm/git/docs/git-init.html libgit2-pygit2-d86662e/docs/recipes/git-log.rst000066400000000000000000000035361521317216500213200ustar00rootroot00000000000000********************************************************************** git-log ********************************************************************** ---------------------------------------------------------------------- Showing HEAD commit logs ---------------------------------------------------------------------- ====================================================================== Show HEAD commit ====================================================================== .. code-block:: bash $ git log -1 .. code-block:: python >>> commit = repo[repo.head.target] >>> commit.message 'commit message' ====================================================================== Traverse commit history ====================================================================== .. code-block:: bash $ git log .. code-block:: python >>> last = repo[repo.head.target] >>> for commit in repo.walk(last.id, pygit2.enums.SortMode.TIME): >>> print(commit.message) # or some other operation ====================================================================== Show trailers from the last commit ====================================================================== .. code-block:: bash $ git log --format='%(trailers:key=Bug)' .. code-block:: python >>> last = repo[repo.head.target] >>> for commit in repo.walk(last.id, pygit2.enums.SortMode.TIME): >>> print(commit.message_trailers.get('Bug')) ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-log_. - `libgit2 discussion about walker behavior `_. Note that the libgit2's walker functions differently than ``git-log`` in some ways. .. _git-log: https://www.kernel.org/pub/software/scm/git/docs/git-log.html libgit2-pygit2-d86662e/docs/recipes/git-show.rst000066400000000000000000000043661521317216500215210ustar00rootroot00000000000000********************************************************************** git-show ********************************************************************** ---------------------------------------------------------------------- Showing a commit ---------------------------------------------------------------------- .. code-block:: bash $ git show d370f56 .. code-block:: python >>> repo = pygit2.Repository('/path/to/repository') >>> commit = repo.revparse_single('d370f56') ====================================================================== Show log message ====================================================================== >>> message = commit.message ====================================================================== Show SHA hash ====================================================================== >>> hash = str(commit.id) ====================================================================== Show diff ====================================================================== >>> diff = repo.diff(commit.parents[0], commit) ====================================================================== Show all files in commit ====================================================================== >>> for e in commit.tree: >>> print(e.name) ====================================================================== Produce something like a ``git show`` message ====================================================================== Then you can make your message: >>> from datetime import datetime, timezone, timedelta >>> tzinfo = timezone( timedelta(minutes=commit.author.offset) ) >>> >>> dt = datetime.fromtimestamp(float(commit.author.time), tzinfo) >>> timestr = dt.strftime('%c %z') >>> msg = '\n'.join([f'commit {commit.tree_id}', ... f'Author: {commit.author.name} <{commit.author.email}>', ... f'Date: {timestr}', ... '', ... commit.message]) ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-show_. .. _git-show: https://www.kernel.org/pub/software/scm/git/docs/git-show.html libgit2-pygit2-d86662e/docs/recipes/git-tag.rst000066400000000000000000000013271521317216500213060ustar00rootroot00000000000000********************************************************************** git-tag ********************************************************************** ---------------------------------------------------------------------- Showing all tags ---------------------------------------------------------------------- .. code-block:: bash $ git tag .. code-block:: python >>> import re >>> regex = re.compile('^refs/tags/') >>> [r for r in repo.references if regex.match(r)] ---------------------------------------------------------------------- References ---------------------------------------------------------------------- - git-tag_. .. _git-tag: https://www.kernel.org/pub/software/scm/git/docs/git-tag.html libgit2-pygit2-d86662e/docs/references.rst000066400000000000000000000057161521317216500204470ustar00rootroot00000000000000********************************************************************** References ********************************************************************** .. autoclass:: pygit2.Repository :members: lookup_reference, lookup_reference_dwim, raw_listall_references, resolve_refish :noindex: .. attribute:: references Returns an instance of the References class (see below). .. autoclass:: pygit2.repository.References :members: :undoc-members: :special-members: __getitem__, __iter__, __contains__ Example:: >>> all_refs = list(repo.references) >>> master_ref = repo.references["refs/heads/master"] >>> commit = master_ref.peel() # or repo[master_ref.target] # Create a reference >>> ref = repo.references.create('refs/tags/version1', LAST_COMMIT) # Delete a reference >>> repo.references.delete('refs/tags/version1') # Pack loose references >>> repo.references.compress() Functions =================================== .. autofunction:: pygit2.reference_is_valid_name Check if the passed string is a valid reference name. Example:: >>> from pygit2 import reference_is_valid_name >>> reference_is_valid_name("refs/heads/master") True >>> reference_is_valid_name("HEAD") True >>> reference_is_valid_name("refs/heads/..") False The Reference type ==================== .. autoclass:: pygit2.Reference :members: :special-members: __eq__, __ne__ :exclude-members: log .. automethod:: log The HEAD ==================== Example. These two lines are equivalent:: >>> head = repo.references['HEAD'].resolve() >>> head = repo.head .. autoattribute:: pygit2.Repository.head .. autoattribute:: pygit2.Repository.head_is_detached .. autoattribute:: pygit2.Repository.head_is_unborn The reference log ==================== Example:: >>> head = repo.references.get('refs/heads/master') # Returns None if not found >>> # Almost equivalent to >>> head = repo.references['refs/heads/master'] # Raises KeyError if not found >>> for entry in head.log(): ... print(entry.message) .. autoclass:: pygit2.RefLogEntry :members: Reference Transactions ======================= For atomic updates of multiple references, use transactions. See the :doc:`transactions` documentation for details. Example:: # Update multiple refs atomically with repo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.lock_ref('refs/heads/develop') txn.set_target('refs/heads/master', new_oid, message='Release') txn.set_target('refs/heads/develop', dev_oid, message='Continue dev') Notes ==================== .. automethod:: pygit2.Repository.notes .. automethod:: pygit2.Repository.create_note .. automethod:: pygit2.Repository.lookup_note The Note type -------------------- .. autoattribute:: pygit2.Note.annotated_id .. autoattribute:: pygit2.Note.id .. autoattribute:: pygit2.Note.message .. automethod:: pygit2.Note.remove libgit2-pygit2-d86662e/docs/remotes.rst000066400000000000000000000042551521317216500200010ustar00rootroot00000000000000********************************************************************** Remotes ********************************************************************** .. py:attribute:: Repository.remotes The collection of configured remotes, an instance of :py:class:`pygit2.remotes.RemoteCollection` The remote collection ========================== .. autoclass:: pygit2.remotes.RemoteCollection :members: The Remote type ==================== .. autoclass:: pygit2.Remote :members: The RemoteCallbacks type ======================== See :doc:`callbacks` for the full reference. The following autoclass is only included here for discoverability. .. autoclass:: pygit2.RemoteCallbacks :noindex: :members: The TransferProgress type =========================== This class contains the data which is available to us during a fetch. .. autoclass:: pygit2.remotes.TransferProgress :members: The RemoteHead type =================== Description of a reference advertised by a remote server, returned by :meth:`pygit2.Remote.list_heads`. .. autoclass:: pygit2.remotes.RemoteHead :members: The PushUpdate type =================== Represents an update which will be performed on the remote during push. Passed to :meth:`pygit2.RemoteCallbacks.push_negotiation`. .. autoclass:: pygit2.remotes.PushUpdate :members: The Refspec type =================== Refspecs objects are not constructed directly, but returned by :meth:`pygit2.Remote.get_refspec`. To create a new a refspec on a Remote, use :meth:`pygit2.Remote.add_fetch` or :meth:`pygit2.Remote.add_push`. .. autoclass:: pygit2.refspec.Refspec :members: Credentials ================ There are several types of credentials. All of them are callable objects, with the appropriate signature for the credentials callback. They will ignore all the arguments and return themselves. This is useful for scripts where the credentials are known ahead of time. More complete interfaces would want to look up in their keychain or ask the user for the data to use in the credentials. .. autoclass:: pygit2.Username .. autoclass:: pygit2.UserPass .. autoclass:: pygit2.Keypair .. autoclass:: pygit2.KeypairFromAgent .. autoclass:: pygit2.KeypairFromMemory libgit2-pygit2-d86662e/docs/repository.rst000066400000000000000000000063301521317216500205360ustar00rootroot00000000000000********************************************************************** Repository ********************************************************************** Everything starts either by creating a new repository, or by opening an existing one. .. contents:: Contents :local: Functions =================================== .. autofunction:: pygit2.init_repository Example:: >>> from pygit2 import init_repository >>> repo = init_repository('test') # Creates a non-bare repository >>> repo = init_repository('test', bare=True) # Creates a bare repository .. autofunction:: pygit2.clone_repository Example:: >>> from pygit2 import clone_repository >>> repo_url = 'git://github.com/libgit2/pygit2.git' >>> repo_path = '/path/to/create/repository' >>> repo = clone_repository(repo_url, repo_path) # Clones a non-bare repository >>> repo = clone_repository(repo_url, repo_path, bare=True) # Clones a bare repository >>> repo = clone_repository(repo_url, repo_path, proxy=True) # Enable automatic proxy detection .. autofunction:: pygit2.discover_repository Example:: >>> current_working_directory = os.getcwd() >>> repository_path = discover_repository(current_working_directory) >>> repo = Repository(repository_path) .. autofunction:: pygit2.tree_entry_cmp(object, other) The Repository class =================================== The API of the Repository class is quite large. Since this documentation is organized by features, the related bits are explained in the related chapters, for instance the :py:meth:`pygit2.Repository.checkout` method is explained in the Checkout section. Below there are some general attributes and methods: .. autoclass:: pygit2.Repository :members: ahead_behind, amend_commit, applies, apply, create_reference, default_signature, descendant_of, describe, free, get_attr, is_bare, is_empty, is_shallow, odb, path, path_is_ignored, reset, revert_commit, state_cleanup, workdir, write, write_archive, set_odb, set_refdb The Repository constructor will most commonly be called with one argument, the path of the repository to open. Alternatively, constructing a repository with no arguments will create a repository with no backends. You can use this path to create repositories with custom backends. Note that most operations on the repository are considered invalid and may lead to undefined behavior if attempted before providing an odb and refdb via :py:meth:`set_odb` and :py:meth:`set_refdb`. Parameters: path The path to open — if not provided, the repository will have no backend. flags Flags controlling how to open the repository can optionally be provided — any combination of: * enums.RepositoryOpenFlag.NO_SEARCH * enums.RepositoryOpenFlag.CROSS_FS * enums.RepositoryOpenFlag.BARE * enums.RepositoryOpenFlag.NO_DOTGIT * enums.RepositoryOpenFlag.FROM_ENV Example:: >>> from pygit2 import Repository >>> repo = Repository('pygit2/.git') The Odb class =================================== .. autoclass:: pygit2.Odb :members: The Refdb class =================================== .. autoclass:: pygit2.Refdb :members: libgit2-pygit2-d86662e/docs/requirements.txt000066400000000000000000000000301521317216500210400ustar00rootroot00000000000000sphinx sphinx-rtd-theme libgit2-pygit2-d86662e/docs/revparse.rst000066400000000000000000000011411521317216500201410ustar00rootroot00000000000000********************************************************************** Revision parsing ********************************************************************** .. autoclass:: pygit2.Repository :members: revparse, revparse_ext, revparse_single :noindex: You can use any of the fancy `` forms supported by libgit2:: >>> commit = repo.revparse_single('HEAD^') .. autoclass:: pygit2.RevSpec :members: Constants: .. py:data:: pygit2.enums.RevSpecFlag.SINGLE :noindex: .. py:data:: pygit2.enums.RevSpecFlag.RANGE :noindex: .. py:data:: pygit2.enums.RevSpecFlag.MERGE_BASE :noindex: libgit2-pygit2-d86662e/docs/settings.rst000066400000000000000000000003231521317216500201530ustar00rootroot00000000000000********************************************************************** Settings ********************************************************************** .. contents:: .. autoclass:: pygit2.Settings :members: libgit2-pygit2-d86662e/docs/submodule.rst000066400000000000000000000012131521317216500203110ustar00rootroot00000000000000********************************************************************** Submodules ********************************************************************** A submodule is a foreign repository that is embedded within a dedicated subdirectory of the repositories tree. .. autoclass:: pygit2.Repository :members: listall_submodules :noindex: .. py:attribute:: Repository.submodules The collection of submodules, an instance of :py:class:`pygit2.submodules.SubmoduleCollection` .. autoclass:: pygit2.submodules.SubmoduleCollection :members: The Submodule type ==================== .. autoclass:: pygit2.Submodule :members: libgit2-pygit2-d86662e/docs/transactions.rst000066400000000000000000000073371521317216500210370ustar00rootroot00000000000000********************************************************************** Reference Transactions ********************************************************************** Reference transactions allow you to update multiple references atomically. All reference updates within a transaction either succeed together or fail together, ensuring repository consistency. Basic Usage =========== Use the :meth:`Repository.transaction` method as a context manager. The transaction commits automatically when the context exits successfully, or rolls back if an exception is raised:: with repo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_oid, message='Update master') Atomic Multi-Reference Updates =============================== Transactions are useful when you need to update multiple references atomically:: # Swap two branches atomically with repo.transaction() as txn: txn.lock_ref('refs/heads/branch-a') txn.lock_ref('refs/heads/branch-b') # Get current targets ref_a = repo.lookup_reference('refs/heads/branch-a') ref_b = repo.lookup_reference('refs/heads/branch-b') # Swap them txn.set_target('refs/heads/branch-a', ref_b.target, message='Swap') txn.set_target('refs/heads/branch-b', ref_a.target, message='Swap') Automatic Rollback ================== If an exception occurs during the transaction, changes are automatically rolled back:: try: with repo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_oid) # If this raises an exception, the ref update is rolled back validate_commit(new_oid) except ValidationError: # Master still points to its original target pass Manual Commit ============= While the context manager is recommended, you can manually manage transactions:: from pygit2 import ReferenceTransaction txn = ReferenceTransaction(repo) try: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_oid, message='Update') txn.commit() finally: del txn # Ensure transaction is freed API Reference ============= Repository Methods ------------------ .. automethod:: pygit2.Repository.transaction The ReferenceTransaction Type ------------------------------ .. autoclass:: pygit2.ReferenceTransaction :members: :special-members: __enter__, __exit__ Usage Notes =========== - Always lock a reference with :meth:`~ReferenceTransaction.lock_ref` before modifying it - Transactions operate on reference names, not Reference objects - Symbolic references can be updated with :meth:`~ReferenceTransaction.set_symbolic_target` - References can be deleted with :meth:`~ReferenceTransaction.remove` - The signature parameter defaults to the repository's configured identity Thread Safety ============= Transactions are thread-local and must be used from the thread that created them. Attempting to use a transaction from a different thread raises :exc:`RuntimeError`:: # This is safe - each thread has its own transaction def thread1(): with repo.transaction() as txn: txn.lock_ref('refs/heads/branch1') txn.set_target('refs/heads/branch1', oid1) def thread2(): with repo.transaction() as txn: txn.lock_ref('refs/heads/branch2') txn.set_target('refs/heads/branch2', oid2) # Both threads can run concurrently without conflicts Different threads can hold transactions simultaneously as long as they don't attempt to lock the same references. If two threads try to acquire locks in different orders, libgit2 will detect potential deadlocks and raise an error. libgit2-pygit2-d86662e/docs/worktree.rst000066400000000000000000000005301521317216500201550ustar00rootroot00000000000000********************************************************************** Worktrees ********************************************************************** .. automethod:: pygit2.Repository.add_worktree .. automethod:: pygit2.Repository.list_worktrees .. automethod:: pygit2.Repository.lookup_worktree .. autoclass:: pygit2.Worktree :members: libgit2-pygit2-d86662e/misc/000077500000000000000000000000001521317216500155665ustar00rootroot00000000000000libgit2-pygit2-d86662e/misc/valgrind-python.supp000066400000000000000000000202511521317216500216240ustar00rootroot00000000000000# # This is a valgrind suppression file that should be used when using valgrind. # # Here's an example of running valgrind: # # cd python/dist/src # valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \ # ./python -E ./Lib/test/regrtest.py -u gui,network # # You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER # to use the preferred suppressions with address_in_range. # # If you do not want to recompile Python, you can uncomment # suppressions for _PyObject_Free and _PyObject_Realloc. # # See Misc/README.valgrind for more information. # all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 fun:address_in_range } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 fun:address_in_range } { ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64) Memcheck:Value8 fun:address_in_range } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond fun:address_in_range } # # Leaks (including possible leaks) # Hmmm, I wonder if this masks some real leaks. I think it does. # Will need to fix that. # { Suppress leaking the GIL. Happens once per process, see comment in ceval.c. Memcheck:Leak fun:malloc fun:PyThread_allocate_lock fun:PyEval_InitThreads } { Suppress leaking the GIL after a fork. Memcheck:Leak fun:malloc fun:PyThread_allocate_lock fun:PyEval_ReInitThreads } { Suppress leaking the autoTLSkey. This looks like it shouldn't leak though. Memcheck:Leak fun:malloc fun:PyThread_create_key fun:_PyGILState_Init fun:Py_InitializeEx fun:Py_Main } { Hmmm, is this a real leak or like the GIL? Memcheck:Leak fun:malloc fun:PyThread_ReInitTLS } { Handle PyMalloc confusing valgrind (possibly leaked) Memcheck:Leak fun:realloc fun:_PyObject_GC_Resize fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING } { Handle PyMalloc confusing valgrind (possibly leaked) Memcheck:Leak fun:malloc fun:_PyObject_GC_New fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING } { Handle PyMalloc confusing valgrind (possibly leaked) Memcheck:Leak fun:malloc fun:_PyObject_GC_NewVar fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING } # # Non-python specific leaks # { Handle pthread issue (possibly leaked) Memcheck:Leak fun:calloc fun:allocate_dtv fun:_dl_allocate_tls_storage fun:_dl_allocate_tls } { Handle pthread issue (possibly leaked) Memcheck:Leak fun:memalign fun:_dl_allocate_tls_storage fun:_dl_allocate_tls } ###{ ### ADDRESS_IN_RANGE/Invalid read of size 4 ### Memcheck:Addr4 ### fun:_PyObject_Free ###} ### ###{ ### ADDRESS_IN_RANGE/Invalid read of size 4 ### Memcheck:Value4 ### fun:_PyObject_Free ###} ### ###{ ### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 ### Memcheck:Addr8 ### fun:_PyObject_Free ###} ### ###{ ### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 ### Memcheck:Value8 ### fun:_PyObject_Free ###} ### ###{ ### ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value ### Memcheck:Cond ### fun:_PyObject_Free ###} ###{ ### ADDRESS_IN_RANGE/Invalid read of size 4 ### Memcheck:Addr4 ### fun:_PyObject_Realloc ###} ### ###{ ### ADDRESS_IN_RANGE/Invalid read of size 4 ### Memcheck:Value4 ### fun:_PyObject_Realloc ###} ### ###{ ### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 ### Memcheck:Addr8 ### fun:_PyObject_Realloc ###} ### ###{ ### ADDRESS_IN_RANGE/Use of uninitialised value of size 8 ### Memcheck:Value8 ### fun:_PyObject_Realloc ###} ### ###{ ### ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value ### Memcheck:Cond ### fun:_PyObject_Realloc ###} ### ### All the suppressions below are for errors that occur within libraries ### that Python uses. The problems to not appear to be related to Python's ### use of the libraries. ### { Generic ubuntu ld problems Memcheck:Addr8 obj:/lib/ld-2.4.so obj:/lib/ld-2.4.so obj:/lib/ld-2.4.so obj:/lib/ld-2.4.so } { Generic gentoo ld problems Memcheck:Cond obj:/lib/ld-2.3.4.so obj:/lib/ld-2.3.4.so obj:/lib/ld-2.3.4.so obj:/lib/ld-2.3.4.so } { DBM problems, see test_dbm Memcheck:Param write(buf) fun:write obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 fun:dbm_close } { DBM problems, see test_dbm Memcheck:Value8 fun:memmove obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 fun:dbm_store fun:dbm_ass_sub } { DBM problems, see test_dbm Memcheck:Cond obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 fun:dbm_store fun:dbm_ass_sub } { DBM problems, see test_dbm Memcheck:Cond fun:memmove obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 obj:/usr/lib/libdb1.so.2 fun:dbm_store fun:dbm_ass_sub } { GDBM problems, see test_gdbm Memcheck:Param write(buf) fun:write fun:gdbm_open } { ZLIB problems, see test_gzip Memcheck:Cond obj:/lib/libz.so.1.2.3 obj:/lib/libz.so.1.2.3 fun:deflate } { Avoid problems w/readline doing a putenv and leaking on exit Memcheck:Leak fun:malloc fun:xmalloc fun:sh_set_lines_and_columns fun:_rl_get_screen_size fun:_rl_init_terminal_io obj:/lib/libreadline.so.4.3 fun:rl_initialize } ### ### These occur from somewhere within the SSL, when running ### test_socket_sll. They are too general to leave on by default. ### ###{ ### somewhere in SSL stuff ### Memcheck:Cond ### fun:memset ###} ###{ ### somewhere in SSL stuff ### Memcheck:Value4 ### fun:memset ###} ### ###{ ### somewhere in SSL stuff ### Memcheck:Cond ### fun:MD5_Update ###} ### ###{ ### somewhere in SSL stuff ### Memcheck:Value4 ### fun:MD5_Update ###} # Fedora's package "openssl-1.0.1-0.1.beta2.fc17.x86_64" on x86_64 # See http://bugs.python.org/issue14171 { openssl 1.0.1 prng 1 Memcheck:Cond fun:bcmp fun:fips_get_entropy fun:FIPS_drbg_instantiate fun:RAND_init_fips fun:OPENSSL_init_library fun:SSL_library_init fun:init_hashlib } { openssl 1.0.1 prng 2 Memcheck:Cond fun:fips_get_entropy fun:FIPS_drbg_instantiate fun:RAND_init_fips fun:OPENSSL_init_library fun:SSL_library_init fun:init_hashlib } { openssl 1.0.1 prng 3 Memcheck:Value8 fun:_x86_64_AES_encrypt_compact fun:AES_encrypt } # # All of these problems come from using test_socket_ssl # { from test_socket_ssl Memcheck:Cond fun:BN_bin2bn } { from test_socket_ssl Memcheck:Cond fun:BN_num_bits_word } { from test_socket_ssl Memcheck:Value4 fun:BN_num_bits_word } { from test_socket_ssl Memcheck:Cond fun:BN_mod_exp_mont_word } { from test_socket_ssl Memcheck:Cond fun:BN_mod_exp_mont } { from test_socket_ssl Memcheck:Param write(buf) fun:write obj:/usr/lib/libcrypto.so.0.9.7 } { from test_socket_ssl Memcheck:Cond fun:RSA_verify } { from test_socket_ssl Memcheck:Value4 fun:RSA_verify } { from test_socket_ssl Memcheck:Value4 fun:DES_set_key_unchecked } { from test_socket_ssl Memcheck:Value4 fun:DES_encrypt2 } { from test_socket_ssl Memcheck:Cond obj:/usr/lib/libssl.so.0.9.7 } { from test_socket_ssl Memcheck:Value4 obj:/usr/lib/libssl.so.0.9.7 } { from test_socket_ssl Memcheck:Cond fun:BUF_MEM_grow_clean } { from test_socket_ssl Memcheck:Cond fun:memcpy fun:ssl3_read_bytes } { from test_socket_ssl Memcheck:Cond fun:SHA1_Update } { from test_socket_ssl Memcheck:Value4 fun:SHA1_Update } { test_buffer_non_debug Memcheck:Addr4 fun:PyUnicodeUCS2_FSConverter } { test_buffer_non_debug Memcheck:Addr4 fun:PyUnicode_FSConverter } { wcscmp_false_positive Memcheck:Addr8 fun:wcscmp fun:_PyOS_GetOpt fun:Py_Main fun:main } # Additional suppressions for the unified decimal tests: { test_decimal Memcheck:Addr4 fun:PyUnicodeUCS2_FSConverter } { test_decimal2 Memcheck:Addr4 fun:PyUnicode_FSConverter } libgit2-pygit2-d86662e/mypy-stubtest.ini000066400000000000000000000007151521317216500202100ustar00rootroot00000000000000# Config file for testing the stub file (_pygit2.pyi) with "stubtest" # (tool shipped with mypy). # # Run "build.sh stubtest", or: # stubtest --mypy-config-file test/mypy-stubtest.ini pygit2._pygit2 # # Format info: # https://mypy.readthedocs.io/en/stable/config_file.html [mypy] warn_unused_configs = True disallow_any_explicit = True # don't follow import pygit2 from _pygit2.pyi, we only want to check the pyi file. [mypy-pygit2] follow_imports = skip libgit2-pygit2-d86662e/mypy.ini000066400000000000000000000004031521317216500163270ustar00rootroot00000000000000[mypy] warn_unused_configs = True warn_redundant_casts = True warn_unused_ignores = True no_implicit_reexport = True disallow_subclassing_any = True disallow_untyped_decorators = True [mypy-test.*] disallow_untyped_defs = True disallow_untyped_calls = True libgit2-pygit2-d86662e/pygit2/000077500000000000000000000000001521317216500160515ustar00rootroot00000000000000libgit2-pygit2-d86662e/pygit2/__init__.py000066400000000000000000000731021521317216500201650ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # ruff: noqa: F401 F403 F405 # Standard Library import functools import os import typing # High level API from . import enums from ._build import __version__ # Low level API from ._pygit2 import ( GIT_APPLY_LOCATION_BOTH, GIT_APPLY_LOCATION_INDEX, GIT_APPLY_LOCATION_WORKDIR, GIT_BLAME_FIRST_PARENT, GIT_BLAME_IGNORE_WHITESPACE, GIT_BLAME_NORMAL, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES, GIT_BLAME_TRACK_COPIES_SAME_FILE, GIT_BLAME_USE_MAILMAP, GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT, GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD, GIT_BLOB_FILTER_CHECK_FOR_BINARY, GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES, GIT_BRANCH_ALL, GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE, GIT_CHECKOUT_ALLOW_CONFLICTS, GIT_CHECKOUT_CONFLICT_STYLE_DIFF3, GIT_CHECKOUT_CONFLICT_STYLE_MERGE, GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3, GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH, GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, GIT_CHECKOUT_DONT_REMOVE_EXISTING, GIT_CHECKOUT_DONT_UPDATE_INDEX, GIT_CHECKOUT_DONT_WRITE_INDEX, GIT_CHECKOUT_DRY_RUN, GIT_CHECKOUT_FORCE, GIT_CHECKOUT_NO_REFRESH, GIT_CHECKOUT_NONE, GIT_CHECKOUT_RECREATE_MISSING, GIT_CHECKOUT_REMOVE_IGNORED, GIT_CHECKOUT_REMOVE_UNTRACKED, GIT_CHECKOUT_SAFE, GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES, GIT_CHECKOUT_SKIP_UNMERGED, GIT_CHECKOUT_UPDATE_ONLY, GIT_CHECKOUT_USE_OURS, GIT_CHECKOUT_USE_THEIRS, GIT_CONFIG_HIGHEST_LEVEL, GIT_CONFIG_LEVEL_APP, GIT_CONFIG_LEVEL_GLOBAL, GIT_CONFIG_LEVEL_LOCAL, GIT_CONFIG_LEVEL_PROGRAMDATA, GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_WORKTREE, GIT_CONFIG_LEVEL_XDG, GIT_DELTA_ADDED, GIT_DELTA_CONFLICTED, GIT_DELTA_COPIED, GIT_DELTA_DELETED, GIT_DELTA_IGNORED, GIT_DELTA_MODIFIED, GIT_DELTA_RENAMED, GIT_DELTA_TYPECHANGE, GIT_DELTA_UNMODIFIED, GIT_DELTA_UNREADABLE, GIT_DELTA_UNTRACKED, GIT_DESCRIBE_ALL, GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, GIT_DIFF_BREAK_REWRITES, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY, GIT_DIFF_DISABLE_PATHSPEC_MATCH, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS, GIT_DIFF_FIND_ALL, GIT_DIFF_FIND_AND_BREAK_REWRITES, GIT_DIFF_FIND_BY_CONFIG, GIT_DIFF_FIND_COPIES, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE, GIT_DIFF_FIND_EXACT_MATCH_ONLY, GIT_DIFF_FIND_FOR_UNTRACKED, GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE, GIT_DIFF_FIND_IGNORE_WHITESPACE, GIT_DIFF_FIND_REMOVE_UNMODIFIED, GIT_DIFF_FIND_RENAMES, GIT_DIFF_FIND_RENAMES_FROM_REWRITES, GIT_DIFF_FIND_REWRITES, GIT_DIFF_FLAG_BINARY, GIT_DIFF_FLAG_EXISTS, GIT_DIFF_FLAG_NOT_BINARY, GIT_DIFF_FLAG_VALID_ID, GIT_DIFF_FLAG_VALID_SIZE, GIT_DIFF_FORCE_BINARY, GIT_DIFF_FORCE_TEXT, GIT_DIFF_IGNORE_BLANK_LINES, GIT_DIFF_IGNORE_CASE, GIT_DIFF_IGNORE_FILEMODE, GIT_DIFF_IGNORE_SUBMODULES, GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_CHANGE, GIT_DIFF_IGNORE_WHITESPACE_EOL, GIT_DIFF_INCLUDE_CASECHANGE, GIT_DIFF_INCLUDE_IGNORED, GIT_DIFF_INCLUDE_TYPECHANGE, GIT_DIFF_INCLUDE_TYPECHANGE_TREES, GIT_DIFF_INCLUDE_UNMODIFIED, GIT_DIFF_INCLUDE_UNREADABLE, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED, GIT_DIFF_INCLUDE_UNTRACKED, GIT_DIFF_INDENT_HEURISTIC, GIT_DIFF_MINIMAL, GIT_DIFF_NORMAL, GIT_DIFF_PATIENCE, GIT_DIFF_RECURSE_IGNORED_DIRS, GIT_DIFF_RECURSE_UNTRACKED_DIRS, GIT_DIFF_REVERSE, GIT_DIFF_SHOW_BINARY, GIT_DIFF_SHOW_UNMODIFIED, GIT_DIFF_SHOW_UNTRACKED_CONTENT, GIT_DIFF_SKIP_BINARY_CHECK, GIT_DIFF_STATS_FULL, GIT_DIFF_STATS_INCLUDE_SUMMARY, GIT_DIFF_STATS_NONE, GIT_DIFF_STATS_NUMBER, GIT_DIFF_STATS_SHORT, GIT_DIFF_UPDATE_INDEX, GIT_FILEMODE_BLOB, GIT_FILEMODE_BLOB_EXECUTABLE, GIT_FILEMODE_COMMIT, GIT_FILEMODE_LINK, GIT_FILEMODE_TREE, GIT_FILEMODE_UNREADABLE, GIT_FILTER_ALLOW_UNSAFE, GIT_FILTER_ATTRIBUTES_FROM_COMMIT, GIT_FILTER_ATTRIBUTES_FROM_HEAD, GIT_FILTER_CLEAN, GIT_FILTER_DEFAULT, GIT_FILTER_DRIVER_PRIORITY, GIT_FILTER_NO_SYSTEM_ATTRIBUTES, GIT_FILTER_SMUDGE, GIT_FILTER_TO_ODB, GIT_FILTER_TO_WORKTREE, GIT_MERGE_ANALYSIS_FASTFORWARD, GIT_MERGE_ANALYSIS_NONE, GIT_MERGE_ANALYSIS_NORMAL, GIT_MERGE_ANALYSIS_UNBORN, GIT_MERGE_ANALYSIS_UP_TO_DATE, GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, GIT_MERGE_PREFERENCE_NO_FASTFORWARD, GIT_MERGE_PREFERENCE_NONE, GIT_OBJECT_ANY, GIT_OBJECT_BLOB, GIT_OBJECT_COMMIT, GIT_OBJECT_INVALID, GIT_OBJECT_OFS_DELTA, GIT_OBJECT_REF_DELTA, GIT_OBJECT_TAG, GIT_OBJECT_TREE, GIT_OID_HEX_ZERO, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN, GIT_OID_RAWSZ, GIT_REFERENCES_ALL, GIT_REFERENCES_BRANCHES, GIT_REFERENCES_TAGS, GIT_RESET_HARD, GIT_RESET_MIXED, GIT_RESET_SOFT, GIT_REVSPEC_MERGE_BASE, GIT_REVSPEC_RANGE, GIT_REVSPEC_SINGLE, GIT_SORT_NONE, GIT_SORT_REVERSE, GIT_SORT_TIME, GIT_SORT_TOPOLOGICAL, GIT_STASH_APPLY_DEFAULT, GIT_STASH_APPLY_REINSTATE_INDEX, GIT_STASH_DEFAULT, GIT_STASH_INCLUDE_IGNORED, GIT_STASH_INCLUDE_UNTRACKED, GIT_STASH_KEEP_ALL, GIT_STASH_KEEP_INDEX, GIT_STATUS_CONFLICTED, GIT_STATUS_CURRENT, GIT_STATUS_IGNORED, GIT_STATUS_INDEX_DELETED, GIT_STATUS_INDEX_MODIFIED, GIT_STATUS_INDEX_NEW, GIT_STATUS_INDEX_RENAMED, GIT_STATUS_INDEX_TYPECHANGE, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_RENAMED, GIT_STATUS_WT_TYPECHANGE, GIT_STATUS_WT_UNREADABLE, GIT_SUBMODULE_IGNORE_ALL, GIT_SUBMODULE_IGNORE_DIRTY, GIT_SUBMODULE_IGNORE_NONE, GIT_SUBMODULE_IGNORE_UNSPECIFIED, GIT_SUBMODULE_IGNORE_UNTRACKED, GIT_SUBMODULE_STATUS_IN_CONFIG, GIT_SUBMODULE_STATUS_IN_HEAD, GIT_SUBMODULE_STATUS_IN_INDEX, GIT_SUBMODULE_STATUS_IN_WD, GIT_SUBMODULE_STATUS_INDEX_ADDED, GIT_SUBMODULE_STATUS_INDEX_DELETED, GIT_SUBMODULE_STATUS_INDEX_MODIFIED, GIT_SUBMODULE_STATUS_WD_ADDED, GIT_SUBMODULE_STATUS_WD_DELETED, GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED, GIT_SUBMODULE_STATUS_WD_MODIFIED, GIT_SUBMODULE_STATUS_WD_UNINITIALIZED, GIT_SUBMODULE_STATUS_WD_UNTRACKED, GIT_SUBMODULE_STATUS_WD_WD_MODIFIED, LIBGIT2_VER_MAJOR, LIBGIT2_VER_MINOR, LIBGIT2_VER_REVISION, LIBGIT2_VERSION, AlreadyExistsError, Blob, Branch, Commit, Diff, DiffDelta, DiffFile, DiffHunk, DiffLine, DiffStats, FilterSource, GitError, InvalidSpecError, Mailmap, Note, Object, Odb, OdbBackend, OdbBackendLoose, OdbBackendPack, Oid, Patch, Refdb, RefdbBackend, RefdbFsBackend, Reference, RefLogEntry, RevSpec, Signature, Stash, Tag, Tree, TreeBuilder, Walker, Worktree, _cache_enums, discover_repository, filter_register, hash, hashfile, init_file_backend, reference_is_valid_name, tree_entry_cmp, ) from .blame import Blame, BlameHunk from .blob import BlobIO from .callbacks import ( CheckoutCallbacks, Payload, RemoteCallbacks, StashApplyCallbacks, get_credentials, git_clone_options, git_fetch_options, git_proxy_options, ) from .config import Config from .credentials import * from .errors import Passthrough, check_error from .ffi import C, ffi from .filter import Filter from .index import Index, IndexEntry from .legacyenums import * from .options import ( GIT_OPT_ADD_SSL_X509_CERT, GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, GIT_OPT_ENABLE_CACHING, GIT_OPT_ENABLE_FSYNC_GITDIR, GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, GIT_OPT_ENABLE_OFS_DELTA, GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, GIT_OPT_GET_CACHED_MEMORY, GIT_OPT_GET_EXTENSIONS, GIT_OPT_GET_HOMEDIR, GIT_OPT_GET_MWINDOW_FILE_LIMIT, GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_GET_OWNER_VALIDATION, GIT_OPT_GET_PACK_MAX_OBJECTS, GIT_OPT_GET_SEARCH_PATH, GIT_OPT_GET_SERVER_CONNECT_TIMEOUT, GIT_OPT_GET_SERVER_TIMEOUT, GIT_OPT_GET_TEMPLATE_PATH, GIT_OPT_GET_USER_AGENT, GIT_OPT_GET_USER_AGENT_PRODUCT, GIT_OPT_GET_WINDOWS_SHAREMODE, GIT_OPT_SET_ALLOCATOR, GIT_OPT_SET_CACHE_MAX_SIZE, GIT_OPT_SET_CACHE_OBJECT_LIMIT, GIT_OPT_SET_EXTENSIONS, GIT_OPT_SET_HOMEDIR, GIT_OPT_SET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, GIT_OPT_SET_MWINDOW_SIZE, GIT_OPT_SET_ODB_LOOSE_PRIORITY, GIT_OPT_SET_ODB_PACKED_PRIORITY, GIT_OPT_SET_OWNER_VALIDATION, GIT_OPT_SET_PACK_MAX_OBJECTS, GIT_OPT_SET_SEARCH_PATH, GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, GIT_OPT_SET_SERVER_TIMEOUT, GIT_OPT_SET_SSL_CERT_LOCATIONS, GIT_OPT_SET_SSL_CIPHERS, GIT_OPT_SET_TEMPLATE_PATH, GIT_OPT_SET_USER_AGENT, GIT_OPT_SET_USER_AGENT_PRODUCT, GIT_OPT_SET_WINDOWS_SHAREMODE, option, ) from .packbuilder import PackBuilder from .remotes import Remote from .repository import Repository from .settings import Settings from .submodules import Submodule from .transaction import ReferenceTransaction from .utils import to_bytes, to_str # Features features = enums.Feature(C.git_libgit2_features()) # libgit version tuple LIBGIT2_VER = (LIBGIT2_VER_MAJOR, LIBGIT2_VER_MINOR, LIBGIT2_VER_REVISION) # Let _pygit2 cache references to Python enum types. # This is separate from PyInit__pygit2() to avoid a circular import. _cache_enums() def init_repository( path: str | bytes | os.PathLike[str] | os.PathLike[bytes] | None, bare: bool = False, flags: enums.RepositoryInitFlag = enums.RepositoryInitFlag.MKPATH, mode: int | enums.RepositoryInitMode = enums.RepositoryInitMode.SHARED_UMASK, workdir_path: typing.Optional[str] = None, description: typing.Optional[str] = None, template_path: typing.Optional[str] = None, initial_head: typing.Optional[str] = None, origin_url: typing.Optional[str] = None, ) -> Repository: """ Creates a new Git repository in the given *path*. If *bare* is True the repository will be bare, i.e. it will not have a working copy. The *flags* may be a combination of enums.RepositoryInitFlag constants: - BARE (overridden by the *bare* parameter) - NO_REINIT - NO_DOTGIT_DIR - MKDIR - MKPATH (set by default) - EXTERNAL_TEMPLATE The *mode* parameter may be any of the predefined modes in enums.RepositoryInitMode (SHARED_UMASK being the default), or a custom int. The *workdir_path*, *description*, *template_path*, *initial_head* and *origin_url* are all strings. If a repository already exists at *path*, it may be opened successfully but you must not rely on that behavior and should use the Repository constructor directly instead. See libgit2's documentation on git_repository_init_ext for further details. """ # Pre-process input parameters if path is None: raise TypeError('Expected string type for path, found None.') if bare: flags |= enums.RepositoryInitFlag.BARE # Options options = ffi.new('git_repository_init_options *') C.git_repository_init_options_init(options, C.GIT_REPOSITORY_INIT_OPTIONS_VERSION) options.flags = int(flags) options.mode = mode if workdir_path: workdir_path_ref = ffi.new('char []', to_bytes(workdir_path)) options.workdir_path = workdir_path_ref if description: description_ref = ffi.new('char []', to_bytes(description)) options.description = description_ref if template_path: template_path_ref = ffi.new('char []', to_bytes(template_path)) options.template_path = template_path_ref if initial_head: initial_head_ref = ffi.new('char []', to_bytes(initial_head)) options.initial_head = initial_head_ref if origin_url: origin_url_ref = ffi.new('char []', to_bytes(origin_url)) options.origin_url = origin_url_ref # Call crepository = ffi.new('git_repository **') err = C.git_repository_init_ext(crepository, to_bytes(path), options) check_error(err) # Ok return Repository(to_str(path)) def clone_repository( url: str | bytes | os.PathLike[str] | os.PathLike[bytes], path: str | bytes | os.PathLike[str] | os.PathLike[bytes], bare: bool = False, repository: typing.Callable | None = None, remote: typing.Callable | None = None, checkout_branch: str | bytes | None = None, callbacks: RemoteCallbacks | None = None, depth: int = 0, proxy: None | bool | str = None, ) -> Repository: """ Clones a new Git repository from *url* in the given *path*. Returns: a Repository class pointing to the newly cloned repository. Parameters: url : str or bytes or pathlike object URL of the repository to clone. path : str or bytes or pathlike object Local path to clone into. bare : bool Whether the local repository should be bare. remote : callable Callback for the remote to use. The remote callback has `(Repository, name, url) -> Remote` as a signature. The Remote it returns will be used instead of the default one. repository : callable Callback for the repository to use. The repository callback has `(path, bare) -> Repository` as a signature. The Repository it returns will be used instead of creating a new one. checkout_branch : str or bytes Branch to checkout after the clone. The default is to use the remote's default branch. callbacks : RemoteCallbacks Object which implements the callbacks as methods. The callbacks should be an object which inherits from `pyclass:RemoteCallbacks`. depth : int Number of commits to clone. If greater than 0, creates a shallow clone with a history truncated to the specified number of commits. The default is 0 (full commit history). proxy : None or True or str Proxy configuration. Can be one of: * `None` (the default) to disable proxy usage * `True` to enable automatic proxy detection * an url to a proxy (`http://proxy.example.org:3128/`) """ if callbacks is None: callbacks = RemoteCallbacks() # Add repository and remote to the payload payload = callbacks payload.repository = repository payload.remote = remote with git_clone_options(payload): opts = payload.clone_options opts.bare = bare opts.fetch_opts.depth = depth if checkout_branch: checkout_branch_ref = ffi.new('char []', to_bytes(checkout_branch)) opts.checkout_branch = checkout_branch_ref with git_fetch_options(payload, opts=opts.fetch_opts): with git_proxy_options(payload, opts.fetch_opts.proxy_opts, proxy): crepo = ffi.new('git_repository **') err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts) payload.check_error(err) # Ok return Repository._from_c(crepo[0], owned=True) def filter_unregister(name: str) -> None: """ Unregister the given filter. Note that the filter registry is not thread safe. Any registering or deregistering of filters should be done outside of any possible usage of the filters. In particular, any FilterLists that use the filter must have been garbage collected before you can unregister the filter. """ from .filter import FilterList if FilterList._is_filter_in_use(name): raise RuntimeError(f"filter still in use: '{name}'") c_name = to_bytes(name) err = C.git_filter_unregister(c_name) check_error(err) tree_entry_key = functools.cmp_to_key(tree_entry_cmp) settings = Settings() __all__ = ( # Standard Library 'functools', 'os', 'typing', # Standard Library symbols 'TYPE_CHECKING', 'annotations', # Low level API 'GIT_OID_HEX_ZERO', 'GIT_OID_HEXSZ', 'GIT_OID_MINPREFIXLEN', 'GIT_OID_RAWSZ', 'LIBGIT2_VER_MAJOR', 'LIBGIT2_VER_MINOR', 'LIBGIT2_VER_REVISION', 'LIBGIT2_VERSION', 'Object', 'Reference', 'AlreadyExistsError', 'Blob', 'Branch', 'Commit', 'Diff', 'DiffDelta', 'DiffFile', 'DiffHunk', 'DiffLine', 'DiffStats', 'GitError', 'InvalidSpecError', 'Mailmap', 'Note', 'Odb', 'OdbBackend', 'OdbBackendLoose', 'OdbBackendPack', 'Oid', 'Patch', 'RefLogEntry', 'Refdb', 'RefdbBackend', 'RefdbFsBackend', 'RevSpec', 'Signature', 'Stash', 'Tag', 'Tree', 'TreeBuilder', 'Walker', 'Worktree', 'discover_repository', 'hash', 'hashfile', 'init_file_backend', 'option', 'reference_is_valid_name', 'tree_entry_cmp', # Low Level API (not present in .pyi) 'FilterSource', 'filter_register', 'GIT_APPLY_LOCATION_BOTH', 'GIT_APPLY_LOCATION_INDEX', 'GIT_APPLY_LOCATION_WORKDIR', 'GIT_BLAME_FIRST_PARENT', 'GIT_BLAME_IGNORE_WHITESPACE', 'GIT_BLAME_NORMAL', 'GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES', 'GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES', 'GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES', 'GIT_BLAME_TRACK_COPIES_SAME_FILE', 'GIT_BLAME_USE_MAILMAP', 'GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT', 'GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD', 'GIT_BLOB_FILTER_CHECK_FOR_BINARY', 'GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES', 'GIT_BRANCH_ALL', 'GIT_BRANCH_LOCAL', 'GIT_BRANCH_REMOTE', 'GIT_CHECKOUT_ALLOW_CONFLICTS', 'GIT_CHECKOUT_CONFLICT_STYLE_DIFF3', 'GIT_CHECKOUT_CONFLICT_STYLE_MERGE', 'GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3', 'GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH', 'GIT_CHECKOUT_DONT_OVERWRITE_IGNORED', 'GIT_CHECKOUT_DONT_REMOVE_EXISTING', 'GIT_CHECKOUT_DONT_UPDATE_INDEX', 'GIT_CHECKOUT_DONT_WRITE_INDEX', 'GIT_CHECKOUT_DRY_RUN', 'GIT_CHECKOUT_FORCE', 'GIT_CHECKOUT_NO_REFRESH', 'GIT_CHECKOUT_NONE', 'GIT_CHECKOUT_RECREATE_MISSING', 'GIT_CHECKOUT_REMOVE_IGNORED', 'GIT_CHECKOUT_REMOVE_UNTRACKED', 'GIT_CHECKOUT_SAFE', 'GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES', 'GIT_CHECKOUT_SKIP_UNMERGED', 'GIT_CHECKOUT_UPDATE_ONLY', 'GIT_CHECKOUT_USE_OURS', 'GIT_CHECKOUT_USE_THEIRS', 'GIT_CONFIG_HIGHEST_LEVEL', 'GIT_CONFIG_LEVEL_APP', 'GIT_CONFIG_LEVEL_GLOBAL', 'GIT_CONFIG_LEVEL_LOCAL', 'GIT_CONFIG_LEVEL_PROGRAMDATA', 'GIT_CONFIG_LEVEL_SYSTEM', 'GIT_CONFIG_LEVEL_WORKTREE', 'GIT_CONFIG_LEVEL_XDG', 'GIT_DELTA_ADDED', 'GIT_DELTA_CONFLICTED', 'GIT_DELTA_COPIED', 'GIT_DELTA_DELETED', 'GIT_DELTA_IGNORED', 'GIT_DELTA_MODIFIED', 'GIT_DELTA_RENAMED', 'GIT_DELTA_TYPECHANGE', 'GIT_DELTA_UNMODIFIED', 'GIT_DELTA_UNREADABLE', 'GIT_DELTA_UNTRACKED', 'GIT_DESCRIBE_ALL', 'GIT_DESCRIBE_DEFAULT', 'GIT_DESCRIBE_TAGS', 'GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY', 'GIT_DIFF_BREAK_REWRITES', 'GIT_DIFF_DISABLE_PATHSPEC_MATCH', 'GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS', 'GIT_DIFF_FIND_ALL', 'GIT_DIFF_FIND_AND_BREAK_REWRITES', 'GIT_DIFF_FIND_BY_CONFIG', 'GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED', 'GIT_DIFF_FIND_COPIES', 'GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE', 'GIT_DIFF_FIND_EXACT_MATCH_ONLY', 'GIT_DIFF_FIND_FOR_UNTRACKED', 'GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE', 'GIT_DIFF_FIND_IGNORE_WHITESPACE', 'GIT_DIFF_FIND_REMOVE_UNMODIFIED', 'GIT_DIFF_FIND_RENAMES_FROM_REWRITES', 'GIT_DIFF_FIND_RENAMES', 'GIT_DIFF_FIND_REWRITES', 'GIT_DIFF_FLAG_BINARY', 'GIT_DIFF_FLAG_EXISTS', 'GIT_DIFF_FLAG_NOT_BINARY', 'GIT_DIFF_FLAG_VALID_ID', 'GIT_DIFF_FLAG_VALID_SIZE', 'GIT_DIFF_FORCE_BINARY', 'GIT_DIFF_FORCE_TEXT', 'GIT_DIFF_IGNORE_BLANK_LINES', 'GIT_DIFF_IGNORE_CASE', 'GIT_DIFF_IGNORE_FILEMODE', 'GIT_DIFF_IGNORE_SUBMODULES', 'GIT_DIFF_IGNORE_WHITESPACE_CHANGE', 'GIT_DIFF_IGNORE_WHITESPACE_EOL', 'GIT_DIFF_IGNORE_WHITESPACE', 'GIT_DIFF_INCLUDE_CASECHANGE', 'GIT_DIFF_INCLUDE_IGNORED', 'GIT_DIFF_INCLUDE_TYPECHANGE_TREES', 'GIT_DIFF_INCLUDE_TYPECHANGE', 'GIT_DIFF_INCLUDE_UNMODIFIED', 'GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED', 'GIT_DIFF_INCLUDE_UNREADABLE', 'GIT_DIFF_INCLUDE_UNTRACKED', 'GIT_DIFF_INDENT_HEURISTIC', 'GIT_DIFF_MINIMAL', 'GIT_DIFF_NORMAL', 'GIT_DIFF_PATIENCE', 'GIT_DIFF_RECURSE_IGNORED_DIRS', 'GIT_DIFF_RECURSE_UNTRACKED_DIRS', 'GIT_DIFF_REVERSE', 'GIT_DIFF_SHOW_BINARY', 'GIT_DIFF_SHOW_UNMODIFIED', 'GIT_DIFF_SHOW_UNTRACKED_CONTENT', 'GIT_DIFF_SKIP_BINARY_CHECK', 'GIT_DIFF_STATS_FULL', 'GIT_DIFF_STATS_INCLUDE_SUMMARY', 'GIT_DIFF_STATS_NONE', 'GIT_DIFF_STATS_NUMBER', 'GIT_DIFF_STATS_SHORT', 'GIT_DIFF_UPDATE_INDEX', 'GIT_FILEMODE_BLOB_EXECUTABLE', 'GIT_FILEMODE_BLOB', 'GIT_FILEMODE_COMMIT', 'GIT_FILEMODE_LINK', 'GIT_FILEMODE_TREE', 'GIT_FILEMODE_UNREADABLE', 'GIT_FILTER_ALLOW_UNSAFE', 'GIT_FILTER_ATTRIBUTES_FROM_COMMIT', 'GIT_FILTER_ATTRIBUTES_FROM_HEAD', 'GIT_FILTER_CLEAN', 'GIT_FILTER_DEFAULT', 'GIT_FILTER_DRIVER_PRIORITY', 'GIT_FILTER_NO_SYSTEM_ATTRIBUTES', 'GIT_FILTER_SMUDGE', 'GIT_FILTER_TO_ODB', 'GIT_FILTER_TO_WORKTREE', 'GIT_MERGE_ANALYSIS_FASTFORWARD', 'GIT_MERGE_ANALYSIS_NONE', 'GIT_MERGE_ANALYSIS_NORMAL', 'GIT_MERGE_ANALYSIS_UNBORN', 'GIT_MERGE_ANALYSIS_UP_TO_DATE', 'GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY', 'GIT_MERGE_PREFERENCE_NO_FASTFORWARD', 'GIT_MERGE_PREFERENCE_NONE', 'GIT_OBJECT_ANY', 'GIT_OBJECT_BLOB', 'GIT_OBJECT_COMMIT', 'GIT_OBJECT_INVALID', 'GIT_OBJECT_OFS_DELTA', 'GIT_OBJECT_REF_DELTA', 'GIT_OBJECT_TAG', 'GIT_OBJECT_TREE', 'GIT_OPT_ADD_SSL_X509_CERT', 'GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS', 'GIT_OPT_ENABLE_CACHING', 'GIT_OPT_ENABLE_FSYNC_GITDIR', 'GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE', 'GIT_OPT_ENABLE_OFS_DELTA', 'GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION', 'GIT_OPT_ENABLE_STRICT_OBJECT_CREATION', 'GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION', 'GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY', 'GIT_OPT_GET_CACHED_MEMORY', 'GIT_OPT_GET_EXTENSIONS', 'GIT_OPT_GET_HOMEDIR', 'GIT_OPT_GET_MWINDOW_FILE_LIMIT', 'GIT_OPT_GET_MWINDOW_MAPPED_LIMIT', 'GIT_OPT_GET_MWINDOW_SIZE', 'GIT_OPT_GET_OWNER_VALIDATION', 'GIT_OPT_GET_PACK_MAX_OBJECTS', 'GIT_OPT_GET_SEARCH_PATH', 'GIT_OPT_GET_SERVER_CONNECT_TIMEOUT', 'GIT_OPT_GET_SERVER_TIMEOUT', 'GIT_OPT_GET_TEMPLATE_PATH', 'GIT_OPT_GET_USER_AGENT', 'GIT_OPT_GET_USER_AGENT_PRODUCT', 'GIT_OPT_GET_WINDOWS_SHAREMODE', 'GIT_OPT_SET_ALLOCATOR', 'GIT_OPT_SET_CACHE_MAX_SIZE', 'GIT_OPT_SET_CACHE_OBJECT_LIMIT', 'GIT_OPT_SET_EXTENSIONS', 'GIT_OPT_SET_HOMEDIR', 'GIT_OPT_SET_MWINDOW_FILE_LIMIT', 'GIT_OPT_SET_MWINDOW_MAPPED_LIMIT', 'GIT_OPT_SET_MWINDOW_SIZE', 'GIT_OPT_SET_ODB_LOOSE_PRIORITY', 'GIT_OPT_SET_ODB_PACKED_PRIORITY', 'GIT_OPT_SET_OWNER_VALIDATION', 'GIT_OPT_SET_PACK_MAX_OBJECTS', 'GIT_OPT_SET_SEARCH_PATH', 'GIT_OPT_SET_SERVER_CONNECT_TIMEOUT', 'GIT_OPT_SET_SERVER_TIMEOUT', 'GIT_OPT_SET_SSL_CERT_LOCATIONS', 'GIT_OPT_SET_SSL_CIPHERS', 'GIT_OPT_SET_TEMPLATE_PATH', 'GIT_OPT_SET_USER_AGENT', 'GIT_OPT_SET_USER_AGENT_PRODUCT', 'GIT_OPT_SET_WINDOWS_SHAREMODE', 'GIT_REFERENCES_ALL', 'GIT_REFERENCES_BRANCHES', 'GIT_REFERENCES_TAGS', 'GIT_RESET_HARD', 'GIT_RESET_MIXED', 'GIT_RESET_SOFT', 'GIT_REVSPEC_MERGE_BASE', 'GIT_REVSPEC_RANGE', 'GIT_REVSPEC_SINGLE', 'GIT_SORT_NONE', 'GIT_SORT_REVERSE', 'GIT_SORT_TIME', 'GIT_SORT_TOPOLOGICAL', 'GIT_STASH_APPLY_DEFAULT', 'GIT_STASH_APPLY_REINSTATE_INDEX', 'GIT_STASH_DEFAULT', 'GIT_STASH_INCLUDE_IGNORED', 'GIT_STASH_INCLUDE_UNTRACKED', 'GIT_STASH_KEEP_ALL', 'GIT_STASH_KEEP_INDEX', 'GIT_STATUS_CONFLICTED', 'GIT_STATUS_CURRENT', 'GIT_STATUS_IGNORED', 'GIT_STATUS_INDEX_DELETED', 'GIT_STATUS_INDEX_MODIFIED', 'GIT_STATUS_INDEX_NEW', 'GIT_STATUS_INDEX_RENAMED', 'GIT_STATUS_INDEX_TYPECHANGE', 'GIT_STATUS_WT_DELETED', 'GIT_STATUS_WT_MODIFIED', 'GIT_STATUS_WT_NEW', 'GIT_STATUS_WT_RENAMED', 'GIT_STATUS_WT_TYPECHANGE', 'GIT_STATUS_WT_UNREADABLE', 'GIT_SUBMODULE_IGNORE_ALL', 'GIT_SUBMODULE_IGNORE_DIRTY', 'GIT_SUBMODULE_IGNORE_NONE', 'GIT_SUBMODULE_IGNORE_UNSPECIFIED', 'GIT_SUBMODULE_IGNORE_UNTRACKED', 'GIT_SUBMODULE_STATUS_IN_CONFIG', 'GIT_SUBMODULE_STATUS_IN_HEAD', 'GIT_SUBMODULE_STATUS_IN_INDEX', 'GIT_SUBMODULE_STATUS_IN_WD', 'GIT_SUBMODULE_STATUS_INDEX_ADDED', 'GIT_SUBMODULE_STATUS_INDEX_DELETED', 'GIT_SUBMODULE_STATUS_INDEX_MODIFIED', 'GIT_SUBMODULE_STATUS_WD_ADDED', 'GIT_SUBMODULE_STATUS_WD_DELETED', 'GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED', 'GIT_SUBMODULE_STATUS_WD_MODIFIED', 'GIT_SUBMODULE_STATUS_WD_UNINITIALIZED', 'GIT_SUBMODULE_STATUS_WD_UNTRACKED', 'GIT_SUBMODULE_STATUS_WD_WD_MODIFIED', # High level API. 'enums', 'blame', 'Blame', 'BlameHunk', 'blob', 'BlobIO', 'callbacks', 'Payload', 'RemoteCallbacks', 'CheckoutCallbacks', 'StashApplyCallbacks', 'git_clone_options', 'git_fetch_options', 'git_proxy_options', 'get_credentials', 'config', 'Config', 'credentials', 'CredentialType', 'Username', 'UserPass', 'Keypair', 'KeypairFromAgent', 'KeypairFromMemory', 'errors', 'check_error', 'Passthrough', 'ffi', 'C', 'filter', 'Filter', 'index', 'Index', 'IndexEntry', 'legacyenums', 'GIT_FEATURE_THREADS', 'GIT_FEATURE_HTTPS', 'GIT_FEATURE_SSH', 'GIT_FEATURE_NSEC', 'GIT_REPOSITORY_INIT_BARE', 'GIT_REPOSITORY_INIT_NO_REINIT', 'GIT_REPOSITORY_INIT_NO_DOTGIT_DIR', 'GIT_REPOSITORY_INIT_MKDIR', 'GIT_REPOSITORY_INIT_MKPATH', 'GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE', 'GIT_REPOSITORY_INIT_RELATIVE_GITLINK', 'GIT_REPOSITORY_INIT_SHARED_UMASK', 'GIT_REPOSITORY_INIT_SHARED_GROUP', 'GIT_REPOSITORY_INIT_SHARED_ALL', 'GIT_REPOSITORY_OPEN_NO_SEARCH', 'GIT_REPOSITORY_OPEN_CROSS_FS', 'GIT_REPOSITORY_OPEN_BARE', 'GIT_REPOSITORY_OPEN_NO_DOTGIT', 'GIT_REPOSITORY_OPEN_FROM_ENV', 'GIT_REPOSITORY_STATE_NONE', 'GIT_REPOSITORY_STATE_MERGE', 'GIT_REPOSITORY_STATE_REVERT', 'GIT_REPOSITORY_STATE_REVERT_SEQUENCE', 'GIT_REPOSITORY_STATE_CHERRYPICK', 'GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE', 'GIT_REPOSITORY_STATE_BISECT', 'GIT_REPOSITORY_STATE_REBASE', 'GIT_REPOSITORY_STATE_REBASE_INTERACTIVE', 'GIT_REPOSITORY_STATE_REBASE_MERGE', 'GIT_REPOSITORY_STATE_APPLY_MAILBOX', 'GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE', 'GIT_ATTR_CHECK_FILE_THEN_INDEX', 'GIT_ATTR_CHECK_INDEX_THEN_FILE', 'GIT_ATTR_CHECK_INDEX_ONLY', 'GIT_ATTR_CHECK_NO_SYSTEM', 'GIT_ATTR_CHECK_INCLUDE_HEAD', 'GIT_ATTR_CHECK_INCLUDE_COMMIT', 'GIT_FETCH_PRUNE_UNSPECIFIED', 'GIT_FETCH_PRUNE', 'GIT_FETCH_NO_PRUNE', 'GIT_CHECKOUT_NOTIFY_NONE', 'GIT_CHECKOUT_NOTIFY_CONFLICT', 'GIT_CHECKOUT_NOTIFY_DIRTY', 'GIT_CHECKOUT_NOTIFY_UPDATED', 'GIT_CHECKOUT_NOTIFY_UNTRACKED', 'GIT_CHECKOUT_NOTIFY_IGNORED', 'GIT_CHECKOUT_NOTIFY_ALL', 'GIT_STASH_APPLY_PROGRESS_NONE', 'GIT_STASH_APPLY_PROGRESS_LOADING_STASH', 'GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX', 'GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED', 'GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED', 'GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED', 'GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED', 'GIT_STASH_APPLY_PROGRESS_DONE', 'GIT_CREDENTIAL_USERPASS_PLAINTEXT', 'GIT_CREDENTIAL_SSH_KEY', 'GIT_CREDENTIAL_SSH_CUSTOM', 'GIT_CREDENTIAL_DEFAULT', 'GIT_CREDENTIAL_SSH_INTERACTIVE', 'GIT_CREDENTIAL_USERNAME', 'GIT_CREDENTIAL_SSH_MEMORY', 'packbuilder', 'PackBuilder', 'refspec', 'remotes', 'Remote', 'repository', 'Repository', 'branches', 'references', 'settings', 'Settings', 'submodules', 'Submodule', 'transaction', 'ReferenceTransaction', 'utils', 'to_bytes', 'to_str', # __init__ module defined symbols 'features', 'LIBGIT2_VER', 'init_repository', 'clone_repository', 'tree_entry_key', ) libgit2-pygit2-d86662e/pygit2/_build.py000066400000000000000000000045031521317216500176630ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """ This is an special module, it provides stuff used by setup.py at build time. But also used by pygit2 at run time. """ import os from pathlib import Path # # The version number of pygit2 # __version__ = '1.19.3' # # Utility functions to get the paths required for building extensions # def _get_libgit2_path() -> Path: # LIBGIT2 environment variable takes precedence libgit2_path = os.getenv('LIBGIT2') if libgit2_path is not None: return Path(libgit2_path) # Default if os.name == 'nt': return Path(r'%s\libgit2' % os.getenv('ProgramFiles')) return Path('/usr/local') def get_libgit2_paths() -> tuple[Path, dict[str, list[str]]]: # Base path path = _get_libgit2_path() # Library dirs libgit2_lib = os.getenv('LIBGIT2_LIB') if libgit2_lib is None: library_dirs = [path / 'lib', path / 'lib64'] else: library_dirs = [Path(libgit2_lib)] include_dirs = [path / 'include'] return ( path / 'bin', { 'libraries': ['git2'], 'include_dirs': [str(x) for x in include_dirs], 'library_dirs': [str(x) for x in library_dirs], }, ) libgit2-pygit2-d86662e/pygit2/_libgit2/000077500000000000000000000000001521317216500175445ustar00rootroot00000000000000libgit2-pygit2-d86662e/pygit2/_libgit2/ffi.pyi000066400000000000000000000243731521317216500210440ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from typing import Any, Generic, Literal, NewType, SupportsIndex, TypeVar, overload T = TypeVar('T') NULL_TYPE = NewType('NULL_TYPE', object) NULL: NULL_TYPE = ... char = NewType('char', object) char_pointer = NewType('char_pointer', object) class size_t: def __getitem__(self, item: Literal[0]) -> int: ... class int_c: def __getitem__(self, item: Literal[0]) -> int: ... class int64_t: def __getitem__(self, item: Literal[0]) -> int: ... class ssize_t: def __getitem__(self, item: Literal[0]) -> int: ... class _Pointer(Generic[T]): def __setitem__(self, item: Literal[0], a: T) -> None: ... @overload def __getitem__(self, item: Literal[0]) -> T: ... @overload def __getitem__(self, item: slice[None, None, None]) -> bytes: ... class _MultiPointer(Generic[T]): def __getitem__(self, item: int) -> T: ... class ArrayC(Generic[T]): # incomplete! # def _len(self, ?) -> ?: ... def __getitem__(self, index: int) -> T: ... def __setitem__(self, index: int, value: T) -> None: ... class GitTimeC: # incomplete time: int offset: int class GitSignatureC: name: char_pointer email: char_pointer when: GitTimeC class GitHunkC: # incomplete boundary: char final_start_line_number: int final_signature: GitSignatureC orig_signature: GitSignatureC orig_start_line_number: int orig_path: char_pointer lines_in_hunk: int class GitRepositoryC: # incomplete # TODO: this has to be unified with pygit2._pygit2(pyi).Repository # def _from_c(cls, ptr: 'GitRepositoryC', owned: bool) -> 'Repository': ... pass class GitFetchOptionsC: # TODO: FetchOptions exist in _pygit2.pyi # incomplete depth: int class GitSubmoduleC: pass class GitSubmoduleUpdateOptionsC: fetch_opts: GitFetchOptionsC class GitRemoteHeadC: local: int oid: GitOidC loid: GitOidC name: char_pointer symref_target: char_pointer class UnsignedIntC: def __getitem__(self, item: Literal[0]) -> int: ... class GitOidC: id: _Pointer[bytes] class GitBlameOptionsC: flags: int min_match_characters: int newest_commit: object oldest_commit: object min_line: int max_line: int class GitBlameC: # incomplete pass class GitBlobC: # incomplete pass class GitMergeOptionsC: file_favor: int flags: int file_flags: int class GitAnnotatedCommitC: pass class GitAttrOptionsC: # incomplete version: int flags: int class GitBufC: ptr: char_pointer class GitCheckoutOptionsC: # incomplete checkout_strategy: int class GitCommitC: pass class GitConfigC: # incomplete pass class GitConfigIteratorC: # incomplete pass class GitConfigEntryC: # incomplete name: char_pointer value: char_pointer level: int class GitDescribeFormatOptionsC: version: int abbreviated_size: int always_use_long_format: int dirty_suffix: ArrayC[char] class GitDescribeOptionsC: version: int max_candidates_tags: int describe_strategy: int pattern: ArrayC[char] only_follow_first_parent: int show_commit_oid_as_fallback: int class GitDescribeResultC: pass class GitFilterListC: # opaque struct pass class GitIndexC: pass class GitIndexEntryC: # incomplete? mode: int path: ArrayC[char] class GitMergeFileResultC: pass class GitObjectC: pass class GitStashSaveOptionsC: version: int flags: int stasher: GitSignatureC message: ArrayC[char] paths: GitStrrayC class GitStrrayC: # incomplete? strings: NULL_TYPE | ArrayC[char_pointer] count: int class GitTreeC: pass class GitRepositoryInitOptionsC: version: int flags: int mode: int workdir_path: ArrayC[char] description: ArrayC[char] template_path: ArrayC[char] initial_head: ArrayC[char] origin_url: ArrayC[char] class GitCloneOptionsC: pass class GitPackbuilderC: pass class GitProxyTC: pass class GitProxyOptionsC: version: int type: GitProxyTC url: char_pointer # credentials # certificate_check # payload class GitRemoteC: pass class GitReferenceC: pass class GitTransactionC: pass def string(a: char_pointer) -> bytes: ... @overload def new(a: Literal['git_repository **']) -> _Pointer[GitRepositoryC]: ... @overload def new(a: Literal['git_remote **']) -> _Pointer[GitRemoteC]: ... @overload def new(a: Literal['git_transaction **']) -> _Pointer[GitTransactionC]: ... @overload def new(a: Literal['git_repository_init_options *']) -> GitRepositoryInitOptionsC: ... @overload def new(a: Literal['git_submodule_update_options *']) -> GitSubmoduleUpdateOptionsC: ... @overload def new(a: Literal['git_submodule **']) -> _Pointer[GitSubmoduleC]: ... @overload def new(a: Literal['unsigned int *']) -> UnsignedIntC: ... @overload def new(a: Literal['git_proxy_options *']) -> GitProxyOptionsC: ... @overload def new(a: Literal['git_oid *']) -> GitOidC: ... @overload def new(a: Literal['git_blame **']) -> _Pointer[GitBlameC]: ... @overload def new(a: Literal['git_blob **']) -> _Pointer[GitBlobC]: ... @overload def new(a: Literal['git_clone_options *']) -> GitCloneOptionsC: ... @overload def new(a: Literal['git_merge_options *']) -> GitMergeOptionsC: ... @overload def new(a: Literal['git_blame_options *']) -> GitBlameOptionsC: ... @overload def new(a: Literal['git_annotated_commit **']) -> _Pointer[GitAnnotatedCommitC]: ... @overload def new(a: Literal['git_attr_options *']) -> GitAttrOptionsC: ... @overload def new(a: Literal['git_buf *']) -> GitBufC: ... @overload def new(a: Literal['char *'], b: bytes) -> char_pointer: ... @overload def new(a: Literal['char *[]'], b: list[char_pointer]) -> ArrayC[char_pointer]: ... @overload def new(a: Literal['git_checkout_options *']) -> GitCheckoutOptionsC: ... @overload def new(a: Literal['git_commit **']) -> _Pointer[GitCommitC]: ... @overload def new(a: Literal['git_config *']) -> GitConfigC: ... @overload def new(a: Literal['git_config **']) -> _Pointer[GitConfigC]: ... @overload def new(a: Literal['git_config_iterator **']) -> _Pointer[GitConfigIteratorC]: ... @overload def new(a: Literal['git_config_entry **']) -> _Pointer[GitConfigEntryC]: ... @overload def new(a: Literal['git_describe_format_options *']) -> GitDescribeFormatOptionsC: ... @overload def new(a: Literal['git_describe_options *']) -> GitDescribeOptionsC: ... @overload def new(a: Literal['git_describe_result *']) -> GitDescribeResultC: ... @overload def new(a: Literal['git_describe_result **']) -> _Pointer[GitDescribeResultC]: ... @overload def new(a: Literal['struct git_reference **']) -> _Pointer[GitReferenceC]: ... @overload def new(a: Literal['git_index **']) -> _Pointer[GitIndexC]: ... @overload def new(a: Literal['git_index_entry *']) -> GitIndexEntryC: ... @overload def new(a: Literal['git_merge_file_result *']) -> GitMergeFileResultC: ... @overload def new(a: Literal['git_object *']) -> GitObjectC: ... @overload def new(a: Literal['git_object **']) -> _Pointer[GitObjectC]: ... @overload def new(a: Literal['git_packbuilder **']) -> _Pointer[GitPackbuilderC]: ... @overload def new(a: Literal['git_signature *']) -> GitSignatureC: ... @overload def new(a: Literal['git_signature **']) -> _Pointer[GitSignatureC]: ... @overload def new(a: Literal['git_filter_list **']) -> _Pointer[GitFilterListC]: ... @overload def new(a: Literal['int *']) -> int_c: ... @overload def new(a: Literal['int64_t *']) -> int64_t: ... @overload def new( a: Literal['git_remote_head ***'], ) -> _Pointer[_MultiPointer[GitRemoteHeadC]]: ... @overload def new(a: Literal['size_t *', 'size_t*']) -> size_t: ... @overload def new(a: Literal['ssize_t *', 'ssize_t*']) -> ssize_t: ... @overload def new(a: Literal['git_stash_save_options *']) -> GitStashSaveOptionsC: ... @overload def new(a: Literal['git_strarray *']) -> GitStrrayC: ... @overload def new(a: Literal['git_tree **']) -> _Pointer[GitTreeC]: ... @overload def new(a: Literal['git_buf *'], b: tuple[NULL_TYPE, Literal[0]]) -> GitBufC: ... @overload def new(a: Literal['char **']) -> _Pointer[char_pointer]: ... @overload def new(a: Literal['void **'], b: bytes) -> _Pointer[bytes]: ... @overload def new(a: Literal['char[]', 'char []'], b: bytes | NULL_TYPE) -> ArrayC[char]: ... @overload def new( a: Literal['char *[]'], b: int ) -> ArrayC[char_pointer]: ... # For ext_array in SET_EXTENSIONS @overload def new( a: Literal['char *[]'], b: list[Any] ) -> ArrayC[char_pointer]: ... # For string arrays def addressof(a: object, attribute: str) -> _Pointer[object]: ... class buffer(bytes): def __init__(self, a: object) -> None: ... def __setitem__(self, item: slice[None, None, None], value: bytes) -> None: ... @overload def __getitem__(self, item: SupportsIndex) -> int: ... @overload def __getitem__(self, item: slice[Any, Any, Any]) -> bytes: ... @overload def cast(a: Literal['int'], b: object) -> int: ... @overload def cast(a: Literal['unsigned int'], b: object) -> int: ... @overload def cast(a: Literal['size_t'], b: object) -> int: ... @overload def cast(a: Literal['ssize_t'], b: object) -> int: ... @overload def cast(a: Literal['char *'], b: object) -> char_pointer: ... libgit2-pygit2-d86662e/pygit2/_pygit2.pyi000066400000000000000000000632741521317216500201650ustar00rootroot00000000000000from collections.abc import Iterator, Sequence from io import DEFAULT_BUFFER_SIZE, IOBase from pathlib import Path from queue import Queue from threading import Event from typing import ( # noqa: UP035 Generic, Literal, Optional, Type, TypedDict, TypeVar, final, overload, ) from typing_extensions import disjoint_base from . import Index from ._libgit2.ffi import ( GitCommitC, GitObjectC, GitSignatureC, _Pointer, ) from .enums import ( ApplyLocation, BlobFilter, BranchType, DeltaStatus, DiffFind, DiffFlag, DiffOption, DiffStatsFormat, FileMode, MergeAnalysis, MergePreference, ObjectType, ReferenceFilter, ReferenceType, ResetMode, SortMode, ) from .filter import Filter LIBGIT2_VER_MAJOR: int LIBGIT2_VER_MINOR: int LIBGIT2_VER_REVISION: int LIBGIT2_VERSION: str GIT_OID_RAWSZ: int GIT_OID_HEXSZ: int GIT_OID_HEX_ZERO: str GIT_OID_MINPREFIXLEN: int GIT_OBJECT_ANY: int GIT_OBJECT_INVALID: int GIT_OBJECT_COMMIT: int GIT_OBJECT_TREE: int GIT_OBJECT_BLOB: int GIT_OBJECT_TAG: int GIT_OBJECT_OFS_DELTA: int GIT_OBJECT_REF_DELTA: int GIT_FILEMODE_UNREADABLE: int GIT_FILEMODE_TREE: int GIT_FILEMODE_BLOB: int GIT_FILEMODE_BLOB_EXECUTABLE: int GIT_FILEMODE_LINK: int GIT_FILEMODE_COMMIT: int GIT_SORT_NONE: int GIT_SORT_TOPOLOGICAL: int GIT_SORT_TIME: int GIT_SORT_REVERSE: int GIT_RESET_SOFT: int GIT_RESET_MIXED: int GIT_RESET_HARD: int GIT_REFERENCES_ALL: int GIT_REFERENCES_BRANCHES: int GIT_REFERENCES_TAGS: int GIT_REVSPEC_SINGLE: int GIT_REVSPEC_RANGE: int GIT_REVSPEC_MERGE_BASE: int GIT_BRANCH_LOCAL: int GIT_BRANCH_REMOTE: int GIT_BRANCH_ALL: int GIT_STATUS_CURRENT: int GIT_STATUS_INDEX_NEW: int GIT_STATUS_INDEX_MODIFIED: int GIT_STATUS_INDEX_DELETED: int GIT_STATUS_INDEX_RENAMED: int GIT_STATUS_INDEX_TYPECHANGE: int GIT_STATUS_WT_NEW: int GIT_STATUS_WT_MODIFIED: int GIT_STATUS_WT_DELETED: int GIT_STATUS_WT_TYPECHANGE: int GIT_STATUS_WT_RENAMED: int GIT_STATUS_WT_UNREADABLE: int GIT_STATUS_IGNORED: int GIT_STATUS_CONFLICTED: int GIT_CHECKOUT_NONE: int GIT_CHECKOUT_SAFE: int GIT_CHECKOUT_FORCE: int GIT_CHECKOUT_RECREATE_MISSING: int GIT_CHECKOUT_ALLOW_CONFLICTS: int GIT_CHECKOUT_REMOVE_UNTRACKED: int GIT_CHECKOUT_REMOVE_IGNORED: int GIT_CHECKOUT_UPDATE_ONLY: int GIT_CHECKOUT_DONT_UPDATE_INDEX: int GIT_CHECKOUT_NO_REFRESH: int GIT_CHECKOUT_SKIP_UNMERGED: int GIT_CHECKOUT_USE_OURS: int GIT_CHECKOUT_USE_THEIRS: int GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH: int GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES: int GIT_CHECKOUT_DONT_OVERWRITE_IGNORED: int GIT_CHECKOUT_CONFLICT_STYLE_MERGE: int GIT_CHECKOUT_CONFLICT_STYLE_DIFF3: int GIT_CHECKOUT_DONT_REMOVE_EXISTING: int GIT_CHECKOUT_DONT_WRITE_INDEX: int GIT_CHECKOUT_DRY_RUN: int GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3: int GIT_DIFF_NORMAL: int GIT_DIFF_REVERSE: int GIT_DIFF_INCLUDE_IGNORED: int GIT_DIFF_RECURSE_IGNORED_DIRS: int GIT_DIFF_INCLUDE_UNTRACKED: int GIT_DIFF_RECURSE_UNTRACKED_DIRS: int GIT_DIFF_INCLUDE_UNMODIFIED: int GIT_DIFF_INCLUDE_TYPECHANGE: int GIT_DIFF_INCLUDE_TYPECHANGE_TREES: int GIT_DIFF_IGNORE_FILEMODE: int GIT_DIFF_IGNORE_SUBMODULES: int GIT_DIFF_IGNORE_CASE: int GIT_DIFF_INCLUDE_CASECHANGE: int GIT_DIFF_DISABLE_PATHSPEC_MATCH: int GIT_DIFF_SKIP_BINARY_CHECK: int GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS: int GIT_DIFF_UPDATE_INDEX: int GIT_DIFF_INCLUDE_UNREADABLE: int GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED: int GIT_DIFF_INDENT_HEURISTIC: int GIT_DIFF_IGNORE_BLANK_LINES: int GIT_DIFF_FORCE_TEXT: int GIT_DIFF_FORCE_BINARY: int GIT_DIFF_IGNORE_WHITESPACE: int GIT_DIFF_IGNORE_WHITESPACE_CHANGE: int GIT_DIFF_IGNORE_WHITESPACE_EOL: int GIT_DIFF_SHOW_UNTRACKED_CONTENT: int GIT_DIFF_SHOW_UNMODIFIED: int GIT_DIFF_PATIENCE: int GIT_DIFF_MINIMAL: int GIT_DIFF_SHOW_BINARY: int GIT_DIFF_STATS_NONE: int GIT_DIFF_STATS_FULL: int GIT_DIFF_STATS_SHORT: int GIT_DIFF_STATS_NUMBER: int GIT_DIFF_STATS_INCLUDE_SUMMARY: int GIT_DIFF_FIND_BY_CONFIG: int GIT_DIFF_FIND_RENAMES: int GIT_DIFF_FIND_RENAMES_FROM_REWRITES: int GIT_DIFF_FIND_COPIES: int GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED: int GIT_DIFF_FIND_REWRITES: int GIT_DIFF_BREAK_REWRITES: int GIT_DIFF_FIND_AND_BREAK_REWRITES: int GIT_DIFF_FIND_FOR_UNTRACKED: int GIT_DIFF_FIND_ALL: int GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE: int GIT_DIFF_FIND_IGNORE_WHITESPACE: int GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE: int GIT_DIFF_FIND_EXACT_MATCH_ONLY: int GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY: int GIT_DIFF_FIND_REMOVE_UNMODIFIED: int GIT_DIFF_FLAG_BINARY: int GIT_DIFF_FLAG_NOT_BINARY: int GIT_DIFF_FLAG_VALID_ID: int GIT_DIFF_FLAG_EXISTS: int GIT_DIFF_FLAG_VALID_SIZE: int GIT_DELTA_UNMODIFIED: int GIT_DELTA_ADDED: int GIT_DELTA_DELETED: int GIT_DELTA_MODIFIED: int GIT_DELTA_RENAMED: int GIT_DELTA_COPIED: int GIT_DELTA_IGNORED: int GIT_DELTA_UNTRACKED: int GIT_DELTA_TYPECHANGE: int GIT_DELTA_UNREADABLE: int GIT_DELTA_CONFLICTED: int GIT_CONFIG_LEVEL_PROGRAMDATA: int GIT_CONFIG_LEVEL_SYSTEM: int GIT_CONFIG_LEVEL_XDG: int GIT_CONFIG_LEVEL_GLOBAL: int GIT_CONFIG_LEVEL_LOCAL: int GIT_CONFIG_LEVEL_WORKTREE: int GIT_CONFIG_LEVEL_APP: int GIT_CONFIG_HIGHEST_LEVEL: int GIT_BLAME_NORMAL: int GIT_BLAME_TRACK_COPIES_SAME_FILE: int GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES: int GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES: int GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES: int GIT_BLAME_FIRST_PARENT: int GIT_BLAME_USE_MAILMAP: int GIT_BLAME_IGNORE_WHITESPACE: int GIT_MERGE_ANALYSIS_NONE: int GIT_MERGE_ANALYSIS_NORMAL: int GIT_MERGE_ANALYSIS_UP_TO_DATE: int GIT_MERGE_ANALYSIS_FASTFORWARD: int GIT_MERGE_ANALYSIS_UNBORN: int GIT_MERGE_PREFERENCE_NONE: int GIT_MERGE_PREFERENCE_NO_FASTFORWARD: int GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY: int GIT_DESCRIBE_DEFAULT: int GIT_DESCRIBE_TAGS: int GIT_DESCRIBE_ALL: int GIT_STASH_DEFAULT: int GIT_STASH_KEEP_INDEX: int GIT_STASH_INCLUDE_UNTRACKED: int GIT_STASH_INCLUDE_IGNORED: int GIT_STASH_KEEP_ALL: int GIT_STASH_APPLY_DEFAULT: int GIT_STASH_APPLY_REINSTATE_INDEX: int GIT_APPLY_LOCATION_WORKDIR: int GIT_APPLY_LOCATION_INDEX: int GIT_APPLY_LOCATION_BOTH: int GIT_SUBMODULE_IGNORE_UNSPECIFIED: int GIT_SUBMODULE_IGNORE_NONE: int GIT_SUBMODULE_IGNORE_UNTRACKED: int GIT_SUBMODULE_IGNORE_DIRTY: int GIT_SUBMODULE_IGNORE_ALL: int GIT_SUBMODULE_STATUS_IN_HEAD: int GIT_SUBMODULE_STATUS_IN_INDEX: int GIT_SUBMODULE_STATUS_IN_CONFIG: int GIT_SUBMODULE_STATUS_IN_WD: int GIT_SUBMODULE_STATUS_INDEX_ADDED: int GIT_SUBMODULE_STATUS_INDEX_DELETED: int GIT_SUBMODULE_STATUS_INDEX_MODIFIED: int GIT_SUBMODULE_STATUS_WD_UNINITIALIZED: int GIT_SUBMODULE_STATUS_WD_ADDED: int GIT_SUBMODULE_STATUS_WD_DELETED: int GIT_SUBMODULE_STATUS_WD_MODIFIED: int GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED: int GIT_SUBMODULE_STATUS_WD_WD_MODIFIED: int GIT_SUBMODULE_STATUS_WD_UNTRACKED: int GIT_BLOB_FILTER_CHECK_FOR_BINARY: int GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES: int GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD: int GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT: int GIT_FILTER_DRIVER_PRIORITY: int GIT_FILTER_TO_WORKTREE: int GIT_FILTER_SMUDGE: int GIT_FILTER_TO_ODB: int GIT_FILTER_CLEAN: int GIT_FILTER_DEFAULT: int GIT_FILTER_ALLOW_UNSAFE: int GIT_FILTER_NO_SYSTEM_ATTRIBUTES: int GIT_FILTER_ATTRIBUTES_FROM_HEAD: int GIT_FILTER_ATTRIBUTES_FROM_COMMIT: int _T = TypeVar('_T') class _ObjectBase(Generic[_T]): _pointer: _Pointer[_T] filemode: FileMode id: Oid name: str | None raw_name: bytes | None short_id: str type: 'Literal[ObjectType.COMMIT] | Literal[ObjectType.TREE] | Literal[ObjectType.TAG] | Literal[ObjectType.BLOB]' type_str: "Literal['commit'] | Literal['tree'] | Literal['tag'] | Literal['blob']" author: Signature committer: Signature tree: Tree @overload def peel( self, target_type: 'Literal[ObjectType.COMMIT] | Type[Commit]', / ) -> 'Commit': ... @overload def peel( self, target_type: 'Literal[ObjectType.TREE] | Type[Tree]', / ) -> 'Tree': ... @overload def peel(self, target_type: 'Literal[ObjectType.TAG] | Type[Tag]', /) -> 'Tag': ... @overload def peel( self, target_type: 'Literal[ObjectType.BLOB] | Type[Blob]', / ) -> 'Blob': ... @overload def peel(self, target_type: 'None', /) -> 'Commit|Tree|Tag|Blob': ... def read_raw(self) -> bytes: ... def __eq__(self, other, /) -> bool: ... def __ge__(self, other, /) -> bool: ... def __gt__(self, other, /) -> bool: ... def __hash__(self) -> int: ... def __le__(self, other, /) -> bool: ... def __lt__(self, other, /) -> bool: ... def __ne__(self, other, /) -> bool: ... @disjoint_base class Object(_ObjectBase[GitObjectC]): pass @final class Reference: name: str raw_name: bytes raw_shorthand: bytes raw_target: Oid | bytes shorthand: str target: Oid | str type: ReferenceType def __init__(self, *args) -> None: ... def delete(self) -> None: ... def log(self) -> Iterator[RefLogEntry]: ... @overload def peel(self, type: 'Literal[ObjectType.COMMIT] | Type[Commit]') -> 'Commit': ... @overload def peel(self, type: 'Literal[ObjectType.TREE] | Type[Tree]') -> 'Tree': ... @overload def peel(self, type: 'Literal[ObjectType.TAG] | Type[Tag]') -> 'Tag': ... @overload def peel(self, type: 'Literal[ObjectType.BLOB] | Type[Blob]') -> 'Blob': ... @overload def peel(self, type: 'None' = None) -> 'Commit|Tree|Tag|Blob': ... def rename(self, new_name: str, /) -> None: ... def resolve(self) -> Reference: ... def set_target(self, target: _OidArg, message: str = ...) -> None: ... def __eq__(self, other, /) -> bool: ... def __ge__(self, other, /) -> bool: ... def __gt__(self, other, /) -> bool: ... def __le__(self, other, /) -> bool: ... def __lt__(self, other, /) -> bool: ... def __ne__(self, other, /) -> bool: ... class AlreadyExistsError(ValueError): ... @final class Blob(Object): data: bytes is_binary: bool size: int def diff( self, blob: Blob = ..., flag: int = ..., old_as_path: str = ..., new_as_path: str = ..., ) -> Patch: ... def diff_to_buffer( self, buffer: Optional[bytes | str] = None, flag: DiffOption = DiffOption.NORMAL, old_as_path: str = ..., buffer_as_path: str = ..., ) -> Patch: ... def _write_to_queue( self, queue: Queue[bytes], ready: Event, done: Event, chunk_size: int = DEFAULT_BUFFER_SIZE, as_path: Optional[str] = None, flags: BlobFilter = BlobFilter.CHECK_FOR_BINARY, commit_id: Optional[Oid] = None, ) -> None: ... def __buffer__(self, flags: int, /) -> memoryview: ... @final class Branch(Reference): # type: ignore[misc] branch_name: str raw_branch_name: bytes remote_name: str upstream: Branch upstream_name: str def delete(self) -> None: ... def is_checked_out(self) -> bool: ... def is_head(self) -> bool: ... def rename(self, name: str, force: bool = False) -> 'Branch': ... # type: ignore[override] @final class Commit(_ObjectBase[GitCommitC]): _pointer: _Pointer[GitCommitC] author: Signature commit_time: int commit_time_offset: int committer: Signature gpg_signature: tuple[bytes, bytes] message: str message_encoding: str message_trailers: dict[str, str] parent_ids: list[Oid] parents: list[Commit] raw_message: bytes tree: Tree tree_id: Oid @disjoint_base class Diff: deltas: Iterator[DiffDelta] patch: str | None patchid: Oid stats: DiffStats text: str def find_similar( self, flags: DiffFind = DiffFind.FIND_BY_CONFIG, rename_threshold: int = 50, copy_threshold: int = 50, rename_from_rewrite_threshold: int = 50, break_rewrite_threshold: int = 60, rename_limit: int = 1000, ) -> None: ... def merge(self, diff: Diff) -> None: ... @staticmethod def from_c(diff, repo) -> Diff: ... @staticmethod def parse_diff(git_diff: str | bytes, /) -> Diff: ... def __getitem__(self, index: int, /) -> Patch | None: ... # Diff_getitem def __iter__(self) -> Iterator[Patch | None]: ... # -> DiffIter def __len__(self) -> int: ... @final class DiffDelta: flags: DiffFlag is_binary: bool nfiles: int new_file: DiffFile old_file: DiffFile similarity: int status: DeltaStatus def status_char(self) -> str: ... @final class DiffFile: flags: DiffFlag id: Oid mode: FileMode path: str raw_path: bytes size: int @staticmethod def from_c(bytes, /) -> DiffFile: ... @disjoint_base class DiffHunk: header: str lines: list[DiffLine] new_lines: int new_start: int old_lines: int old_start: int @final class DiffLine: content: str content_offset: int new_lineno: int num_lines: int old_lineno: int origin: str raw_content: bytes @disjoint_base class DiffStats: deletions: int files_changed: int insertions: int def format(self, format: DiffStatsFormat, width: int) -> str: ... @final class FilterSource: repo: object path: str filemode: int oid: Oid | None mode: int flags: int class GitError(Exception): ... class InvalidSpecError(ValueError): ... @final class Mailmap: def __init__(self, *args) -> None: ... def add_entry( self, real_name: str | None = ..., real_email: str | None = ..., replace_name: str | None = ..., replace_email: str = ..., ) -> None: ... @staticmethod def from_buffer(buffer: str | bytes) -> Mailmap: ... @staticmethod def from_repository(repository: Repository) -> Mailmap: ... def resolve(self, name: str, email: str) -> tuple[str, str]: ... def resolve_signature(self, sig: Signature) -> Signature: ... @final class Note: annotated_id: Oid id: Oid message: str data: bytes def remove( self, author: Signature, committer: Signature, ref: str = 'refs/notes/commits' ) -> None: ... @final class Odb: backends: Iterator[OdbBackend] def __init__(self, *args, **kwargs) -> None: ... def add_backend(self, backend: OdbBackend, priority: int) -> None: ... def add_disk_alternate(self, path: str | Path, /) -> None: ... def exists(self, oid: _OidArg, /) -> bool: ... def read(self, oid: _OidArg, /) -> tuple[ObjectType, bytes]: ... def read_header(self, oid: _OidArg, /) -> tuple[ObjectType, int]: ... def write(self, type: int, data: bytes | str) -> Oid: ... def __contains__(self, other: _OidArg, /) -> bool: ... def __iter__(self) -> Iterator[Oid]: ... # Odb_as_iter @disjoint_base class OdbBackend: def __init__(self, *args, **kwargs) -> None: ... def exists(self, oid: _OidArg, /) -> bool: ... def exists_prefix(self, partial_id: _OidArg, /) -> Oid: ... def read(self, oid: _OidArg, /) -> tuple[int, bytes]: ... def read_header(self, oid: _OidArg, /) -> tuple[int, int]: ... def read_prefix(self, oid: _OidArg, /) -> tuple[int, bytes, Oid]: ... def refresh(self) -> None: ... def __iter__(self) -> Iterator[Oid]: ... # OdbBackend_as_iter @final class OdbBackendLoose(OdbBackend): def __init__(self, *args, **kwargs) -> None: ... @final class OdbBackendPack(OdbBackend): def __init__(self, *args, **kwargs) -> None: ... @final class Oid: raw: bytes def __init__(self, raw: bytes = ..., hex: str = ...) -> None: ... def __eq__(self, other, /) -> bool: ... def __ge__(self, other, /) -> bool: ... def __gt__(self, other, /) -> bool: ... def __hash__(self) -> int: ... def __le__(self, other, /) -> bool: ... def __lt__(self, other, /) -> bool: ... def __ne__(self, other, /) -> bool: ... def __bool__(self) -> bool: ... @final class Patch: data: bytes delta: DiffDelta hunks: list[DiffHunk] line_stats: tuple[int, int, int] # context, additions, deletions text: str | None @staticmethod def create_from( old: Blob | bytes | None, new: Blob | bytes | None, old_as_path: str = ..., new_as_path: str = ..., flag: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Patch: ... @final class RefLogEntry: committer: Signature message: str oid_new: Oid oid_old: Oid def __init__(self, *args, **kwargs) -> None: ... @final class Refdb: def __init__(self) -> None: ... def compress(self) -> None: ... @staticmethod def new(repo: Repository, /) -> Refdb: ... @staticmethod def open(repo: Repository, /) -> Refdb: ... def set_backend(self, backend: RefdbBackend, /) -> None: ... @disjoint_base class RefdbBackend: def __init__(self, *args, **kwargs) -> None: ... def compress(self) -> None: ... def delete( self, ref_name: str, old_id: _OidArg, old_target: str | None ) -> None: ... def ensure_log(self, ref_name: str, /) -> bool: ... def exists(self, refname: str, /) -> bool: ... def has_log(self, ref_name: str, /) -> bool: ... def lookup(self, refname: str, /) -> Reference: ... def rename( self, old_name: str, new_name: str, force: bool, who: Signature, message: str ) -> Reference: ... def write( self, ref: Reference, force: bool, who: Signature, message: str, old: None | _OidArg, old_target: None | str, ) -> None: ... @final class RefdbFsBackend(RefdbBackend): def __init__(self, *args, **kwargs) -> None: ... _Proxy = None | Literal[True] | str class _StrArray: # incomplete count: int class _LsRemotesDict(TypedDict): local: bool loid: Oid | None name: str | None symref_target: str | None oid: Oid @disjoint_base class Repository: def TreeBuilder(self, src: Tree | _OidArg = ...) -> TreeBuilder: ... def __init__(self, /, *args, **kwargs) -> None: ... def _disown(self) -> None: ... default_signature: Signature head: Reference head_is_detached: bool head_is_unborn: bool is_bare: bool is_empty: bool is_shallow: bool odb: Odb path: str | None refdb: Refdb workdir: str | None def add_worktree( self, name: str, path: str | Path, ref: Reference = ... ) -> Worktree: ... def applies( self, diff: Diff, location: ApplyLocation = ApplyLocation.INDEX, raise_error: bool = False, ) -> bool: ... def apply( self, diff: Diff, location: ApplyLocation = ApplyLocation.WORKDIR ) -> None: ... def cherrypick(self, id: _OidArg, /) -> None: ... def compress_references(self) -> None: ... def create_blob(self, data: str | bytes) -> Oid: ... def create_blob_fromdisk(self, path: str, /) -> Oid: ... def create_blob_fromiobase(self, iobase: IOBase, /) -> Oid: ... def create_blob_fromworkdir(self, path: str | Path, /) -> Oid: ... def create_branch(self, name: str, commit: Commit, force=False) -> Branch: ... def create_commit( self, reference_name: Optional[str], author: Signature, committer: Signature, message: str | bytes, tree: _OidArg, parents: Sequence[_OidArg], encoding: str = ..., ) -> Oid: ... def create_commit_string( self, author: Signature, committer: Signature, message: str | bytes, tree: _OidArg, parents: list[_OidArg], encoding: str = ..., ) -> Oid: ... def create_commit_with_signature( self, content: str, signature: str, signature_field: Optional[str] = None ) -> Oid: ... def create_note( self, message: str, author: Signature, committer: Signature, annotated_id: str, ref: str = 'refs/notes/commits', force: bool = False, ) -> Oid: ... def create_reference_direct( self, name: str, target: _OidArg, force: bool, message: Optional[str] = None ) -> Reference: ... def create_reference_symbolic( self, name: str, target: str, force: bool, message: Optional[str] = None ) -> Reference: ... def create_tag( self, name: str, oid: _OidArg, type: ObjectType, tagger: Signature, message: str ) -> Oid: ... def descendant_of(self, oid1: _OidArg, oid2: _OidArg) -> bool: ... def expand_id(self, hex: str, /) -> Oid: ... def free(self) -> None: ... def git_object_lookup_prefix(self, oid: _OidArg, /) -> Object: ... def list_worktrees(self) -> list[str]: ... def listall_branches(self, flag: BranchType = BranchType.LOCAL) -> list[str]: ... def listall_mergeheads(self) -> list[Oid]: ... def listall_stashes(self) -> list[Stash]: ... def listall_submodules(self) -> list[str]: ... def lookup_branch( self, branch_name: str | bytes, branch_type: BranchType = BranchType.LOCAL ) -> Branch: ... def lookup_note( self, annotated_id: str, ref: str = 'refs/notes/commits' ) -> Note: ... def lookup_reference(self, name: str, /) -> Reference: ... def lookup_reference_dwim(self, name: str, /) -> Reference: ... def lookup_worktree(self, name: str) -> Worktree: ... def merge_analysis( self, their_head: _OidArg, our_ref: str = 'HEAD' ) -> tuple[MergeAnalysis, MergePreference]: ... def merge_base(self, oid1: _OidArg, oid2: _OidArg) -> Oid: ... def merge_base_many(self, oids: list[_OidArg]) -> Oid: ... def merge_base_octopus(self, oids: list[_OidArg]) -> Oid: ... def notes(self) -> Iterator[Note]: ... def path_is_ignored(self, path: str) -> bool: ... def raw_listall_branches( self, flag: BranchType = BranchType.LOCAL ) -> list[bytes]: ... def raw_listall_references(self) -> list[bytes]: ... def references_iterator_init(self) -> Iterator[Reference]: ... def references_iterator_next( self, iter: Iterator[_T], references_return_type: ReferenceFilter = ReferenceFilter.ALL, ) -> Reference: ... def reset(self, oid: _OidArg, reset_type: ResetMode) -> None: ... def revparse(self, revspec: str, /) -> RevSpec: ... def revparse_ext(self, revision: str, /) -> tuple[Object, Reference]: ... def revparse_single(self, revision: str, /) -> Object: ... def set_odb(self, odb: Odb, /) -> None: ... def set_refdb(self, refdb: Refdb, /) -> None: ... def status( self, untracked_files: str = 'all', ignored: bool = False ) -> dict[str, int]: ... def status_file(self, path: str, /) -> int: ... def walk( self, oid: _OidArg | None, sort_mode: SortMode = SortMode.NONE ) -> Walker: ... @disjoint_base class RevSpec: flags: int from_object: Object to_object: Object @final class Signature: _encoding: str | None _pointer: _Pointer[GitSignatureC] email: str name: str offset: int raw_email: bytes raw_name: bytes time: int def __init__( self, name: str | bytes, email: str, time: int = -1, offset: int = 0, encoding: Optional[str] = None, ) -> None: ... def __eq__(self, other, /) -> bool: ... def __ge__(self, other, /) -> bool: ... def __gt__(self, other, /) -> bool: ... def __le__(self, other, /) -> bool: ... def __lt__(self, other, /) -> bool: ... def __ne__(self, other, /) -> bool: ... @final class Stash: commit_id: Oid message: str raw_message: bytes def __eq__(self, other, /) -> bool: ... def __ge__(self, other, /) -> bool: ... def __gt__(self, other, /) -> bool: ... def __le__(self, other, /) -> bool: ... def __lt__(self, other, /) -> bool: ... def __ne__(self, other, /) -> bool: ... @final class Tag(Object): message: str name: str raw_message: bytes raw_name: bytes tagger: Signature target: Oid def get_object(self) -> Object: ... class Tree(Object): def diff_to_index( self, index: Index, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Diff: ... def diff_to_tree( self, tree: Tree = ..., flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 3, swap: bool = False, ) -> Diff: ... def diff_to_workdir( self, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Diff: ... def __contains__(self, other: str, /) -> bool: ... # Tree_contains def __getitem__(self, index: str | int, /) -> Tree | Blob: ... # Tree_subscript def __iter__(self) -> Iterator[Object]: ... def __len__(self) -> int: ... # Tree_len def __rtruediv__(self, other: str, /) -> Tree | Blob: ... def __truediv__(self, other: str, /) -> Tree | Blob: ... # Tree_divide @disjoint_base class TreeBuilder: def clear(self) -> None: ... def get(self, name: str, /) -> Object: ... def insert(self, name: str, oid: _OidArg, attr: int) -> None: ... def remove(self, name: str, /) -> None: ... def write(self) -> Oid: ... def __len__(self) -> int: ... @final class Walker: def hide(self, oid: _OidArg, /) -> None: ... def push(self, oid: _OidArg, /) -> None: ... def reset(self) -> None: ... def simplify_first_parent(self) -> None: ... def sort(self, mode: SortMode, /) -> None: ... def __iter__(self) -> Iterator[Commit]: ... # Walker: ... def __next__(self) -> Commit: ... @final class Worktree: is_prunable: bool name: str path: str def prune(self, force=False) -> None: ... def discover_repository( path: str | Path, across_fs: bool = False, ceiling_dirs: str = ... ) -> str | None: ... def hash(data: bytes | str) -> Oid: ... def hashfile(path: str) -> Oid: ... def init_file_backend(path: str, flags: int = 0) -> object: ... def reference_is_valid_name(refname: str, /) -> bool: ... def tree_entry_cmp(a: Object, b: Object) -> int: ... def _cache_enums() -> None: ... def filter_register(name: str, filter: type[Filter]) -> None: ... _OidArg = str | Oid libgit2-pygit2-d86662e/pygit2/_run.py000066400000000000000000000054561521317216500174000ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """ This is an special module, it provides stuff used by by pygit2 at run-time. """ # Import from the Standard Library import codecs import sys from pathlib import Path # Import from cffi from cffi import FFI # Import from pygit2 try: from _build import get_libgit2_paths # type: ignore except ImportError: from ._build import get_libgit2_paths # C_HEADER_SRC if getattr(sys, 'frozen', False): if hasattr(sys, '_MEIPASS'): dir_path = Path(sys._MEIPASS) else: dir_path = Path(sys.executable).parent else: dir_path = Path(__file__).parent.absolute() # Order matters h_files = [ 'types.h', 'oid.h', 'attr.h', 'blame.h', 'buffer.h', 'strarray.h', 'diff.h', 'checkout.h', 'transport.h', 'proxy.h', 'indexer.h', 'pack.h', 'remote.h', 'clone.h', 'common.h', 'config.h', 'describe.h', 'errors.h', 'graph.h', 'index.h', 'merge.h', 'net.h', 'refspec.h', 'repository.h', 'filter.h', 'commit.h', 'revert.h', 'stash.h', 'submodule.h', 'transaction.h', 'options.h', 'callbacks.h', # Bridge from libgit2 to Python ] h_source = [] for h_file in h_files: h_file = dir_path / 'decl' / h_file # type: ignore with codecs.open(h_file, 'r', 'utf-8') as f: h_source.append(f.read()) C_HEADER_SRC = '\n'.join(h_source) C_PREAMBLE = """\ #include #include #include """ # ffi _, libgit2_kw = get_libgit2_paths() ffi = FFI() ffi.set_source('pygit2._libgit2', C_PREAMBLE, **libgit2_kw) ffi.cdef(C_HEADER_SRC) if __name__ == '__main__': ffi.compile() libgit2-pygit2-d86662e/pygit2/blame.py000066400000000000000000000111251521317216500175030ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Iterator from typing import TYPE_CHECKING from ._pygit2 import Oid, Repository, Signature # Import from pygit2 from .ffi import C, ffi from .utils import GenericIterator if TYPE_CHECKING: from ._libgit2.ffi import GitBlameC, GitHunkC, GitSignatureC def wrap_signature(csig: 'GitSignatureC') -> None | Signature: if not csig: return None return Signature( ffi.string(csig.name).decode('utf-8'), ffi.string(csig.email).decode('utf-8'), csig.when.time, csig.when.offset, 'utf-8', ) class BlameHunk: _blame: 'Blame' _hunk: 'GitHunkC' @classmethod def _from_c(cls, blame: 'Blame', ptr: 'GitHunkC') -> 'BlameHunk': hunk = cls.__new__(cls) hunk._blame = blame hunk._hunk = ptr return hunk @property def lines_in_hunk(self) -> int: """Number of lines""" return self._hunk.lines_in_hunk @property def boundary(self) -> bool: """Tracked to a boundary commit""" # Casting directly to bool via cffi does not seem to work return int(ffi.cast('int', self._hunk.boundary)) != 0 @property def final_start_line_number(self) -> int: """Final start line number""" return self._hunk.final_start_line_number @property def final_committer(self) -> None | Signature: """Final committer""" return wrap_signature(self._hunk.final_signature) @property def final_commit_id(self) -> Oid: return Oid( raw=bytes(ffi.buffer(ffi.addressof(self._hunk, 'final_commit_id'))[:]) ) @property def orig_start_line_number(self) -> int: """Origin start line number""" return self._hunk.orig_start_line_number @property def orig_committer(self) -> None | Signature: """Original committer""" return wrap_signature(self._hunk.orig_signature) @property def orig_commit_id(self) -> Oid: return Oid( raw=bytes(ffi.buffer(ffi.addressof(self._hunk, 'orig_commit_id'))[:]) ) @property def orig_path(self) -> None | str: """Original path""" path = self._hunk.orig_path if not path: return None return ffi.string(path).decode('utf-8') class Blame: _repo: Repository _blame: 'GitBlameC' @classmethod def _from_c(cls, repo: Repository, ptr: 'GitBlameC') -> 'Blame': blame = cls.__new__(cls) blame._repo = repo blame._blame = ptr return blame def __del__(self) -> None: C.git_blame_free(self._blame) def __len__(self) -> int: return C.git_blame_get_hunk_count(self._blame) def __getitem__(self, index: int) -> BlameHunk: chunk = C.git_blame_get_hunk_byindex(self._blame, index) if not chunk: raise IndexError return BlameHunk._from_c(self, chunk) def for_line(self, line_no: int) -> BlameHunk: """ Returns the object for a given line given its number in the current Blame. Parameters: line_no Line number, starts at 1. """ if line_no < 0: raise IndexError chunk = C.git_blame_get_hunk_byline(self._blame, line_no) if not chunk: raise IndexError return BlameHunk._from_c(self, chunk) def __iter__(self) -> Iterator[BlameHunk]: return GenericIterator(self) libgit2-pygit2-d86662e/pygit2/blob.py000066400000000000000000000112371521317216500173450ustar00rootroot00000000000000import io import threading import time from contextlib import AbstractContextManager from queue import Queue from typing import Optional from ._pygit2 import Blob, Oid from .enums import BlobFilter class _BlobIO(io.RawIOBase): """Low-level wrapper for streaming blob content. The underlying libgit2 git_writestream filter chain will be run in a separate thread. The GIL will be released while running libgit2 filtering. """ def __init__( self, blob: Blob, as_path: Optional[str] = None, flags: BlobFilter = BlobFilter.CHECK_FOR_BINARY, commit_id: Optional[Oid] = None, ): super().__init__() self._blob = blob self._queue: Optional[Queue] = Queue(maxsize=1) self._ready = threading.Event() self._writer_closed = threading.Event() self._chunk: Optional[bytes] = None self._thread = threading.Thread( target=self._blob._write_to_queue, args=(self._queue, self._ready, self._writer_closed), kwargs={ 'as_path': as_path, 'flags': int(flags), 'commit_id': commit_id, }, daemon=True, ) self._thread.start() def __exit__(self, exc_type, exc_value, traceback): self.close() def isatty(self): return False def readable(self): return True def writable(self): return False def seekable(self): return False def readinto(self, b, /): try: while self._chunk is None: self._ready.wait() if self._queue.empty(): if self._writer_closed.is_set(): # EOF return 0 self._ready.clear() time.sleep(0) continue chunk = self._queue.get() if chunk: self._chunk = chunk if len(self._chunk) <= len(b): bytes_written = len(self._chunk) b[:bytes_written] = self._chunk self._chunk = None return bytes_written bytes_written = len(b) b[:] = self._chunk[:bytes_written] self._chunk = self._chunk[bytes_written:] return bytes_written except KeyboardInterrupt: return 0 def close(self) -> None: try: self._ready.wait() self._writer_closed.wait() while self._queue is not None and not self._queue.empty(): self._queue.get() self._thread.join() except KeyboardInterrupt: pass self._queue = None class BlobIO(io.BufferedReader, AbstractContextManager): """Read-only wrapper for streaming blob content. Supports reading both raw and filtered blob content. Implements io.BufferedReader. Example: >>> with BlobIO(blob) as f: ... while True: ... # Read blob data in 1KB chunks until EOF is reached ... chunk = f.read(1024) ... if not chunk: ... break By default, `BlobIO` will stream the raw contents of the blob, but it can also be used to stream filtered content (i.e. to read the content after applying filters which would be used when checking out the blob to the working directory). Example: >>> with BlobIO(blob, as_path='my_file.ext') as f: ... # Read the filtered content which would be returned upon ... # running 'git checkout -- my_file.txt' ... filtered_data = f.read() """ def __init__( self, blob: Blob, as_path: Optional[str] = None, flags: BlobFilter = BlobFilter.CHECK_FOR_BINARY, commit_id: Optional[Oid] = None, ): """Wrap the specified blob. Parameters: blob: The blob to wrap. as_path: Filter the contents of the blob as if it had the specified path. If `as_path` is None, the raw contents of the blob will be read. flags: A combination of enums.BlobFilter constants (only applicable when `as_path` is set). commit_id: Commit to load attributes from when ATTRIBUTES_FROM_COMMIT is specified in `flags` (only applicable when `as_path` is set). """ raw = _BlobIO(blob, as_path=as_path, flags=flags, commit_id=commit_id) super().__init__(raw) def __exit__(self, exc_type, exc_value, traceback): self.close() io.RawIOBase.register(_BlobIO) io.BufferedIOBase.register(BlobIO) libgit2-pygit2-d86662e/pygit2/branches.py000066400000000000000000000076371521317216500202250ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations from collections.abc import Iterator from typing import TYPE_CHECKING from ._pygit2 import Branch, Commit, Oid from .enums import BranchType, ReferenceType # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: from .repository import BaseRepository class Branches: local: 'Branches' remote: 'Branches' def __init__( self, repository: BaseRepository, flag: BranchType = BranchType.ALL, commit: Commit | Oid | str | None = None, ) -> None: self._repository = repository self._flag = flag if commit is not None: if isinstance(commit, Commit): commit = commit.id elif not isinstance(commit, Oid): commit = self._repository.expand_id(commit) self._commit = commit if flag == BranchType.ALL: self.local = Branches(repository, flag=BranchType.LOCAL, commit=commit) self.remote = Branches(repository, flag=BranchType.REMOTE, commit=commit) def __getitem__(self, name: str) -> Branch: branch = None if self._flag & BranchType.LOCAL: branch = self._repository.lookup_branch(name, BranchType.LOCAL) if branch is None and self._flag & BranchType.REMOTE: branch = self._repository.lookup_branch(name, BranchType.REMOTE) if branch is None or not self._valid(branch): raise KeyError(f'Branch not found: {name}') return branch def get(self, key: str) -> Branch: try: return self[key] except KeyError: return None # type:ignore # next commit def __iter__(self) -> Iterator[str]: for branch_name in self._repository.listall_branches(self._flag): if self._commit is None or self.get(branch_name) is not None: yield branch_name def create(self, name: str, commit: Commit, force: bool = False) -> Branch: return self._repository.create_branch(name, commit, force) def delete(self, name: str) -> None: self[name].delete() def _valid(self, branch: Branch) -> bool: if branch.type == ReferenceType.SYMBOLIC: branch_direct = branch.resolve() else: branch_direct = branch return ( self._commit is None or branch_direct.target == self._commit or self._repository.descendant_of(branch_direct.target, self._commit) ) def with_commit(self, commit: Commit | Oid | str | None) -> 'Branches': assert self._commit is None return Branches(self._repository, self._flag, commit) def __contains__(self, name: str) -> bool: return self.get(name) is not None libgit2-pygit2-d86662e/pygit2/callbacks.py000066400000000000000000000676501521317216500203600ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """ In this module we keep everything concerning callback. This is how it works, with an example: 1. The pygit2 API calls libgit2, it passes a payload object e.g. Remote.fetch calls git_remote_fetch 2. libgit2 calls Python callbacks e.g. git_remote_fetch calls _transfer_progress_cb 3. Optionally, the Python callback may proxy to a user defined function e.g. _transfer_progress_cb calls RemoteCallbacks.transfer_progress 4. The user defined function may return something on success, or raise an exception on error, or raise the special Passthrough exception. 5. The callback may return in 3 different ways to libgit2: - Returns GIT_OK on success. - Returns GIT_PASSTHROUGH if the user defined function raised Passthrough, this tells libgit2 to act as if this callback didn't exist in the first place. - Returns GIT_EUSER if another exception was raised, and keeps the exception in the payload to be re-raised later. 6. libgit2 returns to the pygit2 API, with an error code e.g. git_remote_fetch returns to Remote.fetch 7. The pygit2 API will: - Return something on success. - Raise the original exception if libgit2 returns GIT_EUSER - Raise another exception if libgit2 returns another error code The payload object is passed all the way, so pygit2 API can send information to the inner user defined function, and this can send back results to the pygit2 API. """ # Standard Library from collections.abc import Callable, Generator from contextlib import contextmanager from functools import wraps from typing import TYPE_CHECKING, Any, Optional, ParamSpec, TypeVar # pygit2 from ._pygit2 import DiffFile, Oid from .credentials import Keypair, Username, UserPass from .enums import CheckoutNotify, CheckoutStrategy, CredentialType, StashApplyProgress from .errors import Passthrough, check_error from .ffi import C, ffi from .utils import StrArray, maybe_string, ptr_to_bytes, to_bytes _Credentials = Username | UserPass | Keypair if TYPE_CHECKING: from pygit2._libgit2.ffi import GitProxyOptionsC from .remotes import PushUpdate, TransferProgress # # The payload is the way to pass information from the pygit2 API, through # libgit2, to the Python callbacks. And back. # class Payload: repository: Callable | None remote: Callable | None clone_options: Any def __init__(self, **kw: object) -> None: for key, value in kw.items(): setattr(self, key, value) self._stored_exception = None def check_error(self, error_code: int) -> None: if error_code == C.GIT_EUSER: assert self._stored_exception is not None raise self._stored_exception elif self._stored_exception is not None: # A callback mapped to a C function returning void # might still have raised an exception. raise self._stored_exception check_error(error_code) class RemoteCallbacks(Payload): """Base class for pygit2 remote callbacks. Inherit from this class and override the callbacks which you want to use in your class, which you can then pass to the network operations. For the credentials, you can either subclass and override the 'credentials' method, or if it's a constant value, pass the value to the constructor, e.g. RemoteCallbacks(credentials=credentials). You can as well pass the certificate the same way, for example: RemoteCallbacks(certificate=certificate). """ push_options: Any def __init__( self, credentials: _Credentials | None = None, certificate_check: Callable[[None, bool, bytes], bool] | None = None, ) -> None: super().__init__() if credentials is not None: self.credentials = credentials # type: ignore[method-assign, assignment] if certificate_check is not None: self.certificate_check = certificate_check # type: ignore[method-assign, assignment] def sideband_progress(self, string: str) -> None: """ Progress output callback. Override this function with your own progress reporting function Parameters: string : str Progress output from the remote. """ def credentials( self, url: str, username_from_url: str | None, allowed_types: CredentialType, ) -> _Credentials: """ Credentials callback. If the remote server requires authentication, this function will be called and its return value used for authentication. Override it if you want to be able to perform authentication. Returns: credential Parameters: url : str The url of the remote. username_from_url : str or None Username extracted from the url, if any. allowed_types : CredentialType A combination of CredentialType bitflags representing the credential types supported by the remote. """ raise Passthrough def certificate_check(self, certificate: None, valid: bool, host: bytes) -> bool: """ Certificate callback. Override with your own function to determine whether to accept the server's certificate. Returns: True to connect, False to abort. Parameters: certificate : None The certificate. It is currently always None while we figure out how to represent it cross-platform. valid : bool Whether the TLS/SSH library thinks the certificate is valid. host : str The hostname we want to connect to. """ raise Passthrough def push_negotiation(self, updates: list['PushUpdate']) -> None: """ During a push, called once between the negotiation step and the upload. Provides information about what updates will be performed. Override with your own function to check the pending updates and possibly reject them (by raising an exception). """ def transfer_progress(self, stats: 'TransferProgress') -> None: """ During the download of new data, this will be regularly called with the indexer's progress. Override with your own function to report transfer progress. Parameters: stats : TransferProgress The progress up to now. """ def push_transfer_progress( self, objects_pushed: int, total_objects: int, bytes_pushed: int ) -> None: """ During the upload portion of a push, this will be regularly called with progress information. Be aware that this is called inline with pack building operations, so performance may be affected. Override with your own function to report push transfer progress. """ def update_tips(self, refname: str, old: Oid, new: Oid) -> None: """ Update tips callback. Override with your own function to report reference updates. Parameters: refname : str The name of the reference that's being updated. old : Oid The reference's old value. new : Oid The reference's new value. """ def push_update_reference(self, refname: str, message: str) -> None: """ Push update reference callback. Override with your own function to report the remote's acceptance or rejection of reference updates. refname : str The name of the reference (on the remote). message : str Rejection message from the remote. If None, the update was accepted. """ class CheckoutCallbacks(Payload): """Base class for pygit2 checkout callbacks. Inherit from this class and override the callbacks that you want to use in your class, which you can then pass to checkout operations. """ def __init__(self) -> None: super().__init__() def checkout_notify_flags(self) -> CheckoutNotify: """ Returns a bit mask of the notifications to receive from a checkout (a combination of enums.CheckoutNotify constants). By default, if you override `checkout_notify`, all notifications will be enabled. You can fine tune the notification types to enable by overriding `checkout_notify_flags`. Please note that the flags are only sampled once when checkout begins. You cannot change the flags while a checkout is in progress. """ if type(self).checkout_notify == CheckoutCallbacks.checkout_notify: # If the user hasn't overridden the notify function, # filter out all notifications. return CheckoutNotify.NONE else: # If the user provides their own notify function, # enable all notifications by default. return CheckoutNotify.ALL def checkout_notify( self, why: CheckoutNotify, path: str, baseline: Optional[DiffFile], target: Optional[DiffFile], workdir: Optional[DiffFile], ) -> None: """ Checkout will invoke an optional notification callback for certain cases - you pick which ones via `checkout_notify_flags`. Raising an exception from this callback will cancel the checkout. The exception will be propagated back and raised by the ``Repository.checkout_...`` call. Notification callbacks are made prior to modifying any files on disk, so canceling on any notification will still happen prior to any files being modified. """ pass def checkout_progress( self, path: str, completed_steps: int, total_steps: int ) -> None: """ Optional callback to notify the consumer of checkout progress. """ pass class StashApplyCallbacks(CheckoutCallbacks): """Base class for pygit2 stash apply callbacks. Inherit from this class and override the callbacks that you want to use in your class, which you can then pass to stash apply or pop operations. """ def stash_apply_progress(self, progress: StashApplyProgress) -> None: """ Stash application progress notification function. `progress` is a StashApplyProgress constant. Raising an exception from this callback will abort the stash application. """ pass # # The context managers below wrap the calls to libgit2 functions, which them in # turn call to callbacks defined later in this module. These context managers # are used in the pygit2 API, see for instance remote.py # @contextmanager def git_clone_options(payload, opts=None): if opts is None: opts = ffi.new('git_clone_options *') C.git_clone_options_init(opts, C.GIT_CLONE_OPTIONS_VERSION) handle = ffi.new_handle(payload) # Plug callbacks if payload.repository: opts.repository_cb = C._repository_create_cb opts.repository_cb_payload = handle if payload.remote: opts.remote_cb = C._remote_create_cb opts.remote_cb_payload = handle # Give back control payload._stored_exception = None payload.clone_options = opts yield payload @contextmanager def git_fetch_options(payload, opts=None): if payload is None: payload = RemoteCallbacks() if opts is None: opts = ffi.new('git_fetch_options *') C.git_fetch_options_init(opts, C.GIT_FETCH_OPTIONS_VERSION) # Plug callbacks opts.callbacks.sideband_progress = C._sideband_progress_cb opts.callbacks.transfer_progress = C._transfer_progress_cb opts.callbacks.update_tips = C._update_tips_cb opts.callbacks.credentials = C._credentials_cb opts.callbacks.certificate_check = C._certificate_check_cb # Payload handle = ffi.new_handle(payload) opts.callbacks.payload = handle # Give back control payload.fetch_options = opts payload._stored_exception = None yield payload @contextmanager def git_proxy_options( payload: object, opts: Optional['GitProxyOptionsC'] = None, proxy: None | bool | str = None, ) -> Generator['GitProxyOptionsC', None, None]: if opts is None: opts = ffi.new('git_proxy_options *') C.git_proxy_options_init(opts, C.GIT_PROXY_OPTIONS_VERSION) if proxy is None: opts.type = C.GIT_PROXY_NONE elif proxy is True: opts.type = C.GIT_PROXY_AUTO elif type(proxy) is str: opts.type = C.GIT_PROXY_SPECIFIED # Keep url in memory, otherwise memory is freed and bad things happen payload.__proxy_url = ffi.new('char[]', to_bytes(proxy)) # type: ignore[attr-defined] opts.url = payload.__proxy_url # type: ignore[attr-defined] else: raise TypeError('Proxy must be None, True, or a string') yield opts @contextmanager def git_push_options(payload, opts=None): if payload is None: payload = RemoteCallbacks() opts = ffi.new('git_push_options *') C.git_push_options_init(opts, C.GIT_PUSH_OPTIONS_VERSION) # Plug callbacks opts.callbacks.sideband_progress = C._sideband_progress_cb opts.callbacks.transfer_progress = C._transfer_progress_cb opts.callbacks.update_tips = C._update_tips_cb opts.callbacks.credentials = C._credentials_cb opts.callbacks.certificate_check = C._certificate_check_cb opts.callbacks.push_update_reference = C._push_update_reference_cb opts.callbacks.push_negotiation = C._push_negotiation_cb # Per libgit2 sources, push_transfer_progress may incur a performance hit. # So, set it only if the user has overridden the no-op stub. if ( type(payload).push_transfer_progress is not RemoteCallbacks.push_transfer_progress ): opts.callbacks.push_transfer_progress = C._push_transfer_progress_cb # Payload handle = ffi.new_handle(payload) opts.callbacks.payload = handle # Give back control payload.push_options = opts payload._stored_exception = None yield payload @contextmanager def git_remote_callbacks(payload): if payload is None: payload = RemoteCallbacks() cdata = ffi.new('git_remote_callbacks *') C.git_remote_init_callbacks(cdata, C.GIT_REMOTE_CALLBACKS_VERSION) # Plug callbacks cdata.credentials = C._credentials_cb cdata.update_tips = C._update_tips_cb cdata.certificate_check = C._certificate_check_cb # Payload handle = ffi.new_handle(payload) cdata.payload = handle # Give back control payload._stored_exception = None payload.remote_callbacks = cdata yield payload # # C callbacks # # These functions are called by libgit2. They cannot raise exceptions, since # they return to libgit2, they can only send back error codes. # # They cannot be overridden, but sometimes the only thing these functions do is # to proxy the call to a user defined function. If user defined functions # raises an exception, the callback must store it somewhere and return # GIT_EUSER to libgit2, then the outer Python code will be able to reraise the # exception. # P = ParamSpec('P') T = TypeVar('T') def libgit2_callback(f: Callable[P, T]) -> Callable[P, T]: @wraps(f) def wrapper(*args): data = ffi.from_handle(args[-1]) args = args[:-1] + (data,) try: return f(*args) except Passthrough: # A user defined callback can raise Passthrough to decline to act; # then libgit2 will behave as if there was no callback set in the # first place. return C.GIT_PASSTHROUGH except BaseException as e: # Keep the exception to be re-raised later, and inform libgit2 that # the user defined callback has failed. data._stored_exception = e return C.GIT_EUSER return ffi.def_extern()(wrapper) # type: ignore[attr-defined] def libgit2_callback_void(f: Callable[P, T]) -> Callable[P, T]: @wraps(f) def wrapper(*args): data = ffi.from_handle(args[-1]) args = args[:-1] + (data,) try: f(*args) except Passthrough: # A user defined callback can raise Passthrough to decline to act; # then libgit2 will behave as if there was no callback set in the # first place. pass # Function returns void except BaseException as e: # Keep the exception to be re-raised later data._stored_exception = e pass # Function returns void, so we can't do much here. return ffi.def_extern()(wrapper) # type: ignore[attr-defined] @libgit2_callback def _certificate_check_cb(cert_i, valid, host, data): # We want to simulate what should happen if libgit2 supported pass-through # for this callback. For SSH, 'valid' is always False, because it doesn't # look at known_hosts, but we do want to let it through in order to do what # libgit2 would if the callback were not set. try: is_ssh = cert_i.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2 # python's parsing is deep in the libraries and assumes an OpenSSL-owned cert val = data.certificate_check(None, bool(valid), ffi.string(host)) if not val: return C.GIT_ECERTIFICATE except Passthrough: if is_ssh: return 0 elif valid: return 0 else: return C.GIT_ECERTIFICATE return 0 @libgit2_callback def _credentials_cb(cred_out, url, username, allowed, data): credentials = getattr(data, 'credentials', None) if not credentials: return 0 # convert int flags to enum before forwarding to user code allowed = CredentialType(allowed) ccred = get_credentials(credentials, url, username, allowed) cred_out[0] = ccred[0] return 0 @libgit2_callback def _push_negotiation_cb(updates, num_updates, data): from .remotes import PushUpdate push_negotiation = getattr(data, 'push_negotiation', None) if not push_negotiation: return 0 py_updates = [PushUpdate(updates[i]) for i in range(num_updates)] push_negotiation(py_updates) return 0 @libgit2_callback def _push_update_reference_cb(ref, msg, data): push_update_reference = getattr(data, 'push_update_reference', None) if not push_update_reference: return 0 refname = maybe_string(ref) message = maybe_string(msg) push_update_reference(refname, message) return 0 @libgit2_callback def _remote_create_cb(remote_out, repo, name, url, data): from .repository import Repository remote = data.remote( Repository._from_c(repo, False), ffi.string(name), ffi.string(url) ) remote_out[0] = remote._remote # we no longer own the C object remote._remote = ffi.NULL return 0 @libgit2_callback def _repository_create_cb(repo_out, path, bare, data): repository = data.repository(ffi.string(path), bare != 0) # we no longer own the C object repository._disown() repo_out[0] = repository._repo return 0 @libgit2_callback def _sideband_progress_cb(string, length, data): sideband_progress = getattr(data, 'sideband_progress', None) if not sideband_progress: return 0 s = ffi.string(string, length).decode('utf-8') sideband_progress(s) return 0 @libgit2_callback def _transfer_progress_cb(stats_ptr, data): from .remotes import TransferProgress transfer_progress = getattr(data, 'transfer_progress', None) if not transfer_progress: return 0 transfer_progress(TransferProgress(stats_ptr)) return 0 @libgit2_callback def _push_transfer_progress_cb(current, total, bytes_pushed, payload): push_transfer_progress = getattr(payload, 'push_transfer_progress', None) if not push_transfer_progress: return 0 push_transfer_progress(current, total, bytes_pushed) return 0 @libgit2_callback def _update_tips_cb(refname, a, b, data): update_tips = getattr(data, 'update_tips', None) if not update_tips: return 0 s = maybe_string(refname) a = Oid(raw=bytes(ffi.buffer(a)[:])) b = Oid(raw=bytes(ffi.buffer(b)[:])) update_tips(s, a, b) return 0 # # Other functions, used above. # def get_credentials(fn, url, username, allowed): """Call fn and return the credentials object.""" url_str = maybe_string(url) username_str = maybe_string(username) creds = fn(url_str, username_str, allowed) credential_type = getattr(creds, 'credential_type', None) credential_tuple = getattr(creds, 'credential_tuple', None) if not credential_type or not credential_tuple: raise TypeError('credential does not implement interface') cred_type = credential_type if not (allowed & cred_type): raise TypeError('invalid credential type') ccred = ffi.new('git_credential **') if cred_type == CredentialType.USERPASS_PLAINTEXT: name, passwd = credential_tuple err = C.git_credential_userpass_plaintext_new( ccred, to_bytes(name), to_bytes(passwd) ) elif cred_type == CredentialType.SSH_KEY: name, pubkey, privkey, passphrase = credential_tuple name = to_bytes(name) if pubkey is None and privkey is None: err = C.git_credential_ssh_key_from_agent(ccred, name) else: err = C.git_credential_ssh_key_new( ccred, name, to_bytes(pubkey), to_bytes(privkey), to_bytes(passphrase) ) elif cred_type == CredentialType.USERNAME: (name,) = credential_tuple err = C.git_credential_username_new(ccred, to_bytes(name)) elif cred_type == CredentialType.SSH_MEMORY: name, pubkey, privkey, passphrase = credential_tuple if pubkey is None and privkey is None: raise TypeError('SSH keys from memory are empty') err = C.git_credential_ssh_key_memory_new( ccred, to_bytes(name), to_bytes(pubkey), to_bytes(privkey), to_bytes(passphrase), ) else: raise TypeError('unsupported credential type') check_error(err) return ccred # # Checkout callbacks # @libgit2_callback def _checkout_notify_cb( why, path_cstr, baseline, target, workdir, data: CheckoutCallbacks ): pypath = maybe_string(path_cstr) pybaseline = DiffFile.from_c(ptr_to_bytes(baseline)) pytarget = DiffFile.from_c(ptr_to_bytes(target)) pyworkdir = DiffFile.from_c(ptr_to_bytes(workdir)) try: data.checkout_notify(why, pypath, pybaseline, pytarget, pyworkdir) # type: ignore[arg-type] except Passthrough: # Unlike most other operations with optional callbacks, checkout # doesn't support the GIT_PASSTHROUGH return code, so we must bypass # libgit2_callback's error handling and return 0 explicitly here. pass # If the user's callback has raised any other exception type, # it's caught by the libgit2_callback decorator by now. # So, return success code to libgit2. return 0 @libgit2_callback_void def _checkout_progress_cb(path, completed_steps, total_steps, data: CheckoutCallbacks): data.checkout_progress(maybe_string(path), completed_steps, total_steps) # type: ignore[arg-type] def _git_checkout_options( callbacks=None, strategy=None, directory=None, paths=None, c_checkout_options_ptr=None, ): if callbacks is None: payload = CheckoutCallbacks() else: payload = callbacks # Get handle to payload handle = ffi.new_handle(payload) # Create the options struct to pass if not c_checkout_options_ptr: opts = ffi.new('git_checkout_options *') else: opts = c_checkout_options_ptr check_error(C.git_checkout_options_init(opts, 1)) # References we need to keep to strings and so forth refs = [handle] # pygit2's default is SAFE | RECREATE_MISSING if strategy is None: strategy = CheckoutStrategy.SAFE | CheckoutStrategy.RECREATE_MISSING opts.checkout_strategy = int(strategy) if directory: target_dir = ffi.new('char[]', to_bytes(directory)) refs.append(target_dir) opts.target_directory = target_dir if paths: strarray = StrArray(paths) refs.append(strarray) opts.paths = strarray.ptr[0] # If we want to receive any notifications, set up notify_cb in the options notify_flags = payload.checkout_notify_flags() if notify_flags != CheckoutNotify.NONE: opts.notify_cb = C._checkout_notify_cb opts.notify_flags = int(notify_flags) opts.notify_payload = handle # Set up progress callback if the user has provided their own if type(payload).checkout_progress != CheckoutCallbacks.checkout_progress: opts.progress_cb = C._checkout_progress_cb opts.progress_payload = handle # Give back control payload.checkout_options = opts payload._ffi_handle = handle payload._refs = refs payload._stored_exception = None return payload @contextmanager def git_checkout_options(callbacks=None, strategy=None, directory=None, paths=None): yield _git_checkout_options( callbacks=callbacks, strategy=strategy, directory=directory, paths=paths ) # # Stash callbacks # @libgit2_callback def _stash_apply_progress_cb(progress: StashApplyProgress, data: StashApplyCallbacks): try: data.stash_apply_progress(progress) except Passthrough: # Unlike most other operations with optional callbacks, stash apply # doesn't support the GIT_PASSTHROUGH return code, so we must bypass # libgit2_callback's error handling and return 0 explicitly here. pass # If the user's callback has raised any other exception type, # it's caught by the libgit2_callback decorator by now. # So, return success code to libgit2. return 0 @contextmanager def git_stash_apply_options( callbacks=None, reinstate_index=False, strategy=None, directory=None, paths=None ): if callbacks is None: callbacks = StashApplyCallbacks() # Set up stash options stash_apply_options = ffi.new('git_stash_apply_options *') check_error(C.git_stash_apply_options_init(stash_apply_options, 1)) flags = reinstate_index * C.GIT_STASH_APPLY_REINSTATE_INDEX stash_apply_options.flags = flags # Now set up checkout options c_checkout_options_ptr = ffi.addressof(stash_apply_options.checkout_options) payload = _git_checkout_options( callbacks=callbacks, strategy=strategy, directory=directory, paths=paths, c_checkout_options_ptr=c_checkout_options_ptr, ) assert payload == callbacks assert payload.checkout_options == c_checkout_options_ptr # Set up stash progress callback if the user has provided their own if type(callbacks).stash_apply_progress != StashApplyCallbacks.stash_apply_progress: stash_apply_options.progress_cb = C._stash_apply_progress_cb stash_apply_options.progress_payload = payload._ffi_handle # Give back control payload.stash_apply_options = stash_apply_options yield payload libgit2-pygit2-d86662e/pygit2/config.py000066400000000000000000000301631521317216500176730ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Callable, Iterator from os import PathLike from pathlib import Path from typing import TYPE_CHECKING try: from functools import cached_property except ImportError: from cached_property import cached_property # type: ignore # Import from pygit2 from .errors import check_error from .ffi import C, ffi from .utils import to_bytes if TYPE_CHECKING: from ._libgit2.ffi import GitConfigC, GitConfigEntryC from .repository import BaseRepository def str_to_bytes(value: str | PathLike[str] | bytes, name: str) -> bytes: if not isinstance(value, str): raise TypeError(f'{name} must be a string') return to_bytes(value) class ConfigIterator: def __init__(self, config, ptr) -> None: self._iter = ptr self._config = config def __del__(self) -> None: C.git_config_iterator_free(self._iter) def __iter__(self) -> 'ConfigIterator': return self def __next__(self) -> 'ConfigEntry': return self._next_entry() def _next_entry(self) -> 'ConfigEntry': centry = ffi.new('git_config_entry **') err = C.git_config_next(centry, self._iter) check_error(err) return ConfigEntry._from_c(centry[0], self) class ConfigMultivarIterator(ConfigIterator): def __next__(self) -> str | None: # type: ignore[override] entry = self._next_entry() return entry.value class Config: """Git configuration management.""" _repo: 'BaseRepository' _config: 'GitConfigC' def __init__(self, path: str | None = None) -> None: cconfig = ffi.new('git_config **') if not path: err = C.git_config_new(cconfig) else: path_bytes = str_to_bytes(path, 'path') err = C.git_config_open_ondisk(cconfig, path_bytes) check_error(err, io=True) self._config = cconfig[0] @classmethod def from_c(cls, repo: 'BaseRepository', ptr: 'GitConfigC') -> 'Config': config = cls.__new__(cls) config._repo = repo config._config = ptr return config def __del__(self) -> None: try: C.git_config_free(self._config) except AttributeError: pass def _get(self, key: str | bytes) -> tuple[int, 'ConfigEntry']: key = str_to_bytes(key, 'key') entry = ffi.new('git_config_entry **') err = C.git_config_get_entry(entry, self._config, key) return err, ConfigEntry._from_c(entry[0]) def _get_entry(self, key: str | bytes) -> 'ConfigEntry': err, entry = self._get(key) if err == C.GIT_ENOTFOUND: raise KeyError(key) check_error(err) return entry def __contains__(self, key: str | bytes) -> bool: err, cstr = self._get(key) if err == C.GIT_ENOTFOUND: return False check_error(err) return True def __getitem__(self, key: str | bytes) -> str | None: """ When using the mapping interface, the value is returned as a string. In order to apply the git-config parsing rules, you can use :meth:`Config.get_bool` or :meth:`Config.get_int`. """ entry = self._get_entry(key) return entry.value def __setitem__(self, key: str | bytes, value: bool | int | str | bytes) -> None: key = str_to_bytes(key, 'key') err = 0 if isinstance(value, bool): err = C.git_config_set_bool(self._config, key, value) elif isinstance(value, int): err = C.git_config_set_int64(self._config, key, value) else: err = C.git_config_set_string(self._config, key, to_bytes(value)) check_error(err) def __delitem__(self, key: str | bytes) -> None: key = str_to_bytes(key, 'key') err = C.git_config_delete_entry(self._config, key) check_error(err) def __iter__(self) -> Iterator['ConfigEntry']: """ Iterate over configuration entries, returning a ``ConfigEntry`` objects. These contain the name, level, and value of each configuration variable. Be aware that this may return multiple versions of each entry if they are set multiple times in the configuration files. """ citer = ffi.new('git_config_iterator **') err = C.git_config_iterator_new(citer, self._config) check_error(err) return ConfigIterator(self, citer[0]) def get_multivar( self, name: str | bytes, regex: str | None = None ) -> ConfigMultivarIterator: """Get each value of a multivar ''name'' as a list of strings. The optional ''regex'' parameter is expected to be a regular expression to filter the variables we're interested in. """ name = str_to_bytes(name, 'name') regex_bytes = to_bytes(regex or None) citer = ffi.new('git_config_iterator **') err = C.git_config_multivar_iterator_new(citer, self._config, name, regex_bytes) check_error(err) return ConfigMultivarIterator(self, citer[0]) def set_multivar( self, name: str | bytes, regex: str | bytes, value: str | bytes ) -> None: """Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression to indicate which values to replace. """ name = str_to_bytes(name, 'name') regex = str_to_bytes(regex, 'regex') value = str_to_bytes(value, 'value') err = C.git_config_set_multivar(self._config, name, regex, value) check_error(err) def delete_multivar(self, name: str | bytes, regex: str | bytes) -> None: """Delete a multivar ''name''. ''regexp'' is a regular expression to indicate which values to delete. """ name = str_to_bytes(name, 'name') regex = str_to_bytes(regex, 'regex') err = C.git_config_delete_multivar(self._config, name, regex) check_error(err) def get_bool(self, key: str | bytes) -> bool: """Look up *key* and parse its value as a boolean as per the git-config rules. Return a boolean value (True or False). Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false', 0, 'off' and 'no' """ entry = self._get_entry(key) res = ffi.new('int *') err = C.git_config_parse_bool(res, entry.c_value) check_error(err) return res[0] != 0 def get_int(self, key: bytes | str) -> int: """Look up *key* and parse its value as an integer as per the git-config rules. Return an integer. A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and 'giga' respectively. """ entry = self._get_entry(key) res = ffi.new('int64_t *') err = C.git_config_parse_int64(res, entry.c_value) check_error(err) return res[0] def add_file(self, path: str | Path, level: int = 0, force: int = 0) -> None: """Add a config file instance to an existing config.""" err = C.git_config_add_file_ondisk( self._config, to_bytes(path), level, ffi.NULL, force ) check_error(err) def snapshot(self) -> 'Config': """Create a snapshot from this Config object. This means that looking up multiple values will use the same version of the configuration files. """ ccfg = ffi.new('git_config **') err = C.git_config_snapshot(ccfg, self._config) check_error(err) return Config.from_c(self._repo, ccfg[0]) # # Methods to parse a string according to the git-config rules # @staticmethod def parse_bool(text: str) -> bool: res = ffi.new('int *') err = C.git_config_parse_bool(res, to_bytes(text)) check_error(err) return res[0] != 0 @staticmethod def parse_int(text: str) -> int: res = ffi.new('int64_t *') err = C.git_config_parse_int64(res, to_bytes(text)) check_error(err) return res[0] # # Static methods to get specialized version of the config # @staticmethod def _from_found_config(fn: Callable) -> 'Config': buf = ffi.new('git_buf *', (ffi.NULL, 0)) err = fn(buf) check_error(err, io=True) cpath = ffi.string(buf.ptr).decode('utf-8') C.git_buf_dispose(buf) return Config(cpath) @staticmethod def get_system_config() -> 'Config': """Return a object representing the system configuration file.""" return Config._from_found_config(C.git_config_find_system) @staticmethod def get_global_config() -> 'Config': """Return a object representing the global configuration file.""" return Config._from_found_config(C.git_config_find_global) @staticmethod def get_xdg_config() -> 'Config': """Return a object representing the global configuration file.""" return Config._from_found_config(C.git_config_find_xdg) class ConfigEntry: """An entry in a configuration object.""" _entry: 'GitConfigEntryC' iterator: ConfigIterator | None @classmethod def _from_c( cls, ptr: 'GitConfigEntryC', iterator: ConfigIterator | None = None ) -> 'ConfigEntry': """Builds the entry from a ``git_config_entry`` pointer. ``iterator`` must be a ``ConfigIterator`` instance if the entry was created during ``git_config_iterator`` actions. """ entry = cls.__new__(cls) entry._entry = ptr entry.iterator = iterator # It should be enough to keep a reference to iterator, so we only call # git_config_iterator_free when we've deleted all ConfigEntry objects. # But it's not, to reproduce the error comment the lines below and run # the script in https://github.com/libgit2/pygit2/issues/970 # So instead we load the Python object immediately. Ideally we should # investigate libgit2 source code. if iterator is not None: entry.raw_name = entry.raw_name entry.raw_value = entry.raw_value entry.level = entry.level return entry def __del__(self) -> None: if self.iterator is None: C.git_config_entry_free(self._entry) @property def c_value(self) -> 'ffi.char_pointer': """The raw ``cData`` entry value.""" return self._entry.value @cached_property def raw_name(self) -> bytes: return ffi.string(self._entry.name) @cached_property def raw_value(self) -> bytes | None: return ffi.string(self.c_value) if self.c_value != ffi.NULL else None @cached_property def level(self) -> int: """The entry's ``git_config_level_t`` value.""" return self._entry.level @property def name(self) -> str: """The entry's name.""" return self.raw_name.decode('utf-8') @property def value(self) -> str | None: """The entry's value as a string.""" return self.raw_value.decode('utf-8') if self.raw_value is not None else None libgit2-pygit2-d86662e/pygit2/credentials.py000066400000000000000000000100651521317216500207220ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations from typing import TYPE_CHECKING from .enums import CredentialType if TYPE_CHECKING: from pathlib import Path class Username: """Username credentials This is an object suitable for passing to a remote's credentials callback and for returning from said callback. """ def __init__(self, username: str): self._username = username @property def credential_type(self) -> CredentialType: return CredentialType.USERNAME @property def credential_tuple(self) -> tuple[str]: return (self._username,) def __call__( self, _url: str, _username: str | None, _allowed: CredentialType ) -> Username: return self class UserPass: """Username/Password credentials This is an object suitable for passing to a remote's credentials callback and for returning from said callback. """ def __init__(self, username: str, password: str): self._username = username self._password = password @property def credential_type(self) -> CredentialType: return CredentialType.USERPASS_PLAINTEXT @property def credential_tuple(self) -> tuple[str, str]: return (self._username, self._password) def __call__( self, _url: str, _username: str | None, _allowed: CredentialType ) -> UserPass: return self class Keypair: """ SSH key pair credentials. This is an object suitable for passing to a remote's credentials callback and for returning from said callback. Parameters: username : str The username being used to authenticate with the remote server. pubkey : str The path to the user's public key file. privkey : str The path to the user's private key file. passphrase : str The password used to decrypt the private key file, or empty string if no passphrase is required. """ def __init__( self, username: str, pubkey: str | Path | None, privkey: str | Path | None, passphrase: str | None, ): self._username = username self._pubkey = pubkey self._privkey = privkey self._passphrase = passphrase @property def credential_type(self) -> CredentialType: return CredentialType.SSH_KEY @property def credential_tuple( self, ) -> tuple[str, str | Path | None, str | Path | None, str | None]: return (self._username, self._pubkey, self._privkey, self._passphrase) def __call__( self, _url: str, _username: str | None, _allowed: CredentialType ) -> Keypair: return self class KeypairFromAgent(Keypair): def __init__(self, username: str): super().__init__(username, None, None, None) class KeypairFromMemory(Keypair): @property def credential_type(self) -> CredentialType: return CredentialType.SSH_MEMORY libgit2-pygit2-d86662e/pygit2/decl/000077500000000000000000000000001521317216500167605ustar00rootroot00000000000000libgit2-pygit2-d86662e/pygit2/decl/attr.h000066400000000000000000000015771521317216500201150ustar00rootroot00000000000000#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0 #define GIT_ATTR_CHECK_INDEX_THEN_FILE 1 #define GIT_ATTR_CHECK_INDEX_ONLY 2 #define GIT_ATTR_CHECK_NO_SYSTEM 4 #define GIT_ATTR_CHECK_INCLUDE_HEAD 8 #define GIT_ATTR_CHECK_INCLUDE_COMMIT 16 #define GIT_ATTR_OPTIONS_VERSION ... typedef enum { GIT_ATTR_VALUE_UNSPECIFIED = 0, /**< The attribute has been left unspecified */ GIT_ATTR_VALUE_TRUE, /**< The attribute has been set */ GIT_ATTR_VALUE_FALSE, /**< The attribute has been unset */ GIT_ATTR_VALUE_STRING /**< This attribute has a value */ } git_attr_value_t; typedef struct { unsigned int version; unsigned int flags; git_oid *commit_id; git_oid attr_commit_id; } git_attr_options; int git_attr_get_ext( const char **value_out, git_repository *repo, git_attr_options *opts, const char *path, const char *name); git_attr_value_t git_attr_value(const char *attr); libgit2-pygit2-d86662e/pygit2/decl/blame.h000066400000000000000000000021621521317216500202120ustar00rootroot00000000000000#define GIT_BLAME_OPTIONS_VERSION ... typedef struct git_blame git_blame; typedef struct git_blame_options { unsigned int version; uint32_t flags; uint16_t min_match_characters; git_oid newest_commit; git_oid oldest_commit; size_t min_line; size_t max_line; } git_blame_options; typedef struct git_blame_hunk { size_t lines_in_hunk; git_oid final_commit_id; size_t final_start_line_number; git_signature *final_signature; git_signature *final_committer; git_oid orig_commit_id; const char *orig_path; size_t orig_start_line_number; git_signature *orig_signature; git_signature *orig_committer; const char *summary; char boundary; } git_blame_hunk; int git_blame_options_init( git_blame_options *opts, unsigned int version); uint32_t git_blame_get_hunk_count(git_blame *blame); const git_blame_hunk* git_blame_get_hunk_byindex( git_blame *blame, uint32_t index); const git_blame_hunk* git_blame_get_hunk_byline( git_blame *blame, size_t lineno); int git_blame_file( git_blame **out, git_repository *repo, const char *path, git_blame_options *options); void git_blame_free(git_blame *blame); libgit2-pygit2-d86662e/pygit2/decl/buffer.h000066400000000000000000000001601521317216500203770ustar00rootroot00000000000000typedef struct { char *ptr; size_t reserved; size_t size; } git_buf; void git_buf_dispose(git_buf *buffer); libgit2-pygit2-d86662e/pygit2/decl/callbacks.h000066400000000000000000000033061521317216500210520ustar00rootroot00000000000000extern "Python" int _certificate_check_cb( git_cert *cert, int valid, const char *host, void *payload); extern "Python" int _credentials_cb( git_credential **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload); extern "Python" int _push_update_reference_cb( const char *refname, const char *status, void *data); extern "Python" int _push_negotiation_cb( const git_push_update **updates, size_t len, void *data); extern "Python" int _remote_create_cb( git_remote **out, git_repository *repo, const char *name, const char *url, void *payload); extern "Python" int _repository_create_cb( git_repository **out, const char *path, int bare, void *payload); extern "Python" int _sideband_progress_cb( const char *str, int len, void *payload); extern "Python" int _transfer_progress_cb( const git_indexer_progress *stats, void *payload); extern "Python" int _push_transfer_progress_cb( unsigned int objects_pushed, unsigned int total_objects, size_t bytes_pushed, void *payload); extern "Python" int _update_tips_cb( const char *refname, const git_oid *a, const git_oid *b, void *data); /* Checkout */ extern "Python" int _checkout_notify_cb( git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *payload); extern "Python" void _checkout_progress_cb( const char *path, size_t completed_steps, size_t total_steps, void *payload); /* Stash */ extern "Python" int _stash_apply_progress_cb( git_stash_apply_progress_t progress, void *payload); libgit2-pygit2-d86662e/pygit2/decl/checkout.h000066400000000000000000000034421521317216500207410ustar00rootroot00000000000000typedef enum { GIT_CHECKOUT_NOTIFY_NONE = 0, GIT_CHECKOUT_NOTIFY_CONFLICT = 1, GIT_CHECKOUT_NOTIFY_DIRTY = 2, GIT_CHECKOUT_NOTIFY_UPDATED = 4, GIT_CHECKOUT_NOTIFY_UNTRACKED = 8, GIT_CHECKOUT_NOTIFY_IGNORED = 16, GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFF } git_checkout_notify_t; typedef int (*git_checkout_notify_cb)( git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *payload); typedef void (*git_checkout_progress_cb)( const char *path, size_t completed_steps, size_t total_steps, void *payload); typedef struct { size_t mkdir_calls; size_t stat_calls; size_t chmod_calls; } git_checkout_perfdata; typedef void (*git_checkout_perfdata_cb)( const git_checkout_perfdata *perfdata, void *payload); typedef struct git_checkout_options { unsigned int version; unsigned int checkout_strategy; int disable_filters; unsigned int dir_mode; unsigned int file_mode; int file_open_flags; unsigned int notify_flags; git_checkout_notify_cb notify_cb; void *notify_payload; git_checkout_progress_cb progress_cb; void *progress_payload; git_strarray paths; git_tree *baseline; git_index *baseline_index; const char *target_directory; const char *ancestor_label; const char *our_label; const char *their_label; git_checkout_perfdata_cb perfdata_cb; void *perfdata_payload; } git_checkout_options; int git_checkout_options_init( git_checkout_options *opts, unsigned int version); int git_checkout_tree( git_repository *repo, const git_object *treeish, const git_checkout_options *opts); int git_checkout_head( git_repository *repo, const git_checkout_options *opts); int git_checkout_index( git_repository *repo, git_index *index, const git_checkout_options *opts); libgit2-pygit2-d86662e/pygit2/decl/clone.h000066400000000000000000000016701521317216500202350ustar00rootroot00000000000000#define GIT_CLONE_OPTIONS_VERSION ... typedef int (*git_remote_create_cb)( git_remote **out, git_repository *repo, const char *name, const char *url, void *payload); typedef int (*git_repository_create_cb)( git_repository **out, const char *path, int bare, void *payload); typedef enum { GIT_CLONE_LOCAL_AUTO, GIT_CLONE_LOCAL, GIT_CLONE_NO_LOCAL, GIT_CLONE_LOCAL_NO_LINKS, } git_clone_local_t; typedef struct git_clone_options { unsigned int version; git_checkout_options checkout_opts; git_fetch_options fetch_opts; int bare; git_clone_local_t local; const char* checkout_branch; git_repository_create_cb repository_cb; void *repository_cb_payload; git_remote_create_cb remote_cb; void *remote_cb_payload; } git_clone_options; int git_clone_options_init( git_clone_options *opts, unsigned int version); int git_clone( git_repository **out, const char *url, const char *local_path, const git_clone_options *options); libgit2-pygit2-d86662e/pygit2/decl/commit.h000066400000000000000000000010521521317216500204170ustar00rootroot00000000000000int git_commit_amend( git_oid *id, const git_commit *commit_to_amend, const char *update_ref, const git_signature *author, const git_signature *committer, const char *message_encoding, const char *message, const git_tree *tree); int git_annotated_commit_lookup( git_annotated_commit **out, git_repository *repo, const git_oid *id); int git_annotated_commit_from_ref( git_annotated_commit **out, git_repository *repo, const struct git_reference *ref); void git_annotated_commit_free(git_annotated_commit *commit); libgit2-pygit2-d86662e/pygit2/decl/common.h000066400000000000000000000003261521317216500204220ustar00rootroot00000000000000#define GIT_PATH_MAX ... typedef enum { GIT_FEATURE_THREADS = (1 << 0), GIT_FEATURE_HTTPS = (1 << 1), GIT_FEATURE_SSH = (1 << 2), GIT_FEATURE_NSEC = (1 << 3) } git_feature_t; int git_libgit2_features(void); libgit2-pygit2-d86662e/pygit2/decl/config.h000066400000000000000000000041721521317216500204020ustar00rootroot00000000000000typedef struct git_config_iterator git_config_iterator; typedef enum { GIT_CONFIG_LEVEL_PROGRAMDATA = 1, GIT_CONFIG_LEVEL_SYSTEM = 2, GIT_CONFIG_LEVEL_XDG = 3, GIT_CONFIG_LEVEL_GLOBAL = 4, GIT_CONFIG_LEVEL_LOCAL = 5, GIT_CONFIG_LEVEL_WORKTREE = 6, GIT_CONFIG_LEVEL_APP = 7, GIT_CONFIG_HIGHEST_LEVEL = -1 } git_config_level_t; typedef struct git_config_entry { const char *name; const char *value; const char *backend_type; const char *origin_path; unsigned int include_depth; git_config_level_t level; } git_config_entry; void git_config_entry_free(git_config_entry *); void git_config_free(git_config *cfg); int git_config_get_entry( git_config_entry **out, const git_config *cfg, const char *name); int git_config_get_string(const char **out, const git_config *cfg, const char *name); int git_config_set_string(git_config *cfg, const char *name, const char *value); int git_config_set_bool(git_config *cfg, const char *name, int value); int git_config_set_int64(git_config *cfg, const char *name, int64_t value); int git_config_parse_bool(int *out, const char *value); int git_config_parse_int64(int64_t *out, const char *value); int git_config_delete_entry(git_config *cfg, const char *name); int git_config_add_file_ondisk( git_config *cfg, const char *path, git_config_level_t level, const git_repository *repo, int force); int git_config_iterator_new(git_config_iterator **out, const git_config *cfg); int git_config_next(git_config_entry **entry, git_config_iterator *iter); void git_config_iterator_free(git_config_iterator *iter); int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp); int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value); int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp); int git_config_new(git_config **out); int git_config_snapshot(git_config **out, git_config *config); int git_config_open_ondisk(git_config **out, const char *path); int git_config_find_system(git_buf *out); int git_config_find_global(git_buf *out); int git_config_find_xdg(git_buf *out); libgit2-pygit2-d86662e/pygit2/decl/describe.h000066400000000000000000000023141521317216500207110ustar00rootroot00000000000000typedef enum { GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, GIT_DESCRIBE_ALL, } git_describe_strategy_t; typedef struct git_describe_options { unsigned int version; unsigned int max_candidates_tags; unsigned int describe_strategy; const char *pattern; int only_follow_first_parent; int show_commit_oid_as_fallback; } git_describe_options; #define GIT_DESCRIBE_OPTIONS_VERSION 1 int git_describe_options_init(git_describe_options *opts, unsigned int version); typedef struct { unsigned int version; unsigned int abbreviated_size; int always_use_long_format; const char *dirty_suffix; } git_describe_format_options; #define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1 int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version); typedef struct git_describe_result git_describe_result; int git_describe_commit( git_describe_result **result, git_object *committish, git_describe_options *opts); int git_describe_workdir( git_describe_result **out, git_repository *repo, git_describe_options *opts); int git_describe_format( git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); void git_describe_result_free(git_describe_result *result); libgit2-pygit2-d86662e/pygit2/decl/diff.h000066400000000000000000000041311521317216500200400ustar00rootroot00000000000000typedef struct git_diff git_diff; typedef enum { GIT_DELTA_UNMODIFIED = 0, GIT_DELTA_ADDED = 1, GIT_DELTA_DELETED = 2, GIT_DELTA_MODIFIED = 3, GIT_DELTA_RENAMED = 4, GIT_DELTA_COPIED = 5, GIT_DELTA_IGNORED = 6, GIT_DELTA_UNTRACKED = 7, GIT_DELTA_TYPECHANGE = 8, GIT_DELTA_UNREADABLE = 9, GIT_DELTA_CONFLICTED = 10, } git_delta_t; typedef struct { git_oid id; const char *path; git_off_t size; uint32_t flags; uint16_t mode; uint16_t id_abbrev; } git_diff_file; typedef struct { git_delta_t status; uint32_t flags; uint16_t similarity; uint16_t nfiles; git_diff_file old_file; git_diff_file new_file; } git_diff_delta; typedef int (*git_diff_notify_cb)( const git_diff *diff_so_far, const git_diff_delta *delta_to_add, const char *matched_pathspec, void *payload); typedef int (*git_diff_progress_cb)( const git_diff *diff_so_far, const char *old_path, const char *new_path, void *payload); typedef struct { unsigned int version; uint32_t flags; git_submodule_ignore_t ignore_submodules; git_strarray pathspec; git_diff_notify_cb notify_cb; git_diff_progress_cb progress_cb; void *payload; uint32_t context_lines; uint32_t interhunk_lines; git_oid_t oid_type; uint16_t id_abbrev; git_off_t max_size; const char *old_prefix; const char *new_prefix; } git_diff_options; int git_diff_options_init( git_diff_options *opts, unsigned int version); typedef struct { int (*file_signature)( void **out, const git_diff_file *file, const char *fullpath, void *payload); int (*buffer_signature)( void **out, const git_diff_file *file, const char *buf, size_t buflen, void *payload); void (*free_signature)(void *sig, void *payload); int (*similarity)(int *score, void *siga, void *sigb, void *payload); void *payload; } git_diff_similarity_metric; int git_diff_tree_to_index( git_diff **diff, git_repository *repo, git_tree *old_tree, git_index *index, const git_diff_options *opts); int git_diff_index_to_workdir( git_diff **diff, git_repository *repo, git_index *index, const git_diff_options *opts); libgit2-pygit2-d86662e/pygit2/decl/errors.h000066400000000000000000000047061521317216500204540ustar00rootroot00000000000000typedef enum { GIT_OK = 0, /**< No error */ GIT_ERROR = -1, /**< Generic error */ GIT_ENOTFOUND = -3, /**< Requested object could not be found */ GIT_EEXISTS = -4, /**< Object exists preventing operation */ GIT_EAMBIGUOUS = -5, /**< More than one object matches */ GIT_EBUFS = -6, /**< Output buffer too short to hold data */ /** * GIT_EUSER is a special error that is never generated by libgit2 * code. You can return it from a callback (e.g to stop an iteration) * to know that it was generated by the callback and not by libgit2. */ GIT_EUSER = -7, GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */ GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */ GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */ GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */ GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */ GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */ GIT_ELOCKED = -14, /**< Lock file prevented operation */ GIT_EMODIFIED = -15, /**< Reference value does not match expected */ GIT_EAUTH = -16, /**< Authentication error */ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */ GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */ GIT_EPEEL = -19, /**< The requested peel operation is not possible */ GIT_EEOF = -20, /**< Unexpected EOF */ GIT_EINVALID = -21, /**< Invalid operation or input */ GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */ GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */ GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ GIT_RETRY = -32, /**< Internal only */ GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */ GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */ GIT_EAPPLYFAIL = -35, /**< Patch application failed */ GIT_EOWNER = -36, /**< The object is not owned by the current user */ GIT_TIMEOUT = -37 /**< The operation timed out */ } git_error_code; typedef struct { char *message; int klass; } git_error; const git_error * git_error_last(void); libgit2-pygit2-d86662e/pygit2/decl/filter.h000066400000000000000000000020201521317216500204100ustar00rootroot00000000000000typedef enum { GIT_FILTER_TO_WORKTREE = ..., GIT_FILTER_TO_ODB = ..., } git_filter_mode_t; typedef enum { GIT_FILTER_DEFAULT = ..., GIT_FILTER_ALLOW_UNSAFE = ..., GIT_FILTER_NO_SYSTEM_ATTRIBUTES = ..., GIT_FILTER_ATTRIBUTES_FROM_HEAD = ..., GIT_FILTER_ATTRIBUTES_FROM_COMMIT = ..., } git_filter_flag_t; int git_filter_unregister( const char *name); int git_filter_list_load( git_filter_list **filters, git_repository *repo, git_blob *blob, const char *path, git_filter_mode_t mode, uint32_t flags); int git_filter_list_contains( git_filter_list *filters, const char *name); int git_filter_list_apply_to_buffer( git_buf *out, git_filter_list *filters, const char* in, size_t in_len); int git_filter_list_apply_to_file( git_buf *out, git_filter_list *filters, git_repository *repo, const char *path); int git_filter_list_apply_to_blob( git_buf *out, git_filter_list *filters, git_blob *blob); size_t git_filter_list_length( const git_filter_list *fl); void git_filter_list_free(git_filter_list *filters); libgit2-pygit2-d86662e/pygit2/decl/graph.h000066400000000000000000000002001521317216500202220ustar00rootroot00000000000000int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream); libgit2-pygit2-d86662e/pygit2/decl/index.h000066400000000000000000000050261521317216500202430ustar00rootroot00000000000000typedef struct { int32_t seconds; uint32_t nanoseconds; } git_index_time; typedef struct git_index_entry { git_index_time ctime; git_index_time mtime; uint32_t dev; uint32_t ino; uint32_t mode; uint32_t uid; uint32_t gid; uint32_t file_size; git_oid id; uint16_t flags; uint16_t flags_extended; const char *path; } git_index_entry; typedef int (*git_index_matched_path_cb)( const char *path, const char *matched_pathspec, void *payload); void git_index_free(git_index *index); int git_index_open(git_index **out, const char *index_path); int git_index_read(git_index *index, int force); int git_index_write(git_index *index); size_t git_index_entrycount(const git_index *index); int git_index_find(size_t *at_pos, git_index *index, const char *path); int git_index_add_bypath(git_index *index, const char *path); int git_index_add(git_index *index, const git_index_entry *source_entry); int git_index_remove(git_index *index, const char *path, int stage); int git_index_remove_directory(git_index *index, const char *path, int stage); int git_index_read_tree(git_index *index, const git_tree *tree); int git_index_clear(git_index *index); int git_index_write_tree(git_oid *out, git_index *index); int git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo); const git_index_entry * git_index_get_bypath( git_index *index, const char *path, int stage); const git_index_entry * git_index_get_byindex( git_index *index, size_t n); int git_index_add_all( git_index *index, const git_strarray *pathspec, unsigned int flags, git_index_matched_path_cb callback, void *payload); int git_index_remove_all( git_index *index, const git_strarray *pathspec, git_index_matched_path_cb callback, void *payload); int git_index_has_conflicts(const git_index *index); void git_index_conflict_iterator_free( git_index_conflict_iterator *iterator); int git_index_conflict_iterator_new( git_index_conflict_iterator **iterator_out, git_index *index); int git_index_conflict_add( git_index *index, const git_index_entry *ancestor_entry, const git_index_entry *our_entry, const git_index_entry *their_entry); int git_index_conflict_get( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index *index, const char *path); int git_index_conflict_next( const git_index_entry **ancestor_out, const git_index_entry **our_out, const git_index_entry **their_out, git_index_conflict_iterator *iterator); int git_index_conflict_remove(git_index *index, const char *path); libgit2-pygit2-d86662e/pygit2/decl/indexer.h000066400000000000000000000005441521317216500205720ustar00rootroot00000000000000typedef struct git_indexer_progress { unsigned int total_objects; unsigned int indexed_objects; unsigned int received_objects; unsigned int local_objects; unsigned int total_deltas; unsigned int indexed_deltas; size_t received_bytes; } git_indexer_progress; typedef int (*git_indexer_progress_cb)(const git_indexer_progress *stats, void *payload); libgit2-pygit2-d86662e/pygit2/decl/merge.h000066400000000000000000000046231521317216500202350ustar00rootroot00000000000000#define GIT_MERGE_OPTIONS_VERSION 1 typedef enum { GIT_MERGE_FIND_RENAMES = 1, GIT_MERGE_FAIL_ON_CONFLICT = 2, GIT_MERGE_SKIP_REUC = 4, GIT_MERGE_NO_RECURSIVE = 8, GIT_MERGE_VIRTUAL_BASE = (1 << 4), } git_merge_flag_t; typedef enum { GIT_MERGE_FILE_FAVOR_NORMAL = 0, GIT_MERGE_FILE_FAVOR_OURS = 1, GIT_MERGE_FILE_FAVOR_THEIRS = 2, GIT_MERGE_FILE_FAVOR_UNION = 3, } git_merge_file_favor_t; typedef enum { GIT_MERGE_FILE_DEFAULT = 0, GIT_MERGE_FILE_STYLE_MERGE = 1, GIT_MERGE_FILE_STYLE_DIFF3 = 2, GIT_MERGE_FILE_SIMPLIFY_ALNUM = 4, GIT_MERGE_FILE_IGNORE_WHITESPACE = 8, GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = 16, GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = 32, GIT_MERGE_FILE_DIFF_PATIENCE = 64, GIT_MERGE_FILE_DIFF_MINIMAL = 128, GIT_MERGE_FILE_STYLE_ZDIFF3 = (1 << 8), GIT_MERGE_FILE_ACCEPT_CONFLICTS = (1 << 9), } git_merge_file_flag_t; typedef struct { unsigned int version; git_merge_flag_t flags; unsigned int rename_threshold; unsigned int target_limit; git_diff_similarity_metric *metric; unsigned int recursion_limit; const char *default_driver; git_merge_file_favor_t file_favor; git_merge_file_flag_t file_flags; } git_merge_options; typedef struct { unsigned int automergeable; const char *path; unsigned int mode; const char *ptr; size_t len; } git_merge_file_result; typedef struct { unsigned int version; const char *ancestor_label; const char *our_label; const char *their_label; git_merge_file_favor_t favor; git_merge_file_flag_t flags; unsigned short marker_size; } git_merge_file_options; int git_merge_options_init( git_merge_options *opts, unsigned int version); int git_merge_commits( git_index **out, git_repository *repo, const git_commit *our_commit, const git_commit *their_commit, const git_merge_options *opts); int git_merge_trees( git_index **out, git_repository *repo, const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, const git_merge_options *opts); int git_merge_file_from_index( git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts); int git_merge( git_repository *repo, const git_annotated_commit **their_heads, size_t their_heads_len, const git_merge_options *merge_opts, const git_checkout_options *checkout_opts); void git_merge_file_result_free(git_merge_file_result *result); libgit2-pygit2-d86662e/pygit2/decl/net.h000066400000000000000000000001241521317216500177140ustar00rootroot00000000000000typedef enum { GIT_DIRECTION_FETCH = 0, GIT_DIRECTION_PUSH = 1 } git_direction; libgit2-pygit2-d86662e/pygit2/decl/oid.h000066400000000000000000000005631521317216500177100ustar00rootroot00000000000000typedef enum { GIT_OID_SHA1 = 1, /**< SHA1 */ } git_oid_t; typedef struct git_oid { unsigned char id[20]; } git_oid; // This should go to net.h but due to h_order in _run.py, ffi won't compile properly. typedef struct git_remote_head { int local; git_oid oid; git_oid loid; char *name; char *symref_target; } git_remote_head; libgit2-pygit2-d86662e/pygit2/decl/options.h000066400000000000000000000027301521317216500206260ustar00rootroot00000000000000typedef enum { GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE, GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, GIT_OPT_GET_SEARCH_PATH, GIT_OPT_SET_SEARCH_PATH, GIT_OPT_SET_CACHE_OBJECT_LIMIT, GIT_OPT_SET_CACHE_MAX_SIZE, GIT_OPT_ENABLE_CACHING, GIT_OPT_GET_CACHED_MEMORY, GIT_OPT_GET_TEMPLATE_PATH, GIT_OPT_SET_TEMPLATE_PATH, GIT_OPT_SET_SSL_CERT_LOCATIONS, GIT_OPT_SET_USER_AGENT, GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, GIT_OPT_SET_SSL_CIPHERS, GIT_OPT_GET_USER_AGENT, GIT_OPT_ENABLE_OFS_DELTA, GIT_OPT_ENABLE_FSYNC_GITDIR, GIT_OPT_GET_WINDOWS_SHAREMODE, GIT_OPT_SET_WINDOWS_SHAREMODE, GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, GIT_OPT_SET_ALLOCATOR, GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, GIT_OPT_GET_PACK_MAX_OBJECTS, GIT_OPT_SET_PACK_MAX_OBJECTS, GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, GIT_OPT_GET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_ODB_PACKED_PRIORITY, GIT_OPT_SET_ODB_LOOSE_PRIORITY, GIT_OPT_GET_EXTENSIONS, GIT_OPT_SET_EXTENSIONS, GIT_OPT_GET_OWNER_VALIDATION, GIT_OPT_SET_OWNER_VALIDATION, GIT_OPT_GET_HOMEDIR, GIT_OPT_SET_HOMEDIR, GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, GIT_OPT_GET_SERVER_CONNECT_TIMEOUT, GIT_OPT_SET_SERVER_TIMEOUT, GIT_OPT_GET_SERVER_TIMEOUT, GIT_OPT_SET_USER_AGENT_PRODUCT, GIT_OPT_GET_USER_AGENT_PRODUCT, GIT_OPT_ADD_SSL_X509_CERT } git_libgit2_opt_t; int git_libgit2_opts(int option, ...);libgit2-pygit2-d86662e/pygit2/decl/pack.h000066400000000000000000000013541521317216500200520ustar00rootroot00000000000000typedef int (*git_packbuilder_progress)( int stage, uint32_t current, uint32_t total, void *payload); int git_packbuilder_new(git_packbuilder **out, git_repository *repo); void git_packbuilder_free(git_packbuilder *pb); int git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, const char *name); int git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name); size_t git_packbuilder_object_count(git_packbuilder *pb); int git_packbuilder_write(git_packbuilder *pb, const char *path, unsigned int mode, git_indexer_progress_cb progress_cb, void *progress_cb_payload); uint32_t git_packbuilder_written(git_packbuilder *pb); unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n); libgit2-pygit2-d86662e/pygit2/decl/proxy.h000066400000000000000000000006331521317216500203140ustar00rootroot00000000000000#define GIT_PROXY_OPTIONS_VERSION ... typedef enum { GIT_PROXY_NONE, GIT_PROXY_AUTO, GIT_PROXY_SPECIFIED, } git_proxy_t; typedef struct { unsigned int version; git_proxy_t type; const char *url; git_credential_acquire_cb credentials; git_transport_certificate_check_cb certificate_check; void *payload; } git_proxy_options; int git_proxy_options_init(git_proxy_options *opts, unsigned int version); libgit2-pygit2-d86662e/pygit2/decl/refspec.h000066400000000000000000000011471521317216500205630ustar00rootroot00000000000000const char * git_refspec_src(const git_refspec *refspec); const char * git_refspec_dst(const git_refspec *refspec); int git_refspec_force(const git_refspec *refspec); const char * git_refspec_string(const git_refspec *refspec); git_direction git_refspec_direction(const git_refspec *spec); int git_refspec_src_matches(const git_refspec *refspec, const char *refname); int git_refspec_dst_matches(const git_refspec *refspec, const char *refname); int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name); int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name); libgit2-pygit2-d86662e/pygit2/decl/remote.h000066400000000000000000000120171521317216500204250ustar00rootroot00000000000000#define GIT_FETCH_OPTIONS_VERSION ... #define GIT_PUSH_OPTIONS_VERSION ... #define GIT_REMOTE_CALLBACKS_VERSION ... typedef enum { GIT_REMOTE_REDIRECT_NONE, GIT_REMOTE_REDIRECT_INITIAL, GIT_REMOTE_REDIRECT_ALL } git_remote_redirect_t; typedef enum git_remote_completion_t { GIT_REMOTE_COMPLETION_DOWNLOAD, GIT_REMOTE_COMPLETION_INDEXING, GIT_REMOTE_COMPLETION_ERROR, } git_remote_completion_t; typedef int (*git_push_transfer_progress_cb)( unsigned int current, unsigned int total, size_t bytes, void* payload); typedef struct { char *src_refname; char *dst_refname; git_oid src; git_oid dst; } git_push_update; typedef int (*git_push_negotiation)(const git_push_update **updates, size_t len, void *payload); typedef int (*git_push_update_reference_cb)(const char *refname, const char *status, void *data); typedef int (*git_remote_ready_cb)(git_remote *remote, int direction, void *payload); typedef int (*git_url_resolve_cb)(git_buf *url_resolved, const char *url, int direction, void *payload); struct git_remote_callbacks { unsigned int version; git_transport_message_cb sideband_progress; int (*completion)(git_remote_completion_t type, void *data); git_credential_acquire_cb credentials; git_transport_certificate_check_cb certificate_check; git_indexer_progress_cb transfer_progress; int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); git_packbuilder_progress pack_progress; git_push_transfer_progress_cb push_transfer_progress; git_push_update_reference_cb push_update_reference; git_push_negotiation push_negotiation; git_transport_cb transport; git_remote_ready_cb remote_ready; void *payload; git_url_resolve_cb resolve_url; int (*update_refs)(const char *refname, const git_oid *a, const git_oid *b, git_refspec *spec, void *data); }; typedef struct { unsigned int version; unsigned int pb_parallelism; git_remote_callbacks callbacks; git_proxy_options proxy_opts; git_remote_redirect_t follow_redirects; git_strarray custom_headers; git_strarray remote_push_options; } git_push_options; int git_push_options_init( git_push_options *opts, unsigned int version); typedef enum { GIT_FETCH_PRUNE_UNSPECIFIED, GIT_FETCH_PRUNE, GIT_FETCH_NO_PRUNE, } git_fetch_prune_t; typedef enum { GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = 0, GIT_REMOTE_DOWNLOAD_TAGS_AUTO, GIT_REMOTE_DOWNLOAD_TAGS_NONE, GIT_REMOTE_DOWNLOAD_TAGS_ALL, } git_remote_autotag_option_t; typedef struct { int version; git_remote_callbacks callbacks; git_fetch_prune_t prune; unsigned int update_fetchhead; git_remote_autotag_option_t download_tags; git_proxy_options proxy_opts; int depth; git_remote_redirect_t follow_redirects; git_strarray custom_headers; } git_fetch_options; int git_fetch_options_init( git_fetch_options *opts, unsigned int version); int git_remote_list(git_strarray *out, git_repository *repo); int git_remote_lookup(git_remote **out, git_repository *repo, const char *name); int git_remote_create( git_remote **out, git_repository *repo, const char *name, const char *url); int git_remote_create_with_fetchspec( git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch); int git_remote_create_anonymous( git_remote **out, git_repository *repo, const char *url); int git_remote_delete(git_repository *repo, const char *name); const char * git_remote_name(const git_remote *remote); int git_remote_rename( git_strarray *problems, git_repository *repo, const char *name, const char *new_name); const char * git_remote_url(const git_remote *remote); int git_remote_set_url(git_repository *repo, const char *remote, const char* url); const char * git_remote_pushurl(const git_remote *remote); int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url); int git_remote_fetch( git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts, const char *reflog_message); int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks); int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts); const git_indexer_progress * git_remote_stats(git_remote *remote); int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec); int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec); int git_remote_init_callbacks( git_remote_callbacks *opts, unsigned int version); size_t git_remote_refspec_count(const git_remote *remote); const git_refspec * git_remote_get_refspec(const git_remote *remote, size_t n); int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote); int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote); void git_remote_free(git_remote *remote); int git_remote_connect( git_remote *remote, int direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy_opts, const git_strarray *custom_headers); int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote); libgit2-pygit2-d86662e/pygit2/decl/repository.h000066400000000000000000000057371521317216500213640ustar00rootroot00000000000000#define GIT_REPOSITORY_INIT_OPTIONS_VERSION ... void git_repository_free(git_repository *repo); int git_repository_state_cleanup(git_repository *repo); int git_repository_config(git_config **out, git_repository *repo); int git_repository_config_snapshot(git_config **out, git_repository *repo); typedef enum { GIT_REPOSITORY_INIT_BARE = 1, GIT_REPOSITORY_INIT_NO_REINIT = 2, GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = 4, GIT_REPOSITORY_INIT_MKDIR = 8, GIT_REPOSITORY_INIT_MKPATH = 16, GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = 32, GIT_REPOSITORY_INIT_RELATIVE_GITLINK = 64, } git_repository_init_flag_t; typedef enum { GIT_REPOSITORY_INIT_SHARED_UMASK = 0, GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775, GIT_REPOSITORY_INIT_SHARED_ALL = 0002777, } git_repository_init_mode_t; typedef enum { GIT_REPOSITORY_STATE_NONE, GIT_REPOSITORY_STATE_MERGE, GIT_REPOSITORY_STATE_REVERT, GIT_REPOSITORY_STATE_REVERT_SEQUENCE, GIT_REPOSITORY_STATE_CHERRYPICK, GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE, GIT_REPOSITORY_STATE_BISECT, GIT_REPOSITORY_STATE_REBASE, GIT_REPOSITORY_STATE_REBASE_INTERACTIVE, GIT_REPOSITORY_STATE_REBASE_MERGE, GIT_REPOSITORY_STATE_APPLY_MAILBOX, GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE } git_repository_state_t; typedef struct { unsigned int version; uint32_t flags; uint32_t mode; const char *workdir_path; const char *description; const char *template_path; const char *initial_head; const char *origin_url; } git_repository_init_options; int git_repository_init_options_init( git_repository_init_options *opts, unsigned int version); int git_repository_init( git_repository **out, const char *path, unsigned is_bare); int git_repository_init_ext( git_repository **out, const char *repo_path, git_repository_init_options *opts); typedef enum { GIT_REPOSITORY_OPEN_NO_SEARCH = 1, GIT_REPOSITORY_OPEN_CROSS_FS = 2, GIT_REPOSITORY_OPEN_BARE = 4, GIT_REPOSITORY_OPEN_NO_DOTGIT = 8, GIT_REPOSITORY_OPEN_FROM_ENV = 16, } git_repository_open_flag_t; int git_repository_open_ext( git_repository **out, const char *path, unsigned int flags, const char *ceiling_dirs); int git_repository_set_head( git_repository* repo, const char* refname); int git_repository_set_head_detached( git_repository* repo, const git_oid* committish); int git_repository_hashfile(git_oid *out, git_repository *repo, const char *path, git_object_t type, const char *as_path); int git_repository_ident(const char **name, const char **email, const git_repository *repo); int git_repository_set_ident(git_repository *repo, const char *name, const char *email); int git_repository_index(git_index **out, git_repository *repo); git_repository_state_t git_repository_state(git_repository *repo); int git_repository_message(git_buf *out, git_repository *repo); int git_repository_message_remove(git_repository *repo); int git_repository_submodule_cache_all(git_repository *repo); int git_repository_submodule_cache_clear(git_repository *repo); libgit2-pygit2-d86662e/pygit2/decl/revert.h000066400000000000000000000007371521317216500204470ustar00rootroot00000000000000#define GIT_REVERT_OPTIONS_VERSION ... typedef struct { unsigned int version; unsigned int mainline; git_merge_options merge_opts; git_checkout_options checkout_opts; } git_revert_options; int git_revert( git_repository *repo, git_commit *commit, const git_revert_options *given_opts); int git_revert_commit( git_index **out, git_repository *repo, git_commit *revert_commit, git_commit *our_commit, unsigned int mainline, const git_merge_options *merge_options); libgit2-pygit2-d86662e/pygit2/decl/stash.h000066400000000000000000000041251521317216500202550ustar00rootroot00000000000000#define GIT_STASH_APPLY_OPTIONS_VERSION 1 #define GIT_STASH_SAVE_OPTIONS_VERSION ... typedef int (*git_stash_cb)( size_t index, const char* message, const git_oid *stash_id, void *payload); typedef enum { GIT_STASH_APPLY_PROGRESS_NONE = 0, GIT_STASH_APPLY_PROGRESS_LOADING_STASH, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED, GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED, GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED, GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED, GIT_STASH_APPLY_PROGRESS_DONE, } git_stash_apply_progress_t; typedef int (*git_stash_apply_progress_cb)( git_stash_apply_progress_t progress, void *payload); typedef enum { GIT_STASH_DEFAULT = 0, GIT_STASH_KEEP_INDEX = 1, GIT_STASH_INCLUDE_UNTRACKED = 2, GIT_STASH_INCLUDE_IGNORED = 4, GIT_STASH_KEEP_ALL = 8, } git_stash_flags; typedef enum { GIT_STASH_APPLY_DEFAULT = 0, GIT_STASH_APPLY_REINSTATE_INDEX = 1, } git_stash_apply_flags; typedef struct git_stash_apply_options { unsigned int version; git_stash_apply_flags flags; git_checkout_options checkout_options; git_stash_apply_progress_cb progress_cb; void *progress_payload; } git_stash_apply_options; int git_stash_save( git_oid *out, git_repository *repo, const git_signature *stasher, const char *message, uint32_t flags); int git_stash_apply_options_init( git_stash_apply_options *opts, unsigned int version); int git_stash_apply( git_repository *repo, size_t index, const git_stash_apply_options *options); typedef struct git_stash_save_options { unsigned int version; uint32_t flags; const git_signature *stasher; const char *message; git_strarray paths; } git_stash_save_options; int git_stash_save_options_init( git_stash_save_options *opts, unsigned int version); int git_stash_save_with_opts( git_oid *out, git_repository *repo, const git_stash_save_options *opts); int git_stash_foreach( git_repository *repo, git_stash_cb callback, void *payload); int git_stash_drop( git_repository *repo, size_t index); int git_stash_pop( git_repository *repo, size_t index, const git_stash_apply_options *options); libgit2-pygit2-d86662e/pygit2/decl/strarray.h000066400000000000000000000001771521317216500210050ustar00rootroot00000000000000typedef struct git_strarray { char **strings; size_t count; } git_strarray; void git_strarray_dispose(git_strarray *array); libgit2-pygit2-d86662e/pygit2/decl/submodule.h000066400000000000000000000031001521317216500211220ustar00rootroot00000000000000#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION ... typedef struct git_submodule_update_options { unsigned int version; git_checkout_options checkout_opts; git_fetch_options fetch_opts; int allow_fetch; } git_submodule_update_options; int git_submodule_update_options_init( git_submodule_update_options *opts, unsigned int version); int git_submodule_add_setup( git_submodule **out, git_repository *repo, const char *url, const char *path, int use_gitlink); int git_submodule_clone( git_repository **out, git_submodule *submodule, const git_submodule_update_options *opts); int git_submodule_add_finalize(git_submodule *submodule); int git_submodule_update(git_submodule *submodule, int init, git_submodule_update_options *options); int git_submodule_lookup( git_submodule **out, git_repository *repo, const char *name); void git_submodule_free(git_submodule *submodule); int git_submodule_open(git_repository **repo, git_submodule *submodule); int git_submodule_init(git_submodule *submodule, int overwrite); int git_submodule_reload(git_submodule *submodule, int force); const char * git_submodule_name(git_submodule *submodule); const char * git_submodule_path(git_submodule *submodule); const char * git_submodule_url(git_submodule *submodule); const char * git_submodule_branch(git_submodule *submodule); const git_oid * git_submodule_head_id(git_submodule *submodule); int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore); int git_submodule_set_url(git_repository *repo, const char *name, const char *url); libgit2-pygit2-d86662e/pygit2/decl/transaction.h000066400000000000000000000012671521317216500214640ustar00rootroot00000000000000int git_transaction_new(git_transaction **out, git_repository *repo); int git_transaction_lock_ref(git_transaction *tx, const char *refname); int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg); int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg); int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog); int git_transaction_remove(git_transaction *tx, const char *refname); int git_transaction_commit(git_transaction *tx); void git_transaction_free(git_transaction *tx); libgit2-pygit2-d86662e/pygit2/decl/transport.h000066400000000000000000000027601521317216500211720ustar00rootroot00000000000000typedef struct git_credential git_credential; typedef enum { GIT_CREDENTIAL_USERPASS_PLAINTEXT = (1u << 0), GIT_CREDENTIAL_SSH_KEY = (1u << 1), GIT_CREDENTIAL_SSH_CUSTOM = (1u << 2), GIT_CREDENTIAL_DEFAULT = (1u << 3), GIT_CREDENTIAL_SSH_INTERACTIVE = (1u << 4), GIT_CREDENTIAL_USERNAME = (1u << 5), GIT_CREDENTIAL_SSH_MEMORY = (1u << 6), } git_credential_t; typedef enum { GIT_CERT_SSH_MD5 = 1, GIT_CERT_SSH_SHA1 = 2, } git_cert_ssh_t; typedef struct { git_cert parent; git_cert_ssh_t type; unsigned char hash_md5[16]; unsigned char hash_sha1[20]; } git_cert_hostkey; typedef struct { git_cert parent; void *data; size_t len; } git_cert_x509; typedef int (*git_credential_acquire_cb)( git_credential **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload); typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); int git_credential_username_new(git_credential **out, const char *username); int git_credential_userpass_plaintext_new( git_credential **out, const char *username, const char *password); int git_credential_ssh_key_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase); int git_credential_ssh_key_from_agent( git_credential **out, const char *username); int git_credential_ssh_key_memory_new( git_credential **out, const char *username, const char *publickey, const char *privatekey, const char *passphrase); libgit2-pygit2-d86662e/pygit2/decl/types.h000066400000000000000000000037741521317216500203100ustar00rootroot00000000000000typedef struct git_blob git_blob; typedef struct git_commit git_commit; typedef struct git_annotated_commit git_annotated_commit; typedef struct git_config git_config; typedef struct git_filter_list git_filter_list; typedef struct git_index git_index; typedef struct git_index_conflict_iterator git_index_conflict_iterator; typedef struct git_object git_object; typedef struct git_refspec git_refspec; typedef struct git_remote git_remote; typedef struct git_remote_callbacks git_remote_callbacks; typedef struct git_repository git_repository; typedef struct git_submodule git_submodule; typedef struct git_transport git_transport; typedef struct git_tree git_tree; typedef struct git_packbuilder git_packbuilder; typedef struct git_transaction git_transaction; typedef struct git_reflog git_reflog; typedef int64_t git_off_t; typedef int64_t git_time_t; typedef enum { GIT_REFERENCE_INVALID = 0, GIT_REFERENCE_DIRECT = 1, GIT_REFERENCE_SYMBOLIC = 2, GIT_REFERENCE_ALL = 3, } git_reference_t; typedef struct git_time { git_time_t time; int offset; char sign; } git_time; typedef struct git_signature { char *name; char *email; git_time when; } git_signature; typedef enum git_cert_t { GIT_CERT_NONE, GIT_CERT_X509, GIT_CERT_HOSTKEY_LIBSSH2, GIT_CERT_STRARRAY, } git_cert_t; typedef struct { git_cert_t cert_type; } git_cert; typedef int (*git_transport_message_cb)(const char *str, int len, void *payload); typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload); typedef enum { GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1, GIT_SUBMODULE_IGNORE_NONE = 1, GIT_SUBMODULE_IGNORE_UNTRACKED = 2, GIT_SUBMODULE_IGNORE_DIRTY = 3, GIT_SUBMODULE_IGNORE_ALL = 4, } git_submodule_ignore_t; typedef enum { GIT_OBJECT_ANY = ..., GIT_OBJECT_INVALID = ..., GIT_OBJECT_COMMIT = ..., GIT_OBJECT_TREE = ..., GIT_OBJECT_BLOB = ..., GIT_OBJECT_TAG = ..., GIT_OBJECT_OFS_DELTA = ..., GIT_OBJECT_REF_DELTA = ..., } git_object_t; libgit2-pygit2-d86662e/pygit2/enums.py000066400000000000000000001322521521317216500175570ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from enum import IntEnum, IntFlag from . import _pygit2, options from .ffi import C class ApplyLocation(IntEnum): """Possible application locations for patches""" WORKDIR = _pygit2.GIT_APPLY_LOCATION_WORKDIR """ Apply the patch to the workdir, leaving the index untouched. This is the equivalent of `git apply` with no location argument. """ INDEX = _pygit2.GIT_APPLY_LOCATION_INDEX """ Apply the patch to the index, leaving the working directory untouched. This is the equivalent of `git apply --cached`. """ BOTH = _pygit2.GIT_APPLY_LOCATION_BOTH """ Apply the patch to both the working directory and the index. This is the equivalent of `git apply --index`. """ class AttrCheck(IntFlag): FILE_THEN_INDEX = C.GIT_ATTR_CHECK_FILE_THEN_INDEX INDEX_THEN_FILE = C.GIT_ATTR_CHECK_INDEX_THEN_FILE INDEX_ONLY = C.GIT_ATTR_CHECK_INDEX_ONLY NO_SYSTEM = C.GIT_ATTR_CHECK_NO_SYSTEM INCLUDE_HEAD = C.GIT_ATTR_CHECK_INCLUDE_HEAD INCLUDE_COMMIT = C.GIT_ATTR_CHECK_INCLUDE_COMMIT class BlameFlag(IntFlag): NORMAL = _pygit2.GIT_BLAME_NORMAL 'Normal blame, the default' TRACK_COPIES_SAME_FILE = _pygit2.GIT_BLAME_TRACK_COPIES_SAME_FILE 'Not yet implemented and reserved for future use (as of libgit2 1.9.0).' TRACK_COPIES_SAME_COMMIT_MOVES = _pygit2.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES 'Not yet implemented and reserved for future use (as of libgit2 1.9.0).' TRACK_COPIES_SAME_COMMIT_COPIES = _pygit2.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES 'Not yet implemented and reserved for future use (as of libgit2 1.9.0).' TRACK_COPIES_ANY_COMMIT_COPIES = _pygit2.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES 'Not yet implemented and reserved for future use (as of libgit2 1.9.0).' FIRST_PARENT = _pygit2.GIT_BLAME_FIRST_PARENT 'Restrict the search of commits to those reachable following only the first parents.' USE_MAILMAP = _pygit2.GIT_BLAME_USE_MAILMAP """ Use mailmap file to map author and committer names and email addresses to canonical real names and email addresses. The mailmap will be read from the working directory, or HEAD in a bare repository. """ IGNORE_WHITESPACE = _pygit2.GIT_BLAME_IGNORE_WHITESPACE 'Ignore whitespace differences' class BlobFilter(IntFlag): CHECK_FOR_BINARY = _pygit2.GIT_BLOB_FILTER_CHECK_FOR_BINARY 'Do not apply filters to binary files.' NO_SYSTEM_ATTRIBUTES = _pygit2.GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES 'Filters will not load configuration from the system-wide `gitattributes` in `/etc` (or system equivalent).' ATTRIBUTES_FROM_HEAD = _pygit2.GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD 'Load filters from a `.gitattributes` file in the HEAD commit.' ATTRIBUTES_FROM_COMMIT = _pygit2.GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT 'Load filters from a `.gitattributes` file in the specified commit.' class BranchType(IntFlag): LOCAL = _pygit2.GIT_BRANCH_LOCAL REMOTE = _pygit2.GIT_BRANCH_REMOTE ALL = _pygit2.GIT_BRANCH_ALL class CheckoutNotify(IntFlag): """ Checkout notification flags Checkout will invoke an options notification callback (`CheckoutCallbacks.checkout_notify`) for certain cases - you pick which ones via `CheckoutCallbacks.checkout_notify_flags`. """ NONE = C.GIT_CHECKOUT_NOTIFY_NONE CONFLICT = C.GIT_CHECKOUT_NOTIFY_CONFLICT 'Invokes checkout on conflicting paths.' DIRTY = C.GIT_CHECKOUT_NOTIFY_DIRTY """ Notifies about "dirty" files, i.e. those that do not need an update but no longer match the baseline. Core git displays these files when checkout runs, but won't stop the checkout. """ UPDATED = C.GIT_CHECKOUT_NOTIFY_UPDATED 'Sends notification for any file changed.' UNTRACKED = C.GIT_CHECKOUT_NOTIFY_UNTRACKED 'Notifies about untracked files.' IGNORED = C.GIT_CHECKOUT_NOTIFY_IGNORED 'Notifies about ignored files.' ALL = C.GIT_CHECKOUT_NOTIFY_ALL class CheckoutStrategy(IntFlag): NONE = _pygit2.GIT_CHECKOUT_NONE 'Dry run, no actual updates' SAFE = _pygit2.GIT_CHECKOUT_SAFE """ Allow safe updates that cannot overwrite uncommitted data. If the uncommitted changes don't conflict with the checked out files, the checkout will still proceed, leaving the changes intact. Mutually exclusive with FORCE. FORCE takes precedence over SAFE. """ FORCE = _pygit2.GIT_CHECKOUT_FORCE """ Allow all updates to force working directory to look like index. Mutually exclusive with SAFE. FORCE takes precedence over SAFE. """ RECREATE_MISSING = _pygit2.GIT_CHECKOUT_RECREATE_MISSING """ Allow checkout to recreate missing files """ ALLOW_CONFLICTS = _pygit2.GIT_CHECKOUT_ALLOW_CONFLICTS """ Allow checkout to make safe updates even if conflicts are found """ REMOVE_UNTRACKED = _pygit2.GIT_CHECKOUT_REMOVE_UNTRACKED """ Remove untracked files not in index (that are not ignored) """ REMOVE_IGNORED = _pygit2.GIT_CHECKOUT_REMOVE_IGNORED """ Remove ignored files not in index """ UPDATE_ONLY = _pygit2.GIT_CHECKOUT_UPDATE_ONLY """ Only update existing files, don't create new ones """ DONT_UPDATE_INDEX = _pygit2.GIT_CHECKOUT_DONT_UPDATE_INDEX """ Normally checkout updates index entries as it goes; this stops that. Implies `DONT_WRITE_INDEX`. """ NO_REFRESH = _pygit2.GIT_CHECKOUT_NO_REFRESH """ Don't refresh index/config/etc before doing checkout """ SKIP_UNMERGED = _pygit2.GIT_CHECKOUT_SKIP_UNMERGED """ Allow checkout to skip unmerged files """ USE_OURS = _pygit2.GIT_CHECKOUT_USE_OURS """ For unmerged files, checkout stage 2 from index """ USE_THEIRS = _pygit2.GIT_CHECKOUT_USE_THEIRS """ For unmerged files, checkout stage 3 from index """ DISABLE_PATHSPEC_MATCH = _pygit2.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH """ Treat pathspec as simple list of exact match file paths """ SKIP_LOCKED_DIRECTORIES = _pygit2.GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES """ Ignore directories in use, they will be left empty """ DONT_OVERWRITE_IGNORED = _pygit2.GIT_CHECKOUT_DONT_OVERWRITE_IGNORED """ Don't overwrite ignored files that exist in the checkout target """ CONFLICT_STYLE_MERGE = _pygit2.GIT_CHECKOUT_CONFLICT_STYLE_MERGE """ Write normal merge files for conflicts """ CONFLICT_STYLE_DIFF3 = _pygit2.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 """ Include common ancestor data in diff3 format files for conflicts """ DONT_REMOVE_EXISTING = _pygit2.GIT_CHECKOUT_DONT_REMOVE_EXISTING """ Don't overwrite existing files or folders """ DONT_WRITE_INDEX = _pygit2.GIT_CHECKOUT_DONT_WRITE_INDEX """ Normally checkout writes the index upon completion; this prevents that. """ DRY_RUN = _pygit2.GIT_CHECKOUT_DRY_RUN """ Show what would be done by a checkout. Stop after sending notifications; don't update the working directory or index. """ CONFLICT_STYLE_ZDIFF3 = _pygit2.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 """ Include common ancestor data in zdiff3 format for conflicts """ class ConfigLevel(IntEnum): """ Priority level of a config file. These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git.git. """ PROGRAMDATA = _pygit2.GIT_CONFIG_LEVEL_PROGRAMDATA 'System-wide on Windows, for compatibility with portable git' SYSTEM = _pygit2.GIT_CONFIG_LEVEL_SYSTEM 'System-wide configuration file; /etc/gitconfig on Linux systems' XDG = _pygit2.GIT_CONFIG_LEVEL_XDG 'XDG compatible configuration file; typically ~/.config/git/config' GLOBAL = _pygit2.GIT_CONFIG_LEVEL_GLOBAL 'User-specific configuration file (also called Global configuration file); typically ~/.gitconfig' LOCAL = _pygit2.GIT_CONFIG_LEVEL_LOCAL 'Repository specific configuration file; $WORK_DIR/.git/config on non-bare repos' WORKTREE = _pygit2.GIT_CONFIG_LEVEL_WORKTREE 'Worktree specific configuration file; $GIT_DIR/config.worktree' APP = _pygit2.GIT_CONFIG_LEVEL_APP 'Application specific configuration file; freely defined by applications' HIGHEST_LEVEL = _pygit2.GIT_CONFIG_HIGHEST_LEVEL """Represents the highest level available config file (i.e. the most specific config file available that actually is loaded)""" class CredentialType(IntFlag): """ Supported credential types. This represents the various types of authentication methods supported by the library. """ USERPASS_PLAINTEXT = C.GIT_CREDENTIAL_USERPASS_PLAINTEXT 'A vanilla user/password request' SSH_KEY = C.GIT_CREDENTIAL_SSH_KEY 'An SSH key-based authentication request' SSH_CUSTOM = C.GIT_CREDENTIAL_SSH_CUSTOM 'An SSH key-based authentication request, with a custom signature' DEFAULT = C.GIT_CREDENTIAL_DEFAULT 'An NTLM/Negotiate-based authentication request.' SSH_INTERACTIVE = C.GIT_CREDENTIAL_SSH_INTERACTIVE 'An SSH interactive authentication request.' USERNAME = C.GIT_CREDENTIAL_USERNAME """ Username-only authentication request. Used as a pre-authentication step if the underlying transport (eg. SSH, with no username in its URL) does not know which username to use. """ SSH_MEMORY = C.GIT_CREDENTIAL_SSH_MEMORY """ An SSH key-based authentication request. Allows credentials to be read from memory instead of files. Note that because of differences in crypto backend support, it might not be functional. """ class DeltaStatus(IntEnum): """ What type of change is described by a DiffDelta? `RENAMED` and `COPIED` will only show up if you run `find_similar()` on the Diff object. `TYPECHANGE` only shows up given `INCLUDE_TYPECHANGE` in the DiffOption option flags (otherwise type changes will be split into ADDED / DELETED pairs). """ UNMODIFIED = _pygit2.GIT_DELTA_UNMODIFIED 'no changes' ADDED = _pygit2.GIT_DELTA_ADDED 'entry does not exist in old version' DELETED = _pygit2.GIT_DELTA_DELETED 'entry does not exist in new version' MODIFIED = _pygit2.GIT_DELTA_MODIFIED 'entry content changed between old and new' RENAMED = _pygit2.GIT_DELTA_RENAMED 'entry was renamed between old and new' COPIED = _pygit2.GIT_DELTA_COPIED 'entry was copied from another old entry' IGNORED = _pygit2.GIT_DELTA_IGNORED 'entry is ignored item in workdir' UNTRACKED = _pygit2.GIT_DELTA_UNTRACKED 'entry is untracked item in workdir' TYPECHANGE = _pygit2.GIT_DELTA_TYPECHANGE 'type of entry changed between old and new' UNREADABLE = _pygit2.GIT_DELTA_UNREADABLE 'entry is unreadable' CONFLICTED = _pygit2.GIT_DELTA_CONFLICTED 'entry in the index is conflicted' class DescribeStrategy(IntEnum): """ Reference lookup strategy. These behave like the --tags and --all options to git-describe, namely they say to look for any reference in either refs/tags/ or refs/ respectively. """ DEFAULT = _pygit2.GIT_DESCRIBE_DEFAULT TAGS = _pygit2.GIT_DESCRIBE_TAGS ALL = _pygit2.GIT_DESCRIBE_ALL class DiffFind(IntFlag): """Flags to control the behavior of diff rename/copy detection.""" FIND_BY_CONFIG = _pygit2.GIT_DIFF_FIND_BY_CONFIG """ Obey ``diff.renames``. Overridden by any other ``FIND_...`` flag. """ FIND_RENAMES = _pygit2.GIT_DIFF_FIND_RENAMES """ Look for renames? (`--find-renames`) """ FIND_RENAMES_FROM_REWRITES = _pygit2.GIT_DIFF_FIND_RENAMES_FROM_REWRITES """ Consider old side of MODIFIED for renames? (`--break-rewrites=N`) """ FIND_COPIES = _pygit2.GIT_DIFF_FIND_COPIES """ Look for copies? (a la `--find-copies`). """ FIND_COPIES_FROM_UNMODIFIED = _pygit2.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED """ Consider UNMODIFIED as copy sources? (`--find-copies-harder`). For this to work correctly, use INCLUDE_UNMODIFIED when the initial `Diff` is being generated. """ FIND_REWRITES = _pygit2.GIT_DIFF_FIND_REWRITES """ Mark significant rewrites for split (`--break-rewrites=/M`) """ BREAK_REWRITES = _pygit2.GIT_DIFF_BREAK_REWRITES """ Actually split large rewrites into delete/add pairs """ FIND_AND_BREAK_REWRITES = _pygit2.GIT_DIFF_FIND_AND_BREAK_REWRITES """ Mark rewrites for split and break into delete/add pairs """ FIND_FOR_UNTRACKED = _pygit2.GIT_DIFF_FIND_FOR_UNTRACKED """ Find renames/copies for UNTRACKED items in working directory. For this to work correctly, use INCLUDE_UNTRACKED when the initial `Diff` is being generated (and obviously the diff must be against the working directory for this to make sense). """ FIND_ALL = _pygit2.GIT_DIFF_FIND_ALL """ Turn on all finding features. """ FIND_IGNORE_LEADING_WHITESPACE = _pygit2.GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE """ Measure similarity ignoring leading whitespace (default) """ FIND_IGNORE_WHITESPACE = _pygit2.GIT_DIFF_FIND_IGNORE_WHITESPACE """ Measure similarity ignoring all whitespace """ FIND_DONT_IGNORE_WHITESPACE = _pygit2.GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE """ Measure similarity including all data """ FIND_EXACT_MATCH_ONLY = _pygit2.GIT_DIFF_FIND_EXACT_MATCH_ONLY """ Measure similarity only by comparing SHAs (fast and cheap) """ BREAK_REWRITES_FOR_RENAMES_ONLY = _pygit2.GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY """ Do not break rewrites unless they contribute to a rename. Normally, FIND_AND_BREAK_REWRITES will measure the self- similarity of modified files and split the ones that have changed a lot into a DELETE / ADD pair. Then the sides of that pair will be considered candidates for rename and copy detection. If you add this flag in and the split pair is *not* used for an actual rename or copy, then the modified record will be restored to a regular MODIFIED record instead of being split. """ FIND_REMOVE_UNMODIFIED = _pygit2.GIT_DIFF_FIND_REMOVE_UNMODIFIED """ Remove any UNMODIFIED deltas after find_similar is done. Using FIND_COPIES_FROM_UNMODIFIED to emulate the --find-copies-harder behavior requires building a diff with the INCLUDE_UNMODIFIED flag. If you do not want UNMODIFIED records in the final result, pass this flag to have them removed. """ class DiffFlag(IntFlag): """ Flags for the delta object and the file objects on each side. These flags are used for both the `flags` value of the `DiffDelta` and the flags for the `DiffFile` objects representing the old and new sides of the delta. Values outside of this public range should be considered reserved for internal or future use. """ BINARY = _pygit2.GIT_DIFF_FLAG_BINARY 'file(s) treated as binary data' NOT_BINARY = _pygit2.GIT_DIFF_FLAG_NOT_BINARY 'file(s) treated as text data' VALID_ID = _pygit2.GIT_DIFF_FLAG_VALID_ID '`id` value is known correct' EXISTS = _pygit2.GIT_DIFF_FLAG_EXISTS 'file exists at this side of the delta' VALID_SIZE = _pygit2.GIT_DIFF_FLAG_VALID_SIZE 'file size value is known correct' class DiffOption(IntFlag): """ Flags for diff options. A combination of these flags can be passed in via the `flags` value in `diff_*` functions. """ NORMAL = _pygit2.GIT_DIFF_NORMAL 'Normal diff, the default' REVERSE = _pygit2.GIT_DIFF_REVERSE 'Reverse the sides of the diff' INCLUDE_IGNORED = _pygit2.GIT_DIFF_INCLUDE_IGNORED 'Include ignored files in the diff' RECURSE_IGNORED_DIRS = _pygit2.GIT_DIFF_RECURSE_IGNORED_DIRS """ Even with INCLUDE_IGNORED, an entire ignored directory will be marked with only a single entry in the diff; this flag adds all files under the directory as IGNORED entries, too. """ INCLUDE_UNTRACKED = _pygit2.GIT_DIFF_INCLUDE_UNTRACKED 'Include untracked files in the diff' RECURSE_UNTRACKED_DIRS = _pygit2.GIT_DIFF_RECURSE_UNTRACKED_DIRS """ Even with INCLUDE_UNTRACKED, an entire untracked directory will be marked with only a single entry in the diff (a la what core Git does in `git status`); this flag adds *all* files under untracked directories as UNTRACKED entries, too. """ INCLUDE_UNMODIFIED = _pygit2.GIT_DIFF_INCLUDE_UNMODIFIED 'Include unmodified files in the diff' INCLUDE_TYPECHANGE = _pygit2.GIT_DIFF_INCLUDE_TYPECHANGE """ Normally, a type change between files will be converted into a DELETED record for the old and an ADDED record for the new; this options enabled the generation of TYPECHANGE delta records. """ INCLUDE_TYPECHANGE_TREES = _pygit2.GIT_DIFF_INCLUDE_TYPECHANGE_TREES """ Even with INCLUDE_TYPECHANGE, blob->tree changes still generally show as a DELETED blob. This flag tries to correctly label blob->tree transitions as TYPECHANGE records with new_file's mode set to tree. Note: the tree SHA will not be available. """ IGNORE_FILEMODE = _pygit2.GIT_DIFF_IGNORE_FILEMODE 'Ignore file mode changes' IGNORE_SUBMODULES = _pygit2.GIT_DIFF_IGNORE_SUBMODULES 'Treat all submodules as unmodified' IGNORE_CASE = _pygit2.GIT_DIFF_IGNORE_CASE 'Use case insensitive filename comparisons' INCLUDE_CASECHANGE = _pygit2.GIT_DIFF_INCLUDE_CASECHANGE """ May be combined with IGNORE_CASE to specify that a file that has changed case will be returned as an add/delete pair. """ DISABLE_PATHSPEC_MATCH = _pygit2.GIT_DIFF_DISABLE_PATHSPEC_MATCH """ If the pathspec is set in the diff options, this flags indicates that the paths will be treated as literal paths instead of fnmatch patterns. Each path in the list must either be a full path to a file or a directory. (A trailing slash indicates that the path will _only_ match a directory). If a directory is specified, all children will be included. """ SKIP_BINARY_CHECK = _pygit2.GIT_DIFF_SKIP_BINARY_CHECK """ Disable updating of the `binary` flag in delta records. This is useful when iterating over a diff if you don't need hunk and data callbacks and want to avoid having to load file completely. """ ENABLE_FAST_UNTRACKED_DIRS = _pygit2.GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS """ When diff finds an untracked directory, to match the behavior of core Git, it scans the contents for IGNORED and UNTRACKED files. If *all* contents are IGNORED, then the directory is IGNORED; if any contents are not IGNORED, then the directory is UNTRACKED. This is extra work that may not matter in many cases. This flag turns off that scan and immediately labels an untracked directory as UNTRACKED (changing the behavior to not match core Git). """ UPDATE_INDEX = _pygit2.GIT_DIFF_UPDATE_INDEX """ When diff finds a file in the working directory with stat information different from the index, but the OID ends up being the same, write the correct stat information into the index. Note: without this flag, diff will always leave the index untouched. """ INCLUDE_UNREADABLE = _pygit2.GIT_DIFF_INCLUDE_UNREADABLE 'Include unreadable files in the diff' INCLUDE_UNREADABLE_AS_UNTRACKED = _pygit2.GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED 'Include unreadable files in the diff' INDENT_HEURISTIC = _pygit2.GIT_DIFF_INDENT_HEURISTIC """ Use a heuristic that takes indentation and whitespace into account which generally can produce better diffs when dealing with ambiguous diff hunks. """ IGNORE_BLANK_LINES = _pygit2.GIT_DIFF_IGNORE_BLANK_LINES 'Ignore blank lines' FORCE_TEXT = _pygit2.GIT_DIFF_FORCE_TEXT 'Treat all files as text, disabling binary attributes & detection' FORCE_BINARY = _pygit2.GIT_DIFF_FORCE_BINARY 'Treat all files as binary, disabling text diffs' IGNORE_WHITESPACE = _pygit2.GIT_DIFF_IGNORE_WHITESPACE 'Ignore all whitespace' IGNORE_WHITESPACE_CHANGE = _pygit2.GIT_DIFF_IGNORE_WHITESPACE_CHANGE 'Ignore changes in amount of whitespace' IGNORE_WHITESPACE_EOL = _pygit2.GIT_DIFF_IGNORE_WHITESPACE_EOL 'Ignore whitespace at end of line' SHOW_UNTRACKED_CONTENT = _pygit2.GIT_DIFF_SHOW_UNTRACKED_CONTENT """ When generating patch text, include the content of untracked files. This automatically turns on INCLUDE_UNTRACKED but it does not turn on RECURSE_UNTRACKED_DIRS. Add that flag if you want the content of every single UNTRACKED file. """ SHOW_UNMODIFIED = _pygit2.GIT_DIFF_SHOW_UNMODIFIED """ When generating output, include the names of unmodified files if they are included in the git_diff. Normally these are skipped in the formats that list files (e.g. name-only, name-status, raw). Even with this, these will not be included in patch format. """ PATIENCE = _pygit2.GIT_DIFF_PATIENCE "Use the 'patience diff' algorithm" MINIMAL = _pygit2.GIT_DIFF_MINIMAL 'Take extra time to find minimal diff' SHOW_BINARY = _pygit2.GIT_DIFF_SHOW_BINARY """ Include the necessary deflate / delta information so that `git-apply` can apply given diff information to binary files. """ class DiffStatsFormat(IntFlag): """Formatting options for diff stats""" NONE = _pygit2.GIT_DIFF_STATS_NONE 'No stats' FULL = _pygit2.GIT_DIFF_STATS_FULL 'Full statistics, equivalent of `--stat`' SHORT = _pygit2.GIT_DIFF_STATS_SHORT 'Short statistics, equivalent of `--shortstat`' NUMBER = _pygit2.GIT_DIFF_STATS_NUMBER 'Number statistics, equivalent of `--numstat`' INCLUDE_SUMMARY = _pygit2.GIT_DIFF_STATS_INCLUDE_SUMMARY 'Extended header information such as creations, renames and mode changes, equivalent of `--summary`' class Feature(IntFlag): """ Combinations of these values describe the features with which libgit2 was compiled. """ THREADS = C.GIT_FEATURE_THREADS HTTPS = C.GIT_FEATURE_HTTPS SSH = C.GIT_FEATURE_SSH NSEC = C.GIT_FEATURE_NSEC class FetchPrune(IntEnum): """Acceptable prune settings when fetching.""" UNSPECIFIED = C.GIT_FETCH_PRUNE_UNSPECIFIED 'Use the setting from the configuration' PRUNE = C.GIT_FETCH_PRUNE """Force pruning on: remove any remote branch in the local repository that does not exist in the remote.""" NO_PRUNE = C.GIT_FETCH_NO_PRUNE """Force pruning off: always keep the remote branches.""" class FileMode(IntFlag): UNREADABLE = _pygit2.GIT_FILEMODE_UNREADABLE TREE = _pygit2.GIT_FILEMODE_TREE BLOB = _pygit2.GIT_FILEMODE_BLOB BLOB_EXECUTABLE = _pygit2.GIT_FILEMODE_BLOB_EXECUTABLE LINK = _pygit2.GIT_FILEMODE_LINK COMMIT = _pygit2.GIT_FILEMODE_COMMIT class FileStatus(IntFlag): """ Status flags for a single file. A combination of these values will be returned to indicate the status of a file. Status compares the working directory, the index, and the current HEAD of the repository. The `INDEX_...` set of flags represents the status of the file in the index relative to the HEAD, and the `WT_...` set of flags represents the status of the file in the working directory relative to the index. """ CURRENT = _pygit2.GIT_STATUS_CURRENT INDEX_NEW = _pygit2.GIT_STATUS_INDEX_NEW INDEX_MODIFIED = _pygit2.GIT_STATUS_INDEX_MODIFIED INDEX_DELETED = _pygit2.GIT_STATUS_INDEX_DELETED INDEX_RENAMED = _pygit2.GIT_STATUS_INDEX_RENAMED INDEX_TYPECHANGE = _pygit2.GIT_STATUS_INDEX_TYPECHANGE WT_NEW = _pygit2.GIT_STATUS_WT_NEW WT_MODIFIED = _pygit2.GIT_STATUS_WT_MODIFIED WT_DELETED = _pygit2.GIT_STATUS_WT_DELETED WT_TYPECHANGE = _pygit2.GIT_STATUS_WT_TYPECHANGE WT_RENAMED = _pygit2.GIT_STATUS_WT_RENAMED WT_UNREADABLE = _pygit2.GIT_STATUS_WT_UNREADABLE IGNORED = _pygit2.GIT_STATUS_IGNORED CONFLICTED = _pygit2.GIT_STATUS_CONFLICTED class FilterFlag(IntFlag): """Filter option flags.""" DEFAULT = _pygit2.GIT_FILTER_DEFAULT ALLOW_UNSAFE = _pygit2.GIT_FILTER_ALLOW_UNSAFE "Don't error for `safecrlf` violations, allow them to continue." NO_SYSTEM_ATTRIBUTES = _pygit2.GIT_FILTER_NO_SYSTEM_ATTRIBUTES "Don't load `/etc/gitattributes` (or the system equivalent)" ATTRIBUTES_FROM_HEAD = _pygit2.GIT_FILTER_ATTRIBUTES_FROM_HEAD 'Load attributes from `.gitattributes` in the root of HEAD' ATTRIBUTES_FROM_COMMIT = _pygit2.GIT_FILTER_ATTRIBUTES_FROM_COMMIT 'Load attributes from `.gitattributes` in a given commit. This can only be specified in a `git_filter_options`.' class FilterMode(IntEnum): """ Filters are applied in one of two directions: smudging - which is exporting a file from the Git object database to the working directory, and cleaning - which is importing a file from the working directory to the Git object database. These values control which direction of change is being applied. """ TO_WORKTREE = _pygit2.GIT_FILTER_TO_WORKTREE SMUDGE = _pygit2.GIT_FILTER_SMUDGE TO_ODB = _pygit2.GIT_FILTER_TO_ODB CLEAN = _pygit2.GIT_FILTER_CLEAN class MergeAnalysis(IntFlag): """The results of `Repository.merge_analysis` indicate the merge opportunities.""" NONE = _pygit2.GIT_MERGE_ANALYSIS_NONE 'No merge is possible. (Unused.)' NORMAL = _pygit2.GIT_MERGE_ANALYSIS_NORMAL """ A "normal" merge; both HEAD and the given merge input have diverged from their common ancestor. The divergent commits must be merged. """ UP_TO_DATE = _pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE """ All given merge inputs are reachable from HEAD, meaning the repository is up-to-date and no merge needs to be performed. """ FASTFORWARD = _pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD """ The given merge input is a fast-forward from HEAD and no merge needs to be performed. Instead, the client can check out the given merge input. """ UNBORN = _pygit2.GIT_MERGE_ANALYSIS_UNBORN """ The HEAD of the current repository is "unborn" and does not point to a valid commit. No merge can be performed, but the caller may wish to simply set HEAD to the target commit(s). """ class MergeFavor(IntEnum): """ Merge file favor options for `Repository.merge` instruct the file-level merging functionality how to deal with conflicting regions of the files. """ NORMAL = C.GIT_MERGE_FILE_FAVOR_NORMAL """ When a region of a file is changed in both branches, a conflict will be recorded in the index so that `checkout` can produce a merge file with conflict markers in the working directory. This is the default. """ OURS = C.GIT_MERGE_FILE_FAVOR_OURS """ When a region of a file is changed in both branches, the file created in the index will contain the "ours" side of any conflicting region. The index will not record a conflict. """ THEIRS = C.GIT_MERGE_FILE_FAVOR_THEIRS """ When a region of a file is changed in both branches, the file created in the index will contain the "theirs" side of any conflicting region. The index will not record a conflict. """ UNION = C.GIT_MERGE_FILE_FAVOR_UNION """ When a region of a file is changed in both branches, the file created in the index will contain each unique line from each side, which has the result of combining both files. The index will not record a conflict. """ class MergeFileFlag(IntFlag): """File merging flags""" DEFAULT = C.GIT_MERGE_FILE_DEFAULT """ Defaults """ STYLE_MERGE = C.GIT_MERGE_FILE_STYLE_MERGE """ Create standard conflicted merge files """ STYLE_DIFF3 = C.GIT_MERGE_FILE_STYLE_DIFF3 """ Create diff3-style files """ SIMPLIFY_ALNUM = C.GIT_MERGE_FILE_SIMPLIFY_ALNUM """ Condense non-alphanumeric regions for simplified diff file """ IGNORE_WHITESPACE = C.GIT_MERGE_FILE_IGNORE_WHITESPACE """ Ignore all whitespace """ IGNORE_WHITESPACE_CHANGE = C.GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE """ Ignore changes in amount of whitespace """ IGNORE_WHITESPACE_EOL = C.GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL """ Ignore whitespace at end of line """ DIFF_PATIENCE = C.GIT_MERGE_FILE_DIFF_PATIENCE """ Use the "patience diff" algorithm """ DIFF_MINIMAL = C.GIT_MERGE_FILE_DIFF_MINIMAL """ Take extra time to find minimal diff """ STYLE_ZDIFF3 = C.GIT_MERGE_FILE_STYLE_ZDIFF3 """ Create zdiff3 ("zealous diff3")-style files """ ACCEPT_CONFLICTS = C.GIT_MERGE_FILE_ACCEPT_CONFLICTS """ Do not produce file conflicts when common regions have changed; keep the conflict markers in the file and accept that as the merge result. """ class MergeFlag(IntFlag): """ Flags for `Repository.merge` options. A combination of these flags can be passed in via the `flags` value. """ FIND_RENAMES = C.GIT_MERGE_FIND_RENAMES """ Detect renames that occur between the common ancestor and the "ours" side or the common ancestor and the "theirs" side. This will enable the ability to merge between a modified and renamed file. """ FAIL_ON_CONFLICT = C.GIT_MERGE_FAIL_ON_CONFLICT """ If a conflict occurs, exit immediately instead of attempting to continue resolving conflicts. The merge operation will raise GitError (GIT_EMERGECONFLICT) and no index will be returned. """ SKIP_REUC = C.GIT_MERGE_SKIP_REUC """ Do not write the REUC extension on the generated index. """ NO_RECURSIVE = C.GIT_MERGE_NO_RECURSIVE """ If the commits being merged have multiple merge bases, do not build a recursive merge base (by merging the multiple merge bases), instead simply use the first base. This flag provides a similar merge base to `git-merge-resolve`. """ VIRTUAL_BASE = C.GIT_MERGE_VIRTUAL_BASE """ Treat this merge as if it is to produce the virtual base of a recursive merge. This will ensure that there are no conflicts, any conflicting regions will keep conflict markers in the merge result. """ class MergePreference(IntFlag): """The user's stated preference for merges.""" NONE = _pygit2.GIT_MERGE_PREFERENCE_NONE 'No configuration was found that suggests a preferred behavior for merge.' NO_FASTFORWARD = _pygit2.GIT_MERGE_PREFERENCE_NO_FASTFORWARD """ There is a `merge.ff=false` configuration setting, suggesting that the user does not want to allow a fast-forward merge. """ FASTFORWARD_ONLY = _pygit2.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY """ There is a `merge.ff=only` configuration setting, suggesting that the user only wants fast-forward merges. """ class ObjectType(IntEnum): ANY = _pygit2.GIT_OBJECT_ANY 'Object can be any of the following' INVALID = _pygit2.GIT_OBJECT_INVALID 'Object is invalid.' COMMIT = _pygit2.GIT_OBJECT_COMMIT 'A commit object.' TREE = _pygit2.GIT_OBJECT_TREE 'A tree (directory listing) object.' BLOB = _pygit2.GIT_OBJECT_BLOB 'A file revision object.' TAG = _pygit2.GIT_OBJECT_TAG 'An annotated tag object.' OFS_DELTA = _pygit2.GIT_OBJECT_OFS_DELTA 'A delta, base is given by an offset.' REF_DELTA = _pygit2.GIT_OBJECT_REF_DELTA 'A delta, base is given by object id.' class Option(IntEnum): """Global libgit2 library options""" # Commented out values --> exists in libgit2 but not supported in pygit2's options.c yet GET_MWINDOW_SIZE = options.GIT_OPT_GET_MWINDOW_SIZE SET_MWINDOW_SIZE = options.GIT_OPT_SET_MWINDOW_SIZE GET_MWINDOW_MAPPED_LIMIT = options.GIT_OPT_GET_MWINDOW_MAPPED_LIMIT SET_MWINDOW_MAPPED_LIMIT = options.GIT_OPT_SET_MWINDOW_MAPPED_LIMIT GET_SEARCH_PATH = options.GIT_OPT_GET_SEARCH_PATH SET_SEARCH_PATH = options.GIT_OPT_SET_SEARCH_PATH SET_CACHE_OBJECT_LIMIT = options.GIT_OPT_SET_CACHE_OBJECT_LIMIT SET_CACHE_MAX_SIZE = options.GIT_OPT_SET_CACHE_MAX_SIZE ENABLE_CACHING = options.GIT_OPT_ENABLE_CACHING GET_CACHED_MEMORY = options.GIT_OPT_GET_CACHED_MEMORY GET_TEMPLATE_PATH = options.GIT_OPT_GET_TEMPLATE_PATH SET_TEMPLATE_PATH = options.GIT_OPT_SET_TEMPLATE_PATH SET_SSL_CERT_LOCATIONS = options.GIT_OPT_SET_SSL_CERT_LOCATIONS SET_USER_AGENT = options.GIT_OPT_SET_USER_AGENT ENABLE_STRICT_OBJECT_CREATION = options.GIT_OPT_ENABLE_STRICT_OBJECT_CREATION ENABLE_STRICT_SYMBOLIC_REF_CREATION = ( options.GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION ) SET_SSL_CIPHERS = options.GIT_OPT_SET_SSL_CIPHERS GET_USER_AGENT = options.GIT_OPT_GET_USER_AGENT ENABLE_OFS_DELTA = options.GIT_OPT_ENABLE_OFS_DELTA ENABLE_FSYNC_GITDIR = options.GIT_OPT_ENABLE_FSYNC_GITDIR GET_WINDOWS_SHAREMODE = options.GIT_OPT_GET_WINDOWS_SHAREMODE SET_WINDOWS_SHAREMODE = options.GIT_OPT_SET_WINDOWS_SHAREMODE ENABLE_STRICT_HASH_VERIFICATION = options.GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION SET_ALLOCATOR = options.GIT_OPT_SET_ALLOCATOR ENABLE_UNSAVED_INDEX_SAFETY = options.GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY GET_PACK_MAX_OBJECTS = options.GIT_OPT_GET_PACK_MAX_OBJECTS SET_PACK_MAX_OBJECTS = options.GIT_OPT_SET_PACK_MAX_OBJECTS DISABLE_PACK_KEEP_FILE_CHECKS = options.GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS ENABLE_HTTP_EXPECT_CONTINUE = options.GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE GET_MWINDOW_FILE_LIMIT = options.GIT_OPT_GET_MWINDOW_FILE_LIMIT SET_MWINDOW_FILE_LIMIT = options.GIT_OPT_SET_MWINDOW_FILE_LIMIT SET_ODB_PACKED_PRIORITY = options.GIT_OPT_SET_ODB_PACKED_PRIORITY SET_ODB_LOOSE_PRIORITY = options.GIT_OPT_SET_ODB_LOOSE_PRIORITY GET_EXTENSIONS = options.GIT_OPT_GET_EXTENSIONS SET_EXTENSIONS = options.GIT_OPT_SET_EXTENSIONS GET_OWNER_VALIDATION = options.GIT_OPT_GET_OWNER_VALIDATION SET_OWNER_VALIDATION = options.GIT_OPT_SET_OWNER_VALIDATION GET_HOMEDIR = options.GIT_OPT_GET_HOMEDIR SET_HOMEDIR = options.GIT_OPT_SET_HOMEDIR SET_SERVER_CONNECT_TIMEOUT = options.GIT_OPT_SET_SERVER_CONNECT_TIMEOUT GET_SERVER_CONNECT_TIMEOUT = options.GIT_OPT_GET_SERVER_CONNECT_TIMEOUT SET_SERVER_TIMEOUT = options.GIT_OPT_SET_SERVER_TIMEOUT GET_SERVER_TIMEOUT = options.GIT_OPT_GET_SERVER_TIMEOUT GET_USER_AGENT_PRODUCT = options.GIT_OPT_GET_USER_AGENT_PRODUCT SET_USER_AGENT_PRODUCT = options.GIT_OPT_SET_USER_AGENT_PRODUCT ADD_SSL_X509_CERT = options.GIT_OPT_ADD_SSL_X509_CERT class ReferenceFilter(IntEnum): """Filters for References.iterator().""" ALL = _pygit2.GIT_REFERENCES_ALL BRANCHES = _pygit2.GIT_REFERENCES_BRANCHES TAGS = _pygit2.GIT_REFERENCES_TAGS class ReferenceType(IntFlag): """Basic type of any Git reference.""" INVALID = C.GIT_REFERENCE_INVALID 'Invalid reference' DIRECT = C.GIT_REFERENCE_DIRECT 'A reference that points at an object id' SYMBOLIC = C.GIT_REFERENCE_SYMBOLIC 'A reference that points at another reference' ALL = C.GIT_REFERENCE_ALL 'Bitwise OR of (DIRECT | SYMBOLIC)' class RepositoryInitFlag(IntFlag): """ Option flags for pygit2.init_repository(). """ BARE = C.GIT_REPOSITORY_INIT_BARE 'Create a bare repository with no working directory.' NO_REINIT = C.GIT_REPOSITORY_INIT_NO_REINIT 'Raise GitError if the path appears to already be a git repository.' NO_DOTGIT_DIR = C.GIT_REPOSITORY_INIT_NO_DOTGIT_DIR """Normally a "/.git/" will be appended to the repo path for non-bare repos (if it is not already there), but passing this flag prevents that behavior.""" MKDIR = C.GIT_REPOSITORY_INIT_MKDIR """Make the repo_path (and workdir_path) as needed. Init is always willing to create the ".git" directory even without this flag. This flag tells init to create the trailing component of the repo and workdir paths as needed.""" MKPATH = C.GIT_REPOSITORY_INIT_MKPATH 'Recursively make all components of the repo and workdir paths as necessary.' EXTERNAL_TEMPLATE = C.GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE """libgit2 normally uses internal templates to initialize a new repo. This flags enables external templates, looking at the "template_path" from the options if set, or the `init.templatedir` global config if not, or falling back on "/usr/share/git-core/templates" if it exists.""" RELATIVE_GITLINK = C.GIT_REPOSITORY_INIT_RELATIVE_GITLINK """If an alternate workdir is specified, use relative paths for the gitdir and core.worktree.""" class RepositoryInitMode(IntEnum): """ Mode options for pygit2.init_repository(). """ SHARED_UMASK = C.GIT_REPOSITORY_INIT_SHARED_UMASK 'Use permissions configured by umask - the default.' SHARED_GROUP = C.GIT_REPOSITORY_INIT_SHARED_GROUP """ Use '--shared=group' behavior, chmod'ing the new repo to be group writable and "g+sx" for sticky group assignment. """ SHARED_ALL = C.GIT_REPOSITORY_INIT_SHARED_ALL "Use '--shared=all' behavior, adding world readability." class RepositoryOpenFlag(IntFlag): """ Option flags for Repository.__init__(). """ DEFAULT = 0 'Default flags.' NO_SEARCH = C.GIT_REPOSITORY_OPEN_NO_SEARCH """ Only open the repository if it can be immediately found in the start_path. Do not walk up from the start_path looking at parent directories. """ CROSS_FS = C.GIT_REPOSITORY_OPEN_CROSS_FS """ Unless this flag is set, open will not continue searching across filesystem boundaries (i.e. when `st_dev` changes from the `stat` system call). For example, searching in a user's home directory at "/home/user/source/" will not return "/.git/" as the found repo if "/" is a different filesystem than "/home". """ BARE = C.GIT_REPOSITORY_OPEN_BARE """ Open repository as a bare repo regardless of core.bare config, and defer loading config file for faster setup. Unlike `git_repository_open_bare`, this can follow gitlinks. """ NO_DOTGIT = C.GIT_REPOSITORY_OPEN_NO_DOTGIT """ Do not check for a repository by appending /.git to the start_path; only open the repository if start_path itself points to the git directory. """ FROM_ENV = C.GIT_REPOSITORY_OPEN_FROM_ENV """ Find and open a git repository, respecting the environment variables used by the git command-line tools. If set, `git_repository_open_ext` will ignore the other flags and the `ceiling_dirs` argument, and will allow a NULL `path` to use `GIT_DIR` or search from the current directory. The search for a repository will respect $GIT_CEILING_DIRECTORIES and $GIT_DISCOVERY_ACROSS_FILESYSTEM. The opened repository will respect $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and $GIT_ALTERNATE_OBJECT_DIRECTORIES. In the future, this flag will also cause `git_repository_open_ext` to respect $GIT_WORK_TREE and $GIT_COMMON_DIR; currently, `git_repository_open_ext` with this flag will error out if either $GIT_WORK_TREE or $GIT_COMMON_DIR is set. """ class RepositoryState(IntEnum): """ Repository state: These values represent possible states for the repository to be in, based on the current operation which is ongoing. """ NONE = C.GIT_REPOSITORY_STATE_NONE MERGE = C.GIT_REPOSITORY_STATE_MERGE REVERT = C.GIT_REPOSITORY_STATE_REVERT REVERT_SEQUENCE = C.GIT_REPOSITORY_STATE_REVERT_SEQUENCE CHERRYPICK = C.GIT_REPOSITORY_STATE_CHERRYPICK CHERRYPICK_SEQUENCE = C.GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE BISECT = C.GIT_REPOSITORY_STATE_BISECT REBASE = C.GIT_REPOSITORY_STATE_REBASE REBASE_INTERACTIVE = C.GIT_REPOSITORY_STATE_REBASE_INTERACTIVE REBASE_MERGE = C.GIT_REPOSITORY_STATE_REBASE_MERGE APPLY_MAILBOX = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX APPLY_MAILBOX_OR_REBASE = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE class ResetMode(IntEnum): """Kinds of reset operation.""" SOFT = _pygit2.GIT_RESET_SOFT 'Move the head to the given commit' MIXED = _pygit2.GIT_RESET_MIXED 'SOFT plus reset index to the commit' HARD = _pygit2.GIT_RESET_HARD 'MIXED plus changes in working tree discarded' class RevSpecFlag(IntFlag): """ Revparse flags. These indicate the intended behavior of the spec passed to Repository.revparse() """ SINGLE = _pygit2.GIT_REVSPEC_SINGLE 'The spec targeted a single object.' RANGE = _pygit2.GIT_REVSPEC_RANGE 'The spec targeted a range of commits.' MERGE_BASE = _pygit2.GIT_REVSPEC_MERGE_BASE "The spec used the '...' operator, which invokes special semantics." class SortMode(IntFlag): """ Flags to specify the sorting which a revwalk should perform. """ NONE = _pygit2.GIT_SORT_NONE """ Sort the output with the same default method from `git`: reverse chronological order. This is the default sorting for new walkers. """ TOPOLOGICAL = _pygit2.GIT_SORT_TOPOLOGICAL """ Sort the repository contents in topological order (no parents before all of its children are shown); this sorting mode can be combined with TIME sorting to produce `git`'s `--date-order``. """ TIME = _pygit2.GIT_SORT_TIME """ Sort the repository contents by commit time; this sorting mode can be combined with TOPOLOGICAL. """ REVERSE = _pygit2.GIT_SORT_REVERSE """ Iterate through the repository contents in reverse order; this sorting mode can be combined with any of the above. """ class StashApplyProgress(IntEnum): """ Stash apply progression states """ NONE = C.GIT_STASH_APPLY_PROGRESS_NONE LOADING_STASH = C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH 'Loading the stashed data from the object database.' ANALYZE_INDEX = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX 'The stored index is being analyzed.' ANALYZE_MODIFIED = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED 'The modified files are being analyzed.' ANALYZE_UNTRACKED = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED 'The untracked and ignored files are being analyzed.' CHECKOUT_UNTRACKED = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED 'The untracked files are being written to disk.' CHECKOUT_MODIFIED = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED 'The modified files are being written to disk.' DONE = C.GIT_STASH_APPLY_PROGRESS_DONE 'The stash was applied successfully.' class SubmoduleIgnore(IntEnum): UNSPECIFIED = _pygit2.GIT_SUBMODULE_IGNORE_UNSPECIFIED "use the submodule's configuration" NONE = _pygit2.GIT_SUBMODULE_IGNORE_NONE 'any change or untracked == dirty' UNTRACKED = _pygit2.GIT_SUBMODULE_IGNORE_UNTRACKED 'dirty if tracked files change' DIRTY = _pygit2.GIT_SUBMODULE_IGNORE_DIRTY 'only dirty if HEAD moved' ALL = _pygit2.GIT_SUBMODULE_IGNORE_ALL 'never dirty' class SubmoduleStatus(IntFlag): IN_HEAD = _pygit2.GIT_SUBMODULE_STATUS_IN_HEAD 'superproject head contains submodule' IN_INDEX = _pygit2.GIT_SUBMODULE_STATUS_IN_INDEX 'superproject index contains submodule' IN_CONFIG = _pygit2.GIT_SUBMODULE_STATUS_IN_CONFIG 'superproject gitmodules has submodule' IN_WD = _pygit2.GIT_SUBMODULE_STATUS_IN_WD 'superproject workdir has submodule' INDEX_ADDED = _pygit2.GIT_SUBMODULE_STATUS_INDEX_ADDED 'in index, not in head (flag available if ignore is not ALL)' INDEX_DELETED = _pygit2.GIT_SUBMODULE_STATUS_INDEX_DELETED 'in head, not in index (flag available if ignore is not ALL)' INDEX_MODIFIED = _pygit2.GIT_SUBMODULE_STATUS_INDEX_MODIFIED "index and head don't match (flag available if ignore is not ALL)" WD_UNINITIALIZED = _pygit2.GIT_SUBMODULE_STATUS_WD_UNINITIALIZED 'workdir contains empty repository (flag available if ignore is not ALL)' WD_ADDED = _pygit2.GIT_SUBMODULE_STATUS_WD_ADDED 'in workdir, not index (flag available if ignore is not ALL)' WD_DELETED = _pygit2.GIT_SUBMODULE_STATUS_WD_DELETED 'in index, not workdir (flag available if ignore is not ALL)' WD_MODIFIED = _pygit2.GIT_SUBMODULE_STATUS_WD_MODIFIED "index and workdir head don't match (flag available if ignore is not ALL)" WD_INDEX_MODIFIED = _pygit2.GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED 'submodule workdir index is dirty (flag available if ignore is NONE or UNTRACKED)' WD_WD_MODIFIED = _pygit2.GIT_SUBMODULE_STATUS_WD_WD_MODIFIED 'submodule workdir has modified files (flag available if ignore is NONE or UNTRACKED)' WD_UNTRACKED = _pygit2.GIT_SUBMODULE_STATUS_WD_UNTRACKED 'submodule workdir contains untracked files (flag available if ignore is NONE)' libgit2-pygit2-d86662e/pygit2/errors.py000066400000000000000000000046111521317216500177410ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # Import from pygit2 from ._pygit2 import GitError from .ffi import C, ffi __all__ = ['GitError'] value_errors = set([C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EAMBIGUOUS]) def check_error(err: int, io: bool = False) -> None: if err >= 0: return # These are special error codes, they should never reach here test = err != C.GIT_EUSER and err != C.GIT_PASSTHROUGH assert test, f'Unexpected error code {err}' # Error message giterr = C.git_error_last() if giterr != ffi.NULL: message = ffi.string(giterr.message).decode('utf8', errors='surrogateescape') else: message = f'err {err} (no message provided)' # Translate to Python errors if err in value_errors: raise ValueError(message) if err == C.GIT_ENOTFOUND: if io: raise IOError(message) raise KeyError(message) if err == C.GIT_EINVALIDSPEC: raise ValueError(message) if err == C.GIT_ITEROVER: raise StopIteration() # Generic Git error raise GitError(message) # Indicate that we want libgit2 to pretend a function was not set class Passthrough(Exception): def __init__(self) -> None: super().__init__('The function asked for pass-through') libgit2-pygit2-d86662e/pygit2/ffi.py000066400000000000000000000024661521317216500171770ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # Import from pygit2 from ._libgit2 import ffi # noqa: F401 from ._libgit2 import lib as C # type: ignore # noqa: F401 __all__ = ['C', 'ffi'] libgit2-pygit2-d86662e/pygit2/filter.py000066400000000000000000000152441521317216500177160ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations import weakref from collections.abc import Callable from typing import TYPE_CHECKING from ._pygit2 import Blob, FilterSource from .errors import check_error from .ffi import C, ffi from .utils import to_bytes if TYPE_CHECKING: from ._libgit2.ffi import GitFilterListC from .repository import BaseRepository class Filter: """ Base filter class to be used with libgit2 filters. Inherit from this class and override the `check()`, `write()` and `close()` methods to define a filter which can then be registered via `pygit2.filter_register()`. A new Filter instance will be instantiated for each stream which needs to be filtered. For each stream, filter methods will be called in this order: - `check()` - `write()` (may be called multiple times) - `close()` Filtered output data should be written to the next filter in the chain during `write()` and `close()` via the `write_next` method. All output data must be written to the next filter before returning from `close()`. If a filter is dependent on reading the complete input data stream, the filter should only write output data in `close()`. """ #: Space-separated string list of attributes to be used in `check()` attributes: str = '' @classmethod def nattrs(cls) -> int: return len(cls.attributes.split()) def check(self, src: FilterSource, attr_values: list[str | None]) -> None: """ Check whether this filter should be applied to the given source. `check` will be called once per stream. If `Passthrough` is raised, the filter will not be applied. Parameters: src: The source of the filtered blob. attr_values: The values of each attribute for the blob being filtered. `attr_values` will be a sorted list containing attributes in the order they were defined in ``cls.attributes``. """ def write( self, data: bytes, src: FilterSource, write_next: Callable[[bytes], None] ) -> None: """ Write input `data` to this filter. `write()` may be called multiple times per stream. Parameters: data: Input data. src: The source of the filtered blob. write_next: The ``write()`` method of the next filter in the chain. Filtered output data should be written to `write_next` whenever it is available. """ write_next(data) def close(self, write_next: Callable[[bytes], None]) -> None: """ Close this filter. `close()` will be called once per stream whenever all writes() to this stream have been completed. Parameters: write_next: The ``write()`` method of the next filter in the chain. Any remaining filtered output data must be written to `write_next` before returning. """ class FilterList: _all_filter_lists: set[weakref.ReferenceType[FilterList]] = set() _pointer: GitFilterListC @classmethod def _from_c(cls, ptr: GitFilterListC): if ptr == ffi.NULL: return None fl = cls.__new__(cls) fl._pointer = ptr # Keep track of this FilterList until it's garbage collected. This lets # `filter_unregister` ensure the user isn't trying to delete a filter # that's still in use. ref = weakref.ref(fl, FilterList._all_filter_lists.remove) FilterList._all_filter_lists.add(ref) return fl @classmethod def _is_filter_in_use(cls, name: str) -> bool: for ref in cls._all_filter_lists: fl = ref() if fl is not None and name in fl: return True return False def __contains__(self, name: str) -> bool: if not isinstance(name, str): raise TypeError('argument must be str') c_name = to_bytes(name) result = C.git_filter_list_contains(self._pointer, c_name) return bool(result) def __len__(self) -> int: return C.git_filter_list_length(self._pointer) def apply_to_buffer(self, data: bytes) -> bytes: """ Apply a filter list to a data buffer. Return the filtered contents. """ buf = ffi.new('git_buf *') err = C.git_filter_list_apply_to_buffer(buf, self._pointer, data, len(data)) check_error(err) try: return ffi.string(buf.ptr) finally: C.git_buf_dispose(buf) def apply_to_file(self, repo: BaseRepository, path: str) -> bytes: """ Apply a filter list to the contents of a file on disk. Return the filtered contents. """ buf = ffi.new('git_buf *') c_path = to_bytes(path) err = C.git_filter_list_apply_to_file(buf, self._pointer, repo._repo, c_path) check_error(err) try: return ffi.string(buf.ptr) finally: C.git_buf_dispose(buf) def apply_to_blob(self, blob: Blob) -> bytes: """ Apply a filter list to a data buffer. Return the filtered contents. """ buf = ffi.new('git_buf *') c_blob = ffi.new('git_blob **') ffi.buffer(c_blob)[:] = blob._pointer[:] err = C.git_filter_list_apply_to_blob(buf, self._pointer, c_blob[0]) check_error(err) try: return ffi.string(buf.ptr) finally: C.git_buf_dispose(buf) def __del__(self): C.git_filter_list_free(self._pointer) libgit2-pygit2-d86662e/pygit2/index.py000066400000000000000000000445531521317216500175450ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import typing import warnings from dataclasses import dataclass from os import PathLike # Import from pygit2 from ._pygit2 import Diff, Oid, Tree from .enums import DiffOption, FileMode from .errors import check_error from .ffi import C, ffi from .utils import GenericIterator, StrArray, to_bytes, to_str if typing.TYPE_CHECKING: from .repository import Repository class Index: # XXX Implement the basic features in C (_pygit2.Index) and make # pygit2.Index to inherit from _pygit2.Index? This would allow for # a proper implementation in some places: e.g. checking the index type # from C code (see Tree_diff_to_index) def __init__(self, path: str | PathLike[str] | None = None) -> None: """Create a new Index If path is supplied, the read and write methods will use that path to read from and write to. """ cindex = ffi.new('git_index **') err = C.git_index_open(cindex, to_bytes(path)) check_error(err) self._repo = None self._index = cindex[0] self._cindex = cindex @classmethod def from_c(cls, repo, ptr): index = cls.__new__(cls) index._repo = repo index._index = ptr[0] index._cindex = ptr return index @property def _pointer(self): return bytes(ffi.buffer(self._cindex)[:]) def __del__(self) -> None: C.git_index_free(self._index) def __len__(self) -> int: return C.git_index_entrycount(self._index) def __contains__(self, path) -> bool: err = C.git_index_find(ffi.NULL, self._index, to_bytes(path)) if err == C.GIT_ENOTFOUND: return False check_error(err) return True def __getitem__(self, key: str | int | PathLike[str]) -> 'IndexEntry': centry = ffi.NULL if isinstance(key, str) or hasattr(key, '__fspath__'): centry = C.git_index_get_bypath(self._index, to_bytes(key), 0) elif isinstance(key, int): if key >= 0: centry = C.git_index_get_byindex(self._index, key) else: raise ValueError(key) else: raise TypeError(f'Expected str or int, got {type(key)}') if centry == ffi.NULL: raise KeyError(key) return IndexEntry._from_c(centry) def __iter__(self): return GenericIterator(self) def read(self, force: bool = True) -> None: """ Update the contents of the Index by reading from a file. Parameters: force If True (the default) always reload. If False, only if the file has changed. """ err = C.git_index_read(self._index, force) check_error(err, io=True) def write(self) -> None: """Write the contents of the Index to disk.""" err = C.git_index_write(self._index) check_error(err, io=True) def clear(self) -> None: err = C.git_index_clear(self._index) check_error(err) def read_tree(self, tree: Oid | Tree | str) -> None: """Replace the contents of the Index with those of the given tree, expressed either as a object or as an oid (string or ). The tree will be read recursively and all its children will also be inserted into the Index. """ repo = self._repo if isinstance(tree, str): if repo is None: raise TypeError('id given but no associated repository') tree = repo[tree] if isinstance(tree, Oid): if repo is None: raise TypeError('id given but no associated repository') tree = repo[tree] elif not isinstance(tree, Tree): raise TypeError('argument must be Oid, Tree or str') tree_cptr = ffi.new('git_tree **') ffi.buffer(tree_cptr)[:] = tree._pointer[:] err = C.git_index_read_tree(self._index, tree_cptr[0]) check_error(err) def write_tree(self, repo: 'Repository | None' = None) -> Oid: """Create a tree out of the Index. Return the object of the written tree. The contents of the index will be written out to the object database. If there is no associated repository, 'repo' must be passed. If there is an associated repository and 'repo' is passed, then that repository will be used instead. It returns the id of the resulting tree. """ coid = ffi.new('git_oid *') repo = repo or self._repo if repo: err = C.git_index_write_tree_to(coid, self._index, repo._repo) else: err = C.git_index_write_tree(coid, self._index) check_error(err) return Oid(raw=bytes(ffi.buffer(coid)[:])) def remove(self, path: PathLike[str] | str, level: int = 0) -> None: """Remove an entry from the Index.""" err = C.git_index_remove(self._index, to_bytes(path), level) check_error(err, io=True) def remove_directory(self, path: PathLike[str] | str, level: int = 0) -> None: """Remove a directory from the Index.""" err = C.git_index_remove_directory(self._index, to_bytes(path), level) check_error(err, io=True) def remove_all(self, pathspecs: typing.Sequence[str | PathLike[str]]) -> None: """Remove all index entries matching pathspecs.""" with StrArray(pathspecs) as arr: err = C.git_index_remove_all(self._index, arr.ptr, ffi.NULL, ffi.NULL) check_error(err, io=True) def add_all(self, pathspecs: None | list[str | PathLike[str]] = None) -> None: """Add or update index entries matching files in the working directory. If pathspecs are specified, only files matching those pathspecs will be added. """ pathspecs = pathspecs or [] with StrArray(pathspecs) as arr: err = C.git_index_add_all(self._index, arr.ptr, 0, ffi.NULL, ffi.NULL) check_error(err, io=True) def add(self, path_or_entry: 'IndexEntry | str | PathLike[str]') -> None: """Add or update an entry in the Index. If a path is given, that file will be added. The path must be relative to the root of the worktree and the Index must be associated with a repository. If an IndexEntry is given, that entry will be added or update in the Index without checking for the existence of the path or id. """ if isinstance(path_or_entry, IndexEntry): entry = path_or_entry centry, str_ref = entry._to_c() err = C.git_index_add(self._index, centry) elif isinstance(path_or_entry, str) or hasattr(path_or_entry, '__fspath__'): path = path_or_entry err = C.git_index_add_bypath(self._index, to_bytes(path)) else: raise TypeError('argument must be string, Path or IndexEntry') check_error(err, io=True) def add_conflict( self, ancestor: 'IndexEntry', ours: 'IndexEntry', theirs: 'IndexEntry | None' ) -> None: """ Add or update index entries to represent a conflict. Any staged entries that exist at the given paths will be removed. Parameters: ancestor ancestor of the conflict ours ours side of the conflict theirs their side of the conflict """ if ancestor and not isinstance(ancestor, IndexEntry): raise TypeError('ancestor has to be an instance of IndexEntry or None') if ours and not isinstance(ours, IndexEntry): raise TypeError('ours has to be an instance of IndexEntry or None') if theirs and not isinstance(theirs, IndexEntry): raise TypeError('theirs has to be an instance of IndexEntry or None') centry_ancestor: ffi.NULL_TYPE | ffi.GitIndexEntryC = ffi.NULL centry_ours: ffi.NULL_TYPE | ffi.GitIndexEntryC = ffi.NULL centry_theirs: ffi.NULL_TYPE | ffi.GitIndexEntryC = ffi.NULL if ancestor is not None: centry_ancestor, path_ancestor = ancestor._to_c() if ours is not None: centry_ours, path_ours = ours._to_c() if theirs is not None: centry_theirs, path_theirs = theirs._to_c() err = C.git_index_conflict_add( self._index, centry_ancestor, centry_ours, centry_theirs ) check_error(err, io=True) def diff_to_workdir( self, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Diff: """ Diff the index against the working directory. Return a object with the differences between the index and the working copy. Parameters: flags A combination of enums.DiffOption constants. context_lines The number of unchanged lines that define the boundary of a hunk (and to display before and after). interhunk_lines The maximum number of unchanged lines between hunk boundaries before the hunks will be merged into a one. """ repo = self._repo if repo is None: raise ValueError('diff needs an associated repository') copts = ffi.new('git_diff_options *') err = C.git_diff_options_init(copts, 1) check_error(err) copts.flags = int(flags) copts.context_lines = context_lines copts.interhunk_lines = interhunk_lines cdiff = ffi.new('git_diff **') err = C.git_diff_index_to_workdir(cdiff, repo._repo, self._index, copts) check_error(err) return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo) def diff_to_tree( self, tree: Tree, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Diff: """ Diff the index against a tree. Return a object with the differences between the index and the given tree. Parameters: tree The tree to diff. flags A combination of enums.DiffOption constants. context_lines The number of unchanged lines that define the boundary of a hunk (and to display before and after). interhunk_lines The maximum number of unchanged lines between hunk boundaries before the hunks will be merged into a one. """ repo = self._repo if repo is None: raise ValueError('diff needs an associated repository') if not isinstance(tree, Tree): raise TypeError('tree must be a Tree') copts = ffi.new('git_diff_options *') err = C.git_diff_options_init(copts, 1) check_error(err) copts.flags = int(flags) copts.context_lines = context_lines copts.interhunk_lines = interhunk_lines ctree = ffi.new('git_tree **') ffi.buffer(ctree)[:] = tree._pointer[:] cdiff = ffi.new('git_diff **') err = C.git_diff_tree_to_index(cdiff, repo._repo, ctree[0], self._index, copts) check_error(err) return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo) # # Conflicts # @property def conflicts(self): """A collection of conflict information If there are no conflicts None is returned. Otherwise return an object that represents the conflicts in the index. This object presents a mapping interface with the paths as keys. You can use the ``del`` operator to remove a conflict from the Index. Each conflict is made up of three elements. Access or iteration of the conflicts returns a three-tuple of :py:class:`~pygit2.IndexEntry`. The first is the common ancestor, the second is the "ours" side of the conflict, and the third is the "theirs" side. These elements may be None depending on which sides exist for the particular conflict. """ if not C.git_index_has_conflicts(self._index): return None return ConflictCollection(self) @dataclass class MergeFileResult: automergeable: bool 'True if the output was automerged, false if the output contains conflict markers' path: str | None | PathLike[str] 'The path that the resultant merge file should use, or None if a filename conflict would occur' mode: FileMode 'The mode that the resultant merge file should use' contents: str 'Contents of the file, which might include conflict markers' def __repr__(self): t = type(self) contents = ( self.contents if len(self.contents) <= 20 else f'{self.contents[:20]}...' ) return ( f'<{t.__module__}.{t.__qualname__} "' f'automergeable={self.automergeable} "' f'path={self.path} ' f'mode={self.mode} ' f'contents={contents}>' ) @classmethod def _from_c(cls, centry): if centry == ffi.NULL: return None automergeable = centry.automergeable != 0 path = to_str(ffi.string(centry.path)) if centry.path else None mode = FileMode(centry.mode) contents = ffi.string(centry.ptr, centry.len).decode('utf-8') return MergeFileResult(automergeable, path, mode, contents) class IndexEntry: path: str | PathLike[str] 'The path of this entry' id: Oid 'The id of the referenced object' mode: FileMode 'The mode of this entry, a FileMode value' def __init__( self, path: str | PathLike[str], object_id: Oid, mode: FileMode ) -> None: self.path = path self.id = object_id self.mode = mode @property def oid(self): warnings.warn('Use entry.id', DeprecationWarning) return self.id def __str__(self): return f'' def __repr__(self): t = type(self) return f'<{t.__module__}.{t.__qualname__} path={self.path} id={self.id} mode={self.mode}>' def __eq__(self, other): if self is other: return True if not isinstance(other, IndexEntry): return NotImplemented return ( self.path == other.path and self.id == other.id and self.mode == other.mode ) def _to_c(self) -> tuple['ffi.GitIndexEntryC', 'ffi.ArrayC[ffi.char]']: """Convert this entry into the C structure The first returned arg is the pointer, the second is the reference to the string we allocated, which we need to exist past this function """ centry = ffi.new('git_index_entry *') # basically memcpy() ffi.buffer(ffi.addressof(centry, 'id'))[:] = self.id.raw[:] centry.mode = int(self.mode) path = ffi.new('char[]', to_bytes(self.path)) centry.path = path return centry, path @classmethod def _from_c(cls, centry): if centry == ffi.NULL: return None entry = cls.__new__(cls) entry.path = to_str(ffi.string(centry.path)) entry.mode = FileMode(centry.mode) entry.id = Oid(raw=bytes(ffi.buffer(ffi.addressof(centry, 'id'))[:])) return entry class ConflictCollection: def __init__(self, index): self._index = index def __getitem__(self, path): cancestor = ffi.new('git_index_entry **') cours = ffi.new('git_index_entry **') ctheirs = ffi.new('git_index_entry **') err = C.git_index_conflict_get( cancestor, cours, ctheirs, self._index._index, to_bytes(path) ) check_error(err) ancestor = IndexEntry._from_c(cancestor[0]) ours = IndexEntry._from_c(cours[0]) theirs = IndexEntry._from_c(ctheirs[0]) return ancestor, ours, theirs def __delitem__(self, path): err = C.git_index_conflict_remove(self._index._index, to_bytes(path)) check_error(err) def __iter__(self): return ConflictIterator(self._index) def __contains__(self, path): cancestor = ffi.new('git_index_entry **') cours = ffi.new('git_index_entry **') ctheirs = ffi.new('git_index_entry **') err = C.git_index_conflict_get( cancestor, cours, ctheirs, self._index._index, to_bytes(path) ) if err == C.GIT_ENOTFOUND: return False check_error(err) return True class ConflictIterator: def __init__(self, index): citer = ffi.new('git_index_conflict_iterator **') err = C.git_index_conflict_iterator_new(citer, index._index) check_error(err) self._index = index self._iter = citer[0] def __del__(self): C.git_index_conflict_iterator_free(self._iter) def __iter__(self): return self def __next__(self): cancestor = ffi.new('git_index_entry **') cours = ffi.new('git_index_entry **') ctheirs = ffi.new('git_index_entry **') err = C.git_index_conflict_next(cancestor, cours, ctheirs, self._iter) if err == C.GIT_ITEROVER: raise StopIteration check_error(err) ancestor = IndexEntry._from_c(cancestor[0]) ours = IndexEntry._from_c(cours[0]) theirs = IndexEntry._from_c(ctheirs[0]) return ancestor, ours, theirs libgit2-pygit2-d86662e/pygit2/legacyenums.py000066400000000000000000000127401521317216500207430ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """ GIT_* enum values for compatibility with legacy code. These values are deprecated starting with pygit2 1.14. User programs should migrate to the enum classes defined in `pygit2.enums`. Note that our C module _pygit2 already exports many libgit2 enums (which are all imported by __init__.py). This file only exposes the enums that are not available through _pygit2. """ from . import enums GIT_FEATURE_THREADS = enums.Feature.THREADS GIT_FEATURE_HTTPS = enums.Feature.HTTPS GIT_FEATURE_SSH = enums.Feature.SSH GIT_FEATURE_NSEC = enums.Feature.NSEC GIT_REPOSITORY_INIT_BARE = enums.RepositoryInitFlag.BARE GIT_REPOSITORY_INIT_NO_REINIT = enums.RepositoryInitFlag.NO_REINIT GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = enums.RepositoryInitFlag.NO_DOTGIT_DIR GIT_REPOSITORY_INIT_MKDIR = enums.RepositoryInitFlag.MKDIR GIT_REPOSITORY_INIT_MKPATH = enums.RepositoryInitFlag.MKPATH GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = enums.RepositoryInitFlag.EXTERNAL_TEMPLATE GIT_REPOSITORY_INIT_RELATIVE_GITLINK = enums.RepositoryInitFlag.RELATIVE_GITLINK GIT_REPOSITORY_INIT_SHARED_UMASK = enums.RepositoryInitMode.SHARED_UMASK GIT_REPOSITORY_INIT_SHARED_GROUP = enums.RepositoryInitMode.SHARED_GROUP GIT_REPOSITORY_INIT_SHARED_ALL = enums.RepositoryInitMode.SHARED_ALL GIT_REPOSITORY_OPEN_NO_SEARCH = enums.RepositoryOpenFlag.NO_SEARCH GIT_REPOSITORY_OPEN_CROSS_FS = enums.RepositoryOpenFlag.CROSS_FS GIT_REPOSITORY_OPEN_BARE = enums.RepositoryOpenFlag.BARE GIT_REPOSITORY_OPEN_NO_DOTGIT = enums.RepositoryOpenFlag.NO_DOTGIT GIT_REPOSITORY_OPEN_FROM_ENV = enums.RepositoryOpenFlag.FROM_ENV GIT_REPOSITORY_STATE_NONE = enums.RepositoryState.NONE GIT_REPOSITORY_STATE_MERGE = enums.RepositoryState.MERGE GIT_REPOSITORY_STATE_REVERT = enums.RepositoryState.REVERT GIT_REPOSITORY_STATE_REVERT_SEQUENCE = enums.RepositoryState.REVERT_SEQUENCE GIT_REPOSITORY_STATE_CHERRYPICK = enums.RepositoryState.CHERRYPICK GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE = enums.RepositoryState.CHERRYPICK_SEQUENCE GIT_REPOSITORY_STATE_BISECT = enums.RepositoryState.BISECT GIT_REPOSITORY_STATE_REBASE = enums.RepositoryState.REBASE GIT_REPOSITORY_STATE_REBASE_INTERACTIVE = enums.RepositoryState.REBASE_INTERACTIVE GIT_REPOSITORY_STATE_REBASE_MERGE = enums.RepositoryState.REBASE_MERGE GIT_REPOSITORY_STATE_APPLY_MAILBOX = enums.RepositoryState.APPLY_MAILBOX GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE = ( enums.RepositoryState.APPLY_MAILBOX_OR_REBASE ) GIT_ATTR_CHECK_FILE_THEN_INDEX = enums.AttrCheck.FILE_THEN_INDEX GIT_ATTR_CHECK_INDEX_THEN_FILE = enums.AttrCheck.INDEX_THEN_FILE GIT_ATTR_CHECK_INDEX_ONLY = enums.AttrCheck.INDEX_ONLY GIT_ATTR_CHECK_NO_SYSTEM = enums.AttrCheck.NO_SYSTEM GIT_ATTR_CHECK_INCLUDE_HEAD = enums.AttrCheck.INCLUDE_HEAD GIT_ATTR_CHECK_INCLUDE_COMMIT = enums.AttrCheck.INCLUDE_COMMIT GIT_FETCH_PRUNE_UNSPECIFIED = enums.FetchPrune.UNSPECIFIED GIT_FETCH_PRUNE = enums.FetchPrune.PRUNE GIT_FETCH_NO_PRUNE = enums.FetchPrune.NO_PRUNE GIT_CHECKOUT_NOTIFY_NONE = enums.CheckoutNotify.NONE GIT_CHECKOUT_NOTIFY_CONFLICT = enums.CheckoutNotify.CONFLICT GIT_CHECKOUT_NOTIFY_DIRTY = enums.CheckoutNotify.DIRTY GIT_CHECKOUT_NOTIFY_UPDATED = enums.CheckoutNotify.UPDATED GIT_CHECKOUT_NOTIFY_UNTRACKED = enums.CheckoutNotify.UNTRACKED GIT_CHECKOUT_NOTIFY_IGNORED = enums.CheckoutNotify.IGNORED GIT_CHECKOUT_NOTIFY_ALL = enums.CheckoutNotify.ALL GIT_STASH_APPLY_PROGRESS_NONE = enums.StashApplyProgress.NONE GIT_STASH_APPLY_PROGRESS_LOADING_STASH = enums.StashApplyProgress.LOADING_STASH GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX = enums.StashApplyProgress.ANALYZE_INDEX GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED = enums.StashApplyProgress.ANALYZE_MODIFIED GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED = enums.StashApplyProgress.ANALYZE_UNTRACKED GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED = ( enums.StashApplyProgress.CHECKOUT_UNTRACKED ) GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED = enums.StashApplyProgress.CHECKOUT_MODIFIED GIT_STASH_APPLY_PROGRESS_DONE = enums.StashApplyProgress.DONE GIT_CREDENTIAL_USERPASS_PLAINTEXT = enums.CredentialType.USERPASS_PLAINTEXT GIT_CREDENTIAL_SSH_KEY = enums.CredentialType.SSH_KEY GIT_CREDENTIAL_SSH_CUSTOM = enums.CredentialType.SSH_CUSTOM GIT_CREDENTIAL_DEFAULT = enums.CredentialType.DEFAULT GIT_CREDENTIAL_SSH_INTERACTIVE = enums.CredentialType.SSH_INTERACTIVE GIT_CREDENTIAL_USERNAME = enums.CredentialType.USERNAME GIT_CREDENTIAL_SSH_MEMORY = enums.CredentialType.SSH_MEMORY libgit2-pygit2-d86662e/pygit2/options.py000066400000000000000000000625701521317216500201300ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """ Libgit2 global options management using CFFI. """ from __future__ import annotations # Import only for type checking to avoid circular imports from typing import TYPE_CHECKING, Any, Literal, cast, overload from .errors import check_error from .ffi import C, ffi from .utils import to_bytes, to_str if TYPE_CHECKING: from ._libgit2.ffi import NULL_TYPE, ArrayC, char, char_pointer from .enums import ConfigLevel, ObjectType, Option # Export GIT_OPT constants for backward compatibility GIT_OPT_GET_MWINDOW_SIZE: int = C.GIT_OPT_GET_MWINDOW_SIZE GIT_OPT_SET_MWINDOW_SIZE: int = C.GIT_OPT_SET_MWINDOW_SIZE GIT_OPT_GET_MWINDOW_MAPPED_LIMIT: int = C.GIT_OPT_GET_MWINDOW_MAPPED_LIMIT GIT_OPT_SET_MWINDOW_MAPPED_LIMIT: int = C.GIT_OPT_SET_MWINDOW_MAPPED_LIMIT GIT_OPT_GET_SEARCH_PATH: int = C.GIT_OPT_GET_SEARCH_PATH GIT_OPT_SET_SEARCH_PATH: int = C.GIT_OPT_SET_SEARCH_PATH GIT_OPT_SET_CACHE_OBJECT_LIMIT: int = C.GIT_OPT_SET_CACHE_OBJECT_LIMIT GIT_OPT_SET_CACHE_MAX_SIZE: int = C.GIT_OPT_SET_CACHE_MAX_SIZE GIT_OPT_ENABLE_CACHING: int = C.GIT_OPT_ENABLE_CACHING GIT_OPT_GET_CACHED_MEMORY: int = C.GIT_OPT_GET_CACHED_MEMORY GIT_OPT_GET_TEMPLATE_PATH: int = C.GIT_OPT_GET_TEMPLATE_PATH GIT_OPT_SET_TEMPLATE_PATH: int = C.GIT_OPT_SET_TEMPLATE_PATH GIT_OPT_SET_SSL_CERT_LOCATIONS: int = C.GIT_OPT_SET_SSL_CERT_LOCATIONS GIT_OPT_SET_USER_AGENT: int = C.GIT_OPT_SET_USER_AGENT GIT_OPT_ENABLE_STRICT_OBJECT_CREATION: int = C.GIT_OPT_ENABLE_STRICT_OBJECT_CREATION GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION: int = ( C.GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION ) GIT_OPT_SET_SSL_CIPHERS: int = C.GIT_OPT_SET_SSL_CIPHERS GIT_OPT_GET_USER_AGENT: int = C.GIT_OPT_GET_USER_AGENT GIT_OPT_ENABLE_OFS_DELTA: int = C.GIT_OPT_ENABLE_OFS_DELTA GIT_OPT_ENABLE_FSYNC_GITDIR: int = C.GIT_OPT_ENABLE_FSYNC_GITDIR GIT_OPT_GET_WINDOWS_SHAREMODE: int = C.GIT_OPT_GET_WINDOWS_SHAREMODE GIT_OPT_SET_WINDOWS_SHAREMODE: int = C.GIT_OPT_SET_WINDOWS_SHAREMODE GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION: int = C.GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION GIT_OPT_SET_ALLOCATOR: int = C.GIT_OPT_SET_ALLOCATOR GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY: int = C.GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY GIT_OPT_GET_PACK_MAX_OBJECTS: int = C.GIT_OPT_GET_PACK_MAX_OBJECTS GIT_OPT_SET_PACK_MAX_OBJECTS: int = C.GIT_OPT_SET_PACK_MAX_OBJECTS GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS: int = C.GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS GIT_OPT_GET_MWINDOW_FILE_LIMIT: int = C.GIT_OPT_GET_MWINDOW_FILE_LIMIT GIT_OPT_SET_MWINDOW_FILE_LIMIT: int = C.GIT_OPT_SET_MWINDOW_FILE_LIMIT GIT_OPT_GET_OWNER_VALIDATION: int = C.GIT_OPT_GET_OWNER_VALIDATION GIT_OPT_SET_OWNER_VALIDATION: int = C.GIT_OPT_SET_OWNER_VALIDATION GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE: int = C.GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE GIT_OPT_SET_ODB_PACKED_PRIORITY: int = C.GIT_OPT_SET_ODB_PACKED_PRIORITY GIT_OPT_SET_ODB_LOOSE_PRIORITY: int = C.GIT_OPT_SET_ODB_LOOSE_PRIORITY GIT_OPT_GET_EXTENSIONS: int = C.GIT_OPT_GET_EXTENSIONS GIT_OPT_SET_EXTENSIONS: int = C.GIT_OPT_SET_EXTENSIONS GIT_OPT_GET_HOMEDIR: int = C.GIT_OPT_GET_HOMEDIR GIT_OPT_SET_HOMEDIR: int = C.GIT_OPT_SET_HOMEDIR GIT_OPT_SET_SERVER_CONNECT_TIMEOUT: int = C.GIT_OPT_SET_SERVER_CONNECT_TIMEOUT GIT_OPT_GET_SERVER_CONNECT_TIMEOUT: int = C.GIT_OPT_GET_SERVER_CONNECT_TIMEOUT GIT_OPT_SET_SERVER_TIMEOUT: int = C.GIT_OPT_SET_SERVER_TIMEOUT GIT_OPT_GET_SERVER_TIMEOUT: int = C.GIT_OPT_GET_SERVER_TIMEOUT GIT_OPT_GET_USER_AGENT_PRODUCT: int = C.GIT_OPT_GET_USER_AGENT_PRODUCT GIT_OPT_SET_USER_AGENT_PRODUCT: int = C.GIT_OPT_SET_USER_AGENT_PRODUCT GIT_OPT_ADD_SSL_X509_CERT: int = C.GIT_OPT_ADD_SSL_X509_CERT NOT_PASSED = object() def check_args(option: Option, arg1: Any, arg2: Any, expected: int) -> None: if expected == 0 and (arg1 is not NOT_PASSED or arg2 is not NOT_PASSED): raise TypeError(f'option({option}) takes no additional arguments') if expected == 1 and (arg1 is NOT_PASSED or arg2 is not NOT_PASSED): raise TypeError(f'option({option}, x) requires 1 additional argument') if expected == 2 and (arg1 is NOT_PASSED or arg2 is NOT_PASSED): raise TypeError(f'option({option}, x, y) requires 2 additional arguments') @overload def option( option_type: Literal[ Option.GET_MWINDOW_SIZE, Option.GET_MWINDOW_MAPPED_LIMIT, Option.GET_MWINDOW_FILE_LIMIT, ], ) -> int: ... @overload def option( option_type: Literal[ Option.SET_MWINDOW_SIZE, Option.SET_MWINDOW_MAPPED_LIMIT, Option.SET_MWINDOW_FILE_LIMIT, Option.SET_CACHE_MAX_SIZE, ], arg1: int, # value ) -> None: ... @overload def option( option_type: Literal[Option.GET_SEARCH_PATH], arg1: ConfigLevel, # value ) -> str: ... @overload def option( option_type: Literal[Option.SET_SEARCH_PATH], arg1: ConfigLevel, # type arg2: str, # value ) -> None: ... @overload def option( option_type: Literal[Option.SET_CACHE_OBJECT_LIMIT], arg1: ObjectType, # type arg2: int, # limit ) -> None: ... @overload def option(option_type: Literal[Option.GET_CACHED_MEMORY]) -> tuple[int, int]: ... @overload def option( option_type: Literal[Option.SET_SSL_CERT_LOCATIONS], arg1: str | bytes | None, # cert_file arg2: str | bytes | None, # cert_dir ) -> None: ... @overload def option( option_type: Literal[ Option.ENABLE_CACHING, Option.ENABLE_STRICT_OBJECT_CREATION, Option.ENABLE_STRICT_SYMBOLIC_REF_CREATION, Option.ENABLE_OFS_DELTA, Option.ENABLE_FSYNC_GITDIR, Option.ENABLE_STRICT_HASH_VERIFICATION, Option.ENABLE_UNSAVED_INDEX_SAFETY, Option.DISABLE_PACK_KEEP_FILE_CHECKS, Option.SET_OWNER_VALIDATION, ], arg1: bool, # value ) -> None: ... @overload def option(option_type: Literal[Option.GET_OWNER_VALIDATION]) -> bool: ... @overload def option( option_type: Literal[ Option.GET_TEMPLATE_PATH, Option.GET_USER_AGENT, Option.GET_HOMEDIR, Option.GET_USER_AGENT_PRODUCT, ], ) -> str | None: ... @overload def option( option_type: Literal[ Option.SET_TEMPLATE_PATH, Option.SET_USER_AGENT, Option.SET_SSL_CIPHERS, Option.SET_HOMEDIR, Option.SET_USER_AGENT_PRODUCT, ], arg1: str | bytes, # value ) -> None: ... @overload def option( option_type: Literal[ Option.GET_WINDOWS_SHAREMODE, Option.GET_PACK_MAX_OBJECTS, Option.GET_SERVER_CONNECT_TIMEOUT, Option.GET_SERVER_TIMEOUT, ], ) -> int: ... @overload def option( option_type: Literal[ Option.SET_WINDOWS_SHAREMODE, Option.SET_PACK_MAX_OBJECTS, Option.ENABLE_HTTP_EXPECT_CONTINUE, Option.SET_ODB_PACKED_PRIORITY, Option.SET_ODB_LOOSE_PRIORITY, Option.SET_SERVER_CONNECT_TIMEOUT, Option.SET_SERVER_TIMEOUT, ], arg1: int, # value ) -> None: ... @overload def option(option_type: Literal[Option.GET_EXTENSIONS]) -> list[str]: ... @overload def option( option_type: Literal[Option.SET_EXTENSIONS], arg1: list[str], # extensions arg2: int, # length ) -> None: ... @overload def option( option_type: Literal[Option.ADD_SSL_X509_CERT], arg1: str | bytes, # certificate ) -> None: ... # Fallback overload for generic Option values (used in tests) @overload def option(option_type: Option, arg1: Any = ..., arg2: Any = ...) -> Any: ... def option(option_type: Option, arg1: Any = NOT_PASSED, arg2: Any = NOT_PASSED) -> Any: """ Get or set a libgit2 option. Parameters: GIT_OPT_GET_SEARCH_PATH, level Get the config search path for the given level. GIT_OPT_SET_SEARCH_PATH, level, path Set the config search path for the given level. GIT_OPT_GET_MWINDOW_SIZE Get the maximum mmap window size. GIT_OPT_SET_MWINDOW_SIZE, size Set the maximum mmap window size. GIT_OPT_GET_MWINDOW_FILE_LIMIT Get the maximum number of files that will be mapped at any time by the library. GIT_OPT_SET_MWINDOW_FILE_LIMIT, size Set the maximum number of files that can be mapped at any time by the library. The default (0) is unlimited. GIT_OPT_GET_OWNER_VALIDATION Gets the owner validation setting for repository directories. GIT_OPT_SET_OWNER_VALIDATION, enabled Set that repository directories should be owned by the current user. The default is to validate ownership. GIT_OPT_GET_TEMPLATE_PATH Get the default template path. GIT_OPT_SET_TEMPLATE_PATH, path Set the default template path. GIT_OPT_GET_USER_AGENT Get the user agent string. GIT_OPT_SET_USER_AGENT, user_agent Set the user agent string. GIT_OPT_GET_PACK_MAX_OBJECTS Get the maximum number of objects to include in a pack. GIT_OPT_SET_PACK_MAX_OBJECTS, count Set the maximum number of objects to include in a pack. """ result: str | None | list[str] if option_type in ( C.GIT_OPT_GET_MWINDOW_SIZE, C.GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, C.GIT_OPT_GET_MWINDOW_FILE_LIMIT, ): check_args(option_type, arg1, arg2, 0) size_ptr = ffi.new('size_t *') err = C.git_libgit2_opts(option_type, size_ptr) check_error(err) return size_ptr[0] elif option_type in ( C.GIT_OPT_SET_MWINDOW_SIZE, C.GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, C.GIT_OPT_SET_MWINDOW_FILE_LIMIT, ): check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError(f'option value must be an integer, not {type(arg1)}') size = arg1 if size < 0: raise ValueError('size must be non-negative') err = C.git_libgit2_opts(option_type, ffi.cast('size_t', size)) check_error(err) return None elif option_type == C.GIT_OPT_GET_SEARCH_PATH: check_args(option_type, arg1, arg2, 1) level = int(arg1) # Convert enum to int buf = ffi.new('git_buf *') err = C.git_libgit2_opts(option_type, ffi.cast('int', level), buf) check_error(err) try: if buf.ptr != ffi.NULL: result = to_str(ffi.string(buf.ptr)) else: result = None finally: C.git_buf_dispose(buf) return result elif option_type == C.GIT_OPT_SET_SEARCH_PATH: check_args(option_type, arg1, arg2, 2) level = int(arg1) # Convert enum to int path = arg2 path_cdata: ArrayC[char] | NULL_TYPE if path is None: path_cdata = ffi.NULL else: path_bytes = to_bytes(path) path_cdata = ffi.new('char[]', path_bytes) err = C.git_libgit2_opts(option_type, ffi.cast('int', level), path_cdata) check_error(err) return None elif option_type == C.GIT_OPT_SET_CACHE_OBJECT_LIMIT: check_args(option_type, arg1, arg2, 2) object_type = int(arg1) # Convert enum to int if not isinstance(arg2, int): raise TypeError( f'option value must be an integer, not {type(arg2).__name__}' ) size = arg2 if size < 0: raise ValueError('size must be non-negative') err = C.git_libgit2_opts( option_type, ffi.cast('int', object_type), ffi.cast('size_t', size) ) check_error(err) return None elif option_type == C.GIT_OPT_SET_CACHE_MAX_SIZE: check_args(option_type, arg1, arg2, 1) size = arg1 if not isinstance(size, int): raise TypeError( f'option value must be an integer, not {type(size).__name__}' ) err = C.git_libgit2_opts(option_type, ffi.cast('ssize_t', size)) check_error(err) return None elif option_type == C.GIT_OPT_GET_CACHED_MEMORY: check_args(option_type, arg1, arg2, 0) current_ptr = ffi.new('ssize_t *') allowed_ptr = ffi.new('ssize_t *') err = C.git_libgit2_opts(option_type, current_ptr, allowed_ptr) check_error(err) return (current_ptr[0], allowed_ptr[0]) elif option_type == C.GIT_OPT_SET_SSL_CERT_LOCATIONS: check_args(option_type, arg1, arg2, 2) cert_file = arg1 cert_dir = arg2 cert_file_cdata: ArrayC[char] | NULL_TYPE if cert_file is None: cert_file_cdata = ffi.NULL else: cert_file_bytes = to_bytes(cert_file) cert_file_cdata = ffi.new('char[]', cert_file_bytes) cert_dir_cdata: ArrayC[char] | NULL_TYPE if cert_dir is None: cert_dir_cdata = ffi.NULL else: cert_dir_bytes = to_bytes(cert_dir) cert_dir_cdata = ffi.new('char[]', cert_dir_bytes) err = C.git_libgit2_opts(option_type, cert_file_cdata, cert_dir_cdata) check_error(err) return None # Handle boolean/int enable/disable options elif option_type in ( C.GIT_OPT_ENABLE_CACHING, C.GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, C.GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, C.GIT_OPT_ENABLE_OFS_DELTA, C.GIT_OPT_ENABLE_FSYNC_GITDIR, C.GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, C.GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, C.GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, C.GIT_OPT_SET_OWNER_VALIDATION, ): check_args(option_type, arg1, arg2, 1) enabled = arg1 # Convert to int (0 or 1) value = 1 if enabled else 0 err = C.git_libgit2_opts(option_type, ffi.cast('int', value)) check_error(err) return None elif option_type == C.GIT_OPT_GET_OWNER_VALIDATION: check_args(option_type, arg1, arg2, 0) enabled_ptr = ffi.new('int *') err = C.git_libgit2_opts(option_type, enabled_ptr) check_error(err) return bool(enabled_ptr[0]) elif option_type == C.GIT_OPT_GET_TEMPLATE_PATH: check_args(option_type, arg1, arg2, 0) buf = ffi.new('git_buf *') err = C.git_libgit2_opts(option_type, buf) check_error(err) try: if buf.ptr != ffi.NULL: result = to_str(ffi.string(buf.ptr)) else: result = None finally: C.git_buf_dispose(buf) return result elif option_type == C.GIT_OPT_SET_TEMPLATE_PATH: check_args(option_type, arg1, arg2, 1) path = arg1 template_path_cdata: ArrayC[char] | NULL_TYPE if path is None: template_path_cdata = ffi.NULL else: path_bytes = to_bytes(path) template_path_cdata = ffi.new('char[]', path_bytes) err = C.git_libgit2_opts(option_type, template_path_cdata) check_error(err) return None elif option_type == C.GIT_OPT_GET_USER_AGENT: check_args(option_type, arg1, arg2, 0) buf = ffi.new('git_buf *') err = C.git_libgit2_opts(option_type, buf) check_error(err) try: if buf.ptr != ffi.NULL: result = to_str(ffi.string(buf.ptr)) else: result = None finally: C.git_buf_dispose(buf) return result elif option_type == C.GIT_OPT_SET_USER_AGENT: check_args(option_type, arg1, arg2, 1) agent = arg1 agent_bytes = to_bytes(agent) agent_cdata = ffi.new('char[]', agent_bytes) err = C.git_libgit2_opts(option_type, agent_cdata) check_error(err) return None elif option_type == C.GIT_OPT_SET_SSL_CIPHERS: check_args(option_type, arg1, arg2, 1) ciphers = arg1 ciphers_bytes = to_bytes(ciphers) ciphers_cdata = ffi.new('char[]', ciphers_bytes) err = C.git_libgit2_opts(option_type, ciphers_cdata) check_error(err) return None # Handle GET_WINDOWS_SHAREMODE elif option_type == C.GIT_OPT_GET_WINDOWS_SHAREMODE: check_args(option_type, arg1, arg2, 0) value_ptr = ffi.new('unsigned int *') err = C.git_libgit2_opts(option_type, value_ptr) check_error(err) return value_ptr[0] # Handle SET_WINDOWS_SHAREMODE elif option_type == C.GIT_OPT_SET_WINDOWS_SHAREMODE: check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError( f'option value must be an integer, not {type(arg1).__name__}' ) value = arg1 if value < 0: raise ValueError('value must be non-negative') err = C.git_libgit2_opts(option_type, ffi.cast('unsigned int', value)) check_error(err) return None # Handle GET_PACK_MAX_OBJECTS elif option_type == C.GIT_OPT_GET_PACK_MAX_OBJECTS: check_args(option_type, arg1, arg2, 0) size_ptr = ffi.new('size_t *') err = C.git_libgit2_opts(option_type, size_ptr) check_error(err) return size_ptr[0] # Handle SET_PACK_MAX_OBJECTS elif option_type == C.GIT_OPT_SET_PACK_MAX_OBJECTS: check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError( f'option value must be an integer, not {type(arg1).__name__}' ) size = arg1 if size < 0: raise ValueError('size must be non-negative') err = C.git_libgit2_opts(option_type, ffi.cast('size_t', size)) check_error(err) return None # Handle ENABLE_HTTP_EXPECT_CONTINUE elif option_type == C.GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE: check_args(option_type, arg1, arg2, 1) enabled = arg1 # Convert to int (0 or 1) value = 1 if enabled else 0 err = C.git_libgit2_opts(option_type, ffi.cast('int', value)) check_error(err) return None # Handle SET_ODB_PACKED_PRIORITY elif option_type == C.GIT_OPT_SET_ODB_PACKED_PRIORITY: check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError( f'option value must be an integer, not {type(arg1).__name__}' ) priority = arg1 err = C.git_libgit2_opts(option_type, ffi.cast('int', priority)) check_error(err) return None # Handle SET_ODB_LOOSE_PRIORITY elif option_type == C.GIT_OPT_SET_ODB_LOOSE_PRIORITY: check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError( f'option value must be an integer, not {type(arg1).__name__}' ) priority = arg1 err = C.git_libgit2_opts(option_type, ffi.cast('int', priority)) check_error(err) return None # Handle GET_EXTENSIONS elif option_type == C.GIT_OPT_GET_EXTENSIONS: check_args(option_type, arg1, arg2, 0) # GET_EXTENSIONS expects a git_strarray pointer strarray = ffi.new('git_strarray *') err = C.git_libgit2_opts(option_type, strarray) check_error(err) result = [] try: if strarray.strings != ffi.NULL: # Cast to the non-NULL type for type checking strings = cast('ArrayC[char_pointer]', strarray.strings) for i in range(strarray.count): if strings[i] != ffi.NULL: result.append(to_str(ffi.string(strings[i]))) finally: # Must dispose of the strarray to free the memory C.git_strarray_dispose(strarray) return result # Handle SET_EXTENSIONS elif option_type == C.GIT_OPT_SET_EXTENSIONS: check_args(option_type, arg1, arg2, 2) extensions = arg1 length = arg2 if not isinstance(extensions, list): raise TypeError('extensions must be a list of strings') if not isinstance(length, int): raise TypeError('length must be an integer') # Create array of char pointers # libgit2 will make its own copies with git__strdup ext_array: ArrayC[char_pointer] = ffi.new('char *[]', len(extensions)) ext_strings: list[ArrayC[char]] = [] # Keep references during the call for i, ext in enumerate(extensions): ext_bytes = to_bytes(ext) ext_string: ArrayC[char] = ffi.new('char[]', ext_bytes) ext_strings.append(ext_string) ext_array[i] = ffi.cast('char *', ext_string) err = C.git_libgit2_opts(option_type, ext_array, ffi.cast('size_t', length)) check_error(err) return None # Handle GET_HOMEDIR elif option_type == C.GIT_OPT_GET_HOMEDIR: check_args(option_type, arg1, arg2, 0) buf = ffi.new('git_buf *') err = C.git_libgit2_opts(option_type, buf) check_error(err) try: if buf.ptr != ffi.NULL: result = to_str(ffi.string(buf.ptr)) else: result = None finally: C.git_buf_dispose(buf) return result # Handle SET_HOMEDIR elif option_type == C.GIT_OPT_SET_HOMEDIR: check_args(option_type, arg1, arg2, 1) path = arg1 homedir_cdata: ArrayC[char] | NULL_TYPE if path is None: homedir_cdata = ffi.NULL else: path_bytes = to_bytes(path) homedir_cdata = ffi.new('char[]', path_bytes) err = C.git_libgit2_opts(option_type, homedir_cdata) check_error(err) return None # Handle GET_SERVER_CONNECT_TIMEOUT elif option_type == C.GIT_OPT_GET_SERVER_CONNECT_TIMEOUT: check_args(option_type, arg1, arg2, 0) timeout_ptr = ffi.new('int *') err = C.git_libgit2_opts(option_type, timeout_ptr) check_error(err) return timeout_ptr[0] # Handle SET_SERVER_CONNECT_TIMEOUT elif option_type == C.GIT_OPT_SET_SERVER_CONNECT_TIMEOUT: check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError( f'option value must be an integer, not {type(arg1).__name__}' ) timeout = arg1 err = C.git_libgit2_opts(option_type, ffi.cast('int', timeout)) check_error(err) return None # Handle GET_SERVER_TIMEOUT elif option_type == C.GIT_OPT_GET_SERVER_TIMEOUT: check_args(option_type, arg1, arg2, 0) timeout_ptr = ffi.new('int *') err = C.git_libgit2_opts(option_type, timeout_ptr) check_error(err) return timeout_ptr[0] # Handle SET_SERVER_TIMEOUT elif option_type == C.GIT_OPT_SET_SERVER_TIMEOUT: check_args(option_type, arg1, arg2, 1) if not isinstance(arg1, int): raise TypeError( f'option value must be an integer, not {type(arg1).__name__}' ) timeout = arg1 err = C.git_libgit2_opts(option_type, ffi.cast('int', timeout)) check_error(err) return None # Handle GET_USER_AGENT_PRODUCT elif option_type == C.GIT_OPT_GET_USER_AGENT_PRODUCT: check_args(option_type, arg1, arg2, 0) buf = ffi.new('git_buf *') err = C.git_libgit2_opts(option_type, buf) check_error(err) try: if buf.ptr != ffi.NULL: result = to_str(ffi.string(buf.ptr)) else: result = None finally: C.git_buf_dispose(buf) return result # Handle SET_USER_AGENT_PRODUCT elif option_type == C.GIT_OPT_SET_USER_AGENT_PRODUCT: check_args(option_type, arg1, arg2, 1) product = arg1 product_bytes = to_bytes(product) product_cdata = ffi.new('char[]', product_bytes) err = C.git_libgit2_opts(option_type, product_cdata) check_error(err) return None # Not implemented - ADD_SSL_X509_CERT requires directly binding with OpenSSL # as the API works accepts a X509* struct. Use GIT_OPT_SET_SSL_CERT_LOCATIONS # instead. elif option_type == C.GIT_OPT_ADD_SSL_X509_CERT: raise NotImplementedError('Use GIT_OPT_SET_SSL_CERT_LOCATIONS instead') # Not implemented - SET_ALLOCATOR is not feasible from Python level # because it requires providing C function pointers for memory management # (malloc, free, etc.) that must handle raw memory at the C level, # which cannot be safely implemented in pure Python. elif option_type == C.GIT_OPT_SET_ALLOCATOR: raise NotImplementedError('Setting a custom allocator not possible from Python') else: raise ValueError(f'Invalid option {option_type}') libgit2-pygit2-d86662e/pygit2/packbuilder.py000066400000000000000000000061771521317216500207230ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from os import PathLike from typing import TYPE_CHECKING # Import from pygit2 from .errors import check_error from .ffi import C, ffi from .utils import to_bytes if TYPE_CHECKING: from pygit2 import Oid, Repository from pygit2.repository import BaseRepository class PackBuilder: def __init__(self, repo: 'Repository | BaseRepository') -> None: cpackbuilder = ffi.new('git_packbuilder **') err = C.git_packbuilder_new(cpackbuilder, repo._repo) check_error(err) self._repo = repo self._packbuilder = cpackbuilder[0] self._cpackbuilder = cpackbuilder @property def _pointer(self) -> bytes: return bytes(ffi.buffer(self._packbuilder)[:]) def __del__(self) -> None: C.git_packbuilder_free(self._packbuilder) def __len__(self) -> int: return C.git_packbuilder_object_count(self._packbuilder) @staticmethod def __convert_object_to_oid(oid: 'Oid') -> 'ffi.GitOidC': git_oid = ffi.new('git_oid *') ffi.buffer(git_oid)[:] = oid.raw[:] return git_oid def add(self, oid: 'Oid') -> None: git_oid = self.__convert_object_to_oid(oid) err = C.git_packbuilder_insert(self._packbuilder, git_oid, ffi.NULL) check_error(err) def add_recur(self, oid: 'Oid') -> None: git_oid = self.__convert_object_to_oid(oid) err = C.git_packbuilder_insert_recur(self._packbuilder, git_oid, ffi.NULL) check_error(err) def set_threads(self, n_threads: int) -> int: return C.git_packbuilder_set_threads(self._packbuilder, n_threads) def write(self, path: str | bytes | PathLike[str] | None = None) -> None: path_bytes = ffi.NULL if path is None else to_bytes(path) err = C.git_packbuilder_write( self._packbuilder, path_bytes, 0, ffi.NULL, ffi.NULL ) check_error(err) @property def written_objects_count(self) -> int: return C.git_packbuilder_written(self._packbuilder) libgit2-pygit2-d86662e/pygit2/py.typed000066400000000000000000000000751521317216500175520ustar00rootroot00000000000000# python type marker, see: https://peps.python.org/pep-0561/ libgit2-pygit2-d86662e/pygit2/references.py000066400000000000000000000075531521317216500205560ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations from collections.abc import Iterator from typing import TYPE_CHECKING from pygit2 import Oid from .enums import ReferenceFilter # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: from ._pygit2 import Reference from .repository import BaseRepository class References: def __init__(self, repository: BaseRepository) -> None: self._repository = repository def __getitem__(self, name: str) -> 'Reference': return self._repository.lookup_reference(name) def get(self, key: str) -> 'Reference' | None: try: return self[key] except KeyError: return None def __iter__(self) -> Iterator[str]: iter = self._repository.references_iterator_init() while True: ref = self._repository.references_iterator_next(iter) if ref: yield ref.name else: return def iterator( self, references_return_type: ReferenceFilter = ReferenceFilter.ALL ) -> Iterator['Reference']: """Creates a new iterator and fetches references for a given repository. Can also filter and pass all refs or only branches or only tags. Parameters: references_return_type: ReferenceFilter Optional specifier to filter references. By default, all references are returned. The following values are accepted: - ReferenceFilter.ALL, fetches all refs, this is the default - ReferenceFilter.BRANCHES, fetches only branches - ReferenceFilter.TAGS, fetches only tags TODO: Add support for filtering by reference types notes and remotes. """ # Enforce ReferenceFilter type - raises ValueError if we're given an invalid value references_return_type = ReferenceFilter(references_return_type) iter = self._repository.references_iterator_init() while True: ref = self._repository.references_iterator_next( iter, references_return_type ) if ref: yield ref else: return def create(self, name: str, target: Oid | str, force: bool = False) -> 'Reference': return self._repository.create_reference(name, target, force) def delete(self, name: str) -> None: self[name].delete() def __contains__(self, name: str) -> bool: return self.get(name) is not None @property def objects(self) -> list['Reference']: return self._repository.listall_reference_objects() def compress(self) -> None: return self._repository.compress_references() libgit2-pygit2-d86662e/pygit2/refspec.py000066400000000000000000000067621521317216500200650ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Callable # Import from pygit2 from .errors import check_error from .ffi import C, ffi from .utils import to_bytes class Refspec: """The constructor is for internal use only.""" def __init__(self, owner, ptr) -> None: self._owner = owner self._refspec = ptr @property def src(self) -> str: """Source or lhs of the refspec""" return ffi.string(C.git_refspec_src(self._refspec)).decode('utf-8') @property def dst(self) -> str: """Destination or rhs of the refspec""" return ffi.string(C.git_refspec_dst(self._refspec)).decode('utf-8') @property def force(self) -> bool: """Whether this refspeca llows non-fast-forward updates""" return bool(C.git_refspec_force(self._refspec)) @property def string(self) -> str: """String which was used to create this refspec""" return ffi.string(C.git_refspec_string(self._refspec)).decode('utf-8') @property def direction(self): """Direction of this refspec (fetch or push)""" return C.git_refspec_direction(self._refspec) def src_matches(self, ref: str) -> bool: """Return True if the given string matches the source of this refspec, False otherwise. """ return bool(C.git_refspec_src_matches(self._refspec, to_bytes(ref))) def dst_matches(self, ref: str) -> bool: """Return True if the given string matches the destination of this refspec, False otherwise.""" return bool(C.git_refspec_dst_matches(self._refspec, to_bytes(ref))) def _transform(self, ref: str, fn: Callable) -> str: buf = ffi.new('git_buf *', (ffi.NULL, 0)) err = fn(buf, self._refspec, to_bytes(ref)) check_error(err) try: return ffi.string(buf.ptr).decode('utf-8') finally: C.git_buf_dispose(buf) def transform(self, ref: str) -> str: """Transform a reference name according to this refspec from the lhs to the rhs. Return an string. """ return self._transform(ref, C.git_refspec_transform) def rtransform(self, ref: str) -> str: """Transform a reference name according to this refspec from the lhs to the rhs. Return an string. """ return self._transform(ref, C.git_refspec_rtransform) libgit2-pygit2-d86662e/pygit2/remotes.py000066400000000000000000000430031521317216500201010ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations import warnings from collections.abc import Generator, Iterator from typing import TYPE_CHECKING, Any, Literal # Import from pygit2 from pygit2 import RemoteCallbacks from . import utils from ._pygit2 import Oid from .callbacks import ( git_fetch_options, git_proxy_options, git_push_options, git_remote_callbacks, ) from .enums import FetchPrune from .errors import check_error from .ffi import C, ffi from .refspec import Refspec from .utils import StrArray, maybe_string, strarray_to_strings, to_bytes # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: from ._libgit2.ffi import GitRemoteC, char_pointer from .repository import BaseRepository class RemoteHead: """ Description of a reference advertised by a remote server, given out on `Remote.list_heads` calls. """ local: bool """Available locally""" oid: Oid loid: Oid name: str | None symref_target: str | None """ If the server sent a symref mapping for this ref, this will point to the target. """ def __init__(self, c_struct: Any) -> None: self.local = bool(c_struct.local) self.oid = Oid(raw=bytes(ffi.buffer(c_struct.oid.id)[:])) self.loid = Oid(raw=bytes(ffi.buffer(c_struct.loid.id)[:])) self.name = maybe_string(c_struct.name) self.symref_target = maybe_string(c_struct.symref_target) class PushUpdate: """ Represents an update which will be performed on the remote during push. """ src_refname: str """The source name of the reference""" dst_refname: str """The name of the reference to update on the server""" src: Oid """The current target of the reference""" dst: Oid """The new target for the reference""" def __init__(self, c_struct: Any) -> None: src_refname = maybe_string(c_struct.src_refname) dst_refname = maybe_string(c_struct.dst_refname) assert src_refname is not None, 'libgit2 returned null src_refname' assert dst_refname is not None, 'libgit2 returned null dst_refname' self.src_refname = src_refname self.dst_refname = dst_refname self.src = Oid(raw=bytes(ffi.buffer(c_struct.src.id)[:])) self.dst = Oid(raw=bytes(ffi.buffer(c_struct.dst.id)[:])) class TransferProgress: """Progress downloading and indexing data during a fetch.""" total_objects: int indexed_objects: int received_objects: int local_objects: int total_deltas: int indexed_deltas: int received_bytes: int def __init__(self, tp: Any) -> None: self.total_objects = tp.total_objects """Total number of objects to download""" self.indexed_objects = tp.indexed_objects """Objects which have been indexed""" self.received_objects = tp.received_objects """Objects which have been received up to now""" self.local_objects = tp.local_objects """Local objects which were used to fix the thin pack""" self.total_deltas = tp.total_deltas """Total number of deltas in the pack""" self.indexed_deltas = tp.indexed_deltas """Deltas which have been indexed""" self.received_bytes = tp.received_bytes """"Number of bytes received up to now""" class Remote: def __init__(self, repo: BaseRepository, ptr: 'GitRemoteC') -> None: """The constructor is for internal use only.""" self._repo = repo self._remote = ptr self._stored_exception = None def __del__(self) -> None: C.git_remote_free(self._remote) @property def name(self) -> str | None: """Name of the remote""" return maybe_string(C.git_remote_name(self._remote)) @property def url(self) -> str | None: """Url of the remote""" return maybe_string(C.git_remote_url(self._remote)) @property def push_url(self) -> str | None: """Push url of the remote""" return maybe_string(C.git_remote_pushurl(self._remote)) def connect( self, callbacks: RemoteCallbacks | None = None, direction: int = C.GIT_DIRECTION_FETCH, proxy: None | bool | str = None, ) -> None: """Connect to the remote. Parameters: proxy : None or True or str Proxy configuration. Can be one of: * `None` (the default) to disable proxy usage * `True` to enable automatic proxy detection * an url to a proxy (`http://proxy.example.org:3128/`) """ with git_proxy_options(self, proxy=proxy) as proxy_opts: with git_remote_callbacks(callbacks) as payload: err = C.git_remote_connect( self._remote, direction, payload.remote_callbacks, proxy_opts, ffi.NULL, ) payload.check_error(err) def fetch( self, refspecs: list[str] | None = None, message: str | None = None, callbacks: RemoteCallbacks | None = None, prune: FetchPrune = FetchPrune.UNSPECIFIED, proxy: None | Literal[True] | str = None, depth: int = 0, ) -> TransferProgress: """Perform a fetch against this remote. Returns a object. Parameters: prune : enums.FetchPrune * `UNSPECIFIED`: use the configuration from the repository. * `PRUNE`: remove any remote branch in the local repository that does not exist in the remote. * `NO_PRUNE`: always keep the remote branches proxy : None or True or str Proxy configuration. Can be one of: * `None` (the default) to disable proxy usage * `True` to enable automatic proxy detection * an url to a proxy (`http://proxy.example.org:3128/`) depth : int Number of commits from the tip of each remote branch history to fetch. If non-zero, the number of commits from the tip of each remote branch history to fetch. If zero, all history is fetched. The default is 0 (all history is fetched). """ with git_fetch_options(callbacks) as payload: opts = payload.fetch_options opts.prune = prune opts.depth = depth with git_proxy_options(self, payload.fetch_options.proxy_opts, proxy): with StrArray(refspecs) as arr: err = C.git_remote_fetch( self._remote, arr.ptr, opts, to_bytes(message) ) payload.check_error(err) return TransferProgress(C.git_remote_stats(self._remote)) def list_heads( self, callbacks: RemoteCallbacks | None = None, proxy: str | None | bool = None, connect: bool = True, ) -> list[RemoteHead]: """ Get the list of references with which the server responds to a new connection. Parameters: callbacks : Passed to connect() proxy : Passed to connect() connect : Whether to connect to the remote first. You can pass False if the remote has already connected. The list remains available after disconnecting as long as a new connection is not initiated. """ if connect: self.connect(callbacks=callbacks, proxy=proxy) refs_ptr = ffi.new('git_remote_head ***') size_ptr = ffi.new('size_t *') err = C.git_remote_ls(refs_ptr, size_ptr, self._remote) check_error(err) num_refs = int(size_ptr[0]) results = [RemoteHead(refs_ptr[0][i]) for i in range(num_refs)] return results def ls_remotes( self, callbacks: RemoteCallbacks | None = None, proxy: str | None | bool = None, connect: bool = True, ) -> list[dict[str, Any]]: """ Deprecated interface to list_heads """ warnings.warn('Use list_heads', DeprecationWarning) heads = self.list_heads(callbacks, proxy, connect) return [ { 'local': h.local, 'oid': h.oid, 'loid': h.loid if h.local else None, 'name': h.name, 'symref_target': h.symref_target, } for h in heads ] def prune(self, callbacks: RemoteCallbacks | None = None) -> None: """Perform a prune against this remote.""" with git_remote_callbacks(callbacks) as payload: err = C.git_remote_prune(self._remote, payload.remote_callbacks) payload.check_error(err) @property def refspec_count(self) -> int: """Total number of refspecs in this remote""" return C.git_remote_refspec_count(self._remote) def get_refspec(self, n: int) -> Refspec: """Return the object at the given position.""" spec = C.git_remote_get_refspec(self._remote, n) return Refspec(self, spec) @property def fetch_refspecs(self) -> list[str]: """Refspecs that will be used for fetching""" specs = ffi.new('git_strarray *') err = C.git_remote_get_fetch_refspecs(specs, self._remote) check_error(err) return strarray_to_strings(specs) @property def push_refspecs(self) -> list[str]: """Refspecs that will be used for pushing""" specs = ffi.new('git_strarray *') err = C.git_remote_get_push_refspecs(specs, self._remote) check_error(err) return strarray_to_strings(specs) def push( self, specs: list[str], callbacks: RemoteCallbacks | None = None, proxy: None | bool | str = None, push_options: None | list[str] = None, threads: int = 1, ) -> None: """ Push the given refspec to the remote. Raises ``GitError`` on protocol error or unpack failure. When the remote has a githook installed, that denies the reference this function will return successfully. Thus it is strongly recommended to install a callback, that implements :py:meth:`RemoteCallbacks.push_update_reference` and check the passed parameters for successful operations. Parameters: specs : [str] Push refspecs to use. callbacks : proxy : None or True or str Proxy configuration. Can be one of: * `None` (the default) to disable proxy usage * `True` to enable automatic proxy detection * an url to a proxy (`http://proxy.example.org:3128/`) push_options : [str] Push options to send to the server, which passes them to the pre-receive as well as the post-receive hook. threads : int If the transport being used to push to the remote requires the creation of a pack file, this controls the number of worker threads used by the packbuilder when creating that pack file to be sent to the remote. If set to 0, the packbuilder will auto-detect the number of threads to create. The default value is 1. """ with git_push_options(callbacks) as payload: opts = payload.push_options opts.pb_parallelism = threads with git_proxy_options(self, payload.push_options.proxy_opts, proxy): with StrArray(specs) as refspecs, StrArray(push_options) as pushopts: pushopts.assign_to(opts.remote_push_options) err = C.git_remote_push(self._remote, refspecs.ptr, opts) payload.check_error(err) class RemoteCollection: """Collection of configured remotes You can use this class to look up and manage the remotes configured in a repository. You can access repositories using index access. E.g. to look up the "origin" remote, you can use >>> repo.remotes["origin"] """ def __init__(self, repo: BaseRepository) -> None: self._repo = repo def __len__(self) -> int: with utils.new_git_strarray() as names: err = C.git_remote_list(names, self._repo._repo) check_error(err) return names.count def __iter__(self) -> Iterator[Remote]: cremote = ffi.new('git_remote **') for name in self._ffi_names(): err = C.git_remote_lookup(cremote, self._repo._repo, name) check_error(err) yield Remote(self._repo, cremote[0]) def __getitem__(self, name: str | int) -> Remote: if isinstance(name, int): return list(self)[name] cremote = ffi.new('git_remote **') err = C.git_remote_lookup(cremote, self._repo._repo, to_bytes(name)) check_error(err) return Remote(self._repo, cremote[0]) def _ffi_names(self) -> Generator['char_pointer', None, None]: with utils.new_git_strarray() as names: err = C.git_remote_list(names, self._repo._repo) check_error(err) for i in range(names.count): yield names.strings[i] # type: ignore[index] def names(self) -> Generator[str | None, None, None]: """An iterator over the names of the available remotes.""" for name in self._ffi_names(): yield maybe_string(name) def create(self, name: str, url: str, fetch: str | None = None) -> Remote: """Create a new remote with the given name and url. Returns a object. If 'fetch' is provided, this fetch refspec will be used instead of the default. """ cremote = ffi.new('git_remote **') name_bytes = to_bytes(name) url_bytes = to_bytes(url) if fetch: fetch_bytes = to_bytes(fetch) err = C.git_remote_create_with_fetchspec( cremote, self._repo._repo, name_bytes, url_bytes, fetch_bytes ) else: err = C.git_remote_create(cremote, self._repo._repo, name_bytes, url_bytes) check_error(err) return Remote(self._repo, cremote[0]) def create_anonymous(self, url: str) -> Remote: """Create a new anonymous (in-memory only) remote with the given URL. Returns a object. """ cremote = ffi.new('git_remote **') url_bytes = to_bytes(url) err = C.git_remote_create_anonymous(cremote, self._repo._repo, url_bytes) check_error(err) return Remote(self._repo, cremote[0]) def rename(self, name: str, new_name: str) -> list[str]: """Rename a remote in the configuration. The refspecs in standard format will be renamed. Returns a list of fetch refspecs (list of strings) which were not in the standard format and thus could not be remapped. """ if not name: raise ValueError('Current remote name must be a non-empty string') if not new_name: raise ValueError('New remote name must be a non-empty string') problems = ffi.new('git_strarray *') err = C.git_remote_rename( problems, self._repo._repo, to_bytes(name), to_bytes(new_name) ) check_error(err) return strarray_to_strings(problems) def delete(self, name: str) -> None: """Remove a remote from the configuration All remote-tracking branches and configuration settings for the remote will be removed. """ err = C.git_remote_delete(self._repo._repo, to_bytes(name)) check_error(err) def set_url(self, name: str, url: str) -> None: """Set the URL for a remote""" err = C.git_remote_set_url(self._repo._repo, to_bytes(name), to_bytes(url)) check_error(err) def set_push_url(self, name: str, url: str) -> None: """Set the push-URL for a remote""" err = C.git_remote_set_pushurl(self._repo._repo, to_bytes(name), to_bytes(url)) check_error(err) def add_fetch(self, name: str, refspec: str) -> None: """Add a fetch refspec (str) to the remote""" err = C.git_remote_add_fetch( self._repo._repo, to_bytes(name), to_bytes(refspec) ) check_error(err) def add_push(self, name: str, refspec: str) -> None: """Add a push refspec (str) to the remote""" err = C.git_remote_add_push(self._repo._repo, to_bytes(name), to_bytes(refspec)) check_error(err) libgit2-pygit2-d86662e/pygit2/repository.py000066400000000000000000001700351521317216500206500ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import tarfile import warnings from collections.abc import Callable, Iterator from io import BytesIO from pathlib import Path from string import hexdigits from time import time from typing import TYPE_CHECKING, Optional, overload # Import from pygit2 from ._pygit2 import ( GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN, Blob, Commit, Diff, InvalidSpecError, Object, Oid, Patch, Reference, Signature, Tree, init_file_backend, ) from ._pygit2 import Repository as _Repository from .blame import Blame from .branches import Branches from .callbacks import ( StashApplyCallbacks, git_checkout_options, git_stash_apply_options, ) from .config import Config from .enums import ( AttrCheck, BlameFlag, CheckoutStrategy, DescribeStrategy, DiffOption, FileMode, FilterMode, MergeFavor, MergeFileFlag, MergeFlag, ObjectType, RepositoryOpenFlag, RepositoryState, ) from .errors import check_error from .ffi import C, ffi from .filter import FilterList from .index import Index, IndexEntry, MergeFileResult from .packbuilder import PackBuilder from .references import References from .remotes import RemoteCollection from .submodules import SubmoduleCollection from .transaction import ReferenceTransaction from .utils import StrArray, maybe_string, to_bytes if TYPE_CHECKING: from pygit2._libgit2.ffi import ( ArrayC, GitMergeOptionsC, GitRepositoryC, _Pointer, char, ) from pygit2._pygit2 import Odb, Refdb, RefdbBackend class BaseRepository(_Repository): _pointer: '_Pointer[GitRepositoryC]' _repo: 'GitRepositoryC' backend: 'RefdbBackend' default_signature: Signature head: Reference head_is_detached: bool head_is_unborn: bool is_bare: bool is_empty: bool is_shallow: bool odb: 'Odb' path: str refdb: 'Refdb' workdir: str references: References remotes: RemoteCollection branches: Branches submodules: SubmoduleCollection def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._common_init() def _common_init(self) -> None: self.branches = Branches(self) self.references = References(self) self.remotes = RemoteCollection(self) self.submodules = SubmoduleCollection(self) self._active_transaction = None # Get the pointer as the contents of a buffer and store it for # later access repo_cptr = ffi.new('git_repository **') ffi.buffer(repo_cptr)[:] = self._pointer[:] self._repo = repo_cptr[0] # Backwards compatible ODB access def read(self, oid: Oid | str) -> tuple[int, bytes]: """read(oid) -> type, data, size Read raw object data from the repository. """ return self.odb.read(oid) def write(self, type: int, data: bytes | str) -> Oid: """write(type, data) -> Oid Write raw object data into the repository. First arg is the object type, the second one a buffer with data. Return the Oid of the created object.""" return self.odb.write(type, data) def pack( self, path: str | Path | None = None, pack_delegate: Callable[[PackBuilder], None] | None = None, n_threads: int | None = None, ) -> int: """Pack the objects in the odb chosen by the pack_delegate function and write `.pack` and `.idx` files for them. Returns: the number of objects written to the pack Parameters: path The path to which the `.pack` and `.idx` files should be written. `None` will write to the default location. pack_delegate The method which will provide add the objects to the pack builder. Defaults to all objects. n_threads The number of threads the `PackBuilder` will spawn. If set to 0, libgit2 will autodetect the number of CPUs. """ def pack_all_objects(pack_builder): for obj in self.odb: pack_builder.add(obj) pack_delegate = pack_delegate or pack_all_objects builder = PackBuilder(self) if n_threads is not None: builder.set_threads(n_threads) pack_delegate(builder) builder.write(path=path) return builder.written_objects_count def hashfile( self, path: str, object_type: ObjectType = ObjectType.BLOB, as_path: str | None = None, ) -> Oid: """Calculate the hash of a file using repository filtering rules. If you simply want to calculate the hash of a file on disk with no filters, you can just use `pygit2.hashfile()`. However, if you want to hash a file in the repository and you want to apply filtering rules (e.g. crlf filters) before generating the SHA, then use this function. Note: if the repository has `core.safecrlf` set to fail and the filtering triggers that failure, then this function will raise an error and not calculate the hash of the file. Returns: Output value of calculated SHA (Oid) Parameters: path Path to file on disk whose contents should be hashed. This may be an absolute path or a relative path, in which case it will be treated as a path within the working directory. object_type The object type to hash (e.g. enums.ObjectType.BLOB) as_path The path to use to look up filtering rules. If this is an empty string then no filters will be applied when calculating the hash. If this is `None` and the `path` parameter is a file within the repository's working directory, then the `path` will be used. """ c_path = to_bytes(path) c_as_path: ffi.NULL_TYPE | bytes if as_path is None: c_as_path = ffi.NULL else: c_as_path = to_bytes(as_path) c_oid = ffi.new('git_oid *') err = C.git_repository_hashfile( c_oid, self._repo, c_path, int(object_type), c_as_path ) check_error(err) oid = Oid(raw=bytes(ffi.buffer(c_oid.id)[:])) return oid def load_filter_list( self, path: str, mode: FilterMode = FilterMode.TO_ODB ) -> FilterList | None: """ Load the filter list for a given path. May return None if there are no filters to apply to this path. Parameters: path Relative path of the file to be filtered mode Filtering direction: ODB to worktree (SMUDGE), or worktree to ODB (CLEAN). """ c_filters = ffi.new('git_filter_list **') c_path = to_bytes(path) c_mode = int(mode) err = C.git_filter_list_load(c_filters, self._repo, ffi.NULL, c_path, c_mode, 0) check_error(err) fl = FilterList._from_c(c_filters[0]) return fl def __iter__(self) -> Iterator[Oid]: return iter(self.odb) # # Mapping interface # def get(self, key: Oid | str, default: Optional[Commit] = None) -> None | Object: value = self.git_object_lookup_prefix(key) return value if (value is not None) else default def __getitem__(self, key: str | Oid) -> Object: value = self.git_object_lookup_prefix(key) if value is None: raise KeyError(key) return value def __contains__(self, key: str | Oid) -> bool: return self.git_object_lookup_prefix(key) is not None def __repr__(self) -> str: return f'pygit2.Repository({repr(self.path)})' # # Configuration # @property def config(self) -> Config: """The configuration file for this repository. If a the configuration hasn't been set yet, the default config for repository will be returned, including global and system configurations (if they are available). """ cconfig = ffi.new('git_config **') err = C.git_repository_config(cconfig, self._repo) check_error(err) return Config.from_c(self, cconfig[0]) @property def config_snapshot(self): """A snapshot for this repositiory's configuration This allows reads over multiple values to use the same version of the configuration files. """ cconfig = ffi.new('git_config **') err = C.git_repository_config_snapshot(cconfig, self._repo) check_error(err) return Config.from_c(self, cconfig[0]) # # References # def create_reference( self, name: str, target: Oid | str, force: bool = False, message: str | None = None, ) -> 'Reference': """Create a new reference "name" which points to an object or to another reference. Based on the type and value of the target parameter, this method tries to guess whether it is a direct or a symbolic reference. Keyword arguments: force: bool If True references will be overridden, otherwise (the default) an exception is raised. message: str Optional message to use for the reflog. Examples:: repo.create_reference('refs/heads/foo', repo.head.target) repo.create_reference('refs/tags/foo', 'refs/heads/master') repo.create_reference('refs/tags/foo', 'bbb78a9cec580') """ direct = isinstance(target, Oid) or ( all(c in hexdigits for c in target) and GIT_OID_MINPREFIXLEN <= len(target) <= GIT_OID_HEXSZ ) # duplicate isinstance call for mypy if direct or isinstance(target, Oid): return self.create_reference_direct(name, target, force, message=message) return self.create_reference_symbolic(name, target, force, message=message) def listall_references(self) -> list[str]: """Return a list with all the references in the repository.""" return list(x.name for x in self.references.iterator()) def listall_reference_objects(self) -> list[Reference]: """Return a list with all the reference objects in the repository.""" return list(x for x in self.references.iterator()) def resolve_refish(self, refish: str) -> tuple[Commit, Reference]: """Convert a reference-like short name "ref-ish" to a valid (commit, reference) pair. If ref-ish points to a commit, the reference element of the result will be None. Examples:: repo.resolve_refish('mybranch') repo.resolve_refish('sometag') repo.resolve_refish('origin/master') repo.resolve_refish('bbb78a9') """ try: reference = self.lookup_reference_dwim(refish) except (KeyError, InvalidSpecError): reference = None commit = self.revparse_single(refish) else: commit = reference.peel(Commit) # type: ignore return (commit, reference) # type: ignore def transaction(self) -> ReferenceTransaction: """Create a new reference transaction. Returns a context manager that commits all reference updates atomically when the context exits successfully, or performs no updates if an exception is raised. Example:: with repo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_oid, message='Update') """ txn = ReferenceTransaction(self) return txn # # Checkout # def checkout_head(self, **kwargs): """Checkout HEAD For arguments, see Repository.checkout(). """ with git_checkout_options(**kwargs) as payload: err = C.git_checkout_head(self._repo, payload.checkout_options) payload.check_error(err) def checkout_index(self, index=None, **kwargs): """Checkout the given index or the repository's index For arguments, see Repository.checkout(). """ with git_checkout_options(**kwargs) as payload: err = C.git_checkout_index( self._repo, index._index if index else ffi.NULL, payload.checkout_options, ) payload.check_error(err) def checkout_tree(self, treeish, **kwargs): """Checkout the given treeish For arguments, see Repository.checkout(). """ with git_checkout_options(**kwargs) as payload: cptr = ffi.new('git_object **') ffi.buffer(cptr)[:] = treeish._pointer[:] err = C.git_checkout_tree(self._repo, cptr[0], payload.checkout_options) payload.check_error(err) def checkout( self, refname: str | None | Reference = None, **kwargs, ) -> None: """ Checkout the given reference using the given strategy, and update the HEAD. The reference may be a reference name or a Reference object. The default strategy is SAFE | RECREATE_MISSING. If no reference is given, checkout from the index. Parameters: refname : str or Reference The reference to checkout. After checkout, the current branch will be switched to this one. strategy : CheckoutStrategy A ``CheckoutStrategy`` value. The default is ``SAFE | RECREATE_MISSING``. directory : str Alternative checkout path to workdir. paths : list[str] A list of files to checkout from the given reference. If paths is provided, HEAD will not be set to the reference. callbacks : CheckoutCallbacks Optional. Supply a `callbacks` object to get information about conflicted files, updated files, etc. as the checkout is being performed. The callbacks can also abort the checkout prematurely. The callbacks should be an object which inherits from `pyclass:CheckoutCallbacks`. It should implement the callbacks as overridden methods. Examples: * To checkout from the HEAD, just pass 'HEAD':: >>> checkout('HEAD') This is identical to calling checkout_head(). """ # Case 1: Checkout index if refname is None: return self.checkout_index(**kwargs) # Case 2: Checkout head if refname == 'HEAD': return self.checkout_head(**kwargs) # Case 3: Reference if isinstance(refname, Reference): reference = refname refname = refname.name else: reference = self.lookup_reference(refname) oid = reference.resolve().target treeish = self[oid] self.checkout_tree(treeish, **kwargs) if 'paths' not in kwargs: self.set_head(refname) # # Setting HEAD # def set_head(self, target: Oid | str) -> None: """ Set HEAD to point to the given target. Parameters: target The new target for HEAD. Can be a string or Oid (to detach). """ if isinstance(target, Oid): oid = ffi.new('git_oid *') ffi.buffer(oid)[:] = target.raw[:] err = C.git_repository_set_head_detached(self._repo, oid) check_error(err) return # if it's a string, then it's a reference name err = C.git_repository_set_head(self._repo, to_bytes(target)) check_error(err) # # Diff # def __whatever_to_tree_or_blob(self, obj): if obj is None: return None # If it's a string, then it has to be valid revspec if isinstance(obj, str) or isinstance(obj, bytes): obj = self.revparse_single(obj) elif isinstance(obj, Oid): obj = self[obj] # First we try to get to a blob try: obj = obj.peel(Blob) except Exception: # And if that failed, try to get a tree, raising a type # error if that still doesn't work try: obj = obj.peel(Tree) except Exception: raise TypeError(f'unexpected "{type(obj)}"') return obj @overload def diff( self, a: None | str | bytes | Commit | Oid | Reference = None, b: None | str | bytes | Commit | Oid | Reference = None, cached: bool = False, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Diff: ... @overload def diff( self, a: Blob | None = None, b: Blob | None = None, cached: bool = False, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Patch: ... def diff( self, a: None | Blob | str | bytes | Commit | Oid | Reference = None, b: None | Blob | str | bytes | Commit | Oid | Reference = None, cached: bool = False, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, ) -> Diff | Patch: """ Show changes between the working tree and the index or a tree, changes between the index and a tree, changes between two trees, or changes between two blobs. Keyword arguments: a None, a str (that refers to an Object, see revparse_single()) or a Reference object. If None, b must be None, too. In this case the working directory is compared with the index. Otherwise the referred object is compared to 'b'. b None, a str (that refers to an Object, see revparse_single()) or a Reference object. If None, the working directory is compared to 'a'. (except 'cached' is True, in which case the index is compared to 'a'). Otherwise the referred object is compared to 'a' cached If 'b' is None, by default the working directory is compared to 'a'. If 'cached' is set to True, the index/staging area is used for comparing. flags A combination of enums.DiffOption constants. context_lines The number of unchanged lines that define the boundary of a hunk (and to display before and after) interhunk_lines The maximum number of unchanged lines between hunk boundaries before the hunks will be merged into a one Examples:: # Changes in the working tree not yet staged for the next commit >>> diff() # Changes between the index and your last commit >>> diff(cached=True) # Changes in the working tree since your last commit >>> diff('HEAD') # Changes between commits >>> t0 = revparse_single('HEAD') >>> t1 = revparse_single('HEAD^') >>> diff(t0, t1) >>> diff('HEAD', 'HEAD^') # equivalent If you want to diff a tree against an empty tree, use the low level API (Tree.diff_to_tree()) directly. """ a = self.__whatever_to_tree_or_blob(a) b = self.__whatever_to_tree_or_blob(b) options = { 'flags': int(flags), 'context_lines': context_lines, 'interhunk_lines': interhunk_lines, } # Case 1: Diff tree to tree if isinstance(a, Tree) and isinstance(b, Tree): return a.diff_to_tree(b, **options) # type: ignore[arg-type] # Case 2: Index to workdir elif a is None and b is None: return self.index.diff_to_workdir(**options) # Case 3: Diff tree to index or workdir elif isinstance(a, Tree) and b is None: if cached: return a.diff_to_index(self.index, **options) # type: ignore[arg-type] else: return a.diff_to_workdir(**options) # type: ignore[arg-type] # Case 4: Diff blob to blob if isinstance(a, Blob) and isinstance(b, Blob): return a.diff(b, **options) # type: ignore[arg-type] raise ValueError('Only blobs and treeish can be diffed') def state(self) -> RepositoryState: """Determines the state of a git repository - ie, whether an operation (merge, cherry-pick, etc) is in progress. Returns a RepositoryState constant. """ cstate: int = C.git_repository_state(self._repo) try: return RepositoryState(cstate) except ValueError: # Some value not in the IntEnum - newer libgit2 version? return cstate # type: ignore[return-value] def state_cleanup(self) -> None: """Remove all the metadata associated with an ongoing command like merge, revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc. """ C.git_repository_state_cleanup(self._repo) # # blame # def blame( self, path: str, flags: BlameFlag = BlameFlag.NORMAL, min_match_characters: int | None = None, newest_commit: Oid | str | None = None, oldest_commit: Oid | str | None = None, min_line: int | None = None, max_line: int | None = None, ) -> Blame: """ Return a Blame object for a single file. Parameters: path Path to the file to blame. flags An enums.BlameFlag constant. min_match_characters The number of alphanum chars that must be detected as moving/copying within a file for it to associate those lines with the parent commit. newest_commit The id of the newest commit to consider. oldest_commit The id of the oldest commit to consider. min_line The first line in the file to blame. max_line The last line in the file to blame. Examples:: repo.blame('foo.c', flags=enums.BlameFlag.IGNORE_WHITESPACE) """ options = ffi.new('git_blame_options *') C.git_blame_options_init(options, C.GIT_BLAME_OPTIONS_VERSION) if flags: options.flags = int(flags) if min_match_characters: options.min_match_characters = min_match_characters if newest_commit: if not isinstance(newest_commit, Oid): newest_commit = Oid(hex=newest_commit) ffi.buffer(ffi.addressof(options, 'newest_commit'))[:] = newest_commit.raw if oldest_commit: if not isinstance(oldest_commit, Oid): oldest_commit = Oid(hex=oldest_commit) ffi.buffer(ffi.addressof(options, 'oldest_commit'))[:] = oldest_commit.raw if min_line: options.min_line = min_line if max_line: options.max_line = max_line cblame = ffi.new('git_blame **') err = C.git_blame_file(cblame, self._repo, to_bytes(path), options) check_error(err) return Blame._from_c(self, cblame[0]) # # Index # @property def index(self): """Index representing the repository's index file.""" cindex = ffi.new('git_index **') err = C.git_repository_index(cindex, self._repo) check_error(err, io=True) return Index.from_c(self, cindex) # # Merging # @staticmethod def _merge_options( favor: int | MergeFavor, flags: int | MergeFlag, file_flags: int | MergeFileFlag ) -> 'GitMergeOptionsC': """Return a 'git_merge_opts *'""" # Check arguments type if not isinstance(favor, (int, MergeFavor)): raise TypeError('favor argument must be MergeFavor') if not isinstance(flags, (int, MergeFlag)): raise TypeError('flags argument must be MergeFlag') if not isinstance(file_flags, (int, MergeFileFlag)): raise TypeError('file_flags argument must be MergeFileFlag') opts = ffi.new('git_merge_options *') err = C.git_merge_options_init(opts, C.GIT_MERGE_OPTIONS_VERSION) check_error(err) opts.file_favor = int(favor) opts.flags = int(flags) opts.file_flags = int(file_flags) return opts def merge_file_from_index( self, ancestor: 'IndexEntry | None', ours: 'IndexEntry | None', theirs: 'IndexEntry | None', use_deprecated: bool = True, ) -> 'str | MergeFileResult | None': """Merge files from index. Returns: A string with the content of the file containing possible conflicts if use_deprecated==True. If use_deprecated==False then it returns an instance of MergeFileResult. ancestor The index entry which will be used as a common ancestor. ours The index entry to take as "ours" or base. theirs The index entry which will be merged into "ours" use_deprecated This controls what will be returned. If use_deprecated==True (default), a string with the contents of the file will be returned. An instance of MergeFileResult will be returned otherwise. """ cmergeresult = ffi.new('git_merge_file_result *') cancestor, ancestor_str_ref = ( ancestor._to_c() if ancestor is not None else (ffi.NULL, ffi.NULL) ) cours, ours_str_ref = ours._to_c() if ours is not None else (ffi.NULL, ffi.NULL) ctheirs, theirs_str_ref = ( theirs._to_c() if theirs is not None else (ffi.NULL, ffi.NULL) ) err = C.git_merge_file_from_index( cmergeresult, self._repo, cancestor, cours, ctheirs, ffi.NULL ) check_error(err) mergeFileResult = MergeFileResult._from_c(cmergeresult) C.git_merge_file_result_free(cmergeresult) if use_deprecated: warnings.warn( 'Getting an str from Repository.merge_file_from_index is deprecated. ' 'The method will later return an instance of MergeFileResult by default, instead. ' 'Check parameter use_deprecated.', DeprecationWarning, ) return mergeFileResult.contents if mergeFileResult else '' return mergeFileResult def merge_commits( self, ours: str | Oid | Commit, theirs: str | Oid | Commit, favor: MergeFavor = MergeFavor.NORMAL, flags: MergeFlag = MergeFlag.FIND_RENAMES, file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, ) -> 'Index': """ Merge two arbitrary commits. Returns: an index with the result of the merge. Parameters: ours The commit to take as "ours" or base. theirs The commit which will be merged into "ours" favor An enums.MergeFavor constant specifying how to deal with file-level conflicts. For all but NORMAL, the index will not record a conflict. flags A combination of enums.MergeFlag constants. file_flags A combination of enums.MergeFileFlag constants. Both "ours" and "theirs" can be any object which peels to a commit or the id (string or Oid) of an object which peels to a commit. """ ours_ptr = ffi.new('git_commit **') theirs_ptr = ffi.new('git_commit **') cindex = ffi.new('git_index **') if isinstance(ours, (str, Oid)): ours_object = self[ours] if not isinstance(ours_object, Commit): raise TypeError(f'expected Commit, got {type(ours_object)}') ours = ours_object if isinstance(theirs, (str, Oid)): theirs_object = self[theirs] if not isinstance(theirs_object, Commit): raise TypeError(f'expected Commit, got {type(theirs_object)}') theirs = theirs_object ours = ours.peel(Commit) theirs = theirs.peel(Commit) opts = self._merge_options(favor, flags, file_flags) ffi.buffer(ours_ptr)[:] = ours._pointer[:] ffi.buffer(theirs_ptr)[:] = theirs._pointer[:] err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], opts) check_error(err) return Index.from_c(self, cindex) def merge_trees( self, ancestor: str | Oid | Tree, ours: str | Oid | Tree, theirs: str | Oid | Tree, favor: MergeFavor = MergeFavor.NORMAL, flags: MergeFlag = MergeFlag.FIND_RENAMES, file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, ) -> 'Index': """ Merge two trees. Returns: an Index that reflects the result of the merge. Parameters: ancestor The tree which is the common ancestor between 'ours' and 'theirs'. ours The commit to take as "ours" or base. theirs The commit which will be merged into "ours". favor An enums.MergeFavor constant specifying how to deal with file-level conflicts. For all but NORMAL, the index will not record a conflict. flags A combination of enums.MergeFlag constants. file_flags A combination of enums.MergeFileFlag constants. """ ancestor_ptr = ffi.new('git_tree **') ours_ptr = ffi.new('git_tree **') theirs_ptr = ffi.new('git_tree **') cindex = ffi.new('git_index **') ancestor = self.__ensure_tree(ancestor) ours = self.__ensure_tree(ours) theirs = self.__ensure_tree(theirs) opts = self._merge_options(favor, flags, file_flags) ffi.buffer(ancestor_ptr)[:] = ancestor._pointer[:] ffi.buffer(ours_ptr)[:] = ours._pointer[:] ffi.buffer(theirs_ptr)[:] = theirs._pointer[:] err = C.git_merge_trees( cindex, self._repo, ancestor_ptr[0], ours_ptr[0], theirs_ptr[0], opts ) check_error(err) return Index.from_c(self, cindex) def merge( self, source: Reference | Commit | Oid | str, favor: MergeFavor = MergeFavor.NORMAL, flags: MergeFlag = MergeFlag.FIND_RENAMES, file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, ) -> None: """ Merges the given Reference or Commit into HEAD. Merges the given commit into HEAD, writing the results into the working directory. Any changes are staged for commit and any conflicts are written to the index. Callers should inspect the repository's index after this completes, resolve any conflicts and prepare a commit. Parameters: source The Reference, Commit, or commit Oid to merge into HEAD. It is preferable to pass in a Reference, because this enriches the merge with additional information (for example, Repository.message will specify the name of the branch being merged). Previous versions of pygit2 allowed passing in a partial commit hash as a string; this is deprecated. favor An enums.MergeFavor constant specifying how to deal with file-level conflicts. For all but NORMAL, the index will not record a conflict. flags A combination of enums.MergeFlag constants. file_flags A combination of enums.MergeFileFlag constants. """ if isinstance(source, Reference): # Annotated commit from ref cptr = ffi.new('struct git_reference **') ffi.buffer(cptr)[:] = source._pointer[:] # type: ignore[attr-defined] commit_ptr = ffi.new('git_annotated_commit **') err = C.git_annotated_commit_from_ref(commit_ptr, self._repo, cptr[0]) check_error(err) else: # Annotated commit from commit id if isinstance(source, str): # For backwards compatibility, parse a string as a partial commit hash warnings.warn( 'Passing str to Repository.merge is deprecated. ' 'Pass Commit, Oid, or a Reference (such as a Branch) instead.', DeprecationWarning, ) oid = self[source].peel(Commit).id elif isinstance(source, Commit): oid = source.id elif isinstance(source, Oid): oid = source else: raise TypeError('expected Reference, Commit, or Oid') c_id = ffi.new('git_oid *') ffi.buffer(c_id)[:] = oid.raw[:] commit_ptr = ffi.new('git_annotated_commit **') err = C.git_annotated_commit_lookup(commit_ptr, self._repo, c_id) check_error(err) merge_opts = self._merge_options(favor, flags, file_flags) checkout_opts = ffi.new('git_checkout_options *') C.git_checkout_options_init(checkout_opts, 1) checkout_opts.checkout_strategy = int( CheckoutStrategy.SAFE | CheckoutStrategy.RECREATE_MISSING ) err = C.git_merge(self._repo, commit_ptr, 1, merge_opts, checkout_opts) C.git_annotated_commit_free(commit_ptr[0]) check_error(err) # # Prepared message (MERGE_MSG) # @property def raw_message(self) -> bytes: """ Retrieve git's prepared message (bytes). See `Repository.message` for more information. """ buf = ffi.new('git_buf *', (ffi.NULL, 0)) try: err = C.git_repository_message(buf, self._repo) if err == C.GIT_ENOTFOUND: return b'' check_error(err) return ffi.string(buf.ptr) finally: C.git_buf_dispose(buf) @property def message(self) -> str: """ Retrieve git's prepared message. Operations such as git revert/cherry-pick/merge with the -n option stop just short of creating a commit with the changes and save their prepared message in .git/MERGE_MSG so the next git-commit execution can present it to the user for them to amend if they wish. Use this function to get the contents of this file. Don't forget to call `Repository.remove_message()` after you create the commit. Note that the message is also removed by `Repository.state_cleanup()`. If there is no such message, an empty string is returned. """ return self.raw_message.decode('utf-8') def remove_message(self) -> None: """ Remove git's prepared message. """ err = C.git_repository_message_remove(self._repo) check_error(err) # # Describe # def describe( self, committish: str | Reference | Commit | None = None, max_candidates_tags: int | None = None, describe_strategy: DescribeStrategy = DescribeStrategy.DEFAULT, pattern: str | None = None, only_follow_first_parent: bool | None = None, show_commit_oid_as_fallback: bool | None = None, abbreviated_size: int | None = None, always_use_long_format: bool | None = None, dirty_suffix: str | None = None, ) -> str: """ Describe a commit-ish or the current working tree. Returns: The description (str). Parameters: committish : `str`, :class:`~.Reference`, or :class:`~.Commit` Commit-ish object or object name to describe, or `None` to describe the current working tree. max_candidates_tags : int The number of candidate tags to consider. Increasing above 10 will take slightly longer but may produce a more accurate result. A value of 0 will cause only exact matches to be output. describe_strategy : DescribeStrategy Can be one of: * `DescribeStrategy.DEFAULT` - Only match annotated tags. * `DescribeStrategy.TAGS` - Match everything under refs/tags/ (includes lightweight tags). * `DescribeStrategy.ALL` - Match everything under refs/ (includes branches). pattern : str Only consider tags matching the given `glob(7)` pattern, excluding the "refs/tags/" prefix. only_follow_first_parent : bool Follow only the first parent commit upon seeing a merge commit. show_commit_oid_as_fallback : bool Show uniquely abbreviated commit object as fallback. abbreviated_size : int The minimum number of hexadecimal digits to show for abbreviated object names. A value of 0 will suppress long format, only showing the closest tag. always_use_long_format : bool Always output the long format (the nearest tag, the number of commits, and the abbreviated commit name) even when the committish matches a tag. dirty_suffix : str A string to append if the working tree is dirty. Example:: repo.describe(pattern='public/*', dirty_suffix='-dirty') """ options = ffi.new('git_describe_options *') C.git_describe_options_init(options, C.GIT_DESCRIBE_OPTIONS_VERSION) if max_candidates_tags is not None: options.max_candidates_tags = max_candidates_tags if describe_strategy is not None: options.describe_strategy = int(describe_strategy) if pattern: # The returned pointer object has ownership on the allocated # memory. Make sure it is kept alive until git_describe_commit() or # git_describe_workdir() are called below. pattern_char = ffi.new('char[]', to_bytes(pattern)) options.pattern = pattern_char if only_follow_first_parent is not None: options.only_follow_first_parent = only_follow_first_parent if show_commit_oid_as_fallback is not None: options.show_commit_oid_as_fallback = show_commit_oid_as_fallback result = ffi.new('git_describe_result **') if committish: committish_rev: Object | Reference | Commit if isinstance(committish, str): committish_rev = self.revparse_single(committish) else: committish_rev = committish commit = committish_rev.peel(Commit) cptr = ffi.new('git_object **') ffi.buffer(cptr)[:] = commit._pointer[:] err = C.git_describe_commit(result, cptr[0], options) else: err = C.git_describe_workdir(result, self._repo, options) check_error(err) try: format_options = ffi.new('git_describe_format_options *') C.git_describe_init_format_options( format_options, C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION ) if abbreviated_size is not None: format_options.abbreviated_size = abbreviated_size if always_use_long_format is not None: format_options.always_use_long_format = always_use_long_format dirty_ptr = None if dirty_suffix: dirty_ptr = ffi.new('char[]', to_bytes(dirty_suffix)) format_options.dirty_suffix = dirty_ptr buf = ffi.new('git_buf *', (ffi.NULL, 0)) err = C.git_describe_format(buf, result[0], format_options) check_error(err) try: return ffi.string(buf.ptr).decode('utf-8') finally: C.git_buf_dispose(buf) finally: C.git_describe_result_free(result[0]) # # Stash # def stash( self, stasher: Signature, message: str | None = None, keep_index: bool = False, include_untracked: bool = False, include_ignored: bool = False, keep_all: bool = False, paths: list[str] | None = None, ) -> Oid: """ Save changes to the working directory to the stash. Returns: The Oid of the stash merge commit (Oid). Parameters: stasher : Signature The identity of the person doing the stashing. message : str An optional description of stashed state. keep_index : bool Leave changes already added to the index in the working directory. include_untracked : bool Also stash untracked files. include_ignored : bool Also stash ignored files. keep_all : bool All changes in the index and working directory are left intact. paths : list[str] An optional list of paths that control which files are stashed. Example:: >>> repo = pygit2.Repository('.') >>> repo.stash(repo.default_signature, 'WIP: stashing') """ opts = ffi.new('git_stash_save_options *') C.git_stash_save_options_init(opts, C.GIT_STASH_SAVE_OPTIONS_VERSION) flags = 0 flags |= keep_index * C.GIT_STASH_KEEP_INDEX flags |= keep_all * C.GIT_STASH_KEEP_ALL flags |= include_untracked * C.GIT_STASH_INCLUDE_UNTRACKED flags |= include_ignored * C.GIT_STASH_INCLUDE_IGNORED opts.flags = flags stasher_cptr = ffi.new('git_signature **') ffi.buffer(stasher_cptr)[:] = stasher._pointer[:] opts.stasher = stasher_cptr[0] if message: message_ref = ffi.new('char[]', to_bytes(message)) opts.message = message_ref if paths: arr = StrArray(paths) opts.paths = arr.ptr[0] # type: ignore[index] coid = ffi.new('git_oid *') err = C.git_stash_save_with_opts(coid, self._repo, opts) check_error(err) return Oid(raw=bytes(ffi.buffer(coid)[:])) def stash_apply( self, index: int = 0, reinstate_index: bool = False, strategy: CheckoutStrategy | None = None, callbacks: StashApplyCallbacks | None = None, ) -> None: """ Apply a stashed state in the stash list to the working directory. Parameters: index : int The position within the stash list of the stash to apply. 0 is the most recent stash. reinstate_index : bool Try to reinstate stashed changes to the index. callbacks : StashApplyCallbacks Optional. Supply a `callbacks` object to get information about the progress of the stash application as it is being performed. The callbacks should be an object which inherits from `pyclass:StashApplyCallbacks`. It should implement the callbacks as overridden methods. Note that this class inherits from CheckoutCallbacks, so you can also get information from the checkout part of the unstashing process via the callbacks. The checkout options may be customized using the same arguments taken by Repository.checkout(). Example:: >>> repo = pygit2.Repository('.') >>> repo.stash(repo.default_signature, 'WIP: stashing') >>> repo.stash_apply(strategy=CheckoutStrategy.ALLOW_CONFLICTS) """ with git_stash_apply_options( reinstate_index=reinstate_index, strategy=strategy, callbacks=callbacks, ) as payload: err = C.git_stash_apply(self._repo, index, payload.stash_apply_options) payload.check_error(err) def stash_drop(self, index: int = 0) -> None: """ Remove a stashed state from the stash list. Parameters: index : int The position within the stash list of the stash to remove. 0 is the most recent stash. """ check_error(C.git_stash_drop(self._repo, index)) def stash_pop( self, index: int = 0, reinstate_index: bool = False, strategy: CheckoutStrategy | None = None, callbacks: StashApplyCallbacks | None = None, ) -> None: """Apply a stashed state and remove it from the stash list. For arguments, see Repository.stash_apply(). """ with git_stash_apply_options( reinstate_index=reinstate_index, strategy=strategy, callbacks=callbacks, ) as payload: err = C.git_stash_pop(self._repo, index, payload.stash_apply_options) payload.check_error(err) # # Utility for writing a tree into an archive # def write_archive( self, treeish: str | Tree | Object | Oid, archive: tarfile.TarFile, timestamp: int | None = None, prefix: str = '', ) -> None: """ Write treeish into an archive. If no timestamp is provided and 'treeish' is a commit, its committer timestamp will be used. Otherwise the current time will be used. All path names in the archive are added to 'prefix', which defaults to an empty string. Parameters: treeish The treeish to write. archive An archive from the 'tarfile' module. timestamp Timestamp to use for the files in the archive. prefix Extra prefix to add to the path names in the archive. Example:: >>> import tarfile, pygit2 >>> with tarfile.open('foo.tar', 'w') as archive: >>> repo = pygit2.Repository('.') >>> repo.write_archive(repo.head.target, archive) """ # Try to get a tree form whatever we got if isinstance(treeish, (str, Oid)): treeish = self[treeish] tree = treeish.peel(Tree) # if we don't have a timestamp, try to get it from a commit if not timestamp: try: commit = treeish.peel(Commit) timestamp = commit.committer.time except Exception: pass # as a last resort, use the current timestamp if not timestamp: timestamp = int(time()) index = Index() index.read_tree(tree) for entry in index: content = self[entry.id].read_raw() info = tarfile.TarInfo(prefix + entry.path) info.size = len(content) info.mtime = timestamp info.uname = info.gname = 'root' # just because git does this if entry.mode == FileMode.LINK: info.type = tarfile.SYMTYPE info.linkname = content.decode('utf-8') info.mode = 0o777 # symlinks get placeholder info.size = 0 archive.addfile(info) else: info.mode = entry.mode archive.addfile(info, BytesIO(content)) # # Ahead-behind, which mostly lives on its own namespace # def ahead_behind(self, local: Oid | str, upstream: Oid | str) -> tuple[int, int]: """ Calculate how many different commits are in the non-common parts of the history between the two given ids. Ahead is how many commits are in the ancestry of the `local` commit which are not in the `upstream` commit. Behind is the opposite. Returns: a tuple of two integers with the number of commits ahead and behind respectively. Parameters: local The commit which is considered the local or current state. upstream The commit which is considered the upstream. """ if not isinstance(local, Oid): local = self.expand_id(local) if not isinstance(upstream, Oid): upstream = self.expand_id(upstream) ahead, behind = ffi.new('size_t*'), ffi.new('size_t*') oid1, oid2 = ffi.new('git_oid *'), ffi.new('git_oid *') ffi.buffer(oid1)[:] = local.raw[:] ffi.buffer(oid2)[:] = upstream.raw[:] err = C.git_graph_ahead_behind(ahead, behind, self._repo, oid1, oid2) check_error(err) return int(ahead[0]), int(behind[0]) # # Git attributes # def get_attr( self, path: str | bytes | Path, name: str | bytes, flags: AttrCheck = AttrCheck.FILE_THEN_INDEX, commit: Oid | str | None = None, ) -> bool | None | str: """ Retrieve an attribute for a file by path. Returns: a boolean, `None` if the value is unspecified, or string with the value of the attribute. Parameters: path The path of the file to look up attributes for, relative to the workdir root. name The name of the attribute to look up. flags A combination of enums.AttrCheck flags which determine the lookup order. commit Optional id of commit to load attributes from when the `INCLUDE_COMMIT` flag is specified. Examples:: >>> print(repo.get_attr('splash.bmp', 'binary')) True >>> print(repo.get_attr('splash.bmp', 'unknown-attr')) None >>> repo.get_attr('test.h', 'whitespace') 'tab-in-indent,trailing-space' """ copts = ffi.new('git_attr_options *') copts.version = C.GIT_ATTR_OPTIONS_VERSION copts.flags = int(flags) if commit is not None: if not isinstance(commit, Oid): commit = Oid(hex=commit) ffi.buffer(ffi.addressof(copts, 'attr_commit_id'))[:] = commit.raw cvalue = ffi.new('char **') err = C.git_attr_get_ext( cvalue, self._repo, copts, to_bytes(path), to_bytes(name) ) check_error(err) # Now let's see if we can figure out what the value is attr_kind = C.git_attr_value(cvalue[0]) if attr_kind == C.GIT_ATTR_VALUE_UNSPECIFIED: return None elif attr_kind == C.GIT_ATTR_VALUE_TRUE: return True elif attr_kind == C.GIT_ATTR_VALUE_FALSE: return False elif attr_kind == C.GIT_ATTR_VALUE_STRING: return ffi.string(cvalue[0]).decode('utf-8') assert False, 'the attribute value from libgit2 is invalid' # # Identity for reference operations # @property def ident(self) -> tuple[Optional[str], Optional[str]]: cname = ffi.new('char **') cemail = ffi.new('char **') err = C.git_repository_ident(cname, cemail, self._repo) check_error(err) return (maybe_string(cname[0]), maybe_string(cemail[0])) def set_ident(self, name: Optional[str], email: Optional[str]) -> None: """Set the identity to be used for reference operations. Updates to some references also append data to their reflog. You can use this method to set what identity will be used. If none is set, it will be read from the configuration. """ err = C.git_repository_set_ident(self._repo, to_bytes(name), to_bytes(email)) check_error(err) def revert(self, commit: Commit) -> None: """ Revert the given commit, producing changes in the index and working directory. This operation updates the repository's state and prepared message (MERGE_MSG). """ commit_ptr = ffi.new('git_commit **') ffi.buffer(commit_ptr)[:] = commit._pointer[:] err = C.git_revert(self._repo, commit_ptr[0], ffi.NULL) check_error(err) def revert_commit( self, revert_commit: Commit, our_commit: Commit, mainline: int = 0 ) -> Index: """ Revert the given Commit against the given "our" Commit, producing an Index that reflects the result of the revert. Returns: an Index with the result of the revert. Parameters: revert_commit The Commit to revert. our_commit The Commit to revert against (eg, HEAD). mainline The parent of the revert Commit, if it is a merge (i.e. 1, 2). """ cindex = ffi.new('git_index **') revert_commit_ptr = ffi.new('git_commit **') our_commit_ptr = ffi.new('git_commit **') ffi.buffer(revert_commit_ptr)[:] = revert_commit._pointer[:] ffi.buffer(our_commit_ptr)[:] = our_commit._pointer[:] opts = ffi.new('git_merge_options *') err = C.git_merge_options_init(opts, C.GIT_MERGE_OPTIONS_VERSION) check_error(err) err = C.git_revert_commit( cindex, self._repo, revert_commit_ptr[0], our_commit_ptr[0], mainline, opts ) check_error(err) return Index.from_c(self, cindex) # # Amend commit # def amend_commit( self, commit: Commit | Oid | str, refname: Reference | str | None, author: Signature | None = None, committer: Signature | None = None, message: str | None = None, tree: Tree | Oid | str | None = None, encoding: str = 'UTF-8', ) -> Oid: """ Amend an existing commit by replacing only explicitly passed values, return the rewritten commit's oid. This creates a new commit that is exactly the same as the old commit, except that any explicitly passed values will be updated. The new commit has the same parents as the old commit. You may omit the `author`, `committer`, `message`, `tree`, and `encoding` parameters, in which case this will use the values from the original `commit`. Parameters: commit : Commit, Oid, or str The commit to amend. refname : Reference or str If not `None`, name of the reference that will be updated to point to the newly rewritten commit. Use "HEAD" to update the HEAD of the current branch and make it point to the rewritten commit. If you want to amend a commit that is not currently the tip of the branch and then rewrite the following commits to reach a ref, pass this as `None` and update the rest of the commit chain and ref separately. author : Signature If not None, replace the old commit's author signature with this one. committer : Signature If not None, replace the old commit's committer signature with this one. message : str If not None, replace the old commit's message with this one. tree : Tree, Oid, or str If not None, replace the old commit's tree with this one. encoding : str Optional encoding for `message`. """ # Initialize parameters to pass on to C function git_commit_amend. # Note: the pointers are all initialized to NULL by default. coid = ffi.new('git_oid *') commit_cptr = ffi.new('git_commit **') refname_cstr: 'ArrayC[char]' | 'ffi.NULL_TYPE' = ffi.NULL author_cptr = ffi.new('git_signature **') committer_cptr = ffi.new('git_signature **') message_cstr: 'ArrayC[char]' | 'ffi.NULL_TYPE' = ffi.NULL encoding_cstr: 'ArrayC[char]' | 'ffi.NULL_TYPE' = ffi.NULL tree_cptr = ffi.new('git_tree **') # Get commit as pointer to git_commit. if isinstance(commit, (str, Oid)): commit_object = self[commit] commit_commit = commit_object.peel(Commit) elif isinstance(commit, Commit): commit_commit = commit elif commit is None: raise ValueError('the commit to amend cannot be None') else: raise TypeError('the commit to amend must be a Commit, str, or Oid') ffi.buffer(commit_cptr)[:] = commit_commit._pointer[:] # Get refname as C string. if isinstance(refname, Reference): refname_cstr = ffi.new('char[]', to_bytes(refname.name)) elif type(refname) is str: refname_cstr = ffi.new('char[]', to_bytes(refname)) elif refname is not None: raise TypeError('refname must be a str or Reference') # Get author as pointer to git_signature. if isinstance(author, Signature): ffi.buffer(author_cptr)[:] = author._pointer[:] elif author is not None: raise TypeError('author must be a Signature') # Get committer as pointer to git_signature. if isinstance(committer, Signature): ffi.buffer(committer_cptr)[:] = committer._pointer[:] elif committer is not None: raise TypeError('committer must be a Signature') # Get message and encoding as C strings. if message is not None: message_cstr = ffi.new('char[]', to_bytes(message, encoding)) encoding_cstr = ffi.new('char[]', to_bytes(encoding)) # Get tree as pointer to git_tree. if tree is not None: if isinstance(tree, (str, Oid)): tree_object = self[tree] else: tree_object = tree tree_tree = tree_object.peel(Tree) ffi.buffer(tree_cptr)[:] = tree_tree._pointer[:] # Amend the commit. err = C.git_commit_amend( coid, commit_cptr[0], refname_cstr, author_cptr[0], committer_cptr[0], encoding_cstr, message_cstr, tree_cptr[0], ) check_error(err) return Oid(raw=bytes(ffi.buffer(coid)[:])) def __ensure_tree(self, maybe_tree: str | Oid | Tree) -> Tree: if isinstance(maybe_tree, Tree): return maybe_tree return self[maybe_tree].peel(Tree) class Repository(BaseRepository): def __init__( self, path: str | bytes | None | Path = None, flags: RepositoryOpenFlag = RepositoryOpenFlag.DEFAULT, ): """ The Repository constructor will commonly be called with one argument, the path of the repository to open. Alternatively, constructing a repository with no arguments will create a repository with no backends. You can use this path to create repositories with custom backends. Note that most operations on the repository are considered invalid and may lead to undefined behavior if attempted before providing an odb and refdb via set_odb and set_refdb. Parameters: path : str The path to open - if not provided, the repository will have no backend. flags : enums.RepositoryOpenFlag An optional combination of enums.RepositoryOpenFlag constants controlling how to open the repository. """ if path is not None: if hasattr(path, '__fspath__'): path = path.__fspath__() if not isinstance(path, str): path = path.decode('utf-8') path_backend = init_file_backend(path, int(flags)) super().__init__(path_backend) else: super().__init__() @classmethod def _from_c(cls, ptr: 'GitRepositoryC', owned: bool) -> 'Repository': cptr = ffi.new('git_repository **') cptr[0] = ptr repo = cls.__new__(cls) BaseRepository._from_c(repo, bytes(ffi.buffer(cptr)[:]), owned) # type: ignore repo._common_init() return repo libgit2-pygit2-d86662e/pygit2/settings.py000066400000000000000000000312111521317216500202610ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """ Settings mapping. """ from ssl import DefaultVerifyPaths, get_default_verify_paths from typing import overload import pygit2.enums from .enums import ConfigLevel, Option from .errors import GitError from .options import option class SearchPathList: def __getitem__(self, key: ConfigLevel) -> str: return option(Option.GET_SEARCH_PATH, key) def __setitem__(self, key: ConfigLevel, value: str) -> None: option(Option.SET_SEARCH_PATH, key, value) class Settings: """Library-wide settings interface.""" __slots__ = '_default_tls_verify_paths', '_ssl_cert_dir', '_ssl_cert_file' _search_path = SearchPathList() _default_tls_verify_paths: DefaultVerifyPaths | None _ssl_cert_file: str | bytes | None _ssl_cert_dir: str | bytes | None def __init__(self) -> None: """Initialize global pygit2 and libgit2 settings.""" self._initialize_tls_certificate_locations() def _initialize_tls_certificate_locations(self) -> None: """Set up initial TLS file and directory lookup locations.""" self._default_tls_verify_paths = get_default_verify_paths() try: self.set_ssl_cert_locations( self._default_tls_verify_paths.cafile, self._default_tls_verify_paths.capath, ) except GitError as git_err: valid_msg = "TLS backend doesn't support certificate locations" if str(git_err) != valid_msg: raise self._default_tls_verify_paths = None self._ssl_cert_file = None self._ssl_cert_dir = None @property def search_path(self) -> SearchPathList: """Configuration file search path. This behaves like an array whose indices correspond to ConfigLevel values. The local search path cannot be changed. """ return self._search_path @property def mwindow_size(self) -> int: """Get or set the maximum mmap window size""" return option(Option.GET_MWINDOW_SIZE) @mwindow_size.setter def mwindow_size(self, value: int) -> None: option(Option.SET_MWINDOW_SIZE, value) @property def mwindow_mapped_limit(self) -> int: """ Get or set the maximum memory that will be mapped in total by the library """ return option(Option.GET_MWINDOW_MAPPED_LIMIT) @mwindow_mapped_limit.setter def mwindow_mapped_limit(self, value: int) -> None: option(Option.SET_MWINDOW_MAPPED_LIMIT, value) @property def mwindow_file_limit(self) -> int: """Get or set the maximum number of files to be mapped at any time""" return option(Option.GET_MWINDOW_FILE_LIMIT) @mwindow_file_limit.setter def mwindow_file_limit(self, value: int) -> None: option(Option.SET_MWINDOW_FILE_LIMIT, value) @property def cached_memory(self) -> tuple[int, int]: """ Get the current bytes in cache and the maximum that would be allowed in the cache. """ return option(Option.GET_CACHED_MEMORY) def enable_caching(self, value: bool = True) -> None: """ Enable or disable caching completely. Because caches are repository-specific, disabling the cache cannot immediately clear all cached objects, but each cache will be cleared on the next attempt to update anything in it. """ return option(Option.ENABLE_CACHING, value) def disable_pack_keep_file_checks(self, value: bool = True) -> None: """ This will cause .keep file existence checks to be skipped when accessing packfiles, which can help performance with remote filesystems. """ return option(Option.DISABLE_PACK_KEEP_FILE_CHECKS, value) def cache_max_size(self, value: int) -> None: """ Set the maximum total data size that will be cached in memory across all repositories before libgit2 starts evicting objects from the cache. This is a soft limit, in that the library might briefly exceed it, but will start aggressively evicting objects from cache when that happens. The default cache size is 256MB. """ return option(Option.SET_CACHE_MAX_SIZE, value) def cache_object_limit( self, object_type: pygit2.enums.ObjectType, value: int ) -> None: """ Set the maximum data size for the given type of object to be considered eligible for caching in memory. Setting to value to zero means that that type of object will not be cached. Defaults to 0 for enums.ObjectType.BLOB (i.e. won't cache blobs) and 4k for COMMIT, TREE, and TAG. """ return option(Option.SET_CACHE_OBJECT_LIMIT, object_type, value) @property def ssl_cert_file(self) -> str | bytes | None: """TLS certificate file path.""" return self._ssl_cert_file @ssl_cert_file.setter def ssl_cert_file(self, value: str | bytes) -> None: """Set the TLS cert file path.""" self.set_ssl_cert_locations(value, self._ssl_cert_dir) @ssl_cert_file.deleter def ssl_cert_file(self) -> None: """Reset the TLS cert file path.""" self.ssl_cert_file = self._default_tls_verify_paths.cafile # type: ignore[union-attr] @property def ssl_cert_dir(self) -> str | bytes | None: """TLS certificates lookup directory path.""" return self._ssl_cert_dir @ssl_cert_dir.setter def ssl_cert_dir(self, value: str | bytes) -> None: """Set the TLS certificate lookup folder.""" self.set_ssl_cert_locations(self._ssl_cert_file, value) @ssl_cert_dir.deleter def ssl_cert_dir(self) -> None: """Reset the TLS certificate lookup folder.""" self.ssl_cert_dir = self._default_tls_verify_paths.capath # type: ignore[union-attr] @overload def set_ssl_cert_locations( self, cert_file: str | bytes | None, cert_dir: str | bytes ) -> None: ... @overload def set_ssl_cert_locations( self, cert_file: str | bytes, cert_dir: str | bytes | None ) -> None: ... def set_ssl_cert_locations( self, cert_file: str | bytes | None, cert_dir: str | bytes | None ) -> None: """ Set the SSL certificate-authority locations. - `cert_file` is the location of a file containing several certificates concatenated together. - `cert_dir` is the location of a directory holding several certificates, one per file. Either parameter may be `NULL`, but not both. """ option(Option.SET_SSL_CERT_LOCATIONS, cert_file, cert_dir) self._ssl_cert_file = cert_file self._ssl_cert_dir = cert_dir @property def template_path(self) -> str | None: """Get or set the default template path for new repositories""" return option(Option.GET_TEMPLATE_PATH) @template_path.setter def template_path(self, value: str | bytes) -> None: option(Option.SET_TEMPLATE_PATH, value) @property def user_agent(self) -> str | None: """Get or set the user agent string for network operations""" return option(Option.GET_USER_AGENT) @user_agent.setter def user_agent(self, value: str | bytes) -> None: option(Option.SET_USER_AGENT, value) @property def user_agent_product(self) -> str | None: """Get or set the user agent product name""" return option(Option.GET_USER_AGENT_PRODUCT) @user_agent_product.setter def user_agent_product(self, value: str | bytes) -> None: option(Option.SET_USER_AGENT_PRODUCT, value) def set_ssl_ciphers(self, ciphers: str | bytes) -> None: """Set the SSL ciphers to use for HTTPS connections""" option(Option.SET_SSL_CIPHERS, ciphers) def enable_strict_object_creation(self, value: bool = True) -> None: """Enable or disable strict object creation validation""" option(Option.ENABLE_STRICT_OBJECT_CREATION, value) def enable_strict_symbolic_ref_creation(self, value: bool = True) -> None: """Enable or disable strict symbolic reference creation validation""" option(Option.ENABLE_STRICT_SYMBOLIC_REF_CREATION, value) def enable_ofs_delta(self, value: bool = True) -> None: """Enable or disable offset delta encoding""" option(Option.ENABLE_OFS_DELTA, value) def enable_fsync_gitdir(self, value: bool = True) -> None: """Enable or disable fsync for git directory operations""" option(Option.ENABLE_FSYNC_GITDIR, value) def enable_strict_hash_verification(self, value: bool = True) -> None: """Enable or disable strict hash verification""" option(Option.ENABLE_STRICT_HASH_VERIFICATION, value) def enable_unsaved_index_safety(self, value: bool = True) -> None: """Enable or disable unsaved index safety checks""" option(Option.ENABLE_UNSAVED_INDEX_SAFETY, value) def enable_http_expect_continue(self, value: bool = True) -> None: """Enable or disable HTTP Expect/Continue for large pushes""" option(Option.ENABLE_HTTP_EXPECT_CONTINUE, value) @property def windows_sharemode(self) -> int: """Get or set the Windows share mode for opening files""" return option(Option.GET_WINDOWS_SHAREMODE) @windows_sharemode.setter def windows_sharemode(self, value: int) -> None: option(Option.SET_WINDOWS_SHAREMODE, value) @property def pack_max_objects(self) -> int: """Get or set the maximum number of objects in a pack""" return option(Option.GET_PACK_MAX_OBJECTS) @pack_max_objects.setter def pack_max_objects(self, value: int) -> None: option(Option.SET_PACK_MAX_OBJECTS, value) @property def owner_validation(self) -> bool: """Get or set repository directory ownership validation""" return option(Option.GET_OWNER_VALIDATION) @owner_validation.setter def owner_validation(self, value: bool) -> None: option(Option.SET_OWNER_VALIDATION, value) def set_odb_packed_priority(self, priority: int) -> None: """Set the priority for packed ODB backend (default 1)""" option(Option.SET_ODB_PACKED_PRIORITY, priority) def set_odb_loose_priority(self, priority: int) -> None: """Set the priority for loose ODB backend (default 2)""" option(Option.SET_ODB_LOOSE_PRIORITY, priority) @property def extensions(self) -> list[str]: """Get the list of enabled extensions""" return option(Option.GET_EXTENSIONS) def set_extensions(self, extensions: list[str]) -> None: """Set the list of enabled extensions""" option(Option.SET_EXTENSIONS, extensions, len(extensions)) @property def homedir(self) -> str | None: """Get or set the home directory""" return option(Option.GET_HOMEDIR) @homedir.setter def homedir(self, value: str | bytes) -> None: option(Option.SET_HOMEDIR, value) @property def server_connect_timeout(self) -> int: """Get or set the server connection timeout in milliseconds""" return option(Option.GET_SERVER_CONNECT_TIMEOUT) @server_connect_timeout.setter def server_connect_timeout(self, value: int) -> None: option(Option.SET_SERVER_CONNECT_TIMEOUT, value) @property def server_timeout(self) -> int: """Get or set the server timeout in milliseconds""" return option(Option.GET_SERVER_TIMEOUT) @server_timeout.setter def server_timeout(self, value: int) -> None: option(Option.SET_SERVER_TIMEOUT, value) libgit2-pygit2-d86662e/pygit2/submodules.py000066400000000000000000000316401521317216500206110ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations from collections.abc import Iterable, Iterator from pathlib import Path from typing import TYPE_CHECKING, Optional from ._pygit2 import Oid from .callbacks import RemoteCallbacks, git_fetch_options from .enums import SubmoduleIgnore, SubmoduleStatus from .errors import check_error from .ffi import C, ffi from .utils import maybe_string, to_bytes # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: from pygit2 import Repository from pygit2._libgit2.ffi import GitSubmoduleC from .repository import BaseRepository class Submodule: _repo: BaseRepository _subm: 'GitSubmoduleC' @classmethod def _from_c(cls, repo: BaseRepository, cptr: 'GitSubmoduleC') -> 'Submodule': subm = cls.__new__(cls) subm._repo = repo subm._subm = cptr return subm def __del__(self) -> None: C.git_submodule_free(self._subm) def open(self) -> Repository: """Open the repository for a submodule.""" crepo = ffi.new('git_repository **') err = C.git_submodule_open(crepo, self._subm) check_error(err) return self._repo._from_c(crepo[0], True) # type: ignore[attr-defined] def init(self, overwrite: bool = False) -> None: """ Just like "git submodule init", this copies information about the submodule into ".git/config". Parameters: overwrite By default, existing submodule entries will not be overwritten, but setting this to True forces them to be updated. """ err = C.git_submodule_init(self._subm, int(overwrite)) check_error(err) def update( self, init: bool = False, callbacks: Optional[RemoteCallbacks] = None, depth: int = 0, ) -> None: """ Update a submodule. This will clone a missing submodule and checkout the subrepository to the commit specified in the index of the containing repository. If the submodule repository doesn't contain the target commit (e.g. because fetchRecurseSubmodules isn't set), then the submodule is fetched using the fetch options supplied in options. Parameters: init If the submodule is not initialized, setting this flag to True will initialize the submodule before updating. Otherwise, this will raise an error if attempting to update an uninitialized repository. callbacks Optional RemoteCallbacks to clone or fetch the submodule. depth Number of commits to fetch. The default is 0 (full commit history). """ opts = ffi.new('git_submodule_update_options *') C.git_submodule_update_options_init( opts, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION ) opts.fetch_opts.depth = depth with git_fetch_options(callbacks, opts=opts.fetch_opts) as payload: err = C.git_submodule_update(self._subm, int(init), opts) payload.check_error(err) def reload(self, force: bool = False) -> None: """ Reread submodule info from config, index, and HEAD. Call this to reread cached submodule information for this submodule if you have reason to believe that it has changed. Parameters: force Force reload even if the data doesn't seem out of date """ err = C.git_submodule_reload(self._subm, int(force)) check_error(err) @property def name(self): """Name of the submodule.""" name = C.git_submodule_name(self._subm) return ffi.string(name).decode('utf-8') @property def path(self): """Path of the submodule.""" path = C.git_submodule_path(self._subm) return ffi.string(path).decode('utf-8') @property def url(self) -> str | None: """URL of the submodule.""" url = C.git_submodule_url(self._subm) return maybe_string(url) @url.setter def url(self, url: str) -> None: crepo = self._repo._repo cname = ffi.new('char[]', to_bytes(self.name)) curl = ffi.new('char[]', to_bytes(url)) err = C.git_submodule_set_url(crepo, cname, curl) check_error(err) @property def branch(self): """Branch that is to be tracked by the submodule.""" branch = C.git_submodule_branch(self._subm) return ffi.string(branch).decode('utf-8') @property def head_id(self) -> Oid | None: """ The submodule's HEAD commit id (as recorded in the superproject's current HEAD tree). Returns None if the superproject's HEAD doesn't contain the submodule. """ head = C.git_submodule_head_id(self._subm) if head == ffi.NULL: return None return Oid(raw=bytes(ffi.buffer(head.id)[:])) class SubmoduleCollection: """Collection of submodules in a repository.""" def __init__(self, repository: BaseRepository): self._repository = repository def __getitem__(self, name: str | Path) -> Submodule: """ Look up submodule information by name or path. Raises KeyError if there is no such submodule. """ csub = ffi.new('git_submodule **') cpath = ffi.new('char[]', to_bytes(name)) err = C.git_submodule_lookup(csub, self._repository._repo, cpath) check_error(err) return Submodule._from_c(self._repository, csub[0]) def __contains__(self, name: str) -> bool: return self.get(name) is not None def __iter__(self) -> Iterator[Submodule]: for s in self._repository.listall_submodules(): yield self[s] def get(self, name: str) -> Submodule | None: """ Look up submodule information by name or path. Unlike __getitem__, this returns None if the submodule is not found. """ try: return self[name] except KeyError: return None def add( self, url: str, path: str, link: bool = True, callbacks: Optional[RemoteCallbacks] = None, depth: int = 0, ) -> Submodule: """ Add a submodule to the index. The submodule is automatically cloned. Returns: the submodule that was added. Parameters: url The URL of the submodule. path The path within the parent repository to add the submodule link Should workdir contain a gitlink to the repo in `.git/modules` vs. repo directly in workdir. callbacks Optional RemoteCallbacks to clone the submodule. depth Number of commits to fetch. The default is 0 (full commit history). """ csub = ffi.new('git_submodule **') curl = ffi.new('char[]', to_bytes(url)) cpath = ffi.new('char[]', to_bytes(path)) gitlink = 1 if link else 0 err = C.git_submodule_add_setup( csub, self._repository._repo, curl, cpath, gitlink ) check_error(err) submodule_instance = Submodule._from_c(self._repository, csub[0]) # Prepare options opts = ffi.new('git_submodule_update_options *') C.git_submodule_update_options_init( opts, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION ) opts.fetch_opts.depth = depth with git_fetch_options(callbacks, opts=opts.fetch_opts) as payload: crepo = ffi.new('git_repository **') err = C.git_submodule_clone(crepo, submodule_instance._subm, opts) payload.check_error(err) # Clean up submodule repository from .repository import Repository Repository._from_c(crepo[0], True) err = C.git_submodule_add_finalize(submodule_instance._subm) check_error(err) return submodule_instance def init( self, submodules: Optional[Iterable[str]] = None, overwrite: bool = False ) -> None: """ Initialize submodules in the repository. Just like "git submodule init", this copies information about the submodules into ".git/config". Parameters: submodules Optional list of submodule paths or names to initialize. Default argument initializes all submodules. overwrite Flag indicating if initialization should overwrite submodule entries. """ if submodules is None: submodules = self._repository.listall_submodules() instances = [self[s] for s in submodules] for submodule in instances: submodule.init(overwrite) def update( self, submodules: Optional[Iterable[str]] = None, init: bool = False, callbacks: Optional[RemoteCallbacks] = None, depth: int = 0, ) -> None: """ Update submodules. This will clone a missing submodule and checkout the subrepository to the commit specified in the index of the containing repository. If the submodule repository doesn't contain the target commit (e.g. because fetchRecurseSubmodules isn't set), then the submodule is fetched using the fetch options supplied in options. Parameters: submodules Optional list of submodule paths or names. If you omit this parameter or pass None, all submodules will be updated. init If the submodule is not initialized, setting this flag to True will initialize the submodule before updating. Otherwise, this will raise an error if attempting to update an uninitialized repository. callbacks Optional RemoteCallbacks to clone or fetch the submodule. depth Number of commits to fetch. The default is 0 (full commit history). """ if submodules is None: submodules = self._repository.listall_submodules() instances = [self[s] for s in submodules] for submodule in instances: submodule.update(init, callbacks, depth) def status( self, name: str, ignore: SubmoduleIgnore = SubmoduleIgnore.UNSPECIFIED ) -> SubmoduleStatus: """ Get the status of a submodule. Returns: A combination of SubmoduleStatus flags. Parameters: name Submodule name or path. ignore A SubmoduleIgnore value indicating how deeply to examine the working directory. """ cstatus = ffi.new('unsigned int *') err = C.git_submodule_status( cstatus, self._repository._repo, to_bytes(name), ignore ) check_error(err) return SubmoduleStatus(cstatus[0]) def cache_all(self) -> None: """ Load and cache all submodules in the repository. Because the `.gitmodules` file is unstructured, loading submodules is an O(N) operation. Any operation that requires accessing all submodules is O(N^2) in the number of submodules, if it has to look each one up individually. This function loads all submodules and caches them so that subsequent submodule lookups by name are O(1). """ err = C.git_repository_submodule_cache_all(self._repository._repo) check_error(err) def cache_clear(self) -> None: """ Clear the submodule cache populated by `submodule_cache_all`. If there is no cache, do nothing. The cache incorporates data from the repository's configuration, as well as the state of the working tree, the index, and HEAD. So any time any of these has changed, the cache might become invalid. """ err = C.git_repository_submodule_cache_clear(self._repository._repo) check_error(err) libgit2-pygit2-d86662e/pygit2/transaction.py000066400000000000000000000153751521317216500207630ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from __future__ import annotations import threading from typing import TYPE_CHECKING from .errors import check_error from .ffi import C, ffi from .utils import to_bytes if TYPE_CHECKING: from ._pygit2 import Oid, Signature from .repository import BaseRepository class ReferenceTransaction: """Context manager for transactional reference updates. A transaction allows multiple reference updates to be performed atomically. All updates are applied when the transaction is committed, or none are applied if the transaction is rolled back. Example: with repo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_oid, message='Update master') # Changes committed automatically on context exit """ def __init__(self, repository: BaseRepository) -> None: self._repository = repository self._transaction = ffi.new('git_transaction **') self._tx = None self._thread_id = threading.get_ident() err = C.git_transaction_new(self._transaction, repository._repo) check_error(err) self._tx = self._transaction[0] def _check_thread(self) -> None: """Verify transaction is being used from the same thread that created it.""" current_thread = threading.get_ident() if current_thread != self._thread_id: raise RuntimeError( f'Transaction created in thread {self._thread_id} ' f'but used in thread {current_thread}. ' 'Transactions must be used from the thread that created them.' ) def lock_ref(self, refname: str) -> None: """Lock a reference in preparation for updating it. Args: refname: Name of the reference to lock (e.g., 'refs/heads/master') """ self._check_thread() if self._tx is None: raise ValueError('Transaction already closed') c_refname = ffi.new('char[]', to_bytes(refname)) err = C.git_transaction_lock_ref(self._tx, c_refname) check_error(err) def set_target( self, refname: str, target: Oid | str, signature: Signature | None = None, message: str | None = None, ) -> None: """Set the target of a direct reference. The reference must be locked first via lock_ref(). Args: refname: Name of the reference to update target: Target OID or hex string signature: Signature for the reflog (None to use repo identity) message: Message for the reflog """ self._check_thread() if self._tx is None: raise ValueError('Transaction already closed') from ._pygit2 import Oid c_refname = ffi.new('char[]', to_bytes(refname)) # Convert target to OID if isinstance(target, str): target = Oid(hex=target) c_oid = ffi.new('git_oid *') ffi.buffer(c_oid)[:] = target.raw c_sig = signature._pointer if signature else ffi.NULL c_msg = ffi.new('char[]', to_bytes(message)) if message else ffi.NULL err = C.git_transaction_set_target(self._tx, c_refname, c_oid, c_sig, c_msg) check_error(err) def set_symbolic_target( self, refname: str, target: str, signature: Signature | None = None, message: str | None = None, ) -> None: """Set the target of a symbolic reference. The reference must be locked first via lock_ref(). Args: refname: Name of the reference to update target: Target reference name (e.g., 'refs/heads/master') signature: Signature for the reflog (None to use repo identity) message: Message for the reflog """ self._check_thread() if self._tx is None: raise ValueError('Transaction already closed') c_refname = ffi.new('char[]', to_bytes(refname)) c_target = ffi.new('char[]', to_bytes(target)) c_sig = signature._pointer if signature else ffi.NULL c_msg = ffi.new('char[]', to_bytes(message)) if message else ffi.NULL err = C.git_transaction_set_symbolic_target( self._tx, c_refname, c_target, c_sig, c_msg ) check_error(err) def remove(self, refname: str) -> None: """Remove a reference. The reference must be locked first via lock_ref(). Args: refname: Name of the reference to remove """ self._check_thread() if self._tx is None: raise ValueError('Transaction already closed') c_refname = ffi.new('char[]', to_bytes(refname)) err = C.git_transaction_remove(self._tx, c_refname) check_error(err) def commit(self) -> None: """Commit the transaction, applying all queued updates.""" self._check_thread() if self._tx is None: raise ValueError('Transaction already closed') err = C.git_transaction_commit(self._tx) check_error(err) def __enter__(self) -> ReferenceTransaction: return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: self._check_thread() # Only commit if no exception occurred if exc_type is None and self._tx is not None: self.commit() # Always free the transaction if self._tx is not None: C.git_transaction_free(self._tx) self._tx = None def __del__(self) -> None: if self._tx is not None: C.git_transaction_free(self._tx) self._tx = None libgit2-pygit2-d86662e/pygit2/utils.py000066400000000000000000000150201521317216500175610ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import contextlib import os from collections.abc import Generator, Iterator, Sequence from types import TracebackType from typing import ( TYPE_CHECKING, Generic, Optional, Protocol, TypeVar, Union, overload, ) # Import from pygit2 from .ffi import C, ffi if TYPE_CHECKING: from ._libgit2.ffi import ArrayC, GitStrrayC, char, char_pointer def maybe_string(ptr: 'char_pointer | None') -> str | None: if not ptr: return None return ffi.string(ptr).decode('utf8', errors='surrogateescape') @overload def to_bytes( s: str | bytes | os.PathLike[str] | os.PathLike[bytes], encoding: str = 'utf-8', errors: str = 'strict', ) -> bytes: ... @overload def to_bytes( s: Union['ffi.NULL_TYPE', None], encoding: str = 'utf-8', errors: str = 'strict', ) -> Union['ffi.NULL_TYPE']: ... def to_bytes( s: Union[str, bytes, 'ffi.NULL_TYPE', os.PathLike[str], os.PathLike[bytes], None], encoding: str = 'utf-8', errors: str = 'strict', ) -> Union[bytes, 'ffi.NULL_TYPE']: if s == ffi.NULL or s is None: return ffi.NULL if hasattr(s, '__fspath__'): s = os.fspath(s) if isinstance(s, bytes): return s return s.encode(encoding, errors) # type: ignore[union-attr] def to_str(s: str | bytes | os.PathLike[str] | os.PathLike[bytes]) -> str: if hasattr(s, '__fspath__'): s = os.fspath(s) if type(s) is str: return s if type(s) is bytes: return s.decode() raise TypeError(f'unexpected type "{repr(s)}"') def ptr_to_bytes(ptr_cdata) -> bytes: """ Convert a pointer coming from C code () to a byte buffer containing the address that the pointer refers to. """ pp = ffi.new('void **', ptr_cdata) return bytes(ffi.buffer(pp)[:]) @contextlib.contextmanager def new_git_strarray() -> Generator['GitStrrayC', None, None]: strarray = ffi.new('git_strarray *') yield strarray C.git_strarray_dispose(strarray) def strarray_to_strings(arr) -> list[str]: """ Return a list of strings from a git_strarray pointer. Free the strings contained in the git_strarry, this means it won't be usable after calling this function. """ try: return [ffi.string(arr.strings[i]).decode('utf-8') for i in range(arr.count)] finally: C.git_strarray_dispose(arr) class StrArray: """A git_strarray wrapper Use this in order to get a git_strarray* to pass to libgit2 out of a list of strings. This has a context manager, which you should use, e.g. with StrArray(list_of_strings) as arr: C.git_function_that_takes_strarray(arr.ptr) To make a pre-existing git_strarray point to the provided list of strings, use the context manager's assign_to() method: struct = ffi.new('git_strarray *', [ffi.NULL, 0]) with StrArray(list_of_strings) as arr: arr.assign_to(struct) The above construct is still subject to FFI scoping rules, i.e. the contents of 'struct' only remain valid within the StrArray context. """ __array: 'GitStrrayC | ffi.NULL_TYPE' __strings: list['None | ArrayC[char]'] __arr: 'ArrayC[char_pointer]' def __init__(self, lst: None | Sequence[str | os.PathLike[str]]): # Allow passing in None as lg2 typically considers them the same as empty if lst is None: self.__array = ffi.NULL return if not isinstance(lst, (list, tuple)): raise TypeError('Value must be a list') strings: list[None | 'ArrayC[char]'] = [None] * len(lst) for i in range(len(lst)): li = lst[i] if not isinstance(li, str) and not hasattr(li, '__fspath__'): raise TypeError('Value must be a string or PathLike object') strings[i] = ffi.new('char []', to_bytes(li)) self.__arr = ffi.new('char *[]', strings) self.__strings = strings self.__array = ffi.new('git_strarray *', [self.__arr, len(strings)]) # type: ignore[call-overload] def __enter__(self) -> 'StrArray': return self def __exit__( self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> None: pass @property def ptr(self) -> 'GitStrrayC | ffi.NULL_TYPE': return self.__array def assign_to(self, git_strarray: 'GitStrrayC') -> None: if self.__array == ffi.NULL: git_strarray.strings = ffi.NULL git_strarray.count = 0 else: git_strarray.strings = self.__arr git_strarray.count = len(self.__strings) T = TypeVar('T') U = TypeVar('U', covariant=True) class SequenceProtocol(Protocol[U]): def __len__(self) -> int: ... def __getitem__(self, index: int) -> U: ... class GenericIterator(Generic[T]): """Helper to easily implement an iterator. The constructor gets a container which must implement __len__ and __getitem__ """ def __init__(self, container: SequenceProtocol[T]) -> None: self.container = container self.length = len(container) self.idx = 0 def __iter__(self) -> Iterator[T]: return self def __next__(self) -> T: idx = self.idx if idx >= self.length: raise StopIteration self.idx += 1 return self.container[idx] libgit2-pygit2-d86662e/pyproject.toml000066400000000000000000000051751521317216500175570ustar00rootroot00000000000000[build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" [tool.cibuildwheel] enable = ["pypy"] skip = "*musllinux_ppc64le" archs = ["native"] build-frontend = "default" dependency-versions = "pinned" environment = {LIBGIT2="$(pwd)/ci", LIBGIT2_VERSION="1.9.4", LIBSSH2_VERSION="1.11.1", OPENSSL_VERSION="3.5.4"} before-all = "sh build.sh" test-command = "pytest" test-sources = ["test", "pytest.ini"] before-test = "pip install -r {package}/requirements-test.txt" # Will avoid testing on emulated architectures (specifically ppc64le) # Also, skip testing pypy on macOS arm64 due issue with bootstrapping git config paths # see https://github.com/libgit2/pygit2/issues/1442 test-skip = "*-*linux_ppc64le pp*-macosx_arm64" [tool.cibuildwheel.linux] repair-wheel-command = "LD_LIBRARY_PATH=/project/ci/lib64 auditwheel repair -w {dest_dir} {wheel}" [[tool.cibuildwheel.overrides]] select = "*-musllinux*" repair-wheel-command = "LD_LIBRARY_PATH=/project/ci/lib auditwheel repair -w {dest_dir} {wheel}" [tool.cibuildwheel.macos] repair-wheel-command = "DYLD_LIBRARY_PATH={package}/ci/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}" [tool.cibuildwheel.windows] environment.LIBGIT2_SRC = "build/libgit2_src" environment.LIBGIT2_VERSION = "1.9.4" before-all = "powershell -File build.ps1" before-build = "" [[tool.cibuildwheel.overrides]] select="*-win_amd64" inherit.environment="append" environment.CMAKE_GENERATOR = "Visual Studio 18 2026" environment.CMAKE_GENERATOR_PLATFORM = "x64" environment.CMAKE_INSTALL_PREFIX = "C:/libgit2_install_x86_64" environment.LIBGIT2 = "C:/libgit2_install_x86_64" [[tool.cibuildwheel.overrides]] select="*-win32" inherit.environment="append" environment.CMAKE_GENERATOR = "Visual Studio 18 2026" environment.CMAKE_GENERATOR_PLATFORM = "Win32" environment.CMAKE_INSTALL_PREFIX = "C:/libgit2_install_x86" environment.LIBGIT2 = "C:/libgit2_install_x86" [[tool.cibuildwheel.overrides]] select="*-win_arm64" inherit.environment="append" environment.CMAKE_GENERATOR = "Visual Studio 17 2022" environment.CMAKE_GENERATOR_PLATFORM = "ARM64" environment.CMAKE_INSTALL_PREFIX = "C:/libgit2_install_arm64" environment.LIBGIT2 = "C:/libgit2_install_arm64" [tool.ruff] extend-exclude = [ ".cache", ".coverage", "build", "site-packages", "venv*"] target-version = "py311" # oldest supported Python version [tool.ruff.lint] select = ["E4", "E7", "E9", "F", "I", "UP035", "UP007"] [tool.ruff.format] quote-style = "single" [tool.codespell] # Ref: https://github.com/codespell-project/codespell#using-a-config-file skip = '.git*' check-hidden = true # ignore-regex = '' ignore-words-list = 'devault,claus' libgit2-pygit2-d86662e/pytest.ini000066400000000000000000000001001521317216500166530ustar00rootroot00000000000000[pytest] addopts = --capture=no -ra --verbose testpaths = test/ libgit2-pygit2-d86662e/requirements-test.txt000066400000000000000000000000221521317216500210660ustar00rootroot00000000000000pytest pytest-cov libgit2-pygit2-d86662e/requirements-typing.txt000066400000000000000000000000201521317216500214170ustar00rootroot00000000000000mypy types-cffi libgit2-pygit2-d86662e/requirements-wheel.txt000066400000000000000000000000241521317216500212150ustar00rootroot00000000000000cibuildwheel ~= 3.3 libgit2-pygit2-d86662e/requirements.txt000066400000000000000000000000601521317216500201130ustar00rootroot00000000000000cffi>=2.0 setuptools ; python_version >= "3.12" libgit2-pygit2-d86662e/setup.cfg000066400000000000000000000001311521317216500164470ustar00rootroot00000000000000[pycodestyle] exclude = .eggs,.git,.tox,build,dist,docs,venv* select = E4,E9,W1,W2,W3,W6 libgit2-pygit2-d86662e/setup.py000066400000000000000000000131611521317216500163470ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # mypy: disable-error-code="import-not-found, import-untyped" # Import setuptools before distutils to avoid user warning import os import sys from distutils import log # type: ignore[attr-defined] from distutils.command.build import build from distutils.command.sdist import sdist from pathlib import Path from subprocess import PIPE, Popen from setuptools import Extension, setup # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') from _build import __version__, get_libgit2_paths del sys.path[0] libgit2_bin, libgit2_kw = get_libgit2_paths() class sdist_files_from_git(sdist): def get_file_list(self) -> None: popen = Popen( ['git', 'ls-files'], stdout=PIPE, stderr=PIPE, universal_newlines=True ) stdoutdata, stderrdata = popen.communicate() if popen.returncode != 0: print(stderrdata) sys.exit() def exclude(line: str) -> bool: for prefix in ['.', 'docs/', 'misc/']: if line.startswith(prefix): return True return False for line in stdoutdata.splitlines(): if not exclude(line): self.filelist.append(line) # Ok self.filelist.sort() self.filelist.remove_duplicates() self.write_manifest() classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Software Development :: Version Control', 'Typing :: Typed', ] __dir__ = Path(__file__).parent long_description = (__dir__ / 'README.md').read_text() cmdclass = { 'sdist': sdist_files_from_git, } # On Windows, we install the git2.dll too. class BuildWithDLLs(build): def _get_dlls(self) -> list[tuple[Path, Path]]: # return a list of (FQ-in-name, relative-out-name) tuples. ret = [] bld_ext = self.distribution.get_command_obj('build_ext') compiler_type = bld_ext.compiler.compiler_type # type: ignore[attr-defined] libgit2_dlls = [] if compiler_type == 'msvc': libgit2_dlls.append('git2.dll') elif compiler_type == 'mingw32': libgit2_dlls.append('libgit2.dll') look_dirs = [libgit2_bin] + os.getenv('PATH', '').split(os.pathsep) target = Path(self.build_lib).absolute() / 'pygit2' for dll in libgit2_dlls: for look in look_dirs: f = Path(look) / dll if f.is_file(): ret.append((f, target)) break else: log.warn(f'Could not find required DLL {dll} to include') log.debug(f'(looked in {look_dirs})') return ret def run(self) -> None: build.run(self) for s, d in self._get_dlls(): self.copy_file(s, d) # On Windows we package up the dlls with the plugin. if os.name == 'nt': cmdclass['build'] = BuildWithDLLs # type: ignore[assignment] src = __dir__ / 'src' pygit2_exts = [str(path) for path in sorted(src.iterdir()) if path.suffix == '.c'] ext_modules = [Extension('pygit2._pygit2', pygit2_exts, **libgit2_kw)] setup( name='pygit2', description='Python bindings for libgit2.', keywords='git', version=__version__, classifiers=classifiers, license='GPLv2 with linking exception', maintainer='J. David Ibáñez', maintainer_email='jdavid.ibp@gmail.com', long_description=long_description, long_description_content_type='text/markdown', packages=['pygit2'], package_data={'pygit2': ['decl/*.h', '*.pyi', 'py.typed']}, zip_safe=False, cmdclass=cmdclass, cffi_modules=['pygit2/_run.py:ffi'], ext_modules=ext_modules, # Requirements python_requires='>=3.11', setup_requires=['cffi>=2.0'], install_requires=['cffi>=2.0'], # URLs url='https://github.com/libgit2/pygit2', project_urls={ 'Documentation': 'https://www.pygit2.org/', 'Changelog': 'https://github.com/libgit2/pygit2/blob/master/CHANGELOG.md', 'Funding': 'https://github.com/sponsors/jdavid', }, ) libgit2-pygit2-d86662e/src/000077500000000000000000000000001521317216500154225ustar00rootroot00000000000000libgit2-pygit2-d86662e/src/blob.c000066400000000000000000000415301521317216500165070ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include #include "diff.h" #include "error.h" #include "object.h" #include "oid.h" #include "patch.h" #include "utils.h" extern PyObject *GitError; extern PyTypeObject BlobType; PyDoc_STRVAR(Blob_diff__doc__, "diff([blob: Blob, flags: int = GIT_DIFF_NORMAL, old_as_path: str, new_as_path: str]) -> Patch\n" "\n" "Directly generate a :py:class:`pygit2.Patch` from the difference\n" "between two blobs.\n" "\n" "Returns: Patch.\n" "\n" "Parameters:\n" "\n" "blob : Blob\n" " The :py:class:`~pygit2.Blob` to diff.\n" "\n" "flags\n" " A combination of GIT_DIFF_* constant.\n" "\n" "old_as_path : str\n" " Treat old blob as if it had this filename.\n" "\n" "new_as_path : str\n" " Treat new blob as if it had this filename.\n" "\n" "context_lines: int\n" " Number of unchanged lines that define the boundary of a hunk\n" " (and to display before and after).\n" "\n" "interhunk_lines: int\n" " Maximum number of unchanged lines between hunk boundaries\n" " before the hunks will be merged into one.\n"); PyObject * Blob_diff(Blob *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_patch *patch; char *old_as_path = NULL, *new_as_path = NULL; Blob *other = NULL; int err; char *keywords[] = {"blob", "flags", "old_as_path", "new_as_path", "context_lines", "interhunk_lines", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IssHH", keywords, &BlobType, &other, &opts.flags, &old_as_path, &new_as_path, &opts.context_lines, &opts.interhunk_lines)) return NULL; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load if (other && Object__load((Object*)other) == NULL) { return NULL; } // Lazy load err = git_patch_from_blobs(&patch, self->blob, old_as_path, other ? other->blob : NULL, new_as_path, &opts); if (err < 0) return Error_set(err); return wrap_patch(patch, self, other); } PyDoc_STRVAR(Blob_diff_to_buffer__doc__, "diff_to_buffer(buffer: bytes = None, flags: int = GIT_DIFF_NORMAL[, old_as_path: str, buffer_as_path: str]) -> Patch\n" "\n" "Directly generate a :py:class:`~pygit2.Patch` from the difference\n" "between a blob and a buffer.\n" "\n" "Returns: Patch.\n" "\n" "Parameters:\n" "\n" "buffer : bytes\n" " Raw data for new side of diff.\n" "\n" "flags\n" " A combination of GIT_DIFF_* constants.\n" "\n" "old_as_path : str\n" " Treat old blob as if it had this filename.\n" "\n" "buffer_as_path : str\n" " Treat buffer as if it had this filename.\n"); PyObject * Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_patch *patch; char *old_as_path = NULL, *buffer_as_path = NULL; const char *buffer = NULL; Py_ssize_t buffer_len; int err; char *keywords[] = {"buffer", "flags", "old_as_path", "buffer_as_path", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|z#Iss", keywords, &buffer, &buffer_len, &opts.flags, &old_as_path, &buffer_as_path)) return NULL; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load err = git_patch_from_blob_and_buffer(&patch, self->blob, old_as_path, buffer, buffer_len, buffer_as_path, &opts); if (err < 0) return Error_set(err); return wrap_patch(patch, self, NULL); } struct blob_filter_stream { git_writestream stream; PyObject *py_queue; PyObject *py_ready; PyObject *py_closed; Py_ssize_t chunk_size; }; static int blob_filter_stream_write( git_writestream *s, const char *buffer, size_t len) { struct blob_filter_stream *stream = (struct blob_filter_stream *)s; const char *pos = buffer; const char *endpos = buffer + len; Py_ssize_t chunk_size; PyObject *result; PyGILState_STATE gil = PyGILState_Ensure(); int err = 0; while (pos < endpos) { chunk_size = endpos - pos; if (stream->chunk_size < chunk_size) chunk_size = stream->chunk_size; result = PyObject_CallMethod(stream->py_queue, "put", "y#", pos, chunk_size); if (result == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to put chunk to queue"); err = GIT_ERROR; goto done; } Py_DECREF(result); result = PyObject_CallMethod(stream->py_ready, "set", NULL); if (result == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to signal queue ready"); err = GIT_ERROR; goto done; } pos += chunk_size; } done: PyGILState_Release(gil); return err; } static int blob_filter_stream_close(git_writestream *s) { struct blob_filter_stream *stream = (struct blob_filter_stream *)s; PyGILState_STATE gil = PyGILState_Ensure(); PyObject *result; int err = 0; /* Signal closed and then ready in that order so consumers can block on * ready.wait() and then check for indicated EOF (via closed.is_set()) */ result = PyObject_CallMethod(stream->py_closed, "set", NULL); if (result == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to signal writer closed"); err = GIT_ERROR; } result = PyObject_CallMethod(stream->py_ready, "set", NULL); if (result == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to signal queue ready"); err = GIT_ERROR; } PyGILState_Release(gil); return err; } static void blob_filter_stream_free(git_writestream *s) { } #define STREAM_CHUNK_SIZE (8 * 1024) PyDoc_STRVAR(Blob__write_to_queue__doc__, "_write_to_queue(queue: queue.Queue, ready: threading.Event, done: threading.Event, chunk_size: int = io.DEFAULT_BUFFER_SIZE, [as_path: str = None, flags: enums.BlobFilter = enums.BlobFilter.CHECK_FOR_BINARY, commit_id: oid = None]) -> None\n" "\n" "Write the contents of the blob in chunks to `queue`.\n" "If `as_path` is None, the raw contents of blob will be written to the queue,\n" "otherwise the contents of the blob will be filtered.\n" "\n" "In most cases, the higher level `BlobIO` wrapper should be used when\n" "streaming blob content instead of calling this method directly.\n" "\n" "Note that this method will block the current thread until all chunks have\n" "been written to the queue. The GIL will be released while running\n" "libgit2 filtering.\n" "\n" "Returns: The filtered content.\n" "\n" "Parameters:\n" "\n" "queue: queue.Queue\n" " Destination queue.\n" "\n" "ready: threading.Event\n" " Event to signal consumers that the data is available for reading.\n" " This event is also set upon closing the writer in order to indicate \n" " EOF.\n" "\n" "closed: threading.Event\n" " Event to signal consumers that the writer is closed.\n" "\n" "chunk_size : int\n" " Maximum size of chunks to be written to `queue`.\n" "\n" "as_path : str\n" " When set, the blob contents will be filtered as if it had this\n" " filename (used for attribute lookups).\n" "\n" "flags : enums.BlobFilter\n" " A combination of BlobFilter constants (only applicable when `as_path` is set).\n" "\n" "commit_id : oid\n" " Commit to load attributes from when ATTRIBUTES_FROM_COMMIT is\n" " specified in `flags` (only applicable when `as_path` is set).\n"); PyObject * Blob__write_to_queue(Blob *self, PyObject *args, PyObject *kwds) { PyObject *py_queue = NULL; PyObject *py_ready = NULL; PyObject *py_closed = NULL; Py_ssize_t chunk_size = STREAM_CHUNK_SIZE; char *as_path = NULL; PyObject *py_oid = NULL; int err; char *keywords[] = {"queue", "ready", "closed", "chunk_size", "as_path", "flags", "commit_id", NULL}; git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT; git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT; git_filter_list *fl = NULL; git_blob *blob = NULL; const git_oid *blob_oid; struct blob_filter_stream writer; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO|nzIO", keywords, &py_queue, &py_ready, &py_closed, &chunk_size, &as_path, &opts.flags, &py_oid)) return NULL; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load /* we load our own copy of this blob since libgit2 objects are not * thread-safe */ blob_oid = Object__id((Object*)self); err = git_blob_lookup(&blob, git_blob_owner(self->blob), blob_oid); if (err < 0) return Error_set(err); if (as_path != NULL && !((opts.flags & GIT_BLOB_FILTER_CHECK_FOR_BINARY) != 0 && git_blob_is_binary(blob))) { if (py_oid != NULL && py_oid != Py_None) { err = py_oid_to_git_oid(py_oid, &opts.attr_commit_id); if (err < 0) return Error_set(err); } if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0) filter_opts.flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES; if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD) != 0) filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD; if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_COMMIT; git_oid_cpy(&filter_opts.attr_commit_id, &opts.attr_commit_id); err = git_filter_list_load_ext(&fl, git_blob_owner(blob), blob, as_path, GIT_FILTER_TO_WORKTREE, &filter_opts); if (err < 0) { if (blob != NULL) git_blob_free(blob); return Error_set(err); } } memset(&writer, 0, sizeof(struct blob_filter_stream)); writer.stream.write = blob_filter_stream_write; writer.stream.close = blob_filter_stream_close; writer.stream.free = blob_filter_stream_free; writer.py_queue = py_queue; writer.py_ready = py_ready; writer.py_closed = py_closed; writer.chunk_size = chunk_size; Py_INCREF(writer.py_queue); Py_INCREF(writer.py_ready); Py_INCREF(writer.py_closed); Py_BEGIN_ALLOW_THREADS; err = git_filter_list_stream_blob(fl, blob, &writer.stream); Py_END_ALLOW_THREADS; git_filter_list_free(fl); if (writer.py_queue != NULL) Py_DECREF(writer.py_queue); if (writer.py_ready != NULL) Py_DECREF(writer.py_ready); if (writer.py_closed != NULL) Py_DECREF(writer.py_closed); if (blob != NULL) git_blob_free(blob); if (err < 0) return Error_set(err); Py_RETURN_NONE; } static PyMethodDef Blob_methods[] = { METHOD(Blob, diff, METH_VARARGS | METH_KEYWORDS), METHOD(Blob, diff_to_buffer, METH_VARARGS | METH_KEYWORDS), METHOD(Blob, _write_to_queue, METH_VARARGS | METH_KEYWORDS), {NULL} }; PyDoc_STRVAR(Blob_size__doc__, "Size in bytes.\n" "\n" "Example:\n" "\n" " >>> print(blob.size)\n" " 130\n"); PyObject * Blob_size__get__(Blob *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load return PyLong_FromLongLong(git_blob_rawsize(self->blob)); } PyDoc_STRVAR(Blob_is_binary__doc__, "True if binary data, False if not."); PyObject * Blob_is_binary__get__(Blob *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load if (git_blob_is_binary(self->blob)) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Blob_data__doc__, "The contents of the blob, a byte string. This is the same as\n" "Blob.read_raw().\n" "\n" "Example, print the contents of the ``.gitignore`` file:\n" "\n" " >>> blob = repo['d8022420bf6db02e906175f64f66676df539f2fd']\n" " >>> print(blob.data)\n" " MANIFEST\n" " build\n" " dist\n"); PyGetSetDef Blob_getseters[] = { GETTER(Blob, size), GETTER(Blob, is_binary), {"data", (getter)Object_read_raw, NULL, Blob_data__doc__, NULL}, {NULL} }; static int Blob_getbuffer(Blob *self, Py_buffer *view, int flags) { if (Object__load((Object*)self) == NULL) { return -1; } // Lazy load return PyBuffer_FillInfo(view, (PyObject *) self, (void *) git_blob_rawcontent(self->blob), git_blob_rawsize(self->blob), 1, flags); } static PyBufferProcs Blob_as_buffer = { (getbufferproc)Blob_getbuffer, }; PyDoc_STRVAR(Blob__doc__, "Blob object.\n" "\n" "Blobs implement the buffer interface, which means you can get access\n" "to its data via `memoryview(blob)` without the need to create a copy." ); PyTypeObject BlobType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Blob", /* tp_name */ sizeof(Blob), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Object_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ &Blob_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Blob__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Blob_methods, /* tp_methods */ 0, /* tp_members */ Blob_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/branch.c000066400000000000000000000241161521317216500170270ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "types.h" #include "branch.h" #include "error.h" #include "reference.h" #include "utils.h" extern PyObject *GitError; extern PyTypeObject ReferenceType; PyDoc_STRVAR(Branch_delete__doc__, "delete()\n" "\n" "Delete this branch. It will no longer be valid!"); PyObject * Branch_delete(Branch *self, PyObject *args) { int err; CHECK_REFERENCE(self); /* Delete the branch */ err = git_branch_delete(self->reference); if (err < 0) return Error_set(err); git_reference_free(self->reference); self->reference = NULL; /* Invalidate the pointer */ Py_RETURN_NONE; } PyDoc_STRVAR(Branch_is_head__doc__, "is_head() -> bool\n" "\n" "True if HEAD points at the branch, False otherwise."); PyObject * Branch_is_head(Branch *self) { int err; CHECK_REFERENCE(self); err = git_branch_is_head(self->reference); if (err == 1) Py_RETURN_TRUE; else if (err == 0) Py_RETURN_FALSE; else return Error_set(err); } PyDoc_STRVAR(Branch_is_checked_out__doc__, "is_checked_out() -> bool\n" "\n" "True if branch is checked out by any repo connected to the current one, " " False otherwise."); PyObject * Branch_is_checked_out(Branch *self) { int err; CHECK_REFERENCE(self); err = git_branch_is_checked_out(self->reference); if (err == 1) Py_RETURN_TRUE; else if (err == 0) Py_RETURN_FALSE; else return Error_set(err); } PyDoc_STRVAR(Branch_rename__doc__, "rename(name: str, force: bool = False)\n" "\n" "Move/rename an existing local branch reference. The new branch name will be " "checked for validity.\n" "Returns the new branch."); PyObject * Branch_rename(Branch *self, PyObject *args) { int err, force = 0; git_reference *c_out; const char *c_name; CHECK_REFERENCE(self); if (!PyArg_ParseTuple(args, "s|i", &c_name, &force)) return NULL; err = git_branch_move(&c_out, self->reference, c_name, force); if (err == GIT_OK) return wrap_branch(c_out, self->repo); else return Error_set(err); } PyDoc_STRVAR(Branch_branch_name__doc__, "The name of the local or remote branch."); PyObject * Branch_branch_name__get__(Branch *self) { int err; const char *c_name; CHECK_REFERENCE(self); err = git_branch_name(&c_name, self->reference); if (err == GIT_OK) return to_unicode(c_name, NULL, NULL); else return Error_set(err); } PyDoc_STRVAR(Branch_raw_branch_name__doc__, "The name of the local or remote branch (bytes)."); PyObject * Branch_raw_branch_name__get__(Branch *self) { int err; const char *c_name; CHECK_REFERENCE(self); err = git_branch_name(&c_name, self->reference); if (err == GIT_OK) return PyBytes_FromString(c_name); else return Error_set(err); } PyDoc_STRVAR(Branch_remote_name__doc__, "Find the remote name of a remote-tracking branch.\n" "\n" "This will return the name of the remote whose fetch refspec is matching " "the given branch. E.g. given a branch 'refs/remotes/test/master', it will " "extract the 'test' part. If refspecs from multiple remotes match, the " "function will raise ValueError."); PyObject * Branch_remote_name__get__(Branch *self) { int err; git_buf name = {NULL}; const char *branch_name; PyObject *py_name; CHECK_REFERENCE(self); branch_name = git_reference_name(self->reference); err = git_branch_remote_name(&name, self->repo->repo, branch_name); if (err < GIT_OK) return Error_set(err); py_name = to_unicode_n(name.ptr, name.size, NULL, NULL); git_buf_dispose(&name); return py_name; } PyDoc_STRVAR(Branch_upstream__doc__, "The branch's upstream branch or None if this branch does not have an upstream set. " "Set to None to unset the upstream configuration."); PyObject * Branch_upstream__get__(Branch *self) { int err; git_reference *c_reference; CHECK_REFERENCE(self); err = git_branch_upstream(&c_reference, self->reference); if (err == GIT_ENOTFOUND) Py_RETURN_NONE; else if (err < GIT_OK) return Error_set(err); return wrap_branch(c_reference, self->repo); } int Branch_upstream__set__(Branch *self, Reference *py_ref) { int err; const char *branch_name = NULL; CHECK_REFERENCE_INT(self); if ((PyObject *)py_ref != Py_None) { if (!PyObject_TypeCheck(py_ref, (PyTypeObject *)&ReferenceType)) { PyErr_SetObject(PyExc_TypeError, (PyObject *)py_ref); return -1; } CHECK_REFERENCE_INT(py_ref); err = git_branch_name(&branch_name, py_ref->reference); if (err < GIT_OK) { Error_set(err); return -1; } } err = git_branch_set_upstream(self->reference, branch_name); if (err < GIT_OK) { Error_set(err); return -1; } return 0; } PyDoc_STRVAR(Branch_upstream_name__doc__, "The name of the reference set to be the upstream of this one"); PyObject * Branch_upstream_name__get__(Branch *self) { int err; git_buf name = {NULL}; const char *branch_name; PyObject *py_name; CHECK_REFERENCE(self); branch_name = git_reference_name(self->reference); err = git_branch_upstream_name(&name, self->repo->repo, branch_name); if (err < GIT_OK) return Error_set(err); py_name = to_unicode_n(name.ptr, name.size, NULL, NULL); git_buf_dispose(&name); return py_name; } static PyMethodDef Branch_methods[] = { METHOD(Branch, delete, METH_NOARGS), METHOD(Branch, is_head, METH_NOARGS), METHOD(Branch, is_checked_out, METH_NOARGS), METHOD(Branch, rename, METH_VARARGS), {NULL} }; PyGetSetDef Branch_getseters[] = { GETTER(Branch, branch_name), GETTER(Branch, raw_branch_name), GETTER(Branch, remote_name), GETSET(Branch, upstream), GETTER(Branch, upstream_name), {NULL} }; PyDoc_STRVAR(Branch__doc__, "Branch."); PyTypeObject BranchType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Branch", /* tp_name */ sizeof(Branch), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Branch__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Branch_methods, /* tp_methods */ 0, /* tp_members */ Branch_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_branch(git_reference *c_reference, Repository *repo) { Branch *py_branch=NULL; py_branch = PyObject_New(Branch, &BranchType); if (py_branch) { py_branch->reference = c_reference; if (repo) { py_branch->repo = repo; Py_INCREF(repo); } } return (PyObject *)py_branch; } libgit2-pygit2-d86662e/src/branch.h000066400000000000000000000031301521317216500170250ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_branch_h #define INCLUDE_pygit2_branch_h #define PY_SSIZE_T_CLEAN #include #include PyObject* Branch_delete(Branch *self, PyObject *args); PyObject* Branch_is_head(Branch *self); PyObject* Branch_is_checked_out(Branch *self); PyObject* Branch_rename(Branch *self, PyObject *args); PyObject* wrap_branch(git_reference *c_reference, Repository *repo); #endif libgit2-pygit2-d86662e/src/commit.c000066400000000000000000000266151521317216500170700ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "utils.h" #include "signature.h" #include "object.h" #include "oid.h" extern PyTypeObject TreeType; extern PyObject *GitError; PyDoc_STRVAR(Commit_message_encoding__doc__, "Message encoding."); PyObject * Commit_message_encoding__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const char *encoding = git_commit_message_encoding(self->commit); if (encoding == NULL) Py_RETURN_NONE; return to_encoding(encoding); } PyDoc_STRVAR(Commit_message__doc__, "The commit message, a text string."); PyObject * Commit_message__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const char *message = git_commit_message(self->commit); const char *encoding = git_commit_message_encoding(self->commit); return to_unicode(message, encoding, NULL); } PyDoc_STRVAR(Commit_gpg_signature__doc__, "A tuple with the GPG signature and the signed payload."); PyObject * Commit_gpg_signature__get__(Commit *self) { git_buf gpg_signature = { NULL }, signed_data = { NULL }; PyObject *py_gpg_signature, *py_signed_data; const git_oid *oid = Object__id((Object*)self); int err = git_commit_extract_signature( &gpg_signature, &signed_data, self->repo->repo, (git_oid*) oid, NULL ); if (err != GIT_OK){ git_buf_dispose(&gpg_signature); git_buf_dispose(&signed_data); if (err == GIT_ENOTFOUND){ return Py_BuildValue("OO", Py_None, Py_None); } return Error_set(err); } py_gpg_signature = PyBytes_FromString(gpg_signature.ptr); py_signed_data = PyBytes_FromString(signed_data.ptr); git_buf_dispose(&gpg_signature); git_buf_dispose(&signed_data); return Py_BuildValue("NN", py_gpg_signature, py_signed_data); } PyDoc_STRVAR(Commit_message_trailers__doc__, "Returns commit message trailers (e.g., Bug: 1234) as a dictionary." ); PyObject * Commit_message_trailers__get__(Commit *self) { git_message_trailer_array gmt_arr; int i, trailer_count, err; PyObject *dict; PyObject *py_val; const char *message = git_commit_message(self->commit); const char *encoding = git_commit_message_encoding(self->commit); err = git_message_trailers(&gmt_arr, message); if (err < 0) return Error_set(err); dict = PyDict_New(); if (dict == NULL) goto error; trailer_count = gmt_arr.count; for (i=0; i < trailer_count; i++) { py_val = to_unicode(gmt_arr.trailers[i].value, encoding, NULL); err = PyDict_SetItemString(dict, gmt_arr.trailers[i].key, py_val); Py_DECREF(py_val); if (err < 0) goto error; } git_message_trailer_array_free(&gmt_arr); return dict; error: git_message_trailer_array_free(&gmt_arr); Py_CLEAR(dict); return NULL; } PyDoc_STRVAR(Commit_raw_message__doc__, "Message (bytes)."); PyObject * Commit_raw_message__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load return PyBytes_FromString(git_commit_message(self->commit)); } PyDoc_STRVAR(Commit_commit_time__doc__, "Commit time."); PyObject * Commit_commit_time__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load return PyLong_FromLongLong(git_commit_time(self->commit)); } PyDoc_STRVAR(Commit_commit_time_offset__doc__, "Commit time offset."); PyObject * Commit_commit_time_offset__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load return PyLong_FromLong(git_commit_time_offset(self->commit)); } PyDoc_STRVAR(Commit_committer__doc__, "The committer of the commit."); PyObject * Commit_committer__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const git_signature *signature = git_commit_committer(self->commit); const char *encoding = git_commit_message_encoding(self->commit); return build_signature((Object*)self, signature, encoding); } PyDoc_STRVAR(Commit_author__doc__, "The author of the commit."); PyObject * Commit_author__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const git_signature *signature = git_commit_author(self->commit); const char *encoding = git_commit_message_encoding(self->commit); return build_signature((Object*)self, signature, encoding); } PyDoc_STRVAR(Commit_tree__doc__, "The tree object attached to the commit."); PyObject * Commit_tree__get__(Commit *self) { git_tree *tree; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load int err = git_commit_tree(&tree, self->commit); if (err == GIT_ENOTFOUND) { char tree_id[GIT_OID_HEXSZ + 1] = { 0 }; git_oid_fmt(tree_id, git_commit_tree_id(self->commit)); return PyErr_Format(GitError, "Unable to read tree %s", tree_id); } if (err < 0) return Error_set(err); return wrap_object((git_object*)tree, self->repo, NULL); } PyDoc_STRVAR(Commit_tree_id__doc__, "The id of the tree attached to the commit."); PyObject * Commit_tree_id__get__(Commit *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load return git_oid_to_python(git_commit_tree_id(self->commit)); } PyDoc_STRVAR(Commit_parents__doc__, "The list of parent commits."); PyObject * Commit_parents__get__(Commit *self) { Repository *py_repo; unsigned int i, parent_count; const git_oid *parent_oid; git_commit *parent; int err; PyObject *py_parent; PyObject *list; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load parent_count = git_commit_parentcount(self->commit); list = PyList_New(parent_count); if (!list) return NULL; py_repo = self->repo; for (i=0; i < parent_count; i++) { parent_oid = git_commit_parent_id(self->commit, i); if (parent_oid == NULL) { Py_DECREF(list); Error_set(GIT_ENOTFOUND); return NULL; } err = git_commit_lookup(&parent, py_repo->repo, parent_oid); if (err < 0) { Py_DECREF(list); return Error_set_oid(err, parent_oid, GIT_OID_HEXSZ); } py_parent = wrap_object((git_object*)parent, py_repo, NULL); if (py_parent == NULL) { Py_DECREF(list); return NULL; } PyList_SET_ITEM(list, i, py_parent); } return list; } PyDoc_STRVAR(Commit_parent_ids__doc__, "The list of parent commits' ids."); PyObject * Commit_parent_ids__get__(Commit *self) { unsigned int i, parent_count; const git_oid *id; PyObject *list; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load parent_count = git_commit_parentcount(self->commit); list = PyList_New(parent_count); if (!list) return NULL; for (i=0; i < parent_count; i++) { id = git_commit_parent_id(self->commit, i); PyList_SET_ITEM(list, i, git_oid_to_python(id)); } return list; } PyGetSetDef Commit_getseters[] = { GETTER(Commit, message_encoding), GETTER(Commit, message), GETTER(Commit, raw_message), GETTER(Commit, commit_time), GETTER(Commit, commit_time_offset), GETTER(Commit, committer), GETTER(Commit, author), GETTER(Commit, gpg_signature), GETTER(Commit, tree), GETTER(Commit, tree_id), GETTER(Commit, parents), GETTER(Commit, parent_ids), GETTER(Commit, message_trailers), {NULL} }; PyDoc_STRVAR(Commit__doc__, "Commit objects."); PyTypeObject CommitType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Commit", /* tp_name */ sizeof(Commit), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Object_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Commit__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ Commit_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/diff.c000066400000000000000000001213371521317216500165050ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "diff.h" #include "error.h" #include "oid.h" #include "patch.h" #include "types.h" #include "utils.h" extern PyObject *GitError; extern PyTypeObject TreeType; extern PyTypeObject IndexType; extern PyTypeObject DiffType; extern PyTypeObject DiffDeltaType; extern PyTypeObject DiffFileType; extern PyTypeObject DiffHunkType; extern PyTypeObject DiffLineType; extern PyTypeObject DiffStatsType; extern PyTypeObject RepositoryType; extern PyObject *DeltaStatusEnum; extern PyObject *DiffFlagEnum; extern PyObject *FileModeEnum; PyObject * wrap_diff(git_diff *diff, Repository *repo) { Diff *py_diff; py_diff = PyObject_New(Diff, &DiffType); if (py_diff) { Py_XINCREF(repo); py_diff->repo = repo; py_diff->diff = diff; } return (PyObject*) py_diff; } PyObject * wrap_diff_file(const git_diff_file *file) { DiffFile *py_file; if (!file) Py_RETURN_NONE; py_file = PyObject_New(DiffFile, &DiffFileType); if (py_file) { py_file->id = git_oid_to_python(&file->id); if (file->path) { py_file->path = strdup(file->path); py_file->raw_path = PyBytes_FromString(file->path); } else { py_file->path = NULL; py_file->raw_path = NULL; } py_file->size = file->size; py_file->flags = file->flags; py_file->mode = file->mode; } return (PyObject *) py_file; } PyObject * wrap_diff_delta(const git_diff_delta *delta) { DiffDelta *py_delta; if (!delta) Py_RETURN_NONE; py_delta = PyObject_New(DiffDelta, &DiffDeltaType); if (py_delta) { py_delta->status = delta->status; py_delta->flags = delta->flags; py_delta->similarity = delta->similarity; py_delta->nfiles = delta->nfiles; py_delta->old_file = wrap_diff_file(&delta->old_file); py_delta->new_file = wrap_diff_file(&delta->new_file); } return (PyObject *) py_delta; } PyObject * wrap_diff_hunk(Patch *patch, size_t idx) { DiffHunk *py_hunk; const git_diff_hunk *hunk; size_t lines_in_hunk; int err; err = git_patch_get_hunk(&hunk, &lines_in_hunk, patch->patch, idx); if (err < 0) return Error_set(err); py_hunk = PyObject_New(DiffHunk, &DiffHunkType); if (py_hunk) { Py_INCREF(patch); py_hunk->patch = patch; py_hunk->hunk = hunk; py_hunk->idx = idx; py_hunk->n_lines = lines_in_hunk; } return (PyObject *) py_hunk; } PyObject * wrap_diff_stats(git_diff *diff) { git_diff_stats *stats; DiffStats *py_stats; int err; err = git_diff_get_stats(&stats, diff); if (err < 0) return Error_set(err); py_stats = PyObject_New(DiffStats, &DiffStatsType); if (!py_stats) { git_diff_stats_free(stats); return NULL; } py_stats->stats = stats; return (PyObject *) py_stats; } PyObject * wrap_diff_line(const git_diff_line *line, DiffHunk *hunk) { DiffLine *py_line; py_line = PyObject_New(DiffLine, &DiffLineType); if (py_line) { Py_INCREF(hunk); py_line->hunk = hunk; py_line->line = line; } return (PyObject *) py_line; } static void DiffFile_dealloc(DiffFile *self) { Py_CLEAR(self->id); Py_CLEAR(self->raw_path); free(self->path); PyObject_Del(self); } PyDoc_STRVAR(DiffFile_from_c__doc__, "Method exposed for _checkout_notify_cb to hook into"); /* Expose wrap_diff_file to python so we can call it in callbacks.py. */ PyObject * DiffFile_from_c(DiffFile *dummy, PyObject *py_diff_file_ptr) { const git_diff_file *diff_file; char *buffer; Py_ssize_t length; /* Here we need to do the opposite conversion from the _pointer getters */ if (PyBytes_AsStringAndSize(py_diff_file_ptr, &buffer, &length)) return NULL; if (length != sizeof(git_diff_file *)) { PyErr_SetString(PyExc_TypeError, "passed value is not a pointer"); return NULL; } /* the "buffer" contains the pointer */ diff_file = *((const git_diff_file **) buffer); return wrap_diff_file(diff_file); } PyDoc_STRVAR(DiffFile_flags__doc__, "A combination of enums.DiffFlag constants." ); PyObject * DiffFile_flags__get__(DiffFile *self) { return pygit2_enum(DiffFlagEnum, self->flags); } PyDoc_STRVAR(DiffFile_mode__doc__, "Mode of the entry (an enums.FileMode constant)." ); PyObject * DiffFile_mode__get__(DiffFile *self) { return pygit2_enum(FileModeEnum, self->mode); } PyMemberDef DiffFile_members[] = { MEMBER(DiffFile, id, T_OBJECT, "Oid of the item."), MEMBER(DiffFile, path, T_STRING, "Path to the entry."), MEMBER(DiffFile, raw_path, T_OBJECT, "Path to the entry (bytes)."), MEMBER(DiffFile, size, T_LONG, "Size of the entry."), {NULL} }; static PyMethodDef DiffFile_methods[] = { METHOD(DiffFile, from_c, METH_STATIC | METH_O), {NULL}, }; PyGetSetDef DiffFile_getsetters[] = { GETTER(DiffFile, flags), GETTER(DiffFile, mode), {NULL}, }; PyDoc_STRVAR(DiffFile__doc__, "DiffFile object."); PyTypeObject DiffFileType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffFile", /* tp_name */ sizeof(DiffFile), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DiffFile_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ DiffFile__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ DiffFile_methods, /* tp_methods */ DiffFile_members, /* tp_members */ DiffFile_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyDoc_STRVAR(DiffDelta_status_char__doc__, "status_char() -> str\n" "\n" "Return the single character abbreviation for a delta status code." ); PyObject * DiffDelta_status_char(DiffDelta *self) { char status = git_diff_status_char(self->status); return Py_BuildValue("C", status); } PyDoc_STRVAR(DiffDelta_is_binary__doc__, "True if binary data, False if text, None if not (yet) known." ); PyObject * DiffDelta_is_binary__get__(DiffDelta *self) { if (self->flags & GIT_DIFF_FLAG_BINARY) Py_RETURN_TRUE; if (self->flags & GIT_DIFF_FLAG_NOT_BINARY) Py_RETURN_FALSE; // This means the file has not been loaded, so we don't know whether it's // binary or text Py_RETURN_NONE; } PyDoc_STRVAR(DiffDelta_status__doc__, "An enums.DeltaStatus constant." ); PyObject * DiffDelta_status__get__(DiffDelta *self) { return pygit2_enum(DeltaStatusEnum, self->status); } PyDoc_STRVAR(DiffDelta_flags__doc__, "A combination of enums.DiffFlag constants." ); PyObject * DiffDelta_flags__get__(DiffDelta *self) { return pygit2_enum(DiffFlagEnum, self->flags); } static void DiffDelta_dealloc(DiffDelta *self) { Py_CLEAR(self->old_file); Py_CLEAR(self->new_file); PyObject_Del(self); } static PyMethodDef DiffDelta_methods[] = { METHOD(DiffDelta, status_char, METH_NOARGS), {NULL} }; PyMemberDef DiffDelta_members[] = { MEMBER(DiffDelta, similarity, T_USHORT, "For renamed and copied."), MEMBER(DiffDelta, nfiles, T_USHORT, "Number of files in the delta."), MEMBER(DiffDelta, old_file, T_OBJECT, "\"from\" side of the diff."), MEMBER(DiffDelta, new_file, T_OBJECT, "\"to\" side of the diff."), {NULL} }; PyGetSetDef DiffDelta_getsetters[] = { GETTER(DiffDelta, is_binary), GETTER(DiffDelta, status), GETTER(DiffDelta, flags), {NULL} }; PyDoc_STRVAR(DiffDelta__doc__, "DiffDelta object."); PyTypeObject DiffDeltaType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffDelta", /* tp_name */ sizeof(DiffDelta), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DiffDelta_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ DiffDelta__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ DiffDelta_methods, /* tp_methods */ DiffDelta_members, /* tp_members */ DiffDelta_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static void DiffLine_dealloc(DiffLine *self) { Py_CLEAR(self->hunk); PyObject_Del(self); } PyDoc_STRVAR(DiffLine_origin__doc__, "Type of the diff line"); PyObject * DiffLine_origin__get__(DiffLine *self) { return PyUnicode_FromStringAndSize(&(self->line->origin), 1); } PyDoc_STRVAR(DiffLine_old_lineno__doc__, "Line number in old file or -1 for added line"); PyObject * DiffLine_old_lineno__get__(DiffLine *self) { return PyLong_FromLong(self->line->old_lineno); } PyDoc_STRVAR(DiffLine_new_lineno__doc__, "Line number in new file or -1 for deleted line"); PyObject * DiffLine_new_lineno__get__(DiffLine *self) { return PyLong_FromLong(self->line->new_lineno); } PyDoc_STRVAR(DiffLine_num_lines__doc__, "Number of newline characters in content"); PyObject * DiffLine_num_lines__get__(DiffLine *self) { return PyLong_FromLong(self->line->num_lines); } PyDoc_STRVAR(DiffLine_content_offset__doc__, "Offset in the original file to the content"); PyObject * DiffLine_content_offset__get__(DiffLine *self) { return PyLong_FromLongLong(self->line->content_offset); } PyDoc_STRVAR(DiffLine_content__doc__, "Content of the diff line"); PyObject * DiffLine_content__get__(DiffLine *self) { return to_unicode_n(self->line->content, self->line->content_len, NULL, NULL); } PyDoc_STRVAR(DiffLine_raw_content__doc__, "Content of the diff line (byte string)"); PyObject * DiffLine_raw_content__get__(DiffLine *self) { return PyBytes_FromStringAndSize(self->line->content, self->line->content_len); } PyGetSetDef DiffLine_getsetters[] = { GETTER(DiffLine, origin), GETTER(DiffLine, old_lineno), GETTER(DiffLine, new_lineno), GETTER(DiffLine, num_lines), GETTER(DiffLine, content_offset), GETTER(DiffLine, content), GETTER(DiffLine, raw_content), {NULL} }; PyDoc_STRVAR(DiffLine__doc__, "DiffLine object."); PyTypeObject DiffLineType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffLine", /* tp_name */ sizeof(DiffLine), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DiffLine_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ DiffLine__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ DiffLine_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * diff_get_patch_byindex(git_diff *diff, size_t idx) { git_patch *patch = NULL; int err; err = git_patch_from_diff(&patch, diff, idx); if (err < 0) return Error_set(err); /* libgit2 may decide not to create a patch if the file is "unchanged or binary", but this isn't an error case */ if (patch == NULL) Py_RETURN_NONE; return (PyObject*) wrap_patch(patch, NULL, NULL); } PyObject * DiffIter_iternext(DiffIter *self) { if (self->i < self->n) return diff_get_patch_byindex(self->diff->diff, self->i++); PyErr_SetNone(PyExc_StopIteration); return NULL; } void DiffIter_dealloc(DiffIter *self) { Py_CLEAR(self->diff); PyObject_Del(self); } PyDoc_STRVAR(DiffIter__doc__, "Diff iterator object."); PyTypeObject DiffIterType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffIter", /* tp_name */ sizeof(DiffIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DiffIter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ DiffIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc) DiffIter_iternext, /* tp_iternext */ }; PyObject * diff_get_delta_byindex(git_diff *diff, size_t idx) { const git_diff_delta *delta = git_diff_get_delta(diff, idx); if (delta == NULL) { PyErr_SetObject(PyExc_IndexError, PyLong_FromSize_t(idx)); return NULL; } return (PyObject*) wrap_diff_delta(delta); } PyObject * DeltasIter_iternext(DeltasIter *self) { if (self->i < self->n) return diff_get_delta_byindex(self->diff->diff, self->i++); PyErr_SetNone(PyExc_StopIteration); return NULL; } void DeltasIter_dealloc(DeltasIter *self) { Py_CLEAR(self->diff); PyObject_Del(self); } PyDoc_STRVAR(DeltasIter__doc__, "Deltas iterator object."); PyTypeObject DeltasIterType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DeltasIter", /* tp_name */ sizeof(DeltasIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DeltasIter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ DeltasIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc) DeltasIter_iternext, /* tp_iternext */ }; Py_ssize_t Diff_len(Diff *self) { assert(self->diff); return (Py_ssize_t)git_diff_num_deltas(self->diff); } PyDoc_STRVAR(Diff_patchid__doc__, "Corresponding patchid."); PyObject * Diff_patchid__get__(Diff *self) { git_oid oid; int err; err = git_diff_patchid(&oid, self->diff, NULL); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(Diff_deltas__doc__, "Iterate over the diff deltas."); PyObject * Diff_deltas__get__(Diff *self) { DeltasIter *iter; iter = PyObject_New(DeltasIter, &DeltasIterType); if (iter != NULL) { Py_INCREF(self); iter->diff = self; iter->i = 0; iter->n = git_diff_num_deltas(self->diff); } return (PyObject*)iter; } PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string. Can be None in some cases, such as empty commits."); PyObject * Diff_patch__get__(Diff *self) { git_buf buf = {NULL}; int err = git_diff_to_buf(&buf, self->diff, GIT_DIFF_FORMAT_PATCH); if (err < 0) return Error_set(err); PyObject *py_patch = to_unicode_n(buf.ptr, buf.size, NULL, NULL); git_buf_dispose(&buf); return py_patch; } static void DiffHunk_dealloc(DiffHunk *self) { Py_CLEAR(self->patch); PyObject_Del(self); } PyDoc_STRVAR(DiffHunk_old_start__doc__, "Old start."); PyObject * DiffHunk_old_start__get__(DiffHunk *self) { return PyLong_FromLong(self->hunk->old_start); } PyDoc_STRVAR(DiffHunk_old_lines__doc__, "Old lines."); PyObject * DiffHunk_old_lines__get__(DiffHunk *self) { return PyLong_FromLong(self->hunk->old_lines); } PyDoc_STRVAR(DiffHunk_new_start__doc__, "New start."); PyObject * DiffHunk_new_start__get__(DiffHunk *self) { return PyLong_FromLong(self->hunk->new_start); } PyDoc_STRVAR(DiffHunk_new_lines__doc__, "New lines."); PyObject * DiffHunk_new_lines__get__(DiffHunk *self) { return PyLong_FromLong(self->hunk->new_lines); } PyDoc_STRVAR(DiffHunk_header__doc__, "Header."); PyObject * DiffHunk_header__get__(DiffHunk *self) { return to_unicode_n((const char *) &self->hunk->header, self->hunk->header_len, NULL, NULL); } PyDoc_STRVAR(DiffHunk_lines__doc__, "Lines."); PyObject * DiffHunk_lines__get__(DiffHunk *self) { PyObject *py_lines; PyObject *py_line; const git_diff_line *line; size_t i; int err; // TODO Replace by an iterator py_lines = PyList_New(self->n_lines); for (i = 0; i < self->n_lines; ++i) { err = git_patch_get_line_in_hunk(&line, self->patch->patch, self->idx, i); if (err < 0) return Error_set(err); py_line = wrap_diff_line(line, self); if (py_line == NULL) return NULL; PyList_SetItem(py_lines, i, py_line); } return py_lines; } PyGetSetDef DiffHunk_getsetters[] = { GETTER(DiffHunk, old_start), GETTER(DiffHunk, old_lines), GETTER(DiffHunk, new_start), GETTER(DiffHunk, new_lines), GETTER(DiffHunk, header), GETTER(DiffHunk, lines), {NULL} }; PyDoc_STRVAR(DiffHunk__doc__, "DiffHunk object."); PyTypeObject DiffHunkType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffHunk", /* tp_name */ sizeof(DiffHunk), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DiffHunk_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ DiffHunk__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ DiffHunk_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyDoc_STRVAR(DiffStats_insertions__doc__, "Total number of insertions"); PyObject * DiffStats_insertions__get__(DiffStats *self) { return PyLong_FromSize_t(git_diff_stats_insertions(self->stats)); } PyDoc_STRVAR(DiffStats_deletions__doc__, "Total number of deletions"); PyObject * DiffStats_deletions__get__(DiffStats *self) { return PyLong_FromSize_t(git_diff_stats_deletions(self->stats)); } PyDoc_STRVAR(DiffStats_files_changed__doc__, "Total number of files changed"); PyObject * DiffStats_files_changed__get__(DiffStats *self) { return PyLong_FromSize_t(git_diff_stats_files_changed(self->stats)); } PyDoc_STRVAR(DiffStats_format__doc__, "format(format: enums.DiffStatsFormat, width: int) -> str\n" "\n" "Format the stats as a string.\n" "\n" "Returns: str.\n" "\n" "Parameters:\n" "\n" "format\n" " The format to use. A combination of DiffStatsFormat constants.\n" "\n" "width\n" " The width of the output. The output will be scaled to fit."); PyObject * DiffStats_format(DiffStats *self, PyObject *args, PyObject *kwds) { int err, format; git_buf buf = { 0 }; Py_ssize_t width; PyObject *str; char *keywords[] = {"format", "width", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "in", keywords, &format, &width)) return NULL; if (width <= 0) { PyErr_SetString(PyExc_ValueError, "width must be positive"); return NULL; } err = git_diff_stats_to_buf(&buf, self->stats, format, width); if (err < 0) return Error_set(err); str = to_unicode_n(buf.ptr, buf.size, NULL, NULL); git_buf_dispose(&buf); return str; } static void DiffStats_dealloc(DiffStats *self) { git_diff_stats_free(self->stats); PyObject_Del(self); } static PyMethodDef DiffStats_methods[] = { METHOD(DiffStats, format, METH_VARARGS | METH_KEYWORDS), {NULL} }; PyGetSetDef DiffStats_getsetters[] = { GETTER(DiffStats, insertions), GETTER(DiffStats, deletions), GETTER(DiffStats, files_changed), {NULL} }; PyDoc_STRVAR(DiffStats__doc__, "DiffStats object."); PyTypeObject DiffStatsType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.DiffStats", /* tp_name */ sizeof(DiffStats), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)DiffStats_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ DiffStats__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ DiffStats_methods, /* tp_methods */ 0, /* tp_members */ DiffStats_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyDoc_STRVAR(Diff_from_c__doc__, "Method exposed for Index to hook into"); PyObject * Diff_from_c(Diff *dummy, PyObject *args) { PyObject *py_diff, *py_repository; git_diff *diff; char *buffer; Py_ssize_t length; if (!PyArg_ParseTuple(args, "OO!", &py_diff, &RepositoryType, &py_repository)) return NULL; /* Here we need to do the opposite conversion from the _pointer getters */ if (PyBytes_AsStringAndSize(py_diff, &buffer, &length)) return NULL; if (length != sizeof(git_diff *)) { PyErr_SetString(PyExc_TypeError, "passed value is not a pointer"); return NULL; } /* the "buffer" contains the pointer */ diff = *((git_diff **) buffer); return wrap_diff(diff, (Repository *) py_repository); } PyDoc_STRVAR(Diff_merge__doc__, "merge(diff: Diff)\n" "\n" "Merge one diff into another."); PyObject * Diff_merge(Diff *self, PyObject *args) { Diff *py_diff; int err; if (!PyArg_ParseTuple(args, "O!", &DiffType, &py_diff)) return NULL; err = git_diff_merge(self->diff, py_diff->diff); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Diff_find_similar__doc__, "find_similar(flags: enums.DiffFind = enums.DiffFind.FIND_BY_CONFIG, rename_threshold: int = 50, copy_threshold: int = 50, rename_from_rewrite_threshold: int = 50, break_rewrite_threshold: int = 60, rename_limit: int = 1000)\n" "\n" "Transform a diff marking file renames, copies, etc.\n" "\n" "This modifies a diff in place, replacing old entries that look like\n" "renames or copies with new entries reflecting those changes. This also " "will, if requested, break modified files into add/remove pairs if the " "amount of change is above a threshold.\n" "\n" "flags - Combination of enums.DiffFind.FIND_* and enums.DiffFind.BREAK_* constants." ); PyObject * Diff_find_similar(Diff *self, PyObject *args, PyObject *kwds) { int err; git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; char *keywords[] = {"flags", "rename_threshold", "copy_threshold", "rename_from_rewrite_threshold", "break_rewrite_threshold", "rename_limit", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iHHHHI", keywords, &opts.flags, &opts.rename_threshold, &opts.copy_threshold, &opts.rename_from_rewrite_threshold, &opts.break_rewrite_threshold, &opts.rename_limit)) return NULL; err = git_diff_find_similar(self->diff, &opts); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyObject * Diff_iter(Diff *self) { DiffIter *iter; iter = PyObject_New(DiffIter, &DiffIterType); if (iter != NULL) { Py_INCREF(self); iter->diff = self; iter->i = 0; iter->n = git_diff_num_deltas(self->diff); } return (PyObject*)iter; } PyObject * Diff_getitem(Diff *self, PyObject *value) { size_t i; if (!PyLong_Check(value)) return NULL; /* FIXME Raise error */ i = PyLong_AsSize_t(value); return diff_get_patch_byindex(self->diff, i); } PyDoc_STRVAR(Diff_stats__doc__, "Accumulate diff statistics for all patches."); PyObject * Diff_stats__get__(Diff *self) { return wrap_diff_stats(self->diff); } PyDoc_STRVAR(Diff_parse_diff__doc__, "parse_diff(git_diff: str | bytes) -> Diff\n" "\n" "Parses a git unified diff into a diff object without a repository"); static PyObject * Diff_parse_diff(PyObject *self, PyObject *py_str) { /* A wrapper around git_diff_from_buffer */ git_diff *diff; const char *content = pgit_borrow(py_str); if (content == NULL) return NULL; int err = git_diff_from_buffer(&diff, content, strlen(content)); if (err < 0) return Error_set(err); return wrap_diff(diff, NULL); } static void Diff_dealloc(Diff *self) { git_diff_free(self->diff); Py_CLEAR(self->repo); PyObject_Del(self); } PyGetSetDef Diff_getsetters[] = { GETTER(Diff, deltas), GETTER(Diff, patch), GETTER(Diff, stats), GETTER(Diff, patchid), {NULL} }; PyMappingMethods Diff_as_mapping = { (lenfunc)Diff_len, /* mp_length */ (binaryfunc)Diff_getitem, /* mp_subscript */ 0, /* mp_ass_subscript */ }; static PyMethodDef Diff_methods[] = { METHOD(Diff, merge, METH_VARARGS), METHOD(Diff, find_similar, METH_VARARGS | METH_KEYWORDS), METHOD(Diff, from_c, METH_STATIC | METH_VARARGS), {"parse_diff", (PyCFunction) Diff_parse_diff, METH_O | METH_STATIC, Diff_parse_diff__doc__}, {NULL} }; /* TODO Implement Diff.patches, deprecate Diff_iter and Diff_getitem */ PyDoc_STRVAR(Diff__doc__, "Diff objects."); PyTypeObject DiffType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Diff", /* tp_name */ sizeof(Diff), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Diff_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ &Diff_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ Diff__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)Diff_iter, /* tp_iter */ 0, /* tp_iternext */ Diff_methods, /* tp_methods */ 0, /* tp_members */ Diff_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/diff.h000066400000000000000000000031711521317216500165050ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_diff_h #define INCLUDE_pygit2_diff_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject* wrap_diff(git_diff *diff, Repository *repo); PyObject* wrap_diff_delta(const git_diff_delta *delta); PyObject* wrap_diff_file(const git_diff_file *file); PyObject* wrap_diff_hunk(Patch *patch, size_t idx); PyObject* wrap_diff_line(const git_diff_line *line, DiffHunk *hunk); #endif libgit2-pygit2-d86662e/src/error.c000066400000000000000000000106351521317216500167240ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "error.h" extern PyObject *GitError; extern PyObject *AlreadyExistsError; extern PyObject *InvalidSpecError; PyObject * Error_type(int type) { const git_error* error; /* Expected */ switch (type) { /* Input does not exist in the scope searched. */ case GIT_ENOTFOUND: return PyExc_KeyError; /* A reference with this name already exists */ case GIT_EEXISTS: return AlreadyExistsError; /* The given short oid is ambiguous */ case GIT_EAMBIGUOUS: return PyExc_ValueError; /* The buffer is too short to satisfy the request */ case GIT_EBUFS: return PyExc_ValueError; /* Invalid input spec */ case GIT_EINVALIDSPEC: return InvalidSpecError; /* Skip and passthrough the given ODB backend */ case GIT_PASSTHROUGH: return GitError; /* No entries left in ref walker */ case GIT_ITEROVER: return PyExc_StopIteration; } /* Critical */ error = git_error_last(); if (error != NULL) { switch (error->klass) { case GITERR_NOMEMORY: return PyExc_MemoryError; case GITERR_OS: return PyExc_OSError; case GITERR_INVALID: return PyExc_ValueError; } } return GitError; } PyObject * Error_set(int err) { assert(err < 0); return Error_set_exc(Error_type(err)); } PyObject * Error_set_exc(PyObject* exception) { const git_error* error = git_error_last(); char* message = (error == NULL) ? "(No error information given)" : error->message; PyErr_SetString(exception, message); return NULL; } PyObject * Error_set_str(int err, const char *str) { if (err == GIT_ENOTFOUND) { /* KeyError expects the arg to be the missing key. */ PyErr_SetString(PyExc_KeyError, str); return NULL; } const git_error *error = git_error_last(); if (error == NULL) /* Expected error - no error msg set */ return PyErr_Format(Error_type(err), "%s", str); return PyErr_Format(Error_type(err), "%s: %s", str, error->message); } PyObject * Error_set_oid(int err, const git_oid *oid, size_t len) { char hex[GIT_OID_HEXSZ + 1]; git_oid_fmt(hex, oid); hex[len] = '\0'; return Error_set_str(err, hex); } PyObject * Error_type_error(const char *format, PyObject *value) { PyErr_Format(PyExc_TypeError, format, Py_TYPE(value)->tp_name); return NULL; } int git_error_for_exc(void) { PyObject *err = PyErr_Occurred(); if (err) { // FIXME Here we're masking exception, if the Python implementation has // a genuine Key or Value error. We should have an explicit way for the // Python callbacks to signal ENOTFOUND (and EAMBIGUOUS?) // Not found is an expected condition (the ODB will try with the next // backend), so we clear the exception. if (PyErr_GivenExceptionMatches(err, PyExc_KeyError)) { PyErr_Clear(); return GIT_ENOTFOUND; } if (PyErr_GivenExceptionMatches(err, PyExc_ValueError)) return GIT_EAMBIGUOUS; return GIT_EUSER; } return 0; } libgit2-pygit2-d86662e/src/error.h000066400000000000000000000032121521317216500167220ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_error_h #define INCLUDE_pygit2_error_h #define PY_SSIZE_T_CLEAN #include #include PyObject* Error_type(int type); PyObject* Error_set(int err); PyObject* Error_set_exc(PyObject* exception); PyObject* Error_set_str(int err, const char *str); PyObject* Error_set_oid(int err, const git_oid *oid, size_t len); PyObject* Error_type_error(const char *format, PyObject *value); int git_error_for_exc(void); #endif libgit2-pygit2-d86662e/src/filter.c000066400000000000000000000401551521317216500170600ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include #include #include "diff.h" #include "error.h" #include "object.h" #include "oid.h" #include "patch.h" #include "utils.h" #include "filter.h" extern PyObject *GitError; extern PyTypeObject FilterSourceType; extern PyTypeObject RepositoryType; PyDoc_STRVAR(FilterSource_repo__doc__, "Repository the source data is from\n"); PyObject * FilterSource_repo__get__(FilterSource *self) { git_repository *repo = git_filter_source_repo(self->src); Repository *py_repo; if (repo == NULL) Py_RETURN_NONE; py_repo = PyObject_New(Repository, &RepositoryType); if (py_repo == NULL) return NULL; py_repo->repo = repo; py_repo->config = NULL; py_repo->index = NULL; py_repo->owned = 0; Py_INCREF(py_repo); return (PyObject *)py_repo; } PyDoc_STRVAR(FilterSource_path__doc__, "File path the source data is from.\n"); PyObject * FilterSource_path__get__(FilterSource *self) { return to_unicode_safe(git_filter_source_path(self->src), NULL); } PyDoc_STRVAR(FilterSource_filemode__doc__, "Mode of the source file. If this is unknown, `filemode` will be 0.\n"); PyObject * FilterSource_filemode__get__(FilterSource *self) { return PyLong_FromUnsignedLong(git_filter_source_filemode(self->src)); } PyDoc_STRVAR(FilterSource_oid__doc__, "Oid of the source object. If the oid is unknown " "(often the case with FilterMode.CLEAN) then `oid` will be None.\n"); PyObject * FilterSource_oid__get__(FilterSource *self) { const git_oid *oid = git_filter_source_id(self->src); if (oid == NULL) Py_RETURN_NONE; return git_oid_to_python(oid); } PyDoc_STRVAR(FilterSource_mode__doc__, "Filter mode (either FilterMode.CLEAN or FilterMode.SMUDGE).\n"); PyObject * FilterSource_mode__get__(FilterSource *self) { return PyLong_FromUnsignedLong(git_filter_source_mode(self->src)); } PyDoc_STRVAR(FilterSource_flags__doc__, "A combination of filter flags (enums.FilterFlag) to be applied to the data.\n"); PyObject * FilterSource_flags__get__(FilterSource *self) { return PyLong_FromUnsignedLong(git_filter_source_flags(self->src)); } PyGetSetDef FilterSource_getseters[] = { GETTER(FilterSource, repo), GETTER(FilterSource, path), GETTER(FilterSource, filemode), GETTER(FilterSource, oid), GETTER(FilterSource, mode), GETTER(FilterSource, flags), {NULL} }; PyDoc_STRVAR(FilterSource__doc__, "A filter source represents the file/blob to be processed.\n"); PyTypeObject FilterSourceType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.FilterSource", /* tp_name */ sizeof(FilterSource), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ FilterSource__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ FilterSource_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyDoc_STRVAR(filter__write_next__doc__, "Write to the next writestream in a filter list.\n"); static PyObject * filter__write_next(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *py_next; git_writestream *next; const char *buf; Py_ssize_t size; char *keywords[] = {"next", "data", NULL}; int err; if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oy#", keywords, &py_next, &buf, &size)) return NULL; next = (git_writestream *)PyCapsule_GetPointer(py_next, NULL); if (next == NULL) goto done; Py_BEGIN_ALLOW_THREADS; err = next->write(next, buf, size); Py_END_ALLOW_THREADS; if (err < 0) return Error_set(err); done: Py_RETURN_NONE; } static PyMethodDef filter__write_next_method = { "_write_next", (PyCFunction)filter__write_next, METH_VARARGS | METH_KEYWORDS, filter__write_next__doc__ }; struct pygit2_filter_stream { git_writestream stream; git_writestream *next; PyObject *py_filter; FilterSource *py_src; PyObject *py_write_next; }; struct pygit2_filter_payload { PyObject *py_filter; FilterSource *src; struct pygit2_filter_stream *stream; }; static void pygit2_filter_payload_free( struct pygit2_filter_payload *payload) { if (payload == NULL) return; if (payload->py_filter != NULL) Py_DECREF(payload->py_filter); if (payload->src != NULL) Py_DECREF(payload->src); if (payload->stream != NULL) free(payload->stream); free(payload); } static struct pygit2_filter_payload * pygit2_filter_payload_new( PyObject *py_filter_cls, const git_filter_source *src) { struct pygit2_filter_payload *payload = NULL; payload = malloc(sizeof(struct pygit2_filter_payload)); if (payload == NULL) return NULL; memset(payload, 0, sizeof(struct pygit2_filter_payload)); payload->py_filter = PyObject_CallFunction(py_filter_cls, NULL); if (payload->py_filter == NULL) { PyErr_Clear(); goto error; } payload->src = PyObject_New(FilterSource, &FilterSourceType); if (payload->src == NULL) { PyErr_Clear(); goto error; } payload->src->src = src; goto done; error: pygit2_filter_payload_free(payload); payload = NULL; done: return payload; } static int pygit2_filter_stream_write( git_writestream *s, const char *buffer, size_t len) { struct pygit2_filter_stream *stream = (struct pygit2_filter_stream *)s; PyObject *result = NULL; PyGILState_STATE gil = PyGILState_Ensure(); int err = 0; result = PyObject_CallMethod(stream->py_filter, "write", "y#OO", buffer, len, stream->py_src, stream->py_write_next); if (result == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to write to filter stream"); err = GIT_ERROR; goto done; } Py_DECREF(result); done: PyGILState_Release(gil); return err; } static int pygit2_filter_stream_close(git_writestream *s) { struct pygit2_filter_stream *stream = (struct pygit2_filter_stream *)s; PyObject *result = NULL; PyGILState_STATE gil = PyGILState_Ensure(); int err = 0; int nexterr; result = PyObject_CallMethod(stream->py_filter, "close", "O", stream->py_write_next); if (result == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to close filter stream"); err = GIT_ERROR; goto done; } Py_DECREF(result); done: if (stream->py_write_next != NULL) Py_DECREF(stream->py_write_next); PyGILState_Release(gil); if (stream->next != NULL) { nexterr = stream->next->close(stream->next); if (err == 0) err = nexterr; } return err; } static void pygit2_filter_stream_free(git_writestream *s) { } static int pygit2_filter_stream_init( struct pygit2_filter_stream *stream, git_writestream *next, PyObject *py_filter, FilterSource *py_src) { int err = 0; PyObject *py_next = NULL; PyObject *py_functools = NULL; PyObject *py_write_next = NULL; PyObject *py_partial_write = NULL; PyGILState_STATE gil = PyGILState_Ensure(); memset(stream, 0, sizeof(struct pygit2_filter_stream)); stream->stream.write = pygit2_filter_stream_write; stream->stream.close = pygit2_filter_stream_close; stream->stream.free = pygit2_filter_stream_free; stream->next = next; stream->py_filter = py_filter; stream->py_src = py_src; py_functools = PyImport_ImportModule("functools"); if (py_functools == NULL) { PyErr_Clear(); git_error_set(GIT_ERROR_OS, "failed to import module"); err = GIT_ERROR; goto error; } py_next = PyCapsule_New(stream->next, NULL, NULL); if (py_next == NULL) { PyErr_Clear(); giterr_set_oom(); err = GIT_ERROR; goto error; } py_write_next = PyCFunction_New(&filter__write_next_method, NULL); if (py_write_next == NULL) { PyErr_Clear(); err = GIT_ERROR; goto error; } py_partial_write = PyObject_CallMethod(py_functools, "partial", "OO", py_write_next, py_next); if (py_partial_write == NULL) { PyErr_Clear(); err = GIT_ERROR; goto error; } stream->py_write_next = py_partial_write; goto done; error: if (py_partial_write != NULL) Py_DECREF(py_partial_write); done: if (py_write_next != NULL) Py_DECREF(py_write_next); if (py_functools != NULL) Py_DECREF(py_functools); if (py_next != NULL) Py_DECREF(py_next); PyGILState_Release(gil); return err; } static PyObject * get_passthrough() { PyObject *py_passthrough; PyObject *py_errors = PyImport_ImportModule("pygit2.errors"); if (py_errors == NULL) return NULL; py_passthrough = PyObject_GetAttrString(py_errors, "Passthrough"); Py_DECREF(py_errors); return py_passthrough; } int pygit2_filter_check( git_filter *self, void **payload, const git_filter_source *src, const char **attr_values) { pygit2_filter *filter = (pygit2_filter *)self; struct pygit2_filter_payload *pl = NULL; PyObject *py_attrs = NULL; Py_ssize_t nattrs; Py_ssize_t i; PyObject *result; PyObject *py_passthrough = NULL; PyGILState_STATE gil = PyGILState_Ensure(); int err = 0; py_passthrough = get_passthrough(); if (py_passthrough == NULL) { PyErr_Clear(); err = GIT_ERROR; goto error; } pl = pygit2_filter_payload_new(filter->py_filter_cls, src); if (pl == NULL) { giterr_set_oom(); err = GIT_ERROR; goto done; } result = PyObject_CallMethod(pl->py_filter, "nattrs", NULL); if (result == NULL) { PyErr_Clear(); err = GIT_ERROR; goto error; } nattrs = PyLong_AsSsize_t(result); Py_DECREF(result); py_attrs = PyList_New(nattrs); if (py_attrs == NULL) { PyErr_Clear(); err = GIT_ERROR; goto error; } for (i = 0; i < nattrs; ++i) { if (attr_values[i] == NULL) { if (PyList_SetItem(py_attrs, i, Py_None) < 0) { PyErr_Clear(); err = GIT_ERROR; goto error; } } else if (PyList_SetItem(py_attrs, i, to_unicode_safe(attr_values[i], NULL)) < 0) { PyErr_Clear(); err = GIT_ERROR; goto error; } } result = PyObject_CallMethod(pl->py_filter, "check", "OO", pl->src, py_attrs); if (result == NULL) { if (PyErr_ExceptionMatches(py_passthrough)) { PyErr_Clear(); err = GIT_PASSTHROUGH; } else { PyErr_Clear(); err = GIT_ERROR; goto error; } } else { Py_DECREF(result); *payload = pl; } goto done; error: if (pl != NULL) pygit2_filter_payload_free(pl); done: if (py_attrs != NULL) Py_DECREF(py_attrs); if (py_passthrough != NULL) Py_DECREF(py_passthrough); PyGILState_Release(gil); return err; } int pygit2_filter_stream( git_writestream **out, git_filter *self, void **payload, const git_filter_source *src, git_writestream *next) { pygit2_filter *filter = (pygit2_filter *)self; struct pygit2_filter_stream *stream = NULL; struct pygit2_filter_payload *pl = NULL; PyGILState_STATE gil = PyGILState_Ensure(); int err = 0; if (*payload == NULL) { pl = pygit2_filter_payload_new(filter->py_filter_cls, src); if (pl == NULL) { giterr_set_oom(); err = GIT_ERROR; goto done; } *payload = pl; } else { pl = *payload; } stream = malloc(sizeof(struct pygit2_filter_stream)); if ((err = pygit2_filter_stream_init(stream, next, pl->py_filter, pl->src)) < 0) goto error; *out = &stream->stream; goto done; error: if (stream != NULL) free(stream); done: PyGILState_Release(gil); return err; } void pygit2_filter_cleanup(git_filter *self, void *payload) { struct pygit2_filter_payload *pl = (struct pygit2_filter_payload *)payload; PyGILState_STATE gil = PyGILState_Ensure(); pygit2_filter_payload_free(pl); PyGILState_Release(gil); } void pygit2_filter_shutdown(git_filter *self) { pygit2_filter *filter = (pygit2_filter *)self; PyGILState_STATE gil = PyGILState_Ensure(); free((void*)filter->filter.attributes); Py_DECREF(filter->py_filter_cls); free(filter); PyGILState_Release(gil); } libgit2-pygit2-d86662e/src/filter.h000066400000000000000000000035231521317216500170630ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_filter_h #define INCLUDE_pygit2_filter_h #define PY_SSIZE_T_CLEAN #include #include #include #include "types.h" typedef struct pygit2_filter { git_filter filter; PyObject *py_filter_cls; } pygit2_filter; int pygit2_filter_check( git_filter *self, void **payload, const git_filter_source *src, const char **attr_values); int pygit2_filter_stream( git_writestream **out, git_filter *self, void **payload, const git_filter_source *src, git_writestream *next); void pygit2_filter_cleanup(git_filter *self, void *payload); void pygit2_filter_shutdown(git_filter *self); #endif libgit2-pygit2-d86662e/src/mailmap.c000066400000000000000000000216641521317216500172170ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "error.h" #include "utils.h" #include "types.h" #include "mailmap.h" #include "signature.h" extern PyTypeObject SignatureType; extern PyTypeObject RepositoryType; int Mailmap_init(Mailmap *self, PyObject *args, PyObject *kwargs) { char *keywords[] = {NULL}; git_mailmap *mm; int error; /* Our init method does not handle parameters */ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", keywords)) return -1; error = git_mailmap_new(&mm); if (error < 0) { Error_set(error); return -1; } self->mailmap = mm; return 0; } PyDoc_STRVAR(Mailmap_from_repository__doc__, "from_repository(repository: Repository) -> Mailmap\n" "\n" "Create a new mailmap instance from a repository, loading mailmap files based on the repository's configuration.\n" "\n" "Mailmaps are loaded in the following order:\n" " 1. '.mailmap' in the root of the repository's working directory, if present.\n" " 2. The blob object identified by the 'mailmap.blob' config entry, if set.\n" " [NOTE: 'mailmap.blob' defaults to 'HEAD:.mailmap' in bare repositories]\n" " 3. The path in the 'mailmap.file' config entry, if set."); PyObject * Mailmap_from_repository(Mailmap *dummy, PyObject *args) { Repository *repo = NULL; git_mailmap *mm = NULL; int error; if (!PyArg_ParseTuple(args, "O!", &RepositoryType, &repo)) return NULL; error = git_mailmap_from_repository(&mm, repo->repo); if (error < 0) return Error_set(error); return wrap_mailmap(mm); } PyDoc_STRVAR(Mailmap_from_buffer__doc__, "from_buffer(buffer: str) -> Mailmap\n" "\n" "Parse a passed-in buffer and construct a mailmap object."); PyObject * Mailmap_from_buffer(Mailmap *dummy, PyObject *args) { char *buffer = NULL; Py_ssize_t size = 0; git_mailmap *mm = NULL; int error; if (!PyArg_ParseTuple(args, "s#", &buffer, &size)) return NULL; error = git_mailmap_from_buffer(&mm, buffer, size); if (error < 0) return Error_set(error); return wrap_mailmap(mm); } PyDoc_STRVAR(Mailmap_add_entry__doc__, "add_entry(real_name: str = None, real_email: str = None, replace_name: str = None, replace_email: str)\n" "\n" "Add a new entry to the mailmap, overriding existing entries."); PyObject * Mailmap_add_entry(Mailmap *self, PyObject *args, PyObject *kwargs) { char *keywords[] = {"real_name", "real_email", "replace_name", "replace_email", NULL}; char *real_name = NULL, *real_email = NULL; char *replace_name = NULL, *replace_email = NULL; int error; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zzzs", keywords, &real_name, &real_email, &replace_name, &replace_email)) return NULL; /* replace_email cannot be null */ if (!replace_email) { PyErr_BadArgument(); return NULL; } error = git_mailmap_add_entry(self->mailmap, real_name, real_email, replace_name, replace_email); if (error < 0) return Error_set(error); Py_RETURN_NONE; } PyDoc_STRVAR(Mailmap_resolve__doc__, "resolve(name: str, email: str) -> tuple[str, str]\n" "\n" "Resolve name & email to a real name and email."); PyObject * Mailmap_resolve(Mailmap *self, PyObject *args) { const char *name = NULL, *email = NULL; const char *real_name = NULL, *real_email = NULL; int error; if (!PyArg_ParseTuple(args, "ss", &name, &email)) return NULL; error = git_mailmap_resolve(&real_name, &real_email, self->mailmap, name, email); if (error < 0) return Error_set(error); return Py_BuildValue("ss", real_name, real_email); } PyDoc_STRVAR(Mailmap_resolve_signature__doc__, "resolve_signature(sig: Signature) -> Signature\n" "\n" "Resolve signature to real name and email."); PyObject * Mailmap_resolve_signature(Mailmap *self, PyObject *args) { Signature *sig = NULL; git_signature *resolved = NULL; int error; if (!PyArg_ParseTuple(args, "O!", &SignatureType, &sig)) return NULL; error = git_mailmap_resolve_signature(&resolved, self->mailmap, sig->signature); if (error < 0) return Error_set(error); return build_signature(sig->obj, resolved, sig->encoding); } static void Mailmap_dealloc(Mailmap *self) { git_mailmap_free(self->mailmap); PyObject_Del(self); } static PyMethodDef Mailmap_methods[] = { METHOD(Mailmap, add_entry, METH_VARARGS | METH_KEYWORDS), METHOD(Mailmap, resolve, METH_VARARGS), METHOD(Mailmap, resolve_signature, METH_VARARGS), METHOD(Mailmap, from_repository, METH_VARARGS | METH_STATIC), METHOD(Mailmap, from_buffer, METH_VARARGS | METH_STATIC), {NULL} }; PyDoc_STRVAR(Mailmap__doc__, "Mailmap object."); PyTypeObject MailmapType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Mailmap", /* tp_name */ sizeof(Mailmap), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Mailmap_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Mailmap__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Mailmap_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Mailmap_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_mailmap(git_mailmap* mm) { Mailmap* py_mm = NULL; py_mm = PyObject_New(Mailmap, &MailmapType); if (py_mm == NULL) { PyErr_NoMemory(); return NULL; } py_mm->mailmap = mm; return (PyObject*) py_mm; } libgit2-pygit2-d86662e/src/mailmap.h000066400000000000000000000026211521317216500172140ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_mailmap_h #define INCLUDE_pygit2_mailmap_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject* wrap_mailmap(git_mailmap *c_object); #endif libgit2-pygit2-d86662e/src/note.c000066400000000000000000000221321521317216500165330ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "error.h" #include "utils.h" #include "types.h" #include "oid.h" #include "note.h" extern PyTypeObject SignatureType; PyDoc_STRVAR(Note_remove__doc__, "remove(author: Signature, committer: Signature, ref: str = \"refs/notes/commits\")\n" "\n" "Removes a note for an annotated object"); PyObject * Note_remove(Note *self, PyObject* args) { char *ref = "refs/notes/commits"; int err = GIT_ERROR; Signature *py_author, *py_committer; Oid *id; if (!PyArg_ParseTuple(args, "O!O!|s", &SignatureType, &py_author, &SignatureType, &py_committer, &ref)) return NULL; id = (Oid *) self->annotated_id; err = git_note_remove(self->repo->repo, ref, py_author->signature, py_committer->signature, &id->oid); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Note_message__doc__, "Gets message of the note\n"); PyObject * Note_message__get__(Note *self) { int err; // Lazy load if (self->note == NULL) { err = git_note_read(&self->note, self->repo->repo, self->ref, &((Oid *)self->annotated_id)->oid); if (err < 0) return Error_set(err); } return to_unicode(git_note_message(self->note), NULL, NULL); } static void Note_dealloc(Note *self) { Py_CLEAR(self->repo); Py_CLEAR(self->annotated_id); Py_CLEAR(self->id); if (self->note != NULL) git_note_free(self->note); PyObject_Del(self); } static PyMethodDef Note_methods[] = { METHOD(Note, remove, METH_VARARGS), {NULL} }; PyMemberDef Note_members[] = { MEMBER(Note, id, T_OBJECT, "id of the note object."), MEMBER(Note, annotated_id, T_OBJECT, "id of the annotated object."), {NULL} }; PyGetSetDef Note_getseters[] = { GETTER(Note, message), {NULL} }; PyDoc_STRVAR(Note__doc__, "Note object."); PyTypeObject NoteType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Note", /* tp_name */ sizeof(Note), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Note_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Note__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Note_methods, /* tp_methods */ Note_members, /* tp_members */ Note_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * NoteIter_iternext(NoteIter *self) { int err; git_oid note_id, annotated_id; err = git_note_next(¬e_id, &annotated_id, self->iter); if (err < 0) return Error_set(err); return (PyObject*) wrap_note(self->repo, ¬e_id, &annotated_id, self->ref); } void NoteIter_dealloc(NoteIter *self) { Py_CLEAR(self->repo); git_note_iterator_free(self->iter); PyObject_Del(self); } PyDoc_STRVAR(NoteIter__doc__, "Note iterator object."); PyTypeObject NoteIterType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.NoteIter", /* tp_name */ sizeof(NoteIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)NoteIter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ NoteIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc) NoteIter_iternext, /* tp_iternext */ }; PyObject * wrap_note(Repository* repo, git_oid* note_id, git_oid* annotated_id, const char* ref) { Note* py_note = NULL; int err = GIT_ERROR; py_note = PyObject_New(Note, &NoteType); if (py_note == NULL) { PyErr_NoMemory(); return NULL; } Py_INCREF(repo); py_note->repo = repo; py_note->ref = ref; py_note->annotated_id = git_oid_to_python(annotated_id); py_note->id = NULL; py_note->note = NULL; /* If the note has been provided, defer the git_note_read() call */ if (note_id != NULL) { py_note->id = git_oid_to_python(note_id); } else { err = git_note_read(&py_note->note, repo->repo, ref, annotated_id); if (err < 0) { Py_DECREF(py_note); return Error_set(err); } py_note->id = git_oid_to_python(git_note_id(py_note->note)); } return (PyObject*) py_note; } libgit2-pygit2-d86662e/src/note.h000066400000000000000000000026761521317216500165530ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_note_h #define INCLUDE_pygit2_note_h #define PY_SSIZE_T_CLEAN #include #include PyObject* wrap_note(Repository* repo, git_oid* note_id, git_oid* annotated_id, const char* ref); #endif libgit2-pygit2-d86662e/src/object.c000066400000000000000000000265051521317216500170440ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "error.h" #include "types.h" #include "utils.h" #include "odb.h" #include "oid.h" #include "repository.h" #include "object.h" extern PyTypeObject TreeType; extern PyTypeObject CommitType; extern PyTypeObject BlobType; extern PyTypeObject TagType; extern PyObject *FileModeEnum; PyTypeObject ObjectType; void Object_dealloc(Object* self) { Py_CLEAR(self->repo); git_object_free(self->obj); git_tree_entry_free((git_tree_entry*)self->entry); Py_TYPE(self)->tp_free(self); } git_object* Object__load(Object *self) { if (self->obj == NULL) { int err = git_tree_entry_to_object(&self->obj, self->repo->repo, self->entry); if (err < 0) { Error_set(err); return NULL; } } return self->obj; } const git_oid* Object__id(Object *self) { return (self->obj) ? git_object_id(self->obj) : git_tree_entry_id(self->entry); } git_object_t Object__type(Object *self) { return (self->obj) ? git_object_type(self->obj) : git_tree_entry_type(self->entry); } PyDoc_STRVAR(Object_id__doc__, "The object id, an instance of the Oid type."); PyObject * Object_id__get__(Object *self) { return git_oid_to_python(Object__id(self)); } PyDoc_STRVAR(Object_short_id__doc__, "An unambiguous short (abbreviated) hex Oid string for the object."); PyObject * Object_short_id__get__(Object *self) { if (Object__load(self) == NULL) { return NULL; } // Lazy load git_buf short_id = { NULL, 0, 0 }; int err = git_object_short_id(&short_id, self->obj); if (err != GIT_OK) return Error_set(err); PyObject *py_short_id = to_unicode_n(short_id.ptr, short_id.size, NULL, "strict"); git_buf_dispose(&short_id); return py_short_id; } PyDoc_STRVAR(Object_type__doc__, "One of the enums.ObjectType.COMMIT, TREE, BLOB or TAG constants."); PyObject * Object_type__get__(Object *self) { return PyLong_FromLong(Object__type(self)); } PyDoc_STRVAR(Object_type_str__doc__, "One of the 'commit', 'tree', 'blob' or 'tag' strings."); PyObject * Object_type_str__get__(Object *self) { return PyUnicode_DecodeFSDefault(git_object_type2string(Object__type(self))); } PyDoc_STRVAR(Object__pointer__doc__, "Get the object's pointer. For internal use only."); PyObject * Object__pointer__get__(Object *self) { /* Bytes means a raw buffer */ if (Object__load(self) == NULL) { return NULL; } // Lazy load return PyBytes_FromStringAndSize((char *) &self->obj, sizeof(git_object *)); } PyDoc_STRVAR(Object_name__doc__, "Name (or None if the object was not reached through a tree)"); PyObject * Object_name__get__(Object *self) { if (self->entry == NULL) Py_RETURN_NONE; return PyUnicode_DecodeFSDefault(git_tree_entry_name(self->entry)); } PyDoc_STRVAR(Object_raw_name__doc__, "Name (bytes)."); PyObject * Object_raw_name__get__(Object *self) { if (self->entry == NULL) Py_RETURN_NONE; return PyBytes_FromString(git_tree_entry_name(self->entry)); } PyDoc_STRVAR(Object_filemode__doc__, "An enums.FileMode constant (or None if the object was not reached through a tree)"); PyObject * Object_filemode__get__(Object *self) { if (self->entry == NULL) Py_RETURN_NONE; return pygit2_enum(FileModeEnum, git_tree_entry_filemode(self->entry)); } PyDoc_STRVAR(Object_read_raw__doc__, "read_raw() -> bytes\n" "\n" "Returns the byte string with the raw contents of the object."); PyObject * Object_read_raw(Object *self) { int err; git_odb *odb; PyObject *aux; err = git_repository_odb(&odb, self->repo->repo); if (err < 0) return Error_set(err); const git_oid *oid = Object__id(self); git_odb_object *obj = Odb_read_raw(odb, oid, GIT_OID_HEXSZ); git_odb_free(odb); if (obj == NULL) return NULL; aux = PyBytes_FromStringAndSize( git_odb_object_data(obj), git_odb_object_size(obj)); git_odb_object_free(obj); return aux; } PyDoc_STRVAR(Object_peel__doc__, "peel(target_type) -> Object\n" "\n" "Peel the current object and returns the first object of the given type.\n" "\n" "If you pass None as the target type, then the object will be peeled\n" "until the type changes. A tag will be peeled until the referenced object\n" "is no longer a tag, and a commit will be peeled to a tree. Any other\n" "object type will raise InvalidSpecError.\n"); PyObject * Object_peel(Object *self, PyObject *py_type) { int err; git_otype otype; git_object *peeled; if (Object__load(self) == NULL) { return NULL; } // Lazy load otype = py_object_to_otype(py_type); if (otype == GIT_OBJECT_INVALID) return NULL; err = git_object_peel(&peeled, self->obj, otype); if (err < 0) return Error_set(err); return wrap_object(peeled, self->repo, NULL); } Py_hash_t Object_hash(Object *self) { const git_oid *oid = Object__id(self); PyObject *py_oid = git_oid_to_py_str(oid); Py_hash_t ret = PyObject_Hash(py_oid); Py_DECREF(py_oid); return ret; } PyObject * Object_repr(Object *self) { char hex[GIT_OID_HEXSZ + 1]; git_oid_fmt(hex, Object__id(self)); hex[GIT_OID_HEXSZ] = '\0'; return PyUnicode_FromFormat("", git_object_type2string(Object__type(self)), hex ); } PyObject * Object_richcompare(PyObject *o1, PyObject *o2, int op) { PyObject *res; if (!PyObject_TypeCheck(o2, &ObjectType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } int equal = git_oid_equal(Object__id((Object *)o1), Object__id((Object *)o2)); switch (op) { case Py_NE: res = (equal) ? Py_False : Py_True; break; case Py_EQ: res = (equal) ? Py_True : Py_False; break; case Py_LT: case Py_LE: case Py_GT: case Py_GE: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; default: PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op); return NULL; } Py_INCREF(res); return res; } PyGetSetDef Object_getseters[] = { GETTER(Object, id), GETTER(Object, short_id), GETTER(Object, type), GETTER(Object, type_str), GETTER(Object, _pointer), // These come from git_tree_entry GETTER(Object, name), GETTER(Object, raw_name), GETTER(Object, filemode), {NULL} }; static PyMethodDef Object_methods[] = { METHOD(Object, read_raw, METH_NOARGS), METHOD(Object, peel, METH_O), {NULL} }; PyDoc_STRVAR(Object__doc__, "Base class for Git objects."); PyTypeObject ObjectType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Object", /* tp_name */ sizeof(Object), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Object_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Object_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)Object_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ Object__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)Object_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Object_methods, /* tp_methods */ 0, /* tp_members */ Object_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_object(git_object *c_object, Repository *repo, const git_tree_entry *entry) { Object *py_obj = NULL; git_object_t obj_type = (c_object) ? git_object_type(c_object) : git_tree_entry_type(entry); switch (obj_type) { case GIT_OBJECT_COMMIT: py_obj = PyObject_New(Object, &CommitType); break; case GIT_OBJECT_TREE: py_obj = PyObject_New(Object, &TreeType); break; case GIT_OBJECT_BLOB: py_obj = PyObject_New(Object, &BlobType); break; case GIT_OBJECT_TAG: py_obj = PyObject_New(Object, &TagType); break; default: assert(0); } if (py_obj) { py_obj->obj = c_object; if (repo) { py_obj->repo = repo; Py_INCREF(repo); } py_obj->entry = entry; } return (PyObject *)py_obj; } libgit2-pygit2-d86662e/src/object.h000066400000000000000000000031341521317216500170420ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_object_h #define INCLUDE_pygit2_object_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" git_object* Object__load(Object *self); const git_oid* Object__id(Object *self); PyObject* Object_read_raw(Object *self); PyObject* Object_repr(Object *self); PyObject* wrap_object(git_object *c_object, Repository *repo, const git_tree_entry *entry); #endif libgit2-pygit2-d86662e/src/odb.c000066400000000000000000000320771521317216500163430ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "object.h" #include "odb_backend.h" #include "oid.h" #include "types.h" #include "utils.h" #include extern PyTypeObject OdbBackendType; extern PyObject *ObjectTypeEnum; static git_otype int_to_loose_object_type(int type_id) { switch((git_otype)type_id) { case GIT_OBJECT_COMMIT: case GIT_OBJECT_TREE: case GIT_OBJECT_BLOB: case GIT_OBJECT_TAG: return (git_otype)type_id; default: return GIT_OBJECT_INVALID; } } int Odb_init(Odb *self, PyObject *args, PyObject *kwds) { if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "Odb takes no keyword arguments"); return -1; } PyObject *py_path = NULL; if (!PyArg_ParseTuple(args, "|O", &py_path)) return -1; int err; if (py_path) { PyObject *tvalue; char *path = pgit_borrow_fsdefault(py_path, &tvalue); if (path == NULL) return -1; err = git_odb_open(&self->odb, path); Py_DECREF(tvalue); } else { err = git_odb_new(&self->odb); } if (err) { Error_set(err); return -1; } return 0; } void Odb_dealloc(Odb *self) { git_odb_free(self->odb); Py_TYPE(self)->tp_free((PyObject *) self); } static int Odb_build_as_iter(const git_oid *oid, void *accum) { int err; PyObject *py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; err = PyList_Append((PyObject*)accum, py_oid); Py_DECREF(py_oid); if (err < 0) return GIT_EUSER; return 0; } PyObject * Odb_as_iter(Odb *self) { int err; PyObject *accum = PyList_New(0); PyObject *ret = NULL; err = git_odb_foreach(self->odb, Odb_build_as_iter, (void*)accum); if (err == GIT_EUSER) goto exit; if (err < 0) { ret = Error_set(err); goto exit; } ret = PyObject_GetIter(accum); exit: Py_DECREF(accum); return ret; } PyDoc_STRVAR(Odb_add_disk_alternate__doc__, "add_disk_alternate(path: str)\n" "\n" "Adds a path on disk as an alternate backend for objects.\n" "Alternate backends are checked for objects only *after* the main backends\n" "are checked. Writing is disabled on alternate backends.\n"); PyObject * Odb_add_disk_alternate(Odb *self, PyObject *py_path) { PyObject *tvalue; char *path = pgit_borrow_fsdefault(py_path, &tvalue); if (path == NULL) return NULL; int err = git_odb_add_disk_alternate(self->odb, path); Py_DECREF(tvalue); if (err) return Error_set(err); Py_RETURN_NONE; } git_odb_object * Odb_read_raw(git_odb *odb, const git_oid *oid, size_t len) { git_odb_object *obj; int err; err = git_odb_read_prefix(&obj, odb, oid, (unsigned int)len); if (err < 0 && err != GIT_EUSER) { Error_set_oid(err, oid, len); return NULL; } return obj; } PyDoc_STRVAR(Odb_read__doc__, "read(oid: Oid) -> tuple[enums.ObjectType, bytes]\n" "\n" "Read raw object data from the object db."); PyObject * Odb_read(Odb *self, PyObject *py_hex) { git_oid oid; git_odb_object *obj; size_t len; git_object_t type; PyObject* type_enum; PyObject* tuple; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; obj = Odb_read_raw(self->odb, &oid, len); if (obj == NULL) return NULL; // Convert type to ObjectType enum type = git_odb_object_type(obj); type_enum = pygit2_enum(ObjectTypeEnum, type); tuple = Py_BuildValue( "(Oy#)", type_enum, git_odb_object_data(obj), git_odb_object_size(obj)); git_odb_object_free(obj); return tuple; } PyDoc_STRVAR(Odb_read_header__doc__, "read_header(oid: Oid) -> tuple[enums.ObjectType, int]\n" "\n" "Read the header of an object from the database, without reading its full\n" "contents.\n" "\n" "The header includes the type and the length of an object.\n" "\n" "Note that most backends do not support reading only the header of an object,\n" "so the whole object may be read and then the header will be returned."); PyObject * Odb_read_header(Odb *self, PyObject *py_hex) { git_oid oid; int err; size_t len; git_object_t type; PyObject* type_enum; PyObject* tuple; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; err = git_odb_read_header(&len, &type, self->odb, &oid); if (err != 0) { Error_set_oid(err, &oid, len); return NULL; } // Convert type to ObjectType enum type_enum = pygit2_enum(ObjectTypeEnum, type); tuple = Py_BuildValue("(On)", type_enum, len); return tuple; } PyDoc_STRVAR(Odb_write__doc__, "write(type: int, data: bytes) -> Oid\n" "\n" "Write raw object data into the object db. First arg is the object\n" "type, the second one a buffer with data. Return the Oid of the created\n" "object."); PyObject * Odb_write(Odb *self, PyObject *args) { int err; git_oid oid; git_odb_stream* stream; int type_id; const char* buffer; Py_ssize_t buflen; git_otype type; if (!PyArg_ParseTuple(args, "Is#", &type_id, &buffer, &buflen)) return NULL; type = int_to_loose_object_type(type_id); if (type == GIT_OBJECT_INVALID) return PyErr_Format(PyExc_ValueError, "%d", type_id); err = git_odb_open_wstream(&stream, self->odb, buflen, type); if (err < 0) return Error_set(err); err = git_odb_stream_write(stream, buffer, buflen); if (err) { git_odb_stream_free(stream); return Error_set(err); } err = git_odb_stream_finalize_write(&oid, stream); git_odb_stream_free(stream); if (err) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(Odb_exists__doc__, "exists(oid: Oid) -> bool\n" "\n" "Returns true if the given oid can be found in this odb."); PyObject * Odb_exists(Odb *self, PyObject *py_hex) { git_oid oid; size_t len; int result; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; result = git_odb_exists(self->odb, &oid); if (result < 0) return Error_set(result); else if (result == 0) Py_RETURN_FALSE; else Py_RETURN_TRUE; } PyDoc_STRVAR(Odb_add_backend__doc__, "add_backend(backend: OdbBackend, priority: int)\n" "\n" "Adds an OdbBackend to the list of backends for this object database.\n"); PyObject * Odb_add_backend(Odb *self, PyObject *args) { int err, priority; OdbBackend *backend; if (!PyArg_ParseTuple(args, "OI", &backend, &priority)) return NULL; if (!PyObject_IsInstance((PyObject *)backend, (PyObject *)&OdbBackendType)) { PyErr_SetString(PyExc_TypeError, "add_backend expects an instance of pygit2.OdbBackend"); return NULL; } err = git_odb_add_backend(self->odb, backend->odb_backend, priority); if (err != 0) return Error_set(err); Py_INCREF(backend); Py_RETURN_NONE; } static PyMethodDef Odb_methods[] = { METHOD(Odb, add_disk_alternate, METH_O), METHOD(Odb, read, METH_O), METHOD(Odb, read_header, METH_O), METHOD(Odb, write, METH_VARARGS), METHOD(Odb, exists, METH_O), METHOD(Odb, add_backend, METH_VARARGS), {NULL} }; PyDoc_STRVAR(Odb_backends__doc__, "Return an iterable of backends for this object database."); PyObject * Odb_backends__get__(Odb *self) { int err; git_odb_backend *backend; PyObject *ret = NULL; PyObject *py_backend; PyObject *accum = PyList_New(0); if (accum == NULL) return NULL; size_t nbackends = git_odb_num_backends(self->odb); for (size_t i = 0; i < nbackends; ++i) { err = git_odb_get_backend(&backend, self->odb, i); if (err != 0) { ret = Error_set(err); goto exit; } // XXX This won't return the correct class for custom backends (add a // test and fix) py_backend = wrap_odb_backend(backend); if (py_backend == NULL) goto exit; err = PyList_Append(accum, py_backend); if (err != 0) goto exit; } ret = PyObject_GetIter(accum); exit: Py_DECREF(accum); return ret; } PyGetSetDef Odb_getseters[] = { GETTER(Odb, backends), {NULL} }; int Odb_contains(Odb *self, PyObject *py_name) { git_oid oid; size_t len; len = py_oid_to_git_oid(py_name, &oid); if (len == 0) { PyErr_SetString(PyExc_TypeError, "name must be an oid"); return -1; } return git_odb_exists(self->odb, &oid); } PySequenceMethods Odb_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)Odb_contains, /* sq_contains */ }; PyDoc_STRVAR(Odb__doc__, "Object database."); PyTypeObject OdbType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Odb", /* tp_name */ sizeof(Odb), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Odb_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ &Odb_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Odb__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)Odb_as_iter, /* tp_iter */ 0, /* tp_iternext */ Odb_methods, /* tp_methods */ 0, /* tp_members */ Odb_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Odb_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_odb(git_odb *c_odb) { Odb *py_odb = PyObject_New(Odb, &OdbType); if (py_odb) py_odb->odb = c_odb; return (PyObject *)py_odb; } libgit2-pygit2-d86662e/src/odb.h000066400000000000000000000027751521317216500163520ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_odb_h #define INCLUDE_pygit2_odb_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject *wrap_odb(git_odb *c_odb); git_odb_object *Odb_read_raw(git_odb *odb, const git_oid *oid, size_t len); PyObject *Odb_read(Odb *self, PyObject *py_hex); #endif libgit2-pygit2-d86662e/src/odb_backend.c000066400000000000000000000622441521317216500200110ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * * TODO This still needs much work to make it usable, and maintanable! * - Create OdbBackendCustomType that inherits from OdbBackendType. * OdbBackendType should not be subclassed, instead subclass * OdbBackendCustomType to develop custom backends in Python. * Implement this new type in src/odb_backend_custom.c */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "object.h" #include "oid.h" #include "types.h" #include "utils.h" #include #include #include /* * pgit_odb_backend_t is a container for the state associated with a custom * implementation of git_odb_backend. The git_odb_backend field's function * pointers are assigned to the pgit_odb_backend_* functions, which handle * translating between the libgit2 ABI and the Python ABI. * It holds a pointer to the subclass, which must implement * the callbacks in Python. */ typedef struct { git_odb_backend backend; PyObject *py_backend; } pgit_odb_backend; static int pgit_odb_backend_read(void **ptr, size_t *sz, git_object_t *type, git_odb_backend *_be, const git_oid *oid) { pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; PyObject *result = PyObject_CallMethod(be->py_backend, "read_cb", "N", py_oid); if (result == NULL) return git_error_for_exc(); const char *bytes; Py_ssize_t type_value; Py_ssize_t py_sz; if (!PyArg_ParseTuple(result, "ny#", &type_value, &bytes, &py_sz) || !bytes) { Py_DECREF(result); return GIT_EUSER; } *type = (git_object_t)type_value; *sz = (size_t)py_sz; *ptr = git_odb_backend_data_alloc(_be, *sz); if (!*ptr) { Py_DECREF(result); return GIT_EUSER; } memcpy(*ptr, bytes, *sz); Py_DECREF(result); return 0; } static int pgit_odb_backend_read_prefix(git_oid *oid_out, void **ptr, size_t *sz, git_object_t *type, git_odb_backend *_be, const git_oid *short_id, size_t len) { // short_id to hex char short_id_hex[GIT_OID_HEXSZ]; git_oid_nfmt(short_id_hex, len, short_id); // Call callback pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *result = PyObject_CallMethod(be->py_backend, "read_prefix_cb", "s#", short_id_hex, len); if (result == NULL) return git_error_for_exc(); // Parse output from callback PyObject *py_oid_out; Py_ssize_t type_value; Py_ssize_t py_sz; const char *bytes; if (!PyArg_ParseTuple(result, "ny#O", &type_value, &bytes, &py_sz, &py_oid_out) || !bytes) { Py_DECREF(result); return GIT_EUSER; } *type = (git_object_t)type_value; *sz = (size_t)py_sz; *ptr = git_odb_backend_data_alloc(_be, *sz); if (!*ptr) { Py_DECREF(result); return GIT_EUSER; } memcpy(*ptr, bytes, *sz); py_oid_to_git_oid(py_oid_out, oid_out); Py_DECREF(result); return 0; } static int pgit_odb_backend_read_header(size_t *len, git_object_t *type, git_odb_backend *_be, const git_oid *oid) { pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; PyObject *result = PyObject_CallMethod(be->py_backend, "read_header_cb", "N", py_oid); if (result == NULL) return git_error_for_exc(); Py_ssize_t type_value; if (!PyArg_ParseTuple(result, "nn", &type_value, len)) { Py_DECREF(result); return GIT_EUSER; } *type = (git_object_t)type_value; Py_DECREF(result); return 0; } static int pgit_odb_backend_write(git_odb_backend *_be, const git_oid *oid, const void *data, size_t sz, git_object_t typ) { pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; PyObject *result = PyObject_CallMethod(be->py_backend, "write_cb", "Ny#n", py_oid, data, sz, typ); if (result == NULL) return git_error_for_exc(); Py_DECREF(result); return 0; } static int pgit_odb_backend_exists(git_odb_backend *_be, const git_oid *oid) { pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; PyObject *result = PyObject_CallMethod(be->py_backend, "exists_cb", "N", py_oid); if (result == NULL) return git_error_for_exc(); int r = PyObject_IsTrue(result); Py_DECREF(result); return r; } static int pgit_odb_backend_exists_prefix(git_oid *out, git_odb_backend *_be, const git_oid *short_id, size_t len) { // short_id to hex char short_id_hex[GIT_OID_HEXSZ]; git_oid_nfmt(short_id_hex, len, short_id); // Call callback pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *py_oid = PyObject_CallMethod(be->py_backend, "exists_prefix_cb", "s#", short_id_hex, len); if (py_oid == NULL) return git_error_for_exc(); py_oid_to_git_oid(py_oid, out); Py_DECREF(py_oid); return 0; } static int pgit_odb_backend_refresh(git_odb_backend *_be) { pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject_CallMethod(be->py_backend, "refresh_cb", NULL); return git_error_for_exc(); } static int pgit_odb_backend_foreach(git_odb_backend *_be, git_odb_foreach_cb cb, void *payload) { PyObject *item; git_oid oid; pgit_odb_backend *be = (pgit_odb_backend *)_be; PyObject *iterator = PyObject_GetIter((PyObject *)be->py_backend); assert(iterator); while ((item = PyIter_Next(iterator))) { py_oid_to_git_oid(item, &oid); cb(&oid, payload); Py_DECREF(item); } return git_error_for_exc(); } static void pgit_odb_backend_free(git_odb_backend *backend) { pgit_odb_backend *custom_backend = (pgit_odb_backend *)backend; Py_DECREF(custom_backend->py_backend); } int OdbBackend_init(OdbBackend *self, PyObject *args, PyObject *kwds) { // Check input arguments if (args && PyTuple_Size(args) > 0) { PyErr_SetString(PyExc_TypeError, "OdbBackend takes no arguments"); return -1; } if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "OdbBackend takes no keyword arguments"); return -1; } // Create the C backend pgit_odb_backend *custom_backend = calloc(1, sizeof(pgit_odb_backend)); if (custom_backend == NULL) { PyErr_NoMemory(); return -1; } custom_backend->backend.version = GIT_ODB_BACKEND_VERSION; // Fill the member methods custom_backend->backend.free = pgit_odb_backend_free; custom_backend->backend.read = pgit_odb_backend_read; custom_backend->backend.read_prefix = pgit_odb_backend_read_prefix; custom_backend->backend.read_header = pgit_odb_backend_read_header; custom_backend->backend.write = pgit_odb_backend_write; custom_backend->backend.exists = pgit_odb_backend_exists; custom_backend->backend.exists_prefix = pgit_odb_backend_exists_prefix; custom_backend->backend.refresh = pgit_odb_backend_refresh; // custom_backend->backend.writepack = pgit_odb_backend_writepack; // custom_backend->backend.freshen = pgit_odb_backend_freshen; // custom_backend->backend.writestream = pgit_odb_backend_writestream; // custom_backend->backend.readstream = pgit_odb_backend_readstream; if (PyIter_Check((PyObject *)self)) custom_backend->backend.foreach = pgit_odb_backend_foreach; // Cross reference (don't incref because it's something internal) custom_backend->py_backend = (PyObject *)self; self->odb_backend = (git_odb_backend *)custom_backend; return 0; } void OdbBackend_dealloc(OdbBackend *self) { if (self->odb_backend && self->odb_backend->read == pgit_odb_backend_read) { pgit_odb_backend *custom_backend = (pgit_odb_backend *)self->odb_backend; free(custom_backend); } Py_TYPE(self)->tp_free((PyObject *) self); } static int OdbBackend_build_as_iter(const git_oid *oid, void *accum) { int err; PyObject *py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; err = PyList_Append((PyObject*)accum, py_oid); Py_DECREF(py_oid); if (err < 0) return GIT_EUSER; return 0; } PyObject * OdbBackend_as_iter(OdbBackend *self) { PyObject *accum = PyList_New(0); PyObject *iter = NULL; int err = self->odb_backend->foreach(self->odb_backend, OdbBackend_build_as_iter, (void*)accum); if (err == GIT_EUSER) goto exit; if (err < 0) { Error_set(err); goto exit; } iter = PyObject_GetIter(accum); exit: Py_DECREF(accum); return iter; } PyDoc_STRVAR(OdbBackend_read__doc__, "read(oid) -> (type, data)\n" "\n" "Read raw object data from this odb backend.\n"); PyObject * OdbBackend_read(OdbBackend *self, PyObject *py_hex) { int err; git_oid oid; git_object_t type; size_t len, size; void *data; if (self->odb_backend->read == NULL) Py_RETURN_NOTIMPLEMENTED; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; err = self->odb_backend->read(&data, &size, &type, self->odb_backend, &oid); if (err != 0) { Error_set_oid(err, &oid, len); return NULL; } PyObject *tuple = Py_BuildValue("(ny#)", type, data, size); git_odb_backend_data_free(self->odb_backend, data); return tuple; } PyDoc_STRVAR(OdbBackend_read_prefix__doc__, "read_prefix(oid: Oid) -> tuple[int, bytes, Oid]\n" "\n" "Read raw object data from this odb backend based on an oid prefix.\n" "The returned tuple contains (type, data, oid)."); PyObject * OdbBackend_read_prefix(OdbBackend *self, PyObject *py_hex) { int err; git_oid oid, oid_out; git_object_t type; size_t len, size; void *data; if (self->odb_backend->read_prefix == NULL) Py_RETURN_NOTIMPLEMENTED; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; err = self->odb_backend->read_prefix(&oid_out, &data, &size, &type, self->odb_backend, &oid, len); if (err != 0) { Error_set_oid(err, &oid, len); return NULL; } PyObject *py_oid_out = git_oid_to_python(&oid_out); if (py_oid_out == NULL) return Error_set_exc(PyExc_MemoryError); PyObject *tuple = Py_BuildValue("(ny#N)", type, data, size, py_oid_out); git_odb_backend_data_free(self->odb_backend, data); return tuple; } PyDoc_STRVAR(OdbBackend_read_header__doc__, "read_header(oid) -> (type, len)\n" "\n" "Read raw object header from this odb backend."); PyObject * OdbBackend_read_header(OdbBackend *self, PyObject *py_hex) { int err; size_t len; git_object_t type; git_oid oid; if (self->odb_backend->read_header == NULL) Py_RETURN_NOTIMPLEMENTED; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; err = self->odb_backend->read_header(&len, &type, self->odb_backend, &oid); if (err != 0) { Error_set_oid(err, &oid, len); return NULL; } return Py_BuildValue("(ni)", type, len); } PyDoc_STRVAR(OdbBackend_exists__doc__, "exists(oid: Oid) -> bool\n" "\n" "Returns true if the given oid can be found in this odb."); PyObject * OdbBackend_exists(OdbBackend *self, PyObject *py_hex) { int result; size_t len; git_oid oid; if (self->odb_backend->exists == NULL) Py_RETURN_NOTIMPLEMENTED; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; result = self->odb_backend->exists(self->odb_backend, &oid); if (result < 0) return Error_set(result); else if (result == 0) Py_RETURN_FALSE; else Py_RETURN_TRUE; } PyDoc_STRVAR(OdbBackend_exists_prefix__doc__, "exists_prefix(partial_id: Oid) -> Oid\n" "\n" "Given a partial oid, returns the full oid. Raises KeyError if not found,\n" "or ValueError if ambiguous."); PyObject * OdbBackend_exists_prefix(OdbBackend *self, PyObject *py_hex) { int result; size_t len; git_oid oid; if (self->odb_backend->exists_prefix == NULL) Py_RETURN_NOTIMPLEMENTED; len = py_oid_to_git_oid(py_hex, &oid); if (len == 0) return NULL; git_oid out; result = self->odb_backend->exists_prefix(&out, self->odb_backend, &oid, len); if (result < 0) return Error_set(result); return git_oid_to_python(&out); } PyDoc_STRVAR(OdbBackend_refresh__doc__, "refresh()\n" "\n" "If the backend supports a refreshing mechanism, this function will invoke\n" "it. However, the backend implementation should try to stay up-to-date as\n" "much as possible by itself as libgit2 will not automatically invoke this\n" "function. For instance, a potential strategy for the backend\n" "implementation to utilize this could be internally calling the refresh\n" "function on failed lookups."); PyObject * OdbBackend_refresh(OdbBackend *self) { if (self->odb_backend->refresh == NULL) Py_RETURN_NOTIMPLEMENTED; self->odb_backend->refresh(self->odb_backend); Py_RETURN_NONE; } /* * TODO: * - write * - writepack * - writestream * - readstream * - freshen */ static PyMethodDef OdbBackend_methods[] = { METHOD(OdbBackend, read, METH_O), METHOD(OdbBackend, read_prefix, METH_O), METHOD(OdbBackend, read_header, METH_O), METHOD(OdbBackend, exists, METH_O), METHOD(OdbBackend, exists_prefix, METH_O), METHOD(OdbBackend, refresh, METH_NOARGS), {NULL} }; PyDoc_STRVAR(OdbBackend__doc__, "Object database backend."); PyTypeObject OdbBackendType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.OdbBackend", /* tp_name */ sizeof(OdbBackend), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)OdbBackend_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ OdbBackend__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)OdbBackend_as_iter, /* tp_iter */ 0, /* tp_iternext */ OdbBackend_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)OdbBackend_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_odb_backend(git_odb_backend *odb_backend) { OdbBackend *py_backend = PyObject_New(OdbBackend, &OdbBackendType); if (py_backend) py_backend->odb_backend = odb_backend; return (PyObject *)py_backend; } PyDoc_STRVAR(OdbBackendPack__doc__, "Object database backend for packfiles."); int OdbBackendPack_init(OdbBackendPack *self, PyObject *args, PyObject *kwds) { if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "OdbBackendPack takes no keyword arguments"); return -1; } PyObject *py_path; if (!PyArg_ParseTuple(args, "O", &py_path)) return -1; PyObject *tvalue; char *path = pgit_borrow_fsdefault(py_path, &tvalue); if (path == NULL) return -1; int err = git_odb_backend_pack(&self->super.odb_backend, path); Py_DECREF(tvalue); if (err) { Error_set(err); return -1; } return 0; } PyTypeObject OdbBackendPackType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.OdbBackendPack", /* tp_name */ sizeof(OdbBackendPack), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ OdbBackendPack__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &OdbBackendType, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)OdbBackendPack_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyDoc_STRVAR(OdbBackendLoose__doc__, "OdbBackendLoose(objects_dir, compression_level," " do_fsync, dir_mode=0, file_mode=0)\n" "\n" "Object database backend for loose objects.\n" "\n" "Parameters:\n" "\n" "objects_dir\n" " path to top-level object dir on disk\n" "\n" "compression_level\n" " zlib compression level to use\n" "\n" "do_fsync\n" " true to fsync() after writing\n" "\n" "dir_mode\n" " mode for new directories, or 0 for default\n" "\n" "file_mode\n" " mode for new files, or 0 for default"); int OdbBackendLoose_init(OdbBackendLoose *self, PyObject *args, PyObject *kwds) { if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "OdbBackendLoose takes no keyword arguments"); return -1; } PyObject *py_path; int compression_level, do_fsync; unsigned int dir_mode = 0, file_mode = 0; if (!PyArg_ParseTuple(args, "Oip|II", &py_path, &compression_level, &do_fsync, &dir_mode, &file_mode)) return -1; PyObject *tvalue; char *path = pgit_borrow_fsdefault(py_path, &tvalue); if (path == NULL) return -1; int err = git_odb_backend_loose(&self->super.odb_backend, path, compression_level, do_fsync, dir_mode, file_mode); Py_DECREF(tvalue); if (err) { Error_set(err); return -1; } return 0; } PyTypeObject OdbBackendLooseType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.OdbBackendLoose", /* tp_name */ sizeof(OdbBackendLoose), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ OdbBackendLoose__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &OdbBackendType, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)OdbBackendLoose_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/odb_backend.h000066400000000000000000000026461521317216500200160ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_odb_backend_h #define INCLUDE_pygit2_odb_backend_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject *wrap_odb_backend(git_odb_backend *c_odb_backend); #endif libgit2-pygit2-d86662e/src/oid.c000066400000000000000000000256621521317216500163540ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "utils.h" #include "error.h" #include "oid.h" PyTypeObject OidType; static const git_oid oid_zero = GIT_OID_SHA1_ZERO; PyObject * git_oid_to_python(const git_oid *oid) { Oid *py_oid; py_oid = PyObject_New(Oid, &OidType); if (py_oid == NULL) return NULL; git_oid_cpy(&(py_oid->oid), oid); return (PyObject*)py_oid; } size_t py_hex_to_git_oid(PyObject *py_oid, git_oid *oid) { PyObject *py_hex; int err; char *hex; Py_ssize_t len; /* Unicode */ if (PyUnicode_Check(py_oid)) { py_hex = PyUnicode_AsASCIIString(py_oid); if (py_hex == NULL) return 0; err = PyBytes_AsStringAndSize(py_hex, &hex, &len); if (err) { Py_DECREF(py_hex); return 0; } err = git_oid_fromstrn(oid, hex, len); Py_DECREF(py_hex); if (err < 0) { PyErr_SetObject(Error_type(err), py_oid); return 0; } return (size_t)len; } /* Type error */ PyErr_SetObject(PyExc_TypeError, py_oid); return 0; } size_t py_oid_to_git_oid(PyObject *py_oid, git_oid *oid) { /* Oid */ if (PyObject_TypeCheck(py_oid, (PyTypeObject*)&OidType)) { git_oid_cpy(oid, &((Oid*)py_oid)->oid); return GIT_OID_HEXSZ; } /* Hex */ return py_hex_to_git_oid(py_oid, oid); } int py_oid_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid) { int err; size_t len; git_odb *odb = NULL; git_oid tmp; len = py_oid_to_git_oid(py_str, oid); if (len == 0) return -1; if (len == GIT_OID_HEXSZ) return 0; /* Short oid */ err = git_repository_odb(&odb, repo); if (err < 0) goto error; err = git_odb_exists_prefix(&tmp, odb, oid, len); if (err < 0) goto error; git_oid_cpy(oid, &tmp); git_odb_free(odb); return 0; error: git_odb_free(odb); Error_set(err); return -1; } PyObject * git_oid_to_py_str(const git_oid *oid) { char hex[GIT_OID_HEXSZ]; git_oid_fmt(hex, oid); return to_unicode_n(hex, GIT_OID_HEXSZ, "utf-8", "strict"); } int Oid_init(Oid *self, PyObject *args, PyObject *kw) { char *keywords[] = {"raw", "hex", NULL}; PyObject *raw = NULL, *hex = NULL; int err; char *bytes; Py_ssize_t len; if (!PyArg_ParseTupleAndKeywords(args, kw, "|OO", keywords, &raw, &hex)) return -1; /* We expect one or the other, but not both. */ if (raw == NULL && hex == NULL) { PyErr_SetString(PyExc_ValueError, "Expected raw or hex."); return -1; } if (raw != NULL && hex != NULL) { PyErr_SetString(PyExc_ValueError, "Expected raw or hex, not both."); return -1; } /* Case 1: raw */ if (raw != NULL) { err = PyBytes_AsStringAndSize(raw, &bytes, &len); if (err) return -1; if (len > GIT_OID_RAWSZ) { PyErr_SetObject(PyExc_ValueError, raw); return -1; } memcpy(self->oid.id, (const unsigned char*)bytes, len); return 0; } /* Case 2: hex */ len = py_hex_to_git_oid(hex, &self->oid); if (len == 0) return -1; return 0; } Py_hash_t Oid_hash(PyObject *oid) { PyObject *py_oid = git_oid_to_py_str(&((Oid *)oid)->oid); Py_hash_t ret = PyObject_Hash(py_oid); Py_DECREF(py_oid); return ret; } PyObject * Oid_richcompare(PyObject *self, PyObject *other, int op) { git_oid *oid = &((Oid*)self)->oid; int cmp; // Can compare an oid against another oid or a unicode string if (PyObject_TypeCheck(other, &OidType)) { cmp = git_oid_cmp(oid, &((Oid*)other)->oid); } else if (PyObject_TypeCheck(other, &PyUnicode_Type)) { const char * str = PyUnicode_AsUTF8(other); if (str == NULL) { return NULL; } cmp = git_oid_strcmp(oid, str); } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } // Return boolean PyObject *res; switch (op) { case Py_LT: res = (cmp <= 0) ? Py_True: Py_False; break; case Py_LE: res = (cmp < 0) ? Py_True: Py_False; break; case Py_EQ: res = (cmp == 0) ? Py_True: Py_False; break; case Py_NE: res = (cmp != 0) ? Py_True: Py_False; break; case Py_GT: res = (cmp > 0) ? Py_True: Py_False; break; case Py_GE: res = (cmp >= 0) ? Py_True: Py_False; break; default: PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op); return NULL; } Py_INCREF(res); return res; } PyObject * Oid__str__(Oid *self) { return git_oid_to_py_str(&self->oid); } int Oid__bool(PyObject *self) { git_oid *oid = &((Oid*)self)->oid; return !git_oid_equal(oid, &oid_zero); } PyDoc_STRVAR(Oid_raw__doc__, "Raw oid, a 20 bytes string."); PyObject * Oid_raw__get__(Oid *self) { return PyBytes_FromStringAndSize((const char*)self->oid.id, GIT_OID_RAWSZ); } PyGetSetDef Oid_getseters[] = { GETTER(Oid, raw), {NULL}, }; PyNumberMethods Oid_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ 0, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ Oid__bool, /* nb_bool */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ 0, /* nb_or */ 0, /* nb_int */ 0, /* nb_reserved */ 0, /* nb_float */ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ 0, /* nb_inplace_rshift */ 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ 0, /* nb_floor_divide */ 0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ 0, /* nb_index */ 0, /* nb_matrix_multiply */ 0, /* nb_inplace_matrix_multiply */ }; PyDoc_STRVAR(Oid__doc__, "Object id."); PyTypeObject OidType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Oid", /* tp_name */ sizeof(Oid), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Oid__str__, /* tp_repr */ &Oid_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)Oid_hash, /* tp_hash */ 0, /* tp_call */ (reprfunc)Oid__str__, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Oid__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)Oid_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ Oid_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Oid_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/oid.h000066400000000000000000000031241521317216500163460ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_oid_h #define INCLUDE_pygit2_oid_h #define PY_SSIZE_T_CLEAN #include #include size_t py_oid_to_git_oid(PyObject *py_str, git_oid *oid); int py_oid_to_git_oid_expand(git_repository *repo, PyObject *py_str, git_oid *oid); PyObject* git_oid_to_python(const git_oid *oid); PyObject* git_oid_to_py_str(const git_oid *oid); #endif libgit2-pygit2-d86662e/src/patch.c000066400000000000000000000232111521317216500166640ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "diff.h" #include "error.h" #include "object.h" #include "oid.h" #include "types.h" #include "utils.h" extern PyTypeObject DiffHunkType; extern PyTypeObject BlobType; PyTypeObject PatchType; PyObject * wrap_patch(git_patch *patch, Blob *oldblob, Blob *newblob) { Patch *py_patch; assert(patch); py_patch = PyObject_New(Patch, &PatchType); if (py_patch) { py_patch->patch = patch; Py_XINCREF(oldblob); py_patch->oldblob = oldblob; Py_XINCREF(newblob); py_patch->newblob = newblob; } return (PyObject*) py_patch; } static void Patch_dealloc(Patch *self) { Py_CLEAR(self->oldblob); Py_CLEAR(self->newblob); git_patch_free(self->patch); PyObject_Del(self); } PyDoc_STRVAR(Patch_delta__doc__, "Get the delta associated with a patch."); PyObject * Patch_delta__get__(Patch *self) { assert(self->patch); return wrap_diff_delta(git_patch_get_delta(self->patch)); } PyDoc_STRVAR(Patch_line_stats__doc__, "Get line counts of each type in a patch (context, additions, deletions)."); PyObject * Patch_line_stats__get__(Patch *self) { size_t context, additions, deletions; int err; assert(self->patch); err = git_patch_line_stats(&context, &additions, &deletions, self->patch); if (err < 0) return Error_set(err); return Py_BuildValue("III", context, additions, deletions); } PyDoc_STRVAR(Patch_create_from__doc__, "Create a patch from blobs, buffers, or a blob and a buffer"); static PyObject * Patch_create_from(PyObject *self, PyObject *args, PyObject *kwds) { /* A generic wrapper around * git_patch_from_blob_and_buffer * git_patch_from_buffers * git_patch_from_blobs */ git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_patch *patch; char *old_as_path = NULL, *new_as_path = NULL; PyObject *oldobj = NULL, *newobj = NULL; Blob *oldblob = NULL, *newblob = NULL; const char *oldbuf = NULL, *newbuf = NULL; Py_ssize_t oldbuflen, newbuflen; int err; char *keywords[] = {"old", "new", "old_as_path", "new_as_path", "flag", "context_lines", "interhunk_lines", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|zzIHH", keywords, &oldobj, &newobj, &old_as_path, &new_as_path, &opts.flags, &opts.context_lines, &opts.interhunk_lines)) return NULL; if (oldobj != Py_None && PyObject_TypeCheck(oldobj, &BlobType)) { /* The old object exists and is a blob */ oldblob = (Blob*)oldobj; if (Object__load((Object*)oldblob) == NULL) { return NULL; } // Lazy load if (newobj != Py_None && PyObject_TypeCheck(newobj, &BlobType)) { /* The new object exists and is a blob */ newblob = (Blob*)newobj; if (Object__load((Object*)newblob) == NULL) { return NULL; } // Lazy load err = git_patch_from_blobs(&patch, oldblob->blob, old_as_path, newblob->blob, new_as_path, &opts); } else { /* The new object does not exist or is a buffer */ if (!PyArg_Parse(newobj, "z#", &newbuf, &newbuflen)) return NULL; err = git_patch_from_blob_and_buffer(&patch, oldblob->blob, old_as_path, newbuf, newbuflen, new_as_path, &opts); } } else { /* The old object does exist and is a buffer */ if (!PyArg_Parse(oldobj, "z#", &oldbuf, &oldbuflen)) return NULL; if (!PyArg_Parse(newobj, "z#", &newbuf, &newbuflen)) return NULL; err = git_patch_from_buffers(&patch, oldbuf, oldbuflen, old_as_path, newbuf, newbuflen, new_as_path, &opts); } if (err < 0) return Error_set(err); return wrap_patch(patch, oldblob, newblob); } PyDoc_STRVAR(Patch_data__doc__, "The raw bytes of the patch's contents."); PyObject * Patch_data__get__(Patch *self) { git_buf buf = {NULL}; int err; PyObject *bytes; assert(self->patch); err = git_patch_to_buf(&buf, self->patch); if (err < 0) return Error_set(err); bytes = PyBytes_FromStringAndSize(buf.ptr, buf.size); git_buf_dispose(&buf); return bytes; } PyDoc_STRVAR(Patch_text__doc__, "Patch diff string. Can be None in some cases, such as empty commits.\n" "Note that this decodes the content to Unicode assuming UTF-8 encoding. " "For non-UTF-8 content that can lead be a lossy, non-reversible process. " "To access the raw, un-decoded patch, use `patch.data`."); PyObject * Patch_text__get__(Patch *self) { git_buf buf = {NULL}; int err; PyObject *text; assert(self->patch); err = git_patch_to_buf(&buf, self->patch); if (err < 0) return Error_set(err); text = to_unicode_n(buf.ptr, buf.size, NULL, NULL); git_buf_dispose(&buf); return text; } PyDoc_STRVAR(Patch_hunks__doc__, "hunks"); PyObject * Patch_hunks__get__(Patch *self) { size_t i, hunk_amounts; PyObject *py_hunks; PyObject *py_hunk; hunk_amounts = git_patch_num_hunks(self->patch); py_hunks = PyList_New(hunk_amounts); for (i = 0; i < hunk_amounts; i++) { py_hunk = wrap_diff_hunk(self, i); if (py_hunk == NULL) return NULL; PyList_SET_ITEM((PyObject*) py_hunks, i, py_hunk); } return py_hunks; } static PyMethodDef Patch_methods[] = { {"create_from", (PyCFunction) Patch_create_from, METH_KEYWORDS | METH_VARARGS | METH_STATIC, Patch_create_from__doc__}, {NULL} }; PyGetSetDef Patch_getsetters[] = { GETTER(Patch, delta), GETTER(Patch, line_stats), GETTER(Patch, data), GETTER(Patch, text), GETTER(Patch, hunks), {NULL} }; PyDoc_STRVAR(Patch__doc__, "Diff patch object."); PyTypeObject PatchType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Patch", /* tp_name */ sizeof(Patch), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Patch_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Patch__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Patch_methods, /* tp_methods */ 0, /* tp_members */ Patch_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/patch.h000066400000000000000000000026211521317216500166730ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_patch_h #define INCLUDE_pygit2_patch_h #define PY_SSIZE_T_CLEAN #include #include PyObject* wrap_patch(git_patch *patch, Blob *oldblob, Blob *newblob); #endif libgit2-pygit2-d86662e/src/pygit2.c000066400000000000000000000672051521317216500170160ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include #include "error.h" #include "types.h" #include "utils.h" #include "repository.h" #include "oid.h" #include "filter.h" PyObject *GitError; PyObject *AlreadyExistsError; PyObject *InvalidSpecError; PyObject *DeltaStatusEnum; PyObject *DiffFlagEnum; PyObject *FileModeEnum; PyObject *FileStatusEnum; PyObject *MergeAnalysisEnum; PyObject *MergePreferenceEnum; PyObject *ObjectTypeEnum; PyObject *ReferenceTypeEnum; extern PyTypeObject RepositoryType; extern PyTypeObject OdbType; extern PyTypeObject OdbBackendType; extern PyTypeObject OdbBackendPackType; extern PyTypeObject OdbBackendLooseType; extern PyTypeObject OidType; extern PyTypeObject ObjectType; extern PyTypeObject CommitType; extern PyTypeObject DiffType; extern PyTypeObject DeltasIterType; extern PyTypeObject DiffIterType; extern PyTypeObject DiffDeltaType; extern PyTypeObject DiffFileType; extern PyTypeObject DiffHunkType; extern PyTypeObject DiffLineType; extern PyTypeObject DiffStatsType; extern PyTypeObject PatchType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject TreeIterType; extern PyTypeObject BlobType; extern PyTypeObject TagType; extern PyTypeObject WalkerType; extern PyTypeObject RefdbType; extern PyTypeObject RefdbBackendType; extern PyTypeObject RefdbFsBackendType; extern PyTypeObject ReferenceType; extern PyTypeObject RevSpecType; extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogEntryType; extern PyTypeObject BranchType; extern PyTypeObject SignatureType; extern PyTypeObject RemoteType; extern PyTypeObject RefspecType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; extern PyTypeObject WorktreeType; extern PyTypeObject MailmapType; extern PyTypeObject StashType; extern PyTypeObject RefsIteratorType; extern PyTypeObject FilterSourceType; PyDoc_STRVAR(discover_repository__doc__, "discover_repository(path: str, across_fs: bool = False[, ceiling_dirs: str]) -> str\n" "\n" "Look for a git repository and return its path. If not found returns None."); PyObject * discover_repository(PyObject *self, PyObject *args) { git_buf repo_path = {NULL}; const char *path = NULL; PyBytesObject *py_path = NULL; int across_fs = 0; PyBytesObject *py_ceiling_dirs = NULL; const char *ceiling_dirs = NULL; PyObject *py_repo_path = NULL; int err; PyObject *result = NULL; if (!PyArg_ParseTuple(args, "O&|IO&", PyUnicode_FSConverter, &py_path, &across_fs, PyUnicode_FSConverter, &py_ceiling_dirs)) return NULL; if (py_path != NULL) path = PyBytes_AS_STRING(py_path); if (py_ceiling_dirs != NULL) ceiling_dirs = PyBytes_AS_STRING(py_ceiling_dirs); memset(&repo_path, 0, sizeof(git_buf)); err = git_repository_discover(&repo_path, path, across_fs, ceiling_dirs); if (err == GIT_OK) { py_repo_path = PyUnicode_DecodeFSDefault(repo_path.ptr); git_buf_dispose(&repo_path); result = py_repo_path; } else if (err == GIT_ENOTFOUND) { result = Py_None; } else { result = Error_set_str(err, path); } Py_XDECREF(py_ceiling_dirs); Py_XDECREF(py_path); return result; }; PyDoc_STRVAR(hashfile__doc__, "hashfile(path: str) -> Oid\n" "\n" "Returns the oid of a new blob from a file path without actually writing\n" "to the odb."); PyObject * hashfile(PyObject *self, PyObject *args) { git_oid oid; PyBytesObject *py_path = NULL; const char* path = NULL; int err; if (!PyArg_ParseTuple(args, "O&", PyUnicode_FSConverter, &py_path)) return NULL; if (py_path != NULL) path = PyBytes_AS_STRING(py_path); err = git_odb_hashfile(&oid, path, GIT_OBJECT_BLOB); Py_XDECREF(py_path); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(hash__doc__, "hash(data: bytes) -> Oid\n" "\n" "Returns the oid of a new blob from a string without actually writing to\n" "the odb."); PyObject * hash(PyObject *self, PyObject *args) { git_oid oid; const char *data; Py_ssize_t size; int err; if (!PyArg_ParseTuple(args, "s#", &data, &size)) return NULL; err = git_odb_hash(&oid, data, size, GIT_OBJECT_BLOB); if (err < 0) { return Error_set(err); } return git_oid_to_python(&oid); } PyDoc_STRVAR(init_file_backend__doc__, "init_file_backend(path: str, flags: int = 0) -> object\n" "\n" "Open repo backend given path."); PyObject * init_file_backend(PyObject *self, PyObject *args) { PyBytesObject *py_path = NULL; const char* path = NULL; unsigned int flags = 0; int err = GIT_OK; git_repository *repository = NULL; PyObject *result = NULL; if (!PyArg_ParseTuple(args, "O&|I", PyUnicode_FSConverter, &py_path, &flags)) return NULL; if (py_path != NULL) path = PyBytes_AS_STRING(py_path); err = git_repository_open_ext(&repository, path, flags, NULL); if (err == GIT_OK) { result = PyCapsule_New(repository, "backend", NULL); } else { result = NULL; Error_set_str(err, path); if (repository) { git_repository_free(repository); } if (err == GIT_ENOTFOUND) { PyErr_Format(GitError, "Repository not found at %s", path); } } Py_XDECREF(py_path); return result; } PyDoc_STRVAR(reference_is_valid_name__doc__, "reference_is_valid_name(refname: str) -> bool\n" "\n" "Check if the passed string is a valid reference name."); PyObject * reference_is_valid_name(PyObject *self, PyObject *py_refname) { const char *refname = pgit_borrow(py_refname); if (refname == NULL) return NULL; int result = git_reference_is_valid_name(refname); return PyBool_FromLong(result); } PyDoc_STRVAR(tree_entry_cmp__doc__, "tree_entry_cmp(a: Object, b: Object) -> int\n" "\n" "Rich comparison for objects, only available when the objects have been\n" "obtained through a tree. The sort criteria is the one Git uses to sort\n" "tree entries in a tree object. This function wraps git_tree_entry_cmp.\n" "\n" "Returns < 0 if a is before b, > 0 if a is after b, and 0 if a and b are\n" "the same."); PyObject * tree_entry_cmp(PyObject *self, PyObject *args) { Object *a, *b; int cmp; if (!PyArg_ParseTuple(args, "O!O!", &ObjectType, &a, &ObjectType, &b)) return NULL; if (a->entry == NULL || b->entry == NULL) { PyErr_SetString(PyExc_ValueError, "objects lack entry information"); return NULL; } cmp = git_tree_entry_cmp(a->entry, b->entry); return PyLong_FromLong(cmp); } PyDoc_STRVAR(filter_register__doc__, "filter_register(name: str, filter_cls: Type[Filter], [priority: int = C.GIT_FILTER_DRIVER_PRIORITY]) -> None\n" "\n" "Register a filter under the given name.\n" "\n" "Filters will be run in order of `priority` on smudge (to workdir) and in\n" "reverse order of priority on clean (to odb).\n" "\n" "Two filters are preregistered with libgit2:\n" " - GIT_FILTER_CRLF with priority 0\n" " - GIT_FILTER_IDENT with priority 100\n" "\n" "`priority` defaults to GIT_FILTER_DRIVER_PRIORITY which imitates a core\n" "Git filter driver that will be run last on checkout (smudge) and first \n" "on check-in (clean).\n" "\n" "Note that the filter registry is not thread safe. Any registering or\n" "deregistering of filters should be done outside of any possible usage\n" "of the filters.\n"); PyObject * filter_register(PyObject *self, PyObject *args, PyObject *kwds) { const char *name; Py_ssize_t size; PyObject *py_filter_cls; int priority = GIT_FILTER_DRIVER_PRIORITY; char *keywords[] = {"name", "filter_cls", "priority", NULL}; pygit2_filter *filter; int err; if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#O|i", keywords, &name, &size, &py_filter_cls, &priority)) return NULL; /* py_attrs = py_filter_cls.attributes */ PyObject* py_attrs = PyObject_GetAttrString(py_filter_cls, "attributes"); if (py_attrs == NULL) { return NULL; } char* attrs = pgit_strdup(py_attrs); Py_DECREF(py_attrs); if (attrs == NULL) { return NULL; } /* allocate memory */ filter = malloc(sizeof(pygit2_filter)); if (filter == NULL) { free(attrs); return PyErr_NoMemory(); } memset(filter, 0, sizeof(pygit2_filter)); /* initialize git_filter */ git_filter_init(&filter->filter, GIT_FILTER_VERSION); filter->filter.attributes = attrs; filter->filter.shutdown = pygit2_filter_shutdown; filter->filter.check = pygit2_filter_check; filter->filter.stream = pygit2_filter_stream; filter->filter.cleanup = pygit2_filter_cleanup; /* keep reference to Python filter */ filter->py_filter_cls = py_filter_cls; /* git register filter */ if ((err = git_filter_register(name, &filter->filter, priority)) < 0) { free(attrs); free(filter); return Error_set(err); } Py_INCREF(py_filter_cls); /* libgit2 now owns this reference, will decref in shutdown */ Py_RETURN_NONE; } static void forget_enums(void) { Py_CLEAR(DeltaStatusEnum); Py_CLEAR(DiffFlagEnum); Py_CLEAR(FileModeEnum); Py_CLEAR(FileStatusEnum); Py_CLEAR(MergeAnalysisEnum); Py_CLEAR(MergePreferenceEnum); Py_CLEAR(ObjectTypeEnum); Py_CLEAR(ReferenceTypeEnum); } PyDoc_STRVAR(_cache_enums__doc__, "_cache_enums()\n" "\n" "For internal use only. Do not call this from user code.\n" "\n" "Let the _pygit2 C module cache references to Python enums\n" "defined in pygit2.enums.\n"); PyObject * _cache_enums(PyObject *self, PyObject *args) { (void) args; /* In case this is somehow being called several times, let go of old references */ forget_enums(); PyObject *enums = PyImport_ImportModule("pygit2.enums"); if (enums == NULL) { return NULL; } #define CACHE_PYGIT2_ENUM(name) do { \ name##Enum = PyObject_GetAttrString(enums, #name); \ if (name##Enum == NULL) { goto fail; } \ } while (0) CACHE_PYGIT2_ENUM(DeltaStatus); CACHE_PYGIT2_ENUM(DiffFlag); CACHE_PYGIT2_ENUM(FileMode); CACHE_PYGIT2_ENUM(FileStatus); CACHE_PYGIT2_ENUM(MergeAnalysis); CACHE_PYGIT2_ENUM(MergePreference); CACHE_PYGIT2_ENUM(ObjectType); CACHE_PYGIT2_ENUM(ReferenceType); #undef CACHE_PYGIT2_ENUM Py_RETURN_NONE; fail: Py_DECREF(enums); forget_enums(); return NULL; } void free_module(void *self) { forget_enums(); } static PyMethodDef module_methods[] = { {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hash", hash, METH_VARARGS, hash__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, {"init_file_backend", init_file_backend, METH_VARARGS, init_file_backend__doc__}, {"reference_is_valid_name", reference_is_valid_name, METH_O, reference_is_valid_name__doc__}, {"tree_entry_cmp", tree_entry_cmp, METH_VARARGS, tree_entry_cmp__doc__}, {"filter_register", (PyCFunction)filter_register, METH_VARARGS | METH_KEYWORDS, filter_register__doc__}, {"_cache_enums", _cache_enums, METH_NOARGS, _cache_enums__doc__}, {NULL} }; struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_pygit2", /* m_name */ "Python bindings for libgit2.", /* m_doc */ -1, /* m_size */ module_methods, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ free_module, /* m_free */ }; PyMODINIT_FUNC PyInit__pygit2(void) { PyObject *m = PyModule_Create(&moduledef); if (m == NULL) return NULL; #ifdef Py_GIL_DISABLED PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); #endif /* libgit2 version info */ ADD_CONSTANT_INT(m, LIBGIT2_VER_MAJOR) ADD_CONSTANT_INT(m, LIBGIT2_VER_MINOR) ADD_CONSTANT_INT(m, LIBGIT2_VER_REVISION) ADD_CONSTANT_STR(m, LIBGIT2_VERSION) /* Exceptions */ ADD_EXC(m, GitError, NULL); ADD_EXC(m, AlreadyExistsError, PyExc_ValueError); ADD_EXC(m, InvalidSpecError, PyExc_ValueError); /* Repository */ INIT_TYPE(RepositoryType, NULL, PyType_GenericNew) ADD_TYPE(m, Repository) /* Odb */ INIT_TYPE(OdbType, NULL, PyType_GenericNew) ADD_TYPE(m, Odb) INIT_TYPE(OdbBackendType, NULL, PyType_GenericNew) ADD_TYPE(m, OdbBackend) INIT_TYPE(OdbBackendPackType, &OdbBackendType, PyType_GenericNew) ADD_TYPE(m, OdbBackendPack) INIT_TYPE(OdbBackendLooseType, &OdbBackendType, PyType_GenericNew) ADD_TYPE(m, OdbBackendLoose) /* Oid */ INIT_TYPE(OidType, NULL, PyType_GenericNew) ADD_TYPE(m, Oid) ADD_CONSTANT_INT(m, GIT_OID_RAWSZ) ADD_CONSTANT_INT(m, GIT_OID_HEXSZ) ADD_CONSTANT_STR(m, GIT_OID_HEX_ZERO) ADD_CONSTANT_INT(m, GIT_OID_MINPREFIXLEN) /* * Objects */ INIT_TYPE(ObjectType, NULL, NULL) INIT_TYPE(CommitType, &ObjectType, NULL) INIT_TYPE(SignatureType, NULL, PyType_GenericNew) INIT_TYPE(TreeType, &ObjectType, NULL) INIT_TYPE(TreeIterType, NULL, NULL) INIT_TYPE(TreeBuilderType, NULL, NULL) INIT_TYPE(BlobType, &ObjectType, NULL) INIT_TYPE(TagType, &ObjectType, NULL) INIT_TYPE(RefsIteratorType, NULL, NULL) ADD_TYPE(m, Object) ADD_TYPE(m, Commit) ADD_TYPE(m, Signature) ADD_TYPE(m, Tree) ADD_TYPE(m, TreeBuilder) ADD_TYPE(m, Blob) ADD_TYPE(m, Tag) ADD_CONSTANT_INT(m, GIT_OBJECT_ANY) ADD_CONSTANT_INT(m, GIT_OBJECT_INVALID) ADD_CONSTANT_INT(m, GIT_OBJECT_COMMIT) ADD_CONSTANT_INT(m, GIT_OBJECT_TREE) ADD_CONSTANT_INT(m, GIT_OBJECT_BLOB) ADD_CONSTANT_INT(m, GIT_OBJECT_TAG) ADD_CONSTANT_INT(m, GIT_OBJECT_OFS_DELTA) ADD_CONSTANT_INT(m, GIT_OBJECT_REF_DELTA) /* Valid modes for index and tree entries. */ ADD_CONSTANT_INT(m, GIT_FILEMODE_UNREADABLE) ADD_CONSTANT_INT(m, GIT_FILEMODE_TREE) ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB) ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB_EXECUTABLE) ADD_CONSTANT_INT(m, GIT_FILEMODE_LINK) ADD_CONSTANT_INT(m, GIT_FILEMODE_COMMIT) /* * Log */ INIT_TYPE(WalkerType, NULL, NULL) ADD_TYPE(m, Walker); ADD_CONSTANT_INT(m, GIT_SORT_NONE) ADD_CONSTANT_INT(m, GIT_SORT_TOPOLOGICAL) ADD_CONSTANT_INT(m, GIT_SORT_TIME) ADD_CONSTANT_INT(m, GIT_SORT_REVERSE) /* * Reset */ ADD_CONSTANT_INT(m, GIT_RESET_SOFT) ADD_CONSTANT_INT(m, GIT_RESET_MIXED) ADD_CONSTANT_INT(m, GIT_RESET_HARD) /* Refdb */ INIT_TYPE(RefdbType, NULL, PyType_GenericNew) ADD_TYPE(m, Refdb) INIT_TYPE(RefdbBackendType, NULL, PyType_GenericNew) ADD_TYPE(m, RefdbBackend) INIT_TYPE(RefdbFsBackendType, &RefdbBackendType, PyType_GenericNew) ADD_TYPE(m, RefdbFsBackend) /* * References */ INIT_TYPE(ReferenceType, NULL, PyType_GenericNew) INIT_TYPE(RefLogEntryType, NULL, NULL) INIT_TYPE(RefLogIterType, NULL, NULL) INIT_TYPE(NoteType, NULL, NULL) INIT_TYPE(NoteIterType, NULL, NULL) ADD_TYPE(m, Reference) ADD_TYPE(m, RefLogEntry) ADD_TYPE(m, Note) ADD_CONSTANT_INT(m, GIT_REFERENCES_ALL) ADD_CONSTANT_INT(m, GIT_REFERENCES_BRANCHES) ADD_CONSTANT_INT(m, GIT_REFERENCES_TAGS) /* * RevSpec */ INIT_TYPE(RevSpecType, NULL, NULL) ADD_TYPE(m, RevSpec) ADD_CONSTANT_INT(m, GIT_REVSPEC_SINGLE) ADD_CONSTANT_INT(m, GIT_REVSPEC_RANGE) ADD_CONSTANT_INT(m, GIT_REVSPEC_MERGE_BASE) /* * Worktree */ INIT_TYPE(WorktreeType, NULL, NULL) ADD_TYPE(m, Worktree) /* * Branches */ INIT_TYPE(BranchType, &ReferenceType, NULL); ADD_TYPE(m, Branch) ADD_CONSTANT_INT(m, GIT_BRANCH_LOCAL) ADD_CONSTANT_INT(m, GIT_BRANCH_REMOTE) ADD_CONSTANT_INT(m, GIT_BRANCH_ALL) /* * Index & Working copy */ /* Status */ ADD_CONSTANT_INT(m, GIT_STATUS_CURRENT) ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_NEW) ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_MODIFIED) ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_DELETED) ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_RENAMED) ADD_CONSTANT_INT(m, GIT_STATUS_INDEX_TYPECHANGE) ADD_CONSTANT_INT(m, GIT_STATUS_WT_NEW) ADD_CONSTANT_INT(m, GIT_STATUS_WT_MODIFIED) ADD_CONSTANT_INT(m, GIT_STATUS_WT_DELETED) ADD_CONSTANT_INT(m, GIT_STATUS_WT_TYPECHANGE) ADD_CONSTANT_INT(m, GIT_STATUS_WT_RENAMED) ADD_CONSTANT_INT(m, GIT_STATUS_WT_UNREADABLE) ADD_CONSTANT_INT(m, GIT_STATUS_IGNORED) /* Flags for ignored files */ ADD_CONSTANT_INT(m, GIT_STATUS_CONFLICTED) /* Different checkout strategies */ ADD_CONSTANT_INT(m, GIT_CHECKOUT_NONE) ADD_CONSTANT_INT(m, GIT_CHECKOUT_SAFE) ADD_CONSTANT_INT(m, GIT_CHECKOUT_FORCE) ADD_CONSTANT_INT(m, GIT_CHECKOUT_RECREATE_MISSING) ADD_CONSTANT_INT(m, GIT_CHECKOUT_ALLOW_CONFLICTS) ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_UNTRACKED) ADD_CONSTANT_INT(m, GIT_CHECKOUT_REMOVE_IGNORED) ADD_CONSTANT_INT(m, GIT_CHECKOUT_UPDATE_ONLY) ADD_CONSTANT_INT(m, GIT_CHECKOUT_DONT_UPDATE_INDEX) ADD_CONSTANT_INT(m, GIT_CHECKOUT_NO_REFRESH) ADD_CONSTANT_INT(m, GIT_CHECKOUT_SKIP_UNMERGED) ADD_CONSTANT_INT(m, GIT_CHECKOUT_USE_OURS) ADD_CONSTANT_INT(m, GIT_CHECKOUT_USE_THEIRS) ADD_CONSTANT_INT(m, GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) ADD_CONSTANT_INT(m, GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES) ADD_CONSTANT_INT(m, GIT_CHECKOUT_DONT_OVERWRITE_IGNORED) ADD_CONSTANT_INT(m, GIT_CHECKOUT_CONFLICT_STYLE_MERGE) ADD_CONSTANT_INT(m, GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) ADD_CONSTANT_INT(m, GIT_CHECKOUT_DONT_REMOVE_EXISTING) ADD_CONSTANT_INT(m, GIT_CHECKOUT_DONT_WRITE_INDEX) ADD_CONSTANT_INT(m, GIT_CHECKOUT_DRY_RUN) ADD_CONSTANT_INT(m, GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3) /* * Diff */ INIT_TYPE(DiffType, NULL, NULL) INIT_TYPE(DeltasIterType, NULL, NULL) INIT_TYPE(DiffIterType, NULL, NULL) INIT_TYPE(DiffDeltaType, NULL, NULL) INIT_TYPE(DiffFileType, NULL, NULL) INIT_TYPE(DiffHunkType, NULL, NULL) INIT_TYPE(DiffLineType, NULL, NULL) INIT_TYPE(DiffStatsType, NULL, NULL) INIT_TYPE(PatchType, NULL, NULL) ADD_TYPE(m, Diff) ADD_TYPE(m, DiffDelta) ADD_TYPE(m, DiffFile) ADD_TYPE(m, DiffHunk) ADD_TYPE(m, DiffLine) ADD_TYPE(m, DiffStats) ADD_TYPE(m, Patch) /* (git_diff_options in libgit2) */ ADD_CONSTANT_INT(m, GIT_DIFF_NORMAL) ADD_CONSTANT_INT(m, GIT_DIFF_REVERSE) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_IGNORED) ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNTRACKED) ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_UNTRACKED_DIRS) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNMODIFIED) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_FILEMODE) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_SUBMODULES) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_CASE) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_CASECHANGE) ADD_CONSTANT_INT(m, GIT_DIFF_DISABLE_PATHSPEC_MATCH) ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK) ADD_CONSTANT_INT(m, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS) ADD_CONSTANT_INT(m, GIT_DIFF_UPDATE_INDEX) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNREADABLE) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED) ADD_CONSTANT_INT(m, GIT_DIFF_INDENT_HEURISTIC) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_BLANK_LINES) ADD_CONSTANT_INT(m, GIT_DIFF_FORCE_TEXT) ADD_CONSTANT_INT(m, GIT_DIFF_FORCE_BINARY) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE_CHANGE) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_WHITESPACE_EOL) ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_UNTRACKED_CONTENT) ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_UNMODIFIED) ADD_CONSTANT_INT(m, GIT_DIFF_PATIENCE) ADD_CONSTANT_INT(m, GIT_DIFF_MINIMAL) ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_BINARY) /* Formatting options for diff stats (git_diff_stats_format_t in libgit2) */ ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NONE) ADD_CONSTANT_INT(m, GIT_DIFF_STATS_FULL) ADD_CONSTANT_INT(m, GIT_DIFF_STATS_SHORT) ADD_CONSTANT_INT(m, GIT_DIFF_STATS_NUMBER) ADD_CONSTANT_INT(m, GIT_DIFF_STATS_INCLUDE_SUMMARY) /* Flags for Diff.find_similar (git_diff_find_t in libgit2) */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_BY_CONFIG) /** Obey diff.renames */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES) /* --find-renames */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) /* --break-rewrites=N */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_COPIES) /* --find-copies */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED) /* --find-copies-harder */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_REWRITES) /* --break-rewrites=/M */ ADD_CONSTANT_INT(m, GIT_DIFF_BREAK_REWRITES) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_AND_BREAK_REWRITES) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_FOR_UNTRACKED) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_ALL) /* Turn on all finding features. */ ADD_CONSTANT_INT(m, GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_IGNORE_WHITESPACE) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_EXACT_MATCH_ONLY) ADD_CONSTANT_INT(m, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY) ADD_CONSTANT_INT(m, GIT_DIFF_FIND_REMOVE_UNMODIFIED) /* DiffDelta and DiffFile flags (git_diff_flag_t in libgit2) */ ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_BINARY) ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_NOT_BINARY) ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_VALID_ID) ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_EXISTS) ADD_CONSTANT_INT(m, GIT_DIFF_FLAG_VALID_SIZE) /* DiffDelta.status (git_delta_t in libgit2) */ ADD_CONSTANT_INT(m, GIT_DELTA_UNMODIFIED) ADD_CONSTANT_INT(m, GIT_DELTA_ADDED) ADD_CONSTANT_INT(m, GIT_DELTA_DELETED) ADD_CONSTANT_INT(m, GIT_DELTA_MODIFIED) ADD_CONSTANT_INT(m, GIT_DELTA_RENAMED) ADD_CONSTANT_INT(m, GIT_DELTA_COPIED) ADD_CONSTANT_INT(m, GIT_DELTA_IGNORED) ADD_CONSTANT_INT(m, GIT_DELTA_UNTRACKED) ADD_CONSTANT_INT(m, GIT_DELTA_TYPECHANGE) ADD_CONSTANT_INT(m, GIT_DELTA_UNREADABLE) ADD_CONSTANT_INT(m, GIT_DELTA_CONFLICTED) /* Config */ ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_PROGRAMDATA); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_XDG); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_GLOBAL); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_LOCAL); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_WORKTREE); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_APP); ADD_CONSTANT_INT(m, GIT_CONFIG_HIGHEST_LEVEL); /* Blame */ ADD_CONSTANT_INT(m, GIT_BLAME_NORMAL) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_FILE) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES) ADD_CONSTANT_INT(m, GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES) ADD_CONSTANT_INT(m, GIT_BLAME_FIRST_PARENT) ADD_CONSTANT_INT(m, GIT_BLAME_USE_MAILMAP) ADD_CONSTANT_INT(m, GIT_BLAME_IGNORE_WHITESPACE) /* Merge */ ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_NONE) ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_NORMAL) ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UP_TO_DATE) ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_FASTFORWARD) ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UNBORN) ADD_CONSTANT_INT(m, GIT_MERGE_PREFERENCE_NONE) ADD_CONSTANT_INT(m, GIT_MERGE_PREFERENCE_NO_FASTFORWARD) ADD_CONSTANT_INT(m, GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY) /* Describe */ ADD_CONSTANT_INT(m, GIT_DESCRIBE_DEFAULT); ADD_CONSTANT_INT(m, GIT_DESCRIBE_TAGS); ADD_CONSTANT_INT(m, GIT_DESCRIBE_ALL); /* Stash */ ADD_CONSTANT_INT(m, GIT_STASH_DEFAULT); ADD_CONSTANT_INT(m, GIT_STASH_KEEP_INDEX); ADD_CONSTANT_INT(m, GIT_STASH_INCLUDE_UNTRACKED); ADD_CONSTANT_INT(m, GIT_STASH_INCLUDE_IGNORED); ADD_CONSTANT_INT(m, GIT_STASH_KEEP_ALL); ADD_CONSTANT_INT(m, GIT_STASH_APPLY_DEFAULT); ADD_CONSTANT_INT(m, GIT_STASH_APPLY_REINSTATE_INDEX); /* Apply location */ ADD_CONSTANT_INT(m, GIT_APPLY_LOCATION_WORKDIR); ADD_CONSTANT_INT(m, GIT_APPLY_LOCATION_INDEX); ADD_CONSTANT_INT(m, GIT_APPLY_LOCATION_BOTH); /* Submodule */ ADD_CONSTANT_INT(m, GIT_SUBMODULE_IGNORE_UNSPECIFIED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_IGNORE_NONE); ADD_CONSTANT_INT(m, GIT_SUBMODULE_IGNORE_UNTRACKED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_IGNORE_DIRTY); ADD_CONSTANT_INT(m, GIT_SUBMODULE_IGNORE_ALL); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_IN_HEAD); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_IN_INDEX); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_IN_CONFIG); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_IN_WD); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_INDEX_ADDED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_INDEX_DELETED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_INDEX_MODIFIED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_ADDED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_DELETED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_MODIFIED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_WD_MODIFIED); ADD_CONSTANT_INT(m, GIT_SUBMODULE_STATUS_WD_UNTRACKED); /* Mailmap */ INIT_TYPE(MailmapType, NULL, PyType_GenericNew) ADD_TYPE(m, Mailmap) /* Stash */ INIT_TYPE(StashType, NULL, NULL) ADD_TYPE(m, Stash) /* Blob filter */ ADD_CONSTANT_INT(m, GIT_BLOB_FILTER_CHECK_FOR_BINARY); ADD_CONSTANT_INT(m, GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES); ADD_CONSTANT_INT(m, GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD); ADD_CONSTANT_INT(m, GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT); ADD_CONSTANT_INT(m, GIT_FILTER_DRIVER_PRIORITY); ADD_CONSTANT_INT(m, GIT_FILTER_TO_WORKTREE); ADD_CONSTANT_INT(m, GIT_FILTER_SMUDGE); ADD_CONSTANT_INT(m, GIT_FILTER_TO_ODB); ADD_CONSTANT_INT(m, GIT_FILTER_CLEAN); ADD_CONSTANT_INT(m, GIT_FILTER_DEFAULT); ADD_CONSTANT_INT(m, GIT_FILTER_ALLOW_UNSAFE); ADD_CONSTANT_INT(m, GIT_FILTER_NO_SYSTEM_ATTRIBUTES); ADD_CONSTANT_INT(m, GIT_FILTER_ATTRIBUTES_FROM_HEAD); ADD_CONSTANT_INT(m, GIT_FILTER_ATTRIBUTES_FROM_COMMIT); INIT_TYPE(FilterSourceType, NULL, NULL); ADD_TYPE(m, FilterSource); /* Global initialization of libgit2 */ git_libgit2_init(); return m; fail: Py_DECREF(m); return NULL; } libgit2-pygit2-d86662e/src/refdb.c000066400000000000000000000154511521317216500166560ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "refdb.h" #include "types.h" #include "utils.h" #include #include extern PyTypeObject RepositoryType; extern PyTypeObject RefdbType; void Refdb_dealloc(Refdb *self) { git_refdb_free(self->refdb); Py_TYPE(self)->tp_free((PyObject *) self); } PyDoc_STRVAR(Refdb_compress__doc__, "compress()\n" "\n" "Suggests that the given refdb compress or optimize its references.\n" "This mechanism is implementation specific. For on-disk reference\n" "databases, for example, this may pack all loose references."); PyObject * Refdb_compress(Refdb *self) { int err = git_refdb_compress(self->refdb); if (err != 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Refdb_set_backend__doc__, "set_backend(backend: RefdbBackend)\n" "\n" "Sets a custom RefdbBackend for this Refdb."); PyObject * Refdb_set_backend(Refdb *self, RefdbBackend *backend) { int err; err = git_refdb_set_backend(self->refdb, backend->refdb_backend); if (err != 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Refdb_new__doc__, "Refdb.new(repo: Repository) -> Refdb\n" "Creates a new refdb with no backend."); PyObject * Refdb_new(PyObject *self, Repository *repo) { if (!PyObject_IsInstance((PyObject *)repo, (PyObject *)&RepositoryType)) { PyErr_SetString(PyExc_TypeError, "Refdb.new expects an object of type " "pygit2.Repository"); return NULL; } git_refdb *refdb; int err = git_refdb_new(&refdb, repo->repo); if (err) { Error_set(err); return NULL; } return wrap_refdb(refdb); } PyDoc_STRVAR(Refdb_open__doc__, "open(repo: Repository) -> Refdb\n" "\n" "Create a new reference database and automatically add\n" "the default backends, assuming the repository dir as the folder."); PyObject * Refdb_open(PyObject *self, Repository *repo) { if (!PyObject_IsInstance((PyObject *)repo, (PyObject *)&RepositoryType)) { PyErr_SetString(PyExc_TypeError, "Refdb.open expects an object of type " "pygit2.Repository"); return NULL; } git_refdb *refdb; int err = git_refdb_open(&refdb, repo->repo); if (err) { Error_set(err); return NULL; } return wrap_refdb(refdb); } static PyMethodDef Refdb_methods[] = { METHOD(Refdb, compress, METH_NOARGS), METHOD(Refdb, set_backend, METH_O), {"new", (PyCFunction) Refdb_new, METH_O | METH_STATIC, Refdb_new__doc__}, {"open", (PyCFunction) Refdb_open, METH_O | METH_STATIC, Refdb_open__doc__}, {NULL} }; PyDoc_STRVAR(Refdb__doc__, "Reference database."); PyTypeObject RefdbType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Refdb", /* tp_name */ sizeof(Refdb), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Refdb_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Refdb__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Refdb_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_refdb(git_refdb *c_refdb) { Refdb *py_refdb = PyObject_New(Refdb, &RefdbType); if (py_refdb) py_refdb->refdb = c_refdb; return (PyObject *)py_refdb; } libgit2-pygit2-d86662e/src/refdb.h000066400000000000000000000026101521317216500166540ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_refdb_h #define INCLUDE_pygit2_refdb_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject *wrap_refdb(git_refdb *c_refdb); #endif libgit2-pygit2-d86662e/src/refdb_backend.c000066400000000000000000000712461521317216500203310ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "types.h" #include "oid.h" #include "reference.h" #include "signature.h" #include "utils.h" #include "wildmatch.h" #include #include #include extern PyTypeObject ReferenceType; extern PyTypeObject RepositoryType; extern PyTypeObject SignatureType; struct pygit2_refdb_backend { git_refdb_backend backend; PyObject *RefdbBackend; PyObject *exists, *lookup, *iterator, *write, *rename, *delete, *compress, *has_log, *ensure_log, *reflog_read, *reflog_write, *reflog_rename, *reflog_delete, *lock, *unlock; }; struct pygit2_refdb_iterator { struct git_reference_iterator base; PyObject *iterator; char *glob; }; static Reference * iterator_get_next(struct pygit2_refdb_iterator *iter) { Reference *ref; while ((ref = (Reference *)PyIter_Next(iter->iterator)) != NULL) { if (!iter->glob) { return ref; } const char *name = git_reference_name(ref->reference); if (wildmatch(iter->glob, name, 0) != WM_NOMATCH) { return ref; } } return NULL; } static int pygit2_refdb_iterator_next(git_reference **out, git_reference_iterator *_iter) { struct pygit2_refdb_iterator *iter = (struct pygit2_refdb_iterator *)_iter; Reference *ref = iterator_get_next(iter); if (ref == NULL) { *out = NULL; return GIT_ITEROVER; } if (!PyObject_IsInstance((PyObject *)ref, (PyObject *)&ReferenceType)) { PyErr_SetString(PyExc_TypeError, "RefdbBackend iterator must yield References"); return GIT_EUSER; } *out = ref->reference; return 0; } static int pygit2_refdb_iterator_next_name(const char **ref_name, git_reference_iterator *_iter) { struct pygit2_refdb_iterator *iter = (struct pygit2_refdb_iterator *)_iter; Reference *ref = iterator_get_next(iter); if (ref == NULL) { *ref_name = NULL; return GIT_ITEROVER; } if (!PyObject_IsInstance((PyObject *)ref, (PyObject *)&ReferenceType)) { PyErr_SetString(PyExc_TypeError, "RefdbBackend iterator must yield References"); return GIT_EUSER; } *ref_name = git_reference_name(ref->reference); return 0; } static void pygit2_refdb_iterator_free(git_reference_iterator *_iter) { struct pygit2_refdb_iterator *iter = (struct pygit2_refdb_iterator *)_iter; Py_DECREF(iter->iterator); free(iter->glob); } static int pygit2_refdb_backend_iterator(git_reference_iterator **iter, struct git_refdb_backend *_be, const char *glob) { struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; PyObject *iterator = PyObject_GetIter((PyObject *)be->RefdbBackend); assert(iterator); struct pygit2_refdb_iterator *pyiter = calloc(1, sizeof(struct pygit2_refdb_iterator)); if (pyiter == NULL) { Py_DECREF(iterator); git_error_set(GIT_ERROR_NOMEMORY, "out of memory"); return GIT_ERROR; } *iter = (git_reference_iterator *)pyiter; pyiter->iterator = iterator; pyiter->base.next = pygit2_refdb_iterator_next; pyiter->base.next_name = pygit2_refdb_iterator_next_name; pyiter->base.free = pygit2_refdb_iterator_free; pyiter->glob = strdup(glob); return 0; } static int pygit2_refdb_backend_exists(int *exists, git_refdb_backend *_be, const char *ref_name) { int err; PyObject *args, *result; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; if ((args = Py_BuildValue("(s)", ref_name)) == NULL) return GIT_EUSER; result = PyObject_CallObject(be->exists, args); Py_DECREF(args); if ((err = git_error_for_exc()) != 0) goto out; *exists = PyObject_IsTrue(result); out: Py_DECREF(result); return 0; } static int pygit2_refdb_backend_lookup(git_reference **out, git_refdb_backend *_be, const char *ref_name) { int err; PyObject *args; Reference *result; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; if ((args = Py_BuildValue("(s)", ref_name)) == NULL) return GIT_EUSER; result = (Reference *)PyObject_CallObject(be->lookup, args); Py_DECREF(args); if ((err = git_error_for_exc()) != 0) goto out; if (!PyObject_IsInstance((PyObject *)result, (PyObject *)&ReferenceType)) { PyErr_SetString(PyExc_TypeError, "Expected object of type pygit2.Reference"); err = GIT_EUSER; goto out; } *out = result->reference; out: return err; } static int pygit2_refdb_backend_write(git_refdb_backend *_be, const git_reference *_ref, int force, const git_signature *_who, const char *message, const git_oid *_old, const char *old_target) { int err; PyObject *args = NULL, *ref = NULL, *who = NULL, *old = NULL; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; // XXX: Drops const if ((ref = wrap_reference((git_reference *)_ref, NULL)) == NULL) goto euser; if ((who = build_signature(NULL, _who, "utf-8")) == NULL) goto euser; if ((old = git_oid_to_python(_old)) == NULL) goto euser; if ((args = Py_BuildValue("(NNNsNs)", ref, force ? Py_True : Py_False, who, message, old, old_target)) == NULL) goto euser; PyObject_CallObject(be->write, args); err = git_error_for_exc(); out: Py_DECREF(ref); Py_DECREF(who); Py_DECREF(old); Py_DECREF(args); return err; euser: err = GIT_EUSER; goto out; } static int pygit2_refdb_backend_rename(git_reference **out, git_refdb_backend *_be, const char *old_name, const char *new_name, int force, const git_signature *_who, const char *message) { int err; PyObject *args, *who; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; if ((who = build_signature(NULL, _who, "utf-8")) != NULL) return GIT_EUSER; if ((args = Py_BuildValue("(ssNNs)", old_name, new_name, force ? Py_True : Py_False, who, message)) == NULL) { Py_DECREF(who); return GIT_EUSER; } Reference *ref = (Reference *)PyObject_CallObject(be->rename, args); Py_DECREF(who); Py_DECREF(args); if ((err = git_error_for_exc()) != 0) return err; if (!PyObject_IsInstance((PyObject *)ref, (PyObject *)&ReferenceType)) { PyErr_SetString(PyExc_TypeError, "Expected object of type pygit2.Reference"); return GIT_EUSER; } git_reference_dup(out, ref->reference); Py_DECREF(ref); return 0; } static int pygit2_refdb_backend_del(git_refdb_backend *_be, const char *ref_name, const git_oid *_old, const char *old_target) { PyObject *args, *old; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; old = git_oid_to_python(_old); if ((args = Py_BuildValue("(sOs)", ref_name, old, old_target)) == NULL) { Py_DECREF(old); return GIT_EUSER; } PyObject_CallObject(be->rename, args); Py_DECREF(old); Py_DECREF(args); return git_error_for_exc(); } static int pygit2_refdb_backend_compress(git_refdb_backend *_be) { struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; PyObject_CallObject(be->rename, NULL); return git_error_for_exc(); } static int pygit2_refdb_backend_has_log(git_refdb_backend *_be, const char *refname) { int err; PyObject *args, *result; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; if ((args = Py_BuildValue("(s)", refname)) == NULL) { return GIT_EUSER; } result = PyObject_CallObject(be->has_log, args); Py_DECREF(args); if ((err = git_error_for_exc()) != 0) { return err; } if (PyObject_IsTrue(result)) { Py_DECREF(result); return 1; } Py_DECREF(result); return 0; } static int pygit2_refdb_backend_ensure_log(git_refdb_backend *_be, const char *refname) { int err; PyObject *args, *result; struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; if ((args = Py_BuildValue("(s)", refname)) == NULL) { return GIT_EUSER; } result = PyObject_CallObject(be->ensure_log, args); Py_DECREF(args); if ((err = git_error_for_exc()) != 0) { return err; } if (PyObject_IsTrue(result)) { Py_DECREF(result); return 1; } Py_DECREF(result); return 0; } static int pygit2_refdb_backend_reflog_read(git_reflog **out, git_refdb_backend *backend, const char *name) { /* TODO: Implement first-class pygit2 reflog support * These stubs are here because libgit2 requires refdb_backend to implement * them. libgit2 doesn't actually use them as of 0.99; it assumes the refdb * backend will update the reflogs itself. */ return GIT_EUSER; } static int pygit2_refdb_backend_reflog_write(git_refdb_backend *backend, git_reflog *reflog) { /* TODO: Implement first-class pygit2 reflog support */ return GIT_EUSER; } static int pygit2_refdb_backend_reflog_rename(git_refdb_backend *_backend, const char *old_name, const char *new_name) { /* TODO: Implement first-class pygit2 reflog support */ return GIT_EUSER; } static int pygit2_refdb_backend_reflog_delete(git_refdb_backend *backend, const char *name) { /* TODO: Implement first-class pygit2 reflog support */ return GIT_EUSER; } static void pygit2_refdb_backend_free(git_refdb_backend *_be) { struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)_be; Py_DECREF(be->RefdbBackend); } int RefdbBackend_init(RefdbBackend *self, PyObject *args, PyObject *kwds) { if (args && PyTuple_Size(args) > 0) { PyErr_SetString(PyExc_TypeError, "RefdbBackend takes no arguments"); return -1; } if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "RefdbBackend takes no keyword arguments"); return -1; } struct pygit2_refdb_backend *be = calloc(1, sizeof(struct pygit2_refdb_backend)); if (be == NULL) { PyErr_NoMemory(); return -1; } git_refdb_init_backend(&be->backend, GIT_REFDB_BACKEND_VERSION); be->RefdbBackend = (PyObject *)self; if (PyIter_Check((PyObject *)self)) { be->backend.iterator = pygit2_refdb_backend_iterator; } if (PyObject_HasAttrString((PyObject *)self, "exists")) { be->exists = PyObject_GetAttrString((PyObject *)self, "exists"); be->backend.exists = pygit2_refdb_backend_exists; } if (PyObject_HasAttrString((PyObject *)self, "lookup")) { be->lookup = PyObject_GetAttrString((PyObject *)self, "lookup"); be->backend.lookup = pygit2_refdb_backend_lookup; } if (PyObject_HasAttrString((PyObject *)self, "write")) { be->write = PyObject_GetAttrString((PyObject *)self, "write"); be->backend.write = pygit2_refdb_backend_write; } if (PyObject_HasAttrString((PyObject *)self, "rename")) { be->rename = PyObject_GetAttrString((PyObject *)self, "rename"); be->backend.rename = pygit2_refdb_backend_rename; } if (PyObject_HasAttrString((PyObject *)self, "delete")) { be->delete = PyObject_GetAttrString((PyObject *)self, "delete"); be->backend.del = pygit2_refdb_backend_del; } if (PyObject_HasAttrString((PyObject *)self, "compress")) { be->compress = PyObject_GetAttrString((PyObject *)self, "compress"); be->backend.compress = pygit2_refdb_backend_compress; } if (PyObject_HasAttrString((PyObject *)self, "has_log")) { be->has_log = PyObject_GetAttrString((PyObject *)self, "has_log"); be->backend.has_log = pygit2_refdb_backend_has_log; } if (PyObject_HasAttrString((PyObject *)self, "ensure_log")) { be->ensure_log = PyObject_GetAttrString((PyObject *)self, "ensure_log"); be->backend.ensure_log = pygit2_refdb_backend_ensure_log; } /* TODO: First-class reflog support */ be->backend.reflog_read = pygit2_refdb_backend_reflog_read; be->backend.reflog_write = pygit2_refdb_backend_reflog_write; be->backend.reflog_rename = pygit2_refdb_backend_reflog_rename; be->backend.reflog_delete = pygit2_refdb_backend_reflog_delete; /* TODO: transactions if (PyObject_HasAttrString((PyObject *)self, "lock")) { be->lock = PyObject_GetAttrString((PyObject *)self, "lock"); be->backend.lock = pygit2_refdb_backend_lock; } if (PyObject_HasAttrString((PyObject *)self, "unlock")) { be->unlock = PyObject_GetAttrString((PyObject *)self, "unlock"); be->backend.unlock = pygit2_refdb_backend_unlock; } */ Py_INCREF((PyObject *)self); be->backend.free = pygit2_refdb_backend_free; self->refdb_backend = (git_refdb_backend *)be; return 0; } void RefdbBackend_dealloc(RefdbBackend *self) { if (self->refdb_backend && self->refdb_backend->free == pygit2_refdb_backend_free) { struct pygit2_refdb_backend *be = (struct pygit2_refdb_backend *)self->refdb_backend; Py_CLEAR(be->exists); Py_CLEAR(be->lookup); Py_CLEAR(be->iterator); Py_CLEAR(be->write); Py_CLEAR(be->rename); Py_CLEAR(be->delete); Py_CLEAR(be->compress); Py_CLEAR(be->has_log); Py_CLEAR(be->ensure_log); Py_CLEAR(be->reflog_read); Py_CLEAR(be->reflog_write); Py_CLEAR(be->reflog_rename); Py_CLEAR(be->reflog_delete); Py_CLEAR(be->lock); Py_CLEAR(be->unlock); free(be); } Py_TYPE(self)->tp_free((PyObject *) self); } PyDoc_STRVAR(RefdbBackend_exists__doc__, "exists(refname: str) -> bool\n" "\n" "Returns True if a ref by this name exists, or False otherwise."); PyObject * RefdbBackend_exists(RefdbBackend *self, PyObject *py_str) { int err, exists; const char *ref_name; if (self->refdb_backend->exists == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyUnicode_Check(py_str)) { PyErr_SetString(PyExc_TypeError, "RefdbBackend.exists takes a string argument"); return NULL; } ref_name = PyUnicode_AsUTF8(py_str); err = self->refdb_backend->exists(&exists, self->refdb_backend, ref_name); if (err != 0) return Error_set(err); if (exists) Py_RETURN_TRUE; else Py_RETURN_FALSE; } PyDoc_STRVAR(RefdbBackend_lookup__doc__, "lookup(refname: str) -> Reference\n" "\n" "Looks up a reference and returns it, or None if not found."); PyObject * RefdbBackend_lookup(RefdbBackend *self, PyObject *py_str) { int err; git_reference *ref; const char *ref_name; if (self->refdb_backend->lookup == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyUnicode_Check(py_str)) { PyErr_SetString(PyExc_TypeError, "RefdbBackend.lookup takes a string argument"); return NULL; } ref_name = PyUnicode_AsUTF8(py_str); err = self->refdb_backend->lookup(&ref, self->refdb_backend, ref_name); if (err == GIT_ENOTFOUND) { Py_RETURN_NONE; } else if (err != 0) { return Error_set(err); } return wrap_reference(ref, NULL); } PyDoc_STRVAR(RefdbBackend_write__doc__, "write(ref: Reference, force: bool, who: Signature, message: str, old: Oid, old_target: str)\n" "\n" "Writes a new reference to the reference database."); // TODO: Better docs? libgit2 is scant on documentation for this, too. PyObject * RefdbBackend_write(RefdbBackend *self, PyObject *args) { int err; Reference *ref; int force; Signature *who; const git_signature *sig = NULL; char *message, *old_target; PyObject *py_old; git_oid _old, *old = NULL; if (self->refdb_backend->write == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyArg_ParseTuple(args, "O!pOzOz", &ReferenceType, &ref, &force, &who, &message, &py_old, &old_target)) return NULL; if ((PyObject *)py_old != Py_None) { py_oid_to_git_oid(py_old, &_old); old = &_old; } if ((PyObject *)who != Py_None) { if (!PyObject_IsInstance((PyObject *)who, (PyObject *)&SignatureType)) { PyErr_SetString(PyExc_TypeError, "Signature must be type pygit2.Signature"); return NULL; } sig = who->signature; } err = self->refdb_backend->write(self->refdb_backend, ref->reference, force, sig, message, old, old_target); if (err != 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(RefdbBackend_rename__doc__, "rename(old_name: str, new_name: str, force: bool, who: Signature, message: str) -> Reference\n" "\n" "Renames a reference."); PyObject * RefdbBackend_rename(RefdbBackend *self, PyObject *args) { int err; int force; Signature *who; char *old_name, *new_name, *message; git_reference *out; if (self->refdb_backend->rename == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyArg_ParseTuple(args, "sspO!s", &old_name, &new_name, &force, &SignatureType, &who, &message)) return NULL; err = self->refdb_backend->rename(&out, self->refdb_backend, old_name, new_name, force, who->signature, message); if (err != 0) return Error_set(err); return (PyObject *)wrap_reference(out, NULL); } PyDoc_STRVAR(RefdbBackend_delete__doc__, "delete(ref_name: str, old_id: Oid, old_target: str)\n" "\n" "Deletes a reference."); PyObject * RefdbBackend_delete(RefdbBackend *self, PyObject *args) { int err; PyObject *py_old_id; git_oid old_id; char *ref_name, *old_target; if (self->refdb_backend->del == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyArg_ParseTuple(args, "sOz", &ref_name, &py_old_id, &old_target)) return NULL; if (py_old_id != Py_None) { py_oid_to_git_oid(py_old_id, &old_id); err = self->refdb_backend->del(self->refdb_backend, ref_name, &old_id, old_target); } else { err = self->refdb_backend->del(self->refdb_backend, ref_name, NULL, old_target); } if (err != 0) { return Error_set(err); } Py_RETURN_NONE; } PyDoc_STRVAR(RefdbBackend_compress__doc__, "compress()\n" "\n" "Suggests that the implementation compress or optimize its references.\n" "This behavior is implementation-specific."); PyObject * RefdbBackend_compress(RefdbBackend *self) { int err; if (self->refdb_backend->compress == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } err = self->refdb_backend->compress(self->refdb_backend); if (err != 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(RefdbBackend_has_log__doc__, "has_log(ref_name: str) -> bool\n" "\n" "Returns True if a ref log is available for this reference.\n" "It may be empty even if it exists."); PyObject * RefdbBackend_has_log(RefdbBackend *self, PyObject *_ref_name) { int err; const char *ref_name; if (self->refdb_backend->has_log == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyUnicode_Check(_ref_name)) { PyErr_SetString(PyExc_TypeError, "RefdbBackend.has_log takes a string argument"); return NULL; } ref_name = PyUnicode_AsUTF8(_ref_name); err = self->refdb_backend->has_log(self->refdb_backend, ref_name); if (err < 0) { return Error_set(err); } if (err == 1) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } PyDoc_STRVAR(RefdbBackend_ensure_log__doc__, "ensure_log(ref_name: str) -> bool\n" "\n" "Ensure that a particular reference will have a reflog which will be\n" "appended to on writes."); PyObject * RefdbBackend_ensure_log(RefdbBackend *self, PyObject *_ref_name) { int err; const char *ref_name; if (self->refdb_backend->ensure_log == NULL) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (!PyUnicode_Check(_ref_name)) { PyErr_SetString(PyExc_TypeError, "RefdbBackend.ensure_log takes a string argument"); return NULL; } ref_name = PyUnicode_AsUTF8(_ref_name); err = self->refdb_backend->ensure_log(self->refdb_backend, ref_name); if (err < 0) { return Error_set(err); } if (err == 0) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } static PyMethodDef RefdbBackend_methods[] = { METHOD(RefdbBackend, exists, METH_O), METHOD(RefdbBackend, lookup, METH_O), METHOD(RefdbBackend, write, METH_VARARGS), METHOD(RefdbBackend, rename, METH_VARARGS), METHOD(RefdbBackend, delete, METH_VARARGS), METHOD(RefdbBackend, compress, METH_NOARGS), METHOD(RefdbBackend, has_log, METH_O), METHOD(RefdbBackend, ensure_log, METH_O), {NULL} }; PyDoc_STRVAR(RefdbBackend__doc__, "Reference database backend."); PyTypeObject RefdbBackendType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RefdbBackend", /* tp_name */ sizeof(RefdbBackend), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)RefdbBackend_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ RefdbBackend__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0 /* TODO: Wrap git_reference_iterator */, /* tp_iter */ 0, /* tp_iternext */ RefdbBackend_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)RefdbBackend_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_refdb_backend(git_refdb_backend *c_refdb_backend) { RefdbBackend *pygit2_refdb_backend = PyObject_New(RefdbBackend, &RefdbBackendType); if (pygit2_refdb_backend) pygit2_refdb_backend->refdb_backend = c_refdb_backend; return (PyObject *)pygit2_refdb_backend; } PyDoc_STRVAR(RefdbFsBackend__doc__, "RefdbFsBackend(repo: Repository)\n" "\n" "Reference database filesystem backend. The path to the repository\n" "is used as the basis of the reference database."); int RefdbFsBackend_init(RefdbFsBackend *self, PyObject *args, PyObject *kwds) { if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "RefdbFsBackend takes no keyword arguments"); return -1; } Repository *repo = NULL; if (!PyArg_ParseTuple(args, "O!", &RepositoryType, &repo)) return -1; int err = git_refdb_backend_fs(&self->super.refdb_backend, repo->repo); if (err) { Error_set(err); return -1; } return 0; } PyTypeObject RefdbFsBackendType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RefdbFsBackend", /* tp_name */ sizeof(RefdbFsBackend), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ RefdbFsBackend__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ &RefdbBackendType, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)RefdbFsBackend_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/refdb_backend.h000066400000000000000000000026601521317216500203300ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_refdb_backend_h #define INCLUDE_pygit2_refdb_backend_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject *wrap_refdb_backend(git_refdb_backend *c_refdb_backend); #endif libgit2-pygit2-d86662e/src/reference.c000066400000000000000000000555451521317216500175420ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include #include "object.h" #include "error.h" #include "types.h" #include "utils.h" #include "oid.h" #include "signature.h" #include "reference.h" #include extern PyObject *GitError; extern PyTypeObject RefLogEntryType; extern PyTypeObject RepositoryType; extern PyTypeObject SignatureType; extern PyObject *ReferenceTypeEnum; PyTypeObject ReferenceType; void RefLogIter_dealloc(RefLogIter *self) { git_reflog_free(self->reflog); PyObject_Del(self); } PyObject * RefLogIter_iternext(RefLogIter *self) { const git_reflog_entry *entry; const char * entry_message; RefLogEntry *py_entry; int err; if (self->i < self->size) { entry = git_reflog_entry_byindex(self->reflog, self->i); py_entry = PyObject_New(RefLogEntry, &RefLogEntryType); if (py_entry == NULL) return NULL; py_entry->oid_old = git_oid_to_python(git_reflog_entry_id_old(entry)); py_entry->oid_new = git_oid_to_python(git_reflog_entry_id_new(entry)); entry_message = git_reflog_entry_message(entry); py_entry->message = (entry_message != NULL) ? strdup(entry_message) : NULL; err = git_signature_dup(&py_entry->signature, git_reflog_entry_committer(entry)); if (err < 0) return Error_set(err); ++(self->i); return (PyObject*) py_entry; } PyErr_SetNone(PyExc_StopIteration); return NULL; } PyDoc_STRVAR(RefLogIterType__doc__, "Internal reflog iterator object."); PyTypeObject RefLogIterType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RefLogIter", /* tp_name */ sizeof(RefLogIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)RefLogIter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ RefLogIterType__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)RefLogIter_iternext, /* tp_iternext */ }; PyDoc_STRVAR(Reference__doc__, "Reference(name: str, target: str): create a symbolic reference\n" "\n" "Reference(name: str, oid: Oid, peel: Oid): create a direct reference\n" "\n" "'peel' is the first non-tag object's OID, or None.\n" "\n" "The purpose of this constructor is for use in custom refdb backends.\n" "References created with this function are unlikely to work as\n" "expected in other contexts.\n"); static int Reference_init_symbolic(Reference *self, PyObject *args, PyObject *kwds) { const char *name, *target; if (!PyArg_ParseTuple(args, "ss", &name, &target)) { return -1; } self->reference = git_reference__alloc_symbolic(name, target); return 0; } int Reference_init(Reference *self, PyObject *args, PyObject *kwds) { const char *name; git_oid oid, peel; PyObject *py_oid, *py_peel; if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "Reference takes no keyword arguments"); return -1; } Py_ssize_t nargs = PyTuple_Size(args); if (nargs == 2) { return Reference_init_symbolic(self, args, kwds); } else if (nargs != 3) { PyErr_SetString(PyExc_TypeError, "Invalid arguments to Reference constructor"); return -1; } if (!PyArg_ParseTuple(args, "sOO", &name, &py_oid, &py_peel)) { return -1; } py_oid_to_git_oid(py_oid, &oid); if (py_peel != Py_None) { py_oid_to_git_oid(py_peel, &peel); } self->reference = git_reference__alloc(name, &oid, &peel); return 0; } void Reference_dealloc(Reference *self) { Py_CLEAR(self->repo); git_reference_free(self->reference); PyObject_Del(self); } PyDoc_STRVAR(Reference_delete__doc__, "delete()\n" "\n" "Delete this reference. It will no longer be valid!"); PyObject * Reference_delete(Reference *self, PyObject *args) { int err; CHECK_REFERENCE(self); /* Delete the reference */ err = git_reference_delete(self->reference); if (err < 0) return Error_set(err); git_reference_free(self->reference); self->reference = NULL; /* Invalidate the pointer */ Py_RETURN_NONE; } PyDoc_STRVAR(Reference_rename__doc__, "rename(new_name: str)\n" "\n" "Rename the reference."); PyObject * Reference_rename(Reference *self, PyObject *py_name) { CHECK_REFERENCE(self); // Get the C name PyObject *tvalue; char *c_name = pgit_borrow_fsdefault(py_name, &tvalue); if (c_name == NULL) return NULL; // Rename git_reference *new_reference; int err = git_reference_rename(&new_reference, self->reference, c_name, 0, NULL); Py_DECREF(tvalue); if (err) return Error_set(err); // Update reference git_reference_free(self->reference); self->reference = new_reference; Py_RETURN_NONE; } PyDoc_STRVAR(Reference_resolve__doc__, "resolve() -> Reference\n" "\n" "Resolve a symbolic reference and return a direct reference."); PyObject * Reference_resolve(Reference *self, PyObject *args) { git_reference *c_reference; int err; CHECK_REFERENCE(self); /* Direct: return myself */ if (git_reference_type(self->reference) == GIT_REF_OID) { Py_INCREF(self); return (PyObject *)self; } /* Symbolic: resolve */ err = git_reference_resolve(&c_reference, self->reference); if (err < 0) return Error_set(err); return wrap_reference(c_reference, self->repo); } static PyObject * Reference_target_impl(Reference *self, const char ** c_name) { CHECK_REFERENCE(self); /* Case 1: Direct */ if (GIT_REF_OID == git_reference_type(self->reference)) return git_oid_to_python(git_reference_target(self->reference)); /* Case 2: Symbolic */ *c_name = git_reference_symbolic_target(self->reference); if (*c_name == NULL) PyErr_SetString(PyExc_ValueError, "no target available"); return NULL; } PyDoc_STRVAR(Reference_target__doc__, "The reference target: If direct the value will be an Oid object, if it\n" "is symbolic it will be an string with the full name of the target\n" "reference.\n"); PyObject * Reference_target__get__(Reference *self) { const char * c_name = NULL; PyObject * ret; ret = Reference_target_impl(self, &c_name); if (ret != NULL) return ret; if (c_name != NULL) return PyUnicode_DecodeFSDefault(c_name); return NULL; } PyDoc_STRVAR(Reference_raw_target__doc__, "The raw reference target: If direct the value will be an Oid object, if it\n" "is symbolic it will be bytes with the full name of the target\n" "reference.\n"); PyObject * Reference_raw_target__get__(Reference *self) { const char * c_name = NULL; PyObject * ret; ret = Reference_target_impl(self, &c_name); if (ret != NULL) return ret; if (c_name != NULL) return PyBytes_FromString(c_name); return NULL; } PyDoc_STRVAR(Reference_set_target__doc__, "set_target(target[, message: str])\n" "\n" "Set the target of this reference. Creates a new entry in the reflog.\n" "\n" "Parameters:\n" "\n" "target\n" " The new target for this reference\n" "\n" "message\n" " Message to use for the reflog.\n"); PyObject * Reference_set_target(Reference *self, PyObject *args, PyObject *kwds) { git_oid oid; int err; git_reference *new_ref; PyObject *py_target = NULL; const char *message = NULL; char *keywords[] = {"target", "message", NULL}; CHECK_REFERENCE(self); if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s", keywords, &py_target, &message)) return NULL; /* Case 1: Direct */ if (GIT_REF_OID == git_reference_type(self->reference)) { err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid); if (err < 0) goto error; err = git_reference_set_target(&new_ref, self->reference, &oid, message); if (err < 0) goto error; git_reference_free(self->reference); self->reference = new_ref; Py_RETURN_NONE; } /* Case 2: Symbolic */ PyObject *tvalue; char *c_name = pgit_borrow_fsdefault(py_target, &tvalue); if (c_name == NULL) return NULL; err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, message); Py_DECREF(tvalue); if (err < 0) goto error; git_reference_free(self->reference); self->reference = new_ref; Py_RETURN_NONE; error: Error_set(err); return NULL; } PyDoc_STRVAR(Reference_name__doc__, "The full name of the reference."); PyObject * Reference_name__get__(Reference *self) { CHECK_REFERENCE(self); return PyUnicode_DecodeFSDefault(git_reference_name(self->reference)); } PyDoc_STRVAR(Reference_raw_name__doc__, "The full name of the reference (Bytes)."); PyObject * Reference_raw_name__get__(Reference *self) { CHECK_REFERENCE(self); return PyBytes_FromString(git_reference_name(self->reference)); } PyDoc_STRVAR(Reference_shorthand__doc__, "The shorthand \"human-readable\" name of the reference."); PyObject * Reference_shorthand__get__(Reference *self) { CHECK_REFERENCE(self); return PyUnicode_DecodeFSDefault(git_reference_shorthand(self->reference)); } PyDoc_STRVAR(Reference_raw_shorthand__doc__, "The shorthand \"human-readable\" name of the reference (Bytes)."); PyObject * Reference_raw_shorthand__get__(Reference *self) { CHECK_REFERENCE(self); return PyBytes_FromString(git_reference_shorthand(self->reference)); } PyDoc_STRVAR(Reference_type__doc__, "An enums.ReferenceType constant (either OID or SYMBOLIC)."); PyObject * Reference_type__get__(Reference *self) { git_reference_t c_type; CHECK_REFERENCE(self); c_type = git_reference_type(self->reference); return pygit2_enum(ReferenceTypeEnum, c_type); } PyDoc_STRVAR(Reference__pointer__doc__, "Get the reference's pointer. For internal use only."); PyObject * Reference__pointer__get__(Reference *self) { /* Bytes means a raw buffer */ return PyBytes_FromStringAndSize((char *) &self->reference, sizeof(git_reference *)); } PyDoc_STRVAR(Reference_log__doc__, "log() -> RefLogIter\n" "\n" "Retrieves the current reference log."); PyObject * Reference_log(Reference *self) { int err; RefLogIter *iter; git_repository *repo; CHECK_REFERENCE(self); repo = git_reference_owner(self->reference); iter = PyObject_New(RefLogIter, &RefLogIterType); if (iter != NULL) { err = git_reflog_read(&iter->reflog, repo, git_reference_name(self->reference)); if (err < 0) return Error_set(err); iter->size = git_reflog_entrycount(iter->reflog); iter->i = 0; } return (PyObject*)iter; } PyDoc_STRVAR(Reference_peel__doc__, "peel(type=None) -> Object\n" "\n" "Retrieve an object of the given type by recursive peeling.\n" "\n" "If no type is provided, the first non-tag object will be returned."); PyObject * Reference_peel(Reference *self, PyObject *args) { int err; git_otype otype; git_object *obj; PyObject *py_type = Py_None; CHECK_REFERENCE(self); if (!PyArg_ParseTuple(args, "|O", &py_type)) return NULL; otype = py_object_to_otype(py_type); if (otype == GIT_OBJECT_INVALID) return NULL; err = git_reference_peel(&obj, self->reference, otype); if (err < 0) return Error_set(err); return wrap_object(obj, self->repo, NULL); } PyObject * Reference_richcompare(PyObject *o1, PyObject *o2, int op) { PyObject *res; Reference *obj1; Reference *obj2; const char *name1; const char *name2; if (!PyObject_TypeCheck(o2, &ReferenceType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } switch (op) { case Py_NE: obj1 = (Reference *) o1; obj2 = (Reference *) o2; CHECK_REFERENCE(obj1); CHECK_REFERENCE(obj2); name1 = git_reference_name(obj1->reference); name2 = git_reference_name(obj2->reference); if (strcmp(name1, name2) != 0) { res = Py_True; break; } res = Py_False; break; case Py_EQ: obj1 = (Reference *) o1; obj2 = (Reference *) o2; CHECK_REFERENCE(obj1); CHECK_REFERENCE(obj2); name1 = git_reference_name(obj1->reference); name2 = git_reference_name(obj2->reference); if (strcmp(name1, name2) != 0) { res = Py_False; break; } res = Py_True; break; case Py_LT: case Py_LE: case Py_GT: case Py_GE: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; default: PyErr_Format(PyExc_RuntimeError, "Unexpected '%d' op", op); return NULL; } Py_INCREF(res); return res; } PyDoc_STRVAR(RefLogEntry_committer__doc__, "Committer."); PyObject * RefLogEntry_committer__get__(RefLogEntry *self) { return build_signature((Object*) self, self->signature, "utf-8"); } static int RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds) { self->oid_old = NULL; self->oid_new = NULL; self->message = NULL; self->signature = NULL; return 0; } static void RefLogEntry_dealloc(RefLogEntry *self) { Py_CLEAR(self->oid_old); Py_CLEAR(self->oid_new); free(self->message); git_signature_free(self->signature); PyObject_Del(self); } PyMemberDef RefLogEntry_members[] = { MEMBER(RefLogEntry, oid_new, T_OBJECT, "New oid."), MEMBER(RefLogEntry, oid_old, T_OBJECT, "Old oid."), MEMBER(RefLogEntry, message, T_STRING, "Message."), {NULL} }; PyGetSetDef RefLogEntry_getseters[] = { GETTER(RefLogEntry, committer), {NULL} }; PyDoc_STRVAR(RefLogEntry__doc__, "Reference log object."); PyTypeObject RefLogEntryType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RefLogEntry", /* tp_name */ sizeof(RefLogEntry), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)RefLogEntry_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ RefLogEntry__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ RefLogEntry_members, /* tp_members */ RefLogEntry_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)RefLogEntry_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; static PyMethodDef Reference_methods[] = { METHOD(Reference, delete, METH_NOARGS), METHOD(Reference, rename, METH_O), METHOD(Reference, resolve, METH_NOARGS), METHOD(Reference, log, METH_NOARGS), METHOD(Reference, set_target, METH_VARARGS | METH_KEYWORDS), METHOD(Reference, peel, METH_VARARGS), {NULL} }; PyGetSetDef Reference_getseters[] = { GETTER(Reference, name), GETTER(Reference, raw_name), GETTER(Reference, shorthand), GETTER(Reference, raw_shorthand), GETTER(Reference, target), GETTER(Reference, raw_target), GETTER(Reference, type), GETTER(Reference, _pointer), {NULL} }; PyTypeObject ReferenceType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Reference", /* tp_name */ sizeof(Reference), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Reference_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Reference__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ Reference_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Reference_methods, /* tp_methods */ 0, /* tp_members */ Reference_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Reference_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_reference(git_reference * c_reference, Repository *repo) { Reference *py_reference=NULL; py_reference = PyObject_New(Reference, &ReferenceType); if (py_reference) { py_reference->reference = c_reference; py_reference->repo = repo; if (repo) { Py_INCREF(repo); } } return (PyObject *)py_reference; } libgit2-pygit2-d86662e/src/reference.h000066400000000000000000000031261521317216500175330ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_reference_h #define INCLUDE_pygit2_reference_h #define PY_SSIZE_T_CLEAN #include #include PyObject* Reference_delete(Reference *self, PyObject *args); PyObject* Reference_rename(Reference *self, PyObject *py_name); PyObject* Reference_resolve(Reference *self, PyObject *args); PyObject* wrap_reference(git_reference *c_reference, Repository *repo); #endif libgit2-pygit2-d86662e/src/repository.c000066400000000000000000002207651521317216500200210ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "types.h" #include "reference.h" #include "revspec.h" #include "utils.h" #include "odb.h" #include "object.h" #include "oid.h" #include "note.h" #include "refdb.h" #include "repository.h" #include "diff.h" #include "branch.h" #include "signature.h" #include "worktree.h" #include #include // TODO: remove this function when Python 3.13 becomes the minimum supported version #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 13 static inline PyObject * PyList_GetItemRef(PyObject *op, Py_ssize_t index) { PyObject *item = PyList_GetItem(op, index); Py_XINCREF(item); return item; } #endif extern PyObject *GitError; extern PyTypeObject IndexType; extern PyTypeObject WalkerType; extern PyTypeObject SignatureType; extern PyTypeObject ObjectType; extern PyTypeObject OidType; extern PyTypeObject CommitType; extern PyTypeObject TreeType; extern PyTypeObject TreeBuilderType; extern PyTypeObject ConfigType; extern PyTypeObject DiffType; extern PyTypeObject ReferenceType; extern PyTypeObject RevSpecType; extern PyTypeObject NoteType; extern PyTypeObject NoteIterType; extern PyTypeObject StashType; extern PyTypeObject RefsIteratorType; extern PyObject *FileStatusEnum; extern PyObject *MergeAnalysisEnum; extern PyObject *MergePreferenceEnum; /* forward-declaration for Repository._from_c() */ PyTypeObject RepositoryType; PyObject * wrap_repository(git_repository *c_repo) { Repository *py_repo = PyObject_GC_New(Repository, &RepositoryType); if (py_repo) { py_repo->repo = c_repo; py_repo->config = NULL; py_repo->index = NULL; py_repo->owned = 1; } return (PyObject *)py_repo; } int Repository_init(Repository *self, PyObject *args, PyObject *kwds) { PyObject *backend = NULL; if (kwds && PyDict_Size(kwds) > 0) { PyErr_SetString(PyExc_TypeError, "Repository takes no keyword arguments"); return -1; } if (!PyArg_ParseTuple(args, "|O", &backend)) { return -1; } if (backend == NULL) { /* Create repository without odb/refdb */ int err = git_repository_new(&self->repo); if (err != 0) { Error_set(err); return -1; } self->owned = 1; self->config = NULL; self->index = NULL; return 0; } self->repo = PyCapsule_GetPointer(backend, "backend"); if (self->repo == NULL) { PyErr_SetString(PyExc_TypeError, "Repository unable to unpack backend."); return -1; } self->owned = 1; self->config = NULL; self->index = NULL; return 0; } PyDoc_STRVAR(Repository__from_c__doc__, "Init a Repository from a pointer. For internal use only."); PyObject * Repository__from_c(Repository *py_repo, PyObject *args) { PyObject *py_pointer, *py_free; char *buffer; Py_ssize_t len; int err; py_repo->repo = NULL; py_repo->config = NULL; py_repo->index = NULL; if (!PyArg_ParseTuple(args, "OO!", &py_pointer, &PyBool_Type, &py_free)) return NULL; err = PyBytes_AsStringAndSize(py_pointer, &buffer, &len); if (err < 0) return NULL; if (len != sizeof(git_repository *)) { PyErr_SetString(PyExc_TypeError, "invalid pointer length"); return NULL; } py_repo->repo = *((git_repository **) buffer); py_repo->owned = py_free == Py_True; Py_RETURN_NONE; } PyDoc_STRVAR(Repository__disown__doc__, "Mark the object as not-owned by us. For internal use only."); PyObject * Repository__disown(Repository *py_repo) { py_repo->owned = 0; Py_RETURN_NONE; } void Repository_dealloc(Repository *self) { PyObject_GC_UnTrack(self); Py_CLEAR(self->index); Py_CLEAR(self->config); if (self->owned) git_repository_free(self->repo); Py_TYPE(self)->tp_free(self); } int Repository_traverse(Repository *self, visitproc visit, void *arg) { Py_VISIT(self->index); return 0; } int Repository_clear(Repository *self) { Py_CLEAR(self->index); return 0; } PyDoc_STRVAR(Repository_head__doc__, "Current head reference of the repository."); PyObject * Repository_head__get__(Repository *self) { git_reference *head; int err; err = git_repository_head(&head, self->repo); if (err < 0) { if (err == GIT_ENOTFOUND) PyErr_SetString(GitError, "head reference does not exist"); else Error_set(err); return NULL; } return wrap_reference(head, self); } PyDoc_STRVAR(Repository_head_is_detached__doc__, "A repository's HEAD is detached when it points directly to a commit\n" "instead of a branch."); PyObject * Repository_head_is_detached__get__(Repository *self) { if (git_repository_head_detached(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Repository_head_is_unborn__doc__, "An unborn branch is one named from HEAD but which doesn't exist in the\n" "refs namespace, because it doesn't have any commit to point to."); PyObject * Repository_head_is_unborn__get__(Repository *self) { if (git_repository_head_unborn(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Repository_is_empty__doc__, "Check if a repository is empty."); PyObject * Repository_is_empty__get__(Repository *self) { if (git_repository_is_empty(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Repository_is_bare__doc__, "Check if a repository is a bare repository."); PyObject * Repository_is_bare__get__(Repository *self) { if (git_repository_is_bare(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Repository_is_shallow__doc__, "Check if a repository is a shallow repository."); PyObject * Repository_is_shallow__get__(Repository *self) { if (git_repository_is_shallow(self->repo) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Repository_git_object_lookup_prefix__doc__, "git_object_lookup_prefix(oid: Oid) -> Object\n" "\n" "Returns the Git object with the given oid."); PyObject * Repository_git_object_lookup_prefix(Repository *self, PyObject *key) { int err; size_t len; git_oid oid; git_object *obj; len = py_oid_to_git_oid(key, &oid); if (len == 0) return NULL; err = git_object_lookup_prefix(&obj, self->repo, &oid, len, GIT_OBJECT_ANY); if (err == 0) return wrap_object(obj, self, NULL); if (err == GIT_ENOTFOUND) Py_RETURN_NONE; return Error_set_oid(err, &oid, len); } PyDoc_STRVAR(Repository_lookup_branch__doc__, "lookup_branch(branch_name: str, branch_type: BranchType = BranchType.LOCAL) -> Branch\n" "\n" "Returns the Git reference for the given branch name (local or remote).\n" "If branch_type is BranchType.REMOTE, you must include the remote name\n" "in the branch name (eg 'origin/master')."); PyObject * Repository_lookup_branch(Repository *self, PyObject *args) { git_reference *c_reference; const char *c_name; Py_ssize_t c_name_len; git_branch_t branch_type = GIT_BRANCH_LOCAL; int err; if (!PyArg_ParseTuple(args, "s#|I", &c_name, &c_name_len, &branch_type)) return NULL; err = git_branch_lookup(&c_reference, self->repo, c_name, branch_type); if (err == 0) return wrap_branch(c_reference, self); if (err == GIT_ENOTFOUND) Py_RETURN_NONE; return Error_set(err); } PyDoc_STRVAR(Repository_path_is_ignored__doc__, "path_is_ignored(path: str) -> bool\n" "\n" "Check if a path is ignored in the repository."); PyObject * Repository_path_is_ignored(Repository *self, PyObject *args) { int ignored; char *path; if (!PyArg_ParseTuple(args, "s", &path)) return NULL; git_ignore_path_is_ignored(&ignored, self->repo, path); if (ignored == 1) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Repository_revparse_single__doc__, "revparse_single(revision: str) -> Object\n" "\n" "Find an object, as specified by a revision string. See\n" "`man gitrevisions`, or the documentation for `git rev-parse` for\n" "information on the syntax accepted."); PyObject * Repository_revparse_single(Repository *self, PyObject *py_spec) { /* Get the C revision spec */ const char *c_spec = pgit_borrow(py_spec); if (c_spec == NULL) return NULL; /* Lookup */ git_object *c_obj; int err = git_revparse_single(&c_obj, self->repo, c_spec); if (err) return Error_set_str(err, c_spec); return wrap_object(c_obj, self, NULL); } PyDoc_STRVAR(Repository_revparse_ext__doc__, "revparse_ext(revision: str) -> tuple[Object, Reference]\n" "\n" "Find a single object and intermediate reference, as specified by a revision\n" "string. See `man gitrevisions`, or the documentation for `git rev-parse`\n" "for information on the syntax accepted.\n" "\n" "In some cases (@{<-n>} or @{upstream}), the expression may\n" "point to an intermediate reference, which is returned in the second element\n" "of the result tuple."); PyObject * Repository_revparse_ext(Repository *self, PyObject *py_spec) { /* Get the C revision spec */ const char *c_spec = pgit_borrow(py_spec); if (c_spec == NULL) return NULL; /* Lookup */ git_object *c_obj = NULL; git_reference *c_ref = NULL; int err = git_revparse_ext(&c_obj, &c_ref, self->repo, c_spec); if (err) return Error_set_str(err, c_spec); PyObject *py_obj = wrap_object(c_obj, self, NULL); PyObject *py_ref = NULL; if (c_ref != NULL) { py_ref = wrap_reference(c_ref, self); } else { py_ref = Py_None; Py_INCREF(Py_None); } return Py_BuildValue("NN", py_obj, py_ref); } PyDoc_STRVAR(Repository_revparse__doc__, "revparse(revspec: str) -> RevSpec\n" "\n" "Parse a revision string for from, to, and intent. See `man gitrevisions`,\n" "or the documentation for `git rev-parse` for information on the syntax\n" "accepted."); PyObject * Repository_revparse(Repository *self, PyObject *py_spec) { /* Get the C revision spec */ const char *c_spec = pgit_borrow(py_spec); if (c_spec == NULL) return NULL; /* Lookup */ git_revspec revspec; int err = git_revparse(&revspec, self->repo, c_spec); if (err) { return Error_set_str(err, c_spec); } return wrap_revspec(&revspec, self); } PyDoc_STRVAR(Repository_path__doc__, "The normalized path to the git repository."); PyObject * Repository_path__get__(Repository *self, void *closure) { const char *c_path; if (self->repo == NULL) Py_RETURN_NONE; c_path = git_repository_path(self->repo); if (c_path == NULL) Py_RETURN_NONE; return PyUnicode_DecodeFSDefault(c_path); } PyDoc_STRVAR(Repository_workdir__doc__, "The normalized path to the working directory of the repository. If the\n" "repository is bare, None will be returned."); PyObject * Repository_workdir__get__(Repository *self, void *closure) { const char *c_path; c_path = git_repository_workdir(self->repo); if (c_path == NULL) Py_RETURN_NONE; return PyUnicode_DecodeFSDefault(c_path); } int Repository_workdir__set__(Repository *self, PyObject *py_workdir) { const char *workdir = pgit_borrow(py_workdir); if (workdir == NULL) return -1; int err = git_repository_set_workdir(self->repo, workdir, 0 /* update_gitlink */); if (err) { Error_set_str(err, workdir); return -1; } return 0; } PyDoc_STRVAR(Repository_descendant_of__doc__, "descendant_of(oid1: Oid, oid2: Oid) -> bool\n" "\n" "Determine if the first commit is a descendant of the second commit.\n" "Note that a commit is not considered a descendant of itself."); PyObject * Repository_descendant_of(Repository *self, PyObject *args) { PyObject *value1; PyObject *value2; git_oid oid1; git_oid oid2; int err; if (!PyArg_ParseTuple(args, "OO", &value1, &value2)) return NULL; err = py_oid_to_git_oid_expand(self->repo, value1, &oid1); if (err < 0) return NULL; err = py_oid_to_git_oid_expand(self->repo, value2, &oid2); if (err < 0) return NULL; // err < 0 => error, see source code of `git_graph_descendant_of` err = git_graph_descendant_of(self->repo, &oid1, &oid2); if (err < 0) return Error_set(err); return PyBool_FromLong(err); } PyDoc_STRVAR(Repository_merge_base__doc__, "merge_base(oid1: Oid, oid2: Oid) -> Oid\n" "\n" "Find as good common ancestors as possible for a merge.\n" "Returns None if there is no merge base between the commits"); PyObject * Repository_merge_base(Repository *self, PyObject *args) { PyObject *value1; PyObject *value2; git_oid oid; git_oid oid1; git_oid oid2; int err; if (!PyArg_ParseTuple(args, "OO", &value1, &value2)) return NULL; err = py_oid_to_git_oid_expand(self->repo, value1, &oid1); if (err < 0) return NULL; err = py_oid_to_git_oid_expand(self->repo, value2, &oid2); if (err < 0) return NULL; err = git_merge_base(&oid, self->repo, &oid1, &oid2); if (err == GIT_ENOTFOUND) Py_RETURN_NONE; if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } typedef int (*git_merge_base_xxx_t)(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); static PyObject * merge_base_xxx(Repository *self, PyObject *args, git_merge_base_xxx_t git_merge_base_xxx) { PyObject *py_result = NULL; PyObject *py_commit_oid; PyObject *py_commit_oids; git_oid oid; int commit_oid_count; git_oid *commit_oids = NULL; int i = 0; int err; if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &py_commit_oids)) return NULL; commit_oid_count = (int)PyList_Size(py_commit_oids); commit_oids = malloc(commit_oid_count * sizeof(git_oid)); if (commit_oids == NULL) { PyErr_SetNone(PyExc_MemoryError); goto out; } for (; i < commit_oid_count; i++) { py_commit_oid = PyList_GetItemRef(py_commit_oids, i); if (py_commit_oid == NULL) goto out; err = py_oid_to_git_oid_expand(self->repo, py_commit_oid, &commit_oids[i]); Py_DECREF(py_commit_oid); if (err < 0) goto out; } err = (*git_merge_base_xxx)(&oid, self->repo, commit_oid_count, (const git_oid*)commit_oids); if (err == GIT_ENOTFOUND) { Py_INCREF(Py_None); py_result = Py_None; goto out; } if (err < 0) { py_result = Error_set(err); goto out; } py_result = git_oid_to_python(&oid); out: free(commit_oids); return py_result; } PyDoc_STRVAR(Repository_merge_base_many__doc__, "merge_base_many(oids: list[Oid]) -> Oid\n" "\n" "Find as good common ancestors as possible for an n-way merge.\n" "Returns None if there is no merge base between the commits"); PyObject * Repository_merge_base_many(Repository *self, PyObject *args) { return merge_base_xxx(self, args, &git_merge_base_many); } PyDoc_STRVAR(Repository_merge_base_octopus__doc__, "merge_base_octopus(oids: list[Oid]) -> Oid\n" "\n" "Find as good common ancestors as possible for an n-way octopus merge.\n" "Returns None if there is no merge base between the commits"); PyObject * Repository_merge_base_octopus(Repository *self, PyObject *args) { return merge_base_xxx(self, args, &git_merge_base_octopus); } PyDoc_STRVAR(Repository_merge_analysis__doc__, "merge_analysis(their_head: Oid, our_ref: str = \"HEAD\") -> tuple[MergeAnalysis, MergePreference]\n" "\n" "Analyzes the given branch and determines the opportunities for\n" "merging it into a reference (defaults to HEAD).\n" "\n" "Parameters:\n" "\n" "our_ref\n" " The reference name (String) to perform the analysis from\n" "\n" "their_head\n" " Head (commit Oid) to merge into\n" "\n" "The first returned value is a mixture of the MergeAnalysis.NONE, NORMAL,\n" "UP_TO_DATE, FASTFORWARD and UNBORN flags.\n" "The second value is the user's preference from 'merge.ff'"); PyObject * Repository_merge_analysis(Repository *self, PyObject *args) { char *our_ref_name = "HEAD"; PyObject *py_their_head; PyObject *py_result = NULL; git_oid head_id; git_reference *our_ref; git_annotated_commit *commit; git_merge_analysis_t analysis; git_merge_preference_t preference; int err = 0; if (!PyArg_ParseTuple(args, "O|z", &py_their_head, &our_ref_name)) return NULL; err = git_reference_lookup(&our_ref, self->repo, our_ref_name); if (err < 0) { PyObject *py_err = Error_set_str(err, our_ref_name); return py_err; } err = py_oid_to_git_oid_expand(self->repo, py_their_head, &head_id); if (err < 0) goto out; err = git_annotated_commit_lookup(&commit, self->repo, &head_id); if (err < 0) { py_result = Error_set(err); goto out; } err = git_merge_analysis_for_ref(&analysis, &preference, self->repo, our_ref, (const git_annotated_commit **) &commit, 1); git_annotated_commit_free(commit); if (err < 0) { py_result = Error_set(err); goto out; } // Convert analysis to MergeAnalysis enum PyObject *analysis_enum = pygit2_enum(MergeAnalysisEnum, analysis); if (!analysis_enum) { py_result = NULL; goto out; } // Convert preference to MergePreference enum PyObject *preference_enum = pygit2_enum(MergePreferenceEnum, preference); if (!preference_enum) { py_result = NULL; Py_DECREF(analysis_enum); goto out; } py_result = Py_BuildValue("(OO)", analysis_enum, preference_enum); out: git_reference_free(our_ref); return py_result; } PyDoc_STRVAR(Repository_cherrypick__doc__, "cherrypick(id: Oid)\n" "\n" "Cherry-pick the given oid, producing changes in the index and working directory.\n" "\n" "Merges the given commit into HEAD as a cherrypick, writing the results into the\n" "working directory. Any changes are staged for commit and any conflicts\n" "are written to the index. Callers should inspect the repository's\n" "index after this completes, resolve any conflicts and prepare a\n" "commit."); PyObject * Repository_cherrypick(Repository *self, PyObject *py_oid) { git_commit *commit; git_oid oid; int err; size_t len; git_cherrypick_options cherrypick_opts = GIT_CHERRYPICK_OPTIONS_INIT; len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) return NULL; err = git_commit_lookup(&commit, self->repo, &oid); if (err < 0) return Error_set(err); cherrypick_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; err = git_cherrypick(self->repo, commit, (const git_cherrypick_options *)&cherrypick_opts); git_commit_free(commit); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Repository_walk__doc__, "walk(oid: Oid | None, sort_mode: enums.SortMode = enums.SortMode.NONE) -> Walker\n" "\n" "Start traversing the history from the given commit.\n" "The following SortMode values can be used to control the walk:\n" "\n" "* NONE. Sort the output with the same default method from\n" " `git`: reverse chronological order. This is the default sorting for\n" " new walkers.\n" "* TOPOLOGICAL. Sort the repository contents in topological order\n" " (no parents before all of its children are shown); this sorting mode\n" " can be combined with time sorting to produce `git`'s `--date-order``.\n" "* TIME. Sort the repository contents by commit time; this sorting\n" " mode can be combined with topological sorting.\n" "* REVERSE. Iterate through the repository contents in reverse\n" " order; this sorting mode can be combined with any of the above.\n" "\n" "Example:\n" "\n" " >>> from pygit2 import Repository\n" " >>> from pygit2.enums import SortMode\n" " >>> repo = Repository('.git')\n" " >>> for commit in repo.walk(repo.head.target, SortMode.TOPOLOGICAL):\n" " ... print(commit.message)\n" " >>> for commit in repo.walk(repo.head.target, SortMode.TOPOLOGICAL | SortMode.REVERSE):\n" " ... print(commit.message)\n" " >>>\n"); PyObject * Repository_walk(Repository *self, PyObject *args) { PyObject *value; unsigned int sort = GIT_SORT_NONE; int err; git_oid oid; git_revwalk *walk; Walker *py_walker; if (!PyArg_ParseTuple(args, "O|I", &value, &sort)) return NULL; err = git_revwalk_new(&walk, self->repo); if (err < 0) return Error_set(err); /* Sort */ git_revwalk_sorting(walk, sort); /* Push */ if (value != Py_None) { err = py_oid_to_git_oid_expand(self->repo, value, &oid); if (err < 0) goto error; err = git_revwalk_push(walk, &oid); if (err < 0) { Error_set(err); goto error; } } py_walker = PyObject_New(Walker, &WalkerType); if (py_walker) { Py_INCREF(self); py_walker->repo = self; py_walker->walk = walk; return (PyObject*)py_walker; } error: git_revwalk_free(walk); return NULL; } PyDoc_STRVAR(Repository_create_blob__doc__, "create_blob(data: bytes) -> Oid\n" "\n" "Create a new blob from a bytes string. The blob is added to the Git\n" "object database. Returns the oid of the blob."); PyObject * Repository_create_blob(Repository *self, PyObject *args) { git_oid oid; const char *raw; Py_ssize_t size; int err; if (!PyArg_ParseTuple(args, "s#", &raw, &size)) return NULL; err = git_blob_create_frombuffer(&oid, self->repo, (const void*)raw, size); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(Repository_create_blob_fromworkdir__doc__, "create_blob_fromworkdir(path: str) -> Oid\n" "\n" "Create a new blob from a file within the working directory. The given\n" "path must be relative to the working directory, if it is not an error\n" "is raised."); PyObject * Repository_create_blob_fromworkdir(Repository *self, PyObject *value) { PyObject *tvalue; char *path = pgit_borrow_fsdefault(value, &tvalue); if (path == NULL) return NULL; git_oid oid; int err = git_blob_create_fromworkdir(&oid, self->repo, path); Py_DECREF(tvalue); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(Repository_create_blob_fromdisk__doc__, "create_blob_fromdisk(path: str) -> Oid\n" "\n" "Create a new blob from a file anywhere (no working directory check)."); PyObject * Repository_create_blob_fromdisk(Repository *self, PyObject *value) { PyObject *tvalue; char *path = pgit_borrow_fsdefault(value, &tvalue); if (path == NULL) return NULL; git_oid oid; int err = git_blob_create_fromdisk(&oid, self->repo, path); Py_DECREF(tvalue); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } #define BUFSIZE 4096 PyDoc_STRVAR(Repository_create_blob_fromiobase__doc__, "create_blob_fromiobase(io.IOBase) -> Oid\n" "\n" "Create a new blob from an IOBase object."); PyObject * Repository_create_blob_fromiobase(Repository *self, PyObject *py_file) { git_writestream *stream; git_oid oid; PyObject *py_is_readable; int is_readable; int err; py_is_readable = PyObject_CallMethod(py_file, "readable", NULL); if (!py_is_readable) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_SetObject(PyExc_TypeError, py_file); return NULL; } is_readable = PyObject_IsTrue(py_is_readable); Py_DECREF(py_is_readable); if (!is_readable) { Py_DECREF(py_file); PyErr_SetString(PyExc_TypeError, "expected readable IO type"); return NULL; } err = git_blob_create_fromstream(&stream, self->repo, NULL); if (err < 0) return Error_set(err); for (;;) { PyObject *py_bytes; char *bytes; Py_ssize_t size; py_bytes = PyObject_CallMethod(py_file, "read", "i", 4096); if (!py_bytes) return NULL; if (py_bytes == Py_None) { Py_DECREF(py_bytes); goto cleanup; } if (PyBytes_AsStringAndSize(py_bytes, &bytes, &size)) { Py_DECREF(py_bytes); return NULL; } if (size == 0) { Py_DECREF(py_bytes); break; } err = stream->write(stream, bytes, size); Py_DECREF(py_bytes); if (err < 0) goto cleanup; } cleanup: if (err < 0) { stream->free(stream); return Error_set(err); } err = git_blob_create_fromstream_commit(&oid, stream); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(Repository_create_commit__doc__, "create_commit(reference_name: str, author: Signature, committer: Signature, message: bytes | str, tree: Oid, parents: list[Oid][, encoding: str]) -> Oid\n" "\n" "Create a new commit object, return its oid."); PyObject * Repository_create_commit(Repository *self, PyObject *args) { Signature *py_author, *py_committer; PyObject *py_oid, *py_message, *py_parents, *py_parent; PyObject *py_result = NULL; char *update_ref = NULL; char *encoding = NULL; git_oid oid; git_tree *tree = NULL; int parent_count; git_commit **parents = NULL; int i = 0; if (!PyArg_ParseTuple(args, "zO!O!OOO!|s", &update_ref, &SignatureType, &py_author, &SignatureType, &py_committer, &py_message, &py_oid, &PyList_Type, &py_parents, &encoding)) return NULL; size_t len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) return NULL; PyObject *tmessage; const char *message = pgit_borrow_encoding(py_message, encoding, NULL, &tmessage); if (message == NULL) return NULL; int err = git_tree_lookup_prefix(&tree, self->repo, &oid, len); if (err < 0) { Error_set(err); goto out; } parent_count = (int)PyList_Size(py_parents); parents = malloc(parent_count * sizeof(git_commit*)); if (parents == NULL) { PyErr_SetNone(PyExc_MemoryError); goto out; } for (; i < parent_count; i++) { py_parent = PyList_GetItemRef(py_parents, i); if (py_parent == NULL) goto out; len = py_oid_to_git_oid(py_parent, &oid); Py_DECREF(py_parent); if (len == 0) goto out; err = git_commit_lookup_prefix(&parents[i], self->repo, &oid, len); if (err < 0) { Error_set(err); goto out; } } err = git_commit_create(&oid, self->repo, update_ref, py_author->signature, py_committer->signature, encoding, message, tree, parent_count, (const git_commit **)parents); if (err < 0) { Error_set(err); goto out; } py_result = git_oid_to_python(&oid); out: Py_DECREF(tmessage); git_tree_free(tree); while (i > 0) { i--; git_commit_free(parents[i]); } free(parents); return py_result; } PyDoc_STRVAR(Repository_create_commit_string__doc__, "create_commit_string(author: Signature, committer: Signature, message: bytes | str, tree: Oid, parents: list[Oid][, encoding: str]) -> str\n" "\n" "Create a new commit but return it as a string."); PyObject * Repository_create_commit_string(Repository *self, PyObject *args) { Signature *py_author, *py_committer; PyObject *py_oid, *py_message, *py_parents, *py_parent; PyObject *str = NULL; char *encoding = NULL; git_oid oid; git_tree *tree = NULL; int parent_count; git_commit **parents = NULL; git_buf buf = { 0 }; int i = 0; if (!PyArg_ParseTuple(args, "O!O!OOO!|s", &SignatureType, &py_author, &SignatureType, &py_committer, &py_message, &py_oid, &PyList_Type, &py_parents, &encoding)) return NULL; size_t len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) return NULL; PyObject *tmessage; const char *message = pgit_borrow_encoding(py_message, encoding, NULL, &tmessage); if (message == NULL) return NULL; int err = git_tree_lookup_prefix(&tree, self->repo, &oid, len); if (err < 0) { Error_set(err); goto out; } parent_count = (int)PyList_Size(py_parents); parents = malloc(parent_count * sizeof(git_commit*)); if (parents == NULL) { PyErr_SetNone(PyExc_MemoryError); goto out; } for (; i < parent_count; i++) { py_parent = PyList_GetItemRef(py_parents, i); if (py_parent == NULL) goto out; len = py_oid_to_git_oid(py_parent, &oid); Py_DECREF(py_parent); if (len == 0) goto out; err = git_commit_lookup_prefix(&parents[i], self->repo, &oid, len); if (err < 0) { Error_set(err); goto out; } } err = git_commit_create_buffer(&buf, self->repo, py_author->signature, py_committer->signature, encoding, message, tree, parent_count, (const git_commit **)parents); if (err < 0) { Error_set(err); goto out; } str = to_unicode_n(buf.ptr, buf.size, NULL, NULL); git_buf_dispose(&buf); out: Py_DECREF(tmessage); git_tree_free(tree); while (i > 0) { i--; git_commit_free(parents[i]); } free(parents); return str; } PyDoc_STRVAR(Repository_create_commit_with_signature__doc__, "create_commit_with_signature(content: str, signature: str[, field_name: str]) -> Oid\n" "\n" "Create a new signed commit object, return its oid."); PyObject * Repository_create_commit_with_signature(Repository *self, PyObject *args) { git_oid oid; char *content, *signature; char *signature_field = NULL; if (!PyArg_ParseTuple(args, "ss|s", &content, &signature, &signature_field)) return NULL; int err = git_commit_create_with_signature(&oid, self->repo, content, signature, signature_field); if (err < 0) { Error_set(err); return NULL; } return git_oid_to_python(&oid); } PyDoc_STRVAR(Repository_create_tag__doc__, "create_tag(name: str, oid: Oid, type: enums.ObjectType, tagger: Signature, message: str) -> Oid\n" "\n" "Create a new tag object, return its oid."); PyObject * Repository_create_tag(Repository *self, PyObject *args) { PyObject *py_oid; Signature *py_tagger; char *tag_name, *message; git_oid oid; git_object *target = NULL; int err, target_type; size_t len; if (!PyArg_ParseTuple(args, "sOiO!s", &tag_name, &py_oid, &target_type, &SignatureType, &py_tagger, &message)) return NULL; len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) return NULL; err = git_object_lookup_prefix(&target, self->repo, &oid, len, target_type); err = err < 0 ? err : git_tag_create(&oid, self->repo, tag_name, target, py_tagger->signature, message, 0); git_object_free(target); if (err < 0) return Error_set_oid(err, &oid, len); return git_oid_to_python(&oid); } PyDoc_STRVAR(Repository_create_branch__doc__, "create_branch(name: str, commit: Commit, force: bool = False) -> Branch\n" "\n" "Create a new branch \"name\" which points to a commit.\n" "\n" "Returns: Branch\n" "\n" "Parameters:\n" "\n" "force\n" " If True branches will be overridden, otherwise (the default) an\n" " exception is raised.\n" "\n" "Examples::\n" "\n" " repo.create_branch('foo', repo.head.peel(), force=False)"); PyObject * Repository_create_branch(Repository *self, PyObject *args) { Commit *py_commit; git_reference *c_reference; char *c_name; int err, force = 0; if (!PyArg_ParseTuple(args, "sO!|i", &c_name, &CommitType, &py_commit, &force)) return NULL; err = git_branch_create(&c_reference, self->repo, c_name, py_commit->commit, force); if (err < 0) return Error_set(err); return wrap_branch(c_reference, self); } static PyObject * to_path_f(const char * x) { return PyUnicode_DecodeFSDefault(x); } PyDoc_STRVAR(Repository_raw_listall_references__doc__, "raw_listall_references() -> list[bytes]\n" "\n" "Return a list with all the references in the repository."); static PyObject * Repository_raw_listall_references(Repository *self, PyObject *args) { git_strarray c_result; PyObject *py_result, *py_string; unsigned index; int err; /* Get the C result */ err = git_reference_list(&c_result, self->repo); if (err < 0) return Error_set(err); /* Create a new PyTuple */ py_result = PyList_New(c_result.count); if (py_result == NULL) goto out; /* Fill it */ for (index=0; index < c_result.count; index++) { py_string = PyBytes_FromString(c_result.strings[index]); if (py_string == NULL) { Py_CLEAR(py_result); goto out; } PyList_SET_ITEM(py_result, index, py_string); } out: git_strarray_dispose(&c_result); return py_result; } PyObject * wrap_references_iterator(git_reference_iterator *iter) { RefsIterator *py_refs_iter = PyObject_New(RefsIterator , &ReferenceType); if (py_refs_iter) py_refs_iter->iterator = iter; return (PyObject *)py_refs_iter; } void References_iterator_dealloc(RefsIterator *iter) { git_reference_iterator_free(iter->iterator); Py_TYPE(iter)->tp_free((PyObject *)iter); } PyDoc_STRVAR(Repository_references_iterator_init__doc__, "references_iterator_init() -> git_reference_iterator\n" "\n" "Creates and returns an iterator for references."); PyObject * Repository_references_iterator_init(Repository *self, PyObject *args) { int err; git_reference_iterator *iter; RefsIterator *refs_iter; refs_iter = PyObject_New(RefsIterator, &RefsIteratorType); if (refs_iter == NULL) { return NULL; } if ((err = git_reference_iterator_new(&iter, self->repo)) < 0) return Error_set(err); refs_iter->iterator = iter; return (PyObject*)refs_iter; } PyDoc_STRVAR(Repository_references_iterator_next__doc__, "references_iterator_next(iter: Iterator[Reference], references_return_type: ReferenceFilter = ReferenceFilter.ALL) -> Reference\n" "\n" "Returns next reference object for repository. Optionally, can filter \n" "based on value of references_return_type.\n" "Acceptable values of references_return_type:\n" "ReferenceFilter.ALL -> returns all refs, this is the default\n" "ReferenceFilter.BRANCHES -> returns all branches\n" "ReferenceFilter.TAGS -> returns all tags\n" "all other values -> will return None"); PyObject * Repository_references_iterator_next(Repository *self, PyObject *args) { git_reference *ref; git_reference_iterator *git_iter; PyObject *iter; int references_return_type = GIT_REFERENCES_ALL; if (!PyArg_ParseTuple(args, "O|i", &iter, &references_return_type)) return NULL; git_iter = ((RefsIterator *) iter)->iterator; int err; while (0 == (err = git_reference_next(&ref, git_iter))) { switch(references_return_type) { case GIT_REFERENCES_ALL: return wrap_reference(ref, self); case GIT_REFERENCES_BRANCHES: if (git_reference_is_branch(ref)) { return wrap_reference(ref, self); } break; case GIT_REFERENCES_TAGS: if (git_reference_is_tag(ref)) { return wrap_reference(ref, self); } break; } } if (err == GIT_ITEROVER) { Py_RETURN_NONE; } return Error_set(err); } static PyObject * Repository_listall_branches_impl(Repository *self, PyObject *args, PyObject *(*item_trans)(const char *)) { git_branch_t list_flags = GIT_BRANCH_LOCAL; git_branch_iterator *iter; git_reference *ref = NULL; int err; git_branch_t type; PyObject *list; /* 1- Get list_flags */ if (!PyArg_ParseTuple(args, "|I", &list_flags)) return NULL; list = PyList_New(0); if (list == NULL) return NULL; if ((err = git_branch_iterator_new(&iter, self->repo, list_flags)) < 0) return Error_set(err); while ((err = git_branch_next(&ref, &type, iter)) == 0) { PyObject *py_branch_name = item_trans(git_reference_shorthand(ref)); git_reference_free(ref); if (py_branch_name == NULL) goto error; err = PyList_Append(list, py_branch_name); Py_DECREF(py_branch_name); if (err < 0) goto error; } git_branch_iterator_free(iter); if (err == GIT_ITEROVER) err = 0; if (err < 0) { Py_CLEAR(list); return Error_set(err); } return list; error: git_branch_iterator_free(iter); Py_CLEAR(list); return NULL; } PyDoc_STRVAR(Repository_listall_branches__doc__, "listall_branches(flag: BranchType = BranchType.LOCAL) -> list[str]\n" "\n" "Return a list with all the branches in the repository.\n" "\n" "The *flag* may be:\n" "\n" "- BranchType.LOCAL - return all local branches (set by default)\n" "- BranchType.REMOTE - return all remote-tracking branches\n" "- BranchType.ALL - return local branches and remote-tracking branches"); PyObject * Repository_listall_branches(Repository *self, PyObject *args) { return Repository_listall_branches_impl(self, args, to_path_f); } PyDoc_STRVAR(Repository_raw_listall_branches__doc__, "raw_listall_branches(flag: BranchType = BranchType.LOCAL) -> list[bytes]\n" "\n" "Return a list with all the branches in the repository.\n" "\n" "The *flag* may be:\n" "\n" "- BranchType.LOCAL - return all local branches (set by default)\n" "- BranchType.REMOTE - return all remote-tracking branches\n" "- BranchType.ALL - return local branches and remote-tracking branches"); PyObject * Repository_raw_listall_branches(Repository *self, PyObject *args) { return Repository_listall_branches_impl(self, args, PyBytes_FromString); } PyDoc_STRVAR(Repository_listall_submodules__doc__, "listall_submodules() -> list[str]\n" "\n" "Return a list with all submodule paths in the repository.\n"); static int foreach_path_cb(git_submodule *submodule, const char *name, void *payload) { PyObject *list = (PyObject *)payload; PyObject *path = to_unicode(git_submodule_path(submodule), NULL, NULL); int err = PyList_Append(list, path); Py_DECREF(path); return err; } PyObject * Repository_listall_submodules(Repository *self, PyObject *args) { PyObject *list = PyList_New(0); if (list == NULL) return NULL; int err = git_submodule_foreach(self->repo, foreach_path_cb, list); if (err) { Py_DECREF(list); if (PyErr_Occurred()) return NULL; return Error_set(err); } return list; } PyDoc_STRVAR(Repository_lookup_reference__doc__, "lookup_reference(name: str) -> Reference\n" "\n" "Lookup a reference by its name in a repository."); PyObject * Repository_lookup_reference(Repository *self, PyObject *py_name) { /* 1- Get the C name */ PyObject *tvalue; char *c_name = pgit_borrow_fsdefault(py_name, &tvalue); if (c_name == NULL) return NULL; /* 2- Lookup */ git_reference *c_reference; int err = git_reference_lookup(&c_reference, self->repo, c_name); if (err) { PyObject *err_obj = Error_set_str(err, c_name); Py_DECREF(tvalue); return err_obj; } Py_DECREF(tvalue); /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_lookup_reference_dwim__doc__, "lookup_reference_dwim(name: str) -> Reference\n" "\n" "Lookup a reference by doing-what-i-mean'ing its short name."); PyObject * Repository_lookup_reference_dwim(Repository *self, PyObject *py_name) { /* 1- Get the C name */ PyObject *tvalue; char *c_name = pgit_borrow_fsdefault(py_name, &tvalue); if (c_name == NULL) return NULL; /* 2- Lookup */ git_reference *c_reference; int err = git_reference_dwim(&c_reference, self->repo, c_name); if (err) { PyObject *err_obj = Error_set_str(err, c_name); Py_DECREF(tvalue); return err_obj; } Py_DECREF(tvalue); /* 3- Make an instance of Reference and return it */ return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_create_reference_direct__doc__, "create_reference_direct(name: str, target: Oid, force: bool, message=None) -> Reference\n" "\n" "Create a new reference \"name\" which points to an object.\n" "\n" "Returns: Reference\n" "\n" "Parameters:\n" "\n" "force\n" " If True references will be overridden, otherwise (the default) an\n" " exception is raised.\n" "\n" "message\n" " Optional message to use for the reflog.\n" "\n" "Examples::\n" "\n" " repo.create_reference_direct('refs/heads/foo', repo.head.target, False)"); PyObject * Repository_create_reference_direct(Repository *self, PyObject *args, PyObject *kw) { PyObject *py_obj; git_reference *c_reference; char *c_name; git_oid oid; int err, force; const char *message = NULL; char *keywords[] = {"name", "target", "force", "message", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "sOi|z", keywords, &c_name, &py_obj, &force, &message)) return NULL; err = py_oid_to_git_oid_expand(self->repo, py_obj, &oid); if (err < 0) return NULL; err = git_reference_create(&c_reference, self->repo, c_name, &oid, force, message); if (err < 0) return Error_set(err); return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, "create_reference_symbolic(name: str, target: str, force: bool, message: str = None) -> Reference\n" "\n" "Create a new reference \"name\" which points to another reference.\n" "\n" "Returns: Reference\n" "\n" "Parameters:\n" "\n" "force\n" " If True references will be overridden, otherwise (the default) an\n" " exception is raised.\n" "\n" "message\n" " Optional message to use for the reflog.\n" "\n" "Examples::\n" "\n" " repo.create_reference_symbolic('refs/tags/foo', 'refs/heads/master', False)"); PyObject * Repository_create_reference_symbolic(Repository *self, PyObject *args, PyObject *kw) { git_reference *c_reference; char *c_name, *c_target; int err, force; const char *message = NULL; char *keywords[] = {"name", "target", "force", "message", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kw, "ssi|z", keywords, &c_name, &c_target, &force, &message)) return NULL; err = git_reference_symbolic_create(&c_reference, self->repo, c_name, c_target, force, message); if (err < 0) return Error_set(err); return wrap_reference(c_reference, self); } PyDoc_STRVAR(Repository_compress_references__doc__, "compress_references()\n" "\n" "Suggest that the repository compress or optimize its references.\n" "This mechanism is implementation-specific. For on-disk reference\n" "databases, for example, this may pack all loose references."); PyObject * Repository_compress_references(Repository *self) { git_refdb *refdb; int err; err = git_repository_refdb(&refdb, self->repo); if (err < 0) return Error_set(err); err = git_refdb_compress(refdb); git_refdb_free(refdb); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Repository_status__doc__, "status(untracked_files: str = \"all\", ignored: bool = False) -> dict[str, enums.FileStatus]\n" "\n" "Reads the status of the repository and returns a dictionary with file\n" "paths as keys and FileStatus flags as values.\n" "\n" "Parameters:\n" "\n" "untracked_files\n" " How to handle untracked files, defaults to \"all\":\n" "\n" " - \"no\": do not return untracked files\n" " - \"normal\": include untracked files/directories but do not recurse subdirectories\n" " - \"all\": include all files in untracked directories\n" "\n" " Using `untracked_files=\"no\"` or \"normal\"can be faster than \"all\" when the worktree\n" " contains many untracked files/directories.\n" "\n" "ignored\n" " Whether to show ignored files with untracked files. Ignored when untracked_files == \"no\"\n" " Defaults to False.\n"); PyObject * Repository_status(Repository *self, PyObject *args, PyObject *kw) { int err; size_t len, i; git_status_list *list; char *untracked_files = "all"; static char *kwlist[] = {"untracked_files", "ignored", NULL}; PyObject* ignored = Py_False; if (!PyArg_ParseTupleAndKeywords(args, kw, "|sO", kwlist, &untracked_files, &ignored)) return NULL; git_status_options opts = GIT_STATUS_OPTIONS_INIT; opts.flags = GIT_STATUS_OPT_DEFAULTS; if (!strcmp(untracked_files, "no")) { opts.flags &= ~(GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS); } else if (!strcmp(untracked_files, "normal")){ opts.flags &= ~GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; } else if (strcmp(untracked_files, "all") ){ return PyErr_Format( PyExc_ValueError, "untracked_files must be one of \"all\", \"normal\" or \"one\""); }; if (!PyBool_Check(ignored)) { return PyErr_Format(PyExc_TypeError, "ignored must be True or False"); } if (!PyObject_IsTrue(ignored)) { opts.flags &= ~GIT_STATUS_OPT_INCLUDE_IGNORED; } err = git_status_list_new(&list, self->repo, &opts); if (err < 0) return Error_set(err); PyObject *dict = PyDict_New(); if (dict == NULL) goto error; len = git_status_list_entrycount(list); for (i = 0; i < len; i++) { const git_status_entry *entry; const char *path; PyObject *status; entry = git_status_byindex(list, i); if (entry == NULL) goto error; /* We need to choose one of the strings */ if (entry->head_to_index) path = entry->head_to_index->old_file.path; else path = entry->index_to_workdir->old_file.path; /* Get corresponding entry in enums.FileStatus for status int */ status = pygit2_enum(FileStatusEnum, entry->status); if (status == NULL) goto error; err = PyDict_SetItemString(dict, path, status); Py_CLEAR(status); if (err < 0) goto error; } git_status_list_free(list); return dict; error: git_status_list_free(list); Py_CLEAR(dict); return NULL; } PyDoc_STRVAR(Repository_status_file__doc__, "status_file(path: str) -> enums.FileStatus\n" "\n" "Returns the status of the given file path."); PyObject * Repository_status_file(Repository *self, PyObject *value) { PyObject *tvalue; char *path = pgit_borrow_fsdefault(value, &tvalue); if (!path) return NULL; unsigned int status; int err = git_status_file(&status, self->repo, path); if (err) { PyObject *err_obj = Error_set_str(err, path); Py_DECREF(tvalue); return err_obj; } Py_DECREF(tvalue); return pygit2_enum(FileStatusEnum, (int) status); } PyDoc_STRVAR(Repository_TreeBuilder__doc__, "TreeBuilder([tree]) -> TreeBuilder\n" "\n" "Create a TreeBuilder object for this repository."); PyObject * Repository_TreeBuilder(Repository *self, PyObject *args) { TreeBuilder *builder; git_treebuilder *bld; PyObject *py_src = NULL; git_oid oid; git_tree *tree = NULL; git_tree *must_free = NULL; int err; if (!PyArg_ParseTuple(args, "|O", &py_src)) return NULL; if (py_src) { if (PyObject_TypeCheck(py_src, &TreeType)) { Tree *py_tree = (Tree *)py_src; if (py_tree->repo->repo != self->repo) { /* return Error_set(GIT_EINVALIDARGS); */ return Error_set(GIT_ERROR); } if (Object__load((Object*)py_tree) == NULL) { return NULL; } // Lazy load tree = py_tree->tree; } else { err = py_oid_to_git_oid_expand(self->repo, py_src, &oid); if (err < 0) return NULL; err = git_tree_lookup(&tree, self->repo, &oid); if (err < 0) return Error_set(err); must_free = tree; } } err = git_treebuilder_new(&bld, self->repo, tree); if (must_free != NULL) git_tree_free(must_free); if (err < 0) return Error_set(err); builder = PyObject_New(TreeBuilder, &TreeBuilderType); if (builder) { builder->repo = self; builder->bld = bld; Py_INCREF(self); } return (PyObject*)builder; } PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration"); PyObject * Repository_default_signature__get__(Repository *self) { git_signature *sig; int err; if ((err = git_signature_default(&sig, self->repo)) < 0) return Error_set(err); return build_signature(NULL, sig, "utf-8"); } PyDoc_STRVAR(Repository_odb__doc__, "Return the object database for this repository"); PyObject * Repository_odb__get__(Repository *self) { git_odb *odb; int err; err = git_repository_odb(&odb, self->repo); if (err < 0) return Error_set(err); return wrap_odb(odb); } PyDoc_STRVAR(Repository_refdb__doc__, "Return the reference database for this repository"); PyObject * Repository_refdb__get__(Repository *self) { git_refdb *refdb; int err; err = git_repository_refdb(&refdb, self->repo); if (err < 0) return Error_set(err); return wrap_refdb(refdb); } PyDoc_STRVAR(Repository__pointer__doc__, "Get the repo's pointer. For internal use only."); PyObject * Repository__pointer__get__(Repository *self) { /* Bytes means a raw buffer */ return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *)); } PyDoc_STRVAR(Repository_notes__doc__, ""); PyObject * Repository_notes(Repository *self, PyObject *args) { char *ref = "refs/notes/commits"; if (!PyArg_ParseTuple(args, "|s", &ref)) return NULL; NoteIter *iter = PyObject_New(NoteIter, &NoteIterType); if (iter == NULL) return NULL; Py_INCREF(self); iter->repo = self; iter->ref = ref; iter->iter = NULL; int err = git_note_iterator_new(&iter->iter, self->repo, iter->ref); if (err != GIT_OK) { Py_DECREF(iter); return Error_set(err); } return (PyObject*)iter; } PyDoc_STRVAR(Repository_create_note__doc__, "create_note(message: str, author: Signature, committer: Signature, annotated_id: str, ref: str = \"refs/notes/commits\", force: bool = False) -> Oid\n" "\n" "Create a new note for an object, return its SHA-ID." "If no ref is given 'refs/notes/commits' will be used."); PyObject * Repository_create_note(Repository *self, PyObject* args) { git_oid note_id, annotated_id; char *annotated = NULL, *message = NULL, *ref = "refs/notes/commits"; int err = GIT_ERROR; unsigned int force = 0; Signature *py_author, *py_committer; if (!PyArg_ParseTuple(args, "sO!O!s|si", &message, &SignatureType, &py_author, &SignatureType, &py_committer, &annotated, &ref, &force)) return NULL; err = git_oid_fromstr(&annotated_id, annotated); if (err < 0) return Error_set(err); err = git_note_create(¬e_id, self->repo, ref, py_author->signature, py_committer->signature, &annotated_id, message, force); if (err < 0) return Error_set(err); return git_oid_to_python(¬e_id); } PyDoc_STRVAR(Repository_lookup_note__doc__, "lookup_note(annotated_id: str, ref: str = \"refs/notes/commits\") -> Note\n" "\n" "Lookup a note for an annotated object in a repository."); PyObject * Repository_lookup_note(Repository *self, PyObject* args) { git_oid annotated_id; char* annotated = NULL, *ref = "refs/notes/commits"; int err; if (!PyArg_ParseTuple(args, "s|s", &annotated, &ref)) return NULL; err = git_oid_fromstr(&annotated_id, annotated); if (err < 0) return Error_set(err); return (PyObject*) wrap_note(self, NULL, &annotated_id, ref); } PyDoc_STRVAR(Repository_reset__doc__, "reset(oid: Oid, reset_mode: enums.ResetMode)\n" "\n" "Resets the current head.\n" "\n" "Parameters:\n" "\n" "oid\n" " The oid of the commit to reset to.\n" "\n" "reset_mode\n" " * SOFT: resets head to point to oid, but does not modify\n" " working copy, and leaves the changes in the index.\n" " * MIXED: resets head to point to oid, but does not modify\n" " working copy. It empties the index too.\n" " * HARD: resets head to point to oid, and resets too the\n" " working copy and the content of the index.\n"); PyObject * Repository_reset(Repository *self, PyObject* args) { PyObject *py_oid; git_oid oid; git_object *target = NULL; int err, reset_type; size_t len; if (!PyArg_ParseTuple(args, "Oi", &py_oid, &reset_type )) return NULL; len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) return NULL; err = git_object_lookup_prefix(&target, self->repo, &oid, len, GIT_OBJECT_ANY); err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL); git_object_free(target); if (err < 0) return Error_set_oid(err, &oid, len); Py_RETURN_NONE; } PyDoc_STRVAR(Repository_free__doc__, "free()\n" "\n" "Releases handles to the Git database without deallocating the repository.\n"); PyObject * Repository_free(Repository *self) { if (self->owned) git_repository__cleanup(self->repo); Py_RETURN_NONE; } PyDoc_STRVAR(Repository_expand_id__doc__, "expand_id(hex: str) -> Oid\n" "\n" "Expand a string into a full Oid according to the objects in this repository.\n"); PyObject * Repository_expand_id(Repository *self, PyObject *py_hex) { git_oid oid; int err; err = py_oid_to_git_oid_expand(self->repo, py_hex, &oid); if (err < 0) return NULL; return git_oid_to_python(&oid); } PyDoc_STRVAR(Repository_add_worktree__doc__, "add_worktree(name: str, path: str | bytes[, ref: Reference]) -> Worktree\n" "\n" "Create a new worktree for this repository. If ref is specified, no new \ branch will be created and the provided ref will be checked out instead."); PyObject * Repository_add_worktree(Repository *self, PyObject *args) { char *c_name; PyBytesObject *py_path = NULL; char *c_path = NULL; Reference *py_reference = NULL; git_worktree *wt; git_worktree_add_options add_opts = GIT_WORKTREE_ADD_OPTIONS_INIT; int err; if (!PyArg_ParseTuple(args, "sO&|O!", &c_name, PyUnicode_FSConverter, &py_path, &ReferenceType, &py_reference)) return NULL; if (py_path != NULL) c_path = PyBytes_AS_STRING(py_path); if(py_reference != NULL) add_opts.ref = py_reference->reference; err = git_worktree_add(&wt, self->repo, c_name, c_path, &add_opts); Py_XDECREF(py_path); if (err < 0) return Error_set(err); return wrap_worktree(self, wt); } PyDoc_STRVAR(Repository_lookup_worktree__doc__, "lookup_worktree(name: str) -> Worktree\n" "\n" "Lookup a worktree from its name."); PyObject * Repository_lookup_worktree(Repository *self, PyObject *args) { char *c_name; git_worktree *wt; int err; if (!PyArg_ParseTuple(args, "s", &c_name)) return NULL; err = git_worktree_lookup(&wt, self->repo, c_name); if (err < 0) return Error_set(err); return wrap_worktree(self, wt); } PyDoc_STRVAR(Repository_list_worktrees__doc__, "list_worktrees() -> list[str]\n" "\n" "Return a list with all the worktrees of this repository."); PyObject * Repository_list_worktrees(Repository *self, PyObject *args) { git_strarray c_result; PyObject *py_result, *py_string; unsigned index; int err; /* Get the C result */ err = git_worktree_list(&c_result, self->repo); if (err < 0) return Error_set(err); /* Create a new PyTuple */ py_result = PyList_New(c_result.count); if (py_result == NULL) goto out; /* Fill it */ for (index=0; index < c_result.count; index++) { py_string = PyUnicode_DecodeFSDefault(c_result.strings[index]); if (py_string == NULL) { Py_CLEAR(py_result); goto out; } PyList_SET_ITEM(py_result, index, py_string); } out: git_strarray_dispose(&c_result); return py_result; } PyDoc_STRVAR(Repository_apply__doc__, "apply(diff: Diff, location: ApplyLocation = ApplyLocation.WORKDIR)\n" "\n" "Applies the given Diff object to HEAD, writing the results into the\n" "working directory, the index, or both.\n" "\n" "Parameters:\n" "\n" "diff\n" " The Diff to apply.\n" "\n" "location\n" " The location to apply: ApplyLocation.WORKDIR (default),\n" " ApplyLocation.INDEX, or ApplyLocation.BOTH.\n" ); PyObject * Repository_apply(Repository *self, PyObject *args, PyObject *kwds) { Diff *py_diff; int location = GIT_APPLY_LOCATION_WORKDIR; git_apply_options options = GIT_APPLY_OPTIONS_INIT; char* keywords[] = {"diff", "location", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|i", keywords, &DiffType, &py_diff, &location)) return NULL; int err = git_apply(self->repo, py_diff->diff, location, &options); if (err != 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Repository_applies__doc__, "applies(diff: Diff, location: int = GIT_APPLY_LOCATION_INDEX, raise_error: bool = False) -> bool\n" "\n" "Tests if the given patch will apply to HEAD, without writing it.\n" "\n" "Parameters:\n" "\n" "diff\n" " The Diff to apply.\n" "\n" "location\n" " The location to apply: GIT_APPLY_LOCATION_WORKDIR,\n" " GIT_APPLY_LOCATION_INDEX (default), or GIT_APPLY_LOCATION_BOTH.\n" "\n" "raise_error\n" " If the patch doesn't apply, raise an exception containing more details\n" " about the failure instead of returning False.\n" ); PyObject * Repository_applies(Repository *self, PyObject *args, PyObject *kwds) { Diff *py_diff; int location = GIT_APPLY_LOCATION_INDEX; int raise_error = 0; git_apply_options options = GIT_APPLY_OPTIONS_INIT; options.flags |= GIT_APPLY_CHECK; char* keywords[] = {"diff", "location", "raise_error", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|ii", keywords, &DiffType, &py_diff, &location, &raise_error)) return NULL; int err = git_apply(self->repo, ((Diff*)py_diff)->diff, location, &options); if (err != 0) { if (raise_error) return Error_set(err); else Py_RETURN_FALSE; } Py_RETURN_TRUE; } PyDoc_STRVAR(Repository_set_odb__doc__, "set_odb(odb: Odb)\n" "\n" "Sets the object database for this repository.\n" "This is a low-level function, most users won't need it.\n"); PyObject * Repository_set_odb(Repository *self, Odb *odb) { int err; err = git_repository_set_odb(self->repo, odb->odb); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Repository_set_refdb__doc__, "set_refdb(refdb: Refdb)\n" "\n" "Sets the reference database for this repository.\n" "This is a low-level function, most users won't need it.\n"); PyObject * Repository_set_refdb(Repository *self, Refdb *refdb) { int err; err = git_repository_set_refdb(self->repo, refdb->refdb); if (err < 0) return Error_set(err); Py_RETURN_NONE; } static int foreach_stash_cb(size_t index, const char *message, const git_oid *stash_id, void *payload) { int err; Stash *py_stash; py_stash = PyObject_New(Stash, &StashType); if (py_stash == NULL) return GIT_EUSER; assert(message != NULL); assert(stash_id != NULL); py_stash->commit_id = git_oid_to_python(stash_id); if (py_stash->commit_id == NULL) return GIT_EUSER; py_stash->message = strdup(message); if (py_stash->message == NULL) { PyErr_NoMemory(); return GIT_EUSER; } PyObject* list = (PyObject*) payload; err = PyList_Append(list, (PyObject*) py_stash); Py_DECREF(py_stash); if (err < 0) return GIT_EUSER; return 0; } PyDoc_STRVAR(Repository_listall_stashes__doc__, "listall_stashes() -> list[Stash]\n" "\n" "Return a list with all stashed commits in the repository.\n"); PyObject * Repository_listall_stashes(Repository *self, PyObject *args) { int err; PyObject *list = PyList_New(0); if (list == NULL) return NULL; err = git_stash_foreach(self->repo, foreach_stash_cb, (void*)list); if (err == 0) { return list; } else { Py_CLEAR(list); if (PyErr_Occurred()) return NULL; return Error_set(err); } } static int foreach_mergehead_cb(const git_oid *oid, void *payload) { PyObject* py_oid = git_oid_to_python(oid); if (py_oid == NULL) return GIT_EUSER; PyObject* list = (PyObject*) payload; int err = PyList_Append(list, (PyObject*) py_oid); Py_DECREF(py_oid); if (err < 0) return GIT_EUSER; return 0; } PyDoc_STRVAR(Repository_listall_mergeheads__doc__, "listall_mergeheads() -> list[Oid]\n" "\n" "If a merge is in progress, return a list of all commit oids in the MERGE_HEAD file.\n" "Return an empty list if there is no MERGE_HEAD file (no merge in progress)."); PyObject * Repository_listall_mergeheads(Repository *self, PyObject *args) { int err; PyObject *list = PyList_New(0); if (list == NULL) return NULL; err = git_repository_mergehead_foreach(self->repo, foreach_mergehead_cb, (void*)list); if (err == 0) { return list; } else if (err == GIT_ENOTFOUND) { /* MERGE_HEAD not found - return empty list */ return list; } else { Py_CLEAR(list); if (PyErr_Occurred()) return NULL; return Error_set(err); } } static PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromworkdir, METH_O), METHOD(Repository, create_blob_fromdisk, METH_O), METHOD(Repository, create_blob_fromiobase, METH_O), METHOD(Repository, create_commit, METH_VARARGS), METHOD(Repository, create_commit_string, METH_VARARGS), METHOD(Repository, create_commit_with_signature, METH_VARARGS), METHOD(Repository, create_tag, METH_VARARGS), METHOD(Repository, TreeBuilder, METH_VARARGS), METHOD(Repository, walk, METH_VARARGS), METHOD(Repository, descendant_of, METH_VARARGS), METHOD(Repository, merge_base, METH_VARARGS), METHOD(Repository, merge_base_many, METH_VARARGS), METHOD(Repository, merge_base_octopus, METH_VARARGS), METHOD(Repository, merge_analysis, METH_VARARGS), METHOD(Repository, cherrypick, METH_O), METHOD(Repository, apply, METH_VARARGS | METH_KEYWORDS), METHOD(Repository, applies, METH_VARARGS | METH_KEYWORDS), METHOD(Repository, create_reference_direct, METH_VARARGS | METH_KEYWORDS), METHOD(Repository, create_reference_symbolic, METH_VARARGS | METH_KEYWORDS), METHOD(Repository, compress_references, METH_NOARGS), METHOD(Repository, raw_listall_references, METH_NOARGS), METHOD(Repository, references_iterator_init, METH_NOARGS), METHOD(Repository, references_iterator_next, METH_VARARGS), METHOD(Repository, listall_submodules, METH_NOARGS), METHOD(Repository, lookup_reference, METH_O), METHOD(Repository, lookup_reference_dwim, METH_O), METHOD(Repository, revparse_single, METH_O), METHOD(Repository, revparse_ext, METH_O), METHOD(Repository, revparse, METH_O), METHOD(Repository, status, METH_VARARGS | METH_KEYWORDS), METHOD(Repository, status_file, METH_O), METHOD(Repository, notes, METH_VARARGS), METHOD(Repository, create_note, METH_VARARGS), METHOD(Repository, lookup_note, METH_VARARGS), METHOD(Repository, git_object_lookup_prefix, METH_O), METHOD(Repository, lookup_branch, METH_VARARGS), METHOD(Repository, path_is_ignored, METH_VARARGS), METHOD(Repository, listall_branches, METH_VARARGS), METHOD(Repository, raw_listall_branches, METH_VARARGS), METHOD(Repository, create_branch, METH_VARARGS), METHOD(Repository, reset, METH_VARARGS), METHOD(Repository, free, METH_NOARGS), METHOD(Repository, expand_id, METH_O), METHOD(Repository, add_worktree, METH_VARARGS), METHOD(Repository, lookup_worktree, METH_VARARGS), METHOD(Repository, list_worktrees, METH_VARARGS), METHOD(Repository, _from_c, METH_VARARGS), METHOD(Repository, _disown, METH_NOARGS), METHOD(Repository, set_odb, METH_O), METHOD(Repository, set_refdb, METH_O), METHOD(Repository, listall_stashes, METH_NOARGS), METHOD(Repository, listall_mergeheads, METH_NOARGS), {NULL} }; PyGetSetDef Repository_getseters[] = { GETTER(Repository, path), GETTER(Repository, head), GETTER(Repository, head_is_detached), GETTER(Repository, head_is_unborn), GETTER(Repository, is_empty), GETTER(Repository, is_bare), GETTER(Repository, is_shallow), GETSET(Repository, workdir), GETTER(Repository, default_signature), GETTER(Repository, odb), GETTER(Repository, refdb), GETTER(Repository, _pointer), {NULL} }; PyDoc_STRVAR(Repository__doc__, "Repository(backend) -> Repository\n" "\n" "Git repository."); PyTypeObject RepositoryType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Repository", /* tp_name */ sizeof(Repository), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Repository_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ Repository__doc__, /* tp_doc */ (traverseproc)Repository_traverse, /* tp_traverse */ (inquiry)Repository_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Repository_methods, /* tp_methods */ 0, /* tp_members */ Repository_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Repository_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyDoc_STRVAR(RefsIterator__doc__, "References iterator."); PyTypeObject RefsIteratorType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RefsIterator", /* tp_name */ sizeof(Repository), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)References_iterator_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ RefsIterator__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)Repository_references_iterator_next, /* tp_iternext */ }; libgit2-pygit2-d86662e/src/repository.h000077500000000000000000000067271521317216500200310ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_repository_h #define INCLUDE_pygit2_repository_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" typedef enum { GIT_REFERENCES_ALL = 0, GIT_REFERENCES_BRANCHES = 1, GIT_REFERENCES_TAGS = 2, } git_reference_iterator_return_t; PyObject *wrap_repository(git_repository *c_repo); int Repository_init(Repository *self, PyObject *args, PyObject *kwds); int Repository_traverse(Repository *self, visitproc visit, void *arg); int Repository_clear(Repository *self); PyObject* Repository_walk(Repository *self, PyObject *args); PyObject* Repository_create_blob(Repository *self, PyObject *args); PyObject* Repository_create_blob_fromdisk(Repository *self, PyObject *args); PyObject* Repository_create_commit(Repository *self, PyObject *args); PyObject* Repository_create_commit_string(Repository *self, PyObject *args); PyObject* Repository_create_commit_with_signature(Repository *self, PyObject *args); PyObject* Repository_create_tag(Repository *self, PyObject *args); PyObject* Repository_create_branch(Repository *self, PyObject *args); PyObject* Repository_references_iterator_init(Repository *self, PyObject *args); PyObject* Repository_references_iterator_next(Repository *self, PyObject *args); PyObject* Repository_listall_branches(Repository *self, PyObject *args); PyObject* Repository_lookup_reference(Repository *self, PyObject *py_name); PyObject* Repository_add_worktree(Repository *self, PyObject *args); PyObject* Repository_lookup_worktree(Repository *self, PyObject *py_name); PyObject* Repository_list_worktrees(Repository *self, PyObject *args); PyObject* Repository_create_reference_direct(Repository *self, PyObject *args, PyObject* kw); PyObject* Repository_create_reference_symbolic(Repository *self, PyObject *args, PyObject* kw); PyObject* Repository_compress_references(Repository *self); PyObject* Repository_status(Repository *self, PyObject *args, PyObject *kw); PyObject* Repository_status_file(Repository *self, PyObject *value); PyObject* Repository_TreeBuilder(Repository *self, PyObject *args); PyObject* Repository_cherrypick(Repository *self, PyObject *py_oid); PyObject* Repository_apply(Repository *self, PyObject *py_diff, PyObject *kwds); PyObject* Repository_merge_analysis(Repository *self, PyObject *args); #endif libgit2-pygit2-d86662e/src/revspec.c000066400000000000000000000135441521317216500172440ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "object.h" #include "types.h" #include "utils.h" extern PyTypeObject RevSpecType; PyObject* wrap_revspec(git_revspec *revspec, Repository *repo) { RevSpec *py_revspec; py_revspec = PyObject_New(RevSpec, &RevSpecType); if (py_revspec) { py_revspec->flags = revspec->flags; if (revspec->from != NULL) { py_revspec->from = wrap_object(revspec->from, repo, NULL); } else { py_revspec->from = NULL; } if (revspec->to != NULL) { py_revspec->to = wrap_object(revspec->to, repo, NULL); } else { py_revspec->to = NULL; } } return (PyObject*) py_revspec; } PyDoc_STRVAR(RevSpec_from_object__doc__, "From revision"); PyObject * RevSpec_from_object__get__(RevSpec *self) { if (self->from == NULL) Py_RETURN_NONE; Py_INCREF(self->from); return self->from; } PyDoc_STRVAR(RevSpec_to_object__doc__, "To revision"); PyObject * RevSpec_to_object__get__(RevSpec *self) { if (self->to == NULL) Py_RETURN_NONE; Py_INCREF(self->to); return self->to; } PyDoc_STRVAR(RevSpec_flags__doc__, "A combination of enums.RevSpecFlag constants indicating the\n" "intended behavior of the spec passed to Repository.revparse()"); PyObject * RevSpec_flags__get__(RevSpec *self) { return PyLong_FromLong(self->flags); } static PyObject * RevSpec_repr(RevSpec *self) { return PyUnicode_FromFormat("", (self->from != NULL) ? self->from : Py_None, (self->to != NULL) ? self->to : Py_None); } static void RevSpec_dealloc(RevSpec *self) { Py_XDECREF(self->from); Py_XDECREF(self->to); PyObject_Del(self); } PyGetSetDef RevSpec_getsetters[] = { GETTER(RevSpec, from_object), GETTER(RevSpec, to_object), GETTER(RevSpec, flags), {NULL} }; PyDoc_STRVAR(RevSpec__doc__, "RevSpec object, output from Repository.revparse()."); PyTypeObject RevSpecType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.RevSpec", /* tp_name */ sizeof(RevSpec), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)RevSpec_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)RevSpec_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ RevSpec__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ RevSpec_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/revspec.h000066400000000000000000000026421521317216500172460ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_revspec_h #define INCLUDE_pygit2_revspec_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject* wrap_revspec(git_revspec *revspec, Repository *repo); #endif libgit2-pygit2-d86662e/src/signature.c000066400000000000000000000243771521317216500176040ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "types.h" #include "utils.h" #include "oid.h" #include "signature.h" extern PyTypeObject SignatureType; int Signature_init(Signature *self, PyObject *args, PyObject *kwds) { char *keywords[] = {"name", "email", "time", "offset", "encoding", NULL}; PyObject *py_name; char *email, *encoding = NULL; long long time = -1; int offset = 0; if (!PyArg_ParseTupleAndKeywords( args, kwds, "Os|Liz", keywords, &py_name, &email, &time, &offset, &encoding)) return -1; PyObject *tname; const char *name = pgit_borrow_encoding( py_name, value_or_default(encoding, "utf-8"), NULL, &tname); if (name == NULL) return -1; git_signature *signature; int err = (time == -1) ? git_signature_now(&signature, name, email) : git_signature_new(&signature, name, email, time, offset); Py_DECREF(tname); if (err < 0) { Error_set(err); return -1; } self->obj = NULL; self->signature = signature; if (encoding) { self->encoding = strdup(encoding); if (self->encoding == NULL) { PyErr_NoMemory(); return -1; } } return 0; } void Signature_dealloc(Signature *self) { /* self->obj is the owner of the git_signature C structure, so we mustn't free it */ if (self->obj) { Py_CLEAR(self->obj); } else { git_signature_free((git_signature *) self->signature); } /* we own self->encoding */ free(self->encoding); PyObject_Del(self); } PyDoc_STRVAR(Signature__pointer__doc__, "Get the signature's pointer. For internal use only."); PyObject * Signature__pointer__get__(Signature *self) { /* Bytes means a raw buffer */ return PyBytes_FromStringAndSize((char *) &self->signature, sizeof(git_signature *)); } PyDoc_STRVAR(Signature__encoding__doc__, "Encoding."); PyObject * Signature__encoding__get__(Signature *self) { const char *encoding = self->encoding; if (encoding == NULL) { encoding = "utf-8"; } return to_encoding(encoding); } PyDoc_STRVAR(Signature_raw_name__doc__, "Name (bytes)."); PyObject * Signature_raw_name__get__(Signature *self) { return PyBytes_FromString(self->signature->name); } PyDoc_STRVAR(Signature_raw_email__doc__, "Email (bytes)."); PyObject * Signature_raw_email__get__(Signature *self) { return PyBytes_FromString(self->signature->email); } PyDoc_STRVAR(Signature_name__doc__, "Name."); PyObject * Signature_name__get__(Signature *self) { return to_unicode(self->signature->name, self->encoding, NULL); } PyDoc_STRVAR(Signature_email__doc__, "Email address."); PyObject * Signature_email__get__(Signature *self) { return to_unicode(self->signature->email, self->encoding, NULL); } PyDoc_STRVAR(Signature_time__doc__, "Unix time."); PyObject * Signature_time__get__(Signature *self) { return PyLong_FromLongLong(self->signature->when.time); } PyDoc_STRVAR(Signature_offset__doc__, "Offset from UTC in minutes."); PyObject * Signature_offset__get__(Signature *self) { return PyLong_FromLong(self->signature->when.offset); } PyGetSetDef Signature_getseters[] = { GETTER(Signature, _encoding), GETTER(Signature, raw_name), GETTER(Signature, raw_email), GETTER(Signature, name), GETTER(Signature, email), GETTER(Signature, time), GETTER(Signature, offset), GETTER(Signature, _pointer), {NULL} }; PyObject * Signature_richcompare(PyObject *a, PyObject *b, int op) { int eq; Signature *sa, *sb; /* We only support comparing to another signature */ if (!PyObject_TypeCheck(b, &SignatureType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } sa = (Signature *)a; sb = (Signature *)b; eq = ( strcmp(sa->signature->name, sb->signature->name) == 0 && strcmp(sa->signature->email, sb->signature->email) == 0 && sa->signature->when.time == sb->signature->when.time && sa->signature->when.offset == sb->signature->when.offset && sa->signature->when.sign == sb->signature->when.sign && strcmp(value_or_default(sa->encoding, "utf-8"), value_or_default(sb->encoding, "utf-8")) == 0); switch (op) { case Py_EQ: if (eq) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } case Py_NE: if (eq) { Py_RETURN_FALSE; } else { Py_RETURN_TRUE; } default: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } } static PyObject * Signature__str__(Signature *self) { PyObject *name, *email, *str; name = to_unicode_safe(self->signature->name, self->encoding); email = to_unicode_safe(self->signature->email, self->encoding); assert(name); assert(email); str = PyUnicode_FromFormat("%U <%U>", name, email); Py_DECREF(name); Py_DECREF(email); return str; } static PyObject * Signature__repr__(Signature *self) { PyObject *name, *email, *encoding, *str; name = to_unicode_safe(self->signature->name, self->encoding); email = to_unicode_safe(self->signature->email, self->encoding); if (self->encoding) { encoding = to_unicode_safe(self->encoding, self->encoding); } else { encoding = Py_None; Py_INCREF(Py_None); } assert(name); assert(email); assert(encoding); str = PyUnicode_FromFormat( "pygit2.Signature(%R, %R, %lld, %ld, %R)", name, email, self->signature->when.time, self->signature->when.offset, encoding); Py_DECREF(name); Py_DECREF(email); Py_DECREF(encoding); return str; } PyDoc_STRVAR(Signature__doc__, "Signature."); PyTypeObject SignatureType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Signature", /* tp_name */ sizeof(Signature), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Signature_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Signature__repr__, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)Signature__str__, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Signature__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)Signature_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ Signature_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Signature_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * build_signature(Object *obj, const git_signature *signature, const char *encoding) { Signature *py_signature; py_signature = PyObject_New(Signature, &SignatureType); if (!py_signature) goto on_error; py_signature->encoding = NULL; if (encoding) { py_signature->encoding = strdup(encoding); if (!py_signature->encoding) goto on_error; } Py_XINCREF(obj); py_signature->obj = obj; py_signature->signature = signature; return (PyObject*)py_signature; on_error: git_signature_free((git_signature *) signature); return NULL; } libgit2-pygit2-d86662e/src/signature.h000066400000000000000000000027241521317216500176010ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_signature_h #define INCLUDE_pygit2_signature_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject* build_signature(Object *obj, const git_signature *signature, const char *encoding); #endif libgit2-pygit2-d86662e/src/stash.c000066400000000000000000000135171521317216500167170ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "object.h" #include "error.h" #include "types.h" #include "utils.h" #include "oid.h" PyTypeObject StashType; PyDoc_STRVAR(Stash_commit_id__doc__, "The commit id of the stashed state."); PyObject * Stash_commit_id__get__(Stash *self) { Py_INCREF(self->commit_id); return self->commit_id; } PyDoc_STRVAR(Stash_message__doc__, "Stash message."); PyObject * Stash_message__get__(Stash *self) { return to_unicode(self->message, "utf-8", "strict"); } PyDoc_STRVAR(Stash_raw_message__doc__, "Stash message (bytes)."); PyObject * Stash_raw_message__get__(Stash *self) { return PyBytes_FromString(self->message); } static void Stash_dealloc(Stash *self) { Py_CLEAR(self->commit_id); free(self->message); PyObject_Del(self); } static PyObject * Stash_repr(Stash *self) { return PyUnicode_FromFormat("", self->commit_id); } PyObject * Stash_richcompare(PyObject *o1, PyObject *o2, int op) { int eq = 0; Stash *s1, *s2; git_oid *oid1, *oid2; /* We only support comparing to another stash */ if (!PyObject_TypeCheck(o2, &StashType)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } s1 = (Stash *)o1; s2 = (Stash *)o2; oid1 = &((Oid *)s1->commit_id)->oid; oid2 = &((Oid *)s2->commit_id)->oid; eq = git_oid_equal(oid1, oid2) && (0 == strcmp(s1->message, s2->message)); switch (op) { case Py_EQ: if (eq) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } case Py_NE: if (eq) { Py_RETURN_FALSE; } else { Py_RETURN_TRUE; } default: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } } PyGetSetDef Stash_getseters[] = { GETTER(Stash, commit_id), GETTER(Stash, message), GETTER(Stash, raw_message), {NULL} }; PyDoc_STRVAR(Stash__doc__, "Stashed state."); PyTypeObject StashType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Stash", /* tp_name */ sizeof(Stash), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Stash_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Stash_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Stash__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ Stash_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ Stash_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/tag.c000066400000000000000000000150401521317216500163410ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "object.h" #include "error.h" #include "types.h" #include "utils.h" #include "signature.h" #include "oid.h" PyDoc_STRVAR(Tag_target__doc__, "Tagged object."); PyObject * Tag_target__get__(Tag *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const git_oid *oid = git_tag_target_id(self->tag); return git_oid_to_python(oid); } PyDoc_STRVAR(Tag_get_object__doc__, "get_object() -> Object\n" "\n" "Retrieves the object the current tag is pointing to."); PyObject * Tag_get_object(Tag *self) { git_object* obj; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load int err = git_tag_peel(&obj, self->tag); if (err < 0) return Error_set(err); return wrap_object(obj, self->repo, NULL); } PyDoc_STRVAR(Tag_name__doc__, "Tag name."); PyObject * Tag_name__get__(Tag *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const char *name = git_tag_name(self->tag); if (!name) Py_RETURN_NONE; return to_unicode(name, "utf-8", "strict"); } PyDoc_STRVAR(Tag_raw_name__doc__, "Tag name (bytes)."); PyObject * Tag_raw_name__get__(Tag *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const char *name = git_tag_name(self->tag); if (!name) Py_RETURN_NONE; return PyBytes_FromString(name); } PyDoc_STRVAR(Tag_tagger__doc__, "Tagger."); PyObject * Tag_tagger__get__(Tag *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const git_signature *signature = git_tag_tagger(self->tag); if (!signature) Py_RETURN_NONE; return build_signature((Object*)self, signature, "utf-8"); } PyDoc_STRVAR(Tag_message__doc__, "Tag message."); PyObject * Tag_message__get__(Tag *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const char *message = git_tag_message(self->tag); if (!message) Py_RETURN_NONE; return to_unicode(message, "utf-8", "strict"); } PyDoc_STRVAR(Tag_raw_message__doc__, "Tag message (bytes)."); PyObject * Tag_raw_message__get__(Tag *self) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load const char *message = git_tag_message(self->tag); if (!message) Py_RETURN_NONE; return PyBytes_FromString(message); } static PyMethodDef Tag_methods[] = { METHOD(Tag, get_object, METH_NOARGS), {NULL} }; PyGetSetDef Tag_getseters[] = { GETTER(Tag, target), GETTER(Tag, name), GETTER(Tag, raw_name), GETTER(Tag, tagger), GETTER(Tag, message), GETTER(Tag, raw_message), {NULL} }; PyDoc_STRVAR(Tag__doc__, "Tag objects."); PyTypeObject TagType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Tag", /* tp_name */ sizeof(Tag), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Tag__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Tag_methods, /* tp_methods */ 0, /* tp_members */ Tag_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/tree.c000066400000000000000000000454271521317216500165410ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "error.h" #include "utils.h" #include "repository.h" #include "object.h" #include "oid.h" #include "tree.h" #include "diff.h" extern PyTypeObject TreeType; extern PyTypeObject DiffType; extern PyTypeObject TreeIterType; extern PyTypeObject IndexType; PyObject * treeentry_to_object(const git_tree_entry *entry, Repository *repo) { if (repo == NULL) { PyErr_SetString(PyExc_ValueError, "expected repository"); return NULL; } return wrap_object(NULL, repo, entry); } Py_ssize_t Tree_len(Tree *self) { if (Object__load((Object*)self) == NULL) { return -1; } // Lazy load return (Py_ssize_t)git_tree_entrycount(self->tree); } int Tree_contains(Tree *self, PyObject *py_name) { if (Object__load((Object*)self) == NULL) { return -1; } // Lazy load PyObject *tvalue; char *name = pgit_borrow_fsdefault(py_name, &tvalue); if (name == NULL) return -1; git_tree_entry *entry; int err = git_tree_entry_bypath(&entry, self->tree, name); Py_DECREF(tvalue); if (err == GIT_ENOTFOUND) { return 0; } else if (err < 0) { Error_set(err); return -1; } git_tree_entry_free(entry); return 1; } int Tree_fix_index(const git_tree *tree, PyObject *py_index) { long index; size_t len; long slen; index = PyLong_AsLong(py_index); if (PyErr_Occurred()) return -1; len = git_tree_entrycount(tree); slen = (long)len; if (index >= slen) { PyErr_SetObject(PyExc_IndexError, py_index); return -1; } else if (index < -slen) { PyErr_SetObject(PyExc_IndexError, py_index); return -1; } /* This function is called via mp_subscript, which doesn't do negative * index rewriting, so we have to do it manually. */ if (index < 0) index = len + index; return (int)index; } PyObject * Tree_iter(Tree *self) { TreeIter *iter; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load iter = PyObject_New(TreeIter, &TreeIterType); if (iter) { Py_INCREF(self); iter->owner = self; iter->i = 0; } return (PyObject*)iter; } PyObject* tree_getentry_by_index(const git_tree *tree, Repository *repo, PyObject *py_index) { int index; const git_tree_entry *entry_src; git_tree_entry *entry; index = Tree_fix_index(tree, py_index); if (PyErr_Occurred()) return NULL; entry_src = git_tree_entry_byindex(tree, index); if (!entry_src) { PyErr_SetObject(PyExc_IndexError, py_index); return NULL; } if (git_tree_entry_dup(&entry, entry_src) < 0) { PyErr_SetNone(PyExc_MemoryError); return NULL; } return treeentry_to_object(entry, repo); } PyObject* tree_getentry_by_path(const git_tree *tree, Repository *repo, PyObject *py_path) { PyObject *tvalue; char *path = pgit_borrow_fsdefault(py_path, &tvalue); if (path == NULL) { PyErr_SetString(PyExc_TypeError, "Value must be a path string"); return NULL; } git_tree_entry *entry; int err = git_tree_entry_bypath(&entry, tree, path); Py_DECREF(tvalue); if (err == GIT_ENOTFOUND) { PyErr_SetObject(PyExc_KeyError, py_path); return NULL; } if (err < 0) return Error_set(err); /* git_tree_entry_dup is already done in git_tree_entry_bypath */ return treeentry_to_object(entry, repo); } PyObject* Tree_subscript(Tree *self, PyObject *value) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load /* Case 1: integer */ if (PyLong_Check(value)) return tree_getentry_by_index(self->tree, self->repo, value); /* Case 2: byte or text string */ return tree_getentry_by_path(self->tree, self->repo, value); } PyObject * Tree_divide(Tree *self, PyObject *value) { if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load return tree_getentry_by_path(self->tree, self->repo, value); } PyDoc_STRVAR(Tree_diff_to_workdir__doc__, "diff_to_workdir(flags: enums.DiffOption = enums.DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0) -> Diff\n" "\n" "Show the changes between the :py:class:`~pygit2.Tree` and the workdir.\n" "\n" "Parameters:\n" "\n" "flags\n" " A combination of enums.DiffOption constants.\n" "\n" "context_lines\n" " The number of unchanged lines that define the boundary of a hunk\n" " (and to display before and after).\n" "\n" "interhunk_lines\n" " The maximum number of unchanged lines between hunk boundaries before\n" " the hunks will be merged into a one.\n"); PyObject * Tree_diff_to_workdir(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; int err; char *keywords[] = {"flags", "context_lines", "interhunk_lines", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|IHH", keywords, &opts.flags, &opts.context_lines, &opts.interhunk_lines)) return NULL; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load err = git_diff_tree_to_workdir(&diff, self->repo->repo, self->tree, &opts); if (err < 0) return Error_set(err); return wrap_diff(diff, self->repo); } PyDoc_STRVAR(Tree_diff_to_index__doc__, "diff_to_index(index: Index, flags: enums.DiffOption = enums.DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0) -> Diff\n" "\n" "Show the changes between the index and a given :py:class:`~pygit2.Tree`.\n" "\n" "Parameters:\n" "\n" "index : :py:class:`~pygit2.Index`\n" " The index to diff.\n" "\n" "flags\n" " A combination of enums.DiffOption constants.\n" "\n" "context_lines\n" " The number of unchanged lines that define the boundary of a hunk\n" " (and to display before and after).\n" "\n" "interhunk_lines\n" " The maximum number of unchanged lines between hunk boundaries before\n" " the hunks will be merged into a one.\n"); PyObject * Tree_diff_to_index(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; git_index *index; char *buffer; Py_ssize_t length; PyObject *py_idx; int err; if (!PyArg_ParseTuple(args, "O|IHH", &py_idx, &opts.flags, &opts.context_lines, &opts.interhunk_lines)) return NULL; /* Check whether the first argument is an index. * FIXME Uses duck typing. This would be easy and correct if we had * _pygit2.Index. */ PyObject *pygit2_index = PyObject_GetAttrString(py_idx, "_index"); if (!pygit2_index) { PyErr_SetString(PyExc_TypeError, "argument must be an Index"); return NULL; } Py_DECREF(pygit2_index); /* Get git_index from cffi's pointer */ PyObject *py_idx_ptr = PyObject_GetAttrString(py_idx, "_pointer"); if (!py_idx_ptr) return NULL; /* Here we need to do the opposite conversion from the _pointer getters */ if (PyBytes_AsStringAndSize(py_idx_ptr, &buffer, &length)) goto error; if (length != sizeof(git_index *)) { PyErr_SetString(PyExc_TypeError, "passed value is not a pointer"); goto error; } index = *((git_index **) buffer); /* the "buffer" contains the pointer */ /* Call git_diff_tree_to_index */ if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load err = git_diff_tree_to_index(&diff, self->repo->repo, self->tree, index, &opts); Py_DECREF(py_idx_ptr); if (err < 0) return Error_set(err); return wrap_diff(diff, self->repo); error: Py_DECREF(py_idx_ptr); return NULL; } PyDoc_STRVAR(Tree_diff_to_tree__doc__, "diff_to_tree([tree: Tree, flags: enums.DiffOption = enums.DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, swap: bool = False]) -> Diff\n" "\n" "Show the changes between two trees.\n" "\n" "Parameters:\n" "\n" "tree: :py:class:`~pygit2.Tree`\n" " The tree to diff. If no tree is given the empty tree will be used\n" " instead.\n" "\n" "flags\n" " A combination of enums.DiffOption constants.\n" "\n" "context_lines\n" " The number of unchanged lines that define the boundary of a hunk\n" " (and to display before and after).\n" "\n" "interhunk_lines\n" " The maximum number of unchanged lines between hunk boundaries before\n" " the hunks will be merged into a one.\n" "\n" "swap\n" " Instead of diffing a to b. Diff b to a.\n"); PyObject * Tree_diff_to_tree(Tree *self, PyObject *args, PyObject *kwds) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; git_tree *from, *to = NULL, *tmp; int err, swap = 0; char *keywords[] = {"obj", "flags", "context_lines", "interhunk_lines", "swap", NULL}; Tree *other = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!IHHi", keywords, &TreeType, &other, &opts.flags, &opts.context_lines, &opts.interhunk_lines, &swap)) return NULL; if (Object__load((Object*)self) == NULL) { return NULL; } // Lazy load from = self->tree; if (other) { if (Object__load((Object*)other) == NULL) { return NULL; } // Lazy load to = other->tree; } if (swap > 0) { tmp = from; from = to; to = tmp; } err = git_diff_tree_to_tree(&diff, self->repo->repo, from, to, &opts); if (err < 0) return Error_set(err); return wrap_diff(diff, self->repo); } PySequenceMethods Tree_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)Tree_contains, /* sq_contains */ }; PyMappingMethods Tree_as_mapping = { (lenfunc)Tree_len, /* mp_length */ (binaryfunc)Tree_subscript, /* mp_subscript */ 0, /* mp_ass_subscript */ }; static PyMethodDef Tree_methods[] = { METHOD(Tree, diff_to_tree, METH_VARARGS | METH_KEYWORDS), METHOD(Tree, diff_to_workdir, METH_VARARGS | METH_KEYWORDS), METHOD(Tree, diff_to_index, METH_VARARGS | METH_KEYWORDS), {NULL} }; /* Py2/3 compatible structure * see https://py3c.readthedocs.io/en/latest/ext-types.html#pynumbermethods */ PyNumberMethods Tree_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ 0, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ 0, /* nb_bool (Py2: nb_nonzero) */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ 0, /* nb_or */ 0, /* nb_int */ 0, /* nb_reserved (Py2: nb_long) */ 0, /* nb_float */ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ 0, /* nb_inplace_rshift */ 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ 0, /* nb_floor_divide */ (binaryfunc)Tree_divide, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ 0, /* nb_index */ 0, /* nb_matrix_multiply */ 0, /* nb_inplace_matrix_multiply */ }; PyDoc_STRVAR(Tree__doc__, "Tree objects."); PyTypeObject TreeType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Tree", /* tp_name */ sizeof(Tree), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Object_repr, /* tp_repr */ &Tree_as_number, /* tp_as_number */ &Tree_as_sequence, /* tp_as_sequence */ &Tree_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ Tree__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)Tree_iter, /* tp_iter */ 0, /* tp_iternext */ Tree_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; void TreeIter_dealloc(TreeIter *self) { Py_CLEAR(self->owner); PyObject_Del(self); } PyObject* TreeIter_iternext(TreeIter *self) { const git_tree_entry *entry_src; git_tree_entry *entry; entry_src = git_tree_entry_byindex(self->owner->tree, self->i); if (!entry_src) return NULL; self->i += 1; if (git_tree_entry_dup(&entry, entry_src) < 0) { PyErr_SetNone(PyExc_MemoryError); return NULL; } return treeentry_to_object(entry, self->owner->repo); } PyDoc_STRVAR(TreeIter__doc__, "Tree iterator."); PyTypeObject TreeIterType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.TreeIter", /* tp_name */ sizeof(TreeIter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeIter_dealloc , /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ TreeIter__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)TreeIter_iternext, /* tp_iternext */ }; libgit2-pygit2-d86662e/src/tree.h000066400000000000000000000026531521317216500165400ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_tree_h #define INCLUDE_pygit2_tree_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject* treeentry_to_object(const git_tree_entry *entry, Repository *repo); #endif libgit2-pygit2-d86662e/src/treebuilder.c000066400000000000000000000164451521317216500201060ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "error.h" #include "utils.h" #include "oid.h" #include "treebuilder.h" #include "tree.h" void TreeBuilder_dealloc(TreeBuilder *self) { Py_CLEAR(self->repo); git_treebuilder_free(self->bld); PyObject_Del(self); } PyDoc_STRVAR(TreeBuilder_insert__doc__, "insert(name: str, oid: Oid, attr: FileMode)\n" "\n" "Insert or replace an entry in the treebuilder.\n" "\n" "Parameters:\n" "\n" "attr\n" " Available values are FileMode.BLOB, FileMode.BLOB_EXECUTABLE,\n" " FileMode.TREE, FileMode.LINK and FileMode.COMMIT.\n"); PyObject * TreeBuilder_insert(TreeBuilder *self, PyObject *args) { PyObject *py_oid; size_t len; int err, attr; git_oid oid; const char *fname; if (!PyArg_ParseTuple(args, "sOi", &fname, &py_oid, &attr)) return NULL; len = py_oid_to_git_oid(py_oid, &oid); if (len == 0) return NULL; err = git_treebuilder_insert(NULL, self->bld, fname, &oid, attr); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(TreeBuilder_write__doc__, "write() -> Oid\n" "\n" "Write the tree to the given repository."); PyObject * TreeBuilder_write(TreeBuilder *self) { int err; git_oid oid; err = git_treebuilder_write(&oid, self->bld); if (err < 0) return Error_set(err); return git_oid_to_python(&oid); } PyDoc_STRVAR(TreeBuilder_get__doc__, "get(name: str) -> Object\n" "\n" "Return the Object for the given name, or None if there is not."); PyObject * TreeBuilder_get(TreeBuilder *self, PyObject *py_filename) { PyObject *tvalue; char *filename = pgit_borrow_fsdefault(py_filename, &tvalue); if (filename == NULL) return NULL; const git_tree_entry *entry_src = git_treebuilder_get(self->bld, filename); Py_DECREF(tvalue); if (entry_src == NULL) Py_RETURN_NONE; git_tree_entry *entry; if (git_tree_entry_dup(&entry, entry_src) < 0) { PyErr_SetNone(PyExc_MemoryError); return NULL; } return treeentry_to_object(entry, self->repo); } PyDoc_STRVAR(TreeBuilder_remove__doc__, "remove(name: str)\n" "\n" "Remove an entry from the builder."); PyObject * TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename) { PyObject *tvalue; char *filename = pgit_borrow_fsdefault(py_filename, &tvalue); if (filename == NULL) return NULL; int err = git_treebuilder_remove(self->bld, filename); Py_DECREF(tvalue); if (err) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(TreeBuilder_clear__doc__, "clear()\n" "\n" "Clear all the entries in the builder."); PyObject * TreeBuilder_clear(TreeBuilder *self) { git_treebuilder_clear(self->bld); Py_RETURN_NONE; } static PyMethodDef TreeBuilder_methods[] = { METHOD(TreeBuilder, clear, METH_NOARGS), METHOD(TreeBuilder, get, METH_O), METHOD(TreeBuilder, insert, METH_VARARGS), METHOD(TreeBuilder, remove, METH_O), METHOD(TreeBuilder, write, METH_NOARGS), {NULL} }; Py_ssize_t TreeBuilder_len(TreeBuilder *self) { return (Py_ssize_t)git_treebuilder_entrycount(self->bld); } PyMappingMethods TreeBuilder_as_mapping = { (lenfunc)TreeBuilder_len, /* mp_length */ 0, /* mp_subscript */ 0, /* mp_ass_subscript */ }; PyDoc_STRVAR(TreeBuilder__doc__, "TreeBuilder objects."); PyTypeObject TreeBuilderType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.TreeBuilder", /* tp_name */ sizeof(TreeBuilder), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)TreeBuilder_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ &TreeBuilder_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ TreeBuilder__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ TreeBuilder_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/treebuilder.h000066400000000000000000000031231521317216500201000ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_treebuilder_h #define INCLUDE_pygit2_treebuilder_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" PyObject* TreeBuilder_insert(TreeBuilder *self, PyObject *args); PyObject* TreeBuilder_write(TreeBuilder *self); PyObject* TreeBuilder_remove(TreeBuilder *self, PyObject *py_filename); PyObject* TreeBuilder_clear(TreeBuilder *self); #endif libgit2-pygit2-d86662e/src/types.h000066400000000000000000000131171521317216500167420ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_objects_h #define INCLUDE_pygit2_objects_h #define PY_SSIZE_T_CLEAN #include #include #include #if !(LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 9) #error You need a compatible libgit2 version (1.9.x) #endif /* * Python objects * **/ /* git_repository */ typedef struct { PyObject_HEAD git_repository *repo; PyObject *index; /* It will be None for a bare repository */ PyObject *config; /* It will be None for a bare repository */ int owned; /* _from_c() sometimes means we don't own the C pointer */ } Repository; typedef struct { PyObject_HEAD git_oid oid; } Oid; typedef struct { PyObject_HEAD git_odb *odb; } Odb; typedef struct { PyObject_HEAD git_odb_backend *odb_backend; } OdbBackend; typedef struct { OdbBackend super; } OdbBackendPack; typedef struct { OdbBackend super; } OdbBackendLoose; typedef struct { PyObject_HEAD git_refdb *refdb; } Refdb; typedef struct { PyObject_HEAD git_refdb_backend *refdb_backend; } RefdbBackend; typedef struct { RefdbBackend super; } RefdbFsBackend; typedef struct { PyObject_HEAD git_reference_iterator *iterator; } RefsIterator; #define SIMPLE_TYPE(_name, _ptr_type, _ptr_name) \ typedef struct {\ PyObject_HEAD\ Repository *repo;\ _ptr_type *_ptr_name;\ } _name; #define OBJECT_TYPE(_name, _ptr_type, _ptr_name) \ typedef struct {\ PyObject_HEAD\ Repository *repo;\ _ptr_type *_ptr_name;\ const git_tree_entry *entry;\ } _name; /* git object types * * The structs for some of the object subtypes are identical except for * the type of their object pointers. */ OBJECT_TYPE(Object, git_object, obj) OBJECT_TYPE(Commit, git_commit, commit) OBJECT_TYPE(Tree, git_tree, tree) OBJECT_TYPE(Blob, git_blob, blob) OBJECT_TYPE(Tag, git_tag, tag) SIMPLE_TYPE(Worktree, git_worktree, worktree) /* git_note */ typedef struct { PyObject_HEAD Repository *repo; const char *ref; PyObject *annotated_id; PyObject *id; git_note *note; } Note; typedef struct { PyObject_HEAD Repository *repo; git_note_iterator* iter; char* ref; } NoteIter; /* git_patch */ typedef struct { PyObject_HEAD git_patch *patch; Blob* oldblob; Blob* newblob; } Patch; /* git_diff */ SIMPLE_TYPE(Diff, git_diff, diff) typedef struct { PyObject_HEAD Diff *diff; size_t i; size_t n; } DeltasIter; typedef struct { PyObject_HEAD Diff *diff; size_t i; size_t n; } DiffIter; typedef struct { PyObject_HEAD PyObject *id; char *path; PyObject *raw_path; git_off_t size; uint32_t flags; uint16_t mode; } DiffFile; typedef struct { PyObject_HEAD git_delta_t status; uint32_t flags; uint16_t similarity; uint16_t nfiles; PyObject *old_file; PyObject *new_file; } DiffDelta; typedef struct { PyObject_HEAD Patch *patch; const git_diff_hunk *hunk; size_t idx; size_t n_lines; } DiffHunk; typedef struct { PyObject_HEAD DiffHunk *hunk; const git_diff_line *line; } DiffLine; SIMPLE_TYPE(DiffStats, git_diff_stats, stats); /* git_tree_walk , git_treebuilder*/ SIMPLE_TYPE(TreeBuilder, git_treebuilder, bld) typedef struct { PyObject_HEAD Tree *owner; int i; } TreeIter; /* git_index */ typedef struct { PyObject_HEAD git_index_entry entry; } IndexEntry; /* git_reference, git_reflog */ SIMPLE_TYPE(Walker, git_revwalk, walk) SIMPLE_TYPE(Reference, git_reference, reference) typedef Reference Branch; typedef struct { PyObject_HEAD git_signature *signature; PyObject *oid_old; PyObject *oid_new; char *message; } RefLogEntry; typedef struct { PyObject_HEAD git_reflog *reflog; size_t i; size_t size; } RefLogIter; /* git_revspec */ typedef struct { PyObject_HEAD PyObject *from; PyObject *to; unsigned int flags; } RevSpec; /* git_signature */ typedef struct { PyObject_HEAD Object *obj; const git_signature *signature; char *encoding; } Signature; /* git_mailmap */ typedef struct { PyObject_HEAD git_mailmap *mailmap; } Mailmap; typedef struct { PyObject_HEAD PyObject *commit_id; char *message; } Stash; typedef struct { PyObject_HEAD const git_filter_source *src; } FilterSource; #endif libgit2-pygit2-d86662e/src/utils.c000066400000000000000000000141511521317216500167300ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "utils.h" extern PyTypeObject ReferenceType; extern PyTypeObject TreeType; extern PyTypeObject CommitType; extern PyTypeObject BlobType; extern PyTypeObject TagType; /** * Attempt to convert a C string to a Python string with the given encoding. * If the conversion fails, return a fallback string. */ PyObject * to_unicode_safe(const char *value, const char *encoding) { PyObject *py_str; if (!value) { py_str = PyUnicode_FromString("None"); } else { py_str = to_unicode(value, encoding, "replace"); if (!py_str) { assert(PyErr_Occurred()); py_str = PyUnicode_FromString("(error)"); PyErr_Clear(); } } assert(!PyErr_Occurred()); assert(py_str); return py_str; } char* pgit_borrow_fsdefault(PyObject *value, PyObject **tvalue) { PyObject *str = PyOS_FSPath(value); if (str == NULL) { return NULL; } PyObject *bytes = PyUnicode_EncodeFSDefault(str); if (bytes == NULL) { return NULL; } *tvalue = bytes; return PyBytes_AS_STRING(bytes); } /** * Return a pointer to the underlying C string in 'value'. The pointer is * guaranteed by 'tvalue', decrease its refcount when done with the string. */ const char* pgit_borrow_encoding(PyObject *value, const char *encoding, const char *errors, PyObject **tvalue) { PyObject *py_value = NULL; PyObject *py_str = NULL; py_value = PyOS_FSPath(value); if (py_value == NULL) { Error_type_error("unexpected %.200s", value); return NULL; } // Get new PyBytes reference from value if (PyUnicode_Check(py_value)) { // Text string py_str = PyUnicode_AsEncodedString( py_value, encoding ? encoding : "utf-8", errors ? errors : "strict" ); Py_DECREF(py_value); if (py_str == NULL) return NULL; } else if (PyBytes_Check(py_value)) { // Byte string py_str = py_value; } else { // Type error Error_type_error("unexpected %.200s", value); Py_DECREF(py_value); return NULL; } // Borrow c string from the new PyBytes reference char *c_str = PyBytes_AsString(py_str); if (c_str == NULL) { Py_DECREF(py_str); return NULL; } // Return the borrowed c string and the new PyBytes reference *tvalue = py_str; return c_str; } /** * Return a borrowed c string with the representation of the given Unicode or * Bytes object: * - If value is Unicode return the UTF-8 representation * - If value is Bytes return the raw sttring * In both cases the returned string is owned by value and must not be * modified, nor freed. */ const char* pgit_borrow(PyObject *value) { if (PyUnicode_Check(value)) { // Text string return PyUnicode_AsUTF8(value); } else if (PyBytes_Check(value)) { // Byte string return PyBytes_AsString(value); } // Type error Error_type_error("unexpected %.200s", value); return NULL; } char* pgit_strdup(PyObject *value) { const char *str; char *copy; size_t len; if (PyUnicode_Check(value)) { str = PyUnicode_AsUTF8(value); if (str == NULL) return NULL; len = strlen(str); } else { Error_type_error("unexpected %.200s", value); return NULL; } copy = malloc(len + 1); if (copy == NULL) { PyErr_NoMemory(); return NULL; } memcpy(copy, str, len + 1); return copy; } static git_otype py_type_to_git_type(PyTypeObject *py_type) { if (py_type == &CommitType) return GIT_OBJECT_COMMIT; else if (py_type == &TreeType) return GIT_OBJECT_TREE; else if (py_type == &BlobType) return GIT_OBJECT_BLOB; else if (py_type == &TagType) return GIT_OBJECT_TAG; PyErr_SetString(PyExc_ValueError, "invalid target type"); return GIT_OBJECT_INVALID; /* -1 */ } git_otype py_object_to_otype(PyObject *py_type) { long value; if (py_type == Py_None) return GIT_OBJECT_ANY; if (PyLong_Check(py_type)) { value = PyLong_AsLong(py_type); if (value == -1 && PyErr_Occurred()) return GIT_OBJECT_INVALID; /* TODO Check whether the value is a valid value */ return (git_otype)value; } if (PyType_Check(py_type)) return py_type_to_git_type((PyTypeObject *) py_type); PyErr_SetString(PyExc_ValueError, "invalid target type"); return GIT_OBJECT_INVALID; /* -1 */ } /** * Convert an integer to a reference to an IntEnum or IntFlag in pygit2.enums. */ PyObject * pygit2_enum(PyObject *enum_type, int value) { if (!enum_type) { PyErr_SetString(PyExc_TypeError, "an enum has not been cached in _pygit2.cache_enums()"); return NULL; } PyObject *enum_instance = PyObject_CallFunction(enum_type, "(i)", value); return enum_instance; } libgit2-pygit2-d86662e/src/utils.h000066400000000000000000000115561521317216500167430ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_utils_h #define INCLUDE_pygit2_utils_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" #ifdef __GNUC__ # define PYGIT2_FN_UNUSED __attribute__((unused)) #else # define PYGIT2_FN_UNUSED #endif #if defined(PYPY_VERSION) #define Py_FileSystemDefaultEncodeErrors "surrogateescape" #endif #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #define CHECK_REFERENCE(self)\ if (self->reference == NULL) {\ PyErr_SetString(GitError, "deleted reference");\ return NULL;\ } #define CHECK_REFERENCE_INT(self)\ if (self->reference == NULL) {\ PyErr_SetString(GitError, "deleted reference");\ return -1;\ } /* Utilities */ #define to_unicode(x, encoding, errors) to_unicode_n(x, strlen(x), encoding, errors) PyObject *to_unicode_safe(const char *value, const char *encoding); PYGIT2_FN_UNUSED Py_LOCAL_INLINE(PyObject*) to_unicode_n(const char *value, size_t len, const char *encoding, const char *errors) { if (encoding == NULL) { encoding = "utf-8"; // Default to UTF-8 /* If the encoding is not explicit, it may not be UTF-8, so it is not * safe to decode it strictly. This is rare in the wild, but does * occur in old commits to git itself (e.g. c31820c2). * https://github.com/libgit2/pygit2/issues/77 */ if (errors == NULL) { errors = "replace"; } } return PyUnicode_Decode(value, len, encoding, errors); } #define value_or_default(x, _default) ((x) == NULL ? (_default) : (x)) const char* pgit_borrow(PyObject *value); const char* pgit_borrow_encoding(PyObject *value, const char *encoding, const char *errors, PyObject **tvalue); char* pgit_borrow_fsdefault(PyObject *value, PyObject **tvalue); char* pgit_strdup(PyObject *value); //PyObject * get_pylist_from_git_strarray(git_strarray *strarray); //int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist); git_otype py_object_to_otype(PyObject *py_type); /* Enum utilities (pygit2.enums) */ PyObject *pygit2_enum(PyObject *enum_type, int value); /* Helpers to make shorter PyMethodDef and PyGetSetDef blocks */ #define METHOD(type, name, args)\ {#name, (PyCFunction) type ## _ ## name, args, type ## _ ## name ## __doc__} #define GETTER(type, attr)\ { #attr,\ (getter) type ## _ ## attr ## __get__,\ NULL,\ type ## _ ## attr ## __doc__,\ NULL} #define GETSET(type, attr)\ { #attr,\ (getter) type ## _ ## attr ## __get__,\ (setter) type ## _ ## attr ## __set__,\ type ## _ ## attr ## __doc__,\ NULL} #define MEMBER(type, attr, attr_type, docstr)\ {#attr, attr_type, offsetof(type, attr), 0, PyDoc_STR(docstr)} #define RMEMBER(type, attr, attr_type, docstr)\ {#attr, attr_type, offsetof(type, attr), READONLY, PyDoc_STR(docstr)} /* Helpers to make type init shorter. */ #define INIT_TYPE(type, base, new) \ type.tp_base = base; \ type.tp_new = new; \ if (PyType_Ready(&type) < 0) return NULL; #define ADD_TYPE(module, type) \ Py_INCREF(& type ## Type);\ if (PyModule_AddObject(module, #type, (PyObject*) & type ## Type) == -1)\ return NULL; #define ADD_EXC(m, name, base)\ name = PyErr_NewException("_pygit2." #name, base, NULL);\ if (name == NULL) goto fail;\ Py_INCREF(name);\ if (PyModule_AddObject(m, #name, name)) {\ Py_DECREF(name);\ goto fail;\ } #define ADD_CONSTANT_INT(m, name) \ if (PyModule_AddIntConstant(m, #name, name) == -1) return NULL; #define ADD_CONSTANT_STR(m, name) \ if (PyModule_AddStringConstant(m, #name, name) == -1) return NULL; #endif libgit2-pygit2-d86662e/src/walker.c000066400000000000000000000152041521317216500170550ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include "error.h" #include "object.h" #include "oid.h" #include "tree.h" #include "utils.h" #include "walker.h" extern PyTypeObject CommitType; void Walker_dealloc(Walker *self) { Py_CLEAR(self->repo); git_revwalk_free(self->walk); PyObject_Del(self); } PyDoc_STRVAR(Walker_hide__doc__, "hide(oid: Oid)\n" "\n" "Mark a commit (and its ancestors) uninteresting for the output."); PyObject * Walker_hide(Walker *self, PyObject *py_hex) { int err; git_oid oid; err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); if (err < 0) return NULL; err = git_revwalk_hide(self->walk, &oid); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Walker_push__doc__, "push(oid: Oid)\n" "\n" "Mark a commit to start traversal from."); PyObject * Walker_push(Walker *self, PyObject *py_hex) { int err; git_oid oid; err = py_oid_to_git_oid_expand(self->repo->repo, py_hex, &oid); if (err < 0) return NULL; err = git_revwalk_push(self->walk, &oid); if (err < 0) return Error_set(err); Py_RETURN_NONE; } PyDoc_STRVAR(Walker_sort__doc__, "sort(mode: enums.SortMode)\n" "\n" "Change the sorting mode (this resets the walker)."); PyObject * Walker_sort(Walker *self, PyObject *py_sort_mode) { long sort_mode; sort_mode = PyLong_AsLong(py_sort_mode); if (sort_mode == -1 && PyErr_Occurred()) return NULL; git_revwalk_sorting(self->walk, (unsigned int)sort_mode); Py_RETURN_NONE; } PyDoc_STRVAR(Walker_reset__doc__, "reset()\n" "\n" "Reset the walking machinery for reuse."); PyObject * Walker_reset(Walker *self) { git_revwalk_reset(self->walk); Py_RETURN_NONE; } PyDoc_STRVAR(Walker_simplify_first_parent__doc__, "simplify_first_parent()\n" "\n" "Simplify the history by first-parent."); PyObject * Walker_simplify_first_parent(Walker *self) { git_revwalk_simplify_first_parent(self->walk); Py_RETURN_NONE; } PyObject * Walker_iter(Walker *self) { Py_INCREF(self); return (PyObject*)self; } PyObject * Walker_iternext(Walker *self) { int err; git_commit *commit; git_oid oid; Py_BEGIN_ALLOW_THREADS err = git_revwalk_next(&oid, self->walk); Py_END_ALLOW_THREADS if (err < 0) return Error_set(err); err = git_commit_lookup(&commit, self->repo->repo, &oid); if (err < 0) return Error_set(err); return wrap_object((git_object*)commit, self->repo, NULL); } static PyMethodDef Walker_methods[] = { METHOD(Walker, hide, METH_O), METHOD(Walker, push, METH_O), METHOD(Walker, reset, METH_NOARGS), METHOD(Walker, simplify_first_parent, METH_NOARGS), METHOD(Walker, sort, METH_O), {NULL} }; PyDoc_STRVAR(Walker__doc__, "Revision walker."); PyTypeObject WalkerType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Walker", /* tp_name */ sizeof(Walker), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Walker_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Walker__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)Walker_iter, /* tp_iter */ (iternextfunc)Walker_iternext, /* tp_iternext */ Walker_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; libgit2-pygit2-d86662e/src/walker.h000066400000000000000000000032421521317216500170610ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_walker_h #define INCLUDE_pygit2_walker_h #define PY_SSIZE_T_CLEAN #include #include #include "types.h" void Walker_dealloc(Walker *self); PyObject* Walker_hide(Walker *self, PyObject *py_hex); PyObject* Walker_push(Walker *self, PyObject *py_hex); PyObject* Walker_sort(Walker *self, PyObject *py_sort_mode); PyObject* Walker_reset(Walker *self); PyObject* Walker_iter(Walker *self); PyObject* Walker_iternext(Walker *self); #endif libgit2-pygit2-d86662e/src/wildmatch.c000066400000000000000000000224201521317216500175420ustar00rootroot00000000000000/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. * * Do shell-style pattern matching for ?, \, [], and * characters. * It is 8bit clean. * * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. * Rich $alz is now . * * Modified by Wayne Davison to special-case '/' matching, to make '**' * work differently than '*', and to fix the character-class code. * * Imported from git.git. */ #include #include #include "wildmatch.h" #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 #define GIT_ALPHA 0x04 #define GIT_GLOB_SPECIAL 0x08 #define GIT_REGEX_SPECIAL 0x10 #define GIT_PATHSPEC_MAGIC 0x20 #define GIT_CNTRL 0x40 #define GIT_PUNCT 0x80 enum { S = GIT_SPACE, A = GIT_ALPHA, D = GIT_DIGIT, G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */ P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */ X = GIT_CNTRL, U = GIT_PUNCT, Z = GIT_CNTRL | GIT_SPACE }; static const unsigned char sane_ctype[256] = { X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */ /* Nothing in the 128.. range */ }; #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) typedef unsigned char uchar; /* What character marks an inverted character class? */ #define NEGATE_CLASS '!' #define NEGATE_CLASS2 '^' #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \ && *(class) == *(litmatch) \ && strncmp((char*)class, litmatch, len) == 0) #if defined STDC_HEADERS || !defined isascii # define ISASCII(c) 1 #else # define ISASCII(c) isascii(c) #endif #ifdef isblank # define ISBLANK(c) (ISASCII(c) && isblank(c)) #else # define ISBLANK(c) ((c) == ' ' || (c) == '\t') #endif #ifdef isgraph # define ISGRAPH(c) (ISASCII(c) && isgraph(c)) #else # define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c)) #endif #define ISPRINT(c) (ISASCII(c) && isprint(c)) #define ISDIGIT(c) (ISASCII(c) && isdigit(c)) #define ISALNUM(c) (ISASCII(c) && isalnum(c)) #define ISALPHA(c) (ISASCII(c) && isalpha(c)) #define ISCNTRL(c) (ISASCII(c) && iscntrl(c)) #define ISLOWER(c) (ISASCII(c) && islower(c)) #define ISPUNCT(c) (ISASCII(c) && ispunct(c)) #define ISSPACE(c) (ISASCII(c) && isspace(c)) #define ISUPPER(c) (ISASCII(c) && isupper(c)) #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) /* Match pattern "p" against "text" */ static int dowild(const uchar *p, const uchar *text, unsigned int flags) { uchar p_ch; const uchar *pattern = p; for ( ; (p_ch = *p) != '\0'; text++, p++) { int matched, match_slash, negated; uchar t_ch, prev_ch; if ((t_ch = *text) == '\0' && p_ch != '*') return WM_ABORT_ALL; if ((flags & WM_CASEFOLD) && ISUPPER(t_ch)) t_ch = tolower(t_ch); if ((flags & WM_CASEFOLD) && ISUPPER(p_ch)) p_ch = tolower(p_ch); switch (p_ch) { case '\\': /* Literal match with following character. Note that the test * in "default" handles the p[1] == '\0' failure case. */ p_ch = *++p; /* FALLTHROUGH */ default: if (t_ch != p_ch) return WM_NOMATCH; continue; case '?': /* Match anything but '/'. */ if ((flags & WM_PATHNAME) && t_ch == '/') return WM_NOMATCH; continue; case '*': if (*++p == '*') { const uchar *prev_p = p - 2; while (*++p == '*') {} if (!(flags & WM_PATHNAME)) /* without WM_PATHNAME, '*' == '**' */ match_slash = 1; else if ((prev_p < pattern || *prev_p == '/') && (*p == '\0' || *p == '/' || (p[0] == '\\' && p[1] == '/'))) { /* * Assuming we already match 'foo/' and are at * , just assume it matches * nothing and go ahead match the rest of the * pattern with the remaining string. This * helps make foo/<*><*>/bar (<> because * otherwise it breaks C comment syntax) match * both foo/bar and foo/a/bar. */ if (p[0] == '/' && dowild(p + 1, text, flags) == WM_MATCH) return WM_MATCH; match_slash = 1; } else /* WM_PATHNAME is set */ match_slash = 0; } else /* without WM_PATHNAME, '*' == '**' */ match_slash = flags & WM_PATHNAME ? 0 : 1; if (*p == '\0') { /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ if (!match_slash) { if (strchr((char*)text, '/') != NULL) return WM_NOMATCH; } return WM_MATCH; } else if (!match_slash && *p == '/') { /* * _one_ asterisk followed by a slash * with WM_PATHNAME matches the next * directory */ const char *slash = strchr((char*)text, '/'); if (!slash) return WM_NOMATCH; text = (const uchar*)slash; /* the slash is consumed by the top-level for loop */ break; } while (1) { if (t_ch == '\0') break; /* * Try to advance faster when an asterisk is * followed by a literal. We know in this case * that the string before the literal * must belong to "*". * If match_slash is false, do not look past * the first slash as it cannot belong to '*'. */ if (!is_glob_special(*p)) { p_ch = *p; if ((flags & WM_CASEFOLD) && ISUPPER(p_ch)) p_ch = tolower(p_ch); while ((t_ch = *text) != '\0' && (match_slash || t_ch != '/')) { if ((flags & WM_CASEFOLD) && ISUPPER(t_ch)) t_ch = tolower(t_ch); if (t_ch == p_ch) break; text++; } if (t_ch != p_ch) return WM_NOMATCH; } if ((matched = dowild(p, text, flags)) != WM_NOMATCH) { if (!match_slash || matched != WM_ABORT_TO_STARSTAR) return matched; } else if (!match_slash && t_ch == '/') return WM_ABORT_TO_STARSTAR; t_ch = *++text; } return WM_ABORT_ALL; case '[': p_ch = *++p; #ifdef NEGATE_CLASS2 if (p_ch == NEGATE_CLASS2) p_ch = NEGATE_CLASS; #endif /* Assign literal 1/0 because of "matched" comparison. */ negated = p_ch == NEGATE_CLASS ? 1 : 0; if (negated) { /* Inverted character class. */ p_ch = *++p; } prev_ch = 0; matched = 0; do { if (!p_ch) return WM_ABORT_ALL; if (p_ch == '\\') { p_ch = *++p; if (!p_ch) return WM_ABORT_ALL; if (t_ch == p_ch) matched = 1; } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { p_ch = *++p; if (p_ch == '\\') { p_ch = *++p; if (!p_ch) return WM_ABORT_ALL; } if (t_ch <= p_ch && t_ch >= prev_ch) matched = 1; else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) { uchar t_ch_upper = toupper(t_ch); if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch) matched = 1; } p_ch = 0; /* This makes "prev_ch" get set to 0. */ } else if (p_ch == '[' && p[1] == ':') { const uchar *s; int i; for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ if (!p_ch) return WM_ABORT_ALL; i = (int)(p - s - 1); if (i < 0 || p[-1] != ':') { /* Didn't find ":]", so treat like a normal set. */ p = s - 2; p_ch = '['; if (t_ch == p_ch) matched = 1; continue; } if (CC_EQ(s,i, "alnum")) { if (ISALNUM(t_ch)) matched = 1; } else if (CC_EQ(s,i, "alpha")) { if (ISALPHA(t_ch)) matched = 1; } else if (CC_EQ(s,i, "blank")) { if (ISBLANK(t_ch)) matched = 1; } else if (CC_EQ(s,i, "cntrl")) { if (ISCNTRL(t_ch)) matched = 1; } else if (CC_EQ(s,i, "digit")) { if (ISDIGIT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "graph")) { if (ISGRAPH(t_ch)) matched = 1; } else if (CC_EQ(s,i, "lower")) { if (ISLOWER(t_ch)) matched = 1; } else if (CC_EQ(s,i, "print")) { if (ISPRINT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "punct")) { if (ISPUNCT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "space")) { if (ISSPACE(t_ch)) matched = 1; } else if (CC_EQ(s,i, "upper")) { if (ISUPPER(t_ch)) matched = 1; else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) matched = 1; } else if (CC_EQ(s,i, "xdigit")) { if (ISXDIGIT(t_ch)) matched = 1; } else /* malformed [:class:] string */ return WM_ABORT_ALL; p_ch = 0; /* This makes "prev_ch" get set to 0. */ } else if (t_ch == p_ch) matched = 1; } while (prev_ch = p_ch, (p_ch = *++p) != ']'); if (matched == negated || ((flags & WM_PATHNAME) && t_ch == '/')) return WM_NOMATCH; continue; } } return *text ? WM_NOMATCH : WM_MATCH; } /* Match the "pattern" against the "text" string. */ int wildmatch(const char *pattern, const char *text, unsigned int flags) { return dowild((const uchar*)pattern, (const uchar*)text, flags); } libgit2-pygit2-d86662e/src/wildmatch.h000066400000000000000000000007651521317216500175570ustar00rootroot00000000000000/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #ifndef INCLUDE_wildmatch_h__ #define INCLUDE_wildmatch_h__ #define WM_CASEFOLD 1 #define WM_PATHNAME 2 #define WM_NOMATCH 1 #define WM_MATCH 0 #define WM_ABORT_ALL -1 #define WM_ABORT_TO_STARSTAR -2 int wildmatch(const char *pattern, const char *text, unsigned int flags); #endif libgit2-pygit2-d86662e/src/worktree.c000066400000000000000000000137211521317216500174340ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #define PY_SSIZE_T_CLEAN #include #include #include "error.h" #include "utils.h" #include "types.h" #include "worktree.h" PyDoc_STRVAR(Worktree_name__doc__, "Gets name worktree\n"); PyObject * Worktree_name__get__(Worktree *self) { return to_unicode(git_worktree_name(self->worktree), NULL, NULL); } PyDoc_STRVAR(Worktree_path__doc__, "Gets path worktree\n"); PyObject * Worktree_path__get__(Worktree *self) { return to_unicode(git_worktree_path(self->worktree), NULL, NULL); } PyDoc_STRVAR(Worktree_is_prunable__doc__, "Is the worktree prunable with the given set of flags?\n"); PyObject * Worktree_is_prunable__get__(Worktree *self, PyObject *args) { if (git_worktree_is_prunable(self->worktree, 0) > 0) Py_RETURN_TRUE; Py_RETURN_FALSE; } PyDoc_STRVAR(Worktree_prune__doc__, "prune(force=False)\n" "\n" "Prune a worktree object."); PyObject * Worktree_prune(Worktree *self, PyObject *args) { int err, force = 0; git_worktree_prune_options prune_opts; if (!PyArg_ParseTuple(args, "|i", &force)) return NULL; git_worktree_prune_options_init(&prune_opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION); prune_opts.flags = force & (GIT_WORKTREE_PRUNE_VALID | GIT_WORKTREE_PRUNE_LOCKED); err = git_worktree_prune(self->worktree, &prune_opts); if (err < 0) return Error_set(err); Py_RETURN_NONE; } static void Worktree_dealloc(Worktree *self) { Py_CLEAR(self->repo); git_worktree_free(self->worktree); PyObject_Del(self); } static PyMethodDef Worktree_methods[] = { METHOD(Worktree, prune, METH_VARARGS), {NULL} }; PyGetSetDef Worktree_getseters[] = { GETTER(Worktree, path), GETTER(Worktree, name), GETTER(Worktree, is_prunable), {NULL} }; PyDoc_STRVAR(Worktree__doc__, "Worktree object."); PyTypeObject WorktreeType = { PyVarObject_HEAD_INIT(NULL, 0) "_pygit2.Worktree", /* tp_name */ sizeof(Worktree), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Worktree_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ Worktree__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Worktree_methods, /* tp_methods */ 0, /* tp_members */ Worktree_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyObject * wrap_worktree(Repository* repo, git_worktree* wt) { Worktree* py_wt = NULL; py_wt = PyObject_New(Worktree, &WorktreeType); if (py_wt == NULL) { PyErr_NoMemory(); return NULL; } py_wt->repo = repo; Py_INCREF(repo); py_wt->worktree = wt; return (PyObject*) py_wt; } libgit2-pygit2-d86662e/src/worktree.h000066400000000000000000000026511521317216500174410ustar00rootroot00000000000000/* * Copyright 2010-2025 The pygit2 contributors * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * as published by the Free Software Foundation. * * In addition to the permissions in the GNU General Public License, * the authors give you unlimited permission to link the compiled * version of this file into combinations with other programs, * and to distribute those combinations without any restriction * coming from the use of this file. (The General Public License * restrictions do apply in other respects; for example, they cover * modification of the file, and distribution when not linked into * a combined executable.) * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef INCLUDE_pygit2_worktree_h #define INCLUDE_pygit2_worktree_h #define PY_SSIZE_T_CLEAN #include #include #include PyObject* wrap_worktree(Repository* repo, git_worktree* wt); #endif libgit2-pygit2-d86662e/test/000077500000000000000000000000001521317216500156125ustar00rootroot00000000000000libgit2-pygit2-d86662e/test/__init__.py000066400000000000000000000027471521317216500177350ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # Move the current directory to the end of the list of import paths. This way # we avoid the "ModuleNotFoundError: No module named 'pygit2._pygit2'" error # when running the test suite in Continuous Integration. import os import sys cwd = os.getcwd() if cwd in sys.path: sys.path.remove(cwd) sys.path.append(cwd) libgit2-pygit2-d86662e/test/conftest.py000066400000000000000000000057361521317216500200240ustar00rootroot00000000000000from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Repository from . import utils @pytest.fixture(scope='session', autouse=True) def global_git_config() -> None: # Do not use global config for better test reproducibility. # https://github.com/libgit2/pygit2/issues/989 levels = [ pygit2.enums.ConfigLevel.GLOBAL, pygit2.enums.ConfigLevel.XDG, pygit2.enums.ConfigLevel.SYSTEM, ] for level in levels: pygit2.settings.search_path[level] = '' @pytest.fixture def pygit2_empty_key() -> tuple[Path, str, str]: path = Path(__file__).parent / 'keys' / 'pygit2_empty' return path, f'{path}.pub', 'empty' @pytest.fixture def barerepo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('barerepo.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def barerepo_path(tmp_path: Path) -> Generator[tuple[Repository, Path], None, None]: with utils.TemporaryRepository('barerepo.zip', tmp_path) as path: yield pygit2.Repository(path), path @pytest.fixture def blameflagsrepo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('blameflagsrepo.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def dirtyrepo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('dirtyrepo.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def emptyrepo( barerepo: Repository, tmp_path: Path ) -> Generator[Repository, None, None]: with utils.TemporaryRepository('emptyrepo.zip', tmp_path) as path: repo = pygit2.Repository(path) repo.remotes.create('origin', barerepo.path) yield repo @pytest.fixture def encodingrepo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('encoding.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def mergerepo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('testrepoformerging.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def testrepo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('testrepo.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def testrepo_path(tmp_path: Path) -> Generator[tuple[Repository, Path], None, None]: with utils.TemporaryRepository('testrepo.zip', tmp_path) as path: yield pygit2.Repository(path), path @pytest.fixture def testrepopacked(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('testrepopacked.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def gpgsigned(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('gpgsigned.zip', tmp_path) as path: yield pygit2.Repository(path) libgit2-pygit2-d86662e/test/data/000077500000000000000000000000001521317216500165235ustar00rootroot00000000000000libgit2-pygit2-d86662e/test/data/barerepo.zip000066400000000000000000000453731521317216500210620ustar00rootroot00000000000000PK y‘:F barerepo/UT VuÆTÜàeaux ééPK y‘:Fbarerepo/objects/UT VuÆTÜàeaux ééPK y‘:Fbarerepo/objects/05/UT VuÆTÜàeaux ééPK y‘:F´aWßšš:barerepo/objects/05/6e626e51b1fc1ee2182800e399ed8d84c8f082UT VuÆTáàeaux ééx�Ž[ Â0ýÎ)ò/”ìæ "^e“l´`Ú¢ØÛ[ð~ÎÀÀäµµyHD{�YÕ ›P#*3)¤à —äÀBöP�*,6ê¼ ™s&ãkJè hö@žŒ+à,Ƙ‚ad—UÒ‚^ã±vYøIËÂòò¡÷Ì}úñºßî�æç”×v• ­Žˆ¨Až(%{Lþ3?úm—$¾ŽG¹PK y‘:Fbarerepo/objects/10/UT VuÆTÜàeaux ééPK y‘:F‚oTÑÑ:barerepo/objects/10/2374bdb1e8efca5e66cded18fd8f30571654a5UT VuÆTáàeaux ééxíR;N1 ¥æï£)�((éé8�“X#ï&ñ�Ø#ííñ€ÄVp 7~_YNUž_žÞtpƒìÓŠV˜b Æ¶ kŸllK*ÁÉÒ7p &¡†®ÝÛ ,£i�qÛÃDú!Å»Á •R„€í;€Ñhëªòá·0àAöãwhõÝœV¼ Öyª)gŸ'ßpñiŠâ1ggþ™08y[ñ¥‘£'áJ³`óÄcÜ—hI£«4 |RÏæ¡Ÿ†ßo±þ�ýßéþ3ëã'ÒÚ2PK y‘:Fbarerepo/objects/11/UT VuÆTÜàeaux ééPK y‘:F+‹ÿbˆˆ:barerepo/objects/11/19926b06311143cab273f0af84eae77f5b3462UT VuÆTáàeaux ééx+)JMU045f040031QHd(ï`œÿúɦÈe>A³f½ “K¹»&©—œ_P‰OEÃôk¢÷¨Û8œô*Êy¾Óþv”´™‰($3$úß\ÌéýMâÕäþÿøj¥B~̺5:'¿(5—A@¹dïÆïOÅ¥�}+ñ·ß \,d)åíA»PK y‘:Fbarerepo/objects/18/UT VuÆTÜàeaux ééPK y‘:FËø–»cc:barerepo/objects/18/e2d2e9db075f9eb43bcb2daa65a2867d29a15eUT VuÆTáàeaux ééx+)JMU°0c040031QHd(ï`œÿúɦÈe>A³f½ “K¹»*™Ä0ýšèÃ=ê6'½Š2Džï´¿%mfb É ‰þ7sz“x5¹ÿÿ¾Z©�³.q>%‡PK y‘:Fbarerepo/objects/19/UT VuÆTÜàeaux ééPK y‘:F©—Ò+ˆˆ:barerepo/objects/19/bf31524643d743751b09cf719456914bbd8bd5UT VuÆTáàeaux ééx+)JMU045f040031QHd(ï`œÿúɦÈe>A³f½ “K¹»&©—œ_P‰OEÃôk¢÷¨Û8œô*Êy¾Óþv”´™‰($3$úß\ÌéýMâÕäþÿøj¥B~̺5:³ ¸4—A@¹dïÆïOÅ¥�}+ñ·ß \,d)ç@AÊPK y‘:Fbarerepo/objects/1a/UT VuÆTÜàeaux ééPK y‘:Fiõkk:barerepo/objects/1a/f81f24e48f92009ca02a874edb6151c71f60deUT VuÆTáàeaux ééx+)JMU042`040031QHd(ï`œÿúɦÈe>A³f½ “K¹»&©—œ_P‰OEÃôk¢÷¨Û8œô*Êy¾Óþv”´™‰($3$úß\ÌéýMâÕäþÿøj¥B~̺h3ÙPK C^bBbarerepo/objects/29/UT ]Ù1QÜàeaux ééPK C^bBQuvF:barerepo/objects/29/7efb891a47de80be0cfe9c639e4b8c9b450989UT ]Ù1Qáàeaux ééxKÊÉOR04fHÖOQHÎÏ+IÍ+)æGN²PK Q‘:Fbarerepo/objects/2a/UT  uÆTÜàeaux ééPK Q‘:FwŸ3:cc:barerepo/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8UT  uÆTáàeaux ééx+)JMU°0c040031QHdÈRçiÜÓ°uêÄ—¯¾~Á$àÀá©PÉ$†é×DîQ·q8éU”!ò|§ýí(i3 PHfHô¿¹˜Óû›Ä«ÉýÿðÕJ…ü˜uµÚ&«PK Q‘:Fbarerepo/objects/2c/UT  uÆTÜàeaux ééPK Q‘:Fñ!ŠÇÇ:barerepo/objects/2c/dae28389c059815e951d0bb9eed6533f61a46bUT  uÆTáàeaux ééx�OIj1ÌY¯è»A´Ö‘ „}Ì!дÚáÌ‚¦ ~~ÆI^�[µPEë<7›ñE:3ØRMu>D ä‹¡Ì s­c¦jqô˜£u”ÔV:/á §;¸’ Ã%ĈH#Vö~4¨Ê]¦µÃK‡óºó ^7==Á{›7î­|éBú~{ã\r˜Mà„QÑÏHáÆÕçÔzá] )ðÛ§ÕSØÿÐT–+ï ?Ú.m¹™‡†EޝZ}s]¶PK Q‘:Fbarerepo/objects/39/UT  uÆTÜàeaux ééPK Q‘:Fß‚ú­ÈÈ:barerepo/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47UT  uÆTáàeaux ééx�O9N1$ö+:G²z|Œ=B„„|ÀG{m-sÈî•öùÌ/ «RªJûº6µàw"p*Äè�µQ¥4'CK^ŠÅb�ö&/NU”Gè´1ØB=ùÅê4)§íì1)ëŠ�gÄ1“1qBn\÷ÄÞ÷AWx9d}€·¶Ô[ø’!ÉÛõ&­½F¯­ƒgœEúÉôϸø¬­g` 9p€ß>)Âøc�jØ.4€+ÝÛà¶]ÎÌ�OÃÆçW)¾øM]=PK C^bBbarerepo/objects/3d/UT ]Ù1QÜàeaux ééPK C^bB®r|”ˆˆ:barerepo/objects/3d/2962987c695a29f1f80b6c3aa4ec046ef44369UT ]Ù1Qáàeaux ééx-ÌA Â0@Q×9Å\À’Œm“@�àÒÉ$T,#ePôô–ÒÝßü§©‚ëÜAÆ“¡�H%aˆ‰û:¦‚”»`{BÛÑ–l½%£ßÉx¸Øš£=¾Tù�OnˆáôæU¸­Tv…BJð·Øó6Íû™`¢ö¶P)³Îm¥ûA¾µ3¿”X]fPK C^bBbarerepo/objects/61/UT ]Ù1QÜàeaux ééPK C^bB3Ùã..:barerepo/objects/61/4fd9a3094bf618ea938fffc00e7d1a54f89ad0UT ]Ù1Qáàeaux ééx+)JMU0²d040031QHaЬûÝ)å~¯aÏ¿9Éó¼{f»rvà‚ ‡PK y‘:Fbarerepo/objects/62/UT VuÆTÜàeaux ééPK y‘:F—ã5ðTT:barerepo/objects/62/cc88a53cfb046fcf603b3aaeb73b8e18215442UT VuÆTáàeaux ééx+)JMU0³`040031Q0·0±05MNL3ò, - MRŒ’ÓÌŒR �’,�ÍMRÌÍSÍ-V[NßÁÎZVýøÖ‹Ó²ØæWžïPK y‘:Fbarerepo/objects/63/UT VuÆTÜàeaux ééPK y‘:FŽ] ü––:barerepo/objects/63/59f8019b0954201a807b766547526173f3cc67UT VuÆTáàeaux ééx¥�;Â0D©}ŠíR "ÿã!D]:`¯×`‰ÄRb�r{ ' ›yÅ< –aȤ›:�•ˆÎy£0®mÂd¹ Ê{ � Ž„“Âh-™Ö{™ ÏXàUF¸Ð2Ø{»æãùº;ÑôÈcéBYiMg: [.8g¸š+ý³ÁúRi#E 4·Ï—ñÇöƒ‹C{PK Q‘:Fbarerepo/objects/6a/UT  uÆTÜàeaux ééPK Q‘:F¿Æýú:barerepo/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395UT  uÆTáàeaux ééxKÊÉOR04fHTHÎÏ+IÍ+)V0æH–pPK Q‘:Fbarerepo/objects/72/UT  uÆTÜàeaux ééPK Q‘:F§È¼Rcc:barerepo/objects/72/abb8755b2cc6c4e40fd9f50f54384d973a2f22UT  uÆTáàeaux ééx+)JMU°0c040031QHdÈRçiÜÓ°uêÄ—¯¾~Á$àÀá©PÉ$†Ö� ï|×$\(œUÜð‰ä¬¥‹k¿™�B2C¢ÿÍÅœÞß$^Mîÿ€¯V*äǬ û–(¯PK y‘:Fbarerepo/objects/77/UT VuÆTÜàeaux ééPK y‘:F³j_:barerepo/objects/77/88019febe4f40259a64c529a9aed561e64ddbdUT VuÆTáàeaux ééxKÊÉOR04aHTHÎÏ+IÍ+)V0VàOL‘PK y‘:Fbarerepo/objects/78/UT VuÆTÜàeaux ééPK y‘:FbÜñ°°:barerepo/objects/78/4855caf26449a1914d2cf62d12b9374d76ae78UT VuÆTáàeaux ééx¥Ž;jC1SëÛ‡­>+ B)â.] •Vä�õddÙ�Ûû éfšaÊè}[`\zZS0q³è�#gkp6xd�J ˜œ§„޹F®^�ó”}Aóâsvb)³nBFK)$[H‰"YŽAåëú¾¶2à6v8ÊïeÀëÝ~ÿü~ù�yÚöC•7@KÆa´ÆÀ³F­Uyœ.ùOCõq“ §1¥Ã°�/×®þp¦NUPK C^bBbarerepo/objects/7f/UT ]Ù1QÜàeaux ééPK C^bB7;>€:barerepo/objects/7f/129fd57e31e935c6d60a0c794efe4e6927664bUT ]Ù1Qáàeaux ééxKÊÉOR04fHTHÎÏ+IÍ+)V0âH”oPK C^bBbarerepo/objects/96/UT ]Ù1QÜàeaux ééPK C^bBΪÝbb:barerepo/objects/96/7fce8df97cc71722d3c2a5930ef3e6f1d27b12UT ]Ù1Qáàeaux ééx+)JMU°0c040031QHd¨šµÎð¥é±k\<•~ÿü2ÕÓ¼¡’I ­Þù®I¸P8ÿª¸áÉYK×~31…d†Dÿ›‹9½¿I¼šÜÿÿ_­TÈ�YI·%ÏPK Q‘:Fbarerepo/objects/97/UT  uÆTÜàeaux ééPK Q‘:F<­ê!:barerepo/objects/97/d615e1bc273c40c94a726814e7b93fdb5a1b36UT  uÆTáàeaux ééxKÊÉOR04fHRHÎÏ+IÍ+)V0âH¡pPK y‘:Fbarerepo/objects/a0/UT VuÆTÜàeaux ééPK y‘:F6nv‰¸¸:barerepo/objects/a0/75f5a7394b0838a9f54dfc511e1a3fbbb3b973UT VuÆTáàeaux ééx¥�1n1E©}Šé(hÆ^�×RE)BGÇìñ˜¬ÖÑâDâö Hœ€îÿW|½/í|ž:XW}Q±b+gd­%³�™œ #QE¶± J*¹�ùN‹ÎØùXG¤˜1úÁ"¥CÌ~Þ2W�“~úg[à0Iƒß6Ã^¯—/·¾»ç·�ãö]—¯iÞ}rlÙ6HˆFî¦]ŸÙ0‡Öõ©-�¯°>ýŸlmþ&P7PK y‘:Fbarerepo/objects/ab/UT VuÆTÜàeaux ééPK y‘:FaL "":barerepo/objects/ab/533997b80705767be3dae8cbb06a0740809f79UT VuÆTáàeaux ééxKÊÉOR0´`pË,*.QðË/IUÐUðputákýpPK y‘:Fbarerepo/objects/c2/UT VuÆTÜàeaux ééPK y‘:FkiÙ••:barerepo/objects/c2/c2f6b06efdb6c1e4b1337811f0629fc0cadbd1UT VuÆTáàeaux ééx+)JMU046c040031Q0·0±05MNL3ò, - MRŒ’ÓÌŒR �’,�ÍMRÌÍSÍ-V[NßÁÎZVýøÖ‹Ó²ØæWBÍH3M5ML4I56KL2HK53JM5LNN6KM²H3·L²03³03N²0g¸QYèÛÁVø¶åǪ·ÝêY/�W@6/¯PK y‘:Fbarerepo/objects/cc/UT VuÆTÜàeaux ééPK y‘:F¦�eβ²:barerepo/objects/cc/ca47fbb26183e71a7a46d165299b84e2e6c0b3UT VuÆTáàeaux ééx�Ž[jÃ0Dû­UÜÿBÐ#’%(%[¹Ò×[ ®š¤»�Iwп™9˜Ò·­²>½� aÅ"IÖ“ŸòÙå’­0Ï6†Ilbã¡®¼£b†�.¦¢}ŠIÞˆÎ9¼ss0|YñÏXúN‚•[}<øV±Ÿþzÿ½|m\×SéÛ'çMˆ:M‘ÞµÑZëqràŸºbbº/u€¾¯\@¿âZÔòmSùPK y‘:Fbarerepo/objects/d8/UT VuÆTÜàeaux ééPK y‘:F¯=Ä%%:barerepo/objects/d8/79714d880671ed84f8aaed8b27fca23ba01f27UT VuÆTáàeaux ééxKÊÉOR02dNMÎÏKQðË/IUÐUðput©3ä…ƒmPK y‘:Fbarerepo/objects/f5/UT VuÆTÜàeaux ééPK y‘:F\_º~¬¬:barerepo/objects/f5/e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87UT VuÆTáàeaux ééx¥Ž½NÄ0©ýÛ#N»kÇq$„Å]GwàØŸE¤$FÆ ñö÷ó ×Í4£IuÛ–NꆧÞ‘iR?³·Wt6ÅYG[8–à1Že˜­ój¾cÃÞ‰¯ƒÌR’*A3ì4!‡\ …ƒšøÛ¿j£Ï%Uú«;�ðÿSéõæ‡;¿Ï/hë²2ÞH¬W'ázAÏ,Ì&ÝO;i˜˜32­µa£²¬0n¦K€PK C^bBbarerepo/objects/info/UT ]Ù1QÜàeaux ééPK C^bBBÖùy66barerepo/objects/info/packsUT ]Ù1Qáàeaux ééP pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.pack PK C^bBbarerepo/objects/pack/UT ]Ù1QÜàeaux ééPKC^bBW‘P· Gbarerepo/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.idxUT ]Ù1Qáàeaux ééû_âŸÌÀÀÀÄ0 F&`"˜ f&³ŒDÜúQá�ïš„ …ó¯Š>‘œµtqí·õÎò Bä®]]ÿöñ£;… ß>È‹mû>«Kç¡÷•¥*ÓJ3n½³zõûP¥Î/�YÏŒäÎë\mÈQœ²á/;ÏÌòå5·¾ç�ùp×7Æû ÐŽ@|ˆ'1χM³mªtEí_æøO蔟RÇæÎ®Û óS8D>ýÐ+¾•ªñn^ù…å2ÆPK C^bB60;ÒHbarerepo/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.packUT ]Ù1Qáàeaux ééPACK– xœ�Œ[ Â0ÿsн€%Ió<ƦÙÔ@Û@\<½‚žÀ¿a`†;$W|Ä€z"eR6hµ‰Î[rPšŠu#–$ðÎ×ÖጂSëíYùûœ~xœ[›¦¶@éŒ÷£Š°“^Jñ±ke¦{qÙ*W\€éÆ�‘¾ËA¼~]?ªxœ340031QHdXï,¯ð'DîÚÕõo?ºS˜ð탼˜!D2‰¡õ£Â;ß5  ç_7|"9kéâÚo¹ô;xœKTHÎÏ+IÍ+)æ ú;xœKRHÎÏ+IÍ+)æ+ûð²›PK y‘:F íAbarerepo/UTVuÆTux ééPK y‘:FíACbarerepo/objects/UTVuÆTux ééPK y‘:FíAŽbarerepo/objects/05/UTVuÆTux ééPK y‘:F´aWßšš:¤�Übarerepo/objects/05/6e626e51b1fc1ee2182800e399ed8d84c8f082UTVuÆTux ééPK y‘:FíAêbarerepo/objects/10/UTVuÆTux ééPK y‘:F‚oTÑÑ:¤�8barerepo/objects/10/2374bdb1e8efca5e66cded18fd8f30571654a5UTVuÆTux ééPK y‘:FíA}barerepo/objects/11/UTVuÆTux ééPK y‘:F+‹ÿbˆˆ:¤�Ëbarerepo/objects/11/19926b06311143cab273f0af84eae77f5b3462UTVuÆTux ééPK y‘:FíAÇbarerepo/objects/18/UTVuÆTux ééPK y‘:FËø–»cc:¤�barerepo/objects/18/e2d2e9db075f9eb43bcb2daa65a2867d29a15eUTVuÆTux ééPK y‘:FíAìbarerepo/objects/19/UTVuÆTux ééPK y‘:F©—Ò+ˆˆ:¤�:barerepo/objects/19/bf31524643d743751b09cf719456914bbd8bd5UTVuÆTux ééPK y‘:FíA6barerepo/objects/1a/UTVuÆTux ééPK y‘:Fiõkk:¤�„barerepo/objects/1a/f81f24e48f92009ca02a874edb6151c71f60deUTVuÆTux ééPK C^bBíAcbarerepo/objects/29/UT]Ù1Qux ééPK C^bBQuvF:¤�±barerepo/objects/29/7efb891a47de80be0cfe9c639e4b8c9b450989UT]Ù1Qux ééPK Q‘:FíAB barerepo/objects/2a/UT uÆTux ééPK Q‘:FwŸ3:cc:¤�� barerepo/objects/2a/d1d3456c5c4a1c9e40aeeddb9cd20b409623c8UT uÆTux ééPK Q‘:FíAg barerepo/objects/2c/UT uÆTux ééPK Q‘:Fñ!ŠÇÇ:¤�µ barerepo/objects/2c/dae28389c059815e951d0bb9eed6533f61a46bUT uÆTux ééPK Q‘:FíAð barerepo/objects/39/UT uÆTux ééPK Q‘:Fß‚ú­ÈÈ:¤�> barerepo/objects/39/a3001fcc2b9541fdcf4be2d662618a5d213f47UT uÆTux ééPK C^bBíAz barerepo/objects/3d/UT]Ù1Qux ééPK C^bB®r|”ˆˆ:¤�È barerepo/objects/3d/2962987c695a29f1f80b6c3aa4ec046ef44369UT]Ù1Qux ééPK y‘:FíAÄbarerepo/objects/55/UTVuÆTux ééPK y‘:F�;|��:¤�barerepo/objects/55/60f04f38a674decf34d16d7c7476642fa03794UTVuÆTux ééPK C^bBíAbarerepo/objects/5f/UT]Ù1Qux ééPK C^bBX^™¨ÈÈ:¤�Ubarerepo/objects/5f/e808e8953c12735680c257f56600cb0de44b10UT]Ù1Qux ééPK C^bBíA‘barerepo/objects/61/UT]Ù1Qux ééPK C^bB3Ùã..:¤�ßbarerepo/objects/61/4fd9a3094bf618ea938fffc00e7d1a54f89ad0UT]Ù1Qux ééPK y‘:FíA�barerepo/objects/62/UTVuÆTux ééPK y‘:F—ã5ðTT:¤�Ïbarerepo/objects/62/cc88a53cfb046fcf603b3aaeb73b8e18215442UTVuÆTux ééPK y‘:FíA—barerepo/objects/63/UTVuÆTux ééPK y‘:FŽ] ü––:¤�åbarerepo/objects/63/59f8019b0954201a807b766547526173f3cc67UTVuÆTux ééPK Q‘:FíAïbarerepo/objects/6a/UT uÆTux ééPK Q‘:F¿Æýú:¤�=barerepo/objects/6a/270c81bc80b59591e0d2e3abd7d03450c0c395UT uÆTux ééPK Q‘:FíAÎbarerepo/objects/72/UT uÆTux ééPK Q‘:F§È¼Rcc:¤�barerepo/objects/72/abb8755b2cc6c4e40fd9f50f54384d973a2f22UT uÆTux ééPK y‘:FíAóbarerepo/objects/77/UTVuÆTux ééPK y‘:F³j_:¤�Abarerepo/objects/77/88019febe4f40259a64c529a9aed561e64ddbdUTVuÆTux ééPK y‘:FíAÓbarerepo/objects/78/UTVuÆTux ééPK y‘:FbÜñ°°:¤�!barerepo/objects/78/4855caf26449a1914d2cf62d12b9374d76ae78UTVuÆTux ééPK C^bBíAEbarerepo/objects/7f/UT]Ù1Qux ééPK C^bB7;>€:¤�“barerepo/objects/7f/129fd57e31e935c6d60a0c794efe4e6927664bUT]Ù1Qux ééPK C^bBíA$barerepo/objects/96/UT]Ù1Qux ééPK C^bBΪÝbb:¤�rbarerepo/objects/96/7fce8df97cc71722d3c2a5930ef3e6f1d27b12UT]Ù1Qux ééPK Q‘:FíAHbarerepo/objects/97/UT uÆTux ééPK Q‘:F<­ê!:¤�–barerepo/objects/97/d615e1bc273c40c94a726814e7b93fdb5a1b36UT uÆTux ééPK y‘:FíA'barerepo/objects/a0/UTVuÆTux ééPK y‘:F6nv‰¸¸:¤�ubarerepo/objects/a0/75f5a7394b0838a9f54dfc511e1a3fbbb3b973UTVuÆTux ééPK y‘:FíA¡barerepo/objects/ab/UTVuÆTux ééPK y‘:FaL "":¤�ïbarerepo/objects/ab/533997b80705767be3dae8cbb06a0740809f79UTVuÆTux ééPK y‘:FíA…barerepo/objects/c2/UTVuÆTux ééPK y‘:FkiÙ••:¤�Óbarerepo/objects/c2/c2f6b06efdb6c1e4b1337811f0629fc0cadbd1UTVuÆTux ééPK y‘:FíAÜbarerepo/objects/cc/UTVuÆTux ééPK y‘:F¦�eβ²:¤�* barerepo/objects/cc/ca47fbb26183e71a7a46d165299b84e2e6c0b3UTVuÆTux ééPK y‘:FíAP!barerepo/objects/d8/UTVuÆTux ééPK y‘:F¯=Ä%%:¤�ž!barerepo/objects/d8/79714d880671ed84f8aaed8b27fca23ba01f27UTVuÆTux ééPK y‘:FíA7"barerepo/objects/f5/UTVuÆTux ééPK y‘:F\_º~¬¬:¤�…"barerepo/objects/f5/e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87UTVuÆTux ééPK C^bBíA¥#barerepo/objects/info/UT]Ù1Qux ééPK C^bBBÖùy66¤�õ#barerepo/objects/info/packsUT]Ù1Qux ééPK C^bBíA€$barerepo/objects/pack/UT]Ù1Qux ééPKC^bBW‘P· G¤�Ð$barerepo/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.idxUT]Ù1Qux ééPK C^bB60;ÒH¤�&barerepo/objects/pack/pack-822653eb59791a6df714f8aa5fbf9f1c1951478e.packUT]Ù1Qux ééPK y‘:FíAš'barerepo/refs/UTVuÆTux ééPK y‘:FíAâ'barerepo/refs/heads/UTVuÆTux ééPK y‘:FÜɳº))¤�0(barerepo/refs/heads/masterUTVuÆTux ééPK y‘:FíA­(barerepo/refs/notes/UTVuÆTux ééPK y‘:F(xò))¤�û(barerepo/refs/notes/commitsUTVuÆTux ééPK C^bBíAy)barerepo/refs/tags/UT]Ù1Qux ééPK C^bBÆeäÂ))¤�Æ)barerepo/refs/tags/rootUT]Ù1Qux ééPK C^bB+is§ ¤�@*barerepo/HEADUT]Ù1Qux ééPKy‘:FX‰ìSa¤�ž*barerepo/configUTVuÆTux ééPKC^bBìÝŽ'PU¤�:+barerepo/packed-refsUT]Ù1Qux ééPKKK Ø+libgit2-pygit2-d86662e/test/data/binaryfilerepo.zip000066400000000000000000000375631521317216500222770ustar00rootroot00000000000000PK p8Gbinaryfilerepo/UT Ã/Vv¶cbux ééPK aR—Tbinaryfilerepo/.git/UT v¶cbv¶cbux ééPK aAbinaryfilerepo/.git/branches/UT €�’Pv¶cbux ééPK €8Gw‘ÿ¦"binaryfilerepo/.git/COMMIT_EDITMSGUT ß/Vv¶cbux ééModified binary file PKï­PBqÑG·†¹binaryfilerepo/.git/configUT aðQv¶cbux éé5�A Ã0 Ïñ+LŽ)Ô=ú’ЃËŽÁ‰‚$úûÊ ½iØÕμãÓ ŒIQâO"Þ@ßÈRh÷sC*7Šh¤ÜÐ /à ªUÊP+cjGEù×f¶/E?—\öÑ4�kOQ4XB¿ºúš‹šuY-¿Ø–„!J˜î?8§$œSar_PKaA7‹?Ibinaryfilerepo/.git/descriptionUT €�’Pv¶cbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK aA+is§binaryfilerepo/.git/HEADUT €�’Pv¶cbux ééref: refs/heads/master PK aR—Tbinaryfilerepo/.git/hooks/UT v¶cbv¶cbux ééPKaA`÷‘X Ä/binaryfilerepo/.git/hooks/applypatch-msg.sampleUT €�’Pv¶cbux éé}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PKaAéøÊ÷€+binaryfilerepo/.git/hooks/commit-msg.sampleUT €�’Pv¶cbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKaAš ÷Àн,binaryfilerepo/.git/hooks/post-update.sampleUT €�’Pv¶cbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKaA%[9ýŽ/binaryfilerepo/.git/hooks/pre-applypatch.sampleUT €�’Pv¶cbux éé}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PKaAípÙž¨+binaryfilerepo/.git/hooks/pre-commit.sampleUT €�’Pv¶cbux éé}T]oÛF|6ÅF6ì)YP Û… n€0ú�èCGÞžx5yÇÜ%+úÛ3w¤d8-úÆ�ý˜Ù�Ùã7³R›™¯³ãì˜V†øY´]ÃT[ûD¾rº ,mØiµ£m-iO¢´}ú^2U¶mu, T¸MÃ’ÊMÖ:Œÿ&´Õ¡&cI¸uß² ¾ z¬÷]jÛ7ÉüŒ”1ÔäߨYòA„ýT`‡Æ¾×fMÂ�è:g;§E`jÙ{±fÒ 5b�œ¶£Pï!‰ã£%6¢ÅPƒID0%‡OíøEéøÏÒ¤sœ�Š,ÓŠ"#Ç›¼Î3åù8”ïW¿ÒÝLòffú¦¡ÅÝéE†¶&;k¡�·1$ãÆsvtL�Z4#ª’Z)##7n»°£à˜É–s^ê,Ë«Å;Y].UyÉ嵘_ÎùݲTWòòzqu¥Jæåõ|™)��ëGE;Û§�DFØ�ݦ٠_i�˜FÞž<‡�üF`¦å0€àz.²”„œ”r{òvX«Qz�fç‹Wç±í'–Ú7A$Ý $;WdüÌ]Ü�.bܽ³ÞS׈ ¬k �l±:62áÝX-ÿ ï/ˆå t„"tKÊÙZLâ�’Sv\¼ãÎz¬ÛôC`]cuH¿”¨âƒHo¨1™�¨;a &(ÏËðŸ|'*è¨i�¢B ôƒ\ƒn$F‰|¦ÉÉ«�LèÍ-Mâ('ô…NO£~³�­©‡–¬¢•Ÿ8vt¶Gu�ù�P°ؤfÇSz«Ã™�E"ðûÚcÔrJ˜ uÖ��nt”�¥ßmƒmzº˜ŸyšõÞ%«w>%¯M•´˜¸}í…ã=x7@5†(£À‡´I¡’½^˜NЊìÁ�Y$!çy%ªyw•[ÓìðÿåX †w»¢ü�ìÿ=;"z¸ÿkõðp{Iç’Î>SþÏ—?çgô�¶åÕyã|tW5ìùÞ9ënh…ÓÓG «ÇØ^ë…"ˆb2$íS£Ô+X­qúöÞz\�W~ÙZ÷4Ùç¤Ewlãq´†, ¸ƒvý¿:¤Ó8, z¸šr£ýÞ]‡“Ã΢ø¹ÆèÞ'Ó¦Ãß⦤�B�o‘ƒÜשZl z‚´xóSY¢ÿ·pòýKN¼É/÷$Òæ„b[kì>ù‚ã*ütðP¢d•‚="ÌÃ|2�ºÏÀ^2¹6’Ÿ£p胀 ÉóìPKaA,ß.—ÙW+binaryfilerepo/.git/hooks/pre-rebase.sampleUT €�’Pv¶cbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘•29MCO±�å‚X.Þ~÷Ïw'×gñyaWág ÅcƒGϺÆûL^eÓž¿/ûQÌðÓGXÈŠË·×›}¾;™üUNÒë?�hÆô½!&×ù :ñÝÙ*ÏÜpö¾œ�£y§×þƱ�*W w"÷d§ÏFƒ ¸÷.H̽eϯG£ ˜~ãÛ�¥AÇþÏ^áä¢ôI=-Ǫ¹ïU©¤\½þöí[Å`@¯.—hdíƒñ„ºO�‚ÓëBO†ïqÛÿú]“úOûMÓø¸ú#�ÄÑÿù¢;ÒýFbeªVµ4K{pš¯%Z×B¡€Ú!ëgw310œË唣t°Ž¤\ÒÚºàqJã¹ôŒ~.µ?wx�±A`?:>Þ]ù¢ân'ÐO"FfÍsH®$6�¡Ê¸üh™•84a*+›;l1_Ò»ëÚ b±¼Øu5ìŒaÕPú%rêÔrê[-÷ÙR…šYÔšB;13Þ‹ ذ̯¤É3îÙ@¢¼Sû%˜7O,JP�ÐÀK%ÇÅo8^€Ÿ­™Η�q0ŧÓ÷LYg2„R%ÊZi¶¾AE ÖÌ ãÆÀÄ;¥*öÄ4kÙŒ�Âö º¦5]_}�E�#w²–劣ƒK¬±AÑ8„•í.bhÞAŽMLVq?õ Ââ­>yˆŽ�?³Lô2.�Ú³ë²à ¨(5tä�/Dãc&ó0�ïxþ"'eš­j#@?^ŠÁwo®n^¿y; o`+û’YlÓâ—° CRë±EØF7Ò¯¿tWê à=<Ñt:%´‘=¤q¿õaä"êtÚ`0Rø/ЊC¶_|ñåg]]eË|:VÉð÷ÌI®@_WÍ9'† ñ ÇÇ—Ý¢û ›öªË¯‘�Í@€6ÀVs–à)©¸H¦)�Ι ¬•ÒŒ"¢WH†æê Ùu�N ½6¥ (: *Øo+b8ÿZT}@÷w¯]mxÁW|Îa]ìD(`–LôwÀ޽zóíxosòÜ Ç«®�@Öv7E£!¾$š •ûÂæÙ­Éð ’·Ø73W“go{!vº¬ÓnÒÚxõ±äÆ1²qӌʹyt ±*¬eeÛ#'ú¿òÅ3Bîp®ŠÇ1¼ 8 ­¦ ¥ø›rÇ–ržÒ…¢ˆ ì÷zàÑ“Éäw¿ôžc÷Äæÿ“k¦âœü]ÐÅA¢ÙÞ³4�§üM m÷¥WO)g�oûyÿ„°K·ð&^>¢Œt3úºÈ™�úÀ6�Í– ÄÅ.p…_úSÞã&Zù…¯l–”fŸÐäcIò/±rÐ7Úžäy^yž4+}ÅòÔs_¨ºÈÛ m6%vcö‹oùg_"¡W7“Ôë¹ôR¹ì÷F†VmnÅj¯ë† ü2ÊÛý|€#&ÿzp:ž*±®¹RâÕÅW< ‹ªF?@ëf<Î|Ñô¿!¦ˆON!ˆ:÷Tö˜Ú•»ã ö&oÝë3jL<�OÃl�JÂØ÷¿æ4MàË­ð?PKaA½¾—×3binaryfilerepo/.git/hooks/prepare-commit-msg.sampleUT €�’Pv¶cbux ééuS]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘\I¦”É�ç$9! “å”ÝÛÛ=e¯Ë¥Ô¥ë&Ù$ƒ øS¬z…Ðó®²²÷à ô{a|‡P™ÕJzP¦…:'Z,{)”–ÏÀZºMbð$}QZ¬LÏ�T�Jxè„ Â�¬#c�QÊ<%Æ€©1©‘Foh„°#hæÀ™ÁVXÌ»4ÕúÁöÆ!H&Ášºü Uò*‘G!•K3ÐFó_h 8/üàr‚îØAìbi¬Çºˆ^Ω•Ë8(ݺ,Fb%™`€�ÞòDÄW®eÅ$0lp u¥2€€q“‘Çl¤uÉpÔÞ�üh)»4ºQ²òðÁ8AóÚv#{ÔJ,+£ëÝV¨ú!¢b¦µlà<ŒÀ“À-#¼Ô4GlÓû>�¤%6fÜ™ˆO6Ò«ƒmcö¤=„�•BVÔÍ Ó³0X�)Ω3i46ýv�ƒpÖÛíqÆ!‹m½ÝfQ×ÜÈVcÍMÓpÚ*%u à…𸕕ÐDã¼TŠÅ•¡X!%B_Kq©g"l�!ËjX%H7›¾Í§ï92�dt~@'€rp6>µ­.‹¥xNf®¼-3(s CY™• (o·á•P@åSz…äôŒš38= ;’¯=҄‡>Ùû�ƒ³xMŸHl¡p¿“k%*2ó¯€ï'Ù«¤ˆ”ííÁ4îÛÑœ�Á›1‡á€NTAÚÍõû³é~àÿ!,|ºšß]|›¾þzwõáã—9¬i㨙Ž,ö‹ÃóÅAq8-_dt‹ã²Ÿ…ÑZz%À°Û)Q³Ôx½¬:zD©v~Ë“ßPKaAR¼­v'binaryfilerepo/.git/hooks/update.sampleUT €�’Pv¶cbux éé­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPK€8G~Ëñ\ƒ‘binaryfilerepo/.git/indexUT ß/Vv¶cbux éésñ rf```bÆ0fýÃ@šF320°1÷¦^d`h\¢yô–'p´==ÈÐóÌôòãÿ¿\L޾9Ûϰ��;)3/±¨2>-3'ª0$ÈÕHI2*p1ö{i.ò»vòÀDoMÇà‰Ûg?`ypû¦øF6ÙýoSOÖòߎ»}<PK aR—Tbinaryfilerepo/.git/info/UT v¶cbv¶cbux ééPKaAw=Í!­ð binaryfilerepo/.git/info/excludeUT €�’Pv¶cbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK aR—Tbinaryfilerepo/.git/logs/UT v¶cbv¶cbux ééPK€8Gq·†/¨Sbinaryfilerepo/.git/logs/HEADUT ß/Vv¶cbux éé•λÂ0 …ážÂ# EI“4u…LL¸Ÿ¦µTSÈõžú JÝÌý¦>MPK aR—Tbinaryfilerepo/.git/objects/b8/UT v¶cbv¶cbux ééPK b8G=JûìEbinaryfilerepo/.git/objects/b8/35d73543244b6694f36a8c5dfdffb71b153db7UT §/Vv¶cbux ééxKÊÉOR°``HÊÌK,ªä$ ‡PK aR—Tbinaryfilerepo/.git/objects/d4/UT v¶cbv¶cbux ééPK g8GIm% ††Ebinaryfilerepo/.git/objects/d4/c5ad8f3d1c419ec991230ae43dd967c9735cefUT ²/Vv¶cbux ééx��K ]sŠwÌkAá%ÆèÊsðyX* ¥ o/é\ÍjfB]×Üa¢ùÔ3 AO q¦hÉ_•·Ö[8’R¨/Ž“q …ÛûR¼ö\ ƒôy[ª„ÛÁG©ØØ­Û¹¶÷&­‡MŠ4Èq@Žmç¿â#GðùãÚR.,~§>$PK aA!binaryfilerepo/.git/objects/info/UT €�’Pv¶cbux ééPK aA!binaryfilerepo/.git/objects/pack/UT €�’Pv¶cbux ééPK aAbinaryfilerepo/.git/refs/UT €�’Pv¶cbux ééPK aR—Tbinaryfilerepo/.git/refs/heads/UT v¶cbv¶cbux ééPK €8G)æn))%binaryfilerepo/.git/refs/heads/masterUT ß/Vv¶cbux éé956eb78a3f8dce2f5d2e97cfba5e2f0d16ea976c PK aAbinaryfilerepo/.git/refs/tags/UT €�’Pv¶cbux ééPK p8G²q·ñbinaryfilerepo/binary_fileUT Ã/Vv¶cbux ééchange PK p8GíAbinaryfilerepo/UTÃ/Vux ééPK aR—TíAIbinaryfilerepo/.git/UTv¶cbux ééPK aAíA—binaryfilerepo/.git/branches/UT€�’Pux ééPK €8Gw‘ÿ¦"¤�îbinaryfilerepo/.git/COMMIT_EDITMSGUTß/Vux ééPKï­PBqÑG·†¹¤�_binaryfilerepo/.git/configUTaðQux ééPKaA7‹?I¤�9binaryfilerepo/.git/descriptionUT€�’Pux ééPK aA+is§¤�Ñbinaryfilerepo/.git/HEADUT€�’Pux ééPK aR—TíA:binaryfilerepo/.git/hooks/UTv¶cbux ééPKaA`÷‘X Ä/í�Žbinaryfilerepo/.git/hooks/applypatch-msg.sampleUT€�’Pux ééPKaAéøÊ÷€+í�binaryfilerepo/.git/hooks/commit-msg.sampleUT€�’Pux ééPKaAš ÷Àн,í�]binaryfilerepo/.git/hooks/post-update.sampleUT€�’Pux ééPKaA%[9ýŽ/í�Mbinaryfilerepo/.git/hooks/pre-applypatch.sampleUT€�’Pux ééPKaAípÙž¨+í�³ binaryfilerepo/.git/hooks/pre-commit.sampleUT€�’Pux ééPKaA,ß.—ÙW+í�¶ binaryfilerepo/.git/hooks/pre-rebase.sampleUT€�’Pux ééPKaA½¾—×3í�ôbinaryfilerepo/.git/hooks/prepare-commit-msg.sampleUT€�’Pux ééPKaAR¼­v'í�øbinaryfilerepo/.git/hooks/update.sampleUT€�’Pux ééPK€8G~Ëñ\ƒ‘¤�Ïbinaryfilerepo/.git/indexUTß/Vux ééPK aR—TíA¥binaryfilerepo/.git/info/UTv¶cbux ééPKaAw=Í!­ð ¤�øbinaryfilerepo/.git/info/excludeUT€�’Pux ééPK aR—TíAÿbinaryfilerepo/.git/logs/UTv¶cbux ééPK€8Gq·†/¨S¤�R binaryfilerepo/.git/logs/HEADUTß/Vux ééPK g8GíAQ!binaryfilerepo/.git/logs/refs/UT²/Vux ééPK g8G$íA©!binaryfilerepo/.git/logs/refs/heads/UT²/Vux ééPK€8Gq·†/¨S*¤�"binaryfilerepo/.git/logs/refs/heads/masterUTß/Vux ééPK aR—TíA#binaryfilerepo/.git/objects/UTv¶cbux ééPK aR—TíAi#binaryfilerepo/.git/objects/01/UTv¶cbux ééPK €8Gì0Ä-88E$�Â#binaryfilerepo/.git/objects/01/8f4a29a24ed6c9c0914b2941539108b79be004UTß/Vux ééPK aR—TíAy$binaryfilerepo/.git/objects/07/UTv¶cbux ééPK g8Grí588E$�Ò$binaryfilerepo/.git/objects/07/0b930029d89b63b88b87ced933045aefe97a30UT²/Vux ééPK aR—TíA‰%binaryfilerepo/.git/objects/86/UTv¶cbux ééPK €8GS)[ìE$�â%binaryfilerepo/.git/objects/86/e5c1008b5ce635d3e3fffa4434c5eccd8f00b6UTß/Vux ééPK aR—TíAx&binaryfilerepo/.git/objects/95/UTv¶cbux ééPK €8GÓ{©©E$�Ñ&binaryfilerepo/.git/objects/95/6eb78a3f8dce2f5d2e97cfba5e2f0d16ea976cUTß/Vux ééPK aR—TíAù'binaryfilerepo/.git/objects/b8/UTv¶cbux ééPK b8G=JûìE$�R(binaryfilerepo/.git/objects/b8/35d73543244b6694f36a8c5dfdffb71b153db7UT§/Vux ééPK aR—TíAè(binaryfilerepo/.git/objects/d4/UTv¶cbux ééPK g8GIm% ††E$�A)binaryfilerepo/.git/objects/d4/c5ad8f3d1c419ec991230ae43dd967c9735cefUT²/Vux ééPK aA!íAF*binaryfilerepo/.git/objects/info/UT€�’Pux ééPK aA!íA¡*binaryfilerepo/.git/objects/pack/UT€�’Pux ééPK aAíAü*binaryfilerepo/.git/refs/UT€�’Pux ééPK aR—TíAO+binaryfilerepo/.git/refs/heads/UTv¶cbux ééPK €8G)æn))%¤�¨+binaryfilerepo/.git/refs/heads/masterUTß/Vux ééPK aAíA0,binaryfilerepo/.git/refs/tags/UT€�’Pux ééPK p8G²q·ñ¤�ˆ,binaryfilerepo/binary_fileUTÃ/Vux ééPK,,yä,libgit2-pygit2-d86662e/test/data/blameflagsrepo.zip000066400000000000000000000571441521317216500222450ustar00rootroot00000000000000PK OâRblameflagsrepo/UT §ÆÞ`ðÅcbux ééPK -fQBÙ÷ÚŽblameflagsrepo/.gitignoreUT Åà QðÅcbux éé*.swp PK OâR44ïe))blameflagsrepo/hello.txtUT §ÆÞ`ðÅcbux ééhello world hola mundo bonjour le monde PK -VâRblameflagsrepo/.git/UT ÓÞ`ðÅcbux ééPK s�N>blameflagsrepo/.git/logs/UT y_YMðÅcbux ééPK s�N>blameflagsrepo/.git/logs/refs/UT y_YMðÅcbux ééPK s�N>$blameflagsrepo/.git/logs/refs/heads/UT y_YMðÅcbux ééPKOâR;BY,“ö*blameflagsrepo/.git/logs/refs/heads/masterUT ¶ÆÞ`ðÅcbux éé�“Kn1†×Ì)j B´ìò{¡HÙÀ ÊvyƨQwg¤°àNl¸@.†"vM¼*׿ÿê³Kˆm(qʆ jÒ1ÁŽ¥:X–EGコl$|îà’N5çHw?ù;œ}Ë÷÷óº]ßviÞ‚Äठ>xx-¤/Zw¨+¼¬c]+õ¯öð¡ÎË ú»Íé†#3ǨP¸ t6†’|Dm´ÍV)i�RÛ1m°Áé0÷p‘3t‡†z§™¡Ôžw›£1²q2HƒY�É)�Q&fϦÒ$¸©ùF'þ2<ªôã®ê�2C¼…™ÓͼÔw»Í 2q1’UÁ� $¢g/t$RBeïRˆ:DøÊ‘–öv#¼»ûuà,N|ŽçiénÆúf!š{s—¹Q[4(Q;Ó¨ñ‰Ù/\¦y ŽÜ÷Ónsüæ�žÅi¬zäœyá†9L§:`�àãû‹ËÛ}2åL,�Áâ�Í"³µ¡`L%+á]ÑŒ-­ègr>,ÔSŸW÷ÿs¹n Mì£ÕßPK,fQB’M}Y0—(blameflagsrepo/.git/logs/refs/heads/i18nUT Ãà QðÅcbux éé•‘MNÃ@ …×äÞ#"Ïd~« U€Š`Á¦âÎØQƒš¤J£B¸gàbL6¨xågÙzŸmÄó(Ib+¤£6dêˆ(^”‰&:Q�©­�Þ‰UðXÂ[†‡š>?ä–/|Ò«v"ÚÏeºkP:wGœ‚KTˆõH}Ú.àvš„¡‡::L2g[;"ÒN‹³–ÙmµØ ªÄRbï côü?Fïsµk§lh†­ìv´=löÔ·‡mq¶·‘dª)`^ß(&ÍÎúÀ5M0ÑkL ÿ‚Œþ‚\�’¯[œmm�Gr^åNJ•±Qä($iêÔ˜*‰§­«à©M‡îe> °<éò;_­Ÿ¯ndܵ}É’9+§*Ÿg~ss~w=K9½M@=C/¯ÅPKOâRÚ}.7�¥blameflagsrepo/.git/logs/HEADUT ¶ÆÞ`ðÅcbux é鵕ËnÔ0†×ÌSx BD¾_Fj«R.RYPñÇöñŒa’TI:0,x'6¼@_ Ï —–Eå© +ÇJr>ÿ±CiÝE `ˆ �;.AzG)dÒI§‘%é•qF£bäMCÎ`�#yíáæ~!Gâöþ8OW›&ôís¸3Ì8ë,yJ¥�Êl›'ò8wyʰz2'çy'²ŸŸUWÿ÷˜Úi«Ù/Ì%†�ýõ4'm¿ÎÝ‚¤¡oI ã„™z’™íêa5pÍQ+£’\qT–‰`QAˆÆXŠ‘:ƒ5æŽÓ9¹„ YâjUð:ry]—³êÚƒÖ�¥ÅƒdxÔÊØè©dNZé çV{¤£ö>Èó»°œU—þ/©;ÁïM}›õ6ó}úõ©+ôˆè½àÔ8 q£½KÁz.•ÔQ Á´â0X#ÿz#ie7-º~@’ò gÕ¥¹Ge˜cŠG™,FPîY ÅI,’(H è ÝÐߌ- Ü œ“‹Ý¸…ˆÄoÈ€ázó›Y5Cu›¼Í¡'ë¾#/q3öäh{ßìÆÇçïŸ�â°Ê]± Íe†ÓCö}5‡’†‚6¬< Aë 6`ò!IÐ@¢J‹Ã�Ë;w»bÄXÄb3}žt‘tøiV P�Áá¤ê�ÍUÍÁÊC‘¸‹”SëXqŽ–J ¨ˆÖç¥óä}ùt†ŽœÞ|_¶äÈ÷¸lGôÇal®»ülVEØžZ—’qi¶Ôü–ßw˜ú¡…ivͪËW/èAœj×[ÎGü£¶}õâäìk½O, ÈŒâÉiD­]â¥M¢ Ö$‰¼TKò�œ»ÿÿmŸÛ³j¼*‡))bZýPKÊ‹PB‰¹—XšÖblameflagsrepo/.git/configUT ´QðÅcbux éé5ŽÁ ƒ0 †Ïö)ŠGËØQØ“ÈÕ¦ˆFÒvàÛ/N–þ$ü†Iß®QÜ%S=’èÊ5“lþå®IĸJD£¢]3=!ÎF,s`VLu�¡`þŸy+Œ§©M0ºAÍ¥ oEi¦­µØªl»™J`ºÔñ>É L£Ñöãlw{Ë´ØñÍ‚2,b†®ÿÁå›áò…Î}PKOâR 8ŠÆ¾Ùblameflagsrepo/.git/indexUT ¶ÆÞ`ðÅcbux éésñ rf```á„{Gk$dj?*>Êÿ=¸â—00wqP�M¸<Ôoß²Ÿ]:%�Í{¢¦µj}Ô:ÄÀ¥—žY’™ž—_”Ê ÷Ž-ײ–Ý ¢5[˜ö@ÌSf@3OÓ`QÃ÷÷F}a³ù÷zËLÓZ=©ˆ�3#5''_¯¤¢„!$ÈÕ¨N’ÁHÁ€«øV§é4ÍÝ ÿ> :S¹ŽwÛÍÕ\ÿX—k;óyéHFJ·‹/?ûPK s�N>blameflagsrepo/.git/refs/UT y_YMðÅcbux ééPK OâRblameflagsrepo/.git/refs/heads/UT ¶ÆÞ`ðÅcbux ééPK OâR�ù>Ç))%blameflagsrepo/.git/refs/heads/masterUT ¶ÆÞ`ðÅcbux ééeaddae1752f876d0de669f2bcfd3087f4e2256f4 PK ,fQBA=Ð))#blameflagsrepo/.git/refs/heads/i18nUT Ãà QðÅcbux éé5470a671a80ac3789f1a6a8cefbcf43ce7af0563 PK s�N>blameflagsrepo/.git/refs/tags/UT y_YMðÅcbux ééPK OâRblameflagsrepo/.git/objects/UT ¶ÆÞ`ðÅcbux ééPK OâRblameflagsrepo/.git/objects/73/UT ¶ÆÞ`ðÅcbux ééPK OâRý$%ŸXXEblameflagsrepo/.git/objects/73/da8935960429bba1fc51e252cc79ae0db6d9abUT ¶ÆÞ`ðÅcbux ééx+)JMU07e040031QÐKÏ,ÉLÏË/Je.õÛ·ìg—NIcóž¨i­ZµAUe¤æääë•T”0,jøþ¾Ã¨/#l6ÿ^o™iZ«'ÔvÞPK s�N>!blameflagsrepo/.git/objects/pack/UT y_YMðÅcbux ééPKs�N>‘¿V{ÏSblameflagsrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.packUT y_YMðÅcbux éé ptöf```b¾ Bsæv[g9 ¸V¯M²g[|ÞCÊ‘CóŽÊ×3‹HþÉiò(›jš’\©·©1dz±e:_苵-2²a'—æÜ8ÑY±fãõu“ ~÷.ÚùñB©SH`èĹzÛŠ¾[z¾œúNhÙ4§Ͷχñ„ÌÈYÔ²nc¾fZVÒEQ�›Õ‡¼Úî?iõà)ƒû¿)îÿ5¯ÅmæÜž;ÚÉšß6W^?œ£o95ÇøÐ=«ì³ßâ¦Y~Ý¿·òêÇe Í׫šî>ʸ;3~bN¨ÈD~�7¼¹ˆ0|·ßêùBàÂÉ«}Å͵ìÒ;¯2íVY¿Ü.¡R+>.\Øòþní¶ýOLTÖÙà^"ojüw{ùí[zlû{«™¯-°ŒZÍR1ÇØÄÀÀØ0ð‚÷y�“>çOë{¥ èñ½½ýMút?ÏäÏóVd®�’ßÊ'é’ºäÙ“'¯§†L1Ð µxrû×oÿä5®)?vk+å,_tk£*§ì9³Š9—/iëé?`b`cfÜ»ƒ©bÎi�³'O†kœ×?åùðÔ‰óž!'tÏzŸ|îuêüéSúºA',t¬çé¸Rblameflagsrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.idxUT y_YMðÅcbux ééû_âŸÌÀÀÀÄ@]ÀHcÌDfÆ‚Y¨ˆY)Äl80;æ€bN,˜k˜bn<˜g„b^*b>.õÛ·ìg—NIcóž¨i­Zµi?-œtå‡N1ÓÆ“uûâ¹¼øf˜µ¸¼¹¾v•�·Ú…SïygËWßÊ·–x*t{ù<Ÿf†»ë6×÷u÷®õ;b1«!#EvѵðÞ ŽSZ¦+µœˆÛ÷n·û„SŸÊvþéV ÉM6L5Nù¶ÞjÚ''¶IºégJ"]¿‘vÉX½L/Œ‡o�ñ´ë&›*ëÜ´²OÈZ¥¦—{Õ)èi£M_ÔÙò†·œµùœÁõ™Ÿ/êZ† òHÙ7qÓÍ¥ ‡|[ÿiŒµˆ wE„íTT÷š7W_iª,ÙÉðü¡§gž¼wøô¼ÀW‹�äî>©×ö—Z¬oгy:Ó‡‰kþN.9]9UäÊéM>ñç_§mråÒ˜÷(Øæ©gývÍÉE�sÌy’Âä�½¢ýš=]p@9ðwãAý¸eü†Óg…˜ËU>÷Z=½ni½ °˜I„Ä‹�ýXl=âZ ˜®Yº�r—�â‹€¸XLõŲ€lþeŸ,ï~x6Cª�·•}íÚ0�O>!/®´.ËŒÓÏ[!ºØ}g%§ÜËß'PK ,fQBblameflagsrepo/.git/objects/54/UT Ãà QðÅcbux ééPK ,fQB¤Æ.°°Eblameflagsrepo/.git/objects/54/70a671a80ac3789f1a6a8cefbcf43ce7af0563UT Ãà QðÅcbux ééx¥Ž1N1©ýŠí#N^Û·Þ“PQ„ŽŽìÚ‹8)±ÑÅ@ò{D¾@7ÓŒ¦ôóyfÿ063XrŒJfù�4õ2ÍL’0ÖÀ¨bÊZݧlÖ$+)ò"ì‰)a•PiÎ\Õ'\§%‡À©°“¯ñÑ7x]K‡ïÞàÅn—O>Ýùp|{|¶í´¶©Ú0¢ÇLv½wå~:ì? 'µZ½Ù4®¤Uhöã~XøMtPK £NâRblameflagsrepo/.git/objects/50/UT âÅÞ`ðÅcbux ééPK £NâRXû†o88Eblameflagsrepo/.git/objects/50/62782362b06a2b86da192977ec8e7428323154UT âÅÞ`ðÅcbux ééxKÊÉOR01`ÈHÍÉÉç*Ï/ÊIáÊÈÏITÈ-ÍKÉçJÊÏËÊ/-RÈIUÈÍÏKIå�§’PK ,fQBblameflagsrepo/.git/objects/97/UT Ãà QðÅcbux ééPK ,fQB´�w_ooEblameflagsrepo/.git/objects/97/33b6ee7f6b3cb0a276586a413c8b281baeb8bdUT Ãà QðÅcbux ééx+)JMU040f040031QHªLÕ+©(a¨œ½êkÁÇVJï¹¾|:8§9ÍôTIFjNN>XÑR…C¾­¿ÿ4ÆZD†¿»"Âv*ª�ª(/µœÁÈ~ó—„¼•3eìû½éÏò©Ï<~Äß0 PK OâRblameflagsrepo/.git/objects/ea/UT ¶ÆÞ`ðÅcbux ééPK OâR¢ïÜ•µµEblameflagsrepo/.git/objects/ea/ddae1752f876d0de669f2bcfd3087f4e2256f4UT ¶ÆÞ`ðÅcbux ééx¥ÎÁMÄ0…aήbîˆÕx'¶„âΉ fÆ%R¯lok4@cD´@ãØ:PHO½šÁáãç{=àUŠ­G3y×v{œÛKc®;Ÿù–í ütû á Ñé·Û¿Cî³Tƒvg5X.×jû^Ü/ 7TŽPK 'fQBblameflagsrepo/.git/objects/79/UT ¹Ã QðÅcbux ééPK 'fQBR5ód$$Eblameflagsrepo/.git/objects/79/9baaf570f1f03a22ef0af4f2cfc36c836635f8UT ¹Ã QðÅcbux ééxKÊÉOR02`HªLU(Ï/ÊIáJ,U(J-ËÏ,âƒè KPK OâRblameflagsrepo/.git/objects/30/UT ªÆÞ`ðÅcbux ééPK OâR�ž>ô99Eblameflagsrepo/.git/objects/30/a280f7ef88328e68569b0fbd4b1c962aab9272UT ªÆÞ`ðÅcbux ééxKÊÉOR01dÈHÍÉÉWP(Ï/ÊIáÊÈÏITÈ-ÍKÉçJÊÏËÊ/-RÈIUÈÍÏKI囌ÉPK 'fQBblameflagsrepo/.git/objects/32/UT ºÃ QðÅcbux ééPK 'fQBËipEblameflagsrepo/.git/objects/32/3fae03f4606ea9991df8befbb2fca795e648faUT ºÃ QðÅcbux ééxKÊÉOR0gHËÏOJ,â"§zPK ÒNâRblameflagsrepo/.git/objects/1c/UT ;ÆÞ`ðÅcbux ééPK ÒNâRClß½±±Eblameflagsrepo/.git/objects/1c/ef51e3f29d020891228e804baa303d87c9b49bUT ;ÆÞ`ðÅcbux ééx¥ÎÑi!€á<[…ï!Ç8êªBH ¹ ftdÖ5¸^ki �Ý‘®€ÿãϽµmjôöeÍKµÖV©)¤�]tbMarM1pÌË‚¬~hÈñY|0Éx,®FÌÁ²É�,–(¾9IQÑm®}è«0�s£Cýý®M¿s—µ�Ÿù¼ÜŽíí$;åRäC›=tÁëW@•ÿw§< ©o©}4šz•}ïêÕŒRGPK s�N>!blameflagsrepo/.git/objects/info/UT y_YMðÅcbux ééPK s�N>üýÝó66&blameflagsrepo/.git/objects/info/packsUT y_YMðÅcbux ééP pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.pack PK ¬NâRblameflagsrepo/.git/objects/b6/UT óÅÞ`ðÅcbux ééPK ¬NâRi¨ŒWWEblameflagsrepo/.git/objects/b6/f333fef0028a797c484e31db17cf987b8c662bUT óÅÞ`ðÅcbux ééx+)JMU07e040031QÐKÏ,ÉLÏË/Je.õÛ·ìg—NIcóž¨i­ZµAUe¤æääë•T”0$U('mÈÒn»%©Yþ¦¯DÃÈ0¿ó=PK s�N>blameflagsrepo/.git/hooks/UT y_YMðÅcbux ééPKs�N>ƒlÇz ,blameflagsrepo/.git/hooks/post-commit.sampleUT y_YMðÅcbux éé-ŒA ï¼b WMîÞü€'?°!‹PKeIUž/‰^»gÚ^¦9–Iƒ±ÆâQÀ;åšAäuk¬ -PCT8J‰�o¼‚ ›s¬ê·Ô¯NrŽç*ÓÂã™{ ¸ÐÜk-tq$¯X;ÊâãáCm·_b�¹ã)}PÞæ PKs�N>š ÷Àн,blameflagsrepo/.git/hooks/post-update.sampleUT y_YMðÅcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKs�N>%[9ýŽ/blameflagsrepo/.git/hooks/pre-applypatch.sampleUT y_YMðÅcbux éé}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PKs�N>i 0›ß3blameflagsrepo/.git/hooks/prepare-commit-msg.sampleUT y_YMðÅcbux éé}S]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘Œ$·”É�ç$9!2y�ÏÞ½½ÝSöº\J]ºn’M2¸Ð€?ŪW�1à*+{Þ@o±Áw•Y­¤eZX¡s¢Å‚°—B)¬aù ¬¥·é#OÒw¥Å Á4ñÜH¨„‡N¸P!üÈ:2æÐ¥ÌSb ˜“iô†&A;‚fœl…À¼K3P­lo‚ta¬©Ë¿ÐQ!¯y4 R¹4ƒm4ÿ…Ö€óÂ.'èŽÄ.–Æz¬‹èåœZi±ŒƒÒ»@—ƒÅhC¬$ °Ñ[žˆøÊµ¬˜† ¤®Ô@Ð"n2rã˜�´.ŽÚ;0ƒ-e—F7JVÞ�0 >'h^Ûnd�Z‰Åaet½Û U?DTÌ´–Mœ‡xò¸e„—šæ€mzß’´ÄÆŒ;ñ ÃFÚ`u°mÌž´‡°±CÈŠºtzë1EÀ9u&�Ʀg÷c®Ãz»=îÏ8d±­·Û,êšâ�Ùj¬¹iN[¥¤Ž¼·²šhœ—J‘¢¸2+¤Dèo).õL„­1dY�‚«éfÓ·ùô#G&�ŒÎèPÎÆ«†úz´ ¸,–â8‰˜¹ò¶Ì Ì�deV‚l ¼ÝXBQ•Oé&’Û3jtÌàô4ìI¾öHS zeÿíENÎâ'ô‹DÀšA÷;ùV¢"Sÿ ú~’½JªHÝÞLãÞÁÙ¼ÙpxèDäÝ\¿?›îþGaáÓÕüîâÛüóõ×»«¿ÌaM›GÍt4a±_ž/ŠÃiù"«X—ý,Œ×Òmþû�5K�×kÀª£Ë”jçç±<ù PKs�N>éøÊ÷€+blameflagsrepo/.git/hooks/commit-msg.sampleUT y_YMðÅcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKs�N>}«.íU(-blameflagsrepo/.git/hooks/post-receive.sampleUT y_YMðÅcbux ééeQËrÂ0 ¼ç+T¸6˜<I‡a¦—ÎôÞPl™x vÆv(ýû*†C7K«]yWë'Ñ+Â�­³5¼Z ŽÓ…`pî Az3EÐÎCV“ 1÷$É\it%7‰øñ}pM?[@ÉÃÌ'”g0JIS$K�•Ъ´ËË™èüWì‰,Ì“BÞ¼'á CX¸þ4�ü•Æ2×»ùÄv DÅujÑbaäÜEyºá`éóþð¤-Žtdø��>xY¦ë]U�*úŠêŽöºÑ][×…Æ]ÓÕ�RR5Xn¡iõ{]ãeÙn÷mÛé]ÙËm]‘TUÑï;Ý5ÛÒA „*ˆg’â D ��Þôb >ˆ”9BHy.f+ݸØL~,Ý"\Œ¥%0Vð´x¸[5|¾èþÜc“eë ˆ9x>6zÊIq21—Ž‹ßËsÑ\²oPKs�N> Â\*+blameflagsrepo/.git/hooks/pre-commit.sampleUT y_YMðÅcbux éé}T[kÜ:~ŽÅÔ I±w¶!IÙÂ’}9�óÐSŠl�×êÚ’+É›l)ýíý$Û[ÒSúæËܾËÌá‹Y¡ôÌÕÉarH+Mü$Ú®aª�Ù�+­êOÐV'b-”v~Bn'‡t§•W¢§º!©ªŠÆÈ€�ÛÎïÈ[f2Å.ý¯:‹âêâ•,/eqÉŵ˜_ÎùÕ¢¨®äåõÅÕUU0/®ç‹¤R °ÞU´3}$$ ‚6æ1r+\©TDp;rìð[N‹�o{Γ˜„œ˜²Ññqöƒñ¼oM=hÓ†9Ý[+blameflagsrepo/.git/hooks/pre-rebase.sampleUT y_YMðÅcbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘ªüH•29MCW±�%ƒx.Þ~÷Ïw'×gñyaWåg Å£ƒÇÏ»&- ¼Ú¦3<_ö£˜á:æ� °�—p¯7û0|w2ù«œ¤×ÑŒé{CL¯ót:⻲Už¹áì}9GóN¯ý+Œ0bU® îDîÉNŸ�Apï]�<˜{ÿÊž_�FAp1üÆ·"KƒŽýŸ½ÂÉ Dé!’zZŽUsß« ÒIÿ¸zýíÛ·>ŠÁ€^].ÑÌÚâ uŸ§×1„ž ßã¶ö»&õŸöœ¦ùqˆ`GÿçGˆîX÷[‰•©ZÕÒ,íÁ‰¾–h_ …"j­ŸßÍÔÀ€.—SŒòÁJ’r}Hkë‚Gr(�çBÐ3ú¹LÔþìá%Æ�ýèøxwå ‹;ž@O‰E˜5Ï"¹’؆*ã¤efTâЈ¨¬lî°É|Iï®s'ˆÄòr×Õ°3†UCè—È©SË©o·ÜkKjfQgh íÔÌx7‚`ÃV0¿’&ϸo‰òNYì˜`Þ<±(A5B/–¿åx~¾f^8_6ÆÁŸNß0i�É|J•(k¥Ùú&=(X3_(ŒØÓ¬Nd3FÛ/èœ~Üt}õ}AŽ`ÜÉZ–+Œ.±ÆEãV¶»ˆ¡ x861YÅ=Õƒ Ë·úä!:þÌ2Ñ˸d4jcÌJ¬Ì‚3 ¢ÔЕC¾��™ÌÃP¾ãŒœ”i¶ª�\ýx)ß½¹ºyýæí,¼�­ìKf±Q‹_Â’I ¬Ç>a#ÝH¿Ó]©7€÷ðtDÓé”ÐFö�ÆýÖ‡‘‹¨ÓiƒÁHá¿P@+Ù~ýñ!Ä—Ÿuu•-wðéXa$Ãß3'¹}]5g�*Ä3i_v‹:ì(lÚ«.¿J>6fØ[Íy‚'¥à"™¦@:g.°VJ3Šˆ^!š«/dd×A:�ôØ”& è(¨`¿±ˆáükQõÝß¿vµá_ñY‡u±¡€Y2Ñß9D;vèÍ·ã½íÉs(l±º>YÛÝ�„lø’h‚nTî › d·*ÃJ:bßÌPX\Mž½í…Øë²N»IkãÕÇ’ÇÈÆm3*çæÑQ4ΰ–•m��èÿÊϹÃÙ*Éð*à0´š6”âoÊ[Êy^HŠ"V(°ßë�GO&“ßýFÐ{ŽYÜ›ÿO®™ŠHpòwA‰f{ÏÐtžò7�´Ý—^=¥œu¾íçýÂ.Ý›xùˆ2ÒÍèë"g>êÿMÛ@6[‚c¸À~éOz�›Hhå¾^°]Rš}B“�%É¿"ÄÊAßh{’çyåyÒ¬ôËSÏ}¡ê"o3´Ù”Ø�Ù/¾åŸb|‰„^ÝLR¯çÒKå°ßjX·¹«½®¶ðË(o÷Ž™ü ÂEèxªÄÊnäJ‰WO_ñ€.ªý­›A|ðHó!DÓÿŽp˜"~ ³5* cßÿ¢Ó4�/·ÂÿPKs�N>`÷‘X Ä/blameflagsrepo/.git/hooks/applypatch-msg.sampleUT y_YMðÅcbux éé}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PKs�N>R¼­v'blameflagsrepo/.git/hooks/update.sampleUT y_YMðÅcbux éé­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPK s�N>blameflagsrepo/.git/info/UT y_YMðÅcbux ééPKs�N>ÚÖXtblameflagsrepo/.git/info/refsUT y_YMðÅcbux ééUË1Ã0À:þŒ!éxh’"),ÿ\gûÕœZ`jhÊá­vÄ eS¨uèÄëʵÏwzìóÃø2²v6®º ³’Á“¬H ë"W§´¿øõ}çuw=Í!­ð blameflagsrepo/.git/info/excludeUT y_YMðÅcbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK OâRNÕ”))blameflagsrepo/.git/ORIG_HEADUT “ÆÞ`ðÅcbux éé1cef51e3f29d020891228e804baa303d87c9b49b PK -fQB+is§blameflagsrepo/.git/HEADUT Åà QðÅcbux ééref: refs/heads/master PKs�N>7‹?Iblameflagsrepo/.git/descriptionUT y_YMðÅcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK OâRh/ ò"blameflagsrepo/.git/COMMIT_EDITMSGUT ¶ÆÞ`ðÅcbux ééMore space for hello PK ;fQBýžÔQ blameflagsrepo/bye.txtUT âà QðÅcbux éébye world PK OâRíAblameflagsrepo/UT§ÆÞ`ux ééPK -fQBÙ÷ÚŽ¤�Iblameflagsrepo/.gitignoreUTÅà Qux ééPK OâR44ïe))¤�¢blameflagsrepo/hello.txtUT§ÆÞ`ux ééPK -VâRíAblameflagsrepo/.git/UTÓÞ`ux ééPK s�N>íAkblameflagsrepo/.git/logs/UTy_YMux ééPK s�N>íA¾blameflagsrepo/.git/logs/refs/UTy_YMux ééPK s�N>$íAblameflagsrepo/.git/logs/refs/heads/UTy_YMux ééPKOâR;BY,“ö*¤�tblameflagsrepo/.git/logs/refs/heads/masterUT¶ÆÞ`ux ééPK,fQB’M}Y0—(¤�kblameflagsrepo/.git/logs/refs/heads/i18nUTÃà Qux ééPKOâRÚ}.7�¥¤�ýblameflagsrepo/.git/logs/HEADUT¶ÆÞ`ux ééPKÊ‹PB‰¹—XšÖ¤�Õblameflagsrepo/.git/configUT´Qux ééPKOâR 8ŠÆ¾Ù¤�à blameflagsrepo/.git/indexUT¶ÆÞ`ux ééPK s�N>íAÔ blameflagsrepo/.git/refs/UTy_YMux ééPK OâRíA' blameflagsrepo/.git/refs/heads/UT¶ÆÞ`ux ééPK OâR�ù>Ç))%¤�€ blameflagsrepo/.git/refs/heads/masterUT¶ÆÞ`ux ééPK ,fQBA=Ð))#¤� blameflagsrepo/.git/refs/heads/i18nUTÃà Qux ééPK s�N>íAŽ blameflagsrepo/.git/refs/tags/UTy_YMux ééPK OâRíAæ blameflagsrepo/.git/objects/UT¶ÆÞ`ux ééPK OâRíA< blameflagsrepo/.git/objects/73/UT¶ÆÞ`ux ééPK OâRý$%ŸXXE$�• blameflagsrepo/.git/objects/73/da8935960429bba1fc51e252cc79ae0db6d9abUT¶ÆÞ`ux ééPK s�N>!íAlblameflagsrepo/.git/objects/pack/UTy_YMux ééPKs�N>‘¿V{ÏS$�Çblameflagsrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.packUTy_YMux ééPKs�N>,t¬çé¸R$�Ïblameflagsrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.idxUTy_YMux ééPK ,fQBíADblameflagsrepo/.git/objects/54/UTÃà Qux ééPK ,fQB¤Æ.°°E$��blameflagsrepo/.git/objects/54/70a671a80ac3789f1a6a8cefbcf43ce7af0563UTÃà Qux ééPK £NâRíAÌblameflagsrepo/.git/objects/50/UTâÅÞ`ux ééPK £NâRXû†o88E$�%blameflagsrepo/.git/objects/50/62782362b06a2b86da192977ec8e7428323154UTâÅÞ`ux ééPK ,fQBíAÜblameflagsrepo/.git/objects/97/UTÃà Qux ééPK ,fQB´�w_ooE$�5blameflagsrepo/.git/objects/97/33b6ee7f6b3cb0a276586a413c8b281baeb8bdUTÃà Qux ééPK OâRíA#blameflagsrepo/.git/objects/ea/UT¶ÆÞ`ux ééPK OâR¢ïÜ•µµE$�|blameflagsrepo/.git/objects/ea/ddae1752f876d0de669f2bcfd3087f4e2256f4UT¶ÆÞ`ux ééPK 'fQBíA°blameflagsrepo/.git/objects/79/UT¹Ã Qux ééPK 'fQBR5ód$$E$� blameflagsrepo/.git/objects/79/9baaf570f1f03a22ef0af4f2cfc36c836635f8UT¹Ã Qux ééPK OâRíA¬blameflagsrepo/.git/objects/30/UTªÆÞ`ux ééPK OâR�ž>ô99E$�blameflagsrepo/.git/objects/30/a280f7ef88328e68569b0fbd4b1c962aab9272UTªÆÞ`ux ééPK 'fQBíA½blameflagsrepo/.git/objects/32/UTºÃ Qux ééPK 'fQBËipE$�blameflagsrepo/.git/objects/32/3fae03f4606ea9991df8befbb2fca795e648faUTºÃ Qux ééPK ÒNâRíA«blameflagsrepo/.git/objects/1c/UT;ÆÞ`ux ééPK ÒNâRClß½±±E$�blameflagsrepo/.git/objects/1c/ef51e3f29d020891228e804baa303d87c9b49bUT;ÆÞ`ux ééPK s�N>!íA4 blameflagsrepo/.git/objects/info/UTy_YMux ééPK s�N>üýÝó66&¤�� blameflagsrepo/.git/objects/info/packsUTy_YMux ééPK ¬NâRíA%!blameflagsrepo/.git/objects/b6/UTóÅÞ`ux ééPK ¬NâRi¨ŒWWE$�~!blameflagsrepo/.git/objects/b6/f333fef0028a797c484e31db17cf987b8c662bUTóÅÞ`ux ééPK s�N>íAT"blameflagsrepo/.git/hooks/UTy_YMux ééPKs�N>ƒlÇz ,í�¨"blameflagsrepo/.git/hooks/post-commit.sampleUTy_YMux ééPKs�N>š ÷Àн,í�ˆ#blameflagsrepo/.git/hooks/post-update.sampleUTy_YMux ééPKs�N>%[9ýŽ/í�x$blameflagsrepo/.git/hooks/pre-applypatch.sampleUTy_YMux ééPKs�N>i 0›ß3í�Þ%blameflagsrepo/.git/hooks/prepare-commit-msg.sampleUTy_YMux ééPKs�N>éøÊ÷€+í�æ(blameflagsrepo/.git/hooks/commit-msg.sampleUTy_YMux ééPKs�N>}«.íU(-í�B+blameflagsrepo/.git/hooks/post-receive.sampleUTy_YMux ééPKs�N> Â\*+í�þ,blameflagsrepo/.git/hooks/pre-commit.sampleUTy_YMux ééPKs�N>hÓ†9Ý[+í�¿0blameflagsrepo/.git/hooks/pre-rebase.sampleUTy_YMux ééPKs�N>`÷‘X Ä/í�9blameflagsrepo/.git/hooks/applypatch-msg.sampleUTy_YMux ééPKs�N>R¼­v'í�t:blameflagsrepo/.git/hooks/update.sampleUTy_YMux ééPK s�N>íAK?blameflagsrepo/.git/info/UTy_YMux ééPKs�N>ÚÖXt¤�ž?blameflagsrepo/.git/info/refsUTy_YMux ééPKs�N>w=Í!­ð ¤�M@blameflagsrepo/.git/info/excludeUTy_YMux ééPK OâRNÕ”))¤�TAblameflagsrepo/.git/ORIG_HEADUT“ÆÞ`ux ééPK -fQB+is§¤�ÔAblameflagsrepo/.git/HEADUTÅà Qux ééPKs�N>7‹?I¤�=Bblameflagsrepo/.git/descriptionUTy_YMux ééPK OâRh/ ò"¤�ÕBblameflagsrepo/.git/COMMIT_EDITMSGUT¶ÆÞ`ux ééPK ;fQBýžÔQ ¤�FCblameflagsrepo/bye.txtUTâà Qux ééPK>>® Clibgit2-pygit2-d86662e/test/data/dirtyrepo.zip000066400000000000000000000462241521317216500213000ustar00rootroot00000000000000PK Z^ïT dirtyrepo/UT |8ÑbGÙbux èèPK Û[õ>Ö/"dirtyrepo/staged_new_file_modifiedUT Íñ'N(TÐbux èèOther modified content PK ®[õ>Ö/&dirtyrepo/staged_changes_file_modifiedUT wñ'N¨VÐbux èèOther modified content PK –Võ>dirtyrepo/current_fileUT Ûè'N¨VÐbux èèPK RWõ>dirtyrepo/new_fileUT ;ê'N¨VÐbux èèPK �[õ>@ÅçC%dirtyrepo/staged_delete_file_modifiedUT :ñ'N¨VÐbux èèModified content PK }[õ>@ÅçCdirtyrepo/staged_changesUT ñ'N(TÐbux èèModified content PK ÓVõ>dirtyrepo/staged_newUT Né'N¨VÐbux èèPK à[õ>dirtyrepo/subdir/UT Óñ'NGÙbux èèPK @Wõ>dirtyrepo/subdir/current_fileUT ê'N¨VÐbux èèPK VWõ>dirtyrepo/subdir/new_fileUT Dê'N¨VÐbux èèPK æ[õ>@ÅçCdirtyrepo/subdir/modified_fileUT àñ'N(TÐbux èèModified content PK {[õ>@ÅçCdirtyrepo/modified_fileUT ñ'N(TÐbux èèModified content PK [^ïTdirtyrepo/.git/UT }8ÑbGÙbux èèPK ¼Zõ>dirtyrepo/.git/refs/UT ³ï'NGÙbux èèPK ÖZõ>dirtyrepo/.git/refs/heads/UT äï'NGÙbux èèPK ÖZõ>çé'))) dirtyrepo/.git/refs/heads/masterUT äï'N(TÐbux èèa763aa560953e7cfb87ccbc2f536d665aa4dff22 PK ¼Zõ>dirtyrepo/.git/refs/tags/UT ³ï'NGÙbux èèPK ÖZõ>dirtyrepo/.git/logs/UT äï'NGÙbux èèPK ÖZõ>dirtyrepo/.git/logs/refs/UT äï'NGÙbux èèPK ÖZõ>dirtyrepo/.git/logs/refs/heads/UT äï'NGÙbux èèPKÖZõ>E†m{¹%dirtyrepo/.git/logs/refs/heads/masterUT äï'N¨VÐbux èè�‰I 1ÏúŠ>*ÂÐÙUD¼*øˆž¤#�ÉDœø}€u*ªÿ(xCä<îœáó¸ 1ŽQgg|òÞÙ”³Öpyá ®Òzg8T¹óðàg’ħ[%)ClõÊ(¥-*oaƒqñ©U:¬d’.TÖ{8 ~§eèE†m{¹dirtyrepo/.git/logs/HEADUT äï'N¨VÐbux èè�‰I 1ÏúŠ>*ÂÐÙUD¼*øˆž¤#�ÉDœø}€u*ªÿ(xCä<îœáó¸ 1ŽQgg|òÞÙ”³Öpyá ®Òzg8T¹óðàg’ħ[%)ClõÊ(¥-*oaƒqñ©U:¬d’.TÖ{8 ~§eèdirtyrepo/.git/objects/a7/UT äï'NGÙbux èèPK ÖZõ>ÇÕ×��@dirtyrepo/.git/objects/a7/63aa560953e7cfb87ccbc2f536d665aa4dff22UT äï'N(TÐbux èèx�ÍK Â0€a×9Åì…’GóЏUð1™è`ÒH;½¿oàö_|ê­ƒ–öÀ " ñ:Ä”0›¥3Ñ¡ F&«�=_òÃGlqãW_à¶UÂîÔ™¦Fo>¸dÊxy¶HuH½�A¥ô(•á(µ”b¯û—ñA\gbŠ~ôŒ+C¡Š«ørÍC¨PK ÖZõ>dirtyrepo/.git/objects/e3/UT äï'NGÙbux èèPK ÖZõ>Øìîöšš@dirtyrepo/.git/objects/e3/728acced34e063a6e5830c52659f7fdb7a7858UT äï'N(TÐbux èèx+)JMU010f040031QH.-*JÍ+‰OËÌIex6÷ÑìM¯9{wk®+�ºqèIOðD¨:�||JjNjIj >u¹ù)™i™©) ,.IL*KÎHÌKO-Æg$ªJ°Áĸ›6˜ëˆ°âW¢Bœ…×| P(.MJÉ,bØ|R9¯sý§mªkWuÄ_¼ö_¿¦· PK  [õ>dirtyrepo/.git/objects/c2/UT \ñ'NGÙbux èèPK  [õ>µK±(!!@dirtyrepo/.git/objects/c2/17c63469eca3538ca896a55f1990121a909f9eUT \ñ'N¨VÐbux èèxKÊÉOR04gðÍOÉLËLMQHÎÏ+IÍ+áj¢nPK ¼Zõ>dirtyrepo/.git/objects/pack/UT ³ï'NGÙbux èèPK ÃZõ>dirtyrepo/.git/objects/e6/UT ½ï'NGÙbux èèPK ÃZõ>´ ¤˜@dirtyrepo/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391UT ½ï'N¨VÐbux èèxKÊÉOR0` °ðPK ¼Zõ>dirtyrepo/.git/objects/info/UT ³ï'NGÙbux èèPK ÖZõ>dirtyrepo/.git/objects/b8/UT äï'NGÙbux èèPK ÖZõ>¾ ”ÜNN@dirtyrepo/.git/objects/b8/5d53c9236e89aff2b62558adaa885fd1d6ff1cUT äï'N(TÐbux èèx+)JMU042d040031QH.-*JÍ+‰OËÌIex6÷ÑìM¯9{wk®+�ºqèIOðD¨º”ԜԒÔ‚êróS2Ó2ñ)­<9‚PK ©œîTdirtyrepo/.git/objects/ea/UT MTÐbGÙbux èèPK ©œîT%GîU@dirtyrepo/.git/objects/ea/10ec85c10797d76afeef56f8bfc26da40913bbUT MTÐb¨VÐbux èèxKÊÉOR°`ÈLÏË/JMá(!êPK¼Zõ>7‹?Idirtyrepo/.git/descriptionUT ³ï'N¨VÐbux èè ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK ÖZõ>aAEödirtyrepo/.git/COMMIT_EDITMSGUT äï'N¨VÐbux èèInitial commit of test files PK ¼Zõ>dirtyrepo/.git/info/UT ³ï'NGÙbux èèPK¼Zõ>w=Í!­ðdirtyrepo/.git/info/excludeUT ³ï'N(TÐbux èè-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK ¼Zõ>dirtyrepo/.git/branches/UT ³ï'NGÙbux èèPK ¼Zõ>dirtyrepo/.git/hooks/UT ³ï'NGÙbux èèPK¼Zõ> Â\*&dirtyrepo/.git/hooks/pre-commit.sampleUT ³ï'N¨VÐbux èè}T[kÜ:~ŽÅÔ I±w¶!IÙÂ’}9�óÐSŠl�×êÚ’+É›l)ýíý$Û[ÒSúæËܾËÌá‹Y¡ôÌÕÉarH+Mü$Ú®aª�Ù�+­êOÐV'b-”v~Bn'‡t§•W¢§º!©ªŠÆÈ€�ÛÎïÈ[f2Å.ý¯:‹âêâ•,/eqÉŵ˜_ÎùÕ¢¨®äåõÅÕUU0/®ç‹¤R °ÞU´3}$$ ‚6æ1r+\©TDp;rìð[N‹�o{Γ˜„œ˜²Ññqöƒñ¼oM=½¾—×.dirtyrepo/.git/hooks/prepare-commit-msg.sampleUT ³ï'N¨VÐbux èèuS]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘\I¦”É�ç$9! “å”ÝÛÛ=e¯Ë¥Ô¥ë&Ù$ƒ øS¬z…Ðó®²²÷à ô{a|‡P™ÕJzP¦…:'Z,{)”–ÏÀZºMbð$}QZ¬LÏ�T�Jxè„ Â�¬#c�QÊ<%Æ€©1©‘Foh„°#hæÀ™ÁVXÌ»4ÕúÁöÆ!H&Ášºü Uò*‘G!•K3ÐFó_h 8/üàr‚îØAìbi¬Çºˆ^Ω•Ë8(ݺ,Fb%™`€�ÞòDÄW®eÅ$0lp u¥2€€q“‘Çl¤uÉpÔÞ�üh)»4ºQ²òðÁ8AóÚv#{ÔJ,+£ëÝV¨ú!¢b¦µlà<ŒÀ“À-#¼Ô4GlÓû>�¤%6fÜ™ˆO6Ò«ƒmcö¤=„�•BVÔÍ Ó³0X�)Ω3i46ýv�ƒpÖÛíqÆ!‹m½ÝfQ×ÜÈVcÍMÓpÚ*%u à…𸕕ÐDã¼TŠÅ•¡X!%B_Kq©g"l�!ËjX%H7›¾Í§ï92�dt~@'€rp6>µ­.‹¥xNf®¼-3(s CY™• (o·á•P@åSz…äôŒš38= ;’¯=҄‡>Ùû�ƒ³xMŸHl¡p¿“k%*2ó¯€ï'Ù«¤ˆ”ííÁ4îÛÑœ�Á›1‡á€NTAÚÍõû³é~àÿ!,|ºšß]|›¾þzwõáã—9¬i㨙Ž,ö‹ÃóÅAq8-_dt‹ã²Ÿ…ÑZz%À°Û)Q³Ôx½¬:zD©v~Ë“ßPK¼Zõ>`÷‘X Ä*dirtyrepo/.git/hooks/applypatch-msg.sampleUT ³ï'N¨VÐbux èè}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PK¼Zõ>%[9ýŽ*dirtyrepo/.git/hooks/pre-applypatch.sampleUT ³ï'N¨VÐbux èè}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PK¼Zõ>R¼­v"dirtyrepo/.git/hooks/update.sampleUT ³ï'N¨VÐbux èè­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPK¼Zõ>ƒlÇz 'dirtyrepo/.git/hooks/post-commit.sampleUT ³ï'N¨VÐbux èè-ŒA ï¼b WMîÞü€'?°!‹PKeIUž/‰^»gÚ^¦9–Iƒ±ÆâQÀ;åšAäuk¬ -PCT8J‰�o¼‚ ›s¬ê·Ô¯NrŽç*ÓÂã™{ ¸ÐÜk-tq$¯X;ÊâãáCm·_b�¹ã)}PÞæ PK¼Zõ>B+ék&dirtyrepo/.git/hooks/pre-rebase.sampleUT ³ï'N¨VÐbux èè¥XksÛ¸ý,üŠ»²º–R=l}è´ñ:ÇÎìfÛ&3©§;�ı! ’¸& £hcÿ÷ž €"-+ÙN«Ù|Ü÷=÷|7™¥ÅÄ®Ä�8  ]nLº\9ê'šýiÈÿL?WEªé‚~’¹,´§½Z)ê–F�ŒšI«º´ÒúŽRK¦*è×Ê:š©…6 Z¦Žj"ë¤q–æ:-–’âúW=’,æ”È‚ ð“*9HOtžóó…Ñ9K-ÀC³ ©Ï©ãËuê`6ºý¦ŒfÙ®²ã­uµA‰Ì25÷ä^îBg™^³„R™+§Œ}î¹zÇ4yšª´Î(™û«Lª,­¥¯¹SÁ¤13Lk†™‘E²‚Ï,7x;§¾6¤òÒmh½RExÌï½w•1ìi`ÔfÃ^+ó2ƒÖ•^[8±&§›¸è2M"Lr+éH"È2ƒµó DäÊ,¡<‡…úìkË|—ʹ–�CØ›ÈÊ2 Iê c­«l"[eŽÒ–áQ •Õ,Kí z`°Óf3">;e­‚ÅŧÝÞqW¤ r ˜èöºtJS�¢ãÝ9íµ°“$ÛIoÚ*³ª~wËè±›|¦³4ÖôÓ«óË[º¿Ñ V€�ÀÖ"�¦*]cô\9™°­Ì)© ¬ƒ)^C,¢eÀÙÙäÙ@tNNÿû¶ / ÇÔÊÊD vo�0ÐrRæJf5PI>ÊÝc €Müžô‚ri!LôÚ"ôö¯œË@ˆû3Öq©}îU‰œd™¯ ‹÷>`€�Öèß�Ÿ÷÷ô%+M/¾ŸR÷ J¦bS‚E‘,ú|,Xl:«hÀ¬<ÈMZÜ„Û�-@u$8”`ëÜæT �Pô1�mMºÝâbô>’Õ�iì L\Ñû�9AP‹7*ן8Xã­71ƒ‰¶;^þ•o5Ȥ(3ô%7öiùŽÒ3-m ð3¤gAVÓFWmËØ˜©wÐ/t‘mn˜áæx'8ÛX|¬óÓûKè�î!Ú¸Ûûô+ìÍg{Su-;¸þÚ¦u¤cø[u·ÕÖÔފξÌÕ òö¦®î U9rz4—N…ò¨“ÞùŸÖ -#–«è �áI4Œý]p¶Ø´3©¬ñ3²T&£‘¢CèÉ7±\ËÞù»ÿùþèú$>Ïí’#ü¬¦Xalð(ãYW{߀ɫ¬»ÂóE7Šéÿ¡eúsYrùv:“�ý÷G£¿ÈÑâú�š0}§�Éuú‚Ž|÷@¶ÌRן|(&ÃhÞñµ…ñEl£ÊTNý­È™ýñ³A/î¼’{Sïß^ÙÓëÁ È&úßù6d©×²ÿ‹W8z�(=DRO˱ªï;¥A*éW—¯Þ½óQ tªbŽFÖ�ÖኈkIr)±ôUÊ•HóԨġ[iQßa¡ùšÞmOˆå¯­ak «†:ÐÏ‘^§æcßu¹å*”ϬJÑšá™òŠÁ†­`~%M–rû(å�²X5Á¼~bQ‚„Þ/9.~Ùñü˜M½p¾¬�ƒ)>�¾ aà:“ú *QÖJ³ñ½*z�³f¾P˜ 0&Þ)U²'¦Þ È¦ Œ¶_Ð@ýÔiûêÛ-‚Á¸“•,– \b�5І!¬lwC "plbÒ’[«vpõÙCtü™y¢çq׍կ˜Øœg@E©¡9‡|!ŸR™…Ù|Ç£9)é²2rôã¥èýøúêæòõ»Ix[Ù—Ôb±¿„]’jX}(Âbº–~¦»B¯ïþñ€Æã1¡£ì �[¯#Q«éƒ‘¡€–²Ýú㳈/?ëª2�oáÓ²ÂH†¿gN2úª¬�<1Tˆg8Ù8¾luXPØ´S]~£|l̰¶êcLÀEr±Ò9s�µTšQDôÉÐ\}!#ÛÒ ¤À¦ÔEGAûÅEô§ßŠªèî¶­ /øŠ�<¬‹�Ì’‰þÎ!ر¢@o¶î,Qž{Oaã¤ÕöÈÚljô d×Dt£2_Ø\ Û�”Òùûf†ÂâjòìM/Äz—¶ÚÍ¢2^},¹aŒl\:£rn-E}l +YÚæô‰þ¯|ñ �;±âÉ ¯C«iB)þ¦Ü¡¥Œç…t¡(b…û�xôh4úÝo½ç˜Ä•±þÿäš©ø�'gt¾—h²ólMë)HÛ~éåSÊIëÛ|>„ \áþÀ÷¸‰„V~îëK&-ÒÏhò±$ùÇ„X9èMOò}«.íU((dirtyrepo/.git/hooks/post-receive.sampleUT ³ï'N¨VÐbux èèeQËrÂ0 ¼ç+T¸6˜<I‡a¦—ÎôÞPl™x vÆv(ýû*†C7K«]yWë'Ñ+Â�­³5¼Z ŽÓ…`pî Az3EÐÎCV“ 1÷$É\it%7‰øñ}pM?[@ÉÃÌ'”g0JIS$K�•Ъ´ËË™èüWì‰,Ì“BÞ¼'á CX¸þ4�ü•Æ2×»ùÄv DÅujÑbaäÜEyºá`éóþð¤-Žtdø��>xY¦ë]U�*úŠêŽöºÑ][×…Æ]ÓÕ�RR5Xn¡iõ{]ãeÙn÷mÛé]ÙËm]‘TUÑï;Ý5ÛÒA „*ˆg’â D ��Þôb >ˆ”9BHy.f+ݸØL~,Ý"\Œ¥%0Vð´x¸[5|¾èþÜc“eë ˆ9x>6zÊIq21—Ž‹ßËsÑ\²oPK¼Zõ>éøÊ÷€&dirtyrepo/.git/hooks/commit-msg.sampleUT ³ï'N¨VÐbux èè}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPK¼Zõ>š ÷Àн'dirtyrepo/.git/hooks/post-update.sampleUT ³ï'N¨VÐbux èè-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPK ¼Zõ>+is§dirtyrepo/.git/HEADUT ³ï'N GÙbux èèref: refs/heads/master PK©œîTd| ƒxdirtyrepo/.git/indexUT MTÐbMTÐbux èèsñ rf```bÞ¤ !ÞÆGTØa4PL†E{F<CãæÌÀñJàMëAöé׳þ½û±ÿPîNáÝ \zé™%™éyùE© P4G^Z«d¾Ÿú‹ÛP! yÓ_ ™Çðlî£Ù›.^söîÖ\WuãГžà‰ <É¥EE©y%ñi™9PýÔß—#Ó@§°10ÊM%Î<�9ñ)©9©%©)xÍ‹"Ê<ÞÜü”Ì´ÌÔ„þý(‡äß×hæ ?f’ùfqpÏŠiKã%'IM˜?��¯¸$1hZrFb^zj1Ä]Õ�i¨û¦ež4ªyñ˜Þ™«�LCÍ�%Ê|læÃÂ=þ_ú!…Ǣ— j~^j9ܽ/£�i¨{‰2OazXÍKb@¢¡æ&e®º¹ˆ0@øÿ•’ÿße®pqiRJf‘>f6ºó•,2 uo*IæB½�f®"2 57�(sE æbf †õý{Ì==á@ž¥'fèOuó°ûÛPKªœîTÛšáÛp‹dirtyrepo/.git/configUT PTÐbPTÐbux èè5Ë1! …áNÁ ðždÇ"BÀ¬Y„ c³g _÷Íüo£–xf¼ù°ÖÁáÎØUvL6â[ô+Y’V¨ú-‰®Ö)vd¢Ÿ"z€½PI[ß‹w…ɸd:Ñ»;èx,±T`V,³g0ÿì PKY^ïTž=ìÝ dirtyrepo/.gitignoreUT y8Ñby8Ñbux èèËLÏË/JMáÊ„ÒPK ¶œîTdirtyrepo/untracked_dir/UT gTÐbGÙbux èèPK ¶œîT&dirtyrepo/untracked_dir/untracked_fileUT gTÐb¨VÐbux èèPK Z^ïTdirtyrepo/ignoredUT |8Ñb|8Ñbux èèPK Z^ïT íAdirtyrepo/UT|8Ñbux èèPK Û[õ>Ö/"¤�Ddirtyrepo/staged_new_file_modifiedUTÍñ'Nux èèPK ®[õ>Ö/&¤�·dirtyrepo/staged_changes_file_modifiedUTwñ'Nux èèPK –Võ>¤�.dirtyrepo/current_fileUTÛè'Nux èèPK RWõ>¤�~dirtyrepo/new_fileUT;ê'Nux èèPK �[õ>@ÅçC%¤�Êdirtyrepo/staged_delete_file_modifiedUT:ñ'Nux èèPK }[õ>@ÅçC¤�:dirtyrepo/staged_changesUTñ'Nux èèPK ÓVõ>¤��dirtyrepo/staged_newUTNé'Nux èèPK à[õ>íAëdirtyrepo/subdir/UTÓñ'Nux èèPK @Wõ>¤�6dirtyrepo/subdir/current_fileUTê'Nux èèPK VWõ>¤��dirtyrepo/subdir/new_fileUTDê'Nux èèPK æ[õ>@ÅçC¤�àdirtyrepo/subdir/modified_fileUTàñ'Nux èèPK {[õ>@ÅçC¤�Idirtyrepo/modified_fileUTñ'Nux èèPK [^ïTíA«dirtyrepo/.git/UT}8Ñbux èèPK ¼Zõ>íAôdirtyrepo/.git/refs/UT³ï'Nux èèPK ÖZõ>íABdirtyrepo/.git/refs/heads/UTäï'Nux èèPK ÖZõ>çé'))) ¤�–dirtyrepo/.git/refs/heads/masterUTäï'Nux èèPK ¼Zõ>íAdirtyrepo/.git/refs/tags/UT³ï'Nux èèPK ÖZõ>íAldirtyrepo/.git/logs/UTäï'Nux èèPK ÖZõ>íAºdirtyrepo/.git/logs/refs/UTäï'Nux èèPK ÖZõ>íA dirtyrepo/.git/logs/refs/heads/UTäï'Nux èèPKÖZõ>E†m{¹%¤�fdirtyrepo/.git/logs/refs/heads/masterUTäï'Nux èèPKÖZõ>E†m{¹¤�Ddirtyrepo/.git/logs/HEADUTäï'Nux èèPK ©œîTíA dirtyrepo/.git/objects/UTMTÐbux èèPK ÖZõ>íAf dirtyrepo/.git/objects/a7/UTäï'Nux èèPK ÖZõ>ÇÕ×��@$�º dirtyrepo/.git/objects/a7/63aa560953e7cfb87ccbc2f536d665aa4dff22UTäï'Nux èèPK ÖZõ>íAÄ dirtyrepo/.git/objects/e3/UTäï'Nux èèPK ÖZõ>Øìîöšš@$� dirtyrepo/.git/objects/e3/728acced34e063a6e5830c52659f7fdb7a7858UTäï'Nux èèPK  [õ>íA, dirtyrepo/.git/objects/c2/UT\ñ'Nux èèPK  [õ>µK±(!!@$�€ dirtyrepo/.git/objects/c2/17c63469eca3538ca896a55f1990121a909f9eUT\ñ'Nux èèPK ¼Zõ>íA dirtyrepo/.git/objects/pack/UT³ï'Nux èèPK ÃZõ>íAq dirtyrepo/.git/objects/e6/UT½ï'Nux èèPK ÃZõ>´ ¤˜@$�Å dirtyrepo/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391UT½ï'Nux èèPK ¼Zõ>íANdirtyrepo/.git/objects/info/UT³ï'Nux èèPK ÖZõ>íA¤dirtyrepo/.git/objects/b8/UTäï'Nux èèPK ÖZõ>¾ ”ÜNN@$�ødirtyrepo/.git/objects/b8/5d53c9236e89aff2b62558adaa885fd1d6ff1cUTäï'Nux èèPK ©œîTíAÀdirtyrepo/.git/objects/ea/UTMTÐbux èèPK ©œîT%GîU@$�dirtyrepo/.git/objects/ea/10ec85c10797d76afeef56f8bfc26da40913bbUTMTÐbux èèPK¼Zõ>7‹?I¤�¥dirtyrepo/.git/descriptionUT³ï'Nux èèPK ÖZõ>aAEö¤�8dirtyrepo/.git/COMMIT_EDITMSGUTäï'Nux èèPK ¼Zõ>íA¬dirtyrepo/.git/info/UT³ï'Nux èèPK¼Zõ>w=Í!­ð¤�údirtyrepo/.git/info/excludeUT³ï'Nux èèPK ¼Zõ>íAüdirtyrepo/.git/branches/UT³ï'Nux èèPK ¼Zõ>íANdirtyrepo/.git/hooks/UT³ï'Nux èèPK¼Zõ> Â\*&í��dirtyrepo/.git/hooks/pre-commit.sampleUT³ï'Nux èèPK¼Zõ>½¾—×.í�Ydirtyrepo/.git/hooks/prepare-commit-msg.sampleUT³ï'Nux èèPK¼Zõ>`÷‘X Ä*í�Xdirtyrepo/.git/hooks/applypatch-msg.sampleUT³ï'Nux èèPK¼Zõ>%[9ýŽ*í�Ædirtyrepo/.git/hooks/pre-applypatch.sampleUT³ï'Nux èèPK¼Zõ>R¼­v"í�'dirtyrepo/.git/hooks/update.sampleUT³ï'Nux èèPK¼Zõ>ƒlÇz 'í�ù!dirtyrepo/.git/hooks/post-commit.sampleUT³ï'Nux èèPK¼Zõ>B+ék&í�Ô"dirtyrepo/.git/hooks/pre-rebase.sampleUT³ï'Nux èèPK¼Zõ>}«.íU((í�+dirtyrepo/.git/hooks/post-receive.sampleUT³ï'Nux èèPK¼Zõ>éøÊ÷€&í�Ô,dirtyrepo/.git/hooks/commit-msg.sampleUT³ï'Nux èèPK¼Zõ>š ÷Àн'í�+/dirtyrepo/.git/hooks/post-update.sampleUT³ï'Nux èèPK ¼Zõ>+is§¤�0dirtyrepo/.git/HEADUT³ï'Nux èèPK©œîTd| ƒx¤�z0dirtyrepo/.git/indexUTMTÐbux èèPKªœîTÛšáÛp‹¤�K2dirtyrepo/.git/configUTPTÐbux èèPKY^ïTž=ìÝ ¤� 3dirtyrepo/.gitignoreUTy8Ñbux èèPK ¶œîTíAd3dirtyrepo/untracked_dir/UTgTÐbux èèPK ¶œîT&¤�¶3dirtyrepo/untracked_dir/untracked_fileUTgTÐbux èèPK Z^ïT¤�4dirtyrepo/ignoredUT|8Ñbux èèPK==a4libgit2-pygit2-d86662e/test/data/emptyrepo.zip000066400000000000000000000244101521317216500212740ustar00rootroot00000000000000PK aA emptyrepo/UT €�’P Æcbux ééPK �ޤTemptyrepo/.git/UT >¡rb Æcbux ééPK aAemptyrepo/.git/hooks/UT €�’P Æcbux ééPKaAš ÷Àн'emptyrepo/.git/hooks/post-update.sampleUT €�’P Æcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKaAR¼­v"emptyrepo/.git/hooks/update.sampleUT €�’P Æcbux éé­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPKaAípÙž¨&emptyrepo/.git/hooks/pre-commit.sampleUT €�’P Æcbux éé}T]oÛF|6ÅF6ì)YP Û… n€0ú�èCGÞžx5yÇÜ%+úÛ3w¤d8-úÆ�ý˜Ù�Ùã7³R›™¯³ãì˜V†øY´]ÃT[ûD¾rº ,mØiµ£m-iO¢´}ú^2U¶mu, T¸MÃ’ÊMÖ:Œÿ&´Õ¡&cI¸uß² ¾ z¬÷]jÛ7ÉüŒ”1ÔäߨYòA„ýT`‡Æ¾×fMÂ�è:g;§E`jÙ{±fÒ 5b�œ¶£Pï!‰ã£%6¢ÅPƒID0%‡OíøEéøÏÒ¤sœ�Š,ÓŠ"#Ç›¼Î3åù8”ïW¿ÒÝLòffú¦¡ÅÝéE†¶&;k¡�·1$ãÆsvtL�Z4#ª’Z)##7n»°£à˜É–s^ê,Ë«Å;Y].UyÉ嵘_ÎùݲTWòòzqu¥Jæåõ|™)��ëGE;Û§�DFØ�ݦ٠_i�˜FÞž<‡�üF`¦å0€àz.²”„œ”r{òvX«Qz�fç‹Wç±í'–Ú7A$Ý $;WdüÌ]Ü�.bܽ³ÞS׈ ¬k �l±:62áÝX-ÿ ï/ˆå t„"tKÊÙZLâ�’Sv\¼ãÎz¬ÛôC`]cuH¿”¨âƒHo¨1™�¨;a &(ÏËðŸ|'*è¨i�¢B ôƒ\ƒn$F‰|¦ÉÉ«�LèÍ-Mâ('ô…NO£~³�­©‡–¬¢•Ÿ8vt¶Gu�ù�P°ؤfÇSz«Ã™�E"ðûÚcÔrJ˜ uÖ��nt”�¥ßmƒmzº˜ŸyšõÞ%«w>%¯M•´˜¸}í…ã=x7@5†(£À‡´I¡’½^˜NЊìÁ�Y$!çy%ªyw•[ÓìðÿåX †w»¢ü�ìÿ=;"z¸ÿkõðp{Iç’Î>SþÏ—?çgô�¶åÕyã|tW5ìùÞ9ënh…ÓÓG «ÇØ^ë…"ˆb2$íS£Ô+X­qúöÞz\�W~ÙZ÷4Ùç¤Ewlãq´†, ¸ƒvý¿:¤Ó8, z¸šr£ýÞ]‡“Ã΢ø¹ÆèÞ'Ó¦Ãß⦤�B�o‘ƒÜשZl z‚´xóSY¢ÿ·pòýKN¼É/÷$Òæ„b[kì>ù‚ã*ütðP¢d•‚="ÌÃ|2�ºÏÀ^2¹6’Ÿ£p胀 ÉóìPKaA,ß.—ÙW&emptyrepo/.git/hooks/pre-rebase.sampleUT €�’P Æcbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘•29MCO±�å‚X.Þ~÷Ïw'×gñyaWág ÅcƒGϺÆûL^eÓž¿/ûQÌðÓGXÈŠË·×›}¾;™üUNÒë?�hÆô½!&×ù :ñÝÙ*ÏÜpö¾œ�£y§×þƱ�*W w"÷d§ÏFƒ ¸÷.H̽eϯG£ ˜~ãÛ�¥AÇþÏ^áä¢ôI=-Ǫ¹ïU©¤\½þöí[Å`@¯.—hdíƒñ„ºO�‚ÓëBO†ïqÛÿú]“úOûMÓø¸ú#�ÄÑÿù¢;ÒýFbeªVµ4K{pš¯%Z×B¡€Ú!ëgw310œË唣t°Ž¤\ÒÚºàqJã¹ôŒ~.µ?wx�±A`?:>Þ]ù¢ân'ÐO"FfÍsH®$6�¡Ê¸üh™•84a*+›;l1_Ò»ëÚ b±¼Øu5ìŒaÕPú%rêÔrê[-÷ÙR…šYÔšB;13Þ‹ ذ̯¤É3îÙ@¢¼Sû%˜7O,JP�ÐÀK%ÇÅo8^€Ÿ­™Η�q0ŧÓ÷LYg2„R%ÊZi¶¾AE ÖÌ ãÆÀÄ;¥*öÄ4kÙŒ�Âö º¦5]_}�E�#w²–劣ƒK¬±AÑ8„•í.bhÞAŽMLVq?õ Ââ­>yˆŽ�?³Lô2.�Ú³ë²à ¨(5tä�/Dãc&ó0�ïxþ"'eš­j#@?^ŠÁwo®n^¿y; o`+û’YlÓâ—° CRë±EØF7Ò¯¿tWê à=<Ñt:%´‘=¤q¿õaä"êtÚ`0Rø/ЊC¶_|ñåg]]eË|:VÉð÷ÌI®@_WÍ9'† ñ ÇÇ—Ý¢û ›öªË¯‘�Í@€6ÀVs–à)©¸H¦)�Ι ¬•ÒŒ"¢WH†æê Ùu�N ½6¥ (: *Øo+b8ÿZT}@÷w¯]mxÁW|Îa]ìD(`–LôwÀ޽zóíxosòÜ Ç«®�@Öv7E£!¾$š •ûÂæÙ­Éð ’·Ø73W“go{!vº¬ÓnÒÚxõ±äÆ1²qӌʹyt ±*¬eeÛ#'ú¿òÅ3Bîp®ŠÇ1¼ 8 ­¦ ¥ø›rÇ–ržÒ…¢ˆ ì÷zàÑ“Éäw¿ôžc÷Äæÿ“k¦âœü]ÐÅA¢ÙÞ³4�§üM m÷¥WO)g�oûyÿ„°K·ð&^>¢Œt3úºÈ™�úÀ6�Í– ÄÅ.p…_úSÞã&Zù…¯l–”fŸÐäcIò/±rÐ7Úžäy^yž4+}ÅòÔs_¨ºÈÛ m6%vcö‹oùg_"¡W7“Ôë¹ôR¹ì÷F†VmnÅj¯ë† ü2ÊÛý|€#&ÿzp:ž*±®¹RâÕÅW< ‹ªF?@ëf<Î|Ñô¿!¦ˆON!ˆ:÷Tö˜Ú•»ã ö&oÝë3jL<�OÃl�JÂØ÷¿æ4MàË­ð?PKaA`÷‘X Ä*emptyrepo/.git/hooks/applypatch-msg.sampleUT €�’P Æcbux éé}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PKaA%[9ýŽ*emptyrepo/.git/hooks/pre-applypatch.sampleUT €�’P Æcbux éé}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PKaAéøÊ÷€&emptyrepo/.git/hooks/commit-msg.sampleUT €�’P Æcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKaA½¾—×.emptyrepo/.git/hooks/prepare-commit-msg.sampleUT €�’P Æcbux ééuS]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘\I¦”É�ç$9! “å”ÝÛÛ=e¯Ë¥Ô¥ë&Ù$ƒ øS¬z…Ðó®²²÷à ô{a|‡P™ÕJzP¦…:'Z,{)”–ÏÀZºMbð$}QZ¬LÏ�T�Jxè„ Â�¬#c�QÊ<%Æ€©1©‘Foh„°#hæÀ™ÁVXÌ»4ÕúÁöÆ!H&Ášºü Uò*‘G!•K3ÐFó_h 8/üàr‚îØAìbi¬Çºˆ^Ω•Ë8(ݺ,Fb%™`€�ÞòDÄW®eÅ$0lp u¥2€€q“‘Çl¤uÉpÔÞ�üh)»4ºQ²òðÁ8AóÚv#{ÔJ,+£ëÝV¨ú!¢b¦µlà<ŒÀ“À-#¼Ô4GlÓû>�¤%6fÜ™ˆO6Ò«ƒmcö¤=„�•BVÔÍ Ó³0X�)Ω3i46ýv�ƒpÖÛíqÆ!‹m½ÝfQ×ÜÈVcÍMÓpÚ*%u à…𸕕ÐDã¼TŠÅ•¡X!%B_Kq©g"l�!ËjX%H7›¾Í§ï92�dt~@'€rp6>µ­.‹¥xNf®¼-3(s CY™• (o·á•P@åSz…äôŒš38= ;’¯=҄‡>Ùû�ƒ³xMŸHl¡p¿“k%*2ó¯€ï'Ù«¤ˆ”ííÁ4îÛÑœ�Á›1‡á€NTAÚÍõû³é~àÿ!,|ºšß]|›¾þzwõáã—9¬i㨙Ž,ö‹ÃóÅAq8-_dt‹ã²Ÿ…ÑZz%À°Û)Q³Ôx½¬:zD©v~Ë“ßPK aAemptyrepo/.git/info/UT €�’P Æcbux ééPKaAw=Í!­ðemptyrepo/.git/info/excludeUT €�’P Æcbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPKaA7‹?Iemptyrepo/.git/descriptionUT €�’P Æcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK aA+is§emptyrepo/.git/HEADUT €�’P Æcbux ééref: refs/heads/master PK aAemptyrepo/.git/objects/UT €�’P Æcbux ééPK aAemptyrepo/.git/objects/info/UT €�’P Æcbux ééPK aAemptyrepo/.git/objects/pack/UT €�’P Æcbux ééPK aAemptyrepo/.git/branches/UT €�’P Æcbux ééPK aAemptyrepo/.git/refs/UT €�’P Æcbux ééPK aAemptyrepo/.git/refs/heads/UT €�’P Æcbux ééPK aAemptyrepo/.git/refs/tags/UT €�’P Æcbux ééPK�ޤT75ÁO\emptyrepo/.git/configUT >¡rb>¡rbux éé5ÊÁ € Ð3Lá.à$ÆC•_CR,iÁÄíŃǗ¼õPáªç¦ö°Z¡vÃ<ë5-ÓgAÑ„¡f1ìd˜Ä‡DO1p¯‰üo/PK aA íAemptyrepo/UT€�’Pux ééPK �ޤTíADemptyrepo/.git/UT>¡rbux ééPK aAíA�emptyrepo/.git/hooks/UT€�’Pux ééPKaAš ÷Àн'í�Üemptyrepo/.git/hooks/post-update.sampleUT€�’Pux ééPKaAR¼­v"í�Çemptyrepo/.git/hooks/update.sampleUT€�’Pux ééPKaAípÙž¨&í�™emptyrepo/.git/hooks/pre-commit.sampleUT€�’Pux ééPKaA,ß.—ÙW&í�— emptyrepo/.git/hooks/pre-rebase.sampleUT€�’Pux ééPKaA`÷‘X Ä*í�Ðemptyrepo/.git/hooks/applypatch-msg.sampleUT€�’Pux ééPKaA%[9ýŽ*í�>emptyrepo/.git/hooks/pre-applypatch.sampleUT€�’Pux ééPKaAéøÊ÷€&í�Ÿemptyrepo/.git/hooks/commit-msg.sampleUT€�’Pux ééPKaA½¾—×.í�öemptyrepo/.git/hooks/prepare-commit-msg.sampleUT€�’Pux ééPK aAíAõemptyrepo/.git/info/UT€�’Pux ééPKaAw=Í!­ð¤�Cemptyrepo/.git/info/excludeUT€�’Pux ééPKaA7‹?I¤�Eemptyrepo/.git/descriptionUT€�’Pux ééPK aA+is§¤�Øemptyrepo/.git/HEADUT€�’Pux ééPK aAíA<emptyrepo/.git/objects/UT€�’Pux ééPK aAíA�emptyrepo/.git/objects/info/UT€�’Pux ééPK aAíAãemptyrepo/.git/objects/pack/UT€�’Pux ééPK aAíA9emptyrepo/.git/branches/UT€�’Pux ééPK aAíA‹emptyrepo/.git/refs/UT€�’Pux ééPK aAíAÙemptyrepo/.git/refs/heads/UT€�’Pux ééPK aAíA-emptyrepo/.git/refs/tags/UT€�’Pux ééPK�ޤT75ÁO\¤�€emptyrepo/.git/configUT>¡rbux ééPKÔ libgit2-pygit2-d86662e/test/data/encoding.zip000066400000000000000000000414571521317216500210500ustar00rootroot00000000000000PK ¶¤›L encoding/UT ÷mãZÆcbux ééPK 2[—Tencoding/.git/UT ÆcbÆcbux ééPKj¤›L…Jð[\vencoding/.git/configUT gmãZÆcbux ééMÌA Ä Dѵž"GÈr’!‹Ž)¡µ¥Ô@ng3dùàó?ÁˆÝ;¢ZKÝxGc–~�-YY¶eõ.&E¶S�Þ¢h›RûŠ*G=¥£ý³JËsŒQRx PK 2[—Tencoding/.git/objects/UT ÆcbÆcbux ééPK 2[—Tencoding/.git/objects/61/UT ÆcbÆcbux ééPK ¥›LGÏÎÿ::?encoding/.git/objects/61/f0b8751112c206b3d000d2ba53a5aca49ad180UT ¦nãZÆcbux ééx+)JMU01b040031QÈ,Î×µ°0µÔ5Ô+©(aP�ã™Ðg;?(-ò¾]hŽ-›…߇û]“PK 2[—Tencoding/.git/objects/6a/UT ÆcbÆcbux ééPK Ѥ›L—”ƒÁÄÄ?encoding/.git/objects/6a/a416ac00edba818fc1d4b16305298fb6383b46UT *nãZÆcbux ééx¥ŽAnÂ0EYçsÅÆŽ„*6 ±êذœ!ž’Œ%ã¶§ÇU�Àò}}½ÿ}œgΠ×Ý"'"PŠð¦¯ä®N«Þbï´iQe:ÝÒÊym:G~çìßá.üõȰ¡ÂMáæùÇÛqFžçPVgÍJ¨ÛuÛV%-Ó™Þ’TáÌ8Á¿ ~9@¸ñD@âã@°ÀáøY[ëúZ•¢ddaKO¢ÔçÓÞ-!𠦡Üñúr¬©^/ZvPK 2[—Tencoding/.git/objects/20/UT ÆcbÆcbux ééPK ¥›L/0é×&&?encoding/.git/objects/20/1e0c908e3d9f526659df3e556c3d06384ef0dfUT ”nãZÆcbux ééxKÊÉOR02bð.Ê,.ÉLÌSðø‘^œ”Z”Ε–ŸÏ¡b ›PK j¤›Lencoding/.git/objects/pack/UT gmãZÆcbux ééPK 2[—Tencoding/.git/objects/11/UT ÆcbÆcbux ééPK Ѥ›LpŠ$l;;?encoding/.git/objects/11/eaf2be8b82195a98240a2414629eae38c2468eUT *nãZÆcbux ééx+)JMU01b040031QÈ,Î×µ°0µÔ5Ô+©(axág<ëøŸÊy¿R—M×­R`Ìi„ïLPK j¤›Lencoding/.git/objects/info/UT gmãZÆcbux ééPK 2[—Tencoding/.git/objects/e8/UT ÆcbÆcbux ééPK ¹¤›L…\íã""?encoding/.git/objects/e8/4e339ac7fcc823106efa65a6972d7a20016c85UT þmãZÆcbux ééxKÊÉOR0´`ð.Ê,.ÉLÌSðø‘^œ”Z”ÎxÄ RPK ¥›Lencoding/.git/objects/40/UT ¦nãZÆcbux ééPK ¥›LW¡³³?encoding/.git/objects/40/935d8766cf7a1a8462d1f914fe48ff446f1df3UT ¦nãZÆcbux ééx¥ÎKn1EÑŒ½ŠÚ@[UþQ-!” Ãl€YùÓ‰“4Ž!V�YÃsO/µu­Œwo}+-yç‰È$ƒ!ÚŒˆÙDñV¼$q³dbT²•s‡ â(HB,9 /‰²‹,z3óƒe]Prí_mƒãVà_Îpªß—û2¬‡õýé÷ÏUê¯Nm=yãØ;g&Ü!ªQÇÕ^^Q-×¥– õÒ&f?O¤û­«bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK 2[—Tencoding/.git/logs/UT ÆcbÆcbux ééPK¥›Lu&X§ìªencoding/.git/logs/HEADUT ¦nãZÆcbux éé•�=OÄ0 †gîWx‰VÉÅIÝB, Ý€€…ÍÍGk¸K¥^ˆ_ON°°žü¼~=ËêPK Ѥ›Lencoding/.git/logs/refs/UT *nãZÆcbux ééPK Ѥ›Lencoding/.git/logs/refs/heads/UT *nãZÆcbux ééPK¥›Lu&X§ìª$encoding/.git/logs/refs/heads/masterUT ¦nãZÆcbux éé•�=OÄ0 †gîWx‰VÉÅIÝB, Ý€€…ÍÍGk¸K¥^ˆ_ON°°žü¼~=ËêPKj¤›L7‹?Iencoding/.git/descriptionUT gmãZÆcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK 2[—Tencoding/.git/hooks/UT ÆcbÆcbux ééPKj¤›LéøÊ÷€%encoding/.git/hooks/commit-msg.sampleUT gmãZÆcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKj¤›Lz¼Äß"%encoding/.git/hooks/pre-rebase.sampleUT gmãZÆcbux éé�XkoÛFý,þŠ[E­¥¬¶>Û¸ŽáØA›índ�‹Ä±GäPbMr¸3Ã(jìÿÞsg†"-+ébÈæã¾ï¹�Ñ“of‹¬œ™Uô$zBçªÚèl¹²4ŒG4?<ü~ÌÿNÿ¨ËLÑ9ý, Q*G{¹’Ô¯´œh¹Föi¥Ô-e†t]Òïµ±´�©Ò Zf–"c…¶†••KÉpý»ZŒI” Å¢$ü(KKÒcUü<Õª`©%xh±!ù)³|¹Î,̦R•“?¤V,ÛÖfºµ®1(y.Gîä¦*ÏÕš%TB‹BZ©Í3Ç58¢ÉÄÑÔ•±ZŠÂÝ©3ih- xõ­ô&M™aÞ0,´(ã|f¹ÞÛ„†J“,*»¡õJ–þ1¿wÞÕZ³§žqÔ˜ {�(ªZWjmàÄš¬j㢪,<0É®„%� ‹Ö&ˆ(¤^B5xJùÉ4–¹(.¥µǰ7µaþ’ÌBÆZÕy"Sç–²ŽáA Uõ"ÏÌ z`°Uz3�¢ð섵F,Þ+>éŽúQ–’•ÀDð¤O'4�‚2ê9wNúZ¦f¶‚d3Ìû‘Ì�lÞÝ0z̦X¨<‹�µ”~~yvqCwwQ��@‡t ƒ% $°µH§®+Û�H+b¶•9£4‹¢ÖÁ§¡–¨cÀééìé(êGüïë*¼Ð@SK#â±{�„�–“’H‘7@%ñ w�6Uò{R)Â@þ”è•A6èÍ/œKOˆûSÖq¡\îe‰œä¹« ƒ÷.`€� Öä¿­ŸwwôÅ+EÏ¿›Sÿ5J¦fS¼E�,ø|ݳ&Øàu¤5«hÁå­<��물ö·>[€êH°( ÀÖÚ͉*%!éƒ'Ûšt³ÅÅä<| « ÒÚ뙸¢÷sŒ Z¯e¡>r°¦[oBclv¼ük×j�+H-QfèKvê0Òñ¥§;Úà§HOJFÑFÕmËØXÈwЩ2ß\3ÃõÑNp¶±øÐägð9”Ð=ÝA´¶7öùØÛÏö¶ê:vpýuÌ›H‡ðwên«­5Ò«½‰zû2× ÈËÛ›º¦ƒÔÕĪI"¬ôåÑ$½÷%¬ç[F(ר‡ú'Á0vô/ÁÙ ` ÐÞ¬6ÚÍÈJêœ&’ §Ø„rA,goúõÝáÕqx^˜%GøiC±ÂØàQƳ®ñ¾“SÙt…gïË~3ü¶cú Qqùöz³Ãw‡“Ä$½úÛˆfLßbr�<§£ßÝ“©òÌgïËÙ8˜wtå^a|Û(sYÐp+rGæpút4ð‚{ï¼äÁÜù·Wöüj4òr€‰á7® tìÿìNž#J÷�ÔÑr¬šû^¥‘Jú÷åÅË·o]½½ºLÐÈÚ÷Ñ#ê>5 Ž®B¾mÿëwMê?î7MããêÿñÇ÷oί_¾¾ˆ¢îPv;…©\ÖB'fï<^ 4Ÿ…D ´cÒMߦçc¼–É”üX(RF·0¦.x zp?‹"zJoÊXîN^AŒØ¦�·W®,¸_Eèe”âI"–³|(3. J2-c‹6 Hdes‡=äKz·}7F –W³®†­1¬ê@Ÿ +V&S×,¹S–Ò£~Qg(ëvæe¼Ù@°f+˜_ �gÜu�%q+ 6D0¯Y£ž �×BŽ‹ÛQœ73'œ/ã`ŠK§ë˜“Vg.¥Œ¥1Bo\‹ ¬™/$:Œ�‰·RVì‰n2Çí7ô=7,º¾º.‰ 0xîx%Ê%Fy—Xcƒ¢±+Û]„ÐzÄz‚ë¬âŽè@…ÕY~r:‰UV„FmˆY‰…7â È Õ÷TŸ/Dãc&r?Roy‚"'eš-k-@?^FƒŸ^]^_¼z;óo`+û’ìÃÑo~ņ¤Öc ¿O®…[`é¶TkÀ{x4¢étJh;HãŽéÂÈEÔé•Þ`¤ð?( %‡l·þøáÊÏØºÊ’-|:VhÁðwÌq.A_WÍI%„ ñôË—Ý¢ö…M;ÕåÁ‡f À `«9 ðœS\$ÒHçÌyÖJ*FÑ $CqõùŒl;H'�N›Òìö�h8ÿZT]@w·§mm8Á—|Ra]ì„/`–Lô/ÀŽÍzóÍxg÷qÜ{ ¤®�@Öf;ƒ>®$š k™»ÂæÙ.ºð Ö…Ø53W“co{!¶²¬ÓnÒZ;õ¡äÆ!²aW ʹyt 1ìW¢2í¡ý_ºâ!w8…^yúVÓ†2ú§´†ržÂú¢ ì÷zàQ“Éä/¿ôŽc6½æÿ£k¦âœü]ÐÙ^¢Ùγ=4�§ü�!mû¥�)g�oûyÿˆ°K·p&ž?  t3úºÈ™‹úÿÀ6�Íœ�¢³1\à ?wç´‡MÄ·ò3W/Ø )Í>¡É‡’äßBå o´=Éñ¼pÿŸùŽ'K,ÜZ,eôâ‘âKÐEU£ u3ˆ÷H>øhº_öS„�£ðçD�û*{LíÒÜq{ “·îu�™?6&œ¡§~¶%~ì»ßcš&ðåV5ëߟPKj¤›L%0¨\£j%encoding/.git/hooks/pre-commit.sampleUT gmãZÆcbux éémTaoÛ6ýýŠ«$ `ÙŽáIÖ0¼ lÀ`Úb Ä£ÅE"U’²ã¢Øoß#%» Ðo¢x¼{ïÝ»;~3-´™ú*;ÎŽiiˆ_DÓÖL•µÏäK§Û@ÁÒ†�V;ÚV"�ö$ Û¥ÿSi›F‡Àr‚ +Q×,©ØÑh­Ãp7¢­K­»†Mð¢§j_¥²]-ñ˜_ðd5ù7v–|¡C=Ø¡°ï´Y“0$ÚÖÙÖi˜ö^¬™´BŽ˜A @çƒm)T{ˆ“ÄñÉQ€b¨À$"“ïfø£t¼³4jç�I–iE‘‘ãMÞ ç™ò|å·ûå¯t7•¼™š®®i~wz‘¡¬ÉŽÄZhãÃm ɸöœÓƒÑA‹z@uCR+ECdäÆMv3Ùâ.Ã�<‹âjþN–—‹yY\rq-f—3~·(Ô•¼¼ž_]©‚yq=[dJgàú hg»$Hd„ÞØmÒvùqõð�˜FÞž<‡žüF@Ó¢ ¸Ž'Yz„7—Zßž¼íÛj”^C�ÂÚ:Iè'¯âÎcõ?Yjø¯´½]|�ìÜ$ã.éâîtãVÎzOm-‚²®!46’FÙÈ{cµüì_ËØ I wCÊÙ–L‘’ÓëØÇ­õ:X·›Ð_ ŸµµÕ!])QÆ‘NÈO™�pÂÀT0 –þž|+JØ©ÏàH„@é{×]K(§|¢ÑÉ+AFôæ–FQÑ}¡ÓÓèƒßmàCiê`)«¨@ægŽ�í�]  ´ÓR±ã1½ÕáÌÇ$‘?ø}í µ¤Ö:0еŽ.²ôÑÖhª§‹Ù™§iç]šøàÎÇäµ)“%·¯�p¼€0F…AC¤QàCÚ¤PÉ^¯ fOöÐ&Ù‚õîH~ÎóR”ò<ö*·¦Þá;Þåh Ä»]Rþ�NöÆÿž=®þ^>>Þ®"é\ÒÙ'ÊÿýòyvFßi[R^žGgÃp•PîýûÏ÷|Èî�³î†–XCM¿°Ðh÷Ú4‘`”Ÿ¢ÓKLZ)¢ê0úÝx,�Wã²µî¹olË6îDkÈ¢´;xÕÇdi öš#F÷ûQn´ßÏÑa¹pB�7Ã\>ŒcZ©ñÅ—6z7ž"<¹Ï‘ðV\>Ã%¸É2¢ÿÍáO0 o¥9J[õâÇFˆ 8UÛVmK–æ( ÷öOP­Rpv„s�Î'¿+¡ëa‚÷Ýε‘ü{ž@zhnžgÿPKj¤›L…Oø Þ)encoding/.git/hooks/applypatch-msg.sampleUT gmãZÆcbux ééU�]N1 „ßs “®*¤¯HHp.�n½›¨ùSì¥-ˆ»ã´l[^ÇãoÆ^Ü­7>­É©…ZÀ{<ØX‚ËyÔW_8Cï°ß;„>ÇèB!"‘Øî0Áæ([J8˽ƒ¡æVˆ&Zfóêôáæ—§°•TAî=;H9™/¬ˆ-Ov`¬à‰&ŸFÁ�3j.Õ[ÆK?@#ØÄÔêçrÓv×DO BÞã¶Yq+‹7‡ÍÄÁ‡¹¬˜’ÝÈSØy:Qž Šÿ”fm(}½ÞDõJ©Œž 9CÈSQç ™½êî^&‚ù4ÅVB0¦9eÙ�h}ö6ЃVŒÄ` » BÃr)�Ãþ¿Ø}??êîMÿ¨õ PKj¤›L’Äø–I &encoding/.git/hooks/pre-receive.sampleUT gmãZÆcbux ééuPËNÃ0<Ç_1¸}@[Ê‘*HˆôÒV"½!Unº‘M;ŠÝBü;vÊ£qÙÕzgÆ3Û:®”ZÉZ¬…; :ˆ²*Ò˜ lV«ÊÁ”bCØZ‚ÉQm­„©œ2Ú<+•ôM³Ê·WP& Yˆ¢ø…†“ÂÁ:Q;ì•“hàJd›¤í…„^£¦ÊÜÕ«ì%iÏ$ðã’ŸjBÙàk=h¤¤ÅÊqÒ/BˆK/©Eùù’«°3àUMýš2R;âÆTGÖ¡¯Áã‡Iºœ/ž—³y:™M—÷³Å4åÌ[Ð,RÉ‹ö²Ñ +Ž~áþçEkâˆv¢÷eKÉó¨ñ˜Lø ó¸q(h_êuýÔŒàMÍkS6g9‰Ò‰o¿5­^òÎq{~˜ã±¯Ç•Êa~°"óM%q§£p�Q·,kb¹bPKj¤›L½¾—×-encoding/.git/hooks/prepare-commit-msg.sampleUT gmãZÆcbux ééuS]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘\I¦”É�ç$9! “å”ÝÛÛ=e¯Ë¥Ô¥ë&Ù$ƒ øS¬z…Ðó®²²÷à ô{a|‡P™ÕJzP¦…:'Z,{)”–ÏÀZºMbð$}QZ¬LÏ�T�Jxè„ Â�¬#c�QÊ<%Æ€©1©‘Foh„°#hæÀ™ÁVXÌ»4ÕúÁöÆ!H&Ášºü Uò*‘G!•K3ÐFó_h 8/üàr‚îØAìbi¬Çºˆ^Ω•Ë8(ݺ,Fb%™`€�ÞòDÄW®eÅ$0lp u¥2€€q“‘Çl¤uÉpÔÞ�üh)»4ºQ²òðÁ8AóÚv#{ÔJ,+£ëÝV¨ú!¢b¦µlà<ŒÀ“À-#¼Ô4GlÓû>�¤%6fÜ™ˆO6Ò«ƒmcö¤=„�•BVÔÍ Ó³0X�)Ω3i46ýv�ƒpÖÛíqÆ!‹m½ÝfQ×ÜÈVcÍMÓpÚ*%u à…𸕕ÐDã¼TŠÅ•¡X!%B_Kq©g"l�!ËjX%H7›¾Í§ï92�dt~@'€rp6>µ­.‹¥xNf®¼-3(s CY™• (o·á•P@åSz…äôŒš38= ;’¯=҄‡>Ùû�ƒ³xMŸHl¡p¿“k%*2ó¯€ï'Ù«¤ˆ”ííÁ4îÛÑœ�Á›1‡á€NTAÚÍõû³é~àÿ!,|ºšß]|›¾þzwõáã—9¬i㨙Ž,ö‹ÃóÅAq8-_dt‹ã²Ÿ…ÑZz%À°Û)Q³Ôx½¬:zD©v~Ë“ßPKj¤›Lš ÷Àн&encoding/.git/hooks/post-update.sampleUT gmãZÆcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKj¤›LÏÀL ¨)encoding/.git/hooks/pre-applypatch.sampleUT gmãZÆcbux ééU�[N1 Eÿ³ŠK:ª@�VüVB‚=°�ÌÔÓDL&QìéÄÞñôâ÷Ú>ÇöânÝÆqÍÁ,Ìo#èèS!çpWcHÆžjìO8/ˆ ßæéœ·„.§Eh«€ö_Êp*^º€¾æ¯L—|�ˆÙïhuV½‡›#äiت7 QÆ<ºOª,^&uõBU¥<Åq§8VGÍ¥F/t£"ö˜ ~žcÉnë]¥4úVÏ“ WÌþ'T�Ò5éã\˰¥’û»Ä®ŒYaÅqpL2£ ò‹mîµ¢˜½+¾2Á¹¹SÃÙÀëvi~°FˆîÛü",–K}uÿÃæëùÑ6¯öÛlÌPKj¤›LØ�±�D#encoding/.git/hooks/pre-push.sampleUT gmãZÆcbux éé�TakÛ0ýýŠ›Ö š´)ûT–Â(ŒFceƶÊö9•%O’ë¶¿~w’�¤c°BeéîÝ{ïNš¿8)µ= Jˆ9¼³€²í ‚rîBåu!:¸G¯›G”Œ ÈÒõi¿Dèú °^\Jc°†òŠ­ŽÇ'È&¢AÉ•Âꎂ¢BðØºˆ¢Œ}8†’KlœG�ö1*m·)¥D´#X*sÕP6qÉ჎H°Î.ŸÐ»”¾3Π�9à*æø…A’Lú_eî †¹5Î7pj'½l‘$„ó”¶XÃr ×´®9ÔAn JW9Ÿ 1n‰ŒQ;‹œzÆ©7Ÿ?þ;–¢I'ŸdúQ±á}à/’IÕë]]åYæ·}‹6Y‘µâ¯^š,õÊ’­­ŒÚÙ©uT·rm›¼KL$ùžd“˜Qè»ÎhZSŒ¶ˆ8Áq2lké)Ìv„§íh›o³Mo�#W‰es1­ƒ’kú‰ç“i øhß—�§0(7p“v¬óxO ³_äýž?zLå�ÛB‹!Èm"èc ¼ÔÓâëÕ§^ Îß1×λ­§À×+!2�M±X¢÷†g…OoN7§ÿù'YhX�¬!)ýIÚÆ ;•6Ç%íŠÚ‰™nà‹]hXPmø.f¤ÈŠÙlÈj‚¯ÑÐÒι˜¡ ¼Ó÷ ‡ù!\㥗¶RÇé†S/�&~²�ƒètË.ì©ÐîX†nºZæ!§ "ÏÉŸ€–ŠüpOnµzŽÞh‘ô]ò£À³Ô¥‚òbsK� Ùv¿4T–øR÷:8úAñG¬ŸK·“!S,rvñ̬”ƒ‹—gP¼w½­ÊñP,v­;æ‡cº}EʤWÖ™3ÿÒ-i÷TüPKj¤›L!D%u!encoding/.git/hooks/update.sampleUT gmãZÆcbux éé­WQo"7~fÅÜ‚Â�`IÒ·äHuMÔê*Umî©:Efñ²n›®M¸½¶ÿ½Ÿí]Ø]8H” H1¶gæ›™ofL÷Ýd&äD§A7èÒGIü+[®2N©R�¤ã\¬ E³LÅ�´–LJe˜ás2l¡)ÉÕ’¸4<rAÃ-Ë2Î ÂPÎc.žøxÅâÇ�6¤ÄòÅz }…ÓD²%'�²‹±Êæ~!ùÆ�¹WÐÌfÀbR¡ Dœ„ÛI„=S®Ws@ #'v«d"XŒÝ +©#S›š8 º·zfJeœIÒÜhڤܤ<ßwu#²Œfœœl Ó¸êôä|¥´0*/"¢Ÿ šó„­3cÏ Ú(Ù7�šXæ<ã†C÷ $îÂû=¯±Ts‘§A0k�–¬°¶��€q– ó眡dT3êÔ�G}ŸåLÆésÝ÷·ù[†`ÎeáÐ?JΗÊð¬(=>„ EÉ-ë$˜Á_é…%¯'-¼\29§LH”52 {a€ÉùÖ—a€2ñëÂJð–pt)�‘П4þFaï—O÷wŸ~é˵E$ƒ�S”Í�‹G¾–KY쮤-ð¸# éæì²¤÷P^¨5m˜4#·ŠÕÚÖïzµBxJƒÎ˜ÕߦÞ9}€[7ôÁûƒ…wæfPÝü ê\‰jn”‘i¬ü†—Þ}÷JöÜ\k¶àWG¬î­òà:I»qL{ïmk‹Ý).Zª|§Ç ‚=¶Ÿ’®ßm‚‘n_mØFñ>Ï0.–‚ÛÎpJp{q`çÈG‰ÊI*pÝ“ Í!Xåê/»�u;æÔ¿ø»¿cè¤v?1Ó‡5ÁE„Ÿ¥åÁ¼VYá�þ¥"eʇÃ!ýæÅêüÄH™ö}€»ònf¿s}pÍâ-œ;¦Xq��±d™­Üs|¢(²ÿFèu}Ô²+á&¦�'¶@º(øÆs5 ÏŸù =íw”žbmUÔÈí�,´©·ðLóæA™:fÆÎõ±©ðÏËW•5ª :aìÎôÄNžÉpä�C”;]ÌÇqc@bS§*7UÃêýS®ºÝ�Žÿp­t­]+!½ƒ—&_óš“�ZBïÑ“ÚVGÔkXÙ¼à¼9°WãŠÏw]ógD¡oegº ›Æš¾ô�ÝØ­º�Í,¹b @ˆvºJötÕŽåP#n>=>n%-|Äê¡ØÖßÉ@ÜUÑåx|¥«` ±�×:Êm±7PÒÙùçÞžz9ÂYqŠnUý4‘kÌÇË›3ký@jáF¿éÃô±y�·¨ÐFG2ö«ƒñ‚œLU ‹mŽ—�‚mª�³«FP%ìµ;¯»p$·Õë�•^ŸÂ ý¦µ�Ø›2/áÛ[áõ/©v¼MŽŸ »gÕa�CÅ´'÷¾¶´¾Êé¡ÇûQ��JÛ©ñŠÒAŽ!QßÿÑÞ­5'÷ãÆ Ü+ô¿G©6Ò $R ù_>¶)!J»¢Ã‰»ÑlçÝ\kÒý,¤Ð)Þ®îÂyð?PK ¥›L[Ãencoding/.git/COMMIT_EDITMSGUT ¦nãZÆcbux ééModified iso-8859-1.txt PK 2[—Tencoding/.git/refs/UT ÆcbÆcbux ééPK 2[—Tencoding/.git/refs/heads/UT ÆcbÆcbux ééPK ¥›LX;°))encoding/.git/refs/heads/masterUT ¦nãZÆcbux éé40935d8766cf7a1a8462d1f914fe48ff446f1df3 PK j¤›Lencoding/.git/refs/tags/UT gmãZÆcbux ééPK¥›Ló3Iz†‘encoding/.git/indexUT ¦nãZÆcbux éésñ rf```bƨÇyÝbkg¬„ÑŒ lŒ#—¥¿2@€˜‚Ï„>ÛùAi‘÷íBslÙ,ü>ÜgàË,Î×µ°0µÔ5Ô+©() ruR’ † \‰v” bÛ|�áÒ®à¥k–̺ؠæôÝO~zíûßsú +ÓÍ.{ìºoPK ¥›LhÍ&Wencoding/iso-8859-1.txtUT ‹nãZÆcbux ééKristian Høgsberg foo PK ¶¤›L íAencoding/UT÷mãZux ééPK 2[—TíACencoding/.git/UTÆcbux ééPKj¤›L…Jð[\v¤�‹encoding/.git/configUTgmãZux ééPK 2[—TíA5encoding/.git/objects/UTÆcbux ééPK 2[—TíA…encoding/.git/objects/61/UTÆcbux ééPK ¥›LGÏÎÿ::?$�Øencoding/.git/objects/61/f0b8751112c206b3d000d2ba53a5aca49ad180UT¦nãZux ééPK 2[—TíA‹encoding/.git/objects/6a/UTÆcbux ééPK Ѥ›L—”ƒÁÄÄ?$�Þencoding/.git/objects/6a/a416ac00edba818fc1d4b16305298fb6383b46UT*nãZux ééPK 2[—TíAencoding/.git/objects/20/UTÆcbux ééPK ¥›L/0é×&&?$�nencoding/.git/objects/20/1e0c908e3d9f526659df3e556c3d06384ef0dfUT”nãZux ééPK j¤›LíA encoding/.git/objects/pack/UTgmãZux ééPK 2[—TíAbencoding/.git/objects/11/UTÆcbux ééPK Ѥ›LpŠ$l;;?$�µencoding/.git/objects/11/eaf2be8b82195a98240a2414629eae38c2468eUT*nãZux ééPK j¤›LíAiencoding/.git/objects/info/UTgmãZux ééPK 2[—TíA¾encoding/.git/objects/e8/UTÆcbux ééPK ¹¤›L…\íã""?$�encoding/.git/objects/e8/4e339ac7fcc823106efa65a6972d7a20016c85UTþmãZux ééPK ¥›LíA¬encoding/.git/objects/40/UT¦nãZux ééPK ¥›LW¡³³?$�ÿencoding/.git/objects/40/935d8766cf7a1a8462d1f914fe48ff446f1df3UT¦nãZux ééPK j¤›L+is§¤�+ encoding/.git/HEADUTgmãZux ééPK 2[—TíAŽ encoding/.git/info/UTÆcbux ééPKj¤›Lw=Í!­ð¤�Û encoding/.git/info/excludeUTgmãZux ééPK 2[—TíAÜ encoding/.git/logs/UTÆcbux ééPK¥›Lu&X§ìª¤�) encoding/.git/logs/HEADUT¦nãZux ééPK Ѥ›LíAf encoding/.git/logs/refs/UT*nãZux ééPK Ѥ›LíA¸ encoding/.git/logs/refs/heads/UT*nãZux ééPK¥›Lu&X§ìª$¤� encoding/.git/logs/refs/heads/masterUT¦nãZux ééPKj¤›L7‹?I¤�Zencoding/.git/descriptionUTgmãZux ééPK 2[—TíAìencoding/.git/hooks/UTÆcbux ééPKj¤›LéøÊ÷€%í�:encoding/.git/hooks/commit-msg.sampleUTgmãZux ééPKj¤›Lz¼Äß"%í��encoding/.git/hooks/pre-rebase.sampleUTgmãZux ééPKj¤›L%0¨\£j%í�Îencoding/.git/hooks/pre-commit.sampleUTgmãZux ééPKj¤›L…Oø Þ)í�Ðencoding/.git/hooks/applypatch-msg.sampleUTgmãZux ééPKj¤›L’Äø–I &í�Jencoding/.git/hooks/pre-receive.sampleUTgmãZux ééPKj¤›L½¾—×-í�ó encoding/.git/hooks/prepare-commit-msg.sampleUTgmãZux ééPKj¤›Lš ÷Àн&í�ñ#encoding/.git/hooks/post-update.sampleUTgmãZux ééPKj¤›LÏÀL ¨)í�Û$encoding/.git/hooks/pre-applypatch.sampleUTgmãZux ééPKj¤›LØ�±�D#í�G&encoding/.git/hooks/pre-push.sampleUTgmãZux ééPKj¤›L!D%u!í�A)encoding/.git/hooks/update.sampleUTgmãZux ééPK ¥›L[ä�.encoding/.git/COMMIT_EDITMSGUT¦nãZux ééPK 2[—TíA.encoding/.git/refs/UTÆcbux ééPK 2[—TíAÌ.encoding/.git/refs/heads/UTÆcbux ééPK ¥›LX;°))¤�/encoding/.git/refs/heads/masterUT¦nãZux ééPK j¤›LíA¡/encoding/.git/refs/tags/UTgmãZux ééPK¥›Ló3Iz†‘¤�ó/encoding/.git/indexUT¦nãZux ééPK ¥›LhÍ&W¤�Æ0encoding/iso-8859-1.txtUT‹nãZux ééPK--ì-1libgit2-pygit2-d86662e/test/data/gpgsigned.zip000066400000000000000000016254441521317216500212360ustar00rootroot00000000000000PK C¥T gpgsigned/UT äÁsb�Âsbux èèPK C¥T+is§gpgsigned/HEADUT äÁsbäÁsbux èèref: refs/heads/master PK C¥Tgpgsigned/logs/UT äÁsb�Âsbux èèPK C¥Tgpgsigned/logs/refs/UT äÁsb�Âsbux èèPK C¥Tgpgsigned/logs/refs/heads/UT äÁsb�Âsbux èèPKC¥Tq8Õ‚rœ+gpgsigned/logs/refs/heads/testrepo-worktreeUT äÁsbäÁsbux èè�ÁQ Â0 Ðo=Eþ‰mZQTðSðY—°26G×ûã|ñ?€Ì&�d/¬Â>NÁ¥ìŽ–bTO˜8;5ƒ·´ZòŸ¦e¤ö ÎËz]Æu_¦ ˆ%¢èa‡qÓU™óp‚{UiÚƒÕï¯çí±ýPKC¥T÷âZ�Ûgpgsigned/configUT äÁsbäÁsbux èèEŒÁ‚0 †Ïì)Ž˜Xã‘Ä'!t°dXÒv&¾½EE{hóm¿n$ÆÞùo1n$I‰Ÿ‘x ú@–Dwó—ßMLWšÐ rÁï,†,˜i93ƲMAQާŽÍ¡èkcZ÷®*œm7'm¬/e8�´BNƒ¥ë1]QÇÅ.OæX0LMû¥À®„ƽPK C¥Tgpgsigned/refs/UT äÁsb�Âsbux èèPK C¥Tgpgsigned/refs/tags/UT äÁsb�Âsbux èèPK C¥T. b)))!gpgsigned/refs/tags/point_to_blobUT äÁsbäÁsbux èè1385f264afb75a56a5bec74243be9b367ba4ca08 PK C¥Tzÿ �))gpgsigned/refs/tags/testUT äÁsbäÁsbux èèb25fa35b38051e4ae45d4222e795f9df2e43f1d1 PK C¥Tgpgsigned/refs/tags/foo/UT äÁsb�Âsbux èèPK C¥Tzÿ �))gpgsigned/refs/tags/foo/barUT äÁsbäÁsbux èèb25fa35b38051e4ae45d4222e795f9df2e43f1d1 PK C¥Tgpgsigned/refs/tags/foo/foo/UT äÁsb�Âsbux èèPK C¥Tzÿ �))gpgsigned/refs/tags/foo/foo/barUT äÁsbäÁsbux èèb25fa35b38051e4ae45d4222e795f9df2e43f1d1 PK C¥T€Tn))gpgsigned/refs/tags/e90810bUT äÁsbäÁsbux èè7b4384978d2493e851f9cca7858815fac9b10980 PK C¥Tgpgsigned/refs/heads/UT äÁsb�Âsbux èèPKC¥T¤wžC() gpgsigned/refs/heads/packed-testUT äÁsbäÁsbux èèÁA Á?j2m¡z8 þ%°[;d-�Ñ¥Ÿ�Ç~äi±á†jŽPK C¥T[Ï–é))gpgsigned/refs/heads/masterUT äÁsbäÁsbux èè099fabac3a9ea935598528c27f866e34089c2eff PK C¥TòÅuÌ))gpgsigned/refs/heads/subtreesUT äÁsbäÁsbux èè763d71aadf09a7951596c9746c024e7eece7c7af PK C¥TyH¥í))gpgsigned/refs/heads/br2UT äÁsbäÁsbux èèa4a7dce85cf63874e984719f4fdd239f5145052f PKC¥Tçˆâr()gpgsigned/refs/heads/identUT äÁsbäÁsbux èèÁG À¿i�MYý#x§ÛRÖ�/Ñ’½8�Ö‚s \ï°ìùPK C¥TÈŸ1#))#gpgsigned/refs/heads/long-file-nameUT äÁsbäÁsbux èè6b377958d8c6a4906e8573b53672a1a23a4e8ce6 PK C¥T[Ï–é))&gpgsigned/refs/heads/testrepo-worktreeUT äÁsbäÁsbux èè099fabac3a9ea935598528c27f866e34089c2eff PKC¥T‰z;()gpgsigned/refs/heads/dirUT äÁsbäÁsbux èèÁÁ Á¿Õœ²&XÎè¿g6ˆ(Óݶ¢'ì<Ï“5šõPK C¥T]Î:ã))gpgsigned/refs/heads/testUT äÁsbäÁsbux èèe90810b8df3e80c413d903f631643c716887138d PKC¥TŒªx()gpgsigned/refs/heads/executableUT äÁsbäÁsbux èèÁA ±¿mÄC$ ô�àÖ^I4‚‰33ÐX¯=¨VŠXì[ãPK C¥TXÍøî))#gpgsigned/refs/heads/merge-conflictUT äÁsbäÁsbux èèa38d028f71eaa590febb7d716b1ca32350cf70da PK C¥T+is§gpgsigned/refs/symrefUT äÁsbäÁsbux èèref: refs/heads/master PKC¥TÕ‘Ï–Úgpgsigned/packed-refsUT äÁsbäÁsbux èèeŒA à E÷9…Ðuˆ:3F{›“ÒBCzýºè¢›ÿáÁ{sp¾Ï/­Í¼o}¿šCõ¡Å´ç«�›ÐIŽ9$»’H ™r¨–BȃgD£”dÎIJ+—¶œI- ‰õÄU¬`NÆDFŸb©ÿÕ¹kë“xª $-9EV¤‚Þ{]ÕTªW„êŠûú�·ŸÎÛôPKC¥Tä \Í»ø'gpgsigned/indexUT äÁsbäÁsbux è謚y\LûÿÇO¢•–©,·T–®åšfΜuÜ.©ÜD*\²×Ù¦¦¦i›”pe¹v•-d颲»\„Š„¯\RñYóÊM!ÝÔïL#ÍœïÌôýã7æ£z¾ÞŸ÷ç½�Ïäå3Ù€nìßhßÑ‹!v:V å÷ƒ²6À’L0øÀ~˺oø²\˜¿Â·¥¼ÁbjjS¦÷SûÍ?´Æ®ãÆŽñ;Yíç9œ�š`RÏ‘û¼=x›�?u䪅‘ýùôßsÀÌ5Dª�†È#càëKo“&ÏlIàìGÓpƒGÞ¶Ib^+3˜o×0÷ô÷›6Öï¿)úy›5y>ã.Gú6T%æíJ¿õs]à«_·dôŒ=ýføøyëælÑäØ\;<í·n²Kgzö}v]�5ßëïÒu€ÉD"œ‘Heì.gÝIìöÞ¡¾cU² —¤³œ:5u—Œ|hj‹…ló_P3rC}³÷†!€1! ] Ýö¤iÚÓÍx¢Ë±ŠÏÇæMq뚇òÉ×Õ!€9%u¥#æ‡0ò¯~j·çÓh�Žõ«];8çh8öܲ è7ãU�v柑å%µ,8X’1„œ RnQ¿}[9ç¸Å$ìäV�˜×rƒNœàÈß]>óL`Ɇ…24øDœ"42&V7oǾše-K[’Þx<šç8öˆâ€å‚ÄŒ2 —LJ²8‘kå*•wî÷âJÇúu¿;9¼žûíVVÖìw�Èxî¾Ó<ÚÀ󀱜‰ÿºOåÏö+qèX¿rvq8}·ŠÖU�7¿)nŒJ$'F?îÕ´ 0Ša:‚ѧÿð]gl5üøÖz� ¯ÄR·Ëëýº °‹�¡¤,’ çdž @ùæJé‹Î>»]žº>ìuu¼gþ„afäõm¸Mÿ‹µsCõq9ûî>ôdÍï‡ æO²½¾²~nôÄ^ô¬wÀ¼�OD}¥éäíÖä)JeÅ Ãb–/™nh³Æ)ov�m,•<ŠâS‘Q„¢�é;:é- ¶ªx‡’4yà¬ç/ˆ>OÖþXv~õVƒÞ¼ëo†Ýz¶ó"#"Ø0¤:ìK⩯*Þ#MžÉTô S:œt"yÓ£Ó/]Z[‹¶’÷5x¡z÷»G“gx´m嵘¢å²@—×ÑöÎ=ZsÏܽÐÉ‹”wÁÛ«É3Ýêc·9±ÄÓ‰oïÿÊùN±—l/¾D X+y4#S|"*J6ŸÝ´Þ>Ž}"ŸµÅ•¯�. º´°×rCËÆ~=’Çòÿ›ª‹—ɉÿ/:Œ>3Ét}béùe ò%wˆÀ´�'�ÑÍÉâÄ�ù;£“‹^¹Ô�­Š(üå!o®ÏO#þRù�‰‰aKJç¹jåekò fo6ýéáz›Åçõ 'ž€ÉÇë÷! Þ·sÈ⫯í¼à›ï5y½lh˼/ã¬3<úÔ8ІVÙ�Ϥ€…’§,¢Ê”SY¨ƒ×ÈñÛ÷ÖKJÇÙ‹+oÆH,þ(vL©7çðBõìw¿&�÷®Á ä2)3fÆmúV|]úÓ'9÷€^¼È¨Xýõå€&Ïb?sPÿ–[‰\bV|sÕQ`ÿ�ùû4yuei‚úªâqÐäõ<¿uxzxøÈ~k�eÄOæõ¬”<¶Ð«åšûsò¶ÎÅèA•ÜrçáÈ%_*\*L>/äßÒä©rMïç<˜äÝ£߃�¯OXêÿËžÉ{tèÕN^gÌh?ß3Mžñí´©m¼÷éýW÷í¼Z*a_X2ùŠªî)yR9Í$|­{Zí;ªÉ³›=ýœˆž½9ɪÐl÷C×ÅN“ZzOªÎCÉ‹¤Iýuþ˜&ÏrD«›âÌt^QÚ~oZ´gþéMj<)­Ÿ÷‡&ϼwâA¾×ÑÉ'm«�æï2Òjß-/ªêŠ’ÃÌ‹'dáºëÁqN=¨lgûþ�ˆÛ½"èEçê°ìGîOôîà)B•�˜§�Êb;ò$i­úªâœ¬Éí1>Ý'67¼ÑèP˸s+â=/_ø~¢Uy×Î�a½y÷'§žÚ:½î±ÑpåéÞpü¦Kn"Éç”û�¼Dv�ÑË;É‰ë²ø¹|yi¯ª¬ü‡£×w~FÂlLgfJ^(ÚQuòNqâÚ÷dôÅiyRZø¶²ämù³r³ÿûAj¼Žv®ç¥äu¾¶;Ùó‚GõOã`ïî�‘æ×œX©šT1Ýa ¯'襾¶Û²RÆ9g[G`ŸgXi÷ÉŸ¥ľµþ͇r_®Vçu1oœæì÷9¸ªíáÉ}å®– ï{Ž ­Þ8ØS®êGíÓ‹N§˜U%ÍiÒmt˶5yAÉÑ/mà&€×ΑÊCâÕG}ö�áÄËSò°Ùç]æ¦ZCmïã‚ Ò\wتò$"vžÚD¤ƒwV“·8ŒŒ³©^óôÚÛ9®˜c¶Âgöòﮫö«¬ :ç‚s¿]Èœ•ôãÚ«Æ…­ÃË>Ì@¶(Ù�ëäè´'—Ó7šN>ÿ.*nÞËúM`ß4ãk»›ö|å°5E§=ùœ<½ð:¬ß÷w~žK ã—Ô4^“WŒy«Ê«¨(Jm`ÖÁ;ÏÙŸsÔ–h«/™Ÿ‹åŠÀà~ʲ¹Y¼‘ÃÓ—§4yýmúX¿ùXmÿò¶§Aø\›•’“ñ¿šÔ«Î±ƒÇ®®StõÇþœçÐ Þ÷æv�ûàë4©Ùd@òÈè<·w‰ª~ÁÖÎH2Œ¡T£-k×3@mUñö‡süwà^Ä*õV©Èfý¶ƒöŽgž™ÉûÀå}�»µÍÉ&œùbý2ñÙ‹Û“Ìi‡¤Ènâ~êtœª_tÔöoó…6û"8ùU9ÄâìÕ‚ü×Oß®tnˆœ9ÂÜ‚©Òäéí?—9udØÜ¥Î�²½ÕÀ¹Š9ëŒMÈ´�¥*_5zEWõó §gn9·È‘ߘ¿°1,}é™)R—×ÂU«ZÏÛ‘3Oöñ>>D4ô±Iv ±8e䟦Ãù¯ªêr{R³Sk_û…ãÏb +�£èJŰ]Ýççz½YXáõ\�×U�ºÆ©Sï3í�™¯9ì³áþ�‚��s�¸Eâõ»Uù'—&´S}ù÷gš½r·²voyí­‹iëgW�¤Ê—x©µ?ÍPºy79õF~©6zú`¯£Ñ­H ,xÎŒ”Kö8`ÓÉÓœÊurK8ñx OÞI~·«v@ÒÍ`Ga–ûÍó ùu;;F¯ŒsÏa$5|¾eדE¶Á;÷o˱/úëU!`¥`b±õÛ>¼[œû!o—ckF�÷e˜Œæ£ÃÀ 'm9Û ¯óVM'ï6Ç�»kÚÆ õþ>ÎvX@á“Ô?ò.Œ0zªx~c&ª.ûtóþÍÉÔ`ÞZ çl©Ó\øScï§åF> [¯6*žB(‚|vÖ—Gvu.w8~¬ªÏÚ�.8ú¯Ë»–Õ„õ¦‰ˆ½€m'WÄ�a$”\ÑÅsl9ç¹iݲíWk2[jj»�óH/ü»z?á0`×É…ø±ŠµÒÁ½«ÉµJq ™wýeQŬÚÝVÃÇ÷õÈpóÍQ³W$ä³Ïtiï=NßÙ$ßõÉzצÜM˜ó[èZŸLËi¼.Èú÷šc+4¹»Fû �{ä­Ô”‹U/—ÞX»¥°Vçê�/îsîUŸŒŽu 1í´à�Y%£Å«úû’G:ý ²çÅ6IÅü(F¿_hrÝ–Ù8=¿¼ ÿû¨ú ;È.5¼vµ:RrÕ\ ƒû�ÓßD=?Hó1aa²|ÉìèRï«�Ÿ§ý:¸"eÜ&HÙ/d‘‘±Œn?<ÒäÒ“åçTôõ}²b–½Ñê‘sªGÓz O'W·ÝAÕiïcw`|nÅ´�ÂHžg‰¥ Ç;ÏqËSçBÿ÷‰&wü¯§¼zš¿>ûÖ47gÖ�n©ß¹ïô9z»3!Öñ1R£–¿ZçSN|mt ó5ý×§,Äbnÿ„é é‘ÿ,JêäÂ,7Šˆ‰Uç.Q¨¯*îaÎ\gTþœWži]ê·óhRÛè“vW†ÿ�¼§3`Ö¿4£ :GýÞƒ•uÞ£ƒÿºhóÎöé›­s$§Ÿd¾º½_¦ ‡®êØsN³)y8|àË ¾?æÕ¥æÕ5}qÞR0sðSQæYûð+!ŽHD(LHH !”@H†Â` ‘Œ˜„PŒ$`Šâúô_rô¡M{"m)£ÖìJ÷9'^>e^ Ûü&H‡>.`Ó Âb� �! AÂBEp"Dˆ�„aBhLŸ~5G?>×üáMðÔÝüÚÖŒ�¶óýü§/9§GŸ–HœÆ " ˆ†qHÄ`°P‚ŠE8ÎÊãEëÓÅÑç-Ì,© p7ûTîÒæl¶àU�ùU'íú0" Å8ëk%1ˆb8NSbФH ¥HJ(Œ$…ˆÞý×rô_=%§¤eÅ>ñƉszÖ..´ÇçË#ú„€-$£¬Š„Ò$ÆH$%‘0$DaB’ IZ$„}úuýÝ…ïFüòOâ«8ϦV™¬þs Üøù²v}„dÿE~ìQSbbßY�°H Á„£)�PÖ§_Ï=ׯæß›\›�7GdÇv¯ž_ü›ä�v} °Î¥iaöÌ)Ši ÒB‚a@Æ(±¢HTHèÓoàèOì—ÿ´1 ×5†yo¹ýÇ·´ëã Æq1Á<%„@ 2´'”iÀn�`1 ë�?Î=7ðëœëLKH‹Ö¼¹;Ää?9›½ ¶èЇbTˆ�HÂ(…#0 � ްº¦,(�³ÿ€1±>ýÜøÛŸÆóœ™ub{dÓá¨êÕ×VøM= h×K4á .ÁÄ(¦ŒšbY¿Clðã(Œ3 [–ôéâè_kš¾"-ÿ¬£Ûû”+‹]ÐÒdÈ!Q»> Œ•ÄJ‚B83bÆ@±–дKF„ˆH¢Oÿ3Gß¡ì^5úÍ´¿±* GS�Ë:âúÝ¥:ô1ˆà(Ò'Øs`³^Â~)b-‚Â"6ARïùÿÃÑÿ®ïþ�Ãþ^7;}\ÕIó¿ï½�^FnÒ¡� DŠØbGИ—°õŽÆQ�bD$&á 3ÍP¨>ý/ýK&}î(ÜóeUA¥?òìºuç_âäÚõIF!Ê@‡Ø@H CDRˆ#Ø<„%˜¤‹üoãèï‰û0¦ÁôÞ²ÛÛö+ndÈ6¤ÍðÑ®OÁ  )L„¢„ˆd”>gOAÓÛ@ƒl.ˆõú?Û€£oÍ´Ú3®ÕzÅ‘Ym_®¤œhvdеë3¨@L3"1IŠh�FÙT'qR$&ŒíÆ4N‰§H êÓ7äÖÃxSé¡Þ�½Æl,‚ÜSì… œúi×— ¡ AœDQ˜ak båÙ& F1…Ób†mÏ8­W¿÷swëaÛî¸D/87°DQóñmXñh¬•}B‹I!†‰16Û@‚Fpå8€%"C$l>²#ȈEúô�¹ûç�‚£î÷:_Üpº†G›b‹¯êЧB1Ûb$˜P,DA a‡ A”bKöœµÇP}ý/›3‡v¯Ø|¹¨Öˆ}wæ9ø¥Ž9¹ü~~ÜÑÀxmúQÞþƧ1ŠMA‰˜mˆK„0ˆÃlka˜íFb‚À6Aq•ÒíwRzìá|ŽeP«(jõÉ, ø®„B|ŒwZTþŸìQþïoöh�{=9÷y SÐæ–ÒïŸå?Ñnˆ‹¿Ë>º¾óùRi�Úe¤jKÔׯû”s>¿Â>¦˜Ûo’‰çÏã-0qï)^\X©v/€°sº‚ eêó¿Vn$ç<‰äÝŽþ[žû•2Û`H_h žÕ9ÿ#ìœÞÎýö‹ :ÎÅ‚sϲïç½;Ü+§® *î�•¾¥L×Rùíù�} edQLWŸ›g[rü{|„i_'|ÐÏðóƒ "·ÅŠ-ë ­\½¿Ç�mÅyÞžéPõù‡†¢f™ENåV›iÇnù÷Lí\å'e”Nž5çþÔ!áóFȹâj›Ô ¨y]w«�3ÒáíÿÅÓùùo6�s?™á™çéîºï˹ºðñ†Î»œòó­Ú˜o÷ J^!•ë|®Î¶áÔ¤uWÚœ‹îWWþßÞ\óþuT¥äFŸdd÷ë—00(8-­�5gû9e…vʃMÛc7¦DZPK C¥Tgpgsigned/objects/UT äÁsb�Âsbux èèPK C¥Tgpgsigned/objects/94/UT äÁsb�Âsbux èèPK C¥TV{ž³ww;gpgsigned/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162UT äÁsbäÁsbux èèx+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó�1XPK C¥Tgpgsigned/objects/09/UT äÁsb�Âsbux èèPK C¥T>”%~¡¡;gpgsigned/objects/09/9fabac3a9ea935598528c27f866e34089c2effUT äÁsbäÁsbux èèx�ÎQ P¿9Å^@³ÂB!1F½‚'€î¢�ÒJ?¼½Õ#ø7™ÉK¦ŸJhM›VE€,³·.3û�¼§Þ¦ˆ‚ÔuVsHè-;õŠUÆÑÙ,œMˆ’…P³Iɉ&ÎÄ”×�ìסŠK»O.2µո$8¤ùN·¡Ý—´ë§r„½!½l±CTk»lòUgfˆ0¿ËsêÓG(PK C¥Tgpgsigned/objects/76/UT äÁsb�Âsbux èèPK C¥T~�V�¯¯;gpgsigned/objects/76/3d71aadf09a7951596c9746c024e7eece7c7afUT äÁsbäÁsbux èèx�ŽAj!³ö?0¨£ßÂ09Êo}�HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz�[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R,PK C¥Tgpgsigned/objects/d5/UT äÁsb�Âsbux èèPK C¥T]EH7““;gpgsigned/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83UT äÁsbäÁsbux èèx+)JMU041`040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;fb ‰ ~ÍïÞ½>ø[ÜTLæUÓ÷Óñ«ë’¡º“Šó’3âÓ2sRõJ*J\w6ï3ÛnÃÞqgΞí9»þTo‡*ÍK-+Y.Úvðþ‹å29ûs„æ;ø„9ý7Ü Ìº<ÞPK C¥Tgpgsigned/objects/9f/UT äÁsb�Âsbux èèPK C¥TŒV7†  ;gpgsigned/objects/9f/d738e8f7967c078dceed8190330fc8648ee56aUT äÁsbäÁsbux èèx�Ž[ Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ�—çpS[–YÀ˜x�^ Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞ�pÄ€¨vº?”ò�«jÛºLð«¨Ø?HåPK C¥Tgpgsigned/objects/1f/UT äÁsb�Âsbux èèPK C¥T–yp;gpgsigned/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bUT äÁsbäÁsbux èèxKÊÉOR0c0Ô+©(á¶¿PK C¥Tgpgsigned/objects/ad/UT äÁsb�Âsbux èèPK C¥T(Yè]ww;gpgsigned/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3UT äÁsbäÁsbux èèx+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ0[\òëy¶© v¯ÞWc^§´ª4/µ¬d¹hÛÁû/–ËäìÏšïàæôßp/o/ PK C¥Tgpgsigned/objects/ae/UT äÁsb�Âsbux èèPK C¥TB=;””;gpgsigned/objects/ae/90f12eea699729ed24555e40b9fd669da12a12UT äÁsbäÁsbux èèx+)JMU041d040031Qrutñuený”²~{TØÒ}Ç�œ÷Í6«^rŠÃÄ“>:żW”9гÊðùV×ÿ›ŒÖl˜qª=©(1/9#>-3'U¯¤¢„Áugó>³í6ìwæìÙž³»áOõ†p¨Ò¼Ôr°’_žʧ+O\Á\ðiy|Éãt¹I÷‹=&PK C¥Tgpgsigned/objects/5b/UT äÁsb�Âsbux èèPK C¥T4ážž;gpgsigned/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644UT äÁsbäÁsbux èèx�ŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢> öF-PK C¥Tgpgsigned/objects/32/UT äÁsb�Âsbux èèPK C¥T<222;gpgsigned/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54UT äÁsbäÁsbux èèx+)JMU06f040031Q0Ô+©(a�OÿãܶébáƒKÒ�¸?TK“Ÿ e´PK C¥Tgpgsigned/objects/a3/UT äÁsb�Âsbux èèPK C¥T�:T››;gpgsigned/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70daUT äÁsbäÁsbux èèx•ÎQ Â0 P¿wŠþ Ò4i»�ˆGµ×Zív�øGBK!Þ�Çäü õr‡OµPK C¥Tgpgsigned/objects/e6/UT äÁsb�Âsbux èèPK C¥T´ ¤˜;gpgsigned/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391UT äÁsbäÁsbux èèxKÊÉOR0` °ðPK C¥Tgpgsigned/objects/a8/UT äÁsb�Âsbux èèPK C¥T´ä/ò;gpgsigned/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6UT äÁsbäÁsbux èèxKÊÉOR04`ÈH­T(ÉH-Jå6©PK C¥Tgpgsigned/objects/84/UT äÁsb�Âsbux èèPK C¥Tš<-Ï~~;gpgsigned/objects/84/96071c1b46c854b31185ea97743be6a8774479UT äÁsbäÁsbux èèx��Û 1ýNÛ€²Én‚ˆ` V°‰ñ.`.p·öïa þ ÔÑ{S°Ñt­l²Hù,žÅÖì_’9ø$$Îcf&òÏhä£óXáQ†*Üg)c�ËV~p›º´÷©Œ~ë"Q@JŽÍn÷£ÖZ£uÓ¶Læ   6ÌPK C¥Tgpgsigned/objects/7b/UT äÁsb�Âsbux èèPK C¥T~ðeÆ‘‘;gpgsigned/objects/7b/4384978d2493e851f9cca7858815fac9b10980UT äÁsbäÁsbux èèx-ŒA‚0=÷{7!] í’ã¼ï¥,X¥@`5á÷ÖÄds™?Öö0·OÜhBÝR×&*4]£Mo Úʇ–È¡¡Nɾ0„9¥(Jòãþ|àî1ð$põ«D8‰Ÿæ×û2$Ç"GgÀ’°vdjG]j­Ôí7ÈxøðºÃÓ22ä]¡¾s×0ÕPK C¥T3Ó´1»»;gpgsigned/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785aUT äÁsbäÁsbux èèx+)JMU016c040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3†F@ �“™—_’Ÿ—ZVz ¨ÿÚ™=\ç¸Jœô§÷¨IÔT˜’å¢mï¿X.“³?Gh¾ƒO˜ÓýP%Oû§�L»ìÙeU?gj{íºúÈÿ·ô¢k§ôOû-PK C¥Tgpgsigned/objects/e7/UT äÁsb�Âsbux èèPK C¥T®-áU;gpgsigned/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0UT äÁsbäÁsbux èèxKÊÉOR0c0Ò+©(á¼ÀPK C¥Tgpgsigned/objects/62/UT äÁsb�Âsbux èèPK C¥Td‚H”22;gpgsigned/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dccUT äÁsbäÁsbux èèx+)JMU06f040031QHÒ+©(aH³ºÍ)bž>ÿú…ÕÄ÷¿Üj7{öPK C¥Tgpgsigned/objects/9a/UT äÁsb�Âsbux èèPK C¥Tƒ ²)22;gpgsigned/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4UT äÁsbäÁsbux èèx+)JMU06f040031Q0Ö+©(aPçî[žvõÔ «ŸŠÍåL—šä ¡‚PK C¥Tgpgsigned/objects/4e/UT äÁsb�Âsbux èèPK C¥T÷¶�ð22;gpgsigned/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63UT äÁsbäÁsbux èèx+)JMU06f040075UHÒ+©(aH³ºÍ)bž>ÿú…ÕÄ÷¿Üj7ÒùPK C¥T,\ °88;gpgsigned/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10UT äÁsbäÁsbux èèxKÊÉOR01b(ÉÈ,VHËÌIUÒɉyé©) ™y ¹‰Å%©E ‰y) IE‰yÉ\­PK C¥Tgpgsigned/objects/66/UT äÁsb�Âsbux èèPK C¥T ³Ø;gpgsigned/objects/66/3adb09143767984f7be83a91effa47e128c735UT äÁsbäÁsbux èèxKÊÉOR0aðÈTä/ÐPK C¥Tgpgsigned/objects/c3/UT äÁsb�Âsbux èèPK C¥T,b×CÀÀ;gpgsigned/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbeUT äÁsbäÁsbux èèx+)JMU026d040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥™)©y%†`U�3+ùnYûåø÷s½N0¹OïFVe„[•‘(ädæeÇ—äÇ祖ƒ•ê¿vfF—Ã9î‡Ò'ýé=j’5¦d¹hÛÁû/–ËäìÏšïàæôßp/ßbŒPK C¥Tgpgsigned/objects/99/UT äÁsb�Âsbux èèPK C¥T–d%ßßß;gpgsigned/objects/99/1f1b12603e1d78411c1b4042719f964efa7adfUT äÁsbäÁsbux èèxÍ[msÛF’¾¯Ô¯˜$U)Q²äìnR±­-Ù¦lÝÙ’‹¢×q "A6 °Ð¶ÎÉýö}žžÌ lçîÃÕf-b0ÓÓïÝÓÓ¸šçWêè§»‡‡ÿqgwKíªÑ,-Õ4�'Š‹$Qe>­>ÆErOÝä+5Ž3U$“´¬ŠôjUaZ¥âlr'/Ô"Ÿ¤ÓÁØ*›$…ªf‰ª’bQª|*OÎ_ª'I–ñ\½X]ÍÓ±z–Ž“¬LúêCR”iž©»}ˆKµä„r–LÔÕ�¬>%>—ušc“¸Â’,àš³LÅ“IÊ!Uå²d‰ÝÓ’pK•bÝ‚�pJ¼ªfyQªëôC"d¯²yºH+ Räó4{/+Æùb ®MÁ"4;vf˜ŽYWi&8—êcZÍTŽí µ,òë"^”šòlBÐ��L™4ç+òþò 4Æ$›Ûc“4»†ðò… ¶ÂZ•¥º#ò¡U„à,Õ$Wñr9¿!û4¶x½LÆUyOM!öäS¼XÎ!?PrƒÍA=aˆ6¤c¡ÕJŸzÕ§ºÔÄQTgI¦²¼n‚Å)XE±!Cɧd¼ªâ«yrÐÃ+¾ 4µf— Ù³| å›ÅÕñc:Ÿ««D�ÓÕ¼¯ º„ñêlôôâåH�œ¿V¯N†Ã“óÑë{"ò6ù¼¨)L�Ì ˆ³êôpõóÁðÑS,9yxöìlôZ�§g£óÁå¥:½ªõâd8:{ôòÙÉP½x9|qq9ï/¡Ã 5$�v)gy‘¨IRÅé¼´*þ6XÎòÕ|¢f1”³HÆ ”t"ÌZ3 |dîÏs(ˆè_E{7êwO•7q�.^¼>;”ϦO_},`ÐM ›ì±¯þ~„—qöö¡.+˜-VŸ¦S(üé<Ï Ñó‡yYåY_=?Q‡w�Ž÷�~<<ê«——'$öÎÖÖi6ž¯&‰º_V“I2=˜{ƒßCϰýÙ÷õÄïS¸�OáÐ,.gáÈuZÝÉ'WÜúpÓ,QIV7Ñ’î#»îV7K(ê<Ézªû³ÚWÝn>�–I•OÍ«e\ÍzjOu9§§¶Õá§Ÿz= *ª(„)ÐÂ!˜íj =“½e�ÞÔÁ¡œ¾ —(ŒÙ ýïD£¯ »•&Ðö3éúŸŸÚ©* Ì#Iž¿…ž/h£ž-]ÄsÃÂP5mÛ‹ z[[eO4†CÉÊJ�gq¡ÎÎ~�žN†ÑåÙ“7oÕõyçñN_íœñŸ!ÿy´óç½�‹¿Ž¢Ñp0xtòèéÀƒ1rËü5 Œ1�*ƒÃéÅÅHpømžœ�¢‹³ÇÑðäÕåok{Ëê²ìèîú«¬L¯3íOÍNÿ /Ï.Σó—φØL/-Ó‰fIÌÀýy«³‚þñ.�$”¸Z!ðMŒó‡´–�•«{[šbpò©B˜§»l€ƒûæoo8vfDrPž¥ 9ÒhWé’ÿÒÚð ;‚&É¿4ËýG®€ÒU:ñ__‡�ô”OÙ7O' ÿ7+ŽþþMçñu‰!šúøæèí=un¸¸‚C-â↮æÎ^Ïcœ¦V|õçUÿω%³kê#‘þ$!_¾ž `Ñ<#Sœ$ãy\˜´ <2Æl¬¡€Êê ¨^]'}µ+ºÑ÷�~÷j5�&EJ-¨E€=gk\úæ¦vâû¡VšuXÆ]÷�5†âH�E2ž¬–þÁþ-Ë uôoÀôR® Éíü ÖÀDÍ’ñ{ îì†Ü ç4‹Üø=R> õ†F¨?¦9¬¹RïÐ@¥Gæ’͆Â<‹ÖuDnæËKxé­-1Çèh=; MzñÑd0ôEò¦€r³]-€•É$›hÓ„º „´×’‘HLØ­l�Á²\-Ó¹�ri_½£T¼W;�_»ôåH§/@¨ê 0‹%‡ÑNz1^Þt·e7\üŒ¦™nÖjÒé¼£;EeJ2•ÎÇtUuß©cx`µ½M½/–].©‘óKD‡)Ó;“2‰oƒ£ª Jâ2µ £·Mßal=»öQßi;bŽ_ÜÁ0㨯uÒ¥>€ ó¶š±.qkânÜžèÊ|UŒƒp±3t �âFÖ=Lˆ¤TA Gqœ/â:Áö˜x£¥O6ªcÌ*}ôtÎYVÙ ³ä£ü„èž�á(‘Ž}º~RݳYŒ¿ ª¶GŸÂ>à2áDºÞÖìÌ©ÉÁÝÝ(b9؃Xà Î/à�=£QÞ>ëz<[FmÞ”¨·§Æ¤IëE@òeIªƒ Ùà ï©\ZÊx¥Ó=Ï"ëµOV­¼=ÍèÞI7”kh°O»nµÏé6B—K‚ÚÂXT$ \¹D˼l g¾J(L‘«±Ö ¶kµÛªÙ8_Þ8=ÛÈ ³�ûûà�Dm² þÁéAA7˜~ K N{›–ÐÒ bLðhc"n¾” 04 ã˜EÌó� �YzÍ€Õ¤ŠøÒľ%«4NœÀï d}¸á† T˜À=¾Ú3¯Ž�é÷¨ô¨g/–xm}>0 ¬Š>¼#² ÆE&¯ì:0‹ˆKfÀŠBW3©×1ËÆ¢sš ›ÒûÛ@c« �ÖS5°“õvç?•ÉJ:šÇÜ nÓØ¦Ÿnœ_ŒN/^ž?–J*på†'© 6‚¿w~¯ý¾œÙwå ßV’ÐòÓûõ‚Qw!ßT,8sÿxŒ@<)’Ì�÷ 3Ü5Ò¥„pj¢¦âÝô+&ŸtÖÞ�… ãž yo8­Zky@((çéÓ168꺞F~Õ¬ðát7»æI„YÂQ1{wÓ²KBx(P35…Rk ]Yt{ÉNª’=\³m®üÁݲXG`›rl¾“âìk `6úñÑc¨Æa°Çáì&ÒËÏŠ®~ÆMÏï©ùí×!î×9®÷åD+UŸlµ$d$ˆ¦ûl—`‹êPÂâ"U;�¶<†)r�-µ‚f»·g0D:XcÄ}ãÖ縭õ7?¹|tvÆÊ;ïÛT¶Z\áZ·Ë&ºÂç!ùÁݱ ɪ¢ÆÏ¾wTåsÇ›®VªÝž+bZ| ‹Ê$;øNßJ¯SÎŽÖJìm8Që3Öã::ñ‰¬ñ¶KLªö—Ñÿ=ûKøý£î_¡UáòéÉþ‘Üÿ×üeËDZ픸¹ÔþÅÕq� ÷ÂkCx篑;}./¢Åû"þØÝÖÄ@ž_.J]þ …×Ó@)6»oã¾Rù‹GÙ_¶zK„á5"‰Žtn7Æ´”6a”Ê.€5e[Úo…Ö’BpžÚÛó×ù^eƒ}|ƒ³×'©p¸v`Þr©âL¡Úhè§„ødTŽÚ š‡-Éek×…ýõ�‰¢ÜÛ~è—àš¼PE©›¥ºè\ÇÊˑג†úÑF£¡s`\ŽÊ™öI4iNňÉ�žˆdÀ[A.™Ü‹Ô|“åÜRdóR�/Ä×ܸNaR‹På6¦æÞö½}}õ·OÍkÛ ~Û bšÛ°ssÌ8…^ë’� ðZ a“O]F úb­uG-!¹$hìê½ãεê× U É”N7lûÜÿE"ȶnáóÄ«3-ÎzÕ¸¼ª¼BÏ&ènÕˆÄn 5®K÷ÀtC3(il,ó,k•¡4Ã×b/5¢�B WmlgŒ¡jxI*ü�ö~³çÓÓI—à¸ó%›ùሎÎï†xsø–w>;';LW›ïîãÝo;Ú/®ä·ãxŒÞmúq߈µք®bIö’D�û‹Ø’õŸûmltwÆÎ4@¸>µHýOQ@ ß›ðË€NÀ¸áÊòlݨyÁ/jæÈõbjÓ Õò%+T;¦‰_7y»OPŠÂ—#ºöȄڠäÐdk™µÝ.¡h„îú]¡Ø\'¶ÕÏ6žtÖO»¶9!ðŒÚ5ƒ9&µÂ™Ý^åj¡/r¼š³N„]í`ò‰f°ÍÒõ~£¥ÿ}„b%¾]˜t�íÂ*ÑðYý®½˜£"yzÛãËœ¦ëâ[s<Ú{ �{õ¸¶º}ÿåï,TŠÊx¡ ½Ö‹�†æ½[í¤‰ÀBa^às˹›C1åÈV�˜ª–¬¡ähj̪RíïCÃvðaÈ"®Æ3~&‚O˜ñÛ|ñ!EÇ*ë1GºÚ• *“£>'jC�ðÓÝ´â$ද‹(º?ÔÔN¬mæ´Pir7]B1íØ6éC3áz[ÿµ�MÝleh0]{¯Æ5\¡ÞêÀ‹DË3ùÜŸ©0Å4-†1O¥)>Ò7¢}„%~‚öÂ3â_~B6Ëñ 4Éja9Aã¢âÀ}k¢·|*šÙÈ Mw>PL$lñ»å^µý¾ž«¦;ÛѵmâÁj}3ytð£¹A –ƒI’…™ûÓ6Ø-•)a¨¦hT`)œƸZ+Å¿~ßàí`‘s MËqS¹\‚»MÖ"Õ¡y-íe“÷ÝÆ](®þµÛ¿²¥(É8Ä{€ Ô"‰ññ„ž_½ƒw‚ýÅJ\„j4\Óéf×VuM®WqâÀa„ë˜{YñÛ¯r�i¿’IÔ]\-¿$ÌÝ›ï•ZØgŽš:Ùr „tÚC'‚Jhº„Ldf9fÿ†œãX0D>óyÈoü4?Ý7}n'�…v-}wÛv›?XccÁ²r=�qy®WòR˜M®9,oóD)nÖ8c룅Cº‡ù*�´Øa×bÝë��kb¡-[Û—8eëè÷åòõ–Ä GΊïÁ·Ý¨…ýULjøõë”_ӹРõ¨ó ;_Ü_Ž¢ö>F}ïÆ¸%MŒ»ü%URs×ç�´Y±Œ¾ .õwL¸œnù�—È�±é×”æçõDD'ÛÆÕ'|ë�^ëlÞ𠚉°‡¢A<pHwµØÌõïoˆ©-¬óù; 9¸Dñeü>™¬+Œm£²uõ†Æ£ÊflÓ.ÝðÍ×lÀ~Î÷jx6D¯.†�»ÑǼ@Ñö3òëÚqTaô½¼bxÃwˆó®™Ù»‡™$Õò݇�¨ßøKå°af ž«%ZdC@ûdÈ>DòòéÅpÔ�ÊY�Îb‹¥”qíV|e°,�¥ÌüJ,9W M_ƒ¥7¹�¥¨V77ÔW>Œ$ªÙäM´ìlcP8�1P Nï¶– <Ñg;;G„ÛzâF ðä/º$ ¦¾O½`Y[ª„µ+xf1'\¯ÕGÙI¾"Õ!�ůA¹Ú[†98S²ÙS`ë⟨1æ]P§£ö æ¬U�[æ5ÊÚç|–)]·@05ë–7¶VÝòÊ©[Þ˜êtËš© ©–-Z� _Pƒ>à'•ûÎÒã¡¶G3YªµZ tMþèzí5¬GÌšÀ\-X¶„ˆ®@ú^•Ñ~†ì©‡Ìvµbo™_il_ç´#`aSU]³²Ç=±)N¥9‡®AÇ·Z/ÃY ¡Óg'O.añLÕ._üb¾.w™M©toˆ”+ІÂÅßÔѧ»]ÿF�AÂ1¤µê7¼ÁdÜßµ:öT«v¸ðí¾Dø7¯èPK C¥Tgpgsigned/objects/6f/UT äÁsb�Âsbux èèPK C¥Tb2ª´´;gpgsigned/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045fUT äÁsbäÁsbux èèx¥�Aª!D³ö}�G㌠á“Mr‚\ m[FãÇ1äú1gÈ¢ (¨G×Rr3ëCo"ÀvŽ^hq‘<7éÄAóY{žâ"Î&$ÄDýS“g�˜([B!´Î¡wƳY’Ÿg±£ˆl$%E¯¾Ö·ø¦á±Ö²×'\d¤_w-™[Ýkê'®å¦³1hüµÓZ�t íòBÝó&;ô•:¼ó¶AÈñû"åm�%ªþVPK C¥Tgpgsigned/objects/b2/UT äÁsb�Âsbux èèPK C¥Tê=«ì‡‡;gpgsigned/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1UT äÁsbäÁsbux èèxÌA Â0…a×9ÅìIÒ ™€ˆp'î§1�Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm�'D {mµVêú(+´ñælè¶,ÞPK C¥Tgpgsigned/objects/14/UT äÁsb�Âsbux èèPK C¥T„Ž‘…££;gpgsigned/objects/14/4344043ba4d4a405da03de3844aa829ae8be0eUT äÁsbäÁsbux èèx�ŽA Â0=çûe“4íDDŸà 6É®-˜Vjú‹Oð6 L^j�8m�“ uY¤¨ë‘U:EGCJš;¶vòæÍ«Ì ²*‰j-…¤ž1hŒ^{ë,–‚YSކ·6.+Üd†G[yKpNŸ\ŸS·tÊK½€õ�BØÃD³Û}°É©¹�-3'U¯¤¢„Áugó>³í6ìwæìÙž³»áOõ†p¨Ò¼Ôr°’_žʧ+O\Á\ðiy|Éãt¹IŒŸ/ðPK C¥Tgpgsigned/objects/13/UT äÁsb�Âsbux èèPK C¥T�î£õ;gpgsigned/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08UT äÁsbäÁsbux èèxKÊÉOR0aÈH­äSDPK C¥Tgpgsigned/objects/a6/UT äÁsb�Âsbux èèPK C¥T³=�¡––;gpgsigned/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750UT äÁsbäÁsbux èèx�ŽQ !@ûös�B�Q"‚ŽÐ ÆÙ± r�ÍîßÒú{PK C¥Tgpgsigned/objects/45/UT äÁsb�Âsbux èèPK C¥T#Vaœœ;gpgsigned/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6UT äÁsbäÁsbux èèx+)JMU045e040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3†F@ �“™—_’Ÿ—ZVz ¨ÿÚ™=\ç¸Jœô§÷¨IÔT˜’å¢mï¿X.“³?Gh¾ƒO˜Óý›fC’PK C¥TƒÍê;gpgsigned/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057UT äÁsbäÁsbux èèxKÊÉOR0fÈÈä£ÎPK C¥Tgpgsigned/objects/1d/UT äÁsb�Âsbux èèPK C¥TYßoÚII;gpgsigned/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568UT äÁsbäÁsbux èèxKÊÉOR03`ÈH,ÎPHJÌKW(H,ÉP(ÉW(ÎÏMUÈÌ+I-*(J’\\!™Å i™9@áb…ÔŠÔäҒĤœT."‚®PK C¥Tgpgsigned/objects/cf/UT äÁsb�Âsbux èèPK C¥T/6f‘¢¢;gpgsigned/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9UT äÁsbäÁsbux èèx�ŽÉ Â0y»Šmäcׇ„ÐøX“<#ã<螈ø�Fir[–y€Öö0:3(ë9•­u¡�¬4J“THZGÊÄA“xÅÎ뀈ѕ̞rµÆ;äàÑ©P±–¢M¨¤�$é*â6¦ÖáÎ+‚5=$.>?[îݨO?âÔ²m•£Ë*€ë9Ì1om¥žÑ*OLýª£S|×°ÄÌ‘I¢(ƒ&ß×,½ñrqÇà’Ç5–·t/£¾Ã!¡ÏÏ؈#½�:h%ë1º%Ç{j‚õv7ÝóX]¿ô¡5Bö�ÛÿŠÛ=1/ª¬‘†®¿¨|=ì¡`ÂÎÍËU Œbt6[–Ð¥mw[E°`P<ÛIÚÛDè[íç9â}^ôÛ1È}­ÒnÅó¥¼>>^€>Rgf�…Ìg~ƒ½Û½Ú^f1Ïý§Ûç†û %'ó¯óâ§å†D_¿£6]µÈÜ’7 ßê6>-kÉÜL¹¾ñÊ«pmÌÝç:³’"à‡(Nµ„… ˜Ç�°Fµ[„ÚƒÕXc…,i»/D "{‰…¶á+jWÌt¢äƒeÅ˪—ïíI—�9GAì÷ê6Ë�/柞—üv69x‡½W]u~#Ý%7uåúP>ã«ÕŠÎÙSp�¸ u ᜛P G¦ŽÌ‘7‘–…6¦;†8Ìk¡>¿Eg0s2L³ê™b^]#�ëˆÙZŸ ¿Ä­åìŽd_ï~µ?)·߬âwÚâ„Õjâ}ÿ‰ö„ƒ 3J¨ü‹_mÚé˜ �'Y?0Û�:)*I�žûT Î{‚�ÓD·³JÀ‰‚�  €–Z?/ ˆ¢;GWŸ8CLþu&a)ÒÆ³=µú·[:êõ¨[ªmSw·h›Ö±¢/%µý‰Ss›%5­%½šª¦ Ú5ë¤Qïl3Ï6í}ßÎíã­WwÒ´®ª/e'-{åÍç¢Æ¯t*ŽwË^$~ïÊYç×éÂĪ<ô2O”èS³î½[µîÖ7Jm3ͨU1Oy~¤®Y4Ͷm»? ÏëM(uÖÚÆ½….’õEJ¬�,¾\I .HÈ77Ãs"öÝCîi!NBV 3XV1¼ÐÒµ¤àpKÔÅÊÊ�œA,Z)S¼üð Ôxç·ž ×u”*ÜD 1–�p `zÐO­L®¶rRø"„×[¡%G·RÜGªdq�¡"r%¡4ÒT÷Ë,�HB%”|¾}ø£L†¸Dk®8ª…\ù4нTç™’xù4VIiñaø@ë¡< iG7©}�ç)ýé9¤š�]+¥š¨Gßì¢p`ºÚNN>¾†-à î‰d«&¦Y,‡ü…(p Õžó„¾HÞa©ojV”ÕVb¥4•qãÜs¾HÄ¿8,Pôéîá Æßˆwðíøˆ¢Áὂïå?0Tèþº¥Ô€?¥[h”ØÐh¸C6ffyOnœ'â¦4¼!ô(KØ´U§¬’N'«@S£ÍXÑZN ÅRx µY±ÄÞ©c‘Ý�PΉ+Y.a[œ š66Ÿý˜öŒ\<8€I6iç>�»X'WoÓÁÞžèz}X¢×ÁÄKnðPú¦Hë±3-FQP�6Ö~?š\Î×ï—!¸­x¤cà�Ê€çjaäuÇø¡E$qG÷MÜÒ>/”à—câÅ–Ï[«PÐyç”KîÀWÏÄâfËp J~p~áມKÓ0æQ¡—<Ÿs4PÅm§D[.ð>ó ’Ò±D=Æ„7kÓM÷ÀÁ;ñ¦±ÓŠ vÒäa.Uð>�áN—ã$îµTalá®ëÍå:ñW>44ªP¤Y}¥èÜ.C)>»äÇc-¼UèÖjÕý"6n¡ù Œ:‚'®µüê ¿4�oþes4hWŒhå¤aðØP�J¡N‹Ý¹5ÝVŠ�îß™íéëLêæä#!»Y–Q(÷µÝ¾×jZ“ÚÑ1tÇ�[æiýÓ1¨êû{êÉ|Ý<š‘ôÏY[Äo‚…!ÐÑk{Ŭ9Ï:Ps � Æùg,Ÿèå‘@†¢2ã” ¶×ÂEŽÕ`ŒVØXþ)C(‘‚`EÅgß9K'®¬Ã[ª�þÅO†þœþ-ý`¹}È�¬2È÷£§Ï5ÜhÁ¡�57Šoô~œº»mùŽ4(ÕÂ*G•Pûl9ª0ÿú[¥?U êSRy{Û—t�¿ó„k âÑlieÎ�²Y`\<&]çÆË3¢>°i=Iaœ�·+€¬Gõõ+ø­À—Lþ|ýömt°ðÍeZ¸Éæ² 2/«2{ÿöÞw¾ôO›®´ÍÃÿ›’þoJF ÑâŒ;#Á€iËÚ8)j#6/Zê�íK{yÛñˉÀ\»T[âmSsÏM˪RÔêÖkµ]wÙÕã¦%›AÁlY”âö­×U¿WT.µ²�ÞQûËu‹eêRBÇS“·§ÛÀ6{yÁX^dyÏ•<ĬҚ–’R]u ·¦í„ÍËü޽òõ­ne _Na ¦>å›S9à�œW8j*ÇTÁ«Œ¡õE‡"Ê%PøÕڀɃ¿6XÛÛ¹;Áü,‚8•yCìƃ·lÙgVƒJuâgHùÞ‘åâItœËÎ@ÏýF1Oã¸SóÐXÞcR'=�IŸÉ—>Ó0£cC` 5ª%–y#áÎ/Ò´}²ÓÆÏÿùÛûœº²+Ÿö¯Õ’åN`Âî=¡ö5 ÀJså¸ Õ!îŒF]SÆä°ÉT¯‚ÂÊÉ3EƒV›‚¢‘ß_Ï÷Õ†!$R³fêDd[U×lWkYº[7lJ“´mP“ãN0Ô§a “@Õ¶Ô(m¨[‹*¨—UJ,•7‹¬Bô:àñr&7rãnЭññÒ¯íVì: {‚ŠlÜZ•T)uÒ”¢Rpq™}¡U¥Ò,ÇØUv†zd”#`Ø2­áŠ™ v@ž.|ÈGîó Î:0Â’ç~ñUkòJ(—pzÎ|aׄ6Ì1N¤RKÌx�   íà…Â� Òb,h¦7ÝÃ|`¤ÕÉ“–Ÿ¿F†:Ê{ÝsȵÁm�®ÿÞªšöm Ù¤ïoÞBÑ…×wLÇbF6ØZ’‹Y •VÇj‡ •–.F–:ÿõº„kÆ’… 4wÈã1Ñ“”£˜ I¦©jÒDË0Ó�Ó,ƒÚ£àY™Qsc*€^¨-Ä£,Œ©ýDe-5tÁS̵¥*T¢h"×ø¨W›e"Ï$Ý®ÈÕ�{¿�Ûò%õ¾‹Ë@Wå¤kÚ€=EI ¹»‰ñË)ê±æ-! ¼ÖãÕŸ(%®AœZ�M©pdPBÓ<×T,‡±€µù±VÙ—ZVÛ¸¶ã&LÝzu:PêT›&Çè[DIæñjÛ{!‚w'þæé…ƒ~{>5r hµ+Å‹s=§éˆ.ò+5ؽOáÝ„ÁlZ1‚Z¯nx¤^¸Òà¿{ 0Bø��±�NÀnãÓà�²…§6vVa †K“2ÌŒ¦É1í8öDkÒ å¡'|ð3SÚ‘»¨¬ …„gü‚²\ôu¬ó^šýnRO£åoß’Ù`t3xËÆÕþÕ±óåÊþЛwžBì‘;ވ̺D6Žc Ùd+L¸­oJ7Þ�Ë\:!sèz¯wgÊo ?³h }­"JäIü$YÄÒÜñøçð½3óÙbd–hVô µ”Ú—&I´¦!sn½UUR²Þþ>~¬0áR.ý¿Èæ¶ÿÛÎä ë;fk¡À‘ˆ¦‘–3l ‡ÓÕ.®S J—$½í¿J´ŠFÁÔ‚ ~ÞyïüJ"r àm¡à.m«UÑUõ]ŽÃµËãN™u`‡n‚†¿@zve¦(|¯³]€Ùn<Û€¯](F¨pX®ØnKgùà�™iªç!ÆGvÿv|gÉíll_ʇ_héN¿Åõ©Øî2 É“•zë_ª«ÍÖWzѤŒ˜>=’=´ãðóö8Féè#qYôß`œ§ëÌa’á+ ‚ó¡d±¥)d¡Ó˜W­$ÉïÕ`Ít¡Ò+C•&ZZ‰&Gsm-Q³öæ)Ö»®Ö«zlåì)ÓH&ðÈð =p\Ê»ŸÖ¤_CK�¼0òB¨ eï—a&hôŸnVE]ocçÄ®¶ÈíŒ6f{Zûc~LÆAl++ aá‰GÞžœlùÁ¸nUÈâ2€ª;›×˜Ýâ²ôì÷ê'�æD¸ˆ¼EmÃÁ3¿WWÜ“zÂÞ”Ôð<� $§œ�²9ûg¡M�–éeWnu̺qý«ßïÔà5•æ¿Üo·C€6Hy–&|{Ëj&Y\á�§¥Ú Ç ·Ü:´ã²ß·±'¥FÉnö}ÿ“‰H‰0ˆ8é@x¥ö †Y']ÙbieW’„{(ö i4Rc‚t‚—¶‚Ò7EÖEI•²´ÑUvVÒ…v˜ó7߀|5ú v˜É!gáSúû´§7N ¾~AUè‰ì´'lþT�f]錭6YÒXSýØÀÍM�H£¨…SŸk˜¯@ÄÜÜ3ÀÝ'þSgq{î Òf_Ÿ�9xÔ±=�è A}ÀL`c÷]5BVyî×Á¢†{ߨ�ú'ÄØØ4p÷´yâåL©¬z ˇ݈1s5ò•,£™,Ô­ÏÇ·A%›K×_h6E¾ŸJoüz…}�×Ðl"øKyÙAù0êXÑ‚W—úX û§å¶cž�ÔÞÑ—ŒwnÉÑý\üt𦇫ôKïmQ÷üÃǶã4ÒéçŒ "žný+6;=OEýÛÍÄÊouÔ+¿/{å¾�Ò ®²ÿ‡ÁH�0 „½zF5c‹šId•¡#:A;và:qZÛ2•T2?½”Ó¡ºwæÜøËEE(@ŠÌ¥Pjï9iB·6„Eú(&aÍAï¢ÔÖNȉ=CzPf…I–NgaVQb—ZÑ´Aa¢².‡¡GˆÌUÂ#êåè5€c&W“øzäÚw)v{ÀÖTh Z*«8BKIúáêJ½ñ·TI©µçSt:ƒgá½âJØàï^âÐpØ?¬Ár 6…�ã®v¶ðöÚ·C»ºõq{çê<,LC¢ÍO–tGØKçs"/ÂòÛöÉÄ¿¶KƒIí ãEŽã€Súû”tAúl˜ nâÄ•kQV7»P¶¼×*轆ìc}µdœ3–N‘¯{²\ÜjáÒÿház‚· •]¦ÿ«¨‰½v; !Þ¼£³¤wWëæ‚U%‘Õ –°Q:R!¤Jê}ËÛÙJ>–ÇIÑ™öf�8 Yr+%.‚²²¦ÞwXÊRÄ2Šî)碥�qQjW6i�8Å=ô®ºU¶Ö‚®…!�ÎÕ×Q"«ª¦Ë—Rµ÷Ì�Àß­›y&Ñâisý4û$·~•|>£Y̻汋FÓ{&žkfÉ¥eò™ÙfIµœ»[¿ìê1nVÈ50Ò¡âÆàB«Ó¯ýÖt¹4Hs¡ñð~`ëˆd˜ˆˆ› \8Ñ—á¿ÍÚ{|*Û6ÑÜ}Am¿™g ë¼ ÷<LÜ;s5ï¿ÿ$½�\q�òzä¨&ÌÛ­0 ŒÉWtŸÍøÚRÕ<Öo‚Œ£n+Qׂ¤Sem$Ç*‰7n'-ó"©ÂáüÁyàáŠÈ�Ô³‰¿}÷Ûgß×û4`w#ŠñH<•PD]Í¡”¯FIvÍúÅãÌZªáª×ˆ®„†4SÔqtîª*"9*J'‚¸Æ¹Ü—Ì/Šf®!Ïn>å‚k¯?M4�Ä#s@3œÐ‹``÷¡�fÅ ðÂKI¸Š§‰Q±ìÞçÐ#˜X9ÀvÂ&¨�±ÝiÜ�Þö}ûÒÄ‘S—ž®ì>úöÙÚÑQ6-ŸkጲŒxÛÂ1ç—>èýã饿²_”©ø TYþ´ÑŸ^ŸWœ•6Ô›cüøñ%Šl‚±/+ ̈"º>[ï!-J•i’5ƒ‚ØíB´�ŸniCN™™Òö³'£-ꢨþ~´ãüä$B ëºÁ¶”©å8|Uv&&¥8 ³>ÄÄ~ÛùE¬"ÓÓÞ’7ˆkš’ä´ý‘óË¿AÊQprHkƒ<ß÷B¸ÊDíØrö�ÑÔ¡4  þà,9í‘Oh€÷YCjÐ)¤Gh@øq ­~ÞYˆ¬oåÒG/)­ÒÞî¼Àé¦÷�M\]€?çÅ\}ßâQ€ ¹v¦ëí¹±G§i~¿SÐ?¤Ž¦¸^†—>Ÿš3¸ýMZdÏ}A®ØYÙ7G›ôå®vú<ÛÅ#u¬6€€ÌÍMŽBr›eœ¡Är* áQî6ïÕ ñ³Ež¿{3B±ºº‚ ‡Ç&vÝÊPšÁœ ]ÔÑZ-ù*ZEú¨]X%øÖN7¥ZQCV¦•¥hJs½ô×NmÀÃL­‡ÌñÓÂ@ÞzÔ>ÃNYm·úî?ÓúÐU!ôpo¦\ô/ôUyo²˜«ø¬c1Íó‘0ø¼_(j·ƒ_³ð7‹úáBààºÙ/{9A3†Š–’"l@\`lȰ¼; 8Lp2hÞ‡° l.q/°ñùŽ”¡‹¯…/§Úö¼A¹$ô2¥ ‚ÑLo0Ü×Ò½ÒkÆâ‹ûÏSàˆÇ�Sæò}’9`X’�ÌžZ­y¸¾ZŽã%rK£!;žñü’>[WXO9lO(ì9ºØ{ÖÚmUŠ !”^Ç?¸^á ˜uÖz›Ö]T0÷Èt�K•<Õ5âú¾>Ö•Š;!wLæ˜nj­wþ¥³(ßí^¾—1ë°åûl1¢Ï;ýã”óŸœ.ûO» }奬Hõ?â„Ô܈¿u´–à!uÜÁ+Çh+~õ½—Õ�°Üm»'"K¡‚«ˆÁ§ ±æZ÷D�m ™£t •vK™´®E-„”â¼×riùZȬà6e«ípæ}ªç´Ù@¦cÖº?ŽñÜÏ»×îdz77$ÏM)Ò=סþ‚ÆmM”Šs v5ŽYp«ëÄ @^¹L!(e[ïB?:•8pœt’ì™Ñ`Fïàr´Kô0T@”¿Y¿žƈëgz±G±"Ê÷ÜÝ@~„Fù’O�máä›k1‰ª}áćÅ-oyÚo÷ÿ"óP )LFþkŒ¹F«�=ýFßiji8B�:9T‰þ$u>GóˆµÑ/_‰ü–a3c°à"i äËMÏi²øàÕ¬EC#³ëO †¢®å1Õ�L››b-\¦îÀåE)FMñÁqž&ñ¼ˆ "]:Ûàú$¿ p[¥Õжëï¾»ùÊZ{ üœÆÀ¼–E-ÜK-‘7}¦ñUË·Ò+ŸÂ®˜!]‚tl÷0os„(\©Û!áé…û”êßšiSO•§>Qñéýd«ìZ�†ÐYú9µ*‹vÈŒ>qýFû…•tézIЈ"!� J­ˆhœ}½#æå/zÂ?y®9zÚÔ„¹(›¿fæjÆ¥á½4ÓÌ;T¨fà �†¢ýôѦ[Ø)T£<©¦Õò"b], –X¨oM-©ž™fÒB¯b¡"=‹H¦§ ùRªø1 6¶¸ÞªTr¥Ä±öY^ÌŸ U¥uq©µûÃõ¸pL'ø'd“È"vµ�› <·ä“hv…GK(ü×& ÈΙ  •(Ÿñ HÀŒñI—§Ã õ‰SÚªnÔ²Q1vöW&è à­=cDX¢�¢òÍŸFŸ?|¼Ý j~�XÑâöð…¤–‰Ò4Ut6é9“_ÜÁ{àúÃï÷…œI-±Ò]²Øð6?ð®€å¶ŸÁ£ÞŽ~’÷a$ '(N\ Ç5üæθ¡[J¶Fa…‹Ç[lNûÞË kàØáx&tuÂQÇ{‘<«·t„�í‡Ä'2–Nh´’ü*;*eІÆÞó§é«5î °"ÔÉȘ=l}Érµ­©+ZÞ½mcêóùáöŒšìՀ͢"œÅÃÑy¦Áyíªˆ©M¥½È�Ÿ+àãÄÈ›­Ä:C_¦¼x‰BÕÊÖuÖ3½Åv“‹šD"ÝeÕÝK8n é^42LNR5ՆȫÏzatM@ÉC�¬UKo-9Èêߎë9Õ3|¾Ÿ{ú˜‡‡ö©Îc&sãôÐwæ§ÚÑÝp*ÊÏÏ,§i]Ö%Í|Ž7KQä>ÅQ·XËöe�mÎU͘ÜÄz§ì´Ç%g–˘Äâ� ô>6ÁêA©;¶¬YŠ}ËiöË äë8“o‘ÐÔÖm0Ï¥  ½Ä˜+ïî5îx«ì.ç�lyˆÕa˜ôœæW.â7´ªë} /ø)Ë<%òUùé�ÉÞ‚R á±7¡h†2´q|~cn¡Â”8¡š; ”8 ?óÚÍ©÷QP®2_�BZ ¤!¾…:o´¡»®Éï(@ÿc5qã%8Ü‹‹±”?0ˆ¸sº ãÐOœ²µ¥b ç õOª�~¦tÛýZFø^ÍÅÔi$ͤ(k¿ÍÓ{­þІª ä¿©‚"˜ç¾Õ`-8™2;—“©™èÏÏõ+à¶EèÄ;þ^Q)bÀ«i·°},ú¢6:Õš{Ûªrw‘�Íãž¶ÕlLHr€ÝV¶qt!|£Kø¾1:ªín¯j¥±Z7^W½x°9¦sÀŽúÍÐEæ}üÝ“ƒPþ#A^^�]¼»ï@÷•†ªX79ZáOQW™ãÝÂ@y•b•'&#ìŠpa¾óÜr8h“1Á9ÐdCÎ9': M^©A§MÄhFà©+qŸ]Kʾá( ²C½G“W4V,C öîΙdb6ˆO¨&_ðEnë¥7-É¡�ýL{+$à²k$¹ zkÓRÕô9HõÚ·½ó:ƒ�sû©&Ác û1Ë'Èw•c§§7>% 5ˆ)è¤Æ£'F}÷wObÝŸ)á"¿tã�÷éá~¿ÁO Ž�1´òmêüß 36BI"lÙü<‚À@NR7!›K›@¦| [Nn½ÉÒ10ÙôôõüîÜATĪt]”Ôe£¢95¤"]‹%úÞ¤a¥ÂÕl­bÍ ¦©ªQ5#YÕ;岚ŵÜD)»jfº¨•«*Jép"{s8šÉûÂËÝa óË×GÃýâ³6r—×ÄS“¢³Fn�)]©Äò<ãRß"÷Ü5õÚ^…§�ä@Y`8jà “>[ž]™Øl¬1áœ<Þ¼9€ÍVûà fƒ1Ã�‘À½ø"�=„Ñ“X8##µ#Ûªú22S®®Üj(ëAËM˜¤³ë‚ݱÛsò×ý!��LfÂpä }Yù /YÑ.èMŽà½;Ã<Ù™6Ï9ç7ɵN©ST[¼»#3i:n2Œ±<ÿbʬ�EÀ‰‡ð£�ÛµÀ-‘V·…[uÿ·´ ±ëŒt6Ñ¿ •–ßa@èûPŸ© ·J<&‰¬ : 9ækï¾#s<‘ÑxÙ?¾i÷ ¬þê gFôXí’¶N…�M®…’½ØÖÂLÙ±Mù¥k­ É SÛ¸¤9T瘚&§~µi¹l%¬J­{|MuÑ…Öñ@gêkWÊkp±c¼Eßß<ö=Tzˆ®Rµ¼„Q‰ú™"%¥¨>!³_”5§³–{¥º3êþȰ�ðˆè5ôãR–i¨G”ŒÐž™}v4ùM8nžbcÒJIkœZÇ�A3ê]xsb8eY´ƒO?2}"�ÕCÜáÙž¿Ã”R2Áˆš blDÕ?ñ³�Iˆ+ö8„'ÿ,c﨧©,Å{pa¸WZúZ-âøcÍ{ÐaЯ}.Ä¥oÆ—¦G P¸ýåÌuðIÉŠ?`¸û¢=NÁ%ï¨md£�íÝóÇÊê LIÇmæß¤™›°+Lˆzïå °Ýк†aŒ?022¢Í<ЭdÑf“Ý×»�]/ÓÌìsŽS&#R°&%uïÕ¸¸[ugMÍëEÿ¡Ç}¬®c9Šf�\ D}Ý’¨ÕÛìJä(,ÁûÛív!J.ÁM�j�1g:ƒü ¢Í°ž#@Tù;O? 7¬£Åó&WAëR�ºŽÃ\u7TéŸb{‹É8ÆüáÖú7ˆz;pŽ˜:k àÙ0ßùÄÆ¶Èx¹ðé”DÂ,úf KâA8zÕâàŒÁéЖrÏÙ®rZCm:aÊ|öÏnUÝ]³ œª4¢±ƒÆÄ—ÝzLöxKÿ/Ú+é‘�2ÏÉ o ¤NÑ?ùxÚ¨/€(Т¯Tì¿æ‘”e@è9§~©çË*)¹If§§Ò3]¶Û2È–jöÝêwG½‚†f`žyÁÊFÐafÙ»Œƒ±Šj­Ó%Ø è²Í…gäªÞs,žY(¾µŸÃnéz²¿56Â~Že_³b~–£ÞñÔb÷�üj¿d—sÎXoÿ²Óa˜-÷ÓµÌ^k׫ ¬²»Ö‚ì&1û®º[.ÿ6@[Î#4ÕÐ,AÞAe9Ã2P:�çß2Q ²æS»ò”%òKŽC€`Ša!&|b0¾…'äΑÄ̺*cJ1# (r5eÌ2íˆQäF€©†±�I‹ð/Á¥é¥ã · ~’#/ÍóƒR-ªrü¶nù„眆ꑓ×ø�É ¸û�ÞR,æÞnH†*€“‰adÌÿâŒÍÚP=Bû}/8>ðüœV®Îޱg)ÔÏØ„†ÞJfĶý¤ÎbMS mŸDMà|Åû˜ü„Î)†”[tw[°éId[tdÍ› «ç¹ûGO: ¿ÝŽë/`›ÒzâFsûû¤Ä÷�[ÈC@HÅXs½SíÝÞ„aÎã[ñÚþ�ç 0LÔ®) ~ï1‡üÇ�Ç1$˸Õg�ê7›¼¦‘²’ÕÐ×Ã+ðoȽ(kÔd”¿ b¥þ2r�û÷(Nª¥$K™-š¦¨"ô×ϤÌŦm±-šú ü‚òñîutDŒ ÎlÇ‚DóhHa37MF*éÚd2&ü– �Ã-XÁ’â'©¨)+á;ߨ‰ °{›{gáò?zh…–¥Ìêhµ¬�2ËÕŸüH9÷þÄg™pEÇŠtkÓ°7…ž©ó]+@î¯òÝὌ¿lÑ좬’ç²ûïgн"Ì&IÿbIŒ[a@¹ÿŽêƒ… ‹:‘Dªé �£v»/ŽÛÍÓÈÞH¼eH(ä¬4­ÔSUç%º¸ŽkÇéîc+®J,ª!•®ÄbÙ€“RQ•-2†s‚t¡”!Þ.C[AêÒ3ZΊ{ÕI¦ü�{’J2è([‹s�íóGä ;þý¾~Z¶Ù-ônäûÒc•­æ&K{ ;9�DÉ?ÏFúG?YO‚°jŸìFÐZ€C×¶KG®„énR 2l²:Ý'1X3Wj­{bãÉ‚d©zʲû†ë�Ü�CYG‘Æ/kMWwàI"ñ»Èå" '»í†©ØuM7ð®3bšÎ1œ_Ôùْ'ÅE½å†å laðø8}zynØŠü Í®X•Ø¢zð„ÑPÂaÀ»rù#Æ­VYÜN�Ì_åÎÒ߀(ã�ÁÆ_éµG½½!ñ° $?Q%†w³šØ÷e,'üv¸?ÿ dkg4"ƒð¯¹fÿÕý¾sJŸã5VÙ­B; œqäŽHi¢-«¥Â^^CÜh¤š��FþóP–¨:Ô¢ZñRÉ„²¼xXÇÊ’¦\V pJ­êD\ÇÂC-É£lpI©öÖ3!©e‚H¼É¨Áµ*˳3ttŽƒœ˜yF… �Ó%^»Põ´3 *äêß·€Å[r¬­ÚO]fdsÍó™ßÖKÑrgVCp |ˆ&â 8�S�Ä-£.ÏÈ€`Ð|2|60ÈÂß2—Âq �S /'ò(P1ïìé'G\š.TÉ�Q;2Õ¤Þ¿ 1æ¢�ˆï P;˜h\t(R}+wýøäXÒášãiLŸ‘GmÝor£Ž¸g\âøöKêAP�é¿YvÇwàÏ»û|~ðcÓÈþfÕÄío-ìþ %X=ÊNRÞ²)B�2¢b/Ô†y¶MRviTÔÇ}¹¾)c&Òþ[¦H<$¨]vY¥¥ UȽf�²œz RÎ’úJÅ4'fª86 ¥ šDÞ¼¸c¨iJTשöZŒ¡�+/ZVñTDÖñœ‡§’°x‡3�î I¿žg™ƒ#»•.{¯Ž€>«é°…Š3g9Gi©Ë��ñŸ°•B“ð s¨Ow˜ùeC,‚;$Y.Ô‹ÛX–e.è:mèíx”c”¾s°‰ƒ7X°uT�fg+â+g#³äÐè22A$®L ]/‰]ÝO_à}{ya’˜3I`oz[}N@“÷£¾&HÙ'Fï÷“]10¥ìdfÊ8Ú{¢�š¤$Ocã Þǰ&=à¹àäwB¬.X<žz±;mX‚[<>%!o"‰ËÛ“PÞìZ‘^g�¶-ÜõþÄ;˜ÿÖdKþ¿-')÷×Ä=¨¾âÈ›·Ø„ Ga# zã¹2,#q9s†~òxåZ*ªTv"1Є$-sï)ÄRÏŠÒöÁ·_†äEĆ#Xب`Æ å5³ƒ™+ç;Þ¦®PRE�€±7ãÁ‘O¥lÑÓhöJжû}«ÇðtËJÒì+vý|Bå¹éZ`ÉRRŒ+¦™™—ùÄdÑpþâ#Ö=Kþ ˜yNa8Û"0:†EB´LƒÌ5±kjà[mÇN»ž»˜}RÙ÷O*ùWyK·Ÿ¯ÔGÈçÂvZ Ä‘%ÌÚí „¾òŒê’I�a{ßTUuId•T!Qò[¥ÆºFÇr¤Èåñµ‘ïº&Öpàpìw&#"¤+N•‰®/¹µ1k°ÒÖ—ÖØ.·Ùª¶K¹Ù„'ú€i»•ÞPM¶"D±ãNŠÕv® ­ie´ªªDI¹&òÅáÚ!ãˆ?¤Hë¸íÇESÀØÓ»P¼ø÷Q¬»ÎNwÙÆ@ÏeÂm×NgòA]V•¥<Âõu÷¼sÀ{’`èÜ3ûÂùN-¾ž Üì]ߙ߬K|÷«ç1Â)5ÔäžIÝ…;f¸à-6O_‡¿üééïzóýq!㣠ý9hØ.È;j.WgØáùá5™±áóˆ3tr'ìäTàùF‚#'6v<#9Mb§ÙÍ“YóL-Ë�“Ê=?m2˜*Ò ›raËNuͬu) ¹6ÆýLÈMöiãÍért÷Óh16—-²Yúá ¦ÑOm£0£ô‘ifA�rà €ãþUðêp(éi“#ÍÛÖÍôàyD«á5×›ãi,ô&(®ùu",£e,ĸ¼ö¹¶ ðé+ZÖ�~”ôÃ5Ë4ÔÓ\ò#ö’ˆ†‹'$#Ðf;ã$^‹üót€½ÝnœÐ¶Q(ÈÙêP9 ÉBQ¬&Ke}�€ÒE0€¯ÑÈMÄÓcÌ;ÔG¸@NÕ\>•ˆï^dj3ÃÕ£xb·—rþ§¨½>r{ötuôåÃÌÙ×±ùËÉÓ÷ØÛFál´¥ˆ� ”,‘ôsá6èV,ëqžVe˜m0%XÝÄÎP¾0ðR¾^N.Îs¥ÍöÖ�'mî¡ èí,6J\œu%.µG9ïZ+ýÒ$¤ÏKO€’Ä*—~�˜åû¸uÕ«X ÚŽÒ¦ý¤=ÏÏØ”¶é·²›1ï’bÎÔ0^¢·D{sŠØ =yÍÙaÌè]N­ÖÍ[|nûÚ/×n˜™îñKNØÃ¥H|xq L1‰“–OGÈvl#0^n •^Ôñ‚oJ*ö{¯Ç©Ýµá??÷×ù#þ»Š¿ø°|¸ÿ›m”ý[a÷P…é$)®'ÍÓ’„½/iŸ—¢iâÝR7’ÄÛ¿_q½ÙiS”¨„†{žøˆe¡"X&BkÂêTiу±´éµøoË]è=o¸Wd¤Ú¡u¦õ`f饲¦· Ðf)º²ÖK×pèvÉÁ1f./‚¾‘œS ¿]Јy@lß^ú¤ŒF47‡:‰úù†(¹È꺩úÂß{Lr*ÛåÉJalÆš†¼V`䆂GÊϱ‹ŽD׫®&É%Äù2ð ­F²ÚV#䄵xì¼ÛÐÆä6ª¼AM gGÒÈ1iÃÇà¦ewU@ûA“�Él\Fì²t"›ö˜Þak1Vtìï>Q¡Zé«~èc>d ~ƒôÙŠ®¹2-ƒ·¾C1*æ[’“SÁxMl¨‡dôÂcÌK cc Y Óg�JY É((s vÃÙí ÍÅÄ@™§: ¸ƒ/ ¹¶-â¨Eéï}šå¤êÛÙA¿9±ü’!+¿ÈÐÃ_£É·@‹Ñ.É[ìèAõ�J(i< Ïl�v¦ÁøèɼÉYë6ÜüùQù¤ü\'iÈ·Â{ Ïþ Rþo¥é=W[¿Ø2³Þ²:@ÂŒ„^J)³C¯�-'1Ýî'ÇfàƒƒÀGÚüåí÷ë 1ʦ(‰È–*-t@‡„JúHDÓ¾©è¨¢A5­±µÅ‚ç$¹J¡B¤J%q�‚3ß)OÎ1Ɉø1Š( ”‰ó�TÓ#¡7™»ƒ<Îì%av¿ž¤ª‘™2*l•¤2C)ñŒSgD‡YÔ·¾Wz½¬�wG£6‡˜9ÒYKž8ŸÑ9TËáFÍ^PHv[7Œm ËVkì&DÞ£6Lnkñ—.7;ˆ­_ä‚Âú"‹�Ô�Ѝ±ùjǪŸÍ/WânÕš§š�óx>�?€ðÎ&ñ‚]ÀV§MñëÐâ‚¡–òÂm.ÖEps¯ç`Ç.Ö `¸n½÷ÚÏ*ǘ!§þÅŒ'Á>¬Ïƒˆ5ÓÆ¸}`Óy!�ãÀû¤¬³¦­Ô?÷ãöúx ì"=�)"y¶²´ýʪYŒ—%µ›ðž¡:j 0ÇõÒT#y,á/‘¶eÛoÞc—ê¢*ºÈã{ƧX\"ƒ]ÃD+Õ³Ø:èÚÆ™¥²I*Ï6ån�2Í⽕ҌalFë+¨jòí.ZéJíR«L7ÝXîʦ¢­J­K­ Î瓃jœ^�hÍg‡ÓµÕßÃÔÒ,q&È¢“ŸÇ´&žÊ©šÉè°=R3nÔØbHÜžöߌûvó˜G£#ÙSV)Å“ç!«…‚úät†Ç³~�·¤ilÿ…7÷ÉÔÆP €µ:p{bç•K&} #ÖË�|Ô zödd¶s¦8ãÁïŒ^(~a�q11%'À¿W¹w_ñGEb•ÿ¢Ùé/r{¨2úNq,é{Qu2‡yx¸M…˜ß¯þ^¤�Ø{ãf=舼èÑ„ûàpQzÿ3Ì*&,÷¤˜_>Y¨OüNz‹“i0f©�wJdr9 'nU£õC‡Ð^üš¥Ëc<ì Ç}bŠe·gr…a×s10©%Ž’ä·ë•vn6ú8bà´â4Maä*É·ˆx¿`;…ùî"æ~§èÄÎÂI,ŒÓ¼_âæØyXËÝRTn®Zƒ³>qarƒß$<ÌÚUqëÐmdd³³3�Êð{ZÚ-úÕgªg-Å|.~ö”’+.¾Gt…ÏÉ8ÓGZw M~ù¾PÜêblGkEûªË¶ûæç‹ˆ�üûò12_cÚ18•èÿ²¤%�Zæ�S ��høè£=ÇWê¿=jtzÎÙ×ÇIĦtªTgƒªô‡�ç\¹-gqˆÐz!;½Y�c»n½GQÝé Ô‚+E<ÒãÜdyÕVºÜ–ß=ÇÄz*+óP¦äí™vÙ¡• ¦§‰cT!SSöj}‹B›Þsì ø¯¥… žÇDÞÉV@Ü!³�0 (RÅ}›�ôâCµ†¿û$W˜”†àN ‚·î zb§BÆhŸ <~z¿½„óØ�<�ቘL+Æ ,s”‘ñ<¾g Æ Š\^z7ܹ#4LÄ¢io0�KQ½¬ Ë�Ï.î ú膗i~¼›4E9¼Áæ)á*ž¼�ßç.b½à͹œìL*c–�ØûÃQq#;ºQ­Þ’¾Ø)ÝÛÿǹ°ËT_yµ5—øß^û˜0÷/(;KÅ|bíï.ˆ¥BL®¶]hÉg¡ù$ÂEÉ»ýtwÉp7ˆ®‹äùéts"‹�Þѱ¶ã›X±úlÃÈÆõ`÷ä5¸l·¦¹W«ÖL"aq,ðœ0¸Y-îV,rvìKþù1Œvw8Ùð); ̾ÍÒ dÙD¨�³éÛoWk«ÒÑ­r¯WpYî]æ+Ëù®E­÷šÀL&Ÿ¸ŒI´·¤`zRÑŽV—üµ>ºç±Ø­ ½5ŽÖr`™ÐiÖk¹ì]Ɖδñð 4OXqD¿6ΰ·Š•` ]%²‰“yS>E:ÂQ ˰fP‚ƒ†y0¾Z•À·Éí¸�¹«ùŸðxûÅæÔ¢Í0»ó!jÕaxHêM±O껄Úß²‰{CgœÀ{Ó2S°3û3œ¦AcÊàûdïgÁÐnç ®Žp‰~‘½�O Ï6Ck´ßgfFäö)¬†Þñ‡2Þ„ˆãHváÊy^8«=è�,Âs °»0Xѵ�ŸÁ¹±Úl«È­Eø.8—­QùÒ²”éË\¯òì?o÷}Pž¡!ì&”8.©ýóu0xQ-YÛ2wS®Ûô2³\»4ºZrGÞÌ+3žü³”`·ÖSÕmwV;ê¹PMtÝÕVëHÜS�e´å–)5[‘:Û”ö¦JÏùt›•×bÛ¨RеÓf –&•â£W«è0å]Nø<Ï�¸¹j€5¾�œv¦4+G6tºü¾¾°fk]6ëùCEt$; —Øéalªža€»^4BN¨µ »Ý‰§Â+GS£LG”ƒ „wcàë¸ l„ù ØEœmÛöÀFx´Š¼ŠQ“z‚;º&dOÙÆ=Ìc8Ï; `­Áèt½[ÝWÙÈŒhóÕ?œ6s’mzh>À™m\V,ÀA™ÿà™€ŠÉ‚úäZ.]õb$Û]ÅLpM„4 ÙãŒÝH²îñ¨U_e­è‘BÝ¥ÂK‘}—>E¸­eÚ,hW“X`7ƒÆÐ�È’ ,€wà‘ˆù(õ׬ÿÌ \¥>%a '0Ä«®ä:ËìnîVqâÄK]ÞÛIJï½P=Å”këˆK‘éÍA‘7Ù{ô´ �$9K’ðe¾$*\Ùµ‰ †óWyµ¦¼ŠÞq8H@ g‘±S=ùL&GO‚Žy§#¸Ù1�(\Jüý©¥!ÂÆ¥p7‹%âÞ ,.Ê9ö æ7Ó9§¾«¸í¾}ÑüÁ¿m¿7ä¯Ü-Ѳÿm’Ä÷þ ñæ¥BEœåû˜CG)T„”XùÖ$GËÁÝä“ØÃ»¢ @X�‹yžµ’ éCí)msƉ1ã= c¾½1ÀA®uéBW‚œT$…å¢%`5šWìš»ÀCmÆîªÄ­­¢´fïmVTæ‘·š¹‚‰g4³ÍÚ½sÒ0vŒ)dlÌܳ lmB®Á\OQ”’Áׇ"ë€f+œrÈï™äÛ Ú5^7ù<#9í©ÖÑæ|<�O8Žh˜ðÃ_¨+w@T ¾†Ó§Fk~¹¯zÛz­Tz0̇ôä4ÇweyË)Ÿºb¾Ð€k`«S«÷ ’¤…6þÎ î³�ðÌã¯-r¦öí{ÿùÞ?ÐÏžD¯óˆ›OýGÅ÷­·Æ÷«ïO¡k&Þ¦0aI�ã–ݰæ”:ÓXI r±Oûó×jã·…3rpôíøÞ‘“i°w5ç¦)µ²To÷érï$0¶Úy™§ZgÅ"“Ë `´¨•݆éqòÂÔªt­WíUüÀÞv‰uÊ%U†Öñˆ�ÄsœóœgTË~®²óçTåÌ2®°©I;�/¸Z«Q]žÄÚ“&-mpÌãe&ÝK‡`Þ!·wŽ´K°;žTÉôs®]ÙB©£Ëy;éÒ‰*‡ÞÈXr2Ò¯hya,EIFèàAšyê›ôõX•ÉdnБn¥’6‚©³P~Æ,7¿ Ž"›!Îá�/]Å=Ü}¹»óëç÷ñÞðÝäã½éÓS}`ù3JC—™sAï —çË$®1UN©eâdoÜêÆ�RQüœ¦°†àHAŸ©'Ç(>æGä0‘¹lä)©Ùø:.`‡n¸ª_÷óƒìüÓÕ÷x½ÓÏš�þ×ðóƒÿ>€xç m¡ÖVm“Y–CÆãp !s4£Z3‘bÙËæpÈ¥™ú¾Üg‰hB庭É×A Ûa/í%B#Ë»G 2T!›F¢j8àåd�åEŠ×ƒŠ£Pçµfr-s*¹ÔÕS#Ó†Ò=d¡lœßß9Ðn4ÐP²ñ±ž·kÌ]ÆÞŠuX6Ú¦ÊO3­SaÞ™½z¾”–@x' s]–MÄ7Ûî_¯ºV+K{¾r{dð ŧTÎQ?´ÖªT5ÕÌÛð:fůžä4)Í?I£ì•!´Ý5}øï?ÔÏ\!'�zMå zä„þ_µë—õ±·JÈÔÀÀ@Ȩ¿­^9z$|±�‚ L#~®ÜÙK¸$Š0ü¤$~Ìù’Ö]ÚØ!v8£å‘,KÍBƒ VÈÚl±TÔ¬ÖYG”RG­WÙ€±ˆS*ÂæK Ûwö0šWø:3�;åO›îæ#K×¾2]�™ nh�» ƒØ‹Ë\¯q1<)[H*_ÎZ+šXÏŠ(<µ·i ÚËxb¹§åo\âѵ—CêŽS¦%Ü¡—Äò�‚]‹-©‹Î4W‚ÒxÐãÂ2—‡öeuÿõŸ.q>Ài {°\´¿8ãÿiï">£/äöG#“—!p�PP"�úÛ×#D�òmÊ;ûÉÀTPæg±z’3ZkhÚ)cnX¡ j°M÷ÚÕ±ˆ5`7ž±u\ÊG ËZˆ†Á½Êu‚V&æ(M׋k]–2ç5#rQkL£ ÷{:�æR£Ã� ø×–:óÞÈéÜZ­B]¸b+Øí¥Ñô¡È;àÙL× Fç £Š¡æI¿Xg%ß Æ\b¥Þç\Þ–ÛÆ—ñ| Á(µ™çC®‡Í9+=Ù^ÛYf&ëaaê£d¼u‚èþõô‹ù/È›t!4»‹ ” ÿ _1wþÑ]Åâë7úºª*\ M£þ8{%h#ñ8è£[Ê HÀ5ù}N®Z¦a�g¿9'œ#“Ecoþ·‚X•51œ�J'»ƒ`¦9Gì E¸îê ô5¬c˜QJ*Áu7JÔ¼å·P^[H¸X'Ïμ³Ð%kö´Sˆ&w0DZªŽa<Å�5ÒÖ1Ô9Nµ2Ø;k¬öZ„ü)«®Ø~NÛÜË]xrÖ¤Ã�oj½öŸ³–¦d´åørû\y½¥ç*ä{½ìO MJçUÌÎ1˜Žz¢Ñ< ¡N8„2,1)T„.•yƒÅ°�ß𖹪G‹ª5ˆä[±nò8•hðx¨âL–½"u`¤%NwMk,ïc¾Ø9{x{Öæwìï;»hx{žû_Ìÿ[jBñþ‰º]�;ŽÇÈ]Pé߬q^#u;á@úô**°¬Zä™­Å“ÎkMÚ5àÅ2O„±¬N±[l„ZÓ„k¡Q£É¡ÒàGò:޽±LñR˜‚Ë]Ž#‘D÷Âï2´itzõšÝ#ò5¤×° qM±ÿ0Õb˜ƒ==Ô:u©ßZ“þ÷…Ô±Eº&𧬑ÁîA…ÈV[ÎA¬ïd•ÛßYLÛ<åãpÄÙçÑJï)§\‚c8-~™ePͽKPF\H¦‘i†³>¡ÞQŠ'Ï ¢üðø›ýoÚú!ßL±;ÊúóÿÃ}Áþr_%@¡È¹¾Ì÷ЗZz5§ó 7Oo:�¿Aou¶lC-bÀ0×#°˜˜ÂûÊ……ê5 >]›¨#Žça"NÃÂî0ô4(MÏ*‰šã*æ@í“¢¸) ¨ ÑkìaÕ­s@ýTJºÁ’óôÝ‚K.î:± ì`˜zÜ{åÕq-²ÞxÊUºV1p̃2%Oªõƒ]bËwåZJ/OmÜÚÀ�ç-‡Ï2�¤¹Ì3ô Ó¢šÝVÌܳü8è ,�^Цƒ†‡}pèà:½a�eŽ¥Æ9–Ê¿ÿƒþ�ýC�æ tÝ㌜ˆþ?�ü¯gÿç‘·(Ú˜? P·@(*.~0&·º×ÛÔ#øË© ,ì1ŸÏÕfBÔ¤©Q‹•¶Ü˜NãÆÊuZ’XÂÆuiЉÐÓ‘)×\�b—¶Rh~¤e¦Š+ÅUgy1s–†t®O= í”õfèßTG^ºðÎDCž�•ž–`u)Âñí´VÙŽfѪM=`ø’)p‚«KE5°»H Û@ùznü®:G–G6|Ð`¥!­µ?†a $}PYcôƒ¶ã(%êU®Ä–=G]Û‹XgìéiÆý¹ýñý^7ûIáá¶üï*Fè;Ja÷ï¨ÎÊŠé.ýK¨‰/G‚(´KÚ’Lɶì”’Z¿÷±[£<®æ™ï{(15/»ŠË²¬uRZÕ6.«TLkSIJê¨j7ÄÀœ ¹B¥XtiÉŒ¬D"ËŽVKÆÚT»”µÜqdµÕ)2vÊZá,~s» 79ä•<ÉokòKq£lÅ£3PŠòžên«:ÌHÏiæÅ±‘B1¸cHˆuÀ¨�_àæHg½Ë*‹ü9ÈêB•¥|ÁÉ&ê5Y˜ÿDi’¥`h¯M¦˜,š*\¬]0:‰ØNNHI÷Ém©¢ß¾ÌñçPb"ZMc¯PÝÐTûŽ—Ür¤–5ÙÖª´¹êØNÔ0�9¯W‰±ã*+µÊqQõÜ/8=ç"�°·ÿQš£Ön`¿Ñó* Ù1pŽIÜ�r€¦ð¸³�m+*7·[Äú–¡ŒõQ4Ñ«ßß²9RW á +,Ò.½w?ãú“¿ÿG_#È.‰â¯)'ÿiÀ½ÔghV)fÎAT+DŽ'!#¬�Lrµdn&Ý*_Þ9($vkOÛfd"B,¨mW1i´�a©BmIõ¢ÕôÒÒ¥S1I‘œ,€RTQ­UYHÍjKj¨®)¬-VNjæANzOYhDÄ7/kƒÕhÍ7™�†È�áÇmø™xJ†ãàç-5O\öëé§šbD\W ƒsá3ÃÉËCÞÖÃÖåT¿;.ªšÊTåø]LÈIA¸7_´{bËÀÍH0xÙðˆ‘|ÅbVKÀQ?t� øÞRJÖ » vc\›�o\œ³iäÖAx�È7�3÷úþñûS³mÏP5È�1âþ�]°çÈQ~´ö«¼#¤Ãñ1ð-‚•F~Ó–f“µ0úè ‹³(ºJu~Aõæúã;žÉAr²ž˜æZ) ã½Ø+ØÙ$UûöþÝü]òã*ògv¹ �ýh×ÿßâ/kn<þÇš§ÆBFü:u”ª#u7ëüå+20 ¸z—?žû²2 �y[¤´”LÈoX›ÛFóZ0kq¸åÕºô’;ŒÎ4q+iµìSÉÔ’¼N}ïÁ�Ü”¶:&� {V�ÿfª lïÁ¿™ŠzÌ¥÷ uxIA+_ÀҜ˶gé?ÒüQW¨6çclFã0(%‰ø2îÒ*{Èm.{^ãÆŸã‡Ï3„ttê'ÚU;0@8×PBÛŸÏ)$™-™ƒM¨7Œã¹Sj¹›ÿ“ù—s™’÷8øØ©&’ýE™Èï¼Í3êîeƒçäHšéB².æv�¢·Ž#„„�µ’¨¢ãÿ¡ÐÆÝ"Ìt÷Š·/ )BðfP£Ò{B1³w¡¦EëaÏ=êM×mhÞ™ì=j&ƒ•VH-bü¡ÇbÛ~}+§U¨m*\Ĩå&[�AgéÇÙ»åg@‡öÖÅîâW}|HÝÂ*ñŒ»ßî°ì";lõz U"?H2³Ÿ´Ì9/-cž )§ë{3‰1l¿ÊrY BÑJâ弩ú�S¼ßt�éÀI2 ƒBaèh’¤yJìÞi×SÒz>¥‰:œÖ˜ïzcærzJäó«ÑŽÕ~€N°‰ƒëžp„XîËkLc™ý=h̦wf?áøä¹FÅó •ƒn3@œÅ¤oeÙœéõ:CÄU(ã3¢³éÝß‚-AZËE³Êm¶0˶º�Yz Xw~0ocbGÌÚ>|y×üç�þ÷büw£ÙLeÚÿ_ QDþCZA âíHdïV¡nPPP$¤ ï­>I6·�Ð7ï™â¢7ó³�\»Z ûž›Ô¦½ 9N¡òIŠõÉA N)êÄÚ J´ƒ-ŽKYj{ïÛÞ4)8Ýi^ªÂÖ‡¦VWï°t¦rúA_#ø éáÁ =úqËA;-;A-ËÍßÀ;“œÕ—HR…0å̇`҈pã-UfÅôrªy?7ž¿5&„qÑ~÷«zC;v…%,{�©Ì€$î<{ÀÛ�q,®1¼ ”Iø¥�9šï¿þDþÔý�NóØè:*å ü³û[ÿcvŠrmóúÿ‡äoíA£qr7½B}ó‚ý…k–·ç@ä~o4RÒÂRÑäªHGfq-¤rÑœ:†]p–K%ì²"@å}ý2vZ’èCt ŒeêšÀøÑÈÝß WOêk8¿Ñ9�ØØözë¢Ù¨S¾²&®°…�ý|cÞ‘¬%ÕXnmXwžÅ}P®·„Q;„x¹bç†õ<«yܳúŽ|žcXhù`š®?m€a«¾>M2"Ý©2ÞFþöëa¿õ/•»‹�S4oMÄøGåxåÿk³¢Ú‹ÅêpýKª¸þ&Œo#~r´XÝ͸¿ #@¦Ao×¹^î/•«u,Á ÝÒ£dÑK> ^ëÂ÷"/e^ªãè6*\XÔ»�}¹¸™Á�)ðP´¨ìtU‹3:6éhãD÷ˆ¼ƒöZ"ÂÁ.ûìÊ+=.uöì ïeèmª¼ë8ȱ½DÒ.:?�6 GÒ­åD¼L©¯ª~ž­ÔÃ&}Ž56¿Ÿž-Únoøú™$]Sã~ÁØoŸë�{`Sš­cŒ†”¹¨T� ¥g·!ÞpÎAz×…3d÷Ãù~z×ý™ôòÐóêþúü¤.ôpOž‘w‹"’×-HÍ¢½@DH¨;y„¨‘x\›F~^¯ 0àåâ'J™‰¹ÀµN]¦ŠÜ’ë.Ö®5i«Ð–{:¶±"cÒ²QéLc ð®Õ*v�trð9mBÚ$i‰ðªN¹$+ $w*…©ÜÝ”>E:À·0ƒO.ܱ�A�ŽV~¡E¬p°ŒýA¡Ý•–îIêfü‚ylþ¿(~ o1jý5[¸¦ýu;²èÝaìpR† Þ‘Áôk^kbN'r=Gmî S†t‡ï]­ç u÷ö¬!U*¢�A쥛†ÛÛïó~þþ³þúª×ûûßå®Ð$Aûœ¿¨ï?£:m‹ìÔ©ª�*kW„W…KõŠyiìõ6‡ëï¹R„QZц{<÷Ÿ2ˆ1t‹æœ•jG{nbj¹J‹š–C-�DÛ«fç0&™S¤ØÇ¬BAñDU²Ë*¢¨Ôm÷Ò¤6§Tº®»Ó˜r3ñå�»~ =CF+¯Vñ°qúLÄ{æIº:Í .nKdH»�•â]æ\ ©ª«WUÆ[-›Æ¯ Ý�¼Q.�â ½¡<Û3õ?Ûû1\%º%pÌ…�K\\9‚àAè‰@Ö3ë䦬‡“D“d:âäÿq)ÌH“\g0…`Ú#%ω†Üé‘ô¶$KBoìů;øXüE¥ÍÓì^©Ɇb>ºû;n©(3$*ütz”F‡÷óíESÉ'·8å…èv�ãþToŽx)BíÏ&6Ú°ŲÞ‚VÓǬÚ”}6Ë釱;bŒ šgX6`²M°}óB}ž±&@öõu¤•ŒRê±f!ûAþÝÉä^h_ÿò<þ�»ö^ÛÏu˜‰? õ⟹,¬ïóÿÊ`\bqÇ$lU ˆF&BFµy·ç+VFêmÌÏ÷–�€„ ×�¯:—k2 j*ºÖ©I!BN›±c�›ÖX¯eʹÍu©Y¹ŒÊ5,÷Ï# íN)n†=bÆ´èk1µKÅêbJÓµã)‡´Ù/A�`ª1—-wå Ã;-<Ôb›Ò•¥Ü[mõ®-Êac—̆g¸¶Ãðùó>ÊçÃßNÞŒ>ž7—ŽDàÙ”+ÃçS†p¥vç-C8G—Yû+. tµf}6ý¿ÎÚSgk{Ñ€�©Þš©Œ*Cà’Àù�U°Æô†Q&m“_µ�;îßæÿè›orïöG $£ý¿Š W�Zò¶£j¡›��€Aã߇àlµ¸‘xLðë¸FýgW+BÀ!‹]×¾yÊ:…wS¬øtÐbo«ÍZ'AÖ ™:… b]ó´÷2FyK+e›Uó™�£V]Í9”ÖYº÷ˆ‘¿\Ý*DØ•²ŸnÜÐåW×0Ï^PB™j,ƒÚ0lzN³+®q­ÊAŠ!û°n$Ç<çàà"£¼ûrz¹–mÊç¯ äØ÷X›0�ùýÔ§$#-e–õŒ0Ø�$ñ ô ËËzÄÃg�~ãN¸ÈO¶ð†Û¿U‡÷;öçw’/€L£CV&οæ�[þ¿OL«�P¤�ß°(%E¥@Ë„ù½nÍÎæâáɾ±B‚Ä‚ c–½K&‚ {¯[ð�–ÕºTa�¥Ë|nòÒ‹.mV•z‘áæ BL­cœº–[‘éy—ZªpujUnƒµ©:\û Î�÷G /ÆÞÚfH¿ÝØæ›¥é±xªEJZªWÇa�³ÚÊæy Èö„l�÷#[)†�­2¼WêCòr•]K=Î6x˜×c&L.a’ð£!È�z¾Ï ¼ÙºáèÆ2œ!#Úñ!é�v_—Þ@Li]0‚¼1âOî ¨Œ�iG@&Ù陃 ·‹~ŸŽ–½¢ßÆí1C�—óí·wËkè8ÂÐTôt‚”ÿoÓì3ŠÓªV}ƒ5¶R,~L¹>,ÔPz'¶ÁòóuI ¾-rï`¶ç~V&.¢´8Í‘Øv�i�MW¹5E­Ë0ÛJ­Æ,éI+ARôÓÂ8JŽ]ª)õ¦f!h+Œ:0É5i½¶Í¶U ‘õ\7âáìyð¥ÈÈ­an*<Þs9~ VwÌ»"£Æzª*kÖ4%©(ŽÎ<æá@V$õ€“+Wé¡A­YŸŒçÏ”Jó¯[âmKL¯ô}ùˆW”Èœy@¤´wU±†æõMÐü´�g=Ý`xïR6ã eX"-šnk“û)%ç0BŠcxÏpâ Œ±ah½|<ei³! /À*è“[ÄT‰ÅvŠ~拵ßx³DÝ|÷ßÝWžúé$úжU )Œ ¦$k�MˆÐûD-ÖyRÛ:íG®ˆ"~U.•¨ô»kˆac‚¦& 5Û_ÿ‚Ë.™ŒRˆ�áž1>QÍ´ÙÈv‘�v–KÑMvÖd<:®­él•¢ª‰ˆØÙ(%¥�ÓL3ØsõD4‡šÊTsAiEÑ\�ñ^ŠÄ ûú ü(ò%¶³à€ŒÁ¸}y×Ë@о׎°\V²¹mšû-e‘†ø—›aAp)"\f8½œã}m v˜»[ žÿSU“•¾‡ %ÈŒIF«[&^>.^gPË#3‰ Î�˜õï æzŽ7KœZh‚cx••¡ ¦ët�GÞ¼’Õ­»dy¹0pÓXpêü@ï²öŸ7(ì¶èL1I9™kò¢0ÅcÓ¢"�£ MRƒÚ lïp¼aÍ–až‰ë÷ßÖï³ýó“ÛøßžŒ÷ö¸e"¿%Ϻ¿ý,Ê0?ßÛXˆÖËÇ™F`cv�—¿5T ðk¢Ë˜&&ó|«f$Ÿ»"Ã)�æ[˜¶!d^ÏŸâ­lt´(éÕåÕ¬×â·¢>�¸½!ßÿk“e¥ÿ[dùB &Õù³[ÑQQþ«á U¦,™mƒ,©„îÛ{¹k¨êà,í�Ï3ߤÇLj†Y³Zê1ÇJQ!s¼4%¨cDŠ U3GÜÞBÓ{ÒQ7�2�Â:R¥”b R Ñ\Uz*¡¤&™chòul(ºÓ[èUÇså̵ëRLe8jÆVRËÔUUèÜk\‘7õÕÆ°X{úâ`�Êi΢C¨�Ñ~têתçÁ’Ó¼hv[¨$5|^®;�ðû­:ˆk¹ :�'8U 8çŸÄî"ˆk“='b2ÊM6ç7“x—)v}‰eœ0¹yƒrŸþ…¸ýͰ$Äð§]¦&ç�FàÚ"‘±«=¡q»@÷Þ"<û²BXµ¹Z? ÌU¼_�‘½3íC?‰˜)pd&-ãÔ]À†³ÔÖ|6H~=ÙÑíUË;z”ô#ã§��ßq?ì#È®¦kX ¨ÿ�fÿLA½Ýí"ª¨ôO¿ä¿Æ‡^‹³©ñŸ¬ (àí:ßÛ¦fasÑK\Œ+!/b©¦E’_ËŽ@ñØú ¨ÓàtÏH‰F:oÈ'G-eÒRzvcY›Ýr áœ+é…ZÿÖ¡â)}¨ä5¸…„ÃuöÔ”O]¾F˜r)ݽˆ]•Š]QŠŠ¥«t¯s˜SÁÄÞYEѢƄ›(wb>¦Ÿ—YôõçqÌßË 0Ýzi(§Í×Òî´…è“'1î2)Ó�tdïßÿv¿wÿ¬—>.Å·fÌgÿGðfÿMhŽÞS‹y†q`“o*Òá‡ã)$ è¨ËñLj¹ÞL¸uÝøÏŸµî½õfî{g] ŽvÙµ…nœZN6Ǭ‹’V©Dèf9NõL<²�w˜?[?t�õ u×RpÎGS¥ë¤yFyN*¶>Ó:é©gš8ňŸ†Û‘m£_‡eSÔzcØwëY%³ÔùÃóP·§"x¶³[¦•Å|¿p‰=7´OÃ�ßQÞôï}¹ •-ÿjè€KÆaw IÁZúMà­BÓEÞdúV¢êß2œ¢�å ¶ æ}„šãÂÓÄüäxÆ,GE¤Å rQz÷|¡ ŸÈpÏï+q@·Ícƒ”ýÍ?+% ÆÒĹT6-`{N‰‡~>3ƒ�û„‘ã)Ñ�ì®J.Í\Ë<–KFc»G· DJ²ÿ„X�IŒü©:¯t\ˆ·�öUM"©×ï-†äO•£vÍýÑqV| r-Â8ÃÔÆ8WÉÝrqªòé5âìÓåG`°À摉@™¡Vb³2̾«©¢^g[õôû§î]ÿ]}ư>€é)ã¿ïÞƒRÀŒ(¡‡¾yGw¥¤“2L‘(–èPt ‚þä�í°·$Âdª/jާBíáOÃëTˆi¹*qâ&¥°¢Ä“Z1…í3¼‹Öç#1©NÁs Å]‹â£lóSÔõà”ÝbG˜à˜‹ÅÎÜ)Põ”>¦òÚA²Ã:qç·~C.ZáL»�KÂåa{àìJÒ”u×)ZÅPÝ8Ô‡@¯¬¡¦”˜lÛLÙ¿…ös/o Z¾.G„ÿ@Ü2¥ƒ*…¹T<¿Ä•ßäá¦9äÕ¡ØTšê-ñ)ë ž­zt~Å¿|£Ëúu#Ø'£þ³‘þwÕFä•Kô~£Õ¿)Œ0=(ð $©+•SZ, ½Ï7g´‰Jo0¸*)c° Í7˜£cÍc}º¬5w=+¼~Â릣X®†Ì‘‚á /6ÉÄ*–÷w„|–7õö^âj­»XFíŽQ�|³Ió°6LÎažËtMD:zõífƒšku˜1 �dŠRG�3eŽLʆ‰‡dó4'XFßXk5�˜”J_ÏŸ‡ Oíõ ¨œ˜^æìaß¶É’¯ö]¦áñ)ì=C/\ÄO4�Lã !™'uƒ¿¸äÝø~‚ßû"l€ÿ‰x±ó‚ÿ”RÐ_9Æn©±6äò¿¤¿Û`IŽ!½:w“NÉ^¾Ýà÷otù?ý¨ˆlcØKa=øµä×5ødÌñnå°Ó5›W†Wj4IX0"ÉY`×›DšNɯRÍO7A�!,1t1'ºáë/©Ð>J¿Àî9%C}ß­ÃÅ/òL'+Ëwôçë fçbæb¤›—¤\SÒIÛÈ@ã"£ BÛ‘*×r â�&ªo«yv\ô/ÜÜÍï긙„;ȬË¡h…õH‡Î*õç°!~D¦"9iœ‰ŒaAöýBÂô·\÷SFÚ;Ch{º=ýÑïÂWË´•NDù ;NP äoV~å¥EÍ$»¼sYbš˜Š•æ$­Œ‰%[%Á4±³_�Ö@A¹¦ö£‘Ëîc>²jQ¡ËàÀf–E?_ Ëɉ uæívÕ3!Y*Öê]®ˆUZóLšnª›ÈªÏ Ú;eVåHÓçúÂe‘ÊŸþ} ¹Ò�QýlEË3Á7ä,¥m­r�#LXó Ã¦OmÎA”ë±.\Š?�ÕµÓÛè}„¤" =¨+¤ÿ†wô"9À�j£íÿKÕá^!Ôc›.s(И§cûø;ÚåÉ´5õ¿Äÿ6âDÝsƒ´ÃˆÂ(´™”@D¹Z„Ç_@XDI…Œ(Pú|sŸ�¾ ã Ú‰€È´íÇmÈÊðr#‹­„ØlÀ‰-pàóTQ£JÂ:œ;ÎjŸú!µGáÂ÷`{ÓÁ°Àf¡ìnQ¢¿•u.éÿs­§]x`n:gyœ³;w?`–"7{×Þ`´X-ê¹g\­dàëI´Bh¤Pg¬ÈÔ¡ïtֱ궻,E¤2J9[娢.*óžÝwáÿ†™ç<„h?=¹› ÷•‹��ué^›Jsr‚ú7'gs÷¡@ýí.â"¤ô‹[Hï7¢o„§^í!cy4á&gˆ—À\†[7zƒ)žU†Ì¢éw]ÎÆRbB8Çöƒr 3Œòfþäì‹ñÇKË{à,JQ°¼n"¶�›aþmÓ+4sýÏw¾`ˆhàjþ¿Óÿ�Wd?],q3"— EÐNØ@c”“Eî–Â)úæ¥4 í X�‘(vž—ÁBÄvÑuÜT MVy‚aµª´RÇÚ匳HpŽÞ’²ò&}ljÔ�¤J©Õ5m¬¹ªBÉ*"³’úàȾw JÇp ?ôöêBIÓ¾1ž��)&n(¢“ÒT© 𞢠%{镇ü�>LRgý LPw8çÞìÒ×bÀ­jÛÄGTYÔÊG“¯�1äòtÁ7À™y±“žp-¾÷-Ë@�D9$‹5s¥¤Ü©™Ã\ÿErÏrÉîÞh´8M1�Ñ¿;Ãë!·u|´â¦8Ü3.vÚ!�¿e�ÅIxßkÑ|¡S&ßÝÿ騰\(óß­�„š$B˜ûOvT�˜c_Aˆ€.@d©©8úGŽ–­­ ·Ä§wNKÀò¥ÜýxqtDÅmdE’gi+BѨ½3Jl…âËÊj•Ø®JV3û³ qNcý\£B×jÏÍvÑhÏ¥È2«n¼íæƒP:ì0£]n8+�ß@r3ÊÁØüö®ãƒÌâà¦L—ÍÎ.½ˆb[¨ÎÏ|¦Kxpdâ7¶R«ªrJ9ÂK ÷’™üºC= »—d²æÒsñæxicƒgÇùÁ.Ƭ "nN®ýZõú*¨1½6ò€2Æ+£@’\Ÿ¨Ä=D @ ÚRoò8a-â—@³Ì�¸é CåæU7›cˆþ³GK€T¦EN®DYˆ1îíä®j×' ÖYØæñs"#6Ò†�)ò9 *Ùè$2¥È[�| ýÜxLc,&Meé²×¦¿ÎÏ×û 2ëň}&ó_‹*qÇÿå„w_ÔbÚ»‘²ÈæÉÜ„ EBE�Ü—P"‡ê ¨ãq6æÃ�×™7tô‹¬�_³¯’qZÀÆBk-hª¤žô¡IMwZT(×Ûa–ºî5…¼Æ´™ž+|À{+Aj²ûÖ„™-]Ø.´µ(Üg ‹Ý`�ÞCé6B!à|¤~Aðƒ× õGïÏgJ•Ï(>”Â�1õ�DÎÂÛ}µ¢¢ÕQ ²læ_ý$5æ¶îտe}áá#¸¬4”Œ•‡†A6-ØL!€Õ=[&ò�ñ>%&º›ùZ¤* “žÔ(à>¢M8@¯ ÆFj}‹³†Ø¯qÂ1Òl)+ƒš´EÎ1VˆZ¹ŒÝ‰j}èfábΠˆáÖ ¹�K7‡œ?¶ð'JQÀÁ�ÓÍŠw†q†©Z& Ó;Ö¹–ožCº7Áú3qb¸;Kw¨8=¼é"=šø�ö—ðÞbÏ­-+�ýX|}˜#D»ZÁ³vsº_¢•¢6~s1óÛKMó?¹¹¦úx¦~p# n+b2’†#½i K¼–¿�%ï÷ØPÿØf~¶qòÕÈ˃ùÖqx"(>!næ×ªʦch‹Ämá�6¹ñ‹½g&ˆŒZ«†RŒ\~ïû3=âVÌ"Š%±xäô&I[c3"ŒßÿAqÞÎØÊöµè…¢�¢m Ë’&¼`|Œº2$©Íõù r³ºÒö¾”Ì2k˜5îÖ Žw„b”醠 z{3Î� œ~d›«¡•¾„*Û§:ÈSûÍ è¨nb¾Âþèæx¯�K+mÔ7u_}ý 3ónåHžZ�q¸ (<ƒVq@ûI  S!/¿UJKKðÎß3Ö­Û֯輇Ø4‰Ô]§ÿóóç¤Z>C}é&÷8 >è4P3×ÝÓhì+iÀ"†œC„¢ªT!5Ƴ§¦šåtT7¦ˆªš‰:ÚTµŒ(Õ³ÖdÎm§²àª¨ªd¨T^$Ô“^§î¹ŸÍÎL ‘S]ªìºÎ¹L­¹-öR¶ÎmbÝZ"g`>2÷¼)¨¸ž»Õ&„Q¬Õì tÛ–0ôöø”ˆquzÿû(õ2jŸÆÞØfüvú ôw¤šÈ8çŒ�›cO@³ÏéùûE0þ9bÓ�hõ›i€cÑÁ°aå/°ž3ó?£áb{ú²L¿’ð^÷Âëù3~^¾0=‘�HüO�€ O]é<ÌüâŵæªGydS­å˜Ï‘Äšù5㟳;Ù¶AÏá݉ñì1û<òdp8Ý[¤×tè¤?8çuŽØÁ/¿âRÿvä3¼r!„ yÊ2æ�šÃ÷pÍŽÆ<ƒy‚‘€}h{{cZð{y3 ¡&QQÉ"-cb“KãD‹†s‰ÖFŽ '}ƒ§9Ðd÷:�“ͬƒšP�t£Ý$Áó¤„æÕ ¾·ñŒ‡kO&ZlÖZÖ.d~˜áÞ:> ¥² gAIZR‰bÖ®ößë…LSÖÏýr=qœœ¶¦Võ·o5¾¼åcB;×Þç>x¥9ǯËù­o€ó—ªîi¼[’diëm�¿eããßÿ9Øo¦á“†”$K[J' Û›â7/­ÖuÁS_•ß× µ Qn4rÿŒÛß  â¬ã¼~Ì ªv—qMˆA×Zõª­ F—ñ©eߊY{Ã_ Ut «©YE-iܦT9îVðâ9·�Û¶Ö¾vm2æ)Ÿïp¢&ˆwÀ‹�]àê8÷ünŽòþí%Ÿ_UùúP¦¥ŽšÜw/+õæïܽûÇ4n—tÈËSk,nÝÇÂï…3`=�+dï‡eÍ ‚�ü˜�éÅ gd�™ë O+Yí94®Æ8 ÷ YÄZú 0ø¥ðœ¥-1ó®#OK!¦KãP7 e)Xˆ¼µG^¨ü`ñ9X4�ÇJV/hYCO+™ºò!æ}éÊ�ˆœ ÄÈð$N‘“z$$ÿtâ\B-mj47)+`!Hƒ™’ÊÊyÌi,†ìUë�‰4ÙX b¾eÞ¼Ø|º¤Í¡£a£�°FÙQPiå¾H½…T½™°lf>¶W{—‚|.!α²ŠÌbVq„ÇÙ—Ã!ES#ÔD¡ŽãòD9¦÷¹Ï¡Î‘©ù v&¹�­ZéêWûx½û>ÿǃ`±,!W4Ó�’ÉÔÓÆ¡~¥8×Ë`‹iÀÕìõU%UÁw¿?‘‘~ì4"ž/‡s5‘Nyz¹vPv~Ú–y¹ÇæéTø‹™žb¾û,˜½ì�„ŠÌä„ýåI´dK�µx}{ÒëÓ†L­kEšÞuiÆÞ÷Sÿ `Þ°ÂbæG«þãí»_ÔvÆb‘Ë>(ªªÜ D„�#>qô‘¢Fâ/¡U?yÐ600xŸÏ{½ðq" 7‹-½‚[ìh×m$Õ¯_xÒÊšLž‹¬¤‡�+A‰<Àl¦š®ª‹'Cœ+¦ë[]–Z»Æš–µ¶z[hÑ[:ž0ˆt{bC ò‹®” ðâ%ÎU4‹�P»(]|7ÑXwιçUÐO7—ý\®�h ¤Y³.Á,F€vzdY¤LMÈžôši¥ÊW¹`þã d@ãÄ~ÍLo¡Ôƒùûñ(� †I9‘£,¶ŸqÅ.�1C�Bý�šÆ@püì0‰áÖ;W¡{Bè=#Q7bX8æ� Ñ©8}³†² îÖS²;B.ÁO•ú¾uM±Ï²ßA× ó¾ 1@øèïõ—9l¡9$_ÕØ<›º¹�¼ßœõ€~!5¡%!ýëž¥í€ @_sŠ^‰³æaDQè}‚r.Ƴ¡3Bíï9Knª¯z73 iÐŒa¼3Zg •"m\öN¦¸­cÖŽ¹Ö!ùŽªrWð,MGÉH[iÖ°‹YÈy�«ÉOE8Îì¯jW8a:~'�Y òû·‡ûâ‚V¨ñ¸†#í•$)•´Ý›Ê}ד׉8œO‘Ãþ¢Àþml/ð`ºo°ÉÚ”.KÙ]dçšÛFç-R³¥ƒ÷ÑF“‘"·p–[IU­µIµŠnQEV¥t­c¤gE|Ûxge’u¼éùV8V öv‡Ìóñõrê„’9¸èX¦ËÖïVdcª¹ö˜1\ÎC[¸Ë¡óÖŸPu…-âà †"#ÕÔ8èÎAsr\ÖÀ|-�€«ó&ª^°RµêW,Ú‰ÍÀÓF±€‡ÁÓ·Ó�‡»qà8‡7¥ôíŰ™2¹aR_€]ÂW�2C�*ã/Ȇ€flísq×Òï¼ÉâÍ–¹žöæÕõ9o˜dãæÓ®oô+ˆµnE›=á6P¼�¥€LþHÖ;KéÄL�©]Ðt&@Íì1ÙpX+WçO곩— ©¬{ˆdwèLg�c–ƒŒ?[§aÖh%›Û&mI•“VˆNO²qÞV¬¤W'’¢8kºO‘‹<\Öø¿/�ù?Ê>È¥ÿºIY:~ùÿÜ�%p+f¹D&!hªyÕ“½‘4fk¤Þ¶lÿÉk\^¨ë ‹¼ýõªD䦬zØ3@zæªxf­©¦t”CÕªCÑ©4Z¤1Õ¹QLŒ PíÕ§!kª—ÖBuíʨ[oEjY:¥C·n­³P3àEÖëú€ÌOÞÑÆÓãlùÛÐC–Y4—5³Eóç¯lÔHSÆüÔÐÞÉ/¤E·\¸à”R½šG?_p$W) Aüd‹Â+lR°{ÁÚÕÞdaM†@ã!DŽM!·`�6°ÖZ^0ÂL�wœNy놀Åeì˜Z­› ƒè7�ŒLÓO'X¯!#�ÅfûÅ26Sƒ»ZÒ+âÛáñ×Ëá´ÙÇåÓë8·aµþ_æbP[2â•„nP[{Íà¡ÐUÇIe»¤M¾ nxÝ‚¥0�Ø"÷N’Š ¯Åx´t÷BëºC'–�¦}tcCÙh5¬iPýUpºÊ9½ž^Ù»Æ�ÜaÕ»ŒQÁk*ôÙ@¸'øÏ'êpxI�=™°ŒpOR9º¦X)¹‰ Žf=\3PüZˆv{X!ì¼WmcŸ‚ÿÔaG\ï1è}-T?_¿eÉêÈYÁÂý+œˆ²9ŒÇ¿ñ n;ÍG¢Ðq±ûÍw �|^“G/Þˆt¼Î£ø\¬Í§oõ s�“‡qtteL_`ýLCp_õäÓ"ˆºa�.À›Ü=yeÄjþ-,ØeGå9?‚É£g¡ü¸7œg�c”Õü9—)v9½ˆã£Ÿ¥á#vS©½Ç[;wïûcžÖXe©€-÷_Yíþ'ïüFÝaba´Ùl:-¢€r±�¶ÈŒÉQ*_úôMÁ_p"¼“ØÃÅàE|&}�±-WÑM®FU+Ãs]µ”T©Ö;R`;ŠÉ³Ñ´Vš5ÈèT4^vÕp£ ÑT­‰|¬XŽRÎÉ §×Ò;\Ö„z8º¿¸]¬2*Ýê ÂÕ/L²%ÿK…Àò¦t†:L3�K~¢Vº®SuDG>BÜäȃ6ÍÅ„Þärq˜¹—àA„ìÉLdsrQ�†Ïã›%ÉŸµÐ„˜Â„\´^Ûî€òŒÝ\'½ˆ~¦ôËL'ÞÕ¹Ùq&D2çaøàØFiÇÁ<øv“ 4 ~ñÞÓÆvǸÿ~¿§Gè%Í^I@ûw”Pð_�é>Gu¯M²d’Er8… Ï QnÓʪÙBiì/×òx3ÒN£mW *¸ˆD¬e;QÂ(‚T+^¿™´ž™±{”ÓõH]È5Îå{V,"Æ� ?v²ýÌ m45×UêiWÖ=LM�­PжyhØÛY†Ïä›V’ηêÏÇ$X”0pÿ åªy¶v’O||0ðâ.Â{£`ƒ~æšÅf¹Àá1C:�>Ò•Þ#È%É5ì¢ó-ÒñPhߨdÿä�¸ƒ;K§wÿó-ê _ùDïEsw!7šB�Ÿ[¶‘­pO�aÊÉ�àZ){µxV¹¢�¤ãÀ†ËKbE¯½_‚ÉŽã&‰¼¢=Ö¬Š4¯ �2“ ˆÉLšÆù@Í\„îÜ/‚åŠ,´ÇÙ†™SCëVªhØý—‡å/é¤,[�×lÓ½Æ8Ë�‹Â²ýâ�Ô¤ôÔßËG¶€·ô#½b!¤ê:m@Ó>)Š@÷Ä+È*;ó©z03,ö�c)û£”èßuHñ´¤Â—¿Üó¯ “£ÚäýÃ%?ýï#OÏ9õ:îÞLQ‘_ÞŽ‚ó¹.ñØ;’i™4Lþ÷~j¸Q]+ų3 jL£ÆÁ,–– A©ÂÖI®x¸àŽƒ™ÒÀçàņ-#{Ó'>FØAmq a+ÔÉ1ÖÜ«˜zZ¡çZ�ÛïÛ»?WåZ]*Ý×㸷½êج;°D«–Sµ¿ø…½�ÿ|ÚG¼ÍŒ^ðò mCÚÀ„ëäí@¾ý�Ð^Åó™³ E&¶Í5æ{"aìG/ÓÅ×Õyõ×'êó[Lú�qÿî"ˆxç} ¥§HLA•À&�qòKg7L¢’T=ÜûÀ%È0gáééGH(2lžW<çL�ž“`Å'ç²Æ5» ÆpqP£õ+]°”²h†C¡00.rÆ`Œ3viNk^ž,Bõ¿2×?Ü]ŸÙ?LŲyèô¯?É&T8aÁœB*`+H• î­ky•û Õ¸­]^¢í}†:u.!ê)Ùê£éqŸtkí´Ï¡éÕAz\†nƒ• ž¤M[V|é‹òMU*r9Ì…;pïŠùõô8ºæ"ÿ;hÌ-÷×y ÎùEÎ-Ä?ÛÁ[ròÞ�hÛÕ�âFvðc’žRUÉÞN$T}Ð’nAʲg¤Äs®:R :Нn°ææŒÝ YAyNÊ}‚KN.ªEk…"³'Ч@–Orô“oÿ}œÿ€³=Ë�áGÛüHRÅKÛHßͺ8ÂR–«ªeDZ¾qãºÖY‰¿˜§ˆ¹ñM¥ÏÀE*Á‹ÊèÏä7ì˜Þt©"×ïùÃŽŸÄÍ “¸„ÿ¾2ÎZ1¢ˆ½zFuÓý;ÎÄDP”feCíîŸìY,H†‘ÿº¥¾m‹ Œo~;h‚qK1ƒ˜[èJYô~.UÛ,°½Ï=%Wšùw’2Ìg!xäåBŒ$œøcѳ¯ÇýbMÌPAë`©®s\ÿ!Y}#Ÿû3îoæpåwŸ¶�—²÷ª¨ýFÒûË­ßxdYédT0¨{R/¬g!Ý&Ây^uOùýŽBØá~Íis êžq¦ƒj§y”gÐ= n£Ê�Ï«ÎýJÊPã Iõ¶ùÁ§é„€4�û7�ÿ¿œûJË%±÷[ÒG("*)ÏÀØþލ!Ö¼£�  SöiV¦~æ 2)h³•UÙ$:ŒèWBlì—´V�âéN ù#Z­ò`ÃïÀ–œ¢Çâ8›$¥ç(’¢‚Z}©Pr©Øº &öŽÚýËïñÎTž&z÷‚ËÚ74»:D))ëûOùˆD«G­o!«ûCøkò–$ 8íºž‡oR‡�Ò‘úÿâïe@¾Š3Öül„ùkNgž4Sιõå}öú=Ûôœÿ¢ïßÔÎ{·‹uŸmìBˆ¨ ¤(+}/»&¡6âmÀ'÷š€øË1eæbÄ@}dâXê0`ªH“$Žsƒûï�®«‡ÅþõeùH�R�)œëžà —eÇÒ?Vs–0ÈEœo?…6œ»„ê3Rk—¡>gZŒáÕ”8];JÃ.sëŠ �í/U,r­b’ûwvçúVج6þ«ÂqŒÿ3P•o”BFÐ’xûÛË6áŸwRG˜åÔ mOr#N^²Ë×y|-gÄe#]Zè°±€Æsásv2ÿ?²þA16ZcÛ¶mÛ6:¶mÛ¶m³cÛN:¶mvt;ÿ>wÏ™™Gø°\«JXžíïi¾¹ÊÍj„ùL�á%®^ »šœ›Ò#· /Ïx›ûˆG?4ÏŸèÍf;¯×€5„Ùfk¥­ìØýê›§nÌ]1?Ù®Ù$ˆoæý&È¢‡^Ä'ŽƒæmÛ£9 بü>¢UK°ýÇPX“JPüÕ •ghm—:ïÔЩn™÷�¥?Ù�[GÏ7zKä÷tö ÿ…­¸~ÛA‘ïaª• 4}†ÒÊÂ\ÏÓÞÇ®PRÁB M?=êið&§5\‹'×RT¢ü]ñÉAsy9$(öUIØeŽóg±èmC3šd¬|¨E}…\§šœ?ùÇB™ãT±Ñ,º3ÀƒÍ¸NðÚTÛ»¼ä•·J××OnáÛ‹‰Ê7޽¹Ïõ ÅËj� òÞÈN†V�òáû†ëBü—´ºÇ!úê„ av¿�¶ø¬› §-ÀÑi(K…C¤­ÅO{Nס˜ž=z/!¶¼½¾¼�ž�Ó›ìƒÿãÞ�ûI¡`D_8CXµXT3É,ÅÂQ¡Sí�F]L©J!¥”`»yÿôöaOêéÉÏÖ0µ:$K=Ñ5ŠzáR’•Dxš%¬&`Ƽ T$¯ç“oq/ʵ‡[öuSñ‚²bÌæô5ë8°kSÙÞf"«s¤¼?}üþk»OZëØêË#XWÍä¹$5´)® ›ÚÎúE.2©³‘¥îžÑ €¼ûª¾2QŸôâvœ‰Œ³¬râ´)Go?˜x쌶ÿ‰‡ç=ZÏûµ%à œãcÝ™õÿËW탰?…ˆÇ`ɽ1<$† oî+¿z­�¦YL¥ºî�ìêlì­Ò¿:”Ú˜\ž!Ä&·�Ê^‰«rL’asO•imR¬ÏÛ�Ñ ?Òγ&âˆâ~&½± ¹Â¬»× Ê5¸õ|„×# ‹\S×›–楰ne¦³g{—°Ñ‘º}–G¬V¹+þÍL”{“Oât&NG¨äÇÛj4ÒuñB¥0ìLå:k}Ò­u,µXÀˆ¿ð/wbßQµ£üÁB¥Äü/C \Ñz4BGâ—"Í.ç o«ZƒÎðÙ\/ù?ïqm_7ãxÍÃ( Qƒ=ÈA' ë‰9‡© áeä"’ æp�|‚¹ ëho$_�-†�Ü<<ò(ŽãÒ�=œÂ?ÆÔŸ%ÿGo·Ý¸¶>£çìËÌX8[ʶžåäWâõŸ–uŒ6›4Êa�c?©µuÝ ½÷'q¸»¤•[�f’P«xõù�­ÑÉç;°pùìf±Z¡®QS͵“ϯ;Ð~Të_?}"ÁúΟÁGlpyf“֢ſD>D‡œÝa=œ­vfµ—”¤6^ÚøýÈkV2Âr+¹±ZÛâiù¼Òñ&sYU¤œ{­MͶDE‹Ñ¨8>ŸÖÆ&…:§Âµ’ø²c†µ³Öuư"E­’µñ�Ú.%,Îbè¬ýlÇPÝùó{bHI„°AG÷¶¿Jgeª;Rí„wðžÅ=åOî²'k ^þ[éL€*â7™Õ}Æ 1Pfzµ‚@ÝëúÎØl×µwŸrUãàçs7ž_ä‰U•‘õÁÓ@qμi?æœ�bC�Õ™ÎeƒÄ$Ët/Ò“Få<¹ûh[àoa \Ç7sΛ…|tÅó’.UÉÁØ=ŽÏOE@Óýÿ´3äÚå†.?Q7ŠÐ¯þŽ BAF€A�Çt‹ês;fïò›61­Y¨‚›-z9#ò&íné7‘-­ƒ®É3áBÊZQµÄb<õ1[„ùÔ׌��<}1‰Ù�Ð+ßmÏ” 0—ÅÐßþj´¶Wî­ëÄ/¤“ j(ÙØ± p–OµÏF¨½õ¿'-)a}3‹ÅÂ…ë%=çY}v¡mäkþÝ·˜ˆ¯kð¹ëJ!Y‘vD+Í‘‚!ÿpô÷æf‰É47ç¿Jî¹͉{‘6¨ƒÍ „ …}£Ùçm­�¹E¿ð^Ú¦3æÍcm©Ã ˆ‘Na�’ÁFЇ-À�´c •và=•r½W+2Ê[i(L`”®Q^Ôœh“o‰F$Hñ+Wk¦Ûû¢tª¶öÉ«íµzZßñë w3wêu®ýï$£Ån%`¯ùê.°¾”‰(…ªô =j Ð:–ha£o`×µï]sžY‰´ÑÕ­×úK`­ªâ‘yøsð¶!8iO‚÷æ¾álù¿º!Þ@èŽmÎsþûpŒ?‡ðê5”Fès½\„”†„ǹ±� \¥XCyãEº([œÁñ�NŠš?Žß”‰N³Â-Ko¾ëgt${Ê`«:ìºñq6Ê Š¨ž&.b4DZÈ|ïÃ>._ \ié·Ò|q�®j|¶‘çgÀ]X·LÓrëâlØnSŒûW€¯›×8j­ }G»Ÿ ì¾ï„{Š�´#ш–ªÙ'²]5¦tln�Az/Ñ”¨–lO(AÒǼ1å½øáWÑ‹–Y•&þ‡“™žYü ”jG@…°’Yy`„ êÅ7@]}#ºäj¸�«rR;¹² ¾HìmCÛ[k91Ê�µãU¿«­2NÚ«©¶ðšišÕ¦(Ê;O#[–͆è¸ùòšT˜ÒjDTóä–œ]T W²µTY:öï)Ñ9JúÁ_c�†¸½b‰¼¡¬M¸#eæOuÈ|Øtø„ªï;lÖ[B^<N„þ:°PÎû%ª˜¬©¸< ꥊ0âî#<2ƒ^ZLc® ¢¤Ù;ÁFíž‚u’Û‚Ó»ÚиéŸR b1æ ³cV…‘TŸ…rço4í¥XMÑh8w1 ¿êÇtÖãÛ4ÄÍÕ�ƒ(Ím©ò�×SCZ“Ã#ÕY@5I1’d$ |&:È?�õv@;?š}àäöãÙ%§C 𥲯r«™Á‰ÇºÉLw»zmwG™2î³Ñè¥m ×»óšÛÕôÚÀ±%¿B³rfÀ0›Üî{øŠB¶+~’qÒ¢†C"·bb‚öÒŸNO…º×Û8ù‚]¼Ë^1~zâúiHܯ•dÐPè–Êä~nLdz¢ƒ�¿™öq3iAìÜÉù¦o'p¹×Ào}ôöÒÝ�/bX%ÿg~ÿ«z©Hômº_)Tý«>9úÊ¡a}(å‚ÈÑ‹h Þó/Ú�W»i~uwìö¨ä'¡åÄ…8UáÑ1fR$5«.·H$�GLE<ì #÷�ä¹±©™«EJÈÖ»—Í­Ôä¿Z¼‚zgwµçìŒ|)ݽâãÂ�Ÿ?pÁ¶×_ Û=^J ñ¢1yû-ÉŒ–‹÷SJdG¶¹˜o·Ý¨¿‘} ½”™¥gy™Spu7ˆ®Lm™dH‹³&;4ó¡epçe]�ÙJ7›åg:Лõ?\¤í&ê²pñ»••ëZF/�0xA¨·ÆÒy=À’‹)žô}á;;üqÊ ªT.LËåRR´q1¤*7ËSB U(MHY ÿñˆpñ¡yèR9ÙÌ=×d�øåÁÚbÙ8f2¿<ªÉ[– ä½b´íM‡È\ó×Ùk|jW‚ÏŠ2‹_�ZcIÕ‡k°Sû¸ÝÜGGê5/¾’fÖDq›pÿ7#‰Ea|Ÿ£°ÁóöœR\å}¦aMñ9<8,¿6ëü»50yúÇT¤ú?ƒÖ6\d±‘ÜÖÇCæ½Ò…+SŸ9'Ý—iM5ɲÄq¸Z‹‹hî-_î`­#kp¿ ¬éAbŸÈ‹w;à„Q6BkðÜa‘Ec_#�ýrÄ�¬à§ï¿vnr^A)ê ®’É~Ô–¦mPÄ^x†·ä >‚tbÄj–4œ‹×Xt\”uv¹!õ17LJ-˜¶fªo¼O„2ÃóÕºÐU#HÇåà;ŠëJ– ÷�TGU7ž4��ÙöEø‹4i±½Ldˆ3•–¤Zt¬4ÇKveëPðeíåÂú™;¡? r‰¼óè:ßgKh`Úw¡q#ß^�™(”«¢ìº³Ïv³e#¾àX\h©×t/­}H¯wÀ. ÄÞö‘ŠMHÒ‹ž¼V¯ruýzºýq f0JEBtìAÒxŒÓæsÍ#õ{Z— Ö£ÎnÇÅŧÿ¦Doû?YøpE·žå‚õ¹5‘«”Ḹ,¡¡ãìÀ´F6„�ˆ0±¥¥Šw¶BÊo줗ØzêãøpxÝÂcÔ¶\F® ÀØ—Â@Ý,+¤¾s ó†°2ŠO ¤EºÔÂŒqw¾·`´X¶¿¿|ëão ŽÇ'Òý˜·ÝæO³ä+r¸½!�[pA±Ž€A©Ø!ÄÁ UÚÑC\„4Ñ1ß_ꥱL¢´!Ý.TXL¤Y  }âB¬%±�îå×­Z¤Ë/5Ö?šå¥h^l…äØS�©ncêè‘VóËšj¢Ê‡å¬ãšÍ‰'1K°ƒ”x9aø Ù{U¿Qv“ôäó䋯P`gLLg½(¤tSTg:áøEs™ÏBû‡$¾Á+¨f²>{x…WÑ‘AúaA„ÝêY<1$r«£GÖ`?„ìbxAú€±&Wƒ@U&h¤Ãá¼=$½ÂÅ�Œ¼Œ mQÎXZí¥<ÏÀêáckýí™=y`Ú–8§2èk)™ž�•ü”e|É´6’ä|=¢|Á�’©(;V^sÿ‚Ž'nlDaaSœAÖ²åÕ•üa'Ö÷GèÆ9ÙàUÔèÀ_½ÙÁÃÏÕ;СØ[=klu[[/} â5Y£þ• ãì²J¥#…‰ìFûæ}BâË.myÒ´ñvÐû öŒm÷¶™�óPÞê_±ìöÙŠ¾–jÒœWZœLcqB -‹¥•(#éç£z«À•l{ã3ºÕ_âê^jÓÎVŠÔx \§ØTÈÊ´Hµ92® æ ¹®ÉXÓYY3GÃÏÊ.qíÚE]aPŠB” Íõ0;iÌûxg™ó�<<¯w²ñbéÀ>³ ‡}tNA½œl2èj’œ(c–…æîÌ•=^eøׂäöµnMTË·¬"Ó‡[Ó×%\öyaá羆ï™^…WYf·Ø �ï,–äÒFx¸ÛOh)�Ç’9¥éxrÖ´ LÕ~x8W$‹JQ—0ô[�,‡õW¡ËÁìz3Ã(þÕ©úvºaÛÖ·WÂNO¯�òfrT"Õ–ÿ‡� ×Õ„\®CPõÔhÄ"Q­¹a;�.÷ªœôÛ“L‹V1¤CÖÊKzFf„>FÍ…ù¨ƒî.¥(ǘ©yJâ©®«šg8&ó0Ô±e}ª5u‰U-ØåUíÍQ¹fYNÿò#÷VsÍrP_‰å`é+È;Ó{ºïÞq|�‘ÿÅ{³Ôѳ»~ YŽ­k‡råÅ_06%iÊ6Ïûf¥ÐNsfƒŸçÒd”=¬3¶é­–9вN1 wrì‘n+ÂR—qDš Ì*3 朘€ÝùÈs‘Û·Û§ƒö=ࣄ~-*?ͺpËŽâ!œ_S´óÕ´ˆgayÓh=2=]�Ž™g£ûÌ·ˆÚrÞK‡jôû!Á€3â~p˜lŒ„“"ú%˜DßñFJ¥Ûé©CÈ9°ˆÓßoPÛ1=ÐN°ù” ºŽ¿Npºîž†)«;‡†ŠîÜŸýÂm}üû&:\J”z5ä§ìoG%\“²7¢åñ¿Þgí¾ÿÖ¼ÂB™Åsÿ€¨ô¬6P†wž(é|¤~¼Ú]&3eÎ'»$Ã,,\ÕŒ-¨ÒZJ‹5f™äùª;@co öéOvœäî𦂇A8ЩÒ&yë U©)@Î0R»G¤§f”O4ÆKÖ(-;…™úGŽþ‘R5™øÝ!RÉ0+…j¤v<–­è¤[³'l„{éqò†Øó+�nËÂ…u+QÂÐL–Äš¥Ç;˜d:Šcü ØÞiTfµvqÐŽMŒë¯Eú¦&@:V½…ž(ήƒ'®*Ú™òlU0‹¦Û?&¹–Â�Å� |ë‰Ö•Ÿ¥YDVè¤Ðbc�‘/[W$mƳ ,³\«P#Úwí|ÝÁMÛ1·æžGk令2]|ó…cc��¥l‡ ‰à £ |g5ë͸,æzä]2ŒbM¿?ÎÇnëÖNZ³Pòûµ&}jy“ÉþíMe¾EÁ„â 9«ið7–œÓ] o ¹B—‚ôsàŒ~VIIJ}r9rV  X4ïÜVb�Srí �3˜�~“¼Øú.©ìLËûçÊçâÕµƒ“›×ç£ÇïΞ $£"QäÀŒ¿ Ý™8Ç%Cê´ñÓ·œ»îûÀ& €æ\Ðg8\¶ž_Ñ£sy=¹4ßM €Û«ú®¥Æ«ÕWæ¥É}/,vîÀÉhä/åAzÕóüòàÑ�í+³KÞ)šK£Qßß‚«ôj|3iÿÍ',ÿkòø oÈn’�7!H �*�.¸ixJ‰�K0‘úé->sé\Ûs”™ÕòhЬõä±p:kRšqPÖ(@ÄB�·¤Ë4¦PtïqØSçà{“‹ÏkÉv§$!+I²›o3[u—ÀܘÒÖQ*yŒîìÕq/—º~zƒÑq…hÇâ á$¬±Ì¨MØGÒ%QÞ&Ñ’Šr`p•Š6‡|µÕêÚe¯r<£¢xqp‚›è¤&S¿Ò–lClÓÝ"NX^Ûûr±ÃZÇ’nR2»B®HئuíÈÆ}~w®±‹_™„%Òÿ;¬’ íÆ¿zëÕë¢?Vh"ñ EqÐ>…V“Cl#;µ½)\¡XP‰õþ]¡1¨°»‰Ü@�ù73p©ˆˆ„K–GsOysa&±^¡î}eE šÐª¤ëп’k7œ9& ‰• º0ƒ€ðXÂ[Wµ„¬}z_ì,Ñ O_?JgýõW´ öÀÙí+\HñD‡Ðgžß@Q�ý· g$z6óYçL± šèD–¾qôv?íBÿ²h1t]¥»†‰n†‡õ•0AA3È�Ä2–@ÝèÛ×Ãñ‰oóVmn¾0&�¿¦#e «ŠÅ¥M ž¹Wfl"w òs‚hÉ|(ÊäàhØÖWë9Š\Mm:ĈäMa„Å镱’n©"D+Ê/ü;L¼¹%ÀN¶E’ç‚Ãq#Æ œt¡[(Ÿ®tc=¼½Î^Í_zuäIÐéöü'œqvpdWqy6lƒ†P>täe,3ÑûkÂ0÷ëU¥ñ Fðp u‡‚-§pvRßÕÌÈ_t$/ú^¬ñ#œ[øè¿�€šæô…†‚om-¶N¯¼Kß O?Ða{³€‹ŸOXùüéêúþ&Ñtuy÷Êþ¡žþ�¦!Ô�¸‚u(ö`õ‡41w×ZOÚZÜèñ*B“ lÈù[ò»ìÆ–N$_lïƒ&EÌj´;M§®ùF�r˜V*Æ·/UöÅ4ANÿ LƒRtÐyÛž.\ª§-iÚ2×tMs‘YÛÔ¨ö.¨Ñ02ωªëQ×}{®¡\Ìäô½+¼¶Ä8÷<ïfS—MÆ$c¨G)ü8úTi›¦S �òY³u»£áѪòk`&qi¨0‘_g±´?†åÐ.»=êEXgèÆ�Õ»Òçòèîܪ žX-¯Ž÷oàÞþÞXÓKd�•,ÂÇVøoã¢çB¦�cøÉtG7EAIžö11ùô™X˹DƒeäeEÝÕi¢ìpǨ%B¿Á«ÆŠWi‘½eó¢_myÙEï¼ÈEµUÕŒ» -‰EeÝ¡Þh¥Õ…¿ÙgÃ@ ‹KšãL;¦@.®Ü^V<öÖ_÷D�3¾Ï¦ËÚÍOKUæ�%4êiE|LÊæÏ’I��–šIŒ×�R!DÞ $(Ÿ¸™Gí.½ácäâçÜ5sí ÇÓ0}BäB‚þ^º½óoir}/N\íÆã¹Ãµ[ûŒ¿®ì މ"ºÛ”OŸ§ Æ:Dƹ»öî¥tZõ ¼ÖCŸ¥eMSõAmÂù�ÚY/–ˆì3 Nx3ëÈd]�èÑý!À¯SQaSÞ„\ïŸ!ø³«9?§?]½¾üœ|¿‡Kˆ/}é|´(žÜçWš;ï÷¾| ?ÔKÏá‰Qê³X~”7þ(þ ÕÉHÝF)‘M£fN-X«é%‚×¢šËµ>'õˋ̦„Á\>õÈî‡=÷4"ß¼“UV¹F¥Õ’ÉòÂQU�àX'¦²¿cbÂ…z8ü SÕhž¥vt9QI}«Ð¬š…š¬R]~~o« ‘mxÃã=H¸‹ÙÃ% ˜ûyôìÈ{¯2¿kM¥Ã…Ë'¤›lòEh· KÓÈ3ì<˜oD�]Åcà?«;‰�bVŒÎ®f‘ŸU«ñGtº#Ð…èY»&‘Òyá ’M¼ UFpƒ—à—YÂ<Õ#T Aì¼<ÔÝ0PY´¦Ø'RSˆ×VbÊ ×®JÒFB¡8q è‚^�À€ÐÔTÉBPøÌ…,6€w�?€$šDM$æoŒ!9Øá¡ŠØ÷ÚVÑÜýGwµy´þ¾b�:C—nŽÏ: \‰ ¸CöBv'mI¬(¦¹ÈVñÌ{ƒ»“ã$½¾ÒŒIBRƒzS?fìÇ�Fì=�BÇãœò°+_œ6®gVÅpLäï„™¼¼pçIêÆd幞㭿 áz5§èê¸_kƒÁZnZ£†�¨†änh+†\tró¶{âü ¾[ƒöÑ'Õqo~ôk⥥A‚oÞPcÊR\FIÐMîòw‡Aë¤å~cgKŽ�]÷²óÁCíWHžÖUv’¦–_du Š.²‘žÈj�À¶˜èœh>·#uT;/"eÍ%Åø‹È’SW:Rr6ҸǦ”ß ­ ‰ª¯Hü¢è·�õ:ÙÃõosIÊ~Y9EÈ(�_“i·öí Œ:,LÁIÕ¶ŸIÆG&öhk\Gk{ Ù¥É&Ö騘òq´QUgr`r ¹gQ_ï�YÂÔ¶€}d‚Ë€ÈÝ¿´ñ?%¸†D�;Gü»Viž ½õKbE¬èðsü]3²WÖò«ž¬ «é·Ãûˆ­¸”žÕ¥éÏ4e?ý2ZöÊY5 áÔâ!%Ú¦„}qbµ›pcÜ;±¨Œ©DÔìiMC'@Ôv'â5Û­þ@4pµüÊä‘ÃÈêÈ%ÍYÊÅ»ªÇ»ßo¦iÝHaÛ˲3¼?ÃYÐÝí²t<��‹¼"(,,Ž~w³ìêÄfâ À£g@,(‘'ÏÉ:TSˆÎÑSDX☩nõêlvAy$ Wã!Hè20å%,ò°Lo…~qìÀÆe‰8 ÂÌ‹ßx9WÝ}N'@um*]}NrÚʶ+ÏÔÆâ]‡K²íÜøx•ñšžV{µi>ô¥]4NVÚ»ŒŠÕ·*Ç?ûäA]Ó䋪_®E.j:9\w�à± +õ‡n¡#Í‚{Äldæ¿\7©š` "ømmº¿ …º¦;‡š&kL>¿?{÷ô²†É-=Ÿ2ð¾Ô†Ôܵ(ƒº”-“¦³ZRgDTáÛ«àñLÂnI¤ãD>^‹TºáêNú=ßy»áþ'JñD¤/…™Úå-û:jÐÕ"Ò5D4Vã)ÖBâdw`Ъd3PJ.” C=”•iïiê5%‡íáÄ/vaÅãÌÜ[Byª·š×}As¥°j$CdOÉl•R»viívé4F‚qÅq�8Ocœƒñð²Ñ¶µ€­Škr*—ÙWþÈæˆÓÅáx #SòÎAyRî}Ý•ø­ Ö#Ä‚Hào‹/ͳ˜3 BœÂ¡`\€Û¡û'tLÕúدã,€5!ÒœUÞׇûÔË� ™øæ’‰oë­¥O ‡³�xfƒÑ:H „ ŽBW–Ó�œ³À;­ÊáÃŒ—¨Z¶ó×}}wïìêáåõ=•Êeüì©ÏLÿ,m‡îùÔI‹re¿e$¹ì ¥y‘pZ€Ë2‘P.tº}h´#¨‡ÁÏ@Ë׉ðÈp$0g`dÇÖC–€�ÆF¯¦8žàý¡(T¿(#¯‹ù= +¡¶À6ÀÁÅ€’°×ž'u4›Ž¥ûª<¾ôZë[óù‚(œ¯r¿;iïÛ} DEÚ’9d'À §”éU6qãTÿ´ÖªÆ´Cx5Y&Æ¿"Ô¿^ã=‚·x/v¦ì•ý^âæ^çTVe5¢{lTZOÉh»yôüݱ³§Gô (+dsÿŸNjƒå¿_2§«×`"sKxFâ`ÝÅB™Ïб•B›˜] ýzãDR¡¬2ˆ—¡–`œŸ)<ןiД‰isƒ0Ÿà:£I“©°é½·Ô÷h‰“-BnÁ½^黿Í@}CS“œ@&öZ¢ã€Î¯îv’Ý6ÐãÝ|àVë>S1¸Ik®°Žñ§Î ¯$QI?÷d �ƒ”ê’š'%¼ùÄkvÔû·Î>»TÉãùãùPÄzÊÒ†Ç ¾7Ü]>Bj0í1É�UI© œ÷–Û7ÿö—‹Œ7�µ¤½ræ–?ûŸ È­Wî€[ÓÄÛ Ýbp 0zì-ˆí~#D¦Šd¥…�<øz Ÿoä>5 ÀR#õêh¸Ê·øåø(\÷JšÆGÙŒAø)¨Ð¨r°2õåeŸÛñL Z8«ØlAÓë�ëÔ˶ïŠ�ˮ㠺æµÎA?Glð¶)»!ìüçrf·þ~A(a³Z^ì’Þzäý³ûl!ƒÊ[•Â:Vr¤¼{ÃÎ þÂŽNå,ä•ô&®>N�rÓô}paÝ6…(ìZ\j({Ú&k³ôåÃEÔ}ÖR¸NÙÎ;×W¿Ï�Ûº‰}äK·‡ÄÿPf-� Àn;åGƒEÕ·¬…(Î Òð6À)¸b#ŒÕÑY›<Þ×ùËð¤2G>v³2#ôÙ%èGÑðš/ŽN%°“uŸÖߺÖFê‡�#QaMøá³ÍëÕžd©,&·Ô, ÀYðÓÛ—�&n­� mo“‰P9ë®^q �XÊV ÎkÓ¼º•„iI7ú9ÿ+žntÈ”wÁiþOñäâ»û È¾Â>*Wð÷Qê¦`©1LfÒ8`šEPV¡²¢Šµ+ÄÓv‡ ¥Q“ùI.a“Ì£·¡õóÛê;ÔOÕš×u>KùßyUþ[í~W‡iEkT\(î¤Å¯Ú–  ˆsbm ¯éoˆº²ÿò‘,GC#t”dÊx9žÎrøBŸ*-']jÊ×+5 š+ì¥Õ7„j&¥RL"Ò²Ž¥j¥DäÙ„ª EUU7<‹45U«&Y'þ6ªV7ÔÒ9SkC_ô{¼†ëônsJêþ½»2íHi/œYÓîèúv…=ª¬�¬üecå®–,JX|Á…û/”6O ƒ¹_5Ãôk¸G8Ä^ðÌEo  «Ž·¡ a#üõ÷ºÏU5ŸŒpF€©}“bºÖ´17k(FZßž(ÏAݯ �eW+š1–&‡@,J=!ÉÇçªgž ‘Û,Ò3Ÿ¯¤ùã¦ë@®­«xˆ;»Î3¼½!#•€Ò„#D×~âéâfî9±÷ ÖNì;§Æu“A C$é-ÁjBAËxÇ ¼ˆ«3G¼•ÍØ9L¬+Up5}8ktÉëñ‚mK9tb\À:xã¢uÓÑËy1Π³{ÒR`§Eë²^Š¿–d/"9^6–¢?›) -¿ãu¥¡\Vsý÷fù$jQp8›…Ýÿ8XØ?t·ã×1Ï”k1.xrr,KÀ»ðä"åŒÖÁqª¸f®Oˆ<`àEøÎ^ô—±ÎÅB –´Œ´Ú‚¡‰%z|ˆzìÛíÑç;L$ŒŠ“¯ŸêúCjùQ"=ü饴Á«îèV-6(]ºýÌ-M®ý€£·ô G„hî‰ÓHQcv©dÜyî$Ó¯px8ã›Ñß|#_Än1çp62Ч‰E—±CÄl맇‹qfUk�ÓÉ^ ^žë8fHH½3Êl´qÇ•Þ"dßLé#뜳*‰ªwÁS}UsXË|× §×æåFÍéæ¿ÆÈ»[‹é´7tY¾¿¿? ?VøÅ?@Â9†8óþ+ÿ?d\æê¿[ØŸ !”çüÇÐ_“ªõ¯Æ\AÞ¼RDS,®7 cgš„@ŠL˜+ú©��5ëÜãMÈŠRŒÊæá‰‰£’¨“±NÇÿú#Û^úóY¢²])NÄÊôc§"6“s·ÑݧžÓÝf#«®¦Zm|ŽÌ‰k:ž _ÏÞÀ3H˜T/Hmîqšdh½½ËÜ. R·#A´<9ë¸ú­�8¶U§.H@âf¢äÓ¿T\€Û@À’ò¹†hŒµ#±TÖ@5äÁG¦epûµ÷ê|~ïÉvìc·¿{fÑü3Ó¶[?RóUj1´ÑwòEåRi5*Í–�€£ëlÚ€´/z|’¶E4B¦a€½�àâ��»­ÏÑ@¥jÖsÙCák.EUWp Õ• míwÇf0ÆHÍÛl*Õà*+3k¯dÉÏ¥¤‚¹J°P¿µ)‡íe‹ào±ÓÂÑ_¯qEÛàG/÷(UJ †;•ÞszHÏkëHë¼^‡ô×é£d#€xbÛ8_5èò!¯Þ$ðr�ÎÏ•Âå (ÜÓièÜ .k»–Üü.Ù%P�Ià# ªá …Ììg€0ß™×_›bk¡1%ˆÉê!Ù¢õ·ôø�2]üáÌ9Ë /ís¼Û6mš<ƒqÔmHÜnÃvÓ¢ _�a]K7¼×ëß\Øòs2p·Í)÷²€ñe\óJø gã¿÷ÙñK‹À;Ÿ^4áNÒ!ö+uÏí“ =: :ax�ßÂâÖ”CK·¾&—­Íù®Ç4Ü:\ãH�ôNNÃþÞøe.oݸ½ç½ug.+³V �ÖùâÔ ¾óôx‰¿pt3�ŒñÓ{û_M·T‚ä$¿ëQtQŠ&‚{�‹'®†À«17ÅäÖgE“Ì JÓÇéÌêEk™*IÛ˜s‡¦%‡ø¢KÏ?“p²;EÈèÕÙfñëMÅÖ7ñÚeë© sq‚žX:�2ÀÈî9ò%è¥�7·ºLòõUVß™–Þ|9z5r–Â"aߣ¬\º ÒùÂȈ:×b´\<~GÑ#Féñ«xû%‘ÿI¥ÆÂba¥ÒCD:[§�MÏ1æ“žË %Âhœ?}ù8Ï4 'jR í¿ó¡mÅtÔ±Ô ÛZP±€+͇éèñÜÍ'Ðu1;÷¼¿v|Ù}ÌEQÏæ/–&Qÿ̨úm7`º/¨Ý­–’®H¤"kO]'á&çrÁP0Ì#¤šÖ’ '¡ï_Šr`@]+á�ÞDøºÉ�E@ä¼l.&N-/¢çk­¥d·±´7ljØ3³.¦+€½£Â*Ü€Šn·7©¢l:Ó"0]ñ_â÷Ó_QÖÑ<¥øJá4üWAùÂ9¿„- öŽY»™¼=AÖHa7ã<Õ)tö�[¸Áœé+GßþPó6æްå¡ZŽÙÉ•È £¯ÁÀ1³Q´ç~û¬MK™¡GÇ ÍÅÃø‹˜Kå� :¥^0ôe= U¤Ùë;ºÍ¿w{9|ï¼Ö"Võá£þ„c¥m PDOª÷múÐÀ¬ó# �ç$ÉkQo=†HZüËóþ¬Äp�Sy9—x9[‰�Ô”ŒÔä4©<™c¼¤j´µð W‚×úì$MkSClAö¦žrÁ‹Qn1ΖeCñ»?jW LÕÈQ^ØÄBêN ­<Úd2êLÄMÚE9ëÊdk¥¥;GÃwE3›—ç'_^!Y Ãó©¹[íáʺB„5ní=¦ÙLœt 8ö—v \Ý_òb”ÿ¢Z±rýˆ.ÙïÐRòç,i êί*!5Ž>AõI2¿\YÜù¾Áû+í  �⑈ù“xðÚ3 „xåÒ0²é{5G¶HF²D A~0ƒ±ù'Ø �–Ź¥PýQ{x4=^ôâ$ lˆ^L•üó¤ò‹›¬ÞiÌk÷dÉSl>C.^G|“i,Õ“8¦¾t¯I=um楒Ñ|¢¡X“n#öIß q;Öî6ñÙš¿k Lw×»»=�î ò¤¤ÅÀ'4ôn  š±Ü\š’òšë$1TÒÏæVwüÜqÆ¢7jr§Zç CŒR `¬ª¥]B<4šr0xRמ:Õ˜.€ž[ñÄ;‡Î¹>8!°½p:"Þ�ÝYõ¶‡i§,ìâãÛ«è Ô†40&ýÇ÷Ëÿï^!4öuK`þØf‰èh£È°�ÕxüQéÛ;–m™Üž#�¿[WUÄB y\‚Ò.:‹y-¡åBv¶ Û†PE k©ºÚ<`ÈžØK‹ â™Ü‹ ‹o¼®}T†ÎE:t׆Eƒ›ûxGJNŽ_@kni¶fÚÆÓf[üÄ¿Ü/×ûøžeX³¢Ï¤©¤Ž¶+�6v·iêÝñk�-íÂvNr~U8hË´Ï–QÒ!¡w3•1Ú˜7Ô2”cšÔñR¼ž7ë ¬†‘|„î.sŒ�3Ø´ ÿæVïUÁöxȧ2�ôÇÉÚmüð§Wv¢�e5«+¦(—<'–¿�GÛ¨À Õ¯_öÒ_ˆY¦(3áÛOQF@p¼ t`W,’€ž* ½ÐÂFVéV NÊalÿ†EJJ+Ü *vPZdH\h©“$çÈi³‘|&g¶ Mk鵟vÊcTm2“÷ÓÙø¥id.cM×Û+HmÌט‚Ôso95äâR¢¸Z˜‹íN(ºËhçuûx�•~œ³KÒÙ¢û¡¹êöÆY ¿<]›Ï_›~�^>•õ�òIÙÆ`6â RNO3£ÍS*|±ŒŠì™ßJ«8Ã:n¯“­qm.ˆ<5eÐí'K÷ë×›4°qÎoPÁ;`ÁРù2ÇaxG_TK‹Ø·ˆ<wŠ{¥;n¥2±&ÂŒ‹¹ü™w³åº¹¼}EŠPbˆ_†W âšQviQO¿_b\Pð;°Uù¸XòÏe'Únþìp�¾ä8˜h=GHÌ«vZÌeû§Rh…Ë´'n:Ûâ{0™ÔÂJzwÅÉ3Šad Æž“ ¦ˆüùµCõ‰9ZpV¬D›x‰Jìêßm³îËzÙÌ UCŠsÔn5òUŒ ×̈jë‡è»Ÿx ´¬øîžƒ·#­ƒ© ¤Î¥²1 ¼$[êD½�Ç7~ 7¹ïX 1ËÜÃôQ¾Á`øÞ¤›5ªU#”] V¦öG‡v�B~XÀû 7;€–3À`+òäÜ»áql1ðs|hm[x�‰»Û#: »lÔ ,šnhðÏ+YIC²ZÚ¤ÝBìÇú³¯´1»@š¿0˜<²§= %1v4X�Ú  [00þ7$)Y{r×�Yïõ%–O®H.d6ñô!@ÁÔÝ�íÙÃ9ãBš äÝ%™Ì2ÞèäÖèÍÿkv8ûÑýxMÒy§¬Æß¿(^Òܬ"þ lW’߯ëîçðx!Mú¬þ„dð;°tgÃÚÁ,ïÅ;^�Û„B”û¢ÉŽC2UBÆ\jD²ÇŠ%JÙÒí»ãL”Á¦9Ϋž»NÍ*Š‘B ÉT‘ªÑå'�d\ƒÚ¦QHÌ3 IAüšà/�gGüÖQ¼Õ›M]tŠå 6åÖ<µ„‘È tî\'·‹£É6X¼\8GÊнB«EÎPÜg�»B—UA1 F£0€ä`¢mšC šáe ñ]Ÿë*¬ÞáZ7 ²q»)FžËÕ’CÁìúñ{v6A^«ëFZ°Clâ¢ýcX\fä.Av²mÓ‡øk¸�Ž+‹¦ÅÚZ¹ck4”¯ÐÏÃçf�ê“à gÒD=��yEïÀSf÷r'"¶›P³ô*dÿ¬*Šˆµ5SR@ž±›àÄžÕyRŒëpàÚUØ(Ò)­^´£­ÐÛÍêoX†5†ÕÍ”÷”Mæf“JUê ÖdD‡ÜúϹ=#8ÖÂz$ƒ‰É§û‚•ÏÃíB”SÎùäû—@F’€�W~Ÿ¼¡Ý]Ó®Kj,´Íq‚êdb½Ç7Uh.ÐNæcÐOþÍ9Ü¿ó¢ï2?\å·×…º„Ìatφ�â…`€0˜­(» 4&�¹ªwŒ…{cD¦”êt mtPE;}ì9ûì°c¯�†Éžt}h‘Da¦lï pª ⌯'jëÿÊÈäUS8ÙŠ‡*Ñ%rTA¬* ’Z§CÔŠqÁœÿ §hþ®-¤>D`?Òíã—£õ»q€IÌ$·Ô§»ïðfð¾Í�f=‹àgÊ«ü/–l}R³ :É=˜8ñÖêhìRxÉ7�Ì­»úx- a¤WŠ.œ�ñž•€F²Šk¤xÐÕ§”g/&-TÒ¸«®¤;Ù®"·ƒ�ÈŠZˆÈ  ™×W`¯… Ñzh<4Z'�`ÂU<æ6Äq ¹¡ Îlý%?Ê(Ýî”y³vï óÖ�ñœ”‚hÛSf8zß.lÁõårŸúgæ�ª<¹QOÙÌøÏÌ‚ÄíÕúýÃ"}lšÂçÏlW]¶n�&F‘ê+»ÿÊêmp(âôþ ãn£Ë’ßwu ²I®vòaZÌÞñï:j§ù¤ ¾ö™n`SÄ1�ZÑÉýdýŒ ÙÙ� 7p˜.·PðÌ©Ÿ†‰±ev\ ˜FZ�nÅèÛé¢ÜÛ:¶ßÃQÕF;ì)ÕèÙÿ)eÿŽBo>"Ïh;Rmºü«š¤JŽã%,ª¶²=5 xZ~1È5½=‘n”+9B a隺G†û IT+?•Z”Äç¬CÑisß–žÉl¥TZ’j9‹È+88få¬JSº‡)-›D —Wh*¶Tâµ�q,RËÈÍJâï±¹ËØcÊŸŸçUDâ�ìB#G>ía«°žÙ´¢†E1Ñ›ðhE×ç=&…é�~ˆá˜hÊ…U©ksäÀ¦UVsZK¥Z§u+¾#†`öã­zDì1Z••±ã9zOgÚñËr ÷Çc»o“D–›ÕûjP2®í®­9Q=µ\ëózÿ…:âlcÐ,Êá £{eìO*9ƒ�ïçûü¼þû¾µ÷÷Þýˆ|¡©èš<‹ÀîJiìÌ»1âþpÍ/ñê··$°©ÐC ¼Á0“ÔÙÊèðÅÜØDsQJZÑ×ãB!Ÿ/ æ)ß=Êá�-àÅîßÐ�Žˆ_‘ÇÇìŠoðˆQ>šA&�ÞĶ ] 7:N<$'‚ðŒÿ9Û9¤%ž^ä‚§ ¤…¾ï… }: ¹6¹ç±x‰ïÊŸUºÆ:¤vë¸ÊÏšõ~åõøË„¤A²€;Ñ#¨AÜ>·9éìO ’û¹Ä×Tq–g³?¢¹ØÄ"çàkÅþµ¡XPæhÞ@@ŸâÝ›ëèÞ9zÓVÊUÛ¯R~b˜ÇP3FîþpÉ40[�¹¤8Ü/Fw9D3NÎ%Fky±|M®š´«‹éQ¿áÞü¿�—ìY20(ûl‡Š‚N`t!‚HV§Âœ>§3à5òQdU9«Ø ëÁ!Àñ“œ‚ÐJYè³û�®1½œã]#ÅèСp`S�YlýO ›õ³[0ζ+ª¬.”/pƒî&³»(¹ôqN@qÎâ|¢Ì­ ƒôQjvOÑ«æºV_'™rÒIç^²Ƨdn-dóÖ-4Ò¼g%M¢¯¦v%jº—/¾œ½Þh¿ýºN}/¡§oê Ój>9^«!”�OêíÔ‚Ã[·ìŠVWË=ó³wNHÙìÝâ¦ÏØ…Sg,Âø9µpÿ E'G1¬¨%K #‰‰³‘‡œ yÙø‰ý‚®)¹w2ªkàó&[Q©lèƒ`ÙÃl$]ó鄽ÑåYÐ\Gvƒö)ÞZê{ÌØC)=^Tþ¼ãÞÀx)J£�†Û>·zùÅz•Ýå³Ðä.„T{�´Am¿'»¾15(8V·w/ÈÐ.[ŽØ0$±tdƒ.UN!ÖîÑ�nò[á4Êe'©æo¾�—w¿³ÚºKp+V—µ]HËs¤v€@�””Æ�ÆßñkÈ�”T¸˜cà %!˜‚)H$œÓI÷©çª ÷¼JG>˜�¬_¤û5±úOßHZù„åœÂ5.®Úšs…?ÍpãIòRüðLCÛ®�Œ%÷Nä;ûµ°ùÇWO.ç ËÈÆ¯iY|†IBY*hëYI½QÓ/@YÒÿznq²VÿþðégÇÒGKIôuÛ"XÈÖÑ/9˜\œ4›oßR0nUôõ…*qRäì6©Ó º¶;íÉußb[„nñ-\/nsëA™ÿ„�óuî} ÙéþÇýpªQ¡Ž9w¯fŠrÐ*[mê6Àaħ tf¯VûÔªÔZ9ëþ¦�‰b²h/bEßHsEʤmæs¯&Äm€)ûåJWÛ÷!/ !Ë·ìr˜sÕn#ÕPOPhšNºŽp©S©CÁ4Ú¾|¡Àä«T¢Vr›ýùýqü; ¯Ð”?�ȳæg}ú‡ta�!øÆ3²[ƒ?UUH’�ópI)mÛÄb…m)1u¡ÇG˜ÅsBÛ#=ŽALÒ¦Œ‚ƒnÌͦ(Pý´)m™( –ʰjš‚øM?Xa iý×}±Œ+�‘V±˜Š£�ò„}äÁ>“wG»Í üêt«iœò1áÊ8ù9Ê[lÚ½½aoÍÔv”©9ùk¤½’Puç5âË–'´çÞ/ð¹(Û‚ý-Ô¾\aÿÁ?þìm}î:«‰Óê¼Ñð)‘?·Ÿî;�ßE�x øÀ&öP×?,m°£Œ?€@´R^_2<Á°$ÎhWPsPÒj ¨ª*wý]H,O"¼sàÆ5FÏ`]e- ý€ufe2ŠÓàP4–Ô<À¨šOmm†c ÄV±rJfz„ÈWÙiPBDó9ð5r®?z᧯8�ŒÀõ~ÔË�»×cÚ†ië¶ ÿ6ÿì7Ê3ߨþ$ÞOG—7P¡ÐúüG0¬Ýpž~µzŤ…xTõO/9T6‡ ³Sz¥Ș—Y1Oý£p\M®‘t阯aP9«rê/ÿá§äF¬0SgáQŽ[óv“ƒyÏ΋&ѲÓ¹Yò�ÛÛÔBšúY-î¬e9¡HÖ²¿÷O@«×ö ®¸ƒÊ¿¥  ¨D™pf�cnuÃL>™ìönO\&Ê`둸b°*GªžÎqh(!x@ö³ñúåw€,ReL¤k3Ž›i¤pË”QÔIoªîž½¿/¾;s+ñÇœ=>~²§øi¹‹ŸÍ.ßȵ"1Éì¥iUP-4ºËù!ר­Óy¸ šis03O-vÞÄ�7_†šºášV¨Y&‡@?ú¶^qMý<)—wÄ̉ù©†Fíœ�/¡Åó‘K°ÖÍ¥7ÊÉŠþ‡à4ˆ8TD2éz=9ÒöÏhÔ2a@c¡² 8¶':à¥'{ˆû¿çB‘ÈúÖÑm gÙ¤8AÕñfkEJôÈ´�ªÂºØB(‘±8ÀÝÒBÔÈ‚#‰¨r¨Õµ2 yî`ÁÈ QÅð¨<õ hоØù8ÂÆ”ÌP¯H�PP|8èí¯=öžÀ±£¬~ꡉ֠«èä+Š)J³{ã6«ïÓm_oŸw~P~.öOø5ÂI)?’éŸnmÏe¸î²A áZмè¼rÀU£Áo> ’©t( û䆼"3ÌjE‡ÈÛgüTÄ’ˆö³—ŵEº'©8ñÛj³ùT=þIü‹”=Q‹!Iž`ë–=ýãœK9—E¨šKMrÎ^7Ž‚#EÁŠ6Ãf#,¢ïnâ-Œ^P|ƒ©¼� Khê.6næÕ qx ò»Š!|ž»‡L˜‰§r0z9üÖ«k¿°æÅÉVB\J>Ä|\ý�0¡þì5;A±!/âfñCá Ÿ1Cñ S\{²ØÍGB=ž”ž«'u ÷Xêîéýž¾ùŒ{ŸKÎl+¾Áz× �{}:UÑý%=nõ|J†@q!ç Ý,Ê_Ø?†ëˬJR舮¬öà3Ì*)U§µ‘lh4{ �rDˆº9ì·kg@´Z™õ4@¾šiþÞë†6ýÞG�Cç8ÝßÙ‚w°\¯‡Jt/6^âò¦è¥e3y;~}ß7\XïÀÊ9k‚iøîÙ9 ÿ©t½S?C(i•Hfƒ AÈáR½XÚÔ! #‰·Oìü–Ý‹å¨áÍÌ-Q¥´ŠÂd;j¨UH1N©.®|Í´ò�Pf­DeËD¢`Ím•’’\ÇÁ.¹ £w4·LBw©-¦­ �>÷ñWþç"»pvSÌß9ìâFóêì%bàoÛûÂà±_%0XúßWOø{g{±ñk{Û·PÓM¼ø:8ÐCz.¬¥{fD=4–ðGBæ-Ã…wÞã < ‚Á<—ZŸê°­z»AœÁ®µÊm¤+NBû¸„wúõ…ê¹.½˜¼ÇÎp]ø¢9IY ,�céÃÌ·PT— wÄ —ZÔñ­-¹*6=c"Hb“lINÆçYå#âRæ¬i‹tlþž×¬@d¥·á€³rñ“˜3°Åþa7s’»§ŠÇ|T1 ChW´ fþ…GJv¡³Iñ´ÖÁy{ò•¥o­ópZ“D€�Öåi¯É 6C" 9„B½lî`˜PÖ|\Û§¾#AŽ×õÜ?„Ê#q­êùŠÏdæ½ñDëâÍÀ8½„pÍV ¸‘v>ç…h´Z«n@Pr�Rü–ÓÍ0/<6,ò%y ›ªÕ”´�þ½¦üë¤oõµÀ„Ü@ìÎ-÷j >UuŽñm*Ò¡ˆuÑšV‹¸þº�Š‚wðûí`Mz§§˜=a‰8�0\´ÑÀ§7<-€™þY™ÍßíªË02NŒäðÚ†WÊÒ³¡ñUdfC±~Ê�D¹Í[ »®²Í›è&úŠ;�1ô†ÄnhŽlRªû^VÊý~SZJ ·�- »¡Èd÷tÌPâÁnZÀäA·Œa®‹^�^£í"¾Àn‰&QÐïòÕTãàÝ8VO¬%`=AÒ&(ëIºÚº§„bŠ#²¹Û‰Æ^›œ•©&ÒQtË ¹ƒ]RUcÏ�m1Òæi’_~VïÛóïãKÞ,>Ÿ�÷•–w–56¡¼‘YpRï-ö€mVúaÛÇëQØÞPµ�N]ÕÝ–Ÿ—çŸ^°.–µòŸÅäÛÿfè¼ £-áð�Þ-³‚MFe m[@R…Ö?¥óð\r8sE’€òýM¬Š„mEéBìU&³¨7±¬ÙÒlc+ß­‚öÇât82B�3{®‰wYv«Ù2«F 7ì`WæäÜô´øúPqW­4}—g�µoP2‰Ð}ââêþé€gˆ…O¸‹óÑeGºüPRÊ`¿ñUéô$Ö¥ŒdˆÝ �ðÝ$2T½�L[]Í9ÚDiª]ÓŒ‡öåoª ¡ïëE:ƒÓ;h’x.WÇ{“`^{& uʘl“pWOvï·7@7jÿY(į��c·>ý€ì‡‘0ÎXÀk²Þؘp4dl@¬'ƒ7¨»áH+2�o0sXk=�ÅÔîšÅºÔ]mƒ|]ÐÛŸkÓIC›\êq…q� ÌU>½þÂÆ„GžÑË‚.öZ%BvÞ™àá´ŒýÆpä±úÓÑî•Ý1÷;#4—úÙ˨ß;ÉxBê4HªÜ¿x—,k·ñS?þEíºZ0–ûHðÄÕñZŸ³=uÈ'YG³” %¡ŸîÅ/YC{d¸4ÎB{ëñ–à%"ð‰kÜ€ç–tùsUµ%'­QU[yѳ¥¼Ø žÜa7 ˆu¡‰k샱®Öª´ò‡áQvñj£ô¦Â꯽×es«,_ßb°8¸^Ëm E`œ%{ýJ üùêÕEåÍ_áÅ�ÒŽ:ª‚®f2Ò¿7@û `*¸4åº}‰O{ h¸±ÆNħ,SºÝìB÷�Dp(Á¯xN¸ú÷ËCŽ45ç�¤È‚Y –9Ê3ͯBB ø¦Œ°|«Ü}…Ö ®Q€2#�éTч»³Mœ`I§bfÔCÌ⿈…hê89ç©õõÍþʶnJEÌ’‘í… 05–ˆ/!ÀPLH“Ò ‡æ�ý™ú›éÎq Âñ{ÖÀóÈ�ì ·;ªu¢úŽ'¡3}ÂPFÇœZíµè6+ÞCÀ‘8ñX ‡wŸ’žéwé›RÚÚ=„œXVÑÁÃ5•"Èç„:þ@.tòÛ<‰næ¦?Yí j™Å}è.-ƒ"Õ%Ío?ûõÏá·®ÆÕÖ9 o"1RùV›!îÒ±F’®ƒ-³<�=KÚ�ÆÝsÌQ¿ÅÃx{<ÅµÜ Œm=ÌÏZ'+Â:Fìp(fÝä†úÖü¾$¡»Àô+ø@ðô�êIu±$ßÿÃ,©&ƒL¥mÇ7c(ð=$ÚÝC2¯çäL#äÌ\ Øü0ØoíüÀî ¡.Z~àkJ–› >+ê0’B¿ÅRWÐSšŠéCФ¤@�<Œ£ž¤´$uGßþNÌ$.ðˆB˜ŠMqšÑEæBÜwPuvPÏ`ëVJ¥j_?TÚN¨5¹ r¯¡©*«^çZ3•©™�3î”mv——X‹j£ÒK÷km™W8÷ÛG=ÜbÇ¢¿^Ó€¶½h=ÞÞWýÎp\s~…i§hC‘ì …çÁ-ݼ37$��žAó5½�š£|·_Ï‹šÎ¾¡IÙ- Õ± e~þÃ#XOng½*g*cÍìie ?¨ùwí×HÈÜ) ð£T$åÕ}jw8pÿf�™øÇäùÐ�?™Áf,Ä-ĤÂÐ�(ã`IîEúÄë¾¹×úxÑX_£SdpñdMšì¢�õ:á[äg’°˜}Ó­"F—€hìýþfš^à´•ƒç‘2©ùÈà•SÙÛšîó8 ^¸®_ñ[@X»J&_.X{ÜÕÍ%Vk½tJ<‹1+T+-§³Æ3µ;„ã6¬Ô”vÌ#x²y¦-Nš7ëGïißh¯µ!¿{´O5âIÿù‰Ù¸�ÿ™.´?5ºöÍ„mÕTŠiUèTÌ«I]ÏG[A·i|¿ï§¼è> Á˜çzè&!ìà‚\kA²üóÄHX [ì°Šª˜–N:øÁÄ`Ž�Áñ‰vZÍlBê]Ë¡XE/]h–U¤ËªAa:ÍF%3ànîn·Jòù|Ãw5¯�¹%𭳕™­^å(]{Ù”ü0l¤í¼Îç¶ ”ÄD�ìêûÁ«&àÃzɱ!"ëvréWnFžÜùtâÒÒ��ÒbSŠüð“ðÿÄ=ÙüÜ(ºSÊ?i–ÏÖD¬íÔLGÆã“)#gÞ"<*¬…_#â8¦ÊI?Å;�ðFžÚTö-–“{#‹XÇÝŠÓ_ß�Äw8!ýßuÇJÿ·¸{¢9¼!ÍK’¦ÄuÞâŒ÷èÑÓ`|HžxÙcq$%þ¢Ë¡ Ç*2·`i[í°-é—Ýžàe÷qX½;NÎ*…�ÈÙ8ÍJâ«’ CÈU$Î-5�"V3®˜ŽB"|˜~‘l¤ìºtì�o¸ç±ã;qa×Ùª`í²@uаȄ–¹Ð¿“÷E6Ù€žïA™ˆûï¬ÿ—B†…·Ã>Ap¼@¾Á„®Î¥´¡ÄÕ˜ƒÖÓÎ�Í¢OWÜö»D•.4¡e }r"iZ‚ËÚùBd¤DÕLe)á Yô!òRȾ ªhÌ[I‚Zá®TZ%s³m‚–•³p9Æõ"‘5�§»Ö· È˃³$žßÓÐ`0�»k"õ/ûiNfƒV'Êù^ØÕ¨42–�¹öS{<[Ý•¼�“§Qª&=ã…®Ž±åaí`÷ÀÝ7 lëNXd�‘Üç;ïG÷@bVñ ÈÑ9a,ˆ[Û!×â FJ!®»µlŸœÞ]:SØ]‡´ b27‚±Çµ Ï$R#ì¯Æ¨9—¨gç—Vúzü'®4¡l쟫‘•f„ yá ùã#.Iæ§Ñ ¯„N£Hæ`J”^,dc'_zˆ@­åû\ö² E$AÖ®ÌÂ`ƒº)u�‚’ž®¾éN— ƒ‘LL®ÅÀº+'£©ŒËÂD��ÁUšÒ”Tª¤ÅåȌտ>äÛJŠÎA÷œ½ëâÏÞ4Lȳ¼XãѳB˜ö‰�_¡&&ìeIAJWge û K@�a(”nýLßÐE4 §ÃN:D¬Ü9NHxMV>Sвcs–é’·k$¸âØ>€IHøVéýÀ°�¾9„¤óY°{+Ï›]Äo­ßcðY¿j’Ÿ7îjBl{ &ç {îÆ âýÌóUK÷zj?–!þ¿Ÿò¿²‡�"Dä3ƒ‚%@w€OÀTü¦HQ#±ÖaP=¹MBõ¢Õ×4ǹœ™8ˆ‚YjlšÐóO1bà ­¥+ë*O¨ÍBŠ:ã¶úQ‘§ÌM[}+^Öc‘‰XcϦ ]Jšó4R¥Ëq9ÎÄšwë*3Ù:ðã&é/3ç™È® ô—žû0…þѶåÒ0#[5‘¯I*™åNˆqýÀµŽÇDâßø,ÚbÜÛQÐ[1Õçà8¿¾ ?d:Ãe46Ì·k¶ƒ }�ÿY†gë]2¾ýÐ 5Í—6ͺŠ˜'~ñ)…³¿Óc>LGD‹oÈ̼œ2œö½aIý*ûk|ó±Ë¦q ƒ§®,SÿÉø!Ã<ÞiçŠ Š”:K×,L{á,££—CBøïØgv­»-ý|‹,y÷—�@Ú™5s¡'(®]!‘|ƪ@j¶ËÆBÏß>F©0b¥ÖýZжÐðÈvqv’ã×Âù'ÍÛ°+ðß®àÃôq¨ø?’ã9qZÿÙÁwwäš·ë‡D?”À9άŒeE3ñ(¢óKq8(AÏõ[[GÜv8�ˆãAô²G[An´2!Õ£XLb1l¢Gn!²•µ —»��J&™PPªìÀ¡�² ŸT^ )ö%Åþ6}sÿ?tWÞn�¡4ÈìÆ_àº^ÏJ@V˳K gAOJÑÊV­ëœ/\‚mƒ Ú4Ðçܺö�€—DÏZ`¾Ôš?Aš0»p¼’¸´,„“.”�&ø‡Ï3;ì!v„3+µÌÿfXÓ?|¸Á7<ÃÛÔZÀª*ÿáÆåëûó×G€²Çìô¨k£Ë@èµÜIibw ðè§kl�2ð ”¹+À�œÁ±-ŠQèðá2¾Èž¥¶A†~¿è7ˆ�K»â3Êöaº“¥õšÙ8jàÖ…»[->}[ZÄBr‰7ë–Ãö• KŽ©D¤Üø+éL¢Š¢‘f§2¡pî#õBµ*Fá¬ã×Îä%û~¶ ]$�Ù9B`{õ|o”ìc?”ÆÇäÿ» Åi¹!„ž”ÛZ› F±<}ÙGÛSkIMsS·]MösEù©¾©q(ñKŽ?%KJ’Ûl9ZÌ,²;®…îƒóKÁÖŠÿÜ:-­¦*[Š-·l%Jc*p]U㬴¹'Žgé2Ë_»".n´[)*‚~#íçý7W4¾ìÜüjËmV%Rœ¶Ãì‚<³,¤Oø|t'`}zÍ @YgÀ#ÇœkĹ:Û UaAƒÆí«YW]²ÌßÂÍ:¢#Ûâ‘�^4@TàÀýX’Â÷üÔ˜cQ®V@¢½¦4ñÙ˲™û=-�Z@œû-b0»8¸~-`HœAœä„ný�ÛïØ8¶�´Ç·a~“��vuȼôç.UÞH/¥ždÐWA=y¯ƒ‰DdÁ¼¸¼®Ô¿œÎÅ9 b?fÐÆÿJåxéjêÇþ‘“ʉ—¶úg€/Ùèéá꺪ªªç•Uj™Èé»2æ�",™i� g•ç`Øßy ÒD@tɧ–5Ç@¨Š1-¿)¢Ó=‰x±&µ&ÙdP Ì×îlqbÎÄùds+‚S¤4iYá®ü»Q…6'‘�³Ñ»ÀÜ-vSdA^<Þeðz���ƒUºìÀ¡8îœØ†]\Jæ~r äPŸ 6ò_6¢í o¼�¥ºD c^D«‰•¥}&å¿üŒ.=B©s°*ɵw¥ã ü†Ýõüÿ:ML�2°Œþ½o92ß×�µ§ž Ê«äP§´Æ“F­m¶8ˆ¨á(I·â®†ó«Þõ,n yd–“þ ½Äàêš&ðŒròflëæ·�ö`IQª•Mó“]ÌÛfÿ 9;Ÿ¨€k¨4õ4Š(%q>RÁGLaÔ`ÄŒ�vÜd4›Uõ*¥ú¼n¦#"ò­-‰¶t/¢z –C×k+¢î+*¸+;f4‰º–,K9�lª:*kšm<.:¡dµ¶êË-ÛÏ£hëÊ#¢ÅB¬°€'Ã7`fÍ^…ÆBw éF'ãN8oo!ŽJ¨ÖKi"‘ä†k(’!• ¿Ùô,F"b•eô”‚QüW07aˆØg¸€¼öË—ZçEWíÃbŒ`2û �zƒå(�(�Z¢h^ëóxºfÝùМòÝ2ÜêÎè·NCïÊÈÖFuxº÷Á^½_ÃU4<ÂB‚žYŽzî^V…ó¿dº¹ù‚cÜoÒ}œ¶ƒ.Ž>²·úIï. Kë7+ õ¶xYNI!H᪆†ƒ�Ó!Á{™rRþ‚(a‹!´h°UïC‹a¦·ö”áë­œ+±©>ö¼Â0ªOMŒÎ3Ì#€¨Ète>"Þí‡füU®.~y÷ãö{Âåø¸¿NÅúɶ,†`BvxCÀ$.Š“`5ÛœÖR Á7 Œß;öà*Ñq ŠâŠPÂóGåbqÊ*æ§8(’Û«›Ÿæ”V¬ÎÛ/R8â¥ó«q U'{KmÝ›\™X,Ì3ãY'ç±@r-îñ }¹_Ëw“0WÜ7÷?רñ�Ù˳Ô*Õ`� Mb‰CîÞ{þ%y`áôfêâõ-ÊC(—(ǹÈï§l5¶¡MstCƒ7 w‘ÏéôôÄœ±Õ²·ˆ‰aälï‘òf ç:4úÛj6 x½ ¶Ed·k§o¦ê˜=3±ÖgÒ>ÝWÌwÀܵ‘8ñÿÑ|ÿHÁ#„Üü8Ä’¬Ï¨p¥bõÛRþ$[óÌdëå…VöÔ®¥*ÌôÜ™ìlÁ�WT»ä¹Â«Ãì 4Ò* «ŽÈÊLF]H“1I2ÌuÍåœÒêÚ!ÖF%,æö‚âBv2T¶&ù4<û¦ˆ|•f×dB~ñ¬¥kó„©ô“¹>r1öYû±>Ÿ·Ddô`þ(ú›Å\Ó—�ØÔ]ë†O-Ë ykÀcÀCdÆŸ2bÃÌN Î0Ý4o¡{˹;í� =z‘éà1ê³=ÆHãUkJþÌŒŒ<'ÛÑÀoÑ‹óÈn0rÕ„âzwN “!ψKàîAQZ¾úâ6[I/nWÆbS#ŒôÕ�¶ZŠI§oFÐ eŠ~ò·-þMa3´‹Ç‡–Ù!4E[)zçüÄ?í·žwÊãeéFwöm�Îd%¨‚ÃøÒx¡8šk“ñèCŽ�²‰q¡Ø«~‰Õ/_3$ä€s¸«—uç­/ qUöNõ‹ç¹óRF‘Žá‘òŽSã{È賯æÞ5Ê’–Ê^Åé„p„?‚¸™:ÊF;-¡®:!ÒÿÌú ¼*, öƒÀXô–ræèþèj�oÐÕMÿ¢åΞV\§‚A–�ô¯|àg‹/q¹/ã C�“  'l(­îdŽ\6Í=Fº9”7XôíÕõÉâÕKØû†W?òk} ä¦ì:Sˆtq E“:ò… XD…nˆZ–ØaŒ 糩æüo>2!�ݪ×LÒ4�.)¶Wˆ Yyyt))K`J•eþ0d²_]d®fàmñ=“¨”¨`†Ï‚ñþû�ž“$ô¿Ûó!«)�AÃ50`4ÑÙU]Ôa¢ šê‡”î¥ö™û£ÏA ¾i׊ŠÛTHóL°‡¦#6Û‘dï‰M³o%DR kÊ~�û¶� ²Ñrˆ“2µ1üJéž�†W¡T!>SgŒ�4ÀïuŠúÛ§®”Î=åZô©lšÐÙãL8ÓßÇÔŠA¦).³«@RÛh‚ûB=�õì=±K÷ëÜuŸã¦/Hc-yóˆ�aØ<�”¼ëË 4RÒúÈÂØ éµ{ INiº‚Ôà†ÂíÝ\þ/†ÓVÿדºà�:ŸyÙ?§¢(+=Ä€Ð}¢>5=y�زæ¾ ¯°$žA’Äf›•L2Ùø½ÝD H€7ÐÇþ°],"aÌ5|QzPª¦ÝÔH»^9(S½OÓfÀüÓê«Ä¦ªØÀ¦3±áH ‰ïH½‘^DRÚF1½MÃßeØ]ë_„Ë… "j‹z€Â㻘 û‹l܇EÏA¨?¢=‘y³h¤œï]…æÌ†X`0ÂêËBõz“£�c;<M—dxˆ'MaUc|–bã_'Ž¡nÖÍ•]æÆ©‹ÄB¶U›™¥¤‰�ˆßò{ú=˜â6ò¬)¡¿eö»õlÚ ’1$IªL;Ñ:’~Üï«,Ó§ébýÄÿAÆü–#ÿ¢æB}Ä0óóœõFô-šs2ηCÝhiW¾ÈôÄ'í}q^>9T4gD‚{®Å­¸u;½¦kóëŠç¸~Ö>ó§§7ýïvQ†nÞ¨·eAÝ$³|#ÃwGÔ yHên˜g%?¦˜|{uå/N¤gà‹ LÀžÉ”‰æHš@e¡´3•,S•0J—TVUÊ 'ðÑ0‘"©äª'³�L,J³¶¨f®UikÆ¡_  ljÿÓv!ˆ ÛsËººÅK¥2Q§ÇT£�E•ú¡‡‡áùˆÏqÚþ‘E†´a.x#�Æ!@¢Ÿ\´ýþÛå·Á;°¿»;b‘ïJJ=ÄಬÇ0uR�·Žî˜Ï?vË: Ö £œnV†,ÃmKfO³\à´½ËPMQðÙíäëäÑŒº¿Ë%îZ$ÏîW„ª'Ñ·šMñ_‹ÙêŸ?¸ù‹:(D™|IT×»L,D¯œJò”*ÖH‹i1µüù”²¥ãI :z6Œhl¢_‘Õ¸üw)]ˆ=$A)K­ä°bž­qÜ„” æƒaÑcžŠ²„¨ÒÙGÅCKÉ×ÆÐƒ*VTÕX53€ù…¯Ï+¼œLô¡ŒöÕw žÏ9.×Ic²«’zu2)YèÍ¡éxqgí'ÔÔ{ .ì¬\ «ÍÀqÁeŽž~€•½£{ü0Š��/fŒ¡J?$nG\<ÌpOoèn™©•rŸ¹ìÎÝË×?)b6þ¯0LDÃÄÕ?=vÀÅ~¯ù»¡:´kvpkØòÁBPç<—[ø>!”ÔÛw�« �Äd°;É@^:ËIµkÂ.‘‚¢Ê>®±Ý5Œò‘\Šáaãüg8«Ú­‚ä =mŒ…,J¼H~*üºNº ¹sMtƒs�Á}åÊ–9#§”�†Úíb{õSðN¨ËŽýy;ݧsó{ã;bç“l-#�ç?ÊŸgè¹PŸb ¸4¼1ív¥,‰¦£¥´ÎïÆäi §Á´XZúî1º)âmѧ±Âð{à%‰ W`7ÕË—T›)•RRIMk\J(Â{¿58‰ÖS�µg”¹7›æ‚mø 9±ÕÃ�Ö9Ø*«ˆ2g€C‹øE.àDC°Ÿ\F)”Rüü‚Àã�‚¥+w ¼ÃI;¾ÐÄ‹t`±¡‹»èû鈴ïÒw”fðи$6~Lv ?0ö‘¿‚Eá�Kv`Goㆠ¤®"Éö .¸�8’ Q-?¤ão䚈€q=%9ÐÈ�’ ÈʾÐÞažeŒ8Š`¿-PˆÐ>õk™ Ø–ÔÓÙ˜D2žÔF=ÞMzp½ÜI[¸»­uÿ½�Jüm„É¢þ#R­wºO¯»ÍèqoJ—ðáƒ2‡u×}¼Í¹:& ;«ŸÞЊH)6׫¬Ï‘C¾¶¦¨=Ê- ,— ¶û�1Ÿ…sÊýŠJ…Ñj®°‡¯çúæRs Ii“ÁÆöÖY_ÏHõZú»WŸ^ð%¬Ù,’£±;Ÿºãêwæ7ö --¯_µÚXÁo)þAf³E“º4˜¤Ý¨¾¶ræš™:Ž™JŠƒ�>¶¼˜ƒÑ‡fɈª(ÓŠ4Ø`œa�Ã+ãjÌ <��ñg¼i2ç-±iü¯«Ó¨�UýlŒŸ<ÌÊ6˜&øû¥€ORUµ»_íDÎM%1?^gè*Jnî KÏï^f\$?SLsñ4Ç¡ÀˈłN1a~›cqR º„G‹Q™FU}É€ý^î‰$¹¾‘Ц³9»ur§¹Ðä•By‚A¦M¼íÏ­`:nÿEËÌMÚ2åÎŽ€Ä°�<�Š¶ÓŒ6”]È䩉à*yÈ#‰û�;ÙÛø”�l»À}}ŠC† oÀ,¥@éIì¤:/£+¶>¥�ý1¨´VûŒù+q°³í¦†Øhêaý%ЈÑh‘©uúM•‡ÞÚÁ‘Ý÷o»0ßÙ<”ÿTœ,~¤8n* ±n™°Æ©9ƒ$!­A‰=Œ¡—L„:ä!´=¿_ëØoGÂ(úÁn'ViK,p‚-bñÊô­õ‘]m;Ø !Š=Kœ&ž"TŸx`…’"]�Yåþº[[Yßm¹7 x�ö†h¥¸õ¾ì=xê—¶™„6g:U°²Ñˆ+7ÜjÙìü�ˆÝÎß¾8Ú$üÓâpÓN½ô»ŸŠGxÂ�3_H˨!•ÿ³L±BK7rJXÖBOÞ¯3¿ŸÐâ3ûоRÿ"“èË[ä 0nb“¬&f( f1Q‘ÂË}b—k¡�Ò:Ê|]+q­öô¹.Ýæ $3"K©u®úa0*îlŠp )§²=9õ$Y6%‰BŽPAΩZ{⦒z²¦%i¥ö¯qE§ æ ÌÚ3$3wmpü½&¾!»©õ×àË�:jÚQ_þKkx‚âjj {R‚Òk-ñ<¤é9Ý[æ¾8]3éz½ÒÇ�I�ìwj‚“ÌÉi`lÿ-=†G�‹@^Zî_Hdvýaýà�({>V3òÓoC¿.Þè¢tÒ2ê:³†¿¿mMøÁL<–óþ#ÿ¿DˆÉgè+Ô·—�9˜“–,-ÁÍ„4m‡8Û0U%ÂeÕÄ@Í� –Â儃€�—Ðd„òËýU5KÑšf8…Éü¨ÖΓ�Ø ºO]WŒ®§� ÃaXþ*&t T¶Öþ²5].;˜—¤>¤I3byX�r螦|{ö ´î.'¹¦¾^í¹}�9ð4g³°íʬ8‚’Ãt³#Þ+›õØÏ)ÐXò¹Û…jLˆçÉ·œ¯Õc¦m .âÖ9ûVÐú2€‚Ãf¹ˆ|Sˆ¦áé^ ƒìs¯Ü�z)ÿ3×[ühÜ2FEsÇè˜ÞËk¿½Þ»ÚõKð*ïÔÔÿƒä¥ÿU–Ã7o(N+t’Ï~” û¢]ÏJ¥ãÆì«²©Öj5yÛÖãIÂúd·¾7°ÈÀˆ ‹j™Nó‰]S)’uTPŠ'H¢¶T!²¶ ô«áÚÁBX:ö>ÕXTÕS!jÉ™–$I­&¤¨f£ÛàîýÌ<žâÈÖG­Ýl X]“ɳÇ/ÜL' ôŽº°òÃÉø`ØÄ «/¯g»”Afùl‹î˜-½¼�X*%‡´a¨v‰€°Iø�yE:l %ŸƒrqÊð”ÙÄb¸Fu‹m¡ïx:â�9ƒàr\áxÇù¿ïvkz@�±¬`R…ÂYDÒY$ÿ ög:!æÊéÈ´@w�à«|H �‹w4Tÿžß÷%_3Ä¡09»ŒiðYyœÀþÍ•»òÝ{ªw«§n̯³Tó‘Ìûï&•'ÿ‡“™­�DÐF^«1(!_ÀÓB‘(¿¼dXŒH•ïclÅ­‚C6^·X2¡“ù7·±}øÜ2‘–ú“¡šÔxµ…¨Ž!-õT¤êâRUר¡e*vˆ02….+ÔÑ«Íͽ‹mcÇô íIGÅ%Uå?œÈŒÎËýðŒîÁuËùaVìŸ[<òœvõ¡Ò“fcQLZÆQ]µ»ÊbøI®Î§ú‹Ýv8=EöH{ëq‹ûÇ,N-³˜f'¡Ž×"VÑ}e7Ùêÿ®�‡ú(tŒFI�\Ã(hN®B³ù„�û ¡† @ÀqIež6�£ÆÀ¬c‘‰îxa˜7Ýï#=s9‹¼Ï®ÈC,H€<“Âã° ¦«µ¡Ùb6÷!B°°¬Fä´Ì¨Ö¥{í'ÏU‡5ŒD§ëkDIa[s½´^<ÁXêwC¾E;Á¹Yå®.z¢, $P¥($bz |βò;è*åG ¿ã‰UÉÞKx3ˆˆ2ø¿˜섯¥à"D³Fä]‰àW »Çs„®ôtùß㲆5õ ?&iÈHDl“wZ?`›ìšõ&~:Ÿ•ÛAŠyáKÎ!^Âm} cþÎi‘á‚”¸ÖOgSà D¦¹*Û°ÿbæ&׊5®leàê\¤åÌ)_ *cCÔ‚§J‘«X ¹3ž³“üܤbµ‹UÇ÷ƒ¿Aæ[ÕÃà–Iÿ‡,ø#M'òÏ!ï‘;,€¨–[¯ªÐÝ|_ý’½Úå@‰ýñÈŠ”HHYµ¦¦ŒvÅ’ ˜åjŸ¦FÎëÄ&óñ#jÌHÌ-ôñç¥6CÁ >�™åe™ãM¨“RÊ•µƒ@Ü[bšÎ¸5$€ÒX‚!Þ‡ì`âî.”¹ã&¹­]¶Û9ÛWBœfÏþU ‰²ªÔ}14Å(\8qt¤ºÀ¹<Žv´ÿDä¿ ez×]FONİþ^¹4ã¾ ÉIrQ®Ø�:¢(«žº¢´ÌŠÍ¨8E�­PIt 3�U™¬)A‰¢hc±3!f_©5Ï�ýà Q?û(vßÃgΑ§o.ÞÉptË¥ŒH¹#¨Y’˜=KÏiæzúÏLûЏC̃z+“NÓïŠå7xíEˆ;Zy؈Ê. ØéçéMá0±êâ?ß�óÝ}&àA…á5忼�N.€�üfß2Œêõw­×ñÐáúlù!yyK¿¡S·Þ‡Cıªÿ]õ½¡}èß1óL9|"áÿUÉÅz®R¡Îp>Ç+GlÉá°Mèó¿(uÂĬyuš"B‚*ò‡2ªãO@ë ¾¤Ã"ȯ[y;ƒ˜D˪Œ`M“R¾RNB9x¢$=¶ 'õ¦«KZìšg)L[í3��E)ªëÛú3ûAsYúñzÉß�' .‹5ÇK„\{d'Ê‘ ” {ŽõÌm«W‚,Ü~[�Zƒï—‰bÇ …‚5°U+m9¿†ÑŠÇqxŸ¼ñ¿_É RðqѸ¥dz%•{÷Þü—ߣ�kð�).öÙÔÿÃ:’ýMãTƒb'D4´*J oñ:^fåZ®ºóv³¡µq4Ñ—O˜Ëz%oð3p/½|¤ˆÂŸ’…¥– Ϩæu©ŠÊRš†ëõXª5óʼn–lJ±èCäGc×FµÐÞæÊEÔñÆ-·‰¥H¬Å3ÍÊÏ–ŠG¹Ýl:Âx2í]«[–~›®Â÷.QyáÛ¸áoð”d�ßÔ�ŸpÛi¸¡ý¥"ˆ¹t¯û{ þ7ÁŠÈ›r.Nßh„¹¿ûr#l ±}S„¼Ï½–E¶ÿ21�_@Î�ŠÑ³éð®X²…Ö,0%"€Ão²kðrGãè@̰eØkšÈUívfñ+e‚¢ôó(ÂSösD¦ €Š‰G8ázïcg÷yò gW�õ0”p\°ˆSíŽuáˆv!q;Ur¡³�¹*BèQνÀ¤> �ˆs}_Ò7ì1ÖÑù!›¥ùqÛtË¢å˜`"~4¿%+ðŸTiî¥ ÞíÝ=¾� žÝ^ÙÛá %ñz�³÷§Çhòõø\#ï°XÝA7Ô�›¦ý€¶¸ôë®|"ÛûLyc¯8 5©¼€¼x’Š%ºa¨måÑDžtï|=k˜‚�¥ú V“E¦N�¹óF X† ÄqaÉËšÙα¨í`!‚Øs5xJ¢Ç±|VCÄ ç×/�ã± ª÷ų¤C&qì°»\oG�|? g{WS¸x3‹°ÀX²�ç45VJX¿Üü=Oš½™TÐøB‘oæ .SffTØ4JmÕÎKúi¯Ã.kgê­ûœ‹ãͺôæaÌM–Ê1‘¹\dä.çܤtR-¼©ˆÊŸn[+αeQ�5~¦�9 Jÿ©’û »¨Ü�¹Òhõ14©0ãsH—ý 2ô ‰þΕ†¨À¤Ò)ðñêTGBlÒÙŠ;Ó•z"ãÞ¼Mø�Œkˆ¾ñ¤Z:_ÈÀÀO™ó‘ð·Æ¯Ø ÏEOÓ;Zߣ?òá!Hè®p“Ýéû“§t¹fkÐ�tÂêúÅ㳩ÄRá9;³ÆìóíÓmÛß5w°µ€}êYÿ£v0ÄúÂâ¶De±È&wúKC`˜€ÜÕÕR‰”²ÜNžS߀‹>ÏOúË8m&Æë=5†8(¨IÎÓiNbÕ¨l^Ëð†7š ÔÚJÓÝÙG8‡¨Nõ$Yìâ]qÕà7ô*´ö$)™û0ç˜<£}¨QGé"¼Š,ϨB篺øüÌœõe `7Gú½}ùûÀP~`ž•Û�tœAFú•ãÇI®Ò;y}®-aÒi:„8‘øƒ)¹#ÚTƒ1RYôAð2ža ¨“{^9‡­q�ð°R*O½µ«¯gö/úmçŽW %ÓÀ­x¢‚�_ Ó}B]�#`­šÙlE6 IG° Î4ªA³n,¥<ñ•k-ŒÃ±•GË¿qS"Û,¥¡ZéB<-†em¬1)µâRq^iÙ¹PË:®‰ãL’—O¦”šYÚ_ɒÆËÚtACø[º²éJÓ0F씲aO¾ˆo@ù–e€-·ê]š ZXø4Ôðo[�R…«N³êF_Ì! p™âvv./ú­Q¿+Ovÿ§<�ЦZSʦ3’Zã4ô°_ÀsýîÞ³#·‡ Fãå:ÃÅéÚ aë][ÞôÝ)á…l´rÝRbÑN…D·œ†4Ãi¼»Kwð#×®"ÆÀðåCÚøcõ¦àíµ;ˆøº1'æà â%¹°)å*vlp?Ý^ÒÓÛö> ç“P8û¶S•þ•¥[×go«_Ä÷½²70òN㟠¦0õÝ6ªí¡{·l*M¹-•J6KÇÕ ¨Aý"ÃpòûÞ›!¹NKù h†WÞm/æyb!�%¡ûR‹(We[õ#´ •­ ‹Ú–'1ãËÞuˆ5¶:vY-ÕB MuÅ?]RÕ5÷2¬ée)T]ÞÑÝ·Iðò·[0õg�"û»ÖDÚL_ž�4³Y…Ö…�ô”>2msŒ…aʳvhºÈD/@ètÇo d3nЂʥOdÿâà†4$NîÌÍèçÛÑçèlðoBGlLî¿I,M3p{ªBAn±z: vv¾T •õBãu!’Ýó´Ä/HÏC$ ûzBQZ±ÁO¨lPFv [c4E!çü†"¢‚V´Óî©:¹�æÄ“(6K¡¢—òèî6lÝŒ ìøäÐpawˆ¿ö$´3½ÍWþàh`¶06u稛ž´ž=¹pvny¼½Ú¥oF›6=l±So}ÇX0Ä€cÒâXu›Ñhi¸š�Ù ‘m%Ôü÷¶úKÆ "¿Í³Ç¯¦$è N>ømo8u=F»G¼. š•8à×ò–2°y92ýK¤E\pY�®’5[Wfá×¾ûäaÑÞa÷«aR’yFÛÎwã)r›\#õ�ˆ‚Éåt¡Ð£_’‹[Ì€2‡äË'¨X¹Ìž�3ž`eFÃ~Im¤Ç(2‡ñ¡¦ÄF“×þQ÷�@N…£&¿Ûè„•$éqŠ®ÍŽ×J˜å—Èo¦îœï1ÐN“Êú.1ym=µ[«_¼51=g3aëI{¼T»\猦{?¶ÈØ$S5:ÎYâîïúR$ ×'gôwþØ ¨œ?…ù­«PøË+= £^k²vy1§,*…è®’c…Î1¥ƒõ=›¯—�ïÝ÷®2õ0ýðšûT"²{¶OÜ�Ê- ¨—ßð•"¿‘W³PV V±wÿ“ÕJàj‚Mð«— ëŸ K³&Œ~¥Z,Ën�îtý&�‹n¸HîŒ`i"³ñÚTÀô&UEMd#™{œ?DXq›Ô”Íè›Tɹ»'mÀ¬ i ÃÒR}nšû0Jð,¯ï01NB7DéÀr)Š’_;n<¢Wõ;÷ÔV0·‰Øð÷6ãôÚ>®´7ž©d �é¬ êÁ6 &ßïr¥Ã÷ˆ_YêJväCÄuÉ�'W�U”37_Îvtï�+&õ’òˆ~:¦ý¶ÿQõð ozZÐ]±ÆVRÈÊq².à•céG+®è÷·mtù¬Mó L‚²d¾}ÅíÉ€ÜÛ3ÔæÔ`AØðKo‘? ˜¦—ú.¬Ï\ë«R‡fa¦ˆº`¼/Ç6b—`598.)Y]n“º,®J!µ=$2á óˆ¿Î÷Q´€ìL¸úðl_“™|(QÖüu�ÃÝlî° ‘½ÀJ?ãT}CØÉ Êv…K€²ÊÚ·›€á»�EÙ,išÃ§È~ omÜt"CÇ8¦Ò]�Š7Ý%E�¸p-lþÂç,†àóaÒV®»%Úu„22ut± †]£cnÉnQ±ü äU†EIÏ!\ä<Ò;ä+£,Ý¢O³ìƒfêò—¾ƒÄ¼Ø·Êæ4pÑð$5ø(GÑËt#Œí|Q±|Îykëµ v‡r1È"F>ENrÛZ â÷²{êzþ^ŒÖ šl&Îø¯<´ú�¼¡:ÑӭŬ‹Š 4…¡•œ˜LEwŒ‘%Š ùí,=/%9¥³?ð�Á¿àj…×d¸²6˜žIU•”.—‡ÎÝÓa¬™Õ7ªEÆÓ¶ÚEeå$h2.I5Š«­ÄôYñ…@há oïѺË1ÄÅÙ¼Q»ˆò€éµ+èÌB—g�8·äHÓ7&Ûà„es\³Ãã£z3‰�2ȧ�ÖüKO€õ±M\Ì ˆlÃë†æ^.V¦ÿúü¾ò gî„iEö’ UnÇ@ªz â‡jÀŽÍµ@‚ ÿâóúòjp<Ù¿«æÉâhK ã¼gÒvNq¿ËûVaø—aºlc[3µØ«Aº*=”ÑâQy�êÑå À)±!È�ðÃÂÕ·^Rõ´bq0E¡èæíëwÑœ„­û­�]\ü×Ö~«®C­?qÉ£ã*D'ÛGØÎ™1Å¿ž Ûm%Æa€wï±Ä5™°9nDìŠÒ�Ñì„)BCš9êj´úC–ñ?}ÿNÁ0´3y¦±xtŹÍ"Är%[æ!¨�EüÊÝwFžP~„à(«÷v¤€€9ÛÛ�iý^ÇM?•]éšf§lj×X�ž6dø�g�¬ó*Pî/ÆkôÿPõNQ’6ѶhÙ¶mÛ¶]ÕeÛvU—mÛ¶mÛ¶mvùvÿûž}î}È—Ì123"V¬˜s­øæôWrVÜ/Ie~©¦jôÉ ¿Ü^ÅÏå[ “N/ÄÉ%/-~uØ^˜Ÿ‘½9ÿfþIýý€¯Œ~õÊúÏqgÚrû_Â3‚°2éKã!WˆBÃ%^VúÀ *ýÞÅ‘S«d|ó×j; S&3RÆ-ެfZ>›¡äÚ³Ëu•›ˆê5×µÍ`‰£ ÷�Zj‘©6‰¾u6šÕ¦®S±ty»Pl7Ê©ìífG¾¦u­½ôh[°uO¿L×ÀuCîÍN1MX)~äõ1yÒHS�UUý�ΩŸÜbý�ûP/ /ú]¨Ã=Lëe»:íÓ•ßÌZÍ,Žs*àÅ꟎i�y;噋(@¥ àã!>A.&mX#ψ¯GÕXŽúŽ5 ð¦Yé”(òZ�ovFÍ5W"VÖâ–e“˜¥t×n9w.Á“ïV·?‹�´ÞŒÞêß~¾ÒTwörƒÎ¾�ÓAáÑÈÂI¼û?fÂ2{±`tIž8¡}ÎaD¡ß×'—^?<Šu*›«t¡'ÿ'u9ÿO5Ct÷ÕÛzž´.Y½%ÄÐÜÏu¯u } ºžTØ0åävÔ¬ª¯Ž Äã¶�ÈŒèl̆Ëjs¦}$ÎvNñ£ÿÉøz¤yĺœµÃn) �h°å†«¶3?…µ»šMíc&£¿¹…±ÙIKÞ§Z§ønÈh¢.’Í}¢?9�h‹˜·¨Xý¤y¥šU-ꦢ£"Iú'�rÐwÈ)ßÊ�l*f¯‡futPK§Ô» ÊYS *Xu9ÜoÂOÛ`–W4ùN‚!(|AIÿõSµÜ'¬ &qŽÑ{s0ŒR*#|ž!˜¸ý`–Ýæ�òašVÿI*c÷üXþÖt\EŽùetÆ_œ¢4Ø1îN)$É ¥rÞžì˜!b?3¹ˆ¢¯¡®hŠ`ðvO^Ô%6€jÁÐ@ˆÞ²pQ–gë¾õÞj‚4ù®�Ê;]b|ÖKY,tQe¥¶>þ“ v°¢×ûóÔå;ç«6²LÏ(h7“õ¿´¹ù?½ïj�HßQÊ|kõ”†nçþM§4‰B‘¡”(öéÞÛ±õââÐ…D›óÌÛãð)U“µšÉEQ5Ñt“w3ËíšÊ �SÕäñŽ MÆrlcE³†ü®âE�~ˆîñ‡¨a]åêpãêJJûòS³dr˜»£h˜~�5"Þ¶z]ÐwÅ*vÙM[¨?c8éq¶2QGùÜûí¨]ž’u°‡Ÿw/Úœ¿³ 7 ¬#KüLV˜ü×(AÍû9â(äGðòG©EÁïEìY¹ûæ!î'rÅ îeК܀½³«ð�´³øëûn–™æ¥v�Er‚ÂaÒ€(çpîde„l#Cê#|†í¿�•ò“™"@0på³ç¨�§á5Íhȳ$ëñµÃÆÅYZzÉPyŸ%ŧȖì8a‰'ÙxN×p³xBc¯ü�‰qÆ‘Ðé]ég_ö8§ï¯%t£dð§^’6ÎÄo~“i/ó•Ï“u·­Ì`ØÄ͈Å2áûënm3ÇÞ%M�üyøì¸íáçãsÁ؆ôâÐ&ö: `�”$´aK,!þ ¥b}Žº I%0XÈ€·õ™ÛµÒ§m‚jYŸ²ñï2 Kt|,/j=…ŒYI/$mvÖ¤‡_•ÿÙšï­ÿiN=@çõ!XM¦ý/þµ.¶/Q�2WBD¾ Âü]çe±IÈ Êó‡Ô HLV[o%äØ5Ptâqøzð1x‰ò,š’¥"¹Ð+,œŠ5#›ª+”Âiµ¥“3ŒSðt‚„Κj&UÐ=J¨–—›kŒ#ÓE7W (7µõu#DèàÅW=’{¼*… Àá‘rÍéàñ ‘ è.¤–H½%YdYd£y»àöVs=£ïNlŸzðÆ^±‰ÄP8¯C г΢òÆ17ñÁâj%s¢pãOú€ëbP³pöÿ|KêVùbçÃØ¢ÛÝùшtdøÍ­Û•³†Ò…Pâc!ÞÄð÷n÷w•Í[þAÇt£±+ê³§`ÅN7ëIžO�¾Éí¶iÈlƒ’€ØR[’ÞÂé¥%R?QD3û­·Kú‹K<v‰8�þ \nKcuPä3a*#T+·èî·pƒ¡<ûŸP,¶\B©ÙºúDìÚñ«skî þà÷¯0³­=œØJ -Ìê +ÈOÛè&lq…´¹s2dp앳œ¶ÀiñÛºÍDF/—îŸÓÛ?a_µøñŸ€S�wT)dÿ*¥Ý1Qö�GĹ’±pÚðË9ŠNJ9Òy8³ö6MßúÖ&ø­�‹h˜)Fþô/$f˜hKîJ­õë,ܦx5ÎA^6#òuó9êzÃ…>6°j̤!(C+±à Òñ›)G}›'ýt[“È©(̲[Ç@¼øÜO>“o©Ë›hE,>)Òb{ oʬ=�+¿[ò?ýi8ƒFºvË_|Ã,Û\ÛkblöI:¤(?]r8}à ï瀾ÕAÁÃLÂáu¶ƒÃÜi˜áÊhZL ,S¼ÏÕÖ(¯¦KUIW^Òç(ñ-7ÖGû¼& Â{Óõeñ·Lˆ¿YÞ¤��^Õ>Ò÷me»46‘Ðô^†qøPÖlù,ÀÉðè¯\£fÉÿhÌn÷d‚ÒáP¬ºÑÄÑûê8Óü9TYßV;=Žn¾¸Ó!"R¥.A/3ø£@…¼(fÆZb„3zx÷~7Û¶óóU6¤wU6v�þŸ>öÿÜ/½¤ á-fakèN¹F�:g$q~"¶èBÓ�KÐx¹æî„ã|R€BÂbyüº“�!à®ÆjOuTH¾ªºyÉV,IÕ\Ѭ°$`}‚MW£Á7Ã(‹o¸^.{UÃÞ4×á^Z²Ú¤ˆf¹Jf+¬©ñ€BÌ鎂ô5S‚ÂËÏXH ÷P]ÕY„Sj¾ÓQS‚B>Y?–A„CÎáíJ¹Àí7Ò´^<Õ qïB|r)¼�$ô"C˜] (eöÆ’ÀdîŸÁý‚sô+œ$Ý5¼$#Ü!OõƒeéÏcÐNIdNÃÕbëG:‚dNcÕUæÐ):¢a\У¼¬Å à×a»ƒ;¥$ÙèñxbÛëÊ*‚äåH‘è;ßÉËœR½£«6’1<5ÕŽÙ¹éIꪚ䅷ÛéÕîmƒ@dœÏk½ã6�ÐÀ!‹±›gl�8T?›ägz½úm#ú ¸‘3b÷Ÿ Ž #‚è+§èuqèÅÉv¼ )ZÀž��œ‚$Õ6 ¹EB˜;ó«î”©ó±�örè�G—e0&¥¬µ˜J¿ûåH“ãJ€»LB +‰UV´ïB÷³%¿f5É;Çx·`˜ Æ8‡,”§�4�½Tƒjýür2NX¬‡jç„Ûœ4§eùôÜ ìKØÀñ$iËè t7©ûF?³²Lh‡îàýé�cz­Ÿ$œg¡@è"ùí~ýЩûŸÏ´Êc&áÉ9´�Ôj?œ¾c•V;ÃŒýÍF(‡'ÃÝc®ß�Ò?·×½ÆÃèœä®·é¸ÿ?)ÏÝ|Õo�²\ŽÀ&r{äÖhóDªq�.^Lû?õ–ªÜÞYWIý¤øÊjô”×lGÏ-Fàý¼Ñ˜¶­ôøÛõ�p!ÿϦnÖŒ „îÂ~¨À-ZÎs]Š›'O9˜Ú¿;}¦ŸtF~|3ËÈx�Ò¶cú Bh¢{¡„÷ŸQW£ƒŽeÒÓ¡—ß)š.D")ÇÑu%Ç×[ɹtŸÄò¸8,»ª_vv`ã *�ﶮϘÂtì(qD"½Mô‰W³w 5Ç/}Qñ¸/”Ï3ÚfÒ·}Mï*$—–)'JljHáÃƘ›|&†˜ÈÙ`œ¹_@ç独üá Ò�p$¯ÁáΔzégÂÕ!ë�ØP~þ§Ì¯Õ_käEé¶qÿÓ.³ü¯]öþODQ²…”3(ÇöZm'ÙFŠ„˜2éÓGxC¾8Ôà„�Gwz*¯ËPö*”Aj–Í܈‘ã¢ììÖKºÄÍh·q�Á©ŠA¶%1ç.¹Þ–„­H溬GÔv�Ê=¦«“TûåNcTÆJ®î¡y*Ùš|“�\�*&YºŸâ–Õ;ê�SÞw³×‹d­åN®Ô_!Ç?øž`C÷ÝGeÂɲEÐ"Tyz½¹xù8: KSË Sz�Œ]ޤe©¡ƒ¥jhÍ~ƒl�hdò¸�;Ðk÷C:ÞXðm˜„ýoÈÿÛ¸ï.,ŠÉfa«Pà´!HÆWç¾¢ª'Þô{m+#‡ [¼‰1”" 0¸0_Â.,ÆLf d(¹ôK+4gØÈ«³É÷ÛNdä½Èymj5- ·-uÀ¡Y]� —â°AŒm‰5� ÂÌIhå/2Ú a[vŠr¤ÎÛ�qËbÆ^]N‚”ƒöu·ØOfÐÞñM9´iaœnX~�T…ÕŸu'¿1Õse;=u Bµ‰ØÖ†�éE6²‹œUåWÓë3`% x’ì×3�‰¡r·Q D Û �yîaøun¨a¹`Q’ 5"ç ÇÂ:ß>¢}óôsý…ÎðAUœõ_% Óö/‚õòv®G”Îãv‚¬CñÏ\ «L"v Yˆ“ð?ò,ì�UX£Ý‹ˆmçy…#%Êë(®_Õ¼è¸æZÿ…d—)·’ÒÃ_ñ2÷ȇ^oH� Pn«¹¼|ÁТâr±¶_¦¡"]]Y¡4uŽ¥šß§ÌÄ×—¸èÏ0ðªŠÑèU$Þ‹7!¯°ÌšÉjã­gBZŒñÙ°–DÑ—Ùß˜Š¤ 9(`�¿êóè®!�_-Ð\�  1 p‰ö#Ȱé¯M"áKËÄ"ލ§Ïû¬ ,\¶nÒÄ,J¸Se�Ù#r6Þ©`µ`Ïv´7ŠŽ<ÃL"œŠ8gc¶e ’ŠÎ�ù�6¦„&›D ëk¬9k”B6¸”91’k [ÙJçÐÉMý†ûu}üüú8‹ïEñ²u»²·k1Öð Ùëý`€Ì‰,>ÎöÞä(�æx÷Iv dÏj”VV7¯-\Ày80‚ Jå4˜H¬óÇ�æýú¦Wøý×GyX/X×M&õvá‘[ 0ÿ ÉÏsÓqãÆJ) ”ÅusS>ŠÖ¤Œ.UúÜÙÙ¿õLJæB½ÝߟžŽPÂòF|”U¶óÏ;Äâ±À,7»Uæ”óµ„ÒVÖü½ P íX’+†-w‰¤ª›-¡—“tJ+–tSeC¬28úæ30ö�ê¸Ä^¤#Ÿ¥H}\Š'ÀvÐM�3N×KDå�uy¼7ûÝò ñ½x¾j¸áqp/Þtq@âp£Ÿœðk`xÞÞ’E¯€ƒ RÈžF4ø…ºFDœR²É±£ÃãÈ‚…rÈmô�· s1h^bô›ÎE1rïK¦o‚ÀÛdÔäóæ9¡+0܇×=7‚ˆIë|÷Œ^ºJ¬­]]™~âûªtíˆî‰}‡‘ÓaǪ�˜O°Ö:‚åoÃpl‚Ãü𳆰Ž8ÄÓ9㙜¨ÍHáÂme ]þÐs\• héñæí•PIÏï÷ô²B yö4ˆøÖ `¼ƒoMä^ù]L lL}jü=“#33÷º ÿó l×üãòz…‡t�™õ_K]PþŸÓíªÓEm0Z#ã¤|ˤ$}I0‹J’íÔ·—Ãàx Ý#©ÝÉq7 b¿3”Õ†¢b¡"RyM+¥e•¥2™Wq]œñ<¢�†…IPœ6”¦…††.“˜‡"5U²±”ä´ºìi4¡Ð¡�H÷%WcMÿ à!#�IÏ3ÃŒ‰"c> �‚%£AŽŽF>îsjè*ÎŽ‚x ½z*6Îkv(ǘå¤a¡%3At³½¤í„ø˜íÞª©�¡:û½·íÿfjìâ6¯>o?BßÛסƒ0³�æ™QÚ>¹ìÝ›„Ä%X,´O®Ëj)¿ôWÏ×/ÄXxÏòtÂÿ)Íþ™ìVÆ›·Ò O[Bÿè¦ÐcNR;=NJ›óü§&hîÇkJÿ¡[‘Ý„)•cU6q‹¤Ò‹aƒ:¦b26#‚Ô> º ±Œ›ºjRÉaJ�ÚL©‹’T“õ5Ši{É@ø:7.1w8µá3\ëxWßÑ"q Ö‚ ©¶Ã§’O–õ¿IÃ Ï œ´ L´.úÑeÝgf t‘<¯ò2f0Ä ³ïE3Ð%}Ï>õT‡Ñ¯ž˜ñ…m bqj’c„F Ô‰/ø2`ïÚ|”L¥Ô+Ì—È5°O ¬Ñžôš –C ÞBÝat¾âÖŽÝ¢M™ŒÆê½Ú1�ÁÃ&šv|éüþ%*¼€†œÙ}Fíîä44‹WþåH®‹1óðx6ä·ÕÏ2Õ€)FmMüm¤ÿ{8UçÁ��Z´Cb80¯ �W2C¦Ú¦�„rØÎn)‡'¬4݃Ç÷QW2Œ Û,‚¹ßyWš,é’k…‘Øó6-?ûh@ÑÌK-‘¥.«œWj¤|nBë8x¡¬2Î5®lºi('Í)Ý0€±-ˆl�lšíCßÄ}Ù´ xăžk5{‚‚”-GΉ2MŸ=]ú¯˜#‰¬›˜|mè™a+DÍ+^BÁª ö‹šño–¥-iBf±‡×õ®šÃG•)Ië_uw*› àà$&Œ½?cJ²€ …y³&/ÇÏa«½=tåîñ‡„£K¤$LI‰Ûö†8Ë8óiüiûj<·»í݈—Gu 8OÂûÿ‡èÂÈXÊ&iʆ“Ô€ ¿•‚UtÉŸëËÎgÏ ·—ÀtDA,JÚˆ‰1©e8£,‡ ªòºÄêÍÒ¨ÍÓÄ &u.J™ë��BsÕJZÊ›$Ñeõêõä £÷,ðˆ;$m‘ì=ZÏL$Ð5‹­Å¢ˆ Y(Å_Z„-8xîãœ�,1|fQwœgV~€·e…¢kÅçd/¯ W·~b!ƒ!ìÐ~>úsÄB·�Œ&D\¸«sÙ�Ó9Bï;£çm¦WqàQ¿rÐö‰e♎=@b¨ËqoÄC_Š {¡Ft §Ïú¦ê¤¤!ÅÚã~>ì!ǵ»s•.ÂðÆÊ Ìúc^QåéÊð3¢/”ÞwÀºl�LÉÿNyÞü¨ XCÇã͵Lü·‰ˆ­Õ*~²É™œã7™Ã¤’)\ñ(Ú ¤±–ÜÙ‹>%óÓuæÌMy¨‚D^¬|¯¼R˜>†ÂGLÓüË£¤VíæÈ ,æâs÷µ&îÀÞ§yT‡a> ÂÀÙÈ"�§¸Ýˆ˜U,\õ"Ç-¸¥×Bô€ì‰ö^‚7¡ñµ£Èœø™7ÏŠô¡Å–Ân^ó?EnÄÇbˆTwL‡¼ý±X¾HhâwS«£ö¾·.t‹™‚fï;æ7ˆMJè·€û}Üt‹€jÞ¤4ÈáJ»‡ÑHc2{ŽPØ£¸fµ”olŠܸ<êžE4qö¤¤NGnÐe[27ͼk¡^;ôÞARaÅž@��"œ uC&ŠØÊkhq0Ì;e!ÐÂÏ`ÍVÊ3á~§]´ßõ¡Vã®ùÉË)<¨h2|%�7Ìß ¹áVźIcøNâ—“ü¿?Z_z¿Ë}�§kªâÿƒ�qõrÿ)ˆñÛÀP”_“È¡Z‚¢ÖÍEÀ…ÝdaK—㮋©îædc ,f?åò}í €$¸eŸI¤†u˜+4g2iR¯G Mj*)æQ d2ÄI+`ÅtßpR ®!þ �Z�WÊVP]V¥5^_»#†åëÛxþ ‘ÈÔq ÝêD4ßÊ'ˆ·ýãè**¨H´�ú9¿Ý›(ý¾üÈÁ7q\ƒ‘iŸº«è˜_÷íYLÖíÇÜ™�Õµ·ìÜngéc#?-}׺èú¦X«"!G±!¼ ‘B€IGv 64eéh­·Ý¿¡Òê‰ûá¡NzIîÀ¬'077 ùGŒ·ßNäßv[�Ls<#1ãÿñ¾xF»¿¼/Øí;"0Æt"�¦"ÂH«ÕÊž(– 1E¢"àÛg6¼³I`.gçu§³'<�­âb£ƒ~²ÆÆUEZV¶ñVZUqsÕ�ÁD«ù/ñ3 ÉÍßÀ¶x)VÔYtuÙܬÖSU/XjPíàw^S ¶Ÿaq¿ãB"s�’+FáÙ"Ü-§Dò vlµ2¡�¬ð×Òp ƒ:ôÕ#'åqa%©•&ܼ£¢#­ÓJª´VëàÓ½í,Ò•�Æ'íw kS¯\a{\"V¡h[)Ç–éRÛù¬Æ¶d]V�íÏÍ××ýé—ê«DíôÓ.dçnªÅ*Ô®²«*·ê@§ªê¨ ½Y0ý äí0/p=µÿ”Z79ðÙÝì.'B÷³óÊí°ÔÕp‘üªý5L�(µ§Ñ”oA88ðãÄ\ SW¨›á9âä!›JÏf�Œ~×siBCƒŽeéËks®}Ü!s�G‚`€æÂçdvc«Ý¥/rõêõå-éºîâÑÓ£ß!i¸wq¡ÂÓß}½U#u^é§6°};�±ðüÑŸÔ¼µ®¢³ª'œz¼&¢ÝAãŸt}`@K\в�¿¢9)`@ÝÚÇ(�û£K¢tL4Ž«˜Å–‘0Þ»øã"†NM¨+Žœï�3n±J éxTígæj²ÀLƪÄsžiÑÂRŸ‡DuÑüÈ´°zº­´8%r¶ªÁG#.g ì0Ú·2æ·ß·ñ_ \YõD÷8l¯ÛO@q½Ã˜æ[Æ9ú}Ö¨^Î÷,Qí}6©üиïXòÁÏg–RA³ç„3Wte3½?^÷½Gob0ôwÏ¿×ÏåqÄ®ù4•{âõÍI¹AÑ×GÔÀ‘Ç1+…à Ph5‚gè,ÌÒps³ÌTªßòšõ†þê:“±VöD@Y˜QÕ+Í6&‘.Õ¬¬GÞz°º‹×sÏRSi ¤ÔBÆ<7¥ ÇÔSš•6F¬)o]/¤ x1�¯äƒ0u�cÙ$69VMJÐ4Ù[Î�Mö©(ƒ#K‡d _ˆu�ºRïÓpúÉf[õ? MdB3û=C«ý…\+ý×<à!€}‘ôïbi‚§ÜyЈóK“üqÂÝ�FO››üb“ö™ˆ‡Ü0É3w»{{8S“þkÿtÊ3 \¡7! ï`¤u©A ú @“½�ÔK\�¹ ¼y‘i˜S¨i©ç`f¯®Q"ô7/50gL”°ÛvˆÕÔ •X’æ_<¶¤&'$«ÿ=:S6„V+˜Eó+¤^T(_Ó.Õ*FöXkÇAk,žo²!ƒñ–=ÁÙ%Ü|)%Z " ,+;T@?gLr ¯ÿãmºâŸöwYýÐs ìœ�µÜ� Ú”Ô§°Ðaä?ruUÛÆ¬QA!BAG„`>¬k$CFQ~êõ™la�¯ÆÜ!]ˆ´}Ó0žñ/ˆn_®äýÜEˆn/àÈ]…Ý/ûÓ¹œiÝõ_-­�2`ò|qu�ÂB©%×CN½p‡�Ö²AÈèÙ)ÆÜ9_Œlò9vÕ¥çê�ë´+¼Þ“¦•Ƀ=eÒ^ñ¨•ß-*2™°}êè›ãÏG•!×4�W÷zÕ+¶úy D™·×�£Œ3æ�¶áS«”O�ó‡Â9gÖ¤˜”!t÷oðÀý:‡ Æa„Ù~1Ú 0£#‚4Tžm˜¸Ùp‚Ý6ûh3;Òƒ¬µc‰p�S‹²$Ï;N:Á òý·ŸÒĸÔhÈâ{Š~Ïzòܽ¤Ã$hpà·þŒïV)Ý~<†ú´Æ0ë$c�ˆ)¢ñ¡~ÜS†Òìtè(ssÈÕÑG)v4žÞ¤d‘ô…à¾ñ3O>Épjú <¼œÜ¢Œ\^¯ Žš>Qc sá.ot"3ÍtÀ¡ï’Yÿr\üÿ^$¥HÅIpé×5êŒ(8š–¯Nzè=7´ ßh¥¿´]b ­(ÌH�ýÉå'RèXñ!‡¾V\ªPvÙu¦ .HmŽ“Ý¼h1€{À”€·Xƒgq]“Ü~ò*\ Û’0ØÈ)D�¯6l;¡q"C"ûƒR-GéÞ&ì !­EÚ®¸ê×pcÕ¶ÿ—êòlfQ,Õâõ¨ü –�MÖ±¡ÒÕB<Ö~1ìÑgXk÷=k¯"qö%ÙõñÎp÷ +V­XõcjÇU!ás 2Q>3ú@¤¿³ FŒ4Hõ8û4'–ûOà)§X4Å—�U%âÈ Ñ%Î{ümóÕ›Þ¸ú-™ýÅK™¢0S0|QHëÇß1+]àÈþ‚1ØG­«öì±n…нMcdá_3!»9¨º·Þ~Ðûmo^Ì/Òç®á½û¿¿HÊ ¯ÆñxÒu0F³¹|>ÍÌoÍ~¨)^�ß gqÈÿMºü?�ŒB«NÒ„õñÅϹI JR�Ás4E©x£ kj�Ôí ]7¾1ì¤IxRðB†'ç_xÌœH‚‘vvÕd¬ej­Í$ÛËÍ6Š/ªÑÜ‹y‚FWrSãfgï¦q Ä‘j.Àé!ΕËúeÕÑ ÝÖm­Ì�b{ ¥ûÝ­ñ ý£g2Rýƒ¸ >i—LóÜeI–¶Ø�« Jiö<§ý1¨2´é%ha«øí¡íÇ'•0=PÍR§ j½ÞßÝ™rðÌq\—‰„¡Ÿîö‚S (�yu ¹}LGZˆ‡{6Mb8�m[-ˆ0ä°rgpª_{£§?v8ý¶î@†°ã¶ $gÌ6l0%`}J½9'ÌGîH/>U  ˜­¯õ¸`D{ 5ü{l´«Pzü~ ûs†œ‡z eÒôæa¨.ç±[ÀNOd+$ôëP²Ÿ5jåiq.‹=“= D~®­ßaáæ~΋¿�oU+bTŽ5bHþÙ¬ÝO{GoóO3Ý=´Tíx:æ?5�ù¢ˆ¢»o�Ã6´ QŒðæ zæ’,�óµ­�7à¹';G9Ü̬Ä@¬¦ [RÅ6³ÚôÆ«ã¾EeSåçEIcíxb QõBÔIBfž+R•ìYˆŠ ›TÉÈ�‚Ò!"v.r˜3�="Ì÷�]Ø fMºÃW?¬•rtóL¢¤ 7ûÃá’�S†W#~èìVBn÷Ä)6;J´ó§0€O›®Ò"åë{ 3„ƒ5nÝõÑtæPwÑ®—ÿ„½ J¡wiÝ¡.¤@‡àB›»ñ.‹#³]:[YŸœ¬—ó’žÊ®.îæò{9Žd¢»g%ÇþO³ˆk'®�DZвx¥Á’œ‚ØÂÛp| YÊN¯õàú¢·×ƒÇVD?*¥Ì§ÖÓ®0yŸ�Ó¸;WÀpX,X°8.àÙdš0ÞÃëº=‹Ä<,µ¥…Mw¶|s¿ÉÀ,Ð-8r‹Ä]Suï�ã®°Í!ó“ìŒæ5Žó‚4¥¡NJzú7¹ýWÞ �ƒæ 5-ÿ¸–„%X�­S�ÃIlÐR2”¾$B­ ±;on�û Xû2(дI¯ñ+¯@ãTà€ºU@tý½`Ø$š”Ît;´^ìïIÔ]|xwÜt´–þg2/òÊJ¯/ŒyHD‘aò 2îÚ‡BKÀ3! �×—Üü ¸�éQ^c™x8 1%úGI2º"$7„Ü×–Ô¸%4,HL™6§I`1v‡dà¡;yp¡„N}+Û)Pêú:dz½˜Ï@~ÍÇÃK–Ý"5ÃL±š,®OÓ›wwÉÊÝoÀ ØL�¾¤ ³ªsð|sf¡„Eô�ûO¹±Ñ,ª*õ–¨Öw}2kîV/G�N]‡þºMO!:‹�ºÑµöÛÔ^^q_À%‡°”ÿž `ü§…üO ]° ù�-<ÁÐlÞQçÚÉp¼™øTjéwìb.Yîñéí¶WV¸Ÿq£Uµ‹ø@ ëFKM�ÖâDŒñ¯ÂÅlë²Ê ±¨4J&œÈ²J™B®J®ë¤eË_’•°¢EæµÅ;•–äwSÂöö1w-ÁnÖ9%÷\][36_Ì*RË6Êå%!ÑKJP‰rä³2?À,Í;»�4œø�ž³ztÈ¡.Y�f@r}˜‡G ð �q@ýœžh‰ niõ$tR,!b`Aå"†êú _;¿xPz§®ŒàÑ0SoE/¹P}$À€9ðÍ©‡*éG*½,VÌ4ûÝD}Iw„- `†XhÃDSÅ&ÆøD÷ëhô›Ðíð�†šªL�¥d/ AÑét‡½†BYŠzUÐØò¬ìo5µÁøwÐѯ±º­Û̩Ў³c‹Gý;PDÚåMØ!Ÿ* 9ìkR¨|3Q·2Ý)¯XW-Ð…"I8Ë-êzgžuücÄZþwäL JQA6Ù?ßyÛmŸSêOÀîyØqÙÙÅÀê¥ny0™›oߣ–ä—5)ˆ8Ÿ˜Ùúl7¢4•ØÖy×êFÓ‹#?ˬ ¼ê«ZÜv¬é¢P[ëán°jãÓ"åBVGÖæ’lš ¹JÉM¨U;.^*Rz‡ÐSP= {^¯8@‰Î”ž„-4I뮢—ÊDC“o<›ïÛJæÁ9¢Ýîƒ~ äAÜ4ðãù^̇>òªdM¶P£($éh0ÿ+ü@›`÷á]“órcr#’ÀO„ PÉþ˜?ßâžÒ>�(Hƒ‹âÅlSPã‡0Åo  Üý悩Ìf Z漜Á.›ãÌÕ�Æ–Dp k�g~Û ²ü|¡Œr†^)cÏ%^ØÊD4ÈW¸Ú °:Ó˜™hl5ÂGÊë<.»Ôáarm0ï¸A|>±5Ðk¹ÊÉÌú'kýO|àÖwØ— ùz-&á@ ]  ÜÖÜæ– Q§½ÆÜ^È ËT޹Ap.‘� ä:�SÝJ»®Cœ2ÚªÙÙ¸ãL2z•Dn«Ñ²|Ò%¢jE²V³\ã%+`ÑØÓÿ”먰×Ô}½�Ú ¤µôø×MíÖ‘cpqóuUkI°˜'¥èÔ"ËŠÔ‡·lXè=P¬üGìõ‡ïÇ +ðˆçÈ¡C0ä$ù’¿>eûIˆ;PÇç•)¡ë¡AÉ’ñ Yûï�H;,óî¬ÿÂà/¼}ù‘Q¨‘L'éJ�çKJ*yÂXyàBãŒK8üôD@ò¶–j¬¥³3P"N+J׫T6ŸH%aw”›Œ£Æ—µ %hªÀbŒ‘–�¢Ù KÖX)G5Ûª EDQEV ‰9*RåcÆ”?¾àêâZ§ÞIbÀô+ãœ×HãËÍÖQAiHÑRƒB×pš�äβ‘¾£ PjÔܼìÓƒØÝ†švzñz_³éë<ÄQ”Ù]š€ï<#í‚Ô?íúJß\2œ"dÁ'�ì””“›‹›%<Ó ¸€¤#0ÈL•Hø|`Æ”Löë�º„À*?Ú¶Æõ÷´Þ€pÀt³º;Õ³ýqºqžþÌGx0éÙéqˆÑqÊlhCI�º¾Wl¼³é,+í½ô�ÍJûŸˆ°fD¹õöÖGר\– Òv”ë£Ç�d¶ò§ÅÔ¹¿p’‡“eGQ‰��)Kjö�€7h ³AbüÂcçŸ@2Ÿ]ŸAu0 tÙ+«–Äñ™²î�N±£â@ý²”1*Y9^X`ÅzÀq8[§‡Æ‚Toý®Ù9cИyýx¡p�rr­Ÿ§ø‘Ɇ.5D Éf¨}‘ S$‰˜aúoÎ* ¥í÷HŽjñbî¿9µýÈmpXÁ’b}k–Ø®ˆÞ0ݶ§tñ~RŠßAWÿÔ¦ÿŸÜðÿRЫÈÀ£I5Êâ ö`0o –Ðzc”ä¦V;JN�Îw»Ç�ZQ�9HÌB{²§sÄY°êvèñM ¡æN¢òq‡¢ô*Öêç…èÊ0Lq#�H޼G ŠFG|—iÐ’åm¤.ü=ššº¾E—ŸàÔt;‚ÔÇn·½ô>YÁ ¤í„ @VãHÚ4¥žÉE Îm)Áðˆ’”&ÁdTç+žç�yXÁŠF’ ÍÛÛ3ñÉÒi=o‰î+ÖÙ_¦nìyCÞ‚æ¸Ý åË‚Änz†ÊßXfæ HXL�%«1ÜÅ;±-›Œw/ö‚¯•÷ 1éØIÄ=$³�=·®Í¶ßâhéìcëØ$=€©¥vÂ�Ò41ºÎ8þMŸ<Ÿ×0@Í/Öm‰Œÿ�ecÿG0¨:Õf¹Hv�(ˆc <9åÃÒ:¹pî¥hûá³”P.,±úþñ# SNDwlCÊ­…3WËÚ%øÄžI]|•k“5ÙUÇ­‚›Õ¨ oNÓÙu·ÉÆÍò^Ýe$^Ÿz©ôKñ—ÍuÔùL‹% Ï=°.°÷z?³Èíc—.ߌŽÌxÙE‡ÅKè»ú ëîõëÄ�ÅÃÔÓ.ÈÅ0‰13Oý�H �N)ÕB(DB7]`׿§ff,¹ ¹×*ÔTL’Ûû(ùí{cÅÚÅKôHEÿ¢äg4Õ¦Ë"+g[ü›;n¦Ù§¿ÍHÿ§:õÿñìûÎU’r›[pÆCyG[Kˆ×…Øö‘Ú˜ç ÜWÁëb;«bƒRfX€ª.œ½±eø¾CïgÊ©^K �?›©³ 7ò?=ßW¾Ô�·ß~j»€ÏRv0>&q /p^¥K„�Iš@Ñv&ôZÁ;g#6¥nÅr&åY¡²Æa¹ð¿;ÕôRýÌžþ�%;vÒæ?²Seˆ×_ü6€•P»?B[ŠÍz>ïlèb§!(à/ƒ»�nš€±8‹g4‰Ƕ#É3¶#¶:„]�§>·FA¯àaïãsã©Ô¸M‚_è3QÙá S¶“˜k–-ð-ÈÅäs»V!¼tôHyÝÝJæê¤2i§eu^�¤4äÌ…¯¾ï<(d aS ·ÊìécJ¾¤ì¯ë¦ku9ÂŒy’-Qßð4$| u8YJÎê4ðyËÔÜÜ´45Êø7”èI©á»ê-‚MÓñÁ’ˆ(&+|à+ ñ©ˆv'3‰¥àõÖg<¬°ÛÀfå&ÓYfd:&a‘‹s�? 9…F22c­ŽÑ°t¤8𔓒Þf$ #û[ˆÑkjdƒòÜìZA¤û¹™�£úIwÏ#½$ã&æ«”çóD}Ó¾,‰¯ŠaLR*þâ¸áyæ-lô8ç,ùÑÕôl íü ª¼e¤<·–áqÆ$DsÏT{ßË Âa#eàQÁ ïàùÅîö¹ïÝ4,ùß`ÿSXþiŒhõ‡—Î(!"šK•xð\D+‘ §<¸u{,—ÁUx§³Ä±™)›Üæë¡é,„ìNæ•Ó¬“ú§¸°XrÕ#±Yw¸Ô«G“F‚(€µ.%§Í\Á’G4 `ðªÑ�ŸšÙÚªžÖöAÌJÐ0t_7c»~T6ÀÊ–Ì«2£‚5ÿ2¨U«›ì\„\îAâTŒ2Ñ3Ô3]§ „לVЋy)ÿn¼Ýù$è۽ܺ´à¶#“CŽ¥$°ï·Í€?@St.ÙÿmÁÿñ“äÖ&Xr5' j�àí_Ë‘ìÃå´¥¶•Ĩ·»~úm7Å íœq�µFÒoÞµ ˆ $šŽ.Þ˜/ x½Ôæ<I@Oa8‡ÓAŠ™ÃúÀ�j=†Íïa':߬gø|º#׿D4øXÿÓwi—c©¥§¿äÙ0cuj úŸˆ7÷�VÞŽû4(~ýq÷©fÔ’U �µ‚$‚ÕûˆúäõýÍ"X7#Åîhv*·çXœÒãþ£VQu×Rýóp¿W³´È­~ †ˆ-~Æ@„E·z¹•уfûë÷†âox�òø ]  CG°±á³<�6è­©Ò‘àľý:ë3…Ÿ¾¬�žýÂùÿ.‡œ!¶j˜k ¾rQCíUºC¢èÁ#�ml„'€úWi¯ûÏ*P�4Û]^"㎻!6²ë‡¡øQ :’!yÕŠ‚qy^¸–12ZwþÎ…ëY ]ÜÈ(ž*ßEtÖè]{Ÿ¸äUë3zÎMûäñ;D‡ÍÇ Ü‹9"ê±GŽeòo(˜7.a¿ÓÔWè*Ñ…'PÖÁó?ž€“jƒJ­Z�Šº Ì"í°T.95‘ö‰Ê–¯ƒ»peø?køÉxÿ_ºîuI‰ˆ€<ª¨ÚÜú²$qÞ­©IÑR09±‹.ïi#ÿ‘�9-')ÒDZ’°Ý ~Qtt°>"qaOÞæµ)G܉ „ †yض¢m¿8×ßræ*²’w„U(q“lÊ�Ž�œÅKGìm»/’Óï”s3}YhkÔñ(±Vs��±ug¥sÀ䣼çöùŽ. ûuƒKÛCsk~%F©Zá9J6R:ÙSÂù-.aÓÆè�Žôx�ÕíÁ¬íúλˆû½A #c\}XV‹”ÉV9Wœ=#!iÙ%‡œ@= ƒþº`í7ÐÈäšN×ÏTžpã® �Y}whƒØ<¶fñ˜\ãÔkÈ<ÛÛ�ù ò` Ÿ÷˜òÔú\üå]ë~ƒg&ÅI® y—Aæ:\8ªqòÂvjÊÜíË%^›½œÑÞ¼æ”9RçÀüé˜dª¶Lx>G!¹¨‘˜K!™VèQ3É¡"†“áC|õHøü#šÔ¼6#šÚÓÎ_ÚÕ67 V îŽl©±çÐûÕ÷èß<-Ÿ}O÷� ƒ� B»vµÙí-W¿éìèyäqCÞKöT¹gI'ï—¹#âûˆ¨¯žþEÑ’C6û~~ SÑlhoò2ºÖ‡ið¿Ÿ<ûL™û�mH¿GPM3IWŒâéæè×°`‰+H‹©ZrôK nÞkÚ³ÃÐ] ÄV–bv�k�®KCGšÇ0š>`nZêjYḭ̂ƒ‰¥;Ô‰9iŽÞ-C‘h0É�‰-îYP¨g8™ÜóÏ™3«ldVïÃÎÃ稼妻»3’Á¼¶9ÂeÓñöüwŸq³ÀÅ5³"§«ºÓOŽŸOËðE;ZÜÛoªj¹´®ôA~ÌþËS«à3ð-‚1P 꿎´Ü`VwX  ‡û›0ÈÜB…(_©<û×7O)&I Î/Â8!€¨\Üme&¢¥°•´1›AÙ0µˆ3Ù9¦Š3¬SÌS.0TR³Lª¹±¼ì•PJË|E/rŽÄ…õÉ’,†W<ÖúTG)Ìò¾þM WóFq÷´ÙÞÃóщ/⥨p1HUæ²²‚¤?,1”øý�T¶@ŽL 2oì?µ›3m½VmWõF]>>¡K…ÞÛ^8ü³ (˜eÊûä üQu˜hÜp ?òsT(G&tn^‚¼� ,ÿ¤âþŸš0LÈ­/.»zä!‰×FgL}=Ÿ@¼M±ƒä•!5µÏG±Åº·¤wï~r"�j•öùê¡ŒÕæfÖæ” •fÑ*¬_*Á‹š8ŒŠúe´UT�Í´ì5tuLªZZvc®3 ìKæþȯŒý-£Ê~±¬ÞHdÒwI±“Ž™—i/³ ª¿di‹ƒ’­‹PØb ÍJ~ �)—e z(ö$ê^»"g®‰œûá”Oô<ý…ºF1iÛ‡ Æì”Éörb™ÍáŒ1;ÒÄŽÀû¼Ç‡ÂBÓxr ]'«£ýéI³y^ùhÆ û×�™½•¹ô|.&íÙAÙùËIººÊKøœ�™Ú?š´zá�Ôê/ÕN>ÿ´R Á» §g' þ¯9À_Ô¿ér|÷T«*5*Í)0ºŽV]L@´Õ“ÿP\™¾ÌÐÍ8(á™9‘Õœ¦@i܉˜œßG9¦¨í}¸@(e³„",tPHQ½œx/¨™¤‹%-2Ÿ! õûÚÊÆ^÷«ºopg½uì®AÏ÷èiRØf‘$^M�S–!O¥ÞVë¿¿¶Çg áC|ïÑ÷PÜ6T}rg¿ÅÑ¿ç÷¥Y·WÉã£%ã!‹ÒaFzŸeÉoàëÍý;üÂ"%þ„øwNz ûæ‘n³”#"Öæ ¼87ZÛ1}ë"àäüëm0’7Ãò&$w(Ϩg€=M\͇Œg ¡Þ(¦jëko1÷Sw Á„~\—ÒojŸ;‡uà#xAGã¡$µÒ®LüâÆÚ†êiS W?d�Šr�»ÊÄbî~é…Âöä‘—ÏCwÏbfa``fTœ{´wqv€5ü³iXW:}:»ë��ìKãð›€Wô#—PEÒÓTÒÔS×W‘ 8vÙЭõF[WkçÐ(q‡»’5»bb}—~j™~hq—>��çD. ô0Ûÿ»•:»­O]�Ž ìÂùùéŒ=ÇîéáaO`çéLÇÙ ›vçãtî’iOøz¹E:g9Lå fº#œÍi¼»óP•“Eñdåc¼]¾%4Ó³Ò5Õ¢ò#†·êêöŸöuÂþ»/›˜í"öÙÅü ?:ðv]öú&âÞ²4b«ÇA¹ì½þR劮cçS�è <ÕŒÄt‹¢_qU©é£sŸ¥çòƒÜ¥Ü&pû5VRjw¿~M÷ê€@ ‹ŽÃÄâ }¾4V`Ó4^rýMÝ~ƒ*¬',$†°ÊžÀ{¡Ïüh¿Ú›Ü×$6´w>”z$Yt¼ü]ìk^ elɆÜý_Iëìm¤C„Q¼�)÷`-!ìxö1Ù˜üBóJ­y6åZ•)0î)¯›zº.Ï„ãx³xõ³ˆÌ.Æ&&¦ÇŠÉª¶5%mrެ!õ½‰jli;A‰m…7 é¡åCŠ òä’vlÁ¦Ê¨%ó®lŠ…4R`n ¦(òKO\j‹îLªlyô»¹]ª»£<�¡ÓUì2ï. ±‹<¶å±÷$ò *×-bi•Œlcµ‡Ï7e¯ˆNtÊ aÀ KçL ¹ôsU‚“ôã3L—L` É9¦UèýõíÙºgÈÚ;¤øFX®5¸dÄÆQê |óð¾¬YÀ*D!åƒN¬ÇÒ…œÌ—¥•8±�?išÌyŒ$^Y"Å8æx`š‡ŽøÉà¥r›1}áÞk½Ô}w’t�˜ Ù€ù訴$AÑÅÁ%³@Àõi‹¦C�îã&ÄÖ_8låžà²´Œ¶î›ó€| nãÂÀ5Šc�ÀJæÎ-o�Îk±ð3sVu·Ç('›*�ÅaHŠÅQ +zc¿š-Ö�¯±­çiŠfL¯®cP¢Ë)âØ%ì+<‡¡®×ç‘ü¹¼†/µ£_ê§¿¨73'Gïu†±9Œe ”Ò€«Ï :‚; ìþÍÅãõìÌéîðùþçtˆƒ«‡ƒ“‡ÿ—x©Ù…@7'G'‡ïÛËsºçãËÕôùáÜ7OL3Qœ%‚‰8¦„Šü qšâh#øUH|œ ¹?at˜úaçðe÷”€€Ä·•È'úVX½üî§£÷ož7�ÀKÓÒ4‰ ÅÔS¹V,ísl²Á; GÅLéŒ'>)0Æ�yš rÂ#ºßp ?nYö¶«—Yã—yé¶’Ý,¼}Ãfeåo©…o{´aKØ#}�ÉOÏiÊN©’M�%jt_,²`¥,9¶™¯�K†Þi��‹Üw¥Òþž³N÷7£×£½ÒÁypþʽú /î»^W#Oö˜ür ùÌçÒ‹Öåd̰›Âí•ùÞ&`R‹í|�×ÚÁpKSI�àuý¼Í>þ?)£‹J9ÞÜ•¯3ŠÞ›Ñ‹çG5�TFý<�‡u?€àó~–bÝp¿±…†.û½PoN—pe7¤ Ö}ùf2EÙvÚw'é6©Î ðÀ‘]ŠÕu’/Z÷·ô©¬�§iƒ!+O‹•ÂTL.j¡Í÷…:‘vã Mê�H`{¾—]µ$œá§=«7Ea ×�ݤA@„>CÂp…¥Ìi.V2´¥Gu2€²*š2©0æv¯|Iöªy4ié|×tA·© –Þ_«Ì¾³]øg€©Æ=ï˜&‡A㎠%²äI¶æfôˆKøQÌ|$ó€âyB¤¾£)¦M‡ŠÓÐ�[jhYŠª\�2Ée«÷ٴγ`.¿ rmÇc¥�Wxƒ)üaÔ¤�HëœRg†$µB‚Ï’Ô ¬JŒ5&#1C¼¡ž³1¥(#S(Iy6â�)(µižÎH0†I+6´Ç|Qp =ÚðëÚrƒ|Šè§lbh‚x›Ì(ݤ¤pfÉëÇjYƒñúj3’�’œéL ë™^ ¤bhŒ{ •KŽnÙ(@ó( ©£vUUâÚ?{ÞŸÕaiìÝûƒ7è½rqÕj©óB&R9<³�1öеö›!H¹1ItÝÑ�ÊåñЬùij]ú½v7«äu3¨Õ‰XY\#À•ä�§!͸ý„ÑB]Í €òÔ´ ›xq[ ê—MH"Ÿ–xÉ›Îð!+!_uc«*rºCe¹@èÒ9n·Ø‡#äW {² (Ø|àPÁ�s°!M_� ¹Aõ^Ð5H}àÀ�ËV‰J„�qËFY»�Ä/�L/%‘Õù"Û?¡EˆxÆ �òñþLÆHO�‡·Ã1žÑ}œÇb$@Šô,B€®W�FÌÇb¯]e³ÞP;r»XÖpòPüæÞÅ™ö˜†RÎÒ­&ùPá%4ÍðžÅZ4¨ S³öÝR-ñÁ¿e««›£Nó\—|"÷®XéºÑ¶¯ñ¾½çÙPÞ¢³+Œ'¾6:Ä7ÏïžJscè³ðÎÆÎdbuJG‡×÷óGÏ`uËG‰KG#WKé»ðï&d†ŸÖœF9ì9µØGƒÓo;xåbg:káôôp~-†/²ÌñÎÀÅnáM­Ùeã•ÖCSÇÈëâçâ€iÖaé«h ðoÀò%ô.YU=’Ý;³ù˜gèÀš"¨oö…·Óz5˜ùó®?ú­ÏõÁá ⃴Póú¶:­›—™Í.n‡¼w._Kã-%›j‹=5ÐI;îÑû `–ùIåæ»a¤þú˜YvúAP5§¬—… Ëàp»‘1ÜMðôçÞ£ÀÂaN‚É1Œ ¨Þ§�ML^×@ù)$¢sQrªÎ—L¡·Þß�åéÛ§/WÍ� r:ÈÔVݤdI¦2Î?½nÞé&aa®#ëä œÉÌ ÐN½˜0¦€x|Ã8Q‘c™¢ >�6O^Kjs(%EÃ~`Ó„6äj㫤h欅’8 aÌî/®�‹s‚¼yý´f;Ççé�ÌÆÃõZÚâ~¤@¶\aôüí~ ?„œÊl1ñöŸ9‘½˜1Y ÷ ?»¼ÂÕLcü<øsxƒðè>P‹Be?AnÉ}{ˆ–é>w�z†z¸z ‡?ÀÃ…€5¼œìì >ÁÆh×xR `bP`äREiÕ�Lv@ P·ºAÕב½y늅Áÿ‚g“üH;–�5µ*Z^Ù'tÒ·Üt!j}¤ïg°>é]™•ê™!:Ù+½hJì}¢)¤þ‘C“^znà7�ŒÌ/F�Óz€%,öšXsÀsÆ4ðxý#Éh+F  Èj1æ:ƒî5䢸2AO­3Áµ¶¡9' Š˜Œa!B£ Jÿi(¾qpˆxMíM� îH9²šEž‰C ×(V»Œâš£ÌYäŽH­Ä²os3¨ÁX¦¢Q9a@h?r#·DgžLµü„"R™'{ÈvÉ ¸”¢ÈQ  K°ÊŒWt¸kLï,‚ˆ@±.þ;�“hZ¿æ "çt0ý2`pjjR©Z«`(Ø·.˜ä1q <à+rð×Õ›óØšõûb�YHÒ|ň'Iû/ª�4˜hàøåÓæòÉWX0Q‡1½NñQòÑSþ;•¶O†h€×ƒ-<‚¼ÇÈÆÁÀÜŽPòZ,îö!Álv½CÁN¢å¤ýNw-R¹UÞAEí`Úã�B¨ØH¦ù)¿½<³h„˜½3˜0äF>[¾P¨†m"åÕu†eh1êƒlM§ÒGƒ²‚ 1ȲžïM¹ç¥=7Æ7`Žª,…_Qâ ø~”Z1jP %ëQÎâAFú¨øxYlq�ävz½h«ƒf¹ -N!~ Èɲº¶™ 8‹þŒ>KÂ.‡«á‚O­},Á ìO}áß,ã°ûl¶ t@(ê„N޲Ši /‘Y;æ§¹h„‹“„©ÅÓ̇.¨’Hðý¥£çIÏ¿bÚa€˃G@,ÍoóëHâëQð³Yiâr¦S4@ç*Ž®"¯Y@HM²‹Pö’]°½W.¦iÒå=JÅØÛ¸ì¾W<� ¥î ƒ\iµƒO+8Í•òO»–{»¤ î/’�bš’4ß“ìhKS�XZ*÷û{òÏÌ(l&(9|‹ê±Ì¼¥Ó‡.ȦkÿÌ“ØDÙKÁª _bñ$Å)2•ç]!ÚJH“î/¶Èrélá^E…Cœí–qLÝJ°úφêëÏ»&Ð4zr¦váãñ§X£©Ûv‘4ž’™ŠSåžÀg7‘)ÏP—Çéñ­} LÖ('á_µ`Ø„äý=.Á$Ÿ2›äÔšRNqñ­´ Ú¢n¶pxû¥¥ýŸžkB  °èšï�;î*ÜÌŽè>&B¼=TÞ9êJ³Eý—Gz/QHïùV¿d=:]L­nLuªQ…›t‚-I Ò'd¨d§Rê Ñðòt+…s·FòÚh羕„�©ú²‰‘ñ¼JJóÂÆ­E@([rjª×Ê»Kû»·¶Ñâø×—¶~驺!´…)Ö‹8f€FòbºÑz—ß~îáåkš Ç�^‰«ÑÇLìesò8Aj„ï!=7¯Á_°åЈãj¨7Ý­÷�¹šǯÞ,b|z%’'X@ŽÈŒCÒ�Á]+8É;íò‹Àw™lÜ­”— ^kt(‚` À`"Dl+ÓGS­vw-_—ùU4ÏÆ}‹¢ý‡ªƒë Õð*{ÓöÅpS#÷×ÔkVÜ/òìáFL±˜(ßw¯\Ž|Ém3'„¸aÁ‹¶ˆåa Ô˜öçJ�9îv´6~íÊ€ð?ë®]iÉf|6{o=ÁO;º¼Ê»Ô}�¢•zŠ˜Oíµ$…Ý5Ï�ä�l?¥ ‹ƒÅôÞaL]ç-i»L¸›ð3/ë¥`ž.}!ÆŽ\FÍÃ(„¢²}"õ)‚…Tã?¼ö‰éÔËÛcø/ÅÏÿ� ÀØÀ!�o�v k ‹îç5š]�Tg…w×¶ÄVjÎÀ‘1ÁÅtíÚãó¹¶yï$/‡ z„€×œ@Awy"ûµ“ù•:®ÆxJ„gælö§ Wj¶´m?—'ðŸå¾azXÆ�Hkåà2"ù&Vò!mÂÖv—ßßMd+Žˆ;¹RüwcNiÒ\€%Zr26 *äs“‡’ãÏ‘@9O3;;ºƒ¹3ÌmáÅAÌ‘¤Šº4†ë®�Þ�4é TËf‘s†k?sÃM§ú¸çþ¹MÌ»´,�Œ®IÒ:,6ý«·ºù°÷"¥`6)o·/æÞ¡ëתÄq9Eê'H1–в—=æ.)~¡�ÓS~ú'&Ù¦Þ·ýÒ¬ð–î¿7 —HÃg¼I7a¹a¥�zní¿�ÇÒÔÀD¢¤Ñ¤d£|‹ÌÖ3ŸFWŒò a&‘ë4«•“×ýi‚¶É²?‹uתÛÂ|7Ü ž†'2·Z·…­‰{FŠéL¤QOí¹k.†Áá‚Z‚\k¬‚ºJËí�›@GHã[,·™ C¶ÌSæF€¶º=ŸÅȱn!dp¨ÈýXV×J°[xòTèÂ=†r¯ŒŠ‘tÕGŠI@ñމվݟ.Ó¢œ+ ‡Lòðü¬ðr½[}ÔãÞy.ænÆŒsà'DîùþÙ¾Q¼µCOÐÝ"83s_™z¤:ØÔ4äÇâ÷rÌ�±¼¡ŠÅG¯œÑ‹)ïëÛxNèp"ïR¿¬ÿîÍõÙÏŠÜóMo™rw–‚·ê,ïŸ ¸‘±¸¹iÔöÙ¸¶†Çõ‹tî'èóiÝî©ëÊêR¥¾º¥Œ÷QqÄÎcеžËú {|Ÿ9·hzX®yÚ‚–ïÉÕȯíõŸX_3�†h8ñŒ(Sâǽdã(»¦rw–œ�rr¯Ìn‘ÌCí{#(ù·£\á4ØwøÁ42G1°Vw7q¬+ÖÁhüɾ¡Gy=â¼uª¿/œ¹«o@Àè§3ù÷a—Ãà#„÷ÌUÐêX �»­÷ä€ ™xµ°cŠ=|Lƒt­Ëózã{/ÿ? s43¨=“aiE¡LÞËY.�T#u*æµB*`n¶mÕ=—Þ¶Ú­cE]n2÷øFÔ�Mž=µ¨ùlÒªy©ªÔàLlÔz¦Á‘ùUͲ?íÔ³ =ô>\ôâýkâÇݳ\£½z·Qº1ˈn8μ·ó¢9à§Û‡��ƒ·�]­Éò�Äq±í�Ü[27~ ±C(@õß9WÔÐF— Du,tåµXž’ÚIË£d™BXe>Á M,ÇÚ²0•’¡šêû¡"d8 ’6Î4H|‚ÈÅ2~}ù%eÓÏÓ¨í� Õ(¥Œ©ëÅ46°‹¢# ef>ùÌ ]hG&kžÎ™  q~)à.ÖÑÅ{à›‹ÄîêdõÅ-¸R³”�÷ØóÔÁê?[Œ-Þ„"x¶äÊ; g+\‘º_4r×ÎÚ–Ë£À¬Û_¸Q!HŸX2Ãà@$’PÃ4Á1ÎJ*Y8÷à!ËîT÷ hìš…KŠ`y鿹¨ t¸ºÿ=ÿ}9læm‰Š¬c`7ƒÌ×ób¼˜ïשě”îùÒýåùr’¢¨wD¼¾Ž©Ãª“«Ë£U-þ¸HgbKQô:¡:‘e¬¦ÞHý¶öþA<Þ˜>þäªi%öQ•~H,¦‡SÝQû ”uÜ"ý´­ :¿øÊîFí*ÿ4Ãâ2â£Lî]�sâðIX÷iñVÖ²ô¶OÈpp rhêUOK.H(SP=´ÓÔ ú¼P›«Šå“ÄÞßÁ Ü«S›Y$•ípë¹íìäï½J†�Úÿ)ç6‰Ü"¢ÅüÎ?Íù˜•z¿U|)gªØ@Љ[Ë{ÜTÓFåcÓ8cLF>ÏöD A¨òwTÕfuëyçÐ0c_Z7²Ì™¡9éâ{ # Ë?H”R„dÂ}FãMɹQ[‚Hð©ï„l¹ÕZV]lMy™ó2Eî”_¨˜{?Áß ©D•b”0á8�ËBh³<�9)õP²îdïN³óS»Éçfó‡HãÒµT/gOÍI*4£û¾Ž‘½¹{¬Ç€÷'Ë6ø)§ä×V†¡#'¿N 2Yt˜úälò䑲™:I,ªÒÆ™�ÏŸ’år’/�²±æ-³Gh™Í¢Á([»ïµ\;‹ÄYEÒ @ûþiÅHŠÏ›ŽxIÑŸÞãbZáhÕ†ŽÛPJwˆ“_d.;µÌüž¬ß;ò�#|Û4Ç›+j£ïrY„*ÇÓ�6ž±d;è©£ÞåÎ@ÔÎD³d‡as$S3¦¹[ �l¡t‰µûÜPY;×'�þ¶ÀÏÖáEº7�J»!p‚Ÿ¹ ýÕ“Œˆœmãuð­x\0‘ è],}³¶ìÉ£¹Œ á%‘µ…­ƒ‡›Ø4·=˜ñÚuû0îøø@ÔÉr2ÙŒ?]•ª÷KK�ùjÿ’!¦Œú @\£ÂC…™Ÿy±ì‰.Â{šJ„¾ ×2»€7©¨�„U§3 Yüá¡$qxþFjeÔt�‹û×.ïžâ⟒Êߌú”üæÕλ•ggc´˜h]îY)ùÔpÛ”z¯ÅÕ‡‘‹¿J÷ËÝ9uä-�N-­s’ݬšúîfd¬É�! ¶5™c€M!Q~iÍ»©»ïÃ^Ñ; "….Õ©é¬(Ì'&�_Vy`x×XïÕ=…àã;j,ª�O;˜—ì ÎwàT,™$NÅEÄ7ÝDº‚‡ˆ¢ÚÈ—SǘDàÁ…�rûÅD�Äãê�nÒâšyˆ³©’6’ Þ£HA¿É1,Z$̪myîκäöëôGQ«‰†¡Û†!DP�6fŸ]Qçü„«˜ˆËÌô¦òPuíc&Aq=í•/[To¸f™,­ R¨ �€œËW¬¥ÜýÔS#–;xZš¤;XÒSÔú6GŽr¯„ÿ!Rm�ØN•ØüÆ>R”wÓ6T2ìÊy�Ç}iuU1ÿ^úÊÀþ:‹GX˜vøé âš¾W • ¦¸#ê²í»j} JïTáöY˜ãUÝ2ƒMÍ ì‚?=âÏ]¬zG•9ìY23û@K¿Ò€�­‡£ä~ý3¼ÏISÍZ;“lgOݾESn¥X«ÜÉ(@PÂ9Ú4©�zšž²œœ A™©�ë=Ýîfó{{<(óNƒ€»÷¦¨ ª$ûû³n%cw@}CùØÉ«FƒXUò¥PlÒØVžš2XTÀ«ˆhó݃A~I«rìè[ïÏÚk!€îªuÇ V1©ªnYÏIÍÙÌ¥ëÛΰô[D³2¥;šžÒhç M‹cüDsT¦ÂOÛP²G! …§ó†ÏýK™—·£÷þUûÖàj„ùe0‹Æ±öy¹+×·–J­Ýï÷“J¾_gbýEgOã h³.ibÂoñU#]]f‚·GwÐJ½ÀÖçíÜ3\üÞæìâ�±;Âñ§N=úÕªÔ}´�FüÒëÏDj‰î.´Ç§‡ o #?æ›Sx Kv±›îŒWòfú�7 ø�,…Ÿ|Óm“Þ34–5ä­ŸÝz@ÑÜ•5•šŒˆ?ų MØ—u{û±Í–Ÿ*—�S­ú¼%)I–Ú[�z´mmBJ§Åxþ—�9a u«F6H ^ÞÅPů¿àU¿¦Q �uQ¢¢±Áë4|–@‘õP¤Ûp¹âÈ¡p˜¤· É?r´ìrU5£~nòNÕM½Ñ–̾½å�2këýö×uÁ'FYyöªxá¤aNÆ;²¾*”‡I•3ñ*F÷«jÈù0·ÒhuzÔÌþóÛåëö¸_Ä= �`ìçâÐÿ° ¢Q B¤‘ 6W¡ÑØ °qݨñÃ,1ÑÀS&±ÛÓ6QÅK0Ñž ½±Ë«8à]üPÌ=ËÊ{Òýöì6lÚYƒzôX@«mŸ ß=‹û9äþFp6¤*1µÞ´ñ¤´x8UFw�Œw=HÖüµ%01¶Ž[vgº¿¹=^xÖþÁ÷f®‡†'ä~¸<\tKà9QÏëO=Š™Z±\Ë+K Co3AÔrΈAgÆ8QLB"ýG˜lL3|Ög?§. ¢^Aœ¦¢‚¶Éôpzz+å\ Ããêz"u/9³8`µa¦÷l`vººÖŒ;š\ÏUåxFfz�ï¸xBb$2DÓƒ´4øŸ1ù *šj X$éiJ¹àIÊ5ª#ll 'Ùž=bHœ]ãT¿�i7 �4?ÛvÞ7û±òñHajòR‘pW#“òリlÌò�ôô’<Å<«Ôô_)iÀMÈæÆž¾’=Bºr¾Ž®Þš†¦1ÏGoñž‰¹"e'èE'&%*C§dhæ(æKB(»¿Ä$åwp‹Õj`•´s4²òóâ2²r,­‹òSÔ0l0*Ë!ã²Ô³*ô�A_ ×ü Ø’1ûi:¡Í9‡ˆêÌ•- ÙÒÒ2RÔ3”�!ÊV …çf³š¡òü”ûšÆ†Å)¸¬È7G1î ‡ªÚÈ܆'4œ?°Íé‰0üï Ìa%Ê%f¨ç—›O„ ”¨óÂ/*ífb×'†¡å 4*8š�|ß íßr|%°ˆŠ £ÍÀÍ]}IjØùgÜú!çáP²¼ë²1E´Ð8H‚Ù~¡éƒè•Â^Ò»guzÅÝßó�À^^kT·s0?²å6œšæ|Rjz8EIÂtA�R¹gyuÅ: èŽÀN^±Û5¼ny¯9øÓè­@œ_ƒü�;@Y€†Ÿ¸nÀðnÀ:2h@‚F2èmÀFXQÈF)4f²F_ àFþAÀD_ôDmAÔFGÁxH *‚FPéÐÄ1 {ëÄyNŠÄiEðF+ERØÓJ,õ@-÷¬Ë­É?.ð‡!j­ã?Â$dâP¶|G®Ú8ÞíPrW DîY¡÷K¶#°äRTÐJä»{6fS›Y£CÈsY<±?³GÅ@RÞšïÚYáÃÃ墤Xœå‰øPkÌÀçÓž.JUÃéªå÷™«à—6)1Lú[]$ïDÈ‘D9§€†·UsæýÝß_HÆÉ´â¤kdþ•Le‡Lq à% É~AüwB<~;GòëA x í è-lMœéÌù ˆ-l�¬]ŒMxœœ�-ìþ¾Õø ‚ dj׈܈ õ –ˆ‰LŸh‰ˆš£�“…ŸXŽXŽèdáibgJanìHIC`ghI™˜Ç†›Š”�èÇÓ8€Œ�ÞÉb�›H¨„Ò­t ØÈ«’ŽõNu â‡ò÷·Ä9Ì �Àr¾GE$:=E““CIÙòåý$€ögÃÙúš¾j(š;v¥p8Å—œ�äµu3+h$u_�xz¥Lwfy¸ºt`m)þ$1ÀÖOA>`T:`] aâ !J¢"Nb"!b" zã ! Q#¨D6Ä}*Œæ0ãßÅKo³; a]Ht“䆶7=)>&I)ò<1I1G±Ò´cK\Ÿˆ%)QS­j×Ðeü…  V‹üz ÄížµÉXov¦×>îÆÒ¦þ%äã]EŽ>Þ}TרJA+';¿¥MÙû 8Ölôïo*ÔüD¤Æ:Ô�BüŸ‰Nd¥„sö°7161%prvt1rNœ‡@Iü �–ˆ AÒ¨� ÒH�›èÍ×èÉuÖø[Ð "Ñ�³±^JÐÉ ‡æŠAÏ=ËFiÖ Ye$¤iJ“VZ ɽšå ž}SD"OÚé<ÞP* ]teÚ4!&ºñòì7¢çcű¿.È  ¡û]{·û=>�瓼½iB‰*ÏôÔGÏk7û®×,¾wÛkO¼N”ýeØàrœ 5ûx æ[¿î�%‰�|Y*ЫŠþ§ë·tJU¿\%ã–EëÈùÁêc³®LyÀ3Ÿô2èÔØ\Ž¥H×a_b›nÔU…!„äs ÌçMÖ0h =$Ô! oÑsaϸ1Oi ?Öî ST©»½zê5Ø7E–ÈnÍÁíóV„˜:üÓ²íhM¥,Â1# ê]j·sH½fJ¥EÓ�c/kqFž4< vCïïb‘ §A– Bü þœÕÇõ>Õ ô»¼¦ÄŒ?íI»¹Þ4 H$~´{l\j,í´[ˆcÑ™››7(išÔ@(ýˆs‚B&?CŒùÛ][ Ø7‹mòþ‹¡XÌ~öDAΩ@>C I÷ßX®øÁç¿7o%í›ÓøÑ�1œÖ\qˆ­©6gÈÒ§‘õ¸T«ç_jš4§WþL�œ#§0‘C~Z¬z¶QÔWu×mÞ¦)`ºÞŸ–£z+Ît>¨’ šé•|ó:61š’Àž3žÅߎ8 3µ#h¤d&¥]‰¥FlÏ È®ç‹WK œ�ÇË4£‰(žïT{c­ÒwOè7»âB7oá#ߤ�þMD€*•?Í–Qêu0 ‰¡ Æ‰"`œ�‰`ø‰‰´Ø‰ž´P‰LtJ‰o¤ˆ‰it1‰‡¤ˆxrª22ÜP�fÎÖFVz¶ÎdƆ´|ÖvFV”܉~ô~�á U 4xå× Fÿož�RÏS«Ãm¤µÃO|#QKDÂGOô#ÅNL &Nœ%}KÜ'Ó±0% H EOt"H¤¢ØEú÷µÖÎ&޶Î&Ndd�” ÀjáXGÿò¬B �õ NÊ8¶zù¬âºwæ°*,€6>ø£Á¿�Ùâ�EÐ`2€VêAݳåiÆdÆåéNè)¥hfæ–¤«%ŽfÇe¤fi¨%Nägf¥'eV'e§N&ÁDè¨h»ŸŽ’D’SáK&Û°}1~×—ºg«õÛb³#ðfê1*DhBá´ŸÕ[øµdaÙ£ºúm�Xº©MfHÛJÚZ³Žõ¾Â¶+qÉ:Ç‹À™íty0±^FKò+o‹»Ž»£/ÛÉóKѺŒÖ¯¢.ºì�ðØ«Ðçx'yÚÐÛÌÆ7U–cj'û(Ép‘t°—�hãÎF^7bÍà(—Vîágv·wM›^ }Ÿi_Éþa¹ÒÂ_Ä`oñ­c­N÷®ýÁûäÞ“þSð@éB".aP†‰¦Wë¶K™<ÑÔܳ€ô@ßg1Ó‹Tî pE•£€ªN^Ãìz¨b™^¥ŽÛBžuºØ&ÞPú±“qà-ç¼™‘6¸=ÚEM úÐù�SÂGP®¿µeÏ•ñ›ÃëA(ãYßšÖžÊJA¥Á”ðÚS�€Ïdu8­Ÿ=üs£t)ÜÍ·‡av‡eU[·®ø0Ǥ|û`ðú`ö˜ÅÇZSRªQÆéô²¿!0G�ÛìÁ¬bÐÔDV¦Qq ÖídØ…¿ÿjQ¦Z¯¼›r»•OÈäcd›>jðÐSú|gqÏ®þAÀ»«n¾ã’}X·5B»vÔM¿°ÆŸjÄw¨�ܬÆVkÇøÊ… £Sô‰„#è–ð!O¬:N2ÞÆ©@„){GɆ¨h€ aF¥ÚÍ X2-zqŽ(’‚Eètןö2 ÕL0œaNóƒ›&º{±p�D} îuà7 õÈr� Ÿ ê(„vÅüˆ”= .8vz'­`/KÆÆ^¨l梵ŒŸ¹Ây>r}DÓc‚åß^œq…TY±1z]CB­,ì³1x?cí"BÓiôÀ\¦Ö‚žÌW3³ÈĮ̀óU\ÜIÂ?Ë‚_^p¸Fîk„�´{;Û?Dk’8ÈâZÑYdݨîx;Ïcÿ¸uË][—ÄÄÅ�P²H•i7ölÑ×ÖÒÇvžÂK`˜t��÷FÔúaë4¯ó(ù"18ææâlqð]í@¾Ýø«=ðj�´e!D‘§=/Pð{[ʤ?ç 3Nî²’4D1nJpû¹WR;ì`)KoDïóØÖ$äQÙ%|P"ï§[dñ�Ð¥šýº�E§N‡güum ZxïñHKìpLŽ$ƒ|•‹&;» é*—ËÿKü)Kw±�W^nv³¡ŒcG xÞm�©ª}b‚�a��ñé)÷L~ R�‚!Çõ b|t¯�Æ;¡vѱó§ÇÝ©];›~¦À‰/7Ö„:xà(G×g'W�f6òkžY;°„’çz]:H¡- Ò=«Z[Ëi“£ÁWwé ‘¢w¨Æ�%qj}2ªµDª%Y½3æ*ùÈø«I:þ�³å3Uõ}¡Æ©šÝå…žé:…ì�®ÚñÂRñøÝ$ÿÝ|ÏÌÙkè¿ð³�H/cË'ûë�oÞD´±« ¿k5úËâ–_/œB˜^—¤ƒÚ¨šm·åÉt!UV¹÷„%ÔÆ\�MA*V:ÛTcG?¿ä ¸¥dçMZ+;µ!~¼ðÛ\J¹4Ø×�%¿Ùix¡EãWóòì[ä|gÅðÅY©hÁy�@|ªÞ[´IóúIð¸ÊïÜ¡pTèE}¬Æ±É&%ÝGSZ-äÜKg#‹û¦)›”Í’tÚ\i“éýJÕ͹½ìX1º{]¾KU›´²”�¨q s¾ÑïØ�áD�lU®mÛðÙ!–üD<»óºëbÝùõ4»sS­4êks‹�Ö…LªÉ_o �ÏäÑÉÈ;kh9=5²áÊfxBæa¢C­ªï}ÞÌ“-’è"¯ÁJv?zßÏ�»ïƒH Q%ƒÜ_wî.ì=+O?æLØí€O§�ÜH3½MÅó·¾ßo¸ák.ÍÛËo�ó§«èOû¹¾râ¿° Í–+^W7Ñ6!g^|E³£·Â›F3ãM³2ÓÆ&Î%S½]½rκÛ[�Üå6ä¤ Nk´dcðм1mªÅ¹ 8ò`¼Z _¹~å3ººß/ë•"½~Ø“ŠR’~b•Ìär+ëPŽ=8…Ïá”(Ô¨ ^ÙßYL^^÷é £Ä K\>ñÈ1EÄ ¸ŠéÀïOí‰Ùï颂õ|ñ]s \zõ»¯M=JM©ÅþòõCñ¤%30Ÿ8/Ö«XÇz$ùÅz…¿G$-å±Î]Ö< (U ‘ªNTÏž½{Q}h+WìFj M�Ó¹7ÒW ²Í>”'‘˜ÌZÜ= E‰æP"©¥‘3,&U]Ø"¦C ]ÿòPÙW±¤°�ÆNRfMu󬌓Ú$²qwÀ‘•Âq+}#¢ûáæJ.¼8 É’ˆ7âÿ` £o<¾ºÙ +\\wm'C?QÏé¼FùZ9ê2jÁô41'Ò-%åî£lµNá”UErÒÿÅEÇ —/†ÕÀ…@{èö:„Ù“†JY€ŽÎ«4Ï#Œ.ª“(1—�ö@U'x°¯¡¦çQý9/:ÍH‰è¼Øµ�Îfjõ{þô–n�>üðÊ){H)»·—à;>‡›1œgñ’‹¼$@<í$}© ~�Á)—aú ¸“�oÄ�·ÄS]ý¡0 …|ää1úY«¿Jm#‚ǪÝ�Ü>Ó¶ Ý–° Ddr*¤ÁISíÉ–¾é�4fx&ΰ‹ëŽ1 x nñ;PUÆ`ÓZÙêq®¿RqFsM= LÛÍXdª�àvóf1¡�IS¸„\8Š`µÇ@Oó—F' ÓZøž³$#`ú �¸^rw{Ò£¸�y_ ŽUµ9zgzø4²„å å˜Ë¢æÑM¿tŸ÷N·SŒ�íž™ä�¾Öz@—ŠÇÈ¥Y]LóUN:¸»˜ÒŒJ9[|ªÆw‚mØÂrl8 Ò� €µ[j•ÍUèÊ6ËSâVœÅ7\ÖàGœ�d®}ä4SÌ U䪹q·ì„B˜‰î>þr=ÀjÙ ¾Â°ûk„Fñ0ä0)ƆèÉÍÑÓêÇc Wz*ÊêÉ�~O³džEF�Cì9Q×fèëj* «ìÖ µn‚ÙT€¬…nƒ¸rx™�”¹ ˜LÚ|µ H „6†à¹ ûÞ8yoFy=�ýscèö˜ŽêS‚=“”oE “aänlˆp ·?Ùþˆ̬Ә˜Ø`kþ3X�„ôóHÄ'ÄÌvœßUý™6½ÖU«h�œ8­'$]Ïh‘²p¡lˆ˜qŠ9¥SGl 4‹ÞBÚ„…=g}Ëz1‚¤€p;”3žq ßCUëH’Çc!¬'‰JœëZéš0Aãt¼Ñþüy.Ù5îC§I| P¶W¢K !J‰âaUCŒÆ–k\R“ ìh&¯™Õ$vg¦¸²Õ­³ƒús€x"ïвoYqÓ<æ¦{±CnÜEו¼t »†œS¥Ã� á©?‡¢ã<‘ÈgR"²Fù¥=v9kbÚÓ6·9’èÅmbljÂAo£ˆH ä=ø"T”=ç�þ&›š@¦Â‹16¿‰îSÑ%<‚=9ã8ãY‘²_I9!Í9°hBÍ' r¢°Á±#€¸4Yúüél‡èŒc©Ð6¥F™j޳ßÉ€ê; m³y.ëk¹�ÎàSN÷^0]U¨�ùx…DØ �*ŠöSÌÚ(VFºfhó”$û:}Ö~^ú=nϤ¨G’‚-Ǧìœüž•²¡ã³í3I¤ÀØ#Áƒ’ã|ÅI•Š=¨¡¢üÏï/æ‚`tS´¥œÄrZCšöúâË' �¬p!þH šÇ4U©ÕЉþ³�MŠÏ4¹¼–йý>öœ¯à¥%ï¤4ãaK@ÄÝö ¤4ì]£Xøž îs6¼t¹zÙØ¬"?è¥K8תŸ¤ÿê¼ØÌ.v?ηë;>AáZϱ­"Sæ8>™ 6Ý¡Þ9]/�‚?I)¼hä†Ã_hâ÷fßóØêHA‰™oeØÚ ŽoŸÈ¸]‰%@g'8ì�ŽSÖ®—ƒ‚R€¡ç�Š÷ZXéaÙ'ê 08+2!¿H�ñÐò‰[T$š¨ÐçËMƒ×à`ZC›áCA«‡¼° ëÁ2®ò³ià«Ì¹ÑÎ*»|T�7�>ª%½À"žp+v´°$ÎZ!éËy š.È¡zfJÃŒ¤‘2�[Ï”—{• †°€eÏš�áðQ:ÛºQ}àÄ�Øék“�&MÅ#ºŠÔØ77i�L§v ŸÁók6¹Ë\FWþd²V°l7“Œ�>"¥@òSžíeé3àæ­cbˆ|ð�U¶*8\†‡J8« PøG¤Ät-;KZÁát–°w+œfU�~¿D0OÀ†(à˜¸¢ð«°tmÀàËnÉû¿â b";®ˆÏññIùj¢?�óö§!WøT�€%ò}Ã!uôßUøVD¢Iü Šô´VXBUgËܽ’¯·5 Fmnõ…s2fÑ,…+Æzväõ¯¼µÕËñŠ;”âýv÷í•ÅßMúD�Ǿë±>a½ÅZ¸×aŠä)ÃÄ3=©ˆËZ €s~³hZB· >bË硆úèÐr˜¸c/ÂÊÛL ”ÓAHdÏzÕÄ$Bí Z}n»†µoTÊhž‘üÕÈÁ¸H²ã ¥ ’4å”äa8’×Î�gZ7Ξíëq%­j—_ B�‰[#¸Fà ¤P˜Iu&â«<"uçUð¬ºFG�°&lÒ?åš­Ÿ¹½Á—¹ÁÁ|þy™Ùgþ5&ýíä–SÓ5 (LO¾/s˜ò<üx¹”#k¥6òfË4'ÎáD;˜hIÀW³û¨E **®»ÆßÞ[€°ótãŸG5¬ÚQÓßýXfÇt<”¢½Àå±Bßå¢ þ¢x™[ûÚ¨d¦JDDÖl§¡k€ÌE’�vÊØô5ý>Ž<‡Ã2,$%FWÖSUë¸bw^8k�R§¼+D‹¼Ž\u4NÔÔ¢uÊC F'|7MI[,tHŦwmŸmSTRÝõ““À’…bUÓìY³Dl‘6÷‘ÿ*Þ¢l¶ÝŽošð5t9@Âï�+Ȉ¿„Ìgó½Á1è*†Þ™.#€:s^°$ûA'ö‡•`)ZrZBñÁW œŒÖkÇ¥zTh©q5ôÍ6<¸¹ÃOH„Õ’vR—äXÏå„=vÊó:>V7'ÌÄE ^P¶ÒÎ�Í9WbᢿÁbÂÀ-«E™Y¬Oñ…mù¥…m—-ÅT¾•±'ðÁ³eÆ8™8˜bß«4ªo4#U(àµÍ[K°™ÓÑNÌ΢•QP’`™Ðí¤0«Óüs Ã$I„Ht†ñËFÿÆ*JÅK•'ªˆ×}²‰¦—Ÿ]ñrÂ@UfNÐ'ªXg«¼‚{z žÊÓÅ}Ev!M¨E-¡Ýæ?ÊÆì†àl”®»õ¦A}žgÆ@ÈŒh—œ�Ì8˜ºöî^ô$ƒ �„ ý̺Ó¿ªý!C­ ¾k]Gæ €ÒyÞ«¤nDñ� êÙ›Zø¸§‹e#LÜ2¥kâHLìeŸ)<Ú¤]_½Fi / ýˆFÐ[lýQ¬•ºH€äs`guí{%ìùŸ²¢]¬u}»E¼B³¸&ßãpxàkŒçH‡j¹•ƒ2Ê®·¼î5ñ唦Ï�aÜ ;EÀÚè{™ØÙÃl‹Ñ_(]dÇüÙ‡ò|Èß»it¨~¤‰ûËÏ)™d L±ôÙ×pDfk¹®D@þ ‘ÐÃæ™TêM¸Á“ ‹¨pèKÇHuïÕ)Þµ�ÆJa–…<"¿'x0­$›;`B§f3óR©;5ÐYü‰�(c¦�¨ßA¦ZÝ1Ì*ï#ãˆK˜HÑXÒ—Æ^& ¬#AÉ4Ö×~Ì£atæ Ua^Ó)¿ü[Çæuª�ëŸLÁ™’ÙDDgô�ŸÂ%§ÇÇß+š'Õ¢¡©—`+©Óm£eD�„*AdpJ d|¶ýÒ§¯$ˆJÈ'J‚B÷Â%åsðâù#.ƒö<„äÅy¨)'’v@ÿ=Ü9Àšë«¦I§ý‡¬çô™"t„¦ÃE¢Ë¶× ZQ‘`˜€Â0ÆÍ¥½¡áƒšfShË#d6qñvÎzö ‡8¿} ¹ãdò74[¼œg{ž[±)èŸý~Ù¹©�^ï5E šà(Æ>P'8*§o7bîBQß�$%jk0‘³ÎºÅú†’MeŽß(ÒÊ�£Þž*¬p²ÑE@È-EDõêÄm hé [†º(à=¾G¨Ç ­üÌ!.¾À·V �˜ª�‘ã\BQb�L^›Vvþ¹žæÑcý/µ¦U½]DÓ™l{xI,§\ò¿U•{.bÑ3»âûÔ™&ê²-Õa³•I»ž���#}õÍüG¦‰ï’·^[á‹´’3•ëÐ9ûï²C;‡îÑô¬R ²iƒé±±ï�ÓÖ]á¼Ê»6 ,Ë·v':Æjèèzú\NÐWåÁGÉáAËbî~±ï$™£ô…œWL©`…³ù¸ÜìÙ%^3jÅa*Xþ•ô;Ó*þü–‹`ÂxRŠ+‹=Éþ_aÓ™ %¤ÝThÒ—æáë{r?RŸóEbù [²WGÄeX5Õ Ó#/¦[ªcUJ«U*¦¿€ë]�±@þÊÒ&±Xqs^°/¤X9ŠPw­ãhðOMÄH²Ö!?…ª€kv¶®N|;[:º‚dzꡊ�“~.GøûR?úÚë _{íOTÊV¹Ð ðÎd€lÑI¡Ò¸$ùO”ŽÚFä‘sÃ.Ç9i ÊñüÔë9; ðza™™xÇPal¨Ýš¶À� £¥'aY ûI]`{ ÉbfÛ�š× IŽĺ§hø…y�V¿±}D JYj…ûÚ8òx�x�Qƒõ<1’Äœ•OàB >ºŠë¼IÿþÉ Û`¶µx™÷›ˆ�®cíîdBQÅÆN]Ãäž$çn"Ú¥0Ÿ…ó5mnP x o˜ùæ []‡ÍNY ÊZá1?á�l3ÝûÎÕ8û܈ÀÈÔóÁ’‰ðKú¸·ôþWJºÀñmÀ�7�´­Ç�2Y‡Ú1v1ôcü «.ž„0Tv´¡÷b_Šcô÷ʃŒèf+74ÁŠ¿¼+`Øõyñ‹¨ôêã8 P$úiúlà̃ïWzT’Ð<}ý°4þàÅf¶³s1qžyŽ+›÷lÍòÃÝ †¿ìüÌ€ƒüµ@Ì“c˜ú±žúŒƒ'1qÎl¼ïà¶#+æçUüƒ“2Íø-¾f*zVÛmá�=ÿ{@þ Ås÷1|®ë‡Å+aôš„ OÄìlûV\˜4{°Âãn/ò�AC3€¸ÃËk%Ø/Õeú(<‡'ºfô”›t¾ ±©&õû®T ÿßW‹¨€ÛI'´B¦'n~íÕÍu³º›À?>¼õ3(9šAYš«B•C>D¯Zºÿüt�¬ At*ÏàIlŒ!÷¦€ú2±Á“³ëæeÌ{ ;x¿uð$0j~äi@Pj¬~dëW{~¦uñ¹’•{k~•Ø[TÐŽ1_¿?}ÂH[U(†nj8šèÉxpØ·HL'†ý>ÿ‹×²HÂrd¡Â\åøòû¼‡sÔóà€†tÍe$ÓTÖrœ…ƒuñI^øa[+9Ë>aÞû«�)Ä^4®�ýäBw 6æg pZ´ Æò,žËñ‘ß-ï¥GÀÇ »Îg¬HÛŽ® 5l�ÃÿÄvà%Ýb© æp™§`yò<¦°€8„dÓ1°Êgïîyê苳’v?´¤ñvÚÖCKÁ¦Xd8¹Öž‰ìïê³ÛeÅv‚†/=ä¸ÎD¦„¹önÉcüÖð6k½YTõÔr®aßžö)9ïoÃ>X‹�¨…|œÛ'ï»ã#ë&¬…J‘îÉ_7*™8)[”NnD±W{¯ãVûó|É[ˆ¸§®éWê¿b>ÿPYÍéª#¸oл릺ݥ„þ¼¡ “7qêÞyòÒCí·ÑC´…Ùñ, PtK¤Ký¹í4Ęsܽû�€ ÏÁ„K5ÐÁhU•9x¿æG-GÆ2'¸"B™w;…!>Âý2m÷Û—ÊÞ¡m3žkqBšEúðÉLÿüÛ~ž®Ï.·8&isG4Óhs!Y5ê¸Å¢ôô!*#w½ATÀl::!=ÔH|¹Ð÷õûKõ{Äß;SƒJCâAx 'ˆ!CKׂ>Î$�o¨ÒÔIi&ì€ßσÑÓßmZ«]°’“éöxn“T¼o€ÃíùU‰®8¼Ã�˜L‰Ô-¢¶ˆw¸QÆ—\§ºéñþ;83–Ë)˜é¬›·£§L¶GSkúâ Äs“c´N­Ë³_ÌÏÂ�xãû¸¼¼Ê(Cý:Íw⹆–pé_ã¹êgn�R&ô8>j˜rï8ÉJ�x®©vÓöÐ>¾Ôìåñ‘rߊ¨ësÝêÒ\„&’ÓåíÒsÐIì|i·µN8Ü&ì ÄN ¢?zt~º °¸%J*¾ÐøAµ¡Ê|¼I¤xŸÔˆ‰¡Ô2LßXo³ûAo1ß:U\…EÿÝAEjÓ¸�Ò1Æ©p}Þìè{ÅóLØÙÂVÒ3ïסŽz¼¹.gù�­¥e\Ýîe»¬)s�«UóxP¯±ÚédyF’A-nHYº”:nVlªlÔ²èŽ ¡wÕdz€»€Ô‡ä³C_£•EübN|`+�µÄ7/LúèVédAuê_‹Œ—/ú²~²éø²²jõóIáCÚú&%O»[¨|Xî!*W=ö¥ƒ¤‹à‘¼wÂe}AëæIjk¾¯ÀJ ©±]"áÄç½{ŸDiZÕ`ƒ ÆÔÄÊ(DBÙèðà íQ#2±Lþ {z‘V÷qPÑöB‘¼waÌ'Ôe”¬ÚšO.Ü>æ "=Xy ÿäÚÁ<àðó9WÓ)?ÒsqHßøÖƒ³téoïrÏÎQ×rÛGò-õÁ-Â!®ÃÉõÑh<Æ€LwAGäÚe?€¨õ!"‰!ñÞx8n³si³ÕÏv?'vQñq¹¸¬´ä˜¿@X æ}ò×·ž¡{=÷vfÌÔ~; Ü$Ù½J§¤7¤ V» ‹-ñÃ_�¢àÿÐÊ}…wà&…Ö8™w„oD¾\ÃXäÊí @ç’Ý‘Ù'ñÌb¡f®žÐ·ÞBwúò¹#iºÍf’™$É%!Y ºEù&ÑŒ2©7D+$iŸÊA¸9$�‹g—‹Ÿ€$í7 Š.„$Ûn$9QH·Õ`ÈH—k!Ö7è7Dö|r…x‡aÌ@1óžù £ñMî'é4šönÀ-¶óéă�IßãÓçž0·žóDGobq ô—ö:ŠZàØ—< >§¼�øuYŠ tÜÝv‹‘ÉÄ8æ`‹#ש)ï$<'s^µ¶*ƒÖ¤Öi ðw]’̼Ð2jmØ<"fpC5 ù ¶ß3âõ®§—Yäß³#®}ý†S#`ê÷Vß-ˆ²Eùôî@ÈÏŠgRû.-VXò¢ï”‚„æ¹E6ìí:‡µy£ Ð,㯛·ûž»m&LÍñê‚üà! ’k²°³•“9?�[ý¸;qa�tº:ª–’Vú]„˜·œ_\wÈ`‡M꟰ßZmÔ¾uBoä:õ– iº†7·¨Ù6ýaoS'‡�º-LëËIíeÔÁ$]dX ²_¸e€*$¥Ûçu±T¥N)bFþù"Ÿ¿˜æüÔƒköcÅú í};ûø+pçwHÜê›àŒðô 3×*k÷šY”èÏNFU8ñ¥Èf_ÕL¿ÔÍ$åÖôàæK1ëüµâÇ=:X�“EͲ�¨Gî‹w%P/Ž©[/�"rà6­æÆ-­–ZyæŒ7¸ÆQœD²GuÊMãó^¹r ¸6ýè€aiC©1Xãƒ$(ÕCßøJ=™“æôüÂPûÐ�}¹·wà\›<¨5\æèÞUJté÷x¦T<¾Äm\’úrÁÝr\µöv=ø+¬¤”/óR”•üdç4"¡«ñ{Ñœ»å+¬]+6¿ûù$,µBÁ–¼X‹—Ï„­’÷Éšø·UùÆ%«jWn8Z3Ýa’¦l!ëÛ¯¿Um"ªÕþº�X£è2�ÂÂÈ(!¥?™Xøç}_¹æGkÙ€£þbÓqË/ìK%ËM˜+Ìmu‡›˜ ÁЮ¥ˆ�$ 3[ËÙ�`˜¦ ‰Jb8“ä–ú‚#¥_©¤+W³#~U±É–ç!xÐÉ_s�.×�†"¯áìªO¹m˜÷P�2ì_2ŸW˜g÷ù²bóLÃk©;b ÜÒiÆf}“i‡ËÌP`±¢Tü#‰Pb8ñ¸NfwÛè.‚ZÝ¡ÆÊï¡Lº–›uÞÐ ÿ8bCðL—ÞC¯’´ÛZ· ©êý£J ¸[rh vn„æ]Æl¢„‘ƒîؔ酴•W¡7’š²/©´ö烦©Îl˜i_›Ý vg®’ÌÇÕúæÖ*˜½ãONZóÍÎ믶]Új_pƒ}­s/Û9 ÜÕÉÓú¢Cû�s6¡à7jOZ‹.qt&]$‚Ÿ,~µ Ò¶³b&Õ•?“Ê„|ò,Î ÿ©ÞñÄD�…ûyî1²ÀI\xIw¾¼¤”’€¹,ø}8Põc¾ÃkÙ·öAÕg…k)M–¡UWæê`ûDPæéø(`-Í=”  .œ1ÎøäÆ~/'Qb¦º1/|pâjRíçmþþ°y�q‰^ ˆ‡@Î"âÈ…ÓGÔŽBÏ^¿ž ߀WÊv—Y`äJY?«9І¡ùÁ½ÕØ 1Ž"Ðëù ¡`­üq²ÉX»…Ïïæ‡£sÑž¸cY2B0N±�ñv¶óþé2ûö€»5/VÜÔ[Âðyú¨q�{¶Îÿ�´™¡É l­7¸*»©�^%(^¶D¼ )Üõ|nL†Jüè ôí⣧ á ú,²ñ�»û¾L¬$^pjäd&a‘€oš/ÙŽ<|ÄÚ¿‹Æ®þN¯«¨2€ÀÌ@GoûÏÜ¡æŸÍ›dͳ¦–�pÛubV Û¦0}=d&’g§År•*w‡¬ ø)°Q=ÁšoJœa-�Ã0Á¨Öx‘…?û ïc¥eNÍÇòãψ‰±3�¿G¬zÜr|,t]%…)«£­OÇÊÎG/÷\ie½”Ràñ#º’ $k¢:C-Î_":¾Åª]§÷­âÐX`ðgkÅÿ:‰¹e»Í†àÛgXO©?6c™;ox@áºE"“Mw˜Oï$¿X=L#RIrò‚õSðË¿ðö­>'¿¸i�²-1s2=�Qp»Ô% ^©½ÑfMŒ‰k˜¿ÎPÁË6;¥)b¹ŒªTS·¾˜²Dªä þ&"íUÙ¾,À%•kuBmcU lß²îÙë飮pAUÚâF«l…Ëìm`†”_WA7Å¡—¶-Âø/]ÿXMùºž>ÿAÆÕÿÓNû0³ÜíZ  „y_›‹¯"ÒqõšésÂ܋ƒÞâÌÓò A"¶� ÛñʸٙÍSçY¡€½üî'dÔ&)}K3 ¥¦Å`Ñ5ò–0{0VÐÒ“zP^‡Ëh[I©³U¥õ“W•*Ú2ãÔìÂIöéCÿÞ­’Hb8û‚�f×�ûÓÞÛÙãù÷¨èà ?–ßx,jj,jèâÁ¤~L�Ö¾^-´ó ñk÷rÏ\×0ît•„~mXíÚ hƒ´9F(‘뮲÷˜ÒïCy+œ‚£u&VYЈû‰¿ƒÅK<ü£âJÆE:䘸¼¥2TrIÍHY©9çò-^Ôr%”òÙ—$ÏL…tn•ÆŸæÂ€ ÷™zlOÔQwm³²ýLמÖN€Œ-`,2’ðL‹]î<&ôovÛÔ`DƒÛçÓ¹5º’=ƒGŠq&µ@‰4o:u›ÊFÖt Åt¹±ÑMK5¾ˆ@ÿÎ5œÉÔ¼D¹œçÆý:½h6`T±k¥AâÜb�ô  ½é–o[âÛX'û;’@°='ë n΢JÔ{Qúa�]‰T[|B°Ó´™ŒF~¹B�íÉðŸ4•ò¤Ä]‚œ¬¯¼ƒt�KIÚôe»2ó­1òúm*Ù±Ü98ë)’T6@ &¡¤ yÙýaʬ²`Ïú+c½Á­í–DF­¨Eûªµ fO4E/ã¼íºó"VÉúº!…“4ˆçãù‚v˜)Ÿz™²øêÑþ»y‰§P�6åŲž½ ŽGMW:+|Œ§tç ÌTI|"1 º$Þâû¶%ÆE ¹µ¡ì4ÿ$0Ãþn“d‹3 v -jkÇwðEŽÜyy"Ç}¹aXÄó~¿Í-O“Êäuœnš@y“rv×íÍ}7ŸKW¤î]uÉÙ5ôpSõ’_Â^ýIÌ�Kü“&)æÖ¾md3ͺï¢ÇÅRAÜ£öIÀʾÜ:»ª‚ ²·º/ØH·€Xؿߦ’’„¤x§cU¶Éæt%Æ9•áá“ÜÙ�ØÔ%º!ºµšÊ¦>~ü=®µvÀÙ)挥“ÐTÚ׎éqÚ£�W\�²¬¢¿ªáâ�Î`úªœ†‡ô´­›<Ë)Ž£ Mÿ6-—íÕ�[nî·_g‡Èèj£j_+­l7šíñ0Á‘ëŠIÓbhž˜Œ}¶äjY¼öÝ  ÖöòåÒ0‘ç¨éä#µÊ83†@¸Wúw&ŒfSf½õÌVI2Ú¬ùþ›ðÚΑ€Ã›®‘¿5½f•­=Å÷µÂ°Ä:Íø ÍÛý�ÝÊkJô˜4 ë|Û‰ðÿÞÂé‰ÔcþÕ¥€s ‰>·OÚ932¾ êÎúšèT ¬Úi¥Ùi ÁŒþ‚é——ÐÛE2¸gg‘qª8 ¡`+ù¾žª³I¡P\ƒ¤4»gËÈÊ@ò#ôÌÕ­�±BŸ‘MÉ`òÑÑ‘Aê9#—K¹wÕÚ[ßæÄyDëV t·§'¤’4 ÿúï¥;Ô`´w­•Wéçíîý¹8KÜòñÉßò·-�‚s7¹ö³TžS-àe¯öfíU`¥¸ø4™ä’ åXˆö%GÑeVCWmó¯d0…™Ž#Š‹¹�bZÿþš×é/æ›ðDˆµ›vú[�DãKÛÅŠ9Ê n|½Žêøºæ³�Ôq.$²{”:¢C´BDäCd„‹·Á¦Ö§âÓuìhÛ‰ÊÏzÄcó2[ø¿XB–^[�&azózP±ï0_mܵ—0*Éh™ÉQQ˜ù“l1˜8m³úØi½3�ÖX­~¼ñ¤ÔRêÿÄ ‹â½fê²ÍèElÛ¼Sz⊩úŽËã¢Ïà®ØJ8Eu%;Ô]Wƒ¼d·j‹('õçu i¦&ùxÔ �?êÆ¬0Sõ*´­�d©/2Haàzà“ån=¿Ÿ÷~\¦?p0_мóE�IÐÌh)ãž^3_üûR7*8ødš‚9ˆT”êw2(Áðoó¾ì_~‹)óR ¨˜ž©ýC§Óíß·º},e­ßpD"û(ø>3”ƒ I‚ D ®Æ2*T*;4�æº7Òxv¦œHi‚�"Vê�þnf¢ê.Ô¼OìYo³‹³Á•â3ÒtcÅëâ° ÿª˜7þ2Fnß{Íñe|û•ÑÒI¯‹W´ùÎ,CeX~ÿ:X þÆ,<›Dò¡ž”ýoe©P}w_ö‡oš¶ž§ORSÜG›ÅsãêI::¿0¾°ÿšé¼cXr4 i.ÿ{KÀ—þü®h‰ÉÜ®[rú¨¯áo7>4သ|ü´½2ûó¾ó�NžœO¯œÃ£¶úZšWÁf”âÔcR£b’@ÆÔ�'ŽÔ…þG¿Þ¯* �jH— ¯ŠÅ”~{îV ÷Á¡ñÁ.&uÉI+Ÿ|Òg¤¼jn:°¤ŸZb%OÊxÑRüœ±ue|�‰ef! ˜K/îØë�ÇÓþÓåþS‘sû‡"¾jŠ1J Yéyjªñ©ÿ¢¾vÒ`Z¶Èf³[Êðk½ÉEqø£‘RÆ@?·‹YMöm«^£)« ¼.c£7°´‰‚%Õxör Ë­# Wšúž]K„’ÔÚ›2{c$ ˜“ÑKR`òi¦“•[GwUÇÁ#Rf‰G* LT$QôûŒšt¯ãaŪôÄ(ÇH}7¨#è|djÉä ÏÜM_öOÃÌÉmÝÇö™m×®˜j€¼»Á‚JÉ�Wäë+¸+}–E‡Äø,/°¡ˆ‡inâPÂü¤’á63>§L»ãYÒ!×€)“B1ÈÿN@1+β,ezŽ(�0¿ÉðÃS´4œ–šÁ楶2à ¬˜0€°™õrêÑñ„wW¦¡-Qfª1Eµ €Ã„Ñß�üß896v[ìH¿ÖPƒQ2ÅÂ6´SôKÏñÅ;Vöi6óè,üuÔóH6OTR¡c]={¾sñÉ'ë‘‘:ù1¤³öìÞïÓöâ<\æMÀ¯¿3s�ÖãÊõÖ ãßd†ªÆ4³ëTSF£Eâ¤(SŒÈÔ¦Ü&G_‚Õ¤x˜‡·#v̘M&™P‰ß¹"‚…mÒ„!€Ýe!íÔsN¾(:oaÊÖ#2h‰ä<ØN b�Ên�“½$£:z$†t‹�èLR<£I†©´eÌLɦ,q·É'ŽTJ¹Õ^b4êt§Ú¥‘¢¾«¶DK…È&O©¼9.l›ËÑ_Ë�¥�Q# #²¿Ooqèç�=<§Ê5LìXŠWy~¶å»6ëbM[¶|KJ?´SÄ©‘r-E©ý ßÇ)” áVŒ¬ÈýÚª�éè¬à4ýøï�e 2þ:h4ÑîX�â h–Ù³PûjFdpzoE†–„Dü7žÖÝ4ãë‚Ç̽òÖ¹'�ü³Ž‹a”×Ô­ãɉFÝ2íTúèÔCK^­ÇÂp�@ÑœN�Éf …>g4�"�m¡«ŒÜ^~'ÆÐq;4.O¸Õ ŽPëáÔ40N²÷C+Ü0ãœ×j{_-[»3\Ó P·O±ÃN‘Î�îíN;I�Wðoöw»¿[OŠm*±;€Ï¸\ß©§]¸3Þ5¿"$’ÍmžÞö¶ÝÃS;©Gìÿè¼9" é~>I�×v\ÎKqFÕÅ{òß@š6^̤Ê=FsXR“uÒ�]óh7¢÷ Ðžù_+ŠW‡5ÕæF÷½ Y9¦àBjAH¤È @h Y³ƒ ‹k…^žÒ¨PÏõu·|'w�©¨á‹  �ðfö»™@ÄçÇŒcþ^ÀTèçâMSJ¾@5]¦‰¼uÌ�!�k,PûoÕØ“)š`6�˜`ئ ¥‰ïš¨À-®4MX“òÏýÒJ­Ef QÞ;_Õ!Ù â—•ÊØ©’ˆ¸œ‡Yи©R7†Ç˜œ3•²úûdžŒÉhÎÞø8B¼.·‰zeûõË€7ñfä-ÝÞ,H ˜‚‹÷Ó {-9r š‡“¡f 3šÞô> 8¢yÌa¸2ã5J½³õscZ¢d*U½o“-rPûÄXÕ]ÖÏŽ}†Ó |×]æe!@èbDÐöÿÓÆi xÁAì„s£”Eš:¢-~(´(ŽH˜†^^B 6A�éQYàêE%Ja–GG,§%M…‹ë]YòŒfQKè±a®÷L!Ùü…¯½Òͱ £(ÇþH¨Y˜W�µº~ÙLÌîjÍÊ¥`†e›9’³A…¡$‡³eR‡y„B "³¥ vÔ€Ä!êwF/ÆöôÁÆãR”M=‘ªø‡ÇÖ§ hV—Ú‰ðâz%fÏš÷ÎáÙ:½ÈnÑ ;ÄP`ûN³K¹Jœ¥pV{Žpº6=•¶¶k2ßJØŒLj ߃‡Å¢b½½÷“ø¤§Áoñšµ]'Yºt&ÃânbUˆçÎ`ƒêö6JW¨ˆísùî0ž¹Ìÿ`ò²k­˜Œ�×_™etíöÛáŒûsÌÚXd”ø´µýŒ›»&€¼UÚþ¡ÅÙÜ#@7Ü£LÓvI0�i´/Çì:¤)ä_Z\e Ëæ CÄøÏ++ M“/¤á¢a1ÉV1‘l°³BËØž4g-ÿUè ‚· \ÚÕgþk�vCÑÛņÂsi‡üòÍqlWæZ]xM°À%­»2ÚÄ¿) <º•%·ì&þ¬iS6bG˜Óh§m’ŸE›"¬l=i\ïÚN=µÒ�¿·Q5¶8ܶUpé²\wî Ñ´Ïo ëùׯ¡–†Ì3Ø¢±°ù{_ÉS3ë[‰�!â>m�jׯù$,Ã̵ÒYçùcªã'´†ÏvõÊæ¼¡Ðd[_±µ£ÑÌd‘Ž • É&g¡]`'¨§&Ž¢B*ÉùÁ^j?x”Ë÷ÞÈ è½ø…’SW©#n"ʼn&ýhý BžAô.t=㪤¨‰” Øa1Žr5`¨kß´WêÜœ9Õ¥h·&…ü“õròå†N!I%™ÈJ Ä]-XGg'^U­­0†ó p/*'v"4Ç›Oð¶sC„¹U¤ä>j'#ï�†—ãq½mTŽUÔÛÊ5ÈVÃÛÝÛ9jyñ‹q8LâíLp°iåênù ·V›ë¬ia–x™ç(½t€6E„ ÷ìz¸:»/´ûÐtu1Ô¡Áͳü3;‚_·Ýã„î—1Öé컌�‹�o§±Q_Àós”)à:Úoù½Ãè¹Ãðm”ýç  ‰ù®ݱgܳ îgvæ…~[ðd7:)çÄò0vD›9Šé:´°—:-æ½õíÞnÕ[s.Khø À(Û꼇�K®ñ¼ÒPw0†íŠ€ê–Ú­5‚8»õÛ+²Í|·Ýu¯ëQ'× B&ð>DO~ãz£|@N¿ç}[( ´Ôûõì«Ðóvþ£ò‚7ÿuuÙSKdÓ3•J�â��Ïö(þù()a ž±ER¸4Â+£ŸÇ$<Å”ëÒÆÈ[aœ?x%óPZm¶¼v(£Qßj:œÕø{ª�`7 Û ƒí�Éú?õpÔûÛþÿ(¬íÖ´~ŸO”]Õª�Û(õçAâA-“4ÇkÀ–!/áš<ŪêA¡uÉåö¨ÎëHlÌ6/¯Þ¸¶jŽçf_Šz®$Õrؤ©Ñ½šØ8ðà>Ò �0­éÚätOošç•¯Å¹NåÎYL§±˜‹éüóÁûªª¯g 3Ø‹6ìq¢În �ØcÅöqßítÌtøœTõ¾ués¯fƒºypºÒ¯à¢wÿö(‡üÍ‘×y5ú¢�„u7Ž }¦£=¨õÐöMÁªúÆ;üìTÕö±Jn´#š n¤¯g á|˜Ù\°¢cË!µ勺ωÜwös»F m úK‚£×­ï_™�[–­bÓ}ñ¤;·Ê€¯„¦(bÈOª© ³½)ãvU¶=°KÍ 2xf¡´áP,ã%C¤þÆÁ[¬³ãº9  yÌ¿K9³þaÞÿ[†i¢ù‡�©ÿÉúǽ¥×0/…%½„²)%["Ÿ($'cAÒÔ€êUÑÍ´Ø¡ôy—’�ðx/’Ÿ“±2�¿ÀJØ-—7¦/C< %M«¼ {BæŒmäÔO£4K ŽV¢ŒV,ªB}Nb€èX6ˆ/ÄÁ2M¥o®5.-£+s­Ð׿§±¬ P3ò:+ä©› †Y°J[=ïAʵ�µ‘°ÚÞ&¥R£R¢.?nT^Â"£*b-ëBΔ e¸,æbꪪ"~=8UÄžÚªÚ¡„¼e Ôç°\_ø³´ißÀ—}gìøNàÃMGo–õ�„ÿ; éW¶kùÉô—9ì &!�XöÁžó‘„Œü±+ûYŽï[X;.A±éYP¶?­æ` ëð7»&?µ/>+Ù,_øD;yxà›Á×ò>ú†â;a€'B^¬dŸG× ç-»yù9˜‹ÌÕ¤ çÿŽ×u›Ý)èží¢´»É€àC¦ÇXH£V (X>õØXS)Y¯Ó²&™šÄ>\c$Ök¿âw·~Ðh­,îmzš}]æ—Ò�ê}p›’ùsªßsPÚœiC«lr§Ÿq(¯Hq¬*o W�1S¨s«G|‡€ “X:þØž®ú"”ÿ†/ivX¶­ P�§z¶;Þû|AoS1@}9bÂþms6Äkén—1e#s±»äà&ú®ÀˬSDCœ�(=wT p>Ò.çfhoÿ±;*Ìëóè¨MÍ–æülìâx1Ô';ºq슽©¶¿¤£âôÏ9,´Bì²DU*8°“J“Ü‘£ÀÕÐá_\X{>ù¡«MÂkµ}dK•7&—ä ò­„üP�{¶ÖHC ‘RQx—™xwY^\Y^•¨…©nDƒ‚wÄé)Ô¨Œ4@ó TK³™{vŠò�–ÂϬcȰ=–Àrs�&2p8Id(~ëë”iGV’Ì�ëÜèþÞ7?�_<4ó×®ôîtÖ§/äëÀ¼*ùöj©=­½Ÿp0Ö#ÅðöÚ$9 „…‚ž€e|þ‹¢H0S?‚çÏêLûŽG° &µ ‰­TÊ¥Æ!Q8‚rekHŸb�d=”t&‰¤qv„DpË©S8cn>[K XgbWçÅ·aZÐÀ‰QÅaøX •Ñ�B×g�Á]í\:¸—W=bHþ¼9[–X vÜ=ô3¹aùðhDŽY `Q ÓÎo»È3œÌÑ~º\ã9_uE××K‚W]¡ÿ2‘o†ˆEp"¾9føüKª�„¥¹ÑøÒ¸iâc¼~Ò2PXDýýdÌCL¿%„ ‹¶Ñ‰ëh«Ôn kdgŠˆGfñ{|à#õøÂ‘ÏÙm%çS¶ªkn>fQ_ñŠ`�MÜ¿Žɘ¯âÑŸ‡ú¥YDŽQf­¼¡è\{«‚‚øSµ’WE .üyçëÛ�àÖ¢¶ˆúíÄ-ƒVg]ƒxÒLåkSKe½dî²á ìVe´�`a’cäl“¢ ­u…äÙœUUóUoU9"Û‰&.sÖ,{Y<·"†– P{Lö÷ŽÀãÈmZ]q’R0•°ù÷l(ÐëäÚª]ƒ�ç"–üëÄaêjh½ÖñÆÊË—¡°—Oâß„]®Ð­­k·9WÜ Â¸�بJðdzí"e1e&eSáÿŒS…ë!mþ ¿E]Üýôžlêö~<³µ<V~òÓ~CéiýtENi§Pdv¡ô0Q5ÝÈH�Z.k‚‚r^p‰B»¬ÔàÎ�¢ƒ+žs¸¦ŽÕi- ¥o焟ÊÒý@ 4ýû†,š>PW!}H¡“Ž¢Çe\ÿ�Ÿ*ŽªÓÙ`y@ÈþɽcW§3}uÖìpÇ^Ñ×8u¥€¹gïhj)mI"ù¶ú‰„)IYÙn4}!€WŠ"µ•FW8­“” ­b\H &^Kü~¹ÎÉ0w®o†ù4Tw<;ûùdÜbÞ¼nÎMlËœ‚Üg0€Y°LámÃî3j.Ãäæ™FøõLÛ«ƒÙžßˆBÒÍåñ !Zk…âId1�«7Šf>¤Á›¥Ý„Ð8³ëé‚ÿ¢ð~(¡•VÿÑHüP«ù”",ù;Ïr·û7ÅEäAOÀ㈵‡g$ãfó;É ÚóÌ•‘�ñ’Ž®ü$Ÿ—î‡]¹± ë˜>QHŒö™i_›`¼Có ©:6 y�²h‡gŠ¡$h›Âš…9,·à;æ*¹ß7q³s°GØ"¹%@Mʼncc3SRËŦÿ_‰ÊV�±óu œˆ@Ïn"�eV_8“ ‘‚ ¤¢`� �·�ܦË490+s}�Ò¼„ùĵ³Kʱ´³¾+ÍÖ.òÖìž=Z”¿Ì BEE +ã@P2±5ØYwNo[÷R§ØhXÂ¥–-sÇ ÎÎŒãàø[ÅŽXc>Š!ÅìÉ’á‹¥!³svîÅŽ5M¬ðMÝfœùó*Š6Im3x {˜¿^ØßËrxz=^;‚BŒ>L[»;Ž1â |,¸;>Þ>.-`ÌÒ��Eú ¤3 )Ö€Æ^ R¿©Ÿ~ TJCÅ0ŠÜÁŸÒa™ –ÂÄ,é�Ä]ýDc ›lÿ¶«ˆ?aóSyÕ÷bçèådûð§Ê™çòr“ðjoÏâÝÇù\;i-Sh³Ã˜á1kß³‡o}lQ̘__ ^÷lîímíþ}íúâÓÑÇŠ¯ï¸~¡óeƒIÅ €míx÷*˜?1wѼÁÛÚ[ýÛ}ß°=ÖQ2úB�›™Ï—o]ZJŠpâååÈr)¡ûþ(ÙÉœÝ!Úûú¾ú] xozC…Ó·óÏŸ 3kç|¿*MïôÁÐù Àk8òÒÁB'7lšÓÚS|—M˜0úÜëõÎû€þìöAº}L^BñQ@,z*sxaÌ6þgÞvç…ð¯qª@|g;ŸÜÄò�²=õˆ!ûðÏ¿CB¬ ¯èv�æfH×mÞ¶íÓ¹Yy·%HÅu‹–ðëŸÛH�7 þ`ði#~…¼�¢ukæ0bK°Âl`q¹ÇÀåË7Õó0ðx’ÿÜ3t¼×ìh�PB½UZP9¯ C€´ú_‡K*áe’‚òpÿ&À)ƒ`ו„¾�üÁ>À,Ÿ‘8(ú2}Xajs#³Y­±r×Tì^ÙlLJ•ج€�èá£È€G“ MõXصV¨œ’²/N”“PXT¿ÁCͪ"pgPL4 VL $ÏhÄ–É6bp‚bLŽÈð2–Ù|‡«’_eÏ,û†C°µT"ÅA,�ÁEDú>¬ûŽíÍ+CóŸ/§·Ì¡�ŠqpËò†hQÜ S—ˆSK†X²½üaïàôgzhñ¯ E«8‚à,ÐXNV»g‡ÒëòlÈ£´àKáñþ¶ñ2{-ßµÄük%7Oœô¢‡øü�Ð]''‘‰¦²|š¦<ƽ¨aŽHÀ5¢Û:ø=`ÑËùMb;h_Œ¡b/ZQÌÄÊGü¦ß|£I±‘|¤Ø@}j¦I—�fa Ó6”pE7±~ûæäÔ�­€Ÿö¸)“jxùH}B-ó¿ Ö-‡°ý&Ð!ÚŽ‚•A³ÃUÂ’ñw&<8+M?¬\ºëщ‰‹ú­0Ø,u‘®J}åJö³Þù Œgï„Ø¤ò8#ÏØ’ðÚ†Þ¡$}fv*=)�dõ¬™¨EIÎÁ«�p:ˆB ò—¿¡ÂŒ϶.Û¿Åiúu"“ŒEºkŸ­+!Ø�Ê•zY'àë4-xF÷~åˆUßëd¡H¤f"¨ÑÐ8Gò8âÁêä†F)ÙF¢ÖRDöÈ•Z¸ËÚâÕ…zé¢ùíZahM4¿B¸Í«Š#}ÒÅ3옱´³¤/“°Ä>Ús Cn!w(\CGD+2Іޕ´m3;\Ê‘ÙFä­Ž˜¼'{!~‘‘z㣠°ªX9ÀNúN8~ <%zïs8ÞÐ&¿_¨¬,$ ï•{âEÄÇ·Ç)^#«)rÙÕÖVÇĤ8=‹,ùDÿiLìyòN2F ;+)8ns˜N°à,iX¢Bú0á̵%ŽfÆ*~+ȸ!³!¡q—œû)¨fÛJ¾/Ó9sýìPè¥ÒB“Íí²PU±*ñU9ï4ä�K'±2çÛÉùᢧ ¯³£û·(?ø\´aUк”!Zz!ñÙŸ?†ðãkŸÚÃê6îèB™óò'‡(‚#(ÚŽ‰µ¬²L9àïhû…‰ïŽÊÁPw¹ãYÅn½Ú*ï`Ž�λð7:…Uåæ™Avc=Û¯ùŠéþÊË6Z¼r�éRv\ESÏa°œÐQâ³ÆA2”œڄO§N¢¬¢>*‘jñÄø¦p3y”¤-ZN)ÜüÕ”k™ÃÙÆgP—ëZ#AÓóKÿòÆ$˜6¹09²(lD—˜b‰ç«DGž+ÎøÄcßvãUcÍêKíB<µ«üÁÅ» sGîNî£NTŽûlÒïÆaîLBÝ�^sÍU Y 5'eÙÚD÷ŒÙ†ù¬]Õr‘ï 4h牷‹{åÔ¤Qú¤M™”AÕ+YÞ°|YÞØQÁ/2ÉÇñ�rÞHQǦR€ÄB� ™<)¤, ɪa]Ž/³…¥³Ò9$žòH¶~†Ùt—×!;+ʈ1cõ‚˜ïâ¶b„'„öaÑQ_IÚÌq%– [ Ypc‚³ÙmD莃�)Œ‹�^ ô'¡UÍ‹�Á*´­¥ƒ‘‘…°îÛįNXô y�Ä•x*k~"&lšŠ1 æë,ê(È�„¼@½ÄdvS+¢§ºšØ×kG'yÚ^„gz´\’ZÈb²&$Qð‡µ”§,!ªÑ5«Ü»Ô[wNý\êÚp鲇`â…êŢɣnC["×OK&´²%„"‰á52é3‡¾úÜsóéöuµJ µb,¦º"yäßq uµ7ÖTÁ°cK˜@ hP�1¤PÍLfÚ[Ò«u×e¼Åi[{5Í‘¦âRV‚Â`ÍO äÛ£ §�¥t+̾J̵áU&ZíÈꩯyM@á?¥÷†H†Heª¨Ó‚0AÏÈ)¶¡“¾ÄNo�Ìy¨�†–tEÎf¶'Mµ�ôÞ`Ür×NÄéñVvƒ7ï%qÊò byMz»…ªG\™f¥k™!t"q ˆz€¥8o—«‘“|ÕißOÉ0R\ê¨{m¡&êÜ?rÔtTÝÁôÊwÔõžà"‰Ò‚HKúüöÚB÷æ+y–AÙ)5NŌҔ-4sMªu‘m Rç*uŸC›�€��Wד)NµõO9BóŒ,�Õõ ’� è¯3Ñú÷›(‡H%d"ŠúyªFúðÄÛ(yv1MبêFö±Î俉*ªÅde”øÌÎÿd¨ÅÀ¼"4ö4F¯ÆÉ'ÓåÙ}Äs½¤¼¢¶ž•c¡“˜š°::g³<¾áGwÒ¨”-DŒ‰øþ¿!‡ž3ñÞ�‰xôˆ» È{5LtFO ¥B¾‹kS…ÆÌ¡�k–; @è\Î˺¤v…Ë“w`ß­bŒ±¤¯;þ¶N�×=È�'«�Q‚\gÒÓ\Á<￸œnÔ —H}ÞLu’¤Ô—õ+D½·Õ%É`Ágó›`æ¯:¼Y©�JXýèÕ¨gzúf4aÙî2d´ü]´#‘íÓÂF±Ð ï ù*v÷9ÓÚC�¥êåÒÁÖ‰™|û®nes Tªª©eÍ{\þjÍÛðñ›¬2�ñj[-³# FfÓC^Øá³ü~ûõu.{ÿ0*bŒÛ>�جôÐ�¬Ý¨Ä"Í?•˜l\l /­�½€ß|.Ípì¤î;úF\ðq_ï,Bq‡ªY±'×ÛµTž¡ÿ  ³p:ñ’¨š”Åâ”X…v/#x)Z_CoŸ×Sφð>i”À€]«gµ±X0Õ>À!nü‡ã€±-óÍ-!€îÙ%* vK ¾±~!-½Í  ›úþ«PXÒ…Ô–MfÔ2t5–b ÌŒ9M9Ö2W(„º‡»;nn{·Û€s"`==]¿ž”†Û7í¦Z£ã/ÚþsÂ`ÚÅ"9€<[ö?›º6s¯Ï�@ç–TîÉWwñšûQ¾•p ø� �•J2Œ†YRâ ßD !¡·Ùq !@¾ù=kÚDy�<'´í‹»Ý1wh‰×Âæ9÷ƒérú™™C¤‹r¤éS·îr²û6¹À˜ÈÍ´]1/†¼cĺ¢ 0sÎ’sb¼ÃmL�Qj°§ƒQ;czhS`�£Ðb8´B=/…‡[Ì9Rô†x5ªMYåÝ\!Ô$¨ÿ>v½òС‹—ÑÞ Ž/Z-šìñ$)¡OC“;�­)ä3j4ªH24­m�þD-ó¿pi“ ‚UÁì¤MÉí9ï·FRå))ÍÓ—=©©ûf"°¨�ivÈPaÝò•8™”0†«C±1[°&A§ô�ù²V#)… ¡jgÇH €ÔÌ! SP»¿kAÑÅ�’O —¨ZhHCéËT“Ð'Dut´;;ü’¹·òTS‰# ̲Ä�¸¦,Ü»ŸŒ>¥wÛו�¾˜i‹�BŸ¿ µjÈM뀙Úì•Âó]Üpk¯¸Ã"'°>Šõ›r ‚Æ;=ŸŒùWä¦;=ªZÚÐM#dG›=¡ÀÛö ×»ÎgD-Î1éÜ~ýlÄbvIW€¯¦£ÿ'Mšx««WдRyaàÑ©YöÎàÚßÓÄ=HC~Ëä�õ¼P”RÉÇ Þö²åî1=ZG?K@ITô`]qí[°².c s©CäíûÐk?$è™úö9>€V�¡›,ܳkT¶l7ù|ÞÔ¤ƒ’CÅ!-ã+•S(ÒõYPº:žMŽ×­ÔGªiïˆ2}}ÌÜlŒ�WÑé9�668ÌBáä„§‘îO‹¶ôŒÐ‹¬{fAjrà´ë…ÈÊÜ<¡’ªÃ1`>ïQ�%¡Hþ¦�ëÛŸzÊœØoV$ìw:Ýÿe¼Ûúóu÷—ñ�]܃T yÆí~_6B��ÝWŒÚ?{ ±¶Í:�®¢ö²ã’æžsIþc *"Uãi¡RÇȳA‘;ù¸,7ˆ)döÁ£‚½ÚzYr¼˜aèq 3*NùkÛ.åÔó©«µán?Ÿ Ú÷8ød·u(XÖš5¦®g UlÄTŠi(DÐÀ–üȤÞ-ŽTú}„;ÚàU¶oXVGöNêÑQ U¯”OÙ}¹’ŽÊY-º¦å"8�ZÆk?!šm],¥ž¥t¬'õ_¡Oêà�5#bjÞÇ„3;ØÛWaÚ{óíÂàÏŒ(ù0'–"å:vÃl:ÉçÆÝÿ™Ÿ”²<(ìÜ­³<�»è¹KÜ%Œ&";‘@_Åìa†�²ŸA¡Î_.)îjÙ½‚"sC¦ Hþ9PdXZˆ©³»q·ÀiU›ÖµïÐåz�)R%ívª.+ØXÏø×çÑݹ<³ƒh8–ÒÄ[‰+-c\çºÏ’Ï$A(Œç£eoøPÌ¿¾ôSÅ+=?KQ×û‹O꣜@[~�(®TŽ-P<ìu Å}­�ëˆW‹Þ¾%r—Éöø€T¨1És-b;´MW2š©D9*7ß÷ÎÉЗ‡fŽ4½ZŒ/ëYºÁP%p^Æ¿ñ;§]r8ƒÀϵ1‘6œËjgéÅUÙB‚[ƒœ,½¦|®TõþÃEw‡ïñ†2wLèž�5Ý•ê#“Ñ(hªë(k!Y¨hë Ñhëèk+`¡Š„Þ(mªëÉ!héÈ’Ô-,a"`)i™)h_o–lå.èà`ªbéñrlF3FžV? ÀîBhÒ–ñ�¾ßEð!ë3̈-ï‚)b´œ ¸ˆ" ½>® ±ì@Y#ít´P‰}¾ËÈБH¦åðL`÷:½Öž…yQM!ªC†´ ¦ƒÒymúw°•cS{ÍOÞ$dDä„ýv8B¤· Àn6JŸQI Òl¢-@v$–RÇ�Áš5¢Xý®’Ò ñ#zûöΦ2ˆ�Ÿr3ȤDDŒfB�QVF9*HþhЍ•ðÂÛ‹¿.jÌ–$üøðF_Ïð)µÞ?Nb^têîw "�?ÉO½×Ê2©©1h-“—2Ï¡ù§¸*Ž‘öþÕËö”Å f ¾±NЦ9ŽÄ\“«i _AÎÑ2_zg‹¡£ŠvÙ¨ø„„Œ a�¿s !›3É1hG‡œîïÔ·»c2Ï6<†› „¤SΔ¢é†v £ E{œ°‡�ÐÊFú,*Žazç|Þð¢ 4kðþ÷Åø°Ž¸˜at+½ÏÃkSî²î׃%jîÌH¤#�BñËö¾µÆh3$â#S�Û˜[j¼Øn~µ™[¹HS[ÞÛ òÌ2câÕCÛÖu…ð'Y€¨RˆÃž6»{óyxY-ªú¾sxDèÕ˜³iŸ%ñ€/ÉÒZ¬ä V+#`˜Œª•„A¯UHBzi[‹÷Á™…"ió!¼æH¾XÜ¡ûù9&8^I$ <%üñ(£®ˆ×2K è ‹AÝÌø2H¥s˜ÌOzfÎÛ'¸*=ýí�vßQEy ‚`rÅÚÌ 1KÖ5+\̶ç�3{ÖÕÅwWN3ÜKê&Â=ñ%4µ‚™5âIuȽVxò^ïTDA´à÷ûy±ä�ÚÿñwÃý+’ëö³W€KºÞu@$£¿%e6\Üó‰æ •­>:³nqgBùCÐåPD2~)î#ÎdU4¾ GŸ &–€žF/„c @ñ»&Ø}ŸŽæî�³ñ }{tWBpßgCmmÍj²à�lEÑüè)`ϰ¦*„U•ý­>2yÕrEÖ$„²]x¾Ik‚ÆL§Þf� ªáµX ;Ž£ãc(’DM Û{+ϼ¸Ž¼èèàE€‰þKº½ŒÜÇá*Õ®•³ä3ÓL™Ž_vžÝï}½NaÄÂS%°thÙŸê›Yq” Oé"õ$þ‡i_TÂ:Û5ì†×{åp§2Ó 7¸½5m‡‚¾°aÌ7þÅB9Pªg”·í×Óócïw°°1g�E©ÕE‹Ä£EÙœù-¸¼Øj½=ßÈìží¢8#‹ÍˆÀk–gXŒc®ÏÍ_RIƒ€DIifµŠGN‡íeG<¸äv]bèÐdvú•1—Ëu«ÑÞ£·Ãó«K-ÄS4hp.gY§ÌœVúôlÒ§ÑjL«�Ep�#tÆ=м¬°�àÌ®²‹|îºx+>wìA¸øÇª? <}¤~*«ÓÚ @ ÞAÜsÆ0®aÚN Ôù©aé¿Ý–Ë­O‚1ïeÌ”æ/!® ”FÕˆ„ÖüÄ6 Ü ÄQxÕp%Q©ÒxÛ˜ŽqÃc:*ïмŽ§ïèìû-[§‰®ø‚XÕ‰ŸsƒAý\VÚ¢0Z»¬´|Møï‹²ñYš“Y¥¥¤\áØ­pe‘<–j&€t(Œ—'ÂéùÄô_›ßò«C‹¦]›¼c`rKi²õ­� _¾¯fwLÝ¿¬0Xv®Ú‹+#<õ]$éücz ¯>3MŒTXfúÒöoÖß½L Ê�[úæÇÕ´é^eCÆøíÙ$Àž1›ƒD]Gúâ÷ì/µ¥”?›oœÕ7|fr»l›_@ÁGÔŸNºÿ¦(QAjˆ%$/MÏAC¯[ö¾„…«…¡ÆžÈ€¥ÖzBœÙ%üo'‹Ìᆠ_23é.7+‰)Ÿ;°»=§ÇXi2¸”µ„DPAÜ·8I|ܱÅ'´D³.gEˆš3íë;ƒîñÐQ�b›5ÕÑBô6¢Ý¸Ðò�W«¨­$À̀…Û;) U‘ÔX~U*¹CûimbeDKÐIpÚv¨&í_aXó¸©RyC1k™›GÂ��l�à�0ù÷3�s¸‚´ö•äù¨>6¸ÙÎ×ýPË·�íßò(Ù’YIb à™S.È»°”ZéGâFcÏVï¹$Xî'Ê£Þ¬móÊTŠå;¢ ½¡ß¿Ûv‹qô§ç Áv¸ßÊŸUέkÍ>ýí{€ó\!ð{Dàý2žþ2.ˆ-A¦sp¢î²]ˆ£÷û An²¾¯§˜.~÷l%™¸Mò>C(Å”úk[¯Rsƒ.qš /óªÔlHéëËŸ á» À”‚*ô 7O»ÓYiÉ4W8Ä/®U+3¥È®[ÿºÓOÖZJ!¶_óçÁxeÓ 9´øïØŒí9ЊâbïÆÙK·"ÖR[ÇÑU6¤5DRu™¤4îX1§q—"’1Zɨç1J ìÀhOB¦Ì yiï–&Ìq“´ë­.QçÞµÔïAn¹ÍùXNzCCyß0‚Ç«Y›+i¡EzÕ•¤ªdpà³°n‡öµ•' Ÿ;Sn… 2NBkr¯ª P¡�­¾§ŽÇió(ÖÌ«p%©ÄEiI“@­EŸ¡ŒÖÃv4h š—™`€Òvµ Û|ÊÏA]_æYm¾¹Bš~©^“PÜ$>}9Ñà‹6h>úqÈ?.õ}ìo@ÂÌ­Š|œt”9Pkbì­%W´dz·�Á¤šÛ³å+†4\_(Å�/C=¬x±J0E`xXí./Î[Çm±®øª4AÒæCdŠÒʘD•É>†A¤…w*#» –BªÊi¤Ž†tó$cà¹$õ,ÚÃ"†ÀšŠÑn±nZî Ù»ý]Wpe»3úC´]f&±�ŽÑÑ�I¥kéðz1ÛMRΘ½.d±nÀâ£ë¥E8£Ìè¡ö<Î×ø0®;(aX¯?dE!n¾äÀØ *Pvè³ÇÛ}xži¼.>PAyàž+As÷ —Å4°O.k4p˜ÝîOçCrê®Q¼A>è¦|‚j¦&ENeK±ÝÿÞÍ}Zh®Y²6ë:ž=F]ܼ@á¦HZ%X>Ôójàâ ¬¢eå :Ó¥÷§Ðªîð€â¥Ñ™˜Äj¹œè%%wh‰ßø^l„šÂÝdo_ÆŸZÇ9Z9÷XÆKC¬Þ¾+ {8X¹JeÕ†Þö$ºíÒÊ¿m*’TÄÎá5-JòT5'�ʨ§ˆ„ÊÚ¢£·ð[ ~c®¡«£þãà*ve‚Á!MšÇ !— ¾š8û,åÃ:sqø#M)LE‡èiºe•+ÏüòJ±Oi·Q"䜥)ú3EsFºðrplP«½Œ¹ÉíOªs…Q)عòzÙÐQ„ÃŽôx¾‹I‡$ža•™;AÌ 8yN°s{�qQþ ©q£‘(£Ãæ †Âd¢Ò(5A“öu_ù{q«”)ËDîèÓ¶*…0.sÞ¬tSl%®È¸Ë�+MÂ^ŒPåPnf^ôPGÌ·mã°v¢U¸†*‚i\ ú§2¿¸(ýô#ÉÂ+c­Ô2I)}O©8B>]þªs³�w‰¿'{óø§�hÒ‚(¡Á™±cÃöê––gLÙ«AÄR–Õ0OTò€Oò~È�<™¾«äÕXXºq`LßLiÞ!7v]ÛÛ¹TxÙ[Ðdã=ÿ²Æ™6E}*-IÎ=“÷ºp ÿO«'{ìsÝ݇Õ#z ݲYt+7´ÓVÑÄIVSY!LövѸRÇz¿XøJòeÃ\²ó9Ý*™Ìóô j±ì§Þ&ÏŠT©UØËøGÞ±²ùÏ«]ëð|…ŸímYíJ¯‡3œ •è?ê' ̈ò˜`t¢ëDúéˉˆ@SáQAÈ�uY %Vúýƒ¤Ð’é�™öº“Úe)ŽÍÊZªêjrª/j=̈Ën"ã0®·m �žOV7Ýé´g¢Ã„^�#ä ‡Ã�¦^÷²÷Áéxa‰¿x1�²[Û’òíéõ»>M�þiÝ8ü’/2n”$ i,¼^7[<ÌòV®™ò–X�ž#• =\À[8í2� —çlí8vxCŸ-‚:3¾ÃÁʨG—rÌËšl|�¨ïÒi1¢ý¾¤<ä{K¦»Y*FùDñÃ(œ ¨–ˆ‘h âø9ñwsÏÖeŠ0LPiî2÷ö7 ë®3®fE7û?I JˆzËZêzJ„²¢£›yšJxkšÊV…‡ÏMÈͶîºÄ �•¢_6Ô¹gŸ¨iý‡Y(¾­~ K@-ÆS›cñ%ºV4N@ä#òÎ!RÊ…¢h° ¶«µ?w\è‘…Æ\:à×ô­â¹SnµÜÞ™¨½ÅîHU+’¨anrŸr7º™'Š5¬¶¯M…Hm)•ò5&#•ó)áw…Š0k .è$âOçª_?}Ÿ<š¤‡Â¬JóïDtCyfOM#Z~‹w)�›É ‰¹¹áFȾ?þ‘‚%B©•Ïþ×UU|t&Ê7€õŠÛULß>}îR³èKMØŸ�Ýy\õ@©�wý€­ÃBPϸóà‡E“HŒÚ¶‹üç×M0€Î ˆ¢`} ñöP—x·ñcCF€c<6çRù¶˜|ño�Êî%ï 8Ì5€*Ãé³§ÿ N¢Ì]gusfNy_û½TT­I"+f®7W®|X%á$‹Æ‘–ª6Vž·º”£¹jÓ ‡õôjôôfl¯þ��-]]Å¢ãOV'þ…`kÜÿ�Æ=ÎI«yV©ŽÛ„rDU´TÄë Néq©d†¶ <çaî©tDPTAø¦úƒÃJ;HÜú”¦ñvšçé˜êÑûˆ@¹.#^<¹Ã  µ|(l™DÁgùŸ5úƒj Ryf šµ…HÃð‰‡D_¼Õ˜hxèq{‘ÉSý6¹s‹mq[’ËBWgM’²¡î•ÁXJ�–K£GM… îBPuCÍóN×7M³àªóxÞ:M¦¯¦QËH'åx2�÷›Ë4@{µúŸ6þ2h‹xÔÂOˆµð›Îíik{çq¢]7óRÑÊ#ç¿ý”–»BHKQB.¯î?pÅ?ãéÃÿ²ãåSjŒNÁ-‘TÙ­ƒšC×ÑJ÷œæ„„‘H·È.*D²rºCv«\ÉmbÌ'äëVÄ%"g*ÑÓ®P1X¨Sö6Õ‹k8Ü=ZwÍn¡¼V_1ZÅÂNPF™“âJx±~I6Çðj†•’äí'Ë †«ú�2iMnU×ãb)å³E'Á¥ó†ùûkÓÒÙ ¶Gé=>¶5UݾlBYM’¢¬ÓÖøìk%ˆÜÔB¶ºWÉä7œˆ !Š=à²D’,½e õ^¸“ýÜ+__q;Kšà„·,nÅ„Að#¿ï¥üc±é Oí(î©n·Ù Ò²$Æ"̈¬8VÙ¦t%Q«éµÓ|¤ÃZspñi²J-gv!U{øL:Ã`@j¹Y^o¯ëÀÃÌø]¬LcÙ;çZͰ¬¶¤|!«ëˉ[~Ü N]̉“ŽZ臥Ñ̵Þ4pÂr‹Ñù’ç²ÝzÝzí±äb|–üü™—0˜/]P¦Ò(±VdÊZ€bÊ­üµ-óCF­#�+&]&ÛàfÔ¹˜ªíƒÚË"‹äéC”ó·�H]ýß@½µPnÜÜ�&þ°ìÃæ®ƒu¤¹ˆÜ×c­Á.`É+N¼�†)4 JIcÄ)¸µ±%©c�)x@—îÚDzÑ6®H’i(O|+‘FòÝŒNÙÁ×o}\t~çE×8·ñiòÔÔ^ÁjÉzYSýšÊ¯ºu.ÅeÓöù(´žÅ¡½’eµ=^šAÂF¼êö«”ó�kÿî2ÐzÇUUâ�z³ᆃÂoNÆso°&g] #öfÓ#$Ë^‹ÏBè/Áù‚‡bšJt¾ŠŽð¾¥‡¡4v3/=|óÈF:>ܺÃLâV.„�¶ xmåyd ¥µ„ÔŽ«õˆœ�.rG)1}öãÄì!I¿fÜ´#'}(ü"øú:æÕŒìÛø½×䊧_fïâ6„Ÿ¥œã`÷ùûpé†vY v3eÿ Ç�ç[še’˜»ß¹ŠùâÇ~Àä8¶¹jµÁ®.ý³q'\L27+,ZÛ•µ8Ø+„£p£æµÙ]óZY[°Ð�}{æò@ôLst[ûÀn;Œ‚p϶Rþ*îÈ ¡åi"¡åómâ‹¢Z;•í…B°…àj¸üOßĨI9ý§ox£ú ±ñ¥+›³[ÅZrJ#-G‰ÝªÝ™8�’ ý[W¤½Üã‰L:nFÀ|¼¿tuäˆÓ)©j±˜·P·klJ˜T�å!šÀ øYµ™©“YÄe`ðÝûc›­(Ú'ZÖí�]ÇÔiµpùŧZŒQY¤µäK¼Lbø_¯Î“÷IiÔììn÷h¶L4œà›ìÆ)ž*iÙ»'Ut¨9¶z°¶Wª1õPãè|éœ,ª6n×RPY˰�DÒyiøLóR«ò|£Ï›µwhÔ¢0j$ò‘ñÏÎA¡?b-Ò‘~.}{ÜÑ«*ÅÝÇH"zE“Žt^OoƒŒ<]%†‚­hQ¿�¢Vn>Ÿ<ñdXÚjRí_†¸?Ñx‚ZQÙ¦.y6¹€Öº¤(Qø•ªóBŽðžNoÈ}]k¢Ë|ùoïÞxA}ßzjoÿþQv�ž¯ M(åÐWXžþЪšdu…)špL5 €Ÿy8K¾¯ �a*ÿfµÒ4ÌC~ D•‚6YßšŸƒd�<¾�ˆ~Ï0C£¦}onæ¿£±ÍX(°ú!�fþÍR‘ŒÔ’è�¿6j9Å’¯eo §kÎyé�‡JbÞnçÍæ‹Vˆlwà&ý½#ga ÙG¼æ>Cà±4øø;Â%®‚9ûj—6ù ¯¡ôö—Ü›/œdºzŽ/.È<Î~›€Cx¬ØôÆtkuàÇєӵœ�8 þÊÞhñÜ×MJÓ°T)èU©}[ÅŠr°Z”:É:.v¦']p#â,àt�ÂP8ß\ñÚ9`!u=èáÊî†ÿ«wá.ºÔZ¨‡ÎQ_~IðâÔ¸rq+»ò¼<}Æ}’”ÀcÇ PA¥Ø�7FƒÒÖe­Ká�?[Š•2Of4 ªK%óÙ˜ÌåKϱ¬’|õwÄà†°ÑÂyÈÍJ�á!@¦² ÞUR`=ÍZd½¨F š<:öRcdgÌÐÂ_a…J=ÛÆpG�p`Ö(Ǻ§'€©¨¯ çsBhT±é½J·z1 Äíz¢=Xù®K-rŠÒP×ö:Â$²°Y[‘W,ÊL%Cªl‘5´ó�Ù®ámôÒ˜—$º\R?Ö�H6ܲäEˆà9Þ!`ŠÈ]S—]þ�l¡tw±¯ÏÒc µ�FÕ�‚ç3‡ h¬e‡À¹yÉU‡~Pñý\z”’ÅuõÕf‡~Å*ÿ¬y¶óñààÐ¥»½³€�¤_zÐAJo½œí­`K$9·½ùÚ©‘d�*†lI“l¨ôl}É×åŠ&ã�íZ6( ²Kn9’™ë´œa L‡‚Žs /ØRZA‡çÆÓ³'žX`4ö|!¤ ÃecBAzK;ÄÜŸ§Þ»„œf<ã$åÞÞüä7éatàèz�ôæÈÖ5<q·(0ºœå°¬T¡çªÁö¬"ÖrØfE‚ªnß#›È��Àü'‰µf�}ÇÆ(¬Ëmÿˆq°é”o*wÞA®ÐÆÚ.è’!ç�¶É�‡hõ‘ÿê�¿D™ ø«ºO¸“1Y˜‡�ñ¯t€Éy×¾~g<¶€ȤŽr3Û?×\?êqÿˆ”ÍåkŒ¹ÜÂÜO°îH-ˆ{øUéètÁ�eÜ­ˆx}1w›ž¶ôÇ/ñ‘·-ˆþ!ÐýЂ2iþÅv˜Ô¢MòÚîè – –+Áx=r‹yµ4à|'ÚÏaìž)ʽ˜¿ü{»÷ ê)#»Ñræ–”Ãl�v3ã7¤@+ÑAŽûÉ)ÎûžXøt²í;(ˆÏÎÍÌÐ,Lé�³~<é·¤¦¿¡v Jm8Ã"þ/“å [žÙ£¼&ξ‡;Ølê<œå•$Måd=eå¥s··.È!J»2¿„‘‚8y3,±+Ƨ%Ū)í?>;RY¸«rŸÃœ…[®V²òaTr@TáëÉHÌ%ûkÝ[ž/�~Ã�0úPLJþP¿ÉÖqŸéDWžêO¹5IP‘ŠS“ÈN�‘•ª—�Õ°›4�ëÏiˉþu|-ÙЉ É™é…Zš‰ÀŽM‘åãg/iîNJ–n½¬z®b¦õhÿËåB~„fîPgCo]õça}Cõ¶øïºbî ¥<²2[:>ëd¯^ÖÐv�ÊT‘÷#>FA“5LðO :<¾{ø?`ÌQøŸ-_1=F{¸PQ¯sBºÈùeM]~2•ÄÆ£$þj+ìHj{‰.Áx¿»,RR%YEÖëàí� �»{SúP`¬D5å‹1› EU9*|£œ6¢+-5Ï•åÍääTVÕº‰ÌÒÉd-� ¦š×f¶•ÕØs�5l&T­§" ËÍLí:—ÔÆ8Ä’”œÊÖÎZÙ®Iî ø²š‡¸À¼B†¹Nocô J§×Õ •Ÿ‚ ×)jûÔéO*fŽY¶V°NPÍ׬Úid»›×Xš/*™fÖA@› ®×ExÍÓ²|Æ&¢-•¿pD¯BœxË]€Dièƒðó³�f8]1ÖM*&9x�®TLëæY«•‹q“m²Ú›Ù6qÐCüf³ä“ÎU˜|r.2È/ÅPMGþÇY7­”.=à˜n�I˜dtX"`¢6 JûK’*Y„*§ê!_‘62;æ-ApÏòšFm¨“™.?Ó#ÿº”0×°£�µÂóÕ:Rl4¶ŽR·ÇÓï9;ÞˆÜsÅr²Ã!uxô•âñgñ‰8z$Îɲe„Q’Ð¥×áƒHÕ¢H†73Їޣßîßø—¸‘¤ „]y“îÃÐI‡‰ÙÒø´¤�p½åÈX’Ú˜&Hp:æ÷4 ÜeÚˆíêu&2¤ñ´—­2@p°y2½ˆ~F¦£Ì{ÃO h¶Ëà"‘1¿˜åv#'00Ò†s �"íº7®Q�îyÙ½…Þ$ªÝX‘mØrÇÅß' èôr½Ï!â™ÇþåBëb.BÜ?†!V·5¼[8­G¸]ɈHßÉ­|­Êý!µEã&GU±'L!²ÀE(ñ¢-ä<‹xO¼6Å/9ÈD©G#�?h»»Ø?�òéÑ^�$ š3Êcd߼ɗ¯ô½ Oà‚Ų=C�¿Ny¶[GŸÿŠêózäXâÛ#ñóó]ä +Ÿg<¿+”2s лÿ3{cB;³ù5ž63ÆþÞ:)šón}mP[ NÓI\›õv$ÇôsÃSAœª Ìü•Iøe§ jóÔ9µÜI©Tç½ôÌ8€óË:q¼GóêTæÛ õÈ@…Õ|Ø0ÏÛ9½^é\ø˜8ÄgÕÜp:Ó·gtÖ;k¡CéaÇ·Û¾¬\ô…-×)ÕÑÔ`I°ê´Ðqj«�GXà„î÷¬=»*»nØŸ68ÚícO)eÒÐ�¨€IE>˜Ã±ÌBÍDyó–^"ý“‚˜¾´KY1r•¿s‹T݉º ÐÊíÕvä}'Óž%ö8#5¼ç©¢ú—Óè¾ðq²kÕÉÍ óˆ�–µx9–Vôu°¶°‹ë»*òiÎ÷ýúu²O TDõÂH‹×#–Dý±}=«çÞÑ›ø‘tJ'ÅeÙ -°Xãå–TäÃpËûÐ}°º=é—P²Zr&¤3èõÊ9F�r‚¸4“OÇä� –ÃJ–x#KÍd±ø–ôÈ?\,óñªP‡!‘ŽÓTó®YU]»xÍÜå�QJå5•†B3Ïü»­¿LPáçÜFÜÞÏèg¦3*Ý«¹JœëZÆËg¦ÆÌá! 03j: ² '#º‹–mÓ(­ÑeiÈÇ«ZöìŠÖ}—6~ß­Ç8»yClǵ<Ï뮸ÕÚ¼©ˆuùýý¨ª´¢»Ÿ¨kò–´Ò ‘1�ÄCû ^¼·ð_#ä¹v[èö"ýŠÔBQ˜D@*íºg*æf6x]]~|º0•ý HàþBüÌ쾚ä&7›¥³d]." ‘•Óv׳çõçÁL~…ÏFCƒ» ©’J¹!áºÎJ8ÔŽàš~ V„81Ø)ÐOàÿ£^ AlÐùº´ÙK‘Àе Žç”c‚5†å”Š]@RŽÑ¢›.|a{a'ïÅœ,Ñ9�Ç8ˆáX5?}Œ­pF7cµ �\+ð“·×ë¾:¶Ô%’éIlB©ÃpDDš7Y"fµ¯p<9Ù“…ˆ 3¯w²äVgŸœ’ŽCe®x5AA׆ã1�­¶g\Û¿S! ¯2ÛyË2Z® !šÃæE’� ü >ޱTPÞK-_x:\å½rÊɪ-p)Îùt¿®Vø�ÒžÁå@i_�l%+Ož], ѰÕ]á—PD.“ZG_ªœëÓ÷mª,‚õÁŠ´ák£�Ë?Ÿ¹ \‘6Ò.�&öÚíŸk£V:=5㸮éG-´•fý̈)Õ‡·20ìrëÛ ŠIú¸™æÌí ]ÒSƒD~ ïûW|˜A¤Ë£727ñ¸è³5­ïÇÖºúWvÓ¸ úãÑÓnº[Ï}â3ÈzåÚÒžÛ1`˜WF"DEÒŠïX8&n~ÓðÂr_^lúäö²Ä�9ì úÍ«Þóú,×hü,·ÓCåºÏÈ\‚ÅÞÒŒ¸xÎñ¼þã¯yj­Â-—Y€ËÞ…0¶^‰\U£©œUõøà†ÂÍõbD‹ó?R!|ûhýîï� ¶é°ý©@øu|×Iî®ØÖ{˜“ìò,Fx1ý``~¹R…5ϡ疅ÃöT‘û5å‚ëÇ.õ5¬—¡ °4b¯ÑpKy75ŸÞü§-ò±29j‹ñu„ �iÃ,&Þ‰»Fó@u‚ÏÝÖuúäOiÕê§ÉÕélš1òVZ"t•¢/©Ñ÷Dã<²§h° `Lé² ¯ö]'j|è’ø| &ã³`Hìã¡Ý<±¥òv^27‰W~£lã s¶¢cIàN|é&cö>lé _Þž¼iÑ}_²ÙPÇn¨„�–�I)ÌàE_òððbN�EIþlY4$"Zb�—ÓÞ¨aKèáë7_l­bö?ñE­àleí\^ŽËæ.6�ü¯ç9T¸��ÐÈEš~˜]#•ƒÁ†ÖU‘¿fÞú›3@�Þ¾ÎNo·—·•¼Ó&@r¯…‹ìJ(¥âBÜÝ;ÑÅœHu­¤ºÇn§Hæð£#W³;nzË÷ß5Ž’š9›Ä|±6‰Ì½èP7iLNÌÜù¸4Õ¢gS+¡ç&'*V%qt3³q©v€%Aè¶Ic.ã©Ø¿•)ºYñZ«qQÚåp­Ã9¸#Î0ص †m™àA +1s#ÃÄs›œ¶H«èm/µB gˆ¦bX(,ƉzÛ?¬Ø2K¶ëË�àȈ­ò¸?b¤‘’Å0Å‘ï²Ù“;»ðÄk„"`oÄQ,ë7?B,‘s æž½Ez¯j?$ðêéCK­r¸|<Ý<½Œò]\½|Œ2mòÍ2ÝÝ8œ=�’8¨ JëCw’¼Ü<Ý­ ê6ï®æG^?  Ë@O1Î'. 6 œ]V€1•A:EA5ñ!"ZJÊoACÆõÜÞ™�eÇæ�ï ð½ÏÍÑDáží¢ ð?–2Ý_¨RÞ °2xóR‹ë$ú’‡‚v‚ 'p´%~u$]¶Ø‹Qñ®®¦n¢„«ÊX®ž‰¦×Ñ¡�5)·êCêš0ßàà„Š”_’ýÕÊ6Uæ»Ó¾+ÒE uxžA�Nâõâ‰Hê« ê5#ù¬vÞ/Å ¶�À½0°e’D9ÔÔ…ÀZþÔkãrŠÎ¹FQº8S}|×l]�<&Y:³‹Ö=oNæßâe2–·�‚Õ÷j"± ×q e¦u"®ãÄ$ÈCýçÙËE.´Ç°.]œ�g†^ãª8ÁÄÏCoœE,|Q½9`0nŸµPUëq%PW”#³æyJ(B6KL«yS²‡N?·Kíö3Zù½/·aÊR&}£vè Þyh¥k Û�ýbÓ:3ø‡É·íÎåÌòlÚ~w¼6'ßë¶è�N…§÷´Eyô˳¨ç€îÙV¦>“L„œ³ïyú1ÓÝH33XÑ Ñj ñÿ�9lF„ž3µèqÀðÅùÜ"ÕfÅL!PK»£9S�Æ×ÉW¤ÂÊ¿æÝ™ZH„ÈÈå)2®×n�§S£/Nf(æöÌ»Yò§8'™Ô/Þ¼·OçbX^§Í++bµAöøVŠjÌðˆË‘ÐÝ ýù ÀlSÏ~rDƒd±£A‹Hß `aêäA›T‚è@A UjÄfÖ}kÔðÈ8]a+‰P$MÁºÐ|lÄ€ïÃ/zÏÜgººÚ3=²4µ¦��ck¬Ž!5šê-ÿ¡Å1dK†,2"36÷zÊ„•ç&’ëM¿£4bŒøˆØ–€x#\cúßîÞ¾'Ù†ÖÃ<@È�}΄I:_¢9ò¥äõ4uƨYj&Û0—jÅào{áE±Å½ù\½/ø[&K†7vEyCò${¡[¦ÿ–(Ïi8Z5)Øú�F§•µz&â®&Ú6ÈöÙDªUUñ$¬¼2 Áƒb/Ú“¬BœG/, ¹q¬šë›±~‚àÊ'!ìs¬à¿‹¬Ù�4ÏÃþI=Ë+ó’ Öh*DÃUçÀ¡¤’4%È ²¢´ Meºw2“w¯ñÞC7¸¡v}H—D$+íÊM†y£ýÈT£Xà'e):üã+—Ó‰!r©lz ôÒ¦èMæÐF¹AMñµÁ²ÙëÏÔÓ“ˆØ…@*ײһ:ÁI‘Ó éò°B^lÎXímîSRãÖ àÌ2�Dܵí Ó©2%„h�oÄãà ùËbhûØÔ‹'D£Xšm“Š¢î9àXn®Ðϱʲ€Ìbֽ䊶¢¾Í6 jíºz¹XE'^w�òÀBÉ(3löÚ_C MU1¥. ½¿*›†Öœw{ux‹¯­Ì!}Ѩ0-0R•yšo»æ3™¢5hi.ÇÈ05ëú7áƒ?Œ”eÖT™Èe·Î ø#*´¯îŒÕ±ÿìÍÍU×wãÃ"iÖÃ7“ Ø6äž}£®cw$Šä­ûúv`d¥çè‰p(¿,~*@†š¨9&7Ž£‘$�Pl}øýV…—ˆDÊ¥A8¥³¾WöYÔYVÚñOÍÌYÄ6W©"OÕE¤¤Rù£ºÑªú©Jܤ³Rªg}u…$*º5…HGÞ¤Õ¤òzR½q{a1ùj²]§n<9úT¬©ÆÂ†©g¹ÂnBqA4ÅJg�ªHÆ"×%4Ì“Ôê[¡>éìT]ΚˆU¤SMc[se¯¹À‡²–¨éºPR&´g«RT°E¸ÙZ�nš¹oóÚºÑ:¨œ¡E6%¶³çt]rÏ"\ä>ãT±NÅã'+¡ß(.™©8Óq‡7�,ØG8_Úh±¥Â”l§VEaªÖò_)IÐ?Ò“IÚ'Ë9o£áÔC¯ &˜äVû]…ñ–Ãñw�j+¬J³”,X.’¹êµ-Á5.#·6`CèâãñòýI¨‡kó‚öªøÿl€“›!UdX…ù’ã[›º5Ðpp†èÑ ´ÄÛ†¨Ê,ënŽ®Ýª�J¹ßÒM#"½’Û­h¯ªz¹%w~Lq8JÜHž§QøÑŠ! ñR�N#|—Ð]8Š(&72Ò ƒ$ò³$ÄÚ¹|ÎïðûHâÇi$â˜Âˆüûéć=8ˆÜ ñEì0ܼÉlèc‡`ƒ‚0¡‰ï'Ø™„ŽuýÉÀpD÷"òî°âü‰ŸÑGßñ-ã'!—cD³X8ð“¸Ö=¬€¸ø–Ÿ³Ø·úA"¢h6Mü0¸DÙÀPÃ� ôÐÒ6mðF�l—ù°ÕpèáN`=bz-k.è°€@/éï„Wð™ôò¥@Œ'þXžà·!zðcqÙÔÏ�y�ß8páyfÓçº!¶æ±×ÑŽ­.ù# ~ô9þv?º"öÛ² zwmøpÜœ�½×Ë"S˶UÅüI×s“-æ«ùüì=^èB½ùÀ"Í·ás^N! 8p«ó“u£³£Åo¡U�µïÎÎn®ìéü~©sE­íë•]Z”1±ò˜ÅO*­)“µ\H¨F‰ã�˜*» ñ=aaÓ¡?�€¢Z6ÚÿbYûÉ| Æ~0 ob##· ³�œœš[AksuͰz·QÌԵ◳wl`*Yn�ÕÐóÆJuí-ñ`Ê�µðB‰; ]É’®6Üž5`7‡†ò½ãŠmUJ–éª�žx½º’¦ê¬ÛõŠQ>¼#ù,u.mï¶E¥Ÿ Èm1ïøµ3|»/TÈtÈ׌àÄÕ'³V`Ü{iªªêÚÒ»‘¥\+vnšáÀf�ü›;rocctç饥½™O¨÷«š±Á ¦››ÉÖg‰lÚÝ�»†ë9¨&æ´»È:g«Þ¬‡µ€ab[Ä �³â±RèctÞmsÙ¾èŠ'$ReÙÝCÍ��O2U¥¸y¦xYí1¿ÈŠ'd�{Š+±ïBñ#d)¸`î1¨móú²«Ãœ‘‡jƒT‡úíÓKûòP4/7M„o5sŸ5=­ó�2þšÈ_6Ô¶…Ľ\ËŸUqR¨duÀóÕÝ+ÛËJ5Ë5Êo!›\ÕMýŽx†rr·€.y4v¶hS¤�¬0ðI¤‹Ü˜v®�éÏ pnüb×S…£c«Šíšk8øÏ|àéßôá §Í¦¿¥°aKó´÷,sH™=1¼ŸÃüˆ¿;½ýQ;Z۱ܢ“ìÔs@yá=.7ÀZ?å2 ëãíî${¿<ŠÉ¥ºTÈuß·vŠD‡]¾Â&áøœà­Ü–êYóˆôÚ©;柦ñ¹GÃJçkùô Åt| Ü¿>‹ŽpCp'‰ äWÎ>ÀLåµtÈv"¸Â0ˆa{g›x¹DC,Db´·ðõ§-´íß²•8n‡¶™\ªÖŸJí>µn =À[Ò�.↥+ÞtÛç@¾8‡ûç™'àÞlu\ÿWÍ!�ªè°�S±ðÅŽµÃÿ¸�Þ¶ÿmä«xȼW§&Û +qˆ8Ayœôë|Û) `Ï'†´Q8ÃxTóq† }øŽ·žöÕ»ºÌ­(njcõec6Ûœï¹íÝÈŒ£—K—ö7 m)+§mE‹>…ÀWOQó7J«ýí>`}­o>~ÞüÂZ͘ÛÃÆÇ[u£êÝZ�¿OŠ;{¸è D�¸ïµ>[tJߎTtqüÌ2âèñ=dŠ|gõÑê@õWq˜ÿ[qmuçÍ¥ôç¹½ Û�ûO-¶%ÿÔ:WÌ—z­Ö|ËÛÿ=Y43¤5ÙÜ<¸S»ú¸í©‰f¯ÿ),8œT[Ù ‹ õVyч}€ÊœÙÙ=³±âÎZÉ æÈê´'Ï›=±Ä¬ò;£'Ò›íZª?uÃÝ÷¿î¡Á°ù6{¯ŠL/ÏþÝäÌ凎#xœ}UÍo5×F¨�Ù¤!iéÑ(twµÙ´@+AHÒR±,¢ª�†ÙoÆÑ¬½{³„M9rö…#7 !”C/Žü€„�8éœ�8ñlÏgÚä2òØ~¿÷~¿÷áßš´œ»ùºÏ‡CÎ:á%g™2?.qT6^2ˆh¿ÞpœµVË�lhD`ŸÊ5õí¹Ÿ�_}âÓÚeê§é¯Õ7Nc ZêWçíÇÞÜé¹j¹~Å.Þ­_·‹/껸_ß´‹ë×ì¢=s}^/no½åºÛwz/Â\SÖ›öøË™-»ø}¦WcêåÙ7Ôáì†únvc @ý=»²Èûëy”]âà1õ`îéã­óïÔ–(“MµËƒ¾ãiCÿ¨ïç^©±ãs÷ ‰£ùgíÚ­ÿ7ïמ«ØC7â\�Ìü¥å÷c* ú €ø$<éõ=A:ŽúëüÊã¼' SÉÂÅåIHýÐ\K�DÈÇQ}鵎º»øüS—o!´>&êÏ…+Izq]Ó]­Ív·{»  â µž×sp§·ÝÝkT¸ Ó-´³#hýô§-ü4ËÂFY�§~¼°¤~¹p?ÙÙÛÝÙÛn"L3³rý(Ó(Çp>q¦1! üY½¡åAØéüÓ½÷ÞîîºóYîë&g‡$–em�FD«@åeBÆ”í'“QLaÒ“ -µqeÃíq$� ð`Ä‘>‰µµ—še¸B+†dÈã#ôhÛ4ŒK�tó2 2èÍ‘{C Oª‘ù6掹9ޙɠÏcŒfÄY §þ޼’/ÄüЋ¡UH«=¹’»£‘˜}ë?¥TåòeîÊ"V]çšæ`'¸"‚¡‘"•ˆ¾p6Ó P}>·”䂉Ó\ô~….•˜Ü"˜¹!eèÀøØ§‡„ÁÚ zV²éÎ2Õ‚IRJ[5k’ˆ)‹Ç¹³ª^gxjk„�éÝG5ar¢ Q;L\ be85Ÿ… ÅHXݹ zW!ôDØþ�>ýƒ¼#<¡l4–Ú†caÊûT:ºòQ¦²2–isèžmØEDs9BÇJ°}tR"ÁácsÃE=Q‹l´¨M¤Ó)U�³4þ&¡ŽPÕØ˜6ôd¢¨žl'Zމ'l ú2:*ÎM÷Ve¥–Rˆ;ô"»CkpÊüÄÌé0NŽÏ»�™§ToêÑÇ馇 £Ú”•ùVž�{ Ï$xO/öC@ö4”ÁÊÚ¥ /‡µÔvåJ·¢èrogáej£Ž&*{ëúªÀKyeŸùº��¨�¢¡¾š_Q?,^üÌÒ‘¼òxœÝW]oÛ6}®Å…óPÛsœ¤ÛÀ¤A7ÇË ÎÐ$X±u0(‰Š¸I¤FÑq½"ÿ}çR’-ÙNƒ}銠vøqî½ç’7*Ö‘Œi:_Ý]Læ÷ÊÍ�ŠæÉ|Þ9À„Òrï\ç@é0]D’º¡É2£GIw3vV8«ôý(9ïtŽƒ èM¬RI€8Ä(ñC�U}©™àO:RY³pZøHà9U¡?µñüR,ñ¿ÄÿG�ËéíüÇÉåt6¿˜Œ¯|6t£þ‘ÔSš‚•“EŸLL‚¬XJ »"„­uù q=½˜¿ýá×›ßèÕñ'@ù�bc3ᜌžÂùyò8½6ì€^õKà;­þ^H”'µSnå�õª®ºÇl+7$g¥R�šßÅ}Ä¡Ü*—¬&è_`ñÇÎ FDiT•¶I®ÌíÅBê^ã÷0ƒ¿·Òúã´óXsº–òa ¹[íZ¥�ÁtµoäÕÈ…A/ÏJ™ßÂJr‰$+‹EŠ�-­–öÍ}X�±»9fi®SÊ…£@Rn°#$œÇ+œ°ˆ3@õ�‡ýV nuˆìuD‰x�¼'•¢(wêEHË”{UZ‚؈ËÅV_` ÁŒÜûB™¸•¨O{ÑoîÆãÉÍ ©˜D &ýèdv} ¢ÉhŠ…JAÆhíØÉ»ÛÉÛÜåú5�óì/ÔÝ«ÂT)4™{é˜ío4›|Å™ŠÔJ­¼ÌŽ6!¾„JïÄg©–'„*…&W2ÚÔ8�]Mg“Þ"5‹Äæ½E¶�:À²~þÎd櫞«hȱ‡Tàlš¸샖Ç51?yÃ6(¨ÙkÛx€ùZœGt­Ó6xK¤PnAZVê�¢æ—ïß¿$'m†ÛÁ!@]—ˆ€ß€<&…"M‘$|¬¼,jRV¶�¸Š¶Ícª[bÇßmíãÌõÖV®eßXKOŸU15¦�‡ÕU” —45=ËÏùãvmfV®œçjºB�F£î�–‰DÞø½Ëã\y¬, µ4 À¤{B1 ÁxžS`a» Po²2J3ú·ßW p_�½Nþöb7|¦Å¢¦Å´\‚¤jBÁï^x¸{mðòÚ{2m>¾ðÃZùÙÝÕ€kÍ๠òC"@X —1� ö(’u6•K¼£— î�Ñ%ôú;Ì”l¸ñû=9Ÿsè‚E#-Q´z‚61ÕÙ›–Ç¥Ú�òЬ¬É%B·;¤oN†¼V7€1‡®ÁQfš²§À².ANúðÄ¿"„ûŠQÀ¡ŒÀ UùBy½$V‘´ÖØ‚øÕ ×¯½CÒüõxèCVƒý!#øÌDyB9}çGf9¶Í�RJ,ñ,†+tU-<Ê èl{~¦—bUø3o1Úº(8©&qLü2QaÒ´Yµ¼Pšv,9äWq}_ñãì±þƒmG۾ݠÔD á‚b‘q&ÚlSÜôlÉ6ˆe]–Ê÷“O“ù¬��™—+«ß8pÁsGú™ƒ^·J<[´l—/´Oó‹t²6Ü¥7ä 8ô3Ýw:ûz£õÐ÷vE¯¾|¢3›,ç¯\;Å¢Y€¨ÞÂ'»ÇoÒŠö­¨¼s†…Ÿóc¾ê�þ†|¾üç9;<´:á0Ë·/*±CEà‰¨2Ø$J‚¢Í½y\{mvQþÕx u¤âοæÍèÕ±ÉxœÍXASã¸>/¿B5sx� Ì}¹ŒILpU°YÛ–%Û2ÑâX~² Ën½ÿþ¾–ìÄI`gvOKÕ ¶¬îþúëVw‹/“6a^U1-ŸÖ¦eZ´B¿ˆâëô)…l�–Yg¤ª¯ Öµ‚ÉšµªÓ¹°+™¬¹~c¥Ò›vÊ^¥Y3¥IšUgØF²”9'SƵ`�ÐiŒ(X£Õ‹,ð`ÖÜà?=U¥^eýD*rU’äZ+·æçÚù¸–©r@•«{»ÖÀ!Ã�–ôòL½Ð§æÍ:K:«•‘¹˜bƒlYu¤el´.öA1ØÌ+.7B_|ˆG¤ HàjÑÝŒƒ°Eô÷Á8;D¬wµPy·µáCÔ¾(;h¶áFhÉ«v˼SbÃfeGžì< …´â´£æAÈèy! ›‹Q©† ²™ÚlºZš78cw÷4CÄH"Éà‘³ t ¹{å¬wµô�98<b–N¡r÷4�¹´þ&wþ,À3¨ñá—?L‰¨Mü_V؇�lîÝz ?a§ûì�ü!AˆÓlû·„”$««$ ÒUê³EÍ-ó‰ f~rIòˈÂqÍV‰?…�Ô³æ¡Ä%—ô|µJKa¦~¯îÒ Ïö{0ä8ð =·tG¡u|Eñé%>l4¦ìþÆÇzLôZÖ<Ða)�³t¼VÁg:ò—…þb,üpæÓ׈݉æâ$´'pÆï=X^Y÷)nÀæG=µÑeÁµŸ ¿Y‘}Yg7}èp|99ù,˺eŸªþã“4�Z¼¼òêùqýøxòe->üu^u(ÆŸèSŽâ€ƒ·þt°®Šìx‘6KCë'_&ö¤~-e%ØÈÆÅÚ.gZ#U"¬K:ÛÌhþ"tËÑØP±µ;÷ ÍXÉ ¢ÖZ9ìµïZ~Aúxå/‚ðqîÏ–' `]ʧN»Ò‹ŠÇwÊ¥ª 4o� ^QiQÔ4Í#7F?½_n@§Vh£Ôv™Ø4¨²¤’ êÎæ¶®¡:£�w•¡®µ·…¡j¢ò×è6pU·á(‰Ø®–K’ÅÒÙ<=Ô~EGí4š «Åë�Ú ;Ÿ/\Áu?¤�IÛ*P ÉlÝv%j³¤f±¥ß.¶\ú¿âØ…§îOÎqˆ^”,ÎvÔ$€Õ¸ÖÚ4U?lü§¥‚îR‰ÜpÆ…#¢ÄAþaÑÕª>ÿCheõhñßN´4£ÔÝ&CïC0³7ƒ>†�½×=y¥ê§Œ2tB4ý]ú"˜¶³eo½ÊQ&L­’Ñ Ó^›sƒFÎxž‹¶µ¡Ùá9f}9 Ñr�ÆqnÇ„®Î·á®_Ô3€lÛü ³¶ðæ¨j”¬1,ìpîk%pªé*îzô1õVIÛð~ä)¥ÆDcݶ†£ã[°ç¸w'…%V˜¿pYñ¬r~˜æÑJ{R‰"bZ�Ž.k)’µ±Ó-�ªßF”¸såZïjɤ¹ëÝ9a½µž`Ì©¦’‚�Þrg)°þnÏÞø†Qbo=?8Và„¿a†¼ X<‡‰I)+õ.߯øT=:˜‚çkÊÆ]šJ›~ùÇ6 #›–Õ�:Ô ÔŽ*ãùó^Fì¸ß öç}ª�½ÍØÑ¦‘¨K¡W‰k &E"ß–ÎmG¢ø®0M™4vÒ]@#œ˜Ÿ¸Îø“8ª>¶®Vœ¦!̧'?–ê ýžžüDáÀ+ýš»¨öbítB¤œ�îU…É”‘!69Õ°k-àÆ5ý(SŽ’¤„8x Õ¹j¨è­?ä V]çÙÙ{½ÇVþw:æ�.C÷9ê9¸éÈÝ�UbÆfk^?¹fÛ—€}i^¢�D†CÌËRäf›­$ïÔp`vÈO2 ¢¿u´¼Õ›emÈUêƒ|Ïô^éC›JàÄœâ"sW(äJÆé“ý&èÒB·-¸»µsß\©ôþá·RpC÷oŽkPÉ‘^ï¥Iß íM �ÚÅ•¸Ü6e×~)0â÷5ï¨Õ}Ü‹÷Û°íÀ.Ï1¼Q¶òÞ6±£qæ0“b*ÃßÛi{cW�«�OöHzBlØ’þ½$·›F®° é!ºåú™f*/Ò‹b¨ÍÈâaà ¡^g‚Úvú!|[ѱTßóFíoü8Xlý•KM×®ó#—¦û£ÇÄ=9:X<¥ÆF7r*ü-]ÇÏ.ïÔôñJ. Þ`n:óo"`�që°è3¯¿›Ánÿ§ñNMûØc7ýŒáïR¤?�#—Ç“p­Žß˜Ú£ƒ7ø1>z¤ôØïã&2ÜnrJu-*Ývfý¡“•Wª÷gTÛ5Yåy§Ûï…g¿¥ì£d_ÿ·çîîôYÔ…,Oþh]½–æ�xœ›¨ÙÀ7Y�Í[59?77?O/C‰K93/9§4%UA)?% U`ò6iFýÉ;ØfmÖà fœ|•?T.??GAKS!=³¤¨ >1''?YÈŽêUÐJIšœ-P&”(J-N-K¥–õLþ.°W(QPZœ�,® "u@æÅƒÜ”9ù®à �Œ̔Tü ï —B8%/µÕÂÃÂ…�âiE©(æLÎÑ–W`²ãA€3xœu“ÁjÛ@†q“æ`h�½´…�BëˆÄ©{Lri‚†Ô¶(¾™•´–¦¬vÍî*Æ'õ }Š y‡Þ{ïÃtvU«6±…f53ÿ÷ÿ}þíÅÏ U�•¶`sšßÏ•�(i¹´P‚T0gÚbR ¦Aé”k”ÙEjA†šéè'Ó±FËôò°˜ ^Ð fQÉS3ç Î0q]L¦`Êø OHTA’WLf˜¥7K°Xð®+‹ÜpºWÊg¬¶Ñš) ’//šŽÚ¸¶³ö*FÉá¦MGwãá(š†ƒÖWçýq»}N£6^íqnÕ\ •aÂDíÛµt( _s¢à„�"¥“ã‹í8 •rWŸ0I¥4·ˆ‰*…ÚÜ›\WvÛÕìhx7¼Þô¯?Þv./áû.ôxéä ´^çñ—rh›\UæK7\7ˆûûŸ6‚õ„½ÿ„}Ë5³œÄµ*³|ï†UtȵñqùœwD¹3Çz›– f~6‹Õ=ßË: ?‡£q¸Éú¡f}xÛúódõìðõJ»êþ>§]\ N3IŸ9…´tkïu¬fŽ— /å$ÂIŽjž¾l ®s¿Ò®xËÄ"çÐÇRk‚qV¶¶ù�i2Zý8¸:us¦u>ÿ¿rÎ7,N&˜ –™Õ÷£W½Ê� ít{àž'PJC¾ÈC3yu~ôë/ØaYë¿Nxœ…T]�›0|çW¬ézMuAmßòÆ¢r$ ‰ªJ'Y,`ű‘í4Wé~|m ù(䎗ˆ��ÙٱɈ<DZmg)©&º’˜æä )S¤"Ä™Êñý&Ç;0†Pa%pr§€*Èľ¦ å—š¥ºr9ÖÈó”kÛŸˆƒÌPM¡Òºžº.òÉ‘îh�9M'B–®}s7Í´‡¤ÆŒ4#‰2-Ñ œžN –hìNT£kæ•8á¨mÙ:™TzÏ ÍuN[Íz ±®¿öª«M¸¼™Aœ- Eó{B¼Õ* ù wwÕ'ÏMÑÁ¡ÉÕÊaCFvEŸÇ[ß²ßÞàÙi8ƒÉ6^­—Äÿ¾…¿²ì¦ô+ó[{ÚÍIw¢}§‹xDÄ_>­Q°¶ÔKøç"þþ­™u]"~`[ûcr̘2çxßÎkšÎY]Dnd E:¯¦ƒç´èy>›êp27ÏãryñìVF懽[G�÷kØ^…[”Þ8w \èŠòr š)ø�J©Dh‡?”L¼¤ÌÞåQƒa¨Út!¤<‡ìOy¤rÁ?i(¥Øý#2‘ž‘£é CÕ(\¹þ�`Ý]¢‹ú¯¹Éïÿû=´Xû~½ÐɦöqM­ù"{IvØY¯ùµy½÷e×ü ݈žºTxœíTÁŠÛ@ ½û+Äæ²k–äö²tcBÀø°d¡7ãxäxZgdfä ¡ôß+Mb×)=¤÷BIOzzoì…mœÁ¶Å[þ±Îʃå’=bÙ–e²�Œuø÷d²°®îƒðPÓñHnÙ>Ìbd�’Uš&�Âkc;i°ÒË6ÆöÞÊè�eÐ ô•Öž¡£ÊÈxXæ‡X,\èadq/P'åñÿ9WÉf»+¿d›mQ®³·<ò„wì=t\±%Ô@u@ûoXóRq|îQ 쇚“^¦•sª+Fiá±A�®F`;ž,·Brbly©˜]+�С¤™Ï€NN‚û3°Tyü´AX*ª'ê rB[u8bØ#4‚žÊ‡€^'DD¯êxAiîò@Z§��æÊJ³Ñ¯Ìù‰F–ϪÐX¢ "îZì‘ï¦ô yv¨ièÌH¶ö¨+OÖd_wÙ{ñ8É›>M —Ñ÷¡�9Q!²Oõ|––.\<‘ë©5OwÚqY5ÔË„âG¼<Ò�e×ðßœ{͉òý«7ä8ZÓK7_+N© W]iÝ9¾7WnV EhÈßu³À”f[X3ÛNÏ+Yxý9õ(Ö—OÇ�±Mò a¾ã»zxœ�TmoÚHþ|üŠQór�D×û�* $@-ñrU¶ÇxU{×Ú]'!mÿ{gÖ@h/§ž„B˜�g^žyfÎN[p «\ZÈd�À߬ÎÜ“0x []C"L¥uFƵ#7B¥gÚ@©S™m9Ùj•¢—#84¥�ùÓp SThD÷u\Èf2Ae± �h¬Ô λCX¨ØÁæ˜B¼õè ׳ÜÕMI„#H�Œ ˆ4•l§=¤¢ìÒr\ RýªÂ.¢v¹66ò}Ûµ*d)•ò�SR}öˆD—±–r„}#¾ç�ŠÜÉ+–Ê×láIº4� TFoŒ(mÓ¹J9ôÇTŒÅƒuÍÜoiì™ðãÉOª O—¾´š°Ç¥ôÚ+æáM8ÂQ@ ©QUÅ–ékª¥ç g/!£±ã³(«‚æGo[JNÝs ¯™ør÷Óçì]ßß¡9~}ÊQ�ÒγI3Užˆ]ËdÂgLj'bª¾³öJ}%+Ý�9×3'ËñIÄž‹¬.º@žãS°ú8_¯`>À§áb1 W—nñ›P’”™dg„r[ê‡ÑwãÅÍG‚ GÁ,X=‘1 Váx¹„É|C¸.VÁÍz6\Àýzq?_މû%â^†äí)xfKM*OÑ YؽÄHŒ–ª+RȉÓ`‚$ÒÔ“Um½gžØB“@¼þ¼*vò»»«ÍÓz3¿Â)•d<ž.<ÉjÔû5ù¯}ìŸzê3M–ŽÜ=‘%œZ¯ó‘¶Ž}ï†Ð? ú .úƒ.¬—Cnö¬Õ:‘]‘ ‚ðf¶¾G颗BÆQE­z!i¼ýHP•uŠðŽìg$£’®DþîÕþ�}{ùU«uvꉽö=³wóâm±‘”J¢Æ¼+¿_nC ¡üÖûQ-2T°¯â`hG=„<ýï/¾éi°ŠFãiF·ã›™çšNÓv8'ãd6\�£Ñ|ÞvàëWøgŒ¢¿Ç‹p} ÿ<8ï÷÷=°G<RŠ« í3ðº¢+±7ku¸_\&)ÉÊì/}XWÇ�ÕÊ_Þ£´Ù[ßmªÑú ¶µ?ñçüíŽ_#,Q¹æ(Hg±Èö™®+A¤4Es·vÿVšîšÞ±iБ² T¾¡�æ­®M‚ ëmÒ]¼uh;=ðÀ¦{(ų,ëò'äQóŒî&„³ ·™‘Èu~ìç%j ¬v…v¡qÛi}iý¶Kضð;´ýß¿:pu�ƒáý…·¼gË`йl}k� ¢³éE ×ßöeŒÃÛF»çïØÀ…úá†qxœ[Í?…mò$×Í·Yþ2N¾Év ?p—½¨xœ�WQoâ8~&¿böNªK[º«{Y®•Øír¢P½UŸ¢�˜Æ×G¶Ó–»í¿; I *{Òj�ñÌç™o¾±ÝÓŽXD\ÁŠÇ èS2J¬ô³/Y6"ƒÀO@²�+-ù2Óè¦ÁOÂS!a-B¾ÚÚ²$dtÄ@3¹V VæÇõä®Y¤Ãm¶Œyc°D±.<1©¸HàS—0|)9¨ˆ…°Ü˜è+ÊgžçW7ñ5†œ`ÅŒðÃ�“ ´0!)îÎá*àÉ{9¹ø™Ž„TðÀŸ˜);Kb¾æSÙÒ1OMD Ö)²BQˆ©¹¤3AwôZòÄä¬à™ëKH¥x�þZÙÊ“� +c2Ší‹Œ¸ß`?È3 ŠG?ž<`óÄÚ¤–al5•wA<ìå€*€ B~šÆ¢Ïf‹Ë) ´êà ÛÎ^üucÿpmƒ›cõ„aÔÀ“nÑ}Ú½kê+‹£Õçˆ%�mØDЉ*CD^2šØ 2í/1ûvÞìšR·d…E›#‘s¾&9>ó8†¥áb•Å]@OÂø>Z|›Þ-`0¹‡ïƒÙl0YÜ÷KnÙ³P 䈌²“~¢7XEß g—ß0dðu4-îɸ-&Ãù®¦3Àí`¶]Þ�3¸½›ÝNçCä~ÎX!CÙßÃìZ ÊC¦}«Bâ÷(F…ÙÅ!D>ŠS²€¡HCCVºyÎ ±±@�ýUäòëƒÊs3´^NoïG“kLy´¢ötáYrR£(Æä­yìÂog¸è'�ØQ˜ktÃè+¾Â ¯b!¤ÑùW¡4ùÞ  ÷éì¬w|ö¹wÖ…»ù€Š=uœ_yÄYÈàÔÁÇ<ú¥aãºn“ìéÙ�Éè(�¹ÈJ¢4I ™\ùY¬=##OñœÃçO}ÇyàÚÃÐTˆ:øC¦žÇ"piA„Kè„˶ó¯Óª9ÒNˆ@6omý T¬\³Ôn÷�_�ûÁürZ-Ét&˜Ü�ǸikÍÖŠi—V»Ð{éu!¯ìB N‹\Ž/Äòošº|ËÜ!¯&�Ì×ÌÝ-±�uœC¸Ä¬òDÈÜw^çIðlÝtä»;e6‹÷l.ÐÁÏ~}ÅlÍièhgZ±�òð(Çï1ó¥{äÚŒPMÒ×B¶±þvÿ=o)„V¹ëÞ==žpíÖÈêÂ×–ƒˆ4íº¸°�Aü�°í’{>œ›.µËn)” Š<¾Ð›”‘í��sz=ZxÓ¯z—Ó››Ñâ š«UX:Ý­:m„¡Z[­%ö챿ƒ´˜ ‡%�N…~ïÇÈ[o"�Ó§Õzuðßæ,~�7CYin7U¢„ÔxÇÔ…Bÿwñ¦Tü!±§8�Ÿ‡73¢a §ã ÅøíPÐvv)‡Æ•ÁV¸-^<80f.LF´…]I3í˦ʼý,3ÉÍç¶ÕðãX ¦ÀXR™WjÌp2]\Mï&P¢Û肯³'ªÆ|"ÔeZ¬Ùl:+p>@K_ñÀK}©˜•ǤÄ{઒2.žqϳ0Í2hÖÿwj|¾§1ömïO;N �Þ¹�“‚—uÄ‚Ç/°öñ™—Éâº\/Ä6î>Æ–H z&žtü"é¦F{–`¢Œ^ Xi׸£‡}{á­€EX tGh!NÈç´Îb „r¯L{y5�jÝÓ<7H ÈPðèÙF˜££›gÒ.ª÷÷fxCx¹±×XÄCö“;H‡ZWän(=jéz,¡š&ãBnõs8ÁùÃHÓIÕcPzï‹ú`A¨çj&{fŽ- qóŒÑV¸ä–“ˆùaŠõü¤ß΋oÇt!Ø´KSÆ/dx>~üh†¯RW…}Kx#øðâ’vq‡–J8LÙ•ÃÜK%£=’ê®bÊ»¿¦{½ªé…Êœå £¹$׸o†R;ß;<Ñzc°ëW‘)hv;ŸÎÞbt3lœ† Í׌šü„9½�ާףËÁx/´HÅÿ…ž ÿÎæ&cëELsxG;}©i3J�Åøjÿ‰0üÃ/ÑÛ 5?€ìt¼ÚÇn1ÿ¶ æáó^ß¡Cnå„ïnû´”ßÔÛÇA”b©”²Ãhã f¶$ÿ=WE¡k›^~ËÁD”÷NÌVšþ ¶àÌ\>{Þõçy}D¬ãûÙÏw_Ä{ß­–�7ß­EÏ©âúƒò­·,þ‡»ÐB´nã­Zç˜^¡õ³´×¯ØHúxâÅþ¦¹Rž„ù«óæ;¾y`™7S¤öxú©ó$¥ëMŒ+xœëU]¢´¡€•E&²jòƒå‰9ÙºvÉù¹¹™“—±VA$CÙt!Œf6mã%»,+D7G˜H(3MA£ ??G×dDf^º&—¥–”åYs�&7rHË�é™%EñE©Å©%`�šÖ\µ\\eù™)“·q˜B¬yá"ŠÝ-N£z¬2 w(‚�V¢kW�XTœšqI5Ü= %@Ûã!ªâÁŠâS+2‹K€ÎÖ€ˆj*Ø( ¼€l ˆ t©+�ÖןüšS‡M”â‚3=¹!^(à’æ€™0¹—K"º›KŸá_qnSÒ°—KC†¥Å`}: PÇ#Œ>Æm±‘›Gºf,xŠSSópX�¬ Š ¶¶È!B0¡!ÙÅ“§`«`óÃI?~´ˆ™,Ã+Çc»ðªsÂdŠ“·ó N~Ék,‡æ€¼ü”T­Ì˜Ñ&|æˆ0ÏåS‚„À>U„èv>˜¨‚ˆ€¿�¿»§³£Â“…øá*ä'Çñs"ô.à×OJLΆ$…ÔœâT.$9Cˆ®KüNæ¨ÎTÐÊK­(AN¦8âz²½€ Ä�kŒ0š# ¥¦€ Ѧ1)À€‰ð¬€´ "� ä5°YŒ‚f2莄ÐP¾‚’—¬g“œÜ Ȧ ‘-ÖQPCDqyFfNª‚4â�q:„äÚõu%ù ¾Ðy$¼1°üguê<»a1ªÄ�|Š­ZÍ[&=ŠXVÌuKNlaNçZ M�Í@³Ç2Àjȼk„ ôƽsž9Ú1’¢€ñ‡}Šj%X�J…ÌUŠÇz,õU`Kfž«ù_º!´�ERiÈÆëfȵ·âh¹Ü'7YÒ­mXâP¶!Ý0“Î4¢D§6ü;Ùe†6 fOx6ü’qšø9•ýÑÒu¶­�•ßd1j>åse¾æœíøJ±4v±us¬w^pe­¢Ì Å@…ùÔ§Ði®Fÿî®u£q/ZËsº¬þNò )ÞUu¼$xœ•’ÏNÄ ÆÏ;O1Ù^”4ÛèQ=˜ožI…Á’4t³ÖñÝÐtSðâ…À|ßï˜Ð)@…ã°Œ‡Qv4ÞKïožnït’ô¨54Rõ�jA�`¦h ÷/ž»ÙÛøà÷#¥ …OÑ0Šôƒ~ÛúÊú »³¤ ²=ìÿAšq¢ÐÃWfŸ£Óg2‚nbÕz ôš*W)纇·ŠîD”Õ-,‹X ¯žK_�rS*m-š9,Œ—·´xyJ}+†)gdÿ¹ÅºqÉ ä_=e†tü�.*YV<ÁÐP°Þa§ª_�¾Î7–¼Ê2¸ÌxœÕ\m ÐLOO¿MwO«µÏöwØ>›,ã‚Íã„3ü�sΊl^Þ†9ï±»lÃfaÊrÅE™Ç7›ÀJ¦Ñ³,g«,ŠçwˆÆ6iÄsV.9+y¾*X6§‡W—×ìOy&ìÝæ&‰gìM<ãiÁ;ìÏ‹8KÙóâ ¶F€bÉ#vsG«/�ž±¤‡]d°IXÂ’#X€k†) £(Æ!Vf´d »Çâ-Xœ>D"A�pS.³¼`‹ø'¶7i¯âH1q‹$N¿ÒŠY¶ZƒÔ"Ä !žµ8S¨›8%š v—K–Á✭ól‘‡«BpžFˆÚ’1Sðúâlƒ²¿} ä 'p=ÀÅé”—­ˆ´ ¬µI9b,˜ ¼2@ ‚E ×ëäÅ'¨…é5Ÿ•E�ÍAíü{¸Z' ?˜»ƒÍ�{ÄAÖψ\¥}ܽCüiæpövÉS–f%IDŒ¢"AH–aˆç³MÞõm©lÇR�°"¥æe¶FÉ…%šãmœ$ì†d1ß$�ˆãýpòúêzÂú—ØûþhÔ¿œ|èiÙòo\ Š�Á0ƒÙåaZÞ?¸úí`tö–ô_ß 'ãb8¹ŒÇìâjÄúì]4ž]¿é�Ø»ëÑ»«ñd?æ\™!"ñk�$»ÊÀÊ#^†qR(ÿÆXuIÄ–!gÎgŒ4"a­ï>g$Ø$!û#«�æ×c…¤�ÄzvõîÃðò�<œ£z:ì6�Ñ3uLšÎc‡ýí&Ãô+h”�KƒÕñ6¼H²,';™%¾í³îóããîáñ/Ýã»÷‘Ùg;;?Çé,ÙDœ½(Ê(âó£å©5ø0Žœýå_¬±ÜÎwwhKwd—ϲèw~¼`aŒ§e~7]£ûHAy·CMxÚfÁ?Ù! ‚l>/x™ÍåÔ:,—mvÀ„i³]Öýþ�v[c ååÔÅIØÜ!°Ùͬ”ƒ´Flªñ žžˆ—H,æŠø¿¹ _0¶•'øýOäëþáçŠ�Y´ÐóSøy��ðlñ*L,̰áÛž˜E –9ƒ3’%›-Ü /Ï¿M_úçƒÑt<|õñ3;aÿÞ;ßë°½!þá�³½û^ãâÁo“éd4œõÏ^,½| ~ R>-%š‹«« Ñðû–¿N¦WÃóé¨ÿ~ü{oË2M:-;~^Ýc“ñ"þT.ù¯Áh<¼ºœ^^¿}9Á*±ˆÄFGgºä!îï´6°ì—çSÜtQg€5(cœ=$ä>/Pövù÷ܺ{ÀM2Ôx?þõ³ƒGA’~T–j ä©À^Æ+�ø³W^Éa½AÄ¿Ù�qšÙ�¸èMÙ� ÷=¥¤“öÍâˆeäøï’„‹¢'™F{üxü¹Çž�ÎoÀ¡æa~‡V¾ç^¯Æ-ùêÿw̺Bû�ž$˜K²D°ˆÏ’0—™LÊc`XW˜ÜØX §Ø>ýêØ‡wÿf3Ÿó¼£Ž“x$ÆÚ½fä¨ ƒX<³ýr%…ý d;v<‹5Ô gálÉÿ Ý^Ìâ8¾#ÞHy®?C[!%„8‚FjÿÊöñ/Ï✯ Éœ®³ÂƒÁqzB—�:G�Ñ‚=6²TÓ2ý4$' Ož¨†i®¢†§S¼Oî ÛÇŸ°¢:\]+t`K|ßU@‡UQ ;qf>L@Â21NÑrÇv6r0µâ9 ;9a—×oÞ´wZ­œƒOé‘iá Ñ3]‰½Ð Dk¼m Î`Ü‚lÅWÝ©žî÷®0±*.¹ëá© �<• S�' Ú¬-ú�ý=Ë$Aèy[¤7¥Ú …­{Ø<ÕÙ’ƒÍFÁ�º˜@Š ×ea®(_!óÎáôÝ¡£Ý�¶y~ ®/E)m¤¥‹’‘ó Ä|ä[R#Õt_3½YÂün£¨eç°ÄÀ Þ:‚0w{,f/˜ÜÑŽììà n;2ó1/>ÆŸ�¤hµ:¬¥„VO$aQNÅ%p¦ðPÑñç=DRž�–õÈN¤bê’p�Ÿ-3Z9¢1;àUm9Óm"£â|ÐüdH7�Yœ}Q� ä9¨†¤ô(³5VŠéä`4º©•?UÌè�?Ø£¬ð!�†ªi™ƒQ3Юu\ºò¬³G>¶ë°])±q…¸I�FŽ(Šòí‰r ŸN˜ÏÈ�zA>ì ñIÆ(Dc…Ú!:‚]Ò¶IÖú)Û"&¥ÌZ„•NNì{wéP?à5HS*H·Ðt¢ªréîÑBQŠÇ¶Ô�ä„ð“麖ëÛ:`èÈ�ôÌÙ5BqÔÛŽ¸DPÕ‘TéF£°¸ vE1éÝ4�ƒ(„ÝÇ­u©7ÂP9(z�Mjv±£ˆ�Þ>f%Ö9Ã�"îñG‹Lˆh}´ ùÃ�m=]�å„Qs²%¤/ W¤R²™w3™ŠÑO4.‘g"GSqm°²…]‚jH"‘4XË�0Xø;P¤h볡^�Ì‡ç¿ .'£ÓËþÛÁÛþø?Q'„÷ˆ.<ì�—´O þŒØŒXG5£g§ì—&�^Á§VW¨Oú¯ã×ËIÛ,ò¤Gçr»zÖ¬Ì]%ÅõHÛ�ûô¹Ž¿e*ÊÅö¥àŒƒäà´ÎRŽE–°e»)UNìb¶¾ vi7uìÄe›™´Z_ð(°C‘�´n—˜Ü_Ø)¶»‹v3[­B~$ÎXeƒ/232ÎA“T…dìøs�Ä/�H4~4¶è8>¸ƒÆ¬÷&‘³“²Úñ~ò}ªÈ6ùLÞ©½f±/ªz=‘\�8•ú°æ¬’1,Å›<Ú“m“pp\ÞÅ}´’ÅÈ S~;•`(Aé_pTT<`L=€;wnžmØ>;>úÛ\$F@b`mP¹i¼û�ÊhÛ©�ã .¯Àõ,}[ûTí°ãÉè·lÚóçù2ñ°|bÉ« !'�ˆÑ¸„–aªv"kt~vr9zp Ý>r.°�E[ÖµÕ¾«¶Ýímc?R{ØjÝÊÌðÝ�¶³F¡(œ~QbuM!Bõ(ûÐÖ¬x�H%MMK:†…öBœÇ[<…HtW&Ù-ª¦Ã–ñbÉN|—ÒÈ*¥Gä/³ÊƒJ¶ŠÑhê@N�ž ¿ŠÓàêE®@>ßçí�u&Òp‘º/H>VX9Å”2¦`TF†p’D$çÔ 4 ›¢÷W�FßZ0�¦aù µó=“YIKÈQTÓ9éd&W×—çT°…FOT¨}r­ËWyú÷÷êÖ ù²0��‡§3P”ó´Ršð’⮀ˆOº±¦dÞÓò@;Ã"áy\¹ˆq*v²*§¾Ú)œ¤¨^ÄD áŠ1Ò¤#o0!˜ü㫯}s¿¥ø#«#Ûªr„¸lüõ8lʯ LHQpÀ0ÙÅ?‰  #‚¦Œx4ÍÝÑ2Ô•|ïSwOÝ�Qn`ùò&ëоÈÊLÜféžnÖ2¡€ClLÁ~ °B ÒB …±‡Jzl¨UÕÃI!¤‚†š­[¿LÂô«½y|6â;|³ÉÒÍêðes�éP?C\" }&¬ue–hÙÒØÚº€qÜUôJc‰dþƒÆS9  iT'Dõ³€8ŸÄ)·™4t»‡öÇÉÿ”þý@þñß»‡7qÉÆ¯û‡ÇÔiaä‹Í)q¹WÎTŠ)·9p_Ђg~Ì®òmÙtõ5oƒ]! @vÝ—µ‚û¶©Ë¨}ko†‰—wèg4±¿jj½²>U—�ºû­(§â'¼Ø<éƒ|ï sÞ*R+…õËé Ž^Ü¢jnÝ©«Ù¾X›“ím¥C§*Z�^Âf‘ì¥ïZ.�÷b'º´¨¼§pÕ;ÎÚ_jÛª¨í8Þz*j^þø›9Œ*þ@¤yƒ·VX•}+œ‹x}G4 ºI¸0f€…  [.j`z¦mº1¦Tï+)ºì*èÌAJåB�°0qT×Uðá)ž+žíú‡%šBøõè˜Èí]§OXCp¡=!@7çY‡ÂnD)¢)­Öê¼éÙN­µ²ÃþÚÆ_såDrµ·êŸ¯z<9®..0 ÷õ8>nG·KÁÝÕš³’—îöÄãÏ7¢5(Ø´QZ:ö k¦ÂXf%$*‡ÛjÖŽeHÿŠx�,m×ÌÕ-cÍ‘ÛÌY®3M»B¥Ç­âÐ5H?Ñú�[Ò�d¡}Qï»­rpáÙ«¤à’-q<3–g$ð±û/Ÿ{ý=Ì^ës/`î÷=á¡/¥»ÔíGøì“T•lC—1'»PXy)mÝ œŽìéîøDjZìf ºØPŒT¤ß"á)~©±„{rÂYš¥‡@n–ã—FPô¿JÙ< Ñ—!XüÙ“Uˆnný©FQº0U‚j¿ˆºAYöÛàIÿÜa«5Cù\¥øeµ¿Î°í«Ø¬¦³0™m¬utÌ ÿŽÊ`Û«i¢çüën¹·ÀoÔÎ ìøNŠ}Æ¥Ç¬Š†PÒ§m¾ gM©@#±ÆåI°'?íÜ{Ë OƒùÁÖ3ãu­X�ȹ– izüºl T`¥%KÁnÊ‚‚…í% […ål‰Ÿí”eFô!Wž}‹!‘(¤á̳¬ÄöL(?õm-^µvmWn¨ÒMÔ¹t -*bšö=o,ß¿r×FE\ŽaÔÐÜS'¶:ª¼Ї$æœÚUßaˆIœ«÷§H:ñó05v.–ôyß2K"X�àj¿¬4éï€æªR†)+¶h—|Û›Ø-oaï^.ª½íÛß·þ2÷,¯³üïjx ®-@ FWg‘ï‡Û&M]Ce˜5C³ªZÒC9%7Ëm(uøú�®“ã§¹o·PŽwQù­+9låÇEÄ¥†ç>Çoéê«\ð‘vÏV¼!.„�K…¼xx½|÷£î?ãËs'�\K¦³néJþ(Ž º.Pšú×Àjâ/¢“á:ºxœu�A Â@ E÷sŠÀl´ z)hwâ2T›H°�&"ÞÞŽµA7ÿÃ{ $VØWÄ�ëýq³ÅFüõ�—¶éJE4¶wâéŸ6V†‘j†xÈ‹ÝiµDœ“&p%dnq–š e¤ ÏŽ I�ýìFŽ3s¨®Ñ ð]’¹I-0ææ�ó(Œ%_ � ýM¿‰Ç¼kÀV °>xœ�“ÛNƒ@†ïyŠIHšhµbÔzÓƒi´6¡.7+̶›h˜Eëۻˡ'iô†ÙÌÿýÃì0ØR$ =÷ž^ûÓgÈÂ4^qŘekI&xDµl) $"‡�§=ö6\Ëj·€‡!9.Äi„°â�QaUA€VÛ²+7 Ùä.´×’&!„s {Ü»á:÷•I…eùß\`8¯à2äp¦Ãœ8øÞÙ‡T ä!‚P�ú^¡�L4“aL&Ê÷Œì{LÝnߢ3LÕùmzIˆ `E�¾·HqÌ2%D …¥¥7Xß)C}¹@!®"Øù4ã ƒO,ÒÐ=ÂdÙþ*tŠEÜ8šà*Œž6ö;Å0Q††ÈpjO2z§î½F¢?-Ë$!÷1ðÛl™õ»A·CLšÉHô‰5nbãÐÏËb\Ó‡û)ð^ue‹îYr6ó%�v9ìºø7Ñá¡§”| æ €ÁÞœ4ŒÂªÆʲœ=e=£XåôÑÚDŒu>�”�£ëÞLªÛõÝÎ_BÕÝÑ<%·6&†v1Q�¾û±8üø¸ýÖÜ\o!;¥A;™;7¥Ú¸7Ô*äZ¥&î‡I¥æS�sœÕÓÕxr\¬}_×ötƒU�㊯uæýçªä׋»òÊûçåWñ~·®«zS·Ù;ãõ’‚ñvñe®Üö¹êxœ­WýOã8ý™ü³œt´´l¾Q©Ë¶P©[P?´ÇqU•&Nc‘ØQâ´ôþ÷›qÓPŽ�½ÓÉ*µgžß¼7vÒÚ¶Û0ðyÐgÌ$ÒSs;fuXÈ[@Ì\ž¨˜OR…a láÖd ¡t¹· œK…ËbP>Åâ0éé/—Ý!\2Áb;€›tp:Üa"aU˜±8áRÀn•0ì" H|æÂd¡³[ħŸñ�–ÄMl…)Ÿ1�rÚl×å4Jê”wç á&ÀÅÏ8…Ø©òeœÀ”Ϙ.;¹B*Ï€´EÀŽÎpd¡j.!¬ Ñ5çr Ǩ šss®|�˜CËil‡É²ráôšÆH&a¯“eJÚ/Њth�ò1Ž‹)š'CM-ÅÜu*ŸJÒáM a 0W‚EÁ‚ä[²Ååˆ9*©ƒ‡¶³;Œô׸9VOº¸£é®Ü§Ý«º¾¼8Z�ûL€�J«‰“TZˆ¬dœbÌI•=AöåÌì�ú,–»²Ù—)g+jÇ9˜h-¼4¨FÆ÷öàêz8€F÷¾7z½Fwp[ϵe3¶„âX Gdl»Øj�õPö·fïâ S_Ú�öàPŒV{ÐmöûкîAn½AûbØiôàfØ»¹î7Qû>c«6$�·]Ðʆ»ÜeÊæA²jñ[lÆÙ.ø66ğMêj±¢ÅÏÏ™6�Ø ºÿtWdíW‡$ã¦e½¸¾¹mw/‘rÛ#{ª0�9u£\“÷Îc,\´Å=: }…a˜ÝânØ ¤ŒuŸ‘‰¢Øo 0w-ËܱöL« Ã~ƒŠ­Æo\8Aê2ØÄ>ñ˜û›ksS®j’»/&O© Äô³n‰B.$|J äøv¬�ÄØgw#8ƒÆîö+£¶ ¦IL ²ŠÚÕ@`Vp/S€=û8pâ8ÂqŒãäC ½œ‘…`}"˜…`‚Yÿ™Ñ~Q¥Åè°(FGEtR�]Ф( §( ·( V�§�žê«KKßVJæwÕ¦iíîíŸØÇeÞfÝ0ð1‰/(jŒ—à8¼Ç›¯”}ƒm|zUñy µ„ÚÆÕ²ñÃØHøßl¬ ªô„)EnÖ!‚S %é•0u眻eœ¬Tª€‰P9ƒÝ2Þ—´å SJùMZJÅÚ [Æð;s4‚ÓSØ/c,ÿá£k4ª¸¥1)q#f*�\¶ãf÷zpÝþJ1»»ˆTùÔ žŒõÄþðâŸÆuãÉ0è{»Ûiw›% Û^ˆj VʪB¨ µ­-U*¸_fÎÃù9ìé·Ö~óÁ£ÅŒ †h3²fen¾¾ñÒ«gÿЀ5»8º½ô‹/ýâk~q7ó‹W*¤9v«â4zsÇGå×L"[ùÿŸÍ»˜´Ë³2[µ­ú{ë—Ù/‰®èÛA ÍÿmÒyYK‹ãPg”¨°©ÆWÍ?úB,"M-øIŸ•¼»ÃNWÖ}[±¢ êßPÔ˜[™[¯ýÉfù²’ ¯�l¦°x×ûzOrDs§×ÖÇGp†}Bÿ}Ò™y5›tqlˆ��:èK¥˜C,ñ—D?Õô/ |;ĺõK˜ÆpŽÇR_ï ‘Ç½`¦�2²y9Ká! �hQZ–M8¢¬Ï¯AgüN<˘ëˆóZÇtéù�©xœ340031Q(ÎH4ÔKfx“%©vÅ-.ÑÌ«ª%àÅ—y7?"©È`(˜Z(pÎ%ÇO•¿-<á·íRá³g&!©((HÖ f|÷í¹ô³KÎŒÙqÂíi›Ëë8ÞÜã'Ǿ[xœ�Sïo›0ý Å­“&HI$¡­š µi£õäIë¤IUâ«`�múCkþ÷�1iH¶Uý‚ì{ç{ïÞŸ{6ôàÇ׳~¬¨rZP®ˆb%  ±YY= ¶Ê83BßÃwRçð�¤÷T"aRá½–_$)2(Å*nŸ^gLÂË‘².¨„G DP5猯@#�°UŸò%# ’fŒSÍW R’ç0NŸœä 3,ÒRPÇUB%Ê”J I^¦÷Ê;ˆF�<+*u‰ÏöGÆÓ¼^R˜Hµdå ‹wB5ìÄtýAv`Û†J¶„ªJ[âšq5  z‘™iÉ¥‚šK¶ât iFô*϶, ¶a|ÜÈtOm[_uYô>X\q¦œö¶˜]ÿ‚^êÚ¿m+íÇšâÆ¿…)øOÑÑhýàt‹¹œÏ.ÎÎ�O:Hh�“ãó³‹Ùü²ƒ øÃp<:Š:ÈÈ ³áExÌ}ƒä”먹¤(Û\U5úƒÇõ^7?«%Qt¯Ÿ�M�Ÿ½J okM^â&ð¦ã½X¢Yÿi/ªÀ"häFâášÉD£…r�ÃdC|ý˜±œ‚ÃáÊv),vNÛÊË‹ÎÄ�1ˆÅ¬‹Ô“pªƒ:�XK´6I¼� Z¤Õ³ó ³“ún�ܘg·Tªw_ßoQ%†a:ÝrZ»ÛÕ΃MMSg×ËZãg 4—´#žCCÔàÿ©ÛQ†/&“©I×Õ8ô§Æt«2Rñ¸~kÖs†?¥³7œ–çï}ÞùL+Ÿ›9¶ruxh6ñXwÚÌ “cG� b¤™\;IUwÍ|¯&WMÃïtzkòÚîp!»ýÕ8ÚRµh~stkšÄÅðû´[õŠ™Cè»{¿Ý8F¤2¿"xœe�_KÃ0ÅŸ›Oqa/]©Ý·!t ¥ ú *EJšfm°½ Í­0dßÝÔ9i»·œ{~79'3��/wW P•.e%‘8©kt^TëC£ò‚À�¦°œÏ×ðÌÛž¸ø”Mà lµÕ­¹5¼JyP7ùÎîÍØD¡(ÛLÂÖP¦�‚bÇ´Ìä 5­ øfNk­ëeBPpSÄë�°7Hr³²²”ØI´éºM{2*G™�(xi¼YuÛ=>¾éGHÛ}ÈŽ µHlÕ$z}³Ôy²HQ‘ÛóÁÓpˆ¼éŒ“A>ˆ ÁW­2ð´ÿ™ÊsÀñ%÷ yéƒ{]s.^gûS %äŠÎŽÓ£Æþ©…3ètÉœj8£Z—ÜoRgœý‘ø²÷ç�*xœ[ϲ€eÃRFŽ`Çxç�ÎÉE'ŸdT˜ü–QE (hï–™—˜ÃYP��àrú.ß³ÑxœÅYioÛHý,ýŠÆɈ6¥ˆ”,ËñÎì:¶“ ’Ý1rÀr¡I¶¤^S¤ÀÊæ×ï«êæ!É `€5 KêãUuÕ«£©§G]q$ÞýqÑ÷„^®bµTI! �&b–fâ&]«ìær€E´î2]m2=_¢wé8<7²ŒÅ¿ex§²Læâ+|/óårÈAšÍǾ§ÝîSÞoáD(ãX's¦É=äAÚ3š~œ E_ܧ1ˆ•(Ôre†= 焈Uª“Be3îcâ¹°«ž»£¡cáÞ�]ftÐD.UôL��Ük{{×®ýîTYõú^edS£ò �Õê½è9̘­ã½áB&seTï7›)fÃ^à†nDâ~ t!Ö†Ä?E(ž‰è ‹Þ—ù0¢ÃõèSè<ä5@Á—ðKDú_"çKøÐj¿Y½”ÿM3]lš‘^ðÄHc€'-yË”¬µ/ÔZæ…ödQ$‘‡Hel]ž(™õÓU¡—÷•Ê0¶”I¨(è¤x9Tî9&ªH1êëÇ5% 8±PI¸!ŠºçZ�–óÖÿŠzÝ_Ë�kC1RŒ€ÀT+“aHÏx9“ÜìXsT“ÆX+�*¤º/¯8S€ à*¬&ÚÈJ3Sû ŸÔ¤÷Ušç%ŠF×TžüZP �„k©9§Áð{­ûç¡êÞ÷cÏF�,¥.Ôô$yÙä̶L“H7’Âý…èýÆ›ù žƒêöÀÚØ®”I€¾¨Õ'Á‰}âS·¯ÿêÐg¯£q]±Sß?,«$N¬V†T/†î^ |ÈýŒÇ§¦<ÍdÆ'²¹‡–Ó1àÃ@ЄJLÕࢃk—ƒÊ¡š¹\ß­e†`µ»_™B£¾©�âhk¯ 1bêt"—(©Xn5Ÿª ͽW³z¾X§;¢MjähOúOŽxräl«ñáfÜ›!óà"ª×̨e?z4K=j³§¥Øö$?Æ3ê¹� ûk¬–ß]c•5TÝSÙ>¨³EÛ:RKÛjœaâ-§·F‰öôÔLOL{¾™÷üC &vÁÄê‡#tó8 âÎj~ÍÒû¢yîn}{Öíäźì<Î<·?¢ôZ®ihäŽÍH·ƒÜö†.åJ\àš{MñÞ‰y™êÒFòsgY„E†±“n×¶¹Þ ‰û »Ão'rêcaÖ}‡Dß‘W-$Ù4qe'†æë¥ùêŸUë|¬C6d@zzvvF:¾ö½3Ö�6>·§ÕÆ‘pa'N»Æ’UhàJ…K×Öü6Ý‚¯<ßi|p5¤zPâîŽNè)ŠK·!a�¨èŒ µ­¾ ¤ÇêûÃþÈè_óîÊ3þ¸�3�yÁ>NF!㌇ý“]h8¦ná„râïã„^4aœÉ°ÚƳ:ÀLÆ[‡õxDL¦{£°Ö©¿;ŠqÊæB:#Éc—^“±Í"^ïtâ4ŸO[Ÿ§­Ïg–šȉ™Ê˸à§8ÌœIiÌ“œq� ç2÷êÁ!“ÍeÞUƒþSÎeöÕƒS¦“Ë̪O™J.³ úQòWÇ Œfïèi—ùY‡~rIÍ‚W¿'˜[¼ý…�4µÑׄ•�³&+£Ñawàœ;LĬ,йpÜ£v !Äsa±VÞ§”ÏTç§±ty‰ä3`».§%H¤r ñtSܸؕ!;M<Åpǘ*;>x‚©”韋{åáÈ DÔ\Š�¢j.àhŽV .TÎ1¬xâ�DçÂHÞEvg• —Bc21«;Ë”°_#déÐù£wHž Tj)úš©¼hχF (‚0W‚†þšà˲Åí�9IlÁig�vúÈî­ñp¬žb(5pG¥›³O§÷T}Eq´»ò˜!…&BLP) tɸÄ™“&ö³ïh²+J-Ársš=rvBr\q߇…Âb™ú=@KŠñm<ûry=ƒÑÅ |M&£‹Ù�U`ËXŠc�#£ì"[$k¬‡¼¿~ž|ü‚.£ãóñìŒÓñìâót §—ÁÕh2¼>Màêzru9ýŒØOËeHAšYPÈUî²Äæ~œKüÅcv¾ ž�⌘ÃP¤®+\ïî3¬/Q JJZ~Ä:7ëÇË«›ñŦ<^==XEœÔ(ó6ÙÖ�=x3ÀM[Ü#£0MÐ ½Où<õ¥Œ”Î?È8!Û¯#8: ŽúƒWGƒ\OGTìa«õ;ŽŸº ^ lsï…±±¹øuHË-¥šÀÒ—Èw`?Îñ‹;_ÚN‚8áèåÛ7V«•Š˜ß‰LaØÛÉc„Rús¥¬ùܳc¯�E¢]É]èr·ÓúÙÚËV+ üøfáØû÷^ÛÐ�yüOÜ«xxÊcônôêÚþÑ*G;úN”·9™XÀá8Ïæ—ãOóÉèÛôO8„×ðŽ²ÅŒª¡ïѯݮ,u;ÜíŸ`þßù-¥²wÝ!–úö×]NN ²*žñ�L²,¡·úí‘Ëà•Õ* �u‚x/bI ð¬zÕ0†nv'bvª¹ã›Çü#ì)ƒ†@꣎b¶;TóÀö}é´)’\¶ÕV§£ ùÚÚx×çç²ô§k Em|xÉÒ( hÇÇ©Œ„‡r…CïX™æ©÷ûVå7ü3,¿\Oo?½ýúéíw;Î~k¢Õ?¡u+&åv–ià`ñ‰’¦¹J�–ï´‹ø0èÐPíÂê�Bº,Þ`©AÓfrO~(ì‡ED“ÊŸ¡ô�Ñô[Í,k™7ô¡�HÙ›¥`‹ÁÐŒ®EŠ‘›Glj%ímòîU\²qÝ쳩52xèVQ¥§8>É{@CîÙ-ÐÔ‘ÎGŽà¤&„2@½V|6 ˆ ÊR%ÜØ¬›eu4s”Hæµ+e*TÇnšóûŒjBæc")+ÏýZs˜I÷O4CȾXÆ–>9Cº\ì‘쫺¡3ošCï }I§IÀÁAUiGµ�›Ë¥^Á÷iø„êïÀÿ]lFûìàî¿PfÈl;ò+�^<™°~kž!¹ðrz3~a?Çiî!fÖƒ}S J_d}TÖkÔlšê\Œ„KÝT&T/\{ –¶6Ü6š·sÚl¥¾Æ ÌÒÅíWn¾B<Ô.=Ë� Û¦á_ÄØ1ö &Ê8ÍjÒç,ˆYRš÷ðvÔƒç×|!lYy˜5 @Ý*åÝSïƒ|-ƶ¨Iö¶SUgõÄúQãÃP!fÝ7B«yJúÈXÕZ³«À±hŸŠÝÆ.9”¿*—lq#ëd;•—KdCˆ Ï•¦¼ž'ᵇ ¦´­…Tš»h“ÃL/aÒŒúºM7´ù íàÿ~ ólänΊÆá¶!©ú|«+¨8¢aþä u+o@»•Yî ¿óÄüÂß\ðç>Ž´ ^VÅËH÷>OtÓ7tºö*”I©=P«ØuÒ(b¢@¨|=š›¡ŒËÇ2mèñØ4ŸoãoB¶©³ìñdÍkܯZõ‚¶ß}›õ� ‹Êo6ã´-Ø`߈}pp[¹ïtþ›!¶’²¹”KؼMzÁÄyRŠ´ÊÔž­FYAHšÂêùµ@_ª¡U7oÝQn,..,Úüú ¡wƒ½‚G¢Ua±±�À˜_˜»1g—Ò@ìc 9!ü"-KO´"ŠäüÈxñ•Be¢ø@²Û“S¤^™LWµ�GPâÎSoÅØ˜ÝíS;;F€áÝU =ÐÊT¹Ã{�³q²¾Âà3gòÁzŸ8 ^?Eài»ã„8ÒPg.]Û°<·Þq×~ûáì õÁy5$ÂY å†Qµuåà=ðÉÙ¼~„rP�¶8}o‡»zÉ¡½E™°˜ðWáá!»î��¶ˆ>3(ÜŸ³é÷E‹ðôYÔiŽ…[ε[é"J57\òÔ‡@ ßE’ɬ ÿ‰7IüWDî9b$žï¸àNþºcoç^ ±MŽ6†àŒÊÿüp— cönù¦y7°äVç}0å{çä aVM‚ú SŪijºOÈ]dž‹vÀ½hÎ/Q ç~”ºs&bƒQòÏ%\‰M·8—êM w_‰õ𯱮˜W‰sxËÆwÛS!Ø Œ•XÙÒL‘ïD£2M1kÜþo7¬Í5�â! ж~…Ü}MÁ;«†Í ÞøN –wQEf¡&[ÔuÒžtdW=-Û- hwA(—© ¼%ü5ïÛ¸Zxœ�TÁN„0=Û¯h–ËÚýŽêÁÄxóܰeXÆ…)iËfÕøï¶@b–RäB™7¼÷f:-VTBÅ_Þ_ߟž¥>|€r®84 k)Yæ“H�̳ I5} |wD÷ tÛjº¯w3\c¹–‡2ëL¯÷�4pî´n&þÍnêÙË| ¦O¸Ï Óî³9ûÉI{[¯—6ãê%{²x$ð¦äx]Ø:_ä ’‚àâ’®ÃF%lGº‹õìkW‹_ Ûžæå(ÝSTc[\ä”X+ݦRÒ_¨Þ 7DQaS®Óvž ÍŒ•±0£pÞRs _cLƒ‹¡UÖ8“ùŸƒ‹˜ï*V$•�ÂÁþznH2lÎmÎB“�,·_0–»•sì5—’±G£õ©ïV<”&;.¢ÀÒKŸÃk¬e ´“ÔJ+«4¶­‡û+ù٠ܼ'“L…�“7ÿïדlÎÇH¸uŠ×å² ¨ÄŠý(j¿xœm‘AkÃ0 …Ïõ¯Í%5ea×å¸í0(»íl’XIÕ¥Rpœn0ößg­„.ƒô½'éáŒZöØÂËëãáíéÙ¼|Tý»;:g²ˆq•™Œ¸é'�°í(�œÏÂwÇíM¶(XUSümŽ1LM„¤ÕñƒH_f£¥ø¬¯Ks-¯×Ó�"†*J(—ln?ÿÊ]~ë²»µYA$Žó ù«ºG°RŸ°Qh6�Ô1zÐ4ÄÝÃ}¹hG%ÄJó�ô!¯™ÂàÜp¨:uæËœVßt“ºg-²žó_µ‡eÎ9`Ú“!{j¡°k¶0?¦¹Bxœ�“QoÓ0ÇŸãOq¬Ò”L¥eðX˜TJA•¶¤j@hOVŸK‰]ÅÎBÇøîœÓ5¢[„‘|wÿûçòóe¤t^6á,7Ueô¤8c£>çŠ3ñºqª´¾B%)Pÿ¾Šß½e#Š”Fèb~½œÇ|â7tðÕ£ÑûViaZò¸b#,•|êa±k~DðøØ'8™ê2'~vo§;ë2wpÔBIƦ .àã„Qz ®P–:Àµ¬Ã�…!Ï4dJ̬ƒ-:ßá ÙèÜ)CjwÖTØ$ÌM�5j7¼GíÕ­rÅÁ[(»m¬óïÒHvFÂ����pêaéŽVºàñz“,–išlRžÄ×:ò¤Úç{ªC�ú?�våÅf~ËãÅúÛ¿¹�È,�H•&JÊq£K²àù®±á½Q"b?û]úº 4žŠ×Ïz³Óå ÒÛôëò†¯âÏ ]“43|A—î鮪Åá¹ÏF3Æú¾0¤Y"Ÿ™ˆ6nª;¬¹®MŽÖšÚ¼‰XÔèšZÃßųÿ_ÂÀººÉÐòq±×Y¥r:‹ã�¯º¥ä´XOÅðœªc°ê� é�!ôwÑå˜f~>4I&ôðMËsí^âäÝêxÃ=ýˆþ‘Üh5DѰg÷u¬]ÎØ/ö´ÆU¡µŠxœµ–QoÚ@ ÇŸ›Oá ©"P({ÎVµl‹FÙÔRiÚK.Î85ÉEw—­¬êwŸï’@IßV!Ìß>ûÛÇ€ÇY„1ø«›åãí"Ð[‰aš'*Ø�3 oy†ýgÀc(EÑ𳿾̂ïë/÷‹ù­ëè]Ž&~^ù¥…Æç@Ã/®ƒ„=yΠòr]Þ| ü•¿ûWÅî׋ÖîÏ—þÏÅýÁ‡¢P�€g\C—|ÚÇ”ö X=.—î‰W"ØS—We?ÑYíÑÖïí'±Dì:!B¥¥ØòéÀ7¸ æëowþ� QÖùp2iÇí ̫ܵg�Ïä•Ù‚L"KÈ-`y¡J©WKárÔûû´ß?lÑZ·Ñxœ­VYoÛF~6Å4lI¦m9E^¢Æ€âHŽ Ç6t u‹‚X’+kcr—Ø]ZVÿ÷Î,QG’è IÍÎñÍ7Çê¬ãA¦ a`.ôÖœƒQs»dš÷`¥rˆ˜Íca¬anQÍ“ñ™Ò�ªXÌWäe¹Œ¹»à`¹N ¨¹ûqu3ƒ+.¹f Üåa""¸—†ûðĵJÂkŸ|0)˜�!\9ë!á™”x`¨0³hrŠd3’ÀâX�¬r&F†üòGÈ ©°Ü.”6𠞸K;—‰H…E(k‡"òÑYD*Í�µ˜¯Œ%ÝO}è¾>?ïžœÿÒ=÷a6éS²gž÷³�Q’Ç^a¤8æ‹W[2a7eš?-Yò¸)¤ð$ñž”ˆq¤m@’  íÖª~B‡žmï«wàäîWÏ{ñ¼‡À:32ïÔö"Þg¬¹Íµ„Cœ\¨ð Ê©ˆ�§µzíÉxÌ3ç±gJ%С§[aEìÜoF„wp3»¾îyÞ�˜CË™¿+dmï ÂR©” ÔíFÜÀMWˆ$|ãFï1œáO;1H܈A~ƒ”%‰ŠZFüÍÕ¼ŽÚnWp ÝoÂMyj¸uZ>tŸ»>ìõtFkW>Kð2lDi¡NMtP2DÙªµ]•*± qÁ"ÐkûÈ®2Êîj4 nßÿLǃAØeQà i»Íb³ÌA±]vš·I%¶Ë¾~Áü ÿŸÚ¥Õ(V³ ‡è¡ý�ê�ƒõ(°\&ð+tIÿAáƵV:ˆÎdžíäèm¿uµŽ~ÿ4xK—]4нâ_aÅU¥–rý æàlsj7Á"•x£À6Ú=Ãk,®¯¨ä2Z0í ÎðÏÌŸ!W_�ì‘Gš¼~Àы㣸”P±Û+[/ü*Ër£Ð¹í„ù|ε_}\Æä¡²v–h¥1rëpmïÃN«ž\Ä¡¿³oªq-–%)Yq2ŠVÄÆp맸ÀY¯:"X»ÇpÜ”$\Vý€¥Y«Ê­Á^mÚM4E£ËÛñxv7|h@:~oªurr®,hø ^óV%uŒÃêã¼[ï•’Óããq¹ »®tˆL­3oÃWêãªmp¯ètÜ‹  Áw—]aD‹ªT?¹�üÙ¢Q‘É7kw'ÌZýãj¶l–:Šöv«˜Ñ\ñ@ ¡˜ªv ¸­ëч`Üÿ<ùÃ9Þ —5u”lÉ‹÷E?¼xœmOÁNÃ0 =Ï_a­èa|@à€„vÛ9êw5*N•ºñï$í´ë!Nòüžý^Áµxªñeûøº{z¶‰lc- e¡ë,®<áý�õ.ã›æá‚®#Ãþ�œnš5@¯qpЉ:� Ñ8âÀj�žBY+Õh`åš*bYsKR½S².°ÇtLÒ,O+…>ÔÀwbèØQγLüû5Wî’¯icŠÐ…ÐÚ9Î×´Ÿ?É*îG%›ß'‡¿½ä‹©7ÿN]ë '§Çë,¶uª7ge®·²òÂéªØ/�  ñ\ÃË ¡\¡xœ340031QÈM,ÐKfX"½Ž»kµçÄ›WöϹ»k�M¾ÅûyÃMa³?xœ�SMOÂ@Xv%o¼uxœ¥SYoÚ@~¶Å„HaÍž ‘ªµ‘8$Ô>¥‘µø–ðÚÚ]P›ã¿wfw vJš(}Â3ß1Ãyœ¤™HàëÍ÷0\,Ão7_¦áüÓl¶üìŸg"ÚîâZQ‘ç…lZÇÚ•Ò1—÷ƒÍu³–Tò÷ECç>Óa˜óí¶ˆ˜Ê’PƒüGß³Ÿ%LÀ}ÁØ÷²Ø™ |Ï“‰ÞIÔ_j&R’‘Æéb9ŸÎ ì rì?7è¢ï�{Z¿ v8¬ÿ�;Úp鸕–ñ®dQ!”[W†Ö>­ƒ¨§úg&´‡i®™¾Þ¥G¯»4T=¨ Adƒ���‡Û ?í9ÉÀa8ÖT•æR³=¦‚xC‹š÷J”Q)3$Õt3qÏ …�‰ˆ™}![®àžž€¹M ¸ž¸æšgãrµZ®^õXJ¼Ñ_Qþ"X-›-ÌØK l<Ÿmö%Úè0 évL éìJSQ_Ve¦L² <€³ 4p úP"ö¹¡TíÒ÷(µ0£´:S{ÛD#…Xm²öÁv¹€9¼®ÅØóÃÒ€?Ð%d‘è¸ê¯+Ž3)xž¸·à ‰^•+×›Ú«£8TEgÕJðh°..@À5ŒªÛt±›îà¬S=™dQ›Œ6’Q±ía3žL`ñc6³[#åofµ-²]Ù™Üí|E]F!<°­á-çü®�áË!ÃW‡04Bšh§fÞ©8}¢¾—'ùa¦U�Ý4+·øx‡“Ú?/Û„uý&§zêk®’ZìôúñÜ©ûÍà¡Z¾“©z. {r¦³šø¾½’fTžú›7u8¯‘Öß &‹FÜ]VËnîÙù:½hêí—ìþFu©ÿضÛPwtØ6UÞX÷‰9+Õ°exœ�U]oÚ0}&¿âJ<gtë´Öi¬¥[$ ¥šö%Ë$X 6²�Võ¿ï:q ¥­V’øÞsÏý8¶i‹\f<‡d|:º:ÒÒŠ‚.) Úh’?vì<ƒétðƒ^&?‡á†@hÄ_®rüì5_¿Žþ|c¹–p­DÑBXJW¬(TZEPKúO Òûˆ‘é’i�4Vgå:L•4ÖÛ´}__’¥ã ýš`ƒÑhr´ÁwP9Ï'ÓÏÉ™÷A¦¨T––†û*i&4Om±EN¹ã¬¸„R>Xs™‰|OïÛdv¿Ç÷´émú ­Ÿ ‹çä%´õ€ö´»õ­äªm×Q�BZ¨dÈW6¬§ß¨Ã}Mbèv»$h9Jd¼Ìè·i2ž�‡Ç1¼Û+»#\k,k“®J‹E>ä­aOÄ›2I| s;æ�u—lÅ}W¸j7É™]>‘zÎ ¿ê–ÏÆ½(‚OšÛ¬.9ªkÈ…5ŽP�]rÐL.8¨XCõ7ÉdÃ…„ØÈþCÒPE¹‰@}s’¤æ¾Vl8› g°Ôáì^Cî`q(ï±exœ¥TïkÛ0ýlÿ—R;MÓfƒRK»‚7ʺ~(ÅK^®\l¥]·æß�~$žãl…}0H>½{wï�tÀx&$‡�ó¯q|3�ÎÞÄÑçøæjú%žÍ¯ü!Ó|Å8t™(‡Ë®ïW*Q"!~BÅ™È/ƒt™”Ð7›Tâ'�Ȥ…¬˜(¦ý_¾g£9—0�J•¸(6ö}Odàþè Î' CßóJ®V¥„SŠâáôá9p4ä0ÐëÑŸ[\�î 3�ÓCÊ@¨DmPÝ“.¡šû]]€¥�ýµïÇIèëUñÀ%2mMé�Ivûöôý»;¤ØÂ%rÝu//ЩËW®Èì64ÖDˆ® ʃéP:�þ>Éó" ,�ˆB§I‡6­àãs*¢‘akÄѨžB{ô²’󀲎›i½õÆqþXÆ%ò] É.EY©K‘óMÓ=ϧ;=�yômº˜ÏâOÓh¶¸ˆqs}ñg!¦¶ñ?K3 ÄŒUŒj>kcÖ4Öå*UFLÉq¼�ç%Oy¾õ’i¿�•d$û[µ»ò�Ùb(̶…áŒ{<¯¸nQ�`ÿaô""T‹z(vÚ4É�‘xl˜0JîyH÷É F°¿òyÈbI¡–"kG„,liÖß| ¦šº=wVküXVÔ'l¬MÖ–kd% 7ªhÁ;û÷HµyQÉôD“Z!ã]4Í«î§@S›#Ù3åÌÎø+›¨×4"Øš,FééÕ²¥Ôâ¾ÙüO½,N¿nž½]¬qÛXh.×ö…Æê~”pÛ)¾&xœ]�QOÂ0…Ÿ×_Qá¥3HP^&&à X²lf‚ħfÙZº0:ÒvFùï¶Ýáíž{Ï=÷Ë–„VœÀe´Æx%�8Iñ* B¼ˆâ0}}Êu[8 UMš£³Á¹ùD„à͘=Pqw•¸åuÅ÷¨h¸T°`¹€·Ç\1|§`‡¦DFŽàd:�ºpQ­à°_³VœþöR‘ÃuY¦¬sEú<Ó2dp{kû3èìŠBÔMg0Ùı œß{w÷çÛÍ‘pëÁgÁ6ƒßºxÉBm«y”øÙ‡AžL®à¨üä2’–iå'ABÊ4ê„‹wDáFR–ó²&H;=ÐÃ1ƒ%ï~¸³c-6¡ u˜cÿ«“¹,¼+úSr³¨[ÉšÞRJ„Ô±n·lÓ,€ESš-‰Šs©B!�,‚]ï¦úF–¥¾d1;CØ™¡ µ$—“ÔÆ]ÓõzböoJ·®¸Œxœ•UÛnÚ@}Æ_1¤²‘sQûR‰Bå‚!–D’æÉrã5X26²—Di’ïÌ®¯‰AEBÆž=3{öÌe•/Aôî=g[ww±9S Ö$Q|±(Š’r—�0ºŸÛ#X3îìÜ5sÒ’€mà/SŸâÀÓ”W¥UÃë=¥µxX,ÍÇš�ç�¾¤=Ei>¨í£º·&Œ/^RζVäÇj‘:· ôÉûÂ{6Â0~Ľâh’¸Ñ>t“€¿ ô'Œï“¨º=ZƒˆÃ:àŽ³Å3ªô†ÿÐ�÷\9Bé@¸]sùæ‡î:Í^=bßG>SÆÅy¯�Ùhj‚¿AbªüÐÒ&Ný�y!S}�ŽðA„×,1>òrˆb® ëSÀž?[‰O?²mÈl’±Ü‡» ¯›äI(n𲄫*êí>ÌVÓ©�¨( àJÓò” H?‡¼½I#r(J·7­Ù�1¥ôe ™XKÇ´í¹�å �<—» £õ2“ˆ'ÙÓ§¿ÝI�äîƒo�)»ƒ+óƒ_Æh|„ÅšvÄÒ­=_:÷¶µ45ô)óðÖ‡[cb:¶iŒÄ2º³0eÐäO ÃîóÙô!÷>I«£TËâÀ½ÆŠqcÜ:9Õc,›=iµÐš: s¥µ±õÛ�˜è²ü¨MdÑÁ%Tz¿[•ÚD‡ìå¼VÅÙ±*À6U ¼\vs—í>åð‡áȃ]ÈÐ.‚ˆ†‡îåÉÅ*Kq˜0—³q²w· ¢5¤. T/»W‡+ñm’e¡]Á¶)+e ã”]Ë¡š‡·Ãÿ*R�.øì•Wá«áÐ\,>\û¨v]àCÌýLZ²åÓ²¸Ò¦‰‚\Çåê9…ËŠv©¨Wb$¨A�šŸËÑ*¼+‚´rHÕÖ ÉÔà½Êžv*É79Ìûd"5ìûá¤)ÿ.Ó1ªQxœ340031QÐKÏ,ÉLÏË/JeÈdSÙûgî½Z‘„9Ëgl“ÞòåACˆ*ßÄìÔ´ÌœTUÕu]vì^>©©ó­Õ4Œ¿÷Û ¨?G_O?w†×+EŠ4œ xˆh¼7aýî}:\P%†º©EEyùzÉ ŒeÝ Ûvœ—�3ùþEìÁU6Ïš'�饦%ç•U¾_:»(+׬zíÑÃs›_d‰¥$æ.BRi¬[\RTZ’™TÚ<óØ‹%^¼fñpšu5üÕóZÝŠÕ¥Fº)™E©`CõÒËN=;.[ýzž€–—„Ó|Ÿmp•†@wf$gÕåÍý.8wò®Éæ2†E­ÝžKøÃN¢¨ËÏL*³O\μÁ™!éâ„þ�Ÿ5�îžúI™‘n~RVIeA*Pé}ûbùtNÖøêõÑçì-;d}’Ö )5)…ZÞ,,ÿäpõµ‚ô”wÉÅÊ¢²_w•�³"³¸¤8'?¿d2Oý­>Çoʉ-�1…çÝ�Ý}2 I9(HS`Šóæï¼uCÂç^[´4›bò… N?z‘£(.ßu#ì›A¾Œ�ó9~?3“r…X›ÝpÅÆ@‡”e–€=çÂÃýfÇÎ]Û¢­m&H9Ìñ\{ ®Ð¨° ±l¢M–ç×Rñ é|q²5)V¿k�馤–$fæ•>ººDð¼ßœµ ÿí7‹Ѫ+ì[ˆ¤ÔX7@…oÝíT �þ$òàÝô8SÓm’i•&ÀbTg Šª¤¬Ôä’b-¶Ô)së§í,ÏÎ7ûÆé`*í~ö Ü@�ÂòÄœl yƒÍ~ý9ÿõó‚h_QuUÕÅ)ï‘Ôé–$&å€<ó­Ÿ[zrŽee™P5‡OžÁ�šû'�Cf‚Ò]ß<9ÿ©OüÎTæšÈœ~fß¿„h˜Í àn\S«*íiˤûØ1©ƒ×ÀRk}ÇY¸‘f@7‚¢hÞÍ æB_ĉ=SÛ(½<”Ó®h’:#xÌÜ=øLÕBÖÿ4ƒ@}˜3/ï¾;ù�sQ~È�ì¦?›îFIXÕ¯dÜ `·dCᬯaêR‹Kâ3Rs R‹@Áí¶h¶Ãý°ƒñgĖΚºßp‚ÇŸûØTf0èpJÈ[(»™<©fœhdS_2µÇYeNfÐ<™Ç?µ?ßȵäx®üU�))r_mö «Ê`¨ø9ÉXáÆ±ÿ™Œñ¿zX¾EÎ2™‰¬*713h˜óng½Åw¾ÍöZ¡Qv¸ßíâë"$eÅzÅ¥ ÿæN‹=àp¬ýíÁ5ï_GUJnôIv©Šãì †sxœ›ÅõŽsBŽŠ‰(¥ç—%§3œ¹~ænVâOÇÝÒÃí¼Mg_ÍË_¶1ÇžIä²²¼óÌ×]í¿'ˆÏ¸ºËkÓ}ßÉû™4' 0‹ˆ<Ûf4sÓéÞ+ŠøË�b¶ö”—\Y5Ù‚9$ÐÐÀÀÌÄD¡ÄÀÌÀP·(51E/™áÀEqWQ.ë{ù¦¬˜™u2(Ák ’:#Ýò¢Ì’T Â£O�ßtäüº)owæÿs;äY þ>3ÞÌÊ"Ê 'M6xœÓÒ+ÉOæÕ©´¥xœ­VmoÚHþÌþŠQë48‰�6ùt4TC'‚# I{—“å¬×°Âx©mòRU÷Ûofmó"Ý[¤ØË¼>óìÌó£¨Vc¬×‚:UÇ1[ÄþLxC¨‘$�ˆ(‚t‡}­+…“/Ç�x<ލûùŸ(ˆÕ>:Ë0!\ oVyþ£’dW2îÜ�༌Ÿd \Å¡/?“*†Tpz§¬²–ýNÆ�zJ™ˆ2æ\ô�!â´Æ`¹'`Ý!xÖë6‡$íÙ6XÑXf¨Š~0—ĤXÄò™±Û¥÷w°,‘$*±Ä³Ì¸ DýŠ"áO->|Z) ÒÅ|žˆ4%\uÛ>ÎDš¥6IËàÛõÈõ09EŒxò2Ïcí¯£AÃÓ�êDªçÞŒê–d"À‡tîsÁg·œ½‡ëÈÏB•ÌP"¸ %‡ì q¥D¬øU£Zðc올â8‚G`Å*RcÅ*ú6#OW¢y#™;k0r°ìß$X­»nÿIky­vó¦C,�Â8Y�²äŸ.°ÎJ^"Øöý}$ˆsÛÇŒ?l|ÀSªøôô„Îè·NJ„ÿR¨Š{•aL¥ÕKJ³DÆc¸êö;wG«rM¬ÓÖ±sNOXe�çCâ¿Hˆ§y6I„”±-óhDzœwœ=ó§Œuº£*–0vÙ õ$<É(à~�>Møñ�=1ÙõM³×u¼Ëv£Õ~Ãå¶:Ò!™lzጌÚÑç6Õæ~–.Ò öl~´g«£{«¿µÙï­_þØzX6GjtÄö×övD…ų@Ëœ¥ñিmL†{v²ˆKs Xšß6z»ÍýhÓ¼ØZ⸷íA£Ó~uaîGšT®×z€¼ú®ç^·ûÃaoÛwÓ²�ÌÒ«Ù"±Ekã°ºù-0Öèõ¼eË£¯>šxZw1µYÑÊF•d²JŒÕèýE…HŠ8nŠ§Ô áÀVø�dà3SŸóà!W&!d‰ŸN,­_:üÉ­’­¨Hä;?XŠ‘rÛÜV˜K¯fÔ¬ê2i#gziÓ`šÆ9v–‡m·lž:PÛÛžäG\æs‘¤ö„åí¿ÃRmZ*´¼jtûž³a9óel#ψUßH-oV�ŸpR÷›l­íj¯µlG;!ƒíT-£g•q"æÐlw™Pù_ŒóCV™=¾�(ØÈ‰P¸þM_CÊKUÛ¨ qm­ÖîYRûo¼5/»ëÚ·ZÚgä:õwÆÙ»}¸g• U»Ê¨E[¥/Õr9D=p»•ÅÝ­Fî—p g¯E8óïLXx³ìsó ‹ ež¿¨¿Ùµ±"¸Ð L¬ržÕó‰4ªg&|ø@a`6 d†‘•B•¥wŠqf~Öú-C1ܨ/¤"JJ‹0Ÿ�~XÀ§\Ê%$i‚³þß •Ë ¿-.ý@i__ºýo5 ŸUåY/»å'šý�µU*ÖwÖ_ÅiÑcè‡jxœ8ÇÿÔÇ�Á -Wextra±Á74“ù\“af³Ñ¹def OPENSSL_SHA1³™»]+H¾)xœeRAnÜ0 ¼ë|@bl’ž¶‡ rï!÷€–h[�,¦"ÿ/é]o ”Ã9Ù±ßH„—I@&^J‚ž âLé€þ²º–5síbëD�À¯! ††”Ǭ9Qð6 ¹³®|i Ä‚"yØ‚ZW}m®Ê†ŸñƒDT¹m?AwtÅò/:×”}Ä»AèÏB5ð7¶5딫�Qod‡–«zøÄ¦ú–à>Ûcþ"“Óx§cŸƒlÉê¤æ/1IÂË…>“„û[…p:½¿Ÿá…-�’û†ms2ÍÕ¢íQ(ײÙc-Š5aKÇì�…íe·h<$BU³e1XFôEms—#P³ÀЖj&_»±»Â©µÊ;ÉŒ¥p4¹§×õŒ’#L(;þˆDî %Ófd“Áʱ}ɪ¶ùДR3)Ø×m�õñ›•ûŠ �0Fû•<¥ÓÓkËÇèÍÞü�M\�Ë-ŒÉß#ϳG�Šá/žâi®xœ340031QHÏ,"½Ì¼”Ô ÅØež�5Tþfþòs¿dΩ¡' !ê Õï<™ÿÚÙ~S—ºðŠä妹®ï®›�BqbnANªn~Jƒ[ꔹõÓv–gç›}ãt0•v?ûiº,I¿èAxœŒ� <”]ÀŸ±oÙ—�])Œ=iG •„¢c0˜¥YlY¢…‰H"•¤hOi_©¨PI)KQ) IïŒmÌ}çyÆû½¿ŸêýúŸ{Ï=çÜs·ó,·wZAñ±K©·güÿ A!TFÅ;Š9¡Öý€ ™xÆšfoݬñÂŽ¸QhéeA9ßÓÄkHÜÀOÃÐh¼7�†£Næ¼vžÐPžLÇ÷ó˵fuݺöÍÅø¨Ýl<$Âäáýˆ$ û†÷ž�'üjžYpùí¥[÷®FÙª4f?ÚG/; 0ø †<ò÷±c,‹óxÐÏŠôuç*Žl =\žÝ}zÍÂoÛßÊJ38d )‡¥Á¶ç;ÇKê�Xá“¡ºì�·kJÅç…×=5Ãò!ÁekÖn´w´…ëW&´Gêm‰‘L¤ó·¥%_Q�Ô.�¯«ÎŸ¿ÍXNÂÒ 8"C÷xÑðÿ£ËOøî Ý�|ž7V=ÅâžÌÛçZ_³ïè4húÿùã£ÃÝÃΕ¢÷;óóÊ��q×¼¬>/ջݧ©²s—‘|ðD?[:Þ„'"ØO&´—�¯ã=+gþýîwým'DÿéšžŸ_ɳó0�8_|�›ÈÎåkÎHuI{²ë»ƒfåË}×^Ì“�„4Ù¹N¸ GCÓhcƒ¹FÆ´P¢½fBI€½ò•`>I|wß"£¡|Aîƒå!Ç„¤V}.rL¦ '™]ŽÀœšµé·^-™RâñwØ42Z/î:9¦S�³�]êÉð·‚wõ_?õ¦ýV½Ý,E°Zs‡‹³)ÈÙÏ.‡óµk1õ©’øQÌ;ßÀâ=vÇ{µ–íá"gîä¤ýùx[]®Øóµâúb¥Ä_%ݯ¾Eðý9ÃEŽùää|ð©Ó‹óKLÝ-|1bÞËÇg®o¹9‡‹œyS�“Ê.ÇîISoyÉ«ëÚÆ’ÃâÔÓÆÁØå‰©�:¢œ)0ü4 žÓRl¥eÊ»’ª9ïò+­äý…Ÿ]ºˆý0ž’ßì™Ú°ÓÅ(híâ¯5.gþ‰(>s¾Ý”‹œ©øM: Gu·�ÐåMâBV.áúY‡öµé_ Œá"g*~sˆ]ïªw†¾Õ—žŸÈµx64`DŒzuºÅs>9Sñ› v9üé%EɶV—¬_ªŠ}еjkÓÕ›f\äLÅo29ß6ü<#Óz×úÌýëÊ»M(¯þ9ö;p‘3¿9Ì.Ç´£©ÛœB>˜f»ïa±µÛ¡¢Öe‹#Ú³1¢=gãoô凸æIë�¿…6­Õù_6ÇóŠßSÄ~˜LÉž³¿™Ù7éž´^çÞß—ªÝ/ÎŽ¹ì«_ü�‹œ©ØóÀÎf¿Ö<èîè¸~ùïžù‚a‰— mnr¦bÏ9€Mzm/½ÏÙ‡ç�Ù®ûKD‰t׳ު\äLÅž�rr;fÏ ä^;Ô)V¦Xßj}¿Wø¶`.r¦bϹìrf”anÝühϬ'‡ï;•ûÄioø&�hg&ˆvv ˆÿ+¢­x´ìÂ:†�ú1½qÉò¯;ûa:%;Ëc—#¸wUyëã‹o·c6}þ”{òh|Qè*-.r¦bgùÀ¸l.³¿Neñ©åTç¨ÛÌá 69S±³ã@žvµdÕ›ïì~§Jò™L÷?ÛË;V<ç"g*vVÀ.gšQtÏ‹§Ï !¬¦PoÕå­ ]·Ü:�s‘3;;ÄÍ �Ãzú�?öæ0Æ(D+ïÌ^:9S‰›'Á¸fˆ�¯0‰NÜe–17¹å­Â×Ñ\äL%ß8Ø�„øÒ_{Vü¬ÖˈÎ6‘ÿ¸sU‘–B9S�SÈ.ÇüÉS‹´k³l~¼�qѧ®H‘¿¡Ê»Ñ?Mýó4�oŠÕ\µWø•B�Ld,as+¹¿f‡7b?̦äŸEÀøj<}·ñOÉžR»ôjâÏbœ.=ÆEÎTüó 0.ŸJ†üþè6ψ2N2Êùøbó¼tK�Ÿ\äLÅ?‹Ùåˆ,H$ðÞàMò–ìtˆîüsëï�� ;¸È™ŠžæÏÛ<±f2‘›$ÍR±:­Çj/$–Ýå"g*þyŸÜ%‹–lÈW{Õ÷BU©àØ º‰�Ùs\äLÅ?KØåX¸õ˜,ÄЈŸ^ý(|>d1÷n ½ÝÇ ÑžÍí¹èÇëÈ�Šä}!ÿúúTiY)Û<ŸTuæ@ìÇÜ)ÙóyÀÎøæ+mãùü�*‘­ãðW×à��r<›;%{¾ÌŸÆ? 3®˜õk êÃòl¯§½xÃMÎTìù"°O’ˆæ ëzâ­[”º%yfÊJ÷g&ƒ‘ýfî”ìùП;Fƒær�"1¹ÉåɉØÞ�ó¹È™Š=_f—£rfމ†v¼rŸÇ’¶_/ef* ÷&!çÏsíì 0þŠv>¥>6?Ow~Ò&ôûŽç&¢°ib?̧dgW{.ÝÈsjS(FüÞ¾Õ=^Õu·¥ý. ÇMó)ÙÙ5 ÎX¦Í’šsdcQ}yƒúû™»¼Â��çó)ÙY0?ÏlHß§ZÕh´ÂÂm·xå[C•Ÿ^9S±³ëìr$xß/kx©üþºážU›=uDM{û3êÇßqüo°óµ®Ì´®]ÿü£¨À¹ÎéC=5s›¥ 9|s.û åìüå!;•?7¾žž;G¾#v†r£7LÍRc篧{ð4žè·CÃú�l0Âðoã°Á²~aè×#A:7úŒïôª9?m}wRfçc¨X<Þ‡„5À’ˆ¾Èú¿Ì�[œgK©Põ/$]Ÿùî”×—§–ñ;Áö{a84‰Ìü ul�aø·üîIþWQfÍßb¤;+|󉛿ø~¶å”Ü?MøŸŽòA3:àƒ#�q” xþ ÿÖ0WÒ0ß6\Öd­ý’Alûû×!%v>D¢Ó¨¡Ô .öy—½ý<”×wLk©)¾]K–µ�ò¿Z|2¹h ¨,††¦áBñãmGàßcçó—ñ  mPùfÝù~Îù_çK6(‰Kü�OðAá©´)ðï³ëçš<æ^0„„ÞAÉ[$e½/ÖqYÑ+À}u¦Ùàýíþ;Wú©¥Øõ…3¤ESuvRü’Û˯†ÓÜ!v®Þ×íK¢0´‰ÐÃÈΗŸ¿.Jµl‘Ëþ¨¥õ¡o¾Lw\?Ô}šÉ�ï‡#â(Mfú79�€uõΪB‰çʧÇ/›ÝðfáÔ�9x>0"‡ÝüáøÀùƒáÕÐ qÓ£Wû”•ïËz¾ê[JëÿôDÂz“H�h†y`©T¤öWùÀÀa¼ôC«î_ê;®f’Þ´æÞ4t(³�9òĞûXÏ´~ʨi²©=¸@’à·+ØÝTþCµ<èW¸`%Ì6…¸_ì�êdÂ<_ßWo�¿é�õÙ›±ƒå¹óêÁ¸à‹cŒèÔâN5»_¡æþ‹ íSÏ÷¼dûIÌñI×?èò²  _ùâC§ê·5ìí×ü¬GÚå¼M¿kÞ4³R‡æN>–v@½ûáihŒ�ÏHËa¸Oع2Í÷=ÖwióQÖ§¹,8ZfùsF�lñyÐ_G¸D­Ö�ÐÞ@—‚åÂoݼl��¨é‘IÞ[Ò¾ ´ó.‘HbüÇÅΟ²óUƒ‡/©I¯¬8%ж٪Ogµ[³ÏóL5Pß#|29h²ÁÀðŸ±ó%•�híâ�øÚVïéß1-ãÕû6ƒóøŸ‚õÇÈ$ Q?ÏÙùb=VU·c2(ùš&²®Tmß2t7ôÓ ~0Ž[œ©ìÅâQ†êPãUïÝ×?ysnzxV}bÈðü”É÷ÆSqXV¸„ã׳ó�þɧ½t¢cîù/VîL*s5r­æ¤ÿ‘¹œ»þ_qRÃûÒ°õMÞ¢ÐRBùu÷<ã²~sl?Cœ†áø/Ùùr ʾE´E>�Û�/àIr}lˆão µáȧ}‚p\ù¯Øù¢u[öð Ï.9xÉrÓ‡–’œína9Ù?s.gžÕr±ÿÀ¿ªõbÕtUȃ’‰Âd<_ðì\Ÿ»¦‘“}2Ò?l šyJŽhŸ�@ø˜×Z«zÓ“˜·¢Ø¦å¯Ž\êX b¯ Í‚ãSpàÄ #§ ‡^ï;󫹯"t‰f.¤þzâÂ!m9Œ¤ �'úàB¹ŽÇkv9J¹›Nd‹KŸ “^þΖ" åSûÜ ~q�19\úñÈk%wD—ì ÛzêšÝÛHÞ¬¢•‹ œ?‚48ò)”04� DŠÓo�ù˘ölµctE2ÙÀ6æ¼ÁÕn�;1'{åsÕO3`OgýÄJô‚nÏýø§VIR¾/0<®Æ‘£¿añ4)ˆ+¸GÃY­\�hV%ûD{ÁÊï¢�sâ39Å lCä/ZØùÓ¯Ïú«,¥�±B9em�•ŸÆéª39óIÄ)Ä#àž�ˆ÷p—›¥¨ÏC�}é?ží°”·ñHˆÍá8¾$cí…¦Qp8¤ñîÍèbÄg^ߥÃ÷õcžá'³l¾ØÅ²Ÿ³Ë8ê„ÏUÿ­ìüYª³ßï’L;æŸýšÝWú*Þ[âïÎ|VnŽÀÿì{L£ŸjšQ(pöcu@¤ØÒž ï«-C†�'>�HC“¼™·Š¨ÜÇá0o–¢O¹í[ó²ûªua¬UŒ¼ûR¾%ÛÀ<}DN0Êœ–…ÁÈiÖЕSwDw K&ÎØ3M¶s'ìîÅú`¾8&gòôÇogç¼[W9MGÈ1²½å÷½m–=F}é<ËaøT…‘ø"ó;€ñ>½êø©Y v©[‚µxNs’,Fso Ž@"rïN`=¶3úmfaâçÍœuÞž\ÑqK8B‰S<õÁQ±¼7·ùí3/vz$ª<•%×iC² ˆ‘­zºï§ùmtÍǘ@©ˆóÛ ý·f˜¶[£±´'ü|RÒÓןÓì4laù“æ~;_6%qßÜ+'½ëÑ‹lþuü:ãz¾ÂeNã;Ÿˆ°ün oy°¤lC¤ˆ¶î´¸õ�.Ùç¿ì2U×Kãü)¬—¾ç�¯Ñ>H¹£z¢E*„/§”ýèýX§�çï|1TzÔÓ�âÝ7v~±ðÝÞ§žŸmxƒŠ6¦|xñ¯÷æóûkaù“< †ÿÈ¿\l—'å|½µnØmÎÖÈo¶Î),�â4¾£k>2fl:†ã÷ö)Y'¿Ú#þu`ÏñéAó“ïϼ¡¾ŠÓ|3Âçç~°ó�ä¶6]j»}Áð@¤vóvq…+JWWqЧ Ó§á(ÿKƒáäüúÑ4C;ŒàûâìÙ®ÚgN³ß–)®ÿÄQ�†&à(~84�êÇ]N/`¯å”È�mTg…²S†íEÿÜñ6}/Ÿô‚û»#rH4ÃFJ9»ý¬²QÓr–i÷*)ì×zg‡áßq‰R #‡‘­›8ðrú€ó £ôM±awJ#ûþô®¹Q\>ÓÒþä Žr¨ŒôøÿÓœœß@œ:ùâ¤E1`‹ß ¢·•ÊþyQ÷.pòs¦œ)ðû�qÑ%ïúzr¥E‰¹Œæµ¹ÞYõÃ[l9­ÿý°ˆþ1äq;,ù4Ó”t·üyB¦ò¡ÏÃ5odÏC:¸8F²„¡ŒçMxä88ìç‡:È,R®¾_WÑ„»ññ½Ç¹º´ÓœôãGÁ‘§ Ÿ?@¾¡-Ž“°ßVïÒfjº2¥üÀþõ¹É%uœö]üèx¤ø4ì#Gž9�¬˜L߸‰Tã¸ãêásþÛêÕ9Å? ÕÌ~�ø�ü¥¯WÖméýä¸�¡ž·<ÓŒ¯ÇÕá¤\ÐTô2 ´ÿNû£t9Ï·"/—7×n¾ÿðÕö¼g>œâ«?�Fžaøÿ½Ëžý¹£¥ÔºÜkaˆ·X1ELM¾…Óü9Â'Ó©þˆvƒ‚€ö;_hh2ýêCL‹•µ¼»û§Øª«® œøx†Œ¦âˆÈv‰Bí¿¿Êƒ¶HÕSj¶ªÞ‘ßk“\;w'zÎI?#©×ùÅÄŸ¥jOÿÍ’<6ϽvÚ·yfç›ßŸÌ ç´„'2“o.ù/Šð«Ý[iÙÂu½�?4ŠOOÝøÜ›G»�“ý0ùÜíÅÜ_ªÙ¤Q°ÆÛc™µ|š[‚tÔÝuO�rÊñD* ‚óFÎQüÀzíÕ—3Ýϣ͇±ý¿ÌÛÓÂÖåä·A$?¿B ëpK/½¤Í¿}²¯&ûjJ&¢ íìM9�k‰Êœdèã†ÃüvÑѸ—¢ž—_žUvH4À��™qSÃF”“^‚¨“³^X¾pž[XÔ~ëèÀ—Ë_+j§&µl�#§ÎÉî| ce@CÎKQÂ@ÞU¾šâÝSû8)çÄMl’ViRVÙ< NvÉàOÊzaù"à}>ã®'7}]!ÇKµ·¶ÎÖ0ý¶”“~˜`ðD_ýˆùÐ*�JNjϹþŠÈœ¿ãåFçùÞE]œôÃäSÉAxäuJ ˜O+EUõÿL�)yt¾iÝâÁYŸŸÜñèãd?£¹–7†ŠC´Ÿi€_uó8Þ–ËÏo©ó �m‰m×5¿ï:[žÏÚy„á‹~…ÿ'”è1ÇFçí¡Ö(í§‡�X&ËiÞå³N0| `ŠòfOzq‰˜¨Ÿ£tËÖ㤄[n‹9íŽòIDÛî)l?$�qy]rg—ÿ?‰ËbÆwôxîF¸htw%¼žX¦ ×Öéè«ñ q¦Èûè|{+IØé`ÝÈi}0ÂçšO£¤�q6vUÕ9àµx Þÿ¡N“Pã …jŽvÊäOÚ²ƒáËúy¶·Zjùâ'TÍ3î'Åϯð ž%DZý�4 ÷õJgì9ÅïÄWå]�ŠS *óä¯Ê÷Þå´?ÁàOp|9À�iÙ.^:î&¾˜[‘•òëB®žÐ“à”ׂÇUˆ?‘Ä�"™õëOÿ–¹lqRµ5²”ô¿÷]L.‘y…�‚ æ€s­˜Ä¹«êɲ±›Û¥—½2v»\²½ëÂZNë fÆ0õuj: uÏ´¹Ÿ²ì‡ï´Fàzn½±¸9�ïþ'N~6"‡‚óaLd"×}G”" ¯Ús{�J^9è°¯@ÃÇwÉbùÈUœìtLŽ/ò¹+J è‡[Í2� Ú‹ƒ|Þ©©™"s�Ï~ÝÄY_*Ž}JCê‡2�/ÚØ†Ä½7›þþ�”Ë/ H®iåµWEœÆ}dAÉZÆÀñgüG±±zÄ„ð¨oü•±/ÚÒäÌ—rŠ«d.pr7`ø*_ðÒ� ¯2³4§;»6ÎÔü“ªÅÓ\ù¿÷}#| �ˆ œÏô¤ Œw¸sOÓ…¤Î•�Nôyâé×T<9Å�9ÜㆰÏÿŽê½äuᇷ´ •2”«æl�Å&qÊKÉô  )´_��¯<È/¶¸éÕðÝêk?K~g´>¶Б3Ÿ:…}”p®¦UÏùIÚR²³rZõ¡ŽËŽ^R¯»58�óV:>ˆÆuß ¥ Ü£øEZ•…ŠÞ¶v¥^Îþôù'”UÓE¾sò7 ãÃ}ߥìKum¼S¢qâ¾ä{áÈ*[óM]¾ 8Åm Ž•» èGÈ­ ÚïÍÑùAþ„1GÛú*Îß5ÈÉN)8,Œ›´rB”3Øßk<`þÞÁÉ¢ÒÜÈrN¹Ý·‡gŽ<ÕæÜßñ%0êÏü·ïHUËk'¿õ=Ö9)x~½ìÎ|FšÈ•¯Ãμž#þˆ”úoÆ›�§k~x¸\‘¬Ä™Ïñ`ø³�ûCg¼Ò¶=¼_5´ç,¹Ð0;<\oÍ™Ï>0ü9ÀúUúSù¥õK0þ¡´bó:~³ógrÜ^UqòŸ4ùP †¯ äWM=ßžÕ#½Š›¹'‘§`£‡“‹g;ÚJÇQ§/ô€ýÆ ?5.ÊjènðT•(ù'ÖÖ{gû_Îzbþ�«žô�sëeŸï&^­í®à}eò;ݨ‚�Ä)žRpT�{?Cc‡¾®ÇË“ÓO¾™w**ÐýöyNzáOra¾)°�à?½g•{G–#�å¯,cS¡Ÿ·� 8æÁT†�ÌÅnÌ€øiöÏZb_¢Ó—� ÁÇÌR¼ ¦¬}óƒ9Ù=ÕÇæ¸0ü¹À<ü(¢Ö¸ú>êïÜÜnœï¶Ôvgsí,ÎígÌÀ“f¾9;_S÷Ãm×ÑÍË•?Ë_$l7úâÓuå]&§øÆà‡L>½�áÏâÛºÖÙ›Îü5»‹¯)YP¨½(5[»ù7Gûaò¹ž_¢,Àyøð²„Œ¹~ØÅ!KfÏ_ B9ƒs: £Ÿ�Ég50üù€ßΗ·®mÐ pwµ–ðkrµ<ÞÇ)Ïbò§—ë(Çȵ¯VWÚ|'|‹÷À·þy퇆¡r?7C-ìÿåÎ> 3AûlËS-�‹êÅ_t^µäw|�Ê5î,Æ×….tû#Zÿ…ÈÍ¡íù§|­”·­{Àq|i<™Êp`äý‰ÅÀ¼b1÷Û×?F.gðµ<þGîYÄZv~«äèÞ’=ˆK\^ÂÎw3ÿa$½)¦¬è¨Ì­�­Î ±C!§:t8í¿Rƒ‰Hv¿°º†D3ï1ýW®ÁGc莡‡u7š}ÿ_ý�nÁ›„ÇNíeÉ.G­†/à‚³^‘¥Ý°`ʼn­IØ�šœy)œÚ?¾ûõömê 7$=û\¾µÃ‚ÇOã•àƒž®{xN~Å<›¼ó÷æ«ÀBÞªÄ+꯿§•L»}ÑH–’1ÄÉî™[BÜïß –v鑹ùÚpq_Rßt»M£àþc6ÒÉË9Å5:qdE?±9Ã_¬ç!óg¼Û]$•l›¶êùÍyµ…·Gç §}‰1þÿv@`䨰Ë10U9s’繟_xÛç9{ù”æÅÉŽèdæeàÖ!¬œ@~–>ÿ7ÿ¹sE¤Z±çJÏñæ:EÖ}N~<&gÂZaø¶Àº^dîómbÑâ«ÂU„ÖÉ »µ«•´Ý€fÃòGï¡Y;ã0rì€qï¹þ Cß-´JÌ÷o²èQïEgÖÞÔçå8.ä ce ^[†“cÌûÅW?y²Rñø+!³�+öéÜ¥ÞiTûÌѾFåLd.0ü•€}I˜Û¾ Ú—M;ÿûØ2©Ô]e §zsòë` ‰» hw]FÞäÙ Ô‹¨+д–®E ÍcD·sj7Cûxß0®í^ è_²lGdï ßWgÕg§ýÜèz—ªùŽ“�ñ'6eaø@Ü6?Wœä8ÍïTã[Óº:û¼8µ?ç�fä+¤*¢_;qõø]ù~Ëy¾õ6�„„7›Ä ú9òý™h¢éj àÄYuqâT…»èZáÌ–û'Îu|6à够 ž6…ýüµìüÕ'—žÛiÿú†¸k\ÙlcAõ�ñ’ný�ÜÿøÜγֱs­–¿º(eý¼bÙ g¯ï‹¨…K,í=Áaf•9Cž‡�€{ú®s>8r^ÓÕ,@¯[¾êÅcœ1~W0‡õ 6�¨�õìÜß~ö Y"Ðà³®&µwüóªo´G{ÞOã?±$ CÛt‰‚ÇLe}íÌ.G…vúä7jøjE1¯Ö¨ÙöO¬–_z{’“œ`*š€÷£ŒüÁäl`—£ÿWïõ½ú¾Â%•Cbž:š­Ò4ç–ÌëtÌ>qg`¿ÃÇÅU±Û3Nþ=eN—Ôõ®-2În¼‡8ðý‚HT*fÒn¾+;Îk¾ÝèöÚä{¨º<‰¨náÂÏ%[9äÓþ$RàTΠ܀ó,ç�&J±¥ï­h„´ûómôœ®Ú§sÈ+FËåqÏ+6çÆGZ j]—;ô8`е†9bK3ESÀºv ~ —|hïÏõ?Óÿ é)zZ-zx“bòtSqbÜÕÐ&Èqb3;ßYÂî™ÍÊýµië?µV¢.¼¾‡/اÆÁ>ÿçÊ0ü-ì|‡;Y+רtB…AÍçecüC~f?–û_]”qãgî÷ÑpÄ©Ü+ñ„ØßÏ1;môàÚ¿‡ ï×e„Gèv,ìÇ›±¤¤‘ÆR:ª?"ÃÞÂ2×xµât^¿õ+?Ê«µ„·Š€yÐß�€Á3þODûq¿¯�òÎÙ—o°j¶É5Ôû-±pY‰ÖÒUàý¹Q9£;íh sŸÈ‡Û},pžrá¹lý­#âë³íÍs¨‘>}ýì±ÈAŽ/…D`莆£f6Å{§(Àß?Ø=¤û×YíEû ýôî½Bäé²¹ÁÈy¯="x,�e†1Äù Üs,1Y¸nÉÓ–'{ié”_ ùtÁü¢‡ ¹œåaIÌ™æ)t2 çƒö"yO¾ƒ‡ÐO_` µOÛcÜ^²%yˆü°€d‹ÕØìzÒç,—¹C9¦Ñ©žŸøë�»„%W»¡÷!9CEPu‰ÿ»æ@ÆœäQqd s¾$ú1ƒdÆJyTòX„‘çìcâ,Mh»„VÞžMüGþ+)Š9Üo" ™p–G£“Ñ£{Ž#‹�-3ïïMÁÏñÀþZ>2%>Nü°ÿZ›&÷ºÐå%sBéhH�“ÜI&ƒÆ…bä )ÌOÀ~�†£!½ ã‡]t¨K1æùe\ª5Š Æù1y 'ùÿË 9�€_\^_ Š7<„©PZ-ŠQ{ñîâ~˜~Q™C7z?…J÷f¿&'/¼ögZdAà~M…þžÞúqÓ �ß߬ç]¼±·~€8\?·×r7ñYl³qa©hlzé±îGÀx?r·/(ˆY‰�yÞÉX�GcX9D@Î�ÆÜS_ÕÖ¸œz”¹,̾,ø•Æç68/ŽËa¬]¢F…ÀðIÀºë—%.?/Üâ&UFïèÚ¬sàÒ·ßÀý†HÆ0FÁØ`žÉxÍ>؇Vµ{B‹èmzqà¥5ÕÍå‹1õe­Ñÿø#cÌþ2 †¿Xw­‡Â„O¶Sg~ r;½ µ_`;êgì.ÕÞ½£(@û—I÷»Èê²ÜJP¬u&ZÄÍÇyÌFi̲ ~xÜTòO*pžqüŽŠ½ëV‡ó<¹Òê9Ú󮵬ÍÛÓ®ïÈ�6vµñ=Ьß{¾�VßQõÕLPÇô›$ñ½kÝ·û ŸMQ0|:—ç@§žKÚz%ÌÜÖúôpÅ1‚Z²JÃPOÌ3ȉ)”ñ÷ˆz f—ƒ._�¿ýPÛ\žBÔÉï… ¥Eš œñ³<°Æ‚œPv9�øžS)M_ù%xkgm"¦ü˹î³”CÃaý‰x,&ÈpŠõÍ3QaÀ¸œ\öºå(uÃÌ’cÎÙµ› õOQœrÁù…%CÆ£™ÅW°#ÿíG!…0‚äúøMuT³ªÏ§zñŠßÖ�Ð+�Ťtyþ`þÅ.×{äÆÙ¶z\¤ât≬UU¨ïùou—MwL÷ïØåø0òÊDÅ9Àúí�z‰¶Š�Ÿí|µã—/$}ª=˜ý• æ­€¶—c0r"�xðŽ|úwu_>« Ó+ÎJî®´ÍóP#ã]1lr|¸`äEóeÓ‘CŸ ›;s¾Eù ê¯^WûϺž¡‘äqÚM�í_40^«OdW¤;¸m¾¨¬(xêIÄÕ§…ëT ë‘ýå Œœí€£G;뺫fλºêÏæ MÏÙd®Û€,ÇŸí FN �ÿÛvn8´?_#®´þÌžöÙ1½}K…fZ ëÏŸ1JÌÑò£`ÈÜêß b�}�Êâò[›é ï£,Þ—‰›§]ü§!ᚉ,�±‚ÙEbß‚‡‘·�wý\ÄÝÖƒ=ó3Œ>W;­Ø|^þÏ.HYóE57•ýª�@>‚Í^je|ÕÍGèLƒŠªWŠÞ:ÿ8z7ò¸MiØ}ÉÛËeg÷òÍÊZ�'¹/½*ÕIaº rÜ`DÅ@®÷½wúsá½.~pÚ3L 4°¶ß»ÃÃõÖIäñ](¡1X,ŽÊÕ¿vöX¿>[: Þÿse£ÎÁª3åsO•#˽٠Ne0òâ?Ké8v£Ó‘ÿ{�»n�ŠFâZ“ÜÓëà{rvy[é“/TÁÈIæÿׂ 5Ëw”½ÞT|¨�¬?æ‰?cá7ù2¼<à{Ð¥Ö·Ò;¥3^‡ûëΩ:ç�¼«ÇÜ�`—Ç\X²Û‚<à»Ð^ïô®bÜ:µ´¥5±ÍÏâ®4-q‚ ‘ä…Ž=¹§á(¾×àä߯�Ë›ÎÛæ¸Âóxÿ÷a'‰Û>­lþUŸ·�žQO.\#ç»ÛÅ’K.5&˜e'G¡lˆh$6 À�Ûˆ�ÂÌp°S8w¾cÁKÊ®ßk·1:+*Öð^ƒªè——Xeh¢<2…D#aÇ^~ÀÈIc—£ÑøŽwÕ‚_Iñj_SÖ¿¨™®í+p~þ¢`°aSÚß¾g!xÄ_uØZQ"o_r`EÏ—¬¡¯îU�œœ‰[jh2ž<òÑ.~|×BH|Ãì9;O™#A¦�±7Ít�K‰¯ß¹O’çÏ\E„p'à»’éÚVPI˜è-£Ž¨Ï»‰›�†Ã�Õƒ™»õ¬W@0r€ïZÕ®]ÙQ°ð±õ«Ê.QÜÛaY|êîãà¾�2ñŠ�Û;Aà{ÂVƒfi[ÓNw tH/÷–ÎÚ{ãp{=¸/Ääsá߯@1Í2JÉ j Sò¸'š:SüŸ-GÒÿ×n*Ž‚fÌtLÐD‰Q>û÷+xÒ2 ™A�l–ôæ¾éDá“;‰ÿWÿp2q^Ȩhò»»Uõ—,týÕ;VÉLwE£ä.”FC¶öÎh§õökѶ6Žð< nŽ´L¤˜¾á®ÍÄFmÚ«Bêßx96-Äá1TìH°ï¡I?G9÷€º1 AZ'ú�ÊÖ%Ò½òŸ÷“O^™É b–Ãùû¾Ó¡I?Ç8@}E5žtÅO]fe÷Ë0'†„¶‹\ ­T†¦�]›aÞþ2À"Ù%ÀS‘®�½vy«+¥sOãt¦Ù²—t…7,^8žŒÌê˨Nï7»ôKmõ¯º+‹5Þ5¦d<þ¶P±Ÿ¡÷±k=X8PG†7TTîºeÿ†îÌóùØæÿÕFOqÆCaÇί�ï¸üËYÐbÑ1½÷ƒò‰]k¨¯!w8 ‰`(f¬6ë|œ¾×‚:0ßÉk_{„×8¤-«p™¹S~èíÎIÎódüÂ`=‚þ�ú8 åúŽ¿)Ð`„…�� PNÑžC#uü° ¯QÏ]Þ½ÂZu2Âó¾à|)|§°Ž„1Ž?¨w#ºûj,y@h¹°ûâ¹ú÷5Ý,?x²WbžQYÌe¶†ó¸§s°¦û¥Û‹&Šð ¶ï×̆¤'xãÕW‘ý'£‚½n Êz“¦|î­ˆ Þ›‘|ó¯…¨øï-†$&¸#UW±ð< ^ŒÈ=Í囪†œÂ~¦œ Œs×p|`3’šàq‹;@]˜¿ô½�Â¥¢ûŽÔä^Ñ\c\áu}û|›ï¬ö�V%EhPfñ²Ò¼âðÀ…rbMI¼J»çê:œ}cù’œàq³ þ‹@å[bø*›¡ —/?ûøT¯åÞ¦2ým“x£UGáy`½šUA@ÍÍ¿ |+-Iô Žãß}—³Æy¢Ê(·qê¼ð‡T….}E_·‰§[ÓÿÝ…¨ÖëOçÙC²\VuQ,"¨ï‚Ræ»Rh¬/5ÈowèA†á‰G«’·@JìÜIUE¹´»æ/¡­_Šö/l�,ȶí¹“å9ó�4��Ϫ& «çJ ®‹Å «dºŸrÖTz�~yöô™H�EG&éy¼z(·öõ\$[ìnÍéÙ"ð×m¯Ù}ÿõ[øŠjÕYö:ZÞ^+�ú-óÉ-»Ž‹Zňÿ¥™tÚÑ".òìΓyÌ*˜< ^‹¸røa£¬çÅ'ŸG…H~‹ÕQ›ÛRÉMð&U½DŠO•@�ò†ß·×çJXïX<-‚²áÊeó§‡SY~0V¼~|€|Q÷mÊßû›“}ßk +8ÔŸFLJ¤~šÌ9C…çõXD¯¦\²£…f&Ï¿¾çcA•/Õ«¶_ÿ0¤ÀâM®f‰ O!v®VM—åêY8ëæ‹ï=ò­ÿ.ùsó­ËŽ&ª&r³#a ®Dfõˆ^ˆ2téÒ•Ô:BS|½ïº�‚ Ë_'UKDô×J î _ªBúó9ŸšM[l>ßxQCõ0ºàp'| � Ô[kµò(Å®×lJˆ¢•ÅÄo±Ú¾+’aãN²*X.PgE§'z¸�Î4G¦$âÄö6rô�ì¡¥û!ñÉ\¤|£¨«b†ÞŠ!\—~Êÿ òX¿þƒ’óÕ¼Yö?¹ "¢ýõTL¾Ñ¼Š¢Ÿè:D¡Ö,�W”×䫾2‰;r<òX™ ÔQYµ\Çdþ¹…m æ_×ôî|9û ˜C»±k¼&U=D/ nŠM]T×JñeÅ®^="‘kw Ö±âÊh¡/?ê¤h~ÇSþ÷¥œ½æXBø‘î—EZ‹ÒYþÅ^u�+ÍÎݲ|íü÷Ùô�•&jQmÖ ßbQy²�üwr•A$;ê¢,R"^iŠ¯ëØ¨”è¼²sÜ*»Õ£Î²«‘*|H< Š’ûà]¨¥d‡—÷¦¡˜’92Ò‰Y»{wAbãwõ;öû‹È^›ªÝ�‡ 7·ìŒžÎÒçxµ-X}õBÖí ]{—žH{ðív>7-µ­è¢É¤ùi¢º·þõAs·þ¼p•¦ºÖó¡SÛ£ÕôC†Šj[YýgUÕâÒ .?�il©…ndÕ‹™ÒsÕ÷SšV9l·bÙé¤jZÈvª̧YæüébèôëV¾ÿ\þóÞ¬ìžæE wrf Ãë|Ûç^ÖËÓ¢Þ*Ô~.»ûÊ;eƒyW@.‰Ná6Oõ¸-ûä>€r~ œæ´M÷Ê—¯aóÎ.H‘�KÁad¤õT%P‡`«îÛ–GݦRûúŸV[«îáõmµ~øTŠ÷G÷¯â3Pw@áåÜ¥³Yç|. ‹'5.mUØuÞÁœ÷Á\ú ÔP~‡Ñw¨ÚšµB@KÝrE«ó®ÎÆáC,{�¨ÆÄÍ^�ú-ÐÅ]òFw}¯Ÿ³î°“üÍŸóâþBUÖ<Âö)®uxlÌ´ ½]iÞÔÆ^’�] :Nòæg–°ª!qñ ®€ M²*©ß&úÅ�²¬6ƒ‰Ô�f,ÁNjïäê>Hí5òÿ¸´ÈŽÚ™‰Ô†|�o TÎÖ+”­_ªÌÿÑj>ãÔ˜Vv§Û¿Ñ¢�ºìajâð/ϾÃNºXÖ<5R]©}@½!ʉf#ÃxÊžžˆOf,rò;÷°�¥OVµ.úêX†‰™µ†*î÷éÏÍ>'‘›g†5í³ÖcÕaà×@]€åAÎÏîµ> ÔÿRûýáƒÈ :ÑÎVÏo2o<øÃð€:âÖom¨ÃI5G*º_œ¬W®h:edö5j2o¤ê<x÷¯wwµÆþ”4Ôª»þÎÍ#F^ ¾ÀçÑ*#ã ¼ó·|š0Ï»BÂ^rðšÐ+[¾Ã£úÊài–_NTáæ—Àûþ¹¹‡sí»DQ3ÿ¾ýpømþ#õŠ“W¿Nïñj"\Æx×�¶ìlÚ=MóÎj“Ü–U[¯™>:ŒÞ»~²>GªˆÀëxǯhþ5Ö¶$ñ©àïÅ›ÿ ºˆä ›Öé\cÅ9 �KœÎyÙÓãŸÏŽ{zNûìöž—»«Ÿ6_«Áê7«š—~ïõÕÛž.•ý[¼Xám‹ŸÙ«¤×+‹Ûº×è±Æi¢ ·qÞë;ÐZÒmDt÷þP™Yÿø6”?ú~ôc=k}3¹úb~f ø{dñ3/¾zi±Þ¡�Ò[3º=–Eǰµ7„•Û#p�÷ù‚»nŸ—A•­–°ï¶¸œ1ÏåÚá™§[Xóò¤j Èã¼Ëç+ºõhï…¾o?ú[Óí¯ùnŸµTvA#+.³U+@Š{À{|�wåY&­4î.|ønCàÏÓŽ&e|¬¼—ù Q¯Àû{¡ÄÞ›Çó5ï8S­w .Úâ·ÜfÃÝF–^'ªpÓ+ðÞÞäÓ©”]¾8÷kjõkÎLßêÞX¶Røk?xÍï_ÀûúÕ�Å—õÕez‡r‡j×¾,¥i£•«Xze{½�¤Wà]½€¯Kné…ìÞûCë^ßúá¢-°á†y®Ë&½ÖG¶;à¾Ï§ÂÃ|Ãyºþ’»"$]DÔ_ìu×M™¤ö×óðz°æSƒÞ3]–þë1Žþý̱Ð:d»Iøg–ŸM~uŽhÀ{yá~Êa¹Ô6•ÝÅ6éߣ¢Ï6U¨´9ưô0éµ9²€÷ò|yNù·åþ%¯�rÒ«— -?Ÿê0;i.‹;é•62x'?céϵh£-kp�äèŽ+}«Ú>�Þ �ß͆9ǫÞÃÏ|�?}sèxv�½ì\­¯ÆO‡„@BÜöå+�wï<Ôgßz½,–�½N8F­XßVfÇ_ñj‚{.T ¼oŸûØ3¹c[éýfÊ>b†‡öà’‡†žBbX ÖJzÞ³óI×î ¢Ý?þ+AÔ\L+¢¦¾ wñG6ž?"xÇ,¥Mo>zjÞF,ïl+×–½FÛ_.[÷á� `8NÀýŠÕ/‡ƒzh97^N?štÝQ¶Àäù³m�ÔèyÀØ(R™—eaxÀ;u%¨QW«´ý_òý΃MCuŸœÍ_0ú9z¾€Á†qÑ›3{ûP[�Ý¿×Ñ`ÓÑžòê�˜Ìî÷çgém©ãyz3â ž‚ÈÞ�‹?¥o,]²ªKùÔ¡¼ÆR,ó9‘¡7R‰‚”gïËy²‚~Uì9—$nñ!n é¹tkÍË;Û«Æ9úÞ‘8Ý~ìAëq̹gоÞü0Óä q,‰ÀèŽû¾)ðn\¥yîŽü%�¶ mÍOg-ûB§Ïój\IŽÝÿœt‰†¼_núFÌýo%ýVô—Ë=¥§_ڬݑ( q='Þ…K9‡4§k4VÎzøg�ÞÀ㘇÷‚óŠ­&8p~™‰:ÌkD5ÇbÏ ùø¯9sÎ|ˆiÝ¿:Ÿo¤d ÍЗHÀÐÎ3QyÀyþê%÷Â,ÅSÍò#~~ÇœŒžåp*î<øöåçÒù—Ëvÿ8#.ÖÝ,)Udá‰CI¼Ï‰Œ#"Œg&ê8`g(�ì ›�GóÿZ8oèî«ø9« ~AÒc<F~„£PH.ùA&ª€�+s©wyÆÌ¤Õºâå2aÝ1IGø.‘1\†éÑ<‰4™+÷°/Pd:$ò8³BÃéÌÛÀðœoäÂþãì\2�DäÊ= è!»fÚnª�šá›íI{µ’žßxú)Æ’ãpÆ¿ðþ›‰:ÅÎÛœ¢_¼nÚæYïL/þ~ù/là§ù6þ‰q"à‰~!ˆãTäÇÊsDrH±è�°�×~ã×î)2·Ê9ÇðàïId¢N³ó  ³ú‹ÓK�”féÉìî±®º^¯7a—„@Ž@F°ó"@_*ë Ö{u—è”Pm7•­#/¿ Mç0ˆ÷•2Qg€óäÒC‰g+UÏ÷ËÍopÖ¼>ïcšÒº‰þ’™ëDýƒç]�‹Ÿè÷š–«Ý–Q1°:q͹ 3ÁÛJ�8è�á�eãñ ów_X²¿íðæÆÂŽù3ò½¢£ á'xœb~š‰:ÇÞ>ÛÄËkdy¦•䉤7»»]xà$-ð�Æ·ˆÒ 4?o£«þ N¢œËˆnľ°gÆ¢G F²çR@ªn¾ŸÌ6\;$õÍTv‘j¶ký Æ<>Æ#’)x"Ínÿ1ußs³]'V¯Ws½x?¹Pì?øê§y�Ì8�FÁb¨8*ë÷`׿-µïy©ÞŒt;ã¾óÍu¼–Õeûî »fƒ°ä0»¾ð7XÍ]’‡¶«þò\¼Ùp�ȇi²&â ƒG#Ñ ˜P®ñæà/_æüjÙ™J>×)fɳuø˜�Äþý§'ôI'NŒ ï2pîäø¡‘0L¢k,þä§`�i<[¶Äwñ‘‰~‡à‰"�0ŸuP õßW Pèék÷w×?Ö5Ãé¯q cΧ\ÎÛ�:'|MW‚eóxÄýòÝ|H•(h(öÐ.•F8L ž¯·ŒJ ®‰å�ˆÙ.ß*õ ¶,ú¾´våßÞé¿ï1òµ�‚3À ç@sE×5Öƒ¶ÿN¤ÎÖˆ:¿i¸­óµ�[ a�Ȭ«ÿ«À:ÛŠÿGÊ�žºg¨}ó¦Ç�ÒžÖ¸Tfú/‘ù2ÑpÒ}[μ2À?ì›<:…wcüûöÄU<>¼÷.žoO4}œ7rß)� ™ Ãp¯³ç»â·Uº ™mè†Sñ…ÞÝ/ Q§r¬vAêÿçNüjìãæ0ürv>ÝÁ¨�<=ʧuI½Ñµ"Õ|²yn´4kœÏ´Ç ÜDñ.Öo ¼1T¤øv‹]?F–ÂÇÖ5?¹¸ÖèϬ·j•éÇµŽ§BÊ,9DÆÂ‚N¢S ±øÑ ÈþyÏmW�>·bÍ‚þËÚÚAs.Ì}ó šÍ�Of~h¼z9O$Ñ+hX9w�|Zxû~qBø?Ãâ�2¾þÆXªYªÕ hÎ$9#EDÆ6RÀßÃÛç=Àž*M/ê'$ÖšÞî¨Õš×ê~ l#d'gtpØe!¾§yÄM�Æ�»¢ƒ4¾oúÞ×D·.Uôm`Ù/Ž€ÁRÙ^‘ÃpýÈ8gY¨·²@Vd÷Š,ó;>£!yv.ë–<,÷°ûdÍI±MõÑ5[#³ËÏ/ˆ±î? Óg)±s™Z½iˆ ‚�í©‚�?Ôt§vÆ�Û²Ÿ^ÞVšaú-ïUùÂàwñ�ìÿødn%°¿ïuµï¦‡HŸÃŸö÷æ;-×hž=c)°sƒ±è 4 ·ŠÝ�Uî*'ß'x¢[¯Ôå·Í­ÏŠÇÄ—\cʼn±º+c†2~IŽ ¿Ž©fç ½@Un9ÚqìðÐOãnÏ¿¶_�‡Æ{Ë@*œù#—åÆÔÂðkØùÆΔÙ﵉»n])™¯Ùý¯ï¶·>,Ÿyy™ÿ„�o¡¦¿ÒÒ»À8b�—Úý‡Ë+¢éï¿éCªœù£‹PÄøVËÎ×Úöqå� BrÔòa§‡*ª¿Yl·U‡iÿè} äö?öeœäuþ\ºR<(ä2ÿ¸]�†¥ä%Ì${ŸÌ÷Ãr¯ãòŒ�/ü}ø‘è­ø‡²yMïî ¯ýûªè]´>ÒàÈg]’€ÛÊD=ömœæ¼>^¶ÀõŽ{½Q¹Ö�'7CÛÞðÞ„49ò'¾síG`G�nÉ]¦eî>ÏvýãÛ[by'ѵíÌ8Œš#�C=;öAórçÊ8!‰¤ö, wTìîüS” ãgcJBœ�_°ó§›®Á«§´/ì3­ÍÌ«¼Y·÷î£8 XþÈá""ÿ%0ÎPÙð%ýg2Å{ÿ=KÑHùqCáä·pýŒ6"ëç;_$Kü}yBrÕ•™ÏLã,÷:Ö߸(ó‹Rƒã“‚‚qˆõ€ØùRþößy5ÎÇ›Þu¼úYƒï§eJ½‘Œ�"úq#;³åØÂËçº ?µ\Ò>¨Õ•èÝðI¡9ÒâȧÇ>3:HzjbŸäP[O7\t¯ð+[¿•taN†%[ÆXr¸×CxÍÞ‰ë�?ófŸ³ÕO5 êòÌÛô1»Áêü|VÞÅ&‡yŠ0áp0ü7@\êæ)¸«ôÆJ«Õßå÷‚jYá÷Q%g`ú1é°‚«_7³Ëá/î{=ûu™x=áû–/©!^ÏkYã2é+Õc_ �⸼úS/vrkÿô×rj¾�¢]M÷nÿËŠƒ0ràã` …ºÐb��n^æ¹§u~J±d/_ÜÙ¿Yqä“͸èé=�ÇÍ8z¨cç¯{�;;R|*7 o,³ O(gùÇÿùŒDéýèv¾ü´Ò€;oü6}cú^0ÈçW‘ÞÁ‹ÈG~Ÿ ¼ï»¤uö¼ëµ ¥+Üüý…V¿??-*¼ÒæÄýÁ<¥Nh9Ùå¢ÒÑÊ£®o‰M´Ž Ø^_;IJ[rÂñd†œ0.ãÑÆ.'Ýruæ»k¼‰¹f �”ªVëy‹¥Ï&>då{ �¬ÞÆ~ÂsÛ�ý¢…!w�´òÞX—üN@TÜ׬ñAåoU–_\îï¨;ÙÛ­zc¯ºã³8ˆXà6ýûö½N¯ýŸÚ±ÆÙŸY³�™¤²~5¦þg ýá)ýÃ>’·±B—ß|ŒÈÛ·Z9ÎüÁuÖ<Á‰�¸žÞcÚ>¶2OXûò|ІPN§VV²`×á|UÖ<:R†—})8ò�X>ð>“o”þk[”ðOc¡W 4þ§]ü13xYqbŒOÁ¡1tfó±h†›Ñp”08>ðnSY¨éJEë±Ø,ã¥âç:nÖJlÛÄ?Iÿ#|ÆôÌP Ãö ,€áïÑÑÏöo3D%©(mJ9Y~.Áååº]¬<`”?vÊά…F$á}�ìx¯­¸– ç—ªþ,B5C»8A7Š?wše÷d³1çšØh�áþ`çÎi–X|èrÔ7c!J,îisØ}'‚ÔÖ¸NpGƒÏø4¿¾Þ5JŸÎôs‹n»ù½6â^§™_¹ÂQ’[‰ k\9óê€ï©?Jö®Y¹%ïðCiOW…A_U©+/ “ >óx‰1]ú�°ÞÌz™¡Ô ôXuC*c݆¡07æGþO\âP»?óòÌhRì  �šÝSu~ºBÑc1�Ï�â¸Ü±"&cû|ðóY?0_Þÿý4þ¦r]×½&)‚§ÉɆŒCgXvÄüú kQE"ŽÉBj÷Ðî9«â ´7”]Ðn-—o1x$ýçÌ-³Ö¾Ä(Ÿé¹#Š�÷ßAv®’LïÉËú�=ù‡¤ö Óç»û["E"‡¥�Q.óºÆÈ-k† ¼+ä©›?pL®%Ò~Þ+eñ:P/‘°^ÒçÒüéDÅOñÿR%‘YMƒ«üò¡ðêuû% ÜžçÐU¡�æ-÷ÂW|eíwÁÊÁ�Éd¤u?øNÐ?'4f¸PëcÄ¢¹<É×o‹>™¿Ù’Ÿ¹Ÿ>*'O˜´W ?®<À;>^US �‹âÙÎÍG7˜ë_ ñ’ñšßù—åÇL.5ŒHÄŽ±Y�3þ–Ï Ìï>a?_ãy¾÷‚Q„óLÙÇ«=훿J°æ—%ÐOýR,.=ö{¸ögTz²·ßÂ/ô[÷öi> IË’î/Qnª­1Ù‡» Ãñæ‘ÄOrgºé«KeµJš¥U®ñ+=û `Iä0Äs~ .¶°ÿ™¶5ò[V¢þd&d%´¿ SŠ^} ÅRéö‡§0< þ5ÏcJ¥]ô姬-þ!é·ìÅŽ[†­\÷ ‘JY¼y8 C>Çê9›ö·þwØ¿úÆÃNú™.ß›:ÏÙüìaô“É Â{sÕP·Y! ÒÿJÏÍÞ,ûô6ï? 鉎K–„A#<"iÒD{]fƹê‡3õAZÑ_µ;fTɤÊ;[ UäöÁíþ �½]‹ÖþÙüZ´‹§¤Æ9ãe×½Èå·v/ãÀïKdTÙ9š‡�kj;n4µ¿Ã•X�·îôÎ<¶êÊMHrüsho ˆ`ÿ@]e9‘6rÿº™n÷’!ÒWyeñéIœÄ›SPGY¨L«Ó©á*xšK{ûì×'oïðûÓ1‰G¢0¦#P7yšÒ¹£ÿÚ:ÞÛ”øÊEž ›A;àÚ^Ñ IOðÈxl &”»Qعև” ®[¢b%>þñ;(\„w°YØ~’šàRpÄÑÊ0< .²Duú]A³[V;I¬�±ò�WK´ù,3jÇ#Ÿ)áæ¯@ä5÷æ<{QfÜÜxêÒFþ7^!‹VËGAüŒ¹ ùÞ(tÏæo—)"4ÍØ¶Z‹ì䛟\å ä_•@=cþ>5jÏçÈÁÙæw«3骻*¤Õzú¢Èh¶Ë�0¼€ÇÿõÆ’ƒÃbQ˜ßw�XÖì—8 1?œ@B² þ±‚—¦îéy«býc“�8Þ3Xotæn $ˆcLïHu/*�úÆRfb©¾Ü‡ƒ›ËÎÿ Zµ²¾ySÙÊJhŽŒ§�ˆÌ’UÈzê‹È?ñ³ú⯷±›¸íkP¾ªaÍú1�.‡õÄ|¸Ú+PŸõfãšt™ÝÚbš’ꕟE«ò�ö<8‰Ç;€:Ĺ䄶�¶ƒ)Ö½þŸVGšžxþõ4mÒ~r�zèê½�÷fbÚ�(:è JVgääUù-‚Ä&½oöGl_pNP‚}wü>ý¬¹É�Ç_áÅi‡­ü8»Aâ¾øP:yâþ)¨¬ezVçèrƒÁ¨yn8ݧ I¯ ®ï„·ùËp¦x‹Öܼø%&e§ôffkí\ª›;ÆAŠã1@ÿÊòRéê?ë’‹q-ÎÿÐ|Ù7A�º ’ñÃqÌùÍÓ‘;™Èû°•±ìÜ-MÁÉígîølÿžë÷^Xë ¦~¾¤ÈÌo1>>èÑrŽ,�y«}d«†»8Oñ 8¥8[| »ªüëß–y�GÏ.!¡$!á.á0P—w¿\ÆÏîO¾'ªÄöëwØüݼoÏŸ6SF¿™æeû©í×fTƱsÑ'�Qöþ£P¾FIçsÊZŸüûYд‘“_<•y%i½™Q ÔÛÕŠ¨M¨éݸw^:eeÜir4�ÇFÎã $5~ùCCÓiø ø{ì»ÙÛ§oF8*þ} AÆL³6‘—§mq‘\aæ hú/˜:Z±`lyƒpŽ“QÏÎÅoüàëø]Fè¯ÄåͶ緈´/À�öö†¤Ç¸ÿW'7�}úþ®’Süãþ‘£6ó’ì«¥óSfoþ:ÁýpÈT¸{ØÛ;§púlÍoáÌê�mÖVºz Æ�ª&èªT.–/'¼t $7Îe¤è†ï‘Ïhaƒ�¸@½bêå�ú7öµÏ6’™û;ñnšrã‹ 2;wôÆ.Po„×6$êKØ�¹[îg¿§W{˜[Ü÷qÆ!Hž�;ö©¤Q0 ¨;"äæËK^r Û`ô¨Y6ô4úÕ¼û�õ™�Úÿ¹ž·*FÀð�ú#²þ !Êi«ïaÕÝwUªm³CÝyá?©pâ32|W½uH¦-ù¦ht1E.Å%ð£‹r¢Â]Ç‹9ò?Ž;¨G"z]g±}ß iºà=Þ„7r—b–(qä�eÖ\ø@}ƒ¿E¶Ó¤}Uê\D­TÉŸ5Äî[¨.aùá(Ÿù1L ²Þ¥�{„±�ÇïP&û> ¦Ð­Ÿ¯tê0yóÏ RžÌPOy¢Õ°| N‰D�Ëò…|úÞ7$ƒ7�óÙY,rjY•È'‘¨8O_‘Æ�Ô-qj¼=ãæ]\Ä�ž&��ŽU Ýipà³¾|7"†Ô1Y [;C»«íí0‡ß^�=Iµ?!%ŽüàI͇ãuM¤ÄÞhX)êj´[yûW§Ýl }ùäj?‰=`ƒ0ÌúõÈöÔ5ÑýbyRr®ëºÄÐËëPõè&¾¿³Ç�±é9õM¤·ß‘^áxÏ/ï59|éìAn{¾µgx²Û!–D¤’‚pÈvÔ9#ÛSqÑ()›ë¿ÝÍÉ:öŠÞ¹öðÑü`œëƒ¡aF¿EÆÃp�:'|Es\vÊîÍÜÁs$Î,é×óˆÏ¿FçÛI\–;Âq•ٹƿ�®ä¬T³ 2è¼¼vw_À\ݙݼwä»%\¸@ý‘h�’úh‰<ÙÊ]ËéÑ™ý¹æ×üsØõ€#bI>#5ÐôÔQΠãP®ŸÖµ:á¦%ü$ÌnA$?×qÓ`çN·ð@ívç—á¿tQDáL¸¦Mßþ˜^eööŽÕÖFn¯&°n[øïÂw‰ï…fÚñUómÛ„}{Ú½‘Ý�G¿8ÁÅ�µ?~-¾ëíá˜?—è'ß:¥½=ûühÄ|vîøw¹ÚÀ½NûGWewe8¬(|S�áw_àÔÛWXHûÿ\OÎÓ-œœ™À|¾Íâžœ2ÿ#“FÅ´%˽³†D�^V`÷“‘ZØñD†;‹�;“úsù÷}f;õeVŠk^þ’h_úºœ=ÞSɸ  ‘Iq ñ^ðòuõ ôuפîyüÝΗOzl½Ñÿ{qð!o½�ÔSìö‚'ššp³o þo¨T˜Vÿ.ѫ߿¯–÷i¨j�ç+È0fÏ›F¸ž£@ãw<˜üsŒÔ âÑ{k–çÖô10Ètñú� ˆdÿÇÚ™ÀCÝ}�ÿÃØ·l‘¥M"e0cß+[Ö’µÙwcßÚ”�¢()B…i±U$K%•P(¢H”¨H*müg>3sùÌx¾¿ÏëIOÏëõ>çsï¹çÞ{î½çÍ´·¯£3.(\vƒ¿¿©£§ßÚÈ„º\Z³ø—¨ð Ë]nº‚�&]­Ô6Ï´7‰oH<<õ Fây„üëÃ:�„廥ºËÛ}ÿkãÞX¶HãÝû™u‰;ãZ‰Á d½�¼?kAÆ„¡gRP€à «#Á®ß�œ·p�éO¸eDày$/Ú‡å¿xÖ5´Š§ÎFNñ=G"Ki1n@â’ŽIxXJÞý‰qV«º˜5ë8ú^zÓsò¢“ÑÝ™}"žçâ*ã�CÔÈ÷sïp|EDzó<×þQÐþû’9ÑõTÀc ž[ 5�ïçÐ�˜´S®¥dŠó$ë •\~2ãGñ¿ ¿�À–*ý²DïCº^«êŒ&äϽܷ!lÉüíD‰!¶“e¼†áºaÍ W>®ú¤”cCE•k^Ø„é˜ÙÍæâÕ%\)÷¿œŸäýá?¸ø°byÝC%E�}ŽG¾‹6VE&Îo—‘ø½º<–¨Ø%�ÿ§aãšP®®p•¶¬éVÅ¢Í<ÛjY¶®œ¤Ÿ!ÔÆ'�÷§N�§)¢Êþaã— ¶Íe6‹Ã¯™õRðˆÍ‹Ä[OÉkrZó÷væÛœSR¯w÷µ°çrPð¼©ñ€oñу‚J�ä÷¹Vò¬!Ö-Àù£I§œÞh»ÔÛ Š.R\Ä88‰Gi>H\ Ý[‹»—ôc¨*éÚÿ¾fÕ³™þ°�ÿ‡™ù¿–pÁ…Í|ì>û'1~{oŠr| gÑQ^g£,âôÄcüÀqç�¬ßvZœ‚Áw  Õ}Ã\�H÷øçåÝgî�Ég|ºÕ|y鍸kËõ­|ö§Ø›çK­L7\ˆŸ q|ÎË£ŒƒB¬8{ƒžÃ¸u¢yÏèÙu»ƒ½YÖ�í‰ôœÄ9ç4Ëžæå²Rr9£ßŒ¤bÝèôU½Ø´×nàL¸&9�I\/�¸~¤ü“äs†ù¹lÀ½¶Ê“·ÿÈîlÞ��;Ù„Çc;¨„ÛÍÖ—âq2—�’+Q•²d�œÏcÆ®°ñ*Å¡%køØÊÒ‰ö sáLÆðý¯ùy”$A´WbÝ¿™—@Tõä¤äò«„Üu„²'mo�»i®ÓÛåj—Š �Øanˆ�MÐO‹š+?‹>ì­OŒTŒ¾®›uGöì_b<>5üwQŸ:w%Wé´™¼qîÒb9wUãˆ:ûšwÜ%mo‰ãŠX!žòh�&Ÿ›’¿îå>;µ¡ÄÐòê¯cQÑ×-^б�"ž_‘*ÐS·+J·ìÉ=žù›l rÔË„Wùï°ÉüRšC§�„º]ÞäÃ/Юöª»®™zkyì–©XÿìZGKY?ø|—º~|À½Å]Ìj“(�¾i¹«¯]ûxßZOs.Áí)Ð5 Ä5h�ýÏOÉõŠ}ö7>)ôðyÝé¾g[™uûØÎÊ?ÜHäÂ9ùàÛÝç‹ósßßÝ»"«öýsý¡L!Æ»ïÃ�d<Ó'¶g�©´!5¿'øÑí»²žûY÷ìZÙ=§�½s7vñ´*Äó‚ diŒwAJ6 lQœ—ô¥©3vŸÄŠÖ\špüt¿ˆ8ÞƒBœ|q.!$ׄÀ[BÁ£—mmñ­º'4õ5¥~OgkEF7Dƒ¤_¨ßìwóó„(õã:¤²aÓÅs÷Ntþ^ê—ûG[¸Ìá�q\â§ 4š¼§=n€ø¦Ò!åë/¼XmûúÚòÍ’¤žNë5&Û F÷™‹8@£·lðø#ò ¦�÷ bzkûJ¥(k÷|ûƒ}a‘oL=âŠká}‘7g¼éLçÇÙ?‰\f ïñ¦°[±»'ûv2ýý�&4Æñ#4éM̬ïþ·aEÐÈÿfH/}}““ÁªâIÙ9Ž|š·ãY»ORò\ƒ¨ð€fÄvwðË[þQ/zR:½î—°v BâÙPúÛÔȤ'i5ÉŽŠƒµ—ÞTïµ?7aeu/ö�³x÷-æ÷kÀy ®½ßY;:td7¯¿ïÏcë´½ûËä!fbÍpgdp~Âm>\ööeå^)M3ÈNëΚŸ—Ât›É*óp^‚Mÿ¾×]`Øaã]£H“’‘Óêôá•Ó¬zós€ó¦�϶¾Z=¹X¢ë¦›º©Æ©˜çéI|§I*œÀ¹[½~­×©­/ïlÿ³k�v µìšÄIxžBJ A}ÎAظDÛvÔ¦lK¹i\bÿÊO?¦%´ü>Äׄ§ö}À¹ÃÏ·j·b—þ¼Ì{×røMTÙäT� ‰Cíû€sŽ�â×SפZÇ4o»¨¼ûÀ�Ÿ!1B©CxŽ«õöÎ5¦=×¶aûÑzJ±û0æf‹ÖøáÅñ;\wb&I828¿°ÿ™ÙP²[/R*âÒ–%îéì‡íqèt|»x„Ç®�ÔÛ8·P•åa)2ÂÐý6ðxÔ¾¢éo9:.žm þ;ñ<ªß) ô_aÒ}©Ü°x;¾`‡Ÿ¾‡Â¯sEHjíœCÆ•Z1<³J)x¦s%8)HëÊv I^ˆÙÓ…|“�œ;ò,«Ã°®d-Ô¾ÓödÅvë[£ˆÝÓ×Ñ^ÐÒjwà¼ÁÃc+“ñhwU¯»Hë1¥t>ÍÍË?£ê ø,‘²Íü<`¾g9´U�v¿Âë—σ_<Ù:(º kˆÞ ùã|fž> ðÀóŠmaë÷¹÷\ÚwskpC–L°ç›övvuJÕ}p>Áû¥ñ¦²½at„Ù.‘¼tÑ•‘K?@\„£ ^Àü<à\‚nߢ ªËš á»âá¯C-ÃE77³š“ah¥¼f�Öú8ÏXœ¹~UÄnhjåÇÊ; Ì£Gxÿœ÷dbÆópÎÞ@»ùyÀy†l[õÑ,½"ý›n&GýJÕ?‹Múdqxî`>ëùyÀy½á¶J“M7R}KMœõrõŠØ¹û/GÏâÑú^à<ƒåûá?8UýC:žî�tnØp]ß!‹ M+ùe¡3¨WuùmšÔøôTK¼ƒãÕÄ~TXò«ûÅÆ3Äxp"ê»èyï^¿NÕáOÞw±i‰ÃyUÅ'œ~þ êã 8÷`=°ödFf§bþû¿ë–ÇL¾+_ÑÈ‘‚8機2?8ï`- N¾ïõÌûÛèŠóŽ1›È¥QøñJäÍ.D†ÀÎ9誾†Ê3û§¿azõZ7ÇôªwñQˆ¬—‚Ø~ÀyÆŠUô¸¼«µ`²ß3a­ý…mìÞrò÷RäkžŸœ[ðÛ±+(,NÒ4â�Jíñû®eõ[vÄìëMN½�Àê°·ö½äa;Ó–¯ìû ñÇò¢ºE÷Ý6¦B,xI'„u#�‡—ç°6ÓME÷­LÉ’aùÁ]¢êy<¿¶3A|¾¸HOG4¡æ-ea^.�——é¦Àx[»FÁ�7ŸK™ä†Ý™|À|)p>.�ø p~Â&a^K8“ƒv|ÂYûSõ¼|¿YgÄ×…™Y$!ó€ó�Ô-[£è¦Œ¯ö¾§“èYË¡)ä ±Ì,Ý8À9 ‹Êû9îCŶe§²|¿ñìz§T$ì!ó�ƒ7YôÇ‘o©5èò¾óLöÖ7å¼î¼ ðÃ″êïLP·7à|DÝõMa]»ãWûj9ÞBotå×K·,ƒ¸Iui\Bü\ý¨}'p.®}(#ºNÚui¼†ã¡1ç“6û÷‹ù[.ãúîë;Ï·AœÄ·í³ "ð€:L ' o[Eéy+'nx¹§ÞüØæKùCÂé­¼ Íõ¯â}©>Ø&`±† säe-¸Ä'íókš0ˆ<šã ˆWÑ 7>xÄÃÖf:Ê›³Íhñ®Ò»Œ27èfóhŒ/ ^%.—9aPíÛRÜ»ŸqÊ�ÝÉš^¾þÁÞ‚ÉûÓ´ž ³ç‡Z ?:${¦Ä ðÁÈ�J_gÿf�#-?¢3náûÕ5Ї2pˆÀò£G¾îÊ ;ÉÐ1ŸUqƒ4­vìããÒÆ'�g@ˆÈù’¿s~��Ñõ¦åÞ’£:qÉÕå?–Wè—ßÎ9öo'Å…��þ–ÞbäXòÈÒd£Ü·›÷ªM¸œ=«Š÷óy2[Ly@>t£$žOÒéG´c.-æÀ]ó]þÀ«ú,+´æùz†Þ€éÁRI_ŽÐ¿@Ü�µà[+‹sÞ*�nyç½BSWð‡sÅáíÙ;MÈû@Óž�ztv™ß-¾»ú~E“í VõW5Znw®ÎâQ__§ÑùOSªZ:ºI¬©>Ï»>”Ÿþü~ <Ïß™r™‚Äò Ð%ܽ/œÛ7>dGmõžåì6Ï'†ÎâÑÐ� ¨W¾”ý ÖÓïýº‰ì·ÏY²uÄò¢Ï¥£ ðvCâáÊXPk? îjþI~T!—ç ¯»�ÄêÍŸµU——mî†Xü]ƒƒ#¨ÌÛ@¼UHǪÀ#ò©ÍáÉáí;Fô5Wz²|_R‚ÿÎ@œ{ kPÍ~â­t“ŽËLL7v>0zÅúS¢x‡tßánûY?Yõ5�Ú<„e%2oõ¤lÖ‰¯‡˜‰#¨ø  ýKÅàˆ¡Âö;·]&ƒàzcÜÖáõÂϽhÎ;ÄŸº^@>¨kX ¢éfô'þá1ßÌç弬kþ¤äQíÊ|0з�þu›éÚ"o8©ž©Oc応›©Žï<Ï�2¿Òü< /̆ÖÍ7övþȼóíÑC‚KµÆTÏs|6!ñHH¨ò€ü0¬Š¦IŸ.dM±EÕ=VŠ�,ýd�k±†Xñ<\rüÈ Ã‚õÚôšq:jr×–û¯/oFnºVàõߟ®>>ÔúÈCW›¾Ij”ÓÁ…­4ÉÑt4ÍAnJ?bûWÇ’Ö8òÁ°XëUYØýÍPú¡Hß›âôãuÄÚqÀu&g‡‘x�@jÙ™v¯l]Ì,­ïòÀ, dVî´®H¹½Uu¬îÈ×ÀSiïñóJPp Sˆ?ä�áù6’_¹Q ­¦¢õSÛMã”§¥ŠDVï?²òÀp8¼êÔHå‘þØñLÉ£ð‘~(]Ìö@üx ôôsŸ]B�ä�AÕšzù›Oíh3âäÀ^â²ûYËY©AÉ£:>�|0Œ:ÛíÃ…¿èz¼+"ÂVGëQ¼7æc ¾ý#| —Þh­“Óè?‰‹œ¾¯¿co–.Ã�2»-‘rh#ˆ;Þä:’È…»!ò?-/Ù·ŠŽ�¬Õ0º½äø"e©e×W�‡8‚çd(@à~¥ÇéÚü2ÏÚË÷¹eˆFyðÃÏ.ײClÁÀ«DàÇ—+çm?>T6=�[º÷‰@³í ‹²Ž6Ä2“ �ãEyß‹~eGå��{s³ª,Þz<ßï»ßRðÂjH XÖÑ¿Áp'í ñ &ßÄEàþœ“÷ÑcIµé/¿_}ÙZ¥Ë`þ‹q|‹<ćçúùá‚ X|oѸ7˜FøuÆLãW#Û–5 ~´XéÒú¼EøéE³[©g°ì¿LdÔïï~�m´`CïÚ,µ¬çfŠvŸÌø`RL°< M¾E‡x/�ðë,›z 4[tL_ýtÄêѸ ö׿ë‘G8�v¡ÞŽ€_¦?i XߦîÕUròÔ²Æ\þ¡G“þ§ ÁYçç†Ìü3ôÖ·huÄ㟮 jõÕV�yÎòÏ÷½4 ôÏ 7(?ƒÀL*ú~š'-U|Ûìõ§‡1t6vMg3ŒÕ:BBDâh_´|˜câ}ñ4ú J;•óï]ü¸Sþ¬ÕZëKvçkã1*Vôt„~ –ÃÿB;9y:Së§`JÇS'΋¥+6\/ö¬é©xª:îo{qZDäaðS¹ /„’ÇœiZvY÷¡#ß™nýÞ;‡Ú«£ví€x‰<,Á’f_:Gæ†Rr»4�1e/*î—ãóÝä%ë´T#’†¹òèÏFÃîÃ(¹ ©ÎoVî pÒ±Ó,øq¨üûEu5î~2WâG’ÃfêÜpÀŸ8lÏZ?€ÝªkùÁˆaqZ Ýùqé»Âs1rpQ894­qAÉ»!ò^×7ñÜý2çq£„ÕÕmrºÏHý…•C;ú¸Qí¯HJ«Àñ»™aÍ ‰WN²þ³mïå~£Ò÷c1¤Ú<ð…ú÷ï¾_n¯h«µº²Â ˺€g‹£˜·ºT•ü)�‹…õ$笣êöPrQ<¸#:ª»�ªúÞ„ê¤ý¶s®ÛC²/¬<‘KLðD½]÷Rr1ÓM×r;ÞQ/OF)º”¬ ô2iKM$µƒ¼ÜìúÝÔÛÈ ÆÇŠêwòc¼»O¯3纒qš‹áFü2‰ÀU�CSž+P·û�~Ó¼&höM+4¾)aÉu…«ÝØßKê’ôU”ƒOƃ"‚‚]}ièŒß»~!t_·~>í쥸÷¢É–#t*$®Aß`8“­qäÛèµÿ¦Údƒ×—ýŸ+I±¾šp…Dð\ ÁoÍç©ö�GLŠÓX÷ ¦dïX“WÐ6v;™›»¹7o#ó1|ìBø@>±u=…™Qy¶Ó]QSã¯-�p±er‡–ùX ~…>�gŒ.#Á×ýÓK~�ÊE¢‘|ù†ÕvßaK"ë/?‹ïˆ_&…ÓÔÈ7¶ø– §²Tõ*Þ‚®–¤¯ÏÞð ÛNh¾#óýÃÜhòcý¿^k[¾ýìÍR}Wþ É.£¦ýÙL�0‘¯8‹OQœ™GÉ_RÖyc|úvíÆÝÂÚ��èÄ}‹ZêûS<_ í쌦œX¸‡)¹ìjËj-ÌU¬«^f¸G±^äþ°þ5Œì§ð\eôì{•TýT<À½þGÅû¼üe�íFçqøàv–¥cÌë<遼Bœ\<ñTBzjþÈGưeÿþ»î ]¾<èî§Op‹wŸîq4…–¸x…I% ˆ-=Û« ò�Rò¹9Y „N*>îM�,Êßi�³r¬åæš.b;cñüà¼ÆžŽ´Þ¹¤Ñ'PrÕì]¬÷ß2µÓ>W¡#�å}R©ïx™ØÎòðø÷Ç¡g’¸#s?ÈÝ3TÁfÇ=èoðÔSFE[pŸ´A"™‹!Üûtu�ñԹǀvîJ”Û¡è�ð�kß}¯Ðª©[dž‰ý'�w¼¾3#÷80a71�ðÃE°>Âg¶òcW�ôf2WæèìAÑÜ$`] ZºÌX¸ÁÏúùîÜ#%|÷F3°cö°ßÆ(àÛ—Tï;hø¥dJ.WzФ\÷ªà÷æ¿õûw(ØÇ²FÜzÅñÀ\Â:އSY¿žÖŸz²ÖK[Ù˜žY&°ÔLå½¾à=ž _’žøqAù†’Šž'�y«/æ¥Iá¾§7o_Î/Y¶ò8z‰ê³îƒ?�«ÛW(q®¥þN)�>X×È %a:q¸µëûI^Œ€ ÃókÉ‘Äñ ˆ·/Bþuø­ñp °ƒ—iþôí÷8;ßù©æÓ½í:[$js�8Ÿ(ÂöEÒ—ø;~(Óh�TJ>§Ê)q“³-–¯Jm�׋ ªb\jˆ#ú{Ex>!ó‰Å�hê° ùä 9¹ÍîÌK…ªŠ¶žò!Ú±"~>qvõô!DðN.ˆªŸÆÝ‹oò?Ʀ9:E��ý–yŸƒùè¯|h9ž‹%¬ȹÍH9| ©¶<¨òÓQ1 …`¯›³¼×ûñp»Sê\U™�ùïütÀ¾ý•D£üD®ŠH`�V6±1–EÃíŽ%¬~Ív?KÉgã ˆ7VÖ9ûôÖƒ•)�+ï({pÊžÖ!ë/ò} ŨêŸAÉXªÊ§žilý80¨¹�y8žEx£ß4<¿` ë€ï¦êW3�ö¹T`²Ýëç»sS<ÎH6‹äYäÙækCbD¾â ŸxÁû_Œæz! _NŒÞ8ÿá“Ô¼ŠÚκ"¯\M­ù7¹”gä�$Ðì‡s€¿‰Ðb=RºLH·hhð€Ý¢•iNìnKàõž¯2‹Oô´õ?èŸöµ~ñЏ}’‹™îð•?ªìxŸ„ãH†D‰|ÕYü`Ç`øôƒ†_»Œ3Ã=µ9¶2_ªŸ¹½ó�…BÍÈG{;ì°„ýé?¾£¯“§;¡þ, ÿ“ ìו�{Üz‘—zg7¸«nèÓ"ÍÄ»9¤öÇïO%|»Þö:R>@FàçúKû�÷ü=2Õ[ø]ÁZN0ýKĈLq`fæ;¢•:9ºÀ÷…¨�ƒ\J>ËÉmå»jzŹâÌ½ŠŸç˙Ŵ§C+ˆ| ™O´Ò@WâRÛ¿]úW\¨5ÞT‚9®-RG(3RÛG-g/¶�ÌÇRò)F?ПS^"å}[Tô¯§Gµ,^�{t˜¥¬‚$ˆ|yJ>1Œ‹öõ "¤©£:žóúí[¤óõ˜ž×=Ú›ø/ú|û˜Ë^x>ÇÖ‹„γ™[¬ŸùŸ™ìÍrhæÊXtì›�oQìtw†Ø>x�ÅE*q8{8ú¹SmÿKà:Äë€9“ÉAëjÔ%†]#"ÏO3¬ƒ÷åx>æãÿ%c‡Råv™p›M‘ïGцù "æÎýöÑ–m°_�'Ì_>AÄ­9ìḿVôëÑ+—¯æ_ñßâçäræYp÷¯Õ_®)ÁþSž0|´k¸³OˆËâm—�廓c¸f&²a‹bd$ÝCáûg¯�KWƒý�Ò??ã™û¥Â¿ô3ÇFÇ.Ë ßê–ò‡* ºo×eó—Ÿ!ñ)ù呸%”|&5�AºªÓ)Iìi¿kd¥[ªäEäÃóŒ ;¼Ä5²ƒG—s/ý˜ÃlèÍu¡¢~Ôa/'�'ÄÜBüü"Є.¤¡o%0>y“«ì—f ³Ë.ÿq�—óô£5¡ãaÄþS€÷�ä\(¼*`¼È øvót›ÈçŸãJ^ùº€ÿBD†œ»¿u &Ea¸·ûe÷^f‰ô·Ö¯è³W}Ýr…óÒî+1ð~ ÏÅ’¹³*K#p«}G8å,-´Úç¤÷sp¤a×=ŒÆžØæÄ¶“‰�‰�ÉÄ6;¶51&¶mÛœØN'_ž÷;õ;Ï[§N}§êüÓÝÕ{ß×¾ÖÆZû&KxÖeØë´ù\”š0@]Wß2ïß µW æ÷7|H÷};½�9kZÊN9–lqlñÐÛP½~ƒæ1€w>–1ê­_2ü4tù�o>(ùŽ·ç¬9üÑÞHeÄ>kOÓ{#ûëk@[w!8,W wŠ�lˆ±[ëݲI¬^\� ­6¼NÂtS�îÊñüœo¥��DxÅAº·µW¡º*E¡Z'0�ç Ý« o?Ó']]^S9àtQþ­¿J¶SøŒ*T±“Þî[#½5EF)¸‰•{¯ÌG_CŸ¡¼Ñ™UæqýÜÁ{î·â� \ž¡MƒŠ¬ù7¾eß4ÞžãRÆbHìu ±Ä¯ñþìE‰y5»#Þ¨&¸,þ�ûó3“ÿ¼ôÉö9@Ó¿¬<Å4[÷Öë±Ú«Û-¾ýœõ1ÐuüÚ�óH¤Ú¸!oƒ"±'”ƒTÌ0;h¶wXµìkZÂ8ûÎ5´à¢.ѵºsSuÇx#þ¦Jy¹ÿyéȳv·IjàËš¿TáÎ;~ö;MÙa 6%ô¥F½„…š G{G¯rΓ$er0a®)ÞÉŠ…[WgOÎMc41À˜¾ :üqæsºÿF†ªùöØ·Öv—}›Ü·Ó©»¤ˆ¬à‡Œq—Ìf ø$¹U-šÎ?`>³ç¸Rl-¬Ä>xéÌ씦‰† ôv��<±:»ŸâÔ–â<#ÎXEžîtÒß™’UºT—'rOlÃ}±€wƒ¥Ï �W ë»ðŸ"×ìGS–Xe4˽ħ ]™˜ÒCö£ ª`lMËâ4Òõa=}íØ{]sÒÞëëÝeK˜à «’Qãàñ 1%¯C8¸2:Z3Ï«Ÿ«2^b/3/ªç‹$g+"öÓzæX×~TÒó ×`íhæN°�¾2�¬Ýï–w$þè©ÿyÝϦ¸Fbä*¨ †4AÇú ,sŸÍá7Éuä#Œ@8îòÉ]ÛjÃݾ©é1ªŽŒÌ§Ã¿«™~zß0Ov+*[@ô¾èI. Ò9¸Y~*\EÂùŽýüŽ“§Ûà2ê$Ö=üÛ ó†ëmêu.0rÔÔdÏöëÞÿf‘†¸žÚ³_ ÍÑB4óT9 1L}æÄô²IHŸ¨³¼f Œ)E e']›‰À‹rÚÙ‰lD§`Ët|Óî~µ£ŸYW±fƒÀL•ùög '1pEGR<+ »´|†Îͪ¶Âôr3bUÉ÷´¦ð²Fñt`~ˆ-¶Óv=ë=œ¨)Ëð/‹Ø«¯ÔÏ·]À¸q… âöá�d)âŸÃ-Éî}_L¡pü5°E€Ð¾)ø®“ÃÀ�,±enGéÌ*~ùã’Ýå§“º·“®�·œÌQ;ßJþS¶ÇªÀ§ELà?'¸&‹VVÈ !Îôw &ö›‡SÎJ;h´}m§nµÜo 8·•I#ÌO¢¸5é.Ö¿U> rhnG¸˜n壿5.ZkS”§#³¯_ PIr¦Š�ªÈ¯½ý[ã"2 õQkV�Å~^iž†ƒ2:mK—šr*[È¡Iä®4{½®‚V#„íuV+uì*�/kŸ-ÝS í·4iP§ãŒ«™§CŽ>ðqQº6$ÍÞ©éš;ùº- øÜùŸôàqÎͱvp+˦^:p®a£Ñ CªŸÔ¯ä xC‘i˜éöµ^:ð ¶iP÷&Ä;H¦£H»÷‹ìH"üëÂô Û€N™×á×nÝUŒëE„ƒc•²³Ó k/ßj…L–&× Íé0<šV!‡_ ¯ïV³Þ(™B9>ànÈydˆ°ì„�Ü— rè²+è•n¸^š�»¨–×s�¯Ž‡IBúȶ!Î:oãrS&@B˜èÉBí®XÞêâ‘Ëù3Šs­Ãñ#¹%¹/iÄü"cú§n*/ß[äÔŸzßìefϽà3}¢¡Æ^‡ KMþ÷ŒòA<±ÔІ*åé,“ŒìßxNQ4.jv ˆQHÿ¤Zb!#&á�ßÔƒóxØŠ.+ÚWä¤ ¤Ty½†6K;m«ŽuT¸1½û¥ÒmÑ÷È�û…½›*Õ½BYþ ŽDEv)ÈξéA3õçõóBØþðÜØžìœánü´’í| ìN�øÌŒ)M«ã¶4ÔÌ*j oÀZ¾×ÜÂ?­—ÅŽ ô~½j ×=W™½—ÿ~û˜WhaÜkCøTø˜Ô¤'‚y}óJ2´´!oÄ8F!¾º ¥v2´í̃SM�ZYu’k«i¡©ò6Ä h½¸~,Ù…ÙNlrû0[ögù”aÚ…»‚¸½õëúŒlzål¤ÿÔÐx]é*$ŸáH[�ßJ†‘cÌÄ?ÆM=×à2”%þ#v‰©lÞZ,Hµ�¢*s�$f{Ÿ*ß(pµùìꯃŠò¸!‹¾ë/ÿ!Õçm�§ša� p¸ZšGcݶÅÑ�*µóY†•Æd=(Ýûºæì)˜çäXe`“£Ø ùA Ä€ßr¸m34,ãÇ24з÷•ÕÊøZ¯’[Ö <ƒ×}ŸŽ‹ŸL]ƒ¥ý„]öy·2‚È�%jÞZ­/Îghõ=ü)c1ðI™›Žz§:.Uå…~sø~_®·ˆ HpXÓ àÖ_.�ALUZYüD}`]G¥¹:÷“TÇU¢MÞ·½Ùþ‘}Á·˜ÿ”ˆƒ^€b©6°°á��/®2q­Šü`ŒéèAòE‚¸›ƒ¸¿.YDÙkÃc ^Á·©¹8KF©²í¡a¨Öj…Ty�s2Ï ’¹tïýø­bç¡Xuënøꂤ ä~¨º?%;<|¦ÈH\¢ÌÔ¸Ô¾ºCÙnfâè·’?H×?��¸,¼#éeÓÿØrs–Ë)Ȇ# 3ŸóñÖ sK;C�9Þõ¹0 eÁ˜oâ‹Ý$ðñ€ÖÖcn±6/à4F�ÕÖØðt²Š“ù¢ERº×î¤SŸ�Of»æô)%| û|)çíç ´ê¶n&Ù�TÉž}ÏmÑt˜»?þìø|FŒÞ·ÈÎ8ZÔÑ|H«‡g•6W#еH{½%é&Ñòšyÿ éV«7Í+WK1äVòä%«¸›‰i°>o‰çàœJÀÛ:˜kRBÇÄW¯íŸ¹Hóˆüîc{â_û…p‰àx’_ HE>È×ûH$Ä~~+³F€‘ FVŒàoaøíykyω\Ë]¶ômB_èò€ @Ÿý1Gq�Gúç�Š4uI ¶ûÎy\7=ÿ,‰ ëÝ�)O_'Êùg[Dü$Äál‰2ÍŒý’œ„!×ézN¼ÝKMæåÅ>ž)ö\µth4i*x|ǣУùxe¶¦ž›2ùóy¨`³-þ ê®(±Ö> ùcëj òßÒÝäÀ*þºŠäF2s!IŠxüÔhÒŒôú³õÞkˆ{¶{ÝD6y ‰WOÐÒ\rKï öÒ íSURHÇ>çôÑb›�aOiM¯rÿ.ºuU}œÍìÌi9›ÿ åX^“¡$¸kkÞôô°µIv–ÓöŸÆ’n¿6Èþù{,~Ê6 )ê‘åè2å\‚¡Çó‘Ñ ˜ˆ*º~·ÇÙôÓ9ôN_µ#[�ùï‰z}¹»^• È`‡Õíí÷„erùªt-4µ¡©| 8„‘â”^)¸ÃÒß‹<:6€vB^ëŸÔ¯T:æWSE¬-FJN”üù¶•ÃB ¤MAÍ Ž¯US�=k¶Ä‘ª¨‡Pg² !!Sr" ûÒ'ÑÕjÃí¤V›ƒiÚ®¡�–…›b)µwšÔCðÓ€y¢—�êçï…Ù šÊ^ùý2Çúb¾ëìñfÞ·þgYšÛ5ž˜û? ./ñ´_-Ê¢íð3Ú ×Î0™g|�vȘÕ)ˆá�]îO³Æ%Ì#Ù~6b±%<@ÂE„Rdý.ŸiSÏÕ¾^GØ—´­r�L–³)ˆ–„7x^¾R”Rä‡JÐ]¸yþƒAËöi%®Ó|á¬ÁÆèIa)æ/~ˆ‡Á¼¨UóËO�ýrA‚á ÷lˆ´¿{�$‹PÁ\‡N…+wX­¢A‚Õ=O¢±&r³Ó×M(Þßàp>»yÊ»¸Ò¿£Þîž|Ÿf¼y‚BÔ¨í{b<>|ãÿ ÀAçÎ+ŠÍ~X¿Á`®†"É¡»IVIºí#HÆy OcÅò—Q†y“ß—B5\”²Î^­Ñ©¯§ Û²ß-›0”sauœ…K¼«¥~”™NË,2•Ì­ßB‘YR ÕˆÅSCT›¯N‡Z¶>Å:Yç´Ûî.8&ì¯(�ž¥{>| A¿IÍ4©šôÞ ÕÙ2z·¬p�šÜ’°„’=)‘V}€ŸÚ‰ô›_Ò¯ŒÒzü™·}oÖ§dZœëæÉqâIUòî†>uù¡JMù�¹RÇ•–Û@÷“[�ÊÌUšâDφÉ/šâ:€�Èåzdã;t�-Û¤„ß}k©iV³j†9qÚ< ›øH ©9ô5Ñ^•¾AÈX.‰Ï„7aU‘êâ#«oãóä»SxXÙ ù�?€puÀ=Dr²¦ky´Ã['ñaß�¦ÐuÕ‘¶=.B¸:ÿ{Ûê¨'DXM(>àÿìHxš�z3ó2êcZ|€Q6 cU39К=¢L¯6�(\g-¡]já!GÛ“d½A0mï|ÖV:ÿýè$ÆvÌÆ1ŽFÙN›YêYyá.ζä(p `2ç¾ ‘”Îü3!N½ C-EõšºÎq´\@ s?10ˆ)L÷ÔˆÅÃ.Üì¢þõãë\7ñ9(uYg�c¬)ï³8|ˆ'KEî0Þbúü½DÑ•`¥Xç3Ì™mÑ–l�ïÒ!s;�Ù¶�ǯ٨ʟ«É&åb*�"Y°QE‡×y# ¹îAb3È�ñÓå ”R’ÕÍÛ zž.�}4Q…ÎþéBúBAúz5w†P‹%¢,<³êâѪ"ðdlÜ“gç)�d�)Òÿ0"Ðíþö}úþjûܲLs‡®ZVyñœ>YàÀV5‡¿ Ü›oG ƒ8ýœal9„-5ìþÞ´¾ˆ+ɰ�¬ži™É%‚=¨�]Ú=>mÎNÚD ÒG\ñ–]ið¸V9ͧÁzŒë}üX‚ÿS;VVvÓQGý‡ý"Ç=ÝûñÜ*`ç‰lÛÏøiáÛG¿^†ÃÃs²èÕE¥¼#yz>.7=o©ì>:û˜¥$â¹Lßós·)-–zŸÿeàÏæ9Æ[à%6pZIDÀ¹™Pg@}Ø�—ðç-Œá”×ÓFœ­¶yȇÀ’¬fs¦¡enakêï9m[Àê[&¾7 äd²_˜¼ Ò(i ·ÌÕx¿€@�>mâ;ù‘¿»‚ÉG¤ÄqesÆØÏô?DÞlÂwè'D$¡÷ÝÑZ˜‘§üŒ L;€Õ8ý�kw½UË\*‡ûLcƒ…³I Ø/jéïŠô<÷†²ŠPÕ×—´]}3’2÷-IÈuk» ~ ¬úƒ±ýWLÆv"áÔ´rôÀˆÄ‘V:�ùãG’ï¶žÛóÄ'�A0Œ�%�‚cgðsÞœ-¨ÂÇ~Ì”h­ÓÉzÕÊÓÑtÔRüÓÉ©»™ÙIä{@ÂC>#ÅfØ9þ¦ZRouoðîÏì;;ö<ätHÆ÷€¶\6ÝjÁ¬PƒjŠ“(ª.Uço#™Õ$e¦h­F �»U äŸóBoS°aêaž±Ü ÍûÈ…j-Þ½g–6у6H·¢ü»�Öצ�·cD�äs~bë³á0餥1-¤!‘·�g¾6˜ÄDuÒkAš—ÒwÙlfÖÐW§'œEÃë >ùšºõ?ƒrE|ãK:r­I×–çSͺ¹’©‹Û59-[½ßm 2c ÿp Ñ)¯0>¤-Äi1¬Æšwýß�p%HÓH[â@Údz—v 8[•®ï 'Rn³éMóY º¹Y>r¶½É>«Ö'Ôþ¬ZNIJhLPs ÅÔSú…Qu¹SQ®=MCF@€—àäH� ‡âÓñBì�R˵ÃDV²{nzZsJJ·ˆ˜oê IÚbóªrVz‘�…x„åv™ý®8´LŠVo:?ü))bþªM¹®FGq£«ù`MâdÓ·�Ęå)²ö¶Wº™9nˆ_öz¦úLÔÇÜ[Ú€°ÚŽ«°ü�ÿÛŠî㮂¿ÜÑ ‚´ï@q³qyyºþ=:­�–ÛÞLÖ;§†Ý&¾´Õ =[%Œ SgoÆ�xp«îŽl¼ß„lˆ/ ,6Xšî?Ç�`aÞ^ݼÀITéT¹áî-Vý3`Ó4ÄÒy%»X¾ L±–M} !…a½‹>¦•3 Ω¨A` |Ø¿–ÏÍqã/pÛ•(„y*˜­�Š­�[pûú›¹,š†ê:¯®Äÿ† ‰Û@‹¦2O (Ù$&{âC-TŸƒ]”@1\ôE”ãc1âHèû!gBîxµ%­›némïÁªfê2²Áb U5ÿá ®¸a/»úœ9†fžÎÿå¶Þ¶Ï‘Ï4¯ùKﺊÓNÎ2¬XDKu\®M%îø”jæ@’ÔÁPÐ6 HtN8–½ÓvóßLJÒôÐw\CH$äw¨û*¦L}¿¬(Ïã,êSƒíâ$"�ãëøž'šQçÎWö½¬<ä·+_²‹j©©£/7øyÜëÓÄÖió/ኣ–B€S,�“öºV­›m�ø¤·}ˆž¾uÊüþ9(}’1 Öœjò×wé6&mùœŠè–™Ų̂‚C–ýÄpr—ÌïÊQYøò¬mÂ+(Zr xœIÑÁ§‰1MšÚãöëPøAik-— Àrc+˜ËµJv Jà ý>¯ÀÁ•L�û­e‘}W÷qc<4N‰F…\Ë=ëg-¯Àoo^ðÊ“õå%Ú­â¿€¼ÙgWÍŽÍô»m/¹[Ä $2‡€v8Âo`ª:è~û~–a^ïØ‡oC�mäïj�~þ>xœÒ¨Þµçnaí».ƒù6eu;Ó̤5E:?~½ð7ðè,ˆt9šƒiÆ„/ÇZ‚³G.h•¦*­)¯‚£ì³5«…maîÑ�Ÿ ƒ®ˆÑ…»$^p¥œ©¶Ú�oCmÚûù­Ók=ïÖŠJkÖ� U*ÇÝûP»€ØËÆ}asÏ-ËŽ'ì�y¯ôIP¢I"—AGëvü…¨z Ï›r] Gow÷ö–×/H{Sù²€ß™ÍWº’˜ÌÆt_e¼ 'š‰;Ó� C«+µ1æA¶˜T½;9Óçÿªãȱ¶ÿq—ËÀm‡…}Óë/Ýа'¤¨h8o´;Z4�ëJž^Q»$ÿ(¹k³Se¾+‚X“­WG[·�ßxÿ(}0nŒÎe�ÀÃÇc|Ó7òºê5@C¨pz\ïÚ¡Ì®€u‚ké¬c:÷ýºìüx.”7¾/7^ÝÑ*÷âZ²‡õxœãFªÙ¤MâV«ióú£FnIhôq¡™Md¹`*¦vnå­—”§±ÿreBÀüh51Á�t.otǃÞZ ŽNÎL°søE ±�œiÏ Šœ&]sk•`LsOûžˆÍA„þg@¯‚¾ o`uµqjÏŽ›Óᄆ*ÿ(të,úb{bƒºœvQ~J—à½Û£ÔŒ­p§Þ—î0^Ûé­hM¹s—”›e è­�8¿`=Á¸>L•>rmÛ:í_"³ ]Ó4,ý+ZÁì„X˜‘@ ù6º~ïoÅŠ �å±Ö'8%ÒgfH‡>bU—`å`µë*-†Gë)›Ï>YºÍóJv3ïEÈÜ–Ý}ñǚ©~!0¨­"ݾK2ôžbcù*X©QÈ#ÔÓMKBs¨÷”[R}F£wß”üu¼Á<ß~FŸLq�@¥ ª6ç\g)$ñ%ÌI�ü Ñ_�î’øe.„¦¯§Œüv«HáãKŸvW>òØL’—¿ù Ûgw8ß‘¬²cC­‰ûQ KÂSˆï]ú­¶ŒHͦ?ƒ‹ðžb�Õó}j.¦úö4Jèᮽ]Af¹Ýðàõ,µLoSWéðÎhÇgW¢M›JÍCD0Üåm’+ßaó§I0¿ ‹‰Ï}´þøÊø×–\4¹¯Þóô´¨bâÁ¼¡OõÞèáJÔ»N ~¹kòB¨\Z°÷Å«ÖAÐ/¬öˆa$j}àW¼£ Ó+a9•éVïz¡5Ùós´Z…H dT ù˽ ^îñÈåcåyÄ3óï Oï(Åâj-wƒ™©Ñ—óÁ±KCÇKåúí]”Y²C'L Ðgž·kF»§‚ÚŠÏʤGu Îq¥A& ÌJu½^?tÝw‹êΫv¿væ,}#¸åWËÐëo «¡ÈS\¯¼ý)½¡³“#yÒâq2éSEïÄœÅÆÔMRqÏæˆþ�|!$¬­>˜|DÍÓµ—o„1à'[åUzoü�Àdv×26@ åúêÍ…³Pò`R€y \‚Ĥþ‡ÜҢᮣe¬Ù‡`XX§_;âµ@9¯§&6]Ö°�ªéÏ*v²&;Ïÿa¯# Š¿IôÕiòwŽnù^Ú�W¤ñ @‹oüSÿæFy£â †”5«Ï€v?W„¿›¿æ�“×ìú�¢P‰o}– '£yÆ€ÍiÞ0 �*oÀMn*¦‡ð… ä} �»e¤·ÄÇMvZ³46šÛû6+} ´¬ @èѵšg˜A¨Qý3ˆx“c]e¹¥¼‰ãÆ €äô~ÊÚ«ûÝéD%Ââþ‡Re“­'ņŽÒë€k�qŽ}<¿£éM†¿óij¯\ìíY'^>µ"<ë!M—Kì%¤§Öoâ´‘�L‡ðö³HŒ�mC!V»_(SÓ.‚P· žF¯I­¼Üo0x:ˆX�÷ÅŸ,¸U˜€k¸ZÒ;ÿά•œs¡u¿L�÷ú©ši˜•‹m7ÈZò»ÞÎį|E¨1`û+dPÖþ¦:{uÉÎf&úô¶iP î9>Úi¦Tâvs2;™iÎ?´>äa¨±ŽÃ‹(€õÛ€ W×Ý.2NØ—÷,=£ðêÛò[aÕëc\�€úo~dÀ˜9–Ëpb,…ŽD�n¡‡}‡(¦Þ T(Xæ§ë¨ãŸÛ¬ËS·-Otjv«ã?RŽ»:§ƒ<}É„,¨‹ÒU5Å/�wôjEzÙ]_X{Æœôøg}aßÄs6ƒî@ª‚ Ò9f¨KÄ�7w‹ê'ÌèËEqj{;ÊAß_”’ÎT�Ó/#OŽƒ&hÙ;mýô]G5©ï€b«°„¥žEÊ �� H¨Ú0‘W�½úÕáçp’Ôž«ê­]°ž ìdp`*å‚àf¹[~·•‹6ì¼^z ì¿ãö®=*É$üº~9ìeSµ£¬ç7�»}½rô«ÅÏÇ.YéÎe0¯ÔU¿æƒg¢ýÍþf÷%J-Jà!-Å�×'wz ÊuÍ|u¬‚&èv•¡Õ´óJ^íJ…¥ -{BUb¯ üˆz„µBA® ÒÓ>Ùò‚VkÊÜ€¯lºF(,ì ®ÓW6g<'?êM©2©†æVø¶ãh'H4+×I}¯Çµ–Á·ä(n»ö•o·¿,΄ԯ38'Jýˆ£‡êîÎKrÈÒén4%Š—¿M¶€tö‡ñ¯¯Â( �c¼RcýúîÏ÷¤€ºÑ€aX¬_‚ªWY»wX­ÕmqÔdÁ¡Èw÷Î}!?WQ¾©8…l¤« ,myÏoUaå1)¶ü£SEóð+“.µF>C4Ï«#ñ‘Ãò�¤56|‘7 Y74Ø¿âÍXüvC}ò¾Û©Ü~IJŒ‡ƒ‹ �SD½°?`/ Ê²Ü   vuN»6½ŠÝޣѓÌQs>Õ„‡Ža,«ß:!X|ö¢ —;†‘‘Ø¿¯‹¤íÊãE<¿¢\˜\=�Åz5Ä¿)¡mc:€ä]j)b@°²L-%R ¬¸`×7¿WÁd—DÁ›e=³§cZ» éRDœƒÓcn„*o›�d=¦'`^º]‰õôF¯1~SzO‚(aì ÝäBÑCïngÙ–H Øãðpj?jº‡; O£ŸúÓpR÷:2¯xÙÀ±Idð«ÃGpûùà¯(®‹»º8‘ž1ÂÁr³ÌÏJyS7^ÏÚ}µ–p%1%øwEÙejä£Oz>](ˆO Ô¸Òjøú­ .lJÃÃï€hÛjÄêå"éRÂp{ë~²¥ï.Ež~×ì¨JìqŒV¨ˆFçÛÛÙ[ŠH øi•ô°c…éÑO‡@gJýTJÂ{Ê;žÀf2®7›(ýêÅK€ï%‰Í«óÄ(øžõRÔÝtG"D»¨Úð®Qث윰P¥±¾>yQð*IΛË8ŸÌ—ã‚~�µ— .9Ä"jM:®ïáÅnó‰r£¹Å®ýöÈ!¬»emb[0ŸHÎô&´Zë~²ž[ÏCh­§�B飊Ke�:B›Ü,«ð!wp»Å³ã¿Ð»²v«�šd½{÷z!#ǧfG®¡U:=»^äOwäzWÀTn’nðç†î’lô‡{ê3@ Û»µóûZœ†­‡ì½Ø3¼©F•Ô÷ŽO�N¥ ´ \ orAÂ¥é½kÃÑŸ*ZP€œA"}±2èòJä] `y|Kcm¨‰ÂnC”ÿ¸Þ(®üX–êK0óÃñËY$`36öá²ÏO®¨×cFÌ1aÒRQ•&ާ$6Âùäÿ–)ú†õÐñ4­uä`~´ü|ÌPºzâ=:æ:Mß TÝc;6iåzDD3¡x®˜‡öÃ.ÌbÏŒ2ýÙø —ßÌ<çó®R‹%â¬?›Ó½A›Ü R@žöýkzlWϼÏ\oDŸÚÀ/^`šæ�%þÍk¡w¨ƒ½Äûãñ� �`n|�¤­1C|âqKh^0–êºí´Oæ/q!�ð4Y&õp]½ßp‰@ÇY[;rèHÂίfÀ«ŒâöTÚ9#�€Ï}. ;û‚ŠÎ J$<íáÙvÒ7ö—¸·SCÖK4{‘Ð|³þºõXwy©�+®ö§Çð4jNe… óT Wn9éÓø•¾Ñ(<Õ]…w±Ô%¸OÎË�¨ Ç5zC·»a9‰Êü²½ÝHÁúYöÅñž1·‚8 áTô'Ʀ67C½¯ÇÃ>Ù.l:„�/‰÷ÈlPÁµç\мˆàÊšºJ½jüšŽ;+­ÑQKðÍñ~—ØÝÈŒÆá/ñ¤Rÿʪ°WÓU�„;ÒËöÒTäß•˜X©»›|X,QA­'™Eô�Ãã ®áÍŽG9®­k}®v�ÚÑæô¼kÚ¯ú¿8ê&9c{R¹O(í¾ò ­ØAÝ=¨‚£ˆ@e%4bèÂF/:n(n¹&-N^ÞTb“á’ˆ1yƨPÄ¥õ˧rô…¸LïßæÏ»°7ön+\ÑìdzîD<0âîË‹®Ë»µöjþ>#{«n©¾ûŒÌê«MÊÿ´R§C6û9÷ŸAbP4ÏFÙ¶Ô'(L5|ÈËÈÝÈ s>¯î1&N'<°áWÓC~�gV̧³ã‹‰/ß!„ßÜ}NŸðf•¼�íûŽ"FˆtxŸ™³g?§¯‹Ðàø¶ÄÑG^çµþNs­ø„h4åÓ¤ÃknÀFn¨äŽ¥%G@F Ç!à£hêr5{æ-ܬõ÷š¦§2WE1³a�7‹Ÿn;ìâqjš1z$ÃDC–¶L=i¿ˆAû8Y¥’0ó¦OÂÙc¢ŠŸÚÝ !?’9,¥Š€l}ïÀOû• Ù5˜‘è ºœ–€’LS‹\Fn>C_>sá¾ùÿ:K°,Ò^Ý-}ÏÚ:M,'nꮓ¾­ƒÂ1çòȹɎ¶ÃQJekâH/ÙØ³òey�ü%@Í A‡Iü¹Å?cÈk®x@îW]¡S‡¶‰çËÕw‰Ø¬GêÒ¯'Á�ç9(v–a6ŸMi±‚ùÞ$ÚBþÔpÿsÊ@±€û{mp7æÝósÈÀ8ñ*¤sš¯(’¿¤Ÿ�O•g –NÙÃÿu ¢>Œ’$ŒCB­%t2±eRóÃÏ‚ÂÛ†ÆW`p «†»4wêI¢ÿefðZáa:]—‘©³¾—±¤0À±twÎôœ.E…€}ûm¬mÙ³pâq–›¤ø±é¸W6ŒÛ“04 鈲Ð1\Éiç �ãïpQZ9ý”}šœ�þÖ¥‘öíöüù2kOGDÐÚ·¢4»Î Ûk¡ò»íã«]ÁAæma™ÜZnG¹Ô.ì©¶ 2ž;¿¤º)âô➆þ¡&_fW­ Bcæð~˜š@‰+Øm½õƒ&»ˆêxqÐí‡�klRòg�@á'%ŠqI©îÔrÓ§J`˜ú,ÕÔ#�Ðñù«¢µÐ…# ˆÂ壮œ(çËžÑÓÀŸNqxU“$²_„�þë°jGÈì&¶bî§G/ß"ŸŸÊ] Ñ¢Þ]íöê¿lƒm ¼z:ý_ämo`äâ"ì4›e<4\k¹ìmó�ÌmúïLD4NlŠjß\‰E@–à‡VÆ=ëN|Ï)!öµwX)t‚2=áõÛ7OS½ž Ó>¨m¼ iYº\¯+’ìÖé4ãÃ7‘?='ûžsºÿnÎÜäJÿÞ—½í}?÷}<ºZö>…§Ã–ßè)sôSbÂÂ>ðWÀrÌcƒø}ÛbÂ&“|UpãÂJ|pC¨}L‘Ÿ/ ÷ÕÕr–Æ"…P¾FÓQéNõp\8¹úÇ~Þ ð%ËÃCõ‚æÝ%ç9Æó€Dª¨àšÍcîØjÿ&Ö<„:žPÝŸ¦jÀù�äÜJñ„Ž#Ç(ÇcxÓ^àå‰ìS(Îq^ÝB ïS%çÜ¥Tên˜á$ñ+Ž*Å;IÏ—§vm|öÓ³°—qÉûGÖØ ÿ�_êSðŠƒ›Þ P"#óՆȦѧ»Ý*t Ç8©ŒNìjAM<lOú —cÎM¯;`üW RÏÑIÈ·�C挻5Ëye¡�hNƒpù Tm]ÜCv¦#8™ЫU ~0*ßžÎûúL õ-1™�L ”è%O]+¶ç�KónâWW,ëyy^Æ*ª-ÎÅáWâKß\!Ê(r]ÕØ‰\Þ/Οx�Ü) âË¥Í{¸N@ióÔªWÄ=®JƒÒ†#©}ëBÒnzçÈrBôŒ=ó¦Õ}$¹»u‹;-ª­‡}PLókjöe›Ê¸s¤?þøSê´�Ž ƒ}þ;?üüÁ§a¿yÃmâ{Õ‡›{9Ù¾†*Ü%¢æŠ$Šë¡X£æÎÎRW­l‘ý�Ùóî­±¥yÄ—ù� v˜¿~c‹ùWÁê 5­,•ý÷Hkܪ“‰pæ:ßc»DׇïwÙÍ'×^" " Vb"’R%æÓœç³¬oÚ^ªçºsùÁgRÐ11…lOQ'3^ÿâirô^ŒM@{vñ¿ý&YÒ¯êô.÷,oμåt»íÌ^w‹Î~ˆFB:”sGnÂGkžÞaÇt¤¢è04lú ©¬ÕK§üÀ¿3(§U4„­n( ¢&dñt] ]B<æîÇ}ÃA¸ZÝ�Ñ·òüD~žWéCÒ]Òç÷2â(LNذÑó…"7¦SÜ!^ôo°ôMïnjGȤûÁ×;O]¢|”äÙyûbÞì<Ñì9Dš‡{-ÜRíP·´Ž#'—�;öÏèâĤzAhz(ä€Bè0Ç+³‘\Þ¾‹�àØ°º¿å4DlŒ|MEˑӀϡÏHòÅòÏTƒÀ·í[6˜±ì—ÿ\–ñ8Èí�w¼ !}B¯c$¯ËÚÕE}R –]ï¶Pª:nຩ©â'-71V©¦’‚RÖpî/ÒVKhh‰Ü³€òOöy®Üx}¦\,{Ý—2½í÷«ɾ…õŸ%3YÕ(ÄqÅG£4mÍéänØÁö|VÝñíÈ‹yèdCßtã7îÇ ÜˆÁ/n£,ñÌ…Ë#5…¤Ûéë]²ïRÜÄ{~˜QØteóì\AÞçÙ¨Çn×av Ne6a¼Ns,ÿf˜BÍâlT<*èa.¬õÐ*ùm”û a½qÁÒ›øäÊm�—¡,}´óè4hç¿Ð"tVÔ ›3m»ñp7WdûŠeÚ! TCGµtúL øŽô¡FÜ�]«‘vW@ù÷/ØÛ­m©câá奈m>•Èäîã”qâÙþ3�‰:y7ö¹â» ßÑsWgÛÞ—47>]!Â2À �kóÕàZì§r½«Ç3Ö![À[;«£ ¥8Ñ+Èô¢øé=½°Ïzj‹Øáá:D‹ùl T¹»N«½Šèi|Z/h#íyã Ë(€4wæ…wèíTE~vÜγKˆÜ¢¬œÇü P‚þ´•êì�¥¯ Rvú÷Ô”»¹iÖ4:ÑY¹¦þDúÕÛoÊI¡*`°ExöÝ$â VÛ:¶¼ybÏ.±=b(ToÕ~€“’EÐë5É/I SÇä�z‡)7†Ä³ú,ãwzÖ­Òñ¢½rsCU/\Æ hf4‰/\aÀöJºbÒûñb?à?‹ixLn9OÓá‡@ØåkÖæ³=ã•Yëpƒ3áUñÕøP�K…)Ô¶¯}5�&žiKÄOD™ÏŒ5]Aq}‹?Ómo},¿›w,ÿÜYȼk%i_²‡ˆ‡ß`Ž� �™¦ b²îR³á«�N¯nQª’øxœWxAéÏMP�¶œôý�Úý2Ãë…„c"Ñ'ˆüÌíeUaà•UÍjW†+,੯¹ôØì±ÔJö–é)p¦LË¡¡÷sÖà ¾R�pÏmš‹8}×ÅÏŸ]¬y¤•EÿVKåÚjüb&Cæþ2víçªîÃS“Âjº(“§�.ã>>KRÖ0 ?�Rìê ÆÆà ãƒ4´YòÔ,ɨe9E50ÉÞ©:1¿Óv²°à©¬‘ÞÒsP‚Øäפ›·üUêºß틾Íâ7kg+–ÒÚÆ¸• =ƒø†Ó'Ñ7õkk5Ógt÷ ÷uÈýaCH˜%Mì1jãçðB OŠ ,‘¥ Ä™Ñ~¹¡¸[w¢YÕyäÐ�0}µfŽAN�¬n[²'SïÃdœ‹´§·CêÕ¼?±žò$ OÛ,­×PþÖx³3×÷¬Z8“jX=ÃU~£ÒIU°Í°y¯ß ûÓ£“å|¥ÒˆpÄòuµ„¸>Ëžv³r0Î=®3¤ìÒaþaz¿F9w[·�µûã÷ÔQSÝËø3rŸcr>XØ-´"çª+vÈp{ä(æWÅ®EË›òs>»‚˯qÈR„A¹Ø”¯¤¾M´6Ìo3pj±û�¯èòC¦‘‰�\-•‚Þ6sç1e†Áö�>•%´6$bÔXÞµÕuŽ:JIDÿz8Š[?L1zݼsuGà9óß4§3 ‘?)w¤é ²�{>>sSr©|+K¯¯>£VlK[õoá%WbÉ!æœ ‚iù}�÷ê�R‚(’5my%¥}÷+ú�@O®¯¡‡§½Aå‡=³¬?¶Èõ^t&lô“'EõÑó¡Ò•iî¦%/…+i\MT÷ÁS!£'Á•„›ž¨üå‹b\þ:+Uw-þ•�À‡¸ë3gàÄ_è9<â Ç!prX×ÄùˆÜ¢:r º§ëº fÁŽƒªøW•âÚ\Ñkr¶4Q,L�¿Âþ+gÃìO±|<Ó¾úœ;9Îôk­72@z°é©Ì=)¼ñûX?2Ú7à•p»àVrÄÈ‚¦K,(§N„ìuŠDž™ÁµlýÓº$jýd3Å]�"ô©uüáó˪]¸sÝ�¹Dc{IÝ+v­àzí¯}È>Rì]¥¾ÁrcëïG�ÿ N’Ò®æƒD×Uò¦Ÿ\§U¤ù|ŠØ>Çž�âßËž½uàYŒÒ0íeê{æAm§Üt5wÄà‚”ƒ §Bê øÃGɱeUÛüLš¢3�9‚‹¿VÓ²ÚÎÛÙ'TZ ÌÿxA qá%UPPb#q6§| D÷Þ“UZ6¥v)¥pó§Uqw� �“?yýÒú 9x²Î¦0•;öJVá\Œ“bC§MP}WŒÀû$¿ûDñ|ßëïo¼�üµ.|2çß;[-²¿¹#´›ú‰Ë–t.=•Ç䑹%Òê…9@åJâ^,æíA‚À(_ñ•(Í?·rÊ®w�( §Ö‹À5Púfu÷ÞP޸뫢¹ ¼úX¢nd;׿�çE­—îûu””—ðû'»]i9d6„ÔÊ-$%©‹ÿ¨ÿ”ê1-ò·;,E»aU•bvñP™Ž_¶aŽ[8ìZqãwÞ�Ë7Pðé~xT©•$Z~·/¹¥ú$§� [O7_2cØX¢p6¢TÈ㻌o¤_ø8‚@Ï”®¹þÁ0xT×/ãûPѦh'/hˆIý[™†Ònû õégïF\þ�îÝW§©»–ý¦­'52GUÕÁê*V¿—„-j1‚ŸvˆKª o©cõ©ŠìG½¤`ŠÏ+´o4n‰ü6eÌHpØWPú“Æ0a˜obGz=EV·Alæ2“Uˆë”¥r-¶ÝÚ]h«ÂfµÂ¢øN‡?¯sÀŽŸº>^Vç÷„½¸¶#8åOî„æq‚ÍG‰®hQãÒ]LN=Óms0^¶Â?È{Ô„âVWI텃͉®ð‡h†%}€T%ª¹‡r4jòßIDÎå�=ˆøº¡Zé‘S?aWóX±+Lk’|ôT^'Û•ÊϳO¤–/ö!©ƒF *™07Ž{yŸï:dià— �½ÈJDÕæO‡£ÅZ[�õèb~¦qWÀ>¦g‰êv,œ4¤Ì)©P„ Ëi¹YRäSO¿à–£DþžÎ@t†oìù}óí�èÃ-ž(öl0TÁÞl >*uÍфΚí ïSÜàŒ®øÃÁÅÏ„�·Í’â´€fá^ /PjÚgn¯ý5ëûï+î‘ÃÌ–IŸ$&�ùýÂã´<\šÁ¡?¡Q¥øNÊèvœU§×mŠÜ'—VeæùžÎ%nÕŽ ×È|–<#?ÿåö@½w=ÌÞ t@< (ëJFÒÀåg•2ZH<•w3£ÃPŽåZ×3üA.+�´›bZdRSªA�öÏÕ—hÝÁÚâÆ†÷‡È9мJR:ÔÇ€;¾S0WWwÕ~¨�|Þo›pý²¤ì¯ð¡ô ð�ó„âàj ýÌ òûñÛ>7ñ¯ÖÍ$’“oX=ß÷æ'û;}Ê-¨ÝÌoû©Ê<9ÂSç­ý‰+%«ÍÍW‚ôûW6r†nâA“‰£BÈ…bÄŠÊ`O€§PÁ–?ŸÆV¿Ž9uIö_㣅n„‹Â…‚nHåõkzÁèè ¯q²lkkêZÑŒ+LÖM=ìͲ?4štIî`ûÀ÷©æÅÆr †'�»¶ëãtg]V#�ÕqÖOõ�XQ°ÌŒÝ¡)Ž©' FúÍb²!™fŽEv´ˆ8¥7§_a Çw>Å;žLðHP½û /Åø¨ †)G3pÿ¼ˆöÁ¾µPæîEE�CÓ¶OËØwË¿Y¯¤Ò@Ò¢žÔ�«ó¥"Ÿ�uÏ¡ñcI‹éÕ;º ë;ùxy®Ó4ŽÒvÑÃ×Mrë΋•ÇO Q€«cï‡m9ôƒ=—"‚¶kä'jè›.ȱ�Î{û!7ˆ›º’¥%¯¼y¥2c”®6ƒ®P_vù}‹HŒ³Î€3tÒVyµX'„{ÆøNË}¬9‚·-õŽaÛ‘5 æ·oƒcÙáÛ–ÈO¢DøÂ-ÝžZíÁÕÒìO‹ð‹î�ÇU/;ùÜoÔsUñ†•ÎHÜùæá¿‹Ù(œ£HÀˆµ‘0ü幌éI^@ºÈBÞÝSdã¾wŃ6Þ ×g=Ñ¥¼£EõàCÖ&ì-zÿ’½ÿv ²3þ):±.T @ãœëÆŸ®ýò½ÅO±¾Áâ ç£);ÖrÖô[%Ù™ËÁÚS»Ý•bû#°l>¼ÃÎ�ïôWÃ!ï^{R¤ïd›_oa]QBü 4ɨþü0 ¨8ò±¨*‹`#üQÜÍ Wb?ˆ„JšÅHÀm¬ô J�p÷Í6ÁAT3C'nEÜßåLâ¿öíADGdçØ9Wsá­÷Þ"Z¢¼¦}tG£�´ÜjÌQ³QÍàÚhR*_|@d'_Åë©ÕDÖгëç²__>DÅ9ÏÿH÷‚£ß=A"úæZ¸HƒÀ4Óc”8¼¹p¨Ó¦øÁnÀÔî¾t.Y Kµºâ‡32h›ÁþåÒ[¸ÂÔÖS½qXK�à§„ÿ ·V$6Ò”vœ>;ê%r¤†:_Ž€Ï¶£|íᱎ_ü�Ø»áM@´c¿Lç³·8ù�—>ýu:ü%zç.ž4Ûãñ“˜³\Ü2÷O}W†�OLKÖÖ9Jº[)«ó{ ��Øc?_�¼±îáÆ'îÁu¦R$ì•€·ÒçØãd�$fQ’ºº9ôËü�Áö‹Šü=d°×µËðj)¾ŸŽ;O€nAbXŠAE ßz^at;Jt½§yRâ–~u³ÍK"ÑŒÛcèókF�ä\—5°1‚þV/$ˆì<9ª¼}pô}éœ;!vªµÁ ‘ªû›yúØ eÙ‰C—!M¸ñHf%.S åÓ¿£ÈìUœf¿,wÝÏYùEz,énÔþ Ê‘TÙÇ]ÿ‘ŒJ ”­5lRâ¥Ç÷ɰ–J>o†£Œ2¢Š×�…“+·z3ŽXêά^€K�RT™’}Xãû4ÎHçúƒþɹ×Åv4i΃¸aK–qo+~ÿ÷#Ĩõ­•†JQÙ»?,BеAÆ 8�`HÏ\ ’Óbµ¼r­ö÷£dDêM .ä ÅHÒC0öÕ‘¶·ÇÙ›ÍħQÅQ@ý%ÚN0¼Sò?¯kUä|”ôûÏÈʨÿHHyž}ÙÂŽlšt¿Û*¾O©R1…wyÄ$àõ5�¼aH –ˆ@¿BÝûºž£Oq½¤ˆ–'.Ôã¯K@©€{�0CóƒX0çá·Á¼^Díív6<̽ô‹õö Xi”oûäŒÕ.ißêì& ¥I¸‘Èý?ëbÞ-Û-ŠÓèÇê¦_ŠT9÷ê ÓÒM<$!v xx®Ã!›£Ó·~&5«ÿ—ZÓt92b§"A‰üá 7:`<ÄG#…‘wîZü>Ï Àïv‡Î!ñ(Ý|»_+ÛÛy&}³Ö#ÆïKò߀Á—îgOV5•k‚Üõüíß"òý[AV›Üܶb™( ôxJwép£¢sE̲üÀã#-ïb¡•TÛ“vY–-sB^ &Šú^}äÁWŒ­â„fA'“ÑÔ=ªLöœ Œ¨1ÖXÂhJ7�$HxRáŽzqÑRׇ|™äôS„³e Ën km”È.ž¬ãm?|V�éw䥉9kª-;5?­xÂJ�í#½ƒ“xq°Ò÷×ÃÔЃ&„¥˜.®#ü:”»þœœX“õQd¨—áp!å+’ù‚®³ŒiCŸž¶MÓ ‰"Èœ¤vW²O \’¶”té!=<°åüÛ³âÑü½œ$Yh*íîåmÆîL¯c.Ic¨Ô]¹àЩNû¥�F&)zr0áÃéîÔ¨$Ùñ5yý óÍ•j¼¼Ñ´ŒvD¾TøÑÍ 0Vÿ^�Zåëfˆcõ4©Í–¡ ŠÍHÝœÂIŒô1 øÏ€tÿ2ú³1OŸõ•›`èã¡6¿õ%¡ÚÒ—XÝ‚P¥ö«‚fÎ�´yˆˆñ¾uä¬ o¡žPkÅ)Pü„úôîöY`¾‰øÉ¨U€"Ý“nX¼/ êZã÷Ÿ·KÏYŽŽq6ÏUÐs¦jÄòñÀ1öF*ІMÒ¿s!ϲ`÷ÒhË1c­Ú3’¹N†ú¸æØ§Ã>õÙï"&Èx¤ïò:j‚9}1l�ø3k›îÐ_õÍ•’|·£Eé¢r$ô×›é`š-œš9ƒHI„¹0�ýU>ÔÆ84 )ê)ÑV8ð[]å•ÂÓÐ~+À~w�=�÷p^qF�]©¿évôâÞ±]y³ãØØ§1qËØÜÝåº=^!Ìí;IïŠêWE™º`‰3ÖÂãÿ„ŸÃÀ·¥h$ #àãÎØ¹%©ÙBÞ}3MADíz:.à"¼o1Ää&pSMU/Lm¯×Òk«–¿Ø�ÆH—ûhë<7à {d Úa2¢­.Ëä²iÊ/dÑpžmnršñdšwÏAt£Úêæ!ò±Fl¨®ýöd^W•�×Ôw±�¹Ø­��32+Ì«S—I¤´|yDõ¾¾¸‰qi½1‹ÝSÂF¥ ?L�š¶ Ç´/g4’¤ ÜÎX)�ºhò¼—$xNUuß@#2ã*݇¶„)ýKhjL«z«SŠì:YÛ Š­ù\9ˆS_¾×ï�%A­8cF�ù¸LuÜ­¾|D”)¨G±üm²Q¼M�:gn76ªÏŽ�Lcâ·z»”¸pc1ž¬2*:5ãô¢Ñûþqp¾ÜSÅ.9ÂBhÝI9wðë G3§òMˆg“êùM¿u4±ˆwÌ.Ð}«¾Ðz%û§îÃ+Q "– á–=ÎÛ¡‚|ìõq‘¸¦æ®ŽÜÚ»D³2ëu:q÷îslƒzy]üt“-R7ó1¥æ'>°"4›m«®¬2"ÓO…'"…Ige§_ïÀø«ÿ¼N#FCT SèmZì/ÚT<žŽU,ˆ¤¶Jœ F‚z_ÿÝ÷ŽG^Ô‚¾75º¿¼[Õ%yPÛÒz�?I~�Ì>L­lêé4œb]¬"ó�1ù5“Ü¿‚!ð¶Ì{Œw|”ù^k©¶¤ ÔIÚgæ×/çÐg¤¥ãÌê9œ££ÁÍr1)JSu¼›O6»=¥�9 )ð]!öÌsôì=þ„ù·Â½l´£ƒ@u¶cå*Ó½™Ij+ ?Ì™š¶ù2ÇEOç÷aÉí"Bõšûú­ÑZ˜ãÌ-Š}lݾô#áÊ_ê듊ÀWQîìÝ'Àa7†K~Çh«LpàáLM =‡y€–déøòâx›ê;¥?ÃN¥¶IËáÎLn.~o¨‰ümýCBØ:ýÁêÖÌ�652¨0heý׆…ûmçìGcë~Å›R2SÔÉ€€¶çC:ûŠuL:ªÀ»bV”ôk!b\Û5O“P vŽnésáR׳£ézñ¬‡Þ=Ý µ~¬¿_“#‘«Uí'ZQ¹Ë$|\0�{ ë™ôpº´ªÅÖd4ʹêkw óKÈ'k0‘\Á--¼)™9y1n(hÿ˜î1éWîú7n1€øP ã—&´|FëÙùÙQõiÌ ÍXöžíÜ ýOÉÔ Ø�lx¿EôbÔCG-–3Ñœ«£·ž€"u@JM(BBg,J?{ï”é¥-ÓQõÿ—sÈ©°L.Ø5Ï Lˆ¿o}R~NWi}YcŠVû;)OLù FR»€!Äy˜ÓÊÕן4çïC¿î=)Ûƒ²©é [½Iß8R«7ÆbGAþ¾?Wž� ­(¼ÕÏÈ„�$¹ ×4´ŽÓ™%#CF¬rcÛ?U¡º•ûód ¿Y1�rÀew¬0yx;…\p3þÄ•Á6#ö­,�"fÆ�z:©‰k?·ÀztRêʼnš;$ †OÀIêø7°”zxøPt.ç믖DmäÒ�µœâ¹ ð~’ékîÞ}©ï´TëJ'‡»·ï÷ºœzÒ�ÐRMÍŽWݽ?¹³a§Çô‰ ÿUJŠb8éü6ÑlZkq­–¿®¿,ù¦¢ÉzL—ÀA€Ò‹�v<¿„§„¥]÷.?3$ìÄOC-Ú�ö®T¡Ôýoìc×>ÍTƒe<�³ö7× Î¶….0 ¯ãoªœkë>>{p³òʦ•£Óž[ÿ9ZÊ*¼šDú]�v…q½dƒXYÙTûÒ#[ï[Çó^ó7n×»ˆ„ùl坯×Lq3ÎÎL¢Õï‡ì*{7Ó=ÿm uÕÌ»žhË€Ã}cå!|ï¦C¯ŠroǺ¾…"bû \oÁ‚ìþÆõ‰ök�«¬n5¦æWç)*ÍQ5Ë(›Ø£P¹¦‘^µûÏ=ûâºü†R×õ-£Ùžì/t]Á|Fì ýÍÕ_¯G�Ö„Q\|wàb�°'¦°½7ðc¾rZ¥ï¾x³1#S{Œ×IÎZŒÞŽ-gjxÑð÷­?t€üÐûBͰ'†èßñ.°W§YBÓ¿í£(Ö+ðƒ>¦dí7ƒˆV€ïÞîÐ…„ˆƒHqò·�qº> �ÙFÔpB½ÈéˆÃp¯‹@ÏŠoÖŽ „Œü¸ ;"½kˆ6¨XÖ`ßl�Ï�1H¿=ï$ ;·’ŠjxÆžÔøH½Ÿ¢)…¾’db-8õüºÄ~÷û̯̫ðäp·—K2@‹È©'ëÄKÍΔ³Í@>'ØËð_X÷ªß–—¯yâRòIÏo_DfJ˨wYÚ$äLù’.b†Ž=™Ql'{úì�B­xCFKÕ»Á2íÕí®ÔÙe‹õÇî•KcYr/ÒC‰N–k§0»Ë»[�Ÿ=OçîíUG¿³=Ñê# âyÓ¡¦Ÿt¶Ës¯uTæuèlQu³Æ•мÈ{~^O ZŲ[%}d€åG£Þ¹òˆ±Én6/~MîE1b8[†®w¿öãÈWHRb}Úè×ìëh›šÚÞ졌‡QF#Kêz¨@�º“˜€Å�‰Í¥›²jõM{/>oW}+ŽôœÇ¿ý³ýAœìj 5 xZ#™Ÿûu盃|õ8NŒk⌕±"Õ_À�Y=<^8 ¡‘+Z_Óä–†Eƒü ¿?xéé‘ZšfNß%�)üÆQÝAÃ’E,k¶#�u6E wÑèpu/vËÌð|�1>Ѭ.O*b•+ÏPz83½óe“âÑ~­iÇ}ó“®ì„kx0DZ¯°4’@¯Þï-ããQ7?ç OšH»/0E`˜ªËÚÉôxÚRI>™© 6:�{OŸ 1_ö½”ì[lò³‰â¢=WÆ•l€âc«ÏÄÈ @Ç1H§å×’=T+<À8ËîÃÌèÁI}¯ÖÎE§@Å¿Q÷} y¡MpQÀúe£RÚI›_vFÁKøµøMnªÚ{šgت¬5a½¨Q˜m¡âý”2Lðl½Öã¾Î¬|úgÛá{ çdâ⧾˟-$Ä£³GâÚé’ðÞ ÝäÚ-t}fÜZ�ÿî7ª…ÕU³îÉw2,5e2¶šò{m'NŽœ´OÄ£°¯ûƾp-ËNi¬âf,«ç∛ŸàÃ_܉”*>öïuªK'�8m‚š½ÇIýCØÍ,õíƒh�Ór8©ø)í#`sÙt�äÅÐ6‡þ³I é@šÕ…†ØJ Ãú¢æ”xí2šr½†ëý€æ�zn†p€;þ\â¯'ªemTØPøoôy‘�ÞGµã ˆ(_ñЋOf5†2*\æw,EÌ7(tÖï øb<gú™f�ÒnafH0X°‚¸�ð¿w¡ÅXFÓ²WÉ:Q–c'&¹«öÝxÿNùì&-€¬IœÂq ?3!Ÿ] ší+ßêÌö•HÚìŒmüXÌ—YœõIUDZƒC%j{S ½<2=Íä@æÂ9Џ‚Ý÷Õø«Wfu³ýÄþma5À{V!Õ.@1î‘íÒ‘gLþ‘¾nNš;ãÃcÜâÇv�ï|kÎÌâ€ûœ ¿õ?ÌoÝqo‹Þsø¥ }A‡Èù{ÙÖÀðʵù®*4kEÇä {·IüÈÍ”—Û î–€Öž6Û×?*‚Ž^&¤g)¶‘- l�Ý,ôOÁ´Qj:8UÔL탫¿XiÛcsCDlƒÃÀ‰ïáty ­Î·ù¥Âê(T霩úÅ fR·-a¶pQ�Ù>• ¨¥MÉ\JDX÷ë¥vA‘Ë:ë=öŬWe¨À’Œ;ŸôO•üUüXŒúòß© ƒNJo!}þÎç½Ó*I”„ÛmäNG”K㈈ð»ýŠ-«©¤3z‹��ÃkI–Zºp·üé­'7é©GGºe°:�"Ao›Sg.�¦‹SáôLöýbYæ¹]îeÙ8Økàö5\@—ŠÔÍ£Ö(Ôy]WÖÇÐ9·]ƒ*xFU{¬•z22!?ˆ'luPìI€BuËk�õÜŠš¯†Ú ê‰ö-}ô¸ÏLœÒ¸Ôiþ0?}7\O†~  @ïâ$�wˆ5Oî*‘Ìß’^ ;/7©�ßýè*F‰ÑóBP-Pè 2OѧWCúQýH@€ÖWäà­çzËÖä)ÙÀ�ùqmÁÑ0µw÷𖤔7ˆ±8[‹}eMÈœaMoÿQ…²ÖyÉ}Rì¸ÔA´áÝídô‰®%/g KIýS�¶¤Y¥&ö¸åª2[0‚«95y32^‡�ÙáZ©œõøâÛ¹{—Òu÷Réu¹¿×Ñ‘›‹ëiCÉ¥öSÀ¥m'‹Dáî«“H¦y�TÚV�ÎDÕo.<ÔMe_Ì?n·\®šªj†¨Ù¯ôS ¯uÚ…ýˆwG"£à-y_l¢i~7ÅEÄ€§ÇŠ£¯Äo+éèì¬rcõÕ"¶j»sX™+VIUI b¾ niò<Õ›àï�‚Íl� &½…;›v«¾!È U·Ü­£/Ÿ±3,ïÚ·dÒ—ÈŸÌã¶ùíqÔfYßÚIçÆ–\•†’BØl&SÛKæ» Ö:VÙ§£ÒW31*o'Ý�bÜo­ÏYñÔFĆMh\?Ò¸€N ™·çH¢ŒÚ QiÂ÷qYì™lÃ,W­\ÀN© ѼãÙ±›‰ÙPûyê"µ.ß`åg‘‘8}LÑßû^Ïúà KñŒÌßv=F³/É&ï=Žö»QŠ‘À½o’ÐÆà ’Ù<ëæPV.ê-n%5µé—xI˜Á.ã‘Xºûš6v”î=mGó]é´¡1X³\ùî/ªê–pës¹é¸âw�Dfjµ�òžˆò3Ç"Ý*ÞL»:ÞoÀo&/jt‹Õ¾gçÎ’̧û§GâHKÂ4«‹vuÎw:_èÞoïÙ`|45ž«<™šVŸÄü¾ìhæÀ68Û2j5ˆgrÏ ‘O,"CN!é:÷ؘ»?<½„”s-_éQn¾ª«îd�eK •ȦöK!Š÷Ð|á"¯Öª;3ÚâX¿Bü—š"Uòø§UgØç—R§×>Xœ²«0‚e¶Á½C½h RÀXq¢×^’ËŽÿå´¿sð!S§�µÑéwb>§ŽØcöavªù‚qÐ2¨„Jw€‘$ÿèx©¸õ²VýXFúàGL„4ÊU³Pxd}™ÞÝùQ�®vu«Iñœõc¤ýÝâ›ú"T—sŸgÎ6S,%’¡w—µ�å]ü^û,ãø‡Ûþ¯×ÅØÑCLБgs·¦8ceé*ԃ쉗óSõ« !�žÂ¬µ2³sü)hs“ä8qå¹+É`q‰‘¤˜­‰]ÅÊr¶«pÔ®£Ú(ŸQ½ÜÿÕ6ç÷!,ü{eƒneto|^n­SˆK°jÛ·¼±¦ußS`ÙìϽڽÒwÎSžæ›ÖE×Èî�•°'Ÿ&ãsP[v��ÿ ç3rf2Ã{àú�kÏŽ•9.ÖàÜélJÃïߺrÏ· Ï~áßß²}:¤m|×E½G€0Óè>wÝ“›íG¿1¶|ê>˜°žÞ‰„�‡ÉÈÎ p]‘…ße7™×¬Þ–_~¤È:ßé,/½ì¦3ÍI�Sy—C§ð­ìúR¬ï�s+:ùØ<.ÙŽù^¶dwÞl³\õJ󢳋ø ›¼$¤îðkˆÍ]Öœ|# j™Ìõ•OëT‘)™„ó‚*~º ¯'p+=zÿúë1ƒ‘4ùƒMÈržX,ã´èÚÖk¹RÖP£pøÒ6"�­ZL˜µÄ˵~w±§ÄÁïŒ=]P0È~�·ó›ô™2®ù†ryté“dŒ.]-šN-UÖ–·fåLöë¹;Ê[¥g{©© Ôv¯š¸“°»ë—2_x,#\1Ö»õþIê�zJ¹Ÿ˜ŠhM;hárî8àî Oø ò¨Û»ÿ‚WÛíu` ½¸•X¾‚=¿ d>-™NµÐ¦a°dä=ÜÙÝœ*üÓoœ{^èŠnðß{q6û®_G –é–ó¨V�ª;ÊÇ1˜½Ðb©À°›r‡b—Ül‡KWšml`dñòê|C6I¿ñ�бTJ]™ÞnóÓM\vnƒkÈr©àÑÎuÍ tLuÄá�í9ÙL…Wïwi»¹ÌöÍt–0j¯ƒ!rր𥙝��qåÈk’H·`~Dæü*[óû8Lß8!ÛË<… ¨Ë™ëîï>áÍã]«G-™nÝõãÌ©† ¢2í!¸¬rСD¥G§^󌉆™€q‡œxüä�?j%¤>]9m_*þ#áÕãÔÚG† U&#«»]êö{Ûé´ø¼=hžÄÔ”óÅ9+1Þ|V8E°±-O‡„$ŒÃÅ`Î t•L5É:éײôìI’˜Š,2U�í{·]¹ÝMÏÕj"!´aÝ,®Û«˜�-®kÅç ­�˜7Î;:5.'=��\Lƒ/O~&rMmÈ–c �lE°…‹ƒÂ‰C¬‰­Ž"ôÚ¥ü,fâëKu˜,ì˜h£5Ó­ü}𠪞û›âØÆ7¬½½!NÓÊ@Îóå°5½–´C.Eæj¦ñÅ:O9©Ç9äj�ª±u½o8ÿv…ö¸[žÃ" „‡î©4²‰ 8_“phE®¸ªµ\íL~oàÜÖÖJkô�[;§�åu….A.¦þ]ކ¯uÎõÞ ¯¿æüš®v2Å1åË[Ôv=ÿÎçNr{Y`¤òŠPBÅù�ŽÇB>¹/Um/Ÿs½ÝØýœ/³-Ý4zZì¥.îù¼JýŒ§QfÑØ¨*zøŽ T·^Öþaؾ²–›ß@Dµ~™1˜…‚(ó²£ô ñÐÊÆôË¿8xc ³mºõ ä/åfû¨ùñlpJBÉyõ·ZG¥7OžO¶ÊD…û{o÷-Oš bþ_`÷5\W¶¹]›|8 ^r&œ!!N‹ŽÿJc»vY3û¥Ä^Õm®‰}óø^~¸’îR6´åüIÖZKé€:ÿT¨(85ºaTÙ]šˆ_”²u±áhi¿¨0£w1#f(¥qçjvN2ƒW熧ÒM ޲*=†» ;_._ƒÃå¨ Å))#ãQåòTˆ@‰Á£¢‘&ƒC*@²“îI,!ɶý@ñ@ä0—”{>Q¯k‘lãÉ fÌ U’�àÐD0ÑÙáÀ£´ Á‹ÒÄåQ³Dç£É@Ë€¾½ûþS™‚É&7KG.Ý{¯œWêÍW8º|J¨ck܇4Ç@# x…YÌØYL üTÎÖOºœ�šŠ’ßX\ÕÔ«<ŽŠ-åωÞ�Äg³Ôzž� öÕ 1G•yèÿô7ª�«°ÄaÊüŒÆW{]é{^…)‚¨/]“¥ zw«$eo>ê-B²Ž=¢¬ãDÂL �P�mJÓû”ä)½pð¸ò2ÆH¢H'Ë×ûW:Ë×Ö]Ö�×�áï+ÕMbN¾)Ý‹UqäÓmHA"¨ýCYÅ2á}?ãÁeRBX J¨HåUq¥ñçjO”°/à�AÆû…6ª•ôNçKÆëaÀÎ*Ý\q8Qii¦k81¤_xñ¤ê–½¶Ù¿eà‘ÈXqü:½ívJxI–…³ˆ“ 6µî·D’üÌm9Öò¥HÐdXPÃ,°dˆ !„’Bº8ê©C°$î|î ‹½’¼ÛûEöšh �]?„aý©°¯¾OwÜ©bNˆÄ%ü®õ#RŽEÏõ/ÃbïÙ9,·5д­þ·ô§}ÎÇÔ+½ªÓ4o;@ºû*‹ª½Ù±ýAF\|/‹2`Né»CðT8d™ÂðdãÁZñÈpù¿ÊÈ@@@ØÞÛŸ9†Í�8Ãï®e@Wm©£mÝb)Qš€YT SøÄ(ÈÔ•K9VsððšÐð6—`š ›Ï°’OÛP.!Ìg“�Ûië­ê‘ߦwAÚZ¡T.j„1.ºóPìo‡øx§‡X2–p³!L&l †˜Z–�¹42Ð÷œ¬¤�ÛÁ…å+UË$ Ç~sÞÓJ­Ô‰ÕWzúw`\„–5¤—F~–1©—ºM@¼°µ1èÝ~÷ä*â•§}éþã–ý‘þs…è·�Ì´8Õ€�¶ 7׊; Í:Tî3-.Ž›l ‹(CÊ„C ʵœoA¹7¼@[´éº•‘›o*Lúk`ÀÆ�ÁÕ`È£a…dÅM=Å� ¢ŒrÞL#³`�Bå@�#{Í~~(Qm,;WŽk¿,àÞò—¢Z�¸Åæ}Xªb' — ÿË‚d܆OÀëY /­¬ö#:øp�½wáªþÊTÐc]?õZâø ‹ÎॉòGˆØ¤H*“ aÐ~Ú”@œ�&VY™î»£Úø!w H¥¸�t½ßgà†'êPÐ%u¢°�¥ØãÝUð5.Ù�Q225È!™€‹Ó„5ƒÀ°Œ7�1‚ôÐ#¿E¸Öær·H` ½ìè˜é£ö¼/ÃBqÊ�½>›ÓŒwq8 ‹ï³d‚.B¯†îE%ƒF i]‚#ƒ[Ž¡¤ŒHÚøL³ >ܸ%ƒWöDuRµQ#Üú—š »Ó; |¹ÉZnãµÅ|‡àõ»Ðn,$q„3ð=:†®w‚^Ÿõ¾“—ËñSBâ¼8¿ÿDóD31i=Ǯ٨G^ã|vFJ×è<¾ÐF>Ìí©érœcI†«F,š ßè*® ªÒk>G/M4mæò úlÁñ§Ø'àMYk-¯žÎOªý~ó»¹ïþœÂ¼¾¤v÷Ö»Æ=l¯9ñq½~† Â6Px½qS\~ƒÆbN9TO7î.¢5ò2šNþ\ÖT?&*+ËÀZ+ä”�ï–^¥ ¤ç1).óu‹9èÁ Q^loLu «Pëa‰Œ¸a×Ã&};—Ž0œ§v<8ýcÍŽ÷Û#Õ”=G/WëÈý3/Æb?ù•ƒP&Ö}ø”KO§—Œ•cÏGiHè÷¯£Û¯�ÀÖãšä¾vR»ŒÜ#‹� ZÌf8»GV‘×~c—D9ÙI[€‰ÁiÊ #¼z`n5éÓuT’+Ó±9kÖ~Ãéç2i "¹å¯qϲW« ¥Q@£‰ÃÅÀï>=¶æ€8¹ ßrK»ñ¿ êñ¦OßÎZR¼Ûß°±mÙ´}8‘É-�G4FŽÒža:À€QÛÂ/îú Ì@l:Ëþ¼§�• <ø)þýõo¾*?sA.¹eúðõßÁS„†ø]gÇÐE÷[ûžûÔ´ŠkLòt‹þ™j×î)»õгÙåwÏ&NGúp1ª)<&KE­äÇô»•Lèþrgy`ˆúÍǾ¿¬K­Ë!­Û“z—ꟴ»©s¤}}Ô±BµÐ>œzëUB9À-¢:Cãñg£�§Ûô+{5Ô×zÞ ™t¦'è÷Ûì<§¿Â[ Ç·úQiö°�5ŽÀ83µEÔÛ+ËH§³ŠerA¿Ž;Äæä_-ÛéçqίĨ0ðN£„Ûrÿø:^M„t¬7¹Ÿ™n:‡È³,Œ½6ƒŒ^ì¢.�ÕÀòV åftríø´¬)ÒÍ5ã—k±·B%%’H û—»ÏÉC[°v²µü|¨¯¢s°°°³þ¬eQ`gc—‘—cãVàà”ãP�UàâfecU”m’gãbW’å�Qä¨5âqו;Y'9Û´§>÷}Û¯é_ýl ÀÃ%l÷Ȫ0ºr�%½ Y£–GÊï›d“€Ù(„û?‘X8X¸¸Yêä98e8äØääÙXYXÙ›¤99Ø8¥ÙXY¹¸XÍÓ:Y )Ó/¶ëß�ër¥弇ú�d“ÁÉ�ªQ˜Qâìì�ªb˜ˆ&jÎAìrˆˆÁ±ÇÆh‘:Zý²87œl�g~w=j¯/R—P`j} ¹ �ÿ¹æÂÒýí7N9V.¤:Æ­ ºø'÷ñâöÓx‚T«8`ö¨rõ´5ŸÉ ææÇØF ¦A ¬G¸;Xø§8=­¬‚: Ç/–ÿœ“«Ë�«©VžMéµ­­¨_”mædÁ…,@Šx›F=A-9-5 -7%=©\SågÿN{ƒgîªÁc,¿ó£ puóºÄKŒGÍÒÀŠWkÌi¶9èÉ�!ul5—‚c»�zò¤g׬B”ŕɎ—ö¿ª&]ßÈ¢(ËÉõU5E.––Zi™fyEYE6n9EÙzi9.%Y6NóRm§K[Èå§‘ÕòO¬ÖrÃag’$X°ÀglÆúÿ®ÚÅ`ÄWÑ£Ò§ÁÁ¤c`#þ+ ‹,«œ,›´›´ ›’‚»¢¢œœ›"·´¬¼¬,+ «é?¤-·K¥õ_2Ýu5g5tÌ÷ïÞ°`îp¡j¾R÷Ìð+uÙ±?úYPÅAÓ´�ã\™SŤäPJµŒ+’¿:{$ìw€,H�#…í ΰ֦˺ÓÊv<* âÂpuæ¹€qöz(t¤½U�¦Ã&H©X m¹Ás<3)â¢ÙDc´Ð=c£üæ÷›%k;VWé‹X±5÷ŒÇoùwûñ§N�}‘üµÎµR¢ëí‚ç/$K4÷–vÜÏ2“°rS’ñªŠçÿ�\��…Mák0¤Ùdš98`‡‡±#¾K¦ �Æ6–±1E‚> ~¯öÒ>}ß7Tœ~ËÝýnèK 戄ëÜ ÿ…;Ìøp¯ÃC°¢J€±,EÒǬÙHɵ±(Wµ5Y´M>a.z{Bœ[‹½®Öp×Ä�àþ¸,G¨Y˜³ì97IÂæ¾Öw2ÔJV/¨¿Ã|Än/#ê–¶¼Sæ|!„GIÆ“l©…ÿFÍ©$ÃÊÊÁ!ÃÉ-£¨È!ÏÊÍ)Í*-¯ÀÉÁΪÔÌÁ¢¨ÈÆ.ý{æíÖº¥Ëéjé 2s¹ªéXÞz�ÌsæÿyTPí@Éõ¾Þj+s€‘쬧)® §HÈrò2Vf^ !FvjõšÂüà>Wó;¡† ®s"÷£óÕœ†Ž]¥‡P‡Ò¹Î§ÚDÂñz%=‚>=ÔkKOŸ±}—6°è\ækôGž¤ÿTW[+ÏÁÒ"ÍÅÂ%ÏÎ.¯ÀZ+-ÿÅ>�,ìŠò �\¬\¬2Æ3¹ÙÙæü´F8%xctO'ìUÌ`¼PÌõÐ_€TÀªþ †ßÿ¿�~ªNõF¾V¯ñb„3aêôx 7å~ ïüjߊ†·@ô¦Fõßɯå`gá–çú"F¹fN6.ŽF…¯;rsñ1§\#›ü$#Þ¯žÇ-„Š43�γMNר°õâVEh0Y,³îÿäÞ,à?=ó?=öÕsëþõcvÄ�áFðµ7LFL-�WÆðß ’g½Ukg‰\ª°lê¥Ñ^r†–Î ñxŸ))§M̾ß- î´v†Òm˜ÓšLhø:d¶7>•EûBŸ‘ÿ‘�ÅE›²{*XòÓ)p�o–?Bü‹À5Ùê*ÈøßlÅÊÝ,Ý$ÍÞÂÁ®ôÅVÒ2Š u,�_zÀ%ý”½NZ�“Sî·–�Â(¯ÕÉpú‹°ŽÀ•ÀìN{ 8˜U6ïÿâÝÿpˆhšÎ rü`±�”¼J“%)ì‹Bö\>±7üR!�TuS´{Ó<â Ä/~‹É[¸¹ŒáFŠx´è8t.*�µ»JSÕ{D–¾Mj᱇©T(4a7Ó’b8sýëÌW(^ÚÍÉ€Ûø.krÇy¨›y(jÖÉÜ œÝŠsô/ukN¾�«€³Zo—¿®(EY–>…QœùoäJ2ŠM, ŠììÍ,Ü2M�,¬Ü�òì2r2¬lÍò윬œ,œ²&Ïh³\¶@dÞL HêT}jú© B Ì. K¨é ù(÷�Çñ[A±¢Š�afÛþžméMƒ‰õ�P¨hb7ЉÖðöC„òoØûYó·µÅMžEFxŠ "ƒ#EGkœìïr1O½dþ~"™ÓºóÏmÏQÞ}JñØïÁ6\¢#BV’šïsMˆ2rŠ–og%:ùTyøªáÑ Q˜sðUC㘄Ïñ꾓](ÏBÔ�Wéoð5îß–u'F–ŸaÖÄ¥ÖòꮣÑ~2¶+¼ÄUýÞ€jNPÚÚ�»\èíð˜Δ’ôÛk;5¢5ÿ÷xr4±ÈqÉËÈ}í_­ÿ¥_�N1 óòHò‰ SòÑssÑß’Tp™ôüìl"îObÁ–HÞԽѕô­!À~8|�ÿ%d½”'ƒkñ[í»Ä%@!Ó_ïSfˆ`•ˆUÿKÒ¸¸8ؤÙ徸­Vº�…U±Ž…]^‘S–�›­^Z�K†SQF�Ë$^ëCAòN]i’!n$³ÍPÿÕ‘Ì’ L³{¨ýÅÜýÚyPc’ˆÛ ”Òq'¡´4!À"`àµÿ;"çÙp*~M€›,+ç—X+r*rp²)ɳJsȰ+AÒ™ì jfv·cAO ’ltLM8Ú)H0]F̃úÿtA?˜àÿt�œäW4,Ñz«ÿ8[gdé‹wÕà (^¼Å (}¢ŽB×ÓGj Ⱦ6o~çgv. 4�jjý#'‹[UÐ¥Š°¢â6Ùç�]vô3ôéâ@»í§"¥´žDSýÔ?A®]L-ÎâÛ9RIL'Z¬˜ˆßÐb1aÓ¡ÉU)Y©VãÎÕ€ÞÔ{Þ]Æ9w¿ ûV½§Ž™í­>>€d &¥¸sãHAÙçj†‘fˆi²¾ã@ö$Ê(¹:Œc ~Þ§¦……Eš‹›�]ŽCV–“E†‹ó« XdX8ØÙ9X¾6ÅÆFyYcÐésÂ�×]Þ„œ"˜T‘ÆÃ8‡¦‚ V÷nÈ3øÿØ/Š;dðQÑ©WlTèÓ$­þ_û…€Ç€EÅÖ1éÆC&²þhzÕ!º–ºÍ–²$í�2~‡'æ]r‘ž<,µjÿ >£­ J‚–Å}ò÷ÛIÉ-€½^Órªv#£ƒÏIK–½úe\P³?1cL‚qø¬�‹÷Ê„pµL߀=äšÊ$lƒ‰´€FÊþ¿Ù^^†³^^–M–Už•�•K¦^š­^^ž“›[A¾NšK–ó냳™ã7 ¦Ê¥ ˆz¥ùûëç6ˆÀo¬(Ì¢»Nø¿v¸€€gÈ}X0:höÿ¥ç,-,�Òlìì2œ2œ-\õòJ œMòÍÒuòœ,\-Ò¬¿�˜Ž'õ¨C–çMÂ|‚‚oÁH^ÐÿÕïÿ›š37éýGÅwS�CoOÆ1–%Yp |tˆ oyôíÐÁÐàˆ¼Ê(œÿ—R˳±p·pp³5sÈ˳6±üç}�Sš]–ƒ[��[†“‹�[¾‰ãêB–£DS­WÉ\["ζÐ;K§ãˆ:Ë–mëÿ{.-ÄÆáí7âæÍrÙXÿµÖÞB€ŠÁsþw¼ZEY9vîZvnN®¯!å”ã”Sä”ûÊ gËWôZnvSÔ5{s‡$w“—ô8]C¸"‡ŒeH0¦1L¬ÿ7ƒë g±—ÃkŠ4D!Æ8fÏ,&nx0Ü@¢wÕßdéðÿç®4ÒòÓK ¬J\rò2 _k W£4; ë—ž|m©¬¬Ò¬µò_umä�ç”g[œ�/3Jszn=WÉÒÿýþ�Çή­­ÄåÂFÌ ²ÌŒ†÷¸M×ÄE6ÕW²É³™@Þ€åÞ±æ´*xd½îåNööö² ˆøG@L ²LÀ WR 6ìÄN)< l×==!ì3s»åÖ5¯.[K¨“½®R²Ò|–Ù¥Z\·‘ŒÎ÷wõøG"ÆÏ ¾±1Ë�R—Väšêåb°~�pòY{œxÙelFpCUx-± 'arX¯µåx 7¾6gixÌÒ]kî r •öïX¶¤l½öÙ�]ˆŸ˜‡Áî2UPkݰ³°0A¶GÁ†kB¬ŠÀ‡/*žgz˜÷�¢û‹â¶ªUW›¨˜]�ŸðÎ%2Óm>®�œgw�´òüöŒWÙù(`í!&iÄYˆ.�fFFF¿š-}°œ�‡œˆØP-Ú 7šZŒç�6ìÐEØŸðžëͦÒ5 ±=£ïbåÁ›ÑQy 6UrÏWZQ ãíÏb§sÜô‘SÅE BÂ-N²¥ZÖR’•ûui•žˆ0ÊßtŠ¡•Ò“(wMo],�îĺó�Iìø¯¯@ùúÇ*î]ÛžK€ - ³ á/Sv¨évÁ'$�;»ÒgI†Ñvˆ“‰gÅÓØ|k@ìYÄÑ/¶ãgÃM¬ýë9o®žÑÃÈïœÛüÕ%[Q=4 �¬ãÉoŒß'‚Ÿd4a*¤ºYšyÞ0Þ9œ{=þ[<1%@ij–›Öö¥k»ùºtG·öæ¶Ý�ÜŒ µjwqܼ�á�w‡íñ¼ÐKOJf†ßIßSh Ñâ$½�¶�΄ ëÈÊ`oï ËÌþ¤%èKS5­(«Wxé]�N’Ö¤˜LSõÞNÎbØ‹m!ʤ4sÞµËÒQÛuyr)ÎÏ2TÂ*تmi/šA“ÇÑ÷S1ÑXÎoÿúåoyÃw´A‹à\‚g‘¼kÇ‚.¢?tsÙY¥ßoµdEx¼á„ܬå®pßL¾á…È‘té—Ô[Ëœæ|b€Ýü9]nÇôÈÄ "± †šßÑW;²*v4‰ý¤Bž"~‡ÃŸOl½_æÖ²ikÙï:ÎÙÿVd5R„áŸ]WݯD=;L\_á*Y.Dl·y—_˜uˆ‚í©bù�)é\Õª6‡†)•ÊÈß^š[ƒYH«'è”QÕ‡{ÜøK=„™hKÏÓ…Õ§ˆÐ¸½zŠ¥ àY^’ìÖæŸ¢?iz{JÌW½ø C�Bôý ™ÖZÁÊŸ×#‹¨1Æ2pI’ŸX + ’¿Ù#KuzRnšVAF]™N†‘)8&?7¡¼§õýøøèè8`°5KoS´'¦$ÆÁvÇ&·`½§ª ®óÓªü;Ž9Ÿr- '#o�x^oâ$³l,lÙ¢2ýL£$åþÎq Dv|2¾¡ŠZFy\´XâPüp|œëÔ"ú\‹ ‚U†§sô‹n›R¦¬úE0C–§T7b (~µÓè/¬Iü”uùÙAG%¿W ù’0 ¥“ÛÍ.#ÿcf’sO2ËᘟzÙ¹—8î�TŸ�4þfƒà"|‘>÷Õw8Á׬kjKûÒê±íô £y·Ç·Õ]»5Õï¥Äv3¤ì—Çä™ñW]&ý–¬s 6¨6iò;}ĺ C&Ù»*„IPSS*Ñ?Ëÿ2ó�†A{™mÍîÒ�“JËQÞÿ”'�˜­PS®œWµ7,‰t ’·à��µ²•³ˆ‰�±˜??¡âˆÆG×áöâËSH Öèuö,g…¯§ ”þúJƒ•J4Jª«¦E/ÕÁb>*ÿÒåª=’+¶øˆýþ7v[)NؤB´ø2ÔØÍ Õ‡Â°õïêÛðÎóÒ�–ó!ÝBn•Š�ÜkOE€¬%x>sŽˆpóDZIÂ)ÝŸÝѩݦ.jÞè›Ó´‡< X\ðo—o€iý#ïxøCð¸æ�ÞPêk,|éƒÕdÞ¹Õ™ =aºgz”Îj¼ÚBqö¹B-‹W<¹ú ¶'�Õ/MK„-²¸ºMþu!•FÑÆy3RAù !ÒúŠ(4çl°;+¸Ëäó’Ü’O‹ÕšïHVžŒ:Ò <²¦4mí×¹P?l@’\´u¦îbÜF¿-â±þ+øƒ+��’c!¦Èˆægz…|¾]JËæcß¼‡'ôîmx{_JŸ©OÏÿû¡ª0Õ;ˆ²8ñ®Ñ±LÁ‘OæDT²““Ô–´Ò¶U‘ñ¨7cUøý£Û« Kî‡JÝ®“âè}°š¼]5ÕÁïÓ˜9S•Œ�p Yiði�ÇA'¨RÅe-IÃeÚ«NCT6´ÈãaЬ€MºðÏ%“™â'ÝëÇ™T½»<Ü�ÿ:¦ÐêÛÃ{ÿ€iõ+޿®þû—÷0ªr— DwgeJw‡ Õê’Ÿc‹¨Íoc�{÷7%wzÎWfþâYµ·t´¸h±ÌíˆZ"É1IŠnÿø_Åp{ç…éEÂnrïè?GùñýP�»KI RʼnÕ,vž�ƶf\“ã°²¡L„G™ÙyŸ¼ÔSþîq Y‰n9üÒû“F½xŒFážu©z£¶u—žèô §á + 16+-7MMY±´pt´u5y>¡B*«lß@Ô:fÌ'Ð3.¹¾Tï¯Udä9«( —Ž3HÍæ ©G¡\Ž¢m!é£+áåh(+ëeK5´Îøu " QÇרážU­¤›È�âÝÒ{°!ƒBÕâ…‚¦xXÏH–ÙÓöçx±xêâÕýáhs†6Ô ;�8'ôr&±Ä+}Ö¸6¸„7Pv˜ÂÎÊš¬”Ò :8—dR+¿—­Ê(�¬Kgðý|1A²¹Ra­u ¤®K„ ߇hL¸l˜¸^æAž&Hk-°Ë M¥r¢ŽÁkœIÈœ :­¦\*l›éáVUµ"ßõ.¡É5sÎ$n€'êvÃ%ذ޼¯¹tŒt5£¾!ffzâJº‚ŠÇ¹è/ª·&1ºý}ýíÚçëðÉ¡7ûÀ5¥CÄöú•æÞü³6µ|u?ˆÉÍ.íëÌ9$ÞÚü³‰ønUyº½eè8#ªhÀsö}²Ã3[|²Á®„%ú«zkêà.)ê/{Ê{–•×…™ÓZtø§;›ïš�qnqè5—ëš ¡+°{Ø=ëZÃFnSá'ë+¢‚ŒrX¨D,PúšP%ËùºÜNMÃÊSFõYjƵ¦/üׇù�£^h'¿Tµ^=ÆÍô%Ý,·6u”Êù+ž:?e‚¤ÙË„w©"ö±t–^¥�òõ|¯MÓÛáåãkÀÃíåE_c\¦@…Ú\Ÿ>Lô\¡)Š!Œ¶šoÜT,Ô_7··Ñ‡›¯Ù‡ïïïK"›Õ"õú—Ç‹AŒL�­ñ«˜�K_ù½ÒñL3_'Ç1‰$ä¸~š®–Vå�£�j޼�Ó×TO�EcÌ MÙr“ˆ‚Ê[ë]8UŒ¨B“…µs:dn‘ \(} �AmF!ƒBj¾ó{ùBů2õD`IÀá–'°¥Ë¼=Î>+?IšEœƒB,^*¬È€z56góøyºusÒÜÄ€ÙJÐ?äWÌ>=V8*v©ˆ¤^»Ž`D=Ôu%,0–oïÐO€�Õgj#ØOÔýj‹G•²j›Ö�mžâ½ÚxçÎç–wÝ‹“#×Sf¹£°®Ùõ´±Ð/­:-óËFZå‡o´FÒT_[ÌÊÓà%-çx¦¥Vi²³lÓß1ó.\:æ�¦³›©ÕièjwešÕî Ò[Ï|D¶ù�¾~ìÕ)#ë4ÛBʼn–Wý¤éæ‚Ê8¼ÌµJ#Ì® eÉ89|¡x˔ЬrqTKe}ÉP­Ðjã¦ÓjùAä7ôüŠ`ûŽi‚‡ eÍý ;YzYhܲ@àLöûîÑ-å6µ›Ã‰kÁ„TB‚G÷46±îÕÍëåŠɃJ6c†‹¼Bí çF@Oæ‘L°…*ë;5˨+IãÓ%ÕFÛm‘›«l Ϲ–ã18$íèá\‰ê-1C«?A¥¾9x9Oô�çz®ä¡ô²Ó;•ß�´¥zŠOê^ŸãPœÇðõ±ïBQð#‘Ȱµ@´°/ð³Ð%YcM­÷÷$™ÏLBéi)ðƒV®U‘í,Ü$Ç(`Bp—¥•â(h={>8hfsë|`\vñ/ñŠDö²áõ)/îEMV9(8•’7.ØìÀmÕÄßWåTǦË�.—okO.|çù@¸^?#~Ü]»ý¨Î… oÓÓq¯ü¿{l�;º¿�ïÒëpÕ< _?Û”Ó|Ù÷>O�ι1z³£è¥^!·mZ×6ýÕpÂP¶ÄÉ ˆƒY°8�ÅÁ‘¼üÕ¯/•ft›ÍÈ*,• ûpNÖŽØÍ½áì¿8û`b pÚ‰ÁŸ… ]8Å SÆÇäRóÞ{ÅJ1�r”ñ|Fçû*öR�޼J¡®ïØÌ·=òeÓµŽÚ2 !–UMtî ÓµèLô)úµ�¢d0Óîo�<áif4‰ØoªuåÜ’þ”X7Çòø�‡¡Uö4j¿$ `ÉÌVÿ4Ö°è"r0â‚›¡Y¹UDòítzýe 1úa²råJê{ ŠŒ="�Š~܈È(Ú.Ò¶ébë�;Ò­e6­ÙÉ]ïÇøöZ µ6alÃÆãäˆèàeBKö‘ã˜BU9ÞfÌB’Cñô4&„ÅVâYz"ÚBH¨gú¦ŒçL�ÏNL^Òä`$&­Tͦ *AVN²bÓ6)—'<>¹BKÈ‚îIº?�§m ªËó‡‰�9´ Ã…ZPÔÌñ¡Êæ3™Žõвôer>×·&,£1Dn\l²€?Ì‹ð ík…A!w">†¥¢éÉ�£§å¾¯˜IÀ£¾kRÌ �XI½@ –¦4í¶�8ö¥D�ÜòrR0]¿ètÖ„ñl]<Ð{�DœÓŒ¼ë^ À{Šg¹•2ÈGµ› í±'!Q ¾â'U8�sëkrÖå¸Å†!Ã&ÞÍ6Ww?û�²mèãÔrÁ—œ©‡±‡í§0†-¤Ó÷w Íæ3�©‘\4÷7`×Û$L o¿ ÏY\DVîã (ZoA#‘IX…zwè™I’ ½�xW寃gH‚�®v²pi@À¿E,Ð2]$}£¿îR;iJøg}‹“ì0BfÕ\ý�m�6û��ÂÍYi(Â]Çm§ûD5ú°ªC¿?›Eò84îˆsÌ?x¾Ën#�í ŽêŠJ†ÏsIànWRªzj }fS ï§´YîMA A[ÞgS�fÆrn¦'kó„ê[¸å?5iÄÎWÐ öÙËoZÈñÈü?e·úÇÚYi'tõ�þ8´fbH—â¥Ä„[Lí“ñˆ‰{¼Å(¶ ?«iÓšbJ/QèUº¦~®Ý8ÞDÅÅ‚+*ñôy;±Ðy>è�]èU�vÄÊÍ‚†›/çKæ6D#ÅŸYä:Ú¸¤ò~Þy�^½ÊtË—.×¢Q¤£‹œÆ²ZVŒq@+{fhpR=Nn°üÑY)pl+“RR�¬¯¡(ÜQ.:VCOÆüE¾àZŒ“QŠü�cMìuúZ¯"‹º.G™[%M>Ël0rnä9ÏwíñÝ»Š„‰eLöB¨L °@U|['*ÒçøL ¼­–fWW8áRcqs(|¥[2ã*�8û‚ºÇ8oÿW�µ<#QÇQ=O•XÀ†â ®k*|†1殪0´ƒ·!AÛž”nk˜‰w¸­¶ânsn<~dU\v;™ã‚(XVo¸nͲ©ä|»/þl= =èó]½dÂo¾\¾ÂË^mîvoÞäj¿.Nîæf]½Lûl’nž(=´uötmݰoþù›Â³çÍ©*3e¡á¬2*–Rb²¢Ì:ÛÙƒK5RÑ…æý²W>ìèÔ¼ÙÓ‰å,kt™éý˜Ú+‹ñë“ í»Ð›|`íÓð®qt3®ø®åC õ½Ä�Õßjíó ù5ê«bånk"4ÓÌ6»¤\UíÃBßRLlå~™X‰SZ\ ¶'7K+ ©'ß:ApƒƒSŠ:vA+ÿ,J6TÒŽÈ©× ºð·jœ[fz.~‹^¹<±®ÉC© ­Û«RÕY]àç+Õ?¾ò cº£t>q>S“P_äz…’¦j‹�á<ýuéãX–mxò‰¥kYœô qžcµ—5 c?s 5*xML‹`Þ0ßàË�MÀ#Mèß§üNŠMÀ �C‡!²Ò'EfùGÉÍ\öŽG\)5ŽñÒnD“¥ËølÕm þB¸bKÃK{ó¼‰~8¸� 3¨}@¼›Õß§OíIÓ/¿@·$þ|­|f5�<‡/Å]Åä½ò|kšìz½ 9&!•:]hî7Å¿%yÆí2Q U»'öŽî£=”5), ”’̯ÄÍJŸg·#¨®J_x$ñ TÌQ{Sp!Rœ·²áPQJ�r‡×¦ %嬬Ûí§�XèkHã77Í�–“ÎVmku?O¤¶½P\ÒK;®Áh5VcA7/IHU$¼¨”Û?œ©¯MÖ5ØNwyúÞ¿ÿ%ÙÊtãÚXì8%Åoð¹ê¬2gVuœCõÖ ë8rÉ6i aÑ‹y�TA¸âÊ¥P©´å ÝÜ=ÃMšø™°–N"„n,-¥3¯¡”Ç‹‰¹Ã”WÊ®�¡‚õ äÀÑO+uŒëE…Ó�—ÆÖÔSÆ¥Î/ùêÀª\À<ï‘Ö³9C‘õP »�ÒÏÿlFÊùÝÝýÚ|a/ÒRXðìxÙáªÒ±Z*)⎡›gu½4x5&g �3×xUгö^8óX§/DV=E­Ø1�}ÉΙ!ðE‡ <Ð!c—H‘]”B4(…%to 6T ¬»Y�a‹¨ž¢KÝ/-\þå™-¡UAf#$ëÁZü�pWkQ„Rì@¬¯†KÍ™B܈Z#Œz£h~Mí ÎÚVdÈß ·e‹K:xm‰�Ô¼7¯«Z ñ 6û´°_•4Jã ÓyÛ�UŠÓV2Ê#�Í�ÓqO¯ ×¥‡§Ü ?ª%0�‘Hιﺭº°¾nËñÎ š=bª±œÌþN@Õ¾]í1z”)/ë~KÊ’Á•¬ðE3CèŽ"‰^ñšrŽäD¦> B(ÍåžÈµh;©5�UP ‡#Ž�uQÝð/üßÁÅ8ÿþÈg† øBö·gã&ý\yð§µÈ ðJ–>œhÒ±:Ç)”ÅÍ|oÞ¬ ˆU¹pR>õÆf�žæÃals8�ç}dSÊdïºÌ¸¯ÏPk3}'áÕùþÈD0ƒ±ê>^5ç<¡KÜ’¿ 4O«ûˆtxž¬ÔUœ‘ßÍØtWÕaf87b©—«€×ŠÇ1“«º¦%/»Å5Ï(ŵDûüÏ¢ ¦îÅ?¢mð1Ϙ’r {*ÑF—®mЂvSÝ^ÑšVÚÎkVî•�£)›Íx®&8Ø Ó…ç@S×ß}g'üp™ q’9·�Äkmée)òÜ€p÷ŠÑæò›£VF¶4â@Õ«þly15m[³7G&hSbCuŽÊ‡¢ZÚ%’\ ùïüMï2ùÊs¦v<É5[‹²Êlâ•‘^•TK€öÜ`Pë¿))š—õj‚¸[M—~Žy:ÚQ!µênöʃ †Û8Ü`’Lù“'S­wRUÎh¥a”ë‡ï¨­2¤�‚; ï&ÄlikѸ*³ý“_ŽÉµõžy‚§JŒÆ.µ˜¦¸çÏmÛ�=ãvv5úÊN�´R†´#R”ü¾˜ÉÇ»l¶±ãÞóaxïÚˆˆIÖh’¼‡²ŠHþØå¦Ýe`Ç‘ü¯K¦4é]úÌ2×Dæ ªã¸.=Æ¢Öõ`ÜFÆ_ç<ÉT•ûƒ/_KÑ.®XðX²T™îɨËÇ@ýT¼½2pÈõT~|Ù Išøé]Ãl𿨠ÖÂÅÎÀVí?nQ@\N›á êòºí,HŸO¯ýõ7‹�›¾×Y8Oùµ ÆIì¼7,D]NQ~O+xŒï`¨ïl Ñ«úÁÍ´�xð_�sl)¢.Èò�.'Q§Î§{ŸKjR.ìpô™ ¼Áç0͹ O1¦>ž$y–šÅ¦_ÂV³Y7Õ¬B§Á6cÙÕø8­í~ž>š}ÕöÞÙ‘nކß'ÕìØÐOù´y®¢ÏÞ"Ÿ¿žîîà�{Ñš°I¬j“^ÇMÑ%iëy¿%X� Ú'[»-Œåò’™Â{c±>G=XLå¦p6Fn4þ:3«ÿ^Òñ]o€Ê%šÍ&°ÌÍM½7­~ ¼gÚÍܨ~Þ'LÂ�O6Íü”rV¾™˜H[\æwÞË¢¡$HD3TF›HœPºÅ íbñ)B9ù?÷˜˜ïl‹€üE~Q‡\ÂmôDã÷¸¹&½k¼é€8Q\ç�´÷vAçSl]ŽIâßeì´£ÿðBÇ.»´õ˜?õ =Áò‡|Ri&aðžÄ5áx'—…kvÏJV’‘;d@àMÔ3-—¬ä_pAJväW6§ïžÃÖÔpå)a©1L5ѸßÅ=K©öÇ,>d¿�ŽÒ“3QIÂbσ¥%åÓûر`ï r„m¾ÉÒþ#ŽLéW ü&˜’·»°‚©g8nÉÄO‚Ú@†,ö·ä�EôÓh6LX…Ÿ 9á~ŸbúÁ =NïHbB8£�ùëH8ËA!N ÉdWYó�2Ãü8è�oþ‚^ìroTáK##½uP|´ÃÂÐÁC]õú¯¶çè0sU!j£l0v£;¡Ö^�Bié1u™HÝä‹ÎÆï…àÚ¬Ôpën¨ULƒÑù™Ðf<Ÿ³M¢7{»o÷7gáãù6�zytI±"ÅÁ‡ .WhþÿÝýBÐx¢!v¤L›9«—»Î°Ëmºêaç¹J^©Mü_¥yIùæª3H¼„–=e¨�…8�ð"žÔÁWu¸¥%ì5ÆÎy ©m«zò³#ΚTeŒre—•€»oðü}¿aDtä\Ôn·5þd{)4í\§ßTxt\”_\|ìëýÜo™Uhi] ;vU–~zô¬mè`é3U–€7«Ö^K§lÚèØ^ÿ¸3¬õü‘_]ÖFoÎh¹¬lùW› ¶<¹ÎѼÙT‘;ùæ»þšOé襮Eu²AãÈ’K‘âU�·•¾\Ú0vuê-û£ÛH}¦±¼ò}(ãS™V.W’¸ ]»ÑýÝ®KCv--ó Ì«bSèÃës8“YÛûùy˜Ûý9ãÙÛåt»³y»£ry>’ÒQÚLU¹2¥¦€°Í‚¨µ¬ÊÇuöAî¶Y|ãB™b<êhK6mÙLãêõ¼\¥SQ ‰‡²xNÜ|&³Ôœ1e~î“‘6ßôØmø�h”}½çè7*“Á¹¿A–+ð‚´ÆU›ÊÑtbÙcwXÎÎ2e™›^ë s–Hz.L2÷ª3ô;pÔ®«Ò¯ªìÒP?(3E“ÛoXç2ml¹­Nçê/ð­ežex7®ýÏܾºYƒY§ à#Þ‹(J7y1hR†Ë-[Ó*/»‰£>»UZQÿNɈš^§ÜÃ62zvk´ÚUŽÚUóíÎþñø^ Æ=.ôÇQòe—ÿöíìbçuÇ(9aÆNŸíbÊ- ê?a®™6«`ÿ+‹ºHÁ ÝÕÚu2·I¬ÉNÍX �w±£ÌB™ I›7'>YDÔ’ÿ^m±f g!+“ è gჱÁIYŽ×”p"\8=#��! ß0!k)é3µÐ˜$l&ô¡wÁI™ØÆù$*›ËZ�µ@Å Cd�8‡ë"Û©¸'I°Gå \>ׯ“°Gh£T�÷ªˆ¤™ÐVpRÊSJ0lõQ¥ÏGÖ¿�ñ„ubM)Aµž¶ƒ†RM¶á2añ±½‡IÈ^p]Ah$s ‘¶ VËNb.Ì•R„6ga޹]×{‡£_x#Ï�Ÿ\÷.K�–.Dgrzƒ3Œ&mЫ2È…K‹-ä䑲àù—hEÄípoÄ\ò ä‰#ÜAEæå’�¯®aBw sP˜Fn�gLä·\XX;l4\x~ŒÓRÛà—U¬èŠz]eÎ ÀÎv«gÁSÈ]˜ÎܤÉ]º$*@¥”Ú":è&´ËÛ´P²�¯FØüÀ°kFä‘%!øvÀöÕqkÁ䔦î1> i¤+Ú4¦¾—(ï@~/€V¬;gž[æd¾±‘o¾H^ä ¤ùÊ!~ƒdµ‘óئ�@7’W6Ûm�°‚@G^:Pî·%·ëŽLÝG}rÀ�§±MîFÝžñ]¸uºãW?0‡§Çé�µ«!$Ôêkr–dÓá;hÿzÔN¯³ùšõ#‹l1ꕈOD±@x£´¨œŽÜÛLn\Ü-BÒ!�ðzº.e/ ×ën”#Á[Ðõà~ï¬ñ´Æ9Ÿ_›°#ZRhNêìÉÐn_X¤ƒ%z XÏ”JqA/�ïì.2ùÉï|žõAïäQ“B4ôwÃGóXQ¡€°�‹N7±c,icÉßÃÈ®\ð D“¸ô'¤E”@@œB¶Í¼†ô ·�±:›çjħ n.‘Ÿå™Yn Ò#?qމ€xb�– ç™È @+M°Âœj‰ép{pI`¥U÷ —Èòn ¦l@Ûì—Øep´¯Ò¯Óq)2Hu>«lˆ…òõ ˆôNS’�P‡¹���Ë€Á0"°Ó4ì.¼i¬ïïÁé-ÌŽ÷ùŸDÎ5"Í�~B±Àþh| Ÿ³Üì T—Ç™-L_åè3?F†9åý9£¶Â×ë!!µá{Ay ‹‡«7Ò·Vvœ§ò̳Àó´‡4åßÎsVÜŠëˆS«ã�üãNÙÂçšÃ)žT=çL·Ëûú@€b‚vâ'›yx+ü€¶†|• ‰œŸ#PE|h1ª5f-^ÊÇJ£èäçbXiïÌ„5Pv‘‡`©Ç/y—g+Ìa iHˆ,,À)Ò&Y¡­<ÿZ�'N꬛÷‚koÕŠÜ—\¦ò&íÿSBsÖFƾMbZ.ß  :!œ.Oh»�–?UÅP¥¨:L «J¯³è³l—Jx+ÜÀ 9œ/VA�ï§õ7€k~þ t§l˜z‡ îÊ]™PÅ`h&ýN‰.¹¡T] tüFø€i©B‚ N³³h+5�—žççšýOïó~�£g𠌜:[ãW�$�kRûUdªíQJYD¶'õX.C—÷Soj ãj¦‹&â�|‘"ÍÍU.²àpyQÈ#Ë ðû �HC)BÙ4 Ãdé_ƒÄ€<š=/¬«C ç�ã¸uîàN·)Œä»òõ±ó­óUævOdó¹WóƒÓåM2ã·4ì„÷18¯EuK±Uû:›_¼úK¸2G  U�¢°ÿ U²œ8à ¡èíHà@T;ìŒö78je´ÝùG|ÃÏG£P…jGÅXRü±M¬Éñ9&ÅŽz|hP´ôùÏ¡KØ0k®#[﹊x3Óæ(y’2´”•Í8² 8gD(qoÖ¡¸JæþuÃr¬ʘrâ=…&ẗÇ\�ý" ¼Êï-²Àß‹Œ‰µÌ¼ÁÇ©ìK!¨ËïºÿFY¾ªÉ8„ThÞlø›Oß�i.ttOµh `,³†¹Zˆø™l#š¯“÷¦c[ô9JoŸj$f\ÝçDðÆ=²é§ŸýK`!à}S rPÖ¹¨#7 ]•Ð+ªëç¶4†Kãù/¹´ÖÐ;BF`Õh°·}€Õ/ߥ�Ÿq@bÇl€”™ý’çßû$ê‘z£ºøb¶¦'ò}¼,×o´„ŸF·ºj~΢zиÊ[Ñ]�p5èË´†¨,mÈ "΢×6âf‹ìØtšÕ;„Xhâ“ZŽÄ€©VKšü§Óˆm ÞÀ/�YW¡ØÉº˜<ÒªÔ¯k¸‚àÏA*IBêDµD©¤…“ã;ˆ�x@œÍùLv\ 2²Òt„—r"‡uÉîévåe`b ÿ|Ú³¾¾UÐŒRg.ýÅñ€%�sHwÞãNšÙÎ÷ßDŒ×�KoñFgŽéüsîÔ·�`¾JÈ�ÖãžDÄ-ĨùW¦ká1iÃF!:�ð el“OžíÒ�\wŽ5G° �¢Z! °?’\eY•ò½þ}5¶ÜÍ.t¨§ {b~J—v�3¿�B*�±§‚û¶¥y6Ìy5”[ÆÞ—.鳚— ª—0`飜‚ŠSDìØåqKeABŸ@ÍŒ®>ÏvïQžѼgÕµð -ù€0,}¨³ëºe s1üÄ}žz„¿ÊžàÏ&çq]a¾“_"Üw�üV¿Fl�èšïÒ“—wÚãÒò VÆdpW~·•kíþĨÒôAöe Ï…¡w).†ëþªçX#W'”e®ˇ0(õ¬ð}b,±MMºân­šª÷?èa1ã– ùºƒ§¾Ý³·5ÎåöI§‹8ûjXLSß!»Ú=zó9¿™Ë[c/¨WÓª-Ž‘aÁÙ'pCswþ^^w~ºÝlë±ûv³”ðéaŽ8’eôBX^*êÔNj11”/½ø¤â§ó™] +_¿À¡MÞûÔ«W-êáFc&›2-Gºüåʸ$[ëã¦�^÷äTë±Ížëñ~ìÈw×�»5k‰ùÎö.?ÏŽ|},ën”05ÖÈ^$Fªy~VÍè=?ÝÁb©)un�ÐâCùȧbì¼ å^v¿ô+]ñ~x¼LÏojÃe#_0Ë0ñP¬Ì€=­²� ^bö['sÀ�YÞŽ` °)Êòk_is t‡¡",iü\”X4òQ{å¼ïñ®qw Ï88Ëð}ì“bž:ê3?bZ'·©ƒ³)ÆmÍÐeE…9qÚóÝcîORiW¿ ÊÎ îÖ":üñàðRÝù÷”�'çNGÉQ›¥‹äPûIßô±_5Ò©1¯:¥ÛM�€Ë¦þ�¯ÀÂîi;p¦¸{Vvzg ®BH³èØ‘�0#ä@^3+…` `!¡ÿy;„Á8Û‚¨t´M|P�7�ÙPX–Ú¾´÷¶Š÷J:nÔÌÌSuÅÏomÅ ¾¯Þ×ãëðlïnޝû—û÷Ï9½¯�ûm!8|ïÛÇþ)<ž—â¶Ëín-^�«÷ëÑ|öEà…ÅöÒu÷èe“×ëÙ|âóÏWUÏó埨To•ÎCN×ÑÆÊØS» ŸÖÑÆjú[åRßÒùÇû׆1汬kwY&³ÈQæ§ÑEÖ¿²I­ÑQxÆY|¢Wj²º¬íɤƒqäœ-qÅ`ï¼ûî|“Yñï²ì óFN¥ÑoØvë}5è—½‚è½2Oëît©_Øâžê!5—Æ_�ÙÜNÌég-d cÙg iÞZô[ÕE065?Çë± euk ¥Ô`p†Sz¨— Û{š G÷qÓ9Ç"B߸¯F-°[įØÒ—&œTèõÉew�û;ZiE×;Œz°Žànt­—ETÓëz±g·a« Ú§qšØz0ä­4¶Æ!ÈCµ†å®M1Žšñjr†|Z^ùa°M"}ZHዘ,¢BLÞœä㎭®—¡ZÌ<hÓ ›×àI h˜íþ")“$²»Á;Øçc÷£•�V4ç!"c¿GÐE¥‹|ªÐâÇç#ÐŽÀ¡¹;e/F«#ZšhÇàA¡D39) Úðøˆ!¼ Vrè^䉾c ‚Et¨ˆÂ|ØÔŠn—¥"Ró'îæO!À¼9&TÈ5ŠŒÉ4�6`b~~\ɨŽNó¬ºÎæìz_®ëµÚêý¾mȹÙ.ß|$³ïxÙ˜)yA2úû�4TªvR‡%ðjj˜ø±Ò¥§þ o�ÄÕyÐ݃ÕÃBÓR'¤•…f<ˆ^ÉÍY¯e*M¢¡{׎=îþÛ¡â²!è Zß2<²óQiI.é/¨;1Uß|ƘˆÞè\°Û×­ x­Fœtҵȩgh&毓C{Ä)3’½8†CÄhž1½WÐt"b|KRÎÙ¯F@ Ep×¶ž�M%C½YȇãUÍîÿ°¥ &a]‰A yGbXŸqz´P˜é�ý3Í„¥[ „°y·ˆÄñ‰òHe%—4/¶^­ï—8$‚–½¦o �5œíW„Àz‚ÞT™¼+ BCºëówy²ýDåLœV²Ð…EØnzúh$¦ yy·Çç;ÛWﻢjGüîÑøS!Ùó%]̸î�²Ð(~½å#.ÑêõÆç~ZêA½žÒ5á])?T+)Û¶×ÍåþZê:(h|j¹·n×£¼´~j“vuööÇÕAõSQ7Á¶L?nòYô:2©h Ø *<«Ü#¤˜&AnE—+ú'G€I >»"Çcª°ã¾?¯—,£&&&à´*€.¾Ï-ƒνUš½|§0ÇúR“Ô‘ æ,ý-“½¨ä%9ÝÊ·¦ËSΗû8êL±”îT±S:N7GÈæ�ù1g…èÇSQ²2‹Í¸"ø$]™< ™>'„fhP€¤äå•í…zÿ×úf B¨ªIò`ÄÑ % X“ðz/Õv¡Ìq¾6ŒÎ¢ÚE{wп|$Ö8Üq攃ơ…d;�~Ëìš°òІ3¤ ˆvËñò]6IP Òþ: �_93±‹cMÞñy¥ìá$4ý¢‰ÚgðÁ–ªôsj §y/QÜ 39‹oâýå‰eÛåŽM(œÍóÔr0"†$ŠÁE&�H]ähFèH|öù»üŸe ú0Ï,ídå²³qœi=VxÔPªu0}õ…‹ZX9)G›Í™Ø‰"ˆ¢Òù1�< yÂç¬ ²T(:F„g¿§¶€�Ð)ÂéS°ÏNzü$“ÂØã±”Ô«$ïã&k�jžüšQ˜ ï™å4a†Pó3*Æí©;,z&ˆXq–œf}†¶£Ûæ�l¢ä-'üóQIÆõ³ p ÖÆò7µk�´DqXorÖ£y öœ«–VÁÊœ.L®ikHˆínàB@Mø .s<“¥öòÎd¿F5ki€»ýec¹B9aç‹€uÒtKHj]t.¼qüWOœãf$À§=¶üø[‹ÏÀÊ'ç&9wƒ²�ÔÏNÓ=×丕­#@�»s&ô×î�ê³l“ô5Ì%€œX…â'r$By� ­¬¹Ð)�Vçíú ‡’Øòª¢°ri¦ù‹ÅüÑ9çq_jX«À Q‹Áýoºùéš“¶2“àœæ�óvŸ�ë:çjŠÅ™´ÙØ8ìÿ¬‚>”fNkÕ·ñJ'û³"ŸUyebÕçƒùK0•)I+ùû±ˆŠÃ»Ú¼å'œœV©„h)ÃB0»€” ™¦FWÙPâDÓ–åIàEù¥$”? _° Š;*à{r¼‘ÿåtà®>ÅUvØRÛŠ~ÕôœY/ª4a±T¡©R�Ò¹ hmF|o‰ÁîoA”QÒÉZÜ•¨°t—B‘ÒáL¸Y–«” Ï.ŸùF(Û åu ÿéüƒ7¥9D5Þž¬»¢XaÖ¢ÍGÑB–a|K °L¤¦í.Ï0ÇFÜ0†¼‚: NtbÇD޶ _l)¶ÜD팵ko€V)µÆK--ùÄXT9íU”� ÏXF}ÓóEñ2;ÌŠ¼–·d$Ð< �!—ŒT<‘°sÔ†U“äa„=ì"Ù¸™ü˜Ä¯naXœuôÓ3võ­P6¡méÜ-XDÉ ˜7q—eŒkø«¬þ¶Ø–*¡¬US³b-©<© ¢œôª<®ƒã­§T4Cz/²`néCIžT“�¿ºtn¢ý±‹—áÁ‹ã·hæÚ!Üçªzü©Õyrdëÿ´€bd½Óìy¯÷öø 8ÆW£X4Æçž5¬¶m‹-‚¢ºä—”¬IHª\=±¤’µ½~�·“rd�¯ÍB° �ŸÒl¶¢q°ÍØyØ‘ñØFHîýAÎß°õ&N [ˆÁhæk–í€%cM93•~Ëýèí4³V›óûz8ðš¼çíú)$-?Þð÷u}˜Pð6eìŠî+o£¸ÅXw�?Z66Ûb~jµ`â#¡†âÊE�¦>f¶S7)Ïc¤Ÿ˜~ðF.ZWG_Ö‘D€M¼ÌÇvéBæÃV "ÇO/øåRžrn;O¸ˆ»7¸Ä-Rs QÇç�“?M¨O¤E$q ½•šNlÛ"…;jª�o¾zZº™ÖN þÌ«�¯6 Ïf«œÔ¨¶K[Þ6EL"‹*IKIÓR±OK:À‹_õý!ü÷ï¡+àæ•É\­íD¾•§âÙ†.ë׫)Xõî‡Q}ÜKëP!æËDN&O‚a¢Ø²½ ç0Ÿ–ipUy&ôÃ)È‘©XÄÖ•ò`¯ÓQ'uSÙ(Åì{)*§c!¥ˆ-Kåþd2Ì{m²�\.×ß½ê‰|Ú÷ý:{Ö¡Êô%¾Ú�vô5:¡˜¿‚QELËfK#‚ ´E/Ö¦ ƒœþá$y–ÑÛ:°Ý±–XrF�aà¤Ó¿�ʧB+}‡…)ÌS£TeK¬ÛÑibÕ… ø8É’¨IüVuhæÄm£eË_{Z‡�èˆñ–/¼†JÂ9››ÈLþ8BÆÖåŸTsùr*-¬jæQ›i ÈòÌ™iG«;R˜é¡>ïâ ›�½Ø¼ k}"М1 €HÅ7Ù€ÔQ(@(V硈%0Û“åÇÜ©ËӖФokGÜ8ÃñÙ¶æx-üv² Ç‚Ôm2Ê=H+ÊE„‡uÆ"K|{^Ÿ¹€öð Èõs]¬¶él-ð¦T¿ÕM€òì3riØÛ^«ß ue $$ÃëßMÑ÷z€ÓsÇi"ÌϬüÑöÕÇÝÏ5±ÀéÕ[`ø¡ˆœxYtÚG%G‰¤I6wyz¢” `¨¶H½¹ \í|nªŽáÉD$î©h*[ÆÄ¥û+ïkµóÄ}ÔyìûÕˆhvÆl¤ÿÑ£yÆ…åÖÍ‘½‡é̯ÝÅýþ>÷t‡ÿ- ŽmGgÚ{Ûí”+ö^f‚}{8i4é`Éw¿Ëri‰¨ëhŸÀÊwU Š3ïwcøDùÌI+eo£Ý›Vy抣u¤†"S¸)Ljœç\yrc? i¬C™±kÙåÍÞlûöžv&/fÚEÍ l J=QÛ ¤:5á爫ílrŘfOÎØ5=(2ƒ£a/g鿎Œ£Þlj' f<µx»R›×»Žl ÐC7SæçáÛwåÌ @mjäßçÍË "{G¿âT8ÃÓöväÌÚ‡>5â—õîaéwIkÎRФ~4•Ú”‡˜Ýmêüãtbe)@LÕDðΆþÍŽFÖŠ/nÙñLmJÜÛÇÛýÓ©fÎÀÓâòfðô¬¬ð>:³Ý🋠Ë6v™â?Ö>ÃñTÜ÷¤]FG-zö*婪©;ÞíÊæþÍ2�ý¤µ¾½ë:aõ°-Š:·B0W7<Ì�WS3ü3_s°L±y®’ Kp^§Žóh3lbè$êñëY…Ç rì#º3ßOTÏ+úõîž#2a© tA)êm%? ÊY¯í{ËYoï<ÜÏRf¼ìÐ÷ÐÈ;j»o 3Ð>Õ0®6éÀÏù�Ú.Žn¤¾sÉt´þxå[ŸÚ4¾©F4õ  ¨Ï,¥9×olÏ|ÙG›aG£ZpDù˖ͱ¶/(*BUÀÔÇr&û^æØþðÌIψ]&ÒÍ´ZìÊ·–¶�»JOW4Ôò$΄B¾E/’­œ²ÌNoÐîa4ÑûË´ {ØPDæäætskæÇÙõø æ¨bp®ú"Îr¼yÎq‰Ó¦sáøÓ¹9ó¼OY=K<0=¨¾K,)O±) ƒP�7Ð�}4-™®@Œ&v9›a¼Yu�¥jJ7þ9 nß3«íW!+÷³îvɳ�m—võ\÷[mÑ÷®©©Ÿ‘ŠI#‹Lk;+�à†;M[ÒŠG�ïYûm `Fòy5"�²Ã­¿tT¦•?¬üže¬î? —O^´^ŒrGP ÷§„÷id{Àf€I‹³Èò„>Ù²àg8Œ·ˆùå™tgЛï¨òˆ¾q¾raãßT@e·O¯¨õî&‡a[3Ϲ·»ÌÙÌkI1rñà-�­€mwù~6ÚèÎÎ3µ}RÔøXÖžã¬G;5¤Iƒ+ 4È ®·Âjõ¡–)Ÿ»ºb3è%©íèO‚°»›¦weúÝ‚þ‹‡ð*¿/÷|§‰K•ºK2é7Öè°�¤S2‰<Á²ÝnHž�Å@Už¨•tÙð%\îçÝèW/EäH3G¹2ótåÓG…Žô´·æhºìܳ²“´j9Fs7å "@$(Ke¯¶”=‚ôcUÅϺäh"¼&ËSµ%¯Pw³%²JÆ]IHíLy˜_„t"HP55L¬u-}\ÔiñgªÁUðT¬N¨ Ê©l¸7‡ß®d7mˆûGŸy5¼ww*fëŒZëXR4hãÌ-�â9êЪûÕè\"_Ì]\š›9úC~«ƒX+·gÐÉWº~–z¥@f#RÄóÄQ%�+’ÈTç'âŒEPæ Ä ê7ò®± šv¸è¨�“Ñ$ÐÈ�È)9˜`™¤!脱•Ë)Lc EcB³æÒœ´íiþD„Èg…�8�´Ã0Fã ÇdåIDÔ‹p˜ª¥EP@‚”0:àÌq@£L2Ñ኉ÒkD `ÚcE²d�`ù³IPìS™RzÍ‘8áඇ‰á„"âp¢jðïɈO˜§!Nˆc!ëÆ¥)ŽŠà¾Ú´j÷´½?.Ûü¼œLkÝ.J4ë9–Ó,�wZܧB¯"öVà|pe(Ùäg7SvIåz5ucÐG?Î(c{«Q‡³Ôy¬’Ã*%ªç5¡GÍ×[H6Ýìð»¾\€z�¸'°‹©xÆ’ìæÔxùνÜõ8-ã9©¦Dß‘¼º¡óá´,ä$­Bã_Í_w®Ö~¢D–ˆä€¡ÝƉΚnnç“Ë5Á µ©?Qš¯…mÁúʺç¬Í,Ë·Nîdw”ˆZ¹6„–lg «6w*ÜL¨P­eoD鵸2UVJàwôþY‹SÐõfiyƳhH,ä{’ôNùÔP­b­AÂÉÚMGµð+1Ñ„±˜æ…´�)9ÿU¾N)‰¢žÕ¾²VZVïl·gù-»ŽdUsK‰¼ûÉO(àò {Y¼ÌP™‹_ÆžW�ÔÅÃ`±>.ä�šˆeí ­€‚C©}Z{ì¼ù…úÍ tàîEõüÕ ]Á»¿×{6òeý¥tÓáúÔXÑî$é¡�]ÈÚ.P±eù^·‰�lðO¿ž}š¾ÆÿÞnþ‚›…§w9„ FpÏúô uÐŒ/X:øZ(8Z2ü>Ú ÈRÞþJ* ï\Âä$ÿÃjp”E3 A$ÔoLŸÆEöËd«¼/=KN¥6')Ñpô¡8 õðè„ÒÄ_ÄŠŽ~z´)�¥˜¥Ï¨šQð6”•õe�D"“—3=1™.ÂAˆd!R^ŽEPL�>¿f `œÎRß®þï|*oÆ,2 xϪÁhù_ÙX Jò—ƒl@U7¡ �…ƒQÈl�ÕKô¹ª}»�¸:mÐù„)yÎd1›•¤Û#š© ì Ö+cîÎ,B›-ðÉhücâõÞp“§en­›vÍ1¥*°P—» ‡�šÝmë}oô¸(#Ça'Õ ½¸.¸=AqsÂüÀï»vAÃ)ƒÙvù>3û˜N!ÐЀï½*ØÐSŽÄ ë6ý¼ñÁNøÜ€gÖü»e~�ú½Ái2ˆÝÞ÷ƒõÍl?žÉÊH$xžGƒÇ÷ÕÞÝ�Nµ% ¼ë5]ƒd­%%å(ý¤d½Û»óWPùÚaøäèR`Ù•¾¬cR¥£ànà×�l�qü´­xÿæ$ÍÑ "$HÂdŠwÝ:áO Íè%�jX(Ö6< õÔ›EJ×é¤-Š˜â¾’ó”´ûù©0F”œA«ÕàrŸœÆ‹fõ¾Ñ ýõù qœ'qûzvÌ ŠÁYZ>±²î”~ äUÉóY툕ÞAž &ÌDò𥖿'ìc£oÓ¶2ÚR¥�±½�Ð éAßJ-D·…\Í+µ_JšªÀóèšW-¯¬ú²2N½làY/sy‰Ãútó·q¨3D¨+OT“Ç–cÜÉZíV£—ð_–÷ðþíú7+>¹Bü|1ž¯B_£™ú¯¼˜‘ÞßוL«GZg@˜’…[”@L÷¬Ë£žYNƒ3S /G[º¸‰q:Äí,dö°;{Ó“d ã ¿÷Õ'NNè�dz”Š<”brRBµFåù<ðÆ%®2jã ¯d¶l¤TÖÈJLĤMó[2•Q�ѤËÛrÕ�Ç‘–äWŒ•PÉÇ*¢I’È NTÒ³rDOdÉ Hù#n�›Èƒ­žâÔ@IȦj¬ ´ø3Uq•¥ÿÈAžk¦€rrã ÿåà2?׎o¦j N ˜ñ‘¬‘�j}a,‚&Q–ÌÏLÊÉ™èr�§ò1؇eM5VèmˆÁ—ž�z†”7þQu Ÿèá§K¥Ÿ @.#Ù•èž­b%w(Šò�£ÇÑÜÅ{˜JA=o;TÐP´á`<=Þé#üRÔy�ò»cå™À�`˜´¶Õ~:•îtºƒcZ<–ª˜\�•}„É—Ž×Zè”L¯¼m]VAÁ´=ÂÖŸkšž£ŒÄ±Ó QÓ 7bõ¼Šžâk‹.mÊ…,®lRŸÖˆíO¯=3×?>.l„ß9Šú«¹�“CvˆЋ�È�ß•‘xX¡üW•Ê=sÙš1´T¹ "]UçãOTƒý›g­ Â‰Õº›Ygñ¯]îÖÔèOº6«äiוÔÃø*Ò*[àJÜ0[o¹N™Ñt‹«ç¨ $lýÙ¾³O>ó(¯¢»õÉE]+“‰˜�®©.”æ2ŸÛ®¤!¶/Älò_Ä- h¶ê„{òàFhWgs Ô¹v¤PÙ�ÝX9?Š}¬Ô«n¡’ÈhÔ8ÌÓd�Ô&¯ªYßeûX™�±õð{�È•íô0ÍCÌýÓó°Ê= LZò$:e*µiì�nÆ)mj5=7(ù¬â†±©›kOjuf©i^iÉË{³ûÌÇN^m”ùD» ¥f8?Vè`Þ/ôòr½ˆÜ„Þ2�‰ówøùë­J#sÞø·Œª¹Å:+�ÃlS«„?ƒi4ðÍI¯]Æ€Žî6qSÎ�½õuüûx´¼±}Ú{ ¶ãÝÙÉÅÉá}ò}ÙŠ¦iÏ­¾Žðk –¨uq·´Õ­S¥Šl Çò…oªòæ„Nª­ûa_DΪ8*~Ð >¬+àæ¼ÃÙã»›¾ð@JmŠã-ÄÞ=k“ØYÜ~@óüÿK…&!µ¢ó¦¥› ûO¡îa7;® ß?Ȳ+›ãäŒPÀWešwZ9sr°¤L#K'ÃÄH§"99N5.B£$9==1MX¾5³¯x2dL�ÑwDOû!kNó?:!9ÑžyšJpÏk†Ò¤”XÑFÐ5�4…JRJÒT�ËÍ-Ì%ò7LlÖ(ö#so°w µFõ›ˆ€:@vr|HdUL`ˆ k˜Bµ1TJ`WWÁà$¯÷AYçñ3¦Ã ²eÃ$hÏ€-:"ÊüƒL~j çê‚`2s30¡ýà‚݉)ÿòÏÆ¡ì!d~Åë`M5ø=$æÑ�rðsÂIPKÊ,pÂ^!�Êbd6éw¶þç-ï×É:v‹ (!µ‰zkCIOFÔFÛ2gtÈ‚ƒm‚£Fk*W}`¾§²¦ç!%â—2&Ÿ]£§<-»8Lx&&QöyòT|Fûúø-fJSªêc/’$Yå/Úþ|Âo¸…ž‹J¼F›·ô|>Ÿ«©¹µ¸¨A˜ þþ‚«-Õ JÎÕ�}šÁ€ÙßS+©c3È›d¥KkÌ´þJ°†žU7Âv1I|¨�¢'| òÁ!…Ü�™2sð9u÷d|õiÓ�âW˜z`i…«¤/W‹H�FQ38Í–G¶–*Gçëz’#ƒr­yb±eVƒÛw 'Ú,ß»H$šYšÌ/ 8K¹U>¥I)Gëƒ$fŽCàÑ´V'²ÀC| l…Û�„*¸ Ãctm¿èsÓ}µTCe¦¼¤sˆÉPê­·¡us–@p…T˜› ŠŸ; 0ÑÕ˃¡–·>´'¿Í(gƒK�h AUQmC0ÚFQì!‘Ýv Ò€¡y>—y°´yÚ4¶õ*!¾ì¤H‡™´k²KäUdUO .Tƒ/k¾.†,Žž�[BiSˆcèãØ¥)GѯÄ}´I“kñ?m–l!X¼ÍÕéV+�ð¸j»Ë8~´?�±{}þ]>À}-<&|b‰YÚö‘ª¯PÐÑÝ4ìÀSÊ£³kÌʵá”'Õ²�’QFûÔüìšyÄo“�:ûd»b,¹Ç0¦š Y©å¦H&ºØG›Òà½iæktûþžt¯ïa‰æíÅØ¶-,üC| ø“b¨€ïª zéA¼ë>4)À;/ð݈ù ˆ"TÄY/ꞬÔo›Å€À{X›F¶0AƲ^"ÖÁ«¬£’\ÐÍ«’l¢a @¼iÝZýp[ßO#Õ¨(;2ØÅn·Eo’¹êíoo0¯®;yÕÑA�:iìºAó{TaQ¤3Âr£òýø;�Ì�‹ðY:$Å úÚJ÷Œx+ ¿à8:G©"7/.A¢íiQ4LD*Ǭ$8Vz�ZËíÓûí*W=fyD']ÉBL2¡bEãþÁÐ ÛÕ [´�MÎt—ôF?Ú�À'ă‡½æÝÛ)5k,>ŠâBù-‚7Ë 鼂:Hë%s+=Ç�j±uœFóîŸÀ^P£Ò£Ïø@_‘0ã]ï�†˜‚Ç­�¿ÙEA¼zÑ&Ðïwó¹Zá[Ö÷®šjn} âb@ú’Óý¡3a÷Â’[ïìܧީX‹G�kÕxÔ�XùååP¸ÛßM@ŽQºÖAõà¤Al a38Im„Yƒ§zsÜšûƽœ‘¦”Ý¥ ´ ÜÚ-7\"vÝ…±«üÍ,«üÊmð£ü¦ÿ ¶fÐT¥¤äƒ:(ℊ0¦3•_²oé gïûîvZ�B©ÄÓ2J1ìyæ¾ÿyý{Û"~?Áx(Ëo[a¤²>¡5Ê‘¦?ñ€‡ôþ¦›vÒ2¨5àf„¯Ø”…Ù«�+4ß׃ÁÑí3úsãÅ�å%`ÙÜ!ʼn|a�r° T§uWn* Bzõ³÷5Áù§ó ^~šƒB:kp©šWÄ ‹ O,ÇáÃ/Y‰ÑÛ?>ŒX£¡<©«^j~èøð2£�˜¸·~p> d ±·H‚B:ÿÍ–jÑTÁg=Ö¼ÈLO^A Å«*5þc¹{�(]S47 L=êó£~yûRs¥ &-DC–>òšŽ:DEË_Ö�éÂ$õ°Æ£™Æl»ë3›…|¤öNÍùO %2çkT`rÖó7 ùpïAfîÚa̘z y•�ìq=ß&ywqLÅ8Z�«7Žì[ ±l5iÙ™8úÆ †ªäù/-Ë#§-ÂP˜`UGÔ¹)ªœv(¤¼°ó"H©È\(Ú…^Nš'õ5‡£×À�|e0ŒÎRb^ød[­xýe)àΣc�`T—õh$rÜœšRŒ·†åÎPØ \çn¼ôŸÁ‹Ç ºó7Ò�TÑçÚ·oô¥šqËû·Z�gŠqbþ yaÜ‚ÂÇÇBêÓ‚×Á´S%»‚lÜ‚HD�¶lç˜VÆ _í³H}×ll[&é×eyå)7«~hr¬š¦aQÊÂcƒe³ŽñQÁ8m•�nއõ¨°`wH ˜ìêÊ‚ºt¥ Yöø¢÷²Õø¼÷\Ë9¯Dènu]Ž2n@¼î×-ÈÚ»îþ½qÑo¯Ðƒ8‹ 3S3‘��ƒ~—t»È|²-ñ^_¦ŽÝÂ~S»›¨ýÜë&nÄL%çõçÅvøÎváüö¸²LmtÏòVZÿg¹Q¼czœ‹]$ˆ‡€ÓËA,Y”+�”@Ç&ä™Ã‘×cÖB|O7dÕ è¼oÚOéF» ºÊ>â ã3Ž·!¶³ ¹5Ý”Ç!I¿kò‰]_ƒËÿj =áŒI¤6f°x¤&¸tEü‰k­]`wgÉœNþx-µùIF“ÜÀ‰3ÓÞ�Xn2çj5oWn[3Šei ¡#{ÃÂq?CwÌã˜S(ù€À©Þ#¯^1åCPAhö§Eñ¯žŠ2ÍÚ¯½÷Óq{Ø+Ö¡CNùhªžQùK‰M…›œ¦ä¸kªpvüî9¦ÀË©½¨pUƒU¨]|>Þõ’ça¡â² ÐQøË$áM›†eLÕ¦ä¶Á¼VS*�w& ˜þ=ù)y3ý&c>JW;µzð”•$²Ê#Ò_ ®`p¦e¾¾È’I�DÚVj,Åo„oÑ3—§º˜ð—Ö—„ŽiÉüt³n& ¼Lbž µÖab[OOFì�ÒiB X’×�ó÷L<°qJÀÑòê ±¨];¶PóT‰s¼b�iÉɧhéTZrÒÄXÓàS¤OW�HÊW×Ý'ã·Q¿7ö~Ü¿¾j|E³¾ÁÖËaˆ�:þµŸI¿á�Óf9ì<ù HÎAü(µQ:gðìÒÚØ‡€ÎîYZãõIF‚ôb –> Ž#éÈi€ÒàãÝÊÿéÂèXmò#x¥ø «öï'�Å<,„R° 5ùëC¹äÏ…¥¨Þs.d·q'wÿv»q�âWà&ž‰Ôq7rw6.@j¬Aø‹[Œ›þÝ˽=U_¡X¶ ¹Çñ]Ÿ#sý{ “ W oŠ…á p:›LÚ‘±Ý=;Í<'‡ „Æí�=ìI£eXœp�~Ï 8–—a©!‘–Ø(Ù¯òþÁßµj3ŒÅqb׿õ ŽãÇ€!”õëgmZûSÕX�fÑ&·÷™Ü}Üø…ôxG}Z‚Çî+° °øÌæ…¹PžúíL UʉŒøY�Ô�ÛÁZSLͫܰTm!™¯dz÷ðũő¦·3°·‰GðøŒS4H^–r´ôµßHw¼Gðg"pfÑdÄ~ÀÓdÙQ�XÈjA²V´lÔîÄ»œ ½í„lÅ?)ï>²·º¦öMð=Z{’™T4.rjR]Nƒ7QžÓávgZé8µÎ®¨îÌCZÜ"ÌnÿÝ�“ÚØ Y†«Ÿ¹²øì ÙlçñxçNNJÑPI¬ÁŸ •rûŒ7H;í 5àÆÝ֞ē³ÕÒƒû¤R�JÏá2–š'ž[wëƒ!&ó÷S5Væ^ µ±³õ_«V1�;ä@øÞÑc|r’ê$wÆ÷ piO‰)¶ l“WÀâ^3kboõíÅ$§Õä?x(.ç}<™Î4;µïüÜ£öW'Á9Ss‚vEµÜ Jâœ[²$x‹©ÚÖ%-£ÁiÂCȈÄèmÒ89ñt&�ˆx#Ï+ ù-´TQŒvtK†J@óÏy� °àKJ�F ‰Þ8Z+3žÆb(ÆèŒo�EÊÞ—}mßÁ¯{ØØ|u©Ü2wM¯‹SS§¦#¤¢F)™¤´"'aEŠCøïž‚í hƒ�Ú ÏÁ’X½:ïIñ„xû\®‘„þ~SØc0†ÆjÎï÷ÃDh¿;Üjà¹3ÃËéwsËWNª<6JXTËb~è‹Ð:úàù¹›4¦×ã ¯/ÅÈØãèà:ŒhªFŸ1Ãs÷båc? ™Úâø±uØÆ,G­�8Áfé^’ jG–vÄÐZn!&lƒ�Ké ¢X"¹ªj—÷LV]¸` Ç/§šòËe`y§†=¹“�WHRwÚÈ¢2Ú‘¨ ~¢Îi=°ïÌ1®,Š)Ú ƒ'‰! ‡dÒÂÌénR=Š'£ßͪn­çÜgõæeß²5ÿ4V/&)çáœ0~‘ûTêt4k$l O?°EÎ ‚grû0˜GynÙð®ùfHµ9¦åýµ{-H ·æ�âï€÷˜Iç}#àÛ6«áŠ4HYû5û„/c“&c T” ‚Þ†*$t×¹ýù—”ݪÒ,~Ëæ¸ó&]"ÞÐxsP .d´ï* 3‘[ž±hÒ+1UÒ^²ßçu„8èq¯µE&Eºjø†àz·a¤ä¶?¬�|h‡ÃEMòŽ�ºîÎ÷÷YÙ°O w¾yM³²½Ìà¾ÞÎÔîV ù KC„ÖÈA* CFXØ)¬u2Ý^É{e»E«‚€êžÿ(²Í‹­12dJqå�[˜Ø¬�‘ã—­wK\ˆNËò¶Â©Ftht¾�å œlŽÔã¹íØŽfœŒˆ.éXC¶‘Ækë`jÍèÖBú[ÅÝW’�qõéã–@m•Ï�:O±¨”Ý„Zµëªè2ÏpíÈïžò‡ädgÇì+Û°ûßÒ2³ÜÙÓ<²Ö6CÏS(:²ïžPÒ†×Ò¬1䊭o©$k|,^úbìBV7¡ÏS ±¯ÍÚÅÞڛîS*))ÈÓsj«¿ àuÜÛÓæÎßeUsoABÐíì_Ig\ÅYæcJÅÒã‡Óg)Ì5²½[°L®/ðÚá(·3üP ÷¬OÚuç@„J”ÿöo5Me+5#XÑÍLuÃ,Ãÿó«�±’*óYY‚Ú& 9�U îöp �"bZ¾?œä½*Ä´"ÈU®ø•ôħÆ>l¬ ²u˜å""P-Lôøe±N޼¥îÞ¶†®‚’¦ –Ž> Å‚Q –Q 1‚>’Š<òêÈE Ì2ÀU«ªýˬÌÂÈcéªò ,,2…m$ å…yEyñiJIiŠåqšñÚŠUå +ÑùtdðD€ ðSèÍàVõ› [¬íÉ:R˹Íü fk´PÊKÁs¨*ÆŽoß]‰Bˆ%7ä¡`h�Wù`œFÌG€¥@ª¶®óã/ÚÚkÊÅNÀÕš#&~x„víCóÁEí6ÅòÁyÖÀV5št†!8ÝÇÿ1j2vØìÝŸj0q‘Ä Ù>8W,aDR@Æg…1r@Í“ÉaUc´ãÍÕh߷ރ˒ÔÜ¢H(ó�ˆ™Y˜3]¯¦¦‹¶Yò�¦VÇ*é$?«’3E?ºê%´”äiX4Ò)N«5ÑðLs‚d7FFH¨¶²P¹âS¾4R]ÇŠöcûÍe�Ë‹Q1uÐ'¥‡Y”ë—dÙÎe5K£JVebqŽ­÷V˜ò�ÙÜ_9sÏéð/°yˆx´5íõsÅÅ‘†ÅŽdìÇï;qVãô&ýëÂ)¾6ÔŨ]È…Öƒg–Dÿ9BŒÉKúÂáÕ¯ñÓîC¨æŸðfa�ÔiRá:º:×~eÀdѯTU­µ58fÛŸa�ˆ�Œ—Δè_µîÒõèû®Qvý5EV¤ÞÓ’+qæ2Âé +uNÂ@ú›cƒÙä¥}Êôr“ 8G‡?D6Û‡Ó⎳앆êLýòþlZf§‡³»S[LfãÅ�ŒÀ¬ ¦3ΑÏD×¥Nô™ÊÝ›ä@]n®›™�œsì^Û•Ù4–"ã€<ý'ê”ù"Êö¼„9ÝY´qs’·åA6%J9 f]#ÿí¹îut§è�.ûuž!c©ïL¬QÁŽ«øÇ]·ãFÚTÁíÏ•ô™ÇŸv<9Ï>*R!¸4 làdòèävɃV„B!W A+@ML©0HýãÍÚW 6fñ9=ùaYŸÔ#×vPõÎÃËMsþ …‰'–ó*ÕÂò�£vëúÓ6”fŒâòPÌ®OªáÀï) l矎Ù|nç—�¯¯«åw9³S™ 9nmëÏ££`IFÅÑíý¯ŠVuÊ^[”=Mº•Èáqè5~=fn v¬±ù‘Ÿïô„`‰Å`òÒa_s ÞùR4>mPí§B;Æ›×Òîõo–Ì�øÆU’„q«f‹êÝ•(XB²Q3ÛË�h_¯Œ‘ 39Øo¶Ô›·µÊh/ã³DiZü\ gÞ\‡ü¨ÛÎ>¢9sìUQCøj]MPùÚhØoasd舮Ûã˾ö=ÕN^‘Ï�=:a̲9ÏÛ(x"ýMláÂa•Zn�Žºª×3@Ak�ÕeŒmè&/r†‘²zENÁ/ïvpé�‚§Q²qPã2Pþíç¥["%Èâtu΀8ú·+Ôhôm‰ZæÎ¼šä�ÕÁb$b M­p‰{€ ­äÝ#K¬ݤۛí¶BDJƒªÉÌq§”-K—Éñåé#)W©¨#*ë´*ÖQ?Gº¹õ¡Cμˆ‘FÃájéöñȨKàôš0£ó�ªr`uÏ¡‹À¬ìôÔ—pB+â-364B¤¯Â>Ùx‰æ”A<¨Õ�� �ôO”Œ¡Ž´™NéÅ•4ÜÐ}»MÏë§?Ó‰ó”i¾гœÙªDy`Tv†,–§”ºÐR°t%˜IH ÞçÆAR¬ÅY·ê¤è«’!â°K/vñ»ÁÏx©{UàH/ ‡V@°&«CÍ“ ƒ’�’eæÉ­9ü–‰)­[ŽY5R&_$É»$Mmå\¾V× Œn:Pjº p{´Ý5ŒP'�âÉŠhäõ²'Lâ䨄àô�jèü‚é2j<õóxË“ÏwŽÏN¼àœKùÏfò2±Çº1M¹Ey˜*ç½7 ù²LlEbÁÞùujÞúèÝ|gíÄÏ>!ðÆŒ€&asñî·?À¼r*�¯íœWªÊÚ;ñÃ÷I«ÊfZ—0ÿ¸&78uÏiŠlGýßEéâ''àÐΦñþò².“+ˆÓñˆ4ýVomÞþ줲wP½¾CSè“ÿ04ºÜ„{»g'öÊ2 Œ “ÙÑHÔºJT kÁˆ‡E¯KJ%­%_£@ þEÏÉ£ê´wÝfûîÜÿ¸úÜ~ÿ³{{|àuï´Âу®.;¯®k?ÓäTŽ�£{“«‡�÷¾º’LEmå?iVŒ£‡žíÁo[îÅÈžK„ØÏ¥d÷ôË<*r¡øfË_]•E�‡Zîÿã.»„ØW/QRY.`¥ðDp4R#‰*!ø!d¨þ¾% }ˆ´â—äTÏî eî¦ÖœBÁÈ�¸ŒJK…ÄaHÖ¶n—î BhJ¦ÚÓéwHÔš+÷mÇžÞ‹¡¶M u¤�Bt2ù m[5UC< �U‡)§ßó°î}aÖpþ�ÉJI+�—å”B ½×$GY×Qd%P¦÷ÌO€2 ™ja´¯Aӆߩ¬x4`@íÉOEìq�Y?mLKtMàs‚› 5ÈhI—t»> ØÃB†MåÃFÓz9®"8‡S�9)—è"±Uk��‚™øÀ([f¦�3ãmEz¢ªe¤t°/‘dN8ß!XÖãÒzFç~e ®¨Ííkò—ÐÁ÷LËÀ!+‡¡ KøjsÛ6·B̧gÛþHÇTÛa¦1Y%'lzÌü –‡7Óè5zº>©ƒœz½†s”qgÜ�²(¨ý”ˆÖäŸiÙ_¬}m¤t‹½ëæÆs[�‰ÑêÙ³¢ð†]Ç,Ù‚:Í¿²ë¾¿V“>ß� <'&Àüîw^Ê”Îóå ¡ãq]ì¿¶^ôþ²ç;'|Ežie"‰kmÝKŸf­àÙ��´hEòfÔ¼’%‹cD)Ýg‹'Ë©2|¾t: OÆ=x>U©�׿*a—»¼þ<Ôw¥…WÔ”‘"�öð”ÞáŽEô².L,Gߢ�Æ 1pÕ+R¯£ofÓ=eßàŠø^ ®¼tý¼(øÞ¯Àí·^(6 ÜsË7Ô Ãùi¡CìHöþÃ"ÁÈWÏøèeÍìŽgíúáó°þLÐçÇäºg#XÌõF_†»™WþVy ÒŒ!Èa–±S©>{%¼³£H¦rØyÂÇ­ ‹!q Þ èï›·½Þ!k5Z̨]-ôŠM‹”0°°™{'Ÿ²¼,JQ׋EAš2†ÀNà�-HQ *AÜ}„“ >1oF™Xµ6 ÞÂbáúGcº˜)›NÉŠgÀQõ“zÜ— u»DøÆésÐbÜùªâÿhÌ‚ô/eüL4h0C>(ÐM¶Šâ(e¯²bxT. —?¤éÑ,é}ÐMK:f[ ¦,þÍû…Òsïs“U̎ݪ†ßât^8 ’ ^x› ïÞ,')†¶þCWæ%^ñU¿vÚJÁq*‡]PR݃¦AÌ/åРûCO˜!‹§ïtßš-K>·å+\-„�‚�ib†á¸e þïn´éj;êNÈ<0Æ!CFÔ‰Æî¬>Äâ> T-zë ¦4zãù·WÐÚ,§�±¸ÀŽi?q¦•òß \ä6$_~FÍ*Ù’:S ÙAä"^õá—,æÿ¿YLaÒ ¥fzÐÛÏ‚œ]Ý1 ‘Ð:Š€õÂTúÇøíH�é9,j`+Ó#V$rTÑêÐŧ¸šx³“0KOW;«·÷íü‹ÈoØK|:§Y7I£g£,gÔk{'×[Òm%ë×Cÿ«¿CÙœ‹›+Áðý"*}y’9°ðð0;;g@,Gü"J@•Š.O…°†…û9A"-ië©ü,fL1G‡xÕaë‚ÀSƒìµQ¡)MæÕ©Yõ°m3Ú¨¸ÒÞaë²™K;¢êE¢ó~û‹ëǛ岓|øÁ©Sä6¥—íwðȘÚ8ªl©]BZÙgÓZÙöÖÚJ¿âÅb•Ñ!‰@!Z<±,›¤l}²Ê�V¿üŸÌš~ÛÇâ” ØAÞö 5Ɖ¨ƒs7uU· ÅZQö}’ÑÇ2)�;±Ë#‰È%Ïx)7®?B©1ߟ½g‡A0¤z�ËÏ`€‚ÌpvbÒ½¶ƒ�?°]ÞBÔíH\üă^)?¼…ãbz°ïÞë§¡¶¨WРƒ´Cš$mÍm v2¾+ð÷=µúè‹ä³Æð�x—hHÎÎqUéþ‹1¹ "vwñ–i0®Qp©m’™ÿ¨½=BYëà5͜غ£äÜUµëzE[¦\yc¥9+Æ,/F‡šh—�†[ˆÔ¦XÏŠÓöÈcF“Þaí|ù5Ê\:ow{ïˆ,Êp«Þm�"gf@‚åæà¤�@ÿiÈ8|› ™ã=$ýºgyZ“ÄjLªó²?Eb z›x°iúz¹†vÃyýÍ•4;3ä‘ Â¤êä‡Ì¿¼ª·‰!1m�–KNfAes~{û.3ªwk3J*_}aÉŠ�›"pUø Θž  ûœ÷OÛä 8`Ç=kû?تÜûáI}5@Él¡Êo,õ80UDpF¥øG@æ‹)|ê[? ÄŒµ®;ŸÐÝÍ6?ûaòa‰Þì’° §{ÄôSCzûqò?¿×¸Cé<�£›<~�nÊò�n|ÊðzÃÿ¸¨¯:½™Ó"¬¦7P^u>·1¿¯¼ü€áÜ)ËX‡+þ#mjV1ËA¨–iêuÉ5‚S�²}ÆàBS÷,E¹~„n÷õî7tQ¬4*ÀX–¡4 ž0rë Í–¹"ëñ›”´¸ß¡¡Þ=¾¥õãFøí©©ÏÇgß­ÚÉÛÙHE§â@–¿vô–KÂI'Ìp­’“ÂS›‘„Ųt¦#sº8%J)±Ãñœç'";°,ªLT¶û“M4j°120°±°XØ›¸Ó™„H¾Ìaýú•Ú¡‘à\ŒÊd?å°4.•Í•‘Í�Øü % Ðù¿Be±?ñ}®.pœ;\m‡ /élŒáÔþ�ËW¡B�¤!™Öú8]Ç@̦tN©ÿ;bXþßc Åÿüã¼9Ú*3Q|Fq6sr�mM§Ó™°pKY²ºÒ¨=ñõdS:€9~Ø"4áH<œã,ï‡,NPÛA ö€Â—ÿÍŒ`÷í> @h±Kž‰�ÒY‡Üí× áVÛä`¢< Á­+¼ü `h‚øFy ²lkšûL–m˜BñÈÖ®O[&¡=ú©˜È†u² ¹ÑpϲBéê5àþ<2ÃYÀ‰ñ½F‰Äý·“óãµÉƒ¼lNÇÿ[Î†!¨ñ¡«¡G¿-u%HÊ€R­ÒÑ’3v«×RÔk Èôç õõé?Ú´ ýL· ý‚²�[ΘJÈþäÑÒ‡YËJZ:„Ÿãš) @[J{YŠHXêJZFì¥se$J¹‘ûÎÕÎS‹ÓŒ‡ÍJLKÑ�¯]•P‡Y,œ˜ò*ÖtL=u-¡Ou�?^¡Ýõµ c¾‹dê\u¦¡òËÎ7�MI+Íñ³�T�ûf±ܨ8üðhLšàTYj]]Ãþ[Ûh}ª‘àí ¿¨ìÈG×ÔO°%�ìÌÂT’PãRpÁ�£ÉŸ¸q�”´ÄtC“2`ÃRÕ¤¢“â¢�¶aÒðórUQ}WC Q/YK]‰�ä|Ĺ8Íব¡'C+:ºÅ+´wh€hå¹UE qi‰ñ))5ñ …¹p­„l ½½$Ť ¨fŒ2JÖZ’>¼²­ ãÍ¢î(€ þø+àðõÿë¨HÄedA€¯æf–¶ßrtxo´PšÇ†"P$¡¥¢0Ó¨¼Úd5™4MŒºÉÑ+¨}^¬µ‚ô a�‹ýh7BÆ#ø+ôM%^YõFz[R }þhû›xŸ§žR?9RŽæø“n·ýþÚÂÚø3‡Ó€!±ÿ!úŸªÿÏÍxI(�JPEIúPEÃZ#nWÖ›–h‹(ለ䂺ªn#Ïó›˜uòs™Oˆ{;y•ê¸8÷Ê#r$x”çhö=t‚{8$3ÓÕ™'ï{Ó·™÷îÓ)z=XÞ¯¯s·Á«ó»Î«XtüM m�q�š� ~Ìr)iïóÍ›[168—Ir§ð³ÓÕ�µ\;º7d ù —‹³�m�ØÙKÜȵœÌ¥ñ¸”"‘[,¶q�¡z­Ç08› ¬/�90wÚAÐYBì_s+8¢°bœ£|�l€Ð[Î$0n/JBTý¾ÞJ ›Ä>ÏUXx˜' 6ƒ~Á‹Ý7ã´è¬¿Á¼2xÆöî)ßZ<ÎÑtx*×1*IJ`0l?®ó©d [hha%Èb{~@;\Œ«K7î š†àK®°«‡ùs¢4`fÚ…áín8H` U€Šöð �ã0$•_Fá¾ÚR�AG£„aŒ@ª·­;€AYWàŒ¬éÕ„¤þˆfÛu. €î;Œa™‡fdH�ãæü+,a…#:@·ü.ñÝ<ÞÉÛi?ÍqüDy;DÏ›ÝAì‡bVÖˆ•¶ÍB0ªÓzÅ{ìQ©ùê’ áA¿e<›…´Ìɲ.Ö7PÁFúœ~IRþ³€•ìO^Ç%&í9ljFÀŒaCÈç»ýV—öK�/Gg 9xà á É×uË(“Iþƒ(™:§ø~“|Z„ÔÝžÎ4.Ka¾XßL9n sE¨ÃÎqܼôeš„Ç[wW3q³óôÐe›@Wá SµÎ–-½1o\€´ÆWÊóºÌÖÉ7�¸Ôtb*T–œò>˜Àb…H$Щ-:O/zu™MÛù4'ìqyjè—ÑYëÚ’LÝ;üÕJm1]œ£•ŒºÀMÜïè‚­»~ñÿÞŽ¯ö&D^gƒ·šåù²Ùº&G´bˆß~œלÊúEOùÉ)])á GÓ‡ª;ÿ³­…“o`)Þ²ìÝ ƒXšò»›kâÔ×€ÙüäÆzü ]`YàÅZVÌ D!} :õ�@{‡H r×"¤=$X¿4“Ö„æÇ+WR£ÙŠkÃËJæUÿ¢UuMxŠA|Î mx±×"㣃�X¤)Ú8Lž-± (×ÖP‡€Ÿ Œ šqy€0ScAöÐKÖ²iQX&±B6´*aͧ?òÜD柴ãðFжHâO�E„%¹NâºhMᯠÉ)ÍL2Ý¢Ø*‚»ã1P:¨‘–´n§&vâh3mUH’4Éñ|»ŠaüÔgÒ¦«£À'¯Š¶ôšœ ôYàÜÃ3ÇèÆ´ÛíÔƒ’Qï+«® ËîÀ Ψ¸�CùèИHާbÛØyƒƒ0PÛÚF $ÝéLldwÃjK¸ˆŸ ¾n æaüR5 ûB>Rúbö0:¬Oåú©ÿMnAã%·‡W?Lp£u’·�àa‹ç*죇,Aš>IjGú®J6·¯‹Ý4½«‘™Q-¤Ó˜b*“É%<+"Œ…–·— æóÁ(£òáp�‡sÓû]èMŽ*³LÎû‡I>é�XÆ¢\¬Ï'E+ Ü99ÃÿþO¡ öhËE9_X¬t¶a“³¹¸É{ù|å=ôDmÁfò>žÛv¤;Ä*«¨ ª·ŠƒÕËñJfÇ@Ž.wüzp®¥¿ìÚá<òèý' ˜À�s8¿™Yd╨C /&ï�fî¼iØâw; ÷PRüâ¶W;kÒÚQoÇ×›}S\磚†à¸ÑïôxU)ÃpoRâ•Òñà2_.5øàði÷p/ú_'ªŠêóƒuÝ̪}eË»ç~‹�|ccŒÀ¯‘ö¡žv×ñ+”ÏûŒé…ÇØð8�×Í}  SÙ¼O°ó{yY—ŒrQò%Åžê|И-%g{ëõÜßE¤±ø º ‚ší‚Õ?åGÌ%ã<«:ò6ýªñÚ@õ Zpó?hš\bCa`KvÍ –ÐÝìia›lõ¿¨gÂÿ#jj‡ÚÅɪÜf�€pÛx§h{‡ê£‰"ž.ü'l‡”ý@Hø,û$Ã��AT®Wn³Ý#÷‹‹4@dTÂ`4ÜÿѯaƒoÔY«ÛÌØ™º=Z�3Lcx a0%ù@îYÌ, ÌŒ cSRÕúêʽ�Æ?VfŸ2:Ô ©™'=�pcÒ°�ûÀKÚÿ -g/X+îŸîïo루jé(QïcúO“/ Ňς²ýL©½IõŸ”EþºÌH–¨�‰5ýznÞíàâ%Ç{ãG¢á?ºì‹ �ä&šù_0¥7‡Aæ˜i¨,Ö®DöI‰r23i»Û‚Úb„ƒÛ»bù8uå¬ ùú  AšVunáôà`iõà@yNÑ^yŒzZF’VqLNI|ššrBš¶HÂ�áô@Rä¶©ˆ<ŠýovR¨º„h… ‚�äšsPßBGAÊÏQ3‘c¦Ý¤“=5…´O‘lû¤9Zƒÿv{»˜ž2@ ËÎ`ðØ¿\O§‚,±K_²Û™æÌŽÕde¤&¤�€øÕBr^úùüÿe(ö€(ÿ�Š {¤d7ÎUÝ�‰,뼡xíø»-}Œ‚øÐ€WÞJùߨùßJp–ßF ý³4ü÷2Q´º!)¾±hÀþóÌ_ùŸÔMhc3"ù=2öá…ee ¦åÞœþþê¹J„ 2a�”R®“é¡*åÔÓõ| !ø¡©e_0qLØ8º†ºHä´™7‰èH¤§ä²ûSÚ”fÑØ³AZ2ý“QªAÞynf£[k7pdBlüo×Ýÿ/SßÙDöƒÚ¹bñ«xB}çrœ)ý_ÓW-½–úo~€ú¹ÿk=e€\Æð�ß∹8‚èg8ÆR‡È†ÿ[y™ZX›XÛYý_ˆþ³Ûn`Øœ»ÓަpµS8j±·Ð‰€�ÿ¿Ó! I1uˆ:÷þžt¼–ÀÐÔ‰ ÏGrU1?ãï‡I¡†ÓUÊïI¡ÊQIï9Ëÿž“bBµ@ “Šð·„tš‰we–ÆVÆö6ºuY ‡UêÕ•öæ–°¢“++ý¹`Ê©¬«ÆBlÔ÷S, °ÃOS M_oKS †°qXÓã°8 ‰§u”çòs¢ ÎÐl×ã­±žÛËÛÇA“±8ßÇ×;\õ—ßEÒÏÓÿ†sré_:ÇÑ‚r‚*‡I$æIé_�Ù%ï 9¢Ë/c»Â‹ÄõÊw›‚Œ…>$Úp’ 0àÀÃp™áç hg(‘QßQÈåô) ‚¾¦�Î�Èr¸9A’3éhO®ˆÊÈÉæL�4ʃÓø³ü…�eRÞ=ùäÁ‰°Ý©ŒÛ bÿ?Û‘»9ý#�Eƒï�ßi2j½IéËyÝw °\ ËpÀ÷?„sàëi'T˜‘ÔÏÁÐ, K�@”…ßÿÎx…B–³ ŠïeœÒ—r'áÿΤËxédALîYZØŒ‹vBè—Æ"€> :ÿí·Œ"ĺë�† óVI'rrT‘ë% mwf:4¤NKÑO¿kC§‘C?œ[ÃzNó¾W'½¹——�w9 \MW-¸D%¢�9[œ”œ�¡+[�$£¨\P�$—£�S¸´·‰u~ƒˆŒ€{ж[j:ɼ¤ªÌH+ùQ,7gò¨éz<üy¯W„Êç+9Û@%I1&˜DŠú_& cd<œ'ºœDµ_µ¿/º Î0* …v*o:*â#Jþ}üA&EÍp3«(i†é=¾eéË01{‰Ô¿ièht#AÑÁ7¨ Ôóãsø3øƒ«ìœ"Í\SÎU95e€r’õ=¿¥)Hó¿´[ƒ»¶óvôµÐ…Ô¦«På˜èk®,纗Fž¢”õ%«£ÖÊ#ï_’Ä$)n/'ð²$£[iBS%÷_ Šc;k¬eãÇɯiùrÝ�h4Žw¨ÿfã 6Æ?4V õ˜Yøʃ¨¯¤&pèà`aké­a(k …Z7OÜB@ÒPÂÒK×ÒR’J¹¬�„‘wybˆæÿÔ:&–†¾SzFœFR¬Ó3dœ#Ä}è“KtHÓiÈ$R*B2Q e�Yê€ùQ‘¬×0£÷/ñ£ ¨ëñRWÒ €~4SÎ?£¯ø 96 ¾jÑþ¨m*Ãsmä Js�Œ� ð »rªù? YŽ6Þ úc‹ã¢ é¸0r^µdô? )RüÆV×¹‰Æ…<‡�öZõÛ|ò_hÈáÇâaÿ+Pç¯ÓƒHª=SK_,ÑGQCD ´ÓãØê‘ì—u×fGôÓÔ3Ò³R2ÿÊ+µÂXSÎu5×ߨMc1Æ›³)õ-e«šqFœ�ÄY©Ùƒ¸Lë¡|âø ¤ÜôÔMÄÜ¬Ä õ"@Sàe››“8ý¯tO®Ã#×ý¥q&¢pd2öˆ$AŒ^a õ€€¯@2ðpQF“™ MÈ"¶ËH‰FLsÙÚbÞ¿Í(«Æ°åÓ^ ;Q@2ÿ¯µ@�%þNPÈøP�Îfg¨)*—[�³=eIëó"éõ«õ¼mý÷’>, fšÄTÿ¨£ãÿ:5D•s âT2RR´ }K`+ÿ"­AZÏþ:Ig°/13KM§@A'A3.�c][ÄÉ©žHŽln® àƒ8ç –ù™´Ã )h-ÌV"§‚yF©â¶_eI°ã›S¹ø/ÅŒ‰6ƒ ¢éÿ2µô–µÔõ”C-DüŸÆÁÿÊvÜÛQv”hbzÚÙ'“C„ƒCŠ1Pr Å ZêÐéI©q‰II‰ôŒÁƒéÄTf¸à=Ãgué àqÀ#^ÅP ÖÿUq1[ùM†=Zú”âñ%a~Ðz`‘Ž3púÊ&v`€¯A£ˆþG‡M~ÝÝý)wi\¾´(©mãV,µÙë¿¶úNܰX]Lò`¶¥ÜRï:Ö�Ñšÿ° ·øÉÅKwû’©ôd® xW2û1×ð¯‚jÌÀLn9Ò™~{|ÔÔ¿xnù¯.J+Ð^icX '$œšb䇽}ů…ü�_—ÑR²šË[’3îhP�z_TBÛªúµÜP9*V¹ú�ÈË=“K1þÅâN¢ó†rÆ"üŸ„v*6…–ÕÀ¤rÁÎ{€‡ùø9!X°ÝFé¦\xn3uv× Ÿ-¥°£#‹ùL‚°(ðÌ%í·aZ"-¸î£h«¿¦1E˜nÖ§ëÍüã2¡(Yc^°¯äqÔW@ƒ(9(wY#ó™=³ãT mg”‹Ýhå�çF›̊￘×dr…µæèdRh }h‚GÑg.›b¤‡TÞÛ¡øG¬Á|U_ˆAÅüfïGìûufÊÑè>Å�m’?tM`oÙEQá´ù´m×'z³ÛÏ[2ªdU°�Ü#Ù(᥌´W·'¿ƒRsÚÝí�EÞaWóíOb”5?Lu¹Ã¯9ïaQ×¾ò6†·@Æ­¤ ¡¸ér¶Õ¼µ|󩻌±å-&‰IïÚʧÛ(Âw,ÇIÔÝlä[bù�“^¯?çÊÃb~õ¬©n"âW&nº¼ºúÊfá�gi YÏdZô÷ŸTÓóµúBõ]BŒ½ÃìÝ|‚r…ߨN+$·QÙš1 ñÑVAƒøq¹y`@R°�ïŠ4ss!¥.1ÆCñ%äŽvsʲË9ÈÐ}€NyçyÈ]HÛ ¹‚2ùrî×µ '7õ>æ›îÛe’ˆÕ…Uä=‰Ápkï)ïú©M^¿‘÷…цgtÃ_0vܹßèþ´Çë”JÇŒmIKŠ=~ÑøÝë^�,â̽­fòê úq Ͱù�^f¼'ãñËúÎtznX�W5a%èõ�‚憵\ÎuÜÍdk—s¾ŸŠ[÷Ä™Aë£?ëÚ|Äͺ×Êùï’ÆÙsuî!4¾ÜåÀDÆ<†Ìmtï-{y†ˆ3©³Øùä;Ǫþ²ÉÚáýQSvÎü1[f^sÚà mÊÏ2l.ªœBb¾QÁÎþÒü‘•… Ù‰ZEoSÔ2SSu8´såM긳ªÅž:"^*Á©Q.,<%•c¼vÌx@ÈVºÂPŒÌ£QtM�îŸüøq´OŒ”W�›»^Œ9$h¯=Nf¸²D{¾`Ñ )ŽÞÂ÷e›²x*@õ•jѪÿ˜dQÞd{ˆ °‘ q2ÈdͳØ[ÓK1Ð)Èg0 R±µ$ ø9 Æs0e�x7@�VçvÈé�©(xêfz““*¢“í$åfÁ¥f!¤TŒåõ…‰~,à�ùͺL` Šgvc’¬†0î§ò’ø3p ܘi­hhzŸü4Ù«�ÈMl[1¤†¤Ysùyw3î¶c…~Oe~ëìáÝ„ËÃîúÜŽèૹ�‹1÷Ä]Š—i7~¿º¨/6GSï| nK º[­gn?ØfPåP‘ ŽM8; 0ˆ¼¾ºÜ–²‡”â%qšF&‚2q”T=Vß–°‚-ÄEгe,ã‘zCïéîpPçŒ]Sµ}`£fØÝ´»/p˜‹‚é‚9ùiâ±ÒÄ8�¡42óÁ'øe…æBÖ z†@ßE»�Ë�s‹ojH³tµåÇž ±!žáŸ:…»Ü�*ˆ·p®ÕÄ78éŠâ¯'õgõjË u�µ)ƾ¢{YB ý!qA½úöîý‡4²óÇØTðª€0´Ï/D]ò�l®~š®ÂçÃÛW968V[Ï@1“�}Àϳh£Ï�„óô¤ùbe™q£ qæš®mQ‹˜RuÇ$„B~åq—ã¡!²Ï¯“•Á¯ˆú‹€‚+c©o_Ò÷ÞU¡Wœ;BkB@1©rëç¤F+w’ÀÄ“Th7ŸîëëÔ3Ú¾w:ÛT¿¦¢¼dd ‰È&3K˜b¡öI) ¹ºƒÌ=rk2l .üèP�6½íö3U7Ãlî†=[ëí¿úÁÞ>è÷–|m¶MO¢*Þ`J{¾j*È~`�Å�öp"ÓNóIѯÕàÜò›*혬�l˪HPQšÄfóg\n8Áø£�úòó)�@ó†Kφ3˜,où ƒØEá߀mG~´þAÿǸ¼äÉú£]9°ô› ÉBÒËJîÛZ`Pÿ—õWªšìçtî jìõª£Ç;¥€SMl>|û»ůÚ(ñëf�LJޤÓw6d?†õ²õK y æ´ •éu.ù ý7ÙÄU0aƒ­Ì%˜Ò |"Ðέ· û¸¥6^¹…d¿/éÄÆ) N‰ìŠéJQð7‰‘V¶©±0�îyÞñQ"Ï@êЮ–±£? ë3ÿ:ld2Ý“%ùšå#²=pl60Ä ÎÈØy4(ò�#U=ÿÿ3(Ñïèƒl#”Ÿì Gž5–§„5kt¶¾Žÿg ¼«ËOò» ºì�µƒŠï/§·ñ˜÷w�þƺFÛ]ã¶ï–—GÑå�Æ|xoÓCIùõù"½¡tu¡B†¾ñ'ù–er5}]Lu�5­ƒâaþ€ �çÊ‘mœ$[³:k³ÕS̱t[NÖžq¦õ ¤ðSÄ`ûGá´Äºˆtˆ÷VêÿöŽXñÖ|o¾¯[ñY‚þŒó7¢ýW¸;bDcú¹]ùN/jc¬ñ¢jó't‰¿&àƒÓ­Y‰waåÄò22¡A‡bÐVbò‘󧈙.!NœâÔjóbª�ïy‹/”\<¹'8ðfÔëÐë‘Ï…ièG‡æd-^cÞ·Ù8×oÞ¬è2œMyãÍĔЄlÜ&^ W°ÎñÓ;Œ¢ª™ùT¬rœn_ŒÎ+ØÕØw âéž4äËz�1ý‘—»ÆÜ³ÀÆ^.ßÅ‚Áž˜ùËȹÖv™?uô²áuàIï™î5àyo¢há)ÅËËá3¼(oà�Ì)áo¤µË‡(³LÃ:n€V·øÃgx‘é¦OŠÃa)²ÝgG�EBÃDK'Ϥ,5Á¥ø,åÉàÉ9==’›!çXt=¯r,¡W±|ßÒØ’?�4U“Ò3 Ô1zµ82”' òñGa2ôóú`äå%§À7 ‹WÓ �ÊÇ1+j l �}3M NHÓ® ê£ùò yöµl³@úÐ�ÍÙY~ÄîéöÑö¤Ù‚Ùg A\Lº ¾»NžÍî“A,. 'g·}§cûn(¬jŒ¥e©0n™¢Ñ¢ qhÇHáqDCaI�MkEùŒÒɧZ‚Ó�Ç,è� Ä´�[Ýoâ�ìì^*¾Ò¶ýí; ï —&~ @��‡í bÐc¤™Õë’ãÓcÆ;¾™T0½�ö”÷þÖS(FXû±þ~^§6Bò½¾¬¹Øl0%nÐè�†[xíX:FQ!Mc‰  õ+AZz,+ØP�5Ã_HÇDÙÔvqRH5M6'�o2ü—66··ÕhäÇBje/Ø.dTbTbdÙ.¦�ƒ)o n¾Ñ–óF¥(%Ž}ÊÉâÆ¼"Ý>’mã~)>Ô.Cfب¼ÁŹh³N˜Ôµ°o”�±¦*Ÿ#†0*6IÖè@M׺dÕ'¹¨u¨Ôº®˜âR˜°“Cµóߌ÷��Ù£!Vh+)‘o�£9¤’]a•ß*)í0oÃ(u�¯ö¦"�¯Œ�^ùÕ_Ðwù8\åÇVà©¥À/˜-;¯ãƒ2η.ã�.-ëßr(/í³èÐM5êVVz�&Í%º*£ y]Ø®›ÔäÊã3(†ú~å+ÊÎþbJ³6–;žN_Ha­­ÄÕ%øü> ¢b›R¯ ÙF YT鎮´_´è×m9~öf<퇘ZÉ­ Ԍ櫘3â»òŒ–ÿH¿3�´é¤ò fëÁ×Üg»ÊÐþípdûÊ(…q[ñ¹-ö~»èºF‘È ]Ö/kÊΞëÑMWóƒ�'ÆUÆÉ‡±!²E›�u±ÉÙó+OБdYÏ x#{â°�è 5à§ŽâʬVHt˜H ÞhÚÆqð Yê”øJÏÈŸ.©sH'LÒEŽP‹#|bò]:±B/´â :ÍÆ¬Ì5ç4r7i¨…Ûa�üög5lâEý�©—"÷T5„ûmã[Ö»›J�'®bÄ•âH-˜¡rßä{w-™g.MpÖ£Ç]€š _8t‰Ñ6Ã(Û°KÚ-øú)¤…å«q;Ϻ¬@éþå;&ω ­¼þ^ÂZhºÏÂ1f(‚göÒÛ&·ÞWØàͤ¾¤Q³°í»öMô°ä»é¢lµ°å¾èë컾ä놾í¤;2(](vU[&¦²Í¡xíª™‹hö™ UÌǽ�]ÕÂã¢êF ãœ�vÅdSu=yü‹Í^©r1ÃÍÁt.^NY,îERܨ§Æó £–¬½qê‰Ëh–S¶=Çã‚ ¹I“Ó– ¨#»%÷3‹áàäpf¿ØÞ¥³¢Çµ¡³ÊçÓp©G—Ä /}:бUJ˜Ü6Ïvùð‘_0ÀPsmˆ»%…»Ôå ÎÆúmfVš2+öLßÐÑõIâòMê{é;}›Ú’»ô V9Ò€cô)rضæ£MïöKµYí³ EzgÃEòdu'&ä”åjÃ/bþÅ…ºÝ‡Pw¬Ôˆ!H'²ÿUW{[ÒŽÛ·‰h[ÖðÕŒ@)Ôfcèsì6î˧oßrÁˆ©'ý¢´�t!{!C² ö`ËÌÏìÿ2µe‰ \Ûg}ŒC“cϠ©‹”W0nÜYWQ“’Û¦~ŠcŸ!’=:õ‘Æd[‘ˆ·"œ8ÏýÖþOÌmÑœ8{}7Þ $ÝC(¡Ôg¦&y×oT“ë9jxؘ±Ê(jÄHÉkyzñ+։ɗÌì%(CßÄúBë•Æü=µê)Šç™ QU›rˆ²¡ Âˆ´£èláM:@Âhs¶1�±ÀÇsómÁ±?F*~ùíÆ¤ô¶O�Êî¶yÕ0#Ôjɺñ—�æ¸XT5¨jY£ÃLl^y�Î 6&‰áøZ­bóïHji8kŠ´$»…f2cÀìÕ4œêd0›ó ˜˜w190&Ëoન0Ž�€‚"F”÷¤žÙ~ÊŠŠ!,˜‚KŠßؘ&C"%å£H3\�÷�:oKmLîN›[¤š~LßÝïvp¡æ˜µ‡´Xã4S»¸Š÷ö-†(¦yœpW"]r2!ÔèxïTyéCYñ98�ÜàºÒg”^Ç‚YW„ËUr8»h�¤=jœ�®Ž•ê½A¯¸î}òXÓvðXPÕò‹ìf4‘�ÃòÆuªKÙNZΜÖs¥Zh[•Ð�/&®ÕW8áM…µÞÙVº¶*#´Î' b{ÌQ%-ñUæ�v­Ëª³!îÝ2yù¤É½‡ð®©±ëºd§|ùfñc§ç¢hº×ÊR½ŒüV®EXH¼ñ¦th&âÑ~tŽ÷¹4ãH³6¿É …?È ‡úö J¼ü1š+ÿ¶Å»ˆ/T{]0[|wGňZn=VÅÃYC#Ø©š�,%ìû;r Æ�˾ W†XY2ÄžAÆPøºR5g¡”j GbU×f±Ë﬎f(2@|e›MóT¼ø-Æ/„æb[n·@ z щôO´%²yã‘èø®Îç�÷ëáuŽû…,Fg&. HDÚÆIšµ7|­_S·��"û<³«µÆž 5‹©œôlpúÝ7)ª©IÝï/_V«p¾‘·Ž‘™IÅ,‡‚£§&âÓÍÖ÷è(a´7/'A„7ñµ¹ÁØo©P*&ºAµ„\æU¢@ ]¸YXÉø!—,ÀSÖáuvõjwlé®Îëê¸ÙðìGuæ!ò¨}ÚTšÏÂGï¦&zèÉχm’ðâ­ ÜBpƒýWwéñøÚ˲ؖ’tûSû'þʶ$-ÅgÇeT4)�ÒÆsdѬïp'ít¦â‡Mgö k­[oWµ¸¤Äx‚÷Kki/^¬1èëýxQåy^°æ¨[]Y"ÂH?= ß^Ò$§^ÉÎOûÞ GNŸÛ €$Ê�\ ]euB5vÿ­ŸÌS™�¤á ›öèÜ“�„N2^’ªR,Ï\s­ÒvȸP¬\îR£­V„®5_“ B£b(´©øWË›}Ð#V+Ãï…7Fp :�ßnA�¿‰,ò%·Ž©Ä›saâ3ÃWÂVæA:æTtr� Cëì9N&�<`!�j § ='ÈQ_ä÷M@|ãá ¯re˜ØHÍ(dÞ}Z½g@% U±¢ú òš?]nóBΣH;ú«mjîqCÊ>G`E—VPÀ¯ 5W+³¦aÌ~p¡ýš#LŸXÉE ÁÛ—ÆãËevÒû=¶^fl±JL¬ÖP6+–§"I&T?RQÐÀŸO²P]&O íÿd ª…ˆx§Ø¿øòÆ }¬7´´V6r¡­Ú"e Ò Éõ�Vݾˆ´¸‚Ïkw—*ü|Hý;ÜÑÉ~�ü×o¶V^6B/‹îG³6\vÁ¯ó]ÈœCñH_Åç³~÷/ZOÝè… ±À¢öÆÏ ÛÄ™ÆÿL×ý}¢¦¸,˜šï×ëíŽië×N¿fÂ[í§Ð±T�£¨�ÙècÎôR˰i¬”ÍŽÎní”ÏU.Ù@œËcº•hlrA§!NcÛ”-"…¾E’0\.o‰Ë)íuãÙ৆7Yà)šÑÊ8éÜ$£ãß�ìjàÔ^“¼ä¥«ÙA©h‡ÌW¥ŽeÖË:à…ˆY~ÿηÜNcZÓŸI™£_ž]†�°¨»¯ëýT°AÔÄæåm˜¦Ô1BÆÛåÔŸ¤£fVbwûg×+qƒzšbe‹ö¶TÆàɉ(ÔFÚâÐÃÒÚ²‰«öwœJÿÜ.ñFAïälÉ+l”EdŤòAåž~Q='§fV¤Ä[ºŒX·ð)š½�{@lMJ‹Da ð‰ô ›NÒÕ@"Š >bÂ}nYc)ê–*…þìªVFcE÷ÍY‡ëÀÜ’Uw7òLj{th”h‰³Avñ-«èK!}Ð雸ÔL–aÚ³®ßý¾TÙgÝóØ�3åsT¿Ì“:¦q;P²ˆcšE¼5»¡»{ �†éçdC˜i¿ÏÒW¿ºþrŒ†ÏG;^Ü÷ò�„.ûLæsŒI½O`)dÜLàÞDkð'ùÄÙ¢÷æ&² �¿Œs�¤`÷—´½Ð<‰*u±ÌóÌpá0ÑXýc'äàMÓßîçcòMŠ�Ê|SRkÅ™�âZ3b¶Ð÷ZJ¡®æ²×øíÅ(ŒÄ=_šeÞg-!€~¦õÊw»V”ðZnFä Y~<†sÒ® ·“Ì2)ZÛ°ªrÛÙ”¢ÇOOñƳtÂ×Èp𳇑=aÙ±»ú �Êæô}þÝÌ”5Z„–Zé)ì+‡Ë—ÑŠdTk.î´‚m×çËÓÃ7ÚFBº­ù[?@ÚŒ²_ÛQ$ÐeÔ~}·å0ƒ ƒóâbQZ9~«Wœy%ØQy'Öò.é΋æ\"]‡²ZTÉ·›ae’ÎÇ Ÿ==†ÂpÑ%b={{³{�‚Û8¡·¢9!·r¨ãžéz6÷ßÚêY¼1Ðn÷tº¹›¶{ƒaæíû½„°ÞÖdÆ÷uë2AnbaÆiåDZþë:†³"î&cZ+†›€·7!ð~ñâN,<Ú–Î��¨ÆÃ]*c]ÉñsouÌ�&¿‚;¢>/(ò©¾ÃÛ”¼Ø¦w#TTaîölŠW÷(uìcÊ”^úø•½ ÖÀ/|ù»*LÉ q á„Pb‚OKâ¹k£�VËÏR—Õ†7È|6ÿ þE²qSäýn:R� BŒl)NŽ3èýæ±¥d_û$�ƒŒ`³'Ÿgã·²à¯þ„Zºæ„щñ‹�n£ùëëoA°^åx_•I ”F'>‘sJ‹ù¼Âé8;I¼Õïàѵ¥vw�&Xñä7œ¤ ·¢¤zC\.Ùö^™hÒÐÉG/›ŒS•â ŸÓ@LÐù¶{Q Yr'‚ž\q̬ž ÁNTŠubªR†êt^AD,;“¿´±ÍŠà*¾D>´íÊfpû ­(‹¼L6Gc;K%£}e=Ñ…_]+ÂÀ°//�ÁN6AÅ 3÷ûŽg®ã�ÌêB¢‹R(õgüJ� ÊÃ(f†¯Œþòôžýd,�ØÜ8º[•²²Åž¹ïpt8ôQf(Ø¥˜az@2¾ÔëÙwÍd÷°zèŸK—ºWC\4“ýÓçí!%è—š{t«w?}ûEªñÐnV}ëÁ/ÚãDl$×¾X®JŠbŽØ]p"4iž ™ŒŒüõöÛ‘(ý®¶–Øa_ϦœW­Æ¾£výžÞ±öu…¸¨¯ÒÇLÄ6µ6S^cr×à3ÖÐwÁòQ2%á žÆ¯dœ²q¦ÝvÃdÅ8­k oà"òv0˜ßh'hµJòõs!_½‡³MÕÉÖˆ•}ÂÞ8Ç”¿Õ9ûAeo;›$&Ü4a¯ÕYSgy?èë5F˜%cö�fÍ[5qGm†Qi©É0ƒë{Ò¸û/4�›5rUaƒ˜BâÊÍ`O—IG´þn˜…kÃù˜p�ª¤Ö:bhj2�*·� 4…CãBZ/àRŵNõ#ªý¡ŠM±øë©ì†óò2î}¦—÷á!RTÉĬºW2±+¸>¬p;އüåmˆHÇÇüdwz§N89MŸµœƒ‘±Ñªtæ@ÑôÙ ª“wðn,ö® Ð?ŒŠ»Éø˜B:ßlµs�ìº1hábqQ¿¦c²¡õÜ5O5£Ú5Ó(¦Ã¤À.ÞM¡Ù~BtÓh¬sG?v—qzå¼ç`�ôQYHDȇc;¯Cô ü½ÿëM˜ÎW³ýŠ´X2�ŠÃiíŽLgk�[y§û�LC³¬Mzf§¨¯9~‘BõGè/óû}CRÔêCSª* šE§„ýqnÆx‰Ö¿ÚØ~DQvøàÏnJ0Ê©îÖžþ‹•}Í÷bìøýZÈ+Èòº„29�Ÿ,mwfŸ@ŒÊ› #ºÈÐp•�áV•ºˆyBØ(òp-QêÎ$Ô4´R×UàR[ À_M:—{ôl´£5>3©¢1¨Q4¦ñMÓn|!'îƱ£3]x±à-áÉ…D”އHF˜‰8hç‹DöË>ˆÝNÅÈÕ|¨ª8 <({K¹e6ûòPÚùKʸÚ;B^Þ"ÔŒéx;´Œv¡9“óIª^âƒYÏŽ'ɣع)²Ýo�àÄ­/‹ísZÞ ¹E˜Bšî\ ”��¢h¡´âM·Н´øÁì ðömJЀ3Ðæ†:ò è§ü°…@šèc¡ïŸ­¾}HH¬7•z|.¾-þ£·úÓñL¥°8bsõ+ñ+ž.SgןÙ2H_x,0h©*Ò#©Ì—“˜Ù*²HŽ”,XÊ$b5›+È �ÂI:ûjhCóí’•³¡|¥º¹Þ„É;mÊ¥¢|5¯3PLíöMj¤ƒ£û[Gæ3Q%¨8ßœ7iæ¼Å|·LˆÍ f(ÍB=�g úlöâºy©ß ©òÄÛ­+ƒìŠ€V/ <_ùqtZ�H–ZÍB7Jð-.cÁˆ‰Tt}r¢X“†ÓµWkm†pÐû{-¬¸»#;¹£“_‡Ý†ãhuyJ^üžLXåS öç…þßכшàEb…ÅõÚZ ¨ÂAg›$W]˜¾XùÅTy4µ8?u[™³4šôâÒÛQ=X•àÕ¶°*ßÛáLJºe‘ë}…=¡D(øRÂå…¹úcž“‹xI-þsWðV¢ß2+€'eDø§e ÷1ûqFì\˜sH²‡G±ˆ•ˬFRØÇwó>ñ2i{e0²Éh" vA¯¯>(„Û¨û„Ä%I``žºë�º*·wÏÖžµÊ„ËY5‰%S� ÕT1{ºZ€dáþ`ƒ2晤5]A6åñ“¯Dg’2ûÝܽ!ØèËíê¦íY°Æ m™çr S/Wo‚ÆmëÊËÙY¨Þ;–soœÉû$¹ã“ˆ£µOޏj6„dŸd^ë|Eæƒàü®…•Vêë¹7z-ýmŸ1vz�·æè*ð—(šßÿKDb¤JA;Þ®ÚþêP9:»Z*6ô§ìªÛPg'†Ä�݈‰’ɱ`ªdŒÛS%º¢ÑýDŒ)Ó[;7´äiõâS»xã}¡œ�ȸ~±„ñ XwÀëÝ.­w©QDò•.(ü°ZÌDäL-ƒRYIyÔ=ú#£ÊÕý-™/ %å�KŽvÚÀÏ>åKÝtŸ@é=C"»Õ^KÙ0Õ÷#ÇÂWþ|/uV? |üi¸I.¹áæ™}KãúÆôœ[±üé,㎿‘’ig´};{:Y ªønóíG  ÿ�±ú…7¬ltr ë Æãz½µÀÁ™ W(ÀØ.¦ö)žœg¶_|­ „0j8o!Œû[Ê6²tCS-™‚´±Çè‰}§ß§Õlá,þ>�°#Ê…Å[Pgl=«óX„‘Ñ»‹€ßãŽ3AA‰RS•A|Ôö\›šœé�Åuˆ²½.6¸ ÆÆËˆˆÍŠSSo�tO,¿»¿-«…â1䤩鯲BñzG©Ã¡ÎwšSuoIÑ Z µX¯:‚÷«†ÂY�UÅ…çŸlÁ¤£¬ñI^³îW"1}ÓHyÒ‚k‡•èç—ª[é™^bü:ÌjGɪZé0pVÁ¿*ßi V¬ZûÍýI³v–«4…+mjÞNØÎ•oEïêlnÑr­•¡â8Þ©‹@´51§ÿĈ Ð%Ÿ° zêñå[ö:jðú12BEè@1ú1yL$xj ¦yâhZ3ý] ¼N ÿîÌÈ3Û'¿]Ö+„îv×è²\@Z é´ fóÌR–n`¼az íVv!Ô×¾¢„·2|ùU$ î�“!vøÑò×�ÝBžÙKE.ò>!ƒ»l‰®CË&‡n?(»uP‘Ü”TÚ±]ËÎ�ÝPÛ1\n8úá@$÷TR‰g‡ºõø8Û_�?!VïS!0õÌ×,ÎÛó8ƒŽkKÁô¬†¢óŸð§› T£áˆ7OgL¶˜%©c§rî`ÆPû/"9ÕP`ù颔Š÷MoÓmP¤Iú C f>dž†�a"lNÉÖz¦×+ óÇx M´{O¥Pù:v,Ám?e°{ÒI¼vÛÉíhÀL¸è3 ¹)GÛs#ã½Çí’i¨¨ýM3}¸{Þß&žÄ1 Zx²¦JpõPðüÎ�S¥‰à'Ó@$s¹K}Ù^íëI¤ƒ¶ =×oª±yßr[á¥"7�Ë¢—¿»�iXº•z©šø*kV}{ݶb3‚:åLåbÆN² ŠÔ tmõÃ'�’Mpª`TIÀN˜@b*}îŽä¹ ÓÈż4µ °º„z¯Î | ÅÇGTYuös2,ûhãCB´ó5„ZuìË8zëY‚`Ûãª_Sþ‘9cgéjûÅh`PAT ~@qUgüÞ1ë´¨Ì/H y¿/ý7Œ¬lW±ä² 0gÐö „ x#ìMD¸VÌœ:K?µýÊüÁ#vyšy§ßÂ�\D_ðƒbÔëPi©!/ŒÙê>Â{˜¯êúŲúÛò4rœ.{\²½wh¼ð›{ÊŸQOPÊ¥ØRIë’Z”e¾“¾Q¼2Ãèžå䑽Ï{OZ(¼S{Ë¢£�H;¬°˜Ÿø–Ó@fQjÖAê§k6*ÊpnŠ®×pˆÈ�×e§GÙwN>_óÊ×LìIŸkcBÇ—ÌcíÙ!¼ÕRL_We•’3êü!XÉÕ×Aå&œç MúÏ,Ü3•GÅ”ÔÐTÞ3X¾â TæëëŽðdŸª…Ý‚U?÷`1“œÏÁõn1ìèHx«‹#'gÚ¯�=‚z£ßž¡xcæ¡Yg”Ù`c(y~–rªÎB©`ξ[ßñ6–¹Òá…€`ÇÀû} ÛaÞ,klÈ-­/XMgÔµ×`œi´¥ªÆ‰‡h^•É�GÏÚÞâž«î{ü:kï µCç caiÆ~²c•ßgSfןë.®‘y ÇÂÈe÷O3o‘ê®*«â ~vyD÷oÈ£Å]Y¼XxðrFïî.ÉÅÆþ�ÐÌkiyÁÓ®f� úDN;¿ó¥4 è�Y†qDqXFšwcô40³·Í«d(Ý`¿<)Å(ÝÍ3^DÛËD�k²dáçì­uÞå(·½o½Ó�™}ï ê6ìÚa£kÿíö´›XΉhvH,N"ÛVùVu(xù·IÌ\Ÿêȼq¼Þly½¼²s6×YŠb Ô]Çð(`U%jWШªš¤vP|¹šŽbv¼¦þôžÐyçy <š¶n¯ÿ–?OóÄÙ”nçaè óßÉÝÏÁ’Ûöjú£ €§šï6ôËGcbÈz|4&”,�HLžš²]¬ŒY4c«Žy5:ž¥–º�µ¯sfëh)¹0'ìF¿Ñ:‡†¼Ì„Àˆ&ÖäùIh—\ë˜Äô0�•CO=ØBU¤ãξá´Á€Ó®'5Áדð.´ÁvaWhžÙm]’,!øF,^aSSË Ü· Bœ§¢ ¨z6ƒ …˜J¸…þLǾ;P5Ù.¢WÂf‰Ía ’·:°ìÞoƒ8>¨]Ï©Zåçîù=qWsW€X´·ï9nqz<:µ´¸ñ«ð�žU4|EŠwQ=¥”ï/â×ëj!±5ÆžŽ  _…<:=UµÖŸèb*9}f£,ú²¸î39ôUF|29¦ºÖ‘ò¸Ý6ò–îQÛ{fzë7àŽÌˆ¹�/¾B žcpÜãœ1h:w“X:Z2¬Y ƒÿ®5œ æó+œ‡¶LF"~¶›æœ “³ž£ð›…72€Ø"q ây&ì€Ñ¿¥€†AcMN¨fÜ‚öQMÓ•ðA– ±¹3oA˜Éœ&c’Ó±õ~#™Ó“ú×0¦â&0ªÇàù3…üz±K†|ÒØñs»íH ý0öÕ?Óœy°¡´«ìЫ'¶ª¿vëz›=Š3 Hþ¢øî‚–勬ÎÐL”ff[¬¤a 'Ã� ¾¨¨„öæÃO(˜Û>|‡Ü¡º,,š;’Á:(Pä?ÜY&ÓR”/¸tª«'º¢’ •cÅJñæÉ�_£8î€"øžT·%Ö,å•Ý?Ù�u×8ª0vc�I9Š×¢dM°µM<Š€ˆCV,‚üWÃ+͸Ǭ¬…ç1KÀÑl�lÍe�a‹¿öƒ×¥½å&b¢šËñœAeqÞÅ�ñÍ‘‚ÏxÁÞ‚çb‘€ìÿMPT¶ÏãÐ[ ‹Ö½ìc5ÅxxÜÉQ–_éÎÆy¼òƒ�¨²rþ¥¨0¨ ¤³#˜¨p»§ø¡5ÌMôÕÖ6ºDãÐÞ/ê ×-… †(”Ë×±ÈQéǃ6 ‚ŸÇäõ5ÕŽ¹§Þ0m¹u†W9ükÎ|(Ú(Êòl°O:Åöz°ÃS{ lŽ™fuâÑâ.õ´&»…`ƒíÞG˜¶»HAR[ª(¸ó'÷g'e¦f¤U°,ÆK;£�‡·µåTÜÈÑë ö?,õ$¸uO®Ø[ŒGWóJíjáKë³¥^iDüºû*"×þ1ÉUé–�`}bLZ¾ © .:Ñ9ni" ¦ÁdÖAKãÌrƒ°%˲ÿÜ�¿þc?˜þâðe“ÒØb5ëÊ`PñÈ'€k©²N˲£J9ÌÌ+ãµÕéi„•jòèÇz´2‹Õ 0¤Öš^®ø^³ÇGèm[¡Aø¢ú2Š€^äºÄiyýͯeáßÑ÷ZLÔbPÞ©Dº/1!‹ÁУCBq›“nx³m•]NÅ¿+bù>üFr4Ñæí‰–OèÛ;6q�×§z- ?æã."]¢Ã"è•ÊÑz¦Lª½Œm6%ãâÔo2û¹ÓÂ?&ÏB¶›²:Á^¿L ʱOß èp„òâH¿ú¥6Èãq® •B@Ùþ�áÌ“" µÉÌ�n†“´¤âÞü+Ý ó_SšªŽ¬º7± ¼í‘�ñû ¦3Gle&à 5,!(Ä�m'HAøKåR¿âéˆ ÔY¿¨#Q9“;•K·?pü£(él�«g¶-N;Ãû ´‘mK†1°‹v©â®ÑH–^*ç ”n’úïÕ©ãÄÁ¡N0Å=ËHÓ.ÈO­²FÚ‰À.¢H-‰»bÆŠ½ËžåOØÞ&D>:Z|A‘»Î0^%Ǭ<™Á•‘'j[ÇÁ%E 8M§F�ì™­:.7"+-¯úõ»Ë%œ›Sóã–‰�ˆŒßS�޶Ø-6‘yMkYB+D¡Êeo…"q¶$c±]$¹È¨ª`%ÃÃ'ýþÁ–ü!YÕ%!ùö÷ž7²ªb–£gZÏŒ¼‡8 Sçb£¥äÕÅ1£IznƒPÒ6Dè-‡×ù©8ð^EêþÑîtð’6’ÿÍù( N‹1ã¦óöø¹Bp:1ã¿7ù‚Éñà̈éÚq&w›A,/gýþ>ž(b[ã¼ÍÁžoGýu�•[øÛÕ‰z~´MÀ0Å.ÜôqGÏî6tÙº0ͳ²HE!;O��O‡¾D€�¿�ì§Qß�­|)xÜŸw“a®…ÜfñV׉Í-¿ÈQ—O@QcL�O3¦©o©Þ7€Àí % {}:Ä$‚#‰‹³)33“c}(, TýÒ°~e¢°mäÂæ�cêÝ ›¼ÐºEuþš®0Ý<ÿöBßK‚z#XÜO¼çXl oÓâåE|õb=²~2�tÅø‹ 7Æ¿¼ }}d¨Ä¶fyz›63޼ªïŠ­à�ø­±¤Ñ#<à~2Ë:døîÓ%0´|Šî!yÒ6ÎÂÅþÖQoDœ¤߈äç?ÕÌt•ʶGØŠ½NÀ_³qÈÔøb¬@¤?Žä <ü„ù “À…¹ó@å ])#n»ÞàÀós-~Ÿlƒ¯V ˆ9h¹ý 5ÀyÙ@+Ó~Ù2“`Po Láäà+�@myfgÙ¶L[7:[8+XËTË;IË(XLKüÀŠe‹Ïbfu01‹ÍXÕcɈÚ!‹aÚuölR�Ù¤·�ßbÍ1b|)qK;šáE™•Çî`’ÍŸÅ›Wó»¼…Sœ”—¼VÊ…�z„Rb|,^{Y °x‹vy  üg†t[Âh-¯=é¿:ÞP.R5'9®�Sþ³ ­–óD½|æ‡>ãø0ŸÄò†ü·a7cxƒ›PÁ ¿ ០ŒQ“%Îiˆô[šÓßþ¡T;˜nÔ‚›A˜>vavÔ™\Q‰’iÓÚÑÜß7nÀ<˜töiŸ¬t¶kãX§Ï>Á%¥_ÖW@J¨v þ–ù_ïGú¹4ËeïX·�oÛÊ«ŠÌ7üúùýÊ,ç/ËL÷¾ù-#š_ágðAãÃÔ@ØO/pm $ÿý5yAD€Ÿ©‘É/UºíPZÊŸ_3\*=•d\8ÂWm"ÖÅÞêàýü¬qüó-`…Ø3.»ûï¸ì»$@MÆq8Âò¸*¥­:�Ί ðÞÌž+eLØŽ•ÀU …rÊzÂ4*¯•�Dá` ï·e�õ©ü~ ѓ̺!�=ð˜,¢³ÞMgU^gá…v‘š7¡C\çmÈyV¹ÇœZ`\Tì§’¢)K¡zºÒЮ=ãßíz,,ìŒvÖ�Ñ“)àË©÷�HÿÕ´`ëXžòHË׸*ÜâÇtÓQt›Æ•¥›=Cõb´ó?ž ´#Aœ4ÓžB>E›2€Æ�ç.0§Oä›/¡e–ã+OؽÝ5=àô?Žwã4.œù°|­¦N}òJ}—þƒ q}H˜ÇH,·‰—û¸åÛJ:´|z�åýÂf—ƒ“dþÊg˜()_01A‘LÎkÒÈŒÊM¨K)2ÈÈÈ)æ\/kh’–Óòq*’r2SR³37ju¿>"‡ iÇ:¾Ä ÚÙeY c¥Q`§˜í÷šs_±Ä°EïCœ0ÒÑ1ÒHËµì ¾m{Ù9×I)t+™Cª÷¿òb¦§æ2�…£eÒ£mÏæUõ”� 4ô׋åS�×kåhxKÒ…[Ö@a˜ORûa!WÖ¡Èû‚Ÿë…ÄÛó™cg¡Ó—æ0Ø–»ØÛE"csʃÖ 2côÀŠ6ª0R{ØÆð—ÓžõóîËpŒPà¥és“YÏMfcÇðŸõà…Xû�êê¶,ø°%ÇðÒë��_�Ÿ®@m�±åŨï îÞÖ¡"'±çñ¢OçʼÖ#PÔ0‡ž¢›fÄ-üÀ’k±©n¡š0W^i�oyƒ ™Au;†ˆ2ýc‡W_{0ÖV߆.3zäܦ݈\|ýg‡'LýRÃjïu–ª¨zPÎýòòl‡¼H&h_IÿÑ­°²ú#ïÚŸs/C¡ÕSèîäç£ÃmyîI='@6Ý…3þe�CpêËwrq¨)™:g§í:¸šÍ»¾½k¯sÕŽ@Ê#¯ØÕ¢=ï¥Ó¿ök€ÊŠkBÿ¸‡ª:”|zß 5·tBÔnî2/ñ­8áòiíÿ¤¯bïp}﫽FoY®ƒË统i¬:Ô0»pôÇ—U¿”w Éú¸k iB‚ÖAAÄxÝå± ÁÅôdÇÃ_YÜŽCÓ»³5ÏŸ-‹2ºŠ4ÿ糤ÞvScTb MέnHÿbâœÉ”�ös!tÁ&ì"ŽAD@> ùSÓ<„©ÒFÙFÚZ9785\ÂÚÀÉÂÛÀ&ZÄ *8ÕÂ"ú�¾Et §&#Îüv\UOÖ‘–âkû7ذF €) †Ä1Då+c�½†`øãÇŠYÆ ªé�JFÚÎ᧬VF%F6_��³ÎàüÕ{¾¥ÁôÒXÀŒ¥€+˜àðyÿM`H./óJóŠFZLÓÍnØ)�©KžöµÙéÈôÁNìiiÅ`?³ü¥@Jf+}íéDï–: ¡ éaDó‡D3Êmò=öð$ªoI›}XW3+ž»XVÙ‘ � £Å§` HþÛüòu>öлu8Ê´4‰I׳€Þ?—“•Ætøÿœ{ÀV™­„˜½½n““ª¿Ê1ËÖø¬ £dwíX¸‘éG]Rý‡>fmiæDp‹°\¯OËw³¡ÉöúÐðÄ 7äÈûŸ•‹Tžÿ˜5=ÄÜté¼ÜžpJàÖ4·ö[!š!ìC¶ÎÕ¶i¦+XÕ8�êXeik-/ŸsµMN¤Çiøç ÊL/ÑWÏ”Û&R&ƒ'È8Œí�ׄ§UU�ØcŸGi:àCª�çó=¿f‚QiŠù‡­â=ƒ�Aپ“±ôÝ;ÀŒ¹É[$˜iå}ø™Gf;� W’š³Â£Ô°OW€7dßõ% áh=JPÙ4³jœ©k1§#QŸ‰¹‘¿“§žN]X–”»–Œ¨ŒŽò#k E×`feªm]~`ŸÅ«{_ €93~ý:(Üâ�}„ÉLº%zÂå-øbÅ“á…?ƒüê­½3 ¯GÚ…Iý1çäy{§E'†sÞ‘¹èj¬I>K¯á"ëߺµ~ñø40�ãñéÒ:Ió^ô¹1[mQ^n— >v€ð4høƒ.Öéš÷Âi\&õe«É� Á´pp=¹m^ïà(Ž!"^þÑÄ\œz|yáÍæáØaê¤ëd3⪸æŸõ¯zÒ�…åѦ ]BC5›ÜìÏlÜ ·k¹|uþ͉ ÒW/UÀøv±yÛó¿Ò*^aÇþrÄþ¼0s´ˆ¯�� J…Øì…‰ÌÉ®R{S}•õn»qü[h!óü›’ÊÒt>ïßð¦/Y8SA=mÿFb—cÇ÷‘ÓhÿœHGØ=€.ï[%çr½¬,J‡ÝËêÓ¸ ü  Œ¨¨¿ºÖ  E§è»¬[--ˆ›“‘*ï¬Ôš¤:?ùn¹›J$ Käždå]Ü8ø0è}+_evÞ>ö€ë‡{ôã]Fèkš&Ž»C f-¯Í.¹í“_J¦1*#çà:°0ÕÕ‹¬¦¢ÃQ?¦ÒïNsI²MÜ)<äÉ)Ô»fG”ìvç7iDíÖ¿VØFò,ÛÀu‚bªÉÝ$è¼Wä¥qgêù�M¤9/íFA?Õkæä^MÜaÂb ÛN �þ¥.=µ›j¥‚zú3¯Gv»|Žñ Z#Ãm˜dÿîGc쨳êy÷ÃG1]ÌZ£ºõ¥Ÿw­V=!9À�‡õQHÿH øèì\×yÅ4¿¾¸Ñ,tХˮ€��›ž‘NƒùW \Ú¾9‹U2#/E}cqþùT)È"¿´3÷ŧ ´áa —ºIxèý�kU_Þ¡ÝM4,]e¨ ”Óɾ«LßÁR°0®ŸA=¦“@CÀÛ߯òˆÙ"ñ­äǯL%”ƒð©¸€ØIÜçbhÞãáËë-|þˆI¢Xܯŵ Nä^üF;›»{Û=Ñ}} vÆÖ3¦~ MC„BÐ@šu òdk¶¨/Oíÿ‘¶KÄÒÍ€B·êBÊi¶›÷ž7ÛÙмï¿ †›~AÊ«³àƒªù‚ç{-ÑÓß £àcÊå�7ªÖµ¾›iAN,­÷fÜð�•ù&@eã“€Ò˜x¹ùs×É/Kw˜YÂÕ1vù�ï"Š)U�½U½·ª¨ËóC‰Ù«Û\¬å¯j%9ºå�Ã"ÌÌÄð_‚цM_V²¸ö•|?S6Ý`eÙL¼Èôͽ¡ÄéQÁ>Y³¿\,�ħÉUrð{hÅó¸Bi± øÀ…Þÿlgu‡¢]pÙ#ÏM@áä½Õäß Øx&3ø_‚…GqãÚÀ~oš}ƒ�Ä¥wÿ#M½#2ì¿Äý¿ ªÒË›P�tÿžØ«±‹L� Ù±º\6Íé¸17‡�ôS– �M†[þ¡ù£ÑßX�D–¯ƒb¬ÿðJG´2Õ „d;ö 3ß+üÂ3N¼¦Ÿ…;Sƒøq‹ýÇJ|²w § ˆÙ�?便`Îm^îãþâúì§W7/³€ëO÷Ì®ÙêªZšÌä.QŒ�åÉ»drYH·ëO~£¢*Luè/£Àe6öe·¡ƒr¨CÖA�®Ñ'Åa}ÖÇùØ�ê›ÄqÈÍ×ñÈÐŽ›7þ-7µµ¤¯¾_>»ñ\›¸²ì|éw1æ¨ØR ¶T ÉýCB”ݺXd�/`W΀4ô¬Õ@PäÚT3éaÿ<~RQørðGX‚€Õ§)™ÌoÁh÷+VƒŠR19¬©Xa_{§6e{ŽáàØÝNÈuoÌókYÝÙdš8ë+�Uh#èuÖä Bj?G1Õé ¤¡H�wwÈ+ðG'¡[*K¯û©dgžwnóÇd#é×\ ðJ-²ûŸ[9Ê”D¨í@Ð0HÜC¿&¨š²()þ0Ö§wÃ/r½,T·=}Q@0xÖ²†¶§xªïÄäé,Ú�Öáäß Æ¬ñèÏŸÿ¤˜˜Òñ)/ßo¾r—%08ÅGÕ–¶p£áæ—opLZò–Sç }ÉÐî¸w 6×K]Ý*qúËê ³C‡ì¢›YRínCܤϿvH¯î¡6nP œ: ùO¡w}ëð±ܲĮ煇¥'1/5Puj HcmXv©^ËI ®NÆ®‘’sSÃÚ~n7ªjÖÂ!»›X×5Aü‰&®/ª:™“¿�î)æwGO¥·œ'ÿuMt­e;‰Ù PDî&ŽÛ¤-¿­"áËè«LÂ/JmU¼d)ß1pýÃ)³f®±‰Öñá|ãR,»QŠ#ÖýPìgâÞ Û…ÄÚŠõÓ騬¿h| Á,)êP§¨Ý1¶íðUr uÛ˜ÂJ¼_ÿ¶ë:åç ¯X*ÖQ¿Um¼÷ò×Ê!2?ÜêŸn4ëpZˆAš¬¡e<]±¬xµÇ“¯eϱ]ô+EÏsûfTŽ!–†WñÔ$Œj°ZȄÇKìíãËé$Üœg>o_ðh—o¦%Y—£¤´‚s®k(w[BãFÑ-œœ!ë“™hp?›+yñ›é­D$¼~£RÎ4Âì+±—Ȱ/t×Þ-«Dç©”éõßgg‚k‰¿»§ÍU‰h]*}v-²A„Íþòò‚4ÕÛ– ç€ƒ4„ÒÌ!ÌÞ5€õ½¦[­¤©³ø®'»2å�Q€Î6jÚW½VL}{€ð7˃åä¯@�Km]>B5ÿƒ´oè…Ú8‰u.®þb«S 8L,X!ЫéÅ;ñÐÍxÓGÊÇG¶1ÔN%µŽ$ƒBè” ¯¾uÖ|äö®IÏ1m ƒÏù4§;œ­y{éØlù‚” Çæ0YÿAšîAÓÂÝvª0üD•e;õþ5ÕFRÿ&·… é·UÇL�ú^B‹®Q‹¢7ÆÜU­d4s Mû²¼„"'q‹…*q_Gê�‹uu®kZYÐýAÎÚk˜þ—”˜×‹…m©ak1v›‘çÌXA1(»ÓO0&`èw¶È�‰7P/!nfö—ShA)ÔãÅRK”òÀ„O`N„þ¯*ŶŰœQeÏú˜E²†YE\B=§‘R®! |²ÚþÃPîȰ²Y…ÉNœ 0€ ?¸ËSØ2æ'k{-ëBÿû93”Ë¢!¾…*§AÖRZ+¹ûÁâÁ£B£·CýCC¿­=7oÄV�Œ‚ŸÂÓ•®LïKx³ƒFçôôÀP¼œx¼Ô¶.C"£ç»uÙíé’¯ÏE˜>Q0K² ©åÚ ­¼º!�_ؾ]îa"ç *¡UhÚA[Ξ˜é¶oi9 �ÿÇ-Ì �ƒ´Ð8þHì®ÚÿùC‚a·C-3ÍÌ¢Ëâì¶™OÎYG.qV/Róì|Mq¹ ßÉ>¾GÐô˜¸V<㜺$Ù©jƒÜxïýòº§i| �-7þGœ<ø ×?aèµÔy2â,;»©úŸê “Àl€´<³uû;£ýžØcx-Ó �Ÿù‡ cD-!³ùééÇ*Ú¼àÁ¸ä€eYDøuã_¹vÅÓþ—äÜ:�3EÕXe"þŒƒRÞ±B?¹}ÓRä¢Ãlã.›†qf�m ’ ÿ]R =á�»r[{ú9ÐrD¬&ú’rÿ·¤r'°Ëv�§�™SÖæ¯UÛ^†þ1LpºöLôù… Ueù.à´{µÊ¢úÃ�ΈWçã„P÷�n´ï£xÙ Íçü&49óC†?€Háø÷y©Nœ ê<"‚ä¼AÐØ³SýßóšàÇ%X‡8~šWÇG—^”Ô1j-õ0ísz®€d­¡Ú›@r[eƒ! ¯{¥Ýí,º6àîÆÛ3`*Ç_5 �ÏôŠ ¨é¾ð-àèžì\³U æ„<ÍE¥Ý>&çºë7+~¢XgÛüÒ`Ç |‡Õ�+L€¹Ü«=`ÄåãÒÖ½t—Cñ‹/¨×Ì÷ÒÛSdýkáa¨Îv°&Ç%€?€¼/Ewg®=lž ”#µ¸Ù´TÐBo•’tôTF¿Žg[–º4Ñc-Òþw¹ùè)λxI…_úß@Þ=aIYã'ÿnÏEe,TOiñ“Ü* ƒÈþçÉKNY\Èy¹kªƒàwÖA¡ÿ¡‡Ý&A[”HNÕÆÒ©ˆÖ¸uEÚOz¸@Œl¾ *b}q†…“§Œê@¦/ W¶‘šÆØäZFÚeУë @—?T¢¯t´Cº2þÆMÐúþN`§œc¤Áçð^½«Bþº ±üâÿ�USðÏ3jÞ„Ì ÐHûò�÷Ì*þѪ K`‡¯#5<³}(¾ 7 Ç8ÕCÇ€ŽÌÜÞ­ã! ‚4«ÈUɸW»iÈë8(‹ù}€åò]®jŠuÍÄ+àü{@@@ÞµÑÃÛ„kTEÃù¡7íÓà€¯Zoe®a®!(ƒóƒ³JÏ�Y >jöA27_œÚ†_>wÕ´¨Z‡d�°k2âz â1<7¢G¹ôÚ`ÿ‹×•qŠÁ1úñ7eïEzu;‡~ðÛ�½ å€ì™PºÇ¤(È|èa$”Å=ýžýrãÖFÐ]‘%|‘E㇢ô�]ɦœµû°/E£7sß¹ìÛl~æô,ü_¼;Jÿ� ~¦çŸ«Y2`n`ùhÀµÀÒ’ô®!Rü;vky£ÝÏŒcœî)UsA±>±�÷¼:ß–�Å΀yºÿ¾aP›µ÷8öi3ß.¿�9à\Ô�4¦nzÚ0'_‘O /Ÿ87ý>¹ÿÞ–Y‘œÌLtt§™;£WÒ£ÆÔQbcíË�OåT�V°dÄqzŸ hL Áh›ó~Hb{"ÌŽAÑÙ =­Š[˜_/íÀ”œ w[)ë[j±Y‰à�k·Bõ´@¯5,z±‘›ú‚ƒȯ×�Uau˜‘õ†+ •|~[„­lÔLøÔ¥îçÏ¿¸kžxNàûoÄ)_ÂSf´”íîÞðûË��¼¨.`ÓA©)a\�"v˳™h*”³‹‚‚*÷¢‹¦ÿànf�aé¹–Õš¥Î³,˜„ -VévMÐÙÏ€ÁJ/cáû„+Cnœ‡×9×ny5b.2ˆÌtYž”_„–}P�·gv›©Ï¬»h>fÛ›` i‹ñm‘r¨¬þâ4vùWd•7qv³fļ·C;í@~;âˆÐÇ||©ÜUѩ׽i,ba9£ÈYÕ%FU–Cì¬âvr~®ªㆉH‡èŠIÅ팫x¹¥×¬�3’d}.Ît3fëé­ h[ÀzâC¡x–q†Ÿ(ìÖ6�Dޣͯ»¤c;¸›+˜šiþÍ×Т ¯uLòt,ó¸TzÑß µêm‚e½ ÐàU.T¨Îv´Ø£Û2k½�‘ã‹`Ä”D«I%ç™ê)ij)º¤$IëÅæ™~w°ÌdÊamZ>ªéø'KXDWƒ*Þ ýZ¶±ûlD >»’Ëí/¯‚ÝÐIâ²¢TMƒe;sÆ'G¥Õ'i¤žÎîhçBc4{µIrµÑQ8Õ 0·q#‰äè…tQU Ì� 2Ø¢a‚w ìHÍïô�llr©¢IQ'¶SÌvSJt\ò¥º9üGhYo˜K“�ûpå9ÜÃ!^˜Â:ílëEéM¸a-$�Íö�èC¯Û®ui“·q¶é1÷pRöi’ÍvK‚3²ö.®Yåßíg�’Á y’:öáZ¸žŽÂVÊ9ó_;£µ}(.Ñ;ºøÁ˜ô�~Ø{/¿J¾í˜”�—•¥] ùÌšU`Tid4V €b)¿»#|jXω£åÌ&ß³­³1ÑJa�Û®» YZ™'ëiûà±ÎÒp QîöÒ{ñþ¾×žmàÏ}“AuÎî;¶³Ê.’ª>׿Úóš¯ïà ç†\þááå  ½Á×ûoõÃÈGë-—bynmë‡��Ž‹R£¯ ž|Ô¯mZÛ¤'6±þÕÁNcä™íB9E ¾ä§G]úƒÆ`P˜IVZ¥t[¥dHÞX¢Ô±š¡ú7sñ‡}äBÕca*«´Ô¼avµ%ÇMy•%õÕl£FÎ,ÿAšeuœãDÂøÁ\µù¬íƧÃKnîn¹Õ©�µp�†|£flâÆ·Y •És’JƒÍku7 öUìΊž� 7„´9¸‹_¦UßPmì”m´ŸÎØ·;ÃÔ_*OÝÞ¦áÞ.’dë~·£®~m2`ë=8#ë\IÁþÌw QX Ùõ}NR5¹û=“êm¯lþäZ½ƒÖ—•µq pÂÏÖƒþí‹QAàø…aç!| á¿>/Òó¼ü͉ÎÙ»¬o߯£éë=V í:œÎÝK¼;l©H ‡»�º~ö!hä^3cxùtýÀþ5Iç'\{­ÖZ6úWÿòž³ ±%c‹™Ï…aû¿¦I•Çì}Ð}›—º¸ÆXçÕÄgMu+Ê!އH–è"èüåÚKÇ–`º¼w€á­á›±çiIeŸ‚¢=Ÿ¬O[·YN¼ˆ|œˆ°¿ÿ›wú2ÓÛdèà£>O®%g{³Ì» KÔ#ûÎÞ£@Bšî:0úÑ¿íuŽ4’&§„Èòr2S²Ç!`!§C“?@—AÝ 3ƒâ=dH»„ñÍMïÌß糈ÛÀ@ˆG[ft>`%Š9¿-«çH嘆ú…¡—¼`œ³µŠ´œcéq'ÑW•ïE²ƒÿíHÖÂbx×WƒÜÐñþû0RññÙÑW~½f Lÿj€`{…y†ù꩎èJݦ*u¨K[Þÿck¢Ҍ÷…BÉʵÕí¹”œÿG§þª'.�…̘G+õÑJ7ì‰uDŸ�E‘™œ“µŸA771\ñˆ�«×OÉyó‚˜`—ü}ä;ýß 9•k�“ÒMûl4z¤Ù‰™ãj#É‹’ÚåÜZþts3³=HV¥é‰.¥ X¤2�<ñÁˆ� ™bËL—ÈVý÷ kx|Ô”b¯ /Êrñò~Ê ¯ƒß´è7§@À‘²Šã1t`×3³I¬µÈZ_™žNÆã³Q”7âsÄ‘Î6”{{»©˜!<ã:â77BT8i Ôä],Ë’½âšdê]3ËÝb]¼ÃÀþ�·]$‚Ô‚켬Ô�<]e}Ó™•îçLXúY ¢}�Í£"4ÄZ÷�µŒ&Íô%Ý‘ÙþÅ#ôÄޥ›ÑO �A\П 0=®æØ´E—!%M«ó^ô꼟œ_[ßE€ŸlÉŠÂ&üÈó ÀMŒ©H‡¸î`L”æèUM†³O8díËíõ}Ý!ÞïŠçOÚ7gaàA-ûÜa¬ñ+ ÿÓÛSsè1]ž{‡e‡Qš¾È{á°‡—<Ú2>•‹1í¥ÿß�”lÓèê(¶°L=,ÒÑ?`M¿ÀM@wðfÏm¸p›çmÖm‚F¯j1l&Ž65¬JKçHºpqa—u=y&‚²³ÇºñóÅ´±ÁLèw;QI¢2DMLoO�°­2�˜Q"S¢¥‡¾ Æ—³ß݆é�'�ÊjãabQèìûÌѪëzé ìù žÄænhŒ�f‘ë �Î`]Ê· F}2¯F8»LuÈ}~s=ý0“ _ý‡Û§E;x(5ã£RÜ„âeþTèM÷CÁF¸´ëÜ` z€r^Å�þCÛÀ‹6ÑÈp£S*6Û{Ö�³„ä9õ·OÚöøGôDìhÄ¡éÓ ¯‘š=º™4d«U|QÕò3�äwhò×}ˆùÝ+ì«‹kó8R^ž­@)N©–f»EtV¿ûw1méÔBÜ‚jRóoX³�b=ûHëlö“Àa} ßxºÞÒü}ž Ü›ìѪ12TŽ8/{à-^ÑP�¾ƒJšVN'¡ûo¡xŠ•h¬¥ºr¡U¸§ÕDa£g—†`¨6²­\Ç9Ü*„k††ŒV‘•‰9O8§™ ½QëÆÆwžK§{Lž…ÎIDIë�Ø8Ky°Fß—ì�CU·Ï:Ã"”¿Ã¤ Ù€ E.¸ª¾ùDK©”(nÉ �XêUŸŠ µ}o±‹Œÿ¾ñqÎu–±Ñ±0¨x®ª¬ïªaN€wwû¡ "‰B@AÈ31Dóóëë%b♨oonáF)™ñ²*›9É�úÖ6 áu†nZZ2(Áæx”B&U:^åX”�©ÉÕ+ìLß`ÖXeÛ:Bðõ¶Õ·1¾t V³]¬Rì«"ËÔ>Q×�=3ì4+yƒOd½ÝÅßZOöjhK,â§¾gvŠÚsŸæ³_ûLJDRU$xÈ•Xò©1¢' $‹ú0„ÙEéú+_Ðй`pæ=œ±¢bB¾Æ_cÜÁBE)HT‰·‹–ú�ù¬µß¬f¼ÑGY[¦^§t¢äÉó«æeÀn|¼¦ß‚Yå„Ï=b[CüÃ7K¥µe,àJ¦è ŸÀb„Ù´ì�örG4#xmê‰ “(J…Žÿz†¤pUeƒy©7?Pè5ª©ä^—¹Î¯¾TqÓªÁ½›:²c¤M‹MÿRrï49·ó�amŒì…xP­9ôÈà–/=£Ö°Ž›ï&+�2ÛׄãøWkI/õhg^‡'MÆ`ósš‡KÚR {÷˜Ú¼V²Õ*�õÄvé�¾ i¯Kb%¥þ¬úA½–îâcVfОšŽ/wî­¶d<—�u)b´:9Ì—É(x4‡á�I§r EݨíÚ-�B;ÑÙHj±ûRüêâO;6ß“ Ñœò¨¿X­Z“?Ù�¸áº]J G²jùÕÅ]t ù‹®c«cdÖÇíî]ˆð }"$цïýb¿{�êîÑÙ'½Å"½|«À¹-7+tÆ…Ì·`+øìT‚­{ò£P¦Xf�Ùë ä?®QèØ»c¯qˬ\Ãñùzfát‡ßôŒGýPDmC–«þu�Êø@ñŸg�Ô9o¸Íè2ßÚÈ<ˆYË4—Y•À'RdýoP„†5ÁÚ:(å�ñ¥v—öV)¼ô*+;k3ï>?¼á”yN¯áöPÞìÃ~Äÿ—×êgÞª†.8`3÷FÁp3·é²mÿOÎ×÷ÃLR—Q¯¾Ê0Ïû÷;Ú%Ñyp9œ™8Óü>*¿N4œ šR-2²ŸipÙôói²ò ÊTNÖ�ÖHî6:_û°ù9ÌM>ÉiX¹"fÛr¾\á¹’€1—sz1ò)�¯õ ¡>2cÖrÑ= 2ŒA£µ¢ýà%qݪC»M…mN�YL•†�e™Z¼«F—lVôfe/î%‹<)^y ¼kØòÝ&zXö¨]›ö;ô„žÍY̰áYt�2?¦ñóØÑl5(¤÷¬Yµï0±q4é'&¦eŸ?˜vN9�…ƒûÖEÐ4«]6ÁÖ¹™ål×Ed/ðEïU°m„à ګ?Á 3”].NDúj¸Ác|FÎ.oÖ«úûìP3o µKý�\ÓNÒÂ�’~ÞrWgÎÒ¨ÄÎοŒ�ü•£@ZNQ" �ÿ¨˜8=†þ¹"O¡±ˆ¿²Œ�²…ÁKœùm 빂»»¤Í¡`_p}ËËàpÏÔ"lʧëYJÍwͱ-‹J^Ó„-2ƒ%~GÁTZÂ�ÙiuuðS:F¥Õ‰/,ôhÒ•…}•mœlà´"J8õ*h‹hPªÈ´F Jž°LL;,x*Ÿ×²Š;sát�QНFPµ–ŠÖA™,è8íYõ¹uê¢]5“ÁySx:íì'çsd<˜�s„pûOdDš6(ûƒo¸UUÁïz¾S,ïáAðLæ?âè9@ÞH§Wšéåì(þºL¼–g'ßsõ š)kÌa‚�öDº¥ƒ‚·êÍh‹Ùxë 19_áÿ\?”4Š�üWEI°Íù­`”ä‡ë—”¿5>]' ÀÚØ+Ûài)C‡¬¦•r34A©‚ݪovSSü6§ Àì�¦)ùªÇ�Iâïb3ØŸ°OåSÕß:Í2ˆn†äCìÝÎÙšùÌPêï�¾ ¦NÔò(–¿n´yzqð¼s³©{oó3pžK 0>8kDTD.Uygr„k‡Îý´Zêzj¹Ÿ¾{ Òöq(Q@ûßCµ½_ì|/(ÈÈÔƒ—B¹× ý�Ôæ?‡º—o˜â3ì_Щ$Mç˜X›I(¬mî…Àv%®ß€UþWÀ'!«ê  a'¥v‰Y±�C.[]3µ22²0)Hø�ú§u+5TÛj·}Üùjyq¹S�>òɰÕéVŒjÄ:O9Ï™ðÙ)£ü¨Âu¦ê×ö ø�xϱKÃgâZsÃ{ÝÅí]fû~¥yŒL/7Û£ØÍx]N�#Ð BþJQ§1ß’ÓÅ!œWg-—;f®ZSÓ_ ýMã<„W>´ÿ˜uPâ•ÃÿöÜðýðQ¼âåQ´)ðCQBP´ð^$^éãio­œà”RDü3ŸèÃe, ZAJ:ÊþL wÉeÈ~L,V#ÛÕ„ÓË$c­î´Ò/á3ÀHaýÔ·ï/±×]3|sdJ�Õí×$y¨·Û àåPXö=§ž^ñ~¢œå—om{䘼s³Àý°ë`’¹+‰}W Þƒ¸gA…=…}4b¶*T]ÝIn &ÁìÖA°PÅýxÏ:çU¾ó/½ó¢¼1ªý­fÙC–P%@36öú>0™ôyÅ“Ì{ÀCý±YŽ,™ìίiÿÆ‚}2`ãì‚j¨7 㥞]/aê�–B=@£ •¶�÷t±âe–ËãϯÿR Ê- 8©Xz^Ášb¤Ï•1ìâÂ}�ƒ–%V$ž�m¦Þg½¿KUl†ZÝ3hçôõñœäNgH†O÷Ò-tCÈú)Ûü†eÂÑq4Ÿ€Ã'Åx µ�‰ÕG½»½MmHq®š„Ù ’ÄÑRÑ£ŒÜ@¿@Ø·éBúL^{1ÃñQ%3sN/û ˆ6"¡¶ü«ýɹñààPKéÍ#[µW‹È +±ªQØ�©�ô«§æeœt”m* ÃOŒ¸ƒŠ?ò'Pl\e`7A�•6Ï<«ÙN"©ïÿ£{)É |Å [âóP ž:pˆjÍü(ìñN"s}/¶ÇÓÆw”Œ!ÁM#ˆ!þ˺¢¨ª%ý§°ý�wÅY#ÒYýÌW@¿P<Óx×"‚Ï0Ë�s´’ú£3aÒä§µ"öˆÏ¼ØÕjõX=úAzþgØ� `ò”ôQuÉû÷:Ì�ui¸€´JÐè[ Cµ'Këb/*õí²:©Yv1++æ¥Ü«odƒò®§„î%C£Š@ò”$ËðœÇ�ÿô3Ç\”€¨\*þîíS%ºÖv*]^`4#ØÛ6/Í»9>WM_"žC†Ji²œy†p˜ Óóegw£˜a£˜¶LãCú±_L²‹½ú3¼Êiáße þR/;;«‹]†å•ÿ$„ù¢Ñ3Û>Ž¡‡3t¯Ýð›•fùþúddÌKØÍ]B™5Ùó·òÅÒÔߺ²L `Ûnß´©Ø‘4y¶¼¨¹šYl]“e[Å¥qa z ¥¡lX‹ 7Ü' ãåí LðK¹Œ™˜0®!9Û„aÃ8ÊZlЙ`êG60 >| �·ˆ•ûaßU§¿eÒ±bíyà;Ô×CÀí/lr Ä}ª÷öLvêyé¤ö'›þ?°\º-Ô6d7Ñÿe´�(”ÑÛ]Î=ÀýÆýÁmÕ—µÿ‡ÑVp#è=†øúÇ­µCÙqšêò7^­ «SGÒ&KŸV,øþ9™ËÏ�a´ £Ñü1>?éS„gáàþÁ ‹�_^ƒö¯ÆÍòFÛš'Þu]�“ÙŠ�kýQK� ©kÏ!V64Q‘Èßy°lB}sòÓí•?ß Î'­èp¡ÏEOetXŸ¿�¹Ôµù¬ˆm${€" P?Y²Ç™*qÞÐJ A¡ñÅ©˜”pÀngzèÖ¼ú¯•”¶€WêÌJ:A!Ƙ�Õ¢°ÒG¡w]­wcSþmg¸””ïð«cݦöæà#£Ç†¯ =/ºaÊ ’b fû0W¦“F BÔÄÄ_ÕLtôee©´]TßÔñEs‡4¾¥«tffJ è}ÕâW™.}³-Ê%aý™þ7p"Õê` êÜùßi 2ËÉë¥'C!Q3AGþð�ÊIÄ~¢1pɺÃIoKÓxåüQšp"~í¸ÆFòOƒ�d(Ößc¢õ…M?óa†“Énå§kÔQ.lP~”±[˜L¯f˜´r0ì�+¹��5¸‚ös5–Ž.C�FfppLú—r¸"vs‹Íµ}Øwa$L}ÕÌ NãÕÌ$°›ß©åQ—Ç2ÎFy¯Æ=~2þú:+X…×X8œÿî,d%œ`S$@EÈp�.\ó^ÈVÈ*0cåà%÷ks¦õ:ä²È¤«Óˆ0£ò`õ O ¼wc:ŒŒžƒi‡>Ú|`ÔU1 DMi–2�Î$ ö ÕVdôk 0 û£Œð<«WÖ$²ê/IkÍsAS�QYÅÒ»­ÕòxÝ2¤ ›óã·ãeÑ;2/àß‹‹á›*¼Ñ.±°4Þ¯¡¦Õò7´ÕPÉ­Îæ8­9ûhĘ»æÛ8½9.ÎúÍ›]vä½Á›m6ZJ²Ï±Wü.ùÃd«¸ØEÉÒytñ¦!|¤bwv^ Ÿ_ãã,Íc8™o�!ÅÔ‡�¹,ðœ]ó;_‰Ô? ä�ì½ÂîQ¤/JVº¾º¬íx{¸FÙYd˜ƒÝ*¶‡ ç™Ýön®ðe¾£ G!y¼­˜T‚i{}V|¡Ï×]^¹p9Bx:|£î!j-/£lÏêš1b-Ü#U•Ì¿û­[xH“º…Çá¿4Á÷‹ý„‚úÙ�î=Ä"ýVÁUš¤: ±€¤Rpâæy¬b¤yïž.&aÛ$×ÙMŧ*�C|ªéN{OÍbc¯±;in>�°ZÕc; ð—<æ žÙGÊ~u¤¡9Ⲇ•u¤5ûÛÇuàúëêÛsY»ÎžÎÀ‡øÙ�§Ö(ŸŠ:móˆ³]©UA¹¬ ÈNv@T†è&ÃüQýaÏÑÌs†¼,?jzeF&‘lW˜6(÷Ýqw ¾–rˆé)‹ÚðÞ­%?ö5_d©X!Jé_= ˆ¹ÄúÑ‚p²M±Uæí¹^øCW&˜B ³êU¨9äÚ?¦<çn ñ§yp!Ä<;Éÿ'=â|*§Ô‹ÚïäÏ÷öÆŽØë Y#á8NåÐÿE'¦,wïgdš­¬ \ïOÃðâ&Ä%+4âéù¹²�JŸ—;oÞÜBØ�c1öA-¢¨à-‰6sº°Þוæ½é &^v‹OpÍ¥öL5[ž(€mDê½øÿ—�øA®��<Ÿž5œiéÍv)÷’KŠèh°0)ŽB0Íiœf)™,ÕÎ6µåQpQâìö“_¶†òcD<ÂløÞp{ͺu|c÷¨˜Ýî!öÁR±—Òä?±‹›d-ñäõÜ]æÃ0„ :®Ó�“sH>ëÀðm–gŠÅ?(>2�<:ÐLv/<³—¨§Iëò#Í/àS­«Ä»�ñë È?:.·ã+:2×ø E;¶àóÖáB$ÍööÙM@ZÞ„ yêžtüNf{AœAš7;>‹«:-ºQ·ÙNIÅ!dßÿraŽf&�þ‹™D—ä[„_¤¾U¹îÐs¾å�Ó³þ ¤u!úè±÷¨~KŸ~¦›‹"þs}ëÓµ¢Q¾JßÃãšyIgæY 3¢,ÞWup=JEgP�x55‹ Bƒvp“B«¯ƒb,ÿøTwËO•pº þËë—l÷�àÕ­Zq£çXl IãqóÛþ}cL•8‡5n ¦>ºÔë¸.6/µp´šÚ=’‹Á"$ 0oAèþ;M«cÃh›o¯Wù¯ŸÎuñ]žh$y¾_RüŠjh¨óÎÇ:r¨¹©O+c'¸9†üKýƒ…ÞÅè4q)z®»þZíåt€Â&}V¿#&d³-ˆ@©-u{�o tÿÃÈcë¼N±SÖ¹¡ðmûîEéDü¿¿ÐßÑgŸ¸R~)ÎágÔ‡Oÿ¿½¡¶ƒàú5NùÉŽ»‹¹‰¬íZIàHÍG&�|ò´ä&–¼ZsIÉðÄTã ¾W0È|ßÇ2WZTBû¯�ê$B’�Õ`�“ô>éÇóÒ�$åB:̸%„VØá羈;7,;¨W�®û­�e=U c´ ý&* þÎM§Û?ìœí”vµ¹â†ÈŽî^×�fµ‡ )ÛÖ³E0у ¾Z`˜r&ƒ¥���Ž�ŸKØú}z£ÄüUÀ—&•±·qÃcœ�Aƒð+Ÿo¹|° Ùî óÏ:tª+ûˆb|øìbÀÌÅnóËíê2s+ÎÀw°6‹4´Òü�3¡ ¯¤Ž ß½ÒìJCDâ‘ý·¨¤'OgBÐB(œ§ �¿`Ñÿâ›:Võ�ÀË[2^hüµ•ç†Ø±xÔ`Ë]^Dl£ÿ·Í†]èÛòãüXN¿¦õágÅiªýŒ¥¿¯cdYˆ±äð/ ¾£ñÆý{í^~s²/ <ø(û��áÚ�;Ã¥uëC�¸;[�ݯÝò:k¹�!J¬V÷þ õrxwwx‰Ô§Œ*Š–·Ük*¼¾ �òÆ‹Vœ‹ ~¼‚‰ èàH¼Åùפ&/^»¹:!™\/™¼/Û›øï›�áÏà@" ~›Gª&ý˜žZxø+ÓAÄ6ñ(Å:Õ3Ð�#Ü2¯òUr¦T /®1�~fî›YcN'åßUH‡ÚÊñ#¾/Å¢ÏùðQKƒ‰Zõà`oü5ÏÚÞÖVgHÛÞxëþ%—^ÛÅXÞ ºegÔb¹uo9"¢äÑÐIž§ÇwøKhH‡Êù/%Î(¨ oÚ¬”ƒr/ÄΊ#‡;=M쪣ë{.�p‡é¬½%s¡´½š,-�¤DCµÑ�‰�7Æ¥ ¯òfhÕW�[/ÞIØ£Ò� É�*8¦é¥æFFM­îg¾—näÌä°E¼žç¤¤¿®¬Ü5da€Â6 Ì ?EÌɤH `yÈ¥mo£:´î˜¹WC’éþ.âôHüáŸ1A|ß •Í¢r{&rÄGÑOüZ!ù(Õ§weßmÛ­lÆŠ‰B#ßb®¹ ì¹ãºÛ?\[¾u¼|y¢ó$[´yÞê@„^å|Ñ$—6mÕèÿMqê§›ŸÄת²¤m³•í»åà>ô·u�=•²+¾J~Û0Ìóª­óuÝÉ;χݔì˜ÎQíj]…U|;³'²ßo.Þ‰©ƒ»?⯻,rŸ9�æff@uÈ !LxD�—?¥!7kþûž² ³Dô æÚš —`ÔÁI™ªIeYZJ ªp “ª®2Ÿy-½ô­Òà5Ç?l3ÔY�ö°„t×ߎþâ>�HFùüµ‰å" ÑìÄåsí(ˆŽ‡Ä´ëMB}óáVê Ý9~ Å®Ö_é�ým_SËeñáÒ‹x—‰ò»FÌ=ëJtuyè^L…¥¥7´ì×þ—ví– ÍöCò¦e™ £Ù³w á,S”wÚè‹t}CÒ]]ç>áO·Ö)4ŒÏÄH*�æÖ½RòÆ+]zÃV+�ñú®Ä2•7F[-ÂlÄ{€ßÅe:Ë[ÒxœÒE£ /ªOH–®ªÉŽF ë}éÇVÓ–“û�šêDu3@މ�‡EtwÄíÞ+=¿AëèKó²h½×Ó,Ç×>›•ÞwXy^üzq|Ôå<{ݘf»Åiä*Ÿ¶Âå.,ì|kù:k¾f|•³¤ô›K ÑXsÎO´dæc@Æ=0É/<8e®Ú¡•õÜ%S¼­×ÕäÉCSÈ­Zrù±�’Sò£=_\ ž ÷;VÖ}$ï"�ý*Ö‡õþ9òôFðlª�TRVbþ)Ñ-Y?œ7®xúç;.U™«XÂ!õ„…Ùzí{!¬ÙpÜo‰±¾ê|-ש;y¸—f�Á,N}jF…ˆCU¯=Hó÷ôÏÓòÔýäH;¶Š~®“¿Ã¸–ñíÏÌ*Û1îæ†¢Œx¢@:ÐŽÀÛhNwÆò†«øÅÈëÌW¶“èÉ‚þÈ…4œ¬âÍ ì0Éd›™˜�Û Þão;*)š"rÈ¥©Å“‰³†¡wËùlw\»ŽËo’8+­}¾FÃÅm�tÒ��Ti©a÷9všœ§`(6ZÈ(0ÒîáC²­ÔGTv~æî]Žß/ì:_Íûžº4럑W½Î¦Ó"¯WS.)écð‘n8+Æ0\ý*ZE31ÊÎeƒ®ã%ë³+Z ×L¿»dvÎc¶G¼ÀéŸ?‚Žôú‹®àÙwrbê¹ÁŒ2éBÞf޼Y|Sqúò&QZóÁÌ®£B¡#d€~P�šø}$BWç)‰­ÌÎÊÝQý…vsùYUŽÝ×=ráÏ$,ôZó+ªoÕô1ób@2ò‚Èpk²\'ëD㣘Qd”RdÚ�f¦•aŸü<-[-µ3=1$'3ÁÀúhšv§§G`6Ř"cS ¼�eÈÒ‡G`ToÌõ‡ó˜ÖGÀ˜g¯-m/úrd?ÙB¢zl˜°µJλ;KÝ3nR Sq‚BýøTQ“ÁÿæÛ ÓÿñíŽõÿ¿÷íÚϾùÑ·?,Nßlý¬ÉûÅ 0–7ÝDˆ"”@þ1-…M(äAÀûn*öë¢g'Ì â�±lt´ÿðŽ£Ãcúî%6VY¥m&ÆYú­ ÑÙµ±*jâÒ76Ui½r]}âº%?Îݲ`ÝCôFâßkaií Èyu®£B-!F´X ±<†€ý|´ÃйMé ýŒÈ¾ð¹ãØÐ½~¶q€ËàïN�Ñ«#SP�ø§´MÁ’4]0íz½®–I/X’D(GzÅ.Yð1x¥®×°A¾°Ð·�prB˜üO Pú«—³\›Œ(`6Ê|uîšþþô×9Ô|*A”²þÑ É�||Ig{pö÷ïá(¦§¯Ê2g î LÞfvz)¸{ÔdÑdŸ;œ£(cn^Û•8!VP R•‘˜”*ËŒ&C…è]8}�¹GC` ¤”Ò]ûV ;FG‘žŽö|òÑÕ×Þ³— §£ùÝj MçÝ5\~dz÷ªÑVS†:Å ÇuÉyäÅÿ×(›¨½Í(°Mæ™ÕYÖª×ûÿ"užÓ³Ø}Âí9 �ù9¨µ|�úJénzA‘J60†dל`ô€J:¦Š*GòÌ‹W–éB G³íóK$£)X_¨ kJöÒæ«-A&MS“ÚeÙc,Xˆº«2¾ú>uµ K”ó }“w™+¹ £4cñ¦ëam~«Ù¤Ža9’¿ R¦'Ö½²ö+m�WñgàYò`=‰2 ö¾¢è]™åCJ2e/ó°klÄ,±p—-DžbD,±�wob=Ý.éÉÚùRSŽ Ä‰‚ Ô^ôpÿ�{pœNI§�rú« ^Á·þBËøj‡eÇñÝ”[®9¹+ÎЭÅùçÍЮ±Ÿ&¹�˜Þ'xd#‚°Sàv($]IÔ3$JZtVθÝq~È@!RB2äz~å' ´\(õ”’N=ƒQåÍ]…p[ÑMµÆÊÄ¡-é©{È�¾DŠO;Ž”#ŽÛ§[K:~©Ne˜ê%5Ťê|MƆ`žç[0Âì…Q©íùë)#o–pè}!!ñnÌï—�n ß1òâÑæuPžÙ>Eµ6¢¨·þ�FC>×3-3ì²›¿�K¸na#k€¡Eøõ}™ÇD«YÜŽ¹]]÷M3 WÍÆÄ-ç ´q #`W-Œ™Ýó›°ŠzEË}_Ûè@Ñ!�ÄÚé„¥ÐUf„ŽÓ‘O ¢�Z{BÆ6'vp¦Þbà¿«,נͮČûDäãj  ! †E¡Ô¼&¤”;‚=«»!ƒú�Ñ -ˆÒhôc`‚Ê74HRÊFÍ\/š‹öHªŠÈœ 5Òæñ^¹SÂv6,º„Tj‰Óü”–˜ÈYlʪ céìŸSJ¢;jœêTÑ—,š¤0²F0æ[Ç£Q=Ì–ÜI-ÜvTœDKÑ'5Ö”zçP[å x'ØÊºâ òŒ0r÷Í¡0 S¹Úî}aTór[÷×åiïdåBÎD$*?Á('ãc8h¨zdæñš�©áÿx‡ññ̳¾åêËçèÌadž4r¬Ñ`ÂÉñb¢DUuúÁ¿ñ«àæO³4½š—Í”>Ô Kçw á֮䕮 µ!Âæ�©ÅÏ×#°ÞiNV–¿3I‚O ;<ãe™ÄÉé JŽy�Eä1Xï6/Ɔð„½?…\t[�æý;<—úEûÜV?|]k‰¿ÇJÝd`…-þõr>‡JG„‰&Yµˆex_JûaOœ³’Þž–ÕÊév‡ÍBÿ+½ÓT;¸òhhý<™…Í@äRÊãæþ‹(×Ç Î÷p²˜Å£�šŽÜÕðÄ|¶¶¦Ú�uòÓ Sç~÷†r©ËuþÅ·÷à�ž ºí6qÍ_–¾x-69{'œ9t©R}N[È)Ÿ4ðµïX´y¸^¬ÐéFtƒÉ7A¡O×ú2¿ÒöíÕOBу|TÞïO”“ìÓPr]Ô*˜A>“!0lÁ1?>ÍðJØWħéïÿ&µäiûMm3\*$_Ìn΢#½…MP×$ç�Ž×jâÒ(+éd‘ù‡lQJñ“³SbÉë¹�3�ç�´´Ï«տq‘‡Œ*Å*.8Ú�Ô‡y¬ÍÇ ¹�÷x4ÂvÌ]H€mà{Ïl¾!—`˺%3N.sž$„å.ZŽù¿’ìHÉÏ+¤¶ÌÕ ÷Õ?­ÇGŒkoäí7Ê÷9»Û/o%sæ6ïê_ú ìM…è_õRŰè,Åœ˜|icåt]ëþE�B†UÓõý.ÝòwÙƒ’‰‘•QQV^²ºŠvBEÚè:¦½KãžçgŠÅÑ`kõ#M^E~ý3Ζø)ù¢ã™m…€¼0Æá 9…öùVÅ+&µ¼»`ÖµSÓMÞüfµÓ_.Q¯|<:í…>²¦o”÷S~Ã+ç…*ïªgç ~‘afSÛü}á Ãg8˜ùªuRVçjT•´ÅX¬ó²öª£™XÌÔ'm§úƺã9.»C5†í?­w"ê>:·þ"íyo¥ã'½zj�ˆÇŸ=ì9-ßèyüòe�Þ5.6í¥U˜kîÓ�h|Àwß@4®|Éã4‘Úòü¯|€EI-©-A#7jø ìàh›Y?¬]÷ tHz!YQÏä¸t%jƒ  âï—ú"Þo�Àú|áÉþ&-#�fêeøEŒÐ«^ßd Û>¬­ÓJ`™1b”¶»‚ø­ÉåÉaÿÔen¾o€‹è@1Ó« @ˆ‹��Ý8QÉ!õÊäý·#Œ¯ç9˜MSYB]�)Hú|ƒ¡ï^¨…8Ê¿m\ ¨*¾Çn3 Ö׈g«ýVhó¾Û&¯fJísŸ4ä ;LËâ�˜ƒ{E÷ÜyHúˆUÆæ¾%!nÙÊ‚Œ;óóº¹qð�¦qzR/¹»Ê÷ù£ÅI‹Ë eN¡#Œòí.ŸµfBxû-Ä#eKº‰¨05’—ãlùÓõeÒ®·±5â¬æZ¶Çg•_EµT Wc k�^ÚÅWþ蟚ijÜýhZ²©ÜG6ò~Ÿ]ª|I!ZwU ߇`Ÿp'U²c £T+­¹¢ /ñ‚þÇö%îv�’¥®ŽD»4O„U´b ksIз¢ø–L¦¦!—’R–8HÅÄˉ;?Èý"ÖœÇ1Õx·áÒŠÆázüäœøƒ=KÚq�£ã„Ïéz[ÆH¢iRL4¹3*?å5é*¾² (WÊA%{çðÅL¹€¹lµÈÍU<¬V’ýSÓù¦ê&æ[»Ot›%»‘½X¶½ %ׇ a�œ¤W´-æ_BʤâEëlð|¨æG”î6/·U¤;œóF–}ê¨ß}æ�Û„¼„ŒÝZÜIÙˆ&¦&°à¢g‡Üü’"š½/ÓWAØQ² *uP>šv¬xÑ�x'^ËDTCÖb †!f/+„êâ<ó¥<…g\ÐÄhZ�?„è½Hž-S+¼Hbˆ(·KA|_˜¬DÍ«]�}W(+µu’²²~rŵøë=I±Y¾ÀC…ÍÖ6ÿ}–…¾t¯%a=ÍÞ§¤<ÒC_‡C?þð‘M2=„ÏÌótÄÐhY»0Bè¢g—¦£°4«ZB/~lpÏ‘ÄÒ{àŸ­bø�GW—¸[õŒQ�°hÏ3‡6L©¢éX„Ï­Å%§8õ²Ÿüx‰Èõäг2YØLˆ¢�çbÕýK!6wûã"2n‹q®aÀÎ�{ ÅCàý›¶"¢õÙ��š{GÀÇ· PœH~n¹ß�>þ ûñW©ïq!ÐwÎÿv*IÇ(…Õ�5XÂl�CgòF1½Æ2.’#øúa¸x¿«#IÐßSù"Žøñ…7”§7?ÖòŒïB£ëæ¾}Ú¸¥<�©!pö@A+lO5QwZV½fØåɳ £íjP מ‚ž(Š8󞆒ÄÄ{c(ZM�Ñ –%Rå0×範M´y"·ÅÕÂññœ7á †›%~-ŒlÔ¦/½HÇëÀèã§^ »Šb¸øsííšq™bØ!3÷ žúฃÃï.¦ÈßH��eŒ^\…®Ý¸Ô“ ‚›Ã0K% Ö5èÌVT£5 ÏsËTɾ“!<ÊÝŸquöQJŸ¸Ðú�"ü $ š·ÓÌšçºòPìå�Oe¿ŸÂ׳þ‘ONÌŒMêQ©¯]¶|T1‹xǰ4`?lÊæK½z�‹~ŒWÏöRÃæä­ÎŒ%}f�!œ} ÷ù}=Üë͘{^A0Gë²Þ¶Ã;óN—TÝcÀìöÓÍfÐáQfÈç_Úv¬¹y[?˜7-ÉbÑTÐ0:OV”ÉLLòVŒhöržeã.m|96w”7„§6´Ô�j,ä!ŸJ ©ø(n¢Ùei4Ö)Ú–wm=¸Ã#o0Ú®Õ÷Jì^š]w®`)WG>…KR’âE.aŠ7¥µ‹NyðèÒǶüT· H Á4DÕß0´gv\’²l/ ê'C_ëÜ;ØÒ`3}îï^,®l¾J0ÁRåÖ‡ ήœ©ÎéWq†8.ÚËÇ_Ê™áÒÚVζgnr'š¿sЕ¬0-b$›�nØðó#~*ÜÚOJÙ¡TýJ¢FªÊüU‚_ƒÈp p”xEDšÒÐK0€órDl™LB£º·°Æþì+«ÌLì”Hû9C~ºM£œ©"…� Ynš‹ú.:ÕÞ^z]¶¥ M«#üK ¨wƒí¸"¯¿G¿âm£G=‘b Þ¥…FÒ Û@W1ÐÁçÒSÛmW¸ÑçmJ9Í(Öp"š×)�ø©bBêÂaé9àsk7¸@@hí+M6@‘Îê°NàßU©ï–ËÕ ä-I6þß< ©ÖT— m[O>}ªp¨sh}ÆTê¡�Dž„I‹l5Llóªr®DWãëOM@ÇSV¬‡Q¯ñ;�ï[;:»ºC"Î-g»ÚžÀ[0¿w=€ÀÛOÐmfÂ>îMõ›„r’Øe9·ÀšLÈhÈ—!#¶‘–÷  rœ2‡) H2“üÈÌ æc î�PÒ‰î/OЪ­zU÷tXQ€×EŒzi+÷õpîÜ û"¼úaî��ƒ’�{ïbùçC‰�ù®~ãl!‚d µ¼KÊóQdFõŸKŽS‰ô¾÷"ÜùÓÄOþ�°¨\1š‡Ÿ~Üf6 ]VD°�ds#Yz}ëi´LÆS¹æõ…�Ù†-+q!ìUö®6F¥ˆ�ç�üKñ¬®dqùµàVâ~§@5Klìºoé€ùs…ÿà2ÁÐ÷—ieÎë«Â¢‹0”Ÿ§d˜Ò%®9ûNúÆù|7� ÈÌkZZá=*u¬tü°ôÏ”Ñ�“�O­2%½ßç9;HvªÌ1É8»ëIç4³ÎÛ1lkÅûÝ ]š® bDQ–`™CY_ˆzF¡!Á€o(î o'GÐU¡¨“ºÍfG™5à€”Ý°”Ôê`5ÚÅlŠ1±¯�“ ¯`k/gˆ�K%ÍïdI¹4Ц$µ‘ ÚŒ‘{8L —?;F’ ƒàDÄ~Y*ÅžåôãÏMüÊRR¡ÎÿøãŠr€4S&G÷Nk¼}Mùao8ÉõèRAA•˜¢ Ùjij¿ ­Ar¼„Ñ›5¢™÷Û4³ÏbÃ=!^&‰P«¯@Ù±ë–N`ö.)®ëDýåüpÝÀàBÍ´¤dG$„{ÆçI‰gÖ£[H °ÜèÁ�Íó}_Þð˜•w®F‹øk�wkïà/$úV.¹Œ âDK‰¹–t!ÂNu§¡Z‘q–pžEa•\ëýÀJfÙF”Hn¼‡Š§5ô‹—Ê@.Θþ æ„“ÜÀó‹µõ!�©\•.=‰ˆ}ä£Üú±;¬ÖZØð¹ï�²—½p#’—�”;¡®Ÿ;?¬°#—dÛõ<·æÜ]«²m DÜÀ¯ýÚÆcƒ-]*†ÃÕƒJ^xŸÑ ¢ÛTÄN3¿ ?·;\)9Я¶bÔ�“§Wˆ3Ô°¡¿“§Ëxâ•ÂÌÆKòEñF©ƒiŽŠ&Ù.šÄ {ìôÄ0´Õ¤/+$!Å´Ù÷²!,: ÛŸêšMª!-·6ÑôëÊþT¸8BÞ�í›Á¾žt¶îaçŒ[A9Òw@ñÀÑâ€ãçoÐݶý)SÇñÙùi£-¾gr¯Øq½ †Ÿ÷¼îwƒ'B£ ©üñ‰‰øé¯"O´gëõX¿W�8ðDНÀqý‰×3P¥Ò­>DÓ÷ ”põ™¶ ]qÇQ@çÈ1bÑ…½ï‡cŸQaã6üqpL�clÔ\D´’bú‘[F�MÂîï6/çÕJR/ž°þy~7]v«¦27z«Š1·r_9çRr÷³ÊuN~«©ø¦~.ç“Õüc_*f‘ÊŸ‚_y,§ºÓ¢£•Vø|1¸Ÿ xN×#Ë;C?—¥ÌŠ-¿D;³CÓApœZÀ‰!Â`ÁŽÿF²¸�í)c=C�ÃDè´O“)UÓ²B®<]„¹ÚŠ<óEÎtõ뻳Åî1Ô�©¨êÂ+U8qÐÑvÔ§ûA@…4Sž¹ë°DœóŸTf>â£Ûì7Zô(�ŸV†¶ã§U†ë|Nåí÷ÆØ™Rgºýò³áps+gÕî¦!@-ö›VlÜ™ãéÙÍ8áàRþÔӔĄ}l[%ê77µØñe¸ýß <üù�<Í+¿º�éøxhK½í·¥>ÿû˜ø2}]/Œ™‘Ÿæé,ï²Ëø�FžòKÐ37LÝN— ùw¾Ó6j¡Qn¢ôµUJ$ŽpjÆr5�…GòÍ\Ý�­ƒ°öZ@7ö…½>QÅÄ|>ö$ï5/‹n.Ç&¶°q£Ä~&×½,w¦L]0­y€Évn» ç{�2ePø€ìpèÚ³ÐòoѓҩïÖÍ›©r Õ9) ì¶²@™: /ši§_é¦MpTuœYMéw˜^íGºNM1«^ô¾±N"¡y.¿q®þ|bh¤¬»‡Š˜˜ï5ÝtŽ8=]^ø'LÓqD–;›Æ´ .ZÞrÅC´3!ºç�ÀTùm¦ˆÃ8CŠ\×w¼7w 30¼%J¼™¼}&˜Û=,¾g_DUaV‹I#é}djÐf(bß!åçæ0Cd˜�0É#Ã…†iÍ)3.nŒˆ‰?£pC�gLÃ)lvÜ‘³~nMØ%kRüÎdUiçK(r²³�±ZbøÍ~)y¹¨¢,ÃôB£RV2ˆP‚A¬hóƒT½aßoüŽá.-Í$ \¬"£”�0ïY›eÙꨰf—ö¿æd£¬Vµ‰sžå �ìï?ö!ä à*•à ħo(E!l,£éªÍ'GÈãøOð«Uüj¸ówáUÐD­T½§€e#\€fq—=æû .]¬×Žó¹£í\±„|Húhö9ÊP 怦ÚÕ…ã´ × z‹ÈÂ$#íý¥ÝŠAóéŽ7Kã†z 49•a‰&ó*™#]@^èuޝ5WT¯Å ·&2¸Pw¢h‰ª;ð†ü&¶ÐL>±¯ˆá@[5œûÑ\YÃ�†Cˆ m¯†AO2ÐJ®™çL mçGW5/¶C`ªX:ŒA­ÀR’¼¡ý8sé ‹…úö5ê/NH!ƒŽ@V0øH^ ÜðÏcé5RëM!ú—j‰¹L?LE>ƒ ­”Õ%[ÌAÝa�Í—gÜ"øå¤%RÈ,ªÀÈjSî^ YXFóÔç%õK$(µ¹ýXV™€ï¢©ù›ÊÕÅÇ�ŠïÉ¿UZ`N~ðöojYLþ1ª8× *ÎC_^¹ÛrŸm£Ì¸wù¼QÒ#aèê“Òsæ ¿öí]ÿûX‰}‹å‰÷¹ñá ;:½_ò½DE�;á€Ke[Øq¬—i¨'b¾*¹Ó„É"sÑSH’ÉyÐnqà!}¥¯j†/Ò˜úŒP3BÿuÙ �ÿÒ#ÕÙaâ,ÊÐŽ¼aj–ùA€OÜq­X¦(@QÂèt8¶³ÃYÉ=�‰ýǯ§|;BãÖNë&K”§¥hCîøï|¨ËOVе$¡Êæz3Y §NeØr¦;•׎xOAÁ²­vßm¿màù °ã„î^anT&5Ö¡ûœ«¼¬CLš­IMósn¶d(;�Ž2)3IVÃ…<9ðѹ厚ãXý=t–S$ŸGÌ Úôù>Išª{ÔI—³Õ–ÛG¨«•©0Yü,™ìc£øîOO…–ˆJózg×Õ¾•·l”‚ì˜qá»™Ûx’ñÏ??ww…3Úß2NÎZ" û¨¨f.èrEÝ1oøÍØÿ]yò±¸Òè˜ÒÄκ²8áq8�9M ¾xϯwäí‹Êó¶&x~æBê9Ì­S�pC�šÐÓ¿¯XIM–Õœ&„›êÖÕó�Ùox.–ºðn‚ Äÿ�pé›AZ=ýõ tÒûLO|Á×é?–oóHq¶oœÕ¾/ ~"ø3û\²ˆ±â!p›s ø¿Î4Œ“¬ãW‚¬Ø?$¨r7üfyÂÿæÕøSüºÿŒ¸P†¸dÛÍÿ‡ jBwaî ïÎ èkÚ„}#*>ÿë4ã“ïÞ £¥&må;‘`iC”I-ühb±”øqì;ƒÁrª¥â´3áv)0@´p«§óæ©uá .HiâÈ.¬?Ýf{![Þ‚F}mÞ�ÿò��$€†ÕG<ÂKˆÏŸ`ëÅ;ý{­±I¾n… ˆPú‘¡õI„æ¢}´,ar¥´HÀ+…ô�Â&ý÷µþ¶µ&Ž£(V{‚”’ï—ÞŽ]y*OwŽÁrVð²L.Z^C¹ëÍ�¯wžÚz¤‘3/€óÄëB!!ÙÑ$½×†T?MWñ<‘_å.”õNðô#ÕI#‚ýrFŠ:þ! ýãÕºÿu/Ûü{wþ¼ðòRtó7é}»å_¹:\˜˜[]ϱo_5²FyÝ{㠛ʯ&d÷�{öI°®Wk¨ì™ËSz+ëÆ3³KFÑ1 •µËÎhÎn¾ô+s-'M‹ÃÀ¨žëóè>ƺßG¤8|ý° îß²ƒÛç‰Ð‡Ýµì.§í˜êªmùd øÆç¿Q.ÀZ°ß~ªqÞìgÃ¥¼c±³7Yc]ÊgËykûÚ\Nœ�Hd Ȧž¸ãÈÿV�èrøb‰åë ž°MMí쟉l¸?gÀZ½â™V•tšƒö�ýKAI’‘1–¼j€|ÞÆÚW·ÍB®5›€jÞéà\K¼h°0/�•x £ð÷k`_ ¾@Qþ‹qeBoâ`çhô¦©«ùm>•ì�ܱz>|¯‚$êöåô}¥m` á“7üe¤ÖU EaK©éîHC&�ÄvLˆ`xBë™{²•5ÔEâumb.GÀ{GÛs¾&fÚkuï··D³b«rÄ2ɼœ*oþueQ]%IÒòü¥#Ólc6ÕM Dî�Í_a Tú…¡üþ_B3+Ó×?¸ždH%#ïD�³rÚé ƒÈ¯5Išqjƒ¤’9ÆŽzòõÌ ‹¯“î£[’ochC³³—(I�pD76¡QØ`ÈœÓ�ç;†×zÛqÝïä‘bhÏ»�ºv\Z™0¢+Už¯‘^8^®'Nö�ënëë Óu@Ýr¨Üëçw_\v]3÷‡¹rUR¾gARp¢rò‘1|0ŒTëð1Sþ9âŸúÔÿüs“ä õÎOnyÒÉ1ëŸB¸Ö‹€‘úÁ̶Áa\ö93Ûœ�½½Ç£c^x�dÍ™'¤æ$ÿú_ñ& p,Ò dÒ_Ÿ¾{åï9Â[ðÓ—S†å;‚™Ç&¢JK™½¨)¯­ô*ÿ 5hó­È{Öµ“6ê—­rÔÖYëLêò0•ðÌ|­ùhþQ˜í>˜›J ÈP«&ÐqMökMïsºü Œ ÈP￉å"ˆv-™åq{;’sté„Õá,ÐþM:*…øí$Ùtç#šHûÒ®ÓíƒSD /ˆ-Ã�ŸÓÐ ×B Òwg)pìC®“AŠ?è Û›•ÖD#´»}mÒ&úpJøþ2ÅL¸n‘Ý>ú Éëêß?Ü\ÜìKÍâXJy@u�§mc£^S*OSÎå½ý Bزl–WÎh£H,@B æxö†s(yÔÜûTäËÇ:Ç?sj0�ކYÚx„tÝò�)­pPönoÐÓ#ÉA„Õdj_·üŽ@«a¶ Š�ùÇ™åŽêÏee08ä[’›vIrîTüqãÚ/Gˆz/Äo*ÆW™"i/�~q6 åÏæ%£ý©övqõljº{)ü­”?ãAèÙEþÓxÞ•ñòŸçX£Wa릊»*¼ZÑPèW‚�9 z \ 7`௱7¢Õ¯�Yˆ„“J‰×ÊÝÆ95{L÷üÙ˜íL­åVf¾>Ë`sWòH6ÄH†&üÙ¨‘q–m£?ѽiŸJÛ…âmЄ°àz;`�ˆÿµ¸lžj™+–â–.…ßtìèWˆ|+.Á]#`Û¤—á~ÿF‰³¯xdäÍ IĆÞ¦¼î�>å¾:^"`“¦ˆãã:ž±š@žˆë `´Ã¿«È—lê;Y|ãŸ(ìòðü;³7vâŒü怕wM[À”WÀ¿B¼HÕZítM&þn~\b‰Ÿà:€Ô|…¹Tÿ ]±T$®Ý'×µ$ÓØÜÅ‹§ë4á½%ñßɈÍ{`A¯5� 0òW�s…f|•êUƒ}bySÏ·ÍY^ü£X Bûa¿sÜ%Áb¸½2U\£x+¦??‡ûý*zàµ÷>ÞÑ^ÂýÃU¨/ÖÐmž�Z.Neê©¥¯õ € V;e K L…9FùZ­1ZU%С6¶œMÖHëÍÝQÉÚËœ€}I¶pb7÷×¶*YÛpDÖ,ˆNõ±âßZeÉaºisMã—ñé9wõܶɦïÏ:) k•ù ÈÅDô,”lÝ…X•‡ÑG.ÍP-Ãuzc­‰È¸K’Ë j*Ž:@å×bùäÙÕ…h Ô]øtîÜ5›!|xÀ•2eà1xÄõëZéšùÊ¢ô[àCŒEb𼩞xȸT¬ 6„òé{ܸ÷ØD¬®XÕ^–²?mÌ;R gy'šHž4@üÖÆ}£~èD‹²™òh%²µcÅ�)¹Fkeçþ¯ä±*ß¹ õ¦«:âþšˆ4¶ A†¢®@¯‘¶®®H_/¦œÏ·I)c\z‡¹]±°ð1?‰¤$ÀìE±·ö~ûŸ»™/¿[ũŪ†/óìcfÁ?kn¾è( 4sl@ïC\¦êîÒ�¦ô6úÖI‘dt;Çõ°÷5Àm6»›¶xvÌõËQcs êe-À‚f�K‡Ñ "oëâDeÍ1.ŒE€žþ‹(1%›Ã•Nº8ò'ÉùZÉ鄹ZiÕIéÅUUoφT}ÖRay`û0¸;ðÑ;<³Mæ:šeæZY�ùš"Â9õã¨[@Ò¿cøZr�¨œ)®ýˆ iâl aó¿–ˆoˈ‚r<†¼Æ�ŒB2�rÎ;•ÒV¡ã†1xĤÌ¥ò¶oÇs@ßy˜Ö#PÄ…Õk¶#öuìC˜6ÐB'cžAc�þË-çjfz[†ýM–µ�“#¦ÖήGðÜ�HBþ¯ö…åç•X�«Ã¡„–î&J$PV—”’“Qø&‚•BY§OSeÚ¼îƒ:½nI."¬£»ntfœ[p•РÃ[€ùM²•eggÿñµŽ÷íü\QàÓZ75h›ã•%ÿÞÞ˜9ï€ë1æ˜öo„9ï"÷.…³Õ¢ k¸o„ÿñôÙþŸÜøõ.{ ˜gü'7R×Eý—ÜÈZq°sü¶A§lÿžgžÿÕ<ÿ=úFŽV ¦ž^fö€ÿuyŒù¿=euDR[Â÷Q3WKµ°–ùTt)æ ‘ùÏ8™·’«Çc„õGPT»diûéiú[+Ò˜ídäßg¥ÿO#¡‚3ia º.C±ö2+‰È-ߺö„9)å ´»×A�„:4ÍÕ3—hL@�fîôª'0Pל¿–ß>“Ma6ÁƒÜåÞqˆt³hqž†÷ïˆä ØØ Z [+ ²¢ÊË߬¿á3@ÐD¬ý÷Üß¾•TßÅyüthªè$!¤©vwÊD"··ÇªgïQlÌÂfs·U q°3‹Ç:QÐŽ:SÇǃǜd÷^l²g0�¯ˆ}¥·)Ø&„MÅïßñóÙÅ·“"£>XØ<¤»È±>µ00N‡ˆ®�x뇤44<)…J4üÖ¼m4óµô×i/ê,+AÈÐ}š·žÐ¨"RÓ2L|”Žc¬[÷�£Ž‡Œ†¿7ß„?~³÷¦m�6ï…Ô𣧇hùU¼« Á‚V:†ö«±ñ%ìµ÷ªa¿xÃ/†“Ð|ô~ { Ã<9<8Z =)' £Nr[chcnbdPd~2ŸRð7Õ‚˜XRЙ¯�³_;†k÷×›²»±ý$Ÿ~å¢áC˜uÎ:ËÕÏ`£C05*ðOTöçç$êÖiEJeCÐw17ÁÞŒ¤uÈdÍEÐI»ßŒv+ * >ˆ›ëGíïÚ‹3ºÖ�Obéy|*�ŒvÑJáÿ+E”Ñ%i…¶4Až×mä¹r¯¢ÄE0Ç•]g¾¿j‰dæÊ®×o›Â9l5Æ÷sj§qÊýÈ0Iì�>ç˜x¤ §bhcÒSè(*œ‰²És²—ô,­Ïø‘ˆÄó¼Äý¨Ý€½ ‹uÏ-(¢ÍÑH(ù……µ9 úÏêëT8“?¹ÖÔnÜæ4ææžJº?‡MжÔ%�Ι*L î·uµ0=®»ê�#ÚP¯/æÕ¸/·¯®?#Ù³ÎdÙ"ŽOMÜ$µ2`ÔW|´ËPnÜ“¨×¦k°¼°ž„�ôn?|±ÂLháóÿ¤9$ÿɱ1³FÝ™1cæ�a|%sÂ�mî®9×çåöîyFt—Ëa[íê%µŽ€ÚÄѽƼ˜,_*‹b»þÏk^Oãw·†W)>Ï¿0=Î7îÖõê²líbž”£�›mçVÕ ôU²1l5Vžk½�[ô†õÓŒÅ?bXOwb|0^P3[I£ªž3bÊáÃ]ÕŠ9ÊM'ÈœjŒy6.+`{ ð[d\Ž.FÝÛ²ùiñÛ½?VÌÁˆ|çyS"¾Ó ]Qä‰�–íØJnûƒ~3Nþl=5¢Å©¹‰œ£óýtí¢fõj:ÓÏAx+R>g�z•P]Éj/¦>1Ç—úH¢P¸ ›JtŒ+¥‰R�¹«vMô\#Êœ~ò‚ÏEæOZ{8ç’œ¬8�ŽÙÁÓ;õµ/ôù� Æ>Ïûe ܱò#ßžc‘øO+~»“=¾tJx9´Ý )ÌZdWMv*hÉnðÝ`ϳ$™ðUÀZ¥æ{M¬XI»žÅÎËÉŒuJÔ«‚¡ËEÉŽö“±&òVAÇit“Óã~‹ÜŸýy4e!zí·1&%‰žPšÆJ94˜—‡ÃÎEPš–5›ú(Æ&=‰‚’'þ³‹hÎE™¢Ùå0.—ßF{®BÍKtT˜ã¦Þ‘¼M¼IˆF½Ñ«—¼ÙLõUý» ½”®ÒI…·¼PôÂO­Ùà2¶#ô½ee>êJO�7é; µ6Ž%ù, å‘GˆE¨®øŠšˆv¸Ütæ´°Äg2*R|e¸CÖÆXÛÆîC®¯² ÕH¡IéO‰�ò´Ub SkZ‘pÃù8½þ%áNÜÚì¡û?éŠî®2?}€>ì�› íû¹6ºØZMñdVJˆeðËqkÍ,ýÛ�$XDSúàU:ºÅ£Î̪†xõÅ€*Òä»´SšÃ™wƒ£.h‚$mjz‚dê,×öäùH)y´£3\µäg£aÜ·‰lJ`•/æ­Lk‚F�§EPÒ"šÀìJå>Õ-ˆæ'6Ã"JÐûÛÔu+gÚÈü4„§(²0áo¼búÇ.‡ˆZõㆨÀ oµO�Œ»paÄGÁÞ�šX~�!÷e-=W)íšèé;l1»ñ3 ¦+¶íH͇Ã)<ë_iq=[‹Ú²L)?0w^ï®Ù´ Ö(–MZ1Ïy2^©�‘K—úIkwóÖs™°yJ‹;7(*Ö�Š6r(�÷¼Ó¾ßL0úúzZú­ëÆ‚Q÷È7¼UØ•3/¼sãœ|´^ŒŠ~4ÍJ‡ä›µL%))¢èý»°…‡N}L.!½*ñE9cÁ—}™ò"q†*£¡bÊñ2{V¬„’Qa7ô”)’5bªtšèùéêÆˆ¿nCsJJÇ{œ�A·”ÕgäÇÄ£B†'~SEh �ibúnegoX/E®üÔ5çÒ‘0:’ylkÇV.y¢�«Ó’åqCÿ„Ô‘¦x0±}2êDFð g>© §Ý˜Èa¼�Q�[¬+'ܵT\¥€ ÍÇÒè”r…Ò5È ¬ŸrEÀ–e,sšûãQÅáæø19e ?Ò O–åM@7xYkdÚb÷+q©"ŽsÝ¿GÑ1Í–~4î=»kKðӨPQÌn˜¯ô!»óLdÜ‚€F­Ø kÅå�ƒEÁ !UUo·7nÏ[š�B« !µã ƒirPQµ1ðã–ϼbË<>žD•Dn$>¶oº†€T¸Ÿ«¦Ó‰å5ÁOˆ¨øôb‡è#(ÈçUÍ)ÒqŽå1;û¼9F°Ð¨™SéaWÚU,'Ý}�(TE—ÊÉ<q•8!Õ;ÆŽ\û`=[Rã3U-¦5‡,‹¤ƒ�²X°Ë˜*|vKî�å¡E,ã®ùÓÓÖHfûãP1¡UO»Ìñеv…]Uýn ›”Fü“J|yçgüš!C¿HgOõæò� ¢œçHßæ.SÕ‰ñ‘{„wƒ gTs—§BËjcàfÛf¾âÚ“Ï—ÆŸhüH‚û+;ìÿ“Ø‘†*·æ‚6ò1oè{eþ–x¤Ó{ÀdΦ�è»+P}L|A²w:ªÈS´:í-ácqN»(b"‰M¯øOIW>ËnæqªÆ“zº¸Gç—p«fKï´F]ð #2÷°.ø�Hj+xL�Üg׎j*æáà H¿ò8 ÏâÓ¹°ã$b4C\1…¨Ç„zlªwüžÉò’ *qN 6$¿ûíˆòqé³HR$)KÕ&À :Ûý±—žâIäYßI#ÐUÜ_uÕÊlB/¢³z à ]ˆµõ¶˜«ø¸�¤Åt¥=;@·Q;O„•n3=Ì~Ø ¾¦FÉRÓ6=ð+ý!®¥Àè°«ÒQj�¯é§U#èb¼Ù�Ñ""ªˆZQ}1!iñ Ÿ‡‹ÇÈß‹žS¹¨-aOúmI‹Ýué\³Ùê\­I¸ÉïOûÔ–¶?0ã1ÊÊïì×]°¦ Z�M](r·D@®ÅCc±¸•uT:­Þ8€�‡#Çä!£-tkc$”MÖ9ÑJÒfúiñªÎ0æ8±z_êƒVóŽ¡zMùöÏaÚ•$Ò6©…²‚Ž–½0ïÂ~†ªIû-Dü²�ùÖßÖ"b¤�¸Š°gi]“>†W ‡V€øžù¥ZpÍ¡¥i2Òu!‚GÎ1`1µã> l q.`ö†v„[LbÏõáݪڪ“!‘F{]¡õÍæµ"¹Å;Ë/ æ%ci§Ä‘Y‹m�3˜¶a,&ã—Å*±Aš[‚�¶i}ñ½þc@ùi“‹¯��¾, 3üè‘Ú—Ê£›£š ÄKÌ_á®5K@ŽˆüùÒôëíöûŠì� ËA�F˜ç%(wœæìŠŸLª)"*¿‡dañƒ �?=(/·íIç\$»œ`!¥Àßàð¿\§øú|/ëUÒ/˜€°ü¨=çøõ8Òð„0~”‘%×ÕPóYwÆV޶À’sQiÍéþ2Ïc½‹ÇËñàwˆÜÍ7ÔÇ“2{ÞËâǘÂf†�›öΰmY›�‡¤G—r!b~4ãǶl<´Ñ=Šç¹)Ñ’âßû6mU/‹t5ÖL:š”VI)·‚Ÿ«�…&� ô(QãnôÎn8Ú/…¦mt.Ëà±�7ò·y6Þhkàóé†PÌ�ѵ¸�† TÊÀ_pÆ0îú?ýÀ¾­éÎöïrùªž!rú8&™×|NcÒ³ä£ÌŒC9j�np’I�ÀDƬÁI´b2„r!6Ôrž½¶àÐ' .¶æ±·èÝJ>XŸ¥(1[>þÔo©~<ùÜ­þÛ×oïbAÃÿ÷N×Ñ· … Âü—«Ì–îλ9ü­úÆ·¯y""F�±ø||ŸO~ð¦µ?»bïK$ èœË2žë=Ÿ �nERÜcï·®©n*׬ºæß¬;ºit‹µÔp‘âÊÍ�5)÷¸buú cʾ¡ŒÆiûµN„f|^Œ2u݇¦'Lp!Zª£·`ì ç‡T“FSSOLãÄtÛ«& ‡OuõïUëÕ}`§Êq‡Äl«‚ò§g±I¼¯A5©ÕI*›øYK#Ñ)[÷˜i¶<5‹$u:fÊä“��yœ~i";:®õnã¾`øÈ6Nw§&êG XZV×¾Z�Ñ1î:¦à¦y¦0"ÎÅ`2'öÑoWXß�3 #àD–›€/Ré°¦0Iž‰K8qyþ⢒Œº²‰Ü‡xX®å Äp¼#&Þ6}¡•µÉ¥AI$nïX¡Cn­�ÂPF*„0¦ë¡ÞÓð,j ƒ]ÜIôÿ‰…)JÙ~» AL¤¨]²/–7v:FôñK1dßÚï =eí/·]`bk4MÛ�n·!ñÍJ¤¤�…!gÐ�ÎÅ4û«r®,�.ú|Ø9Ž·äu¤ìž[s)6ÝÝ8Ñ„ë÷›Ù(Ü_¢BÓ¿Ôë4†Ò1‡ã %Ô¦¡_R®Ô¢&drqì“`Jþî™<€±#�œVæ�D_Š0§T0\ÀL9D¼/ìg÷-zŠO›ÖN”°Ã»®¹wpð9ª¬Á‰>HŒï�Š¥c¤ôKA_vÞÌ›OØ¿jÌ+âšÛFV }I8¿o“Ë'K˜ !EG'…(Íç];�’‰ë1L”H0¥EÇox«<ùl ‘j{œ×«F<­Q,6­·ߦº >4·\J/›9ÀM1ÅC%…ù æìžše;§xðW"§EêT‡î"ø{jqÑ©mp9zvaUÍ÷ˆ¡ò‚¬¢eOä‰.Wë:‘�e³KPßÄ'Ønìñ gm­]ã›:;õЭôýmðt(?q£5;áø"¡Iææ;CEO0{ÏÒÜn?Ùt¶¿¹›<­´èæ6(g�½÷fhØ~¼j00‹x ÷,ï‘ñ5)Øë•ßRkÊ7àm»�ú`ÿÛ•\¥9R"æ\Y—�ZZŽh8ÒQxf 6S4´4ã#ß�Éå×ò2Ð:=<­)jy’�eÆ"ÏW åõèwQA†ÌÕƒü¨LŠSDCï9)ª÷‘#?9àÇBë�@ÛDÑû*ùÅÔ§eÉTJ™šÂj¯·˜–ä¾»­ú¼¥›:,h=Ú€pÎbi_kÞA…¢$N$DÍÙW}K¨(½•=U—°ghi…õnbxm¬Sð×R±#�0-‰2"L³lÄîÁ`"a &+vUÏ×¼Q£&›‰jŽ3QÖLŽOÆ&${hIä<…ž`#ìºÖd‚@­÷åÑFm‚ïeé©p§8”½Â¨M3¼€hC—n87�ÃÇ0íäÜïPÒ×%T„kB,±faf+²8rïPÑû|â_Ú ¦´CÁƒ€»z—à7(JŸ—èÑYº˜É7åTjÉÀBwCÅÑGRÅj0ô¨ æÙ޼v´Ü2,”!{©qÞ&›<¯õËÍŽZù¤X^Úi\{(-²:�™èDîÃŽT¤9ÄQ$”º�¶[vþØÒÏ12ÈÃÐ5I)_&aŽà¡R(’�¤øt&£xŸŠäÐ,¡ŠLæóCÅx¼1 z1Qu�v»Õ'˜¢3ðh‡é_K}Šz¶QM»È¬Cº°‰âI{ÃÃäæ%Ÿ‰÷ˆ�t{ßՔɥ G¡SO#‘ƒ Œ•TÁ"UûÛˆ;´µph¨Ú;|¾�X. ¯²øô{¢°" 畽Ͱ§á°¶Ní#[xãk;Ê”ÍÐÞ•ëz-HŸÔ(!=¸%J?©ñ�sH¶p�ÜÐ@GÂ&ž·MX¿d^Zd@Ü‘ y<EÏEÅF¦ÑÅs |¨�CKÇΠ*lˆÁ�_Hã;hM ²òŒ“«ø:¶ÏÝNS�Å,vz0Rkm¢ÉƒöJBo/C{öÌp)Y3@(»¼ÂAºÇämÂ;ðí÷=ÒÜY`½`4²ì†Dlá \´Ìºä霎ܦþLm-ƒ§öŠiA-ð?è–?Ï6y<é)0W²K—­ NµgØz„´s±¢…ØÇ¡e·Â+jZ®óQ%LbhÚ[ó¨ße˜¥‘{ìññ€ä�ï½£C´©n#à]YŸ]£cÚìÇÏù²ák˜N nŠ—ëôx?RAÔ8ánw9°‹=ga³ôb¦‘´D}�æTb­ùú‰×2­Dd�#Àæ+í0PÀ¼Â�K<ús­¶7%*mT¹ ¹£Ç¨¶ ’Àj\‡N.Ðf«|1_À�†Ì6‘ToÓAÌ`#LÔìÌå2™&R³Q‘£ÜµeGÆ)ÃÝÔ.¥¬ 3¾“ÍüŠá=©Ð¦½†)C³Xõ8[BÀ|’MYT0‚HùTF§Œ�Lj¨›†¢l6ÏH]S2È¿Tõ;¢ß^ ŽaöC:×ð™¯úRÎ9ž ™;mu•(N .¼ÂõÂf‰‰êw X¤Nfï¶4ú®{œLbÍÐÇ6\Ö9å¹À\V�ú7�B½ÍÒG¡Ýc$*‰’#.§¸¼fûí’YÆ÷PÌò/ŒåëˆUI*­Œ”Ö«ÎÌy+ZÝc»îAWÊæf¡ÖúÎ?îæ_ÄŠ×O€Á o† ´nâÎ}¶éÔš¢­Ó,2?ßî¯Ù]ê`]€Ä1áS°æØDK™sø(Iõà›÷¢´ÐÃM4†jÂä`‚„¥¬�¤˜À^t$¼ã󿤌›Íò½Øw­£Ã¹¬±’�ÁšþÉ))¢AcølØ«ñl}üµ™·…SžƒÇèŸÒ lDkýµEkéÙdƒà³;áDÚøûB´·¯Á$d8Ãó=3ù›—úÿ™Â…ó¿§p%Õ'{>ân7|zæ~R×sXaGóË{2†²ßv]IY)®Ù))Sþ€j7Ä‚—.¦«A©JRÕýt°Ñ–zÆ·ðeš(§Óà`Õå³³ûtªöîˆR9¬Ô¯Fåd–¬ hÚˆÙÌl”¢áU.5]ŸL/n>–té”ê�[DœÜhèt“3š]MëìX·—ÒV•¡C¬Í;¡´«GÍÊßékWÄ–gøÅã©Ùn:g)¯‚_ÈÔX|ùÁ€� �”¡ÂY«P*UZµMzEѪ:Tƒ÷Í­ôg‚õ&‘-‰uî‰å§s³@½á´‘’ÔˆÀÜbÅG•’²ZAM©¤§�àc>ÜlcFè´øÖî�Jü”`2il6´Ò $ Ôü›Xœ³K³c~!þµf½˜§H€žûôBv'™‚qTQFÖÜ îÀ¾ðq~YÛUNa篅‰ÎªìÇ`ŽßµÈšÕï¯üÒbwKZÆŸ˜v¾œ›�…š¿×ª$&ä´Iò!†¿'ÙÄ7ãÂ!GîâpÇ’WÓ£�¯åJ¿¹ @#~úñû Áíï÷Yq}f?Úã1<À™J\™ föÆcÅ]y²é‹/¯¼YKVöNoÍù�w.ÔÌ»Dß:�3éTó¥U·ÜQºC°ð«i•G ú­ÌHˆ§YÕØòm�ˆ » £?ý ßmn‚ÐþÆÑâ1õ–\»¥B÷ƒfœ9Å G‚ßã3^†p€°„Õ¹ä…Lñ–™Q,w€E®Å )N'¯a'ç‹›ãzÅžo¿á$wÞMÆ0Uvüç Ì”9í‡4¥#ƒOÜ“N@3¼®�ùhî´Ÿðß,�•¢ð“N\'äAQ`ü_BF­â�?$Ãj3]n(rT¥Ò©’1`má@¡Û©øMb\cÊ¡øÀÖ¯íÒd×0Pìœ("Ð7=V„÷*WuÁŽÀ’vÅ­ì-£·šÀ-÷öß3Ìw©ÄäH3 $úçÝOdL0âME#\æ×"ÄT¢ñ�‰Ž vÏ.W±Mª$ÃÅô  þÆqÿrN„p¿ ÷vdï „ ɲ�•§{P�òùró=��R“DXšf>eÛ.“ÞÖw¤[Λã\^Ù7Þ’fg¼“vf†“:;c—¹˜óߟ`X&kPB�EÃùPÍÁùÃÐjnfݰM"‚Z/í:š é{Ìq(�¹ßSØqÀJœÿf·Ìó¶ÛRPØŸør\¯~ˆ=Rô™z7´F1@ó0ô!‡ÄÎt!SÐQ4XaÖzóàhìQnžŸÿtÑXõs>6°ª_3Bjôõ†ÿ†ä T²G¬\[Ò—sZq8O±ñ˜åŽîrLèØÓÏT' ò³oÓ÷i¯ƒ7�Ѽ›)AÆ5ÞÜå¶(ÀɤLEËg^‡¤¿u1%ø�¶7åç£Û“¯È%*Á?õ‡sޝ¨4Û‚ýìÄ×@Ê©dd�ma^óàuÿµÉÕŸÅz]“eƒ”y/€Æ®¬‚S¹HKÏÜ�T!T!´oÄ�$p|Ð ?¿›Zq°‘DR‹U)Á•…±' gžvžÅŸNô/iÍx.©t^žÏù ô„hl¬Ø¾'?*ÃÄL7³Xÿµñò¼¾òìwwÐ5|�¶U$¥RÒzþ‰Ÿ¢Mtq"Wí‘.Òƒ¢[ý Ú·cTa+¬X|s¹÷ï k�gq¨èO±b“7[:Ù¢’¦¡?ßz?÷ua>—8$lùŒ^kA’3æ�ZT匸âÎ{úÜAI6ÛZ}"ÒÁ…]£¿ÚVïû`q#qv9ªœüs2õ¤¡zzBõ¨zšÅ“M½´Dá‘£³(’Û30œ€UNR‡ì%‰ýHÒÑ.|RQ¡s²ù½\´Î*Ÿ ‹šZiB ^ZZ,¹=ȹ�¹öžXcoL¾ÇŸc—IO{øW*Ê·&ÞšVŸé|V~eCusi=ÒØ0ˆ‚ {AKÖ¶,¦\�*ì! ôfªß}ŒeÃs̈@‰lm{™®ôáûÿ¬ÄˆÑÊç\®¼mËoGþD}Œû>:)vÆ'o¡íVRËyG<¢•\y¹TjÓïö‡ª%Œ›zf^}&l™Öï©”j[S«§/HŽ`+Âq hp\�ªtK1ÂÁ&^ƒ}vEú`´µ¿}ïÂ9ç7Ãü�„þØj/M qýô‚%MEDX8àE�7Îòõí]H½t›µ«xô ]}dÜ>(›:4Àqäš<‚¬ÉüàêéÌýŒqn"¹ÆbÖøµd ñѳádÀ3€Z·@=ä“$BJ"3"ç~‡£/{Å`ž8 lÝÂå�áʺ¤%ÌjÀ{ZõÊ¿ \5ýúŒŒû`²'à ÿUPe>áaäAq™G­¯>é·ó~H’Y]üMPµQc¿x»=Æ#X‚õî˜É˜‹„Më�!Jão�‹"-,^D�À׌óÍ�Œ@Cø­ìÿ‰?4<Àg QßvTûõk U­ûô›Ç—˜äžlÀ7‘п¥%MÒÎþ[! ϺÝŸŒ » §þ�¦Ž1΀Ãlž¹'hœ#Ý=yVÎ*Ô *µÁÍgææûÖûãr¶g±€( èÂS` ÷ß…•|ÉP{\znÀ|¯’‡ñXíÎn[þö&$à³ ùX�é?ä&‰ìhAÃ@¹õéÐÁ�kvÒ‚p‹Š>Ž«õ¾cˆ¢£>%”äb© 2þÄëVù�­b/+>ý!Núwúïú¤ˆ==Ç+@I$\ÀØ›¼ûÏVQYÿ&t;ü0}¥«ûäï@mžuÙ‰ëÙÙÒހܢ)£àå`U+Q¸�ê+Ñtñ ™SàÀ(„0jÎ�¯¡EºDÙÖìæÂuBP>ÌŽ‹çÿ[¨†¸HÈ?§„3Z�û cÿ:@¸ÿ ±„Çi«/ôCI‰ü|ô£j—cè¢áqìM²d ÿu DÃTIOòËlGÝëLËé[%õ]“"bÿ‹Z/(ÿ.xUyvä*ôûušÅøAºT³R¦"è4ãòÆâd1ïsnÜé4�ëD kD“«±f˜(žMž.æ…ÆÄÒPÈ`5s!º–c$°¼Ãþ!~Àº^õO£þµþYSÁw¢ÝÎå(a#¥(Ú.ÖšÅ8�CÒ/=«‡Å…}„õN³‹@âLÕg9õÆþVŸk< zq@ƒgÜ‹ÿãñS ÅØœ^áæEÚ›F…®ìÚÊë«ö?Cµ‘€ðtDû:T>€Ðÿm,1ï¢&öÉu h �{Vc}ëÖxx‹rf‹ì4ÑE~U0":9ý'£O )*‰º$ BJ ™ý­mƒ#�cG.&Ë3×yJ3Ðí˜ê;æ˜7m6sÞ©Í û�°œsó Oh S�ͪן…QàÖç_>L÷€«öõ|ñ5Q%­/�°‰ÍIã)|@è* B /p=„‹æêåhfjfNdeïJäâbåmfàÊ“’ &ÿ�Ï IYsOA‡ ÌSÀÚÙýx�_ž^Ö+á:Uéd}H‘~ù’ÔLjTÛþÚü‡ ¤_¤¦ï�fÈ)ˆBhÑǯëÖÓ§¯Ã e”W¼l6:y¶˜7 ‹B×°ž?'œ•ÿá=;{$Sʇ%Õ<”t—TÚ˜7>1óµ²ÊÏ·'ïRÑ••gyßüN=4â’G‚Uå¶“û“ÇL+�!ÍIÝö½ßÎÝ¿PÂf;Mÿ„ÛîßU ë¯ïËí/ÍU�,’4o|xj¤± \ú-Û;˜­«Kd’û&pu‘F“fVFÿ‚®}ˆQ¾‡¹:›˜ìcÐIJ„®¶¢ÈN¡tòº>88 *µHïÇ,KG,¥8QƒÒ†MÛÀØ[Ï+Ѐ 1µ2éœ>pi_  %ô&(âo°JÎ~ñjaḤ·�ùh>“sEí7�ø`òÇúà´áé<Ääÿ”®ž0¾aˆ.}ü -2ÍN©ªg^G ¡ ×€fµ Áà�Ü£Î3Nú«¹ý‰˜�)8ðÇ -Ú?!“ëÖ{u B¹À,-ïnÚ˜ÖNÑ`ÑÏ]ýZ:ü{óø¢\‰é3`¢…öšIxÿÑ÷åö‰ã¸õ Þ�÷|š�Sü ¹å1Õú~�‡è¼ööÇÿCiUk †Ö™6½’g•²{‹šnbôòÀK‰”â Æ€¶Ô!ËSj{·®äŽªü­Vš^Ê„g¼l«êxý&PòHwa‡“1×�ê5I'‡¶!?�ôB¯ýO ÃðÞø¹â2ac'LÀ­§}þ—Õ!Ý(s(•äè˜Ô8#$ ÷ o‹�l÷ÝÉÌjAƒýŠîãÎ�ieñj?¨0x96“dA¹ÀÌÜ1²?7ŽyœOý3"À�•ºÔîˆáMÍñ0ÊGœJÍâ`2ˬ ½€€9‹ûçé«huWÁRQ'ŸŸ¹4#¿‚2ãþ÷¥�ÿ°'ȰàÖÞç yÐy'HWðrí`t'a2l�‚Þÿ½µ‹ð–D~:ê~Qá²·Öi¦Ÿ{W;ŸÔýàÂTÂ� X‡WÛ¹ÿ#§OØOèÔsùG�@p<ÛåÍ6Š®KÝlíÄ!;Æ› í©¹†dË0./Šó’)éã;¹2Ó"ØV=ëÙ\XCûpÕ¶QÁ¨¯�AéâØ÷®ys±† I†à>:ýccʱ£íUæ3llfˆqÍ&~Ùˆ|ƒÚž®ö•þ×òX2Ôè›óƒ â^*H—øŽdƒ–üó"Ùçè½X hÍ;n�w+¨Lx_²]p›%‹�×8áÌ!ÚË “»~�¹ð\·®Ø‚b�`%vœ«þ}Ô{AÙ¢ÿ«šI$ë·Ý-ÔsÉBvPüï™§aw·~~]å:¾«�Gì>öNË«®196öSym–w-ü?žÏî6‚4g 8VÎ;Úè[î}ì»{~®\_]þ ÿíåo)øÂÒí…„¸P¡u+…,ôõçN˜†B³þc¥Só»Ö/Î=ÈAǼ$@˳¢Gž€‘·ž$ós+j?}Þ¯,öšyGŒCÊ\å¯ ?�><&ÄTIÓ§í¦ÕÏ}(�ô­6%É�üðt+©×÷Å%ZÐI”¼ ƒ­E�žŽÞQd�Å¢¯í†Nï½×ÐÈŸå0g+Љh‡ýËoxÄ Ô¤³Yì÷¥Y�g~5x,éã±ÄTâC$”}FæÍpØ2‘©·NÁA©›÷™ÅdÉ}˜¢Ž9}!õ•&î`Ú·#Gjí¥ZÖa%‹¦–¡8öt¸ƒ °*?S2:Úêv¨k DÒð2îܽ� l©ŸŸž½儉pÈD”¡*ÇM>9S hÐÎN)a:ðð=Ï_éJ‰$ö_“·8d7Së¼Úô–ðÞkÂÛ�B‰|OñÔ�]_í�ã}3½Y�w.Ët¶N?M»*Nh§~s{ƒ•Pžç÷…ç>Aˆ@¹ÊT& »µ4 ëçã Wh Õ(.öµ,lNtÚDˆý˜Ó¤õ€Í±RK~]‹a¡^6è OÅ:lÚÐæ#@ ˜uƒ8ÅüÙm|F¸{Dw‰>Ü–O¦Ù<ª’ÐÉgy@Þü̲ýÙú�³-9Ê™uÆ º‰´a¬øšË&œcÌDÐP÷ ýMP¤Á?j¶ôÚ¹¤áÎù§>[Ô¸Ž»Uƒ“¼‡?Ç “®ð_÷Á#ß¼LÄmÿràÒÄïSœÛϪŽ­¾-+ñt,�ùò?©¯ÕCM‰ÔèÃæ‹’`´dw�¼hýþ@c5‚Û™7™sWagP’ßµÿíµ.€0¼Î6çu�Í!¾–õ�\Ù§jö;F€Ûn‚¢‘þ›žæi:º¥FäN{ünèÓà”dëAÜ·4´Xäõ}°@æÿ†WW+¤ò¾Óº•úœÇ]PÒ¡5Ýuÿ…W…µ Áè¾X}q¼÷W=4~pþ5¸� Ø—ûþ­/3äuB¡JóoÖ*ŒŒ}ñ2\,Œ\¬äj=嬌 ­dTìZSdÕ­õšÝ¥õDMXjJRæö4ÕЊ òæ(íšJæúÜÕšÒæJÖú0C÷þû+)L1å»åûû˜j4»h¥åTdiåeeÔ–G.Né{åT–edédÚ´TT‡6Ô�™°Ri¯u¢áÃ_jÒ°ÜY^S/"Ž.2°ÒBub¾2zË«}×ô�Ø”’ExÊsÔÝÑòÚ¥„®Ð(jú€+â Aëk¼sÀ‚±î�3¸@­×±Oæ¤ùòúaÂÔu‚Œ"Qÿ㳄¨É ©Þ/%N™:#�F+RÈ 1‘³Ñ³0²‰õ ¬ÏL2Q)§È`“”P•Þo*Ë�Ë Ëæt\Þš3íßŦ4i†Á�L³ÈL_í¤°–Û YÒ|ÅÒ%Ú ’F[åÙîã{Ž�£0Q2…Â)ÈVÉ…5ýÐŒ°f;®=Âê…úôiÜ�m 1ÇóR þ³Š’£ÐTŽGR+¯Ýh™CFHŽ-QͬšÜ4a÷l‚âåþa‡÷ºíDÇÁdàth4CìëZkä øüå_‚‡p½ áýwÅ }Ð�ʰnâ§vȲ³š7xÕNÿ±&'»+ ð2�X{ˆø‚òÇyë·8´�¤”¬œ£„�¤H%ë÷·é¤é©éGÀà,ÑÈÒM©*Ñ)í0̽¾±z ðPªhÿÞE=¥Pz(#ow§Ž"�‚m†mk‹‚Àñ/âçç†åªò↠ý&¾ª‰gà†ý¼ì!ˆªt׎ŽV¶ö6ϧøž>Æ �@Æb‹Î:rÅ¢ÒÈê†@Éã<ô#¤,0(•<Ÿ‚»ÄŠ ÔüŠHK÷¾�iÁj ’gëÎéæ¶•ya·qþÏÑpäÝÔËxïA¾Ù*~tVð 2¥vG½{m?îçXòýýê+Šû5�‚ÿ~ø^ $zI¼'Ò�À„^X`"â_ìcBR�™R’”–››Q­Uzÿ}7Ûv’ŒÍ‹$§v-ÑKP[Î2—Bƒ “²?xš·=Ç�+ß øÆAݱ JrùÇ,%ÊæÄ§ÝÖáÙå�)pÙŸžÏ™rñþ1-ìR‚sÞSüÓ_‰ „ÖeöÓ1£ü…r�?1¾gŽw°ö·äŸò ú©c—êòW¢�ÿ/‰ÖÍ s€Ä<úÿÉôîQ}¶k*Ç ùyïíwwÇÓziL¨±…¯>‹þ}R·±Q»Dóü°b†”<ÊZ»ô‚Yˆz²üõ!åˆ�e7Õ—Õ‘ �œÈÍÎunÔm™Bú&üwxç"t’á?¢×ÿ»¢×ü€ÿˆ^ÿ#zý�èõ?¢×ÿˆ^ÿ#zý�èõ?¢×ÿˆ^ÿ#zý�èõ?¢×ÿ½¶©�Šüÿ”èµó?¢×ÿˆ^ÿ#zý�èõ?¢×ÿˆ^ÿ#zý�èõÿ+Ñ«+dñÚ)Øç�U£œ7':æ… òJ�ø½_x¾š>’žS@ þ¡ˆ�aî3Þm]&—½,—®eú.(¬?•.ah§pA[ Ïÿ¼6¢]UEGN²8w5ù9CµÙŸ¥ÕÙjüsK‰Rˆ´kÛDë™ËCóEÕW„zt¿Šò@”.vü á™ë3ò˜êÖ‹ÿ®@U£ï(}íÞëÂóÿÑî+ù× ’6pÙQû',WÄ3×.q\Œ -¬ "Bô°½=ŽˆeÆIƾT,”›h>-•¸ùÁ(Y¨ô'°_ŽiG܃p@J'àˆ&buõ"7å „Ò;S��< ², ã’îM'ËqÓ«œõ ­ß�&Íìô„aü;˜l˜ù œÑ8:¬‚a^¤OµSg†�˜³�gù§/µ_ïÚ¬x> Ég\á´«üà‚X?òœ‘£¬6O“+À-Ñd:o Ü£�¯�ÊO>af(ñžnÀë‘»ªyb=ßc w‚ßu'yY%çq�H4øt}}®l 6üÇ)/MÌŒ/P»ä ‡IšóÙÓÕ®ùþK²r*01Ñs¶ó‚þóþÙëÓ•OÄÇÍnE�ÊÜ ™ÊçåøZ|6Úø�_ ü7AÁ¶ÿ ±íɹã½DËh©v´gWrŽm7=�þ!±m- ‚5@½Î''àÛ¿üÂIð\¾iŽ?óøõë kØá�37½ƒ%«E¢�Î[ÈGPÀHNjÖ3Øÿ™æºCLtÛ9ôן~ uëãÏÀžÃR,@@ìAv€Þ.J=š…B“ôþº(µª'.ÞÅ'>I+žíDé‚ça`Øc…øÛ3áÅ“RAà[¡¤ƒYd"HV´'P†‹�‘œÅîÊp%g Ó¢Nv½ß߯…P�€OG$¼´|�&†[QÛÒeió)Sñ§ñ¥ØN Š”ð&(õ¿Ý~ÛJNéîW¶Ž élæÓöIé[M}»ý¾yFì9 BÿûŠê TTâi Ð�këݸæÏשŽ2-Äþ Šýç_à+�T«ñ±p}Œå©°©–ïè‡s×0+Š@ÿãúuHŽî»Œùÿú†µ< îÝX0Û?ßR)A‰<1&;Qûý|{&’ΣÇÚ§–Ô†n��-¨Ã¦@ëírè¦T%Uûç‘3gl§Mðq£×VÝ/u„cµIÀ”"`IЍ²� ,MokâìåèêÐ0 D†…q¶#¢7'rq6a|{¯ÁRdâÜ0{þ2�1ºòözQÀ÷uº _ƒmõ=A@Ö¸A$L<3»•Ò=ØQmRŠÖëø(smŸ)º›ç_2žÏŠ 9„2�}úŸ( ¼=[–|¿ *´†öq©œ&Ó~”´¦d­þ�±Lïž3i!Q:S·|Œ"ŒÓÔpî)¼ —m0Q£"à(äm¦{Ýú„Äã·íÚ’vÁw +`�qEӯ܏¸ ^Sļ×–Éáí7!¾߯•â‹PªS=BÉþÿ»°»öw`,ÆR·�›ðúëtâ_— >`Ú`Š×e'ü�ÛWü¾=¾/zæ”2îÝh;­fyô7vÕHî›[YûàAÚ3Bº » kù�\ß~hþ$,4áHG¡‡!¦è#oœÿŸ†/�ïûo¤•´   ÖÅÅ-ú:”“cAƒp8ôŒ÷pÖ%óvû|%ç\^él®ï,o%],çdVçoooœ¯¾&Šð9ª© 4,VEŽóf5Ì Ûö@]…j3ÎÅL€„�¢‡ZK¾~Gù/A¯à}ñ§³`xü.°Wöm(5Iþ1”Eã+X"­agH¨S`à?¯7ü x6àŒÐ²ͤ}L}"R­Ø“ Ÿ¶€ÑÞo™¿=έ�dp» ;ŽâÅÌL†bÛ û¯Sõ�—Ö+æ<˜„¶O†aàÚhçtoÓƒqVËɧâã& ê¤OºJEŸ¬DìäM`Ø›€£EÒ¾�»f8¢ �ãeÖà‹…,:‚ˆñø³íDšõ‘ˆoÏö§æþúµ­Úe yMù��õØý™ÑµËdºUÓ¶!!¦(((+*J8*()(‰¡~E +z# \³ó,K¹©ø‘ ì%E�‡“  äAÖ~’ìDñÌ-Sïv˜DS§EN¦D~_´ Î#-–tOŒ£�AÒ#X%J…Б›=øÅØ 0¸:¤†N’@ë.ŒR�ÝœýKks ‰2>§±p=ùúÚgãiýi=ïzë¤åõóæÚêjéfnêw€rþO_Ä�Ï��«äw›Y­ÞN8îÆÒ_ ®Q*›9¸äNÕ†OtË’–ÙÊÛ�õ¤°2ßÙbÙobƒ¥û*f÷ Õ;dvÚyú{ý">áËÀÐÒ{”m!g{³¾&æÕxYàcu KÇ~M¦¯B2’õÌÔ³Lz_…43}³„9YKà^·¬US‡9¥W\«S\VWÚEÈ®.%ºd‚@þõ|@ ¡QÙˆu¾6Z´ÚR/²“JØŽú„˜Åö‚P^¿ƒ^ùüò»Ä ¯¼°üZ/çrvûÇfȬ‘#•{¾ïÛLŽ’ÎBWÓü‘Û™%¿†^µ—$à#gfÄ]žC½WAC ˜ ¹.x­ð ñi ‘E]dáY 8„OuÑÁs‚)ItÇ\0QkfU¾™«�ÒIKõ·‚ �¥šb*꤯®Èâ>íü\ŒmÔû%vŽ9Y°0;¾4Þe•Ü¿P4Õêò¬Þp¯]cH*÷Ly¨bJon󨺜 ù9&Ž$G° ”Z¡BûÒAÖ^å:3|¸É,­á&åAß7[d÷l J‚”ªt½rË">sƒ¢½|Yr‹iõÊj‡ØrÁž™[Íç>šG<Ç ,ßj«JKÕúp� wxh¦ìý˜\¶;)e4)·Á÷PñÓ©DKÆóðËö¼ƒõÄ$LaËŒóÉÓddS†(â_·ÇÍw“-x^¾›¯Eú! W)E %\uÃæ³,]1©×‰¸,¿®H®\I›3ì<·5ò£¯˜E Óýeón?ƒEãKó��ÅÁ3 _Ø~ä-ƒ9ùÆßñÂÕ±]Œcµáé|£rkr1È?ÁwPϸþíÔ wGAWãyR"m@È 7FcÍ|G}¦þ7H¸z‹í° ,ì5åeñ$à‹!‡5‰§OíÇ˾åílÞŸº¶5##K„‡‰ÊÊ—¤£[o_G”„DÌC!-z¦%“¿§Žä¹qÓ¸éš85tõ(7Ps›¨ª¾5p`øíçrùt¢3Ã8™O/Ð}¿39yîwùcÊ#�ÐïéÇVÆÀ·9Ê»‰QåX…|�.:~zýT7­ ŸæØë” …Æ»Š“ŽóΖ›>5%‚ëij߶£Ï\Ã9ö©ã¿mG>Œnß_pz/¿ä[xyg5œO*í4 x´PwËFݵ£/ñÁ>¸}·R”¬Ñäko*µQ!…®3¤Üõ‹`×±³Àtâë/iŽä)‘.LìÓ ñ�]Ïî}s{ƒdsCÁø9õ�ždC9í Ý;â¿âZr5¬©ù{1k¤\y´>¿{²YªL~ïJ ¢=;rKÊÞ²oI9éUrÇAȾ� éðÐ_ëð:yìÍÈà9~Ü;[¹p°�NÀ#¦xÿ]ÓÝMzŒFePlš ®~d$‡>.ÓÙ¿ºÏÀEù#œ‘-†¾:$1½¬3 ëRü88frW#Ke„xŸæQ7a/׫]ÉmYèîU×Ü38ñ+º¬ÁšR:HŒ@d¬¨îP0¤3ŵxãk—º°]PÚeý2•±¨©ä+Ã7Œ+<‰� 'SØ$,Vys;8%œk’z�ÔÙÔõ!Zdä2pdOM»aõ´ò¤³)BDþŒÁ¼!A¯š²f±Ø4pÏÔ–õÅ0I9W«éž¹¯/ØŒSÄ‘v÷U,À\ÝX¯¤FÊ¡Ã.ÛåÁæ¨ÛŠÖÖÅVôÊ GZ_Ã-Ï 9|&¥';t ¸:× ç¸u•¦-±á§lü¾äQæ|M:–¼Aª xúQ�#°;‡ ã�c'P�ÌÞà Ÿ.`æ:ļ¢}îìü|'±2�pÑ%h8À½"µŸ�EP#äþx“Ò7 pшŠBÁ‚y7ï?š–?ÈìÀc Ðúô¸]z[ÖI¶¦ØC”1ªu“Ú? æË•[é½]F–„¬0Ÿ¯÷_ˆY(‰VìoßûL ·�N¡qxV=!l<ˆI-=’ 02V)¼öùöÆ]0K+óeÒ¦ xS,—Ʋg´,çáëp|bDq÷b•?p!(wÄ&êçûYñ‹$ö¸ªÅ™ƒIÒTYk¦üßA�2T*¾§0ä‚Ö~Œ –>Ùµ?ÖüsŽÇLGŒ7B�>¼f úiå Ї¾/uŽ`O pdr×Ò¯Ør„½ªIX\ô šRFô‘ë$ÒNj#(à„GÈG ‚ßMj')æÚ¡Á½— Ò;†€ù…1™03Ô¨—nUXÁEF·.ž¢{ˆ¤Ök­Ìø]Cé ð6v%e"}ú^°§*¥r­ÊIˆ¯lÒµ²B£>+:ìTª JìD6åC7È@æ¿/ØtãÿæPÚB>!/ÏuØ;x�7ý“A€¡hé<97õ› ³I4 |” Œ©¾�1oÔ÷�DyõÁjTíá’“gýkÈ`®Û˜TQŒû,®.‚eÒRý¸CÐqÔte­ {¬^*Þ_µ£± ßñÂ�#£ã ç,[3ÑÖX7¼œÞá"Z÷P_Y¬ìž‹ëZµ±Š¨Ø÷¾~�J.å˜x�ñqï»\µ†ò=é-Ž‡Ì©8�’#·ú± ²j›™ûC½+†Fr‹‡OßÛ^Ø�Óä'IòZ W‰ß&ôUéAú_‘+ òDr�Â-§ƒd…M’Ún¿ O)gì»&NHùŒ{¯ ¶p�@V�~¥^ ÍÛý�xt�ܱ¢À¢ú1$ÜE0j¸x‰QG³FÕË<“ }xjNB¹—FŸ„èU:(¾°è:¢<ô±¦d#x ’ÚNP{ŽA;;P:Ĩ\¼rf`j£‰d)B#�  \ó éZS· ná’³Sãï– ¶z”빤›¶Ú~tÚt­†Û�´fv;½}¯9/[Øpe÷qÀã" Šb¸P� ¦[¹(N4¬åw»½YpŸâ=ïª õ]„á¸�­ûˆ¹µýcCœÆ”µb·¦fZë&„�é­ áÜ“ # �»ƒ³¬‡°A}êg~üHJì²g$ÉZÕ€z–¼(’Fñ “rÞ»�«háØÝ­³ÇV›w¤¢ÞZ2U5ÄÚ*Ï…‹8VÉu»Õ˜êÓÞ�à fì$±üZ~d&�‰ÜD7�Ôjzµ™òïFDÌÒ_â(kÔúÏ¥Œ‚µû_`ˆô>íÇ�“ŸÎúF Ù¹ßíA֌ԴQõ{—À%ãS‰õü˜J¥¶O1�¦èX6‹]m9T£r<^“û ºW•v�ìîNËü"^Pl›Y§ÿ�D®  ^áÊBwŠüôíWôjŽM­V¢èÀÍ�¢ÙüTv\’5õIR`ýU>,w}L‰ƒLììpÑ—H9¹IàW¦½Ðé9‹­�lœçÂØ˜—•Ï}“þ¿–å1a2k¾F»Ø,ä®Ò'êÓÅ%ޝˆÛ3ü–{Aü¸¿ ÊÂúÇ™NOæûá²zÝÎo·ø­\rþëùXÑÀå¢"|Ó>TþÛ�; ê¸|…í^qØŽvÝДæló†³p8–Ú~I)¸CÙf½ç~-ÑN�ÒF²?9¡šÿO˜ó‡l$­G»4Ä1½â $i^±µásŠÐ�®,O£T T¾"º,CÏQQ)žÙðö¡0•ž‚åÓ¼B­¯�I^Á{ýZP‹Ò×Ì12ÊßX¤i+öÊJË©ÁÁ½Ò!CÁ�¨i^Y^Uï�e¹ÊeÃܲ:€Jj´.¡×òÍô AÛ1ELà´€Ãm‚rˆþÛAC–7[8YÝ|ù:Å Uæ`gÝÕÛyÈÖ0¢ð1dìëŸ[Òþ‰J4Ie ß쇚ûÜà êßÁŸY“:ƒÑb{S„r®›Žo𢯋*mi…”ˆS”�©h× ßA­Gò†g¬OgK)Oð¯‰Dc -ù°qLŠ�L.Eý"O©™¹×q›‚ FÒ'È<± ¼Âz{£»QÍR„®d{W£lÑôä�Gu$òž¿Ke;°ŒÚPéàñ�®—UÖ¸Ô @]èóÿƒŸØaA³ÍÉ5#ưéÍ�•‰Á ßKÛÓä &ab qƒœO„C<è-W°«U|\ª›„¶Ëâ·€†¯mŸËš­—¤N?Ñ^ž[VÛ‘ÌøØõŠ&lÚð^´ïûë¡ÌEs¶“Å–"¤´ èvû *– 3K9Ó"¿{�z¸âãS™ßø%_IÚ~io¬Ï;ûÖg½nÊcͲ‡ÑÊPQ‚®±$™ÐZA௮KOSaÿƒá«èþ¦³ï ¥Èæ{PP·ÿFçšœëÏÖƒ-E±Ú9º{žaÌs “Û)0Ä÷߇MÐ6Ù¸'7ã9ö®üì¿=Óï>‚]jRÏÀ‹_g]Pÿ² ¾ ^[R°ÿtÝö}ê°®áx¨DJðÓU6ýß)Ïõ=ó{fzg3#S[3@ ÓI T,\ýom¾ù l}úDËµä”  ‹•IÌ1°ØÞ3WÇ#ÅéHnæÎ3O7Dd;LWO�žÃ) �†Û¿ÝFùT רFŠ+…‘3�-1Yòk:lŠú~ ¥YçjF,N"JÄN–hEÄÂÌìæq|RsÜñ_p)ádüo?÷Ÿ£Ÿc7ø½Y®w66ìù[o¶| ÖÚ^öž<wgtîÎ×¾<9ÝŠ»<›»;ä7üîÙ‰ø¶w*¥¿¿§“°ïïýãÇÖÎþçïDzŒŸì<²÷::óõºøN|}â¯Ê�ö<šø>üúɹò²’@ÐÒ]!•¯¿O·Ò�Á5ZËç×ÀÃ<�½kG]¢ƒ}Ðüî¤&Q£Ì·æ¢JA¼BÔO¿Î_!*¡™ü·_¯U¯�Ïâ ¢’ˆÓ}zv_;º!›PFxƒ¨¦‰f¸þq÷¸\uÙ so4]ôƒÚ:�z}òÛŒz%b#Ëp2xk%ú•»EÝœ’<Þ'®Øõux'D£ž–q¼/vߤ{Ï´ïâç°æ®¼1tŽM )›YÝ‚¡?ëM»út ϱž”õé#'�d@Ð3§]FÔI �×Ä1ÄiÿQ ]&G³¥Î˜FÞPÏZÊ'îa¾Û…µ¢Ž ¹DMý¡-qÄl\øÊ6:ý¡HaDŽ˜‚êÐ÷#g>Ýõ˜gÛ‹@,öŒqK{I3e¬èÑõíI!¡è”Ïm*ŒC+ágÀFÉc�D,Ó,§¨”BàQ^[>* n ºY�O}?%SsDõ(†NÛ£ÍW4ü—!ƒaËzû;8Á´XÚ�)»àbyswëÄÉ8Ê÷¶pÛ8Ž­¸í*øª4] 0ªºn·¬ûw!kÓ~øï»‡p°/§eÇî�Ðmا\ô⃞¨sNL“j7K“”“/<¿/ï•õsºœ÷æÒ.H;Á!`Gã奴Èh¸)´º“Õ´±TÀgH(™Ü˜K¤&`·¤‡�¬‰Bͨ¶ÄZ“ÚzÂ,~V� ï!«¼ûõé›óD§nó'"@G§¦Ž‘‰ ÿÀ´”jÚW$J3s -Ó¯ë1JìSö…ú4YózPy\CiEÍ%_é^ñé©Ïθ¿)|Öl‚¹‚Í5IrKÆLæòu"R8Db‹Ží°¶ZyòÙ"ÿÞ`ÎW~/š ²&­Ø´Ý–ŒæùÌ0Á8G«Ù¯�wâØ”SD’v÷Ô¨—¸z‘^n‰S:õ°†Æ±‘ˆ)ýbhlM‹‘šF=ß £¹�0Ä4㌇êäÝ]áКÒ5]Ý6»º%'ž`¥eS«ØÅWÛîù² ‰ n¯³_6_ 0ìwoëZΨ$�#%I“5S§D$­-ð x|îv¯ (¾xá OÒ†ù™»B±Eë¾táy| sî6Ý?Í_qàŸ›ÑùTa7Ù`ßéû{ g­{6cW‹Õå‹â¯bJâ»À"QJ‰‘|êMêc²Ó”¦ mâZȈD¹Ä=á"ð¥÷È�Jëœ÷Šàã? #„ÜÕ…r‡ ETs*‘=¹Æ9Š6KÂmíNtÝêÉó#³ïó8ŠÀËïÓ²\\ŠëH|À_Áj¤Ê'©W°Úùó½A¾�=¿„9Ï´8¢s0Mšï,c�¿´ “䔡ïWçDÖ DÖìüÔZ½gÐ}ƒ˜‘¦01Íœ:"m{G8Ýá;´@§â¥RÌ–ñivò®ü·-´S9ÜÚº3Áæ2ÄüæbÏ8štƒC6A –±°rìH®>º!N"œºV¦‹(È ïÁ ç¨Yjá§kyJèÀŠ%3<ÉE­¸cÚh|²'�}-†‹JN7”1†¡Š/?£šXeæÑDꈽ”�…¦a£\õÀbêàò 8‘ ý°AŽ€öÕ¬J¨¼”òBæIr5›¼AB¬wUfˆòÑNríca‘ÅÐzdŠàY åX $¬p’͆è4Ÿ¾»ð9rôòî!¤·cŽ@ímï u˶LCU1”W¿áÐevÒðÒ£Ç!ýŠM¡a«ô­æÐ��¶o¿hì…Æ‡‚ØÂºo°wRô eøù}W�€Öîlh‰N<<�k�Jw>¤Á ¯û�þN§¨CÜ�4�Š“žV¬M³½ÊÛæ—…C;<”ÕÑe”uŸpìÝ}�=À½HÛöR—>K;*Èh9ó†V­"ÿB«"É Û ¬Ý¯hõàZÕ÷Ö l¡¶‚¢žø>ʤ†¿û›xj’Ò£¡ ²J <´˜o¶·v‡Ë½])Ь˜ø}x ^L~ŒÖ† ÿJ²(®¦î(³4QJOb"æ�ŠwP#3�^-+(MB¿_´eI`t¾–à«k¸j*N(@~ù¢uVÙY,®�ãØV™{LHf*È|n¦ç\´£œlØqEsûë¥"æPÁ€å:NòIŸ"¶¦DÐÄHË´'­N1‚L²ë4ÕethI�П† ((ÌG�ˆ�f­bs�ïU$ Gkê“n¢JêÈU®#ŸÞ}WfN#Ø^ãfIÉ‹X A½ô̦PîÈŠ;«/ÐÏI¦|�#?³k�QY^ê•.ûŠÀhù�¦®®jžÐH§Öó/øS~÷S-ßT5BÄôR™þç¨+™w+Þ3R’u,Vªš1’ÃÝ·OZ1X œ3+ÚÒnS('Pê ±Ï"°‚M•’ö|TX‡yï´vaN’÷ÁÞ¹`f±�­!•YËÍ5Ò¸bEÆN#8ÚlGÙ¼«õQÌ,ÄØêIjÄ<ÇÃð°+æé¶ÉŸ¦t0ÀÖ©W} 6…Š~÷ž€†\:�º3ʉm•–m� ÄMæA³gí1á0*�JsN†ûEÖÀ¡*¤s΢ïŇK*Ÿ¤é3éÄÁp�|öó–8' L£“,ÞŽ/¥E9±lʼ‘”òR€À_˜Zà=Þ4G6�+/?lf: §üpRˆóW {9/ªZèÛ²ØØ¨›� úÔÒéo.ëQzÀ—f0K‘š^·'¯ÄNn´å¼ ™ˆÔ[n‚’‰ÿL -â®Kï­]±A2——®ïγ=úø†žZ‘Þƒ¥ãÿíñ‘ÿVœÑzi§o”$²&nÌü,Ö‡Ôhü[RÒ^‹ó_šÑŠôðˆ·h}ïp9Å ½â12 I¦UPÄ¿\´.û±m¨qà‚'�1ÇoV­5mb&"LOæ´ñ²tçùÄ—)imD±†ö,Ð%):H½‹g#ßö°÷®žcàBð”Ÿï="‘I!t f†aV³‘�°yå ÷ux–€�òD'‡ào@ÅuÂ&}>ˆåc�㜢XYè��ì×.y†Ù¾Iß‚õ„z´�Ð.—Ë,ÔT¶îzÃÐÊ÷)°™_(~€ËbИ‚x+0qö¤eWN ¹oO‡ç§§íwy]ï=õ´ÜÉ�8b"%Ó"eMCÿr‹Ü´ÃHï5´Ö¢�çL�ª0W+¬ô¥»)jVÕgK~dFÁÅ#.¤å]¤û�‹à°�À]´Užm•Ö[&=åi‹¢Yx=‚Ã)Ÿ)êIÕÖ{@e¤.áY·\èP ÎE0�„ M�N Z„¦®êº;ÊÊcU;c+†ms#Ø·rú1b©V)¨��Ý¡¾ÏO¢¬“º›c>NætL)‚�¦2'›EµÜ§†G„Ÿ×àB Û�½&¹¿^Ÿ5ëù ׎¶¹»<ÍÑl3¶‚Ûl¿Q@GÔݨ¥ ˆ'»e�™ýIïýF ”•PjYÍROš~;Â8ìO„¢JÖ²­ZrkVþû‘QŠV³R€…í@…™Ë³úá©À:bi†�©è›ï®¡H™êBc’/7½/‘' zÎ Ç«é§yйÃeÓ8æSq§aÉ]V(Z0Q*pã¼û†ŸLÊ,EÅfSù(e+­H ü°óòQK=˜[ñdË ×v>/8àÖ{ƶãbþæ¹›êîUÎ%‡,jÜ0õ’.º.”‚ûn�&Ž�O}òÒYiÞ%^DA Ioß­;ðΟž!ÛÃÛ�~Ròšh‡œ¬Ö5ZÅ‚ãb«,#ã뢬‹‚OˆS!³ðÝœ�8þÓ²6&˜Ÿ±ßæö‚Æž¼Ûoî”A+NNÖŸÅx±ž:.R¥ÜÈßÝÐ4ääíª�$ä}vðO¡=€EõÌÍ){5ëCJçýø’IñäbiW³ÍÙǺ²bé‚pgÆÎþÇ6na¸H˜æ�0ÞJ¬ª çAJë/I~ö¼n®c|ð§`ƒÊ„6�³]bÆ n枪Þ+‚VùãË]þ�DÔ®ÉóÝ;‚OeR’*¼‘†1šË$aÍ•‡vY§�¦c¡YKážK(‘Î�áœQ~ žß klœº7H•N�&�ù\<­ßuÝù <}IòÓ[uˆàŽo²–°ir+Îr e£¿ë«qÓ�¶çñæûÝàÙÖXXïÐôA¢Ýç‹Ê;Ž6Ka(l¨òú®´o;„OpKs!˜2{3ƒ÷×ÏS³3ž{Ó/g¦Ÿ ~ëRä¥.w1ê&~ªù"Ë·ýEÓ¯ ÌÌððk$´|ˆcÆGᛞͯ…b{™¨d!°~gZ¡huQ®Dtï±,‹ðp ÆF¤‹1ÕÂ�M>ŽGF¶~nJ’G²‚Ò ÍúÈÁž”±è9VÛji^SWkÙ–W�í¥´Y™¦ˆ{l^ˆ~×ÏáþEÏÇÇÏŸW‰§Â Ԁ؉mÉþUg'îJ5hF˜ÌSçýÈZí%»J{T®“W–¯É ”9hD­ÙÁ9ž—¦ d9îl•*UVô±c(ûë-gÅ#˜mF‡|¸÷ÑÏ)¨Ú˜ÖÜ¿§t“ˆ�~¾ñ^'vÊO�‡rœ µ^ ê¥oö š�â™=»hÈŸ^@NÊ:ñRÌž:J+êfWlx~ 8kן/Òo; xN@X»>RA¶Nä4€sJ^öýäpº\ ?˜eBPØ™ÇY4rRÂéà»,*ÕÈ®�.ºŠ*i±Ó•¡ÞGÉê=a³¤02ú1=y·±bÚ· MKü‹ã¬ÉŒgdAF¹©Áy“|'¾ˆyÌ\žÚå/k#¸«¹×u7s�ýì`G Ÿåþ¡†5…y+ñüÚLئ5´N58¹GV÷úµ{¸(ÓQpûø4ŽWߘXgA±~7‘£Å·ï ¼w†¸ÚüÂN²ë;KžàhŠÄiÍãb{n³rƒZTÞ×J`Ê®h +ú71ò϶D«ðÿèË»;–W¥ oþã ÆÉ»Œ?[¤æ&¨Pô�÷K# ÐÙš-Eø€60ÿa⡊õÏ1^Üê(îu?ÍùÆž`×Ç"žô>¯YE£÷mì‘]DAŒ­�\Îÿ¨g¼K xC‡H¾ *"üGÓ©×å°ÊMùi-2òëºþêìIvÜ:ÖXá^›öýoý\ù z1Ñ"ó‘ªíîR[ÙתB¸øÓo´8d™sàë¶þuìFÝ�3`ê›~ùY>E€u�!/Šø)©¥mc‰~ ²\ÿµ#Œ^úν¿âpÇ59�T˜cÐ\á(Œ¬$ jGäô³�X îmn»J2(~—Ë9í-�¡#e_ ÌCeø¿¸û˨*×µaž“I·t(¡tw’‚tww‡tw—t—Ò-Ý!Ò--Ý - RïœèÞk­½Ÿûþžñ�÷×ûË1äšg]çq´¸akR’[ˆv�€¾*ÿܹÿb¬ˆ£Túy‡Üj×h[“¬æ ¼óÝz¤•;Ø `lRµïBÝ)LQç’9@7î€üðÿ³\eùnx�Ñíñ.}.𺾖ؤa*¶Á+Láéj™æ$£¸/–õ9ž½mNY< �ïy6Á—MXÈAÀ«‹Ù]‹â$b²æë9d›KÎ à¦d5¯ÆK™aÝW€ljZiÞ5÷8×Ma+ à ì]¾ËŽJvbá�î6¢ÌÒd‹·î€üéþy¤×ýòè•2»4ùT]ó*ÜD È«ÿíH]¡xº{о�ö‚dM"pû›z*+Î{MsÔ©"ˆÍ0ˆñÙU4²8úŠYÝ(Ý9ñs ÇоÕn5UûPN?@NáVGú¸†0iclæ /ö_ñ5±ÄºoÄ�Ö©ª×J3 šÄE¯Í¡Å¾ã9è3‡«;@MeëwPeÔ£”á7äœÃ¬'Jý ëЩ鯌s@ÜU×â§lAw¼ùžΩTßÃ?>°æ|2ù^ `Â9ÙÕ$r`ê�Ö])_Ÿ¢¢yú–&^ˆë*¢';À‹Q7S•±! Scx]E,F¦nòf�¬HLÇö(kV`UˆÓŸ^.Z9ó£!&¦ÇM8Cà`ŽçÎæ+¬øžÆ˜e­Ù]QºP Kî»1”pÿad 9Ç^L5,«ü¬G¢\'‹Ÿ)�Ø¢›Õæîzûzåî§Li`f/ý�ÑÄØrìÛt¾›Ó£íCžC2ŸÛ÷�û鸌—šK »�¨>7'Û%óMÈBä’®—rxyå»âd75Kƒió‡Uä§ÑãæŠbG{Y4ñ¨¶b#ãzÞ ßÀ³Ãu¢j� Õa^Òæ—P×}ìɶF¥yî´f¥É£Ü•ÚÿI{Ýa9¡:É_Åäh2~þ+âŽÖÇ þøGyLÜÑ|õÕ^SS>á}œ-6DÑ‚Ÿ,þƒñµ5Ï{¼©ÍèbI¸䢷P±†a`p‹“K[¨eN NL#F+Q‘K>>W™bO6Â�E¹´~ÛSNDÁ§˜^58€’`›_ ž›3|çr!)VÐ@5ý”^ªÒDy¢ò…).@Ù‚ê“õ��åmµV*¡‘òÓ^£¹Ù´÷ÎUÈL˃zÓ†ƒ …˜+NföºT޾|&-_™$¼šuôW�¥|Œb£¿§Eïwª1)�Q–äHü)Jx}�h¾%]/Ôç¤Öà á ö� ÎÂdgiÔfΠ½,Kšñª(~¦— }:JóŽàž‰<"—ƒQŸÅ•}É'F¹Qƒ£Bu>¬zŒ÷³C@ܳê� g2+_‰¿Õ/òÛwRs‰5Ér{÷/{Ij×ê”ÖO¥ôðÓ�jØKŽS_¢^ç8>›ò²\“å{ªõøÑ„ä+ (rœÿòm�o€ Rëq°õM˜^Ú"•þÕ¨=‡êSôÌš*¿=Óš],:ˆïI4ÚY¯ü"u÷å¾Ñ´}ÊžSCYâ®òßѳøQÒÑZ.ÇeÜ‚�Éä–ªó¨»Å™Ü/ò&Ôkˆf ¾Y[jðJ0�Û—Ïjª6ñ³ÿ·x/{»«.\% P4"ÙƒÄóð®L†“¦Òª6ÐÜdû©¿Ø�\u‚4:aòAz…4âÜ”S­J%sþº-mî2®$­œù»UiÇÂû+”} S^Î ôû‰8Nª/ùIqpÏÛåeâ'ôQ5ÂQ¥ÔeáZçÓȺP ¢A³–Ú$>Î¥n´ìªþù,•íÀÌD[IÆËšëH^&qØ(šbGrô"øÝΊÜeT­¼±¦½ð5eÐW©~ÔÅcÕ±NTQ¹UùJŒ`6B =¹â.�aŠþã31{Eâ_”Q;2wâo*€K¨$‘ÍGf<°í»C÷ÕlÇ8*406Ö‚ L¡÷Z,#'óH€"[áÝ­äZ¶¶B�p²Üªçë -÷Ø“Q?ÖžÿÒ}ÉŽÏLõ™ª[Œhü(д‡9ü×UgeÂANî Zxy�7Þ­"’@]‘åžYìSì,môìL65>í(DÁ)ÏbñìË\G�Ö‡·ëóØ:ØE"MË”Ú/¶#Ú(æ=C•&Ù¯I.?�Dá}/›2¬õÀã1&bäª,O5b ÚDSb¥GFij‰Ü¨IZÍ ò36ÚÍŽÅZ&ö‰ByÔìÀ�ƒ?5>{½0xœXöxw&VhGŽWÔˆ€0¢Œx÷ ³VÒæA'í4 ±¹2"w§ííÞº.Ö_¡3‚ôŸvW‘Þr½‰�_ŸBg;�ž–…´Ðe ÚÉîÔ–œi<ìPÐVðsžÏG5ƒn�Û­®­|¶]òñÚü°¼ ü�øÙ>^àØCcµØ³äîî†ù]VqfGHüìúµm *éí÷@˜%74ÎÞ uÁ'ö* ea£Q±÷rR)vd+r¸2†2l囃ûTÊOÖ¾F3d¢9Sc á….?% ý­cïZÚ&Ë¡¼dû²‘ìYmÛ¦ L‰•8=µÉ·Ò}d¾}M/q-‡IÞ›f'Ž�B1Zà°Oéä é5ÉèüíRü€âðõÏ2ß±<;Ó3M¸ÕÜ;S!Æýc:9š[1˜¢‘%•ø%ë÷±ñîÞˆ¦n2ÃOÆGäö›v‚-Û…±«4¯.K*HÇÊïLlw:Äz€p϶E·7¤ÄvÐ6ÇÀ× �îÓP㯱nGÈC)Ö™‹^®3›D«‘Þmã…VUÜ‘€>äÓàH°×³{×_÷)€,Õ· åë󷺊‹1ò0“ÄQEÕF{.x‹ïÕ|w,¿†LÔ!K} ÿ@ü+bl6��µ_z¸š0ÌsÙ½¾Ù†ƒ±Ì&;Y L@»²·E2ø4lïÌü·E Çžp:¡_¬BÙü‚çŽ ‚i…�?¹Å‰ùõ(ݵn>•I@¡ãCÚq§÷9;}-‹²�LàóCÛ�ÏËô·’ Ø=e�Õ£�»;#·l—,“ÚV¡êÙ§g^÷(·O_Á(†‰I‹=ø……ý�Ö¹âü­`)í½Ð‘y×˼�'?��½tƒîC5¤1cZçµËF0|ýˆµ•¼E<Þ™´ GB‘¬¸ô¨¬8;Ç„˜8�¸Ò7•oßú¥Æë†¤Â8B§Ù§”*«ä™&Àÿ(0Un¯Ó�–ID1Ê÷÷+U*ù7 ;m8ÒÓƒ[£ÌÄÆÄÜ“P-Ú^Ä�OOÍÑká�M²;Õz³;°sNFAùc(½€ÚìËˆŠ¤äð#}Øh8Ló[ ¢š j'“Šb|„é(Šü…Ã(”vË]^Ùõf+†å×)ÂÔ´^ Ù$ÖŸYdT|nÁ~w›8¸'¯ïâÙQî•ØÙ$ÎÂY'ϱâX1"jÙ�øJBǤé¾�nÞbþ�Ž@ ž½0‹7tÄæ”Ä»`NÜÝh轨G|ÛDlf`n¢Võì½X£ïsØØA9VuYEß.í)‘�Ô4%1'v\,lÇG&f¹o x›n"Ìq†¶J‹ý PoV¡þÖ°QÌ€fv!>¾½(kæÄ�±€Ñ£ªÛƒ1<�»¥å�¿N¶‘¹R6žï[ñ@úþ{%X‚Ðζñ‡“¤DÙ óU¨@�¿�îx$hòQk#²gÕv¡xí¶{Óö4€àµu Ú|@óc)ø1Ü8M‹ Pð’A^8žá—°BôWv�¥ÿUEæ� 5|óµïL^T6ãfV¾† l]&$]ÇéssÍ�Ž”•èx$~€O¶'4Ûñ2.•4)ùå|b3º]Q ¾ÉD`’xŠ”t.Œ, <·/ÉÏêŦ A—è«öc¯ïºìþù:6á¡ìòä̺éÔ|x*­êqt¼P%Im "Ô¹š1-ñºT€/°Ø•CEÔ‚:ÍÊÑX›ŽRköî[¶¥7Þ/2«$T‰D?A‹œí/¿bïUaϤ°a]¿ž³S€Öã–2ùi�„øxG* ˆHÛ ^õgÎr³L¨36ÿ¸”É=³yÜ4‰¸S¾凊*LG9‰Ý7W·E/ŠÀ„Èá-Ak!Nü4Gn¼ç‘.Ô» ë½L›:^^V�µ¦áÕò.ƒË†ö�þòñ¼l Ë$8³çÜFóƒ”Õå|ÏkÔv¶IEÁp2>¼gµy׺Ž´Ùý¦Ü¿;.rà6¥§;\AR"â+ì³ ø¿F´èOKßaá_/¦•£Š– “¡_]ûcfí_Ì 8Y�(õ…"A!Ð0wK#0¾I¥FÔx-·Àæg�Û�ã(]aÑ8sšp�1Å £ùñ^Dö; $ØË�%ˆ¨ïšxÝyÊÎÎ$I‘½¥œ9<˜<µn˰ê g2hµÜõ¹ {"Ÿ±ŒD‚Ä-¼ã|ºè Fr†ûð95팘@žz˜ñU¨pŒ¿I¸¡‡¸Â׸‰"JÃ;%mö�×£ ÞãOnÏåd *¬Õ4GšÓœº°Dc›8’®l©N?#ns_.¿p©9;¡ÅKÅ¸­?4Cæ)¤ÕñY‚^¡9(O˜0YKØý`(W¡"ÿ¦ º8°Þ6/C�Å9f|ÆüIí37öTl¹¡‰r» BûóVô‰|°Òü}Êe—ÊTÛµlj~ôœ½zLU¨Š±P<ß[|=·¸:?�jŠéhÑ­)‚€²\/ªÀ›úü‡Fèl³ÛjMþ†ñàå­�X®N:Ñ0Ñ’V¡\þ| ÙµR ëú¾(Ê6…íhÿ¥À׺Í*ùxS¨·»ú¨�Û@ß.ðêéÔ\‚ï…‰_™z¶¶DÆ;xÞmÃDòÏT8áºW¡"ÿ¦þÄâ¤n¹ã _`7SÎ5ì¶¹‘ñ›®.=]zk¬{œþcû‹âð¹g�N£Â¯:s7�ßí¦B¶GÛ~F´ôíö~Ú^þ¿¶Ï¢K×­´1‚øÎ#˜€Õ*Ì¿AŸû~ìÜ¡N‘ž˜EÊtÞÂÇsz4ë×iÿï'”¯QR× |¡ÿ†ðŠƒBÁDDÊ,G‡Cíû‡{¿íßß#@•ŸøoýºC…æiZ`�’£‚qœÖÇ<O¼Oo˜WMÊ!÷lAkþSÂÁ›öÕ¹@o Dv˜NYÿ|s¯”QYù_.ØÞJ#pÅ"È8yà <›@‡>I8‘åç³VÁ\®– ‹+Ál»Í8ìÅ x‰ƒb¶óxø£}º FfSùª€Ws0&«Pþ¨#ý1JމlµˆeïÆû9µ¯Ì?ëû�;Ÿ q¨‚½ ãË>¯  ø_$¨Ÿò©ÚzIÇ–jø–‹0¶oL�£*~ÈñPÕ^-ï»ù®=Å _!O^x7ÀéŸvØ?óQEÒ7F™ L2¯£Sö »Ô€£>ä–3rOõ¸ñn¯76Ý{_^4AÛØÆ�¥ÑÈCš“gïO©ÞÖT< Û4L½U„%dDÛE�CÜävª”e ॠt×(ào1õ0Ç£Ïøë´|¥ž,7?Õ·e¶€UôRÔ5"øùÐ~ÙciÃó3½™g(r¤�[y;ÀlL ¾ûD‚†€Œ?HR}1©Ý¿¡˜eÉ€½G“:Å·9qêÿ_Hbµ¨ö‘)¥–?Ä¡cnžð¸ñýž½fjîP`Ãh¿Bz!ÕD§*ÖaO¾3V# 42“PFnâ†ÒC�Õn Ü0zÖËþz0Õm€;ŸºQ¢XDäF¤Ç_B ¾ 2–¤v8cê*¦{ó>2K¨‡<ŒQà¤öÎ�µ˜ÍúÔ¥—|`�ºØ<Õ­X.õΊ7 ,¯‘_ÕftŸHà ?cPzñôBWò87 ïe¼Sª©,ßh™Nt +Ü&rÑë�–lØß`¾YI€$ ó”síǧrQ¶7X›YñÍpú•H,«9kÎ�p’*GÒçºñ€"è~Ç«ð¾B`–éqˆ¶ x9i„[0°[ŒM^,↘êFޏ›Jn~srSìF0pãÖCŽ8€&‚¨ñôJXÆœ;l·,áñÜã"0%ß› ŽÈŠO×fYõðzœ²›Ô�Ъ¥á[øž5€÷pR”¶]`B~3ÛáÿU:rxΫëýH8@UéÀ¤ˆ©Ž: .ݸëá–jËð(x{õí©Gôqìp7AÉø÷šVŸVmeBCMǶÏi%ƒ§í7|éj‡r¯6ç�2s«j¬ä±ð±ÞcÆ’$ô„)pìþ +Oh­þR�ÙhÕ½Ú ˜F�}X9¯ºa—½ÊV[7ê³ù¡¿IÖ*]åhO“é3à»TN\ÂXÚrQЬMÌhÓñV©Ùožeoõ &˜¥x÷© -&™›ªd«eØÔaÕ�„ Þ„ä…¤hÔ¢_ˆŽc¾`ëV¸Ë×ÓÓ…ùKà¦É�·Üæ"~:A°þw®¼8ؼ®S2z¦2©çáʸÖ_æQP/K¼ˆô‘ÉCU¬oóÎÏz,E·3nÔ¦0°^&—dð1?sÜDêëùË¿š]xQŒszQwý>�Ü*`Ÿ¡p5ΊÌÎà— åù2²µG¿ ¢ÏsIj(#g-(OM’wÇÞ/[�kKÇ%s:;ÂG÷`æ"ä°7dįv}Ì”|Û-dSL‚�Œt ›e6;ú¶ìë–üÙIÊ¥–ª× ¢#?Ûgzé½!憣ÌÝlÃî³+ˆô6q½Ú f$lúõPù«>U±f#˜çC|±�(¢¢:褂°ÇüíÙÁÞžD%=òHãèŒÜ°~döÉÕ¹�Úí÷g· 6Z� ôU8=š�'ý× «5x‹4.ˆÛV`üõ&ŠŸø|ç‰ö—œ`«�p° ̨�JV¾ŒÔš©l¡?(¢S%äá$c±¶z×ÎÕ‹rK“Î�‡1Þ%±Ï®¼|ÜæáÍ�B(ÿø¢OšÊ ŠÃ¨nêv§%üKþÂ2ñ±ßR`]<‹Ì?eà©ùçw—®8~oòj?š³·w2—Û)w˜½H6 µ�H»ù±ÖOÙÏÈ)P]V‰òOR.Õ0fÒ„ÁÄ’@Ôª¶È@‚\ibˆE 7;5@�ýåè«øÏÖVÅ"ìÛËv•–¢˜9–-…±,øÜ؆¦{xô”UPµØ>yy"ŸÀÎLÙ7haаe$*ª,OÙ§'»ãjÊIüîJ†VF{l=gs@_™“_y ð^õøè÷æð¾Īd1z�Åé¬ùl5¤ú¨1êWͬ_fF,Qü÷ �3n©Ü%ý7Eéé98*b\Ñ’?`Îåq¸Tå×.ðô«PòÓF{nQ…嘃ù¸Ó´KÍÔ×É;?ž ±ÅHÊ5(?û? ÅÒ?~Š«6ÿ���eº—B#¢]4>Ò=ÙO†Ï£1êèÈ—þo˜c£Òó¹g\‹7 #÷X‰â#О3økÐ×ñzÛ™c·L–*öµfCä¾ k²ö`NÏîÈõO#“?J0¬þ’ØCúu?Âreœ{ä4vm+QUü ~»¯ù2&t™5ï úãðs³» ¦ÊL ~ûp“¢çZÓܾö„ɦS̲"JëŒü€H)ÀlCňƒíªÆ4;ÿRÇ?ýÜM¼ÅEå- ÂmÌÅMdªt‚'Œ8Qm0*(Ô±Ÿb)nBÓO”ðÜzlŒüÍïë÷Bƒ­x¥«�¤v_†¨*žª"³2ÃôdŒ|{‹Œ· 뫾˜9 é_ã)²¤]ãzs½]¦*µP8>ý -6ž «TP[}äzäÙ¼µ‚_ŸÒ+óçˆl›± JXöCë15Eƒ× ¡ÖoH{ÛÇã…�òX«Ž!<‘P빓íÇ %Þ«tg£YOxÑ€cYYƒÚ~z�Óšàæ¿ÆîÉ@)¾E°ª´gÚˆ‚F\*+wœ¼É©Š(!`FùÝ*”߸¤ôÊ1ÐDœ+võkÂËå†ýÊ*þVQ‘µNÊù¢©qs@"R°q�òqñ †Ë˜[¢aɆ‘µèÂ3]ë"чÎ )d`Cú$þ嘡·¿º³=ãÙÐ@ŸÐf íÃÐ�ÿ¯ôdhpŒÙ̯h¤ÏªÝÁìÃûÜIßZ{áeTÈ~«0~^à]†[£Üü,ÁâB YÞV¾®ä¹€'?µ¤Ëº-¶3•Ç:6Lå�¶LŒ‡kF/Da¸ÆÌìÉ qÕ‘¸¬«P¶3Œc¨#\¿"…Ý™{H–ú¤^ñj{Ä-üž}1郂|ô§ÿ—QêH‹V³ì9p;s¿à"®�'¨(¼˜…µBSGeÞ†pƒW>I÷ àØØÔ½(Å»àdeÁfä²aCþ9‰®á°ž*zÅÊg®ºÙ¤ä\á0'€÷É%?iB¹?Dtþ³é×õ_„s—ºÍ.Þ|ÂùqÑèñëê¸((ÈÃRRXOÃÃ/XðdÚøL¬'bYî“�—GDs5ÒÃdµ?YL°8(œ!…_Äá+$2˜as�Ãäea”+Eòez%8q§å 7¡%±-µ{hŸ-\¾IŠÆR¿¤IÁø ¹› ÷8d™Y—Ñ/´S!Ù© }N?5¬a5„}Ðáγô3pÍØ_mlïLw³ဠÀ[�F°ˆ ·†Ü¶6ÁüëÈM$_n-âë^K{ûq÷.I5Ç_¤^(qÔÜlRlP½|ÖúÛj�˜XâùæñcÌ´d¬ü òrßfàO\NÄèiúņ%†%U¿MdßùŠˆ´ˆ ƒŠ¨¥;UH9™))ùÕ_Q²ËzTÓ”â{�Œ—�¸b[-÷ÌwqÍ¥¿q9Õ¹„'�™d‰Çf9ó­.F„xÃéÊÜ)ê¾£‹Vý˜½lê!.gR|¶ßHɦC /X£ÝR<Šx�¶ø¶˜ŠµfGf—Šª{›„o›˜^Ž<d",CCfV¾ìñî"ÚàLQÆjââ¶´Àbc‹“˜œÀ²Âž`•\ßpÀt°?�g·W8êŦ@ªI‘–1[¹ó͉Ŧãç¡Çý:ï/�† `Î"øp FøÃ‡þab§çzÜ–h|årÛŽ4š>n–KyaD>€ÁKT}Kßa0|»x²]6 ê%eÐp«P�ïÿû�Æ 9–ÞÜ�pŠ‹ýÖÉWŒñDAhhJÅT͘‘™‘££Ò{Gï¬5¼µ�6=|¡3š<<%là°€üiðÔÁ�Uò<”YÛ•ß¶'“lޱú’†è™f{¿ûÇ’½Õ¤»¸ƒ™_WgŸ Ë©‡½Â–µµõ9Ä,NÏ|-¬¶n^B¯ £InÑ^WïMUÜPŒ¥|´øMÕ~Ή;-¸DoÎ^å�·Ì©[Ü6-eäXYüÿ vØ·>Z5[4Ñ»—ùR}‹*ý=ß+:™®:í{½< ç\>Ó.{-´÷�>Ò��ÄÀÅÆèôW?hõ—6™„§?ó¹ÄJ¤Ü+”x3“,Õ/F7 ÐX°»2/¯ƒ A Kœ[Úæ5–ä$°˜÷”·P÷cÇMnOJò.‡ì¥Ù’­¥5hÉö:&¸€´HÔ�C XFÐþŠ5=#ÃyÇñpw÷<2O(gªŠÄãôƒ œ IÛ’¢«L»¼føÕh�/+a|~ðún1»fD)ŒHt1=ïp5—áûº‚g­=}¶--�§ãHš�RÀñ�\¶‘W°m�Eyã©ýä˜ù!4¤‚,"b»t…Y�¨‡öbH­¤ÇÛ§t¼m ×¢Ç†��ÍsÎ?%N‹~¼ °d˜ß€Æ!“&ï ï`Á7\8¸B)ì3üâQ/”•Ìœi U±S“úÿfã§Äû(´á¯f/>j°¼Ø@$-Õ d\�dÞ)Øêä®1´Š@¥k�XbÊÁr„98W®Âf×n� 5±".Â’À$Ny±à•('<ØMÑä6 ‚ðKn�îclU�ƒØ^^fèpÚ£ŒóÚXÖ´Óþ•Þ´]æèÇP,Íšû_Š#ÉVï¬ õ*XÃÇ?œ÷Vd~ùòÜR—^U‡}UÖ0¸½ÍCåר,|ë"¹_XçòÊ~ŒØŸEHÙJö4zÂ~¸ú°d&vôK6Ç€šM4q}޵Å;£¨È„—mëÃ%JÖÂÒû#Š[Üꔌ*M¼MÓ^gvÓb*€o/,ýBÀFb ƒ“¸E}ÖÀ• à%1ŒÅ:†ï.˜œ¤?¢ú3a½å´ùÕæWúÙ1L °Ý�i U+f˜_€%Å\ê �¼< )F+šÄz¶ýè?{w“r‘æ’,E`c)�[mü¼!J_aSÄ&ÚÔÀ´ë›•ç 2È7àþE‡¶F%åàórBJBùÂqçw—·¶n<€W¼^s¢ZM4Åo¡_JäIú¢�H—Z.ƹJ–µ…… =›§îø>ßÅä+ƒÀñ";)Íá'ΆGà±ÌÜ\‡·>ÏÈWìÝtÞ'Yv=ÇiÓòiw†U29C˜¶]I:HÀ¼$ˆÔOM“r—�Ÿ‰ vQeŒò`h™…ŸË��:Àœh˜J…@Ö‚¢·Œ•‚L…å‹v:ÝEÎHˆ�¡%V¡â8ÿC�ÑLºtvî.¿¾ æãÆ9½k?‚¨±³G´ˆU¨Èw>ƒ {r[.<_"U&¤çÍÌã›¶´ô)Iá}ù_\û±Œfþä¯pîTŽŒ´’‚â½Ûð*ì0L–ö…%oÍÒKP_^¨Ð§ |=�Éè�H‡`¬BÅKüÍ”ìW!/Uöâ�,¯°÷¤Ž�Lã�^ñ~2%Gf�À`Øîß7:4Zb ’ßìö*Øý4ãþf�Ãv’ó¸Û�Ïmëz; 8^�ò` ɸò`¹“8vá”ðbðH?A{¯B%¼þ[Ðó[w%!*�ìŽã¯©bÍÏ‹ó›mgØ�O~Û< ÆÐ±?`?cÜãdt½!™óùc¾¿è¯ŸB¨¸§@6K¾ÜGRž¯äÒ¨¢TÚ^çkJªLÀl²ã m“!õ´ƒw1æÿñY˜ }R¨&ÈP¹pöë@¿×�³6³:9‹'LdަOãÝÈ\‚ã0í Dë¿yƒS’§Ÿ¨ ƒ¢4±—Ÿ?bV²¿´®ªÿËï7k~Îuë®'²p4C)»iÙV&¥||†O¶ ¶�Ž€ˆ?·áB}¦¥¹ÝANS4T¨/xtkz¢DûÇí7¶··µw`0Ô¬b-‰¡§°& ”"öµ¡Ž?ÊÇ6¸bÕùžÎ‡]›î‚ˆ3µ¿_„“4Ÿ"±>yPÿ$tÙX\6�©‚OÊ.éß�hÅœ-ü ‰8¨- ß–퓾ëçí´3Pû² (0ŽÏOE¡˜evT»á’w@‘6ÊCŸ´�Þ>møÒ¢wÃúG�(§¬\ã¢X«¬N‰Ož�žF¹Ãh¤Â\éCŸRéYâ__.HàÛDÎá�5�‰×ïÚ˜‘ hâ€&Þ@'³³“†–2ƒv~^P¹9-ºEž­BÒQ`­§Ñ�¢žžï‹tb|ZYÅ\O%DÍŸÉj‰È°¼½ß,ŒD£ô`F�öþÒ]}ƒ¢Hñ Cÿ¼Ê"Õ ÌÚ½‹C¼�®†RX¯X% §U(Õ¿Q>||×ÙRùUG>—Qj»{y³`qÈo'jë…ü1ïÊÿ™-¿¹\} ¼(âÝz{ìñ ÖóñnW æÊH¿€°§Šj· Àü×ÊðW/htÔ Äå›t á/«ú#è­~i÷C!)À@¨"àð›*4¹ú ÂO_H¨åÍfÏæ Å0¬Q‰þ×8x[kk[3@Û)«�ü÷m�Ôô¤»T%�â,F(ˆ¿ýŒsÕXŽy¢Ç&N[þ³‰5je“8Äßnì–Øž'´»Ô¢`‘ÈÍÈU€¥$SK+Ör�eŽ’½ JÇŠ¸´ÜÕÙé +R<&”³÷Ï7hæþ»`¶èrM³óŸ{{V ÀdµYÇ2=QP°¹Ð@÷½y|~€â+ŒÄ0*ÿœÔbùZ$^Oû{¢â™Ëd#@öá&÷Òäþ!îog˜‚mTšÑ\Q¼8%öBó�µþ¯ð¸�ö¤‹¹²Oã¨Må5}k!·L“ìpl¬–ÃË)ÞðƒŸ–ú ’Ñ3oôœ‚}‹U!Ôeü‹úÙ0" Çî’Üü—º[#Üa~¾OóŒ•Gh|ƒÿµ UоÅhU¤¡ÿFw¥æ‹·7�1–™ZKoî×µµ2½8XYK˜@€š„ü @ðÇ-_¼Ã½ügß_äü ”#BlA¤|�ˆÁñ0ø-”cÆ=ei¬nü¹½›F-#)çx@æqŒÖU¨‘ÿýa Æ•»?ëd3ûu?û»Hóÿzª®˜C“9€y¢ ¬ž¨¢�Û!N.¸ßZ ^Êa{6¯&ÔZ޳ó?`eØÞ\QYA ÅMAŸÐÚbEbû>¬ÃjýÈ+#PušzÄ|ñžNT�0úF/~ø…¨ŒòÛ!(ªHå qÄ9Èv®ÇuÇ  ·f ·*‘(³T­èÀ+WÒñ³WSä¾iù—)øÅ»~Ê/æÉ$±èzÁðé?…d~sFl—™eÚÇ�¤BÔÛU(?ø¿Ã�ˆ•E7�X÷KÅHb±³ýfc¥¯#òRE~�Šþzô!Ò´Û5Äî+Í+†ìá<ZmâĽŒá¼„á ˜¥ŒóaŒW ;µÝŽ1 …N¿{ŒÒˆ¥!ÞÝa~?ÙýÅ‹âw~e˜&%ü–M©ø®ž ­*›j©§é�@�¸…�8s®àþCÆ`ë-§¡S�#öëÖÆ_ó@@$®è.ÈÿÙŸœê“.fb™É‹0e$I7åeZzßè ,˜1u2?IàB–¾|l#oe²@Á É$�8ƒB *ˆë~ ‚ù»¯�…õ£ÊñmqéÄ@;ÒÛ¢ëµ×Œ+”±Õqûhhú–?0Àr«Ø]Õ®‹Ôoà,0H"ݶŒdÿ7¾û²mkÒÈbÙIYúì™ý@´6À"Ô ,¨Œ½ûÙHm qY_p´@Ÿ6`üœÁ»*Êþç0Üî.‘ÇÖÅ+o¥ÉývdŠÌÛĆ猙Ǽ.xóù&Tòa=’·ˆfmÿw2ÊÑØÁÑ�pu«~%³?é¤B�‡e¤Ýîswš´FeeUÂäîrŽÂÈ;ÛJ£J¢’-hK?NˆØ|3XÝagO¼a À«‚~oŽ9ãòXó‚å±HfÊ—ì?8Ä(”E|rèdAäÕœæÁÞÏ›ëm1-‡ÀË£§¬C=!ä%` Œ�蘬Á¹ÕþkÓç×��6UQ̃bPßã€ñ€·’º »ÀHÄGØ;€‡õ©“Ì&ú¶W¼ˆƒÕÃèi6 „1yä ûçÝçO¨>¨v,Ê®¯®p;÷¼0|H�½óf0Äl<9±âXŒëeƒ¸ã¬*£¾´Å‹ÂWè+ ± ʽùA`V åš1)K×/5 ˰=ÁH-A7úmzB²\lkbbný-<`’ÑeXf�Ü/q×.ëí�±”h)“iÍç]ƒ,܉kFœ8€ÜĨ# /¤Ϩc$€NôáL|yL˜ï‘ž±æÚ™/ðÓeÑ÷îCLÀ¼!jâ1°\²d³®žÿ”è’ÌöH¡:%‹¬Ì–×\mj[#IUW" IÆ÷ýÊèƒÜ7 ´=|CokУÐ×Np&y'òë�-¶A‹í´KÎýdãø3øÌCõŒ|ç¿SR˜Û�A7û£fŠ42æ‘Ðlhé­9ç¨Bö‰4ÔŸNóœâ[ÐzŽÑ=$'¹y×Z;ìØ9Ÿš€Ï™mrhœ•1Øô¾šP•ÅÃNÐ@ÞŽ@!Ž)mõ,cè*5õ„ '·Ý¤4Õ�¯H ÏwA ?<ÙÝ]ù ˆ5¡­Í3J² ðYTÇqŠ¾Æ“i±Þ|c ’;“öúÅ;"“bº¦o\³3Åͱj…¤ Ï< ÏL·á¤Äï Mס‚ÌÀ?vÑ0éÅIšˆÎˆVûÅ飇b8×*Üg\Ô|½éb€©FE³Ž6~b¼Q®NWƒ–#ò^+À«èv°Ä3øƒ¸¦ÜКÁ7I¸wÌÇÍö3Ó÷W%å’3ée£`k`"¹o .ò¢È þt%°Å{ÑU›®ùz‡g´m@"0ì5�öÜ*6^�{ëòØê9Áóu@÷²dÉÇȱµÿÒ�µo×Cl>ËÕ 6w~ðÿ_Áa~É�nþ|^Í.¶s¡¥nñF4–U‘ëï(YtáŒHªƒšT0Š»ÀVÀèºè{|ðâ)tMíz2zÕÓ³Ÿ¢Y(¶A~P&w÷ýæ)(ó®kz‰øFõG55ZO[<á…–aOmk/pÓmè(¢Ã›¾~¨J¯2¨d‰mˆ‰{ƒoTú?1HÌÈŽÑ�ËŽŽIIÍ­þvŒŽ^° òþs{ÌÞ¸À:Òž´K—æòI«äüVÈö�ÐRËÏjJʤjöÄg(½uÅÀÛ7$5AýïÛ?íH"Þ ð"9p*ÐB³?}}„ŸMnrék'¯ú>êÍºŽ­^”ãç—å¿ñ£“¢áˆÏ®ÙDèP¶­ ‰Ç/>Á>]ü#BÃ;jò,ì�¯ð_4$Î9ôvÄ‹¦L– çØ�¬Á~gó2QÝG ÙWàY]c^Šþg¥N;ëÙ�Ee¨Wê²FŸ ’¡“)8SkÐ�×Q‚€öß,ÃdßÀ¼üÆáGmþÑbô]¾•bvÛõÆ?„ô»ºÑ@üÚ¡£8íƒàÒ¯¢­«ÔtMn­tÕã”þ„v¿ò>»dÔõù¡Z䬑×þþçBíck²NÁx·t<`.õ�å…î“2SsïB\~RfDaо«ÀHHåìqƒŠY—}ÒÀ˜Z3çgùëd€p?\›y�"£®¾vþÝ?ˆXH m0;!ý øÁpŸ‘XóÏ£‡lTµÅÞ4̪_ƒÍöþ>d°wëk I†Ú' “抵"¼û˜÷8¸yù°åé¦Ë1Úî¸x<¾©Ò ŒÖ¢¾µŠûì-/�,ºž'�ª%½ÈnaèÀ;zýµ£˜På`Å�Äf†þ¢Êo�µ×»e#'°Ë:¦�‚¾²ÿãýá_† 4üŸX¼æ¿è¤µçßh�aª‚ù ÕÆg±#ãBígí‹´K!„áŸÕ@ s®™u¢„ÏCì h   ‡¾]~†t4§èP±ÈíAøp¶ÆúÃjj2@š¬A(þš�ÔAcqž>æJ/‰¹–ÎONvOýގ¿åKý4|¹ðñ6 Ù·}.¨{ZÉWåº ¾{´»Õ#£‘R‘€å+”W`2ûÃCOf‹V�îíå[£ 7ç0Q¾ÄY¥¤/ÓJ±± XVô‘ÜãícG¼­‘iÔ7‘q±“vÝÒ¥P‰�¤Pïø³>_ƒ‡I$î¨a"³×Ý…ÿÜ1Ì�ÑÝ=}…Ø$DæßúUÑ@Pb'nÐå3#me'ÿ½™³c½ß ¼ ÷ýè 6î€Ä€Õßœ}�…`z©¤5QÿC\ }©íìóc<�oÃöÄðtÂlkgÈz¾¨Zs.ÁK'§$å[êÚü•Ô…ëIÖØLðŠ¹´;‘ýqÕÔbSQÇ (OÁ)[‚×:`K_›ØtW¼²h2mÞS«3éU„:ØdÀZg¿ÎVŠr “�iΗŒxÓòY™s5>ø%›Ou©|ý¿iVÌ! 6áÆ|Fs;©¹ïúOÄj!0Håv€~5Ò�Äš~MˆÇÛO�DæbÖ6™/É€8W³lªƒoN2ý²þSŒÃÙs™½çÕwÃXAO�­ÙéÚ jÀ-j³­ä¤Td;;ØÉT ÛØ¦;ùTd¦d8ÔfÀDs{e»í‚m \% öÞ¼qÁ*@�V8k©ÂÙ¤žZÚŒV� 8”òÜlQX>KÕ¬�Š @œƒÊÙ€Š"ü#‡™„ë°Ç™ZÈUÚÊ&ÛãcIþçÊÂLP‹ÂÅkJšZMñ «X�'Š;�­ÖÂòܶ]$vqMMçá Ñ ±ÛgÔÂ��D"v‘ðÁb6�’1¶gÜ¥Œ»y·¸xÇ +ÿûùpþ±z¸põÙ`"¢Zrí¥�…_]mS¥FÆ#¥ÈÌìÌ®,~ÑDضšÙÑk¼i÷e{¥ë­ âi/ãmW" –¢wF ŸH ¹Ø/× À–BK�½°û)Y—þ?)g “µ!·Œ«O¸gc‘5Á´ýêop}ã¡Q.‹®ºÛ1§ G~µÏ—}mÿ¤Kû°�«÷á"!óA<�¿UÛ�ªÁ<–fì[®yœZ¯•ge¯UByþËUù½Ý‹%{ùêhÈÙS%+Amñ¯êFêq µî„ß Šîw<þïmŒ¹v¤ÿ+fÄY L£àÀ”Hë$ø¾ €bbÞ„‡ª"LpèeÈŸÊɹË1hCÎèÀSüO“ôYÐÜ_û¿šò€Ù�ùoêsL™½càW•KÁ(öb³kƒrKÉåÀ^$"`Ù} ÚõOAîohp?ÿx[ð!T}:.òWSÙ¬ø6^ýÁAƒe¦é�³÷j¡&£›½ïô+Ór 0�ó…�éݧ{e;uyQP2Ao6 & Î{jü½þë^唺†öó=ÏǸR㥟¶F‘&v@AŽÿ”/vQ8ÚB�º,¨]þý§Ù*ù‚U3i¾¡£¨¼�h}v+BF,»‘/Mød›ƒ�`òתïý§Þ.]‡¥ïòÜÞ6¹5÷yP}§ÁÂ%ß/4MúA¹³“o\°oüÞî†Ç †ÖLØè4âB™í.€ýâ´‰WËõ+Á7òUw8–4&Ä|}³TÞTê6AÐ6²4ƒLHíÝË © aÞ�ïßÇ8´ÖÔ I´ =¹}%þáþ’ÿ)axÄèß�ô�¿QÃìw±G¾„Þ >ûD£ïV¬¥•êÖCÆÍÝŒ_˜:´uwqZÞÈå7˜Qežå­¼øìHŒÖwßxÛΧ°€ªwŸ‡ëއK€·<»¢â£A®^áúÇ‘žTÍ@g‡¡µÁæ· è`¨©á‘¶ƒB|ÊB{ æâïRÎkÆ!®ñHáUWö5?° •NË%þ^(?-7Z¶µ&=:×£Ò¿Ü?6°Áˆ›Öãµp>§Ês{:1:åoN¨ï‘�£®�? !^œ��;ÀѼKM +û­Â£&2x³Ý0�ÄÖLÞ%iFZ¡KÇ¿PïæÔ¬"[E;pÔøÇ0ÁðÃ�%Àø«]nß TÏÃó˦ۓC¯Ò­© µ .i°+@ú¦&p-zZf—þjºDg9þóåüëÒü‹»Ž�ÑH™jtò›äçrw*oRƒYÿz’ú¦·‘Þ7[Ò6ƼÀ¶ �y!$HÊ–jî¿Ò�— x& šÎÅù«´btldRW‘—épœe‘ôÃA›-ÏZÑþ1ñsý?Z‘ÉúNùúŠÅåpë:9�E­ýÕøè FzVmFaFÖèO4𻩨5¯9É}ª•ѹ½?ó±Ð•ÐÕQuá™MÁÌ"Ï{¿YŽùt¡-}ïIïóF“AIÚ[CÎïÆòÖi­�—Ü4s¶ÅRÚcþJãìš=Åë£ÿQqâ‚%’?‹‰[èö[�37`v,$K³‰Ë í¼ì:Ýöé×Ü!«V)‘ù/KI.-Ž =†?e i?½oìF½,f+dhFZ_ïÖo%ñȉ�㋇Q7ç“§r“ÌÎðu¼ÒÕav¨¯sòòX³¼Ç#å/,É�ŠqØZ©”1°Y¡1Q້‰’×`z”À!FAUúf(ŽJ IŠK+þÚÇ {›‰ÇxgÿMGÞõ ç4�Î4 ÿ+Jî~,iòCáÇûóŸÚ ºWFïæk$e[m›§=á}R¨.‡œ¾É`>(�²€bÆ æ\"+åûZü-ǹöp¾߈ãåtEH™Œ¦8 v KD»�ªW8_N“”¼ Õ„M°Ãý?ñMó}ãÃCk‰k=•T÷$ZªÌ1J$þ¬ž36í×t$(kù=Kgbp™Y­jTå†cì°2’-Suj _£6ÿjú›ÉÜo ?‘£é#cÈÈCFãwÕ¬kȤ©Âh£sÔy½^ víÖ†¸¼+ ?  NƒûI¡†zX›¤º”10Œ¶4í]�ŠªÞ%8tÒwI«G!Ô"ÍÛƒÙÿŽ ÙÆöÊä¡¿*õ¥‚=ðž/pÜõj�þ9MÀ8 ÁÂ>:ËG¶ ò*þwMšã86FôšH\ak70ð¯³Ð%Ö–ßµ¬Ø•“Onò~ÞSÐ+lm=%öUÆÅ¼ÕÕÛB`4×b­yGôÊÁ,!ɸ@p·Ûsâ»búkXëS¯Ÿò}4‡4/4åosßÄ…áæÿŠRÇ.w”1ª~V·Ñw*n°U>/{�vÅ\C@�Xúç�]�!H2„Ããwx|O4w) *Gq ”ø×BªÏÊ«ª<Èó·Ó›˜bS©ŒX(�¶ŒdDyÝϑܷ€ñÁòŽâ—¶Y—NÒLYÒpú±Ã{V•U@+Îì‘áýµ\ÛŽ#®ËÑŠv…–ëOõÊ>Í]uœxnüCÀÏ<”{+Y0‘28 špÞn¿tV¡¼|M ^Aãß+Äj}hf@)à‘DNäÑe ­¤õ´sg¡Ì}ø¾‚¢¸ô}YƒMÙ¬Ë~f$Z�Ÿ·ª†þ ûöèGöOïð-ÕJCÔÇ顗Ɇý�,®—çGšwï6=n è« Yë\êýßÖ¢d•1Y£ƒ˜¶ÂCƒ×süçzŸ#…öC…J×aq³�‚¥n‹¼y‹!ëÝ+£ÁýèŸÎ¦øïõ(ÁëI= x›² Îl¢Ë*„‡��þ’�Ìg9ÃÓª§Ç~ľ;O›GÁD-wà÷ʆ+ø¿Q\‚*H+DÖ†ôÆØ¥ß&×—?{öz�°Â­M­tr^h«ÊÄ|ø�®ŸUùrÕä�ïÿ¥âBïAÏTrU2ë9Ìf<[jÁ9€))"Ë¥bgi¨%c…ÛœO3½qÙ¢5­¯·‘5cô,'~µ §¬ž�`o“94þ« nù¾„mWÙÚttñÜ8b  9Eeå&‰rGÙ÷ ¤…mØzM)© ti-˜‰ƒ”:mÖ™ÐÛΰs� $w-dÛbª¿‰æøŠÉõ ¤º •½v6âÉ¿¹Í´ûth¡y¸žzu·=ÂpF”!¬C�–?·�Þ¹Àò<°Ëò‡RÓª qÆ|�ß<ü+¸Aë|ŠO53bŽè4�¾—zC×v<º]ôŽB]ÕKæÊ_¤°Íx/×2Ug¾(gs†za8k¿•U—už)J&‡ Ä A1Q°“ä)yBwŠŠ 7ÐŽµ%ÜH€âßG˜ g†´¹år¶¬-Q&¡¶ ô RÙf~‰¨É¹™fŒåذF[¼Ð–™d}'@“³8yysé�qìÄUADægj×c¥Ì¶ÎÅòb�Ç�Gáq£ñÀ'î—IoyS5 %TßÉÇÚ`íó+'â¢xn¢‰;›ªQÒTÛURõò8·ö&ï œìŸAã¾á)ÔÎ2)߯y#Û³Ëz] Åm&ÔÓl}‡S n<㌙´ËHüŒ«'OÜ“Êôštèqç[ÇÐe™�â¢Ð3Ëéóµ Ð%n»9±De÷j%ä¸^àˆ‘&Ž—ÜþÊ1¬Ë÷í¾ëQz¾á‚òPD?ÿZ‡Žs÷.‰¬�üs!\!WÔ幟G´×õ›¼¦èZù~¨ºgî%ÍïKÊ�˜w8gàŽÚdWO-¸Zï¸ïœ¸º^Kº~üìR ñ —š43ÚtŸ?þP'ÈZ_ºørÁ¢å0S>êë‰zêjÁ·ò·×¢‰~ãf±Q¢¸Óœ%Ÿ>¹^>@ÕØ5Zùm(�…0Ý/Ç.å¤Nõ¼ïªÑU`䌤>½#Ò§Ë)i¤Aý�E¦}À 7¶ 5ý§ÕÒaT9ÝÙd°�©6¸üèÚI8݃ ñ¢LWð¦B&>¬ð·%©qœb\Nü²‚È&\"«0(H£¹Ÿzg&h&*€—!Ðt‡ $~ˆÈ²÷sj“Wz¨. Ê,-L"žä;|ôGÍ Ø……<6ŒÉRÿú…ÂÛº'¹\Í6¤w+–¿µBÅò9ýʵbÖ3€.A…y)xƒ®k;¡íîywÎ>´õ:T¨ñŸ¬èTTŒslZ<ÖéÒÉÑpn“{£’‰s~‚àÞÅj¼ ð¥HʳË<–§¨NŠu—lÁʰà9´è*$cóïHÛëš¶‰D£rŸÃÙMi—šQË'Wøü-ÊXc+üÑ‹ß@ ,²Üu‰ŸiesuM‘Ìwù¾‚¨Uñ„ÀÞ¨æeZ�S»FÙ ]?�šZ¼�ÝH}ÿÖ"Cþ±æ‡„TŸ²ìFå¶ø ËadïµÑº,ü !Ž/‹=¼¤>F梜]ЗU(È´ÚyÚ¤É+¢" šnM]Ïð̧>ôJäêéÈKwÈG–þ�¢…/w)æ9Í}½‘€ç¤Ðøà%\ÿ¶Ä~z{¶ZwWÀÒ({¹cQk[–ðõS˜iÚylÊ_ìOûŠo v¨XJ±ØøX±þtrû›7š-ð7—Êï \´â5€JÌ{Ï¨Ž€ºÿTÀâL–G«õV~Ž„GD€w° [q„�x=*p 6 òž„ñ%iÔlÏ+I?ë0s·“AhÚ§ŽxÕ''STÐ@ÜÎ\Á.Ì¿D§úC‰Nû‡pQ7 {ùp!;ùêö$�êŠä�¡î�—„Œj,ÒF¨ú.—h¡çç+žAn¨H€ÕbÄ �kÆ>¦ë?©µá>+…ó4Õw¸³GD†ÔÖ ðªG@?žøõÖ:]Ë\¶iôÒÙ>A°½Uk3øð»wh¾oþZÎ’’€7­¾òÓþ'SÕÐ\¼Ÿ��Ö鉀ÿvUþÚ‘BÅôÂTS1"¸)ч 7Ãü—¦ÜÆ,Bæ ê7Zð&Jžý§—4)JýÔÿóæµéç�\bR)=ÌÀéë[©?.°þΑàÖ­S†Ò=÷ÒŸ‡?m|*ÇР„ zØcp`m%ÉÕâÑ¡õÖ²gò˜Õô�&l'–Q›—åž™?¯A­/ÚiVáØfai_x„X`xŒÑäºÞrZøéY N-LÕ!÷‚éù#ZI U]/3%¡Ù×nŠLßYYï£èå¥á“í�†oþy§xE9q‘9SÝXK�<²ÁÇÁø i‡ÌLZœeT®>Òu®SVŽD|ä8ÅE4ú ÉÉÔ–¦ÕÍjÌx2Üý1~dÅ”]Ð!T0Xjé�`î&û[U˜¹#ƒ ²fgêkOGá·*Àý ª¹>·ŠŒ �1&nÀßÒÐñTijq -É'�õ�l¸ §¿(½½Ÿb}�kÈ„`t›þAwT/0†« 0Ë+·e;ž„~E]÷­»%V(°Ç¥ÛÂÒ‡-ÑüëçšÈ ®.¨þhûOzÑqÍoÅ_œ{'¿aýˆ×¼xŸÙŸ ¦—ŸÖϰ­­!ôr=Ø ˜áv†¼Í… ’…~^ü—Áöí콓£ŠÁõ‚AeáÈÎØt´ç'Lo:;’7"·Ù¤Jâî&â&î:Þò4]ÕACÞÞï«î_7«N¼­à*oãnïá®ïã£9PûŸŒ¶£=N†'ÊhéÑÑâbÐ"A¸Â”+ãøìÄn¥sÉÁTÛу°béf{Ì¡c¥û`ªv8C 6eíxža³ib;Ñy£Õ%æËQßs9ö0˜TwêìÄÝb7ä6Ev5Ý„Ï:5‹_÷lWJ~¨;ÑÉ $h}NWÓˆ‹ÕU&)?•ôQï¾»*>|—|±˜–¿íÝ× €Çb9‹s�PTÀ�´SñÑϪ›ý† {û“ •ž°�ŸÀÑQYÓT3%ñéS˜ùÍÔÆ¥& ���‘¤B„d8>j8c]?õ-ÉáÆzö·c—%-urC€ v©íIì±ßá…éÿ9‘­·€J²�ŠÁNc'iÌVFîB6r'1Bþ„âì,õœÏùÉÌêAòâ0êtÜüOA�€¡.r*5Á MwØxâ3“o›ÊRMñ¦Léý“æ0³§1nhBCP3ÉL—ÛDõt¢€ôΠŲ̀ óaŒà…£Ñ4bÕ(£ÛÇ*’ÏI5š®Mœ€Ü¤‡üÿâÿ!½swwƒŸr÷‡“:ª¬Òä.RÍgë{‚áÙoÜØ�ŽO±¹º@hâµ^cžÇxØÅi@Õøä¤ÔŠäOÉ©}«ÕÖÅ×}zÛ……ž36)ïÍ2owoy™Œo¨Åª5®/Í-©KÕ‚dRp®¼—,9�&™`R©`\ PÛ‹�›íý‘ϱ�œÊ2ȉ(s§’¼#8’´i­TvsÍ‘µzÌ2I Üêøem¬"`>Ù,l& àÕŒ^âÿ¥PD&µ8UŒóET�‘ñ¾®ïV¼ÍÞXÒ¥ÍòÎææ™g�ÿíÄîFß�¶õ¼ö.kIrwY8Ò!¶xø!ËÓüöC� ¹;¡î kA+©ÑKüûÓ¡€¿ü¦*qÊÕ‹øìÛ\N‡Ö›úZ’b^XÀµƒhð(„8�3àÊ�å#45< Þ¿•j’áÏr𹑽rk}ª`h¤JÕ¿ F²u¢Ìªü�ñ£¥­×9z`ƒ÷EÅ@Xy«þ"hþ©d'ó££�o��Æ‹BÞ~0×Õ0 ´½Ayü?‘ ºáy£Š1ãùÚÙæ»™wõünó] Šã¿ý½m¥=»+b Ûöçâ»¶ûÜå“|-i “f�`¢€¡úüuâ€Ôì¬Sõý¯ŸœŽ �ÏŒ· É€#k ¹Öeùeدõ5o�>/5Êv=i’ÅÌ_<×íûƒãv¬Ã˜*ЋðL� Ü%üÜÌ©Äßâl9ýJÚÇ!d¨@nI¤Øx+Œ{Ns´ÞC*±¬Ùöî­IîXlyÇyö#ãaMÉà4÷Kqg]û� òÁ/ƒÛ+¶ÜE*NXÀÒèû[Ë€æ'wMÛL£oSéÃgzk™�FØ|,c°è²ª™0ª¿ߟ¾+^Yž—¶ø^×ȃƴ½®yÑéÑå†ãdÁ0 yâs—@Sž@fY¡aÉÀ#y3ä�É~Ãqg$2C×~]›3¥„ÒøÓSEP$…} ûÚÇÓkµu#Ô½³Ï“ª­«d�ž¢²X¸ä¬ ­5ž‹³0Õ¸–ÙoîiÕš¿}7’\aqÏ/ÿŽvXLÚã�c0Š 2:J>Œ˜!{uch @JÔbÅ=©žD¨D¨O,ÖŸ£ø£~³“” UKÙjv³ ƒ±1A«+h–¿“ð�áîOaÆBBH)—’ŒU€&¼ø¾ßˆH'Õ+"ûj8Ípõ©€Eæo,�]Ýj­MÂê`KÝo!lßÂþ…~�¹ŸÅã)1ÿ‚Öl…¾�æàéîÄbÚÀ|¤3`õJö?oÉñ�˜Fò3ùa¨ùÐŒ¥ü Ï{ €ß¢ÑÿQ~_z,:O—}»¤uoÎûˆûVg>«céÿ ò^“öâ²VAÞ˜à¤^(8�ða›wª”D{’CJüÖÄ5E_Öóvh9ë`-•ù¦™ÿ/É[ÉÜs ~¼™_ Ž1£NYêƒdöòÎñ¸‘J.ÂçlTq¹·k}ÏçÂ\êÛþ´ œ×€ôpÔ5ëâO�* £P$@OU`þÊdxrS“3Óâ h|œï¸ë㘭³LùžTíÙÌÖ›“–—Ÿî¶2·o·ÚbÚÔnvu;'‚œûHªÿ·J–±—”`+�á§´CµCÅ`¥O÷ü¤ê�¹qñ4Ü êáNÊèhŒòý@gøY^(˪¦3ÄX M…�©D&¦*f´‰¬ˆU&�–Wéƒç©v_MïAh‡ H·Ä¾Úh󯄹ƒ®É[³Ä8ì£ZìÇSn“ùUÛhöÿ’û¤Z4NAë¥yA§Ð:ÆbæüÜmVÕÏÁF©Fþþ?侃 ßœ «–PG‚Ô¾±ç Eçn= céÓ0ÿÏ„¢Ä¤/ãÇ|< ÷~•XÌǵˆ×ÖÁº·\4,–£Zo}û ó$æóš×à†LˆThëˆEÓ-·¾5`?l€¢4ÿ)‚Ź1µÆ¼Ê2òüU®åÛt‰ð?É7 ‚¯?àæžäC¸éoù`¥~v.‰EA[åÎã`·giN}�¸M‘�C½YHM¶��‡³Ež”‡CK–Dn²MF±•·Gm¶ë`—ô¯zœÍžWXêÓ²£ªIFtY‹ŸÊ8Ý:ªAòâæòY_�p°ap>²ájB)[Ÿ�ý ö·t>®Äu÷KíßÝvïkâ¶ÍÃDŸi% ¯NsH.å­$Ñϓߡ¶{ŠFP 1Ӝɸ‹ÿÀ¾¸øö.�—�ÕÈ�[�ià 9±¦êŠ= å©LQ�)ˆÃ×`ô3<ë w¬I .9S=Ç–‰òF'ªªÒa7\çVŒt£øþw: þVçÍÅ»3wØ'ª]üV-]÷€)ÔšM`Ð�XŸÑ²ªZt ' Ì”‰¤£UÛWËfuy ÔßÁ?K}ê?´ü„([¬à0ï¶Á´‘+×øÔÝnWk¿,„›Øò½&´ŽÿÎnQ•ªrMŸ˜\,Ð:¼ ×˜Ï]!ÎF|�öjX«y+«6Ô¡Ï�T(m!/¯Ó®¼´F—1 Pg‡cùÿ²Ôézí þÞ°oH·~iÍs/ëžÛ4éù[;y–®ÏÆBÔ§.J”eÞM/ýöj½`NÐçâ¦Öã ‚Ö%éíùÄn¯)Ïoµ9UUðë»þÒÿ ¥Ìý#²žÕ¡z´fǵUru›ºø€Üid†#`ð»?=žc𱾨C s?*á%‹¢^ø•âùœ¬;J¾±‡šñßÄÔH¨Ï|¡ïxtȱ2í40ë¬IžÌS ²³=€i%3†Y¦K]„úYü ,ô°O9ò�°dT`ƒÄ¼èk/Ñ­õŒ¾ ²ú:<õ% ‰ÔP„Ap¤‚ÞŸëÇ¿ÏvÇ=ñôXyò\õnYÑlCâ^ÜœVT…ŸA Q ¾I^î6<šJþ—m>•ŽˆM#‹BtŽ&þY¡w}¦—ÃHëg—V¸Ïe1 ä5HuÏr>éÅV¸´¾3¤i£4~)XØ­�Ëê#ö7¬W÷´)�÷5ëÌ6—¾#ɦpéœÙ˜%;Á%FJþéÉdPT}ª”I'Ë T9A ÿ¯ Xìs6°†B(ÂÁ@�T8@ÞÌœH’ÿ5 E÷÷ù&åÛç2ø­Á$"¡ñ̈Ä)n*",a:„�ï��¨ñmà°¬´²ŒúÄÍgi_<%R©¾mGâgðd,ȶ˜J›áBÃÅѰq&FÅÍxú{�H?ht¶­¿¥òÑxõÐüf¶ë%ûóÔ‰ù›ieOá‡ò¨ÝùOÜê+P�%Ïa¦ÿä^U’–ÇOœÎBƒ}·]’ìyµ²2`î�ÏGmÿoîm4;¯f›�Ô1ßÝ!GÑeÝÀÀ) ä!÷­!¿ûLC±¦]|8õëëÃ%Sפe½æ7"ˆ33ñ¸Õƒb »Td;‰fvö&ÔfXÔߩ𨊊¥ÛJr*°6¥FÇh†i¥aŠ©$è©é© ª-›,š?sT§~Ay3F1+# ¨éfÖÛ‡ yö/¹[']§…bîssµÌ¿b\¹åææ/ÆÒ1z�¶Éw òXžÛÂÙ°qWÝäxgÝþó qÝÖg.Ya%ˆM,eƒÃoÎ5V¤ï`+8ÕsF>›¾sÒŸôôÔÔsø$…Çz ª¡¦Áš˜kÔ55ó×Òw@L�Mβ٠’òõÌtë’è¸Å¿@üCƒæÍY&tßÏîüÒÅë´>VÉÙn‡¤ù –nÀD ýyòÄzlXzçö(·Ú®nÊ?¶¹ÔúI5ÆŠ)3 ~uvðÉúKc2´D=«y.åø»¸ËÖ0†›9Æj¨a”¤ðÒ°}Mè˜_§¾QÞZùú -šÅ»ëGPnþÏdìŸóÔÌOQThÕkË¿šç» œœ º…5¤Íhý»¯¡YZc#WÄï²Èe`äñ…@©ƒí¸ž¥.Ȳ¯ºÙ¹ÈøºWܪ–¸"»zzÔ44 õôF”‡`˜o‘6@Á¯þvO¶o.VËDÚEmœ|;�QJ[`mh‹0tERlƒ–_iNí]ŒóÛ¯ÉìûÃÇ¿¤V_L¼î‰{λN˜¶3[z\Ò=„'·¡ °z8S�) ’Ê”Ÿ:’ÏÀÅž4…¥º¹h‘ž´Èò>y@L‰»ÑÒ¢! =áË’ISé�ãNwÇG¤! bìL#!¯è$=©xV! Ìt¡+Gô°°ZF�8 Ê[zCŠØÔHAY6í`W€›�CÅÕÔ RöÑP‹ÑQ3HÓ0ÈËMÍÊOKÎÈW�+U Û¿³÷Ö·ÐgÆ- ®aíW‚´hsùxÓœõ/`6ô3È}y÷þž]ŸÀißï+j? " |Ç�_?¨lç™&áÙÙálÑü(øTÔ½M¢ö:è—  Ž�¡ucã9ïÝýËr, {’ƒ‚Q€i�ùïIØ<_¸"ìr4ÒûÃý§]6¢Ð²ÛˆX)Å?}?\_/Ö£ä‚M6п­ÓX‡Î�½É‰p´òc'1;ÓcM=/V>ŒÜ€Å0Ÿ®szØxÉ´ ­Vù‡™_ýÕ.ä°!� @�#*ã0ôõûÓ?L,07 g6a•gèù´®;ùIý}™…·ÑÓ€®N”*«¨MH––WÕú| &Œ—mÃ’)è Pþß ã«q{ªÀðç„^oÖ«Þa¡\Ãé™8 Ö•y\�G‰‹O�ÿij”TúP¾¢Qü»¶õªÍe–Q¿ÍØèç;çLJ�c EŸb  þñ·ÒûsØþHŠéŸgù3+Ø;¶2¯ËXçVíµ<¿B;¾‹Ò_‚ð—X¯¼t7ŒÕfŒ1·¿ãåÁtÓ7R?×àö�;÷3m&G �$=CØØ€T‰�¦«Ù²ó2’fÇw+kx‰ØûøY�]ÜæäàfÉ3j“>T×<Ö†û 'S;ùV•Äô”T86+‘ν–“Þ�1�èÔø¼b¹g»“É-À,Þ¸m ¿/D(`/¼�1vx½¯A�Æ×#/Ÿ×¤5á„v@~ò<+ÒýbÍ:¤“þHÿ¦¤`ì›ÍÜkø½ØE,¼óú:+x‰º`\änoÝ(åǵòéx" 3õsj‹ m0o�x%zøo‹Þ³—ð† Á6~4Å¿sè)¹´Îš8Û"á01ÌAYÚó;ƒ4L@‘‚ö“/"¨C3 òvíbcÉ5DW>ååfËFyK I²päíšüÝä`¿Í÷ËRyцt„°)¸±Øv¬aÕï%obn Ýž+’¾«¤Ç‰øt¨kþoÓ tAdÏ#x,H_†ûP§€¹¤ª2…dL Ícu^âªÚËXdaãšÊWøÛ �]zu]IFûçŠÛ›šRg‡k,*`A÷ £Ó™†çÞÈ¥Ìö<€ ;."fØ�&‹×O9èU +9S @f•æá`VJ,Å嵤 “™1^”«zpvÅ ˆ¨B¬ý�ªúÃE¡û6ñ8�N¸ûåÖ:?^Ñð‹‹;æôYºf�…­¢–O§jýbÍŠ GË ‰ïÂÆ1þigøÈ”¬½åä�º)â»þâ”þ±1¿æÇ!@fMC�´ð§ˆ� ¼Þhå3l4+™eV¶^åýçü¶·usMI9‹2_Äý¶ƒì Ä#×,ßÃ?£¹·‹¡©p|ý„ø á¹ Siðïðÿi&Ièª- Ÿ¤Eã|‹ eNOÁ€säv<%‹Š¬Œa  6 7ôà$�§¼Y@¥™ïÏ7¿MíþÒnû®Ž�Í�,ðù¿2Cz?·:Ü^w&¾i¬èõ®^4œM~‡üì©›×£´Œ]üM¯Ç‰ÇŠÒl”l 'Cªr|v´R¥{ߺg€¤ç ·}H;Řþy ÷w DŘT(¥ÕÊ;¹éÔõ_nù™í¥no~EÈ"( þÉxÈ�[’F1Îqh\QÒ©ŸM^~ °Û=$;‹w ¢Ž± H¹e¨]ÃÚ7¢ø0à é« SqV— £|ñÑÕYßy#6H76ɵ¨æÃM³óû©µA àÈxÆ_2‘â<Ý.Ç>42º°¦T·’6 àš4Ó™ƒaÇÇ%ø1Ѐ£·_�8�‹{Âì&ûx§¦>IÝKÛªûeÃ'³�Ïëz yH~�£ÚQæ$«Ae±˜­Ò±ú³¯ÿ‹@I!±ó*|ÔœîÓä^-ë•M÷v‘xin Í“ˆJZ�/F>ðIë9SÝ÷þs‹0asˆæ©ÜÅ=îñ–×IŠÃXº¼MÊ•„Éê¼'$] ë§gŠ”²èl™­ïÞüTÈww�úà»Up([w½}™¼Qe’É=nozÇ`wÊHª FBX5%_µ„#Û4€—€˜ Åû§úF½æÞƺE¹•™úa³õg=Ï/]/8@•#jÕÿ‰ Ûr¯Ú{絘K·¶jkøUŠ ^Nmî§�ò¨ øÐ¹µ,YH{Dr#}:†éŠÒîÈ>úÏ6KFóyë­Éª— ±ê,]r!=QÄÏUk÷Ѷ¸{´Ä<§ÂŽåÚ±˜ÇoìEþÚ*}ÌaÍÁ±�ÐÑË6èê¸;(KDÜEø�a¥ ¶a>B"ꯪ¤ º<�žZ‹AžÍ Xα7b—4q ¾„t;Ì®dÔËXìUg»Á-Œ,òWôöþðAlóØ…A) yUÌÅ�½·õ0åR{‡P°!ª?ê€Å<ʼn®£0*aîæ‡ÉÜ`AOQ‡(aÈÈœÈ�H ÿ¾ª ÑDiÙ 1ô¶Õ„U(*«‡Ö%Ó‡%zK¦Œ—}GŸŸ7lE3�›=ܶï9“0E°yœšKu”ÛhT§§{Búi%-Î7gr5¦”ìÂ<¼Ã•?-TŒï·Oä”ÑÙbï†kLr6UsZj�dqÌÕºÏ,a¦×ݘýúP_ð�0Ó4’¹ L^©Ô¤´E[‰Ó¤éysaY?LF5ÛX³»Qè¢R�IÛÌžµòÕÎê|¡Ëh�Üg{E�z[è)ãŸIv5us'Šm �,°òƯ3K™¡%ÉWñ¶¾›)·:…ë±2²¶)�l…d}YF '‹n1ÞÕ‚kš�ÄHtïcúüKé;¶@�”Ö•’’!TõüV¶Ú‘×Ó?ʬtS\H8¬U®¨âfá…óË_K*Í©T™zfí~€ªSÌ/ž#¹1i9¤]:|™Hd.#KúÒ´“�þnnRÃN™”C¸„æ=ÒuðýBš àý¢`“ï•Üüêô³é4é«á;ª†zîY„ZÚ·�³ï›”+~Î_RÕŸ‹R$¼�<_Ž€Yk ÍåÁõ¥@sÅÚ|¢6j£Ð¶íŠyúÕ—ýžJÊ�[½àÙ‘L+ô å]pÆ N•F¸A\CD4r·NŠDl¤ºvðƒ'~ÿ{ =9¼|¶ä– #‚¨ò]]vuæ´‰òÐ{ÂEWLîäÝ€º×‰æý8…L»ã_#wwt¤"Œ[C1ôù�ñú»³=NU “�ÅØò7{ÓÇVDž/…¼Eîäyö~&}8ˆ5Ä„�Äÿtz±ðŽ‹»á„iM2C0J@KçŽÉÄ�Ú‘Óõ f¹œ!³Í8äb3�÷fHm±úø#™ºLœRÁ�Ò7ºp¨!Ï—ë<»›i;Ï %†ÞFêS^”¹}ômÜL‹7~IÄ"%òœ·8ò«CÞ=27*¯\ÒU­~Àg*o=­í.C©+N¨+æIKõ3��|ô¯Ý*²2ÿ±s*Ä+Ó—ç0¢ÈŽŒ˜vG§”˜I±£×mpC’„|;(íMò"ÑÖÎâ2åîï)牔cû?ùÓ<ŒÄ¸*DsÛŠëð%”ˈœO‚Ò-.j5¢dN h/ÆYKk.-g�WNW~$Àf¦J©b2ǯѠ?dˆy½Ó-ùÑÍÿ‰Ø6ÿ-2Ë[þw…%Vã˜?ö �Õ'±–>è¼þžG²ùZcßêÅ­²cør>gî³Õ”ýÏ„Ú4îù›§Ë„(�ÖÍ–%Ce5Š_ÓÞg.K¬_þú¿Ñp΄[F`txLÌ‘lK]©“cçðOTéjÅaRµéþž‡5ÑX¥€ºvr×ù6Ÿ—yÀ›ù.tRL¯eÝ•q`ù�0OŒ™^Ékí¯â±s¡Îä&µÓ—[Xôf´XÛ”X[¡Å7Iú;4ƒ…M¤ »®õãâ ¡"tcGÐ'ü[*Tvóè5¿Fñ%걺,>ðƒSyíÛ‹·!>\Nå+Êg¤KJƒÛu ÇT-+cSÞ·¢äm•4NAßÛIŠ0õ,ízd:æ 'C0-™C –ŠÜáåè§}k¨DÒ÷h©…Fªqö¨F6|X{¤Hêö:�â‡ÖâÇ)YZøãêùCTé2ZÍ—y(æ­x¯ÃeÈ÷.'o€Ýu#[ËTM4‘ùö»9ÏÞ™§Î�i9a rªïÇÓ‹ùñÝŠ:£oäŒq¼êaÓïÿhVôQ‰�¸6Þ¹ä£t¼Í"Ô²š÷»¥L ìqŒ–ߨ„Kבí ]vø —d’<¶b ]$BYU3ȳõœó§0cT’&7ó²øk­ñ¸DÕ1„¼,ˆqÓ[g"2dˆêi±Ä!¬bë¼[“/eßs^²’E>pt?håÓ Q‰‡1ª>ƒ#BÌóÎpHrãÈìT äÍò€F�ÏxÙ¾Éýò¬Û!ßg´6l 2‰§ñ¤�wý¦W9ó„·Û”jwrPÖ�+6{¢DLU¼w±5ŒtÌ¿G‡Ï´‡á¡o'3÷5:Ç6<ÞiXÁìZü(Á¯Œ?껪¿pV5ÉÀí ÌÏgd¯ 95«|7�-ñov{kކ>X¡]¯‚&²k"�:š Ï:ù`D“’xèÂÍ“ÎZûñGâÉGMé¼Ö€CYÖÞ ß�‡‹úSB^7‚`z�Ç.yéYŽè6íF'jÕ¶xÈ¿]²?'àýûâuKü÷�`]ñæªÆ´Y”è¤àÛ©ù}­3ý�,ì�5YÉ×±;QÆ+¢î�‰.&Rí6¾Þ¯>V®�uKâD$Ê•K”Åà�‘·-ݦ o<×’¹‰cOΠ¹ázš€—®QÞÊÒêWßó7T§­�êûqdÎò)±’ömKÓLKYû,óeX§`zô;$·�hb�r^xáxLŒ¬wÂÕº†Œìj2Ja2o#¼€e*ˆŠDÁS–�oµ¾. ,»¨É}gžåİš6P~²Õ¸˜×ó`[Ðô%þR¹z}±¿oã‘h’Wtò•znP�i—fz´¥È¾¡¤º¡Ä¯§®ðV—¡ÕÆck¥º¹�+PKh³ëÇ*¿R›¹Ùèý/ßÔžo: PS §’|X‘0{oŒ mB 1”ŸP'�¢É²óe9€œºli#ØÝà€´`ÄY7°0wšˆ]{*)éØÈ5É*¹~¦‡U«çÛú4¾¦RÍdÐÈçÒ$Â�¶áƒ…B~ ‘=&oêD¯$ ±û€®å*Ú²\X^ëá^æ’°T ³*¦WùaæôAyéy•ð¤XÉ“dÊqIƦ7ÇXT‘&u¥#�uÇo®2 w(<Þ¸�ì( ¶¨kËSÓÈW¼mŽpªÃURq¢¶M®£³?KHJ$IUT¦zû#Q3µo $ÁR*Õœ|®`\j^UFM«À�(^Æ$.)ˆ$oR]!"Q­6€ïŽ3{ýÊ—5÷Š¥C]<¶PaKù5#æÅgK‹ãå˜Ò¢¾*‰p¬ª¡’à�¯a5ƒWphëê\¿`ÃÊëL±<£€2qøB�0’ãö+ ë“ K´1ÅàR6X¡(f¥X4C·˜² ™Š>fJ�~~ �Q˜|©nòòóF¡â­M¥¢ÎlNÂI«»s;p¼¯À�3¡•ê[‹†ó‘„IŠHå”}Ùñ³ jVþ¦Ó¯XÕ®tW�£b[&å†i Ì%4mÍÏegË5Bß.q’ l¿Ò)®Ð\Öj-™ÿ†å*öɇ@[ øj¸ÏÏgôÅö·UŒôH+OüŸkT9±Ç¯éàNŸ'šßê`ýì$;­ª·[VñÀKîö&†£C@ ÍŽÒFåÅ‚§c\§cS~}eå²þÞÖ ƒ©ÿì•ý ~4:þMýa�Ĺìn$Ì%<íŒo o󔲬Ûfqx!•ùøwÄ—6²Z´OÈðnI'£_“½æ­ÕŽÈf6Å}.—ü�ûì%⻤ývù]W:ß`âMT¼~ P2ûÉ÷5`Í+ÌFVB>ÿ¼Ò‹`C‡a}”ÚF¶D_”9[ÌnýøçûÃÕ1¯?/Éߨ¢|åž‹÷ͤ>ª$Ë@þú.>tüÛÕ‹CÏ~›šŸj[-í¯?(¦¿ÍìŸØòƒÚøjÈN,%TA¿©Óí¯Çò¡ñØÉî÷}ÑÉ¡úÜÃË7ô„Þ7�ù_•½L2ítWQyÔYâ|Ãù"À3ùVùßúy4Š6ŸQÖÈTÅg_×þæ‘›¹žŸ×AAƒ˜ôº™„�ì“Q„Ö{“äÞ%“™ן7ìh…ÚÛç=jùïð{Yd·%Ï'yÔW°X–2­;貊DcWGÛ DóÝ¥…[‡NÞSÔ4NÉþ‘º³s’ÖýÑrRa;ñˆïñf~EµL°×ãøÑÏl…ì+Ô ½Ö>®n®¯×¾'Ï60{w| Ý‡‹Ð*Û¡Uò}ùØ'¾· ±籈©Çó²Bú¡¨)�½¾Õl�DeŽDe�$V Ã6-Ýü©™€Ï-ùìWA�SDË@û§ÏNYÞÓS]œAÅ|?ßj¢Ýjo´T‘ŸtÖpqÔÿŒ}— Ó¼øÀLÿZû§¬¼O¿Ç•r�6ÝÑAJ Ýü�œ6[}f‹�ÄN“´Á£Åã¯4™•’>ã¥QUƒi ¦Aˆ·Úwß¿öV›÷ç7䎽À®að¨“ój¨æt·w ò}gÈúLš˜Q¬•”Ŧ“�#áä^ÖÊFâ ´?K,yDå…þ’§¥¨ªò/B-:ž¨PÈP ÒqnCÊÓÜ o Og;ñXØ„õ ôD»jŽhF…JÄ6�ü1Ù‰†Á¿¤�$�Tª[:w+>PSöÇá«TV× '9A™.&ôVÒ¹õ/˜ÈbýaBÝAŠ‹í>`,˜ÓgpUg’ɵwG»"`è¦`ãz¸˜˜º8wŒu0ð�Õ „žÔ)0Þ\"ê}(öC:Õ{¡Z=ÚP cbÔD¾}¯÷Á*�¶­•‘vGj·õíJPtI;#)S¸ˆ³Ü茨ó9|Aa}â—<|üºM2iÄ;ùŒðK¤“­Áö:Çc°«öž$R;,ùl¹|˜ŸY#SŽvù¨ï–_ Ÿ’hEðÆqpÐ; ƒÒ\�Å8u‚‘_J½ËÊšµ´Ez¶¬×%Ã=¶;çØÁ •›©?ŠÚ<ûˆv|™ Xýr°ì½äÜ?Á�)¼žUdŸ×/ï¶^xÁèd\x©~�+Ð'úC[Hµq ã{i=abB°JÚNµêò^À"€ÿBñ^LOa‘#` ô;–'$Òÿ7}TFÚ þ\wÇŽ„A×Q¶éé�‰—û&D500 ;ã›Î¤«-¡&ˆ”÷.¾”@j“ä29Þš› ¨ÄI´ é+ÀCVdÀ¸°im.U7¿R#îPhšÔ½SHFm–O&Ía�v½€“FÑ´a«º¯ àüF>‡(€ô+(¸å¯jOÉ1º©±!F c9N¾z•:rjïà�! Ù‹‚0Lï¥f3xù¿—sþ´xÈ)?‰M ¶åL&b™ËÑ·"Kn .›füž(¨x#ÃC˜|ÖÁ3Ê¢%ÄLh¸Y†&˜xMaRX`¼�Çâha›öò�uòH¿�A}É8DýÌÖ~%âtýS’Ÿ‹¤dG W_ótxøÍû™Ò3KŽ)q•é9öý+«eÏdú0œÖny9;ÆY›Ù³ÃVΆèñV0:‚ŽÉóvͰĺ§ªîŒp¨&.ÌI”pÒ0‘±Ð1°±°öõäÅÅ-ü{PíÉ_ù0‘Õ•ŒˆP”ìæ‰èŸŠÊ¤sS{!x“[ÀFI׌}ƒ›>ÞΤ¦F*·IÂÐy×é„g5ZxÄu oè‹§JµT7;ùSÑ\ûßkÂ#°ÅØÅF~wÉù M·óêÍŠãiã&GSÕz“ÔæKÜŒ ‘ð&Ye€ŸQÆK¿?ÉñË¢^ÅñÌs6§7'Êh[×ÞÝå»›‰AÈ.DÒ\?²Î;°Åµ!cŽ&’¤’ëNe?Q§ÑŸ~œ³{ôß5 gÁƒbA“Á¨ »ã;Ù›­IîOŠ87˜�ÒJÓòÞÛ­#d5x|˜ÓVN©7ìê 6ôèæÈ¡ÔSÛdl³ëéÁYZ¯�?éu·}�•ü(  ” ¡’•�ÂV^ãraw›x†ÌN,sfõžÇÚÓ –'7K6sÿÍÅcz7þÕYn ‘ˆMu8�:[bYGð'wÕ…Y10Á; N‚VÙÖÒü‰˜®‘ôýŸB¥.vðÊ6¡îd�Ì{&2GR¢ZIè®}`1ÒŸ0X×ÛËÿsŒ+r ´ t7ào÷U>ös�NiO÷£í\që�´³¤à‰—~," .ÙáÆŸõOçúe¨e¨^¬HWÖýc )³Y*ã–3Œ»7¾Vk±rãoÐCÅ�vsM�3黃~™Ï�ê^´šš;êÚ;»è[Y2˜4f8©N²¤ô,wD4Âí€äš´¤ÅÒTøG@_Ì?sÀ9H•ÛF¯½_ÆÊ‰Ï÷ …½eÇŸI±Ò‘‡ÄhãV��#ÕBz°ûÛQp)€Wy0ð `™?å>Ö�Dø~ÜHÁ÷“#v?BüýAyUn¬´+öaõ[†œzË3¬]Ù”7r¦Œ¸Ð�'D·Tö©¶_O€Z¿ŠÝ¨SÝ÷NÈ÷f!e¦ÅØaM0ÜÅ�i¶'7©Ô'䈂L”äÔíFgʽÃéœÕÄzPeE|Ýÿ‰Þ]Óg›?×¼*«¿ò'—ß»Möä.· ýÄàçäÓü3÷N±†Ï Kóö0+;yzŠà7½'bX˜.l:·%ò:tÔO­&ùÛ¿¥‹k†änógg«Êd$pT�8¹T Ëôß9²‰à#{iœNº\¿~a‡öùà¾k½É®|„ÎH$•Z žãdÈyë0îÙÑ ½R FÛêƒó+Èoj¶EËK§Ýâ|fLZi%\à›^QÓüë] §¬Î®ÔàVÏÄv4c†˜ò ï:^´ˆ>l×YC¢ÃJ‡iª[~AÜžÅÔöeÝ AÀ³õ°çVÍÀ'ª¦þCÕhfh´Cœ)Où÷AÃ.MŽ‚ë¸Û9 ·uI¬‰ßªb]’ƒÇ~Š•¥WvyŒ5®½÷&›ª<Ÿý† ™1¢ÅTìÎÈØÿ*eùë�Í™¦z¼•&iRäD¬‚è]+´|Ëì/»Å:U:í¡(ÞºÃFcÚEôÁeØöobè 9ÃÀXÂ�¤´&Í>ßlŽôW ýB«#Ó|ïÿ+=ó:G5³‹ñª½¾·yêWBD jñ׈�ùÅrÆ€ºC Çït41ˆ*ÔEeõ#-гþµyC˜åÆ©L¸¯ú?¾�>÷wf؇‚tмýh'¾jýÈñÜ÷”–3;Ä*`ñHõ]ž [ü*-Ó½vQÜ3€�Ã& öɺÑùÃä6GZ`&_�ž´­|ÿÄãF BJÈ_e(E4MÐ6 Èÿ =?B¤Š¶Í`CLé'ÉXîúnýÿXtÔUu¿nšÉrC*˜œJ£Gq¢âäý@ˆx¢ÿÓ”ÿ¿è�‚½�ª®‘­ºHú±™�„‰½�8�ª�¹½½…]Õƒ‘*’ ÊÇ™ÚáœuŒ �t¢‹bóÇúÌÛ›�9ŠŠr)w™ùïÏD/AR€dW޽ÿwÙ¨É+ÞE�KöõVµíü&&ð ×Uv@+:��ŸÁ¬øg[L¦…2+²›òÕ¦U¿:1‡æ­dÂé‹mÏìŘ8ÿ‡VíÍÇe!ú=šêœa¬�â><;-2‰ÏÞ¸žßêÛ¦rt„y(v}øÖL\ù„ÓéëŒt®[·ü·eá|Ù½¨d"ÿz-7YbÙãc³P#~Yµu+‚ö¸rû‹yû‡1"+žÊ5¢Cø¨F‘…ŠV½àQÿ ò¹® ž#JÖ*2‹æã‰Ì"pұ‚åÞÓ+ÉãÝë™U‰Eÿ誱ى}jS¥¡ÜBÂØf,;6Ü•M=_öm}u¼:w›%×ÜD]'"<»À~rˆfoêù—f�øÚ�J‰n¹¡¬( °f†_8úÃ�É®Ï1Ú\(\,k3ê†ÂG¨§�ÝIc½R·Èû¢' { 0š‡J¶ ì´…°û¥Šc{Ÿ1jØ!—çñZÊ 4@÷zHõÏóšÐ˜_5R /‘éÇF+³s ˆ"n@Î+z¾áb—þæ'e–Éù÷YSOŸOÖlV¸ƒÇ@_&H½nk¼›Rh¼ae¡Ã_í+LÁ¦Gïµ…( máã10@rðÚ;ßÐ~URó¦²�•ìŸïyßÈ9’XØÛÄÝT…a+& QÙ,}_ƒíÚæ·ŽÂ¸³›CZ ¢bèp°jbü§jòv-îŸ.-”@šõ¾ˆ)..²l«¦šhÙVAB�‘°.1.]I#6eÕR€—üÐìÛ ÈHÞ§Ñÿë¬X;øÔc™Pèµ–¢<¾“ñ”®± " §`V‰«GdbÜ–;íî\ŒÔ�=TôËó:y,=nÂoBô v `Ú0zÑ› À]ì5Á6]wc˜ÝWÞØûx¬Ó–ørž¤­¬Ó4*�¬á‘KÀG%¤ô2¤ÿ–hc“Cz×7;ã8=I² ÌÇ>§&÷Þ¹óŽÂ�%ßf+}$‚T–gíé0nÉŸ¡Å<ò¸JÑØ ÷W:w¿úvC?k²K&_â9îk[ˆª$¤B±í~Å8¶´m"Ç‘æùFÝVh­�™³Û(œÎ>›Å ^uµùmÕõ.õýg@™H®p,ðÄbŽ]Ï?Œœq-3É´“Ãøùa—÷`* þ#Ç‘Û×ãr&61‚~?)ÏX˜[Öϸ†ôGAA/äâ§–¤í¨} GQø11./7&#[y:=•…Û¿ 0¿F’´ÏÿÿtvíÑL÷aü·Ú"FdHR‘ËRÂFa«1ÆHc¨µ ǵ"zen¹Œ(3™°!—’–:¤›–Í\òVŠ"·Ä\J.¥§S�"^¿ê­÷ý·ÿ¿ç<çû9Ÿçú}¾Ï³äиnMaÝÖJõɵ«Úôn–éâëÇÝZÒ+ˆzÝì3J·z¥}ÞÁÓ…Ÿ­๸¸—ò³«çWWK, Ƴ~qQÃu±ÓïŸ>oôùV{¼ÓPY†ªtÃætúÙJ>Cä¹5Å£ ç¿\ûb[y[¨¥ᘮ(àÈÛX8V@²cÿ•䌢ÈñïBÃR!ª‰M�’˜6•b%ÇñµL“gG=¼¤w_1æc-C¢Ý⫲+�™& W ‚#_'.  L×dZœ2¡h'|¤›A޶:»ë„ÛÒµ4¤%^½¥_–§â� Y—Ñ›2›ún­~Y–Ç%ê}sÐxî1ßÓP’p®úabçòlܵ-PE�DŒfD×F™>ˆ¥çÓ>.#®Ûß6´�oo¢q¢¹¢}%µ)EÍlwíÊ\)œæAkxŸ ¬ÒÊ®ÁSe/é{“4wÜ@ÑTÈ~2ëÖ{F¹êî´“<^ƒjpÈhɦh¹õ˜b+­íU“( vuKGŸœþUw›ŽÑÊIûU׃Ru:®ä^ÍF8 ¨¡)mí3ùE L¨k`ì)l®R+ÕÕ¥�¤ÁHg1s‚Ðü ½¶!~¯Ÿ^Ùa†œjIòe0°¨HxvŽnáuçäNÞõÖæ°'™Ø°A-×E�5V@™kL.t¤Ô‹‹ÑÖ…ç”üAåæ>ɹ‚v�‘Á§jÔyï½YbÅã#_?XDÔ˪í^ÌSpmÖñe@NsÉFøì§Š)ë+ˆš{¡ås³ªr&Îõó¬6Pè §MANú/�»'H Ç3³>Ÿsl^�ÃT7î{B$÷Ä^¼BÌÜ×1š»Œ�°«ÀÔÒÆßÖt.h [ˆ´Ðd $ ùs¼Â-ÐNœC<›VI~ç,*ŠÉ/Ùv¹™PHœæ ž+|ýC}žckxÉcå bd{£ÿä ,«ýa49ôMÍ,ô¶b¯Ëõs„[Ë [ûžñ$©tßô~Í`/£¬\ë gØ]è`˜*³‡¼ÂvðB[aˆKþÕ¾¹û‘´£2ØgjÔ\KBN¡ÁW‚EVtŠÆ@À.© ¹ª,�¤Ëþ¶V+>Ìç²ʅ\—JªàPxóǺ»¨ð€ƒ»Š¼AùH¾“ïƒÙÿž–Ÿ9IKC½ÅM*u@Ç7Å ö cÊXû„{ö#-qv<ˆ¶7tbIÒ±ÿ#Yð‘oŹ™ŸØu{ÁƒišÃ4Ê*Ú.ù§_²ˆ‡Øý>dýëãÆVŠêO�„‘Ó¤aUn:Áh3PŒ‡ôbÁÞÚ;v¡¶»'–-ž.z«A‘vmAÀO_™ùÉåàXÕïħtNœÀ\`¥MݸõŒ«új�³yîµÿßbJPüð[×í�£Ÿjw{.œ¤¯!=©|#†œ…5Q#�Ò½KwzR—{o™=I}‘IŒ¦à<·ÛÃðˆÿo6^îº\ž~€ð‚žçs@0š»¶ûø‹Ë@ ~àÏrÊeºPÝ‚†‹AŠø‡‚ïü,£jœ –�5¢ôSÅBß�hÍW¸°³“Â)•ñ¨z^ŒOÜãoÄZd°8™4¹“ÇÃÇt Mîk‰¯ ¥ôV=î "q6êîèÓÔ8`§å¦= ey|gHÞCŠˆ?‡&¥/cž=ŸƒðÁR³‡¸7cŽ­]t'û9Å“ÁYõK)ï”ÃÆ±ÊÍ)Fê®Ö/�$GÿæUM— ~-ü¦*êÚí×3/—ÜVz¯yéA%ÖoÒ*.ŸW~¾²ûŸãYÏù²uÌ ŸÙžÞs’˜À�æ²…>Ëú:,/P¿ë?i%›/saU=kþƒo‡ßrUðr;/áÞc\x¢Oõüì§gÕçߘ$<_]¼ù¡³üÚÃQ“Þp…¿‹z°fÏöçïhO¿>!joƒjþD5q7 EÅYùk]Cÿ-x¦–ßzŸaÍO«Wf�˘*æ›jxùúœô½ª½R#1ÁKxó§š+O^G[öŧŸ{«{ÆŽŸá[ÓfæŠ9Üžºz'Ïx‡{„ ô<ütZ?\Ã?ÔÃÇ“±Kœyiq�h‘Ñç Q£ î � ×ôw`·ÊÃ,û侪E×_,¿zùÖ´-ÏÒƒ¢û¥XíøïleÄaCh:{›»ô”K³Ö�u÷Óðcº¨#û¤ÞÏ PKC¥Túo9"ÑëIgpgsigned/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packUT äÁsbäÁsbux èè ptöf```b¶i|sæöÅr2h¹_ܵ·aÓÏl3¥ ÿ yÛ¶eH¯ÔŽ»¿ÈüCª„ÝŒþ‹¡,lE­KøïX‹,ʺøxCêÒ7+ŠÍ¦¥,>?·rJ¨XkGF¬šã±˜¬4y•ªûZ¦O™“$áeòtirôÃC¾¢ç’n;ððäý¢]çRw½ßyÖ}ƒÞÏsIbe3κ=÷ ýw3Oÿ§šhß…rcy«ó.ÉÓ¸�Žë‰æb2äaø_ÝãðÒ{6¿àAåŠ&5ß;®»§šÞ‹¿(ýÜçÌ -&Ë„C7BšµM—vifæyñ-ópìzöèÕéeµ¶KÎY~*¹ñðäý»¾YÝßnóKÐI'S¾¦�Ól_Å{»mçÓ>ù¿–›šéÁ÷†�ÕÌ+>Y|ZËR1ÇØÄÀÀØ0ÐãÌiý“º—´Wj$®Ø8=+uæ é“ÚG.e—\ûpéë�3¦m©GWžüY©™º[8Ÿ#ìëšäSõ‡ÔÔ«2®m)í¶ýÌpçŒôÍ�ŒsVûèÂL ò8}Æó)ƒÕ6=ëŠ9Úç=tAB[Ÿ31HüdÞ·œ‰xû^…ñÚŸØ7ñ΂û…K½]§²kK^êžÿPKC¥Tƒ|ÓòØHgpgsigned/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxUT äÁsbäÁsbux èèû_âŸÌÀÀÀÄ0 @€‰Ì<Ä0ËƬ#³áÃŽÍM÷?þß±÷÷ ã/7vŸ}Ú-Wï'“=zï–œ˜|Ö´3=G·äuß `Ôìãá\;Çä‰Êì£ 2¥/Ký—ÖØKµ²LÔ�äbé5:kxézlæšÝ‰7«zÖ1å?a“=4kںŞy?´ú¶œVxÑ+`ðWàÆ¤6ŨÂài·–ÞúÁmÅë`ðJˆég‡ã£=.«'Xñ300.º‹ˆWÙå@< ˆÕNì›xgÁýŒÂ¥�Þ®SÙµ%/uÏÿ s§Iι|u‚5ëó)L;o-ï�¢PKC¥T"¿.°@¶Hgpgsigned/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxUT äÁsbäÁsbux èè ×ST­k†álÛ¶mÛÜÕʶkeÛ¶í•mÛ¶m[{\ç÷xŸï5ÿ*�@ àPаpȨè˜X8¸ø„Ä$¤TÔt L¬lì\<¼ü€ÀÀ  �Ú€™ØDÄ$4�À€N`@'°=€€'€?@0@$@"@ ¸   ``` Ð |p ðèt‚€@ `ú@¨è} €>@àv Šjšº&6€./@H4@<@*@@9à† 5M€;‚ôn èYXØ88t‚:A>�€@·…4‚bà�0°¶�4‚*ú@õoÐêàhl šPP Ø´   p;ÐY€yÀΠû€.Ð3€ÀÖ ¯€>0À ÁÀ;ƒv4‚an F@xƒ`ì¼|€­Ád[ƒ¶ÜÌÀÀðÁB¢û‚ešÁÍ`e�m€^0À-Á¦V[ƒÞ$`o0ÀÁ^¾€€Àwl ŽØð�€Þ"8- Ðè—t�knè·Ü°38à[ô�¶¼ApÀ�ì è4‚ö¯ì ؼ°58àžà€­Á�.wÿ‚|+€�!àß à~€·x‹€·ØBð![C:!�€÷¡„ŸÓcÙBN(:Ë)Â>Ê8—¿$št{úL8ìG~ùs¬œ´*Ë ¤|—¦Y-JsÛ1Y·±Y\ èä ©Êì¯VÁ@µ»Ï)Ž#—ÊVâ’oªË zÖ¡xû5H¼Õ69Tèô‡‘³@ßò4´yxÓMgí³‰„ÏÒfW¤Žöt>sQ ¨�®ûÓ.³ñûUøƒcWöÁÈ—6Þo§6,ØOÇp-z¸zÄèþã¶Û�–a8óUy¨å{à<榋ûv(–†hѾ‹b­Û( Ì8®$·(o·¨Óå�áCbþ§`6JÜL|�²±a-Êö�ùn0S6³/¼×õÀ~"ó®£úåA†'Å.Ž˜ÞÒŠ†ŸêÎ!¡/Ys¥<Ñ‚FWV( LóMfÙ‹jØ BxŠ�êŠwjΧö¼6P>¬¾BWBAäÁñv/¦VPÛróÀŒÂÂ"s³`ÌJPÀƒüó&—˜jE "SxðßÊîeáòåü ‚zŒÎaµ2DÁ@n+ü\ó_³¿Š þ—]ÌÂÄB1B¹Õ¹ˆí,—d—ÜÞ$*+{<ï]ß|i~8ßGÊÖ;W� 'SïôöFÃ*£=8�²_â?l�ìï�ÛZX<~ÀuHÅuŽ™Ïû�-LBІ�›½müëh!°OÒ†!ŸžßÝÉ#¸LÏ„zge,Y 0¼víÔx÷ÿ}íåìã”fáñøûBa’dõ|‡÷î*²L cýçb%h"?Ã:ÓÆÙzÿUc]C륾íÚñ¸ý¯ù÷°žSuZ Ì$Ê|´Ù ìx<º×|ÉöŽÿû¿ìº:(«Æý:U—¯IOYúÜe­V%¨Ç¡­ß+2˜Ç]¹OÐ?ðÚ]4vP‘΄@Ey�è1ãè°Þ1˜¶ÏXæPÙôI¸ÖÛ�*™6”ÃñI;×K¡–ç€ÔËH½^FCJOi]¤gX¡±=Ùh�5‚{;¶g­#¿€çEÞøÑÅt+«=vc+®›F[£ gK¡Í_þû/d¡åLZi¶ñ±ö:XÏU\`\Å‘êUpùŸ#Ú9z0 tHì,í˜� .œ´ÆãÀXTtä°'*†J|ÇéQrÞÈ=·A tLã¢åA”žªÚfŠíã§8¡â%tk^4CYå˜À”=çòPºô”G©Q{äăÅÚ·AH‘ôþ>qüüø…£\úÌV€›o¸8ôçBdk�è«ÖôëXõȼTÛiŒôO’‰Þ üü�5BŠxý‘W}aŒtz¥´œl‚ îóÚe �\;ŒíÄSÝ(];ê™Á¥±8»ƒk‡Œï\}YZžÏsôd�™ާHR¤\F¦éßnwfˆ),œr]l¶Ð»ä¸Šž1Ÿûà²,œEuó"n*x7j;Ͱ_é¨]ì)œßF¬ð‹;©!Tp€Žã¬äÈöQ*ÜØðj棄3§,Bv§Û±VÜ,™ÉE`¦1ºµÞ>3«{úÃ4Ü2…B£î—EJrsQ'OZeÜã;½”²ý¹ æF5žÜú8¸ïPÜeÉxÂ�ýø3ö\˜ì¬xxÓýÔLà’+“–DÌ{–ŽþHø$2»›F:‡ÎlLÖo"HîøJ›­êÄ¿¦Ê¯±»¼{]X1Š2ðÿÂùA¦ÑhBžMh>VÓUÏáÛÂ7—8líŽÃp¬;¸Ux'�u´OzÉiÊ'Ë …o>ÍïÿÃ�@à;/HBud[T›¨j ˜hÔà@š`ðv°x²z}zXꚌïíÔG�¨ŸS¡»‹9”°Öii'\4ëC°óÛêÍ~þs—I@‹Ÿyj-=u‡àWðcÁYl_ zú‘É j’N�Ð9hš ‰BÜâ¯/¬û×HQøá}¡2IÞî�!‘]´îõôˆ"·;y¾ >W˜çÎÓ¯–•¾ùý¢-£Ü|7NúHy.ÃIi6Ì\bEè^íÜÑ{{±³+p'gù*âì†óPôðä–5 KTÞ¶Ã'âêQo‡6O`Oø<¸›ÆD�ž-¤„2Þ�NΊžÂs�4 ã�F „ãÞkkÎR«æ)ÛufþV~Ô9ûºn¸!e 2¾Î,lÒÐÙj­ÀõÊ­œ…62aïæ1ycõ_cÊàsHdêô%rG¯v’™¿g/woÌk[âHÈœë¦JÌ Ãv‰ºÊµf¶uíÒÈ6•Vc3±T³« —ÔæàÿM•á"Ìn»AàrïN;™!ÔNáiÄÝÂ"w‚emïâñÿÄ­€âÉw°Dº�#¯;�+yiX< ¢Tç}°ßuÔ„%!¿Ú1 š ɂшJžY›¡R¢"§Ó‚+¡t%1î{†¥¨Áº¢ ºÊRm­2Îàjn:)ÞrQh-£à…ÒñÐŽØ ´HƒC­uÆPƒ ¢X9MÄ7––O¼93„Ûò�ôÌ£$¨¯ �gGÈùRòV4ʶ_ ¢!PàJƒqﬠwoûØñ êO£¢´˜šl|¯ðÆÎªJÊâ3é•9¢ôê °9¬Ã×»MÝ7Ÿ¡˜@sú� lÝÞnŽ§Ñ½)ÇÛ”Œ�ýÛN¡¶9©‡U†oþóÌd Õ ^ù°ØìE�ÉÃM¥L#ËQŸ2AQù5ô+ÒBÝù¯�Â3�ž¼Ë¦Î¦V=‹vÀõz¾u9ÃÊg0ºõåÛ÷Õ� Ÿ!cÙ›Òû²nûßt¥Ë?4^pFÌÏÆ¨HW›´32Hí �hš9÷Ð|òË(ÕüÿEÐ�"1z^üCëÉí(‚Á79‹¸™x(”¶¸)T@Ûb"ð˘Ö‡Žeá°T~=¢E‡� gçÑf¡Å*IÏE±y£ 1BW¸ti«ê:MÂèø/—;ïç‚ ý¿[�툧uE Ý®×ô¡$tM½®�4óðKÖêŒ/†ÄÚ�'t�Œs÷ÎC�H¦ÐT) ¯�ôXZך'1á*Úo‘Ë0.’â/ôä—�ÄgHG‘ß¾Gíÿètû¡Ð{¿Î_gÄë}’4’0Ì¥´"ÀÔÐÇ‚:NÒYp:x1蟸ÈÅQ1.Ñ_q“©ÅV0Z¬Ó-ÐÎÓù…”0€¥V<Qî“x‡Z�Ž£Ú‰1¬ç¾È/†îƒ‹°ôš3¤ÃgW€12n‡ˆ}u”ªn æS×_N¹Ê0r,C3š„ÏH‚’¬*«b5G®0JÝZ×VI1‡Z]ˆùHË»ÿ>ïa´o©€@Ɇø`xG¾@ˆÐ¿:¶Uat€tWX�«õô÷Ž“h¯žKßÛa¬w«&õØ nëÔ(“u± a"ß¼œâž,ˆ[롆™5ºùBÝbâÔ¯¿þ!qŒOe4\\0ä“×ÄTMMðIï@=îJÉÜ"õÞ–&âÀ4žoí.êôM¿ß*_Êjß-âäÅ4á4`›Q”‹ÈCÃC!À=Çð!Â4] J€I�6ihõ_o´0ÃÜO^Ã?öXWÿã»*Lpéöóà†<+d+‹y‡]ñ{™$Ñ?Ý�œ€E¥ùŒ£³mu^ðªG|#˜2WΊÅHËÍMø´)¥¦�æ"¸Y¼ßì‹eõÔ²”…‘È¡óóœ¦Ö ‚åŠZÌ| ü^û.ûsÍWN³‰Uö‚G}­“)uØ{±z÷gÎ( k�x‚�Ÿ.ÎÆƒo)òg»©Yë’6r{Ë·õ C/Æ%w“Æ´ë©¥ÙJ¦9nÈW?Fíw¿zâ�#‡bv©ö§ J¥Å[&a{4­]n­ÙÎæª«SaAŽ_\Þ ›Â½Y‡iΔT½W…0–HŸ� ›S#oè¡K7@�/ÊÍ"Š30‹[[—Ð|Sº®|1s Ê[§?•4J;Yr DÁŒ.§Ña¼t,»ˆ1o; îxéï~oXÈHÀ»bÚ>Û»–[z"ú IºØE{†)aõ©ë{¤d¦H„°•ÑWN¡±H‡ý?ìy>Ó}BñÁ3³‘¡qg:{™&ÐTV¦¬ÍðK=‰ù·�êcBè†lDì;4>Ë:yÝ” ~�YT Ùy)Ã\‚;$볬Lר_c�gØ’ƒ…ø@Øõ'tïH%¯yÄœ$´vp¤,mìÖ“^œÝÕ°ËlâÕD½ìݳð!œJùš¯]¥ÈÃ3dæ5$IÆ©(œ=[Ø©ÈuG¸€ŽÄ+…Í'uœC3èV{]Vç›-¬;8Eplif\úKýø? Ï·ÅEöNÉ©d\²Â6Ž?y·¦a¤Ã¥Ñ™’[¸LÃ+÷º•ÏG×ú¬KB`_¸†¨~™ôöñOvEe^moÜ ¸¶é˜å�dlw�x[}M &¸!áÜûwa‰ÇûÚM_¿S)¡¸ Û‘"žÛAÐ%9Wã æP´5¸¹‰£¼ ¤f„'ü¿ùã¨1Åûþ¸õXšìŽh28$ vq\„Ûhðè<5ÛáÎÙzŽqR: }¿öG=ñò?ë‚bÃ5—£?Úÿ­JŸ`µ\á-ÀóÜ™CóiàžšôÜqõàp7ƒàmf**2yÅÀ°q¯,¥žþæd`â¾ÓÞ�ÖÃ�Ú.#%›<óuá}">µ`ó å[i&Z°kÖ.õ@âãžÝ�”f.zÙT)Ã>?ùoÙ¾zà/êŽD©æZ3 _Ðd, -<âïzòŽð/üv's 6ü ØÉìë}$*µšSy·,ç–»Tü¨ì8‰ �=ônZ†co-!‘('ÐáÿG ®HpwX\òŠ+"_›Ô}$O »ì„¶y…‹aއ'6–Mº�Ip‘ñX̽[ª?¾fï×ÔEÊõíÓGÈs|@×¹žg·ýx‰êdÓUÎLèó@]S©p@|Õw D^�x�I̾ä¹üµ~Nã??IÎñéˆjF˜™¥°Ñ²á\o¤?ž«ñÜwñJ8ýƲY‹èÙ#…¨Î17SSM¸iè–-�Æ=9ø¨ÈNÐÏ×tC¸>ªŸÒ@ÿøL•°Í±Ø›×·@xžÖŒÁæ–4ÀÇbæE·Nø”¢@+Ç¡¥ìltZÑ¢JãqLG“lyòj¥½âˆ�£ÁÇ3ŽCd8{£yµ )ûÕw1º¥ÍÁ¹ÿLdV)à¯h,VÏÌå‹y"¿ÿ ÷F”.],m¸ôú¥ìó{e-äýJÔ‰{%ó� ðÏ›ý”QB¦$kÑP©@ôZlß[rui‰ÌçÎïÇ.Ñ|ÂÜu׈À2Qj[ݾÿHF=ÑéÅà²uÅQ³>s)û6s“ ±€\9änSäº�&N&±n©ôþˆ¿Ç'èÓ{"¶ÍDºÚ±I”8»•ô¨íŸ�š°ë‘ó´üHðœGÿL øù i#iØâ]²4“�úÙ@ü¢Ý.°ît ·ãñ?¤ ‘è “Tø»t÷ÇÔÄψçyxš‘ØMˆYcZ‡¾W¦AT2[€‘4ž ðm•ðù‚”¥Œ 6¹û¯“ôN˜2�¾5±.^¶³uåà€;äé7¸ìïȆ8!o! ºUí}Áª»®ºnôˆ SÐ?9*×â¼æ`�ƒP”cyéðQ{ªQ7!%;ƒŒ]ÓµÑ%Ät q ! ªéû¼Z-÷>©Í~'Í·œ¥;jª÷ jªa¨ÑM-xæï½ÿž ‡\¨a óypùäm]�}îÆËSåû©1è `D¶Dñ`Ptêux¨Åú¦¯“‘ÝC;CŽŽ}dxÔXÄ© ‘Š Re,^ïïUÐF(†£1�©KqüB¸ÑÚèñ!êGÜt,䩇éxà6Б·¹Wˆ½$^$:…¨ç�mõîáõ×°׺qЉ©O½ä \³D<á._4x'Ûܲ®¨ïΟpW!sŒÐ9x‚BÂi L“süRÛݬí9^ …Øq%§ŸhlÓFãý,´Žêc˜níF¡Ó_h|ÍqQ$#E$ âø[pFíÉôhV™þ$BÁfMeÀ�CfÆhJ¬Óâ,âºõ÷Ÿï𼆠pc<|uÏßÐF„_xÆ7)ctÉ n™GK5!чk¦ ƒ%FÇ9¹Wøõ¸ðоd¨ïZ"´IÐHWF®—+IvѾ @âñ¿>qSŒ� v“ª®ÐAcr‘J°¹£'°ðùýM‰f¥“uƒ|Ý_N¸Â4­rÐÈg Æ Ëm`Biè»ý~�¬ØÝÙ�Ÿ²Zk¦+ÉÑ(Ç)ÝÛ�¥ç,/—H§«Ó¸ù‡\·*c¶6+«‚ÔyE׌ Ö¹Æ{Í݉[àÔÀV´¬G·t”üÀõÂ!,‚�‹8³øÇ•nK\Œ”W‰Gë×g¾£¸#Lô絊îä 8®ãžüZéÄjKâW1&1œîÞ€«} }e 'É&þÕ@ZŠçA�žë]Áy±tW×~Ë‚}õ¿tQúbR|pÄ4ÃCm·�Ãj ²%�"úî6²»Ù^ý?ÖÉ5£ª®×¬ 4¹30bk�ˆüﵡå'ÐW ¸Å-8 qk÷~—?$?Iµ‡ ú•îõDR Q�¡Ê6 aÄyY æ®'£øµ/s‘hd°DòøäZ|#T§å½0©r }køf“q‰BD|1ùÏXd›| _^#¹þÚ0dx²§�‘Õ¯”îPÅî<#‘6цŸHc‡rÃ…ûjû/»;c–µ¢R4$[¥dO¯æ<Ó ½VŒ‰"*òÒë� •{¬\@8³h,Ù‰kú+òh—ùŸ;A,ÅÌÁEU»¤Ø‰äÒÔ¿ ”Ò�;_˜Ã7‹–Ýá¤ÄY#ÉDíXþà%‰¤9̉•"Ô�O�õl¢²˜g¦ˆ­ üJD'¯÷iX "M%ÉR¢”OÌ;@.� A¥ëó´ÿŸ4Ëò‰„Dß` I.ÔŽx™ð~ÚËJ;2Ž«ï^™"*£‡“ÿ6©9˦\‚ê«Ò}ÐóK� �>4�s&Ëjü |· æØ´{%Võ”x¯Æ;+ºû'¾à+0/35ÚG¶û�áÚŒ +—×ÖøÒ:'" )[DÙ œà¬â‘7¡@™=aO÷�(0ÀÛ�±ó¬Š¿h)Ãÿýµ)µ'Õ¤Âõ9ÉcïaµjðS¨– œR¾Æ cî¾çõ`ÍDX´ ¤™ŠR ~;ì0n³¨d-‹ß~K¬oM¤±~�5JÕñkÆc½{žÁž X]¤T©ÚÎûã¸Ê0Áú TÏdó_Ú?`LË%nŸ6Y•G‡äù¤B¿ý(ò•ùÓA6‘v)»à°ŒX�ô÷q§?¨‚lª†çFt¢Z�&y÷Á§YnØ 6Kh<I¡»&ôº]·b((‡Ù~YÝ/}Uˆ°õHµÈ ³|ÉÙ1»,]�> �A-uÖñËíP–SØñ>d¤ƒößÉÐEÊÕ–ù“Æ•?õÙ½ÞHñ�m“žñó$°®ˆ,ÈÙwU… ºý=&Ú»»Ã½=˜³j_8`‘<Ük!”'4ïþ2};^« #ÈpxÖ!­äý>³Ž:\–Ô‡3zL s´N·ž„Œ¢¡Aø™- µ¿ÇrÌ"òÑdaÓïd6{$n;ÃüWxÂq÷B8¦ì^±•ÅZ�?ç½™^rÌñm:KŬ\›´Õ|Bþó3šf´ÎÉ*nÆæ`²û—:Ì£CìÆ{MìˆS]úÍc`�j¸¦;ñxMض‡3 FN º?�¿Ø#­ ÖÝÞž1ê瑽4V_Ük­£ø‡X@F£œç¥`�F ÜßiÑZIšÔãØKêÑ.~•%7åc:亴e´&Žnáí5.“®…JÂSgÓ3f£>N¦ùf®Øl#œœKbÿi¦¡énfXU®aÔöù—þíÛ…‡�Lî,½C“Ò¹+Š“Sr{»êÜ$Ôð¨‰{T’þœJ½ èÆ:õÜ{XÈÓž.õÕQd™Ñ«!9žxhª¶ö)‹Ì+)‹ȱ™lÐyXGýÔ–»™~“%È¥sM²2sx i‡?x§cÖÁIÃé�¬x¬Ìdå´û.lË?yyÿ[%Û⩸l0!‘H¶)J“Žy²?Ù„á9SFªS8§)¡Ü€Vç&�~å¹”CSÅu%Y-[‹Ób|ɨæ¹Öp2ÿ¯[ºk‰|½=s[·Æ�IŒƒS6-äkÃ;‡÷þT¹ÿØž7²®��, Ñ[úyc)?bZš·ÜšvŸ‹ÿí䟆8×fÙüò "oϯà­K¡½L{‹˜t#HM €Õër–{†’–8j�‚ï{±!‡‡hJ¹8”2?äåàù>+9ègÌ9s=|÷?Þ&<Ÿ>þ|ýûü?yešc'¸T޳)wEm|#ì9ZÃc–ñøk:ÿØâäÒùÖZ–·ü!J.Îχ6PÎvÍçù þ _å©›"8¼5xNtä±�€ð“»ý4¥ë–&¹ù¸OgÆF*Û𳫜};$¢‹±ß`ÅÚ7%!xqó³÷'ýšB­Ór‡–Ð!ñÒn_ ò •;S,¨Š0!6ûÿ•pƒûŠŽ�JéQÞkòƒoZ!>Ý|+B3Jh±K>’cüoç9Ô¯5ÎKv¥bÛ§ºˆ­„x Ü™þÅzuÒŒf‡¾HëºÿAJŠƒ¼Îq4 Û½¾«»h¯ÆÝ·6õ²]¨ûïuk”´€&b AQ?f+›‡z0?*Í­@„™^Ër• 8þpÞX +F|ö¼@’AíÂG�{â|æum8n’­@®%tä° œàù‘Íö¤ªé¶j”à„ V…›Ò4�Ô{�ÚŒ Ë4@ëD�ŒæããúO'£RNƒ[agöÿJÞÿÁø g®ƒ O�Ì—vüx¨ÇDl+yÍ?˜Õ�Üì¢Úн�†d,M†I¶`ò£b]i›DGòì" oQÒÕÔ—·¸R@ÜRYšxž{L1Ÿ>M•nQ2>ªTF×Ǫî¢�.š»1‡c²;œ¤ªÍ­ü%$ºÊ…”–&r"´FI{(޼ÔÉäéKqõBVír?…;!u ny"E�ö"¬I~ �¾yuÝ}ˆäh ù{�M—ŠÙ#ï8:�üîõ-ÕGfÔ’*ðœröËýH 1›Ü¦^Õ~’û+T©ïµ½*álu~ó¯†[EPh¿6¨Ûµk˜Ây¡ÚâúYÖÖ^˜a訴L8«¿H�ðèï±\SÆ�°ðvÐqY{¶cCŠC.Ãp©ÕÕ˜°LW–ð:Ñn(»yƒÞ7h7žpøBhâZê—‡:Þ�á'JŸp ²7^ 1ž÷_ëbmù…±§+Dáþî™?>:QÑù"}nJCÌ-Ýn#û¿ÒiTš,‡*"ßB›ÇÇ.Ì[„º§5�™$F}¢H�eŸžI1L†³�M\ÛC4ç:1¢Ø¡pk ʼ‡ û¥ºWz3-C¢d;`gˆ:7‘¶1N5¾¿2>¾Æ ¢T‘�¬ý[0JVâP^­ïÝas’¢6á�­’#¦Ò—ˆ}c9È ïæý¢¡74““½„»£Ú <©ª�:ö¢�¢B Eßí×Ö2åT®ƒqó—NbXx¤6ÜË8²RøÃ¹³tƒ©¾®bT»­1ùË“$iÆc¿,î/O7Ú_bLEÄ¡#ðž|£`qiÅ{úLR=b‘žÞX%éq(”Ò↡û;Èbé¦r%9Mʘ¹ &ÿD[Q ÅZJ0ïUA�'äªëâj$'wá,ĺˆi�&¢»§(•»¥Tþ2‹‹ítûÅÇŠæÈ(i­¾ìp¸²×]Š+ÉzÅ£.ëa3�±Úó×]Ñ\‹»¬µ°ÄwÔv�±iQ{ˆ+‹‹<œ‰GSâÇ ûqŽÊÿWV,A»j+^òÆQ×¹‰Áƒ:ƒ€[pöúï·ùD¼“-9ÉwÞ7�¦:¨@�0‘]ü3�+Ñê5¦Œ ®M!Ÿäìu#}\‚u;àP‚ŒÌX߇‡Ô9 ï”cI‚kŒ Ø÷ÃÙëïF®©õ2ú?õ‰(”êz}5‰ÓaU{'" ðÏ5A[‰Â,¡]õþpý)Œ’Ì”^æx©¯]‰f]|ãnlœ<1 .¼¶l½s‰Ñ¹Ò¤:Ňvè"i±GÿµA‰µ¶�nSôØèf³Y#/Í„®3‰§C7Ç,þ;Ê\³§µUso¿Û‰¯ÉU‘¦¸«6Þ£XŒpíÐNêxJI`ënksŽt-uÚÊŠˆ6ø7IH0·\ŒïÅ0Æyd¨œkÍJ”WIäE§Û�1âhy½ÚYÐÍmIˆ[I¦VW�¼©¸Ñïö^»û?ßG’\O�dßMM žè—vµï�i’úû<êBY&í—]8n >Äó’u”,TÛP¥3†~q< Ð4ôò’ƒ„ Wó8þ¦»–Âyµ´8÷I®ê�¹<Ë*(ò(Åò8vò=xJÞØ9@k(Ç `…¾ZÞ ki’[H~3 *E¥Œ{�czj~#ÐÃ€Š¸Irå…º.>£’™y!IÁ�"0ÇDú›úq|ª¥þu‡{¼’âÅTÏ¡PëΟù›2D™ÅHK#¥ìsSå'Hó–}6CýHQË&¥WU'œk¨ Òñ�ÄÄš�®*å€$²Æúñõ/Oà" ó™\Ês{ì¿ê… ÞOò‡ìÕ’�Þ!D©˜Ó{ôPQ‡~Û,�°‡ÙOjD©¤V]6+¢]$ÌvZ쨛cMà|©ž7†¡m•i1Cje…þ ôXi4‘wßþ˜ï�ðz¢&yÛkÎ8iôdÏQBz\Ås¢¥)1›B®Ki&뛽›€¿¸7@’­Ç㉸Òz·#{éAíúaiÒ"µ—;ÎÒQ}ñHÝ¢#Ÿ£¬!?“~ô`=<ÒQS»W„¤ÞðÁ ˆOØà±âô2h<½þå´vÊEE °G° jyV˜2šë‚Š†Ñ´ ;¸Äc� bªàö2¦ éš) Œ?n¶ 8Ÿ{£ ór2¡™”lîë\H`* bBò,…2Ù~êê£Ã‚ÊNû†Û­š$ vØe¡IqÜgÇö“ݘhÚ`-²Ûfe¡û"oÛ¨£î8.£¤®ªë‹dÅfH›Ø}®£67†Åo¥‹r´W.dc£Xú/po¢£�=ô$*Ó€¬ëLåx»Y™Þ#¬ŸÝÓMˆé®ˆË'`åLž•×q�Ïšc ÖçÌ—³h¾åLřӾ‰%áwi�~VjIåÌÎõa[îк½îTÞÑMï©å2¹�©E^�±ñ%{”›Ð:Çp;¿å*#ŒJkóþ9UÔÖqztñ̾#ɓ׬½×¸ª0ßnÂZÐ]�šË+ †Ë�á)ùý=<”àcà–W‘צI$V¾]ÝCIÃJ5bÔt“�A\Ÿ4‡T qÀLèg?’¿WAÅrf_14,ò›Ï7!�ÛòQ€· •ç`)'Zëå_Ë{dmï\”W0·ÔißA*Úfy�þ¾Y'èE×UpIU÷Ë ý©Î^L_<¬S/ õÛTðÍ3h=ËòSOÛ¸gÅÀÁºµˆSˆÞ Üëñis™©{4tcs’³U(ÁÓ¬RÃÈòî¨QµZ†•^¹S„Ùš;!¨Ú³?¤=±ÆPKáD‘õTDUGšgÌuyÇ%Ó½ëG õV´«¾„jŸ›?«o¼+’ª~ȤWLâÞMæSEöÔ;³cär´,µP “ES9‚?V¯‡­Ív}|6Q¢Ž+ÖÛà¡Þ8£ïIÀÕ*pý©D3�¡X�îãÄ,¶ÁàÕ\(•Ð©Ä Ò­¸®“Sÿõðî'‚uDëD¡¤ÒË®›­"fjX”ÆZîêõHÀ©dÙŸˆÌ§ùš°ülÖWâgŽÖb¥ä¸ÌB^¼±¾ç57~'#>—U ¡ôon˜Ý©' yXÔ£AŒû† ZU©NvYI$ÒžÕk™¾©%-ãþViÝÝý)oi\©¬8¹mã^2­Ù[™é��§ÜøôÞg Îô'ƒRYe?ŒFeÅX¬~…jABÿÖF9bí"ªÎëds ¦ò€Z‘Kh‘]ù’1¾@"EÄ#�tï‘,…¨7.ûQùkqbÌníåŠXY¢5 :b òJJ/ {¬ÌH™ÂÕ¤¡M‚Eu$1¸™x¸ñÅ­£zÑ&­¤‘UWÁrd¯PÃI^óq ôéyO…P…‚ æk}sKXâ¡¥A¥K… ¥ç_®�NC.¢×‰ü|)B… Óà Ë>µÛÅëƒ3ú¸zVVE]þ0G¾(¨â4~u¹4Ê[YEÜáa°][+/æU*IœìØSåÈ÷ÄE†K+ ÈcyŸRÊN¥×Bå�VêGùj檋½ÜëåT!³\åq÷¼Y“/gIÓÐŽ,ãá|Ç_•†Qâ¨ü·_»¹€?„C8 Y×XUZ§B[—°aSŇý¤NLQ§ �XRïrÈ|Ig #·3#,éWwUüuŒIúç *Ä&roÕsÕ¡lB¤Frº|V�­)wtîAë£Çvôͼ[ñí� \•Ï:}¦ž„�Ó´cK9lsN¦çþ6Š:¨ž$øÛöÀ’×^m¦…6~E§>w2‚¦øBz3éê�ªž›ügKýÕ+å͸ÙÞ™ ³Yã®e ö\Ç´É𨀰é›…öh²Tƒ¨ó·€ÓUäjbôsP–½¸I±FACgK� x«çî×Ì™dlØÕ C£yWÁX7ˆlÂ¥n›u2•WÄŽôŽƒVGœ´²wÏ@ç‘£’ÈüA¥å“Œ ‘èÊ—’¼ w‡<Ùž?Âl40ɪ[¥B[ª X×BÙ¡L8ìºb9HÎ)�¡è©þÃÙûf87: ÔP+Øg³MYý©�"¶‚.µ&®ò$ƨþVµy'ùSµéGÊ0(SaÁÈMá£MÚý§nòâo¿ÁÁ(¤…z,:œ<ó ëŸv�d¤~˜ÜM?Bå/ ¦?¯IXœ[P�ïò3q?y>„êõ5Ûà)©·Ãy X1ÔB�Ê;ˆìp5‡Q¿ò/Ìâtt~ßA)3BÒ˜µ„ü9*3hK=�€¶ç��´�Ÿ4´L Ó®áP T)š¿ÖµÖ" ‹v´ìK�#ªRÜFh'o ÛàúÍ™´zr7-ŠB5½ ’̳ÛG´FLb¯érßl¯�2u´V8©îså ¬Oŵ"�Çɵi‰µ‰²SÿãÄ®óÉë_ ò.24Ü×þÖs"à§Ór�¿LsÂLm³¦�mžõæÞ~3€ ÂhF_aÌ&¸«=•|!]ä~îJ!j£MÕƒÙ¬p1¢ ¤mè<ŒR¾ù~¾…³³¢ò ¦ã9Å  ö™9™/$²t¯â¸«ScCN™µ­¢LÖjakI¤Ù‰¸¬Ó>ßl\Û&O„Ÿý•^é&ïí(b¦KàAÝ™ÔÔL[/ÆIÒvÜÞA§ëgºköЕÇpU_–DËÜ6Y Ûœ"D¡7ø6MŽ 9¡ëÑò£»¹|ý&*c$�‰“�l¢ëÏ´¥»ý:¦ö>vÄý_E¦}H_úîèµ°îYvw4 \ÔÓ—hRôqGÛÀ¨îs̆º{*óhäÍ %Ô~K²pŠ:¦í¿(ØJ{¥çWcƒè †ø$=÷’.”ù­uIåCÛ_¬Ž­ïœ±½@¢'›ÂS ÚŽ¿ºÑ�PFt&èˆz)Çl´uŒ.dõÍi�÷pTHzé«é]ã†|ƒ2PœØƒîË#Öí…z¹„$S&‹E¸œ‡sc¦$‘ŽCÂúz�ÔÕ“Ë»ßÓï^ú¤¾âwô~ܼ…׫s}ÛfԈߘ†§õ‘j@–´AáîÈõßPWg}Õ\ôÙ ”±+ŒÃâÊìžê³FôÿÑXÔhÏT££cdZ|¯ïÝqé÷Y�q�¿ûÀ‚ÁɑȔÛð°#êO†Êö>¤Æª·{Ð�1Ã04�èo9š]ŒDÁB“Šf…†æ= ôP þ–Ú=¯©7 �Ò‰Õ—òxOÑj`m~8ЄÄ×Xоw^ŸbÜR#¨’uhWЋÍÞ‘Ad»ø �î8"»(ƒª‘�aÊÏÛ�é/g\àÂÖt_i‡£ÊG¹È&q· İ~˜[U,*1Óõù‚Lû%û4´(†5œ¦XÑàêtiCý;Cƒ5¸Ð [÷l¥Rýa�’?Á±˜ðä†2xr‚�}šì†~}¯Žîvk �k3DK–x "ÐmoMÏÿA¬¶3Þ7Ô¹U¯õ(ynÌUdõ^9>îwÉ44…ŽO2z"Ÿ¤CRŸ¬�‚Ó”%3L� +h°ÝÖ³§šúäÂM�Uá0ì­€‰Ä6;dÊ^�@‡¬#11 ›a[ w§‡la¸: LnWÞ6Âï1˜k&Ͳ)|€ø9¶pc±Õ1%Z~sŸÄÄý޾ñk‘5’6UQ•�ÖRìÊ&ä…?ê Í22Ÿ¦H_@ XXá‘0¯.œß%1rA=�ÜÛœnaà$(ý+­Œ3a VUØœ…?×é•€ØËF5ëb�6㓽”Ò,8ZÙíç¦i”õ�ô…â—æ×ñ¬ Ä_ Ñz9£*˜÷jµxdÖßGƒÞûT†,4£ 4í1®qeŒ&;Ÿ]ä<êá¼F7°)Á5Ø—÷Q ü\¥…wÆ�!Æq+‹º’ļ…¡ž”n[ËÆ2&õ0F¾&÷ÅœðPz×°"-ׯ $ ,ì×són/¹>?Ò Æiª¬ÙÕýÇî¤Ð"®µ‹Í^ÛÖF&ˆ�ßRÈyx"˜gx&²T‡¨F–&0Á˜VM·Í¦-»O˜ºí&á,Ò‹“÷›â¤ëÚWwïo'R:&ÅÞÀ"VÓ­ÇéóXËÿu¦™4/}žvFö•lîÞl7'±(˜Œ'í_ÿ•¦ýÞþ(?~ÍÈÒæV1%™£qŠU1!Ÿ@÷ Öž´/3%[æ…«NDO¥·®s¯]u‚1e­Ü"¦RY›ûÃ^…ZxobªË…ƒ-åÎïCñ›;|ûßµuÏ'µ©©¬yJž-7yA9b.·¥©÷š¯¼ìpörJIÝÚ‘¦¡›ËÁyõy’Ø»Æ×YÅ1Ú¦ ôWt¬×x‡D»Š¡±H�û¦7ÌÝÒÜ<âÃßèIUù‹,cÔ/f�õ#Ÿì-Ïú$ý88Š0ÈâfH‘Æ.uE åiÛ³bªr¶"’fØ@|ï1éÿ­�ý‘9@ß¿,�2#+:�¹ËDù·ÞÜÍ ýUâ/Œ`FcùÚF·Mf˜§ÆÎ)$Ž sh–¿¸çß®Ô ‚+|$ÖHôk³nVÜû/£ÏËcUXë:NÓ鮸ˆÃ¬&Â+/ÿ¶}(M÷ ‡Ã&pžÜlÆÍÐ�â>‡ZO}Öç�iLŠE]¼‰)éoàÒüšÛ5ïPéÍ_&ªÁ_ó]¬¥°…Ø]ÁîÔ?(¯× 6ÔC‰ì\Ý.#ÕVäñýÿÊ— Q,dðùÅ&»B¹ —Ö™”PáÌSÔ ÚH܇>š{¾gö†¯Ë9:ÅŠú=:цùòMvåö�òþdR«éòÙŽ@ÎFj^礼mJXï_;6�–WÒàV²M‰ôz™:U¿ƒÀÊœ…;Ed¸Í.˜øüAèwk‘hÉî46§™!‹-4jÆ»UZGÖóã­(fv`+Š-ÛÞ^ vÔ’À€R }, ¦­‰•üæbßG ,6O9¯Tc”Tpœ­n)·¡y°J€£ŸÌ9hÄ[ðŽ”mlR—hâ„XOjÒ˜Òn¾l²e­m휺yh1æì>*NX½Ñ×hºmŸš¸§ƒM°jã�„x‘òÏK‘b¾öŸ~¡b†ÝQ!áCDr7Z:;ìµ<¶/5Œç>t‘* ¥^¿ªy;Zfðþá�ÀÖòy¥•l%w:»Ìñ1=Tê�.¬"8MTxml»œW䜤Ž$NGûQ‰þ»Á`ú*&ÚŽYKOãª*û­ié'‘2"ÑŒ‰,á8`g’Í�5ûSõ¸ÁP¥A×dOEÍ;uxιNíÊ8kž. þHøŒÑ˜=÷†N ©ôSYýÜøê÷,®…„ÑœÎ$K|‘Wwֆɲq+ü.¡þÎ쿃„Èp§^… Žé¤ØÄ[õ^Î’œXÔˆ{d!©?&yD‘ölÎJ'¬žôÉ àÁú£QùÛc¹%ÎÆ÷Ä—2ø¯Á»ïÎ�Ä‚ ÎY_˜é�iêN}Òx³q»WµV’Î�4¯K ƒ‚œV ’ÆÎT$Ðsœ‡¶Uß–øDíÇbM -‘žîÛnœ·Â쯪27+®²ú¥¦:š˜kQþ*à¹~%s]G'{–{]!u´ÿuµ_'`†‡ùøµLß|„ô ü›™°*Òªª‡ó×êÄ!HÉA�Rêïè�"�©��• �\«¹4d6ÂîßÛGÁ¨ÿ3âus™zú¢¾VÛjR-E—,Yš¥[$–1ß«9[c{IÇá©7“Ù QœÖ™Nht—å`&µt!¯£a~‘1½ ¾åè4>—uÇ ¤ƒ¹“yî\W¸Uîv—ý3yd¢ ¾�ÛÑS“°(ïÕ—“u­°ö‘ {Yõϯ̓c_—gá{èÕ …ô²¿“yvî ©šÒ®àÔkΧ$@W+³à˜yù|§~û®ü+ºÞLr¼åñú�zF]ýˆ&Â¿ê ½®—r„¹‚ôoG³\ˇ$)…¬\”‘hwN ð °]—!\¦Ê3#Œø‰ÐFwü4Õõ]’"å ‚íU”yEàW¢&c (ÝàOªza €î.C;è6¾‹…ÝôdåPRÏ‘i8&²ßÙ­P¡5Ý:ÖÔ_˜ìñPDg8ØÜˆtù:Ýú~Òý†Š¤DG]Ñ[ž(«ÜÓ Ä\“‡ÏŸGç2'GÃÕaXèÝηÊRâžóñç}NŽ¿‘] FÜ©>Ñ8©£z™7Lÿ`º˜é]�O»³h,¶QæŒÐcË^ðú1áD$)»‹sOZ€Ï½H³úæ75À׸Ë"5Õ¹”"D;ð†—‰!–]òºë ž�njíБˆüLK ÒøÉç¹§P~Š;©MÅ7´æT©µ8�޶-¸Wmš"&N²•E¯ÿbæ£>mô½»¿'²­�üZëDƒ!½hf²ex€Æ5.)m{q²”a>¸Lú¶îy`QL‡&ÛF^cOíú¸0m‘þÑõ Áo�eÀÛÞ¸“(Ÿhê…}xùôÐ+[Ýw ȨÌ�˜e/Ñå$Ù³ñ°yŸË·N¤qF�³ Xî]öˆÁv°í…¢4Ûe_1[‚űÐòñ˜`ØZ:-B,â£sÐŽ-Y}ö¤õXì¡÷€ Ju�V£fèÁ±§±ø˜u¸÷ ‚æu7uŒeüÁ MÉжÚY™÷1’qlÓƒžíß²ðsá� ”ïû-B7÷Œ,^Þ0ƒ0ÇKFï³Ù`„LùP 88T÷¸V1c³?o>:NÄu8�xéPh:ÓZì¶#~”>KV/­ýn�\ƒaz'«™î[H®>k¯H*S(oÛÙW µ½P»®¾ ÁŸ�K&'¸K;g+ 8_SV¾bÃ~Ý}7l1äa – OMs¾îcËUe2p!«¾Ïñ¥"Ö�¾ÞÜœÉ#}Ræ ¡ßò’`b¤ L¾�ŒÄ¤+ð£ÀJ2j(ÿI%<Ö€ûAšùÈ©À }[«3÷c÷ÒŸ&œûAKØ”£Ãxµ_¦Qãe4 mMù!£RY’âýgØŽÜ÷Ÿ†Þ¯,®Ê �2÷ø�À²¿Ø4 ?™®Ÿî¢›¥š_ÛŒ]Ý}W·B»â”Ÿ#h›¦y¡�ÐoʰðUíßNM?çÉiܳ�§9ãíVÚN«q¿H|UùøëШR6^ðÀ?Û#ò~õzÓ ¦‡-�µ„ä=@d}=”Õþ¨üÕ‘-k=£à0n+ ÃÓ!(þ˜öã©n©Þ…EI&ÊìIþäsŽà ²cÑñѹF×�ßæé)þL$Ìîw]°å<â°>è!;2[9þ<ÖòÂѲô�{IË­ 'I5¯Òýs-Oí'of Ì“?µ/Õ 1xÉŒí÷.ß+ßk»ª‰•ã�œ4Þ�½Öw´Úµ#>³KÌ‚HžL}NHÓNiŸœ¨Gó ý@É·Û~œøH.ö49ÿþû¥AÁñ(ó×< &½ÏL5¸Æîåµ1FŽíß{s@UaÓW¼«Ø@ûåÔí‡ÜþOÀb•)�Õ£” ÓuR]Äu{Æé¼P ÈÔmt¼$Â裲„b"Êû„r ‹LÉ•DºÛµ®¿w˜+ ÆuN`ý&/û¡\X(±S¯ $øÐ¿ò©ôÀ®rcÁÙŽŸä ¿ymjÝI‡ÖSA¨„Gƒ^]æ&7ÆÎ¤èñÖ—A<ç(gËÝÆÐ…—U ^Ùg*ª<ƒ§Ù™�ûRùõ…p;/š_öÈâ ƒ¢fšÚ|­LÁStâ¿Ç e‹:‚2FΊ¿Î.Á¤D2—5.N}èÝ«‚v˜%ø@ ¹îWmè° ¹‰¦yƒ^O;aÂ�e†fÙØ …£õ©üƒ¾¿üoD¶\…4Äÿý-GôòÌ› Flk�D…ȦӷĽdÍâ§6×éjé'æ#0£ùG3ÌÈ«ö.à¯O¨Û´F4S�ièͱ9ÿ\Bê}*ø4ýÖ"¶ý‘;³)¾Ü>3J7sÁªÊ¢'rëÞøz:šüZmøÈÍ=íu'S'7^ã}µv÷¤$‰-£ñMØÂyWÀˆÅUÍB݉3lâ�z_«�$ȶÃ�Aüb€ùù"frèá!!�²ú_jßò!å¢S¨ÆIôÑËógþdíì~ðjN¡Àjò°¥y®XÙ(ö·0µ¸…¡°c»K¬XSà¨ç1©»Îî-�Þ¡2/s8¿þeödx—�£ò9O¹l¡ÿ�%,qöþKÞŒ?ê ‹Eøgµü¼i™ÏS è†�!.B�èÆZu²ÑŠFzef\¼r~\@‚�ðížÈÜuOÊ8r· È…~ÝúHô`êŠ'Í5ñþ¨Öqƒh�ý%¶�;1ݸÚI±æÚñä…s ƒò�°�ÌèLGŠçáà¾1ìµ#)ó ®„󊋳Áól©ù­ £Y‹1h¹æÐ^Q„gX„% +Ȩ͇ޓ4èRÜÂÖ+Ô$¹ {}tJý^y3෸ל®ZR†‡©È;ñÞæh_P�@W×#D‡_ßWâP,¤ÙФkÞxG=u™íç^®£}ÄðÐ~s´a½=Pl› ÔÛ›úoÜÖY"3ÅZÉ«7:¼3îÀåEî�oc*Üo¾æB«}køà()"¿´¿3énðÖ‘~[ç_áS?,måâZ>v©fÒr¬£\Jᛲ@ZŠ\A÷”k¿ˆE¹elòÚì ©�¦hGz3»úèZ±ÒÞšö#ˆ}âp6¨1V�Ç#Nê$¦Û'"¨Ë2gÛ…å½ÚŸLÆçºƒ×"h’qå2öÃ;ðéKÁ·õíŸ"Ĉ!ÓiRãL¸ *ŽÆI¼ñ:"d4 ·‚(*ÜѪÉ@‹õ¼£Œ#Tt$ˆ t—üVSᆠ#k"Tb4þi/2Jû£Ù>a99Óþ‹Pm™ù -âN”×…»ô[&-�þÞŠˆ©¬Èe¾Ç4“ö—˜d+”�H›(”4…ôäãuØiO Hﺌ�hy!ô’çƒÞI¦LôŒ^Foê"ˆh5­;¿kVÌÓϯt“‡¬`ÿˆ˜Ø¦|x¶×IP¶­8XúŠXD‡ê»Öæ²CæËÈ"â$˜‰¸’A3LjUƒœMhEi è‰Ü‰„²£ám˜�õCàT(ˆÕé(ψ$ –’¯é¥7;Y8’]—ð~�äPeGt?<;'d@ûëc;!+ªi}¬¢H„A$6v© Ó]u{Ûéc¤¯êd?êÀßWŒjaKÐÿ_ø^dôâñcMw¯ê‰[½ëa1)Åsä6°j¢¸‰cˆÊìyPn:\XuøUQÇAE{RX…ÕW«0‚hwyE´Þ]¼:x‘YU”=PìÆBÜ•'ËשQ�A¬œDTŠG«�ƒŠE–?º�féÕc>KTY†Ø2ŠÅ¬)'½RôÓ\i¦|T­KÏ6"uW Á—·wŠ+~ÕŒžqÔ1Œ0½˜?{û_fÒˆóRdzɨ3õµ·±Ê¶’}#×xkr6¤µŒ}‡€IÆ*MÿB:€™bR·BBŒƒ“ýOP¶åëê‹_"ãˆj1)î_ò.»�ƒA£G‚ꌷHäK{¼L?-é1ÇdÕ5Ž$ÂM¤«¦Eñ�•™þÙ<®1a<Ù£Ë´Š¼�óíñK w_´�½®M¿PT$ÙØ1ñï¿æj¬°ú·/ƒk˜�7ì‰U` �tJ—Gï½Ú�APWï/jFn: ëμI¸’3l¿S� h?â×}XzM3î¿“[ær 0pf.J8Ÿÿ€¬&Ï‚`P&Žâ—Ð-¹â)Ä 7¯w¹Tg%_†êYë�Ú”VcT#1ÌpO°&¿~j¾*lB-º¨Óc¡™èöø}=¤ ‰é©*~²6Ã…©”�Søò7w¦,ª™L¢²†]�~)5ÿ1+¢%K…pŸÏYûo’_ŸTZ³Ô¿3Tý:®`Õ#¤•®œŠ“Oðì`”-•îq3%`Xóöö}ÒÑ!/U�oÌøwx�kGkð‰|×îþðð"U›±‹:íÌq ¯EвñŠÓË,Uíec,åÒØ½œÍLe(ÕžO#ZH?Qæ¿Í[ãòÌŠÔf¼ µí\f­š7‚²ÞxoÏÔ£]ÞM(�Ÿg·Ã‡ÅY§¦¢ÔG1Éc¨á¨ÿÄ2èÅIÖ9ÂÆ·ÇS_AˆãNº&¡Õ£äúCÿ� »¤É`þb¨R.iLò+±( �‹�«‘¥Ðc˜âšKCx®ûOUe;ê§yc&1i39¹�²7�JÜ6ŠÔ¦¥Awwyá%()sí×TåE9Šù_§uh9¦’k̺ð¾ç5 Ò%1-êé¤Ç¾äöPˆ@ì£9¿� ¼y§?6 ¹&×§ù.4ýüΜ`çÙ–þûÓ&A-^1~ŸcµæP{ñg/ƒ$ó]úË8±Hs9‡\ƒ5ÃãÄš!¬.ä‚tVŠ»™Ú8G-“a3Â;©l¾w ²² 7•mÛBñ=SÚ¹Ãú ¢òKª=ÔM„ñäXîÕ$3ЯZqû_«F�Ž]ãÙð#éo†Jf®V·¯Ý‘ýÊîs<|¹KŠdfiƒ½ô\0Ø7%Dø³wDNFæ)íŠ;¼Í⎙øÁ(ñ¿ÍeY˜\  «GS¦ G"ýòœ9TYìn×ù'�¨ó‘L’Æí¦Z»‹Y*O(°ÏRÔhÙ�·.¶JXŸŒYËÃwTÃàïnÞÆ!ÃÜY“Ù½mY›.ÉŽî¯ô� h+Öã]ãð/¢«Y»Úw�1Pû IÈ@CºøÐY¿;KÖ$ì„|(—: 0{àÇÞ]ñÙüšOtñÊ<–“N‹êW+_ƒÐÙõK™žÉï“.~Â2G†¡÷�mÜ9|Ü»kĈ•jQp#Ÿ"^ð�¢§9�§|œo®¢�1ßv¹Å%A¦Ï9;…@1¬a‘; Öp&5�·¶·ÒM9{eÀ=ݺ��5Î�›‚ÑÜ`ÝD¹Äpm�1<ëDòñ%ÁÐ1¢Cê%g¹tË0Íê&2[•¨=£½Ñ7¹L࡟ò]«e§š Q‹Ö5ÉÖ³†¹ÂAZ*T‘bëÕ [Ó°}¹’@¥ ü"’rþpÿlû µ/ëå\sã¯áÑq ²;zˆ-ýî`Dr[ÓL¤z-ä?…3^F ÒÓâÞYs/ß<Ð"B€"œ#SÜøùðÕ×róf€>$RÓyTúmÎÛ+òÖ„L©2æØ]±á:~`ö“ÙÝ)ó5´qÞ§"/î%ÂoH~$8 óKoʱü·Æƒ -\�•wIxÏõñò[pÕè >UƒLÃ`Ÿr’¬$pòû”i¢ê2ýƼ…U¢J™‚š„Ô¿ógç©}®�ç,á0|Ì�áö¬Íò×ÿ¥j`-jÛ×ÞÛÿ9Þw”ôúéECdçU-¼VÔìb£”±©]òÓ¤8EË˼**ô×Ïô9Å Ô;쟈>`4ó‚N+àBŒ[Äï×0ËÉÊpJŒY1U¬F,ÊöÍœ¬Òñê±äUþ‘6$,„wæÉ+Ë+išl7N7yg%¿¢,-ä½"]ƒÔŸÛeTùdÜ«)TÄ­/Ÿ7¡îÄ×Òge¸ ?u{+T—Ö×k 8Þ!~ô"©á“öb÷/ôk·¿lˆ¤D-7.c·¿Y-Ügj|.O‹êjˆ~þËïê4|�TZ”A f¦~ÕGèª:¼Ü¯ßoWŒ[Y%�°¾Ô›½Ýñ�Ïžë6·˜þ®hW€œl˜åýUëf$›;x°Xð…[œ?S�SnE­·ÙhaÍ+w»XÛŸ7j²í‡fîY“=å/)ºTq-ýj|© 1ÊI‚í\8îÏÁ$ pñ¿Õ ±† põÅÞ PÆ‹í#cñ›q­<�Š û%0e„På+åBc ¥|@â¿m²úÑíÉbJŠgáØâš2mõeè�Z™gQ¥&´,³ ê{¢”oE¢GÍS5:•÷ßÌè~mÜv¨F£ïÀ,ŽcÔ’¬¤Ÿp|Í èà)Æ€¦yz&ÛÈ’*Žùå’V~øg=mĽ pãRn ˜¨î7�fRò‰bšn4|³RåÎ"<¶Å â�i툸jê1ÖÒ‚Òä×¹ñˆÚ‰”%3t´éò±Ò²$4Ÿ¢Î]ô$P”ÈWêæ¹¡Î2–Q|¶i1…\ÐÐÖ4"¯~Œ ‘2Ve‡§ ã±·Àvzеë2®„ÕáÊú4¥ºûŒ"—\«ºK–2 ð=u“mÕHõ¢‡ÜrU’'Ø2ßÞ\ÑGS9:pfw=d—ÈieQ=~¾+¿*KU KŸo�rRe©(¢Z%õöoU§c¡ jÕ@eý�ÃÉh33HcØéY3‚(’OSe#p_¶<©–·:9�äŽYyL³e¯�âoúbj©(:5vì¾Ámríäe_¯³VÍ®<ÏE,`¨”XÄL¤ZMåò¨Ðè™äήuC£’ÅÏhåUÛú¬ƒÓ«:ÙFÔDÎóEÇeã ê?vx.ÿA;-Œ\Aþ[2® 2æY¤±Ÿ,÷vÊBÛ îÍO«)i>ßú0ÑÊ»?pÍ'ÿ�¶"]FÁ€ûOÙ,�¸Kœä&?±�PÅ*L˜±4ËZ·ë˜ù”î© mÅù.¯¥^B„ì‰ ‡=í”óoj%¢$¾êÅ¢Ù�[ÅW IîfSW• ©‚>«ˆ¡ÓÕÅÚºI;e'–R•°®æôÛ4ò†­JÀ褖x0lGU"óÊr+õ@>R!›SX縄Uê! 6­pXC%b³š×UÙY¹ÇªßÈï/˪ Ьޮ`kU¹X“¼† nú¸…~$ëÈßpÃUmEöL ö6ÏÆÙÀ |ý!ìUÏx AïÄéß31L£¥G4OÿÀº_ön¬›¨ŒE»VÅÑìê�!þ ›¢ƒíŽð¡É³ŸøÓ Ž®êýó–‡ÍŒ_±ÔPÆßÚžF øþçCŽ+ÍB(lÏÄMS>Í÷/�s?¦ ïÑåN¹Â¹cß¶“Qý_doaäþVý¯ìµ§“eµÉÙSè¿G£ b`"F}èñfüF•Êæ^ßjÆ…Ë̺¢DÙᥠ¨ðe¥®.øjï;¾êbãöÒ°Ãp Д±SÊê<Ÿ—®û–“¬#¹TÿàØ ŽÆR)£�§}‹ÔÚe^¢md,x .œæËVÈh؆-þ•4l=úx‹íÄ ôò*ëLÔÛôOAƒÛSJCU¤�KYŠdБyS>¯FÇëSîðÄKÚ»sµÏÌ5n�ÝV>ÓËÀ³šznåúw_±#J` h¸U!‰š³;c’žãÿ@K1ØV÷pÝjŠûkn³!< dq˜¤Ú°˜—nùs|ëk~ÌæÝP€-¿Ï˜TÅ\z®˜j©é2FÐbÜh™µðþž±2�^¬Öòf,÷°ÂwÎÀ&* –|‚'`CMÕªÍM_RÁãûw1®ë�p=XD²ln5¦ˆ‹²K<‘DG}j9d#éÃ߸/k|‘i(”9¹L¡úNüð88=lÊŽ¥¼2Ÿ:ö�ÑšúîEË•NÊ]Œk Ÿu[ ˆø‘vx¨‘VÂDvKÚù»­, Ì·<ӳܶúAoÉá=.Øj8 rO 檽Ǯ‚Û®ÜïÓQF�û >0þf߈ݻ³‘LM§nh^P˜ Iþ¸ùh}È5Ùw0æê®Rê ñM•BŌà 2B‘íä`¢5Ë ]ê~ä‡{>ð¶jõö¬"ü¡Ì� ;׿¢¡¤Ò ò/1Ž-n ý� Y7¥sƾ¨¸dg-+HàS„¦è�Ðñ�ÇHÊ}SIWÖ½Q>VTCŸ�H6‹2^– ‹� lè"¼ CÀ�˜¡”�¾rÖd+x ™‚þGô6Ä�¸,Ï$̆ˆ ÑtéûØIÕtižV âô†£ æ^«yþR¨›?5:l©ìäóðÓã£@7XâŸ�’6:êöÿ«þO3îÂrk<ñ÷õBá¤Ñ±O½ÓÒ„énwænù ×(Yñ‘´Ñu•ZÖÿôû?xÝþ47“ÆÈ¶¡‚ñC‰qÄwN¯Ã ñFÒÆtÑÇ#ºXê†R>¾“»VÆÆÂw„ÿºiÂçÙ¹�Í©“†A]¹{GÕé•w…uЪ^w}â"n÷ж¥�ÒYSÇ« ¯G{æ•Ïò«z=”Ñ&Ânõåá9ÚÁš0“Ê™Š‚Zrjµ-AîfOÿ71 :šCƒë{ÕjÏr‡ãËýkb†‹� 7"ÈòÃ5µtžjâ¶›.3 ¦µ3ºÆüJ:Åûhƒƒ½j»ƒîhÑæå‹ÇÊ–®^h’üÔ‚bõÌ`kûïmEÕòŽ�s É«œËß z)Íê}01Îæ\`Y¼©þ9ZæË€­ù=w{>Χýx¹™ Aé®»$‡jNÜò£]˜ú»YwÕ·,f[zýŠ�2¤±¿:çt¹Ùÿa#âÍü”0Gwí9’¨ã–µybÓí›ØêF·Ÿ˜XdÇÞ}iɯyŠì„t™Ü�s#ÔÈ+ŠY:Yg§y�†×u¤k…Š"Âé�êć´,|U]c‹æt2RXÅ¿Ûç¥eüˆˆ×²ÄõŸŽ‰pÙÁ‹ë¬AË3º‡?ª�ènWþQ ZAv§fË|ëržˆ4�L‰Ü��Cç˜bx˪$£zÛº}¤êIbë²ïzRËWY»ƒµÝzWÖfׂT¦yq+x´Yа~â—â·ÓßkáúÔ·VºBs¡QTGò¨yw¬Hþtn‚V…Û� ;w|“0v²W¢JßÐÖZòÖñ†'Žo+�ŽÞœ4yCä—ùÖº¹kú„1^ÍùVoþ.¶C;:˜Ö±„ê…ô‡5R¤µ™…)(Ô6ü5„èV‰l+ü+Á8vcȯÏ6“]#ÞˆjóÈð˜Yx¹ ?œÁ¶eô-�R㻃‘\Ò‘)‘"ãå•¶¯}Z ÷fkx¯£GÒ¦Ÿ[±�ÁvÒ‡wŸ»t<¬�¤ö¨åÓ&9^�všSç¥hýÞù‡¹¸flPÑß±v‰ûÒçï(4%ÄóÐù| è9D¬ö(è |…Mä†ï�ÄÀbBòýöb Æ »ErzY2„6Ù°½J˜ØO¦i榕®ËŸ¼4Å?íöaðtS�&ÜCÜßN}Ú8ÎM†ƒ\טòGsžz›B Ñ�´Õ4Œ6Ï[E‹è^È ïÓp0;ëÖ{êéé°SÈO›4:{Mê–ûÛ�6sþLìüº÷ã.£w.d>7%*÷И¤w7…wЂդv‘‚Q¥x¯¡øêÚó³Â= ljtÑ…ÙAçÊ ´6„yTu`a°ë¨v ±Hß\rÎȽò�˜#÷óébçv «®€Æž›I½û¹Ô>é�(¡¡tý‘mäøÛÃËM>ÚÙO}ÿÌó€eÙ¥Õ!úÒ/6Þ<"dÿ9cåw¥ ÖåÙ^ùF·ìwžŽì%¡­Ó-ܦ€lßt…*6PÃ]³Ø IÞ•²°”c™tt ƒšjløûÎG‰Ø•ϧ Tÿ� ú‹Šï@'¡‰÷nÐ5¦—û)})·¾‰—çÅ›¦øÙu”Që°Äv–�‰ ú÷¾ud›ƒx·žeTæÄvã+;L›s¿ûÛ>ºÇ¡”V|6‡LY|̺§ÛôÉ!­ô—rG?>¶™o¤îGªûäÛ³øúä5GøC{ŸÝ|ž4|4»û�UDyíD+#+õ+ã?¯Š\Fðà!�Ô®W›é ò¤<#Y›öž0»�¨‹nIVƒƒÍùÂ=ÿq½žJ­n5XJ›Ë"–��íÍxÖžE’,5½i¬.w²Á‹ÕNZ}E­^ð®Ž.æ-WÊ®iVUí)ëéØ^ƃ˜ó¬ê3þ´[A¢¯H»­½jO�+ë±ÊO eE¥•»˜\AÒ½‰DöQƼ᥶2 ˜nưŸÿzk£E.ÃEª 0ónBå>Ë}ù*z÷Ô]–Î^šs”’h¶Ãâïó­‘nzß PX!�Y¼?lNÎÑ6ƒŸñw{ßœÏCÖó®Uô=òÜ|fúðkœ”¡c<ï›4»æÜí– mûH'ÌŸ¢"u…�—yÑBId Òûèa1`oœ$²)òPwä(Ê. 'û4•aæ›´šƒâ÷DVwöÿI8pý×çVáySÉGC½ù‘Ê[›¦ÖçÌê)¿JiÉÜÔøÝ%$t.†™Ô—Fg‚¼‡$5 —Î�§T…my5Ý7�)ŽNûÖ��\ža5®b ÓÔ&·ƒÜXY­"×x!½ªByí«/ÕN{àðèñ/–ì×c„Jt,[? 76µ”ˆ± HR :ËäF…‹g?f —�뮜˜¢AœÜvM> k¿L�w›·IÑ"ñ¡(WÖdA=K¿ºÔäœf;¦Kh&p�9 �k@¿Ž:v > üHOßz*aî3œ±{¿QÀ!9¼#hµ¡t*œ%VÃo±æo(ÅÍËä|Q›þø[±¼s5ãt -ÃÎûy𥔠(«¾rŽ\ÿÝŸžç.¢o?66·ïlOïƒpÕíIóÕ›¡;w�û�®º4�7è=ÂD\úÃ�Îò ²“_2‚Ohž•%3fžM}ÌwÆÔFi|[¸„ÇX«Ø×hYÔø!4ÏÐCrm€Ô  }!ÝäY¢%/ƒË®O’¼Äì¶m�%cÒý+«~h[L¹2ƒð …D½Äs ~ƒ¶æ¥náªû#ò]Û®áCt ŽC@ˆÁ!…»�»ÒæP~|ÜãtSª ŸˆÇþ]ܳ”>C, ¨…W@+´Q¡ ÚËÝÆöwædÎK\kÐF\}åa 6ó°cµgÉâ½ãÿLi�: þ–,SÎÅló½PTYèWÏHG�¿² €#0È,Jä@9[f¦/ø©yL`Àfì‹ÄoºçñY YÛö 9ìí%.{Zà?/r~›|vgP= yUÊlVtY>•ÅÊÚQkË õg�é'Á;eÂ{l�¯ï?ó£ªõ§1v$Qä·+’M �® C?7ØuZþδ‚3}Ÿ°Ž€æÄ‘%#Ê^N:'œ‚·4º¼}&�=èúµ“§·aášÎ-’QªIŒÐú?Xe‰¨V¶«®ÌÇî‰tëŒðµ”c ˜JŸë„„”jФ>­°�8›Šü&ÜÍ�´”_ÝÅN­"ÑuŽlþ€ük’yI×õv«mžhVÀߎ\6U)Ø´^Cß­ºô%=ò¾·�Ò4ïø‹TÞ^CLàtá�E}ÃÈŽ {‚,Kì4±³çmí`÷õ0�:Ü4dg§,xå<,ÂÚK®0¸Ïy�æì,=x”®™Á¢>}�«²I�Ny�HÁ`}|Í…ü-â4‰³x½Ó$|"Ì7æLE¡‡&¦$oZ3NŽ|À—ùS¼Q˞ǎbܨ7f~ô+5 „ÉÓ „\6­ÉÓ¾¯7¶D›5ßr¤/õSÝ\Ç€N§§Oh4¶ ’¦ÆËMÉwmÞØÍ�:¾4:ö<[ÎÑZU{Jnsnr¢MÖø;N…˜‡ XÄ9HŽ˜‚Æ Š˜7Ž;.˜#*êžßMª.4…¦0¨Ý8®ÐTéj:'ŒRÆÈþŸ>CÄ߸ÁJÑ»o7üàøÐwã´î°Ù¸mºî{Н7IüšÅ’âÍZñÔxÈݬT›ŠGœœ†Ê ÛËbhßxr§$uvŽzt†¿þ}S†tÍÃøhݾä�DàøRÐÖA|¿¤ÑoÈ7÷I`v$?Ö­_op +Ê–á„áÐ=Ý•z+î2Ì)ƒ T”%á„l3¡·5DŸ˜ !?y¢yYÇÇ„J«Há”� ³óKËŠ– ckЄé¨äÛ·2•"ñ Ù÷曟ðÇDø¥Ù¥ÚÏ÷˵h*¯.Û Z èDºÐŸL¬öÔ¥½t/ºš½$‚ć‰2éùDâ…à ÞjÛ ±^Ö;ω²õ½±7s¼·¶ñ~»úæ )�µ‰áuÊfÜG÷YÍL�Å®YÏV`‰‰Ë^¶p“´Z»õ¾„cñIž:‹ ¨Ì«¥ÔîúQˆko‹Iñ3Äø{9ܘÊݔ޾ê ¯ÉØø:é«°½ŠŒr± l>Ì·XÎÉôŸ‰¹!ÎäÔGAïÚ 5^žÉW4ù(.¯v?dgeídÕsYÆ)t.‚ÂeîɰŒõJ“`Âôþï)�µÊ±;ÔÉ›Ï'àšÔµ?WYSZ•úåä %’‹£%}*rRªÓSÎÀk…<î9' é¥õ ÕÞ†ÁSmC2'¸Ž/:,2EÙE*”¸S�JÞƒ�p©xìsÑžøX@™ƒ½&S«SÛV†ïÂ&ô.ƒað�Ó¹ö¥Ót|#²Ù@JÒkE·G?«è vüÓaR)Øž»¬GxõrCÍŒ§¿.q3 D–©°Æ’ ï¿§þô)‚3 s·¹å[FøOø‚dRº žð¥fDÅ>”¼àŒ™ŠÔҕƧ±lqÁégd÷úûìc™q-^á"0t~-ctBfTä¬yÒÅÔžþ¦Æß†¢Û%n¢Ï¨ø™™9ÔÆ™HBÆæó®ë[Ïän�G@Ù:ÖMþëOÁ�‡6™Y¿È.Æ„]ÕÁœÌ¦‹Ã~g—ÆŸÙ¤!9I�ãí¼Žï¼~û&JéÕò™ÅHÈ™e5Ö2ÉHQ!1îÙ‰¥˜eýçÉEgÝ`¦YÝ«¹Ãí 5«ïÄÞ>n‘…*hW@„î+ŒJÝ=k3uOíÄ“ ŠI¨Áõ�g“<ë‘Ø½ï}Z–]¥G8y–�F|={˜OY…¢d+,vÉn–yö%ëY?{nϼ[íU%�æY°ë òúñUG>û,�!~+|¾à’˜0×/P3Gèâü�mà¤ï>zÇY î/²òœ ºKÒ£DÁDj‰_@ÂÎ]ú<Åáœd“gõ[0iÇs¿Ï\ üQØ\þ SèÇa�Í+þ¡¢„í\±ìÊ5쮓Vj_n—GÎps«§ÇŸ“áJào½äц+�s§)9²)¿W†é»öZžš <úJswƒ” 9nð¸H·£¥ãLþ×>bsß[¿ŒÿE¨Ü sL�,˜© wͳœ»hÊ’”<û?[êsx©ÎKù-{ÀþnäÛ`á«N¦Z‰ŸeÌë%÷-]"îÙÎèúæòI û©Ï2°·lÂSΜ�Ô×ñg¬nÎÏ{ÝÏ´æÁqâý–J|\ù•�$ÎWZ&fßyÄ>P@Fg ëëÏWß÷˜d¼�ËÙú»<°´òš Î_0 fåEþX„fcâÌ3_.PÇš=B�\壒g³Ï¨{Âÿ@-Z¿å’ŽÑÞË&ì$á5IP˼,èd5ôÊ�3 _,³DBÄYP­j.èêòÎ y³h¤Ïc”,Ü}mÁeW x™Ê´FÌûýdZ$%͸ŒÄ ûŒÇL_î�éÙ•_ä¬z!oI`lSÂNjý¤•¤×ëZ\Ìæ¶ QäVÄ×$49˜Q¬Zl€úqb¿àòg#Êk–i}Zœ¥A`jZ¬BfCøäÏ|Õ3l݉Z8_w`>W$RFʰ% ئZÂÆ žýik�BNjÝ8g•fþ»DçKåÍ-Á©yƒ|ôã5¾Jrö²Ä ‰‚%–}�³êôŒlÂíâ¼Ä~ ±·QH1o4g «7€Ëµ$÷-�@§²âs�Jœa>›Ý¾¤))™ÏàÔ“”ÑX"„en™ÑC²däyp¼  k‚çoÂÊNdÝÔì²£¼S°ß�Š`Q{JW5üR¹/N�™wÍð>%øbÎmÊ"b ºÔî¯Þ%èC öûy¾T¡º´Ùz¹5N”1„멲ìÿ¾ê·,ëö ˜ýtl;}±s-U_­ºìÐh[¸‚¦T —ÏÇ÷›…&;/¹¼Ô6íWÓú7=ŠI_•Ù8hùÖ 5àJ®3Ë=-‡¡lDU×Å+âqµõÒ9 Ê•ÖÕ¶ûOȹð'��üÔ…@áá{¶¾vÐðeÛƒ6Ö ƒô°|§ÕK";vHˆŠãf=€Eý“ILŸ _wJ­ u`�à{w¥å•^&Üawê!zífó€ÅªÚ²ßs‡-¿ÐõdDÉ~÷€Õ�{°¸ ÌÚSƒq&b—Rœp¡ó€ý-ï¿g:ðŽÙ(þÎZ5ν!|•ÿzÁ)™J"g�¦>ãòç>·áÉÌ`‘Ê8@&ÛƒU»Ö³´ø{–!îâG[=Ä-0ñ3(CæœêAÃ/‡Z¯óšè—ì8“°šB¼\(Ô’Å“zn8 ³¬ñ® •\Í sï n–DÑ샙¯«¾É¨Œt¨­ø§Ê 6ZÃX-j¨°Áó4'8Rð¹0Í•™+×èŒllÓQ_æ8¨-ó­f$KlTóºßÎI¿”1¾†¾¹;7{ª(QÀ yd—ß-ðjp#ÔÉ|F¹PÊ„ÌÐÒR.º!—Vû2g![¦ôåoàü`ƪ¨8r‘0. 0† ÿêÝ�§ä»úatÖ(¾nãA¢†³úëþR‚~¥×ŸuêÇ:ž Þ³½Ž8dïÃ/ù­íZ©\ìü½ƒN=üai)¾júðN¡Ò@žRi+>BY.FžUÈ®öÿlD¢ñuŒ-8RcÎnÅò%ÿFžW@hôÓõ;:6Ä܉â�ëŽHÞ�D™¸¡]9ŽØ>&¹J-"…ºÁoïXfò8ŽVg•”c¿Äx߀^ö¬ ÇM›:. ‰€œW�×킞Óð˜ø³e{\³!öÒ£ÞY¿Îr¬2› uŠy<ÿq[…ÛLÈ�øK¯G÷£xn•Mrür낇ƒwà 4´#;J”=»üKñ¢JFÜz¯ÉŽëc7îŒdŸ÷Dw“ 8ËŽŽ".²} ¶>«Þ¨ŽåÄ|ÿ/þPšy›:gjX›ì@]@ÚI 3”ª é_=—¹/V½4Ñ,—“–Œ†É>Ùr'D7�æh·¿K•'G{âê»Ð´3˜éH :Ǭ°lkË'?L[">ÆÛGHnèWÄÙ‰ù˜c&§hâC|ùËÎ/¨@GÿÍܰ»’5�b_QÏæ”‚P 5YKðú/&-PºŸ mÚ³Š¨&C’lM‹Üˆ�Û=ÛŸö,:7¯/À z’:•]œZ�X�N³Y´J}^4cP'üºЕ+y�^{äÜÞ‚e,yÇ1¥h5†U‡«œ¡W?‡ƒ ˆ—< Ë•„¾äšõož%d�@D£5�gF¾»fï¯æž¥Î€ó=Κ;Ó ¾L݃�U¨¨ã)hN0Å`;ïH|±T'䓜U-<èè|úÚS›eÜw¡À¸wµvœÕÿ«¨Zïá€+Ûç‘aÜ¥ž—]¤~HæÙÞåðs67ÌB÷ë¢=ʱ™Â*øN4µ}¶õW_tˆ§}%×¶tèA)æœæ*pãäýK7µnÂû0Ç|uóܪà@Ofu£y1�Ä«ü^ž‰¶1éÜÑ£e‡š/Ž*BcK͈»X±„Aà<îï1Ý<)gLÅ…Ðü�¼_áù8pœlD$íy%<8³ê¤OtäÝùó™[ÀYdQm™yZИ�˜ÇûùÕæ”'m&KΕõ: dzˆ¼SËÍ&9:‡E ŸÔ�Ù+J·Ü…f!Òf•ñ|T§¡ý€CØJÌpšÛ…"«‚¡žÌDoæû@—‹­>ïE^a³¾¦óu£Ë[+ �]ïE©ZªÍù  GjîöÖëy€‹øE£ øÝ:þ°3  ?LWnf#ñÅVöɤM+‰“IL®Øð©µÜ% Õ“5Ín|qñ4?Û(îì%Brîk™ —|ç¼á²>´ö%}Éèàä‚CÁ»8�ç\úŦŒÅô¥#P#äàž'=.8ŠNsÈUÚe€~j�$K[Ræ´™61_âe¢Éá?4Ñ<"Žõ#æ—wEHæ°Ëd’(4·P‘T/¹quŽ®³¸Ë̱V#F>4 pgÁ&\ïêhá÷Ë 4'*Q¦|)4j¥þ�øÚÎ:˜Ë=Â5%Rªz×ÁQ¢qü3 ËuùpXh U‰™À !�Ͱ;�˃F~oR3"]1#� ^˜ïÅ+r8;¬c[GÅx¤ýz=ý›á'ç+³Žgg°Ïqù(Üg-#“–"9M§++lŠ% =CÏ`¥³§1»5ừz Š±ŸúÅ®m;ÔË%hßšàžÌ«‰—·C¾°¶DQtNñ?à×røWs›ë(o˜ÖïkÕgm©{@ÛU×H¯(2.3}J­Êþ¸V0"Ç6×&Ó=|²DˆMØ¡5$×$‰ýt;âÙ49hÅé/ñ•9®%Ș‡ûPÐönÒôØÙ[°Í<®íBz"›2Co‘ÿjkQö?M2^G¶0âB!o6Ž€r«™zv'_—x«ž`à¬N’# vM2›!_àCh³é[½WÉ Bþ¼çµ¦¼âºÙµøo˜Þã¿Òàx‰çâ—ëg/Ùôwñ¾G/[ Bô‹ÒBõØ“{UAR¢çˆ6uQ¦ÿHn0M�©«¬Åª»Fòí2ûÙ³mVnZ>¾HsÎÖü1 4’Ðâ£LxÃn z£óç:„Êÿnû¥œ§Ð‚oôeíè0ÆgÉ‘¾}Ka>ƒ`"nj´7Ç‹ õßo¬ jýeuï‘ÿ»º9\| þÎg3”ºÐáÎ=%ðU¼¹Ôhþ[™¦„»úäÆ C;ƒ»{xm¸,i8”"ÿ¬n[A=Y$žbx[4”«íI6¹>HKÖ9½Ô8y[’ådeËáU=<˜tf…abh[x[)â6 ÝC±²ä’èb¹œ¸ÉÝ´u[©^“þ¾9 x×àSûÇÅð™y»ÿâùgà�;˜Û›æörcuæv—v~E¢MÓ00*••Sš,oú»³À5t;°-dµÉº½v‹úŽS.œÖ­Cü‚^é‚4§ÐÊàû.óêÉî%êS¾ØW“)Ð_¯ËD^ëxf�>ÐTXsÁdÞí:soG¤eõ^•ããköù1_[Q�’\‘¼Èäö^Û4ÇG¾í<¢M¿/ää^˜¡ëÞpeòŒlç AL{v³Óž_IiäÞF*'µ"_bz“O8ýÜÚô••é¾Þ2ì* L®}ŠßŠÁG•/±4î¢ÑçœüºíÐv†ì{EOšŸnüÁ²VòÅ#€¿¸)ß’½§Ÿ¹ÚQí!Qš ¹ÎòÈÚá M߯‡˜7ù¡"2|–Õ©»ÙÇiÕúd‰V{¿é¡�_˜l¡Ønî¥†Ëø _Mýaûþfª(“ɳí?9"×’dá‹ç½‡ƒÓ}7åê)>Ôº²¹‰:Íiü§‡› |¡Õþë§”?§µýÄ‹êÊ�„óV›£…:@  ÇPÈïºï�Rn//ËAYç÷ŽX�N‚£2�Šs mç ׿l~Ÿ×ëœIŸp�>c.$õ8‚dŸ®€ ¢y)Wü’ÉÈ»P`-»CÄÁs댟�yãl<ìv×ué1c…Ζ/…žÐùF¬þÄÈ~êÁçLÇÚTÓ?%¹‘ïOºžGi]fmúÅ:ªú= ~­.p�;^m‡b.énŒáÕ>Sè§YýÕÓ�'F’`_I!qtœ~FÁ%°}=‚ƒ:îsêÓJ²5âyF‘ý–SÔ Ó¯y\Ò†’`ï‡È¦Â@8Ýjö¡Sgý�ªôçæÕ}¦›³ö�v‘³w$ã¢ÍkL;‚Dx6ðú“Ï¡X‘[ßѶßg¿.5å€ûìM[—œU¼²�mÞÙ9úŸVßܳ�LæoS;i‡~“ƒó.*šûsEä:òøï¡ËŒèiÑÑ­äyÆsHS–6Ï)ïL÷¸Lp0í“Íóá¶{˜¾r!<ÉQçaug%QšhØK,n’ ·‡+Š”œSŸ÷îØK+?ÒáUëyp•w n ­S?ÜKGå>æÜŸŽüPˆ6¶éX–»Wøih²ûåØ,‹+É�3~jK ÓÕWòÄf¯J7ÞÆÀ…3‹ô úp�×è/,ïŸj�É‹O;8¦·ÀŠÕ~ÅסY!r^¸òµtdâCjÉÈÄíשÓ�Eƒ _¸>´+è ºCÊ7^ËRÕ€ðó�¹–-ècIëG›L‚·?¡©]m|�qC~ä³±ä¬xžo:®?R�£Å=߉dÑÀŠ»3oÆ�ÅÛÐ&gÔPU¶;ƒÖ_o]Roñ#Ž×1Ï`°žFéYâ¨fsÁo×På1öé `ˆ7—$B?}ÍGõÊïd(&Ê NàjHÂãŒú~Á½¶ïÜJ.‡£=/3ÊB†P©O!:Nïf DªŠŒOI™ƲÛÚÃÚÁïŽ3™¿[oÿ©Q^ ¨�0ô9¾ÏõW7èl�âýÓ>c$B�* x?âÃÍ"wº¾Û|dÅVÞDkE~ÿ:h"~ÀÂѹ}‡¯�½§HûÀ¼u¬¿‚üËV·uçâ³YIRóÁÚLDŠnŽçʰoH¯‹Î§öJuýQŠbB¸E_ay^nó’Úã@ùñ°q^²!‰ÂÚ[n;¹Û 2ÿñ³Õ—õ¬ðß]9v5šá@ ¯˜ñ'QIq –nBø]˜|ãºSî#!ï§GÎ�ÛhÁø�¿ ñŠfóŸ˜ðÏÞÝ<ÔsúÂ-æfƒ&Fèïé/žvܦ] ÏóB/™æT!“�ÙŸ/>¢ ó—·\ëe�d¢Sxvאָ?‘»Jôp/hƒÈ8\_ÑÐcß}¬ÜR»,¦ÿ$z@À‹9ÊK¾ƒ™ºBå¾–ïÔâߊ%÷p“i¾#òÄSðÐû‡_eñBºMÕG¾ yÓ¤2N÷ßÿ¯ÀºÃ©|ØðsÎyO©ˆ"!E[*E¤4´�(-$¡Ù{FJËHõ£”-#3!D*R¢„šÊ(!›ïþþ8W]qÞ÷÷zR6¹rcÉŒñØ “Ç—�ÿ�gø|ÍAÓœð ƒAÁbkOMÌK(^�Û¸Võû“¾Ô$n…rbüÄz+ÍçÞ½ ÃÎ<Ût÷Dß ·– ² U‡ü³{M^‹(/{o`·¹(7lbwÎÛŽ=m4J•ŽRQ{ÝZåÞ„íí¥ŠìM;„ΤùE¾š{©(gIÿDtä‘R�Ê î²û¿;�\Eì:®1ñ 8$K×çë¶ÇQ¦Ïõât7ÿôwœd÷Oõ~®É·æâ…«eY?ÄïaM=·pÚP¶ËNÜz>wâQ~ÃãÉÜÃûV¼œ—]áÕ“RY©©‘<±t²ü`IáÁâ±Ã±c/Š+E†¢ß+óÜš�¶½ãß�?oÝ>m]Ns¨+¨çóh¤×:Kã…+»n;[0ËÆå}¬3÷KAíÍv�뾦·ôeô>:Ƭ¼¼¼¥ç†éÈõ¬¹gÿcε>Û5nì_nõë��ÒëékëÔ[¼{¿).ÿoûÐìJö ®ºÔí%âì©Oý½~zÊu/¼5Äo72);ð/ºÛ¡ãìy]—YKõš›âÁ:*³5ZnëþíÇ¢ÞÖ¡!­·Š gOŒ\P1r¹|eüڪͺ£Óå jÞ®æ·JªÑÝülÙÇÕw%µ¬º5äºMoYÅš†oóMwzt°âÎêÛõ]W¼=‚ôùDç�Ò‡ê¤ÍÞC=;Ø·ç.�isR¦˜»·P"0ì s¡Gª›×”söúÚKý¦dN›Ô3¨J5wÍnþ²•o Ï–ÇÇDüùšîuë•M:\-“Ô—èû ô_'ÃÐqx¸õ‹ïüUv>s­ý-ù84¨,¸Æ»Ú]Hñ¥é œÆÞ�^…I¾ÂéM¯?)ê÷{s%!¿*I�ÑÞ÷º¸­·âéE™éGu oÔþ ½Ûv¶k™¶ê¼ùY£1+¶ŽÌ˜e¹~Ý¢ùç/¶Xh]—*R½¹%×îØÔ&…˜Œpý¼¿?·§e�Oùh®²-\=ÕB§ìü}-[õ¶'u�7ö 5¾8ëiÿ`{€Ÿ)ûÚý�„°“ #ϵ3džì§ã]­—tR‚³#õ„é¬`€ÜªµzrU-^«Œ§Ä‹œ¬j瘔îèvê–d«e•jNy#£ú㤣b½Z$;ÿ‡æ�}óëüÜœuÔp‹_ê£ßÕÑ+ dF­ú2?ZÛt ôhXÈï»v!hX:X¦Ó+^7ÓÚóôæ›Ý¯þ©¿úþ Ûì¤ÁÊÔ ³¢hïuaRi‹_/þ‘¼õäKk!u¿-µ‡Ù;O«¯Š‹T›9ñ‚#Å·×¹¡ìWwØŒ¯{†2›¿°¢%¼¦z‰oÌñÙîàuúµ�™IļîGz‡«.°ŽèP^}ën²ïÆM�ô˜=ÿµ>عµ¥À¸àáõU9ïaÛ¯eG·7_iJV¨‰ïjª’lj:6='GfoÒ”Ìà€{Oí6ø]?wsÉ;i鼫î×,’wG½3 Žßl](ö¸¶_S<Þ$¼(ëôô]ËæH’8>7öŠxÒžz“vå4^_áçX/_°7 M/½X×'WëráÔ»‚’áÞL�º–>æjz\xÔ¾²EÒq³ü›[\V%†Fß—Ðû¶MýfcY�j\™à³“ñ=NžäûÔ6~Qô‘kÇ?‹ïõÞ¾T3�ï`É @…œsÊV³ãÕ¯Ž$ÇOë{lSXÐöO3z,‘—,¯ûݸ}¹±È*›ß%*óÝ­’ëN•¨ûÄöüi’ªTÛµ³Ÿ ypçGæ®w-”�#Vê:ßüeÒëäÝøUYwoNb̧äá‡Yõmsÿûî›Þ°Zð´h §ié%‰Må®ù¬è}º›‹Œ_+{Õ¹üìV�ñ0Z#‘DµQƒof÷ôMŠ?Ë»¡~¦ÁJà†§¤ø•þi“¬¡NC§LËrŽå‘%ç·;š/åû{¨RNˆ™^bëÉËŠ�zºKŽ¥æ¾òæ³s/�°ÖŒ=¬¿uMIur¤y_!Ÿ…Oõk>ƒS$ƒÚvN©ü)«äüíö†à«u³=›ËŽûZ[,íXTë”ÇÄÄ*HGWW?È* û¾(=á¤ãò“ö2{OØ;JÅqxŽX3ª9ëþY9ócXçïe}JUìÁ²¢ëE™oJv�ÌÙðPuIf¶ó´· ÏŽÅL½øÓf–³«}¿r¡ØäÛ˜þô c®3êÆ¿H+ Þå[ž�âf•®Ýâjäo¸?¼¤ÔXãèãû+§ÌÌžž0§öí¼êÓ‘’Ezç�™Ôi~ïÌùïm¸Xîç=ÛRšÊ¥CÌn·2E �/ÌQç=,ú­óÄÓ`½?µ%G�ݾ§\^þh¿AÞ ­–lÙº"½gŒÖ�*üwp+ë˜ö ¦Ä¹ns�B‰·õþ¨ígþcJÝ÷Xu-‘fuô¼Z»ìˆ‰J½âTV}þ"½¶ ^�ÙbÉÑŸ·OùzaáWí[ßMz„–|ªµ»°�_jDZ¹ÿ•J�z41eG»þÕ9-A™Ì+—Š[FÛÛ ¿ŽéhôG6†ŽZõu¤uÊ�ÇixEök,Vä>1~xÞö­¿súŒ& ‡�ðºà­ð*k‹0«Ù!"ô�I�aX!I ÿx-tõÀ&ÿÍÂGç†NÙ”ð$JìáâQŸ£Á©#N©-.Ù/„Ÿ,ž¶Vrã‹,��³wn°ÓX¥�½ÄùöÆEÁÿÞiòyZ*¶oëí®k7[ìS}ì{Zžú×”ˆõåÌÜ&%xH4[@©ÓJ;zø±®sl¥‰Åf©¹³"Ôßì±ã\ףƔ)²ù¥¥ ü!âEz»…‡n\:ûqL‰nËš‹›¥owü:P0]äÁŒ¹ê²ãÊ…Oï<;§Ý¿jrZÊO½”êÒÿ;ÒúÙ6�.?Ç˃õï̑؞ë½â‰ü­ÑÝÚãûÌ&Ǥrs.*¬·W8¸}É}±(ž×9E×ÇŽ§=Íw’µùuû+oÉüç'Rw)U&ð8®3}¸àe(÷,»x‚wfˆièÛé'îTþ9­{?ùÏ §°7Zž}j‘7$wôùMV–}Ôõo¦tΫUÝé÷ 6¬ìØ-âµ/±¹GásîÚ?RO¸δÏ?ñhÛ×)²U÷k3~ˆ³›Ü­–íÑ#"9ê*_¾­WÓKr �,ŸÊfF´.uíôïk^¬Å·ü¥ªo�õ´TTþÌ�ðç©·’ëÙïÊÜ×KVêåX8-X­yK¨;RéYëþÇb[ݼwÛ=Ê(Å`öŠó{þéŒ:©¬yŸAü¥EÛ„+zF]±N×ÏlÈq�ã?¾Ñ¶hRûªíwysùw:;2KBŠ¿\‘•û=#Â~ö[bÙí’AÑF‰ó×QO•œ:ï°C²kSªÿ†ˆy»5…ƒ;ebd>üð˜vtí›Í"'ö[–Ý}è§wdݯCí‡ÞÞ½8{ùû­üËwŠQNÓ‰êÔcöø~ /½g«ç®°tzí¿ŒÄGC=ýÆÆ?TguèŽ4~øÒSdWOgÏ»åÛßß°i$o›öä/vN�KÉ~ÃÞò8J[:MºÆZÉÄÑ×°ýn¹[Õ¬ªòØŸçW,Œ³E_9e¸|mO>uìxFž”æg®ÜD3·Ð„¹ìíÒ—›}¡n^ÓóÃÖúk…Ïñ±sÿ $Pî2Ld Ûdt§k�¨Šœ]qe9��ظÊüºl�ò§J-™ù±ýæÛ”ö¤î×Ù—Õ±�û}ŽJ­‘�÷s'«¾;[OŠ+>âÉ}”×i»þÞùZY§Àn�÷‚B� vôU”¶ p–ßÞyy¬0ÿ‘¹Û_á ­•¿Wý�ðˆ+kV‹}Í–Ua¤çU\)çŠvz\l:}·vø’¯SÃï­ç—Þ<—j4’{øýÑou¦yÞ'¶K´¨pÆòO®2)±ß¯M+>râÝûOCgânË|®Û$y�Ϧ~ÏñÏ'Ÿ¨Ü®õP•j”W¾ðvxŽìÓXÙ¡ø©òÓ÷MÆt•Ëê¬{×j—˜n¸ËÂ�£’ªßëàl:¯«ƒw¨Ü2òýÔWnn3Ó »õÐùyÃSî%³üaCÛîé·Ñ…Uu}+þi~sسá@Ésõ}5�§.YÒ\ôwS#¡w°v}ÿò»Ò§+„ôø.ݯQ¼Hí÷$·6Ú;rÿ€Â‰:u/¤Ý̈,â×z'ðáNKx$ý˜Ó¦t|E‡Láôk»~Z¾Ž]uDIÛ€?i†•Þ²M]‹»Ìvšl Þ6£î²±Ä—±œ‹:“óÕ¢Rk.I* šrü‚ߌë $Hª>Ì;´¨ëÅ�¾ìøîjÞ¡+¡9¤+I¦úU¢.×Í ¾$ýt+—¯¼ÓÙµD╲Ð%k¯�Jw{;š:,ŽK=`ºë[èÀ‘¯]õŽòáÖYšžš�a¬â‘Có‡‚$ùdÜVôWûmó´{ÑΊ&9ÑÓ1•é[ó^?;fÒdùJ¨ñü�¬«¦Šä…ö>>4p¨y…;î¯å/§ ­�ÏI»æ½Ùk-ñè^ŠD˜ŒÌ!‹¤m#YZi)å[nå ²Ï:=y³¹èͽ¶ÎÙJC”ð£J^Ý^tÖ– ëç|ÞÝ,ñâY¬¿Ìï—¸õam¤v¥],GßD(PèU4ó¬îï…;'V7iéKŠÄi (X¥mîjõ/‹]2²Ç°Š×ÆV ¢vo\¨°OPAÝH-¯ð˜c¦S‹vÆûØÌE¥o{î©“êãpkø6^X)*{kº:ïéß\Å¢Nþ^w_±IêÏ_¡ÒµÒkŸ—ÈÇ5^¶3Ýý¯wúb;› ‡2ŸŸï›U¢V¯2<£qàzࣈÓbüGuËm;rws#Æ/Ÿ6b÷¾r1wKÏÞr·N¿šuÓßD/·ežÛÒ:¼X¼ÅäÄÙÛ{ÉB¨u¦åÆÄD‘Í4i­³Õ¼-ñR{2>Uïïí#];,{¦Æˆ¿4xûQíÓ¸pµ?¿²¬O\zéaÉuîæyxõ{ÏZðJ>Ò¦'æéÞ«…êÚrg—¾¾&Éíg…Ô4aß¶úL¯«íÿ1æVõPö«V§‰£wäëîm\%ï–³µfG¬°7LzVuk¾é ß]ýš«.�m—˜à‹ôž-z¾!äËß„X=‘± 6sD;lŠ&ôb<[?¥JW×s¬W½M`‰Õlêwê½¼éÕhD~]¾ç¶)ÒBɤvI}ü7xððAÁy‡ LÆŒºþ[jãÑÙÚÙ¿Ë—v4ü»Ø5'åPåÃSÏy·-LÔhç[ó7§®¥£ ó÷ž]–?µ;{ʧ~–ç™'¬åç3ÏÛ²1оԼ—ÿ»mÎ"=‘ã{Öêµ\=ϵÜÈ3Õּ冴H÷ìÂB¹=*–<“O-]æn “öiÙ”ŸG‹¼½& ,< –±jœ/Ô‡º4�•I9øì&›{ËìOeHÈǩڇO}üÔª­äy̓sÿã¬Ï7 ½"¸_57ÌéR÷=¼ÆzoÒ!ßcîx¶Î9Æ3l,˜ ßsiùY½ÜZ!µ¥�?å„$Ϙ!ô¶àžü´Ô|³ïÎÓ¬ôJÍfO‹4Œ4u½ð,ö$˜<|�òîûÝýŸþz¯°‹X˜¸|ËÚ2"KD>ª†5Œ[úΨ¸z‚gsØÖsK/�DŽðœ_Xm8÷Øt�º…%ìùõö™€¢˜ä4!A©çç• ÷õYd~+=AìżùÊÿæñÅ©f¹²…6š­÷6\ð�_¤ç‡ƒfíñ¹# ûgȽ˜£5‹Ùñj@rÚ+—‚BJJwg·ÖÛONin‘]V�üËä™\þ˜(o —áãIu¿ü«ÌV_Ü}3l¿°ïÉW¯Š\ÜÝ­¢Rõù­Ú•׸ÿ’Ô8>n#×ùñô¯—’éÖùs̹¡»Ü$#—lðìªS<’Â+m!~k47Bî~Òú'rSäT ÜÊù¨àÜ©1ÿÏ2‡W#Š‘i¡Ú§žþæù÷Dn…£ÝćÑͪUyÆ~+x§�µ…íü·âI`X«»�AzkÇžýC{Ü�8ü!Êekó™u%�Šþë½í|ÕyPø³Î+ÎÅ£7Ê%b¶4i~©îì7MˆO¯)U9-pýÎøõı‹ß^n�N�“]1E!*Ü!à–AóÇö�¹a2ûöýŠuÞ³úɼY I÷¬Ï§+YfŠ»m _µ�íê0ú#0ëj°¿­ö¼�±‡=æ®;yÿéï�²Ѳ݊?åd§ ˆ>ž«¯þëê–Y'k>¼´ÞŸ(6{ž[E‘ÔÂ.1Ÿ¾ã¡ ´">1Bì—•í_/¾ö)\|k]“«_ÓŸ¯V¼ø–ôƒghü{À¸Áöø+“1â¶Ç¾K”/Þ»цÊö/ŠyƒÜKÎ]KóFŸT·‹4K˜ZX»f‘�ïóÜ9'«cÇÍG¢Þ§…ŸíSã]>à¬Ò*ý->Vm£×ýÈ©alù‚áR‰Ç~ ¥g¤79™Vú­LdÍ}Ä??óOyýzæÐºäÕK—¬»”wýàù#m£µ®ÞV ì•Eë’ßÜçqŸSÿV¹-Þá˨RØTÍÎÒ·ÝO\m6ÆÜÈ22r�±¤™u^{á­UWgͬºþ.¢·ÉN¸]¿÷œ~®�†Ñ¼�‡³+Oî4kåóžý7ÓWàj†HþÏÌ ÿý¯�I,tnÜ80`Ønü îniO_àN«3®m*3‚Ó‹jÔ.]Ñ;§¯çz¢ŽëI‡³+Ζ^ÙÈ9îñô´Å»ºíþIÆ;5Ó ,×V>vB7ýu`Ρ¥ÈMõæ¿2Xi»gÉIÎXUá 4З.¥óz2ÍɰÇÒñdÍ‹Õù/Cú°^ïªd‡æ• GkÌñÉ”ùíæª °›÷ov(OºÚ膪)�ºŠ¶Ž¾Î͵‹WÎwŸ>ýbº¶�úƪ+ë:µ*©ÈÅߺøçNŽqm°ožÎÉð޲d÷´ð7‹y\–¤?W¼è¾¼l|þ4†÷áîá2ûþ‰¤”NKÁ7K¯lJ÷�õ)Ä=|E…Ò5¯y¿ŠwõU>ý&³k�JÑ¥‡’©«ÉtÇ¡\0š‘iákiâÉT˜2Ùsî“ú[Ôè•ÝŸ\©¯ÂéknËÃóÃãeЉe®Ñ…§ì¦/ajÅoÖ— Þ2ÒvX¾÷�å��´Ì›²­ý®i›hNµŸ,ðxîùVchJQì»Å u7¸×»Uìû6‘dÎQëfߘçµÑüK·™'.ž¦¶~éAãMKdg^Õ‹P² ÞÖ_ó`äôG³[kEç†�å ÿ”ëýtLÙq!óVìÚ]Ÿâ%ù£ÖYmöî / ïrÙ8×¹ÓéÒßÁ·/ó„ñ^K¾)Uù–=Mzëp¡Ù�Ȩ́ÃÉK�܆Šsô öÙêIÇÄjäŸóªß®¥ê–Ü­8°ŠüÒ#lj¯çlh‰›’ò­g«ëÀëç¶æ µÃ÷¤«B<´ÖV(EìOÍ_k¥‘°×+ ¾0ÒYûÑézÏr‘«êU Å3$F]ȪvÝ^^yfKÜ—X³ÉT‡>5?ål+¯~¾ÕM¡öË&i¶ÝsÄ–E,–OY£T–Ýåð/fÓ¸ºÔ¡#®ûÈ&\úí¨úÉã´`xÏ”½»õYן‰¼;�¶pvR�91¶ÄIœFÌí¢Þ…ĽGlÏb‚D‰0"fÝu"Ö âš"Nˆ"qãJˆ«ÿŒ8.—ˆ™˜E\÷â¶ê×Ëšè¾qFº‰ëö›˜=©D-ÄT–sLЏ�‰ðˆ¸·‰í�F܆­Ä�’'îíIâøúWò1�‰Î$æ· Ñå^b<ïW!ŸXýÚÄÒ!nø¢ŽÄyK\­éÄÌ#NO"q·¤óà q]ºˆÓÆ%î…qâzdÓH?ub‚‰XI\£Mĺž³iDÒû‰t}‰ŒvS¼•»Ib%4·~+±œzˆ^Í$nÐ1x6×K�¸ìÅè÷qs*Qsú›AÜMºÄî%’Í'níSÔËC\OâZ†×õ 沄غèkð.1Ï1'»gD‚‰åÉ"Æd?qõt‰“TKÜX!bæ67ЋHë?b ¼#®F?1Mˆ67ëq.1;݈{F‘8ãâÄrýCÌô…xWæò•�MDþ~D™¦˜!ævô=1³‰É©'²�#î«jôlNtußC¬]ĸ…'М¸ö—±b}+!ª�'æt1â˜Õ|ôÒ¸}×÷¸4qm¾ã´sÔÂ÷ˆù—NŒËbmÍ n…q_» Ÿ0âÞ%F3ŠÄ¿Kc'سé|3´Á<}ð{‰Yó�XЉSÕŠ™¿%nŒq½9Ä’¯BýnÄR›B\ì*ê"1B†Ä>äB¬=‹ˆùƒ]ZbGì¯Ä5t$fj8>Ähý"Æð>÷‰ëL½"&£3{LÜÌ¿DyÀ`\8ÑdÞ³ŸRÔ5•¸N•¼"6«˜È ¿[>Œ¹~ F% "ò}Id8HÌIOb 쉛­MÜPÌ#ûqM"î6ÔèqέÅlk‰%‘1f¿=û¬#ÖÎÓØÏ#âôM!:7•h+æ½ÿ1›]‰™�úÌåˆ+®Cä…Yµµ`n7‰¶Óó㉛·x%&°†˜ô—Ä^¾˜®%æˆ1‹ç÷ pz­Ÿ¸Àåĭ[Iô2„¨GŒØQlb ƒ󉫃$ð>Kðø¿éÄ]=ï u‰Üʼn½0€¸ĈÛlLlޱ#€ÉÔWøðµü%1»±_·bÛ•£ÿ'Є/ĽI\çÛÀF1 ³©‘Xðy<ûc qsÇ€÷åÄK‚¶üGÜKÀ öTD¬�Äqº��NÁ±òßĵþŒ>¡;³€í{¢¨&âtó5G‰åÂOÜ›'‰ã�ç$´9CîöÄØ§—¸Äê^M¬U…Ĉ@?Œ�%Ö â\5!®.ð¼(�¸òùàtáb¾w ÏéÄ,†‰ À{ gžÀE±*êÆ{V�$ö®lâ¶ã;ÁsÛ�Ä,À3�¹›ÀUï‘ôÀ ú¹u1¹àëa`>¸)Á¼ýÐ žsše <ÌÚGLætb¬&1GÌ&U“è±1eø.ÿb)šíÇžg‚ë ÉU[ æ#æj ö‘@\Ù{Ä vfƒ‹oþû| 06Jœ‹Ä}€º$ÖKì Q-v?õ�-&Æï q·ÞÆ{×3ej2&–28`ÚGŒ´¤ øÔ NôM*�Èô;ÑÂh×6b†Ë‰þACʶ7`3´ sœŒþ7A,)xC%‘˜,±Ô±«�»ˆµ:š˜ëêÄ { .iÇG çJàtîxæt Ø "’„ž›§Ó3ƒX}ÀTÊ[b_¼CŒn4í*1OƈŽÊkÚðzú †èc:0%KÔíœH!zßHä™L\ið/�^ÐEÜ{'ˆ›4 ?†®ÚÕ+�ß…‡e 3 <‡¡[¼‰‘ö¤›ù… ]öÍÁýÅÜãœ÷½Ú‰ ÛMŒ9jÚ˜Ž¼¿;‰¹Db–—B‰®‚Ö]À¾VÁ[0Óyˆ€á3GÁµûØíI¢}Ày±íÁ § øtéôýã8Ñø{b9£nO]bÏ÷m€µ$"žkÄšM4<ó„.øŒ|L䌬äŸ6FRŠà¹Àtö§ïÏb£¾iØ tê2ÃÞjâú| n±ôðŽ¦øf‘bDì=Ï Ðæ3xÁ'bͦÇÀ_ówÄðÔcæx¾Ó$ÑW<Ͼí-4GÙS‰¥ ÞïB6ÚŠ�YÜ îhÛTÌxØ ü™‹ÞáKáØm8ÞVÀúC€ohS†qÃò‰ÓÞƒ‡Ð3pgïMì«àƒÃvpÇž¨ ¹J´~WìNl„¦+@óÞ3à¸04 ï‡çtgi»s°;/h¿ z“èA?è5¸®ýJ¬áqÌïDvɇ÷lÂ;ç%R@æjžLû‰5<¹ú‘8ùø� xJ2éç>¢ß³P pÕœƒ,‚™¹¢ÇŠ ¢•è=ãÿ9F�Èbò×JÌ3jîƒü‰íç½AO>ð„ãÀØYKøä Ì׌èÝ}àç54ýzÀãÃE‰| a dámàXålâÝ%òÞEŒ6ú°ôÁÙÓ7Ù^èH-²I>všŠÐ{ ÷Ð=Ìw^´¾üwtùl øÏ·î›ˆ®c–!àƒÆI``6‘2÷ð?¢ƒØ‰2ªX)±VBÿB�%….bF¡ø‡ò¿±²à —Áí¼F즅h:ôòêiø�<^�{é"´ÿtk î Ckˆi­&N“*pÛ Yç>t+ >÷˜ù%MÄ‹œv³LÅÜŒÎÇÕ3€·£§±³ÿë‰8÷»�n~pý„®[÷"ç#Ïê F'äR_ä)[ÌôXQÈMx úš€h¿ÇÜ|¾ð^܈¾ø¸TâùРâ|$‰½F3H„¿ãt†æŽe[ ûlG¿•q_!GÍï4"‰-ë^­�¦¯€,Ç|v9 OlÉFÖ†îÚí„_õüE ðâÏðEh²!n…]xÆoÔyõdí„fâîâ‡î]9…ù!çûp‰šÊáw¨k»Þ‡›Èx‚wð1¶ÀÐ?ä<)èÈœEÀßNÌØ™†ýÿBFÙ >·�Åí�ÜXîD3Ôÿ÷‰±½ðû™˜yé bë ãÎ…7\�ç§ ¼R?›…l# }rÁ� /m‡Ï§#›Þ„~G�‡Þ�#æ)²l nch÷/à_�h¯Ñ àÏOØâ#¶<ñ>t—oqŸƒ?Ýxg6²ÈTð]Þá MD†mýn‹&VÌ4b.C/M0Ó�¸µ]'ˆ°d¦\ì™ûžØW€�tdk!.êAÍÞÏ1ç@b‰"g( «Þš€=œ‚·þt"º³Þ¬æ"ÃuìC–BÞ¨ƒ^±ü±gìf+n¶©èóþè °¢‚?KðLkd~èñ)xaƒ ‘¼ã„#üйE~¾¢x_MÔ½�8“¸¥Šáaç„à7ÐëÜŽí7‘Y<�£D3p§^fˆµ] z�ïŒAKÎì'V)î&®!qnÀO¶À¯Þ^%N© 0¾8€ï^ÄÚ�½ÿ7΀3ÙØûC?èö­9йdW�¡aà«Zè³~ÈÁ]Â/ßu÷š"±þ߯Å7©Ã÷à»D,%à(žtÜú_¿Þî•‚¸ËìPû¦Xèõwè8|' z+�;;Ü3ñבG0¯“S°Ü'K�ë~hŽC 8`J,mÜë9ðDàžƒü-°wF®{ŒÜR޼`NôLþ7:Šçnwä‹qxоç�;ƒ› Æ]™†:‘oG€ËÕ6ÄÁíÌä#ÿH¢µD¢§‘;P×.pïzý#ˆŒ…›‡·›ßAàšçþˆØF‚Ą֢=øë¶³Ä6üÿÿSdéÃßÅá³¹‰Óúú ½³Ç ‰¹üƒÖ˜*#óâΛ‹ºrßCKõˆ;ï>QÄ=ìiôýÚÃS·>$ŠAÖ³Áœµ2qìá£ÅÓ‰þ€wfÈjÍÿð xR‘Ìg¢{à¥üÙÀš Ÿ•„`ßÛq[9£ïKȃ'ŽÁ'ÿ#&99 ¼x޼`…ç•+YmÀ 0!=¶@̧ľ~ w·-ò1v÷í!r 8°3Ñ�n܃·¤ƒGûÖ• CPoê¢XÌåø±Ê¤�7 EçqÇ+ lWvué"+�ãæYIì3»ð»èç9°=é¿ ?ÀD1è»òîxë ô¼¿Á}rïÙ†ÌË�;2 ÊBCÔŠˆó ?Û™ <#5"+�‡�ä#ï"§ú<· î%ª`_2ÄiÁì6Á¯7�îðëÈ›fω†±KSp¹£�XË —\‘ïàå[Á -`fÙ9dç…¸_þ" ÏÌ÷.rà)x“ÏMd Ìã²MV=‘Õbäd!öÒ�ì¨~œE .°ïMÐtös`ê7îð­^žÈ/ù¹÷ô»ÙÈwO(4ó˜-fkG¤ ¼ºáæ×Ö\’Ð;�‰ó¹Ê솟 ƒÍ¸+èC3Ñ�44 xhC»=v\Lä myŽâøßAf8Aä/•€þ¾ÇÍ?úÿŒ¦@t9ʽžMTЈì}<þ» ONFjÁ_îløž4™ Ú'·‰¸ÅÜ^È�È€-³5—&;û¹ >Ex£ybÍ@ 1À{xô[܉ÆÐú‡¡}ÐÊmཷ9qj‘EKuð¼O¸ë"qO¼È/®È÷¡˜‰4! YO³4…'÷—´�Aà‡�žà™©È©øûu`í"¼»íØa¢ÄLÂSÍt‰Õ͇ž¡!5ØÍ à2Ùÿ ògËÔ)B$ WÁ-_Gæ×Á>>{á~A.(ú¯†îcÎÀ®´b3ð» õO9C, xn2†ÞSüÛðz©û šï®»­G½nð ,Mâ4@Íb�/ÁÓ|ÜVÀÉ6ä�Ex_82BÂCâø Ç ÔÛg…7À?=èT4ò° êÐF¼�‘÷�->†âž$æ,nß$ÜOð5î ‰¬Kyx÷ròp«4%µ×ÀI|/œ“ÖF®Þ€|ˆ "oüÿÿãÏÖÃëÏ ›!?œC.Â..ý#Öüzb½%ú¾³(¬©“2½ÆÓî9tðkPÝ,�é**#m·w.ûÛ1CÎÑåûÀ–çì+¦ßÿPKC¥Tùò?ÜôØHgpgsigned/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxUT äÁsbäÁsbux èèû_âŸÌÀÀÀÄ€Œ£˜bÌD!f&³ŒbŠ1ëÂl´ÂLibVž�Ž|}+Çñë¬Ù¢½…Ùçƒÿ],*›<ÿø¼ Ñk×}ºÍ9“A,Ù¬%ûê‰^£ŸVOk[ÒšŸIm=j�{~ö÷¹‡,aA3ŒTœ>·2òØî»ÐÅm°]k®×Ûz©'N ùõ¦ì|É!°ã¾]Ãá›Ìß Sl 3Ú…{×øÛî‹Yîóþ_¤ƒç„ù)³ÿ9¨½k/]ÌF!@¬ Äû�î[¤û€4�»ô”K³Ö�u÷Óðcº¨#û¤ÞÏ‹�Å™M&„£À_öþƿ׷Ëï´XPK C¥Tgpgsigned/objects/d4/UT äÁsb�Âsbux èèPK C¥T°Nñ¥&&;gpgsigned/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46UT äÁsbäÁsbux èèxKÊÉOR02a(ÉÈ,VHËÌIUÒ‰ Éùyi9™É%\«Ï —PK C¥Tgpgsigned/objects/ee/UT äÁsb�Âsbux èèPK C¥T…ÒÌ@@;gpgsigned/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfUT äÁsbäÁsbux èèxKÊÉOR021g(ÉÈ,VHËÌIUÒɉyé©) ™y ¹‰Å%©E\(Ò‰¥%ù¹©E驉I9©C[ Fq]@PK C¥Tgpgsigned/objects/be/UT äÁsb�Âsbux èèPK C¥TÁ� ÂÂ;gpgsigned/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644UT äÁsbäÁsbux èèx��Kj1D³Ö)zçUBëÛ-0Á�uV9�¦Õò<#£È÷ÏȲ+ŠW$I¥0<úØ6xS…Oßðˆp�ûs˜úxÄ[jË„r&pá„€+wœ³“žo�þ³Ø+g(ÓLð=ã°ë˜[ âB?D7GëPK C¥Tgpgsigned/objects/a4/UT äÁsb�Âsbux èèPK C¥T�Ñ÷ ÈÈ;gpgsigned/objects/a4/a7dce85cf63874e984719f4fdd239f5145052fUT äÁsbäÁsbux èèx��;j1Dë�mdÓú·À˜ÇŽ|M«µ3`�ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9�]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘ ‹Ò ­Åʉø�U~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù�«>dܶ‘OÞáÒò}í\à8g_ШÂoYrPK C¥Tgpgsigned/objects/fd/UT äÁsb�Âsbux èèPK C¥TO‹[RR;gpgsigned/objects/fd/093bff70906175335656e6ce6ae05783708765UT äÁsbäÁsbux èèx+)JMU0³d040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘—Z®WRQÂðËsCùtå‰k#˜ >-�/yœ.7 ‚üPK C¥Tgpgsigned/objects/87/UT äÁsb�Âsbux èèPK C¥TQVô¡¡;gpgsigned/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80UT äÁsbäÁsbux èèx�ÎQ P¿9Å^@³ÂBibŒzOÝE¥5”~x{Ñ#ø7™ÉKf˜s+hM›ZD€,³·.1û>zOƒ!Œ©ë¬æ>¢·ìÔ+™*g“p2}�$„šMŒN4q"¦Ô6²_‡*¬õ>¸È×ZÂá—_8ÝÆz_ãn˜óö†tÚ#l±CT­m«üAÕ™,ïü§‡úÀEGPK C¥Tgpgsigned/worktrees/UT äÁsb�Âsbux èèPK C¥T&gpgsigned/worktrees/testrepo-worktree/UT äÁsb�Âsbux èèPK C¥Tà"Õ­""*gpgsigned/worktrees/testrepo-worktree/HEADUT äÁsbäÁsbux èèref: refs/heads/testrepo-worktree PK C¥T+gpgsigned/worktrees/testrepo-worktree/logs/UT äÁsb�Âsbux èèPKC¥TÔØìÖ/gpgsigned/worktrees/testrepo-worktree/logs/HEADUT äÁsbäÁsbux èè�ÍÍ !@á³V1w£ù6ÆX‚‰ .!,Fm_KØÛ;|ÉCç¢ø ½#ï¤ÖÎjaƒ8Ek I…ÖA1®…7Ï=… w¦´Ì¾?Îm\[‡T.pTJ(«”‘°C�¸ 3…\ß–ò ›Z¯5þ¸´ê±ôÄܦʸR7¦ %ŸRPK C¥T0`ÅÓ/gpgsigned/worktrees/testrepo-worktree/commondirUT äÁsbäÁsbux èè../.. PK C¥TA�(Û gpgsigned/HEAD_TRACKERUT äÁsbäÁsbux èèref: HEAD PK C¥T ýAgpgsigned/UTäÁsbux èèPK C¥T+is§´�Dgpgsigned/HEADUTäÁsbux èèPK C¥TýA£gpgsigned/logs/UTäÁsbux èèPK C¥TýAìgpgsigned/logs/refs/UTäÁsbux èèPK C¥TýA:gpgsigned/logs/refs/heads/UTäÁsbux èèPKC¥Tq8Õ‚rœ+´�Žgpgsigned/logs/refs/heads/testrepo-worktreeUTäÁsbux èèPKC¥T÷âZ�Û´�egpgsigned/configUTäÁsbux èèPK C¥TýA?gpgsigned/refs/UTäÁsbux èèPK C¥TýAˆgpgsigned/refs/tags/UTäÁsbux èèPK C¥T. b)))!´�Ögpgsigned/refs/tags/point_to_blobUTäÁsbux èèPK C¥Tzÿ �))´�Zgpgsigned/refs/tags/testUTäÁsbux èèPK C¥TýAÕgpgsigned/refs/tags/foo/UTäÁsbux èèPK C¥Tzÿ �))´�'gpgsigned/refs/tags/foo/barUTäÁsbux èèPK C¥TýA¥gpgsigned/refs/tags/foo/foo/UTäÁsbux èèPK C¥Tzÿ �))´�ûgpgsigned/refs/tags/foo/foo/barUTäÁsbux èèPK C¥T€Tn))´�}gpgsigned/refs/tags/e90810bUTäÁsbux èèPK C¥TýAûgpgsigned/refs/heads/UTäÁsbux èèPKC¥T¤wžC() ´�Jgpgsigned/refs/heads/packed-testUTäÁsbux èèPK C¥T[Ï–é))´�Ìgpgsigned/refs/heads/masterUTäÁsbux èèPK C¥TòÅuÌ))´�Jgpgsigned/refs/heads/subtreesUTäÁsbux èèPK C¥TyH¥í))´�Êgpgsigned/refs/heads/br2UTäÁsbux èèPKC¥Tçˆâr()´�E gpgsigned/refs/heads/identUTäÁsbux èèPK C¥TÈŸ1#))#´�Á gpgsigned/refs/heads/long-file-nameUTäÁsbux èèPK C¥T[Ï–é))&´�G gpgsigned/refs/heads/testrepo-worktreeUTäÁsbux èèPKC¥T‰z;()´�Ð gpgsigned/refs/heads/dirUTäÁsbux èèPK C¥T]Î:ã))´�J gpgsigned/refs/heads/testUTäÁsbux èèPKC¥TŒªx()´�Æ gpgsigned/refs/heads/executableUTäÁsbux èèPK C¥TXÍøî))#´�G gpgsigned/refs/heads/merge-conflictUTäÁsbux èèPK C¥T+is§´�Í gpgsigned/refs/symrefUTäÁsbux èèPKC¥TÕ‘Ï–Ú´�3 gpgsigned/packed-refsUTäÁsbux èèPKC¥Tä \Í»ø'´�gpgsigned/indexUTäÁsbux èèPK C¥TýA!gpgsigned/objects/UTäÁsbux èèPK C¥TýAh!gpgsigned/objects/94/UTäÁsbux èèPK C¥TV{ž³ww;´�·!gpgsigned/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162UTäÁsbux èèPK C¥TýA£"gpgsigned/objects/09/UTäÁsbux èèPK C¥T>”%~¡¡;´�ò"gpgsigned/objects/09/9fabac3a9ea935598528c27f866e34089c2effUTäÁsbux èèPK C¥TýA$gpgsigned/objects/76/UTäÁsbux èèPK C¥T~�V�¯¯;´�W$gpgsigned/objects/76/3d71aadf09a7951596c9746c024e7eece7c7afUTäÁsbux èèPK C¥TýA{%gpgsigned/objects/d5/UTäÁsbux èèPK C¥T]EH7““;´�Ê%gpgsigned/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83UTäÁsbux èèPK C¥TýAÒ&gpgsigned/objects/9f/UTäÁsbux èèPK C¥TŒV7†  ;´�!'gpgsigned/objects/9f/d738e8f7967c078dceed8190330fc8648ee56aUTäÁsbux èèPK C¥TýA6(gpgsigned/objects/1f/UTäÁsbux èèPK C¥T–yp;´�…(gpgsigned/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bUTäÁsbux èèPK C¥TýA)gpgsigned/objects/ad/UTäÁsbux èèPK C¥T(Yè]ww;´�^)gpgsigned/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3UTäÁsbux èèPK C¥TýAJ*gpgsigned/objects/ae/UTäÁsbux èèPK C¥TB=;””;´�™*gpgsigned/objects/ae/90f12eea699729ed24555e40b9fd669da12a12UTäÁsbux èèPK C¥TýA¢+gpgsigned/objects/5b/UTäÁsbux èèPK C¥T4ážž;´�ñ+gpgsigned/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644UTäÁsbux èèPK C¥TýA-gpgsigned/objects/32/UTäÁsbux èèPK C¥T<222;´�S-gpgsigned/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54UTäÁsbux èèPK C¥TýAú-gpgsigned/objects/a3/UTäÁsbux èèPK C¥T�:T››;´�I.gpgsigned/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70daUTäÁsbux èèPK C¥TýAY/gpgsigned/objects/e3/UTäÁsbux èèPK C¥TnRcÖ;;;´�¨/gpgsigned/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13UTäÁsbux èèPK C¥TýAX0gpgsigned/objects/16/UTäÁsbux èèPK C¥T¸Õû©““;´�§0gpgsigned/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925UTäÁsbux èèPK C¥TýA¯1gpgsigned/objects/af/UTäÁsbux èèPK C¥T=ÞǪ««;´�þ1gpgsigned/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6UTäÁsbux èèPK C¥TýA3gpgsigned/objects/e6/UTäÁsbux èèPK C¥T´ ¤˜;´�m3gpgsigned/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391UTäÁsbux èèPK C¥TýAñ3gpgsigned/objects/a8/UTäÁsbux èèPK C¥T´ä/ò;´�@4gpgsigned/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6UTäÁsbux èèPK C¥TýAÏ4gpgsigned/objects/84/UTäÁsbux èèPK C¥Tš<-Ï~~;´�5gpgsigned/objects/84/96071c1b46c854b31185ea97743be6a8774479UTäÁsbux èèPK C¥TýA6gpgsigned/objects/7b/UTäÁsbux èèPK C¥T~ðeÆ‘‘;´�`6gpgsigned/objects/7b/4384978d2493e851f9cca7858815fac9b10980UTäÁsbux èèPK C¥T3Ó´1»»;´�f7gpgsigned/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785aUTäÁsbux èèPK C¥TýA–8gpgsigned/objects/e7/UTäÁsbux èèPK C¥T®-áU;´�å8gpgsigned/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0UTäÁsbux èèPK C¥TýAo9gpgsigned/objects/62/UTäÁsbux èèPK C¥Td‚H”22;´�¾9gpgsigned/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dccUTäÁsbux èèPK C¥TýAe:gpgsigned/objects/9a/UTäÁsbux èèPK C¥Tƒ ²)22;´�´:gpgsigned/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4UTäÁsbux èèPK C¥TýA[;gpgsigned/objects/4e/UTäÁsbux èèPK C¥T÷¶�ð22;´�ª;gpgsigned/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63UTäÁsbux èèPK C¥T,\ °88;´�Q<gpgsigned/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10UTäÁsbux èèPK C¥TýAþ<gpgsigned/objects/66/UTäÁsbux èèPK C¥T ³Ø;´�M=gpgsigned/objects/66/3adb09143767984f7be83a91effa47e128c735UTäÁsbux èèPK C¥TýAÕ=gpgsigned/objects/c3/UTäÁsbux èèPK C¥T,b×CÀÀ;´�$>gpgsigned/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbeUTäÁsbux èèPK C¥TýAY?gpgsigned/objects/99/UTäÁsbux èèPK C¥T–d%ßßß;´�¨?gpgsigned/objects/99/1f1b12603e1d78411c1b4042719f964efa7adfUTäÁsbux èèPK C¥TýAüUgpgsigned/objects/6f/UTäÁsbux èèPK C¥Tb2ª´´;´�KVgpgsigned/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045fUTäÁsbux èèPK C¥TýAtWgpgsigned/objects/b2/UTäÁsbux èèPK C¥Tê=«ì‡‡;´�ÃWgpgsigned/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1UTäÁsbux èèPK C¥TýA¿Xgpgsigned/objects/14/UTäÁsbux èèPK C¥T„Ž‘…££;´�Ygpgsigned/objects/14/4344043ba4d4a405da03de3844aa829ae8be0eUTäÁsbux èèPK C¥TýA&Zgpgsigned/objects/81/UTäÁsbux èèPK C¥T–N&RR;´�uZgpgsigned/objects/81/4889a078c031f61ed08ab5fa863aea9314344dUTäÁsbux èèPK C¥TýA<[gpgsigned/objects/9b/UTäÁsbux èèPK C¥T Âî;´�‹[gpgsigned/objects/9b/1719f5cf069568785080a0bbabbe7c377e22aeUTäÁsbux èèPK C¥TýA\gpgsigned/objects/97/UTäÁsbux èèPK C¥T©Ø“§((;´�g\gpgsigned/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888UTäÁsbux èèPK C¥TýA]gpgsigned/objects/f1/UTäÁsbux èèPK C¥T•äŒígg;´�S]gpgsigned/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3UTäÁsbux èèPK C¥TýA/^gpgsigned/objects/fa/UTäÁsbux èèPK C¥T0$Ȩ;´�~^gpgsigned/objects/fa/49b077972391ad58037050f2a75f74e3671e92UTäÁsbux èèPK C¥TýA _gpgsigned/objects/c0/UTäÁsbux èèPK C¥T%‰d;´�Z_gpgsigned/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346eUTäÁsbux èèPK C¥TýAå_gpgsigned/objects/d6/UTäÁsbux èèPK C¥Têõq;´�4`gpgsigned/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fUTäÁsbux èèPK C¥TýA¾`gpgsigned/objects/a7/UTäÁsbux èèPK C¥T¹7Šw;´� agpgsigned/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdUTäÁsbux èèPK C¥TýAžagpgsigned/objects/75/UTäÁsbux èèPK C¥T,†Gžww;´�íagpgsigned/objects/75/057dd4114e74cca1d750d0aee1647c903cb60aUTäÁsbux èèPK C¥TýAÙbgpgsigned/objects/13/UTäÁsbux èèPK C¥T�î£õ;´�(cgpgsigned/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08UTäÁsbux èèPK C¥TýA°cgpgsigned/objects/a6/UTäÁsbux èèPK C¥T³=�¡––;´�ÿcgpgsigned/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750UTäÁsbux èèPK C¥TýA egpgsigned/objects/2b/UTäÁsbux èèPK C¥T‡‘ßÝ88;´�Yegpgsigned/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863UTäÁsbux èèPK C¥TýAfgpgsigned/objects/57/UTäÁsbux èèPK C¥TÙHØ  ;´�Ufgpgsigned/objects/57/43a3ef145d3638a0fa28233ca92897117ad74fUTäÁsbux èèPK C¥TýAjggpgsigned/objects/c4/UTäÁsbux èèPK C¥Tçk—[¡¡;´�¹ggpgsigned/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bdUTäÁsbux èèPK C¥TýAÏhgpgsigned/objects/27/UTäÁsbux èèPK C¥Tà+PÌ;´�igpgsigned/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dUTäÁsbux èèPK C¥TýA¨igpgsigned/objects/36/UTäÁsbux èèPK C¥T/¾;´�÷igpgsigned/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccUTäÁsbux èèPK C¥TýAƒjgpgsigned/objects/45/UTäÁsbux èèPK C¥T#Vaœœ;´�Òjgpgsigned/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6UTäÁsbux èèPK C¥TƒÍê;´�ãkgpgsigned/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057UTäÁsbux èèPK C¥TýAjlgpgsigned/objects/1d/UTäÁsbux èèPK C¥TYßoÚII;´�¹lgpgsigned/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568UTäÁsbux èèPK C¥TýAwmgpgsigned/objects/cf/UTäÁsbux èèPK C¥T/6f‘¢¢;´�Æmgpgsigned/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9UTäÁsbux èèPK C¥TýAÝngpgsigned/objects/f6/UTäÁsbux èèPK C¥TŠ#’RR;´�,ogpgsigned/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1UTäÁsbux èèPK C¥TýAóogpgsigned/objects/18/UTäÁsbux èèPK C¥T$g'ôww;´�Bpgpgsigned/objects/18/10dff58d8a660512d4832e740f692884338ccdUTäÁsbux èèPK C¥T‡ ’33;´�.qgpgsigned/objects/18/1037049a54a1eb5fab404658a3a250b44335d7UTäÁsbux èèPK C¥TýAÖqgpgsigned/objects/ce/UTäÁsbux èèPK C¥T¤PD/ÂÂ;´�%rgpgsigned/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3beUTäÁsbux èèPK C¥TýA\sgpgsigned/objects/4a/UTäÁsbux èèPK C¥T‚”  ;´�«sgpgsigned/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045UTäÁsbux èèPK C¥TýAÀtgpgsigned/objects/pack/UTäÁsbux èèPKC¥Ts¯j¢¢)äI´�ugpgsigned/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packUTäÁsbux èèPKC¥T7h$äÊòI´�6gpgsigned/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packUTäÁsbux èèPKC¥Túo9"ÑëI´�ƒgpgsigned/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packUTäÁsbux èèPKC¥Tƒ|ÓòØH´�×gpgsigned/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxUTäÁsbux èèPKC¥T"¿.°@¶H´�Kgpgsigned/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxUTäÁsbux èèPKC¥Tùò?ÜôØH´�ûÎgpgsigned/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxUTäÁsbux èèPK C¥TýAqÐgpgsigned/objects/d4/UTäÁsbux èèPK C¥T°Nñ¥&&;´�ÀÐgpgsigned/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46UTäÁsbux èèPK C¥TýA[Ñgpgsigned/objects/ee/UTäÁsbux èèPK C¥T…ÒÌ@@;´�ªÑgpgsigned/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfUTäÁsbux èèPK C¥TýA_Ògpgsigned/objects/be/UTäÁsbux èèPK C¥TÁ� ÂÂ;´�®Ògpgsigned/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644UTäÁsbux èèPK C¥TýAåÓgpgsigned/objects/b6/UTäÁsbux èèPK C¥TAk4'PP;´�4Ôgpgsigned/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593UTäÁsbux èèPK C¥TýAùÔgpgsigned/objects/f9/UTäÁsbux èèPK C¥T" $¢¢;´�HÕgpgsigned/objects/f9/ed4af42472941da45a3ce44458455ed227a6beUTäÁsbux èèPK C¥TýA_Ögpgsigned/objects/6b/UTäÁsbux èèPK C¥T≣‰));´�®Ögpgsigned/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602eUTäÁsbux èèPK C¥TŸ[0ǧ§;´�L×gpgsigned/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6UTäÁsbux èèPK C¥TýAhØgpgsigned/objects/a4/UTäÁsbux èèPK C¥T�Ñ÷ ÈÈ;´�·Øgpgsigned/objects/a4/a7dce85cf63874e984719f4fdd239f5145052fUTäÁsbux èèPK C¥TýAôÙgpgsigned/objects/fd/UTäÁsbux èèPK C¥TO‹[RR;´�CÚgpgsigned/objects/fd/093bff70906175335656e6ce6ae05783708765UTäÁsbux èèPK C¥TýA Ûgpgsigned/objects/87/UTäÁsbux èèPK C¥TQVô¡¡;´�YÛgpgsigned/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80UTäÁsbux èèPK C¥TýAoÜgpgsigned/worktrees/UTäÁsbux èèPK C¥T&ýA½Ügpgsigned/worktrees/testrepo-worktree/UTäÁsbux èèPK C¥Tà"Õ­""*´�Ýgpgsigned/worktrees/testrepo-worktree/HEADUTäÁsbux èèPK C¥T+ýA£Ýgpgsigned/worktrees/testrepo-worktree/logs/UTäÁsbux èèPKC¥TÔØìÖ/´�Þgpgsigned/worktrees/testrepo-worktree/logs/HEADUTäÁsbux èèPKC¥T¶o#,´�ðÞgpgsigned/worktrees/testrepo-worktree/gitdirUTäÁsbux èèPKC¥T~SBQùq+´�sßgpgsigned/worktrees/testrepo-worktree/indexUTäÁsbux èèPK C¥T0`ÅÓ/´�Ñàgpgsigned/worktrees/testrepo-worktree/commondirUTäÁsbux èèPK C¥TA�(Û ´�@ágpgsigned/HEAD_TRACKERUTäÁsbux èèPK­­tIšálibgit2-pygit2-d86662e/test/data/submodulerepo.zip000066400000000000000000000467731521317216500221550ustar00rootroot00000000000000PK U‰“Psubmodulerepo/UT ñiœ^U¾cbux ééPK U‰“P submodulerepo/TestGitRepository/UT ñiœ^U¾cbux ééPKU‰“Pð-ýZNnsubmodulerepo/.gitmodulesUT ñiœ^U¾cbux éé‹..MÊÍO)ÍIUP I-.qÏ, J-È/Î,É/ªTŠåâ,H,ÉP°UÀ�ãâ,-ÊJd””[éë§g–d”&é%ççêçd&yFú˜zPK U‰“Psubmodulerepo/.git/UT ñiœ^U¾cbux ééPKU‰“P”#d¶ásubmodulerepo/.git/indexUT ñiœ^U¾cbux éésñ rf```á¸9™ m•xa4PL‹Á ðCãæÌ�'>CQâûÊmÙ7ù«.œÜÁtSu·^zfIn~JiNj1à0ï<Ã$ó4<�´7ÖZ�™èö³gú…Š@#�ÝŸ0†¤—¸g–¥äg–äU2„¹ºÕK2)p5êþóÊû¬xxqÓ¯÷R柙/ÔŸ´jˆk|Ý·/ òLƺ_!]bÌPKU‰“PºŠ¤÷submodulerepo/.git/configUT ñiœ^U¾cbux ééM�K Ã0 D×ö)Œ—éÂ]z’PŠ“È‰ÁŸ É-½}•˜B—�½Aã\Z!ì•"Wü„ŠÙó �b-æn®Z…˜ ׄh5y< øDB©®>%„ÐöÅ3Я6¢\1[1®±X™i˜$uœwGmgKpL? ¼exÞ$¿ˆ‹Ü~!7ÜNè*r]å=Nè‹´möÄ€öü᜻›^Ò*®ÿéz[PK U‰“P+is§submodulerepo/.git/HEADUT ñiœ^U¾cbux ééref: refs/heads/master PK U‰“Psubmodulerepo/.git/logs/UT ñiœ^U¾cbux ééPKU‰“Pƒ†¸‡¯submodulerepo/.git/logs/HEADUT ñiœ^U¾cbux éé�ÈÁ ‚0гNñï&奴Æ^t Óß~L ¥ Š&nãÎÀbÆ |LJø¨:ë$±#§Ps­Ø´VQ-ÙO¾‘Fj¥¥vpp´�àáDË{ùð v½ÿ…”×hà \Š{(ÝÖhP–°Á qå†4òº)E(î1·™bòóÀçtù¹þPK U‰“Psubmodulerepo/.git/logs/refs/UT ñiœ^U¾cbux ééPK U‰“P#submodulerepo/.git/logs/refs/heads/UT ñiœ^U¾cbux ééPKU‰“Pƒ†¸‡¯)submodulerepo/.git/logs/refs/heads/masterUT ñiœ^U¾cbux éé�ÈÁ ‚0гNñï&奴Æ^t Óß~L ¥ Š&nãÎÀbÆ |LJø¨:ë$±#§Ps­Ø´VQ-ÙO¾‘Fj¥¥vpp´�àáDË{ùð v½ÿ…”×hà \Š{(ÝÖhP–°Á qå†4òº)E(î1·™bòóÀçtù¹þPK U‰“P%submodulerepo/.git/logs/refs/remotes/UT ñiœ^U¾cbux ééPK U‰“P,submodulerepo/.git/logs/refs/remotes/origin/UT ñiœ^U¾cbux ééPKU‰“Pƒ†¸‡¯0submodulerepo/.git/logs/refs/remotes/origin/HEADUT ñiœ^U¾cbux éé�ÈÁ ‚0гNñï&奴Æ^t Óß~L ¥ Š&nãÎÀbÆ |LJø¨:ë$±#§Ps­Ø´VQ-ÙO¾‘Fj¥¥vpp´�àáDË{ùð v½ÿ…”×hà \Š{(ÝÖhP–°Á qå†4òº)E(î1·™bòóÀçtù¹þPKU‰“PT² Afrsubmodulerepo/.git/packed-refsUT ñiœ^U¾cbux éé-ÌA Fá}O1‰ëcGoÃ�©4�ñöjâêåÛ¼mA³"wz•q¿ÒT$Ê{­ïù�Þt|3�rŽ�(öè°XøK°qaxŸb:³gg;¡ßÏ(Ö6ÐMÓr+O³†> ÓPK U‰“Psubmodulerepo/.git/objects/UT ñiœ^U¾cbux ééPK U‰“Psubmodulerepo/.git/objects/17/UT ñiœ^U¾cbux ééPK [ˆ“P0šQ3]]Dsubmodulerepo/.git/objects/17/982118f7a9c886506bd90f7ad0c9b802d9259cUT hœ^U¾cbux ééxKÊÉOR044`ˆ..MÊÍO)ÍIUP I-.qÏ, J-È/Î,É/ªTŠåâ,H,ÉP°UÀ�ãâ,-ÊJd””[éë§g–d”&é%ççêçd&yFú˜z²´*:PK U‰“Psubmodulerepo/.git/objects/80/UT ñiœ^U¾cbux ééPK fˆ“P'C×8eeDsubmodulerepo/.git/objects/80/712dfe4a6ef321c3a382faef40701a37f303d0UT /hœ^U¾cbux ééx+)JMU°0a040031QÐKÏ,ÉÍO)ÍI-fŸ¡(ñ}剶€ì›üUNî`º©:ÇÐÌBR‹KÜ3K‚R ò‹3Kò‹*<�´7ÖZ�™èö³gú…Š@#�ÝŸö$PPK U‰“Psubmodulerepo/.git/objects/2f/UT ñiœ^U¾cbux ééPK iˆ“PÇ@ªt¢¢Dsubmodulerepo/.git/objects/2f/ac4becbc608e36e97a6b34e99dbd549486848cUT 6hœ^U¾cbux ééx¥ÍA Â0@Q×9Åì…2IZSADA]Š˜6�4¤4© ·ñž¡SÏàö-þoc>ƒ’Õ,ÌP£‘Ê:.iÁN+ÙjÒµrÄ®Dƒ’´qµEAc¾ÆŽìèî-šé5½ù «›ýAá›~s 仢�a ²ª�ƺZJ˜£B_ýž3ÿÓ[káÌ)ï}>q“Ïqx%Hc¢;“ HªPK U‰“P submodulerepo/.git/objects/info/UT ñiœ^U¾cbux ééPK U‰“P submodulerepo/.git/objects/pack/UT ñiœ^U¾cbux ééPK U‰“Psubmodulerepo/.git/refs/UT ñiœ^U¾cbux ééPK U‰“P submodulerepo/.git/refs/remotes/UT ñiœ^U¾cbux ééPK U‰“P'submodulerepo/.git/refs/remotes/origin/UT ñiœ^U¾cbux ééPK U‰“PÖ%Ô  +submodulerepo/.git/refs/remotes/origin/HEADUT ñiœ^U¾cbux ééref: refs/remotes/origin/master PK U‰“Psubmodulerepo/.git/refs/tags/UT ñiœ^U¾cbux ééPK U‰“Psubmodulerepo/.git/refs/heads/UT ñiœ^U¾cbux ééPK U‰“P†êCŒ))$submodulerepo/.git/refs/heads/masterUT ñiœ^U¾cbux éé2fac4becbc608e36e97a6b34e99dbd549486848c PKU‰“P7‹?Isubmodulerepo/.git/descriptionUT ñiœ^U¾cbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK U‰“Psubmodulerepo/.git/info/UT ñiœ^U¾cbux ééPKU‰“Pw=Í!­ðsubmodulerepo/.git/info/excludeUT ñiœ^U¾cbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK U‰“Psubmodulerepo/.git/hooks/UT ñiœ^U¾cbux ééPKU‰“PéøÊ÷€*submodulerepo/.git/hooks/commit-msg.sampleUT ñiœ^U¾cbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKU‰“Pš ÷Àн+submodulerepo/.git/hooks/post-update.sampleUT ñiœ^U¾cbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKU‰“PD?ó^ÿ 0submodulerepo/.git/hooks/pre-merge-commit.sampleUT ñiœ^U¾cbux éé}�OO„0ÅïýO0{¸{3š˜½î¦À@¡m:ƒËúévñj�Ó÷{ʇ¦ó¡agJSâ%€6»¤™àbü÷Ù'�D|Söãgža»¸Þî¡�ËâEh¨ÕáÕÎ3 è®(&/X(OTàâÅ!DØ<­ áhÝ_ˆ‹ë<(K›‡4T?”#X¬¬7 eÍåÕ‡ 6À¦”cÊÞ i³�HÛ¨Ë@Y¥#v/«Q{K–˜ Žî}ŽÆõmrAÁvºXœÛ=!ëi9.£ßÿ"Š”©ºñÕ�/jcjèÊŠ]Å$k2B,¨6�ïçöóíüÑì~Üìèát28mÔÿ§5ÏæPKU‰“P’Äø–I +submodulerepo/.git/hooks/pre-receive.sampleUT ñiœ^U¾cbux ééuPËNÃ0<Ç_1¸}@[Ê‘*HˆôÒV"½!Unº‘M;ŠÝBü;vÊ£qÙÕzgÆ3Û:®”ZÉZ¬…; :ˆ²*Ò˜ lV«ÊÁ”bCØZ‚ÉQm­„©œ2Ú<+•ôM³Ê·WP& Yˆ¢ø…†“ÂÁ:Q;ì•“hàJd›¤í…„^£¦ÊÜÕ«ì%iÏ$ðã’ŸjBÙàk=h¤¤ÅÊqÒ/BˆK/©Eùù’«°3àUMýš2R;âÆTGÖ¡¯Áã‡Iºœ/ž—³y:™M—÷³Å4åÌ[Ð,RÉ‹ö²Ñ +Ž~áþçEkâˆv¢÷eKÉó¨ñ˜Lø ó¸q(h_êuýÔŒàMÍkS6g9‰Ò‰o¿5­^òÎq{~˜ã±¯Ç•Êa~°"óM%q§£p�Q·,kb¹bPKU‰“P„ìXQß"*submodulerepo/.git/hooks/pre-rebase.sampleUT ñiœ^U¾cbux éé�XkoÛFý,þŠ[E­¥¬¶>Û¸ŽáØA›índ�‹Ä±GäPbMr¸3Ã(jìÿÞsg†"-+ébÈæã¾ï¹�Ñ“of‹¬œ™Uô$zBçªÚèl¹²4ŒG4?<ü~ÌÿNÿ¨ËLÑ9ý, Q*G{¹’Ô¯´œh¹Föi¥Ô-e†t]Òïµ±´�©Ò Zf–"c…¶†••KÉpý»ZŒI” Å¢$ü(KKÒcUü<Õª`©%xh±!ù)³|¹Î,̦R•“?¤V,ÛÖfºµ®1(y.Gîä¦*ÏÕš%TB‹BZ©Í3Ç58¢ÉÄÑÔ•±ZŠÂÝ©3ih- xõ­ô&M™aÞ0,´(ã|f¹ÞÛ„†J“,*»¡õJ–þ1¿wÞÕZ³§žqÔ˜ {�(ªZWjmàÄš¬j㢪,<0É®„%� ‹Ö&ˆ(¤^B5xJùÉ4–¹(.¥µǰ7µaþ’ÌBÆZÕy"Sç–²ŽáA Uõ"ÏÌ z`°Uz3�¢ð섵F,Þ+>éŽúQ–’•ÀDð¤O'4�‚2ê9wNúZ¦f¶‚d3Ìû‘Ì�lÞÝ0z̦X¨<‹�µ”~~yvqCwwQ��@‡t ƒ% $°µH§®+Û�H+b¶•9£4‹¢ÖÁ§¡–¨cÀééìé(êGüïë*¼Ð@SK#â±{�„�–“’H‘7@%ñ w�6Uò{R)Â@þ”è•A6èÍ/œKOˆûSÖq¡\îe‰œä¹« ƒ÷.`€� Öä¿­ŸwwôÅ+EÏ¿›Sÿ5J¦fS¼E�,ø|ݳ&Øàu¤5«hÁå­<��물ö·>[€êH°( ÀÖÚ͉*%!éƒ'Ûšt³ÅÅä<| « ÒÚ뙸¢÷sŒ Z¯e¡>r°¦[oBclv¼ük×j�+H-QfèKvê0Òñ¥§;Úà§HOJFÑFÕmËØXÈwЩ2ß\3ÃõÑNp¶±øÐägð9”Ð=ÝA´¶7öùØÛÏö¶ê:vpýuÌ›H‡ðwên«­5Ò«½‰zû2× ÈËÛ›º¦ƒÔ‡2Vúòh’Þû¿Öó-#”kÔCý“`;ú—àì°hoVífd%uNIÐSlB¹ –ƒ³·?ýúîðê8”%™–±E$²²¹Ãò%½Û¾#Ë«YWÃÖV u O�+“©k–Ü)KéQ¿¨3”u;ó2Þl X³Ì/…Î3îºÀ’¸•"˜×�,ŠQOÐÀk!ÇÅí(N€›Ž™Η�q0Å¥ÓuÌI«3„RÆÒ¡7®Å ÖÌ ÆÀÄ[)+öD7‹™Œ�áÃöúž]_]—D�+´`ø;æ8— Ç$ '•*ÄÓH,_v‹ÚOx6íT—[š�3l€­æ4ÀsNyp‘HS �3çY+©ED/� ÅÕç3²í �@:lJPtT°Û7¢áükQuÝÝž¶µá_òI…u±¾€Y2Ñ¿8D;6 èÍ7ã�ÝÇqï)l�º>Y›í øl¸’h‚®eî › d»èƒJXb×ÌPX\MŽ½í…ØÊ²N»IkíÔ‡’‡È†]1(çæÑQ4İ_‰Ê´‡FôéŠg„ÜádTxåqè[MÊèŸÒÊy^ë‹"T(°ßë�GM&“¿üÐ;ŽYØôšÿ�®™ŠpòwAg{‰f;ÏöÐtžò7†´í—^<¦œu¾íçý#Â.Ý™xþ€2ÐÍèë"g.êÿKÛ@6s>ŠÎÆp�+üÜ�Ó6ßÊÏ\½`7¤4û„&J’•ã7ØÐ“Ï Ç“f¥«Xžzö Ux›¡Í¦„nÌ~ñ-ÿ�âJÄ÷êf’:=çN*w€ÝÞÈPò̭Xît]¿CŸyÛpHäóÿ™ïx²Ä­ÅRF/)¾ä]T5úZ7ƒxï�äƒ�¦û`?Eø8 Ž@Ô¹/ ²ÇÔ.ͱ·0yë^טùccÂzêgkPâǾû=¦i_n…Q³þý PKU‰“Pí60èÔ2submodulerepo/.git/hooks/prepare-commit-msg.sampleUT ñiœ^U¾cbux éé…TÑRÛ0|n¾âp2„�È.í[™t†RJ3SJ‡¤o‚bŸc[r% ÃÇ÷$9i ™6“GöîíínÜÝK–B&¦èt;]8‘€¿xU—…R·`R-j VA­±æÁ©ª*a¡T+¨Ð¾Â˜°§¼,1ƒåD+ºŠà^ØÂ£$¯Tî¯sQ:*n¡àÆ�¾emG�«²T÷�Ña2 j„’kš!l ê0ªÑ)Ƴ"ì@gu£ke„q›`FSv¡½*BN¹7 ç¢4aRIöˆZ�±Ü6fDÐ-;ˆ�/•¶˜ÅÞË�’|é¥{Žn½ þ$˜  j½e�ˆUfÅǰÆ��iÙ�ÔˆëŒLì·Ì…6”Db¯Ô® �ºð­DN‹£´¨·µ®c‹ã ,ë­ý\ƒ©’Ùö` ¯±uc�ù>áLä90æbÁ`:"¼�´•l²üÑ�Â%æªm�ÇLÔÒ:ã�‰mœdŠSÞ¸ähšB#û®W5†@£É¤QéðÛül¸)0ÛtÉüY‡ ×Ù¦Û<Ë(,˜Š•ÄŒ©­É©…ZÀ{<ØX‚ËyÔW_8Cï°ß;„>ÇèB!"‘Øî0Áæ([J8˽ƒ¡æVˆ&Zfóêôáæ—§°•TAî=;H9™/¬ˆ-Ov`¬à‰&ŸFÁ�3j.Õ[ÆK?@#ØÄÔêçrÓv×DO BÞã¶Yq+‹7‡ÍÄÁ‡¹¬˜’ÝÈSØy:Qž Šÿ”fm(}½ÞDõJ©Œž 9CÈSQç ™½êî^&‚ù4ÅVB0¦9eÙ�h}ö6ЃVŒÄ` » BÃr)�Ãþ¿Ø}??êîMÿ¨õ PKU‰“PÏÀL ¨.submodulerepo/.git/hooks/pre-applypatch.sampleUT ñiœ^U¾cbux ééU�[N1 Eÿ³ŠK:ª@�VüVB‚=°�ÌÔÓDL&QìéÄÞñôâ÷Ú>ÇöânÝÆqÍÁ,Ìo#èèS!çpWcHÆžjìO8/ˆ ßæéœ·„.§Eh«€ö_Êp*^º€¾æ¯L—|�ˆÙïhuV½‡›#äiت7 QÆ<ºOª,^&uõBU¥<Åq§8VGÍ¥F/t£"ö˜ ~žcÉnë]¥4úVÏ“ WÌþ'T�Ò5éã\˰¥’û»Ä®ŒYaÅqpL2£ ò‹mîµ¢˜½+¾2Á¹¹SÃÙÀëvi~°FˆîÛü",–K}uÿÃæëùÑ6¯öÛlÌPKU‰“PªŽ· 2submodulerepo/.git/hooks/fsmonitor-watchman.sampleUT ñiœ^U¾cbux éé•VÛn7}–¾b²6 +�´vúR¬b7�siŠÀ6ri Ä�AíRã¹&¹–Gùöž!wåKR´öC¢%g†gÎÌrëQZ;›N”N+iËn·v’œ·*÷ãð{)¬Vzæâ×›“Ã,;®¤~2îv·è¹&y%U)inÌ9¹ÜªÊ“7¤´—3+¼¤OÂçó…Ð0ß™{_¹,M§"—8ŒfÊÏëÉH™tÙ˜¥}Zb‘°Ãq\%eAuE…ô2÷@‚8Z.I肦PS…ý©*¥u·°÷aÞ`QŽ*áv]Jë”Ñ´“×ÖJíËíõCA^-$à’Ú8™]8„™»Þ³·ƒ3¢gÁÃÔ¾ª½ à|�/e�Ÿ Osq)i"%g¼Aè”Î%ö%»”:œ:¢á玵óp +Ká±Ë¡ÙÒãÉL†¿–Æž3o¥ @œ¬3\Ðdű‰:}|ÛaHj1)ùTpÁœ p„‹f…!óQÉE-íjØ i‚{Äèq@ÉTÍ🕣©[­¼±Ä¥K9¨Kïú÷púbE;Û éÚædû´OÏž¿{ýghœÃ¹ÌÏCZ±VèËMÑVªÛUÓ›´¿Ïõºîv¶ Výír…R4•ƒ…«'ÞŠÔiÙ¬³ÅBœã³¶ÜÒ) •³Ò×V‡"æs¡gAb. )íí¶}ÒÞ¸»&Yb&«P’’�ÚÕUe,W$RrÃVÈ²Í¦×æÕ�‚îQ·CøK^x"˜¤” Í£7±Žë2¯ÛàýŒ{áŒayúrLûß©·p+×£oߨ]ÈW³¥Ò½@Ý]G$÷Ié_ždÙké—ÅNü£Éw4[zzšž¦éíl­¼¨x„ÛOœx9Ëò²Á Ží [ ­[ŠZçó³¶lÕEÉèÞ:êvØ·R< kÎÎéãÃßß¾8;þøa@Íï7Gêmœ†_i8ÔfXáD¿êõ#µ(@¨P Ò§©@ëm?zÿÜ[othÚP^HŽq¡¤Yš%-e´æ BkE!ö×B²”p×zô¯vJr÷wnes1Å”°ÿ�‡6TIJ£œï#—-¶Àð‹<7PfåæaÜ ºÚAI8>¡™Ô:¸HšE›ÃÊ\ê|…!(äUœ±L ¥PK»6²b¹H reá6+Õ‚µ;‰jÉËAnBt¹5å�TîÓÓ§IçåÑ ðÜé|Ž’” (¹Û]XAÿu: ô,21K ‚Œ>'|Jò7V×øAqTey–›ŽiNÅQy i—ÇjZí¸• C×¥ÉEIÛ阞¶Íw°æn-ÕÞn„i±`*¢ž \¨MÌþSNçò‚&ä¿NRúR”ª½š²›8¸ÖèPw;ûï”~9½NÇM¡¾:£ÏªóðH Ñ*Áï��²ì¯÷\¼�!øKÚ�PVLg†ÑüÄûää_¼ONØ{Ýâ0ØÙX ðÔ~úëð €Örg“+K'ècÔŸÚ Íºm†×ÒZc×?|#ëEZ7÷¦ÅΔ—ÍUj ˆ˜‡Cþ}q•ÞÜYÁòþ\¤lÚäUжͅwÏï>Œ¶5ÿwoþF�öi—‰Åpºs‘Šr)Vîö}UÏDÍœ*‹gNwg‚cv6‰‹qµP ²ý*¡i)fŒ†ß"\®ÂDq¹ †ýÊ)¶”ž`l->&5ÄÙ�p~EéžÇÃpüùäÜÀ ï¿æÁYòM�÷¦  Ò§.UQ‹˜ËhSÝ$=Ý eŠ£pÿò³»WºËÝû£^Ü꺇 Ä�'ow<3%w\¸“ŒÇ�Ñ5Ò5àYŠx#úg×!D(ÏzÍ·ô?PKU‰“P!D%u&submodulerepo/.git/hooks/update.sampleUT ñiœ^U¾cbux éé­WQo"7~fÅÜ‚Â�`IÒ·äHuMÔê*Umî©:Efñ²n›®M¸½¶ÿ½Ÿí]Ø]8H” H1¶gæ›™ofL÷Ýd&äD§A7èÒGIü+[®2N©R�¤ã\¬ E³LÅ�´–LJe˜ás2l¡)ÉÕ’¸4<rAÃ-Ë2Î ÂPÎc.žøxÅâÇ�6¤ÄòÅz }…ÓD²%'�²‹±Êæ~!ùÆ�¹WÐÌfÀbR¡ Dœ„ÛI„=S®Ws@ #'v«d"XŒÝ +©#S›š8 º·zfJeœIÒÜhڤܤ<ßwu#²Œfœœl Ó¸êôä|¥´0*/"¢Ÿ šó„­3cÏ Ú(Ù7�šXæ<ã†C÷ $îÂû=¯±Ts‘§A0k�–¬°¶��€q– ó眡dT3êÔ�G}ŸåLÆésÝ÷·ù[†`ÎeáÐ?JΗÊð¬(=>„ EÉ-ë$˜Á_é…%¯'-¼\29§LH”52 {a€ÉùÖ—a€2ñëÂJð–pt)�‘П4þFaï—O÷wŸ~é˵E$ƒ�S”Í�‹G¾–KY쮤-ð¸# éæì²¤÷P^¨5m˜4#·ŠÕÚÖïzµBxJƒÎ˜ÕߦÞ9}€[7ôÁûƒ…wæfPÝü ê\‰jn”‘i¬ü†—Þ}÷JöÜ\k¶àWG¬î­òà:I»qL{ïmk‹Ý).Zª|§Ç ‚=¶Ÿ’®ßm‚‘n_mØFñ>Ï0.–‚ÛÎpJp{q`çÈG‰ÊI*pÝ“ Í!Xåê/»�u;æÔ¿ø»¿cè¤v?1Ó‡5ÁE„Ÿ¥åÁ¼VYá�þ¥"eʇÃ!ýæÅêüÄH™ö}€»ònf¿s}pÍâ-œ;¦Xq��±d™­Üs|¢(²ÿFèu}Ô²+á&¦�'¶@º(øÆs5 ÏŸù =íw”žbmUÔÈí�,´©·ðLóæA™:fÆÎõ±©ðÏËW•5ª :aìÎôÄNžÉpä�C”;]ÌÇqc@bS§*7UÃêýS®ºÝ�Žÿp­t­]+!½ƒ—&_óš“�ZBïÑ“ÚVGÔkXÙ¼à¼9°WãŠÏw]ógD¡oegº ›Æš¾ô�ÝØ­º�Í,¹b @ˆvºJötÕŽåP#n>=>n%-|Äê¡ØÖßÉ@ÜUÑåx|¥«` ±�×:Êm±7PÒÙùçÞžz9ÂYqŠnUý4‘kÌÇË›3ký@jáF¿éÃô±y�·¨ÐFG2ö«ƒñ‚œLU ‹mŽ—�‚mª�³«FP%ìµ;¯»p$·Õë�•^ŸÂ ý¦µ�Ø›2/áÛ[áõ/©v¼MŽŸ »gÕa�CÅ´'÷¾¶´¾Êé¡ÇûQ��JÛ©ñŠÒAŽ!QßÿÑÞ­5'÷ãÆ Ü+ô¿G©6Ò $R ù_>¶)!J»¢Ã‰»ÑlçÝ\kÒý,¤Ð)Þ®îÂyð?PKU‰“Pg*‰f*submodulerepo/.git/hooks/pre-commit.sampleUT ñiœ^U¾cbux éémTaoÛ6ýlýŠ«$ `ÚI?fMÃ˰A¬ö¡-Zëºü¸z|Ì,…s¤Èi ¾ÓÐs=�O¡çy‘á�Ž¥µcñÒ»ÊnÀ~í}“å‹óWqWRýO66>ø¤¬“áæ¿pI7÷ï$n|ŒÔ5:U>´„¦ +t��ɰwÞšŸÁþ±¼ƒ•�Z·TßÂŽÙÆp~-½Üùh“‡9ýÅðX×x›òU¥KùÐù„<ð“KY�  ó`î)vº„•jð nÔ@”qpl²��bpÉ'šž¿dJoîh*ŠNé ]\ˆ>øÄ§ÒÔÃN¾¢52oY*ß#»FF(h&¥æÀ3zkÓe”$Âü¾ö�ÚÌ RçØÆŠƒ<}ô šéæú2Ò¢�!O{ W3ŠÖ•ÙŽ™Û×^>Àø&£Æ�¡ÒTàCÖåPÃÑnæÎ ÐæÅÁ‰wd/+Uê²F€RÒ+å]sÀ·Ü)4âÝ-I}£ó£é¿¢§Õß˧§»•�V†.?‘ú÷ËçëKúNû’Ty%2^�ƒUB¹÷ï??üñ[ñ‚·´Ä j‡e…þC»×¦!A‚1~§—˜²R‹ê0úÝF,�Wã²÷a;4¶c/ûÐ;ò(N^�’,/ÀAsÄØa7š��Ç9:-Î(ðfœË­Ã8æu*'ßxñ®œž9æÈxk.·p n‹‚èsø“ÌÃ[ˆ4“¼Qo~laÀ¹Ú¾¶h[¶4‹€q6Ø?CõUg œ“t1û½Ò¶'øØme�áéyyêý©¹JÿPK U‰“PíAsubmodulerepo/UTñiœ^ux ééPK U‰“P íAHsubmodulerepo/TestGitRepository/UTñiœ^ux ééPKU‰“Pð-ýZNn¤�¢submodulerepo/.gitmodulesUTñiœ^ux ééPK U‰“PíACsubmodulerepo/.git/UTñiœ^ux ééPKU‰“P”#d¶á¤��submodulerepo/.git/indexUTñiœ^ux ééPKU‰“PºŠ¤÷¤�˜submodulerepo/.git/configUTñiœ^ux ééPK U‰“P+is§¤��submodulerepo/.git/HEADUTñiœ^ux ééPK U‰“PíA÷submodulerepo/.git/logs/UTñiœ^ux ééPKU‰“Pƒ†¸‡¯¤�Isubmodulerepo/.git/logs/HEADUTñiœ^ux ééPK U‰“PíA&submodulerepo/.git/logs/refs/UTñiœ^ux ééPK U‰“P#íA}submodulerepo/.git/logs/refs/heads/UTñiœ^ux ééPKU‰“Pƒ†¸‡¯)¤�Úsubmodulerepo/.git/logs/refs/heads/masterUTñiœ^ux ééPK U‰“P%íAÄsubmodulerepo/.git/logs/refs/remotes/UTñiœ^ux ééPK U‰“P,íA#submodulerepo/.git/logs/refs/remotes/origin/UTñiœ^ux ééPKU‰“Pƒ†¸‡¯0¤�‰submodulerepo/.git/logs/refs/remotes/origin/HEADUTñiœ^ux ééPKU‰“PT² Afr¤�zsubmodulerepo/.git/packed-refsUTñiœ^ux ééPK U‰“PíA8 submodulerepo/.git/objects/UTñiœ^ux ééPK U‰“PíA� submodulerepo/.git/objects/17/UTñiœ^ux ééPK [ˆ“P0šQ3]]D$�å submodulerepo/.git/objects/17/982118f7a9c886506bd90f7ad0c9b802d9259cUThœ^ux ééPK U‰“PíAÀ submodulerepo/.git/objects/80/UTñiœ^ux ééPK fˆ“P'C×8eeD$� submodulerepo/.git/objects/80/712dfe4a6ef321c3a382faef40701a37f303d0UT/hœ^ux ééPK U‰“PíAû submodulerepo/.git/objects/2f/UTñiœ^ux ééPK iˆ“PÇ@ªt¢¢D$�S submodulerepo/.git/objects/2f/ac4becbc608e36e97a6b34e99dbd549486848cUT6hœ^ux ééPK U‰“P íAs submodulerepo/.git/objects/info/UTñiœ^ux ééPK U‰“P íAÍ submodulerepo/.git/objects/pack/UTñiœ^ux ééPK U‰“PíA'submodulerepo/.git/refs/UTñiœ^ux ééPK U‰“P íAysubmodulerepo/.git/refs/remotes/UTñiœ^ux ééPK U‰“P'íAÓsubmodulerepo/.git/refs/remotes/origin/UTñiœ^ux ééPK U‰“PÖ%Ô  +¤�4submodulerepo/.git/refs/remotes/origin/HEADUTñiœ^ux ééPK U‰“PíA¹submodulerepo/.git/refs/tags/UTñiœ^ux ééPK U‰“PíAsubmodulerepo/.git/refs/heads/UTñiœ^ux ééPK U‰“P†êCŒ))$¤�hsubmodulerepo/.git/refs/heads/masterUTñiœ^ux ééPKU‰“P7‹?I¤�ïsubmodulerepo/.git/descriptionUTñiœ^ux ééPK U‰“PíA†submodulerepo/.git/info/UTñiœ^ux ééPKU‰“Pw=Í!­ð¤�Øsubmodulerepo/.git/info/excludeUTñiœ^ux ééPK U‰“PíAÞsubmodulerepo/.git/hooks/UTñiœ^ux ééPKU‰“PéøÊ÷€*í�1submodulerepo/.git/hooks/commit-msg.sampleUTñiœ^ux ééPKU‰“Pš ÷Àн+í�Œsubmodulerepo/.git/hooks/post-update.sampleUTñiœ^ux ééPKU‰“PD?ó^ÿ 0í�{submodulerepo/.git/hooks/pre-merge-commit.sampleUTñiœ^ux ééPKU‰“P’Äø–I +í�äsubmodulerepo/.git/hooks/pre-receive.sampleUTñiœ^ux ééPKU‰“P„ìXQß"*í�’submodulerepo/.git/hooks/pre-rebase.sampleUTñiœ^ux ééPKU‰“Pí60èÔ2í�Õ!submodulerepo/.git/hooks/prepare-commit-msg.sampleUTñiœ^ux ééPKU‰“PØ�±�D(í�)%submodulerepo/.git/hooks/pre-push.sampleUTñiœ^ux ééPKU‰“P…Oø Þ.í�((submodulerepo/.git/hooks/applypatch-msg.sampleUTñiœ^ux ééPKU‰“PÏÀL ¨.í�§)submodulerepo/.git/hooks/pre-applypatch.sampleUTñiœ^ux ééPKU‰“PªŽ· 2í�+submodulerepo/.git/hooks/fsmonitor-watchman.sampleUTñiœ^ux ééPKU‰“P!D%u&í�;1submodulerepo/.git/hooks/update.sampleUTñiœ^ux ééPKU‰“Pg*‰f*í�6submodulerepo/.git/hooks/pre-commit.sampleUTñiœ^ux ééPK00èý9libgit2-pygit2-d86662e/test/data/testrepo.zip000066400000000000000000000465521521317216500211300ustar00rootroot00000000000000PK ;fQB testrepo/UT âà Q$Æcbux ééPK -fQBÙ÷ÚŽtestrepo/.gitignoreUT Åà Q$Æcbux éé*.swp PK s�N>¢§ÓÝ((testrepo/hello.txtUT y_YM$Æcbux ééhello world hola mundo bonjour le monde PK »]sTtestrepo/.git/UT a´5b$Æcbux ééPK©]sTš”X¨’¸testrepo/.git/indexUT =´5b$Æcbux éésñ rf```á$Ó-–æ•{*>ÊŠ ŒòŒ �K˜_B0›py¨ß¾e?»tJ›÷DMkÕú¨uˆ�K/=³$3=/¿(ª—fžod|%’y hæi,U8äÛúûOc¬Edø»+"l§¢Ú¸83RsròõJ*JŠö­?:½sgZ§5Kè¯)/Ý PK s�N>testrepo/.git/refs/UT y_YM$Æcbux ééPK s�N>testrepo/.git/refs/tags/UT y_YM$Æcbux ééPK ,fQBtestrepo/.git/refs/heads/UT Ãà Q$Æcbux ééPK ,fQBA=Ð))testrepo/.git/refs/heads/i18nUT Ãà Q$Æcbux éé5470a671a80ac3789f1a6a8cefbcf43ce7af0563 PK s�N>ä[­ÿ))testrepo/.git/refs/heads/masterUT y_YM$Æcbux éé2be5719152d4f82c7302b1c0932d8e5f0a4a0e98 PK s�N>¢Tž{))testrepo/.git/ORIG_HEADUT y_YM$Æcbux éé5ebeeebb320790caf276b9fc8b24546d63316533 PK s�N>testrepo/.git/logs/UT y_YM$Æcbux ééPK s�N>testrepo/.git/logs/refs/UT y_YM$Æcbux ééPK s�N>testrepo/.git/logs/refs/heads/UT y_YM$Æcbux ééPK,fQB’M}Y0—"testrepo/.git/logs/refs/heads/i18nUT Ãà Q$Æcbux éé•‘MNÃ@ …×äÞ#"Ïd~« U€Š`Á¦âÎØQƒš¤J£B¸gàbL6¨xågÙzŸmÄó(Ib+¤£6dêˆ(^”‰&:Q�©­�Þ‰UðXÂ[†‡š>?ä–/|Ò«v"ÚÏeºkP:wGœ‚KTˆõH}Ú.àvš„¡‡::L2g[;"ÒN‹³–ÙmµØ ªÄRbï côü?Fïsµk§lh†­ìv´=löÔ·‡mq¶·‘dª)`^ß(&ÍÎúÀ5M0ÑkL ÿ‚Œþ‚\�’¯[œmm�Gr^åNJ•±Qä($iêÔ˜*‰§­«à©M‡îe> °<éò;_­Ÿ¯ndܵ}É’9+§*Ÿg~ss~w=K9½M@=C/¯ÅPKs�N>Ëvaîø$testrepo/.git/logs/refs/heads/masterUT y_YM$Æcbux éé•ÐMJÄ@àµ9E-!Tÿ§‚ˆ‚ ¢º»:´L2’‰ã­<ƒ3*îb­ªÞæ}â¶N’²Ö¤-ÛHˆDY²äE] àÅ)xláŽ�5ÃCä�wyƒ«çüußÔ…ùåÔ¦ýx JSP�:êàâÙšŽu�ó:Õ¥òû:øÉ›ÍíN¢ˆÄh4ÂÄE©¤.jë¬ÏÞå�1Û™ž<û‡ÙÃmÎÐ+u˜ö³@©;i6Wë(.(RNg[:�‚AUB2:wâ ²e”õ5ÿ0ü5Ž2UuSOßûÈY ž`–ô:êQÚæPK-fQB/r¸Eä§testrepo/.git/logs/HEADUT Åà Q$Æcbux é鵓݊1…¯�§¨KEl*ÿɰÈ*2¢ 7‹PIªg¢ÓÝKOïèøV>ƒ/fÏ, ëÅ’ÍU¥H8çT!Ö Ä)&¤&";:è`Y´:œe#à}oèX2¼‹ôó‡«Ïù|¿.Ñí©IC÷„ N¸àƒ‡ç(ŸÌÝ®Lð´ôe*´¶†MÜ÷WÕêÿÓë­ø�¹ãôe¸›ÖÐ ÇÒo¡‡::L<Â4@¾¯‡µD$­dkLÎFK#Ùx¡’gC);ç‘3——Á:÷ÀÓ5ÜÐ v¼ßÏx=ÜÜR_»Uµ¶æ¤•äqöA‹L2[ã|ލEÐ^'¥×É/‚ èƒÜŒÜ§ÝªZú¿¤”|4õsÖçÌïÓ¯OÝpdæ•D0Q+��¡M>Jm´ÍV)a�RË`�þËÐW9C³�·iÛ#C[ö¼ª––‘�A™uëer e gOò¸:ß›K}½ùôâ5�ûÒ7™g`e…@á$.Ùûj£’ub~II9ZA–|â6¦V«ÄŽZ4V-žÿ<œÊ™ól,7Ó· ¨ÏÐó×U5@uËIÍ’åúPKº]sT Ö!½œØtestrepo/.git/configUT _´5b$Æcbux éé5ŽÁ ƒ0 †Ïö)ŠGëØQØ“ÈÕ¦¶PMIÒ�o¿8Y _òóÿ™$x›Ž "gA:"ÒæåÄwû²ÓÅ\`ÃJB L7{:!úÂJW_ Al5xþˬ„ÓT7ÌDê"`{¤¼æ½×ØFEoI¤òèÜš%µù¾àæJž•ž®ç¸kÓ7@–¤ò›F±Kà»aüÁåÌîrvƒùPK ,fQBtestrepo/.git/objects/UT Ãà Q$Æcbux ééPK s�N>testrepo/.git/objects/pack/UT y_YM$Æcbux ééPKs�N>,t¬çé¸Ltestrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.idxUT y_YM$Æcbux ééû_âŸÌÀÀÀÄ@]ÀHcÌDfÆ‚Y¨ˆY)Äl80;æ€bN,˜k˜bn<˜g„b^*b>.õÛ·ìg—NIcóž¨i­Zµi?-œtå‡N1ÓÆ“uûâ¹¼øf˜µ¸¼¹¾v•�·Ú…SïygËWßÊ·–x*t{ù<Ÿf†»ë6×÷u÷®õ;b1«!#EvѵðÞ ŽSZ¦+µœˆÛ÷n·û„SŸÊvþéV ÉM6L5Nù¶ÞjÚ''¶IºégJ"]¿‘vÉX½L/Œ‡o�ñ´ë&›*ëÜ´²OÈZ¥¦—{Õ)èi£M_ÔÙò†·œµùœÁõ™Ÿ/êZ† òHÙ7qÓÍ¥ ‡|[ÿiŒµˆ wE„íTT÷š7W_iª,ÙÉðü¡§gž¼wøô¼ÀW‹�äî>©×ö—Z¬oгy:Ó‡‰kþN.9]9UäÊéM>ñç_§mråÒ˜÷(Øæ©gývÍÉE�sÌy’Âä�½¢ýš=]p@9ðwãAý¸eü†Óg…˜ËU>÷Z=½ni½ °˜I„Ä‹�ýXl=âZ ˜®Yº�r—�â‹€¸XLõŲ€lþeŸ,ï~x6Cª�·•}íÚ0�O>!/®´.ËŒÓÏ[!ºØ}g%§ÜËß'PKs�N>‘¿V{ÏMtestrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.packUT y_YM$Æcbux éé ptöf```b¾ Bsæv[g9 ¸V¯M²g[|ÞCÊ‘CóŽÊ×3‹HþÉiò(›jš’\©·©1dz±e:_苵-2²a'—æÜ8ÑY±fãõu“ ~÷.ÚùñB©SH`èĹzÛŠ¾[z¾œúNhÙ4§Ͷχñ„ÌÈYÔ²nc¾fZVÒEQ�›Õ‡¼Úî?iõà)ƒû¿)îÿ5¯ÅmæÜž;ÚÉšß6W^?œ£o95ÇøÐ=«ì³ßâ¦Y~Ý¿·òêÇe Í׫šî>ʸ;3~bN¨ÈD~�7¼¹ˆ0|·ßêùBàÂÉ«}Å͵ìÒ;¯2íVY¿Ü.¡R+>.\Øòþní¶ýOLTÖÙà^"ojüw{ùí[zlû{«™¯-°ŒZÍR1ÇØÄÀÀØ0ð‚÷y�“>çOë{¥ èñ½½ýMút?ÏäÏóVd®�’ßÊ'é’ºäÙ“'¯§†L1Ð µxrû×oÿä5®)?vk+å,_tk£*§ì9³Š9—/iëé?`b`cfÜ»ƒ©bÎi�³'O†kœ×?åùðÔ‰óž!'tÏzŸ|îuêüéSúºA'XÑR…C¾­¿ÿ4ÆZD†¿»"Âv*ª�ª(/µœÁÈ~ó—„¼•3eìû½éÏò©Ï<~Äß0 PK s�N>testrepo/.git/objects/info/UT y_YM$Æcbux ééPK s�N>üýÝó66 testrepo/.git/objects/info/packsUT y_YM$Æcbux ééP pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.pack PK ,fQBtestrepo/.git/objects/54/UT Ãà Q$Æcbux ééPK ,fQB¤Æ.°°?testrepo/.git/objects/54/70a671a80ac3789f1a6a8cefbcf43ce7af0563UT Ãà Q$Æcbux ééx¥Ž1N1©ýŠí#N^Û·Þ“PQ„ŽŽìÚ‹8)±ÑÅ@ò{D¾@7ÓŒ¦ôóyfÿ063XrŒJfù�4õ2ÍL’0ÖÀ¨bÊZݧlÖ$+)ò"ì‰)a•PiÎ\Õ'\§%‡À©°“¯ñÑ7x]K‡ïÞàÅn—O>Ýùp|{|¶í´¶©Ú0¢ÇLv½wå~:ì? 'µZ½Ù4®¤Uhöã~XøMtPK -fQB+is§testrepo/.git/HEADUT Åà Q$Æcbux ééref: refs/heads/master PKs�N>7‹?Itestrepo/.git/descriptionUT y_YM$Æcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK s�N>testrepo/.git/info/UT y_YM$Æcbux ééPKs�N>ÚÖXttestrepo/.git/info/refsUT y_YM$Æcbux ééUË1Ã0À:þŒ!éxh’"),ÿ\gûÕœZ`jhÊá­vÄ eS¨uèÄëʵÏwzìóÃø2²v6®º ³’Á“¬H ë"W§´¿øõ}çuw=Í!­ðtestrepo/.git/info/excludeUT y_YM$Æcbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK ,fQBƒ>Úktestrepo/.git/COMMIT_EDITMSGUT Ãà Q$Æcbux ééadded bye.txt and new PK s�N>testrepo/.git/hooks/UT y_YM$Æcbux ééPKs�N>i 0›ß-testrepo/.git/hooks/prepare-commit-msg.sampleUT y_YM$Æcbux éé}S]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘Œ$·”É�ç$9!2y�ÏÞ½½ÝSöº\J]ºn’M2¸Ð€?ŪW�1à*+{Þ@o±Áw•Y­¤eZX¡s¢Å‚°—B)¬aù ¬¥·é#OÒw¥Å Á4ñÜH¨„‡N¸P!üÈ:2æÐ¥ÌSb ˜“iô†&A;‚fœl…À¼K3P­lo‚ta¬©Ë¿ÐQ!¯y4 R¹4ƒm4ÿ…Ö€óÂ.'èŽÄ.–Æz¬‹èåœZi±ŒƒÒ»@—ƒÅhC¬$ °Ñ[žˆøÊµ¬˜† ¤®Ô@Ð"n2rã˜�´.ŽÚ;0ƒ-e—F7JVÞ�0 >'h^Ûnd�Z‰Åaet½Û U?DTÌ´–Mœ‡xò¸e„—šæ€mzß’´ÄÆŒ;ñ ÃFÚ`u°mÌž´‡°±CÈŠºtzë1EÀ9u&�Ʀg÷c®Ãz»=îÏ8d±­·Û,êšâ�Ùj¬¹iN[¥¤Ž¼·²šhœ—J‘¢¸2+¤Dèo).õL„­1dY�‚«éfÓ·ùô#G&�ŒÎèPÎÆ«†úz´ ¸,–â8‰˜¹ò¶Ì Ì�deV‚l ¼ÝXBQ•Oé&’Û3jtÌàô4ìI¾öHS zeÿíENÎâ'ô‹DÀšA÷;ùV¢"Sÿ ú~’½JªHÝÞLãÞÁÙ¼ÙpxèDäÝ\¿?›îþGaáÓÕüîâÛüóõ×»«¿ÌaM›GÍt4a±_ž/ŠÃiù"«X—ý,Œ×Òmþû�5K�×kÀª£Ë”jçç±<ù PKs�N>}«.íU('testrepo/.git/hooks/post-receive.sampleUT y_YM$Æcbux ééeQËrÂ0 ¼ç+T¸6˜<I‡a¦—ÎôÞPl™x vÆv(ýû*†C7K«]yWë'Ñ+Â�­³5¼Z ŽÓ…`pî Az3EÐÎCV“ 1÷$É\it%7‰øñ}pM?[@ÉÃÌ'”g0JIS$K�•Ъ´ËË™èüWì‰,Ì“BÞ¼'á CX¸þ4�ü•Æ2×»ùÄv DÅujÑbaäÜEyºá`éóþð¤-Žtdø��>xY¦ë]U�*úŠêŽöºÑ][×…Æ]ÓÕ�RR5Xn¡iõ{]ãeÙn÷mÛé]ÙËm]‘TUÑï;Ý5ÛÒA „*ˆg’â D ��Þôb >ˆ”9BHy.f+ݸØL~,Ý"\Œ¥%0Vð´x¸[5|¾èþÜc“eë ˆ9x>6zÊIq21—Ž‹ßËsÑ\²oPKs�N>éøÊ÷€%testrepo/.git/hooks/commit-msg.sampleUT y_YM$Æcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKs�N>%[9ýŽ)testrepo/.git/hooks/pre-applypatch.sampleUT y_YM$Æcbux éé}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PKs�N>`÷‘X Ä)testrepo/.git/hooks/applypatch-msg.sampleUT y_YM$Æcbux éé}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PKs�N>hÓ†9Ý[%testrepo/.git/hooks/pre-rebase.sampleUT y_YM$Æcbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘ªüH•29MCW±�%ƒx.Þ~÷Ïw'×gñyaWåg Å£ƒÇÏ»&- ¼Ú¦3<_ö£˜á:æ� °�—p¯7û0|w2ù«œ¤×ÑŒé{CL¯ót:⻲Už¹áì}9GóN¯ý+Œ0bU® îDîÉNŸ�Apï]�<˜{ÿÊž_�FAp1üÆ·"KƒŽýŸ½ÂÉ Dé!’zZŽUsß« ÒIÿ¸zýíÛ·>ŠÁ€^].ÑÌÚâ uŸ§×1„ž ßã¶ö»&õŸöœ¦ùqˆ`GÿçGˆîX÷[‰•©ZÕÒ,íÁ‰¾–h_ …"j­ŸßÍÔÀ€.—SŒòÁJ’r}Hkë‚Gr(�çBÐ3ú¹LÔþìá%Æ�ýèøxwå ‹;ž@O‰E˜5Ï"¹’؆*ã¤efTâЈ¨¬lî°É|Iï®s'ˆÄòr×Õ°3†UCè—È©SË©o·ÜkKjfQgh íÔÌx7‚`ÃV0¿’&ϸo‰òNYì˜`Þ<±(A5B/–¿åx~¾f^8_6ÆÁŸNß0i�É|J•(k¥Ùú&=(X3_(ŒØÓ¬Nd3FÛ/èœ~Üt}õ}AŽ`ÜÉZ–+Œ.±ÆEãV¶»ˆ¡ x861YÅ=Õƒ Ë·úä!:þÌ2Ñ˸d4jcÌJ¬Ì‚3 ¢ÔЕC¾��™ÌÃP¾ãŒœ”i¶ª�\ýx)ß½¹ºyýæí,¼�­ìKf±Q‹_Â’I ¬Ç>a#ÝH¿Ó]©7€÷ðtDÓé”ÐFö�ÆýÖ‡‘‹¨ÓiƒÁHá¿P@+Ù~ýñ!Ä—Ÿuu•-wðéXa$Ãß3'¹}]5g�*Ä3i_v‹:ì(lÚ«.¿J>6fØ[Íy‚'¥à"™¦@:g.°VJ3Šˆ^!š«/dd×A:�ôØ”& è(¨`¿±ˆáükQõÝß¿vµá_ñY‡u±¡€Y2Ñß9D;vèÍ·ã½íÉs(l±º>YÛÝ�„lø’h‚nTî › d·*ÃJ:bßÌPX\Mž½í…Øë²N»IkãÕÇ’ÇÈÆm3*çæÑQ4ΰ–•m��èÿÊϹÃÙ*Éð*à0´š6”âoÊ[Êy^HŠ"V(°ßë�GO&“ßýFÐ{ŽYÜ›ÿO®™ŠHpòwA‰f{ÏÐtžò7�´Ý—^=¥œu¾íçýÂ.Ý›xùˆ2ÒÍèë"g>êÿMÛ@6[‚c¸À~éOz�›Hhå¾^°]Rš}B“�%É¿"ÄÊAßh{’çyåyÒ¬ôËSÏ}¡ê"o3´Ù”Ø�Ù/¾åŸb|‰„^ÝLR¯çÒKå°ßjX·¹«½®¶ðË(o÷Ž™ü ÂEèxªÄÊnäJ‰WO_ñ€.ªý­›A|ðHó!DÓÿŽp˜"~ ³5* cßÿ¢Ó4�/·ÂÿPKs�N>ƒlÇz &testrepo/.git/hooks/post-commit.sampleUT y_YM$Æcbux éé-ŒA ï¼b WMîÞü€'?°!‹PKeIUž/‰^»gÚ^¦9–Iƒ±ÆâQÀ;åšAäuk¬ -PCT8J‰�o¼‚ ›s¬ê·Ô¯NrŽç*ÓÂã™{ ¸ÐÜk-tq$¯X;ÊâãáCm·_b�¹ã)}PÞæ PKs�N> Â\*%testrepo/.git/hooks/pre-commit.sampleUT y_YM$Æcbux éé}T[kÜ:~ŽÅÔ I±w¶!IÙÂ’}9�óÐSŠl�×êÚ’+É›l)ýíý$Û[ÒSúæËܾËÌá‹Y¡ôÌÕÉarH+Mü$Ú®aª�Ù�+­êOÐV'b-”v~Bn'‡t§•W¢§º!©ªŠÆÈ€�ÛÎïÈ[f2Å.ý¯:‹âêâ•,/eqÉŵ˜_ÎùÕ¢¨®äåõÅÕUU0/®ç‹¤R °ÞU´3}$$ ‚6æ1r+\©TDp;rìð[N‹�o{Γ˜„œ˜²Ññqöƒñ¼oM=R¼­v!testrepo/.git/hooks/update.sampleUT y_YM$Æcbux éé­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPKs�N>š ÷Àн&testrepo/.git/hooks/post-update.sampleUT y_YM$Æcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPK ;fQBýžÔQ testrepo/bye.txtUT âà Q$Æcbux éébye world PK ;fQB íAtestrepo/UTâà Qux ééPK -fQBÙ÷ÚŽ¤�Ctestrepo/.gitignoreUTÅà Qux ééPK s�N>¢§ÓÝ((¤�–testrepo/hello.txtUTy_YMux ééPK »]sTíA testrepo/.git/UTa´5bux ééPK©]sTš”X¨’¸¤�Rtestrepo/.git/indexUT=´5bux ééPK s�N>íA1testrepo/.git/refs/UTy_YMux ééPK s�N>íA~testrepo/.git/refs/tags/UTy_YMux ééPK ,fQBíAÐtestrepo/.git/refs/heads/UTÃà Qux ééPK ,fQBA=Ð))¤�#testrepo/.git/refs/heads/i18nUTÃà Qux ééPK s�N>ä[­ÿ))¤�£testrepo/.git/refs/heads/masterUTy_YMux ééPK s�N>¢Tž{))¤�%testrepo/.git/ORIG_HEADUTy_YMux ééPK s�N>íAŸtestrepo/.git/logs/UTy_YMux ééPK s�N>íAìtestrepo/.git/logs/refs/UTy_YMux ééPK s�N>íA>testrepo/.git/logs/refs/heads/UTy_YMux ééPK,fQB’M}Y0—"¤�–testrepo/.git/logs/refs/heads/i18nUTÃà Qux ééPKs�N>Ëvaîø$¤�"testrepo/.git/logs/refs/heads/masterUTy_YMux ééPK-fQB/r¸E䧤�ntestrepo/.git/logs/HEADUTÅà Qux ééPKº]sT Ö!½œØ¤�£ testrepo/.git/configUT_´5bux ééPK ,fQBíA� testrepo/.git/objects/UTÃà Qux ééPK s�N>íAÝ testrepo/.git/objects/pack/UTy_YMux ééPKs�N>,t¬çé¸L$�2 testrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.idxUTy_YMux ééPKs�N>‘¿V{ÏM$�¡testrepo/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.packUTy_YMux ééPK 'fQBíA£testrepo/.git/objects/32/UTºÃ Qux ééPK 'fQBËip?$�ötestrepo/.git/objects/32/3fae03f4606ea9991df8befbb2fca795e648faUTºÃ Qux ééPK 'fQBíA…testrepo/.git/objects/79/UT¹Ã Qux ééPK 'fQBR5ód$$?$�Øtestrepo/.git/objects/79/9baaf570f1f03a22ef0af4f2cfc36c836635f8UT¹Ã Qux ééPK ,fQBíAutestrepo/.git/objects/97/UTÃà Qux ééPK ,fQB´�w_oo?$�Ètestrepo/.git/objects/97/33b6ee7f6b3cb0a276586a413c8b281baeb8bdUTÃà Qux ééPK s�N>íA°testrepo/.git/objects/info/UTy_YMux ééPK s�N>üýÝó66 ¤�testrepo/.git/objects/info/packsUTy_YMux ééPK ,fQBíA•testrepo/.git/objects/54/UTÃà Qux ééPK ,fQB¤Æ.°°?$�ètestrepo/.git/objects/54/70a671a80ac3789f1a6a8cefbcf43ce7af0563UTÃà Qux ééPK -fQB+is§¤�testrepo/.git/HEADUTÅà Qux ééPKs�N>7‹?I¤�ttestrepo/.git/descriptionUTy_YMux ééPK s�N>íAtestrepo/.git/info/UTy_YMux ééPKs�N>ÚÖXt¤�Stestrepo/.git/info/refsUTy_YMux ééPKs�N>w=Í!­ð¤�ütestrepo/.git/info/excludeUTy_YMux ééPK ,fQBƒ>Úk¤�ýtestrepo/.git/COMMIT_EDITMSGUTÃà Qux ééPK s�N>íAitestrepo/.git/hooks/UTy_YMux ééPKs�N>i 0›ß-í�·testrepo/.git/hooks/prepare-commit-msg.sampleUTy_YMux ééPKs�N>}«.íU('í�¹testrepo/.git/hooks/post-receive.sampleUTy_YMux ééPKs�N>éøÊ÷€%í�o!testrepo/.git/hooks/commit-msg.sampleUTy_YMux ééPKs�N>%[9ýŽ)í�Å#testrepo/.git/hooks/pre-applypatch.sampleUTy_YMux ééPKs�N>`÷‘X Ä)í�%%testrepo/.git/hooks/applypatch-msg.sampleUTy_YMux ééPKs�N>hÓ†9Ý[%í�’&testrepo/.git/hooks/pre-rebase.sampleUTy_YMux ééPKs�N>ƒlÇz &í�Î.testrepo/.git/hooks/post-commit.sampleUTy_YMux ééPKs�N> Â\*%í�¨/testrepo/.git/hooks/pre-commit.sampleUTy_YMux ééPKs�N>R¼­v!í�c3testrepo/.git/hooks/update.sampleUTy_YMux ééPKs�N>š ÷Àн&í�48testrepo/.git/hooks/post-update.sampleUTy_YMux ééPK ;fQBýžÔQ ¤�9testrepo/bye.txtUTâà Qux ééPK22âr9libgit2-pygit2-d86662e/test/data/testrepoformerging.zip000066400000000000000000000712411521317216500232010ustar00rootroot00000000000000PK ém„Ctestrepoformerging/UT V$ŸRÆcbux ééPK ém„Ctestrepoformerging/.git/UT V$ŸRÆcbux ééPK s�N>testrepoformerging/.git/logs/UT y_YMÆcbux ééPKém„Cz‚SDi!testrepoformerging/.git/logs/HEADUT V$ŸRÆcbux éé͘ÛnI†¯wž¢/A«X}>D hÒrƒ@Ⲻ»Úi˜ƒ5ž˜xßjŸa_lÛ !ÄB¦Å_ŒzFc×WUUw™Òº�€!*î¸é¥h�I'�F–¤WÆ�Š‘× òlr$¯<ü÷/þCÎ>ÆÝýyžVÛEºg„qg˜qÖYò'e”þQžvy"OrŸ§ íÓSr‘ÇõDnŸ7ÕÖS;m5»Ã¼Äði¸šNI7lr¿$i:ÒÁz‘LÉÌöõ°¸æ¨•ŠQI®8*ËD°¨ Dc,ÅH�‰ó`�yÓSò¶äÛ¶àõäí ú¼¾lªmK RX––8H�G­Œ�žJ椕Îpne°³ µ‡ /FìÃeSmú(Yw‚Ìú.×»œßf¿>ë ="z/85ŽHÜhïR°žK%uÔB0­„˜kä^@ŸÇHËRMË~‘¤ÜbSmš{T†9¦x”Éò`åžZb‹B ݬ¤ú•±Ãq‰7<%߬;ˆHü–Œ®ÆuÞࢩf¨–É›²zò·ë�œíî7ëó‹w'/pls¿ˆX€…fŒ2Ã霺¯æPÒPІ•7!c]b ÁL>$)HTi1¸|ç¡ FŒ%°¸˜®'}$=~nªªs0ŸTÍ)®Çׇáj$o Cr¶®ÎñºU‹_ô*¬¦”3w˜ñ>ýµVë%ò˜~p*úQm«xºÂ•=Iù×õ¾P!ML{¦€:ÇF/€j"m’÷6J–yŸÃ4Œä%Œ!9ó#lðãÐf<_v�ÛN êö¤^„}ÓåîÔÞTÛ­V×,À4�ûHþŒÎ�ƒ,\¥àÜr9î1+œ¨=E~Sd¿TÃ’ÊÃ�ã÷Ó°“¦V)�”_Ú�3«AÐ/CŠª4¹ä%zðI¦Ò_�q$ªÈ‚ ÖÌ"Ölÿ,üÛ²,‡"\¯a‰MµÙ£DÔÜiß�è×8u¿®&fšÑêyínܳ2‡O"svðÛ\�„¡OmA˜±�3Ï= *ó¡SeÂA‘Êü.¼Õeî1Žs)–Ab†ú‹kœ©=õù+`×Ça3|B䎶©fxüRØÁŠÃ¥°Ûo*âPK s�N>"testrepoformerging/.git/logs/refs/UT y_YMÆcbux ééPK ‘m„C(testrepoformerging/.git/logs/refs/heads/UT ±#ŸRÆcbux ééPK,fQB’M}Y0—,testrepoformerging/.git/logs/refs/heads/i18nUT Ãà QÆcbux éé•‘MNÃ@ …×äÞ#"Ïd~« U€Š`Á¦âÎØQƒš¤J£B¸gàbL6¨xågÙzŸmÄó(Ib+¤£6dêˆ(^”‰&:Q�©­�Þ‰UðXÂ[†‡š>?ä–/|Ò«v"ÚÏeºkP:wGœ‚KTˆõH}Ú.àvš„¡‡::L2g[;"ÒN‹³–ÙmµØ ªÄRbï côü?Fïsµk§lh†­ìv´=löÔ·‡mq¶·‘dª)`^ß(&ÍÎúÀ5M0ÑkL ÿ‚Œþ‚\�’¯[œmm�Gr^åNJ•±Qä($iêÔ˜*‰§­«à©M‡îe> °<éò;_­Ÿ¯ndܵ}É’9+§*Ÿg~ss~w=K9½M@=C/¯ÅPKÏŒ‚CŒäéÀ:2testrepoformerging/.git/logs/refs/heads/pep8-fixesUT …·œRÆcbux éé�Ž1N1kòŠí‘¢Ý³ã[G ‚*J$ʵ½ŒÎ1²œ�üž4t™ršÄÛ«ÑöÂèØYJ2$·9´ä-[?ÛÈðQ� Þ¤(l.õ¸Õ³”ïY—±– Ãq Ïp�„xšâçžšJ×S«^w�Ï‹›{h¬Ç‰\ • ÷d4#èF1)ºq �“¥‰à=Ç^¼H‹Y`sMŸô«ÎY·û"yþwhÐÿ^mÉ} ý�)Ï á¢Ë~î‹_PK �‚C¸ôDªD1testrepoformerging/.git/logs/refs/heads/ff-branchUT ÔœRÆcbux é镎A Â0E×zŠÙ 2I“&Á…7p?™L4ÒXH‹^ß.<@ý«ÏçÁˆë:Šu*(«“É^³ëPGÅ:�¼ØŒd%x¸žÇWj\ޱÑ[žãPä|¯T†=�õªó=b0ÎÂâfÁ^ü8À¥ Í’ ·±B¥i–¶]}-ÁEÃ9Ù1G#‘b6Yc\`2b“bÇÞýåØ+ÿs\ÖZæ|dXª@•i¢»l¿PKÜm„Ca`›'µO8testrepoformerging/.git/logs/refs/heads/branch-conflictsUT ?$ŸRÆcbux é镎ÁJ1DÏîWô]�NÒI:‹ˆ²»èxï$=�ÙHöûw@?`­KÁ£¨*ÄÛ¤…'a ÈT±5øÈ5#™DL)ZËTÞ[Yû€W¥ <æ!ýêsÓç�EÚüPúòÆq0Á`tp�ñn‹�ËçCeÕ Óè ¼�^Ž»›‡M¶YÔ{)É eu'v™C©>&kCEÍ–â¿Zãÿntië~Ö?£_ú·‚lì<Í[ëî PKs�N>Ëvaîø.testrepoformerging/.git/logs/refs/heads/masterUT y_YMÆcbux éé•ÐMJÄ@àµ9E-!Tÿ§‚ˆ‚ ¢º»:´L2’‰ã­<ƒ3*îb­ªÞæ}â¶N’²Ö¤-ÛHˆDY²äE] àÅ)xláŽ�5ÃCä�wyƒ«çüußÔ…ùåÔ¦ýx JSP�:êàâÙšŽu�ó:Õ¥òû:øÉ›ÍíN¢ˆÄh4ÂÄE©¤.jë¬ÏÞå�1Û™ž<û‡ÙÃmÎÐ+u˜ö³@©;i6Wë(.(RNg[:�‚AUB2:wâ ²e”õ5ÿ0ü5Ž2UuSOßûÈY ž`–ô:êQÚæPK í�‚Cä[­ÿ))!testrepoformerging/.git/ORIG_HEADUT M¾œRÆcbux éé2be5719152d4f82c7302b1c0932d8e5f0a4a0e98 PKs�N>7‹?I#testrepoformerging/.git/descriptionUT y_YMÆcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK s�N>testrepoformerging/.git/info/UT y_YMÆcbux ééPKs�N>w=Í!­ð$testrepoformerging/.git/info/excludeUT y_YMÆcbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPKs�N>ÚÖXt!testrepoformerging/.git/info/refsUT y_YMÆcbux ééUË1Ã0À:þŒ!éxh’"),ÿ\gûÕœZ`jhÊá­vÄ eS¨uèÄëʵÏwzìóÃø2²v6®º ³’Á“¬H ë"W§´¿øõ}çu%testrepoformerging/.git/objects/pack/UT y_YMÆcbux ééPKs�N>‘¿V{ÏWtestrepoformerging/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.packUT y_YMÆcbux éé ptöf```b¾ Bsæv[g9 ¸V¯M²g[|ÞCÊ‘CóŽÊ×3‹HþÉiò(›jš’\©·©1dz±e:_苵-2²a'—æÜ8ÑY±fãõu“ ~÷.ÚùñB©SH`èĹzÛŠ¾[z¾œúNhÙ4§Ͷχñ„ÌÈYÔ²nc¾fZVÒEQ�›Õ‡¼Úî?iõà)ƒû¿)îÿ5¯ÅmæÜž;ÚÉšß6W^?œ£o95ÇøÐ=«ì³ßâ¦Y~Ý¿·òêÇe Í׫šî>ʸ;3~bN¨ÈD~�7¼¹ˆ0|·ßêùBàÂÉ«}Å͵ìÒ;¯2íVY¿Ü.¡R+>.\Øòþní¶ýOLTÖÙà^"ojüw{ùí[zlû{«™¯-°ŒZÍR1ÇØÄÀÀØ0ð‚÷y�“>çOë{¥ èñ½½ýMút?ÏäÏóVd®�’ßÊ'é’ºäÙ“'¯§†L1Ð µxrû×oÿä5®)?vk+å,_tk£*§ì9³Š9—/iëé?`b`cfÜ»ƒ©bÎi�³'O†kœ×?åùðÔ‰óž!'tÏzŸ|îuêüéSúºA',t¬çé¸Vtestrepoformerging/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.idxUT y_YMÆcbux ééû_âŸÌÀÀÀÄ@]ÀHcÌDfÆ‚Y¨ˆY)Äl80;æ€bN,˜k˜bn<˜g„b^*b>.õÛ·ìg—NIcóž¨i­Zµi?-œtå‡N1ÓÆ“uûâ¹¼øf˜µ¸¼¹¾v•�·Ú…SïygËWßÊ·–x*t{ù<Ÿf†»ë6×÷u÷®õ;b1«!#EvѵðÞ ŽSZ¦+µœˆÛ÷n·û„SŸÊvþéV ÉM6L5Nù¶ÞjÚ''¶IºégJ"]¿‘vÉX½L/Œ‡o�ñ´ë&›*ëÜ´²OÈZ¥¦—{Õ)èi£M_ÔÙò†·œµùœÁõ™Ÿ/êZ† òHÙ7qÓÍ¥ ‡|[ÿiŒµˆ wE„íTT÷š7W_iª,ÙÉðü¡§gž¼wøô¼ÀW‹�äî>©×ö—Z¬oгy:Ó‡‰kþN.9]9UäÊéM>ñç_§mråÒ˜÷(Øæ©gývÍÉE�sÌy’Âä�½¢ýš=]p@9ðwãAý¸eü†Óg…˜ËU>÷Z=½ni½ °˜I„Ä‹�ýXl=âZ ˜®Yº�r—�â‹€¸XLõŲ€lþeŸ,ï~x6Cª�·•}íÚ0�O>!/®´.ËŒÓÏ[!ºØ}g%§ÜËß'PK 'fQB#testrepoformerging/.git/objects/32/UT ºÃ QÆcbux ééPK 'fQBËipItestrepoformerging/.git/objects/32/3fae03f4606ea9991df8befbb2fca795e648faUT ºÃ QÆcbux ééxKÊÉOR0gHËÏOJ,â"§zPK �‚C#testrepoformerging/.git/objects/76/UT ÔœRÆcbux ééPK �‚CÐÀîyyItestrepoformerging/.git/objects/76/da4e485dfdbd64dc995708f37d01be19b1fbffUT ÔœRÆcbux ééx+)JMU044a040031QÐKÏ,ÉLÏË/Je.õÛ·ìg—NIcóž¨i­ZµAUe¤æääë•T”0,U8äÛúûOc¬Edø»+"l§¢Ú¸¡ŠÊSs’ósSÁÊÌÿ´Ú,XÑR…C¾­¿ÿ4ÆZD†¿»"Âv*ª�ª(/µœÁÈ~ó—„¼•3eìû½éÏò©Ï<~Äß0 PK ,fQB#testrepoformerging/.git/objects/54/UT Ãà QÆcbux ééPK ,fQB¤Æ.°°Itestrepoformerging/.git/objects/54/70a671a80ac3789f1a6a8cefbcf43ce7af0563UT Ãà QÆcbux ééx¥Ž1N1©ýŠí#N^Û·Þ“PQ„ŽŽìÚ‹8)±ÑÅ@ò{D¾@7ÓŒ¦ôóyfÿ063XrŒJfù�4õ2ÍL’0ÖÀ¨bÊZݧlÖ$+)ò"ì‰)a•PiÎ\Õ'\§%‡À©°“¯ñÑ7x]K‡ïÞàÅn—O>Ýùp|{|¶í´¶©Ú0¢ÇLv½wå~:ì? 'µZ½Ù4®¤Uhöã~XøMtPK Üm„C#testrepoformerging/.git/objects/1b/UT ?$ŸRÆcbux ééPK Üm„CkWM¯¯Itestrepoformerging/.git/objects/1b/2bae55ac95a4be3f8983b86cd579226d0eb247UT ?$ŸRÆcbux ééx�ŽMjÃ0F³ö)f(Òxô¥t—t?–F�Û2Bõù«EN�վNjuÛJ¤péM’ËŠCÐlÌÂ)›œf—ó`hI¡£\œn²w ‰4ûÀ^YoI'Æd�óiQ¤y ÑSôÿõ{mðSbsã ÃçÒø”G]‹|ÿn\Ö�X·/г·ÚjÔ®J+5 :*»¼û  W8Z=ëS€!Ö=¯#gúž«OlPK s�N>%testrepoformerging/.git/objects/info/UT y_YMÆcbux ééPK s�N>üýÝó66*testrepoformerging/.git/objects/info/packsUT y_YMÆcbux ééP pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.pack PK Ûm„C#testrepoformerging/.git/objects/8f/UT =$ŸRÆcbux ééPK Ûm„CŽšþû--Itestrepoformerging/.git/objects/8f/a7ba34297098f11f19980a5096087d4c43a524UT =$ŸRÆcbux ééxKÊÉOR0²d(ÉÈ,V(ÏÌÉQ((Ê/ËÏNUHTHÎÏKËÉL.áðE ÞPK ÏŒ‚C#testrepoformerging/.git/objects/03/UT …·œRÆcbux ééPK ÏŒ‚C*€ÞਨItestrepoformerging/.git/objects/03/490f16b15a09913edb3a067a3dc67fbb8d41f1UT …·œRÆcbux ééx�ŽKjÅ0 »ö)´/�?œÓœfúª$#5''¬h©Â!ßÖßc-"Ãß]a;ÕÆ U”—ZÎ`d¿ŽùKBÞÊ™²?öýÞôgùÔg¿8ú?CPK �‚C#testrepoformerging/.git/objects/37/UT  ÔœRÆcbux ééPK �‚C„xÐiItestrepoformerging/.git/objects/37/fc853ca1c576800d9a79de5fe83efb8ed0ebb2UT  ÔœRÆcbux ééxKÊÉOR°d(OÍIÎÏMUä-zPK —Œ‚C#testrepoformerging/.git/objects/09/UT ·œRÆcbux ééPK —Œ‚Càc4wItestrepoformerging/.git/objects/09/07563af06c7464d62a70cdd135a6ba7d2b41d8UT ·œRÆcbux ééxKÊÉOR04`HªLU(Ï/ÊIá6=³PK u�‚C#testrepoformerging/.git/objects/d5/UT n½œRÆcbux ééPK u�‚CåYSOttItestrepoformerging/.git/objects/d5/fb8a80eef91402a6faf2af3b0f56175b44a23cUT n½œRÆcbux ééx+)JMU044`040031QÐKÏ,ÉLÏË/Je.õÛ·ìg—NIcóž¨i­ZµAU%U¦ê•T”0p²‡Y}È)I¹¦Upö¢é²]µÚŽ7 J2RsròÁŠ–*òmýý§1Ö"2üݶSQmÜË-…PK s�N>testrepoformerging/.git/hooks/UT y_YMÆcbux ééPKs�N> Â\*/testrepoformerging/.git/hooks/pre-commit.sampleUT y_YMÆcbux éé}T[kÜ:~ŽÅÔ I±w¶!IÙÂ’}9�óÐSŠl�×êÚ’+É›l)ýíý$Û[ÒSúæËܾËÌá‹Y¡ôÌÕÉarH+Mü$Ú®aª�Ù�+­êOÐV'b-”v~Bn'‡t§•W¢§º!©ªŠÆÈ€�ÛÎïÈ[f2Å.ý¯:‹âêâ•,/eqÉŵ˜_ÎùÕ¢¨®äåõÅÕUU0/®ç‹¤R °ÞU´3}$$ ‚6æ1r+\©TDp;rìð[N‹�o{Γ˜„œ˜²Ññqöƒñ¼oM=%[9ýŽ3testrepoformerging/.git/hooks/pre-applypatch.sampleUT y_YMÆcbux éé}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PKs�N>éøÊ÷€/testrepoformerging/.git/hooks/commit-msg.sampleUT y_YMÆcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKs�N>hÓ†9Ý[/testrepoformerging/.git/hooks/pre-rebase.sampleUT y_YMÆcbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘ªüH•29MCW±�%ƒx.Þ~÷Ïw'×gñyaWåg Å£ƒÇÏ»&- ¼Ú¦3<_ö£˜á:æ� °�—p¯7û0|w2ù«œ¤×ÑŒé{CL¯ót:⻲Už¹áì}9GóN¯ý+Œ0bU® îDîÉNŸ�Apï]�<˜{ÿÊž_�FAp1üÆ·"KƒŽýŸ½ÂÉ Dé!’zZŽUsß« ÒIÿ¸zýíÛ·>ŠÁ€^].ÑÌÚâ uŸ§×1„ž ßã¶ö»&õŸöœ¦ùqˆ`GÿçGˆîX÷[‰•©ZÕÒ,íÁ‰¾–h_ …"j­ŸßÍÔÀ€.—SŒòÁJ’r}Hkë‚Gr(�çBÐ3ú¹LÔþìá%Æ�ýèøxwå ‹;ž@O‰E˜5Ï"¹’؆*ã¤efTâЈ¨¬lî°É|Iï®s'ˆÄòr×Õ°3†UCè—È©SË©o·ÜkKjfQgh íÔÌx7‚`ÃV0¿’&ϸo‰òNYì˜`Þ<±(A5B/–¿åx~¾f^8_6ÆÁŸNß0i�É|J•(k¥Ùú&=(X3_(ŒØÓ¬Nd3FÛ/èœ~Üt}õ}AŽ`ÜÉZ–+Œ.±ÆEãV¶»ˆ¡ x861YÅ=Õƒ Ë·úä!:þÌ2Ñ˸d4jcÌJ¬Ì‚3 ¢ÔЕC¾��™ÌÃP¾ãŒœ”i¶ª�\ýx)ß½¹ºyýæí,¼�­ìKf±Q‹_Â’I ¬Ç>a#ÝH¿Ó]©7€÷ðtDÓé”ÐFö�ÆýÖ‡‘‹¨ÓiƒÁHá¿P@+Ù~ýñ!Ä—Ÿuu•-wðéXa$Ãß3'¹}]5g�*Ä3i_v‹:ì(lÚ«.¿J>6fØ[Íy‚'¥à"™¦@:g.°VJ3Šˆ^!š«/dd×A:�ôØ”& è(¨`¿±ˆáükQõÝß¿vµá_ñY‡u±¡€Y2Ñß9D;vèÍ·ã½íÉs(l±º>YÛÝ�„lø’h‚nTî › d·*ÃJ:bßÌPX\Mž½í…Øë²N»IkãÕÇ’ÇÈÆm3*çæÑQ4ΰ–•m��èÿÊϹÃÙ*Éð*à0´š6”âoÊ[Êy^HŠ"V(°ßë�GO&“ßýFÐ{ŽYÜ›ÿO®™ŠHpòwA‰f{ÏÐtžò7�´Ý—^=¥œu¾íçýÂ.Ý›xùˆ2ÒÍèë"g>êÿMÛ@6[‚c¸À~éOz�›Hhå¾^°]Rš}B“�%É¿"ÄÊAßh{’çyåyÒ¬ôËSÏ}¡ê"o3´Ù”Ø�Ù/¾åŸb|‰„^ÝLR¯çÒKå°ßjX·¹«½®¶ðË(o÷Ž™ü ÂEèxªÄÊnäJ‰WO_ñ€.ªý­›A|ðHó!DÓÿŽp˜"~ ³5* cßÿ¢Ó4�/·ÂÿPKs�N>}«.íU(1testrepoformerging/.git/hooks/post-receive.sampleUT y_YMÆcbux ééeQËrÂ0 ¼ç+T¸6˜<I‡a¦—ÎôÞPl™x vÆv(ýû*†C7K«]yWë'Ñ+Â�­³5¼Z ŽÓ…`pî Az3EÐÎCV“ 1÷$É\it%7‰øñ}pM?[@ÉÃÌ'”g0JIS$K�•Ъ´ËË™èüWì‰,Ì“BÞ¼'á CX¸þ4�ü•Æ2×»ùÄv DÅujÑbaäÜEyºá`éóþð¤-Žtdø��>xY¦ë]U�*úŠêŽöºÑ][×…Æ]ÓÕ�RR5Xn¡iõ{]ãeÙn÷mÛé]ÙËm]‘TUÑï;Ý5ÛÒA „*ˆg’â D ��Þôb >ˆ”9BHy.f+ݸØL~,Ý"\Œ¥%0Vð´x¸[5|¾èþÜc“eë ˆ9x>6zÊIq21—Ž‹ßËsÑ\²oPKs�N>R¼­v+testrepoformerging/.git/hooks/update.sampleUT y_YMÆcbux éé­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPKs�N>š ÷Àн0testrepoformerging/.git/hooks/post-update.sampleUT y_YMÆcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKs�N>`÷‘X Ä3testrepoformerging/.git/hooks/applypatch-msg.sampleUT y_YMÆcbux éé}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PKs�N>i 0›ß7testrepoformerging/.git/hooks/prepare-commit-msg.sampleUT y_YMÆcbux éé}S]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘Œ$·”É�ç$9!2y�ÏÞ½½ÝSöº\J]ºn’M2¸Ð€?ŪW�1à*+{Þ@o±Áw•Y­¤eZX¡s¢Å‚°—B)¬aù ¬¥·é#OÒw¥Å Á4ñÜH¨„‡N¸P!üÈ:2æÐ¥ÌSb ˜“iô†&A;‚fœl…À¼K3P­lo‚ta¬©Ë¿ÐQ!¯y4 R¹4ƒm4ÿ…Ö€óÂ.'èŽÄ.–Æz¬‹èåœZi±ŒƒÒ»@—ƒÅhC¬$ °Ñ[žˆøÊµ¬˜† ¤®Ô@Ð"n2rã˜�´.ŽÚ;0ƒ-e—F7JVÞ�0 >'h^Ûnd�Z‰Åaet½Û U?DTÌ´–Mœ‡xò¸e„—šæ€mzß’´ÄÆŒ;ñ ÃFÚ`u°mÌž´‡°±CÈŠºtzë1EÀ9u&�Ʀg÷c®Ãz»=îÏ8d±­·Û,êšâ�Ùj¬¹iN[¥¤Ž¼·²šhœ—J‘¢¸2+¤Dèo).õL„­1dY�‚«éfÓ·ùô#G&�ŒÎèPÎÆ«†úz´ ¸,–â8‰˜¹ò¶Ì Ì�deV‚l ¼ÝXBQ•Oé&’Û3jtÌàô4ìI¾öHS zeÿíENÎâ'ô‹DÀšA÷;ùV¢"Sÿ ú~’½JªHÝÞLãÞÁÙ¼ÙpxèDäÝ\¿?›îþGaáÓÕüîâÛüóõ×»«¿ÌaM›GÍt4a±_ž/ŠÃiù"«X—ý,Œ×Òmþû�5K�×kÀª£Ë”jçç±<ù PKs�N>ƒlÇz 0testrepoformerging/.git/hooks/post-commit.sampleUT y_YMÆcbux éé-ŒA ï¼b WMîÞü€'?°!‹PKeIUž/‰^»gÚ^¦9–Iƒ±ÆâQÀ;åšAäuk¬ -PCT8J‰�o¼‚ ›s¬ê·Ô¯NrŽç*ÓÂã™{ ¸ÐÜk-tq$¯X;ÊâãáCm·_b�¹ã)}PÞæ PK Üm„Cþ&�&testrepoformerging/.git/COMMIT_EDITMSGUT ?$ŸRÆcbux éécommit to provoke a conflict PKém„CHmï’¸testrepoformerging/.git/indexUT V$ŸRÆcbux éésñ rf```á ù*a@šF30üc``©Ka`h\ÂÀÀü‚Ø„ËCýö-ûÙ¥SÒØ¼'jZ«ÖG­C \zé™%™éyùE© P4g;#ˆö�Œ¯D˜×¨�fžÆR…C¾­¿ÿ4ÆZD†¿»"Âv*ª�›�3#5''_¯¤¢„áXŠÕ¦?Ëç¾Y&éþë¶ÜãN…–PKñ‘‚CS(¬B�Ætestrepoformerging/.git/configUT 5ÀœRÆcbux éé5�Á ƒ@ DÏîWˆG MéQè—H«f5�d³ÿ¾±ÒË„ÇLfúQß¡RÜ%“‰It�öAÍ$[ýª¡JĸʄN¦C5D=!EÎN,sdVLeŸ¢aþÇzõ/ú¥™¶Ægв»3Yດá>Ê LƒÓöãtestrepoformerging/.git/refs/UT y_YMÆcbux ééPK Üm„C#testrepoformerging/.git/refs/heads/UT ?$ŸRÆcbux ééPK ,fQBA=Ð))'testrepoformerging/.git/refs/heads/i18nUT Ãà QÆcbux éé5470a671a80ac3789f1a6a8cefbcf43ce7af0563 PK ÏŒ‚C¥‰))-testrepoformerging/.git/refs/heads/pep8-fixesUT …·œRÆcbux éé03490f16b15a09913edb3a067a3dc67fbb8d41f1 PK �‚C΄Fû)),testrepoformerging/.git/refs/heads/ff-branchUT ÔœRÆcbux éée97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87 PK Üm„C�Š$u))3testrepoformerging/.git/refs/heads/branch-conflictsUT ?$ŸRÆcbux éé1b2bae55ac95a4be3f8983b86cd579226d0eb247 PK s�N>ä[­ÿ)))testrepoformerging/.git/refs/heads/masterUT y_YMÆcbux éé2be5719152d4f82c7302b1c0932d8e5f0a4a0e98 PK s�N>"testrepoformerging/.git/refs/tags/UT y_YMÆcbux ééPK s�N>¢§ÓÝ((testrepoformerging/hello.txtUT y_YMÆcbux ééhello world hola mundo bonjour le monde PK ém„CÙ÷ÚŽtestrepoformerging/.gitignoreUT V$ŸRÆcbux éé*.swp PK ém„CíAtestrepoformerging/UTV$ŸRux ééPK ém„CíAMtestrepoformerging/.git/UTV$ŸRux ééPK s�N>íAŸtestrepoformerging/.git/logs/UTy_YMux ééPKém„Cz‚SDi!¤�ötestrepoformerging/.git/logs/HEADUTV$ŸRux ééPK s�N>"íA•testrepoformerging/.git/logs/refs/UTy_YMux ééPK ‘m„C(íAñtestrepoformerging/.git/logs/refs/heads/UT±#ŸRux ééPK,fQB’M}Y0—,¤�Stestrepoformerging/.git/logs/refs/heads/i18nUTÃà Qux ééPKÏŒ‚CŒäéÀ:2¤�étestrepoformerging/.git/logs/refs/heads/pep8-fixesUT…·œRux ééPK �‚C¸ôDªD1¤�testrepoformerging/.git/logs/refs/heads/ff-branchUTÔœRux ééPKÜm„Ca`›'µO8¤�* testrepoformerging/.git/logs/refs/heads/branch-conflictsUT?$ŸRux ééPKs�N>Ëvaîø.¤�Q testrepoformerging/.git/logs/refs/heads/masterUTy_YMux ééPK í�‚Cä[­ÿ))!¤�§ testrepoformerging/.git/ORIG_HEADUTM¾œRux ééPKs�N>7‹?I#¤�+ testrepoformerging/.git/descriptionUTy_YMux ééPK s�N>íAÇ testrepoformerging/.git/info/UTy_YMux ééPKs�N>w=Í!­ð$¤� testrepoformerging/.git/info/excludeUTy_YMux ééPKs�N>ÚÖXt!¤�)testrepoformerging/.git/info/refsUTy_YMux ééPK ém„C+is§¤�Ütestrepoformerging/.git/HEADUTV$ŸRux ééPK Üm„C íAItestrepoformerging/.git/objects/UT?$ŸRux ééPK ÏŒ‚C#íA£testrepoformerging/.git/objects/de/UT…·œRux ééPK ÏŒ‚Cø'75QQI$�testrepoformerging/.git/objects/de/9b035de7240be11378cc214ac45c427d80e241UT…·œRux ééPK s�N>%íAÔtestrepoformerging/.git/objects/pack/UTy_YMux ééPKs�N>‘¿V{ÏW$�3testrepoformerging/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.packUTy_YMux ééPKs�N>,t¬çé¸V$�?testrepoformerging/.git/objects/pack/pack-e6fbc15b315a0eab6005c44e5b7054b1f0043f39.idxUTy_YMux ééPK 'fQB#íA¸testrepoformerging/.git/objects/32/UTºÃ Qux ééPK 'fQBËipI$�testrepoformerging/.git/objects/32/3fae03f4606ea9991df8befbb2fca795e648faUTºÃ Qux ééPK �‚C#íA®testrepoformerging/.git/objects/76/UTÔœRux ééPK �‚CÐÀîyyI$� testrepoformerging/.git/objects/76/da4e485dfdbd64dc995708f37d01be19b1fbffUTÔœRux ééPK Üm„C#íAtestrepoformerging/.git/objects/d7/UT?$ŸRux ééPK Üm„C¿zNWWI$�dtestrepoformerging/.git/objects/d7/f0a991a55badf5fd32e7ffa9925e240274997cUT?$ŸRux ééPK �‚C#íA>testrepoformerging/.git/objects/e9/UTÔœRux ééPK �‚Cw›G%§§I$�›testrepoformerging/.git/objects/e9/7b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87UTÔœRux ééPK ,fQB#íAÅtestrepoformerging/.git/objects/97/UTÃà Qux ééPK ,fQB´�w_ooI$�"testrepoformerging/.git/objects/97/33b6ee7f6b3cb0a276586a413c8b281baeb8bdUTÃà Qux ééPK ,fQB#íAtestrepoformerging/.git/objects/54/UTÃà Qux ééPK ,fQB¤Æ.°°I$�qtestrepoformerging/.git/objects/54/70a671a80ac3789f1a6a8cefbcf43ce7af0563UTÃà Qux ééPK Üm„C#íA¤ testrepoformerging/.git/objects/1b/UT?$ŸRux ééPK Üm„CkWM¯¯I$�!testrepoformerging/.git/objects/1b/2bae55ac95a4be3f8983b86cd579226d0eb247UT?$ŸRux ééPK s�N>%íA3"testrepoformerging/.git/objects/info/UTy_YMux ééPK s�N>üýÝó66*¤�’"testrepoformerging/.git/objects/info/packsUTy_YMux ééPK Ûm„C#íA,#testrepoformerging/.git/objects/8f/UT=$ŸRux ééPK Ûm„CŽšþû--I$�‰#testrepoformerging/.git/objects/8f/a7ba34297098f11f19980a5096087d4c43a524UT=$ŸRux ééPK ÏŒ‚C#íA9$testrepoformerging/.git/objects/03/UT…·œRux ééPK ÏŒ‚C*€ÞਨI$�–$testrepoformerging/.git/objects/03/490f16b15a09913edb3a067a3dc67fbb8d41f1UT…·œRux ééPK 'fQB#íAÁ%testrepoformerging/.git/objects/79/UT¹Ã Qux ééPK 'fQBR5ód$$I$�&testrepoformerging/.git/objects/79/9baaf570f1f03a22ef0af4f2cfc36c836635f8UT¹Ã Qux ééPK í�‚C#íAÅ&testrepoformerging/.git/objects/c6/UTM¾œRux ééPK í�‚C„¯ž��I$�"'testrepoformerging/.git/objects/c6/5d223d98aaa8726b4ac359af2e270ee348ad2aUTM¾œRux ééPK �‚C#íA5(testrepoformerging/.git/objects/37/UT ÔœRux ééPK �‚C„xÐiI$�’(testrepoformerging/.git/objects/37/fc853ca1c576800d9a79de5fe83efb8ed0ebb2UT ÔœRux ééPK —Œ‚C#íA-)testrepoformerging/.git/objects/09/UT·œRux ééPK —Œ‚Càc4wI$�Š)testrepoformerging/.git/objects/09/07563af06c7464d62a70cdd135a6ba7d2b41d8UT·œRux ééPK u�‚C#íA'*testrepoformerging/.git/objects/d5/UTn½œRux ééPK u�‚CåYSOttI$�„*testrepoformerging/.git/objects/d5/fb8a80eef91402a6faf2af3b0f56175b44a23cUTn½œRux ééPK s�N>íA{+testrepoformerging/.git/hooks/UTy_YMux ééPKs�N> Â\*/í�Ó+testrepoformerging/.git/hooks/pre-commit.sampleUTy_YMux ééPKs�N>%[9ýŽ3í�˜/testrepoformerging/.git/hooks/pre-applypatch.sampleUTy_YMux ééPKs�N>éøÊ÷€/í�1testrepoformerging/.git/hooks/commit-msg.sampleUTy_YMux ééPKs�N>hÓ†9Ý[/í�b3testrepoformerging/.git/hooks/pre-rebase.sampleUTy_YMux ééPKs�N>}«.íU(1í�¨;testrepoformerging/.git/hooks/post-receive.sampleUTy_YMux ééPKs�N>R¼­v+í�h=testrepoformerging/.git/hooks/update.sampleUTy_YMux ééPKs�N>š ÷Àн0í�CBtestrepoformerging/.git/hooks/post-update.sampleUTy_YMux ééPKs�N>`÷‘X Ä3í�7Ctestrepoformerging/.git/hooks/applypatch-msg.sampleUTy_YMux ééPKs�N>i 0›ß7í�®Dtestrepoformerging/.git/hooks/prepare-commit-msg.sampleUTy_YMux ééPKs�N>ƒlÇz 0í�ºGtestrepoformerging/.git/hooks/post-commit.sampleUTy_YMux ééPK Üm„Cþ&�&¤�žHtestrepoformerging/.git/COMMIT_EDITMSGUT?$ŸRux ééPKém„CHmï’¸¤�Itestrepoformerging/.git/indexUTV$ŸRux ééPKñ‘‚CS(¬B�Ƥ�Jtestrepoformerging/.git/configUT5ÀœRux ééPK s�N>íAìJtestrepoformerging/.git/refs/UTy_YMux ééPK Üm„C#íACKtestrepoformerging/.git/refs/heads/UT?$ŸRux ééPK ,fQBA=Ð))'¤� Ktestrepoformerging/.git/refs/heads/i18nUTÃà Qux ééPK ÏŒ‚C¥‰))-¤�*Ltestrepoformerging/.git/refs/heads/pep8-fixesUT…·œRux ééPK �‚C΄Fû)),¤�ºLtestrepoformerging/.git/refs/heads/ff-branchUTÔœRux ééPK Üm„C�Š$u))3¤�IMtestrepoformerging/.git/refs/heads/branch-conflictsUT?$ŸRux ééPK s�N>ä[­ÿ)))¤�ßMtestrepoformerging/.git/refs/heads/masterUTy_YMux ééPK s�N>"íAkNtestrepoformerging/.git/refs/tags/UTy_YMux ééPK s�N>¢§ÓÝ((¤�ÇNtestrepoformerging/hello.txtUTy_YMux ééPK ém„CÙ÷ÚŽ¤�EOtestrepoformerging/.gitignoreUTV$ŸRux ééPKMMé"¢Olibgit2-pygit2-d86662e/test/data/testrepopacked.zip000066400000000000000000000432711521317216500222730ustar00rootroot00000000000000PK ;fQBtestrepopacked/UT âà QÆcbux ééPK -fQBÙ÷ÚŽtestrepopacked/.gitignoreUT Åà QÆcbux éé*.swp PK s�N>¢§ÓÝ((testrepopacked/hello.txtUT y_YMÆcbux ééhello world hola mundo bonjour le monde PK i€|Qtestrepopacked/.git/UT ¶fÂ_Æcbux ééPKi€|Quð³~¢testrepopacked/.git/packed-refsUT ¶fÂ_Æcbux ééMŒ»Â0 ÷~E$æªÎÛáo\ÇV+TMâï‰�étÃÝÉlÄ—qíæ¹ËÙl"MªÑGk¯ñ'ý¾ 1d ”-!ûŒE-%B�Yƒgɤ“7ßß´Õ>­oƒ›%f[lt5(:ÎÜlŠw%*P �‚ÿá•ú!ûðPK s�N>testrepopacked/.git/refs/UT y_YMÆcbux ééPK s�N>testrepopacked/.git/refs/tags/UT y_YMÆcbux ééPK i€|Qtestrepopacked/.git/refs/heads/UT ¶fÂ_Æcbux ééPK s�N>¢Tž{))testrepopacked/.git/ORIG_HEADUT y_YMÆcbux éé5ebeeebb320790caf276b9fc8b24546d63316533 PK i€|Qtestrepopacked/.git/logs/UT ¶fÂ_Æcbux ééPK i€|Qtestrepopacked/.git/logs/HEADUT ¶fÂ_Æcbux ééPK s�N>testrepopacked/.git/logs/refs/UT y_YMÆcbux ééPK i€|Q$testrepopacked/.git/logs/refs/heads/UT ¶fÂ_Æcbux ééPK i€|Q*testrepopacked/.git/logs/refs/heads/masterUT ¶fÂ_Æcbux ééPK i€|Q(testrepopacked/.git/logs/refs/heads/i18nUT ¶fÂ_Æcbux ééPKÊ‹PB‰¹—XšÖtestrepopacked/.git/configUT ´QÆcbux éé5ŽÁ ƒ0 †Ïö)ŠGËØQØ“ÈÕ¦ˆFÒvàÛ/N–þ$ü†Iß®QÜ%S=’èÊ5“lþå®IĸJD£¢]3=!ÎF,s`VLu�¡`þŸy+Œ§©M0ºAÍ¥ oEi¦­µØªl»™J`ºÔñ>É L£Ñöãlw{Ë´ØñÍ‚2,b†®ÿÁå›áò…Î}PK i€|Qtestrepopacked/.git/objects/UT ¶fÂ_Æcbux ééPK i€|Q!testrepopacked/.git/objects/pack/UT ¶fÂ_Æcbux ééPKi€|Q <Ö®g(Rtestrepopacked/.git/objects/pack/pack-6371e8325a610822b319bbd75ccda015ae31a08d.idxUT ¶fÂ_Æcbux ééû_âŸÌÀÀÀÄ@]ÀHcÌ„3£a,˜•Š˜ f'sàÀœh˜ й±`"1ïa>21?,0B± ± —‡úí[ö³K§¤±yOÔ´V­�Z‡´ŸN ºòC§˜iã�ɺ}ñ\^|3Œì×1IÈ[9SöǾߛþ,ŸúÌã—Y‹Ë›ëkWhp«]8õžw¶|õ­|k‰§B·—Ïóif¸Ë±ns}_QwïZ¿#³2Rd] ïÝà8¥eºRˉ�‚e…+¸WÌ—Êêy¿ç‹Íóõ¬ÉqûÞí6bŸpêSÙÎ?Ý*!¹É†©Æ)ßÖ[MûääÂ6I7ýLI¤Ëâ7Ò.«—é…ñðí1žöbÝdSe�›Vö Y«ÔôRc¯:=m´é‹:[Þð–³6Ÿ3¸¾€#óóE]ËÐ#A)û&nºY9{Õׂ�¬”Þs}ùtþpNsšé�éÆÛÞÕgÛlXT‘åhÓ­!½nÇÞ¥ ‡|[ÿiŒµˆ wE„íTT÷š7W_iª,ÙÉðü¡§gž¼wøô¼ÀW‹�äî>©×ö—Z¬oгy:Ó‡‰kþN.9]9UäÊéM>ñç_§mråÒ˜÷(Øæ©ç{‘úíš/’‹ç˜ó$1<ùw8LÞØ+Úÿ ÙÓ”7tÑ�[Våj—ûH3.ƒßpú¬s¹Êç^«§×-­Wm»€ñ¶Xœ5‹¡ 6} à|ΤäûñY`qYTóˆS€z~ñI ªe]›”\øÂ(*‘Ci³äîë1gˆ®3\ÐûQäââZÎ4·w¦ËýÎø{„[=|#PKi€|QÙøÓdµStestrepopacked/.git/objects/pack/pack-6371e8325a610822b319bbd75ccda015ae31a08d.packUT ¶fÂ_Æcbux éé�‘wPÓLj(CDA+C¤%P Œ$H€²š"T¶,EQ–8 aFe²!Pš0 –©(C!Lb€ ”ÂõλÞÙúÇûãÝ÷Ý÷}ßçYí”T¦™‘ߥo)„?3nP˜p 7°l.’ËÎû\õk3gîÍÜw:,LÈ!;Ìe…·ñíþ…ÃjJæXÛä Òdº¨:Èâº*9íýFô‡Rwçe¤¬6ÎôVGAå©®DGN�¢tÕV¶® €)ñâu–xÝÿó D¢Öl”ø«K×¢ê¨iIÊø1×{9Ô•�¬ÞíW8”ïq�˜г¥¹ È൥ȌûwOù#%MÂJ]õäÀy\óƒ†"êïTMºÌóÖå7ý¢¥Ì¯ÒuÜÝBàU7mÓ.£½ v Šöœ|¿a+ömrIå`Y²Ö5·~¹'ØÈÖÚ.iÌþ,h �›¡/H¤ªÇ.MåÙ‹Û¦ûåF—U^T÷ôq}-«}Ö‚��šÑ›Ìþr<ˆÛ¦ÌÉaÃ÷¦r(˜¾¹É4ÝÕ2¢ç۽꜊æ7³Bú— ~Œ �|ÿÁû}†K’Ÿ�L’ÄîÑ-` Ý>,6©z–Æ:Õ½ö'.%7öÄmYåY]œ”oLç‹Â½ÒÕÅP}ý¤Ëq©JÖÔb“r‹¶3 -=�Ÿd!…‚aögWÔì£ái«�$áS5 J[¥* ëãOËư\…ñù™žÜ|ùòË—Nq¼ÑÞ@ è'c€4”ƒ•ÉR Rß\Dè‘r¦†ý#1F'{ÓYë i3|ü¼ð 2c_;sï -IÀª NHwNéÕ„:©Ž¨ a «âƲn6ñXݽâ|÷T=i sÌO3œ²Ÿ«¾ßH½§áÓ⛽?еP¿AÓWŽîWBól# 4�Cw%˜‚òK‰»<âwyHžÙá1/Œ+ á•àØžÒãhê¥_ÌnGŽt ÈÈ£Q{ʡſ÷UhÓ±‰‰ì l—‚�è„r’mæ»vWÕ´ã !y:‹ûF:ÔÖ…©Ñ?«:)éóäÍ‹Dê;ü)X ©Õyá\ìG“Dx /3¢qî{Ó±ðÂPdëÝ j�’’§”O=ôÜíUT»fñø´©.…û³¯A<Å<5Cl'è=CQRX 7YhIö T:Œíqyäß&Ú‰§FþÕqr)1v ìYRd—¬BVyö¥ÜÖõ–ñÈ}NãÆà¦%{8ýñ—£Â÷ªï1UÊ ~ Ñpqv�F�21DÍiý„-v’2´ÂìÊ‘Sv¨­ÚkÃoáàfj˜à@6št‚ÌèýCŽ`AVˆÌè4ïæpÔ¸ˆÜx›‹³eú œ)|·³³aãìØÝ\n U!1…`2¥­¥…BZ›[ôk©�Ì`Íú>)a�_¬o¢èE袻¿Ü:ŠÅ™úXZ8¾¶‰1n*mÞÖ~AÂÆ63•Z®jS«!jÏÙ×WÒ¢é(�ˆù Ö ÷ss]höƒ=® 3ÅvÿÃCíÔð°N.ô8k¹GãäŽØÉ%âw{1(ú‰Ð·X=.”cÁíDà=$áüáÚy¯4KœÛʃÇJIGj òF¶�xstÛ-¨�îÄð× ¢[‰‰û:SSɯ0÷m�€ê^ÅWÿ×Ñ‘€‘m`QQ­ç⃳æ6î4„ ‚9=ÅYÌ¿aƒ€ÃAB^ù o{Ø»âà.RÇâ@v-íá Ç»ì¥kØ!K“šK‚Àú+HÆwçÉ5C¸…Z¨DQûW°~V!*<½ |ÆAjþóÅ À[HÄñ»†©d�+abzÆ2æN ®É¯• ³Û�·KÓ?‘΋(=•g:ugË–!³©PK i€|Q!testrepopacked/.git/objects/info/UT ¶fÂ_Æcbux ééPKi€|QrÙ-testrepopacked/.git/objects/info/commit-graphUT ¶fÂ_Æcbux éésvð`ddfð÷tqc€ ÛÂd±pvq �²70ÀkÃÐŒƒ3áÀÌDb1ë(c¶QL=¬ý´pbЕ:ÅLLÖí‹çòâ›áwÄbVCFŠì¢ká½§´LWj9R°¬p×áŠùRY=ï÷|±y¾ž59nß»ÝFìN}*Ûù§[%$7Ù0Õ8k•š^jìU§ §�6}QgËÞrÖ®ysõ•¦Ê’� ÏzzæÉ{‡OÏ ü;¹TätåT‘+;¤7ùÄŸ�R´É•’/@yKÀ7ÒÓ(åÛz«iŸœ\Ø&馟)‰tYüFÚ(ÇRÀ<¾‘=Ó�·½«Ï¶Ù°¨,"ËѦ[CzÝŽ½ 3 jÎç ®/àÈü|Q×2ôH�Gʾ‰›n‚ÒT Мsf-.o®¯]e Á­váÔ{ÞÙòÕ·òÑÔäf¬^¦ÆÃ·ÇxÚ‹u“M•unZÙ'€ä¡jX|S~�û’’×U<ƒyþµÜ'±…ø¢PK i€|QTÂÍ™66&testrepopacked/.git/objects/info/packsUT ¶fÂ_Æcbux ééP pack-6371e8325a610822b319bbd75ccda015ae31a08d.pack PK -fQB+is§testrepopacked/.git/HEADUT Åà QÆcbux ééref: refs/heads/master PKs�N>7‹?Itestrepopacked/.git/descriptionUT y_YMÆcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK i€|Qtestrepopacked/.git/info/UT ¶fÂ_Æcbux ééPKi€|Qä�\„Yttestrepopacked/.git/info/refsUT ¶fÂ_Æcbux ééMË9Ã0À:þŒ9„@ÏA&)’ÂòÿÇm¶_i ÞÝÀƒÕF¡w·ÈšQ�#Õ ¤óëÊÚç;}íóƒö;h¦(Z­ŒBhbÀ`Z–RàÍ!‡ýǯï;¯ãPKs�N>w=Í!­ð testrepopacked/.git/info/excludeUT y_YMÆcbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK ,fQBƒ>Úk"testrepopacked/.git/COMMIT_EDITMSGUT Ãà QÆcbux ééadded bye.txt and new PK-fQB¦dÀ±™¸testrepopacked/.git/indexUT Åà QÆcbux éésñ rf```á@…ÃGU(zÁh†L :VÙ �K˜_Õ¤1›py¨ß¾e?»tJ›÷DMkÕú¨uˆ�K/=³$3=/¿(• €æ”+$]Uó�Œ¯„ˆ€Ì³nD3Oc©Â!ßÖßc-"Ãß]a;ÕÆÍÀ™‘𓓝WRQ°õàêô}¿eŠžfóýR;Óhn³ü²PK s�N>testrepopacked/.git/hooks/UT y_YMÆcbux ééPKs�N>i 0›ß3testrepopacked/.git/hooks/prepare-commit-msg.sampleUT y_YMÆcbux éé}S]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘Œ$·”É�ç$9!2y�ÏÞ½½ÝSöº\J]ºn’M2¸Ð€?ŪW�1à*+{Þ@o±Áw•Y­¤eZX¡s¢Å‚°—B)¬aù ¬¥·é#OÒw¥Å Á4ñÜH¨„‡N¸P!üÈ:2æÐ¥ÌSb ˜“iô†&A;‚fœl…À¼K3P­lo‚ta¬©Ë¿ÐQ!¯y4 R¹4ƒm4ÿ…Ö€óÂ.'èŽÄ.–Æz¬‹èåœZi±ŒƒÒ»@—ƒÅhC¬$ °Ñ[žˆøÊµ¬˜† ¤®Ô@Ð"n2rã˜�´.ŽÚ;0ƒ-e—F7JVÞ�0 >'h^Ûnd�Z‰Åaet½Û U?DTÌ´–Mœ‡xò¸e„—šæ€mzß’´ÄÆŒ;ñ ÃFÚ`u°mÌž´‡°±CÈŠºtzë1EÀ9u&�Ʀg÷c®Ãz»=îÏ8d±­·Û,êšâ�Ùj¬¹iN[¥¤Ž¼·²šhœ—J‘¢¸2+¤Dèo).õL„­1dY�‚«éfÓ·ùô#G&�ŒÎèPÎÆ«†úz´ ¸,–â8‰˜¹ò¶Ì Ì�deV‚l ¼ÝXBQ•Oé&’Û3jtÌàô4ìI¾öHS zeÿíENÎâ'ô‹DÀšA÷;ùV¢"Sÿ ú~’½JªHÝÞLãÞÁÙ¼ÙpxèDäÝ\¿?›îþGaáÓÕüîâÛüóõ×»«¿ÌaM›GÍt4a±_ž/ŠÃiù"«X—ý,Œ×Òmþû�5K�×kÀª£Ë”jçç±<ù PKs�N>}«.íU(-testrepopacked/.git/hooks/post-receive.sampleUT y_YMÆcbux ééeQËrÂ0 ¼ç+T¸6˜<I‡a¦—ÎôÞPl™x vÆv(ýû*†C7K«]yWë'Ñ+Â�­³5¼Z ŽÓ…`pî Az3EÐÎCV“ 1÷$É\it%7‰øñ}pM?[@ÉÃÌ'”g0JIS$K�•Ъ´ËË™èüWì‰,Ì“BÞ¼'á CX¸þ4�ü•Æ2×»ùÄv DÅujÑbaäÜEyºá`éóþð¤-Žtdø��>xY¦ë]U�*úŠêŽöºÑ][×…Æ]ÓÕ�RR5Xn¡iõ{]ãeÙn÷mÛé]ÙËm]‘TUÑï;Ý5ÛÒA „*ˆg’â D ��Þôb >ˆ”9BHy.f+ݸØL~,Ý"\Œ¥%0Vð´x¸[5|¾èþÜc“eë ˆ9x>6zÊIq21—Ž‹ßËsÑ\²oPKs�N>éøÊ÷€+testrepopacked/.git/hooks/commit-msg.sampleUT y_YMÆcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKs�N>%[9ýŽ/testrepopacked/.git/hooks/pre-applypatch.sampleUT y_YMÆcbux éé}�1OÃ0…gü+iÕÒˆ• $$ÔuGNz©O�}–}¡)ˆÿŽSZ±±¾»û¾{‹Û¦åÐdgf�皬�Á‰�»ÄQ¡‚JÜŸptVÁ¶•ñœ·„N¼gUÚ@{‚�q8E«�CŸÄÃfí-ð”³ÝÓú¬Úº«ÃÉ8슗GV‡ ¡þ¤$Èju,®^)i9ì ®G’˜Ø*]©à3ÁÍócY%BÝõ½‹T@Á¶¥žºÒböß#•È_’žç™ Š‰ê¿&ÕÚ˜5ö¬uvu&£QÊŠzBµ|Ýlß_6oÍLËÍ|ø«¬°Z™š¨ûwiùõpW-Ÿªoóh~PKs�N>`÷‘X Ä/testrepopacked/.git/hooks/applypatch-msg.sampleUT y_YMÆcbux éé}�=OÃ@ †gîW˜´êIÄÊT$$ÔuG×ÔÉY¹/�µñß¹k íÄj½~ž×^Ü·;ò-µP xñ€Gí¢E0!ŒÀ]¢( :ƒÝbºà Ø0€Cf= ˆÑÃî” :F{ŠZ:} t&ÖN“�ÃÍY´5³Á„Éî³5#$|ðõ'¦,Z&Ý & æ‰ü�qG 1‘ü«@=‚öÂ¥.Kˆ7m¸‰ ÂÚpÀ}‰â>/Þ6{²sÙòz—Ÿ"†øLy„”GîwR¢U]¯¯U£TIͦf”)*A¨�P-ß6Û�×Í{[hÜ^Üç%X­Ô±û7´üzz¨–ëê[=«PKs�N>hÓ†9Ý[+testrepopacked/.git/hooks/pre-rebase.sampleUT y_YMÆcbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘ªüH•29MCW±�%ƒx.Þ~÷Ïw'×gñyaWåg Å£ƒÇÏ»&- ¼Ú¦3<_ö£˜á:æ� °�—p¯7û0|w2ù«œ¤×ÑŒé{CL¯ót:⻲Už¹áì}9GóN¯ý+Œ0bU® îDîÉNŸ�Apï]�<˜{ÿÊž_�FAp1üÆ·"KƒŽýŸ½ÂÉ Dé!’zZŽUsß« ÒIÿ¸zýíÛ·>ŠÁ€^].ÑÌÚâ uŸ§×1„ž ßã¶ö»&õŸöœ¦ùqˆ`GÿçGˆîX÷[‰•©ZÕÒ,íÁ‰¾–h_ …"j­ŸßÍÔÀ€.—SŒòÁJ’r}Hkë‚Gr(�çBÐ3ú¹LÔþìá%Æ�ýèøxwå ‹;ž@O‰E˜5Ï"¹’؆*ã¤efTâЈ¨¬lî°É|Iï®s'ˆÄòr×Õ°3†UCè—È©SË©o·ÜkKjfQgh íÔÌx7‚`ÃV0¿’&ϸo‰òNYì˜`Þ<±(A5B/–¿åx~¾f^8_6ÆÁŸNß0i�É|J•(k¥Ùú&=(X3_(ŒØÓ¬Nd3FÛ/èœ~Üt}õ}AŽ`ÜÉZ–+Œ.±ÆEãV¶»ˆ¡ x861YÅ=Õƒ Ë·úä!:þÌ2Ñ˸d4jcÌJ¬Ì‚3 ¢ÔЕC¾��™ÌÃP¾ãŒœ”i¶ª�\ýx)ß½¹ºyýæí,¼�­ìKf±Q‹_Â’I ¬Ç>a#ÝH¿Ó]©7€÷ðtDÓé”ÐFö�ÆýÖ‡‘‹¨ÓiƒÁHá¿P@+Ù~ýñ!Ä—Ÿuu•-wðéXa$Ãß3'¹}]5g�*Ä3i_v‹:ì(lÚ«.¿J>6fØ[Íy‚'¥à"™¦@:g.°VJ3Šˆ^!š«/dd×A:�ôØ”& è(¨`¿±ˆáükQõÝß¿vµá_ñY‡u±¡€Y2Ñß9D;vèÍ·ã½íÉs(l±º>YÛÝ�„lø’h‚nTî › d·*ÃJ:bßÌPX\Mž½í…Øë²N»IkãÕÇ’ÇÈÆm3*çæÑQ4ΰ–•m��èÿÊϹÃÙ*Éð*à0´š6”âoÊ[Êy^HŠ"V(°ßë�GO&“ßýFÐ{ŽYÜ›ÿO®™ŠHpòwA‰f{ÏÐtžò7�´Ý—^=¥œu¾íçýÂ.Ý›xùˆ2ÒÍèë"g>êÿMÛ@6[‚c¸À~éOz�›Hhå¾^°]Rš}B“�%É¿"ÄÊAßh{’çyåyÒ¬ôËSÏ}¡ê"o3´Ù”Ø�Ù/¾åŸb|‰„^ÝLR¯çÒKå°ßjX·¹«½®¶ðË(o÷Ž™ü ÂEèxªÄÊnäJ‰WO_ñ€.ªý­›A|ðHó!DÓÿŽp˜"~ ³5* cßÿ¢Ó4�/·ÂÿPKs�N>ƒlÇz ,testrepopacked/.git/hooks/post-commit.sampleUT y_YMÆcbux éé-ŒA ï¼b WMîÞü€'?°!‹PKeIUž/‰^»gÚ^¦9–Iƒ±ÆâQÀ;åšAäuk¬ -PCT8J‰�o¼‚ ›s¬ê·Ô¯NrŽç*ÓÂã™{ ¸ÐÜk-tq$¯X;ÊâãáCm·_b�¹ã)}PÞæ PKs�N> Â\*+testrepopacked/.git/hooks/pre-commit.sampleUT y_YMÆcbux éé}T[kÜ:~ŽÅÔ I±w¶!IÙÂ’}9�óÐSŠl�×êÚ’+É›l)ýíý$Û[ÒSúæËܾËÌá‹Y¡ôÌÕÉarH+Mü$Ú®aª�Ù�+­êOÐV'b-”v~Bn'‡t§•W¢§º!©ªŠÆÈ€�ÛÎïÈ[f2Å.ý¯:‹âêâ•,/eqÉŵ˜_ÎùÕ¢¨®äåõÅÕUU0/®ç‹¤R °ÞU´3}$$ ‚6æ1r+\©TDp;rìð[N‹�o{Γ˜„œ˜²Ññqöƒñ¼oM=R¼­v'testrepopacked/.git/hooks/update.sampleUT y_YMÆcbux éé­WQoÛ6~¶~ÅU6âÚ°å${Kš ]‚ }PlÉÓP´LY\dÒé¸êÖÿ¾�¤dK²k'HŒ¡IÞÝwwßÝÑÝw“©��Ý K%ñ¯l±Ì8¥J=’Žs±4dM3?jZI&¥2Ìð6×”äjA\ž 9� â†e§…sa(ç1O|¼dñcHkaRbù|µ€ˆ¾Ài"Ù‚“NÙÙXe3¿�|íÐÜ)hfS€1©ÐÑ"NÂí$ž) WË …‘»Q2s,Æîƒ…•Ô€©uÍÝY=S¥2Î$in4­SnRžïººYFSNN6…„i\uzr¾TZ•Ñ/ÍxÂV™±ç­•ìˆFM,3žqáûwáýŒW�X¨™HŠã ˜µN VXÛNHÀ8K�yŠsÎŒP2ªuê@€ƒ¾Os&ãô¹îûÛü-C0ã²pèŸ%ç exV”ïÃ�¢ä–‡uÌà¯ô’ד ^,˜œQ&$ʹ {ga€ÉùÖça€2ñëŸÂJðO–pt)�‘Ð_4þFaï·Ow·ŸþéË¥E$ƒ�S”Í­‹G¾’KYí®¤-ð¸# éú伤÷P^¨­™4#·ŠÕÊÖïj¹DxJƒÎ˜ÕߦÞ)}€[×ôÁûƒ…wæzPÝü ꜉jn”‘i¬ü†—Þ~÷Jvܼ×lÎ/XÝ5ZåÁu’vã¸ê½·­-v§¸h©òƒ3vØ~Lº~w´ z@º}µaÅû<øX n:Ã1ÁÍÅ� œ#%*'©ÀuO*4‡`™«¿ylì4jÔí˜SÿìŸþ–¡“ÚýpÄLsÖCUÞK˃Y­²Â!ýG!DÊ”‡CúìÅêüÄH™ö}€»ònf¿sypÍâ œ;¦Xr��±d™­ÜS|¢(²ÿFèu}Ô²+áF¦�'¶@º(øÆsuž>ózÚo)}…µUQ#·?z°Ð®¼­€gš7ÊÔ13v®�M…àx^F¸ª¬QeÐ »`wp¦'vòL†#ï¢Üéb>Ž›:U¹y¨VïßrÕínu|ǵҵv­„ô^š|ÅkNvj ½COj[Q¯audó‚óælÀ^�+>ßuÍ÷ˆBßÊÎ8t6�5}é;»±[u;›YrÉæ€mu•ìé ªË¡FÜ|z|ÜJZøˆÕC±©¿£�¸­&¢Ê/ðø JWÁb;¯u”›bo ¤“òϽ'<õr„³â]ªúi"W˜�ç×'ÖúžÔÂ�~%Ò‡+èc³�Q¡�Žödìwã18šªÛ/›Tg[� JØkw^wá@þnª×+-¼>…ú=Lk;±3e^··Âë_Ríx›?¶ÏªýûŠiGî-|mi}•ÓC�÷£, •¶Sã¥-‚C¢¾ÿ³½[kNîÇ�¸è�R­¥H¤ò¿|lSB”¶E‡w£ÙÎ=º ¸Ö¤ûUH¡S¼]Ý…ÓàPKs�N>š ÷Àн,testrepopacked/.git/hooks/post-update.sampleUT y_YMÆcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPK ;fQBýžÔQ testrepopacked/bye.txtUT âà QÆcbux éébye world PK ;fQBíAtestrepopacked/UTâà Qux ééPK -fQBÙ÷ÚŽ¤�Itestrepopacked/.gitignoreUTÅà Qux ééPK s�N>¢§ÓÝ((¤�¢testrepopacked/hello.txtUTy_YMux ééPK i€|QíAtestrepopacked/.git/UT¶fÂ_ux ééPKi€|Quð³~¢¤�jtestrepopacked/.git/packed-refsUT¶fÂ_ux ééPK s�N>íAAtestrepopacked/.git/refs/UTy_YMux ééPK s�N>íA”testrepopacked/.git/refs/tags/UTy_YMux ééPK i€|QíAìtestrepopacked/.git/refs/heads/UT¶fÂ_ux ééPK s�N>¢Tž{))¤�Etestrepopacked/.git/ORIG_HEADUTy_YMux ééPK i€|QíAÅtestrepopacked/.git/logs/UT¶fÂ_ux ééPK i€|Q¤�testrepopacked/.git/logs/HEADUT¶fÂ_ux ééPK s�N>íAotestrepopacked/.git/logs/refs/UTy_YMux ééPK i€|Q$íAÇtestrepopacked/.git/logs/refs/heads/UT¶fÂ_ux ééPK i€|Q*¤�%testrepopacked/.git/logs/refs/heads/masterUT¶fÂ_ux ééPK i€|Q(¤�‰testrepopacked/.git/logs/refs/heads/i18nUT¶fÂ_ux ééPKÊ‹PB‰¹—XšÖ¤�ëtestrepopacked/.git/configUT´Qux ééPK i€|QíAÙtestrepopacked/.git/objects/UT¶fÂ_ux ééPK i€|Q!íA/testrepopacked/.git/objects/pack/UT¶fÂ_ux ééPKi€|Q <Ö®g(R$�Štestrepopacked/.git/objects/pack/pack-6371e8325a610822b319bbd75ccda015ae31a08d.idxUT¶fÂ_ux ééPKi€|QÙøÓdµS$�} testrepopacked/.git/objects/pack/pack-6371e8325a610822b319bbd75ccda015ae31a08d.packUT¶fÂ_ux ééPK i€|Q!íA¿testrepopacked/.git/objects/info/UT¶fÂ_ux ééPKi€|QrÙ-¤�testrepopacked/.git/objects/info/commit-graphUT¶fÂ_ux ééPK i€|QTÂÍ™66&¤�testrepopacked/.git/objects/info/packsUT¶fÂ_ux ééPK -fQB+is§¤�—testrepopacked/.git/HEADUTÅà Qux ééPKs�N>7‹?I¤�testrepopacked/.git/descriptionUTy_YMux ééPK i€|QíA˜testrepopacked/.git/info/UT¶fÂ_ux ééPKi€|Qä�\„Yt¤�ëtestrepopacked/.git/info/refsUT¶fÂ_ux ééPKs�N>w=Í!­ð ¤�›testrepopacked/.git/info/excludeUTy_YMux ééPK ,fQBƒ>Úk"¤�¢testrepopacked/.git/COMMIT_EDITMSGUTÃà Qux ééPK-fQB¦dÀ±™¸¤�testrepopacked/.git/indexUTÅà Qux ééPK s�N>íAtestrepopacked/.git/hooks/UTy_YMux ééPKs�N>i 0›ß3í�Ttestrepopacked/.git/hooks/prepare-commit-msg.sampleUTy_YMux ééPKs�N>}«.íU(-í�\testrepopacked/.git/hooks/post-receive.sampleUTy_YMux ééPKs�N>éøÊ÷€+í�testrepopacked/.git/hooks/commit-msg.sampleUTy_YMux ééPKs�N>%[9ýŽ/í�ttestrepopacked/.git/hooks/pre-applypatch.sampleUTy_YMux ééPKs�N>`÷‘X Ä/í�Ú testrepopacked/.git/hooks/applypatch-msg.sampleUTy_YMux ééPKs�N>hÓ†9Ý[+í�M"testrepopacked/.git/hooks/pre-rebase.sampleUTy_YMux ééPKs�N>ƒlÇz ,í��*testrepopacked/.git/hooks/post-commit.sampleUTy_YMux ééPKs�N> Â\*+í�o+testrepopacked/.git/hooks/pre-commit.sampleUTy_YMux ééPKs�N>R¼­v'í�0/testrepopacked/.git/hooks/update.sampleUTy_YMux ééPKs�N>š ÷Àн,í�4testrepopacked/.git/hooks/post-update.sampleUTy_YMux ééPK ;fQBýžÔQ ¤�÷4testrepopacked/bye.txtUTâà Qux ééPK**RQ5libgit2-pygit2-d86662e/test/data/trailerrepo.zip000066400000000000000000000420311521317216500215770ustar00rootroot00000000000000PK «½]S trailerrepo/UT rk|a)Æcbux ééPK —½]Strailerrepo/.git/UT Mk|a)Æcbux ééPK –½]Strailerrepo/.git/refs/UT Lk|a)Æcbux ééPK –½]Strailerrepo/.git/refs/heads/UT Lk|a)Æcbux ééPK –½]Strailerrepo/.git/refs/tags/UT Lk|a)Æcbux ééPK –½]Strailerrepo/.git/branches/UT Lk|a)Æcbux ééPK –½]SµcdWtrailerrepo/.git/HEADUT Lk|a)Æcbux ééref: refs/heads/main PK —½]S‰x³AAtrailerrepo/.git/indexUT Mk|a)Æcbux ééDIRCTREE0 0 K‚]ÆBËn¹ `åKøÖ’ˆûîIrÕ?x}†©2¢Z…7Îâ6ØF¨ñPK –½]Strailerrepo/.git/hooks/UT Lk|a)Æcbux ééPK–½]S„ìXQß"(trailerrepo/.git/hooks/pre-rebase.sampleUT Lk|a)Æcbux éé�XkoÛFý,þŠ[E­¥¬¶>Û¸ŽáØA›índ�‹Ä±GäPbMr¸3Ã(jìÿÞsg†"-+ébÈæã¾ï¹�Ñ“of‹¬œ™Uô$zBçªÚèl¹²4ŒG4?<ü~ÌÿNÿ¨ËLÑ9ý, Q*G{¹’Ô¯´œh¹Föi¥Ô-e†t]Òïµ±´�©Ò Zf–"c…¶†••KÉpý»ZŒI” Å¢$ü(KKÒcUü<Õª`©%xh±!ù)³|¹Î,̦R•“?¤V,ÛÖfºµ®1(y.Gîä¦*ÏÕš%TB‹BZ©Í3Ç58¢ÉÄÑÔ•±ZŠÂÝ©3ih- xõ­ô&M™aÞ0,´(ã|f¹ÞÛ„†J“,*»¡õJ–þ1¿wÞÕZ³§žqÔ˜ {�(ªZWjmàÄš¬j㢪,<0É®„%� ‹Ö&ˆ(¤^B5xJùÉ4–¹(.¥µǰ7µaþ’ÌBÆZÕy"Sç–²ŽáA Uõ"ÏÌ z`°Uz3�¢ð섵F,Þ+>éŽúQ–’•ÀDð¤O'4�‚2ê9wNúZ¦f¶‚d3Ìû‘Ì�lÞÝ0z̦X¨<‹�µ”~~yvqCwwQ��@‡t ƒ% $°µH§®+Û�H+b¶•9£4‹¢ÖÁ§¡–¨cÀééìé(êGüïë*¼Ð@SK#â±{�„�–“’H‘7@%ñ w�6Uò{R)Â@þ”è•A6èÍ/œKOˆûSÖq¡\îe‰œä¹« ƒ÷.`€� Öä¿­ŸwwôÅ+EÏ¿›Sÿ5J¦fS¼E�,ø|ݳ&Øàu¤5«hÁå­<��물ö·>[€êH°( ÀÖÚ͉*%!éƒ'Ûšt³ÅÅä<| « ÒÚ뙸¢÷sŒ Z¯e¡>r°¦[oBclv¼ük×j�+H-QfèKvê0Òñ¥§;Úà§HOJFÑFÕmËØXÈwЩ2ß\3ÃõÑNp¶±øÐägð9”Ð=ÝA´¶7öùØÛÏö¶ê:vpýuÌ›H‡ðwên«­5Ò«½‰zû2× ÈËÛ›º¦ƒÔ‡2Vúòh’Þû¿Öó-#”kÔCý“`;ú—àì°hoVífd%uNIÐSlB¹ –ƒ³·?ýúîðê8”%™–±E$²²¹Ãò%½Û¾#Ë«YWÃÖV u O�+“©k–Ü)KéQ¿¨3”u;ó2Þl X³Ì/…Î3îºÀ’¸•"˜×�,ŠQOÐÀk!ÇÅí(N€›Ž™Η�q0Å¥ÓuÌI«3„RÆÒ¡7®Å ÖÌ ÆÀÄ[)+öD7‹™Œ�áÃöúž]_]—D�+´`ø;æ8— Ç$ '•*ÄÓH,_v‹ÚOx6íT—[š�3l€­æ4ÀsNyp‘HS �3çY+©ED/� ÅÕç3²í �@:lJPtT°Û7¢áükQuÝÝž¶µá_òI…u±¾€Y2Ñ¿8D;6 èÍ7ã�ÝÇqï)l�º>Y›í øl¸’h‚®eî › d»èƒJXb×ÌPX\MŽ½í…ØÊ²N»IkíÔ‡’‡È†]1(çæÑQ4İ_‰Ê´‡FôéŠg„ÜádTxåqè[MÊèŸÒÊy^ë‹"T(°ßë�GM&“¿üÐ;ŽYØôšÿ�®™ŠpòwAg{‰f;ÏöÐtžò7†´í—^<¦œu¾íçý#Â.Ý™xþ€2ÐÍèë"g.êÿKÛ@6s>ŠÎÆp�+üÜ�Ó6ßÊÏ\½`7¤4û„&J’•ã7ØÐ“Ï Ç“f¥«Xžzö Ux›¡Í¦„nÌ~ñ-ÿ�âJÄ÷êf’:=çN*w€ÝÞÈPò̭Xît]¿CŸyÛpHäóÿ™ïx²Ä­ÅRF/)¾ä]T5úZ7ƒxï�äƒ�¦û`?Eø8 Ž@Ô¹/ ²ÇÔ.ͱ·0yë^טùccÂzêgkPâǾû=¦i_n…Q³þý PK–½]Sš ÷Àн)trailerrepo/.git/hooks/post-update.sampleUT Lk|a)Æcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPK–½]SÓðæß .trailerrepo/.git/hooks/push-to-checkout.sampleUT Lk|a)Æcbux éé�VMsÛ6=G¿‘=­Ý1åqŽN“Žãº“ú1­;mO\ЍI€Á‡dµÓÿÞ·DÓ�fÒ“-r÷aßÛ· ž¼¼¬µ¹ôÝbq"nŒ G9Œ=‰ÎÚá•ÓcÁŠ862��Bu¤¨©lÄsG$¬ÁÓ�bŒ¾[-NsßiŸðW›­E‚¨÷U9R¤·T�R=œ]�‹]GF Û‘TÁóQˆ£ išr²G@KŽŒ¢3Päð³Ñz¬Û_¤Ø:JÙ N“Ÿ•ϯj'�êð¯ \žŠ¨¡ß˜ fÆ`D©wÕ�ÙßæØwAYÓêMt2hh°•NËÂÓk†ä|ìãÉ&kón/jeìÃ…ð02SÕ‰aô(@·©Ð�uÚl²Ê¥ oè06Ç8l ™¢“Á8D·E/Ñ:;¤è‰+æl•^gùjºÏœ/TÒ÷ ´€\ð³½�Ž ù†åOñ)ÚÐR3c~£Ç'*¹/«çæAn $Ÿaì–œÓMîcQ5ur«mt¯‘·#åÂ,"kêíúh6õ€:ý<ù)0,œðAºÀ¬G«AªµŽû¼%£™ã`!«V©ã«—Q)>ñåhÖBì4¤ÜuºpÝÙ"ŸÍ®›ÔÎŒ³v׋ŒôæôŠÇóT–<¢l)Œ5Õ_ä,b,Ûhqv±Æ’Š·ükˆ> ½�ÚϺkÝ'ý?_-Mâì\ü½xAª³âí¯Äòô«%~r9W‹¸ÂCs‘ƒ| dDa¼—ŽLšMžÇãþ²eær¬„K ‡¤†¼vpÓ¥§yÿŒ´3+Nvåƒç‚ÎÄÌþr”¥˜Ì�ÛÍ=›j/\4iÿaƒ5U"SEQ âýÝÍ·�èj™AÕ†�i ±çâ9§¥0ÛEŒ¤MlØ]lÀV±Ý’*oeÈ�MÓ÷sܦ­˜¶˜ü“E�Þ@ƒ/}’¹ ¶šÔH�G†6¼¥MY€¶vÝÐØÛ=F¤�&µŒE¨£î±O.ç×ÞJegý`Ce²ÕN£VÌ?=’ŠìØiwÏV|ö_zEÊ)h.7®ËWÉÞF±“&Ýâ#O.Á?I /ì�ÆóÜ”ÅÅ@âTÙH| ìY¡´X¥Rp4zܽ´Ú¬ØXü ?KÕˆÕjÉ~Šž8âŽj”¡ãN¢²—Éky6«<óÕGQUzc¸«>ÖXM�Å3,5G¸ÓQ©Y¼à]´üu俤{=uH´žl–‹VÏàÙSU«3ÊǨq5?aý[ÑbR:]«÷¸Ü )Åäù¤ò¹“ÜèlÜtÏ hÛëÔk!:Œä0kD3$Œò�Xò’XŠkq÷ýO÷¬ï¾»[ÿòþæjýþîwæ�†Q†D@T”wÊ«·— m/MìûR4c¿áw ê=•ß§gœ�»ÊÖ‚‡¨Ê'[Uù€6‰¯'œó#¢•ŽL¢)©øÒ>¦Þ)ø¿4<¦àtì·Öò4oéå÷ÖÆ¾I¶/uÏwŒMÛ=ÉÊàÿPK–½]SP¹˜cÿ/0trailerrepo/.git/hooks/fsmonitor-watchman.sampleUT Lk|a)Æcbux éé­WYo9~–~E¥#@R u;Ù—�{g�̑Š1rlˆ3ÕMIŒ[d‡d[<šß>UE¶ËqœÝ̓£füê.>|�ÕÎfS¥³Jڲݮ�ç­Êý˜¯„ÕJÏ]øzqþl4zUIýdÜn?„Ÿ4È/bY•Æ\‚Ë­ª«(…ó¨¾ ¤Þ\J‚93v)¼'I‡Âä =gnSûªöŽ�ù¿�L`ö#*e`�_ q%a*‘¼í”ÎQñî‹§p.üÂÁ²vÕL%XY ¯®ˆÎÜÖfÆ¿WÆ^8o¥d„NV‚Ü^ÀtMØ‘ˆÁyùî÷èR‹iI÷¢ƒÈQ¼@‹eœÂ�?½þõßœZïtn–KŒ…£?­çóý ÃááÍÛç?¿~ I祷è¼Ð k{¶�ù%{,äf§¥$l2£­f;pRJ�ëv«P’wÚÕUe,95Xµ3˜µ5bÝFA7Å{!m·ÿ%¿`*PŒ¦‚ `æäBSI¥iÊð6mrM]7¡pN8–§0—á9'…²½þ8ðYéíé�ã÷'gô¤ºœ�ÛòJ”„ÚÊϵ²þõæÕËÑè?oÆíÖ– “æïn¡– �ƒÍ±äùùí’ç焯/E­óŤ 2Ctõnœ“fBjPEs6aO’D‹\¯ÜÎö '‹^Çô9 ­Pv+]]z<ž]ç¥É/7øñš?¹Ò6R·!‡Š©ˆ¡×aAU $ËpÒMwë«)·Å5«Á=2j¶@r–àNþ†wxSü/!TXÉJcéË(1ªý쇄 !Ÿñ“9¢»Ðç²à±cIÚ( xõîí¾®¨)*Ù$'DÅSÌ�΀N9\5nܶ�«ib‡^¬Œv”˜Ÿ¿l­„À“&o’�céü"PuAI�Ù™¶Qª•‘mÅ´¾Q.€ÙÒù'<8…òCmõ®†gØŽ‡gä�áY!sôSo‹½l#gacc…ÐN9´Oz�žýöûó ;8þ~ñrÝ­åÃO0j3¬‚_wûíå ôaÆÆ� ó ÚöÍN@¡~¡¹M`ò ªwÜ\¦²4+XÉ.–§pÜô)K©s;š»Yà ³z¾�1Ç}¦8»4Α™5K¾�²5–…´õ!)Áa!r,‰ªTnÁãaÔŽ‡Š%|C‚ñÕç"B»hS@"é°2—:_cÇ-ä¶Y–8Ç-/O=�1r<^‹eáb+Õ’€ $ÔóxŠ6ër�Ò¸×Ç v~CeÒ¢…†ôÌj‹dKà ‡ºPÁÕ¡AàúÀ�Ói½c— àd�û ?C’'¡1ÝâX*®ä–A”$Ü›8ÑBpOáéÓ¤õóËçDi}—É�jðU­èéÑ-Ñ0=zoòPò1ïù‚IÆ£Æ ŽæŠ�+ùø¹7øÝÙ yíhª€1߯!2kJ…â®ï‡wºÀ‹oÇxâñø¨?Ñ(‹½.ÃÓ¦ŽÏ6wwù­]ÐèÚÙõU£Ö¯ØÕ��MãNѬÆ# HT¡Ÿaáj³>½_óྸuåh2þö-JãÊ š½v´ÓpÏKk�MÇí{ÿ/Èþ¸¸Îþ‡Þ|Û*°�Þh3´9òÒZŒã)D.|²‰›EÜ—Îà„{Ldßû‰�—Y—bCÁ7åUܲÓGØÒ±…a_ÃÛÓG}zUPÛŒ¸²ØøŽá�êøŽa²ë¶aysZ.Åå^‘¹¯NÌ{,™SñƆzzÿà Ð޾™h�÷»f:KPÚ|Ï"ößlb7+¿ßo=­ðÕ&Ê•X»X${ï8ÆÅLY|2†î ËGV¦Ò{2‘øXãÀD̘0˜B~�À¬s^…p°Qþ&Ì·}Aí'|û5K2ÔÅWgpÞáÃOw=>p×,O7ç…p¤Å‡sI/|7K�¹ƒÓW]©¢Á–4æéÁBÎÛYï[¡þ?ÇúŽÍ¾—dI?À¹mÊîñ²¦ðø¹ùôÀ5Œó3Fè„gðQ_<4á»,ˆŠoÛ×�g[³[î5�é�WÔ{ºK·v]øóOhòõ|¥t7¶–}]§ð^é<�~•þÙªˆÁ:àø ¼Í..²‹ŒúÊd‰™~ÍO;”;–¢ÓÑ(�*7{Íû÷¦ý7PK–½]S ù½^&trailerrepo/.git/hooks/pre-push.sampleUT Lk|a)Æcbux éé…TßkÜ0 ~>ÿZîØµÐ\¼­´…Q+Œ2ÆÊÆ6œD‰½:vf;M;öÇO²s½Ûìá8Y–>}ú$gùâ¸Òö8(!–ðÚ>Ê~0ʹ{µ×C„èà½nŸ`R2‚ +7&…0ŒAa³¸–Æ`Õ�ŽÇ7È6¢AɵÂúž‚¢BðØ»ˆ¢Œc8‚Š+l�G�ö)*m»”R!Ú,•¹i)›8ÌäðQÇ“Ž $XgËŸèÝ JçŒ3icö¸Š%~d�Ô&ý×™{‚an­3ÆMœ:H/{¤ÂyJ[�BYÂ-ùÀµû}�“ÒuÎçBŒ[!c4Î"§žqê݇wÿ�¥hê“o2ý¨Xð1ð‰Ú¤êÍs]åIæ»±G›¤È½â�QšÜê�%Y{µ³ÛÑQÝÚõ}Ò.1‘¤{f�EbFa£É¦1m1q‚ãdØ6ÒS˜OÛY6ßg™.Œ#U‰e{µµ�nÈžyç‹ùÀ7»©„¼ƒA¹‰G4±^ƒÇê/«EÊïØ£ÇTܸz Av‰ž��ðÒD‹O7ï 8˜œ¿g¦ƒw�§ÀÃ�¹þe±:-Äè g…¼C—«ƒ.o­*]õëHÓ ±!€‹ãŽíHBÿ‚èaýù¤|%ËöËÖ'ëC!HRÃÊRçߨÙÙ¢NçÉ%çl’W4N,4­†Åê9º€K:2£B,¨O+‹%¼%ù©Dƒ†v“<çb�&°µƒØaÿ�±!”[œ òÒÖê(½|š1ÐKØŠËAtÛ±>;>ä�KÂÝÐȼüôCäýùÐR‘îØm6¢·Z¤¯ùcÁ;4¿‚.²1�‡¶¢4TJ ü2i°¬¿RšàZÅáž*V¬2¾X+W/Ï xãFÛìä…Y=ñˆ?)ÛwY¤LúþÀifÍ¿ô~EòžˆßPK–½]Sï³2 Œk(trailerrepo/.git/hooks/pre-commit.sampleUT Lk|a)Æcbux éémTaoÛ6ýlýŠ«$ `:I?fuÃ˰A¬ö¡-Z´„Î 5´��ÉØwÞšŸaÿ±¼ƒŸ�‚·Tß“Ù$Æp~-Üùh“‡9ýÅ0Z×x›òU¥KùÐù„<0•KY† \`î)vº„Ÿjð –Ô@”q°m²��l°Ê'šž¿dJo4Y§ô…..Ä|âSiêá)_Ñ™·,ƒï‘]£#ôãRsཱུé2Já~_{Hmf©ó lcÅFž>ú��t{séº�!�| W3ŠÖ•Ù“™Û×^>À '¸£Æ¤¡ÒTàCÖåPÃÑn†Ï ÐæÅÁ‰‹dC+Uê²F€RÒ+å]sÀ·Ü)4â-–¤¾ÑùÑùß‹ ÑÓêïåÓÓb%¤•¡ËO¤þýòùæ’¾Ó¾$U^‰Œ7ãt•PîýûÏüV<„àÃ-±‡Úac¡ÿÐîµiH�`–ŸÅî%F­Ô¢: ‡~·äÕÌì}Ø�íØËRôŽv[YgøEzžAžzj®RÅPK–½]S…Oø Þ,trailerrepo/.git/hooks/applypatch-msg.sampleUT Lk|a)Æcbux ééU�]N1 „ßs “®*¤¯HHp.�n½›¨ùSì¥-ˆ»ã´l[^ÇãoÆ^Ü­7>­É©…ZÀ{<ØX‚ËyÔW_8Cï°ß;„>ÇèB!"‘Øî0Áæ([J8˽ƒ¡æVˆ&Zfóêôáæ—§°•TAî=;H9™/¬ˆ-Ov`¬à‰&ŸFÁ�3j.Õ[ÆK?@#ØÄÔêçrÓv×DO BÞã¶Yq+‹7‡ÍÄÁ‡¹¬˜’ÝÈSØy:Qž Šÿ”fm(}½ÞDõJ©Œž 9CÈSQç ™½êî^&‚ù4ÅVB0¦9eÙ�h}ö6ЃVŒÄ` » BÃr)�Ãþ¿Ø}??êîMÿ¨õ PK–½]SD?ó^ÿ .trailerrepo/.git/hooks/pre-merge-commit.sampleUT Lk|a)Æcbux éé}�OO„0ÅïýO0{¸{3š˜½î¦À@¡m:ƒËúévñj�Ó÷{ʇ¦ó¡agJSâ%€6»¤™àbü÷Ù'�D|Söãgža»¸Þî¡�ËâEh¨ÕáÕÎ3 è®(&/X(OTàâÅ!DØ<­ áhÝ_ˆ‹ë<(K›‡4T?”#X¬¬7 eÍåÕ‡ 6À¦”cÊÞ i³�HÛ¨Ë@Y¥#v/«Q{K–˜ Žî}ŽÆõmrAÁvºXœÛ=!ëi9.£ßÿ"Š”©ºñÕ�/jcjèÊŠ]Å$k2B,¨6�ïçöóíüÑì~Üìèát28mÔÿ§5ÏæPK–½]SÙðü•B$trailerrepo/.git/hooks/update.sampleUT Lk|a)Æcbux éé­WmoÛ6þlýŠ«lDµaÉIöiyºúaÀ°¥ŸŠ" eÊâ"“žHÇU×ý÷=$%[–ݼ 1„&ywÏÝ=wG÷ßL¦BNtôƒ>½“Ä¿²Å²à”+uG:-ÅÒ�Q4-TzG+ɤT†>#Ãæš²R-ˆKÃK!ç 4\³¢Àá´¢p. •<åâžÇK–Þ…´&'VÎW ˆè3œf’-8éœ�Ī˜ù…äkæFA3›‹É…v€Æqn'öLQ¸ZÎ)LœØµ’™˜c»VR'¦Ö-p@tcõL•*8“¤¹Ñ´Î¹Éy¹ïêZM99=ئqÕé)ùRiaTY%D¿V4ã[ÆžW´V22Mv±ÌxÁ ‡îG�¸{ï�`¼ÄBÍDV=‚Yë´`•µí„Œ³ ™§´äÌ%“–Q§xÐ÷iÉdš?Õ}›¿ff\VýÓ ”|¡ /ªÚãC˜ QÔÜò°€3ø«½°äõ¤ƒ &gTɃºF.ÃÁI FJ~�õi Lüú§°ü‹eÜ]ÎÓ»@dô™âo~ûxsûþãŸ!}9·ˆdÐãi޲yïâQ®¤ÇR»+i °­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPK–½]SÏÀL ¨,trailerrepo/.git/hooks/pre-applypatch.sampleUT Lk|a)Æcbux ééU�[N1 Eÿ³ŠK:ª@�VüVB‚=°�ÌÔÓDL&QìéÄÞñôâ÷Ú>ÇöânÝÆqÍÁ,Ìo#èèS!çpWcHÆžjìO8/ˆ ßæéœ·„.§Eh«€ö_Êp*^º€¾æ¯L—|�ˆÙïhuV½‡›#äiت7 QÆ<ºOª,^&uõBU¥<Åq§8VGÍ¥F/t£"ö˜ ~žcÉnë]¥4úVÏ“ WÌþ'T�Ò5éã\˰¥’û»Ä®ŒYaÅqpL2£ ò‹mîµ¢˜½+¾2Á¹¹SÃÙÀëvi~°FˆîÛü",–K}uÿÃæëùÑ6¯öÛlÌPK–½]Sí60èÔ0trailerrepo/.git/hooks/prepare-commit-msg.sampleUT Lk|a)Æcbux éé…TÑRÛ0|n¾âp2„�È.í[™t†RJ3SJ‡¤o‚bŸc[r% ÃÇ÷$9i ™6“GöîíínÜÝK–B&¦èt;]8‘€¿xU—…R·`R-j VA­±æÁ©ª*a¡T+¨Ð¾Â˜°§¼,1ƒåD+ºŠà^ØÂ£$¯Tî¯sQ:*n¡àÆ�¾emG�«²T÷�Ña2 j„’kš!l ê0ªÑ)Ƴ"ì@gu£ke„q›`FSv¡½*BN¹7 ç¢4aRIöˆZ�±Ü6fDÐ-;ˆ�/•¶˜ÅÞË�’|é¥{Žn½ þ$˜  j½e�ˆUfÅǰÆ��iÙ�ÔˆëŒLì·Ì…6”Db¯Ô® �ºð­DN‹£´¨·µ®c‹ã ,ë­ý\ƒ©’Ùö` ¯±uc�ù>áLä90æbÁ`:"¼�´•l²üÑ�Â%æªm�ÇLÔÒ:ã�‰mœdŠSÞ¸ähšB#û®W5†@£É¤QéðÛül¸)0ÛtÉüY‡ ×Ù¦Û<Ë(,˜Š•ÄŒ©bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK–½]S7‹?Itrailerrepo/.git/descriptionUT Lk|a)Æcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK ¡½]Strailerrepo/.git/objects/UT ^k|a)Æcbux ééPK –½]Strailerrepo/.git/objects/info/UT Lk|a)Æcbux ééPK —½]Strailerrepo/.git/objects/4b/UT Mk|a)Æcbux ééPK —½]S«:à Btrailerrepo/.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904UT Mk|a)Æcbux ééx+)JMU0` ,PK –½]Strailerrepo/.git/objects/pack/UT Lk|a)Æcbux ééPK ¡½]Strailerrepo/.git/objects/01/UT ^k|a)Æcbux ééPK ¡½]S{yÇ ¦¦Btrailerrepo/.git/objects/01/0231b2fdaee6b21da4f06058cf6c6a3392dd12UT ^k|a)Æcbux ééx�ŽK‚0„]÷½¤ö•–Bôr�>þb£PSK ·Œ®Ü¹™Ìf¾ù\ÇX0lW2æVQá�äÔY V" nƒòRS¥‚àšpdærI÷Ë 2>Å{ŽfŠøPܧvÏx�#øhꔇï%‚3M(®V&Aîý\Öõÿ Ôã tœ‡ï)ãè‡ |•B¨ìÒü’7Û®lùõ¬W�¡)mT^PK «½]S íAtrailerrepo/UTrk|aux ééPK —½]SíAFtrailerrepo/.git/UTMk|aux ééPK –½]SíA‘trailerrepo/.git/refs/UTLk|aux ééPK –½]SíAátrailerrepo/.git/refs/heads/UTLk|aux ééPK –½]SíA7trailerrepo/.git/refs/tags/UTLk|aux ééPK –½]SíAŒtrailerrepo/.git/branches/UTLk|aux ééPK –½]SµcdW¤�àtrailerrepo/.git/HEADUTLk|aux ééPK —½]S‰x³AA¤�Dtrailerrepo/.git/indexUTMk|aux ééPK –½]SíAÕtrailerrepo/.git/hooks/UTLk|aux ééPK–½]S„ìXQß"(í�&trailerrepo/.git/hooks/pre-rebase.sampleUTLk|aux ééPK–½]Sš ÷Àн)í�g trailerrepo/.git/hooks/post-update.sampleUTLk|aux ééPK–½]SÓðæß .í�T trailerrepo/.git/hooks/push-to-checkout.sampleUTLk|aux ééPK–½]SP¹˜cÿ/0í�¢trailerrepo/.git/hooks/fsmonitor-watchman.sampleUTLk|aux ééPK–½]S ù½^&í� trailerrepo/.git/hooks/pre-push.sampleUTLk|aux ééPK–½]Sï³2 Œk(í�(trailerrepo/.git/hooks/pre-commit.sampleUTLk|aux ééPK–½]S…Oø Þ,í� trailerrepo/.git/hooks/applypatch-msg.sampleUTLk|aux ééPK–½]SD?ó^ÿ .í�“!trailerrepo/.git/hooks/pre-merge-commit.sampleUTLk|aux ééPK–½]SÙðü•B$í�ú"trailerrepo/.git/hooks/update.sampleUTLk|aux ééPK–½]SéøÊ÷€(í�í'trailerrepo/.git/hooks/commit-msg.sampleUTLk|aux ééPK–½]SÏÀL ¨,í�F*trailerrepo/.git/hooks/pre-applypatch.sampleUTLk|aux ééPK–½]Sí60èÔ0í�µ+trailerrepo/.git/hooks/prepare-commit-msg.sampleUTLk|aux ééPK–½]S’Äø–I )í�/trailerrepo/.git/hooks/pre-receive.sampleUTLk|aux ééPK–½]S75ÁO\¤�³0trailerrepo/.git/configUTLk|aux ééPK –½]SíAS1trailerrepo/.git/info/UTLk|aux ééPK–½]Sw=Í!­ð¤�£1trailerrepo/.git/info/excludeUTLk|aux ééPK–½]S7‹?I¤�§2trailerrepo/.git/descriptionUTLk|aux ééPK ¡½]SíA<3trailerrepo/.git/objects/UT^k|aux ééPK –½]SíA�3trailerrepo/.git/objects/info/UTLk|aux ééPK —½]SíAç3trailerrepo/.git/objects/4b/UTMk|aux ééPK —½]S«:à B$�=4trailerrepo/.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904UTMk|aux ééPK –½]SíAÈ4trailerrepo/.git/objects/pack/UTLk|aux ééPK ¡½]SíA 5trailerrepo/.git/objects/01/UT^k|aux ééPK ¡½]S{yÇ ¦¦B$�v5trailerrepo/.git/objects/01/0231b2fdaee6b21da4f06058cf6c6a3392dd12UT^k|aux ééPK!!k ˜6libgit2-pygit2-d86662e/test/data/utf8branchrepo.zip000066400000000000000000000411601521317216500222030ustar00rootroot00000000000000PK ‰BLutf8branchrepo/UT ïŒtZ-Æcbux ééPK ‰BLutf8branchrepo/emptyUT ïŒtZ-Æcbux ééPK #‰BLutf8branchrepo/.git/UT !�tZ-Æcbux ééPKäˆBL«~“�Þutf8branchrepo/.git/configUT ¬ŒtZ-Æcbux ééMŽ»Â0 Eçä+º#U @À€ÿÀV1¸©S"%qä$•ú÷8�ñøú>CŒO­eWˆWK ,ÈÙQì®ÝV+ë<šP¨pE­Fà|ò4ƒ÷Œ¶¦ æï››£¤È?g¦ ]X£3™CÍÈ2$Bh·{œ×îÁuóÂE+ à¼(»ýñp9Ÿ6P>Ú­Ysߺ’_ûÙ•W{©ÑoPK ‰BLutf8branchrepo/.git/objects/UT õŒtZ-Æcbux ééPK êˆBLutf8branchrepo/.git/objects/4b/UT ¸ŒtZ-Æcbux ééPK êˆBL«:à Eutf8branchrepo/.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904UT ¸ŒtZ-Æcbux ééx+)JMU0` ,PK àˆBL!utf8branchrepo/.git/objects/pack/UT £ŒtZ-Æcbux ééPK àˆBL!utf8branchrepo/.git/objects/info/UT £ŒtZ-Æcbux ééPK ‰BLutf8branchrepo/.git/objects/e6/UT òŒtZ-Æcbux ééPK ‰BL´ ¤˜Eutf8branchrepo/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391UT òŒtZ-Æcbux ééxKÊÉOR0` °ðPK ‰BLutf8branchrepo/.git/objects/41/UT õŒtZ-Æcbux ééPK ‰BLཫ22Eutf8branchrepo/.git/objects/41/7c01c8795a35b8e835113a85a5c0c1c77f67fbUT õŒtZ-Æcbux ééx+)JMU06f040031QHÍ-(©dx6÷ÑìM¯9{wk®+�ºqèIOðD6T½PK ‰BLutf8branchrepo/.git/objects/82/UT õŒtZ-Æcbux ééPK ‰BL[nÉü––Eutf8branchrepo/.git/objects/82/052aecedd419e87f28742a598154cd56b0fe79UT õŒtZ-Æcbux ééx­ŽA Â0=ç{/”¬iÜDôèÝ$ëÖl#i"ô÷Äx�ÃÌpš¦X`oú]É"Ð!±FvÔ[olpâŒE4ÞYoY32Ñp !(_˘2\æ{–n¹Ï£¼áˆÆv½£Æ—;×EòÒÎ)Ëë¹¶�XÆZNÓ Ð"YG¤m´VÝ~Šü߬®s,Ñ?á›P_ÍOPK #‰BL+is§utf8branchrepo/.git/HEADUT !�tZ-Æcbux ééref: refs/heads/master PK àˆBLutf8branchrepo/.git/info/UT £ŒtZ-Æcbux ééPKàˆBLw=Í!­ð utf8branchrepo/.git/info/excludeUT £ŒtZ-Æcbux éé-�A Â0D÷žb ‹ªØv/¸\yqÛŸ6’ä—䇨�g7•î†Ç¼™ £ØØhc)¢iX& k OoÓ@�ì.miuÆkî6¼«p7¾2)AÙÈ„ºª¡¡gçÈKlKóÆ sà7õÇQìãq=� ÙZÎÆ�Ȝ쀕öÈ< ’€uYØ^1+ >bŸüö°N8�…²Z#EúóùÈÇöÁ깆ïîPK ‰BLutf8branchrepo/.git/logs/UT õŒtZ-Æcbux ééPK#‰BL´"9Ímutf8branchrepo/.git/logs/HEADUT !�tZ-Æcbux ééÍÏÁj1às}Š9*K—dMš¬ˆè±w_ ›�5A“H6Q¼ù>aŸÄ-K½ÅS霆ÿ‡a>B^�á•B�mËh�Rt•¬R¼–”3Ýò�†t(jØø6â¶17J<Á’Î9«¥(TúÉÖ¹ÇØ—>D<.åÎ&“›R·Ê©àRB¡ sBÞ†ÔÙSëm²ê0[Àç¸ÁØL^þì¯ ìA0¨÷!§¸p²~] œêFHš¨¼6ïçáÆ×õ–S'ÿ1ŠÿŠz¦|GêäPK ‰BLutf8branchrepo/.git/logs/refs/UT õŒtZ-Æcbux ééPK !‰BL$utf8branchrepo/.git/logs/refs/heads/UT �tZ-Æcbux ééPK!‰BL�2ë�‡¼6utf8branchrepo/.git/logs/refs/heads/branch-with—utf8UT �tZ-Æcbux éé�ÈK Â0еžbö…2I3&‹ À ä35‚meÚ ½½+÷¾åCü8�¤'ÎÙ(ÏÎvÚY£y§È¤L»ˆ[í�…W¸ËC*ü�ƒjÈxg«0ÿî´L,S=ŒÂï×Z?žsYb�ÆþŠ”%g­QPaƒ¸‰†Töp3gèdìávm/Û/PK ‰BL8¾ÀˆÃ*utf8branchrepo/.git/logs/refs/heads/masterUT õŒtZ-Æcbux éé�‰Ë Â0Ïú{T e“&nRDôèÝH“­ ô!i*ôïŠwç4Ì þ‰Z:ö‚– µÒ�’N[#´òAŸl™,ÜÆ�x…GZç;~ÃYTZYC…Ë¿v]fNs9N‰_ýZ>cî–¦ôÓp¡iC„ ¬wß:Ä ‡8Æ]¬á¾lgÿPKàˆBL7‹?Iutf8branchrepo/.git/descriptionUT £ŒtZ-Æcbux éé ÍËKÌMMQ(J-È/Î,É/ª´VHMÉ,Q(ÉÈ,VHËÌIUPOI-N.Ê,(ÉÌÏSW(ÉWéʧ"iÒãPK àˆBLutf8branchrepo/.git/hooks/UT £ŒtZ-Æcbux ééPKàˆBLéøÊ÷€+utf8branchrepo/.git/hooks/commit-msg.sampleUT £ŒtZ-Æcbux éé}’_oÓ0ÅŸ›OqH+ÒVM£òˆ´Jƒ"Ø “X÷Déä&7‰µÄÎl‡ñ§|w®ª&ñE×÷þÎ=Ç¿ÈRe¶ŽÆÑ— ôM´]C¨µ¾‡Í�ìœF^S~WrݶҡÑZ²VT´äÉ·¢i¨Àá;âŠO‡¦�ÒÕЊ LÕ·¤Ü"0”h º ÿ¥lˆ®µ°ç'>°­OÕºo Þ’�\i•þ £a�p½…(Hk{©*ÑuFwF G'$d OÊYoÏ:Ýye¦ Úçš’™M£Ù·RÁƒÏ— >–!Å-7)qà]ͳž±€¡`:T|«GÅ"mm/#ž¼U¾Â1�±¨ïE�Y)*R]–)ÇÜH¾ñì6Ú›*’�?ÀA� _¯H‘͇~�ê uÂPú´³N³­6NÓ p8ÁæÍõ›‹ÉÔßöWaðþj{wy»ýpýéîjóîãGXîO›íwÓå|½›-ç“ì/¯±[e]2c\Å+ }°ˆ÷FLj'«Ç#(¯9ª¡¶^‡²Ïjë=ž^k.¿P‹¢ï™û‹~ž–åˆY‡8Æc¦A1Ùÿ³PòG8ÁjãØG¯äÒüäˆ�dûÏ}™¯ü7+’YØóg4 «®_¾Âæÿ{ŒÂû]E¿¢ßPKàˆBL,ß.—ÙW+utf8branchrepo/.git/hooks/pre-rebase.sampleUT £ŒtZ-Æcbux éé¥XksÛÆýÌý7[‘.?tZkd�,g'm2ãjšéز´$"‹î.L3–þ{ÏÝ]E;�–3”ð¸ï{îcyôÍl‘•3»Gâˆ.uµ5Ùjíh˜Œh~ròç1ÿý ýP—™¦Kú^²Ôžöj­¨_51j!­êÓZë;Ê,™º¤_këh¡Rm@´Ê5DÖIã,-uV® $Ãõ¯z1&Y.)‘%AàGU:r�žè¢àç©ÑK-ÁC‹-©O™ãËMæ`6•ºœü¦ŒfÙ®¶Ó�u�A‰Ìsµôä^nªó\oXB%�,”SÆ>÷\ƒSšLŠ+å\ÇÀ1ìMdm™?†$s�±Ñu¾‘­sGYÇ𨆪z‘gv =0Øi³� Ÿ�³VÁâƒâóþà´/²”œ&úƒ£>�Ó\ ¥èywÎûF¥v¶†d;ÌûBåV5ïn=v[,tž%ÀZJß{ñú–îïE��@'tƒ $°µH§©+×½TN&l+sŠ4"�u0ÅkèƒEt xùröl$zgg‚ÿ}]…†ˆcjee"»Ÿ�0ÐrR–Jæ PI>ÊÝc €M—üžtJ…´�?%zc‘ úùGÎe ÄýKÖñZûÜ«(9És_ï}À¬É¿[?ïïé3lÌ‚êX¼Q…þÈÁšî¼‰L<°=Øñð¯}«A® µD™¡/¹©ÇHÇw”žéhc€¿DzR²š¶ºæh³XÆÆBµ¸ƒ~¡Ë|{à 7§{ÁÙÅâC“ŸÁçXBtÑÆÝvØç_`o?ØÛªëØÁõ×}0o"Ãß©»�¶ÖÈ öVôe®AP�w0uM©«‰Ó“¥t*”G“ôÞÿ”°^h±\E O¢aìèÀ ½Ym�Ÿ‘•29MCO±�å‚X.Þ~÷Ïw'×gñyaWág ÅcƒGϺÆûL^eÓž¿/ûQÌðÓGXÈŠË·×›}¾;™üUNÒë?�hÆô½!&×ù :ñÝÙ*ÏÜpö¾œ�£y§×þƱ�*W w"÷d§ÏFƒ ¸÷.H̽eϯG£ ˜~ãÛ�¥AÇþÏ^áä¢ôI=-Ǫ¹ïU©¤\½þöí[Å`@¯.—hdíƒñ„ºO�‚ÓëBO†ïqÛÿú]“úOûMÓø¸ú#�ÄÑÿù¢;ÒýFbeªVµ4K{pš¯%Z×B¡€Ú!ëgw310œË唣t°Ž¤\ÒÚºàqJã¹ôŒ~.µ?wx�±A`?:>Þ]ù¢ân'ÐO"FfÍsH®$6�¡Ê¸üh™•84a*+›;l1_Ò»ëÚ b±¼Øu5ìŒaÕPú%rêÔrê[-÷ÙR…šYÔšB;13Þ‹ ذ̯¤É3îÙ@¢¼Sû%˜7O,JP�ÐÀK%ÇÅo8^€Ÿ­™Η�q0ŧÓ÷LYg2„R%ÊZi¶¾AE ÖÌ ãÆÀÄ;¥*öÄ4kÙŒ�Âö º¦5]_}�E�#w²–劣ƒK¬±AÑ8„•í.bhÞAŽMLVq?õ Ââ­>yˆŽ�?³Lô2.�Ú³ë²à ¨(5tä�/Dãc&ó0�ïxþ"'eš­j#@?^ŠÁwo®n^¿y; o`+û’YlÓâ—° CRë±EØF7Ò¯¿tWê à=<Ñt:%´‘=¤q¿õaä"êtÚ`0Rø/ЊC¶_|ñåg]]eË|:VÉð÷ÌI®@_WÍ9'† ñ ÇÇ—Ý¢û ›öªË¯‘�Í@€6ÀVs–à)©¸H¦)�Ι ¬•ÒŒ"¢WH†æê Ùu�N ½6¥ (: *Øo+b8ÿZT}@÷w¯]mxÁW|Îa]ìD(`–LôwÀ޽zóíxosòÜ Ç«®�@Öv7E£!¾$š •ûÂæÙ­Éð ’·Ø73W“go{!vº¬ÓnÒÚxõ±äÆ1²qӌʹyt ±*¬eeÛ#'ú¿òÅ3Bîp®ŠÇ1¼ 8 ­¦ ¥ø›rÇ–ržÒ…¢ˆ ì÷zàÑ“Éäw¿ôžc÷Äæÿ“k¦âœü]ÐÅA¢ÙÞ³4�§üM m÷¥WO)g�oûyÿ„°K·ð&^>¢Œt3úºÈ™�úÀ6�Í– ÄÅ.p…_úSÞã&Zù…¯l–”fŸÐäcIò/±rÐ7Úžäy^yž4+}ÅòÔs_¨ºÈÛ m6%vcö‹oùg_"¡W7“Ôë¹ôR¹ì÷F†VmnÅj¯ë† ü2ÊÛý|€#&ÿzp:ž*±®¹RâÕÅW< ‹ªF?@ëf<Î|Ñô¿!¦ˆON!ˆ:÷Tö˜Ú•»ã ö&oÝë3jL<�OÃl�JÂØ÷¿æ4MàË­ð?PKàˆBL%0¨\£j+utf8branchrepo/.git/hooks/pre-commit.sampleUT £ŒtZ-Æcbux éémTaoÛ6ýýŠ«$ `ÙŽáIÖ0¼ lÀ`Úb Ä£ÅE"U’²ã¢Øoß#%» Ðo¢x¼{ïÝ»;~3-´™ú*;ÎŽiiˆ_DÓÖL•µÏäK§Û@ÁÒ†�V;ÚV"�ö$ Û¥ÿSi›F‡Àr‚ +Q×,©ØÑh­Ãp7¢­K­»†Mð¢§j_¥²]-ñ˜_ðd5ù7v–|¡C=Ø¡°ï´Y“0$ÚÖÙÖi˜ö^¬™´BŽ˜A @çƒm)T{ˆ“ÄñÉQ€b¨À$"“ïfø£t¼³4jç�I–iE‘‘ãMÞ ç™ò|å·ûå¯t7•¼™š®®i~wz‘¡¬ÉŽÄZhãÃm ɸöœÓƒÑA‹z@uCR+ECdäÆMv3Ùâ.Ã�<‹âjþN–—‹yY\rq-f—3~·(Ô•¼¼ž_]©‚yq=[dJgàú hg»$Hd„ÞØmÒvùqõð�˜FÞž<‡žüF@Ó¢ ¸Ž'Yz„7—Zßž¼íÛj”^C�ÂÚ:Iè'¯âÎcõ?Yjø¯´½]|�ìÜ$ã.éâîtãVÎzOm-‚²®!46’FÙÈ{cµüì_ËØ I wCÊÙ–L‘’ÓëØÇ­õ:X·›Ð_ ŸµµÕ!])QÆ‘NÈO™�pÂÀT0 –þž|+JØ©ÏàH„@é{×]K(§|¢ÑÉ+AFôæ–FQÑ}¡ÓÓèƒßmàCiê`)«¨@ægŽ�í�]  ´ÓR±ã1½ÕáÌÇ$‘?ø}í µ¤Ö:0еŽ.²ôÑÖhª§‹Ù™§iç]šøàÎÇäµ)“%·¯�p¼€0F…AC¤QàCÚ¤PÉ^¯ fOöÐ&Ù‚õîH~ÎóR”ò<ö*·¦Þá;Þåh Ä»]Rþ�NöÆÿž=®þ^>>Þ®"é\ÒÙ'ÊÿýòyvFßi[R^žGgÃp•PîýûÏ÷|Èî�³î†–XCM¿°Ðh÷Ú4‘`”Ÿ¢ÓKLZ)¢ê0úÝx,�Wã²µî¹olË6îDkÈ¢´;xÕÇdi öš#F÷ûQn´ßÏÑa¹pB�7Ã\>ŒcZ©ñÅ—6z7ž"<¹Ï‘ðV\>Ã%¸É2¢ÿÍáO0 o¥9J[õâÇFˆ 8UÛVmK–æ( ÷öOP­Rpv„s�Î'¿+¡ëa‚÷Ýε‘ü{ž@zhnžgÿPKàˆBL…Oø Þ/utf8branchrepo/.git/hooks/applypatch-msg.sampleUT £ŒtZ-Æcbux ééU�]N1 „ßs “®*¤¯HHp.�n½›¨ùSì¥-ˆ»ã´l[^ÇãoÆ^Ü­7>­É©…ZÀ{<ØX‚ËyÔW_8Cï°ß;„>ÇèB!"‘Øî0Áæ([J8˽ƒ¡æVˆ&Zfóêôáæ—§°•TAî=;H9™/¬ˆ-Ov`¬à‰&ŸFÁ�3j.Õ[ÆK?@#ØÄÔêçrÓv×DO BÞã¶Yq+‹7‡ÍÄÁ‡¹¬˜’ÝÈSØy:Qž Šÿ”fm(}½ÞDõJ©Œž 9CÈSQç ™½êî^&‚ù4ÅVB0¦9eÙ�h}ö6ЃVŒÄ` » BÃr)�Ãþ¿Ø}??êîMÿ¨õ PKàˆBL’Äø–I ,utf8branchrepo/.git/hooks/pre-receive.sampleUT £ŒtZ-Æcbux ééuPËNÃ0<Ç_1¸}@[Ê‘*HˆôÒV"½!Unº‘M;ŠÝBü;vÊ£qÙÕzgÆ3Û:®”ZÉZ¬…; :ˆ²*Ò˜ lV«ÊÁ”bCØZ‚ÉQm­„©œ2Ú<+•ôM³Ê·WP& Yˆ¢ø…†“ÂÁ:Q;ì•“hàJd›¤í…„^£¦ÊÜÕ«ì%iÏ$ðã’ŸjBÙàk=h¤¤ÅÊqÒ/BˆK/©Eùù’«°3àUMýš2R;âÆTGÖ¡¯Áã‡Iºœ/ž—³y:™M—÷³Å4åÌ[Ð,RÉ‹ö²Ñ +Ž~áþçEkâˆv¢÷eKÉó¨ñ˜Lø ó¸q(h_êuýÔŒàMÍkS6g9‰Ò‰o¿5­^òÎq{~˜ã±¯Ç•Êa~°"óM%q§£p�Q·,kb¹bPKàˆBL½¾—×3utf8branchrepo/.git/hooks/prepare-commit-msg.sampleUT £ŒtZ-Æcbux ééuS]sÓ0|&¿â�3M?,›Â[;íL) ô…ÎÐð–i«Øg[T‘\I¦”É�ç$9! “å”ÝÛÛ=e¯Ë¥Ô¥ë&Ù$ƒ øS¬z…Ðó®²²÷à ô{a|‡P™ÕJzP¦…:'Z,{)”–ÏÀZºMbð$}QZ¬LÏ�T�Jxè„ Â�¬#c�QÊ<%Æ€©1©‘Foh„°#hæÀ™ÁVXÌ»4ÕúÁöÆ!H&Ášºü Uò*‘G!•K3ÐFó_h 8/üàr‚îØAìbi¬Çºˆ^Ω•Ë8(ݺ,Fb%™`€�ÞòDÄW®eÅ$0lp u¥2€€q“‘Çl¤uÉpÔÞ�üh)»4ºQ²òðÁ8AóÚv#{ÔJ,+£ëÝV¨ú!¢b¦µlà<ŒÀ“À-#¼Ô4GlÓû>�¤%6fÜ™ˆO6Ò«ƒmcö¤=„�•BVÔÍ Ó³0X�)Ω3i46ýv�ƒpÖÛíqÆ!‹m½ÝfQ×ÜÈVcÍMÓpÚ*%u à…𸕕ÐDã¼TŠÅ•¡X!%B_Kq©g"l�!ËjX%H7›¾Í§ï92�dt~@'€rp6>µ­.‹¥xNf®¼-3(s CY™• (o·á•P@åSz…äôŒš38= ;’¯=҄‡>Ùû�ƒ³xMŸHl¡p¿“k%*2ó¯€ï'Ù«¤ˆ”ííÁ4îÛÑœ�Á›1‡á€NTAÚÍõû³é~àÿ!,|ºšß]|›¾þzwõáã—9¬i㨙Ž,ö‹ÃóÅAq8-_dt‹ã²Ÿ…ÑZz%À°Û)Q³Ôx½¬:zD©v~Ë“ßPKàˆBLš ÷Àн,utf8branchrepo/.git/hooks/post-update.sampleUT £ŒtZ-Æcbux éé-ÍQ‚0 Ðÿžb…_�3x/ •Òt’ààí­èçîμm/Øò`KhC‹[´•'cYa“¦âpAQ.¤ B¡iå5‹%}#Šb7†¼X+2ïÛWÊVDÝúS¾ 8ÓXa_’�úµ™¶Ów4•õn/397}|ð„GrüªÎXëM—r”ðPKàˆBLÏÀL ¨/utf8branchrepo/.git/hooks/pre-applypatch.sampleUT £ŒtZ-Æcbux ééU�[N1 Eÿ³ŠK:ª@�VüVB‚=°�ÌÔÓDL&QìéÄÞñôâ÷Ú>ÇöânÝÆqÍÁ,Ìo#èèS!çpWcHÆžjìO8/ˆ ßæéœ·„.§Eh«€ö_Êp*^º€¾æ¯L—|�ˆÙïhuV½‡›#äiت7 QÆ<ºOª,^&uõBU¥<Åq§8VGÍ¥F/t£"ö˜ ~žcÉnë]¥4úVÏ“ WÌþ'T�Ò5éã\˰¥’û»Ä®ŒYaÅqpL2£ ò‹mîµ¢˜½+¾2Á¹¹SÃÙÀëvi~°FˆîÛü",–K}uÿÃæëùÑ6¯öÛlÌPKàˆBLØ�±�D)utf8branchrepo/.git/hooks/pre-push.sampleUT £ŒtZ-Æcbux éé�TakÛ0ýýŠ›Ö š´)ûT–Â(ŒFceƶÊö9•%O’ë¶¿~w’�¤c°BeéîÝ{ïNš¿8)µ= Jˆ9¼³€²í ‚rîBåu!:¸G¯›G”Œ ÈÒõi¿Dèú °^\Jc°†òŠ­ŽÇ'È&¢AÉ•Âꎂ¢BðØºˆ¢Œ}8†’KlœG�ö1*m·)¥D´#X*sÕP6qÉ჎H°Î.ŸÐ»”¾3Π�9à*æø…A’Lú_eî †¹5Î7pj'½l‘$„ó”¶XÃr ×´®9ÔAn JW9Ÿ 1n‰ŒQ;‹œzÆ©7Ÿ?þ;–¢I'ŸdúQ±á}à/’IÕë]]åYæ·}‹6Y‘µâ¯^š,õÊ’­­ŒÚÙ©uT·rm›¼KL$ùžd“˜Qè»ÎhZSŒ¶ˆ8Áq2lké)Ìv„§íh›o³Mo�#W‰es1­ƒ’kú‰ç“i øhß—�§0(7p“v¬óxO ³_äýž?zLå�ÛB‹!Èm"èc ¼ÔÓâëÕ§^ Îß1×λ­§À×+!2�M±X¢÷†g…OoN7§ÿù'YhX�¬!)ýIÚÆ ;•6Ç%íŠÚ‰™nà‹]hXPmø.f¤ÈŠÙlÈj‚¯ÑÐÒι˜¡ ¼Ó÷ ‡ù!\㥗¶RÇé†S/�&~²�ƒètË.ì©ÐîX†nºZæ!§ "ÏÉŸ€–ŠüpOnµzŽÞh‘ô]ò£À³Ô¥‚òbsK� Ùv¿4T–øR÷:8úAñG¬ŸK·“!S,rvñ̬”ƒ‹—gP¼w½­ÊñP,v­;æ‡cº}EʤWÖ™3ÿÒ-i÷TüPKàˆBL!D%u'utf8branchrepo/.git/hooks/update.sampleUT £ŒtZ-Æcbux éé­WQo"7~fÅÜ‚Â�`IÒ·äHuMÔê*Umî©:Efñ²n›®M¸½¶ÿ½Ÿí]Ø]8H” H1¶gæ›™ofL÷Ýd&äD§A7èÒGIü+[®2N©R�¤ã\¬ E³LÅ�´–LJe˜ás2l¡)ÉÕ’¸4<rAÃ-Ë2Î ÂPÎc.žøxÅâÇ�6¤ÄòÅz }…ÓD²%'�²‹±Êæ~!ùÆ�¹WÐÌfÀbR¡ Dœ„ÛI„=S®Ws@ #'v«d"XŒÝ +©#S›š8 º·zfJeœIÒÜhڤܤ<ßwu#²Œfœœl Ó¸êôä|¥´0*/"¢Ÿ šó„­3cÏ Ú(Ù7�šXæ<ã†C÷ $îÂû=¯±Ts‘§A0k�–¬°¶��€q– ó眡dT3êÔ�G}ŸåLÆésÝ÷·ù[†`ÎeáÐ?JΗÊð¬(=>„ EÉ-ë$˜Á_é…%¯'-¼\29§LH”52 {a€ÉùÖ—a€2ñëÂJð–pt)�‘П4þFaï—O÷wŸ~é˵E$ƒ�S”Í�‹G¾–KY쮤-ð¸# éæì²¤÷P^¨5m˜4#·ŠÕÚÖïzµBxJƒÎ˜ÕߦÞ9}€[7ôÁûƒ…wæfPÝü ê\‰jn”‘i¬ü†—Þ}÷JöÜ\k¶àWG¬î­òà:I»qL{ïmk‹Ý).Zª|§Ç ‚=¶Ÿ’®ßm‚‘n_mØFñ>Ï0.–‚ÛÎpJp{q`çÈG‰ÊI*pÝ“ Í!Xåê/»�u;æÔ¿ø»¿cè¤v?1Ó‡5ÁE„Ÿ¥åÁ¼VYá�þ¥"eʇÃ!ýæÅêüÄH™ö}€»ònf¿s}pÍâ-œ;¦Xq��±d™­Üs|¢(²ÿFèu}Ô²+á&¦�'¶@º(øÆs5 ÏŸù =íw”žbmUÔÈí�,´©·ðLóæA™:fÆÎõ±©ðÏËW•5ª :aìÎôÄNžÉpä�C”;]ÌÇqc@bS§*7UÃêýS®ºÝ�Žÿp­t­]+!½ƒ—&_óš“�ZBïÑ“ÚVGÔkXÙ¼à¼9°WãŠÏw]ógD¡oegº ›Æš¾ô�ÝØ­º�Í,¹b @ˆvºJötÕŽåP#n>=>n%-|Äê¡ØÖßÉ@ÜUÑåx|¥«` ±�×:Êm±7PÒÙùçÞžz9ÂYqŠnUý4‘kÌÇË›3ký@jáF¿éÃô±y�·¨ÐFG2ö«ƒñ‚œLU ‹mŽ—�‚mª�³«FP%ìµ;¯»p$·Õë�•^ŸÂ ý¦µ�Ø›2/áÛ[áõ/©v¼MŽŸ »gÕa�CÅ´'÷¾¶´¾Êé¡ÇûQ��JÛ©ñŠÒAŽ!QßÿÑÞ­5'÷ãÆ Ü+ô¿G©6Ò $R ù_>¶)!J»¢Ã‰»ÑlçÝ\kÒý,¤Ð)Þ®îÂyð?PK àˆBLutf8branchrepo/.git/refs/UT £ŒtZ-Æcbux ééPK !‰BLutf8branchrepo/.git/refs/heads/UT �tZ-Æcbux ééPK !‰BLn–D?))1utf8branchrepo/.git/refs/heads/branch-with—utf8UT �tZ-Æcbux éé82052aecedd419e87f28742a598154cd56b0fe79 PK ‰BLn–D?))%utf8branchrepo/.git/refs/heads/masterUT õŒtZ-Æcbux éé82052aecedd419e87f28742a598154cd56b0fe79 PK àˆBLutf8branchrepo/.git/refs/tags/UT £ŒtZ-Æcbux ééPK#‰BL°„0™„‰utf8branchrepo/.git/indexUT !�tZ-Æcbux éésñ rf```bƨ’ž÷ÆGk!´ÿ Fw¾) �K·æY*'ìªcx6÷ÑìM¯9{wk®+�ºqèIOðDÖÔÜ‚’J�4CH�«+�’d0T0àr¬a3‘Çbö©=Þ¦jŽ<�=“ËE PK ‰BLâÚ9Ó"utf8branchrepo/.git/COMMIT_EDITMSGUT õŒtZ-Æcbux ééInitial commit PK ‰BLíAutf8branchrepo/UTïŒtZux ééPK ‰BL¤�Iutf8branchrepo/emptyUTïŒtZux ééPK #‰BLíA—utf8branchrepo/.git/UT!�tZux ééPKäˆBL«~“�Þ¤�åutf8branchrepo/.git/configUT¬ŒtZux ééPK ‰BLíAÖutf8branchrepo/.git/objects/UTõŒtZux ééPK êˆBLíA,utf8branchrepo/.git/objects/4b/UT¸ŒtZux ééPK êˆBL«:à E$�…utf8branchrepo/.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904UT¸ŒtZux ééPK àˆBL!íAutf8branchrepo/.git/objects/pack/UT£ŒtZux ééPK àˆBL!íAnutf8branchrepo/.git/objects/info/UT£ŒtZux ééPK ‰BLíAÉutf8branchrepo/.git/objects/e6/UTòŒtZux ééPK ‰BL´ ¤˜E$�"utf8branchrepo/.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391UTòŒtZux ééPK ‰BLíA°utf8branchrepo/.git/objects/41/UTõŒtZux ééPK ‰BLཫ22E$� utf8branchrepo/.git/objects/41/7c01c8795a35b8e835113a85a5c0c1c77f67fbUTõŒtZux ééPK ‰BLíAºutf8branchrepo/.git/objects/82/UTõŒtZux ééPK ‰BL[nÉü––E$�utf8branchrepo/.git/objects/82/052aecedd419e87f28742a598154cd56b0fe79UTõŒtZux ééPK #‰BL+is§¤�(utf8branchrepo/.git/HEADUT!�tZux ééPK àˆBLíA‘utf8branchrepo/.git/info/UT£ŒtZux ééPKàˆBLw=Í!­ð ¤�äutf8branchrepo/.git/info/excludeUT£ŒtZux ééPK ‰BLíAëutf8branchrepo/.git/logs/UTõŒtZux ééPK#‰BL´"9Ím¤�> utf8branchrepo/.git/logs/HEADUT!�tZux ééPK ‰BLíAb utf8branchrepo/.git/logs/refs/UTõŒtZux ééPK !‰BL$íAº utf8branchrepo/.git/logs/refs/heads/UT�tZux ééPK!‰BL�2ë�‡¼6¤� utf8branchrepo/.git/logs/refs/heads/branch-with—utf8UT�tZux ééPK ‰BL8¾ÀˆÃ*¤� utf8branchrepo/.git/logs/refs/heads/masterUTõŒtZux ééPKàˆBL7‹?I¤�û utf8branchrepo/.git/descriptionUT£ŒtZux ééPK àˆBLíA“ utf8branchrepo/.git/hooks/UT£ŒtZux ééPKàˆBLéøÊ÷€+í�ç utf8branchrepo/.git/hooks/commit-msg.sampleUT£ŒtZux ééPKàˆBL,ß.—ÙW+í�Cutf8branchrepo/.git/hooks/pre-rebase.sampleUT£ŒtZux ééPKàˆBL%0¨\£j+í��utf8branchrepo/.git/hooks/pre-commit.sampleUT£ŒtZux ééPKàˆBL…Oø Þ/í�‰utf8branchrepo/.git/hooks/applypatch-msg.sampleUT£ŒtZux ééPKàˆBL’Äø–I ,¤� utf8branchrepo/.git/hooks/pre-receive.sampleUT£ŒtZux ééPKàˆBL½¾—×3í�¸utf8branchrepo/.git/hooks/prepare-commit-msg.sampleUT£ŒtZux ééPKàˆBLš ÷Àн,í�¼"utf8branchrepo/.git/hooks/post-update.sampleUT£ŒtZux ééPKàˆBLÏÀL ¨/í�¬#utf8branchrepo/.git/hooks/pre-applypatch.sampleUT£ŒtZux ééPKàˆBLØ�±�D)í�%utf8branchrepo/.git/hooks/pre-push.sampleUT£ŒtZux ééPKàˆBL!D%u'í�(utf8branchrepo/.git/hooks/update.sampleUT£ŒtZux ééPK àˆBLíAô,utf8branchrepo/.git/refs/UT£ŒtZux ééPK !‰BLíAG-utf8branchrepo/.git/refs/heads/UT�tZux ééPK !‰BLn–D?))1¤� -utf8branchrepo/.git/refs/heads/branch-with—utf8UT�tZux ééPK ‰BLn–D?))%¤�4.utf8branchrepo/.git/refs/heads/masterUTõŒtZux ééPK àˆBLíA¼.utf8branchrepo/.git/refs/tags/UT£ŒtZux ééPK#‰BL°„0™„‰¤�/utf8branchrepo/.git/indexUT!�tZux ééPK ‰BLâÚ9Ó"¤�ë/utf8branchrepo/.git/COMMIT_EDITMSGUTõŒtZux ééPK++V0libgit2-pygit2-d86662e/test/keys/000077500000000000000000000000001521317216500165655ustar00rootroot00000000000000libgit2-pygit2-d86662e/test/keys/pygit2_empty000066400000000000000000000051371521317216500211520ustar00rootroot00000000000000-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCcT0eRuC NRvnDkorGSaQqaAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC5bu0UuVdG eimAo2mu87uy/hWCbvie6N8H4a7ZjnSThpDIv0clcnbycCukDb/d8o9FRIPNNp2astBlWp nZ9vCHByP8z8ITO/pG5Mu0Wp0n1Hrja5UVdk4cBvvoEcr2sdNqj3wUUhqeAnNCYeeJuW9V j2JNV2wwJYPAZBDi6GJTM9FPlCJz+QA6dJLkUiIwngrL8bSPzXd+1mzHOrIcqjxKm8J+ed 3tVSlRq7YvefrACUOTI9ymXwYr8a/TiwA76nTHvNPYIhmzH8PEF65QsFTs/qk0nqSwmqKA vineapBMmrb3Vf/Hd/c0nEMgHt6gy/+dtlgPh1bTqaQaS/oeo5OtjMDK/w93SR9M3UZBX+ eeMUGfyhvYT4Nvo3TumKzaZ6peGu1T0MKprSwgRdI+v1q+58sKfzSK5QevTpKiX1+4Leo6 BiNKKu6doD19fgNSD/dwfOWehxFtD8q/1J5k0QPgqslFkqyZJXRCzzowRYunSN+AaHVD3W o4AuqtfTiazPMAAAWQVrRkwWjO1Fcw7zebagqfBufB05nc08wL911ZPCVwqVSIepcEK/hM CJ/5/N+UILn9BXGe9qmOHPUuMa9UaLBSyzmlJ1s/NMGLzYWiv62SX1QNEXPegxwLasQvbL njjzdESGX+qUHxT4okNH52zi4DcBLX4HPL/TYQsKTNxCOclOljPDo+3IfHzx76yG5dAl5L C7ghLsd1zxpwZI+ag7NhNzZ4hBxX9JUenAfGyuOL+YCTp8JnU+dXJ3XaA3WAVGnvsZlAaq GJUGCdLlMiacO0eXNTm53xc92X9tPmetEVwhuD/Af7Vc4dOmH9Zu+7n9z9bLPrOowNr7ue w8YCqCg83iuQYmSSPj/JTvCzaoGDfW+yjALlb5RJUAIMJ51k0WyVIyqS0TE8+EINKETlj7 iIx1Y5z54ZnldlqrD2vLImO2b401oOb7fJUEU9Ke5NPi93tsps8nYKhatcRYLnLq9gsFv9 YlDCueoJJobg1k9TO+IwxraPgz3jl24zskSKT/tLFvsz0fQM5QWha2vB8kyZI067ojuNLb /mj5itgLIDmISa9cf98HhafeE8kGAtKEJR2rLwvb79EAhZ2ypt2I8LVur5hCM4cC9vSVyS dq/f4sgQpyQqSByMXeLEFYJSCDDc/PL3RC0Q9PqrQYZ1+pqj/6esV3ujLogMAHqEuW4EVw tMDUvjzfnC8MVUQpc5H4yonsWjGeGhH+VEkBSVISpABTSrYFN5kBodPD16wmRTbFF4tTQq Egmj5vUmxSY+a2EjDJREQBerMhj3W5sPhAB1QGVVn5kyFvmsjM4t06zzZj/R5muIcX0cT+ Th3N+xeYIuVi9kS5v7yOBlMk0KGq8QATSL/u+niO0e0neoT5Jv6E7EIafAFrn3Ji0rNave ObCqse3yZct0pbspM4f0c9mHaVbzmvwwtjmUFGdMJgse0UARXqvOlF9PUaN/AhqQlIyVjj ednPLrOz617XDSixiP+tKzKmqjZsBASZzpGwpHKii9/k7Q7aG5/Int8ulBS3H8C6ipMSxW EKSMJ4g6k33RY1EFL3dWtJYWhReAhY6kvIc3lmSeo7I9SQWXLupx0NUnkXeO63hLmJ9tjD CXeI0cwb2a6DWKLh6c2gQ5LPNb/8xzvYJfdop2Oxr+9L2NP7pIgvYr/cmgPtF5WkLT2Ypk z+KgwWxUKRiK/3G+dVe27u0Id7Yi596wnNGxJfZmlnbfioY4i+K9KcyS08LxlmgsIsQHiY Scv6SuamPdjiHdSwK/GuAcQUVwXQRA7DoV2uxOosAaUXWMiiSjJ3n1L8IVgp17OKxVN0Bd 5phre4VhYFoXGnq43xFAY3XQJctBqLPdb47RNi3JlhVK+Q1WKzK9OWbDoiseoNnMD5NXOt Wqf/vxD6AJEyO8sOT55l6hZAkNHIfFUGx4MNmLl12hJYSZgY9tx7aizz8RMT6GMBammQcU Q0pNDF1RBFOtxgb/QE+9/Vym4dMGnJrhhdbcYZbKngcsho4Qs39qMQvv0V23zAExreQH8U TBTZYyYkiPqdUiB2fNCW89QWksvBe3CXZAC0T0tdBcEYe5UPJRQ/K2FS6bJTYmxDkDWzHD 9iHbiu3Z8JGB9kHT6B5AgM+fYgEhpCgieDEHdF85cXtGSt8rjFFW6SMS70aLkgzFpYVeD0 zgzumI6JRY3vSMpUY60NCz+FOmIxy7oIpv7nDf/6Ubvah/heUF/P6IQwOQXumVMK9/Khqx j5TxaRCZ7fXV7IXH1hjQgWSZkGNUHc+rEAZdPOYFXlQb/2+DkO8IE7SxSWwk8tDzS0L7+H hWXgIm7mjIB6HDNfRb2zPL7gOgm83qZfrhSdP76XqnuV1LvvZMIs2dC8lKFfLk6oayzUvQ z5AMR0EutUSCby7+DKyBmaYSq0s= -----END OPENSSH PRIVATE KEY----- libgit2-pygit2-d86662e/test/keys/pygit2_empty.pub000066400000000000000000000010661521317216500217340ustar00rootroot00000000000000ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5bu0UuVdGeimAo2mu87uy/hWCbvie6N8H4a7ZjnSThpDIv0clcnbycCukDb/d8o9FRIPNNp2astBlWpnZ9vCHByP8z8ITO/pG5Mu0Wp0n1Hrja5UVdk4cBvvoEcr2sdNqj3wUUhqeAnNCYeeJuW9Vj2JNV2wwJYPAZBDi6GJTM9FPlCJz+QA6dJLkUiIwngrL8bSPzXd+1mzHOrIcqjxKm8J+ed3tVSlRq7YvefrACUOTI9ymXwYr8a/TiwA76nTHvNPYIhmzH8PEF65QsFTs/qk0nqSwmqKAvineapBMmrb3Vf/Hd/c0nEMgHt6gy/+dtlgPh1bTqaQaS/oeo5OtjMDK/w93SR9M3UZBX+eeMUGfyhvYT4Nvo3TumKzaZ6peGu1T0MKprSwgRdI+v1q+58sKfzSK5QevTpKiX1+4Leo6BiNKKu6doD19fgNSD/dwfOWehxFtD8q/1J5k0QPgqslFkqyZJXRCzzowRYunSN+AaHVD3Wo4AuqtfTiazPM= pygit2_empty libgit2-pygit2-d86662e/test/test_apply_diff.py000066400000000000000000000140141521317216500213400ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import os from pathlib import Path import pytest import pygit2 from pygit2 import Diff, Repository from pygit2.enums import ApplyLocation, CheckoutStrategy, FileStatus def read_content(testrepo: Repository) -> str: with (Path(testrepo.workdir) / 'hello.txt').open('rb') as f: return f.read().decode('utf-8') @pytest.fixture def new_content() -> str: content_list = ['bye world', 'adiós', 'au revoir monde'] content = ''.join(x + os.linesep for x in content_list) return content @pytest.fixture def old_content(testrepo: Repository) -> str: with (Path(testrepo.workdir) / 'hello.txt').open('rb') as f: return f.read().decode('utf-8') @pytest.fixture def patch_diff(testrepo: Repository, new_content: str) -> Diff: # Create the patch with (Path(testrepo.workdir) / 'hello.txt').open('wb') as f: f.write(new_content.encode('utf-8')) patch = testrepo.diff().patch assert patch is not None # Rollback all changes testrepo.checkout('HEAD', strategy=CheckoutStrategy.FORCE) # Return the diff return pygit2.Diff.parse_diff(patch) @pytest.fixture def foreign_patch_diff() -> Diff: patch_contents = """diff --git a/this_file_does_not_exist b/this_file_does_not_exist index 7f129fd..af431f2 100644 --- a/this_file_does_not_exist +++ b/this_file_does_not_exist @@ -1 +1 @@ -a contents 2 +a contents """ return pygit2.Diff.parse_diff(patch_contents) def test_apply_type_error(testrepo: Repository) -> None: # Check apply type error with pytest.raises(TypeError): testrepo.apply('HEAD') # type: ignore def test_apply_diff_to_workdir( testrepo: Repository, new_content: str, patch_diff: Diff ) -> None: # Apply the patch and compare testrepo.apply(patch_diff, ApplyLocation.WORKDIR) assert read_content(testrepo) == new_content assert testrepo.status_file('hello.txt') == FileStatus.WT_MODIFIED def test_apply_diff_to_index( testrepo: Repository, old_content: str, patch_diff: Diff ) -> None: # Apply the patch and compare testrepo.apply(patch_diff, ApplyLocation.INDEX) assert read_content(testrepo) == old_content assert testrepo.status_file('hello.txt') & FileStatus.INDEX_MODIFIED def test_apply_diff_to_both( testrepo: Repository, new_content: str, patch_diff: Diff ) -> None: # Apply the patch and compare testrepo.apply(patch_diff, ApplyLocation.BOTH) assert read_content(testrepo) == new_content assert testrepo.status_file('hello.txt') & FileStatus.INDEX_MODIFIED def test_diff_applies_to_workdir( testrepo: Repository, old_content: str, patch_diff: Diff ) -> None: # See if patch applies assert testrepo.applies(patch_diff, ApplyLocation.WORKDIR) # Ensure it was a dry run assert read_content(testrepo) == old_content # Apply patch for real, then ensure it can't be applied again testrepo.apply(patch_diff, ApplyLocation.WORKDIR) assert not testrepo.applies(patch_diff, ApplyLocation.WORKDIR) # It can still be applied to the index, though assert testrepo.applies(patch_diff, ApplyLocation.INDEX) def test_diff_applies_to_index( testrepo: Repository, old_content: str, patch_diff: Diff ) -> None: # See if patch applies assert testrepo.applies(patch_diff, ApplyLocation.INDEX) # Ensure it was a dry run assert read_content(testrepo) == old_content # Apply patch for real, then ensure it can't be applied again testrepo.apply(patch_diff, ApplyLocation.INDEX) assert not testrepo.applies(patch_diff, ApplyLocation.INDEX) # It can still be applied to the workdir, though assert testrepo.applies(patch_diff, ApplyLocation.WORKDIR) def test_diff_applies_to_both( testrepo: Repository, old_content: str, patch_diff: Diff ) -> None: # See if patch applies assert testrepo.applies(patch_diff, ApplyLocation.BOTH) # Ensure it was a dry run assert read_content(testrepo) == old_content # Apply patch for real, then ensure it can't be applied again testrepo.apply(patch_diff, ApplyLocation.BOTH) assert not testrepo.applies(patch_diff, ApplyLocation.BOTH) assert not testrepo.applies(patch_diff, ApplyLocation.WORKDIR) assert not testrepo.applies(patch_diff, ApplyLocation.INDEX) def test_applies_error( testrepo: Repository, old_content: str, patch_diff: Diff, foreign_patch_diff: Diff ) -> None: # Try to apply a "foreign" patch that affects files that aren't in the repo; # ensure we get OSError about the missing file (due to raise_error) with pytest.raises(OSError): testrepo.applies(foreign_patch_diff, ApplyLocation.BOTH, raise_error=True) # Apply a valid patch testrepo.apply(patch_diff, ApplyLocation.BOTH) # Ensure it can't be applied again and we get an exception about it (due to raise_error) with pytest.raises(pygit2.GitError): testrepo.applies(patch_diff, ApplyLocation.BOTH, raise_error=True) libgit2-pygit2-d86662e/test/test_archive.py000066400000000000000000000047251521317216500206540ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import tarfile from pathlib import Path from pygit2 import Index, Object, Oid, Repository, Tree TREE_HASH = 'fd937514cb799514d4b81bb24c5fcfeb6472b245' COMMIT_HASH = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' def check_writing( repo: Repository, treeish: str | Tree | Oid | Object, timestamp: int | None = None ) -> None: archive = tarfile.open('foo.tar', mode='w') repo.write_archive(treeish, archive) index = Index() if isinstance(treeish, Object): index.read_tree(treeish.peel(Tree)) else: index.read_tree(repo[treeish].peel(Tree)) assert len(index) == len(archive.getmembers()) if timestamp: fileinfo = archive.getmembers()[0] assert timestamp == fileinfo.mtime archive.close() path = Path('foo.tar') assert path.is_file() path.unlink() def test_write_tree(testrepo: Repository) -> None: check_writing(testrepo, TREE_HASH) check_writing(testrepo, Oid(hex=TREE_HASH)) check_writing(testrepo, testrepo[TREE_HASH]) def test_write_commit(testrepo: Repository) -> None: commit_timestamp = testrepo[COMMIT_HASH].committer.time check_writing(testrepo, COMMIT_HASH, commit_timestamp) check_writing(testrepo, Oid(hex=COMMIT_HASH), commit_timestamp) check_writing(testrepo, testrepo[COMMIT_HASH], commit_timestamp) libgit2-pygit2-d86662e/test/test_attributes.py000066400000000000000000000037021521317216500214130ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # Standard Library from pathlib import Path from pygit2 import Repository def test_no_attr(testrepo: Repository) -> None: assert testrepo.get_attr('file', 'foo') is None with (Path(testrepo.workdir) / '.gitattributes').open('w+') as f: print('*.py text\n', file=f) print('*.jpg -text\n', file=f) print('*.sh eol=lf\n', file=f) assert testrepo.get_attr('file.py', 'foo') is None assert testrepo.get_attr('file.py', 'text') assert not testrepo.get_attr('file.jpg', 'text') assert 'lf' == testrepo.get_attr('file.sh', 'eol') def test_no_attr_aspath(testrepo: Repository) -> None: with (Path(testrepo.workdir) / '.gitattributes').open('w+') as f: print('*.py text\n', file=f) assert testrepo.get_attr(Path('file.py'), 'text') libgit2-pygit2-d86662e/test/test_blame.py000066400000000000000000000121621521317216500203050ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Blame objects.""" import pytest from pygit2 import Oid, Repository, Signature from pygit2.enums import BlameFlag PATH = 'hello.txt' HUNKS = [ ( Oid(hex='acecd5ea2924a4b900e7e149496e1f4b57976e51'), 1, Signature( 'J. David Ibañez', 'jdavid@itaapy.com', 1297179898, 60, encoding='utf-8' ), True, ), ( Oid(hex='6aaa262e655dd54252e5813c8e5acd7780ed097d'), 2, Signature( 'J. David Ibañez', 'jdavid@itaapy.com', 1297696877, 60, encoding='utf-8' ), False, ), ( Oid(hex='4ec4389a8068641da2d6578db0419484972284c8'), 3, Signature( 'J. David Ibañez', 'jdavid@itaapy.com', 1297696908, 60, encoding='utf-8' ), False, ), ] def test_blame_index(testrepo: Repository) -> None: blame = testrepo.blame(PATH) assert len(blame) == 3 for i, hunk in enumerate(blame): assert hunk.lines_in_hunk == 1 assert HUNKS[i][0] == hunk.final_commit_id assert HUNKS[i][1] == hunk.final_start_line_number assert HUNKS[i][2] == hunk.final_committer assert HUNKS[i][0] == hunk.orig_commit_id assert hunk.orig_path == PATH assert HUNKS[i][1] == hunk.orig_start_line_number assert HUNKS[i][2] == hunk.orig_committer assert HUNKS[i][3] == hunk.boundary def test_blame_flags(blameflagsrepo: Repository) -> None: blame = blameflagsrepo.blame(PATH, flags=BlameFlag.IGNORE_WHITESPACE) assert len(blame) == 3 for i, hunk in enumerate(blame): assert hunk.lines_in_hunk == 1 assert HUNKS[i][0] == hunk.final_commit_id assert HUNKS[i][1] == hunk.final_start_line_number assert HUNKS[i][2] == hunk.final_committer assert HUNKS[i][0] == hunk.orig_commit_id assert hunk.orig_path == PATH assert HUNKS[i][1] == hunk.orig_start_line_number assert HUNKS[i][2] == hunk.orig_committer assert HUNKS[i][3] == hunk.boundary def test_blame_with_invalid_index(testrepo: Repository) -> None: blame = testrepo.blame(PATH) with pytest.raises(IndexError): blame[100000] with pytest.raises(OverflowError): blame[-1] def test_blame_for_line(testrepo: Repository) -> None: blame = testrepo.blame(PATH) for i, line in zip(range(0, 2), range(1, 3)): hunk = blame.for_line(line) assert hunk.lines_in_hunk == 1 assert HUNKS[i][0] == hunk.final_commit_id assert HUNKS[i][1] == hunk.final_start_line_number assert HUNKS[i][2] == hunk.final_committer assert HUNKS[i][0] == hunk.orig_commit_id assert hunk.orig_path == PATH assert HUNKS[i][1] == hunk.orig_start_line_number assert HUNKS[i][2] == hunk.orig_committer assert HUNKS[i][3] == hunk.boundary def test_blame_with_invalid_line(testrepo: Repository) -> None: blame = testrepo.blame(PATH) with pytest.raises(IndexError): blame.for_line(0) with pytest.raises(IndexError): blame.for_line(100000) with pytest.raises(IndexError): blame.for_line(-1) def test_blame_newest(testrepo: Repository) -> None: revs = [ ('master^2', 3), ('master^2^', 2), ('master^2^^', 1), ] for rev, num_commits in revs: commit = testrepo.revparse_single(rev) blame = testrepo.blame(PATH, newest_commit=commit.id) assert len(blame) == num_commits for i, hunk in enumerate(tuple(blame)[:num_commits]): assert hunk.lines_in_hunk == 1 assert HUNKS[i][0] == hunk.final_commit_id assert HUNKS[i][1] == hunk.final_start_line_number assert HUNKS[i][2] == hunk.final_committer assert HUNKS[i][0] == hunk.orig_commit_id assert hunk.orig_path == PATH assert HUNKS[i][1] == hunk.orig_start_line_number assert HUNKS[i][2] == hunk.orig_committer assert HUNKS[i][3] == hunk.boundary libgit2-pygit2-d86662e/test/test_blob.py000066400000000000000000000175171521317216500201540ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Blob objects.""" import io from pathlib import Path from queue import Queue from threading import Event import pytest import pygit2 from pygit2 import Repository from pygit2.enums import ObjectType from . import utils BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_CONTENT = """hello world hola mundo bonjour le monde """.encode() BLOB_NEW_CONTENT = b'foo bar\n' BLOB_FILE_CONTENT = b'bye world\n' BLOB_PATCH = r"""diff --git a/file b/file index a520c24..95d09f2 100644 --- a/file +++ b/file @@ -1,3 +1 @@ -hello world -hola mundo -bonjour le monde +hello world \ No newline at end of file """ BLOB_PATCH_2 = """diff --git a/file b/file index a520c24..d675fa4 100644 --- a/file +++ b/file @@ -1,3 +1 @@ -hello world -hola mundo -bonjour le monde +foo bar """ BLOB_PATCH_DELETED = """diff --git a/file b/file deleted file mode 100644 index a520c24..0000000 --- a/file +++ /dev/null @@ -1,3 +0,0 @@ -hello world -hola mundo -bonjour le monde """ def test_read_blob(testrepo: Repository) -> None: blob = testrepo[BLOB_SHA] assert blob.id == BLOB_SHA assert blob.id == BLOB_SHA assert isinstance(blob, pygit2.Blob) assert not blob.is_binary assert ObjectType.BLOB == blob.type assert BLOB_CONTENT == blob.data assert len(BLOB_CONTENT) == blob.size assert BLOB_CONTENT == blob.read_raw() def test_create_blob(testrepo: Repository) -> None: blob_oid = testrepo.create_blob(BLOB_NEW_CONTENT) blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) assert ObjectType.BLOB == blob.type assert blob_oid == blob.id assert utils.gen_blob_sha1(BLOB_NEW_CONTENT) == blob_oid assert BLOB_NEW_CONTENT == blob.data assert len(BLOB_NEW_CONTENT) == blob.size assert BLOB_NEW_CONTENT == blob.read_raw() blob_buffer = memoryview(blob) assert len(BLOB_NEW_CONTENT) == len(blob_buffer) assert BLOB_NEW_CONTENT == blob_buffer def set_content() -> None: blob_buffer[:2] = b'hi' with pytest.raises(TypeError): set_content() def test_create_blob_fromworkdir(testrepo: Repository) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) assert ObjectType.BLOB == blob.type assert blob_oid == blob.id assert utils.gen_blob_sha1(BLOB_FILE_CONTENT) == blob_oid assert BLOB_FILE_CONTENT == blob.data assert len(BLOB_FILE_CONTENT) == blob.size assert BLOB_FILE_CONTENT == blob.read_raw() def test_create_blob_fromworkdir_aspath(testrepo: Repository) -> None: blob_oid = testrepo.create_blob_fromworkdir(Path('bye.txt')) blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) def test_create_blob_outside_workdir(testrepo: Repository) -> None: with pytest.raises(KeyError): testrepo.create_blob_fromworkdir(__file__) def test_create_blob_fromdisk(testrepo: Repository) -> None: blob_oid = testrepo.create_blob_fromdisk(__file__) blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) assert ObjectType.BLOB == blob.type def test_create_blob_fromiobase(testrepo: Repository) -> None: with pytest.raises(TypeError): testrepo.create_blob_fromiobase('bad type') # type: ignore f = io.BytesIO(BLOB_CONTENT) blob_oid = testrepo.create_blob_fromiobase(f) blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) assert ObjectType.BLOB == blob.type assert blob_oid == blob.id assert BLOB_SHA == blob_oid def test_diff_blob(testrepo: Repository) -> None: blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) old_blob = testrepo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad'] assert isinstance(old_blob, pygit2.Blob) patch = blob.diff(old_blob, old_as_path='hello.txt') assert len(patch.hunks) == 1 def test_diff_blob_to_buffer(testrepo: Repository) -> None: blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) patch = blob.diff_to_buffer('hello world') assert len(patch.hunks) == 1 def test_diff_blob_to_buffer_patch_patch(testrepo: Repository) -> None: blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) patch = blob.diff_to_buffer('hello world') assert patch.text == BLOB_PATCH def test_diff_blob_to_buffer_delete(testrepo: Repository) -> None: blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) patch = blob.diff_to_buffer(None) assert patch.text == BLOB_PATCH_DELETED def test_diff_blob_create(testrepo: Repository) -> None: old = testrepo[testrepo.create_blob(BLOB_CONTENT)] new = testrepo[testrepo.create_blob(BLOB_NEW_CONTENT)] assert isinstance(old, pygit2.Blob) assert isinstance(new, pygit2.Blob) patch = old.diff(new) assert patch.text == BLOB_PATCH_2 def test_blob_from_repo(testrepo: Repository) -> None: blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) patch_one = blob.diff_to_buffer(None) blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) patch_two = blob.diff_to_buffer(None) assert patch_one.text == patch_two.text def test_blob_write_to_queue(testrepo: Repository) -> None: queue: Queue[bytes] = Queue() ready = Event() done = Event() blob = testrepo[BLOB_SHA] assert isinstance(blob, pygit2.Blob) blob._write_to_queue(queue, ready, done) assert ready.wait() assert done.wait() chunks = [] while not queue.empty(): chunks.append(queue.get()) assert BLOB_CONTENT == b''.join(chunks) def test_blob_write_to_queue_filtered(testrepo: Repository) -> None: queue: Queue[bytes] = Queue() ready = Event() done = Event() blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) blob._write_to_queue(queue, ready, done, as_path='bye.txt') assert ready.wait() assert done.wait() chunks = [] while not queue.empty(): chunks.append(queue.get()) assert b'bye world\n' == b''.join(chunks) def test_blobio(testrepo: Repository) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) with pygit2.BlobIO(blob) as reader: assert b'bye world\n' == reader.read() assert not reader.raw._thread.is_alive() # type: ignore[attr-defined] def test_blobio_filtered(testrepo: Repository) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) with pygit2.BlobIO(blob, as_path='bye.txt') as reader: assert b'bye world\n' == reader.read() assert not reader.raw._thread.is_alive() # type: ignore[attr-defined] libgit2-pygit2-d86662e/test/test_branch.py000066400000000000000000000220511521317216500204600ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for branch methods.""" import os import pytest import pygit2 from pygit2 import Commit, Repository from pygit2.enums import BranchType LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' I18N_LAST_COMMIT = '5470a671a80ac3789f1a6a8cefbcf43ce7af0563' ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78' EXCLUSIVE_MASTER_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' SHARED_COMMIT = '4ec4389a8068641da2d6578db0419484972284c8' def test_branches_getitem(testrepo: Repository) -> None: branch = testrepo.branches['master'] assert branch.target == LAST_COMMIT branch = testrepo.branches.local['i18n'] assert branch.target == I18N_LAST_COMMIT assert testrepo.branches.get('not-exists') is None with pytest.raises(KeyError): testrepo.branches['not-exists'] def test_branches(testrepo: Repository) -> None: branches = sorted(testrepo.branches) assert branches == ['i18n', 'master'] def test_branches_create(testrepo: Repository) -> None: commit = testrepo[LAST_COMMIT] assert isinstance(commit, Commit) reference = testrepo.branches.create('version1', commit) assert 'version1' in testrepo.branches reference = testrepo.branches['version1'] assert reference.target == LAST_COMMIT # try to create existing reference with pytest.raises(ValueError): testrepo.branches.create('version1', commit) # try to create existing reference with force reference = testrepo.branches.create('version1', commit, True) assert reference.target == LAST_COMMIT def test_branches_delete(testrepo: Repository) -> None: testrepo.branches.delete('i18n') assert testrepo.branches.get('i18n') is None def test_branches_delete_error(testrepo: Repository) -> None: with pytest.raises(pygit2.GitError): testrepo.branches.delete('master') def test_branches_is_head(testrepo: Repository) -> None: branch = testrepo.branches.get('master') assert branch.is_head() def test_branches_is_not_head(testrepo: Repository) -> None: branch = testrepo.branches.get('i18n') assert not branch.is_head() def test_branches_rename(testrepo: Repository) -> None: new_branch = testrepo.branches['i18n'].rename('new-branch') assert new_branch.target == I18N_LAST_COMMIT new_branch_2 = testrepo.branches.get('new-branch') assert new_branch_2.target == I18N_LAST_COMMIT def test_branches_rename_error(testrepo: Repository) -> None: original_branch = testrepo.branches.get('i18n') with pytest.raises(ValueError): original_branch.rename('master') def test_branches_rename_force(testrepo: Repository) -> None: original_branch = testrepo.branches.get('master') new_branch = original_branch.rename('i18n', True) assert new_branch.target == LAST_COMMIT def test_branches_rename_invalid(testrepo: Repository) -> None: original_branch = testrepo.branches.get('i18n') with pytest.raises(ValueError): original_branch.rename('abc@{123') def test_branches_name(testrepo: Repository) -> None: branch = testrepo.branches.get('master') assert branch.branch_name == 'master' assert branch.name == 'refs/heads/master' assert branch.raw_branch_name == branch.branch_name.encode('utf-8') branch = testrepo.branches.get('i18n') assert branch.branch_name == 'i18n' assert branch.name == 'refs/heads/i18n' assert branch.raw_branch_name == branch.branch_name.encode('utf-8') def test_branches_with_commit(testrepo: Repository) -> None: branches = testrepo.branches.with_commit(EXCLUSIVE_MASTER_COMMIT) assert sorted(branches) == ['master'] assert branches.get('i18n') is None assert branches['master'].branch_name == 'master' branches = testrepo.branches.with_commit(SHARED_COMMIT) assert sorted(branches) == ['i18n', 'master'] branches = testrepo.branches.with_commit(LAST_COMMIT) assert sorted(branches) == ['master'] commit = testrepo[LAST_COMMIT] assert isinstance(commit, Commit) branches = testrepo.branches.with_commit(commit) assert sorted(branches) == ['master'] branches = testrepo.branches.remote.with_commit(LAST_COMMIT) assert sorted(branches) == [] # # Low level API written in C, repo.branches call these. # def test_lookup_branch_local(testrepo: Repository) -> None: assert testrepo.lookup_branch('master').target == LAST_COMMIT assert testrepo.lookup_branch(b'master').target == LAST_COMMIT assert testrepo.lookup_branch('i18n', BranchType.LOCAL).target == I18N_LAST_COMMIT assert testrepo.lookup_branch(b'i18n', BranchType.LOCAL).target == I18N_LAST_COMMIT assert testrepo.lookup_branch('not-exists') is None assert testrepo.lookup_branch(b'not-exists') is None if os.name == 'posix': # this call fails with an InvalidSpecError on NT assert testrepo.lookup_branch(b'\xb1') is None def test_listall_branches(testrepo: Repository) -> None: branches = sorted(testrepo.listall_branches()) assert branches == ['i18n', 'master'] branches_raw = sorted(testrepo.raw_listall_branches()) assert branches_raw == [b'i18n', b'master'] def test_create_branch(testrepo: Repository) -> None: commit = testrepo[LAST_COMMIT] assert isinstance(commit, Commit) testrepo.create_branch('version1', commit) refs = testrepo.listall_branches() assert 'version1' in refs assert testrepo.lookup_branch('version1').target == LAST_COMMIT assert testrepo.lookup_branch(b'version1').target == LAST_COMMIT # try to create existing reference with pytest.raises(ValueError): testrepo.create_branch('version1', commit) # try to create existing reference with force assert testrepo.create_branch('version1', commit, True).target == LAST_COMMIT def test_delete(testrepo: Repository) -> None: branch = testrepo.lookup_branch('i18n') branch.delete() assert testrepo.lookup_branch('i18n') is None def test_cant_delete_master(testrepo: Repository) -> None: branch = testrepo.lookup_branch('master') with pytest.raises(pygit2.GitError): branch.delete() def test_branch_is_head_returns_true_if_branch_is_head(testrepo: Repository) -> None: branch = testrepo.lookup_branch('master') assert branch.is_head() def test_branch_is_head_returns_false_if_branch_is_not_head( testrepo: Repository, ) -> None: branch = testrepo.lookup_branch('i18n') assert not branch.is_head() def test_branch_is_checked_out_returns_true_if_branch_is_checked_out( testrepo: Repository, ) -> None: branch = testrepo.lookup_branch('master') assert branch.is_checked_out() def test_branch_is_checked_out_returns_false_if_branch_is_not_checked_out( testrepo: Repository, ) -> None: branch = testrepo.lookup_branch('i18n') assert not branch.is_checked_out() def test_branch_rename_succeeds(testrepo: Repository) -> None: branch = testrepo.lookup_branch('i18n') assert branch.rename('new-branch').target == I18N_LAST_COMMIT assert testrepo.lookup_branch('new-branch').target == I18N_LAST_COMMIT def test_branch_rename_fails_if_destination_already_exists( testrepo: Repository, ) -> None: original_branch = testrepo.lookup_branch('i18n') with pytest.raises(ValueError): original_branch.rename('master') def test_branch_rename_not_fails_if_force_is_true(testrepo: Repository) -> None: branch = testrepo.lookup_branch('master') assert branch.rename('i18n', True).target == LAST_COMMIT def test_branch_rename_fails_with_invalid_names(testrepo: Repository) -> None: original_branch = testrepo.lookup_branch('i18n') with pytest.raises(ValueError): original_branch.rename('abc@{123') def test_branch_name(testrepo: Repository) -> None: branch = testrepo.lookup_branch('master') assert branch.branch_name == 'master' assert branch.name == 'refs/heads/master' branch = testrepo.lookup_branch('i18n') assert branch.branch_name == 'i18n' assert branch.name == 'refs/heads/i18n' libgit2-pygit2-d86662e/test/test_branch_empty.py000066400000000000000000000110771521317216500217040ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator import pytest from pygit2 import Commit, Repository from pygit2.enums import BranchType ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78' @pytest.fixture def repo(emptyrepo: Repository) -> Generator[Repository, None, None]: remote = emptyrepo.remotes[0] remote.fetch() yield emptyrepo def test_branches_remote_get(repo: Repository) -> None: branch = repo.branches.remote.get('origin/master') assert branch.target == ORIGIN_MASTER_COMMIT assert repo.branches.remote.get('origin/not-exists') is None def test_branches_remote(repo: Repository) -> None: branches = sorted(repo.branches.remote) assert branches == ['origin/master'] def test_branches_remote_getitem(repo: Repository) -> None: branch = repo.branches.remote['origin/master'] assert branch.remote_name == 'origin' def test_branches_upstream(repo: Repository) -> None: remote_master = repo.branches.remote['origin/master'] commit = repo[remote_master.target] assert isinstance(commit, Commit) master = repo.branches.create('master', commit) assert master.upstream is None master.upstream = remote_master assert master.upstream.branch_name == 'origin/master' def set_bad_upstream(): master.upstream = 2.5 with pytest.raises(TypeError): set_bad_upstream() master.upstream = None assert master.upstream is None def test_branches_upstream_name(repo: Repository) -> None: remote_master = repo.branches.remote['origin/master'] commit = repo[remote_master.target] assert isinstance(commit, Commit) master = repo.branches.create('master', commit) master.upstream = remote_master assert master.upstream_name == 'refs/remotes/origin/master' # # Low level API written in C, repo.remotes call these. # def test_lookup_branch_remote(repo: Repository) -> None: branch = repo.lookup_branch('origin/master', BranchType.REMOTE) assert branch.target == ORIGIN_MASTER_COMMIT assert repo.lookup_branch('origin/not-exists', BranchType.REMOTE) is None def test_listall_branches(repo: Repository) -> None: branches = sorted(repo.listall_branches(BranchType.REMOTE)) assert branches == ['origin/master'] branches_raw = sorted(repo.raw_listall_branches(BranchType.REMOTE)) assert branches_raw == [b'origin/master'] def test_branch_remote_name(repo: Repository) -> None: branch = repo.lookup_branch('origin/master', BranchType.REMOTE) assert branch.remote_name == 'origin' def test_branch_upstream(repo: Repository) -> None: remote_master = repo.lookup_branch('origin/master', BranchType.REMOTE) commit = repo[remote_master.target] assert isinstance(commit, Commit) master = repo.create_branch('master', commit) assert master.upstream is None master.upstream = remote_master assert master.upstream.branch_name == 'origin/master' def set_bad_upstream(): master.upstream = 2.5 with pytest.raises(TypeError): set_bad_upstream() master.upstream = None assert master.upstream is None def test_branch_upstream_name(repo: Repository) -> None: remote_master = repo.lookup_branch('origin/master', BranchType.REMOTE) commit = repo[remote_master.target] assert isinstance(commit, Commit) master = repo.create_branch('master', commit) master.upstream = remote_master assert master.upstream_name == 'refs/remotes/origin/master' libgit2-pygit2-d86662e/test/test_cherrypick.py000066400000000000000000000056161521317216500213760ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for merging and information about it.""" from pathlib import Path import pytest import pygit2 from pygit2 import Repository from pygit2.enums import RepositoryState def test_cherrypick_none(mergerepo: Repository) -> None: with pytest.raises(TypeError): mergerepo.cherrypick(None) # type: ignore def test_cherrypick_invalid_hex(mergerepo: Repository) -> None: branch_head_hex = '12345678' with pytest.raises(KeyError): mergerepo.cherrypick(branch_head_hex) def test_cherrypick_already_something_in_index(mergerepo: Repository) -> None: branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' branch_object = mergerepo.get(branch_head_hex) assert branch_object is not None branch_oid = branch_object.id with (Path(mergerepo.workdir) / 'inindex.txt').open('w') as f: f.write('new content') mergerepo.index.add('inindex.txt') with pytest.raises(pygit2.GitError): mergerepo.cherrypick(branch_oid) def test_cherrypick_remove_conflicts(mergerepo: Repository) -> None: assert mergerepo.state() == RepositoryState.NONE assert not mergerepo.message other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' mergerepo.cherrypick(other_branch_tip) assert mergerepo.state() == RepositoryState.CHERRYPICK assert mergerepo.message.startswith('commit to provoke a conflict') idx = mergerepo.index conflicts = idx.conflicts assert conflicts is not None conflicts['.gitignore'] del idx.conflicts['.gitignore'] with pytest.raises(KeyError): conflicts.__getitem__('.gitignore') assert idx.conflicts is None mergerepo.state_cleanup() assert mergerepo.state() == RepositoryState.NONE assert not mergerepo.message libgit2-pygit2-d86662e/test/test_commit.py000066400000000000000000000255621521317216500205250ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Commit objects.""" import sys import pytest from pygit2 import Commit, GitError, Oid, Repository, Signature, Tree from pygit2.enums import ObjectType from . import utils COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA_TO_AMEND = ( '784855caf26449a1914d2cf62d12b9374d76ae78' # tip of the master branch ) @utils.requires_refcount def test_commit_refcount(barerepo: Repository) -> None: commit = barerepo[COMMIT_SHA] start = sys.getrefcount(commit) tree = commit.tree del tree end = sys.getrefcount(commit) assert start == end def test_read_commit(barerepo: Repository) -> None: commit = barerepo[COMMIT_SHA] assert isinstance(commit, Commit) assert COMMIT_SHA == commit.id parents = commit.parents assert 1 == len(parents) assert 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' == parents[0].id assert commit.message_encoding is None assert commit.message == ( 'Second test data commit.\n\nThis commit has some additional text.\n' ) commit_time = 1288481576 assert commit_time == commit.commit_time assert commit.committer == Signature( 'Dave Borowitz', 'dborowitz@google.com', commit_time, -420 ) assert commit.author == Signature( 'Dave Borowitz', 'dborowitz@google.com', 1288477363, -420 ) assert '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' == commit.tree.id def test_new_commit(barerepo: Repository) -> None: repo = barerepo message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' committer = Signature('John Doe', 'jdoe@example.com', 12346, 0) author = Signature( 'J. David Ibáñez', 'jdavid@example.com', 12345, 0, encoding='utf-8' ) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' tree_prefix = tree[:5] too_short_prefix = tree[:3] parents = [COMMIT_SHA[:5]] with pytest.raises(ValueError): repo.create_commit(None, author, committer, message, too_short_prefix, parents) sha = repo.create_commit(None, author, committer, message, tree_prefix, parents) commit = repo[sha] assert isinstance(commit, Commit) assert ObjectType.COMMIT.value == commit.type assert '98286caaab3f1fde5bf52c8369b2b0423bad743b' == commit.id assert commit.message_encoding is None assert message == commit.message assert 12346 == commit.commit_time assert committer == commit.committer assert author == commit.author assert tree == commit.tree.id assert Oid(hex=tree) == commit.tree_id assert 1 == len(commit.parents) assert COMMIT_SHA == commit.parents[0].id assert Oid(hex=COMMIT_SHA) == commit.parent_ids[0] def test_new_commit_encoding(barerepo: Repository) -> None: repo = barerepo encoding = 'iso-8859-1' message = 'New commit.\n\nMessage with non-ascii chars: ééé.\n' committer = Signature('John Doe', 'jdoe@example.com', 12346, 0, encoding) author = Signature('J. David Ibáñez', 'jdavid@example.com', 12345, 0, encoding) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' tree_prefix = tree[:5] parents = [COMMIT_SHA[:5]] sha = repo.create_commit( None, author, committer, message, tree_prefix, parents, encoding ) commit = repo[sha] assert isinstance(commit, Commit) assert ObjectType.COMMIT.value == commit.type assert 'iso-8859-1' == commit.message_encoding assert message.encode(encoding) == commit.raw_message assert 12346 == commit.commit_time assert committer == commit.committer assert author == commit.author assert tree == commit.tree.id assert Oid(hex=tree) == commit.tree_id assert 1 == len(commit.parents) assert COMMIT_SHA == commit.parents[0].id assert Oid(hex=COMMIT_SHA) == commit.parent_ids[0] def test_modify_commit(barerepo: Repository) -> None: message = 'New commit.\n\nMessage.\n' committer = ('John Doe', 'jdoe@example.com', 12346) author = ('Jane Doe', 'jdoe2@example.com', 12345) commit = barerepo[COMMIT_SHA] with pytest.raises(AttributeError): setattr(commit, 'message', message) with pytest.raises(AttributeError): setattr(commit, 'committer', committer) with pytest.raises(AttributeError): setattr(commit, 'author', author) with pytest.raises(AttributeError): setattr(commit, 'tree', None) with pytest.raises(AttributeError): setattr(commit, 'parents', None) def test_amend_commit_metadata(barerepo: Repository) -> None: repo = barerepo commit = repo[COMMIT_SHA_TO_AMEND] assert isinstance(commit, Commit) assert commit.id == repo.head.target encoding = 'iso-8859-1' amended_message = 'Amended commit message.\n\nMessage with non-ascii chars: ééé.\n' amended_author = Signature( 'Jane Author', 'jane@example.com', 12345, 0, encoding=encoding ) amended_committer = Signature( 'John Committer', 'john@example.com', 12346, 0, encoding=encoding ) amended_oid = repo.amend_commit( commit, 'HEAD', message=amended_message, author=amended_author, committer=amended_committer, encoding=encoding, ) amended_commit = repo[amended_oid] assert isinstance(amended_commit, Commit) assert repo.head.target == amended_oid assert ObjectType.COMMIT.value == amended_commit.type assert amended_committer == amended_commit.committer assert amended_author == amended_commit.author assert amended_message.encode(encoding) == amended_commit.raw_message assert commit.author != amended_commit.author assert commit.committer != amended_commit.committer assert commit.tree == amended_commit.tree # we didn't touch the tree def test_amend_commit_tree(barerepo: Repository) -> None: repo = barerepo commit = repo[COMMIT_SHA_TO_AMEND] assert isinstance(commit, Commit) assert commit.id == repo.head.target tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' tree_prefix = tree[:5] amended_oid = repo.amend_commit(commit, 'HEAD', tree=tree_prefix) amended_commit = repo[amended_oid] assert isinstance(amended_commit, Commit) assert isinstance(commit, Commit) assert repo.head.target == amended_oid assert ObjectType.COMMIT.value == amended_commit.type assert commit.message == amended_commit.message assert commit.author == amended_commit.author assert commit.committer == amended_commit.committer assert commit.tree_id != amended_commit.tree_id assert Oid(hex=tree) == amended_commit.tree_id def test_amend_commit_not_tip_of_branch(barerepo: Repository) -> None: repo = barerepo # This commit isn't at the tip of the branch. commit = repo['5fe808e8953c12735680c257f56600cb0de44b10'] assert isinstance(commit, Commit) assert commit.id != repo.head.target # Can't update HEAD to the rewritten commit because it's not the tip of the branch. with pytest.raises(GitError): repo.amend_commit(commit, 'HEAD', message="this won't work!") # We can still amend the commit if we don't try to update a ref. repo.amend_commit(commit, None, message='this will work') def test_amend_commit_no_op(barerepo: Repository) -> None: repo = barerepo commit = repo[COMMIT_SHA_TO_AMEND] assert isinstance(commit, Commit) assert commit.id == repo.head.target amended_oid = repo.amend_commit(commit, None) assert amended_oid == commit.id def test_amend_commit_argument_types(barerepo: Repository) -> None: repo = barerepo some_tree = repo['967fce8df97cc71722d3c2a5930ef3e6f1d27b12'] commit = repo[COMMIT_SHA_TO_AMEND] alt_commit1 = Oid(hex=COMMIT_SHA_TO_AMEND) alt_commit2 = COMMIT_SHA_TO_AMEND alt_tree = some_tree assert isinstance(alt_tree, Tree) alt_refname = ( repo.head ) # try this one last, because it'll change the commit at the tip # Pass bad values/types for the commit with pytest.raises(ValueError): repo.amend_commit(None, None) # type: ignore with pytest.raises(TypeError): repo.amend_commit(some_tree, None) # type: ignore # Pass bad types for signatures with pytest.raises(TypeError): repo.amend_commit(commit, None, author='Toto') # type: ignore with pytest.raises(TypeError): repo.amend_commit(commit, None, committer='Toto') # type: ignore # Pass bad refnames with pytest.raises(ValueError): repo.amend_commit(commit, 'this-ref-doesnt-exist') # type: ignore with pytest.raises(TypeError): repo.amend_commit(commit, repo) # type: ignore # Pass bad trees with pytest.raises(ValueError): repo.amend_commit(commit, None, tree="can't parse this") # type: ignore with pytest.raises(KeyError): repo.amend_commit(commit, None, tree='baaaaad') # type: ignore # Pass an Oid for the commit amended_oid = repo.amend_commit(alt_commit1, None, message='Hello') amended_commit = repo[amended_oid] assert ObjectType.COMMIT == amended_commit.type assert amended_oid != COMMIT_SHA_TO_AMEND # Pass a str for the commit amended_oid = repo.amend_commit(alt_commit2, None, message='Hello', tree=alt_tree) amended_commit = repo[amended_oid] assert isinstance(amended_commit, Commit) assert ObjectType.COMMIT.value == amended_commit.type assert amended_oid != COMMIT_SHA_TO_AMEND assert repo[COMMIT_SHA_TO_AMEND].tree != amended_commit.tree assert alt_tree.id == amended_commit.tree_id # Pass an actual reference object for refname # (Warning: the tip of the branch will be altered after this test!) amended_oid = repo.amend_commit(alt_commit2, alt_refname, message='Hello') amended_commit = repo[amended_oid] assert ObjectType.COMMIT == amended_commit.type assert amended_oid != COMMIT_SHA_TO_AMEND assert repo.head.target == amended_oid libgit2-pygit2-d86662e/test/test_commit_gpg.py000066400000000000000000000127251521317216500213570ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from pygit2 import Commit, Oid, Repository, Signature from pygit2.enums import ObjectType content = """\ tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 parent 8496071c1b46c854b31185ea97743be6a8774479 author Ben Burkert 1358451456 -0800 committer Ben Burkert 1358451456 -0800 a simple commit which works\ """ gpgsig = """\ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin) iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8 JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9 ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc cpxtDQQMGYFpXK/71stq =ozeK -----END PGP SIGNATURE-----\ """ gpgsig_content = """\ tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 parent 8496071c1b46c854b31185ea97743be6a8774479 author Ben Burkert 1358451456 -0800 committer Ben Burkert 1358451456 -0800 gpgsig -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (Darwin) iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8 JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9 ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc cpxtDQQMGYFpXK/71stq =ozeK -----END PGP SIGNATURE----- a simple commit which works\ """ # NOTE: ^^^ mind the gap (space must exist after GnuPG header) ^^^ # XXX: seems macos wants the space while linux does not def test_commit_signing(gpgsigned: Repository) -> None: repo = gpgsigned message = 'a simple commit which works' author = Signature( name='Ben Burkert', email='ben@benburkert.com', time=1358451456, offset=-480, ) committer = Signature( name='Ben Burkert', email='ben@benburkert.com', time=1358451456, offset=-480, ) tree = '4b825dc642cb6eb9a060e54bf8d69288fbee4904' parent = '8496071c1b46c854b31185ea97743be6a8774479' # create commit string commit_string = repo.create_commit_string( author, committer, message, tree, [parent] ) assert commit_string == content # create/retrieve signed commit oid = repo.create_commit_with_signature(content, gpgsig) commit = repo.get(oid) assert isinstance(commit, Commit) signature, payload = commit.gpg_signature # validate signed commit assert content == payload.decode('utf-8') assert gpgsig == signature.decode('utf-8') assert gpgsig_content == commit.read_raw().decode('utf-8') # perform sanity checks assert ObjectType.COMMIT == commit.type assert '6569fdf71dbd99081891154641869c537784a3ba' == commit.id assert commit.message_encoding is None assert message == commit.message assert 1358451456 == commit.commit_time assert committer == commit.committer assert author == commit.author assert tree == commit.tree.id assert Oid(hex=tree) == commit.tree_id assert 1 == len(commit.parents) assert parent == commit.parents[0].id assert Oid(hex=parent) == commit.parent_ids[0] def test_get_gpg_signature_when_unsigned(gpgsigned: Repository) -> None: unhash = '5b5b025afb0b4c913b4c338a42934a3863bf3644' repo = gpgsigned commit = repo.get(unhash) assert isinstance(commit, Commit) signature, payload = commit.gpg_signature assert signature is None assert payload is None libgit2-pygit2-d86662e/test/test_commit_trailer.py000066400000000000000000000037421521317216500222430ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Commit, Repository from . import utils @pytest.fixture def repo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('trailerrepo.zip', tmp_path) as path: yield pygit2.Repository(path) def test_get_trailers_array(repo: Repository) -> None: commit_hash = '010231b2fdaee6b21da4f06058cf6c6a3392dd12' expected_trailers = { 'Bug': '1234', 'Signed-off-by': 'Tyler Cipriani ', } commit = repo.get(commit_hash) assert isinstance(commit, Commit) trailers = commit.message_trailers assert trailers['Bug'] == expected_trailers['Bug'] assert trailers['Signed-off-by'] == expected_trailers['Signed-off-by'] libgit2-pygit2-d86662e/test/test_config.py000066400000000000000000000156061521317216500205000ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator from pathlib import Path import pytest from pygit2 import Config, Repository from . import utils CONFIG_FILENAME = 'test_config' @pytest.fixture def config(testrepo: Repository) -> Generator[object, None, None]: yield testrepo.config try: Path(CONFIG_FILENAME).unlink() except OSError: pass def test_config(config: Config) -> None: assert config is not None def test_global_config() -> None: try: assert Config.get_global_config() is not None except IOError: # There is no user config pass def test_system_config() -> None: try: assert Config.get_system_config() is not None except IOError: # There is no system config pass def test_new() -> None: # Touch file open(CONFIG_FILENAME, 'w').close() config_write = Config(CONFIG_FILENAME) assert config_write is not None config_write['core.bare'] = False config_write['core.editor'] = 'ed' config_read = Config(CONFIG_FILENAME) assert 'core.bare' in config_read assert not config_read.get_bool('core.bare') assert 'core.editor' in config_read assert config_read['core.editor'] == 'ed' def test_add() -> None: with open(CONFIG_FILENAME, 'w') as new_file: new_file.write('[this]\n\tthat = true\n') new_file.write('[something "other"]\n\there = false') config = Config() config.add_file(CONFIG_FILENAME, 0) assert 'this.that' in config assert config.get_bool('this.that') assert 'something.other.here' in config assert not config.get_bool('something.other.here') def test_add_aspath() -> None: with open(CONFIG_FILENAME, 'w') as new_file: new_file.write('[this]\n\tthat = true\n') config = Config() config.add_file(Path(CONFIG_FILENAME), 0) assert 'this.that' in config def test_read(config: Config) -> None: with pytest.raises(TypeError): config[()] # type: ignore with pytest.raises(TypeError): config[-4] # type: ignore utils.assertRaisesWithArg( ValueError, "invalid config item name 'abc'", lambda: config['abc'] ) utils.assertRaisesWithArg(KeyError, 'abc.def', lambda: config['abc.def']) assert 'core.bare' in config assert not config.get_bool('core.bare') assert 'core.editor' in config assert config['core.editor'] == 'ed' assert 'core.repositoryformatversion' in config assert config.get_int('core.repositoryformatversion') == 0 def test_write(config: Config) -> None: with pytest.raises(TypeError): config.__setitem__((), 'This should not work') # type: ignore assert 'core.dummy1' not in config config['core.dummy1'] = 42 assert 'core.dummy1' in config assert config.get_int('core.dummy1') == 42 assert 'core.dummy2' not in config config['core.dummy2'] = 'foobar' assert 'core.dummy2' in config assert config['core.dummy2'] == 'foobar' assert 'core.dummy3' not in config config['core.dummy3'] = True assert 'core.dummy3' in config assert config['core.dummy3'] del config['core.dummy1'] assert 'core.dummy1' not in config del config['core.dummy2'] assert 'core.dummy2' not in config del config['core.dummy3'] assert 'core.dummy3' not in config def test_multivar() -> None: with open(CONFIG_FILENAME, 'w') as new_file: new_file.write('[this]\n\tthat = foobar\n\tthat = foobeer\n') config = Config() config.add_file(CONFIG_FILENAME, 6) assert 'this.that' in config assert ['foobar', 'foobeer'] == list(config.get_multivar('this.that')) assert ['foobar'] == list(config.get_multivar('this.that', 'bar')) assert ['foobar', 'foobeer'] == list(config.get_multivar('this.that', 'foo.*')) config.set_multivar('this.that', '^.*beer', 'fool') assert ['fool'] == list(config.get_multivar('this.that', 'fool')) config.set_multivar('this.that', 'foo.*', 'foo-123456') assert ['foo-123456', 'foo-123456'] == list( config.get_multivar('this.that', 'foo.*') ) config.delete_multivar('this.that', 'bar') assert ['foo-123456', 'foo-123456'] == list(config.get_multivar('this.that', '')) config.delete_multivar('this.that', 'foo-[0-9]+') assert [] == list(config.get_multivar('this.that', '')) def test_iterator(config: Config) -> None: lst = {} for entry in config: assert entry.level > -1 lst[entry.name] = entry.value assert 'core.bare' in lst assert lst['core.bare'] def test_valueless_key_iteration() -> None: # A valueless key (no `= value`) has a NULL value pointer in libgit2. # Iterating over such entries must not raise a RuntimeError. with open(CONFIG_FILENAME, 'w') as new_file: new_file.write('[section]\n\tvaluelesskey\n\tnormalkey = somevalue\n') config = Config() config.add_file(CONFIG_FILENAME, 6) entries = {entry.name: entry for entry in config} assert 'section.valuelesskey' in entries assert 'section.normalkey' in entries def test_valueless_key_value() -> None: # A valueless key must expose value=None and raw_value=None. with open(CONFIG_FILENAME, 'w') as new_file: new_file.write('[section]\n\tvaluelesskey\n\tnormalkey = somevalue\n') config = Config() config.add_file(CONFIG_FILENAME, 6) entries = {entry.name: entry for entry in config} assert entries['section.valuelesskey'].raw_value is None assert entries['section.valuelesskey'].value is None assert entries['section.normalkey'].raw_value == b'somevalue' assert entries['section.normalkey'].value == 'somevalue' def test_parsing() -> None: assert Config.parse_bool('on') assert Config.parse_bool('1') assert 5 == Config.parse_int('5') assert 1024 == Config.parse_int('1k') libgit2-pygit2-d86662e/test/test_credentials.py000066400000000000000000000156471521317216500215350ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import platform from pathlib import Path import pytest import pygit2 from pygit2 import ( Keypair, KeypairFromAgent, KeypairFromMemory, Repository, Username, UserPass, ) from pygit2.enums import CredentialType from . import utils REMOTE_NAME = 'origin' REMOTE_URL = 'git://github.com/libgit2/pygit2.git' REMOTE_FETCHSPEC_SRC = 'refs/heads/*' REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' REMOTE_REPO_OBJECTS = 30 REMOTE_REPO_BYTES = 2758 ORIGIN_REFSPEC = '+refs/heads/*:refs/remotes/origin/*' def test_username() -> None: username = 'git' cred = Username(username) assert (username,) == cred.credential_tuple def test_userpass() -> None: username = 'git' password = 'sekkrit' cred = UserPass(username, password) assert (username, password) == cred.credential_tuple def test_ssh_key() -> None: username = 'git' pubkey = 'id_rsa.pub' privkey = 'id_rsa' passphrase = 'bad wolf' cred = Keypair(username, pubkey, privkey, passphrase) assert (username, pubkey, privkey, passphrase) == cred.credential_tuple def test_ssh_key_aspath() -> None: username = 'git' pubkey = Path('id_rsa.pub') privkey = Path('id_rsa') passphrase = 'bad wolf' cred = Keypair(username, pubkey, privkey, passphrase) assert (username, pubkey, privkey, passphrase) == cred.credential_tuple def test_ssh_agent() -> None: username = 'git' cred = KeypairFromAgent(username) assert (username, None, None, None) == cred.credential_tuple def test_ssh_from_memory() -> None: username = 'git' pubkey = 'public key data' privkey = 'private key data' passphrase = 'secret passphrase' cred = KeypairFromMemory(username, pubkey, privkey, passphrase) assert (username, pubkey, privkey, passphrase) == cred.credential_tuple @utils.requires_network @utils.requires_ssh def test_keypair(tmp_path: Path, pygit2_empty_key: tuple[Path, str, str]) -> None: url = 'ssh://git@github.com/pygit2/empty' with pytest.raises(pygit2.GitError): pygit2.clone_repository(url, tmp_path) prv, pub, secret = pygit2_empty_key keypair = pygit2.Keypair('git', pub, prv, secret) callbacks = pygit2.RemoteCallbacks(credentials=keypair) pygit2.clone_repository(url, tmp_path, callbacks=callbacks) @utils.requires_network @utils.requires_ssh def test_keypair_from_memory( tmp_path: Path, pygit2_empty_key: tuple[Path, str, str] ) -> None: url = 'ssh://git@github.com/pygit2/empty' with pytest.raises(pygit2.GitError): pygit2.clone_repository(url, tmp_path) prv, pub, secret = pygit2_empty_key with open(prv) as f: prv_mem = f.read() with open(pub) as f: pub_mem = f.read() keypair = pygit2.KeypairFromMemory('git', pub_mem, prv_mem, secret) callbacks = pygit2.RemoteCallbacks(credentials=keypair) pygit2.clone_repository(url, tmp_path, callbacks=callbacks) def test_callback(testrepo: Repository) -> None: class MyCallbacks(pygit2.RemoteCallbacks): def credentials( self, url: str, username_from_url: str | None, allowed_types: CredentialType, ) -> Username | UserPass | Keypair: assert allowed_types & CredentialType.USERPASS_PLAINTEXT raise Exception("I don't know the password") url = 'https://github.com/github/github' remote = testrepo.remotes.create('github', url) with pytest.raises(Exception): remote.fetch(callbacks=MyCallbacks()) @utils.requires_network def test_bad_cred_type(testrepo: Repository) -> None: class MyCallbacks(pygit2.RemoteCallbacks): def credentials( self, url: str, username_from_url: str | None, allowed_types: CredentialType, ) -> Username | UserPass | Keypair: assert allowed_types & CredentialType.USERPASS_PLAINTEXT return Keypair('git', 'foo.pub', 'foo', 'sekkrit') url = 'https://github.com/github/github' remote = testrepo.remotes.create('github', url) with pytest.raises(TypeError): remote.fetch(callbacks=MyCallbacks()) @utils.requires_network def test_fetch_certificate_check(testrepo: Repository) -> None: class MyCallbacks(pygit2.RemoteCallbacks): def certificate_check( self, certificate: None, valid: bool, host: bytes ) -> bool: assert certificate is None assert valid is True assert host == b'github.com' return False url = 'https://github.com/libgit2/pygit2.git' remote = testrepo.remotes.create('https', url) with pytest.raises(pygit2.GitError) as exc: remote.fetch(callbacks=MyCallbacks()) # libgit2 uses different error message for Linux and Windows value = str(exc.value) if platform.system() == 'Windows': assert value == 'user cancelled certificate check' # winhttp else: assert value == 'user rejected certificate for github.com' # httpclient # TODO Add GitError.error_code # assert exc.value.error_code == pygit2.GIT_ERROR_HTTP @utils.requires_network def test_user_pass(testrepo: Repository) -> None: credentials = UserPass('libgit2', 'libgit2') callbacks = pygit2.RemoteCallbacks(credentials=credentials) url = 'https://github.com/libgit2/TestGitRepository' remote = testrepo.remotes.create('bb', url) remote.fetch(callbacks=callbacks) @utils.requires_proxy @utils.requires_network @utils.requires_future_libgit2 def test_proxy(testrepo: Repository) -> None: credentials = UserPass('libgit2', 'libgit2') callbacks = pygit2.RemoteCallbacks(credentials=credentials) url = 'https://github.com/libgit2/TestGitRepository' remote = testrepo.remotes.create('bb', url) remote.fetch(callbacks=callbacks, proxy='http://localhost:8888') libgit2-pygit2-d86662e/test/test_describe.py000066400000000000000000000106061521317216500210060ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for describing commits.""" import pytest import pygit2 from pygit2 import Oid, Repository from pygit2.enums import DescribeStrategy, ObjectType def add_tag(repo: Repository, name: str, target: str) -> Oid: message = 'Example tag.\n' tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0) sha = repo.create_tag(name, target, ObjectType.COMMIT, tagger, message) return sha def test_describe(testrepo: Repository) -> None: add_tag(testrepo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') assert 'thetag-2-g2be5719' == testrepo.describe() def test_describe_without_ref(testrepo: Repository) -> None: with pytest.raises(pygit2.GitError): testrepo.describe() def test_describe_default_oid(testrepo: Repository) -> None: assert '2be5719' == testrepo.describe(show_commit_oid_as_fallback=True) def test_describe_strategies(testrepo: Repository) -> None: assert 'heads/master' == testrepo.describe(describe_strategy=DescribeStrategy.ALL) testrepo.create_reference( 'refs/tags/thetag', '4ec4389a8068641da2d6578db0419484972284c8' ) with pytest.raises(KeyError): testrepo.describe() assert 'thetag-2-g2be5719' == testrepo.describe( describe_strategy=DescribeStrategy.TAGS ) def test_describe_pattern(testrepo: Repository) -> None: add_tag(testrepo, 'private/tag1', '5ebeeebb320790caf276b9fc8b24546d63316533') add_tag(testrepo, 'public/tag2', '4ec4389a8068641da2d6578db0419484972284c8') assert 'public/tag2-2-g2be5719' == testrepo.describe(pattern='public/*') def test_describe_committish(testrepo: Repository) -> None: add_tag(testrepo, 'thetag', 'acecd5ea2924a4b900e7e149496e1f4b57976e51') assert 'thetag-4-g2be5719' == testrepo.describe(committish='HEAD') assert 'thetag-1-g5ebeeeb' == testrepo.describe(committish='HEAD^') assert 'thetag-4-g2be5719' == testrepo.describe(committish=testrepo.head) assert 'thetag-1-g6aaa262' == testrepo.describe( committish='6aaa262e655dd54252e5813c8e5acd7780ed097d' ) assert 'thetag-1-g6aaa262' == testrepo.describe(committish='6aaa262') def test_describe_follows_first_branch_only(testrepo: Repository) -> None: add_tag(testrepo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') with pytest.raises(KeyError): testrepo.describe(only_follow_first_parent=True) def test_describe_abbreviated_size(testrepo: Repository) -> None: add_tag(testrepo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') assert 'thetag-2-g2be5719152d4f82c' == testrepo.describe(abbreviated_size=16) assert 'thetag' == testrepo.describe(abbreviated_size=0) def test_describe_long_format(testrepo: Repository) -> None: add_tag(testrepo, 'thetag', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') assert 'thetag-0-g2be5719' == testrepo.describe(always_use_long_format=True) def test_describe_dirty(dirtyrepo: Repository) -> None: add_tag(dirtyrepo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22') assert 'thetag' == dirtyrepo.describe() def test_describe_dirty_with_suffix(dirtyrepo: Repository) -> None: add_tag(dirtyrepo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22') assert 'thetag-dirty' == dirtyrepo.describe(dirty_suffix='-dirty') libgit2-pygit2-d86662e/test/test_diff.py000066400000000000000000000373421521317216500201440ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Diff objects.""" import textwrap from collections.abc import Iterator from itertools import chain from pathlib import Path import pytest import pygit2 from pygit2 import Diff, Repository from pygit2.enums import DeltaStatus, DiffFlag, DiffOption, DiffStatsFormat, FileMode from .utils import diff_safeiter COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' COMMIT_SHA1_3 = '2cdae28389c059815e951d0bb9eed6533f61a46b' COMMIT_SHA1_4 = 'ccca47fbb26183e71a7a46d165299b84e2e6c0b3' COMMIT_SHA1_5 = '056e626e51b1fc1ee2182800e399ed8d84c8f082' COMMIT_SHA1_6 = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' COMMIT_SHA1_7 = '784855caf26449a1914d2cf62d12b9374d76ae78' PATCH = """diff --git a/a b/a index 7f129fd..af431f2 100644 --- a/a +++ b/a @@ -1 +1 @@ -a contents 2 +a contents diff --git a/c/d b/c/d deleted file mode 100644 index 297efb8..0000000 --- a/c/d +++ /dev/null @@ -1 +0,0 @@ -c/d contents """ PATCHID = 'f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7' DIFF_HEAD_TO_INDEX_EXPECTED = [ '.gitignore', 'staged_changes', 'staged_changes_file_deleted', 'staged_changes_file_modified', 'staged_delete', 'staged_delete_file_modified', 'staged_new', 'staged_new_file_deleted', 'staged_new_file_modified', ] DIFF_HEAD_TO_WORKDIR_EXPECTED = [ 'file_deleted', 'modified_file', 'staged_changes', 'staged_changes_file_deleted', 'staged_changes_file_modified', 'staged_delete', 'staged_delete_file_modified', 'subdir/deleted_file', 'subdir/modified_file', ] DIFF_INDEX_TO_WORK_EXPECTED = [ '.gitignore', 'file_deleted', 'modified_file', 'staged_changes_file_deleted', 'staged_changes_file_modified', 'staged_new_file_deleted', 'staged_new_file_modified', 'subdir/deleted_file', 'subdir/modified_file', ] HUNK_EXPECTED = """- a contents 2 + a contents """ STATS_EXPECTED = """ a | 2 +- c/d | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 c/d """ TEXT_BLOB1 = """Common header of the file Blob 1 line 1 Common middle line 1 Common middle line 2 Common middle line 3 Blob 1 line 2 Common footer of the file """ TEXT_BLOB2 = """Common header of the file Blob 2 line 1 Common middle line 1 Common middle line 2 Common middle line 3 Blob 2 line 2 Common footer of the file """ PATCH_BLOBS_DEFAULT = """diff --git a/file b/file index 0b5ac93..ddfdbcc 100644 --- a/file +++ b/file @@ -1,7 +1,7 @@ Common header of the file -Blob 1 line 1 +Blob 2 line 1 Common middle line 1 Common middle line 2 Common middle line 3 -Blob 1 line 2 +Blob 2 line 2 Common footer of the file """ PATCH_BLOBS_NO_LEEWAY = """diff --git a/file b/file index 0b5ac93..ddfdbcc 100644 --- a/file +++ b/file @@ -2 +2 @@ Common header of the file -Blob 1 line 1 +Blob 2 line 1 @@ -6 +6 @@ Common middle line 3 -Blob 1 line 2 +Blob 2 line 2 """ PATCH_BLOBS_ONE_CONTEXT_LINE = """diff --git a/file b/file index 0b5ac93..ddfdbcc 100644 --- a/file +++ b/file @@ -1,3 +1,3 @@ Common header of the file -Blob 1 line 1 +Blob 2 line 1 Common middle line 1 @@ -5,3 +5,3 @@ Common middle line 2 Common middle line 3 -Blob 1 line 2 +Blob 2 line 2 Common footer of the file """ def test_diff_empty_index(dirtyrepo: Repository) -> None: repo = dirtyrepo head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff_to_index(repo.index) files = [patch.delta.new_file.path for patch in diff_safeiter(diff)] assert DIFF_HEAD_TO_INDEX_EXPECTED == files diff = repo.diff('HEAD', cached=True) files = [patch.delta.new_file.path for patch in diff_safeiter(diff)] assert DIFF_HEAD_TO_INDEX_EXPECTED == files def test_workdir_to_tree(dirtyrepo: Repository) -> None: repo = dirtyrepo head = repo[repo.lookup_reference('HEAD').resolve().target] diff = head.tree.diff_to_workdir() files = [patch.delta.new_file.path for patch in diff_safeiter(diff)] assert DIFF_HEAD_TO_WORKDIR_EXPECTED == files diff = repo.diff('HEAD') files = [patch.delta.new_file.path for patch in diff_safeiter(diff)] assert DIFF_HEAD_TO_WORKDIR_EXPECTED == files def test_index_to_workdir(dirtyrepo: Repository) -> None: diff = dirtyrepo.diff() files = [patch.delta.new_file.path for patch in diff_safeiter(diff)] assert DIFF_INDEX_TO_WORK_EXPECTED == files def test_diff_invalid(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] with pytest.raises(TypeError): commit_a.tree.diff_to_tree(commit_b) # type: ignore with pytest.raises(TypeError): commit_a.tree.diff_to_index(commit_b) # type: ignore def test_diff_empty_index_bare(barerepo: Repository) -> None: repo = barerepo head = repo[repo.lookup_reference('HEAD').resolve().target] diff = barerepo.index.diff_to_tree(head.tree) files = [patch.delta.new_file.path.split('/')[0] for patch in diff_safeiter(diff)] assert [x.name for x in head.tree] == files diff = head.tree.diff_to_index(repo.index) files = [patch.delta.new_file.path.split('/')[0] for patch in diff_safeiter(diff)] assert [x.name for x in head.tree] == files diff = repo.diff('HEAD', cached=True) files = [patch.delta.new_file.path.split('/')[0] for patch in diff_safeiter(diff)] assert [x.name for x in head.tree] == files def test_diff_tree(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] def _test(diff: Diff) -> None: assert diff is not None assert 2 == sum(map(lambda x: len(x.hunks), diff_safeiter(diff))) patch = diff[0] assert patch is not None hunk = patch.hunks[0] assert hunk.old_start == 1 assert hunk.old_lines == 1 assert hunk.new_start == 1 assert hunk.new_lines == 1 assert not patch.delta.is_binary assert patch.delta.flags & DiffFlag.NOT_BINARY for dfile in patch.delta.old_file, patch.delta.new_file: assert dfile.path == 'a' assert ( dfile.flags == DiffFlag.NOT_BINARY | DiffFlag.VALID_ID | DiffFlag.VALID_SIZE | DiffFlag.EXISTS ) assert dfile.mode == FileMode.BLOB _test(commit_a.tree.diff_to_tree(commit_b.tree)) _test(barerepo.diff(COMMIT_SHA1_1, COMMIT_SHA1_2)) def test_diff_empty_tree(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] diff = commit_a.tree.diff_to_tree() def get_context_for_lines(diff: Diff) -> Iterator[str]: hunks = chain.from_iterable(map(lambda x: x.hunks, diff_safeiter(diff))) lines = chain.from_iterable(map(lambda x: x.lines, hunks)) return map(lambda x: x.origin, lines) entries = [p.delta.new_file.path for p in diff_safeiter(diff)] assert all(commit_a.tree[x] for x in entries) assert all('-' == x for x in get_context_for_lines(diff)) diff_swaped = commit_a.tree.diff_to_tree(swap=True) entries = [p.delta.new_file.path for p in diff_safeiter(diff_swaped)] assert all(commit_a.tree[x] for x in entries) assert all('+' == x for x in get_context_for_lines(diff_swaped)) def test_diff_revparse(barerepo: Repository) -> None: diff = barerepo.diff('HEAD', 'HEAD~6') assert type(diff) is pygit2.Diff def test_diff_tree_opts(barerepo: Repository) -> None: commit_c = barerepo[COMMIT_SHA1_3] commit_d = barerepo[COMMIT_SHA1_4] for flag in [DiffOption.IGNORE_WHITESPACE, DiffOption.IGNORE_WHITESPACE_EOL]: diff = commit_c.tree.diff_to_tree(commit_d.tree, flag) assert diff is not None patch = diff[0] assert patch is not None assert 0 == len(patch.hunks) diff = commit_c.tree.diff_to_tree(commit_d.tree) assert diff is not None patch = diff[0] assert patch is not None assert 1 == len(patch.hunks) def test_diff_merge(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] commit_c = barerepo[COMMIT_SHA1_3] diff_b = commit_a.tree.diff_to_tree(commit_b.tree) assert diff_b is not None diff_c = commit_b.tree.diff_to_tree(commit_c.tree) assert diff_c is not None assert 'b' not in [patch.delta.new_file.path for patch in diff_safeiter(diff_b)] assert 'b' in [patch.delta.new_file.path for patch in diff_safeiter(diff_c)] diff_b.merge(diff_c) assert 'b' in [patch.delta.new_file.path for patch in diff_safeiter(diff_b)] patch = diff_b[0] assert patch is not None hunk = patch.hunks[0] assert hunk.old_start == 1 assert hunk.old_lines == 1 assert hunk.new_start == 1 assert hunk.new_lines == 1 assert patch.delta.old_file.path == 'a' assert patch.delta.new_file.path == 'a' def test_diff_patch(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] diff = commit_a.tree.diff_to_tree(commit_b.tree) assert diff.patch == PATCH assert len(diff) == len([patch for patch in diff]) def test_diff_ids(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] assert patch is not None delta = patch.delta assert delta.old_file.id == '7f129fd57e31e935c6d60a0c794efe4e6927664b' assert delta.new_file.id == 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' def test_diff_patchid(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] diff = commit_a.tree.diff_to_tree(commit_b.tree) assert diff.patch == PATCH assert diff.patchid == PATCHID def test_hunk_content(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] assert patch is not None hunk = patch.hunks[0] lines = (f'{x.origin} {x.content}' for x in hunk.lines) assert HUNK_EXPECTED == ''.join(lines) for line in hunk.lines: assert line.content == line.raw_content.decode() def test_find_similar(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_6] commit_b = barerepo[COMMIT_SHA1_7] # ~ Must pass INCLUDE_UNMODIFIED if you expect to emulate # ~ --find-copies-harder during rename transformion... diff = commit_a.tree.diff_to_tree(commit_b.tree, DiffOption.INCLUDE_UNMODIFIED) assert all( patch.delta.status != DeltaStatus.RENAMED for patch in diff_safeiter(diff) ) assert all(patch.delta.status_char() != 'R' for patch in diff_safeiter(diff)) diff.find_similar() assert any( patch.delta.status == DeltaStatus.RENAMED for patch in diff_safeiter(diff) ) assert any(patch.delta.status_char() == 'R' for patch in diff_safeiter(diff)) def test_diff_stats(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] diff = commit_a.tree.diff_to_tree(commit_b.tree) stats = diff.stats assert 1 == stats.insertions assert 2 == stats.deletions assert 2 == stats.files_changed formatted = stats.format( format=DiffStatsFormat.FULL | DiffStatsFormat.INCLUDE_SUMMARY, width=80 ) assert STATS_EXPECTED == formatted def test_deltas(barerepo: Repository) -> None: commit_a = barerepo[COMMIT_SHA1_1] commit_b = barerepo[COMMIT_SHA1_2] diff = commit_a.tree.diff_to_tree(commit_b.tree) deltas = list(diff.deltas) patches = list(diff_safeiter(diff)) assert len(deltas) == len(patches) for i, delta in enumerate(deltas): patch_delta = patches[i].delta assert isinstance(delta.status, DeltaStatus) assert isinstance(patch_delta.status, DeltaStatus) assert delta.status == patch_delta.status assert delta.similarity == patch_delta.similarity assert delta.nfiles == patch_delta.nfiles assert delta.old_file.id == patch_delta.old_file.id assert delta.new_file.id == patch_delta.new_file.id assert delta.old_file.mode == patch_delta.old_file.mode assert delta.new_file.mode == patch_delta.new_file.mode # As explained in the libgit2 documentation, flags are not set # assert delta.flags == patch_delta.flags def test_diff_parse(barerepo: Repository) -> None: diff = pygit2.Diff.parse_diff(PATCH) stats = diff.stats assert 2 == stats.deletions assert 1 == stats.insertions assert 2 == stats.files_changed deltas = list(diff.deltas) assert 2 == len(deltas) def test_parse_diff_null() -> None: with pytest.raises(TypeError): pygit2.Diff.parse_diff(None) # type: ignore def test_parse_diff_bad() -> None: diff = textwrap.dedent( """ diff --git a/file1 b/file1 old mode 0644 new mode 0644 @@ -1,1 +1,1 @@ -Hi! """ ) with pytest.raises(pygit2.GitError): pygit2.Diff.parse_diff(diff) def test_diff_blobs(emptyrepo: Repository) -> None: repo = emptyrepo blob1 = repo.create_blob(TEXT_BLOB1.encode()) blob2 = repo.create_blob(TEXT_BLOB2.encode()) diff_default = repo.diff(blob1, blob2) assert diff_default.text == PATCH_BLOBS_DEFAULT diff_no_leeway = repo.diff(blob1, blob2, context_lines=0) assert diff_no_leeway.text == PATCH_BLOBS_NO_LEEWAY diff_one_context_line = repo.diff(blob1, blob2, context_lines=1) assert diff_one_context_line.text == PATCH_BLOBS_ONE_CONTEXT_LINE diff_all_together = repo.diff(blob1, blob2, context_lines=1, interhunk_lines=1) assert diff_all_together.text == PATCH_BLOBS_DEFAULT def test_diff_unchanged_file_no_patch(testrepo: Repository) -> None: repo = testrepo # Convert hello.txt line endings to CRLF path = Path(repo.workdir) / 'hello.txt' data = path.read_bytes() data = data.replace(b'\n', b'\r\n') path.write_bytes(data) # Enable CRLF filter repo.config['core.autocrlf'] = 'input' diff = repo.diff() assert len(diff) == 1 # Get patch #0 in the same diff several times. # git_patch_from_diff eventually decides that the file is "unchanged"; # it returns a NULL patch in this case. # https://libgit2.org/docs/reference/main/patch/git_patch_from_diff for i in range(10): # loop typically exits in the third iteration patch = diff[0] if patch is None: # libgit2 decides the file is unchanged break assert patch.delta.new_file.path == path.name assert patch.text == '' # no content change (just line endings) else: # Didn't find the edge case that this test is supposed to exercise. assert False, 'libgit2 rebuilt a new patch every time' libgit2-pygit2-d86662e/test/test_diff_binary.py000066400000000000000000000045171521317216500215060ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Repository from pygit2.enums import DiffOption from . import utils @pytest.fixture def repo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('binaryfilerepo.zip', tmp_path) as path: yield pygit2.Repository(path) PATCH_BINARY = """diff --git a/binary_file b/binary_file index 86e5c10..b835d73 100644 Binary files a/binary_file and b/binary_file differ """ PATCH_BINARY_SHOW = """diff --git a/binary_file b/binary_file index 86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6..b835d73543244b6694f36a8c5dfdffb71b153db7 100644 GIT binary patch literal 8 Pc${NM%FIhFs^kIy3n&7R literal 8 Pc${NM&PdElPvrst3ey5{ """ def test_binary_diff(repo: Repository) -> None: diff = repo.diff('HEAD', 'HEAD^') assert PATCH_BINARY == diff.patch diff = repo.diff('HEAD', 'HEAD^', flags=DiffOption.SHOW_BINARY) assert PATCH_BINARY_SHOW == diff.patch diff = repo.diff(b'HEAD', b'HEAD^') assert PATCH_BINARY == diff.patch diff = repo.diff(b'HEAD', b'HEAD^', flags=DiffOption.SHOW_BINARY) assert PATCH_BINARY_SHOW == diff.patch libgit2-pygit2-d86662e/test/test_filter.py000066400000000000000000000160531521317216500205150ustar00rootroot00000000000000import codecs import gc from collections.abc import Callable, Generator from io import BytesIO import pytest import pygit2 from pygit2 import Blob, Filter, FilterSource, Repository from pygit2.enums import BlobFilter, FilterMode from pygit2.errors import Passthrough def _rot13(data: bytes) -> bytes: return codecs.encode(data.decode('utf-8'), 'rot_13').encode('utf-8') class _Rot13Filter(pygit2.Filter): attributes = 'text' def write( self, data: bytes, src: FilterSource, write_next: Callable[[bytes], None], ) -> None: return super().write(_rot13(data), src, write_next) class _BufferedFilter(pygit2.Filter): attributes = 'text' def __init__(self) -> None: super().__init__() self.buf = BytesIO() def write( self, data: bytes, src: FilterSource, write_next: Callable[[bytes], None], ) -> None: self.buf.write(data) def close(self, write_next: Callable[[bytes], None]) -> None: write_next(_rot13(self.buf.getvalue())) class _PassthroughFilter(_Rot13Filter): def check(self, src: FilterSource, attr_values: list[str | None]) -> None: assert attr_values == [None] assert src.repo raise Passthrough class _UnmatchedFilter(_Rot13Filter): attributes = 'filter=rot13' def _filter_fixture(name: str, filter: type[Filter]) -> Generator[None, None, None]: pygit2.filter_register(name, filter) yield # Collect any FilterLists that may use this filter before unregistering it gc.collect() pygit2.filter_unregister(name) @pytest.fixture def rot13_filter() -> Generator[None, None, None]: yield from _filter_fixture('rot13', _Rot13Filter) @pytest.fixture def passthrough_filter() -> Generator[None, None, None]: yield from _filter_fixture('passthrough-rot13', _PassthroughFilter) @pytest.fixture def buffered_filter() -> Generator[None, None, None]: yield from _filter_fixture('buffered-rot13', _BufferedFilter) @pytest.fixture def unmatched_filter() -> Generator[None, None, None]: yield from _filter_fixture('unmatched-rot13', _UnmatchedFilter) def test_filter(testrepo: Repository, rot13_filter: Filter) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, Blob) flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD assert b'olr jbeyq\n' == blob.data with pygit2.BlobIO(blob) as reader: assert b'olr jbeyq\n' == reader.read() with pygit2.BlobIO(blob, as_path='bye.txt', flags=flags) as reader: assert b'bye world\n' == reader.read() def test_filter_buffered(testrepo: Repository, buffered_filter: Filter) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, Blob) flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD assert b'olr jbeyq\n' == blob.data with pygit2.BlobIO(blob) as reader: assert b'olr jbeyq\n' == reader.read() with pygit2.BlobIO(blob, 'bye.txt', flags=flags) as reader: assert b'bye world\n' == reader.read() def test_filter_passthrough(testrepo: Repository, passthrough_filter: Filter) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, Blob) flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD assert b'bye world\n' == blob.data with pygit2.BlobIO(blob) as reader: assert b'bye world\n' == reader.read() with pygit2.BlobIO(blob, 'bye.txt', flags=flags) as reader: assert b'bye world\n' == reader.read() def test_filter_unmatched(testrepo: Repository, unmatched_filter: Filter) -> None: blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] assert isinstance(blob, Blob) flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD assert b'bye world\n' == blob.data with pygit2.BlobIO(blob) as reader: assert b'bye world\n' == reader.read() with pygit2.BlobIO(blob, as_path='bye.txt', flags=flags) as reader: assert b'bye world\n' == reader.read() def test_filter_cleanup(dirtyrepo: Repository, rot13_filter: Filter) -> None: # Indirectly test that pygit2_filter_cleanup has the GIL # before calling pygit2_filter_payload_free. dirtyrepo.diff() def test_filterlist_none(testrepo: Repository) -> None: fl = testrepo.load_filter_list('hello.txt') assert fl is None def test_filterlist_apply_to_buffer_crlf_clean(testrepo: Repository) -> None: testrepo.config['core.autocrlf'] = True fl = testrepo.load_filter_list('whatever.txt', mode=FilterMode.CLEAN) assert fl is not None assert len(fl) == 1 assert 'crlf' in fl assert 'bogus_filter_name' not in fl with pytest.raises(TypeError): 1234 in fl # type: ignore filtered = fl.apply_to_buffer(b'hello\r\nworld\r\n') assert filtered == b'hello\nworld\n' def test_filterlist_apply_to_buffer_crlf_smudge(testrepo: Repository) -> None: testrepo.config['core.autocrlf'] = True fl = testrepo.load_filter_list('whatever.txt', mode=FilterMode.SMUDGE) assert fl is not None assert len(fl) == 1 assert 'crlf' in fl filtered = fl.apply_to_buffer(b'hello\nworld\n') assert filtered == b'hello\r\nworld\r\n' def test_filterlist_dangerous_unregister(testrepo: Repository) -> None: pygit2.filter_register('rot13', _Rot13Filter) fl = testrepo.load_filter_list('hello.txt') assert fl is not None assert len(fl) == 1 assert 'rot13' in fl # Unregistering a filter that's still in use in a FilterList is dangerous! # Our built-in check (that raises RuntimeError) may avert a segfault. with pytest.raises(RuntimeError): pygit2.filter_unregister('rot13') # Delete any FilterLists that use the filter, and only then is it safe # to unregister the filter. del fl gc.collect() pygit2.filter_unregister('rot13') def test_filterlist_apply_to_file(testrepo: Repository, rot13_filter: Filter) -> None: fl = testrepo.load_filter_list('bye.txt') assert fl is not None assert len(fl) == 1 assert 'rot13' in fl filtered = fl.apply_to_file(testrepo, 'bye.txt') assert filtered == b'olr jbeyq\n' def test_filterlist_apply_to_blob(testrepo: Repository, rot13_filter: Filter) -> None: fl = testrepo.load_filter_list('whatever.txt') assert fl is not None assert len(fl) == 1 assert 'rot13' in fl blob_oid = testrepo.create_blob(b'bye world\n') blob = testrepo[blob_oid] assert isinstance(blob, Blob) filtered = fl.apply_to_blob(blob) assert filtered == b'olr jbeyq\n' def test_filterlist_apply_to_buffer_multiple( testrepo: Repository, rot13_filter: Filter ) -> None: testrepo.config['core.autocrlf'] = True fl = testrepo.load_filter_list('whatever.txt') assert fl is not None assert len(fl) == 2 assert 'crlf' in fl assert 'rot13' in fl filtered = fl.apply_to_buffer(b'bye\r\nworld\r\n') assert filtered == b'olr\njbeyq\n' libgit2-pygit2-d86662e/test/test_index.py000066400000000000000000000237231521317216500203410ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Index files.""" from pathlib import Path import pytest import pygit2 from pygit2 import Index, IndexEntry, Oid, Repository, Tree from pygit2.enums import FileMode from . import utils def test_bare(barerepo: Repository) -> None: assert len(barerepo.index) == 0 def test_index(testrepo: Repository) -> None: assert testrepo.index is not None def test_read(testrepo: Repository) -> None: index = testrepo.index assert len(index) == 2 with pytest.raises(TypeError): index[()] utils.assertRaisesWithArg(ValueError, -4, lambda: index[-4]) utils.assertRaisesWithArg(KeyError, 'abc', lambda: index['abc']) sha = 'a520c24d85fbfc815d385957eed41406ca5a860b' assert 'hello.txt' in index assert index['hello.txt'].id == sha assert index['hello.txt'].path == 'hello.txt' assert index[1].id == sha def test_add(testrepo: Repository) -> None: index = testrepo.index sha = '0907563af06c7464d62a70cdd135a6ba7d2b41d8' assert 'bye.txt' not in index index.add('bye.txt') assert 'bye.txt' in index assert len(index) == 3 assert index['bye.txt'].id == sha def test_add_aspath(testrepo: Repository) -> None: index = testrepo.index assert 'bye.txt' not in index index.add(Path('bye.txt')) assert 'bye.txt' in index def test_add_all(testrepo: Repository) -> None: clear(testrepo) sha_bye = '0907563af06c7464d62a70cdd135a6ba7d2b41d8' sha_hello = 'a520c24d85fbfc815d385957eed41406ca5a860b' index = testrepo.index index.add_all(['*.txt']) assert 'bye.txt' in index assert 'hello.txt' in index assert index['bye.txt'].id == sha_bye assert index['hello.txt'].id == sha_hello clear(testrepo) index.add_all(['bye.t??', 'hello.*']) assert 'bye.txt' in index assert 'hello.txt' in index assert index['bye.txt'].id == sha_bye assert index['hello.txt'].id == sha_hello clear(testrepo) index.add_all(['[byehlo]*.txt']) assert 'bye.txt' in index assert 'hello.txt' in index assert index['bye.txt'].id == sha_bye assert index['hello.txt'].id == sha_hello def test_add_all_aspath(testrepo: Repository) -> None: clear(testrepo) index = testrepo.index index.add_all([Path('bye.txt'), Path('hello.txt')]) assert 'bye.txt' in index assert 'hello.txt' in index def clear(repo: Repository) -> None: index = repo.index assert len(index) == 2 index.clear() assert len(index) == 0 def test_write(testrepo: Repository) -> None: index = testrepo.index index.add('bye.txt') index.write() index.clear() assert 'bye.txt' not in index index.read() assert 'bye.txt' in index def test_read_tree(testrepo: Repository) -> None: tree_oid = '68aba62e560c0ebc3396e8ae9335232cd93a3f60' # Test reading first tree index = testrepo.index assert len(index) == 2 index.read_tree(tree_oid) assert len(index) == 1 # Test read-write returns the same oid assert index.write_tree() == tree_oid # Test the index is only modified in memory index.read() assert len(index) == 2 def test_write_tree(testrepo: Repository) -> None: assert testrepo.index.write_tree() == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' def test_iter(testrepo: Repository) -> None: index = testrepo.index n = len(index) assert len(list(index)) == n # Compare SHAs, not IndexEntry object identity entries = [index[x].id for x in range(n)] assert list(x.id for x in index) == entries def test_mode(testrepo: Repository) -> None: """ Testing that we can access an index entry mode. """ index = testrepo.index hello_mode = index['hello.txt'].mode assert hello_mode == 33188 def test_bare_index(testrepo: Repository) -> None: index = pygit2.Index(Path(testrepo.path) / 'index') assert [x.id for x in index] == [x.id for x in testrepo.index] with pytest.raises(pygit2.GitError): index.add('bye.txt') def test_remove(testrepo: Repository) -> None: index = testrepo.index assert 'hello.txt' in index index.remove('hello.txt') assert 'hello.txt' not in index def test_remove_directory(dirtyrepo: Repository) -> None: index = dirtyrepo.index assert 'subdir/current_file' in index index.remove_directory('subdir') assert 'subdir/current_file' not in index def test_remove_all(testrepo: Repository) -> None: index = testrepo.index assert 'hello.txt' in index index.remove_all(['*.txt']) assert 'hello.txt' not in index index.remove_all(['not-existing']) # this doesn't error def test_remove_aspath(testrepo: Repository) -> None: index = testrepo.index assert 'hello.txt' in index index.remove(Path('hello.txt')) assert 'hello.txt' not in index def test_remove_directory_aspath(dirtyrepo: Repository) -> None: index = dirtyrepo.index assert 'subdir/current_file' in index index.remove_directory(Path('subdir')) assert 'subdir/current_file' not in index def test_remove_all_aspath(testrepo: Repository) -> None: index = testrepo.index assert 'hello.txt' in index index.remove_all([Path('hello.txt')]) assert 'hello.txt' not in index def test_change_attributes(testrepo: Repository) -> None: index = testrepo.index entry = index['hello.txt'] ign_entry = index['.gitignore'] assert ign_entry.id != entry.id assert entry.mode != FileMode.BLOB_EXECUTABLE entry.path = 'foo.txt' entry.id = ign_entry.id entry.mode = FileMode.BLOB_EXECUTABLE assert 'foo.txt' == entry.path assert ign_entry.id == entry.id assert FileMode.BLOB_EXECUTABLE == entry.mode def test_write_tree_to(testrepo: Repository, tmp_path: Path) -> None: pygit2.option(pygit2.enums.Option.ENABLE_STRICT_OBJECT_CREATION, False) with utils.TemporaryRepository('emptyrepo.zip', tmp_path) as path: nrepo = Repository(path) id = testrepo.index.write_tree(nrepo) assert nrepo[id] is not None def test_create_entry(testrepo: Repository) -> None: index = testrepo.index hello_entry = index['hello.txt'] entry = pygit2.IndexEntry('README.md', hello_entry.id, hello_entry.mode) index.add(entry) assert '60e769e57ae1d6a2ab75d8d253139e6260e1f912' == index.write_tree() def test_create_entry_aspath(testrepo: Repository) -> None: index = testrepo.index hello_entry = index[Path('hello.txt')] entry = pygit2.IndexEntry(Path('README.md'), hello_entry.id, hello_entry.mode) index.add(entry) index.write_tree() def test_entry_eq(testrepo: Repository) -> None: index = testrepo.index hello_entry = index['hello.txt'] entry = pygit2.IndexEntry(hello_entry.path, hello_entry.id, hello_entry.mode) assert hello_entry == entry entry = pygit2.IndexEntry('README.md', hello_entry.id, hello_entry.mode) assert hello_entry != entry oid = Oid(hex='0907563af06c7464d62a70cdd135a6ba7d2b41d8') entry = pygit2.IndexEntry(hello_entry.path, oid, hello_entry.mode) assert hello_entry != entry entry = pygit2.IndexEntry( hello_entry.path, hello_entry.id, FileMode.BLOB_EXECUTABLE ) assert hello_entry != entry def test_entry_repr(testrepo: Repository) -> None: index = testrepo.index hello_entry = index['hello.txt'] assert ( repr(hello_entry) == '' ) assert ( str(hello_entry) == '' ) def test_create_empty() -> None: Index() def test_create_empty_read_tree_as_string() -> None: index = Index() # no repo associated, so we don't know where to read from with pytest.raises(TypeError): index('read_tree', 'fd937514cb799514d4b81bb24c5fcfeb6472b245') # type: ignore def test_create_empty_read_tree(testrepo: Repository) -> None: index = Index() tree = testrepo['fd937514cb799514d4b81bb24c5fcfeb6472b245'] assert isinstance(tree, Tree) index.read_tree(tree) @utils.fails_in_macos def test_add_conflict(testrepo: Repository) -> None: ancestor_blob_id = testrepo.create_blob('ancestor') ancestor = IndexEntry('conflict.txt', ancestor_blob_id, FileMode.BLOB_EXECUTABLE) ours_blob_id = testrepo.create_blob('ours') ours = IndexEntry('conflict.txt', ours_blob_id, FileMode.BLOB) index = Index() assert index.conflicts is None index.add_conflict(ancestor, ours, None) assert index.conflicts is not None assert 'conflict.txt' in index.conflicts conflict_ancestor, conflict_ours, conflict_theirs = index.conflicts['conflict.txt'] assert conflict_ancestor.id == ancestor_blob_id assert conflict_ancestor.mode == FileMode.BLOB_EXECUTABLE assert conflict_ours.id == ours_blob_id assert conflict_ours.mode == FileMode.BLOB assert conflict_theirs is None libgit2-pygit2-d86662e/test/test_mailmap.py000066400000000000000000000067231521317216500206530ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Mailmap.""" from pygit2 import Mailmap TEST_MAILMAP = """\ # Simple Comment line Some Dude nick1 Other Author nick2 Other Author Phil Hill # Comment at end of line Joseph Santa Claus """ TEST_ENTRIES = [ (None, 'cto@company.xx', None, 'cto@coompany.xx'), ('Some Dude', 'some@dude.xx', 'nick1', 'bugs@company.xx'), ('Other Author', 'other@author.xx', 'nick2', 'bugs@company.xx'), ('Other Author', 'other@author.xx', None, 'nick2@company.xx'), ('Phil Hill', None, None, 'phil@company.xx'), (None, 'joseph@company.xx', 'Joseph', 'bugs@company.xx'), ('Santa Claus', 'santa.claus@northpole.xx', None, 'me@company.xx'), ] TEST_RESOLVE = [ ('Brad', 'cto@company.xx', 'Brad', 'cto@coompany.xx'), ('Brad L', 'cto@company.xx', 'Brad L', 'cto@coompany.xx'), ('Some Dude', 'some@dude.xx', 'nick1', 'bugs@company.xx'), ('Other Author', 'other@author.xx', 'nick2', 'bugs@company.xx'), ('nick3', 'bugs@company.xx', 'nick3', 'bugs@company.xx'), ('Other Author', 'other@author.xx', 'Some Garbage', 'nick2@company.xx'), ('Phil Hill', 'phil@company.xx', 'unknown', 'phil@company.xx'), ('Joseph', 'joseph@company.xx', 'Joseph', 'bugs@company.xx'), ('Santa Claus', 'santa.claus@northpole.xx', 'Clause', 'me@company.xx'), ('Charles', 'charles@charles.xx', 'Charles', 'charles@charles.xx'), ] def test_empty() -> None: mailmap = Mailmap() for _, _, name, email in TEST_RESOLVE: assert mailmap.resolve(name, email) == (name, email) def test_new() -> None: mailmap = Mailmap() # Add entries to the mailmap for entry in TEST_ENTRIES: mailmap.add_entry(*entry) for real_name, real_email, name, email in TEST_RESOLVE: assert mailmap.resolve(name, email) == (real_name, real_email) def test_parsed() -> None: mailmap = Mailmap.from_buffer(TEST_MAILMAP) for real_name, real_email, name, email in TEST_RESOLVE: assert mailmap.resolve(name, email) == (real_name, real_email) # TODO: Add a testcase which uses .mailmap in a repo libgit2-pygit2-d86662e/test/test_merge.py000066400000000000000000000324051521317216500203260ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for merging and information about it.""" from pathlib import Path import pytest import pygit2 from pygit2 import Repository from pygit2.enums import FileStatus, MergeAnalysis, MergeFavor, MergeFileFlag, MergeFlag @pytest.mark.parametrize('id', [None, 42]) def test_merge_invalid_type(mergerepo: Repository, id: None | int) -> None: with pytest.raises(TypeError): mergerepo.merge(id) # type:ignore # TODO: Once Repository.merge drops support for str arguments, # add an extra parameter to test_merge_invalid_type above # to make sure we cover legacy code. def test_merge_string_argument_deprecated(mergerepo: Repository) -> None: branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' with pytest.warns(DeprecationWarning, match=r'Pass Commit.+instead'): mergerepo.merge(branch_head_hex) def test_merge_analysis_uptodate(mergerepo: Repository) -> None: branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' branch_id = mergerepo[branch_head_hex].id analysis, preference = mergerepo.merge_analysis(branch_id) assert analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD assert {} == mergerepo.status() analysis, preference = mergerepo.merge_analysis(branch_id, 'refs/heads/ff-branch') assert analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD assert {} == mergerepo.status() def test_merge_analysis_fastforward(mergerepo: Repository) -> None: branch_head_hex = 'e97b4cfd5db0fb4ebabf4f203979ca4e5d1c7c87' branch_id = mergerepo[branch_head_hex].id analysis, preference = mergerepo.merge_analysis(branch_id) assert not analysis & MergeAnalysis.UP_TO_DATE assert analysis & MergeAnalysis.FASTFORWARD assert {} == mergerepo.status() analysis, preference = mergerepo.merge_analysis(branch_id, 'refs/heads/master') assert not analysis & MergeAnalysis.UP_TO_DATE assert analysis & MergeAnalysis.FASTFORWARD assert {} == mergerepo.status() def test_merge_no_fastforward_no_conflicts(mergerepo: Repository) -> None: branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' branch_id = mergerepo[branch_head_hex].id analysis, preference = mergerepo.merge_analysis(branch_id) assert not analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD # Asking twice to assure the reference counting is correct assert {} == mergerepo.status() assert {} == mergerepo.status() def test_merge_invalid_hex(mergerepo: Repository) -> None: branch_head_hex = '12345678' with ( pytest.raises(KeyError), pytest.warns(DeprecationWarning, match=r'Pass Commit.+instead'), ): mergerepo.merge(branch_head_hex) def test_merge_already_something_in_index(mergerepo: Repository) -> None: branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' branch_oid = mergerepo[branch_head_hex].id with (Path(mergerepo.workdir) / 'inindex.txt').open('w') as f: f.write('new content') mergerepo.index.add('inindex.txt') with pytest.raises(pygit2.GitError): mergerepo.merge(branch_oid) def test_merge_no_fastforward_conflicts(mergerepo: Repository) -> None: branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' branch_id = mergerepo[branch_head_hex].id analysis, preference = mergerepo.merge_analysis(branch_id) assert not analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD mergerepo.merge(branch_id) assert mergerepo.index.conflicts is not None with pytest.raises(KeyError): mergerepo.index.conflicts.__getitem__('some-file') assert 'some-file' not in mergerepo.index.conflicts assert '.gitignore' in mergerepo.index.conflicts status = FileStatus.CONFLICTED # Asking twice to assure the reference counting is correct assert {'.gitignore': status} == mergerepo.status() assert {'.gitignore': status} == mergerepo.status() ancestor, ours, theirs = mergerepo.index.conflicts['.gitignore'] assert ancestor is None assert ours is not None assert theirs is not None assert '.gitignore' == ours.path assert '.gitignore' == theirs.path assert 1 == len(list(mergerepo.index.conflicts)) # Checking the index works as expected mergerepo.index.add('.gitignore') mergerepo.index.write() assert mergerepo.index.conflicts is None assert {'.gitignore': FileStatus.INDEX_MODIFIED} == mergerepo.status() def test_merge_remove_conflicts(mergerepo: Repository) -> None: other_branch_tip = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') mergerepo.merge(other_branch_tip) idx = mergerepo.index conflicts = idx.conflicts assert conflicts is not None assert '.gitignore' in conflicts try: conflicts['.gitignore'] except KeyError: mergerepo.fail("conflicts['.gitignore'] raised KeyError unexpectedly") # type: ignore del idx.conflicts['.gitignore'] with pytest.raises(KeyError): conflicts.__getitem__('.gitignore') assert '.gitignore' not in conflicts assert idx.conflicts is None @pytest.mark.parametrize( 'favor', [ MergeFavor.OURS, MergeFavor.THEIRS, MergeFavor.UNION, ], ) def test_merge_favor(mergerepo: Repository, favor: MergeFavor) -> None: branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') mergerepo.merge(branch_head, favor=favor) assert mergerepo.index.conflicts is None def test_merge_fail_on_conflict(mergerepo: Repository) -> None: branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') with pytest.raises(pygit2.GitError, match=r'merge conflicts exist'): mergerepo.merge( branch_head, flags=MergeFlag.FIND_RENAMES | MergeFlag.FAIL_ON_CONFLICT ) def test_merge_commits(mergerepo: Repository) -> None: branch_head = pygit2.Oid(hex='03490f16b15a09913edb3a067a3dc67fbb8d41f1') merge_index = mergerepo.merge_commits(mergerepo.head.target, branch_head) assert merge_index.conflicts is None merge_commits_tree = merge_index.write_tree(mergerepo) mergerepo.merge(branch_head) index = mergerepo.index assert index.conflicts is None merge_tree = index.write_tree() assert merge_tree == merge_commits_tree def test_merge_commits_favor(mergerepo: Repository) -> None: branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') merge_index = mergerepo.merge_commits( mergerepo.head.target, branch_head, favor=MergeFavor.OURS ) assert merge_index.conflicts is None # Incorrect favor value with pytest.raises(TypeError, match=r'favor argument must be MergeFavor'): mergerepo.merge_commits(mergerepo.head.target, branch_head, favor='foo') # type: ignore def test_merge_trees(mergerepo: Repository) -> None: branch_id = pygit2.Oid(hex='03490f16b15a09913edb3a067a3dc67fbb8d41f1') ancestor_id = mergerepo.merge_base(mergerepo.head.target, branch_id) merge_index = mergerepo.merge_trees(ancestor_id, mergerepo.head.target, branch_id) assert merge_index.conflicts is None merge_commits_tree = merge_index.write_tree(mergerepo) mergerepo.merge(branch_id) index = mergerepo.index assert index.conflicts is None merge_tree = index.write_tree() assert merge_tree == merge_commits_tree def test_merge_trees_favor(mergerepo: Repository) -> None: branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' ancestor_id = mergerepo.merge_base(mergerepo.head.target, branch_head_hex) merge_index = mergerepo.merge_trees( ancestor_id, mergerepo.head.target, branch_head_hex, favor=MergeFavor.OURS ) assert merge_index.conflicts is None with pytest.raises(TypeError): mergerepo.merge_trees( ancestor_id, mergerepo.head.target, branch_head_hex, favor='foo', # type: ignore ) def test_merge_options() -> None: favor = MergeFavor.OURS flags: int | MergeFlag = MergeFlag.FIND_RENAMES | MergeFlag.FAIL_ON_CONFLICT file_flags: int | MergeFileFlag = ( MergeFileFlag.IGNORE_WHITESPACE | MergeFileFlag.DIFF_PATIENCE ) o1 = pygit2.Repository._merge_options( favor=favor, flags=flags, file_flags=file_flags ) assert favor == o1.file_favor assert flags == o1.flags assert file_flags == o1.file_flags favor = MergeFavor.THEIRS flags = 0 file_flags = 0 o1 = pygit2.Repository._merge_options( favor=favor, flags=flags, file_flags=file_flags ) assert favor == o1.file_favor assert flags == o1.flags assert file_flags == o1.file_flags favor = MergeFavor.UNION flags = MergeFlag.FIND_RENAMES | MergeFlag.NO_RECURSIVE file_flags = ( MergeFileFlag.STYLE_DIFF3 | MergeFileFlag.IGNORE_WHITESPACE | MergeFileFlag.DIFF_PATIENCE ) o1 = pygit2.Repository._merge_options( favor=favor, flags=flags, file_flags=file_flags ) assert favor == o1.file_favor assert flags == o1.flags assert file_flags == o1.file_flags def test_merge_many(mergerepo: Repository) -> None: branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' branch_id = mergerepo[branch_head_hex].id ancestor_id = mergerepo.merge_base_many([mergerepo.head.target, branch_id]) merge_index = mergerepo.merge_trees( ancestor_id, mergerepo.head.target, branch_head_hex ) assert merge_index.conflicts is None merge_commits_tree = merge_index.write_tree(mergerepo) mergerepo.merge(branch_id) index = mergerepo.index assert index.conflicts is None merge_tree = index.write_tree() assert merge_tree == merge_commits_tree def test_merge_octopus(mergerepo: Repository) -> None: branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' branch_id = mergerepo[branch_head_hex].id ancestor_id = mergerepo.merge_base_octopus([mergerepo.head.target, branch_id]) merge_index = mergerepo.merge_trees( ancestor_id, mergerepo.head.target, branch_head_hex ) assert merge_index.conflicts is None merge_commits_tree = merge_index.write_tree(mergerepo) mergerepo.merge(branch_id) index = mergerepo.index assert index.conflicts is None merge_tree = index.write_tree() assert merge_tree == merge_commits_tree def test_merge_mergeheads(mergerepo: Repository) -> None: assert mergerepo.listall_mergeheads() == [] branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') mergerepo.merge(branch_head) assert mergerepo.listall_mergeheads() == [branch_head] mergerepo.state_cleanup() assert mergerepo.listall_mergeheads() == [], ( 'state_cleanup() should wipe the mergeheads' ) def test_merge_message(mergerepo: Repository) -> None: assert not mergerepo.message assert not mergerepo.raw_message branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') mergerepo.merge(branch_head) assert mergerepo.message.startswith(f"Merge commit '{branch_head}'") assert mergerepo.message.encode('utf-8') == mergerepo.raw_message mergerepo.state_cleanup() assert not mergerepo.message def test_merge_remove_message(mergerepo: Repository) -> None: branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') mergerepo.merge(branch_head) assert mergerepo.message.startswith(f"Merge commit '{branch_head}'") mergerepo.remove_message() assert not mergerepo.message def test_merge_commit(mergerepo: Repository) -> None: commit = mergerepo['1b2bae55ac95a4be3f8983b86cd579226d0eb247'] assert isinstance(commit, pygit2.Commit) mergerepo.merge(commit) assert mergerepo.message.startswith(f"Merge commit '{str(commit.id)}'") assert mergerepo.listall_mergeheads() == [commit.id] def test_merge_reference(mergerepo: Repository) -> None: branch = mergerepo.branches.local['branch-conflicts'] branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' mergerepo.merge(branch) assert mergerepo.message.startswith("Merge branch 'branch-conflicts'") assert mergerepo.listall_mergeheads() == [pygit2.Oid(hex=branch_head_hex)] libgit2-pygit2-d86662e/test/test_nonunicode.py000066400000000000000000000041231521317216500213640ustar00rootroot00000000000000# Copyright 2010-2024 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for non unicode byte strings""" import os import shutil import sys import pytest import pygit2 from pygit2 import Repository from . import utils # FIXME Detect the filesystem rather than the operating system works_in_linux = pytest.mark.xfail( sys.platform != 'linux', reason='fails in macOS/Windows, and also in Linux with the FAT filesystem', ) @utils.requires_network @works_in_linux def test_nonunicode_branchname(testrepo: Repository) -> None: folderpath = 'temp_repo_nonutf' if os.path.exists(folderpath): shutil.rmtree(folderpath) newrepo = pygit2.clone_repository( path=folderpath, url='https://github.com/pygit2/test_branch_notutf.git' ) bstring = b'\xc3master' assert bstring in [ (ref.split('/')[-1]).encode('utf8', 'surrogateescape') for ref in newrepo.listall_references() ] # Remote branch among references: 'refs/remotes/origin/\udcc3master' libgit2-pygit2-d86662e/test/test_note.py000066400000000000000000000057641521317216500202040ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for note objects.""" import pytest from pygit2 import Blob, Repository, Signature NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') NOTES = [ ( 'ab533997b80705767be3dae8cbb06a0740809f79', 'First Note - HEAD\n', '784855caf26449a1914d2cf62d12b9374d76ae78', ), ( 'd879714d880671ed84f8aaed8b27fca23ba01f27', 'Second Note - HEAD~1\n', 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87', ), ] def test_create_note(barerepo: Repository) -> None: annotated_id = barerepo.revparse_single('HEAD~3').id author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note_id = barerepo.create_note(NOTE[1], author, committer, str(annotated_id)) assert NOTE[0] == note_id note = barerepo[note_id] assert isinstance(note, Blob) # check the note blob assert NOTE[1].encode() == note.data def test_lookup_note(barerepo: Repository) -> None: annotated_id = str(barerepo.head.target) note = barerepo.lookup_note(annotated_id) assert NOTES[0][0] == note.id assert NOTES[0][1] == note.message def test_remove_note(barerepo: Repository) -> None: head = barerepo.head note = barerepo.lookup_note(str(head.target)) author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note.remove(author, committer) with pytest.raises(KeyError): barerepo.lookup_note(str(head.target)) def test_iterate_notes(barerepo: Repository) -> None: for i, note in enumerate(barerepo.notes()): note_id, message, annotated_id = NOTES[i] assert note_id == note.id assert message == note.message assert annotated_id == note.annotated_id def test_iterate_non_existing_ref(barerepo: Repository) -> None: with pytest.raises(KeyError): barerepo.notes('refs/notes/bad_ref') # type: ignore libgit2-pygit2-d86662e/test/test_object.py000066400000000000000000000112211521317216500204660ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Object objects.""" import pytest from pygit2 import Commit, Object, Oid, Repository, Tag, Tree from pygit2.enums import ObjectType BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_CONTENT = """hello world hola mundo bonjour le monde """.encode() BLOB_NEW_CONTENT = b'foo bar\n' BLOB_FILE_CONTENT = b'bye world\n' def test_equality(testrepo: Repository) -> None: # get a commit object twice and see if it equals ittestrepo commit_id = testrepo.lookup_reference('refs/heads/master').target commit_a = testrepo[commit_id] commit_b = testrepo[commit_id] assert commit_a is not commit_b assert commit_a == commit_b assert not (commit_a != commit_b) def test_hashing(testrepo: Repository) -> None: # get a commit object twice and compare hashes commit_id = testrepo.lookup_reference('refs/heads/master').target commit_a = testrepo[commit_id] commit_b = testrepo[commit_id] assert hash(commit_a) assert commit_a is not commit_b assert commit_a == commit_b # if the commits are equal then their hash *must* be equal # but different objects can have the same commit assert hash(commit_a) == hash(commit_b) # sanity check that python container types work as expected s = set() s.add(commit_a) s.add(commit_b) assert len(s) == 1 assert commit_a in s assert commit_b in s d = {} d[commit_a] = True assert commit_b in d assert d[commit_b] assert commit_b == commit_a def test_peel_commit(testrepo: Repository) -> None: # start by looking up the commit commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] # and peel to the tree tree = commit.peel(ObjectType.TREE) assert type(tree) is Tree assert tree.id == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' def test_peel_commit_type(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] tree = commit.peel(Tree) assert type(tree) is Tree assert tree.id == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' def test_invalid(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] with pytest.raises(ValueError): commit.peel(ObjectType.TAG) def test_invalid_type(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] with pytest.raises(ValueError): commit.peel(Tag) def test_short_id(testrepo: Repository) -> None: seen: dict[str, Oid] = {} # from short_id to full hex id def test_obj(obj: Object | Commit, msg: str) -> None: short_id = obj.short_id msg = msg + f' short_id={short_id}' already = seen.get(short_id) if already: assert already == obj.id else: seen[short_id] = obj.id lookup = testrepo[short_id] assert obj.id == lookup.id for commit in testrepo.walk(testrepo.head.target): test_obj(commit, f'commit#{commit.id}') tree = commit.tree test_obj(tree, f'tree#{tree.id}') for entry in tree: test_obj(testrepo[entry.id], f'entry={entry.name}#{entry.id}') def test_repr(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit_a = testrepo[commit_id] assert repr(commit_a) == '' % commit_id libgit2-pygit2-d86662e/test/test_odb.py000066400000000000000000000064461521317216500200010ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Odb objects.""" # Standard Library import binascii from collections.abc import Generator from pathlib import Path import pytest # pygit2 from pygit2 import Odb, Oid, Repository from pygit2.enums import ObjectType from . import utils BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) BLOB_OID = Oid(raw=BLOB_RAW) BLOB_CONTENTS = b'a contents\n' def test_emptyodb(barerepo: Repository) -> None: odb = Odb() assert len(list(odb)) == 0 assert BLOB_HEX not in odb path = Path(barerepo.path) / 'objects' odb.add_disk_alternate(path) assert BLOB_HEX in odb @pytest.fixture def odb(barerepo: Repository) -> Generator[Odb, None, None]: odb = barerepo.odb yield odb def test_iterable(odb: Odb) -> None: assert BLOB_HEX in odb def test_contains(odb: Odb) -> None: assert BLOB_HEX in odb def test_read(odb: Odb) -> None: with pytest.raises(TypeError): odb.read(123) # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, odb.read, '1' * 40) ab = odb.read(BLOB_OID) a = odb.read(BLOB_HEX) assert ab == a assert (ObjectType.BLOB, BLOB_CONTENTS) == a assert isinstance(a[0], ObjectType) a2 = odb.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') assert (ObjectType.BLOB, b'a contents 2\n') == a2 assert isinstance(a2[0], ObjectType) a_hex_prefix = BLOB_HEX[:4] a3 = odb.read(a_hex_prefix) assert (ObjectType.BLOB, BLOB_CONTENTS) == a3 assert isinstance(a3[0], ObjectType) def test_read_header(odb: Odb) -> None: with pytest.raises(TypeError): odb.read_header(123) # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, odb.read_header, '1' * 40) ab = odb.read_header(BLOB_OID) a = odb.read_header(BLOB_HEX) assert ab == a assert (ObjectType.BLOB, len(BLOB_CONTENTS)) == a assert isinstance(a[0], ObjectType) def test_write(odb: Odb) -> None: data = b'hello world' # invalid object type with pytest.raises(ValueError): odb.write(ObjectType.ANY, data) oid = odb.write(ObjectType.BLOB, data) assert type(oid) is Oid libgit2-pygit2-d86662e/test/test_odb_backend.py000066400000000000000000000115261521317216500214430ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Odb backends.""" # Standard Library import binascii from collections.abc import Generator, Iterator from pathlib import Path import pytest # pygit2 import pygit2 from pygit2 import Odb, Oid, Repository from pygit2.enums import ObjectType from . import utils BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) BLOB_OID = pygit2.Oid(raw=BLOB_RAW) @pytest.fixture def odb_path(barerepo: Repository) -> Generator[tuple[Odb, Path], None, None]: yield barerepo.odb, Path(barerepo.path) / 'objects' def test_pack(odb_path: tuple[Odb, Path]) -> None: odb, path = odb_path pack = pygit2.OdbBackendPack(path) assert len(list(pack)) > 0 for obj in pack: assert obj in odb def test_loose(odb_path: tuple[Odb, Path]) -> None: odb, path = odb_path pack = pygit2.OdbBackendLoose(path, 5, False) assert len(list(pack)) > 0 for obj in pack: assert obj in odb class ProxyBackend(pygit2.OdbBackend): def __init__(self, source: pygit2.OdbBackend | pygit2.OdbBackendPack) -> None: super().__init__() self.source = source def read_cb(self, oid: Oid | str) -> tuple[int, bytes]: return self.source.read(oid) def read_prefix_cb(self, oid: Oid | str) -> tuple[int, bytes, Oid]: return self.source.read_prefix(oid) def read_header_cb(self, oid: Oid | str) -> tuple[int, int]: typ, data = self.source.read(oid) return typ, len(data) def exists_cb(self, oid: Oid | str) -> bool: return self.source.exists(oid) def exists_prefix_cb(self, oid: Oid | str) -> Oid: return self.source.exists_prefix(oid) def refresh_cb(self) -> None: self.source.refresh() def __iter__(self) -> Iterator[Oid]: return iter(self.source) # # Test a custom object backend alone (without adding it to an ODB) # This doesn't make much sense, but it's possible. # @pytest.fixture def proxy(barerepo: Repository) -> Generator[ProxyBackend, None, None]: path = Path(barerepo.path) / 'objects' yield ProxyBackend(pygit2.OdbBackendPack(path)) def test_iterable(proxy: ProxyBackend) -> None: assert BLOB_HEX in [o for o in proxy] def test_read(proxy: ProxyBackend) -> None: with pytest.raises(TypeError): proxy.read(123) # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, proxy.read, '1' * 40) ab = proxy.read(BLOB_OID) a = proxy.read(BLOB_HEX) assert ab == a assert (ObjectType.BLOB, b'a contents\n') == a def test_read_prefix(proxy: ProxyBackend) -> None: a_hex_prefix = BLOB_HEX[:4] a3 = proxy.read_prefix(a_hex_prefix) assert (ObjectType.BLOB, b'a contents\n', BLOB_OID) == a3 def test_exists(proxy: ProxyBackend) -> None: with pytest.raises(TypeError): proxy.exists(123) # type: ignore assert not proxy.exists('1' * 40) assert proxy.exists(BLOB_HEX) def test_exists_prefix(proxy: ProxyBackend) -> None: a_hex_prefix = BLOB_HEX[:4] assert BLOB_HEX == proxy.exists_prefix(a_hex_prefix) # # Test a custom object backend, through a Repository. # @pytest.fixture def repo(barerepo: Repository) -> Generator[Repository, None, None]: odb = pygit2.Odb() path = Path(barerepo.path) / 'objects' backend_org = pygit2.OdbBackendPack(path) backend = ProxyBackend(backend_org) odb.add_backend(backend, 1) repo = pygit2.Repository() repo.set_odb(odb) yield repo def test_repo_read(repo: Repository) -> None: with pytest.raises(TypeError): repo[123] # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, repo.__getitem__, '1' * 40) ab = repo[BLOB_OID] a = repo[BLOB_HEX] assert ab == a libgit2-pygit2-d86662e/test/test_oid.py000066400000000000000000000054241521317216500200030ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Object ids.""" # Standard Library from binascii import unhexlify import pytest from pygit2 import Oid HEX = '15b648aec6ed045b5ca6f57f8b7831a8b4757298' RAW = unhexlify(HEX.encode('ascii')) def test_raw() -> None: oid = Oid(raw=RAW) assert oid.raw == RAW assert oid == HEX def test_hex() -> None: oid = Oid(hex=HEX) assert oid.raw == RAW assert oid == HEX def test_hex_bytes() -> None: hex = bytes(HEX, 'ascii') with pytest.raises(TypeError): Oid(hex=hex) # type: ignore def test_none() -> None: with pytest.raises(ValueError): Oid() def test_both() -> None: with pytest.raises(ValueError): Oid(raw=RAW, hex=HEX) def test_long() -> None: with pytest.raises(ValueError): Oid(raw=RAW + b'a') with pytest.raises(ValueError): Oid(hex=HEX + 'a') def test_cmp() -> None: oid1 = Oid(raw=RAW) # Equal oid2 = Oid(hex=HEX) assert oid1 == oid2 # Not equal oid2 = Oid(hex='15b648aec6ed045b5ca6f57f8b7831a8b4757299') assert oid1 != oid2 # Other assert oid1 < oid2 assert oid1 <= oid2 assert not oid1 == oid2 assert not oid1 > oid2 assert not oid1 >= oid2 def test_hash() -> None: s = set() s.add(Oid(raw=RAW)) s.add(Oid(hex=HEX)) assert len(s) == 1 s.add(Oid(hex='0000000000000000000000000000000000000000')) s.add(Oid(hex='0000000000000000000000000000000000000001')) assert len(s) == 3 def test_bool() -> None: assert Oid(raw=RAW) assert Oid(hex=HEX) assert not Oid(raw=b'') assert not Oid(hex='0000000000000000000000000000000000000000') libgit2-pygit2-d86662e/test/test_options.py000066400000000000000000000203641521317216500207230ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import sys import pytest import pygit2 from pygit2 import option from pygit2.enums import ConfigLevel, ObjectType, Option def __option(getter: Option, setter: Option, value: object) -> None: old_value = option(getter) option(setter, value) assert value == option(getter) # Reset to avoid side effects in later tests option(setter, old_value) def __proxy(name: str, value: object) -> None: old_value = getattr(pygit2.settings, name) setattr(pygit2.settings, name, value) assert value == getattr(pygit2.settings, name) # Reset to avoid side effects in later tests setattr(pygit2.settings, name, old_value) def test_mwindow_size() -> None: __option(Option.GET_MWINDOW_SIZE, Option.SET_MWINDOW_SIZE, 200 * 1024) def test_mwindow_size_proxy() -> None: __proxy('mwindow_size', 300 * 1024) def test_mwindow_mapped_limit_200() -> None: __option( Option.GET_MWINDOW_MAPPED_LIMIT, Option.SET_MWINDOW_MAPPED_LIMIT, 200 * 1024 ) def test_mwindow_mapped_limit_300() -> None: __proxy('mwindow_mapped_limit', 300 * 1024) def test_cache_object_limit() -> None: new_limit = 2 * 1024 option(Option.SET_CACHE_OBJECT_LIMIT, ObjectType.BLOB, new_limit) def test_cache_object_limit_proxy() -> None: new_limit = 4 * 1024 pygit2.settings.cache_object_limit(ObjectType.BLOB, new_limit) def test_cached_memory() -> None: value = option(Option.GET_CACHED_MEMORY) assert value[1] == 256 * 1024**2 def test_cached_memory_proxy() -> None: assert pygit2.settings.cached_memory[1] == 256 * 1024**2 def test_enable_caching() -> None: pygit2.settings.enable_caching(False) pygit2.settings.enable_caching(True) # Lower level API option(Option.ENABLE_CACHING, False) option(Option.ENABLE_CACHING, True) def test_disable_pack_keep_file_checks() -> None: pygit2.settings.disable_pack_keep_file_checks(False) pygit2.settings.disable_pack_keep_file_checks(True) # Lower level API option(Option.DISABLE_PACK_KEEP_FILE_CHECKS, False) option(Option.DISABLE_PACK_KEEP_FILE_CHECKS, True) def test_cache_max_size_proxy() -> None: pygit2.settings.cache_max_size(128 * 1024**2) assert pygit2.settings.cached_memory[1] == 128 * 1024**2 pygit2.settings.cache_max_size(256 * 1024**2) assert pygit2.settings.cached_memory[1] == 256 * 1024**2 def test_search_path() -> None: paths = [ (ConfigLevel.GLOBAL, '/tmp/global'), (ConfigLevel.XDG, '/tmp/xdg'), (ConfigLevel.SYSTEM, '/tmp/etc'), ] for level, path in paths: option(Option.SET_SEARCH_PATH, level, path) assert path == option(Option.GET_SEARCH_PATH, level) def test_search_path_proxy() -> None: paths = [ (ConfigLevel.GLOBAL, '/tmp2/global'), (ConfigLevel.XDG, '/tmp2/xdg'), (ConfigLevel.SYSTEM, '/tmp2/etc'), ] for level, path in paths: pygit2.settings.search_path[level] = path assert path == pygit2.settings.search_path[level] def test_owner_validation() -> None: __option(Option.GET_OWNER_VALIDATION, Option.SET_OWNER_VALIDATION, 0) def test_template_path() -> None: original_path = option(Option.GET_TEMPLATE_PATH) test_path = '/tmp/test_templates' option(Option.SET_TEMPLATE_PATH, test_path) assert option(Option.GET_TEMPLATE_PATH) == test_path if original_path: option(Option.SET_TEMPLATE_PATH, original_path) else: option(Option.SET_TEMPLATE_PATH, None) def test_user_agent() -> None: original_agent = option(Option.GET_USER_AGENT) test_agent = 'test-agent/1.0' option(Option.SET_USER_AGENT, test_agent) assert option(Option.GET_USER_AGENT) == test_agent if original_agent: option(Option.SET_USER_AGENT, original_agent) def test_pack_max_objects() -> None: __option(Option.GET_PACK_MAX_OBJECTS, Option.SET_PACK_MAX_OBJECTS, 100000) @pytest.mark.skipif(sys.platform != 'win32', reason='Windows-specific feature') def test_windows_sharemode() -> None: __option(Option.GET_WINDOWS_SHAREMODE, Option.SET_WINDOWS_SHAREMODE, 1) def test_ssl_ciphers() -> None: # Setting SSL ciphers (no getter available) try: option(Option.SET_SSL_CIPHERS, 'DEFAULT') except pygit2.GitError as e: if "TLS backend doesn't support custom ciphers" in str(e): pytest.skip(str(e)) raise def test_enable_http_expect_continue() -> None: option(Option.ENABLE_HTTP_EXPECT_CONTINUE, True) option(Option.ENABLE_HTTP_EXPECT_CONTINUE, False) def test_odb_priorities() -> None: option(Option.SET_ODB_PACKED_PRIORITY, 1) option(Option.SET_ODB_LOOSE_PRIORITY, 2) def test_extensions() -> None: original_extensions = option(Option.GET_EXTENSIONS) assert isinstance(original_extensions, list) test_extensions = ['objectformat', 'worktreeconfig'] option(Option.SET_EXTENSIONS, test_extensions, len(test_extensions)) new_extensions = option(Option.GET_EXTENSIONS) assert isinstance(new_extensions, list) # Note: libgit2 may add its own built-in extensions and sort them for ext in test_extensions: assert ext in new_extensions, f"Extension '{ext}' not found in {new_extensions}" option(Option.SET_EXTENSIONS, [], 0) empty_extensions = option(Option.GET_EXTENSIONS) assert isinstance(empty_extensions, list) custom_extensions = ['myextension', 'objectformat'] option(Option.SET_EXTENSIONS, custom_extensions, len(custom_extensions)) custom_result = option(Option.GET_EXTENSIONS) assert 'myextension' in custom_result assert 'objectformat' in custom_result if original_extensions: option(Option.SET_EXTENSIONS, original_extensions, len(original_extensions)) else: option(Option.SET_EXTENSIONS, [], 0) final_extensions = option(Option.GET_EXTENSIONS) assert set(final_extensions) == set(original_extensions) def test_homedir() -> None: original_homedir = option(Option.GET_HOMEDIR) test_homedir = '/tmp/test_home' option(Option.SET_HOMEDIR, test_homedir) assert option(Option.GET_HOMEDIR) == test_homedir if original_homedir: option(Option.SET_HOMEDIR, original_homedir) else: option(Option.SET_HOMEDIR, None) def test_server_timeouts() -> None: original_connect = option(Option.GET_SERVER_CONNECT_TIMEOUT) option(Option.SET_SERVER_CONNECT_TIMEOUT, 5000) assert option(Option.GET_SERVER_CONNECT_TIMEOUT) == 5000 option(Option.SET_SERVER_CONNECT_TIMEOUT, original_connect) original_timeout = option(Option.GET_SERVER_TIMEOUT) option(Option.SET_SERVER_TIMEOUT, 10000) assert option(Option.GET_SERVER_TIMEOUT) == 10000 option(Option.SET_SERVER_TIMEOUT, original_timeout) def test_user_agent_product() -> None: original_product = option(Option.GET_USER_AGENT_PRODUCT) test_product = 'test-product' option(Option.SET_USER_AGENT_PRODUCT, test_product) assert option(Option.GET_USER_AGENT_PRODUCT) == test_product if original_product: option(Option.SET_USER_AGENT_PRODUCT, original_product) def test_mwindow_file_limit() -> None: __option(Option.GET_MWINDOW_FILE_LIMIT, Option.SET_MWINDOW_FILE_LIMIT, 100) libgit2-pygit2-d86662e/test/test_packbuilder.py000066400000000000000000000105541521317216500215150ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Index files.""" from collections.abc import Callable from pathlib import Path import pygit2 from pygit2 import Oid, PackBuilder, Repository from . import utils def test_create_packbuilder(testrepo: Repository) -> None: # simple test of PackBuilder creation packbuilder = PackBuilder(testrepo) assert len(packbuilder) == 0 def test_add(testrepo: Repository) -> None: # Add a few objects and confirm that the count is correct packbuilder = PackBuilder(testrepo) objects_to_add = [obj for obj in testrepo] packbuilder.add(objects_to_add[0]) assert len(packbuilder) == 1 packbuilder.add(objects_to_add[1]) assert len(packbuilder) == 2 def test_add_recursively(testrepo: Repository) -> None: # Add the head object and referenced objects recursively and confirm that the count is correct packbuilder = PackBuilder(testrepo) assert isinstance(testrepo.head.target, Oid) packbuilder.add_recur(testrepo.head.target) # expect a count of 4 made up of the following referenced objects: # Commit # Tree # Blob: hello.txt # Blob: .gitignore assert len(packbuilder) == 4 def test_repo_pack(testrepo: Repository, tmp_path: Path) -> None: # pack the repo with the default strategy confirm_same_repo_after_packing(testrepo, tmp_path, None) def test_pack_with_delegate(testrepo: Repository, tmp_path: Path) -> None: # loop through all branches and add each commit to the packbuilder def pack_delegate(pb: PackBuilder) -> None: for branch in pb._repo.branches: br = pb._repo.branches.get(branch) for commit in br.log(): pb.add_recur(commit.oid_new) confirm_same_repo_after_packing(testrepo, tmp_path, pack_delegate) def setup_second_repo(tmp_path: Path) -> Repository: # helper method to set up a second repo for comparison tmp_path_2 = tmp_path / 'test_repo2' with utils.TemporaryRepository('testrepo.zip', tmp_path_2) as path: testrepo = pygit2.Repository(path) return testrepo def confirm_same_repo_after_packing( testrepo: Repository, tmp_path: Path, pack_delegate: Callable[[PackBuilder], None] | None, ) -> None: # Helper method to confirm the contents of two repos before and after packing pack_repo = setup_second_repo(tmp_path) pack_repo_path = Path(pack_repo.path) objects_dir = pack_repo_path / 'objects' utils.rmtree(objects_dir) pack_path = objects_dir / 'pack' pack_path.mkdir(parents=True) # assert that the number of written objects is the same as the number of objects in the repo written_objects = testrepo.pack(pack_path, pack_delegate=pack_delegate) assert written_objects == len([obj for obj in testrepo]) # assert that the number of objects in the pack repo is the same as the original repo orig_objects = [obj for obj in testrepo.odb] packed_objects = [obj for obj in pack_repo.odb] assert len(packed_objects) == len(orig_objects) # assert that the objects in the packed repo are the same objects as the original repo for i, obj in enumerate(orig_objects): assert pack_repo[obj].type == testrepo[obj].type assert pack_repo[obj].read_raw() == testrepo[obj].read_raw() libgit2-pygit2-d86662e/test/test_patch.py000066400000000000000000000170611521317216500203270ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import pytest import pygit2 from pygit2 import Blob, Repository BLOB_OLD_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_NEW_SHA = '3b18e512dba79e4c8300dd08aeb37f8e728b8dad' BLOB_OLD_CONTENT = b"""hello world hola mundo bonjour le monde """ BLOB_NEW_CONTENT = b'foo bar\n' BLOB_OLD_PATH = 'a/file' BLOB_NEW_PATH = 'b/file' BLOB_PATCH2 = """diff --git a/a/file b/b/file index a520c24..3b18e51 100644 --- a/a/file +++ b/b/file @@ -1,3 +1 @@ hello world -hola mundo -bonjour le monde """ BLOB_PATCH = """diff --git a/a/file b/b/file index a520c24..d675fa4 100644 --- a/a/file +++ b/b/file @@ -1,3 +1 @@ -hello world -hola mundo -bonjour le monde +foo bar """ BLOB_PATCH_ADDED = """diff --git a/a/file b/b/file new file mode 100644 index 0000000..d675fa4 --- /dev/null +++ b/b/file @@ -0,0 +1 @@ +foo bar """ BLOB_PATCH_DELETED = """diff --git a/a/file b/b/file deleted file mode 100644 index a520c24..0000000 --- a/a/file +++ /dev/null @@ -1,3 +0,0 @@ -hello world -hola mundo -bonjour le monde """ def test_patch_create_from_buffers() -> None: patch = pygit2.Patch.create_from( BLOB_OLD_CONTENT, BLOB_NEW_CONTENT, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH def test_patch_create_from_blobs(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] new_blob = testrepo[BLOB_NEW_SHA] assert isinstance(old_blob, Blob) assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, new_blob, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH2 def test_patch_create_from_blob_buffer(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] assert isinstance(old_blob, Blob) patch = pygit2.Patch.create_from( old_blob, BLOB_NEW_CONTENT, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH def test_patch_create_from_blob_buffer_add(testrepo: Repository) -> None: patch = pygit2.Patch.create_from( None, BLOB_NEW_CONTENT, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH_ADDED def test_patch_create_from_blob_buffer_delete(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] assert isinstance(old_blob, Blob) patch = pygit2.Patch.create_from( old_blob, None, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH_DELETED def test_patch_create_from_bad_old_type_arg(testrepo: Repository) -> None: with pytest.raises(TypeError): pygit2.Patch.create_from(testrepo, BLOB_NEW_CONTENT) # type: ignore def test_patch_create_from_bad_new_type_arg(testrepo: Repository) -> None: with pytest.raises(TypeError): pygit2.Patch.create_from(None, testrepo) # type: ignore def test_context_lines(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] new_blob = testrepo[BLOB_NEW_SHA] assert isinstance(old_blob, Blob) assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, new_blob, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text is not None context_count = len( [line for line in patch.text.splitlines() if line.startswith(' ')] ) assert context_count != 0 def test_no_context_lines(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] new_blob = testrepo[BLOB_NEW_SHA] assert isinstance(old_blob, Blob) assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, new_blob, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, context_lines=0, ) assert patch.text is not None context_count = len( [line for line in patch.text.splitlines() if line.startswith(' ')] ) assert context_count == 0 def test_patch_create_blob_blobs(testrepo: Repository) -> None: old_blob = testrepo[testrepo.create_blob(BLOB_OLD_CONTENT)] new_blob = testrepo[testrepo.create_blob(BLOB_NEW_CONTENT)] assert isinstance(old_blob, Blob) assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, new_blob, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH def test_patch_create_blob_buffer(testrepo: Repository) -> None: blob = testrepo[testrepo.create_blob(BLOB_OLD_CONTENT)] assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( blob, BLOB_NEW_CONTENT, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH def test_patch_create_blob_delete(testrepo: Repository) -> None: blob = testrepo[testrepo.create_blob(BLOB_OLD_CONTENT)] assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( blob, None, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH_DELETED def test_patch_create_blob_add(testrepo: Repository) -> None: blob = testrepo[testrepo.create_blob(BLOB_NEW_CONTENT)] assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( None, blob, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) assert patch.text == BLOB_PATCH_ADDED def test_patch_delete_blob(testrepo: Repository) -> None: blob = testrepo[BLOB_OLD_SHA] assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( blob, None, old_as_path=BLOB_OLD_PATH, new_as_path=BLOB_NEW_PATH, ) # Make sure that even after deleting the blob the patch still has the # necessary references to generate its patch del blob assert patch.text == BLOB_PATCH_DELETED def test_patch_multi_blob(testrepo: Repository) -> None: blob = testrepo[BLOB_OLD_SHA] assert isinstance(blob, Blob) patch = pygit2.Patch.create_from(blob, None) patch_text = patch.text blob = testrepo[BLOB_OLD_SHA] assert isinstance(blob, Blob) patch2 = pygit2.Patch.create_from(blob, None) patch_text2 = patch.text assert patch_text == patch_text2 assert patch_text == patch.text assert patch_text2 == patch2.text assert patch.text == patch2.text libgit2-pygit2-d86662e/test/test_patch_encoding.py000066400000000000000000000054211521317216500221720ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import pygit2 from pygit2 import Blob, Repository expected_diff = b"""diff --git a/iso-8859-1.txt b/iso-8859-1.txt index e84e339..201e0c9 100644 --- a/iso-8859-1.txt +++ b/iso-8859-1.txt @@ -1 +1,2 @@ Kristian H\xf8gsberg +foo """ def test_patch_from_non_utf8() -> None: # blobs encoded in ISO-8859-1 old_content = b'Kristian H\xf8gsberg\n' new_content = old_content + b'foo\n' patch = pygit2.Patch.create_from( old_content, new_content, old_as_path='iso-8859-1.txt', new_as_path='iso-8859-1.txt', ) assert patch.data == expected_diff assert patch.text == expected_diff.decode('utf-8', errors='replace') # `patch.text` corrupted the ISO-8859-1 content as it forced UTF-8 # decoding, so assert that we cannot get the original content back: assert patch.text.encode('utf-8') != expected_diff def test_patch_create_from_blobs(encodingrepo: Repository) -> None: old_content = encodingrepo['e84e339ac7fcc823106efa65a6972d7a20016c85'] new_content = encodingrepo['201e0c908e3d9f526659df3e556c3d06384ef0df'] assert isinstance(old_content, Blob) assert isinstance(new_content, Blob) patch = pygit2.Patch.create_from( old_content, new_content, old_as_path='iso-8859-1.txt', new_as_path='iso-8859-1.txt', ) assert patch.data == expected_diff assert patch.text == expected_diff.decode('utf-8', errors='replace') # `patch.text` corrupted the ISO-8859-1 content as it forced UTF-8 # decoding, so assert that we cannot get the original content back: assert patch.text.encode('utf-8') != expected_diff libgit2-pygit2-d86662e/test/test_refdb_backend.py000066400000000000000000000115261521317216500217610ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Refdb objects.""" from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Commit, Oid, Reference, Repository, Signature # Note: the refdb abstraction from libgit2 is meant to provide information # which libgit2 transforms into something more useful, and in general YMMV by # using the backend directly. So some of these tests are a bit vague or # incomplete, to avoid hitting the semi-valid states that refdbs produce by # design. class ProxyRefdbBackend(pygit2.RefdbBackend): def __init__(self, source: pygit2.RefdbBackend) -> None: self.source = source def exists(self, ref: str) -> bool: return self.source.exists(ref) def lookup(self, ref: str) -> Reference: return self.source.lookup(ref) def write( self, ref: Reference, force: bool, who: Signature, message: str, old: None | str | Oid, old_target: None | str, ) -> None: return self.source.write(ref, force, who, message, old, old_target) def rename( self, old_name: str, new_name: str, force: bool, who: Signature, message: str ) -> Reference: return self.source.rename(old_name, new_name, force, who, message) def delete(self, ref_name: str, old_id: Oid | str, old_target: str | None) -> None: return self.source.delete(ref_name, old_id, old_target) def compress(self) -> None: return self.source.compress() def has_log(self, ref_name: str) -> bool: return self.source.has_log(ref_name) def ensure_log(self, ref_name: str) -> bool: return self.source.ensure_log(ref_name) @pytest.fixture def repo(testrepo: Repository) -> Generator[Repository, None, None]: testrepo.backend = ProxyRefdbBackend(pygit2.RefdbFsBackend(testrepo)) yield testrepo def test_exists(repo: Repository) -> None: assert not repo.backend.exists('refs/heads/does-not-exist') assert repo.backend.exists('refs/heads/master') def test_lookup(repo: Repository) -> None: assert repo.backend.lookup('refs/heads/does-not-exist') is None assert repo.backend.lookup('refs/heads/master').name == 'refs/heads/master' def test_write(repo: Repository) -> None: master = repo.backend.lookup('refs/heads/master') commit = repo[master.target] ref = pygit2.Reference('refs/heads/test-write', master.target, None) repo.backend.write(ref, False, commit.author, 'Create test-write', None, None) assert repo.backend.lookup('refs/heads/test-write').target == master.target def test_rename(repo: Repository) -> None: old_ref = repo.backend.lookup('refs/heads/i18n') target = repo.get(old_ref.target) assert isinstance(target, Commit) repo.backend.rename( 'refs/heads/i18n', 'refs/heads/intl', False, target.committer, target.message ) assert repo.backend.lookup('refs/heads/intl').target == target.id def test_delete(repo: Repository) -> None: old = repo.backend.lookup('refs/heads/i18n') repo.backend.delete('refs/heads/i18n', old.target, None) assert not repo.backend.lookup('refs/heads/i18n') def test_compress(repo: Repository) -> None: repo = repo packed_refs_file = Path(repo.path) / 'packed-refs' assert not packed_refs_file.exists() repo.backend.compress() assert packed_refs_file.exists() def test_has_log(repo: Repository) -> None: assert repo.backend.has_log('refs/heads/master') assert not repo.backend.has_log('refs/heads/does-not-exist') def test_ensure_log(repo: Repository) -> None: assert not repo.backend.has_log('refs/heads/new-log') repo.backend.ensure_log('refs/heads/new-log') assert repo.backend.has_log('refs/heads/new-log') libgit2-pygit2-d86662e/test/test_refs.py000066400000000000000000000663261521317216500201770ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for reference objects.""" from pathlib import Path import pytest from pygit2 import ( AlreadyExistsError, Commit, GitError, InvalidSpecError, Oid, Reference, Repository, Signature, Tree, reference_is_valid_name, ) from pygit2.enums import ReferenceFilter, ReferenceType LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' def test_refs_list_objects(testrepo: Repository) -> None: refs = [(ref.name, ref.target) for ref in testrepo.references.objects] assert sorted(refs) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] def test_refs_list(testrepo: Repository) -> None: # Without argument assert sorted(testrepo.references) == ['refs/heads/i18n', 'refs/heads/master'] # We add a symbolic reference testrepo.create_reference('refs/tags/version1', 'refs/heads/master') assert sorted(testrepo.references) == [ 'refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1', ] def test_head(testrepo: Repository) -> None: head = testrepo.head assert LAST_COMMIT == testrepo[head.target].id assert not isinstance(head.raw_target, bytes) assert LAST_COMMIT == testrepo[head.raw_target].id def test_refs_getitem(testrepo: Repository) -> None: refname = 'refs/foo' # Raise KeyError ? with pytest.raises(KeyError): testrepo.references[refname] # Return None ? assert testrepo.references.get(refname) is None # Test a lookup reference = testrepo.references.get('refs/heads/master') assert reference is not None assert reference.name == 'refs/heads/master' def test_refs_get_sha(testrepo: Repository) -> None: reference = testrepo.references['refs/heads/master'] assert reference is not None assert reference.target == LAST_COMMIT def test_refs_set_sha(testrepo: Repository) -> None: NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = testrepo.references.get('refs/heads/master') assert reference is not None reference.set_target(NEW_COMMIT) assert reference.target == NEW_COMMIT def test_refs_set_sha_prefix(testrepo: Repository) -> None: NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = testrepo.references.get('refs/heads/master') assert reference is not None reference.set_target(NEW_COMMIT[0:6]) assert reference.target == NEW_COMMIT def test_refs_get_type(testrepo: Repository) -> None: reference = testrepo.references.get('refs/heads/master') assert reference is not None assert reference.type == ReferenceType.DIRECT def test_refs_get_target(testrepo: Repository) -> None: reference = testrepo.references.get('HEAD') assert reference is not None assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' def test_refs_set_target(testrepo: Repository) -> None: reference = testrepo.references.get('HEAD') assert reference is not None assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' reference.set_target('refs/heads/i18n') assert reference.target == 'refs/heads/i18n' assert reference.raw_target == b'refs/heads/i18n' def test_refs_get_shorthand(testrepo: Repository) -> None: reference = testrepo.references.get('refs/heads/master') assert reference is not None assert reference.shorthand == 'master' reference = testrepo.references.create('refs/remotes/origin/master', LAST_COMMIT) assert reference.shorthand == 'origin/master' def test_refs_set_target_with_message(testrepo: Repository) -> None: reference = testrepo.references.get('HEAD') assert reference is not None assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' sig = Signature('foo', 'bar') testrepo.set_ident('foo', 'bar') msg = 'Hello log' reference.set_target('refs/heads/i18n', message=msg) assert reference.target == 'refs/heads/i18n' assert reference.raw_target == b'refs/heads/i18n' first = list(reference.log())[0] assert first.message == msg assert first.committer == sig def test_refs_delete(testrepo: Repository) -> None: # We add a tag as a new reference that points to "origin/master" reference = testrepo.references.create('refs/tags/version1', LAST_COMMIT) assert 'refs/tags/version1' in testrepo.references # And we delete it reference.delete() assert 'refs/tags/version1' not in testrepo.references # Access the deleted reference with pytest.raises(GitError): getattr(reference, 'name') with pytest.raises(GitError): getattr(reference, 'type') with pytest.raises(GitError): getattr(reference, 'target') with pytest.raises(GitError): reference.delete() with pytest.raises(GitError): reference.resolve() with pytest.raises(GitError): reference.rename('refs/tags/version2') def test_refs_rename(testrepo: Repository) -> None: # We add a tag as a new reference that points to "origin/master" reference = testrepo.references.create('refs/tags/version1', LAST_COMMIT) assert reference.name == 'refs/tags/version1' reference.rename('refs/tags/version2') assert reference.name == 'refs/tags/version2' with pytest.raises(AlreadyExistsError): reference.rename('refs/tags/version2') with pytest.raises(InvalidSpecError): reference.rename('b1') # def test_reload(testrepo: Repository) -> None: # name = 'refs/tags/version1' # ref = testrepo.create_reference(name, "refs/heads/master", symbolic=True) # ref2 = testrepo.lookup_reference(name) # ref.delete() # assert ref2.name == name # with pytest.raises(KeyError): ref2.reload() # with pytest.raises(GitError): getattr(ref2, 'name') def test_refs_resolve(testrepo: Repository) -> None: reference = testrepo.references.get('HEAD') assert reference is not None assert reference.type == ReferenceType.SYMBOLIC reference = reference.resolve() assert reference.type == ReferenceType.DIRECT assert reference.target == LAST_COMMIT def test_refs_resolve_identity(testrepo: Repository) -> None: head = testrepo.references.get('HEAD') assert head is not None ref = head.resolve() assert ref.resolve() is ref def test_refs_create(testrepo: Repository) -> None: # We add a tag as a new reference that points to "origin/master" reference: Reference | None = testrepo.references.create( 'refs/tags/version1', LAST_COMMIT ) refs = testrepo.references assert 'refs/tags/version1' in refs reference = testrepo.references.get('refs/tags/version1') assert reference is not None assert reference.target == LAST_COMMIT # try to create existing reference with pytest.raises(ValueError): testrepo.references.create('refs/tags/version1', LAST_COMMIT) # try to create existing reference with force reference = testrepo.references.create( 'refs/tags/version1', LAST_COMMIT, force=True ) assert reference.target == LAST_COMMIT def test_refs_create_symbolic(testrepo: Repository) -> None: # We add a tag as a new symbolic reference that always points to # "refs/heads/master" reference = testrepo.references.create('refs/tags/beta', 'refs/heads/master') assert reference.type == ReferenceType.SYMBOLIC assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' # try to create existing symbolic reference with pytest.raises(ValueError): testrepo.references.create('refs/tags/beta', 'refs/heads/master') # try to create existing symbolic reference with force reference = testrepo.references.create( 'refs/tags/beta', 'refs/heads/master', force=True ) assert reference.type == ReferenceType.SYMBOLIC assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' # def test_packall_references(testrepo: Repository) -> None: # testrepo.packall_references() def test_refs_peel(testrepo: Repository) -> None: ref = testrepo.references.get('refs/heads/master') assert ref is not None assert testrepo[ref.target].id == ref.peel().id assert not isinstance(ref.raw_target, bytes) assert testrepo[ref.raw_target].id == ref.peel().id commit = ref.peel(Commit) assert commit.tree.id == ref.peel(Tree).id def test_refs_equality(testrepo: Repository) -> None: ref1 = testrepo.references.get('refs/heads/master') ref2 = testrepo.references.get('refs/heads/master') ref3 = testrepo.references.get('refs/heads/i18n') assert ref1 is not ref2 assert ref1 == ref2 assert not ref1 != ref2 assert ref1 != ref3 assert not ref1 == ref3 def test_refs_compress(testrepo: Repository) -> None: packed_refs_file = Path(testrepo.path) / 'packed-refs' assert not packed_refs_file.exists() old_refs = [(ref.name, ref.target) for ref in testrepo.references.objects] testrepo.references.compress() assert packed_refs_file.exists() new_refs = [(x.name, x.target) for x in testrepo.references.objects] assert old_refs == new_refs # # Low level API written in C, repo.references call these. # def test_list_all_reference_objects(testrepo: Repository) -> None: repo = testrepo refs = [(ref.name, ref.target) for ref in repo.listall_reference_objects()] assert sorted(refs) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] def test_list_all_references(testrepo: Repository) -> None: repo = testrepo # Without argument assert sorted(repo.listall_references()) == ['refs/heads/i18n', 'refs/heads/master'] assert sorted(repo.raw_listall_references()) == [ b'refs/heads/i18n', b'refs/heads/master', ] # We add a symbolic reference repo.create_reference('refs/tags/version1', 'refs/heads/master') assert sorted(repo.listall_references()) == [ 'refs/heads/i18n', 'refs/heads/master', 'refs/tags/version1', ] assert sorted(repo.raw_listall_references()) == [ b'refs/heads/i18n', b'refs/heads/master', b'refs/tags/version1', ] def test_references_iterator_init(testrepo: Repository) -> None: repo = testrepo iter = repo.references_iterator_init() assert iter.__class__.__name__ == 'RefsIterator' def test_references_iterator_next(testrepo: Repository) -> None: repo = testrepo repo.create_reference( 'refs/tags/version1', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' ) repo.create_reference( 'refs/tags/version2', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' ) iter_all = repo.references_iterator_init() all_refs = [] for _ in range(4): curr_ref = repo.references_iterator_next(iter_all) if curr_ref: all_refs.append((curr_ref.name, curr_ref.target)) assert sorted(all_refs) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ('refs/tags/version1', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ('refs/tags/version2', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] iter_branches = repo.references_iterator_init() all_branches = [] for _ in range(4): curr_ref = repo.references_iterator_next( iter_branches, ReferenceFilter.BRANCHES ) if curr_ref: all_branches.append((curr_ref.name, curr_ref.target)) assert sorted(all_branches) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] iter_tags = repo.references_iterator_init() all_tags = [] for _ in range(4): curr_ref = repo.references_iterator_next(iter_tags, ReferenceFilter.TAGS) if curr_ref: all_tags.append((curr_ref.name, curr_ref.target)) assert sorted(all_tags) == [ ('refs/tags/version1', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ('refs/tags/version2', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] def test_references_iterator_next_python(testrepo: Repository) -> None: repo = testrepo repo.create_reference( 'refs/tags/version1', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' ) repo.create_reference( 'refs/tags/version2', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' ) refs = [(x.name, x.target) for x in repo.references.iterator()] assert sorted(refs) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ('refs/tags/version1', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ('refs/tags/version2', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] branches = [ (x.name, x.target) for x in repo.references.iterator(ReferenceFilter.BRANCHES) ] assert sorted(branches) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] tags = [(x.name, x.target) for x in repo.references.iterator(ReferenceFilter.TAGS)] assert sorted(tags) == [ ('refs/tags/version1', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ('refs/tags/version2', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] def test_references_iterator_invalid_filter(testrepo: Repository) -> None: repo = testrepo iter_all = repo.references_iterator_init() all_refs = [] for _ in range(4): curr_ref = repo.references_iterator_next(iter_all, 5) # type: ignore if curr_ref: all_refs.append((curr_ref.name, curr_ref.target)) assert all_refs == [] def test_references_iterator_invalid_filter_python(testrepo: Repository) -> None: repo = testrepo refs = [] with pytest.raises(ValueError): for ref in repo.references.iterator(5): # type: ignore refs.append((ref.name, ref.target)) def test_lookup_reference(testrepo: Repository) -> None: repo = testrepo # Raise KeyError ? with pytest.raises(KeyError): repo.lookup_reference('refs/foo') # Test a lookup reference = repo.lookup_reference('refs/heads/master') assert reference.name == 'refs/heads/master' def test_lookup_reference_dwim(testrepo: Repository) -> None: repo = testrepo # remote ref reference = testrepo.create_reference('refs/remotes/origin/master', LAST_COMMIT) assert reference.shorthand == 'origin/master' # tag repo.create_reference('refs/tags/version1', LAST_COMMIT) # Test dwim lookups # Raise KeyError ? with pytest.raises(KeyError): repo.lookup_reference_dwim('foo') with pytest.raises(KeyError): repo.lookup_reference_dwim('refs/foo') reference = repo.lookup_reference_dwim('refs/heads/master') assert reference.name == 'refs/heads/master' reference = repo.lookup_reference_dwim('master') assert reference.name == 'refs/heads/master' reference = repo.lookup_reference_dwim('origin/master') assert reference.name == 'refs/remotes/origin/master' reference = repo.lookup_reference_dwim('version1') assert reference.name == 'refs/tags/version1' def test_resolve_refish(testrepo: Repository) -> None: repo = testrepo # remote ref reference = testrepo.create_reference('refs/remotes/origin/master', LAST_COMMIT) assert reference.shorthand == 'origin/master' # tag repo.create_reference('refs/tags/version1', LAST_COMMIT) # Test dwim lookups # Raise KeyError ? with pytest.raises(KeyError): repo.resolve_refish('foo') with pytest.raises(KeyError): repo.resolve_refish('refs/foo') commit, ref = repo.resolve_refish('refs/heads/i18n') assert ref.name == 'refs/heads/i18n' assert commit.id == '5470a671a80ac3789f1a6a8cefbcf43ce7af0563' commit, ref = repo.resolve_refish('master') assert ref.name == 'refs/heads/master' assert commit.id == LAST_COMMIT commit, ref = repo.resolve_refish('origin/master') assert ref.name == 'refs/remotes/origin/master' assert commit.id == LAST_COMMIT commit, ref = repo.resolve_refish('version1') assert ref.name == 'refs/tags/version1' assert commit.id == LAST_COMMIT commit, ref = repo.resolve_refish(LAST_COMMIT) assert ref is None assert commit.id == LAST_COMMIT commit, ref = repo.resolve_refish('HEAD~1') assert ref is None assert commit.id == '5ebeeebb320790caf276b9fc8b24546d63316533' def test_reference_get_sha(testrepo: Repository) -> None: reference = testrepo.lookup_reference('refs/heads/master') assert reference.target == LAST_COMMIT def test_reference_set_sha(testrepo: Repository) -> None: NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = testrepo.lookup_reference('refs/heads/master') reference.set_target(NEW_COMMIT) assert reference.target == NEW_COMMIT def test_reference_set_sha_prefix(testrepo: Repository) -> None: NEW_COMMIT = '5ebeeebb320790caf276b9fc8b24546d63316533' reference = testrepo.lookup_reference('refs/heads/master') reference.set_target(NEW_COMMIT[0:6]) assert reference.target == NEW_COMMIT def test_reference_get_type(testrepo: Repository) -> None: reference = testrepo.lookup_reference('refs/heads/master') assert reference.type == ReferenceType.DIRECT def test_get_target(testrepo: Repository) -> None: reference = testrepo.lookup_reference('HEAD') assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' def test_set_target(testrepo: Repository) -> None: reference = testrepo.lookup_reference('HEAD') assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' reference.set_target('refs/heads/i18n') assert reference.target == 'refs/heads/i18n' assert reference.raw_target == b'refs/heads/i18n' def test_get_shorthand(testrepo: Repository) -> None: reference = testrepo.lookup_reference('refs/heads/master') assert reference.shorthand == 'master' reference = testrepo.create_reference('refs/remotes/origin/master', LAST_COMMIT) assert reference.shorthand == 'origin/master' def test_set_target_with_message(testrepo: Repository) -> None: reference = testrepo.lookup_reference('HEAD') assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' sig = Signature('foo', 'bar') testrepo.set_ident('foo', 'bar') msg = 'Hello log' reference.set_target('refs/heads/i18n', message=msg) assert reference.target == 'refs/heads/i18n' assert reference.raw_target == b'refs/heads/i18n' first = list(reference.log())[0] assert first.message == msg # Signature.time and Signature.encoding may not be equal. # Here we only care that the name and email are correctly set. assert first.committer.name == sig.name assert first.committer.email == sig.email def test_delete(testrepo: Repository) -> None: repo = testrepo # We add a tag as a new reference that points to "origin/master" reference = repo.create_reference('refs/tags/version1', LAST_COMMIT) assert 'refs/tags/version1' in repo.listall_references() assert b'refs/tags/version1' in repo.raw_listall_references() # And we delete it reference.delete() assert 'refs/tags/version1' not in repo.listall_references() assert b'refs/tags/version1' not in repo.raw_listall_references() # Access the deleted reference with pytest.raises(GitError): getattr(reference, 'name') with pytest.raises(GitError): getattr(reference, 'type') with pytest.raises(GitError): getattr(reference, 'target') with pytest.raises(GitError): reference.delete() with pytest.raises(GitError): reference.resolve() with pytest.raises(GitError): reference.rename('refs/tags/version2') def test_rename(testrepo: Repository) -> None: # We add a tag as a new reference that points to "origin/master" reference = testrepo.create_reference('refs/tags/version1', LAST_COMMIT) assert reference.name == 'refs/tags/version1' reference.rename('refs/tags/version2') assert reference.name == 'refs/tags/version2' # def test_reload(testrepo: Repository) -> None: # name = 'refs/tags/version1' # repo = testrepo # ref = repo.create_reference(name, "refs/heads/master", symbolic=True) # ref2 = repo.lookup_reference(name) # ref.delete() # assert ref2.name == name # with pytest.raises(KeyError): ref2.reload() # with pytest.raises(GitError): getattr(ref2, 'name') def test_reference_resolve(testrepo: Repository) -> None: reference = testrepo.lookup_reference('HEAD') assert reference.type == ReferenceType.SYMBOLIC reference = reference.resolve() assert reference.type == ReferenceType.DIRECT assert reference.target == LAST_COMMIT def test_reference_resolve_identity(testrepo: Repository) -> None: head = testrepo.lookup_reference('HEAD') ref = head.resolve() assert ref.resolve() is ref def test_create_reference(testrepo: Repository) -> None: # We add a tag as a new reference that points to "origin/master" reference = testrepo.create_reference('refs/tags/version1', LAST_COMMIT) assert 'refs/tags/version1' in testrepo.listall_references() assert b'refs/tags/version1' in testrepo.raw_listall_references() reference = testrepo.lookup_reference('refs/tags/version1') assert reference.target == LAST_COMMIT # try to create existing reference with pytest.raises(AlreadyExistsError) as error: testrepo.create_reference('refs/tags/version1', LAST_COMMIT) assert isinstance(error.value, ValueError) # Clear error del error # try to create existing reference with force reference = testrepo.create_reference('refs/tags/version1', LAST_COMMIT, force=True) assert reference.target == LAST_COMMIT def test_create_reference_with_message(testrepo: Repository) -> None: sig = Signature('foo', 'bar') testrepo.set_ident('foo', 'bar') msg = 'Hello log' reference = testrepo.create_reference( 'refs/heads/feature', LAST_COMMIT, message=msg ) first = list(reference.log())[0] assert first.message == msg assert first.committer == sig def test_create_symbolic_reference(testrepo: Repository) -> None: repo = testrepo # We add a tag as a new symbolic reference that always points to # "refs/heads/master" reference = repo.create_reference('refs/tags/beta', 'refs/heads/master') assert reference.type == ReferenceType.SYMBOLIC assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' # try to create existing symbolic reference with pytest.raises(AlreadyExistsError) as error: repo.create_reference('refs/tags/beta', 'refs/heads/master') assert isinstance(error.value, ValueError) # try to create existing symbolic reference with force reference = repo.create_reference('refs/tags/beta', 'refs/heads/master', force=True) assert reference.type == ReferenceType.SYMBOLIC assert reference.target == 'refs/heads/master' assert reference.raw_target == b'refs/heads/master' def test_create_symbolic_reference_with_message(testrepo: Repository) -> None: sig = Signature('foo', 'bar') testrepo.set_ident('foo', 'bar') msg = 'Hello log' reference = testrepo.create_reference( 'HEAD', 'refs/heads/i18n', force=True, message=msg ) first = list(reference.log())[0] assert first.message == msg assert first.committer == sig def test_create_invalid_reference(testrepo: Repository) -> None: repo = testrepo # try to create a reference with an invalid name with pytest.raises(InvalidSpecError) as error: repo.create_reference('refs/tags/in..valid', 'refs/heads/master') assert isinstance(error.value, ValueError) # def test_packall_references(testrepo: Repository) -> None: # testrepo.packall_references() def test_peel(testrepo: Repository) -> None: repo = testrepo ref = repo.lookup_reference('refs/heads/master') assert repo[ref.target].id == ref.peel().id assert isinstance(ref.raw_target, Oid) assert repo[ref.raw_target].id == ref.peel().id commit = ref.peel(Commit) assert commit.tree.id == ref.peel(Tree).id def test_valid_reference_names_ascii() -> None: assert reference_is_valid_name('HEAD') assert reference_is_valid_name('refs/heads/master') assert reference_is_valid_name('refs/heads/perfectly/valid') assert reference_is_valid_name('refs/tags/v1') assert reference_is_valid_name('refs/special/ref') def test_valid_reference_names_unicode() -> None: assert reference_is_valid_name('refs/heads/ünicöde') assert reference_is_valid_name('refs/tags/😀') def test_invalid_reference_names() -> None: assert not reference_is_valid_name('') assert not reference_is_valid_name(' refs/heads/master') assert not reference_is_valid_name('refs/heads/in..valid') assert not reference_is_valid_name('refs/heads/invalid~') assert not reference_is_valid_name('refs/heads/invalid^') assert not reference_is_valid_name('refs/heads/invalid:') assert not reference_is_valid_name('refs/heads/invalid\\') assert not reference_is_valid_name('refs/heads/invalid?') assert not reference_is_valid_name('refs/heads/invalid[') assert not reference_is_valid_name('refs/heads/invalid*') assert not reference_is_valid_name('refs/heads/@{no}') assert not reference_is_valid_name('refs/heads/foo//bar') def test_invalid_arguments() -> None: with pytest.raises(TypeError): reference_is_valid_name() # type: ignore with pytest.raises(TypeError): reference_is_valid_name(None) # type: ignore with pytest.raises(TypeError): reference_is_valid_name(1) # type: ignore with pytest.raises(TypeError): reference_is_valid_name('too', 'many') # type: ignore libgit2-pygit2-d86662e/test/test_remote.py000066400000000000000000000453671521317216500205350ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import sys from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Remote, Repository from pygit2.remotes import PushUpdate, TransferProgress from . import utils REMOTE_NAME = 'origin' REMOTE_URL = 'https://github.com/libgit2/pygit2.git' REMOTE_FETCHSPEC_SRC = 'refs/heads/*' REMOTE_FETCHSPEC_DST = 'refs/remotes/origin/*' REMOTE_REPO_OBJECTS = 30 REMOTE_FETCHTEST_FETCHSPECS = ['refs/tags/v1.13.2'] REMOTE_REPO_FETCH_ALL_OBJECTS = 13276 REMOTE_REPO_FETCH_HEAD_COMMIT_OBJECTS = 238 ORIGIN_REFSPEC = '+refs/heads/*:refs/remotes/origin/*' def test_remote_create(testrepo: Repository) -> None: name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' remote = testrepo.remotes.create(name, url) assert type(remote) is pygit2.Remote assert name == remote.name assert url == remote.url assert remote.push_url is None with pytest.raises(ValueError): testrepo.remotes.create(*(name, url)) def test_remote_create_with_refspec(testrepo: Repository) -> None: name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' fetch = '+refs/*:refs/*' remote = testrepo.remotes.create(name, url, fetch) assert type(remote) is pygit2.Remote assert name == remote.name assert url == remote.url assert [fetch] == remote.fetch_refspecs assert remote.push_url is None def test_remote_create_anonymous(testrepo: Repository) -> None: url = 'https://github.com/libgit2/pygit2.git' remote = testrepo.remotes.create_anonymous(url) assert remote.name is None assert url == remote.url assert remote.push_url is None assert [] == remote.fetch_refspecs assert [] == remote.push_refspecs def test_remote_delete(testrepo: Repository) -> None: name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' testrepo.remotes.create(name, url) assert 2 == len(testrepo.remotes) remote = testrepo.remotes[1] assert name == remote.name testrepo.remotes.delete(remote.name) assert 1 == len(testrepo.remotes) def test_remote_rename(testrepo: Repository) -> None: remote = testrepo.remotes[0] assert REMOTE_NAME == remote.name problems = testrepo.remotes.rename(remote.name, 'new') assert [] == problems assert 'new' != remote.name with pytest.raises(ValueError): testrepo.remotes.rename('', '') with pytest.raises(ValueError): testrepo.remotes.rename(None, None) # type: ignore def test_remote_set_url(testrepo: Repository) -> None: remote = testrepo.remotes['origin'] assert REMOTE_URL == remote.url new_url = 'https://github.com/cholin/pygit2.git' testrepo.remotes.set_url('origin', new_url) remote = testrepo.remotes['origin'] assert new_url == remote.url with pytest.raises(ValueError): testrepo.remotes.set_url('origin', '') testrepo.remotes.set_push_url('origin', new_url) remote = testrepo.remotes['origin'] assert new_url == remote.push_url with pytest.raises(ValueError): testrepo.remotes.set_push_url('origin', '') def test_refspec(testrepo: Repository) -> None: remote = testrepo.remotes['origin'] assert remote.refspec_count == 1 refspec = remote.get_refspec(0) assert refspec.src == REMOTE_FETCHSPEC_SRC assert refspec.dst == REMOTE_FETCHSPEC_DST assert refspec.force is True assert ORIGIN_REFSPEC == refspec.string assert list is type(remote.fetch_refspecs) assert 1 == len(remote.fetch_refspecs) assert ORIGIN_REFSPEC == remote.fetch_refspecs[0] assert refspec.src_matches('refs/heads/master') assert refspec.dst_matches('refs/remotes/origin/master') assert 'refs/remotes/origin/master' == refspec.transform('refs/heads/master') assert 'refs/heads/master' == refspec.rtransform('refs/remotes/origin/master') assert list is type(remote.push_refspecs) assert 0 == len(remote.push_refspecs) push_specs = remote.push_refspecs assert list is type(push_specs) assert 0 == len(push_specs) testrepo.remotes.add_fetch('origin', '+refs/test/*:refs/test/remotes/*') remote = testrepo.remotes['origin'] fetch_specs = remote.fetch_refspecs assert list is type(fetch_specs) assert 2 == len(fetch_specs) assert [ '+refs/heads/*:refs/remotes/origin/*', '+refs/test/*:refs/test/remotes/*', ] == fetch_specs testrepo.remotes.add_push('origin', '+refs/test/*:refs/test/remotes/*') with pytest.raises(TypeError): testrepo.remotes.add_fetch(['+refs/*:refs/*', 5]) # type: ignore remote = testrepo.remotes['origin'] assert ['+refs/test/*:refs/test/remotes/*'] == remote.push_refspecs def test_remote_list(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] assert REMOTE_NAME == remote.name assert REMOTE_URL == remote.url name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' remote = testrepo.remotes.create(name, url) assert remote.name in testrepo.remotes.names() assert remote.name in [x.name for x in testrepo.remotes] @utils.requires_network def test_list_heads(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] refs = remote.list_heads() assert refs # Check that a known ref is returned. assert next(iter(r for r in refs if r.name == 'refs/tags/v0.28.2')) @utils.requires_network def test_ls_remotes_deprecated(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] new_refs = remote.list_heads() with pytest.warns(DeprecationWarning, match='Use list_heads'): old_refs = remote.ls_remotes() assert new_refs assert old_refs for new, old in zip(new_refs, old_refs, strict=True): assert new.name == old['name'] assert new.oid == old['oid'] assert new.local == old['local'] assert new.symref_target == old['symref_target'] if new.local: assert new.loid == old['loid'] else: assert new.loid == pygit2.Oid(b'') assert old['loid'] is None @utils.requires_network def test_list_heads_without_implicit_connect(testrepo: Repository) -> None: assert 1 == len(testrepo.remotes) remote = testrepo.remotes[0] with pytest.raises(pygit2.GitError, match='this remote has never connected'): remote.list_heads(connect=False) remote.connect() refs = remote.list_heads(connect=False) assert refs # Check that a known ref is returned. assert next(iter(r for r in refs if r.name == 'refs/tags/v0.28.2')) def test_remote_collection(testrepo: Repository) -> None: remote = testrepo.remotes['origin'] assert REMOTE_NAME == remote.name assert REMOTE_URL == remote.url with pytest.raises(KeyError): testrepo.remotes['upstream'] name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' remote = testrepo.remotes.create(name, url) assert remote.name in testrepo.remotes.names() assert remote.name in [x.name for x in testrepo.remotes] @utils.requires_refcount def test_remote_refcount(testrepo: Repository) -> None: start = sys.getrefcount(testrepo) remote = testrepo.remotes[0] del remote end = sys.getrefcount(testrepo) assert start == end def test_fetch(emptyrepo: Repository) -> None: remote = emptyrepo.remotes[0] stats = remote.fetch() assert stats.received_bytes > 2700 assert stats.received_bytes < 3100 assert stats.indexed_objects == REMOTE_REPO_OBJECTS assert stats.received_objects == REMOTE_REPO_OBJECTS @utils.requires_network def test_fetch_depth_zero(testrepo: Repository) -> None: remote = testrepo.remotes[0] stats = remote.fetch(REMOTE_FETCHTEST_FETCHSPECS, depth=0) assert stats.indexed_objects == REMOTE_REPO_FETCH_ALL_OBJECTS assert stats.received_objects == REMOTE_REPO_FETCH_ALL_OBJECTS @utils.requires_network def test_fetch_depth_one(testrepo: Repository) -> None: remote = testrepo.remotes[0] stats = remote.fetch(REMOTE_FETCHTEST_FETCHSPECS, depth=1) assert stats.indexed_objects == REMOTE_REPO_FETCH_HEAD_COMMIT_OBJECTS assert stats.received_objects == REMOTE_REPO_FETCH_HEAD_COMMIT_OBJECTS def test_transfer_progress(emptyrepo: Repository) -> None: class MyCallbacks(pygit2.RemoteCallbacks): def transfer_progress(self, stats: TransferProgress) -> None: self.tp = stats callbacks = MyCallbacks() remote = emptyrepo.remotes[0] stats = remote.fetch(callbacks=callbacks) assert stats.received_bytes == callbacks.tp.received_bytes assert stats.indexed_objects == callbacks.tp.indexed_objects assert stats.received_objects == callbacks.tp.received_objects def test_update_tips(emptyrepo: Repository) -> None: remote = emptyrepo.remotes[0] tips = [ ( 'refs/remotes/origin/master', pygit2.Oid(hex='0' * 40), pygit2.Oid(hex='784855caf26449a1914d2cf62d12b9374d76ae78'), ), ( 'refs/tags/root', pygit2.Oid(hex='0' * 40), pygit2.Oid(hex='3d2962987c695a29f1f80b6c3aa4ec046ef44369'), ), ] class MyCallbacks(pygit2.RemoteCallbacks): tips: list[tuple[str, pygit2.Oid, pygit2.Oid]] def __init__(self, tips: list[tuple[str, pygit2.Oid, pygit2.Oid]]) -> None: self.tips = tips self.i = 0 def update_tips(self, name: str, old: pygit2.Oid, new: pygit2.Oid) -> None: assert self.tips[self.i] == (name, old, new) self.i += 1 callbacks = MyCallbacks(tips) remote.fetch(callbacks=callbacks) assert callbacks.i > 0 @utils.requires_network def test_list_heads_certificate_check() -> None: url = 'https://github.com/pygit2/empty.git' class MyCallbacks(pygit2.RemoteCallbacks): def __init__(self) -> None: self.i = 0 def certificate_check( self, certificate: None, valid: bool, host: str | bytes ) -> bool: self.i += 1 assert certificate is None assert valid is True assert host == b'github.com' return True # We create an in-memory repository git = pygit2.Repository() remote = git.remotes.create_anonymous(url) callbacks = MyCallbacks() refs = remote.list_heads(callbacks=callbacks) # Sanity check that we indeed got some refs. assert len(refs) > 0 # Make sure our certificate_check callback triggered. assert callbacks.i > 0 @pytest.fixture def origin(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('barerepo.zip', tmp_path) as path: yield pygit2.Repository(path) @pytest.fixture def clone(tmp_path: Path) -> Generator[Repository, None, None]: clone = tmp_path / 'clone' clone.mkdir() with utils.TemporaryRepository('barerepo.zip', clone) as path: yield pygit2.Repository(path) @pytest.fixture def remote(origin: Repository, clone: Repository) -> Generator[Remote, None, None]: yield clone.remotes.create('origin', origin.path) def test_push_fast_forward_commits_to_remote_succeeds( origin: Repository, clone: Repository, remote: Remote ) -> None: tip = clone[clone.head.target] oid = clone.create_commit( 'refs/heads/master', tip.author, tip.author, 'empty commit', tip.tree.id, [tip.id], ) remote.push(['refs/heads/master']) assert origin[origin.head.target].id == oid def test_push_when_up_to_date_succeeds( origin: Repository, clone: Repository, remote: Remote ) -> None: remote.push(['refs/heads/master']) origin_tip = origin[origin.head.target].id clone_tip = clone[clone.head.target].id assert origin_tip == clone_tip def test_push_transfer_progress( origin: Repository, clone: Repository, remote: Remote ) -> None: tip = clone[clone.head.target] new_tip_id = clone.create_commit( 'refs/heads/master', tip.author, tip.author, 'empty commit', tip.tree.id, [tip.id], ) # NOTE: We're currently not testing bytes_pushed due to a bug in libgit2 # 1.9.0: it passes a junk value for bytes_pushed when pushing to a remote # on the local filesystem, as is the case in this unit test. (When pushing # to a remote over the network, the value is correct.) class MyCallbacks(pygit2.RemoteCallbacks): def push_transfer_progress( self, objects_pushed: int, total_objects: int, bytes_pushed: int ) -> None: self.objects_pushed = objects_pushed self.total_objects = total_objects assert origin.branches['master'].target == tip.id callbacks = MyCallbacks() remote.push(['refs/heads/master'], callbacks=callbacks) assert callbacks.objects_pushed == 1 assert callbacks.total_objects == 1 assert origin.branches['master'].target == new_tip_id @pytest.mark.parametrize('reject_from', ['push_transfer_progress', 'push_negotiation']) def test_push_interrupted_from_callbacks( origin: Repository, clone: Repository, remote: Remote, reject_from: str ) -> None: reject_message = 'retreat! retreat!' tip = clone[clone.head.target] clone.create_commit( 'refs/heads/master', tip.author, tip.author, 'empty commit', tip.tree.id, [tip.id], ) class MyCallbacks(pygit2.RemoteCallbacks): def push_negotiation(self, updates: list[PushUpdate]) -> None: if reject_from == 'push_negotiation': raise InterruptedError(reject_message) def push_transfer_progress( self, objects_pushed: int, total_objects: int, bytes_pushed: int ) -> None: if reject_from == 'push_transfer_progress': raise InterruptedError(reject_message) assert origin.branches['master'].target == tip.id callbacks = MyCallbacks() with pytest.raises(InterruptedError, match='retreat! retreat!'): remote.push(['refs/heads/master'], callbacks=callbacks) assert origin.branches['master'].target == tip.id def test_push_non_fast_forward_commits_to_remote_fails( origin: Repository, clone: Repository, remote: Remote ) -> None: tip = origin[origin.head.target] origin.create_commit( 'refs/heads/master', tip.author, tip.author, 'some commit', tip.tree.id, [tip.id], ) tip = clone[clone.head.target] clone.create_commit( 'refs/heads/master', tip.author, tip.author, 'other commit', tip.tree.id, [tip.id], ) with pytest.raises(pygit2.GitError): remote.push(['refs/heads/master']) def test_push_options(origin: Repository, clone: Repository, remote: Remote) -> None: from pygit2 import RemoteCallbacks callbacks = RemoteCallbacks() remote.push(['refs/heads/master'], callbacks) remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 0 callbacks = RemoteCallbacks() remote.push(['refs/heads/master'], callbacks, push_options=[]) remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 0 callbacks = RemoteCallbacks() # Local remotes don't support push_options, so pushing will raise an error. # However, push_options should still be set in RemoteCallbacks. with pytest.raises(pygit2.GitError, match='push-options not supported by remote'): remote.push(['refs/heads/master'], callbacks, push_options=['foo']) remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 1 # strings pointed to by remote_push_options.strings[] are already freed callbacks = RemoteCallbacks() with pytest.raises(pygit2.GitError, match='push-options not supported by remote'): remote.push(['refs/heads/master'], callbacks, push_options=['Opt A', 'Opt B']) remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 2 # strings pointed to by remote_push_options.strings[] are already freed def test_push_threads(origin: Repository, clone: Repository, remote: Remote) -> None: from pygit2 import RemoteCallbacks callbacks = RemoteCallbacks() remote.push(['refs/heads/master'], callbacks) assert callbacks.push_options.pb_parallelism == 1 callbacks = RemoteCallbacks() remote.push(['refs/heads/master'], callbacks, threads=0) assert callbacks.push_options.pb_parallelism == 0 callbacks = RemoteCallbacks() remote.push(['refs/heads/master'], callbacks, threads=1) assert callbacks.push_options.pb_parallelism == 1 def test_push_negotiation( origin: Repository, clone: Repository, remote: Remote ) -> None: old_tip = clone[clone.head.target] new_tip_id = clone.create_commit( 'refs/heads/master', old_tip.author, old_tip.author, 'empty commit', old_tip.tree.id, [old_tip.id], ) the_updates: list[PushUpdate] = [] class MyCallbacks(pygit2.RemoteCallbacks): def push_negotiation(self, updates: list[PushUpdate]) -> None: the_updates.extend(updates) assert origin.branches['master'].target == old_tip.id assert 'new_branch' not in origin.branches callbacks = MyCallbacks() remote.push(['refs/heads/master'], callbacks=callbacks) assert len(the_updates) == 1 assert the_updates[0].src_refname == 'refs/heads/master' assert the_updates[0].dst_refname == 'refs/heads/master' assert the_updates[0].src == old_tip.id assert the_updates[0].dst == new_tip_id assert origin.branches['master'].target == new_tip_id libgit2-pygit2-d86662e/test/test_remote_prune.py000066400000000000000000000053711521317216500217350ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Oid, Repository from pygit2.enums import FetchPrune @pytest.fixture def clonerepo( testrepo: Repository, tmp_path: Path ) -> Generator[Repository, None, None]: cloned_repo_path = tmp_path / 'test_remote_prune' pygit2.clone_repository(testrepo.workdir, cloned_repo_path) clonerepo = pygit2.Repository(cloned_repo_path) testrepo.branches.delete('i18n') yield clonerepo def test_fetch_remote_default(clonerepo: Repository) -> None: clonerepo.remotes[0].fetch() assert 'origin/i18n' in clonerepo.branches def test_fetch_remote_prune(clonerepo: Repository) -> None: clonerepo.remotes[0].fetch(prune=FetchPrune.PRUNE) assert 'origin/i18n' not in clonerepo.branches def test_fetch_no_prune(clonerepo: Repository) -> None: clonerepo.remotes[0].fetch(prune=FetchPrune.NO_PRUNE) assert 'origin/i18n' in clonerepo.branches def test_remote_prune(clonerepo: Repository) -> None: pruned = [] class MyCallbacks(pygit2.RemoteCallbacks): def update_tips(self, name: str, old: Oid, new: Oid) -> None: pruned.append(name) callbacks = MyCallbacks() remote = clonerepo.remotes['origin'] # We do a fetch in order to establish the connection to the remote. # Prune operation requires an active connection. remote.fetch(prune=FetchPrune.NO_PRUNE) assert 'origin/i18n' in clonerepo.branches remote.prune(callbacks) assert pruned == ['refs/remotes/origin/i18n'] assert 'origin/i18n' not in clonerepo.branches libgit2-pygit2-d86662e/test/test_remote_utf8.py000066400000000000000000000031401521317216500214620ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator from pathlib import Path import pytest import pygit2 from . import utils @pytest.fixture def repo(tmp_path: Path) -> Generator[pygit2.Repository, None, None]: with utils.TemporaryRepository('utf8branchrepo.zip', tmp_path) as path: yield pygit2.Repository(path) def test_fetch(repo: pygit2.Repository) -> None: remote = repo.remotes.create('origin', repo.workdir) remote.fetch() libgit2-pygit2-d86662e/test/test_repository.py000066400000000000000000001216601521317216500214500ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import shutil import tempfile from pathlib import Path from typing import Optional import pytest # pygit2 import pygit2 from pygit2 import ( Blob, Commit, DiffFile, IndexEntry, Oid, Remote, Repository, Worktree, clone_repository, discover_repository, init_repository, ) from pygit2.credentials import Keypair, Username, UserPass from pygit2.enums import ( CheckoutNotify, CheckoutStrategy, CredentialType, FileMode, FileStatus, ObjectType, RepositoryOpenFlag, RepositoryState, ResetMode, StashApplyProgress, ) from pygit2.index import MergeFileResult from . import utils def test_is_empty(testrepo: Repository) -> None: assert not testrepo.is_empty def test_is_bare(testrepo: Repository) -> None: assert not testrepo.is_bare def test_get_path(testrepo_path: tuple[Repository, Path]) -> None: testrepo, path = testrepo_path assert Path(testrepo.path).resolve() == (path / '.git').resolve() def test_get_workdir(testrepo_path: tuple[Repository, Path]) -> None: testrepo, path = testrepo_path assert Path(testrepo.workdir).resolve() == path.resolve() def test_set_workdir(testrepo: Repository) -> None: directory = tempfile.mkdtemp() testrepo.workdir = directory assert Path(testrepo.workdir).resolve() == Path(directory).resolve() def test_checkout_ref(testrepo: Repository) -> None: ref_i18n = testrepo.lookup_reference('refs/heads/i18n') # checkout i18n with conflicts and default strategy should # not be possible with pytest.raises(pygit2.GitError): testrepo.checkout(ref_i18n) # checkout i18n with GIT_CHECKOUT_FORCE head_object = testrepo.head head = testrepo[head_object.target] assert 'new' not in head.tree testrepo.checkout(ref_i18n, strategy=CheckoutStrategy.FORCE) head_object = testrepo.head head = testrepo[head_object.target] assert head.id == ref_i18n.target assert 'new' in head.tree assert 'bye.txt' not in testrepo.status() def test_checkout_callbacks(testrepo: Repository) -> None: ref_i18n = testrepo.lookup_reference('refs/heads/i18n') class MyCheckoutCallbacks(pygit2.CheckoutCallbacks): def __init__(self) -> None: super().__init__() self.conflicting_paths: set[str] = set() self.updated_paths: set[str] = set() self.completed_steps = -1 self.total_steps = -1 def checkout_notify_flags(self) -> CheckoutNotify: return CheckoutNotify.CONFLICT | CheckoutNotify.UPDATED def checkout_notify( self, why: CheckoutNotify, path: str, baseline: Optional[DiffFile], target: Optional[DiffFile], workdir: Optional[DiffFile], ) -> None: if why == CheckoutNotify.CONFLICT: self.conflicting_paths.add(path) elif why == CheckoutNotify.UPDATED: self.updated_paths.add(path) def checkout_progress( self, path: str, completed_steps: int, total_steps: int ) -> None: self.completed_steps = completed_steps self.total_steps = total_steps # checkout i18n with conflicts and default strategy should not be possible callbacks = MyCheckoutCallbacks() with pytest.raises(pygit2.GitError): testrepo.checkout(ref_i18n, callbacks=callbacks) # make sure the callbacks caught that assert {'bye.txt'} == callbacks.conflicting_paths assert -1 == callbacks.completed_steps # shouldn't have done anything # checkout i18n with GIT_CHECKOUT_FORCE head_object = testrepo.head head = testrepo[head_object.target] assert 'new' not in head.tree callbacks = MyCheckoutCallbacks() testrepo.checkout(ref_i18n, strategy=CheckoutStrategy.FORCE, callbacks=callbacks) # make sure the callbacks caught the files affected by the checkout assert set() == callbacks.conflicting_paths assert {'bye.txt', 'new'} == callbacks.updated_paths assert callbacks.completed_steps > 0 assert callbacks.completed_steps == callbacks.total_steps def test_checkout_aborted_from_callbacks(testrepo: Repository) -> None: ref_i18n = testrepo.lookup_reference('refs/heads/i18n') def read_bye_txt() -> bytes: blob = testrepo[testrepo.create_blob_fromworkdir('bye.txt')] assert isinstance(blob, Blob) return blob.data s = testrepo.status() assert s == {'bye.txt': FileStatus.WT_NEW} class MyCheckoutCallbacks(pygit2.CheckoutCallbacks): def __init__(self) -> None: super().__init__() self.invoked_times = 0 def checkout_notify( self, why: CheckoutNotify, path: str, baseline: Optional[DiffFile], target: Optional[DiffFile], workdir: Optional[DiffFile], ) -> None: self.invoked_times += 1 # skip one file so we're certain that NO files are affected, # even if aborting the checkout from the second file if self.invoked_times == 2: raise InterruptedError('Stop the checkout!') head_object = testrepo.head head = testrepo[head_object.target] assert 'new' not in head.tree assert b'bye world\n' == read_bye_txt() callbacks = MyCheckoutCallbacks() # checkout i18n with GIT_CHECKOUT_FORCE - callbacks should prevent checkout from completing with pytest.raises(InterruptedError): testrepo.checkout( ref_i18n, strategy=CheckoutStrategy.FORCE, callbacks=callbacks ) assert callbacks.invoked_times == 2 assert 'new' not in head.tree assert b'bye world\n' == read_bye_txt() def test_checkout_branch(testrepo: Repository) -> None: branch_i18n = testrepo.lookup_branch('i18n') # checkout i18n with conflicts and default strategy should # not be possible with pytest.raises(pygit2.GitError): testrepo.checkout(branch_i18n) # checkout i18n with GIT_CHECKOUT_FORCE head_object = testrepo.head head = testrepo[head_object.target] assert 'new' not in head.tree testrepo.checkout(branch_i18n, strategy=CheckoutStrategy.FORCE) head_object = testrepo.head head = testrepo[head_object.target] assert head.id == branch_i18n.target assert 'new' in head.tree assert 'bye.txt' not in testrepo.status() def test_checkout_index(testrepo: Repository) -> None: # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: f.write('new content') # checkout index assert 'hello.txt' in testrepo.status() testrepo.checkout(strategy=CheckoutStrategy.FORCE) assert 'hello.txt' not in testrepo.status() def test_checkout_head(testrepo: Repository) -> None: # some changes to the index with (Path(testrepo.workdir) / 'bye.txt').open('w') as f: f.write('new content') testrepo.index.add('bye.txt') # checkout from index should not change anything assert 'bye.txt' in testrepo.status() testrepo.checkout(strategy=CheckoutStrategy.FORCE) assert 'bye.txt' in testrepo.status() # checkout from head will reset index as well testrepo.checkout('HEAD', strategy=CheckoutStrategy.FORCE) assert 'bye.txt' not in testrepo.status() def test_checkout_alternative_dir(testrepo: Repository) -> None: ref_i18n = testrepo.lookup_reference('refs/heads/i18n') extra_dir = Path(testrepo.workdir) / 'extra-dir' extra_dir.mkdir() assert len(list(extra_dir.iterdir())) == 0 testrepo.checkout(ref_i18n, directory=extra_dir) assert not len(list(extra_dir.iterdir())) == 0 def test_checkout_paths(testrepo: Repository) -> None: ref_i18n = testrepo.lookup_reference('refs/heads/i18n') ref_master = testrepo.lookup_reference('refs/heads/master') testrepo.checkout(ref_master) testrepo.checkout(ref_i18n, paths=['new']) status = testrepo.status() assert status['new'] == FileStatus.INDEX_NEW def test_merge_base(testrepo: Repository) -> None: commit = testrepo.merge_base( '5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8', ) assert commit == 'acecd5ea2924a4b900e7e149496e1f4b57976e51' # Create a commit without any merge base to any other sig = pygit2.Signature('me', 'me@example.com') indep = testrepo.create_commit( None, sig, sig, 'a new root commit', testrepo[commit].peel(pygit2.Tree).id, [] ) assert testrepo.merge_base(indep, commit) is None def test_descendent_of(testrepo: Repository) -> None: assert not testrepo.descendant_of( '5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8', ) assert not testrepo.descendant_of( '5ebeeebb320790caf276b9fc8b24546d63316533', '5ebeeebb320790caf276b9fc8b24546d63316533', ) assert testrepo.descendant_of( '5ebeeebb320790caf276b9fc8b24546d63316533', 'acecd5ea2924a4b900e7e149496e1f4b57976e51', ) assert not testrepo.descendant_of( 'acecd5ea2924a4b900e7e149496e1f4b57976e51', '5ebeeebb320790caf276b9fc8b24546d63316533', ) with pytest.raises(pygit2.GitError): testrepo.descendant_of( '2' * 40, # a valid but inexistent SHA '5ebeeebb320790caf276b9fc8b24546d63316533', ) def test_ahead_behind(testrepo: Repository) -> None: ahead, behind = testrepo.ahead_behind( '5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8', ) assert 1 == ahead assert 2 == behind ahead, behind = testrepo.ahead_behind( '4ec4389a8068641da2d6578db0419484972284c8', '5ebeeebb320790caf276b9fc8b24546d63316533', ) assert 2 == ahead assert 1 == behind def test_reset_hard(testrepo: Repository) -> None: ref = '5ebeeebb320790caf276b9fc8b24546d63316533' with (Path(testrepo.workdir) / 'hello.txt').open() as f: lines = f.readlines() assert 'hola mundo\n' in lines assert 'bonjour le monde\n' in lines testrepo.reset(ref, ResetMode.HARD) assert testrepo.head.target == ref with (Path(testrepo.workdir) / 'hello.txt').open() as f: lines = f.readlines() # Hard reset will reset the working copy too assert 'hola mundo\n' not in lines assert 'bonjour le monde\n' not in lines def test_reset_soft(testrepo: Repository) -> None: ref = '5ebeeebb320790caf276b9fc8b24546d63316533' with (Path(testrepo.workdir) / 'hello.txt').open() as f: lines = f.readlines() assert 'hola mundo\n' in lines assert 'bonjour le monde\n' in lines testrepo.reset(ref, ResetMode.SOFT) assert testrepo.head.target == ref with (Path(testrepo.workdir) / 'hello.txt').open() as f: lines = f.readlines() # Soft reset will not reset the working copy assert 'hola mundo\n' in lines assert 'bonjour le monde\n' in lines # soft reset will keep changes in the index diff = testrepo.diff(cached=True) with pytest.raises(KeyError): diff[0] def test_reset_mixed(testrepo: Repository) -> None: ref = '5ebeeebb320790caf276b9fc8b24546d63316533' with (Path(testrepo.workdir) / 'hello.txt').open() as f: lines = f.readlines() assert 'hola mundo\n' in lines assert 'bonjour le monde\n' in lines testrepo.reset(ref, ResetMode.MIXED) assert testrepo.head.target == ref with (Path(testrepo.workdir) / 'hello.txt').open() as f: lines = f.readlines() # mixed reset will not reset the working copy assert 'hola mundo\n' in lines assert 'bonjour le monde\n' in lines # mixed reset will set the index to match working copy diff = testrepo.diff(cached=True) assert diff.patch is not None assert 'hola mundo\n' in diff.patch assert 'bonjour le monde\n' in diff.patch def test_stash(testrepo: Repository) -> None: stash_hash = '6aab5192f88018cb98a7ede99c242f43add5a2fd' stash_message = 'custom stash message' sig = pygit2.Signature( name='Stasher', email='stasher@example.com', time=1641000000, # fixed time so the oid is stable offset=0, ) # make sure we're starting with no stashes assert [] == testrepo.listall_stashes() # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: f.write('new content') testrepo.stash(sig, include_untracked=True, message=stash_message) assert 'hello.txt' not in testrepo.status() repo_stashes = testrepo.listall_stashes() assert 1 == len(repo_stashes) assert repr(repo_stashes[0]) == f'' assert repo_stashes[0].commit_id == stash_hash assert repo_stashes[0].message == 'On master: ' + stash_message testrepo.stash_apply() assert 'hello.txt' in testrepo.status() assert repo_stashes == testrepo.listall_stashes() # still the same stashes testrepo.stash_drop() assert [] == testrepo.listall_stashes() with pytest.raises(KeyError): testrepo.stash_pop() def test_stash_partial(testrepo: Repository) -> None: stash_message = 'custom stash message' sig = pygit2.Signature( name='Stasher', email='stasher@example.com', time=1641000000, offset=0 ) # make sure we're starting with no stashes assert [] == testrepo.listall_stashes() # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: f.write('stash me') with (Path(testrepo.workdir) / 'untracked2.txt').open('w') as f: f.write('do not stash me') assert testrepo.status()['hello.txt'] == FileStatus.WT_MODIFIED assert testrepo.status()['bye.txt'] == FileStatus.WT_NEW assert testrepo.status()['untracked2.txt'] == FileStatus.WT_NEW def stash_pathspecs(paths: list[str]) -> bool: stash_id = testrepo.stash( sig, message=stash_message, keep_all=True, paths=paths ) stash_commit = testrepo[stash_id].peel(pygit2.Commit) stash_diff = testrepo.diff(stash_commit.parents[0], stash_commit) stash_files = set( patch.delta.new_file.path for patch in utils.diff_safeiter(stash_diff) ) return stash_files == set(paths) # Stash a modified file assert stash_pathspecs(['hello.txt']) # Stash one of several untracked files assert stash_pathspecs(['bye.txt']) # Stash a modified file and an untracked file assert stash_pathspecs(['hello.txt', 'bye.txt']) def test_stash_progress_callback(testrepo: Repository) -> None: sig = pygit2.Signature( name='Stasher', email='stasher@example.com', time=1641000000, offset=0 ) # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: f.write('new content') # create the stash testrepo.stash(sig, include_untracked=True, message='custom stash message') progress_sequence = [] class MyStashApplyCallbacks(pygit2.StashApplyCallbacks): def stash_apply_progress(self, progress: StashApplyProgress) -> None: progress_sequence.append(progress) # apply the stash testrepo.stash_apply(callbacks=MyStashApplyCallbacks()) # make sure the callbacks were notified of all the steps assert progress_sequence == [ StashApplyProgress.LOADING_STASH, StashApplyProgress.ANALYZE_INDEX, StashApplyProgress.ANALYZE_MODIFIED, StashApplyProgress.ANALYZE_UNTRACKED, StashApplyProgress.CHECKOUT_UNTRACKED, StashApplyProgress.CHECKOUT_MODIFIED, StashApplyProgress.DONE, ] def test_stash_aborted_from_callbacks(testrepo: Repository) -> None: sig = pygit2.Signature( name='Stasher', email='stasher@example.com', time=1641000000, offset=0 ) # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: f.write('new content') with (Path(testrepo.workdir) / 'untracked.txt').open('w') as f: f.write('yo') # create the stash testrepo.stash(sig, include_untracked=True, message='custom stash message') # define callbacks that will abort the unstash process # just as libgit2 is ready to write the files to disk class MyStashApplyCallbacks(pygit2.StashApplyCallbacks): def stash_apply_progress(self, progress: StashApplyProgress) -> None: if progress == StashApplyProgress.CHECKOUT_UNTRACKED: raise InterruptedError('Stop applying the stash!') # attempt to apply and delete the stash; the callbacks will interrupt that with pytest.raises(InterruptedError): testrepo.stash_pop(callbacks=MyStashApplyCallbacks()) # we interrupted right before the checkout part of the unstashing process, # so the untracked file shouldn't be here assert not (Path(testrepo.workdir) / 'untracked.txt').exists() # and hello.txt should be as it is on master with (Path(testrepo.workdir) / 'hello.txt').open('r') as f: assert f.read() == 'hello world\nhola mundo\nbonjour le monde\n' # and since we didn't let stash_pop run to completion, the stash itself should still be here repo_stashes = testrepo.listall_stashes() assert 1 == len(repo_stashes) assert repo_stashes[0].message == 'On master: custom stash message' def test_stash_apply_checkout_options(testrepo: Repository) -> None: sig = pygit2.Signature( name='Stasher', email='stasher@example.com', time=1641000000, offset=0 ) hello_txt = Path(testrepo.workdir) / 'hello.txt' # some changes to working dir with hello_txt.open('w') as f: f.write('stashed content') # create the stash testrepo.stash(sig, include_untracked=True, message='custom stash message') # define callbacks that raise an InterruptedError when checkout detects a conflict class MyStashApplyCallbacks(pygit2.StashApplyCallbacks): def checkout_notify( self, why: CheckoutNotify, path: str, baseline: Optional[DiffFile], target: Optional[DiffFile], workdir: Optional[DiffFile], ) -> None: if why == CheckoutNotify.CONFLICT: raise InterruptedError('Applying the stash would create a conflict') # overwrite hello.txt so that applying the stash would create a conflict with hello_txt.open('w') as f: f.write('conflicting content') # apply the stash with the default (safe) strategy; # the callbacks should detect a conflict on checkout with pytest.raises(InterruptedError): testrepo.stash_apply( strategy=CheckoutStrategy.SAFE, callbacks=MyStashApplyCallbacks() ) # hello.txt should be intact with hello_txt.open('r') as f: assert f.read() == 'conflicting content' # force apply the stash; this should work testrepo.stash_apply( strategy=CheckoutStrategy.FORCE, callbacks=MyStashApplyCallbacks() ) with hello_txt.open('r') as f: assert f.read() == 'stashed content' def test_revert_commit(testrepo: Repository) -> None: master = testrepo.head.peel() assert isinstance(master, Commit) commit_to_revert = testrepo['4ec4389a8068641da2d6578db0419484972284c8'] assert isinstance(commit_to_revert, Commit) parent = commit_to_revert.parents[0] commit_diff_stats = parent.tree.diff_to_tree(commit_to_revert.tree).stats revert_index = testrepo.revert_commit(commit_to_revert, master) revert_diff_stats = revert_index.diff_to_tree(master.tree).stats assert revert_diff_stats.insertions == commit_diff_stats.deletions assert revert_diff_stats.deletions == commit_diff_stats.insertions assert revert_diff_stats.files_changed == commit_diff_stats.files_changed def test_revert(testrepo: Repository) -> None: hello_txt = Path(testrepo.workdir) / 'hello.txt' commit_to_revert = testrepo['4ec4389a8068641da2d6578db0419484972284c8'] assert isinstance(commit_to_revert, Commit) assert testrepo.state() == RepositoryState.NONE assert not testrepo.message assert 'bonjour le monde' in hello_txt.read_text() # Revert addition of French line in hello.txt testrepo.revert(commit_to_revert) assert 'bonjour le monde' not in hello_txt.read_text() assert testrepo.status()['hello.txt'] == FileStatus.INDEX_MODIFIED assert testrepo.state() == RepositoryState.REVERT assert ( testrepo.message == f'Revert "Say hello in French"\n\nThis reverts commit {commit_to_revert.id}.\n' ) def test_default_signature(testrepo: Repository) -> None: config = testrepo.config config['user.name'] = 'Random J Hacker' config['user.email'] = 'rjh@example.com' sig = testrepo.default_signature assert 'Random J Hacker' == sig.name assert 'rjh@example.com' == sig.email def test_ident_get_set(testrepo: Repository) -> None: # By default, reflog identity should be unset. assert testrepo.ident == (None, None) cname = 'C O Mitter' cemail = 'committer@example.com' testrepo.set_ident(cname, cemail) assert testrepo.ident == (cname, cemail) testrepo.set_ident(None, None) assert testrepo.ident == (None, None) def test_new_repo(tmp_path: Path) -> None: repo = init_repository(tmp_path, False) oid = repo.write(ObjectType.BLOB, 'Test') assert type(oid) is Oid assert (tmp_path / '.git').exists() def test_no_arg(tmp_path: Path) -> None: repo = init_repository(tmp_path) assert not repo.is_bare def test_no_arg_aspath(tmp_path: Path) -> None: repo = init_repository(Path(tmp_path)) assert not repo.is_bare def test_pos_arg_false(tmp_path: Path) -> None: repo = init_repository(tmp_path, False) assert not repo.is_bare def test_pos_arg_true(tmp_path: Path) -> None: repo = init_repository(tmp_path, True) assert repo.is_bare def test_keyword_arg_false(tmp_path: Path) -> None: repo = init_repository(tmp_path, bare=False) assert not repo.is_bare def test_keyword_arg_true(tmp_path: Path) -> None: repo = init_repository(tmp_path, bare=True) assert repo.is_bare def test_discover_repo(tmp_path: Path) -> None: repo = init_repository(tmp_path, False) subdir = tmp_path / 'test1' / 'test2' subdir.mkdir(parents=True) assert repo.path == discover_repository(str(subdir)) def test_discover_repo_aspath(tmp_path: Path) -> None: repo = init_repository(Path(tmp_path), False) subdir = Path(tmp_path) / 'test1' / 'test2' subdir.mkdir(parents=True) assert repo.path == discover_repository(subdir) def test_discover_repo_not_found() -> None: tempdir = tempfile.tempdir assert tempdir is not None assert discover_repository(tempdir) is None def test_repository_init(barerepo_path: tuple[Repository, Path]) -> None: barerepo, path = barerepo_path assert isinstance(path, Path) pygit2.Repository(path) pygit2.Repository(str(path)) pygit2.Repository(bytes(path)) def test_clone_repository(barerepo: Repository, tmp_path: Path) -> None: assert barerepo.is_bare repo = clone_repository(Path(barerepo.path), tmp_path / 'clonepath') assert not repo.is_empty assert not repo.is_bare repo = clone_repository(str(barerepo.path), str(tmp_path / 'clonestr')) assert not repo.is_empty assert not repo.is_bare def test_clone_bare_repository(barerepo: Repository, tmp_path: Path) -> None: repo = clone_repository(barerepo.path, tmp_path / 'clone', bare=True) assert not repo.is_empty assert repo.is_bare @utils.requires_network def test_clone_shallow_repository(tmp_path: Path) -> None: # shallow cloning currently only works with remote repositories url = 'https://github.com/libgit2/TestGitRepository' repo = clone_repository(url, tmp_path / 'clone-shallow', depth=1) assert not repo.is_empty assert repo.is_shallow def test_clone_repository_and_remote_callbacks( barerepo: Repository, tmp_path: Path ) -> None: url = Path(barerepo.path).resolve().as_uri() repo_path = tmp_path / 'clone-into' def create_repository(path: Path, bare: bool) -> Repository: return init_repository(path, bare) # here we override the name def create_remote(repo: Repository, name: str, url: str) -> Remote: return repo.remotes.create('custom_remote', url) repo = clone_repository( url, repo_path, repository=create_repository, remote=create_remote ) assert not repo.is_empty assert 'refs/remotes/custom_remote/master' in repo.listall_references() assert b'refs/remotes/custom_remote/master' in repo.raw_listall_references() assert repo.remotes['custom_remote'] is not None @utils.requires_network def test_clone_with_credentials(tmp_path: Path) -> None: url = 'https://github.com/libgit2/TestGitRepository' credentials = pygit2.UserPass('libgit2', 'libgit2') callbacks = pygit2.RemoteCallbacks(credentials=credentials) repo = clone_repository(url, tmp_path, callbacks=callbacks) assert not repo.is_empty @utils.requires_network def test_clone_bad_credentials(tmp_path: Path) -> None: class MyCallbacks(pygit2.RemoteCallbacks): def credentials( self, url: str, username_from_url: str | None, allowed_types: CredentialType, ) -> Username | UserPass | Keypair: raise RuntimeError('Unexpected error') url = 'https://github.com/github/github' with pytest.raises(RuntimeError) as exc: clone_repository(url, tmp_path, callbacks=MyCallbacks()) assert str(exc.value) == 'Unexpected error' def test_clone_with_checkout_branch(barerepo: Repository, tmp_path: Path) -> None: # create a test case which isolates the remote test_repo = clone_repository( barerepo.path, tmp_path / 'testrepo-orig.git', bare=True ) commit = test_repo[test_repo.head.target] assert isinstance(commit, Commit) test_repo.create_branch('test', commit) repo = clone_repository( test_repo.path, tmp_path / 'testrepo.git', checkout_branch='test', bare=True ) assert repo.lookup_reference('HEAD').target == 'refs/heads/test' @utils.requires_proxy @utils.requires_network def test_clone_with_proxy(tmp_path: Path) -> None: url = 'https://github.com/libgit2/TestGitRepository' repo = clone_repository( url, tmp_path / 'testrepo-orig.git', proxy=True, ) assert not repo.is_empty # FIXME The tests below are commented because they are broken: # # - test_clone_push_url: Passes, but does nothing useful. # # - test_clone_fetch_spec: Segfaults because of a bug in libgit2 0.19, # this has been fixed already, so wait for 0.20 # # - test_clone_push_spec: Passes, but does nothing useful. # # def test_clone_push_url(): # repo_path = "./test/data/testrepo.git/" # repo = clone_repository( # repo_path, tmp_path, push_url="custom_push_url" # ) # assert not repo.is_empty # # FIXME: When pygit2 supports retrieving the pushurl parameter, # # enable this test # # assert repo.remotes[0].pushurl == "custom_push_url" # # def test_clone_fetch_spec(): # repo_path = "./test/data/testrepo.git/" # repo = clone_repository(repo_path, tmp_path, # fetch_spec="refs/heads/test") # assert not repo.is_empty # # FIXME: When pygit2 retrieve the fetchspec we passed to git clone. # # fetchspec seems to be going through, but the Repository class is # # not getting it. # # assert repo.remotes[0].fetchspec == "refs/heads/test" # # def test_clone_push_spec(): # repo_path = "./test/data/testrepo.git/" # repo = clone_repository(repo_path, tmp_path, # push_spec="refs/heads/test") # assert not repo.is_empty # # FIXME: When pygit2 supports retrieving the pushspec parameter, # # enable this test # # not sure how to test this either... couldn't find pushspec # # assert repo.remotes[0].fetchspec == "refs/heads/test" def test_worktree(testrepo: Repository) -> None: worktree_name = 'foo' worktree_dir = Path(tempfile.mkdtemp()) # Delete temp path so that it's not present when we attempt to add the # worktree later worktree_dir.rmdir() def _check_worktree(worktree: Worktree) -> None: # Confirm the name attribute matches the specified name assert worktree.name == worktree_name # Confirm the path attribute points to the correct path assert Path(worktree.path).resolve() == worktree_dir.resolve() # The "gitdir" in a worktree should be a file with a reference to # the actual gitdir. Let's make sure that the path exists and is a # file. assert (worktree_dir / '.git').is_file() # We should have zero worktrees assert testrepo.list_worktrees() == [] # Add a worktree worktree = testrepo.add_worktree(worktree_name, str(worktree_dir)) # Check that the worktree was added properly _check_worktree(worktree) # We should have one worktree now assert testrepo.list_worktrees() == [worktree_name] # We should also have a branch of the same name assert worktree_name in testrepo.listall_branches() # Test that lookup_worktree() returns a properly-instantiated # pygit2._Worktree object _check_worktree(testrepo.lookup_worktree(worktree_name)) # Remove the worktree dir shutil.rmtree(worktree_dir) # Prune the worktree. For some reason, libgit2 treats a worktree as # valid unless both the worktree directory and data dir under # $GIT_DIR/worktrees are gone. This doesn't make much sense since the # normal usage involves removing the worktree directory and then # pruning. So, for now we have to force the prune. This may be # something to take up with libgit2. worktree.prune(True) assert testrepo.list_worktrees() == [] def test_worktree_aspath(testrepo: Repository) -> None: worktree_name = 'foo' worktree_dir = Path(tempfile.mkdtemp()) # Delete temp path so that it's not present when we attempt to add the # worktree later worktree_dir.rmdir() testrepo.add_worktree(worktree_name, worktree_dir) assert testrepo.list_worktrees() == [worktree_name] def test_worktree_custom_ref(testrepo: Repository) -> None: worktree_name = 'foo' worktree_dir = Path(tempfile.mkdtemp()) branch_name = 'version1' # New branch based on head tip = testrepo.revparse_single('HEAD') assert isinstance(tip, Commit) worktree_ref = testrepo.branches.create(branch_name, tip) # Delete temp path so that it's not present when we attempt to add the # worktree later worktree_dir.rmdir() # Add a worktree for the given ref worktree = testrepo.add_worktree(worktree_name, str(worktree_dir), worktree_ref) # We should have one worktree now assert testrepo.list_worktrees() == [worktree_name] # We should not have a branch of the same name assert worktree_name not in testrepo.listall_branches() # The given ref is checked out in the "worktree repository" assert worktree_ref.is_checked_out() # Remove the worktree dir and prune the worktree shutil.rmtree(worktree_dir) worktree.prune(True) assert testrepo.list_worktrees() == [] # The ref is no longer checked out assert worktree_ref.is_checked_out() is False # The branch still exists assert branch_name in testrepo.branches def test_open_extended(tmp_path: Path) -> None: with utils.TemporaryRepository('dirtyrepo.zip', tmp_path) as path: orig_repo = pygit2.Repository(path) assert not orig_repo.is_bare assert orig_repo.path assert orig_repo.workdir # GIT_REPOSITORY_OPEN_NO_SEARCH subdir_path = path / 'subdir' repo = pygit2.Repository(subdir_path) assert not repo.is_bare assert repo.path == orig_repo.path assert repo.workdir == orig_repo.workdir with pytest.raises(pygit2.GitError): repo = pygit2.Repository(subdir_path, RepositoryOpenFlag.NO_SEARCH) # GIT_REPOSITORY_OPEN_NO_DOTGIT gitdir_path = path / '.git' with pytest.raises(pygit2.GitError): repo = pygit2.Repository(path, RepositoryOpenFlag.NO_DOTGIT) repo = pygit2.Repository(gitdir_path, RepositoryOpenFlag.NO_DOTGIT) assert not repo.is_bare assert repo.path == orig_repo.path assert repo.workdir == orig_repo.workdir # GIT_REPOSITORY_OPEN_BARE repo = pygit2.Repository(gitdir_path, RepositoryOpenFlag.BARE) assert repo.is_bare assert repo.path == orig_repo.path assert not repo.workdir def test_is_shallow(testrepo: Repository) -> None: assert not testrepo.is_shallow # create a dummy shallow file with (Path(testrepo.path) / 'shallow').open('wt') as f: f.write('abcdef0123456789abcdef0123456789abcdef00\n') assert testrepo.is_shallow def test_repository_hashfile(testrepo: Repository) -> None: original_hash = testrepo.index['hello.txt'].id # Test simple use h = testrepo.hashfile('hello.txt') assert h == original_hash # Test absolute path # For best results on Windows, pass a pure POSIX path. (See https://github.com/libgit2/libgit2/issues/6825) absolute_path = Path(testrepo.workdir, 'hello.txt') absolute_path_str = absolute_path.as_posix() # Windows compatibility h = testrepo.hashfile(str(absolute_path_str)) assert h == original_hash # Test missing path with pytest.raises(KeyError): testrepo.hashfile('missing-file') # Test invalid object type with pytest.raises(pygit2.GitError): testrepo.hashfile('hello.txt', ObjectType.OFS_DELTA) def test_repository_hashfile_filter(testrepo: Repository) -> None: original_hash = testrepo.index['hello.txt'].id with open(Path(testrepo.workdir, 'hello.txt'), 'rb') as f: original_text = f.read() crlf_data = original_text.replace(b'\n', b'\r\n') crlf_hash = utils.gen_blob_sha1(crlf_data) assert crlf_hash != original_hash # Write hellocrlf.txt as a copy of hello.txt with CRLF line endings with open(Path(testrepo.workdir, 'hellocrlf.txt'), 'wb') as f: f.write(crlf_data) # Set up a CRLF filter testrepo.config['core.autocrlf'] = True with open(Path(testrepo.workdir, '.gitattributes'), 'wt') as f: f.write('*.txt text\n*.bin binary\n\n') # By default, hellocrlf.txt should have the same hash as the original, # due to core.autocrlf=True h = testrepo.hashfile('hellocrlf.txt') assert h == original_hash # Treat absolute path with filters. # For best results on Windows, pass a pure POSIX path. (See https://github.com/libgit2/libgit2/issues/6825) absolute_path = Path(testrepo.workdir, 'hellocrlf.txt') absolute_path_str = absolute_path.as_posix() # Windows compatibility h = testrepo.hashfile(str(absolute_path_str)) assert h == original_hash # Bypass filters h = testrepo.hashfile('hellocrlf.txt', as_path='') assert h == crlf_hash # Bypass filters via .gitattributes h = testrepo.hashfile('hellocrlf.txt', as_path='foobar.bin') assert h == crlf_hash # If core.safecrlf=fail, hashing a non-CRLF file will fail testrepo.config['core.safecrlf'] = 'fail' with pytest.raises(pygit2.GitError): h = testrepo.hashfile('hello.txt') def test_merge_file_from_index_deprecated(testrepo: Repository) -> None: hello_txt = testrepo.index['hello.txt'] hello_txt_executable = IndexEntry( hello_txt.path, hello_txt.id, FileMode.BLOB_EXECUTABLE ) hello_world = IndexEntry('hello_world.txt', hello_txt.id, hello_txt.mode) def get_hello_txt_from_repo() -> str: blob = testrepo.get(hello_txt.id) assert isinstance(blob, Blob) return blob.data.decode() # no change res = testrepo.merge_file_from_index(hello_txt, hello_txt, hello_txt) assert res == get_hello_txt_from_repo() # executable switch on ours res = testrepo.merge_file_from_index(hello_txt, hello_txt_executable, hello_txt) assert res == get_hello_txt_from_repo() # executable switch on theirs res = testrepo.merge_file_from_index(hello_txt, hello_txt, hello_txt_executable) assert res == get_hello_txt_from_repo() # executable switch on both res = testrepo.merge_file_from_index( hello_txt, hello_txt_executable, hello_txt_executable ) assert res == get_hello_txt_from_repo() # path switch on ours res = testrepo.merge_file_from_index(hello_txt, hello_world, hello_txt) assert res == get_hello_txt_from_repo() # path switch on theirs res = testrepo.merge_file_from_index(hello_txt, hello_txt, hello_world) assert res == get_hello_txt_from_repo() # path switch on both res = testrepo.merge_file_from_index(hello_txt, hello_world, hello_world) assert res == get_hello_txt_from_repo() # path switch on ours, executable flag switch on theirs res = testrepo.merge_file_from_index(hello_txt, hello_world, hello_txt_executable) assert res == get_hello_txt_from_repo() # path switch on theirs, executable flag switch on ours res = testrepo.merge_file_from_index(hello_txt, hello_txt_executable, hello_world) assert res == get_hello_txt_from_repo() def test_merge_file_from_index_non_deprecated(testrepo: Repository) -> None: hello_txt = testrepo.index['hello.txt'] hello_txt_executable = IndexEntry( hello_txt.path, hello_txt.id, FileMode.BLOB_EXECUTABLE ) hello_world = IndexEntry('hello_world.txt', hello_txt.id, hello_txt.mode) def get_hello_txt_from_repo() -> str: blob = testrepo.get(hello_txt.id) assert isinstance(blob, Blob) return blob.data.decode() # no change res = testrepo.merge_file_from_index( hello_txt, hello_txt, hello_txt, use_deprecated=False ) assert res == MergeFileResult( True, hello_txt.path, hello_txt.mode, get_hello_txt_from_repo() ) # executable switch on ours res = testrepo.merge_file_from_index( hello_txt, hello_txt_executable, hello_txt, use_deprecated=False ) assert res == MergeFileResult( True, hello_txt.path, hello_txt_executable.mode, get_hello_txt_from_repo(), ) # executable switch on theirs res = testrepo.merge_file_from_index( hello_txt, hello_txt, hello_txt_executable, use_deprecated=False ) assert res == MergeFileResult( True, hello_txt.path, hello_txt_executable.mode, get_hello_txt_from_repo(), ) # executable switch on both res = testrepo.merge_file_from_index( hello_txt, hello_txt_executable, hello_txt_executable, use_deprecated=False ) assert res == MergeFileResult( True, hello_txt.path, hello_txt_executable.mode, get_hello_txt_from_repo(), ) # path switch on ours res = testrepo.merge_file_from_index( hello_txt, hello_world, hello_txt, use_deprecated=False ) assert res == MergeFileResult( True, hello_world.path, hello_txt.mode, get_hello_txt_from_repo() ) # path switch on theirs res = testrepo.merge_file_from_index( hello_txt, hello_txt, hello_world, use_deprecated=False ) assert res == MergeFileResult( True, hello_world.path, hello_txt.mode, get_hello_txt_from_repo() ) # path switch on both res = testrepo.merge_file_from_index( hello_txt, hello_world, hello_world, use_deprecated=False ) assert res == MergeFileResult(True, None, hello_txt.mode, get_hello_txt_from_repo()) # path switch on ours, executable flag switch on theirs res = testrepo.merge_file_from_index( hello_txt, hello_world, hello_txt_executable, use_deprecated=False ) assert res == MergeFileResult( True, hello_world.path, hello_txt_executable.mode, get_hello_txt_from_repo(), ) # path switch on theirs, executable flag switch on ours res = testrepo.merge_file_from_index( hello_txt, hello_txt_executable, hello_world, use_deprecated=False ) assert res == MergeFileResult( True, hello_world.path, hello_txt_executable.mode, get_hello_txt_from_repo(), ) libgit2-pygit2-d86662e/test/test_repository_bare.py000066400000000000000000000176521521317216500224460ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import binascii import os import pathlib import sys import tempfile from pathlib import Path import pytest import pygit2 from pygit2 import Branch, Commit, Oid, Repository from pygit2.enums import FileMode, ObjectType from . import utils HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78' PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^ BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) BLOB_OID = pygit2.Oid(raw=BLOB_RAW) def test_is_empty(barerepo: Repository) -> None: assert not barerepo.is_empty def test_is_bare(barerepo: Repository) -> None: assert barerepo.is_bare def test_head(barerepo: Repository) -> None: head = barerepo.head assert HEAD_SHA == head.target assert type(head) is pygit2.Reference assert not barerepo.head_is_unborn assert not barerepo.head_is_detached def test_set_head(barerepo: Repository) -> None: # Test setting a detached HEAD. barerepo.set_head(pygit2.Oid(hex=PARENT_SHA)) assert barerepo.head.target == PARENT_SHA # And test setting a normal HEAD. barerepo.set_head('refs/heads/master') assert barerepo.head.name == 'refs/heads/master' assert barerepo.head.target == HEAD_SHA def test_read(barerepo: Repository) -> None: with pytest.raises(TypeError): barerepo.read(123) # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, barerepo.read, '1' * 40) ab = barerepo.read(BLOB_OID) a = barerepo.read(BLOB_HEX) assert ab == a assert (ObjectType.BLOB, b'a contents\n') == a a2 = barerepo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') assert (ObjectType.BLOB, b'a contents 2\n') == a2 a_hex_prefix = BLOB_HEX[:4] a3 = barerepo.read(a_hex_prefix) assert (ObjectType.BLOB, b'a contents\n') == a3 def test_write(barerepo: Repository) -> None: data = b'hello world' # invalid object type with pytest.raises(ValueError): barerepo.write(ObjectType.ANY, data) oid = barerepo.write(ObjectType.BLOB, data) assert type(oid) is pygit2.Oid def test_contains(barerepo: Repository) -> None: with pytest.raises(TypeError): 123 in barerepo # type: ignore assert BLOB_OID in barerepo assert BLOB_HEX in barerepo assert BLOB_HEX[:10] in barerepo assert ('a' * 40) not in barerepo assert ('a' * 20) not in barerepo def test_iterable(barerepo: Repository) -> None: oid = pygit2.Oid(hex=BLOB_HEX) assert oid in [obj for obj in barerepo] def test_lookup_blob(barerepo: Repository) -> None: with pytest.raises(TypeError): barerepo[123] # type: ignore assert barerepo[BLOB_OID].id == BLOB_HEX a = barerepo[BLOB_HEX] assert b'a contents\n' == a.read_raw() assert BLOB_HEX == a.id assert int(ObjectType.BLOB) == a.type def test_lookup_blob_prefix(barerepo: Repository) -> None: a = barerepo[BLOB_HEX[:5]] assert b'a contents\n' == a.read_raw() assert BLOB_HEX == a.id assert int(ObjectType.BLOB) == a.type def test_lookup_commit(barerepo: Repository) -> None: commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = barerepo[commit_sha] assert commit_sha == commit.id assert int(ObjectType.COMMIT) == commit.type assert isinstance(commit, Commit) assert commit.message == ( 'Second test data commit.\n\nThis commit has some additional text.\n' ) def test_lookup_commit_prefix(barerepo: Repository) -> None: commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit_sha_prefix = commit_sha[:7] too_short_prefix = commit_sha[:3] commit = barerepo[commit_sha_prefix] assert commit_sha == commit.id assert int(ObjectType.COMMIT) == commit.type assert isinstance(commit, Commit) assert ( 'Second test data commit.\n\n' 'This commit has some additional text.\n' == commit.message ) with pytest.raises(ValueError): barerepo.__getitem__(too_short_prefix) def test_expand_id(barerepo: Repository) -> None: commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' expanded = barerepo.expand_id(commit_sha[:7]) assert commit_sha == expanded @utils.requires_refcount def test_lookup_commit_refcount(barerepo: Repository) -> None: start = sys.getrefcount(barerepo) commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = barerepo[commit_sha] del commit end = sys.getrefcount(barerepo) assert start == end def test_get_path(barerepo_path: tuple[Repository, Path]) -> None: barerepo, path = barerepo_path directory = pathlib.Path(barerepo.path).resolve() assert directory == path.resolve() def test_get_workdir(barerepo: Repository) -> None: assert barerepo.workdir is None def test_revparse_single(barerepo: Repository) -> None: parent = barerepo.revparse_single('HEAD^') assert parent.id == PARENT_SHA def test_hash(barerepo: Repository) -> None: data = 'foobarbaz' hashed_sha1 = pygit2.hash(data) written_sha1 = barerepo.create_blob(data) assert hashed_sha1 == written_sha1 def test_hashfile(barerepo: Repository) -> None: data = 'bazbarfoo' handle, tempfile_path = tempfile.mkstemp() with os.fdopen(handle, 'w') as fh: fh.write(data) hashed_sha1 = pygit2.hashfile(tempfile_path) pathlib.Path(tempfile_path).unlink() written_sha1 = barerepo.create_blob(data) assert hashed_sha1 == written_sha1 def test_conflicts_in_bare_repository(barerepo: Repository) -> None: def create_conflict_file(repo: Repository, branch: Branch, content: str) -> Oid: oid = repo.create_blob(content.encode('utf-8')) tb = repo.TreeBuilder() tb.insert('conflict', oid, FileMode.BLOB) tree = tb.write() sig = pygit2.Signature('Author', 'author@example.com') commit = repo.create_commit( branch.name, sig, sig, 'Conflict', tree, [branch.target] ) assert commit is not None return commit head_peeled = barerepo.head.peel() assert isinstance(head_peeled, Commit) b1 = barerepo.create_branch('b1', head_peeled) c1 = create_conflict_file(barerepo, b1, 'ASCII - abc') head_peeled = barerepo.head.peel() assert isinstance(head_peeled, Commit) b2 = barerepo.create_branch('b2', head_peeled) c2 = create_conflict_file(barerepo, b2, 'Unicode - äüö') index = barerepo.merge_commits(c1, c2) assert index.conflicts is not None # ConflictCollection does not allow calling len(...) on it directly so # we have to calculate length by iterating over its entries assert sum(1 for _ in index.conflicts) == 1 (a, t, o) = index.conflicts['conflict'] diff = barerepo.merge_file_from_index(a, t, o) assert ( diff == """<<<<<<< conflict ASCII - abc ======= Unicode - äüö >>>>>>> conflict """ ) libgit2-pygit2-d86662e/test/test_repository_custom.py000066400000000000000000000044221521317216500230360ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Repository from pygit2.enums import ObjectType @pytest.fixture def repo(testrepopacked: Repository) -> Generator[Repository, None, None]: testrepo = testrepopacked odb = pygit2.Odb() object_path = Path(testrepo.path) / 'objects' odb.add_backend(pygit2.OdbBackendPack(object_path), 1) odb.add_backend(pygit2.OdbBackendLoose(object_path, 0, False), 1) refdb = pygit2.Refdb.new(testrepo) refdb.set_backend(pygit2.RefdbFsBackend(testrepo)) repo = pygit2.Repository() repo.set_odb(odb) repo.set_refdb(refdb) yield repo def test_references(repo: Repository) -> None: refs = [(ref.name, ref.target) for ref in repo.references.objects] assert sorted(refs) == [ ('refs/heads/i18n', '5470a671a80ac3789f1a6a8cefbcf43ce7af0563'), ('refs/heads/master', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'), ] def test_objects(repo: Repository) -> None: a = repo.read('323fae03f4606ea9991df8befbb2fca795e648fa') assert (ObjectType.BLOB, b'foobar\n') == a libgit2-pygit2-d86662e/test/test_repository_empty.py000066400000000000000000000027471521317216500226720ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from pygit2 import Repository def test_is_empty(emptyrepo: Repository) -> None: assert emptyrepo.is_empty def test_is_base(emptyrepo: Repository) -> None: assert not emptyrepo.is_bare def test_head(emptyrepo: Repository) -> None: assert emptyrepo.head_is_unborn assert not emptyrepo.head_is_detached libgit2-pygit2-d86662e/test/test_revparse.py000066400000000000000000000066241521317216500210620ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for revision parsing.""" from pytest import raises from pygit2 import InvalidSpecError, Repository from pygit2.enums import RevSpecFlag HEAD_SHA = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' PARENT_SHA = '5ebeeebb320790caf276b9fc8b24546d63316533' # HEAD^ def test_revparse_single(testrepo: Repository) -> None: assert testrepo.revparse_single('HEAD').id == HEAD_SHA assert testrepo.revparse_single('HEAD^').id == PARENT_SHA o = testrepo.revparse_single('@{-1}') assert o.id == '5470a671a80ac3789f1a6a8cefbcf43ce7af0563' def test_revparse_ext(testrepo: Repository) -> None: o, r = testrepo.revparse_ext('master') assert o.id == HEAD_SHA assert r == testrepo.references['refs/heads/master'] o, r = testrepo.revparse_ext('HEAD^') assert o.id == PARENT_SHA assert r is None o, r = testrepo.revparse_ext('i18n') assert str(o.id).startswith('5470a67') assert r == testrepo.references['refs/heads/i18n'] def test_revparse_1(testrepo: Repository) -> None: s = testrepo.revparse('master') assert s.from_object.id == HEAD_SHA assert s.to_object is None assert s.flags == RevSpecFlag.SINGLE def test_revparse_range_1(testrepo: Repository) -> None: s = testrepo.revparse('HEAD^1..acecd5e') assert s.from_object.id == PARENT_SHA assert str(s.to_object.id).startswith('acecd5e') assert s.flags == RevSpecFlag.RANGE def test_revparse_range_2(testrepo: Repository) -> None: s = testrepo.revparse('HEAD...i18n') assert str(s.from_object.id).startswith('2be5719') assert str(s.to_object.id).startswith('5470a67') assert s.flags == RevSpecFlag.RANGE | RevSpecFlag.MERGE_BASE assert testrepo.merge_base(s.from_object.id, s.to_object.id) is not None def test_revparse_range_errors(testrepo: Repository) -> None: with raises(KeyError): testrepo.revparse('nope..2be571915') with raises(InvalidSpecError): testrepo.revparse('master............2be571915') def test_revparse_repr(testrepo: Repository) -> None: s = testrepo.revparse('HEAD...i18n') assert ( repr(s) == ',to=}>' ) libgit2-pygit2-d86662e/test/test_revwalk.py000066400000000000000000000100221521317216500206710ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for revision walk.""" from pygit2 import Repository from pygit2.enums import SortMode # In the order given by git log log = [ '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98', '5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8', '6aaa262e655dd54252e5813c8e5acd7780ed097d', 'acecd5ea2924a4b900e7e149496e1f4b57976e51', ] REVLOGS = [ ('Nico von Geyso', 'checkout: moving from i18n to master'), ('Nico von Geyso', 'commit: added bye.txt and new'), ('Nico von Geyso', 'checkout: moving from master to i18n'), ('J. David Ibañez', 'merge i18n: Merge made by recursive.'), ('J. David Ibañez', 'commit: Add .gitignore file'), ('J. David Ibañez', 'checkout: moving from i18n to master'), ('J. David Ibañez', 'commit: Say hello in French'), ('J. David Ibañez', 'commit: Say hello in Spanish'), ('J. David Ibañez', 'checkout: moving from master to i18n'), ('J. David Ibañez', 'commit (initial): First commit'), ] def test_log(testrepo: Repository) -> None: ref = testrepo.lookup_reference('HEAD') for i, entry in enumerate(ref.log()): assert entry.committer.name == REVLOGS[i][0] assert entry.message == REVLOGS[i][1] def test_walk(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME) assert [x.id for x in walker] == log def test_reverse(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME | SortMode.REVERSE) assert [x.id for x in walker] == list(reversed(log)) def test_hide(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME) walker.hide('4ec4389a8068641da2d6578db0419484972284c8') assert len(list(walker)) == 2 def test_hide_prefix(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME) walker.hide('4ec4389a') assert len(list(walker)) == 2 def test_reset(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME) walker.reset() assert list(walker) == [] def test_push(testrepo: Repository) -> None: walker = testrepo.walk(log[-1], SortMode.TIME) assert [x.id for x in walker] == log[-1:] walker.reset() walker.push(log[0]) assert [x.id for x in walker] == log def test_sort(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME) walker.sort(SortMode.TIME | SortMode.REVERSE) assert [x.id for x in walker] == list(reversed(log)) def test_simplify_first_parent(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.TIME) walker.simplify_first_parent() assert len(list(walker)) == 3 def test_default_sorting(testrepo: Repository) -> None: walker = testrepo.walk(log[0], SortMode.NONE) list1 = list([x.id for x in walker]) walker = testrepo.walk(log[0]) list2 = list([x.id for x in walker]) assert list1 == list2 libgit2-pygit2-d86662e/test/test_settings.py000066400000000000000000000222111521317216500210610ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Test the Settings class.""" import sys import pytest import pygit2 from pygit2.enums import ConfigLevel, ObjectType def test_mwindow_size() -> None: original = pygit2.settings.mwindow_size try: test_size = 200 * 1024 pygit2.settings.mwindow_size = test_size assert pygit2.settings.mwindow_size == test_size finally: pygit2.settings.mwindow_size = original def test_mwindow_mapped_limit() -> None: original = pygit2.settings.mwindow_mapped_limit try: test_limit = 300 * 1024 pygit2.settings.mwindow_mapped_limit = test_limit assert pygit2.settings.mwindow_mapped_limit == test_limit finally: pygit2.settings.mwindow_mapped_limit = original def test_cached_memory() -> None: cached = pygit2.settings.cached_memory assert isinstance(cached, tuple) assert len(cached) == 2 assert isinstance(cached[0], int) assert isinstance(cached[1], int) def test_enable_caching() -> None: assert hasattr(pygit2.settings, 'enable_caching') assert callable(pygit2.settings.enable_caching) # Should not raise exceptions pygit2.settings.enable_caching(False) pygit2.settings.enable_caching(True) def test_disable_pack_keep_file_checks() -> None: assert hasattr(pygit2.settings, 'disable_pack_keep_file_checks') assert callable(pygit2.settings.disable_pack_keep_file_checks) # Should not raise exceptions pygit2.settings.disable_pack_keep_file_checks(False) pygit2.settings.disable_pack_keep_file_checks(True) pygit2.settings.disable_pack_keep_file_checks(False) def test_cache_max_size() -> None: original_max_size = pygit2.settings.cached_memory[1] try: pygit2.settings.cache_max_size(128 * 1024**2) assert pygit2.settings.cached_memory[1] == 128 * 1024**2 pygit2.settings.cache_max_size(256 * 1024**2) assert pygit2.settings.cached_memory[1] == 256 * 1024**2 finally: pygit2.settings.cache_max_size(original_max_size) @pytest.mark.parametrize( 'object_type,test_size,default_size', [ (ObjectType.BLOB, 2 * 1024, 0), (ObjectType.COMMIT, 8 * 1024, 4096), (ObjectType.TREE, 8 * 1024, 4096), (ObjectType.TAG, 8 * 1024, 4096), (ObjectType.BLOB, 0, 0), ], ) def test_cache_object_limit( object_type: ObjectType, test_size: int, default_size: int ) -> None: assert callable(pygit2.settings.cache_object_limit) pygit2.settings.cache_object_limit(object_type, test_size) pygit2.settings.cache_object_limit(object_type, default_size) @pytest.mark.parametrize( 'level,test_path', [ (ConfigLevel.GLOBAL, '/tmp/test_global'), (ConfigLevel.XDG, '/tmp/test_xdg'), (ConfigLevel.SYSTEM, '/tmp/test_system'), ], ) def test_search_path(level: ConfigLevel, test_path: str) -> None: original = pygit2.settings.search_path[level] try: pygit2.settings.search_path[level] = test_path assert pygit2.settings.search_path[level] == test_path finally: pygit2.settings.search_path[level] = original def test_template_path() -> None: original = pygit2.settings.template_path try: pygit2.settings.template_path = '/tmp/test_templates' assert pygit2.settings.template_path == '/tmp/test_templates' finally: if original: pygit2.settings.template_path = original def test_user_agent() -> None: original = pygit2.settings.user_agent try: pygit2.settings.user_agent = 'test-agent/1.0' assert pygit2.settings.user_agent == 'test-agent/1.0' finally: if original: pygit2.settings.user_agent = original def test_user_agent_product() -> None: original = pygit2.settings.user_agent_product try: pygit2.settings.user_agent_product = 'test-product' assert pygit2.settings.user_agent_product == 'test-product' finally: if original: pygit2.settings.user_agent_product = original def test_pack_max_objects() -> None: original = pygit2.settings.pack_max_objects try: pygit2.settings.pack_max_objects = 100000 assert pygit2.settings.pack_max_objects == 100000 finally: pygit2.settings.pack_max_objects = original def test_owner_validation() -> None: original = pygit2.settings.owner_validation try: pygit2.settings.owner_validation = False assert pygit2.settings.owner_validation == False # noqa: E712 pygit2.settings.owner_validation = True assert pygit2.settings.owner_validation == True # noqa: E712 finally: pygit2.settings.owner_validation = original def test_mwindow_file_limit() -> None: original = pygit2.settings.mwindow_file_limit try: pygit2.settings.mwindow_file_limit = 100 assert pygit2.settings.mwindow_file_limit == 100 finally: pygit2.settings.mwindow_file_limit = original def test_homedir() -> None: original = pygit2.settings.homedir try: pygit2.settings.homedir = '/tmp/test_home' assert pygit2.settings.homedir == '/tmp/test_home' finally: if original: pygit2.settings.homedir = original def test_server_timeouts() -> None: original_connect = pygit2.settings.server_connect_timeout original_timeout = pygit2.settings.server_timeout try: pygit2.settings.server_connect_timeout = 5000 assert pygit2.settings.server_connect_timeout == 5000 pygit2.settings.server_timeout = 10000 assert pygit2.settings.server_timeout == 10000 finally: pygit2.settings.server_connect_timeout = original_connect pygit2.settings.server_timeout = original_timeout def test_extensions() -> None: original = pygit2.settings.extensions try: test_extensions = ['objectformat', 'worktreeconfig'] pygit2.settings.set_extensions(test_extensions) new_extensions = pygit2.settings.extensions for ext in test_extensions: assert ext in new_extensions finally: if original: pygit2.settings.set_extensions(original) @pytest.mark.parametrize( 'method_name,default_value', [ ('enable_strict_object_creation', True), ('enable_strict_symbolic_ref_creation', True), ('enable_ofs_delta', True), ('enable_fsync_gitdir', False), ('enable_strict_hash_verification', True), ('enable_unsaved_index_safety', False), ('enable_http_expect_continue', False), ], ) def test_enable_methods(method_name: str, default_value: bool) -> None: assert hasattr(pygit2.settings, method_name) method = getattr(pygit2.settings, method_name) assert callable(method) method(True) method(False) method(default_value) @pytest.mark.parametrize('priority', [1, 5, 10, 0, -1, -2]) def test_odb_priorities(priority: int) -> None: """Test setting ODB priorities""" assert hasattr(pygit2.settings, 'set_odb_packed_priority') assert hasattr(pygit2.settings, 'set_odb_loose_priority') assert callable(pygit2.settings.set_odb_packed_priority) assert callable(pygit2.settings.set_odb_loose_priority) pygit2.settings.set_odb_packed_priority(priority) pygit2.settings.set_odb_loose_priority(priority) pygit2.settings.set_odb_packed_priority(1) pygit2.settings.set_odb_loose_priority(2) def test_ssl_ciphers() -> None: assert callable(pygit2.settings.set_ssl_ciphers) try: pygit2.settings.set_ssl_ciphers('DEFAULT') except pygit2.GitError as e: if "TLS backend doesn't support" in str(e): pytest.skip(str(e)) raise @pytest.mark.skipif(sys.platform != 'win32', reason='Windows-specific feature') def test_windows_sharemode() -> None: original = pygit2.settings.windows_sharemode try: pygit2.settings.windows_sharemode = 1 assert pygit2.settings.windows_sharemode == 1 pygit2.settings.windows_sharemode = 2 assert pygit2.settings.windows_sharemode == 2 finally: pygit2.settings.windows_sharemode = original libgit2-pygit2-d86662e/test/test_signature.py000066400000000000000000000100471521317216500212260ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import re import time import pytest import pygit2 from pygit2 import Repository, Signature def __assert(signature: Signature, encoding: None | str) -> None: encoding = encoding or 'utf-8' assert signature._encoding == encoding assert signature.name == signature.raw_name.decode(encoding) assert signature.name.encode(encoding) == signature.raw_name assert signature.email == signature.raw_email.decode(encoding) assert signature.email.encode(encoding) == signature.raw_email @pytest.mark.parametrize('encoding', [None, 'utf-8', 'iso-8859-1']) def test_encoding(encoding: None | str) -> None: signature = pygit2.Signature('Foo Ibáñez', 'foo@example.com', encoding=encoding) __assert(signature, encoding) assert abs(signature.time - time.time()) < 5 assert str(signature) == 'Foo Ibáñez ' def test_default_encoding() -> None: signature = pygit2.Signature('Foo Ibáñez', 'foo@example.com', 1322174594, 60) __assert(signature, 'utf-8') def test_ascii() -> None: with pytest.raises(UnicodeEncodeError): pygit2.Signature('Foo Ibáñez', 'foo@example.com', encoding='ascii') @pytest.mark.parametrize('encoding', [None, 'utf-8', 'iso-8859-1']) def test_repr(encoding: str | None) -> None: signature = pygit2.Signature( 'Foo Ibáñez', 'foo@bar.com', 1322174594, 60, encoding=encoding ) expected = f"pygit2.Signature('Foo Ibáñez', 'foo@bar.com', 1322174594, 60, {repr(encoding)})" assert repr(signature) == expected assert signature == eval(expected) def test_repr_from_commit(barerepo: Repository) -> None: repo = barerepo signature = pygit2.Signature('Foo Ibáñez', 'foo@example.com', encoding=None) tree = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' parents = ['5fe808e8953c12735680c257f56600cb0de44b10'] sha = repo.create_commit(None, signature, signature, 'New commit.', tree, parents) commit = repo[sha] assert repr(signature) == repr(commit.author) assert repr(signature) == repr(commit.committer) def test_incorrect_encoding() -> None: gbk_bytes = 'Café'.encode('GBK') # deliberately specifying a mismatching encoding (mojibake) signature = pygit2.Signature(gbk_bytes, 'foo@example.com', 999, 0, encoding='utf-8') # repr() and str() may display junk, but they must not crash assert re.match( r"pygit2.Signature\('Caf.+', 'foo@example.com', 999, 0, 'utf-8'\)", repr(signature), ) assert re.match(r'Caf.+ ', str(signature)) # deliberately specifying an unsupported encoding signature = pygit2.Signature( gbk_bytes, 'foo@example.com', 999, 0, encoding='this-encoding-does-not-exist' ) # repr() and str() may display junk, but they must not crash assert "pygit2.Signature('(error)', '(error)', 999, 0, '(error)')" == repr( signature ) assert '(error) <(error)>' == str(signature) libgit2-pygit2-d86662e/test/test_status.py000066400000000000000000000056121521317216500205520ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import pytest from pygit2 import Repository from pygit2.enums import FileStatus def test_status(dirtyrepo: Repository) -> None: """ For every file in the status, check that the flags are correct. """ git_status = dirtyrepo.status() for filepath, status in git_status.items(): assert filepath in git_status assert status == git_status[filepath] def test_status_untracked_no(dirtyrepo: Repository) -> None: git_status = dirtyrepo.status(untracked_files='no') assert not any(status & FileStatus.WT_NEW for status in git_status.values()) @pytest.mark.parametrize( 'untracked_files,expected', [ ('no', set()), ( 'normal', { 'untracked_dir/', 'staged_delete_file_modified', 'subdir/new_file', 'new_file', }, ), ( 'all', { 'new_file', 'subdir/new_file', 'staged_delete_file_modified', 'untracked_dir/untracked_file', }, ), ], ) def test_status_untracked_normal( dirtyrepo: Repository, untracked_files: str, expected: set[str] ) -> None: git_status = dirtyrepo.status(untracked_files=untracked_files) assert { file for file, status in git_status.items() if status & FileStatus.WT_NEW } == expected @pytest.mark.parametrize('ignored,expected', [(True, {'ignored'}), (False, set())]) def test_status_ignored( dirtyrepo: Repository, ignored: bool, expected: set[str] ) -> None: git_status = dirtyrepo.status(ignored=ignored) assert { file for file, status in git_status.items() if status & FileStatus.IGNORED } == expected libgit2-pygit2-d86662e/test/test_submodule.py000066400000000000000000000264121521317216500212270ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Submodule objects.""" from collections.abc import Generator from pathlib import Path import pytest import pygit2 from pygit2 import Repository, Submodule from pygit2.enums import SubmoduleIgnore as SI from pygit2.enums import SubmoduleStatus as SS from . import utils SUBM_NAME = 'TestGitRepository' SUBM_PATH = 'TestGitRepository' SUBM_URL = 'https://github.com/libgit2/TestGitRepository' SUBM_HEAD_SHA = '49322bb17d3acc9146f98c97d078513228bbf3c0' SUBM_BOTTOM_SHA = '6c8b137b1c652731597c89668f417b8695f28dd7' @pytest.fixture def repo(tmp_path: Path) -> Generator[Repository, None, None]: with utils.TemporaryRepository('submodulerepo.zip', tmp_path) as path: yield pygit2.Repository(path) def test_lookup_submodule(repo: Repository) -> None: s: Submodule | None = repo.submodules[SUBM_PATH] assert s is not None s = repo.submodules.get(SUBM_PATH) assert s is not None def test_lookup_submodule_aspath(repo: Repository) -> None: s = repo.submodules[Path(SUBM_PATH)] assert s is not None def test_lookup_missing_submodule(repo: Repository) -> None: with pytest.raises(KeyError): repo.submodules['does-not-exist'] assert repo.submodules.get('does-not-exist') is None def test_listall_submodules(repo: Repository) -> None: submodules = repo.listall_submodules() assert len(submodules) == 1 assert submodules[0] == SUBM_PATH def test_contains_submodule(repo: Repository) -> None: assert SUBM_PATH in repo.submodules assert 'does-not-exist' not in repo.submodules def test_submodule_iterator(repo: Repository) -> None: for s in repo.submodules: assert isinstance(s, pygit2.Submodule) assert s.path == repo.submodules[s.path].path @utils.requires_network def test_submodule_open(repo: Repository) -> None: s = repo.submodules[SUBM_PATH] repo.submodules.init() repo.submodules.update() r = s.open() assert r is not None assert r.head.target == SUBM_HEAD_SHA @utils.requires_network def test_submodule_open_from_repository_subclass(repo: Repository) -> None: class CustomRepoClass(pygit2.Repository): pass custom_repo = CustomRepoClass(repo.workdir) s = custom_repo.submodules[SUBM_PATH] custom_repo.submodules.init() custom_repo.submodules.update() r = s.open() assert isinstance(r, CustomRepoClass) assert r.head.target == SUBM_HEAD_SHA def test_name(repo: Repository) -> None: s = repo.submodules[SUBM_PATH] assert SUBM_NAME == s.name def test_path(repo: Repository) -> None: s = repo.submodules[SUBM_PATH] assert SUBM_PATH == s.path def test_url(repo: Repository) -> None: s = repo.submodules[SUBM_PATH] assert SUBM_URL == s.url def test_set_url(repo: Repository) -> None: new_url = 'ssh://git@127.0.0.1:2222/my_repo' s = repo.submodules[SUBM_PATH] s.url = new_url assert new_url == repo.submodules[SUBM_PATH].url # Ensure .gitmodules has been correctly altered with open(Path(repo.workdir, '.gitmodules'), 'r') as fd: modules = fd.read() assert new_url in modules def test_missing_url(repo: Repository) -> None: # Remove "url" from .gitmodules with open(Path(repo.workdir, '.gitmodules'), 'wt') as f: f.write('[submodule "TestGitRepository"]\n') f.write('\tpath = TestGitRepository\n') s = repo.submodules[SUBM_PATH] assert not s.url @utils.requires_network def test_init_and_update(repo: Repository) -> None: subrepo_file_path = Path(repo.workdir) / SUBM_PATH / 'master.txt' assert not subrepo_file_path.exists() status = repo.submodules.status(SUBM_NAME) assert status == (SS.IN_HEAD | SS.IN_INDEX | SS.IN_CONFIG | SS.WD_UNINITIALIZED) repo.submodules.init() repo.submodules.update() assert subrepo_file_path.exists() status = repo.submodules.status(SUBM_NAME) assert status == (SS.IN_HEAD | SS.IN_INDEX | SS.IN_CONFIG | SS.IN_WD) @utils.requires_network def test_specified_update(repo: Repository) -> None: subrepo_file_path = Path(repo.workdir) / SUBM_PATH / 'master.txt' assert not subrepo_file_path.exists() repo.submodules.init(submodules=['TestGitRepository']) repo.submodules.update(submodules=['TestGitRepository']) assert subrepo_file_path.exists() @utils.requires_network def test_update_instance(repo: Repository) -> None: subrepo_file_path = Path(repo.workdir) / SUBM_PATH / 'master.txt' assert not subrepo_file_path.exists() sm = repo.submodules['TestGitRepository'] sm.init() sm.update() assert subrepo_file_path.exists() @utils.requires_network @pytest.mark.parametrize('depth', [0, 1]) def test_oneshot_update(repo: Repository, depth: int) -> None: status = repo.submodules.status(SUBM_NAME) assert status == (SS.IN_HEAD | SS.IN_INDEX | SS.IN_CONFIG | SS.WD_UNINITIALIZED) subrepo_file_path = Path(repo.workdir) / SUBM_PATH / 'master.txt' assert not subrepo_file_path.exists() repo.submodules.update(init=True, depth=depth) assert subrepo_file_path.exists() status = repo.submodules.status(SUBM_NAME) assert status == (SS.IN_HEAD | SS.IN_INDEX | SS.IN_CONFIG | SS.IN_WD) sm_repo = repo.submodules[SUBM_NAME].open() if depth == 0: sm_repo[SUBM_BOTTOM_SHA] # full history must be available else: with pytest.raises(KeyError): sm_repo[SUBM_BOTTOM_SHA] # shallow clone @utils.requires_network @pytest.mark.parametrize('depth', [0, 1]) def test_oneshot_update_instance(repo: Repository, depth: int) -> None: subrepo_file_path = Path(repo.workdir) / SUBM_PATH / 'master.txt' assert not subrepo_file_path.exists() sm = repo.submodules[SUBM_NAME] sm.update(init=True, depth=depth) assert subrepo_file_path.exists() sm_repo = sm.open() if depth == 0: sm_repo[SUBM_BOTTOM_SHA] # full history must be available else: with pytest.raises(KeyError): sm_repo[SUBM_BOTTOM_SHA] # shallow clone @utils.requires_network def test_head_id(repo: Repository) -> None: assert repo.submodules[SUBM_PATH].head_id == SUBM_HEAD_SHA @utils.requires_network def test_head_id_null(repo: Repository) -> None: gitmodules_newlines = ( '\n' '[submodule "uncommitted_submodule"]\n' ' path = pygit2\n' ' url = https://github.com/libgit2/pygit2\n' '\n' ) with open(Path(repo.workdir, '.gitmodules'), 'a') as f: f.write(gitmodules_newlines) subm = repo.submodules['uncommitted_submodule'] # The submodule isn't in the HEAD yet, so head_id should be None assert subm.head_id is None @utils.requires_network @pytest.mark.parametrize('depth', [0, 1]) def test_add_submodule(repo: Repository, depth: int) -> None: sm_repo_path = 'test/testrepo' sm = repo.submodules.add(SUBM_URL, sm_repo_path, depth=depth) status = repo.submodules.status(sm_repo_path) assert status == (SS.IN_INDEX | SS.IN_CONFIG | SS.IN_WD | SS.INDEX_ADDED) sm_repo = sm.open() assert sm_repo_path == sm.path assert SUBM_URL == sm.url assert not sm_repo.is_empty if depth == 0: sm_repo[SUBM_BOTTOM_SHA] # full history must be available else: with pytest.raises(KeyError): sm_repo[SUBM_BOTTOM_SHA] # shallow clone @utils.requires_network def test_submodule_status(repo: Repository) -> None: common_status = SS.IN_HEAD | SS.IN_INDEX | SS.IN_CONFIG # Submodule needs initializing assert repo.submodules.status(SUBM_PATH) == common_status | SS.WD_UNINITIALIZED # If ignoring ALL, don't look at WD assert repo.submodules.status(SUBM_PATH, ignore=SI.ALL) == common_status # Update the submodule repo.submodules.update(init=True) # It's in our WD now assert repo.submodules.status(SUBM_PATH) == common_status | SS.IN_WD # Open submodule repo sm_repo: pygit2.Repository = repo.submodules[SUBM_PATH].open() # Move HEAD in the submodule (WD_MODIFIED) sm_repo.checkout('refs/tags/annotated_tag') assert ( repo.submodules.status(SUBM_PATH) == common_status | SS.IN_WD | SS.WD_MODIFIED ) # Move HEAD back to master sm_repo.checkout('refs/heads/master') # Touch some file in the submodule's workdir (WD_WD_MODIFIED) with open(Path(repo.workdir, SUBM_PATH, 'master.txt'), 'wt') as f: f.write('modifying master.txt') assert ( repo.submodules.status(SUBM_PATH) == common_status | SS.IN_WD | SS.WD_WD_MODIFIED ) # Add an untracked file in the submodule's workdir (WD_UNTRACKED) with open(Path(repo.workdir, SUBM_PATH, 'some_untracked_file.txt'), 'wt') as f: f.write('hi') assert ( repo.submodules.status(SUBM_PATH) == common_status | SS.IN_WD | SS.WD_WD_MODIFIED | SS.WD_UNTRACKED ) # Add modified files to the submodule's index (WD_INDEX_MODIFIED) sm_repo.index.add_all() sm_repo.index.write() assert ( repo.submodules.status(SUBM_PATH) == common_status | SS.IN_WD | SS.WD_INDEX_MODIFIED ) def test_submodule_cache(repo: Repository) -> None: # When the cache is turned on, looking up the same submodule twice must return the same git_submodule object repo.submodules.cache_all() sm1 = repo.submodules[SUBM_NAME] sm2 = repo.submodules[SUBM_NAME] assert sm1._subm == sm2._subm # After turning off the cache, each lookup must return a new git_submodule object repo.submodules.cache_clear() sm3 = repo.submodules[SUBM_NAME] sm4 = repo.submodules[SUBM_NAME] assert sm1._subm != sm3._subm assert sm3._subm != sm4._subm def test_submodule_reload(repo: Repository) -> None: sm = repo.submodules[SUBM_NAME] assert sm.url == 'https://github.com/libgit2/TestGitRepository' # Doctor the config file outside of libgit2 with open(Path(repo.workdir, '.gitmodules'), 'wt') as f: f.write('[submodule "TestGitRepository"]\n') f.write('\tpath = TestGitRepository\n') f.write('\turl = https://github.com/libgit2/pygit2\n') # Submodule object is oblivious to the change assert sm.url == 'https://github.com/libgit2/TestGitRepository' # Tell it to refresh its cache sm.reload() assert sm.url == 'https://github.com/libgit2/pygit2' libgit2-pygit2-d86662e/test/test_tag.py000066400000000000000000000066111521317216500200020ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. """Tests for Tag objects.""" import pytest import pygit2 from pygit2 import Repository from pygit2.enums import ObjectType TAG_SHA = '3d2962987c695a29f1f80b6c3aa4ec046ef44369' def test_read_tag(barerepo: Repository) -> None: repo = barerepo tag = repo[TAG_SHA] assert isinstance(tag, pygit2.Tag) target = repo[tag.target] assert isinstance(target, pygit2.Commit) assert int(ObjectType.TAG) == tag.type assert int(ObjectType.COMMIT) == target.type assert 'root' == tag.name assert 'Tagged root commit.\n' == tag.message assert 'Initial test data commit.\n' == target.message assert tag.tagger == pygit2.Signature( 'Dave Borowitz', 'dborowitz@google.com', 1288724692, -420 ) def test_new_tag(barerepo: Repository) -> None: name = 'thetag' target = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' message = 'Tag a blob.\n' tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0) target_prefix = target[:5] too_short_prefix = target[:3] with pytest.raises(ValueError): barerepo.create_tag(name, too_short_prefix, ObjectType.BLOB, tagger, message) sha = barerepo.create_tag(name, target_prefix, ObjectType.BLOB, tagger, message) tag = barerepo[sha] assert isinstance(tag, pygit2.Tag) assert '3ee44658fd11660e828dfc96b9b5c5f38d5b49bb' == tag.id assert name == tag.name assert target == tag.target assert tagger == tag.tagger assert message == tag.message assert name == barerepo[tag.id].name def test_modify_tag(barerepo: Repository) -> None: name = 'thetag' target = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' message = 'Tag a blob.\n' tagger = ('John Doe', 'jdoe@example.com', 12347) tag = barerepo[TAG_SHA] with pytest.raises(AttributeError): setattr(tag, 'name', name) with pytest.raises(AttributeError): setattr(tag, 'target', target) with pytest.raises(AttributeError): setattr(tag, 'tagger', tagger) with pytest.raises(AttributeError): setattr(tag, 'message', message) def test_get_object(barerepo: Repository) -> None: repo = barerepo tag = repo[TAG_SHA] assert isinstance(tag, pygit2.Tag) assert repo[tag.target].id == tag.get_object().id libgit2-pygit2-d86662e/test/test_transaction.py000066400000000000000000000312361521317216500215550ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import threading import pytest from pygit2 import GitError, Oid, Repository from pygit2.transaction import ReferenceTransaction def test_transaction_context_manager(testrepo: Repository) -> None: """Test basic transaction with context manager.""" master_ref = testrepo.lookup_reference('refs/heads/master') assert str(master_ref.target) == '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' # Create a transaction and update a ref new_target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') with testrepo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_target, message='Test update') # Verify the update was applied master_ref = testrepo.lookup_reference('refs/heads/master') assert master_ref.target == new_target def test_transaction_rollback_on_exception(testrepo: Repository) -> None: """Test that transaction rolls back when exception is raised.""" master_ref = testrepo.lookup_reference('refs/heads/master') original_target = master_ref.target new_target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') # Transaction should not commit if exception is raised with pytest.raises(RuntimeError): with testrepo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.set_target('refs/heads/master', new_target, message='Test update') raise RuntimeError('Abort transaction') # Verify the update was NOT applied master_ref = testrepo.lookup_reference('refs/heads/master') assert master_ref.target == original_target def test_transaction_multiple_refs(testrepo: Repository) -> None: """Test updating multiple refs in a single transaction.""" master_ref = testrepo.lookup_reference('refs/heads/master') i18n_ref = testrepo.lookup_reference('refs/heads/i18n') new_master = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') new_i18n = Oid(hex='2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') with testrepo.transaction() as txn: txn.lock_ref('refs/heads/master') txn.lock_ref('refs/heads/i18n') txn.set_target('refs/heads/master', new_master, message='Update master') txn.set_target('refs/heads/i18n', new_i18n, message='Update i18n') # Verify both updates were applied master_ref = testrepo.lookup_reference('refs/heads/master') i18n_ref = testrepo.lookup_reference('refs/heads/i18n') assert master_ref.target == new_master assert i18n_ref.target == new_i18n def test_transaction_symbolic_ref(testrepo: Repository) -> None: """Test updating symbolic reference in transaction.""" with testrepo.transaction() as txn: txn.lock_ref('HEAD') txn.set_symbolic_target('HEAD', 'refs/heads/i18n', message='Switch HEAD') head = testrepo.lookup_reference('HEAD') assert head.target == 'refs/heads/i18n' # Restore HEAD to master with testrepo.transaction() as txn: txn.lock_ref('HEAD') txn.set_symbolic_target('HEAD', 'refs/heads/master', message='Restore HEAD') def test_transaction_remove_ref(testrepo: Repository) -> None: """Test removing a reference in a transaction.""" # Create a test ref test_ref_name = 'refs/heads/test-transaction-delete' target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') testrepo.create_reference(test_ref_name, target) # Verify it exists assert test_ref_name in testrepo.references # Remove it in a transaction with testrepo.transaction() as txn: txn.lock_ref(test_ref_name) txn.remove(test_ref_name) # Verify it's gone assert test_ref_name not in testrepo.references def test_transaction_error_without_lock(testrepo: Repository) -> None: """Test that setting target without lock raises error.""" new_target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') with pytest.raises(KeyError, match='not locked'): with testrepo.transaction() as txn: # Try to set target without locking first txn.set_target('refs/heads/master', new_target, message='Should fail') def test_transaction_isolated_across_threads(testrepo: Repository) -> None: """Test that transactions from different threads are isolated.""" # Create two test refs ref1_name = 'refs/heads/thread-test-1' ref2_name = 'refs/heads/thread-test-2' target1 = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') target2 = Oid(hex='2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') testrepo.create_reference(ref1_name, target1) testrepo.create_reference(ref2_name, target2) results = [] errors = [] thread1_ref1_locked = threading.Event() thread2_ref2_locked = threading.Event() def update_ref1() -> None: try: with testrepo.transaction() as txn: txn.lock_ref(ref1_name) thread1_ref1_locked.set() thread2_ref2_locked.wait(timeout=5) txn.set_target(ref1_name, target2, message='Thread 1 update') results.append('thread1_success') except Exception as e: errors.append(('thread1', str(e))) def update_ref2() -> None: try: with testrepo.transaction() as txn: txn.lock_ref(ref2_name) thread2_ref2_locked.set() thread1_ref1_locked.wait(timeout=5) txn.set_target(ref2_name, target1, message='Thread 2 update') results.append('thread2_success') except Exception as e: errors.append(('thread2', str(e))) thread1 = threading.Thread(target=update_ref1) thread2 = threading.Thread(target=update_ref2) thread1.start() thread2.start() thread1.join() thread2.join() # Both threads should succeed - transactions are isolated assert len(errors) == 0, f'Errors: {errors}' assert 'thread1_success' in results assert 'thread2_success' in results # Verify both updates were applied ref1 = testrepo.lookup_reference(ref1_name) ref2 = testrepo.lookup_reference(ref2_name) assert str(ref1.target) == '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' assert str(ref2.target) == '5ebeeebb320790caf276b9fc8b24546d63316533' def test_transaction_deadlock_prevention(testrepo: Repository) -> None: """Test that acquiring locks in different order raises error instead of deadlock.""" # Create two test refs ref1_name = 'refs/heads/deadlock-test-1' ref2_name = 'refs/heads/deadlock-test-2' target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') testrepo.create_reference(ref1_name, target) testrepo.create_reference(ref2_name, target) thread1_ref1_locked = threading.Event() thread2_ref2_locked = threading.Event() errors = [] successes = [] def thread1_task() -> None: try: with testrepo.transaction() as txn: txn.lock_ref(ref1_name) thread1_ref1_locked.set() thread2_ref2_locked.wait(timeout=5) # this would cause a deadlock, so will throw (GitError) txn.lock_ref(ref2_name) # shouldn't get here successes.append('thread1') except Exception as e: errors.append(('thread1', type(e).__name__, str(e))) def thread2_task() -> None: try: with testrepo.transaction() as txn: txn.lock_ref(ref2_name) thread2_ref2_locked.set() thread1_ref1_locked.wait(timeout=5) # this would cause a deadlock, so will throw (GitError) txn.lock_ref(ref2_name) # shouldn't get here successes.append('thread2') except Exception as e: errors.append(('thread2', type(e).__name__, str(e))) thread1 = threading.Thread(target=thread1_task) thread2 = threading.Thread(target=thread2_task) thread1.start() thread2.start() thread1.join(timeout=5) thread2.join(timeout=5) # At least one thread should fail with an error (not deadlock) # If both threads are still alive, we have a deadlock assert not thread1.is_alive(), 'Thread 1 deadlocked' assert not thread2.is_alive(), 'Thread 2 deadlocked' # Both can't succeed. # libgit2 doesn't *wait* for locks, so it's possible for neither to succeed # if they both try to take the second lock at basically the same time. # The other possibility is that one thread throws, exits its transaction, # and the other thread is able to acquire the second lock. assert len(successes) <= 1 and len(errors) >= 1, ( f'Successes: {successes}; errors: {errors}' ) def test_transaction_commit_from_wrong_thread(testrepo: Repository) -> None: """Test that committing a transaction from wrong thread raises error.""" txn: ReferenceTransaction | None = None def create_transaction() -> None: nonlocal txn txn = testrepo.transaction().__enter__() ref_name = 'refs/heads/wrong-thread-test' target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') testrepo.create_reference(ref_name, target) txn.lock_ref(ref_name) # Create transaction in thread 1 thread = threading.Thread(target=create_transaction) thread.start() thread.join() assert txn is not None with pytest.raises(RuntimeError): # Try to commit from main thread (different from creator) doesn't cause libgit2 to crash, # it raises an exception instead txn.commit() def test_transaction_nested_same_thread(testrepo: Repository) -> None: """Test that two concurrent transactions from same thread work with different refs.""" # Create test refs ref1_name = 'refs/heads/nested-test-1' ref2_name = 'refs/heads/nested-test-2' target1 = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') target2 = Oid(hex='2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') testrepo.create_reference(ref1_name, target1) testrepo.create_reference(ref2_name, target2) # Nested transactions should work as long as they don't conflict with testrepo.transaction() as txn1: txn1.lock_ref(ref1_name) with testrepo.transaction() as txn2: txn2.lock_ref(ref2_name) txn2.set_target(ref2_name, target1, message='Inner transaction') # Inner transaction committed, now update outer txn1.set_target(ref1_name, target2, message='Outer transaction') # Both updates should have been applied ref1 = testrepo.lookup_reference(ref1_name) ref2 = testrepo.lookup_reference(ref2_name) assert str(ref1.target) == '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' assert str(ref2.target) == '5ebeeebb320790caf276b9fc8b24546d63316533' def test_transaction_nested_same_ref_conflict(testrepo: Repository) -> None: """Test that nested transactions fail when trying to lock the same ref.""" ref_name = 'refs/heads/nested-conflict-test' target = Oid(hex='5ebeeebb320790caf276b9fc8b24546d63316533') new_target = Oid(hex='2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') testrepo.create_reference(ref_name, target) with testrepo.transaction() as txn1: txn1.lock_ref(ref_name) # Inner transaction should fail to lock the same ref with pytest.raises(GitError): with testrepo.transaction() as txn2: txn2.lock_ref(ref_name) # Outer transaction should still be able to complete txn1.set_target(ref_name, new_target, message='Outer transaction') # Outer transaction's update should have been applied ref = testrepo.lookup_reference(ref_name) assert ref.target == new_target libgit2-pygit2-d86662e/test/test_tree.py000066400000000000000000000172631521317216500201730ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. import operator import pytest import pygit2 from pygit2 import Object, Repository, Tree from pygit2.enums import FileMode, ObjectType from . import utils TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' SUBTREE_SHA = '614fd9a3094bf618ea938fffc00e7d1a54f89ad0' def assertTreeEntryEqual(entry: Object, sha: str, name: str, filemode: int) -> None: assert entry.id == sha assert entry.name == name assert entry.filemode == filemode assert entry.raw_name == name.encode('utf-8') def test_read_tree(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) with pytest.raises(TypeError): tree[()] # type: ignore with pytest.raises(TypeError): tree / 123 # type: ignore utils.assertRaisesWithArg(KeyError, 'abcd', lambda: tree['abcd']) utils.assertRaisesWithArg(IndexError, -4, lambda: tree[-4]) utils.assertRaisesWithArg(IndexError, 3, lambda: tree[3]) utils.assertRaisesWithArg(KeyError, 'abcd', lambda: tree / 'abcd') assert 3 == len(tree) sha = '7f129fd57e31e935c6d60a0c794efe4e6927664b' assert 'a' in tree assertTreeEntryEqual(tree[0], sha, 'a', 0o0100644) assertTreeEntryEqual(tree[-3], sha, 'a', 0o0100644) assertTreeEntryEqual(tree['a'], sha, 'a', 0o0100644) assertTreeEntryEqual(tree / 'a', sha, 'a', 0o0100644) sha = '85f120ee4dac60d0719fd51731e4199aa5a37df6' assert 'b' in tree assertTreeEntryEqual(tree[1], sha, 'b', 0o0100644) assertTreeEntryEqual(tree[-2], sha, 'b', 0o0100644) assertTreeEntryEqual(tree['b'], sha, 'b', 0o0100644) assertTreeEntryEqual(tree / 'b', sha, 'b', 0o0100644) sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' assertTreeEntryEqual(tree['c/d'], sha, 'd', 0o0100644) assertTreeEntryEqual(tree / 'c/d', sha, 'd', 0o0100644) assertTreeEntryEqual(tree / 'c' / 'd', sha, 'd', 0o0100644) # type: ignore[operator] assertTreeEntryEqual(tree['c']['d'], sha, 'd', 0o0100644) # type: ignore[index] assertTreeEntryEqual((tree / 'c')['d'], sha, 'd', 0o0100644) # type: ignore[index] utils.assertRaisesWithArg(KeyError, 'ab/cd', lambda: tree['ab/cd']) utils.assertRaisesWithArg(KeyError, 'ab/cd', lambda: tree / 'ab/cd') utils.assertRaisesWithArg(KeyError, 'ab', lambda: tree / 'c' / 'ab') # type: ignore[operator] with pytest.raises(TypeError): tree / 'a' / 'cd' # type: ignore def test_equality(barerepo: Repository) -> None: tree_a = barerepo['18e2d2e9db075f9eb43bcb2daa65a2867d29a15e'] tree_b = barerepo['2ad1d3456c5c4a1c9e40aeeddb9cd20b409623c8'] assert isinstance(tree_a, Tree) assert isinstance(tree_b, Tree) assert tree_a['a'] != tree_b['a'] assert tree_a['a'] != tree_b['b'] assert tree_a['b'] == tree_b['b'] def test_sorting(barerepo: Repository) -> None: tree_a = barerepo['18e2d2e9db075f9eb43bcb2daa65a2867d29a15e'] assert isinstance(tree_a, Tree) assert list(tree_a) == sorted(reversed(list(tree_a)), key=pygit2.tree_entry_key) assert list(tree_a) != reversed(list(tree_a)) def test_read_subtree(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) subtree_entry = tree['c'] assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) assert subtree_entry.type == int(ObjectType.TREE) assert subtree_entry.type_str == 'tree' subtree_entry = tree / 'c' assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) assert subtree_entry.type == int(ObjectType.TREE) assert subtree_entry.type_str == 'tree' subtree = barerepo[subtree_entry.id] assert isinstance(subtree, Tree) assert 1 == len(subtree) sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) subtree_entry = tree / 'c' assert subtree_entry == barerepo[subtree_entry.id] def test_new_tree(barerepo: Repository) -> None: repo = barerepo b0 = repo.create_blob('1') b1 = repo.create_blob('2') st = repo.TreeBuilder() st.insert('a', b0, 0o0100644) subtree = repo[st.write()] t = repo.TreeBuilder() t.insert('x', b0, 0o0100644) t.insert('y', b1, 0o0100755) t.insert('z', subtree.id, FileMode.TREE) tree = repo[t.write()] for name, oid, cls, filemode, type, type_str in [ ('x', b0, pygit2.Blob, FileMode.BLOB, ObjectType.BLOB, 'blob'), ('y', b1, pygit2.Blob, FileMode.BLOB_EXECUTABLE, ObjectType.BLOB, 'blob'), ('z', subtree.id, pygit2.Tree, FileMode.TREE, ObjectType.TREE, 'tree'), ]: assert name in tree # type: ignore[operator] obj = tree[name] # type: ignore[index] assert isinstance(obj, cls) assert obj.name == name assert obj.filemode == filemode assert obj.type == type assert obj.type_str == type_str assert repo[obj.id].id == oid assert obj == repo[obj.id] obj = tree / name # type: ignore[operator] assert isinstance(obj, cls) assert obj.name == name assert obj.filemode == filemode assert obj.type == type assert obj.type_str == type_str assert repo[obj.id].id == oid assert obj == repo[obj.id] def test_modify_tree(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] with pytest.raises(TypeError): operator.setitem('c', tree['a']) # type: ignore with pytest.raises(TypeError): operator.delitem('c') # type: ignore def test_iterate_tree(barerepo: Repository) -> None: """ Testing that we're able to iterate of a Tree object and that the resulting sha strings are consistent with the sha strings we could get with other Tree access methods. """ tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) for tree_entry in tree: assert tree_entry.name is not None assert tree_entry == tree[tree_entry.name] def test_iterate_tree_nested(barerepo: Repository) -> None: """ Testing that we're able to iterate of a Tree object and then iterate trees we receive as a result. """ tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) for tree_entry in tree: if isinstance(tree_entry, pygit2.Tree): for tree_entry2 in tree_entry: pass def test_deep_contains(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) assert 'a' in tree assert 'c' in tree assert 'c/d' in tree assert 'c/e' not in tree assert 'd' not in tree assert isinstance(tree['c'], Tree) assert 'd' in tree['c'] assert 'e' not in tree['c'] libgit2-pygit2-d86662e/test/test_treebuilder.py000066400000000000000000000044451521317216500215400ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. from pygit2 import Repository, Tree TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' def test_new_empty_treebuilder(barerepo: Repository) -> None: barerepo.TreeBuilder() def test_noop_treebuilder(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) bld = barerepo.TreeBuilder(TREE_SHA) result = bld.write() assert len(bld) == len(tree) assert tree.id == result def test_noop_treebuilder_from_tree(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) bld = barerepo.TreeBuilder(tree) result = bld.write() assert len(bld) == len(tree) assert tree.id == result def test_rebuild_treebuilder(barerepo: Repository) -> None: tree = barerepo[TREE_SHA] assert isinstance(tree, Tree) bld = barerepo.TreeBuilder() for entry in tree: name = entry.name assert name is not None assert bld.get(name) is None bld.insert(name, entry.id, entry.filemode) assert bld.get(name).id == entry.id result = bld.write() assert len(bld) == len(tree) assert tree.id == result libgit2-pygit2-d86662e/test/utils.py000066400000000000000000000124751521317216500173350ustar00rootroot00000000000000# Copyright 2010-2025 The pygit2 contributors # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, # as published by the Free Software Foundation. # # In addition to the permissions in the GNU General Public License, # the authors give you unlimited permission to link the compiled # version of this file into combinations with other programs, # and to distribute those combinations without any restriction # coming from the use of this file. (The General Public License # restrictions do apply in other respects; for example, they cover # modification of the file, and distribution when not linked into # a combined executable.) # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # Standard library import hashlib import shutil import socket import stat import sys import zipfile from collections.abc import Callable, Iterator from pathlib import Path from types import TracebackType from typing import Any, Optional, ParamSpec, TypeVar # Requirements import pytest # Pygit2 import pygit2 T = TypeVar('T') P = ParamSpec('P') requires_future_libgit2 = pytest.mark.xfail( pygit2.LIBGIT2_VER < (2, 0, 0), reason='This test may work with a future version of libgit2', ) try: socket.gethostbyname('github.com') has_network = True except socket.gaierror: has_network = False requires_network = pytest.mark.skipif(not has_network, reason='Requires network') with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: has_proxy = sock.connect_ex(('', 8888)) == 0 requires_proxy = pytest.mark.skipif(not has_proxy, reason='Requires proxy at port 8888') requires_ssh = pytest.mark.skipif( pygit2.enums.Feature.SSH not in pygit2.features, reason='Requires SSH' ) is_pypy = '__pypy__' in sys.builtin_module_names requires_refcount = pytest.mark.skipif(is_pypy, reason='skip refcounts checks in pypy') fails_in_macos = pytest.mark.xfail( sys.platform == 'darwin', reason='fails in macOS for an unknown reason' ) def gen_blob_sha1(data: bytes) -> str: # http://stackoverflow.com/questions/552659/assigning-git-sha1s-without-git m = hashlib.sha1() m.update(f'blob {len(data)}\0'.encode()) m.update(data) return m.hexdigest() def force_rm_handle( # Callable[..., Any], str, , object remove_path: Callable[..., Any], path_str: str, excinfo: tuple[type[BaseException], BaseException, TracebackType], ) -> None: path = Path(path_str) path.chmod(path.stat().st_mode | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) remove_path(path) def rmtree(path: str | Path) -> None: """In Windows a read-only file cannot be removed, and shutil.rmtree fails. So we implement our own version of rmtree to address this issue. """ if Path(path).exists(): shutil.rmtree(path, onerror=force_rm_handle) def diff_safeiter(diff: pygit2.Diff) -> Iterator[pygit2.Patch]: """ In rare cases, Diff.__iter__ may yield None (see diff_get_patch_byindex). To make mypy happy, use this iterator instead of Diff.__iter__ to ensure that all patches in a Diff are valid Patch objects, not None. """ for patch in diff: if patch is None: raise TypeError('patch is None') yield patch class TemporaryRepository: def __init__(self, name: str, tmp_path: Path) -> None: self.name = name self.tmp_path = tmp_path def __enter__(self) -> Path: path = Path(__file__).parent / 'data' / self.name temp_repo_path = Path(self.tmp_path) / path.stem if path.suffix == '.zip': with zipfile.ZipFile(path) as zipf: zipf.extractall(self.tmp_path) elif path.suffix == '.git': shutil.copytree(path, temp_repo_path) else: raise ValueError(f'Unexpected {path.suffix} extension') return temp_repo_path def __exit__( self, exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> None: pass def assertRaisesWithArg( exc_class: type[Exception], arg: object, func: Callable[P, T], *args: P.args, **kwargs: P.kwargs, ) -> None: with pytest.raises(exc_class) as excinfo: func(*args, **kwargs) assert excinfo.value.args == (arg,) # Explicitly clear the Exception Info. Citing # https://docs.pytest.org/en/latest/reference.html#pytest-raises: # # Clearing those references breaks a reference cycle # (ExceptionInfo –> caught exception –> frame stack raising the exception # –> current frame stack –> local variables –> ExceptionInfo) which makes # Python keep all objects referenced from that cycle (including all local # variables in the current frame) alive until the next cyclic garbage # collection run. See the official Python try statement documentation for # more detailed information. del excinfo