| ... | @@ -456,6 +456,20 @@ const Packet = union(enum) { | ... | @@ -456,6 +456,20 @@ const Packet = union(enum) { |
| 456 | }, | 456 | }, |
| 457 | } | 457 | } |
| 458 | } | 458 | } |
| | 459 | |
| | 460 | /// Returns the normalized form of textual packet data, stripping any |
| | 461 | /// trailing '\n'. |
| | 462 | /// |
| | 463 | /// As documented in |
| | 464 | /// [protocol-common](https://git-scm.com/docs/protocol-common#_pkt_line_format), |
| | 465 | /// non-binary (textual) pkt-line data should contain a trailing '\n', but |
| | 466 | /// is not required to do so (implementations must support both forms). |
| | 467 | fn normalizeText(data: []const u8) []const u8 { |
| | 468 | return if (mem.endsWith(u8, data, "\n")) |
| | 469 | data[0 .. data.len - 1] |
| | 470 | else |
| | 471 | data; |
| | 472 | } |
| 459 | }; | 473 | }; |
| 460 | | 474 | |
| 461 | /// A client session for the Git protocol, currently limited to an HTTP(S) | 475 | /// A client session for the Git protocol, currently limited to an HTTP(S) |
| ... | @@ -554,7 +568,7 @@ pub const Session = struct { | ... | @@ -554,7 +568,7 @@ pub const Session = struct { |
| 554 | switch (packet) { | 568 | switch (packet) { |
| 555 | .flush => state = .response_start, | 569 | .flush => state = .response_start, |
| 556 | .data => |data| switch (state) { | 570 | .data => |data| switch (state) { |
| 557 | .response_start => if (mem.eql(u8, data, "version 2\n")) { | 571 | .response_start => if (mem.eql(u8, Packet.normalizeText(data), "version 2")) { |
| 558 | return .{ .request = request }; | 572 | return .{ .request = request }; |
| 559 | } else { | 573 | } else { |
| 560 | state = .response_content; | 574 | state = .response_content; |
| ... | @@ -573,6 +587,13 @@ pub const Session = struct { | ... | @@ -573,6 +587,13 @@ pub const Session = struct { |
| 573 | const Capability = struct { | 587 | const Capability = struct { |
| 574 | key: []const u8, | 588 | key: []const u8, |
| 575 | value: ?[]const u8 = null, | 589 | value: ?[]const u8 = null, |
| | 590 | |
| | 591 | fn parse(data: []const u8) Capability { |
| | 592 | return if (mem.indexOfScalar(u8, data, '=')) |separator_pos| |
| | 593 | .{ .key = data[0..separator_pos], .value = data[separator_pos + 1 ..] } |
| | 594 | else |
| | 595 | .{ .key = data }; |
| | 596 | } |
| 576 | }; | 597 | }; |
| 577 | | 598 | |
| 578 | fn deinit(iterator: *CapabilityIterator) void { | 599 | fn deinit(iterator: *CapabilityIterator) void { |
| ... | @@ -583,13 +604,7 @@ pub const Session = struct { | ... | @@ -583,13 +604,7 @@ pub const Session = struct { |
| 583 | fn next(iterator: *CapabilityIterator) !?Capability { | 604 | fn next(iterator: *CapabilityIterator) !?Capability { |
| 584 | switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { | 605 | switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { |
| 585 | .flush => return null, | 606 | .flush => return null, |
| 586 | .data => |data| if (data.len > 0 and data[data.len - 1] == '\n') { | 607 | .data => |data| return Capability.parse(Packet.normalizeText(data)), |
| 587 | if (mem.indexOfScalar(u8, data, '=')) |separator_pos| { | | |
| 588 | return .{ .key = data[0..separator_pos], .value = data[separator_pos + 1 .. data.len - 1] }; | | |
| 589 | } else { | | |
| 590 | return .{ .key = data[0 .. data.len - 1] }; | | |
| 591 | } | | |
| 592 | } else return error.UnexpectedPacket, | | |
| 593 | else => return error.UnexpectedPacket, | 608 | else => return error.UnexpectedPacket, |
| 594 | } | 609 | } |
| 595 | } | 610 | } |
| ... | @@ -676,18 +691,19 @@ pub const Session = struct { | ... | @@ -676,18 +691,19 @@ pub const Session = struct { |
| 676 | switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { | 691 | switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { |
| 677 | .flush => return null, | 692 | .flush => return null, |
| 678 | .data => |data| { | 693 | .data => |data| { |
| 679 | const oid_sep_pos = mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidRefPacket; | 694 | const ref_data = Packet.normalizeText(data); |
| | 695 | const oid_sep_pos = mem.indexOfScalar(u8, ref_data, ' ') orelse return error.InvalidRefPacket; |
| 680 | const oid = parseOid(data[0..oid_sep_pos]) catch return error.InvalidRefPacket; | 696 | const oid = parseOid(data[0..oid_sep_pos]) catch return error.InvalidRefPacket; |
| 681 | | 697 | |
| 682 | const name_sep_pos = mem.indexOfAnyPos(u8, data, oid_sep_pos + 1, " \n") orelse return error.InvalidRefPacket; | 698 | const name_sep_pos = mem.indexOfScalarPos(u8, ref_data, oid_sep_pos + 1, ' ') orelse ref_data.len; |
| 683 | const name = data[oid_sep_pos + 1 .. name_sep_pos]; | 699 | const name = ref_data[oid_sep_pos + 1 .. name_sep_pos]; |
| 684 | | 700 | |
| 685 | var symref_target: ?[]const u8 = null; | 701 | var symref_target: ?[]const u8 = null; |
| 686 | var peeled: ?Oid = null; | 702 | var peeled: ?Oid = null; |
| 687 | var last_sep_pos = name_sep_pos; | 703 | var last_sep_pos = name_sep_pos; |
| 688 | while (data[last_sep_pos] == ' ') { | 704 | while (last_sep_pos < ref_data.len) { |
| 689 | const next_sep_pos = mem.indexOfAnyPos(u8, data, last_sep_pos + 1, " \n") orelse return error.InvalidRefPacket; | 705 | const next_sep_pos = mem.indexOfScalarPos(u8, ref_data, last_sep_pos + 1, ' ') orelse ref_data.len; |
| 690 | const attribute = data[last_sep_pos + 1 .. next_sep_pos]; | 706 | const attribute = ref_data[last_sep_pos + 1 .. next_sep_pos]; |
| 691 | if (mem.startsWith(u8, attribute, "symref-target:")) { | 707 | if (mem.startsWith(u8, attribute, "symref-target:")) { |
| 692 | symref_target = attribute["symref-target:".len..]; | 708 | symref_target = attribute["symref-target:".len..]; |
| 693 | } else if (mem.startsWith(u8, attribute, "peeled:")) { | 709 | } else if (mem.startsWith(u8, attribute, "peeled:")) { |
| ... | @@ -762,7 +778,7 @@ pub const Session = struct { | ... | @@ -762,7 +778,7 @@ pub const Session = struct { |
| 762 | const packet = try Packet.read(reader, &buf); | 778 | const packet = try Packet.read(reader, &buf); |
| 763 | switch (state) { | 779 | switch (state) { |
| 764 | .section_start => switch (packet) { | 780 | .section_start => switch (packet) { |
| 765 | .data => |data| if (mem.eql(u8, data, "packfile\n")) { | 781 | .data => |data| if (mem.eql(u8, Packet.normalizeText(data), "packfile")) { |
| 766 | return .{ .request = request }; | 782 | return .{ .request = request }; |
| 767 | } else { | 783 | } else { |
| 768 | state = .section_content; | 784 | state = .section_content; |
| ... | @@ -1462,5 +1478,11 @@ pub fn main() !void { | ... | @@ -1462,5 +1478,11 @@ pub fn main() !void { |
| 1462 | std.debug.print("Starting checkout...\n", .{}); | 1478 | std.debug.print("Starting checkout...\n", .{}); |
| 1463 | var repository = try Repository.init(allocator, pack_file, index_file); | 1479 | var repository = try Repository.init(allocator, pack_file, index_file); |
| 1464 | defer repository.deinit(); | 1480 | defer repository.deinit(); |
| 1465 | try repository.checkout(worktree, commit); | 1481 | var diagnostics: Diagnostics = .{ .allocator = allocator }; |
| | 1482 | defer diagnostics.deinit(); |
| | 1483 | try repository.checkout(worktree, commit, &diagnostics); |
| | 1484 | |
| | 1485 | for (diagnostics.errors.items) |err| { |
| | 1486 | std.debug.print("Diagnostic: {}\n", .{err}); |
| | 1487 | } |
| 1466 | } | 1488 | } |