public void Process(ExecutionContext context) { CmdStore cs = context.Store; StoreInfo storeInfo = cs.Store; string upLoc = storeInfo.UpstreamLocation; if (String.IsNullOrEmpty(upLoc)) { throw new ApplicationException("There is no upstream location"); } // Collate the number of commands we have in local branches IdCount[] have = cs.Branches.Values .Where(x => !x.IsRemote) .Select(x => new IdCount(x.Id, x.Info.CommandCount)) .ToArray(); IRemoteStore rs = context.GetRemoteStore(upLoc); // We don't care about new branches in the origin, but we do care // about local branches that have been created since the last push IdRange[] toPush = rs.GetMissingRanges(cs.Id, have, false).ToArray(); // How many commands do we need to push uint total = (uint)toPush.Sum(x => x.Size); Log.Info($"To push {total} command`s in {toPush.Length} branch`es".TrimExtras()); foreach (IdRange idr in toPush) { Branch b = cs.FindBranch(idr.Id); if (b == null) { throw new ApplicationException("Cannot locate branch " + idr.Id); } Log.Info($"Push [{idr.Min},{idr.Max}] from {b}"); CmdData[] data = b.TakeRange(idr.Min, idr.Max).ToArray(); rs.Push(cs.Name, b.Info, data); // Remember how much we were ahead at the time of the push b.Info.LastPush = idr.Max + 1; b.Store.SaveBranchInfo(b); } Log.Info("Push completed"); }
public void Process(ExecutionContext context) { CmdStore cs = context.Store; StoreInfo storeInfo = cs.Store; string upLoc = storeInfo.UpstreamLocation; if (String.IsNullOrEmpty(upLoc)) { throw new ApplicationException("There is no upstream location"); } // Collate how much we already have in all remote branches. // We may have previously pushed some local branches to the remote // store, but nothing is supposed to mutate those remote copies. IdCount[] have = cs.Branches.Values .Where(x => x.IsRemote) .Select(x => new IdCount(x.Id, x.Info.CommandCount)) .ToArray(); // Open a channel to the upstream store IRemoteStore rs = context.GetRemoteStore(upLoc); // Determine what we are missing (including any new branches in the remote) IdRange[] toFetch = rs.GetMissingRanges(cs.Id, have, true); // How many commands do we need to fetch uint total = (uint)toFetch.Sum(x => x.Size); Log.Info($"To fetch {total} command`s from {toFetch.Length} branch`es".TrimExtras()); // Retrieve the command data from the remote, keeping new branches // apart from appends to existing branches. var newBranchData = new Dictionary <BranchInfo, CmdData[]>(); var moreBranchData = new Dictionary <BranchInfo, CmdData[]>(); foreach (IdRange idr in toFetch) { // Fetch the remote AC file BranchInfo ac = rs.GetBranchInfo(idr.Id); if (ac == null) { throw new ApplicationException("Could not locate remote branch " + idr.Id); } // And the relevant data Log.Info($"Fetch [{idr.Min},{idr.Max}] for {ac.BranchName} ({ac.BranchId})"); CmdData[] branchData = rs.GetData(idr).ToArray(); if (cs.FindBranch(ac.BranchId) == null) { newBranchData.Add(ac, branchData); } else { moreBranchData.Add(ac, branchData); } } // All done with the remote store // Copy any brand new branches (ensuring they get created in the // right order so that parent/child relationships can be formed // as we go). foreach (KeyValuePair <BranchInfo, CmdData[]> kvp in newBranchData.OrderBy(x => x.Key.CreatedAt)) { cs.CopyIn(kvp.Key, kvp.Value); } // Append command data for branches we previously had (the order // shouldn't matter) foreach (KeyValuePair <BranchInfo, CmdData[]> kvp in moreBranchData) { cs.CopyIn(kvp.Key, kvp.Value); } Log.Info("Fetch completed"); // Reload the current command stream (from scratch, kind of brute force, // not sure whether appending the new commands would really be sufficient) // TODO: Is this really necessary? Perhaps only if the current branch has // been fetched (the stuff we're fetching should only come from remotes, // but the current branch could be one of those remotes) //cs.Stream = new CmdStream(cs.Current); }
public void Process(ExecutionContext context) { Guid sourceId = (Input as IMerge).FromId; CmdStore cs = context.Store; Branch target = cs.Current; Branch source = cs.FindBranch(sourceId); if (source == null) { throw new ApplicationException($"Cannot locate branch {sourceId}"); } // Merges can only be done between branches that are part // of the local store (fetches from remote stores should // involve a totally different set of branches) //if (target.Info.StoreId != source.Info.StoreId) // throw new NotSupportedException("Attempt to merge with a remote store"); // Determine the first command that needs to be merged from // the source branch. The source branch doesn't know anything // about merges that have already been done, so figuring this // out involves scanning back from the end of the target branch // to locate the last merge (if any). // How far we go back depends on whether we're merging from // a child branch to its parent, or vice versa. If the target // is the child we go back as far as command 0. If the target // is the parent, we go back as far the command that followed // the branch start point. bool targetIsParent = target.Id.Equals(source.Info.ParentId); // Define the range of commands to be merged from the source uint minCmd = 0; if (targetIsParent) { // Merging into the parent, so start from the command immediately // after the last command that was previously merged (or 0 if this // is the first time the parent has merged from the child). if (target.Info.LastMerge.TryGetValue(sourceId, out MergeInfo mi)) { minCmd = mi.ChildCount; } } else { // Merging from the parent into the child, so start from the command // immediately after the last command that was previously merged (it // could potentially be 0) minCmd = target.Info.RefreshCount; } uint maxCmd = (uint)source.Info.CommandCount - 1; // TODO: Round about here we need to actually include the new stuff // as part of the current stream. Replaying the commands may then // lead to some sort of conflict (things are not guaranteed to work), // so there needs to be some way to preview the results. // Write the command data Input.Add(nameof(IMerge.MinCmd), minCmd); Input.Add(nameof(IMerge.MaxCmd), maxCmd); target.SaveData(Input); }