git2wrapC++20 wrapper for libgit2 | 
          
| git clone git://git.dimitrijedobrota.com/git2wrap.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 11f4f27c1e02d678c8ee77f5f12ebda5daf44c49 | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Sun, 5 Jan 2025 14:08:26 +0100 | 
Initial commit
| A | .clang-format | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | .clang-tidy | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | .codespellrc | | | ++++++ | 
| A | .github/workflows/ci.yml | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | .gitignore | | | +++++++++++++ | 
| A | BUILDING.md | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | CMakeLists.txt | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | CMakePresets.json | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | CODE_OF_CONDUCT.md | | | +++++ | 
| A | CONTRIBUTING.md | | | +++++++++++++++++++ | 
| A | HACKING.md | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | README.md | | | +++++++++++++++++++ | 
| A | cmake/coverage.cmake | | | +++++++++++++++++++++++++++++++++ | 
| A | cmake/dev-mode.cmake | | | +++++++++++++++++++++ | 
| A | cmake/docs-ci.cmake | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | cmake/docs.cmake | | | ++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | cmake/folders.cmake | | | +++++++++++++++++++++ | 
| A | cmake/install-config.cmake | | | + | 
| A | cmake/install-rules.cmake | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | cmake/lint-targets.cmake | | | ++++++++++++++++++++++++++++++++++ | 
| A | cmake/lint.cmake | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | cmake/prelude.cmake | | | ++++++++++ | 
| A | cmake/project-is-top-level.cmake | | | ++++++ | 
| A | cmake/spell-targets.cmake | | | ++++++++++++++++++++++ | 
| A | cmake/spell.cmake | | | +++++++++++++++++++++++++++++ | 
| A | cmake/variables.cmake | | | +++++++++++++++++++++++++++++++++++++++++ | 
| A | docs/Doxyfile.in | | | ++++++++++++++++++++++++++++++++ | 
| A | docs/conf.py.in | | | ++++++ | 
| A | docs/pages/about.dox | | | +++++++ | 
| A | example/CMakeLists.txt | | | +++++++++++++++++++++++++ | 
| A | example/empty_example.cpp | | | ++++ | 
| A | include/git2wrap/git2wrap.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | source/git2wrap.cpp | | | +++++++++++++ | 
| A | test/CMakeLists.txt | | | +++++++++++++++++++++++++ | 
| A | test/source/git2wrap_test.cpp | | | ++++++++++ | 
35 files changed, 1793 insertions(+), 0 deletions(-)
diff --git a/ .clang-format b/ .clang-format
@@ -0,0 +1,178 @@
---
          Language: Cpp
          # BasedOnStyle: Chromium
          AccessModifierOffset: -2
          AlignAfterOpenBracket: Align
          AlignConsecutiveMacros: false
          AlignConsecutiveAssignments: false
          AlignConsecutiveBitFields: false
          AlignConsecutiveDeclarations: false
          AlignEscapedNewlines: DontAlign
          AlignOperands: DontAlign
          AlignTrailingComments: false
          AllowAllArgumentsOnNextLine: true
          AllowAllConstructorInitializersOnNextLine: false
          AllowAllParametersOfDeclarationOnNextLine: true
          AllowShortEnumsOnASingleLine: false
          AllowShortBlocksOnASingleLine: Empty
          AllowShortCaseLabelsOnASingleLine: false
          AllowShortFunctionsOnASingleLine: Inline
          AllowShortLambdasOnASingleLine: All
          AllowShortIfStatementsOnASingleLine: Never
          AllowShortLoopsOnASingleLine: false
          AlwaysBreakAfterDefinitionReturnType: None
          AlwaysBreakAfterReturnType: None
          AlwaysBreakBeforeMultilineStrings: true
          AlwaysBreakTemplateDeclarations: Yes
          BinPackArguments: false
          BinPackParameters: false
          BraceWrapping:
            AfterCaseLabel: false
            AfterClass: true
            AfterControlStatement: MultiLine
            AfterEnum: true
            AfterFunction: true
            AfterNamespace: true
            AfterObjCDeclaration: false
            AfterStruct: true
            AfterUnion: true
            AfterExternBlock: true
            BeforeCatch: false
            BeforeElse: false
            BeforeLambdaBody: true
            BeforeWhile: false
            IndentBraces: false
            SplitEmptyFunction: true
            SplitEmptyRecord: true
            SplitEmptyNamespace: true
          BreakBeforeBinaryOperators: NonAssignment
          BreakBeforeBraces: Custom
          # BreakBeforeInheritanceComma: true
          BreakInheritanceList: BeforeComma
          BreakBeforeTernaryOperators: true
          BreakConstructorInitializersBeforeComma: true
          BreakConstructorInitializers: BeforeComma
          BreakAfterJavaFieldAnnotations: true
          BreakStringLiterals: true
          ColumnLimit: 80
          CommentPragmas: '^ IWYU pragma:'
          CompactNamespaces: false
          ConstructorInitializerAllOnOneLineOrOnePerLine: false
          ConstructorInitializerIndentWidth: 4
          ContinuationIndentWidth: 4
          Cpp11BracedListStyle: true
          DeriveLineEnding: false
          DerivePointerAlignment: false
          DisableFormat: false
          ExperimentalAutoDetectBinPacking: false
          FixNamespaceComments: true
          ForEachMacros:
            - foreach
            - Q_FOREACH
            - BOOST_FOREACH
          IncludeBlocks: Regroup
          IncludeCategories:
            # Standard library headers come before anything else
            - Regex: '^<[a-z_]+>'
              Priority: -1
            - Regex: '^<.+\.h(pp)?>'
              Priority: 1
            - Regex: '^<.*'
              Priority: 2
            - Regex: '.*'
              Priority: 3
          IncludeIsMainRegex: ''
          IncludeIsMainSourceRegex: ''
          IndentCaseLabels: true
          IndentCaseBlocks: false
          IndentGotoLabels: true
          IndentPPDirectives: AfterHash
          IndentExternBlock: NoIndent
          IndentWidth: 2
          IndentWrappedFunctionNames: false
          InsertTrailingCommas: Wrapped
          JavaScriptQuotes: Double
          JavaScriptWrapImports: true
          KeepEmptyLinesAtTheStartOfBlocks: false
          MacroBlockBegin: ''
          MacroBlockEnd: ''
          MaxEmptyLinesToKeep: 1
          NamespaceIndentation: None
          ObjCBinPackProtocolList: Never
          ObjCBlockIndentWidth: 2
          ObjCBreakBeforeNestedBlockParam: true
          ObjCSpaceAfterProperty: false
          ObjCSpaceBeforeProtocolList: true
          PenaltyBreakAssignment: 2
          PenaltyBreakBeforeFirstCallParameter: 1
          PenaltyBreakComment: 300
          PenaltyBreakFirstLessLess: 120
          PenaltyBreakString: 1000
          PenaltyBreakTemplateDeclaration: 10
          PenaltyExcessCharacter: 1000000
          PenaltyReturnTypeOnItsOwnLine: 200
          PointerAlignment: Left
          RawStringFormats:
            - Language: Cpp
              Delimiters:
                - cc
                - CC
                - cpp
                - Cpp
                - CPP
                - 'c++'
                - 'C++'
              CanonicalDelimiter: ''
              BasedOnStyle: google
            - Language: TextProto
              Delimiters:
                - pb
                - PB
                - proto
                - PROTO
              EnclosingFunctions:
                - EqualsProto
                - EquivToProto
                - PARSE_PARTIAL_TEXT_PROTO
                - PARSE_TEST_PROTO
                - PARSE_TEXT_PROTO
                - ParseTextOrDie
                - ParseTextProtoOrDie
                - ParseTestProto
                - ParsePartialTestProto
              CanonicalDelimiter: ''
              BasedOnStyle: google
          ReflowComments: true
          SortIncludes: true
          SortUsingDeclarations: true
          SpaceAfterCStyleCast: false
          SpaceAfterLogicalNot: false
          SpaceAfterTemplateKeyword: false
          SpaceBeforeAssignmentOperators: true
          SpaceBeforeCpp11BracedList: true
          SpaceBeforeCtorInitializerColon: true
          SpaceBeforeInheritanceColon: true
          SpaceBeforeParens: ControlStatementsExceptForEachMacros
          SpaceBeforeRangeBasedForLoopColon: true
          SpaceInEmptyBlock: false
          SpaceInEmptyParentheses: false
          SpacesBeforeTrailingComments: 2
          SpacesInAngles: false
          SpacesInConditionalStatement: false
          SpacesInContainerLiterals: false
          SpacesInCStyleCastParentheses: false
          SpacesInParentheses: false
          SpacesInSquareBrackets: false
          SpaceBeforeSquareBrackets: false
          Standard: Auto
          StatementMacros:
            - Q_UNUSED
            - QT_REQUIRE_VERSION
          TabWidth: 8
          UseCRLF: false
          UseTab: Never
          WhitespaceSensitiveMacros:
            - STRINGIZE
            - PP_STRINGIZE
            - BOOST_PP_STRINGIZE
          ...
        
        diff --git a/ .clang-tidy b/ .clang-tidy
