|
8 | 8 | using System.Linq; |
9 | 9 | using System.Runtime.InteropServices; |
10 | 10 | using System.Text; |
| 11 | +using Validation; |
11 | 12 |
|
12 | 13 | namespace Nerdbank.GitVersioning.ManagedGit |
13 | 14 | { |
@@ -222,9 +223,7 @@ public object GetHeadAsReferenceOrSha() |
222 | 223 | /// </returns> |
223 | 224 | public GitObjectId GetHeadCommitSha() |
224 | 225 | { |
225 | | - var reference = this.GetHeadAsReferenceOrSha(); |
226 | | - var objectId = this.ResolveReference(reference); |
227 | | - return objectId; |
| 226 | + return this.Lookup("HEAD") ?? GitObjectId.Empty; |
228 | 227 | } |
229 | 228 |
|
230 | 229 | /// <summary> |
@@ -280,16 +279,25 @@ public GitCommit GetCommit(GitObjectId sha, bool readAuthor = false) |
280 | 279 | /// <returns>The object ID referenced by <paramref name="objectish"/> if found; otherwise <see langword="null"/>.</returns> |
281 | 280 | public GitObjectId? Lookup(string objectish) |
282 | 281 | { |
| 282 | + bool skipObjectIdLookup = false; |
| 283 | + |
283 | 284 | if (objectish == "HEAD") |
284 | 285 | { |
285 | | - return this.GetHeadCommitSha(); |
| 286 | + var reference = this.GetHeadAsReferenceOrSha(); |
| 287 | + if (reference is GitObjectId headObjectId) |
| 288 | + { |
| 289 | + return headObjectId; |
| 290 | + } |
| 291 | + |
| 292 | + objectish = (string)reference; |
286 | 293 | } |
287 | 294 |
|
288 | 295 | var possibleLooseFileMatches = new List<string>(); |
289 | 296 | if (objectish.StartsWith("refs/", StringComparison.Ordinal)) |
290 | 297 | { |
291 | 298 | // Match on loose ref files by their canonical name. |
292 | | - possibleLooseFileMatches.Add(Path.Combine(this.GitDirectory, objectish)); |
| 299 | + possibleLooseFileMatches.Add(Path.Combine(this.CommonDirectory, objectish)); |
| 300 | + skipObjectIdLookup = true; |
293 | 301 | } |
294 | 302 | else |
295 | 303 | { |
@@ -341,6 +349,11 @@ public GitCommit GetCommit(GitObjectId sha, bool readAuthor = false) |
341 | 349 | } |
342 | 350 | } |
343 | 351 |
|
| 352 | + if (skipObjectIdLookup) |
| 353 | + { |
| 354 | + return null; |
| 355 | + } |
| 356 | + |
344 | 357 | if (objectish.Length == 40) |
345 | 358 | { |
346 | 359 | return GitObjectId.Parse(objectish); |
@@ -372,25 +385,30 @@ public GitCommit GetCommit(GitObjectId sha, bool readAuthor = false) |
372 | 385 | objectish += "0"; |
373 | 386 | } |
374 | 387 |
|
375 | | - var hex = ConvertHexStringToByteArray(objectish); |
376 | | - |
377 | | - foreach (var pack in this.packs.Value.Span) |
| 388 | + if (objectish.Length <= 40 && objectish.Length % 2 == 0) |
378 | 389 | { |
379 | | - var objectId = pack.Lookup(hex, endsWithHalfByte); |
380 | | - |
381 | | - // It's possible for the same object to be present in both the object database and the pack files, |
382 | | - // or in multiple pack files. |
383 | | - if (objectId != null && !possibleObjectIds.Contains(objectId.Value)) |
| 390 | + Span<byte> decodedHex = stackalloc byte[objectish.Length / 2]; |
| 391 | + if (TryConvertHexStringToByteArray(objectish, decodedHex)) |
384 | 392 | { |
385 | | - if (possibleObjectIds.Count > 0) |
386 | | - { |
387 | | - // If objectish already resolved to at least one object which is different from the current |
388 | | - // object id, objectish is not well-defined; so stop resolving and return null instead. |
389 | | - return null; |
390 | | - } |
391 | | - else |
| 393 | + foreach (var pack in this.packs.Value.Span) |
392 | 394 | { |
393 | | - possibleObjectIds.Add(objectId.Value); |
| 395 | + var objectId = pack.Lookup(decodedHex, endsWithHalfByte); |
| 396 | + |
| 397 | + // It's possible for the same object to be present in both the object database and the pack files, |
| 398 | + // or in multiple pack files. |
| 399 | + if (objectId != null && !possibleObjectIds.Contains(objectId.Value)) |
| 400 | + { |
| 401 | + if (possibleObjectIds.Count > 0) |
| 402 | + { |
| 403 | + // If objectish already resolved to at least one object which is different from the current |
| 404 | + // object id, objectish is not well-defined; so stop resolving and return null instead. |
| 405 | + return null; |
| 406 | + } |
| 407 | + else |
| 408 | + { |
| 409 | + possibleObjectIds.Add(objectId.Value); |
| 410 | + } |
| 411 | + } |
394 | 412 | } |
395 | 413 | } |
396 | 414 | } |
@@ -610,33 +628,6 @@ private bool TryGetObjectByPath(GitObjectId sha, string objectType, [NotNullWhen |
610 | 628 | return true; |
611 | 629 | } |
612 | 630 |
|
613 | | - private GitObjectId ResolveReference(object reference) |
614 | | - { |
615 | | - if (reference is string) |
616 | | - { |
617 | | - if (!FileHelpers.TryOpen(Path.Combine(this.CommonDirectory, (string)reference), out FileStream? stream)) |
618 | | - { |
619 | | - return GitObjectId.Empty; |
620 | | - } |
621 | | - |
622 | | - using (stream) |
623 | | - { |
624 | | - Span<byte> objectId = stackalloc byte[40]; |
625 | | - stream!.Read(objectId); |
626 | | - |
627 | | - return GitObjectId.ParseHex(objectId); |
628 | | - } |
629 | | - } |
630 | | - else if (reference is GitObjectId) |
631 | | - { |
632 | | - return (GitObjectId)reference; |
633 | | - } |
634 | | - else |
635 | | - { |
636 | | - throw new GitException(); |
637 | | - } |
638 | | - } |
639 | | - |
640 | 631 | private ReadOnlyMemory<GitPack> LoadPacks() |
641 | 632 | { |
642 | 633 | var packDirectory = Path.Combine(this.ObjectDirectory, "pack/"); |
@@ -687,22 +678,34 @@ private static string TrimEndingDirectorySeparator(string path) |
687 | 678 | #endif |
688 | 679 | } |
689 | 680 |
|
690 | | - private static byte[] ConvertHexStringToByteArray(string hexString) |
| 681 | + private static bool TryConvertHexStringToByteArray(string hexString, Span<byte> data) |
691 | 682 | { |
692 | 683 | // https://stackoverflow.com/questions/321370/how-can-i-convert-a-hex-string-to-a-byte-array |
693 | 684 | if (hexString.Length % 2 != 0) |
694 | 685 | { |
695 | | - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", hexString)); |
| 686 | + data = null; |
| 687 | + return false; |
696 | 688 | } |
697 | 689 |
|
698 | | - byte[] data = new byte[hexString.Length / 2]; |
| 690 | + Requires.Argument(data.Length == hexString.Length / 2, nameof(data), "Length must be exactly half that of " + nameof(hexString) + "."); |
699 | 691 | for (int index = 0; index < data.Length; index++) |
700 | 692 | { |
| 693 | +#if NETCOREAPP3_1_OR_GREATER |
| 694 | + ReadOnlySpan<char> byteValue = hexString.AsSpan(index * 2, 2); |
| 695 | + if (!byte.TryParse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out data[index])) |
| 696 | + { |
| 697 | + return false; |
| 698 | + } |
| 699 | +#else |
701 | 700 | string byteValue = hexString.Substring(index * 2, 2); |
702 | | - data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); |
| 701 | + if (!byte.TryParse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out data[index])) |
| 702 | + { |
| 703 | + return false; |
| 704 | + } |
| 705 | +#endif |
703 | 706 | } |
704 | 707 |
|
705 | | - return data; |
| 708 | + return true; |
706 | 709 | } |
707 | 710 |
|
708 | 711 | /// <summary> |
|
0 commit comments