Skip to content

Commit 50ae7e9

Browse files
committed
Merge branch 'pack-protocol' into plastic
2 parents 338b4ec + 0fabb9a commit 50ae7e9

File tree

11 files changed

+422
-0
lines changed

11 files changed

+422
-0
lines changed

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<Compile Include="TreeDefinitionFixture.cs" />
8989
<Compile Include="TreeFixture.cs" />
9090
<Compile Include="TupleFixture.cs" />
91+
<Compile Include="RefspecFixture.cs" />
9192
</ItemGroup>
9293
<ItemGroup>
9394
<ProjectReference Include="..\LibGit2Sharp\LibGit2Sharp.csproj">

LibGit2Sharp.Tests/RefspecFixture.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Linq;
3+
using LibGit2Sharp.Tests.TestHelpers;
4+
using Xunit;
5+
using Xunit.Extensions;
6+
7+
namespace LibGit2Sharp.Tests
8+
{
9+
public class RefspecFixture
10+
{
11+
[Theory]
12+
[InlineData("refs/heads/master:refs/remotes/origin/master", false, "refs/heads/master", "refs/remotes/origin/master")]
13+
[InlineData("+refs/heads/master:refs/remotes/origin/master", true, "refs/heads/master", "refs/remotes/origin/master")]
14+
public void CanParseRefspecs(string spec, bool force, string left, string right)
15+
{
16+
var refspec = new Refspec(spec);
17+
Assert.True(refspec.Src.Equals(left));
18+
Assert.True(refspec.Dst.Equals(right));
19+
Assert.True(refspec.Force == force);
20+
}
21+
22+
23+
[Fact]
24+
public void CanMatchNonWildcards()
25+
{
26+
var refspec = new Refspec("refs/heads/master:refs/heads/master");
27+
Assert.True(refspec.Matches("refs/heads/master"));
28+
}
29+
30+
[Theory]
31+
[InlineData("refs/heads/master")]
32+
[InlineData("refs/heads/development")]
33+
[InlineData("refs/heads/some/branch")]
34+
public void CanMatchWildcards(string refname)
35+
{
36+
var refspec = new Refspec("refs/heads/*");
37+
Assert.True(refspec.Matches(refname));
38+
}
39+
}
40+
}
41+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace LibGit2Sharp
5+
{
6+
public static class StreamExtensions
7+
{
8+
public static void CopyTo(this Stream from, Stream to)
9+
{
10+
int len = Math.Min(65535, (int)(from.Length - from.Position));
11+
var buff = new byte[len];
12+
int n;
13+
do {
14+
n = from.Read(buff, 0, len);
15+
to.Write(buff, 0, n);
16+
} while (n != 0);
17+
}
18+
}
19+
}

LibGit2Sharp/GitProtocolException.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
namespace LibGit2Sharp
3+
{
4+
public class GitProtocolException : Exception
5+
{
6+
public GitProtocolException()
7+
{
8+
}
9+
10+
public GitProtocolException(string message)
11+
: base(message)
12+
{
13+
}
14+
15+
public GitProtocolException(string message, Exception innerException)
16+
: base(message, innerException)
17+
{
18+
}
19+
}
20+
}
21+