@@ -0,0 +1,155 @@
---
          # Enable ALL the things! Except not really
          # misc-non-private-member-variables-in-classes: the options don't do anything
          # modernize-use-nodiscard: too aggressive, attribute is situationally useful
          Checks: "*,\
            -google-readability-todo,\
            -altera-*,\
            -fuchsia-*,\
            fuchsia-multiple-inheritance,\
            -llvm-header-guard,\
            -llvm-include-order,\
            -llvmlibc-*,\
            -modernize-use-nodiscard,\
            -misc-non-private-member-variables-in-classes"
          WarningsAsErrors: ''
          CheckOptions:
            - key: 'bugprone-argument-comment.StrictMode'
              value: 'true'
          # Prefer using enum classes with 2 values for parameters instead of bools
            - key: 'bugprone-argument-comment.CommentBoolLiterals'
              value: 'true'
            - key: 'bugprone-misplaced-widening-cast.CheckImplicitCasts'
              value: 'true'
            - key: 'bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression'
              value: 'true'
            - key: 'bugprone-suspicious-string-compare.WarnOnLogicalNotComparison'
              value: 'true'
            - key: 'readability-simplify-boolean-expr.ChainedConditionalReturn'
              value: 'true'
            - key: 'readability-simplify-boolean-expr.ChainedConditionalAssignment'
              value: 'true'
            - key: 'readability-uniqueptr-delete-release.PreferResetCall'
              value: 'true'
            - key: 'cppcoreguidelines-init-variables.MathHeader'
              value: '<cmath>'
            - key: 'cppcoreguidelines-narrowing-conversions.PedanticMode'
              value: 'true'
            - key: 'readability-else-after-return.WarnOnUnfixable'
              value: 'true'
            - key: 'readability-else-after-return.WarnOnConditionVariables'
              value: 'true'
            - key: 'readability-inconsistent-declaration-parameter-name.Strict'
              value: 'true'
            - key: 'readability-qualified-auto.AddConstToQualified'
              value: 'true'
            - key: 'readability-redundant-access-specifiers.CheckFirstDeclaration'
              value: 'true'
          # These seem to be the most common identifier styles
            - key: 'readability-identifier-naming.AbstractClassCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ClassMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstantPointerParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstexprFunctionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstexprMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ConstexprVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.EnumCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.EnumConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.FunctionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalConstantPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalFunctionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.GlobalVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.InlineNamespaceCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalConstantPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalPointerCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.LocalVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.MacroDefinitionCase'
              value: 'UPPER_CASE'
            - key: 'readability-identifier-naming.MemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.MethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.NamespaceCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ParameterPackCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PointerParameterCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PrivateMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PrivateMemberPrefix'
              value: 'm_'
            - key: 'readability-identifier-naming.PrivateMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ProtectedMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ProtectedMemberPrefix'
              value: 'm_'
            - key: 'readability-identifier-naming.ProtectedMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PublicMemberCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.PublicMethodCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ScopedEnumConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.StaticConstantCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.StaticVariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.StructCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.TemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.TemplateTemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.TypeAliasCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.TypedefCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.TypeTemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.UnionCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.ValueTemplateParameterCase'
              value: 'CamelCase'
            - key: 'readability-identifier-naming.VariableCase'
              value: 'lower_case'
            - key: 'readability-identifier-naming.VirtualMethodCase'
              value: 'lower_case'
          ...
        
        diff --git a/ .codespellrc b/ .codespellrc
@@ -0,0 +1,6 @@
[codespell]
          builtin = clear,rare,en-GB_to_en-US,names,informal,code
          check-filenames =
          check-hidden =
          skip = */.git,*/build,*/prefix
          quiet-level = 2
        
        diff --git a/ .github/workflows/ci.yml b/ .github/workflows/ci.yml
