diff --git a/LibGit2Sharp.Tests/FileHistoryFixture.cs b/LibGit2Sharp.Tests/FileHistoryFixture.cs
index 5380e66de..e6465d1ac 100644
--- a/LibGit2Sharp.Tests/FileHistoryFixture.cs
+++ b/LibGit2Sharp.Tests/FileHistoryFixture.cs
@@ -10,54 +10,56 @@ namespace LibGit2Sharp.Tests
 {
     public class FileHistoryFixture : BaseFixture
     {
-        [Theory]
-        [InlineData("https://github.com/nulltoken/follow-test.git")]
-        public void CanDealWithFollowTest(string url)
-        {
-            var scd = BuildSelfCleaningDirectory();
-            var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath);
-
-            using (var repo = new Repository(clonedRepoPath))
-            {
-                // $ git log --follow --format=oneline so-renamed.txt
-                // 88f91835062161febb46fb270ef4188f54c09767 Update not-yet-renamed.txt AND rename into so-renamed.txt
-                // ef7cb6a63e32595fffb092cb1ae9a32310e58850 Add not-yet-renamed.txt
-                var fileHistoryEntries = repo.Commits.QueryBy("so-renamed.txt").ToList();
-                Assert.Equal(2, fileHistoryEntries.Count());
-                Assert.Equal("88f91835062161febb46fb270ef4188f54c09767", fileHistoryEntries[0].Commit.Sha);
-                Assert.Equal("ef7cb6a63e32595fffb092cb1ae9a32310e58850", fileHistoryEntries[1].Commit.Sha);
-
-                // $ git log --follow --format=oneline untouched.txt
-                // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit
-                fileHistoryEntries = repo.Commits.QueryBy("untouched.txt").ToList();
-                Assert.Single(fileHistoryEntries);
-                Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[0].Commit.Sha);
-
-                // $ git log --follow --format=oneline under-test.txt
-                // 0b5b18f2feb917dee98df1210315b2b2b23c5bec Rename file renamed.txt into under-test.txt
-                // 49921d463420a892c9547a326632ef6a9ba3b225 Update file renamed.txt
-                // 70f636e8c64bbc2dfef3735a562bb7e195d8019f Rename file under-test.txt into renamed.txt
-                // d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d Updated file under test
-                // 9da10ef7e139c49604a12caa866aae141f38b861 Updated file under test
-                // 599a5d821fb2c0a25855b4233e26d475c2fbeb34 Updated file under test
-                // 678b086b44753000567aa64344aa0d8034fa0083 Updated file under test
-                // 8f7d9520f306771340a7c79faea019ad18e4fa1f Updated file under test
-                // bd5f8ee279924d33be8ccbde82e7f10b9d9ff237 Updated file under test
-                // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit
-                fileHistoryEntries = repo.Commits.QueryBy("under-test.txt").ToList();
-                Assert.Equal(10, fileHistoryEntries.Count());
-                Assert.Equal("0b5b18f2feb917dee98df1210315b2b2b23c5bec", fileHistoryEntries[0].Commit.Sha);
-                Assert.Equal("49921d463420a892c9547a326632ef6a9ba3b225", fileHistoryEntries[1].Commit.Sha);
-                Assert.Equal("70f636e8c64bbc2dfef3735a562bb7e195d8019f", fileHistoryEntries[2].Commit.Sha);
-                Assert.Equal("d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d", fileHistoryEntries[3].Commit.Sha);
-                Assert.Equal("9da10ef7e139c49604a12caa866aae141f38b861", fileHistoryEntries[4].Commit.Sha);
-                Assert.Equal("599a5d821fb2c0a25855b4233e26d475c2fbeb34", fileHistoryEntries[5].Commit.Sha);
-                Assert.Equal("678b086b44753000567aa64344aa0d8034fa0083", fileHistoryEntries[6].Commit.Sha);
-                Assert.Equal("8f7d9520f306771340a7c79faea019ad18e4fa1f", fileHistoryEntries[7].Commit.Sha);
-                Assert.Equal("bd5f8ee279924d33be8ccbde82e7f10b9d9ff237", fileHistoryEntries[8].Commit.Sha);
-                Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[9].Commit.Sha);
-            }
-        }
+        //Looks like nulltoken deleted the repo this test was using
+
+        //[Theory]
+        //[InlineData("https://github.com/nulltoken/follow-test.git")]
+        //public void CanDealWithFollowTest(string url)
+        //{
+        //    var scd = BuildSelfCleaningDirectory();
+        //    var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath);
+
+        //    using (var repo = new Repository(clonedRepoPath))
+        //    {
+        //        // $ git log --follow --format=oneline so-renamed.txt
+        //        // 88f91835062161febb46fb270ef4188f54c09767 Update not-yet-renamed.txt AND rename into so-renamed.txt
+        //        // ef7cb6a63e32595fffb092cb1ae9a32310e58850 Add not-yet-renamed.txt
+        //        var fileHistoryEntries = repo.Commits.QueryBy("so-renamed.txt").ToList();
+        //        Assert.Equal(2, fileHistoryEntries.Count());
+        //        Assert.Equal("88f91835062161febb46fb270ef4188f54c09767", fileHistoryEntries[0].Commit.Sha);
+        //        Assert.Equal("ef7cb6a63e32595fffb092cb1ae9a32310e58850", fileHistoryEntries[1].Commit.Sha);
+
+        //        // $ git log --follow --format=oneline untouched.txt
+        //        // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit
+        //        fileHistoryEntries = repo.Commits.QueryBy("untouched.txt").ToList();
+        //        Assert.Single(fileHistoryEntries);
+        //        Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[0].Commit.Sha);
+
+        //        // $ git log --follow --format=oneline under-test.txt
+        //        // 0b5b18f2feb917dee98df1210315b2b2b23c5bec Rename file renamed.txt into under-test.txt
+        //        // 49921d463420a892c9547a326632ef6a9ba3b225 Update file renamed.txt
+        //        // 70f636e8c64bbc2dfef3735a562bb7e195d8019f Rename file under-test.txt into renamed.txt
+        //        // d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d Updated file under test
+        //        // 9da10ef7e139c49604a12caa866aae141f38b861 Updated file under test
+        //        // 599a5d821fb2c0a25855b4233e26d475c2fbeb34 Updated file under test
+        //        // 678b086b44753000567aa64344aa0d8034fa0083 Updated file under test
+        //        // 8f7d9520f306771340a7c79faea019ad18e4fa1f Updated file under test
+        //        // bd5f8ee279924d33be8ccbde82e7f10b9d9ff237 Updated file under test
+        //        // c10c1d5f74b76f20386d18674bf63fbee6995061 Initial commit
+        //        fileHistoryEntries = repo.Commits.QueryBy("under-test.txt").ToList();
+        //        Assert.Equal(10, fileHistoryEntries.Count());
+        //        Assert.Equal("0b5b18f2feb917dee98df1210315b2b2b23c5bec", fileHistoryEntries[0].Commit.Sha);
+        //        Assert.Equal("49921d463420a892c9547a326632ef6a9ba3b225", fileHistoryEntries[1].Commit.Sha);
+        //        Assert.Equal("70f636e8c64bbc2dfef3735a562bb7e195d8019f", fileHistoryEntries[2].Commit.Sha);
+        //        Assert.Equal("d3868d57a6aaf2ae6ed4887d805ae4bc91d8ce4d", fileHistoryEntries[3].Commit.Sha);
+        //        Assert.Equal("9da10ef7e139c49604a12caa866aae141f38b861", fileHistoryEntries[4].Commit.Sha);
+        //        Assert.Equal("599a5d821fb2c0a25855b4233e26d475c2fbeb34", fileHistoryEntries[5].Commit.Sha);
+        //        Assert.Equal("678b086b44753000567aa64344aa0d8034fa0083", fileHistoryEntries[6].Commit.Sha);
+        //        Assert.Equal("8f7d9520f306771340a7c79faea019ad18e4fa1f", fileHistoryEntries[7].Commit.Sha);
+        //        Assert.Equal("bd5f8ee279924d33be8ccbde82e7f10b9d9ff237", fileHistoryEntries[8].Commit.Sha);
+        //        Assert.Equal("c10c1d5f74b76f20386d18674bf63fbee6995061", fileHistoryEntries[9].Commit.Sha);
+        //    }
+        //}
 
         [Theory]
         [InlineData(null)]
