private bool PullVersions(SharedNetwork.SharedNetworkInfo sharedInfo, out bool receivedData)
{
bool importResult = sharedInfo.Workspace.RunLocked(() =>
{
return SharedNetwork.ImportRecords(sharedInfo, true);
}, false);
receivedData = false;
if (!importResult)
return false;
if (sharedInfo.PushedVersions.Count == 0 && sharedInfo.ReceivedBranchJournals.Count == 0 && sharedInfo.ReceivedBranches.Count == 0)
return true;
receivedData = true;
return sharedInfo.Workspace.RunLocked(() =>
{
lock (sharedInfo.Workspace)
{
try
{
sharedInfo.Workspace.BeginDatabaseTransaction();
if (!SharedNetwork.ImportBranchJournal(sharedInfo, true))
return false;
SharedNetwork.ImportBranches(sharedInfo);
Dictionary<Guid, List<Head>> temporaryHeads = new Dictionary<Guid, List<Head>>();
Dictionary<Guid, Guid> pendingMerges = new Dictionary<Guid, Guid>();
Dictionary<Guid, HashSet<Guid>> headAncestry = new Dictionary<Guid, HashSet<Guid>>();
HashSet<Guid> terminatedBranches = new HashSet<Guid>();
List<Guid> mergeResults = new List<Guid>();
foreach (var x in ((IEnumerable<VersionInfo>)sharedInfo.PushedVersions).Reverse())
{
if (terminatedBranches.Contains(x.Version.Branch))
continue;
List<Head> heads;
if (!temporaryHeads.TryGetValue(x.Version.Branch, out heads))
{
Branch branch = sharedInfo.Workspace.GetBranch(x.Version.Branch);
if (branch.Terminus.HasValue)
{
terminatedBranches.Add(branch.ID);
continue;
}
heads = new List<Head>();
var bheads = sharedInfo.Workspace.GetBranchHeads(branch);
if (bheads.Count == 0)
heads.Add(new Head() { Branch = branch.ID, Version = x.Version.ID });
else
{
foreach (var h in bheads)
heads.Add(h);
}
temporaryHeads[branch.ID] = heads;
}
mergeResults.Clear();
for (int i = 0; i < heads.Count; i++)
{
if (heads[i].Version != x.Version.ID)
{
HashSet<Guid> headAncestors = null;
if (!headAncestry.TryGetValue(heads[i].Version, out headAncestors))
{
headAncestors = SharedNetwork.GetAncestry(heads[i].Version, sharedInfo);
headAncestry[heads[i].Version] = headAncestors;
}
if (headAncestors.Contains(x.Version.ID))
{
// all best
mergeResults.Add(heads[i].Version);
}
else if (SharedNetwork.IsAncestor(heads[i].Version, x.Version.ID, sharedInfo))
{
mergeResults.Add(x.Version.ID);
}
else
{
mergeResults.Add(Guid.Empty);
}
}
}
pendingMerges[x.Version.Branch] = Guid.Empty;
// Remove any superceded heads
// Add a merge if required
bool unrelated = true;
for (int i = 0; i < mergeResults.Count; i++)
{
if (mergeResults[i] == Guid.Empty)
continue;
else if (mergeResults[i] != heads[i].Version)
{
headAncestry.Remove(heads[i].Version);
heads[i].Version = x.Version.ID;
unrelated = false;
}
else
unrelated = false;
}
if (unrelated)
{
heads.Add(new Head() { Branch = x.Version.Branch, Version = x.Version.ID });
}
for (int i = 0; i < heads.Count; i++)
{
for (int j = i + 1; j < heads.Count; j++)
{
if (heads[i].Version == heads[j].Version)
{
heads.RemoveAt(j);
--j;
}
}
}
}
List<Head> newHeads = new List<Head>();
List<VersionInfo> autoMerged = new List<VersionInfo>();
foreach (var x in pendingMerges)
{
Branch branch = sharedInfo.Workspace.GetBranch(x.Key);
List<Head> heads = temporaryHeads[x.Key];
var bheads = sharedInfo.Workspace.GetBranchHeads(branch);
bool headsChanged = bheads.Count != heads.Count;
if (!headsChanged)
{
for (int i = 0; i < bheads.Count; i++)
{
if (bheads[i].Version != heads[i].Version)
headsChanged = true;
}
}
if (!headsChanged)
{
temporaryHeads[x.Key] = null;
continue;
}
if (heads.Count == 1)
{
Printer.PrintDiagnostics("Uncontested head update for branch \"{0}\".", Workspace.GetBranch(x.Key).Name);
Printer.PrintDiagnostics(" - Head updated to {0}", temporaryHeads[x.Key][0].Version);
continue;
}
var localVersions = bheads.Where(h => heads.Any(y => y.Version != h.Version));
var remoteVersions = heads.Where(h => !bheads.Any(y => y.Version != h.Version));
if (localVersions.Count() != 1)
{
Printer.PrintDiagnostics("Too many heads in local branch to merge remote head. Please merge locally and try again to update branch \"{0}\".", Workspace.GetBranch(x.Key).Name);
return false;
}
Guid localVersion = localVersions.First().Version;
if (remoteVersions.Count() == 1)
{
VersionInfo result;
string error;
result = Workspace.MergeRemote(Workspace.GetLocalOrRemoteVersion(localVersion, sharedInfo), remoteVersions.First().Version, sharedInfo, out error, true);
Printer.PrintMessage("Resolved incoming merge for branch \"{0}\".", branch.Name);
Printer.PrintDiagnostics(" - Merge local input {0}", localVersion);
Printer.PrintDiagnostics(" - Merge remote input {0}", remoteVersions.First().Version);
Printer.PrintDiagnostics(" - Head updated to {0}", result.Version.ID);
for (int i = 0; i < heads.Count; i++)
{
if ((remoteVersions.Any() && heads[i].Version == remoteVersions.First().Version) || heads[i].Version == localVersion)
{
heads.RemoveAt(i);
--i;
}
}
heads.Add(new Head() { Branch = branch.ID, Version = result.Version.ID });
autoMerged.Add(result);
}
}
var versionsToImport = sharedInfo.PushedVersions.OrderBy(x => x.Version.Timestamp).ToArray();
if (versionsToImport.Length != 0)
{
Dictionary<Guid, bool> importList = new Dictionary<Guid, bool>();
foreach (var x in versionsToImport)
importList[x.Version.ID] = false;
int importCount = versionsToImport.Length;
var orderedImports = versionsToImport.OrderBy(x => x.Version.Revision).ToList();
Printer.InteractivePrinter printer = null;
Printer.PrintMessage("Importing #b#{0}## versions...", orderedImports.Count);
printer = Printer.CreateProgressBarPrinter("Importing", string.Empty,
(obj) =>
{
return string.Empty;
},
(obj) =>
{
return (100.0f * (int)(orderedImports.Count - importCount)) / (float)orderedImports.Count;
},
(pct, obj) =>
{
return string.Format("{0}/{1}", (int)(orderedImports.Count - importCount), orderedImports.Count);
},
60);
while (importCount > 0)
{
foreach (var x in orderedImports)
{
if (importList[x.Version.ID] != true)
{
bool accept;
if (!x.Version.Parent.HasValue || !importList.TryGetValue(x.Version.Parent.Value, out accept))
accept = true;
if (accept)
{
sharedInfo.Workspace.ImportVersionNoCommit(sharedInfo, x, true);
importList[x.Version.ID] = true;
importCount--;
printer.Update(importCount);
}
}
}
}
printer.End(importCount);
}
Printer.PrintMessage("Updating internal state...");
foreach (var x in autoMerged)
Workspace.ImportVersionNoCommit(sharedInfo, x, false);
foreach (var x in temporaryHeads)
{
if (x.Value != null)
Workspace.ReplaceHeads(x.Key, x.Value);
}
Workspace.CommitDatabaseTransaction();
sharedInfo.Workspace.CommitDatabaseTransaction();
return true;
}
catch
{
sharedInfo.Workspace.RollbackDatabaseTransaction();
throw;
}
}
}, false);
}