bool TryResolveConflict(ChangeGraphBuilder changeGraphBuilder, IMultiFileSystemHistoryService historyService, ConflictInfo conflict, out IFileReference resolved) { var graph = changeGraphBuilder.GetChangeGraphs(GetDiff(historyService, conflict)).Single(); var sinks = graph.GetSinks().ToArray(); return TryResolveConflict(sinks, out resolved); }
public void T04_Items_returns_expected_conflicts() { var expected = new ConflictInfo("/file1", "id"); m_Service.AddItems(expected); Assert.Single(m_Service.Items); var actual = m_Service.Items.Single(); Assert.Equal(expected.FilePath, actual.FilePath); Assert.Equal(expected.SnapshotId, actual.SnapshotId); }
public void T08_Indexer_throws_ItemNotFoundException_if_item_does_not_exist() { var conflict = new ConflictInfo("/file1", null); m_Service.AddItems(conflict); Assert.Throws<ItemNotFoundException>(() => m_Service["/some/other/path"]); }
public void T13_ItemExists_returns_the_expected_result() { var conflict = new ConflictInfo("/some/file/path", null); m_Service.AddItems(conflict); Assert.True(m_Service.ItemExists("/SOME/file/Path")); Assert.False(m_Service.ItemExists("/another/path")); }
public void T11_RemoveItems_removes_the_specified_items() { var commitCount = m_Group.Repository.GetAllCommits().Count(); var conflict1 = new ConflictInfo("/file1", null); var conflict2 = new ConflictInfo("/file2", null); Assert.Empty(m_Service.Items); m_Service.AddItems(conflict1, conflict2); Assert.NotEmpty(m_Service.Items); m_Service.RemoveItems(conflict1, conflict2); Assert.Empty(m_Service.Items); Assert.Equal(commitCount + 2, m_Group.Repository.GetAllCommits().Count()); }
public void T10_RemoveItems_throws_ItemNotFoundException_if_conflict_does_not_exist() { var conflict = new ConflictInfo("/file1", null); Assert.Throws<ItemNotFoundException>(() => m_Service.RemoveItems(conflict)); }
public void T09_AddItems_properly_stores_added_conflicts() { var conflict1 = new ConflictInfo("/file1", null); var conflict2 = new ConflictInfo("/file2", null); var commitCount = m_Group.Repository.GetAllCommits().Count(); m_Service.AddItems(conflict1, conflict2); Assert.Equal(commitCount +1 , m_Group.Repository.GetAllCommits().Count()); var newServiceInstance = new GitConflictService(m_Group); Assert.Equal(2, newServiceInstance.Items.Count()); }
public void T08_AddItems_throws_DuplicateItemException_if_conflict_has_already_been_added() { var conflict = new ConflictInfo("/file1", null); m_Service.AddItems(conflict); Assert.Throws<DuplicateItemException>(() => m_Service.AddItems(conflict)); }
public void T09_Indexer_returns_expected_result() { var expected = new ConflictInfo("/file1", null); m_Service.AddItems(expected); var actual = m_Service[expected.FilePath]; Assert.NotNull(actual); Assert.Equal(expected.FilePath,actual.FilePath); Assert.Equal(expected.SnapshotId, actual.SnapshotId); }
IMultiFileSystemDiff GetDiff(IMultiFileSystemHistoryService historyService, ConflictInfo conflict) { return historyService.GetChanges(conflict.SnapshotId, historyService.LatestSnapshot.Id, new[] {conflict.FilePath}); }
public void Synchronize(IGroup group) { var syncFolders = group.GetConfigurationService().Items.ToArray(); // there need to be at least 2 sync folders, otherwise, syncing makes no sense if (syncFolders.Length < 2) { return; } var historyService = group.GetService<IMultiFileSystemHistoryService>(); historyService.CreateSnapshot(); // we cannot sync if there isn't at least one snapshot if(!historyService.Snapshots.Any()) { return; } // we cannot sync if there is not at least one snapshot per history if (historyService.LatestSnapshot.HistoryNames.Any(name => historyService.LatestSnapshot.GetSnapshot(name) == null)) { return; } // get required services var syncPointService = group.GetSyncPointService(); var conflictService = group.GetSyncConflictService(); var syncActionService = group.GetSyncActionService(); var filter = syncFolders.ToMultiFileSystemChangeFilter(m_FilterFactory); var latestSyncPoint = syncPointService.LatestSyncPoint; var diff = GetDiff(historyService, latestSyncPoint, filter); var wasReset = ResetSyncStateIfNecessary(group, diff); if (wasReset) { diff = GetDiff(historyService, latestSyncPoint, filter); latestSyncPoint = syncPointService.LatestSyncPoint; } // for all folders, get the changes since the last sync var newSyncPoint = new MutableSyncPoint() { Id = GetNextSyncPointId(syncPointService.LatestSyncPoint), MultiFileSystemSnapshotId = diff.ToSnapshot.Id, FilterConfigurations = syncFolders.ToDictionary(f => f.Name, f => f.Filter) }; var syncStateUpdater = new SyncActionUpdateBuilder(); var changeGraphBuilder = new ChangeGraphBuilder(m_FileReferenceComparer); foreach (var graph in changeGraphBuilder.GetChangeGraphs(diff)) { var path = graph.ValueNodes.First(node => node.Value != null).Value.Path; // skip if there is a conflict for the current file if (conflictService.ItemExists(path)) { continue; } // check if all pending sync actions can be applied to the change graph var unapplicaleSyncActions = GetUnapplicableSyncActions(graph, syncActionService[path].Where(IsPendingSyncAction)); // pending sync actions could not be applied => skip file if (unapplicaleSyncActions.Any()) { // cancel unapplicable actions syncStateUpdater.UpdateSyncActions(unapplicaleSyncActions.Select(a => a.WithState(SyncActionState.Cancelled))); // add a conflict for the file (the snapshot id of the conflict can be determined from the oldest unapplicable sync action) var oldestSyncPointId = unapplicaleSyncActions.Min(a => a.SyncPointId); var snapshotId = oldestSyncPointId > 1 ? syncPointService[oldestSyncPointId - 1].MultiFileSystemSnapshotId : null; syncStateUpdater.AddConflict(new ConflictInfo(unapplicaleSyncActions.First().Path, snapshotId)); continue; } //in the change graph, detect conflicts // if there is only one sink, no conflicts exist var acylicGraph = graph.ToAcyclicGraph(); var sinks = acylicGraph.GetSinks().ToArray(); if (!sinks.Any()) { // not possible (in this case the graph would be empty, which cannot happen) throw new InvalidOperationException(); } if (sinks.Length == 1) { // no conflict => generate sync actions, to replace the outdated file versions or add the file to a target var sink = sinks.Single(); //TODO: Use C# 7 tuples after it was released IEnumerable<Tuple<string, IFile>> currentFiles = diff.ToSnapshot.GetFiles(path); foreach (var historyFileTuple in currentFiles) { var targetSyncFolderName = historyFileTuple.Item1; var currentVersion = historyFileTuple.Item2; var syncAction = m_SyncActionFactory.GetSyncAction(targetSyncFolderName, newSyncPoint.Id, currentVersion?.ToReference(), sink); if (syncAction != null && filter.GetFilter(targetSyncFolderName).IncludeInResult(syncAction)) { syncStateUpdater.AddSyncAction(syncAction); } } } else { // multiple sinks in the change graph => conflict // if there are pending actions for this file, they need to be cancelled var pendingSyncActions = syncActionService[path] .Where(IsPendingSyncAction) .ToArray(); if (pendingSyncActions.Any()) { //cancel actions syncStateUpdater.UpdateSyncActions(pendingSyncActions.Select(a => a.WithState(SyncActionState.Cancelled))); //determine the oldest sync action to determine the snapshot ids for the conflict var syncPointId = pendingSyncActions.Min(x => x.SyncPointId); var snapshotId = syncPointId > 1 ? syncPointService[syncPointId - 1].MultiFileSystemSnapshotId : null; // generate conflict; syncStateUpdater.AddConflict(new ConflictInfo(path, snapshotId)); } else { // no pending action => the snapshot ids for the conflict are the start snapshots of the current sync // generate conflict var conflictInfo = new ConflictInfo(path, diff.FromSnapshot?.Id); syncStateUpdater.AddConflict(conflictInfo); } } } // save actions, conflicts and sync point syncPointService.AddItem(newSyncPoint); syncStateUpdater.Apply(syncActionService, conflictService); }