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 6a6337edb..32e17b4d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ Thumbs.db
*_p.c
*.ncb
*.suo
+.vs/
*.sln.ide/
*.tlb
*.tlh
@@ -35,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 0335ab080..000000000
--- a/.nuget/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5ab00c105..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# Travis-CI Build for libgit2sharp
-# see travis-ci.org for details
-
-language: c
-
-os:
- - osx
- - linux
-
-before_install:
- - date -u
- - uname -a
- - export PATH=/opt/mono/bin:$PATH
- - env | sort
-
-# Make sure CMake and Mono are installed
-install: ./CI/travis.${TRAVIS_OS_NAME}.install.deps.sh
-
-# Build libgit2, LibGit2Sharp and run the tests
-script:
- - ./build.libgit2sharp.sh 'LEAKS_IDENTIFYING'
-
-# Only watch the development branch
-branches:
- only:
- - vNext
- - master
-
-# Notify of build changes
-notifications:
- email:
- - emeric.fermas@gmail.com
diff --git a/CHANGES.md b/CHANGES.md
index 3fbecda78..a00b598d7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,14 +1,209 @@
# LibGit2Sharp Changes
-**LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .Net and Mono.**
-
- - Source code:
- - NuGet package:
- - Issue tracker:
- - @libgit2sharp:
- - CI servers:
- - Windows (x86/amd64):
- - Linux/Mac OS X:
+## v0.31 - ([diff](https://github.com/libgit2/libgit2sharp/compare/0.30.0..0.31.0))
+
+### 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.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))
+
+This is the last release before a moving to .NET Core compatible library.
+
+It will be the last supported release with the prior architecture; as a
+result, this release is primarily bugfixes and does not include major new
+APIs.
+
+## v0.23 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.22..v0.23))
+
+### Additions
+
+ - Add `CherryPickCommit` and `RevertCommit` to `ObjectDatabase`.
+ - Add `IncludeIgnored` field to `SatusOptions`.
+ - Add `Commit.CreateBuffer` to write a commit object to a buffer and
+ `ObjectDatabase.CreateCommitWithSignature` to create commits which include a
+ signature.
+ - Add `Commit.ExtractSignature` to get a commit's signature.
+ - Add `ObjectDatabase.Write` to write arbitrary objects to the object db.
+ - Add `Commit.PrettifyMessage`
+
+
+### Changes
+
+ - The native libraries are now expected to be in the `lib` directory,
+ instead of `NativeBinaries` for improved mono compatibility. In
+ addition, the names of platform architectures now better reflect
+ the vendor naming (eg, `x86_64` instead of `amd64` on Linux).
+ - Deprecate the config paths in RepositoryOptions
+ - Deprecate the `QueryBy` overload with `FollowFilter`.
+ - Deprecate `Branch.Remote` in favour of `Branch.RemoteName`
+ - `Remote` no longer implement the equality operator.
+ - `Remote.Update` takes a remote name instead of an instance.
+ - `Fetch`, `Pull`, `Move`, `Remove`, `Stage` are now in a commands namespace to
+ indicate what they represent.
+
+## v0.22 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.21.1...v0.22))
+
+### Additions
+
+ - Add CustomHeaders in the push options (#1217)
+ - Expose the minimal diff algorithm (#1229)
+ - Expose Reset() with checkout options (#1219)
+ - Add a prettify option to history rewrite options (#1185)
+ - Add option to describe to only follow the first parent (#1190)
+ - Allow setting the config search path (#1123)
+ - Provide access to the remote's host HTTPS certificate (#1134)
+ - Add support for rebase (#964)
+ - ListReferences() now accepts a credentials provider (#1099)
+ - Introduce FileStatus.Conflicted and introduce staging of conflicts (#1062)
+ - Support streaming filters written in C# (#1030)
+ - Add support for the pre-push callback (#1061)
+ - Add support for listing remote references without a Repository instance (#1065)
+ - Add StashCollection.Apply() and .Pop() (#1068)
+ - Support retrieving a configuration for a repository without instantiating it (#1042)
+ - Implement 'log --follow'-like functionality (#963)
+ - Introduce in-memory merging via Repository.MergeCommits() (#990)
+ - Allow setting whether to prune during a fetch (#1258)
+
+### Changes
+
+ - Deprecate MergeConflictException in a backwards-compatible way (#1243)
+ - Improve type safety in the generic type for Diff.Compare() (#1180)
+ - Obsolete Repository.Commit(), NoteCollection.Add() and
+ NoteCollection.Remove() overloads which do not require a signature (#1173)
+ - BuildSignature() no longer tries to build a signature from the
+ environment if there is none configured (#1171)
+ - Rename the commit walker's Since to IncludeReachableFrom and Until to ExcludeReachableFrom (#1069)
+ - Rename MergeConflictException to CheckoutConflictException to more
+ accurately reflect what it means (#1059)
+ - Specify the diff algorithm instead of setting a boolean to use patience (#1043)
+ - Remove optional parameters (#1031)
+ - Move Repository.Reset(paths) into Index (#959)
+ - Move FindMergeBase() overloads to ObjectDatabase (#957)
+
+### Fixes
+
+ - ListReferences() is now able to handle symbolic references (#1132)
+ - Repository.IsValid() returns false on empty paths (#1156)
+ - The included version of libgit2 includes racy-git support
+ - Fix a racy NRE in the filters (#1113)
## v0.21.1 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.21...v0.21.1))
diff --git a/CI/build.msbuild b/CI/build.msbuild
deleted file mode 100644
index 97c503354..000000000
--- a/CI/build.msbuild
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
- Release
- $(MSBuildProjectDirectory)\..
- $(RootDir)\LibGit2Sharp.Tests\bin\$(Configuration)
- $(RootDir)\Build
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CI/travis.linux.install.deps.sh b/CI/travis.linux.install.deps.sh
deleted file mode 100755
index 365fc51a6..000000000
--- a/CI/travis.linux.install.deps.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-set -ev
-
-sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
-
-echo "deb http://download.mono-project.com/repo/debian wheezy/snapshots/3.12.0 main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
-echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
-
-sudo apt-get update
-sudo apt-get install mono-devel cmake
diff --git a/CI/travis.osx.install.deps.sh b/CI/travis.osx.install.deps.sh
deleted file mode 100755
index c6621b735..000000000
--- a/CI/travis.osx.install.deps.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-set -ev
-
-MONO_VER=3.6.0
-
-brew update
-which cmake || brew install cmake
-
-wget "http://download.mono-project.com/archive/${MONO_VER}/macos-10-x86/MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.pkg"
-sudo installer -pkg "MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.pkg" -target /
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..218cb2a28
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,58 @@
+# How to Contribute
+
+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
+submitting issues, writing documentation, to making code changes. We welcome it all.
+
+But first things first...
+
+* Make sure you have a [GitHub account](https://github.com/signup/free)
+* Submit a ticket for your issue, assuming one does not already exist.
+ * 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 `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
+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.
+* Make sure you have added the necessary tests for your changes.
+* Run _all_ the tests to assure nothing else was accidentally broken.
+
+## Submitting Changes
+
+* Push your changes to a topic branch in your fork of the repository.
+* Send a Pull Request targeting the master branch. Note what issue/issues your patch fixes.
+
+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.
+ 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/)
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 9ca66594f..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/AttributesFixture.cs b/LibGit2Sharp.Tests/AttributesFixture.cs
index c9c4eb712..3ac8326d3 100644
--- a/LibGit2Sharp.Tests/AttributesFixture.cs
+++ b/LibGit2Sharp.Tests/AttributesFixture.cs
@@ -9,8 +9,7 @@ public class AttributesFixture : BaseFixture
[Fact]
public void StagingHonorsTheAttributesFiles()
{
- string path = SandboxStandardTestRepo();
- using (var repo = new Repository(path))
+ using (var repo = new Repository(InitNewRepository()))
{
CreateAttributesFile(repo);
@@ -30,7 +29,7 @@ private static void AssertNormalization(IRepository repo, string filename, bool
Touch(repo.Info.WorkingDirectory, filename, sb.ToString());
- repo.Stage(filename);
+ Commands.Stage(repo, filename);
IndexEntry entry = repo.Index[filename];
Assert.NotNull(entry);
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 4c984bd34..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")]
@@ -63,10 +65,11 @@ public void CanGetBlobAsTextWithVariousEncodings(string encodingName, int expect
var bomPath = Touch(repo.Info.WorkingDirectory, bomFile, content, encoding);
Assert.Equal(expectedContentBytes, File.ReadAllBytes(bomPath).Length);
- repo.Stage(bomFile);
+ Commands.Stage(repo, bomFile);
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,16 +194,17 @@ 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());
}
- repo.Stage("small.txt");
+ Commands.Stage(repo, "small.txt");
IndexEntry entry = repo.Index["small.txt"];
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")))
@@ -202,7 +212,7 @@ public void CanStageAFileGeneratedFromABlobContentStream()
CopyStream(stream, file);
}
- repo.Stage("small.fromblob.txt");
+ Commands.Stage(repo, "small.fromblob.txt");
IndexEntry newentry = repo.Index["small.fromblob.txt"];
Assert.Equal("baae1fb3760a73481ced1fa03dc15614142c19ef", newentry.Id.Sha);
@@ -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 715defd87..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);
@@ -121,7 +133,7 @@ public void CanCreateBranchFromImplicitHead(string headCommitOrBranchSpec)
{
EnableRefLog(repo);
- repo.Checkout(headCommitOrBranchSpec);
+ Commands.Checkout(repo, headCommitOrBranchSpec);
const string name = "unit_test";
@@ -154,7 +166,7 @@ public void CanCreateBranchFromExplicitHead(string headCommitOrBranchSpec)
{
EnableRefLog(repo);
- repo.Checkout(headCommitOrBranchSpec);
+ Commands.Checkout(repo, headCommitOrBranchSpec);
const string name = "unit_test";
@@ -262,7 +274,7 @@ public void CreatingABranchTriggersTheCreationOfADirectReference()
Reference reference = repo.Refs[newBranch.CanonicalName];
Assert.NotNull(reference);
- Assert.IsType(typeof(DirectReference), reference);
+ Assert.IsType(reference);
}
}
@@ -394,7 +406,7 @@ public void CanResolveRemote()
using (var repo = new Repository(path))
{
Branch master = repo.Branches["master"];
- Assert.Equal(repo.Network.Remotes["origin"], master.Remote);
+ Assert.Equal("origin", master.RemoteName);
}
}
@@ -405,7 +417,7 @@ public void RemoteAndUpstreamBranchCanonicalNameForNonTrackingBranchIsNull()
using (var repo = new Repository(path))
{
Branch test = repo.Branches["i-do-numbers"];
- Assert.Null(test.Remote);
+ Assert.Null(test.RemoteName);
Assert.Null(test.UpstreamBranchCanonicalName);
}
}
@@ -418,7 +430,7 @@ public void QueryRemoteForLocalTrackingBranch()
using (var repo = new Repository(path))
{
Branch trackLocal = repo.Branches["track-local"];
- Assert.Null(trackLocal.Remote);
+ Assert.Null(trackLocal.RemoteName);
}
}
@@ -440,44 +452,42 @@ public void QueryRemoteForRemoteBranch()
using (var repo = new Repository(path))
{
var master = repo.Branches["origin/master"];
- Assert.Equal(repo.Network.Remotes["origin"], master.Remote);
+ Assert.Equal("origin", master.RemoteName);
}
}
[Fact]
public void QueryUnresolvableRemoteForRemoteBranch()
{
- var path = SandboxStandardTestRepo();
-
var fetchRefSpecs = new string[] { "+refs/heads/notfound/*:refs/remotes/origin/notfound/*" };
- using (var repo = InitIsolatedRepository(path))
+ var path = SandboxStandardTestRepo();
+ using (var repo = new Repository(path))
{
// Update the remote config such that the remote for a
// remote branch cannot be resolved
Remote remote = repo.Network.Remotes["origin"];
Assert.NotNull(remote);
- repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
+ repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = fetchRefSpecs);
Branch branch = repo.Branches["refs/remotes/origin/master"];
Assert.NotNull(branch);
Assert.True(branch.IsRemote);
- Assert.Null(branch.Remote);
+ Assert.Null(branch.RemoteName);
}
}
[Fact]
public void QueryAmbigousRemoteForRemoteBranch()
{
- var path = SandboxStandardTestRepo();
-
const string fetchRefSpec = "+refs/heads/*:refs/remotes/origin/*";
const string url = "http://github.com/libgit2/TestGitRepository";
- using (var repo = InitIsolatedRepository(path))
+ var path = SandboxStandardTestRepo();
+ using (var repo = new Repository(path))
{
// Add a second remote so that it is ambiguous which remote
// the remote-tracking branch tracks.
@@ -488,7 +498,7 @@ public void QueryAmbigousRemoteForRemoteBranch()
Assert.NotNull(branch);
Assert.True(branch.IsRemote);
- Assert.Null(branch.Remote);
+ Assert.Null(branch.RemoteName);
}
}
@@ -565,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);
@@ -734,7 +744,7 @@ public void CanSetTrackedBranch()
Assert.True(branch.IsTracking);
Assert.Equal(trackedBranch, branch.TrackedBranch);
- Assert.Equal(upstreamRemote, branch.Remote);
+ Assert.Equal("origin", branch.RemoteName);
}
}
@@ -752,7 +762,7 @@ public void SetTrackedBranchForUnreasolvableRemoteThrows()
// cannot be resolved.
Remote remote = repo.Network.Remotes["origin"];
Assert.NotNull(remote);
- repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs);
+ repo.Network.Remotes.Update("origin", r => r.FetchRefSpecs = fetchRefSpecs);
// Now attempt to update the tracked branch
Branch branch = repo.CreateBranch(testBranchName);
@@ -795,7 +805,7 @@ public void CanSetUpstreamBranch()
Assert.True(updatedBranch.IsTracking);
Assert.Equal(trackedBranch, updatedBranch.TrackedBranch);
Assert.Equal(upstreamBranchName, updatedBranch.UpstreamBranchCanonicalName);
- Assert.Equal(upstreamRemote, updatedBranch.Remote);
+ Assert.Equal(remoteName, updatedBranch.RemoteName);
}
}
@@ -822,7 +832,7 @@ public void CanSetLocalTrackedBranch()
// Branches that track the local remote do not have the "Remote" property set.
// Verify (through the configuration entry) that the local remote is set as expected.
- Assert.Null(branch.Remote);
+ Assert.Null(branch.RemoteName);
ConfigurationEntry remoteConfigEntry = repo.Config.Get("branch", testBranchName, "remote");
Assert.NotNull(remoteConfigEntry);
Assert.Equal(".", remoteConfigEntry.Value);
@@ -863,7 +873,7 @@ public void CanUnsetTrackedBranch()
// Verify this is no longer a tracking branch
Assert.False(branch.IsTracking);
- Assert.Null(branch.Remote);
+ Assert.Null(branch.RemoteName);
Assert.Null(branch.UpstreamBranchCanonicalName);
}
}
@@ -990,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);
@@ -1094,7 +1104,7 @@ public void DetachedHeadIsNotATrackingBranch()
repo.RemoveUntrackedFiles();
string headSha = repo.Head.Tip.Sha;
- repo.Checkout(headSha);
+ Commands.Checkout(repo, headSha);
Assert.False(repo.Head.IsTracking);
Assert.Null(repo.Head.TrackedBranch);
@@ -1115,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();
@@ -1125,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);
@@ -1136,11 +1146,10 @@ public void TrackedBranchExistsFromDefaultConfigInEmptyClone()
Assert.Null(repo.Head.TrackingDetails.BehindBy);
Assert.Null(repo.Head.TrackingDetails.CommonAncestor);
- Assert.NotNull(repo.Head.Remote);
- Assert.Equal("origin", repo.Head.Remote.Name);
+ Assert.Equal("origin", repo.Head.RemoteName);
Touch(repo.Info.WorkingDirectory, "a.txt", "a");
- repo.Stage("a.txt");
+ Commands.Stage(repo, "a.txt");
repo.Commit("A file", Constants.Signature, Constants.Signature);
Assert.NotNull(repo.Head.Tip);
@@ -1165,7 +1174,7 @@ public void RemoteBranchesDoNotTrackAnything()
foreach (var branch in branches)
{
Assert.True(branch.IsRemote);
- Assert.NotNull(branch.Remote);
+ Assert.NotNull(branch.RemoteName);
Assert.False(branch.IsTracking);
Assert.Null(branch.TrackedBranch);
diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs
index 80cb9727d..045e20e1f 100644
--- a/LibGit2Sharp.Tests/CheckoutFixture.cs
+++ b/LibGit2Sharp.Tests/CheckoutFixture.cs
@@ -34,7 +34,7 @@ public void CanCheckoutAnExistingBranch(string branchName)
Assert.NotNull(branch);
AssertBelongsToARepository(repo, branch);
- Branch test = repo.Checkout(branch);
+ Branch test = Commands.Checkout(repo, branch);
Assert.False(repo.Info.IsHeadDetached);
AssertBelongsToARepository(repo, test);
@@ -73,7 +73,7 @@ public void CanCheckoutAnExistingBranchByName(string branchName)
Assert.False(repo.RetrieveStatus().IsDirty);
- Branch test = repo.Checkout(branchName);
+ Branch test = Commands.Checkout(repo, branchName);
Assert.False(repo.Info.IsHeadDetached);
Assert.False(test.IsRemote);
@@ -97,7 +97,7 @@ public void CanCheckoutAnExistingBranchByName(string branchName)
[Theory]
[InlineData("6dcf9bf", true, "6dcf9bf")]
- [InlineData("refs/tags/lw", true, "refs/tags/lw")]
+ [InlineData("refs/tags/lw", true, "lw")]
[InlineData("HEAD~2", true, "HEAD~2")]
[InlineData("6dcf9bf", false, "6dcf9bf7541ee10456529833502442f385010c3d")]
[InlineData("refs/tags/lw", false, "e90810b8df3e80c413d903f631643c716887138d")]
@@ -118,7 +118,7 @@ public void CanCheckoutAnArbitraryCommit(string commitPointer, bool checkoutByCo
var commit = repo.Lookup(commitPointer);
AssertBelongsToARepository(repo, commit);
- Branch detachedHead = checkoutByCommitOrBranchSpec ? repo.Checkout(commitPointer) : repo.Checkout(commit);
+ Branch detachedHead = checkoutByCommitOrBranchSpec ? Commands.Checkout(repo, commitPointer) : Commands.Checkout(repo, commit);
Assert.Equal(repo.Head, detachedHead);
Assert.Equal(commit.Sha, detachedHead.Tip.Sha);
@@ -156,13 +156,13 @@ public void CheckoutAddsMissingFilesInWorkingDirectory()
// Remove the file in master branch
// Verify it exists after checking out otherBranch.
string fileFullPath = Path.Combine(repo.Info.WorkingDirectory, originalFilePath);
- repo.Remove(fileFullPath);
+ Commands.Remove(repo, fileFullPath);
repo.Commit("2nd commit", Constants.Signature, Constants.Signature);
// Checkout other_branch
Branch otherBranch = repo.Branches[otherBranchName];
Assert.NotNull(otherBranch);
- repo.Checkout(otherBranch);
+ Commands.Checkout(repo, otherBranch);
// Verify working directory is updated
Assert.False(repo.RetrieveStatus().IsDirty);
@@ -184,13 +184,13 @@ public void CheckoutRemovesExtraFilesInWorkingDirectory()
string newFileFullPath = Touch(
repo.Info.WorkingDirectory, "b.txt", "hello from master branch!\n");
- repo.Stage(newFileFullPath);
+ Commands.Stage(repo, newFileFullPath);
repo.Commit("2nd commit", Constants.Signature, Constants.Signature);
// Checkout other_branch
Branch otherBranch = repo.Branches[otherBranchName];
Assert.NotNull(otherBranch);
- repo.Checkout(otherBranch);
+ Commands.Checkout(repo, otherBranch);
// Verify working directory is updated
Assert.False(repo.RetrieveStatus().IsDirty);
@@ -212,13 +212,13 @@ public void CheckoutUpdatesModifiedFilesInWorkingDirectory()
string fullPath = Touch(
repo.Info.WorkingDirectory, originalFilePath, "Update : hello from master branch!\n");
- repo.Stage(fullPath);
+ Commands.Stage(repo, fullPath);
repo.Commit("2nd commit", Constants.Signature, Constants.Signature);
// Checkout other_branch
Branch otherBranch = repo.Branches[otherBranchName];
Assert.NotNull(otherBranch);
- repo.Checkout(otherBranch);
+ Commands.Checkout(repo, otherBranch);
// Verify working directory is updated
Assert.False(repo.RetrieveStatus().IsDirty);
@@ -254,22 +254,22 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges()
// Add change to master.
Touch(repo.Info.WorkingDirectory, originalFilePath, originalFileContent);
- repo.Stage(originalFilePath);
+ Commands.Stage(repo, originalFilePath);
repo.Commit("change in master", Constants.Signature, Constants.Signature);
// Checkout otherBranch.
- repo.Checkout(otherBranchName);
+ Commands.Checkout(repo, otherBranchName);
// Add change to otherBranch.
Touch(repo.Info.WorkingDirectory, originalFilePath, alternateFileContent);
- repo.Stage(originalFilePath);
+ Commands.Stage(repo, originalFilePath);
// Assert that normal checkout throws exception
// for the conflict.
- Assert.Throws(() => repo.Checkout(master.CanonicalName));
+ Assert.Throws(() => Commands.Checkout(repo, master.CanonicalName));
// Checkout with force option should succeed.
- repo.Checkout(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);
@@ -287,7 +287,7 @@ public void CheckingOutWithMergeConflictsThrows()
using (var repo = new Repository(repoPath))
{
Touch(repo.Info.WorkingDirectory, originalFilePath, "Hello\n");
- repo.Stage(originalFilePath);
+ Commands.Stage(repo, originalFilePath);
repo.Commit("Initial commit", Constants.Signature, Constants.Signature);
// Create 2nd branch
@@ -295,20 +295,20 @@ public void CheckingOutWithMergeConflictsThrows()
// Update file in main
Touch(repo.Info.WorkingDirectory, originalFilePath, "Hello from master!\n");
- repo.Stage(originalFilePath);
+ Commands.Stage(repo, originalFilePath);
repo.Commit("2nd commit", Constants.Signature, Constants.Signature);
// Checkout branch2
- repo.Checkout("branch2");
+ Commands.Checkout(repo, "branch2");
Touch(repo.Info.WorkingDirectory, originalFilePath, "Hello From branch2!\n");
// Assert that checking out master throws
// when there are unstaged commits
- Assert.Throws(() => repo.Checkout("master"));
+ Assert.Throws(() => Commands.Checkout(repo, "master"));
// And when there are staged commits
- repo.Stage(originalFilePath);
- Assert.Throws(() => repo.Checkout("master"));
+ Commands.Stage(repo, originalFilePath);
+ Assert.Throws(() => Commands.Checkout(repo, "master"));
}
}
@@ -322,7 +322,7 @@ public void CanCancelCheckoutThroughNotifyCallback()
const string relativePath = "a.txt";
Touch(repo.Info.WorkingDirectory, relativePath, "Hello\n");
- repo.Stage(relativePath);
+ Commands.Stage(repo, relativePath);
repo.Commit("Initial commit", Constants.Signature, Constants.Signature);
// Create 2nd branch
@@ -330,11 +330,11 @@ public void CanCancelCheckoutThroughNotifyCallback()
// Update file in main
Touch(repo.Info.WorkingDirectory, relativePath, "Hello from master!\n");
- repo.Stage(relativePath);
+ Commands.Stage(repo, relativePath);
repo.Commit("2nd commit", Constants.Signature, Constants.Signature);
// Checkout branch2
- repo.Checkout("branch2");
+ Commands.Checkout(repo, "branch2");
// Update the context of a.txt - a.txt will then conflict between branch2 and master.
Touch(repo.Info.WorkingDirectory, relativePath, "Hello From branch2!\n");
@@ -348,7 +348,7 @@ public void CanCancelCheckoutThroughNotifyCallback()
CheckoutNotifyFlags = CheckoutNotifyFlags.Conflict,
};
- Assert.Throws(() => repo.Checkout("master", options));
+ Assert.Throws(() => Commands.Checkout(repo, "master", options));
Assert.Equal(relativePath, conflictPath);
}
}
@@ -359,8 +359,8 @@ public void CheckingOutInABareRepoThrows()
string path = SandboxBareTestRepo();
using (var repo = new Repository(path))
{
- Assert.Throws(() => repo.Checkout(repo.Branches["refs/heads/test"]));
- Assert.Throws(() => repo.Checkout("refs/heads/test"));
+ Assert.Throws(() => Commands.Checkout(repo, repo.Branches["refs/heads/test"]));
+ Assert.Throws(() => Commands.Checkout(repo, "refs/heads/test"));
}
}
@@ -373,7 +373,7 @@ public void CheckingOutAgainstAnUnbornBranchThrows()
{
Assert.True(repo.Info.IsHeadUnborn);
- Assert.Throws(() => repo.Checkout(repo.Head));
+ Assert.Throws(() => Commands.Checkout(repo, repo.Head));
}
}
@@ -383,7 +383,7 @@ public void CheckingOutANonExistingBranchThrows()
string path = SandboxBareTestRepo();
using (var repo = new Repository(path))
{
- Assert.Throws(() => repo.Checkout("i-do-not-exist"));
+ Assert.Throws(() => Commands.Checkout(repo, "i-do-not-exist"));
}
}
@@ -393,9 +393,9 @@ public void CheckingOutABranchWithBadParamsThrows()
string path = SandboxBareTestRepo();
using (var repo = new Repository(path))
{
- Assert.Throws(() => repo.Checkout(string.Empty));
- Assert.Throws(() => repo.Checkout(default(Branch)));
- Assert.Throws(() => repo.Checkout(default(string)));
+ Assert.Throws(() => Commands.Checkout(repo, string.Empty));
+ Assert.Throws(() => Commands.Checkout(repo, default(Branch)));
+ Assert.Throws(() => Commands.Checkout(repo, default(string)));
}
}
@@ -410,8 +410,8 @@ public void CheckingOutThroughBranchCallsCheckoutProgress()
bool wasCalled = false;
Branch branch = repo.Branches[otherBranchName];
- repo.Checkout(branch,
- new CheckoutOptions { OnCheckoutProgress = (path, completed, total) => wasCalled = true});
+ Commands.Checkout(repo, branch,
+ new CheckoutOptions { OnCheckoutProgress = (path, completed, total) => wasCalled = true });
Assert.True(wasCalled);
}
@@ -427,7 +427,7 @@ public void CheckingOutThroughRepositoryCallsCheckoutProgress()
PopulateBasicRepository(repo);
bool wasCalled = false;
- repo.Checkout(otherBranchName, new CheckoutOptions() { OnCheckoutProgress = (path, completed, total) => wasCalled = true});
+ Commands.Checkout(repo, otherBranchName, new CheckoutOptions() { OnCheckoutProgress = (path, completed, total) => wasCalled = true });
Assert.True(wasCalled);
}
@@ -453,13 +453,13 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri
const string relativePathUpdated = "updated.txt";
Touch(repo.Info.WorkingDirectory, relativePathUpdated, "updated file text A");
- repo.Stage(relativePathUpdated);
+ Commands.Stage(repo, relativePathUpdated);
repo.Commit("Commit initial update file", Constants.Signature, Constants.Signature);
// Create conflicting change
const string relativePathConflict = "conflict.txt";
Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text A");
- repo.Stage(relativePathConflict);
+ Commands.Stage(repo, relativePathConflict);
repo.Commit("Initial commit of conflict.txt and update.txt", Constants.Signature, Constants.Signature);
// Create another branch
@@ -467,25 +467,25 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri
// Make an edit to conflict.txt and update.txt
Touch(repo.Info.WorkingDirectory, relativePathUpdated, "updated file text BB");
- repo.Stage(relativePathUpdated);
+ Commands.Stage(repo, relativePathUpdated);
Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text BB");
- repo.Stage(relativePathConflict);
+ Commands.Stage(repo, relativePathConflict);
repo.Commit("2nd commit of conflict.txt and update.txt on master branch", Constants.Signature, Constants.Signature);
// Checkout other branch
- repo.Checkout("newbranch");
+ Commands.Checkout(repo, "newbranch");
// Make alternate edits to conflict.txt and update.txt
Touch(repo.Info.WorkingDirectory, relativePathUpdated, "updated file text CCC");
- repo.Stage(relativePathUpdated);
+ Commands.Stage(repo, relativePathUpdated);
Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text CCC");
- repo.Stage(relativePathConflict);
+ Commands.Stage(repo, relativePathConflict);
repo.Commit("2nd commit of conflict.txt and update.txt on newbranch", Constants.Signature, Constants.Signature);
// make conflicting change to conflict.txt
Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text DDDD");
- repo.Stage(relativePathConflict);
+ Commands.Stage(repo, relativePathConflict);
// Create ignored change
string relativePathIgnore = Path.Combine("bin", "ignored.txt");
@@ -505,7 +505,7 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri
CheckoutNotifyFlags = notifyFlags,
};
- Assert.Throws(() => repo.Checkout("master", options));
+ Assert.Throws(() => Commands.Checkout(repo, "master", options));
Assert.True(wasCalled);
Assert.Equal(expectedNotificationPath, actualNotificationPath);
@@ -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));
- repo.Checkout(otherBranchName);
+ 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));
- repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
+ 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));
- repo.Checkout(otherBranchName);
+ 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));
}
}
@@ -596,16 +596,16 @@ public void CheckoutRetainsStagedChanges()
// Generate a staged change.
string fullPathFileA = Touch(repo.Info.WorkingDirectory, originalFilePath, alternateFileContent);
- repo.Stage(fullPathFileA);
+ 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));
- repo.Checkout(otherBranchName);
+ 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,11 +625,11 @@ 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));
- repo.Checkout(otherBranchName);
+ Commands.Checkout(repo, otherBranchName);
// Verify that the ignored file still exists.
Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath));
@@ -652,11 +652,11 @@ 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));
- repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
+ Commands.Checkout(repo, otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
// Verify that the ignored file still exists.
Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath));
@@ -680,12 +680,12 @@ public void CheckoutBranchSnapshot()
// Add commit to master
string fullPath = Touch(repo.Info.WorkingDirectory, originalFilePath, "Update : hello from master branch!\n");
- repo.Stage(fullPath);
+ Commands.Stage(repo, fullPath);
repo.Commit("2nd commit", Constants.Signature, Constants.Signature);
Assert.False(repo.Info.IsHeadDetached);
- repo.Checkout(initial);
+ Commands.Checkout(repo, initial);
// Head should point at initial commit.
Assert.Equal(repo.Head.Tip, initialCommit);
@@ -712,7 +712,7 @@ public void CheckingOutRemoteBranchResultsInDetachedHead(string remoteBranchSpec
// Set the working directory to the current head
ResetAndCleanWorkingDirectory(repo);
- repo.Checkout(remoteBranchSpec);
+ Commands.Checkout(repo, remoteBranchSpec);
// Verify that HEAD is detached.
Assert.Equal(repo.Refs["HEAD"].TargetIdentifier, repo.Branches["origin/master"].Tip.Sha);
@@ -733,7 +733,7 @@ public void CheckingOutABranchDoesNotAlterBinaryFiles()
// The blob actually exists in the object database with the correct Sha
Assert.Equal(expectedSha, repo.Lookup(expectedSha).Sha);
- repo.Checkout("refs/heads/logo", new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
+ Commands.Checkout(repo, "refs/heads/logo", new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
// The Index has been updated as well with the blob
Assert.Equal(expectedSha, repo.Index["square-logo.png"].Id.Sha);
@@ -748,7 +748,7 @@ public void CheckingOutABranchDoesNotAlterBinaryFiles()
[Theory]
[InlineData("a447ba2ca8")]
- [InlineData("refs/tags/lw")]
+ [InlineData("lw")]
[InlineData("e90810^{}")]
public void CheckoutFromDetachedHead(string commitPointer)
{
@@ -761,9 +761,9 @@ public void CheckoutFromDetachedHead(string commitPointer)
var commitSha = repo.Lookup(commitPointer).Sha;
- Branch initialHead = repo.Checkout("6dcf9bf");
+ Branch initialHead = Commands.Checkout(repo, "6dcf9bf");
- repo.Checkout(commitPointer);
+ Commands.Checkout(repo, commitPointer);
// Assert reflog entry is created
var reflogEntry = repo.Refs.Log(repo.Refs.Head).First();
@@ -779,19 +779,19 @@ 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);
Assert.False(repo.RetrieveStatus().IsDirty);
- Branch initialHead = repo.Checkout("6dcf9bf");
+ Branch initialHead = Commands.Checkout(repo, "6dcf9bf");
Assert.True(repo.Info.IsHeadDetached);
var before = DateTimeOffset.Now.TruncateMilliseconds();
- Branch newHead = repo.Checkout(repo.Branches["master"]);
+ Branch newHead = Commands.Checkout(repo, repo.Branches["master"]);
// Assert reflog entry is created
AssertRefLogEntry(repo, "HEAD",
@@ -812,10 +812,10 @@ public void CheckoutBranchByShortNameAttachesTheHead(string shortBranchName, str
ResetAndCleanWorkingDirectory(repo);
Assert.False(repo.RetrieveStatus().IsDirty);
- repo.Checkout("6dcf9bf");
+ Commands.Checkout(repo, "6dcf9bf");
Assert.True(repo.Info.IsHeadDetached);
- var branch = repo.Checkout(shortBranchName);
+ var branch = Commands.Checkout(repo, shortBranchName);
Assert.False(repo.Info.IsHeadDetached);
Assert.Equal(referenceName, repo.Head.CanonicalName);
@@ -833,11 +833,11 @@ public void CheckoutPreviousCheckedOutBranch()
ResetAndCleanWorkingDirectory(repo);
Assert.False(repo.RetrieveStatus().IsDirty);
- Branch previousHead = repo.Checkout("i-do-numbers");
- repo.Checkout("diff-test-cases");
+ Branch previousHead = Commands.Checkout(repo, "i-do-numbers");
+ Commands.Checkout(repo, "diff-test-cases");
//Go back to previous branch checked out
- var branch = repo.Checkout(@"@{-1}");
+ var branch = Commands.Checkout(repo, @"@{-1}");
Assert.False(repo.Info.IsHeadDetached);
Assert.Equal(previousHead.CanonicalName, repo.Head.CanonicalName);
@@ -860,14 +860,14 @@ public void CheckoutCurrentReference()
var reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count();
// Checkout branch
- repo.Checkout(master);
+ Commands.Checkout(repo, master);
Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count());
var before = DateTimeOffset.Now.TruncateMilliseconds();
// Checkout in detached mode
- repo.Checkout(master.Tip.Sha);
+ Commands.Checkout(repo, master.Tip.Sha);
Assert.True(repo.Info.IsHeadDetached);
AssertRefLogEntry(repo, "HEAD",
@@ -877,19 +877,19 @@ public void CheckoutCurrentReference()
// Checkout detached "HEAD" => nothing should happen
reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count();
- repo.Checkout(repo.Head);
+ Commands.Checkout(repo, repo.Head);
Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count());
// Checkout attached "HEAD" => nothing should happen
- repo.Checkout("master");
+ Commands.Checkout(repo, "master");
reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count();
- repo.Checkout(repo.Head);
+ Commands.Checkout(repo, repo.Head);
Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count());
- repo.Checkout("HEAD");
+ Commands.Checkout(repo, "HEAD");
Assert.Equal(reflogEntriesCount, repo.Refs.Log(repo.Refs.Head).Count());
}
@@ -901,7 +901,7 @@ public void CheckoutLowerCasedHeadThrows()
string path = SandboxStandardTestRepo();
using (var repo = new Repository(path))
{
- Assert.Throws(() => repo.Checkout("head"));
+ Assert.Throws(() => Commands.Checkout(repo, "head"));
}
}
@@ -913,10 +913,10 @@ public void CanCheckoutAttachedHead()
{
Assert.False(repo.Info.IsHeadDetached);
- repo.Checkout(repo.Head);
+ Commands.Checkout(repo, repo.Head);
Assert.False(repo.Info.IsHeadDetached);
- repo.Checkout("HEAD");
+ Commands.Checkout(repo, "HEAD");
Assert.False(repo.Info.IsHeadDetached);
}
}
@@ -927,14 +927,14 @@ public void CanCheckoutDetachedHead()
string path = SandboxStandardTestRepo();
using (var repo = new Repository(path))
{
- repo.Checkout(repo.Head.Tip.Sha);
+ Commands.Checkout(repo, repo.Head.Tip.Sha);
Assert.True(repo.Info.IsHeadDetached);
- repo.Checkout(repo.Head);
+ Commands.Checkout(repo, repo.Head);
Assert.True(repo.Info.IsHeadDetached);
- repo.Checkout("HEAD");
+ Commands.Checkout(repo, "HEAD");
Assert.True(repo.Info.IsHeadDetached);
}
}
@@ -952,13 +952,13 @@ public void CanCheckoutPath(string originalBranch, string checkoutFrom, string p
// Set the working directory to the current head
ResetAndCleanWorkingDirectory(repo);
- repo.Checkout(originalBranch);
+ Commands.Checkout(repo, originalBranch);
Assert.False(repo.RetrieveStatus().IsDirty);
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.
@@ -1038,10 +1083,10 @@ private void PopulateBasicRepository(IRepository repo)
{
// Generate a .gitignore file.
string gitIgnoreFilePath = Touch(repo.Info.WorkingDirectory, ".gitignore", "bin");
- repo.Stage(gitIgnoreFilePath);
+ Commands.Stage(repo, gitIgnoreFilePath);
string fullPathFileA = Touch(repo.Info.WorkingDirectory, originalFilePath, originalFileContent);
- repo.Stage(fullPathFileA);
+ Commands.Stage(repo, fullPathFileA);
repo.Commit("Initial commit", Constants.Signature, Constants.Signature);
diff --git a/LibGit2Sharp.Tests/CherryPickFixture.cs b/LibGit2Sharp.Tests/CherryPickFixture.cs
index d9828e266..f4a383fef 100644
--- a/LibGit2Sharp.Tests/CherryPickFixture.cs
+++ b/LibGit2Sharp.Tests/CherryPickFixture.cs
@@ -19,7 +19,7 @@ public void CanCherryPick(bool fromDetachedHead)
{
if (fromDetachedHead)
{
- repo.Checkout(repo.Head.Tip.Id.Sha);
+ Commands.Checkout(repo, repo.Head.Tip.Id.Sha);
}
Commit commitToMerge = repo.Branches["fast_forward"].Tip;
@@ -46,7 +46,7 @@ public void CherryPickWithConflictDoesNotCommit()
using (var repo = new Repository(path))
{
var firstBranch = repo.CreateBranch("FirstBranch");
- repo.Checkout(firstBranch);
+ Commands.Checkout(repo, firstBranch);
// Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
AddFileCommitToRepo(repo, sharedBranchFileName);
@@ -56,7 +56,7 @@ public void CherryPickWithConflictDoesNotCommit()
AddFileCommitToRepo(repo, firstBranchFileName);
AddFileCommitToRepo(repo, sharedBranchFileName, "The first branches comment"); // Change file in first branch
- repo.Checkout(secondBranch);
+ Commands.Checkout(repo, secondBranch);
// Commit with ONE new file to second branch (FirstBranch and SecondBranch now point to separate commits that both have the same parent commit).
AddFileCommitToRepo(repo, secondBranchFileName);
AddFileCommitToRepo(repo, sharedBranchFileName, "The second branches comment"); // Change file in second branch
@@ -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));
@@ -126,11 +126,97 @@ public void CanSpecifyConflictFileStrategy(CheckoutFileConflictStrategy conflict
}
}
+ [Fact]
+ public void CanCherryPickCommit()
+ {
+ string path = SandboxMergeTestRepo();
+ using (var repo = new Repository(path))
+ {
+ var ours = repo.Head.Tip;
+
+ Commit commitToMerge = repo.Branches["fast_forward"].Tip;
+
+ var result = repo.ObjectDatabase.CherryPickCommit(commitToMerge, ours, 0, null);
+
+ Assert.Equal(MergeTreeStatus.Succeeded, result.Status);
+ Assert.Empty(result.Conflicts);
+ }
+ }
+
+ [Fact]
+ public void CherryPickWithConflictsReturnsConflicts()
+ {
+ const string conflictBranchName = "conflicts";
+
+ string path = SandboxMergeTestRepo();
+ using (var repo = new Repository(path))
+ {
+ Branch branch = repo.Branches[conflictBranchName];
+ Assert.NotNull(branch);
+
+ var result = repo.ObjectDatabase.CherryPickCommit(branch.Tip, repo.Head.Tip, 0, null);
+
+ Assert.Equal(MergeTreeStatus.Conflicts, result.Status);
+ Assert.NotEmpty(result.Conflicts);
+
+ }
+ }
+
+ [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);
- repository.Stage(filename);
+ Commands.Stage(repository, filename);
return repository.Commit("New commit", Constants.Signature, Constants.Signature);
}
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 82148246d..e99ca918f 100644
--- a/LibGit2Sharp.Tests/CommitFixture.cs
+++ b/LibGit2Sharp.Tests/CommitFixture.cs
@@ -2,10 +2,9 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using LibGit2Sharp.Core;
+using System.Text;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;
-using Xunit.Extensions;
namespace LibGit2Sharp.Tests
{
@@ -34,11 +33,11 @@ public void CanCorrectlyCountCommitsWhenSwitchingToAnotherBranch()
repo.Reset(ResetMode.Hard);
repo.RemoveUntrackedFiles();
- repo.Checkout("test");
+ Commands.Checkout(repo, "test");
Assert.Equal(2, repo.Commits.Count());
Assert.Equal("e90810b8df3e80c413d903f631643c716887138d", repo.Commits.First().Id.Sha);
- repo.Checkout("master");
+ Commands.Checkout(repo, "master");
Assert.Equal(9, repo.Commits.Count());
Assert.Equal("32eab9cb1f450b5fe7ab663462b77d7f4b703344", repo.Commits.First().Id.Sha);
}
@@ -69,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());
}
@@ -149,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++;
}
}
@@ -169,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);
@@ -203,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);
}
}
@@ -215,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++;
}
}
@@ -235,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);
@@ -274,7 +273,7 @@ public void CanEnumerateFromDetachedHead()
repoClone.RemoveUntrackedFiles();
string headSha = repoClone.Head.Tip.Sha;
- repoClone.Checkout(headSha);
+ Commands.Checkout(repoClone, headSha);
AssertEnumerationOfCommitsInRepo(repoClone,
repo => new CommitFilter { IncludeReachableFrom = repo.Head },
@@ -330,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",
@@ -388,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",
@@ -405,7 +407,7 @@ public void CanEnumerateCommitsFromATagWhichPointsToABlob()
{
AssertEnumerationOfCommits(
repo => new CommitFilter { IncludeReachableFrom = repo.Tags["point_to_blob"] },
- new string[] { });
+ Array.Empty());
}
[Fact]
@@ -420,7 +422,7 @@ public void CanEnumerateCommitsFromATagWhichPointsToATree()
AssertEnumerationOfCommitsInRepo(repo,
r => new CommitFilter { IncludeReachableFrom = tag },
- new string[] { });
+ Array.Empty());
}
}
@@ -474,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);
}
}
@@ -542,21 +544,20 @@ public void DirectlyAccessingAnUnknownTreeEntryOfTheCommitReturnsNull()
public void CanCommitWithSignatureFromConfig()
{
string repoPath = InitNewRepository();
- string configPath = CreateConfigurationWithDummyUser(Constants.Identity);
- var options = new RepositoryOptions { GlobalConfigurationLocation = configPath };
- using (var repo = new Repository(repoPath, options))
+ using (var repo = new Repository(repoPath))
{
+ CreateConfigurationWithDummyUser(repo, Constants.Identity);
string dir = repo.Info.Path;
Assert.True(Path.IsPathRooted(dir));
Assert.True(Directory.Exists(dir));
const string relativeFilepath = "new.txt";
string filePath = Touch(repo.Info.WorkingDirectory, relativeFilepath, "null");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
File.AppendAllText(filePath, "token\n");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
Assert.Null(repo.Head[relativeFilepath]);
@@ -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();
@@ -615,7 +616,7 @@ public void CommitCleansUpMergeMetadata()
const string relativeFilepath = "new.txt";
Touch(repo.Info.WorkingDirectory, relativeFilepath, "this is a new file");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
string mergeHeadPath = Touch(repo.Info.Path, "MERGE_HEAD", "abcdefabcdefabcdefabcdefabcdefabcdefabcd");
string mergeMsgPath = Touch(repo.Info.Path, "MERGE_MSG", "This is a dummy merge.\n");
@@ -652,9 +653,9 @@ public void CanCommitALittleBit()
const string relativeFilepath = "new.txt";
string filePath = Touch(repo.Info.WorkingDirectory, relativeFilepath, "null");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
File.AppendAllText(filePath, "token\n");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
Assert.Null(repo.Head[relativeFilepath]);
@@ -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,11 +694,11 @@ 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");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
var author2 = new Signature(author.Name, author.Email, author.When.AddSeconds(5));
Commit commit2 = repo.Commit("Are you trying to fork me?", author2, author2);
@@ -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
@@ -709,19 +714,19 @@ public void CanCommitALittleBit()
Assert.Equal(reflogEntry.To, repo.Refs.Log("HEAD").First().From);
Branch firstCommitBranch = repo.CreateBranch("davidfowl-rules", commit);
- repo.Checkout(firstCommitBranch);
+ Commands.Checkout(repo, firstCommitBranch);
File.WriteAllText(filePath, "davidfowl commits!\n");
var author3 = new Signature("David Fowler", "david.fowler@microsoft.com", author.When.AddSeconds(2));
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
Commit commit3 = repo.Commit("I'm going to branch you backwards in time!", author3, author3);
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");
@@ -740,7 +745,7 @@ private static void AddCommitToRepo(string path)
{
const string relativeFilepath = "test.txt";
Touch(repo.Info.WorkingDirectory, relativeFilepath, "test\n");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
var author = new Signature("nulltoken", "emeric.fermas@gmail.com", DateTimeOffset.Parse("Wed, Dec 14 2011 08:29:03 +0100"));
repo.Commit("Initial commit", author, author);
@@ -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);
}
@@ -826,7 +831,7 @@ private static void CreateAndStageANewFile(IRepository repo)
{
string relativeFilepath = string.Format("new-file-{0}.txt", Path.GetRandomFileName());
Touch(repo.Info.WorkingDirectory, relativeFilepath, "brand new content\n");
- repo.Stage(relativeFilepath);
+ Commands.Stage(repo, relativeFilepath);
}
private static void AssertCommitHasBeenAmended(IRepository repo, Commit amendedCommit, Commit 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);
@@ -915,10 +920,10 @@ public void CanCommitOnOrphanedBranch()
const string relativeFilepath = "test.txt";
Touch(repo.Info.WorkingDirectory, relativeFilepath, "test\n");
- repo.Stage(relativeFilepath);
+ 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);
}
}
@@ -1031,20 +1036,143 @@ public void CanNotAmendACommitInAWayThatWouldLeadTheNewCommitToBecomeEmpty()
using (var repo = new Repository(repoPath))
{
Touch(repo.Info.WorkingDirectory, "test.txt", "test\n");
- repo.Stage("test.txt");
+ Commands.Stage(repo, "test.txt");
repo.Commit("Initial commit", Constants.Signature, Constants.Signature);
Touch(repo.Info.WorkingDirectory, "new.txt", "content\n");
- repo.Stage("new.txt");
+ Commands.Stage(repo, "new.txt");
repo.Commit("One commit", Constants.Signature, Constants.Signature);
- repo.Remove("new.txt");
+ Commands.Remove(repo, "new.txt");
Assert.Throws(() => repo.Commit("Oops", Constants.Signature, Constants.Signature,
new CommitOptions { AmendPreviousCommit = true }));
}
}
+
+ [Fact]
+ public void CanPrettifyAMessage()
+ {
+ string input = "# Comment\nA line that will remain\n# And another character\n\n\n";
+ string expected = "A line that will remain\n";
+
+ Assert.Equal(expected, Commit.PrettifyMessage(input, '#'));
+ Assert.Equal(expected, Commit.PrettifyMessage(input.Replace('#', ';'), ';'));
+ }
+
+ private readonly string signedCommit =
+ "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n" +
+ "parent 8496071c1b46c854b31185ea97743be6a8774479\n" +
+ "author Ben Burkert 1358451456 -0800\n" +
+ "committer Ben Burkert 1358451456 -0800\n" +
+ "gpgsig -----BEGIN PGP SIGNATURE-----\n" +
+ " Version: GnuPG v1.4.12 (Darwin)\n" +
+ " \n" +
+ " iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n" +
+ " o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n" +
+ " JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n" +
+ " AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n" +
+ " SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n" +
+ " who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n" +
+ " 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n" +
+ " cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n" +
+ " c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n" +
+ " ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n" +
+ " 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n" +
+ " cpxtDQQMGYFpXK/71stq\n" +
+ " =ozeK\n" +
+ " -----END PGP SIGNATURE-----\n" +
+ "\n" +
+ "a simple commit which works\n";
+
+ private readonly string signatureData =
+ "-----BEGIN PGP SIGNATURE-----\n" +
+ "Version: GnuPG v1.4.12 (Darwin)\n" +
+ "\n" +
+ "iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n" +
+ "o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n" +
+ "JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n" +
+ "AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n" +
+ "SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n" +
+ "who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n" +
+ "6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n" +
+ "cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n" +
+ "c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n" +
+ "ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n" +
+ "7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n" +
+ "cpxtDQQMGYFpXK/71stq\n" +
+ "=ozeK\n" +
+ "-----END PGP SIGNATURE-----";
+
+ private readonly string signedData =
+ "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n" +
+ "parent 8496071c1b46c854b31185ea97743be6a8774479\n" +
+ "author Ben Burkert 1358451456 -0800\n" +
+ "committer Ben Burkert 1358451456 -0800\n" +
+ "\n" +
+ "a simple commit which works\n";
+
+ [Fact]
+ public void CanExtractSignatureFromCommit()
+ {
+ string repoPath = InitNewRepository();
+ using (var repo = new Repository(repoPath))
+ {
+ var odb = repo.ObjectDatabase;
+ var signedId = odb.Write(Encoding.UTF8.GetBytes(signedCommit));
+
+ // Look up the commit to make sure we wrote something valid
+ var commit = repo.Lookup(signedId);
+ Assert.Equal("a simple commit which works\n", commit.Message);
+
+ var signatureInfo = Commit.ExtractSignature(repo, signedId, "gpgsig");
+ Assert.Equal(signedData, signatureInfo.SignedData);
+ Assert.Equal(signatureData, signatureInfo.Signature);
+
+ signatureInfo = Commit.ExtractSignature(repo, signedId);
+ Assert.Equal(signedData, signatureInfo.SignedData);
+ Assert.Equal(signatureData, signatureInfo.Signature);
+ }
+ }
+
+ [Fact]
+ public void CanCreateACommitString()
+ {
+ string repoPath = SandboxStandardTestRepo();
+ using (var repo = new Repository(repoPath))
+ {
+ var tipCommit = repo.Head.Tip;
+ var recreatedCommit = Commit.CreateBuffer(
+ tipCommit.Author,
+ tipCommit.Committer,
+ tipCommit.Message,
+ tipCommit.Tree,
+ tipCommit.Parents,
+ false, null);
+
+ var recreatedId = repo.ObjectDatabase.Write(Encoding.UTF8.GetBytes(recreatedCommit));
+ Assert.Equal(tipCommit.Id, recreatedId);
+ }
+ }
+
+ [Fact]
+ public void CanCreateASignedCommit()
+ {
+ string repoPath = SandboxStandardTestRepo();
+ using (var repo = new Repository(repoPath))
+ {
+ var odb = repo.ObjectDatabase;
+ var signedId = odb.Write(Encoding.UTF8.GetBytes(signedCommit));
+ var signedId2 = odb.CreateCommitWithSignature(signedData, signatureData);
+
+ Assert.Equal(signedId, signedId2);
+
+ var signatureInfo = Commit.ExtractSignature(repo, signedId2);
+ Assert.Equal(signedData, signatureInfo.SignedData);
+ Assert.Equal(signatureData, signatureInfo.Signature);
+ }
+ }
}
}
diff --git a/LibGit2Sharp.Tests/ConfigurationFixture.cs b/LibGit2Sharp.Tests/ConfigurationFixture.cs
index 30c8b207f..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
{
@@ -36,12 +35,8 @@ public void CanUnsetAnEntryFromTheLocalConfiguration()
[Fact]
public void CanUnsetAnEntryFromTheGlobalConfiguration()
{
- SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
-
- var options = BuildFakeConfigs(scd);
-
string path = SandboxBareTestRepo();
- using (var repo = new Repository(path, options))
+ using (var repo = new Repository(path))
{
Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global));
Assert.Equal(42, repo.Config.Get("Wow.Man-I-am-totally-global").Value);
@@ -54,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()
{
@@ -63,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));
}
}
@@ -114,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"));
@@ -143,12 +245,10 @@ public void CanReadStringValue()
[Fact]
public void CanEnumerateGlobalConfig()
{
- string configPath = CreateConfigurationWithDummyUser(Constants.Identity);
- var options = new RepositoryOptions { GlobalConfigurationLocation = configPath };
-
var path = SandboxStandardTestRepoGitDir();
- using (var repo = new Repository(path, options))
+ using (var repo = new Repository(path))
{
+ CreateConfigurationWithDummyUser(repo, Constants.Identity);
var entry = repo.Config.FirstOrDefault>(e => e.Key == "user.name");
Assert.NotNull(entry);
Assert.Equal(Constants.Signature.Name, entry.Value);
@@ -200,16 +300,14 @@ public void CanFindInLocalConfig()
[Fact]
public void CanFindInGlobalConfig()
{
- string configPath = CreateConfigurationWithDummyUser(Constants.Identity);
- var options = new RepositoryOptions { GlobalConfigurationLocation = configPath };
var path = SandboxStandardTestRepoGitDir();
- using (var repo = new Repository(path, options))
+ using (var repo = new Repository(path))
{
- var matches = repo.Config.Find(@"\.name", ConfigurationLevel.Global);
+ var matches = repo.Config.Find("-rocks", ConfigurationLevel.Global);
Assert.NotNull(matches);
- Assert.Equal(new[] { "user.name" },
+ Assert.Equal(new[] { "woot.this-rocks" },
matches.Select(m => m.Key).ToArray());
}
}
@@ -331,12 +429,8 @@ public void SettingUnsupportedTypeThrows()
[Fact]
public void CanGetAnEntryFromASpecificStore()
{
- SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
-
- var options = BuildFakeConfigs(scd);
-
string path = SandboxStandardTestRepo();
- using (var repo = new Repository(path, options))
+ using (var repo = new Repository(path))
{
Assert.True(repo.Config.HasConfig(ConfigurationLevel.Local));
Assert.True(repo.Config.HasConfig(ConfigurationLevel.Global));
@@ -356,12 +450,8 @@ public void CanGetAnEntryFromASpecificStore()
[Fact]
public void CanTellIfASpecificStoreContainsAKey()
{
- SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
-
- var options = BuildFakeConfigs(scd);
-
string path = SandboxBareTestRepo();
- using (var repo = new Repository(path, options))
+ using (var repo = new Repository(path))
{
Assert.True(repo.Config.HasConfig(ConfigurationLevel.System));
@@ -382,21 +472,19 @@ public static IEnumerable