diff --git a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs
index 55260a6f5..6026f267a 100644
--- a/LibGit2Sharp.Tests/GlobalSettingsFixture.cs
+++ b/LibGit2Sharp.Tests/GlobalSettingsFixture.cs
@@ -15,7 +15,6 @@ public void CanGetMinimumCompiledInFeatures()
             BuiltInFeatures features = GlobalSettings.Version.Features;
 
             Assert.True(features.HasFlag(BuiltInFeatures.Threads));
-            Assert.True(features.HasFlag(BuiltInFeatures.Https));
         }
 
         [Fact]
diff --git a/LibGit2Sharp/AmbiguousSpecificationException.cs b/LibGit2Sharp/AmbiguousSpecificationException.cs
index 1d19bbfde..16c77f6df 100644
--- a/LibGit2Sharp/AmbiguousSpecificationException.cs
+++ b/LibGit2Sharp/AmbiguousSpecificationException.cs
@@ -1,3 +1,4 @@
+using LibGit2Sharp.Core;
 using System;
 using System.Runtime.Serialization;
 
@@ -7,7 +8,7 @@ namespace LibGit2Sharp
     /// The exception that is thrown when the provided specification cannot uniquely identify a reference, an object or a path.
     /// </summary>
     [Serializable]
-    public class AmbiguousSpecificationException : LibGit2SharpException
+    public class AmbiguousSpecificationException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="AmbiguousSpecificationException"/> class.
@@ -50,5 +51,13 @@ public AmbiguousSpecificationException(string message, Exception innerException)
         protected AmbiguousSpecificationException(SerializationInfo info, StreamingContext context)
             : base(info, context)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.Ambiguous;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/BareRepositoryException.cs b/LibGit2Sharp/BareRepositoryException.cs
index 75ad9695c..7ee830a0c 100644
--- a/LibGit2Sharp/BareRepositoryException.cs
+++ b/LibGit2Sharp/BareRepositoryException.cs
@@ -9,7 +9,7 @@ namespace LibGit2Sharp
     /// working directory is performed against a bare repository.
     /// </summary>
     [Serializable]
-    public class BareRepositoryException : LibGit2SharpException
+    public class BareRepositoryException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="LibGit2Sharp.BareRepositoryException"/> class.
@@ -52,8 +52,16 @@ protected BareRepositoryException(SerializationInfo info, StreamingContext conte
             : base(info, context)
         { }
 
-        internal BareRepositoryException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal BareRepositoryException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.BareRepo;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/CertificateX509.cs b/LibGit2Sharp/CertificateX509.cs
index 8de124b8f..da45eb43e 100644
--- a/LibGit2Sharp/CertificateX509.cs
+++ b/LibGit2Sharp/CertificateX509.cs
@@ -10,7 +10,6 @@ namespace LibGit2Sharp
     /// </summary>
     public class CertificateX509 : Certificate
     {
-
         /// <summary>
         /// For mocking purposes
         /// </summary>
@@ -30,6 +29,11 @@ internal unsafe CertificateX509(git_certificate_x509* cert)
             Certificate = new X509Certificate(data);
         }
 
+        internal CertificateX509(X509Certificate cert)
+        {
+            Certificate = cert;
+        }
+
         internal unsafe IntPtr ToPointers(out IntPtr dataPtr)
         {
             var certData = Certificate.Export(X509ContentType.Cert);
diff --git a/LibGit2Sharp/CheckoutConflictException.cs b/LibGit2Sharp/CheckoutConflictException.cs
index a06360afb..f2f5092e9 100644
--- a/LibGit2Sharp/CheckoutConflictException.cs
+++ b/LibGit2Sharp/CheckoutConflictException.cs
@@ -10,7 +10,7 @@ namespace LibGit2Sharp
     /// in the working directory.
     /// </summary>
     [Serializable]
-    public class CheckoutConflictException : LibGit2SharpException
+    public class CheckoutConflictException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="LibGit2Sharp.CheckoutConflictException"/> class.
@@ -53,8 +53,16 @@ protected CheckoutConflictException(SerializationInfo info, StreamingContext con
             : base(info, context)
         { }
 
-        internal CheckoutConflictException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal CheckoutConflictException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.Conflict;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/Core/Ensure.cs b/LibGit2Sharp/Core/Ensure.cs
index cf327d544..9b00da4eb 100644
--- a/LibGit2Sharp/Core/Ensure.cs
+++ b/LibGit2Sharp/Core/Ensure.cs
@@ -114,21 +114,20 @@ public static void ArgumentIsExpectedIntPtr(IntPtr argumentValue, IntPtr expecte
             }
         }
 
-        private static readonly Dictionary<GitErrorCode, Func<string, GitErrorCode, GitErrorCategory, LibGit2SharpException>>
+        private static readonly Dictionary<GitErrorCode, Func<string, GitErrorCategory, LibGit2SharpException>>
             GitErrorsToLibGit2SharpExceptions =
-                new Dictionary<GitErrorCode, Func<string, GitErrorCode, GitErrorCategory, LibGit2SharpException>>
+                new Dictionary<GitErrorCode, Func<string, GitErrorCategory, LibGit2SharpException>>
                 {
-                    { GitErrorCode.User, (m, r, c) => new UserCancelledException(m, r, c) },
-                    { GitErrorCode.BareRepo, (m, r, c) => new BareRepositoryException(m, r, c) },
-                    { GitErrorCode.Exists, (m, r, c) => new NameConflictException(m, r, c) },
-                    { GitErrorCode.InvalidSpecification, (m, r, c) => new InvalidSpecificationException(m, r, c) },
-                    { GitErrorCode.UnmergedEntries, (m, r, c) => new UnmergedIndexEntriesException(m, r, c) },
-                    { GitErrorCode.NonFastForward, (m, r, c) => new NonFastForwardException(m, r, c) },
-                    { GitErrorCode.Conflict, (m, r, c) => new CheckoutConflictException(m, r, c) },
-                    { GitErrorCode.LockedFile, (m, r, c) => new LockedFileException(m, r, c) },
-                    { GitErrorCode.NotFound, (m, r, c) => new NotFoundException(m, r, c) },
-                    { GitErrorCode.Peel, (m, r, c) => new PeelException(m, r, c) },
-                    { GitErrorCode.Auth, (m, r, c) => new AuthenticationException(m, r, c) },
+                    { GitErrorCode.User, (m, c) => new UserCancelledException(m, c) },
+                    { GitErrorCode.BareRepo, (m, c) => new BareRepositoryException(m, c) },
+                    { GitErrorCode.Exists, (m, c) => new NameConflictException(m, c) },
+                    { GitErrorCode.InvalidSpecification, (m, c) => new InvalidSpecificationException(m, c) },
+                    { GitErrorCode.UnmergedEntries, (m, c) => new UnmergedIndexEntriesException(m, c) },
+                    { GitErrorCode.NonFastForward, (m, c) => new NonFastForwardException(m, c) },
+                    { GitErrorCode.Conflict, (m, c) => new CheckoutConflictException(m, c) },
+                    { GitErrorCode.LockedFile, (m, c) => new LockedFileException(m, c) },
+                    { GitErrorCode.NotFound, (m, c) => new NotFoundException(m, c) },
+                    { GitErrorCode.Peel, (m, c) => new PeelException(m, c) },
                 };
 
         private static unsafe void HandleError(int result)
@@ -146,13 +145,13 @@ private static unsafe void HandleError(int result)
                 errorMessage = LaxUtf8Marshaler.FromNative(error->Message);
             }
 
-            Func<string, GitErrorCode, GitErrorCategory, LibGit2SharpException> exceptionBuilder;
+            Func<string, GitErrorCategory, LibGit2SharpException> exceptionBuilder;
             if (!GitErrorsToLibGit2SharpExceptions.TryGetValue((GitErrorCode)result, out exceptionBuilder))
             {
-                exceptionBuilder = (m, r, c) => new LibGit2SharpException(m, r, c);
+                exceptionBuilder = (m, c) => new LibGit2SharpException(m, c);
             }
 
-            throw exceptionBuilder(errorMessage, (GitErrorCode)result, errorCategory);
+            throw exceptionBuilder(errorMessage, errorCategory);
         }
 
         /// <summary>