@@ -0,0 +1,197 @@
name: Continuous Integration
          on:
            push:
              branches:
              - master
            pull_request:
              branches:
              - master
          jobs:
            lint:
              runs-on: ubuntu-22.04
              steps:
              - uses: actions/checkout@v4
              - uses: actions/setup-python@v5
                with: { python-version: "3.12" }
              - name: Install codespell
                run: pip3 install codespell
              - name: Lint
                run: cmake -D FORMAT_COMMAND=clang-format-14 -P cmake/lint.cmake
              - name: Spell check
                if: always()
                run: cmake -P cmake/spell.cmake
            coverage:
              needs: [lint]
              runs-on: ubuntu-22.04
              # To enable coverage, delete the last line from the conditional below and
              # edit the "<name>" placeholder to your GitHub name.
              # If you do not wish to use codecov, then simply delete this job from the
              # workflow.
              if: github.repository_owner == '<name>'
                && false
              steps:
              - uses: actions/checkout@v4
              - name: Install LCov
                run: sudo apt-get update -q
                  && sudo apt-get install lcov -q -y
              - name: Configure
                run: cmake --preset=ci-coverage
              - name: Build
                run: cmake --build build/coverage -j 2
              - name: Test
                working-directory: build/coverage
                run: ctest --output-on-failure --no-tests=error -j 2
              - name: Process coverage info
                run: cmake --build build/coverage -t coverage
              - name: Submit to codecov.io
                uses: codecov/codecov-action@v4
                with:
                  file: build/coverage/coverage.info
                  token: ${{ secrets.CODECOV_TOKEN }}
            sanitize:
              needs: [lint]
              runs-on: ubuntu-22.04
              env: { CXX: clang++-14 }
              steps:
              - uses: actions/checkout@v4
              - name: Configure
                run: cmake --preset=ci-sanitize
              - name: Build
                run: cmake --build build/sanitize -j 2
              - name: Test
                working-directory: build/sanitize
                env:
                  ASAN_OPTIONS: "strict_string_checks=1:\
                    detect_stack_use_after_return=1:\
                    check_initialization_order=1:\
                    strict_init_order=1:\
                    detect_leaks=1:\
                    halt_on_error=1"
                  UBSAN_OPTIONS: "print_stacktrace=1:\
                    halt_on_error=1"
                run: ctest --output-on-failure --no-tests=error -j 2
            test:
              needs: [lint]
              strategy:
                matrix:
                  os: [macos-14, ubuntu-22.04, windows-2022]
                  type: [shared, static]
                  include:
                  - { type: shared, shared: YES }
                  - { type: static, shared: NO }
              runs-on: ${{ matrix.os }}
              steps:
              - uses: actions/checkout@v4
              - name: Install static analyzers
                if: matrix.os == 'ubuntu-22.04'
                run: >-
                  sudo apt-get install clang-tidy-14 cppcheck -y -q
                  sudo update-alternatives --install
                  /usr/bin/clang-tidy clang-tidy
                  /usr/bin/clang-tidy-14 140
              - name: Setup MultiToolTask
                if: matrix.os == 'windows-2022'
                run: |
                  Add-Content "$env:GITHUB_ENV" 'UseMultiToolTask=true'
                  Add-Content "$env:GITHUB_ENV" 'EnforceProcessCountAcrossBuilds=true'
              - name: Configure
                shell: pwsh
                run: cmake "--preset=ci-$("${{ matrix.os }}".split("-")[0])"
                  -D BUILD_SHARED_LIBS=${{ matrix.shared }}
              - name: Setup PATH
                if: matrix.os == 'windows-2022' && matrix.type == 'shared'
                run: Add-Content "$env:GITHUB_PATH" "$(Get-Location)\build\Release"
              - name: Build
                run: cmake --build build --config Release -j 2
              - name: Install
                run: cmake --install build --config Release --prefix prefix
              - name: Test
                working-directory: build
                run: ctest --output-on-failure --no-tests=error -C Release -j 2
            docs:
              # Deploy docs only when builds succeed
              needs: [sanitize, test]
              runs-on: ubuntu-22.04
              # To enable, first you have to create an orphaned gh-pages branch:
              #
              #    git switch --orphan gh-pages
              #    git commit --allow-empty -m "Initial commit"
              #    git push -u origin gh-pages
              #
              # Edit the <name> placeholder below to your GitHub name, so this action
              # runs only in your repository and no one else's fork. After these, delete
              # this comment and the last line in the conditional below.
              # If you do not wish to use GitHub Pages for deploying documentation, then
              # simply delete this job similarly to the coverage one.
              if: github.ref == 'refs/heads/master'
                && github.event_name == 'push'
                && github.repository_owner == '<name>'
                && false
              permissions:
                contents: write
              steps:
              - uses: actions/checkout@v4
              - uses: actions/setup-python@v5
                with: { python-version: "3.12" }
              - name: Install m.css dependencies
                run: pip3 install jinja2 Pygments
              - name: Install Doxygen
                run: sudo apt-get update -q
                  && sudo apt-get install doxygen -q -y
              - name: Build docs
                run: cmake "-DPROJECT_SOURCE_DIR=$PWD" "-DPROJECT_BINARY_DIR=$PWD/build"
                  -P cmake/docs-ci.cmake
              - name: Deploy docs
                uses: peaceiris/actions-gh-pages@v4
                with:
                  github_token: ${{ secrets.GITHUB_TOKEN }}
                  publish_dir: build/docs/html
        
        diff --git a/ .gitignore b/ .gitignore
@@ -0,0 +1,13 @@
**/.DS_Store
          .idea/
          .vs/
          .vscode/
          build/
          cmake-build-*/
          prefix/
          .clangd
          CMakeLists.txt.user
          CMakeUserPresets.json
          compile_commands.json
          env.bat
          env.ps1
        
        diff --git a/ BUILDING.md b/ BUILDING.md
@@ -0,0 +1,89 @@
# Building with CMake
          ## Build
          This project doesn't require any special command-line flags to build to keep
          things simple.
          Here are the steps for building in release mode with a single-configuration
          generator, like the Unix Makefiles one:
          ```sh
          cmake -S . -B build -D CMAKE_BUILD_TYPE=Release
          cmake --build build
          ```
          Here are the steps for building in release mode with a multi-configuration
          generator, like the Visual Studio ones:
          ```sh
          cmake -S . -B build
          cmake --build build --config Release
          ```
          ### Building with MSVC
          Note that MSVC by default is not standards compliant and you need to pass some
          flags to make it behave properly. See the `flags-msvc` preset in the
          [CMakePresets.json](CMakePresets.json) file for the flags and with what
          variable to provide them to CMake during configuration.
          ### Building on Apple Silicon
          CMake supports building on Apple Silicon properly since 3.20.1. Make sure you
          have the [latest version][1] installed.
          ## Install
          This project doesn't require any special command-line flags to install to keep
          things simple. As a prerequisite, the project has to be built with the above
          commands already.
          The below commands require at least CMake 3.15 to run, because that is the
          version in which [Install a Project][2] was added.
          Here is the command for installing the release mode artifacts with a
          single-configuration generator, like the Unix Makefiles one:
          ```sh
          cmake --install build
          ```
          Here is the command for installing the release mode artifacts with a
          multi-configuration generator, like the Visual Studio ones:
          ```sh
          cmake --install build --config Release
          ```
          ### CMake package
          This project exports a CMake package to be used with the [`find_package`][3]
          command of CMake:
          * Package name: `git2wrap`
          * Target name: `git2wrap::git2wrap`
          Example usage:
          ```cmake
          find_package(git2wrap REQUIRED)
          # Declare the imported target as a build requirement using PRIVATE, where
          # project_target is a target created in the consuming project
          target_link_libraries(
              project_target PRIVATE
              git2wrap::git2wrap
          )
          ```
          ### Note to packagers
          The `CMAKE_INSTALL_INCLUDEDIR` is set to a path other than just `include` if
          the project is configured as a top level project to avoid indirectly including
          other libraries when installed to a common prefix. Please review the
          [install-rules.cmake](cmake/install-rules.cmake) file for the full set of
          install rules.
          [1]: https://cmake.org/download/
          [2]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project
          [3]: https://cmake.org/cmake/help/latest/command/find_package.html
        
        diff --git a/ CMakeLists.txt b/ CMakeLists.txt
@@ -0,0 +1,86 @@
cmake_minimum_required(VERSION 3.14)
          include(cmake/prelude.cmake)
          project(
              git2wrap
              VERSION 0.1.0
              DESCRIPTION "C++ 20 wrapper for libgit2"
              HOMEPAGE_URL "https://git.dimitrijedobrota.com/git2wrap.git"
              LANGUAGES CXX
          )
          include(cmake/project-is-top-level.cmake)
          include(cmake/variables.cmake)
          # ---- Declare library ----
          add_library(
              git2wrap_git2wrap
              source/git2wrap.cpp
          )
          add_library(git2wrap::git2wrap ALIAS git2wrap_git2wrap)
          include(GenerateExportHeader)
          generate_export_header(
              git2wrap_git2wrap
              BASE_NAME git2wrap
              EXPORT_FILE_NAME export/git2wrap/git2wrap_export.hpp
              CUSTOM_CONTENT_FROM_VARIABLE pragma_suppress_c4251
          )
          if(NOT BUILD_SHARED_LIBS)
            target_compile_definitions(git2wrap_git2wrap PUBLIC GIT2WRAP_STATIC_DEFINE)
          endif()
          set_target_properties(
              git2wrap_git2wrap PROPERTIES
              CXX_VISIBILITY_PRESET hidden
              VISIBILITY_INLINES_HIDDEN YES
              VERSION "${PROJECT_VERSION}"
              SOVERSION "${PROJECT_VERSION_MAJOR}"
              EXPORT_NAME git2wrap
              OUTPUT_NAME git2wrap
          )
          target_include_directories(
              git2wrap_git2wrap ${warning_guard}
              PUBLIC
              "\$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
          )
          target_include_directories(
              git2wrap_git2wrap SYSTEM
              PUBLIC
              "\$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/export>"
          )
          target_compile_features(git2wrap_git2wrap PUBLIC cxx_std_20)
          # ---- Install rules ----
          if(NOT CMAKE_SKIP_INSTALL_RULES)
            include(cmake/install-rules.cmake)
          endif()
          # ---- Examples ----
          if(PROJECT_IS_TOP_LEVEL)
            option(BUILD_EXAMPLES "Build examples tree." "${git2wrap_DEVELOPER_MODE}")
            if(BUILD_EXAMPLES)
              add_subdirectory(example)
            endif()
          endif()
          # ---- Developer mode ----
          if(NOT git2wrap_DEVELOPER_MODE)
            return()
          elseif(NOT PROJECT_IS_TOP_LEVEL)
            message(
                AUTHOR_WARNING
                "Developer mode is intended for developers of git2wrap"
            )
          endif()
          include(cmake/dev-mode.cmake)
        
        diff --git a/ CMakePresets.json b/ CMakePresets.json
