private static bool Fetch(Repository aRepository, Uri aUri, BinaryWriter aWriter, BinaryReader aReader)
{
// Request
WriteFetchRequest(aUri, aWriter);
// Collect HEAD and capabilities
string sha1;
byte[] bytes = ReadFetchHeader(aReader, out sha1);
if (bytes.Length < 5 || bytes[4] != 0)
{
return (false);
}
string head = ASCIIEncoding.ASCII.GetString(bytes, 0, 4);
if (head != "HEAD")
{
return (false);
}
string capabilities = ASCIIEncoding.ASCII.GetString(bytes, 5, bytes.Length - 5);
if (capabilities.EndsWith("\n"))
{
capabilities = capabilities.Substring(0, capabilities.Length - 1);
}
List<string> caps = new List<string>(capabilities.Split(new char[] { ' ' }));
if (!caps.Contains("ofs-delta"))
{
return (false);
}
// Collect remote branches
Dictionary<string, string> branches = new Dictionary<string, string>();
while (true)
{
bytes = ReadFetchHeader(aReader, out sha1);
if (bytes == null)
{
break;
}
string branch = ASCIIEncoding.ASCII.GetString(bytes);
if (branch.StartsWith("refs/heads/"))
{
branch = branch.Substring(11);
if (branch.EndsWith("\n"))
{
branch = branch.Substring(0, branch.Length - 1);
}
if (branch.Length > 0)
{
branches.Add(branch, sha1);
}
}
}
// Find branches that are different
Dictionary<string, string> update = new Dictionary<string, string>();
Dictionary<string, string> create = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> entry in branches)
{
var branch = aRepository.Branch(entry.Key);
if (branch != null)
{
if (branch.IsEmpty || branch.Commit.Id != entry.Value)
{
update.Add(entry.Key, entry.Value);
}
}
else
{
create.Add(entry.Key, entry.Value);
}
}
if (update.Count + create.Count == 0)
{
return (false);
}
// Write request
capabilities = "\0ofs-delta";
if (caps.Contains("thin-pack"))
{
capabilities += " thin-pack";
}
foreach (string entry in update.Values)
{
string want = "want " + entry + capabilities + "\n";
WriteFetchHeader(aWriter, want);
capabilities = String.Empty;
}
foreach (string entry in create.Values)
{
string want = "want " + entry + capabilities + "\n";
WriteFetchHeader(aWriter, want);
capabilities = String.Empty;
}
WriteZeroHeader(aWriter);
foreach (IBranch branch in aRepository.Branches)
{
if (!branch.IsEmpty)
{
string have = "have " + branch.Commit.Id + "\n";
WriteFetchHeader(aWriter, have);
}
}
WriteFetchHeader(aWriter, "done\n");
// Read acks
foreach (string entry in create.Values)
{
string ack = ReadFetchRecord(aReader);
//Console.WriteLine("CREATE: {0}", ack);
}
foreach (string entry in update.Values)
{
string ack = ReadFetchRecord(aReader);
//Console.WriteLine("UPDATE: {0}", ack);
}
/*
foreach (IBranch branch in aRepository.Branches)
{
string ack = ReadFetchRecord(reader);
if (!branch.IsEmpty)
{
if (!ack.StartsWith("ACK "))
{
return (false);
}
}
else
{
if (ack !="NAK\n")
{
return (false);
}
}
}
*/
// Collect pack parts
int count = 0;
List<byte[]> parts = new List<byte[]>();
while (true)
{
byte[] part = aReader.ReadBytes(10000);
if (part.Length == 0)
{
break;
}
count += part.Length;
parts.Add(part);
}
// Combine pack parts
byte[] pack = new byte[count];
int index = 0;
foreach (byte[] part in parts)
{
Array.Copy(part, 0, pack, index, part.Length);
index += part.Length;
}
// Read pack header
var pstream = new MemoryStream(pack);
using (var preader = new BinaryReader(pstream))
{
Pack.ReadSignature(preader);
Pack.ReadVersion(preader);
uint objectcount = Pack.ReadItemCount(preader);
// Inflate items
Dictionary<long, Object> objects = new Dictionary<long, Object>();
while (objectcount-- > 0)
{
Object obj;
long length;
long start = pstream.Position;
int type = Pack.ReadItemTypeAndLength(preader, out length);
switch (type)
{
case 0:
case 5:
return (false);
case 6:
long offset = start - Pack.ReadDeltaOffset(preader);
obj = Pack.ApplyDelta(preader, objects[offset], length);
break;
case 7:
byte[] id = new byte[20];
preader.Read(id, 0, 20);
obj = Pack.ApplyDelta(preader, aRepository.GetObject(Hash.String(id)), length);
break;
default:
obj = Pack.ReadObject(preader, type, length);
break;
}
objects.Add(start, obj);
aRepository.WriteObject(obj.Contents, obj.Type);
}
}
// Update existing branches
foreach (var entry in update)
{
aRepository.UpdateBranch(entry.Key, entry.Value);
}
// Create new branches
foreach (var entry in create)
{
aRepository.CreateBranch(entry.Key, entry.Value);
}
return (true);
}