@@ -257,7 +256,7 @@ public static void GitObjectIsNotNull(GitObject gitObject, string identifier)
             }
 
             var messageFormat = "No valid git object identified by '{0}' exists in the repository.";
-                                        
+
             if (string.Equals("HEAD", identifier, StringComparison.Ordinal))
             {
                 throw new UnbornBranchException(messageFormat, identifier);
diff --git a/LibGit2Sharp/Core/ManagedHttpSmartSubtransport.cs b/LibGit2Sharp/Core/ManagedHttpSmartSubtransport.cs
new file mode 100644
index 000000000..88eced880
--- /dev/null
+++ b/LibGit2Sharp/Core/ManagedHttpSmartSubtransport.cs
@@ -0,0 +1,270 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Security.Authentication;
+
+namespace LibGit2Sharp.Core
+{
+    internal class ManagedHttpSmartSubtransport : 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 ManagedHttpSmartSubtransportStream(this, endpointUrl, isPost, contentType);
+        }
+
+        private class ManagedHttpSmartSubtransportStream : SmartSubtransportStream
+        {
+            private static int MAX_REDIRECTS = 7;
+
+#if NETCOREAPP
+            private static readonly SocketsHttpHandler httpHandler;
+#else
+            private static readonly HttpClientHandler httpHandler;
+#endif
+
+            private static readonly CredentialCache credentialCache;
+
+            private MemoryStream postBuffer = new MemoryStream();
+            private HttpResponseMessage response;
+            private Stream responseStream;
+
+            static ManagedHttpSmartSubtransportStream()
+            {
+#if NETCOREAPP
+                httpHandler = new SocketsHttpHandler();
+                httpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);
+#else
+                httpHandler = new HttpClientHandler();
+                httpHandler.SslProtocols |= SslProtocols.Tls12;
+#endif
+
+                httpHandler.AllowAutoRedirect = false;
+
+                credentialCache = new CredentialCache();
+                httpHandler.Credentials = credentialCache;
+            }
+
+            public ManagedHttpSmartSubtransportStream(ManagedHttpSmartSubtransport parent, string endpointUrl, bool isPost, string contentType)
+                : base(parent)
+            {
+                EndpointUrl = new Uri(endpointUrl);
+                IsPost = isPost;
+                ContentType = contentType;
+            }
+
+            private HttpClient CreateHttpClient(HttpMessageHandler handler)
+            {
+                return new HttpClient(handler, false)
+                {
+                    DefaultRequestHeaders =
+                    {
+                        // This worked fine when it was on, but git.exe doesn't specify this header, so we don't either.
+                        ExpectContinue = false,
+                    },
+                };
+            }
+
+            private Uri 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 string GetUserAgent()
+            {
+                string userAgent = GlobalSettings.GetUserAgent();
+
+                if (string.IsNullOrEmpty(userAgent))
+                {
+                    userAgent = "LibGit2Sharp " + GlobalSettings.Version.InformationalVersion;
+                }
+
+                return userAgent;
+            }
+
+            private HttpRequestMessage CreateRequest(Uri endpointUrl, bool isPost)
+            {
+                var verb = isPost ? new HttpMethod("POST") : new HttpMethod("GET");
+                var request = new HttpRequestMessage(verb, endpointUrl);
+                request.Headers.Add("User-Agent", $"git/2.0 ({GetUserAgent()})");
+                request.Headers.Remove("Expect");
+
+                return request;
+            }
+
+            private HttpResponseMessage GetResponseWithRedirects()
+            {
+                var url = EndpointUrl;
+                int retries;
+
+                for (retries = 0; ; retries++)
+                {
+                    using (var httpClient = CreateHttpClient(httpHandler))
+                    {
+                        var request = CreateRequest(url, IsPost);
+
+                        if (retries > MAX_REDIRECTS)
+                        {
+                            throw new Exception("too many redirects or authentication replays");
+                        }
+
+                        if (IsPost && postBuffer.Length > 0)
+                        {
+                            var bufferDup = new MemoryStream(postBuffer.GetBuffer(), 0, (int)postBuffer.Length);
+
+                            request.Content = new StreamContent(bufferDup);
+                            request.Content.Headers.Add("Content-Type", ContentType);
+                        }
+
+                        var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead).GetAwaiter().GetResult();
+
+                        if (response.StatusCode == HttpStatusCode.OK)
+                        {
+                            return response;
+                        }
+                        else if (response.StatusCode == HttpStatusCode.Unauthorized)
+                        {
+                            int ret = SmartTransport.AcquireCredentials(out Credentials cred, null, typeof(UsernamePasswordCredentials));
+
+                            if (ret != 0)
+                            {
+                                throw new InvalidOperationException("authentication cancelled");
+                            }
+
+                            var scheme = response.Headers.WwwAuthenticate.First().Scheme;
+
+                            if (cred is DefaultCredentials)
+                            {
+                                lock (credentialCache)
+                                {
+                                    credentialCache.Add(url, scheme, CredentialCache.DefaultNetworkCredentials);
+                                }
+                            }
+                            else if (cred is UsernamePasswordCredentials userpass)
+                            {
+                                lock (credentialCache)
+                                {
+                                    credentialCache.Add(url, scheme, new NetworkCredential(userpass.Username, userpass.Password));
+                                }
+                            }
+
+                            continue;
+                        }
+                        else if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect)
+                        {
+                            url = new Uri(response.Headers.GetValues("Location").First());
+                            continue;
+                        }
+
+                        throw new Exception(string.Format("unexpected HTTP response: {0}", response.StatusCode));
+                    }
+                }
+
+                throw new Exception("too many redirects or authentication replays");
+            }
+
+            public override int Read(Stream dataStream, long length, out long readTotal)
+            {
+                byte[] buffer = new byte[4096];
+                readTotal = 0;
+
+                if (responseStream == null)
+                {
+                    response = GetResponseWithRedirects();
+                    responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
+                }
+
+                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;
+                }
+
+                if (response != null)
+                {
+                    response.Dispose();
+                    response = null;
+                }
+
+                base.Free();
+            }
+        }
+    }
+}
diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs
index 7caa909f1..7be9a1734 100644
--- a/LibGit2Sharp/Core/NativeMethods.cs
+++ b/LibGit2Sharp/Core/NativeMethods.cs
@@ -26,6 +26,9 @@ internal static class NativeMethods
         private static NativeShutdownObject shutdownObject;
 #pragma warning restore 0414
 