@@ -0,0 +1,160 @@
{
            "version": 2,
            "cmakeMinimumRequired": {
              "major": 3,
              "minor": 14,
              "patch": 0
            },
            "configurePresets": [
              {
                "name": "cmake-pedantic",
                "hidden": true,
                "warnings": {
                  "dev": true,
                  "deprecated": true,
                  "uninitialized": true,
                  "unusedCli": true,
                  "systemVars": false
                },
                "errors": {
                  "dev": true,
                  "deprecated": true
                }
              },
              {
                "name": "dev-mode",
                "hidden": true,
                "inherits": "cmake-pedantic",
                "cacheVariables": {
                  "git2wrap_DEVELOPER_MODE": "ON"
                }
              },
              {
                "name": "cppcheck",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_CPPCHECK": "cppcheck;--inline-suppr"
                }
              },
              {
                "name": "clang-tidy",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=^${sourceDir}/"
                }
              },
              {
                "name": "ci-std",
                "description": "This preset makes sure the project actually builds with at least the specified standard",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_EXTENSIONS": "OFF",
                  "CMAKE_CXX_STANDARD": "20",
                  "CMAKE_CXX_STANDARD_REQUIRED": "ON"
                }
              },
              {
                "name": "flags-gcc-clang",
                "description": "These flags are supported by both GCC and Clang",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_FLAGS": "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS=1 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast",
                  "CMAKE_EXE_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now,-z,nodlopen",
                  "CMAKE_SHARED_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now,-z,nodlopen"
                }
              },
              {
                "name": "flags-appleclang",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_FLAGS": "-fstack-protector-strong -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Woverloaded-virtual -Wnon-virtual-dtor -Wold-style-cast"
                }
              },
              {
                "name": "flags-msvc",
                "description": "Note that all the flags after /W4 are required for MSVC to conform to the language standard",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CXX_FLAGS": "/sdl /guard:cf /utf-8 /diagnostics:caret /w14165 /w44242 /w44254 /w44263 /w34265 /w34287 /w44296 /w44365 /w44388 /w44464 /w14545 /w14546 /w14547 /w14549 /w14555 /w34619 /w34640 /w24826 /w14905 /w14906 /w14928 /w45038 /W4 /permissive- /volatile:iso /Zc:inline /Zc:preprocessor /Zc:enumTypes /Zc:lambda /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /EHsc",
                  "CMAKE_EXE_LINKER_FLAGS": "/machine:x64 /guard:cf",
                  "CMAKE_SHARED_LINKER_FLAGS": "/machine:x64 /guard:cf"
                }
              },
              {
                "name": "ci-linux",
                "inherits": ["flags-gcc-clang", "ci-std"],
                "generator": "Unix Makefiles",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_BUILD_TYPE": "Release"
                }
              },
              {
                "name": "ci-darwin",
                "inherits": ["flags-appleclang", "ci-std"],
                "generator": "Xcode",
                "hidden": true
              },
              {
                "name": "ci-win64",
                "inherits": ["flags-msvc", "ci-std"],
                "generator": "Visual Studio 17 2022",
                "architecture": "x64",
                "hidden": true
              },
              {
                "name": "coverage-linux",
                "binaryDir": "${sourceDir}/build/coverage",
                "inherits": "ci-linux",
                "hidden": true,
                "cacheVariables": {
                  "ENABLE_COVERAGE": "ON",
                  "CMAKE_BUILD_TYPE": "Coverage",
                  "CMAKE_CXX_FLAGS_COVERAGE": "-Og -g --coverage -fkeep-inline-functions -fkeep-static-functions",
                  "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage",
                  "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage"
                }
              },
              {
                "name": "ci-coverage",
                "inherits": ["coverage-linux", "dev-mode"],
                "cacheVariables": {
                  "COVERAGE_HTML_COMMAND": ""
                }
              },
              {
                "name": "ci-sanitize",
                "binaryDir": "${sourceDir}/build/sanitize",
                "inherits": ["ci-linux", "dev-mode"],
                "cacheVariables": {
                  "CMAKE_BUILD_TYPE": "Sanitize",
                  "CMAKE_CXX_FLAGS_SANITIZE": "-U_FORTIFY_SOURCE -O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common"
                }
              },
              {
                "name": "ci-build",
                "binaryDir": "${sourceDir}/build",
                "hidden": true
              },
              {
                "name": "ci-multi-config",
                "description": "Speed up multi-config generators by generating only one configuration instead of the defaults",
                "hidden": true,
                "cacheVariables": {
                  "CMAKE_CONFIGURATION_TYPES": "Release"
                }
              },
              {
                "name": "ci-macos",
                "inherits": ["ci-build", "ci-darwin", "dev-mode", "ci-multi-config"]
              },
              {
                "name": "ci-ubuntu",
                "inherits": ["ci-build", "ci-linux", "clang-tidy", "cppcheck", "dev-mode"]
              },
              {
                "name": "ci-windows",
                "inherits": ["ci-build", "ci-win64", "dev-mode", "ci-multi-config"]
              }
            ]
          }
        
        diff --git a/ CODE_OF_CONDUCT.md b/ CODE_OF_CONDUCT.md
@@ -0,0 +1,5 @@
# Code of Conduct
          * You will be judged by your contributions first, and your sense of humor
            second.
          * Nobody owes you anything.
        
        diff --git a/ CONTRIBUTING.md b/ CONTRIBUTING.md
@@ -0,0 +1,19 @@
# Contributing
          <!--
              Short overview, rules, general guidelines, notes about pull requests and
              style should go here.
          -->
          ## Code of Conduct
          Please see the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) document.
          ## Getting started
          Helpful notes for developers can be found in the [`HACKING.md`](HACKING.md)
          document.
          In addition to he above, if you use the presets file as instructed, then you
          should NOT check it into source control, just as the CMake documentation
          suggests.
        
        diff --git a/ HACKING.md b/ HACKING.md
