diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..096ff2565 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; Check http://editorconfig.org/ for more informations +; Top-most EditorConfig file +root = true + +; 4-column space indentation +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true + +; Not change VS generated files +[*.{sln,csroj}] +trim_trailing_whitespace = false +insert_final_newline = false diff --git a/.gitignore b/.gitignore index 8bb24a91b..842317924 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ _ReSharper*/ LibGit2Sharp/Core/UniqueIdentifier.cs !Lib/NativeBinaries/*/*.pdb +!nuget.package/build/ +_NCrunch_LibGit2Sharp/ diff --git a/.travis.yml b/.travis.yml index 8932fc543..49106a6cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,12 +10,11 @@ os: before_install: - date -u - uname -a + - export PATH=/opt/mono/bin:$PATH - env | sort # Make sure CMake and Mono are installed -install: - - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/travis.linux.install.deps.sh; fi - - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/travis.osx.install.deps.sh; fi +install: ./CI/travis.${TRAVIS_OS_NAME}.install.deps.sh # Build libgit2, LibGit2Sharp and run the tests script: diff --git a/CHANGES.md b/CHANGES.md index 9dee434dc..2e4766edb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,36 @@ - CodeBetter TeamCity: - Travis: +## v0.20 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.19...v0.20)) + +### Additions + + - Teach RemoteUpdater to update the remote url (#803) + - Introduce ObjectDatabase.CreateTree(Index) and Index.Reset(Tree) (#788, #799) + - Add process wide logging feature (#832) + - Add process wide SmartSubtransport registration/unregistration (#528) + - Expose Index.Clear() (#814) + +### Changes + + - Require Mono 3.6+ on non Windows platform (#800) + - Require NuGet 2.7+ to install the package (#836) + - Throw MergeFetchHeadNotFoundException when Pull cannot find ref to merge (#841) + - Drop Remote.IsSupportedUrl() (#857) + - Deprecate repo.Version in favor of GlobalSettings.Version (#726, #820) + - Remove optional parameters from IRepository (#779, #815) + - Move higher level Index operations to IRepository (#822, #851) + - Deprecate repo.Refs.Move() in favor of repo.Refs.Rename() (#752, #819) + - Update libgit2 binaries to libgit2/libgit2@3f8d005 + +### Fixes + + - Fix compareOptions handling in Diff.Compare (#827, #828) + - Honor MSBuild Publish mechanism (#597, #821) + - Make Configuration.BuildSignature() throw a more descriptive message (#831, #858) + - Prevent Branch.Remote property from throwing when the remote is unresolvable (#823) + - Teach Revert() to clean up repository state when there is nothing to revert (#816) + ## v0.19 - ([diff](https://github.com/libgit2/libgit2sharp/compare/v0.18.1...v0.19)) ### Additions diff --git a/CI/travis.linux.install.deps.sh b/CI/travis.linux.install.deps.sh index 94400152c..1a820d07b 100755 --- a/CI/travis.linux.install.deps.sh +++ b/CI/travis.linux.install.deps.sh @@ -1,4 +1,9 @@ #!/bin/bash set -ev -sudo apt-get install cmake mono-devel mono-gmcs +sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/tpokorra:/mono/xUbuntu_12.04/ /' >> /etc/apt/sources.list.d/mono-opt.list" + +curl http://download.opensuse.org/repositories/home:/tpokorra:/mono/xUbuntu_12.04/Release.key | sudo apt-key add - + +sudo apt-get update +sudo apt-get install mono-opt cmake diff --git a/CI/travis.osx.install.deps.sh b/CI/travis.osx.install.deps.sh index 51082f15a..c6621b735 100755 --- a/CI/travis.osx.install.deps.sh +++ b/CI/travis.osx.install.deps.sh @@ -1,11 +1,10 @@ #!/bin/bash set -ev -MONO_VER=2.10.10 +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.dmg" -hdid "MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.dmg" -sudo installer -pkg "/Volumes/Mono Framework MDK ${MONO_VER}/MonoFramework-MDK-${MONO_VER}.macos10.xamarin.x86.pkg" -target / +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/Lib/NativeBinaries/amd64/git2-3f8d005.dll b/Lib/NativeBinaries/amd64/git2-3f8d005.dll new file mode 100644 index 000000000..b7c5193cd Binary files /dev/null and b/Lib/NativeBinaries/amd64/git2-3f8d005.dll differ diff --git a/Lib/NativeBinaries/amd64/git2-3f8d005.pdb b/Lib/NativeBinaries/amd64/git2-3f8d005.pdb new file mode 100644 index 000000000..01c00edd9 Binary files /dev/null and b/Lib/NativeBinaries/amd64/git2-3f8d005.pdb differ diff --git a/Lib/NativeBinaries/amd64/git2-69db893.dll b/Lib/NativeBinaries/amd64/git2-69db893.dll deleted file mode 100644 index 1b4395446..000000000 Binary files a/Lib/NativeBinaries/amd64/git2-69db893.dll and /dev/null differ diff --git a/Lib/NativeBinaries/amd64/git2-69db893.pdb b/Lib/NativeBinaries/amd64/git2-69db893.pdb deleted file mode 100644 index 7ac433959..000000000 Binary files a/Lib/NativeBinaries/amd64/git2-69db893.pdb and /dev/null differ diff --git a/Lib/NativeBinaries/x86/git2-3f8d005.dll b/Lib/NativeBinaries/x86/git2-3f8d005.dll new file mode 100644 index 000000000..9754921f6 Binary files /dev/null and b/Lib/NativeBinaries/x86/git2-3f8d005.dll differ diff --git a/Lib/NativeBinaries/x86/git2-69db893.pdb b/Lib/NativeBinaries/x86/git2-3f8d005.pdb similarity index 50% rename from Lib/NativeBinaries/x86/git2-69db893.pdb rename to Lib/NativeBinaries/x86/git2-3f8d005.pdb index 1a4fcf5c7..576c18572 100644 Binary files a/Lib/NativeBinaries/x86/git2-69db893.pdb and b/Lib/NativeBinaries/x86/git2-3f8d005.pdb differ diff --git a/Lib/NativeBinaries/x86/git2-69db893.dll b/Lib/NativeBinaries/x86/git2-69db893.dll deleted file mode 100644 index 4295b98eb..000000000 Binary files a/Lib/NativeBinaries/x86/git2-69db893.dll and /dev/null differ diff --git a/LibGit2Sharp.Tests/AttributesFixture.cs b/LibGit2Sharp.Tests/AttributesFixture.cs index 5092518b3..b6700becb 100644 --- a/LibGit2Sharp.Tests/AttributesFixture.cs +++ b/LibGit2Sharp.Tests/AttributesFixture.cs @@ -30,7 +30,7 @@ private static void AssertNormalization(IRepository repo, string filename, bool Touch(repo.Info.WorkingDirectory, filename, sb.ToString()); - repo.Index.Stage(filename); + repo.Stage(filename); IndexEntry entry = repo.Index[filename]; Assert.NotNull(entry); diff --git a/LibGit2Sharp.Tests/BlobFixture.cs b/LibGit2Sharp.Tests/BlobFixture.cs index f615aed18..8e2722425 100644 --- a/LibGit2Sharp.Tests/BlobFixture.cs +++ b/LibGit2Sharp.Tests/BlobFixture.cs @@ -62,7 +62,7 @@ public void CanGetBlobAsTextWithVariousEncodings(string encodingName, int expect var bomPath = Touch(repo.Info.WorkingDirectory, bomFile, content, encoding); Assert.Equal(expectedContentBytes, File.ReadAllBytes(bomPath).Length); - repo.Index.Stage(bomFile); + repo.Stage(bomFile); var commit = repo.Commit("bom", Constants.Signature, Constants.Signature); var blob = (Blob)commit.Tree[bomFile].Target; @@ -186,7 +186,7 @@ public void CanStageAFileGeneratedFromABlobContentStream() File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, "small.txt"), sb.ToString()); } - repo.Index.Stage("small.txt"); + repo.Stage("small.txt"); IndexEntry entry = repo.Index["small.txt"]; Assert.Equal("baae1fb3760a73481ced1fa03dc15614142c19ef", entry.Id.Sha); @@ -198,7 +198,7 @@ public void CanStageAFileGeneratedFromABlobContentStream() CopyStream(stream, file); } - repo.Index.Stage("small.fromblob.txt"); + repo.Stage("small.fromblob.txt"); IndexEntry newentry = repo.Index["small.fromblob.txt"]; Assert.Equal("baae1fb3760a73481ced1fa03dc15614142c19ef", newentry.Id.Sha); diff --git a/LibGit2Sharp.Tests/BranchFixture.cs b/LibGit2Sharp.Tests/BranchFixture.cs index b24cf582a..908d0f414 100644 --- a/LibGit2Sharp.Tests/BranchFixture.cs +++ b/LibGit2Sharp.Tests/BranchFixture.cs @@ -75,6 +75,7 @@ public void CanCreateAnUnbornBranch() // The branch now exists... Branch orphan = repo.Branches["orphan"]; Assert.NotNull(orphan); + AssertBelongsToARepository(repo, orphan); // ...and points to that newly created commit Assert.Equal(c, orphan.Tip); @@ -390,6 +391,64 @@ public void QueryUpstreamBranchCanonicalNameForLocalTrackingBranch() } } + [Fact] + public void QueryRemoteForRemoteBranch() + { + using (var repo = new Repository(StandardTestRepoPath)) + { + var master = repo.Branches["origin/master"]; + Assert.Equal(repo.Network.Remotes["origin"], master.Remote); + } + } + + [Fact] + public void QueryUnresolvableRemoteForRemoteBranch() + { + var path = CloneStandardTestRepo(); + + var fetchRefSpecs = new string[] { "+refs/heads/notfound/*:refs/remotes/origin/notfound/*" }; + + using (var repo = InitIsolatedRepository(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); + + Branch branch = repo.Branches["refs/remotes/origin/master"]; + + Assert.NotNull(branch); + Assert.True(branch.IsRemote); + + Assert.Null(branch.Remote); + } + } + + [Fact] + public void QueryAmbigousRemoteForRemoteBranch() + { + var path = CloneStandardTestRepo(); + + var fetchRefSpec = "+refs/heads/*:refs/remotes/origin/*"; + var url = "http://github.com/libgit2/TestGitRepository"; + + using (var repo = InitIsolatedRepository(path)) + { + // Add a second remote so that it is ambiguous which remote + // the remote-tracking branch tracks. + repo.Network.Remotes.Add("ambiguous", url, fetchRefSpec); + + Branch branch = repo.Branches["refs/remotes/origin/master"]; + + Assert.NotNull(branch); + Assert.True(branch.IsRemote); + + Assert.Null(branch.Remote); + } + } + [Fact] public void CanLookupABranchByItsCanonicalName() { @@ -489,6 +548,7 @@ public void CanGetTrackingInformationFromBranchSharingNoHistoryWithItsTrackedBra Assert.True(master.IsTracking); Assert.NotNull(master.TrackedBranch); + AssertBelongsToARepository(repo, master.TrackedBranch); Assert.NotNull(master.TrackingDetails); Assert.Equal(9, master.TrackingDetails.AheadBy); @@ -502,6 +562,28 @@ public void CanGetTrackingInformationFromBranchSharingNoHistoryWithItsTrackedBra } } + [Fact] + public void TrackingInformationIsEmptyForBranchTrackingPrunedRemoteBranch() + { + var path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + const string remoteRef = "refs/remotes/origin/master"; + repo.Refs.Remove(remoteRef); + + Branch master = repo.Branches["master"]; + Assert.True(master.IsTracking); + Assert.NotNull(master.TrackedBranch); + Assert.Equal(remoteRef, master.TrackedBranch.CanonicalName); + Assert.Null(master.TrackedBranch.Tip); + + Assert.NotNull(master.TrackingDetails); + Assert.Null(master.TrackingDetails.AheadBy); + Assert.Null(master.TrackingDetails.BehindBy); + Assert.Null(master.TrackingDetails.CommonAncestor); + } + } + [Fact] public void TrackingInformationIsEmptyForNonTrackingBranch() { @@ -603,6 +685,33 @@ public void CanSetTrackedBranch() } } + [Fact] + public void SetTrackedBranchForUnreasolvableRemoteThrows() + { + const string testBranchName = "branchToSetUpstreamInfoFor"; + const string trackedBranchName = "refs/remotes/origin/master"; + var fetchRefSpecs = new string[] { "+refs/heads/notfound/*:refs/remotes/origin/notfound/*" }; + + string path = CloneStandardTestRepo(); + using (var repo = new Repository(path)) + { + // Modify the fetch spec so that the remote for the remote-tracking branch + // cannot be resolved. + Remote remote = repo.Network.Remotes["origin"]; + Assert.NotNull(remote); + repo.Network.Remotes.Update(remote, r => r.FetchRefSpecs = fetchRefSpecs); + + // Now attempt to update the tracked branch + Branch branch = repo.CreateBranch(testBranchName); + Assert.False(branch.IsTracking); + + Branch trackedBranch = repo.Branches[trackedBranchName]; + + Assert.Throws(() => repo.Branches.Update(branch, + b => b.TrackedBranch = trackedBranch.CanonicalName)); + } + } + [Fact] public void CanSetUpstreamBranch() { @@ -942,7 +1051,7 @@ public void TrackedBranchExistsFromDefaultConfigInEmptyClone() Assert.Equal("origin", repo.Head.Remote.Name); Touch(repo.Info.WorkingDirectory, "a.txt", "a"); - repo.Index.Stage("a.txt"); + repo.Stage("a.txt"); repo.Commit("A file", Constants.Signature, Constants.Signature); Assert.NotNull(repo.Head.Tip); diff --git a/LibGit2Sharp.Tests/CheckoutFixture.cs b/LibGit2Sharp.Tests/CheckoutFixture.cs index abe4ad9aa..c8e6e9766 100644 --- a/LibGit2Sharp.Tests/CheckoutFixture.cs +++ b/LibGit2Sharp.Tests/CheckoutFixture.cs @@ -28,13 +28,15 @@ public void CanCheckoutAnExistingBranch(string branchName) // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Branch branch = repo.Branches[branchName]; Assert.NotNull(branch); + AssertBelongsToARepository(repo, branch); Branch test = repo.Checkout(branch); Assert.False(repo.Info.IsHeadDetached); + AssertBelongsToARepository(repo, test); Assert.False(test.IsRemote); Assert.True(test.IsCurrentRepositoryHead); @@ -43,7 +45,7 @@ public void CanCheckoutAnExistingBranch(string branchName) Assert.False(master.IsCurrentRepositoryHead); // Working directory should not be dirty - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Assert reflog entry is created var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); @@ -69,7 +71,7 @@ public void CanCheckoutAnExistingBranchByName(string branchName) // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Branch test = repo.Checkout(branchName); Assert.False(repo.Info.IsHeadDetached); @@ -81,7 +83,7 @@ public void CanCheckoutAnExistingBranchByName(string branchName) Assert.False(master.IsCurrentRepositoryHead); // Working directory should not be dirty - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Assert reflog entry is created var reflogEntry = repo.Refs.Log(repo.Refs.Head).First(); @@ -111,9 +113,10 @@ public void CanCheckoutAnArbitraryCommit(string commitPointer, bool checkoutByCo // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); var commit = repo.Lookup(commitPointer); + AssertBelongsToARepository(repo, commit); Branch detachedHead = checkoutByCommitOrBranchSpec ? repo.Checkout(commitPointer) : repo.Checkout(commit); @@ -121,7 +124,7 @@ public void CanCheckoutAnArbitraryCommit(string commitPointer, bool checkoutByCo Assert.Equal(commit.Sha, detachedHead.Tip.Sha); Assert.True(repo.Head.IsCurrentRepositoryHead); Assert.True(repo.Info.IsHeadDetached); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Assert.True(detachedHead.IsCurrentRepositoryHead); Assert.False(detachedHead.IsRemote); @@ -153,7 +156,7 @@ 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.Index.Remove(fileFullPath); + repo.Remove(fileFullPath); repo.Commit("2nd commit", Constants.Signature, Constants.Signature); // Checkout other_branch @@ -162,7 +165,7 @@ public void CheckoutAddsMissingFilesInWorkingDirectory() otherBranch.Checkout(); // Verify working directory is updated - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Assert.Equal(originalFileContent, File.ReadAllText(fileFullPath)); } } @@ -181,7 +184,7 @@ public void CheckoutRemovesExtraFilesInWorkingDirectory() string newFileFullPath = Touch( repo.Info.WorkingDirectory, "b.txt", "hello from master branch!\n"); - repo.Index.Stage(newFileFullPath); + repo.Stage(newFileFullPath); repo.Commit("2nd commit", Constants.Signature, Constants.Signature); // Checkout other_branch @@ -190,7 +193,7 @@ public void CheckoutRemovesExtraFilesInWorkingDirectory() otherBranch.Checkout(); // Verify working directory is updated - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Assert.False(File.Exists(newFileFullPath)); } } @@ -209,7 +212,7 @@ public void CheckoutUpdatesModifiedFilesInWorkingDirectory() string fullPath = Touch( repo.Info.WorkingDirectory, originalFilePath, "Update : hello from master branch!\n"); - repo.Index.Stage(fullPath); + repo.Stage(fullPath); repo.Commit("2nd commit", Constants.Signature, Constants.Signature); // Checkout other_branch @@ -218,7 +221,7 @@ public void CheckoutUpdatesModifiedFilesInWorkingDirectory() otherBranch.Checkout(); // Verify working directory is updated - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Assert.Equal(originalFileContent, File.ReadAllText(fullPath)); } } @@ -243,7 +246,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() // Set the working directory to the current head. ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Create otherBranch from current Head. repo.Branches.Add(otherBranchName, master.Tip); @@ -251,7 +254,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() // Add change to master. Touch(repo.Info.WorkingDirectory, originalFilePath, originalFileContent); - repo.Index.Stage(originalFilePath); + repo.Stage(originalFilePath); repo.Commit("change in master", Constants.Signature, Constants.Signature); // Checkout otherBranch. @@ -259,7 +262,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() // Add change to otherBranch. Touch(repo.Info.WorkingDirectory, originalFilePath, alternateFileContent); - repo.Index.Stage(originalFilePath); + repo.Stage(originalFilePath); // Assert that normal checkout throws exception // for the conflict. @@ -272,7 +275,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges() Assert.True(repo.Branches["master"].IsCurrentRepositoryHead); // And that the current index is not dirty. - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); } } @@ -284,7 +287,7 @@ public void CheckingOutWithMergeConflictsThrows() using (var repo = new Repository(repoPath)) { Touch(repo.Info.WorkingDirectory, originalFilePath, "Hello\n"); - repo.Index.Stage(originalFilePath); + repo.Stage(originalFilePath); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); // Create 2nd branch @@ -292,7 +295,7 @@ public void CheckingOutWithMergeConflictsThrows() // Update file in main Touch(repo.Info.WorkingDirectory, originalFilePath, "Hello from master!\n"); - repo.Index.Stage(originalFilePath); + repo.Stage(originalFilePath); repo.Commit("2nd commit", Constants.Signature, Constants.Signature); // Checkout branch2 @@ -304,7 +307,7 @@ public void CheckingOutWithMergeConflictsThrows() Assert.Throws(() => repo.Checkout("master")); // And when there are staged commits - repo.Index.Stage(originalFilePath); + repo.Stage(originalFilePath); Assert.Throws(() => repo.Checkout("master")); } } @@ -319,7 +322,7 @@ public void CanCancelCheckoutThroughNotifyCallback() string relativePath = "a.txt"; Touch(repo.Info.WorkingDirectory, relativePath, "Hello\n"); - repo.Index.Stage(relativePath); + repo.Stage(relativePath); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); // Create 2nd branch @@ -327,7 +330,7 @@ public void CanCancelCheckoutThroughNotifyCallback() // Update file in main Touch(repo.Info.WorkingDirectory, relativePath, "Hello from master!\n"); - repo.Index.Stage(relativePath); + repo.Stage(relativePath); repo.Commit("2nd commit", Constants.Signature, Constants.Signature); // Checkout branch2 @@ -446,13 +449,13 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri string relativePathUpdated = "updated.txt"; Touch(repo.Info.WorkingDirectory, relativePathUpdated, "updated file text A"); - repo.Index.Stage(relativePathUpdated); + repo.Stage(relativePathUpdated); repo.Commit("Commit initial update file", Constants.Signature, Constants.Signature); // Create conflicting change string relativePathConflict = "conflict.txt"; Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text A"); - repo.Index.Stage(relativePathConflict); + repo.Stage(relativePathConflict); repo.Commit("Initial commit of conflict.txt and update.txt", Constants.Signature, Constants.Signature); // Create another branch @@ -460,9 +463,9 @@ 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.Index.Stage(relativePathUpdated); + repo.Stage(relativePathUpdated); Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text BB"); - repo.Index.Stage(relativePathConflict); + repo.Stage(relativePathConflict); repo.Commit("2nd commit of conflict.txt and update.txt on master branch", Constants.Signature, Constants.Signature); @@ -471,14 +474,14 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri // Make alternate edits to conflict.txt and update.txt Touch(repo.Info.WorkingDirectory, relativePathUpdated, "updated file text CCC"); - repo.Index.Stage(relativePathUpdated); + repo.Stage(relativePathUpdated); Touch(repo.Info.WorkingDirectory, relativePathConflict, "conflict file text CCC"); - repo.Index.Stage(relativePathConflict); + repo.Stage(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.Index.Stage(relativePathConflict); + repo.Stage(relativePathConflict); // Create ignored change string relativePathIgnore = Path.Combine("bin", "ignored.txt"); @@ -519,14 +522,14 @@ public void CheckoutRetainsUntrackedChanges() string fullPathFileB = Touch(repo.Info.WorkingDirectory, "b.txt", alternateFileContent); // Verify that there is an untracked entry. - Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); repo.Checkout(otherBranchName); // Verify untracked entry still exists. - Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); } } @@ -543,14 +546,14 @@ public void ForceCheckoutRetainsUntrackedChanges() string fullPathFileB = Touch(repo.Info.WorkingDirectory, "b.txt", alternateFileContent); // Verify that there is an untracked entry. - Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); // Verify untracked entry still exists. - Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB)); + Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(fullPathFileB)); } } @@ -567,14 +570,14 @@ public void CheckoutRetainsUnstagedChanges() string fullPathFileA = Touch(repo.Info.WorkingDirectory, originalFilePath, alternateFileContent); // Verify that there is a modified entry. - Assert.Equal(1, repo.Index.RetrieveStatus().Modified.Count()); - Assert.Equal(FileStatus.Modified, repo.Index.RetrieveStatus(fullPathFileA)); + Assert.Equal(1, repo.RetrieveStatus().Modified.Count()); + Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(fullPathFileA)); repo.Checkout(otherBranchName); // Verify modified entry still exists. - Assert.Equal(1, repo.Index.RetrieveStatus().Modified.Count()); - Assert.Equal(FileStatus.Modified, repo.Index.RetrieveStatus(fullPathFileA)); + Assert.Equal(1, repo.RetrieveStatus().Modified.Count()); + Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(fullPathFileA)); } } @@ -589,17 +592,17 @@ public void CheckoutRetainsStagedChanges() // Generate a staged change. string fullPathFileA = Touch(repo.Info.WorkingDirectory, originalFilePath, alternateFileContent); - repo.Index.Stage(fullPathFileA); + repo.Stage(fullPathFileA); // Verify that there is a staged entry. - Assert.Equal(1, repo.Index.RetrieveStatus().Staged.Count()); - Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus(fullPathFileA)); + Assert.Equal(1, repo.RetrieveStatus().Staged.Count()); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus(fullPathFileA)); repo.Checkout(otherBranchName); // Verify staged entry still exists. - Assert.Equal(1, repo.Index.RetrieveStatus().Staged.Count()); - Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus(fullPathFileA)); + Assert.Equal(1, repo.RetrieveStatus().Staged.Count()); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus(fullPathFileA)); } } @@ -618,14 +621,14 @@ public void CheckoutRetainsIgnoredChanges() "bin/some_ignored_file.txt", "hello from this ignored file."); - Assert.Equal(1, repo.Index.RetrieveStatus().Ignored.Count()); + Assert.Equal(1, repo.RetrieveStatus().Ignored.Count()); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath)); repo.Checkout(otherBranchName); // Verify that the ignored file still exists. - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath)); Assert.True(File.Exists(ignoredFilePath)); } } @@ -645,14 +648,14 @@ public void ForceCheckoutRetainsIgnoredChanges() "bin/some_ignored_file.txt", "hello from this ignored file."); - Assert.Equal(1, repo.Index.RetrieveStatus().Ignored.Count()); + Assert.Equal(1, repo.RetrieveStatus().Ignored.Count()); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath)); repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force }); // Verify that the ignored file still exists. - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(ignoredFilePath)); Assert.True(File.Exists(ignoredFilePath)); } } @@ -673,7 +676,7 @@ public void CheckoutBranchSnapshot() // Add commit to master string fullPath = Touch(repo.Info.WorkingDirectory, originalFilePath, "Update : hello from master branch!\n"); - repo.Index.Stage(fullPath); + repo.Stage(fullPath); repo.Commit("2nd commit", Constants.Signature, Constants.Signature); Assert.False(repo.Info.IsHeadDetached); @@ -682,7 +685,7 @@ public void CheckoutBranchSnapshot() // Head should point at initial commit. Assert.Equal(repo.Head.Tip, initialCommit); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Verify that HEAD is detached. Assert.Equal(repo.Refs["HEAD"].TargetIdentifier, initial.Tip.Sha); @@ -750,7 +753,7 @@ public void CheckoutFromDetachedHead(string commitPointer) { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); var commitSha = repo.Lookup(commitPointer).Sha; @@ -776,7 +779,7 @@ public void CheckoutBranchFromDetachedHead() { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Branch initialHead = repo.Checkout("6dcf9bf"); @@ -801,7 +804,7 @@ public void CheckoutBranchByShortNameAttachesTheHead(string shortBranchName, str { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); repo.Checkout("6dcf9bf"); Assert.True(repo.Info.IsHeadDetached); @@ -822,7 +825,7 @@ public void CheckoutPreviousCheckedOutBranch() { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Branch previousHead = repo.Checkout("i-do-numbers"); repo.Checkout("diff-test-cases"); @@ -846,7 +849,7 @@ public void CheckoutCurrentReference() Assert.True(master.IsCurrentRepositoryHead); ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); var reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); @@ -940,12 +943,12 @@ public void CanCheckoutPath(string originalBranch, string checkoutFrom, string p ResetAndCleanWorkingDirectory(repo); repo.Checkout(originalBranch); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); repo.CheckoutPaths(checkoutFrom, new[] { path }); - Assert.Equal(expectedStatus, repo.Index.RetrieveStatus(path)); - Assert.Equal(1, repo.Index.RetrieveStatus().Count()); + Assert.Equal(expectedStatus, repo.RetrieveStatus(path)); + Assert.Equal(1, repo.RetrieveStatus().Count()); } } @@ -959,13 +962,13 @@ public void CanCheckoutPaths() { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); repo.CheckoutPaths("i-do-numbers", checkoutPaths); foreach (string checkoutPath in checkoutPaths) { - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(checkoutPath)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(checkoutPath)); } } } @@ -979,7 +982,7 @@ public void CannotCheckoutPathsWithEmptyOrNullPathArgument() { // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Passing null 'paths' parameter should throw Assert.Throws(typeof(ArgumentNullException), @@ -987,7 +990,7 @@ public void CannotCheckoutPathsWithEmptyOrNullPathArgument() // Passing empty list should do nothing repo.CheckoutPaths("i-do-numbers", Enumerable.Empty()); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); } } @@ -1003,16 +1006,16 @@ public void CanCheckoutPathFromCurrentBranch(string fileName) // Set the working directory to the current head ResetAndCleanWorkingDirectory(repo); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); Touch(repo.Info.WorkingDirectory, fileName, "new text file"); - Assert.True(repo.Index.RetrieveStatus().IsDirty); + Assert.True(repo.RetrieveStatus().IsDirty); var opts = new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force }; repo.CheckoutPaths("HEAD", new[] { fileName }, opts); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); } } @@ -1025,10 +1028,10 @@ private void PopulateBasicRepository(IRepository repo) { // Generate a .gitignore file. string gitIgnoreFilePath = Touch(repo.Info.WorkingDirectory, ".gitignore", "bin"); - repo.Index.Stage(gitIgnoreFilePath); + repo.Stage(gitIgnoreFilePath); string fullPathFileA = Touch(repo.Info.WorkingDirectory, originalFilePath, originalFileContent); - repo.Index.Stage(fullPathFileA); + repo.Stage(fullPathFileA); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); diff --git a/LibGit2Sharp.Tests/CherryPickFixture.cs b/LibGit2Sharp.Tests/CherryPickFixture.cs index 8ce6b04a0..7afb3f033 100644 --- a/LibGit2Sharp.Tests/CherryPickFixture.cs +++ b/LibGit2Sharp.Tests/CherryPickFixture.cs @@ -28,7 +28,7 @@ public void CanCherryPick(bool fromDetachedHead) Assert.Equal(CherryPickStatus.CherryPicked, result.Status); Assert.Equal(cherryPickedCommitId, result.Commit.Id.Sha); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); Assert.Equal(fromDetachedHead, repo.Info.IsHeadDetached); Assert.Equal(commitToMerge.Author, result.Commit.Author); Assert.Equal(Constants.Signature, result.Commit.Committer); @@ -130,7 +130,7 @@ private Commit AddFileCommitToRepo(IRepository repository, string filename, stri { Touch(repository.Info.WorkingDirectory, filename, content); - repository.Index.Stage(filename); + repository.Stage(filename); return repository.Commit("New commit", Constants.Signature, Constants.Signature); } diff --git a/LibGit2Sharp.Tests/CleanFixture.cs b/LibGit2Sharp.Tests/CleanFixture.cs index 9613a2c96..358000b94 100644 --- a/LibGit2Sharp.Tests/CleanFixture.cs +++ b/LibGit2Sharp.Tests/CleanFixture.cs @@ -13,14 +13,14 @@ public void CanCleanWorkingDirectory() using (var repo = new Repository(path)) { // Verify that there are the expected number of entries and untracked files - Assert.Equal(6, repo.Index.RetrieveStatus().Count()); - Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count()); + Assert.Equal(6, repo.RetrieveStatus().Count()); + Assert.Equal(1, repo.RetrieveStatus().Untracked.Count()); repo.RemoveUntrackedFiles(); // Verify that there are the expected number of entries and 0 untracked files - Assert.Equal(5, repo.Index.RetrieveStatus().Count()); - Assert.Equal(0, repo.Index.RetrieveStatus().Untracked.Count()); + Assert.Equal(5, repo.RetrieveStatus().Count()); + Assert.Equal(0, repo.RetrieveStatus().Untracked.Count()); } } diff --git a/LibGit2Sharp.Tests/CommitFixture.cs b/LibGit2Sharp.Tests/CommitFixture.cs index e372fd7d2..a4d7eca44 100644 --- a/LibGit2Sharp.Tests/CommitFixture.cs +++ b/LibGit2Sharp.Tests/CommitFixture.cs @@ -5,6 +5,7 @@ using LibGit2Sharp.Core; using LibGit2Sharp.Tests.TestHelpers; using Xunit; +using Xunit.Extensions; namespace LibGit2Sharp.Tests { @@ -519,6 +520,27 @@ public void DirectlyAccessingAnUnknownTreeEntryOfTheCommitReturnsNull() } } + [Theory] + [InlineData(null, "x@example.com")] + [InlineData("", "x@example.com")] + [InlineData("X", null)] + [InlineData("X", "")] + public void CommitWithInvalidSignatureConfigThrows(string name, string email) + { + string repoPath = InitNewRepository(); + string configPath = CreateConfigurationWithDummyUser(name, email); + var options = new RepositoryOptions { GlobalConfigurationLocation = configPath }; + + using (var repo = new Repository(repoPath, options)) + { + Assert.Equal(name, repo.Config.GetValueOrDefault("user.name")); + Assert.Equal(email, repo.Config.GetValueOrDefault("user.email")); + + Assert.Throws( + () => repo.Commit("Initial egotistic commit", new CommitOptions { AllowEmptyCommit = true })); + } + } + [Fact] public void CanCommitWithSignatureFromConfig() { @@ -534,10 +556,10 @@ public void CanCommitWithSignatureFromConfig() const string relativeFilepath = "new.txt"; string filePath = Touch(repo.Info.WorkingDirectory, relativeFilepath, "null"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); File.AppendAllText(filePath, "token\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); Assert.Null(repo.Head[relativeFilepath]); @@ -594,7 +616,7 @@ public void CommitCleansUpMergeMetadata() const string relativeFilepath = "new.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "this is a new file"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); string mergeHeadPath = Touch(repo.Info.Path, "MERGE_HEAD", "abcdefabcdefabcdefabcdefabcdefabcdefabcd"); string mergeMsgPath = Touch(repo.Info.Path, "MERGE_MSG", "This is a dummy merge.\n"); @@ -629,9 +651,9 @@ public void CanCommitALittleBit() const string relativeFilepath = "new.txt"; string filePath = Touch(repo.Info.WorkingDirectory, relativeFilepath, "null"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); File.AppendAllText(filePath, "token\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); Assert.Null(repo.Head[relativeFilepath]); @@ -662,7 +684,7 @@ public void CanCommitALittleBit() Assert.Equal(commit.Id, repo.Refs.Log(targetCanonicalName).First().To); File.WriteAllText(filePath, "nulltoken commits!\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(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); @@ -683,7 +705,7 @@ public void CanCommitALittleBit() File.WriteAllText(filePath, "davidfowl commits!\n"); var author3 = new Signature("David Fowler", "david.fowler@microsoft.com", author.When.AddSeconds(2)); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); Commit commit3 = repo.Commit("I'm going to branch you backwards in time!", author3, author3); @@ -709,7 +731,7 @@ private static void AddCommitToRepo(string path) { const string relativeFilepath = "test.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "test\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(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); @@ -793,7 +815,7 @@ private static void CreateAndStageANewFile(IRepository repo) { string relativeFilepath = string.Format("new-file-{0}.txt", Guid.NewGuid()); Touch(repo.Info.WorkingDirectory, relativeFilepath, "brand new content\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); } private static void AssertCommitHasBeenAmended(IRepository repo, Commit amendedCommit, Commit originalCommit) @@ -882,7 +904,7 @@ public void CanCommitOnOrphanedBranch() const string relativeFilepath = "test.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "test\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); Assert.Equal(1, repo.Head.Commits.Count()); @@ -998,16 +1020,16 @@ public void CanNotAmendACommitInAWayThatWouldLeadTheNewCommitToBecomeEmpty() using (var repo = new Repository(repoPath)) { Touch(repo.Info.WorkingDirectory, "test.txt", "test\n"); - repo.Index.Stage("test.txt"); + repo.Stage("test.txt"); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); Touch(repo.Info.WorkingDirectory, "new.txt", "content\n"); - repo.Index.Stage("new.txt"); + repo.Stage("new.txt"); repo.Commit("One commit", Constants.Signature, Constants.Signature); - repo.Index.Remove("new.txt"); + repo.Remove("new.txt"); Assert.Throws(() => repo.Commit("Oops", Constants.Signature, Constants.Signature, new CommitOptions { AmendPreviousCommit = true })); diff --git a/LibGit2Sharp.Tests/ConflictFixture.cs b/LibGit2Sharp.Tests/ConflictFixture.cs index 9a98bd1b6..148081c4c 100644 --- a/LibGit2Sharp.Tests/ConflictFixture.cs +++ b/LibGit2Sharp.Tests/ConflictFixture.cs @@ -77,12 +77,12 @@ public void CanResolveConflictsByRemovingFromTheIndex( Assert.NotNull(repo.Index.Conflicts[filename]); Assert.Equal(0, repo.Index.Conflicts.ResolvedConflicts.Count()); - repo.Index.Remove(filename, removeFromWorkdir); + repo.Remove(filename, removeFromWorkdir); Assert.Null(repo.Index.Conflicts[filename]); Assert.Equal(count - removedIndexEntries, repo.Index.Count); Assert.Equal(existsAfterRemove, File.Exists(fullpath)); - Assert.Equal(lastStatus, repo.Index.RetrieveStatus(filename)); + Assert.Equal(lastStatus, repo.RetrieveStatus(filename)); Assert.Equal(1, repo.Index.Conflicts.ResolvedConflicts.Count()); Assert.NotNull(repo.Index.Conflicts.ResolvedConflicts[filename]); diff --git a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs index d58508ca8..79055475f 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTargetFixture.cs @@ -12,12 +12,12 @@ private static void SetUpSimpleDiffContext(IRepository repo) { var fullpath = Touch(repo.Info.WorkingDirectory, "file.txt", "hello\n"); - repo.Index.Stage(fullpath); + repo.Stage(fullpath); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); File.AppendAllText(fullpath, "world\n"); - repo.Index.Stage(fullpath); + repo.Stage(fullpath); File.AppendAllText(fullpath, "!!!\n"); } @@ -158,10 +158,10 @@ public void ShowcaseTheDifferenceBetweenTheTwoKindOfComparison() var fullpath = Path.Combine(repo.Info.WorkingDirectory, "file.txt"); File.Move(fullpath, fullpath + ".bak"); - repo.Index.Stage(fullpath); + repo.Stage(fullpath); File.Move(fullpath + ".bak", fullpath); - FileStatus state = repo.Index.RetrieveStatus("file.txt"); + FileStatus state = repo.RetrieveStatus("file.txt"); Assert.Equal(FileStatus.Removed | FileStatus.Untracked, state); var wrkDirToIdxToTree = repo.Diff.Compare(repo.Head.Tip.Tree, @@ -373,11 +373,11 @@ public void CanCopeWithEndOfFileNewlineChanges() { var fullpath = Touch(repo.Info.WorkingDirectory, "file.txt", "a"); - repo.Index.Stage("file.txt"); + repo.Stage("file.txt"); repo.Commit("Add file without line ending", Constants.Signature, Constants.Signature); File.AppendAllText(fullpath, "\n"); - repo.Index.Stage("file.txt"); + repo.Stage("file.txt"); var changes = repo.Diff.Compare(repo.Head.Tip.Tree, DiffTargets.Index); Assert.Equal(1, changes.Modified.Count()); diff --git a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs index ea230a037..2ba238f4f 100644 --- a/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs +++ b/LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs @@ -96,7 +96,7 @@ public void CanDetectABinaryChange() CreateBinaryFile(filepath); - repo.Index.Stage(filename); + repo.Stage(filename); var commit = repo.Commit("Add binary file", Constants.Signature, Constants.Signature); File.AppendAllText(filepath, "abcdef"); @@ -104,7 +104,7 @@ public void CanDetectABinaryChange() var patch = repo.Diff.Compare(commit.Tree, DiffTargets.WorkingDirectory, new[] { filename }); Assert.True(patch[filename].IsBinaryComparison); - repo.Index.Stage(filename); + repo.Stage(filename); var commit2 = repo.Commit("Update binary file", Constants.Signature, Constants.Signature); var patch2 = repo.Diff.Compare(commit.Tree, commit2.Tree, new[] { filename }); @@ -122,7 +122,7 @@ public void CanDetectABinaryDeletion() CreateBinaryFile(filepath); - repo.Index.Stage(filename); + repo.Stage(filename); var commit = repo.Commit("Add binary file", Constants.Signature, Constants.Signature); File.Delete(filepath); @@ -130,7 +130,7 @@ public void CanDetectABinaryDeletion() var patch = repo.Diff.Compare(commit.Tree, DiffTargets.WorkingDirectory, new [] {filename}); Assert.True(patch[filename].IsBinaryComparison); - repo.Index.Remove(filename); + repo.Remove(filename); var commit2 = repo.Commit("Delete binary file", Constants.Signature, Constants.Signature); var patch2 = repo.Diff.Compare(commit.Tree, commit2.Tree, new[] { filename }); @@ -282,11 +282,11 @@ public void DetectsTheExactRenamingOfFilesByDefault() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); - repo.Index.Move(originalPath, renamedPath); + repo.Move(originalPath, renamedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -317,14 +317,14 @@ public void RenameThresholdsAreObeyed() // 4 lines Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); // 8 lines, 50% are from original file Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\ne\nf\ng\nh\n"); - repo.Index.Stage(originalPath); - repo.Index.Move(originalPath, renamedPath); + repo.Stage(originalPath); + repo.Move(originalPath, renamedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -358,11 +358,11 @@ public void ExactModeDetectsExactRenames() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); - repo.Index.Move(originalPath, renamedPath); + repo.Move(originalPath, renamedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -392,11 +392,11 @@ public void ExactModeDetectsExactCopies() var copiedFullPath = Path.Combine(repo.Info.WorkingDirectory, copiedPath); Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.Copy(originalFullPath, copiedFullPath); - repo.Index.Stage(copiedPath); + repo.Stage(copiedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -423,13 +423,13 @@ public void ExactModeDoesntDetectRenamesWithEdits() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); - repo.Index.Move(originalPath, renamedPath); + repo.Move(originalPath, renamedPath); File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, renamedPath), "e\nf\n"); - repo.Index.Stage(renamedPath); + repo.Stage(renamedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -460,12 +460,12 @@ public void CanIncludeUnmodifiedEntriesWhenDetectingTheExactRenamingOfFilesWhenE Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.Copy(originalFullPath, copiedFullPath); - repo.Index.Stage(copiedPath); + repo.Stage(copiedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -497,11 +497,11 @@ public void CanNotDetectTheExactRenamingFilesWhenNotEnabled() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); - repo.Index.Move(originalPath, renamedPath); + repo.Move(originalPath, renamedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -531,12 +531,12 @@ public void CanDetectTheExactCopyingOfNonModifiedFilesWhenEnabled() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.Copy(originalFullPath, copiedFullPath); - repo.Index.Stage(copiedPath); + repo.Stage(copiedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -568,12 +568,12 @@ public void CanNotDetectTheExactCopyingOfNonModifiedFilesWhenNotEnabled() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.Copy(originalFullPath, copiedFullPath); - repo.Index.Stage(copiedPath); + repo.Stage(copiedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -598,15 +598,15 @@ public void CanDetectTheExactCopyingOfModifiedFilesWhenEnabled() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.Copy(originalFullPath, copiedFullPath); Touch(repo.Info.WorkingDirectory, originalPath, "e\n"); - repo.Index.Stage(originalPath); - repo.Index.Stage(copiedPath); + repo.Stage(originalPath); + repo.Stage(copiedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -638,15 +638,15 @@ public void CanNotDetectTheExactCopyingOfModifiedFilesWhenNotEnabled() Touch(repo.Info.WorkingDirectory, originalPath, "a\nb\nc\nd\n"); - repo.Index.Stage(originalPath); + repo.Stage(originalPath); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.Copy(originalFullPath, copiedFullPath); File.AppendAllText(originalFullPath, "e\n"); - repo.Index.Stage(originalPath); - repo.Index.Stage(copiedPath); + repo.Stage(originalPath); + repo.Stage(copiedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); @@ -667,11 +667,11 @@ public void CanIncludeUnmodifiedEntriesWhenEnabled() Touch(repo.Info.WorkingDirectory, "a.txt", "abc\ndef\n"); Touch(repo.Info.WorkingDirectory, "b.txt", "abc\ndef\n"); - repo.Index.Stage(new[] {"a.txt", "b.txt"}); + repo.Stage(new[] {"a.txt", "b.txt"}); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, "b.txt"), "ghi\njkl\n"); - repo.Index.Stage("b.txt"); + repo.Stage("b.txt"); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); var changes = repo.Diff.Compare(old.Tree, @new.Tree, @@ -701,9 +701,9 @@ public void CanDetectTheExactRenamingExactCopyingOfNonModifiedAndModifiedFilesWh Touch(repo.Info.WorkingDirectory, originalPath2, "1\n2\n3\n4\n"); Touch(repo.Info.WorkingDirectory, originalPath3, "5\n6\n7\n8\n"); - repo.Index.Stage(originalPath); - repo.Index.Stage(originalPath2); - repo.Index.Stage(originalPath3); + repo.Stage(originalPath); + repo.Stage(originalPath2); + repo.Stage(originalPath3); Commit old = repo.Commit("Initial", Constants.Signature, Constants.Signature); @@ -715,10 +715,10 @@ public void CanDetectTheExactRenamingExactCopyingOfNonModifiedAndModifiedFilesWh File.Copy(originalFullPath3, copiedFullPath2); File.AppendAllText(originalFullPath3, "9\n"); - repo.Index.Stage(originalPath3); - repo.Index.Stage(copiedPath1); - repo.Index.Stage(copiedPath2); - repo.Index.Move(originalPath, renamedPath); + repo.Stage(originalPath3); + repo.Stage(copiedPath1); + repo.Stage(copiedPath2); + repo.Move(originalPath, renamedPath); Commit @new = repo.Commit("Updated", Constants.Signature, Constants.Signature); diff --git a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs index 2b7c528c4..fd0a34b44 100644 --- a/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs +++ b/LibGit2Sharp.Tests/DiffWorkdirToIndexFixture.cs @@ -47,7 +47,7 @@ public void CanCompareTheWorkDirAgainstTheIndexWithLaxUnmatchedExplicitPathsVali { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); var changes = repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false }); Assert.Equal(0, changes.Count()); @@ -64,7 +64,7 @@ public void ComparingTheWorkDirAgainstTheIndexWithStrictUnmatchedExplicitPathsVa { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); Assert.Throws(() => repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions())); } @@ -79,7 +79,7 @@ public void CallbackForUnmatchedExplicitPathsIsCalledWhenSet(string relativePath using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); repo.Diff.Compare(new[] { relativePath }, false, new ExplicitPathsOptions { diff --git a/LibGit2Sharp.Tests/FilterBranchFixture.cs b/LibGit2Sharp.Tests/FilterBranchFixture.cs index 9cd5beac7..bdfb137ee 100644 --- a/LibGit2Sharp.Tests/FilterBranchFixture.cs +++ b/LibGit2Sharp.Tests/FilterBranchFixture.cs @@ -609,7 +609,7 @@ public void CanRewriteParents() // Graft the orphan "test" branch to the tip of "packed" // // Before: - // * e90810b (test, lw, e90810b, test) + // * e90810b (test, tag: lw, tag: e90810b, tag: test) // | // * 6dcf9bf // <------------------ note: no connection @@ -619,7 +619,7 @@ public void CanRewriteParents() // // ... and after: // - // * f558880 (test, lw, e90810b, test) + // * f558880 (test, tag: lw, tag: e90810b, tag: test) // | // * 0c25efa // | <------------------ add this link @@ -630,29 +630,40 @@ public void CanRewriteParents() public void CanRewriteParentWithRewrittenCommit() { var commitToRewrite = repo.Lookup("6dcf9bf"); - var newParent = repo.Lookup("41bc8c6"); + var newParent = repo.Branches["packed"].Tip; + + Assert.True(newParent.Sha.StartsWith("41bc8c6")); repo.Refs.RewriteHistory(new RewriteHistoryOptions { OnError = OnError, OnSucceeding = OnSucceeding, - CommitHeaderRewriter = - c => - c.Id != newParent.Id - ? null - : CommitRewriteInfo.From(c, message: "changed"), CommitParentsRewriter = c => c.Id != commitToRewrite.Id ? c.Parents : new[] { newParent } - }, commitToRewrite, newParent); + }, commitToRewrite); AssertSucceedingButNotError(); - var rewrittenParent = repo.Lookup("refs/heads/test~").Parents.Single(); - Assert.Equal(newParent.Tree, rewrittenParent.Tree); - Assert.NotEqual(newParent, rewrittenParent); + // Assert "packed" hasn't been rewritten + Assert.True(repo.Branches["packed"].Tip.Sha.StartsWith("41bc8c6")); + + // Assert (test, tag: lw, tag: e90810b, tag: test) have been rewritten + var rewrittenTestCommit = repo.Branches["test"].Tip; + Assert.True(rewrittenTestCommit.Sha.StartsWith("f558880")); + Assert.Equal(rewrittenTestCommit, repo.Lookup("refs/tags/lw^{commit}")); + Assert.Equal(rewrittenTestCommit, repo.Lookup("refs/tags/e90810b^{commit}")); + Assert.Equal(rewrittenTestCommit, repo.Lookup("refs/tags/test^{commit}")); + + // Assert parent of rewritten commit + var rewrittenTestCommitParent = rewrittenTestCommit.Parents.Single(); + Assert.True(rewrittenTestCommitParent.Sha.StartsWith("0c25efa")); + + // Assert grand parent of rewritten commit + var rewrittenTestCommitGrandParent = rewrittenTestCommitParent.Parents.Single(); + Assert.True(rewrittenTestCommitGrandParent.Sha.StartsWith("41bc8c6")); } [Fact] diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs index 299359034..d8533f035 100644 --- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs +++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs @@ -1,5 +1,4 @@ -using System; -using LibGit2Sharp; +using System.Text.RegularExpressions; using LibGit2Sharp.Tests.TestHelpers; using Xunit; @@ -10,10 +9,44 @@ public class GlobalSettingsFixture : BaseFixture [Fact] public void CanGetMinimumCompiledInFeatures() { - BuiltInFeatures features = GlobalSettings.Features(); + BuiltInFeatures features = GlobalSettings.Version.Features; Assert.True(features.HasFlag(BuiltInFeatures.Threads)); Assert.True(features.HasFlag(BuiltInFeatures.Https)); } + + [Fact] + public void CanRetrieveValidVersionString() + { + // Version string format is: + // Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features) + // Example output: + // "0.17.0-unknown-06d772d (x86 - Threads, Https)" + + string versionInfo = GlobalSettings.Version.ToString(); + + // The GlobalSettings.Version returned string should contain : + // version:'0.17.0' LibGit2Sharp version number. + // git2SharpHash:'unknown' ( when compiled from source ) else LibGit2Sharp library hash. + // git2hash: '06d772d' LibGit2 library hash. + // arch: 'x86' or 'amd64' LibGit2 target. + // git2Features: 'Threads, Ssh' LibGit2 features compiled with. + string regex = @"^(?\d{1,}\.\d{1,2}\.\d{1,3})-(?\w+)-(?\w+) \((?\w+) - (?(?:\w*(?:, )*\w+)*)\)$"; + + Assert.NotNull(versionInfo); + + Match regexResult = Regex.Match(versionInfo, regex); + + Assert.True(regexResult.Success, "The following version string format is enforced:" + + "Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features)"); + + GroupCollection matchGroups = regexResult.Groups; + + // Check that all groups are valid + foreach (Group group in matchGroups) + { + Assert.True(group.Success); + } + } } } diff --git a/LibGit2Sharp.Tests/IgnoreFixture.cs b/LibGit2Sharp.Tests/IgnoreFixture.cs index 0ad15a3f4..523a08c8b 100644 --- a/LibGit2Sharp.Tests/IgnoreFixture.cs +++ b/LibGit2Sharp.Tests/IgnoreFixture.cs @@ -16,15 +16,15 @@ public void TemporaryRulesShouldApplyUntilCleared() { Touch(repo.Info.WorkingDirectory, "Foo.cs", "Bar"); - Assert.True(repo.Index.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); + Assert.True(repo.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); repo.Ignore.AddTemporaryRules(new[] { "*.cs" }); - Assert.False(repo.Index.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); + Assert.False(repo.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); repo.Ignore.ResetAllTemporaryRules(); - Assert.True(repo.Index.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); + Assert.True(repo.RetrieveStatus().Untracked.Select(s => s.FilePath).Contains("Foo.cs")); } } diff --git a/LibGit2Sharp.Tests/IndexFixture.cs b/LibGit2Sharp.Tests/IndexFixture.cs index f6c1690a2..a1ec882a3 100644 --- a/LibGit2Sharp.Tests/IndexFixture.cs +++ b/LibGit2Sharp.Tests/IndexFixture.cs @@ -93,13 +93,13 @@ public void CanRenameAFile() const string oldName = "polite.txt"; - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus(oldName)); Touch(repo.Info.WorkingDirectory, oldName, "hello test file\n"); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(oldName)); - repo.Index.Stage(oldName); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(oldName)); + repo.Stage(oldName); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(oldName)); // Generated through // $ echo "hello test file" | git hash-object --stdin @@ -111,13 +111,13 @@ public void CanRenameAFile() Signature who = Constants.Signature; repo.Commit("Initial commit", who, who); - Assert.Equal(FileStatus.Unaltered, repo.Index.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus(oldName)); const string newName = "being.frakking.polite.txt"; - repo.Index.Move(oldName, newName); - Assert.Equal(FileStatus.Removed, repo.Index.RetrieveStatus(oldName)); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(newName)); + repo.Move(oldName, newName); + Assert.Equal(FileStatus.Removed, repo.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(newName)); Assert.Equal(1, repo.Index.Count); Assert.Equal(expectedHash, repo.Index[newName].Id.Sha); @@ -125,8 +125,8 @@ public void CanRenameAFile() who = who.TimeShift(TimeSpan.FromMinutes(5)); Commit commit = repo.Commit("Fix file name", who, who); - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus(oldName)); - Assert.Equal(FileStatus.Unaltered, repo.Index.RetrieveStatus(newName)); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus(oldName)); + Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus(newName)); Assert.Equal(expectedHash, commit.Tree[newName].Target.Id.Sha); } @@ -142,13 +142,13 @@ public void CanMoveAnExistingFileOverANonExistingFile(string sourcePath, FileSta string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(sourceStatus, repo.Index.RetrieveStatus(sourcePath)); - Assert.Equal(destStatus, repo.Index.RetrieveStatus(destPath)); + Assert.Equal(sourceStatus, repo.RetrieveStatus(sourcePath)); + Assert.Equal(destStatus, repo.RetrieveStatus(destPath)); - repo.Index.Move(sourcePath, destPath); + repo.Move(sourcePath, destPath); - Assert.Equal(sourcePostStatus, repo.Index.RetrieveStatus(sourcePath)); - Assert.Equal(destPostStatus, repo.Index.RetrieveStatus(destPath)); + Assert.Equal(sourcePostStatus, repo.RetrieveStatus(sourcePath)); + Assert.Equal(destPostStatus, repo.RetrieveStatus(destPath)); } } @@ -182,12 +182,12 @@ private static void InvalidMoveUseCases(string sourcePath, FileStatus sourceStat { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Equal(sourceStatus, repo.Index.RetrieveStatus(sourcePath)); + Assert.Equal(sourceStatus, repo.RetrieveStatus(sourcePath)); foreach (var destPath in destPaths) { string path = destPath; - Assert.Throws(() => repo.Index.Move(sourcePath, path)); + Assert.Throws(() => repo.Move(sourcePath, path)); } } } @@ -205,7 +205,7 @@ public void PathsOfIndexEntriesAreExpressedInNativeFormat() Touch(repo.Info.WorkingDirectory, relFilePath, "Anybody out there?"); // Stage the file - repo.Index.Stage(relFilePath); + repo.Stage(relFilePath); // Get the index Index index = repo.Index; @@ -241,7 +241,7 @@ public void StagingAFileWhenTheIndexIsLockedThrowsALockedFileException() Touch(repo.Info.Path, "index.lock"); Touch(repo.Info.WorkingDirectory, "newfile", "my my, this is gonna crash\n"); - Assert.Throws(() => repo.Index.Stage("newfile")); + Assert.Throws(() => repo.Stage("newfile")); } } @@ -258,25 +258,113 @@ public void CanCopeWithExternalChangesToTheIndex() using (var repoWrite = new Repository(path)) using (var repoRead = new Repository(path)) { - var writeStatus = repoWrite.Index.RetrieveStatus(); + var writeStatus = repoWrite.RetrieveStatus(); Assert.True(writeStatus.IsDirty); Assert.Equal(0, repoWrite.Index.Count); - var readStatus = repoRead.Index.RetrieveStatus(); + var readStatus = repoRead.RetrieveStatus(); Assert.True(readStatus.IsDirty); Assert.Equal(0, repoRead.Index.Count); - repoWrite.Index.Stage("*"); + repoWrite.Stage("*"); repoWrite.Commit("message", Constants.Signature, Constants.Signature); - writeStatus = repoWrite.Index.RetrieveStatus(); + writeStatus = repoWrite.RetrieveStatus(); Assert.False(writeStatus.IsDirty); Assert.Equal(2, repoWrite.Index.Count); - readStatus = repoRead.Index.RetrieveStatus(); + readStatus = repoRead.RetrieveStatus(); Assert.False(readStatus.IsDirty); Assert.Equal(2, repoRead.Index.Count); } } + + [Fact] + public void CanResetFullyMergedIndexFromTree() + { + string path = CloneStandardTestRepo(); + + const string testFile = "new_tracked_file.txt"; + + // It is sufficient to check just one of the stage area changes, such as the added file, + // to verify that the index has indeed been read from the tree. + using (var repo = new Repository(path)) + { + const string headIndexTreeSha = "e5d221fc5da11a3169bf503d76497c81be3207b6"; + + Assert.True(repo.Index.IsFullyMerged); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(testFile)); + + var headIndexTree = repo.Lookup(headIndexTreeSha); + Assert.NotNull(headIndexTree); + repo.Index.Replace(headIndexTree); + + Assert.True(repo.Index.IsFullyMerged); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(testFile)); + } + + // Check that the index was persisted to disk. + using (var repo = new Repository(path)) + { + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(testFile)); + } + } + + [Fact] + public void CanResetIndexWithUnmergedEntriesFromTree() + { + string path = CloneMergedTestRepo(); + + const string testFile = "one.txt"; + + // It is sufficient to check just one of the stage area changes, such as the modified file, + // to verify that the index has indeed been read from the tree. + using (var repo = new Repository(path)) + { + const string headIndexTreeSha = "1cb365141a52dfbb24933515820eb3045fbca12b"; + + Assert.False(repo.Index.IsFullyMerged); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus(testFile)); + + var headIndexTree = repo.Lookup(headIndexTreeSha); + Assert.NotNull(headIndexTree); + repo.Index.Replace(headIndexTree); + + Assert.True(repo.Index.IsFullyMerged); + Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(testFile)); + } + + // Check that the index was persisted to disk. + using (var repo = new Repository(path)) + { + Assert.Equal(FileStatus.Modified, repo.RetrieveStatus(testFile)); + } + } + + [Fact] + public void CanClearTheIndex() + { + string path = CloneStandardTestRepo(); + const string testFile = "1.txt"; + + // It is sufficient to check just one of the stage area changes, such as the modified file, + // to verify that the index has indeed been read from the tree. + using (var repo = new Repository(path)) + { + Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus(testFile)); + Assert.NotEqual(0, repo.Index.Count); + + repo.Index.Clear(); + Assert.Equal(0, repo.Index.Count); + + Assert.Equal(FileStatus.Removed | FileStatus.Untracked, repo.RetrieveStatus(testFile)); + } + + // Check that the index was persisted to disk. + using (var repo = new Repository(path)) + { + Assert.Equal(FileStatus.Removed | FileStatus.Untracked, repo.RetrieveStatus(testFile)); + } + } } } diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index 6a1fd4038..b631e0275 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -102,6 +102,7 @@ + @@ -123,6 +124,7 @@ + diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.v2.ncrunchproject b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.v2.ncrunchproject new file mode 100644 index 000000000..e24470157 --- /dev/null +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.v2.ncrunchproject @@ -0,0 +1,31 @@ + + 1000 + false + true + false + true + false + false + false + false + false + true + true + false + true + true + true + 60000 + + + + AutoDetect + STA + x86 + + + LibGit2Sharp.Tests.ShadowCopyFixture.CanProbeForNativeBinariesFromAShadowCopiedAssembly + + + Resources\**;Resources\**.* + \ No newline at end of file diff --git a/LibGit2Sharp.Tests/LogFixture.cs b/LibGit2Sharp.Tests/LogFixture.cs new file mode 100644 index 000000000..76654e3d3 --- /dev/null +++ b/LibGit2Sharp.Tests/LogFixture.cs @@ -0,0 +1,40 @@ +using System; +using LibGit2Sharp; +using LibGit2Sharp.Core; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class LogFixture + { + [Fact] + public void CanEnableAndDisableLogging() + { + // Setting logging produces a log message at level Info, + // ensure that we catch it. + LogLevel level = LogLevel.None; + string message = null; + + GlobalSettings.LogConfiguration = new LogConfiguration(LogLevel.Trace, (l, m) => { level = l; message = m; }); + + Assert.Equal(LogLevel.Info, level); + Assert.Equal("Logging enabled at level Trace", message); + + // Configuring at Warning and higher means that the + // message at level Info should not be produced. + level = LogLevel.None; + message = null; + + GlobalSettings.LogConfiguration = new LogConfiguration(LogLevel.Warning, (l, m) => { level = l; message = m; }); + + Assert.Equal(LogLevel.None, level); + Assert.Equal(null, message); + + // Similarly, turning logging off should produce no messages. + GlobalSettings.LogConfiguration = LogConfiguration.None; + + Assert.Equal(LogLevel.None, level); + Assert.Equal(null, message); + } + } +} diff --git a/LibGit2Sharp.Tests/MergeFixture.cs b/LibGit2Sharp.Tests/MergeFixture.cs index 7801de718..428a6f677 100644 --- a/LibGit2Sharp.Tests/MergeFixture.cs +++ b/LibGit2Sharp.Tests/MergeFixture.cs @@ -202,7 +202,7 @@ public void CanFastForwardRepos(bool shouldMergeOccurInDetachedHeadState) Assert.Equal(repo.Branches["FirstBranch"].Tip, repo.Head.Tip); Assert.Equal(repo.Head.Tip, mergeResult.Commit); - Assert.Equal(0, repo.Index.RetrieveStatus().Count()); + Assert.Equal(0, repo.RetrieveStatus().Count()); Assert.Equal(shouldMergeOccurInDetachedHeadState, repo.Info.IsHeadDetached); if (!shouldMergeOccurInDetachedHeadState) @@ -314,7 +314,7 @@ public void CanFastForwardCommit(bool fromDetachedHead, FastForwardStrategy fast Assert.Equal(expectedMergeStatus, result.Status); Assert.Equal(expectedCommitId, result.Commit.Id.Sha); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); Assert.Equal(fromDetachedHead, repo.Info.IsHeadDetached); } } @@ -339,7 +339,7 @@ public void CanNonFastForwardMergeCommit(bool fromDetachedHead, FastForwardStrat MergeResult result = repo.Merge(commitToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = fastForwardStrategy }); Assert.Equal(expectedMergeStatus, result.Status); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); Assert.Equal(fromDetachedHead, repo.Info.IsHeadDetached); } } @@ -497,11 +497,11 @@ public void CanMergeAndNotCommit() Assert.Equal(MergeStatus.NonFastForward, result.Status); Assert.Equal(null, result.Commit); - RepositoryStatus repoStatus = repo.Index.RetrieveStatus(); + RepositoryStatus repoStatus = repo.RetrieveStatus(); // Verify that there is a staged entry. Assert.Equal(1, repoStatus.Count()); - Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus("b.txt")); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("b.txt")); } } @@ -517,7 +517,7 @@ public void CanForceNonFastForwardMerge() Assert.Equal(MergeStatus.NonFastForward, result.Status); Assert.Equal("f58f780d5a0ae392efd4a924450b1bbdc0577d32", result.Commit.Id.Sha); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); } } @@ -535,7 +535,7 @@ public void CanForceNonFastForwardMergeThroughConfig() Assert.Equal(MergeStatus.NonFastForward, result.Status); Assert.Equal("f58f780d5a0ae392efd4a924450b1bbdc0577d32", result.Commit.Id.Sha); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); } } @@ -551,7 +551,7 @@ public void VerifyUpToDateMerge() Assert.Equal(MergeStatus.UpToDate, result.Status); Assert.Equal(null, result.Commit); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); } } @@ -568,7 +568,7 @@ public void CanMergeCommittish(string committish, FastForwardStrategy strategy, MergeResult result = repo.Merge(committish, Constants.Signature, new MergeOptions() { FastForwardStrategy = strategy }); Assert.Equal(expectedMergeStatus, result.Status); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); } } @@ -591,7 +591,7 @@ public void MergeWithWorkDirConflictsThrows(bool shouldStage, FastForwardStrateg if (shouldStage) { - repo.Index.Stage("b.txt"); + repo.Stage("b.txt"); } Assert.Throws(() => repo.Merge(committishToMerge, Constants.Signature, new MergeOptions() { FastForwardStrategy = strategy })); @@ -663,7 +663,7 @@ public void MergeCanSpecifyMergeFileFavorOption(MergeFileFavor fileFavorFlag) Branch branch = repo.Branches[conflictBranchName]; Assert.NotNull(branch); - var status = repo.Index.RetrieveStatus(); + var status = repo.RetrieveStatus(); MergeOptions mergeOptions = new MergeOptions() { MergeFileFavor = fileFavorFlag, @@ -675,7 +675,7 @@ public void MergeCanSpecifyMergeFileFavorOption(MergeFileFavor fileFavorFlag) // Verify that the index and working directory are clean Assert.True(repo.Index.IsFullyMerged); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Get the blob containing the expected content. Blob expectedBlob = null; @@ -716,7 +716,7 @@ public void CanMergeBranch(string branchName, FastForwardStrategy strategy, Merg MergeResult result = repo.Merge(branch, Constants.Signature, new MergeOptions() { FastForwardStrategy = strategy }); Assert.Equal(expectedMergeStatus, result.Status); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); } } @@ -729,20 +729,20 @@ public void CanMergeIntoOrphanedBranch() repo.Refs.Add("HEAD", "refs/heads/orphan", true); // Remove entries from the working directory - foreach(var entry in repo.Index.RetrieveStatus()) + foreach(var entry in repo.RetrieveStatus()) { - repo.Index.Unstage(entry.FilePath); - repo.Index.Remove(entry.FilePath, true); + repo.Unstage(entry.FilePath); + repo.Remove(entry.FilePath, true); } // Assert that we have an empty working directory. - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); MergeResult result = repo.Merge("master", Constants.Signature); Assert.Equal(MergeStatus.FastForward, result.Status); Assert.Equal(masterBranchInitialId, result.Commit.Id.Sha); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); } } @@ -750,7 +750,7 @@ private Commit AddFileCommitToRepo(IRepository repository, string filename, stri { Touch(repository.Info.WorkingDirectory, filename, content); - repository.Index.Stage(filename); + repository.Stage(filename); return repository.Commit("New commit", Constants.Signature, Constants.Signature); } diff --git a/LibGit2Sharp.Tests/MetaFixture.cs b/LibGit2Sharp.Tests/MetaFixture.cs index 3cff83cc1..fb1b567cb 100644 --- a/LibGit2Sharp.Tests/MetaFixture.cs +++ b/LibGit2Sharp.Tests/MetaFixture.cs @@ -12,6 +12,11 @@ namespace LibGit2Sharp.Tests { public class MetaFixture { + private static readonly HashSet explicitOnlyInterfaces = new HashSet + { + typeof(IBelongToARepository), + }; + [Fact] public void PublicTestMethodsAreFactsOrTheories() { @@ -114,7 +119,7 @@ public void LibGit2SharpPublicInterfacesCoverAllPublicMembers() var methodsMissingFromInterfaces = from t in Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() where !t.IsInterface - where t.GetInterfaces().Any(i => i.IsPublic && i.Namespace == typeof(IRepository).Namespace) + where t.GetInterfaces().Any(i => i.IsPublic && i.Namespace == typeof(IRepository).Namespace && !explicitOnlyInterfaces.Contains(i)) let interfaceTargetMethods = from i in t.GetInterfaces() from im in t.GetInterfaceMap(i).TargetMethods select im @@ -126,6 +131,24 @@ from tm in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | Bindin methodsMissingFromInterfaces.ToArray())); } + [Fact] + public void LibGit2SharpExplicitOnlyInterfacesAreIndeedExplicitOnly() + { + var methodsMissingFromInterfaces = + from t in Assembly.GetAssembly(typeof(IRepository)).GetExportedTypes() + where t.GetInterfaces().Any(explicitOnlyInterfaces.Contains) + let interfaceTargetMethods = from i in t.GetInterfaces() + where explicitOnlyInterfaces.Contains(i) + from im in t.GetInterfaceMap(i).TargetMethods + select im + from tm in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) + where interfaceTargetMethods.Contains(tm) + select t.Name + " has public method " + tm.Name + " which should be explicitly implemented."; + + Assert.Equal("", string.Join(Environment.NewLine, + methodsMissingFromInterfaces.ToArray())); + } + [Fact] public void EnumsWithFlagsHaveMutuallyExclusiveValues() { diff --git a/LibGit2Sharp.Tests/NetworkFixture.cs b/LibGit2Sharp.Tests/NetworkFixture.cs index d1d221de5..31959286c 100644 --- a/LibGit2Sharp.Tests/NetworkFixture.cs +++ b/LibGit2Sharp.Tests/NetworkFixture.cs @@ -144,7 +144,7 @@ public void CanPull(FastForwardStrategy fastForwardStrategy) { repo.Reset(ResetMode.Hard, "HEAD~1"); - Assert.False(repo.Index.RetrieveStatus().Any()); + Assert.False(repo.RetrieveStatus().Any()); Assert.Equal(repo.Lookup("refs/remotes/origin/master~1"), repo.Head.Tip); PullOptions pullOptions = new PullOptions() @@ -196,6 +196,38 @@ public void CanPullIntoEmptyRepo() } } + [Fact] + public void PullWithoutMergeBranchThrows() + { + var scd = BuildSelfCleaningDirectory(); + string clonedRepoPath = Repository.Clone(StandardTestRepoPath, scd.DirectoryPath); + + using (var repo = new Repository(clonedRepoPath)) + { + Branch branch = repo.Branches["master"]; + + // Update the Upstream merge branch + repo.Branches.Update(branch, + b => b.UpstreamBranch = "refs/heads/another_master"); + + bool didPullThrow = false; + MergeFetchHeadNotFoundException thrownException = null; + + try + { + repo.Network.Pull(Constants.Signature, new PullOptions()); + } + catch(MergeFetchHeadNotFoundException ex) + { + didPullThrow = true; + thrownException = ex; + } + + Assert.True(didPullThrow, "Pull did not throw."); + Assert.True(thrownException.Message.Contains("refs/heads/another_master"), "Exception message did not contain expected reference."); + } + } + /* * git ls-remote http://github.com/libgit2/TestGitRepository * 49322bb17d3acc9146f98c97d078513228bbf3c0 HEAD diff --git a/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs b/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs index c6e724c10..d0f6565cd 100644 --- a/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs +++ b/LibGit2Sharp.Tests/ObjectDatabaseFixture.cs @@ -31,7 +31,7 @@ public void CanCreateABlobFromAFileInTheWorkingDirectory() string path = InitNewRepository(); using (var repo = new Repository(path)) { - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus("hello.txt")); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus("hello.txt")); File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, "hello.txt"), "I'm a new file\n"); @@ -40,7 +40,7 @@ public void CanCreateABlobFromAFileInTheWorkingDirectory() Assert.Equal("dc53d4c6b8684c21b0b57db29da4a2afea011565", blob.Sha); /* The file is unknown from the Index nor the Head ... */ - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus("hello.txt")); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus("hello.txt")); /* ...however, it's indeed stored in the repository. */ var fetchedBlob = repo.Lookup(blob.Id); @@ -280,7 +280,7 @@ public void CanCreateATreeContainingABlobFromAFileInTheWorkingDirectory() string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus("hello.txt")); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus("hello.txt")); File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, "hello.txt"), "I'm a new file\n"); TreeDefinition td = TreeDefinition.From(repo.Head.Tip.Tree) @@ -360,6 +360,40 @@ public void CannotCreateATreeContainingABlobFromARelativePathAgainstABareReposit } } + [Fact] + public void CreatingATreeFromIndexWithUnmergedEntriesThrows() + { + using (var repo = new Repository(MergedTestRepoWorkingDirPath)) + { + Assert.False(repo.Index.IsFullyMerged); + + Assert.Throws( + () => repo.ObjectDatabase.CreateTree(repo.Index)); + } + } + + [Fact] + public void CanCreateATreeFromIndex() + { + string path = CloneStandardTestRepo(); + + using (var repo = new Repository(path)) + { + const string expectedIndexTreeSha = "0fe0fd1943a1b63ecca36fa6bbe9bbe045f791a4"; + + // The tree representing the index is not in the db. + Assert.Null(repo.Lookup(expectedIndexTreeSha)); + + var tree = repo.ObjectDatabase.CreateTree(repo.Index); + Assert.NotNull(tree); + Assert.Equal(expectedIndexTreeSha, tree.Id.Sha); + + // The tree representing the index is now in the db. + tree = repo.Lookup(expectedIndexTreeSha); + Assert.NotNull(tree); + } + } + [Fact] public void CanCreateACommit() { diff --git a/LibGit2Sharp.Tests/OdbBackendFixture.cs b/LibGit2Sharp.Tests/OdbBackendFixture.cs index c99442c9b..161dcb938 100644 --- a/LibGit2Sharp.Tests/OdbBackendFixture.cs +++ b/LibGit2Sharp.Tests/OdbBackendFixture.cs @@ -16,7 +16,7 @@ private static Commit AddCommitToRepo(IRepository repo) { string relativeFilepath = "test.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, content); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); var ie = repo.Index[relativeFilepath]; Assert.NotNull(ie); @@ -28,7 +28,7 @@ private static Commit AddCommitToRepo(IRepository repo) relativeFilepath = "big.txt"; var zeros = new string('0', 32*1024 + 3); Touch(repo.Info.WorkingDirectory, relativeFilepath, zeros); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); ie = repo.Index[relativeFilepath]; Assert.NotNull(ie); diff --git a/LibGit2Sharp.Tests/PushFixture.cs b/LibGit2Sharp.Tests/PushFixture.cs index 9f2a9e106..d6b906ba6 100644 --- a/LibGit2Sharp.Tests/PushFixture.cs +++ b/LibGit2Sharp.Tests/PushFixture.cs @@ -35,7 +35,7 @@ private void AssertPush(Action push) // Change local state (commit) const string relativeFilepath = "new_file.txt"; Touch(clonedRepo.Info.WorkingDirectory, relativeFilepath, "__content__"); - clonedRepo.Index.Stage(relativeFilepath); + clonedRepo.Stage(relativeFilepath); clonedRepo.Commit("__commit_message__", Constants.Signature, Constants.Signature); // Assert local state has changed @@ -164,7 +164,7 @@ private Commit AddCommitToRepo(IRepository repository) Touch(repository.Info.WorkingDirectory, filename, random); - repository.Index.Stage(filename); + repository.Stage(filename); return repository.Commit("New commit", Constants.Signature, Constants.Signature); } diff --git a/LibGit2Sharp.Tests/ReferenceFixture.cs b/LibGit2Sharp.Tests/ReferenceFixture.cs index 6090d8e09..c3c71a836 100644 --- a/LibGit2Sharp.Tests/ReferenceFixture.cs +++ b/LibGit2Sharp.Tests/ReferenceFixture.cs @@ -601,21 +601,21 @@ public void UpdatingADirectReferenceTargetWithARevparsePointingAtAnUnknownObject } [Fact] - public void CanMoveAReferenceToADeeperReferenceHierarchy() + public void CanRenameAReferenceToADeeperReferenceHierarchy() { string path = CloneBareTestRepo(); using (var repo = new Repository(path)) { const string newName = "refs/tags/test/deep"; - Reference moved = repo.Refs.Move("refs/tags/test", newName); - Assert.NotNull(moved); - Assert.Equal(newName, moved.CanonicalName); + Reference renamed = repo.Refs.Rename("refs/tags/test", newName); + Assert.NotNull(renamed); + Assert.Equal(newName, renamed.CanonicalName); } } [Fact] - public void CanMoveAReferenceToAUpperReferenceHierarchy() + public void CanRenameAReferenceToAUpperReferenceHierarchy() { string path = CloneBareTestRepo(); using (var repo = new Repository(path)) @@ -624,14 +624,14 @@ public void CanMoveAReferenceToAUpperReferenceHierarchy() const string oldName = newName + "/mio"; repo.Refs.Add(oldName, repo.Head.CanonicalName); - Reference moved = repo.Refs.Move(oldName, newName); - Assert.NotNull(moved); - Assert.Equal(newName, moved.CanonicalName); + Reference renamed = repo.Refs.Rename(oldName, newName); + Assert.NotNull(renamed); + Assert.Equal(newName, renamed.CanonicalName); } } [Fact] - public void CanMoveAReferenceToADifferentReferenceHierarchy() + public void CanRenameAReferenceToADifferentReferenceHierarchy() { string path = CloneBareTestRepo(); using (var repo = new Repository(path)) @@ -643,27 +643,27 @@ public void CanMoveAReferenceToADifferentReferenceHierarchy() var oldId = repo.Refs[oldName].ResolveToDirectReference().Target.Id; - Reference moved = repo.Refs.Move(oldName, newName); - Assert.NotNull(moved); - Assert.Equal(newName, moved.CanonicalName); - Assert.Equal(oldId, moved.ResolveToDirectReference().Target.Id); + Reference renamed = repo.Refs.Rename(oldName, newName); + Assert.NotNull(renamed); + Assert.Equal(newName, renamed.CanonicalName); + Assert.Equal(oldId, renamed.ResolveToDirectReference().Target.Id); - AssertRefLogEntry(repo, newName, moved.ResolveToDirectReference().Target.Id, + AssertRefLogEntry(repo, newName, renamed.ResolveToDirectReference().Target.Id, string.Format("reference: renamed {0} to {1}", oldName, newName)); } } [Fact] - public void MovingANonExistingReferenceThrows() + public void RenamingANonExistingReferenceThrows() { using (var repo = new Repository(BareTestRepoPath)) { - Assert.Throws(() => repo.Refs.Move("refs/tags/i-am-void", "refs/atic/tagtest")); + Assert.Throws(() => repo.Refs.Rename("refs/tags/i-am-void", "refs/atic/tagtest")); } } [Fact] - public void CanMoveAndOverWriteAExistingReference() + public void CanRenameAndOverWriteAExistingReference() { string path = CloneBareTestRepo(); using (var repo = new Repository(path)) @@ -671,10 +671,10 @@ public void CanMoveAndOverWriteAExistingReference() const string oldName = "refs/heads/packed"; const string newName = "refs/heads/br2"; - Reference moved = repo.Refs.Move(oldName, newName, allowOverwrite: true); + Reference renamed = repo.Refs.Rename(oldName, newName, allowOverwrite: true); Assert.Null(repo.Refs[oldName]); - Assert.NotNull(repo.Refs[moved.CanonicalName]); + Assert.NotNull(repo.Refs[renamed.CanonicalName]); } } @@ -683,12 +683,12 @@ public void BlindlyOverwritingAExistingReferenceThrows() { using (var repo = new Repository(BareTestRepoPath)) { - Assert.Throws(() => repo.Refs.Move("refs/heads/packed", "refs/heads/br2")); + Assert.Throws(() => repo.Refs.Rename("refs/heads/packed", "refs/heads/br2")); } } [Fact] - public void MovingAReferenceDoesNotDecreaseTheRefsCount() + public void RenamingAReferenceDoesNotDecreaseTheRefsCount() { string path = CloneBareTestRepo(); using (var repo = new Repository(path)) @@ -699,7 +699,7 @@ public void MovingAReferenceDoesNotDecreaseTheRefsCount() List refs = repo.Refs.Select(r => r.CanonicalName).ToList(); Assert.True(refs.Contains(oldName)); - repo.Refs.Move(oldName, newName); + repo.Refs.Rename(oldName, newName); List refs2 = repo.Refs.Select(r => r.CanonicalName).ToList(); Assert.False(refs2.Contains(oldName)); @@ -710,7 +710,7 @@ public void MovingAReferenceDoesNotDecreaseTheRefsCount() } [Fact] - public void CanLookupAMovedReference() + public void CanLookupARenamedReference() { string path = CloneBareTestRepo(); using (var repo = new Repository(path)) @@ -718,10 +718,10 @@ public void CanLookupAMovedReference() const string oldName = "refs/tags/test"; const string newName = "refs/atic/tagtest"; - Reference moved = repo.Refs.Move(oldName, newName); + Reference renamed = repo.Refs.Rename(oldName, newName); Reference lookedUp = repo.Refs[newName]; - Assert.Equal(lookedUp, moved); + Assert.Equal(lookedUp, renamed); } } diff --git a/LibGit2Sharp.Tests/ReflogFixture.cs b/LibGit2Sharp.Tests/ReflogFixture.cs index f8bc249d1..155ea5a73 100644 --- a/LibGit2Sharp.Tests/ReflogFixture.cs +++ b/LibGit2Sharp.Tests/ReflogFixture.cs @@ -63,7 +63,7 @@ public void CommitShouldCreateReflogEntryOnHeadAndOnTargetedDirectReference() const string relativeFilepath = "new.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "content\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); var author = Constants.Signature; const string commitMessage = "Hope reflog behaves as it should"; @@ -97,7 +97,7 @@ public void CommitOnUnbornReferenceShouldCreateReflogEntryWithInitialTag() { const string relativeFilepath = "new.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "content\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); var author = Constants.Signature; const string commitMessage = "First commit should be logged as initial"; @@ -124,7 +124,7 @@ public void CommitOnDetachedHeadShouldInsertReflogEntry() const string relativeFilepath = "new.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "content\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); var author = Constants.Signature; const string commitMessage = "Commit on detached head"; diff --git a/LibGit2Sharp.Tests/RemoteFixture.cs b/LibGit2Sharp.Tests/RemoteFixture.cs index f07ba3ffb..00d15a993 100644 --- a/LibGit2Sharp.Tests/RemoteFixture.cs +++ b/LibGit2Sharp.Tests/RemoteFixture.cs @@ -14,6 +14,7 @@ public void CanGetRemoteOrigin() { Remote origin = repo.Network.Remotes["origin"]; Assert.NotNull(origin); + AssertBelongsToARepository(repo, origin); Assert.Equal("origin", origin.Name); Assert.Equal("c:/GitHub/libgit2sharp/Resources/testrepo.git", origin.Url); } @@ -38,6 +39,7 @@ public void CanEnumerateTheRemotes() foreach (Remote remote in repo.Network.Remotes) { Assert.NotNull(remote); + AssertBelongsToARepository(repo, remote); count++; } @@ -68,6 +70,27 @@ public void CanSetTagFetchMode(TagFetchMode tagFetchMode) } } + [Fact] + public void CanSetRemoteUrl() + { + string path = CloneBareTestRepo(); + using (var repo = new Repository(path)) + { + const string name = "upstream"; + const string url = "https://github.com/libgit2/libgit2sharp.git"; + const string newUrl = "https://github.com/libgit2/libgit2.git"; + + repo.Network.Remotes.Add(name, url); + Remote remote = repo.Network.Remotes[name]; + Assert.NotNull(remote); + + Remote updatedremote = repo.Network.Remotes.Update(remote, + r => r.Url = newUrl); + + Assert.Equal(newUrl, updatedremote.Url); + } + } + [Fact] public void CanCheckEqualityOfRemote() { @@ -236,14 +259,14 @@ public void CanRenameExistingRemote() } [Fact] - public void CanRenameNonExistingRemote() + public void RenamingNonExistingRemoteThrows() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Null(repo.Network.Remotes["i_dont_exist"]); - - repo.Network.Remotes.Rename("i_dont_exist", "i_dont_either", problem => Assert.True(false)); - Assert.Null(repo.Network.Remotes["i_dont_either"]); + Assert.Throws(() => + { + repo.Network.Remotes.Rename("i_dont_exist", "i_dont_either"); + }); } } @@ -304,20 +327,5 @@ public void CanNotRenameWhenRemoteWithSameNameExists() Assert.Throws(() => repo.Network.Remotes.Rename("origin", "upstream")); } } - - [Theory] - [InlineData("git@github.com:org/repo.git", false)] - [InlineData("git://site.com:80/org/repo.git", true)] - [InlineData("ssh://user@host.com:80/org/repo.git", false)] - [InlineData("http://site.com:80/org/repo.git", true)] - [InlineData("https://github.com:80/org/repo.git", true)] - [InlineData("ftp://site.com:80/org/repo.git", false)] - [InlineData("ftps://site.com:80/org/repo.git", false)] - [InlineData("file:///path/repo.git", true)] - [InlineData("protocol://blah.meh/whatever.git", false)] - public void CanCheckIfUrlisSupported(string url, bool supported) - { - Assert.Equal(supported, Remote.IsSupportedUrl(url)); - } } } diff --git a/LibGit2Sharp.Tests/RemoveFixture.cs b/LibGit2Sharp.Tests/RemoveFixture.cs index 04d12feec..fb9f122e7 100644 --- a/LibGit2Sharp.Tests/RemoveFixture.cs +++ b/LibGit2Sharp.Tests/RemoveFixture.cs @@ -54,21 +54,21 @@ public void CanRemoveAnUnalteredFileFromTheIndexWithoutRemovingItFromTheWorkingD string fullpath = Path.Combine(repo.Info.WorkingDirectory, filename); - Assert.Equal(initialStatus, repo.Index.RetrieveStatus(filename)); + Assert.Equal(initialStatus, repo.RetrieveStatus(filename)); Assert.Equal(existsBeforeRemove, File.Exists(fullpath)); if (throws) { - Assert.Throws(() => repo.Index.Remove(filename, removeFromWorkdir)); + Assert.Throws(() => repo.Remove(filename, removeFromWorkdir)); Assert.Equal(count, repo.Index.Count); } else { - repo.Index.Remove(filename, removeFromWorkdir); + repo.Remove(filename, removeFromWorkdir); Assert.Equal(count - 1, repo.Index.Count); Assert.Equal(existsAfterRemove, File.Exists(fullpath)); - Assert.Equal(lastStatus, repo.Index.RetrieveStatus(filename)); + Assert.Equal(lastStatus, repo.RetrieveStatus(filename)); } } } @@ -91,10 +91,10 @@ public void RemovingAModifiedFileWhoseChangesHaveBeenPromotedToTheIndexAndWithAd Assert.Equal(true, File.Exists(fullpath)); File.AppendAllText(fullpath, "additional content"); - Assert.Equal(FileStatus.Staged | FileStatus.Modified, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Staged | FileStatus.Modified, repo.RetrieveStatus(filename)); - Assert.Throws(() => repo.Index.Remove(filename)); - Assert.Throws(() => repo.Index.Remove(filename, false)); + Assert.Throws(() => repo.Remove(filename)); + Assert.Throws(() => repo.Remove(filename, false)); } } @@ -104,16 +104,16 @@ public void CanRemoveAFolderThroughUsageOfPathspecsForNewlyAddedFiles() string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) { - repo.Index.Stage(Touch(repo.Info.WorkingDirectory, "2/subdir1/2.txt", "whone")); - repo.Index.Stage(Touch(repo.Info.WorkingDirectory, "2/subdir1/3.txt", "too")); - repo.Index.Stage(Touch(repo.Info.WorkingDirectory, "2/subdir2/4.txt", "tree")); - repo.Index.Stage(Touch(repo.Info.WorkingDirectory, "2/5.txt", "for")); - repo.Index.Stage(Touch(repo.Info.WorkingDirectory, "2/6.txt", "fyve")); + repo.Stage(Touch(repo.Info.WorkingDirectory, "2/subdir1/2.txt", "whone")); + repo.Stage(Touch(repo.Info.WorkingDirectory, "2/subdir1/3.txt", "too")); + repo.Stage(Touch(repo.Info.WorkingDirectory, "2/subdir2/4.txt", "tree")); + repo.Stage(Touch(repo.Info.WorkingDirectory, "2/5.txt", "for")); + repo.Stage(Touch(repo.Info.WorkingDirectory, "2/6.txt", "fyve")); int count = repo.Index.Count; Assert.True(Directory.Exists(Path.Combine(repo.Info.WorkingDirectory, "2"))); - repo.Index.Remove("2", false); + repo.Remove("2", false); Assert.Equal(count - 5, repo.Index.Count); } @@ -128,7 +128,7 @@ public void CanRemoveAFolderThroughUsageOfPathspecsForFilesAlreadyInTheIndexAndI int count = repo.Index.Count; Assert.True(Directory.Exists(Path.Combine(repo.Info.WorkingDirectory, "1"))); - repo.Index.Remove("1"); + repo.Remove("1"); Assert.False(Directory.Exists(Path.Combine(repo.Info.WorkingDirectory, "1"))); Assert.Equal(count - 1, repo.Index.Count); @@ -145,10 +145,10 @@ public void RemovingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(strin using (var repo = new Repository(StandardTestRepoPath)) { Assert.Null(repo.Index[relativePath]); - Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(status, repo.RetrieveStatus(relativePath)); - repo.Index.Remove(relativePath, i % 2 == 0); - repo.Index.Remove(relativePath, i % 2 == 0, + repo.Remove(relativePath, i % 2 == 0); + repo.Remove(relativePath, i % 2 == 0, new ExplicitPathsOptions {ShouldFailOnUnmatchedPath = false}); } } @@ -164,10 +164,10 @@ public void RemovingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileS using (var repo = new Repository(StandardTestRepoPath)) { Assert.Null(repo.Index[relativePath]); - Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(status, repo.RetrieveStatus(relativePath)); Assert.Throws( - () => repo.Index.Remove(relativePath, i%2 == 0, new ExplicitPathsOptions())); + () => repo.Remove(relativePath, i%2 == 0, new ExplicitPathsOptions())); } } } @@ -177,10 +177,10 @@ public void RemovingFileWithBadParamsThrows() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Throws(() => repo.Index.Remove(string.Empty)); - Assert.Throws(() => repo.Index.Remove((string)null)); - Assert.Throws(() => repo.Index.Remove(new string[] { })); - Assert.Throws(() => repo.Index.Remove(new string[] { null })); + Assert.Throws(() => repo.Remove(string.Empty)); + Assert.Throws(() => repo.Remove((string)null)); + Assert.Throws(() => repo.Remove(new string[] { })); + Assert.Throws(() => repo.Remove(new string[] { null })); } } } diff --git a/LibGit2Sharp.Tests/RepositoryFixture.cs b/LibGit2Sharp.Tests/RepositoryFixture.cs index 7f4d208a0..c74804ffd 100644 --- a/LibGit2Sharp.Tests/RepositoryFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryFixture.cs @@ -4,7 +4,6 @@ using System.Linq; using LibGit2Sharp.Tests.TestHelpers; using Xunit; -using System.Text.RegularExpressions; namespace LibGit2Sharp.Tests { @@ -86,40 +85,6 @@ public void CanCreateStandardRepo() } } - [Fact] - public void CanRetrieveValidVersionString() - { - // Version string format is: - // Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features) - // Example output: - // "0.17.0-unknown-06d772d (x86 - Threads, Https)" - - string versionInfo = Repository.Version; - - // The Repository.Version returned string should contain : - // version:'0.17.0' LibGit2Sharp version number. - // git2SharpHash:'unknown' ( when compiled from source ) else LibGit2Sharp library hash. - // git2hash: '06d772d' LibGit2 library hash. - // arch: 'x86' or 'amd64' LibGit2 target. - // git2Features: 'Threads, Ssh' LibGit2 features compiled with. - string regex = @"^(?\d{1,}\.\d{1,2}\.\d{1,3})-(?\w+)-(?\w+) \((?\w+) - (?(?:\w*(?:, )*\w+)*)\)$"; - - Assert.NotNull(versionInfo); - - Match regexResult = Regex.Match(versionInfo, regex); - - Assert.True(regexResult.Success, "The following version string format is enforced:" + - "Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features)"); - - GroupCollection matchGroups = regexResult.Groups; - - // Check that all groups are valid - foreach(Group group in matchGroups) - { - Assert.True(group.Success); - } - } - [Fact] public void CanCreateStandardRepoAndSpecifyAFolderWhichWillContainTheNewlyCreatedGitDirectory() { @@ -444,7 +409,7 @@ public void CanLookupWhithShortIdentifers() { const string filename = "new.txt"; Touch(repo.Info.WorkingDirectory, filename, "one "); - repo.Index.Stage(filename); + repo.Stage(filename); Signature author = Constants.Signature; Commit commit = repo.Commit("Initial commit", author, author); diff --git a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs index 946d4a31a..e8177a292 100644 --- a/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs +++ b/LibGit2Sharp.Tests/RepositoryOptionsFixture.cs @@ -33,7 +33,7 @@ public void CanOpenABareRepoAsIfItWasAStandardOne() using (var repo = new Repository(BareTestRepoPath, options)) { - var st = repo.Index.RetrieveStatus("1/branch_file.txt"); + var st = repo.RetrieveStatus("1/branch_file.txt"); Assert.Equal(FileStatus.Missing, st); } } @@ -45,7 +45,7 @@ public void CanOpenABareRepoAsIfItWasAStandardOneWithANonExisitingIndexFile() using (var repo = new Repository(BareTestRepoPath, options)) { - var st = repo.Index.RetrieveStatus("1/branch_file.txt"); + var st = repo.RetrieveStatus("1/branch_file.txt"); Assert.Equal(FileStatus.Removed, st); } } @@ -56,7 +56,7 @@ public void CanProvideADifferentWorkDirToAStandardRepo() var path1 = CloneStandardTestRepo(); using (var repo = new Repository(path1)) { - Assert.Equal(FileStatus.Unaltered, repo.Index.RetrieveStatus("1/branch_file.txt")); + Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus("1/branch_file.txt")); } var options = new RepositoryOptions { WorkingDirectoryPath = newWorkdir }; @@ -64,7 +64,7 @@ public void CanProvideADifferentWorkDirToAStandardRepo() var path2 = CloneStandardTestRepo(); using (var repo = new Repository(path2, options)) { - Assert.Equal(FileStatus.Missing, repo.Index.RetrieveStatus("1/branch_file.txt")); + Assert.Equal(FileStatus.Missing, repo.RetrieveStatus("1/branch_file.txt")); } } @@ -74,11 +74,11 @@ public void CanProvideADifferentIndexToAStandardRepo() var path1 = CloneStandardTestRepo(); using (var repo = new Repository(path1)) { - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus("new_untracked_file.txt")); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus("new_untracked_file.txt")); - repo.Index.Stage("new_untracked_file.txt"); + repo.Stage("new_untracked_file.txt"); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus("new_untracked_file.txt")); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus("new_untracked_file.txt")); File.Copy(Path.Combine(repo.Info.Path, "index"), newIndex); } @@ -88,7 +88,7 @@ public void CanProvideADifferentIndexToAStandardRepo() var path2 = CloneStandardTestRepo(); using (var repo = new Repository(path2, options)) { - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus("new_untracked_file.txt")); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus("new_untracked_file.txt")); } } @@ -107,7 +107,7 @@ public void CanSneakAdditionalCommitsIntoAStandardRepoWithoutAlteringTheWorkdirO { Branch head = repo.Head; - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus("zomg.txt")); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus("zomg.txt")); string commitSha = MeanwhileInAnotherDimensionAnEvilMastermindIsAtWork(path); @@ -116,7 +116,7 @@ public void CanSneakAdditionalCommitsIntoAStandardRepoWithoutAlteringTheWorkdirO Assert.NotEqual(head.Tip.Sha, newHead.Tip.Sha); Assert.Equal(commitSha, newHead.Tip.Sha); - Assert.Equal(FileStatus.Removed, repo.Index.RetrieveStatus("zomg.txt")); + Assert.Equal(FileStatus.Removed, repo.RetrieveStatus("zomg.txt")); } } @@ -133,7 +133,7 @@ private string MeanwhileInAnotherDimensionAnEvilMastermindIsAtWork(string workin const string filename = "zomg.txt"; Touch(sneakyRepo.Info.WorkingDirectory, filename, "I'm being sneaked in!\n"); - sneakyRepo.Index.Stage(filename); + sneakyRepo.Stage(filename); return sneakyRepo.Commit("Tadaaaa!", Constants.Signature, Constants.Signature).Sha; } } @@ -194,7 +194,7 @@ public void CanCommitOnBareRepository() { const string relativeFilepath = "test.txt"; Touch(repo.Info.WorkingDirectory, relativeFilepath, "test\n"); - repo.Index.Stage(relativeFilepath); + repo.Stage(relativeFilepath); Assert.NotNull(repo.Commit("Initial commit", Constants.Signature, Constants.Signature)); Assert.Equal(1, repo.Head.Commits.Count()); diff --git a/LibGit2Sharp.Tests/ResetHeadFixture.cs b/LibGit2Sharp.Tests/ResetHeadFixture.cs index 870e46c39..ed76df8f0 100644 --- a/LibGit2Sharp.Tests/ResetHeadFixture.cs +++ b/LibGit2Sharp.Tests/ResetHeadFixture.cs @@ -114,7 +114,7 @@ private void AssertSoftReset(Func branchIdentifierRetriever, boo Assert.Equal(expectedHeadName, repo.Head.Name); Assert.Equal(tag.Target.Id, repo.Head.Tip.Id); - Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus("a.txt")); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("a.txt")); AssertRefLogEntry(repo, "HEAD", tag.Target.Id, @@ -134,7 +134,7 @@ private void AssertSoftReset(Func branchIdentifierRetriever, boo Assert.Equal(expectedHeadName, repo.Head.Name); Assert.Equal(branch.Tip.Sha, repo.Head.Tip.Sha); - Assert.Equal(FileStatus.Unaltered, repo.Index.RetrieveStatus("a.txt")); + Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus("a.txt")); AssertRefLogEntry(repo, "HEAD", branch.Tip.Id, @@ -156,12 +156,12 @@ private void AssertSoftReset(Func branchIdentifierRetriever, boo private static void FeedTheRepository(IRepository repo) { string fullPath = Touch(repo.Info.WorkingDirectory, "a.txt", "Hello\n"); - repo.Index.Stage(fullPath); + repo.Stage(fullPath); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); repo.ApplyTag("mytag"); File.AppendAllText(fullPath, "World\n"); - repo.Index.Stage(fullPath); + repo.Stage(fullPath); Signature shiftedSignature = Constants.Signature.TimeShift(TimeSpan.FromMinutes(1)); repo.Commit("Update file", shiftedSignature, shiftedSignature); @@ -169,7 +169,7 @@ private static void FeedTheRepository(IRepository repo) repo.Checkout("mybranch"); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); } [Fact] @@ -187,7 +187,7 @@ public void MixedResetRefreshesTheIndex() repo.Reset(ResetMode.Mixed, tag.CanonicalName); - Assert.Equal(FileStatus.Modified, repo.Index.RetrieveStatus("a.txt")); + Assert.Equal(FileStatus.Modified, repo.RetrieveStatus("a.txt")); AssertRefLogEntry(repo, "HEAD", tag.Target.Id, diff --git a/LibGit2Sharp.Tests/ResetIndexFixture.cs b/LibGit2Sharp.Tests/ResetIndexFixture.cs index 9cc7849ac..ebfdd20f5 100644 --- a/LibGit2Sharp.Tests/ResetIndexFixture.cs +++ b/LibGit2Sharp.Tests/ResetIndexFixture.cs @@ -64,14 +64,14 @@ public void ResetTheIndexWithTheHeadUnstagesEverything() string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) { - RepositoryStatus oldStatus = repo.Index.RetrieveStatus(); + RepositoryStatus oldStatus = repo.RetrieveStatus(); Assert.Equal(3, oldStatus.Where(IsStaged).Count()); var reflogEntriesCount = repo.Refs.Log(repo.Refs.Head).Count(); repo.Reset(); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.Where(IsStaged).Count()); // Assert that no reflog entry is created @@ -87,7 +87,7 @@ public void CanResetTheIndexToTheContentOfACommitWithCommittishAsArgument() { repo.Reset("be3563a"); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); var expected = new[] { "1.txt", Path.Combine("1", "branch_file.txt"), "deleted_staged_file.txt", "deleted_unstaged_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt" }; @@ -105,7 +105,7 @@ public void CanResetTheIndexToTheContentOfACommitWithCommitAsArgument() { repo.Reset(repo.Lookup("be3563a")); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); var expected = new[] { "1.txt", Path.Combine("1", "branch_file.txt"), "deleted_staged_file.txt", "deleted_unstaged_file.txt", "modified_staged_file.txt", "modified_unstaged_file.txt" }; @@ -157,10 +157,10 @@ public void CanResetTheIndexWhenARenameExists() { using (var repo = new Repository(CloneStandardTestRepo())) { - repo.Index.Move("branch_file.txt", "renamed_branch_file.txt"); + repo.Move("branch_file.txt", "renamed_branch_file.txt"); repo.Reset(repo.Lookup("32eab9c")); - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(0, status.Where(IsStaged).Count()); } } @@ -170,16 +170,16 @@ public void CanResetSourceOfARenameInIndex() { using (var repo = new Repository(CloneStandardTestRepo())) { - repo.Index.Move("branch_file.txt", "renamed_branch_file.txt"); + repo.Move("branch_file.txt", "renamed_branch_file.txt"); - RepositoryStatus oldStatus = repo.Index.RetrieveStatus(); + RepositoryStatus oldStatus = repo.RetrieveStatus(); Assert.Equal(1, oldStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.Nonexistent, oldStatus["branch_file.txt"].State); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); repo.Reset(repo.Lookup("32eab9c"), new string[] { "branch_file.txt" }); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.Missing, newStatus["branch_file.txt"].State); Assert.Equal(FileStatus.Added, newStatus["renamed_branch_file.txt"].State); @@ -191,15 +191,15 @@ public void CanResetTargetOfARenameInIndex() { using (var repo = new Repository(CloneStandardTestRepo())) { - repo.Index.Move("branch_file.txt", "renamed_branch_file.txt"); + repo.Move("branch_file.txt", "renamed_branch_file.txt"); - RepositoryStatus oldStatus = repo.Index.RetrieveStatus(); + RepositoryStatus oldStatus = repo.RetrieveStatus(); Assert.Equal(1, oldStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); repo.Reset(repo.Lookup("32eab9c"), new string[] { "renamed_branch_file.txt" }); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.Untracked, newStatus["renamed_branch_file.txt"].State); Assert.Equal(FileStatus.Removed, newStatus["branch_file.txt"].State); diff --git a/LibGit2Sharp.Tests/RevertFixture.cs b/LibGit2Sharp.Tests/RevertFixture.cs index 046179b17..7f60431f9 100644 --- a/LibGit2Sharp.Tests/RevertFixture.cs +++ b/LibGit2Sharp.Tests/RevertFixture.cs @@ -37,7 +37,7 @@ public void CanRevert() // Verify workspace is clean. Assert.True(repo.Index.IsFullyMerged); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Lookup the blob containing the expected reverted content of a.txt. Blob expectedBlob = repo.Lookup("bc90ea420cf6c5ae3db7dcdffa0d79df567f219b"); @@ -82,7 +82,7 @@ public void CanRevertAndNotCommit() Assert.Null(result.Commit); // Verify workspace is dirty. - FileStatus fileStatus = repo.Index.RetrieveStatus(revertedFile); + FileStatus fileStatus = repo.RetrieveStatus(revertedFile); Assert.Equal(FileStatus.Staged, fileStatus); // This is the ID of the blob containing the expected content. @@ -131,8 +131,8 @@ public void RevertWithConflictDoesNotCommit() Assert.NotNull(repo.Index.Conflicts["a.txt"]); // Verify the non-conflicting paths are staged. - Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus("b.txt")); - Assert.Equal(FileStatus.Staged, repo.Index.RetrieveStatus("c.txt")); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("b.txt")); + Assert.Equal(FileStatus.Staged, repo.RetrieveStatus("c.txt")); } } @@ -344,7 +344,7 @@ public void CanRevertMergeCommit(int mainline, string expectedId) { // In this case, we expect "d_renamed.txt" to be reverted (deleted), // and a.txt to match the tip of the "revert" branch. - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus("d_renamed.txt")); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus("d_renamed.txt")); // This is the commit containing the expected contents of a.txt. Commit commit = repo.Lookup("b6fbb29b625aabe0fb5736da6fd61d4147e4405e"); @@ -358,7 +358,7 @@ public void CanRevertMergeCommit(int mainline, string expectedId) // In this case, we expect "d_renamed.txt" to be reverted (deleted), // and a.txt to match the tip of the "revert" branch. - Assert.Equal(FileStatus.Unaltered, repo.Index.RetrieveStatus("d_renamed.txt")); + Assert.Equal(FileStatus.Unaltered, repo.RetrieveStatus("d_renamed.txt")); // This is the commit containing the expected contents of "d_renamed.txt". Commit commit = repo.Lookup("c4b5cea70e4cd5b633ed0f10ae0ed5384e8190d8"); @@ -391,5 +391,72 @@ public void CanNotRevertAMergeCommitWithoutSpecifyingTheMainlineBranch() Assert.Throws(() => repo.Revert(commitToRevert, Constants.Signature)); } } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RevertWithNothingToRevert(bool commitOnSuccess) + { + // The branch name to perform the revert on + const string revertBranchName = "refs/heads/revert"; + + string path = CloneRevertTestRepo(); + using (var repo = new Repository(path)) + { + // Checkout the revert branch. + Branch branch = repo.Checkout(revertBranchName); + Assert.NotNull(branch); + + Commit commitToRevert = repo.Head.Tip; + + // Revert tip commit. + RevertResult result = repo.Revert(commitToRevert, Constants.Signature); + Assert.NotNull(result); + Assert.Equal(RevertStatus.Reverted, result.Status); + + // Revert the same commit a second time + result = repo.Revert( + commitToRevert, + Constants.Signature, + new RevertOptions() { CommitOnSuccess = commitOnSuccess }); + + Assert.NotNull(result); + Assert.Equal(null, result.Commit); + Assert.Equal(RevertStatus.NothingToRevert, result.Status); + + if (commitOnSuccess) + { + Assert.Equal(CurrentOperation.None, repo.Info.CurrentOperation); + } + else + { + Assert.Equal(CurrentOperation.Revert, repo.Info.CurrentOperation); + } + } + } + + [Fact] + public void RevertOrphanedBranchThrows() + { + // The branch name to perform the revert on + const string revertBranchName = "refs/heads/revert"; + + string path = CloneRevertTestRepo(); + using (var repo = new Repository(path)) + { + // Checkout the revert branch. + Branch branch = repo.Checkout(revertBranchName); + Assert.NotNull(branch); + + Commit commitToRevert = repo.Head.Tip; + + // Move the HEAD to an orphaned branch. + repo.Refs.UpdateTarget("HEAD", "refs/heads/orphan"); + Assert.True(repo.Info.IsHeadUnborn); + + // Revert the tip of the refs/heads/revert branch. + Assert.Throws(() => repo.Revert(commitToRevert, Constants.Signature)); + } + } } } diff --git a/LibGit2Sharp.Tests/SignatureFixture.cs b/LibGit2Sharp.Tests/SignatureFixture.cs index 978141837..e40cabd6c 100644 --- a/LibGit2Sharp.Tests/SignatureFixture.cs +++ b/LibGit2Sharp.Tests/SignatureFixture.cs @@ -35,13 +35,7 @@ public void CreatingASignatureWithBadParamsThrows() Assert.Throws(() => new Signature(null, "me@there.com", DateTimeOffset.Now)); Assert.Throws(() => new Signature(string.Empty, "me@there.com", DateTimeOffset.Now)); Assert.Throws(() => new Signature("Me", null, DateTimeOffset.Now)); - } - - [Fact] - public void CanCreateASignatureWithAnEmptyEmail() - { - var sig = new Signature("Me", string.Empty, DateTimeOffset.Now); - Assert.Equal(string.Empty, sig.Email); + Assert.Throws(() => new Signature("Me", string.Empty, DateTimeOffset.Now)); } } } diff --git a/LibGit2Sharp.Tests/SmartSubtransportFixture.cs b/LibGit2Sharp.Tests/SmartSubtransportFixture.cs new file mode 100644 index 000000000..4dbbd9b0f --- /dev/null +++ b/LibGit2Sharp.Tests/SmartSubtransportFixture.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class SmartSubtransportFixture : BaseFixture + { + // Warning: this certification validation callback will accept *all* + // SSL certificates without validation. This is *only* for testing. + // Do *NOT* enable this in production. + private readonly RemoteCertificateValidationCallback certificateValidationCallback = + (sender, certificate, chain, errors) => { return true; }; + + [Theory] + [InlineData("http", "http://github.com/libgit2/TestGitRepository")] + [InlineData("https", "https://github.com/libgit2/TestGitRepository")] + public void CustomSmartSubtransportTest(string scheme, string url) + { + string remoteName = "testRemote"; + + var scd = BuildSelfCleaningDirectory(); + var repoPath = Repository.Init(scd.RootedDirectoryPath); + + SmartSubtransportRegistration registration = null; + + try + { + // Disable server certificate validation for testing. + // Do *NOT* enable this in production. + ServicePointManager.ServerCertificateValidationCallback = certificateValidationCallback; + + registration = GlobalSettings.RegisterSmartSubtransport(scheme); + Assert.NotNull(registration); + + using (var repo = new Repository(scd.DirectoryPath)) + { + Remote remote = repo.Network.Remotes.Add(remoteName, url); + + // Set up structures for the expected results + // and verifying the RemoteUpdateTips callback. + TestRemoteInfo expectedResults = TestRemoteInfo.TestRemoteInstance; + ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName); + + // Add expected branch objects + foreach (KeyValuePair kvp in expectedResults.BranchTips) + { + expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value); + } + + // Add the expected tags + string[] expectedTagNames = { "blob", "commit_tree" }; + foreach (string tagName in expectedTagNames) + { + TestRemoteInfo.ExpectedTagInfo expectedTagInfo = expectedResults.Tags[tagName]; + expectedFetchState.AddExpectedTag(tagName, ObjectId.Zero, expectedTagInfo); + } + + // Perform the actual fetch + repo.Network.Fetch(remote, new FetchOptions { OnUpdateTips = expectedFetchState.RemoteUpdateTipsHandler, TagFetchMode = TagFetchMode.Auto }); + + // Verify the expected + expectedFetchState.CheckUpdatedReferences(repo); + } + } + finally + { + GlobalSettings.UnregisterSmartSubtransport(registration); + + ServicePointManager.ServerCertificateValidationCallback -= certificateValidationCallback; + } + } + + [Fact] + public void CannotReregisterScheme() + { + SmartSubtransportRegistration httpRegistration = + GlobalSettings.RegisterSmartSubtransport("http"); + + try + { + Assert.Throws(() => + GlobalSettings.RegisterSmartSubtransport("http")); + } + finally + { + GlobalSettings.UnregisterSmartSubtransport(httpRegistration); + } + } + + [Fact] + public void CannotUnregisterTwice() + { + SmartSubtransportRegistration httpRegistration = + GlobalSettings.RegisterSmartSubtransport("http"); + + GlobalSettings.UnregisterSmartSubtransport(httpRegistration); + + Assert.Throws(() => + GlobalSettings.UnregisterSmartSubtransport(httpRegistration)); + } + + private class MockSmartSubtransport : RpcSmartSubtransport + { + protected override SmartSubtransportStream Action(String url, GitSmartSubtransportAction action) + { + String endpointUrl, contentType = null; + bool isPost = false; + + switch (action) + { + case GitSmartSubtransportAction.UploadPackList: + endpointUrl = String.Concat(url, "/info/refs?service=git-upload-pack"); + break; + + case GitSmartSubtransportAction.UploadPack: + endpointUrl = String.Concat(url, "/git-upload-pack"); + contentType = "application/x-git-upload-pack-request"; + isPost = true; + break; + + case GitSmartSubtransportAction.ReceivePackList: + endpointUrl = String.Concat(url, "/info/refs?service=git-receive-pack"); + break; + + case GitSmartSubtransportAction.ReceivePack: + endpointUrl = String.Concat(url, "/git-receive-pack"); + contentType = "application/x-git-receive-pack-request"; + isPost = true; + break; + + default: + throw new InvalidOperationException(); + } + + return new MockSmartSubtransportStream(this, endpointUrl, isPost, contentType); + } + + private class MockSmartSubtransportStream : SmartSubtransportStream + { + private static int MAX_REDIRECTS = 5; + + private MemoryStream postBuffer = new MemoryStream(); + private Stream responseStream; + + public MockSmartSubtransportStream(MockSmartSubtransport parent, string endpointUrl, bool isPost, string contentType) + : base(parent) + { + EndpointUrl = endpointUrl; + IsPost = isPost; + ContentType = contentType; + } + + private string EndpointUrl + { + get; + set; + } + + private bool IsPost + { + get; + set; + } + + private string ContentType + { + get; + set; + } + + public override int Write(Stream dataStream, long length) + { + byte[] buffer = new byte[4096]; + long writeTotal = 0; + + while (length > 0) + { + int readLen = dataStream.Read(buffer, 0, (int)Math.Min(buffer.Length, length)); + + if (readLen == 0) + { + break; + } + + postBuffer.Write(buffer, 0, readLen); + length -= readLen; + writeTotal += readLen; + } + + if (writeTotal < length) + { + throw new EndOfStreamException("Could not write buffer (short read)"); + } + + return 0; + } + + private static HttpWebRequest CreateWebRequest(string endpointUrl, bool isPost, string contentType) + { + HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(endpointUrl); + webRequest.UserAgent = "git/1.0 (libgit2 custom transport)"; + webRequest.ServicePoint.Expect100Continue = false; + webRequest.AllowAutoRedirect = false; + + if (isPost) + { + webRequest.Method = "POST"; + webRequest.ContentType = contentType; + } + + return webRequest; + } + + private HttpWebResponse GetResponseWithRedirects() + { + HttpWebRequest request = CreateWebRequest(EndpointUrl, IsPost, ContentType); + HttpWebResponse response = null; + + for (int i = 0; i < MAX_REDIRECTS; i++) + { + if (IsPost && postBuffer.Length > 0) + { + postBuffer.Seek(0, SeekOrigin.Begin); + + using (Stream requestStream = request.GetRequestStream()) + { + postBuffer.WriteTo(requestStream); + } + } + + response = (HttpWebResponse)request.GetResponse(); + + if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect) + { + request = CreateWebRequest(response.Headers["Location"], IsPost, ContentType); + continue; + } + + break; + } + + if (response == null) + { + throw new Exception("Too many redirects"); + } + + return response; + } + + public override int Read(Stream dataStream, long length, out long readTotal) + { + byte[] buffer = new byte[4096]; + readTotal = 0; + + if (responseStream == null) + { + HttpWebResponse response = GetResponseWithRedirects(); + responseStream = response.GetResponseStream(); + } + + while (length > 0) + { + int readLen = responseStream.Read(buffer, 0, (int)Math.Min(buffer.Length, length)); + + if (readLen == 0) + break; + + dataStream.Write(buffer, 0, readLen); + readTotal += readLen; + length -= readLen; + } + + return 0; + } + + protected override void Free() + { + if (responseStream != null) + { + responseStream.Dispose(); + responseStream = null; + } + + base.Free(); + } + } + } + } +} diff --git a/LibGit2Sharp.Tests/StageFixture.cs b/LibGit2Sharp.Tests/StageFixture.cs index 3355645f0..a6173719e 100644 --- a/LibGit2Sharp.Tests/StageFixture.cs +++ b/LibGit2Sharp.Tests/StageFixture.cs @@ -23,13 +23,13 @@ public void CanStage(string relativePath, FileStatus currentStatus, bool doesCur { int count = repo.Index.Count; Assert.Equal(doesCurrentlyExistInTheIndex, (repo.Index[relativePath] != null)); - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); - repo.Index.Stage(relativePath); + repo.Stage(relativePath); Assert.Equal(count + expectedIndexCountVariation, repo.Index.Count); Assert.Equal(doesExistInTheIndexOnceStaged, (repo.Index[relativePath] != null)); - Assert.Equal(expectedStatusOnceStaged, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(expectedStatusOnceStaged, repo.RetrieveStatus(relativePath)); } } @@ -43,17 +43,17 @@ public void CanStageTheUpdationOfAStagedFile() const string filename = "new_tracked_file.txt"; IndexEntry blob = repo.Index[filename]; - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); Touch(repo.Info.WorkingDirectory, filename, "brand new content"); - Assert.Equal(FileStatus.Added | FileStatus.Modified, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added | FileStatus.Modified, repo.RetrieveStatus(filename)); - repo.Index.Stage(filename); + repo.Stage(filename); IndexEntry newBlob = repo.Index[filename]; Assert.Equal(count, repo.Index.Count); Assert.NotEqual(newBlob.Id, blob.Id); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); } } @@ -65,9 +65,9 @@ public void StagingAnUnknownFileThrowsIfExplicitPath(string relativePath, FileSt using (var repo = new Repository(StandardTestRepoPath)) { Assert.Null(repo.Index[relativePath]); - Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(status, repo.RetrieveStatus(relativePath)); - Assert.Throws(() => repo.Index.Stage(relativePath, new StageOptions { ExplicitPathsOptions = new ExplicitPathsOptions() })); + Assert.Throws(() => repo.Stage(relativePath, new StageOptions { ExplicitPathsOptions = new ExplicitPathsOptions() })); } } @@ -79,12 +79,12 @@ public void CanStageAnUnknownFileWithLaxUnmatchedExplicitPathsValidation(string using (var repo = new Repository(StandardTestRepoPath)) { Assert.Null(repo.Index[relativePath]); - Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(status, repo.RetrieveStatus(relativePath)); - Assert.DoesNotThrow(() => repo.Index.Stage(relativePath)); - Assert.DoesNotThrow(() => repo.Index.Stage(relativePath, new StageOptions { ExplicitPathsOptions = new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false } })); + Assert.DoesNotThrow(() => repo.Stage(relativePath)); + Assert.DoesNotThrow(() => repo.Stage(relativePath, new StageOptions { ExplicitPathsOptions = new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false } })); - Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(status, repo.RetrieveStatus(relativePath)); } } @@ -96,10 +96,10 @@ public void StagingAnUnknownFileWithLaxExplicitPathsValidationDoesntThrow(string using (var repo = new Repository(StandardTestRepoPath)) { Assert.Null(repo.Index[relativePath]); - Assert.Equal(status, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(status, repo.RetrieveStatus(relativePath)); - repo.Index.Stage(relativePath); - repo.Index.Stage(relativePath, new StageOptions { ExplicitPathsOptions = new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false } }); + repo.Stage(relativePath); + repo.Stage(relativePath, new StageOptions { ExplicitPathsOptions = new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false } }); } } @@ -113,16 +113,16 @@ public void CanStageTheRemovalOfAStagedFile() const string filename = "new_tracked_file.txt"; Assert.NotNull(repo.Index[filename]); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); File.Delete(Path.Combine(repo.Info.WorkingDirectory, filename)); - Assert.Equal(FileStatus.Added | FileStatus.Missing, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added | FileStatus.Missing, repo.RetrieveStatus(filename)); - repo.Index.Stage(filename); + repo.Stage(filename); Assert.Null(repo.Index[filename]); Assert.Equal(count - 1, repo.Index.Count); - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus(filename)); } } @@ -135,22 +135,22 @@ public void CanStageANewFileInAPersistentManner(string filename) string path = CloneStandardTestRepo(); using (var repo = new Repository(path)) { - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus(filename)); Assert.Null(repo.Index[filename]); Touch(repo.Info.WorkingDirectory, filename, "some contents"); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(filename)); Assert.Null(repo.Index[filename]); - repo.Index.Stage(filename); + repo.Stage(filename); Assert.NotNull(repo.Index[filename]); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); } using (var repo = new Repository(path)) { Assert.NotNull(repo.Index[filename]); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); } } @@ -190,10 +190,10 @@ private static void AssertStage(bool? ignorecase, IRepository repo, string path) { try { - repo.Index.Stage(path); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(path)); + repo.Stage(path); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(path)); repo.Reset(); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(path)); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(path)); } catch (ArgumentException) { @@ -213,7 +213,7 @@ public void CanStageANewFileWithARelativePathContainingNativeDirectorySeparatorC Touch(repo.Info.WorkingDirectory, file, "With backward slash on Windows!"); - repo.Index.Stage(file); + repo.Stage(file); Assert.Equal(count + 1, repo.Index.Count); @@ -232,7 +232,7 @@ public void StagingANewFileWithAFullPathWhichEscapesOutOfTheWorkingDirThrows() { string fullPath = Touch(scd.RootedDirectoryPath, "unit_test.txt", "some contents"); - Assert.Throws(() => repo.Index.Stage(fullPath)); + Assert.Throws(() => repo.Stage(fullPath)); } } @@ -241,10 +241,10 @@ public void StagingFileWithBadParamsThrows() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Throws(() => repo.Index.Stage(string.Empty)); - Assert.Throws(() => repo.Index.Stage((string)null)); - Assert.Throws(() => repo.Index.Stage(new string[] { })); - Assert.Throws(() => repo.Index.Stage(new string[] { null })); + Assert.Throws(() => repo.Stage(string.Empty)); + Assert.Throws(() => repo.Stage((string)null)); + Assert.Throws(() => repo.Stage(new string[] { })); + Assert.Throws(() => repo.Stage(new string[] { null })); } } @@ -280,7 +280,7 @@ public void CanStageWithPathspec(string relativePath, int expectedIndexCountVari { int count = repo.Index.Count; - repo.Index.Stage(relativePath); + repo.Stage(relativePath); Assert.Equal(count + expectedIndexCountVariation, repo.Index.Count); } @@ -293,7 +293,7 @@ public void CanStageWithMultiplePathspecs() { int count = repo.Index.Count; - repo.Index.Stage(new string[] { "*", "u*" }); + repo.Stage(new string[] { "*", "u*" }); Assert.Equal(count, repo.Index.Count); // 1 added file, 1 deleted file, so same count } @@ -309,9 +309,9 @@ public void CanIgnoreIgnoredPaths(string path) Touch(repo.Info.WorkingDirectory, ".gitignore", "ignored_file.txt\nignored_folder/\n"); Touch(repo.Info.WorkingDirectory, path, "This file is ignored."); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(path)); - repo.Index.Stage("*"); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(path)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(path)); + repo.Stage("*"); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(path)); } } @@ -325,9 +325,9 @@ public void CanStageIgnoredPaths(string path) Touch(repo.Info.WorkingDirectory, ".gitignore", "ignored_file.txt\nignored_folder/\n"); Touch(repo.Info.WorkingDirectory, path, "This file is ignored."); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(path)); - repo.Index.Stage(path, new StageOptions { IncludeIgnored = true }); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(path)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(path)); + repo.Stage(path, new StageOptions { IncludeIgnored = true }); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(path)); } } } diff --git a/LibGit2Sharp.Tests/StashFixture.cs b/LibGit2Sharp.Tests/StashFixture.cs index 2bdf4e33c..14fc38449 100644 --- a/LibGit2Sharp.Tests/StashFixture.cs +++ b/LibGit2Sharp.Tests/StashFixture.cs @@ -29,7 +29,7 @@ public void CanAddAndRemoveStash() { var stasher = Constants.Signature; - Assert.True(repo.Index.RetrieveStatus().IsDirty); + Assert.True(repo.RetrieveStatus().IsDirty); Stash stash = repo.Stashes.Add(stasher, "My very first stash", StashModifiers.IncludeUntracked); @@ -45,7 +45,7 @@ public void CanAddAndRemoveStash() var stashRef = repo.Refs["refs/stash"]; Assert.Equal(stash.WorkTree.Sha, stashRef.TargetIdentifier); - Assert.False(repo.Index.RetrieveStatus().IsDirty); + Assert.False(repo.RetrieveStatus().IsDirty); // Create extra file untrackedFilename = "stash_candidate.txt"; @@ -139,18 +139,18 @@ public void CanStashWithoutOptions() const string staged = "staged_file_path.txt"; Touch(repo.Info.WorkingDirectory, staged, "I'm staged\n"); - repo.Index.Stage(staged); + repo.Stage(staged); Stash stash = repo.Stashes.Add(stasher, "Stash with default options", StashModifiers.Default); Assert.NotNull(stash); //It should not keep staged files - Assert.Equal(FileStatus.Nonexistent, repo.Index.RetrieveStatus(staged)); + Assert.Equal(FileStatus.Nonexistent, repo.RetrieveStatus(staged)); Assert.NotNull(stash.Index[staged]); //It should leave untracked files untracked - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(untracked)); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus(untracked)); Assert.Null(stash.Untracked); } } @@ -165,13 +165,13 @@ public void CanStashAndKeepIndex() const string filename = "staged_file_path.txt"; Touch(repo.Info.WorkingDirectory, filename, "I'm staged\n"); - repo.Index.Stage(filename); + repo.Stage(filename); Stash stash = repo.Stashes.Add(stasher, "This stash will keep index", StashModifiers.KeepIndex); Assert.NotNull(stash); Assert.NotNull(stash.Index[filename]); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(filename)); Assert.Null(stash.Untracked); } } @@ -186,7 +186,7 @@ public void CanStashIgnoredFiles() const string ignoredFilename = "ignored_file.txt"; Touch(repo.Info.WorkingDirectory, gitIgnore, ignoredFilename); - repo.Index.Stage(gitIgnore); + repo.Stage(gitIgnore); repo.Commit("Modify gitignore", Constants.Signature, Constants.Signature); Touch(repo.Info.WorkingDirectory, ignoredFilename, "I'm ignored\n"); diff --git a/LibGit2Sharp.Tests/StatusFixture.cs b/LibGit2Sharp.Tests/StatusFixture.cs index b70ff0ae0..a7086d857 100644 --- a/LibGit2Sharp.Tests/StatusFixture.cs +++ b/LibGit2Sharp.Tests/StatusFixture.cs @@ -15,7 +15,7 @@ public void CanRetrieveTheStatusOfAFile() { using (var repo = new Repository(StandardTestRepoPath)) { - FileStatus status = repo.Index.RetrieveStatus("new_tracked_file.txt"); + FileStatus status = repo.RetrieveStatus("new_tracked_file.txt"); Assert.Equal(FileStatus.Added, status); } } @@ -32,7 +32,7 @@ public void CanLimitStatusToWorkDirOnly(StatusShowOption show, FileStatus expect { Touch(repo.Info.WorkingDirectory, "file.txt", "content"); - RepositoryStatus status = repo.Index.RetrieveStatus(new StatusOptions() { Show = show }); + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { Show = show }); Assert.Equal(expected, status["file.txt"].State); } } @@ -48,9 +48,9 @@ public void CanLimitStatusToIndexOnly(StatusShowOption show, FileStatus expected using (var repo = new Repository(clone)) { Touch(repo.Info.WorkingDirectory, "file.txt", "content"); - repo.Index.Stage("file.txt"); + repo.Stage("file.txt"); - RepositoryStatus status = repo.Index.RetrieveStatus(new StatusOptions() { Show = show }); + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { Show = show }); Assert.Equal(expected, status["file.txt"].State); } } @@ -87,7 +87,7 @@ public void CanRetrieveTheStatusOfAnUntrackedFile(string filePath) { Touch(repo.Info.WorkingDirectory, filePath, "content"); - FileStatus status = repo.Index.RetrieveStatus(filePath); + FileStatus status = repo.RetrieveStatus(filePath); Assert.Equal(FileStatus.Untracked, status); } } @@ -97,7 +97,7 @@ public void RetrievingTheStatusOfADirectoryThrows() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Throws(() => { FileStatus status = repo.Index.RetrieveStatus("1"); }); + Assert.Throws(() => { FileStatus status = repo.RetrieveStatus("1"); }); } } @@ -109,7 +109,7 @@ public void CanRetrieveTheStatusOfTheWholeWorkingDirectory() { const string file = "modified_staged_file.txt"; - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(FileStatus.Staged, status[file].State); @@ -127,9 +127,9 @@ public void CanRetrieveTheStatusOfTheWholeWorkingDirectory() File.AppendAllText(Path.Combine(repo.Info.WorkingDirectory, file), "Tclem's favorite commit message: boom"); - Assert.Equal(FileStatus.Staged | FileStatus.Modified, repo.Index.RetrieveStatus(file)); + Assert.Equal(FileStatus.Staged | FileStatus.Modified, repo.RetrieveStatus(file)); - RepositoryStatus status2 = repo.Index.RetrieveStatus(); + RepositoryStatus status2 = repo.RetrieveStatus(); Assert.Equal(FileStatus.Staged | FileStatus.Modified, status2[file].State); Assert.NotNull(status2); @@ -157,12 +157,12 @@ public void CanRetrieveTheStatusOfRenamedFilesInWorkDir() "This is a file with enough data to trigger similarity matching.\r\n" + "This is a file with enough data to trigger similarity matching.\r\n"); - repo.Index.Stage("old_name.txt"); + repo.Stage("old_name.txt"); File.Move(Path.Combine(repo.Info.WorkingDirectory, "old_name.txt"), Path.Combine(repo.Info.WorkingDirectory, "rename_target.txt")); - RepositoryStatus status = repo.Index.RetrieveStatus( + RepositoryStatus status = repo.RetrieveStatus( new StatusOptions() { DetectRenamesInIndex = true, @@ -184,10 +184,10 @@ public void CanRetrieveTheStatusOfRenamedFilesInIndex() Path.Combine(repo.Info.WorkingDirectory, "1.txt"), Path.Combine(repo.Info.WorkingDirectory, "rename_target.txt")); - repo.Index.Stage("1.txt"); - repo.Index.Stage("rename_target.txt"); + repo.Stage("1.txt"); + repo.Stage("rename_target.txt"); - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(FileStatus.RenamedInIndex, status["rename_target.txt"].State); Assert.Equal(100, status["rename_target.txt"].HeadToIndexRenameDetails.Similarity); @@ -206,7 +206,7 @@ public void CanDetectedVariousKindsOfRenaming() "This is a file with enough data to trigger similarity matching.\r\n" + "This is a file with enough data to trigger similarity matching.\r\n"); - repo.Index.Stage("file.txt"); + repo.Stage("file.txt"); repo.Commit("Initial commit", Constants.Signature, Constants.Signature); File.Move(Path.Combine(repo.Info.WorkingDirectory, "file.txt"), @@ -218,22 +218,22 @@ public void CanDetectedVariousKindsOfRenaming() DetectRenamesInWorkDir = true }; - RepositoryStatus status = repo.Index.RetrieveStatus(opts); + RepositoryStatus status = repo.RetrieveStatus(opts); // This passes as expected Assert.Equal(FileStatus.RenamedInWorkDir, status.Single().State); - repo.Index.Stage("file.txt"); - repo.Index.Stage("renamed.txt"); + repo.Stage("file.txt"); + repo.Stage("renamed.txt"); - status = repo.Index.RetrieveStatus(opts); + status = repo.RetrieveStatus(opts); Assert.Equal(FileStatus.RenamedInIndex, status.Single().State); File.Move(Path.Combine(repo.Info.WorkingDirectory, "renamed.txt"), Path.Combine(repo.Info.WorkingDirectory, "renamed_again.txt")); - status = repo.Index.RetrieveStatus(opts); + status = repo.RetrieveStatus(opts); Assert.Equal(FileStatus.RenamedInWorkDir | FileStatus.RenamedInIndex, status.Single().State); @@ -247,7 +247,7 @@ public void CanRetrieveTheStatusOfANewRepository() using (var repo = new Repository(repoPath)) { - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.NotNull(status); Assert.Equal(0, status.Count()); Assert.False(status.IsDirty); @@ -275,10 +275,10 @@ public void RetrievingTheStatusOfARepositoryReturnNativeFilePaths() Touch(repo.Info.WorkingDirectory, relFilePath, "Anybody out there?"); // Add the file to the index - repo.Index.Stage(relFilePath); + repo.Stage(relFilePath); // Get the repository status - RepositoryStatus repoStatus = repo.Index.RetrieveStatus(); + RepositoryStatus repoStatus = repo.RetrieveStatus(); Assert.Equal(1, repoStatus.Count()); StatusEntry statusEntry = repoStatus.Single(); @@ -299,15 +299,15 @@ public void RetrievingTheStatusOfAnEmptyRepositoryHonorsTheGitIgnoreDirectives() const string relativePath = "look-ma.txt"; Touch(repo.Info.WorkingDirectory, relativePath, "I'm going to be ignored!"); - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(new[] { relativePath }, status.Untracked.Select(s => s.FilePath)); Touch(repo.Info.WorkingDirectory, ".gitignore", "*.txt" + Environment.NewLine); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(".gitignore", newStatus.Untracked.Select(s => s.FilePath).Single()); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); Assert.Equal(new[] { relativePath }, newStatus.Ignored.Select(s => s.FilePath)); } } @@ -352,7 +352,7 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectives() * # new_untracked_file.txt */ - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(new[] { relativePath, "new_untracked_file.txt" }, status.Untracked.Select(s => s.FilePath)); @@ -393,10 +393,10 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectives() * # new_untracked_file.txt */ - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(".gitignore", newStatus.Untracked.Select(s => s.FilePath).Single()); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); Assert.Equal(new[] { relativePath, "new_untracked_file.txt" }, newStatus.Ignored.Select(s => s.FilePath)); } } @@ -412,7 +412,7 @@ public void RetrievingTheStatusOfAnAmbiguousFileThrows() string relativePath = Path.Combine("1", "ambiguous[1].txt"); Touch(repo.Info.WorkingDirectory, relativePath, "Brackets all the way."); - Assert.Throws(() => repo.Index.RetrieveStatus(relativePath)); + Assert.Throws(() => repo.RetrieveStatus(relativePath)); } } @@ -436,7 +436,7 @@ FileStatus expectedCamelCasedFileStatus lowerCasedPath = Touch(repo.Info.WorkingDirectory, lowercasedFilename); - repo.Index.Stage(lowercasedFilename); + repo.Stage(lowercasedFilename); repo.Commit("initial", Constants.Signature, Constants.Signature); } @@ -447,8 +447,8 @@ FileStatus expectedCamelCasedFileStatus string camelCasedPath = Path.Combine(repo.Info.WorkingDirectory, upercasedFilename); File.Move(lowerCasedPath, camelCasedPath); - Assert.Equal(expectedlowerCasedFileStatus, repo.Index.RetrieveStatus(lowercasedFilename)); - Assert.Equal(expectedCamelCasedFileStatus, repo.Index.RetrieveStatus(upercasedFilename)); + Assert.Equal(expectedlowerCasedFileStatus, repo.RetrieveStatus(lowercasedFilename)); + Assert.Equal(expectedCamelCasedFileStatus, repo.RetrieveStatus(upercasedFilename)); AssertStatus(shouldIgnoreCase, expectedlowerCasedFileStatus, repo, camelCasedPath.ToLowerInvariant()); AssertStatus(shouldIgnoreCase, expectedCamelCasedFileStatus, repo, camelCasedPath.ToUpperInvariant()); @@ -459,7 +459,7 @@ private static void AssertStatus(bool shouldIgnoreCase, FileStatus expectedFileS { try { - Assert.Equal(expectedFileStatus, repo.Index.RetrieveStatus(path)); + Assert.Equal(expectedFileStatus, repo.RetrieveStatus(path)); } catch (ArgumentException) { @@ -481,10 +481,10 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectivesThroug const string gitIgnore = ".gitignore"; Touch(repo.Info.WorkingDirectory, gitIgnore, "bin"); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus("bin/look-ma.txt")); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus("bin/what-about-me.txt")); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/look-ma.txt")); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/what-about-me.txt")); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(new[] { "bin" + dirSep }, newStatus.Ignored.Select(s => s.FilePath)); var sb = new StringBuilder(); @@ -492,10 +492,10 @@ public void RetrievingTheStatusOfTheRepositoryHonorsTheGitIgnoreDirectivesThroug sb.AppendLine("!bin/w*"); Touch(repo.Info.WorkingDirectory, gitIgnore, sb.ToString()); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus("bin/look-ma.txt")); - Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus("bin/what-about-me.txt")); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus("bin/look-ma.txt")); + Assert.Equal(FileStatus.Untracked, repo.RetrieveStatus("bin/what-about-me.txt")); - newStatus = repo.Index.RetrieveStatus(); + newStatus = repo.RetrieveStatus(); Assert.Equal(new[] { "bin" + dirSep + "look-ma.txt" }, newStatus.Ignored.Select(s => s.FilePath)); Assert.True(newStatus.Untracked.Select(s => s.FilePath).Contains("bin" + dirSep + "what-about-me.txt")); @@ -517,7 +517,7 @@ public void CanRetrieveStatusOfFilesInSubmodule() "sm_missing_commits" }; - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(expected, status.Modified.Select(x => x.FilePath).ToArray()); } } @@ -532,7 +532,7 @@ public void CanExcludeStatusOfFilesInSubmodule() ".gitmodules", }; - RepositoryStatus status = repo.Index.RetrieveStatus(new StatusOptions() { ExcludeSubmodules = true }); + RepositoryStatus status = repo.RetrieveStatus(new StatusOptions() { ExcludeSubmodules = true }); Assert.Equal(expected, status.Modified.Select(x => x.FilePath).ToArray()); } } diff --git a/LibGit2Sharp.Tests/SubmoduleFixture.cs b/LibGit2Sharp.Tests/SubmoduleFixture.cs index e1b378b71..e72b7b2e5 100644 --- a/LibGit2Sharp.Tests/SubmoduleFixture.cs +++ b/LibGit2Sharp.Tests/SubmoduleFixture.cs @@ -60,6 +60,7 @@ public void CanRetrieveTheCommitIdsOfASubmodule(string name, string headId, stri { var submodule = repo.Submodules[name]; Assert.NotNull(submodule); + AssertBelongsToARepository(repo, submodule); Assert.Equal(name, submodule.Name); Assert.Equal((ObjectId)headId, submodule.HeadCommitId); @@ -119,7 +120,7 @@ public void CanStageChangeInSubmoduleViaIndexStage(string submodulePath, bool ap var statusBefore = submodule.RetrieveStatus(); Assert.Equal(SubmoduleStatus.WorkDirModified, statusBefore & SubmoduleStatus.WorkDirModified); - repo.Index.Stage(submodulePath); + repo.Stage(submodulePath); var statusAfter = submodule.RetrieveStatus(); Assert.Equal(SubmoduleStatus.IndexModified, statusAfter & SubmoduleStatus.IndexModified); @@ -144,7 +145,7 @@ public void CanStageChangeInSubmoduleViaIndexStageWithOtherPaths(string submodul Touch(repo.Info.WorkingDirectory, "new-file.txt"); - repo.Index.Stage(new[] { "new-file.txt", submodulePath, "does-not-exist.txt" }); + repo.Stage(new[] { "new-file.txt", submodulePath, "does-not-exist.txt" }); var statusAfter = submodule.RetrieveStatus(); Assert.Equal(SubmoduleStatus.IndexModified, statusAfter & SubmoduleStatus.IndexModified); diff --git a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs index b01d04c40..dd2ab2c37 100644 --- a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs +++ b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs @@ -210,7 +210,7 @@ protected static void InconclusiveIf(Func predicate, string message) throw new SkipException(message); } - protected void RequiresDotNetOrMonoGreaterThanOrEqualTo(Version minimumVersion) + protected void RequiresDotNetOrMonoGreaterThanOrEqualTo(System.Version minimumVersion) { Type type = Type.GetType("Mono.Runtime"); @@ -229,11 +229,11 @@ protected void RequiresDotNetOrMonoGreaterThanOrEqualTo(Version minimumVersion) var version = (string) displayName.Invoke(null, null); - Version current; + System.Version current; try { - current = new Version(version.Split(' ')[0]); + current = new System.Version(version.Split(' ')[0]); } catch (Exception e) { @@ -298,6 +298,11 @@ private static RepositoryOptions BuildFakeRepositoryOptions(SelfCleaningDirector /// The signature to use for user.name and user.email /// The path to the configuration file protected string CreateConfigurationWithDummyUser(Signature signature) + { + return CreateConfigurationWithDummyUser(signature.Name, signature.Email); + } + + protected string CreateConfigurationWithDummyUser(string name, string email) { SelfCleaningDirectory scd = BuildSelfCleaningDirectory(); Directory.CreateDirectory(scd.DirectoryPath); @@ -305,8 +310,15 @@ protected string CreateConfigurationWithDummyUser(Signature signature) using (Configuration config = new Configuration(configFilePath)) { - config.Set("user.name", signature.Name, ConfigurationLevel.Global); - config.Set("user.email", signature.Email, ConfigurationLevel.Global); + if (name != null) + { + config.Set("user.name", name, ConfigurationLevel.Global); + } + + if (email != null) + { + config.Set("user.email", email, ConfigurationLevel.Global); + } } return configFilePath; @@ -412,5 +424,11 @@ public static bool StreamEquals(Stream one, Stream two) return true; } + + public void AssertBelongsToARepository(IRepository repo, T instance) + where T : IBelongToARepository + { + Assert.Same(repo, ((IBelongToARepository)instance).Repository); + } } } diff --git a/LibGit2Sharp.Tests/UnstageFixture.cs b/LibGit2Sharp.Tests/UnstageFixture.cs index d58784423..a965187bf 100644 --- a/LibGit2Sharp.Tests/UnstageFixture.cs +++ b/LibGit2Sharp.Tests/UnstageFixture.cs @@ -25,12 +25,12 @@ public void StagingANewVersionOfAFileThenUnstagingItRevertsTheBlobToTheVersionOf string fullpath = Path.Combine(repo.Info.WorkingDirectory, filename); File.AppendAllText(fullpath, "Is there there anybody out there?"); - repo.Index.Stage(filename); + repo.Stage(filename); Assert.Equal(count, repo.Index.Count); Assert.NotEqual((blobId), repo.Index[posixifiedFileName].Id); - repo.Index.Unstage(posixifiedFileName); + repo.Unstage(posixifiedFileName); Assert.Equal(count, repo.Index.Count); Assert.Equal(blobId, repo.Index[posixifiedFileName].Id); @@ -48,13 +48,13 @@ public void CanStageAndUnstageAnIgnoredFile() const string relativePath = "Champa.ign"; Touch(repo.Info.WorkingDirectory, relativePath, "On stage!" + Environment.NewLine); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); - repo.Index.Stage(relativePath, new StageOptions { IncludeIgnored = true }); - Assert.Equal(FileStatus.Added, repo.Index.RetrieveStatus(relativePath)); + repo.Stage(relativePath, new StageOptions { IncludeIgnored = true }); + Assert.Equal(FileStatus.Added, repo.RetrieveStatus(relativePath)); - repo.Index.Unstage(relativePath); - Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(relativePath)); + repo.Unstage(relativePath); + Assert.Equal(FileStatus.Ignored, repo.RetrieveStatus(relativePath)); } } @@ -74,13 +74,13 @@ public void CanUnstage( { int count = repo.Index.Count; Assert.Equal(doesCurrentlyExistInTheIndex, (repo.Index[relativePath] != null)); - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); - repo.Index.Unstage(relativePath); + repo.Unstage(relativePath); Assert.Equal(count + expectedIndexCountVariation, repo.Index.Count); Assert.Equal(doesExistInTheIndexOnceStaged, (repo.Index[relativePath] != null)); - Assert.Equal(expectedStatusOnceStaged, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(expectedStatusOnceStaged, repo.RetrieveStatus(relativePath)); } } @@ -91,9 +91,9 @@ public void UnstagingUnknownPathsWithStrictUnmatchedExplicitPathsValidationThrow { using (var repo = new Repository(CloneStandardTestRepo())) { - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); - Assert.Throws(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions())); + Assert.Throws(() => repo.Unstage(relativePath, new ExplicitPathsOptions())); } } @@ -104,10 +104,10 @@ public void CanUnstageUnknownPathsWithLaxUnmatchedExplicitPathsValidation(string { using (var repo = new Repository(CloneStandardTestRepo())) { - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); - Assert.DoesNotThrow(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions() { ShouldFailOnUnmatchedPath = false })); - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.DoesNotThrow(() => repo.Unstage(relativePath, new ExplicitPathsOptions() { ShouldFailOnUnmatchedPath = false })); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); } } @@ -124,12 +124,12 @@ public void CanUnstageTheRemovalOfAFile() string fullPath = Path.Combine(repo.Info.WorkingDirectory, filename); Assert.False(File.Exists(fullPath)); - Assert.Equal(FileStatus.Removed, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Removed, repo.RetrieveStatus(filename)); - repo.Index.Unstage(filename); + repo.Unstage(filename); Assert.Equal(count + 1, repo.Index.Count); - Assert.Equal(FileStatus.Missing, repo.Index.RetrieveStatus(filename)); + Assert.Equal(FileStatus.Missing, repo.RetrieveStatus(filename)); } } @@ -143,14 +143,14 @@ public void CanUnstageUntrackedFileAgainstAnOrphanedHead() const string relativePath = "a.txt"; Touch(repo.Info.WorkingDirectory, relativePath, "hello test file\n"); - repo.Index.Stage(relativePath); + repo.Stage(relativePath); - repo.Index.Unstage(relativePath); - RepositoryStatus status = repo.Index.RetrieveStatus(); + repo.Unstage(relativePath); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(0, status.Staged.Count()); Assert.Equal(1, status.Untracked.Count()); - Assert.Throws(() => repo.Index.Unstage("i-dont-exist", new ExplicitPathsOptions())); + Assert.Throws(() => repo.Unstage("i-dont-exist", new ExplicitPathsOptions())); } } @@ -164,9 +164,9 @@ public void UnstagingUnknownPathsAgainstAnOrphanedHeadWithStrictUnmatchedExplici repo.Refs.UpdateTarget("HEAD", "refs/heads/orphaned"); Assert.True(repo.Info.IsHeadUnborn); - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); - Assert.Throws(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions())); + Assert.Throws(() => repo.Unstage(relativePath, new ExplicitPathsOptions())); } } @@ -180,11 +180,11 @@ public void CanUnstageUnknownPathsAgainstAnOrphanedHeadWithLaxUnmatchedExplicitP repo.Refs.UpdateTarget("HEAD", "refs/heads/orphaned"); Assert.True(repo.Info.IsHeadUnborn); - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); - Assert.DoesNotThrow(() => repo.Index.Unstage(relativePath)); - Assert.DoesNotThrow(() => repo.Index.Unstage(relativePath, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false })); - Assert.Equal(currentStatus, repo.Index.RetrieveStatus(relativePath)); + Assert.DoesNotThrow(() => repo.Unstage(relativePath)); + Assert.DoesNotThrow(() => repo.Unstage(relativePath, new ExplicitPathsOptions { ShouldFailOnUnmatchedPath = false })); + Assert.Equal(currentStatus, repo.RetrieveStatus(relativePath)); } } @@ -200,7 +200,7 @@ public void UnstagingANewFileWithAFullPathWhichEscapesOutOfTheWorkingDirThrows() const string filename = "unit_test.txt"; string fullPath = Touch(di.FullName, filename, "some contents"); - Assert.Throws(() => repo.Index.Unstage(fullPath)); + Assert.Throws(() => repo.Unstage(fullPath)); } } @@ -218,7 +218,7 @@ public void UnstagingANewFileWithAFullPathWhichEscapesOutOfTheWorkingDirAgainstA const string filename = "unit_test.txt"; string fullPath = Touch(di.FullName, filename, "some contents"); - Assert.Throws(() => repo.Index.Unstage(fullPath)); + Assert.Throws(() => repo.Unstage(fullPath)); } } @@ -227,10 +227,10 @@ public void UnstagingFileWithBadParamsThrows() { using (var repo = new Repository(StandardTestRepoPath)) { - Assert.Throws(() => repo.Index.Unstage(string.Empty)); - Assert.Throws(() => repo.Index.Unstage((string)null)); - Assert.Throws(() => repo.Index.Unstage(new string[] { })); - Assert.Throws(() => repo.Index.Unstage(new string[] { null })); + Assert.Throws(() => repo.Unstage(string.Empty)); + Assert.Throws(() => repo.Unstage((string)null)); + Assert.Throws(() => repo.Unstage(new string[] { })); + Assert.Throws(() => repo.Unstage(new string[] { null })); } } @@ -239,16 +239,16 @@ public void CanUnstageSourceOfARename() { using (var repo = new Repository(CloneStandardTestRepo())) { - repo.Index.Move("branch_file.txt", "renamed_branch_file.txt"); + repo.Move("branch_file.txt", "renamed_branch_file.txt"); - RepositoryStatus oldStatus = repo.Index.RetrieveStatus(); + RepositoryStatus oldStatus = repo.RetrieveStatus(); Assert.Equal(1, oldStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.Nonexistent, oldStatus["branch_file.txt"].State); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); - repo.Index.Unstage(new string[] { "branch_file.txt" }); + repo.Unstage(new string[] { "branch_file.txt" }); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.Missing, newStatus["branch_file.txt"].State); Assert.Equal(FileStatus.Added, newStatus["renamed_branch_file.txt"].State); @@ -260,15 +260,15 @@ public void CanUnstageTargetOfARename() { using (var repo = new Repository(CloneStandardTestRepo())) { - repo.Index.Move("branch_file.txt", "renamed_branch_file.txt"); + repo.Move("branch_file.txt", "renamed_branch_file.txt"); - RepositoryStatus oldStatus = repo.Index.RetrieveStatus(); + RepositoryStatus oldStatus = repo.RetrieveStatus(); Assert.Equal(1, oldStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.RenamedInIndex, oldStatus["renamed_branch_file.txt"].State); - repo.Index.Unstage(new string[] { "renamed_branch_file.txt" }); + repo.Unstage(new string[] { "renamed_branch_file.txt" }); - RepositoryStatus newStatus = repo.Index.RetrieveStatus(); + RepositoryStatus newStatus = repo.RetrieveStatus(); Assert.Equal(0, newStatus.RenamedInIndex.Count()); Assert.Equal(FileStatus.Untracked, newStatus["renamed_branch_file.txt"].State); Assert.Equal(FileStatus.Removed, newStatus["branch_file.txt"].State); @@ -280,10 +280,10 @@ public void CanUnstageBothSidesOfARename() { using (var repo = new Repository(CloneStandardTestRepo())) { - repo.Index.Move("branch_file.txt", "renamed_branch_file.txt"); - repo.Index.Unstage(new string[] { "branch_file.txt", "renamed_branch_file.txt" }); + repo.Move("branch_file.txt", "renamed_branch_file.txt"); + repo.Unstage(new string[] { "branch_file.txt", "renamed_branch_file.txt" }); - RepositoryStatus status = repo.Index.RetrieveStatus(); + RepositoryStatus status = repo.RetrieveStatus(); Assert.Equal(FileStatus.Missing, status["branch_file.txt"].State); Assert.Equal(FileStatus.Untracked, status["renamed_branch_file.txt"].State); } diff --git a/LibGit2Sharp.sln.DotSettings b/LibGit2Sharp.sln.DotSettings new file mode 100644 index 000000000..3d1a941ea --- /dev/null +++ b/LibGit2Sharp.sln.DotSettings @@ -0,0 +1,13 @@ + + <?xml version="1.0" encoding="utf-16"?><Profile name="LibGit2Sharp"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode></Profile> + TOGETHER + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + True + False + True + True + diff --git a/LibGit2Sharp.v2.ncrunchsolution b/LibGit2Sharp.v2.ncrunchsolution new file mode 100644 index 000000000..bf2ebc9c1 --- /dev/null +++ b/LibGit2Sharp.v2.ncrunchsolution @@ -0,0 +1,13 @@ + + 1 + false + false + false + UseDynamicAnalysis + UseStaticAnalysis + UseStaticAnalysis + UseStaticAnalysis + UseStaticAnalysis + + + \ No newline at end of file diff --git a/LibGit2Sharp/Branch.cs b/LibGit2Sharp/Branch.cs index 6b5cb25ce..0067bebb3 100644 --- a/LibGit2Sharp/Branch.cs +++ b/LibGit2Sharp/Branch.cs @@ -149,7 +149,13 @@ public virtual string UpstreamBranchCanonicalName } /// - /// Gets the configured to fetch from and push to. + /// Get the remote for the branch. + /// + /// If this is a local branch, this will return the configured + /// to fetch from and push to. If this is a + /// remote-tracking branch, this will return the remote containing + /// the tracked branch. + /// /// public virtual Remote Remote { @@ -164,11 +170,11 @@ public virtual Remote Remote else { remoteName = RemoteNameFromLocalBranch(); + } - if (remoteName == null) - { - return null; - } + if (remoteName == null) + { + return null; } return repo.Network.Remotes[remoteName]; @@ -210,7 +216,7 @@ private string RemoteNameFromLocalBranch() private string RemoteNameFromRemoteTrackingBranch() { - return Proxy.git_branch_remote_name(repo.Handle, CanonicalName); + return Proxy.git_branch_remote_name(repo.Handle, CanonicalName, false); } /// @@ -260,7 +266,7 @@ private Branch ResolveTrackedBranch() return branch; } - return new Branch(repo, new VoidReference(trackedReferenceName), trackedReferenceName); + return new Branch(repo, new VoidReference(repo, trackedReferenceName), trackedReferenceName); } private static bool IsRemoteBranch(string canonicalName) diff --git a/LibGit2Sharp/BranchUpdater.cs b/LibGit2Sharp/BranchUpdater.cs index 83a61d34c..f51332eb7 100644 --- a/LibGit2Sharp/BranchUpdater.cs +++ b/LibGit2Sharp/BranchUpdater.cs @@ -181,7 +181,7 @@ private void GetUpstreamInformation(string canonicalName, out string remoteName, } else if (canonicalName.LooksLikeRemoteTrackingBranch()) { - remoteName = Proxy.git_branch_remote_name(repo.Handle, canonicalName); + remoteName = Proxy.git_branch_remote_name(repo.Handle, canonicalName, true); Remote remote = repo.Network.Remotes.RemoteForName(remoteName); mergeBranchName = remote.FetchSpecTransformToSource(canonicalName); diff --git a/LibGit2Sharp/CloneOptions.cs b/LibGit2Sharp/CloneOptions.cs index 7a1fc785d..068391ade 100644 --- a/LibGit2Sharp/CloneOptions.cs +++ b/LibGit2Sharp/CloneOptions.cs @@ -7,7 +7,7 @@ namespace LibGit2Sharp /// /// Options to define clone behaviour /// - public sealed class CloneOptions : IConvertableToGitCheckoutOpts, ICredentialsProvider + public sealed class CloneOptions : IConvertableToGitCheckoutOpts { /// /// Creates default for a non-bare clone @@ -38,12 +38,6 @@ public CloneOptions() /// public CheckoutProgressHandler OnCheckoutProgress { get; set; } - /// - /// Credentials to use for user/pass authentication - /// - [Obsolete("This will be removed in future release. Use CredentialsProvider.")] - public Credentials Credentials { get; set; } - /// /// Handler to generate for authentication. /// diff --git a/LibGit2Sharp/Configuration.cs b/LibGit2Sharp/Configuration.cs index f70442245..89059b63e 100644 --- a/LibGit2Sharp/Configuration.cs +++ b/LibGit2Sharp/Configuration.cs @@ -227,6 +227,7 @@ public virtual ConfigurationEntry Get(string key, ConfigurationLevel level /// The configuration file which should be considered as the target of this operation public virtual void Set(string key, T value, ConfigurationLevel level = ConfigurationLevel.Local) { + Ensure.ArgumentNotNull(value, "value"); Ensure.ArgumentNotNullOrEmptyString(key, "key"); using (ConfigurationSafeHandle h = RetrieveConfigurationHandle(level, true, configHandle)) @@ -341,19 +342,37 @@ public virtual Signature BuildSignature(DateTimeOffset now) internal Signature BuildSignature(DateTimeOffset now, bool shouldThrowIfNotFound) { - var name = Get("user.name"); - var email = Get("user.email"); + const string userNameKey = "user.name"; + var name = this.GetValueOrDefault(userNameKey); + var normalizedName = NormalizeUserSetting(shouldThrowIfNotFound, userNameKey, name, + () => "unknown"); + + const string userEmailKey = "user.email"; + var email = this.GetValueOrDefault(userEmailKey); + var normalizedEmail = NormalizeUserSetting(shouldThrowIfNotFound, userEmailKey, email, + () => string.Format( + CultureInfo.InvariantCulture, "{0}@{1}", Environment.UserName, Environment.UserDomainName)); + + return new Signature(normalizedName, normalizedEmail, now); + } - if (shouldThrowIfNotFound && ((name == null) || (email == null))) + private string NormalizeUserSetting(bool shouldThrowIfNotFound, string entryName, string currentValue, Func defaultValue) + { + if (!string.IsNullOrEmpty(currentValue)) { - throw new LibGit2SharpException("Can not find Name or Email setting of the current user in Git configuration."); + return currentValue; } - return new Signature( - name != null ? name.Value : "unknown", - email != null ? email.Value : string.Format( - CultureInfo.InvariantCulture, "{0}@{1}", Environment.UserName, Environment.UserDomainName), - now); + string message = string.Format("Configuration value '{0}' is missing or invalid.", entryName); + + if (shouldThrowIfNotFound) + { + throw new LibGit2SharpException(message); + } + + Log.Write(LogLevel.Warning, message); + + return defaultValue(); } private ConfigurationSafeHandle Snapshot() diff --git a/LibGit2Sharp/Core/GitDiff.cs b/LibGit2Sharp/Core/GitDiff.cs index 27b5fab19..7ce4eafdf 100644 --- a/LibGit2Sharp/Core/GitDiff.cs +++ b/LibGit2Sharp/Core/GitDiff.cs @@ -205,8 +205,8 @@ internal class GitDiffOptions : IDisposable /* options controlling how to diff text is generated */ - public ushort ContextLines; - public ushort InterhunkLines; + public uint ContextLines; + public uint InterhunkLines; public ushort IdAbbrev; public Int64 MaxSize; public IntPtr OldPrefixString; diff --git a/LibGit2Sharp/Core/GitRemoteCallbacks.cs b/LibGit2Sharp/Core/GitRemoteCallbacks.cs index 7903f567a..3aec0bfdc 100644 --- a/LibGit2Sharp/Core/GitRemoteCallbacks.cs +++ b/LibGit2Sharp/Core/GitRemoteCallbacks.cs @@ -17,6 +17,8 @@ internal struct GitRemoteCallbacks internal NativeMethods.git_cred_acquire_cb acquire_credentials; + internal IntPtr certificate_check; + internal NativeMethods.git_transfer_progress_callback download_progress; internal NativeMethods.remote_update_tips_callback update_tips; diff --git a/LibGit2Sharp/Core/GitSmartSubtransport.cs b/LibGit2Sharp/Core/GitSmartSubtransport.cs new file mode 100644 index 000000000..d9b3c7545 --- /dev/null +++ b/LibGit2Sharp/Core/GitSmartSubtransport.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitSmartSubtransport + { + static GitSmartSubtransport() + { + GCHandleOffset = Marshal.OffsetOf(typeof(GitSmartSubtransport), "GCHandle").ToInt32(); + } + + public action_callback Action; + public close_callback Close; + public free_callback Free; + + /* The libgit2 structure definition ends here. Subsequent fields are for libgit2sharp bookkeeping. */ + + public IntPtr GCHandle; + + /* The following static fields are not part of the structure definition. */ + + public static int GCHandleOffset; + + public delegate int action_callback( + out IntPtr stream, + IntPtr subtransport, + IntPtr url, + GitSmartSubtransportAction action); + + public delegate int close_callback(IntPtr subtransport); + + public delegate void free_callback(IntPtr subtransport); + } +} diff --git a/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs new file mode 100644 index 000000000..724c6c414 --- /dev/null +++ b/LibGit2Sharp/Core/GitSmartSubtransportRegistration.cs @@ -0,0 +1,16 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitSmartSubtransportRegistration + { + public IntPtr SubtransportCallback; + public uint Rpc; + + public delegate int create_callback( + out IntPtr subtransport, + IntPtr transport); + } +} diff --git a/LibGit2Sharp/Core/GitSmartSubtransportStream.cs b/LibGit2Sharp/Core/GitSmartSubtransportStream.cs new file mode 100644 index 000000000..f73218c09 --- /dev/null +++ b/LibGit2Sharp/Core/GitSmartSubtransportStream.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibGit2Sharp.Core +{ + [StructLayout(LayoutKind.Sequential)] + internal class GitSmartSubtransportStream + { + static GitSmartSubtransportStream() + { + GCHandleOffset = Marshal.OffsetOf(typeof(GitSmartSubtransportStream), "GCHandle").ToInt32(); + } + + public IntPtr SmartTransport; + + public read_callback Read; + public write_callback Write; + public free_callback Free; + + /* The libgit2 structure definition ends here. Subsequent fields are for libgit2sharp bookkeeping. */ + + public IntPtr GCHandle; + + /* The following static fields are not part of the structure definition. */ + + public static int GCHandleOffset; + + public delegate int read_callback( + IntPtr stream, + IntPtr buffer, + UIntPtr buf_size, + out UIntPtr bytes_read); + + public delegate int write_callback( + IntPtr stream, + IntPtr buffer, + UIntPtr len); + + public delegate void free_callback( + IntPtr stream); + } +} diff --git a/LibGit2Sharp/Core/Handles/GitMergeHeadHandle.cs b/LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs similarity index 62% rename from LibGit2Sharp/Core/Handles/GitMergeHeadHandle.cs rename to LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs index f49e30e54..519874d03 100644 --- a/LibGit2Sharp/Core/Handles/GitMergeHeadHandle.cs +++ b/LibGit2Sharp/Core/Handles/GitAnnotatedCommitHandle.cs @@ -2,11 +2,11 @@ namespace LibGit2Sharp.Core.Handles { - internal class GitMergeHeadHandle : SafeHandleBase + internal class GitAnnotatedCommitHandle : SafeHandleBase { protected override bool ReleaseHandleImpl() { - Proxy.git_merge_head_free(handle); + Proxy.git_annotated_commit_free(handle); return true; } } diff --git a/LibGit2Sharp/Core/HistoryRewriter.cs b/LibGit2Sharp/Core/HistoryRewriter.cs index 916ef7d97..33c35c6fd 100644 --- a/LibGit2Sharp/Core/HistoryRewriter.cs +++ b/LibGit2Sharp/Core/HistoryRewriter.cs @@ -181,8 +181,8 @@ private Reference RewriteReference( return refMap[oldRef] = newRef; } - var movedRef = repo.Refs.Move(newRef, newRefName); - rollbackActions.Enqueue(() => repo.Refs.Move(newRef, oldRef.CanonicalName)); + var movedRef = repo.Refs.Rename(newRef, newRefName); + rollbackActions.Enqueue(() => repo.Refs.Rename(newRef, oldRef.CanonicalName)); return refMap[oldRef] = movedRef; } diff --git a/LibGit2Sharp/Core/NativeDllName.cs b/LibGit2Sharp/Core/NativeDllName.cs index 62603ace9..ecd41aaa6 100644 --- a/LibGit2Sharp/Core/NativeDllName.cs +++ b/LibGit2Sharp/Core/NativeDllName.cs @@ -2,6 +2,6 @@ namespace LibGit2Sharp.Core { internal static class NativeDllName { - public const string Name = "git2-69db893"; + public const string Name = "git2-3f8d005"; } } diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index 9a3d72076..1ef9f77ed 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -215,7 +215,8 @@ internal static extern int git_branch_remote_name( [DllImport(libgit2)] internal static extern int git_remote_rename( ref GitStrArray problems, - RemoteSafeHandle remote, + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string old_name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string new_name); internal delegate int git_remote_rename_problem_cb( @@ -223,11 +224,6 @@ internal delegate int git_remote_rename_problem_cb( IntPtr payload); - [DllImport(libgit2)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool git_remote_supported_url( - [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); - [DllImport(libgit2)] internal static extern int git_branch_upstream_name( GitBuf buf, @@ -609,6 +605,12 @@ internal static extern IndexReucEntrySafeHandle git_index_reuc_get_bypath( [DllImport(libgit2)] internal static extern int git_index_write_tree(out GitOid treeOid, IndexSafeHandle index); + [DllImport(libgit2)] + internal static extern int git_index_read_tree(IndexSafeHandle index, GitObjectSafeHandle tree); + + [DllImport(libgit2)] + internal static extern int git_index_clear(IndexSafeHandle index); + [DllImport(libgit2)] internal static extern int git_merge_base_many( out GitOid mergeBase, @@ -624,28 +626,28 @@ internal static extern int git_merge_base_octopus( [In] GitOid[] input_array); [DllImport(libgit2)] - internal static extern int git_merge_head_from_ref( - out GitMergeHeadHandle mergehead, + internal static extern int git_annotated_commit_from_ref( + out GitAnnotatedCommitHandle annotatedCommit, RepositorySafeHandle repo, ReferenceSafeHandle reference); [DllImport(libgit2)] - internal static extern int git_merge_head_from_fetchhead( - out GitMergeHeadHandle mergehead, + internal static extern int git_annotated_commit_from_fetchhead( + out GitAnnotatedCommitHandle annotatedCommit, RepositorySafeHandle repo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string branch_name, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_url, ref GitOid oid); [DllImport(libgit2)] - internal static extern int git_merge_head_from_id( - out GitMergeHeadHandle mergehead, + internal static extern int git_annotated_commit_lookup( + out GitAnnotatedCommitHandle annotatedCommit, RepositorySafeHandle repo, ref GitOid id); [DllImport(libgit2)] - internal static extern OidSafeHandle git_merge_head_id( - GitMergeHeadHandle mergeHead); + internal static extern OidSafeHandle git_annotated_commit_id( + GitAnnotatedCommitHandle annotatedCommit); [DllImport(libgit2)] internal static extern int git_merge( @@ -664,7 +666,7 @@ internal static extern int git_merge_analysis( int their_heads_len); [DllImport(libgit2)] - internal static extern void git_merge_head_free( + internal static extern void git_annotated_commit_free( IntPtr merge_head); [DllImport(libgit2)] @@ -1037,7 +1039,9 @@ internal static extern int git_remote_create_with_fetchspec( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string refspec); [DllImport(libgit2)] - internal static extern int git_remote_delete(RemoteSafeHandle remote); + internal static extern int git_remote_delete( + RepositorySafeHandle repo, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string name); [DllImport(libgit2)] internal static extern void git_remote_disconnect(RemoteSafeHandle remote); @@ -1045,6 +1049,7 @@ internal static extern int git_remote_create_with_fetchspec( [DllImport(libgit2)] internal static extern int git_remote_fetch( RemoteSafeHandle remote, + ref GitStrArray refspecs, SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); @@ -1069,6 +1074,11 @@ internal static extern int git_remote_fetch( [DllImport(libgit2)] internal static extern int git_remote_set_push_refspecs(RemoteSafeHandle remote, ref GitStrArray array); + [DllImport(libgit2)] + internal static extern int git_remote_set_url( + RemoteSafeHandle remote, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string url); + [DllImport(libgit2)] internal static extern int git_remote_is_valid_name( [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string remote_name); @@ -1240,6 +1250,7 @@ internal static extern int git_reset( RepositorySafeHandle repo, GitObjectSafeHandle target, ResetMode reset_type, + ref GitCheckoutOpts opts, SignatureSafeHandle signature, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string log_message); @@ -1481,8 +1492,31 @@ internal static extern int git_tag_delete( [DllImport(libgit2)] internal static extern void git_threads_shutdown(); + internal delegate void git_trace_cb(LogLevel level, IntPtr message); + + [DllImport(libgit2)] + internal static extern int git_trace_set(LogLevel level, git_trace_cb trace_cb); + internal delegate int git_transfer_progress_callback(ref GitTransferProgress stats, IntPtr payload); + internal delegate int git_transport_cb(out IntPtr transport, IntPtr remote, IntPtr payload); + + [DllImport(libgit2)] + internal static extern int git_transport_register( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string prefix, + IntPtr transport_cb, + IntPtr payload); + + [DllImport(libgit2)] + internal static extern int git_transport_smart( + out IntPtr transport, + IntPtr remote, + IntPtr definition); + + [DllImport(libgit2)] + internal static extern int git_transport_unregister( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string prefix); + [DllImport(libgit2)] internal static extern uint git_tree_entry_filemode(SafeHandle entry); diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 5ab210936..a9ab5e1d5 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -203,15 +203,21 @@ public static ReferenceSafeHandle git_branch_move(ReferenceSafeHandle reference, } } - public static string git_branch_remote_name(RepositorySafeHandle repo, string canonical_branch_name) + public static string git_branch_remote_name(RepositorySafeHandle repo, string canonical_branch_name, bool shouldThrowIfNotFound) { using (ThreadAffinity()) using (var buf = new GitBuf()) { int res = NativeMethods.git_branch_remote_name(buf, repo, canonical_branch_name); - Ensure.Int32Result(res); - return LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty; + if (!shouldThrowIfNotFound && + (res == (int) GitErrorCode.NotFound || res == (int) GitErrorCode.Ambiguous)) + { + return null; + } + + Ensure.ZeroResult(res); + return LaxUtf8Marshaler.FromNative(buf.ptr); } } @@ -984,6 +990,24 @@ public static ObjectId git_tree_create_fromindex(Index index) } } + public static void git_index_read_fromtree(Index index, GitObjectSafeHandle tree) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_index_read_tree(index.Handle, tree); + Ensure.ZeroResult(res); + } + } + + public static void git_index_clear(Index index) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_index_clear(index.Handle); + Ensure.ZeroResult(res); + } + } + #endregion #region git_merge_ @@ -1024,13 +1048,13 @@ public static ObjectId git_merge_base_octopus(RepositorySafeHandle repo, GitOid[ } } - public static GitMergeHeadHandle git_merge_head_from_fetchhead(RepositorySafeHandle repo, string branchName, string remoteUrl, GitOid oid) + public static GitAnnotatedCommitHandle git_annotated_commit_from_fetchhead(RepositorySafeHandle repo, string branchName, string remoteUrl, GitOid oid) { using (ThreadAffinity()) { - GitMergeHeadHandle merge_head; + GitAnnotatedCommitHandle merge_head; - int res = NativeMethods.git_merge_head_from_fetchhead(out merge_head, repo, branchName, remoteUrl, ref oid); + int res = NativeMethods.git_annotated_commit_from_fetchhead(out merge_head, repo, branchName, remoteUrl, ref oid); Ensure.ZeroResult(res); @@ -1038,13 +1062,13 @@ public static GitMergeHeadHandle git_merge_head_from_fetchhead(RepositorySafeHan } } - public static GitMergeHeadHandle git_merge_head_from_id(RepositorySafeHandle repo, GitOid oid) + public static GitAnnotatedCommitHandle git_annotated_commit_lookup(RepositorySafeHandle repo, GitOid oid) { using (ThreadAffinity()) { - GitMergeHeadHandle their_head; + GitAnnotatedCommitHandle their_head; - int res = NativeMethods.git_merge_head_from_id(out their_head, repo, ref oid); + int res = NativeMethods.git_annotated_commit_lookup(out their_head, repo, ref oid); Ensure.ZeroResult(res); @@ -1052,13 +1076,13 @@ public static GitMergeHeadHandle git_merge_head_from_id(RepositorySafeHandle rep } } - public static GitMergeHeadHandle git_merge_head_from_ref(RepositorySafeHandle repo, ReferenceSafeHandle reference) + public static GitAnnotatedCommitHandle git_annotated_commit_from_ref(RepositorySafeHandle repo, ReferenceSafeHandle reference) { using (ThreadAffinity()) { - GitMergeHeadHandle their_head; + GitAnnotatedCommitHandle their_head; - int res = NativeMethods.git_merge_head_from_ref(out their_head, repo, reference); + int res = NativeMethods.git_annotated_commit_from_ref(out their_head, repo, reference); Ensure.ZeroResult(res); @@ -1066,12 +1090,12 @@ public static GitMergeHeadHandle git_merge_head_from_ref(RepositorySafeHandle re } } - public static ObjectId git_merge_head_id(GitMergeHeadHandle mergeHead) + public static ObjectId git_annotated_commit_id(GitAnnotatedCommitHandle mergeHead) { - return NativeMethods.git_merge_head_id(mergeHead).MarshalAsObjectId(); + return NativeMethods.git_annotated_commit_id(mergeHead).MarshalAsObjectId(); } - public static void git_merge(RepositorySafeHandle repo, GitMergeHeadHandle[] heads, GitMergeOpts mergeOptions, GitCheckoutOpts checkoutOptions) + public static void git_merge(RepositorySafeHandle repo, GitAnnotatedCommitHandle[] heads, GitMergeOpts mergeOptions, GitCheckoutOpts checkoutOptions) { using (ThreadAffinity()) { @@ -1090,7 +1114,7 @@ public static void git_merge(RepositorySafeHandle repo, GitMergeHeadHandle[] hea public static void git_merge_analysis( RepositorySafeHandle repo, - GitMergeHeadHandle[] heads, + GitAnnotatedCommitHandle[] heads, out GitMergeAnalysis analysis_out, out GitMergePreference preference_out) { @@ -1109,9 +1133,9 @@ public static void git_merge_analysis( } } - public static void git_merge_head_free(IntPtr handle) + public static void git_annotated_commit_free(IntPtr handle) { - NativeMethods.git_merge_head_free(handle); + NativeMethods.git_annotated_commit_free(handle); } #endregion @@ -1864,16 +1888,14 @@ public static void git_remote_delete(RepositorySafeHandle repo, string name) { using (ThreadAffinity()) { - using (RemoteSafeHandle remote = git_remote_load(repo, name, false)) - { - if (remote == null) - { - return; - } + int res = NativeMethods.git_remote_delete(repo, name); - int res = NativeMethods.git_remote_delete(remote); - Ensure.ZeroResult(res); + if (res == (int)GitErrorCode.NotFound) + { + return; } + + Ensure.ZeroResult(res); } } @@ -1975,13 +1997,31 @@ public static void git_remote_set_push_refspecs(RemoteSafeHandle remote, IEnumer } } + public static void git_remote_set_url(RemoteSafeHandle remote, string url) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_remote_set_url(remote, url); + Ensure.ZeroResult(res); + } + } + public static void git_remote_fetch(RemoteSafeHandle remote, Signature signature, string logMessage) { using (ThreadAffinity()) using (var sigHandle = signature.BuildHandle()) { - int res = NativeMethods.git_remote_fetch(remote, sigHandle, logMessage); - Ensure.ZeroResult(res); + var array = new GitStrArrayNative(); + + try + { + int res = NativeMethods.git_remote_fetch(remote, ref array.Array, sigHandle, logMessage); + Ensure.ZeroResult(res); + } + finally + { + array.Dispose(); + } } } @@ -2085,39 +2125,38 @@ public static void git_remote_rename(RepositorySafeHandle repo, string name, str { using (ThreadAffinity()) { - using (RemoteSafeHandle remote = git_remote_load(repo, name, false)) + if (callback == null) { - if (remote == null) - { - return; - } + callback = problem => {}; + } - if (callback == null) - { - callback = problem => {}; - } + var array = new GitStrArrayNative(); - var array = new GitStrArrayNative(); + try + { + int res = NativeMethods.git_remote_rename( + ref array.Array, + repo, + name, + new_name); - try + if (res == (int)GitErrorCode.NotFound) { - int res = NativeMethods.git_remote_rename( - ref array.Array, - remote, - new_name); + throw new NotFoundException( + string.Format("Remote '{0}' does not exist and cannot be renamed.", name)); + } - Ensure.ZeroResult(res); + Ensure.ZeroResult(res); - foreach (var item in array.ReadStrings()) - { - callback(item); - } - } - finally + foreach (var item in array.ReadStrings()) { - array.Dispose(); + callback(item); } } + finally + { + array.Dispose(); + } } } @@ -2149,11 +2188,6 @@ public static string git_remote_url(RemoteSafeHandle remote) return NativeMethods.git_remote_url(remote); } - public static bool git_remote_supported_url(string url) - { - return NativeMethods.git_remote_supported_url(url); - } - #endregion #region git_repository_ @@ -2385,6 +2419,7 @@ public static void git_reset( RepositorySafeHandle repo, ObjectId committishId, ResetMode resetKind, + ref GitCheckoutOpts checkoutOptions, Signature signature, string logMessage) { @@ -2392,7 +2427,7 @@ public static void git_reset( using (var osw = new ObjectSafeWrapper(committishId, repo)) using (var sigHandle = signature.BuildHandle()) { - int res = NativeMethods.git_reset(repo, osw.ObjectPtr, resetKind, sigHandle, logMessage); + int res = NativeMethods.git_reset(repo, osw.ObjectPtr, resetKind, ref checkoutOptions, sigHandle, logMessage); Ensure.ZeroResult(res); } } @@ -2910,6 +2945,53 @@ public static GitObjectType git_tag_target_type(GitObjectSafeHandle tag) #endregion + #region git_trace_ + + public static void git_trace_set(LogLevel level, NativeMethods.git_trace_cb callback) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_trace_set(level, callback); + Ensure.ZeroResult(res); + } + } + + #endregion + + #region git_transport_ + + public static void git_transport_register(String prefix, IntPtr transport_cb, IntPtr param) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_transport_register(prefix, transport_cb, param); + + if (res == (int)GitErrorCode.Exists) + { + throw new EntryExistsException(String.Format("A custom transport for '{0}' is already registered", prefix)); + } + + Ensure.ZeroResult(res); + } + } + + public static void git_transport_unregister(String prefix) + { + using (ThreadAffinity()) + { + int res = NativeMethods.git_transport_unregister(prefix); + + if (res == (int)GitErrorCode.NotFound) + { + throw new NotFoundException("The given transport was not found"); + } + + Ensure.ZeroResult(res); + } + } + + #endregion + #region git_tree_ public static Mode git_tree_entry_attributes(SafeHandle entry) diff --git a/LibGit2Sharp/Credentials.cs b/LibGit2Sharp/Credentials.cs index adf9582fe..b5540705e 100644 --- a/LibGit2Sharp/Credentials.cs +++ b/LibGit2Sharp/Credentials.cs @@ -1,6 +1,5 @@ using System; using LibGit2Sharp.Core; -using LibGit2Sharp.Handlers; namespace LibGit2Sharp { @@ -20,41 +19,4 @@ public abstract class Credentials /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired. protected internal abstract int GitCredentialHandler(out IntPtr cred, IntPtr url, IntPtr usernameFromUrl, GitCredentialType types, IntPtr payload); } - - internal interface ICredentialsProvider - { - /// - /// The to authenticate with during the push. - /// - [Obsolete("This will be removed in future release. Use CredentialsProvider.")] - Credentials Credentials { get; } - - /// - /// Handler to generate for authentication. - /// - CredentialsHandler CredentialsProvider { get; } - } - - internal static class CredentialsProviderExtensions - { - public static CredentialsHandler GetCredentialsHandler(this ICredentialsProvider provider) - { - if (provider == null) - { - return null; - } - - if (provider.CredentialsProvider != null) - { - return provider.CredentialsProvider; - } - - if (provider.Credentials == null) - { - return null; - } - - return (url, user, type) => provider.Credentials; - } - } } diff --git a/LibGit2Sharp/Diff.cs b/LibGit2Sharp/Diff.cs index a1e87e3b3..4e0e398d6 100644 --- a/LibGit2Sharp/Diff.cs +++ b/LibGit2Sharp/Diff.cs @@ -235,7 +235,7 @@ public virtual T Compare(Tree oldTree, DiffTargets diffTargets, IEnumerable(IEnumerable paths = null, bool includeUntracked = false, ExplicitPathsOptions explicitPathsOptions = null, CompareOptions compareOptions = null) where T : class { - return Compare(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions); + return Compare(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions, compareOptions); } internal virtual T Compare(DiffModifiers diffOptions, IEnumerable paths = null, diff --git a/LibGit2Sharp/DirectReference.cs b/LibGit2Sharp/DirectReference.cs index d5de46621..00de258a6 100644 --- a/LibGit2Sharp/DirectReference.cs +++ b/LibGit2Sharp/DirectReference.cs @@ -16,7 +16,7 @@ protected DirectReference() { } internal DirectReference(string canonicalName, IRepository repo, ObjectId targetId) - : base(canonicalName, targetId.Sha) + : base(repo, canonicalName, targetId.Sha) { targetBuilder = new Lazy(() => repo.Lookup(targetId)); } diff --git a/LibGit2Sharp/EntryExistsException.cs b/LibGit2Sharp/EntryExistsException.cs new file mode 100644 index 000000000..9dff4d750 --- /dev/null +++ b/LibGit2Sharp/EntryExistsException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The exception that is thrown attempting to create a resource that already exists. + /// + [Serializable] + public class EntryExistsException : LibGit2SharpException + { + /// + /// Initializes a new instance of the class. + /// + public EntryExistsException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A message that describes the error. + public EntryExistsException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. + public EntryExistsException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected EntryExistsException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + internal EntryExistsException(string message, GitErrorCode code, GitErrorCategory category) + : base(message, code, category) + { + } + } +} diff --git a/LibGit2Sharp/FetchOptions.cs b/LibGit2Sharp/FetchOptions.cs index 2379cf70c..020f47f45 100644 --- a/LibGit2Sharp/FetchOptions.cs +++ b/LibGit2Sharp/FetchOptions.cs @@ -6,7 +6,7 @@ namespace LibGit2Sharp /// /// Collection of parameters controlling Fetch behavior. /// - public sealed class FetchOptions : ICredentialsProvider + public sealed class FetchOptions { /// /// Specifies the tag-following behavior of the fetch operation. @@ -39,12 +39,6 @@ public sealed class FetchOptions : ICredentialsProvider /// public TransferProgressHandler OnTransferProgress { get; set; } - /// - /// Credentials to use for username/password authentication. - /// - [Obsolete("This will be removed in future release. Use CredentialsProvider.")] - public Credentials Credentials { get; set; } - /// /// Handler to generate for authentication. /// diff --git a/LibGit2Sharp/GitObject.cs b/LibGit2Sharp/GitObject.cs index ad4614dc4..5e11489a2 100644 --- a/LibGit2Sharp/GitObject.cs +++ b/LibGit2Sharp/GitObject.cs @@ -11,7 +11,7 @@ namespace LibGit2Sharp /// A GitObject /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public abstract class GitObject : IEquatable + public abstract class GitObject : IEquatable, IBelongToARepository { internal static IDictionary TypeToKindMap = new Dictionary @@ -154,5 +154,7 @@ private string DebuggerDisplay { get { return Id.ToString(7); } } + + IRepository IBelongToARepository.Repository { get { return repo; } } } } diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs index 0e01ef570..190cfadf4 100644 --- a/LibGit2Sharp/GlobalSettings.cs +++ b/LibGit2Sharp/GlobalSettings.cs @@ -1,4 +1,6 @@ -using LibGit2Sharp.Core; +using System; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; namespace LibGit2Sharp { @@ -7,14 +9,116 @@ namespace LibGit2Sharp /// public static class GlobalSettings { + private static readonly Lazy version = new Lazy(Version.Build); + + private static LogConfiguration logConfiguration = LogConfiguration.None; + /// /// Returns all the optional features that were compiled into /// libgit2. /// /// A enumeration. + [Obsolete("This method will be removed in the next release. Use Version.Features instead.")] public static BuiltInFeatures Features() { - return Proxy.git_libgit2_features(); + return Version.Features; + } + + /// + /// Returns information related to the current LibGit2Sharp + /// library. + /// + public static Version Version + { + get + { + return version.Value; + } + } + + /// + /// Registers a new as a custom + /// smart-protocol transport with libgit2. Any Git remote with + /// the scheme registered will delegate to the given transport + /// for all communication with the server. use this transport to communicate + /// with the server This is not commonly + /// used: some callers may want to re-use an existing connection to + /// perform fetch / push operations to a remote. + /// + /// Note that this configuration is global to an entire process + /// and does not honor application domains. + /// + /// The type of SmartSubtransport to register + /// The scheme (eg "http" or "gopher") to register + public static SmartSubtransportRegistration RegisterSmartSubtransport(string scheme) + where T : SmartSubtransport, new() + { + Ensure.ArgumentNotNull(scheme, "scheme"); + + var registration = new SmartSubtransportRegistration(scheme); + + try + { + Proxy.git_transport_register( + registration.Scheme, + registration.FunctionPointer, + registration.RegistrationPointer); + } + catch (Exception) + { + registration.Free(); + throw; + } + + return registration; + } + + /// + /// Unregisters a previously registered + /// as a custom smart-protocol transport with libgit2. + /// + /// The type of SmartSubtransport to register + /// The previous registration + public static void UnregisterSmartSubtransport(SmartSubtransportRegistration registration) + where T : SmartSubtransport, new() + { + Ensure.ArgumentNotNull(registration, "registration"); + + Proxy.git_transport_unregister(registration.Scheme); + registration.Free(); + } + + /// + /// Registers a new to receive + /// information logging information from libgit2 and LibGit2Sharp. + /// + /// Note that this configuration is global to an entire process + /// and does not honor application domains. + /// + public static LogConfiguration LogConfiguration + { + set + { + Ensure.ArgumentNotNull(value, "value"); + + logConfiguration = value; + + if (logConfiguration.Level == LogLevel.None) + { + Proxy.git_trace_set(0, null); + } + else + { + Proxy.git_trace_set(value.Level, value.GitTraceHandler); + + Log.Write(LogLevel.Info, "Logging enabled at level {0}", value.Level); + } + } + + get + { + return logConfiguration; + } } } } diff --git a/LibGit2Sharp/Handlers.cs b/LibGit2Sharp/Handlers.cs index ef688c550..74c746f1a 100644 --- a/LibGit2Sharp/Handlers.cs +++ b/LibGit2Sharp/Handlers.cs @@ -111,4 +111,12 @@ public enum PackBuilderStage /// Deltafying } + + /// + /// Delegate definition for logging. This callback will be used to + /// write logging messages in libgit2 and LibGit2Sharp. + /// + /// The level of the log message. + /// The log message. + public delegate void LogHandler(LogLevel level, string message); } diff --git a/LibGit2Sharp/HistoryDivergence.cs b/LibGit2Sharp/HistoryDivergence.cs index c5dab176b..27d84dd19 100644 --- a/LibGit2Sharp/HistoryDivergence.cs +++ b/LibGit2Sharp/HistoryDivergence.cs @@ -20,7 +20,7 @@ protected HistoryDivergence() internal HistoryDivergence(Repository repo, Commit one, Commit another) { commonAncestor = new Lazy(() => repo.Commits.FindMergeBase(one, another)); - Tuple div = Proxy.git_graph_ahead_behind(repo.Handle, another, one); + Tuple div = Proxy.git_graph_ahead_behind(repo.Handle, one, another); One = one; Another = another; diff --git a/LibGit2Sharp/IBelongToARepository.cs b/LibGit2Sharp/IBelongToARepository.cs new file mode 100644 index 000000000..f0297c6cc --- /dev/null +++ b/LibGit2Sharp/IBelongToARepository.cs @@ -0,0 +1,26 @@ +namespace LibGit2Sharp +{ + /// + /// Can be used to reference the from which + /// an instance was created. + /// + /// While convenient in some situations (e.g. Checkout branch bound to UI element), + /// it is important to ensure instances created from an + /// are not used after it is disposed. + /// + /// + /// It's generally better to create and dependant instances + /// on demand, with a short lifespan. + /// + /// + public interface IBelongToARepository + { + /// + /// The from which this instance was created. + /// + /// The returned value should not be disposed. + /// + /// + IRepository Repository { get; } + } +} diff --git a/LibGit2Sharp/IRepository.cs b/LibGit2Sharp/IRepository.cs index 9dc915a68..a68bdb862 100644 --- a/LibGit2Sharp/IRepository.cs +++ b/LibGit2Sharp/IRepository.cs @@ -81,7 +81,7 @@ public interface IRepository : IDisposable /// controlling checkout behavior. /// Identity for use when updating the reflog. /// The that was checked out. - Branch Checkout(Branch branch, CheckoutOptions options, Signature signature = null); + Branch Checkout(Branch branch, CheckoutOptions options, Signature signature); /// /// Checkout the specified branch, reference or SHA. @@ -94,7 +94,7 @@ public interface IRepository : IDisposable /// controlling checkout behavior. /// Identity for use when updating the reflog. /// The that was checked out. - Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature = null); + Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature); /// /// Checkout the specified . @@ -106,7 +106,7 @@ public interface IRepository : IDisposable /// controlling checkout behavior. /// Identity for use when updating the reflog. /// The that was checked out. - Branch Checkout(Commit commit, CheckoutOptions options, Signature signature = null); + Branch Checkout(Commit commit, CheckoutOptions options, Signature signature); /// /// Updates specifed paths in the index and working directory with the versions from the specified branch, reference, or SHA. @@ -117,7 +117,7 @@ public interface IRepository : IDisposable /// A revparse spec for the commit or branch to checkout paths from. /// The paths to checkout. /// Collection of parameters controlling checkout behavior. - void CheckoutPaths(string committishOrBranchSpec, IEnumerable paths, CheckoutOptions checkoutOptions = null); + void CheckoutPaths(string committishOrBranchSpec, IEnumerable paths, CheckoutOptions checkoutOptions); /// /// Try to lookup an object by its . If no matching object is found, null will be returned. @@ -159,7 +159,7 @@ public interface IRepository : IDisposable /// The of who added the change to the repository. /// The that specify the commit behavior. /// The generated . - Commit Commit(string message, Signature author, Signature committer, CommitOptions options = null); + Commit Commit(string message, Signature author, Signature committer, CommitOptions options); /// /// Sets the current to the specified commit and optionally resets the and @@ -169,7 +169,7 @@ public interface IRepository : IDisposable /// The target commit object. /// Identity for use when updating the reflog. /// Message to use when updating the reflog. - void Reset(ResetMode resetMode, Commit commit, Signature signature = null, string logMessage = null); + void Reset(ResetMode resetMode, Commit commit, Signature signature, string logMessage); /// /// Replaces entries in the with entries from the specified commit. @@ -180,7 +180,7 @@ public interface IRepository : IDisposable /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// - void Reset(Commit commit, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null); + void Reset(Commit commit, IEnumerable paths, ExplicitPathsOptions explicitPathsOptions); /// /// Clean the working tree by removing files that are not under version control. @@ -194,7 +194,7 @@ public interface IRepository : IDisposable /// The of who is performing the reverte. /// controlling revert behavior. /// The result of the revert. - RevertResult Revert(Commit commit, Signature reverter, RevertOptions options = null); + RevertResult Revert(Commit commit, Signature reverter, RevertOptions options); /// /// Merge changes from commit into the branch pointed at by HEAD.. @@ -203,7 +203,7 @@ public interface IRepository : IDisposable /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - MergeResult Merge(Commit commit, Signature merger, MergeOptions options = null); + MergeResult Merge(Commit commit, Signature merger, MergeOptions options); /// /// Merges changes from branch into the branch pointed at by HEAD.. @@ -212,7 +212,7 @@ public interface IRepository : IDisposable /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - MergeResult Merge(Branch branch, Signature merger, MergeOptions options = null); + MergeResult Merge(Branch branch, Signature merger, MergeOptions options); /// /// Merges changes from the commit into the branch pointed at by HEAD. @@ -221,7 +221,7 @@ public interface IRepository : IDisposable /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - MergeResult Merge(string committish, Signature merger, MergeOptions options = null); + MergeResult Merge(string committish, Signature merger, MergeOptions options); /// /// Cherry picks changes from the commit into the branch pointed at by HEAD. @@ -230,7 +230,7 @@ public interface IRepository : IDisposable /// The of who is performing the cherry pick. /// Specifies optional parameters controlling cherry pick behavior; if null, the defaults are used. /// The of the merge. - CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options = null); + CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options); /// /// Manipulate the currently ignored files. @@ -253,6 +253,118 @@ public interface IRepository : IDisposable /// Path of the file to blame. /// Specifies optional parameters; if null, the defaults are used. /// The blame for the file. - BlameHunkCollection Blame(string path, BlameOptions options = null); + BlameHunkCollection Blame(string path, BlameOptions options); + + /// + /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). + /// + /// If this path is ignored by configuration then it will not be staged unless is unset. + /// + /// The path of the file within the working directory. + /// Determines how paths will be staged. + void Stage(string path, StageOptions stageOptions); + + /// + /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). + /// + /// Any paths (even those listed explicitly) that are ignored by configuration will not be staged unless is unset. + /// + /// The collection of paths of the files within the working directory. + /// Determines how paths will be staged. + void Stage(IEnumerable paths, StageOptions stageOptions); + + /// + /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). + /// + /// The path of the file within the working directory. + /// + /// The passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + void Unstage(string path, ExplicitPathsOptions explicitPathsOptions); + + /// + /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). + /// + /// The collection of paths of the files within the working directory. + /// + /// The passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions); + + /// + /// Moves and/or renames a file in the working directory and promotes the change to the staging area. + /// + /// The path of the file within the working directory which has to be moved/renamed. + /// The target path of the file within the working directory. + void Move(string sourcePath, string destinationPath); + + /// + /// Moves and/or renames a collection of files in the working directory and promotes the changes to the staging area. + /// + /// The paths of the files within the working directory which have to be moved/renamed. + /// The target paths of the files within the working directory. + void Move(IEnumerable sourcePaths, IEnumerable destinationPaths); + + /// + /// Removes a file from the staging area, and optionally removes it from the working directory as well. + /// + /// If the file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the file from the working directory as well. + /// + /// + /// When not passing a , the passed path will be treated as + /// a pathspec. You can for example use it to pass the relative path to a folder inside the working directory, + /// so that all files beneath this folders, and the folder itself, will be removed. + /// + /// + /// The path of the file within the working directory. + /// True to remove the file from the working directory, False otherwise. + /// + /// The passed will be treated as an explicit path. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); + + /// + /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. + /// + /// If a file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the files from the working directory as well. + /// + /// + /// When not passing a , the passed paths will be treated as + /// a pathspec. You can for example use it to pass the relative paths to folders inside the working directory, + /// so that all files beneath these folders, and the folders themselves, will be removed. + /// + /// + /// The collection of paths of the files within the working directory. + /// True to remove the files from the working directory, False otherwise. + /// + /// The passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions); + + /// + /// Retrieves the state of a file in the working directory, comparing it against the staging area and the latest commmit. + /// + /// The relative path within the working directory to the file. + /// A representing the state of the parameter. + FileStatus RetrieveStatus(string filePath); + + /// + /// Retrieves the state of all files in the working directory, comparing them against the staging area and the latest commmit. + /// + /// If set, the options that control the status investigation. + /// A holding the state of all the files. + RepositoryStatus RetrieveStatus(StatusOptions options); } } diff --git a/LibGit2Sharp/Index.cs b/LibGit2Sharp/Index.cs index 9548ceda4..5e9e941a0 100644 --- a/LibGit2Sharp/Index.cs +++ b/LibGit2Sharp/Index.cs @@ -127,22 +127,6 @@ IEnumerator IEnumerable.GetEnumerator() #endregion - /// - /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). - /// - /// If this path is ignored by configuration then it will not be staged. - /// - /// The path of the file within the working directory. - /// - /// If set, the passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This will be removed in a future release. Supply ExplicitPathsOptions to StageOptions.")] - public virtual void Stage(string path, ExplicitPathsOptions explicitPathsOptions) - { - Stage(path, new StageOptions { ExplicitPathsOptions = explicitPathsOptions }); - } - /// /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). /// @@ -150,27 +134,10 @@ public virtual void Stage(string path, ExplicitPathsOptions explicitPathsOptions /// /// The path of the file within the working directory. /// If set, determines how paths will be staged. + [Obsolete("This method will be removed in the next release. Use IRepository.Stage() instead.")] public virtual void Stage(string path, StageOptions stageOptions = null) { - Ensure.ArgumentNotNull(path, "path"); - - Stage(new[] { path }, stageOptions); - } - - /// - /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). - /// - /// Any paths (even those listed explicitly) that are ignored by configuration will not be staged. - /// - /// The collection of paths of the files within the working directory. - /// - /// If set, the passed will be treated as explicit paths. - /// Use these options to determine how unmatched explicit paths should be handled. - /// - [Obsolete("This will be removed in a future release. Supply ExplicitPathsOptions to StageOptions.")] - public virtual void Stage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) - { - Stage(paths, new StageOptions { ExplicitPathsOptions = explicitPathsOptions }); + repo.Stage(path, stageOptions); } /// @@ -180,44 +147,10 @@ public virtual void Stage(IEnumerable paths, ExplicitPathsOptions explic /// /// The collection of paths of the files within the working directory. /// If set, determines how paths will be staged. + [Obsolete("This method will be removed in the next release. Use IRepository.Stage() instead.")] public virtual void Stage(IEnumerable paths, StageOptions stageOptions = null) { - Ensure.ArgumentNotNull(paths, "paths"); - - DiffModifiers diffModifiers = DiffModifiers.IncludeUntracked; - ExplicitPathsOptions explicitPathsOptions = stageOptions != null ? stageOptions.ExplicitPathsOptions : null; - - if (stageOptions != null && stageOptions.IncludeIgnored) - { - diffModifiers |= DiffModifiers.IncludeIgnored; - } - - var changes = repo.Diff.Compare(diffModifiers, paths, explicitPathsOptions); - - foreach (var treeEntryChanges in changes) - { - switch (treeEntryChanges.Status) - { - case ChangeKind.Unmodified: - continue; - - case ChangeKind.Deleted: - RemoveFromIndex(treeEntryChanges.Path); - continue; - - case ChangeKind.Added: - /* Fall through */ - case ChangeKind.Modified: - AddToIndex(treeEntryChanges.Path); - continue; - - default: - throw new InvalidOperationException( - string.Format(CultureInfo.InvariantCulture, "Entry '{0}' bears an unexpected ChangeKind '{1}'", treeEntryChanges.Path, treeEntryChanges.Status)); - } - } - - UpdatePhysicalIndex(); + repo.Stage(paths, stageOptions); } /// @@ -228,11 +161,10 @@ public virtual void Stage(IEnumerable paths, StageOptions stageOptions = /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// + [Obsolete("This method will be removed in the next release. Use IRepository.Unstage() instead.")] public virtual void Unstage(string path, ExplicitPathsOptions explicitPathsOptions = null) { - Ensure.ArgumentNotNull(path, "path"); - - Unstage(new[] { path }, explicitPathsOptions); + repo.Unstage(path, explicitPathsOptions); } /// @@ -243,20 +175,10 @@ public virtual void Unstage(string path, ExplicitPathsOptions explicitPathsOptio /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// + [Obsolete("This method will be removed in the next release. Use IRepository.Unstage() instead.")] public virtual void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions = null) { - Ensure.ArgumentNotNull(paths, "paths"); - - if (repo.Info.IsHeadUnborn) - { - var changes = repo.Diff.Compare(null, DiffTargets.Index, paths, explicitPathsOptions, new CompareOptions { Similarity = SimilarityOptions.None }); - - Reset(changes); - } - else - { - repo.Reset("HEAD", paths, explicitPathsOptions); - } + repo.Unstage(paths, explicitPathsOptions); } /// @@ -264,9 +186,10 @@ public virtual void Unstage(IEnumerable paths, ExplicitPathsOptions expl /// /// The path of the file within the working directory which has to be moved/renamed. /// The target path of the file within the working directory. + [Obsolete("This method will be removed in the next release. Use IRepository.Move() instead.")] public virtual void Move(string sourcePath, string destinationPath) { - Move(new[] { sourcePath }, new[] { destinationPath }); + repo.Move(new[] { sourcePath }, new[] { destinationPath }); } /// @@ -274,62 +197,10 @@ public virtual void Move(string sourcePath, string destinationPath) /// /// The paths of the files within the working directory which have to be moved/renamed. /// The target paths of the files within the working directory. + [Obsolete("This method will be removed in the next release. Use IRepository.Move() instead.")] public virtual void Move(IEnumerable sourcePaths, IEnumerable destinationPaths) { - Ensure.ArgumentNotNull(sourcePaths, "sourcePaths"); - Ensure.ArgumentNotNull(destinationPaths, "destinationPaths"); - - //TODO: Move() should support following use cases: - // - Moving a file under a directory ('file' and 'dir' -> 'dir/file') - // - Moving a directory (and its content) under another directory ('dir1' and 'dir2' -> 'dir2/dir1/*') - - //TODO: Move() should throw when: - // - Moving a directory under a file - - IDictionary, Tuple> batch = PrepareBatch(sourcePaths, destinationPaths); - - if (batch.Count == 0) - { - throw new ArgumentNullException("sourcePaths"); - } - - foreach (KeyValuePair, Tuple> keyValuePair in batch) - { - string sourcePath = keyValuePair.Key.Item1; - string destPath = keyValuePair.Value.Item1; - - if (Directory.Exists(sourcePath) || Directory.Exists(destPath)) - { - throw new NotImplementedException(); - } - - FileStatus sourceStatus = keyValuePair.Key.Item2; - if (sourceStatus.HasAny(new Enum[] { FileStatus.Nonexistent, FileStatus.Removed, FileStatus.Untracked, FileStatus.Missing })) - { - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Unable to move file '{0}'. Its current status is '{1}'.", sourcePath, sourceStatus)); - } - - FileStatus desStatus = keyValuePair.Value.Item2; - if (desStatus.HasAny(new Enum[] { FileStatus.Nonexistent, FileStatus.Missing })) - { - continue; - } - - throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Unable to overwrite file '{0}'. Its current status is '{1}'.", destPath, desStatus)); - } - - string wd = repo.Info.WorkingDirectory; - foreach (KeyValuePair, Tuple> keyValuePair in batch) - { - string from = keyValuePair.Key.Item1; - string to = keyValuePair.Value.Item1; - - RemoveFromIndex(from); - File.Move(Path.Combine(wd, from), Path.Combine(wd, to)); - AddToIndex(to); - } - - UpdatePhysicalIndex(); + repo.Move(sourcePaths, destinationPaths); } /// @@ -353,11 +224,10 @@ public virtual void Move(IEnumerable sourcePaths, IEnumerable de /// If set, the passed will be treated as an explicit path. /// Use these options to determine how unmatched explicit paths should be handled. /// + [Obsolete("This method will be removed in the next release. Use IRepository.Remove() instead.")] public virtual void Remove(string path, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null) { - Ensure.ArgumentNotNull(path, "path"); - - Remove(new[] { path }, removeFromWorkingDirectory, explicitPathsOptions); + repo.Remove(new[] { path }, removeFromWorkingDirectory, explicitPathsOptions); } /// @@ -381,157 +251,53 @@ public virtual void Remove(string path, bool removeFromWorkingDirectory = true, /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// + [Obsolete("This method will be removed in the next release. Use IRepository.Remove() instead.")] public virtual void Remove(IEnumerable paths, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null) { - Ensure.ArgumentNotNullOrEmptyEnumerable(paths, "paths"); - - var pathsToDelete = paths.Where(p => Directory.Exists(Path.Combine(repo.Info.WorkingDirectory, p))).ToList(); - var notConflictedPaths = new List(); - - foreach (var path in paths) - { - Ensure.ArgumentNotNullOrEmptyString(path, "path"); - - var conflict = repo.Index.Conflicts[path]; - - if (conflict != null) - { - pathsToDelete.Add(RemoveFromIndex(path)); - } - else - { - notConflictedPaths.Add(path); - } - } - - if (notConflictedPaths.Count > 0) - { - pathsToDelete.AddRange(RemoveStagedItems(notConflictedPaths, removeFromWorkingDirectory, explicitPathsOptions)); - } - - if (removeFromWorkingDirectory) - { - RemoveFilesAndFolders(pathsToDelete); - } - - UpdatePhysicalIndex(); - } - - private IEnumerable RemoveStagedItems(IEnumerable paths, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null) - { - var removed = new List(); - var changes = repo.Diff.Compare(DiffModifiers.IncludeUnmodified | DiffModifiers.IncludeUntracked, paths, explicitPathsOptions); - - foreach (var treeEntryChanges in changes) - { - var status = repo.Index.RetrieveStatus(treeEntryChanges.Path); - - switch (treeEntryChanges.Status) - { - case ChangeKind.Added: - case ChangeKind.Deleted: - removed.Add(RemoveFromIndex(treeEntryChanges.Path)); - break; - - case ChangeKind.Unmodified: - if (removeFromWorkingDirectory && ( - status.HasFlag(FileStatus.Staged) || - status.HasFlag(FileStatus.Added))) - { - throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has changes staged in the index. You can call the Remove() method with removeFromWorkingDirectory=false if you want to remove it from the index only.", - treeEntryChanges.Path)); - } - removed.Add(RemoveFromIndex(treeEntryChanges.Path)); - continue; - - case ChangeKind.Modified: - if (status.HasFlag(FileStatus.Modified) && status.HasFlag(FileStatus.Staged)) - { - throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has staged content different from both the working directory and the HEAD.", - treeEntryChanges.Path)); - } - if (removeFromWorkingDirectory) - { - throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has local modifications. You can call the Remove() method with removeFromWorkingDirectory=false if you want to remove it from the index only.", - treeEntryChanges.Path)); - } - removed.Add(RemoveFromIndex(treeEntryChanges.Path)); - continue; - - default: - throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}'. Its current status is '{1}'.", - treeEntryChanges.Path, treeEntryChanges.Status)); - } - } - - return removed; - } - - private void RemoveFilesAndFolders(IEnumerable pathsList) - { - string wd = repo.Info.WorkingDirectory; - - foreach (string path in pathsList) - { - string fileName = Path.Combine(wd, path); - - if (Directory.Exists(fileName)) - { - Directory.Delete(fileName, true); - continue; - } - - if (!File.Exists(fileName)) - { - continue; - } - - File.Delete(fileName); - } + repo.Remove(paths, removeFromWorkingDirectory, explicitPathsOptions); } - private IDictionary, Tuple> PrepareBatch(IEnumerable leftPaths, IEnumerable rightPaths) - { - IDictionary, Tuple> dic = new Dictionary, Tuple>(); - - IEnumerator leftEnum = leftPaths.GetEnumerator(); - IEnumerator rightEnum = rightPaths.GetEnumerator(); - - while (Enumerate(leftEnum, rightEnum)) - { - Tuple from = BuildFrom(leftEnum.Current); - Tuple to = BuildFrom(rightEnum.Current); - dic.Add(from, to); - } - - return dic; - } - - private Tuple BuildFrom(string path) + /// + /// Replaces entries in the staging area with entries from the specified tree. + /// + /// This overwrites all existing state in the staging area. + /// + /// + /// The to read the entries from. + [Obsolete("This method will be removed in the next release. Use Replace instead.")] + public virtual void Reset(Tree source) { - string relativePath = repo.BuildRelativePathFrom(path); - return new Tuple(relativePath, RetrieveStatus(relativePath)); + Replace(source); } - private static bool Enumerate(IEnumerator leftEnum, IEnumerator rightEnum) + /// + /// Replaces entries in the staging area with entries from the specified tree. + /// + /// This overwrites all existing state in the staging area. + /// + /// + /// The to read the entries from. + public virtual void Replace(Tree source) { - bool isLeftEoF = leftEnum.MoveNext(); - bool isRightEoF = rightEnum.MoveNext(); - - if (isLeftEoF == isRightEoF) + using (var obj = new ObjectSafeWrapper(source.Id, repo.Handle)) { - return isLeftEoF; + Proxy.git_index_read_fromtree(this, obj.ObjectPtr); } - throw new ArgumentException("The collection of paths are of different lengths."); + UpdatePhysicalIndex(); } - private void AddToIndex(string relativePath) + /// + /// Clears all entries the index. This is semantically equivalent to + /// creating an empty tree object and resetting the index to that tree. + /// + /// This overwrites all existing state in the staging area. + /// + /// + public virtual void Clear() { - if (!repo.Submodules.TryStage(relativePath, true)) - { - Proxy.git_index_add_bypath(handle, relativePath); - } + Proxy.git_index_clear(this); + UpdatePhysicalIndex(); } private string RemoveFromIndex(string relativePath) @@ -551,13 +317,10 @@ private void UpdatePhysicalIndex() /// /// The relative path within the working directory to the file. /// A representing the state of the parameter. + [Obsolete("This method will be removed in the next release. Use IRepository.RetrieveStatus() instead.")] public virtual FileStatus RetrieveStatus(string filePath) { - Ensure.ArgumentNotNullOrEmptyString(filePath, "filePath"); - - string relativePath = repo.BuildRelativePathFrom(filePath); - - return Proxy.git_status_file(repo.Handle, relativePath); + return repo.RetrieveStatus(filePath); } /// @@ -565,14 +328,13 @@ public virtual FileStatus RetrieveStatus(string filePath) /// /// If set, the options that control the status investigation. /// A holding the state of all the files. + [Obsolete("This method will be removed in the next release. Use IRepository.RetrieveStatus() instead.")] public virtual RepositoryStatus RetrieveStatus(StatusOptions options = null) { - ReloadFromDisk(); - - return new RepositoryStatus(repo, options); + return repo.RetrieveStatus(options); } - internal void Reset(TreeChanges changes) + internal void Replace(TreeChanges changes) { foreach (TreeEntryChanges treeEntryChanges in changes) { @@ -623,11 +385,6 @@ private void ReplaceIndexEntryWith(TreeEntryChanges treeEntryChanges) EncodingMarshaler.Cleanup(indexEntry.Path); } - internal void ReloadFromDisk() - { - Proxy.git_index_read(handle); - } - private string DebuggerDisplay { get diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index d8814ab95..5d302db71 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -83,6 +83,8 @@ + + @@ -103,6 +105,7 @@ + @@ -114,13 +117,14 @@ - + + @@ -135,6 +139,9 @@ + + + @@ -196,6 +203,7 @@ + @@ -223,6 +231,11 @@ + + + + + @@ -324,6 +337,7 @@ + diff --git a/LibGit2Sharp/LibGit2Sharp.v2.ncrunchproject b/LibGit2Sharp/LibGit2Sharp.v2.ncrunchproject new file mode 100644 index 000000000..bb6727939 --- /dev/null +++ b/LibGit2Sharp/LibGit2Sharp.v2.ncrunchproject @@ -0,0 +1,26 @@ + + 1000 + false + false + false + true + false + false + false + false + false + true + true + false + true + true + true + 60000 + + + + AutoDetect + STA + x86 + ..\Lib\NativeBinaries\**.* + \ No newline at end of file diff --git a/LibGit2Sharp/Log.cs b/LibGit2Sharp/Log.cs new file mode 100644 index 000000000..23f79d93e --- /dev/null +++ b/LibGit2Sharp/Log.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + internal class Log + { + private static bool IsEnabled(LogConfiguration configuration, LogLevel level) + { + return (configuration.Level != LogLevel.None && configuration.Level >= level); + } + + internal static bool IsEnabled(LogLevel level) + { + return IsEnabled(GlobalSettings.LogConfiguration, level); + } + + internal static void Write(LogLevel level, string message, params object[] args) + { + LogConfiguration configuration = GlobalSettings.LogConfiguration; + + if (!IsEnabled(configuration, level)) + { + return; + } + + configuration.Handler(level, string.Format(message, args)); + } + } +} diff --git a/LibGit2Sharp/LogConfiguration.cs b/LibGit2Sharp/LogConfiguration.cs new file mode 100644 index 000000000..f31eeac18 --- /dev/null +++ b/LibGit2Sharp/LogConfiguration.cs @@ -0,0 +1,45 @@ +using System; +using LibGit2Sharp.Core; +using LibGit2Sharp.Handlers; + +namespace LibGit2Sharp +{ + /// + /// Logging and tracing configuration for libgit2 and LibGit2Sharp. + /// + public sealed class LogConfiguration + { + /// + /// The default logging configuration, which performs no logging at all. + /// + public static readonly LogConfiguration None = new LogConfiguration { Level = LogLevel.None }; + + /// + /// Creates a new logging configuration to call the given + /// delegate when logging occurs at the given level. + /// + /// Level to log at + /// Handler to call when logging occurs + public LogConfiguration(LogLevel level, LogHandler handler) + { + Ensure.ArgumentConformsTo(level, (t) => { return (level != LogLevel.None); }, "level"); + Ensure.ArgumentNotNull(handler, "handler"); + + Level = level; + Handler = handler; + } + + private LogConfiguration() + { + } + + internal LogLevel Level { get; private set; } + internal LogHandler Handler { get; private set; } + + internal void GitTraceHandler(LogLevel level, IntPtr msg) + { + string message = LaxUtf8Marshaler.FromNative(msg); + Handler(level, message); + } + } +} diff --git a/LibGit2Sharp/LogLevel.cs b/LibGit2Sharp/LogLevel.cs new file mode 100644 index 000000000..472bd5aa5 --- /dev/null +++ b/LibGit2Sharp/LogLevel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LibGit2Sharp +{ + /// + /// Available logging levels. When logging is enabled at a particular + /// level, callers will be provided logging at the given level and all + /// lower levels. + /// + public enum LogLevel + { + /// + /// No logging will be provided. + /// + None = 0, + + /// + /// Severe errors that may impact the program's execution. + /// + Fatal = 1, + + /// + /// Errors that do not impact the program's execution. + /// + Error = 2, + + /// + /// Warnings that suggest abnormal data. + /// + Warning = 3, + + /// + /// Informational messages about program execution. + /// + Info = 4, + + /// + /// Detailed data that allows for debugging. + /// + Debug = 5, + + /// + /// Tracing is exceptionally detailed debugging data. + /// + Trace = 6, + } +} diff --git a/LibGit2Sharp/MergeFetchHeadNotFoundException.cs b/LibGit2Sharp/MergeFetchHeadNotFoundException.cs new file mode 100644 index 000000000..ff4c9cdb6 --- /dev/null +++ b/LibGit2Sharp/MergeFetchHeadNotFoundException.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.Serialization; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The exception that is thrown when the ref to merge with was as part of a pull operation not fetched. + /// + [Serializable] + public class MergeFetchHeadNotFoundException : NotFoundException + { + /// + /// Initializes a new instance of the class. + /// + public MergeFetchHeadNotFoundException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A message that describes the error. + public MergeFetchHeadNotFoundException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. + public MergeFetchHeadNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected MergeFetchHeadNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index da4cd4b0d..e473f0d74 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -36,25 +36,6 @@ public virtual RemoteCollection Remotes get { return remotes.Value; } } - /// - /// List references in a repository. - /// - /// When the remote tips are ahead of the local ones, the retrieved - /// s may point to non existing - /// s in the local repository. In that - /// case, will return null. - /// - /// - /// The to list from. - /// The optional used to connect to remote repository. - /// The references in the repository. - [Obsolete("This will be removed in future release. Use the overload ListReferences(Remote, CredentialsHandler).")] - public virtual IEnumerable ListReferences(Remote remote, Credentials credentials) - { - return ListReferences(remote, - credentials == null ? null : new CredentialsHandler((url, user, type) => credentials)); - } - /// /// List references in a repository. /// @@ -119,7 +100,8 @@ static void DoFetch(RemoteSafeHandle remoteHandle, FetchOptions options, Signatu Proxy.git_remote_set_autotag(remoteHandle, options.TagFetchMode.Value); } - var callbacks = new RemoteCallbacks(options); + var callbacks = new RemoteCallbacks( + options.OnProgress, options.OnTransferProgress, options.OnUpdateTips, options.CredentialsProvider); GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks(); // It is OK to pass the reference to the GitCallbacks directly here because libgit2 makes a copy of @@ -291,7 +273,8 @@ public virtual void Push( // Load the remote. using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repository.Handle, remote.Name, true)) { - var callbacks = new RemoteCallbacks(null, null, null, pushOptions); + var callbacks = new RemoteCallbacks( + null, null, null, pushOptions.CredentialsProvider); GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks(); Proxy.git_remote_set_callbacks(remoteHandle, ref gitCallbacks); diff --git a/LibGit2Sharp/NotFoundException.cs b/LibGit2Sharp/NotFoundException.cs new file mode 100644 index 000000000..c2a32ed00 --- /dev/null +++ b/LibGit2Sharp/NotFoundException.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.Serialization; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// The exception that is thrown attempting to reference a resource that does not exist. + /// + [Serializable] + public class NotFoundException : LibGit2SharpException + { + /// + /// Initializes a new instance of the class. + /// + public NotFoundException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// A message that describes the error. + public NotFoundException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. If the parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception. + public NotFoundException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with a serialized data. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + protected NotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + internal NotFoundException(string message, GitErrorCode code, GitErrorCategory category) + : base(message, code, category) + { + } + } +} diff --git a/LibGit2Sharp/ObjectDatabase.cs b/LibGit2Sharp/ObjectDatabase.cs index d7752b41b..9398855fc 100644 --- a/LibGit2Sharp/ObjectDatabase.cs +++ b/LibGit2Sharp/ObjectDatabase.cs @@ -246,27 +246,22 @@ public virtual Tree CreateTree(TreeDefinition treeDefinition) } /// - /// Inserts a into the object database, referencing an existing . + /// Inserts a into the object database, created from the . /// - /// Prettifing the message includes: - /// * Removing empty lines from the beginning and end. - /// * Removing trailing spaces from every line. - /// * Turning multiple consecutive empty lines between paragraphs into just one empty line. - /// * Ensuring the commit message ends with a newline. - /// * Removing every line starting with "#". + /// It recursively creates tree objects for each of the subtrees stored in the index, but only returns the root tree. + /// + /// + /// The index must be fully merged. /// /// - /// The of who made the change. - /// The of who added the change to the repository. - /// The description of why a change was made to the repository. - /// True to prettify the message, or false to leave it as is - /// The of the to be created. - /// The parents of the to be created. - /// The created . - [Obsolete("This will be removed in future relases. Use the overload CreateCommit(Signature, Signature, string, Tree, IEnumerable, bool, char).")] - public virtual Commit CreateCommit(Signature author, Signature committer, string message, bool prettifyMessage, Tree tree, IEnumerable parents) + /// The . + /// The created . This can be used e.g. to create a . + public virtual Tree CreateTree(Index index) { - return CreateCommit(author, committer, message, tree, parents, prettifyMessage); + Ensure.ArgumentNotNull(index, "index"); + + var treeId = Proxy.git_tree_create_fromindex(index); + return this.repo.Lookup(treeId); } /// diff --git a/LibGit2Sharp/Properties/AssemblyInfo.cs b/LibGit2Sharp/Properties/AssemblyInfo.cs index 4e510cf50..0811ab3d7 100644 --- a/LibGit2Sharp/Properties/AssemblyInfo.cs +++ b/LibGit2Sharp/Properties/AssemblyInfo.cs @@ -42,5 +42,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.19.0")] -[assembly: AssemblyFileVersion("0.19.0")] +[assembly: AssemblyVersion("0.20.0")] +[assembly: AssemblyFileVersion("0.20.0")] diff --git a/LibGit2Sharp/PushOptions.cs b/LibGit2Sharp/PushOptions.cs index d8fa90b2d..15e6af691 100644 --- a/LibGit2Sharp/PushOptions.cs +++ b/LibGit2Sharp/PushOptions.cs @@ -1,19 +1,12 @@ -using System; -using LibGit2Sharp.Handlers; +using LibGit2Sharp.Handlers; namespace LibGit2Sharp { /// /// Collection of parameters controlling Push behavior. /// - public sealed class PushOptions : ICredentialsProvider + public sealed class PushOptions { - /// - /// The to authenticate with during the push. - /// - [Obsolete("This will be removed in future release. Use CredentialsProvider.")] - public Credentials Credentials { get; set; } - /// /// Handler to generate for authentication. /// diff --git a/LibGit2Sharp/Reference.cs b/LibGit2Sharp/Reference.cs index c338bf050..8ab7d1b78 100644 --- a/LibGit2Sharp/Reference.cs +++ b/LibGit2Sharp/Reference.cs @@ -10,11 +10,12 @@ namespace LibGit2Sharp /// A Reference to another git object /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public abstract class Reference : IEquatable + public abstract class Reference : IEquatable, IBelongToARepository { private static readonly LambdaEqualityHelper equalityHelper = new LambdaEqualityHelper(x => x.CanonicalName, x => x.TargetIdentifier); + private readonly IRepository repo; private readonly string canonicalName; private readonly string targetIdentifier; @@ -29,8 +30,19 @@ protected Reference() /// /// The canonical name. /// The target identifier. + [Obsolete("This ctor will be removed in a future release.")] protected Reference(string canonicalName, string targetIdentifier) + : this(null, canonicalName, targetIdentifier) { + } + + /// + /// This would be protected+internal, were that supported by C#. + /// Do not use except in subclasses. + /// + internal Reference(IRepository repo, string canonicalName, string targetIdentifier) + { + this.repo = repo; this.canonicalName = canonicalName; this.targetIdentifier = targetIdentifier; } @@ -48,7 +60,7 @@ internal static T BuildFromPtr(ReferenceSafeHandle handle, Repository repo) w string targetIdentifier = Proxy.git_reference_symbolic_target(handle); var targetRef = repo.Refs[targetIdentifier]; - reference = new SymbolicReference(name, targetIdentifier, targetRef); + reference = new SymbolicReference(repo, name, targetIdentifier, targetRef); break; case GitReferenceType.Oid: @@ -197,5 +209,7 @@ private string DebuggerDisplay "{0} => \"{1}\"", CanonicalName, TargetIdentifier); } } + + IRepository IBelongToARepository.Repository { get { return repo; } } } } diff --git a/LibGit2Sharp/ReferenceCollection.cs b/LibGit2Sharp/ReferenceCollection.cs index a6cf58c42..a6d89c9e0 100644 --- a/LibGit2Sharp/ReferenceCollection.cs +++ b/LibGit2Sharp/ReferenceCollection.cs @@ -151,7 +151,35 @@ public virtual void Remove(Reference reference) /// Message added to the reflog. /// True to allow silent overwriting a potentially existing reference, false otherwise. /// A new . + [Obsolete("This method will be removed in the next release. Please use Rename() instead.")] public virtual Reference Move(Reference reference, string newName, Signature signature, string logMessage = null, bool allowOverwrite = false) + { + return Rename(reference, newName, signature, logMessage, allowOverwrite); + } + + /// + /// Rename an existing reference with a new name + /// + /// The reference to rename. + /// The new canonical name. + /// True to allow silent overwriting a potentially existing reference, false otherwise. + /// A new . + [Obsolete("This method will be removed in the next release. Please use Rename() instead.")] + public virtual Reference Move(Reference reference, string newName, bool allowOverwrite = false) + { + return Rename(reference, newName, null, null, allowOverwrite); + } + + /// + /// Rename an existing reference with a new name, and update the reflog + /// + /// The reference to rename. + /// The new canonical name. + /// Identity used for updating the reflog. + /// Message added to the reflog. + /// True to allow silent overwriting a potentially existing reference, false otherwise. + /// A new . + public virtual Reference Rename(Reference reference, string newName, Signature signature, string logMessage = null, bool allowOverwrite = false) { Ensure.ArgumentNotNull(reference, "reference"); Ensure.ArgumentNotNullOrEmptyString(newName, "newName"); @@ -176,9 +204,9 @@ public virtual Reference Move(Reference reference, string newName, Signature sig /// The new canonical name. /// True to allow silent overwriting a potentially existing reference, false otherwise. /// A new . - public virtual Reference Move(Reference reference, string newName, bool allowOverwrite = false) + public virtual Reference Rename(Reference reference, string newName, bool allowOverwrite = false) { - return Move(reference, newName, null, null, allowOverwrite); + return Rename(reference, newName, null, null, allowOverwrite); } internal T Resolve(string name) where T : Reference diff --git a/LibGit2Sharp/ReferenceCollectionExtensions.cs b/LibGit2Sharp/ReferenceCollectionExtensions.cs index 4442b48da..962a5af94 100644 --- a/LibGit2Sharp/ReferenceCollectionExtensions.cs +++ b/LibGit2Sharp/ReferenceCollectionExtensions.cs @@ -134,8 +134,25 @@ public static Reference UpdateTarget(this ReferenceCollection refsColl, Referenc /// True to allow silent overwriting a potentially existing reference, false otherwise. /// The being worked with. /// A new . + [Obsolete("This method will be removed in the next release. Please use Rename() instead.")] public static Reference Move(this ReferenceCollection refsColl, string currentName, string newName, Signature signature = null, string logMessage = null, bool allowOverwrite = false) + { + return refsColl.Rename(currentName, newName, signature, logMessage, allowOverwrite); + } + + /// + /// Rename an existing reference with a new name + /// + /// The canonical name of the reference to rename. + /// The new canonical name. + /// The identity used for updating the reflog + /// The optional message to log in the + /// True to allow silent overwriting a potentially existing reference, false otherwise. + /// The being worked with. + /// A new . + public static Reference Rename(this ReferenceCollection refsColl, string currentName, string newName, + Signature signature = null, string logMessage = null, bool allowOverwrite = false) { Ensure.ArgumentNotNullOrEmptyString(currentName, "currentName"); @@ -148,7 +165,7 @@ public static Reference Move(this ReferenceCollection refsColl, string currentNa "Reference '{0}' doesn't exist. One cannot move a non existing reference.", currentName)); } - return refsColl.Move(reference, newName, signature, logMessage, allowOverwrite); + return refsColl.Rename(reference, newName, signature, logMessage, allowOverwrite); } /// diff --git a/LibGit2Sharp/ReferenceWrapper.cs b/LibGit2Sharp/ReferenceWrapper.cs index aaf560709..583c68d4f 100644 --- a/LibGit2Sharp/ReferenceWrapper.cs +++ b/LibGit2Sharp/ReferenceWrapper.cs @@ -10,7 +10,7 @@ namespace LibGit2Sharp /// /// The type of the referenced Git object. [DebuggerDisplay("{DebuggerDisplay,nq}")] - public abstract class ReferenceWrapper : IEquatable> where TObject : GitObject + public abstract class ReferenceWrapper : IEquatable>, IBelongToARepository where TObject : GitObject { /// /// The repository. @@ -160,5 +160,7 @@ private string DebuggerDisplay (TargetObject != null) ? TargetObject.Id.ToString(7) : "?"); } } + + IRepository IBelongToARepository.Repository { get { return repo; } } } } diff --git a/LibGit2Sharp/Remote.cs b/LibGit2Sharp/Remote.cs index e999d08d4..bb6de70ff 100644 --- a/LibGit2Sharp/Remote.cs +++ b/LibGit2Sharp/Remote.cs @@ -12,7 +12,7 @@ namespace LibGit2Sharp /// A remote repository whose branches are tracked. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class Remote : IEquatable + public class Remote : IEquatable, IBelongToARepository { private static readonly LambdaEqualityHelper equalityHelper = new LambdaEqualityHelper(x => x.Name, x => x.Url); @@ -105,16 +105,6 @@ public static bool IsValidName(string name) return Proxy.git_remote_is_valid_name(name); } - /// - /// Determines if the proposed remote URL is supported by the library. - /// - /// The URL to be checked. - /// true if the url is supported; false otherwise. - public static bool IsSupportedUrl(string url) - { - return Proxy.git_remote_supported_url(url); - } - /// /// Determines whether the specified is equal to the current . /// @@ -174,5 +164,7 @@ private string DebuggerDisplay "{0} => {1}", Name, Url); } } + + IRepository IBelongToARepository.Repository { get { return repository; } } } } diff --git a/LibGit2Sharp/RemoteCallbacks.cs b/LibGit2Sharp/RemoteCallbacks.cs index 8adb17b4c..b2540dfbd 100644 --- a/LibGit2Sharp/RemoteCallbacks.cs +++ b/LibGit2Sharp/RemoteCallbacks.cs @@ -12,18 +12,6 @@ namespace LibGit2Sharp /// internal class RemoteCallbacks { - internal RemoteCallbacks( - ProgressHandler onProgress = null, - TransferProgressHandler onDownloadProgress = null, - UpdateTipsHandler onUpdateTips = null, - ICredentialsProvider credentialsProvider = null) - { - Progress = onProgress; - DownloadTransferProgress = onDownloadProgress; - UpdateTips = onUpdateTips; - CredentialsProvider = credentialsProvider.GetCredentialsHandler(); - } - internal RemoteCallbacks( ProgressHandler onProgress = null, TransferProgressHandler onDownloadProgress = null, @@ -36,15 +24,6 @@ internal RemoteCallbacks( CredentialsProvider = credentialsProvider; } - internal RemoteCallbacks(FetchOptions fetchOptions) - { - Ensure.ArgumentNotNull(fetchOptions, "fetchOptions"); - Progress = fetchOptions.OnProgress; - DownloadTransferProgress = fetchOptions.OnTransferProgress; - UpdateTips = fetchOptions.OnUpdateTips; - CredentialsProvider = fetchOptions.GetCredentialsHandler(); - } - #region Delegates /// diff --git a/LibGit2Sharp/RemoteCollection.cs b/LibGit2Sharp/RemoteCollection.cs index 276cf6dd5..491e2d874 100644 --- a/LibGit2Sharp/RemoteCollection.cs +++ b/LibGit2Sharp/RemoteCollection.cs @@ -57,11 +57,12 @@ internal Remote RemoteForName(string name, bool shouldThrowIfNotFound = true) /// The updated remote. public virtual Remote Update(Remote remote, params Action[] actions) { - var updater = new RemoteUpdater(this.repository, remote); - - foreach (Action action in actions) + using (var updater = new RemoteUpdater(this.repository, remote)) { - action(updater); + foreach (Action action in actions) + { + action(updater); + } } return this[remote.Name]; diff --git a/LibGit2Sharp/RemoteUpdater.cs b/LibGit2Sharp/RemoteUpdater.cs index 3759ff498..0ad7559ea 100644 --- a/LibGit2Sharp/RemoteUpdater.cs +++ b/LibGit2Sharp/RemoteUpdater.cs @@ -8,12 +8,11 @@ namespace LibGit2Sharp /// /// Exposes properties of a remote that can be updated. /// - public class RemoteUpdater + public class RemoteUpdater : IDisposable { - private readonly Repository repo; - private readonly Remote remote; private readonly UpdatingCollection fetchRefSpecs; private readonly UpdatingCollection pushRefSpecs; + private readonly RemoteSafeHandle remoteHandle; /// /// Needed for mocking purposes. @@ -26,59 +25,55 @@ internal RemoteUpdater(Repository repo, Remote remote) Ensure.ArgumentNotNull(repo, "repo"); Ensure.ArgumentNotNull(remote, "remote"); - this.repo = repo; - this.remote = remote; - fetchRefSpecs = new UpdatingCollection(GetFetchRefSpecs, SetFetchRefSpecs); pushRefSpecs = new UpdatingCollection(GetPushRefSpecs, SetPushRefSpecs); + + remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true); } private IEnumerable GetFetchRefSpecs() { - using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true)) - { - return Proxy.git_remote_get_fetch_refspecs(remoteHandle); - } + return Proxy.git_remote_get_fetch_refspecs(remoteHandle); } private void SetFetchRefSpecs(IEnumerable value) { - using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true)) - { - Proxy.git_remote_set_fetch_refspecs(remoteHandle, value); - Proxy.git_remote_save(remoteHandle); - } + Proxy.git_remote_set_fetch_refspecs(remoteHandle, value); + Proxy.git_remote_save(remoteHandle); } private IEnumerable GetPushRefSpecs() { - using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true)) - { - return Proxy.git_remote_get_push_refspecs(remoteHandle); - } + return Proxy.git_remote_get_push_refspecs(remoteHandle); } private void SetPushRefSpecs(IEnumerable value) { - using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true)) + Proxy.git_remote_set_push_refspecs(remoteHandle, value); + Proxy.git_remote_save(remoteHandle); + } + + /// + /// Set the default TagFetchMode value for the remote. + /// + public virtual TagFetchMode TagFetchMode + { + set { - Proxy.git_remote_set_push_refspecs(remoteHandle, value); + Proxy.git_remote_set_autotag(remoteHandle, value); Proxy.git_remote_save(remoteHandle); } } /// - /// Set the default TagFetchMode value for the remote. + /// Sets the url defined for this /// - public virtual TagFetchMode TagFetchMode + public virtual string Url { set { - using (RemoteSafeHandle remoteHandle = Proxy.git_remote_load(repo.Handle, remote.Name, true)) - { - Proxy.git_remote_set_autotag(remoteHandle, value); - Proxy.git_remote_save(remoteHandle); - } + Proxy.git_remote_set_url(remoteHandle, value); + Proxy.git_remote_save(remoteHandle); } } @@ -86,7 +81,6 @@ public virtual TagFetchMode TagFetchMode /// Sets the list of s defined for this that are intended to /// be used during a Fetch operation /// - /// Changing the list updates the . public virtual ICollection FetchRefSpecs { get { return fetchRefSpecs; } @@ -97,7 +91,6 @@ public virtual ICollection FetchRefSpecs /// Sets or gets the list of s defined for this that are intended to /// be used during a Push operation /// - /// Changing the list updates the . public virtual ICollection PushRefSpecs { get { return pushRefSpecs; } @@ -182,5 +175,13 @@ private void Save() setter(list.Value); } } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + remoteHandle.Dispose(); + } } } diff --git a/LibGit2Sharp/Repository.cs b/LibGit2Sharp/Repository.cs index 6773fe503..1c81bd26d 100644 --- a/LibGit2Sharp/Repository.cs +++ b/LibGit2Sharp/Repository.cs @@ -35,7 +35,6 @@ public sealed class Repository : IRepository private readonly Stack toCleanup = new Stack(); private readonly Ignore ignore; private readonly SubmoduleCollection submodules; - private static readonly Lazy versionRetriever = new Lazy(RetrieveVersion); private readonly Lazy pathCase; /// @@ -572,7 +571,7 @@ public static string Clone(string sourceUrl, string workdirPath, /// Path of the file to blame. /// Specifies optional parameters; if null, the defaults are used. /// The blame for the file. - public BlameHunkCollection Blame(string path, BlameOptions options = null) + public BlameHunkCollection Blame(string path, BlameOptions options) { return new BlameHunkCollection(this, Handle, path, options ?? new BlameOptions()); } @@ -588,7 +587,7 @@ public BlameHunkCollection Blame(string path, BlameOptions options = null) /// controlling checkout behavior. /// Identity for use when updating the reflog. /// The that was checked out. - public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature = null) + public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, Signature signature) { Ensure.ArgumentNotNullOrEmptyString(committishOrBranchSpec, "committishOrBranchSpec"); Ensure.ArgumentNotNull(options, "options"); @@ -638,7 +637,7 @@ public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options, S /// controlling checkout behavior. /// Identity for use when updating the reflog. /// The that was checked out. - public Branch Checkout(Branch branch, CheckoutOptions options, Signature signature = null) + public Branch Checkout(Branch branch, CheckoutOptions options, Signature signature) { Ensure.ArgumentNotNull(branch, "branch"); Ensure.ArgumentNotNull(options, "options"); @@ -675,7 +674,7 @@ public Branch Checkout(Branch branch, CheckoutOptions options, Signature signatu /// controlling checkout behavior. /// Identity for use when updating the reflog. /// The that was checked out. - public Branch Checkout(Commit commit, CheckoutOptions options, Signature signature = null) + public Branch Checkout(Commit commit, CheckoutOptions options, Signature signature) { Ensure.ArgumentNotNull(commit, "commit"); Ensure.ArgumentNotNull(options, "options"); @@ -736,7 +735,21 @@ private void CheckoutTree( /// The target commit object. /// Identity for use when updating the reflog. /// Message to use when updating the reflog. - public void Reset(ResetMode resetMode, Commit commit, Signature signature = null, string logMessage = null) + public void Reset(ResetMode resetMode, Commit commit, Signature signature, string logMessage) + { + Reset(resetMode, commit, new CheckoutOptions(), signature, logMessage); + } + + /// + /// Sets the current to the specified commit and optionally resets the and + /// the content of the working tree to match. + /// + /// Flavor of reset operation to perform. + /// The target commit object. + /// Collection of parameters controlling checkout behavior. + /// Identity for use when updating the reflog. + /// Message to use when updating the reflog. + private void Reset(ResetMode resetMode, Commit commit, IConvertableToGitCheckoutOpts opts, Signature signature, string logMessage) { Ensure.ArgumentNotNull(commit, "commit"); @@ -747,7 +760,11 @@ public void Reset(ResetMode resetMode, Commit commit, Signature signature = null "reset: moving to {0}", commit.Sha); } - Proxy.git_reset(handle, commit.Id, resetMode, signature.OrDefault(Config), logMessage); + using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(opts)) + { + var options = checkoutOptionsWrapper.Options; + Proxy.git_reset(handle, commit.Id, resetMode, ref options, signature.OrDefault(Config), logMessage); + } } /// @@ -759,7 +776,7 @@ public void Reset(ResetMode resetMode, Commit commit, Signature signature = null /// A revparse spec for the commit or branch to checkout paths from. /// The paths to checkout. Will throw if null is passed in. Passing an empty enumeration results in nothing being checked out. /// Collection of parameters controlling checkout behavior. - public void CheckoutPaths(string committishOrBranchSpec, IEnumerable paths, CheckoutOptions checkoutOptions = null) + public void CheckoutPaths(string committishOrBranchSpec, IEnumerable paths, CheckoutOptions checkoutOptions) { Ensure.ArgumentNotNullOrEmptyString(committishOrBranchSpec, "committishOrBranchSpec"); Ensure.ArgumentNotNull(paths, "paths"); @@ -783,7 +800,7 @@ public void CheckoutPaths(string committishOrBranchSpec, IEnumerable pat /// If set, the passed will be treated as explicit paths. /// Use these options to determine how unmatched explicit paths should be handled. /// - public void Reset(Commit commit, IEnumerable paths = null, ExplicitPathsOptions explicitPathsOptions = null) + public void Reset(Commit commit, IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) { if (Info.IsBare) { @@ -793,7 +810,7 @@ public void Reset(Commit commit, IEnumerable paths = null, ExplicitPaths Ensure.ArgumentNotNull(commit, "commit"); var changes = Diff.Compare(commit.Tree, DiffTargets.Index, paths, explicitPathsOptions, new CompareOptions { Similarity = SimilarityOptions.None }); - Index.Reset(changes); + Index.Replace(changes); } /// @@ -806,7 +823,7 @@ public void Reset(Commit commit, IEnumerable paths = null, ExplicitPaths /// The of who added the change to the repository. /// The that specify the commit behavior. /// The generated . - public Commit Commit(string message, Signature author, Signature committer, CommitOptions options = null) + public Commit Commit(string message, Signature author, Signature committer, CommitOptions options) { if (options == null) { @@ -951,38 +968,10 @@ internal T RegisterForCleanup(T disposable) where T : IDisposable /// Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features) /// /// + [Obsolete("This property will be removed in the next release. Use GlobalSettings.Version instead.")] public static string Version { - get { return versionRetriever.Value; } - } - - private static string RetrieveVersion() - { - Assembly assembly = typeof(Repository).Assembly; - - Version version = assembly.GetName().Version; - - string libgit2Hash = ReadContentFromResource(assembly, "libgit2_hash.txt"); - string libgit2sharpHash = ReadContentFromResource(assembly, "libgit2sharp_hash.txt"); - string features = GlobalSettings.Features().ToString(); - - return string.Format( - CultureInfo.InvariantCulture, - "{0}-{1}-{2} ({3} - {4})", - version.ToString(3), - libgit2sharpHash.Substring(0, 7), - libgit2Hash.Substring(0, 7), - NativeMethods.ProcessorArchitecture, - features); - } - - private static string ReadContentFromResource(Assembly assembly, string partialResourceName) - { - string name = string.Format(CultureInfo.InvariantCulture, "LibGit2Sharp.{0}", partialResourceName); - using (var sr = new StreamReader(assembly.GetManifestResourceStream(name))) - { - return sr.ReadLine(); - } + get { return GlobalSettings.Version.ToString(); } } /// @@ -992,16 +981,16 @@ private static string ReadContentFromResource(Assembly assembly, string partialR /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - public MergeResult Merge(Commit commit, Signature merger, MergeOptions options = null) + public MergeResult Merge(Commit commit, Signature merger, MergeOptions options) { Ensure.ArgumentNotNull(commit, "commit"); Ensure.ArgumentNotNull(merger, "merger"); options = options ?? new MergeOptions(); - using (GitMergeHeadHandle mergeHeadHandle = Proxy.git_merge_head_from_id(Handle, commit.Id.Oid)) + using (GitAnnotatedCommitHandle annotatedCommitHandle = Proxy.git_annotated_commit_lookup(Handle, commit.Id.Oid)) { - return Merge(new[] { mergeHeadHandle }, merger, options); + return Merge(new[] { annotatedCommitHandle }, merger, options); } } @@ -1012,7 +1001,7 @@ public MergeResult Merge(Commit commit, Signature merger, MergeOptions options = /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - public MergeResult Merge(Branch branch, Signature merger, MergeOptions options = null) + public MergeResult Merge(Branch branch, Signature merger, MergeOptions options) { Ensure.ArgumentNotNull(branch, "branch"); Ensure.ArgumentNotNull(merger, "merger"); @@ -1020,9 +1009,9 @@ public MergeResult Merge(Branch branch, Signature merger, MergeOptions options = options = options ?? new MergeOptions(); using (ReferenceSafeHandle referencePtr = Refs.RetrieveReferencePtr(branch.CanonicalName)) - using (GitMergeHeadHandle mergeHeadHandle = Proxy.git_merge_head_from_ref(Handle, referencePtr)) + using (GitAnnotatedCommitHandle annotatedCommitHandle = Proxy.git_annotated_commit_from_ref(Handle, referencePtr)) { - return Merge(new[] { mergeHeadHandle }, merger, options); + return Merge(new[] { annotatedCommitHandle }, merger, options); } } @@ -1033,7 +1022,7 @@ public MergeResult Merge(Branch branch, Signature merger, MergeOptions options = /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - public MergeResult Merge(string committish, Signature merger, MergeOptions options = null) + public MergeResult Merge(string committish, Signature merger, MergeOptions options) { Ensure.ArgumentNotNull(committish, "committish"); Ensure.ArgumentNotNull(merger, "merger"); @@ -1061,39 +1050,54 @@ internal MergeResult MergeFetchHeads(Signature merger, MergeOptions options) if (fetchHeads.Length == 0) { - throw new LibGit2SharpException("Remote ref to merge from was not fetched."); + var expectedRef = this.Head.UpstreamBranchCanonicalName; + throw new MergeFetchHeadNotFoundException(string.Format(CultureInfo.InvariantCulture, + "The current branch is configured to merge with the reference '{0}' from the remote, but this reference was not fetched.", expectedRef)); } - GitMergeHeadHandle[] mergeHeadHandles = fetchHeads.Select(fetchHead => - Proxy.git_merge_head_from_fetchhead(Handle, fetchHead.RemoteCanonicalName, fetchHead.Url, fetchHead.Target.Id.Oid)).ToArray(); + GitAnnotatedCommitHandle[] annotatedCommitHandles = fetchHeads.Select(fetchHead => + Proxy.git_annotated_commit_from_fetchhead(Handle, fetchHead.RemoteCanonicalName, fetchHead.Url, fetchHead.Target.Id.Oid)).ToArray(); try { // Perform the merge. - return Merge(mergeHeadHandles, merger, options); + return Merge(annotatedCommitHandles, merger, options); } finally { // Cleanup. - foreach (GitMergeHeadHandle mergeHeadHandle in mergeHeadHandles) + foreach (GitAnnotatedCommitHandle annotatedCommitHandle in annotatedCommitHandles) { - mergeHeadHandle.Dispose(); + annotatedCommitHandle.Dispose(); } } } /// /// Revert the specified commit. + /// + /// If the revert is successful but there are no changes to commit, + /// then the will be . + /// If the revert is successful and there are changes to revert, then + /// the will be . + /// If the revert resulted in conflicts, then the + /// will be . + /// /// /// The to revert. /// The of who is performing the revert. /// controlling revert behavior. /// The result of the revert. - public RevertResult Revert(Commit commit, Signature reverter, RevertOptions options = null) + public RevertResult Revert(Commit commit, Signature reverter, RevertOptions options) { Ensure.ArgumentNotNull(commit, "commit"); Ensure.ArgumentNotNull(reverter, "reverter"); + if (Info.IsHeadUnborn) + { + throw new UnbornBranchException("Can not revert the commit. The Head doesn't point at a commit."); + } + options = options ?? new RevertOptions(); RevertResult result = null; @@ -1123,12 +1127,39 @@ public RevertResult Revert(Commit commit, Signature reverter, RevertOptions opti if (Index.IsFullyMerged) { Commit revertCommit = null; + + // Check if the revert generated any changes + // and set the revert status accordingly + bool anythingToRevert = RetrieveStatus( + new StatusOptions() + { + DetectRenamesInIndex = false, + Show = StatusShowOption.IndexOnly + }).Any(); + + RevertStatus revertStatus = anythingToRevert ? + RevertStatus.Reverted : RevertStatus.NothingToRevert; + if (options.CommitOnSuccess) { - revertCommit = this.Commit(Info.Message, author: reverter, committer: reverter); + if (!anythingToRevert) + { + // If there were no changes to revert, and we are + // asked to commit the changes, then cleanup + // the repository state (following command line behavior). + Proxy.git_repository_state_cleanup(handle); + } + else + { + revertCommit = this.Commit( + Info.Message, + author: reverter, + committer: reverter, + options: null); + } } - result = new RevertResult(RevertStatus.Reverted, revertCommit); + result = new RevertResult(revertStatus, revertCommit); } else { @@ -1146,7 +1177,7 @@ public RevertResult Revert(Commit commit, Signature reverter, RevertOptions opti /// The of who is performing the cherry pick. /// controlling cherry pick behavior. /// The result of the cherry pick. - public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options = null) + public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPickOptions options) { Ensure.ArgumentNotNull(commit, "commit"); Ensure.ArgumentNotNull(committer, "committer"); @@ -1182,7 +1213,7 @@ public CherryPickResult CherryPick(Commit commit, Signature committer, CherryPic Commit cherryPickCommit = null; if (options.CommitOnSuccess) { - cherryPickCommit = this.Commit(Info.Message, commit.Author, committer); + cherryPickCommit = this.Commit(Info.Message, commit.Author, committer, null); } result = new CherryPickResult(CherryPickStatus.CherryPicked, cherryPickCommit); @@ -1214,16 +1245,16 @@ private FastForwardStrategy FastForwardStrategyFromMergePreference(GitMergePrefe /// /// Internal implementation of merge. /// - /// Merge heads to operate on. + /// Merge heads to operate on. /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - private MergeResult Merge(GitMergeHeadHandle[] mergeHeads, Signature merger, MergeOptions options) + private MergeResult Merge(GitAnnotatedCommitHandle[] annotatedCommits, Signature merger, MergeOptions options) { GitMergeAnalysis mergeAnalysis; GitMergePreference mergePreference; - Proxy.git_merge_analysis(Handle, mergeHeads, out mergeAnalysis, out mergePreference); + Proxy.git_merge_analysis(Handle, annotatedCommits, out mergeAnalysis, out mergePreference); MergeResult mergeResult = null; @@ -1240,29 +1271,29 @@ private MergeResult Merge(GitMergeHeadHandle[] mergeHeads, Signature merger, Mer case FastForwardStrategy.Default: if (mergeAnalysis.HasFlag(GitMergeAnalysis.GIT_MERGE_ANALYSIS_FASTFORWARD)) { - if (mergeHeads.Length != 1) + if (annotatedCommits.Length != 1) { // We should not reach this code unless there is a bug somewhere. throw new LibGit2SharpException("Unable to perform Fast-Forward merge with mith multiple merge heads."); } - mergeResult = FastForwardMerge(mergeHeads[0], merger, options); + mergeResult = FastForwardMerge(annotatedCommits[0], merger, options); } else if (mergeAnalysis.HasFlag(GitMergeAnalysis.GIT_MERGE_ANALYSIS_NORMAL)) { - mergeResult = NormalMerge(mergeHeads, merger, options); + mergeResult = NormalMerge(annotatedCommits, merger, options); } break; case FastForwardStrategy.FastForwardOnly: if (mergeAnalysis.HasFlag(GitMergeAnalysis.GIT_MERGE_ANALYSIS_FASTFORWARD)) { - if (mergeHeads.Length != 1) + if (annotatedCommits.Length != 1) { // We should not reach this code unless there is a bug somewhere. throw new LibGit2SharpException("Unable to perform Fast-Forward merge with mith multiple merge heads."); } - mergeResult = FastForwardMerge(mergeHeads[0], merger, options); + mergeResult = FastForwardMerge(annotatedCommits[0], merger, options); } else { @@ -1274,7 +1305,7 @@ private MergeResult Merge(GitMergeHeadHandle[] mergeHeads, Signature merger, Mer case FastForwardStrategy.NoFastFoward: if (mergeAnalysis.HasFlag(GitMergeAnalysis.GIT_MERGE_ANALYSIS_NORMAL)) { - mergeResult = NormalMerge(mergeHeads, merger, options); + mergeResult = NormalMerge(annotatedCommits, merger, options); } break; default: @@ -1294,11 +1325,11 @@ private MergeResult Merge(GitMergeHeadHandle[] mergeHeads, Signature merger, Mer /// /// Perform a normal merge (i.e. a non-fast-forward merge). /// - /// The merge head handles to merge. + /// The merge head handles to merge. /// The of who is performing the merge. /// Specifies optional parameters controlling merge behavior; if null, the defaults are used. /// The of the merge. - private MergeResult NormalMerge(GitMergeHeadHandle[] mergeHeads, Signature merger, MergeOptions options) + private MergeResult NormalMerge(GitAnnotatedCommitHandle[] annotatedCommits, Signature merger, MergeOptions options) { MergeResult mergeResult; @@ -1316,7 +1347,7 @@ private MergeResult NormalMerge(GitMergeHeadHandle[] mergeHeads, Signature merge { var checkoutOpts = checkoutOptionsWrapper.Options; - Proxy.git_merge(Handle, mergeHeads, mergeOptions, checkoutOpts); + Proxy.git_merge(Handle, annotatedCommits, mergeOptions, checkoutOpts); } if (Index.IsFullyMerged) @@ -1325,7 +1356,7 @@ private MergeResult NormalMerge(GitMergeHeadHandle[] mergeHeads, Signature merge if (options.CommitOnSuccess) { // Commit the merge - mergeCommit = Commit(Info.Message, author: merger, committer: merger); + mergeCommit = Commit(Info.Message, author: merger, committer: merger, options: null); } mergeResult = new MergeResult(MergeStatus.NonFastForward, mergeCommit); @@ -1341,13 +1372,13 @@ private MergeResult NormalMerge(GitMergeHeadHandle[] mergeHeads, Signature merge /// /// Perform a fast-forward merge. /// - /// The merge head handle to fast-forward merge. + /// The merge head handle to fast-forward merge. /// The of who is performing the merge. /// Options controlling merge behavior. /// The of the merge. - private MergeResult FastForwardMerge(GitMergeHeadHandle mergeHead, Signature merger, MergeOptions options) + private MergeResult FastForwardMerge(GitAnnotatedCommitHandle annotatedCommit, Signature merger, MergeOptions options) { - ObjectId id = Proxy.git_merge_head_id(mergeHead); + ObjectId id = Proxy.git_annotated_commit_id(annotatedCommit); Commit fastForwardCommit = (Commit) Lookup(id, ObjectType.Commit); Ensure.GitObjectIsNotNull(fastForwardCommit, id.Sha); @@ -1423,6 +1454,424 @@ internal FilePath[] ToFilePaths(IEnumerable paths) return filePaths.ToArray(); } + /// + /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). + /// + /// If this path is ignored by configuration then it will not be staged unless is unset. + /// + /// The path of the file within the working directory. + /// Determines how paths will be staged. + public void Stage(string path, StageOptions stageOptions) + { + Ensure.ArgumentNotNull(path, "path"); + + Stage(new[] { path }, stageOptions); + } + + /// + /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). + /// + /// Any paths (even those listed explicitly) that are ignored by configuration will not be staged unless is unset. + /// + /// The collection of paths of the files within the working directory. + /// Determines how paths will be staged. + public void Stage(IEnumerable paths, StageOptions stageOptions) + { + Ensure.ArgumentNotNull(paths, "paths"); + + DiffModifiers diffModifiers = DiffModifiers.IncludeUntracked; + ExplicitPathsOptions explicitPathsOptions = stageOptions != null ? stageOptions.ExplicitPathsOptions : null; + + if (stageOptions != null && stageOptions.IncludeIgnored) + { + diffModifiers |= DiffModifiers.IncludeIgnored; + } + + var changes = Diff.Compare(diffModifiers, paths, explicitPathsOptions); + + foreach (var treeEntryChanges in changes) + { + switch (treeEntryChanges.Status) + { + case ChangeKind.Unmodified: + continue; + + case ChangeKind.Deleted: + RemoveFromIndex(treeEntryChanges.Path); + continue; + + case ChangeKind.Added: + /* Fall through */ + case ChangeKind.Modified: + AddToIndex(treeEntryChanges.Path); + continue; + + default: + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "Entry '{0}' bears an unexpected ChangeKind '{1}'", treeEntryChanges.Path, treeEntryChanges.Status)); + } + } + + UpdatePhysicalIndex(); + } + + /// + /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). + /// + /// The path of the file within the working directory. + /// + /// The passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public void Unstage(string path, ExplicitPathsOptions explicitPathsOptions) + { + Ensure.ArgumentNotNull(path, "path"); + + Unstage(new[] { path }, explicitPathsOptions); + } + + /// + /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). + /// + /// The collection of paths of the files within the working directory. + /// + /// The passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public void Unstage(IEnumerable paths, ExplicitPathsOptions explicitPathsOptions) + { + Ensure.ArgumentNotNull(paths, "paths"); + + if (Info.IsHeadUnborn) + { + var changes = Diff.Compare(null, DiffTargets.Index, paths, explicitPathsOptions, new CompareOptions { Similarity = SimilarityOptions.None }); + + Index.Replace(changes); + } + else + { + this.Reset("HEAD", paths, explicitPathsOptions); + } + } + + /// + /// Moves and/or renames a file in the working directory and promotes the change to the staging area. + /// + /// The path of the file within the working directory which has to be moved/renamed. + /// The target path of the file within the working directory. + public void Move(string sourcePath, string destinationPath) + { + Move(new[] { sourcePath }, new[] { destinationPath }); + } + + /// + /// Moves and/or renames a collection of files in the working directory and promotes the changes to the staging area. + /// + /// The paths of the files within the working directory which have to be moved/renamed. + /// The target paths of the files within the working directory. + public void Move(IEnumerable sourcePaths, IEnumerable destinationPaths) + { + Ensure.ArgumentNotNull(sourcePaths, "sourcePaths"); + Ensure.ArgumentNotNull(destinationPaths, "destinationPaths"); + + //TODO: Move() should support following use cases: + // - Moving a file under a directory ('file' and 'dir' -> 'dir/file') + // - Moving a directory (and its content) under another directory ('dir1' and 'dir2' -> 'dir2/dir1/*') + + //TODO: Move() should throw when: + // - Moving a directory under a file + + IDictionary, Tuple> batch = PrepareBatch(sourcePaths, destinationPaths); + + if (batch.Count == 0) + { + throw new ArgumentNullException("sourcePaths"); + } + + foreach (KeyValuePair, Tuple> keyValuePair in batch) + { + string sourcePath = keyValuePair.Key.Item1; + string destPath = keyValuePair.Value.Item1; + + if (Directory.Exists(sourcePath) || Directory.Exists(destPath)) + { + throw new NotImplementedException(); + } + + FileStatus sourceStatus = keyValuePair.Key.Item2; + if (sourceStatus.HasAny(new Enum[] { FileStatus.Nonexistent, FileStatus.Removed, FileStatus.Untracked, FileStatus.Missing })) + { + throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Unable to move file '{0}'. Its current status is '{1}'.", sourcePath, sourceStatus)); + } + + FileStatus desStatus = keyValuePair.Value.Item2; + if (desStatus.HasAny(new Enum[] { FileStatus.Nonexistent, FileStatus.Missing })) + { + continue; + } + + throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Unable to overwrite file '{0}'. Its current status is '{1}'.", destPath, desStatus)); + } + + string wd = Info.WorkingDirectory; + foreach (KeyValuePair, Tuple> keyValuePair in batch) + { + string from = keyValuePair.Key.Item1; + string to = keyValuePair.Value.Item1; + + RemoveFromIndex(from); + File.Move(Path.Combine(wd, from), Path.Combine(wd, to)); + AddToIndex(to); + } + + UpdatePhysicalIndex(); + } + + /// + /// Removes a file from the staging area, and optionally removes it from the working directory as well. + /// + /// If the file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the file from the working directory as well. + /// + /// + /// When not passing a , the passed path will be treated as + /// a pathspec. You can for example use it to pass the relative path to a folder inside the working directory, + /// so that all files beneath this folders, and the folder itself, will be removed. + /// + /// + /// The path of the file within the working directory. + /// True to remove the file from the working directory, False otherwise. + /// + /// The passed will be treated as an explicit path. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) + { + Ensure.ArgumentNotNull(path, "path"); + + Remove(new[] { path }, removeFromWorkingDirectory, explicitPathsOptions); + } + + /// + /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. + /// + /// If a file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the files from the working directory as well. + /// + /// + /// When not passing a , the passed paths will be treated as + /// a pathspec. You can for example use it to pass the relative paths to folders inside the working directory, + /// so that all files beneath these folders, and the folders themselves, will be removed. + /// + /// + /// The collection of paths of the files within the working directory. + /// True to remove the files from the working directory, False otherwise. + /// + /// The passed will be treated as explicit paths. + /// Use these options to determine how unmatched explicit paths should be handled. + /// + public void Remove(IEnumerable paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions) + { + Ensure.ArgumentNotNullOrEmptyEnumerable(paths, "paths"); + + var pathsToDelete = paths.Where(p => Directory.Exists(Path.Combine(Info.WorkingDirectory, p))).ToList(); + var notConflictedPaths = new List(); + + foreach (var path in paths) + { + Ensure.ArgumentNotNullOrEmptyString(path, "path"); + + var conflict = Index.Conflicts[path]; + + if (conflict != null) + { + pathsToDelete.Add(RemoveFromIndex(path)); + } + else + { + notConflictedPaths.Add(path); + } + } + + if (notConflictedPaths.Count > 0) + { + pathsToDelete.AddRange(RemoveStagedItems(notConflictedPaths, removeFromWorkingDirectory, explicitPathsOptions)); + } + + if (removeFromWorkingDirectory) + { + RemoveFilesAndFolders(pathsToDelete); + } + + UpdatePhysicalIndex(); + } + + /// + /// Retrieves the state of a file in the working directory, comparing it against the staging area and the latest commmit. + /// + /// The relative path within the working directory to the file. + /// A representing the state of the parameter. + public FileStatus RetrieveStatus(string filePath) + { + Ensure.ArgumentNotNullOrEmptyString(filePath, "filePath"); + + string relativePath = this.BuildRelativePathFrom(filePath); + + return Proxy.git_status_file(Handle, relativePath); + } + + /// + /// Retrieves the state of all files in the working directory, comparing them against the staging area and the latest commmit. + /// + /// If set, the options that control the status investigation. + /// A holding the state of all the files. + public RepositoryStatus RetrieveStatus(StatusOptions options) + { + ReloadFromDisk(); + + return new RepositoryStatus(this, options); + } + + internal void ReloadFromDisk() + { + Proxy.git_index_read(Index.Handle); + } + + private void AddToIndex(string relativePath) + { + if (!Submodules.TryStage(relativePath, true)) + { + Proxy.git_index_add_bypath(Index.Handle, relativePath); + } + } + + private string RemoveFromIndex(string relativePath) + { + Proxy.git_index_remove_bypath(Index.Handle, relativePath); + + return relativePath; + } + + private void UpdatePhysicalIndex() + { + Proxy.git_index_write(Index.Handle); + } + + private Tuple BuildFrom(string path) + { + string relativePath = this.BuildRelativePathFrom(path); + return new Tuple(relativePath, RetrieveStatus(relativePath)); + } + + private static bool Enumerate(IEnumerator leftEnum, IEnumerator rightEnum) + { + bool isLeftEoF = leftEnum.MoveNext(); + bool isRightEoF = rightEnum.MoveNext(); + + if (isLeftEoF == isRightEoF) + { + return isLeftEoF; + } + + throw new ArgumentException("The collection of paths are of different lengths."); + } + + private IDictionary, Tuple> PrepareBatch(IEnumerable leftPaths, IEnumerable rightPaths) + { + IDictionary, Tuple> dic = new Dictionary, Tuple>(); + + IEnumerator leftEnum = leftPaths.GetEnumerator(); + IEnumerator rightEnum = rightPaths.GetEnumerator(); + + while (Enumerate(leftEnum, rightEnum)) + { + Tuple from = BuildFrom(leftEnum.Current); + Tuple to = BuildFrom(rightEnum.Current); + dic.Add(from, to); + } + + return dic; + } + + private void RemoveFilesAndFolders(IEnumerable pathsList) + { + string wd = Info.WorkingDirectory; + + foreach (string path in pathsList) + { + string fileName = Path.Combine(wd, path); + + if (Directory.Exists(fileName)) + { + Directory.Delete(fileName, true); + continue; + } + + if (!File.Exists(fileName)) + { + continue; + } + + File.Delete(fileName); + } + } + + private IEnumerable RemoveStagedItems(IEnumerable paths, bool removeFromWorkingDirectory = true, ExplicitPathsOptions explicitPathsOptions = null) + { + var removed = new List(); + var changes = Diff.Compare(DiffModifiers.IncludeUnmodified | DiffModifiers.IncludeUntracked, paths, explicitPathsOptions); + + foreach (var treeEntryChanges in changes) + { + var status = RetrieveStatus(treeEntryChanges.Path); + + switch (treeEntryChanges.Status) + { + case ChangeKind.Added: + case ChangeKind.Deleted: + removed.Add(RemoveFromIndex(treeEntryChanges.Path)); + break; + + case ChangeKind.Unmodified: + if (removeFromWorkingDirectory && ( + status.HasFlag(FileStatus.Staged) || + status.HasFlag(FileStatus.Added) )) + { + throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has changes staged in the index. You can call the Remove() method with removeFromWorkingDirectory=false if you want to remove it from the index only.", + treeEntryChanges.Path)); + } + removed.Add(RemoveFromIndex(treeEntryChanges.Path)); + continue; + + case ChangeKind.Modified: + if (status.HasFlag(FileStatus.Modified) && status.HasFlag(FileStatus.Staged)) + { + throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has staged content different from both the working directory and the HEAD.", + treeEntryChanges.Path)); + } + if (removeFromWorkingDirectory) + { + throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}', as it has local modifications. You can call the Remove() method with removeFromWorkingDirectory=false if you want to remove it from the index only.", + treeEntryChanges.Path)); + } + removed.Add(RemoveFromIndex(treeEntryChanges.Path)); + continue; + + default: + throw new RemoveFromIndexException(string.Format(CultureInfo.InvariantCulture, "Unable to remove file '{0}'. Its current status is '{1}'.", + treeEntryChanges.Path, treeEntryChanges.Status)); + } + } + + return removed; + } + private string DebuggerDisplay { get diff --git a/LibGit2Sharp/RepositoryExtensions.cs b/LibGit2Sharp/RepositoryExtensions.cs index 278614dec..a86064a45 100644 --- a/LibGit2Sharp/RepositoryExtensions.cs +++ b/LibGit2Sharp/RepositoryExtensions.cs @@ -439,5 +439,304 @@ internal static ObjectId Committish(this Repository repo, object identifier) { return repo.Committishes(identifier, true).First(); } + + /// + /// Merges changes from branch into the branch pointed at by HEAD. + /// + /// The being worked with. + /// The branch to merge into the branch pointed at by HEAD. + /// The of who is performing the merge. + /// The of the merge. + public static MergeResult Merge(this IRepository repository, Branch branch, Signature merger) + { + return repository.Merge(branch, merger, null); + } + + /// + /// Merges changes from the commit into the branch pointed at by HEAD. + /// + /// The being worked with. + /// The commit to merge into the branch pointed at by HEAD. + /// The of who is performing the merge. + /// The of the merge. + public static MergeResult Merge(this IRepository repository, string committish, Signature merger) + { + return repository.Merge(committish, merger, null); + } + + /// + /// Checkout the tip commit of the specified object. If this commit is the + /// current tip of the branch, will checkout the named branch. Otherwise, will checkout the tip commit + /// as a detached HEAD. + /// + /// The being worked with. + /// The to check out. + /// controlling checkout behavior. + /// The that was checked out. + public static Branch Checkout(this IRepository repository, Branch branch, CheckoutOptions options) + { + return repository.Checkout(branch, options, null); + } + + /// + /// Checkout the specified . + /// + /// Will detach the HEAD and make it point to this commit sha. + /// + /// + /// The being worked with. + /// The to check out. + /// controlling checkout behavior. + /// The that was checked out. + public static Branch Checkout(this IRepository repository, Commit commit, CheckoutOptions options) + { + return repository.Checkout(commit, options, null); + } + + /// + /// Checkout the specified , reference or SHA. + /// + /// If the committishOrBranchSpec parameter resolves to a branch name, then the checked out HEAD will + /// will point to the branch. Otherwise, the HEAD will be detached, pointing at the commit sha. + /// + /// + /// The being worked with. + /// A revparse spec for the commit or branch to checkout. + /// controlling checkout behavior. + /// The that was checked out. + public static Branch Checkout(this IRepository repository, string committishOrBranchSpec, CheckoutOptions options) + { + return repository.Checkout(committishOrBranchSpec, options, null); + } + + /// + /// Updates specifed paths in the index and working directory with the versions from the specified branch, reference, or SHA. + /// + /// This method does not switch branches or update the current repository HEAD. + /// + /// + /// The being worked with. + /// A revparse spec for the commit or branch to checkout paths from. + /// The paths to checkout. Will throw if null is passed in. Passing an empty enumeration results in nothing being checked out. + public static void CheckoutPaths(this IRepository repository, string committishOrBranchSpec, IEnumerable paths) + { + repository.CheckoutPaths(committishOrBranchSpec, paths, null); + } + + /// + /// Sets the current to the specified commit and optionally resets the and + /// the content of the working tree to match. + /// + /// The being worked with. + /// Flavor of reset operation to perform. + /// The target commit object. + public static void Reset(this IRepository repository, ResetMode resetMode, Commit commit) + { + repository.Reset(resetMode, commit, null, null); + } + + /// + /// Replaces entries in the with entries from the specified commit. + /// + /// The being worked with. + /// The target commit object. + /// The list of paths (either files or directories) that should be considered. + public static void Reset(this IRepository repository, Commit commit, IEnumerable paths) + { + repository.Reset(commit, paths, null); + } + + /// + /// Replaces entries in the with entries from the specified commit. + /// + /// The being worked with. + /// The target commit object. + public static void Reset(this IRepository repository, Commit commit) + { + repository.Reset(commit, null, null); + } + + /// + /// Stores the content of the as a new into the repository. + /// The tip of the will be used as the parent of this new Commit. + /// Once the commit is created, the will move forward to point at it. + /// + /// The being worked with. + /// The description of why a change was made to the repository. + /// The of who made the change. + /// The of who added the change to the repository. + /// The generated . + public static Commit Commit(this IRepository repository, string message, Signature author, Signature committer) + { + return repository.Commit(message, author, committer, null); + } + + /// + /// Find where each line of a file originated. + /// + /// The being worked with. + /// Path of the file to blame. + /// The blame for the file. + public static BlameHunkCollection Blame(this IRepository repository, string path) + { + return repository.Blame(path, null); + } + + /// + /// Cherry-picks the specified commit. + /// + /// The being worked with. + /// The to cherry-pick. + /// The of who is performing the cherry pick. + /// The result of the cherry pick. + public static CherryPickResult CherryPick(this IRepository repository, Commit commit, Signature committer) + { + return repository.CherryPick(commit, committer, null); + } + + /// + /// Merges changes from commit into the branch pointed at by HEAD. + /// + /// The being worked with. + /// The commit to merge into the branch pointed at by HEAD. + /// The of who is performing the merge. + /// The of the merge. + public static MergeResult Merge(this IRepository repository, Commit commit, Signature merger) + { + return repository.Merge(commit, merger, null); + } + + /// + /// Revert the specified commit. + /// + /// The being worked with. + /// The to revert. + /// The of who is performing the revert. + /// The result of the revert. + public static RevertResult Revert(this IRepository repository, Commit commit, Signature reverter) + { + return repository.Revert(commit, reverter, null); + } + + /// + /// Promotes to the staging area the latest modifications of a file in the working directory (addition, updation or removal). + /// + /// The being worked with. + /// The path of the file within the working directory. + public static void Stage(this IRepository repository, string path) + { + repository.Stage(path, null); + } + + /// + /// Promotes to the staging area the latest modifications of a collection of files in the working directory (addition, updation or removal). + /// + /// The being worked with. + /// The collection of paths of the files within the working directory. + public static void Stage(this IRepository repository, IEnumerable paths) + { + repository.Stage(paths, null); + } + + /// + /// Removes from the staging area all the modifications of a file since the latest commit (addition, updation or removal). + /// + /// The being worked with. + /// The path of the file within the working directory. + public static void Unstage(this IRepository repository, string path) + { + repository.Unstage(path, null); + } + + /// + /// Removes from the staging area all the modifications of a collection of file since the latest commit (addition, updation or removal). + /// + /// The being worked with. + /// The collection of paths of the files within the working directory. + public static void Unstage(this IRepository repository, IEnumerable paths) + { + repository.Unstage(paths, null); + } + + /// + /// Removes a file from the staging area, and optionally removes it from the working directory as well. + /// + /// If the file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the file from the working directory as well. + /// + /// + /// The being worked with. + /// The path of the file within the working directory. + public static void Remove(this IRepository repository, string path) + { + repository.Remove(path, true, null); + } + + /// + /// Removes a file from the staging area, and optionally removes it from the working directory as well. + /// + /// If the file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the file from the working directory as well. + /// + /// + /// The being worked with. + /// The path of the file within the working directory. + /// True to remove the file from the working directory, False otherwise. + public static void Remove(this IRepository repository, string path, bool removeFromWorkingDirectory) + { + repository.Remove(path, removeFromWorkingDirectory, null); + } + + /// + /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. + /// + /// If a file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the files from the working directory as well. + /// + /// + /// The being worked with. + /// The collection of paths of the files within the working directory. + public static void Remove(this IRepository repository, IEnumerable paths) + { + repository.Remove(paths, true, null); + } + + /// + /// Removes a collection of fileS from the staging, and optionally removes them from the working directory as well. + /// + /// If a file has already been deleted from the working directory, this method will only deal + /// with promoting the removal to the staging area. + /// + /// + /// The default behavior is to remove the files from the working directory as well. + /// + /// + /// The being worked with. + /// The collection of paths of the files within the working directory. + /// True to remove the files from the working directory, False otherwise. + public static void Remove(this IRepository repository, IEnumerable paths, bool removeFromWorkingDirectory) + { + repository.Remove(paths, removeFromWorkingDirectory, null); + } + + /// + /// Retrieves the state of all files in the working directory, comparing them against the staging area and the latest commmit. + /// + /// A holding the state of all the files. + /// The being worked with. + public static RepositoryStatus RetrieveStatus(this IRepository repository) + { + Proxy.git_index_read(repository.Index.Handle); + return new RepositoryStatus((Repository)repository, null); + } } } diff --git a/LibGit2Sharp/RevertOptions.cs b/LibGit2Sharp/RevertOptions.cs index 2e48f04ed..8b1a103bc 100644 --- a/LibGit2Sharp/RevertOptions.cs +++ b/LibGit2Sharp/RevertOptions.cs @@ -43,7 +43,17 @@ public RevertOptions() public CheckoutNotifyHandler OnCheckoutNotify { get; set; } /// - /// Commit the revert if the revert is successful. + /// Commit changes if there are no conflicts and the revert results + /// in changes. + /// + /// Following command line behavior, if the revert results in no + /// changes, then Revert will cleanup the repository state if + /// is true (i.e. the repository + /// will not be left in a "revert in progress" state). + /// If is false and there are no + /// changes to revert, then the repository will be left in + /// the "revert in progress" state. + /// /// public bool CommitOnSuccess { get; set; } diff --git a/LibGit2Sharp/RevertResult.cs b/LibGit2Sharp/RevertResult.cs index bb8700c79..813903a76 100644 --- a/LibGit2Sharp/RevertResult.cs +++ b/LibGit2Sharp/RevertResult.cs @@ -52,6 +52,11 @@ public enum RevertStatus /// /// The revert resulted in conflicts. /// - Conflicts + Conflicts, + + /// + /// Revert was run, but there were no changes to commit. + /// + NothingToRevert, } } diff --git a/LibGit2Sharp/Signature.cs b/LibGit2Sharp/Signature.cs index 74be26448..7dbb437d7 100644 --- a/LibGit2Sharp/Signature.cs +++ b/LibGit2Sharp/Signature.cs @@ -36,7 +36,7 @@ internal Signature(IntPtr signaturePtr) public Signature(string name, string email, DateTimeOffset when) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); - Ensure.ArgumentNotNull(email, "email"); + Ensure.ArgumentNotNullOrEmptyString(email, "email"); Ensure.ArgumentDoesNotContainZeroByte(name, "name"); Ensure.ArgumentDoesNotContainZeroByte(email, "email"); diff --git a/LibGit2Sharp/SmartSubtransport.cs b/LibGit2Sharp/SmartSubtransport.cs new file mode 100644 index 000000000..2f1f5f623 --- /dev/null +++ b/LibGit2Sharp/SmartSubtransport.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// An enumeration of the type of connections which a "smart" subtransport + /// may be asked to create on behalf of libgit2. + /// + public enum GitSmartSubtransportAction + { + /// + /// For HTTP, this indicates a GET to /info/refs?service=git-upload-pack + /// + UploadPackList = 1, + + /// + /// For HTTP, this indicates a POST to /git-upload-pack + /// + UploadPack = 2, + + /// + /// For HTTP, this indicates a GET to /info/refs?service=git-receive-pack + /// + ReceivePackList = 3, + + /// + /// For HTTP, this indicates a POST to /git-receive-pack + /// + ReceivePack = 4 + } + + /// + /// Base class for custom RPC-based subtransports that use the standard + /// "smart" git protocol. RPC-based subtransports perform work over + /// multiple requests, like the http transport. + /// + public abstract class RpcSmartSubtransport : SmartSubtransport + { + } + + /// + /// Base class for typical custom subtransports for the "smart" + /// transport that work over a single connection, like the git and ssh + /// transports. + /// + public abstract class SmartSubtransport + { + /// + /// Invoked by libgit2 to create a connection using this subtransport. + /// + /// The endpoint to connect to + /// The type of connection to create + /// A SmartSubtransportStream representing the connection + protected abstract SmartSubtransportStream Action(String url, GitSmartSubtransportAction action); + + /// + /// Invoked by libgit2 when this subtransport is no longer needed, but may be re-used in the future. + /// Override this method to add additional cleanup steps to your subclass. Be sure to call base.Close(). + /// + protected virtual void Close() + { + } + + /// + /// Invoked by libgit2 when this subtransport is being freed. Override this method to add additional + /// cleanup steps to your subclass. Be sure to call base.Dispose(). + /// + protected virtual void Dispose() + { + Close(); + + if (IntPtr.Zero != nativeSubtransportPointer) + { + GCHandle.FromIntPtr(Marshal.ReadIntPtr(nativeSubtransportPointer, GitSmartSubtransport.GCHandleOffset)).Free(); + Marshal.FreeHGlobal(nativeSubtransportPointer); + nativeSubtransportPointer = IntPtr.Zero; + } + } + + private IntPtr nativeSubtransportPointer; + + internal IntPtr GitSmartSubtransportPointer + { + get + { + if (IntPtr.Zero == nativeSubtransportPointer) + { + var nativeTransport = new GitSmartSubtransport(); + + nativeTransport.Action = EntryPoints.ActionCallback; + nativeTransport.Close = EntryPoints.CloseCallback; + nativeTransport.Free = EntryPoints.FreeCallback; + + nativeTransport.GCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(this)); + nativeSubtransportPointer = Marshal.AllocHGlobal(Marshal.SizeOf(nativeTransport)); + Marshal.StructureToPtr(nativeTransport, nativeSubtransportPointer, false); + } + + return nativeSubtransportPointer; + } + } + + private static class EntryPoints + { + // Because our GitSmartSubtransport structure exists on the managed heap only for a short time (to be marshaled + // to native memory with StructureToPtr), we need to bind to static delegates. If at construction time + // we were to bind to the methods directly, that's the same as newing up a fresh delegate every time. + // Those delegates won't be rooted in the object graph and can be collected as soon as StructureToPtr finishes. + public static GitSmartSubtransport.action_callback ActionCallback = new GitSmartSubtransport.action_callback(Action); + public static GitSmartSubtransport.close_callback CloseCallback = new GitSmartSubtransport.close_callback(Close); + public static GitSmartSubtransport.free_callback FreeCallback = new GitSmartSubtransport.free_callback(Free); + + private static int Action( + out IntPtr stream, + IntPtr subtransport, + IntPtr url, + GitSmartSubtransportAction action) + { + stream = IntPtr.Zero; + + SmartSubtransport t = GCHandle.FromIntPtr(Marshal.ReadIntPtr(subtransport, GitSmartSubtransport.GCHandleOffset)).Target as SmartSubtransport; + String urlAsString = LaxUtf8Marshaler.FromNative(url); + + if (null != t && + !String.IsNullOrEmpty(urlAsString)) + { + try + { + stream = t.Action(urlAsString, action).GitSmartTransportStreamPointer; + + return 0; + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + } + + return (int)GitErrorCode.Error; + } + + private static int Close(IntPtr subtransport) + { + SmartSubtransport t = GCHandle.FromIntPtr(Marshal.ReadIntPtr(subtransport, GitSmartSubtransport.GCHandleOffset)).Target as SmartSubtransport; + + if (null != t) + { + try + { + t.Close(); + + return 0; + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + } + + return (int)GitErrorCode.Error; + } + + private static void Free(IntPtr subtransport) + { + SmartSubtransport t = GCHandle.FromIntPtr(Marshal.ReadIntPtr(subtransport, GitSmartSubtransport.GCHandleOffset)).Target as SmartSubtransport; + + if (null != t) + { + try + { + t.Dispose(); + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + } + } + } + } +} diff --git a/LibGit2Sharp/SmartSubtransportRegistration.cs b/LibGit2Sharp/SmartSubtransportRegistration.cs new file mode 100644 index 000000000..d7d7a0adf --- /dev/null +++ b/LibGit2Sharp/SmartSubtransportRegistration.cs @@ -0,0 +1,122 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// An object representing the registration of a SmartSubtransport type with libgit2 + /// under a particular scheme (eg "http"). + /// + /// The type of SmartSubtransport to register + public sealed class SmartSubtransportRegistration + where T : SmartSubtransport, new() + { + /// + /// Creates a new native registration for a smart protocol transport + /// in libgit2. + /// + /// The URL scheme (eg "http") to register + internal SmartSubtransportRegistration(string scheme) + { + Scheme = scheme; + RegistrationPointer = CreateRegistrationPointer(); + FunctionPointer = CreateFunctionPointer(); + } + + /// + /// The URI scheme (eg "http") for this transport. + /// + public string Scheme + { + get; + private set; + } + + internal IntPtr RegistrationPointer + { + get; + private set; + } + + internal IntPtr FunctionPointer + { + get; + private set; + } + + private IntPtr CreateRegistrationPointer() + { + var registration = new GitSmartSubtransportRegistration(); + + registration.SubtransportCallback = Marshal.GetFunctionPointerForDelegate(EntryPoints.SubtransportCallback); + registration.Rpc = typeof(RpcSmartSubtransport).IsAssignableFrom(typeof(T)) ? (uint)1 : (uint)0; + + var registrationPointer = Marshal.AllocHGlobal(Marshal.SizeOf(registration)); + Marshal.StructureToPtr(registration, registrationPointer, false); + + return registrationPointer; + } + + private IntPtr CreateFunctionPointer() + { + return Marshal.GetFunctionPointerForDelegate(EntryPoints.TransportCallback); + } + + internal void Free() + { + Marshal.FreeHGlobal(RegistrationPointer); + RegistrationPointer = IntPtr.Zero; + } + + private static class EntryPoints + { + // Because our GitSmartSubtransportRegistration structure exists on the managed heap only for a short time (to be marshaled + // to native memory with StructureToPtr), we need to bind to static delegates. If at construction time + // we were to bind to the methods directly, that's the same as newing up a fresh delegate every time. + // Those delegates won't be rooted in the object graph and can be collected as soon as StructureToPtr finishes. + public static GitSmartSubtransportRegistration.create_callback SubtransportCallback = new GitSmartSubtransportRegistration.create_callback(Subtransport); + public static NativeMethods.git_transport_cb TransportCallback = new NativeMethods.git_transport_cb(Transport); + + private static int Subtransport( + out IntPtr subtransport, + IntPtr transport) + { + subtransport = IntPtr.Zero; + + try + { + subtransport = new T().GitSmartSubtransportPointer; + + return 0; + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + + return (int)GitErrorCode.Error; + } + + private static int Transport( + out IntPtr transport, + IntPtr remote, + IntPtr payload) + { + transport = IntPtr.Zero; + + try + { + return NativeMethods.git_transport_smart(out transport, remote, payload); + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + + return (int)GitErrorCode.Error; + } + } + } +} diff --git a/LibGit2Sharp/SmartSubtransportStream.cs b/LibGit2Sharp/SmartSubtransportStream.cs new file mode 100644 index 000000000..7048370ea --- /dev/null +++ b/LibGit2Sharp/SmartSubtransportStream.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// A stream that represents a two-way connection (socket) for a SmartSubtransport. + /// + public abstract class SmartSubtransportStream + { + /// + /// This is to quiet the MetaFixture.TypesInLibGit2SharpMustBeExtensibleInATestingContext test. + /// Do not use this constructor. + /// + protected internal SmartSubtransportStream() + { + throw new InvalidOperationException(); + } + + /// + /// Base constructor for SmartTransportStream. Make sure that your derived class calls this base constructor. + /// + /// The subtransport that this stream represents a connection over. + protected SmartSubtransportStream(SmartSubtransport subtransport) + { + this.subtransport = subtransport; + } + + /// + /// Invoked by libgit2 when this stream is no longer needed. + /// Override this method to add additional cleanup steps to your subclass. Be sure + /// to call base.Free(). + /// + protected virtual void Free() + { + if (IntPtr.Zero != nativeStreamPointer) + { + GCHandle.FromIntPtr(Marshal.ReadIntPtr(nativeStreamPointer, GitSmartSubtransportStream.GCHandleOffset)).Free(); + Marshal.FreeHGlobal(nativeStreamPointer); + nativeStreamPointer = IntPtr.Zero; + } + } + + /// + /// Requests that the stream write the next length bytes of the stream to the provided Stream object. + /// + public abstract int Read( + Stream dataStream, + long length, + out long bytesRead); + + /// + /// Requests that the stream write the first length bytes of the provided Stream object to the stream. + /// + public abstract int Write( + Stream dataStream, + long length); + + /// + /// The smart transport that this stream represents a connection over. + /// + public virtual SmartSubtransport SmartTransport + { + get + { + return this.subtransport; + } + } + + private SmartSubtransport subtransport; + private IntPtr nativeStreamPointer; + + internal IntPtr GitSmartTransportStreamPointer + { + get + { + if (IntPtr.Zero == nativeStreamPointer) + { + var nativeTransportStream = new GitSmartSubtransportStream(); + + nativeTransportStream.SmartTransport = this.subtransport.GitSmartSubtransportPointer; + nativeTransportStream.Read = EntryPoints.ReadCallback; + nativeTransportStream.Write = EntryPoints.WriteCallback; + nativeTransportStream.Free = EntryPoints.FreeCallback; + + nativeTransportStream.GCHandle = GCHandle.ToIntPtr(GCHandle.Alloc(this)); + nativeStreamPointer = Marshal.AllocHGlobal(Marshal.SizeOf(nativeTransportStream)); + Marshal.StructureToPtr(nativeTransportStream, nativeStreamPointer, false); + } + + return nativeStreamPointer; + } + } + + private static class EntryPoints + { + // Because our GitSmartSubtransportStream structure exists on the managed heap only for a short time (to be marshaled + // to native memory with StructureToPtr), we need to bind to static delegates. If at construction time + // we were to bind to the methods directly, that's the same as newing up a fresh delegate every time. + // Those delegates won't be rooted in the object graph and can be collected as soon as StructureToPtr finishes. + public static GitSmartSubtransportStream.read_callback ReadCallback = new GitSmartSubtransportStream.read_callback(Read); + public static GitSmartSubtransportStream.write_callback WriteCallback = new GitSmartSubtransportStream.write_callback(Write); + public static GitSmartSubtransportStream.free_callback FreeCallback = new GitSmartSubtransportStream.free_callback(Free); + + private unsafe static int Read( + IntPtr stream, + IntPtr buffer, + UIntPtr buf_size, + out UIntPtr bytes_read) + { + bytes_read = UIntPtr.Zero; + + SmartSubtransportStream transportStream = GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream; + + if (transportStream != null && + buf_size.ToUInt64() < (ulong)long.MaxValue) + { + using (UnmanagedMemoryStream memoryStream = new UnmanagedMemoryStream((byte*)buffer, 0, (long)buf_size.ToUInt64(), FileAccess.ReadWrite)) + { + try + { + long longBytesRead; + + int toReturn = transportStream.Read(memoryStream, (long)buf_size.ToUInt64(), out longBytesRead); + + bytes_read = new UIntPtr((ulong)Math.Max(0, longBytesRead)); + + return toReturn; + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + } + } + + return (int)GitErrorCode.Error; + } + + private static unsafe int Write( + IntPtr stream, + IntPtr buffer, + UIntPtr len) + { + SmartSubtransportStream transportStream = GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream; + + if (transportStream != null && + len.ToUInt64() < (ulong)long.MaxValue) + { + long length = (long)len.ToUInt64(); + + using (UnmanagedMemoryStream dataStream = new UnmanagedMemoryStream((byte*)buffer, length)) + { + try + { + return transportStream.Write(dataStream, length); + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + } + } + + return (int)GitErrorCode.Error; + } + + private static void Free( + IntPtr stream) + { + SmartSubtransportStream transportStream = GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream; + + if (transportStream != null) + { + try + { + transportStream.Free(); + } + catch (Exception ex) + { + Proxy.giterr_set_str(GitErrorCategory.Net, ex); + } + } + } + } + } +} diff --git a/LibGit2Sharp/Submodule.cs b/LibGit2Sharp/Submodule.cs index 3d0b35629..c15e224d0 100644 --- a/LibGit2Sharp/Submodule.cs +++ b/LibGit2Sharp/Submodule.cs @@ -9,7 +9,7 @@ namespace LibGit2Sharp /// A Submodule. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] - public class Submodule : IEquatable + public class Submodule : IEquatable, IBelongToARepository { private static readonly LambdaEqualityHelper equalityHelper = new LambdaEqualityHelper(x => x.Name, x => x.HeadCommitId); @@ -155,5 +155,7 @@ private string DebuggerDisplay "{0} => {1}", Name, Url); } } + + IRepository IBelongToARepository.Repository { get { return repo; } } } } diff --git a/LibGit2Sharp/SymbolicReference.cs b/LibGit2Sharp/SymbolicReference.cs index 2b2ae05fa..5bb475312 100644 --- a/LibGit2Sharp/SymbolicReference.cs +++ b/LibGit2Sharp/SymbolicReference.cs @@ -17,8 +17,8 @@ public class SymbolicReference : Reference protected SymbolicReference() { } - internal SymbolicReference(string canonicalName, string targetIdentifier, Reference target) - : base(canonicalName, targetIdentifier) + internal SymbolicReference(IRepository repo, string canonicalName, string targetIdentifier, Reference target) + : base(repo, canonicalName, targetIdentifier) { this.target = target; } diff --git a/LibGit2Sharp/Version.cs b/LibGit2Sharp/Version.cs new file mode 100644 index 000000000..f519c3a17 --- /dev/null +++ b/LibGit2Sharp/Version.cs @@ -0,0 +1,109 @@ +using System.Globalization; +using System.IO; +using System.Reflection; +using LibGit2Sharp.Core; + +namespace LibGit2Sharp +{ + /// + /// Gets the current LibGit2Sharp version. + /// + public class Version + { + private readonly Assembly assembly = typeof(Repository).Assembly; + + /// + /// Needed for mocking purposes. + /// + protected Version() + { } + + internal static Version Build() + { + return new Version(); + } + + /// + /// Returns the of the + /// the LibGit2Sharp library. + /// + public virtual System.Version MajorMinorPatch + { + get + { + return assembly.GetName().Version; + } + } + + /// + /// Returns all the optional features that were compiled into + /// libgit2. + /// + /// A enumeration. + public virtual BuiltInFeatures Features + { + get + { + return Proxy.git_libgit2_features(); + } + } + + /// + /// Returns the SHA hash for the libgit2 library. + /// + public virtual string LibGit2CommitSha + { + get + { + return ReadContentFromResource(assembly, "libgit2_hash.txt").Substring(0, 7); + } + } + + /// + /// Returns the SHA hash for the LibGit2Sharp library. + /// + public virtual string LibGit2SharpCommitSha + { + get + { + return ReadContentFromResource(assembly, "libgit2sharp_hash.txt").Substring(0, 7); + } + } + + /// + /// Returns a string representing the LibGit2Sharp version. + /// + /// + /// The format of the version number is as follows: + /// Major.Minor.Patch-LibGit2Sharp_abbrev_hash-libgit2_abbrev_hash (x86|amd64 - features) + /// + /// + public override string ToString() + { + return RetrieveVersion(); + } + + private string RetrieveVersion() + { + string features = Features.ToString(); + + return string.Format( + CultureInfo.InvariantCulture, + "{0}-{1}-{2} ({3} - {4})", + MajorMinorPatch.ToString(3), + LibGit2SharpCommitSha, + LibGit2CommitSha, + NativeMethods.ProcessorArchitecture, + features); + } + + private string ReadContentFromResource(Assembly assembly, string partialResourceName) + { + string name = string.Format(CultureInfo.InvariantCulture, "LibGit2Sharp.{0}", partialResourceName); + using (var sr = new StreamReader(assembly.GetManifestResourceStream(name))) + { + return sr.ReadLine(); + } + } + } +} diff --git a/LibGit2Sharp/VoidReference.cs b/LibGit2Sharp/VoidReference.cs index b8defc8c0..cc20d1e39 100644 --- a/LibGit2Sharp/VoidReference.cs +++ b/LibGit2Sharp/VoidReference.cs @@ -2,8 +2,8 @@ { internal class VoidReference : Reference { - internal VoidReference(string canonicalName) - : base(canonicalName, null) + internal VoidReference(IRepository repo, string canonicalName) + : base(repo, canonicalName, null) { } public override DirectReference ResolveToDirectReference() diff --git a/LibGit2Sharp/libgit2_hash.txt b/LibGit2Sharp/libgit2_hash.txt index a34df7394..d6a1f3602 100644 --- a/LibGit2Sharp/libgit2_hash.txt +++ b/LibGit2Sharp/libgit2_hash.txt @@ -1 +1 @@ -69db89342712f47ee84d9368823ec294a0db1c65 +3f8d005a82b39c504220d65b6a6aa696c3b1a9c4 diff --git a/README.md b/README.md index a288b2829..18379c00c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,14 @@ [0]: http://libgit2.github.com/ +## Prerequisites + + - **Windows:** .Net 4.0+ + - **Linux/MacOsX:** Mono 3.6+ + ## Online resources - - [NuGet package][1] + - [NuGet package][1] (Requires NuGet 2.7+) - [Source code][2] - [Continuous integration][3] diff --git a/ResharperSettings.xml b/ResharperSettings.xml deleted file mode 100644 index 3afd3d805..000000000 --- a/ResharperSettings.xml +++ /dev/null @@ -1,319 +0,0 @@ - - - - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - 1 - 1 - True - - public - protected - internal - private - new - abstract - virtual - override - sealed - static - readonly - extern - unsafe - volatile - - False - False - False - True - False - - - - $object$_On$event$ - $event$Handler - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]> - - - - - - $object$_On$event$ - $event$Handler - - - - - - - - - - - - - - - - - - - - - - - $object$_On$event$ - $event$Handler - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/UpdateLibgit2ToSha.ps1 b/UpdateLibgit2ToSha.ps1 index e86e2f74d..ee70de107 100644 --- a/UpdateLibgit2ToSha.ps1 +++ b/UpdateLibgit2ToSha.ps1 @@ -144,7 +144,7 @@ function Assert-Consistent-Naming($expected, $path) { Run-Command -Quiet { & remove-item build -recurse -force } Run-Command -Quiet { & mkdir build } cd build - Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs" -D THREADSAFE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON .. } + Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs" -D ENABLE_TRACE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON .. } Run-Command -Quiet -Fatal { & $cmake --build . --config $configuration } if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } } cd $configuration @@ -157,7 +157,7 @@ function Assert-Consistent-Naming($expected, $path) { cd .. Run-Command -Quiet { & mkdir build64 } cd build64 - Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs Win64" -D THREADSAFE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON ../.. } + Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs Win64" -D THREADSAFE=ON -D ENABLE_TRACE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON ../.. } Run-Command -Quiet -Fatal { & $cmake --build . --config $configuration } if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } } cd $configuration @@ -181,6 +181,32 @@ namespace LibGit2Sharp.Core sc -Encoding ASCII (Join-Path $libgit2sharpDirectory "Libgit2sharp\Core\NativeDllName.cs") $dllNameClass sc -Encoding ASCII (Join-Path $libgit2sharpDirectory "Libgit2sharp\libgit2_hash.txt") $sha + $buildProperties = @" + + + + + NativeBinaries\amd64\$binaryFilename.dll + PreserveNewest + + + NativeBinaries\amd64\$binaryFilename.pdb + PreserveNewest + + + NativeBinaries\x86\$binaryFilename.dll + PreserveNewest + + + NativeBinaries\x86\$binaryFilename.pdb + PreserveNewest + + + +"@ + + sc -Encoding UTF8 (Join-Path $libgit2sharpDirectory "nuget.package\build\LibGit2Sharp.props") $buildProperties + Write-Output "Done!" } exit diff --git a/build.libgit2sharp.sh b/build.libgit2sharp.sh index 1a97e214a..a64434fb2 100755 --- a/build.libgit2sharp.sh +++ b/build.libgit2sharp.sh @@ -9,9 +9,9 @@ pushd libgit2/build export _BINPATH=`pwd` cmake -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \ - -DTHREADSAFE:BOOL=ON \ -DBUILD_CLAR:BOOL=OFF \ -DUSE_SSH=OFF \ + -DENABLE_TRACE=ON \ -DLIBGIT2_FILENAME=git2-$SHORTSHA \ -DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \ .. diff --git a/libgit2 b/libgit2 index 69db89342..3f8d005a8 160000 --- a/libgit2 +++ b/libgit2 @@ -1 +1 @@ -Subproject commit 69db89342712f47ee84d9368823ec294a0db1c65 +Subproject commit 3f8d005a82b39c504220d65b6a6aa696c3b1a9c4 diff --git a/nuget.package/LibGit2Sharp.nuspec b/nuget.package/LibGit2Sharp.nuspec index 99c326536..f605336c7 100644 --- a/nuget.package/LibGit2Sharp.nuspec +++ b/nuget.package/LibGit2Sharp.nuspec @@ -1,6 +1,6 @@ - - + + $id$ $version$ $author$ @@ -22,7 +22,7 @@ - + diff --git a/nuget.package/Tools/GetLibGit2SharpPostBuildCmd.ps1 b/nuget.package/Tools/GetLibGit2SharpPostBuildCmd.ps1 deleted file mode 100644 index e1847e610..000000000 --- a/nuget.package/Tools/GetLibGit2SharpPostBuildCmd.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -$solutionDir = [System.IO.Path]::GetDirectoryName($dte.Solution.FullName) + "\" -$path = $installPath.Replace($solutionDir, "`$(SolutionDir)") - -$NativeAssembliesDir = Join-Path $path "lib\net40\NativeBinaries" -$x86 = $(Join-Path $NativeAssembliesDir "x86\*.*") -$x64 = $(Join-Path $NativeAssembliesDir "amd64\*.*") - -$LibGit2SharpPostBuildCmd = " -if not exist `"`$(TargetDir)NativeBinaries`" md `"`$(TargetDir)NativeBinaries`" -if not exist `"`$(TargetDir)NativeBinaries\x86`" md `"`$(TargetDir)NativeBinaries\x86`" -xcopy /s /y /d `"$x86`" `"`$(TargetDir)NativeBinaries\x86`" -if not exist `"`$(TargetDir)NativeBinaries\amd64`" md `"`$(TargetDir)NativeBinaries\amd64`" -xcopy /s /y /d `"$x64`" `"`$(TargetDir)NativeBinaries\amd64`"" diff --git a/nuget.package/Tools/install.ps1 b/nuget.package/Tools/install.ps1 deleted file mode 100644 index bc403c27b..000000000 --- a/nuget.package/Tools/install.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -. (Join-Path $toolsPath "GetLibGit2SharpPostBuildCmd.ps1") - -# Get the current Post Build Event cmd -$currentPostBuildCmd = $project.Properties.Item("PostBuildEvent").Value - -# Append our post build command if it's not already there -if (!$currentPostBuildCmd.Contains($LibGit2SharpPostBuildCmd)) { - $project.Properties.Item("PostBuildEvent").Value += $LibGit2SharpPostBuildCmd -} \ No newline at end of file diff --git a/nuget.package/Tools/uninstall.ps1 b/nuget.package/Tools/uninstall.ps1 deleted file mode 100644 index a1854cb32..000000000 --- a/nuget.package/Tools/uninstall.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -. (Join-Path $toolsPath "GetLibGit2SharpPostBuildCmd.ps1") - -# Get the current Post Build Event cmd -$currentPostBuildCmd = $project.Properties.Item("PostBuildEvent").Value - -# Remove our post build command from it (if it's there) -$project.Properties.Item("PostBuildEvent").Value = $currentPostBuildCmd.Replace($LibGit2SharpPostBuildCmd, "") \ No newline at end of file diff --git a/nuget.package/build/LibGit2Sharp.props b/nuget.package/build/LibGit2Sharp.props new file mode 100644 index 000000000..f3f5effd2 --- /dev/null +++ b/nuget.package/build/LibGit2Sharp.props @@ -0,0 +1,21 @@ + + + + + NativeBinaries\amd64\git2-3f8d005.dll + PreserveNewest + + + NativeBinaries\amd64\git2-3f8d005.pdb + PreserveNewest + + + NativeBinaries\x86\git2-3f8d005.dll + PreserveNewest + + + NativeBinaries\x86\git2-3f8d005.pdb + PreserveNewest + + +