+        private static SmartSubtransportRegistration<ManagedHttpSmartSubtransport> httpSubtransportRegistration;
+        private static SmartSubtransportRegistration<ManagedHttpSmartSubtransport> httpsSubtransportRegistration;
+
         static NativeMethods()
         {
             if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore())
@@ -42,11 +45,8 @@ static NativeMethods()
                     string nativeLibraryPath = GetGlobalSettingsNativeLibraryPath();
                     if (nativeLibraryPath != null)
                     {
-#if NETFRAMEWORK
-                        if (Platform.OperatingSystem == OperatingSystemType.Windows)
-#else
                         if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-#endif
+
                         {
                             LoadWindowsLibrary(nativeLibraryPath);
                         }
@@ -146,8 +146,6 @@ private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImpor
                     return handle;
                 }
 
-#if NETFRAMEWORK
-#else
                 // We cary a number of .so files for Linux which are linked against various
                 // libc/OpenSSL libraries. Try them out.
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
@@ -170,7 +168,6 @@ private static IntPtr ResolveDll(string libraryName, Assembly assembly, DllImpor
                         }
                     }
                 }
-#endif
             }
             return handle;
         }
@@ -199,10 +196,11 @@ private static void InitializeNativeLibrary()
                 shutdownObject = new NativeShutdownObject();
             }
 
-            // Configure the OpenSSL locking on the first initialization of the library in the current process.
+            // Configure the .NET HTTP(S) mechanism on the first initialization of the library in the current process.
             if (initCounter == 1)
             {
-                git_openssl_set_locking();
+                httpSubtransportRegistration = GlobalSettings.RegisterDefaultSmartSubtransport<ManagedHttpSmartSubtransport>("http");
+                httpsSubtransportRegistration = GlobalSettings.RegisterDefaultSmartSubtransport<ManagedHttpSmartSubtransport>("https");
             }
         }
 
@@ -211,6 +209,16 @@ private sealed class NativeShutdownObject : CriticalFinalizerObject
         {
             ~NativeShutdownObject()
             {
+                if (httpSubtransportRegistration != null)
+                {
+                    GlobalSettings.UnregisterDefaultSmartSubtransport(httpSubtransportRegistration);
+                }
+
+                if (httpsSubtransportRegistration != null)
+                {
+                    GlobalSettings.UnregisterDefaultSmartSubtransport(httpsSubtransportRegistration);
+                }
+
                 git_libgit2_shutdown();
             }
         }