@@ -0,0 +1,174 @@
# Hacking
          Here is some wisdom to help you build and test this project as a developer and
          potential contributor.
          If you plan to contribute, please read the [CONTRIBUTING](CONTRIBUTING.md)
          guide.
          ## Developer mode
          Build system targets that are only useful for developers of this project are
          hidden if the `git2wrap_DEVELOPER_MODE` option is disabled. Enabling this
          option makes tests and other developer targets and options available. Not
          enabling this option means that you are a consumer of this project and thus you
          have no need for these targets and options.
          Developer mode is always set to on in CI workflows.
          ### Presets
          This project makes use of [presets][1] to simplify the process of configuring
          the project. As a developer, you are recommended to always have the [latest
          CMake version][2] installed to make use of the latest Quality-of-Life
          additions.
          You have a few options to pass `git2wrap_DEVELOPER_MODE` to the configure
          command, but this project prefers to use presets.
          As a developer, you should create a `CMakeUserPresets.json` file at the root of
          the project:
          ```json
          {
            "version": 2,
            "cmakeMinimumRequired": {
              "major": 3,
              "minor": 14,
              "patch": 0
            },
            "configurePresets": [
              {
                "name": "dev",
                "binaryDir": "${sourceDir}/build/dev",
                "inherits": ["dev-mode", "ci-<os>"],
                "cacheVariables": {
                  "CMAKE_BUILD_TYPE": "Debug"
                }
              }
            ],
            "buildPresets": [
              {
                "name": "dev",
                "configurePreset": "dev",
                "configuration": "Debug"
              }
            ],
            "testPresets": [
              {
                "name": "dev",
                "configurePreset": "dev",
                "configuration": "Debug",
                "output": {
                  "outputOnFailure": true
                }
              }
            ]
          }
          ```
          You should replace `<os>` in your newly created presets file with the name of
          the operating system you have, which may be `win64`, `linux` or `darwin`. You
          can see what these correspond to in the
          [`CMakePresets.json`](CMakePresets.json) file.
          `CMakeUserPresets.json` is also the perfect place in which you can put all
          sorts of things that you would otherwise want to pass to the configure command
          in the terminal.
          > **Note**
          > Some editors are pretty greedy with how they open projects with presets.
          > Some just randomly pick a preset and start configuring without your consent,
          > which can be confusing. Make sure that your editor configures when you
          > actually want it to, for example in CLion you have to make sure only the
          > `dev-dev preset` has `Enable profile` ticked in
          > `File > Settings... > Build, Execution, Deployment > CMake` and in Visual
          > Studio you have to set the option `Never run configure step automatically`
          > in `Tools > Options > CMake` **prior to opening the project**, after which
          > you can manually configure using `Project > Configure Cache`.
          ### Configure, build and test
          If you followed the above instructions, then you can configure, build and test
          the project respectively with the following commands from the project root on
          any operating system with any build system:
          ```sh
          cmake --preset=dev
          cmake --build --preset=dev
          ctest --preset=dev
          ```
          If you are using a compatible editor (e.g. VSCode) or IDE (e.g. CLion, VS), you
          will also be able to select the above created user presets for automatic
          integration.
          Please note that both the build and test commands accept a `-j` flag to specify
          the number of jobs to use, which should ideally be specified to the number of
          threads your CPU has. You may also want to add that to your preset using the
          `jobs` property, see the [presets documentation][1] for more details.
          ### Developer mode targets
          These are targets you may invoke using the build command from above, with an
          additional `-t <target>` flag:
          #### `coverage`
          Available if `ENABLE_COVERAGE` is enabled. This target processes the output of
          the previously run tests when built with coverage configuration. The commands
          this target runs can be found in the `COVERAGE_TRACE_COMMAND` and
          `COVERAGE_HTML_COMMAND` cache variables. The trace command produces an info
          file by default, which can be submitted to services with CI integration. The
          HTML command uses the trace command's output to generate an HTML document to
          `<binary-dir>/coverage_html` by default.
          #### `docs`
          Available if `BUILD_MCSS_DOCS` is enabled. Builds to documentation using
          Doxygen and m.css. The output will go to `<binary-dir>/docs` by default
          (customizable using `DOXYGEN_OUTPUT_DIRECTORY`).
          #### `format-check` and `format-fix`
          These targets run the clang-format tool on the codebase to check errors and to
          fix them respectively. Customization available using the `FORMAT_PATTERNS` and
          `FORMAT_COMMAND` cache variables.
          #### `run-examples`
          Runs all the examples created by the `add_example` command.
          #### `spell-check` and `spell-fix`
          These targets run the codespell tool on the codebase to check errors and to fix
          them respectively. Customization available using the `SPELL_COMMAND` cache
          variable.
          ## Running tests on Windows with `BUILD_SHARED_LIBS=ON`
          If you are building a shared library on Windows, you must add the path to the
          DLL file to `PATH` when you want to run tests. One way you could do that is by
          using PowerShell and writing a script for it, e.g. `env.ps1` at the project
          root:
          ```powershell
          $oldPrompt = (Get-Command prompt).ScriptBlock
          function prompt() { "(Debug) $(& $oldPrompt)" }
          $VsInstallPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -Property InstallationPath
          $Env:Path += ";$VsInstallPath\Common7\IDE;$Pwd\build\dev\Debug"
          ```
          Then you can source this script by running `. env.ps1`. This particular
          example will only work for Debug builds.
          ### Passing `PATH` to editors
          Make sure you launch your editor of choice from the console with the above
          script sourced. Look for `(Debug)` in the prompt to confirm, then run e.g.
          `code .` for VScode or `devenv .` for Visual Studio.
          [1]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
          [2]: https://cmake.org/download/
        
        diff --git a/ README.md b/ README.md
@@ -0,0 +1,19 @@
# git2wrap
          This is the git2wrap project.
          # Building and installing
          See the [BUILDING](BUILDING.md) document.
          # Contributing
          See the [CONTRIBUTING](CONTRIBUTING.md) document.
          # Licensing
          <!--
          Please go to https://choosealicense.com/licenses/ and choose a license that
          fits your needs. The recommended license for a project of this type is the
          Boost Software License 1.0.
          -->
        
        diff --git a/ cmake/coverage.cmake b/ cmake/coverage.cmake
@@ -0,0 +1,33 @@
# ---- Variables ----
          # We use variables separate from what CTest uses, because those have
          # customization issues
          set(
              COVERAGE_TRACE_COMMAND
              lcov -c -q
              -o "${PROJECT_BINARY_DIR}/coverage.info"
              -d "${PROJECT_BINARY_DIR}"
              --include "${PROJECT_SOURCE_DIR}/*"
              CACHE STRING
              "; separated command to generate a trace for the 'coverage' target"
          )
          set(
              COVERAGE_HTML_COMMAND
              genhtml --legend -f -q
              "${PROJECT_BINARY_DIR}/coverage.info"
              -p "${PROJECT_SOURCE_DIR}"
              -o "${PROJECT_BINARY_DIR}/coverage_html"
              CACHE STRING
              "; separated command to generate an HTML report for the 'coverage' target"
          )
          # ---- Coverage target ----
          add_custom_target(
              coverage
              COMMAND ${COVERAGE_TRACE_COMMAND}
              COMMAND ${COVERAGE_HTML_COMMAND}
              COMMENT "Generating coverage report"
              VERBATIM
          )
        
        diff --git a/ cmake/dev-mode.cmake b/ cmake/dev-mode.cmake
@@ -0,0 +1,21 @@
include(cmake/folders.cmake)
          include(CTest)
          if(BUILD_TESTING)
            add_subdirectory(test)
          endif()
          option(BUILD_MCSS_DOCS "Build documentation using Doxygen and m.css" OFF)
          if(BUILD_MCSS_DOCS)
            include(cmake/docs.cmake)
          endif()
          option(ENABLE_COVERAGE "Enable coverage support separate from CTest's" OFF)
          if(ENABLE_COVERAGE)
            include(cmake/coverage.cmake)
          endif()
          include(cmake/lint-targets.cmake)
          include(cmake/spell-targets.cmake)
          add_folders(Project)
        
        diff --git a/ cmake/docs-ci.cmake b/ cmake/docs-ci.cmake