LibGit2Sharp/GitTransport.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.IO;
3+
using System.Net.Sockets;
4+
using System.Collections.Generic;
5+
using System.Text;
6+
7+
namespace LibGit2Sharp
8+
{
9+
public class GitTransport : ITransport
10+
{
11+
Uri uri = null;
12+
bool connected;
13+
Stream stream;
14+
15+
public GitTransport(string url)
16+
{
17+
var uribld = new UriBuilder(url);
18+
if (!uribld.Scheme.Equals("git"))
19+
throw new ArgumentException("This transport is only valid for git://");
20+
21+
if (uribld.Port == -1)
22+
uribld.Port = 9418;
23+
24+
uri = uribld.Uri;
25+
connected = false;
26+
}
27+
28+
byte[] GenRequest(Uri uri)
29+
{
30+
var tmp = string.Format("git-upload-pack {0}\0host={1}\0", uri.AbsolutePath, uri.Host);
31+
return Encoding.UTF8.GetBytes(string.Format("{0:x4}{1}", tmp.Length, tmp));
32+
}
33+
// Connect to the remote host asking for the specified repository
34+
public void Connect()
35+
{
36+
var req = GenRequest(uri);
37+
var tcp = new TcpClient(uri.Host, uri.Port);
38+
stream = tcp.GetStream();
39+
stream.Write(req, 0, req.Length);
40+
connected = true;
41+
}
42+
43+
public Stream GetStream()
44+
{
45+
return stream;
46+
}
47+
48+
public bool Connected()
49+
{
50+
return connected;
51+
}
52+
53+
public void NegotiationStep(Stream stream)
54+
{
55+
/* The raw protocol doesn't need anything else */
56+
stream.CopyTo(this.stream);
57+
}
58+
}
59+
}
60+

LibGit2Sharp/ITransport.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.IO;
3+
using System.Collections.Generic;
4+
5+
namespace LibGit2Sharp
6+
{
7+
public interface ITransport
8+
{
9+
void Connect();
10+
bool Connected();
11+
Stream GetStream();
12+
/// <summary>
13+
/// Send the contents of <param name="stream"> as part of a negotiation
14+
/// step. This is necessary because some transports (notably the smart
15+
/// HTTP transport) needs to send data in packets instead of a stream,
16+
/// so each negotiation step needs to be handled on its own.
17+
/// </summary>
18+
void NegotiationStep(Stream stream);
19+
}
20+
}
21+

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@
164164
<Compile Include="Core\Handles\StreamingIndexerSafeHandle.cs" />
165165
<Compile Include="Indexer.cs" />
166166
<Compile Include="ObjectDatabaseExtensions.cs" />
167+
<Compile Include="PktParser.cs" />
168+
<Compile Include="Pkt.cs" />
169+
<Compile Include="ITransport.cs" />
170+
<Compile Include="GitTransport.cs" />
171+
<Compile Include="Refspec.cs" />
172+
<Compile Include="GitProtocolException.cs" />
173+
<Compile Include="Core\Compat\StreamExtensions.cs" />
167174
</ItemGroup>
168175
<ItemGroup>
169176
<CodeAnalysisDictionary Include="CustomDictionary.xml" />