@@ -818,6 +826,15 @@ internal static extern int git_libgit2_opts(int option, uint level,
         // git_libgit2_opts(GIT_OPT_ENABLE_*, int enabled)
         [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
         internal static extern int git_libgit2_opts(int option, int enabled);
+
+        // git_libgit2_opts(GIT_OPT_SET_USER_AGENT, const char *path)
+        [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern int git_libgit2_opts(int option,
+            [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string path);
+
+        // git_libgit2_opts(GIT_OPT_GET_USER_AGENT, git_buf *buf)
+        [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern int git_libgit2_opts(int option, GitBuf buf);
         #endregion
 
         [DllImport(libgit2, CallingConvention = CallingConvention.Cdecl)]
diff --git a/LibGit2Sharp/Core/Platform.cs b/LibGit2Sharp/Core/Platform.cs
index 52859cbe2..e8d536475 100644
--- a/LibGit2Sharp/Core/Platform.cs
+++ b/LibGit2Sharp/Core/Platform.cs
@@ -13,30 +13,11 @@ internal enum OperatingSystemType
     internal static class Platform
     {
         public static string ProcessorArchitecture => IntPtr.Size == 8 ? "x64" : "x86";
-#if NETFRAMEWORK 
-        private static bool? _isRunningOnMac;
-        private static bool IsRunningOnMac() => _isRunningOnMac ?? (_isRunningOnMac = TryGetIsRunningOnMac()) ?? false;
-#endif
 
         public static OperatingSystemType OperatingSystem
         {
             get
             {
-#if NETFRAMEWORK
-                var platform = (int)Environment.OSVersion.Platform;
-                if (platform <= 3 || platform == 5)
-                {
-                    return OperatingSystemType.Windows;
-                }
-                if (IsRunningOnMac())
-                {
-                    return OperatingSystemType.MacOSX;
-                }
-                if (platform == 4 || platform == 6 || platform == 128)
-                {
-                    return OperatingSystemType.Unix;
-                }
-#else
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                 {
                     return OperatingSystemType.Windows;
@@ -51,7 +32,7 @@ public static OperatingSystemType OperatingSystem
                 {
                     return OperatingSystemType.MacOSX;
                 }
-#endif
+
                 throw new PlatformNotSupportedException();
             }
         }
@@ -90,70 +71,5 @@ public static bool IsRunningOnNetFramework()
         /// </summary>
         public static bool IsRunningOnNetCore()
             => typeof(object).Assembly.GetName().Name != "mscorlib";
-
-#if NETFRAMEWORK
-#pragma warning disable IDE1006 // Naming Styles
-        [DllImport("libc")]
-        private static extern int sysctlbyname(
-            [MarshalAs(UnmanagedType.LPStr)] string property,
-            IntPtr output,
-            IntPtr oldLen,
-            IntPtr newp,
-            uint newlen);
-#pragma warning restore IDE1006 // Naming Styles
-
-        private static bool TryGetIsRunningOnMac()
-        {
-            const string OsType = "kern.ostype";
-            const string MacOsType = "Darwin";
-
-            return MacOsType == GetOsType();
-
-            string GetOsType()
-            {
-                try
-                {
-                    IntPtr
-                        pointerLength = IntPtr.Zero,
-                        pointerString = IntPtr.Zero;
-
-                    try
-                    {
-                        pointerLength = Marshal.AllocHGlobal(sizeof(int));
-
-                        sysctlbyname(OsType, IntPtr.Zero, pointerLength, IntPtr.Zero, 0);
-
-                        var length = Marshal.ReadInt32(pointerLength);
-
-                        if (length <= 0)
-                        {
-                            return string.Empty;
-                        }
-
-                        pointerString = Marshal.AllocHGlobal(length);
-
-                        sysctlbyname(OsType, pointerString, pointerLength, IntPtr.Zero, 0);
-
-                        return Marshal.PtrToStringAnsi(pointerString);
-                    }
-                    finally
-                    {
-                        if (pointerLength != IntPtr.Zero)
-                        {
-                            Marshal.FreeHGlobal(pointerLength);
-                        }
-                        if (pointerString != IntPtr.Zero)
-                        {
-                            Marshal.FreeHGlobal(pointerString);
-                        }
-                    }
-                }
-                catch
-                {
-                    return null;
-                }
-            }
-        }
-#endif
     }
 }
diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs
index eddf092fd..7857224b4 100644
--- a/LibGit2Sharp/Core/Proxy.cs
+++ b/LibGit2Sharp/Core/Proxy.cs
@@ -3462,6 +3462,38 @@ public static void git_libgit2_opts_set_enable_strictobjectcreation(bool enabled
             Ensure.ZeroResult(res);
         }
 
+        /// <summary>
+        /// Sets the user-agent string to be used by the HTTP(S) transport.
+        /// Note that "git/2.0" will be prepended for compatibility.
+        /// </summary>
+        /// <param name="userAgent">The user-agent string to use</param>
+        public static void git_libgit2_opts_set_user_agent(string userAgent)
+        {
+            var res = NativeMethods.git_libgit2_opts((int)LibGit2Option.SetUserAgent, userAgent);
+            Ensure.ZeroResult(res);
+        }
+
+        /// <summary>
+        /// Gets the user-agent string used by libgit2.
+        /// <returns>
+        /// The user-agent string.
+        /// </returns>
+        /// </summary>
+        public static string git_libgit2_opts_get_user_agent()
+        {
+            string userAgent;
+
+            using (var buf = new GitBuf())
+            {
+                var res = NativeMethods.git_libgit2_opts((int)LibGit2Option.GetUserAgent, buf);
+                Ensure.ZeroResult(res);
+
+                userAgent = LaxUtf8Marshaler.FromNative(buf.ptr) ?? string.Empty;
+            }
+
+            return userAgent;
+        }
+
         #endregion
 
         #region git_worktree_
diff --git a/LibGit2Sharp/GlobalSettings.cs b/LibGit2Sharp/GlobalSettings.cs
index 8828cb883..fda9e93a0 100644
--- a/LibGit2Sharp/GlobalSettings.cs
+++ b/LibGit2Sharp/GlobalSettings.cs
@@ -22,6 +22,14 @@ public static class GlobalSettings
         private static bool nativeLibraryPathLocked;
         private static string nativeLibraryDefaultPath;
 
+        internal class SmartSubtransportData
+        {
+            internal bool isCustom;
+            internal SmartSubtransportRegistrationData defaultSubtransport;
+        }
+
+        private static readonly Dictionary<string, SmartSubtransportData> smartSubtransportData = new Dictionary<string, SmartSubtransportData>();
+
         static GlobalSettings()
         {
             bool netFX = Platform.IsRunningOnNetFramework();
@@ -71,20 +79,55 @@ private static string GetExecutingAssemblyDirectory()
         /// Returns information related to the current LibGit2Sharp
         /// library.
         /// </summary>
-        public static Version Version
+        public static Version Version => version.Value;
+
+        private static SmartSubtransportData GetOrCreateSmartSubtransportData(string scheme)
         {
-            get
+            Ensure.ArgumentNotNull(scheme, "scheme");
+
+            lock (smartSubtransportData)
             {
-                return version.Value;
+                if (!smartSubtransportData.ContainsKey(scheme))
+                {
+                    smartSubtransportData[scheme] = new SmartSubtransportData();
+                }
+
+                return smartSubtransportData[scheme];
+            }
+        }
+
+        internal static SmartSubtransportRegistration<T> RegisterDefaultSmartSubtransport<T>(string scheme)
+            where T : SmartSubtransport, new()
+        {
+            Ensure.ArgumentNotNull(scheme, "scheme");
+
+            lock (smartSubtransportData)
+            {
+                var data = GetOrCreateSmartSubtransportData(scheme);
+
+                if (data.defaultSubtransport != null)
+                {
+                    throw new Exception(string.Format("A default subtransport is already configured for {0}", scheme));
+                }
+
+                var registration = new SmartSubtransportRegistration<T>(scheme);
+
+                if (!data.isCustom)
+                {
+                    RegisterSmartSubtransportInternal(registration);
+                }
+
+                data.defaultSubtransport = registration;
+
+                return registration;
             }
         }
 
         /// <summary>
         /// Registers a new <see cref="SmartSubtransport"/> as a custom
-        /// smart-protocol transport with libgit2.  Any Git remote with
+        /// smart-protocol transport with libgit2. Any Git remote with
         /// the scheme registered will delegate to the given transport
-        /// for all communication with the server.  use this transport to communicate
-        /// with the server This is not commonly
+        /// for all communication with the server. This is not commonly
         /// used: some callers may want to re-use an existing connection to
         /// perform fetch / push operations to a remote.
         ///
@@ -98,21 +141,53 @@ public static SmartSubtransportRegistration<T> RegisterSmartSubtransport<T>(stri
         {
             Ensure.ArgumentNotNull(scheme, "scheme");
 
-            var registration = new SmartSubtransportRegistration<T>(scheme);
+            lock (smartSubtransportData)
+            {
+                var data = GetOrCreateSmartSubtransportData(scheme);
+
+                if (data.isCustom)
+                {
+                    throw new EntryExistsException(string.Format("A smart subtransport is already registered for {0}", scheme));
+                }
+
+                if (data.defaultSubtransport != null)
+                {
+                    Proxy.git_transport_unregister(scheme);
+                }
+
+                var previousValue = data.isCustom;
+                data.isCustom = true;
+
+                var registration = new SmartSubtransportRegistration<T>(scheme);
 
+                try
+                {
+                    RegisterSmartSubtransportInternal(registration);
+                }
+                catch
+                {
+                    data.isCustom = previousValue;
+                    throw;
+                }
+
+                return registration;
+            }
+        }
+
+        private static void RegisterSmartSubtransportInternal<T>(SmartSubtransportRegistration<T> registration)
+            where T : SmartSubtransport, new()
+        {
             try
             {
                 Proxy.git_transport_register(registration.Scheme,
                                              registration.FunctionPointer,
                                              registration.RegistrationPointer);
             }
-            catch (Exception)
+            catch
             {
                 registration.Free();
                 throw;
             }
-
-            return registration;
         }
 
         /// <summary>
@@ -126,6 +201,59 @@ public static void UnregisterSmartSubtransport<T>(SmartSubtransportRegistration<
         {
             Ensure.ArgumentNotNull(registration, "registration");
 
+            var scheme = registration.Scheme;
+
+            lock (smartSubtransportData)
+            {
+                var data = GetOrCreateSmartSubtransportData(scheme);
+
+                if (!data.isCustom)
+                {
+                    throw new NotFoundException(string.Format("No smart subtransport has been registered for {0}", scheme));
+                }
+
+                UnregisterSmartSubtransportInternal(registration);
+
+                data.isCustom = false;
+
+                if (data.defaultSubtransport != null)
+                {
+                    var defaultRegistration = data.defaultSubtransport;
+
+                    Proxy.git_transport_register(defaultRegistration.Scheme,
+                                                 defaultRegistration.FunctionPointer,
+                                                 defaultRegistration.RegistrationPointer);
+                }
+            }
+        }
+
+        internal static void UnregisterDefaultSmartSubtransport<T>(SmartSubtransportRegistration<T> registration)
+            where T : SmartSubtransport, new()
+        {
+            Ensure.ArgumentNotNull(registration, "registration");
+
+            var scheme = registration.Scheme;
+
+            lock (smartSubtransportData)
+            {
+                if (!smartSubtransportData.ContainsKey(scheme))
+                {
+                    throw new Exception(string.Format("No default smart subtransport has been registered for {0}", scheme));
+                }
+
+                if (registration != smartSubtransportData[scheme].defaultSubtransport)
+                {
+                    throw new Exception(string.Format("The given smart subtransport is not the default for {0}", scheme));
+                }
+
+                smartSubtransportData.Remove(scheme);
+                UnregisterSmartSubtransportInternal(registration);
+            }
+        }
+
+        private static void UnregisterSmartSubtransportInternal<T>(SmartSubtransportRegistration<T> registration)
+            where T : SmartSubtransport, new()
+        {
             Proxy.git_transport_unregister(registration.Scheme);
             registration.Free();
         }
@@ -184,16 +312,15 @@ public static LogConfiguration LogConfiguration
         /// </summary>
         public static string NativeLibraryPath
         {
-            get
-            {
-                if (!nativeLibraryPathAllowed)
-                {
-                    throw new LibGit2SharpException("Querying the native hint path is only supported on .NET Framework and .NET Core platforms");
-                }
+            get {
+                lock (smartSubtransportData) {
+                    if (!nativeLibraryPathAllowed) {
+                        throw new LibGit2SharpException ("Querying the native hint path is only supported on .NET Framework and .NET Core platforms");
+                    }
 
-                return nativeLibraryPath ?? nativeLibraryDefaultPath;
+                    return nativeLibraryPath ?? nativeLibraryDefaultPath;
+                }
             }
-
             set
             {
                 if (!nativeLibraryPathAllowed)
@@ -386,5 +513,26 @@ public static void SetEnableStrictObjectCreation(bool enabled)
         {
             Proxy.git_libgit2_opts_set_enable_strictobjectcreation(enabled);
         }
+
+        /// <summary>
+        /// Sets the user-agent string to be used by the HTTP(S) transport.
+        /// Note that "git/2.0" will be prepended for compatibility.
+        /// </summary>
+        /// <param name="userAgent">The user-agent string to use</param>
+        public static void SetUserAgent(string userAgent)
+        {
+            Proxy.git_libgit2_opts_set_user_agent(userAgent);
+        }
+
+        /// <summary>
+        /// Gets the user-agent string used by libgit2.
+        /// <returns>
+        /// The user-agent string.
+        /// </returns>
+        /// </summary>
+        public static string GetUserAgent()
+        {
+            return Proxy.git_libgit2_opts_get_user_agent();
+        }
     }
 }
diff --git a/LibGit2Sharp/InvalidSpecificationException.cs b/LibGit2Sharp/InvalidSpecificationException.cs
index 64654852c..3d34571a4 100644
--- a/LibGit2Sharp/InvalidSpecificationException.cs
+++ b/LibGit2Sharp/InvalidSpecificationException.cs
@@ -11,7 +11,7 @@ namespace LibGit2Sharp
     /// create a branch from a blob, or peeling a blob to a commit).
     /// </summary>
     [Serializable]
-    public class InvalidSpecificationException : LibGit2SharpException
+    public class InvalidSpecificationException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="InvalidSpecificationException"/> class.
@@ -54,8 +54,16 @@ protected InvalidSpecificationException(SerializationInfo info, StreamingContext
             : base(info, context)
         { }
 
-        internal InvalidSpecificationException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal InvalidSpecificationException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.InvalidSpecification;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj
index 943f167f7..3b5cefd25 100644
--- a/LibGit2Sharp/LibGit2Sharp.csproj
+++ b/LibGit2Sharp/LibGit2Sharp.csproj
@@ -2,7 +2,7 @@
 
   <Import Project="..\..\..\MonoDevelop.props" Condition="Exists('..\..\..\MonoDevelop.props')" />
   <PropertyGroup>
-    <TargetFrameworks>net472;net48;netstandard2.0;netstandard2.1</TargetFrameworks>
+    <TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <Description>LibGit2Sharp brings all the might and speed of libgit2, a native Git implementation, to the managed world of .Net and Mono.</Description>
     <Company>LibGit2Sharp contributors</Company>
diff --git a/LibGit2Sharp/LibGit2SharpException.cs b/LibGit2Sharp/LibGit2SharpException.cs
index e85dd638f..5d1c33f25 100644
--- a/LibGit2Sharp/LibGit2SharpException.cs
+++ b/LibGit2Sharp/LibGit2SharpException.cs
@@ -52,11 +52,5 @@ public LibGit2SharpException(string format, params object[] args)
         protected LibGit2SharpException(SerializationInfo info, StreamingContext context)
             : base(info, context)
         { }
-
-        internal LibGit2SharpException(string message, GitErrorCode code, GitErrorCategory category) : this(message)
-        {
-            Data.Add("libgit2.code", (int)code);
-            Data.Add("libgit2.category", (int)category);
-        }
     }
 }
diff --git a/LibGit2Sharp/LockedFileException.cs b/LibGit2Sharp/LockedFileException.cs
index 05859503a..44fd65b02 100644
--- a/LibGit2Sharp/LockedFileException.cs
+++ b/LibGit2Sharp/LockedFileException.cs
@@ -8,7 +8,7 @@ namespace LibGit2Sharp
     /// The exception that is thrown attempting to open a locked file.
     /// </summary>
     [Serializable]
-    public class LockedFileException : LibGit2SharpException
+    public class LockedFileException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="LibGit2Sharp.LockedFileException"/> class.
@@ -51,8 +51,16 @@ protected LockedFileException(SerializationInfo info, StreamingContext context)
             : base(info, context)
         { }
 
-        internal LockedFileException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal LockedFileException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.LockedFile;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/NameConflictException.cs b/LibGit2Sharp/NameConflictException.cs
index 815415729..0dcffc648 100644
--- a/LibGit2Sharp/NameConflictException.cs
+++ b/LibGit2Sharp/NameConflictException.cs
@@ -8,7 +8,7 @@ namespace LibGit2Sharp
     /// The exception that is thrown when a reference, a remote, a submodule... with the same name already exists in the repository
     /// </summary>
     [Serializable]
-    public class NameConflictException : LibGit2SharpException
+    public class NameConflictException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="NameConflictException"/> class.
@@ -51,8 +51,16 @@ protected NameConflictException(SerializationInfo info, StreamingContext context
             : base(info, context)
         { }
 
-        internal NameConflictException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal NameConflictException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.Exists;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/NativeException.cs b/LibGit2Sharp/NativeException.cs
new file mode 100644
index 000000000..2d418b85d
--- /dev/null
+++ b/LibGit2Sharp/NativeException.cs
@@ -0,0 +1,46 @@
+using LibGit2Sharp.Core;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace LibGit2Sharp
+{
+    /// <summary>
+    /// An exception thrown that corresponds to a libgit2 (native library) error.
+    /// </summary>
+    [Serializable]
+    public abstract class NativeException : LibGit2SharpException
+    {
+        /// <summary>
+        /// Needed for mocking purposes.
+        /// </summary>
+        protected NativeException()
+        { }
+
+        internal NativeException(string message)
+            : base(message)
+        { }
+
+        internal NativeException(string message, Exception innerException)
+            : base(message, innerException)
+        { }
+
+        internal NativeException(string format, params object[] args)
+            : base(format, args)
+        {
+        }
+
+        internal NativeException(SerializationInfo info, StreamingContext context)
+            : base(info, context)
+        { }
+
+        internal NativeException(string message, GitErrorCategory category) : this(message)
+        {
+            Data.Add("libgit2.category", (int)category);
+        }
+
+        internal abstract GitErrorCode ErrorCode { get; }
+    }
+}
diff --git a/LibGit2Sharp/NonFastForwardException.cs b/LibGit2Sharp/NonFastForwardException.cs
index 487e8fd03..b5a858f47 100644
--- a/LibGit2Sharp/NonFastForwardException.cs
+++ b/LibGit2Sharp/NonFastForwardException.cs
@@ -9,7 +9,7 @@ namespace LibGit2Sharp
     /// against the remote without losing commits.
     /// </summary>
     [Serializable]
-    public class NonFastForwardException : LibGit2SharpException
+    public class NonFastForwardException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="LibGit2Sharp.NonFastForwardException"/> class.
@@ -52,8 +52,16 @@ protected NonFastForwardException(SerializationInfo info, StreamingContext conte
             : base(info, context)
         { }
 
-        internal NonFastForwardException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal NonFastForwardException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.NonFastForward;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/NotFoundException.cs b/LibGit2Sharp/NotFoundException.cs
index 0e9b45bf3..f8c49cc91 100644
--- a/LibGit2Sharp/NotFoundException.cs
+++ b/LibGit2Sharp/NotFoundException.cs
@@ -8,7 +8,7 @@ namespace LibGit2Sharp
     /// The exception that is thrown attempting to reference a resource that does not exist.
     /// </summary>
     [Serializable]
-    public class NotFoundException : LibGit2SharpException
+    public class NotFoundException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="LibGit2Sharp.NotFoundException"/> class.
@@ -51,8 +51,16 @@ protected NotFoundException(SerializationInfo info, StreamingContext context)
             : base(info, context)
         { }
 
-        internal NotFoundException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal NotFoundException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.NotFound;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/PeelException.cs b/LibGit2Sharp/PeelException.cs
index 09d6bdcc8..d7758d7c9 100644
--- a/LibGit2Sharp/PeelException.cs
+++ b/LibGit2Sharp/PeelException.cs
@@ -9,7 +9,7 @@ namespace LibGit2Sharp
     /// target type due to the object model.
     /// </summary>
     [Serializable]
-    public class PeelException : LibGit2SharpException
+    public class PeelException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="PeelException"/> class.
@@ -52,8 +52,16 @@ protected PeelException(SerializationInfo info, StreamingContext context)
             : base(info, context)
         { }
 
-        internal PeelException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal PeelException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.Peel;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/SmartSubtransport.cs b/LibGit2Sharp/SmartSubtransport.cs
index 77ad9f2da..c6d747073 100644
--- a/LibGit2Sharp/SmartSubtransport.cs
+++ b/LibGit2Sharp/SmartSubtransport.cs
@@ -81,6 +81,11 @@ public int CertificateCheck(Certificate cert, bool valid, string hostname)
                 Marshal.FreeHGlobal(certPtr);
             }
 
+            if (ret > 0 || ret == (int)GitErrorCode.PassThrough)
+            {
+                ret = valid ? 0 : -1;
+            }
+
             return ret;
         }
 
@@ -101,6 +106,10 @@ public int AcquireCredentials(out Credentials cred, string user, params Type[] m
                 {
                     allowed |= (int)GitCredentialType.UserPassPlaintext;
                 }
+                else if (method == typeof(DefaultCredentials))
+                {
+                    allowed |= (int)GitCredentialType.Default;
+                }
                 else
                 {
                     throw new InvalidOperationException("Unknown type passes as allowed credential");
@@ -117,7 +126,7 @@ public int AcquireCredentials(out Credentials cred, string user, params Type[] m
 
             if (credHandle == IntPtr.Zero)
             {
-                throw new InvalidOperationException("creditals callback indicated success but returned no credentials");
+                throw new InvalidOperationException("credentials callback indicated success but returned no credentials");
             }
 
             unsafe
@@ -128,12 +137,21 @@ public int AcquireCredentials(out Credentials cred, string user, params Type[] m
                     case GitCredentialType.UserPassPlaintext:
                         cred = UsernamePasswordCredentials.FromNative((GitCredentialUserpass*) credHandle);
                         return 0;
+                    case GitCredentialType.Default:
+                        cred = new DefaultCredentials();
+                        return 0;
                     default:
                         throw new InvalidOperationException("User returned an unkown credential type");
                 }
             }
         }
 
+        /// <summary>
+        /// libgit2 will call an action back with a null url to indicate that
+        /// it should re-use the prior url; store the url so that we can replay.
+        /// </summary>
+        private string LastActionUrl { get; set; }
+
         /// <summary>
         /// Invoked by libgit2 to create a connection using this subtransport.
         /// </summary>
@@ -209,43 +227,57 @@ private static int Action(
                 SmartSubtransport t = GCHandle.FromIntPtr(Marshal.ReadIntPtr(subtransport, GitSmartSubtransport.GCHandleOffset)).Target as SmartSubtransport;
                 String urlAsString = LaxUtf8Marshaler.FromNative(url);
 
-                if (null != t &&
-                    !String.IsNullOrEmpty(urlAsString))
+                if (t == null)
                 {
-                    try
-                    {
-                        stream = t.Action(urlAsString, action).GitSmartTransportStreamPointer;
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "no subtransport provided");
+                    return (int)GitErrorCode.Error;
+                }
 
-                        return 0;
-                    }
-                    catch (Exception ex)
-                    {
-                        Proxy.giterr_set_str(GitErrorCategory.Net, ex);
-                    }
+                if (String.IsNullOrEmpty(urlAsString))
+                {
+                    urlAsString = t.LastActionUrl;
                 }
 
-                return (int)GitErrorCode.Error;
+                if (String.IsNullOrEmpty(urlAsString))
+                {
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "no url provided");
+                    return (int)GitErrorCode.Error;
+                }
+
+                try
+                {
+                    stream = t.Action(urlAsString, action).GitSmartTransportStreamPointer;
+                    t.LastActionUrl = urlAsString;
+                    return 0;
+                }
+                catch (Exception ex)
+                {
+                    Proxy.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)
+                if (t == null)
                 {
-                    try
-                    {
-                        t.Close();
-
-                        return 0;
-                    }
-                    catch (Exception ex)
-                    {
-                        Proxy.giterr_set_str(GitErrorCategory.Net, ex);
-                    }
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "no subtransport provided");
+                    return (int)GitErrorCode.Error;
                 }
 
-                return (int)GitErrorCode.Error;
+                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)
diff --git a/LibGit2Sharp/SmartSubtransportRegistration.cs b/LibGit2Sharp/SmartSubtransportRegistration.cs
index e9f21dada..adb9ba23d 100644
--- a/LibGit2Sharp/SmartSubtransportRegistration.cs
+++ b/LibGit2Sharp/SmartSubtransportRegistration.cs
@@ -11,7 +11,7 @@ namespace LibGit2Sharp
     /// under a particular scheme (eg "http").
     /// </summary>
     /// <typeparam name="T">The type of SmartSubtransport to register</typeparam>
-    public sealed class SmartSubtransportRegistration<T>
+    public sealed class SmartSubtransportRegistration<T> : SmartSubtransportRegistrationData
         where T : SmartSubtransport, new()
     {
         /// <summary>
@@ -26,15 +26,6 @@ internal SmartSubtransportRegistration(string scheme)
             FunctionPointer = CreateFunctionPointer();
         }
 
-        /// <summary>
-        /// The URI scheme (eg "http") for this transport.
-        /// </summary>
-        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();
diff --git a/LibGit2Sharp/SmartSubtransportRegistrationData.cs b/LibGit2Sharp/SmartSubtransportRegistrationData.cs
new file mode 100644
index 000000000..470d64393
--- /dev/null
+++ b/LibGit2Sharp/SmartSubtransportRegistrationData.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace LibGit2Sharp
+{
+    /// <summary>
+    /// Information about a smart subtransport registration.
+    /// </summary>
+    public abstract class SmartSubtransportRegistrationData
+    {
+        /// <summary>
+        /// The URI scheme for this transport, for example "http" or "ssh".
+        /// </summary>
+        public string Scheme { get; internal set; }
+
+        internal IntPtr RegistrationPointer { get; set; }
+
+        internal IntPtr FunctionPointer { get; set; }
+    }
+}
diff --git a/LibGit2Sharp/SmartSubtransportStream.cs b/LibGit2Sharp/SmartSubtransportStream.cs
index b5cb21c02..ce8aebdf0 100644
--- a/LibGit2Sharp/SmartSubtransportStream.cs
+++ b/LibGit2Sharp/SmartSubtransportStream.cs
@@ -44,13 +44,20 @@ protected virtual void Free()
         }
 
         /// <summary>
-        /// Requests that the stream write the next length bytes of the stream to the provided Stream object.
+        /// Reads from the transport into the provided <paramref name="dataStream"/> object.
         /// </summary>
+        /// <param name="dataStream">The stream to copy the read bytes into.</param>
+        /// <param name="length">The number of bytes expected from the underlying transport.</param>
+        /// <param name="bytesRead">Receives the number of bytes actually read.</param>
+        /// <returns>The error code to propagate back to the native code that requested this operation. 0 is expected, and exceptions may be thrown.</returns>
         public abstract int Read(Stream dataStream, long length, out long bytesRead);
 
         /// <summary>
-        /// Requests that the stream write the first length bytes of the provided Stream object to the stream.
+        /// Writes the content of a given stream to the transport.
         /// </summary>
+        /// <param name="dataStream">The stream with the data to write to the transport.</param>
+        /// <param name="length">The number of bytes to read from <paramref name="dataStream"/>.</param>
+        /// <returns>The error code to propagate back to the native code that requested this operation. 0 is expected, and exceptions may be thrown.</returns>
         public abstract int Write(Stream dataStream, long length);
 
         /// <summary>
@@ -61,6 +68,13 @@ public virtual SmartSubtransport SmartTransport
             get { return this.subtransport; }
         }
 
+        private Exception StoredError { get; set; }
+
+        internal void SetError(Exception ex)
+        {
+            StoredError = ex;
+        }
+
         private SmartSubtransport subtransport;
         private IntPtr nativeStreamPointer;
 
@@ -96,6 +110,19 @@ private static class EntryPoints
             public static GitSmartSubtransportStream.write_callback WriteCallback = new GitSmartSubtransportStream.write_callback(Write);
             public static GitSmartSubtransportStream.free_callback FreeCallback = new GitSmartSubtransportStream.free_callback(Free);
 
+            private static int SetError(SmartSubtransportStream stream, Exception caught)
+            {
+                Exception ret = (stream.StoredError != null) ? stream.StoredError : caught;
+                GitErrorCode errorCode = GitErrorCode.Error;
+
+                if (ret is NativeException)
+                {
+                    errorCode = ((NativeException)ret).ErrorCode;
+                }
+
+                return (int)errorCode;
+            }
+
             private unsafe static int Read(
                 IntPtr stream,
                 IntPtr buffer,
@@ -107,31 +134,37 @@ private unsafe static int Read(
                 SmartSubtransportStream transportStream =
                     GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream;
 
-                if (transportStream != null &&
-                    buf_size.ToUInt64() < (ulong)long.MaxValue)
+                if (transportStream == null)
+                {
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "no transport stream provided");
+                    return (int)GitErrorCode.Error;
+                }
+
+                if (buf_size.ToUInt64() >= (ulong)long.MaxValue)
+                {
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "buffer size is too large");
+                    return (int)GitErrorCode.Error;
+                }
+
+                try
                 {
                     using (UnmanagedMemoryStream memoryStream = new UnmanagedMemoryStream((byte*)buffer, 0,
                                                                                           (long)buf_size.ToUInt64(),
                                                                                           FileAccess.ReadWrite))
                     {
-                        try
-                        {
-                            long longBytesRead;
+                        long longBytesRead;
 
-                            int toReturn = transportStream.Read(memoryStream, (long)buf_size.ToUInt64(), out longBytesRead);
+                        int toReturn = transportStream.Read(memoryStream, (long)buf_size.ToUInt64(), out longBytesRead);
 
-                            bytes_read = new UIntPtr((ulong)Math.Max(0, longBytesRead));
+                        bytes_read = new UIntPtr((ulong)Math.Max(0, longBytesRead));
 
-                            return toReturn;
-                        }
-                        catch (Exception ex)
-                        {
-                            Proxy.giterr_set_str(GitErrorCategory.Net, ex);
-                        }
+                        return toReturn;
                     }
                 }
-
-                return (int)GitErrorCode.Error;
+                catch (Exception ex)
+                {
+                    return SetError(transportStream, ex);
+                }
             }
 
             private static unsafe int Write(IntPtr stream, IntPtr buffer, UIntPtr len)
@@ -139,24 +172,31 @@ private static unsafe int Write(IntPtr stream, IntPtr buffer, UIntPtr len)
                 SmartSubtransportStream transportStream =
                     GCHandle.FromIntPtr(Marshal.ReadIntPtr(stream, GitSmartSubtransportStream.GCHandleOffset)).Target as SmartSubtransportStream;
 
-                if (transportStream != null && len.ToUInt64() < (ulong)long.MaxValue)
+                if (transportStream == null)
+                {
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "no transport stream provided");
+                    return (int)GitErrorCode.Error;
+                }
+
+                if (len.ToUInt64() >= (ulong)long.MaxValue)
+                {
+                    Proxy.giterr_set_str(GitErrorCategory.Net, "write length is too large");
+                    return (int)GitErrorCode.Error;
+                }
+
+                try
                 {
                     long length = (long)len.ToUInt64();
 
                     using (UnmanagedMemoryStream dataStream = new UnmanagedMemoryStream((byte*)buffer, length))
                     {
-                        try
-                        {
-                            return transportStream.Write(dataStream, length);
-                        }
-                        catch (Exception ex)
-                        {
-                            Proxy.giterr_set_str(GitErrorCategory.Net, ex);
-                        }
+                        return transportStream.Write(dataStream, length);
                     }
                 }
-
-                return (int)GitErrorCode.Error;
+                catch (Exception ex)
+                {
+                    return SetError(transportStream, ex);
+                }
             }
 
             private static void Free(IntPtr stream)
diff --git a/LibGit2Sharp/UnmergedIndexEntriesException.cs b/LibGit2Sharp/UnmergedIndexEntriesException.cs
index 729882678..7594049b1 100644
--- a/LibGit2Sharp/UnmergedIndexEntriesException.cs
+++ b/LibGit2Sharp/UnmergedIndexEntriesException.cs
@@ -9,7 +9,7 @@ namespace LibGit2Sharp
     /// is performed against an index with unmerged entries
     /// </summary>
     [Serializable]
-    public class UnmergedIndexEntriesException : LibGit2SharpException
+    public class UnmergedIndexEntriesException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="UnmergedIndexEntriesException"/> class.
@@ -52,8 +52,16 @@ protected UnmergedIndexEntriesException(SerializationInfo info, StreamingContext
             : base(info, context)
         { }
 
-        internal UnmergedIndexEntriesException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal UnmergedIndexEntriesException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.UnmergedEntries;
+            }
+        }
     }
 }
diff --git a/LibGit2Sharp/UserCanceledException.cs b/LibGit2Sharp/UserCanceledException.cs
index 41eebb29a..ba6458049 100644
--- a/LibGit2Sharp/UserCanceledException.cs
+++ b/LibGit2Sharp/UserCanceledException.cs
@@ -8,7 +8,7 @@ namespace LibGit2Sharp
     /// The exception that is thrown when an operation is canceled.
     /// </summary>
     [Serializable]
-    public class UserCancelledException : LibGit2SharpException
+    public class UserCancelledException : NativeException
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="LibGit2Sharp.UserCancelledException"/> class.
@@ -51,8 +51,16 @@ protected UserCancelledException(SerializationInfo info, StreamingContext contex
             : base(info, context)
         { }
 
-        internal UserCancelledException(string message, GitErrorCode code, GitErrorCategory category)
-            : base(message, code, category)
+        internal UserCancelledException(string message, GitErrorCategory category)
+            : base(message, category)
         { }
+
+        internal override GitErrorCode ErrorCode
+        {
+            get
+            {
+                return GitErrorCode.User;
+            }
+        }
     }
 }