@@ -0,0 +1,112 @@
cmake_minimum_required(VERSION 3.14)
          foreach(var IN ITEMS PROJECT_BINARY_DIR PROJECT_SOURCE_DIR)
            if(NOT DEFINED "${var}")
              message(FATAL_ERROR "${var} must be defined")
            endif()
          endforeach()
          set(bin "${PROJECT_BINARY_DIR}")
          set(src "${PROJECT_SOURCE_DIR}")
          # ---- Dependencies ----
          set(mcss_SOURCE_DIR "${bin}/docs/.ci")
          if(NOT IS_DIRECTORY "${mcss_SOURCE_DIR}")
            file(MAKE_DIRECTORY "${mcss_SOURCE_DIR}")
            file(
                DOWNLOAD
                https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip
                "${mcss_SOURCE_DIR}/mcss.zip"
                STATUS status
                EXPECTED_MD5 00cd2757ebafb9bcba7f5d399b3bec7f
            )
            if(NOT status MATCHES "^0;")
              message(FATAL_ERROR "Download failed with ${status}")
            endif()
            execute_process(
                COMMAND "${CMAKE_COMMAND}" -E tar xf mcss.zip
                WORKING_DIRECTORY "${mcss_SOURCE_DIR}"
                RESULT_VARIABLE result
            )
            if(NOT result EQUAL "0")
              message(FATAL_ERROR "Extraction failed with ${result}")
            endif()
            file(REMOVE "${mcss_SOURCE_DIR}/mcss.zip")
          endif()
          find_program(Python3_EXECUTABLE NAMES python3 python)
          if(NOT Python3_EXECUTABLE)
            message(FATAL_ERROR "Python executable was not found")
          endif()
          # ---- Process project() call in CMakeLists.txt ----
          file(READ "${src}/CMakeLists.txt" content)
          string(FIND "${content}" "project(" index)
          if(index EQUAL "-1")
            message(FATAL_ERROR "Could not find \"project(\"")
          endif()
          string(SUBSTRING "${content}" "${index}" -1 content)
          string(FIND "${content}" "\n)\n" index)
          if(index EQUAL "-1")
            message(FATAL_ERROR "Could not find \"\\n)\\n\"")
          endif()
          string(SUBSTRING "${content}" 0 "${index}" content)
          file(WRITE "${bin}/docs-ci.project.cmake" "docs_${content}\n)\n")
          macro(list_pop_front list out)
            list(GET "${list}" 0 "${out}")
            list(REMOVE_AT "${list}" 0)
          endmacro()
          function(docs_project name)
            cmake_parse_arguments(PARSE_ARGV 1 "" "" "VERSION;DESCRIPTION;HOMEPAGE_URL" LANGUAGES)
            set(PROJECT_NAME "${name}" PARENT_SCOPE)
            if(DEFINED _VERSION)
              set(PROJECT_VERSION "${_VERSION}" PARENT_SCOPE)
              string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" versions "${_VERSION}")
              string(REPLACE . ";" versions "${versions}")
              set(suffixes MAJOR MINOR PATCH TWEAK)
              while(NOT versions STREQUAL "" AND NOT suffixes STREQUAL "")
                list_pop_front(versions version)
                list_pop_front(suffixes suffix)
                set("PROJECT_VERSION_${suffix}" "${version}" PARENT_SCOPE)
              endwhile()
            endif()
            if(DEFINED _DESCRIPTION)
              set(PROJECT_DESCRIPTION "${_DESCRIPTION}" PARENT_SCOPE)
            endif()
            if(DEFINED _HOMEPAGE_URL)
              set(PROJECT_HOMEPAGE_URL "${_HOMEPAGE_URL}" PARENT_SCOPE)
            endif()
          endfunction()
          include("${bin}/docs-ci.project.cmake")
          # ---- Generate docs ----
          if(NOT DEFINED DOXYGEN_OUTPUT_DIRECTORY)
            set(DOXYGEN_OUTPUT_DIRECTORY "${bin}/docs")
          endif()
          set(out "${DOXYGEN_OUTPUT_DIRECTORY}")
          foreach(file IN ITEMS Doxyfile conf.py)
            configure_file("${src}/docs/${file}.in" "${bin}/docs/${file}" @ONLY)
          endforeach()
          set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py")
          set(config "${bin}/docs/conf.py")
          file(REMOVE_RECURSE "${out}/html" "${out}/xml")
          execute_process(
              COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}"
              WORKING_DIRECTORY "${bin}/docs"
              RESULT_VARIABLE result
          )
          if(NOT result EQUAL "0")
            message(FATAL_ERROR "m.css returned with ${result}")
          endif()
        
        diff --git a/ cmake/docs.cmake b/ cmake/docs.cmake
@@ -0,0 +1,46 @@
# ---- Dependencies ----
          set(extract_timestamps "")
          if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24")
            set(extract_timestamps DOWNLOAD_EXTRACT_TIMESTAMP YES)
          endif()
          include(FetchContent)
          FetchContent_Declare(
              mcss URL
              https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip
              URL_MD5 00cd2757ebafb9bcba7f5d399b3bec7f
              SOURCE_DIR "${PROJECT_BINARY_DIR}/mcss"
              UPDATE_DISCONNECTED YES
              ${extract_timestamps}
          )
          FetchContent_MakeAvailable(mcss)
          find_package(Python3 3.6 REQUIRED)
          # ---- Declare documentation target ----
          set(
              DOXYGEN_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/docs"
              CACHE PATH "Path for the generated Doxygen documentation"
          )
          set(working_dir "${PROJECT_BINARY_DIR}/docs")
          foreach(file IN ITEMS Doxyfile conf.py)
            configure_file("docs/${file}.in" "${working_dir}/${file}" @ONLY)
          endforeach()
          set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py")
          set(config "${working_dir}/conf.py")
          add_custom_target(
              docs
              COMMAND "${CMAKE_COMMAND}" -E remove_directory
              "${DOXYGEN_OUTPUT_DIRECTORY}/html"
              "${DOXYGEN_OUTPUT_DIRECTORY}/xml"
              COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}"
              COMMENT "Building documentation using Doxygen and m.css"
              WORKING_DIRECTORY "${working_dir}"
              VERBATIM
          )
        
        diff --git a/ cmake/folders.cmake b/ cmake/folders.cmake
@@ -0,0 +1,21 @@
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
          # Call this function at the end of a directory scope to assign a folder to
          # targets created in that directory. Utility targets will be assigned to the
          # UtilityTargets folder, otherwise to the ${name}Targets folder. If a target
          # already has a folder assigned, then that target will be skipped.
          function(add_folders name)
            get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
            foreach(target IN LISTS targets)
              get_property(folder TARGET "${target}" PROPERTY FOLDER)
              if(DEFINED folder)
                continue()
              endif()
              set(folder Utility)
              get_property(type TARGET "${target}" PROPERTY TYPE)
              if(NOT type STREQUAL "UTILITY")
                set(folder "${name}")
              endif()
              set_property(TARGET "${target}" PROPERTY FOLDER "${folder}Targets")
            endforeach()
          endfunction()
        
        diff --git a/ cmake/install-config.cmake b/ cmake/install-config.cmake
@@ -0,0 +1,1 @@
include("${CMAKE_CURRENT_LIST_DIR}/git2wrapTargets.cmake")
        
        diff --git a/ cmake/install-rules.cmake b/ cmake/install-rules.cmake