LibGit2Sharp/Pkt.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
5+
namespace LibGit2Sharp
6+
{
7+
public class Pkt
8+
{
9+
static public Pkt Parse(BinaryReader reader)
10+
{
11+
var buf = reader.ReadBytes(4);
12+
int len = Int32.Parse(Encoding.ASCII.GetString(buf), System.Globalization.NumberStyles.HexNumber);
13+
if (len == 0)
14+
return new PktFlush();
15+
16+
len -= 4;
17+
18+
return NewPkt(reader, len);
19+
}
20+
21+
/*
22+
* We can't seek on a NetworkStream, which means that we can't use
23+
* reader.PeekChar() to see if we got an error (which starts with
24+
* "ERR " instead of a hash) so we need this new function that chooses
25+
* which type to create
26+
*/
27+
public static Pkt NewPkt(BinaryReader reader, int len)
28+
{
29+
var pref = Encoding.ASCII.GetString(reader.ReadBytes(3));
30+
len -= 3;
31+
if (pref.Equals("ERR")) {
32+
return new PktError(reader, len);
33+
} else if (pref.Equals("NAK")) {
34+
return new PktNak(reader, len);
35+
} else if (pref.Equals("ACK")) {
36+
return new PktAck(reader, len);
37+
}
38+
39+
return new PktRef(reader, len, pref);
40+
}
41+
}
42+
43+
44+
public class PktRef : Pkt
45+
{
46+
public readonly ObjectId Id;
47+
public readonly string Name;
48+
public readonly string CapString;
49+
50+
public PktRef(BinaryReader reader, int len, string pre)
51+
{
52+
int toread = 40 - pre.Length;
53+
var hash = Encoding.ASCII.GetString(reader.ReadBytes(toread));
54+
len -= toread; /* The hash */
55+
ObjectId.TryParse(pre + hash, out Id);
56+
reader.ReadByte(); /* Skip the SP */
57+
len -= 1; /* The SP */
58+
59+
/* Each line SHOULD end with LF, but unfortunately we can't rely on it being there */
60+
var tmp = Encoding.UTF8.GetString(reader.ReadBytes(len));
61+
if (tmp.EndsWith("\n"))
62+
tmp = tmp.Remove(tmp.Length - 1);
63+
64+
/*
65+
* The server's capabilities are sent as "HEAD" NUL "cap1 cap2". In
66+
* C it works just fine, but C# knows that the string doesn't stop
67+
* at the first NUL, so we need to split it out explicitly.
68+
*/
69+
var nul = tmp.IndexOf('\0');
70+
if (nul == -1) {
71+
Name = tmp;
72+
} else {
73+
var parts = tmp.Split('\0');
74+
Name = parts[0];
75+
CapString = parts[1];
76+
}
77+
}
78+
}
79+
80+
public class PktError : Pkt
81+
{
82+
public readonly string Error;
83+
84+
public PktError(BinaryReader reader, int len)
85+
{
86+
/* NewPkt() has already removed "ERR" from the stream, remove SP */
87+
reader.ReadByte();
88+
Error = Encoding.UTF8.GetString(reader.ReadBytes(len));
89+
}
90+
}
91+
92+
public class PktAck : Pkt
93+
{
94+
public PktAck(BinaryReader reader, int len)
95+
{
96+
reader.ReadBytes(len);
97+
}
98+
}
99+
100+
public class PktNak : Pkt
101+
{
102+
public PktNak(BinaryReader reader, int len)
103+
{
104+
reader.ReadBytes(len);
105+
}
106+
}
107+
108+
public class PktFlush : Pkt
109+
{
110+
}
111+
}
112+

LibGit2Sharp/PktParser.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
5+
namespace LibGit2Sharp
6+
{
7+
public class PktParser
8+
{
9+
private BinaryReader reader;
10+
public PktParser(Stream stream) : this(new BinaryReader(stream))
11+
{
12+
}
13+
14+
public PktParser(BinaryReader reader)
15+
{
16+
this.reader = reader;
17+
}
18+
19+
20+
public Pkt Next()
21+
{
22+
return Pkt.Parse(reader);
23+
}
24+
}
25+
}
26+

LibGit2Sharp/Refspec.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
namespace LibGit2Sharp
3+
{
4+
public class Refspec
5+
{
6+
public string Src { get; private set; }
7+
public string Dst { get; private set; }
8+
public bool Force { get; private set; }
9+
bool wildcard;
10+
11+
public Refspec(string str)
12+
{
13+
if (string.IsNullOrEmpty(str))
14+
{
15+
Src = "";
16+
Dst = "";
17+
return;
18+
}
19+
20+
if (str[0] == '+') {
21+
Force = true;
22+
str = str.Remove(0, 1);
23+
} else {
24+
Force = false;
25+
}
26+
27+
var parts = str.Split(':');
28+
Src = parts[0];
29+
wildcard = Src.EndsWith("*");
30+
if (parts.Length > 1) {
31+
Dst = parts[1];
32+
if (wildcard && !Dst.EndsWith("*"))
33+
throw new ArgumentException("Left side has wildcard, but right one doesn't");
34+
if (!wildcard && Dst.EndsWith("*"))
35+
throw new ArgumentException("Right side has wildcard, but left side doesn't");
36+
}
37+
}
38+
39+
public bool Matches(string str)
40+
{
41+
/* If it's not a wildcard, then it's a straight string match */
42+
if (!wildcard)
43+
return Src.Equals(str);
44+
45+
var prefix = Src.Remove(Src.Length - 1);
46+
return str.StartsWith(prefix);
47+
}
48+
}
49+
}
50+

0 commit comments

Comments
 (0)