diff --git a/.editorconfig b/.editorconfig index 096ff2565..2e54d0f2d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,9 @@ trim_trailing_whitespace = true insert_final_newline = true ; Not change VS generated files -[*.{sln,csroj}] +[*.{sln,csproj}] trim_trailing_whitespace = false insert_final_newline = false + +[*.{props,targets,csproj,config}] +indent_size = 2 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 000000000..c37520864 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,17 @@ +You are opening a _bug report_ against the LibGit2Sharp project: we +use GitHub Issues for tracking bug reports and feature requests. If +you have a question about an API or usage, please ask on StackOverflow: +http://stackoverflow.com/questions/tagged/libgit2sharp. + +Otherwise, to report a bug, please fill out the reproduction steps +(below) and delete these introductory paragraphs. Thanks! + +### Reproduction steps + +### Expected behavior + +### Actual behavior + +### Version of LibGit2Sharp (release number or SHA1) + +### Operating system(s) tested; .NET runtime tested diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..54837ac35 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,102 @@ +name: CI +on: + push: + branches: [master, release-*] + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+-*' + pull_request: + workflow_dispatch: +env: + DOTNET_NOLOGO: true +jobs: + build: + name: Build + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4.1.2 + with: + fetch-depth: 0 + - name: Install .NET SDK + uses: actions/setup-dotnet@v4.0.0 + with: + dotnet-version: 9.0.x + - name: Build + run: dotnet build LibGit2Sharp.sln --configuration Release + - name: Upload packages + uses: actions/upload-artifact@v4.3.1 + with: + name: NuGet packages + path: artifacts/package/ + retention-days: 7 + - name: Verify trimming compatibility + run: dotnet publish TrimmingTestApp + test: + name: Test / ${{ matrix.os }} / ${{ matrix.arch }} / ${{ matrix.tfm }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + arch: [ x64 ] + os: [ windows-2019, windows-2022, macos-13 ] + tfm: [ net472, net8.0, net9.0 ] + exclude: + - os: macos-13 + tfm: net472 + include: + - arch: arm64 + os: macos-14 + tfm: net8.0 + - arch: arm64 + os: macos-14 + tfm: net9.0 + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v4.1.2 + with: + fetch-depth: 0 + - name: Install .NET SDK + uses: actions/setup-dotnet@v4.0.0 + with: + dotnet-version: | + 9.0.x + 8.0.x + - name: Run ${{ matrix.tfm }} tests + run: dotnet test LibGit2Sharp.sln --configuration Release --framework ${{ matrix.tfm }} --logger "GitHubActions" /p:ExtraDefine=LEAKS_IDENTIFYING + test-linux: + name: Test / ${{ matrix.distro }} / ${{ matrix.arch }} / ${{ matrix.tfm }} + runs-on: ${{ matrix.runnerImage }} + strategy: + matrix: + arch: [ amd64, arm64 ] + distro: [ alpine.3.17, alpine.3.18, alpine.3.19, alpine.3.20, centos.stream.9, debian.12, fedora.40, ubuntu.20.04, ubuntu.22.04, ubuntu.24.04 ] + sdk: [ '8.0', '9.0' ] + exclude: + - distro: alpine.3.17 + sdk: '9.0' + - distro: alpine.3.18 + sdk: '9.0' + - distro: alpine.3.19 + sdk: '9.0' + include: + - sdk: '8.0' + tfm: net8.0 + - sdk: '9.0' + tfm: net9.0 + - arch: amd64 + runnerImage: ubuntu-22.04 + - arch: arm64 + runnerImage: ubuntu-22.04-arm + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v4.1.2 + with: + fetch-depth: 0 + - name: Run ${{ matrix.tfm }} tests + run: | + git_command="git config --global --add safe.directory /app" + test_command="dotnet test LibGit2Sharp.sln --configuration Release -p:TargetFrameworks=${{ matrix.tfm }} --logger "GitHubActions" -p:ExtraDefine=LEAKS_IDENTIFYING" + docker run -t --rm --platform linux/${{ matrix.arch }} -v "$PWD:/app" -e OPENSSL_ENABLE_SHA1_SIGNATURES=1 gittools/build-images:${{ matrix.distro }}-sdk-${{ matrix.sdk }} sh -c "$git_command && $test_command" + diff --git a/.gitignore b/.gitignore index 2009f0208..32e17b4d0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,10 +36,7 @@ _ReSharper*/ *.userprefs *.swp *.DotSettings -#Ignore custom generated files -LibGit2Sharp/Core/UniqueIdentifier.cs -LibGit2Sharp/Core/NativeDllName.cs -!nuget.package/build/ _NCrunch_LibGit2Sharp/ -packages/ +artifacts/ +worktree.playlist diff --git a/.nuget/packages.config b/.nuget/packages.config deleted file mode 100644 index 57b9c468a..000000000 --- a/.nuget/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d09406bbe..000000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Travis-CI Build for libgit2sharp -# see travis-ci.org for details - -language: csharp -mono: - - 4.8.0 - -os: - - osx - - linux - -env: - global: - - MONO_OPTIONS=--debug - -install: - - curl -L -o nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - - mono nuget.exe restore LibGit2Sharp.sln - -before_install: - - date -u - - uname -a - - env | sort - -solution: LibGit2Sharp.sln - -# Build libgit2, LibGit2Sharp and run the tests -script: - - ./build.libgit2sharp.sh 'LEAKS_IDENTIFYING' - -# Only watch the development branch -branches: - only: - - master - - /^maint.*/ - -# Notify of build changes -notifications: - email: - - emeric.fermas@gmail.com diff --git a/CHANGES.md b/CHANGES.md index 790f7a84f..a00b598d7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,22 +1,127 @@ # LibGit2Sharp Changes -**LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .Net and Mono.** +## v0.31 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.30.0..0.31.0)) - - Source code: - - NuGet package: - - Issue tracker: - - @libgit2sharp: - - CI servers: - - Windows (x86/amd64): - - Linux/Mac OS X: +### Changes +- This release includes [libgit2 v1.8.4](https://github.com/libgit2/libgit2/releases/tag/v1.8.4). + - SSH is now supported through [libgit2's support for OpenSSH](https://github.com/libgit2/libgit2/pull/6617). +- The ppc64le architecture is now supported on Linux. +- .NET 6 has reached end of support, so LibGit2Sharp now targets `net472` and `net8.0`. + +### Additions +- Adds Depth to FetchOptions allowing for shallow cloning [#2070](https://github.com/libgit2/libgit2sharp/pull/2070) +- Make owner validation configurable [#2093](https://github.com/libgit2/libgit2sharp/pull/2093) +- Add a CloneOptions constructor that takes a FetchOptions [#2132](https://github.com/libgit2/libgit2sharp/pull/2132) + +### Fixes +- TreeDefinition.Remove fails to remove unwrapped trees [#1869](https://github.com/libgit2/libgit2sharp/issues/1869) +- ObjectDatabase.Write(Stream stream...) overload does not respect T [#2071](https://github.com/libgit2/libgit2sharp/issues/2071) +- Repository.Worktrees.Add leaves now worktree empty [#2037](https://github.com/libgit2/libgit2sharp/issues/2037) + +## v0.30 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.29.0..0.30.0)) + +### Changes +- This release includes [libgit2 v1.7.2](https://github.com/libgit2/libgit2/releases/tag/v1.7.2). +- Updates for trimming compatibility [#2084](https://github.com/libgit2/libgit2sharp/pull/2084) +- Updates for .NET 8 [#2085](https://github.com/libgit2/libgit2sharp/pull/2085) + +## v0.29 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.28.0..0.29.0)) + +### Changes +- This release includes [libgit2 v1.7.1](https://github.com/libgit2/libgit2/releases/tag/v1.7.1). + - CI changes for the native binaries has removed support for CentOS 7. See [#2066](https://github.com/libgit2/libgit2sharp/pull/2066) for details. + +### Additions +- Add proxy options [#2065](https://github.com/libgit2/libgit2sharp/pull/2065) + - See PR for details, including some breaking changes to `CloneOptions` and `SubmoduleUpdateOptions` -## v0.24 + 1 +## v0.28 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.27.2..0.28.0)) ### Additions +- Add CustomHeaders to PushOptions [#2052](https://github.com/libgit2/libgit2sharp/pull/2052) + +## v0.27.2 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.27.1..0.27.2)) ### Changes +- This release includes [libgit2 v1.6.4](https://github.com/libgit2/libgit2/releases/tag/v1.6.4). + +### Fixes +- Can't access GIT config (Repository.Config) since v0.27.0 [#2031](https://github.com/libgit2/libgit2sharp/issues/2031) + +## v0.27.1 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.27.0..0.27.1)) ### Fixes +- AssemblyVersion of v0.27.0 is `0.0.0.0`, which is lower than the AssemblyVersion of the v0.26.x releases. [#2030](https://github.com/libgit2/libgit2sharp/pull/2030) + +## v0.27 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.26..0.27.0)) + +### Changes +- LibGit2Sharp now targets .NET Framework 4.7.2 and .NET 6. +- This release includes [libgit2 v1.6.3](https://github.com/libgit2/libgit2/releases/tag/v1.6.3). +- Changes to the native binaries let LibGit2Sharp work on all [.NET 6 supported OS versions and architectures](https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md). +- `GlobalSetings.NativeLibraryPath` used to automatically append architecture to the path when running on .NET Framework. This behavior has been removed to make it consistent. [#1918](https://github.com/libgit2/libgit2sharp/pull/1918) + +### Additions +- Add support for adding and clearing multi-valued configuration [#1720](https://github.com/libgit2/libgit2sharp/pull/1720) +- added lines and deleted lines in content changes [#1790](https://github.com/libgit2/libgit2sharp/pull/1790) +- Set / get supported extensions [#1908](https://github.com/libgit2/libgit2sharp/pull/1908) +- Simplify dealing with missing git objects [#1909](https://github.com/libgit2/libgit2sharp/pull/1909) +- Throw NotFoundException if trees are missing when computing diff [#1936](https://github.com/libgit2/libgit2sharp/pull/1936) + +### Fixes +- Adjust GitStatusOptions to match structure of native libgit2 [#1884](https://github.com/libgit2/libgit2sharp/pull/1884) +- Update git_worktree_add_options struct to include ref pointer [#1890](https://github.com/libgit2/libgit2sharp/pull/1890) +- Fix git_remote_connect not throwing on non-zero result [#1913](https://github.com/libgit2/libgit2sharp/pull/1913) +- Fix incorrect information in exceptions [#1919](https://github.com/libgit2/libgit2sharp/pull/1919) +- Checkout branch looks to remote tracking branches as fallback [#1820](https://github.com/libgit2/libgit2sharp/pull/1820) +- Fixed calling into native libgit2 on osx-arm64 [#1955](https://github.com/libgit2/libgit2sharp/pull/1955) + +## v0.26 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.25..v0.26)) + +### Additions + +* Add `CherryPickCommitIntoIndex` to `ObjectDatabase` +* The underlying native library (libgit2) now no longer relies on libcurl +* The underlying native library now no longer relies on zlib +* Add `IndentHeuristic` option to `CompareOptions` + +## v0.25 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.24..v0.25)) + +LibGit2Sharp is now .NET Core 2.0+ and .NET Framework compatible. + +### Additions + + - `GitObject` now has a `Peel` method that will let you peel (for example) + a `Tag` to a `Tree`. + - `MergeOptions` now includes an option to `IgnoreWhitespaceChanges`. + - `TreeDefinition` can now `Add` an object with only the ID, which allows + users of large files to add entries without realizing a `Blob`. + - `ObjectDatabase` can now `Write` a `Stream`, which allows users of + large files to stream an object into storage without loading it into + memory. + - `ObjectDatabase` can now `MergeCommitsIntoIndex` allowing users to perform + an in-memory merge that produces an `Index` structure with conflicts. + - Users can enable or disable dependent object existence checks when + creating new objects with `GlobalSettings.SetEnableStrictObjectCreation` + - Users can enable or disable `ofs_delta` support with + `GlobalSettings.SetEnableOfsDelta` + +### Changes + + - Status now does not show untracked files by default. To retrieve + untracked files, included the `StatusOptions.IncludeUntracked` and/or + the `StatusOptions.RecurseUntrackedDirs` options. + - Status now does not show the ignored files by default. To retrieve + ignored files, include the `StatusOptions.IncludeIgnored` option. + - `Commands.Pull` can now provide a `null` value for `PullOptions`, + which indicates that default values should be used. + +### Fixes + + - The exception thrown when the native library cannot be loaded is now + able to be caught and will no longer crash the process. + - Getting the `Notes` collection from a `Repository` no longer throws an + exception when the repository has no notes. ## v0.24 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.23..v0.24)) diff --git a/CI/build.msbuild b/CI/build.msbuild deleted file mode 100644 index d4b0faae4..000000000 --- a/CI/build.msbuild +++ /dev/null @@ -1,58 +0,0 @@ - - - Release - $(MSBuildProjectDirectory)\.. - $(RootDir)\LibGit2Sharp.Tests\bin\$(Configuration) - $(RootDir)\Build - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d487a49aa..218cb2a28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ We love Pull Requests! Your contributions help make LibGit2Sharp great. ## Getting Started -So you want to contribute to LibGit2Sharp. Great! Contributions take many forms from +So you want to contribute to LibGit2Sharp. Great! Contributions take many forms from submitting issues, writing documentation, to making code changes. We welcome it all. But first things first... @@ -14,18 +14,25 @@ But first things first... * Clearly describe the issue including steps to reproduce when it is a bug. * Make sure you fill in the earliest version that you know has the issue. * Fork the repository on GitHub, then clone it using your favorite Git client. -* Make sure the project builds and all tests pass on your machine by running - the `build.libgit2sharp.cmd` script on Windows or `build.libgit2sharp.sh` on Linux/Mac. +* Make sure the project builds and all tests pass on your machine by running + the `buildandtest.cmd` script on Windows or `buildandtest.sh` on Linux/Mac. ## LibGit2 LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .Net and Mono. -LibGit2 is a git submodule referencing the [libgit2 project](https://github.com/libgit2/libgit2). To learn more about +LibGit2 is a git submodule referencing the [libgit2 project](https://github.com/libgit2/libgit2). To learn more about submodules read [here](http://git-scm.com/book/en/v2/Git-Tools-Submodules). To build libgit2 see [here](https://github.com/libgit2/libgit2sharp/wiki/How-to-build-x64-libgit2-and-LibGit2Sharp). ## Making Changes +Make sure you have the required .NET Core SDK and runtimes installed. +The easiest way to do this is run our `tools\Install-DotNetSdk.ps1` script. +Using the `-InstallLocality Machine` switch requires elevation but ensures +that Visual Studio will be able to load the solution even when launched from a shortcut. + +Then proceed to: + * Create a topic branch off master (don't work directly on master). * Implement your feature or fix your bug. Please following existing coding styles and do not introduce new ones. * Make atomic, focused commits with good commit messages. @@ -42,10 +49,10 @@ Some things that will increase the chance that your pull request is accepted. * Following existing code conventions. * Including unit tests that would otherwise fail without the patch, but pass after applying it. * Updating the documentation and tests that are affected by the contribution. -* If code from elsewhere is used, proper credit and a link to the source should exist in the code comments. +* If code from elsewhere is used, proper credit and a link to the source should exist in the code comments. Then licensing issues can be checked against LibGit2Sharp's very permissive MIT based open source license. * Having a configured git client that converts line endings to LF. [See here.](https://help.github.com/articles/dealing-with-line-endings/). # Additional Resources * [General GitHub documentation](http://help.github.com/) -* [GitHub pull request documentation](https://help.github.com/articles/using-pull-requests/) \ No newline at end of file +* [GitHub pull request documentation](https://help.github.com/articles/using-pull-requests/) diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..2c14cc2bd --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,14 @@ + + + + true + true + true + $(DefineConstants);$(ExtraDefine) + + + + true + + + diff --git a/Lib/.gitattributes b/Lib/.gitattributes deleted file mode 100644 index 2fa88711b..000000000 --- a/Lib/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -* binary -.gitattributes text -binary -*.txt text -binary diff --git a/Lib/CustomBuildTasks/CustomBuildTasks.csproj b/Lib/CustomBuildTasks/CustomBuildTasks.csproj deleted file mode 100644 index 351e4873f..000000000 --- a/Lib/CustomBuildTasks/CustomBuildTasks.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - - Debug - AnyCPU - {B6138573-A4B9-44E7-83C2-8964CAF51EDA} - Library - Properties - CustomBuildTasks - CustomBuildTasks - v4.0 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - diff --git a/Lib/CustomBuildTasks/CustomBuildTasks.dll b/Lib/CustomBuildTasks/CustomBuildTasks.dll deleted file mode 100644 index cd763a2e1..000000000 Binary files a/Lib/CustomBuildTasks/CustomBuildTasks.dll and /dev/null differ diff --git a/Lib/CustomBuildTasks/GenerateNativeDllNameTask.cs b/Lib/CustomBuildTasks/GenerateNativeDllNameTask.cs deleted file mode 100644 index 9b31fba34..000000000 --- a/Lib/CustomBuildTasks/GenerateNativeDllNameTask.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.IO; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace CustomBuildTasks -{ - public class GenerateNativeDllNameTask : Task - { - public ITaskItem InputHashFile { get; set; } - - public string OutputFile { get; set; } - - public override bool Execute() - { - var fileName = InputHashFile.GetMetadata("FullPath"); - string libgit2FileName; - - using (var sr = new StreamReader(fileName)) - { - libgit2FileName = sr.ReadLine(); - } - - var nativeDllName = @"namespace LibGit2Sharp.Core -{{ - internal static class NativeDllName - {{ - public const string Name = ""{0}""; - }} -}} -"; - - using (var sw = new StreamWriter(OutputFile)) - { - sw.Write(nativeDllName, libgit2FileName); - } - - return true; - } - } -} diff --git a/Lib/CustomBuildTasks/GenerateUniqueIdentifierTask.cs b/Lib/CustomBuildTasks/GenerateUniqueIdentifierTask.cs deleted file mode 100644 index 2f26ac94d..000000000 --- a/Lib/CustomBuildTasks/GenerateUniqueIdentifierTask.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.IO; -using System.Text; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace CustomBuildTasks -{ - public class GenerateUniqueIdentifierTask : Task - { - public override bool Execute() - { - using (FileStream fs = new FileStream(this.OutputFile, FileMode.Create, FileAccess.Write, FileShare.None)) - using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) - { - sw.WriteLine("using System;"); - sw.WriteLine(); - sw.WriteLine("namespace LibGit2Sharp.Core"); - sw.WriteLine("{"); - sw.WriteLine(" internal static class UniqueId"); - sw.WriteLine(" {"); - sw.WriteLine(" public const String UniqueIdentifier = \"" + Guid.NewGuid().ToString() + "\";"); - sw.WriteLine(" }"); - sw.WriteLine("}"); - } - - return true; - } - - public String OutputFile - { - get; - set; - } - } -} diff --git a/Lib/NuGet/NuGet.exe b/Lib/NuGet/NuGet.exe deleted file mode 100644 index 66794573f..000000000 Binary files a/Lib/NuGet/NuGet.exe and /dev/null differ diff --git a/Lib/NuGet/NuGet.license.txt b/Lib/NuGet/NuGet.license.txt deleted file mode 100644 index 48715cacc..000000000 --- a/Lib/NuGet/NuGet.license.txt +++ /dev/null @@ -1,29 +0,0 @@ -This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. - -1. Definitions - -The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. - -A "contribution" is the original software, or any additions or changes to the software. - -A "contributor" is any person that distributes its contribution under this license. - -"Licensed patents" are a contributor's patent claims that read directly on its contribution. - -2. Grant of Rights - -(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. - -(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. - -3. Conditions and Limitations - -(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. - -(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. - -(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. - -(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. - -(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/LibGit2Sharp.Tests/ArchiveTarFixture.cs b/LibGit2Sharp.Tests/ArchiveTarFixture.cs index a21847ea0..247a9a3b0 100644 --- a/LibGit2Sharp.Tests/ArchiveTarFixture.cs +++ b/LibGit2Sharp.Tests/ArchiveTarFixture.cs @@ -30,8 +30,8 @@ public void CanArchiveACommitWithDirectoryAsTar() repo.ObjectDatabase.Archive(commit, archivePath); - using (var expectedStream = new StreamReader(Path.Combine(ResourcesDirectory.FullName, "expected_archives/commit_with_directory.tar"))) - using (var actualStream = new StreamReader(archivePath)) + using (var expectedStream = new StreamReader(File.OpenRead(Path.Combine(ResourcesDirectory.FullName, "expected_archives/commit_with_directory.tar")))) + using (var actualStream = new StreamReader(File.OpenRead(archivePath))) { string expected = expectedStream.ReadToEnd(); string actual = actualStream.ReadToEnd(); diff --git a/LibGit2Sharp.Tests/BlameFixture.cs b/LibGit2Sharp.Tests/BlameFixture.cs index 9138646c3..8cefcfb45 100644 --- a/LibGit2Sharp.Tests/BlameFixture.cs +++ b/LibGit2Sharp.Tests/BlameFixture.cs @@ -9,7 +9,7 @@ public class BlameFixture : BaseFixture { private static void AssertCorrectHeadBlame(BlameHunkCollection blame) { - Assert.Equal(1, blame.Count()); + Assert.Single(blame); Assert.Equal(0, blame[0].FinalStartLineNumber); Assert.Equal("schacon@gmail.com", blame[0].FinalSignature.Email); Assert.Equal("4a202b3", blame[0].FinalCommit.Id.ToString(7)); @@ -39,7 +39,7 @@ public void CanBlameFromADifferentCommit() Assert.Throws(() => repo.Blame("ancestor-only.txt")); var blame = repo.Blame("ancestor-only.txt", new BlameOptions { StartingAt = "9107b30" }); - Assert.Equal(1, blame.Count()); + Assert.Single(blame); } } @@ -62,10 +62,10 @@ public void CanBlameFromVariousTypes() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions {StartingAt = "HEAD" })); - AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions {StartingAt = repo.Head })); - AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions {StartingAt = repo.Head.Tip })); - AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions {StartingAt = repo.Branches["master"]})); + AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions { StartingAt = "HEAD" })); + AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions { StartingAt = repo.Head })); + AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions { StartingAt = repo.Head.Tip })); + AssertCorrectHeadBlame(repo.Blame("README", new BlameOptions { StartingAt = repo.Branches["master"] })); } } @@ -78,8 +78,8 @@ public void CanStopBlame() // $ git blame .\new.txt // 9fd738e8 (Scott Chacon 2010-05-24 10:19:19 -0700 1) my new file // (be3563a comes after 9fd738e8) - var blame = repo.Blame("new.txt", new BlameOptions {StoppingAt = "be3563a"}); - Assert.True(blame[0].FinalCommit.Sha.StartsWith("be3563a")); + var blame = repo.Blame("new.txt", new BlameOptions { StoppingAt = "be3563a" }); + Assert.StartsWith("be3563a", blame[0].FinalCommit.Sha); } } } diff --git a/LibGit2Sharp.Tests/BlobFixture.cs b/LibGit2Sharp.Tests/BlobFixture.cs index 371e50c51..314dea379 100644 --- a/LibGit2Sharp.Tests/BlobFixture.cs +++ b/LibGit2Sharp.Tests/BlobFixture.cs @@ -3,7 +3,6 @@ using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -16,6 +15,7 @@ public void CanGetBlobAsText() using (var repo = new Repository(path)) { var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + Assert.False(blob.IsMissing); var text = blob.GetContentText(); @@ -37,6 +37,7 @@ public void CanGetBlobAsFilteredText(string autocrlf, string expectedText) repo.Config.Set("core.autocrlf", autocrlf); var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + Assert.False(blob.IsMissing); var text = blob.GetContentText(new FilteringOptions("foo.txt")); @@ -44,6 +45,7 @@ public void CanGetBlobAsFilteredText(string autocrlf, string expectedText) } } +#if NETFRAMEWORK //UTF-7 is disabled in .NET 5+ [Theory] [InlineData("ascii", 4, "31 32 33 34")] [InlineData("utf-7", 4, "31 32 33 34")] @@ -67,6 +69,7 @@ public void CanGetBlobAsTextWithVariousEncodings(string encodingName, int expect var commit = repo.Commit("bom", Constants.Signature, Constants.Signature); var blob = (Blob)commit.Tree[bomFile].Target; + Assert.False(blob.IsMissing); Assert.Equal(expectedContentBytes, blob.Size); using (var stream = blob.GetContentStream()) { @@ -83,6 +86,7 @@ public void CanGetBlobAsTextWithVariousEncodings(string encodingName, int expect Assert.Equal(expectedUtf7Chars, string.Join(" ", utf7Chars)); } } +#endif [Fact] public void CanGetBlobSize() @@ -91,6 +95,7 @@ public void CanGetBlobSize() using (var repo = new Repository(path)) { var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + Assert.False(blob.IsMissing); Assert.Equal(10, blob.Size); } } @@ -103,6 +108,7 @@ public void CanLookUpBlob() { var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); Assert.NotNull(blob); + Assert.False(blob.IsMissing); } } @@ -113,6 +119,7 @@ public void CanReadBlobStream() using (var repo = new Repository(path)) { var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + Assert.False(blob.IsMissing); var contentStream = blob.GetContentStream(); Assert.Equal(blob.Size, contentStream.Length); @@ -139,6 +146,7 @@ public void CanReadBlobFilteredStream(string autocrlf, string expectedContent) repo.Config.Set("core.autocrlf", autocrlf); var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + Assert.False(blob.IsMissing); var contentStream = blob.GetContentStream(new FilteringOptions("foo.txt")); Assert.Equal(expectedContent.Length, contentStream.Length); @@ -163,6 +171,7 @@ public void CanReadBlobFilteredStreamOfUnmodifiedBinary() using (var stream = new MemoryStream(binaryContent)) { Blob blob = repo.ObjectDatabase.CreateBlob(stream); + Assert.False(blob.IsMissing); using (var filtered = blob.GetContentStream(new FilteringOptions("foo.txt"))) { @@ -185,7 +194,7 @@ public void CanStageAFileGeneratedFromABlobContentStream() var sb = new StringBuilder(); for (int j = 0; j < 2000; j++) { - sb.Append(((i + 1)*(j + 1)).ToString("X8")); + sb.Append(((i + 1) * (j + 1)).ToString("X8")); } File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, "small.txt"), sb.ToString()); } @@ -195,6 +204,7 @@ public void CanStageAFileGeneratedFromABlobContentStream() Assert.Equal("baae1fb3760a73481ced1fa03dc15614142c19ef", entry.Id.Sha); var blob = repo.Lookup(entry.Id.Sha); + Assert.False(blob.IsMissing); using (Stream stream = blob.GetContentStream()) using (Stream file = File.OpenWrite(Path.Combine(repo.Info.WorkingDirectory, "small.fromblob.txt"))) @@ -216,7 +226,32 @@ public void CanTellIfTheBlobContentLooksLikeBinary() using (var repo = new Repository(path)) { var blob = repo.Lookup("a8233120f6ad708f843d861ce2b7228ec4e3dec6"); - Assert.Equal(false, blob.IsBinary); + Assert.False(blob.IsMissing); + Assert.False(blob.IsBinary); + } + } + + [Fact] + public void CanTellIfABlobIsMissing() + { + string repoPath = SandboxBareTestRepo(); + + // Manually delete the objects directory to simulate a partial clone + Directory.Delete(Path.Combine(repoPath, "objects", "a8"), true); + + using (var repo = new Repository(repoPath)) + { + // Look up for the tree that reference the blob which is now missing + var tree = repo.Lookup("fd093bff70906175335656e6ce6ae05783708765"); + var blob = (Blob)tree["README"].Target; + + Assert.Equal("a8233120f6ad708f843d861ce2b7228ec4e3dec6", blob.Sha); + Assert.NotNull(blob); + Assert.True(blob.IsMissing); + Assert.Throws(() => blob.Size); + Assert.Throws(() => blob.IsBinary); + Assert.Throws(() => blob.GetContentText()); + Assert.Throws(() => blob.GetContentText(new FilteringOptions("foo.txt"))); } } diff --git a/LibGit2Sharp.Tests/BranchFixture.cs b/LibGit2Sharp.Tests/BranchFixture.cs index 2bc65892b..88247e256 100644 --- a/LibGit2Sharp.Tests/BranchFixture.cs +++ b/LibGit2Sharp.Tests/BranchFixture.cs @@ -4,7 +4,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -51,6 +50,19 @@ public void CanCreateBranch(string name) } } + [Theory] + [InlineData("32eab9cb1f450b5fe7ab663462b77d7f4b703344")] + public void CanHeadBeDetached(string commit) + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + Assert.False(repo.Info.IsHeadDetached); + Commands.Checkout(repo, commit); + Assert.True(repo.Info.IsHeadDetached); + } + } + [Fact] public void CanCreateAnUnbornBranch() { @@ -74,7 +86,7 @@ public void CanCreateAnUnbornBranch() Commit c = repo.Commit("New initial root commit", Constants.Signature, Constants.Signature); // Ensure this commit has no parent - Assert.Equal(0, c.Parents.Count()); + Assert.Empty(c.Parents); // The branch now exists... Branch orphan = repo.Branches["orphan"]; @@ -90,7 +102,7 @@ public void CanCreateAnUnbornBranch() public void CanCreateBranchUsingAbbreviatedSha() { string path = SandboxBareTestRepo(); - using (var repo = new Repository(path, new RepositoryOptions{ Identity = Constants.Identity })) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); @@ -262,7 +274,7 @@ public void CreatingABranchTriggersTheCreationOfADirectReference() Reference reference = repo.Refs[newBranch.CanonicalName]; Assert.NotNull(reference); - Assert.IsType(typeof(DirectReference), reference); + Assert.IsType(reference); } } @@ -563,7 +575,7 @@ public void CanGetInformationFromUnbornBranch() var head = repo.Head; Assert.Equal("refs/heads/master", head.CanonicalName); - Assert.Equal(0, head.Commits.Count()); + Assert.Empty(head.Commits); Assert.True(head.IsCurrentRepositoryHead); Assert.False(head.IsRemote); Assert.Equal("master", head.FriendlyName); @@ -988,7 +1000,7 @@ public void OnlyOneBranchIsTheHead() continue; } - Assert.True(false, string.Format("Both '{0}' and '{1}' appear to be Head.", head.CanonicalName, branch.CanonicalName)); + Assert.Fail(string.Format("Both '{0}' and '{1}' appear to be Head.", head.CanonicalName, branch.CanonicalName)); } Assert.NotNull(head); @@ -1113,7 +1125,7 @@ public void TrackedBranchExistsFromDefaultConfigInEmptyClone() using (var emptyRepo = new Repository(repoPath)) { - uri = new Uri(emptyRepo.Info.Path); + uri = new Uri($"file://{emptyRepo.Info.Path}"); } SelfCleaningDirectory scd2 = BuildSelfCleaningDirectory(); @@ -1123,7 +1135,7 @@ public void TrackedBranchExistsFromDefaultConfigInEmptyClone() using (var repo = new Repository(clonedRepoPath)) { Assert.Empty(Directory.GetFiles(scd2.RootedDirectoryPath)); - Assert.Equal(repo.Head.FriendlyName, "master"); + Assert.Equal("master", repo.Head.FriendlyName); Assert.Null(repo.Head.Tip); Assert.NotNull(repo.Head.TrackedBranch); diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index 0648ded10..045e20e1f 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -269,7 +269,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() Assert.Throws(() => Commands.Checkout(repo, master.CanonicalName)); // Checkout with force option should succeed. - Commands.Checkout(repo, master.CanonicalName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force}); + Commands.Checkout(repo, master.CanonicalName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); // Assert that master branch is checked out. Assert.True(repo.Branches["master"].IsCurrentRepositoryHead); @@ -411,7 +411,7 @@ public void CheckingOutThroughBranchCallsCheckoutProgress() Branch branch = repo.Branches[otherBranchName]; Commands.Checkout(repo, branch, - new CheckoutOptions { OnCheckoutProgress = (path, completed, total) => wasCalled = true}); + new CheckoutOptions { OnCheckoutProgress = (path, completed, total) => wasCalled = true }); Assert.True(wasCalled); } @@ -427,7 +427,7 @@ public void CheckingOutThroughRepositoryCallsCheckoutProgress() PopulateBasicRepository(repo); bool wasCalled = false; - Commands.Checkout(repo, otherBranchName, new CheckoutOptions() { OnCheckoutProgress = (path, completed, total) => wasCalled = true}); + Commands.Checkout(repo, otherBranchName, new CheckoutOptions() { OnCheckoutProgress = (path, completed, total) => wasCalled = true }); Assert.True(wasCalled); } @@ -526,13 +526,13 @@ public void CheckoutRetainsUntrackedChanges() string fullPathFileB = Touch(repo.Info.WorkingDirectory, "b.txt", alternateFileContent); // Verify that there is an untracked entry. - Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Single(repo.RetrieveStatus().Untracked); Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); Commands.Checkout(repo, otherBranchName); // Verify untracked entry still exists. - Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Single(repo.RetrieveStatus().Untracked); Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); } } @@ -550,13 +550,13 @@ public void ForceCheckoutRetainsUntrackedChanges() string fullPathFileB = Touch(repo.Info.WorkingDirectory, "b.txt", alternateFileContent); // Verify that there is an untracked entry. - Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Single(repo.RetrieveStatus().Untracked); Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); Commands.Checkout(repo, otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); // Verify untracked entry still exists. - Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Single(repo.RetrieveStatus().Untracked); Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus(fullPathFileB)); } } @@ -574,13 +574,13 @@ public void CheckoutRetainsUnstagedChanges() string fullPathFileA = Touch(repo.Info.WorkingDirectory, originalFilePath, alternateFileContent); // Verify that there is a modified entry. - Assert.Equal(1, repo.RetrieveStatus().Modified.Count()); + Assert.Single(repo.RetrieveStatus().Modified); Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(fullPathFileA)); Commands.Checkout(repo, otherBranchName); // Verify modified entry still exists. - Assert.Equal(1, repo.RetrieveStatus().Modified.Count()); + Assert.Single(repo.RetrieveStatus().Modified); Assert.Equal(FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(fullPathFileA)); } } @@ -599,13 +599,13 @@ public void CheckoutRetainsStagedChanges() Commands.Stage(repo, fullPathFileA); // Verify that there is a staged entry. - Assert.Equal(1, repo.RetrieveStatus().Staged.Count()); + Assert.Single(repo.RetrieveStatus().Staged); Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus(fullPathFileA)); Commands.Checkout(repo, otherBranchName); // Verify staged entry still exists. - Assert.Equal(1, repo.RetrieveStatus().Staged.Count()); + Assert.Single(repo.RetrieveStatus().Staged); Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus(fullPathFileA)); } } @@ -625,7 +625,7 @@ public void CheckoutRetainsIgnoredChanges() "bin/some_ignored_file.txt", "hello from this ignored file."); - Assert.Equal(1, repo.RetrieveStatus().Ignored.Count()); + Assert.Single(repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }).Ignored); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath)); @@ -652,7 +652,7 @@ public void ForceCheckoutRetainsIgnoredChanges() "bin/some_ignored_file.txt", "hello from this ignored file."); - Assert.Equal(1, repo.RetrieveStatus().Ignored.Count()); + Assert.Single(repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }).Ignored); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath)); @@ -779,7 +779,7 @@ public void CheckoutFromDetachedHead(string commitPointer) public void CheckoutBranchFromDetachedHead() { string path = SandboxStandardTestRepo(); - using (var repo = new Repository(path, new RepositoryOptions{ Identity = Constants.Identity })) + using (var repo = new Repository(path, new RepositoryOptions { Identity = Constants.Identity })) { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); @@ -958,7 +958,7 @@ public void CanCheckoutPath(string originalBranch, string checkoutFrom, string p repo.CheckoutPaths(checkoutFrom, new[] { path }); Assert.Equal(expectedStatus, repo.RetrieveStatus(path)); - Assert.Equal(1, repo.RetrieveStatus().Count()); + Assert.Single(repo.RetrieveStatus()); } } @@ -995,8 +995,7 @@ public void CannotCheckoutPathsWithEmptyOrNullPathArgument() Assert.False(repo.RetrieveStatus().IsDirty); // Passing null 'paths' parameter should throw - Assert.Throws(typeof(ArgumentNullException), - () => repo.CheckoutPaths("i-do-numbers", null)); + Assert.Throws(() => repo.CheckoutPaths("i-do-numbers", null)); // Passing empty list should do nothing repo.CheckoutPaths("i-do-numbers", Enumerable.Empty()); @@ -1029,6 +1028,52 @@ public void CanCheckoutPathFromCurrentBranch(string fileName) } } + [Theory] + [InlineData("br2", "origin")] + [InlineData("unique/branch", "another/remote")] + public void CheckoutBranchTriesRemoteTrackingBranchAsFallbackAndSucceedsIfOnlyOne(string branchName, string expectedRemoteName) + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + ResetAndCleanWorkingDirectory(repo); + + // Define another remote + var otherRemote = "another/remote"; + repo.Network.Remotes.Add(otherRemote, "https://github.com/libgit2/TestGitRepository"); + + // Define an extra remote tracking branch that does not conflict + repo.Refs.Add($"refs/remotes/{otherRemote}/unique/branch", repo.Head.Tip.Sha); + + Branch branch = Commands.Checkout(repo, branchName); + + Assert.NotNull(branch); + Assert.True(branch.IsTracking); + Assert.Equal($"refs/remotes/{expectedRemoteName}/{branchName}", branch.TrackedBranch.CanonicalName); + } + } + + [Fact] + public void CheckoutBranchTriesRemoteTrackingBranchAsFallbackAndThrowsIfMoreThanOne() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + ResetAndCleanWorkingDirectory(repo); + + // Define another remote + var otherRemote = "another/remote"; + repo.Network.Remotes.Add(otherRemote, "https://github.com/libgit2/TestGitRepository"); + + // Define remote tracking branches that conflict + var branchName = "conflicting/branch"; + repo.Refs.Add($"refs/remotes/origin/{branchName}", repo.Head.Tip.Sha); + repo.Refs.Add($"refs/remotes/{otherRemote}/{branchName}", repo.Head.Tip.Sha); + + Assert.Throws(() => Commands.Checkout(repo, branchName)); + } + } + /// /// Helper method to populate a simple repository with /// a single file and two branches. diff --git a/LibGit2Sharp.Tests/CherryPickFixture.cs b/LibGit2Sharp.Tests/CherryPickFixture.cs index 1bbff1428..f4a383fef 100644 --- a/LibGit2Sharp.Tests/CherryPickFixture.cs +++ b/LibGit2Sharp.Tests/CherryPickFixture.cs @@ -66,7 +66,7 @@ public void CherryPickWithConflictDoesNotCommit() Assert.Equal(CherryPickStatus.Conflicts, cherryPickResult.Status); Assert.Null(cherryPickResult.Commit); - Assert.Equal(1, repo.Index.Conflicts.Count()); + Assert.Single(repo.Index.Conflicts); var conflict = repo.Index.Conflicts.First(); var changes = repo.Diff.Compare(repo.Lookup(conflict.Theirs.Id), repo.Lookup(conflict.Ours.Id)); @@ -139,7 +139,7 @@ public void CanCherryPickCommit() var result = repo.ObjectDatabase.CherryPickCommit(commitToMerge, ours, 0, null); Assert.Equal(MergeTreeStatus.Succeeded, result.Status); - Assert.Equal(0, result.Conflicts.Count()); + Assert.Empty(result.Conflicts); } } @@ -162,6 +162,56 @@ public void CherryPickWithConflictsReturnsConflicts() } } + [Fact] + public void CanCherryPickCommitIntoIndex() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var ours = repo.Head.Tip; + + Commit commitToMerge = repo.Branches["fast_forward"].Tip; + + using (TransientIndex index = repo.ObjectDatabase.CherryPickCommitIntoIndex(commitToMerge, ours, 0, null)) + { + var tree = index.WriteToTree(); + Assert.Equal(commitToMerge.Tree.Id, tree.Id); + } + } + } + + [Fact] + public void CanCherryPickIntoIndexWithConflicts() + { + const string conflictBranchName = "conflicts"; + + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + Branch branch = repo.Branches[conflictBranchName]; + Assert.NotNull(branch); + + using (TransientIndex index = repo.ObjectDatabase.CherryPickCommitIntoIndex(branch.Tip, repo.Head.Tip, 0, null)) + { + Assert.False(index.IsFullyMerged); + + var conflict = index.Conflicts.First(); + + //Resolve the conflict by taking the blob from branch + var blob = repo.Lookup(conflict.Theirs.Id); + //Add() does not remove conflict entries for the same path, so they must be explicitly removed first. + index.Remove(conflict.Ours.Path); + index.Add(blob, conflict.Ours.Path, Mode.NonExecutableFile); + + Assert.True(index.IsFullyMerged); + var tree = index.WriteToTree(); + + //Since we took the conflicted blob from the branch, the merged result should be the same as the branch. + Assert.Equal(branch.Tip.Tree.Id, tree.Id); + } + } + } + private Commit AddFileCommitToRepo(IRepository repository, string filename, string content = null) { Touch(repository.Info.WorkingDirectory, filename, content); diff --git a/LibGit2Sharp.Tests/CleanFixture.cs b/LibGit2Sharp.Tests/CleanFixture.cs index f674285c6..39c7a6152 100644 --- a/LibGit2Sharp.Tests/CleanFixture.cs +++ b/LibGit2Sharp.Tests/CleanFixture.cs @@ -14,13 +14,13 @@ public void CanCleanWorkingDirectory() { // Verify that there are the expected number of entries and untracked files Assert.Equal(6, repo.RetrieveStatus().Count()); - Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Single(repo.RetrieveStatus().Untracked); repo.RemoveUntrackedFiles(); // Verify that there are the expected number of entries and 0 untracked files Assert.Equal(5, repo.RetrieveStatus().Count()); - Assert.Equal(0, repo.RetrieveStatus().Untracked.Count()); + Assert.Empty(repo.RetrieveStatus().Untracked); } } diff --git a/LibGit2Sharp.Tests/CloneFixture.cs b/LibGit2Sharp.Tests/CloneFixture.cs index ae98788eb..831f6779f 100644 --- a/LibGit2Sharp.Tests/CloneFixture.cs +++ b/LibGit2Sharp.Tests/CloneFixture.cs @@ -5,7 +5,6 @@ using LibGit2Sharp.Handlers; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -14,8 +13,6 @@ public class CloneFixture : BaseFixture [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository")] - //[InlineData("git@github.com:libgit2/TestGitRepository")] public void CanClone(string url) { var scd = BuildSelfCleaningDirectory(); @@ -33,8 +30,36 @@ public void CanClone(string url) Assert.False(repo.Info.IsBare); Assert.True(File.Exists(Path.Combine(scd.RootedDirectoryPath, "master.txt"))); - Assert.Equal(repo.Head.FriendlyName, "master"); - Assert.Equal(repo.Head.Tip.Id.ToString(), "49322bb17d3acc9146f98c97d078513228bbf3c0"); + Assert.Equal("master", repo.Head.FriendlyName); + Assert.Equal("49322bb17d3acc9146f98c97d078513228bbf3c0", repo.Head.Tip.Id.ToString()); + } + } + + [Theory] + [InlineData("https://github.com/libgit2/TestGitRepository", 1)] + [InlineData("https://github.com/libgit2/TestGitRepository", 5)] + [InlineData("https://github.com/libgit2/TestGitRepository", 7)] + public void CanCloneShallow(string url, int depth) + { + var scd = BuildSelfCleaningDirectory(); + + var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, new CloneOptions + { + FetchOptions = + { + Depth = depth, + }, + }); + + using (var repo = new Repository(clonedRepoPath)) + { + var commitsFirstParentOnly = repo.Commits.QueryBy(new CommitFilter + { + FirstParentOnly = true, + }); + + Assert.Equal(depth, commitsFirstParentOnly.Count()); + Assert.Equal("49322bb17d3acc9146f98c97d078513228bbf3c0", repo.Head.Tip.Id.ToString()); } } @@ -70,18 +95,18 @@ private void AssertLocalClone(string url, string path = null, bool isCloningAnEm Assert.NotEqual(originalRepo.Info.Path, clonedRepo.Info.Path); Assert.Equal(originalRepo.Head, clonedRepo.Head); - Assert.Equal(originalRepo.Branches.Count(), clonedRepo.Branches.Count(b => b.IsRemote)); + Assert.Equal(originalRepo.Branches.Count(), clonedRepo.Branches.Count(b => b.IsRemote && b.FriendlyName != "origin/HEAD")); Assert.Equal(isCloningAnEmptyRepository ? 0 : 1, clonedRepo.Branches.Count(b => !b.IsRemote)); Assert.Equal(originalRepo.Tags.Count(), clonedRepo.Tags.Count()); - Assert.Equal(1, clonedRepo.Network.Remotes.Count()); + Assert.Single(clonedRepo.Network.Remotes); } } [Fact] public void CanCloneALocalRepositoryFromALocalUri() { - var uri = new Uri(Path.GetFullPath(BareTestRepoPath)); + var uri = new Uri($"file://{Path.GetFullPath(BareTestRepoPath)}"); AssertLocalClone(uri.AbsoluteUri, BareTestRepoPath); } @@ -103,16 +128,14 @@ public void CanCloneALocalRepositoryFromANewlyCreatedTemporaryPath() [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository")] - //[InlineData("git@github.com:libgit2/TestGitRepository")] public void CanCloneBarely(string url) { var scd = BuildSelfCleaningDirectory(); string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, new CloneOptions - { - IsBare = true - }); + { + IsBare = true + }); using (var repo = new Repository(clonedRepoPath)) { @@ -127,7 +150,7 @@ public void CanCloneBarely(string url) } [Theory] - [InlineData("git://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] public void WontCheckoutIfAskedNotTo(string url) { var scd = BuildSelfCleaningDirectory(); @@ -144,7 +167,7 @@ public void WontCheckoutIfAskedNotTo(string url) } [Theory] - [InlineData("git://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] public void CallsProgressCallbacks(string url) { bool transferWasCalled = false; @@ -156,10 +179,14 @@ public void CallsProgressCallbacks(string url) Repository.Clone(url, scd.DirectoryPath, new CloneOptions() { - OnTransferProgress = _ => { transferWasCalled = true; return true; }, - OnProgress = progress => { progressWasCalled = true; return true; }, - OnUpdateTips = (name, oldId, newId) => { updateTipsWasCalled = true; return true; }, + FetchOptions = + { + OnTransferProgress = _ => { transferWasCalled = true; return true; }, + OnProgress = progress => { progressWasCalled = true; return true; }, + OnUpdateTips = (name, oldId, newId) => { updateTipsWasCalled = true; return true; } + }, OnCheckoutProgress = (a, b, c) => checkoutWasCalled = true + }); Assert.True(transferWasCalled); @@ -179,7 +206,7 @@ public void CanCloneWithCredentials() string clonedRepoPath = Repository.Clone(Constants.PrivateRepoUrl, scd.DirectoryPath, new CloneOptions() { - CredentialsProvider = Constants.PrivateRepoCredentials + FetchOptions = { CredentialsProvider = Constants.PrivateRepoCredentials } }); @@ -195,7 +222,7 @@ public void CanCloneWithCredentials() } } - static Credentials CreateUsernamePasswordCredentials (string user, string pass, bool secure) + static Credentials CreateUsernamePasswordCredentials(string user, string pass, bool secure) { if (secure) { @@ -213,80 +240,87 @@ static Credentials CreateUsernamePasswordCredentials (string user, string pass, }; } - [Theory] - [InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3", true)] - [InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3", false)] - public void CanCloneFromBBWithCredentials(string url, string user, string pass, bool secure) - { - var scd = BuildSelfCleaningDirectory(); - - string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, new CloneOptions() - { - CredentialsProvider = (_url, _user, _cred) => CreateUsernamePasswordCredentials (user, pass, secure) - }); - - using (var repo = new Repository(clonedRepoPath)) - { - string dir = repo.Info.Path; - Assert.True(Path.IsPathRooted(dir)); - Assert.True(Directory.Exists(dir)); - - Assert.NotNull(repo.Info.WorkingDirectory); - Assert.Equal(Path.Combine(scd.RootedDirectoryPath, ".git" + Path.DirectorySeparatorChar), repo.Info.Path); - Assert.False(repo.Info.IsBare); - } - } + //[Theory] + //[InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3", true)] + //[InlineData("https://libgit2@bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3", false)] + //public void CanCloneFromBBWithCredentials(string url, string user, string pass, bool secure) + //{ + // var scd = BuildSelfCleaningDirectory(); + + // string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, new CloneOptions() + // { + // CredentialsProvider = (_url, _user, _cred) => CreateUsernamePasswordCredentials(user, pass, secure) + // }); + + // using (var repo = new Repository(clonedRepoPath)) + // { + // string dir = repo.Info.Path; + // Assert.True(Path.IsPathRooted(dir)); + // Assert.True(Directory.Exists(dir)); + + // Assert.NotNull(repo.Info.WorkingDirectory); + // Assert.Equal(Path.Combine(scd.RootedDirectoryPath, ".git" + Path.DirectorySeparatorChar), repo.Info.Path); + // Assert.False(repo.Info.IsBare); + // } + //} [SkippableTheory] [InlineData("https://github.com/libgit2/TestGitRepository.git", "github.com", typeof(CertificateX509))] - [InlineData("git@github.com:libgit2/TestGitRepository.git", "github.com", typeof(CertificateSsh))] + //[InlineData("git@github.com:libgit2/TestGitRepository.git", "github.com", typeof(CertificateSsh))] public void CanInspectCertificateOnClone(string url, string hostname, Type certType) { var scd = BuildSelfCleaningDirectory(); InconclusiveIf( () => - certType == typeof (CertificateSsh) && !GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh), + certType == typeof(CertificateSsh) && !GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh), "SSH not supported"); bool wasCalled = false; bool checksHappy = false; - var options = new CloneOptions { - CertificateCheck = (cert, valid, host) => { - wasCalled = true; - - Assert.Equal(hostname, host); - Assert.Equal(certType, cert.GetType()); - - if (certType == typeof(CertificateX509)) { - Assert.True(valid); - var x509 = ((CertificateX509)cert).Certificate; - // we get a string with the different fields instead of a structure, so... - Assert.True(x509.Subject.Contains("CN=github.com,")); - checksHappy = true; - return false; - } + var options = new CloneOptions + { + FetchOptions = + { + CertificateCheck = (cert, valid, host) => + { + wasCalled = true; + + Assert.Equal(hostname, host); + Assert.Equal(certType, cert.GetType()); + + if (certType == typeof(CertificateX509)) + { + Assert.True(valid); + var x509 = ((CertificateX509)cert).Certificate; + // we get a string with the different fields instead of a structure, so... + Assert.Contains("CN=github.com", x509.Subject); + checksHappy = true; + return false; + } + + if (certType == typeof(CertificateSsh)) + { + var hostkey = (CertificateSsh)cert; + Assert.True(hostkey.HasMD5); + /* + * Once you've connected and thus your ssh has stored the hostkey, + * you can get the hostkey for a host with + * + * ssh-keygen -F github.com -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':' + * + * though GitHub's hostkey won't change anytime soon. + */ + Assert.Equal("1627aca576282d36631b564debdfa648", + BitConverter.ToString(hostkey.HashMD5).ToLower().Replace("-", "")); + checksHappy = true; + return false; + } - if (certType == typeof(CertificateSsh)) { - var hostkey = (CertificateSsh)cert; - Assert.True(hostkey.HasMD5); - /* - * Once you've connected and thus your ssh has stored the hostkey, - * you can get the hostkey for a host with - * - * ssh-keygen -F github.com -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':' - * - * though GitHub's hostkey won't change anytime soon. - */ - Assert.Equal("1627aca576282d36631b564debdfa648", - BitConverter.ToString(hostkey.HashMD5).ToLower().Replace("-", "")); - checksHappy = true; return false; } - - return false; - }, + } }; Assert.Throws(() => @@ -297,16 +331,8 @@ public void CanInspectCertificateOnClone(string url, string hostname, Type certT Assert.True(checksHappy); } - [Fact] - public void CloningAnUrlWithoutPathThrows() - { - var scd = BuildSelfCleaningDirectory(); - - Assert.Throws(() => Repository.Clone("http://github.com", scd.DirectoryPath)); - } - [Theory] - [InlineData("git://github.com/libgit2/TestGitRepository")] + [InlineData("https://github.com/libgit2/TestGitRepository")] public void CloningWithoutWorkdirPathThrows(string url) { Assert.Throws(() => Repository.Clone(url, null)); @@ -361,7 +387,7 @@ private class CloneCallbackInfo [Fact] public void CanRecursivelyCloneSubmodules() { - var uri = new Uri(Path.GetFullPath(SandboxSubmoduleSmallTestRepo())); + var uri = new Uri($"file://{Path.GetFullPath(SandboxSubmoduleSmallTestRepo())}"); var scd = BuildSelfCleaningDirectory(); string relativeSubmodulePath = "submodule_target_wd"; @@ -441,15 +467,18 @@ public void CanRecursivelyCloneSubmodules() { RecurseSubmodules = true, OnCheckoutProgress = checkoutProgressHandler, - OnUpdateTips = remoteRefUpdated, - RepositoryOperationStarting = repositoryOperationStarting, - RepositoryOperationCompleted = repositoryOperationCompleted, + FetchOptions = + { + OnUpdateTips = remoteRefUpdated, + RepositoryOperationStarting = repositoryOperationStarting, + RepositoryOperationCompleted = repositoryOperationCompleted + } }; string clonedRepoPath = Repository.Clone(uri.AbsolutePath, scd.DirectoryPath, options); string workDirPath; - using(Repository repo = new Repository(clonedRepoPath)) + using (Repository repo = new Repository(clonedRepoPath)) { workDirPath = repo.Info.WorkingDirectory.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); } @@ -460,14 +489,14 @@ public void CanRecursivelyCloneSubmodules() Dictionary expectedCallbackInfo = new Dictionary(); expectedCallbackInfo.Add(workDirPath, new CloneCallbackInfo() - { - RecursionDepth = 0, - RemoteUrl = uri.AbsolutePath, - StartingWorkInRepositoryCalled = true, - FinishedWorkInRepositoryCalled = true, - CheckoutProgressCalled = true, - RemoteRefUpdateCalled = true, - }); + { + RecursionDepth = 0, + RemoteUrl = uri.AbsolutePath, + StartingWorkInRepositoryCalled = true, + FinishedWorkInRepositoryCalled = true, + CheckoutProgressCalled = true, + RemoteRefUpdateCalled = true, + }); expectedCallbackInfo.Add(Path.Combine(workDirPath, relativeSubmodulePath), new CloneCallbackInfo() { @@ -494,7 +523,7 @@ public void CanRecursivelyCloneSubmodules() } // Verify the state of the submodule - using(Repository repo = new Repository(clonedRepoPath)) + using (Repository repo = new Repository(clonedRepoPath)) { var sm = repo.Submodules[relativeSubmodulePath]; Assert.True(sm.RetrieveStatus().HasFlag(SubmoduleStatus.InWorkDir | @@ -512,7 +541,7 @@ public void CanRecursivelyCloneSubmodules() [Fact] public void CanCancelRecursiveClone() { - var uri = new Uri(Path.GetFullPath(SandboxSubmoduleSmallTestRepo())); + var uri = new Uri($"file://{Path.GetFullPath(SandboxSubmoduleSmallTestRepo())}"); var scd = BuildSelfCleaningDirectory(); string relativeSubmodulePath = "submodule_target_wd"; @@ -526,7 +555,7 @@ public void CanCancelRecursiveClone() CloneOptions options = new CloneOptions() { RecurseSubmodules = true, - RepositoryOperationStarting = repositoryOperationStarting, + FetchOptions = { RepositoryOperationStarting = repositoryOperationStarting } }; Assert.Throws(() => @@ -541,7 +570,7 @@ public void CanCancelRecursiveClone() { Repository.Clone(uri.AbsolutePath, scd.DirectoryPath, options); } - catch(RecurseSubmodulesException ex) + catch (RecurseSubmodulesException ex) { Assert.NotNull(ex.InnerException); Assert.Equal(typeof(UserCancelledException), ex.InnerException.GetType()); @@ -549,7 +578,7 @@ public void CanCancelRecursiveClone() } // Verify that the submodule was not initialized. - using(Repository repo = new Repository(clonedRepoPath)) + using (Repository repo = new Repository(clonedRepoPath)) { var submoduleStatus = repo.Submodules[relativeSubmodulePath].RetrieveStatus(); Assert.Equal(SubmoduleStatus.InConfig | SubmoduleStatus.InHead | SubmoduleStatus.InIndex | SubmoduleStatus.WorkDirUninitialized, @@ -557,5 +586,48 @@ public void CanCancelRecursiveClone() } } + + [Fact] + public void CannotCloneWithForbiddenCustomHeaders() + { + var scd = BuildSelfCleaningDirectory(); + + const string url = "https://github.com/libgit2/TestGitRepository"; + + const string knownHeader = "User-Agent: mygit-201"; + var cloneOptions = new CloneOptions(); + cloneOptions.FetchOptions.CustomHeaders = new string[] { knownHeader }; + + Assert.Throws(() => Repository.Clone(url, scd.DirectoryPath, cloneOptions)); + } + + [Fact] + public void CannotCloneWithMalformedCustomHeaders() + { + var scd = BuildSelfCleaningDirectory(); + + const string url = "https://github.com/libgit2/TestGitRepository"; + + const string knownHeader = "hello world"; + var cloneOptions = new CloneOptions(); + cloneOptions.FetchOptions.CustomHeaders = new string[] { knownHeader }; + + Assert.Throws(() => Repository.Clone(url, scd.DirectoryPath, cloneOptions)); + } + + [Fact] + public void CanCloneWithCustomHeaders() + { + var scd = BuildSelfCleaningDirectory(); + + const string url = "https://github.com/libgit2/TestGitRepository"; + + const string knownHeader = "X-Hello: world"; + var cloneOptions = new CloneOptions(); + cloneOptions.FetchOptions.CustomHeaders = new string[] { knownHeader }; + + var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, cloneOptions); + Assert.True(Directory.Exists(clonedRepoPath)); + } } } diff --git a/LibGit2Sharp.Tests/CommitFixture.cs b/LibGit2Sharp.Tests/CommitFixture.cs index b522e3c34..e99ca918f 100644 --- a/LibGit2Sharp.Tests/CommitFixture.cs +++ b/LibGit2Sharp.Tests/CommitFixture.cs @@ -3,10 +3,8 @@ using System.IO; using System.Linq; using System.Text; -using LibGit2Sharp.Core; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -70,7 +68,7 @@ public void CanEnumerateCommitsInDetachedHeadState() ObjectId parentOfHead = repo.Head.Tip.Parents.First().Id; repo.Refs.Add("HEAD", parentOfHead.Sha, true); - Assert.Equal(true, repo.Info.IsHeadDetached); + Assert.True(repo.Info.IsHeadDetached); Assert.Equal(6, repo.Commits.Count()); } @@ -150,13 +148,13 @@ public void CanEnumerateCommitsWithReverseTimeSorting() using (var repo = new Repository(path)) { foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", - SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse - })) + { + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", + SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse + })) { Assert.NotNull(commit); - Assert.True(commit.Sha.StartsWith(reversedShas[count])); + Assert.StartsWith(reversedShas[count], commit.Sha); count++; } } @@ -170,10 +168,10 @@ public void CanEnumerateCommitsWithReverseTopoSorting() using (var repo = new Repository(path)) { List commits = repo.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", - SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse - }).ToList(); + { + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", + SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse + }).ToList(); foreach (Commit commit in commits) { Assert.NotNull(commit); @@ -204,7 +202,7 @@ public void CanGetParentsCount() string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(1, repo.Commits.First().Parents.Count()); + Assert.Single(repo.Commits.First().Parents); } } @@ -216,13 +214,13 @@ public void CanEnumerateCommitsWithTimeSorting() using (var repo = new Repository(path)) { foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", - SortBy = CommitSortStrategies.Time - })) + { + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", + SortBy = CommitSortStrategies.Time + })) { Assert.NotNull(commit); - Assert.True(commit.Sha.StartsWith(expectedShas[count])); + Assert.StartsWith(expectedShas[count], commit.Sha); count++; } } @@ -236,10 +234,10 @@ public void CanEnumerateCommitsWithTopoSorting() using (var repo = new Repository(path)) { List commits = repo.Commits.QueryBy(new CommitFilter - { - IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", - SortBy = CommitSortStrategies.Topological - }).ToList(); + { + IncludeReachableFrom = "a4a7dce85cf63874e984719f4fdd239f5145052f", + SortBy = CommitSortStrategies.Topological + }).ToList(); foreach (Commit commit in commits) { Assert.NotNull(commit); @@ -331,9 +329,12 @@ public void CanEnumerateCommitsFromTwoHeads() public void CanEnumerateCommitsFromMixedStartingPoints() { AssertEnumerationOfCommits( - repo => new CommitFilter { IncludeReachableFrom = new object[] { repo.Branches["br2"], + repo => new CommitFilter + { + IncludeReachableFrom = new object[] { repo.Branches["br2"], "refs/heads/master", - new ObjectId("e90810b8df3e80c413d903f631643c716887138d") } }, + new ObjectId("e90810b8df3e80c413d903f631643c716887138d") } + }, new[] { "4c062a6", "e90810b", "6dcf9bf", "a4a7dce", @@ -389,9 +390,9 @@ public void CanEnumerateAllCommits() { AssertEnumerationOfCommits( repo => new CommitFilter - { - IncludeReachableFrom = repo.Refs.OrderBy(r => r.CanonicalName, StringComparer.Ordinal), - }, + { + IncludeReachableFrom = repo.Refs.OrderBy(r => r.CanonicalName, StringComparer.Ordinal), + }, new[] { "44d5d18", "bb65291", "532740a", "503a16f", "3dfd6fd", @@ -406,7 +407,7 @@ public void CanEnumerateCommitsFromATagWhichPointsToABlob() { AssertEnumerationOfCommits( repo => new CommitFilter { IncludeReachableFrom = repo.Tags["point_to_blob"] }, - new string[] { }); + Array.Empty()); } [Fact] @@ -421,7 +422,7 @@ public void CanEnumerateCommitsFromATagWhichPointsToATree() AssertEnumerationOfCommitsInRepo(repo, r => new CommitFilter { IncludeReachableFrom = tag }, - new string[] { }); + Array.Empty()); } } @@ -475,16 +476,16 @@ public void CanReadCommitData() Assert.NotNull(commit.Author); Assert.Equal("Scott Chacon", commit.Author.Name); Assert.Equal("schacon@gmail.com", commit.Author.Email); - Assert.Equal(1273360386, commit.Author.When.ToSecondsSinceEpoch()); + Assert.Equal(1273360386, commit.Author.When.ToUnixTimeSeconds()); Assert.NotNull(commit.Committer); Assert.Equal("Scott Chacon", commit.Committer.Name); Assert.Equal("schacon@gmail.com", commit.Committer.Email); - Assert.Equal(1273360386, commit.Committer.When.ToSecondsSinceEpoch()); + Assert.Equal(1273360386, commit.Committer.When.ToUnixTimeSeconds()); Assert.Equal("181037049a54a1eb5fab404658a3a250b44335d7", commit.Tree.Sha); - Assert.Equal(0, commit.Parents.Count()); + Assert.Empty(commit.Parents); } } @@ -590,8 +591,8 @@ public void CommitParentsAreMergeHeads() Assert.Equal(CurrentOperation.None, repo.Info.CurrentOperation); Assert.Equal(2, newMergedCommit.Parents.Count()); - Assert.Equal(newMergedCommit.Parents.First().Sha, "c47800c7266a2be04c571c04d5a6614691ea99bd"); - Assert.Equal(newMergedCommit.Parents.Skip(1).First().Sha, "9fd738e8f7967c078dceed8190330fc8648ee56a"); + Assert.Equal("c47800c7266a2be04c571c04d5a6614691ea99bd", newMergedCommit.Parents.First().Sha); + Assert.Equal("9fd738e8f7967c078dceed8190330fc8648ee56a", newMergedCommit.Parents.Skip(1).First().Sha); // Assert reflog entry is created var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); @@ -670,18 +671,22 @@ public void CanCommitALittleBit() AssertBlobContent(repo.Head[relativeFilepath], "nulltoken\n"); AssertBlobContent(commit[relativeFilepath], "nulltoken\n"); - Assert.Equal(0, commit.Parents.Count()); + Assert.Empty(commit.Parents); Assert.False(repo.Info.IsHeadUnborn); // Assert a reflog entry is created on HEAD - Assert.Equal(1, repo.Refs.Log("HEAD").Count()); + Assert.Single(repo.Refs.Log("HEAD")); var reflogEntry = repo.Refs.Log("HEAD").First(); Assert.Equal(identity.Name, reflogEntry.Committer.Name); Assert.Equal(identity.Email, reflogEntry.Committer.Email); - var now = DateTimeOffset.Now; - Assert.InRange(reflogEntry.Committer.When, before, now); + // When verifying the timestamp range, give a little more room on the range. + // Git or file system datetime truncation seems to cause these stamps to jump up to a second earlier + // than we expect. See https://github.com/libgit2/libgit2sharp/issues/1764 + var low = before - TimeSpan.FromSeconds(1); + var high = DateTimeOffset.Now.TruncateMilliseconds() + TimeSpan.FromSeconds(1); + Assert.InRange(reflogEntry.Committer.When, low, high); Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(ObjectId.Zero, reflogEntry.From); @@ -689,7 +694,7 @@ public void CanCommitALittleBit() // Assert a reflog entry is created on HEAD target var targetCanonicalName = repo.Refs.Head.TargetIdentifier; - Assert.Equal(1, repo.Refs.Log(targetCanonicalName).Count()); + Assert.Single(repo.Refs.Log(targetCanonicalName)); Assert.Equal(commit.Id, repo.Refs.Log(targetCanonicalName).First().To); File.WriteAllText(filePath, "nulltoken commits!\n"); @@ -701,7 +706,7 @@ public void CanCommitALittleBit() AssertBlobContent(repo.Head[relativeFilepath], "nulltoken commits!\n"); AssertBlobContent(commit2[relativeFilepath], "nulltoken commits!\n"); - Assert.Equal(1, commit2.Parents.Count()); + Assert.Single(commit2.Parents); Assert.Equal(commit.Id, commit2.Parents.First().Id); // Assert the reflog is shifted @@ -721,7 +726,7 @@ public void CanCommitALittleBit() AssertBlobContent(repo.Head[relativeFilepath], "davidfowl commits!\n"); AssertBlobContent(commit3[relativeFilepath], "davidfowl commits!\n"); - Assert.Equal(1, commit3.Parents.Count()); + Assert.Single(commit3.Parents); Assert.Equal(commit.Id, commit3.Parents.First().Id); AssertBlobContent(firstCommitBranch[relativeFilepath], "nulltoken\n"); @@ -776,17 +781,17 @@ public void CanAmendARootCommit() using (var repo = new Repository(repoPath)) { - Assert.Equal(1, repo.Head.Commits.Count()); + Assert.Single(repo.Head.Commits); Commit originalCommit = repo.Head.Tip; - Assert.Equal(0, originalCommit.Parents.Count()); + Assert.Empty(originalCommit.Parents); CreateAndStageANewFile(repo); Commit amendedCommit = repo.Commit("I'm rewriting the history!", Constants.Signature, Constants.Signature, new CommitOptions { AmendPreviousCommit = true }); - Assert.Equal(1, repo.Head.Commits.Count()); + Assert.Single(repo.Head.Commits); AssertCommitHasBeenAmended(repo, amendedCommit, originalCommit); } @@ -859,21 +864,21 @@ public void CanRetrieveChildrenOfASpecificCommit() const string parentSha = "5b5b025afb0b4c913b4c338a42934a3863bf3644"; var filter = new CommitFilter - { - /* Revwalk from all the refs (git log --all) ... */ - IncludeReachableFrom = repo.Refs, + { + /* Revwalk from all the refs (git log --all) ... */ + IncludeReachableFrom = repo.Refs, - /* ... and stop when the parent is reached */ - ExcludeReachableFrom = parentSha - }; + /* ... and stop when the parent is reached */ + ExcludeReachableFrom = parentSha + }; var commits = repo.Commits.QueryBy(filter); var children = from c in commits - from p in c.Parents - let pId = p.Id - where pId.Sha == parentSha - select c; + from p in c.Parents + let pId = p.Id + where pId.Sha == parentSha + select c; var expectedChildren = new[] { "c47800c7266a2be04c571c04d5a6614691ea99bd", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045" }; @@ -889,9 +894,9 @@ public void CanCorrectlyDistinguishAuthorFromCommitter() using (var repo = new Repository(path)) { var author = new Signature("Wilbert van Dolleweerd", "getit@xs4all.nl", - Epoch.ToDateTimeOffset(1244187936, 120)); + DateTimeOffset.FromUnixTimeSeconds(1244187936).ToOffset(TimeSpan.FromMinutes(120))); var committer = new Signature("Henk Westhuis", "Henk_Westhuis@hotmail.com", - Epoch.ToDateTimeOffset(1244286496, 120)); + DateTimeOffset.FromUnixTimeSeconds(1244286496).ToOffset(TimeSpan.FromMinutes(120))); Commit c = repo.Commit("I can haz an author and a committer!", author, committer); @@ -918,7 +923,7 @@ public void CanCommitOnOrphanedBranch() Commands.Stage(repo, relativeFilepath); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); - Assert.Equal(1, repo.Head.Commits.Count()); + Assert.Single(repo.Head.Commits); } } @@ -1000,8 +1005,8 @@ public void CanCommitAnEmptyCommitWhenMerging() Commit newMergedCommit = repo.Commit("Merge commit", Constants.Signature, Constants.Signature); Assert.Equal(2, newMergedCommit.Parents.Count()); - Assert.Equal(newMergedCommit.Parents.First().Sha, "32eab9cb1f450b5fe7ab663462b77d7f4b703344"); - Assert.Equal(newMergedCommit.Parents.Skip(1).First().Sha, "f705abffe7015f2beacf2abe7a36583ebee3487e"); + Assert.Equal("32eab9cb1f450b5fe7ab663462b77d7f4b703344", newMergedCommit.Parents.First().Sha); + Assert.Equal("f705abffe7015f2beacf2abe7a36583ebee3487e", newMergedCommit.Parents.Skip(1).First().Sha); } } @@ -1058,8 +1063,8 @@ public void CanPrettifyAMessage() } private readonly string signedCommit = - "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n" + - "parent 34734e478d6cf50c27c9d69026d93974d052c454\n" + + "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n" + + "parent 8496071c1b46c854b31185ea97743be6a8774479\n" + "author Ben Burkert 1358451456 -0800\n" + "committer Ben Burkert 1358451456 -0800\n" + "gpgsig -----BEGIN PGP SIGNATURE-----\n" + @@ -1102,8 +1107,8 @@ public void CanPrettifyAMessage() "-----END PGP SIGNATURE-----"; private readonly string signedData = - "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n" + - "parent 34734e478d6cf50c27c9d69026d93974d052c454\n" + + "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n" + + "parent 8496071c1b46c854b31185ea97743be6a8774479\n" + "author Ben Burkert 1358451456 -0800\n" + "committer Ben Burkert 1358451456 -0800\n" + "\n" + @@ -1155,7 +1160,7 @@ public void CanCreateACommitString() [Fact] public void CanCreateASignedCommit() { - string repoPath = InitNewRepository(); + string repoPath = SandboxStandardTestRepo(); using (var repo = new Repository(repoPath)) { var odb = repo.ObjectDatabase; diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs index 3e071c3b7..aaee77b02 100644 --- a/LibGit2Sharp.Tests/ConfigurationFixture.cs +++ b/LibGit2Sharp.Tests/ConfigurationFixture.cs @@ -4,7 +4,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -50,6 +49,113 @@ public void CanUnsetAnEntryFromTheGlobalConfiguration() } } + [Fact] + public void CanAddAndReadMultivarFromTheLocalConfiguration() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + Assert.DoesNotContain(repo.Config.OfType>(), x => x.Key == "unittests.plugin"); + + repo.Config.Add("unittests.plugin", "value1", ConfigurationLevel.Local); + repo.Config.Add("unittests.plugin", "value2", ConfigurationLevel.Local); + + Assert.Equal(new[] { "value1", "value2" }, repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Local) + .Select(x => x.Value) + .ToArray()); + } + } + + [Fact] + public void CanAddAndReadMultivarFromTheGlobalConfiguration() + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); + Assert.DoesNotContain(repo.Config.OfType>(), x => x.Key == "unittests.plugin"); + + repo.Config.Add("unittests.plugin", "value1", ConfigurationLevel.Global); + repo.Config.Add("unittests.plugin", "value2", ConfigurationLevel.Global); + + Assert.Equal(new[] { "value1", "value2" }, repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin") + .Select(x => x.Value) + .ToArray()); + } + } + + [Fact] + public void CanUnsetAllFromTheGlobalConfiguration() + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); + Assert.Empty(repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin") + .Select(x => x.Value) + .ToArray()); + + repo.Config.Add("unittests.plugin", "value1", ConfigurationLevel.Global); + repo.Config.Add("unittests.plugin", "value2", ConfigurationLevel.Global); + + Assert.Equal(2, repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Global) + .Select(x => x.Value) + .Count()); + + repo.Config.UnsetAll("unittests.plugin"); + + Assert.Equal(2, repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Global) + .Select(x => x.Value) + .Count()); + + repo.Config.UnsetAll("unittests.plugin", ConfigurationLevel.Global); + + Assert.Empty(repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin") + .Select(x => x.Value) + .ToArray()); + } + } + + [Fact] + public void CanUnsetAllFromTheLocalConfiguration() + { + string path = SandboxStandardTestRepo(); + using (var repo = new Repository(path)) + { + Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); + Assert.Empty(repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin") + .Select(x => x.Value) + .ToArray()); + + repo.Config.Add("unittests.plugin", "value1"); + repo.Config.Add("unittests.plugin", "value2"); + + Assert.Equal(2, repo.Config + .OfType>() + .Where(x => x.Key == "unittests.plugin" && x.Level == ConfigurationLevel.Local) + .Select(x => x.Value) + .Count()); + + repo.Config.UnsetAll("unittests.plugin"); + + Assert.DoesNotContain(repo.Config.OfType>(), x => x.Key == "unittests.plugin"); + } + } + [Fact] public void CanReadBooleanValue() { @@ -59,9 +165,9 @@ public void CanReadBooleanValue() Assert.True(repo.Config.Get("core.ignorecase").Value); Assert.True(repo.Config.GetValueOrDefault("core.ignorecase")); - Assert.Equal(false, repo.Config.GetValueOrDefault("missing.key")); - Assert.Equal(true, repo.Config.GetValueOrDefault("missing.key", true)); - Assert.Equal(true, repo.Config.GetValueOrDefault("missing.key", () => true)); + Assert.False(repo.Config.GetValueOrDefault("missing.key")); + Assert.True(repo.Config.GetValueOrDefault("missing.key", true)); + Assert.True(repo.Config.GetValueOrDefault("missing.key", () => true)); } } @@ -110,26 +216,26 @@ public void CanReadStringValue() Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.GetValueOrDefault("remote", "origin", "fetch")); Assert.Equal("+refs/heads/*:refs/remotes/origin/*", repo.Config.GetValueOrDefault(new[] { "remote", "origin", "fetch" })); - Assert.Equal(null, repo.Config.GetValueOrDefault("missing.key")); - Assert.Equal(null, repo.Config.GetValueOrDefault("missing.key", default(string))); + Assert.Null(repo.Config.GetValueOrDefault("missing.key")); + Assert.Null(repo.Config.GetValueOrDefault("missing.key", default(string))); Assert.Throws(() => repo.Config.GetValueOrDefault("missing.key", default(Func))); Assert.Equal("value", repo.Config.GetValueOrDefault("missing.key", "value")); Assert.Equal("value", repo.Config.GetValueOrDefault("missing.key", () => "value")); - Assert.Equal(null, repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local)); - Assert.Equal(null, repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local, default(string))); + Assert.Null(repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local)); + Assert.Null(repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local, default(string))); Assert.Throws(() => repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local, default(Func))); Assert.Equal("value", repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local, "value")); Assert.Equal("value", repo.Config.GetValueOrDefault("missing.key", ConfigurationLevel.Local, () => "value")); - Assert.Equal(null, repo.Config.GetValueOrDefault("missing", "config", "key")); - Assert.Equal(null, repo.Config.GetValueOrDefault("missing", "config", "key", default(string))); + Assert.Null(repo.Config.GetValueOrDefault("missing", "config", "key")); + Assert.Null(repo.Config.GetValueOrDefault("missing", "config", "key", default(string))); Assert.Throws(() => repo.Config.GetValueOrDefault("missing", "config", "key", default(Func))); Assert.Equal("value", repo.Config.GetValueOrDefault("missing", "config", "key", "value")); Assert.Equal("value", repo.Config.GetValueOrDefault("missing", "config", "key", () => "value")); - Assert.Equal(null, repo.Config.GetValueOrDefault(new[] { "missing", "key" })); - Assert.Equal(null, repo.Config.GetValueOrDefault(new[] { "missing", "key" }, default(string))); + Assert.Null(repo.Config.GetValueOrDefault(new[] { "missing", "key" })); + Assert.Null(repo.Config.GetValueOrDefault(new[] { "missing", "key" }, default(string))); Assert.Throws(() => repo.Config.GetValueOrDefault(new[] { "missing", "key" }, default(Func))); Assert.Equal("value", repo.Config.GetValueOrDefault(new[] { "missing", "key" }, "value")); Assert.Equal("value", repo.Config.GetValueOrDefault(new[] { "missing", "key" }, () => "value")); @@ -418,20 +524,20 @@ public void CanSetAndGetSearchPath() { string globalPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); string systemPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); - string xdgPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); + string xdgPath = Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()); GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, globalPath); GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.System, systemPath); - GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Xdg, xdgPath); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Xdg, xdgPath); Assert.Equal(globalPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global).Single()); Assert.Equal(systemPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.System).Single()); - Assert.Equal(xdgPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Xdg).Single()); + Assert.Equal(xdgPath, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Xdg).Single()); // reset the search paths to their defaults GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.System, null); - GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Xdg, null); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Xdg, null); } [Fact] @@ -464,12 +570,12 @@ public void CanResetSearchPaths() var newPaths = new string[] { Path.Combine(Constants.TemporaryReposPath, Path.GetRandomFileName()) }; // change to the non-default path - GlobalSettings.SetConfigSearchPaths (ConfigurationLevel.Global, newPaths); - Assert.Equal (newPaths, GlobalSettings.GetConfigSearchPaths (ConfigurationLevel.Global)); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, newPaths); + Assert.Equal(newPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global)); // set it back to the default - GlobalSettings.SetConfigSearchPaths (ConfigurationLevel.Global, null); - Assert.Equal (oldPaths, GlobalSettings.GetConfigSearchPaths (ConfigurationLevel.Global)); + GlobalSettings.SetConfigSearchPaths(ConfigurationLevel.Global, null); + Assert.Equal(oldPaths, GlobalSettings.GetConfigSearchPaths(ConfigurationLevel.Global)); } [Fact] diff --git a/LibGit2Sharp.Tests/ConflictFixture.cs b/LibGit2Sharp.Tests/ConflictFixture.cs index cabf4a4cf..6317bf431 100644 --- a/LibGit2Sharp.Tests/ConflictFixture.cs +++ b/LibGit2Sharp.Tests/ConflictFixture.cs @@ -48,7 +48,7 @@ private static List RenameConflictData [Theory] [InlineData(true, "ancestor-and-ours.txt", true, false, FileStatus.DeletedFromIndex, 2)] - [InlineData(false, "ancestor-and-ours.txt", true, true, FileStatus.DeletedFromIndex |FileStatus.NewInWorkdir, 2)] + [InlineData(false, "ancestor-and-ours.txt", true, true, FileStatus.DeletedFromIndex | FileStatus.NewInWorkdir, 2)] [InlineData(true, "ancestor-and-theirs.txt", true, false, FileStatus.Nonexistent, 2)] [InlineData(false, "ancestor-and-theirs.txt", true, true, FileStatus.NewInWorkdir, 2)] [InlineData(true, "ancestor-only.txt", false, false, FileStatus.Nonexistent, 1)] @@ -75,7 +75,7 @@ public void CanResolveConflictsByRemovingFromTheIndex( Assert.Equal(existsBeforeRemove, File.Exists(fullpath)); Assert.NotNull(repo.Index.Conflicts[filename]); - Assert.Equal(0, repo.Index.Conflicts.ResolvedConflicts.Count()); + Assert.Empty(repo.Index.Conflicts.ResolvedConflicts); Commands.Remove(repo, filename, removeFromWorkdir); @@ -84,7 +84,7 @@ public void CanResolveConflictsByRemovingFromTheIndex( Assert.Equal(existsAfterRemove, File.Exists(fullpath)); Assert.Equal(lastStatus, repo.RetrieveStatus(filename)); - Assert.Equal(1, repo.Index.Conflicts.ResolvedConflicts.Count()); + Assert.Single(repo.Index.Conflicts.ResolvedConflicts); Assert.NotNull(repo.Index.Conflicts.ResolvedConflicts[filename]); } } @@ -101,7 +101,7 @@ public void CanGetOriginalNamesOfRenameConflicts() Assert.Equal(expected.Count, actual.Count()); int i = 0; - foreach(var name in actual) + foreach (var name in actual) { Assert.Equal(expected[i][0], name.Ancestor); Assert.Equal(expected[i][1], name.Ours); diff --git a/LibGit2Sharp.Tests/DescribeFixture.cs b/LibGit2Sharp.Tests/DescribeFixture.cs index ca859b9cd..bb2cacd06 100644 --- a/LibGit2Sharp.Tests/DescribeFixture.cs +++ b/LibGit2Sharp.Tests/DescribeFixture.cs @@ -23,7 +23,7 @@ public void CanDescribeACommit() // No lightweight tags can either be used to describe "master" Assert.Throws(() => repo.Describe(masterTip, - new DescribeOptions{ Strategy = DescribeStrategy.Tags })); + new DescribeOptions { Strategy = DescribeStrategy.Tags })); repo.ApplyTag("myTag", "5b5b025afb0b4c913b4c338a42934a3863bf3644"); Assert.Equal("myTag-5-g4c062a6", repo.Describe(masterTip, @@ -47,7 +47,7 @@ public void CanDescribeACommit() var anotherTip = repo.Branches["ForLackOfABetterName"].Tip; Assert.Equal("test", repo.Describe(anotherTip)); Assert.Equal("test-0-g7b43849", repo.Describe(anotherTip, - new DescribeOptions{ AlwaysRenderLongFormat = true })); + new DescribeOptions { AlwaysRenderLongFormat = true })); } } diff --git a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs index 00ef0ab2b..046fe5214 100644 --- a/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs +++ b/LibGit2Sharp.Tests/DiffBlobToBlobFixture.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Linq; using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -126,5 +127,119 @@ public void ComparingTwoNullBlobsReturnsAnEmptyContentChanges() Assert.Equal(0, changes.LinesDeleted); } } + + [Fact] + public void ComparingBlobsWithNoSpacesAndIndentHeuristicOptionMakesADifference() + { + var path = SandboxStandardTestRepoGitDir(); + using (var repo = new Repository(path)) + { + // Based on test diff indent heuristic from: + // https://github.com/git/git/blob/433860f3d0beb0c6f205290bd16cda413148f098/t/t4061-diff-indent.sh#L17 + var oldContent = +@" 1 + 2 + a + + b + 3 + 4"; + var newContent = +@" 1 + 2 + a + + b + a + + b + 3 + 4"; + var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent))); + var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent))); + var noIndentHeuristicOption = new CompareOptions { IndentHeuristic = false }; + var indentHeuristicOption = new CompareOptions { IndentHeuristic = true }; + + ContentChanges changes0 = repo.Diff.Compare(oldBlob, newBlob, noIndentHeuristicOption); + ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption); + + Assert.NotEqual(changes0.Patch, changes1.Patch); + Assert.Equal(CanonicalChangedLines(changes0), CanonicalChangedLines(changes1)); + } + } + + [Fact] + public void ComparingBlobsWithNoSpacesIndentHeuristicOptionMakesNoDifference() + { + var path = SandboxStandardTestRepoGitDir(); + using (var repo = new Repository(path)) + { + var oldContent = +@" 1 + 2 + a + b + 3 + 4"; + var newContent = +@" 1 + 2 + a + b + a + b + 3 + 4"; + var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent))); + var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent))); + var noIndentHeuristicOption = new CompareOptions { IndentHeuristic = false }; + var indentHeuristicOption = new CompareOptions { IndentHeuristic = true }; + + ContentChanges changes0 = repo.Diff.Compare(oldBlob, newBlob, noIndentHeuristicOption); + ContentChanges changes1 = repo.Diff.Compare(oldBlob, newBlob, indentHeuristicOption); + + Assert.Equal(changes0.Patch, changes1.Patch); + } + } + + [Fact] + public void DiffSetsTheAddedAndDeletedLinesCorrectly() + { + var path = SandboxStandardTestRepoGitDir(); + + using (var repo = new Repository(path)) + { + var oldContent = + @"1 +2 +3 +4"; + + var newContent = + @"1 +2 +3 +5"; + var oldBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(oldContent))); + var newBlob = repo.ObjectDatabase.CreateBlob(new MemoryStream(Encoding.UTF8.GetBytes(newContent))); + + ContentChanges changes = repo.Diff.Compare(oldBlob, newBlob); + + Assert.Single(changes.AddedLines); + Assert.Single(changes.DeletedLines); + + Assert.Equal("4", changes.DeletedLines.First().Content); + Assert.Equal("5", changes.AddedLines.First().Content); + + Assert.Equal(4, changes.DeletedLines.First().LineNumber); + Assert.Equal(4, changes.AddedLines.First().LineNumber); + } + } + + static string CanonicalChangedLines(ContentChanges changes) + { + // Create an ordered representation of lines that have been added or removed + return string.Join("\n", changes.Patch.Split('\n').Where(l => l.StartsWith("+") || l.StartsWith("-")).OrderBy(l => l)); + } } } diff --git a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs index 71aad3755..b712a214b 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs @@ -17,7 +17,7 @@ private static void SetUpSimpleDiffContext(IRepository repo) File.AppendAllText(fullpath, "world\n"); - Commands.Stage(repo,fullpath); + Commands.Stage(repo, fullpath); File.AppendAllText(fullpath, "!!!\n"); } @@ -46,7 +46,7 @@ public void CanCompareASimpleTreeAgainstTheWorkDir() using (var changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.WorkingDirectory)) { - Assert.Equal(1, changes.Modified.Count()); + Assert.Single(changes.Modified); } using (var patch = repo.Diff.Compare(repo.Head.Tip.Tree, @@ -116,7 +116,7 @@ public void CanCompareASimpleTreeAgainstTheWorkDirAndTheIndex() using (var changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index | DiffTargets.WorkingDirectory)) { - Assert.Equal(1, changes.Modified.Count()); + Assert.Single(changes.Modified); } using (var patch = repo.Diff.Compare(repo.Head.Tip.Tree, @@ -178,8 +178,8 @@ public void ShowcaseTheDifferenceBetweenTheTwoKindOfComparison() using (var wrkDirToIdxToTree = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index | DiffTargets.WorkingDirectory)) { - Assert.Equal(1, wrkDirToIdxToTree.Deleted.Count()); - Assert.Equal(0, wrkDirToIdxToTree.Modified.Count()); + Assert.Single(wrkDirToIdxToTree.Deleted); + Assert.Empty(wrkDirToIdxToTree.Modified); } using (var patch = repo.Diff.Compare(repo.Head.Tip.Tree, @@ -200,8 +200,8 @@ public void ShowcaseTheDifferenceBetweenTheTwoKindOfComparison() using (var wrkDirToTree = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.WorkingDirectory)) { - Assert.Equal(0, wrkDirToTree.Deleted.Count()); - Assert.Equal(1, wrkDirToTree.Modified.Count()); + Assert.Empty(wrkDirToTree.Deleted); + Assert.Single(wrkDirToTree.Modified); } using (var patch = repo.Diff.Compare(repo.Head.Tip.Tree, @@ -244,7 +244,7 @@ public void CanCompareASimpleTreeAgainstTheIndex() using (var changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index)) { - Assert.Equal(1, changes.Modified.Count()); + Assert.Single(changes.Modified); } using (var patch = repo.Diff.Compare(repo.Head.Tip.Tree, @@ -331,7 +331,7 @@ public void CanCompareASubsetofTheTreeAgainstTheIndex() { Assert.NotNull(changes); - Assert.Equal(1, changes.Count()); + Assert.Single(changes); Assert.Equal("deleted_staged_file.txt", changes.Deleted.Single().Path); } } @@ -340,7 +340,7 @@ public void CanCompareASubsetofTheTreeAgainstTheIndex() private static void AssertCanCompareASubsetOfTheTreeAgainstTheIndex(TreeChanges changes) { Assert.NotNull(changes); - Assert.Equal(1, changes.Count()); + Assert.Single(changes); Assert.Equal("deleted_staged_file.txt", changes.Deleted.Single().Path); } @@ -413,7 +413,7 @@ public void CanCopeWithEndOfFileNewlineChanges() using (var changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index)) { - Assert.Equal(1, changes.Modified.Count()); + Assert.Single(changes.Modified); } using (var patch = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index)) @@ -462,8 +462,8 @@ public void CanCompareANullTreeAgainstTheIndex() using (var changes = repo.Diff.Compare(null, DiffTargets.Index)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Added.Count()); + Assert.Single(changes); + Assert.Single(changes.Added); Assert.Equal("file.txt", changes.Added.Single().Path); } @@ -482,8 +482,8 @@ public void CanCompareANullTreeAgainstTheWorkdir() using (var changes = repo.Diff.Compare(null, DiffTargets.WorkingDirectory)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Added.Count()); + Assert.Single(changes); + Assert.Single(changes.Added); Assert.Equal("file.txt", changes.Added.Single().Path); } @@ -502,12 +502,32 @@ public void CanCompareANullTreeAgainstTheWorkdirAndTheIndex() using (var changes = repo.Diff.Compare(null, DiffTargets.WorkingDirectory | DiffTargets.Index)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Added.Count()); + Assert.Single(changes); + Assert.Single(changes.Added); Assert.Equal("file.txt", changes.Added.Single().Path); } } } + + [Fact] + public void CompareSetsCorrectAddedAndDeletedLines() + { + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + SetUpSimpleDiffContext(repo); + + using (var changes = repo.Diff.Compare(repo.Head.Tip.Tree, + DiffTargets.WorkingDirectory | DiffTargets.Index)) + { + foreach (var entry in changes) + { + Assert.Equal(2, entry.AddedLines.Count()); + } + } + } + } } } diff --git a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs index f51def511..8c2956331 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs @@ -4,13 +4,12 @@ using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { public class DiffTreeToTreeFixture : BaseFixture { - private static readonly string subBranchFilePath = Path.Combine("1", "branch_file.txt"); + private static readonly string subBranchFilePath = string.Join("/", "1", "branch_file.txt"); [Fact] public void ComparingATreeAgainstItselfReturnsNoDifference() @@ -20,7 +19,7 @@ public void ComparingATreeAgainstItselfReturnsNoDifference() { Tree tree = repo.Head.Tip.Tree; - using(var changes = repo.Diff.Compare(tree, tree)) + using (var changes = repo.Diff.Compare(tree, tree)) { Assert.Empty(changes); } @@ -28,7 +27,7 @@ public void ComparingATreeAgainstItselfReturnsNoDifference() using (var patch = repo.Diff.Compare(tree, tree)) { Assert.Empty(patch); - Assert.Equal(String.Empty, patch); + Assert.Equal(string.Empty, patch); } } } @@ -64,8 +63,8 @@ public void CanCompareACommitTreeAgainstItsParent() using (var changes = repo.Diff.Compare(parentCommitTree, commitTree)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Added.Count()); + Assert.Single(changes); + Assert.Single(changes.Added); TreeEntryChanges treeEntryChanges = changes.Single(c => c.Path == "1.txt"); @@ -112,13 +111,13 @@ public void CanDetectABinaryChange() File.AppendAllText(filepath, "abcdef"); - using(var patch = repo.Diff.Compare(commit.Tree, DiffTargets.WorkingDirectory, new[] { filename })) + using (var patch = repo.Diff.Compare(commit.Tree, DiffTargets.WorkingDirectory, new[] { filename })) Assert.True(patch[filename].IsBinaryComparison); Commands.Stage(repo, filename); var commit2 = repo.Commit("Update binary file", Constants.Signature, Constants.Signature); - using(var patch2 = repo.Diff.Compare(commit.Tree, commit2.Tree, new[] { filename })) + using (var patch2 = repo.Diff.Compare(commit.Tree, commit2.Tree, new[] { filename })) Assert.True(patch2[filename].IsBinaryComparison); } } @@ -138,13 +137,13 @@ public void CanDetectABinaryDeletion() File.Delete(filepath); - using(var patch = repo.Diff.Compare(commit.Tree, DiffTargets.WorkingDirectory, new [] {filename})) + using (var patch = repo.Diff.Compare(commit.Tree, DiffTargets.WorkingDirectory, new[] { filename })) Assert.True(patch[filename].IsBinaryComparison); Commands.Remove(repo, filename); var commit2 = repo.Commit("Delete binary file", Constants.Signature, Constants.Signature); - using(var patch2 = repo.Diff.Compare(commit.Tree, commit2.Tree, new[] { filename })) + using (var patch2 = repo.Diff.Compare(commit.Tree, commit2.Tree, new[] { filename })) Assert.True(patch2[filename].IsBinaryComparison); } } @@ -172,7 +171,7 @@ public void CanCompareASubsetofTheTreeAgainstOneOfItsAncestor() { Assert.NotNull(changes); - Assert.Equal(1, changes.Count()); + Assert.Single(changes); Assert.Equal(subBranchFilePath, changes.Added.Single().Path); } } @@ -205,7 +204,7 @@ public void CanCompareACommitTreeAgainstATreeWithNoCommonAncestor() { Assert.Equal(10, changes.Count()); Assert.Equal(9, changes.Added.Count()); - Assert.Equal(1, changes.Deleted.Count()); + Assert.Single(changes.Deleted); Assert.Equal("readme.txt", changes.Deleted.Single().Path); Assert.Equal(new[] { "1.txt", subBranchFilePath, "README", "branch_file.txt", "deleted_staged_file.txt", "deleted_unstaged_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt", "new.txt" }, @@ -233,13 +232,13 @@ public void CanCompareATreeAgainstAnotherTreeWithLaxExplicitPathsValidationAndNo using (var changes = repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree, new[] { "if-I-exist-this-test-is-really-unlucky.txt" }, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false })) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } using (var changes = repo.Diff.Compare(commitTreeWithDifferentAncestor, commitTree, new[] { "if-I-exist-this-test-is-really-unlucky.txt" })) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } } } @@ -288,9 +287,9 @@ public void DetectsTheRenamingOfAModifiedFileByDefault() using (var changes = repo.Diff.Compare(rootCommitTree, commitTreeWithRenamedFile)) { - Assert.Equal(1, changes.Count()); + Assert.Single(changes); Assert.Equal("my-name-does-not-feel-right.txt", changes.Single(c => c.Path == "super-file.txt").OldPath); - Assert.Equal(1, changes.Renamed.Count()); + Assert.Single(changes.Renamed); } } } @@ -317,8 +316,8 @@ public void DetectsTheExactRenamingOfFilesByDefault() using (var changes = repo.Diff.Compare(old.Tree, @new.Tree)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Renamed.Count()); + Assert.Single(changes); + Assert.Single(changes.Renamed); Assert.Equal(originalPath, changes.Renamed.Single().OldPath); Assert.Equal(renamedPath, changes.Renamed.Single().Path); } @@ -370,7 +369,7 @@ public void RenameThresholdsAreObeyed() compareOptions.Similarity.RenameThreshold = 90; using (var changes = repo.Diff.Compare(old.Tree, @new.Tree, compareOptions: compareOptions)) - Assert.False(changes.Any(x => x.Status == ChangeKind.Renamed)); + Assert.DoesNotContain(changes, x => x.Status == ChangeKind.Renamed); } } @@ -400,8 +399,8 @@ public void ExactModeDetectsExactRenames() Similarity = SimilarityOptions.Exact, })) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Renamed.Count()); + Assert.Single(changes); + Assert.Single(changes.Renamed); Assert.Equal(originalPath, changes.Renamed.Single().OldPath); Assert.Equal(renamedPath, changes.Renamed.Single().Path); } @@ -435,8 +434,8 @@ public void ExactModeDetectsExactCopies() Similarity = SimilarityOptions.Exact, })) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Copied.Count()); + Assert.Single(changes); + Assert.Single(changes.Copied); } } } @@ -470,9 +469,9 @@ public void ExactModeDoesntDetectRenamesWithEdits() })) { Assert.Equal(2, changes.Count()); - Assert.Equal(0, changes.Renamed.Count()); - Assert.Equal(1, changes.Added.Count()); - Assert.Equal(1, changes.Deleted.Count()); + Assert.Empty(changes.Renamed); + Assert.Single(changes.Added); + Assert.Single(changes.Deleted); } } } @@ -509,8 +508,8 @@ public void CanIncludeUnmodifiedEntriesWhenDetectingTheExactRenamingOfFilesWhenE })) { Assert.Equal(2, changes.Count()); - Assert.Equal(1, changes.Unmodified.Count()); - Assert.Equal(1, changes.Copied.Count()); + Assert.Single(changes.Unmodified); + Assert.Single(changes.Copied); Assert.Equal(originalPath, changes.Copied.Single().OldPath); Assert.Equal(copiedPath, changes.Copied.Single().Path); } @@ -545,7 +544,7 @@ public void CanNotDetectTheExactRenamingFilesWhenNotEnabled() })) { Assert.Equal(2, changes.Count()); - Assert.Equal(0, changes.Renamed.Count()); + Assert.Empty(changes.Renamed); } } } @@ -580,8 +579,8 @@ public void CanDetectTheExactCopyingOfNonModifiedFilesWhenEnabled() Similarity = SimilarityOptions.CopiesHarder, })) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Copied.Count()); + Assert.Single(changes); + Assert.Single(changes.Copied); Assert.Equal(originalPath, changes.Copied.Single().OldPath); Assert.Equal(copiedPath, changes.Copied.Single().Path); } @@ -613,8 +612,8 @@ public void CanNotDetectTheExactCopyingOfNonModifiedFilesWhenNotEnabled() using (var changes = repo.Diff.Compare(old.Tree, @new.Tree)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(0, changes.Copied.Count()); + Assert.Single(changes); + Assert.Empty(changes.Copied); } } } @@ -653,7 +652,7 @@ public void CanDetectTheExactCopyingOfModifiedFilesWhenEnabled() })) { Assert.Equal(2, changes.Count()); - Assert.Equal(1, changes.Copied.Count()); + Assert.Single(changes.Copied); Assert.Equal(originalPath, changes.Copied.Single().OldPath); Assert.Equal(copiedPath, changes.Copied.Single().Path); } @@ -689,7 +688,7 @@ public void CanNotDetectTheExactCopyingOfModifiedFilesWhenNotEnabled() using (var changes = repo.Diff.Compare(old.Tree, @new.Tree)) { Assert.Equal(2, changes.Count()); - Assert.Equal(0, changes.Copied.Count()); + Assert.Empty(changes.Copied); } } } @@ -704,7 +703,7 @@ public void CanIncludeUnmodifiedEntriesWhenEnabled() Touch(repo.Info.WorkingDirectory, "a.txt", "abc\ndef\n"); Touch(repo.Info.WorkingDirectory, "b.txt", "abc\ndef\n"); - Commands.Stage(repo, new[] {"a.txt", "b.txt"}); + Commands.Stage(repo, new[] { "a.txt", "b.txt" }); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, "b.txt"), "ghi\njkl\n"); @@ -715,8 +714,8 @@ public void CanIncludeUnmodifiedEntriesWhenEnabled() compareOptions: new CompareOptions { IncludeUnmodified = true })) { Assert.Equal(2, changes.Count()); - Assert.Equal(1, changes.Unmodified.Count()); - Assert.Equal(1, changes.Modified.Count()); + Assert.Single(changes.Unmodified); + Assert.Single(changes.Modified); } } } @@ -728,12 +727,12 @@ public void CanDetectTheExactRenamingExactCopyingOfNonModifiedAndModifiedFilesWh var path = Repository.Init(scd.DirectoryPath); using (var repo = new Repository(path)) { - const string originalPath = "original.txt"; - const string renamedPath = "renamed.txt"; + const string originalPath = "original.txt"; + const string renamedPath = "renamed.txt"; const string originalPath2 = "original2.txt"; - const string copiedPath1 = "copied.txt"; + const string copiedPath1 = "copied.txt"; const string originalPath3 = "original3.txt"; - const string copiedPath2 = "copied2.txt"; + const string copiedPath2 = "copied2.txt"; Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); Touch(repo.Info.WorkingDirectory, originalPath2, "1\n2\n3\n4\n"); @@ -768,8 +767,8 @@ public void CanDetectTheExactRenamingExactCopyingOfNonModifiedAndModifiedFilesWh })) { Assert.Equal(4, changes.Count()); - Assert.Equal(1, changes.Modified.Count()); - Assert.Equal(1, changes.Renamed.Count()); + Assert.Single(changes.Modified); + Assert.Single(changes.Renamed); Assert.Equal(originalPath, changes.Renamed.Single().OldPath); Assert.Equal(renamedPath, changes.Renamed.Single().Path); Assert.Equal(2, changes.Copied.Count()); @@ -818,8 +817,8 @@ public void CanCompareTwoVersionsOfAFileWithATrailingNewlineDeletion(int context using (var changes = repo.Diff.Compare(rootCommitTree, commitTreeWithUpdatedFile)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Modified.Count()); + Assert.Single(changes); + Assert.Single(changes.Modified); } using (var patch = repo.Diff.Compare(rootCommitTree, commitTreeWithUpdatedFile, @@ -917,9 +916,9 @@ public void CanCompareTwoVersionsOfAFileWithADiffOfTwoHunks(int contextLines, in using (var changes = repo.Diff.Compare(rootCommitTree, mergedCommitTree, compareOptions: compareOptions)) { Assert.Equal(3, changes.Count()); - Assert.Equal(1, changes.Modified.Count()); - Assert.Equal(1, changes.Deleted.Count()); - Assert.Equal(1, changes.Added.Count()); + Assert.Single(changes.Modified); + Assert.Single(changes.Deleted); + Assert.Single(changes.Added); Assert.Equal(Mode.Nonexistent, changes.Single(c => c.Path == "my-name-does-not-feel-right.txt").Mode); } @@ -983,10 +982,10 @@ public void CanHandleTwoTreeEntryChangesWithTheSamePathUsingSimilarityNone() (path, changes) => { Assert.Equal(2, changes.Count()); - Assert.Equal(1, changes.Deleted.Count()); - Assert.Equal(1, changes.TypeChanged.Count()); + Assert.Single(changes.Deleted); + Assert.Single(changes.TypeChanged); - TreeEntryChanges change = changes.Single(c => c.Path== path); + TreeEntryChanges change = changes.Single(c => c.Path == path); Assert.Equal(Mode.SymbolicLink, change.OldMode); Assert.Equal(Mode.NonExecutableFile, change.Mode); Assert.Equal(ChangeKind.TypeChanged, change.Status); @@ -1005,8 +1004,8 @@ public void CanHandleTwoTreeEntryChangesWithTheSamePathUsingSimilarityDefault() (path, changes) => { Assert.Equal(2, changes.Count()); - Assert.Equal(1, changes.Deleted.Count()); - Assert.Equal(1, changes.Renamed.Count()); + Assert.Single(changes.Deleted); + Assert.Single(changes.Renamed); TreeEntryChanges renamed = changes.Renamed.Single(); Assert.Equal(Mode.NonExecutableFile, renamed.OldMode); @@ -1032,16 +1031,16 @@ public void CanCompareATreeAgainstANullTree() using (var changes = repo.Diff.Compare(tree, null)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Deleted.Count()); + Assert.Single(changes); + Assert.Single(changes.Deleted); Assert.Equal("readme.txt", changes.Deleted.Single().Path); } using (var changes = repo.Diff.Compare(null, tree)) { - Assert.Equal(1, changes.Count()); - Assert.Equal(1, changes.Added.Count()); + Assert.Single(changes); + Assert.Single(changes.Added); Assert.Equal("readme.txt", changes.Added.Single().Path); } @@ -1056,7 +1055,7 @@ public void ComparingTwoNullTreesReturnsAnEmptyTreeChanges() { using (var changes = repo.Diff.Compare(default(Tree), default(Tree))) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } } } @@ -1087,9 +1086,9 @@ public void ComparingReliesOnProvidedConfigEntriesIfAny() using (var repo = new Repository(path)) { SetFilemode(repo, true); - using(var changes = repo.Diff.Compare(new[] { file })) + using (var changes = repo.Diff.Compare(new[] { file })) { - Assert.Equal(1, changes.Count()); + Assert.Single(changes); var change = changes.Modified.Single(); Assert.Equal(Mode.ExecutableFile, change.OldMode); @@ -1102,7 +1101,7 @@ public void ComparingReliesOnProvidedConfigEntriesIfAny() SetFilemode(repo, false); using (var changes = repo.Diff.Compare(new[] { file })) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } } } @@ -1147,6 +1146,44 @@ public void RetrievingDiffChangesMustAlwaysBeCaseSensitive() } } + [Fact] + public void RetrievingDiffContainsRightAmountOfAddedAndDeletedLines() + { + ObjectId treeOldOid, treeNewOid; + + string repoPath = InitNewRepository(); + + using (var repo = new Repository(repoPath)) + { + Blob oldContent = OdbHelper.CreateBlob(repo, "awesome content\n"); + Blob newContent = OdbHelper.CreateBlob(repo, "more awesome content\n"); + + var td = new TreeDefinition() + .Add("A.TXT", oldContent, Mode.NonExecutableFile) + .Add("a.txt", oldContent, Mode.NonExecutableFile); + + treeOldOid = repo.ObjectDatabase.CreateTree(td).Id; + + td = new TreeDefinition() + .Add("A.TXT", newContent, Mode.NonExecutableFile) + .Add("a.txt", newContent, Mode.NonExecutableFile); + + treeNewOid = repo.ObjectDatabase.CreateTree(td).Id; + } + + using (var repo = new Repository(repoPath)) + { + using (var changes = repo.Diff.Compare(repo.Lookup(treeOldOid), repo.Lookup(treeNewOid))) + { + foreach (var entry in changes) + { + Assert.Single(entry.AddedLines); + Assert.Single(entry.DeletedLines); + } + } + } + } + [Fact] public void UsingPatienceAlgorithmCompareOptionProducesPatienceDiff() { @@ -1219,5 +1256,40 @@ public void UsingPatienceAlgorithmCompareOptionProducesPatienceDiff() Assert.Equal(diffPatience, changes); } } + + [Fact] + public void DiffThrowsANotFoundExceptionIfATreeIsMissing() + { + string repoPath = SandboxBareTestRepo(); + + // Manually delete the tree object to simulate a partial clone + File.Delete(Path.Combine(repoPath, "objects", "58", "1f9824ecaf824221bd36edf5430f2739a7c4f5")); + + using (var repo = new Repository(repoPath)) + { + // The commit is there but its tree is missing + var commit = repo.Lookup("4c062a6361ae6959e06292c1fa5e2822d9c96345"); + Assert.NotNull(commit); + Assert.Equal("581f9824ecaf824221bd36edf5430f2739a7c4f5", commit.Tree.Sha); + Assert.True(commit.Tree.IsMissing); + + var tree = repo.Lookup("581f9824ecaf824221bd36edf5430f2739a7c4f5"); + Assert.Null(tree); + + var otherCommit = repo.Lookup("be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + Assert.NotNull(otherCommit); + Assert.False(otherCommit.Tree.IsMissing); + + Assert.Throws(() => + { + using (repo.Diff.Compare(commit.Tree, otherCommit.Tree)) { } + }); + + Assert.Throws(() => + { + using (repo.Diff.Compare(otherCommit.Tree, commit.Tree)) { } + }); + } + } } } diff --git a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs index ba7658ebc..c6ef700bb 100644 --- a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs +++ b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs @@ -54,12 +54,12 @@ public void CanCompareTheWorkDirAgainstTheIndexWithLaxUnmatchedExplicitPathsVali using (var changes = repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false })) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } using (var changes = repo.Diff.Compare(new[] { relativePath })) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } } } @@ -137,9 +137,9 @@ public void ComparingReliesOnProvidedConfigEntriesIfAny() using (var repo = new Repository(path)) { SetFilemode(repo, true); - using(var changes = repo.Diff.Compare(new[] { file })) + using (var changes = repo.Diff.Compare(new[] { file })) { - Assert.Equal(1, changes.Count()); + Assert.Single(changes); var change = changes.Modified.Single(); Assert.Equal(Mode.ExecutableFile, change.OldMode); @@ -150,9 +150,9 @@ public void ComparingReliesOnProvidedConfigEntriesIfAny() using (var repo = new Repository(path)) { SetFilemode(repo, false); - using(var changes = repo.Diff.Compare(new[] { file })) + using (var changes = repo.Diff.Compare(new[] { file })) { - Assert.Equal(0, changes.Count()); + Assert.Empty(changes); } } } diff --git a/LibGit2Sharp.Tests/EpochFixture.cs b/LibGit2Sharp.Tests/EpochFixture.cs deleted file mode 100644 index 773f4c846..000000000 --- a/LibGit2Sharp.Tests/EpochFixture.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using LibGit2Sharp.Core; -using Xunit; -using Xunit.Extensions; - -namespace LibGit2Sharp.Tests -{ - public class EpochFixture - { - [Theory] - [InlineData(0)] - [InlineData(17)] - public void UnixTimestampShouldBeCastIntoAUtcBasedDateTimeOffset(long secondsSinceEpoch) - { - DateTimeOffset date = Epoch.ToDateTimeOffset(secondsSinceEpoch, 0); - Assert.Equal(0, date.Offset.TotalMinutes); - - Assert.Equal(TimeSpan.Zero, date.Offset); - Assert.Equal(DateTimeKind.Utc, date.UtcDateTime.Kind); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(17, -120)] - [InlineData(31, 60)] - public void AreEqual(long secondsSinceEpoch, int timezoneOffset) - { - DateTimeOffset one = Epoch.ToDateTimeOffset(secondsSinceEpoch, timezoneOffset); - DateTimeOffset another = Epoch.ToDateTimeOffset(secondsSinceEpoch, timezoneOffset); - - Assert.Equal(one, another); - Assert.Equal(another, one); - - Assert.True(one == another); - Assert.True(another == one); - - Assert.False(one != another); - Assert.False(another != one); - - Assert.Equal(one.GetHashCode(), another.GetHashCode()); - } - - [Theory] - [InlineData(1291801952, "Wed, 08 Dec 2010 09:52:32 +0000")] - [InlineData(1234567890, "Fri, 13 Feb 2009 23:31:30 +0000")] - [InlineData(1288114383, "Tue, 26 Oct 2010 17:33:03 +0000")] - public void UnixTimestampShouldShouldBeCastIntoAPlainUtcDate(long secondsSinceEpoch, string expected) - { - DateTimeOffset expectedDate = DateTimeOffset.Parse(expected); - - DateTimeOffset date = Epoch.ToDateTimeOffset(secondsSinceEpoch, 0); - - Assert.Equal(secondsSinceEpoch, date.ToSecondsSinceEpoch()); - Assert.Equal(expectedDate, date); - Assert.Equal(TimeSpan.Zero, date.Offset); - } - - [Theory] - [InlineData(1250379778, -210, "Sat, 15 Aug 2009 20:12:58 -0330")] - public void UnixTimestampAndTimezoneOffsetShouldBeCastIntoAUtcDateBearingAnOffset(long secondsSinceEpoch, Int32 offset, string expected) - { - DateTimeOffset expectedDate = DateTimeOffset.Parse(expected); - - DateTimeOffset date = Epoch.ToDateTimeOffset(secondsSinceEpoch, offset); - Assert.Equal(offset, date.Offset.TotalMinutes); - Assert.Equal(secondsSinceEpoch, date.ToSecondsSinceEpoch()); - - Assert.Equal(expectedDate, date); - Assert.Equal(expectedDate.Offset, date.Offset); - } - - [Theory] - [InlineData("Wed, 08 Dec 2010 09:52:32 +0000", 1291801952, 0)] - [InlineData("Fri, 13 Feb 2009 23:31:30 +0000", 1234567890, 0)] - [InlineData("Tue, 26 Oct 2010 17:33:03 +0000", 1288114383, 0)] - [InlineData("Sat, 14 Feb 2009 00:31:30 +0100", 1234567890, 60)] - [InlineData("Sat, 15 Aug 2009 20:12:58 -0330", 1250379778, -210)] - [InlineData("Sat, 15 Aug 2009 23:42:58 +0000", 1250379778, 0)] - [InlineData("Sun, 16 Aug 2009 00:42:58 +0100", 1250379778, 60)] - public void DateTimeOffsetShoudlBeCastIntoAUnixTimestampAndATimezoneOffset(string formattedDate, long expectedSeconds, Int32 expectedOffset) - { - DateTimeOffset when = DateTimeOffset.Parse(formattedDate); - DateTimeOffset date = Epoch.ToDateTimeOffset(expectedSeconds, expectedOffset); - Assert.Equal(when, date); - } - } -} diff --git a/LibGit2Sharp.Tests/FetchFixture.cs b/LibGit2Sharp.Tests/FetchFixture.cs index 0689bc47e..b36da7ccd 100644 --- a/LibGit2Sharp.Tests/FetchFixture.cs +++ b/LibGit2Sharp.Tests/FetchFixture.cs @@ -4,7 +4,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -15,7 +14,6 @@ public class FetchFixture : BaseFixture [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository.git")] public void CanFetchIntoAnEmptyRepository(string url) { string path = InitNewRepository(); @@ -44,7 +42,7 @@ public void CanFetchIntoAnEmptyRepository(string url) } // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null); + Commands.Fetch(repo, remoteName, Array.Empty(), new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null); // Verify the expected expectedFetchState.CheckUpdatedReferences(repo); @@ -64,7 +62,7 @@ public void CanFetchIntoAnEmptyRepositoryWithCredentials() repo.Network.Remotes.Add(remoteName, Constants.PrivateRepoUrl); // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[0], new FetchOptions + Commands.Fetch(repo, remoteName, Array.Empty(), new FetchOptions { CredentialsProvider = Constants.PrivateRepoCredentials }, null); @@ -74,7 +72,6 @@ public void CanFetchIntoAnEmptyRepositoryWithCredentials() [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository.git")] public void CanFetchAllTagsIntoAnEmptyRepository(string url) { string path = InitNewRepository(); @@ -101,7 +98,8 @@ public void CanFetchAllTagsIntoAnEmptyRepository(string url) } // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { + Commands.Fetch(repo, remoteName, Array.Empty(), new FetchOptions + { TagFetchMode = TagFetchMode.All, OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null); @@ -110,14 +108,13 @@ public void CanFetchAllTagsIntoAnEmptyRepository(string url) expectedFetchState.CheckUpdatedReferences(repo); // Verify the reflog entries - Assert.Equal(1, repo.Refs.Log(string.Format("refs/remotes/{0}/master", remoteName)).Count()); // Branches are also retrieved + Assert.Single(repo.Refs.Log(string.Format("refs/remotes/{0}/master", remoteName))); // Branches are also retrieved } } [Theory] [InlineData("http://github.com/libgit2/TestGitRepository", "test-branch", "master")] [InlineData("https://github.com/libgit2/TestGitRepository", "master", "master")] - [InlineData("git://github.com/libgit2/TestGitRepository.git", "master", "first-merge")] public void CanFetchCustomRefSpecsIntoAnEmptyRepository(string url, string localBranchName, string remoteBranchName) { string path = InitNewRepository(); @@ -147,7 +144,8 @@ public void CanFetchCustomRefSpecsIntoAnEmptyRepository(string url, string local } // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[] { refSpec }, new FetchOptions { + Commands.Fetch(repo, remoteName, new string[] { refSpec }, new FetchOptions + { TagFetchMode = TagFetchMode.None, OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null); @@ -157,7 +155,7 @@ public void CanFetchCustomRefSpecsIntoAnEmptyRepository(string url, string local // Verify the reflog entries var reflogEntry = repo.Refs.Log(string.Format("refs/remotes/{0}/{1}", remoteName, localBranchName)).Single(); - Assert.True(reflogEntry.Message.StartsWith("fetch ")); + Assert.StartsWith("fetch ", reflogEntry.Message); } } @@ -181,7 +179,7 @@ public void FetchRespectsConfiguredAutoTagSetting(TagFetchMode tagFetchMode, int r => r.TagFetchMode = tagFetchMode); // Perform the actual fetch. - Commands.Fetch(repo, remoteName, new string[0], null, null); + Commands.Fetch(repo, remoteName, Array.Empty(), null, null); // Verify the number of fetched tags. Assert.Equal(expectedTagCount, repo.Tags.Count()); @@ -199,7 +197,7 @@ public void CanFetchAllTagsAfterAnInitialClone() using (var repo = new Repository(clonedRepoPath)) { - Commands.Fetch(repo, "origin", new string[0], new FetchOptions { TagFetchMode = TagFetchMode.All }, null); + Commands.Fetch(repo, "origin", Array.Empty(), new FetchOptions { TagFetchMode = TagFetchMode.All }, null); } } @@ -207,7 +205,7 @@ public void CanFetchAllTagsAfterAnInitialClone() public void FetchHonorsTheFetchPruneConfigurationEntry() { var source = SandboxBareTestRepo(); - var url = new Uri(Path.GetFullPath(source)).AbsoluteUri; + var url = new Uri($"file://{Path.GetFullPath(source)}").AbsoluteUri; var scd = BuildSelfCleaningDirectory(); @@ -215,7 +213,7 @@ public void FetchHonorsTheFetchPruneConfigurationEntry() using (var clonedRepo = new Repository(clonedRepoPath)) { - Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote)); + Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote && b.FriendlyName != "origin/HEAD")); // Drop one of the branches in the remote repository using (var sourceRepo = new Repository(source)) @@ -225,19 +223,71 @@ public void FetchHonorsTheFetchPruneConfigurationEntry() // No pruning when the configuration entry isn't defined Assert.Null(clonedRepo.Config.Get("fetch.prune")); - Commands.Fetch(clonedRepo, "origin", new string[0], null, null); - Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote)); + Commands.Fetch(clonedRepo, "origin", Array.Empty(), null, null); + Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote && b.FriendlyName != "origin/HEAD")); // No pruning when the configuration entry is set to false clonedRepo.Config.Set("fetch.prune", false); - Commands.Fetch(clonedRepo, "origin", new string[0], null, null); - Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote)); + Commands.Fetch(clonedRepo, "origin", Array.Empty(), null, null); + Assert.Equal(5, clonedRepo.Branches.Count(b => b.IsRemote && b.FriendlyName != "origin/HEAD")); // Auto pruning when the configuration entry is set to true clonedRepo.Config.Set("fetch.prune", true); - Commands.Fetch(clonedRepo, "origin", new string[0], null, null); - Assert.Equal(4, clonedRepo.Branches.Count(b => b.IsRemote)); + Commands.Fetch(clonedRepo, "origin", Array.Empty(), null, null); + Assert.Equal(4, clonedRepo.Branches.Count(b => b.IsRemote && b.FriendlyName != "origin/HEAD")); + } + } + + [Fact] + public void CannotFetchWithForbiddenCustomHeaders() + { + var scd = BuildSelfCleaningDirectory(); + + const string url = "https://github.com/libgit2/TestGitRepository"; + + string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + const string knownHeader = "User-Agent: mygit-201"; + var options = new FetchOptions { CustomHeaders = new string[] { knownHeader } }; + using (var repo = new Repository(clonedRepoPath)) + { + Assert.Throws(() => Commands.Fetch(repo, "origin", Array.Empty(), options, null)); + } + } + + [Fact] + public void CanFetchWithCustomHeaders() + { + var scd = BuildSelfCleaningDirectory(); + + const string url = "https://github.com/libgit2/TestGitRepository"; + + string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + const string knownHeader = "X-Hello: mygit-201"; + var options = new FetchOptions { CustomHeaders = new string[] { knownHeader } }; + using (var repo = new Repository(clonedRepoPath)) + { + Commands.Fetch(repo, "origin", Array.Empty(), options, null); } } + + [Fact] + public void CannotFetchWithMalformedCustomHeaders() + { + var scd = BuildSelfCleaningDirectory(); + + const string url = "https://github.com/libgit2/TestGitRepository"; + + string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + const string knownHeader = "Hello world"; + var options = new FetchOptions { CustomHeaders = new string[] { knownHeader } }; + using (var repo = new Repository(clonedRepoPath)) + { + Assert.Throws(() => Commands.Fetch(repo, "origin", Array.Empty(), options, null)); + } + } + } } diff --git a/LibGit2Sharp.Tests/FileHistoryFixture.cs b/LibGit2Sharp.Tests/FileHistoryFixture.cs index 7f7a94b4c..dcbd0e6d8 100644 --- a/LibGit2Sharp.Tests/FileHistoryFixture.cs +++ b/LibGit2Sharp.Tests/FileHistoryFixture.cs @@ -10,54 +10,56 @@ namespace LibGit2Sharp.Tests { public class FileHistoryFixture : BaseFixture { - [Theory] - [InlineData("https://github.com/nulltoken/follow-test.git")] - public void CanDealWithFollowTest(string url) - { - var scd = BuildSelfCleaningDirectory(); - var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); - - using (var repo = new Repository(clonedRepoPath)) - { - // $ git log --follow --format=oneline so-renamed.txt - // 88f91835062161febb46fb270ef4188f54c09767 Update not-yet-renamed.txt AND rename into so-renamed.txt - // ef7cb6a63e32595fffb092cb1ae9a32310e58850 Add not-yet-renamed.txt - var fileHistoryEntries = repo.Commits.QueryBy("so-renamed.txt").ToList(); - Assert.Equal(2, fileHistoryEntries.Count()); - Assert.Equal("88f91835062161febb46fb270ef4188f54c09767", fileHistoryEntries[0].Commit.Sha); - Assert.Equal("ef7cb6a63e32595fffb092cb1ae9a32310e58850", fileHistoryEntries[1].Commit.Sha); - - // $ git log --follow --format=oneline untouched.txt - // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit - fileHistoryEntries = repo.Commits.QueryBy("untouched.txt").ToList(); - Assert.Equal(1, fileHistoryEntries.Count()); - Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[0].Commit.Sha); - - // $ git log --follow --format=oneline under-test.txt - // 0b5b18f2feb917dee98df1210315b2b2b23c5bec Rename file renamed.txt into under-test.txt - // 49921d463420a892c9547a326632ef6a9ba3b225 Update file renamed.txt - // 70f636e8c64bbc2dfef3735a562bb7e195d8019f Rename file under-test.txt into renamed.txt - // d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d Updated file under test - // 9da10ef7e139c49604a12caa866aae141f38b861 Updated file under test - // 599a5d821fb2c0a25855b4233e26d475c2fbeb34 Updated file under test - // 678b086b44753000567aa64344aa0d8034fa0083 Updated file under test - // 8f7d9520f306771340a7c79faea019ad18e4fa1f Updated file under test - // bd5f8ee279924d33be8ccbde82e7f10b9d9ff237 Updated file under test - // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit - fileHistoryEntries = repo.Commits.QueryBy("under-test.txt").ToList(); - Assert.Equal(10, fileHistoryEntries.Count()); - Assert.Equal("0b5b18f2feb917dee98df1210315b2b2b23c5bec", fileHistoryEntries[0].Commit.Sha); - Assert.Equal("49921d463420a892c9547a326632ef6a9ba3b225", fileHistoryEntries[1].Commit.Sha); - Assert.Equal("70f636e8c64bbc2dfef3735a562bb7e195d8019f", fileHistoryEntries[2].Commit.Sha); - Assert.Equal("d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d", fileHistoryEntries[3].Commit.Sha); - Assert.Equal("9da10ef7e139c49604a12caa866aae141f38b861", fileHistoryEntries[4].Commit.Sha); - Assert.Equal("599a5d821fb2c0a25855b4233e26d475c2fbeb34", fileHistoryEntries[5].Commit.Sha); - Assert.Equal("678b086b44753000567aa64344aa0d8034fa0083", fileHistoryEntries[6].Commit.Sha); - Assert.Equal("8f7d9520f306771340a7c79faea019ad18e4fa1f", fileHistoryEntries[7].Commit.Sha); - Assert.Equal("bd5f8ee279924d33be8ccbde82e7f10b9d9ff237", fileHistoryEntries[8].Commit.Sha); - Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[9].Commit.Sha); - } - } + //Looks like nulltoken deleted the repo this test was using + + //[Theory] + //[InlineData("https://github.com/nulltoken/follow-test.git")] + //public void CanDealWithFollowTest(string url) + //{ + // var scd = BuildSelfCleaningDirectory(); + // var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath); + + // using (var repo = new Repository(clonedRepoPath)) + // { + // // $ git log --follow --format=oneline so-renamed.txt + // // 88f91835062161febb46fb270ef4188f54c09767 Update not-yet-renamed.txt AND rename into so-renamed.txt + // // ef7cb6a63e32595fffb092cb1ae9a32310e58850 Add not-yet-renamed.txt + // var fileHistoryEntries = repo.Commits.QueryBy("so-renamed.txt").ToList(); + // Assert.Equal(2, fileHistoryEntries.Count()); + // Assert.Equal("88f91835062161febb46fb270ef4188f54c09767", fileHistoryEntries[0].Commit.Sha); + // Assert.Equal("ef7cb6a63e32595fffb092cb1ae9a32310e58850", fileHistoryEntries[1].Commit.Sha); + + // // $ git log --follow --format=oneline untouched.txt + // // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit + // fileHistoryEntries = repo.Commits.QueryBy("untouched.txt").ToList(); + // Assert.Single(fileHistoryEntries); + // Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[0].Commit.Sha); + + // // $ git log --follow --format=oneline under-test.txt + // // 0b5b18f2feb917dee98df1210315b2b2b23c5bec Rename file renamed.txt into under-test.txt + // // 49921d463420a892c9547a326632ef6a9ba3b225 Update file renamed.txt + // // 70f636e8c64bbc2dfef3735a562bb7e195d8019f Rename file under-test.txt into renamed.txt + // // d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d Updated file under test + // // 9da10ef7e139c49604a12caa866aae141f38b861 Updated file under test + // // 599a5d821fb2c0a25855b4233e26d475c2fbeb34 Updated file under test + // // 678b086b44753000567aa64344aa0d8034fa0083 Updated file under test + // // 8f7d9520f306771340a7c79faea019ad18e4fa1f Updated file under test + // // bd5f8ee279924d33be8ccbde82e7f10b9d9ff237 Updated file under test + // // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit + // fileHistoryEntries = repo.Commits.QueryBy("under-test.txt").ToList(); + // Assert.Equal(10, fileHistoryEntries.Count()); + // Assert.Equal("0b5b18f2feb917dee98df1210315b2b2b23c5bec", fileHistoryEntries[0].Commit.Sha); + // Assert.Equal("49921d463420a892c9547a326632ef6a9ba3b225", fileHistoryEntries[1].Commit.Sha); + // Assert.Equal("70f636e8c64bbc2dfef3735a562bb7e195d8019f", fileHistoryEntries[2].Commit.Sha); + // Assert.Equal("d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d", fileHistoryEntries[3].Commit.Sha); + // Assert.Equal("9da10ef7e139c49604a12caa866aae141f38b861", fileHistoryEntries[4].Commit.Sha); + // Assert.Equal("599a5d821fb2c0a25855b4233e26d475c2fbeb34", fileHistoryEntries[5].Commit.Sha); + // Assert.Equal("678b086b44753000567aa64344aa0d8034fa0083", fileHistoryEntries[6].Commit.Sha); + // Assert.Equal("8f7d9520f306771340a7c79faea019ad18e4fa1f", fileHistoryEntries[7].Commit.Sha); + // Assert.Equal("bd5f8ee279924d33be8ccbde82e7f10b9d9ff237", fileHistoryEntries[8].Commit.Sha); + // Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[9].Commit.Sha); + // } + //} [Theory] [InlineData(null)] @@ -151,7 +153,7 @@ public void CanTellComplexCommitHistory() var commit2 = MakeAndCommitChange(repo, repoPath, path1, "Hello World again"); // Move the first file to a new directory. - var newPath1 = Path.Combine(SubFolderPath1, path1); + var newPath1 = Path.Combine(SubFolderPath1, path1).Replace(@"\", "/"); Commands.Move(repo, path1, newPath1); var commit3 = repo.Commit("Moved " + path1 + " to " + newPath1, Constants.Signature, Constants.Signature); @@ -161,7 +163,7 @@ public void CanTellComplexCommitHistory() var commit4 = MakeAndCommitChange(repo, repoPath, newPath1, "I have done it again!"); // Perform tests. - var commitFilter = new CommitFilter () { SortBy = CommitSortStrategies.Topological }; + var commitFilter = new CommitFilter() { SortBy = CommitSortStrategies.Topological }; var fileHistoryEntries = repo.Commits.QueryBy(newPath1, commitFilter).ToList(); var changedBlobs = fileHistoryEntries.Blobs().Distinct().ToList(); @@ -223,8 +225,8 @@ public void CanTellSingleCommitHistory() IEnumerable history = repo.Commits.QueryBy(path).ToList(); var changedBlobs = history.Blobs().Distinct(); - Assert.Equal(1, history.Count()); - Assert.Equal(1, changedBlobs.Count()); + Assert.Single(history); + Assert.Single(changedBlobs); Assert.Equal(path, history.First().Path); Assert.Equal(commit, history.First().Commit); @@ -239,8 +241,8 @@ public void EmptyRepositoryHasNoHistory() using (var repo = new Repository(repoPath)) { IEnumerable history = repo.Commits.QueryBy("Test.txt").ToList(); - Assert.Equal(0, history.Count()); - Assert.Equal(0, history.Blobs().Count()); + Assert.Empty(history); + Assert.Empty(history.Blobs()); } } diff --git a/LibGit2Sharp.Tests/FilterBranchFixture.cs b/LibGit2Sharp.Tests/FilterBranchFixture.cs index d71cb22d8..de4663a22 100644 --- a/LibGit2Sharp.Tests/FilterBranchFixture.cs +++ b/LibGit2Sharp.Tests/FilterBranchFixture.cs @@ -3,7 +3,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -162,10 +161,8 @@ public void CanRewriteAuthorOfCommits() AssertSucceedingButNotError(); - var nonBackedUpRefs = repo.Refs.Where( - x => !x.CanonicalName.StartsWith("refs/original/") && !x.CanonicalName.StartsWith("refs/notes/")); - Assert.Empty(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = nonBackedUpRefs }) - .Where(c => c.Author.Name != "Ben Straub")); + var nonBackedUpRefs = repo.Refs.Where(x => !x.CanonicalName.StartsWith("refs/original/") && !x.CanonicalName.StartsWith("refs/notes/")); + Assert.DoesNotContain(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = nonBackedUpRefs }), c => c.Author.Name != "Ben Straub"); } [Fact] @@ -234,9 +231,9 @@ public void CanRewriteTreesByInjectingTreeEntry() AssertSucceedingButNotError(); - Assert.Equal(new Commit[0], + Assert.Equal(Array.Empty(), repo.Commits - .QueryBy(new CommitFilter {IncludeReachableFrom = repo.Branches}) + .QueryBy(new CommitFilter { IncludeReachableFrom = repo.Branches }) .Where(c => c["README"] != null && c["README"].Target.Id != currentReadme.Target.Id) .ToArray()); @@ -366,7 +363,7 @@ public void OnlyRewriteSelectedCommits() var commit = repo.Branches["packed"].Tip; var parent = commit.Parents.Single(); - Assert.True(parent.Sha.StartsWith("5001298")); + Assert.StartsWith("5001298", parent.Sha); Assert.NotEqual(Constants.Signature, commit.Author); Assert.NotEqual(Constants.Signature, parent.Author); @@ -403,9 +400,9 @@ public void CanCustomizeTheNamespaceOfBackedUpRefs(string backupRefsNamespace) AssertSucceedingButNotError(); - Assert.NotEmpty(repo.Refs.Where(x => x.CanonicalName.StartsWith("refs/original"))); + Assert.Contains(repo.Refs, x => x.CanonicalName.StartsWith("refs/original")); - Assert.Empty(repo.Refs.Where(x => x.CanonicalName.StartsWith("refs/rewritten"))); + Assert.DoesNotContain(repo.Refs, x => x.CanonicalName.StartsWith("refs/rewritten")); repo.Refs.RewriteHistory(new RewriteHistoryOptions { @@ -418,7 +415,7 @@ public void CanCustomizeTheNamespaceOfBackedUpRefs(string backupRefsNamespace) AssertSucceedingButNotError(); - Assert.NotEmpty(repo.Refs.Where(x => x.CanonicalName.StartsWith("refs/rewritten"))); + Assert.Contains(repo.Refs, x => x.CanonicalName.StartsWith("refs/rewritten")); } [Fact] @@ -494,7 +491,7 @@ public void DoesNotRewriteRefsThatDontChange() // Ensure br2 is still a merge commit var parents = repo.Branches["br2"].Tip.Parents.ToList(); Assert.Equal(2, parents.Count()); - Assert.NotEmpty(parents.Where(c => c.Sha.StartsWith("9fd738e"))); + Assert.Contains(parents, c => c.Sha.StartsWith("9fd738e")); Assert.Equal("abc", parents.Single(c => !c.Sha.StartsWith("9fd738e")).Message); } @@ -535,7 +532,7 @@ public void CanNotOverWriteBackedUpReferences() var newOriginalRefs = repo.Refs.FromGlob("refs/original/*").OrderBy(r => r.CanonicalName).ToArray(); Assert.Equal(originalRefs, newOriginalRefs); - Assert.Empty(repo.Refs.Where(x => x.CanonicalName.StartsWith("refs/original/original/"))); + Assert.DoesNotContain(repo.Refs, x => x.CanonicalName.StartsWith("refs/original/original/")); } [Fact] @@ -557,7 +554,7 @@ public void CanNotOverWriteAnExistingReference() AssertErrorFired(ex); AssertSucceedingNotFired(); - Assert.Equal(0, repo.Refs.FromGlob("refs/original/*").Count()); + Assert.Empty(repo.Refs.FromGlob("refs/original/*")); } // Graft the orphan "test" branch to the tip of "packed" @@ -633,7 +630,7 @@ public void CanRewriteParentWithRewrittenCommit() var commitToRewrite = repo.Lookup("6dcf9bf"); var newParent = repo.Branches["packed"].Tip; - Assert.True(newParent.Sha.StartsWith("41bc8c6")); + Assert.StartsWith("41bc8c6", newParent.Sha); repo.Refs.RewriteHistory(new RewriteHistoryOptions { @@ -649,22 +646,22 @@ public void CanRewriteParentWithRewrittenCommit() AssertSucceedingButNotError(); // Assert "packed" hasn't been rewritten - Assert.True(repo.Branches["packed"].Tip.Sha.StartsWith("41bc8c6")); + Assert.StartsWith("41bc8c6", repo.Branches["packed"].Tip.Sha); // Assert (test, tag: lw, tag: e90810b, tag: test) have been rewritten var rewrittenTestCommit = repo.Branches["test"].Tip; - Assert.True(rewrittenTestCommit.Sha.StartsWith("f558880")); + Assert.StartsWith("f558880", rewrittenTestCommit.Sha); Assert.Equal(rewrittenTestCommit, repo.Lookup("refs/tags/lw^{commit}")); Assert.Equal(rewrittenTestCommit, repo.Lookup("refs/tags/e90810b^{commit}")); Assert.Equal(rewrittenTestCommit, repo.Lookup("refs/tags/test^{commit}")); // Assert parent of rewritten commit var rewrittenTestCommitParent = rewrittenTestCommit.Parents.Single(); - Assert.True(rewrittenTestCommitParent.Sha.StartsWith("0c25efa")); + Assert.StartsWith("0c25efa", rewrittenTestCommitParent.Sha); // Assert grand parent of rewritten commit var rewrittenTestCommitGrandParent = rewrittenTestCommitParent.Parents.Single(); - Assert.True(rewrittenTestCommitGrandParent.Sha.StartsWith("41bc8c6")); + Assert.StartsWith("41bc8c6", rewrittenTestCommitGrandParent.Sha); } [Fact] diff --git a/LibGit2Sharp.Tests/FilterFixture.cs b/LibGit2Sharp.Tests/FilterFixture.cs index 0e2a32428..8fd9c8392 100644 --- a/LibGit2Sharp.Tests/FilterFixture.cs +++ b/LibGit2Sharp.Tests/FilterFixture.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; using System.IO; +using System.Threading.Tasks; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using System.Threading.Tasks; namespace LibGit2Sharp.Tests { @@ -174,7 +173,7 @@ public void CleanFilterWritesOutputToObjectTree() } [Fact] - public void CanHandleMultipleSmudgesConcurrently() + public async Task CanHandleMultipleSmudgesConcurrently() { const string decodedInput = "This is a substitution cipher"; const string encodedInput = "Guvf vf n fhofgvghgvba pvcure"; @@ -193,20 +192,18 @@ public void CanHandleMultipleSmudgesConcurrently() for (int i = 0; i < count; i++) { - tasks[i] = Task.Factory.StartNew(() => + tasks[i] = Task.Run(() => { string repoPath = InitNewRepository(); return CheckoutFileForSmudge(repoPath, branchName, encodedInput); }); } - Task.WaitAll(tasks); + var files = await Task.WhenAll(tasks); - foreach(var task in tasks) + foreach (var file in files) { - FileInfo expectedFile = task.Result; - - string readAllText = File.ReadAllText(expectedFile.FullName); + string readAllText = File.ReadAllText(file.FullName); Assert.Equal(decodedInput, readAllText); } } @@ -314,23 +311,23 @@ public void CanFilterLargeFiles() [Fact] public void DoubleRegistrationFailsButDoubleDeregistrationDoesNot() { - Assert.Equal(0, GlobalSettings.GetRegisteredFilters().Count()); + Assert.Empty(GlobalSettings.GetRegisteredFilters()); var filter = new EmptyFilter(FilterName, attributes); var registration = GlobalSettings.RegisterFilter(filter); Assert.Throws(() => { GlobalSettings.RegisterFilter(filter); }); - Assert.Equal(1, GlobalSettings.GetRegisteredFilters().Count()); + Assert.Single(GlobalSettings.GetRegisteredFilters()); Assert.True(registration.IsValid, "FilterRegistration.IsValid should be true."); GlobalSettings.DeregisterFilter(registration); - Assert.Equal(0, GlobalSettings.GetRegisteredFilters().Count()); + Assert.Empty(GlobalSettings.GetRegisteredFilters()); Assert.False(registration.IsValid, "FilterRegistration.IsValid should be false."); GlobalSettings.DeregisterFilter(registration); - Assert.Equal(0, GlobalSettings.GetRegisteredFilters().Count()); + Assert.Empty(GlobalSettings.GetRegisteredFilters()); Assert.False(registration.IsValid, "FilterRegistration.IsValid should be false."); } @@ -399,7 +396,7 @@ private FileInfo CheckoutFileForSmudge(string repoPath, string branchName, strin return expectedPath; } - private static FileInfo CommitFileOnBranch(Repository repo, string branchName, String content) + private static FileInfo CommitFileOnBranch(Repository repo, string branchName, string content) { var branch = repo.CreateBranch(branchName); Commands.Checkout(repo, branch.FriendlyName); diff --git a/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs b/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs index a8705fe81..89b61c546 100644 --- a/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs +++ b/LibGit2Sharp.Tests/FilterSubstitutionCipherFixture.cs @@ -122,8 +122,8 @@ public void WhenStagedFileDoesNotMatchPathSpecFileIsNotFiltered(string pathSpec, [InlineData("rot13", "*.txt filter=rot13", 1)] [InlineData("rot13", "*.txt filter=fake", 0)] [InlineData("rot13", "*.bat filter=rot13", 0)] - [InlineData("rot13", "*.txt filter=fake", 0)] [InlineData("fake", "*.txt filter=fake", 1)] + [InlineData("fake", "*.txt filter=rot13", 0)] [InlineData("fake", "*.bat filter=fake", 0)] [InlineData("rot13", "*.txt filter=rot13 -crlf", 1)] public void CleanIsCalledIfAttributeEntryMatches(string filterAttribute, string attributeEntry, int cleanCount) diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 8055a98d1..925efc3d0 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -1,4 +1,8 @@ -using System.Text.RegularExpressions; +using System; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using LibGit2Sharp.Core; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -19,50 +23,99 @@ public void CanGetMinimumCompiledInFeatures() public void CanRetrieveValidVersionString() { // Version string format is: - // Major.Minor.Patch[-preDateTime]-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|x64 - features) + // Major.Minor.Patch[-previewTag]+libgit2-{libgit2_abbrev_hash}.{LibGit2Sharp_hash} (arch - features) // Example output: - // "0.17.0[-pre20170914123547]-deadcafe-06d772d (x86 - Threads, Https)" + // "0.27.0-preview.0.1896+libgit2-c058aa8.c1ac3ed74487da5fac24cf1e48dc8ea71e917b75 (x64 - Threads, Https, NSec)" string versionInfo = GlobalSettings.Version.ToString(); // The GlobalSettings.Version returned string should contain : - // version: '0.17.0[-pre20170914123547]' LibGit2Sharp version number. - // git2SharpHash:'unknown' ( when compiled from source ) else LibGit2Sharp library hash. - // git2hash: '06d772d' LibGit2 library hash. - // arch: 'x86' or 'x64' LibGit2 target. - // git2Features: 'Threads, Ssh' LibGit2 features compiled with. - string regex = @"^(?\d{1,}\.\d{1,2}\.\d{1,3}(-(pre|dev)\d{14})?)-(?\w+)-(?\w+) \((?\w+) - (?(?:\w*(?:, )*\w+)*)\)$"; + // version: '0.25.0[-previewTag]' LibGit2Sharp version number. + // git2SharpHash: 'c1ac3ed74487da5fac24cf1e48dc8ea71e917b75' LibGit2Sharp hash. + // arch: 'x86' or 'x64' libgit2 target. + // git2Features: 'Threads, Ssh' libgit2 features compiled with. + string regex = @"^(?\d+\.\d+\.\d+(-[\w\-\.]+)?)\+libgit2-[a-f0-9]{7}\.((?[a-f0-9]{40}))? \((?\w+) - (?(?:\w*(?:, )*\w+)*)\)$"; Assert.NotNull(versionInfo); Match regexResult = Regex.Match(versionInfo, regex); Assert.True(regexResult.Success, "The following version string format is enforced:" + - "Major.Minor.Patch[-preDateTime]-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|x64 - features)"); + "Major.Minor.Patch[-previewTag]+libgit2-{libgit2_abbrev_hash}.{LibGit2Sharp_hash} (arch - features). " + + "But found \"" + versionInfo + "\" instead."); + } + + [Fact] + public void TryingToResetNativeLibraryPathAfterLoadedThrows() + { + // Do something that loads the native library + var features = GlobalSettings.Version.Features; - GroupCollection matchGroups = regexResult.Groups; + Assert.Throws(() => { GlobalSettings.NativeLibraryPath = "C:/Foo"; }); + } + + [SkippableTheory] + [InlineData("x86")] + [InlineData("x64")] + public void LoadFromSpecifiedPath(string architecture) + { + Skip.IfNot(Platform.IsRunningOnNetFramework(), ".NET Framework only test."); - Assert.Equal(8, matchGroups.Count); + var nativeDllFileName = NativeDllName.Name + ".dll"; + var testDir = Path.GetDirectoryName(typeof(GlobalSettingsFixture).Assembly.Location); + var testAppExe = Path.Combine(testDir, $"NativeLibraryLoadTestApp.{architecture}.exe"); + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var platformDir = Path.Combine(tempDir, "plat", architecture); + var libraryPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "lib", "win32", architecture); - // Check that all groups are valid - for (int i = 0; i < matchGroups.Count; i++) + try { - if (i == 1 || i == 2) // '-pre' segment is optional - { - continue; - } + Directory.CreateDirectory(platformDir); + File.Copy(Path.Combine(libraryPath, nativeDllFileName), Path.Combine(platformDir, nativeDllFileName)); + + var (output, exitCode) = ProcessHelper.RunProcess(testAppExe, arguments: $@"{NativeDllName.Name} ""{platformDir}""", workingDirectory: tempDir); - Assert.True(matchGroups[i].Success); + Assert.Empty(output); + Assert.Equal(0, exitCode); + } + finally + { + DirectoryHelper.DeleteDirectory(tempDir); } } [Fact] - public void TryingToResetNativeLibraryPathAfterLoadedThrows() + public void SetExtensions() { - // Do something that loads the native library - Assert.NotNull(GlobalSettings.Version.Features); + var extensions = GlobalSettings.GetExtensions(); - Assert.Throws(() => { GlobalSettings.NativeLibraryPath = "C:/Foo"; }); + // Assert that "noop" is supported by default + Assert.Equal(new[] { "noop", "objectformat", "worktreeconfig" }, extensions); + + // Disable "noop" extensions + GlobalSettings.SetExtensions("!noop"); + extensions = GlobalSettings.GetExtensions(); + Assert.Equal(new[] { "objectformat", "worktreeconfig" }, extensions); + + // Enable two new extensions (it will reset the configuration and "noop" will be enabled) + GlobalSettings.SetExtensions("partialclone", "newext"); + extensions = GlobalSettings.GetExtensions(); + Assert.Equal(new[] { "newext", "noop", "objectformat", "partialclone", "worktreeconfig" }, extensions); + } + + [Fact] + public void OwnerValidation() + { + // Assert that owner validation is enabled by default + Assert.True(GlobalSettings.GetOwnerValidation()); + + // Disable owner validation + GlobalSettings.SetOwnerValidation(false); + Assert.False(GlobalSettings.GetOwnerValidation()); + + // Enable it again + GlobalSettings.SetOwnerValidation(true); + Assert.True(GlobalSettings.GetOwnerValidation()); } } } diff --git a/LibGit2Sharp.Tests/IgnoreFixture.cs b/LibGit2Sharp.Tests/IgnoreFixture.cs index 7957bf3e0..fc9d65bc2 100644 --- a/LibGit2Sharp.Tests/IgnoreFixture.cs +++ b/LibGit2Sharp.Tests/IgnoreFixture.cs @@ -16,15 +16,15 @@ public void TemporaryRulesShouldApplyUntilCleared() { Touch(repo.Info.WorkingDirectory, "Foo.cs", "Bar"); - Assert.True(repo.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); + Assert.Contains("Foo.cs", repo.RetrieveStatus().Untracked.Select(s => s.FilePath)); repo.Ignore.AddTemporaryRules(new[] { "*.cs" }); - Assert.False(repo.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); + Assert.DoesNotContain("Foo.cs", repo.RetrieveStatus().Untracked.Select(s => s.FilePath)); repo.Ignore.ResetAllTemporaryRules(); - Assert.True(repo.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); + Assert.Contains("Foo.cs", repo.RetrieveStatus().Untracked.Select(s => s.FilePath)); } } @@ -79,8 +79,8 @@ public void CanCheckIfAPathIsIgnoredUsingThePreferedPlatformDirectorySeparatorCh Assert.False(repo.Ignore.IsPathIgnored("File.txt")); Assert.True(repo.Ignore.IsPathIgnored("NewFolder")); - Assert.True(repo.Ignore.IsPathIgnored(string.Format(@"NewFolder{0}NewFolder", Path.DirectorySeparatorChar))); - Assert.True(repo.Ignore.IsPathIgnored(string.Format(@"NewFolder{0}NewFolder{0}File.txt", Path.DirectorySeparatorChar))); + Assert.True(repo.Ignore.IsPathIgnored(string.Join("/", "NewFolder", "NewFolder"))); + Assert.True(repo.Ignore.IsPathIgnored(string.Join("/", "NewFolder", "NewFolder", "File.txt"))); } } @@ -90,9 +90,7 @@ public void HonorDeeplyNestedGitIgnoreFile() string path = InitNewRepository(); using (var repo = new Repository(path)) { - char pd = Path.DirectorySeparatorChar; - - var gitIgnoreFile = string.Format("deeply{0}nested{0}.gitignore", pd); + var gitIgnoreFile = string.Join("/", "deeply", "nested", ".gitignore"); Touch(repo.Info.WorkingDirectory, gitIgnoreFile, "SmtCounters.h"); Commands.Stage(repo, gitIgnoreFile); @@ -100,14 +98,14 @@ public void HonorDeeplyNestedGitIgnoreFile() Assert.False(repo.RetrieveStatus().IsDirty); - var ignoredFile = string.Format("deeply{0}nested{0}SmtCounters.h", pd); + var ignoredFile = string.Join("/", "deeply", "nested", "SmtCounters.h"); Touch(repo.Info.WorkingDirectory, ignoredFile, "Content"); Assert.False(repo.RetrieveStatus().IsDirty); - var file = string.Format("deeply{0}nested{0}file.txt", pd); + var file = string.Join("/", "deeply", "nested", "file.txt"); Touch(repo.Info.WorkingDirectory, file, "Yeah!"); - var repositoryStatus = repo.RetrieveStatus(); + var repositoryStatus = repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }); Assert.True(repositoryStatus.IsDirty); Assert.Equal(FileStatus.Ignored, repositoryStatus[ignoredFile].State); diff --git a/LibGit2Sharp.Tests/IndexFixture.cs b/LibGit2Sharp.Tests/IndexFixture.cs index 63dc91103..4c0d150f0 100644 --- a/LibGit2Sharp.Tests/IndexFixture.cs +++ b/LibGit2Sharp.Tests/IndexFixture.cs @@ -10,7 +10,7 @@ namespace LibGit2Sharp.Tests { public class IndexFixture : BaseFixture { - private static readonly string subBranchFile = Path.Combine("1", "branch_file.txt"); + private static readonly string subBranchFile = string.Join("/", "1", "branch_file.txt"); private readonly string[] expectedEntries = new[] { "1.txt", @@ -208,7 +208,7 @@ private void InvalidMoveUseCases(string sourcePath, FileStatus sourceStatus, IEn public void PathsOfIndexEntriesAreExpressedInNativeFormat() { // Build relative path - string relFilePath = Path.Combine("directory", "Testfile.txt"); + string relFilePath = Path.Combine("directory", "Testfile.txt").Replace('\\', '/'); string repoPath = InitNewRepository(); @@ -462,18 +462,18 @@ public void CanMimicGitAddAll() using (var repo = new Repository(path)) { var before = repo.RetrieveStatus(); - Assert.True(before.Any(se => se.State == FileStatus.NewInWorkdir)); - Assert.True(before.Any(se => se.State == FileStatus.ModifiedInWorkdir)); - Assert.True(before.Any(se => se.State == FileStatus.DeletedFromWorkdir)); + Assert.Contains(before, se => se.State == FileStatus.NewInWorkdir); + Assert.Contains(before, se => se.State == FileStatus.ModifiedInWorkdir); + Assert.Contains(before, se => se.State == FileStatus.DeletedFromWorkdir); AddSomeCornerCases(repo); Commands.Stage(repo, "*"); var after = repo.RetrieveStatus(); - Assert.False(after.Any(se => se.State == FileStatus.NewInWorkdir)); - Assert.False(after.Any(se => se.State == FileStatus.ModifiedInWorkdir)); - Assert.False(after.Any(se => se.State == FileStatus.DeletedFromWorkdir)); + Assert.DoesNotContain(after, se => se.State == FileStatus.NewInWorkdir); + Assert.DoesNotContain(after, se => se.State == FileStatus.ModifiedInWorkdir); + Assert.DoesNotContain(after, se => se.State == FileStatus.DeletedFromWorkdir); } } diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index eee61b802..fb81a76a3 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -1,188 +1,43 @@ - - - + + - Debug - AnyCPU - 8.0.30703 - 2.0 - {286E63EB-04DD-4ADE-88D6-041B57800761} - Library - Properties - LibGit2Sharp.Tests - LibGit2Sharp.Tests - v4.6 - 512 - - - + net472;net8.0;net9.0 - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET40 - prompt - 4 - true - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - false - - - - ..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll - - - ..\packages\Moq.4.7.8\lib\net45\Moq.dll - - - - - - ..\packages\Validation.2.4.15\lib\net45\Validation.dll - - - ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll - True - - - ..\packages\xunit.assert.2.2.0\lib\netstandard1.1\xunit.assert.dll - - - ..\packages\xunit.extensibility.core.2.2.0\lib\netstandard1.1\xunit.core.dll - - - ..\packages\xunit.extensibility.execution.2.2.0\lib\net452\xunit.execution.desktop.dll - - - ..\packages\Xunit.SkippableFact.1.3.1\lib\net452\Xunit.SkippableFact.dll - - + - - TestHelpers\Epoch.cs - - - TestHelpers\Platform.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - {EE6ED99F-CB12-4683-B055-D28FC7357A34} - LibGit2Sharp - + + + + + + + + - - + + + + - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + \ No newline at end of file diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.v2.ncrunchproject b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.v2.ncrunchproject deleted file mode 100644 index e24470157..000000000 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.v2.ncrunchproject +++ /dev/null @@ -1,31 +0,0 @@ - - 1000 - false - true - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - - - LibGit2Sharp.Tests.ShadowCopyFixture.CanProbeForNativeBinariesFromAShadowCopiedAssembly - - - Resources\**;Resources\**.* - \ No newline at end of file diff --git a/LibGit2Sharp.Tests/LogFixture.cs b/LibGit2Sharp.Tests/LogFixture.cs index b74fe97b0..b8d43fe36 100644 --- a/LibGit2Sharp.Tests/LogFixture.cs +++ b/LibGit2Sharp.Tests/LogFixture.cs @@ -25,13 +25,13 @@ public void CanEnableAndDisableLogging() GlobalSettings.LogConfiguration = new LogConfiguration(LogLevel.Warning, (l, m) => { level = l; message = m; }); Assert.Equal(LogLevel.None, level); - Assert.Equal(null, message); + Assert.Null(message); // Similarly, turning logging off should produce no messages. GlobalSettings.LogConfiguration = LogConfiguration.None; Assert.Equal(LogLevel.None, level); - Assert.Equal(null, message); + Assert.Null(message); } } } diff --git a/LibGit2Sharp.Tests/MergeFixture.cs b/LibGit2Sharp.Tests/MergeFixture.cs index 70adeab7f..7b1fda718 100644 --- a/LibGit2Sharp.Tests/MergeFixture.cs +++ b/LibGit2Sharp.Tests/MergeFixture.cs @@ -16,7 +16,7 @@ public void ANewRepoIsFullyMerged() using (var repo = new Repository(repoPath)) { - Assert.Equal(true, repo.Index.IsFullyMerged); + Assert.True(repo.Index.IsFullyMerged); } } @@ -26,7 +26,7 @@ public void AFullyMergedRepoOnlyContainsStagedIndexEntries() string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(true, repo.Index.IsFullyMerged); + Assert.True(repo.Index.IsFullyMerged); foreach (var entry in repo.Index) { @@ -41,7 +41,7 @@ public void SoftResetARepoWithUnmergedEntriesThrows() var path = SandboxMergedTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(false, repo.Index.IsFullyMerged); + Assert.False(repo.Index.IsFullyMerged); var headCommit = repo.Head.Tip; var firstCommitParent = headCommit.Parents.First(); @@ -56,7 +56,7 @@ public void CommitAgainARepoWithUnmergedEntriesThrows() var path = SandboxMergedTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(false, repo.Index.IsFullyMerged); + Assert.False(repo.Index.IsFullyMerged); var author = Constants.Signature; Assert.Throws( @@ -204,7 +204,7 @@ public void CanFastForwardRepos(bool shouldMergeOccurInDetachedHeadState) Assert.Equal(repo.Branches["FirstBranch"].Tip, repo.Head.Tip); Assert.Equal(repo.Head.Tip, mergeResult.Commit); - Assert.Equal(0, repo.RetrieveStatus().Count()); + Assert.Empty(repo.RetrieveStatus()); Assert.Equal(shouldMergeOccurInDetachedHeadState, repo.Info.IsHeadDetached); if (!shouldMergeOccurInDetachedHeadState) @@ -246,7 +246,7 @@ public void ConflictingMergeRepos() Assert.Equal(MergeStatus.Conflicts, mergeResult.Status); Assert.Null(mergeResult.Commit); - Assert.Equal(1, repo.Index.Conflicts.Count()); + Assert.Single(repo.Index.Conflicts); var conflict = repo.Index.Conflicts.First(); var changes = repo.Diff.Compare(repo.Lookup(conflict.Theirs.Id), repo.Lookup(conflict.Ours.Id)); @@ -285,7 +285,7 @@ public void ConflictingMergeReposBinary() Assert.Equal(MergeStatus.Conflicts, mergeResult.Status); - Assert.Equal(1, repo.Index.Conflicts.Count()); + Assert.Single(repo.Index.Conflicts); Conflict conflict = repo.Index.Conflicts.First(); @@ -323,7 +323,7 @@ public void CanFastForwardCommit(bool fromDetachedHead, FastForwardStrategy fast string path = SandboxMergeTestRepo(); using (var repo = new Repository(path)) { - if(fromDetachedHead) + if (fromDetachedHead) { Commands.Checkout(repo, repo.Head.Tip.Id.Sha); } @@ -512,15 +512,15 @@ public void CanMergeAndNotCommit() { Commit commitToMerge = repo.Branches["normal_merge"].Tip; - MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { CommitOnSuccess = false}); + MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { CommitOnSuccess = false }); Assert.Equal(MergeStatus.NonFastForward, result.Status); - Assert.Equal(null, result.Commit); + Assert.Null(result.Commit); RepositoryStatus repoStatus = repo.RetrieveStatus(); // Verify that there is a staged entry. - Assert.Equal(1, repoStatus.Count()); + Assert.Single(repoStatus); Assert.Equal(FileStatus.ModifiedInIndex, repo.RetrieveStatus("b.txt")); } } @@ -570,7 +570,7 @@ public void VerifyUpToDateMerge() MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = FastForwardStrategy.NoFastForward }); Assert.Equal(MergeStatus.UpToDate, result.Status); - Assert.Equal(null, result.Commit); + Assert.Null(result.Commit); Assert.False(repo.RetrieveStatus().Any()); } } @@ -649,7 +649,7 @@ public void CanSpecifyConflictFileStrategy(CheckoutFileConflictStrategy conflict // Get the blob containing the expected content. Blob expectedBlob = null; - switch(conflictStrategy) + switch (conflictStrategy) { case CheckoutFileConflictStrategy.Theirs: expectedBlob = repo.Lookup(conflict.Theirs.Id); @@ -731,7 +731,7 @@ public void CanMergeBranch(string branchName, FastForwardStrategy strategy, Merg string path = SandboxMergeTestRepo(); using (var repo = new Repository(path)) { - Branch branch = repo. Branches[branchName]; + Branch branch = repo.Branches[branchName]; MergeResult result = repo.Merge(branch, Constants.Signature, new MergeOptions() { FastForwardStrategy = strategy }); Assert.Equal(expectedMergeStatus, result.Status); @@ -748,7 +748,7 @@ public void CanMergeIntoOrphanedBranch() repo.Refs.Add("HEAD", "refs/heads/orphan", true); // Remove entries from the working directory - foreach(var entry in repo.RetrieveStatus()) + foreach (var entry in repo.RetrieveStatus()) { Commands.Unstage(repo, entry.FilePath); Commands.Remove(repo, entry.FilePath, true); @@ -776,7 +776,7 @@ public void CanMergeTreeIntoSameTree() var result = repo.ObjectDatabase.MergeCommits(master, master, null); Assert.Equal(MergeTreeStatus.Succeeded, result.Status); - Assert.Equal(0, result.Conflicts.Count()); + Assert.Empty(result.Conflicts); } } @@ -800,7 +800,7 @@ public void CanMergeTreeIntoTreeFromUnbornBranch() var result = repo.ObjectDatabase.MergeCommits(master, branch, null); Assert.Equal(MergeTreeStatus.Succeeded, result.Status); Assert.NotNull(result.Tree); - Assert.Equal(0, result.Conflicts.Count()); + Assert.Empty(result.Conflicts); } } @@ -822,7 +822,7 @@ public void CanMergeCommitsAndDetectConflicts() var result = repo.ObjectDatabase.MergeCommits(master, branch, null); Assert.Equal(MergeTreeStatus.Conflicts, result.Status); Assert.Null(result.Tree); - Assert.NotEqual(0, result.Conflicts.Count()); + Assert.NotEmpty(result.Conflicts); } } @@ -838,7 +838,7 @@ public void CanMergeFastForwardTreeWithoutConflicts() var result = repo.ObjectDatabase.MergeCommits(master, branch, null); Assert.Equal(MergeTreeStatus.Succeeded, result.Status); Assert.NotNull(result.Tree); - Assert.Equal(0, result.Conflicts.Count()); + Assert.Empty(result.Conflicts); } } @@ -856,7 +856,7 @@ public void CanIdentifyConflictsInMergeCommits() Assert.Equal(MergeTreeStatus.Conflicts, result.Status); Assert.Null(result.Tree); - Assert.Equal(1, result.Conflicts.Count()); + Assert.Single(result.Conflicts); var conflict = result.Conflicts.First(); Assert.Equal(new ObjectId("8e9daea300fbfef6c0da9744c6214f546d55b279"), conflict.Ancestor.Id); @@ -865,6 +865,89 @@ public void CanIdentifyConflictsInMergeCommits() } } + [Theory] + [InlineData("conflicts_spaces")] + [InlineData("conflicts_tabs")] + public void CanConflictOnWhitespaceChangeMergeConflict(string branchName) + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var mergeResult = repo.Merge(branchName, Constants.Signature, new MergeOptions()); + Assert.Equal(MergeStatus.Conflicts, mergeResult.Status); + + var master = repo.Branches["master"]; + var branch = repo.Branches[branchName]; + var mergeTreeResult = repo.ObjectDatabase.MergeCommits(master.Tip, branch.Tip, new MergeTreeOptions()); + Assert.Equal(MergeTreeStatus.Conflicts, mergeTreeResult.Status); + } + } + + [Theory] + [InlineData("conflicts_spaces")] + [InlineData("conflicts_tabs")] + public void CanIgnoreWhitespaceChangeMergeConflict(string branchName) + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var mergeResult = repo.Merge(branchName, Constants.Signature, new MergeOptions() { IgnoreWhitespaceChange = true }); + Assert.NotEqual(MergeStatus.Conflicts, mergeResult.Status); + + var master = repo.Branches["master"]; + var branch = repo.Branches[branchName]; + var mergeTreeResult = repo.ObjectDatabase.MergeCommits(master.Tip, branch.Tip, new MergeTreeOptions() { IgnoreWhitespaceChange = true }); + Assert.NotEqual(MergeTreeStatus.Conflicts, mergeTreeResult.Status); + Assert.Empty(mergeTreeResult.Conflicts); + } + } + + [Fact] + public void CanMergeIntoIndex() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + + using (TransientIndex index = repo.ObjectDatabase.MergeCommitsIntoIndex(master, master, null)) + { + var tree = index.WriteToTree(); + Assert.Equal(master.Tree.Id, tree.Id); + } + } + } + + [Fact] + public void CanMergeIntoIndexWithConflicts() + { + string path = SandboxMergeTestRepo(); + using (var repo = new Repository(path)) + { + var master = repo.Lookup("master"); + var branch = repo.Lookup("conflicts"); + + using (TransientIndex index = repo.ObjectDatabase.MergeCommitsIntoIndex(branch, master, null)) + { + Assert.False(index.IsFullyMerged); + + var conflict = index.Conflicts.First(); + + //Resolve the conflict by taking the blob from branch + var blob = repo.Lookup(conflict.Ours.Id); + //Add() does not remove conflict entries for the same path, so they must be explicitly removed first. + index.Remove(conflict.Ours.Path); + index.Add(blob, conflict.Ours.Path, Mode.NonExecutableFile); + + Assert.True(index.IsFullyMerged); + var tree = index.WriteToTree(); + + //Since we took the conflicted blob from the branch, the merged result should be the same as the branch. + Assert.Equal(branch.Tree.Id, tree.Id); + } + } + } + private Commit AddFileCommitToRepo(IRepository repository, string filename, string content = null) { Touch(repository.Info.WorkingDirectory, filename, content); diff --git a/LibGit2Sharp.Tests/MetaFixture.cs b/LibGit2Sharp.Tests/MetaFixture.cs index a3ff18f78..1d0a1d101 100644 --- a/LibGit2Sharp.Tests/MetaFixture.cs +++ b/LibGit2Sharp.Tests/MetaFixture.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; -using Moq; namespace LibGit2Sharp.Tests { @@ -20,6 +17,42 @@ public class MetaFixture typeof(IBelongToARepository), typeof(IDiffResult), }; + [Fact] + public void LibGit2SharpPublicInterfacesCoverAllPublicMembers() + { + var methodsMissingFromInterfaces = + from t in typeof(IRepository).GetTypeInfo().Assembly.GetExportedTypes() + where !t.GetTypeInfo().IsInterface + where t.GetTypeInfo().GetInterfaces().Any(i => i.GetTypeInfo().IsPublic && i.Namespace == typeof(IRepository).Namespace && !explicitOnlyInterfaces.Contains(i)) + let interfaceTargetMethods = from i in t.GetTypeInfo().GetInterfaces() + from im in t.GetInterfaceMap(i).TargetMethods + select im + from tm in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) + where !interfaceTargetMethods.Contains(tm) + select t.Name + " has extra method " + tm.Name; + + Assert.Equal("", string.Join(Environment.NewLine, + methodsMissingFromInterfaces.ToArray())); + } + + [Fact] + public void LibGit2SharpExplicitOnlyInterfacesAreIndeedExplicitOnly() + { + var methodsMissingFromInterfaces = + from t in typeof(IRepository).GetTypeInfo().Assembly.GetExportedTypes() + where t.GetInterfaces().Any(explicitOnlyInterfaces.Contains) + let interfaceTargetMethods = from i in t.GetInterfaces() + where explicitOnlyInterfaces.Contains(i) + from im in t.GetTypeInfo().GetInterfaceMap(i).TargetMethods + select im + from tm in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) + where interfaceTargetMethods.Contains(tm) + select t.Name + " has public method " + tm.Name + " which should be explicitly implemented."; + + Assert.Equal("", string.Join(Environment.NewLine, + methodsMissingFromInterfaces.ToArray())); + } + [Fact] public void PublicTestMethodsAreFactsOrTheories() { @@ -28,8 +61,8 @@ public void PublicTestMethodsAreFactsOrTheories() "LibGit2Sharp.Tests.FilterBranchFixture.Dispose", }; - var fixtures = from t in Assembly.GetAssembly(typeof(MetaFixture)).GetExportedTypes() - where t.IsPublic && !t.IsNested + var fixtures = from t in typeof(MetaFixture).GetTypeInfo().Assembly.GetExportedTypes() + where t.GetTypeInfo().IsPublic && !t.IsNested where t.Namespace != typeof(BaseFixture).Namespace // Exclude helpers let methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public) from m in methods @@ -49,12 +82,12 @@ public void TypesInLibGit2DecoratedWithDebuggerDisplayMustFollowTheStandardImplP { var typesWithDebuggerDisplayAndInvalidImplPattern = new List(); - IEnumerable libGit2SharpTypes = Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() - .Where(t => t.GetCustomAttributes(typeof(DebuggerDisplayAttribute), false).Any()); + IEnumerable libGit2SharpTypes = typeof(IRepository).GetTypeInfo().Assembly.GetExportedTypes() + .Where(t => t.GetTypeInfo().GetCustomAttributes(typeof(DebuggerDisplayAttribute), false).Length != 0); foreach (Type type in libGit2SharpTypes) { - var debuggerDisplayAttribute = (DebuggerDisplayAttribute)type.GetCustomAttributes(typeof(DebuggerDisplayAttribute), false).Single(); + var debuggerDisplayAttribute = (DebuggerDisplayAttribute)type.GetTypeInfo().GetCustomAttributes(typeof(DebuggerDisplayAttribute), false).Single(); if (debuggerDisplayAttribute.Value != "{DebuggerDisplay,nq}") { @@ -77,9 +110,9 @@ public void TypesInLibGit2DecoratedWithDebuggerDisplayMustFollowTheStandardImplP } } - if (typesWithDebuggerDisplayAndInvalidImplPattern.Any()) + if (typesWithDebuggerDisplayAndInvalidImplPattern.Count != 0) { - Assert.True(false, Environment.NewLine + BuildMissingDebuggerDisplayPropertyMessage(typesWithDebuggerDisplayAndInvalidImplPattern)); + Assert.Fail(Environment.NewLine + BuildMissingDebuggerDisplayPropertyMessage(typesWithDebuggerDisplayAndInvalidImplPattern)); } } @@ -89,16 +122,16 @@ public void TypesInLibGit2SharpMustBeExtensibleInATestingContext() { var nonTestableTypes = new Dictionary>(); - IEnumerable libGit2SharpTypes = Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() + IEnumerable libGit2SharpTypes = typeof(IRepository).GetTypeInfo().Assembly.GetExportedTypes() .Where(t => MustBeMockable(t) && t.Namespace == typeof(IRepository).Namespace); foreach (Type type in libGit2SharpTypes) { - if (type.IsInterface || type.IsEnum || IsStatic(type)) + if (type.GetTypeInfo().IsInterface || type.GetTypeInfo().IsEnum || IsStatic(type)) continue; var nonVirtualMethodNamesForType = GetNonVirtualPublicMethodsNames(type).ToList(); - if (nonVirtualMethodNamesForType.Any()) + if (nonVirtualMethodNamesForType.Count != 0) { nonTestableTypes.Add(type, nonVirtualMethodNamesForType); continue; @@ -109,14 +142,14 @@ public void TypesInLibGit2SharpMustBeExtensibleInATestingContext() nonTestableTypes.Add(type, new List()); } - if (type.IsAbstract) + if (type.GetTypeInfo().IsAbstract) { continue; } try { - if (type.ContainsGenericParameters) + if (type.GetTypeInfo().ContainsGenericParameters) { var constructType = type.MakeGenericType(Enumerable.Repeat(typeof(object), type.GetGenericArguments().Length).ToArray()); Activator.CreateInstance(constructType, true); @@ -132,70 +165,35 @@ public void TypesInLibGit2SharpMustBeExtensibleInATestingContext() } } - if (nonTestableTypes.Any()) + if (nonTestableTypes.Count != 0) { - Assert.True(false, Environment.NewLine + BuildNonTestableTypesMessage(nonTestableTypes)); + Assert.Fail(Environment.NewLine + BuildNonTestableTypesMessage(nonTestableTypes)); } } private static bool MustBeMockable(Type type) { - if (type.IsSealed) + if (type.GetTypeInfo().IsSealed) { return false; } - if (type.IsAbstract) + if (type.GetTypeInfo().IsAbstract) { - return !type.Assembly.GetExportedTypes() - .Where(t => t.IsSubclassOf(type)) - .All(t => t.IsAbstract || t.IsSealed); + return !type.GetTypeInfo().Assembly.GetExportedTypes() + .Where(t => t.GetTypeInfo().IsSubclassOf(type)) + .All(t => t.GetTypeInfo().IsAbstract || t.GetTypeInfo().IsSealed); } return true; } - [Fact] - public void LibGit2SharpPublicInterfacesCoverAllPublicMembers() - { - var methodsMissingFromInterfaces = - from t in Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() - where !t.IsInterface - where t.GetInterfaces().Any(i => i.IsPublic && i.Namespace == typeof(IRepository).Namespace && !explicitOnlyInterfaces.Contains(i)) - let interfaceTargetMethods = from i in t.GetInterfaces() - from im in t.GetInterfaceMap(i).TargetMethods - select im - from tm in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) - where !interfaceTargetMethods.Contains(tm) - select t.Name + " has extra method " + tm.Name; - - Assert.Equal("", string.Join(Environment.NewLine, - methodsMissingFromInterfaces.ToArray())); - } - - [Fact] - public void LibGit2SharpExplicitOnlyInterfacesAreIndeedExplicitOnly() - { - var methodsMissingFromInterfaces = - from t in Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() - where t.GetInterfaces().Any(explicitOnlyInterfaces.Contains) - let interfaceTargetMethods = from i in t.GetInterfaces() - where explicitOnlyInterfaces.Contains(i) - from im in t.GetInterfaceMap(i).TargetMethods - select im - from tm in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) - where interfaceTargetMethods.Contains(tm) - select t.Name + " has public method " + tm.Name + " which should be explicitly implemented."; - - Assert.Equal("", string.Join(Environment.NewLine, - methodsMissingFromInterfaces.ToArray())); - } [Fact] public void EnumsWithFlagsHaveMutuallyExclusiveValues() { - var flagsEnums = Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() - .Where(t => t.IsEnum && t.GetCustomAttributes(typeof(FlagsAttribute), false).Any()); + var flagsEnums = typeof(IRepository).GetTypeInfo().Assembly.GetExportedTypes() + .Where(t => t.GetTypeInfo().IsEnum && t.GetTypeInfo().GetCustomAttributes(typeof(FlagsAttribute), false).Length != 0); var overlaps = from t in flagsEnums from int x in Enum.GetValues(t) @@ -259,19 +257,19 @@ private static bool HasEmptyPublicOrProtectedConstructor(Type type) private static bool IsStatic(Type type) { - return type.IsAbstract && type.IsSealed; + return type.GetTypeInfo().IsAbstract && type.GetTypeInfo().IsSealed; } // Related to https://github.com/libgit2/libgit2sharp/issues/644 and https://github.com/libgit2/libgit2sharp/issues/645 [Fact] public void GetEnumeratorMethodsInLibGit2SharpMustBeVirtualForTestability() { - var nonVirtualGetEnumeratorMethods = Assembly.GetAssembly(typeof(IRepository)) + var nonVirtualGetEnumeratorMethods = typeof(IRepository).GetTypeInfo().Assembly .GetExportedTypes() .Where(t => t.Namespace == typeof(IRepository).Namespace && - !t.IsSealed && - !t.IsAbstract && + !t.GetTypeInfo().IsSealed && + !t.GetTypeInfo().IsAbstract && t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(IEnumerable<>)))) .Select(t => t.GetMethod("GetEnumerator")) .Where(m => @@ -279,7 +277,7 @@ public void GetEnumeratorMethodsInLibGit2SharpMustBeVirtualForTestability() (!m.IsVirtual || m.IsFinal)) .ToList(); - if (nonVirtualGetEnumeratorMethods.Any()) + if (nonVirtualGetEnumeratorMethods.Count != 0) { var sb = new StringBuilder(); @@ -289,7 +287,7 @@ public void GetEnumeratorMethodsInLibGit2SharpMustBeVirtualForTestability() method.DeclaringType, Environment.NewLine); } - Assert.True(false, Environment.NewLine + sb.ToString()); + Assert.Fail(Environment.NewLine + sb.ToString()); } } @@ -298,7 +296,7 @@ public void NoPublicTypesUnderLibGit2SharpCoreNamespace() { const string coreNamespace = "LibGit2Sharp.Core"; - var types = Assembly.GetAssembly(typeof(IRepository)) + var types = typeof(IRepository).GetTypeInfo().Assembly .GetExportedTypes() .Where(t => t.FullName.StartsWith(coreNamespace + ".")) @@ -308,7 +306,7 @@ public void NoPublicTypesUnderLibGit2SharpCoreNamespace() .Where(t => t.FullName != "LibGit2Sharp.Core.LeaksContainer") .ToList(); - if (types.Any()) + if (types.Count != 0) { var sb = new StringBuilder(); @@ -318,7 +316,7 @@ public void NoPublicTypesUnderLibGit2SharpCoreNamespace() type.FullName, coreNamespace, Environment.NewLine); } - Assert.True(false, Environment.NewLine + sb.ToString()); + Assert.Fail(Environment.NewLine + sb.ToString()); } } @@ -326,7 +324,7 @@ public void NoPublicTypesUnderLibGit2SharpCoreNamespace() public void NoOptionalParametersinMethods() { IEnumerable mis = - from t in Assembly.GetAssembly(typeof(IRepository)) + from t in typeof(IRepository).GetTypeInfo().Assembly .GetExportedTypes() from m in t.GetMethods() where !m.IsObsolete() @@ -349,7 +347,7 @@ where p.IsOptional public void NoOptionalParametersinConstructors() { IEnumerable mis = - from t in Assembly.GetAssembly(typeof(IRepository)) + from t in typeof(IRepository).GetTypeInfo().Assembly .GetExportedTypes() from c in t.GetConstructors() from p in c.GetParameters() @@ -389,12 +387,12 @@ from m in GetInvalidPublicExtensionMethods() static IEnumerable GetInvalidPublicExtensionMethods() { - var query = from type in (Assembly.GetAssembly(typeof(IRepository))).GetTypes() - where type.IsSealed && !type.IsGenericType && !type.IsNested && type.IsPublic + var query = from type in typeof(IRepository).GetTypeInfo().Assembly.GetTypes() + where type.GetTypeInfo().IsSealed && !type.GetTypeInfo().IsGenericType && !type.IsNested && type.GetTypeInfo().IsPublic from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public) where method.IsDefined(typeof(ExtensionAttribute), false) let parameterType = method.GetParameters()[0].ParameterType - where parameterType != null && !parameterType.IsInterface && !parameterType.IsEnum + where parameterType != null && !parameterType.GetTypeInfo().IsInterface && !parameterType.GetTypeInfo().IsEnum select method; return query; } @@ -405,8 +403,8 @@ public void AllIDiffResultsAreInChangesBuilder() var diff = typeof(Diff).GetField("ChangesBuilders", BindingFlags.NonPublic | BindingFlags.Static); var changesBuilders = (System.Collections.IDictionary)diff.GetValue(null); - IEnumerable diffResults = typeof(Diff).Assembly.GetExportedTypes() - .Where(type => type.GetInterface("IDiffResult") != null); + IEnumerable diffResults = typeof(Diff).GetTypeInfo().Assembly.GetExportedTypes() + .Where(type => type.GetTypeInfo().GetInterface("IDiffResult") != null); var nonBuilderTypes = diffResults.Where(diffResult => !changesBuilders.Contains(diffResult)); Assert.False(nonBuilderTypes.Any(), "Classes which implement IDiffResult but are not registered under ChangesBuilders in Diff:" + Environment.NewLine + diff --git a/LibGit2Sharp.Tests/NetworkFixture.cs b/LibGit2Sharp.Tests/NetworkFixture.cs index 9153ffeb0..f4ad922f6 100644 --- a/LibGit2Sharp.Tests/NetworkFixture.cs +++ b/LibGit2Sharp.Tests/NetworkFixture.cs @@ -3,7 +3,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -12,7 +11,6 @@ public class NetworkFixture : BaseFixture [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository.git")] public void CanListRemoteReferences(string url) { string remoteName = "testRemote"; @@ -49,7 +47,6 @@ public void CanListRemoteReferences(string url) [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository.git")] public void CanListRemoteReferencesFromUrl(string url) { string repoPath = InitNewRepository(); @@ -94,9 +91,9 @@ public void CanListRemoteReferenceObjects() Remote remote = repo.Network.Remotes[remoteName]; IEnumerable references = repo.Network.ListReferences(remote).ToList(); - var actualRefs = new List>(); + var actualRefs = new List>(); - foreach(Reference reference in references) + foreach (Reference reference in references) { Assert.NotNull(reference.CanonicalName); @@ -166,15 +163,15 @@ public void CanPull(FastForwardStrategy fastForwardStrategy) MergeResult mergeResult = Commands.Pull(repo, Constants.Signature, pullOptions); - if(fastForwardStrategy == FastForwardStrategy.Default || fastForwardStrategy == FastForwardStrategy.FastForwardOnly) + if (fastForwardStrategy == FastForwardStrategy.Default || fastForwardStrategy == FastForwardStrategy.FastForwardOnly) { - Assert.Equal(mergeResult.Status, MergeStatus.FastForward); + Assert.Equal(MergeStatus.FastForward, mergeResult.Status); Assert.Equal(mergeResult.Commit, repo.Branches["refs/remotes/origin/master"].Tip); Assert.Equal(repo.Head.Tip, repo.Branches["refs/remotes/origin/master"].Tip); } else { - Assert.Equal(mergeResult.Status, MergeStatus.NonFastForward); + Assert.Equal(MergeStatus.NonFastForward, mergeResult.Status); } } } @@ -199,7 +196,7 @@ public void CanPullIntoEmptyRepo() // Pull! MergeResult mergeResult = Commands.Pull(repo, Constants.Signature, new PullOptions()); - Assert.Equal(mergeResult.Status, MergeStatus.FastForward); + Assert.Equal(MergeStatus.FastForward, mergeResult.Status); Assert.Equal(mergeResult.Commit, repo.Branches["refs/remotes/origin/master"].Tip); Assert.Equal(repo.Head.Tip, repo.Branches["refs/remotes/origin/master"].Tip); } @@ -226,7 +223,7 @@ public void PullWithoutMergeBranchThrows() { Commands.Pull(repo, Constants.Signature, new PullOptions()); } - catch(MergeFetchHeadNotFoundException ex) + catch (MergeFetchHeadNotFoundException ex) { didPullThrow = true; thrownException = ex; @@ -252,7 +249,7 @@ public void CanMergeFetchedRefs() Assert.False(repo.RetrieveStatus().Any()); Assert.Equal(repo.Lookup("refs/remotes/origin/master~1"), repo.Head.Tip); - Commands.Fetch(repo, repo.Head.RemoteName, new string[0], null, null); + Commands.Fetch(repo, repo.Head.RemoteName, Array.Empty(), null, null); MergeOptions mergeOptions = new MergeOptions() { @@ -260,7 +257,7 @@ public void CanMergeFetchedRefs() }; MergeResult mergeResult = repo.MergeFetchedRefs(Constants.Signature, mergeOptions); - Assert.Equal(mergeResult.Status, MergeStatus.NonFastForward); + Assert.Equal(MergeStatus.NonFastForward, mergeResult.Status); } } @@ -279,7 +276,7 @@ public void CanPruneRefs() using (var repo = new Repository(clonedRepoPath)) { repo.Network.Remotes.Add("pruner", clonedRepoPath2); - Commands.Fetch(repo, "pruner", new string[0], null, null); + Commands.Fetch(repo, "pruner", Array.Empty(), null, null); Assert.NotNull(repo.Refs["refs/remotes/pruner/master"]); // Remove the branch from the source repository @@ -289,11 +286,11 @@ public void CanPruneRefs() } // and by default we don't prune it - Commands.Fetch(repo, "pruner", new string[0], null, null); + Commands.Fetch(repo, "pruner", Array.Empty(), null, null); Assert.NotNull(repo.Refs["refs/remotes/pruner/master"]); // but we do when asked by the user - Commands.Fetch(repo, "pruner", new string[0], new FetchOptions { Prune = true}, null); + Commands.Fetch(repo, "pruner", Array.Empty(), new FetchOptions { Prune = true }, null); Assert.Null(repo.Refs["refs/remotes/pruner/master"]); } } diff --git a/LibGit2Sharp.Tests/NoteFixture.cs b/LibGit2Sharp.Tests/NoteFixture.cs index c1ea2b71e..4f23ced5c 100644 --- a/LibGit2Sharp.Tests/NoteFixture.cs +++ b/LibGit2Sharp.Tests/NoteFixture.cs @@ -10,7 +10,7 @@ namespace LibGit2Sharp.Tests public class NoteFixture : BaseFixture { private static readonly Signature signatureNullToken = new Signature("nulltoken", "emeric.fermas@gmail.com", DateTimeOffset.UtcNow); - private static readonly Signature signatureYorah = new Signature("yorah", "yoram.harmelin@gmail.com", Epoch.ToDateTimeOffset(1300557894, 60)); + private static readonly Signature signatureYorah = new Signature("yorah", "yoram.harmelin@gmail.com", DateTimeOffset.FromUnixTimeSeconds(1300557894).ToOffset(TimeSpan.FromMinutes(60))); [Fact] public void RetrievingNotesFromANonExistingGitObjectYieldsNoResult() @@ -20,7 +20,7 @@ public void RetrievingNotesFromANonExistingGitObjectYieldsNoResult() { var notes = repo.Notes[ObjectId.Zero]; - Assert.Equal(0, notes.Count()); + Assert.Empty(notes); } } @@ -32,7 +32,7 @@ public void RetrievingNotesFromAGitObjectWhichHasNoNoteYieldsNoResult() { var notes = repo.Notes[new ObjectId("4c062a6361ae6959e06292c1fa5e2822d9c96345")]; - Assert.Equal(0, notes.Count()); + Assert.Empty(notes); } } @@ -56,7 +56,7 @@ public void RetrievingNotesFromAGitObjectWhichHasNoNoteYieldsNoResult() [Fact] public void CanRetrieveNotesFromAGitObject() { - var expectedMessages = new [] { "Just Note, don't you understand?\n", "Nope\n", "Not Nope, Note!\n" }; + var expectedMessages = new[] { "Just Note, don't you understand?\n", "Nope\n", "Not Nope, Note!\n" }; string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) @@ -308,6 +308,21 @@ public void CanRetrieveTheListOfNotesForAGivenNamespace() } } + [Fact] + public void CanRetrieveNotesWhenThereAreNotAny() + { + string path = InitNewRepository(); // doesn't reproduce an error when using a sandbox repository so we have to create an actual repo. + using (var repo = new Repository(path)) + { + foreach (var note in repo.Notes) + { + Assert.NotNull(note); + } + Assert.Empty(repo.Notes); + } + } + + private static T[] SortedNotes(IEnumerable notes, Func selector) { return notes.OrderBy(n => n.Message, StringComparer.Ordinal).Select(selector).ToArray(); diff --git a/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs b/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs index fc06ef713..34d3eb77c 100644 --- a/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs +++ b/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs @@ -61,7 +61,7 @@ public void RetrieveObjectMetadataReturnsCorrectSizeAndTypeForBlob() GitObjectMetadata blobMetadata = repo.ObjectDatabase.RetrieveObjectMetadata(blob.Id); Assert.Equal(blobMetadata.Size, blob.Size); - Assert.Equal(blobMetadata.Type, ObjectType.Blob); + Assert.Equal(ObjectType.Blob, blobMetadata.Type); Blob fetchedBlob = repo.Lookup(blob.Id); Assert.Equal(blobMetadata.Size, fetchedBlob.Size); @@ -121,6 +121,31 @@ public void CanCreateABlobFromAStream(string expectedSha, string hintPath) } } + [Fact] + public void CanWriteABlobFromAByteArray() + { + var ba = Encoding.ASCII.GetBytes("libgit2\r\n"); + + using (var repo = new Repository(InitNewRepository())) + { + var id = repo.ObjectDatabase.Write(ba); + Assert.Equal(new ObjectId("99115ea359379a218c47cffc83cd0af8c91c4061"), id); + } + } + + [Fact] + public void CanWriteABlobFromAStream() + { + var ba = Encoding.ASCII.GetBytes("libgit2\r\n"); + + using (var stream = new MemoryStream(ba)) + using (var repo = new Repository(InitNewRepository())) + { + var id = repo.ObjectDatabase.Write(stream, stream.Length); + Assert.Equal(new ObjectId("99115ea359379a218c47cffc83cd0af8c91c4061"), id); + } + } + Stream PrepareMemoryStream(int contentSize) { var sb = new StringBuilder(); @@ -449,7 +474,7 @@ public void CanCreateABinaryBlobFromAStream() { Blob blob = repo.ObjectDatabase.CreateBlob(stream); Assert.Equal(6, blob.Size); - Assert.Equal(true, blob.IsBinary); + Assert.True(blob.IsBinary); } } } diff --git a/LibGit2Sharp.Tests/ObjectIdFixture.cs b/LibGit2Sharp.Tests/ObjectIdFixture.cs index e118cfdb8..8d3468bdd 100644 --- a/LibGit2Sharp.Tests/ObjectIdFixture.cs +++ b/LibGit2Sharp.Tests/ObjectIdFixture.cs @@ -133,7 +133,7 @@ public void TryParse(string maybeSha, bool isValidSha) Assert.NotNull(parsedObjectId); Assert.Equal(maybeSha, parsedObjectId.Sha); - Assert.True(maybeSha.StartsWith(parsedObjectId.ToString(3))); + Assert.StartsWith(parsedObjectId.ToString(3), maybeSha); Assert.Equal(maybeSha, parsedObjectId.ToString(42)); } diff --git a/LibGit2Sharp.Tests/OdbBackendFixture.cs b/LibGit2Sharp.Tests/OdbBackendFixture.cs index 975d0e88c..65011ce0f 100644 --- a/LibGit2Sharp.Tests/OdbBackendFixture.cs +++ b/LibGit2Sharp.Tests/OdbBackendFixture.cs @@ -26,7 +26,7 @@ private static Commit AddCommitToRepo(IRepository repo) var commit = repo.Commit("Initial commit", author, author); relativeFilepath = "big.txt"; - var zeros = new string('0', 32*1024 + 3); + var zeros = new string('0', 32 * 1024 + 3); Touch(repo.Info.WorkingDirectory, relativeFilepath, zeros); Commands.Stage(repo, relativeFilepath); @@ -145,7 +145,7 @@ public void CanEnumerateTheContentOfTheObjectDatabase() AddCommitToRepo(repo); - var expected = new[]{ "1fe3126", "2b297e6", "6518215", "9daeafb" }; + var expected = new[] { "1fe3126", "2b297e6", "6518215", "9daeafb" }; IEnumerable objs = repo.ObjectDatabase; @@ -296,7 +296,7 @@ public override int Read(ObjectId oid, out UnmanagedMemoryStream data, out Objec if (!m_objectIdToContent.TryGetValue(oid, out gitObject)) { - return (int) ReturnCode.GIT_ENOTFOUND; + return (int)ReturnCode.GIT_ENOTFOUND; } data = Allocate(gitObject.Length); @@ -411,7 +411,7 @@ public override int ExistsPrefix(string shortSha, out ObjectId found) if (numFound > 1) { found = null; - return (int) ReturnCode.GIT_EAMBIGUOUS; + return (int)ReturnCode.GIT_EAMBIGUOUS; } } diff --git a/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs b/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs new file mode 100644 index 000000000..ff4949aa4 --- /dev/null +++ b/LibGit2Sharp.Tests/PatchEntryChangesFixture.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class PatchEntryChangesFixture : BaseFixture + { + [Fact] + public void PatchEntryBasics() + { + // Init test repo + var path = SandboxStandardTestRepoGitDir(); + string file = "numbers.txt"; + + // The repo + using (var repo = new Repository(path)) + { + Tree rootCommitTree = repo.Lookup("f8d44d7").Tree; + Tree commitTreeWithUpdatedFile = repo.Lookup("ec9e401").Tree; + + // Create patch by diffing + using (var patch = repo.Diff.Compare(rootCommitTree, commitTreeWithUpdatedFile)) + { + PatchEntryChanges entryChanges = patch[file]; + Assert.Equal(2, entryChanges.LinesAdded); + Assert.Equal(1, entryChanges.LinesDeleted); + Assert.Equal(187, entryChanges.Patch.Length); + // Smoke test + Assert.Equal(Mode.NonExecutableFile, entryChanges.Mode); + Assert.Equal(new ObjectId("4625a3628cb78970c57e23a2fe2574514ba403c7"), entryChanges.Oid); + Assert.Equal(ChangeKind.Modified, entryChanges.Status); + Assert.Equal(file, entryChanges.OldPath); + Assert.Equal(Mode.NonExecutableFile, entryChanges.OldMode); + Assert.Equal(new ObjectId("7909961ae96accd75b6813d32e0fc1d6d52ec941"), entryChanges.OldOid); + } + } + } + } +} diff --git a/LibGit2Sharp.Tests/Properties/AssemblyInfo.cs b/LibGit2Sharp.Tests/Properties/AssemblyInfo.cs index adf33cb34..a4bcec543 100644 --- a/LibGit2Sharp.Tests/Properties/AssemblyInfo.cs +++ b/LibGit2Sharp.Tests/Properties/AssemblyInfo.cs @@ -1,42 +1,3 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using Xunit; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("libgit2sharp.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("libgit2sharp.Tests")] -[assembly: AssemblyCopyright("Copyright © 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("808554a4-f9fd-4035-8ab9-325793c7da51")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using Xunit; [assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/LibGit2Sharp.Tests/PushFixture.cs b/LibGit2Sharp.Tests/PushFixture.cs index d8cf2befe..824c1d8c0 100644 --- a/LibGit2Sharp.Tests/PushFixture.cs +++ b/LibGit2Sharp.Tests/PushFixture.cs @@ -12,8 +12,7 @@ public class PushFixture : BaseFixture { private void OnPushStatusError(PushStatusError pushStatusErrors) { - Assert.True(false, string.Format("Failed to update reference '{0}': {1}", - pushStatusErrors.Reference, pushStatusErrors.Message)); + Assert.Fail(string.Format("Failed to update reference '{0}': {1}", pushStatusErrors.Reference, pushStatusErrors.Message)); } private void AssertPush(Action push) @@ -196,6 +195,33 @@ public void CanForcePush() } } + [Fact] + public void CanPushWithCustomHeaders() + { + const string knownHeader = "X-Hello: mygit-201"; + var options = new PushOptions { CustomHeaders = new string[] { knownHeader } }; + AssertPush(repo => + repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)); + } + + [Fact] + public void CannotPushWithForbiddenCustomHeaders() + { + const string knownHeader = "User-Agent: mygit-201"; + var options = new PushOptions { CustomHeaders = new string[] { knownHeader } }; + Assert.Throws( + () => AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options))); + } + + [Fact] + public void CannotPushWithMalformedCustomHeaders() + { + const string knownHeader = "Hello world"; + var options = new PushOptions { CustomHeaders = new string[] { knownHeader } }; + Assert.Throws( + () => AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options))); + } + private static void AssertRemoteHeadTipEquals(IRepository localRepo, string sha) { var remoteReferences = localRepo.Network.ListReferences(localRepo.Network.Remotes.Single()); diff --git a/LibGit2Sharp.Tests/RebaseFixture.cs b/LibGit2Sharp.Tests/RebaseFixture.cs index 28c49738e..355e19295 100644 --- a/LibGit2Sharp.Tests/RebaseFixture.cs +++ b/LibGit2Sharp.Tests/RebaseFixture.cs @@ -62,7 +62,7 @@ public void CanRebase(string initialBranchName, RebaseOptions options = new RebaseOptions() { - RebaseStepStarting = x => + RebaseStepStarting = x => { beforeRebaseStepCountCorrect &= beforeStepCallCount == x.StepIndex; totalStepCountCorrect &= (x.TotalStepCount == stepCount); @@ -253,10 +253,10 @@ public void VerifyRebaseDetailed(string attributes, string lineEnding, string[] repo.Rebase.Start(null, upstreamBranch, null, Constants.Identity2, options); - Assert.Equal(true, wasCheckoutNotifyCalledForResetingHead); - Assert.Equal(true, wasCheckoutProgressCalledForResetingHead); - Assert.Equal(true, wasCheckoutNotifyCalled); - Assert.Equal(true, wasCheckoutProgressCalled); + Assert.True(wasCheckoutNotifyCalledForResetingHead); + Assert.True(wasCheckoutProgressCalledForResetingHead); + Assert.True(wasCheckoutNotifyCalled); + Assert.True(wasCheckoutProgressCalled); // Verify the chain of resultant rebased commits. CommitFilter commitFilter = new CommitFilter() @@ -276,7 +276,7 @@ public void VerifyRebaseDetailed(string attributes, string lineEnding, string[] List rebasedCommits = repo.Commits.QueryBy(commitFilter).ToList(); Assert.Equal(3, rebasedCommits.Count); - for(int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { Assert.Equal(expectedTreeIds[i], rebasedCommits[i].Tree.Id); Assert.Equal(Constants.Signature.Name, rebasedCommits[i].Author.Name); diff --git a/LibGit2Sharp.Tests/RefSpecFixture.cs b/LibGit2Sharp.Tests/RefSpecFixture.cs index b8f2b6a09..e0639caa8 100644 --- a/LibGit2Sharp.Tests/RefSpecFixture.cs +++ b/LibGit2Sharp.Tests/RefSpecFixture.cs @@ -15,7 +15,7 @@ public void CanCountRefSpecs() using (var repo = new Repository(path)) { var remote = repo.Network.Remotes["origin"]; - Assert.Equal(1, remote.RefSpecs.Count()); + Assert.Single(remote.RefSpecs); } } @@ -63,7 +63,7 @@ public void CanReadRefSpecDetails() Assert.Equal("refs/heads/*", refSpec.Source); Assert.Equal("refs/remotes/origin/*", refSpec.Destination); - Assert.Equal(true, refSpec.ForceUpdate); + Assert.True(refSpec.ForceUpdate); } } @@ -83,7 +83,7 @@ public void CanReplaceRefSpecs(string[] newFetchRefSpecs, string[] newPushRefSpe repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = newFetchRefSpecs, r => r.PushRefSpecs = newPushRefSpecs); - Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList()); + Assert.Equal(oldRefSpecs, remote.RefSpecs.ToList()); } using (var newRemote = repo.Network.Remotes["origin"]) @@ -225,7 +225,6 @@ public void CanCheckForMatches(string reference, bool shouldMatchSource, bool sh [Theory] [InlineData("refs/heads/master", "refs/remotes/foo/master")] [InlineData("refs/heads/bar/master", "refs/remotes/foo/bar/master")] - [InlineData("refs/heads/master", "refs/remotes/foo/master")] public void CanTransformRefspecs(string lhs, string rhs) { using (var repo = new Repository(InitNewRepository())) diff --git a/LibGit2Sharp.Tests/ReferenceFixture.cs b/LibGit2Sharp.Tests/ReferenceFixture.cs index ac2af3c24..b4ec734d5 100644 --- a/LibGit2Sharp.Tests/ReferenceFixture.cs +++ b/LibGit2Sharp.Tests/ReferenceFixture.cs @@ -295,12 +295,12 @@ public void RemovingAReferenceDecreasesTheRefsCount() const string refName = "refs/heads/test"; List refs = repo.Refs.Select(r => r.CanonicalName).ToList(); - Assert.True(refs.Contains(refName)); + Assert.Contains(refName, refs); repo.Refs.Remove(refName); List refs2 = repo.Refs.Select(r => r.CanonicalName).ToList(); - Assert.False(refs2.Contains(refName)); + Assert.DoesNotContain(refName, refs2); Assert.Equal(refs.Count - 1, refs2.Count); } @@ -459,7 +459,7 @@ public void CanUpdateTargetOfADirectReferenceWithAnAbbreviatedSha() Reference master = repo.Refs[masterRef]; Assert.NotEqual(sha, master.ResolveToDirectReference().Target.Sha); - Reference updated = repo.Refs.UpdateTarget(masterRef, sha.Substring(0,4)); + Reference updated = repo.Refs.UpdateTarget(masterRef, sha.Substring(0, 4)); master = repo.Refs[masterRef]; Assert.Equal(updated, master); @@ -556,7 +556,7 @@ public void CanUpdateTargetOfADirectReferenceWithARevparseSpec() const string name = "refs/heads/master"; - var master = (DirectReference) repo.Refs[name]; + var master = (DirectReference)repo.Refs[name]; var @from = master.Target.Id; const string logMessage = "update target message"; @@ -736,13 +736,13 @@ public void RenamingAReferenceDoesNotDecreaseTheRefsCount() const string newName = "refs/atic/tagtest"; List refs = repo.Refs.Select(r => r.CanonicalName).ToList(); - Assert.True(refs.Contains(oldName)); + Assert.Contains(oldName, refs); repo.Refs.Rename(oldName, newName); List refs2 = repo.Refs.Select(r => r.CanonicalName).ToList(); - Assert.False(refs2.Contains(oldName)); - Assert.True(refs2.Contains(newName)); + Assert.DoesNotContain(oldName, refs2); + Assert.Contains(newName, refs2); Assert.Equal(refs2.Count, refs.Count); } @@ -774,7 +774,7 @@ public void CanFilterReferencesWithAGlob() Assert.Equal(5, repo.Refs.FromGlob("refs/heads/*").Count()); Assert.Equal(5, repo.Refs.FromGlob("refs/tags/*").Count()); Assert.Equal(3, repo.Refs.FromGlob("*t?[pqrs]t*").Count()); - Assert.Equal(0, repo.Refs.FromGlob("test").Count()); + Assert.Empty(repo.Refs.FromGlob("test")); } } @@ -894,7 +894,7 @@ public void CanHandleInvalidArguments() Assert.Throws(() => repo.Refs.ReachableFrom(null)); Assert.Throws(() => repo.Refs.ReachableFrom(null, repo.Commits.Take(2))); Assert.Throws(() => repo.Refs.ReachableFrom(repo.Refs, null)); - Assert.Empty(repo.Refs.ReachableFrom(new Commit[] { })); + Assert.Empty(repo.Refs.ReachableFrom(Array.Empty())); } } } diff --git a/LibGit2Sharp.Tests/ReflogFixture.cs b/LibGit2Sharp.Tests/ReflogFixture.cs index fa5a0d842..52973454b 100644 --- a/LibGit2Sharp.Tests/ReflogFixture.cs +++ b/LibGit2Sharp.Tests/ReflogFixture.cs @@ -3,7 +3,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -23,7 +22,7 @@ public void CanReadReflog() // Initial commit assertions Assert.Equal("timothy.clem@gmail.com", reflog.Last().Committer.Email); - Assert.True(reflog.Last().Message.StartsWith("clone: from")); + Assert.StartsWith("clone: from", reflog.Last().Message); Assert.Equal(ObjectId.Zero, reflog.Last().From); // second commit assertions @@ -59,7 +58,7 @@ public void CommitShouldCreateReflogEntryOnHeadAndOnTargetedDirectReference() var identity = Constants.Identity; - using (var repo = new Repository(repoPath, new RepositoryOptions{ Identity = identity })) + using (var repo = new Repository(repoPath, new RepositoryOptions { Identity = identity })) { // setup refs as HEAD => unit_test => master var newRef = repo.Refs.Add("refs/heads/unit_test", "refs/heads/master"); @@ -78,32 +77,36 @@ public void CommitShouldCreateReflogEntryOnHeadAndOnTargetedDirectReference() Commit commit = repo.Commit(commitMessage, author, author); // Assert a reflog entry is created on HEAD - Assert.Equal(1, repo.Refs.Log("HEAD").Count()); + Assert.Single(repo.Refs.Log("HEAD")); var reflogEntry = repo.Refs.Log("HEAD").First(); Assert.Equal(identity.Name, reflogEntry.Committer.Name); Assert.Equal(identity.Email, reflogEntry.Committer.Email); - var now = DateTimeOffset.Now; - Assert.InRange(reflogEntry.Committer.When, before, now); + // When verifying the timestamp range, give a little more room on the range. + // Git or file system datetime truncation seems to cause these stamps to jump up to a second earlier + // than we expect. See https://github.com/libgit2/libgit2sharp/issues/1764 + var low = before - TimeSpan.FromSeconds(1); + var high = DateTimeOffset.Now.TruncateMilliseconds() + TimeSpan.FromSeconds(1); + Assert.InRange(reflogEntry.Committer.When, low, high); Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(ObjectId.Zero, reflogEntry.From); // Assert the same reflog entry is created on refs/heads/master - Assert.Equal(1, repo.Refs.Log("refs/heads/master").Count()); + Assert.Single(repo.Refs.Log("refs/heads/master")); reflogEntry = repo.Refs.Log("HEAD").First(); Assert.Equal(identity.Name, reflogEntry.Committer.Name); Assert.Equal(identity.Email, reflogEntry.Committer.Email); - Assert.InRange(reflogEntry.Committer.When, before, now); + Assert.InRange(reflogEntry.Committer.When, low, high); Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(ObjectId.Zero, reflogEntry.From); // Assert no reflog entry is created on refs/heads/unit_test - Assert.Equal(0, repo.Refs.Log("refs/heads/unit_test").Count()); + Assert.Empty(repo.Refs.Log("refs/heads/unit_test")); } } @@ -123,7 +126,7 @@ public void CommitOnUnbornReferenceShouldCreateReflogEntryWithInitialTag() repo.Commit(commitMessage, author, author); // Assert the reflog entry message is correct - Assert.Equal(1, repo.Refs.Log("HEAD").Count()); + Assert.Single(repo.Refs.Log("HEAD")); Assert.Equal(string.Format("commit (initial): {0}", commitMessage), repo.Refs.Log("HEAD").First().Message); } } @@ -160,8 +163,12 @@ public void CommitOnDetachedHeadShouldInsertReflogEntry() Assert.Equal(identity.Name, reflogEntry.Committer.Name); Assert.Equal(identity.Email, reflogEntry.Committer.Email); - var now = DateTimeOffset.Now; - Assert.InRange(reflogEntry.Committer.When, before, now); + // When verifying the timestamp range, give a little more room on the range. + // Git or file system datetime truncation seems to cause these stamps to jump up to a second earlier + // than we expect. See https://github.com/libgit2/libgit2sharp/issues/1764 + var low = before - TimeSpan.FromSeconds(1); + var high = DateTimeOffset.Now.TruncateMilliseconds() + TimeSpan.FromSeconds(1); + Assert.InRange(reflogEntry.Committer.When, low, high); Assert.Equal(commit.Id, reflogEntry.To); Assert.Equal(string.Format("commit: {0}", commitMessage), repo.Refs.Log("HEAD").First().Message); @@ -202,7 +209,7 @@ public void AppendingToReflogDependsOnCoreLogAllRefUpdatesSetting(bool isBare, b public void UnsignedMethodsWriteCorrectlyToTheReflog() { var repoPath = InitNewRepository(true); - using (var repo = new Repository(repoPath, new RepositoryOptions{ Identity = Constants.Identity })) + using (var repo = new Repository(repoPath, new RepositoryOptions { Identity = Constants.Identity })) { EnableRefLog(repo); diff --git a/LibGit2Sharp.Tests/RemoteFixture.cs b/LibGit2Sharp.Tests/RemoteFixture.cs index 28049f0e0..36990bb6e 100644 --- a/LibGit2Sharp.Tests/RemoteFixture.cs +++ b/LibGit2Sharp.Tests/RemoteFixture.cs @@ -203,7 +203,7 @@ public void DoesNotThrowWhenARemoteHasNoUrlSet() { var noUrlRemote = repo.Network.Remotes["no_url"]; Assert.NotNull(noUrlRemote); - Assert.Equal(null, noUrlRemote.Url); + Assert.Null(noUrlRemote.Url); var remotes = repo.Network.Remotes.ToList(); Assert.Equal(1, remotes.Count(r => r.Name == "no_url")); diff --git a/LibGit2Sharp.Tests/RemoveFixture.cs b/LibGit2Sharp.Tests/RemoveFixture.cs index a89977fce..1b74995ca 100644 --- a/LibGit2Sharp.Tests/RemoveFixture.cs +++ b/LibGit2Sharp.Tests/RemoveFixture.cs @@ -28,14 +28,14 @@ public class RemoveFixture : BaseFixture * 'git rm ' fails ("error: '' has local modifications"). */ [InlineData(false, "modified_unstaged_file.txt", false, FileStatus.ModifiedInWorkdir, true, true, FileStatus.NewInWorkdir | FileStatus.DeletedFromIndex)] - [InlineData(true, "modified_unstaged_file.txt", true, FileStatus.ModifiedInWorkdir, true, true, 0)] + [InlineData(true, "modified_unstaged_file.txt", true, FileStatus.ModifiedInWorkdir, true, true, FileStatus.Unaltered)] /*** * Test case: modified file in wd, the modifications have already been promoted to the index. * 'git rm --cached ' works (removes the file from the index) * 'git rm ' fails ("error: '' has changes staged in the index") */ [InlineData(false, "modified_staged_file.txt", false, FileStatus.ModifiedInIndex, true, true, FileStatus.NewInWorkdir | FileStatus.DeletedFromIndex)] - [InlineData(true, "modified_staged_file.txt", true, FileStatus.ModifiedInIndex, true, true, 0)] + [InlineData(true, "modified_staged_file.txt", true, FileStatus.ModifiedInIndex, true, true, FileStatus.Unaltered)] /*** * Test case: modified file in wd, the modifications have already been promoted to the index, and * the file does not exist in the HEAD. @@ -43,7 +43,7 @@ public class RemoveFixture : BaseFixture * 'git rm ' throws ("error: '' has changes staged in the index") */ [InlineData(false, "new_tracked_file.txt", false, FileStatus.NewInIndex, true, true, FileStatus.NewInWorkdir)] - [InlineData(true, "new_tracked_file.txt", true, FileStatus.NewInIndex, true, true, 0)] + [InlineData(true, "new_tracked_file.txt", true, FileStatus.NewInIndex, true, true, FileStatus.Unaltered)] public void CanRemoveAnUnalteredFileFromTheIndexWithoutRemovingItFromTheWorkingDirectory( bool removeFromWorkdir, string filename, bool throws, FileStatus initialStatus, bool existsBeforeRemove, bool existsAfterRemove, FileStatus lastStatus) { @@ -88,7 +88,7 @@ public void RemovingAModifiedFileWhoseChangesHaveBeenPromotedToTheIndexAndWithAd { string fullpath = Path.Combine(repo.Info.WorkingDirectory, filename); - Assert.Equal(true, File.Exists(fullpath)); + Assert.True(File.Exists(fullpath)); File.AppendAllText(fullpath, "additional content"); Assert.Equal(FileStatus.ModifiedInIndex | FileStatus.ModifiedInWorkdir, repo.RetrieveStatus(filename)); @@ -150,7 +150,7 @@ public void RemovingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(strin Commands.Remove(repo, relativePath, i % 2 == 0); Commands.Remove(repo, relativePath, i % 2 == 0, - new ExplicitPathsOptions {ShouldFailOnUnmatchedPath = false}); + new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); } } } @@ -169,7 +169,7 @@ public void RemovingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileS Assert.Equal(status, repo.RetrieveStatus(relativePath)); Assert.Throws( - () => Commands.Remove(repo, relativePath, i%2 == 0, new ExplicitPathsOptions())); + () => Commands.Remove(repo, relativePath, i % 2 == 0, new ExplicitPathsOptions())); } } } @@ -182,7 +182,7 @@ public void RemovingFileWithBadParamsThrows() { Assert.Throws(() => Commands.Remove(repo, string.Empty)); Assert.Throws(() => Commands.Remove(repo, (string)null)); - Assert.Throws(() => Commands.Remove(repo, new string[] { })); + Assert.Throws(() => Commands.Remove(repo, Array.Empty())); Assert.Throws(() => Commands.Remove(repo, new string[] { null })); } } diff --git a/LibGit2Sharp.Tests/RepositoryFixture.cs b/LibGit2Sharp.Tests/RepositoryFixture.cs index ec2192875..ef3e72f07 100644 --- a/LibGit2Sharp.Tests/RepositoryFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryFixture.cs @@ -4,7 +4,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -123,7 +122,7 @@ public void CanCreateStandardRepoAndSpecifyAFolderWhichWillContainTheNewlyCreate Assert.True(Repository.IsValid(repo.Info.WorkingDirectory)); Assert.True(Repository.IsValid(repo.Info.Path)); - Assert.Equal(false, repo.Info.IsBare); + Assert.False(repo.Info.IsBare); char sep = Path.DirectorySeparatorChar; Assert.Equal(scd1.RootedDirectoryPath + sep, repo.Info.WorkingDirectory); @@ -148,7 +147,7 @@ public void CanCreateStandardRepoAndDirectlySpecifyAGitDirectory() Assert.True(Repository.IsValid(repo.Info.WorkingDirectory)); Assert.True(Repository.IsValid(repo.Info.Path)); - Assert.Equal(false, repo.Info.IsBare); + Assert.False(repo.Info.IsBare); char sep = Path.DirectorySeparatorChar; Assert.Equal(scd1.RootedDirectoryPath + sep, repo.Info.WorkingDirectory); @@ -167,6 +166,10 @@ private static void CheckGitConfigFile(string dir) private static void AssertIsHidden(string repoPath) { + //Workaround for .NET Core 1.x never considering a directory hidden if the path has a trailing slash + //https://github.com/dotnet/corefx/issues/18520 + repoPath = repoPath.TrimEnd('/'); + FileAttributes attribs = File.GetAttributes(repoPath); Assert.Equal(FileAttributes.Hidden, (attribs & FileAttributes.Hidden)); @@ -209,13 +212,13 @@ public void CanFetchFromRemoteByName() } // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null); + Commands.Fetch(repo, remoteName, Array.Empty(), new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler }, null); // Verify the expected state expectedFetchState.CheckUpdatedReferences(repo); // Now fetch the rest of the tags - Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { TagFetchMode = TagFetchMode.All }, null); + Commands.Fetch(repo, remoteName, Array.Empty(), new FetchOptions { TagFetchMode = TagFetchMode.All }, null); // Verify that the "nearly-dangling" tag is now in the repo. Tag nearlyDanglingTag = repo.Tags["nearly-dangling"]; @@ -263,18 +266,18 @@ private static void AssertInitializedRepository(IRepository repo, string expecte Assert.Equal(headRef.TargetIdentifier, repo.Head.CanonicalName); Assert.Null(repo.Head.Tip); - Assert.Equal(0, repo.Commits.Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter()).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs.Head }).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Head }).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = "HEAD" }).Count()); - Assert.Equal(0, repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = expectedHeadTargetIdentifier }).Count()); + Assert.Empty(repo.Commits); + Assert.Empty(repo.Commits.QueryBy(new CommitFilter())); + Assert.Empty(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Refs.Head })); + Assert.Empty(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = repo.Head })); + Assert.Empty(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = "HEAD" })); + Assert.Empty(repo.Commits.QueryBy(new CommitFilter { IncludeReachableFrom = expectedHeadTargetIdentifier })); Assert.Null(repo.Head["subdir/I-do-not-exist"]); - Assert.Equal(0, repo.Branches.Count()); - Assert.Equal(0, repo.Refs.Count()); - Assert.Equal(0, repo.Tags.Count()); + Assert.Empty(repo.Branches); + Assert.Empty(repo.Refs); + Assert.Empty(repo.Tags); } [Fact] @@ -611,10 +614,8 @@ public void QueryingTheRemoteForADetachedHeadBranchReturnsNull() string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { - Console.WriteLine("head, {0}", repo.Head); Commands.Checkout(repo, repo.Head.Tip.Sha, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); Branch trackLocal = repo.Head; - Console.WriteLine("head, {0}", repo.Head); Assert.Null(trackLocal.RemoteName); } } @@ -707,7 +708,6 @@ public void CanListRemoteReferencesWithCredentials() [Theory] [InlineData("http://github.com/libgit2/TestGitRepository")] [InlineData("https://github.com/libgit2/TestGitRepository")] - [InlineData("git://github.com/libgit2/TestGitRepository.git")] public void CanListRemoteReferences(string url) { IEnumerable references = Repository.ListRemoteReferences(url).ToList(); diff --git a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs index 8cf033506..46863f44d 100644 --- a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs @@ -55,7 +55,7 @@ public void CanOpenABareRepoAsIfItWasAStandardOneWithANonExisitingIndexFile() [Fact] public void CanOpenABareRepoWithOptions() { - var options = new RepositoryOptions { GlobalConfigurationLocation = null }; + var options = new RepositoryOptions { }; string path = SandboxBareTestRepo(); using (var repo = new Repository(path, options)) @@ -110,8 +110,8 @@ public void CanProvideADifferentIndexToAStandardRepo() public void OpeningABareRepoWithoutProvidingBothWorkDirAndIndexThrows() { string path = SandboxBareTestRepo(); - Assert.Throws(() => new Repository(path, new RepositoryOptions {IndexPath = newIndex})); - Assert.Throws(() => new Repository(path, new RepositoryOptions {WorkingDirectoryPath = newWorkdir})); + Assert.Throws(() => new Repository(path, new RepositoryOptions { IndexPath = newIndex })); + Assert.Throws(() => new Repository(path, new RepositoryOptions { WorkingDirectoryPath = newWorkdir })); } [Fact] @@ -153,45 +153,6 @@ private string MeanwhileInAnotherDimensionAnEvilMastermindIsAtWork(string workin } } - [Fact] - public void CanProvideDifferentConfigurationFilesToARepository() - { - string globalLocation = Path.Combine(newWorkdir, "my-global-config"); - string xdgLocation = Path.Combine(newWorkdir, "my-xdg-config"); - string systemLocation = Path.Combine(newWorkdir, "my-system-config"); - - const string name = "Adam 'aroben' Roben"; - const string email = "adam@github.com"; - - StringBuilder sb = new StringBuilder() - .AppendLine("[user]") - .AppendFormat("name = {0}{1}", name, Environment.NewLine) - .AppendFormat("email = {0}{1}", email, Environment.NewLine); - - File.WriteAllText(globalLocation, sb.ToString()); - File.WriteAllText(systemLocation, string.Empty); - File.WriteAllText(xdgLocation, string.Empty); - - var options = new RepositoryOptions { - GlobalConfigurationLocation = globalLocation, - XdgConfigurationLocation = xdgLocation, - SystemConfigurationLocation = systemLocation, - }; - - string path = SandboxBareTestRepo(); - using (var repo = new Repository(path, options)) - { - Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global)); - Assert.Equal(name, repo.Config.Get("user.name").Value); - Assert.Equal(email, repo.Config.Get("user.email").Value); - - repo.Config.Set("xdg.setting", "https://twitter.com/libgit2sharp", ConfigurationLevel.Xdg); - repo.Config.Set("help.link", "https://twitter.com/xpaulbettsx/status/205761932626636800", ConfigurationLevel.System); - } - - AssertValueInConfigFile(systemLocation, "xpaulbettsx"); - } - [Fact] public void CanCommitOnBareRepository() { @@ -213,8 +174,8 @@ public void CanCommitOnBareRepository() Commands.Stage(repo, relativeFilepath); Assert.NotNull(repo.Commit("Initial commit", Constants.Signature, Constants.Signature)); - Assert.Equal(1, repo.Head.Commits.Count()); - Assert.Equal(1, repo.Commits.Count()); + Assert.Single(repo.Head.Commits); + Assert.Single(repo.Commits); } } } diff --git a/LibGit2Sharp.Tests/ResetHeadFixture.cs b/LibGit2Sharp.Tests/ResetHeadFixture.cs index 0379c855a..5fb841ae0 100644 --- a/LibGit2Sharp.Tests/ResetHeadFixture.cs +++ b/LibGit2Sharp.Tests/ResetHeadFixture.cs @@ -94,7 +94,7 @@ private void AssertSoftReset(Func branchIdentifierRetriever, boo { string repoPath = InitNewRepository(); - using (var repo = new Repository(repoPath, new RepositoryOptions{ Identity = Constants.Identity })) + using (var repo = new Repository(repoPath, new RepositoryOptions { Identity = Constants.Identity })) { FeedTheRepository(repo); @@ -256,7 +256,7 @@ public void HardResetUpdatesTheContentOfTheWorkingDirectory() names = new DirectoryInfo(repo.Info.WorkingDirectory).GetFileSystemInfos().Select(fsi => fsi.Name).ToList(); names.Sort(StringComparer.Ordinal); - Assert.Equal(true, progressCalled); + Assert.True(progressCalled); Assert.Equal(new[] { ".git", "README", "WillNotBeRemoved.txt", "branch_file.txt", "new.txt", "new_untracked_file.txt" }, names); } } diff --git a/LibGit2Sharp.Tests/ResetIndexFixture.cs b/LibGit2Sharp.Tests/ResetIndexFixture.cs index 0952e55f5..d0228ae2b 100644 --- a/LibGit2Sharp.Tests/ResetIndexFixture.cs +++ b/LibGit2Sharp.Tests/ResetIndexFixture.cs @@ -1,4 +1,3 @@ -using System.IO; using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -62,7 +61,7 @@ public void ResetTheIndexWithTheHeadUnstagesEverything() repo.Index.Replace(repo.Head.Tip); RepositoryStatus newStatus = repo.RetrieveStatus(); - Assert.Equal(0, newStatus.Where(IsStaged).Count()); + Assert.DoesNotContain(newStatus, IsStaged); // Assert that no reflog entry is created Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count()); @@ -79,7 +78,7 @@ public void CanResetTheIndexToTheContentOfACommitWithCommitAsArgument() RepositoryStatus newStatus = repo.RetrieveStatus(); - var expected = new[] { "1.txt", Path.Combine("1", "branch_file.txt"), "deleted_staged_file.txt", + var expected = new[] { "1.txt", string.Join("/", "1", "branch_file.txt"), "deleted_staged_file.txt", "deleted_unstaged_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt" }; Assert.Equal(expected.Length, newStatus.Where(IsStaged).Count()); @@ -120,7 +119,7 @@ public void CanResetTheIndexWhenARenameExists() repo.Index.Replace(repo.Lookup("32eab9c")); RepositoryStatus status = repo.RetrieveStatus(); - Assert.Equal(0, status.Where(IsStaged).Count()); + Assert.DoesNotContain(status, IsStaged); } } @@ -132,14 +131,14 @@ public void CanResetSourceOfARenameInIndex() Commands.Move(repo, "branch_file.txt", "renamed_branch_file.txt"); RepositoryStatus oldStatus = repo.RetrieveStatus(); - Assert.Equal(1, oldStatus.RenamedInIndex.Count()); + Assert.Single(oldStatus.RenamedInIndex); Assert.Equal(FileStatus.Nonexistent, oldStatus["branch_file.txt"].State); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); repo.Index.Replace(repo.Lookup("32eab9c"), new string[] { "branch_file.txt" }); RepositoryStatus newStatus = repo.RetrieveStatus(); - Assert.Equal(0, newStatus.RenamedInIndex.Count()); + Assert.Empty(newStatus.RenamedInIndex); Assert.Equal(FileStatus.DeletedFromWorkdir, newStatus["branch_file.txt"].State); Assert.Equal(FileStatus.NewInIndex, newStatus["renamed_branch_file.txt"].State); } @@ -153,13 +152,13 @@ public void CanResetTargetOfARenameInIndex() Commands.Move(repo, "branch_file.txt", "renamed_branch_file.txt"); RepositoryStatus oldStatus = repo.RetrieveStatus(); - Assert.Equal(1, oldStatus.RenamedInIndex.Count()); + Assert.Single(oldStatus.RenamedInIndex); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); repo.Index.Replace(repo.Lookup("32eab9c"), new string[] { "renamed_branch_file.txt" }); RepositoryStatus newStatus = repo.RetrieveStatus(); - Assert.Equal(0, newStatus.RenamedInIndex.Count()); + Assert.Empty(newStatus.RenamedInIndex); Assert.Equal(FileStatus.NewInWorkdir, newStatus["renamed_branch_file.txt"].State); Assert.Equal(FileStatus.DeletedFromIndex, newStatus["branch_file.txt"].State); } diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/index b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/index index abbebf181..1d91f96f1 100644 Binary files a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/index and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/index differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/09/55f5468a8e493c0116872fa3e1807559bd5bb2 b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/09/55f5468a8e493c0116872fa3e1807559bd5bb2 new file mode 100644 index 000000000..77e845bdc Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/09/55f5468a8e493c0116872fa3e1807559bd5bb2 differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/1e/b39b79963b9acb87bb1b76a218d862b689f4cd b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/1e/b39b79963b9acb87bb1b76a218d862b689f4cd new file mode 100644 index 000000000..81a42108b Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/1e/b39b79963b9acb87bb1b76a218d862b689f4cd differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/79/853dbb13f5e83a1e9e69bf747c5a667c21d420 b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/79/853dbb13f5e83a1e9e69bf747c5a667c21d420 new file mode 100644 index 000000000..5eba30ecd Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/79/853dbb13f5e83a1e9e69bf747c5a667c21d420 differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/96/adea09cb7c3e9e81e488fbf0bc29e61d8100be b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/96/adea09cb7c3e9e81e488fbf0bc29e61d8100be new file mode 100644 index 000000000..12b03deec Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/96/adea09cb7c3e9e81e488fbf0bc29e61d8100be differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/cf/2badd0f483ffe19755560e6f736f1b4621f737 b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/cf/2badd0f483ffe19755560e6f736f1b4621f737 new file mode 100644 index 000000000..501f4f309 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/cf/2badd0f483ffe19755560e6f736f1b4621f737 differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/e5/5b31220c73a5535ba58709791880f3035849d4 b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/e5/5b31220c73a5535ba58709791880f3035849d4 new file mode 100644 index 000000000..a10105db5 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/objects/e5/5b31220c73a5535ba58709791880f3035849d4 differ diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/refs/heads/conflicts_spaces b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/refs/heads/conflicts_spaces new file mode 100644 index 000000000..49be02a92 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/refs/heads/conflicts_spaces @@ -0,0 +1 @@ +79853dbb13f5e83a1e9e69bf747c5a667c21d420 diff --git a/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/refs/heads/conflicts_tabs b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/refs/heads/conflicts_tabs new file mode 100644 index 000000000..8a2211064 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/refs/heads/conflicts_tabs @@ -0,0 +1 @@ +e55b31220c73a5535ba58709791880f3035849d4 diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/1.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/1.txt new file mode 100644 index 000000000..5626abf0f --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/1.txt @@ -0,0 +1 @@ +one diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/1/branch_file.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/1/branch_file.txt new file mode 100644 index 000000000..45b983be3 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/1/branch_file.txt @@ -0,0 +1 @@ +hi diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/README b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/README new file mode 100644 index 000000000..a8233120f --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/README @@ -0,0 +1 @@ +hey there diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/branch_file.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/branch_file.txt new file mode 100644 index 000000000..45b983be3 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/branch_file.txt @@ -0,0 +1 @@ +hi diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/COMMIT_EDITMSG b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/COMMIT_EDITMSG new file mode 100644 index 000000000..63ec8fdda --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/COMMIT_EDITMSG @@ -0,0 +1 @@ +Add "1.txt" file beside "1" folder diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/HEAD new file mode 100644 index 000000000..cb4380516 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/config b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/config new file mode 100644 index 000000000..1599f0b76 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/config @@ -0,0 +1,23 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = c:/GitHub/libgit2sharp/Resources/testrepo.git +[remote "no_url"] + url = + fetch = +refs/heads/*:refs/remotes/no_url/* +[branch "master"] + remote = origin + merge = refs/heads/master +[branch "track-local"] + remote = . + merge = refs/heads/master +[unittests] + longsetting = 15234 + intsetting = 2 diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/index b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/index new file mode 100644 index 000000000..1f5bd73a7 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/index differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/info/exclude b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/info/exclude new file mode 100644 index 000000000..f00680973 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/HEAD new file mode 100644 index 000000000..23375c60c --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/HEAD @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 4c062a6361ae6959e06292c1fa5e2822d9c96345 Tim Clem 1303768198 -0700 clone: from c:/GitHub/libgit2sharp/Resources/testrepo.git +4c062a6361ae6959e06292c1fa5e2822d9c96345 592d3c869dbc4127fc57c189cb94f2794fa84e7e Tim Clem 1303835722 -0700 commit: add more test files +592d3c869dbc4127fc57c189cb94f2794fa84e7e 32eab9cb1f450b5fe7ab663462b77d7f4b703344 nulltoken 1320047537 +0100 commit: Add "1.txt" file beside "1" folder diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/refs/heads/logo b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/refs/heads/logo new file mode 100644 index 000000000..e3e3e53ff --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/refs/heads/logo @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 4c062a6361ae6959e06292c1fa5e2822d9c96345 nulltoken 1359021419 +0100 branch: Created from 4c062a6361ae6959e06292c1fa5e2822d9c96345 +4c062a6361ae6959e06292c1fa5e2822d9c96345 a447ba2ca8fffd46dece72f7db6faf324afb8fcd nulltoken 1359021433 +0100 commit: Add logo diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/refs/heads/master b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/refs/heads/master new file mode 100644 index 000000000..23375c60c --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/logs/refs/heads/master @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 4c062a6361ae6959e06292c1fa5e2822d9c96345 Tim Clem 1303768198 -0700 clone: from c:/GitHub/libgit2sharp/Resources/testrepo.git +4c062a6361ae6959e06292c1fa5e2822d9c96345 592d3c869dbc4127fc57c189cb94f2794fa84e7e Tim Clem 1303835722 -0700 commit: add more test files +592d3c869dbc4127fc57c189cb94f2794fa84e7e 32eab9cb1f450b5fe7ab663462b77d7f4b703344 nulltoken 1320047537 +0100 commit: Add "1.txt" file beside "1" folder diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/04/c9b35f51fbff2338d5cdc959b23a93a14c5063 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/04/c9b35f51fbff2338d5cdc959b23a93a14c5063 new file mode 100644 index 000000000..a8660a9ea Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/04/c9b35f51fbff2338d5cdc959b23a93a14c5063 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/0a/99448e920a3615f33273047412949d09015ff8 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/0a/99448e920a3615f33273047412949d09015ff8 new file mode 100644 index 000000000..560fa54b9 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/0a/99448e920a3615f33273047412949d09015ff8 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 new file mode 100644 index 000000000..cedb2a22e Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/15/d2ecc60893449f4fe4593dd51a4706dec212f5 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/15/d2ecc60893449f4fe4593dd51a4706dec212f5 new file mode 100644 index 000000000..ec005aa90 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/15/d2ecc60893449f4fe4593dd51a4706dec212f5 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/16/bdf1dece5c56c92a9187550fafe0270a03a93a b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/16/bdf1dece5c56c92a9187550fafe0270a03a93a new file mode 100644 index 000000000..6f579f741 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/16/bdf1dece5c56c92a9187550fafe0270a03a93a @@ -0,0 +1,2 @@ +x ̱ +1EQjm\,Dl,'Ä:"큛3^47uو \0yVg(Wϝ XmL?2ʍjK=yPK™I #Y \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 new file mode 100644 index 000000000..93a16f146 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/18/10dff58d8a660512d4832e740f692884338ccd new file mode 100644 index 000000000..ba0bfb30c Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/18/10dff58d8a660512d4832e740f692884338ccd differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/32/eab9cb1f450b5fe7ab663462b77d7f4b703344 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/32/eab9cb1f450b5fe7ab663462b77d7f4b703344 new file mode 100644 index 000000000..9690a6ad0 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/32/eab9cb1f450b5fe7ab663462b77d7f4b703344 @@ -0,0 +1 @@ +x]j!)} ?!dm+qq]3䥠>Z&0M1h}VvY+.a9z4O *LGxxdVðc^Zg(e-<n-Qo߀kBgORE5%6r %bx7GP diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/33/a9574ff4dca3fbf68c6785859b80895c6b77b1 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/33/a9574ff4dca3fbf68c6785859b80895c6b77b1 new file mode 100644 index 000000000..95151430c Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/33/a9574ff4dca3fbf68c6785859b80895c6b77b1 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/37/d22f870ffe688c0d1220fbbf1f06629c64142c b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/37/d22f870ffe688c0d1220fbbf1f06629c64142c new file mode 100644 index 000000000..0598edb69 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/37/d22f870ffe688c0d1220fbbf1f06629c64142c differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 new file mode 100644 index 000000000..7ca4ceed5 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/46/25a3628cb78970c57e23a2fe2574514ba403c7 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/46/25a3628cb78970c57e23a2fe2574514ba403c7 new file mode 100644 index 000000000..500ff4036 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/46/25a3628cb78970c57e23a2fe2574514ba403c7 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 new file mode 100644 index 000000000..8953b6cef --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 @@ -0,0 +1,2 @@ +xQ +0D)6ͦ "xO-FbEo0 Ǥ,ske[Pn8R,EpD?g}^3 <GhYK8ЖDA);gݧjp4-r;sGA4ۺ=(in7IKFE \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4b/e51d6fc0943aa42b635c762145ca209cf39771 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4b/e51d6fc0943aa42b635c762145ca209cf39771 new file mode 100644 index 000000000..e1ab3daf0 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4b/e51d6fc0943aa42b635c762145ca209cf39771 @@ -0,0 +1,2 @@ +xK +1D] C tg&`&QVkQFgauxved0Sr.yJ!'C^! @`2,@ ({Oi_eO\VRag):w>R-&BEy \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4c/062a6361ae6959e06292c1fa5e2822d9c96345 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4c/062a6361ae6959e06292c1fa5e2822d9c96345 new file mode 100644 index 000000000..4d86b3208 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4c/062a6361ae6959e06292c1fa5e2822d9c96345 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4e/935b73cf0fe06c513267d517fc2e65fc0d100e b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4e/935b73cf0fe06c513267d517fc2e65fc0d100e new file mode 100644 index 000000000..c48084358 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/4e/935b73cf0fe06c513267d517fc2e65fc0d100e differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/50/9d02afef0632c7f733ddcd62500b0538d9157f b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/50/9d02afef0632c7f733ddcd62500b0538d9157f new file mode 100644 index 000000000..b3fac2ef1 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/50/9d02afef0632c7f733ddcd62500b0538d9157f differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/56/05472eb48cb4e60b5aa8810cc5ec8138026fad b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/56/05472eb48cb4e60b5aa8810cc5ec8138026fad new file mode 100644 index 000000000..ecaf0c6fe Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/56/05472eb48cb4e60b5aa8810cc5ec8138026fad differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 new file mode 100644 index 000000000..4d5447467 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/58/0c2111be43802dab11328176d94c391f1deae9 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/58/0c2111be43802dab11328176d94c391f1deae9 new file mode 100644 index 000000000..81671a754 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/58/0c2111be43802dab11328176d94c391f1deae9 @@ -0,0 +1,2 @@ +xAj1 E)t{d[6EvG ep}B{>B0I \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/58/1f9824ecaf824221bd36edf5430f2739a7c4f5 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/58/1f9824ecaf824221bd36edf5430f2739a7c4f5 new file mode 100644 index 000000000..2ae137844 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/58/1f9824ecaf824221bd36edf5430f2739a7c4f5 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/59/2d3c869dbc4127fc57c189cb94f2794fa84e7e b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/59/2d3c869dbc4127fc57c189cb94f2794fa84e7e new file mode 100644 index 000000000..33d011c41 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/59/2d3c869dbc4127fc57c189cb94f2794fa84e7e differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 new file mode 100644 index 000000000..c1f22c54f --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 @@ -0,0 +1,2 @@ +x 1ENi@k2 "X$YW0YcÅszMD08!s Xgd::@X0Pw"F/RUzmZZV}|/o5I!1z:vUim}/> +F- \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/67/b8324ec2fefc01fd9d31d328116df0474e7acd b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/67/b8324ec2fefc01fd9d31d328116df0474e7acd new file mode 100644 index 000000000..8c4a65994 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/67/b8324ec2fefc01fd9d31d328116df0474e7acd differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/6b/53f5d357f29607605ce2e612d2fda6693ff8d7 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/6b/53f5d357f29607605ce2e612d2fda6693ff8d7 new file mode 100644 index 000000000..c0685b971 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/6b/53f5d357f29607605ce2e612d2fda6693ff8d7 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/72/52fe2da2c4dd6d85231a150d0485ec46abaa7a b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/72/52fe2da2c4dd6d85231a150d0485ec46abaa7a new file mode 100644 index 000000000..15682ca4d Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/72/52fe2da2c4dd6d85231a150d0485ec46abaa7a differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/74/9a42f6ef33405e5ac16687963aebab8b78abd1 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/74/9a42f6ef33405e5ac16687963aebab8b78abd1 new file mode 100644 index 000000000..dbebba8e4 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/74/9a42f6ef33405e5ac16687963aebab8b78abd1 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a new file mode 100644 index 000000000..2ef4faa0f Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/79/09961ae96accd75b6813d32e0fc1d6d52ec941 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/79/09961ae96accd75b6813d32e0fc1d6d52ec941 new file mode 100644 index 000000000..a1f7d97f3 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/79/09961ae96accd75b6813d32e0fc1d6d52ec941 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 new file mode 100644 index 000000000..23c462f34 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/7f/76480d939dc401415927ea7ef25c676b8ddb8f b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/7f/76480d939dc401415927ea7ef25c676b8ddb8f new file mode 100644 index 000000000..7018c7f77 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/7f/76480d939dc401415927ea7ef25c676b8ddb8f differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d new file mode 100644 index 000000000..2f9b6b6e3 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/84/96071c1b46c854b31185ea97743be6a8774479 new file mode 100644 index 000000000..5df58dda5 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/84/96071c1b46c854b31185ea97743be6a8774479 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/84/9f67c87f926a81af895fc037c04ad85549d73f b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/84/9f67c87f926a81af895fc037c04ad85549d73f new file mode 100644 index 000000000..eb5f7b230 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/84/9f67c87f926a81af895fc037c04ad85549d73f differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/89/657cd6da3ada7bfef880e6dfdb9732f28c272b b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/89/657cd6da3ada7bfef880e6dfdb9732f28c272b new file mode 100644 index 000000000..89e7566cd --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/89/657cd6da3ada7bfef880e6dfdb9732f28c272b @@ -0,0 +1,2 @@ +xA + E/cJ=8TA#]M ϭQvL0I?!4Z=!צ8F!rsQy9]$D&l6A>jFWҵ IKNiZ%S + U~̽>' w [ DGڡQ-M>dO}\8g_ШoYr \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd new file mode 100644 index 000000000..d0d7e736e Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 new file mode 100644 index 000000000..18a7f61c2 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/ab/38987d12dc243c103a432608648c78fc6651a1 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/ab/38987d12dc243c103a432608648c78fc6651a1 new file mode 100644 index 000000000..649e9bbfa --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/ab/38987d12dc243c103a432608648c78fc6651a1 @@ -0,0 +1,2 @@ +xM +0F] eDzq@@ oo {Rré 1EȀKƀ<|V~ƛ|L|ܞ>I-Ws@Ԡ\"KZ Gw: \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 new file mode 100644 index 000000000..f460f2547 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 @@ -0,0 +1,2 @@ +xA +0a9I p'1Ѷv\x{cVpvWgǎ0x[ ]"g#{rD Cot N U $?9-p+1^Qx9O\C m'D {mV(+l, \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b3/e375c923d50c589b11b6da4a769bdd7f6502e3 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b3/e375c923d50c589b11b6da4a769bdd7f6502e3 new file mode 100644 index 000000000..511e72c6a Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b3/e375c923d50c589b11b6da4a769bdd7f6502e3 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b5/9b86c5f4874aea5255bf14d67a5ce13c80265f b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b5/9b86c5f4874aea5255bf14d67a5ce13c80265f new file mode 100644 index 000000000..eed20b8e3 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b5/9b86c5f4874aea5255bf14d67a5ce13c80265f differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b7/58c5bc1c8117c2a4c545dae2903e36360501c5 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b7/58c5bc1c8117c2a4c545dae2903e36360501c5 new file mode 100644 index 000000000..ee6419e03 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/b7/58c5bc1c8117c2a4c545dae2903e36360501c5 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 new file mode 100644 index 000000000..0817229bc --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 @@ -0,0 +1,3 @@ +xKj1D)zUB-0uV9<#+W e^7t:wo܂ p@.=..nD"JHqDV1tUeޕi n afu9FkcOe׿*qk9rL^"!ay%_2fw3G_K \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/e8/953ab38d30b11c45b5ac7229fcef0ab4d603c6 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/e8/953ab38d30b11c45b5ac7229fcef0ab4d603c6 new file mode 100644 index 000000000..16eca526d --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/e8/953ab38d30b11c45b5ac7229fcef0ab4d603c6 @@ -0,0 +1 @@ +x A@0P[!N#ϋz]ld uE/DnDT$hXϼzp0(=bhj73|e~#[ \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/ec/9e401198937e33a8617be9f235a449728d9f6d b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/ec/9e401198937e33a8617be9f235a449728d9f6d new file mode 100644 index 000000000..9ba063ec3 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/ec/9e401198937e33a8617be9f235a449728d9f6d @@ -0,0 +1,4 @@ +xA +0E]$ fL#1 +>ǃ?ScU`=J'DdQ)xFDGު'WD¨0x2L-Z#qbm-> +n呶ے9=+hG7B3jDuaZuO-[WcT_FHn \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f2/e41136eac73c39554dede1fd7e67b12502d577 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f2/e41136eac73c39554dede1fd7e67b12502d577 new file mode 100644 index 000000000..1cccf6543 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f2/e41136eac73c39554dede1fd7e67b12502d577 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 new file mode 100644 index 000000000..03770969a Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f7/05abffe7015f2beacf2abe7a36583ebee3487e b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f7/05abffe7015f2beacf2abe7a36583ebee3487e new file mode 100644 index 000000000..7490425a2 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f7/05abffe7015f2beacf2abe7a36583ebee3487e @@ -0,0 +1 @@ +x B!D=S h؅Bb;XGc|/Kdz-FѲDXy) Y1X4z.rdv4Mbst+ҌS/zkuk}I\qVOlm QΣCPp1J \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f8/d44d712e0680d942a4015058dd84e382879fe2 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f8/d44d712e0680d942a4015058dd84e382879fe2 new file mode 100644 index 000000000..639e89094 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/f8/d44d712e0680d942a4015058dd84e382879fe2 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 new file mode 100644 index 000000000..112998d42 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/fd/093bff70906175335656e6ce6ae05783708765 new file mode 100644 index 000000000..12bf5f3e3 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/fd/093bff70906175335656e6ce6ae05783708765 differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx new file mode 100644 index 000000000..5068f2818 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack new file mode 100644 index 000000000..a6a1f3020 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx new file mode 100644 index 000000000..94c3c71da Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack new file mode 100644 index 000000000..74c7fe4f3 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx new file mode 100644 index 000000000..555cfa977 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack new file mode 100644 index 000000000..4d539ed0a Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/packed-refs b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/packed-refs new file mode 100644 index 000000000..71a7668ba --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/packed-refs @@ -0,0 +1,10 @@ +# pack-refs with: peeled +b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/test +^e90810b8df3e80c413d903f631643c716887138d +e90810b8df3e80c413d903f631643c716887138d refs/tags/lw +7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b +^e90810b8df3e80c413d903f631643c716887138d +e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/test +4a202b346bb0fb0db7eff3cffeb3c70babbd2045 refs/remotes/origin/packed-test +580c2111be43802dab11328176d94c391f1deae9 refs/remotes/origin/master +a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/br2 diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/diff-test-cases b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/diff-test-cases new file mode 100644 index 000000000..f385e58ba --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/diff-test-cases @@ -0,0 +1 @@ +e7039e6d0e7dd4d4c1e2e8e5aa5306b2776436ca diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/i-do-numbers b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/i-do-numbers new file mode 100644 index 000000000..882969dfc --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/i-do-numbers @@ -0,0 +1 @@ +7252fe2da2c4dd6d85231a150d0485ec46abaa7a diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/logo b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/logo new file mode 100644 index 000000000..2bbca050f --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/logo @@ -0,0 +1 @@ +a447ba2ca8fffd46dece72f7db6faf324afb8fcd diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/master b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/master new file mode 100644 index 000000000..bca334acf --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/master @@ -0,0 +1 @@ +32eab9cb1f450b5fe7ab663462b77d7f4b703344 diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/track-local b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/track-local new file mode 100644 index 000000000..99098dc82 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/track-local @@ -0,0 +1 @@ +580c2111be43802dab11328176d94c391f1deae9 diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/treesame_as_32eab b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/treesame_as_32eab new file mode 100644 index 000000000..2f412c398 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/heads/treesame_as_32eab @@ -0,0 +1 @@ +f705abffe7015f2beacf2abe7a36583ebee3487e diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/remotes/origin/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/remotes/origin/HEAD new file mode 100644 index 000000000..b827f0c47 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/master diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/HEAD new file mode 100644 index 000000000..43d92a0f8 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/HEAD @@ -0,0 +1 @@ +ref: refs/heads/i-do-numbers diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/ORIG_HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/ORIG_HEAD new file mode 100644 index 000000000..882969dfc --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/ORIG_HEAD @@ -0,0 +1 @@ +7252fe2da2c4dd6d85231a150d0485ec46abaa7a diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/commondir b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/commondir new file mode 100644 index 000000000..aab0408ce --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/commondir @@ -0,0 +1 @@ +../.. diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/gitdir b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/gitdir new file mode 100644 index 000000000..3644e42d1 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/gitdir @@ -0,0 +1 @@ +../../../../worktrees/i-do-numbers/.git diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/index b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/index new file mode 100644 index 000000000..d8a77a021 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/index differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/logs/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/logs/HEAD new file mode 100644 index 000000000..f08b3ba25 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/i-do-numbers/logs/HEAD @@ -0,0 +1 @@ +7252fe2da2c4dd6d85231a150d0485ec46abaa7a 7252fe2da2c4dd6d85231a150d0485ec46abaa7a Mike Minns 1513714384 +0000 reset: moving to HEAD diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/HEAD new file mode 100644 index 000000000..846e685a7 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/HEAD @@ -0,0 +1 @@ +ref: refs/heads/logo diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/ORIG_HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/ORIG_HEAD new file mode 100644 index 000000000..2bbca050f --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/ORIG_HEAD @@ -0,0 +1 @@ +a447ba2ca8fffd46dece72f7db6faf324afb8fcd diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/commondir b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/commondir new file mode 100644 index 000000000..aab0408ce --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/commondir @@ -0,0 +1 @@ +../.. diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/gitdir b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/gitdir new file mode 100644 index 000000000..ad8863ee6 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/gitdir @@ -0,0 +1 @@ +../../../../worktrees/logo/.git diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/index b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/index new file mode 100644 index 000000000..2b8b35b77 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/index differ diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/locked b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/locked new file mode 100644 index 000000000..9f0b8bf32 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/locked @@ -0,0 +1 @@ +Test lock reason diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/logs/HEAD b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/logs/HEAD new file mode 100644 index 000000000..ab8778340 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/dot_git/worktrees/logo/logs/HEAD @@ -0,0 +1 @@ +a447ba2ca8fffd46dece72f7db6faf324afb8fcd a447ba2ca8fffd46dece72f7db6faf324afb8fcd Mike Minns 1513713776 +0000 reset: moving to HEAD diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/modified_staged_file.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/modified_staged_file.txt new file mode 100644 index 000000000..e68bcc7b5 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/modified_staged_file.txt @@ -0,0 +1,2 @@ +a change +more files! diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/modified_unstaged_file.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/modified_unstaged_file.txt new file mode 100644 index 000000000..da6fd6537 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/modified_unstaged_file.txt @@ -0,0 +1,2 @@ +some more text +more files! more files! diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new.txt new file mode 100644 index 000000000..a71586c1d --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new.txt @@ -0,0 +1 @@ +my new file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new_tracked_file.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new_tracked_file.txt new file mode 100644 index 000000000..935a81d39 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new_tracked_file.txt @@ -0,0 +1 @@ +a new file diff --git a/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new_untracked_file.txt b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new_untracked_file.txt new file mode 100644 index 000000000..d95f3ad14 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/testrepo_wd/new_untracked_file.txt @@ -0,0 +1 @@ +content diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/dot_git b/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/dot_git new file mode 100644 index 000000000..c14c3a26c --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/dot_git @@ -0,0 +1 @@ +gitdir: ../../testrepo_wd/.git/worktrees/i-do-numbers diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/numbers.txt b/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/numbers.txt new file mode 100644 index 000000000..85e1bcbc0 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/numbers.txt @@ -0,0 +1,17 @@ +1 +2 +3 +4 +5 +6 +7 +7.2 +8 +9 +10 +11 +12 +13 +14 +15 +16 diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/super-file.txt b/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/super-file.txt new file mode 100644 index 000000000..f9ff5589e --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/i-do-numbers/super-file.txt @@ -0,0 +1,5 @@ +That's a terrible name! +I don't like it. +People look down at me and laugh. :-( +Really!!!! +Yeah! Better! diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/1/branch_file.txt b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/1/branch_file.txt new file mode 100644 index 000000000..edf0effbb --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/1/branch_file.txt @@ -0,0 +1 @@ +hi diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/README b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/README new file mode 100644 index 000000000..ca8c64728 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/README @@ -0,0 +1 @@ +hey there diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/branch_file.txt b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/branch_file.txt new file mode 100644 index 000000000..edf0effbb --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/branch_file.txt @@ -0,0 +1 @@ +hi diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/dot_git b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/dot_git new file mode 100644 index 000000000..8295ccb37 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/dot_git @@ -0,0 +1 @@ +gitdir: ../../testrepo_wd/.git/worktrees/logo diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/new.txt b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/new.txt new file mode 100644 index 000000000..8e0884e36 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/new.txt @@ -0,0 +1 @@ +my new file diff --git a/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/square-logo.png b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/square-logo.png new file mode 100644 index 000000000..b758c5bc1 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/worktree/worktrees/logo/square-logo.png differ diff --git a/LibGit2Sharp.Tests/RevertFixture.cs b/LibGit2Sharp.Tests/RevertFixture.cs index cf17dcfe1..c43479f0f 100644 --- a/LibGit2Sharp.Tests/RevertFixture.cs +++ b/LibGit2Sharp.Tests/RevertFixture.cs @@ -157,7 +157,7 @@ public void RevertWithFileConflictStrategyOption(CheckoutFileConflictStrategy co FileConflictStrategy = conflictStrategy, }; - RevertResult result = repo.Revert(repo.Head.Tip.Parents.First(), Constants.Signature, options); + RevertResult result = repo.Revert(repo.Head.Tip.Parents.First(), Constants.Signature, options); Assert.Equal(RevertStatus.Conflicts, result.Status); // Verify there is a conflict. @@ -288,7 +288,7 @@ public void RevertFindsRenames(bool? findRenames) RevertResult result = repo.Revert(commitToRevert, Constants.Signature, options); Assert.NotNull(result); - if(!findRenames.HasValue || + if (!findRenames.HasValue || findRenames.Value == true) { Assert.Equal(RevertStatus.Reverted, result.Status); @@ -340,7 +340,7 @@ public void CanRevertMergeCommit(int mainline, string expectedId) Assert.Equal(RevertStatus.Reverted, result.Status); Assert.Equal(result.Commit.Sha, expectedId); - if(mainline == 1) + if (mainline == 1) { // In this case, we expect "d_renamed.txt" to be reverted (deleted), // and a.txt to match the tip of the "revert" branch. @@ -351,7 +351,7 @@ public void CanRevertMergeCommit(int mainline, string expectedId) Assert.NotNull(commit); Assert.Equal(commit["a.txt"].Target.Id, repo.Index["a.txt"].Id); } - else if(mainline == 2) + else if (mainline == 2) { // In this case, we expect "d_renamed.txt" to be preset, // and a.txt to match the tip of the master branch. @@ -421,7 +421,7 @@ public void RevertWithNothingToRevert(bool commitOnSuccess) new RevertOptions() { CommitOnSuccess = commitOnSuccess }); Assert.NotNull(result); - Assert.Equal(null, result.Commit); + Assert.Null(result.Commit); Assert.Equal(RevertStatus.NothingToRevert, result.Status); if (commitOnSuccess) @@ -488,7 +488,7 @@ public void RevertWithNothingToRevertInObjectDatabaseSucceeds() [Fact] public void RevertWithConflictReportsConflict() - { + { // The branch name to perform the revert on, // and the file whose contents we expect to be reverted. const string revertBranchName = "refs/heads/revert"; diff --git a/LibGit2Sharp.Tests/SetErrorFixture.cs b/LibGit2Sharp.Tests/SetErrorFixture.cs index b10a54108..35ee15d26 100644 --- a/LibGit2Sharp.Tests/SetErrorFixture.cs +++ b/LibGit2Sharp.Tests/SetErrorFixture.cs @@ -49,7 +49,11 @@ public void FormatAggregateException() Exception exceptionToThrow = new AggregateException(aggregateExceptionMessage, new Exception(innerExceptionMessage), new Exception(innerExceptionMessage2)); StringBuilder sb = new StringBuilder(); +#if NETFRAMEWORK sb.AppendLine(aggregateExceptionMessage); +#else + sb.AppendLine($"{aggregateExceptionMessage} ({innerExceptionMessage}) ({innerExceptionMessage2})"); +#endif sb.AppendLine(); AppendIndentedLine(sb, expectedAggregateExceptionsHeaderText, 0); diff --git a/LibGit2Sharp.Tests/StageFixture.cs b/LibGit2Sharp.Tests/StageFixture.cs index 5b4538670..c087aa7be 100644 --- a/LibGit2Sharp.Tests/StageFixture.cs +++ b/LibGit2Sharp.Tests/StageFixture.cs @@ -240,7 +240,7 @@ public void CanStageANewFileWithARelativePathContainingNativeDirectorySeparatorC const string posixifiedPath = "Project/a_file.txt"; Assert.NotNull(repo.Index[posixifiedPath]); - Assert.Equal(file, repo.Index[posixifiedPath].Path); + Assert.Equal(posixifiedPath, repo.Index[posixifiedPath].Path); } } @@ -265,7 +265,7 @@ public void StagingFileWithBadParamsThrows() { Assert.Throws(() => Commands.Stage(repo, string.Empty)); Assert.Throws(() => Commands.Stage(repo, (string)null)); - Assert.Throws(() => Commands.Stage(repo, new string[] { })); + Assert.Throws(() => Commands.Stage(repo, Array.Empty())); Assert.Throws(() => Commands.Stage(repo, new string[] { null })); } } @@ -362,7 +362,7 @@ public void IgnoredFilesAreOnlyStagedIfTheyreInTheRepo(string filename, FileStat using (var repo = new Repository(path)) { File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"), - String.Format("{0}\n", filename)); + string.Format("{0}\n", filename)); Commands.Stage(repo, filename); Assert.Equal(expected, repo.RetrieveStatus(filename)); @@ -384,7 +384,7 @@ public void CanStageConflictedIgnoredFiles(string filename, FileStatus expected) using (var repo = new Repository(path)) { File.WriteAllText(Path.Combine(repo.Info.WorkingDirectory, ".gitignore"), - String.Format("{0}\n", filename)); + string.Format("{0}\n", filename)); Commands.Stage(repo, filename); Assert.Equal(expected, repo.RetrieveStatus(filename)); diff --git a/LibGit2Sharp.Tests/StashFixture.cs b/LibGit2Sharp.Tests/StashFixture.cs index 6d6d5565d..27a535e8e 100644 --- a/LibGit2Sharp.Tests/StashFixture.cs +++ b/LibGit2Sharp.Tests/StashFixture.cs @@ -69,7 +69,7 @@ public void CanAddAndRemoveStash() //Remove one stash repo.Stashes.Remove(0); - Assert.Equal(1, repo.Stashes.Count()); + Assert.Single(repo.Stashes); Stash newTopStash = repo.Stashes.First(); Assert.Equal("stash@{0}", newTopStash.CanonicalName); Assert.Equal(stash.WorkTree.Sha, newTopStash.WorkTree.Sha); @@ -220,7 +220,7 @@ public void CanStashAndApplyWithOptions() Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Apply(0)); Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); - Assert.Equal(1, repo.Stashes.Count()); + Assert.Single(repo.Stashes); Commands.Stage(repo, filename); @@ -245,7 +245,7 @@ public void CanStashAndPop() { var stasher = Constants.Signature; - Assert.Equal(0, repo.Stashes.Count()); + Assert.Empty(repo.Stashes); const string filename = "staged_file_path.txt"; const string contents = "I'm staged"; @@ -253,10 +253,10 @@ public void CanStashAndPop() Commands.Stage(repo, filename); repo.Stashes.Add(stasher, "This stash with default options"); - Assert.Equal(1, repo.Stashes.Count()); + Assert.Single(repo.Stashes); Assert.Equal(StashApplyStatus.Applied, repo.Stashes.Pop(0)); - Assert.Equal(0, repo.Stashes.Count()); + Assert.Empty(repo.Stashes); Assert.Equal(FileStatus.NewInIndex, repo.RetrieveStatus(filename)); Assert.Equal(contents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename))); @@ -287,10 +287,10 @@ public void StashFailsWithUncommittedChangesIntheIndex() Touch(repo.Info.WorkingDirectory, filename2, newContents); Assert.Equal(StashApplyStatus.UncommittedChanges, repo.Stashes.Pop(0, new StashApplyOptions - { - ApplyModifiers = StashApplyModifiers.ReinstateIndex, - })); - Assert.Equal(1, repo.Stashes.Count()); + { + ApplyModifiers = StashApplyModifiers.ReinstateIndex, + })); + Assert.Single(repo.Stashes); Assert.Equal(newContents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename))); Assert.Equal(newContents, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, filename2))); } @@ -317,21 +317,21 @@ public void StashCallsTheCallback() called = false; repo.Stashes.Apply(0, new StashApplyOptions - { - ProgressHandler = (progress) => { called = true; return true; } - }); + { + ProgressHandler = (progress) => { called = true; return true; } + }); - Assert.Equal(true, called); + Assert.True(called); repo.Reset(ResetMode.Hard); called = false; repo.Stashes.Pop(0, new StashApplyOptions - { - ProgressHandler = (progress) => { called = true; return true; } - }); + { + ProgressHandler = (progress) => { called = true; return true; } + }); - Assert.Equal(true, called); + Assert.True(called); } } diff --git a/LibGit2Sharp.Tests/StatusFixture.cs b/LibGit2Sharp.Tests/StatusFixture.cs index 1fb0f889a..698639aa4 100644 --- a/LibGit2Sharp.Tests/StatusFixture.cs +++ b/LibGit2Sharp.Tests/StatusFixture.cs @@ -255,20 +255,20 @@ public void CanRetrieveTheStatusOfANewRepository(bool includeUnaltered) { RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = includeUnaltered }); Assert.NotNull(status); - Assert.Equal(0, status.Count()); + Assert.Empty(status); Assert.False(status.IsDirty); - Assert.Equal(0, status.Untracked.Count()); - Assert.Equal(0, status.Modified.Count()); - Assert.Equal(0, status.Missing.Count()); - Assert.Equal(0, status.Added.Count()); - Assert.Equal(0, status.Staged.Count()); - Assert.Equal(0, status.Removed.Count()); + Assert.Empty(status.Untracked); + Assert.Empty(status.Modified); + Assert.Empty(status.Missing); + Assert.Empty(status.Added); + Assert.Empty(status.Staged); + Assert.Empty(status.Removed); } } [Fact] - public void RetrievingTheStatusOfARepositoryReturnNativeFilePaths() + public void RetrievingTheStatusOfARepositoryReturnsGitPaths() { // Build relative path string relFilePath = Path.Combine("directory", "Testfile.txt"); @@ -286,10 +286,10 @@ public void RetrievingTheStatusOfARepositoryReturnNativeFilePaths() // Get the repository status RepositoryStatus repoStatus = repo.RetrieveStatus(); - Assert.Equal(1, repoStatus.Count()); + Assert.Single(repoStatus); StatusEntry statusEntry = repoStatus.Single(); - Assert.Equal(relFilePath, statusEntry.FilePath); + Assert.Equal(relFilePath.Replace('\\', '/'), statusEntry.FilePath); Assert.Equal(statusEntry.FilePath, repoStatus.Added.Select(s => s.FilePath).Single()); } @@ -310,7 +310,7 @@ public void RetrievingTheStatusOfAnEmptyRepositoryHonorsTheGitIgnoreDirectives() Touch(repo.Info.WorkingDirectory, ".gitignore", "*.txt" + Environment.NewLine); - RepositoryStatus newStatus = repo.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }); Assert.Equal(".gitignore", newStatus.Untracked.Select(s => s.FilePath).Single()); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); @@ -383,6 +383,7 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectives() RepositoryStatus status = repo.RetrieveStatus(); + relativePath = relativePath.Replace('\\', '/'); Assert.Equal(new[] { relativePath, "new_untracked_file.txt" }, status.Untracked.Select(s => s.FilePath)); Touch(repo.Info.WorkingDirectory, ".gitignore", "*.txt" + Environment.NewLine); @@ -422,7 +423,7 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectives() * # new_untracked_file.txt */ - RepositoryStatus newStatus = repo.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }); Assert.Equal(".gitignore", newStatus.Untracked.Select(s => s.FilePath).Single()); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); @@ -435,12 +436,12 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectives() [InlineData(false, FileStatus.DeletedFromWorkdir, FileStatus.NewInWorkdir)] public void RetrievingTheStatusOfAFilePathHonorsTheIgnoreCaseConfigurationSetting( bool shouldIgnoreCase, - FileStatus expectedlowerCasedFileStatus, - FileStatus expectedCamelCasedFileStatus + FileStatus expectedLowercaseFileStatus, + FileStatus expectedUppercaseFileStatus ) { - string lowerCasedPath; - const string lowercasedFilename = "plop"; + string lowercasePath; + const string lowercaseFileName = "plop"; string repoPath = InitNewRepository(); @@ -448,24 +449,28 @@ FileStatus expectedCamelCasedFileStatus { repo.Config.Set("core.ignorecase", shouldIgnoreCase); - lowerCasedPath = Touch(repo.Info.WorkingDirectory, lowercasedFilename); + lowercasePath = Touch(repo.Info.WorkingDirectory, lowercaseFileName); - Commands.Stage(repo, lowercasedFilename); + Commands.Stage(repo, lowercaseFileName); repo.Commit("initial", Constants.Signature, Constants.Signature); } using (var repo = new Repository(repoPath)) { - const string upercasedFilename = "Plop"; + const string uppercaseFileName = "PLOP"; - string camelCasedPath = Path.Combine(repo.Info.WorkingDirectory, upercasedFilename); - File.Move(lowerCasedPath, camelCasedPath); + string uppercasePath = Path.Combine(repo.Info.WorkingDirectory, uppercaseFileName); - Assert.Equal(expectedlowerCasedFileStatus, repo.RetrieveStatus(lowercasedFilename)); - Assert.Equal(expectedCamelCasedFileStatus, repo.RetrieveStatus(upercasedFilename)); + //Workaround for problem with .NET Core 1.x on macOS where going directly from lowercasePath to uppercasePath fails + //https://github.com/dotnet/corefx/issues/18521 + File.Move(lowercasePath, "__tmp__"); + File.Move("__tmp__", uppercasePath); - AssertStatus(shouldIgnoreCase, expectedlowerCasedFileStatus, repo, camelCasedPath.ToLowerInvariant()); - AssertStatus(shouldIgnoreCase, expectedCamelCasedFileStatus, repo, camelCasedPath.ToUpperInvariant()); + Assert.Equal(expectedLowercaseFileStatus, repo.RetrieveStatus(lowercaseFileName)); + Assert.Equal(expectedUppercaseFileStatus, repo.RetrieveStatus(uppercaseFileName)); + + AssertStatus(shouldIgnoreCase, expectedLowercaseFileStatus, repo, uppercasePath.ToLowerInvariant()); + AssertStatus(shouldIgnoreCase, expectedUppercaseFileStatus, repo, uppercasePath.ToUpperInvariant()); } } @@ -484,8 +489,6 @@ private static void AssertStatus(bool shouldIgnoreCase, FileStatus expectedFileS [Fact] public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectivesThroughoutDirectories() { - char dirSep = Path.DirectorySeparatorChar; - string path = SandboxStandardTestRepo(); using (var repo = new Repository(path)) { @@ -498,8 +501,8 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectivesThroug Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/look-ma.txt")); Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/what-about-me.txt")); - RepositoryStatus newStatus = repo.RetrieveStatus(); - Assert.Equal(new[] { "bin" + dirSep }, newStatus.Ignored.Select(s => s.FilePath)); + RepositoryStatus newStatus = repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }); + Assert.Equal(new[] { "bin/" }, newStatus.Ignored.Select(s => s.FilePath)); var sb = new StringBuilder(); sb.AppendLine("bin/*"); @@ -509,10 +512,10 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectivesThroug Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/look-ma.txt")); Assert.Equal(FileStatus.NewInWorkdir, repo.RetrieveStatus("bin/what-about-me.txt")); - newStatus = repo.RetrieveStatus(); + newStatus = repo.RetrieveStatus(new StatusOptions { IncludeIgnored = true }); - Assert.Equal(new[] { "bin" + dirSep + "look-ma.txt" }, newStatus.Ignored.Select(s => s.FilePath)); - Assert.True(newStatus.Untracked.Select(s => s.FilePath).Contains("bin" + dirSep + "what-about-me.txt")); + Assert.Equal(new[] { "bin/look-ma.txt" }, newStatus.Ignored.Select(s => s.FilePath)); + Assert.Contains("bin/what-about-me.txt", newStatus.Untracked.Select(s => s.FilePath)); } } @@ -568,8 +571,8 @@ public void CanRetrieveTheStatusOfARelativeWorkingDirectory() Assert.Equal(2, status.Untracked.Count()); status = repo.RetrieveStatus(new StatusOptions() { PathSpec = new[] { "just_a_dir/another_dir" } }); - Assert.Equal(1, status.Count()); - Assert.Equal(1, status.Untracked.Count()); + Assert.Single(status); + Assert.Single(status.Untracked); } } @@ -626,7 +629,7 @@ public void CanIncludeStatusOfUnalteredFiles() var path = SandboxStandardTestRepo(); string[] unalteredPaths = { "1.txt", - "1" + Path.DirectorySeparatorChar + "branch_file.txt", + "1/branch_file.txt", "branch_file.txt", "new.txt", "README", @@ -637,7 +640,7 @@ public void CanIncludeStatusOfUnalteredFiles() RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = true }); Assert.Equal(unalteredPaths.Length, status.Unaltered.Count()); - Assert.Equal(unalteredPaths, status.Unaltered.OrderBy(s => s.FilePath).Select(s => s.FilePath).ToArray()); + Assert.Equal(unalteredPaths, status.Unaltered.OrderBy(s => s.FilePath, StringComparer.OrdinalIgnoreCase).Select(s => s.FilePath).ToArray()); } } @@ -653,7 +656,7 @@ public void UnalteredFilesDontMarkIndexAsDirty() RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { IncludeUnaltered = true }); - Assert.Equal(false, status.IsDirty); + Assert.False(status.IsDirty); Assert.Equal(9, status.Count()); } } diff --git a/LibGit2Sharp.Tests/SubmoduleFixture.cs b/LibGit2Sharp.Tests/SubmoduleFixture.cs index a526ef614..2d7f04e6d 100644 --- a/LibGit2Sharp.Tests/SubmoduleFixture.cs +++ b/LibGit2Sharp.Tests/SubmoduleFixture.cs @@ -3,7 +3,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -133,12 +132,10 @@ public void CanEnumerateRepositorySubmodules() } [Theory] - [InlineData("sm_changed_head", false)] - [InlineData("sm_changed_head", true)] - public void CanStageChangeInSubmoduleViaIndexStage(string submodulePath, bool appendPathSeparator) + [InlineData("sm_changed_head")] + [InlineData("sm_changed_head/")] + public void CanStageChangeInSubmoduleViaIndexStage(string submodulePath) { - submodulePath += appendPathSeparator ? Path.DirectorySeparatorChar : default(char?); - var path = SandboxSubmoduleTestRepo(); using (var repo = new Repository(path)) { @@ -156,12 +153,10 @@ public void CanStageChangeInSubmoduleViaIndexStage(string submodulePath, bool ap } [Theory] - [InlineData("sm_changed_head", false)] - [InlineData("sm_changed_head", true)] - public void CanStageChangeInSubmoduleViaIndexStageWithOtherPaths(string submodulePath, bool appendPathSeparator) + [InlineData("sm_changed_head")] + [InlineData("sm_changed_head/")] + public void CanStageChangeInSubmoduleViaIndexStageWithOtherPaths(string submodulePath) { - submodulePath += appendPathSeparator ? Path.DirectorySeparatorChar : default(char?); - var path = SandboxSubmoduleTestRepo(); using (var repo = new Repository(path)) { @@ -244,9 +239,10 @@ public void CanUpdateSubmodule() OnCheckoutProgress = (x, y, z) => checkoutProgressCalled = true, OnCheckoutNotify = (x, y) => { checkoutNotifyCalled = true; return true; }, CheckoutNotifyFlags = CheckoutNotifyFlags.Updated, - OnUpdateTips = (x, y, z) => { updateTipsCalled = true; return true; }, }; + options.FetchOptions.OnUpdateTips = (x, y, z) => { updateTipsCalled = true; return true; }; + repo.Submodules.Init(submodule.Name, false); repo.Submodules.Update(submodule.Name, options); diff --git a/LibGit2Sharp.Tests/TagFixture.cs b/LibGit2Sharp.Tests/TagFixture.cs index e6897935e..9f125806c 100644 --- a/LibGit2Sharp.Tests/TagFixture.cs +++ b/LibGit2Sharp.Tests/TagFixture.cs @@ -13,7 +13,7 @@ public class TagFixture : BaseFixture private readonly string[] expectedTags = new[] { "e90810b", "lw", "point_to_blob", "tag_without_tagger", "test", }; private static readonly Signature signatureTim = new Signature("Tim Clem", "timothy.clem@gmail.com", TruncateSubSeconds(DateTimeOffset.UtcNow)); - private static readonly Signature signatureNtk = new Signature("nulltoken", "emeric.fermas@gmail.com", Epoch.ToDateTimeOffset(1300557894, 60)); + private static readonly Signature signatureNtk = new Signature("nulltoken", "emeric.fermas@gmail.com", DateTimeOffset.FromUnixTimeSeconds(1300557894).ToOffset(TimeSpan.FromMinutes(60))); private const string tagTestSha = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1"; private const string commitE90810BSha = "e90810b8df3e80c413d903f631643c716887138d"; private const string tagE90810BSha = "7b4384978d2493e851f9cca7858815fac9b10980"; @@ -164,7 +164,7 @@ public void CanAddAnAnnotatedTagFromObject() { GitObject obj = repo.Lookup(tagTestSha); - Tag newTag = repo.Tags.Add("unit_test",obj, signatureTim, "a new tag"); + Tag newTag = repo.Tags.Add("unit_test", obj, signatureTim, "a new tag"); Assert.NotNull(newTag); Assert.True(newTag.IsAnnotated); Assert.Equal(tagTestSha, newTag.Target.Sha); @@ -606,12 +606,12 @@ public void RemovingATagDecreasesTheTagsCount() const string tagName = "e90810b"; List tags = repo.Tags.Select(r => r.FriendlyName).ToList(); - Assert.True(tags.Contains(tagName)); + Assert.Contains(tagName, tags); repo.Tags.Remove(tagName); List tags2 = repo.Tags.Select(r => r.FriendlyName).ToList(); - Assert.False(tags2.Contains(tagName)); + Assert.DoesNotContain(tagName, tags2); Assert.Equal(tags.Count - 1, tags2.Count); } @@ -661,7 +661,7 @@ public void CanListAllTagsInAEmptyRepository() using (var repo = new Repository(repoPath)) { Assert.True(repo.Info.IsHeadUnborn); - Assert.Equal(0, repo.Tags.Count()); + Assert.Empty(repo.Tags); } } diff --git a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs index 44aafabb4..e9429d562 100644 --- a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs +++ b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using LibGit2Sharp.Core; using Xunit; namespace LibGit2Sharp.Tests.TestHelpers @@ -21,7 +20,7 @@ public BaseFixture() BuildFakeConfigs(this); #if LEAKS_IDENTIFYING - LeaksContainer.Clear(); + Core.LeaksContainer.Clear(); #endif } @@ -43,6 +42,8 @@ static BaseFixture() private static string SubmoduleTargetTestRepoWorkingDirPath { get; set; } private static string AssumeUnchangedRepoWorkingDirPath { get; set; } public static string SubmoduleSmallTestRepoWorkingDirPath { get; set; } + public static string WorktreeTestRepoWorkingDirPath { get; private set; } + public static string WorktreeTestRepoWorktreesDirPath { get; private set; } public static string PackBuilderTestRepoPath { get; private set; } public static DirectoryInfo ResourcesDirectory { get; private set; } @@ -51,33 +52,43 @@ static BaseFixture() protected static DateTimeOffset TruncateSubSeconds(DateTimeOffset dto) { - int seconds = dto.ToSecondsSinceEpoch(); - return Epoch.ToDateTimeOffset(seconds, (int)dto.Offset.TotalMinutes); + var seconds = dto.ToUnixTimeSeconds(); + return DateTimeOffset.FromUnixTimeSeconds(seconds).ToOffset(dto.Offset); } private static void SetUpTestEnvironment() { IsFileSystemCaseSensitive = IsFileSystemCaseSensitiveInternal(); - string initialAssemblyParentFolder = Directory.GetParent(new Uri(typeof(BaseFixture).Assembly.EscapedCodeBase).LocalPath).FullName; + var resourcesPath = Environment.GetEnvironmentVariable("LIBGIT2SHARP_RESOURCES"); - const string sourceRelativePath = @"../../Resources"; - ResourcesDirectory = new DirectoryInfo(Path.Combine(initialAssemblyParentFolder, sourceRelativePath)); + if (resourcesPath == null) + { +#if NETFRAMEWORK + resourcesPath = Path.Combine(Directory.GetParent(new Uri(typeof(BaseFixture).GetTypeInfo().Assembly.CodeBase).LocalPath).FullName, "Resources"); +#else + resourcesPath = Path.Combine(Directory.GetParent(typeof(BaseFixture).GetTypeInfo().Assembly.Location).FullName, "Resources"); +#endif + } + + ResourcesDirectory = new DirectoryInfo(resourcesPath); // Setup standard paths to our test repositories - BareTestRepoPath = Path.Combine(sourceRelativePath, "testrepo.git"); - StandardTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "testrepo_wd"); + BareTestRepoPath = Path.Combine(ResourcesDirectory.FullName, "testrepo.git"); + StandardTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "testrepo_wd"); StandardTestRepoPath = Path.Combine(StandardTestRepoWorkingDirPath, "dot_git"); - ShallowTestRepoPath = Path.Combine(sourceRelativePath, "shallow.git"); - MergedTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "mergedrepo_wd"); - MergeRenamesTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "mergerenames_wd"); - MergeTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "merge_testrepo_wd"); - RevertTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "revert_testrepo_wd"); - SubmoduleTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "submodule_wd"); - SubmoduleTargetTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "submodule_target_wd"); - AssumeUnchangedRepoWorkingDirPath = Path.Combine(sourceRelativePath, "assume_unchanged_wd"); - SubmoduleSmallTestRepoWorkingDirPath = Path.Combine(sourceRelativePath, "submodule_small_wd"); - PackBuilderTestRepoPath = Path.Combine(sourceRelativePath, "packbuilder_testrepo_wd"); + ShallowTestRepoPath = Path.Combine(ResourcesDirectory.FullName, "shallow.git"); + MergedTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "mergedrepo_wd"); + MergeRenamesTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "mergerenames_wd"); + MergeTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "merge_testrepo_wd"); + RevertTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "revert_testrepo_wd"); + SubmoduleTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "submodule_wd"); + SubmoduleTargetTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "submodule_target_wd"); + AssumeUnchangedRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "assume_unchanged_wd"); + SubmoduleSmallTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "submodule_small_wd"); + PackBuilderTestRepoPath = Path.Combine(ResourcesDirectory.FullName, "packbuilder_testrepo_wd"); + WorktreeTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "worktree", "testrepo_wd"); + WorktreeTestRepoWorktreesDirPath = Path.Combine(ResourcesDirectory.FullName, "worktree", "worktrees"); CleanupTestReposOlderThan(TimeSpan.FromMinutes(15)); } @@ -213,6 +224,11 @@ public string SandboxSubmoduleSmallTestRepo() return path; } + public string SandboxWorktreeTestRepo() + { + return Sandbox(WorktreeTestRepoWorkingDirPath, WorktreeTestRepoWorktreesDirPath); + } + protected string SandboxPackBuilderTestRepo() { return Sandbox(PackBuilderTestRepoPath); @@ -260,11 +276,11 @@ public virtual void Dispose() GC.Collect(); GC.WaitForPendingFinalizers(); - if (LeaksContainer.TypeNames.Any()) + if (Core.LeaksContainer.TypeNames.Any()) { - Assert.False(true, string.Format("Some handles of the following types haven't been properly released: {0}.{1}" + Assert.Fail(string.Format("Some handles of the following types haven't been properly released: {0}.{1}" + "In order to get some help fixing those leaks, uncomment the define LEAKS_TRACKING in Libgit2Object.cs{1}" - + "and run the tests locally.", string.Join(", ", LeaksContainer.TypeNames), Environment.NewLine)); + + "and run the tests locally.", string.Join(", ", Core.LeaksContainer.TypeNames), Environment.NewLine)); } #endif } @@ -383,10 +399,19 @@ protected static string Touch(string parent, string file, string content = null, string dir = Path.GetDirectoryName(filePath); Debug.Assert(dir != null); + var newFile = !File.Exists(filePath); + Directory.CreateDirectory(dir); File.WriteAllText(filePath, content ?? string.Empty, encoding ?? Encoding.ASCII); + //Workaround for .NET Core 1.x behavior where all newly created files have execute permissions set. + //https://github.com/dotnet/corefx/issues/13342 + if (Constants.IsRunningOnUnix && newFile) + { + RemoveExecutePermissions(filePath, newFile); + } + return filePath; } @@ -398,6 +423,8 @@ protected static string Touch(string parent, string file, Stream stream) string dir = Path.GetDirectoryName(filePath); Debug.Assert(dir != null); + var newFile = !File.Exists(filePath); + Directory.CreateDirectory(dir); using (var fs = File.Open(filePath, FileMode.Create)) @@ -406,9 +433,22 @@ protected static string Touch(string parent, string file, Stream stream) fs.Flush(); } + //Work around .NET Core 1.x behavior where all newly created files have execute permissions set. + //https://github.com/dotnet/corefx/issues/13342 + if (Constants.IsRunningOnUnix && newFile) + { + RemoveExecutePermissions(filePath, newFile); + } + return filePath; } + private static void RemoveExecutePermissions(string filePath, bool newFile) + { + var process = Process.Start("chmod", $"644 {filePath}"); + process.WaitForExit(); + } + protected string Expected(string filename) { return File.ReadAllText(Path.Combine(ResourcesDirectory.FullName, "expected/" + filename)); @@ -430,7 +470,13 @@ protected static void AssertRefLogEntry(IRepository repo, string canonicalName, Assert.Equal(@from ?? ObjectId.Zero, reflogEntry.From); Assert.Equal(committer.Email, reflogEntry.Committer.Email); - Assert.InRange(reflogEntry.Committer.When, before, DateTimeOffset.Now); + + // When verifying the timestamp range, give a little more room on the range. + // Git or file system datetime truncation seems to cause these stamps to jump up to a second earlier + // than we expect. See https://github.com/libgit2/libgit2sharp/issues/1764 + var low = before - TimeSpan.FromSeconds(1); + var high = DateTimeOffset.Now.TruncateMilliseconds() + TimeSpan.FromSeconds(1); + Assert.InRange(reflogEntry.Committer.When, low, high); } protected static void EnableRefLog(IRepository repository, bool enable = true) diff --git a/LibGit2Sharp.Tests/TestHelpers/Constants.cs b/LibGit2Sharp.Tests/TestHelpers/Constants.cs index 334b61dc1..d8c14dbca 100644 --- a/LibGit2Sharp.Tests/TestHelpers/Constants.cs +++ b/LibGit2Sharp.Tests/TestHelpers/Constants.cs @@ -55,27 +55,25 @@ public static string BuildPath() { string tempPath = null; - if (IsRunningOnUnix) + const string LibGit2TestPath = "LibGit2TestPath"; + + // We're running on .Net/Windows + if (Environment.GetEnvironmentVariables().Contains(LibGit2TestPath)) { - // We're running on Mono/*nix. Let's unwrap the path - tempPath = UnwrapUnixTempPath(); + Trace.TraceInformation("{0} environment variable detected", LibGit2TestPath); + tempPath = Environment.GetEnvironmentVariables()[LibGit2TestPath] as string; } - else + + if (string.IsNullOrWhiteSpace(tempPath) || !Directory.Exists(tempPath)) + { + Trace.TraceInformation("Using default test path value"); + tempPath = Path.GetTempPath(); + } + + //workaround macOS symlinking its temp folder + if (tempPath.StartsWith("/var")) { - const string LibGit2TestPath = "LibGit2TestPath"; - - // We're running on .Net/Windows - if (Environment.GetEnvironmentVariables().Contains(LibGit2TestPath)) - { - Trace.TraceInformation("{0} environment variable detected", LibGit2TestPath); - tempPath = Environment.GetEnvironmentVariables()[LibGit2TestPath] as String; - } - - if (String.IsNullOrWhiteSpace(tempPath) || !Directory.Exists(tempPath)) - { - Trace.TraceInformation("Using default test path value"); - tempPath = Path.GetTempPath(); - } + tempPath = "/private" + tempPath; } string testWorkingDirectory = Path.Combine(tempPath, "LibGit2Sharp-TestRepos"); @@ -83,16 +81,6 @@ public static string BuildPath() return testWorkingDirectory; } - private static string UnwrapUnixTempPath() - { - var type = Type.GetType("Mono.Unix.UnixPath, Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"); - - return (string)type.InvokeMember("GetCompleteRealPath", - BindingFlags.Static | BindingFlags.FlattenHierarchy | - BindingFlags.InvokeMethod | BindingFlags.Public, - null, type, new object[] { Path.GetTempPath() }); - } - // To help with creating secure strings to test with. internal static SecureString StringToSecureString(string str) { diff --git a/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs b/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs index d1ff4024a..14b5b06f9 100644 --- a/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs +++ b/LibGit2Sharp.Tests/TestHelpers/DateTimeOffsetExtensions.cs @@ -4,11 +4,6 @@ namespace LibGit2Sharp.Tests.TestHelpers { public static class DateTimeOffsetExtensions { - public static DateTimeOffset TruncateMilliseconds(this DateTimeOffset dto) - { - // From http://stackoverflow.com/a/1005222/335418 - - return dto.AddTicks( - (dto.Ticks % TimeSpan.TicksPerSecond)); - } + public static DateTimeOffset TruncateMilliseconds(this DateTimeOffset dto) => new DateTimeOffset(dto.Year, dto.Month, dto.Day, dto.Hour, dto.Minute, dto.Second, dto.Offset); } } diff --git a/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs b/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs index 66c1d594a..636d4f198 100644 --- a/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs +++ b/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection; using System.Threading; namespace LibGit2Sharp.Tests.TestHelpers @@ -78,7 +79,7 @@ private static void DeleteDirectory(string directoryPath, int maxAttempts, int i { var caughtExceptionType = ex.GetType(); - if (!whitelist.Any(knownExceptionType => knownExceptionType.IsAssignableFrom(caughtExceptionType))) + if (!whitelist.Any(knownExceptionType => knownExceptionType.GetTypeInfo().IsAssignableFrom(caughtExceptionType))) { throw; } diff --git a/LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs b/LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs new file mode 100644 index 000000000..7c2855528 --- /dev/null +++ b/LibGit2Sharp.Tests/TestHelpers/ProcessHelper.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace LibGit2Sharp.Tests +{ + public static class ProcessHelper + { + public static (string, int) RunProcess(string fileName, string arguments, string workingDirectory = null) + { + var process = new Process + { + StartInfo = new ProcessStartInfo(fileName, arguments) + { + RedirectStandardError = true, + RedirectStandardOutput = true, + CreateNoWindow = true, + UseShellExecute = false, + WorkingDirectory = workingDirectory ?? string.Empty + } + }; + + var output = new StringBuilder(); + + process.OutputDataReceived += (_, e) => output.AppendLine(e.Data); + process.ErrorDataReceived += (_, e) => output.AppendLine(e.Data); + + process.Start(); + + process.WaitForExit(); + + return (output.ToString(), process.ExitCode); + } + } +} diff --git a/LibGit2Sharp.Tests/TreeDefinitionFixture.cs b/LibGit2Sharp.Tests/TreeDefinitionFixture.cs index 2a7aba07c..6c0c0a41a 100644 --- a/LibGit2Sharp.Tests/TreeDefinitionFixture.cs +++ b/LibGit2Sharp.Tests/TreeDefinitionFixture.cs @@ -67,10 +67,10 @@ public void RequestingAnEntryWithBadParamsThrows() [Theory] [InlineData("1/branch_file.txt", "100755", TreeEntryTargetType.Blob, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057")] - [InlineData("README", "100644", TreeEntryTargetType.Blob, "a8233120f6ad708f843d861ce2b7228ec4e3dec6")] - [InlineData("branch_file.txt", "100644", TreeEntryTargetType.Blob, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057")] - [InlineData("new.txt", "100644", TreeEntryTargetType.Blob, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd")] - [InlineData("1", "040000", TreeEntryTargetType.Tree, "7f76480d939dc401415927ea7ef25c676b8ddb8f")] + [InlineData("README", "100644", TreeEntryTargetType.Blob, "a8233120f6ad708f843d861ce2b7228ec4e3dec6")] + [InlineData("branch_file.txt", "100644", TreeEntryTargetType.Blob, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057")] + [InlineData("new.txt", "100644", TreeEntryTargetType.Blob, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd")] + [InlineData("1", "040000", TreeEntryTargetType.Tree, "7f76480d939dc401415927ea7ef25c676b8ddb8f")] public void CanRetrieveEntries(string path, string expectedAttributes, TreeEntryTargetType expectedType, string expectedSha) { string repoPath = SandboxBareTestRepo(); @@ -142,6 +142,17 @@ public void CanAddAnExistingGitLinkTreeEntryDefinition() } } + private const string StringOf40Chars = "0123456789012345678901234567890123456789"; + + /// + /// Used to verify that windows path limitation to 260 chars is not limiting the size of + /// the keys present in the object database. + /// + private const string StringOf600Chars = + StringOf40Chars + StringOf40Chars + StringOf40Chars + StringOf40Chars + StringOf40Chars + + StringOf40Chars + StringOf40Chars + StringOf40Chars + StringOf40Chars + StringOf40Chars + + StringOf40Chars + StringOf40Chars + StringOf40Chars + StringOf40Chars + StringOf40Chars; + [Theory] [InlineData("README", "README_TOO")] [InlineData("README", "1/README")] @@ -152,7 +163,10 @@ public void CanAddAnExistingGitLinkTreeEntryDefinition() [InlineData("1/branch_file.txt", "1/2/3/another_one.txt")] [InlineData("1", "2")] [InlineData("1", "2/3")] - public void CanAddAnExistingTreeEntry(string sourcePath, string targetPath) + [InlineData("1", "C:\\/10")] + [InlineData("1", " : * ? \" < > |")] + [InlineData("1", StringOf600Chars)] + public void CanAddAndRemoveAnExistingTreeEntry(string sourcePath, string targetPath) { string path = SandboxBareTestRepo(); using (var repo = new Repository(path)) @@ -168,6 +182,36 @@ public void CanAddAnExistingTreeEntry(string sourcePath, string targetPath) Assert.NotNull(fetched); Assert.Equal(te.Target.Id, fetched.TargetId); + + // Ensuring that the object database can handle uncommon paths. + var newTree = repo.ObjectDatabase.CreateTree(td); + Assert.Equal(newTree[targetPath].Target.Id, te.Target.Id); + + td.Remove(targetPath); + Assert.Null(td[targetPath]); + } + } + + [Theory] + [InlineData("C:\\")] + [InlineData(" : * ? \" \n < > |")] + [InlineData("a\\b")] + [InlineData("\\\\b\a")] + [InlineData("éàµ")] + [InlineData(StringOf600Chars)] + public void TreeNamesCanContainCharsForbiddenOnSomeOS(string targetName) + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + var pointedItem = repo.Head.Tip.Tree; + + var td = new TreeDefinition(); + td.Add(targetName, pointedItem); + + var newTree = repo.ObjectDatabase.CreateTree(td); + Assert.Equal(newTree[targetName].Target.Sha, pointedItem.Sha); + Assert.Equal(newTree[targetName].Name, targetName); } } @@ -222,6 +266,49 @@ public void CanAddAnExistingBlob(string blobSha, string targetPath) } } + [Theory] + [InlineData("a8233120f6ad708f843d861ce2b7228ec4e3dec6", "README_TOO")] + [InlineData("a8233120f6ad708f843d861ce2b7228ec4e3dec6", "1/README")] + [InlineData("45b983be36b73c0788dc9cbcb76cbb80fc7bb057", "1/another_one.txt")] + [InlineData("45b983be36b73c0788dc9cbcb76cbb80fc7bb057", "another_one.txt")] + public void CanAddBlobById(string blobSha, string targetPath) + { + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.Null(td[targetPath]); + + var objectId = new ObjectId(blobSha); + + td.Add(targetPath, objectId, Mode.NonExecutableFile); + + TreeEntryDefinition fetched = td[targetPath]; + Assert.NotNull(fetched); + + Assert.Equal(objectId, fetched.TargetId); + Assert.Equal(Mode.NonExecutableFile, fetched.Mode); + } + } + + [Fact] + public void CannotAddTreeById() + { + const string treeSha = "7f76480d939dc401415927ea7ef25c676b8ddb8f"; + const string targetPath = "1/2"; + + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree); + Assert.Null(td[targetPath]); + + var objectId = new ObjectId(treeSha); + + Assert.Throws(() => td.Add(targetPath, objectId, Mode.Directory)); + } + } + [Fact] public void CanAddAnExistingSubmodule() { @@ -500,5 +587,23 @@ public void CanAddAnExistingBlobEntryWithAnExistingTree() Assert.NotNull(td["1/branch_file.txt"]); } } + + [Fact] + public void CanRemoveADirectoryWithChildren() + { + const string blobSha = "a8233120f6ad708f843d861ce2b7228ec4e3dec6"; + string path = SandboxBareTestRepo(); + using (var repo = new Repository(path)) + { + TreeDefinition td = new TreeDefinition(); + var blob = repo.Lookup(blobSha); + td.Add("folder/subfolder/file1", blob, Mode.NonExecutableFile); + td.Add("folder/file1", blob, Mode.NonExecutableFile); + td.Remove("folder"); + Assert.Null(td["folder"]); + Tree t = repo.ObjectDatabase.CreateTree(td); + Assert.Null(t["folder"]); + } + } } } diff --git a/LibGit2Sharp.Tests/TreeFixture.cs b/LibGit2Sharp.Tests/TreeFixture.cs index f57f14063..a3a8d89eb 100644 --- a/LibGit2Sharp.Tests/TreeFixture.cs +++ b/LibGit2Sharp.Tests/TreeFixture.cs @@ -17,6 +17,7 @@ public void CanCompareTwoTreeEntries() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); TreeEntry treeEntry1 = tree["README"]; TreeEntry treeEntry2 = tree["README"]; Assert.Equal(treeEntry2, treeEntry1); @@ -31,6 +32,7 @@ public void CanConvertEntryToBlob() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); TreeEntry treeEntry = tree["README"]; var blob = treeEntry.Target as Blob; @@ -45,6 +47,7 @@ public void CanConvertEntryToTree() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); TreeEntry treeEntry = tree["1"]; var subtree = treeEntry.Target as Tree; @@ -59,6 +62,7 @@ public void CanEnumerateBlobs() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); IEnumerable blobs = tree .Where(e => e.TargetType == TreeEntryTargetType.Blob) @@ -76,13 +80,14 @@ public void CanEnumerateSubTrees() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); IEnumerable subTrees = tree .Where(e => e.TargetType == TreeEntryTargetType.Tree) .Select(e => e.Target) .Cast(); - Assert.Equal(1, subTrees.Count()); + Assert.Single(subTrees); } } @@ -93,6 +98,7 @@ public void CanEnumerateTreeEntries() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); Assert.Equal(tree.Count, tree.Count()); Assert.Equal(new[] { "1", "README", "branch_file.txt", "new.txt" }, tree.Select(te => te.Name).ToArray()); @@ -106,6 +112,7 @@ public void CanGetEntryByName() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); TreeEntry treeEntry = tree["README"]; Assert.Equal("a8233120f6ad708f843d861ce2b7228ec4e3dec6", treeEntry.Target.Sha); Assert.Equal("README", treeEntry.Name); @@ -119,6 +126,7 @@ public void GettingAnUknownTreeEntryReturnsNull() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); TreeEntry treeEntry = tree["I-do-not-exist"]; Assert.Null(treeEntry); } @@ -131,6 +139,7 @@ public void CanGetEntryCountFromTree() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); Assert.Equal(4, tree.Count); } } @@ -142,6 +151,7 @@ public void CanReadEntryAttributes() using (var repo = new Repository(path)) { var tree = repo.Lookup(sha); + Assert.False(tree.IsMissing); Assert.Equal(Mode.NonExecutableFile, tree["README"].Mode); } } @@ -154,6 +164,7 @@ public void CanReadTheTreeData() { var tree = repo.Lookup(sha); Assert.NotNull(tree); + Assert.False(tree.IsMissing); } } @@ -165,6 +176,20 @@ public void TreeDataIsPresent() { GitObject tree = repo.Lookup(sha); Assert.NotNull(tree); + Assert.False(tree.IsMissing); + } + } + + [Fact] + public void TreeUsesPosixStylePaths() + { + using (var repo = new Repository(BareTestRepoPath)) + { + /* From a commit tree */ + var commitTree = repo.Lookup("4c062a6").Tree; + Assert.False(commitTree.IsMissing); + Assert.NotNull(commitTree["1/branch_file.txt"]); + Assert.Null(commitTree["1\\branch_file.txt"]); } } @@ -176,11 +201,12 @@ public void CanRetrieveTreeEntryPath() { /* From a commit tree */ var commitTree = repo.Lookup("4c062a6").Tree; + Assert.False(commitTree.IsMissing); TreeEntry treeTreeEntry = commitTree["1"]; Assert.Equal("1", treeTreeEntry.Path); - string completePath = Path.Combine("1", "branch_file.txt"); + string completePath = "1/branch_file.txt"; TreeEntry blobTreeEntry = commitTree["1/branch_file.txt"]; Assert.Equal(completePath, blobTreeEntry.Path); @@ -189,6 +215,7 @@ public void CanRetrieveTreeEntryPath() // tree but exposes a complete path through its Path property var subTree = treeTreeEntry.Target as Tree; Assert.NotNull(subTree); + Assert.False(subTree.IsMissing); TreeEntry anInstance = subTree["branch_file.txt"]; Assert.NotEqual("branch_file.txt", anInstance.Path); @@ -227,6 +254,7 @@ public void CanParseSymlinkTreeEntries() .Add("A symlink", linkContent, Mode.SymbolicLink); Tree t = repo.ObjectDatabase.CreateTree(td); + Assert.False(t.IsMissing); var te = t["A symlink"]; @@ -236,5 +264,31 @@ public void CanParseSymlinkTreeEntries() Assert.Equal(linkContent, te.Target); } } + + [Fact] + public void CanTellIfATreeIsMissing() + { + var path = SandboxBareTestRepo(); + + // Manually delete the objects directory to simulate a partial clone + Directory.Delete(Path.Combine(path, "objects", "fd"), true); + + using (var repo = new Repository(path)) + { + // Look up for the commit that reference the tree which is now missing + var commit = repo.Lookup("4a202b346bb0fb0db7eff3cffeb3c70babbd2045"); + + Assert.True(commit.Tree.IsMissing); + Assert.Equal("fd093bff70906175335656e6ce6ae05783708765", commit.Tree.Sha); + Assert.Throws(() => commit.Tree.Count); + Assert.Throws(() => commit.Tree.Count()); + Assert.Throws(() => commit.Tree["README"]); + Assert.Throws(() => commit.Tree.ToArray()); + Assert.Throws(() => + { + foreach (var _ in commit.Tree) { } + }); + } + } } } diff --git a/LibGit2Sharp.Tests/UnstageFixture.cs b/LibGit2Sharp.Tests/UnstageFixture.cs index c4791d10d..1eeee0e72 100644 --- a/LibGit2Sharp.Tests/UnstageFixture.cs +++ b/LibGit2Sharp.Tests/UnstageFixture.cs @@ -167,8 +167,8 @@ public void CanUnstageUntrackedFileAgainstAnOrphanedHead() Commands.Unstage(repo, relativePath); RepositoryStatus status = repo.RetrieveStatus(); - Assert.Equal(0, status.Staged.Count()); - Assert.Equal(1, status.Untracked.Count()); + Assert.Empty(status.Staged); + Assert.Single(status.Untracked); Assert.Throws(() => Commands.Unstage(repo, "i-dont-exist", new ExplicitPathsOptions())); } @@ -251,7 +251,7 @@ public void UnstagingFileWithBadParamsThrows() { Assert.Throws(() => Commands.Unstage(repo, string.Empty)); Assert.Throws(() => Commands.Unstage(repo, (string)null)); - Assert.Throws(() => Commands.Unstage(repo, new string[] { })); + Assert.Throws(() => Commands.Unstage(repo, Array.Empty())); Assert.Throws(() => Commands.Unstage(repo, new string[] { null })); } } @@ -264,14 +264,14 @@ public void CanUnstageSourceOfARename() Commands.Move(repo, "branch_file.txt", "renamed_branch_file.txt"); RepositoryStatus oldStatus = repo.RetrieveStatus(); - Assert.Equal(1, oldStatus.RenamedInIndex.Count()); + Assert.Single(oldStatus.RenamedInIndex); Assert.Equal(FileStatus.Nonexistent, oldStatus["branch_file.txt"].State); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); Commands.Unstage(repo, new string[] { "branch_file.txt" }); RepositoryStatus newStatus = repo.RetrieveStatus(); - Assert.Equal(0, newStatus.RenamedInIndex.Count()); + Assert.Empty(newStatus.RenamedInIndex); Assert.Equal(FileStatus.DeletedFromWorkdir, newStatus["branch_file.txt"].State); Assert.Equal(FileStatus.NewInIndex, newStatus["renamed_branch_file.txt"].State); } @@ -285,13 +285,13 @@ public void CanUnstageTargetOfARename() Commands.Move(repo, "branch_file.txt", "renamed_branch_file.txt"); RepositoryStatus oldStatus = repo.RetrieveStatus(); - Assert.Equal(1, oldStatus.RenamedInIndex.Count()); + Assert.Single(oldStatus.RenamedInIndex); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); Commands.Unstage(repo, new string[] { "renamed_branch_file.txt" }); RepositoryStatus newStatus = repo.RetrieveStatus(); - Assert.Equal(0, newStatus.RenamedInIndex.Count()); + Assert.Empty(newStatus.RenamedInIndex); Assert.Equal(FileStatus.NewInWorkdir, newStatus["renamed_branch_file.txt"].State); Assert.Equal(FileStatus.DeletedFromIndex, newStatus["branch_file.txt"].State); } diff --git a/LibGit2Sharp.Tests/VisualStudio.Tests.targets b/LibGit2Sharp.Tests/VisualStudio.Tests.targets deleted file mode 100644 index 53e10341f..000000000 --- a/LibGit2Sharp.Tests/VisualStudio.Tests.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/LibGit2Sharp.Tests/WorktreeFixture.cs b/LibGit2Sharp.Tests/WorktreeFixture.cs new file mode 100644 index 000000000..224a99dbe --- /dev/null +++ b/LibGit2Sharp.Tests/WorktreeFixture.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class WorktreeFixture : BaseFixture + { + [Fact] + public void RetrievingWorktreeForRandomNameReturnsNull() + { + var path = SandboxWorktreeTestRepo(); + using (var repo = new Repository(path)) + { + var worktree = repo.Worktrees["random"]; + Assert.Null(worktree); + } + } + + [Fact] + public void RetrievingWorktreeForWorktreeNameReturnsWorktree() + { + var path = SandboxWorktreeTestRepo(); + using (var repo = new Repository(path)) + { + var worktree = repo.Worktrees["logo"]; + Assert.NotNull(worktree); + } + } + + [Fact] + public void CanEnumerateRepositoryWorktrees() + { + var expectedWorktrees = new[] + { + "i-do-numbers", + "logo", + }; + + var path = SandboxWorktreeTestRepo(); + using (var repo = new Repository(path)) + { + var worktrees = repo.Worktrees.OrderBy(w => w.Name, StringComparer.Ordinal); + + Assert.Equal(expectedWorktrees, worktrees.Select(w => w.Name).ToArray()); + } + } + + [Fact] + public void CanViewLockStatusForWorktrees() + { + var testpath = SandboxWorktreeTestRepo(); + var repoPath = testpath; + using (var repo = new Repository(repoPath)) + { + // locked + var worktreeLogo = repo.Worktrees["logo"]; + Assert.Equal("logo", worktreeLogo.Name); + Assert.True(worktreeLogo.IsLocked); + Assert.Equal("Test lock reason\n", worktreeLogo.LockReason); + + // not locked + var worktreeIDoNumbers = repo.Worktrees["i-do-numbers"]; + Assert.Equal("i-do-numbers", worktreeIDoNumbers.Name); + Assert.False(worktreeIDoNumbers.IsLocked); + Assert.Null(worktreeIDoNumbers.LockReason); + } + } + + [Fact] + public void CanUnlockWorktree() + { + var testpath = SandboxWorktreeTestRepo(); + var repoPath = testpath; + using (var repo = new Repository(repoPath)) + { + // locked + var worktreeLocked = repo.Worktrees["logo"]; + Assert.Equal("logo", worktreeLocked.Name); + Assert.True(worktreeLocked.IsLocked); + Assert.Equal("Test lock reason\n", worktreeLocked.LockReason); + + worktreeLocked.Unlock(); + + // unlocked + var worktreeUnlocked = repo.Worktrees["logo"]; + Assert.Equal("logo", worktreeLocked.Name); + Assert.False(worktreeUnlocked.IsLocked); + Assert.Null(worktreeUnlocked.LockReason); + } + } + + [Fact] + public void CanLockWorktree() + { + var testpath = SandboxWorktreeTestRepo(); + var repoPath = testpath; + using (var repo = new Repository(repoPath)) + { + // unlocked + var worktreeUnlocked = repo.Worktrees["i-do-numbers"]; + Assert.Equal("i-do-numbers", worktreeUnlocked.Name); + Assert.False(worktreeUnlocked.IsLocked); + Assert.Null(worktreeUnlocked.LockReason); + + worktreeUnlocked.Lock("add a lock"); + + // locked + var worktreeLocked = repo.Worktrees["i-do-numbers"]; + Assert.Equal("i-do-numbers", worktreeLocked.Name); + Assert.True(worktreeLocked.IsLocked); + Assert.Equal("add a lock", worktreeLocked.LockReason); + } + } + + [Fact] + public void CanGetRepositoryForWorktree() + { + var testpath = SandboxWorktreeTestRepo(); + var repoPath = testpath; + using (var repo = new Repository(repoPath)) + { + var worktree = repo.Worktrees["logo"]; + + Assert.Equal("logo", worktree.Name); + using (var repository = worktree.WorktreeRepository) + { + Assert.NotNull(repository); + } + } + } + + [Fact] + public void CanPruneUnlockedWorktree() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + // unlocked + var worktreeUnlocked = repo.Worktrees["i-do-numbers"]; + Assert.Equal("i-do-numbers", worktreeUnlocked.Name); + Assert.False(worktreeUnlocked.IsLocked); + + Assert.True(repo.Worktrees.Prune(worktreeUnlocked)); + + Assert.Single(repo.Worktrees); + } + } + + [Fact] + public void CanPruneDeletedWorktree() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + var repoPath2 = repo.Info.Path; + var repoWd = repo.Info.WorkingDirectory; + // unlocked + var worktreeUnlocked = repo.Worktrees["i-do-numbers"]; + Assert.Equal("i-do-numbers", worktreeUnlocked.Name); + Assert.False(worktreeUnlocked.IsLocked); + using (var wtRepo = worktreeUnlocked.WorktreeRepository) + { + var info = wtRepo.Info; + + Directory.Delete(info.WorkingDirectory, true); + } + + Assert.True(repo.Worktrees.Prune(worktreeUnlocked)); + + Assert.Single(repo.Worktrees); + } + } + + [Fact] + public void CanNotPruneLockedWorktree() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + // locked + var worktreeUnlocked = repo.Worktrees["logo"]; + Assert.Equal("logo", worktreeUnlocked.Name); + Assert.True(worktreeUnlocked.IsLocked); + + Assert.Throws(() => repo.Worktrees.Prune(worktreeUnlocked)); + } + } + + [Fact] + public void CanUnlockThenPruneLockedWorktree() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + // locked + var worktreeLocked = repo.Worktrees["logo"]; + Assert.Equal("logo", worktreeLocked.Name); + Assert.True(worktreeLocked.IsLocked); + + worktreeLocked.Unlock(); + + repo.Worktrees.Prune(worktreeLocked); + + Assert.Single(repo.Worktrees); + } + } + + [Fact] + public void CanForcePruneLockedWorktree() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + // locked + var worktreeLocked = repo.Worktrees["logo"]; + Assert.Equal("logo", worktreeLocked.Name); + Assert.True(worktreeLocked.IsLocked); + + repo.Worktrees.Prune(worktreeLocked, true); + + Assert.Single(repo.Worktrees); + } + } + + [Fact] + public void CanAddWorktree_WithUncommitedChanges() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + var name = "blah"; + var path = Path.Combine(repo.Info.WorkingDirectory, "..", "worktrees", name); + var worktree = repo.Worktrees.Add(name, path, false); + Assert.Equal(name, worktree.Name); + Assert.False(worktree.IsLocked); + + Assert.Equal(3, repo.Worktrees.Count()); + + // Check that branch contains same number of files and folders + Assert.True(repo.RetrieveStatus().IsDirty); + var filesInMain = GetFilesOfRepo(repoPath); + var filesInBranch = GetFilesOfRepo(path); + Assert.NotEqual(filesInMain, filesInBranch); + + repo.Reset(ResetMode.Hard); + repo.RemoveUntrackedFiles(); + + Assert.False(repo.RetrieveStatus().IsDirty); + filesInMain = GetFilesOfRepo(repoPath); + filesInBranch = GetFilesOfRepo(path); + Assert.Equal(filesInMain, filesInBranch); + } + } + + [Fact] + public void CanAddWorktree_WithCommitedChanges() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + // stage all changes + Commands.Stage(repo, "*"); + repo.Commit("Apply all changes", Constants.Signature, Constants.Signature); + + Assert.Equal(2, repo.Worktrees.Count()); + + var name = "blah"; + var path = Path.Combine(repo.Info.WorkingDirectory, "..", "worktrees", name); + var worktree = repo.Worktrees.Add(name, path, false); + Assert.Equal(name, worktree.Name); + Assert.False(worktree.IsLocked); + + Assert.Equal(3, repo.Worktrees.Count()); + + // Check that branch contains same number of files and folders + Assert.False(repo.RetrieveStatus().IsDirty); + var filesInMain = GetFilesOfRepo(repoPath); + var filesInBranch = GetFilesOfRepo(path); + + Assert.Equal(filesInMain, filesInBranch); + } + } + + [Fact] + public void CanAddLockedWorktree_WithUncommitedChanges() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + var name = "blah"; + var path = Path.Combine(repo.Info.WorkingDirectory, "..", "worktrees", name); + var worktree = repo.Worktrees.Add(name, path, true); + Assert.Equal(name, worktree.Name); + Assert.True(worktree.IsLocked); + + Assert.Equal(3, repo.Worktrees.Count()); + + // Check that branch contains same number of files and folders + Assert.True(repo.RetrieveStatus().IsDirty); + var filesInMain = GetFilesOfRepo(repoPath); + var filesInBranch = GetFilesOfRepo(path); + Assert.NotEqual(filesInMain, filesInBranch); + + repo.Reset(ResetMode.Hard); + repo.RemoveUntrackedFiles(); + + Assert.False(repo.RetrieveStatus().IsDirty); + filesInMain = GetFilesOfRepo(repoPath); + filesInBranch = GetFilesOfRepo(path); + Assert.Equal(filesInMain, filesInBranch); + } + } + + [Fact] + public void CanAddLockedWorktree_WithCommitedChanges() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + // stage all changes + Commands.Stage(repo, "*"); + repo.Commit("Apply all changes", Constants.Signature, Constants.Signature); + + Assert.Equal(2, repo.Worktrees.Count()); + + var name = "blah"; + var path = Path.Combine(repo.Info.WorkingDirectory, "..", "worktrees", name); + var worktree = repo.Worktrees.Add(name, path, true); + Assert.Equal(name, worktree.Name); + Assert.True(worktree.IsLocked); + + Assert.Equal(3, repo.Worktrees.Count()); + + // Check that branch contains same number of files and folders + Assert.False(repo.RetrieveStatus().IsDirty); + var filesInMain = GetFilesOfRepo(repoPath); + var filesInBranch = GetFilesOfRepo(path); + Assert.Equal(filesInMain, filesInBranch); + } + } + + [Fact] + public void CanAddWorktreeForCommittish() + { + var repoPath = SandboxWorktreeTestRepo(); + using (var repo = new Repository(repoPath)) + { + Assert.Equal(2, repo.Worktrees.Count()); + + var name = "blah"; + var committish = "diff-test-cases"; + var path = Path.Combine(repo.Info.WorkingDirectory, "..", "worktrees", name); + var worktree = repo.Worktrees.Add(committish, name, path, false); + Assert.Equal(name, worktree.Name); + Assert.False(worktree.IsLocked); + using (var repository = worktree.WorktreeRepository) + { + Assert.Equal(committish, repository.Head.FriendlyName); + } + Assert.Equal(3, repo.Worktrees.Count()); + + // Check that branch contains same number of files and folders + var filesInCommittish = new string[] { "numbers.txt", "super-file.txt" }; + var filesInBranch = GetFilesOfRepo(path); + Assert.Equal(filesInCommittish, filesInBranch); + } + } + + private static IEnumerable GetFilesOfRepo(string repoPath) + { + return Directory.GetFiles(repoPath, "*", SearchOption.AllDirectories) + .Where(fileName => !fileName.StartsWith(Path.Combine(repoPath, ".git"))) + .Select(fileName => fileName.Replace($"{repoPath}{Path.DirectorySeparatorChar}", "")) + .OrderBy(fileName => fileName) + .ToList(); + } + } +} diff --git a/LibGit2Sharp.Tests/app.config b/LibGit2Sharp.Tests/app.config deleted file mode 100644 index 34dc4a041..000000000 --- a/LibGit2Sharp.Tests/app.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/LibGit2Sharp.Tests/ShadowCopyFixture.cs b/LibGit2Sharp.Tests/desktop/ShadowCopyFixture.cs similarity index 96% rename from LibGit2Sharp.Tests/ShadowCopyFixture.cs rename to LibGit2Sharp.Tests/desktop/ShadowCopyFixture.cs index dd3fdbaab..d9618c06c 100644 --- a/LibGit2Sharp.Tests/ShadowCopyFixture.cs +++ b/LibGit2Sharp.Tests/desktop/ShadowCopyFixture.cs @@ -11,6 +11,7 @@ namespace LibGit2Sharp.Tests public class ShadowCopyFixture : BaseFixture { [Fact] + [Trait("TestCategory", "FailsWhileInstrumented")] public void CanProbeForNativeBinariesFromAShadowCopiedAssembly() { Type type = typeof(Wrapper); @@ -55,7 +56,7 @@ public void CanProbeForNativeBinariesFromAShadowCopiedAssembly() // ...that the assembly in the other domain is stored in the shadow copy cache... string cachedAssembliesPath = Path.Combine(setup.CachePath, setup.ApplicationName); - Assert.True(cachedAssemblyLocation.StartsWith(cachedAssembliesPath)); + Assert.StartsWith(cachedAssembliesPath, cachedAssemblyLocation); if (!Constants.IsRunningOnUnix) { diff --git a/LibGit2Sharp.Tests/SmartSubtransportFixture.cs b/LibGit2Sharp.Tests/desktop/SmartSubtransportFixture.cs similarity index 79% rename from LibGit2Sharp.Tests/SmartSubtransportFixture.cs rename to LibGit2Sharp.Tests/desktop/SmartSubtransportFixture.cs index d55785baa..4e3b03ce3 100644 --- a/LibGit2Sharp.Tests/SmartSubtransportFixture.cs +++ b/LibGit2Sharp.Tests/desktop/SmartSubtransportFixture.cs @@ -4,9 +4,7 @@ using System.Net; using System.Net.Security; using LibGit2Sharp.Tests.TestHelpers; -using LibGit2Sharp.Core; using Xunit; -using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -63,7 +61,7 @@ public void CustomSmartSubtransportTest(string scheme, string url) } // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[0], + Commands.Fetch(repo, remoteName, Array.Empty(), new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto }, null); @@ -79,58 +77,58 @@ public void CustomSmartSubtransportTest(string scheme, string url) } } - [Theory] - [InlineData("https", "https://bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3")] - public void CanUseCredentials(string scheme, string url, string user, string pass) - { - string remoteName = "testRemote"; - - var scd = BuildSelfCleaningDirectory(); - Repository.Init(scd.RootedDirectoryPath); - - SmartSubtransportRegistration registration = null; - - try - { - // Disable server certificate validation for testing. - // Do *NOT* enable this in production. - ServicePointManager.ServerCertificateValidationCallback = certificateValidationCallback; - - registration = GlobalSettings.RegisterSmartSubtransport(scheme); - Assert.NotNull(registration); - - using (var repo = new Repository(scd.DirectoryPath)) - { - repo.Network.Remotes.Add(remoteName, url); - - // Set up structures for the expected results - // and verifying the RemoteUpdateTips callback. - TestRemoteInfo expectedResults = TestRemoteInfo.TestRemoteInstance; - ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName); - - // Add expected branch objects - foreach (KeyValuePair kvp in expectedResults.BranchTips) - { - expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value); - } - - // Perform the actual fetch - Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { - OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto, - CredentialsProvider = (_user, _valid, _hostname) => new UsernamePasswordCredentials() { Username = "libgit3", Password = "libgit3" }, - }, null); - - // Verify the expected - expectedFetchState.CheckUpdatedReferences(repo); - } - } - finally - { - GlobalSettings.UnregisterSmartSubtransport(registration); - - ServicePointManager.ServerCertificateValidationCallback -= certificateValidationCallback; - } - } + //[Theory] + //[InlineData("https", "https://bitbucket.org/libgit2/testgitrepository.git", "libgit3", "libgit3")] + //public void CanUseCredentials(string scheme, string url, string user, string pass) + //{ + // string remoteName = "testRemote"; + + // var scd = BuildSelfCleaningDirectory(); + // Repository.Init(scd.RootedDirectoryPath); + + // SmartSubtransportRegistration registration = null; + + // try + // { + // // Disable server certificate validation for testing. + // // Do *NOT* enable this in production. + // ServicePointManager.ServerCertificateValidationCallback = certificateValidationCallback; + + // registration = GlobalSettings.RegisterSmartSubtransport(scheme); + // Assert.NotNull(registration); + + // using (var repo = new Repository(scd.DirectoryPath)) + // { + // repo.Network.Remotes.Add(remoteName, url); + + // // Set up structures for the expected results + // // and verifying the RemoteUpdateTips callback. + // TestRemoteInfo expectedResults = TestRemoteInfo.TestRemoteInstance; + // ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName); + + // // Add expected branch objects + // foreach (KeyValuePair kvp in expectedResults.BranchTips) + // { + // expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value); + // } + + // // Perform the actual fetch + // Commands.Fetch(repo, remoteName, new string[0], new FetchOptions { + // OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto, + // CredentialsProvider = (_user, _valid, _hostname) => new UsernamePasswordCredentials() { Username = user, Password = pass }, + // }, null); + + // // Verify the expected + // expectedFetchState.CheckUpdatedReferences(repo); + // } + // } + // finally + // { + // GlobalSettings.UnregisterSmartSubtransport(registration); + + // ServicePointManager.ServerCertificateValidationCallback -= certificateValidationCallback; + // } + //} [Fact] public void CannotReregisterScheme() @@ -163,29 +161,29 @@ public void CannotUnregisterTwice() private class MockSmartSubtransport : RpcSmartSubtransport { - protected override SmartSubtransportStream Action(String url, GitSmartSubtransportAction action) + protected override SmartSubtransportStream Action(string url, GitSmartSubtransportAction action) { - String endpointUrl, contentType = null; + string endpointUrl, contentType = null; bool isPost = false; switch (action) { case GitSmartSubtransportAction.UploadPackList: - endpointUrl = String.Concat(url, "/info/refs?service=git-upload-pack"); + endpointUrl = string.Concat(url, "/info/refs?service=git-upload-pack"); break; case GitSmartSubtransportAction.UploadPack: - endpointUrl = String.Concat(url, "/git-upload-pack"); + endpointUrl = string.Concat(url, "/git-upload-pack"); contentType = "application/x-git-upload-pack-request"; isPost = true; break; case GitSmartSubtransportAction.ReceivePackList: - endpointUrl = String.Concat(url, "/info/refs?service=git-receive-pack"); + endpointUrl = string.Concat(url, "/info/refs?service=git-receive-pack"); break; case GitSmartSubtransportAction.ReceivePack: - endpointUrl = String.Concat(url, "/git-receive-pack"); + endpointUrl = string.Concat(url, "/git-receive-pack"); contentType = "application/x-git-receive-pack-request"; isPost = true; break; @@ -259,6 +257,8 @@ public override int Write(Stream dataStream, long length) private static HttpWebRequest CreateWebRequest(string endpointUrl, bool isPost, string contentType) { + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(endpointUrl); webRequest.UserAgent = "git/1.0 (libgit2 custom transport)"; webRequest.ServicePoint.Expect100Continue = false; @@ -313,7 +313,7 @@ private HttpWebResponse GetResponseWithRedirects() } // rethrow if it's not 401 - throw ex; + throw; } if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect) diff --git a/LibGit2Sharp.Tests/packages.config b/LibGit2Sharp.Tests/packages.config deleted file mode 100644 index 2748122d3..000000000 --- a/LibGit2Sharp.Tests/packages.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/LibGit2Sharp.Tests/xunit.runner.json b/LibGit2Sharp.Tests/xunit.runner.json new file mode 100644 index 000000000..e54567a36 --- /dev/null +++ b/LibGit2Sharp.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.github.io/schema/current/xunit.runner.schema.json", + "shadowCopy": false +} diff --git a/LibGit2Sharp.sln b/LibGit2Sharp.sln index 765e8dd01..e99eec26f 100644 --- a/LibGit2Sharp.sln +++ b/LibGit2Sharp.sln @@ -1,15 +1,24 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibGit2Sharp", "LibGit2Sharp\LibGit2Sharp.csproj", "{EE6ED99F-CB12-4683-B055-D28FC7357A34}" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibGit2Sharp", "LibGit2Sharp\LibGit2Sharp.csproj", "{EE6ED99F-CB12-4683-B055-D28FC7357A34}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibGit2Sharp.Tests", "LibGit2Sharp.Tests\LibGit2Sharp.Tests.csproj", "{286E63EB-04DD-4ADE-88D6-041B57800761}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibGit2Sharp.Tests", "LibGit2Sharp.Tests\LibGit2Sharp.Tests.csproj", "{286E63EB-04DD-4ADE-88D6-041B57800761}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{19D079A4-A273-4630-B2D2-79EADE3E7CA1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0CA739FD-DA4D-4F64-9834-DA14A3ECD04B}" ProjectSection(SolutionItems) = preProject - .nuget\packages.config = .nuget\packages.config + .gitignore = .gitignore + Targets\CodeGenerator.targets = Targets\CodeGenerator.targets + Directory.Build.props = Directory.Build.props + Targets\GenerateNativeDllName.targets = Targets\GenerateNativeDllName.targets + nuget.config = nuget.config EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeLibraryLoadTestApp.x86", "NativeLibraryLoadTestApp\x86\NativeLibraryLoadTestApp.x86.csproj", "{86453D2C-4953-4DF4-B12A-ADE579608BAA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeLibraryLoadTestApp.x64", "NativeLibraryLoadTestApp\x64\NativeLibraryLoadTestApp.x64.csproj", "{5C55175D-6A1F-4C51-B791-BF7DD00124EE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,33 +33,19 @@ Global {286E63EB-04DD-4ADE-88D6-041B57800761}.Debug|Any CPU.Build.0 = Debug|Any CPU {286E63EB-04DD-4ADE-88D6-041B57800761}.Release|Any CPU.ActiveCfg = Release|Any CPU {286E63EB-04DD-4ADE-88D6-041B57800761}.Release|Any CPU.Build.0 = Release|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86453D2C-4953-4DF4-B12A-ADE579608BAA}.Release|Any CPU.Build.0 = Release|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C55175D-6A1F-4C51-B791-BF7DD00124EE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - Policies = $0 - $0.TextStylePolicy = $1 - $1.inheritsSet = null - $1.scope = text/x-csharp - $0.CSharpFormattingPolicy = $2 - $2.IndentSwitchBody = True - $2.BeforeMethodCallParentheses = False - $2.BeforeMethodDeclarationParentheses = False - $2.BeforeConstructorDeclarationParentheses = False - $2.BeforeDelegateDeclarationParentheses = False - $2.NewParentheses = False - $2.inheritsSet = Mono - $2.inheritsScope = text/x-csharp - $2.scope = text/x-csharp - $0.StandardHeader = $3 - $3.Text = - $3.inheritsSet = Apache2License - $0.TextStylePolicy = $4 - $4.FileWidth = 120 - $4.RemoveTrailingWhitespace = True - $4.inheritsSet = VisualStudio - $4.inheritsScope = text/plain - $4.scope = text/plain + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9BD5F77D-E47D-4621-9AA0-8598766902B9} EndGlobalSection EndGlobal diff --git a/LibGit2Sharp.sln.DotSettings b/LibGit2Sharp.sln.DotSettings deleted file mode 100644 index 8bc2282a8..000000000 --- a/LibGit2Sharp.sln.DotSettings +++ /dev/null @@ -1,17 +0,0 @@ - - <?xml version="1.0" encoding="utf-16"?><Profile name="LibGit2Sharp"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode></Profile> - TOGETHER - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - True - False - True - True - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - True - True - True - diff --git a/LibGit2Sharp.v2.ncrunchsolution b/LibGit2Sharp.v2.ncrunchsolution deleted file mode 100644 index 9420cc077..000000000 --- a/LibGit2Sharp.v2.ncrunchsolution +++ /dev/null @@ -1,13 +0,0 @@ - - 1 - false - true - true - UseDynamicAnalysis - UseStaticAnalysis - UseStaticAnalysis - UseStaticAnalysis - UseStaticAnalysis - - - \ No newline at end of file diff --git a/LibGit2Sharp/AfterRebaseStepInfo.cs b/LibGit2Sharp/AfterRebaseStepInfo.cs index 8e6e78e2d..54558b59d 100644 --- a/LibGit2Sharp/AfterRebaseStepInfo.cs +++ b/LibGit2Sharp/AfterRebaseStepInfo.cs @@ -27,7 +27,7 @@ internal AfterRebaseStepInfo(RebaseStepInfo stepInfo, Commit commit, long comple /// /// internal AfterRebaseStepInfo(RebaseStepInfo stepInfo, long completedStepIndex, long totalStepCount) - : this (stepInfo, null, completedStepIndex, totalStepCount) + : this(stepInfo, null, completedStepIndex, totalStepCount) { WasPatchAlreadyApplied = true; } diff --git a/LibGit2Sharp/AmbiguousSpecificationException.cs b/LibGit2Sharp/AmbiguousSpecificationException.cs index bad7af141..b5ddd7963 100644 --- a/LibGit2Sharp/AmbiguousSpecificationException.cs +++ b/LibGit2Sharp/AmbiguousSpecificationException.cs @@ -1,14 +1,18 @@ using System; -using System.Globalization; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif +using LibGit2Sharp.Core; namespace LibGit2Sharp { /// /// The exception that is thrown when the provided specification cannot uniquely identify a reference, an object or a path. /// +#if NETFRAMEWORK [Serializable] - public class AmbiguousSpecificationException : LibGit2SharpException +#endif + public class AmbiguousSpecificationException : NativeException { /// /// Initializes a new instance of the class. @@ -27,10 +31,10 @@ public AmbiguousSpecificationException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public AmbiguousSpecificationException(string format, params object[] args) - : base(String.Format(format, args)) + : base(string.Format(format, args)) { } @@ -43,6 +47,7 @@ public AmbiguousSpecificationException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,5 +56,14 @@ public AmbiguousSpecificationException(string message, Exception innerException) protected AmbiguousSpecificationException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.Ambiguous; + } + } } } diff --git a/LibGit2Sharp/BareRepositoryException.cs b/LibGit2Sharp/BareRepositoryException.cs index 75ad9695c..412e5e4d4 100644 --- a/LibGit2Sharp/BareRepositoryException.cs +++ b/LibGit2Sharp/BareRepositoryException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -8,8 +10,10 @@ namespace LibGit2Sharp /// The exception that is thrown when an operation which requires a /// working directory is performed against a bare repository. /// +#if NETFRAMEWORK [Serializable] - public class BareRepositoryException : LibGit2SharpException +#endif + public class BareRepositoryException : NativeException { /// /// Initializes a new instance of the class. @@ -28,7 +32,7 @@ public BareRepositoryException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public BareRepositoryException(string format, params object[] args) : base(format, args) @@ -43,6 +47,7 @@ public BareRepositoryException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,9 +56,18 @@ public BareRepositoryException(string message, Exception innerException) protected BareRepositoryException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal BareRepositoryException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal BareRepositoryException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.BareRepo; + } + } } } diff --git a/LibGit2Sharp/BlameHunk.cs b/LibGit2Sharp/BlameHunk.cs index 553efb14e..6350a9bbc 100644 --- a/LibGit2Sharp/BlameHunk.cs +++ b/LibGit2Sharp/BlameHunk.cs @@ -114,8 +114,8 @@ private string DebuggerDisplay return string.Format(CultureInfo.InvariantCulture, "{0}-{1} ({2})", FinalStartLineNumber, - FinalStartLineNumber+LineCount-1, - FinalCommit.ToString().Substring(0,7)); + FinalStartLineNumber + LineCount - 1, + FinalCommit.ToString().Substring(0, 7)); } } @@ -135,10 +135,10 @@ public bool Equals(BlameHunk other) } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as BlameHunk); diff --git a/LibGit2Sharp/BlameHunkCollection.cs b/LibGit2Sharp/BlameHunkCollection.cs index 869daf527..2766ee7a6 100644 --- a/LibGit2Sharp/BlameHunkCollection.cs +++ b/LibGit2Sharp/BlameHunkCollection.cs @@ -82,7 +82,7 @@ public virtual BlameHunk HunkForLine(int line) { return hunk; } - throw new ArgumentOutOfRangeException("line", "No hunk for that line"); + throw new ArgumentOutOfRangeException(nameof(line), "No hunk for that line"); } /// diff --git a/LibGit2Sharp/Blob.cs b/LibGit2Sharp/Blob.cs index 9b14cb50f..29ef8d812 100644 --- a/LibGit2Sharp/Blob.cs +++ b/LibGit2Sharp/Blob.cs @@ -8,9 +8,12 @@ namespace LibGit2Sharp /// /// Stores the binary content of a tracked file. /// + /// + /// Since the introduction of partially cloned repositories, blobs might be missing on your local repository (see https://git-scm.com/docs/partial-clone) + /// public class Blob : GitObject { - private readonly ILazy lazySize; + private readonly ILazy lazySize; private readonly ILazy lazyIsBinary; /// @@ -22,8 +25,8 @@ protected Blob() internal Blob(Repository repo, ObjectId id) : base(repo, id) { - lazySize = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_blob_rawsize); - lazyIsBinary = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_blob_is_binary); + lazySize = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_blob_rawsize, throwIfMissing: true); + lazyIsBinary = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_blob_is_binary, throwIfMissing: true); } /// @@ -33,16 +36,19 @@ internal Blob(Repository repo, ObjectId id) /// can be used. /// /// - public virtual long Size { get { return lazySize.Value; } } + /// Throws if blob is missing + public virtual long Size => lazySize.Value; /// /// Determine if the blob content is most certainly binary or not. /// - public virtual bool IsBinary { get { return lazyIsBinary.Value; } } + /// Throws if blob is missing + public virtual bool IsBinary => lazyIsBinary.Value; /// /// Gets the blob content in a . /// + /// Throws if blob is missing public virtual Stream GetContentStream() { return Proxy.git_blob_rawcontent_stream(repo.Handle, Id, Size); @@ -53,6 +59,7 @@ public virtual Stream GetContentStream() /// checked out to the working directory. /// Parameter controlling content filtering behavior /// + /// Throws if blob is missing public virtual Stream GetContentStream(FilteringOptions filteringOptions) { Ensure.ArgumentNotNull(filteringOptions, "filteringOptions"); @@ -64,6 +71,7 @@ public virtual Stream GetContentStream(FilteringOptions filteringOptions) /// Gets the blob content, decoded with UTF8 encoding if the encoding cannot be detected from the byte order mark /// /// Blob content as text. + /// Throws if blob is missing public virtual string GetContentText() { return ReadToEnd(GetContentStream(), null); @@ -75,6 +83,7 @@ public virtual string GetContentText() /// /// The encoding of the text to use, if it cannot be detected /// Blob content as text. + /// Throws if blob is missing public virtual string GetContentText(Encoding encoding) { Ensure.ArgumentNotNull(encoding, "encoding"); @@ -87,6 +96,7 @@ public virtual string GetContentText(Encoding encoding) /// /// Parameter controlling content filtering behavior /// Blob content as text. + /// Throws if blob is missing public virtual string GetContentText(FilteringOptions filteringOptions) { return GetContentText(filteringOptions, null); @@ -101,6 +111,7 @@ public virtual string GetContentText(FilteringOptions filteringOptions) /// Parameter controlling content filtering behavior /// The encoding of the text. (default: detected or UTF8) /// Blob content as text. + /// Throws if blob is missing public virtual string GetContentText(FilteringOptions filteringOptions, Encoding encoding) { Ensure.ArgumentNotNull(filteringOptions, "filteringOptions"); diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index 80f4648e3..807456688 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -162,7 +162,7 @@ public virtual string UpstreamBranchCanonicalName /// If this is a local branch, this will return the configured /// to fetch from and push to. If this is a /// remote-tracking branch, this will return the name of the remote - /// containing the tracked branch. If there no tracking information + /// containing the tracked branch. If there is no tracking information, /// this will return null. /// /// @@ -176,31 +176,6 @@ public virtual string RemoteName } } - /// - /// Get the remote for the branch. - /// - /// If this is a local branch, this will return the configured - /// to fetch from and push to. If this is a - /// remote-tracking branch, this will return the remote containing - /// the tracked branch. - /// - /// - [Obsolete("This property is deprecated. Use Repository.Network.Remotes[] using the RemoteName property")] - public virtual Remote Remote - { - get - { - string remoteName = RemoteName; - - if (remoteName == null) - { - return null; - } - - return repo.Network.Remotes[remoteName]; - } - } - private string UpstreamBranchCanonicalNameFromLocalBranch() { ConfigurationEntry mergeRefEntry = repo.Config.Get("branch", FriendlyName, "merge"); diff --git a/LibGit2Sharp/BuiltInFeatures.cs b/LibGit2Sharp/BuiltInFeatures.cs index db6a1a0ed..1cf0d92e9 100644 --- a/LibGit2Sharp/BuiltInFeatures.cs +++ b/LibGit2Sharp/BuiltInFeatures.cs @@ -29,5 +29,11 @@ public enum BuiltInFeatures /// libgit2. /// Ssh = (1 << 2), + + /// + /// Support for sub-second resolution in file modification times + /// is compiled into libgit2. + /// + NSec = (1 << 3), } } diff --git a/LibGit2Sharp/CertificateSsh.cs b/LibGit2Sharp/CertificateSsh.cs index 3c66bd995..683c04402 100644 --- a/LibGit2Sharp/CertificateSsh.cs +++ b/LibGit2Sharp/CertificateSsh.cs @@ -35,32 +35,22 @@ protected CertificateSsh() /// public readonly bool HasSHA1; - /// - /// True if we have the SHA1 hostkey hash from the server - /// public readonly bool HasSHA1; - internal unsafe CertificateSsh(git_certificate_ssh* cert) { - HasMD5 = cert->type.HasFlag(GitCertificateSshType.MD5); + HasMD5 = cert->type.HasFlag(GitCertificateSshType.MD5); HasSHA1 = cert->type.HasFlag(GitCertificateSshType.SHA1); HashMD5 = new byte[16]; - fixed (byte* p = &HashMD5[0]) + for (var i = 0; i < HashMD5.Length; i++) { - for (var i = 0; i < HashMD5.Length; i++) - { - HashMD5[i] = p[i]; - } + HashMD5[i] = cert->HashMD5[i]; } HashSHA1 = new byte[20]; - fixed (byte* p = &HashSHA1[0]) + for (var i = 0; i < HashSHA1.Length; i++) { - for (var i = 0; i < HashSHA1.Length; i++) - { - HashSHA1[i] = p[i]; - } + HashSHA1[i] = cert->HashSHA1[i]; } } @@ -82,7 +72,7 @@ internal unsafe IntPtr ToPointer() type = sshCertType, }; - fixed (byte *p = &HashMD5[0]) + fixed (byte* p = &HashMD5[0]) { for (var i = 0; i < HashMD5.Length; i++) { @@ -90,7 +80,7 @@ internal unsafe IntPtr ToPointer() } } - fixed (byte *p = &HashSHA1[0]) + fixed (byte* p = &HashSHA1[0]) { for (var i = 0; i < HashSHA1.Length; i++) { diff --git a/LibGit2Sharp/CertificateX509.cs b/LibGit2Sharp/CertificateX509.cs index b48d3fc55..7b5b0fac6 100644 --- a/LibGit2Sharp/CertificateX509.cs +++ b/LibGit2Sharp/CertificateX509.cs @@ -10,7 +10,6 @@ namespace LibGit2Sharp /// public class CertificateX509 : Certificate { - /// /// For mocking purposes /// @@ -24,12 +23,17 @@ protected CertificateX509() internal unsafe CertificateX509(git_certificate_x509* cert) { - int len = checked((int) cert->len.ToUInt32()); + int len = checked((int)cert->len.ToUInt32()); byte[] data = new byte[len]; Marshal.Copy(new IntPtr(cert->data), data, 0, len); Certificate = new X509Certificate(data); } + internal CertificateX509(X509Certificate cert) + { + Certificate = cert; + } + internal unsafe IntPtr ToPointers(out IntPtr dataPtr) { var certData = Certificate.Export(X509ContentType.Cert); @@ -38,8 +42,8 @@ internal unsafe IntPtr ToPointers(out IntPtr dataPtr) var gitCert = new git_certificate_x509() { cert_type = GitCertificateType.X509, - data = (byte*) dataPtr.ToPointer(), - len = (UIntPtr)certData.LongLength, + data = (byte*)dataPtr.ToPointer(), + len = (UIntPtr)certData.Length, }; var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(gitCert)); diff --git a/LibGit2Sharp/CheckoutConflictException.cs b/LibGit2Sharp/CheckoutConflictException.cs index a06360afb..67dc8d2cc 100644 --- a/LibGit2Sharp/CheckoutConflictException.cs +++ b/LibGit2Sharp/CheckoutConflictException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -9,8 +11,10 @@ namespace LibGit2Sharp /// because of a conflicting change staged in the index, or unstaged /// in the working directory. /// +#if NETFRAMEWORK [Serializable] - public class CheckoutConflictException : LibGit2SharpException +#endif + public class CheckoutConflictException : NativeException { /// /// Initializes a new instance of the class. @@ -29,7 +33,7 @@ public CheckoutConflictException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public CheckoutConflictException(string format, params object[] args) : base(format, args) @@ -44,6 +48,7 @@ public CheckoutConflictException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -52,9 +57,18 @@ public CheckoutConflictException(string message, Exception innerException) protected CheckoutConflictException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal CheckoutConflictException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal CheckoutConflictException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.Conflict; + } + } } } diff --git a/LibGit2Sharp/CloneOptions.cs b/LibGit2Sharp/CloneOptions.cs index 8d260db74..12d47c9f3 100644 --- a/LibGit2Sharp/CloneOptions.cs +++ b/LibGit2Sharp/CloneOptions.cs @@ -4,12 +4,23 @@ namespace LibGit2Sharp { /// - /// Options to define clone behaviour + /// Options to define clone behavior /// - public sealed class CloneOptions : FetchOptionsBase, IConvertableToGitCheckoutOpts + public sealed class CloneOptions : IConvertableToGitCheckoutOpts { /// - /// Creates default for a non-bare clone + /// Creates with specified for a non-bare clone. + /// + /// The fetch options to use. + public CloneOptions(FetchOptions fetchOptions) : this() + { + Ensure.ArgumentNotNull(fetchOptions, "fetchOptions"); + + FetchOptions = fetchOptions; + } + + /// + /// Creates default for a non-bare clone. /// public CloneOptions() { @@ -43,6 +54,11 @@ public CloneOptions() /// public CheckoutProgressHandler OnCheckoutProgress { get; set; } + /// + /// Gets or sets the fetch options. + /// + public FetchOptions FetchOptions { get; } = new(); + #region IConvertableToGitCheckoutOpts CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() diff --git a/LibGit2Sharp/Commands/Checkout.cs b/LibGit2Sharp/Commands/Checkout.cs index 18749246f..46d456be1 100644 --- a/LibGit2Sharp/Commands/Checkout.cs +++ b/LibGit2Sharp/Commands/Checkout.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -37,18 +38,49 @@ public static Branch Checkout(IRepository repository, string committishOrBranchS Ensure.ArgumentNotNullOrEmptyString(committishOrBranchSpec, "committishOrBranchSpec"); Ensure.ArgumentNotNull(options, "options"); - Reference reference; - GitObject obj; + Reference reference = null; + GitObject obj = null; + Branch branch = null; + + try + { + repository.RevParse(committishOrBranchSpec, out reference, out obj); + } + catch (NotFoundException) + { + // If committishOrBranchSpec is not a local branch but matches a tracking branch + // in exactly one remote, use it. This is the "git checkout" command's default behavior. + // https://git-scm.com/docs/git-checkout#Documentation/git-checkout.txt-emgitcheckoutemltbranchgt + var remoteBranches = repository.Network.Remotes + .SelectMany(r => repository.Branches.Where(b => + b.IsRemote && + b.CanonicalName == $"refs/remotes/{r.Name}/{committishOrBranchSpec}")) + .ToList(); + + if (remoteBranches.Count == 1) + { + branch = repository.CreateBranch(committishOrBranchSpec, remoteBranches[0].Tip); + repository.Branches.Update(branch, b => b.TrackedBranch = remoteBranches[0].CanonicalName); + + return Checkout(repository, branch, options); + } + + if (remoteBranches.Count > 1) + { + throw new AmbiguousSpecificationException($"'{committishOrBranchSpec}' matched multiple ({remoteBranches.Count}) remote tracking branches"); + } + + throw; + } - repository.RevParse(committishOrBranchSpec, out reference, out obj); if (reference != null && reference.IsLocalBranch) { - Branch branch = repository.Branches[reference.CanonicalName]; + branch = repository.Branches[reference.CanonicalName]; return Checkout(repository, branch, options); } - Commit commit = obj.DereferenceToCommit(true); - Checkout(repository, commit.Tree, options, committishOrBranchSpec); + Commit commit = obj.Peel(true); + Checkout(repository, commit.Tree, options, committishOrBranchSpec); return repository.Head; } diff --git a/LibGit2Sharp/Commands/Fetch.cs b/LibGit2Sharp/Commands/Fetch.cs index ed8ca66c0..e531aac51 100644 --- a/LibGit2Sharp/Commands/Fetch.cs +++ b/LibGit2Sharp/Commands/Fetch.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using LibGit2Sharp; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -38,6 +37,7 @@ public static void Fetch(Repository repository, string remote, IEnumerable 0) + { + fetchOptions.CustomHeaders = GitStrArrayManaged.BuildFrom(options.CustomHeaders); + } + + fetchOptions.ProxyOptions = options.ProxyOptions.CreateGitProxyOptions(); Proxy.git_remote_fetch(remoteHandle, refspecs, fetchOptions, logMessage); } diff --git a/LibGit2Sharp/Commands/Pull.cs b/LibGit2Sharp/Commands/Pull.cs index d42c1951c..f0a68fe9b 100644 --- a/LibGit2Sharp/Commands/Pull.cs +++ b/LibGit2Sharp/Commands/Pull.cs @@ -1,5 +1,4 @@ using System; -using LibGit2Sharp; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -19,7 +18,6 @@ public static MergeResult Pull(Repository repository, Signature merger, PullOpti { Ensure.ArgumentNotNull(repository, "repository"); Ensure.ArgumentNotNull(merger, "merger"); - Ensure.ArgumentNotNull(options, "options"); options = options ?? new PullOptions(); @@ -35,7 +33,7 @@ public static MergeResult Pull(Repository repository, Signature merger, PullOpti throw new LibGit2SharpException("No upstream remote for the current branch."); } - Commands.Fetch(repository, currentBranch.RemoteName, new string[0], options.FetchOptions, null); + Commands.Fetch(repository, currentBranch.RemoteName, Array.Empty(), options.FetchOptions, null); return repository.MergeFetchedRefs(merger, options.MergeOptions); } } diff --git a/LibGit2Sharp/Commands/Remove.cs b/LibGit2Sharp/Commands/Remove.cs index 939c427d1..f96339c12 100644 --- a/LibGit2Sharp/Commands/Remove.cs +++ b/LibGit2Sharp/Commands/Remove.cs @@ -1,7 +1,6 @@ -using System.Linq; +using System.Collections.Generic; using System.IO; -using System.Collections.Generic; -using LibGit2Sharp; +using System.Linq; using LibGit2Sharp.Core; namespace LibGit2Sharp diff --git a/LibGit2Sharp/Commands/Stage.cs b/LibGit2Sharp/Commands/Stage.cs index a1febafcb..d11bf6f76 100644 --- a/LibGit2Sharp/Commands/Stage.cs +++ b/LibGit2Sharp/Commands/Stage.cs @@ -237,7 +237,7 @@ public static void Move(IRepository repository, IEnumerable sourcePaths, if (batch.Count == 0) { - throw new ArgumentNullException("sourcePaths"); + throw new ArgumentNullException(nameof(sourcePaths)); } foreach (KeyValuePair, Tuple> keyValuePair in batch) diff --git a/LibGit2Sharp/CommitFilter.cs b/LibGit2Sharp/CommitFilter.cs index 56b23389c..8997ca772 100644 --- a/LibGit2Sharp/CommitFilter.cs +++ b/LibGit2Sharp/CommitFilter.cs @@ -6,7 +6,7 @@ namespace LibGit2Sharp { /// - /// Criterias used to filter out and order the commits of the repository when querying its history. + /// Criteria used to filter out and order the commits of the repository when querying its history. /// public sealed class CommitFilter { @@ -21,7 +21,7 @@ public CommitFilter() } /// - /// The ordering stragtegy to use. + /// The ordering strategy to use. /// /// By default, the commits are shown in reverse chronological order. /// diff --git a/LibGit2Sharp/CommitLog.cs b/LibGit2Sharp/CommitLog.cs index 06cb1b9ea..4a6ab1de3 100644 --- a/LibGit2Sharp/CommitLog.cs +++ b/LibGit2Sharp/CommitLog.cs @@ -91,21 +91,6 @@ public IEnumerable QueryBy(string path) return new FileHistory(repo, path); } - /// - /// Returns the list of commits of the repository representing the history of a file beyond renames. - /// - /// The file's path. - /// The options used to control which commits will be returned. - /// A list of file history entries, ready to be enumerated. - [Obsolete("This method is deprecated. Please use the overload which take LibGit2Sharp.CommitFilter")] - public IEnumerable QueryBy(string path, FollowFilter filter) - { - Ensure.ArgumentNotNull(path, "path"); - Ensure.ArgumentNotNull(filter, "filter"); - - return QueryBy(path, new CommitFilter { SortBy = filter.SortBy }); - } - /// /// Returns the list of commits of the repository representing the history of a file beyond renames. /// diff --git a/LibGit2Sharp/CompareOptions.cs b/LibGit2Sharp/CompareOptions.cs index fbd147c79..fb4234439 100644 --- a/LibGit2Sharp/CompareOptions.cs +++ b/LibGit2Sharp/CompareOptions.cs @@ -44,5 +44,11 @@ public CompareOptions() /// By default, will be used. /// public DiffAlgorithm Algorithm { get; set; } + + /// + /// Enable --indent-heuristic Diff option, that attempts to produce more aesthetically pleasing diffs. + /// By default, this option will be false. + /// + public bool IndentHeuristic { get; set; } } } diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index 5a0aa03f3..84a8a3e53 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -52,41 +52,42 @@ internal Configuration( private void Init(Repository repository) { configHandle = Proxy.git_config_new(); + RepositoryHandle repoHandle = (repository != null) ? repository.Handle : null; - if (repository != null) + if (repoHandle != null) { //TODO: push back this logic into libgit2. // As stated by @carlosmn "having a helper function to load the defaults and then allowing you // to modify it before giving it to git_repository_open_ext() would be a good addition, I think." // -- Agreed :) string repoConfigLocation = Path.Combine(repository.Info.Path, "config"); - Proxy.git_config_add_file_ondisk(configHandle, repoConfigLocation, ConfigurationLevel.Local); + Proxy.git_config_add_file_ondisk(configHandle, repoConfigLocation, ConfigurationLevel.Local, repoHandle); - Proxy.git_repository_set_config(repository.Handle, configHandle); + Proxy.git_repository_set_config(repoHandle, configHandle); } else if (repoConfigPath != null) { - Proxy.git_config_add_file_ondisk(configHandle, repoConfigPath, ConfigurationLevel.Local); + Proxy.git_config_add_file_ondisk(configHandle, repoConfigPath, ConfigurationLevel.Local, repoHandle); } if (globalConfigPath != null) { - Proxy.git_config_add_file_ondisk(configHandle, globalConfigPath, ConfigurationLevel.Global); + Proxy.git_config_add_file_ondisk(configHandle, globalConfigPath, ConfigurationLevel.Global, repoHandle); } if (xdgConfigPath != null) { - Proxy.git_config_add_file_ondisk(configHandle, xdgConfigPath, ConfigurationLevel.Xdg); + Proxy.git_config_add_file_ondisk(configHandle, xdgConfigPath, ConfigurationLevel.Xdg, repoHandle); } if (systemConfigPath != null) { - Proxy.git_config_add_file_ondisk(configHandle, systemConfigPath, ConfigurationLevel.System); + Proxy.git_config_add_file_ondisk(configHandle, systemConfigPath, ConfigurationLevel.System, repoHandle); } if (programDataConfigPath != null) { - Proxy.git_config_add_file_ondisk(configHandle, programDataConfigPath, ConfigurationLevel.ProgramData); + Proxy.git_config_add_file_ondisk(configHandle, programDataConfigPath, ConfigurationLevel.ProgramData, repoHandle); } } @@ -232,9 +233,9 @@ public void Dispose() /// Unset a configuration variable (key and value) in the local configuration. /// /// The key to unset. - public virtual void Unset(string key) + public virtual bool Unset(string key) { - Unset(key, ConfigurationLevel.Local); + return Unset(key, ConfigurationLevel.Local); } /// @@ -242,23 +243,37 @@ public virtual void Unset(string key) /// /// The key to unset. /// The configuration file which should be considered as the target of this operation - public virtual void Unset(string key, ConfigurationLevel level) + public virtual bool Unset(string key, ConfigurationLevel level) { Ensure.ArgumentNotNullOrEmptyString(key, "key"); using (ConfigurationHandle h = RetrieveConfigurationHandle(level, true, configHandle)) { - Proxy.git_config_delete(h, key); + return Proxy.git_config_delete(h, key); } } - internal void UnsetMultivar(string key, ConfigurationLevel level) + /// + /// Unset all configuration values in a multivar variable (key and value) in the local configuration. + /// + /// The key to unset. + public virtual bool UnsetAll(string key) + { + return UnsetAll(key, ConfigurationLevel.Local); + } + + /// + /// Unset all configuration values in a multivar variable (key and value). + /// + /// The key to unset. + /// The configuration file which should be considered as the target of this operation + public virtual bool UnsetAll(string key, ConfigurationLevel level) { Ensure.ArgumentNotNullOrEmptyString(key, "key"); using (ConfigurationHandle h = RetrieveConfigurationHandle(level, true, configHandle)) { - Proxy.git_config_delete_multivar(h, key); + return Proxy.git_config_delete_multivar(h, key); } } @@ -633,6 +648,53 @@ public virtual void Set(string key, T value, ConfigurationLevel level) } } + /// + /// Adds a configuration value for a multivalue key in the local configuration. Keys are in the form 'section.name'. + /// + /// For example in order to add the value for this in a .git\config file: + /// + /// [test] + /// plugin = first + /// + /// You would call: + /// + /// repo.Config.Add("test.plugin", "first"); + /// + /// + /// The key parts + /// The value + public virtual void Add(string key, string value) + { + Add(key, value, ConfigurationLevel.Local); + } + + /// + /// Adds a configuration value for a multivalue key. Keys are in the form 'section.name'. + /// + /// For example in order to add the value for this in a .git\config file: + /// + /// [test] + /// plugin = first + /// + /// You would call: + /// + /// repo.Config.Add("test.plugin", "first"); + /// + /// + /// The key parts + /// The value + /// The configuration file which should be considered as the target of this operation + public virtual void Add(string key, string value, ConfigurationLevel level) + { + Ensure.ArgumentNotNull(value, "value"); + Ensure.ArgumentNotNullOrEmptyString(key, "key"); + + using (ConfigurationHandle h = RetrieveConfigurationHandle(level, true, configHandle)) + { + Proxy.git_config_add_string(h, key, value); + } + } + /// /// Find configuration entries matching . /// diff --git a/LibGit2Sharp/ConfigurationLevel.cs b/LibGit2Sharp/ConfigurationLevel.cs index 9fd57df28..f0971a1c1 100644 --- a/LibGit2Sharp/ConfigurationLevel.cs +++ b/LibGit2Sharp/ConfigurationLevel.cs @@ -5,6 +5,11 @@ /// public enum ConfigurationLevel { + /// + /// Worktree specific configuration file; $GIT_DIR/config.worktree + /// + Worktree = 6, + /// /// The local .git/config of the current repository. /// diff --git a/LibGit2Sharp/Conflict.cs b/LibGit2Sharp/Conflict.cs index 252535af1..705f66d15 100644 --- a/LibGit2Sharp/Conflict.cs +++ b/LibGit2Sharp/Conflict.cs @@ -61,12 +61,12 @@ public virtual IndexEntry Theirs } /// - /// Determines whether the specified is + /// Determines whether the specified is /// equal to the current . /// - /// The to compare with + /// The to compare with /// the current . - /// true if the specified is equal + /// true if the specified is equal /// to the current ; otherwise, /// false. public override bool Equals(object obj) diff --git a/LibGit2Sharp/ContentChanges.cs b/LibGit2Sharp/ContentChanges.cs index 221c99efa..c4628f919 100644 --- a/LibGit2Sharp/ContentChanges.cs +++ b/LibGit2Sharp/ContentChanges.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Text; @@ -51,6 +52,16 @@ internal void AppendToPatch(string patch) /// public virtual int LinesDeleted { get; internal set; } + /// + /// The list of added lines. + /// + public virtual List AddedLines { get; } = new List(); + + /// + /// The list of deleted lines. + /// + public virtual List DeletedLines { get; } = new List(); + /// /// The patch corresponding to these changes. /// @@ -95,11 +106,13 @@ private unsafe int LineCallback(git_diff_delta* delta, GitDiffHunk hunk, GitDiff switch (line.lineOrigin) { case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION: + AddedLines.Add(new Line(line.NewLineNo, decodedContent)); LinesAdded++; prefix = Encoding.ASCII.GetString(new[] { (byte)line.lineOrigin }); break; case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION: + DeletedLines.Add(new Line(line.OldLineNo, decodedContent)); LinesDeleted++; prefix = Encoding.ASCII.GetString(new[] { (byte)line.lineOrigin }); break; diff --git a/LibGit2Sharp/Core/ArrayMarshaler.cs b/LibGit2Sharp/Core/ArrayMarshaler.cs index b831f6dbc..4a37d241b 100644 --- a/LibGit2Sharp/Core/ArrayMarshaler.cs +++ b/LibGit2Sharp/Core/ArrayMarshaler.cs @@ -13,7 +13,7 @@ public ArrayMarshaler(T[] objs) for (var i = 0; i < objs.Length; i++) { - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); ptrs[i] = ptr; Marshal.StructureToPtr(objs[i], ptr, false); } diff --git a/LibGit2Sharp/Core/EncodingMarshaler.cs b/LibGit2Sharp/Core/EncodingMarshaler.cs index 0cafd9aa1..cb02c649b 100644 --- a/LibGit2Sharp/Core/EncodingMarshaler.cs +++ b/LibGit2Sharp/Core/EncodingMarshaler.cs @@ -32,7 +32,7 @@ public int GetNativeDataSize() return -1; } - public virtual IntPtr MarshalManagedToNative(Object managedObj) + public virtual IntPtr MarshalManagedToNative(object managedObj) { if (managedObj == null) { @@ -51,14 +51,14 @@ public virtual IntPtr MarshalManagedToNative(Object managedObj) return FromManaged(encoding, str); } - public virtual Object MarshalNativeToManaged(IntPtr pNativeData) + public virtual object MarshalNativeToManaged(IntPtr pNativeData) { return FromNative(encoding, pNativeData); } #endregion - public static unsafe IntPtr FromManaged(Encoding encoding, String value) + public static unsafe IntPtr FromManaged(Encoding encoding, string value) { if (encoding == null || value == null) { @@ -114,10 +114,10 @@ public static unsafe string FromNative(Encoding encoding, byte* pNativeData) if (walk == start) { - return String.Empty; + return string.Empty; } - return new String((sbyte*)pNativeData, 0, (int)(walk - start), encoding); + return new string((sbyte*)pNativeData, 0, (int)(walk - start), encoding); } public static unsafe string FromNative(Encoding encoding, IntPtr pNativeData, int length) @@ -129,10 +129,10 @@ public static unsafe string FromNative(Encoding encoding, IntPtr pNativeData, in if (length == 0) { - return String.Empty; + return string.Empty; } - return new String((sbyte*)pNativeData.ToPointer(), 0, length, encoding); + return new string((sbyte*)pNativeData.ToPointer(), 0, length, encoding); } public static string FromBuffer(Encoding encoding, byte[] buffer) @@ -160,7 +160,7 @@ public static string FromBuffer(Encoding encoding, byte[] buffer, int length) if (length == 0) { - return String.Empty; + return string.Empty; } return encoding.GetString(buffer, 0, length); diff --git a/LibGit2Sharp/Core/Ensure.cs b/LibGit2Sharp/Core/Ensure.cs index d82fa3a4b..cd681e4ba 100644 --- a/LibGit2Sharp/Core/Ensure.cs +++ b/LibGit2Sharp/Core/Ensure.cs @@ -49,7 +49,7 @@ public static void ArgumentNotNullOrEmptyString(string argumentValue, string arg { ArgumentNotNull(argumentValue, argumentName); - if (String.IsNullOrWhiteSpace (argumentValue)) + if (string.IsNullOrWhiteSpace(argumentValue)) { throw new ArgumentException("String cannot be empty", argumentName); } @@ -114,27 +114,27 @@ public static void ArgumentIsExpectedIntPtr(IntPtr argumentValue, IntPtr expecte } } - private static readonly Dictionary> + private static readonly Dictionary> GitErrorsToLibGit2SharpExceptions = - new Dictionary> + new Dictionary> { - { GitErrorCode.User, (m, r, c) => new UserCancelledException(m, r, c) }, - { GitErrorCode.BareRepo, (m, r, c) => new BareRepositoryException(m, r, c) }, - { GitErrorCode.Exists, (m, r, c) => new NameConflictException(m, r, c) }, - { GitErrorCode.InvalidSpecification, (m, r, c) => new InvalidSpecificationException(m, r, c) }, - { GitErrorCode.UnmergedEntries, (m, r, c) => new UnmergedIndexEntriesException(m, r, c) }, - { GitErrorCode.NonFastForward, (m, r, c) => new NonFastForwardException(m, r, c) }, - { GitErrorCode.Conflict, (m, r, c) => new CheckoutConflictException(m, r, c) }, - { GitErrorCode.LockedFile, (m, r, c) => new LockedFileException(m, r, c) }, - { GitErrorCode.NotFound, (m, r, c) => new NotFoundException(m, r, c) }, - { GitErrorCode.Peel, (m, r, c) => new PeelException(m, r, c) }, + { GitErrorCode.User, (m, c) => new UserCancelledException(m, c) }, + { GitErrorCode.BareRepo, (m, c) => new BareRepositoryException(m, c) }, + { GitErrorCode.Exists, (m, c) => new NameConflictException(m, c) }, + { GitErrorCode.InvalidSpecification, (m, c) => new InvalidSpecificationException(m, c) }, + { GitErrorCode.UnmergedEntries, (m, c) => new UnmergedIndexEntriesException(m, c) }, + { GitErrorCode.NonFastForward, (m, c) => new NonFastForwardException(m, c) }, + { GitErrorCode.Conflict, (m, c) => new CheckoutConflictException(m, c) }, + { GitErrorCode.LockedFile, (m, c) => new LockedFileException(m, c) }, + { GitErrorCode.NotFound, (m, c) => new NotFoundException(m, c) }, + { GitErrorCode.Peel, (m, c) => new PeelException(m, c) }, }; private static unsafe void HandleError(int result) { string errorMessage; GitErrorCategory errorCategory = GitErrorCategory.Unknown; - GitError* error = NativeMethods.giterr_last(); + GitError* error = NativeMethods.git_error_last(); if (error == null) { @@ -145,13 +145,13 @@ private static unsafe void HandleError(int result) errorMessage = LaxUtf8Marshaler.FromNative(error->Message); } - Func exceptionBuilder; + Func exceptionBuilder; if (!GitErrorsToLibGit2SharpExceptions.TryGetValue((GitErrorCode)result, out exceptionBuilder)) { - exceptionBuilder = (m, r, c) => new LibGit2SharpException(m, r, c); + exceptionBuilder = (m, c) => new LibGit2SharpException(m, c); } - throw exceptionBuilder(errorMessage, (GitErrorCode)result, errorCategory); + throw exceptionBuilder(errorMessage, errorCategory); } /// @@ -256,7 +256,7 @@ public static void GitObjectIsNotNull(GitObject gitObject, string identifier) } var messageFormat = "No valid git object identified by '{0}' exists in the repository."; - + if (string.Equals("HEAD", identifier, StringComparison.Ordinal)) { throw new UnbornBranchException(messageFormat, identifier); diff --git a/LibGit2Sharp/Core/Epoch.cs b/LibGit2Sharp/Core/Epoch.cs deleted file mode 100644 index 0f2657267..000000000 --- a/LibGit2Sharp/Core/Epoch.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace LibGit2Sharp.Core -{ - /// - /// Provides helper methods to help converting between Epoch (unix timestamp) and . - /// - internal static class Epoch - { - private static readonly DateTimeOffset epochDateTimeOffset = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); - - /// - /// Builds a from a Unix timestamp and a timezone offset. - /// - /// The number of seconds since 00:00:00 UTC on 1 January 1970. - /// The number of minutes from UTC in a timezone. - /// A representing this instant. - public static DateTimeOffset ToDateTimeOffset(long secondsSinceEpoch, int timeZoneOffsetInMinutes) - { - DateTimeOffset utcDateTime = epochDateTimeOffset.AddSeconds(secondsSinceEpoch); - TimeSpan offset = TimeSpan.FromMinutes(timeZoneOffsetInMinutes); - return new DateTimeOffset(utcDateTime.DateTime.Add(offset), offset); - } - - /// - /// Converts the part of a into a Unix timestamp. - /// - /// The to convert. - /// The number of seconds since 00:00:00 UTC on 1 January 1970. - public static Int32 ToSecondsSinceEpoch(this DateTimeOffset date) - { - DateTimeOffset utcDate = date.ToUniversalTime(); - return (Int32)utcDate.Subtract(epochDateTimeOffset).TotalSeconds; - } - } -} diff --git a/LibGit2Sharp/Core/FileHistory.cs b/LibGit2Sharp/Core/FileHistory.cs index 5c10a1a24..5775d0ab8 100644 --- a/LibGit2Sharp/Core/FileHistory.cs +++ b/LibGit2Sharp/Core/FileHistory.cs @@ -74,7 +74,7 @@ internal FileHistory(Repository repo, string path, CommitFilter queryFilter) if (!AllowedSortStrategies.Contains(queryFilter.SortBy)) { throw new ArgumentException("Unsupported sort strategy. Only 'Topological', 'Time', or 'Topological | Time' are allowed.", - "queryFilter"); + nameof(queryFilter)); } _repo = repo; diff --git a/LibGit2Sharp/Core/FilePathMarshaler.cs b/LibGit2Sharp/Core/FilePathMarshaler.cs index 9d238a46d..af6afb048 100644 --- a/LibGit2Sharp/Core/FilePathMarshaler.cs +++ b/LibGit2Sharp/Core/FilePathMarshaler.cs @@ -18,7 +18,7 @@ internal class LaxFilePathNoCleanupMarshaler : LaxFilePathMarshaler { private static readonly LaxFilePathNoCleanupMarshaler staticInstance = new LaxFilePathNoCleanupMarshaler(); - public new static ICustomMarshaler GetInstance(String cookie) + public new static ICustomMarshaler GetInstance(string cookie) { return staticInstance; } @@ -50,14 +50,14 @@ internal class StrictFilePathMarshaler : StrictUtf8Marshaler { private static readonly StrictFilePathMarshaler staticInstance = new StrictFilePathMarshaler(); - public new static ICustomMarshaler GetInstance(String cookie) + public new static ICustomMarshaler GetInstance(string cookie) { return staticInstance; } #region ICustomMarshaler - public override IntPtr MarshalManagedToNative(Object managedObj) + public override IntPtr MarshalManagedToNative(object managedObj) { if (null == managedObj) { @@ -70,7 +70,7 @@ public override IntPtr MarshalManagedToNative(Object managedObj) { throw new MarshalDirectiveException(string.Format(CultureInfo.InvariantCulture, "{0} must be used on a FilePath.", - GetType().Name)); + this.GetType().Name)); } return FromManaged(filePath); @@ -98,14 +98,14 @@ internal class LaxFilePathMarshaler : LaxUtf8Marshaler { private static readonly LaxFilePathMarshaler staticInstance = new LaxFilePathMarshaler(); - public new static ICustomMarshaler GetInstance(String cookie) + public new static ICustomMarshaler GetInstance(string cookie) { return staticInstance; } #region ICustomMarshaler - public override Object MarshalNativeToManaged(IntPtr pNativeData) + public override object MarshalNativeToManaged(IntPtr pNativeData) { return FromNative(pNativeData); } diff --git a/LibGit2Sharp/Core/GitBlame.cs b/LibGit2Sharp/Core/GitBlame.cs index df99f44b7..d484b0b4b 100644 --- a/LibGit2Sharp/Core/GitBlame.cs +++ b/LibGit2Sharp/Core/GitBlame.cs @@ -46,7 +46,7 @@ internal class git_blame_options public uint version = 1; public GitBlameOptionFlags flags; - public UInt16 min_match_characters; + public ushort min_match_characters; public git_oid newest_commit; public git_oid oldest_commit; public UIntPtr min_line; @@ -61,7 +61,7 @@ internal unsafe struct git_blame_hunk public git_oid final_commit_id; public UIntPtr final_start_line_number; public git_signature* final_signature; - + public git_oid orig_commit_id; public char* orig_path; public UIntPtr orig_start_line_number; diff --git a/LibGit2Sharp/Core/GitBuf.cs b/LibGit2Sharp/Core/GitBuf.cs index 09860fdc3..19b1328b9 100644 --- a/LibGit2Sharp/Core/GitBuf.cs +++ b/LibGit2Sharp/Core/GitBuf.cs @@ -12,7 +12,7 @@ internal class GitBuf : IDisposable public void Dispose() { - Proxy.git_buf_free(this); + Proxy.git_buf_dispose(this); } } } diff --git a/LibGit2Sharp/Core/GitCertificateSsh.cs b/LibGit2Sharp/Core/GitCertificateSsh.cs index b1b9d7940..e3e7c4927 100644 --- a/LibGit2Sharp/Core/GitCertificateSsh.cs +++ b/LibGit2Sharp/Core/GitCertificateSsh.cs @@ -14,7 +14,7 @@ internal unsafe struct git_certificate_ssh public unsafe fixed byte HashMD5[16]; /// - /// The MD5 hash (if appropriate) + /// The SHA1 hash (if appropriate) /// public unsafe fixed byte HashSHA1[20]; } diff --git a/LibGit2Sharp/Core/GitCertificateSshType.cs b/LibGit2Sharp/Core/GitCertificateSshType.cs index a5151123c..4fc432e9a 100644 --- a/LibGit2Sharp/Core/GitCertificateSshType.cs +++ b/LibGit2Sharp/Core/GitCertificateSshType.cs @@ -5,7 +5,7 @@ namespace LibGit2Sharp.Core [Flags] internal enum GitCertificateSshType { - MD5 = (1 << 0), + MD5 = (1 << 0), SHA1 = (1 << 1), } } diff --git a/LibGit2Sharp/Core/GitCheckoutOpts.cs b/LibGit2Sharp/Core/GitCheckoutOpts.cs index 3424094be..053258565 100644 --- a/LibGit2Sharp/Core/GitCheckoutOpts.cs +++ b/LibGit2Sharp/Core/GitCheckoutOpts.cs @@ -118,6 +118,7 @@ internal enum CheckoutStrategy GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1 << 17), } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int checkout_notify_cb( CheckoutNotifyFlags why, IntPtr path, @@ -126,12 +127,14 @@ internal delegate int checkout_notify_cb( IntPtr workdir, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void progress_cb( IntPtr strPtr, UIntPtr completed_steps, UIntPtr total_steps, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int perfdata_cb( IntPtr perfdata, IntPtr payload); diff --git a/LibGit2Sharp/Core/GitConfigEntry.cs b/LibGit2Sharp/Core/GitConfigEntry.cs index 30550126b..7af657894 100644 --- a/LibGit2Sharp/Core/GitConfigEntry.cs +++ b/LibGit2Sharp/Core/GitConfigEntry.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace LibGit2Sharp.Core @@ -8,8 +7,10 @@ internal unsafe struct GitConfigEntry { public char* namePtr; public char* valuePtr; + public char* backend_type; + public char* origin_path; + public uint include_depth; public uint level; public void* freePtr; - public void* payloadPtr; } } diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs index eb21d6881..44679124d 100644 --- a/LibGit2Sharp/Core/GitDiff.cs +++ b/LibGit2Sharp/Core/GitDiff.cs @@ -133,6 +133,13 @@ internal enum GitDiffOptionFlags * Options controlling how output will be generated */ + /// + /// Use a heuristic that takes indentation and whitespace into account + /// which generally can produce better diffs when dealing with ambiguous + /// diff hunks. + /// + GIT_DIFF_INDENT_HEURISTIC = (1 << 18), + /// /// Treat all files as text, disabling binary attributes and detection /// @@ -191,12 +198,14 @@ internal enum GitDiffOptionFlags GIT_DIFF_SHOW_BINARY = (1 << 30), } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int diff_notify_cb( IntPtr diff_so_far, IntPtr delta_to_add, IntPtr matched_pathspec, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int diff_progress_cb( IntPtr diff_so_far, IntPtr old_path, @@ -222,7 +231,7 @@ internal class GitDiffOptions : IDisposable public uint ContextLines; public uint InterhunkLines; public ushort IdAbbrev; - public Int64 MaxSize; + public long MaxSize; public IntPtr OldPrefixString; public IntPtr NewPrefixString; @@ -246,10 +255,10 @@ internal unsafe struct git_diff_file { public git_oid Id; public char* Path; - public Int64 Size; + public long Size; public GitDiffFlags Flags; - public UInt16 Mode; - public UInt16 IdAbbrev; + public ushort Mode; + public ushort IdAbbrev; } [StructLayout(LayoutKind.Sequential)] @@ -257,8 +266,8 @@ internal unsafe struct git_diff_delta { public ChangeKind status; public GitDiffFlags flags; - public UInt16 similarity; - public UInt16 nfiles; + public ushort similarity; + public ushort nfiles; public git_diff_file old_file; public git_diff_file new_file; } @@ -284,7 +293,7 @@ internal class GitDiffLine public int NewLineNo; public int NumLines; public UIntPtr contentLen; - public Int64 contentOffset; + public long contentOffset; public IntPtr content; } @@ -304,11 +313,11 @@ enum GitDiffLineOrigin : byte enum GitDiffFormat { - GIT_DIFF_FORMAT_PATCH = 1, // < full git diff + GIT_DIFF_FORMAT_PATCH = 1, // < full git diff GIT_DIFF_FORMAT_PATCH_HEADER = 2, // < just the file headers of patch - GIT_DIFF_FORMAT_RAW = 3, // < like git diff --raw - GIT_DIFF_FORMAT_NAME_ONLY = 4, // < like git diff --name-only - GIT_DIFF_FORMAT_NAME_STATUS = 5, // < like git diff --name-status + GIT_DIFF_FORMAT_RAW = 3, // < like git diff --raw + GIT_DIFF_FORMAT_NAME_ONLY = 4, // < like git diff --name-only + GIT_DIFF_FORMAT_NAME_STATUS = 5, // < like git diff --name-status } [Flags] @@ -362,10 +371,10 @@ internal class GitDiffFindOptions { public uint Version = 1; public GitDiffFindFlags Flags; - public UInt16 RenameThreshold; - public UInt16 RenameFromRewriteThreshold; - public UInt16 CopyThreshold; - public UInt16 BreakRewriteThreshold; + public ushort RenameThreshold; + public ushort RenameFromRewriteThreshold; + public ushort CopyThreshold; + public ushort BreakRewriteThreshold; public UIntPtr RenameLimit; // TODO diff --git a/LibGit2Sharp/Core/GitFetchOptions.cs b/LibGit2Sharp/Core/GitFetchOptions.cs index 298c5ae33..26f4c8c7e 100644 --- a/LibGit2Sharp/Core/GitFetchOptions.cs +++ b/LibGit2Sharp/Core/GitFetchOptions.cs @@ -11,6 +11,8 @@ internal class GitFetchOptions public bool UpdateFetchHead = true; public TagFetchMode download_tags; public GitProxyOptions ProxyOptions; - public GitStrArrayManaged custom_headers; + public int Depth = 0; // GIT_FETCH_DEPTH_FULL + public RemoteRedirectMode FollowRedirects = RemoteRedirectMode.Initial; + public GitStrArrayManaged CustomHeaders; } } diff --git a/LibGit2Sharp/Core/GitFetchOptionsWrapper.cs b/LibGit2Sharp/Core/GitFetchOptionsWrapper.cs new file mode 100644 index 000000000..9c7f3952a --- /dev/null +++ b/LibGit2Sharp/Core/GitFetchOptionsWrapper.cs @@ -0,0 +1,37 @@ +using System; + +namespace LibGit2Sharp.Core +{ + /// + /// Git fetch options wrapper. Disposable wrapper for GitFetchOptions + /// + internal class GitFetchOptionsWrapper : IDisposable + { + public GitFetchOptionsWrapper() : this(new GitFetchOptions()) { } + + public GitFetchOptionsWrapper(GitFetchOptions fetchOptions) + { + Options = fetchOptions; + } + + public GitFetchOptions Options { get; private set; } + + #region IDisposable + private bool disposedValue = false; // To detect redundant calls + protected virtual void Dispose(bool disposing) + { + if (disposedValue) + return; + + Options.CustomHeaders.Dispose(); + EncodingMarshaler.Cleanup(Options.ProxyOptions.Url); + disposedValue = true; + } + + public void Dispose() + { + Dispose(true); + } + #endregion + } +} diff --git a/LibGit2Sharp/Core/GitFilter.cs b/LibGit2Sharp/Core/GitFilter.cs index 1ca228e79..72fa2f763 100644 --- a/LibGit2Sharp/Core/GitFilter.cs +++ b/LibGit2Sharp/Core/GitFilter.cs @@ -43,6 +43,7 @@ internal class GitFilter /// before the first use of the filter, so you can defer expensive /// initialization operations (in case libgit2 is being used in a way that doesn't need the filter). /// + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int git_filter_init_fn(IntPtr filter); /// @@ -53,6 +54,7 @@ internal class GitFilter /// will be called once at most and should release resources as needed. /// Typically this function will free the `git_filter` object itself. /// + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void git_filter_shutdown_fn(IntPtr filter); /// @@ -69,6 +71,7 @@ internal class GitFilter /// away before the `apply` callback can use it. If a filter allocates and assigns a value to the `payload`, it will need a `cleanup` /// callback to free the payload. /// + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int git_filter_check_fn( GitFilter gitFilter, IntPtr payload, @@ -85,6 +88,7 @@ public delegate int git_filter_check_fn( /// /// The `payload` value will refer to any payload that was set by the `check` callback. It may be read from or written to as needed. /// + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int git_filter_apply_fn( GitFilter gitFilter, IntPtr payload, @@ -92,6 +96,7 @@ public delegate int git_filter_apply_fn( IntPtr gitBufFrom, IntPtr filterSource); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int git_filter_stream_fn( out IntPtr git_writestream_out, GitFilter self, @@ -104,6 +109,7 @@ public delegate int git_filter_stream_fn( /// after the filter has been applied. If the `check` or `apply` callbacks allocated a `payload` /// to keep per-source filter state, use this callback to free that payload and release resources as required. /// + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void git_filter_cleanup_fn(IntPtr gitFilter, IntPtr payload); } /// diff --git a/LibGit2Sharp/Core/GitObjectLazyGroup.cs b/LibGit2Sharp/Core/GitObjectLazyGroup.cs index 4e0ba384e..f00900837 100644 --- a/LibGit2Sharp/Core/GitObjectLazyGroup.cs +++ b/LibGit2Sharp/Core/GitObjectLazyGroup.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp.Core @@ -21,11 +22,16 @@ protected override void EvaluateInternal(Action evaluator) } } - public static ILazy Singleton(Repository repo, ObjectId id, Func resultSelector) +#if NET + public static ILazy Singleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TResult>(Repository repo, ObjectId id, Func resultSelector, bool throwIfMissing = false) +#else + public static ILazy Singleton(Repository repo, ObjectId id, Func resultSelector, bool throwIfMissing = false) +#endif + { return Singleton(() => { - using (var osw = new ObjectSafeWrapper(id, repo.Handle)) + using (var osw = new ObjectSafeWrapper(id, repo.Handle, throwIfMissing: throwIfMissing)) { return resultSelector(osw.ObjectPtr); } diff --git a/LibGit2Sharp/Core/GitOdbBackend.cs b/LibGit2Sharp/Core/GitOdbBackend.cs index f5335655e..c102c94eb 100644 --- a/LibGit2Sharp/Core/GitOdbBackend.cs +++ b/LibGit2Sharp/Core/GitOdbBackend.cs @@ -8,7 +8,7 @@ internal struct GitOdbBackend { static GitOdbBackend() { - GCHandleOffset = Marshal.OffsetOf(typeof(GitOdbBackend), "GCHandle").ToInt32(); + GCHandleOffset = Marshal.OffsetOf(nameof(GCHandle)).ToInt32(); } public uint Version; @@ -33,7 +33,8 @@ static GitOdbBackend() public exists_prefix_callback ExistsPrefix; public IntPtr Refresh; public foreach_callback Foreach; - public IntPtr Writepack; + public IntPtr WritePack; + public IntPtr WriteMidx; public IntPtr Freshen; public free_callback Free; @@ -55,6 +56,7 @@ static GitOdbBackend() /// [in] A pointer to the backend which is being asked to perform the task. /// [in] The OID which the backend is being asked to look up. /// 0 if successful; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int read_callback( out IntPtr buffer_p, out UIntPtr len_p, @@ -77,6 +79,7 @@ public delegate int read_callback( /// [in] The short-form OID which the backend is being asked to look up. /// [in] The length of the short-form OID (short_oid). /// 0 if successful; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int read_prefix_callback( out GitOid out_oid, out IntPtr buffer_p, @@ -95,6 +98,7 @@ public delegate int read_prefix_callback( /// [in] A pointer to the backend which is being asked to perform the task. /// [in] The OID which the backend is being asked to look up. /// 0 if successful; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int read_header_callback( out UIntPtr len_p, out GitObjectType type_p, @@ -111,6 +115,7 @@ public delegate int read_header_callback( /// [in] The length of the buffer pointed to by data. /// [in] The type of the object. /// 0 if successful; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int write_callback( IntPtr backend, ref GitOid oid, @@ -128,10 +133,11 @@ public delegate int write_callback( /// [in] The length of the object's contents. /// [in] The type of the object being written. /// 0 if successful; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int writestream_callback( out IntPtr stream_out, IntPtr backend, - Int64 length, + long length, GitObjectType type); /// @@ -142,6 +148,7 @@ public delegate int writestream_callback( /// [in] A pointer to the backend which is being asked to perform the task. /// [in] The object ID that the caller is requesting. /// 0 if successful; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int readstream_callback( out IntPtr stream_out, IntPtr backend, @@ -154,6 +161,7 @@ public delegate int readstream_callback( /// [in] A pointer to the backend which is being asked to perform the task. /// [in] The object ID that the caller is requesting. /// True if the object exists; false otherwise + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool exists_callback( IntPtr backend, ref GitOid oid); @@ -169,6 +177,7 @@ public delegate bool exists_callback( /// [in] The short-form OID which the backend is being asked to look up. /// [in] The length of the short-form OID (short_oid). /// 1 if the object exists, 0 if the object doesn't; an error code otherwise. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int exists_prefix_callback( ref GitOid found_oid, IntPtr backend, @@ -182,6 +191,7 @@ public delegate int exists_prefix_callback( /// [in] A pointer to the backend which is being asked to perform the task. /// [in] The callback function to invoke. /// [in] An arbitrary parameter to pass through to the callback + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int foreach_callback( IntPtr backend, foreach_callback_callback cb, @@ -191,6 +201,7 @@ public delegate int foreach_callback( /// The owner of this backend is finished with it. The backend is asked to clean up and shut down. /// /// [in] A pointer to the backend which is being freed. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_callback( IntPtr backend); @@ -200,6 +211,7 @@ public delegate void free_callback( /// The oid of each object in the backing store. /// The arbitrary parameter given to foreach_callback. /// A non-negative result indicates the enumeration should continue. Otherwise, the enumeration should stop. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int foreach_callback_callback( IntPtr oid, IntPtr data); diff --git a/LibGit2Sharp/Core/GitOdbBackendStream.cs b/LibGit2Sharp/Core/GitOdbBackendStream.cs index 30477ecbd..14b126c7a 100644 --- a/LibGit2Sharp/Core/GitOdbBackendStream.cs +++ b/LibGit2Sharp/Core/GitOdbBackendStream.cs @@ -15,15 +15,15 @@ internal class GitOdbBackendStream { static GitOdbBackendStream() { - GCHandleOffset = Marshal.OffsetOf(typeof(GitOdbBackendStream), "GCHandle").ToInt32(); + GCHandleOffset = Marshal.OffsetOf(nameof(GCHandle)).ToInt32(); } public IntPtr Backend; public GitOdbBackendStreamMode Mode; public IntPtr HashCtx; - public Int64 DeclaredSize; - public Int64 ReceivedBytes; + public long DeclaredSize; + public long ReceivedBytes; public read_callback Read; public write_callback Write; @@ -38,18 +38,22 @@ static GitOdbBackendStream() public static int GCHandleOffset; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int read_callback( IntPtr stream, IntPtr buffer, UIntPtr len); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int write_callback( IntPtr stream, IntPtr buffer, UIntPtr len); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int finalize_write_callback(IntPtr stream, ref GitOid oid); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_callback(IntPtr stream); } } diff --git a/LibGit2Sharp/Core/GitProxyOptions.cs b/LibGit2Sharp/Core/GitProxyOptions.cs index b62b8e08f..85d4057ab 100644 --- a/LibGit2Sharp/Core/GitProxyOptions.cs +++ b/LibGit2Sharp/Core/GitProxyOptions.cs @@ -16,8 +16,8 @@ internal struct GitProxyOptions public uint Version; public GitProxyType Type; public IntPtr Url; - public IntPtr CredentialsCb; - public IntPtr CertificateCheck; - public IntPtr CbPayload; + public NativeMethods.git_cred_acquire_cb Credentials; + public NativeMethods.git_transport_certificate_check_cb CertificateCheck; + public IntPtr Payload; } } diff --git a/LibGit2Sharp/Core/GitProxyOptionsWrapper.cs b/LibGit2Sharp/Core/GitProxyOptionsWrapper.cs new file mode 100644 index 000000000..053213e96 --- /dev/null +++ b/LibGit2Sharp/Core/GitProxyOptionsWrapper.cs @@ -0,0 +1,32 @@ +using System; + +namespace LibGit2Sharp.Core +{ + internal class GitProxyOptionsWrapper : IDisposable + { + public GitProxyOptionsWrapper() : this(new GitProxyOptions()) { } + + public GitProxyOptionsWrapper(GitProxyOptions fetchOptions) + { + Options = fetchOptions; + } + + public GitProxyOptions Options { get; private set; } + + private bool disposedValue = false; + + protected virtual void Dispose(bool disposing) + { + if (disposedValue) + return; + + EncodingMarshaler.Cleanup(Options.Url); + disposedValue = true; + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/LibGit2Sharp/Core/GitPushOptions.cs b/LibGit2Sharp/Core/GitPushOptions.cs index f733534d2..ac9a99e1e 100644 --- a/LibGit2Sharp/Core/GitPushOptions.cs +++ b/LibGit2Sharp/Core/GitPushOptions.cs @@ -9,6 +9,8 @@ internal class GitPushOptions public int PackbuilderDegreeOfParallelism; public GitRemoteCallbacks RemoteCallbacks; public GitProxyOptions ProxyOptions; + public RemoteRedirectMode FollowRedirects = RemoteRedirectMode.Initial; public GitStrArrayManaged CustomHeaders; + public GitStrArrayManaged remote_push_options; } } diff --git a/LibGit2Sharp/Core/GitPushOptionsWrapper.cs b/LibGit2Sharp/Core/GitPushOptionsWrapper.cs new file mode 100644 index 000000000..3ccffcf06 --- /dev/null +++ b/LibGit2Sharp/Core/GitPushOptionsWrapper.cs @@ -0,0 +1,37 @@ +using System; + +namespace LibGit2Sharp.Core +{ + /// + /// Git push options wrapper. Disposable wrapper for . + /// + internal class GitPushOptionsWrapper : IDisposable + { + public GitPushOptionsWrapper() : this(new GitPushOptions()) { } + + public GitPushOptionsWrapper(GitPushOptions pushOptions) + { + this.Options = pushOptions; + } + + public GitPushOptions Options { get; private set; } + + #region IDisposable + private bool disposedValue = false; // To detect redundant calls + protected virtual void Dispose(bool disposing) + { + if (disposedValue) + return; + + this.Options.CustomHeaders.Dispose(); + EncodingMarshaler.Cleanup(Options.ProxyOptions.Url); + disposedValue = true; + } + + public void Dispose() + { + Dispose(true); + } + #endregion + } +} diff --git a/LibGit2Sharp/Core/GitRebaseOptions.cs b/LibGit2Sharp/Core/GitRebaseOptions.cs index 3ae4e0ed1..981bfe919 100644 --- a/LibGit2Sharp/Core/GitRebaseOptions.cs +++ b/LibGit2Sharp/Core/GitRebaseOptions.cs @@ -17,5 +17,9 @@ internal class GitRebaseOptions public GitMergeOpts merge_options = new GitMergeOpts { Version = 1 }; public GitCheckoutOpts checkout_options = new GitCheckoutOpts { version = 1 }; + + private IntPtr padding; // TODO: add git_commit_create_cb + + public NativeMethods.commit_signing_callback signing_callback; } } diff --git a/LibGit2Sharp/Core/GitRemoteCallbacks.cs b/LibGit2Sharp/Core/GitRemoteCallbacks.cs index 4c797b596..4900ad562 100644 --- a/LibGit2Sharp/Core/GitRemoteCallbacks.cs +++ b/LibGit2Sharp/Core/GitRemoteCallbacks.cs @@ -33,6 +33,10 @@ internal struct GitRemoteCallbacks internal IntPtr transport; + private IntPtr padding; // TODO: add git_remote_ready_cb + internal IntPtr payload; + + internal NativeMethods.url_resolve_callback resolve_url; } } diff --git a/LibGit2Sharp/Core/GitSmartSubtransport.cs b/LibGit2Sharp/Core/GitSmartSubtransport.cs index d9b3c7545..d4ce6d728 100644 --- a/LibGit2Sharp/Core/GitSmartSubtransport.cs +++ b/LibGit2Sharp/Core/GitSmartSubtransport.cs @@ -8,7 +8,7 @@ internal class GitSmartSubtransport { static GitSmartSubtransport() { - GCHandleOffset = Marshal.OffsetOf(typeof(GitSmartSubtransport), "GCHandle").ToInt32(); + GCHandleOffset = Marshal.OffsetOf(nameof(GCHandle)).ToInt32(); } public action_callback Action; @@ -23,14 +23,17 @@ static GitSmartSubtransport() public static int GCHandleOffset; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int action_callback( out IntPtr stream, IntPtr subtransport, IntPtr url, GitSmartSubtransportAction action); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int close_callback(IntPtr subtransport); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_callback(IntPtr subtransport); } } diff --git a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs index e721c1e79..c8ae4fde7 100644 --- a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs +++ b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs @@ -10,6 +10,7 @@ internal class GitSmartSubtransportRegistration public uint Rpc; public uint Param; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int create_callback( out IntPtr subtransport, IntPtr owner, diff --git a/LibGit2Sharp/Core/GitSmartSubtransportStream.cs b/LibGit2Sharp/Core/GitSmartSubtransportStream.cs index 16155aeba..ae371b980 100644 --- a/LibGit2Sharp/Core/GitSmartSubtransportStream.cs +++ b/LibGit2Sharp/Core/GitSmartSubtransportStream.cs @@ -8,7 +8,7 @@ internal class GitSmartSubtransportStream { static GitSmartSubtransportStream() { - GCHandleOffset = Marshal.OffsetOf(typeof(GitSmartSubtransportStream), "GCHandle").ToInt32(); + GCHandleOffset = Marshal.OffsetOf(nameof(GCHandle)).ToInt32(); } public IntPtr SmartTransport; @@ -25,17 +25,20 @@ static GitSmartSubtransportStream() public static int GCHandleOffset; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int read_callback( IntPtr stream, IntPtr buffer, UIntPtr buf_size, out UIntPtr bytes_read); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int write_callback( IntPtr stream, IntPtr buffer, UIntPtr len); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_callback(IntPtr stream); } } diff --git a/LibGit2Sharp/Core/GitStashApplyOpts.cs b/LibGit2Sharp/Core/GitStashApplyOpts.cs index e7f2be19c..739c40b0c 100644 --- a/LibGit2Sharp/Core/GitStashApplyOpts.cs +++ b/LibGit2Sharp/Core/GitStashApplyOpts.cs @@ -3,6 +3,7 @@ namespace LibGit2Sharp.Core { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int stash_apply_progress_cb(StashApplyProgress progress, IntPtr payload); [StructLayout(LayoutKind.Sequential)] diff --git a/LibGit2Sharp/Core/GitStatusOptions.cs b/LibGit2Sharp/Core/GitStatusOptions.cs index 3e9dbd5d9..d577cefe6 100644 --- a/LibGit2Sharp/Core/GitStatusOptions.cs +++ b/LibGit2Sharp/Core/GitStatusOptions.cs @@ -13,6 +13,8 @@ internal class GitStatusOptions : IDisposable public GitStrArrayManaged PathSpec; + public IntPtr Baseline = IntPtr.Zero; + public void Dispose() { PathSpec.Dispose(); diff --git a/LibGit2Sharp/Core/GitStrArrayNative.cs b/LibGit2Sharp/Core/GitStrArrayNative.cs index 8813f8e6e..01cd18e6e 100644 --- a/LibGit2Sharp/Core/GitStrArrayNative.cs +++ b/LibGit2Sharp/Core/GitStrArrayNative.cs @@ -15,11 +15,11 @@ internal struct GitStrArrayNative : IDisposable /// /// Enumerates each string from the array using the UTF-8 marshaler. /// - public String[] ReadStrings() + public string[] ReadStrings() { var count = checked((int)Array.Count.ToUInt32()); - String[] toReturn = new String[count]; + string[] toReturn = new string[count]; for (int i = 0; i < count; i++) { diff --git a/LibGit2Sharp/Core/GitSubmoduleOptions.cs b/LibGit2Sharp/Core/GitSubmoduleOptions.cs index 59c2b3f80..09a8e8265 100644 --- a/LibGit2Sharp/Core/GitSubmoduleOptions.cs +++ b/LibGit2Sharp/Core/GitSubmoduleOptions.cs @@ -11,8 +11,6 @@ internal struct GitSubmoduleUpdateOptions public GitFetchOptions FetchOptions; - public CheckoutStrategy CloneCheckoutStrategy; - public int AllowFetch; } } diff --git a/LibGit2Sharp/Core/GitWorktree.cs b/LibGit2Sharp/Core/GitWorktree.cs new file mode 100644 index 000000000..2e17bc20d --- /dev/null +++ b/LibGit2Sharp/Core/GitWorktree.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + /** + * Flags which can be passed to git_worktree_prune to alter its + * behavior. + */ + [Flags] + internal enum GitWorktreePruneOptionFlags : uint + { + /// + /// Prune working tree even if working tree is valid + /// + GIT_WORKTREE_PRUNE_VALID = (1u << 0), + + /// + /// Prune working tree even if it is locked + /// + GIT_WORKTREE_PRUNE_LOCKED = (1u << 1), + + /// + /// Prune checked out working tree + /// + GIT_WORKTREE_PRUNE_WORKING_TREE = (1u << 2) + } + + + [StructLayout(LayoutKind.Sequential)] + internal class git_worktree_add_options + { + public uint version = 1; + + public int locked; + + public int checkout_existing; + + public IntPtr @ref = IntPtr.Zero; + + public GitCheckoutOpts checkoutOpts = new GitCheckoutOpts + { + version = 1, + checkout_strategy = CheckoutStrategy.GIT_CHECKOUT_SAFE + }; + } + + [StructLayout(LayoutKind.Sequential)] + internal class git_worktree_prune_options + { + public uint version = 1; + + public GitWorktreePruneOptionFlags flags; + } +} diff --git a/LibGit2Sharp/Core/GitWriteStream.cs b/LibGit2Sharp/Core/GitWriteStream.cs index a53e7bd74..891a765fb 100644 --- a/LibGit2Sharp/Core/GitWriteStream.cs +++ b/LibGit2Sharp/Core/GitWriteStream.cs @@ -15,8 +15,13 @@ internal struct GitWriteStream [MarshalAs(UnmanagedType.FunctionPtr)] public free_fn free; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int write_fn(IntPtr stream, IntPtr buffer, UIntPtr len); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int close_fn(IntPtr stream); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void free_fn(IntPtr stream); } } diff --git a/LibGit2Sharp/Core/Handles/Libgit2Object.cs b/LibGit2Sharp/Core/Handles/Libgit2Object.cs index cbb431a98..a96d99e10 100644 --- a/LibGit2Sharp/Core/Handles/Libgit2Object.cs +++ b/LibGit2Sharp/Core/Handles/Libgit2Object.cs @@ -1,13 +1,13 @@ // This activates a lightweight mode which will help put under the light -// incorrectly released handles by outputing a warning message in the console. +// incorrectly released handles by outputting a warning message in the console. // -// This should be activated when tests are being run of the CI server. +// This should be activated when tests are being run on the CI server. // // Uncomment the line below or add a conditional symbol to activate this mode -// #define LEAKS_IDENTIFYING +//#define LEAKS_IDENTIFYING -// This activates a more throrough mode which will show the stack trace of the +// This activates a more thorough mode which will show the stack trace of the // allocation code path for each handle that has been improperly released. // // This should be manually activated when some warnings have been raised as @@ -15,17 +15,17 @@ // // Uncomment the line below or add a conditional symbol to activate this mode -// #define LEAKS_TRACKING +//#define LEAKS_TRACKING using System; -using System.Linq; -using System.Diagnostics; -using System.Globalization; -using System.Collections.Generic; +using Microsoft.Win32.SafeHandles; #if LEAKS_IDENTIFYING namespace LibGit2Sharp.Core { + using System.Collections.Generic; + using System.Linq; + /// /// Holds leaked handle type names reported by /// @@ -78,67 +78,42 @@ public static IEnumerable TypeNames namespace LibGit2Sharp.Core.Handles { - internal unsafe abstract class Libgit2Object : IDisposable +#if LEAKS_TRACKING + using System.Diagnostics; + using System.Globalization; +#endif + + internal unsafe abstract class Libgit2Object : SafeHandleZeroOrMinusOneIsInvalid { #if LEAKS_TRACKING private readonly string trace; private readonly Guid id; #endif - protected void* ptr; - - internal void* Handle + internal unsafe Libgit2Object(void* ptr, bool owned) + : this(new IntPtr(ptr), owned) { - get - { - return ptr; - } } - bool owned; - bool disposed; - - internal unsafe Libgit2Object(void* handle, bool owned) + internal unsafe Libgit2Object(IntPtr ptr, bool owned) + : base(owned) { - this.ptr = handle; - this.owned = owned; + SetHandle(ptr); #if LEAKS_TRACKING id = Guid.NewGuid(); Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Allocating {0} handle ({1})", GetType().Name, id)); + trace = new StackTrace(2, true).ToString(); #endif } - internal unsafe Libgit2Object(IntPtr ptr, bool owned) - : this(ptr.ToPointer(), owned) - { - } + internal IntPtr AsIntPtr() => DangerousGetHandle(); - ~Libgit2Object() + protected override void Dispose(bool disposing) { - Dispose(false); - } - - internal bool IsNull - { - get - { - return ptr == null; - } - } - - internal IntPtr AsIntPtr() - { - return new IntPtr(ptr); - } - - public abstract void Free(); - - void Dispose(bool disposing) - { - #if LEAKS_IDENTIFYING - bool leaked = !disposing && ptr != null; +#if LEAKS_IDENTIFYING + bool leaked = !disposing && DangerousGetHandle() != IntPtr.Zero; if (leaked) { @@ -146,17 +121,7 @@ void Dispose(bool disposing) } #endif - if (!disposed) - { - if (owned) - { - Free(); - } - - ptr = null; - } - - disposed = true; + base.Dispose(disposing); #if LEAKS_TRACKING if (!leaked) @@ -171,11 +136,6 @@ void Dispose(bool disposing) } #endif } - - public void Dispose() - { - Dispose(true); - } } } diff --git a/LibGit2Sharp/Core/Handles/Objects.cs b/LibGit2Sharp/Core/Handles/Objects.cs index f904b75b9..ddca49bee 100644 --- a/LibGit2Sharp/Core/Handles/Objects.cs +++ b/LibGit2Sharp/Core/Handles/Objects.cs @@ -6,8 +6,8 @@ namespace LibGit2Sharp.Core.Handles internal unsafe class TreeEntryHandle : Libgit2Object { - internal TreeEntryHandle(git_tree_entry *ptr, bool owned) - : base((void *) ptr, owned) + internal TreeEntryHandle(git_tree_entry* ptr, bool owned) + : base(ptr, owned) { } @@ -16,21 +16,23 @@ internal TreeEntryHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_tree_entry_free((git_tree_entry*) ptr); + NativeMethods.git_tree_entry_free((git_tree_entry*)AsIntPtr()); + + return true; } public static implicit operator git_tree_entry*(TreeEntryHandle handle) { - return (git_tree_entry*) handle.Handle; + return (git_tree_entry*)handle.AsIntPtr(); } } internal unsafe class ReferenceHandle : Libgit2Object { - internal ReferenceHandle(git_reference *ptr, bool owned) - : base((void *) ptr, owned) + internal ReferenceHandle(git_reference* ptr, bool owned) + : base(ptr, owned) { } @@ -39,21 +41,23 @@ internal ReferenceHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_reference_free((git_reference*) ptr); + NativeMethods.git_reference_free((git_reference*)AsIntPtr()); + + return true; } public static implicit operator git_reference*(ReferenceHandle handle) { - return (git_reference*) handle.Handle; + return (git_reference*)handle.AsIntPtr(); } } internal unsafe class RepositoryHandle : Libgit2Object { - internal RepositoryHandle(git_repository *ptr, bool owned) - : base((void *) ptr, owned) + internal RepositoryHandle(git_repository* ptr, bool owned) + : base(ptr, owned) { } @@ -62,21 +66,23 @@ internal RepositoryHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_repository_free((git_repository*) ptr); + NativeMethods.git_repository_free((git_repository*)AsIntPtr()); + + return true; } public static implicit operator git_repository*(RepositoryHandle handle) { - return (git_repository*) handle.Handle; + return (git_repository*)handle.AsIntPtr(); } } internal unsafe class SignatureHandle : Libgit2Object { - internal SignatureHandle(git_signature *ptr, bool owned) - : base((void *) ptr, owned) + internal SignatureHandle(git_signature* ptr, bool owned) + : base(ptr, owned) { } @@ -85,21 +91,23 @@ internal SignatureHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_signature_free((git_signature*) ptr); + NativeMethods.git_signature_free((git_signature*)AsIntPtr()); + + return true; } public static implicit operator git_signature*(SignatureHandle handle) { - return (git_signature*) handle.Handle; + return (git_signature*)handle.AsIntPtr(); } } internal unsafe class StatusListHandle : Libgit2Object { - internal StatusListHandle(git_status_list *ptr, bool owned) - : base((void *) ptr, owned) + internal StatusListHandle(git_status_list* ptr, bool owned) + : base(ptr, owned) { } @@ -108,21 +116,23 @@ internal StatusListHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_status_list_free((git_status_list*) ptr); + NativeMethods.git_status_list_free((git_status_list*)AsIntPtr()); + + return true; } public static implicit operator git_status_list*(StatusListHandle handle) { - return (git_status_list*) handle.Handle; + return (git_status_list*)handle.AsIntPtr(); } } internal unsafe class BlameHandle : Libgit2Object { - internal BlameHandle(git_blame *ptr, bool owned) - : base((void *) ptr, owned) + internal BlameHandle(git_blame* ptr, bool owned) + : base(ptr, owned) { } @@ -131,21 +141,23 @@ internal BlameHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_blame_free((git_blame*) ptr); + NativeMethods.git_blame_free((git_blame*)AsIntPtr()); + + return true; } public static implicit operator git_blame*(BlameHandle handle) { - return (git_blame*) handle.Handle; + return (git_blame*)handle.AsIntPtr(); } } internal unsafe class DiffHandle : Libgit2Object { - internal DiffHandle(git_diff *ptr, bool owned) - : base((void *) ptr, owned) + internal DiffHandle(git_diff* ptr, bool owned) + : base(ptr, owned) { } @@ -154,21 +166,23 @@ internal DiffHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_diff_free((git_diff*) ptr); + NativeMethods.git_diff_free((git_diff*)AsIntPtr()); + + return true; } public static implicit operator git_diff*(DiffHandle handle) { - return (git_diff*) handle.Handle; + return (git_diff*)handle.AsIntPtr(); } } internal unsafe class PatchHandle : Libgit2Object { - internal PatchHandle(git_patch *ptr, bool owned) - : base((void *) ptr, owned) + internal PatchHandle(git_patch* ptr, bool owned) + : base(ptr, owned) { } @@ -177,21 +191,23 @@ internal PatchHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_patch_free((git_patch*) ptr); + NativeMethods.git_patch_free((git_patch*)AsIntPtr()); + + return true; } public static implicit operator git_patch*(PatchHandle handle) { - return (git_patch*) handle.Handle; + return (git_patch*)handle.AsIntPtr(); } } internal unsafe class ConfigurationHandle : Libgit2Object { - internal ConfigurationHandle(git_config *ptr, bool owned) - : base((void *) ptr, owned) + internal ConfigurationHandle(git_config* ptr, bool owned) + : base(ptr, owned) { } @@ -200,21 +216,23 @@ internal ConfigurationHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_config_free((git_config*) ptr); + NativeMethods.git_config_free((git_config*)AsIntPtr()); + + return true; } public static implicit operator git_config*(ConfigurationHandle handle) { - return (git_config*) handle.Handle; + return (git_config*)handle.AsIntPtr(); } } internal unsafe class ConflictIteratorHandle : Libgit2Object { - internal ConflictIteratorHandle(git_index_conflict_iterator *ptr, bool owned) - : base((void *) ptr, owned) + internal ConflictIteratorHandle(git_index_conflict_iterator* ptr, bool owned) + : base(ptr, owned) { } @@ -223,21 +241,23 @@ internal ConflictIteratorHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_index_conflict_iterator_free((git_index_conflict_iterator*) ptr); + NativeMethods.git_index_conflict_iterator_free((git_index_conflict_iterator*)AsIntPtr()); + + return true; } public static implicit operator git_index_conflict_iterator*(ConflictIteratorHandle handle) { - return (git_index_conflict_iterator*) handle.Handle; + return (git_index_conflict_iterator*)handle.AsIntPtr(); } } internal unsafe class IndexHandle : Libgit2Object { - internal IndexHandle(git_index *ptr, bool owned) - : base((void *) ptr, owned) + internal IndexHandle(git_index* ptr, bool owned) + : base(ptr, owned) { } @@ -246,21 +266,23 @@ internal IndexHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_index_free((git_index*) ptr); + NativeMethods.git_index_free((git_index*)AsIntPtr()); + + return true; } public static implicit operator git_index*(IndexHandle handle) { - return (git_index*) handle.Handle; + return (git_index*)handle.AsIntPtr(); } } internal unsafe class ReflogHandle : Libgit2Object { - internal ReflogHandle(git_reflog *ptr, bool owned) - : base((void *) ptr, owned) + internal ReflogHandle(git_reflog* ptr, bool owned) + : base(ptr, owned) { } @@ -269,21 +291,23 @@ internal ReflogHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_reflog_free((git_reflog*) ptr); + NativeMethods.git_reflog_free((git_reflog*)AsIntPtr()); + + return true; } public static implicit operator git_reflog*(ReflogHandle handle) { - return (git_reflog*) handle.Handle; + return (git_reflog*)handle.AsIntPtr(); } } internal unsafe class TreeBuilderHandle : Libgit2Object { - internal TreeBuilderHandle(git_treebuilder *ptr, bool owned) - : base((void *) ptr, owned) + internal TreeBuilderHandle(git_treebuilder* ptr, bool owned) + : base(ptr, owned) { } @@ -292,21 +316,23 @@ internal TreeBuilderHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_treebuilder_free((git_treebuilder*) ptr); + NativeMethods.git_treebuilder_free((git_treebuilder*)AsIntPtr()); + + return true; } public static implicit operator git_treebuilder*(TreeBuilderHandle handle) { - return (git_treebuilder*) handle.Handle; + return (git_treebuilder*)handle.AsIntPtr(); } } internal unsafe class PackBuilderHandle : Libgit2Object { - internal PackBuilderHandle(git_packbuilder *ptr, bool owned) - : base((void *) ptr, owned) + internal PackBuilderHandle(git_packbuilder* ptr, bool owned) + : base(ptr, owned) { } @@ -315,21 +341,23 @@ internal PackBuilderHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_packbuilder_free((git_packbuilder*) ptr); + NativeMethods.git_packbuilder_free((git_packbuilder*)AsIntPtr()); + + return true; } public static implicit operator git_packbuilder*(PackBuilderHandle handle) { - return (git_packbuilder*) handle.Handle; + return (git_packbuilder*)handle.AsIntPtr(); } } internal unsafe class NoteHandle : Libgit2Object { - internal NoteHandle(git_note *ptr, bool owned) - : base((void *) ptr, owned) + internal NoteHandle(git_note* ptr, bool owned) + : base(ptr, owned) { } @@ -338,21 +366,23 @@ internal NoteHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_note_free((git_note*) ptr); + NativeMethods.git_note_free((git_note*)AsIntPtr()); + + return true; } public static implicit operator git_note*(NoteHandle handle) { - return (git_note*) handle.Handle; + return (git_note*)handle.AsIntPtr(); } } internal unsafe class DescribeResultHandle : Libgit2Object { - internal DescribeResultHandle(git_describe_result *ptr, bool owned) - : base((void *) ptr, owned) + internal DescribeResultHandle(git_describe_result* ptr, bool owned) + : base(ptr, owned) { } @@ -361,21 +391,23 @@ internal DescribeResultHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_describe_result_free((git_describe_result*) ptr); + NativeMethods.git_describe_result_free((git_describe_result*)AsIntPtr()); + + return true; } public static implicit operator git_describe_result*(DescribeResultHandle handle) { - return (git_describe_result*) handle.Handle; + return (git_describe_result*)handle.AsIntPtr(); } } internal unsafe class SubmoduleHandle : Libgit2Object { - internal SubmoduleHandle(git_submodule *ptr, bool owned) - : base((void *) ptr, owned) + internal SubmoduleHandle(git_submodule* ptr, bool owned) + : base(ptr, owned) { } @@ -384,21 +416,23 @@ internal SubmoduleHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_submodule_free((git_submodule*) ptr); + NativeMethods.git_submodule_free((git_submodule*)AsIntPtr()); + + return true; } public static implicit operator git_submodule*(SubmoduleHandle handle) { - return (git_submodule*) handle.Handle; + return (git_submodule*)handle.AsIntPtr(); } } internal unsafe class AnnotatedCommitHandle : Libgit2Object { - internal AnnotatedCommitHandle(git_annotated_commit *ptr, bool owned) - : base((void *) ptr, owned) + internal AnnotatedCommitHandle(git_annotated_commit* ptr, bool owned) + : base(ptr, owned) { } @@ -407,21 +441,23 @@ internal AnnotatedCommitHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_annotated_commit_free((git_annotated_commit*) ptr); + NativeMethods.git_annotated_commit_free((git_annotated_commit*)AsIntPtr()); + + return true; } public static implicit operator git_annotated_commit*(AnnotatedCommitHandle handle) { - return (git_annotated_commit*) handle.Handle; + return (git_annotated_commit*)handle.AsIntPtr(); } } internal unsafe class ObjectDatabaseHandle : Libgit2Object { - internal ObjectDatabaseHandle(git_odb *ptr, bool owned) - : base((void *) ptr, owned) + internal ObjectDatabaseHandle(git_odb* ptr, bool owned) + : base(ptr, owned) { } @@ -430,21 +466,23 @@ internal ObjectDatabaseHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_odb_free((git_odb*) ptr); + NativeMethods.git_odb_free((git_odb*)AsIntPtr()); + + return true; } public static implicit operator git_odb*(ObjectDatabaseHandle handle) { - return (git_odb*) handle.Handle; + return (git_odb*)handle.AsIntPtr(); } } internal unsafe class RevWalkerHandle : Libgit2Object { - internal RevWalkerHandle(git_revwalk *ptr, bool owned) - : base((void *) ptr, owned) + internal RevWalkerHandle(git_revwalk* ptr, bool owned) + : base(ptr, owned) { } @@ -453,21 +491,23 @@ internal RevWalkerHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_revwalk_free((git_revwalk*) ptr); + NativeMethods.git_revwalk_free((git_revwalk*)AsIntPtr()); + + return true; } public static implicit operator git_revwalk*(RevWalkerHandle handle) { - return (git_revwalk*) handle.Handle; + return (git_revwalk*)handle.AsIntPtr(); } } internal unsafe class RemoteHandle : Libgit2Object { - internal RemoteHandle(git_remote *ptr, bool owned) - : base((void *) ptr, owned) + internal RemoteHandle(git_remote* ptr, bool owned) + : base(ptr, owned) { } @@ -476,21 +516,23 @@ internal RemoteHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_remote_free((git_remote*) ptr); + NativeMethods.git_remote_free((git_remote*)AsIntPtr()); + + return true; } public static implicit operator git_remote*(RemoteHandle handle) { - return (git_remote*) handle.Handle; + return (git_remote*)handle.AsIntPtr(); } } internal unsafe class ObjectHandle : Libgit2Object { - internal ObjectHandle(git_object *ptr, bool owned) - : base((void *) ptr, owned) + internal ObjectHandle(git_object* ptr, bool owned) + : base(ptr, owned) { } @@ -499,21 +541,23 @@ internal ObjectHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_object_free((git_object*) ptr); + NativeMethods.git_object_free((git_object*)AsIntPtr()); + + return true; } public static implicit operator git_object*(ObjectHandle handle) { - return (git_object*) handle.Handle; + return (git_object*)handle.AsIntPtr(); } } internal unsafe class RebaseHandle : Libgit2Object { - internal RebaseHandle(git_rebase *ptr, bool owned) - : base((void *) ptr, owned) + internal RebaseHandle(git_rebase* ptr, bool owned) + : base(ptr, owned) { } @@ -522,21 +566,23 @@ internal RebaseHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_rebase_free((git_rebase*) ptr); + NativeMethods.git_rebase_free((git_rebase*)AsIntPtr()); + + return true; } public static implicit operator git_rebase*(RebaseHandle handle) { - return (git_rebase*) handle.Handle; + return (git_rebase*)handle.AsIntPtr(); } } internal unsafe class OdbStreamHandle : Libgit2Object { - internal OdbStreamHandle(git_odb_stream *ptr, bool owned) - : base((void *) ptr, owned) + internal OdbStreamHandle(git_odb_stream* ptr, bool owned) + : base(ptr, owned) { } @@ -545,14 +591,41 @@ internal OdbStreamHandle(IntPtr ptr, bool owned) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.git_odb_stream_free((git_odb_stream*) ptr); + NativeMethods.git_odb_stream_free((git_odb_stream*)AsIntPtr()); + + return true; } public static implicit operator git_odb_stream*(OdbStreamHandle handle) { - return (git_odb_stream*) handle.Handle; + return (git_odb_stream*)handle.AsIntPtr(); + } + } + + internal unsafe class WorktreeHandle : Libgit2Object + { + internal WorktreeHandle(git_worktree* ptr, bool owned) + : base(ptr, owned) + { + } + + internal WorktreeHandle(IntPtr ptr, bool owned) + : base(ptr, owned) + { + } + + protected override bool ReleaseHandle() + { + NativeMethods.git_worktree_free((git_worktree*)AsIntPtr()); + + return true; + } + + public static implicit operator git_worktree*(WorktreeHandle handle) + { + return (git_worktree*)handle.AsIntPtr(); } } diff --git a/LibGit2Sharp/Core/Handles/Objects.tt b/LibGit2Sharp/Core/Handles/Objects.tt index 3ecfe811e..e522bd859 100644 --- a/LibGit2Sharp/Core/Handles/Objects.tt +++ b/LibGit2Sharp/Core/Handles/Objects.tt @@ -36,6 +36,7 @@ var cNames = new[] { "git_object", "git_rebase", "git_odb_stream", + "git_worktree", }; var csNames = new[] { @@ -63,6 +64,7 @@ var csNames = new[] { "ObjectHandle", "RebaseHandle", "OdbStreamHandle", + "WorktreeHandle" }; for (var i = 0; i < cNames.Length; i++) @@ -70,8 +72,8 @@ for (var i = 0; i < cNames.Length; i++) #> internal unsafe class <#= csNames[i] #> : Libgit2Object { - internal <#= csNames[i] #>(<#= cNames[i] #> *ptr, bool owned) - : base((void *) ptr, owned) + internal <#= csNames[i] #>(<#= cNames[i] #>* ptr, bool owned) + : base(ptr, owned) { } @@ -80,14 +82,16 @@ for (var i = 0; i < cNames.Length; i++) { } - public override void Free() + protected override bool ReleaseHandle() { - NativeMethods.<#= cNames[i] #>_free((<#= cNames[i] #>*) ptr); + NativeMethods.<#= cNames[i] #>_free((<#= cNames[i] #>*)AsIntPtr()); + + return true; } public static implicit operator <#= cNames[i] #>*(<#= csNames[i] #> handle) { - return (<#= cNames[i] #>*) handle.Handle; + return (<#= cNames[i] #>*)handle.AsIntPtr(); } } diff --git a/LibGit2Sharp/Core/HistoryRewriter.cs b/LibGit2Sharp/Core/HistoryRewriter.cs index c4cc2be8b..094d5ca1c 100644 --- a/LibGit2Sharp/Core/HistoryRewriter.cs +++ b/LibGit2Sharp/Core/HistoryRewriter.cs @@ -170,7 +170,7 @@ private Reference RewriteReference( if (repo.Refs.Resolve(backupName) != null) { - throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Can't back up reference '{0}' - '{1}' already exists", oldRef.CanonicalName, backupName)); diff --git a/LibGit2Sharp/Core/LazyGroup.cs b/LibGit2Sharp/Core/LazyGroup.cs index d8b13fa42..bcd160290 100644 --- a/LibGit2Sharp/Core/LazyGroup.cs +++ b/LibGit2Sharp/Core/LazyGroup.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace LibGit2Sharp.Core { @@ -44,7 +45,11 @@ public void Evaluate() protected abstract void EvaluateInternal(Action evaluator); +#if NET + protected static ILazy Singleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TResult>(Func resultSelector) +#else protected static ILazy Singleton(Func resultSelector) +#endif { return new LazyWrapper(resultSelector); } @@ -90,7 +95,11 @@ void IEvaluator.Evaluate(TInput input) } } +#if NET + protected class LazyWrapper<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TType> : Lazy, ILazy +#else protected class LazyWrapper : Lazy, ILazy +#endif { public LazyWrapper(Func evaluator) : base(evaluator) diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index c69bc1f8b..cbb850b16 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -1,151 +1,237 @@ using System; -using System.Globalization; using System.IO; +#if NET +using System.Reflection; +#endif using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; -using System.Threading; using LibGit2Sharp.Core.Handles; -// ReSharper disable InconsistentNaming +// Restrict the set of directories where the native library is loaded from to safe directories. +[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.SafeDirectories)] + namespace LibGit2Sharp.Core { internal static class NativeMethods { public const uint GIT_PATH_MAX = 4096; private const string libgit2 = NativeDllName.Name; - // This is here to keep the pointer alive - #pragma warning disable 0414 - private static readonly LibraryLifetimeObject lifetimeObject; - #pragma warning restore 0414 - private static int handlesCount; - - /// - /// Internal hack to ensure that the call to git_threads_shutdown is called after all handle finalizers - /// have run to completion ensuring that no dangling git-related finalizer runs after git_threads_shutdown. - /// There should never be more than one instance of this object per AppDomain. - /// - private sealed class LibraryLifetimeObject : CriticalFinalizerObject + + // An object tied to the lifecycle of the NativeMethods static class. + // This will handle initialization and shutdown of the underlying + // native library. + private static NativeShutdownObject shutdownObject; + + static NativeMethods() { - // Ensure mono can JIT the .cctor and adjust the PATH before trying to load the native library. - // See https://github.com/libgit2/libgit2sharp/pull/190 - [MethodImpl(MethodImplOptions.NoInlining)] - public LibraryLifetimeObject() + if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore()) { - int res = git_libgit2_init(); - Ensure.Int32Result(res); - if (res == 1) + // Use NativeLibrary when available. + if (!TryUseNativeLibrary()) { - // Ignore the error that this propagates. Call it in case openssl is being used. - git_openssl_set_locking(); + // NativeLibrary is not available, fall back. + + // Use GlobalSettings.NativeLibraryPath when set. + // Try to load the .dll from the path explicitly. + // If this call succeeds further DllImports will find the library loaded and not attempt to load it again. + // If it fails the next DllImport will load the library from safe directories. + string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath(); + + if (nativeLibraryPath != null) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + + { + LoadWindowsLibrary(nativeLibraryPath); + } + else + { + LoadUnixLibrary(nativeLibraryPath, RTLD_NOW); + } + } } - AddHandle(); } - ~LibraryLifetimeObject() + InitializeNativeLibrary(); + } + + private static string GetGlobalSettingsNativeLibraryPath() + { + string nativeLibraryDir = GlobalSettings.GetAndLockNativeLibraryPath(); + + if (nativeLibraryDir == null) { - RemoveHandle(); + return null; } + + return Path.Combine(nativeLibraryDir, libgit2 + Platform.GetNativeLibraryExtension()); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - internal static void AddHandle() +#if NETFRAMEWORK + private static bool TryUseNativeLibrary() => false; +#else + private static bool TryUseNativeLibrary() { - Interlocked.Increment(ref handlesCount); + NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, ResolveDll); + + return true; } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - internal static void RemoveHandle() + private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) { - int count = Interlocked.Decrement(ref handlesCount); - if (count == 0) + IntPtr handle = IntPtr.Zero; + + if (libraryName == libgit2) { - git_libgit2_shutdown(); + // Use GlobalSettings.NativeLibraryPath when set. + string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath(); + + if (nativeLibraryPath != null && NativeLibrary.TryLoad(nativeLibraryPath, out handle)) + { + return handle; + } + + // Use Default DllImport resolution. + if (NativeLibrary.TryLoad(libraryName, assembly, searchPath, out handle)) + { + return handle; + } + + // We carry a number of .so files for Linux which are linked against various + // libc/OpenSSL libraries. Try them out. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + // The libraries are located at 'runtimes//native/lib{libraryName}.so' + // The ends with the processor architecture. e.g. fedora-x64. + string assemblyDirectory = Path.GetDirectoryName(AppContext.BaseDirectory); + string processorArchitecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); + string runtimesDirectory = Path.Combine(assemblyDirectory, "runtimes"); + + if (Directory.Exists(runtimesDirectory)) + { + foreach (var runtimeFolder in Directory.GetDirectories(runtimesDirectory, $"*-{processorArchitecture}")) + { + string libPath = Path.Combine(runtimeFolder, "native", $"lib{libraryName}.so"); + + if (NativeLibrary.TryLoad(libPath, out handle)) + { + return handle; + } + } + } + } } + + return handle; } +#endif - static NativeMethods() + public const int RTLD_NOW = 0x002; + + [DllImport("libdl", EntryPoint = "dlopen")] + private static extern IntPtr LoadUnixLibrary(string path, int flags); + + [DllImport("kernel32", EntryPoint = "LoadLibrary")] + private static extern IntPtr LoadWindowsLibrary(string path); + + // Avoid inlining this method because otherwise mono's JITter may try + // to load the library _before_ we've configured the path. + [MethodImpl(MethodImplOptions.NoInlining)] + private static void InitializeNativeLibrary() { - if (Platform.OperatingSystem == OperatingSystemType.Windows) + int initCounter; + try { - string nativeLibraryPath = GlobalSettings.GetAndLockNativeLibraryPath(); - - string path = Path.Combine(nativeLibraryPath, Platform.ProcessorArchitecture); + } + finally // avoid thread aborts + { + // Initialization can be called multiple times as long as there is a corresponding shutdown to each initialization. + initCounter = git_libgit2_init(); + shutdownObject = new NativeShutdownObject(); + } - const string pathEnvVariable = "PATH"; - Environment.SetEnvironmentVariable(pathEnvVariable, - String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", path, Path.PathSeparator, Environment.GetEnvironmentVariable(pathEnvVariable))); + // Configure the OpenSSL locking on the first initialization of the library in the current process. + if (initCounter == 1) + { + git_openssl_set_locking(); } + } - // See LibraryLifetimeObject description. - lifetimeObject = new LibraryLifetimeObject(); + // Shutdown the native library in a finalizer. + private sealed class NativeShutdownObject : CriticalFinalizerObject + { + ~NativeShutdownObject() + { + git_libgit2_shutdown(); + } } - [DllImport(libgit2)] - internal static extern unsafe GitError* giterr_last(); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe GitError* git_error_last(); - [DllImport(libgit2)] - internal static extern void giterr_set_str( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_error_set_str( GitErrorCategory error_class, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string errorString); - [DllImport(libgit2)] - internal static extern void giterr_set_oom(); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern void git_error_set_oom(); - [DllImport(libgit2)] - internal static extern unsafe UInt32 git_blame_get_hunk_count(git_blame* blame); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe uint git_blame_get_hunk_count(git_blame* blame); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_blame_hunk* git_blame_get_hunk_byindex( - git_blame* blame, UInt32 index); + git_blame* blame, uint index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_blame_file( out git_blame* blame, git_repository* repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path, git_blame_options options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_blame_free(git_blame* blame); - [DllImport(libgit2)] - internal static extern unsafe int git_blob_create_fromdisk( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_blob_create_from_disk( ref GitOid id, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); - [DllImport(libgit2)] - internal static extern unsafe int git_blob_create_fromworkdir( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_blob_create_from_workdir( ref GitOid id, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath relative_path); - [DllImport(libgit2)] - internal static extern unsafe int git_blob_create_fromstream( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_blob_create_from_stream( out IntPtr stream, git_repository* repositoryPtr, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath hintpath); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string hintpath); - [DllImport(libgit2)] - internal static extern unsafe int git_blob_create_fromstream_commit( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_blob_create_from_stream_commit( ref GitOid oid, IntPtr stream); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_blob_filtered_content( GitBuf buf, git_object* blob, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath as_path, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string as_path, [MarshalAs(UnmanagedType.Bool)] bool check_for_binary_data); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe IntPtr git_blob_rawcontent(git_object* blob); - [DllImport(libgit2)] - internal static extern unsafe Int64 git_blob_rawsize(git_object* blob); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe long git_blob_rawsize(git_object* blob); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_branch_create_from_annotated( out git_reference* ref_out, git_repository* repo, @@ -153,45 +239,53 @@ internal static extern unsafe int git_branch_create_from_annotated( git_annotated_commit* target, [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_branch_delete( git_reference* reference); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int branch_foreach_callback( IntPtr branch_name, GitBranchType branch_type, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_branch_iterator_free( IntPtr iterator); - [DllImport(libgit2)] - internal static extern int git_branch_iterator_new( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_branch_iterator_new( out IntPtr iter_out, IntPtr repo, GitBranchType branch_type); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_branch_move( out git_reference* ref_out, git_reference* reference, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string new_branch_name, [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_branch_next( out IntPtr ref_out, out GitBranchType type_out, IntPtr iter); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_branch_remote_name( GitBuf buf, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string canonical_branch_name); - [DllImport(libgit2)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int commit_signing_callback( + IntPtr signature, + IntPtr signature_field, + IntPtr commit_content, + IntPtr payload); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_rebase_init( out git_rebase* rebase, git_repository* repo, @@ -200,31 +294,31 @@ internal static extern unsafe int git_rebase_init( git_annotated_commit* onto, GitRebaseOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_rebase_open( out git_rebase* rebase, git_repository* repo, GitRebaseOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_rebase_operation_entrycount( git_rebase* rebase); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_rebase_operation_current( git_rebase* rebase); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_rebase_operation* git_rebase_operation_byindex( git_rebase* rebase, UIntPtr index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_rebase_next( out git_rebase_operation* operation, git_rebase* rebase); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_rebase_commit( ref GitOid id, git_rebase* rebase, @@ -233,64 +327,65 @@ internal static extern unsafe int git_rebase_commit( IntPtr message_encoding, IntPtr message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_rebase_abort( git_rebase* rebase); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_rebase_finish( git_rebase* repo, git_signature* signature); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_rebase_free(git_rebase* rebase); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_rename( ref GitStrArray problems, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string old_name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string new_name); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_remote_rename_problem_cb( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] string problematic_refspec, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_branch_upstream_name( GitBuf buf, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string referenceName); - [DllImport(libgit2)] - internal static extern void git_buf_free(GitBuf buf); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern void git_buf_dispose(GitBuf buf); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_checkout_tree( git_repository* repo, git_object* treeish, ref GitCheckoutOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_checkout_index( git_repository* repo, git_object* treeish, ref GitCheckoutOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_clone( out git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string origin_url, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath workdir_path, ref GitCloneOptions opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_signature* git_commit_author(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_signature* git_commit_committer(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_commit_create_from_ids( out GitOid id, git_repository* repo, @@ -301,9 +396,9 @@ internal static extern unsafe int git_commit_create_from_ids( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string message, ref GitOid tree, UIntPtr parentCount, - [MarshalAs(UnmanagedType.LPArray)] [In] IntPtr[] parents); + [MarshalAs(UnmanagedType.LPArray)][In] IntPtr[] parents); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_commit_create_buffer( GitBuf res, git_repository* repo, @@ -315,7 +410,7 @@ internal static extern unsafe int git_commit_create_buffer( UIntPtr parent_count, IntPtr* parents /* git_commit** originally */); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_commit_create_with_signature( out GitOid id, git_repository* repo, @@ -323,154 +418,162 @@ internal static extern unsafe int git_commit_create_with_signature( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string signature_field); - - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_commit_message(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_commit_summary(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_commit_message_encoding(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_commit_parent_id(git_object* commit, uint n); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe uint git_commit_parentcount(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_commit_tree_id(git_object* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_commit_extract_signature( GitBuf signature, GitBuf signed_data, - git_repository *repo, + git_repository* repo, ref GitOid commit_id, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string field); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_delete_entry( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_lock(out IntPtr txn, git_config* config); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_delete_multivar( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_config_set_multivar( + git_config* cfg, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string value); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_find_global(GitBuf global_config_path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_find_system(GitBuf system_config_path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_find_xdg(GitBuf xdg_config_path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_find_programdata(GitBuf programdata_config_path); - [DllImport(libgit2)] - internal static extern unsafe void git_config_free(git_config *cfg); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe void git_config_free(git_config* cfg); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_config_entry_free(GitConfigEntry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_get_entry( out GitConfigEntry* entry, git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_add_file_ondisk( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, uint level, + git_repository* repo, [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_new(out git_config* cfg); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_open_level( out git_config* cfg, git_config* parent, uint level); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_parse_bool( [MarshalAs(UnmanagedType.Bool)] out bool value, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string valueToParse); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_parse_int32( [MarshalAs(UnmanagedType.I4)] out int value, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string valueToParse); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_parse_int64( [MarshalAs(UnmanagedType.I8)] out long value, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string valueToParse); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_set_bool( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.Bool)] bool value); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_set_int32( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, int value); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_set_int64( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, long value); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_set_string( git_config* cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int config_foreach_callback( IntPtr entry, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_foreach( git_config* cfg, config_foreach_callback callback, IntPtr payload); - [DllImport(libgit2)] - internal static extern unsafe int git_config_iterator_glob_new( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_config_iterator_glob_new( out IntPtr iter, IntPtr cfg, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string regexp); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_config_next( out IntPtr entry, IntPtr iter); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_config_iterator_free(IntPtr iter); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_config_snapshot(out git_config* @out, git_config* config); // Ordinarily we would decorate the `url` parameter with the StrictUtf8Marshaler like we do everywhere @@ -478,6 +581,7 @@ internal static extern int git_config_next( // sometimes vomit when using a custom IMarshaler. So yeah, don't do that. If you need the url, // call StrictUtf8Marshaler.FromNative manually. See the discussion here: // http://social.msdn.microsoft.com/Forums/en-US/netfx64bit/thread/1eb746c6-d695-4632-8a9e-16c4fa98d481 + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_cred_acquire_cb( out IntPtr cred, IntPtr url, @@ -485,37 +589,37 @@ internal delegate int git_cred_acquire_cb( GitCredentialType allowed_types, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_cred_default_new(out IntPtr cred); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_cred_userpass_plaintext_new( out IntPtr cred, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string password); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_cred_free(IntPtr cred); - - [DllImport(libgit2)] + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_describe_commit( out git_describe_result* describe, git_object* committish, ref GitDescribeOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_describe_format( GitBuf buf, git_describe_result* describe, ref GitDescribeFormatOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_describe_result_free(git_describe_result* describe); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_diff_free(git_diff* diff); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_tree_to_tree( out git_diff* diff, git_repository* repo, @@ -523,7 +627,7 @@ internal static extern unsafe int git_diff_tree_to_tree( git_object* newTree, GitDiffOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_tree_to_index( out git_diff* diff, git_repository* repo, @@ -531,52 +635,56 @@ internal static extern unsafe int git_diff_tree_to_index( git_index* index, GitDiffOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_merge( git_diff* onto, git_diff* from); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_index_to_workdir( out git_diff* diff, git_repository* repo, git_index* index, GitDiffOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_tree_to_workdir( out git_diff* diff, git_repository* repo, git_object* oldTree, GitDiffOptions options); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal unsafe delegate int git_diff_file_cb( [In] git_diff_delta* delta, float progress, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal unsafe delegate int git_diff_hunk_cb( [In] git_diff_delta* delta, [In] GitDiffHunk hunk, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal unsafe delegate int git_diff_line_cb( [In] git_diff_delta* delta, [In] GitDiffHunk hunk, [In] GitDiffLine line, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal unsafe delegate int git_diff_binary_cb( [In] git_diff_delta* delta, [In] GitDiffBinary binary, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_blobs( git_object* oldBlob, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath old_as_path, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string old_as_path, git_object* newBlob, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath new_as_path, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string new_as_path, GitDiffOptions options, git_diff_file_cb fileCallback, git_diff_binary_cb binaryCallback, @@ -584,7 +692,7 @@ internal static extern unsafe int git_diff_blobs( git_diff_line_cb lineCallback, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_foreach( git_diff* diff, git_diff_file_cb fileCallback, @@ -593,30 +701,30 @@ internal static extern unsafe int git_diff_foreach( git_diff_line_cb lineCallback, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_diff_find_similar( git_diff* diff, GitDiffFindOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_diff_num_deltas(git_diff* diff); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_diff_delta* git_diff_get_delta(git_diff* diff, UIntPtr idx); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_filter_register( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, IntPtr gitFilter, int priority); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_filter_unregister( - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]string name); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_filter_source_mode(git_filter_source* source); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_libgit2_features(); #region git_libgit2_opts @@ -634,160 +742,222 @@ internal static extern int git_filter_unregister( // git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_libgit2_opts(int option, uint level, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))]string path); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); // git_libgit2_opts(GIT_OPT_ENABLE_*, int enabled) + // git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, int enabled) [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_libgit2_opts(int option, int enabled); + + // git_libgit2_opts(GIT_OPT_SET_USER_AGENT, const char *path) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_libgit2_opts(int option, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); + + // git_libgit2_opts(GIT_OPT_GET_USER_AGENT, git_buf *buf) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_libgit2_opts(int option, GitBuf buf); + + // git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_libgit2_opts(int option, IntPtr extensions, UIntPtr len); + + // git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_libgit2_opts(int option, out GitStrArray extensions); + + // git_libgit2_opts(GIT_OPT_GET_OWNER_VALIDATION, int *enabled) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_libgit2_opts(int option, int* enabled); + #endregion + + #region git_libgit2_opts_osxarm64 + + // For RID osx-arm64 the calling convention is different: we need to pad out to 8 arguments before varargs + // (see discussion at https://github.com/dotnet/runtime/issues/48796) + + // git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, uint level, GitBuf buf); + + // git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, uint level, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); + + // git_libgit2_opts(GIT_OPT_ENABLE_*, int enabled) + // git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, int enabled) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, int enabled); + + // git_libgit2_opts(GIT_OPT_SET_USER_AGENT, const char *path) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); + + // git_libgit2_opts(GIT_OPT_GET_USER_AGENT, git_buf *buf) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, GitBuf buf); + + // git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, IntPtr extensions, UIntPtr len); + + // git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, out GitStrArray extensions); + + // git_libgit2_opts(GIT_OPT_GET_OWNER_VALIDATION, int *enabled) + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl, EntryPoint = "git_libgit2_opts")] + internal static extern unsafe int git_libgit2_opts_osxarm64(int option, IntPtr nop2, IntPtr nop3, IntPtr nop4, IntPtr nop5, IntPtr nop6, IntPtr nop7, IntPtr nop8, int* enabled); #endregion - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_graph_ahead_behind(out UIntPtr ahead, out UIntPtr behind, git_repository* repo, ref GitOid one, ref GitOid two); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_graph_descendant_of( git_repository* repo, ref GitOid commit, ref GitOid ancestor); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_ignore_add_rule( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string rules); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_ignore_clear_internal_rules(git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_ignore_path_is_ignored( out int ignored, git_repository* repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_add_bypath( git_index* index, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_add( git_index* index, git_index_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_conflict_get( out git_index_entry* ancestor, out git_index_entry* ours, out git_index_entry* theirs, git_index* index, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_conflict_iterator_new( out git_index_conflict_iterator* iterator, git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_conflict_next( out git_index_entry* ancestor, out git_index_entry* ours, out git_index_entry* theirs, git_index_conflict_iterator* iterator); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_index_conflict_iterator_free( git_index_conflict_iterator* iterator); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_index_entrycount(git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_entry_stage(git_index_entry* indexentry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_index_free(git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_index_entry* git_index_get_byindex(git_index* index, UIntPtr n); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_index_entry* git_index_get_bypath( git_index* index, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path, int stage); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_has_conflicts(git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_index_name_entrycount(git_index* handle); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_index_name_entry* git_index_name_get_byindex(git_index* handle, UIntPtr n); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_open( out git_index* index, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath indexpath); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_read( git_index* index, [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_remove_bypath( git_index* index, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); - + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_index_reuc_entrycount(git_index* handle); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_index_reuc_entry* git_index_reuc_get_byindex(git_index* handle, UIntPtr n); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_index_reuc_entry* git_index_reuc_get_bypath( git_index* handle, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_write(git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_write_tree(out GitOid treeOid, git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_write_tree_to(out GitOid treeOid, git_index* index, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_read_tree(git_index* index, git_object* tree); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_index_clear(git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_merge_base_many( out GitOid mergeBase, git_repository* repo, int length, [In] GitOid[] input_array); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_merge_base_octopus( out GitOid mergeBase, git_repository* repo, int length, [In] GitOid[] input_array); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_annotated_commit_from_ref( out git_annotated_commit* annotatedCommit, git_repository* repo, git_reference* reference); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_annotated_commit_from_fetchhead( out git_annotated_commit* annotatedCommit, git_repository* repo, @@ -795,23 +965,23 @@ internal static extern unsafe int git_annotated_commit_from_fetchhead( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_url, ref GitOid oid); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_annotated_commit_from_revspec( out git_annotated_commit* annotatedCommit, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string revspec); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_annotated_commit_lookup( out git_annotated_commit* annotatedCommit, git_repository* repo, ref GitOid id); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_annotated_commit_id( git_annotated_commit* annotatedCommit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_merge( git_repository* repo, [In] IntPtr[] their_heads, @@ -819,7 +989,7 @@ internal static extern unsafe int git_merge( ref GitMergeOpts merge_opts, ref GitCheckoutOpts checkout_opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_merge_commits( out git_index* index, git_repository* repo, @@ -827,7 +997,7 @@ internal static extern unsafe int git_merge_commits( git_object* their_commit, ref GitMergeOpts merge_opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_merge_analysis( out GitMergeAnalysis status_out, out GitMergePreference preference_out, @@ -835,17 +1005,17 @@ internal static extern unsafe int git_merge_analysis( [In] IntPtr[] their_heads, int their_heads_len); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_annotated_commit_free(git_annotated_commit* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_message_prettify( GitBuf buf, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string message, [MarshalAs(UnmanagedType.Bool)] bool strip_comments, sbyte comment_char); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_note_create( out GitOid noteOid, git_repository* repo, @@ -856,24 +1026,24 @@ internal static extern unsafe int git_note_create( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string note, int force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_note_free(git_note* note); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_note_message(git_note* note); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_note_id(git_note* note); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_note_read( out git_note* note, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string notes_ref, ref GitOid oid); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_note_remove( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string notes_ref, @@ -881,141 +1051,145 @@ internal static extern unsafe int git_note_remove( git_signature* committer, ref GitOid oid); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_note_default_ref( GitBuf notes_ref, git_repository* repo); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_note_foreach_cb( ref GitOid blob_id, ref GitOid annotated_object_id, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_note_foreach( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string notes_ref, git_note_foreach_cb cb, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_odb_add_backend(git_odb* odb, IntPtr backend, int priority); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr git_odb_backend_malloc(IntPtr backend, UIntPtr len); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_odb_exists(git_odb* odb, ref GitOid id); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_odb_foreach_cb( IntPtr id, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_odb_foreach( git_odb* odb, git_odb_foreach_cb cb, IntPtr payload); - [DllImport(libgit2)] - internal static extern unsafe int git_odb_open_wstream(out git_odb_stream* stream, git_odb* odb, Int64 size, GitObjectType type); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_odb_open_wstream(out git_odb_stream* stream, git_odb* odb, long size, GitObjectType type); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_odb_free(git_odb* odb); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_odb_read_header(out UIntPtr len_out, out GitObjectType type, git_odb* odb, ref GitOid id); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_object_free(git_object* obj); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_odb_stream_write(git_odb_stream* Stream, IntPtr Buffer, UIntPtr len); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_odb_stream_finalize_write(out GitOid id, git_odb_stream* stream); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_odb_stream_free(git_odb_stream* stream); - [DllImport(libgit2)] - internal static extern unsafe int git_odb_write(out GitOid id, git_odb *odb, byte* data, UIntPtr len, GitObjectType type); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_odb_write(out GitOid id, git_odb* odb, byte* data, UIntPtr len, GitObjectType type); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_object_id(git_object* obj); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_object_lookup(out git_object* obj, git_repository* repo, ref GitOid id, GitObjectType type); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_object_peel( out git_object* peeled, git_object* obj, GitObjectType type); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_object_short_id( GitBuf buf, git_object* obj); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe GitObjectType git_object_type(git_object* obj); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_patch_from_diff(out git_patch* patch, git_diff* diff, UIntPtr idx); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_patch_print(git_patch* patch, git_diff_line_cb print_cb, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_patch_line_stats( out UIntPtr total_context, out UIntPtr total_additions, out UIntPtr total_deletions, git_patch* patch); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_patch_free(git_patch* patch); /* Push network progress notification function */ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_push_transfer_progress(uint current, uint total, UIntPtr bytes, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_packbuilder_progress(int stage, uint current, uint total, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_packbuilder_free(git_packbuilder* packbuilder); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_packbuilder_insert( git_packbuilder* packbuilder, ref GitOid id, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_packbuilder_insert_commit( git_packbuilder* packbuilder, ref GitOid id); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_packbuilder_insert_recur( git_packbuilder* packbuilder, ref GitOid id, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_packbuilder_insert_tree( git_packbuilder* packbuilder, ref GitOid id); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_packbuilder_new(out git_packbuilder* packbuilder, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_packbuilder_object_count(git_packbuilder* packbuilder); - [DllImport(libgit2)] - internal static extern unsafe UInt32 git_packbuilder_set_threads(git_packbuilder* packbuilder, UInt32 numThreads); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe uint git_packbuilder_set_threads(git_packbuilder* packbuilder, uint numThreads); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_packbuilder_write( git_packbuilder* packbuilder, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, @@ -1023,10 +1197,10 @@ internal static extern unsafe int git_packbuilder_write( IntPtr progressCallback, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_packbuilder_written(git_packbuilder* packbuilder); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_create( out git_reference* reference, git_repository* repo, @@ -1035,7 +1209,7 @@ internal static extern unsafe int git_reference_create( [MarshalAs(UnmanagedType.Bool)] bool force, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_symbolic_create( out git_reference* reference, git_repository* repo, @@ -1044,46 +1218,47 @@ internal static extern unsafe int git_reference_symbolic_create( [MarshalAs(UnmanagedType.Bool)] bool force, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int ref_glob_callback( IntPtr reference_name, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_foreach_glob( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string glob, ref_glob_callback callback, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_reference_free(git_reference* reference); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_reference_is_valid_name( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refname); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_list(out GitStrArray array, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_lookup( out git_reference* reference, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_reference_name(git_reference* reference); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_remove( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_reference_target(git_reference* reference); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_rename( out git_reference* ref_out, git_reference* reference, @@ -1091,114 +1266,114 @@ internal static extern unsafe int git_reference_rename( [MarshalAs(UnmanagedType.Bool)] bool force, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_set_target( out git_reference* ref_out, git_reference* reference, ref GitOid id, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_symbolic_set_target( out git_reference* ref_out, git_reference* reference, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string target, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_reference_symbolic_target(git_reference* reference); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe GitReferenceType git_reference_type(git_reference* reference); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reference_ensure_log( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refname); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_reflog_free(git_reflog* reflog); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reflog_read( out git_reflog* ref_out, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_reflog_entrycount (git_reflog* reflog); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_reflog_entry* git_reflog_entry_byindex( git_reflog* reflog, UIntPtr idx); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_reflog_entry_id_old( git_reflog_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_reflog_entry_id_new( git_reflog_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_signature* git_reflog_entry_committer( git_reflog_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_reflog_entry_message(git_reflog_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_refspec_transform( GitBuf buf, IntPtr refspec, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - - [DllImport(libgit2)] + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_refspec_rtransform( GitBuf buf, IntPtr refspec, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern string git_refspec_string( IntPtr refSpec); - [DllImport(libgit2)] - internal static extern unsafe RefSpecDirection git_refspec_direction(IntPtr refSpec); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern RefSpecDirection git_refspec_direction(IntPtr refSpec); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] - internal static extern unsafe string git_refspec_dst( + internal static extern string git_refspec_dst( IntPtr refSpec); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] - internal static extern unsafe string git_refspec_src( + internal static extern string git_refspec_src( IntPtr refspec); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern bool git_refspec_force(IntPtr refSpec); - [DllImport(libgit2)] - internal static extern unsafe bool git_refspec_src_matches( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern bool git_refspec_src_matches( IntPtr refspec, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string reference); - - [DllImport(libgit2)] - internal static extern unsafe bool git_refspec_dst_matches( + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern bool git_refspec_dst_matches( IntPtr refspec, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string reference); - - [DllImport(libgit2)] + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_autotag(git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_connect( git_remote* remote, GitDirection direction, @@ -1206,21 +1381,21 @@ internal static extern unsafe int git_remote_connect( ref GitProxyOptions proxy_options, ref GitStrArray custom_headers); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_create( out git_remote* remote, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_create_anonymous( out git_remote* remote, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_create_with_fetchspec( out git_remote* remote, git_repository* repo, @@ -1228,125 +1403,131 @@ internal static extern unsafe int git_remote_create_with_fetchspec( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refspec); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_delete( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_fetch( git_remote* remote, ref GitStrArray refspecs, GitFetchOptions fetch_opts, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_remote_free(git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_get_fetch_refspecs(out GitStrArray array, git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_refspec* git_remote_get_refspec(git_remote* remote, UIntPtr n); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_get_push_refspecs(out GitStrArray array, git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_push( git_remote* remote, ref GitStrArray refSpecs, GitPushOptions opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_remote_refspec_count(git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_set_url( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_add_fetch( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_set_pushurl( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_add_push( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_remote_is_valid_name( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_list(out GitStrArray array, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_lookup( out git_remote* remote, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_remote_ls(out git_remote_head** heads, out UIntPtr size, git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_remote_name(git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_remote_url(git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_remote_pushurl(git_remote* remote); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_remote_set_autotag( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, TagFetchMode option); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int remote_progress_callback(IntPtr str, int len, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int remote_completion_callback(RemoteCompletionType type, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int remote_update_tips_callback( IntPtr refName, ref GitOid oldId, ref GitOid newId, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int push_negotiation_callback( IntPtr updates, UIntPtr len, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int push_update_reference_callback( IntPtr refName, IntPtr status, IntPtr data ); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_repository_discover( GitBuf buf, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath start_path, [MarshalAs(UnmanagedType.Bool)] bool across_fs, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath ceiling_dirs); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_repository_fetchhead_foreach_cb( IntPtr remote_name, IntPtr remote_url, @@ -1354,147 +1535,148 @@ internal delegate int git_repository_fetchhead_foreach_cb( [MarshalAs(UnmanagedType.Bool)] bool is_merge, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_fetchhead_foreach( git_repository* repo, git_repository_fetchhead_foreach_cb cb, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_repository_free(git_repository* repo); - [DllImport(libgit2)] - internal static extern unsafe int git_repository_head_detached(IntPtr repo); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_repository_head_detached(IntPtr repo); - [DllImport(libgit2)] - internal static extern unsafe int git_repository_head_unborn(IntPtr repo); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern int git_repository_head_unborn(IntPtr repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_ident( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] out string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] out string email, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_index(out git_index* index, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_init_ext( out git_repository* repository, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, GitRepositoryInitOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_repository_is_bare(IntPtr handle); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_repository_is_shallow(IntPtr repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_state_cleanup(git_repository* repo); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_repository_mergehead_foreach_cb( ref GitOid oid, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_mergehead_foreach( git_repository* repo, git_repository_mergehead_foreach_cb cb, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_message( GitBuf buf, git_repository* repository); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_new( out git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_odb(out git_odb* odb, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_open( out git_repository* repository, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_open_ext( out git_repository* repository, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath path, RepositoryOpenFlags flags, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath ceilingDirs); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))] internal static extern unsafe FilePath git_repository_path(git_repository* repository); - [DllImport(libgit2)] - internal static extern unsafe void git_repository_set_config( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_repository_set_config( git_repository* repository, git_config* config); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_set_ident( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string email); - [DllImport(libgit2)] - internal static extern unsafe void git_repository_set_index( + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_repository_set_index( git_repository* repository, git_index* index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_set_workdir( git_repository* repository, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath workdir, [MarshalAs(UnmanagedType.Bool)] bool update_gitlink); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_set_head_detached( git_repository* repo, ref GitOid commitish); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_set_head_detached_from_annotated( git_repository* repo, git_annotated_commit* commit); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_set_head( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refname); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_repository_state( git_repository* repository); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))] internal static extern unsafe FilePath git_repository_workdir(git_repository* repository); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxFilePathNoCleanupMarshaler))] - internal static extern unsafe FilePath git_repository_workdir(IntPtr repository); + internal static extern FilePath git_repository_workdir(IntPtr repository); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_reset( git_repository* repo, git_object* target, ResetMode reset_type, ref GitCheckoutOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revert( git_repository* repo, git_object* commit, GitRevertOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revert_commit( out git_index* index, git_repository* repo, @@ -1503,41 +1685,41 @@ internal static extern unsafe int git_revert_commit( uint mainline, ref GitMergeOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revparse_ext( out git_object* obj, out git_reference* reference, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string spec); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_revwalk_free(git_revwalk* walker); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revwalk_hide(git_revwalk* walker, ref GitOid commit_id); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revwalk_new(out git_revwalk* walker, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revwalk_next(out GitOid id, git_revwalk* walker); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_revwalk_push(git_revwalk* walker, ref GitOid id); - [DllImport(libgit2)] - internal static extern unsafe void git_revwalk_reset(git_revwalk* walker); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_revwalk_reset(git_revwalk* walker); - [DllImport(libgit2)] - internal static extern unsafe void git_revwalk_sorting(git_revwalk* walk, CommitSortStrategies sort); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_revwalk_sorting(git_revwalk* walk, CommitSortStrategies sort); - [DllImport(libgit2)] - internal static extern unsafe void git_revwalk_simplify_first_parent(git_revwalk* walk); + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_revwalk_simplify_first_parent(git_revwalk* walk); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_signature_free(git_signature* signature); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_signature_new( out git_signature* signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, @@ -1545,16 +1727,16 @@ internal static extern unsafe int git_signature_new( long time, int offset); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_signature_now( out git_signature* signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string email); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_signature_dup(out git_signature* dest, git_signature* sig); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_stash_save( out GitOid id, git_repository* repo, @@ -1562,152 +1744,154 @@ internal static extern unsafe int git_stash_save( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string message, StashModifiers flags); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_stash_cb( UIntPtr index, IntPtr message, ref GitOid stash_id, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_stash_foreach( git_repository* repo, git_stash_cb callback, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_stash_drop(git_repository* repo, UIntPtr index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_stash_apply( git_repository* repo, UIntPtr index, GitStashApplyOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_stash_pop( git_repository* repo, UIntPtr index, GitStashApplyOpts opts); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_status_file( out FileStatus statusflags, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath filepath); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_status_list_new( out git_status_list* git_status_list, git_repository* repo, GitStatusOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_status_list_entrycount( git_status_list* statusList); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_status_entry* git_status_byindex( git_status_list* list, UIntPtr idx); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_status_list_free( git_status_list* statusList); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_strarray_free( ref GitStrArray array); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_lookup( out git_submodule* reference, git_repository* repo, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath name); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_resolve_url( GitBuf buf, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_update( git_submodule* sm, [MarshalAs(UnmanagedType.Bool)] bool init, ref GitSubmoduleUpdateOptions submoduleUpdateOptions); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int submodule_callback( IntPtr sm, IntPtr name, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_foreach( git_repository* repo, submodule_callback callback, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_add_to_index( git_submodule* submodule, [MarshalAs(UnmanagedType.Bool)] bool write_index); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_submodule_free(git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_submodule_path( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_submodule_url( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_submodule_index_id( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_submodule_head_id( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_submodule_wd_id( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe SubmoduleIgnore git_submodule_ignore( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe SubmoduleUpdate git_submodule_update_strategy( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe SubmoduleRecurse git_submodule_fetch_recurse_submodules( git_submodule* submodule); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_reload( git_submodule* submodule, [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_status( out SubmoduleStatus status, git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath name, GitSubmoduleIgnore ignore); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_submodule_init( git_submodule* submodule, [MarshalAs(UnmanagedType.Bool)] bool overwrite); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_tag_annotation_create( out GitOid oid, git_repository* repo, @@ -1716,7 +1900,7 @@ internal static extern unsafe int git_tag_annotation_create( git_signature* signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_tag_create( out GitOid oid, git_repository* repo, @@ -1727,7 +1911,7 @@ internal static extern unsafe int git_tag_create( [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_tag_create_lightweight( out GitOid oid, git_repository* repo, @@ -1736,113 +1920,117 @@ internal static extern unsafe int git_tag_create_lightweight( [MarshalAs(UnmanagedType.Bool)] bool force); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_tag_delete( git_repository* repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string tagName); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_tag_list(out GitStrArray array, git_repository* repo); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_tag_message(git_object* tag); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_tag_name(git_object* tag); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_signature* git_tag_tagger(git_object* tag); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_tag_target_id(git_object* tag); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe GitObjectType git_tag_target_type(git_object* tag); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_libgit2_init(); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_libgit2_shutdown(); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_openssl_set_locking(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void git_trace_cb(LogLevel level, IntPtr message); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_trace_set(LogLevel level, git_trace_cb trace_cb); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_transfer_progress_callback(ref GitTransferProgress stats, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int git_transport_cb(out IntPtr transport, IntPtr remote, IntPtr payload); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal unsafe delegate int git_transport_certificate_check_cb(git_certificate* cert, int valid, IntPtr hostname, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_transport_register( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string prefix, IntPtr transport_cb, IntPtr payload); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_transport_smart( out IntPtr transport, IntPtr remote, IntPtr definition); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_transport_smart_certificate_check( IntPtr transport, IntPtr cert, int valid, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string hostname); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_transport_smart_credentials( out IntPtr cred_out, IntPtr transport, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string user, int methods); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_transport_unregister( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string prefix); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe uint git_tree_entry_filemode(git_tree_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_tree_entry* git_tree_entry_byindex(git_object* tree, UIntPtr idx); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_tree_entry_bypath( out git_tree_entry* tree, git_object* root, - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictFilePathMarshaler))] FilePath treeentry_path); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string treeentry_path); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_tree_entry_free(git_tree_entry* treeEntry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe git_oid* git_tree_entry_id(git_tree_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(LaxUtf8NoCleanupMarshaler))] internal static extern unsafe string git_tree_entry_name(git_tree_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe GitObjectType git_tree_entry_type(git_tree_entry* entry); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe UIntPtr git_tree_entrycount(git_object* tree); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_treebuilder_new(out git_treebuilder* builder, git_repository* repo, IntPtr src); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_treebuilder_insert( IntPtr entry_out, git_treebuilder* builder, @@ -1850,19 +2038,19 @@ internal static extern unsafe int git_treebuilder_insert( ref GitOid id, uint attributes); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_treebuilder_write(out GitOid id, git_treebuilder* bld); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void git_treebuilder_free(git_treebuilder* bld); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_blob_is_binary(git_object* blob); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_cherrypick(git_repository* repo, git_object* commit, GitCherryPickOptions options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int git_cherrypick_commit(out git_index* index, git_repository* repo, git_object* cherrypick_commit, @@ -1870,11 +2058,67 @@ internal static extern unsafe int git_cherrypick_commit(out git_index* index, uint mainline, ref GitMergeOpts options); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern int git_transaction_commit(IntPtr txn); - [DllImport(libgit2)] + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] internal static extern void git_transaction_free(IntPtr txn); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int url_resolve_callback( + IntPtr url_resolved, + IntPtr url, + int direction, + IntPtr payload); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe void git_worktree_free(git_worktree* worktree); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_lookup( + out git_worktree* reference, + git_repository* repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_list( + out GitStrArray array, + git_repository* repo); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_repository_open_from_worktree( + out git_repository* repository, + git_worktree* worktree); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_is_locked( + GitBuf reason, + git_worktree* worktree); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_validate( + git_worktree* worktree); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_lock( + git_worktree* worktree, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string reason); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_unlock( + git_worktree* worktree); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_add( + out git_worktree* reference, + git_repository* repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path, + git_worktree_add_options options); + + [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe int git_worktree_prune( + git_worktree* worktree, + git_worktree_prune_options options); } } -// ReSharper restore InconsistentNaming diff --git a/LibGit2Sharp/Core/ObjectSafeWrapper.cs b/LibGit2Sharp/Core/ObjectSafeWrapper.cs index 8bb7e9633..f2ab4a9e1 100644 --- a/LibGit2Sharp/Core/ObjectSafeWrapper.cs +++ b/LibGit2Sharp/Core/ObjectSafeWrapper.cs @@ -7,7 +7,7 @@ internal class ObjectSafeWrapper : IDisposable { private readonly ObjectHandle objectPtr; - public unsafe ObjectSafeWrapper(ObjectId id, RepositoryHandle handle, bool allowNullObjectId = false) + public unsafe ObjectSafeWrapper(ObjectId id, RepositoryHandle handle, bool allowNullObjectId = false, bool throwIfMissing = false) { Ensure.ArgumentNotNull(handle, "handle"); @@ -20,13 +20,15 @@ public unsafe ObjectSafeWrapper(ObjectId id, RepositoryHandle handle, bool allow Ensure.ArgumentNotNull(id, "id"); objectPtr = Proxy.git_object_lookup(handle, id, GitObjectType.Any); } - } - public ObjectHandle ObjectPtr - { - get { return objectPtr; } + if (objectPtr == null && throwIfMissing) + { + throw new NotFoundException($"No valid git object identified by '{id}' exists in the repository."); + } } + public ObjectHandle ObjectPtr => objectPtr; + public void Dispose() { Dispose(true); diff --git a/LibGit2Sharp/Core/Opaques.cs b/LibGit2Sharp/Core/Opaques.cs index 0d0bb55f0..f83e8be10 100644 --- a/LibGit2Sharp/Core/Opaques.cs +++ b/LibGit2Sharp/Core/Opaques.cs @@ -2,30 +2,31 @@ namespace LibGit2Sharp.Core { - internal struct git_tree_entry {} + internal struct git_tree_entry { } internal struct git_reference { } - internal struct git_refspec {} - internal struct git_repository {} - internal struct git_status_list {} - internal struct git_blame {} - internal struct git_diff {} - internal struct git_patch {} - internal struct git_config {} - internal struct git_index_conflict_iterator {} - internal struct git_index {} - internal struct git_reflog {} - internal struct git_reflog_entry {} - internal struct git_treebuilder {} - internal struct git_packbuilder {} - internal struct git_note {} - internal struct git_describe_result {} - internal struct git_submodule {} - internal struct git_annotated_commit {} - internal struct git_odb {} - internal struct git_revwalk {} - internal struct git_remote {} - internal struct git_object {} - internal struct git_rebase {} - internal struct git_odb_stream {} + internal struct git_refspec { } + internal struct git_repository { } + internal struct git_status_list { } + internal struct git_blame { } + internal struct git_diff { } + internal struct git_patch { } + internal struct git_config { } + internal struct git_index_conflict_iterator { } + internal struct git_index { } + internal struct git_reflog { } + internal struct git_reflog_entry { } + internal struct git_treebuilder { } + internal struct git_packbuilder { } + internal struct git_note { } + internal struct git_describe_result { } + internal struct git_submodule { } + internal struct git_annotated_commit { } + internal struct git_odb { } + internal struct git_revwalk { } + internal struct git_remote { } + internal struct git_object { } + internal struct git_rebase { } + internal struct git_odb_stream { } + internal struct git_worktree { } } diff --git a/LibGit2Sharp/Core/Platform.cs b/LibGit2Sharp/Core/Platform.cs index 1f43b6a24..1fcb59faf 100644 --- a/LibGit2Sharp/Core/Platform.cs +++ b/LibGit2Sharp/Core/Platform.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace LibGit2Sharp.Core { @@ -11,29 +12,68 @@ internal enum OperatingSystemType internal static class Platform { - public static string ProcessorArchitecture - { - get { return Environment.Is64BitProcess ? "x64" : "x86"; } - } + public static string ProcessorArchitecture => RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); public static OperatingSystemType OperatingSystem { get { - // See http://www.mono-project.com/docs/faq/technical/#how-to-detect-the-execution-platform - switch ((int)Environment.OSVersion.Platform) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - case 4: - case 128: - return OperatingSystemType.Unix; + return OperatingSystemType.Windows; + } - case 6: - return OperatingSystemType.MacOSX; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystemType.Unix; + } - default: - return OperatingSystemType.Windows; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystemType.MacOSX; } + + throw new PlatformNotSupportedException(); + } + } + + public static string GetNativeLibraryExtension() + { + switch (OperatingSystem) + { + case OperatingSystemType.MacOSX: + return ".dylib"; + + case OperatingSystemType.Unix: + return ".so"; + + case OperatingSystemType.Windows: + return ".dll"; } + + throw new PlatformNotSupportedException(); } + + /// + /// Returns true if the runtime is Mono. + /// + public static bool IsRunningOnMono() +#if NETFRAMEWORK + => Type.GetType("Mono.Runtime") != null; +#else + => false; +#endif + + /// + /// Returns true if the runtime is .NET Framework. + /// + public static bool IsRunningOnNetFramework() + => typeof(object).Assembly.GetName().Name == "mscorlib" && !IsRunningOnMono(); + + /// + /// Returns true if the runtime is .NET Core. + /// + public static bool IsRunningOnNetCore() + => typeof(object).Assembly.GetName().Name != "mscorlib"; } } diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index e73e5a83b..83d35e22c 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -14,90 +14,14 @@ namespace LibGit2Sharp.Core { internal class Proxy { - #region giterr_ - - public static void giterr_set_str(GitErrorCategory error_class, Exception exception) - { - if (exception is OutOfMemoryException) - { - NativeMethods.giterr_set_oom(); - } - else - { - NativeMethods.giterr_set_str(error_class, ErrorMessageFromException(exception)); - } - } - - public static void giterr_set_str(GitErrorCategory error_class, String errorString) - { - NativeMethods.giterr_set_str(error_class, errorString); - } - - /// - /// This method will take an exception and try to generate an error message - /// that captures the important messages of the error. - /// The formatting is a bit subjective. - /// - /// - /// - public static string ErrorMessageFromException(Exception ex) - { - StringBuilder sb = new StringBuilder(); - BuildErrorMessageFromException(sb, 0, ex); - return sb.ToString(); - } - - private static void BuildErrorMessageFromException(StringBuilder sb, int level, Exception ex) - { - string indent = new string(' ', level * 4); - sb.AppendFormat("{0}{1}", indent, ex.Message); - - if (ex is AggregateException) - { - AggregateException aggregateException = ((AggregateException)ex).Flatten(); - - if (aggregateException.InnerExceptions.Count == 1) - { - sb.AppendLine(); - sb.AppendLine(); - - sb.AppendFormat("{0}Contained Exception:{1}", indent, Environment.NewLine); - BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerException); - } - else - { - sb.AppendLine(); - sb.AppendLine(); - - sb.AppendFormat("{0}Contained Exceptions:{1}", indent, Environment.NewLine); - for (int i = 0; i < aggregateException.InnerExceptions.Count; i++) - { - if (i != 0) - { - sb.AppendLine(); - sb.AppendLine(); - } - - BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerExceptions[i]); - } - } - } - else if (ex.InnerException != null) - { - sb.AppendLine(); - sb.AppendLine(); - sb.AppendFormat("{0}Inner Exception:{1}", indent, Environment.NewLine); - BuildErrorMessageFromException(sb, level + 1, ex.InnerException); - } - } - - #endregion + internal static readonly bool isOSXArm64 = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 + && RuntimeInformation.IsOSPlatform(OSPlatform.OSX); #region git_blame_ public static unsafe BlameHandle git_blame_file( RepositoryHandle repo, - FilePath path, + string path, git_blame_options options) { git_blame* ptr; @@ -115,43 +39,43 @@ public static unsafe BlameHandle git_blame_file( #region git_blob_ - public static unsafe IntPtr git_blob_create_fromstream(RepositoryHandle repo, FilePath hintpath) + public static unsafe IntPtr git_blob_create_from_stream(RepositoryHandle repo, string hintpath) { IntPtr writestream_ptr; - Ensure.ZeroResult(NativeMethods.git_blob_create_fromstream(out writestream_ptr, repo, hintpath)); + Ensure.ZeroResult(NativeMethods.git_blob_create_from_stream(out writestream_ptr, repo, hintpath)); return writestream_ptr; } public static unsafe ObjectId git_blob_create_fromstream_commit(IntPtr writestream_ptr) { var oid = new GitOid(); - Ensure.ZeroResult(NativeMethods.git_blob_create_fromstream_commit(ref oid, writestream_ptr)); + Ensure.ZeroResult(NativeMethods.git_blob_create_from_stream_commit(ref oid, writestream_ptr)); return oid; } - public static unsafe ObjectId git_blob_create_fromdisk(RepositoryHandle repo, FilePath path) + public static unsafe ObjectId git_blob_create_from_disk(RepositoryHandle repo, FilePath path) { var oid = new GitOid(); - int res = NativeMethods.git_blob_create_fromdisk(ref oid, repo, path); + int res = NativeMethods.git_blob_create_from_disk(ref oid, repo, path); Ensure.ZeroResult(res); return oid; } - public static unsafe ObjectId git_blob_create_fromfile(RepositoryHandle repo, FilePath path) + public static unsafe ObjectId git_blob_create_from_workdir(RepositoryHandle repo, FilePath path) { var oid = new GitOid(); - int res = NativeMethods.git_blob_create_fromworkdir(ref oid, repo, path); + int res = NativeMethods.git_blob_create_from_workdir(ref oid, repo, path); Ensure.ZeroResult(res); return oid; } - public static unsafe UnmanagedMemoryStream git_blob_filtered_content_stream(RepositoryHandle repo, ObjectId id, FilePath path, bool check_for_binary_data) + public static unsafe UnmanagedMemoryStream git_blob_filtered_content_stream(RepositoryHandle repo, ObjectId id, string path, bool check_for_binary_data) { var buf = new GitBuf(); - var handle = new ObjectSafeWrapper(id, repo).ObjectPtr; + var handle = new ObjectSafeWrapper(id, repo, throwIfMissing: true).ObjectPtr; return new RawContentStream(handle, h => { @@ -162,9 +86,9 @@ public static unsafe UnmanagedMemoryStream git_blob_filtered_content_stream(Repo new[] { buf }); } - public static unsafe UnmanagedMemoryStream git_blob_rawcontent_stream(RepositoryHandle repo, ObjectId id, Int64 size) + public static unsafe UnmanagedMemoryStream git_blob_rawcontent_stream(RepositoryHandle repo, ObjectId id, long size) { - var handle = new ObjectSafeWrapper(id, repo).ObjectPtr; + var handle = new ObjectSafeWrapper(id, repo, throwIfMissing: true).ObjectPtr; return new RawContentStream(handle, h => NativeMethods.git_blob_rawcontent(h), h => size); } @@ -286,9 +210,9 @@ public static unsafe string git_branch_upstream_name(RepositoryHandle handle, st #region git_buf_ - public static void git_buf_free(GitBuf buf) + public static void git_buf_dispose(GitBuf buf) { - NativeMethods.git_buf_free(buf); + NativeMethods.git_buf_dispose(buf); } #endregion @@ -350,7 +274,7 @@ public static unsafe RepositoryHandle git_clone( string workdir, ref GitCloneOptions opts) { - git_repository *repo; + git_repository* repo; int res = NativeMethods.git_clone(out repo, url, workdir, ref opts); Ensure.ZeroResult(res); return new RepositoryHandle(repo, true); @@ -417,13 +341,13 @@ public static unsafe string git_commit_create_buffer( using (var treeHandle = Proxy.git_object_lookup(tree.repo.Handle, tree.Id, GitObjectType.Tree)) using (var buf = new GitBuf()) { - ObjectHandle[] handles = new ObjectHandle[0]; + ObjectHandle[] handles = Array.Empty(); try { handles = parents.Select(c => Proxy.git_object_lookup(c.repo.Handle, c.Id, GitObjectType.Commit)).ToArray(); var ptrs = handles.Select(p => p.AsIntPtr()).ToArray(); int res; - fixed(IntPtr* objs = ptrs) + fixed (IntPtr* objs = ptrs) { res = NativeMethods.git_commit_create_buffer(buf, repo, @@ -517,9 +441,11 @@ public static unsafe SignatureInfo git_commit_extract_signature(RepositoryHandle #region git_config_ - public static unsafe void git_config_add_file_ondisk(ConfigurationHandle config, FilePath path, ConfigurationLevel level) + public static unsafe void git_config_add_file_ondisk(ConfigurationHandle config, FilePath path, ConfigurationLevel level, RepositoryHandle repo) { - int res = NativeMethods.git_config_add_file_ondisk(config, path, (uint)level, true); + // RepositoryHandle does implicit cast voodoo that is not null-safe, thus this explicit check + git_repository* repoHandle = (repo != null) ? (git_repository*)repo : null; + int res = NativeMethods.git_config_add_file_ondisk(config, path, (uint)level, repoHandle, true); Ensure.ZeroResult(res); } @@ -570,8 +496,8 @@ public static FilePath git_config_find_programdata() { return ConvertPath(NativeMethods.git_config_find_programdata); } - - public static unsafe void git_config_free(git_config *config) + + public static unsafe void git_config_free(git_config* config) { NativeMethods.git_config_free(config); } @@ -678,6 +604,14 @@ public static unsafe void git_config_set_string(ConfigurationHandle config, stri Ensure.ZeroResult(res); } + static readonly string non_existing_regex = Guid.NewGuid().ToString(); + + public static unsafe void git_config_add_string(ConfigurationHandle config, string name, string value) + { + int res = NativeMethods.git_config_set_multivar(config, name, non_existing_regex, value); + Ensure.ZeroResult(res); + } + public static unsafe ICollection git_config_foreach( ConfigurationHandle config, Func resultSelector) @@ -866,8 +800,8 @@ public static unsafe DiffHandle git_diff_tree_to_tree( ObjectId newTree, GitDiffOptions options) { - using (var osw1 = new ObjectSafeWrapper(oldTree, repo, true)) - using (var osw2 = new ObjectSafeWrapper(newTree, repo, true)) + using (var osw1 = new ObjectSafeWrapper(oldTree, repo, true, throwIfMissing: true)) + using (var osw2 = new ObjectSafeWrapper(newTree, repo, true, throwIfMissing: true)) { git_diff* diff; int res = NativeMethods.git_diff_tree_to_tree(out diff, repo, osw1.ObjectPtr, osw2.ObjectPtr, options); @@ -922,6 +856,86 @@ public static unsafe int git_diff_num_deltas(DiffHandle diff) #endregion + #region git_error_ + + public static int git_error_set_str(GitErrorCategory error_class, Exception exception) + { + if (exception is OutOfMemoryException) + { + NativeMethods.git_error_set_oom(); + return 0; + } + else + { + return NativeMethods.git_error_set_str(error_class, ErrorMessageFromException(exception)); + } + } + + public static int git_error_set_str(GitErrorCategory error_class, string errorString) + { + return NativeMethods.git_error_set_str(error_class, errorString); + } + + /// + /// This method will take an exception and try to generate an error message + /// that captures the important messages of the error. + /// The formatting is a bit subjective. + /// + /// + /// + public static string ErrorMessageFromException(Exception ex) + { + StringBuilder sb = new StringBuilder(); + BuildErrorMessageFromException(sb, 0, ex); + return sb.ToString(); + } + + private static void BuildErrorMessageFromException(StringBuilder sb, int level, Exception ex) + { + string indent = new string(' ', level * 4); + sb.AppendFormat("{0}{1}", indent, ex.Message); + + if (ex is AggregateException) + { + AggregateException aggregateException = ((AggregateException)ex).Flatten(); + + if (aggregateException.InnerExceptions.Count == 1) + { + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendFormat("{0}Contained Exception:{1}", indent, Environment.NewLine); + BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerException); + } + else + { + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendFormat("{0}Contained Exceptions:{1}", indent, Environment.NewLine); + for (int i = 0; i < aggregateException.InnerExceptions.Count; i++) + { + if (i != 0) + { + sb.AppendLine(); + sb.AppendLine(); + } + + BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerExceptions[i]); + } + } + } + else if (ex.InnerException != null) + { + sb.AppendLine(); + sb.AppendLine(); + sb.AppendFormat("{0}Inner Exception:{1}", indent, Environment.NewLine); + BuildErrorMessageFromException(sb, level + 1, ex.InnerException); + } + } + + #endregion + #region git_filter_ public static void git_filter_register(string name, IntPtr filterPtr, int priority) @@ -1024,7 +1038,7 @@ public static unsafe void git_index_add_bypath(IndexHandle index, FilePath path) public static unsafe Conflict git_index_conflict_get( IndexHandle index, - FilePath path) + string path) { git_index_entry* ancestor, ours, theirs; @@ -1089,7 +1103,7 @@ public static unsafe StageLevel git_index_entry_stage(git_index_entry* entry) return NativeMethods.git_index_get_byindex(index, n); } - public static unsafe git_index_entry* git_index_get_bypath(IndexHandle index, FilePath path, int stage) + public static unsafe git_index_entry* git_index_get_bypath(IndexHandle index, string path, int stage) { return NativeMethods.git_index_get_bypath(index, path, stage); } @@ -1128,7 +1142,7 @@ public static unsafe void git_index_read(IndexHandle index) Ensure.ZeroResult(res); } - public static unsafe void git_index_remove_bypath(IndexHandle index, FilePath path) + public static unsafe void git_index_remove_bypath(IndexHandle index, string path) { int res = NativeMethods.git_index_remove_bypath(index, path); Ensure.ZeroResult(res); @@ -1392,7 +1406,7 @@ public static unsafe ICollection git_note_foreach(RepositoryHa return git_foreach(resultSelector, c => NativeMethods.git_note_foreach(repo, notes_ref, (ref GitOid x, ref GitOid y, IntPtr p) => c(x, y, p), - IntPtr.Zero)); + IntPtr.Zero), GitErrorCode.NotFound); } public static unsafe string git_note_message(NoteHandle note) @@ -1601,7 +1615,7 @@ public static unsafe ObjectId git_odb_write(ObjectDatabaseHandle odb, byte[] dat { GitOid id; int res; - fixed(byte* p = data) + fixed (byte* p = data) { res = NativeMethods.git_odb_write(out id, odb, p, new UIntPtr((ulong)data.LongLength), type.ToGitObjectType()); } @@ -2161,6 +2175,7 @@ public static unsafe void git_remote_connect(RemoteHandle remote, GitDirection d catch (Exception) { customHeaders.Dispose(); + throw; } } @@ -2353,7 +2368,7 @@ public static unsafe IEnumerable git_remote_ls(Repository repository, } var refs = directRefs.Values.ToList(); - refs.Sort((r1, r2) => String.CompareOrdinal(r1.CanonicalName, r2.CanonicalName)); + refs.Sort((r1, r2) => string.CompareOrdinal(r1.CanonicalName, r2.CanonicalName)); return refs; } @@ -2560,7 +2575,7 @@ public static unsafe RepositoryHandle git_repository_new() public static unsafe void git_repository_open_ext(string path, RepositoryOpenFlags flags, string ceilingDirs) { int res; - git_repository *repo; + git_repository* repo; res = NativeMethods.git_repository_open_ext(out repo, path, flags, ceilingDirs); NativeMethods.git_repository_free(repo); @@ -2579,9 +2594,9 @@ public static unsafe FilePath git_repository_path(RepositoryHandle repo) return NativeMethods.git_repository_path(repo); } - public static unsafe void git_repository_set_config(RepositoryHandle repo, ConfigurationHandle config) + public static unsafe int git_repository_set_config(RepositoryHandle repo, ConfigurationHandle config) { - NativeMethods.git_repository_set_config(repo, config); + return NativeMethods.git_repository_set_config(repo, config); } public static unsafe void git_repository_set_ident(RepositoryHandle repo, string name, string email) @@ -2590,9 +2605,9 @@ public static unsafe void git_repository_set_ident(RepositoryHandle repo, string Ensure.ZeroResult(res); } - public static unsafe void git_repository_set_index(RepositoryHandle repo, IndexHandle index) + public static unsafe int git_repository_set_index(RepositoryHandle repo, IndexHandle index) { - NativeMethods.git_repository_set_index(repo, index); + return NativeMethods.git_repository_set_index(repo, index); } public static unsafe void git_repository_set_workdir(RepositoryHandle repo, FilePath workdir) @@ -2773,14 +2788,14 @@ public static unsafe void git_revwalk_reset(RevWalkerHandle walker) NativeMethods.git_revwalk_reset(walker); } - public static unsafe void git_revwalk_sorting(RevWalkerHandle walker, CommitSortStrategies options) + public static unsafe int git_revwalk_sorting(RevWalkerHandle walker, CommitSortStrategies options) { - NativeMethods.git_revwalk_sorting(walker, options); + return NativeMethods.git_revwalk_sorting(walker, options); } - public static unsafe void git_revwalk_simplify_first_parent(RevWalkerHandle walker) + public static unsafe int git_revwalk_simplify_first_parent(RevWalkerHandle walker) { - NativeMethods.git_revwalk_simplify_first_parent(walker); + return NativeMethods.git_revwalk_simplify_first_parent(walker); } #endregion @@ -2791,7 +2806,7 @@ public static unsafe SignatureHandle git_signature_new(string name, string email { git_signature* ptr; - int res = NativeMethods.git_signature_new(out ptr, name, email, when.ToSecondsSinceEpoch(), + int res = NativeMethods.git_signature_new(out ptr, name, email, when.ToUnixTimeSeconds(), (int)when.Offset.TotalMinutes); Ensure.ZeroResult(res); @@ -2953,7 +2968,7 @@ public static unsafe int git_status_list_entrycount(StatusListHandle list) /// Returns a handle to the corresponding submodule, /// or an invalid handle if a submodule is not found. /// - public static unsafe SubmoduleHandle git_submodule_lookup(RepositoryHandle repo, FilePath name) + public static unsafe SubmoduleHandle git_submodule_lookup(RepositoryHandle repo, string name) { git_submodule* submodule; var res = NativeMethods.git_submodule_lookup(out submodule, repo, name); @@ -3196,7 +3211,7 @@ public static void git_trace_set(LogLevel level, NativeMethods.git_trace_cb call #region git_transport_ - public static void git_transport_register(String prefix, IntPtr transport_cb, IntPtr param) + public static void git_transport_register(string prefix, IntPtr transport_cb, IntPtr param) { int res = NativeMethods.git_transport_register(prefix, transport_cb, param); @@ -3209,7 +3224,7 @@ public static void git_transport_register(String prefix, IntPtr transport_cb, In Ensure.ZeroResult(res); } - public static void git_transport_unregister(String prefix) + public static void git_transport_unregister(string prefix) { int res = NativeMethods.git_transport_unregister(prefix); @@ -3250,9 +3265,9 @@ public static unsafe TreeEntryHandle git_tree_entry_byindex(ObjectHandle tree, l return new TreeEntryHandle(handle, false); } - public static unsafe TreeEntryHandle git_tree_entry_bypath(RepositoryHandle repo, ObjectId id, FilePath treeentry_path) + public static unsafe TreeEntryHandle git_tree_entry_bypath(RepositoryHandle repo, ObjectId id, string treeentry_path) { - using (var obj = new ObjectSafeWrapper(id, repo)) + using (var obj = new ObjectSafeWrapper(id, repo, throwIfMissing: true)) { git_tree_entry* treeEntryPtr; int res = NativeMethods.git_tree_entry_bypath(out treeEntryPtr, obj.ObjectPtr, treeentry_path); @@ -3370,6 +3385,20 @@ private enum LibGit2Option GetWindowsSharemode, // GIT_OPT_GET_WINDOWS_SHAREMODE SetWindowsSharemode, // GIT_OPT_SET_WINDOWS_SHAREMODE EnableStrictHashVerification, // GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION + SetAllocator, // GIT_OPT_SET_ALLOCATOR, + EnableUnsavedIndexSafety, // GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, + GetPackMaxObject, // GIT_OPT_GET_PACK_MAX_OBJECTS, + SetPackMaxObjects, // GIT_OPT_SET_PACK_MAX_OBJECTS, + DisabledPackKeepFileChecks, // GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, + EnableHttpExpectContinue, // GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, + GetMWindowFileLimit, // GIT_OPT_GET_MWINDOW_FILE_LIMIT, + SetMWindowFileLimit, // GIT_OPT_SET_MWINDOW_FILE_LIMIT, + SetOdbPackedPriority, // GIT_OPT_SET_ODB_PACKED_PRIORITY, + SetOdbLoosePriority, // GIT_OPT_SET_ODB_LOOSE_PRIORITY, + GetExtensions, // GIT_OPT_GET_EXTENSIONS, + SetExtensions, // GIT_OPT_SET_EXTENSIONS + GetOwnerValidation, // GIT_OPT_GET_OWNER_VALIDATION + SetOwnerValidation, // GIT_OPT_SET_OWNER_VALIDATION } /// @@ -3385,7 +3414,11 @@ public static string git_libgit2_opts_get_search_path(ConfigurationLevel level) using (var buf = new GitBuf()) { - var res = NativeMethods.git_libgit2_opts((int)LibGit2Option.GetSearchPath, (uint)level, buf); + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.GetSearchPath, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, (uint)level, buf); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.GetSearchPath, (uint)level, buf); Ensure.ZeroResult(res); path = LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty; @@ -3396,7 +3429,10 @@ public static string git_libgit2_opts_get_search_path(ConfigurationLevel level) public static void git_libgit2_opts_enable_strict_hash_verification(bool enabled) { - NativeMethods.git_libgit2_opts((int)LibGit2Option.EnableStrictHashVerification, enabled ? 1 : 0); + if (isOSXArm64) + NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.EnableStrictHashVerification, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, enabled ? 1 : 0); + else + NativeMethods.git_libgit2_opts((int)LibGit2Option.EnableStrictHashVerification, enabled ? 1 : 0); } /// @@ -3409,8 +3445,293 @@ public static void git_libgit2_opts_enable_strict_hash_verification(bool enabled /// public static void git_libgit2_opts_set_search_path(ConfigurationLevel level, string path) { - var res = NativeMethods.git_libgit2_opts((int)LibGit2Option.SetSearchPath, (uint)level, path); + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.SetSearchPath, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, (uint)level, path); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.SetSearchPath, (uint)level, path); + Ensure.ZeroResult(res); + } + + /// + /// Enable or disable the libgit2 cache + /// + /// true to enable the cache, false otherwise + public static void git_libgit2_opts_set_enable_caching(bool enabled) + { + // libgit2 expects non-zero value for true + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.EnableCaching, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, enabled ? 1 : 0); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.EnableCaching, enabled ? 1 : 0); + Ensure.ZeroResult(res); + } + + /// + /// Enable or disable the ofs_delta capabilty + /// + /// true to enable the ofs_delta capabilty, false otherwise + public static void git_libgit2_opts_set_enable_ofsdelta(bool enabled) + { + // libgit2 expects non-zero value for true + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.EnableOfsDelta, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, enabled ? 1 : 0); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.EnableOfsDelta, enabled ? 1 : 0); + Ensure.ZeroResult(res); + } + + /// + /// Enable or disable the strict_object_creation capabilty + /// + /// true to enable the strict_object_creation capabilty, false otherwise + public static void git_libgit2_opts_set_enable_strictobjectcreation(bool enabled) + { + // libgit2 expects non-zero value for true + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.EnableStrictObjectCreation, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, enabled ? 1 : 0); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.EnableStrictObjectCreation, enabled ? 1 : 0); + Ensure.ZeroResult(res); + } + + /// + /// Sets the user-agent string to be used by the HTTP(S) transport. + /// Note that "git/2.0" will be prepended for compatibility. + /// + /// The user-agent string to use + public static void git_libgit2_opts_set_user_agent(string userAgent) + { + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.SetUserAgent, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, userAgent); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.SetUserAgent, userAgent); + Ensure.ZeroResult(res); + } + + /// + /// Gets the user-agent string used by libgit2. + /// + /// The user-agent string. + /// + /// + public static string git_libgit2_opts_get_user_agent() + { + string userAgent; + + using (var buf = new GitBuf()) + { + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.GetUserAgent, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, buf); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.GetUserAgent, buf); + Ensure.ZeroResult(res); + + userAgent = LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty; + } + + return userAgent; + } + + public static void git_libgit2_opts_set_extensions(string[] extensions) + { + using (var array = GitStrArrayManaged.BuildFrom(extensions)) + { + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.SetExtensions, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, array.Array.Strings, array.Array.Count); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.SetExtensions, array.Array.Strings, array.Array.Count); + Ensure.ZeroResult(res); + } + } + + public static string[] git_libgit2_opts_get_extensions() + { + var array = new GitStrArrayNative(); + + try + { + int res; + if (isOSXArm64) + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.GetExtensions, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out array.Array); + else + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.GetExtensions, out array.Array); + Ensure.ZeroResult(res); + + return array.ReadStrings(); + } + finally + { + array.Dispose(); + } + } + + /// + /// Gets the value of owner validation + /// + public static unsafe bool git_libgit2_opts_get_owner_validation() + { + int res; + int enabled; + + if (isOSXArm64) + { + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.GetOwnerValidation, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, &enabled); + } + else + { + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.GetOwnerValidation, &enabled); + } + + Ensure.ZeroResult(res); + + return enabled != 0; + } + + /// + /// Enable or disable owner validation + /// + /// true to enable owner validation, false otherwise + public static void git_libgit2_opts_set_owner_validation(bool enabled) + { + int res; + + if (isOSXArm64) + { + res = NativeMethods.git_libgit2_opts_osxarm64((int)LibGit2Option.SetOwnerValidation, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, enabled ? 1 : 0); + } + else + { + res = NativeMethods.git_libgit2_opts((int)LibGit2Option.SetOwnerValidation, enabled ? 1 : 0); + } + + Ensure.ZeroResult(res); + } + #endregion + + #region git_worktree_ + + /// + /// Returns a handle to the corresponding worktree, + /// or an invalid handle if a worktree is not found. + /// + public static unsafe WorktreeHandle git_worktree_lookup(RepositoryHandle repo, string name) + { + git_worktree* worktree; + var res = NativeMethods.git_worktree_lookup(out worktree, repo, name); + + switch (res) + { + case (int)GitErrorCode.Error: + case (int)GitErrorCode.NotFound: + case (int)GitErrorCode.Exists: + case (int)GitErrorCode.OrphanedHead: + return null; + + default: + Ensure.ZeroResult(res); + return new WorktreeHandle(worktree, true); + } + } + + public static unsafe IList git_worktree_list(RepositoryHandle repo) + { + var array = new GitStrArrayNative(); + + try + { + int res = NativeMethods.git_worktree_list(out array.Array, repo); + Ensure.ZeroResult(res); + + return array.ReadStrings(); + } + finally + { + array.Dispose(); + } + } + + public static unsafe RepositoryHandle git_repository_open_from_worktree(WorktreeHandle handle) + { + git_repository* repo; + int res = NativeMethods.git_repository_open_from_worktree(out repo, handle); + + if (res == (int)GitErrorCode.NotFound) + { + throw new RepositoryNotFoundException("Handle doesn't point at a valid Git repository or workdir."); + } + + Ensure.ZeroResult(res); + + return new RepositoryHandle(repo, true); + } + + public static unsafe WorktreeLock git_worktree_is_locked(WorktreeHandle worktree) + { + using (var buf = new GitBuf()) + { + int res = NativeMethods.git_worktree_is_locked(buf, worktree); + + if (res < 0) + { + // error + return null; + } + + if (res == (int)GitErrorCode.Ok) + { + return new WorktreeLock(); + } + + return new WorktreeLock(true, LaxUtf8Marshaler.FromNative(buf.ptr)); + } + } + + public static unsafe bool git_worktree_validate(WorktreeHandle worktree) + { + int res = NativeMethods.git_worktree_validate(worktree); + + return res == (int)GitErrorCode.Ok; + } + + public static unsafe bool git_worktree_unlock(WorktreeHandle worktree) + { + int res = NativeMethods.git_worktree_unlock(worktree); + + return res == (int)GitErrorCode.Ok; + } + + public static unsafe bool git_worktree_lock(WorktreeHandle worktree, string reason) + { + int res = NativeMethods.git_worktree_lock(worktree, reason); + + return res == (int)GitErrorCode.Ok; + } + + public static unsafe WorktreeHandle git_worktree_add( + RepositoryHandle repo, + string name, + string path, + git_worktree_add_options options) + { + git_worktree* worktree; + int res = NativeMethods.git_worktree_add(out worktree, repo, name, path, options); Ensure.ZeroResult(res); + return new WorktreeHandle(worktree, true); + } + + public static unsafe bool git_worktree_prune(WorktreeHandle worktree, + git_worktree_prune_options options) + { + int res = NativeMethods.git_worktree_prune(worktree, options); + Ensure.ZeroResult(res); + return true; } #endregion @@ -3429,7 +3750,7 @@ private static ICollection git_foreach( if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) { - return new TResult[0]; + return Array.Empty(); } Ensure.ZeroResult(res); @@ -3450,7 +3771,7 @@ private static ICollection git_foreach( if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) { - return new TResult[0]; + return Array.Empty(); } Ensure.ZeroResult(res); @@ -3471,7 +3792,7 @@ private static ICollection git_foreach( if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) { - return new TResult[0]; + return Array.Empty(); } Ensure.ZeroResult(res); @@ -3494,7 +3815,7 @@ private static ICollection git_foreach( if (ignoredErrorCodes != null && ignoredErrorCodes.Contains((GitErrorCode)res)) { - return new TResult[0]; + return Array.Empty(); } Ensure.ZeroResult(res); diff --git a/LibGit2Sharp/Core/TarWriter.cs b/LibGit2Sharp/Core/TarWriter.cs index 7c479cec9..0a051b9e6 100644 --- a/LibGit2Sharp/Core/TarWriter.cs +++ b/LibGit2Sharp/Core/TarWriter.cs @@ -301,38 +301,38 @@ public UsTarHeader( if (userName.Length > 32) { - throw new ArgumentException("ustar userName cannot be longer than 32 characters.", "userName"); + throw new ArgumentException("ustar userName cannot be longer than 32 characters.", nameof(userName)); } if (groupName.Length > 32) { - throw new ArgumentException("ustar groupName cannot be longer than 32 characters.", "groupName"); + throw new ArgumentException("ustar groupName cannot be longer than 32 characters.", nameof(groupName)); } if (userId.Length > 7) { - throw new ArgumentException("ustar userId cannot be longer than 7 characters.", "userId"); + throw new ArgumentException("ustar userId cannot be longer than 7 characters.", nameof(userId)); } if (groupId.Length > 7) { - throw new ArgumentException("ustar groupId cannot be longer than 7 characters.", "groupId"); + throw new ArgumentException("ustar groupId cannot be longer than 7 characters.", nameof(groupId)); } if (deviceMajorNumber.Length > 7) { - throw new ArgumentException("ustar deviceMajorNumber cannot be longer than 7 characters.", "deviceMajorNumber"); + throw new ArgumentException("ustar deviceMajorNumber cannot be longer than 7 characters.", nameof(deviceMajorNumber)); } if (deviceMinorNumber.Length > 7) { - throw new ArgumentException("ustar deviceMinorNumber cannot be longer than 7 characters.", "deviceMinorNumber"); + throw new ArgumentException("ustar deviceMinorNumber cannot be longer than 7 characters.", nameof(deviceMinorNumber)); } if (link.Length > 100) { - throw new ArgumentException("ustar link cannot be longer than 100 characters.", "link"); + throw new ArgumentException("ustar link cannot be longer than 100 characters.", nameof(link)); } #endregion this.mode = Convert.ToString(mode, 8).PadLeft(7, '0'); this.size = size; - unixTime = Convert.ToString(lastModificationTime.ToSecondsSinceEpoch(), 8).PadLeft(11, '0'); + unixTime = Convert.ToString(lastModificationTime.ToUnixTimeSeconds(), 8).PadLeft(11, '0'); this.userId = userId.PadLeft(7, '0'); this.groupId = userId.PadLeft(7, '0'); this.userName = userName; diff --git a/LibGit2Sharp/Core/Utf8Marshaler.cs b/LibGit2Sharp/Core/Utf8Marshaler.cs index fbe127abf..54e0086cb 100644 --- a/LibGit2Sharp/Core/Utf8Marshaler.cs +++ b/LibGit2Sharp/Core/Utf8Marshaler.cs @@ -19,7 +19,7 @@ internal class LaxUtf8NoCleanupMarshaler : LaxUtf8Marshaler { private static readonly LaxUtf8NoCleanupMarshaler staticInstance = new LaxUtf8NoCleanupMarshaler(); - public new static ICustomMarshaler GetInstance(String cookie) + public new static ICustomMarshaler GetInstance(string cookie) { return staticInstance; } @@ -42,7 +42,7 @@ public override void CleanUpNativeData(IntPtr pNativeData) /// Use this marshaler for function parameters, for example: /// [DllImport(libgit2)] /// internal static extern int git_tag_delete(RepositorySafeHandle repo, - /// [MarshalAs(UnmanagedType.CustomMarshaler, + /// [MarshalAs(UnmanagedType.CustomMarshaler /// MarshalCookie = UniqueId.UniqueIdentifier, /// MarshalTypeRef = typeof(StrictUtf8Marshaler))] String tagName); /// @@ -60,14 +60,14 @@ static StrictUtf8Marshaler() public StrictUtf8Marshaler() : base(encoding) { } - public static ICustomMarshaler GetInstance(String cookie) + public static ICustomMarshaler GetInstance(string cookie) { return staticInstance; } #region ICustomMarshaler - public override Object MarshalNativeToManaged(IntPtr pNativeData) + public override object MarshalNativeToManaged(IntPtr pNativeData) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "{0} cannot be used to retrieve data from libgit2.", @@ -76,7 +76,7 @@ public override Object MarshalNativeToManaged(IntPtr pNativeData) #endregion - public static IntPtr FromManaged(String value) + public static IntPtr FromManaged(string value) { return FromManaged(encoding, value); } @@ -96,7 +96,7 @@ internal class LaxUtf8Marshaler : EncodingMarshaler public LaxUtf8Marshaler() : base(Encoding) { } - public static ICustomMarshaler GetInstance(String cookie) + public static ICustomMarshaler GetInstance(string cookie) { return staticInstance; } diff --git a/LibGit2Sharp/CustomDictionary.xml b/LibGit2Sharp/CustomDictionary.xml deleted file mode 100644 index fe603c22b..000000000 --- a/LibGit2Sharp/CustomDictionary.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - git - sha - unstage - unstaged - compat - oid - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/LibGit2Sharp/Diff.cs b/LibGit2Sharp/Diff.cs index 9bf14660c..857eb8ed1 100644 --- a/LibGit2Sharp/Diff.cs +++ b/LibGit2Sharp/Diff.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; using LibGit2Sharp.Core; @@ -63,6 +62,11 @@ private static GitDiffOptions BuildOptions(DiffModifiers diffOptions, FilePath[] options.Flags |= GitDiffOptionFlags.GIT_DIFF_DISABLE_PATHSPEC_MATCH; } + if (compareOptions.IndentHeuristic) + { + options.Flags |= GitDiffOptionFlags.GIT_DIFF_INDENT_HEURISTIC; + } + if (matchedPathsAggregator != null) { options.NotifyCallback = matchedPathsAggregator.OnGitDiffNotify; @@ -351,7 +355,7 @@ public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable(diff); @@ -541,7 +545,7 @@ private DiffHandle BuildDiffList( MatchedPathsAggregator matchedPaths = null; - // We can't match paths unless we've got something to match + // We can't match paths unless we've got something to match // against and we're told to do so. if (filePaths != null && explicitPathsOptions != null) { @@ -648,7 +652,7 @@ private static void DispatchUnmatchedPaths( List unmatchedPaths = (filePaths != null ? filePaths.Except(matchedPaths) : Enumerable.Empty()).ToList(); - if (!unmatchedPaths.Any()) + if (unmatchedPaths.Count == 0) { return; } diff --git a/LibGit2Sharp/DiffTargets.cs b/LibGit2Sharp/DiffTargets.cs index 58e3f2f4d..40203ee60 100644 --- a/LibGit2Sharp/DiffTargets.cs +++ b/LibGit2Sharp/DiffTargets.cs @@ -18,4 +18,4 @@ public enum DiffTargets /// WorkingDirectory = 2, } -} \ No newline at end of file +} diff --git a/LibGit2Sharp/EmptyCommitException.cs b/LibGit2Sharp/EmptyCommitException.cs index 8cd48e49f..00d1081e5 100644 --- a/LibGit2Sharp/EmptyCommitException.cs +++ b/LibGit2Sharp/EmptyCommitException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { @@ -7,7 +9,9 @@ namespace LibGit2Sharp /// The exception that is thrown when a commit would create an "empty" /// commit that is treesame to its parent without an explicit override. /// +#if NETFRAMEWORK [Serializable] +#endif public class EmptyCommitException : LibGit2SharpException { /// @@ -27,7 +31,7 @@ public EmptyCommitException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public EmptyCommitException(string format, params object[] args) : base(format, args) @@ -42,6 +46,7 @@ public EmptyCommitException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,5 +55,6 @@ public EmptyCommitException(string message, Exception innerException) protected EmptyCommitException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/LibGit2Sharp/EntryExistsException.cs b/LibGit2Sharp/EntryExistsException.cs index 2c46e4acd..3ebfbdfba 100644 --- a/LibGit2Sharp/EntryExistsException.cs +++ b/LibGit2Sharp/EntryExistsException.cs @@ -1,5 +1,8 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif + using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -7,7 +10,9 @@ namespace LibGit2Sharp /// /// The exception that is thrown attempting to create a resource that already exists. /// +#if NETFRAMEWORK [Serializable] +#endif public class EntryExistsException : LibGit2SharpException { /// @@ -27,7 +32,7 @@ public EntryExistsException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public EntryExistsException(string format, params object[] args) : base(format, args) @@ -42,6 +47,7 @@ public EntryExistsException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,6 +56,7 @@ public EntryExistsException(string message, Exception innerException) protected EntryExistsException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif internal EntryExistsException(string message, GitErrorCode code, GitErrorCategory category) : base(message, code, category) diff --git a/LibGit2Sharp/ExtraDefine.targets b/LibGit2Sharp/ExtraDefine.targets deleted file mode 100644 index b5ba508b5..000000000 --- a/LibGit2Sharp/ExtraDefine.targets +++ /dev/null @@ -1,7 +0,0 @@ - - - - - $(DefineConstants);$(ExtraDefine) - - diff --git a/LibGit2Sharp/FetchHead.cs b/LibGit2Sharp/FetchHead.cs index 456abedc2..812865cf3 100644 --- a/LibGit2Sharp/FetchHead.cs +++ b/LibGit2Sharp/FetchHead.cs @@ -61,7 +61,7 @@ public virtual GitObject Target /// The URL of the remote repository this /// has been built from. /// - public virtual String Url { get; private set; } + public virtual string Url { get; private set; } /// /// Determines if this fetch head entry has been explicitly fetched. diff --git a/LibGit2Sharp/FetchOptions.cs b/LibGit2Sharp/FetchOptions.cs index a9d2fb2c7..6f354a5d5 100644 --- a/LibGit2Sharp/FetchOptions.cs +++ b/LibGit2Sharp/FetchOptions.cs @@ -25,5 +25,34 @@ public sealed class FetchOptions : FetchOptionsBase /// /// public bool? Prune { get; set; } + + /// + /// Specifies the depth of the fetch to perform. + /// + /// Default value is 0 (full fetch). + /// + /// + public int Depth { get; set; } = 0; + + /// + /// Get/Set the custom headers. + /// + /// + /// This allows you to set custom headers (e.g. X-Forwarded-For, + /// X-Request-Id, etc), + /// + /// + /// + /// Libgit2 sets some headers for HTTP requests (User-Agent, Host, + /// Accept, Content-Type, Transfer-Encoding, Content-Length, Accept) that + /// cannot be overriden. + /// + /// + /// var fetchOptions - new FetchOptions() { + /// CustomHeaders = new String[] {"X-Request-Id: 12345"} + /// }; + /// + /// The custom headers string array + public string[] CustomHeaders { get; set; } } } diff --git a/LibGit2Sharp/FetchOptionsBase.cs b/LibGit2Sharp/FetchOptionsBase.cs index 7b946e9e1..0e548652f 100644 --- a/LibGit2Sharp/FetchOptionsBase.cs +++ b/LibGit2Sharp/FetchOptionsBase.cs @@ -34,8 +34,8 @@ internal FetchOptionsBase() public CredentialsHandler CredentialsProvider { get; set; } /// - /// This hanlder will be called to let the user make a decision on whether to allow - /// the connection to preoceed based on the certificate presented by the server. + /// This handler will be called to let the user make a decision on whether to allow + /// the connection to proceed based on the certificate presented by the server. /// public CertificateCheckHandler CertificateCheck { get; set; } @@ -48,5 +48,10 @@ internal FetchOptionsBase() /// Completed operating on the current repository. /// public RepositoryOperationCompleted RepositoryOperationCompleted { get; set; } + + /// + /// Options for connecting through a proxy. + /// + public ProxyOptions ProxyOptions { get; } = new(); } } diff --git a/LibGit2Sharp/Filter.cs b/LibGit2Sharp/Filter.cs index 050d4a83c..0ab999f19 100644 --- a/LibGit2Sharp/Filter.cs +++ b/LibGit2Sharp/Filter.cs @@ -165,10 +165,10 @@ protected virtual void Smudge(string path, string root, Stream input, Stream out } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as Filter); @@ -236,7 +236,7 @@ int InitializeCallback(IntPtr filterPointer) { Log.Write(LogLevel.Error, "Filter.InitializeCallback exception"); Log.Write(LogLevel.Error, exception.ToString()); - Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + Proxy.git_error_set_str(GitErrorCategory.Filter, exception); result = (int)GitErrorCode.Error; } return result; @@ -261,8 +261,8 @@ int StreamCreateCallback(out IntPtr git_writestream_out, GitFilter self, IntPtr Marshal.StructureToPtr(state.thisStream, state.thisPtr, false); state.nextPtr = git_writestream_next; - state.nextStream = (GitWriteStream)Marshal.PtrToStructure(state.nextPtr, typeof(GitWriteStream)); - + state.nextStream = Marshal.PtrToStructure(state.nextPtr); + state.filterSource = FilterSource.FromNativePtr(filterSourcePtr); state.output = new WriteStream(state.nextStream, state.nextPtr); @@ -270,7 +270,7 @@ int StreamCreateCallback(out IntPtr git_writestream_out, GitFilter self, IntPtr if (!activeStreams.TryAdd(state.thisPtr, state)) { - // AFAICT this is a theoretical error that could only happen if we manage + // AFAICT this is a theoretical error that could only happen if we manage // to free the stream pointer but fail to remove the dictionary entry. throw new InvalidOperationException("Overlapping stream pointers"); } @@ -286,7 +286,7 @@ int StreamCreateCallback(out IntPtr git_writestream_out, GitFilter self, IntPtr Log.Write(LogLevel.Error, "Filter.StreamCreateCallback exception"); Log.Write(LogLevel.Error, exception.ToString()); - Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + Proxy.git_error_set_str(GitErrorCategory.Filter, exception); result = (int)GitErrorCode.Error; } @@ -304,9 +304,9 @@ int StreamCloseCallback(IntPtr stream) { Ensure.ArgumentNotZeroIntPtr(stream, "stream"); - if(!activeStreams.TryGetValue(stream, out state)) + if (!activeStreams.TryGetValue(stream, out state)) { - throw new ArgumentException("Unknown stream pointer", "stream"); + throw new ArgumentException("Unknown stream pointer", nameof(stream)); } Ensure.ArgumentIsExpectedIntPtr(stream, state.thisPtr, "stream"); @@ -322,7 +322,7 @@ int StreamCloseCallback(IntPtr stream) { Log.Write(LogLevel.Error, "Filter.StreamCloseCallback exception"); Log.Write(LogLevel.Error, exception.ToString()); - Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + Proxy.git_error_set_str(GitErrorCategory.Filter, exception); result = (int)GitErrorCode.Error; } @@ -339,7 +339,7 @@ void StreamFreeCallback(IntPtr stream) if (!activeStreams.TryRemove(stream, out state)) { - throw new ArgumentException("Double free or invalid stream pointer", "stream"); + throw new ArgumentException("Double free or invalid stream pointer", nameof(stream)); } Ensure.ArgumentIsExpectedIntPtr(stream, state.thisPtr, "stream"); @@ -365,7 +365,7 @@ unsafe int StreamWriteCallback(IntPtr stream, IntPtr buffer, UIntPtr len) if (!activeStreams.TryGetValue(stream, out state)) { - throw new ArgumentException("Invalid or already freed stream pointer", "stream"); + throw new ArgumentException("Invalid or already freed stream pointer", nameof(stream)); } Ensure.ArgumentIsExpectedIntPtr(stream, state.thisPtr, "stream"); @@ -384,7 +384,7 @@ unsafe int StreamWriteCallback(IntPtr stream, IntPtr buffer, UIntPtr len) break; default: - Proxy.giterr_set_str(GitErrorCategory.Filter, "Unexpected filter mode."); + Proxy.git_error_set_str(GitErrorCategory.Filter, "Unexpected filter mode."); return (int)GitErrorCode.Ambiguous; } } @@ -393,7 +393,7 @@ unsafe int StreamWriteCallback(IntPtr stream, IntPtr buffer, UIntPtr len) { Log.Write(LogLevel.Error, "Filter.StreamWriteCallback exception"); Log.Write(LogLevel.Error, exception.ToString()); - Proxy.giterr_set_str(GitErrorCategory.Filter, exception); + Proxy.git_error_set_str(GitErrorCategory.Filter, exception); result = (int)GitErrorCode.Error; } diff --git a/LibGit2Sharp/FilterSource.cs b/LibGit2Sharp/FilterSource.cs index ed551aba8..ab1dcb35c 100644 --- a/LibGit2Sharp/FilterSource.cs +++ b/LibGit2Sharp/FilterSource.cs @@ -28,7 +28,7 @@ internal unsafe FilterSource(FilePath path, FilterMode mode, git_filter_source* /// internal static unsafe FilterSource FromNativePtr(IntPtr ptr) { - return FromNativePtr((git_filter_source*) ptr.ToPointer()); + return FromNativePtr((git_filter_source*)ptr.ToPointer()); } /// diff --git a/LibGit2Sharp/FollowFilter.cs b/LibGit2Sharp/FollowFilter.cs deleted file mode 100644 index e4665ed45..000000000 --- a/LibGit2Sharp/FollowFilter.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LibGit2Sharp -{ - /// - /// Criteria used to order the commits of the repository when querying its history. - /// - /// The commits will be enumerated from the current HEAD of the repository. - /// - /// - [Obsolete("This type is deprecated. Please use LibGit2Sharp.CommitFilter")] - public sealed class FollowFilter - { - private static readonly List AllowedSortStrategies = new List - { - CommitSortStrategies.Topological, - CommitSortStrategies.Time, - CommitSortStrategies.Topological | CommitSortStrategies.Time - }; - - private CommitSortStrategies _sortBy; - - /// - /// Initializes a new instance of . - /// - public FollowFilter() - { - SortBy = CommitSortStrategies.Time; - } - - /// - /// The ordering strategy to use. - /// - /// By default, the commits are shown in reverse chronological order. - /// - /// - /// Only 'Topological', 'Time', or 'Topological | Time' are allowed. - /// - /// - public CommitSortStrategies SortBy - { - get { return _sortBy; } - - set - { - if (!AllowedSortStrategies.Contains(value)) - { - throw new ArgumentException("Unsupported sort strategy. Only 'Topological', 'Time', or 'Topological | Time' are allowed.", - "value"); - } - - _sortBy = value; - } - } - } -} diff --git a/LibGit2Sharp/GitObject.cs b/LibGit2Sharp/GitObject.cs index 2412003d8..f9813a3ea 100644 --- a/LibGit2Sharp/GitObject.cs +++ b/LibGit2Sharp/GitObject.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using LibGit2Sharp.Core; -using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { @@ -21,10 +19,20 @@ public abstract class GitObject : IEquatable, IBelongToARepository { typeof(Blob), ObjectType.Blob }, { typeof(TagAnnotation), ObjectType.Tag }, }; + internal static IDictionary TypeToGitKindMap = + new Dictionary + { + { typeof(Commit), GitObjectType.Commit }, + { typeof(Tree), GitObjectType.Tree }, + { typeof(Blob), GitObjectType.Blob }, + { typeof(TagAnnotation), GitObjectType.Tag }, + }; private static readonly LambdaEqualityHelper equalityHelper = new LambdaEqualityHelper(x => x.Id); + private readonly ILazy lazyIsMissing; + /// /// The containing the object. /// @@ -45,6 +53,7 @@ protected GitObject(Repository repo, ObjectId id) { this.repo = repo; Id = id; + lazyIsMissing = GitObjectLazyGroup.Singleton(repo, id, handle => handle == null, throwIfMissing: false); } /// @@ -52,15 +61,20 @@ protected GitObject(Repository repo, ObjectId id) /// public virtual ObjectId Id { get; private set; } + /// + /// Determine if the object is missing + /// + /// + /// This is common when dealing with partially cloned repositories as blobs or trees could be missing + /// + public virtual bool IsMissing => lazyIsMissing.Value; + /// /// Gets the 40 character sha1 of this object. /// - public virtual string Sha - { - get { return Id.Sha; } - } + public virtual string Sha => Id.Sha; - internal static GitObject BuildFrom(Repository repo, ObjectId id, GitObjectType type, FilePath path) + internal static GitObject BuildFrom(Repository repo, ObjectId id, GitObjectType type, string path) { switch (type) { @@ -83,24 +97,42 @@ internal static GitObject BuildFrom(Repository repo, ObjectId id, GitObjectType } } - internal Commit DereferenceToCommit(bool throwsIfCanNotBeDereferencedToACommit) + internal T Peel(bool throwOnError) where T : GitObject { - using (ObjectHandle peeledHandle = Proxy.git_object_peel(repo.Handle, Id, GitObjectType.Commit, throwsIfCanNotBeDereferencedToACommit)) + GitObjectType kind; + if (!TypeToGitKindMap.TryGetValue(typeof(T), out kind)) { - if (peeledHandle == null) + throw new ArgumentException("Invalid type passed to peel"); + } + + using (var handle = Proxy.git_object_peel(repo.Handle, Id, kind, throwOnError)) + { + if (handle == null) { return null; } - return (Commit)BuildFrom(repo, Proxy.git_object_id(peeledHandle), GitObjectType.Commit, null); + return (T)BuildFrom(this.repo, Proxy.git_object_id(handle), kind, null); } } /// - /// Determines whether the specified is equal to the current . + /// Peel this object to the specified type + /// + /// It will throw if the object cannot be peeled to the type. + /// + /// The kind of to peel to. + /// The peeled object + public virtual T Peel() where T : GitObject + { + return Peel(true); + } + + /// + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as GitObject); @@ -148,7 +180,7 @@ public override int GetHashCode() } /// - /// Returns the , a representation of the current . + /// Returns the , a representation of the current . /// /// The that represents the current . public override string ToString() diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs index 8fac7877f..9807155e7 100644 --- a/LibGit2Sharp/GlobalSettings.cs +++ b/LibGit2Sharp/GlobalSettings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Runtime.InteropServices; using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -13,60 +14,70 @@ public static class GlobalSettings { private static readonly Lazy version = new Lazy(Version.Build); private static readonly Dictionary registeredFilters; + private static readonly bool nativeLibraryPathAllowed; private static LogConfiguration logConfiguration = LogConfiguration.None; private static string nativeLibraryPath; private static bool nativeLibraryPathLocked; + private static readonly string nativeLibraryDefaultPath = null; static GlobalSettings() { - if (Platform.OperatingSystem == OperatingSystemType.Windows) - { - /* Assembly.CodeBase is not actually a correctly formatted - * URI. It's merely prefixed with `file:///` and has its - * backslashes flipped. This is superior to EscapedCodeBase, - * which does not correctly escape things, and ambiguates a - * space (%20) with a literal `%20` in the path. Sigh. - */ - var managedPath = Assembly.GetExecutingAssembly().CodeBase; - if (managedPath == null) - { - managedPath = Assembly.GetExecutingAssembly().Location; - } - else if (managedPath.StartsWith("file:///")) - { - managedPath = managedPath.Substring(8).Replace('/', '\\'); - } - else if (managedPath.StartsWith("file://")) - { - managedPath = @"\\" + managedPath.Substring(7).Replace('/', '\\'); - } + bool netFX = Platform.IsRunningOnNetFramework(); + bool netCore = Platform.IsRunningOnNetCore(); + + nativeLibraryPathAllowed = netFX || netCore; - nativeLibraryPath = Path.Combine(Path.Combine(Path.GetDirectoryName(managedPath), "lib"), "win32"); +#if NETFRAMEWORK + if (netFX) + { + // For .NET Framework apps the dependencies are deployed to lib/win32/{architecture} directory + nativeLibraryDefaultPath = Path.Combine(GetExecutingAssemblyDirectory(), "lib", "win32", Platform.ProcessorArchitecture); } +#endif registeredFilters = new Dictionary(); } - /// - /// Returns information related to the current LibGit2Sharp - /// library. - /// - public static Version Version +#if NETFRAMEWORK + private static string GetExecutingAssemblyDirectory() { - get + // Assembly.CodeBase is not actually a correctly formatted + // URI. It's merely prefixed with `file:///` and has its + // backslashes flipped. This is superior to EscapedCodeBase, + // which does not correctly escape things, and ambiguates a + // space (%20) with a literal `%20` in the path. Sigh. + var managedPath = Assembly.GetExecutingAssembly().CodeBase; + if (managedPath == null) { - return version.Value; + managedPath = Assembly.GetExecutingAssembly().Location; } + else if (managedPath.StartsWith("file:///")) + { + managedPath = managedPath.Substring(8).Replace('/', '\\'); + } + else if (managedPath.StartsWith("file://")) + { + managedPath = @"\\" + managedPath.Substring(7).Replace('/', '\\'); + } + + managedPath = Path.GetDirectoryName(managedPath); + return managedPath; } +#endif + + /// + /// Returns information related to the current LibGit2Sharp + /// library. + /// + public static Version Version => version.Value; /// /// Registers a new as a custom - /// smart-protocol transport with libgit2. Any Git remote with + /// smart-protocol transport with libgit2. Any Git remote with /// the scheme registered will delegate to the given transport - /// for all communication with the server. use this transport to communicate - /// with the server This is not commonly + /// for all communication with the server. This is not commonly /// used: some callers may want to re-use an existing connection to /// perform fetch / push operations to a remote. /// @@ -146,35 +157,35 @@ public static LogConfiguration LogConfiguration } /// - /// Sets a hint path for searching for native binaries: when - /// specified, native binaries will first be searched in a - /// subdirectory of the given path corresponding to the operating - /// system and architecture (eg, "x86" or "x64") before falling - /// back to the default path ("lib\win32\x86" or "lib\win32\x64" - /// next to the application). + /// Sets a path for loading native binaries on .NET Framework or .NET Core. + /// When specified, native library will first be searched under the given path. + /// + /// If the library is not found it will be searched in standard search paths: + /// , + /// and + /// . /// /// This must be set before any other calls to the library, - /// and is not available on Unix platforms: see your dynamic - /// library loader's documentation for details. + /// and is not available on other platforms than .NET Framework and .NET Core. /// /// public static string NativeLibraryPath { get { - if (Platform.OperatingSystem != OperatingSystemType.Windows) + if (!nativeLibraryPathAllowed) { - throw new LibGit2SharpException("Querying the native hint path is only supported on Windows platforms"); + throw new LibGit2SharpException("Querying the native hint path is only supported on .NET Framework and .NET Core platforms"); } - return nativeLibraryPath; + return nativeLibraryPath ?? nativeLibraryDefaultPath; } set { - if (Platform.OperatingSystem != OperatingSystemType.Windows) + if (!nativeLibraryPathAllowed) { - throw new LibGit2SharpException("Setting the native hint path is only supported on Windows platforms"); + throw new LibGit2SharpException("Setting the native hint path is only supported on .NET Framework and .NET Core platforms"); } if (nativeLibraryPathLocked) @@ -182,14 +193,21 @@ public static string NativeLibraryPath throw new LibGit2SharpException("You cannot set the native library path after it has been loaded"); } - nativeLibraryPath = value; + try + { + nativeLibraryPath = Path.GetFullPath(value); + } + catch (Exception e) + { + throw new LibGit2SharpException(e.Message); + } } } internal static string GetAndLockNativeLibraryPath() { nativeLibraryPathLocked = true; - return nativeLibraryPath; + return nativeLibraryPath ?? nativeLibraryDefaultPath; } /// @@ -229,9 +247,9 @@ public static FilterRegistration RegisterFilter(Filter filter, int priority) Ensure.ArgumentNotNull(filter, "filter"); if (priority < FilterRegistration.FilterPriorityMin || priority > FilterRegistration.FilterPriorityMax) { - throw new ArgumentOutOfRangeException("priority", + throw new ArgumentOutOfRangeException(nameof(priority), priority, - String.Format(System.Globalization.CultureInfo.InvariantCulture, + string.Format(System.Globalization.CultureInfo.InvariantCulture, "Filter priorities must be within the inclusive range of [{0}, {1}].", FilterRegistration.FilterPriorityMin, FilterRegistration.FilterPriorityMax)); @@ -319,9 +337,106 @@ public static void SetConfigSearchPaths(ConfigurationLevel level, params string[ Proxy.git_libgit2_opts_set_search_path(level, pathString); } + /// + /// Enable or disable strict hash verification. + /// + /// true to enable strict hash verification; false otherwise. public static void SetStrictHashVerification(bool enabled) { Proxy.git_libgit2_opts_enable_strict_hash_verification(enabled); } + + /// + /// Enable or disable the libgit2 cache + /// + /// true to enable the cache, false otherwise + public static void SetEnableCaching(bool enabled) + { + Proxy.git_libgit2_opts_set_enable_caching(enabled); + } + + /// + /// Enable or disable the ofs_delta capability + /// + /// true to enable the ofs_delta capability, false otherwise + public static void SetEnableOfsDelta(bool enabled) + { + Proxy.git_libgit2_opts_set_enable_ofsdelta(enabled); + } + + /// + /// Enable or disable the libgit2 strict_object_creation capability + /// + /// true to enable the strict_object_creation capability, false otherwise + public static void SetEnableStrictObjectCreation(bool enabled) + { + Proxy.git_libgit2_opts_set_enable_strictobjectcreation(enabled); + } + + /// + /// Sets the user-agent string to be used by the HTTP(S) transport. + /// Note that "git/2.0" will be prepended for compatibility. + /// + /// The user-agent string to use + public static void SetUserAgent(string userAgent) + { + Proxy.git_libgit2_opts_set_user_agent(userAgent); + } + + /// + /// Set that the given git extensions are supported by the caller. + /// + /// + /// Extensions supported by libgit2 may be negated by prefixing them with a `!`. For example: setting extensions to { "!noop", "newext" } indicates that the caller does not want + /// to support repositories with the `noop` extension but does want to support repositories with the `newext` extension. + /// + /// Supported extensions + public static void SetExtensions(params string[] extensions) + { + Proxy.git_libgit2_opts_set_extensions(extensions); + } + + /// + /// Returns the list of git extensions that are supported. + /// + /// + /// This is the list of built-in extensions supported by libgit2 and custom extensions that have been added with `SetExtensions`. Extensions that have been negated will not be returned. + /// + public static string[] GetExtensions() + { + return Proxy.git_libgit2_opts_get_extensions(); + } + + /// + /// Gets the user-agent string used by libgit2. + /// + /// The user-agent string. + /// + /// + public static string GetUserAgent() + { + return Proxy.git_libgit2_opts_get_user_agent(); + } + + /// + /// Gets the owner validation setting for repository directories. + /// + /// + public static bool GetOwnerValidation() + { + return Proxy.git_libgit2_opts_get_owner_validation(); + } + + /// + /// Sets whether repository directories should be owned by the current user. The default is to validate ownership. + /// + /// + /// Disabling owner validation can lead to security vulnerabilities (see CVE-2022-24765). + /// + /// true to enable owner validation; otherwise, false. + public static void SetOwnerValidation(bool enabled) + { + Proxy.git_libgit2_opts_set_owner_validation(enabled); + } } } diff --git a/LibGit2Sharp/IDiffResult.cs b/LibGit2Sharp/IDiffResult.cs index ed6e521fd..5090de88e 100644 --- a/LibGit2Sharp/IDiffResult.cs +++ b/LibGit2Sharp/IDiffResult.cs @@ -5,6 +5,6 @@ namespace LibGit2Sharp /// /// Marker interface to identify Diff results. /// - public interface IDiffResult: IDisposable + public interface IDiffResult : IDisposable { } } diff --git a/LibGit2Sharp/IQueryableCommitLog.cs b/LibGit2Sharp/IQueryableCommitLog.cs index 76ea49172..ab8a92136 100644 --- a/LibGit2Sharp/IQueryableCommitLog.cs +++ b/LibGit2Sharp/IQueryableCommitLog.cs @@ -22,15 +22,6 @@ public interface IQueryableCommitLog : ICommitLog /// A list of file history entries, ready to be enumerated. IEnumerable QueryBy(string path); - /// - /// Returns the list of commits of the repository representing the history of a file beyond renames. - /// - /// The file's path. - /// The options used to control which commits will be returned. - /// A list of file history entries, ready to be enumerated. - [Obsolete("This method is deprecated. Please use the overload which take LibGit2Sharp.CommitFilter")] - IEnumerable QueryBy(string path, FollowFilter filter); - /// /// Returns the list of commits of the repository representing the history of a file beyond renames. /// diff --git a/LibGit2Sharp/IRepository.cs b/LibGit2Sharp/IRepository.cs index e7fa9c713..fd19f9659 100644 --- a/LibGit2Sharp/IRepository.cs +++ b/LibGit2Sharp/IRepository.cs @@ -70,42 +70,9 @@ public interface IRepository : IDisposable SubmoduleCollection Submodules { get; } /// - /// Checkout the commit pointed at by the tip of the specified . - /// - /// If this commit is the current tip of the branch as it exists in the repository, the HEAD - /// will point to this branch. Otherwise, the HEAD will be detached, pointing at the commit sha. - /// - /// - /// The to check out. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - Branch Checkout(Branch branch, CheckoutOptions options); - - /// - /// Checkout the specified branch, reference or SHA. - /// - /// If the committishOrBranchSpec parameter resolves to a branch name, then the checked out HEAD will - /// will point to the branch. Otherwise, the HEAD will be detached, pointing at the commit sha. - /// - /// - /// A revparse spec for the commit or branch to checkout. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - Branch Checkout(string committishOrBranchSpec, CheckoutOptions options); - - /// - /// Checkout the specified . - /// - /// Will detach the HEAD and make it point to this commit sha. - /// + /// Worktrees in the repository. /// - /// The to check out. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - Branch Checkout(Commit commit, CheckoutOptions options); + WorktreeCollection Worktrees { get; } /// /// Checkout the specified tree. @@ -275,112 +242,6 @@ public interface IRepository : IDisposable /// The blame for the file. BlameHunkCollection Blame(string path, BlameOptions options); - /// - /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). - /// - /// If this path is ignored by configuration then it will not be staged unless is unset. - /// - /// The path of the file within the working directory. - /// Determines how paths will be staged. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Stage()")] - void Stage(string path, StageOptions stageOptions); - - /// - /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). - /// - /// Any paths (even those listed explicitly) that are ignored by configuration will not be staged unless is unset. - /// - /// The collection of paths of the files within the working directory. - /// Determines how paths will be staged. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Stage()")] - void Stage(IEnumerable paths, StageOptions stageOptions); - - /// - /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). - /// - /// The path of the file within the working directory. - /// - /// The passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - void Unstage(string path, ExplicitPathsOptions explicitPathsOptions); - - /// - /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). - /// - /// The collection of paths of the files within the working directory. - /// - /// The passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions); - - /// - /// Moves and/or renames a file in the working directory and promotes the change to the staging area. - /// - /// The path of the file within the working directory which has to be moved/renamed. - /// The target path of the file within the working directory. - [Obsolete("This method is deprecatd. Please use LibGit2Sharp.Commands.Move()")] - void Move(string sourcePath, string destinationPath); - - /// - /// Moves and/or renames a collection of files in the working directory and promotes the changes to the staging area. - /// - /// The paths of the files within the working directory which have to be moved/renamed. - /// The target paths of the files within the working directory. - [Obsolete("This method is deprecatd. Please use LibGit2Sharp.Commands.Move()")] - void Move(IEnumerable sourcePaths, IEnumerable destinationPaths); - - /// - /// Removes a file from the staging area, and optionally removes it from the working directory as well. - /// - /// If the file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the file from the working directory as well. - /// - /// - /// When not passing a , the passed path will be treated as - /// a pathspec. You can for example use it to pass the relative path to a folder inside the working directory, - /// so that all files beneath this folders, and the folder itself, will be removed. - /// - /// - /// The path of the file within the working directory. - /// True to remove the file from the working directory, False otherwise. - /// - /// The passed will be treated as an explicit path. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove()")] - void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); - - /// - /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. - /// - /// If a file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the files from the working directory as well. - /// - /// - /// When not passing a , the passed paths will be treated as - /// a pathspec. You can for example use it to pass the relative paths to folders inside the working directory, - /// so that all files beneath these folders, and the folders themselves, will be removed. - /// - /// - /// The collection of paths of the files within the working directory. - /// True to remove the files from the working directory, False otherwise. - /// - /// The passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); - /// /// Retrieves the state of a file in the working directory, comparing it against the staging area and the latest commit. /// @@ -404,7 +265,7 @@ public interface IRepository : IDisposable /// /// /// Optionally, the parameter allow to tweak the - /// search strategy (considering lightweith tags, or even branches as reference points) + /// search strategy (considering lightweight tags, or even branches as reference points) /// and the formatting of the returned identifier. /// /// diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index d68419ab1..321673606 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -26,13 +26,16 @@ public class Index : IEnumerable protected Index() { } - internal Index(Repository repo) + internal Index(IndexHandle handle, Repository repo) { this.repo = repo; - - handle = Proxy.git_repository_index(repo.Handle); + this.handle = handle; conflicts = new ConflictCollection(this); + } + internal Index(Repository repo) + : this(Proxy.git_repository_index(repo.Handle), repo) + { repo.RegisterForCleanup(handle); } @@ -244,7 +247,7 @@ private unsafe void AddEntryToTheIndex(string path, ObjectId id, Mode mode) var indexEntry = new git_index_entry { mode = (uint)mode, - path = (char*) pathPtr, + path = (char*)pathPtr, }; Marshal.Copy(id.RawId, 0, new IntPtr(indexEntry.id.Id), GitOid.Size); @@ -305,5 +308,16 @@ public virtual void Write() { Proxy.git_index_write(handle); } + + /// + /// Write the contents of this to a tree + /// + /// + public virtual Tree WriteToTree() + { + var treeId = Proxy.git_index_write_tree_to(this.handle, this.repo.Handle); + var result = this.repo.Lookup(treeId); + return result; + } } } diff --git a/LibGit2Sharp/IndexEntry.cs b/LibGit2Sharp/IndexEntry.cs index dc652dfcf..554d9a9f1 100644 --- a/LibGit2Sharp/IndexEntry.cs +++ b/LibGit2Sharp/IndexEntry.cs @@ -47,11 +47,11 @@ internal static unsafe IndexEntry BuildFromPtr(git_index_entry* entry) return null; } - FilePath path = LaxFilePathMarshaler.FromNative(entry->path); + string path = LaxUtf8Marshaler.FromNative(entry->path); return new IndexEntry { - Path = path.Native, + Path = path, Id = new ObjectId(entry->id.Id), StageLevel = Proxy.git_index_entry_stage(entry), Mode = (Mode)entry->mode, @@ -60,10 +60,10 @@ internal static unsafe IndexEntry BuildFromPtr(git_index_entry* entry) } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as IndexEntry); diff --git a/LibGit2Sharp/IndexNameEntry.cs b/LibGit2Sharp/IndexNameEntry.cs index 79b3f6993..40c202acc 100644 --- a/LibGit2Sharp/IndexNameEntry.cs +++ b/LibGit2Sharp/IndexNameEntry.cs @@ -63,10 +63,10 @@ internal static unsafe IndexNameEntry BuildFromPtr(git_index_name_entry* entry) public virtual string Theirs { get; private set; } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as IndexNameEntry); diff --git a/LibGit2Sharp/IndexReucEntry.cs b/LibGit2Sharp/IndexReucEntry.cs index 583df95ba..becd20122 100644 --- a/LibGit2Sharp/IndexReucEntry.cs +++ b/LibGit2Sharp/IndexReucEntry.cs @@ -88,10 +88,10 @@ internal static unsafe IndexReucEntry BuildFromPtr(git_index_reuc_entry* entry) public virtual Mode TheirMode { get; private set; } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as IndexReucEntry); diff --git a/LibGit2Sharp/IndexReucEntryCollection.cs b/LibGit2Sharp/IndexReucEntryCollection.cs index 61af48b18..818bce70c 100644 --- a/LibGit2Sharp/IndexReucEntryCollection.cs +++ b/LibGit2Sharp/IndexReucEntryCollection.cs @@ -40,7 +40,7 @@ public virtual unsafe IndexReucEntry this[string path] } } - private unsafe IndexReucEntry this[int idx] + private unsafe IndexReucEntry this[int idx] { get { diff --git a/LibGit2Sharp/InvalidSpecificationException.cs b/LibGit2Sharp/InvalidSpecificationException.cs index 64654852c..d9625dc32 100644 --- a/LibGit2Sharp/InvalidSpecificationException.cs +++ b/LibGit2Sharp/InvalidSpecificationException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -10,8 +12,10 @@ namespace LibGit2Sharp /// if the spec refers to an object of an incorrect type (e.g. asking to /// create a branch from a blob, or peeling a blob to a commit). /// +#if NETFRAMEWORK [Serializable] - public class InvalidSpecificationException : LibGit2SharpException +#endif + public class InvalidSpecificationException : NativeException { /// /// Initializes a new instance of the class. @@ -30,7 +34,7 @@ public InvalidSpecificationException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public InvalidSpecificationException(string format, params object[] args) : base(format, args) @@ -45,6 +49,7 @@ public InvalidSpecificationException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -53,9 +58,18 @@ public InvalidSpecificationException(string message, Exception innerException) protected InvalidSpecificationException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal InvalidSpecificationException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal InvalidSpecificationException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.InvalidSpecification; + } + } } } diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index 34fb0d630..1c4abef7b 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -1,397 +1,61 @@ - - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {EE6ED99F-CB12-4683-B055-D28FC7357A34} - Library - Properties - LibGit2Sharp - LibGit2Sharp - v4.0 - 512 - - - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;NET40 - prompt - 4 - true - AllRules.ruleset - bin\Debug\LibGit2Sharp.xml - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - bin\Release\LibGit2Sharp.xml - + + + net472;net8.0 + 12.0 + true + LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .NET + LibGit2Sharp contributors + Copyright © LibGit2Sharp contributors + libgit2 git + https://github.com/libgit2/libgit2sharp/ + LibGit2Sharp contributors + true + true + embedded true + ..\libgit2sharp.snk + square-logo.png + App_Readme/README.md + App_Readme/LICENSE.md + true + $(ArtifactsPath)\package + preview.0 + libgit2-$(libgit2_hash.Substring(0,7)) - - libgit2sharp.snk + + + true + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Objects.tt - - - - - - - - - - - - - - - - + + + - - - TextTemplatingFileGenerator - Objects.cs - - + + + + + + + - - - - - - - - - - - + + - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + $(MinVerMajor).$(MinVerMinor).0.0 - + diff --git a/LibGit2Sharp/LibGit2Sharp.v2.ncrunchproject b/LibGit2Sharp/LibGit2Sharp.v2.ncrunchproject deleted file mode 100644 index cc3cf2122..000000000 --- a/LibGit2Sharp/LibGit2Sharp.v2.ncrunchproject +++ /dev/null @@ -1,25 +0,0 @@ - - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/LibGit2Sharp/LibGit2SharpException.cs b/LibGit2Sharp/LibGit2SharpException.cs index e85dd638f..0518fa757 100644 --- a/LibGit2Sharp/LibGit2SharpException.cs +++ b/LibGit2Sharp/LibGit2SharpException.cs @@ -1,14 +1,17 @@ using System; using System.Globalization; +#if NETFRAMEWORK using System.Runtime.Serialization; -using LibGit2Sharp.Core; +#endif namespace LibGit2Sharp { /// /// The exception that is thrown when an error occurs during application execution. /// +#if NETFRAMEWORK [Serializable] +#endif public class LibGit2SharpException : Exception { /// @@ -37,13 +40,14 @@ public LibGit2SharpException(string message, Exception innerException) /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public LibGit2SharpException(string format, params object[] args) - : base(String.Format(CultureInfo.InvariantCulture, format, args)) + : base(string.Format(CultureInfo.InvariantCulture, format, args)) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -52,11 +56,6 @@ public LibGit2SharpException(string format, params object[] args) protected LibGit2SharpException(SerializationInfo info, StreamingContext context) : base(info, context) { } - - internal LibGit2SharpException(string message, GitErrorCode code, GitErrorCategory category) : this(message) - { - Data.Add("libgit2.code", (int)code); - Data.Add("libgit2.category", (int)category); - } +#endif } } diff --git a/LibGit2Sharp/Line.cs b/LibGit2Sharp/Line.cs new file mode 100644 index 000000000..830247fc3 --- /dev/null +++ b/LibGit2Sharp/Line.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// Represents a line with line number and content. + /// + public struct Line + { + /// + /// The line number of the original line in the blob. + /// + public int LineNumber { get; } + + /// + /// The content of the line in the original blob. + /// + public string Content { get; } + + internal Line(int lineNumber, string content) + { + LineNumber = lineNumber; + Content = content; + } + } +} diff --git a/LibGit2Sharp/LockedFileException.cs b/LibGit2Sharp/LockedFileException.cs index 05859503a..b38f40496 100644 --- a/LibGit2Sharp/LockedFileException.cs +++ b/LibGit2Sharp/LockedFileException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -7,8 +9,10 @@ namespace LibGit2Sharp /// /// The exception that is thrown attempting to open a locked file. /// +#if NETFRAMEWORK [Serializable] - public class LockedFileException : LibGit2SharpException +#endif + public class LockedFileException : NativeException { /// /// Initializes a new instance of the class. @@ -27,7 +31,7 @@ public LockedFileException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public LockedFileException(string format, params object[] args) : base(format, args) @@ -42,6 +46,7 @@ public LockedFileException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,9 +55,18 @@ public LockedFileException(string message, Exception innerException) protected LockedFileException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal LockedFileException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal LockedFileException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.LockedFile; + } + } } } diff --git a/LibGit2Sharp/MergeFetchHeadNotFoundException.cs b/LibGit2Sharp/MergeFetchHeadNotFoundException.cs index a86bf5caf..d7d761c1d 100644 --- a/LibGit2Sharp/MergeFetchHeadNotFoundException.cs +++ b/LibGit2Sharp/MergeFetchHeadNotFoundException.cs @@ -1,12 +1,16 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { /// /// The exception that is thrown when the ref to merge with was as part of a pull operation not fetched. /// +#if NETFRAMEWORK [Serializable] +#endif public class MergeFetchHeadNotFoundException : NotFoundException { /// @@ -26,7 +30,7 @@ public MergeFetchHeadNotFoundException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public MergeFetchHeadNotFoundException(string format, params object[] args) : base(format, args) @@ -41,6 +45,7 @@ public MergeFetchHeadNotFoundException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -49,5 +54,6 @@ public MergeFetchHeadNotFoundException(string message, Exception innerException) protected MergeFetchHeadNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/LibGit2Sharp/MergeOptionsBase.cs b/LibGit2Sharp/MergeOptionsBase.cs index 4bd391852..85e930ab1 100644 --- a/LibGit2Sharp/MergeOptionsBase.cs +++ b/LibGit2Sharp/MergeOptionsBase.cs @@ -50,6 +50,11 @@ protected MergeOptionsBase() /// How to handle conflicts encountered during a merge. /// public MergeFileFavor MergeFileFavor { get; set; } + + /// + /// Ignore changes in amount of whitespace + /// + public bool IgnoreWhitespaceChange { get; set; } } /// diff --git a/LibGit2Sharp/NameConflictException.cs b/LibGit2Sharp/NameConflictException.cs index 815415729..0517f2550 100644 --- a/LibGit2Sharp/NameConflictException.cs +++ b/LibGit2Sharp/NameConflictException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -7,8 +9,10 @@ namespace LibGit2Sharp /// /// The exception that is thrown when a reference, a remote, a submodule... with the same name already exists in the repository /// +#if NETFRAMEWORK [Serializable] - public class NameConflictException : LibGit2SharpException +#endif + public class NameConflictException : NativeException { /// /// Initializes a new instance of the class. @@ -27,7 +31,7 @@ public NameConflictException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public NameConflictException(string format, params object[] args) : base(format, args) @@ -42,6 +46,7 @@ public NameConflictException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,9 +55,18 @@ public NameConflictException(string message, Exception innerException) protected NameConflictException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal NameConflictException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal NameConflictException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.Exists; + } + } } } diff --git a/LibGit2Sharp/NativeDllName.targets b/LibGit2Sharp/NativeDllName.targets deleted file mode 100644 index a6afed504..000000000 --- a/LibGit2Sharp/NativeDllName.targets +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - . - $(MSBuildThisFileDirectory) - $(LibGit2SharpPath)\Core\NativeDllName.cs - $(CoreCompileDependsOn);GenerateNativeDllNameCs - $(CoreCleanDependsOn);CleanNativeDllNameCs - - - - - - - - - - diff --git a/LibGit2Sharp/NativeException.cs b/LibGit2Sharp/NativeException.cs new file mode 100644 index 000000000..66dc03c57 --- /dev/null +++ b/LibGit2Sharp/NativeException.cs @@ -0,0 +1,49 @@ +using System; +#if NETFRAMEWORK +using System.Runtime.Serialization; +#endif +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// An exception thrown that corresponds to a libgit2 (native library) error. + /// +#if NETFRAMEWORK + [Serializable] +#endif + public abstract class NativeException : LibGit2SharpException + { + /// + /// Needed for mocking purposes. + /// + protected NativeException() + { } + + internal NativeException(string message) + : base(message) + { } + + internal NativeException(string message, Exception innerException) + : base(message, innerException) + { } + + internal NativeException(string format, params object[] args) + : base(format, args) + { + } + +#if NETFRAMEWORK + internal NativeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + internal NativeException(string message, GitErrorCategory category) : this(message) + { + Data.Add("libgit2.category", (int)category); + } + + internal abstract GitErrorCode ErrorCode { get; } + } +} diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index 0d49c083b..ba0a33144 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -52,7 +52,26 @@ public virtual IEnumerable ListReferences(Remote remote) { Ensure.ArgumentNotNull(remote, "remote"); - return ListReferencesInternal(remote.Url, null); + return ListReferencesInternal(remote.Url, null, new ProxyOptions()); + } + + /// + /// List references in a repository. + /// + /// When the remote tips are ahead of the local ones, the retrieved + /// s may point to non existing + /// s in the local repository. In that + /// case, will return null. + /// + /// + /// The to list from. + /// Options for connecting through a proxy. + /// The references in the repository. + public virtual IEnumerable ListReferences(Remote remote, ProxyOptions proxyOptions) + { + Ensure.ArgumentNotNull(remote, "remote"); + + return ListReferencesInternal(remote.Url, null, proxyOptions); } /// @@ -72,7 +91,28 @@ public virtual IEnumerable ListReferences(Remote remote, CredentialsH Ensure.ArgumentNotNull(remote, "remote"); Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - return ListReferencesInternal(remote.Url, credentialsProvider); + return ListReferencesInternal(remote.Url, credentialsProvider, new ProxyOptions()); + } + + /// + /// List references in a repository. + /// + /// When the remote tips are ahead of the local ones, the retrieved + /// s may point to non existing + /// s in the local repository. In that + /// case, will return null. + /// + /// + /// The to list from. + /// The used to connect to remote repository. + /// Options for connecting through a proxy. + /// The references in the repository. + public virtual IEnumerable ListReferences(Remote remote, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions) + { + Ensure.ArgumentNotNull(remote, "remote"); + Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); + + return ListReferencesInternal(remote.Url, credentialsProvider, proxyOptions); } /// @@ -90,7 +130,7 @@ public virtual IEnumerable ListReferences(string url) { Ensure.ArgumentNotNull(url, "url"); - return ListReferencesInternal(url, null); + return ListReferencesInternal(url, null, new ProxyOptions()); } /// @@ -103,138 +143,86 @@ public virtual IEnumerable ListReferences(string url) /// /// /// The url to list from. - /// The used to connect to remote repository. + /// Options for connecting through a proxy. /// The references in the remote repository. - public virtual IEnumerable ListReferences(string url, CredentialsHandler credentialsProvider) + public virtual IEnumerable ListReferences(string url, ProxyOptions proxyOptions) { Ensure.ArgumentNotNull(url, "url"); - Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - - return ListReferencesInternal(url, credentialsProvider); - } - - private IEnumerable ListReferencesInternal(string url, CredentialsHandler credentialsProvider) - { - using (RemoteHandle remoteHandle = BuildRemoteHandle(repository.Handle, url)) - { - GitRemoteCallbacks gitCallbacks = new GitRemoteCallbacks { version = 1 }; - GitProxyOptions proxyOptions = new GitProxyOptions { Version = 1 }; - - if (credentialsProvider != null) - { - var callbacks = new RemoteCallbacks(credentialsProvider); - gitCallbacks = callbacks.GenerateCallbacks(); - } - - Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks, ref proxyOptions); - return Proxy.git_remote_ls(repository, remoteHandle); - } - } - - static RemoteHandle BuildRemoteHandle(RepositoryHandle repoHandle, string url) - { - Debug.Assert(repoHandle != null && !repoHandle.IsNull); - Debug.Assert(url != null); - - RemoteHandle remoteHandle = Proxy.git_remote_create_anonymous(repoHandle, url); - Debug.Assert(remoteHandle != null && !(remoteHandle.IsNull)); - return remoteHandle; + return ListReferencesInternal(url, null, proxyOptions); } /// - /// Fetch from the . + /// List references in a remote repository. + /// + /// When the remote tips are ahead of the local ones, the retrieved + /// s may point to non existing + /// s in the local repository. In that + /// case, will return null. + /// /// - /// The remote to fetch - [Obsolete("This method is deprecated. Please us LibGit2Sharp.Commands.Fetch()")] - public virtual void Fetch(Remote remote) + /// The url to list from. + /// The used to connect to remote repository. + /// The references in the remote repository. + public virtual IEnumerable ListReferences(string url, CredentialsHandler credentialsProvider) { - Commands.Fetch(repository, remote.Name, new string[0], null, null); - } + Ensure.ArgumentNotNull(url, "url"); + Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - /// - /// Fetch from the . - /// - /// The remote to fetch - /// controlling fetch behavior - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Fetch()")] - public virtual void Fetch(Remote remote, FetchOptions options) - { - Commands.Fetch(repository, remote.Name, new string[0], options, null); + return ListReferencesInternal(url, credentialsProvider, new ProxyOptions()); } /// - /// Fetch from the . + /// List references in a remote repository. + /// + /// When the remote tips are ahead of the local ones, the retrieved + /// s may point to non existing + /// s in the local repository. In that + /// case, will return null. + /// /// - /// The remote to fetch - /// Message to use when updating the reflog. - [Obsolete("This method is deprecated. Please use the LibGit2Sharp.Commands.Fetch()")] - public virtual void Fetch(Remote remote, string logMessage) + /// The url to list from. + /// The used to connect to remote repository. + /// Options for connecting through a proxy. + /// The references in the remote repository. + public virtual IEnumerable ListReferences(string url, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions) { - Commands.Fetch(repository, remote.Name, new string[0], null, logMessage); - } + Ensure.ArgumentNotNull(url, "url"); + Ensure.ArgumentNotNull(credentialsProvider, "credentialsProvider"); - /// - /// Fetch from the . - /// - /// The remote to fetch - /// controlling fetch behavior - /// Message to use when updating the reflog. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Fetch()")] - public virtual void Fetch(Remote remote, FetchOptions options, string logMessage) - { - Commands.Fetch(repository, remote.Name, new string[0], options, logMessage); + return ListReferencesInternal(url, credentialsProvider, new ProxyOptions()); } - /// - /// Fetch from the , using custom refspecs. - /// - /// The remote to fetch - /// Refspecs to use, replacing the remote's fetch refspecs - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Fetch()")] - public virtual void Fetch(Remote remote, IEnumerable refspecs) + private IEnumerable ListReferencesInternal(string url, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions) { - Commands.Fetch(repository, remote.Name, refspecs, null, null); - } + proxyOptions ??= new(); - /// - /// Fetch from the , using custom refspecs. - /// - /// The remote to fetch - /// Refspecs to use, replacing the remote's fetch refspecs - /// controlling fetch behavior - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Fetch")] - public virtual void Fetch(Remote remote, IEnumerable refspecs, FetchOptions options) - { - Commands.Fetch(repository, remote.Name, refspecs, options, null); - } + using RemoteHandle remoteHandle = BuildRemoteHandle(repository.Handle, url); + using var proxyOptionsWrapper = new GitProxyOptionsWrapper(proxyOptions.CreateGitProxyOptions()); - /// - /// Fetch from the , using custom refspecs. - /// - /// The remote to fetch - /// Refspecs to use, replacing the remote's fetch refspecs - /// Message to use when updating the reflog. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Fetch")] - public virtual void Fetch(Remote remote, IEnumerable refspecs, string logMessage) - { - Commands.Fetch(repository, remote.Name, refspecs, null, logMessage); + GitRemoteCallbacks gitCallbacks = new GitRemoteCallbacks { version = 1 }; + + if (credentialsProvider != null) + { + var callbacks = new RemoteCallbacks(credentialsProvider); + gitCallbacks = callbacks.GenerateCallbacks(); + } + + var gitProxyOptions = proxyOptionsWrapper.Options; + + Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks, ref gitProxyOptions); + return Proxy.git_remote_ls(repository, remoteHandle); } - /// - /// Fetch from the , using custom refspecs. - /// - /// The remote to fetch - /// Refspecs to use, replacing the remote's fetch refspecs - /// controlling fetch behavior - /// Message to use when updating the reflog. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Fetch()")] - public virtual void Fetch(Remote remote, IEnumerable refspecs, FetchOptions options, string logMessage) + static RemoteHandle BuildRemoteHandle(RepositoryHandle repoHandle, string url) { - Ensure.ArgumentNotNull(remote, "remote"); - Ensure.ArgumentNotNull(refspecs, "refspecs"); + Debug.Assert(repoHandle != null && !repoHandle.IsInvalid); + Debug.Assert(url != null); + + RemoteHandle remoteHandle = Proxy.git_remote_create_anonymous(repoHandle, url); + Debug.Assert(remoteHandle != null && !remoteHandle.IsInvalid); - Commands.Fetch(repository, remote.Name, refspecs, options, logMessage); + return remoteHandle; } /// @@ -460,32 +448,30 @@ public virtual void Push(Remote remote, IEnumerable pushRefSpecs, PushOp // Load the remote. using (RemoteHandle remoteHandle = Proxy.git_remote_lookup(repository.Handle, remote.Name, true)) + + // Create a git options wrapper so managed strings are disposed. + using (var pushOptionsWrapper = new GitPushOptionsWrapper()) { var callbacks = new RemoteCallbacks(pushOptions); GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks(); + var gitPushOptions = pushOptionsWrapper.Options; + gitPushOptions.PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism; + gitPushOptions.RemoteCallbacks = gitCallbacks; + gitPushOptions.ProxyOptions = pushOptions.ProxyOptions.CreateGitProxyOptions(); + + // If there are custom headers, create a managed string array. + if (pushOptions.CustomHeaders != null && pushOptions.CustomHeaders.Length > 0) + { + gitPushOptions.CustomHeaders = GitStrArrayManaged.BuildFrom(pushOptions.CustomHeaders); + } + Proxy.git_remote_push(remoteHandle, pushRefSpecs, - new GitPushOptions() - { - PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism, - RemoteCallbacks = gitCallbacks, - ProxyOptions = new GitProxyOptions { Version = 1 }, - }); + gitPushOptions); } } - /// - /// Pull changes from the configured upstream remote and branch into the branch pointed at by HEAD. - /// - /// If the merge is a non-fast forward merge that generates a merge commit, the of who made the merge. - /// Specifies optional parameters controlling merge behavior of pull; if null, the defaults are used. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Pull()")] - public virtual MergeResult Pull(Signature merger, PullOptions options) - { - return Commands.Pull(repository, merger, options); - } - /// /// The heads that have been updated during the last fetch. /// diff --git a/LibGit2Sharp/NonFastForwardException.cs b/LibGit2Sharp/NonFastForwardException.cs index 487e8fd03..d8ed8f474 100644 --- a/LibGit2Sharp/NonFastForwardException.cs +++ b/LibGit2Sharp/NonFastForwardException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -8,8 +10,10 @@ namespace LibGit2Sharp /// The exception that is thrown when push cannot be performed /// against the remote without losing commits. /// +#if NETFRAMEWORK [Serializable] - public class NonFastForwardException : LibGit2SharpException +#endif + public class NonFastForwardException : NativeException { /// /// Initializes a new instance of the class. @@ -28,7 +32,7 @@ public NonFastForwardException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public NonFastForwardException(string format, params object[] args) : base(format, args) @@ -43,6 +47,7 @@ public NonFastForwardException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,9 +56,18 @@ public NonFastForwardException(string message, Exception innerException) protected NonFastForwardException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal NonFastForwardException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal NonFastForwardException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.NonFastForward; + } + } } } diff --git a/LibGit2Sharp/NotFoundException.cs b/LibGit2Sharp/NotFoundException.cs index 0e9b45bf3..f282c4340 100644 --- a/LibGit2Sharp/NotFoundException.cs +++ b/LibGit2Sharp/NotFoundException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -7,8 +9,10 @@ namespace LibGit2Sharp /// /// The exception that is thrown attempting to reference a resource that does not exist. /// +#if NETFRAMEWORK [Serializable] - public class NotFoundException : LibGit2SharpException +#endif + public class NotFoundException : NativeException { /// /// Initializes a new instance of the class. @@ -27,7 +31,7 @@ public NotFoundException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public NotFoundException(string format, params object[] args) : base(format, args) @@ -42,6 +46,7 @@ public NotFoundException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,9 +55,18 @@ public NotFoundException(string message, Exception innerException) protected NotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal NotFoundException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal NotFoundException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.NotFound; + } + } } } diff --git a/LibGit2Sharp/Note.cs b/LibGit2Sharp/Note.cs index 1df0125e4..2ffc89690 100644 --- a/LibGit2Sharp/Note.cs +++ b/LibGit2Sharp/Note.cs @@ -59,10 +59,10 @@ internal static Note BuildFromPtr(NoteHandle note, string @namespace, ObjectId t } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as Note); diff --git a/LibGit2Sharp/ObjectDatabase.cs b/LibGit2Sharp/ObjectDatabase.cs index c662a478c..1bad9c907 100644 --- a/LibGit2Sharp/ObjectDatabase.cs +++ b/LibGit2Sharp/ObjectDatabase.cs @@ -104,8 +104,8 @@ public virtual Blob CreateBlob(string path) } ObjectId id = Path.IsPathRooted(path) - ? Proxy.git_blob_create_fromdisk(repo.Handle, path) - : Proxy.git_blob_create_fromfile(repo.Handle, path); + ? Proxy.git_blob_create_from_disk(repo.Handle, path) + : Proxy.git_blob_create_from_workdir(repo.Handle, path); return repo.Lookup(id); } @@ -178,7 +178,7 @@ public int Provider(IntPtr content, int max_length, IntPtr data) } /// - /// Write an object to the object database + /// Writes an object to the object database. /// /// The contents of the object /// The type of object to write @@ -187,6 +187,45 @@ public virtual ObjectId Write(byte[] data) where T : GitObject return Proxy.git_odb_write(handle, data, GitObject.TypeToKindMap[typeof(T)]); } + /// + /// Writes an object to the object database. + /// + /// The contents of the object + /// The number of bytes to consume from the stream + /// The type of object to write + public virtual ObjectId Write(Stream stream, long numberOfBytesToConsume) where T : GitObject + { + Ensure.ArgumentNotNull(stream, "stream"); + + if (!stream.CanRead) + { + throw new ArgumentException("The stream cannot be read from.", nameof(stream)); + } + + using (var odbStream = Proxy.git_odb_open_wstream(handle, numberOfBytesToConsume, GitObject.TypeToGitKindMap[typeof(T)])) + { + var buffer = new byte[4 * 1024]; + long totalRead = 0; + + while (totalRead < numberOfBytesToConsume) + { + long left = numberOfBytesToConsume - totalRead; + int toRead = left < buffer.Length ? (int)left : buffer.Length; + var read = stream.Read(buffer, 0, toRead); + + if (read == 0) + { + throw new EndOfStreamException("The stream ended unexpectedly"); + } + + Proxy.git_odb_stream_write(odbStream, buffer, read); + totalRead += read; + } + + return Proxy.git_odb_stream_finalize_write(odbStream); + } + } + /// /// Inserts a into the object database, created from the content of a stream. /// Optionally, git filters will be applied to the content before storing it. @@ -235,11 +274,11 @@ private unsafe Blob CreateBlob(Stream stream, string hintpath, long? numberOfByt if (!stream.CanRead) { - throw new ArgumentException("The stream cannot be read from.", "stream"); + throw new ArgumentException("The stream cannot be read from.", nameof(stream)); } - IntPtr writestream_ptr = Proxy.git_blob_create_fromstream(repo.Handle, hintpath); - GitWriteStream writestream = (GitWriteStream)Marshal.PtrToStructure(writestream_ptr, typeof(GitWriteStream)); + IntPtr writestream_ptr = Proxy.git_blob_create_from_stream(repo.Handle, hintpath); + GitWriteStream writestream = Marshal.PtrToStructure(writestream_ptr); try { @@ -276,10 +315,10 @@ private unsafe Blob CreateBlob(Stream stream, string hintpath, long? numberOfByt throw new EndOfStreamException("The stream ended unexpectedly"); } } - catch(Exception e) + catch (Exception) { writestream.free(writestream_ptr); - throw e; + throw; } ObjectId id = Proxy.git_blob_create_fromstream_commit(writestream_ptr); @@ -294,37 +333,8 @@ private unsafe Blob CreateBlob(Stream stream, string hintpath, long? numberOfByt /// The created . public virtual Blob CreateBlob(Stream stream, long numberOfBytesToConsume) { - Ensure.ArgumentNotNull(stream, "stream"); - - if (!stream.CanRead) - { - throw new ArgumentException("The stream cannot be read from.", "stream"); - } - - using (var odbStream = Proxy.git_odb_open_wstream(handle, numberOfBytesToConsume, GitObjectType.Blob)) - { - var buffer = new byte[4 * 1024]; - long totalRead = 0; - - while (totalRead < numberOfBytesToConsume) - { - long left = numberOfBytesToConsume - totalRead; - int toRead = left < buffer.Length ? (int)left : buffer.Length; - var read = stream.Read(buffer, 0, toRead); - - if (read == 0) - { - throw new EndOfStreamException("The stream ended unexpectedly"); - } - - Proxy.git_odb_stream_write(odbStream, buffer, read); - totalRead += read; - } - - var id = Proxy.git_odb_stream_finalize_write(odbStream); - - return repo.Lookup(id); - } + var id = Write(stream, numberOfBytesToConsume); + return repo.Lookup(id); } /// @@ -552,44 +562,33 @@ public virtual HistoryDivergence CalculateHistoryDivergence(Commit one, Commit a public virtual MergeTreeResult CherryPickCommit(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options) { Ensure.ArgumentNotNull(cherryPickCommit, "cherryPickCommit"); - Ensure.ArgumentNotNull(cherryPickOnto, "ours"); + Ensure.ArgumentNotNull(cherryPickOnto, "cherryPickOnto"); - options = options ?? new MergeTreeOptions(); + var modifiedOptions = new MergeTreeOptions(); // We throw away the index after looking at the conflicts, so we'll never need the REUC // entries to be there - GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL | GitMergeFlag.GIT_MERGE_SKIP_REUC; - if (options.FindRenames) - { - mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; - } - if (options.FailOnConflict) - { - mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; - } - + modifiedOptions.SkipReuc = true; - var opts = new GitMergeOpts + if (options != null) { - Version = 1, - MergeFileFavorFlags = options.MergeFileFavor, - MergeTreeFlags = mergeFlags, - RenameThreshold = (uint)options.RenameThreshold, - TargetLimit = (uint)options.TargetLimit - }; + modifiedOptions.FailOnConflict = options.FailOnConflict; + modifiedOptions.FindRenames = options.FindRenames; + modifiedOptions.MergeFileFavor = options.MergeFileFavor; + modifiedOptions.RenameThreshold = options.RenameThreshold; + modifiedOptions.TargetLimit = options.TargetLimit; + } bool earlyStop; - using (var cherryPickOntoHandle = Proxy.git_object_lookup(repo.Handle, cherryPickOnto.Id, GitObjectType.Commit)) - using (var cherryPickCommitHandle = Proxy.git_object_lookup(repo.Handle, cherryPickCommit.Id, GitObjectType.Commit)) - using (var indexHandle = Proxy.git_cherrypick_commit(repo.Handle, cherryPickCommitHandle, cherryPickOntoHandle, (uint)mainline, opts, out earlyStop)) + using (var indexHandle = CherryPickCommit(cherryPickCommit, cherryPickOnto, mainline, modifiedOptions, out earlyStop)) { MergeTreeResult cherryPickResult; // Stopped due to FailOnConflict so there's no index or conflict list if (earlyStop) { - return new MergeTreeResult(new Conflict[] { }); + return new MergeTreeResult(Array.Empty()); } if (Proxy.git_index_has_conflicts(indexHandle)) @@ -640,7 +639,7 @@ public virtual string ShortenObjectId(GitObject gitObject, int minLength) if (minLength <= 0 || minLength > ObjectId.HexSize) { - throw new ArgumentOutOfRangeException("minLength", + throw new ArgumentOutOfRangeException(nameof(minLength), minLength, string.Format("Expected value should be greater than zero and less than or equal to {0}.", ObjectId.HexSize)); @@ -715,7 +714,7 @@ public virtual Commit FindMergeBase(IEnumerable commits, MergeBaseFindin { if (commit == null) { - throw new ArgumentException("Enumerable contains null at position: " + count.ToString(CultureInfo.InvariantCulture), "commits"); + throw new ArgumentException("Enumerable contains null at position: " + count.ToString(CultureInfo.InvariantCulture), nameof(commits)); } ids.Add(commit.Id.Oid); count++; @@ -723,7 +722,7 @@ public virtual Commit FindMergeBase(IEnumerable commits, MergeBaseFindin if (count < 2) { - throw new ArgumentException("The enumerable must contains at least two commits.", "commits"); + throw new ArgumentException("The enumerable must contains at least two commits.", nameof(commits)); } switch (strategy) @@ -737,7 +736,7 @@ public virtual Commit FindMergeBase(IEnumerable commits, MergeBaseFindin break; default: - throw new ArgumentException("", "strategy"); + throw new ArgumentException("", nameof(strategy)); } return id == null ? null : repo.Lookup(id); @@ -745,54 +744,43 @@ public virtual Commit FindMergeBase(IEnumerable commits, MergeBaseFindin /// /// Perform a three-way merge of two commits, looking up their - /// commit ancestor. The returned index will contain the results - /// of the merge and can be examined for conflicts. The returned - /// index must be disposed. + /// commit ancestor. The returned will contain the results + /// of the merge and can be examined for conflicts. /// - /// The first tree - /// The second tree + /// The first commit + /// The second commit /// The controlling the merge - /// The containing the merged trees and any conflicts + /// The containing the merged trees and any conflicts public virtual MergeTreeResult MergeCommits(Commit ours, Commit theirs, MergeTreeOptions options) { Ensure.ArgumentNotNull(ours, "ours"); Ensure.ArgumentNotNull(theirs, "theirs"); - options = options ?? new MergeTreeOptions(); + var modifiedOptions = new MergeTreeOptions(); // We throw away the index after looking at the conflicts, so we'll never need the REUC // entries to be there - GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL | GitMergeFlag.GIT_MERGE_SKIP_REUC; - if (options.FindRenames) - { - mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; - } - if (options.FailOnConflict) - { - mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; - } - + modifiedOptions.SkipReuc = true; - var mergeOptions = new GitMergeOpts + if (options != null) { - Version = 1, - MergeFileFavorFlags = options.MergeFileFavor, - MergeTreeFlags = mergeFlags, - RenameThreshold = (uint)options.RenameThreshold, - TargetLimit = (uint)options.TargetLimit, - }; + modifiedOptions.FailOnConflict = options.FailOnConflict; + modifiedOptions.FindRenames = options.FindRenames; + modifiedOptions.IgnoreWhitespaceChange = options.IgnoreWhitespaceChange; + modifiedOptions.MergeFileFavor = options.MergeFileFavor; + modifiedOptions.RenameThreshold = options.RenameThreshold; + modifiedOptions.TargetLimit = options.TargetLimit; + } bool earlyStop; - using (var oneHandle = Proxy.git_object_lookup(repo.Handle, ours.Id, GitObjectType.Commit)) - using (var twoHandle = Proxy.git_object_lookup(repo.Handle, theirs.Id, GitObjectType.Commit)) - using (var indexHandle = Proxy.git_merge_commits(repo.Handle, oneHandle, twoHandle, mergeOptions, out earlyStop)) + using (var indexHandle = MergeCommits(ours, theirs, modifiedOptions, out earlyStop)) { MergeTreeResult mergeResult; // Stopped due to FailOnConflict so there's no index or conflict list if (earlyStop) { - return new MergeTreeResult(new Conflict[] { }); + return new MergeTreeResult(Array.Empty()); } if (Proxy.git_index_has_conflicts(indexHandle)) @@ -849,6 +837,152 @@ public virtual PackBuilderResults Pack(PackBuilderOptions options, Action + /// Perform a three-way merge of two commits, looking up their + /// commit ancestor. The returned index will contain the results + /// of the merge and can be examined for conflicts. + /// + /// The first tree + /// The second tree + /// The controlling the merge + /// The containing the merged trees and any conflicts, or null if the merge stopped early due to conflicts. + /// The index must be disposed by the caller. + public virtual TransientIndex MergeCommitsIntoIndex(Commit ours, Commit theirs, MergeTreeOptions options) + { + Ensure.ArgumentNotNull(ours, "ours"); + Ensure.ArgumentNotNull(theirs, "theirs"); + + options = options ?? new MergeTreeOptions(); + + bool earlyStop; + var indexHandle = MergeCommits(ours, theirs, options, out earlyStop); + if (earlyStop) + { + if (indexHandle != null) + { + indexHandle.Dispose(); + } + return null; + } + var result = new TransientIndex(indexHandle, repo); + return result; + } + + /// + /// Performs a cherry-pick of onto commit. + /// + /// The commit to cherry-pick. + /// The commit to cherry-pick onto. + /// Which commit to consider the parent for the diff when cherry-picking a merge commit. + /// The options for the merging in the cherry-pick operation. + /// The containing the cherry-pick result tree and any conflicts, or null if the merge stopped early due to conflicts. + /// The index must be disposed by the caller. + public virtual TransientIndex CherryPickCommitIntoIndex(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options) + { + Ensure.ArgumentNotNull(cherryPickCommit, "cherryPickCommit"); + Ensure.ArgumentNotNull(cherryPickOnto, "cherryPickOnto"); + + options = options ?? new MergeTreeOptions(); + + bool earlyStop; + var indexHandle = CherryPickCommit(cherryPickCommit, cherryPickOnto, mainline, options, out earlyStop); + if (earlyStop) + { + if (indexHandle != null) + { + indexHandle.Dispose(); + } + return null; + } + var result = new TransientIndex(indexHandle, repo); + return result; + } + + /// + /// Perform a three-way merge of two commits, looking up their + /// commit ancestor. The returned index will contain the results + /// of the merge and can be examined for conflicts. + /// + /// The first tree + /// The second tree + /// The controlling the merge + /// True if the merge stopped early due to conflicts + /// The containing the merged trees and any conflicts + private IndexHandle MergeCommits(Commit ours, Commit theirs, MergeTreeOptions options, out bool earlyStop) + { + GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL; + if (options.SkipReuc) + { + mergeFlags |= GitMergeFlag.GIT_MERGE_SKIP_REUC; + } + if (options.FindRenames) + { + mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; + } + if (options.FailOnConflict) + { + mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; + } + + var mergeOptions = new GitMergeOpts + { + Version = 1, + MergeFileFavorFlags = options.MergeFileFavor, + MergeTreeFlags = mergeFlags, + RenameThreshold = (uint)options.RenameThreshold, + TargetLimit = (uint)options.TargetLimit, + }; + using (var oneHandle = Proxy.git_object_lookup(repo.Handle, ours.Id, GitObjectType.Commit)) + using (var twoHandle = Proxy.git_object_lookup(repo.Handle, theirs.Id, GitObjectType.Commit)) + { + var indexHandle = Proxy.git_merge_commits(repo.Handle, oneHandle, twoHandle, mergeOptions, out earlyStop); + return indexHandle; + } + } + + /// + /// Performs a cherry-pick of onto commit. + /// + /// The commit to cherry-pick. + /// The commit to cherry-pick onto. + /// Which commit to consider the parent for the diff when cherry-picking a merge commit. + /// The options for the merging in the cherry-pick operation. + /// True if the cherry-pick stopped early due to conflicts + /// The containing the cherry-pick result tree and any conflicts + private IndexHandle CherryPickCommit(Commit cherryPickCommit, Commit cherryPickOnto, int mainline, MergeTreeOptions options, out bool earlyStop) + { + GitMergeFlag mergeFlags = GitMergeFlag.GIT_MERGE_NORMAL; + if (options.SkipReuc) + { + mergeFlags |= GitMergeFlag.GIT_MERGE_SKIP_REUC; + } + if (options.FindRenames) + { + mergeFlags |= GitMergeFlag.GIT_MERGE_FIND_RENAMES; + } + if (options.FailOnConflict) + { + mergeFlags |= GitMergeFlag.GIT_MERGE_FAIL_ON_CONFLICT; + } + + var mergeOptions = new GitMergeOpts + { + Version = 1, + MergeFileFavorFlags = options.MergeFileFavor, + MergeTreeFlags = mergeFlags, + RenameThreshold = (uint)options.RenameThreshold, + TargetLimit = (uint)options.TargetLimit, + }; + + using (var cherryPickOntoHandle = Proxy.git_object_lookup(repo.Handle, cherryPickOnto.Id, GitObjectType.Commit)) + using (var cherryPickCommitHandle = Proxy.git_object_lookup(repo.Handle, cherryPickCommit.Id, GitObjectType.Commit)) + { + var indexHandle = Proxy.git_cherrypick_commit(repo.Handle, cherryPickCommitHandle, cherryPickOntoHandle, (uint)mainline, mergeOptions, out earlyStop); + return indexHandle; + } + } + + /// /// Packs objects in the and write a pack (.pack) and index (.idx) files for them. /// For internal use only. @@ -929,7 +1063,7 @@ public virtual MergeTreeResult RevertCommit(Commit revertCommit, Commit revertOn // Stopped due to FailOnConflict so there's no index or conflict list if (earlyStop) { - return new MergeTreeResult(new Conflict[] { }); + return new MergeTreeResult(Array.Empty()); } if (Proxy.git_index_has_conflicts(indexHandle)) diff --git a/LibGit2Sharp/ObjectId.cs b/LibGit2Sharp/ObjectId.cs index c04100fe9..d87bbcb34 100644 --- a/LibGit2Sharp/ObjectId.cs +++ b/LibGit2Sharp/ObjectId.cs @@ -39,7 +39,7 @@ internal ObjectId(GitOid oid) { if (oid.Id == null || oid.Id.Length != rawSize) { - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "A non null array of {0} bytes is expected.", rawSize), "oid"); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "A non null array of {0} bytes is expected.", rawSize), nameof(oid)); } this.oid = oid; @@ -59,7 +59,7 @@ public ObjectId(byte[] rawId) internal static unsafe ObjectId BuildFromPtr(IntPtr ptr) { - return BuildFromPtr((git_oid*) ptr.ToPointer()); + return BuildFromPtr((git_oid*)ptr.ToPointer()); } internal static unsafe ObjectId BuildFromPtr(git_oid* id) @@ -71,7 +71,7 @@ internal unsafe ObjectId(byte* rawId) { byte[] id = new byte[GitOid.Size]; - fixed(byte* p = id) + fixed (byte* p = id) { for (int i = 0; i < rawSize; i++) { @@ -145,10 +145,10 @@ public static bool TryParse(string sha, out ObjectId result) } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as ObjectId); @@ -174,7 +174,7 @@ public override int GetHashCode() } /// - /// Returns the , a representation of the current . + /// Returns the , a representation of the current . /// /// The that represents the current . public override string ToString() @@ -183,7 +183,7 @@ public override string ToString() } /// - /// Returns the , a representation of the current . + /// Returns the , a representation of the current . /// /// The number of chars the should be truncated to. /// The that represents the current . @@ -327,10 +327,10 @@ private static bool LooksValid(string objectId, bool throwIfInvalid) "'{0}' is not a valid object identifier. Its length should be {1}.", objectId, HexSize), - "objectId"); + nameof(objectId)); } - return objectId.All(c => hexDigits.Contains(c.ToString(CultureInfo.InvariantCulture))); + return objectId.All(c => hexDigits.IndexOf(c) >= 0); } /// diff --git a/LibGit2Sharp/OdbBackend.cs b/LibGit2Sharp/OdbBackend.cs index d96e15909..645d0ac5f 100644 --- a/LibGit2Sharp/OdbBackend.cs +++ b/LibGit2Sharp/OdbBackend.cs @@ -63,7 +63,7 @@ protected unsafe UnmanagedMemoryStream Allocate(long size) { if (size < 0 || (UIntPtr.Size == sizeof(int) && size > int.MaxValue)) { - throw new ArgumentOutOfRangeException("size"); + throw new ArgumentOutOfRangeException(nameof(size)); } IntPtr buffer = Proxy.git_odb_backend_malloc(this.GitOdbBackendPointer, new UIntPtr((ulong)size)); @@ -238,7 +238,7 @@ private static OdbBackend MarshalOdbBackend(IntPtr backendPtr) if (odbBackend == null) { - Proxy.giterr_set_str(GitErrorCategory.Reference, "Cannot retrieve the managed OdbBackend."); + Proxy.git_error_set_str(GitErrorCategory.Reference, "Cannot retrieve the managed OdbBackend."); return null; } @@ -288,7 +288,7 @@ private unsafe static int Read( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } finally @@ -352,7 +352,7 @@ private unsafe static int ReadPrefix( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } finally @@ -397,7 +397,7 @@ private static int ReadHeader( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } @@ -428,7 +428,7 @@ private static unsafe int Write( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } } @@ -463,7 +463,7 @@ private static int WriteStream( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } } @@ -495,7 +495,7 @@ private static int ReadStream( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } } @@ -516,7 +516,7 @@ private static bool Exists( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return false; } } @@ -550,7 +550,7 @@ private static int ExistsPrefix( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } } @@ -572,7 +572,7 @@ private static int Foreach( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); return (int)GitErrorCode.Error; } } @@ -601,7 +601,7 @@ private static void Free( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); } } diff --git a/LibGit2Sharp/OdbBackendStream.cs b/LibGit2Sharp/OdbBackendStream.cs index 2889ac20b..e7d177903 100644 --- a/LibGit2Sharp/OdbBackendStream.cs +++ b/LibGit2Sharp/OdbBackendStream.cs @@ -140,7 +140,7 @@ private unsafe static int Read(IntPtr stream, IntPtr buffer, UIntPtr len) } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); } } } @@ -164,7 +164,7 @@ private static unsafe int Write(IntPtr stream, IntPtr buffer, UIntPtr len) } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); } } } @@ -184,7 +184,7 @@ private static int FinalizeWrite(IntPtr stream, ref GitOid oid) } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); } } @@ -203,7 +203,7 @@ private static void Free(IntPtr stream) } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Odb, ex); + Proxy.git_error_set_str(GitErrorCategory.Odb, ex); } } } diff --git a/LibGit2Sharp/PackBuilder.cs b/LibGit2Sharp/PackBuilder.cs index dcaa2617a..2ede4ab7b 100644 --- a/LibGit2Sharp/PackBuilder.cs +++ b/LibGit2Sharp/PackBuilder.cs @@ -189,7 +189,7 @@ public int MaximumNumberOfThreads { if (value < 0) { - throw new ArgumentException("Argument can not be negative", "value"); + throw new ArgumentException("Argument can not be negative", nameof(value)); } nThreads = value; diff --git a/LibGit2Sharp/Patch.cs b/LibGit2Sharp/Patch.cs index 2cd4d1605..50157eb32 100644 --- a/LibGit2Sharp/Patch.cs +++ b/LibGit2Sharp/Patch.cs @@ -77,12 +77,14 @@ private unsafe int PrintCallBack(git_diff_delta* delta, GitDiffHunk hunk, GitDif case GitDiffLineOrigin.GIT_DIFF_LINE_ADDITION: linesAdded++; currentChange.LinesAdded++; + currentChange.AddedLines.Add(new Line(line.NewLineNo, patchPart)); prefix = "+"; break; case GitDiffLineOrigin.GIT_DIFF_LINE_DELETION: linesDeleted++; currentChange.LinesDeleted++; + currentChange.DeletedLines.Add(new Line(line.OldLineNo, patchPart)); prefix = "-"; break; } @@ -168,7 +170,7 @@ public virtual string Content /// /// . /// The patch content as string. - public static implicit operator string (Patch patch) + public static implicit operator string(Patch patch) { return patch.fullPatchBuilder.ToString(); } diff --git a/LibGit2Sharp/PeelException.cs b/LibGit2Sharp/PeelException.cs index 09d6bdcc8..b5a3d628b 100644 --- a/LibGit2Sharp/PeelException.cs +++ b/LibGit2Sharp/PeelException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -8,8 +10,10 @@ namespace LibGit2Sharp /// The exception that is thrown when a tag cannot be peeled to the /// target type due to the object model. /// +#if NETFRAMEWORK [Serializable] - public class PeelException : LibGit2SharpException +#endif + public class PeelException : NativeException { /// /// Initializes a new instance of the class. @@ -28,7 +32,7 @@ public PeelException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public PeelException(string format, params object[] args) : base(format, args) @@ -43,6 +47,7 @@ public PeelException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,9 +56,18 @@ public PeelException(string message, Exception innerException) protected PeelException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal PeelException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal PeelException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.Peel; + } + } } } diff --git a/LibGit2Sharp/Properties/AssemblyInfo.cs b/LibGit2Sharp/Properties/AssemblyInfo.cs deleted file mode 100644 index 18643022e..000000000 --- a/LibGit2Sharp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("LibGit2Sharp")] -[assembly: AssemblyDescription("LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .Net and Mono.")] -[assembly: AssemblyCompany("LibGit2Sharp contributors")] - -#if DEBUG -[assembly: AssemblyConfiguration("Debug")] -#else -[assembly: AssemblyConfiguration("Release")] -#endif - -[assembly: AssemblyProduct("LibGit2Sharp")] -[assembly: AssemblyCopyright("Copyright © LibGit2Sharp contributors")] - -[assembly: CLSCompliant(true)] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("c6f71967-5be1-49f5-b48e-861bff498ea3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("0.24.0")] -[assembly: AssemblyFileVersion("0.24.0")] -[assembly: AssemblyInformationalVersion("0.24.0-dev00000000000000")] diff --git a/LibGit2Sharp/ProxyOptions.cs b/LibGit2Sharp/ProxyOptions.cs new file mode 100644 index 000000000..076c4e357 --- /dev/null +++ b/LibGit2Sharp/ProxyOptions.cs @@ -0,0 +1,119 @@ +using System; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp +{ + /// + /// Options for connecting through a proxy. + /// + public sealed class ProxyOptions + { + /// + /// The type of proxy to use. Set to Auto by default. + /// + public ProxyType ProxyType { get; set; } = ProxyType.Auto; + + /// + /// The URL of the proxy when is set to Specified. + /// + public string Url { get; set; } + + /// + /// Handler to generate for authentication. + /// + public CredentialsHandler CredentialsProvider { get; set; } + + /// + /// This handler will be called to let the user make a decision on whether to allow + /// the connection to proceed based on the certificate presented by the server. + /// + public CertificateCheckHandler CertificateCheck { get; set; } + + internal unsafe GitProxyOptions CreateGitProxyOptions() + { + var gitProxyOptions = new GitProxyOptions + { + Version = 1, + Type = (GitProxyType)ProxyType + }; + + if (Url is not null) + { + gitProxyOptions.Url = StrictUtf8Marshaler.FromManaged(Url); + } + + if (CredentialsProvider is not null) + { + gitProxyOptions.Credentials = GitCredentialHandler; + } + + if (CertificateCheck is not null) + { + gitProxyOptions.CertificateCheck = GitCertificateCheck; + } + + return gitProxyOptions; + } + + private int GitCredentialHandler(out IntPtr ptr, IntPtr cUrl, IntPtr usernameFromUrl, GitCredentialType credTypes, IntPtr payload) + { + string url = LaxUtf8Marshaler.FromNative(cUrl); + string username = LaxUtf8Marshaler.FromNative(usernameFromUrl); + + SupportedCredentialTypes types = default(SupportedCredentialTypes); + if (credTypes.HasFlag(GitCredentialType.UserPassPlaintext)) + { + types |= SupportedCredentialTypes.UsernamePassword; + } + if (credTypes.HasFlag(GitCredentialType.Default)) + { + types |= SupportedCredentialTypes.Default; + } + + ptr = IntPtr.Zero; + try + { + var cred = CredentialsProvider(url, username, types); + if (cred == null) + { + return (int)GitErrorCode.PassThrough; + } + return cred.GitCredentialHandler(out ptr); + } + catch (Exception exception) + { + Proxy.git_error_set_str(GitErrorCategory.Callback, exception); + return (int)GitErrorCode.Error; + } + } + + private unsafe int GitCertificateCheck(git_certificate* certPtr, int valid, IntPtr cHostname, IntPtr payload) + { + string hostname = LaxUtf8Marshaler.FromNative(cHostname); + Certificate cert = null; + + switch (certPtr->type) + { + case GitCertificateType.X509: + cert = new CertificateX509((git_certificate_x509*)certPtr); + break; + case GitCertificateType.Hostkey: + cert = new CertificateSsh((git_certificate_ssh*)certPtr); + break; + } + + bool result = false; + try + { + result = CertificateCheck(cert, valid != 0, hostname); + } + catch (Exception exception) + { + Proxy.git_error_set_str(GitErrorCategory.Callback, exception); + } + + return Proxy.ConvertResultToCancelFlag(result); + } + } +} diff --git a/LibGit2Sharp/ProxyType.cs b/LibGit2Sharp/ProxyType.cs new file mode 100644 index 000000000..13ec705ee --- /dev/null +++ b/LibGit2Sharp/ProxyType.cs @@ -0,0 +1,23 @@ +namespace LibGit2Sharp +{ + /// + /// The type of proxy to use. + /// + public enum ProxyType + { + /// + /// Do not attempt to connect through a proxy. + /// + None, + + /// + /// Try to auto-detect the proxy from the git configuration. + /// + Auto, + + /// + /// Connect via the URL given in the options. + /// + Specified + } +} diff --git a/LibGit2Sharp/PushOptions.cs b/LibGit2Sharp/PushOptions.cs index b5afc3eb2..829eb0d60 100644 --- a/LibGit2Sharp/PushOptions.cs +++ b/LibGit2Sharp/PushOptions.cs @@ -13,7 +13,7 @@ public sealed class PushOptions public CredentialsHandler CredentialsProvider { get; set; } /// - /// This hanlder will be called to let the user make a decision on whether to allow + /// This handler will be called to let the user make a decision on whether to allow /// the connection to preoceed based on the certificate presented by the server. /// public CertificateCheckHandler CertificateCheck { get; set; } @@ -51,5 +51,30 @@ public sealed class PushOptions /// information about what updates will be performed. /// public PrePushHandler OnNegotiationCompletedBeforePush { get; set; } + + /// + /// Get/Set the custom headers. + /// + /// This allows you to set custom headers (e.g. X-Forwarded-For, + /// X-Request-Id, etc), + /// + /// + /// + /// Libgit2 sets some headers for HTTP requests (User-Agent, Host, + /// Accept, Content-Type, Transfer-Encoding, Content-Length, Accept) that + /// cannot be overriden. + /// + /// + /// var pushOptions - new PushOptions() { + /// CustomHeaders = new String[] {"X-Request-Id: 12345"} + /// }; + /// + /// The custom headers string array + public string[] CustomHeaders { get; set; } + + /// + /// Options for connecting through a proxy. + /// + public ProxyOptions ProxyOptions { get; set; } = new(); } } diff --git a/LibGit2Sharp/PushUpdate.cs b/LibGit2Sharp/PushUpdate.cs index bbabb6817..0aa915dc0 100644 --- a/LibGit2Sharp/PushUpdate.cs +++ b/LibGit2Sharp/PushUpdate.cs @@ -29,9 +29,9 @@ internal unsafe PushUpdate(git_push_update* update) protected PushUpdate() { DestinationObjectId = ObjectId.Zero; - DestinationRefName = String.Empty; + DestinationRefName = string.Empty; SourceObjectId = ObjectId.Zero; - SourceRefName = String.Empty; + SourceRefName = string.Empty; } /// diff --git a/LibGit2Sharp/Rebase.cs b/LibGit2Sharp/Rebase.cs index 00dc3f267..c573ffa65 100644 --- a/LibGit2Sharp/Rebase.cs +++ b/LibGit2Sharp/Rebase.cs @@ -88,7 +88,7 @@ public virtual RebaseResult Start(Branch branch, Branch upstream, Branch onto, I if (this.repository.Info.CurrentOperation != CurrentOperation.None) { - throw new LibGit2SharpException("A {0} operation is already in progress.", + throw new LibGit2SharpException("A {0} operation is already in progress.", this.repository.Info.CurrentOperation); } diff --git a/LibGit2Sharp/RecurseSubmodulesException.cs b/LibGit2Sharp/RecurseSubmodulesException.cs index c322f7605..2269f0d16 100644 --- a/LibGit2Sharp/RecurseSubmodulesException.cs +++ b/LibGit2Sharp/RecurseSubmodulesException.cs @@ -1,4 +1,7 @@ using System; +#if NETFRAMEWORK +using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { @@ -7,7 +10,9 @@ namespace LibGit2Sharp /// through submodules. The inner exception contains the exception that was /// initially thrown while operating on the submodule. /// +#if NETFRAMEWORK [Serializable] +#endif public class RecurseSubmodulesException : LibGit2SharpException { /// @@ -32,5 +37,16 @@ public RecurseSubmodulesException(string message, Exception innerException, stri { InitialRepositoryPath = initialRepositoryPath; } + +#if NETFRAMEWORK + /// + /// Initializes a new instance of the class with a serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected RecurseSubmodulesException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif } } diff --git a/LibGit2Sharp/RefSpec.cs b/LibGit2Sharp/RefSpec.cs index 5819820eb..4d9e28fbe 100644 --- a/LibGit2Sharp/RefSpec.cs +++ b/LibGit2Sharp/RefSpec.cs @@ -13,9 +13,9 @@ namespace LibGit2Sharp public class RefSpec { // This is here to keep the pointer alive - #pragma warning disable 0414 +#pragma warning disable 0414 readonly Remote remote; - #pragma warning restore 0414 +#pragma warning restore 0414 readonly IntPtr handle; internal unsafe RefSpec(Remote remote, git_refspec* handle) diff --git a/LibGit2Sharp/RefSpecCollection.cs b/LibGit2Sharp/RefSpecCollection.cs index 6ba813e47..a35710719 100644 --- a/LibGit2Sharp/RefSpecCollection.cs +++ b/LibGit2Sharp/RefSpecCollection.cs @@ -16,10 +16,10 @@ namespace LibGit2Sharp public class RefSpecCollection : IEnumerable { // These are here to keep the pointer alive - #pragma warning disable 0414 +#pragma warning disable 0414 readonly Remote remote; readonly RemoteHandle handle; - #pragma warning restore 0414 +#pragma warning restore 0414 readonly Lazy> refspecs; /// diff --git a/LibGit2Sharp/Reference.cs b/LibGit2Sharp/Reference.cs index 40a85f79f..9a86195d1 100644 --- a/LibGit2Sharp/Reference.cs +++ b/LibGit2Sharp/Reference.cs @@ -25,11 +25,7 @@ public abstract class Reference : IEquatable, IBelongToARepository protected Reference() { } - /// - /// This would be protected+internal, were that supported by C#. - /// Do not use except in subclasses. - /// - internal Reference(IRepository repo, string canonicalName, string targetIdentifier) + private protected Reference(IRepository repo, string canonicalName, string targetIdentifier) { this.repo = repo; this.canonicalName = canonicalName; @@ -39,7 +35,7 @@ internal Reference(IRepository repo, string canonicalName, string targetIdentifi // This overload lets public-facing methods avoid having to use the pointers directly internal static unsafe T BuildFromPtr(ReferenceHandle handle, Repository repo) where T : Reference { - return BuildFromPtr((git_reference*) handle.Handle, repo); + return BuildFromPtr((git_reference*)handle.AsIntPtr(), repo); } internal static unsafe T BuildFromPtr(git_reference* handle, Repository repo) where T : Reference @@ -153,10 +149,10 @@ public virtual string TargetIdentifier } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as Reference); @@ -204,7 +200,7 @@ public override int GetHashCode() } /// - /// Returns the , a representation of the current . + /// Returns the , a representation of the current . /// /// The that represents the current . public override string ToString() diff --git a/LibGit2Sharp/ReferenceCollection.cs b/LibGit2Sharp/ReferenceCollection.cs index df098d261..92bf85426 100644 --- a/LibGit2Sharp/ReferenceCollection.cs +++ b/LibGit2Sharp/ReferenceCollection.cs @@ -404,7 +404,7 @@ public virtual Reference Rename(string currentName, string newName, if (reference == null) { - throw new LibGit2SharpException("Reference '{0}' doesn't exist. One cannot move a non existing reference.", + throw new LibGit2SharpException("Reference '{0}' doesn't exist. One cannot move a non existing reference.", currentName); } @@ -539,7 +539,7 @@ public virtual Reference UpdateTarget(string name, string canonicalRefNameOrObje if (refState == RefState.DoesNotLookValid) { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is a Symbolic reference, you must provide a reference canonical name as the target.", name), "canonicalRefNameOrObjectish"); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is a Symbolic reference, you must provide a reference canonical name as the target.", name), nameof(canonicalRefNameOrObjectish)); } return UpdateTarget(symbolicReference, targetRef, logMessage); @@ -736,7 +736,7 @@ public virtual IEnumerable ReachableFrom( { var peeledTargetCommit = reference .ResolveToDirectReference() - .Target.DereferenceToCommit(false); + .Target.Peel(false); if (peeledTargetCommit == null) { diff --git a/LibGit2Sharp/ReferenceWrapper.cs b/LibGit2Sharp/ReferenceWrapper.cs index 3e4243a7e..7fb8497c6 100644 --- a/LibGit2Sharp/ReferenceWrapper.cs +++ b/LibGit2Sharp/ReferenceWrapper.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using LibGit2Sharp.Core; @@ -10,7 +11,11 @@ namespace LibGit2Sharp /// /// The type of the referenced Git object. [DebuggerDisplay("{DebuggerDisplay,nq}")] +#if NET + public abstract class ReferenceWrapper<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TObject> : IEquatable>, IBelongToARepository where TObject : GitObject +#else public abstract class ReferenceWrapper : IEquatable>, IBelongToARepository where TObject : GitObject +#endif { /// /// The repository. @@ -124,10 +129,10 @@ public bool Equals(ReferenceWrapper other) } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as ReferenceWrapper); diff --git a/LibGit2Sharp/Remote.cs b/LibGit2Sharp/Remote.cs index b41fe0634..401a7ddd0 100644 --- a/LibGit2Sharp/Remote.cs +++ b/LibGit2Sharp/Remote.cs @@ -34,6 +34,9 @@ internal Remote(RemoteHandle handle, Repository repository) repository.RegisterForCleanup(this); } + /// + /// The finalizer for the class. + /// ~Remote() { Dispose(false); @@ -80,7 +83,8 @@ public virtual string Name /// public virtual string Url { - get { return Proxy.git_remote_url(handle); } } + get { return Proxy.git_remote_url(handle); } + } /// /// Gets the distinct push url for this remote repository, if set. diff --git a/LibGit2Sharp/RemoteCallbacks.cs b/LibGit2Sharp/RemoteCallbacks.cs index 9958c6844..6061b10e1 100644 --- a/LibGit2Sharp/RemoteCallbacks.cs +++ b/LibGit2Sharp/RemoteCallbacks.cs @@ -297,7 +297,7 @@ private int GitCredentialHandler( } catch (Exception exception) { - Proxy.giterr_set_str(GitErrorCategory.Callback, exception); + Proxy.git_error_set_str(GitErrorCategory.Callback, exception); return (int)GitErrorCode.Error; } } @@ -310,10 +310,10 @@ private unsafe int GitCertificateCheck(git_certificate* certPtr, int valid, IntP switch (certPtr->type) { case GitCertificateType.X509: - cert = new CertificateX509((git_certificate_x509*) certPtr); + cert = new CertificateX509((git_certificate_x509*)certPtr); break; case GitCertificateType.Hostkey: - cert = new CertificateSsh((git_certificate_ssh*) certPtr); + cert = new CertificateSsh((git_certificate_ssh*)certPtr); break; } @@ -324,7 +324,7 @@ private unsafe int GitCertificateCheck(git_certificate* certPtr, int valid, IntP } catch (Exception exception) { - Proxy.giterr_set_str(GitErrorCategory.Callback, exception); + Proxy.git_error_set_str(GitErrorCategory.Callback, exception); } return Proxy.ConvertResultToCancelFlag(result); @@ -355,7 +355,7 @@ private int GitPushNegotiationHandler(IntPtr updates, UIntPtr len, IntPtr payloa throw new NullReferenceException("Unexpected null git_push_update pointer was encountered"); } - PushUpdate pushUpdate = new PushUpdate((git_push_update*) ptr[i].ToPointer()); + PushUpdate pushUpdate = new PushUpdate((git_push_update*)ptr[i].ToPointer()); pushUpdates[i] = pushUpdate; } @@ -365,7 +365,7 @@ private int GitPushNegotiationHandler(IntPtr updates, UIntPtr len, IntPtr payloa catch (Exception exception) { Log.Write(LogLevel.Error, exception.ToString()); - Proxy.giterr_set_str(GitErrorCategory.Callback, exception); + Proxy.git_error_set_str(GitErrorCategory.Callback, exception); result = false; } diff --git a/LibGit2Sharp/RemoteCollection.cs b/LibGit2Sharp/RemoteCollection.cs index fe750c390..45e71c8b2 100644 --- a/LibGit2Sharp/RemoteCollection.cs +++ b/LibGit2Sharp/RemoteCollection.cs @@ -47,25 +47,6 @@ internal Remote RemoteForName(string name, bool shouldThrowIfNotFound = true) return handle == null ? null : new Remote(handle, this.repository); } - /// - /// Update properties of a remote. - /// - /// The remote to update. - /// Delegate to perform updates on the remote. - /// The updated remote. - [Obsolete("This method is deprecated. Use the overload with a remote name")] - public virtual Remote Update(Remote remote, params Action[] actions) - { - var updater = new RemoteUpdater(repository, remote); - - foreach (Action action in actions) - { - action(updater); - } - - return this[remote.Name]; - } - /// /// Update properties of a remote. /// @@ -77,7 +58,8 @@ public virtual void Update(string remote, params Action[] actions { var updater = new RemoteUpdater(repository, remote); - repository.Config.WithinTransaction(() => { + repository.Config.WithinTransaction(() => + { foreach (Action action in actions) { action(updater); diff --git a/LibGit2Sharp/RemoteRedirectMode.cs b/LibGit2Sharp/RemoteRedirectMode.cs new file mode 100644 index 000000000..029208857 --- /dev/null +++ b/LibGit2Sharp/RemoteRedirectMode.cs @@ -0,0 +1,28 @@ +namespace LibGit2Sharp +{ + /// + /// Remote redirection settings; whether redirects to another + /// host are permitted. By default, git will follow a redirect + /// on the initial request (`/info/refs`) but not subsequent + /// requests. + /// + public enum RemoteRedirectMode + { + /// + /// Do not follow any off-site redirects at any stage of + /// the fetch or push. + /// + None = 1 << 0, // GIT_REMOTE_REDIRECT_NONE + + /// + /// Allow off-site redirects only upon the initial + /// request. This is the default. + /// + Initial = 1 << 1, // GIT_REMOTE_REDIRECT_INITIAL + + /// + /// Allow redirects at any stage in the fetch or push. + /// + All = 1 << 2 // GIT_REMOTE_REDIRECT_ALL + } +} diff --git a/LibGit2Sharp/RemoteUpdater.cs b/LibGit2Sharp/RemoteUpdater.cs index ec8b08bcd..53fd33a4b 100644 --- a/LibGit2Sharp/RemoteUpdater.cs +++ b/LibGit2Sharp/RemoteUpdater.cs @@ -56,7 +56,7 @@ private IEnumerable GetFetchRefSpecs() private void SetFetchRefSpecs(IEnumerable value) { - repo.Config.UnsetMultivar(string.Format("remote.{0}.fetch", remoteName), ConfigurationLevel.Local); + repo.Config.UnsetAll(string.Format("remote.{0}.fetch", remoteName), ConfigurationLevel.Local); foreach (var url in value) { @@ -74,7 +74,7 @@ private IEnumerable GetPushRefSpecs() private void SetPushRefSpecs(IEnumerable value) { - repo.Config.UnsetMultivar(string.Format("remote.{0}.push", remoteName), ConfigurationLevel.Local); + repo.Config.UnsetAll(string.Format("remote.{0}.push", remoteName), ConfigurationLevel.Local); foreach (var url in value) { diff --git a/LibGit2Sharp/RemoveFromIndexException.cs b/LibGit2Sharp/RemoveFromIndexException.cs index 6d9718c18..847e4026e 100644 --- a/LibGit2Sharp/RemoveFromIndexException.cs +++ b/LibGit2Sharp/RemoveFromIndexException.cs @@ -1,13 +1,16 @@ using System; -using System.Globalization; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { /// /// The exception that is thrown when a file cannot be removed from the index. /// +#if NETFRAMEWORK [Serializable] +#endif public class RemoveFromIndexException : LibGit2SharpException { /// @@ -27,7 +30,7 @@ public RemoveFromIndexException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public RemoveFromIndexException(string format, params object[] args) : base(format, args) @@ -43,6 +46,7 @@ public RemoveFromIndexException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,5 +55,6 @@ public RemoveFromIndexException(string message, Exception innerException) protected RemoveFromIndexException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/LibGit2Sharp/RenameDetails.cs b/LibGit2Sharp/RenameDetails.cs index b866aac60..8742ff0d3 100644 --- a/LibGit2Sharp/RenameDetails.cs +++ b/LibGit2Sharp/RenameDetails.cs @@ -56,10 +56,10 @@ public virtual int Similarity } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as RenameDetails); diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 7c4f2f1de..9ac5e2424 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -35,6 +35,7 @@ public sealed class Repository : IRepository private readonly Stack toCleanup = new Stack(); private readonly Ignore ignore; private readonly SubmoduleCollection submodules; + private readonly WorktreeCollection worktrees; private readonly Lazy pathCase; [Flags] @@ -91,7 +92,56 @@ public Repository(string path, RepositoryOptions options) : this(path, options, RepositoryRequiredParameter.Path | RepositoryRequiredParameter.Options) { } - + + internal Repository(WorktreeHandle worktreeHandle) + { + try + { + handle = Proxy.git_repository_open_from_worktree(worktreeHandle); + RegisterForCleanup(handle); + RegisterForCleanup(worktreeHandle); + + isBare = Proxy.git_repository_is_bare(handle); + + Func indexBuilder = () => new Index(this); + + string configurationGlobalFilePath = null; + string configurationXDGFilePath = null; + string configurationSystemFilePath = null; + + if (!isBare) + { + index = new Lazy(() => indexBuilder()); + } + + commits = new CommitLog(this); + refs = new ReferenceCollection(this); + branches = new BranchCollection(this); + tags = new TagCollection(this); + stashes = new StashCollection(this); + info = new Lazy(() => new RepositoryInformation(this, isBare)); + config = new Lazy(() => RegisterForCleanup(new Configuration(this, + null, + configurationGlobalFilePath, + configurationXDGFilePath, + configurationSystemFilePath))); + odb = new Lazy(() => new ObjectDatabase(this)); + diff = new Diff(this); + notes = new NoteCollection(this); + ignore = new Ignore(this); + network = new Lazy(() => new Network(this)); + rebaseOperation = new Lazy(() => new Rebase(this)); + pathCase = new Lazy(() => new PathCase(this)); + submodules = new SubmoduleCollection(this); + worktrees = new WorktreeCollection(this); + } + catch + { + CleanupDisposableDependencies(); + throw; + } + } + private Repository(string path, RepositoryOptions options, RepositoryRequiredParameter requiredParameter) { if ((requiredParameter & RepositoryRequiredParameter.Path) == RepositoryRequiredParameter.Path) @@ -150,10 +200,6 @@ private Repository(string path, RepositoryOptions options, RepositoryRequiredPar Proxy.git_repository_set_workdir(handle, options.WorkingDirectoryPath); } - configurationGlobalFilePath = options.GlobalConfigurationLocation; - configurationXDGFilePath = options.XdgConfigurationLocation; - configurationSystemFilePath = options.SystemConfigurationLocation; - if (options.Identity != null) { Proxy.git_repository_set_ident(handle, options.Identity.Name, options.Identity.Email); @@ -184,6 +230,7 @@ private Repository(string path, RepositoryOptions options, RepositoryRequiredPar rebaseOperation = new Lazy(() => new Rebase(this)); pathCase = new Lazy(() => new PathCase(this)); submodules = new SubmoduleCollection(this); + worktrees = new WorktreeCollection(this); EagerlyLoadComponentsWithSpecifiedPaths(options); } @@ -230,19 +277,6 @@ private void EagerlyLoadComponentsWithSpecifiedPaths(RepositoryOptions options) return; } - if (options.GlobalConfigurationLocation != null || - options.XdgConfigurationLocation != null || - options.SystemConfigurationLocation != null) - { - // Dirty hack to force the eager load of the configuration - // without Resharper pestering about useless code - - if (!Config.HasConfig(ConfigurationLevel.Local)) - { - throw new InvalidOperationException("Unexpected state."); - } - } - if (!string.IsNullOrEmpty(options.IndexPath)) { // Another dirty hack to avoid warnings @@ -413,6 +447,14 @@ public SubmoduleCollection Submodules get { return submodules; } } + /// + /// Worktrees in the repository. + /// + public WorktreeCollection Worktrees + { + get { return worktrees; } + } + #region IDisposable Members /// @@ -527,13 +569,13 @@ public GitObject Lookup(string objectish, ObjectType type) return Lookup(objectish, type.ToGitObjectType(), LookUpOptions.None); } - internal GitObject LookupInternal(ObjectId id, GitObjectType type, FilePath knownPath) + internal GitObject LookupInternal(ObjectId id, GitObjectType type, string knownPath) { Ensure.ArgumentNotNull(id, "id"); using (ObjectHandle obj = Proxy.git_object_lookup(handle, id, type)) { - if (obj == null || obj.IsNull) + if (obj == null || obj.IsInvalid) { return null; } @@ -587,7 +629,7 @@ internal GitObject Lookup(string objectish, GitObjectType type, LookUpOptions lo if (lookUpOptions.HasFlag(LookUpOptions.DereferenceResultToCommit)) { - return obj.DereferenceToCommit(lookUpOptions.HasFlag(LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit)); + return obj.Peel(lookUpOptions.HasFlag(LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit)); } return obj; @@ -614,7 +656,18 @@ internal Commit LookupCommit(string committish) /// The references in the remote repository. public static IEnumerable ListRemoteReferences(string url) { - return ListRemoteReferences(url, null); + return ListRemoteReferences(url, null, new ProxyOptions()); + } + + /// + /// Lists the Remote Repository References. + /// + /// The url to list from. + /// Options for connecting through a proxy. + /// The references in the remote repository. + public static IEnumerable ListRemoteReferences(string url, ProxyOptions proxyOptions) + { + return ListRemoteReferences(url, null, proxyOptions); } /// @@ -629,24 +682,44 @@ public static IEnumerable ListRemoteReferences(string url) /// The used to connect to remote repository. /// The references in the remote repository. public static IEnumerable ListRemoteReferences(string url, CredentialsHandler credentialsProvider) + { + return ListRemoteReferences(url, credentialsProvider, new ProxyOptions()); + } + + /// + /// Lists the Remote Repository References. + /// + /// + /// Does not require a local Repository. The retrieved + /// + /// throws in this case. + /// + /// The url to list from. + /// The used to connect to remote repository. + /// Options for connecting through a proxy. + /// The references in the remote repository. + public static IEnumerable ListRemoteReferences(string url, CredentialsHandler credentialsProvider, ProxyOptions proxyOptions) { Ensure.ArgumentNotNull(url, "url"); - using (RepositoryHandle repositoryHandle = Proxy.git_repository_new()) - using (RemoteHandle remoteHandle = Proxy.git_remote_create_anonymous(repositoryHandle, url)) - { - var gitCallbacks = new GitRemoteCallbacks { version = 1 }; - var proxyOptions = new GitProxyOptions { Version = 1 }; + proxyOptions ??= new(); - if (credentialsProvider != null) - { - var callbacks = new RemoteCallbacks(credentialsProvider); - gitCallbacks = callbacks.GenerateCallbacks(); - } + using RepositoryHandle repositoryHandle = Proxy.git_repository_new(); + using RemoteHandle remoteHandle = Proxy.git_remote_create_anonymous(repositoryHandle, url); + using var proxyOptionsWrapper = new GitProxyOptionsWrapper(proxyOptions.CreateGitProxyOptions()); - Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks, ref proxyOptions); - return Proxy.git_remote_ls(null, remoteHandle); + var gitCallbacks = new GitRemoteCallbacks { version = 1 }; + + if (credentialsProvider != null) + { + var callbacks = new RemoteCallbacks(credentialsProvider); + gitCallbacks = callbacks.GenerateCallbacks(); } + + var gitProxyOptions = proxyOptionsWrapper.Options; + + Proxy.git_remote_connect(remoteHandle, GitDirection.Fetch, ref gitCallbacks, ref gitProxyOptions); + return Proxy.git_remote_ls(null, remoteHandle); } /// @@ -699,42 +772,46 @@ public static string Clone(string sourceUrl, string workdirPath) /// Local path to clone into /// controlling clone behavior /// The path to the created repository. - public static string Clone(string sourceUrl, string workdirPath, - CloneOptions options) + public static string Clone(string sourceUrl, string workdirPath, CloneOptions options) { Ensure.ArgumentNotNull(sourceUrl, "sourceUrl"); Ensure.ArgumentNotNull(workdirPath, "workdirPath"); - options = options ?? new CloneOptions(); + options ??= new CloneOptions(); // context variable that contains information on the repository that // we are cloning. var context = new RepositoryOperationContext(Path.GetFullPath(workdirPath), sourceUrl); // Notify caller that we are starting to work with the current repository. - bool continueOperation = OnRepositoryOperationStarting(options.RepositoryOperationStarting, - context); + bool continueOperation = OnRepositoryOperationStarting(options.FetchOptions.RepositoryOperationStarting, context); if (!continueOperation) { throw new UserCancelledException("Clone cancelled by the user."); } - using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options)) + using (var checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options)) + using (var fetchOptionsWrapper = new GitFetchOptionsWrapper()) { var gitCheckoutOptions = checkoutOptionsWrapper.Options; - var remoteCallbacks = new RemoteCallbacks(options); - var gitRemoteCallbacks = remoteCallbacks.GenerateCallbacks(); + var gitFetchOptions = fetchOptionsWrapper.Options; + gitFetchOptions.Depth = options.FetchOptions.Depth; + gitFetchOptions.ProxyOptions = options.FetchOptions.ProxyOptions.CreateGitProxyOptions(); + gitFetchOptions.RemoteCallbacks = new RemoteCallbacks(options.FetchOptions).GenerateCallbacks(); - var gitProxyOptions = new GitProxyOptions { Version = 1 }; + if (options.FetchOptions != null && options.FetchOptions.CustomHeaders != null) + { + gitFetchOptions.CustomHeaders = GitStrArrayManaged.BuildFrom(options.FetchOptions.CustomHeaders); + } var cloneOpts = new GitCloneOptions { Version = 1, Bare = options.IsBare ? 1 : 0, CheckoutOpts = gitCheckoutOptions, - FetchOpts = new GitFetchOptions { ProxyOptions = gitProxyOptions, RemoteCallbacks = gitRemoteCallbacks }, + FetchOpts = gitFetchOptions, }; string clonedRepoPath; @@ -754,8 +831,7 @@ public static string Clone(string sourceUrl, string workdirPath, } // Notify caller that we are done with the current repository. - OnRepositoryOperationCompleted(options.RepositoryOperationCompleted, - context); + OnRepositoryOperationCompleted(options.FetchOptions.RepositoryOperationCompleted, context); // Recursively clone submodules if requested. try @@ -764,9 +840,7 @@ public static string Clone(string sourceUrl, string workdirPath, } catch (Exception ex) { - throw new RecurseSubmodulesException("The top level repository was cloned, but there was an error cloning its submodules.", - ex, - clonedRepoPath); + throw new RecurseSubmodulesException("The top level repository was cloned, but there was an error cloning its submodules.", ex, clonedRepoPath); } return clonedRepoPath; @@ -787,14 +861,11 @@ private static void RecursivelyCloneSubmodules(CloneOptions options, string repo using (Repository repo = new Repository(repoPath)) { - SubmoduleUpdateOptions updateOptions = new SubmoduleUpdateOptions() + var updateOptions = new SubmoduleUpdateOptions() { Init = true, - CredentialsProvider = options.CredentialsProvider, OnCheckoutProgress = options.OnCheckoutProgress, - OnProgress = options.OnProgress, - OnTransferProgress = options.OnTransferProgress, - OnUpdateTips = options.OnUpdateTips, + FetchOptions = options.FetchOptions }; string parentRepoWorkDir = repo.Info.WorkingDirectory; @@ -815,7 +886,7 @@ private static void RecursivelyCloneSubmodules(CloneOptions options, string repo sm.Name, recursionDepth); - bool continueOperation = OnRepositoryOperationStarting(options.RepositoryOperationStarting, + bool continueOperation = OnRepositoryOperationStarting(options.FetchOptions.RepositoryOperationStarting, context); if (!continueOperation) @@ -825,7 +896,7 @@ private static void RecursivelyCloneSubmodules(CloneOptions options, string repo repo.Submodules.Update(sm.Name, updateOptions); - OnRepositoryOperationCompleted(options.RepositoryOperationCompleted, + OnRepositoryOperationCompleted(options.FetchOptions.RepositoryOperationCompleted, context); submodules.Add(Path.Combine(repo.Info.WorkingDirectory, sm.Path)); @@ -883,51 +954,6 @@ public BlameHunkCollection Blame(string path, BlameOptions options) return new BlameHunkCollection(this, Handle, path, options ?? new BlameOptions()); } - /// - /// Checkout the specified , reference or SHA. - /// - /// If the committishOrBranchSpec parameter resolves to a branch name, then the checked out HEAD will - /// will point to the branch. Otherwise, the HEAD will be detached, pointing at the commit sha. - /// - /// - /// A revparse spec for the commit or branch to checkout. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options) - { - return Commands.Checkout(this, committishOrBranchSpec, options); - } - - /// - /// Checkout the tip commit of the specified object. If this commit is the - /// current tip of the branch, will checkout the named branch. Otherwise, will checkout the tip commit - /// as a detached HEAD. - /// - /// The to check out. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public Branch Checkout(Branch branch, CheckoutOptions options) - { - return Commands.Checkout(this, branch, options); - } - - /// - /// Checkout the specified . - /// - /// Will detach the HEAD and make it point to this commit sha. - /// - /// - /// The to check out. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public Branch Checkout(Commit commit, CheckoutOptions options) - { - return Commands.Checkout(this, commit, options); - } - /// /// Checkout the specified tree. /// @@ -1048,7 +1074,7 @@ public Commit Commit(string message, Signature author, Signature committer, Comm if (treesame && !amendMergeCommit) { - throw (options.AmendPreviousCommit ? + throw (options.AmendPreviousCommit ? new EmptyCommitException("Amending this commit would produce a commit that is identical to its parent (id = {0})", parents[0].Id) : new EmptyCommitException("No changes; nothing to commit.")); } @@ -1239,7 +1265,7 @@ public MergeResult MergeFetchedRefs(Signature merger, MergeOptions options) if (fetchHeads.Length == 0) { var expectedRef = this.Head.UpstreamBranchCanonicalName; - throw new MergeFetchHeadNotFoundException("The current branch is configured to merge with the reference '{0}' from the remote, but this reference was not fetched.", + throw new MergeFetchHeadNotFoundException("The current branch is configured to merge with the reference '{0}' from the remote, but this reference was not fetched.", expectedRef); } @@ -1426,7 +1452,7 @@ private FastForwardStrategy FastForwardStrategyFromMergePreference(GitMergePrefe case GitMergePreference.GIT_MERGE_PREFERENCE_NO_FASTFORWARD: return FastForwardStrategy.NoFastForward; default: - throw new InvalidOperationException(String.Format("Unknown merge preference: {0}", preference)); + throw new InvalidOperationException(string.Format("Unknown merge preference: {0}", preference)); } } @@ -1498,13 +1524,13 @@ private MergeResult Merge(AnnotatedCommitHandle[] annotatedCommits, Signature me break; default: throw new NotImplementedException( - string.Format(CultureInfo.InvariantCulture, "Unknown fast forward strategy: {0}", mergeAnalysis)); + string.Format(CultureInfo.InvariantCulture, "Unknown fast forward strategy: {0}", fastForwardStrategy)); } if (mergeResult == null) { throw new NotImplementedException( - string.Format(CultureInfo.InvariantCulture, "Unknown merge analysis: {0}", options.FastForwardStrategy)); + string.Format(CultureInfo.InvariantCulture, "Unknown merge analysis: {0}", mergeAnalysis)); } return mergeResult; @@ -1533,6 +1559,10 @@ private MergeResult NormalMerge(AnnotatedCommitHandle[] annotatedCommits, Signat treeFlags |= GitMergeFlag.GIT_MERGE_SKIP_REUC; } + var fileFlags = options.IgnoreWhitespaceChange + ? GitMergeFileFlag.GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE + : GitMergeFileFlag.GIT_MERGE_FILE_DEFAULT; + var mergeOptions = new GitMergeOpts { Version = 1, @@ -1540,6 +1570,7 @@ private MergeResult NormalMerge(AnnotatedCommitHandle[] annotatedCommits, Signat MergeTreeFlags = treeFlags, RenameThreshold = (uint)options.RenameThreshold, TargetLimit = (uint)options.TargetLimit, + FileFlags = fileFlags }; bool earlyStop; @@ -1639,7 +1670,7 @@ internal FilePath[] ToFilePaths(IEnumerable paths) { if (string.IsNullOrEmpty(path)) { - throw new ArgumentException("At least one provided path is either null or empty.", "paths"); + throw new ArgumentException("At least one provided path is either null or empty.", nameof(paths)); } filePaths.Add(this.BuildRelativePathFrom(path)); @@ -1647,142 +1678,12 @@ internal FilePath[] ToFilePaths(IEnumerable paths) if (filePaths.Count == 0) { - throw new ArgumentException("No path has been provided.", "paths"); + throw new ArgumentException("No path has been provided.", nameof(paths)); } return filePaths.ToArray(); } - /// - /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). - /// - /// If this path is ignored by configuration then it will not be staged unless is unset. - /// - /// The path of the file within the working directory. - /// Determines how paths will be staged. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Stage()")] - public void Stage(string path, StageOptions stageOptions) - { - Commands.Stage(this, path, stageOptions); - } - - /// - /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). - /// - /// Any paths (even those listed explicitly) that are ignored by configuration will not be staged unless is unset. - /// - /// The collection of paths of the files within the working directory. - /// Determines how paths will be staged. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Stage()")] - public void Stage(IEnumerable paths, StageOptions stageOptions) - { - Commands.Stage(this, paths, stageOptions); - } - - /// - /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). - /// - /// The path of the file within the working directory. - /// - /// The passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - public void Unstage(string path, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Unstage(this, path, explicitPathsOptions); - } - - /// - /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). - /// - /// The collection of paths of the files within the working directory. - /// - /// The passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - public void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Unstage(this, paths, explicitPathsOptions); - } - - /// - /// Moves and/or renames a file in the working directory and promotes the change to the staging area. - /// - /// The path of the file within the working directory which has to be moved/renamed. - /// The target path of the file within the working directory. - [Obsolete("This method is deprecatd. Please use LibGit2Sharp.Commands.Move()")] - public void Move(string sourcePath, string destinationPath) - { - Commands.Move(this, sourcePath, destinationPath); - } - - /// - /// Moves and/or renames a collection of files in the working directory and promotes the changes to the staging area. - /// - /// The paths of the files within the working directory which have to be moved/renamed. - /// The target paths of the files within the working directory. - [Obsolete("This method is deprecatd. Please use LibGit2Sharp.Commands.Move()")] - public void Move(IEnumerable sourcePaths, IEnumerable destinationPaths) - { - Commands.Move(this, sourcePaths, destinationPaths); - } - - /// - /// Removes a file from the staging area, and optionally removes it from the working directory as well. - /// - /// If the file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the file from the working directory as well. - /// - /// - /// When not passing a , the passed path will be treated as - /// a pathspec. You can for example use it to pass the relative path to a folder inside the working directory, - /// so that all files beneath this folders, and the folder itself, will be removed. - /// - /// - /// The path of the file within the working directory. - /// True to remove the file from the working directory, False otherwise. - /// - /// The passed will be treated as an explicit path. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove")] - public void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Remove(this, path, removeFromWorkingDirectory, explicitPathsOptions); - } - - /// - /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. - /// - /// If a file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the files from the working directory as well. - /// - /// - /// When not passing a , the passed paths will be treated as - /// a pathspec. You can for example use it to pass the relative paths to folders inside the working directory, - /// so that all files beneath these folders, and the folders themselves, will be removed. - /// - /// - /// The collection of paths of the files within the working directory. - /// True to remove the files from the working directory, False otherwise. - /// - /// The passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove")] - public void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) - { - Commands.Remove(this, paths, removeFromWorkingDirectory, explicitPathsOptions); - } - /// /// Retrieves the state of a file in the working directory, comparing it against the staging area and the latest commit. /// @@ -1843,7 +1744,7 @@ internal void UpdatePhysicalIndex() /// /// /// Optionally, the parameter allow to tweak the - /// search strategy (considering lightweith tags, or even branches as reference points) + /// search strategy (considering lightweight tags, or even branches as reference points) /// and the formatting of the returned identifier. /// /// @@ -1876,7 +1777,7 @@ public void RevParse(string revision, out Reference reference, out GitObject obj using (var objH = handles.Item1) using (var refH = handles.Item2) { - reference = refH.IsNull ? null : Reference.BuildFromPtr(refH, this); + reference = refH.IsInvalid ? null : Reference.BuildFromPtr(refH, this); obj = GitObject.BuildFrom(this, Proxy.git_object_id(objH), Proxy.git_object_type(objH), PathFromRevparseSpec(revision)); } } diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index ce8154633..5d0788c8a 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -24,7 +24,7 @@ public static T Lookup(this IRepository repository, string objectish) where T { EnsureNoGitLink(); - if (typeof (T) == typeof (GitObject)) + if (typeof(T) == typeof(GitObject)) { return (T)repository.Lookup(objectish); } @@ -182,7 +182,7 @@ private static Commit LookUpCommit(IRepository repository, string committish) { GitObject obj = repository.Lookup(committish); Ensure.GitObjectIsNotNull(obj, committish); - return obj.DereferenceToCommit(true); + return obj.Peel(true); } /// @@ -200,81 +200,6 @@ public static Commit Commit(this IRepository repository, string message, Signatu return repository.Commit(message, author, committer, default(CommitOptions)); } - /// - /// Fetch from the specified remote. - /// - /// The being worked with. - /// The name of the to fetch from. - [Obsolete("This method is deprecated. Please us the LibGit2Sharp.Commands.Fetch class")] - public static void Fetch(this IRepository repository, string remoteName) - { - repository.Fetch(remoteName, null); - } - - /// - /// Fetch from the specified remote. - /// - /// The being worked with. - /// The name of the to fetch from. - /// controlling fetch behavior - [Obsolete("This method is deprecated. Please us the LibGit2Sharp.Commands.Fetch class")] - public static void Fetch(this IRepository repository, string remoteName, FetchOptions options) - { - Ensure.ArgumentNotNull(repository, "repository"); - Ensure.ArgumentNotNullOrEmptyString(remoteName, "remoteName"); - - using (Remote remote = repository.Network.Remotes.RemoteForName(remoteName, true)) - { - repository.Network.Fetch(remote, options); - } - } - - /// - /// Checkout the specified , reference or SHA. - /// - /// The being worked with. - /// A revparse spec for the commit or branch to checkout. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public static Branch Checkout(this IRepository repository, string commitOrBranchSpec) - { - CheckoutOptions options = new CheckoutOptions(); - return repository.Checkout(commitOrBranchSpec, options); - } - - /// - /// Checkout the commit pointed at by the tip of the specified . - /// - /// If this commit is the current tip of the branch as it exists in the repository, the HEAD - /// will point to this branch. Otherwise, the HEAD will be detached, pointing at the commit sha. - /// - /// - /// The being worked with. - /// The to check out. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public static Branch Checkout(this IRepository repository, Branch branch) - { - CheckoutOptions options = new CheckoutOptions(); - return repository.Checkout(branch, options); - } - - /// - /// Checkout the specified . - /// - /// Will detach the HEAD and make it point to this commit sha. - /// - /// - /// The being worked with. - /// The to check out. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public static Branch Checkout(this IRepository repository, Commit commit) - { - CheckoutOptions options = new CheckoutOptions(); - return repository.Checkout(commit, options); - } - internal static string BuildRelativePathFrom(this IRepository repo, string path) { //TODO: To be removed when libgit2 natively implements this @@ -446,54 +371,6 @@ public static MergeResult Merge(this IRepository repository, string committish, return repository.Merge(committish, merger, null); } - /// - /// Checkout the tip commit of the specified object. If this commit is the - /// current tip of the branch, will checkout the named branch. Otherwise, will checkout the tip commit - /// as a detached HEAD. - /// - /// The being worked with. - /// The to check out. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public static Branch Checkout(this IRepository repository, Branch branch, CheckoutOptions options) - { - return repository.Checkout(branch, options); - } - - /// - /// Checkout the specified . - /// - /// Will detach the HEAD and make it point to this commit sha. - /// - /// - /// The being worked with. - /// The to check out. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public static Branch Checkout(this IRepository repository, Commit commit, CheckoutOptions options) - { - return repository.Checkout(commit, options); - } - - /// - /// Checkout the specified , reference or SHA. - /// - /// If the committishOrBranchSpec parameter resolves to a branch name, then the checked out HEAD will - /// will point to the branch. Otherwise, the HEAD will be detached, pointing at the commit sha. - /// - /// - /// The being worked with. - /// A revparse spec for the commit or branch to checkout. - /// controlling checkout behavior. - /// The that was checked out. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Checkout()")] - public static Branch Checkout(this IRepository repository, string committishOrBranchSpec, CheckoutOptions options) - { - return repository.Checkout(committishOrBranchSpec, options); - } - /// /// Updates specifed paths in the index and working directory with the versions from the specified branch, reference, or SHA. /// @@ -567,124 +444,6 @@ public static RevertResult Revert(this IRepository repository, Commit commit, Si return repository.Revert(commit, reverter, null); } - /// - /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). - /// - /// The being worked with. - /// The path of the file within the working directory. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Stage()")] - public static void Stage(this IRepository repository, string path) - { - repository.Stage(path, null); - } - - /// - /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). - /// - /// The being worked with. - /// The collection of paths of the files within the working directory. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Stage()")] - public static void Stage(this IRepository repository, IEnumerable paths) - { - repository.Stage(paths, null); - } - - /// - /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). - /// - /// The being worked with. - /// The path of the file within the working directory. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - public static void Unstage(this IRepository repository, string path) - { - repository.Unstage(path, null); - } - - /// - /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). - /// - /// The being worked with. - /// The collection of paths of the files within the working directory. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Unstage()")] - public static void Unstage(this IRepository repository, IEnumerable paths) - { - repository.Unstage(paths, null); - } - - /// - /// Removes a file from the staging area, and optionally removes it from the working directory as well. - /// - /// If the file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the file from the working directory as well. - /// - /// - /// The being worked with. - /// The path of the file within the working directory. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove")] - public static void Remove(this IRepository repository, string path) - { - Commands.Remove(repository, path, true, null); - } - - /// - /// Removes a file from the staging area, and optionally removes it from the working directory as well. - /// - /// If the file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the file from the working directory as well. - /// - /// - /// The being worked with. - /// The path of the file within the working directory. - /// True to remove the file from the working directory, False otherwise. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove")] - public static void Remove(this IRepository repository, string path, bool removeFromWorkingDirectory) - { - Commands.Remove(repository, path, removeFromWorkingDirectory, null); - } - - /// - /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. - /// - /// If a file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the files from the working directory as well. - /// - /// - /// The being worked with. - /// The collection of paths of the files within the working directory. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove")] - public static void Remove(this IRepository repository, IEnumerable paths) - { - Commands.Remove(repository, paths, true, null); - } - - /// - /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. - /// - /// If a file has already been deleted from the working directory, this method will only deal - /// with promoting the removal to the staging area. - /// - /// - /// The default behavior is to remove the files from the working directory as well. - /// - /// - /// The being worked with. - /// The collection of paths of the files within the working directory. - /// True to remove the files from the working directory, False otherwise. - [Obsolete("This method is deprecated. Please use LibGit2Sharp.Commands.Remove")] - public static void Remove(this IRepository repository, IEnumerable paths, bool removeFromWorkingDirectory) - { - repository.Remove(paths, removeFromWorkingDirectory, null); - } - /// /// Retrieves the state of all files in the working directory, comparing them against the staging area and the latest commit. /// diff --git a/LibGit2Sharp/RepositoryNotFoundException.cs b/LibGit2Sharp/RepositoryNotFoundException.cs index 2255c0891..e2bc63d8b 100644 --- a/LibGit2Sharp/RepositoryNotFoundException.cs +++ b/LibGit2Sharp/RepositoryNotFoundException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { @@ -7,7 +9,9 @@ namespace LibGit2Sharp /// The exception that is thrown when a is being built with /// a path that doesn't point at a valid Git repository or workdir. /// +#if NETFRAMEWORK [Serializable] +#endif public class RepositoryNotFoundException : LibGit2SharpException { /// @@ -27,7 +31,7 @@ public RepositoryNotFoundException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public RepositoryNotFoundException(string format, params object[] args) : base(format, args) @@ -42,6 +46,7 @@ public RepositoryNotFoundException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,5 +55,6 @@ public RepositoryNotFoundException(string message, Exception innerException) protected RepositoryNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/LibGit2Sharp/RepositoryOptions.cs b/LibGit2Sharp/RepositoryOptions.cs index 3b6d39da5..55692a663 100644 --- a/LibGit2Sharp/RepositoryOptions.cs +++ b/LibGit2Sharp/RepositoryOptions.cs @@ -28,39 +28,6 @@ public sealed class RepositoryOptions /// public string IndexPath { get; set; } - /// - /// Overrides the probed location of the Global configuration file of a repository. - /// - /// The path has either to lead to an existing valid configuration file, - /// or to a non existent configuration file which will be eventually created. - /// - /// . - /// - [Obsolete("This option is deprecated. Use GlobalConfiguration.SetConfigSearchPaths()")] - public string GlobalConfigurationLocation { get; set; } - - /// - /// Overrides the probed location of the XDG configuration file of a repository. - /// - /// The path has either to lead to an existing valid configuration file, - /// or to a non existent configuration file which will be eventually created. - /// - /// . - /// - [Obsolete("This option is deprecated. Use GlobalConfiguration.SetConfigSearchPaths()")] - public string XdgConfigurationLocation { get; set; } - - /// - /// Overrides the probed location of the System configuration file of a repository. - /// - /// The path has to lead to an existing valid configuration file, - /// or to a non existent configuration file which will be eventually created. - /// - /// . - /// - [Obsolete("This option is deprecated. Use GlobalConfiguration.SetConfigSearchPaths()")] - public string SystemConfigurationLocation { get; set; } - /// /// Overrides the default identity to be used when creating reflog entries. /// diff --git a/LibGit2Sharp/RepositoryStatus.cs b/LibGit2Sharp/RepositoryStatus.cs index 609a63b51..cc1c6e7e0 100644 --- a/LibGit2Sharp/RepositoryStatus.cs +++ b/LibGit2Sharp/RepositoryStatus.cs @@ -64,7 +64,7 @@ internal unsafe RepositoryStatus(Repository repo, StatusOptions options) for (int i = 0; i < count; i++) { - git_status_entry *entry = Proxy.git_status_byindex(list, i); + git_status_entry* entry = Proxy.git_status_byindex(list, i); AddStatusEntryForDelta(entry->status, entry->head_to_index, entry->index_to_workdir); } @@ -78,9 +78,6 @@ private static GitStatusOptions CreateStatusOptions(StatusOptions options) { Version = 1, Show = (GitStatusShow)options.Show, - Flags = - GitStatusOptionFlags.IncludeUntracked | - GitStatusOptionFlags.RecurseUntrackedDirs, }; if (options.IncludeIgnored) @@ -88,6 +85,11 @@ private static GitStatusOptions CreateStatusOptions(StatusOptions options) coreOptions.Flags |= GitStatusOptionFlags.IncludeIgnored; } + if (options.IncludeUntracked) + { + coreOptions.Flags |= GitStatusOptionFlags.IncludeUntracked; + } + if (options.DetectRenamesInIndex) { coreOptions.Flags |= @@ -114,6 +116,12 @@ private static GitStatusOptions CreateStatusOptions(StatusOptions options) GitStatusOptionFlags.RecurseIgnoredDirs; } + if (options.RecurseUntrackedDirs) + { + coreOptions.Flags |= + GitStatusOptionFlags.RecurseUntrackedDirs; + } + if (options.PathSpec != null) { coreOptions.PathSpec = GitStrArrayManaged.BuildFrom(options.PathSpec); @@ -142,24 +150,24 @@ private unsafe void AddStatusEntryForDelta(FileStatus gitStatus, git_diff_delta* if ((gitStatus & FileStatus.RenamedInIndex) == FileStatus.RenamedInIndex) { headToIndexRenameDetails = - new RenameDetails(LaxFilePathMarshaler.FromNative(deltaHeadToIndex->old_file.Path).Native, - LaxFilePathMarshaler.FromNative(deltaHeadToIndex->new_file.Path).Native, + new RenameDetails(LaxUtf8Marshaler.FromNative(deltaHeadToIndex->old_file.Path), + LaxUtf8Marshaler.FromNative(deltaHeadToIndex->new_file.Path), (int)deltaHeadToIndex->similarity); } if ((gitStatus & FileStatus.RenamedInWorkdir) == FileStatus.RenamedInWorkdir) { indexToWorkDirRenameDetails = - new RenameDetails(LaxFilePathMarshaler.FromNative(deltaIndexToWorkDir->old_file.Path).Native, - LaxFilePathMarshaler.FromNative(deltaIndexToWorkDir->new_file.Path).Native, + new RenameDetails(LaxUtf8Marshaler.FromNative(deltaIndexToWorkDir->old_file.Path), + LaxUtf8Marshaler.FromNative(deltaIndexToWorkDir->new_file.Path), (int)deltaIndexToWorkDir->similarity); } - var filePath = (deltaIndexToWorkDir != null) - ? LaxFilePathMarshaler.FromNative(deltaIndexToWorkDir->new_file.Path) - : LaxFilePathMarshaler.FromNative(deltaHeadToIndex->new_file.Path); + var filePath = LaxUtf8Marshaler.FromNative(deltaIndexToWorkDir != null ? + deltaIndexToWorkDir->new_file.Path : + deltaHeadToIndex->new_file.Path); - StatusEntry statusEntry = new StatusEntry(filePath.Native, gitStatus, headToIndexRenameDetails, indexToWorkDirRenameDetails); + StatusEntry statusEntry = new StatusEntry(filePath, gitStatus, headToIndexRenameDetails, indexToWorkDirRenameDetails); if (gitStatus == FileStatus.Unaltered) { diff --git a/LibGit2Sharp/RevertResult.cs b/LibGit2Sharp/RevertResult.cs index da54046a4..8f9a270d3 100644 --- a/LibGit2Sharp/RevertResult.cs +++ b/LibGit2Sharp/RevertResult.cs @@ -34,7 +34,7 @@ internal RevertResult(RevertStatus status, Commit commit = null) public virtual RevertStatus Status { get; private set; } } - /// + /// /// The status of what happened as a result of a revert. /// public enum RevertStatus diff --git a/LibGit2Sharp/Signature.cs b/LibGit2Sharp/Signature.cs index 003abd629..7ed7a4916 100644 --- a/LibGit2Sharp/Signature.cs +++ b/LibGit2Sharp/Signature.cs @@ -21,7 +21,7 @@ internal unsafe Signature(git_signature* sig) { name = LaxUtf8Marshaler.FromNative(sig->name); email = LaxUtf8Marshaler.FromNative(sig->email); - when = Epoch.ToDateTimeOffset(sig->when.time, sig->when.offset); + when = DateTimeOffset.FromUnixTimeSeconds(sig->when.time).ToOffset(TimeSpan.FromMinutes(sig->when.offset)); } /// @@ -86,10 +86,10 @@ public DateTimeOffset When } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as Signature); diff --git a/LibGit2Sharp/SimilarityOptions.cs b/LibGit2Sharp/SimilarityOptions.cs index 13d26abf2..4d2b0cd95 100644 --- a/LibGit2Sharp/SimilarityOptions.cs +++ b/LibGit2Sharp/SimilarityOptions.cs @@ -81,7 +81,7 @@ public SimilarityOptions() /// public static SimilarityOptions None { - get { return new SimilarityOptions {RenameDetectionMode = RenameDetectionMode.None}; } + get { return new SimilarityOptions { RenameDetectionMode = RenameDetectionMode.None }; } } /// @@ -89,7 +89,7 @@ public static SimilarityOptions None /// public static SimilarityOptions Renames { - get { return new SimilarityOptions {RenameDetectionMode = RenameDetectionMode.Renames}; } + get { return new SimilarityOptions { RenameDetectionMode = RenameDetectionMode.Renames }; } } /// @@ -97,7 +97,7 @@ public static SimilarityOptions Renames /// public static SimilarityOptions Exact { - get { return new SimilarityOptions {RenameDetectionMode = RenameDetectionMode.Exact}; } + get { return new SimilarityOptions { RenameDetectionMode = RenameDetectionMode.Exact }; } } /// @@ -105,7 +105,7 @@ public static SimilarityOptions Exact /// public static SimilarityOptions Copies { - get { return new SimilarityOptions {RenameDetectionMode = RenameDetectionMode.Copies}; } + get { return new SimilarityOptions { RenameDetectionMode = RenameDetectionMode.Copies }; } } /// @@ -113,7 +113,7 @@ public static SimilarityOptions Copies /// public static SimilarityOptions CopiesHarder { - get { return new SimilarityOptions {RenameDetectionMode = RenameDetectionMode.CopiesHarder}; } + get { return new SimilarityOptions { RenameDetectionMode = RenameDetectionMode.CopiesHarder }; } } /// @@ -121,7 +121,7 @@ public static SimilarityOptions CopiesHarder /// public static SimilarityOptions Default { - get { return new SimilarityOptions {RenameDetectionMode = RenameDetectionMode.Default}; } + get { return new SimilarityOptions { RenameDetectionMode = RenameDetectionMode.Default }; } } /// diff --git a/LibGit2Sharp/SmartSubtransport.cs b/LibGit2Sharp/SmartSubtransport.cs index 709f0c230..6160c849b 100644 --- a/LibGit2Sharp/SmartSubtransport.cs +++ b/LibGit2Sharp/SmartSubtransport.cs @@ -73,7 +73,9 @@ public int CertificateCheck(Certificate cert, bool valid, string hostname) var certPtr = sshCert.ToPointer(); ret = NativeMethods.git_transport_smart_certificate_check(Transport, certPtr, valid ? 1 : 0, hostname); Marshal.FreeHGlobal(certPtr); - } else { + } + else + { IntPtr certPtr, dataPtr; certPtr = x509Cert.ToPointers(out dataPtr); ret = NativeMethods.git_transport_smart_certificate_check(Transport, certPtr, valid ? 1 : 0, hostname); @@ -81,9 +83,21 @@ public int CertificateCheck(Certificate cert, bool valid, string hostname) Marshal.FreeHGlobal(certPtr); } + if (ret > 0 || ret == (int)GitErrorCode.PassThrough) + { + ret = valid ? 0 : -1; + } + return ret; } + /// + /// Acquires credentials. + /// + /// Receives the credentials if the operation is successful. + /// The username. + /// The credential types allowed. The only supported one is . May be empty but should not be null. + /// 0 if successful; a non-zero error code that came from otherwise. public int AcquireCredentials(out Credentials cred, string user, params Type[] methods) { // Convert the user-provided types to libgit2's flags @@ -94,6 +108,10 @@ public int AcquireCredentials(out Credentials cred, string user, params Type[] m { allowed |= (int)GitCredentialType.UserPassPlaintext; } + else if (method == typeof(DefaultCredentials)) + { + allowed |= (int)GitCredentialType.Default; + } else { throw new InvalidOperationException("Unknown type passes as allowed credential"); @@ -110,16 +128,19 @@ public int AcquireCredentials(out Credentials cred, string user, params Type[] m if (credHandle == IntPtr.Zero) { - throw new InvalidOperationException("creditals callback indicated success but returned no credentials"); + throw new InvalidOperationException("credentials callback indicated success but returned no credentials"); } unsafe { - var baseCred = (GitCredential*) credHandle; + var baseCred = (GitCredential*)credHandle; switch (baseCred->credtype) { case GitCredentialType.UserPassPlaintext: - cred = UsernamePasswordCredentials.FromNative((GitCredentialUserpass*) credHandle); + cred = UsernamePasswordCredentials.FromNative((GitCredentialUserpass*)credHandle); + return 0; + case GitCredentialType.Default: + cred = new DefaultCredentials(); return 0; default: throw new InvalidOperationException("User returned an unkown credential type"); @@ -127,13 +148,19 @@ public int AcquireCredentials(out Credentials cred, string user, params Type[] m } } + /// + /// libgit2 will call an action back with a null url to indicate that + /// it should re-use the prior url; store the url so that we can replay. + /// + private string LastActionUrl { get; set; } + /// /// Invoked by libgit2 to create a connection using this subtransport. /// /// The endpoint to connect to /// The type of connection to create /// A SmartSubtransportStream representing the connection - protected abstract SmartSubtransportStream Action(String url, GitSmartSubtransportAction action); + protected abstract SmartSubtransportStream Action(string url, GitSmartSubtransportAction action); /// /// Invoked by libgit2 when this subtransport is no longer needed, but may be re-used in the future. @@ -200,45 +227,59 @@ private static int Action( stream = IntPtr.Zero; SmartSubtransport t = GCHandle.FromIntPtr(Marshal.ReadIntPtr(subtransport, GitSmartSubtransport.GCHandleOffset)).Target as SmartSubtransport; - String urlAsString = LaxUtf8Marshaler.FromNative(url); + string urlAsString = LaxUtf8Marshaler.FromNative(url); - if (null != t && - !String.IsNullOrEmpty(urlAsString)) + if (t == null) { - try - { - stream = t.Action(urlAsString, action).GitSmartTransportStreamPointer; + Proxy.git_error_set_str(GitErrorCategory.Net, "no subtransport provided"); + return (int)GitErrorCode.Error; + } - return 0; - } - catch (Exception ex) - { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); - } + if (string.IsNullOrEmpty(urlAsString)) + { + urlAsString = t.LastActionUrl; } - return (int)GitErrorCode.Error; + if (string.IsNullOrEmpty(urlAsString)) + { + Proxy.git_error_set_str(GitErrorCategory.Net, "no url provided"); + return (int)GitErrorCode.Error; + } + + try + { + stream = t.Action(urlAsString, action).GitSmartTransportStreamPointer; + t.LastActionUrl = urlAsString; + return 0; + } + catch (Exception ex) + { + Proxy.git_error_set_str(GitErrorCategory.Net, ex); + return (int)GitErrorCode.Error; + } } private static int Close(IntPtr subtransport) { SmartSubtransport t = GCHandle.FromIntPtr(Marshal.ReadIntPtr(subtransport, GitSmartSubtransport.GCHandleOffset)).Target as SmartSubtransport; - if (null != t) + if (t == null) { - try - { - t.Close(); - - return 0; - } - catch (Exception ex) - { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); - } + Proxy.git_error_set_str(GitErrorCategory.Net, "no subtransport provided"); + return (int)GitErrorCode.Error; } - return (int)GitErrorCode.Error; + try + { + t.Close(); + + return 0; + } + catch (Exception ex) + { + Proxy.git_error_set_str(GitErrorCategory.Net, ex); + return (int)GitErrorCode.Error; + } } private static void Free(IntPtr subtransport) @@ -253,7 +294,7 @@ private static void Free(IntPtr subtransport) } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); + Proxy.git_error_set_str(GitErrorCategory.Net, ex); } } } diff --git a/LibGit2Sharp/SmartSubtransportRegistration.cs b/LibGit2Sharp/SmartSubtransportRegistration.cs index 2c017dc57..d33887122 100644 --- a/LibGit2Sharp/SmartSubtransportRegistration.cs +++ b/LibGit2Sharp/SmartSubtransportRegistration.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using System.Runtime.InteropServices; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -10,7 +11,7 @@ namespace LibGit2Sharp /// under a particular scheme (eg "http"). /// /// The type of SmartSubtransport to register - public sealed class SmartSubtransportRegistration + public sealed class SmartSubtransportRegistration : SmartSubtransportRegistrationData where T : SmartSubtransport, new() { /// @@ -25,15 +26,6 @@ internal SmartSubtransportRegistration(string scheme) FunctionPointer = CreateFunctionPointer(); } - /// - /// The URI scheme (eg "http") for this transport. - /// - public string Scheme { get; private set; } - - internal IntPtr RegistrationPointer { get; private set; } - - internal IntPtr FunctionPointer { get; private set; } - private IntPtr CreateRegistrationPointer() { var registration = new GitSmartSubtransportRegistration(); @@ -84,7 +76,7 @@ private static int Subtransport( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); + Proxy.git_error_set_str(GitErrorCategory.Net, ex); } return (int)GitErrorCode.Error; @@ -103,7 +95,7 @@ private static int Transport( } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); + Proxy.git_error_set_str(GitErrorCategory.Net, ex); } return (int)GitErrorCode.Error; diff --git a/LibGit2Sharp/SmartSubtransportRegistrationData.cs b/LibGit2Sharp/SmartSubtransportRegistrationData.cs new file mode 100644 index 000000000..dbf612adb --- /dev/null +++ b/LibGit2Sharp/SmartSubtransportRegistrationData.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// Information about a smart subtransport registration. + /// + public abstract class SmartSubtransportRegistrationData + { + /// + /// The URI scheme for this transport, for example "http" or "ssh". + /// + public string Scheme { get; internal set; } + + internal IntPtr RegistrationPointer { get; set; } + + internal IntPtr FunctionPointer { get; set; } + } +} diff --git a/LibGit2Sharp/SmartSubtransportStream.cs b/LibGit2Sharp/SmartSubtransportStream.cs index b5cb21c02..008d1fcd0 100644 --- a/LibGit2Sharp/SmartSubtransportStream.cs +++ b/LibGit2Sharp/SmartSubtransportStream.cs @@ -44,13 +44,20 @@ protected virtual void Free() } /// - /// Requests that the stream write the next length bytes of the stream to the provided Stream object. + /// Reads from the transport into the provided object. /// + /// The stream to copy the read bytes into. + /// The number of bytes expected from the underlying transport. + /// Receives the number of bytes actually read. + /// The error code to propagate back to the native code that requested this operation. 0 is expected, and exceptions may be thrown. public abstract int Read(Stream dataStream, long length, out long bytesRead); /// - /// Requests that the stream write the first length bytes of the provided Stream object to the stream. + /// Writes the content of a given stream to the transport. /// + /// The stream with the data to write to the transport. + /// The number of bytes to read from . + /// The error code to propagate back to the native code that requested this operation. 0 is expected, and exceptions may be thrown. public abstract int Write(Stream dataStream, long length); /// @@ -61,6 +68,13 @@ public virtual SmartSubtransport SmartTransport get { return this.subtransport; } } + private Exception StoredError { get; set; } + + internal void SetError(Exception ex) + { + StoredError = ex; + } + private SmartSubtransport subtransport; private IntPtr nativeStreamPointer; @@ -96,6 +110,19 @@ private static class EntryPoints public static GitSmartSubtransportStream.write_callback WriteCallback = new GitSmartSubtransportStream.write_callback(Write); public static GitSmartSubtransportStream.free_callback FreeCallback = new GitSmartSubtransportStream.free_callback(Free); + private static int SetError(SmartSubtransportStream stream, Exception caught) + { + Exception ret = (stream.StoredError != null) ? stream.StoredError : caught; + GitErrorCode errorCode = GitErrorCode.Error; + + if (ret is NativeException) + { + errorCode = ((NativeException)ret).ErrorCode; + } + + return (int)errorCode; + } + private unsafe static int Read( IntPtr stream, IntPtr buffer, @@ -107,31 +134,37 @@ private unsafe static int Read( SmartSubtransportStream transportStream = GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream; - if (transportStream != null && - buf_size.ToUInt64() < (ulong)long.MaxValue) + if (transportStream == null) + { + Proxy.git_error_set_str(GitErrorCategory.Net, "no transport stream provided"); + return (int)GitErrorCode.Error; + } + + if (buf_size.ToUInt64() >= (ulong)long.MaxValue) + { + Proxy.git_error_set_str(GitErrorCategory.Net, "buffer size is too large"); + return (int)GitErrorCode.Error; + } + + try { using (UnmanagedMemoryStream memoryStream = new UnmanagedMemoryStream((byte*)buffer, 0, (long)buf_size.ToUInt64(), FileAccess.ReadWrite)) { - try - { - long longBytesRead; + long longBytesRead; - int toReturn = transportStream.Read(memoryStream, (long)buf_size.ToUInt64(), out longBytesRead); + int toReturn = transportStream.Read(memoryStream, (long)buf_size.ToUInt64(), out longBytesRead); - bytes_read = new UIntPtr((ulong)Math.Max(0, longBytesRead)); + bytes_read = new UIntPtr((ulong)Math.Max(0, longBytesRead)); - return toReturn; - } - catch (Exception ex) - { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); - } + return toReturn; } } - - return (int)GitErrorCode.Error; + catch (Exception ex) + { + return SetError(transportStream, ex); + } } private static unsafe int Write(IntPtr stream, IntPtr buffer, UIntPtr len) @@ -139,24 +172,31 @@ private static unsafe int Write(IntPtr stream, IntPtr buffer, UIntPtr len) SmartSubtransportStream transportStream = GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream; - if (transportStream != null && len.ToUInt64() < (ulong)long.MaxValue) + if (transportStream == null) + { + Proxy.git_error_set_str(GitErrorCategory.Net, "no transport stream provided"); + return (int)GitErrorCode.Error; + } + + if (len.ToUInt64() >= (ulong)long.MaxValue) + { + Proxy.git_error_set_str(GitErrorCategory.Net, "write length is too large"); + return (int)GitErrorCode.Error; + } + + try { long length = (long)len.ToUInt64(); using (UnmanagedMemoryStream dataStream = new UnmanagedMemoryStream((byte*)buffer, length)) { - try - { - return transportStream.Write(dataStream, length); - } - catch (Exception ex) - { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); - } + return transportStream.Write(dataStream, length); } } - - return (int)GitErrorCode.Error; + catch (Exception ex) + { + return SetError(transportStream, ex); + } } private static void Free(IntPtr stream) @@ -172,7 +212,7 @@ private static void Free(IntPtr stream) } catch (Exception ex) { - Proxy.giterr_set_str(GitErrorCategory.Net, ex); + Proxy.git_error_set_str(GitErrorCategory.Net, ex); } } } diff --git a/LibGit2Sharp/StashCollection.cs b/LibGit2Sharp/StashCollection.cs index 5fe775eba..42162ada5 100644 --- a/LibGit2Sharp/StashCollection.cs +++ b/LibGit2Sharp/StashCollection.cs @@ -67,7 +67,7 @@ public virtual Stash this[int index] { if (index < 0) { - throw new ArgumentOutOfRangeException("index", "The passed index must be a positive integer."); + throw new ArgumentOutOfRangeException(nameof(index), "The passed index must be a positive integer."); } GitObject stashCommit = repo.Lookup(string.Format(CultureInfo.InvariantCulture, @@ -146,7 +146,7 @@ public virtual StashApplyStatus Apply(int index, StashApplyOptions options) { if (index < 0) { - throw new ArgumentException("The passed index must be a positive integer.", "index"); + throw new ArgumentException("The passed index must be a positive integer.", nameof(index)); } if (options == null) @@ -189,7 +189,7 @@ public virtual StashApplyStatus Pop(int index, StashApplyOptions options) { if (index < 0) { - throw new ArgumentException("The passed index must be a positive integer.", "index"); + throw new ArgumentException("The passed index must be a positive integer.", nameof(index)); } if (options == null) @@ -231,7 +231,7 @@ public virtual void Remove(int index) { if (index < 0) { - throw new ArgumentException("The passed index must be a positive integer.", "index"); + throw new ArgumentException("The passed index must be a positive integer.", nameof(index)); } Proxy.git_stash_drop(repo.Handle, index); diff --git a/LibGit2Sharp/StatusEntry.cs b/LibGit2Sharp/StatusEntry.cs index 7008712c6..bd2ef8883 100644 --- a/LibGit2Sharp/StatusEntry.cs +++ b/LibGit2Sharp/StatusEntry.cs @@ -66,10 +66,10 @@ public virtual RenameDetails IndexToWorkDirRenameDetails } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as StatusEntry); diff --git a/LibGit2Sharp/StatusOptions.cs b/LibGit2Sharp/StatusOptions.cs index bde54c168..47dba9197 100644 --- a/LibGit2Sharp/StatusOptions.cs +++ b/LibGit2Sharp/StatusOptions.cs @@ -36,6 +36,8 @@ public StatusOptions() { DetectRenamesInIndex = true; IncludeIgnored = true; + IncludeUntracked = true; + RecurseUntrackedDirs = true; } /// @@ -63,6 +65,11 @@ public StatusOptions() /// public bool RecurseIgnoredDirs { get; set; } + /// + /// Recurse into untracked directories + /// + public bool RecurseUntrackedDirs { get; set; } + /// /// Limit the scope of paths to consider to the provided pathspecs /// @@ -94,5 +101,9 @@ public StatusOptions() /// public bool IncludeIgnored { get; set; } + /// + /// Include untracked files when scanning for status + /// + public bool IncludeUntracked { get; set; } } } diff --git a/LibGit2Sharp/Submodule.cs b/LibGit2Sharp/Submodule.cs index ace995205..f8193af13 100644 --- a/LibGit2Sharp/Submodule.cs +++ b/LibGit2Sharp/Submodule.cs @@ -107,10 +107,10 @@ public virtual SubmoduleStatus RetrieveStatus() } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as Submodule); @@ -136,7 +136,7 @@ public override int GetHashCode() } /// - /// Returns the , a representation of the current . + /// Returns the , a representation of the current . /// /// The that represents the current . public override string ToString() diff --git a/LibGit2Sharp/SubmoduleCollection.cs b/LibGit2Sharp/SubmoduleCollection.cs index fc508107a..061196c7d 100644 --- a/LibGit2Sharp/SubmoduleCollection.cs +++ b/LibGit2Sharp/SubmoduleCollection.cs @@ -74,43 +74,41 @@ public virtual void Init(string name, bool overwrite) /// Update specified submodule. /// /// This will: - /// 1) Optionally initialize the if it not already initialzed, + /// 1) Optionally initialize the if it not already initialized, /// 2) clone the sub repository if it has not already been cloned, and /// 3) checkout the commit ID for the submodule in the sub repository. /// /// /// The name of the submodule to update. - /// Options controlling submodule udpate behavior and callbacks. + /// Options controlling submodule update behavior and callbacks. public virtual void Update(string name, SubmoduleUpdateOptions options) { - options = options ?? new SubmoduleUpdateOptions(); + options ??= new SubmoduleUpdateOptions(); - using (var handle = Proxy.git_submodule_lookup(repo.Handle, name)) - { - if (handle == null) - { - throw new NotFoundException("Submodule lookup failed for '{0}'.", - name); - } + using var handle = Proxy.git_submodule_lookup(repo.Handle, name) ?? throw new NotFoundException("Submodule lookup failed for '{0}'.", name); + using var checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options); + using var fetchOptionsWrapper = new GitFetchOptionsWrapper(); - using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options)) - { - var gitCheckoutOptions = checkoutOptionsWrapper.Options; + var gitCheckoutOptions = checkoutOptionsWrapper.Options; - var remoteCallbacks = new RemoteCallbacks(options); - var gitRemoteCallbacks = remoteCallbacks.GenerateCallbacks(); + var gitFetchOptions = fetchOptionsWrapper.Options; + gitFetchOptions.ProxyOptions = options.FetchOptions.ProxyOptions.CreateGitProxyOptions(); + gitFetchOptions.RemoteCallbacks = new RemoteCallbacks(options.FetchOptions).GenerateCallbacks(); - var gitSubmoduleUpdateOpts = new GitSubmoduleUpdateOptions - { - Version = 1, - CheckoutOptions = gitCheckoutOptions, - FetchOptions = new GitFetchOptions { ProxyOptions = new GitProxyOptions { Version = 1 }, RemoteCallbacks = gitRemoteCallbacks }, - CloneCheckoutStrategy = CheckoutStrategy.GIT_CHECKOUT_SAFE - }; - - Proxy.git_submodule_update(handle, options.Init, ref gitSubmoduleUpdateOpts); - } + if (options.FetchOptions != null && options.FetchOptions.CustomHeaders != null) + { + gitFetchOptions.CustomHeaders = + GitStrArrayManaged.BuildFrom(options.FetchOptions.CustomHeaders); } + + var gitSubmoduleUpdateOpts = new GitSubmoduleUpdateOptions + { + Version = 1, + CheckoutOptions = gitCheckoutOptions, + FetchOptions = gitFetchOptions + }; + + Proxy.git_submodule_update(handle, options.Init, ref gitSubmoduleUpdateOpts); } /// diff --git a/LibGit2Sharp/SubmoduleUpdateOptions.cs b/LibGit2Sharp/SubmoduleUpdateOptions.cs index 89f895d75..082e17338 100644 --- a/LibGit2Sharp/SubmoduleUpdateOptions.cs +++ b/LibGit2Sharp/SubmoduleUpdateOptions.cs @@ -6,7 +6,7 @@ namespace LibGit2Sharp /// /// Options controlling Submodule Update behavior and callbacks. /// - public sealed class SubmoduleUpdateOptions : FetchOptionsBase, IConvertableToGitCheckoutOpts + public sealed class SubmoduleUpdateOptions : IConvertableToGitCheckoutOpts { /// /// Initialize the submodule if it is not already initialized. @@ -30,6 +30,11 @@ public sealed class SubmoduleUpdateOptions : FetchOptionsBase, IConvertableToGit /// public CheckoutNotifyFlags CheckoutNotifyFlags { get; set; } + /// + /// Collection of parameters controlling Fetch behavior. + /// + public FetchOptions FetchOptions { get; internal set; } = new(); + CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks() { return CheckoutCallbacks.From(OnCheckoutProgress, OnCheckoutNotify); diff --git a/LibGit2Sharp/TagCollection.cs b/LibGit2Sharp/TagCollection.cs index 8bd9168b0..98bfd257d 100644 --- a/LibGit2Sharp/TagCollection.cs +++ b/LibGit2Sharp/TagCollection.cs @@ -114,7 +114,7 @@ public virtual Tag Add(string name, string objectish) /// The name. /// Revparse spec for the target object. /// True to allow silent overwriting a potentially existing tag, false otherwise. - public virtual Tag Add( string name, string objectish, bool allowOverwrite) + public virtual Tag Add(string name, string objectish, bool allowOverwrite) { Ensure.ArgumentNotNullOrEmptyString(objectish, "objectish"); diff --git a/LibGit2Sharp/TransientIndex.cs b/LibGit2Sharp/TransientIndex.cs new file mode 100644 index 000000000..b62678c83 --- /dev/null +++ b/LibGit2Sharp/TransientIndex.cs @@ -0,0 +1,31 @@ +using System; +using LibGit2Sharp.Core.Handles; + +namespace LibGit2Sharp +{ + /// + /// An implementation of with disposal managed by the caller + /// (instead of automatically disposing when the repository is disposed) + /// + public class TransientIndex : Index, IDisposable + { + /// + /// Needed for mocking purposes. + /// + protected TransientIndex() + { } + + internal TransientIndex(IndexHandle handle, Repository repo) + : base(handle, repo) + { + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Handle.SafeDispose(); + } + } +} diff --git a/LibGit2Sharp/Tree.cs b/LibGit2Sharp/Tree.cs index 0a19f4e04..30f534a99 100644 --- a/LibGit2Sharp/Tree.cs +++ b/LibGit2Sharp/Tree.cs @@ -5,16 +5,21 @@ using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; +using System.Text; +using System; namespace LibGit2Sharp { /// /// A container which references a list of other s and s. /// + /// + /// Since the introduction of partially cloned repositories, trees might be missing on your local repository (see https://git-scm.com/docs/partial-clone) + /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public class Tree : GitObject, IEnumerable { - private readonly FilePath path; + private readonly string path; private readonly ILazy lazyCount; @@ -24,32 +29,34 @@ public class Tree : GitObject, IEnumerable protected Tree() { } - internal Tree(Repository repo, ObjectId id, FilePath path) + internal Tree(Repository repo, ObjectId id, string path) : base(repo, id) { this.path = path ?? ""; - lazyCount = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_tree_entrycount); + lazyCount = GitObjectLazyGroup.Singleton(repo, id, Proxy.git_tree_entrycount, throwIfMissing: true); } /// /// Gets the number of immediately under this . /// - public virtual int Count { get { return lazyCount.Value; } } + /// Throws if tree is missing + public virtual int Count => lazyCount.Value; /// /// Gets the pointed at by the in this instance. /// /// The relative path to the from this instance. /// null if nothing has been found, the otherwise. + /// Throws if tree is missing public virtual TreeEntry this[string relativePath] { get { return RetrieveFromPath(relativePath); } } - private unsafe TreeEntry RetrieveFromPath(FilePath relativePath) + private unsafe TreeEntry RetrieveFromPath(string relativePath) { - if (relativePath.IsNullOrEmpty()) + if (string.IsNullOrEmpty(relativePath)) { return null; } @@ -61,21 +68,17 @@ private unsafe TreeEntry RetrieveFromPath(FilePath relativePath) return null; } - string posixPath = relativePath.Posix; - string filename = posixPath.Split('/').Last(); - string parentPath = posixPath.Substring(0, posixPath.Length - filename.Length); - return new TreeEntry(treeEntry, Id, repo, path.Combine(parentPath)); + string filename = relativePath.Split('/').Last(); + string parentPath = relativePath.Substring(0, relativePath.Length - filename.Length); + return new TreeEntry(treeEntry, Id, repo, Tree.CombinePath(path, parentPath)); } } - internal string Path - { - get { return path.Native; } - } + internal string Path => path; #region IEnumerable Members - unsafe TreeEntry byIndex(ObjectSafeWrapper obj, uint i, ObjectId parentTreeId, Repository repo, FilePath parentPath) + unsafe TreeEntry byIndex(ObjectSafeWrapper obj, uint i, ObjectId parentTreeId, Repository repo, string parentPath) { using (var entryHandle = Proxy.git_tree_entry_byindex(obj.ObjectPtr, i)) { @@ -83,15 +86,32 @@ unsafe TreeEntry byIndex(ObjectSafeWrapper obj, uint i, ObjectId parentTreeId, R } } + internal static string CombinePath(string a, string b) + { + var bld = new StringBuilder(); + bld.Append(a); + if (!string.IsNullOrEmpty(a) && + !a.EndsWith("/", StringComparison.Ordinal) && + !b.StartsWith("/", StringComparison.Ordinal)) + { + bld.Append('/'); + } + bld.Append(b); + + return bld.ToString(); + } + /// /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. + /// Throws if tree is missing public virtual IEnumerator GetEnumerator() { - using (var obj = new ObjectSafeWrapper(Id, repo.Handle)) + using (var obj = new ObjectSafeWrapper(Id, repo.Handle, throwIfMissing: true)) { - for (uint i = 0; i < Count; i++) { + for (uint i = 0; i < Count; i++) + { yield return byIndex(obj, i, Id, repo, path); } } @@ -101,6 +121,7 @@ public virtual IEnumerator GetEnumerator() /// Returns an enumerator that iterates through the collection. /// /// An object that can be used to iterate through the collection. + /// Throws if tree is missing IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/LibGit2Sharp/TreeChanges.cs b/LibGit2Sharp/TreeChanges.cs index 6e8a0eff5..6a54d9c09 100644 --- a/LibGit2Sharp/TreeChanges.cs +++ b/LibGit2Sharp/TreeChanges.cs @@ -55,7 +55,7 @@ private IEnumerable GetChangesOfKind(ChangeKind changeKind) private unsafe bool TryGetEntryWithChangeTypeAt(int index, ChangeKind changeKind, out TreeEntryChanges entry) { if (index < 0 || index > count.Value) - throw new ArgumentOutOfRangeException("index", "Index was out of range. Must be non-negative and less than the size of the collection."); + throw new ArgumentOutOfRangeException(nameof(index), "Index was out of range. Must be non-negative and less than the size of the collection."); var delta = Proxy.git_diff_get_delta(diff, index); @@ -90,7 +90,7 @@ public virtual IEnumerator GetEnumerator() private unsafe TreeEntryChanges GetEntryAt(int index) { if (index < 0 || index > count.Value) - throw new ArgumentOutOfRangeException("index", "Index was out of range. Must be non-negative and less than the size of the collection."); + throw new ArgumentOutOfRangeException(nameof(index), "Index was out of range. Must be non-negative and less than the size of the collection."); return new TreeEntryChanges(Proxy.git_diff_get_delta(diff, index)); } diff --git a/LibGit2Sharp/TreeDefinition.cs b/LibGit2Sharp/TreeDefinition.cs index 68f287879..91389f6e3 100644 --- a/LibGit2Sharp/TreeDefinition.cs +++ b/LibGit2Sharp/TreeDefinition.cs @@ -94,6 +94,7 @@ public virtual TreeDefinition Remove(string treeEntryPath) if (segments.Item2 == null) { entries.Remove(segments.Item1); + unwrappedTrees.Remove(segments.Item1); } if (!unwrappedTrees.ContainsKey(segments.Item1)) @@ -126,11 +127,6 @@ public virtual TreeDefinition Add(string targetTreeEntryPath, TreeEntryDefinitio Ensure.ArgumentNotNullOrEmptyString(targetTreeEntryPath, "targetTreeEntryPath"); Ensure.ArgumentNotNull(treeEntryDefinition, "treeEntryDefinition"); - if (Path.IsPathRooted(targetTreeEntryPath)) - { - throw new ArgumentException("The provided path is an absolute path."); - } - if (treeEntryDefinition is TransientTreeTreeEntryDefinition) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, @@ -206,6 +202,23 @@ public virtual TreeDefinition Add(string targetTreeEntryPath, string filePath, M return Add(targetTreeEntryPath, ted); } + /// + /// Adds or replaces a from an existing blob specified by its Object ID at the specified location. + /// + /// The path within this . + /// The object ID for this entry. + /// The file related attributes. + /// The current . + public virtual TreeDefinition Add(string targetTreeEntryPath, ObjectId id, Mode mode) + { + Ensure.ArgumentNotNull(id, "id"); + Ensure.ArgumentConformsTo(mode, m => m.HasAny(TreeEntryDefinition.BlobModes), "mode"); + + TreeEntryDefinition ted = TreeEntryDefinition.From(id, mode); + + return Add(targetTreeEntryPath, ted); + } + /// /// Adds or replaces a , dynamically built from the provided , at the specified location. /// @@ -383,9 +396,9 @@ public virtual TreeEntryDefinition this[string treeEntryPath] } } - private static Tuple ExtractPosixLeadingSegment(FilePath targetPath) + private static Tuple ExtractPosixLeadingSegment(string targetPath) { - string[] segments = targetPath.Posix.Split(new[] { '/' }, 2); + string[] segments = targetPath.Split(new[] { '/' }, 2); if (segments[0] == string.Empty || (segments.Length == 2 && (segments[1] == string.Empty || segments[1].StartsWith("/", StringComparison.Ordinal)))) { diff --git a/LibGit2Sharp/TreeEntry.cs b/LibGit2Sharp/TreeEntry.cs index f3713a776..943e14570 100644 --- a/LibGit2Sharp/TreeEntry.cs +++ b/LibGit2Sharp/TreeEntry.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.Globalization; -using System.Runtime.InteropServices; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; @@ -28,7 +27,7 @@ public class TreeEntry : IEquatable protected TreeEntry() { } - internal unsafe TreeEntry(TreeEntryHandle entry, ObjectId parentTreeId, Repository repo, FilePath parentPath) + internal unsafe TreeEntry(TreeEntryHandle entry, ObjectId parentTreeId, Repository repo, string parentPath) { this.parentTreeId = parentTreeId; this.repo = repo; @@ -41,7 +40,7 @@ internal unsafe TreeEntry(TreeEntryHandle entry, ObjectId parentTreeId, Reposito Mode = Proxy.git_tree_entry_attributes(entry); Name = Proxy.git_tree_entry_name(entry); - path = new Lazy(() => System.IO.Path.Combine(parentPath.Native, Name)); + path = new Lazy(() => Tree.CombinePath(parentPath, Name)); } /// @@ -94,10 +93,10 @@ private GitObject RetrieveTreeEntryTarget() } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as TreeEntry); diff --git a/LibGit2Sharp/TreeEntryChanges.cs b/LibGit2Sharp/TreeEntryChanges.cs index cfe0fd8e8..1ab1a6172 100644 --- a/LibGit2Sharp/TreeEntryChanges.cs +++ b/LibGit2Sharp/TreeEntryChanges.cs @@ -18,8 +18,8 @@ protected TreeEntryChanges() internal unsafe TreeEntryChanges(git_diff_delta* delta) { - Path = LaxFilePathMarshaler.FromNative(delta->new_file.Path).Native; - OldPath = LaxFilePathMarshaler.FromNative(delta->old_file.Path).Native; + Path = LaxUtf8Marshaler.FromNative(delta->new_file.Path); + OldPath = LaxUtf8Marshaler.FromNative(delta->old_file.Path); Mode = (Mode)delta->new_file.Mode; OldMode = (Mode)delta->old_file.Mode; diff --git a/LibGit2Sharp/TreeEntryDefinition.cs b/LibGit2Sharp/TreeEntryDefinition.cs index b89c59306..d32cc722c 100644 --- a/LibGit2Sharp/TreeEntryDefinition.cs +++ b/LibGit2Sharp/TreeEntryDefinition.cs @@ -54,6 +54,8 @@ internal static TreeEntryDefinition From(TreeEntry treeEntry) internal static TreeEntryDefinition From(Blob blob, Mode mode) { + Ensure.ArgumentNotNull(blob, "blob"); + return new TreeEntryDefinition { Mode = mode, @@ -63,6 +65,19 @@ internal static TreeEntryDefinition From(Blob blob, Mode mode) }; } + internal static TreeEntryDefinition From(ObjectId id, Mode mode) + { + Ensure.ArgumentNotNull(id, "id"); + Ensure.ArgumentNotNull(mode, "mode"); + + return new TreeEntryDefinition + { + Mode = mode, + TargetType = TreeEntryTargetType.Blob, + TargetId = id + }; + } + internal static TreeEntryDefinition TransientBlobFrom(string filePath, Mode mode) { Ensure.ArgumentConformsTo(mode, m => m.HasAny(BlobModes), "mode"); @@ -97,10 +112,10 @@ internal static TreeEntryDefinition From(Tree tree) } /// - /// Determines whether the specified is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// The to compare with the current . - /// True if the specified is equal to the current ; otherwise, false. + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. public override bool Equals(object obj) { return Equals(obj as TreeEntryDefinition); diff --git a/LibGit2Sharp/UnbornBranchException.cs b/LibGit2Sharp/UnbornBranchException.cs index 099704e8d..8f01a63ab 100644 --- a/LibGit2Sharp/UnbornBranchException.cs +++ b/LibGit2Sharp/UnbornBranchException.cs @@ -1,6 +1,7 @@ using System; -using System.Globalization; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { @@ -8,7 +9,9 @@ namespace LibGit2Sharp /// The exception that is thrown when a operation requiring an existing /// branch is performed against an unborn branch. /// +#if NETFRAMEWORK [Serializable] +#endif public class UnbornBranchException : LibGit2SharpException { /// @@ -28,7 +31,7 @@ public UnbornBranchException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public UnbornBranchException(string format, params object[] args) : base(format, args) @@ -43,6 +46,7 @@ public UnbornBranchException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,5 +55,6 @@ public UnbornBranchException(string message, Exception innerException) protected UnbornBranchException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/LibGit2Sharp/UniqueIdentifier.targets b/LibGit2Sharp/UniqueIdentifier.targets deleted file mode 100644 index f6eb926e3..000000000 --- a/LibGit2Sharp/UniqueIdentifier.targets +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - . - $(MSBuildThisFileDirectory) - $(LibGit2SharpPath)\Core\UniqueIdentifier.cs - $(CoreCompileDependsOn);GenerateUniqueIdentifierCs - $(CoreCleanDependsOn);CleanUniqueIdentifierCs - - - - - - - - - - - diff --git a/LibGit2Sharp/UnmatchedPathException.cs b/LibGit2Sharp/UnmatchedPathException.cs index 7d118346d..96e5654c7 100644 --- a/LibGit2Sharp/UnmatchedPathException.cs +++ b/LibGit2Sharp/UnmatchedPathException.cs @@ -1,12 +1,16 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif namespace LibGit2Sharp { /// /// The exception that is thrown when an explicit path or a list of explicit paths could not be matched. /// +#if NETFRAMEWORK [Serializable] +#endif public class UnmatchedPathException : LibGit2SharpException { /// @@ -26,7 +30,7 @@ public UnmatchedPathException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public UnmatchedPathException(string format, params object[] args) : base(format, args) @@ -41,6 +45,7 @@ public UnmatchedPathException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -49,5 +54,6 @@ public UnmatchedPathException(string message, Exception innerException) protected UnmatchedPathException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } diff --git a/LibGit2Sharp/UnmergedIndexEntriesException.cs b/LibGit2Sharp/UnmergedIndexEntriesException.cs index 729882678..f9f1a834b 100644 --- a/LibGit2Sharp/UnmergedIndexEntriesException.cs +++ b/LibGit2Sharp/UnmergedIndexEntriesException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -8,8 +10,10 @@ namespace LibGit2Sharp /// The exception that is thrown when an operation that requires a fully merged index /// is performed against an index with unmerged entries /// +#if NETFRAMEWORK [Serializable] - public class UnmergedIndexEntriesException : LibGit2SharpException +#endif + public class UnmergedIndexEntriesException : NativeException { /// /// Initializes a new instance of the class. @@ -28,7 +32,7 @@ public UnmergedIndexEntriesException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public UnmergedIndexEntriesException(string format, params object[] args) : base(format, args) @@ -43,6 +47,7 @@ public UnmergedIndexEntriesException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -51,9 +56,18 @@ public UnmergedIndexEntriesException(string message, Exception innerException) protected UnmergedIndexEntriesException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal UnmergedIndexEntriesException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal UnmergedIndexEntriesException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.UnmergedEntries; + } + } } } diff --git a/LibGit2Sharp/UserCanceledException.cs b/LibGit2Sharp/UserCanceledException.cs index 41eebb29a..f3c6af7dd 100644 --- a/LibGit2Sharp/UserCanceledException.cs +++ b/LibGit2Sharp/UserCanceledException.cs @@ -1,5 +1,7 @@ using System; +#if NETFRAMEWORK using System.Runtime.Serialization; +#endif using LibGit2Sharp.Core; namespace LibGit2Sharp @@ -7,8 +9,10 @@ namespace LibGit2Sharp /// /// The exception that is thrown when an operation is canceled. /// +#if NETFRAMEWORK [Serializable] - public class UserCancelledException : LibGit2SharpException +#endif + public class UserCancelledException : NativeException { /// /// Initializes a new instance of the class. @@ -27,7 +31,7 @@ public UserCancelledException(string message) /// /// Initializes a new instance of the class with a specified error message. /// - /// A composite format string for use in . + /// A composite format string for use in . /// An object array that contains zero or more objects to format. public UserCancelledException(string format, params object[] args) : base(format, args) @@ -42,6 +46,7 @@ public UserCancelledException(string message, Exception innerException) : base(message, innerException) { } +#if NETFRAMEWORK /// /// Initializes a new instance of the class with a serialized data. /// @@ -50,9 +55,18 @@ public UserCancelledException(string message, Exception innerException) protected UserCancelledException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif - internal UserCancelledException(string message, GitErrorCode code, GitErrorCategory category) - : base(message, code, category) + internal UserCancelledException(string message, GitErrorCategory category) + : base(message, category) { } + + internal override GitErrorCode ErrorCode + { + get + { + return GitErrorCode.User; + } + } } } diff --git a/LibGit2Sharp/Version.cs b/LibGit2Sharp/Version.cs index b4cbd88f0..2c21ccad2 100644 --- a/LibGit2Sharp/Version.cs +++ b/LibGit2Sharp/Version.cs @@ -1,6 +1,4 @@ using System.Globalization; -using System.IO; -using System.Linq; using System.Reflection; using LibGit2Sharp.Core; @@ -11,8 +9,6 @@ namespace LibGit2Sharp /// public class Version { - private readonly Assembly assembly = typeof(Repository).Assembly; - /// /// Needed for mocking purposes. /// @@ -31,10 +27,7 @@ public virtual string InformationalVersion { get { - var attribute = (AssemblyInformationalVersionAttribute)assembly - .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) - .Single(); - + var attribute = Assembly.GetExecutingAssembly().GetCustomAttribute(); return attribute.InformationalVersion; } } @@ -52,24 +45,17 @@ public virtual BuiltInFeatures Features /// /// Returns the SHA hash for the libgit2 library. /// - public virtual string LibGit2CommitSha - { - get { return RetrieveAbbrevShaFrom("libgit2_hash.txt"); } - } + public virtual string LibGit2CommitSha => RetrieveAbbrevShaFrom(AssemblyCommitIds.LibGit2CommitSha); /// /// Returns the SHA hash for the LibGit2Sharp library. /// - public virtual string LibGit2SharpCommitSha - { - get { return RetrieveAbbrevShaFrom("libgit2sharp_hash.txt"); } - } + public virtual string LibGit2SharpCommitSha => RetrieveAbbrevShaFrom(AssemblyCommitIds.LibGit2SharpCommitSha); - private string RetrieveAbbrevShaFrom(string name) + private string RetrieveAbbrevShaFrom(string sha) { - string sha = ReadContentFromResource(assembly, name) ?? "unknown"; - - return sha.Substring(0, 7); + var index = sha.Length > 7 ? 7 : sha.Length; + return sha.Substring(0, index); } /// @@ -77,7 +63,7 @@ private string RetrieveAbbrevShaFrom(string name) /// /// /// The format of the version number is as follows: - /// Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|x64 - features) + /// Major.Minor.Patch[-previewTag]+libgit2-{libgit2_abbrev_hash}.{LibGit2Sharp_hash} (arch - features) /// /// public override string ToString() @@ -90,21 +76,10 @@ private string RetrieveVersion() string features = Features.ToString(); return string.Format(CultureInfo.InvariantCulture, - "{0}-{1}-{2} ({3} - {4})", + "{0} ({1} - {2})", InformationalVersion, - LibGit2SharpCommitSha, - LibGit2CommitSha, Platform.ProcessorArchitecture, features); } - - private string ReadContentFromResource(Assembly assembly, string partialResourceName) - { - string name = string.Format(CultureInfo.InvariantCulture, "LibGit2Sharp.{0}", partialResourceName); - using (var sr = new StreamReader(assembly.GetManifestResourceStream(name))) - { - return sr.ReadLine(); - } - } } } diff --git a/LibGit2Sharp/Worktree.cs b/LibGit2Sharp/Worktree.cs new file mode 100644 index 000000000..ca7f5ef16 --- /dev/null +++ b/LibGit2Sharp/Worktree.cs @@ -0,0 +1,139 @@ +using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Handles; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// A Worktree. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class Worktree : IEquatable, IBelongToARepository + { + private static readonly LambdaEqualityHelper equalityHelper = + new LambdaEqualityHelper(x => x.Name); + + private readonly Repository parent; + //private readonly Repository worktree; + private readonly string name; + private WorktreeLock worktreeLock; + + /// + /// Needed for mocking purposes. + /// + protected Worktree() + { } + + internal Worktree(Repository repo, string name, WorktreeLock worktreeLock) + { + this.parent = repo; + this.name = name; + this.worktreeLock = worktreeLock; + } + + /// + /// + /// + /// + internal WorktreeHandle GetWorktreeHandle() + { + return Proxy.git_worktree_lookup(parent.Handle, name); + } + + /// + /// The name of the worktree. + /// + public virtual string Name { get { return name; } } + + /// + /// The Repository representation of the worktree + /// + public virtual Repository WorktreeRepository { get { return new Repository(GetWorktreeHandle()); } } + + /// + /// A flag indicating if the worktree is locked or not. + /// + public virtual bool IsLocked { get { return worktreeLock == null ? false : worktreeLock.IsLocked; } } + + /// + /// Gets the reason associated with the lock + /// + public virtual string LockReason { get { return worktreeLock == null ? null : worktreeLock.Reason; } } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. + public override bool Equals(object obj) + { + return Equals(obj as Worktree); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// True if the specified is equal to the current ; otherwise, false. + public bool Equals(Worktree other) + { + return equalityHelper.Equals(this, other); + } + + /// + /// Unlock the worktree + /// + public virtual void Unlock() + { + using (var handle = GetWorktreeHandle()) + { + Proxy.git_worktree_unlock(handle); + this.worktreeLock = Proxy.git_worktree_is_locked(handle); + } + } + + /// + /// Lock the worktree + /// + public virtual void Lock(string reason) + { + using (var handle = GetWorktreeHandle()) + { + Proxy.git_worktree_lock(handle, reason); + this.worktreeLock = Proxy.git_worktree_is_locked(handle); + } + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return equalityHelper.GetHashCode(this); + } + + /// + /// Returns the , a representation of the current . + /// + /// The that represents the current . + public override string ToString() + { + return Name; + } + + private string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "{0} => {1}", Name, worktreeLock); + } + } + + IRepository IBelongToARepository.Repository { get { return parent; } } + } +} diff --git a/LibGit2Sharp/WorktreeCollection.cs b/LibGit2Sharp/WorktreeCollection.cs new file mode 100644 index 000000000..d99e11d7a --- /dev/null +++ b/LibGit2Sharp/WorktreeCollection.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using LibGit2Sharp.Core; +using LibGit2Sharp.Core.Handles; + +namespace LibGit2Sharp +{ + /// + /// The collection of worktrees in a + /// + public class WorktreeCollection : IEnumerable + { + internal readonly Repository repo; + + /// + /// Needed for mocking purposes. + /// + protected WorktreeCollection() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The repo. + internal WorktreeCollection(Repository repo) + { + this.repo = repo; + } + + /// + /// Gets the with the specified name. + /// + public virtual Worktree this[string name] + { + get + { + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return Lookup(name, handle => new Worktree(repo, + name, + Proxy.git_worktree_is_locked(handle))); + } + } + + /// + /// Creates a worktree. + /// + /// The committish to checkout into the new worktree. + /// Name of the worktree. + /// Location of the worktree. + /// + public virtual Worktree Add(string committishOrBranchSpec, string name, string path, bool isLocked) + { + if (string.Equals(committishOrBranchSpec, name)) + { + // Proxy.git_worktree_add() creates a new branch of name = name, so if we want to checkout a given branch then the 'name' cannot be the same as the target branch + return null; + } + + var options = new git_worktree_add_options + { + version = 1, + locked = Convert.ToInt32(isLocked) + }; + + using (var handle = Proxy.git_worktree_add(repo.Handle, name, path, options)) + { + var worktree = new Worktree( + repo, + name, + Proxy.git_worktree_is_locked(handle)); + + // switch the worktree to the target branch + using (var repository = worktree.WorktreeRepository) + { + Commands.Checkout(repository, committishOrBranchSpec); + } + } + + return this[name]; + } + + /// + /// Creates a worktree. + /// + /// Name of the worktree. + /// Location of the worktree. + /// + public virtual Worktree Add(string name, string path, bool isLocked) + { + var options = new git_worktree_add_options + { + version = 1, + locked = Convert.ToInt32(isLocked) + }; + + using (var handle = Proxy.git_worktree_add(repo.Handle, name, path, options)) + { + return new Worktree( + repo, + name, + Proxy.git_worktree_is_locked(handle)); + } + } + + /// + /// + /// + /// + /// + public virtual bool Prune(Worktree worktree) + { + return Prune(worktree, false); + } + + /// + /// + /// + /// + /// + /// + public virtual bool Prune(Worktree worktree, bool ifLocked) + { + using (var handle = worktree.GetWorktreeHandle()) + { + git_worktree_prune_options options = new git_worktree_prune_options + { + version = 1, + // default + flags = GitWorktreePruneOptionFlags.GIT_WORKTREE_PRUNE_WORKING_TREE | GitWorktreePruneOptionFlags.GIT_WORKTREE_PRUNE_VALID + }; + + if (ifLocked) + { + options.flags |= GitWorktreePruneOptionFlags.GIT_WORKTREE_PRUNE_LOCKED; + } + + return Proxy.git_worktree_prune(handle, options); + } + } + + internal T Lookup(string name, Func selector, bool throwIfNotFound = false) + { + using (var handle = Proxy.git_worktree_lookup(repo.Handle, name)) + { + if (handle != null && Proxy.git_worktree_validate(handle)) + { + return selector(handle); + } + + if (throwIfNotFound) + { + throw new LibGit2SharpException("Worktree lookup failed for '{0}'.", name); + } + + return default(T); + } + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// An object that can be used to iterate through the collection. + public virtual IEnumerator GetEnumerator() + { + return Proxy.git_worktree_list(repo.Handle) + .Select(n => Lookup(n, handle => new Worktree(repo, n, Proxy.git_worktree_is_locked(handle)))) + .GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// An object that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Count = {0}", this.Count()); + } + } + } +} diff --git a/LibGit2Sharp/WorktreeLock.cs b/LibGit2Sharp/WorktreeLock.cs new file mode 100644 index 000000000..4ae5d799f --- /dev/null +++ b/LibGit2Sharp/WorktreeLock.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// Represents the lock state of a Worktree + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorktreeLock + { + /// + /// Creates a new instance of with default, unlocked, state + /// + public WorktreeLock() : this(false, null) + { + + } + + /// + /// Creates a new instance of + /// + /// the locked state + /// the reason given for the lock + public WorktreeLock(bool isLocked, string reason) + { + IsLocked = isLocked; + Reason = reason; + } + /// + /// Gets a flag indicating if the worktree is locked + /// + public virtual bool IsLocked { get; } + + /// + /// Gets the reason, if set, for the lock + /// + public virtual string Reason { get; } + + private string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "{0} => {1}", IsLocked, Reason); + } + } + } +} diff --git a/LibGit2Sharp/libgit2sharp_hash.txt b/LibGit2Sharp/libgit2sharp_hash.txt deleted file mode 100644 index 354664565..000000000 --- a/LibGit2Sharp/libgit2sharp_hash.txt +++ /dev/null @@ -1 +0,0 @@ -unknown diff --git a/LibGit2Sharp/packages.config b/LibGit2Sharp/packages.config deleted file mode 100644 index 72b9ea358..000000000 --- a/LibGit2Sharp/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/NativeLibraryLoadTestApp/TestApp.cs b/NativeLibraryLoadTestApp/TestApp.cs new file mode 100644 index 000000000..6a9f3ab60 --- /dev/null +++ b/NativeLibraryLoadTestApp/TestApp.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace LibGit2Sharp.Tests +{ + public class TestApp + { + [DllImport("kernel32")] + private static extern IntPtr GetModuleHandle(string path); + + [DllImport("kernel32")] + private static extern int GetModuleFileName(IntPtr handle, [Out] StringBuilder path, int size); + + static int Main(string[] args) + { + if (args.Length < 1 || args.Length > 2) + { + Console.Error.WriteLine("Usage: "); + return -1; + } + + var moduleName = args[0]; + var loadFromDirectory = args[1]; + var expectedPath = Path.Combine(loadFromDirectory, moduleName + ".dll"); + + GlobalSettings.NativeLibraryPath = loadFromDirectory; + var isValid = Repository.IsValid(Path.GetTempPath()); + + var capacity = ushort.MaxValue; + var moduleHandle = GetModuleHandle(moduleName); + var buffer = new StringBuilder(capacity); + int actualLength = GetModuleFileName(moduleHandle, buffer, capacity); + var actualPath = buffer.ToString(0, actualLength); + + if (expectedPath != actualPath) + { + Console.WriteLine(actualPath); + return 1; + } + + return 0; + } + } +} diff --git a/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj b/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj new file mode 100644 index 000000000..3bca18b34 --- /dev/null +++ b/NativeLibraryLoadTestApp/x64/NativeLibraryLoadTestApp.x64.csproj @@ -0,0 +1,17 @@ + + + + Exe + net472 + x64 + + + + + + + + + + + diff --git a/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj b/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj new file mode 100644 index 000000000..0596f203c --- /dev/null +++ b/NativeLibraryLoadTestApp/x86/NativeLibraryLoadTestApp.x86.csproj @@ -0,0 +1,17 @@ + + + + Exe + net472 + x86 + + + + + + + + + + + diff --git a/README.md b/README.md index e82907553..3aafdceb1 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,46 @@ # LibGit2Sharp -**LibGit2Sharp brings all the might and speed of [libgit2][libgit2], a native Git implementation, to the managed world of .NET and Mono.** +[![CI](https://github.com/libgit2/libgit2sharp/actions/workflows/ci.yml/badge.svg)](https://github.com/libgit2/libgit2sharp/actions/workflows/ci.yml) +[![NuGet version (LibGit2Sharp)](https://img.shields.io/nuget/v/LibGit2Sharp.svg)](https://www.nuget.org/packages/LibGit2Sharp/) - [libgit2]: http://libgit2.github.com/ - -## Prerequisites - - - **Windows:** .NET 4.0+ - - **Linux/Mac OS X:** Mono 3.6+ +**LibGit2Sharp brings all the might and speed of [libgit2](http://libgit2.github.com/), a native Git implementation, to the managed world of .NET** ## Online resources - - [NuGet package][nuget] (Requires NuGet 2.7+) - - [Source code][source] - - [nuget]: http://nuget.org/List/Packages/LibGit2Sharp - [source]: https://github.com/libgit2/libgit2sharp/ +- [NuGet package](http://nuget.org/List/Packages/LibGit2Sharp) +- [Source code](https://github.com/libgit2/libgit2sharp/) ## Troubleshooting and support - - Usage or programming related question? Post it on [StackOverflow][so] using the tag *libgit2sharp* - - Found a bug or missing a feature? Feed the [issue tracker][tracker] - - Announcements and related miscellanea through Twitter ([@libgit2sharp][twitter]) - - [so]: http://stackoverflow.com/questions/tagged/libgit2sharp - [tracker]: https://github.com/libgit2/libgit2sharp/issues - [twitter]: http://twitter.com/libgit2sharp - -## Current project status - -The CI builds are generously hosted and run on the [Travis][travis] and [AppVeyor][appveyor] infrastructures. - -| | Windows (x86/amd64) | Linux/Mac OS X | -| :------ | :------: | :------: | -| **master** | [![master win][master-win-badge]][master-win] | [![master nix][master-nix-badge]][master-nix] | -| **vNext** | [![vNext win][vNext-win-badge]][vNext-win] | [![vNext nix][vNext-nix-badge]][vNext-nix] | - -The security-oriented static code analysis is kindly run through the [Coverity][coverity] service. Code coverage is kindly run through [Coveralls.io][coveralls]. - -| | Static Analysis | Code Coverage | -|-------|-----------------|---------------| -| **vNext** | [![coverity][coverity-badge]][coverity-project] | [![coveralls][coveralls-badge]][coveralls-project] | - - - [travis]: https://travis-ci.org/ - [appveyor]: http://appveyor.com/ - [coverity]: https://scan.coverity.com/ - [coveralls]: https://coveralls.io/ - - [master-win-badge]: https://ci.appveyor.com/api/projects/status/8qxcoqdo9kp7x2w9/branch/master?svg=true - [master-win]: https://ci.appveyor.com/project/libgit2/libgit2sharp/branch/master - [master-nix-badge]: https://travis-ci.org/libgit2/libgit2sharp.svg?branch=master - [master-nix]: https://travis-ci.org/libgit2/libgit2sharp/branches - [vNext-win-badge]: https://ci.appveyor.com/api/projects/status/8qxcoqdo9kp7x2w9/branch/vNext?svg=true - [vNext-win]: https://ci.appveyor.com/project/libgit2/libgit2sharp/branch/vNext - [vNext-nix-badge]: https://travis-ci.org/libgit2/libgit2sharp.svg?branch=vNext - [vNext-nix]: https://travis-ci.org/libgit2/libgit2sharp/branches - - [coverity-project]: https://scan.coverity.com/projects/2088 - [coverity-badge]: https://scan.coverity.com/projects/2088/badge.svg - - [coveralls-project]: https://coveralls.io/r/libgit2/libgit2sharp?branch=vNext - [coveralls-badge]: https://coveralls.io/repos/libgit2/libgit2sharp/badge.svg?branch=vNext +- Usage or programming related question? Post it on [StackOverflow](http://stackoverflow.com/questions/tagged/libgit2sharp) using the tag *libgit2sharp* +- Found a bug or missing a feature? Feed the [issue tracker](https://github.com/libgit2/libgit2sharp/issues) +- Announcements and related miscellanea through Twitter ([@libgit2sharp](http://twitter.com/libgit2sharp)) ## Quick contributing guide - - Fork and clone locally - - Create a topic specific branch. Add some nice feature. Do not forget the tests ;-) - - Send a Pull Request to spread the fun! +- Fork and clone locally +- Create a topic specific branch. Add some nice feature. Do not forget the tests ;-) +- Send a Pull Request to spread the fun! -More thorough information available in the [wiki][wiki]. - - [wiki]: https://github.com/libgit2/libgit2sharp/wiki +More thorough information is available in the [wiki](https://github.com/libgit2/libgit2sharp/wiki). ## Optimizing unit testing -LibGit2Sharp strives to have comprehensive and robust unit test suite to insure the quality of the software and to assist new contributors and users who can use the tests as sample to jump start development. There are over one-thousand unit-tests for LibGit2Sharp, this number will only grow as functionality is added. -You can do a few things to optimize running unit-tests on Windows: +LibGit2Sharp strives to have a comprehensive and robust unit test suite to ensure the quality of the software and to assist new contributors and users, who can use the tests as examples to jump start development. There are over one thousand unit tests for LibGit2Sharp, and this number will only grow as functionality is added. + +You can do a few things to optimize running unit tests on Windows: 1. Set the `LibGit2TestPath` environment variable to a path in your development environment. - * If the unit-test framework cannot find the specified folder at runtime, it will fall back to the default location. + * If the unit test framework cannot find the specified folder at runtime, it will fall back to the default location. 2. Configure your anti-virus software to ignore the `LibGit2TestPath` path. 3. Install a RAM disk like [IMDisk](http://www.ltr-data.se/opencode.html/#ImDisk) and set `LibGit2TestPath` to use it. - * Use `imdisk.exe -a -s 512M -m X: -p "/fs:fat /q /v:ramdisk /y"` to create a RAM disk. This command requires elevated privileges and can be placed into a scheduled task or run manually before you begin unit-testing. + * Use `imdisk.exe -a -s 512M -m X: -p "/fs:fat /q /v:ramdisk /y"` to create a RAM disk. This command requires elevated privileges and can be placed into a scheduled task or run manually before you begin unit-testing. ## Authors - - **Code:** The LibGit2Sharp [contributors][committers] - - **Logo:** [Jason "blackant" Long][blackant] - - [committers]: https://github.com/libgit2/libgit2sharp/contributors - [blackant]: https://github.com/jasonlong +- **Code:** The LibGit2Sharp [contributors](https://github.com/libgit2/libgit2sharp/contributors) +- **Logo:** [Jason "blackant" Long](https://github.com/jasonlong) ## License -The MIT license (Refer to the [LICENSE.md][license] file) - - [license]: https://github.com/libgit2/libgit2sharp/blob/master/LICENSE.md +The MIT license (Refer to the [LICENSE.md](https://github.com/libgit2/libgit2sharp/blob/master/LICENSE.md) file) diff --git a/Targets/CodeGenerator.targets b/Targets/CodeGenerator.targets new file mode 100644 index 000000000..16eb8f05b --- /dev/null +++ b/Targets/CodeGenerator.targets @@ -0,0 +1,65 @@ + + + + + $(IntermediateOutputPath)SourceRevisionId.txt + $(IntermediateOutputPath)UniqueIdentifier.g.cs + $(IntermediateOutputPath)AssemblyCommitIds.g.cs + + + + + + + + + + + + + $(SourceRevisionId) + $([System.Guid]::NewGuid()) + + namespace LibGit2Sharp.Core + { + internal static class UniqueId + { + public const string UniqueIdentifier = "$(UniqueIdentifier)"%3b + } + } + + + + + + + + + + + + + + $(SourceRevisionId) + unknown + + namespace LibGit2Sharp + { + internal static class AssemblyCommitIds + { + public const string LibGit2CommitSha = "$(libgit2_hash)"%3b + public const string LibGit2SharpCommitSha = "$(LibGit2SharpCommitSha)"%3b + } + } + + + + + + + + + + + + diff --git a/Targets/GenerateNativeDllName.targets b/Targets/GenerateNativeDllName.targets new file mode 100644 index 000000000..c74dcd31e --- /dev/null +++ b/Targets/GenerateNativeDllName.targets @@ -0,0 +1,30 @@ + + + + + $(IntermediateOutputPath)NativeDllName.g.cs + + + + + + + namespace LibGit2Sharp.Core + { + internal static class NativeDllName + { + public const string Name = "$(libgit2_filename)"%3b + } + } + + + + + + + + + + + + diff --git a/TrimmingTestApp/Program.cs b/TrimmingTestApp/Program.cs new file mode 100644 index 000000000..e568c227b --- /dev/null +++ b/TrimmingTestApp/Program.cs @@ -0,0 +1,3 @@ +using LibGit2Sharp; + +_ = new Repository(); diff --git a/TrimmingTestApp/TrimmingTestApp.csproj b/TrimmingTestApp/TrimmingTestApp.csproj new file mode 100644 index 000000000..3c6d341f6 --- /dev/null +++ b/TrimmingTestApp/TrimmingTestApp.csproj @@ -0,0 +1,18 @@ + + + + net9.0 + Exe + enable + enable + true + true + true + + + + + + + + diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 693cd95ee..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,186 +0,0 @@ -version: '{build}' - -branches: - only: - - master - - /^maint.*/ - -skip_tags: true - -image: Visual Studio 2015 - -clone_folder: C:\projects\libgit2sharp - -environment: - coveralls_token: - secure: ixIsBslo9NheDb5lJknF58EYdgvZ0r3/L0ecRiXjfXmjHBLvoSU6/ZRwaMM+BAlG - coverity_token: - secure: nuzUT+HecXGIi3KaPd/1hgFEZJan/j6+oNbPV75JKjk= - coverity_email: - secure: eGVilNg1Yuq+Xj+SW8r3WCtjnzhoDV0sNJkma4NRq7A= - version : 0.24.0 - matrix: - - xunit_runner: xunit.console.x86.exe - Arch: 32 - publish_on_success: False - - xunit_runner: xunit.console.exe - Arch: 64 - publish_on_success: True - -matrix: - fast_finish: true - -install: -- ps: | - Write-Host "Commit being built = " -NoNewLine - Write-Host $Env:APPVEYOR_REPO_COMMIT -ForegroundColor "Green" - Write-Host "Current build version = " -NoNewLine - Write-Host $Env:VERSION -ForegroundColor "Green" - Write-Host "Target branch = " -NoNewLine - Write-Host $Env:APPVEYOR_REPO_BRANCH -ForegroundColor "Green" - Write-Host "Is a Pull Request = " -NoNewLine - Write-Host $($Env:APPVEYOR_PULL_REQUEST_NUMBER -ne $null) -ForegroundColor "Green" - - $CommitDate = [DateTime]::Parse($Env:APPVEYOR_REPO_COMMIT_TIMESTAMP) - $BuildDate = $CommitDate.ToUniversalTime().ToString("yyyyMMddHHmmss") - Write-Host "Merge commit UTC timestamp = " -NoNewLine - Write-Host $BuildDate -ForegroundColor "Green" - - $VersionSuffix = "" - If ($Env:APPVEYOR_REPO_BRANCH -ne "master") - { - $VersionSuffix = "-pre$BuildDate" - } - $Version = "$($Env:VERSION)$($VersionSuffix)" - $Env:ASSEMBLY_INFORMATIONAL_VERSION = $Version - Write-Host "Assembly informational version = " -NoNewLine - Write-Host $Env:ASSEMBLY_INFORMATIONAL_VERSION -ForegroundColor "Green" - - $Env:SHOULD_RUN_COVERITY_ANALYSIS = $($Env:APPVEYOR_SCHEDULED_BUILD -eq $True) - Write-Host "Should run Coverity analysis = " -NoNewLine - Write-Host $Env:SHOULD_RUN_COVERITY_ANALYSIS -ForegroundColor "Green" - - $Env:SHOULD_PACKAGE_NUGET_ARTIFACT = -not $Env:APPVEYOR_PULL_REQUEST_NUMBER -and -not $Env:APPVEYOR_SCHEDULED_BUILD - Write-Host "Should package Nuget artifact = " -NoNewLine - Write-Host $Env:SHOULD_PACKAGE_NUGET_ARTIFACT -ForegroundColor "Green" - - $Env:SHOULD_RUN_COVERALLS = $($Env:APPVEYOR_SCHEDULED_BUILD -eq $True) - Write-Host "Should run Coveralls = " -NoNewLine - Write-Host $Env:SHOULD_RUN_COVERALLS -ForegroundColor "Green" - - Write-Host "Should publish on success = " -NoNewLine - Write-Host $Env:publish_on_success -ForegroundColor "Green" - - If ($Env:SHOULD_PACKAGE_NUGET_ARTIFACT -eq $True) - { - cinst sourcelink -y - } - - If ($Env:SHOULD_RUN_COVERALLS -eq $True) - { - nuget install OpenCover -Version 4.6.166 -ExcludeVersion -OutputDirectory .\packages - nuget install coveralls.net -Version 0.6.0 -ExcludeVersion -OutputDirectory .\packages - } - - If ($Env:SHOULD_RUN_COVERITY_ANALYSIS -eq $True) - { - cinst curl -y - } - -assembly_info: - patch: true - file: LibGit2Sharp\Properties\AssemblyInfo.cs - assembly_version: '$(VERSION)' - assembly_file_version: '$(VERSION)' - assembly_informational_version: '$(ASSEMBLY_INFORMATIONAL_VERSION)' - -cache: - - packages - -before_build: -- ps: nuget restore "$Env:APPVEYOR_BUILD_FOLDER\LibGit2Sharp.sln" - -build_script: -- ps: | - & cov-build.exe --dir cov-int msbuild "$Env:APPVEYOR_BUILD_FOLDER\LibGit2Sharp.sln" ` - /verbosity:normal ` - /p:Configuration=Release ` - /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" ` - /property:ExtraDefine="LEAKS_IDENTIFYING" - -test_script: -- ps: | - If ($Env:SHOULD_RUN_COVERALLS -eq $True -and $Env:publish_on_success -eq $True) - { - .\packages\OpenCover\tools\OpenCover.Console.exe ` - -register:user ` - "-target:""$Env:APPVEYOR_BUILD_FOLDER\packages\xunit.runner.console.2.2.0\tools\$Env:xunit_runner""" ` - "-targetargs:""$Env:APPVEYOR_BUILD_FOLDER\LibGit2Sharp.Tests\bin\Release\LibGit2Sharp.Tests.dll"" -noshadow" ` - "-filter:+[LibGit2Sharp]* -[LibGit2Sharp.Tests]*" ` - -hideskipped:All ` - -output:opencoverCoverage.xml - } - ElseIf ($Env:SHOULD_RUN_COVERITY_ANALYSIS -eq $False) - { - & "$Env:APPVEYOR_BUILD_FOLDER\packages\xunit.runner.console.2.2.0\tools\$Env:xunit_runner" ` - "$Env:APPVEYOR_BUILD_FOLDER\LibGit2Sharp.Tests\bin\Release\LibGit2Sharp.Tests.dll" -noshadow - } - -after_test: -- ps: | - If ($Env:SHOULD_PACKAGE_NUGET_ARTIFACT -eq $True -and $Env:publish_on_success -eq $True) - { - & "$Env:APPVEYOR_BUILD_FOLDER\nuget.package\BuildNugetPackage.ps1" ` - -commitSha "$Env:APPVEYOR_REPO_COMMIT" ` - -postBuild { sourcelink index ` - -pr LibGit2Sharp.csproj ` - -pp Configuration Release ` - -nf Core\NativeDllName.cs ` - -nf Core\UniqueIdentifier.cs ` - -nf Properties\AssemblyInfo.cs ` - -r .. ` - -u 'https://raw.githubusercontent.com/libgit2/libgit2sharp/{0}/%var2%' } - - Add-Type -Path "$Env:APPVEYOR_BUILD_FOLDER\LibGit2Sharp\bin\Release\LibGit2Sharp.dll" - Write-Host "LibGit2Sharp version = $([LibGit2Sharp.GlobalSettings]::Version)" -ForegroundColor "Magenta" - - Get-ChildItem "$Env:APPVEYOR_BUILD_FOLDER\LibGit2sharp\*.nupkg" | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } - } - - If ($Env:SHOULD_RUN_COVERALLS -eq $True -and $Env:publish_on_success -eq $True) - { - Write-Host "Uploading code coverage result..." -ForegroundColor "Green" - - .\packages\coveralls.net\tools\csmacnz.Coveralls.exe ` - --opencover -i opencoverCoverage.xml ` - --repoToken $Env:coveralls_token ` - --useRelativePaths ` - --basePath "$Env:APPVEYOR_BUILD_FOLDER\"` - } - - If ($Env:SHOULD_RUN_COVERITY_ANALYSIS -eq $True -and $Env:publish_on_success -eq $True) - { - 7z a "$Env:APPVEYOR_BUILD_FOLDER\$Env:APPVEYOR_PROJECT_NAME.zip" "$Env:APPVEYOR_BUILD_FOLDER\cov-int\" - - # cf. http://stackoverflow.com/a/25045154/335418 - Remove-item alias:curl - - Write-Host "Uploading Coverity analysis result..." -ForegroundColor "Green" - - curl --silent --show-error ` - --output curl-out.txt ` - --form token="$Env:coverity_token" ` - --form email="$Env:coverity_email" ` - --form "file=@$Env:APPVEYOR_BUILD_FOLDER\$Env:APPVEYOR_PROJECT_NAME.zip" ` - --form version="$Env:APPVEYOR_REPO_COMMIT" ` - --form description="CI server scheduled build." ` - https://scan.coverity.com/builds?project=libgit2%2Flibgit2sharp - - cat .\curl-out.txt - } - -notifications: -- provider: Email - to: - - emeric.fermas@gmail.com - on_build_status_changed: true diff --git a/build.libgit2sharp.cmd b/build.libgit2sharp.cmd deleted file mode 100644 index 6cd7d72cf..000000000 --- a/build.libgit2sharp.cmd +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -REM Sample usages: -REM -REM Building and running tests -REM - build.libgit2sharp.cmd -REM -REM Building, running tests and embedding the libgit2sharp commit sha -REM - build.libgit2sharp.cmd "6a6eb81272876fd63555165beef44de2aaa78a14" -REM -REM Building and identifying potential leaks while running tests -REM - build.libgit2sharp.cmd "unknown" "LEAKS_IDENTIFYING" - - -SETLOCAL - -SET BASEDIR=%~dp0 -SET FrameworkVersion=v4.0.30319 -SET FrameworkDir=%SystemRoot%\Microsoft.NET\Framework - -if exist "%SystemRoot%\Microsoft.NET\Framework64" ( - SET FrameworkDir=%SystemRoot%\Microsoft.NET\Framework64 -) - -ECHO ON - -SET CommitSha=%~1 -SET ExtraDefine=%~2 - -"%BASEDIR%Lib/NuGet/NuGet.exe" restore "%BASEDIR%LibGit2Sharp.sln" -"%FrameworkDir%\%FrameworkVersion%\msbuild.exe" "%BASEDIR%CI\build.msbuild" /property:CommitSha=%CommitSha% /property:ExtraDefine="%ExtraDefine%" - -ENDLOCAL - -EXIT /B %ERRORLEVEL% diff --git a/build.libgit2sharp.sh b/build.libgit2sharp.sh deleted file mode 100755 index 52b30fad1..000000000 --- a/build.libgit2sharp.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -e - -EXTRADEFINE="$1" - -# Setting LD_LIBRARY_PATH to the current working directory is needed to run -# the tests successfully in linux. Without this, mono can't find libgit when -# the libgit2sharp assembly has been shadow copied. OS X includes the current -# working directory in its library search path, so it works without this value. -export LD_LIBRARY_PATH=. - -xbuild CI/build.msbuild /target:Deploy /property:ExtraDefine="$EXTRADEFINE" - -exit $? diff --git a/LibGit2Sharp/libgit2sharp.snk b/libgit2sharp.snk similarity index 100% rename from LibGit2Sharp/libgit2sharp.snk rename to libgit2sharp.snk diff --git a/nuget.config b/nuget.config new file mode 100644 index 000000000..35696f810 --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/nuget.package/BuildNugetPackage.ps1 b/nuget.package/BuildNugetPackage.ps1 deleted file mode 100644 index 77d9c9b88..000000000 --- a/nuget.package/BuildNugetPackage.ps1 +++ /dev/null @@ -1,87 +0,0 @@ -<# -.SYNOPSIS - Generates the NuGet packages (including the symbols). - A clean build is performed the packaging. -.PARAMETER commitSha - The LibGit2Sharp commit sha that contains the version of the source code being packaged. -#> - -Param( - [Parameter(Mandatory=$true)] - [string]$commitSha, - [scriptblock]$postBuild -) - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version Latest - -function Run-Command([scriptblock]$Command) { - $output = "" - - $exitCode = 0 - $global:lastexitcode = 0 - - & $Command - - if ($LastExitCode -ne 0) { - $exitCode = $LastExitCode - } elseif (!$?) { - $exitCode = 1 - } else { - return - } - - $error = "``$Command`` failed" - - if ($output) { - Write-Host -ForegroundColor "Red" $output - $error += ". See output above." - } - - Throw $error -} - -function Clean-OutputFolder($folder) { - - If (Test-Path $folder) { - Write-Host -ForegroundColor "Green" "Dropping `"$folder`" folder..." - - Run-Command { & Remove-Item -Recurse -Force "$folder" } - - Write-Host "Done." - } -} - -################# - -$root = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition -$projectPath = Join-Path $root "..\LibGit2Sharp" -$slnPath = Join-Path $projectPath "..\LibGit2Sharp.sln" - -Remove-Item (Join-Path $projectPath "*.nupkg") - -Clean-OutputFolder (Join-Path $projectPath "bin\") -Clean-OutputFolder (Join-Path $projectPath "obj\") - -# The nuspec file needs to be next to the csproj, so copy it there during the pack operation -Copy-Item (Join-Path $root "LibGit2Sharp.nuspec") $projectPath - -Push-Location $projectPath - -try { - Set-Content -Encoding ASCII $(Join-Path $projectPath "libgit2sharp_hash.txt") $commitSha - Run-Command { & "$(Join-Path $projectPath "..\Lib\NuGet\Nuget.exe")" Restore "$slnPath" } - Run-Command { & "MSBuild.exe" "$slnPath" "/verbosity:minimal" "/p:Configuration=Release" } - - If ($postBuild) { - Write-Host -ForegroundColor "Green" "Run post build script..." - Run-Command { & ($postBuild) } - } - - Run-Command { & "$(Join-Path $projectPath "..\Lib\NuGet\Nuget.exe")" Pack -Prop Configuration=Release } -} -finally { - Pop-Location - Remove-Item (Join-Path $projectPath "LibGit2Sharp.nuspec") - Set-Content -Encoding ASCII $(Join-Path $projectPath "libgit2sharp_hash.txt") "unknown" -} diff --git a/nuget.package/LibGit2Sharp.nuspec b/nuget.package/LibGit2Sharp.nuspec deleted file mode 100644 index 8d51c7581..000000000 --- a/nuget.package/LibGit2Sharp.nuspec +++ /dev/null @@ -1,22 +0,0 @@ - - - - $id$ - $version$ - $author$ - LibGit2Sharp maintainers - https://github.com/libgit2/libgit2sharp/raw/master/LICENSE.md - https://github.com/libgit2/libgit2sharp/ - false - $description$ - https://github.com/libgit2/libgit2sharp/blob/master/CHANGES.md#libgit2sharp-changes - https://github.com/libgit2/libgit2sharp/raw/master/square-logo.png - git libgit2 - - - - - - - -