@@ -0,0 +1,72 @@
if(PROJECT_IS_TOP_LEVEL)
            set(
                CMAKE_INSTALL_INCLUDEDIR "include/git2wrap-${PROJECT_VERSION}"
                CACHE STRING ""
            )
            set_property(CACHE CMAKE_INSTALL_INCLUDEDIR PROPERTY TYPE PATH)
          endif()
          include(CMakePackageConfigHelpers)
          include(GNUInstallDirs)
          # find_package(<package>) call for consumers to find this project
          set(package git2wrap)
          install(
              DIRECTORY
              include/
              "${PROJECT_BINARY_DIR}/export/"
              DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
              COMPONENT git2wrap_Development
          )
          install(
              TARGETS git2wrap_git2wrap
              EXPORT git2wrapTargets
              RUNTIME #
              COMPONENT git2wrap_Runtime
              LIBRARY #
              COMPONENT git2wrap_Runtime
              NAMELINK_COMPONENT git2wrap_Development
              ARCHIVE #
              COMPONENT git2wrap_Development
              INCLUDES #
              DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
          )
          write_basic_package_version_file(
              "${package}ConfigVersion.cmake"
              COMPATIBILITY SameMajorVersion
          )
          # Allow package maintainers to freely override the path for the configs
          set(
              git2wrap_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package}"
              CACHE STRING "CMake package config location relative to the install prefix"
          )
          set_property(CACHE git2wrap_INSTALL_CMAKEDIR PROPERTY TYPE PATH)
          mark_as_advanced(git2wrap_INSTALL_CMAKEDIR)
          install(
              FILES cmake/install-config.cmake
              DESTINATION "${git2wrap_INSTALL_CMAKEDIR}"
              RENAME "${package}Config.cmake"
              COMPONENT git2wrap_Development
          )
          install(
              FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake"
              DESTINATION "${git2wrap_INSTALL_CMAKEDIR}"
              COMPONENT git2wrap_Development
          )
          install(
              EXPORT git2wrapTargets
              NAMESPACE git2wrap::
              DESTINATION "${git2wrap_INSTALL_CMAKEDIR}"
              COMPONENT git2wrap_Development
          )
          if(PROJECT_IS_TOP_LEVEL)
            include(CPack)
          endif()
        
        diff --git a/ cmake/lint-targets.cmake b/ cmake/lint-targets.cmake
@@ -0,0 +1,34 @@
set(
              FORMAT_PATTERNS
              source/*.cpp source/*.hpp
              include/*.hpp
              test/*.cpp test/*.hpp
              example/*.cpp example/*.hpp
              CACHE STRING
              "; separated patterns relative to the project source dir to format"
          )
          set(FORMAT_COMMAND clang-format CACHE STRING "Formatter to use")
          add_custom_target(
              format-check
              COMMAND "${CMAKE_COMMAND}"
              -D "FORMAT_COMMAND=${FORMAT_COMMAND}"
              -D "PATTERNS=${FORMAT_PATTERNS}"
              -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Linting the code"
              VERBATIM
          )
          add_custom_target(
              format-fix
              COMMAND "${CMAKE_COMMAND}"
              -D "FORMAT_COMMAND=${FORMAT_COMMAND}"
              -D "PATTERNS=${FORMAT_PATTERNS}"
              -D FIX=YES
              -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Fixing the code"
              VERBATIM
          )
        
        diff --git a/ cmake/lint.cmake b/ cmake/lint.cmake
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.14)
          macro(default name)
            if(NOT DEFINED "${name}")
              set("${name}" "${ARGN}")
            endif()
          endmacro()
          default(FORMAT_COMMAND clang-format)
          default(
              PATTERNS
              source/*.cpp source/*.hpp
              include/*.hpp
              test/*.cpp test/*.hpp
              example/*.cpp example/*.hpp
          )
          default(FIX NO)
          set(flag --output-replacements-xml)
          set(args OUTPUT_VARIABLE output)
          if(FIX)
            set(flag -i)
            set(args "")
          endif()
          file(GLOB_RECURSE files ${PATTERNS})
          set(badly_formatted "")
          set(output "")
          string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length)
          foreach(file IN LISTS files)
            execute_process(
                COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}"
                WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
                RESULT_VARIABLE result
                ${args}
            )
            if(NOT result EQUAL "0")
              message(FATAL_ERROR "'${file}': formatter returned with ${result}")
            endif()
            if(NOT FIX AND output MATCHES "\n<replacement offset")
              string(SUBSTRING "${file}" "${path_prefix_length}" -1 relative_file)
              list(APPEND badly_formatted "${relative_file}")
            endif()
            set(output "")
          endforeach()
          if(NOT badly_formatted STREQUAL "")
            list(JOIN badly_formatted "\n" bad_list)
            message("The following files are badly formatted:\n\n${bad_list}\n")
            message(FATAL_ERROR "Run again with FIX=YES to fix these files.")
          endif()
        
        diff --git a/ cmake/prelude.cmake b/ cmake/prelude.cmake
@@ -0,0 +1,10 @@
# ---- In-source guard ----
          if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
            message(
                FATAL_ERROR
                "In-source builds are not supported. "
                "Please read the BUILDING document before trying to build this project. "
                "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first."
            )
          endif()
        
        diff --git a/ cmake/project-is-top-level.cmake b/ cmake/project-is-top-level.cmake
@@ -0,0 +1,6 @@
# This variable is set by project() in CMake 3.21+
          string(
              COMPARE EQUAL
              "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}"
              PROJECT_IS_TOP_LEVEL
          )
        
        diff --git a/ cmake/spell-targets.cmake b/ cmake/spell-targets.cmake
@@ -0,0 +1,22 @@
set(SPELL_COMMAND codespell CACHE STRING "Spell checker to use")
          add_custom_target(
              spell-check
              COMMAND "${CMAKE_COMMAND}"
              -D "SPELL_COMMAND=${SPELL_COMMAND}"
              -P "${PROJECT_SOURCE_DIR}/cmake/spell.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Checking spelling"
              VERBATIM
          )
          add_custom_target(
              spell-fix
              COMMAND "${CMAKE_COMMAND}"
              -D "SPELL_COMMAND=${SPELL_COMMAND}"
              -D FIX=YES
              -P "${PROJECT_SOURCE_DIR}/cmake/spell.cmake"
              WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
              COMMENT "Fixing spelling errors"
              VERBATIM
          )
        
        diff --git a/ cmake/spell.cmake b/ cmake/spell.cmake
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.14)
          macro(default name)
            if(NOT DEFINED "${name}")
              set("${name}" "${ARGN}")
            endif()
          endmacro()
          default(SPELL_COMMAND codespell)
          default(FIX NO)
          set(flag "")
          if(FIX)
            set(flag -w)
          endif()
          execute_process(
              COMMAND "${SPELL_COMMAND}" ${flag}
              WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
              RESULT_VARIABLE result
          )
          if(result EQUAL "65")
            message(FATAL_ERROR "Run again with FIX=YES to fix these errors.")
          elseif(result EQUAL "64")
            message(FATAL_ERROR "Spell checker printed the usage info. Bad arguments?")
          elseif(NOT result EQUAL "0")
            message(FATAL_ERROR "Spell checker returned with ${result}")
          endif()
        
        diff --git a/ cmake/variables.cmake b/ cmake/variables.cmake
@@ -0,0 +1,41 @@
# ---- Developer mode ----
          # Developer mode enables targets and code paths in the CMake scripts that are
          # only relevant for the developer(s) of git2wrap
          # Targets necessary to build the project must be provided unconditionally, so
          # consumers can trivially build and package the project
          if(PROJECT_IS_TOP_LEVEL)
            option(git2wrap_DEVELOPER_MODE "Enable developer mode" OFF)
            option(BUILD_SHARED_LIBS "Build shared libs." OFF)
          endif()
          # ---- Suppress C4251 on Windows ----
          # Please see include/git2wrap/git2wrap.hpp for more details
          set(pragma_suppress_c4251 "
          /* This needs to suppress only for MSVC */
          #if defined(_MSC_VER) && !defined(__ICL)
          #  define GIT2WRAP_SUPPRESS_C4251 _Pragma(\"warning(suppress:4251)\")
          #else
          #  define GIT2WRAP_SUPPRESS_C4251
          #endif
          ")
          # ---- Warning guard ----
          # target_include_directories with the SYSTEM modifier will request the compiler
          # to omit warnings from the provided paths, if the compiler supports that
          # This is to provide a user experience similar to find_package when
          # add_subdirectory or FetchContent is used to consume this project
          set(warning_guard "")
          if(NOT PROJECT_IS_TOP_LEVEL)
            option(
                git2wrap_INCLUDES_WITH_SYSTEM
                "Use SYSTEM modifier for git2wrap's includes, disabling warnings"
                ON
            )
            mark_as_advanced(git2wrap_INCLUDES_WITH_SYSTEM)
            if(git2wrap_INCLUDES_WITH_SYSTEM)
              set(warning_guard SYSTEM)
            endif()
          endif()
        
        diff --git a/ docs/Doxyfile.in b/ docs/Doxyfile.in
@@ -0,0 +1,32 @@
# Configuration for Doxygen for use with CMake
          # Only options that deviate from the default are included
          # To create a new Doxyfile containing all available options, call `doxygen -g`
          # Get Project name and version from CMake
          PROJECT_NAME = "@PROJECT_NAME@"
          PROJECT_NUMBER = "@PROJECT_VERSION@"
          # Add sources
          INPUT = "@PROJECT_SOURCE_DIR@/README.md" "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@/docs/pages"
          EXTRACT_ALL = YES
          RECURSIVE = YES
          OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIRECTORY@"
          # Use the README as a main page
          USE_MDFILE_AS_MAINPAGE = "@PROJECT_SOURCE_DIR@/README.md"
          # set relative include paths
          FULL_PATH_NAMES = YES
          STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@"
          STRIP_FROM_INC_PATH =
          # We use m.css to generate the html documentation, so we only need XML output
          GENERATE_XML = YES
          GENERATE_HTML = NO
          GENERATE_LATEX = NO
          XML_PROGRAMLISTING = NO
          CREATE_SUBDIRS = NO
          # Include all directories, files and namespaces in the documentation
          # Disable to include only explicitly documented objects
          M_SHOW_UNDOCUMENTED = YES
        
        diff --git a/ docs/conf.py.in b/ docs/conf.py.in
@@ -0,0 +1,6 @@
DOXYFILE = 'Doxyfile'
          LINKS_NAVBAR1 = [
              (None, 'pages', [(None, 'about')]),
              (None, 'namespaces', []),
          ]
        
        diff --git a/ docs/pages/about.dox b/ docs/pages/about.dox
@@ -0,0 +1,7 @@
/**
           * @page about About
           * @section about-doxygen Doxygen documentation
           * This page is auto generated using
           * <a href="https://www.doxygen.nl/">Doxygen</a>, making use of some useful
           * <a href="https://www.doxygen.nl/manual/commands.html">special commands</a>.
           */
        
        diff --git a/ example/CMakeLists.txt b/ example/CMakeLists.txt
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.14)
          project(git2wrapExamples CXX)
          include(../cmake/project-is-top-level.cmake)
          include(../cmake/folders.cmake)
          if(PROJECT_IS_TOP_LEVEL)
            find_package(git2wrap REQUIRED)
          endif()
          add_custom_target(run-examples)
          function(add_example NAME)
            add_executable("${NAME}" "${NAME}.cpp")
            target_link_libraries("${NAME}" PRIVATE git2wrap::git2wrap)
            target_compile_features("${NAME}" PRIVATE cxx_std_20)
            add_custom_target("run_${NAME}" COMMAND "${NAME}" VERBATIM)
            add_dependencies("run_${NAME}" "${NAME}")
            add_dependencies(run-examples "run_${NAME}")
          endfunction()
          add_example(empty_example)
          add_folders(Example)
        
        diff --git a/ example/empty_example.cpp b/ example/empty_example.cpp
@@ -0,0 +1,4 @@
auto main() -> int
          {
            return 0;
          }
        
        diff --git a/ include/git2wrap/git2wrap.hpp b/ include/git2wrap/git2wrap.hpp
@@ -0,0 +1,70 @@
#pragma once
          #include <string>
          #include "git2wrap/git2wrap_export.hpp"
          /**
           * A note about the MSVC warning C4251:
           * This warning should be suppressed for private data members of the project's
           * exported classes, because there are too many ways to work around it and all
           * involve some kind of trade-off (increased code complexity requiring more
           * developer time, writing boilerplate code, longer compile times), but those
           * solutions are very situational and solve things in slightly different ways,
           * depending on the requirements of the project.
           * That is to say, there is no general solution.
           *
           * What can be done instead is understand where issues could arise where this
           * warning is spotting a legitimate bug. I will give the general description of
           * this warning's cause and break it down to make it trivial to understand.
           *
           * C4251 is emitted when an exported class has a non-static data member of a
           * non-exported class type.
           *
           * The exported class in our case is the class below (exported_class), which
           * has a non-static data member (m_name) of a non-exported class type
           * (std::string).
           *
           * The rationale here is that the user of the exported class could attempt to
           * access (directly, or via an inline member function) a static data member or
           * a non-inline member function of the data member, resulting in a linker
           * error.
           * Inline member function above means member functions that are defined (not
           * declared) in the class definition.
           *
           * Since this exported class never makes these non-exported types available to
           * the user, we can safely ignore this warning. It's fine if there are
           * non-exported class types as private member variables, because they are only
           * accessed by the members of the exported class itself.
           *
           * The name() method below returns a pointer to the stored null-terminated
           * string as a fundamental type (char const), so this is safe to use anywhere.
           * The only downside is that you can have dangling pointers if the pointer
           * outlives the class instance which stored the string.
           *
           * Shared libraries are not easy, they need some discipline to get right, but
           * they also solve some other problems that make them worth the time invested.
           */
          /**
           * @brief Reports the name of the library
           *
           * Please see the note above for considerations when creating shared libraries.
           */
          class GIT2WRAP_EXPORT exported_class
          {
          public:
            /**
             * @brief Initializes the name field to the name of the project
             */
            exported_class();
            /**
             * @brief Returns a non-owning pointer to the string stored in this class
             */
            auto name() const -> char const*;
          private:
            GIT2WRAP_SUPPRESS_C4251
            std::string m_name;
          };
        
        diff --git a/ source/git2wrap.cpp b/ source/git2wrap.cpp
@@ -0,0 +1,13 @@
#include <string>
          #include "git2wrap/git2wrap.hpp"
          exported_class::exported_class()
              : m_name {"git2wrap"}
          {
          }
          auto exported_class::name() const -> char const*
          {
            return m_name.c_str();
          }
        
        diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.14)
          project(git2wrapTests LANGUAGES CXX)
          include(../cmake/project-is-top-level.cmake)
          include(../cmake/folders.cmake)
          # ---- Dependencies ----
          if(PROJECT_IS_TOP_LEVEL)
            find_package(git2wrap REQUIRED)
            enable_testing()
          endif()
          # ---- Tests ----
          add_executable(git2wrap_test source/git2wrap_test.cpp)
          target_link_libraries(git2wrap_test PRIVATE git2wrap::git2wrap)
          target_compile_features(git2wrap_test PRIVATE cxx_std_20)
          add_test(NAME git2wrap_test COMMAND git2wrap_test)
          # ---- End-of-file commands ----
          add_folders(Test)
        
        diff --git a/ test/source/git2wrap_test.cpp b/ test/source/git2wrap_test.cpp
@@ -0,0 +1,10 @@
#include <string>
          #include "git2wrap/git2wrap.hpp"
          auto main() -> int
          {
            auto const exported = exported_class {};
            return std::string("git2wrap") == exported.name() ? 0 : 1;
          }