private void FlattenDeltaTable( List <string> outMappedWorkItemUpdates, Dictionary <string, List <MigrationAction> > outMappedAttachmentUpdates, bool getSourceSideCopyInMigrationInstructionTable) { Guid sourceId = getSourceSideCopyInMigrationInstructionTable ? SourceSystemId : TargetSystemId; Guid targetId = getSourceSideCopyInMigrationInstructionTable ? TargetSystemId : SourceSystemId; int pageNumber = 0; int pageSize = 50; IEnumerable <ChangeGroup> changeGroups = null; do { changeGroups = getSourceSideCopyInMigrationInstructionTable ? TargetChangeGroupService.NextMigrationInstructionTablePage(pageNumber++, pageSize, true, true) : TargetChangeGroupService.NextDeltaTablePage(pageNumber++, pageSize, true); foreach (ChangeGroup changeGroupEntry in changeGroups) { foreach (MigrationAction action in changeGroupEntry.Actions) { if (action.Action.Equals(WellKnownChangeActionId.SyncContext)) { continue; } else if (action.Action == WellKnownChangeActionId.Add) { // do nothing for adding item, be it created by sync engine or not continue; } else if (action.Action == WellKnownChangeActionId.Edit) { string sourceSideItemId = GetSourceWorkItemIdFromActionDescription(action.MigrationActionDescription); string targetWorkItemId = TryGetTargetWorkItemId( action.MigrationActionDescription, sourceId, getSourceSideCopyInMigrationInstructionTable); if (string.IsNullOrEmpty(targetWorkItemId)) { // the source work item has never been migrated yet continue; } if (getSourceSideCopyInMigrationInstructionTable) { if (ConflictManager.IsItemInBacklog(sourceId, targetId, sourceSideItemId)) { // previous revision of the work item has conflict, push this revision to backlog as well string conflictDetails = ChainOnBackloggedItemConflictType.CreateConflictDetails(sourceSideItemId, action.Version); string scopeHint = ChainOnBackloggedItemConflictType.CreateScopeHint(sourceSideItemId); if (changeGroupEntry.ReflectedChangeGroupId.HasValue) { // we reactivate the original delta entry and discard this migration instruction entry // and then backlog the delta entry, to preserve correct order of the revisions ChangeGroup deltaChangeGroupEntry = TargetChangeGroupService.ReactivateDeltaEntry(changeGroupEntry); if (null != deltaChangeGroupEntry) { MigrationConflict chainedConflict = new ChainOnBackloggedItemConflictType().CreateConflict( conflictDetails, scopeHint, deltaChangeGroupEntry.Actions.First()); ConflictManager.BacklogUnresolvedConflict(ConflictManager.SourceId, chainedConflict, false); break; } } // if we can't find the original delta entry successfully, we block this migration instruction entry MigrationConflict conflict = new ChainOnBackloggedItemConflictType().CreateConflict( conflictDetails, scopeHint, action); ConflictManager.BacklogUnresolvedConflict(ConflictManager.SourceId, conflict, false); if (!m_conflictedWorkItems[targetId].Contains(targetWorkItemId)) { // record the target-side work item to be in conflict m_conflictedWorkItems[targetId].Add(targetWorkItemId); } break; } if (!m_perMappedItemEdits.ContainsKey(m_targetToSourceWorkItemIdMapping[targetWorkItemId])) { m_perMappedItemEdits.Add(m_targetToSourceWorkItemIdMapping[targetWorkItemId], new List <MigrationAction>()); } m_perMappedItemEdits[m_targetToSourceWorkItemIdMapping[targetWorkItemId]].Add(action); } else { // check if the revision is a sync copy from the other side if (TranslationService.IsSyncGeneratedAction(action, sourceId)) { continue; } if (m_conflictedWorkItems[sourceId].Contains(sourceSideItemId)) { string conflictDetails = ChainOnBackloggedItemConflictType.CreateConflictDetails(sourceSideItemId, action.Version); string scopeHint = ChainOnBackloggedItemConflictType.CreateScopeHint(sourceSideItemId); MigrationConflict conflict = new ChainOnBackloggedItemConflictType().CreateConflict( conflictDetails, scopeHint, action); ConflictManager.BacklogUnresolvedConflict(TargetSystemId, conflict, false); break; } if (!m_perMappedItemEditsTargetDelta.ContainsKey(targetWorkItemId)) { m_perMappedItemEditsTargetDelta.Add(targetWorkItemId, new List <MigrationAction>()); } m_perMappedItemEditsTargetDelta[targetWorkItemId].Add(action); } // item is mapped to an item on target system if (!outMappedWorkItemUpdates.Contains(targetWorkItemId)) { outMappedWorkItemUpdates.Add(targetWorkItemId); } } else if (action.Action == WellKnownChangeActionId.AddAttachment) { string targetWorkItemId = TryGetTargetWorkItemId( action.MigrationActionDescription, sourceId, getSourceSideCopyInMigrationInstructionTable); if (string.IsNullOrEmpty(targetWorkItemId)) { continue; } if (!outMappedAttachmentUpdates.ContainsKey(targetWorkItemId)) { outMappedAttachmentUpdates.Add(targetWorkItemId, new List <MigrationAction>()); } List <MigrationAction> attachmentList = outMappedAttachmentUpdates[targetWorkItemId]; IMigrationFileAttachment sourceItem = action.SourceItem as IMigrationFileAttachment; if (null == sourceItem) { throw new MigrationException(MigrationToolkitResources.InvalidSourceItemForAttachmentOperation); } if (!attachmentList.Contains(action)) { attachmentList.Add(action); } } else if (action.Action == WellKnownChangeActionId.DelAttachment) { // do nothing for now continue; } } } }while (changeGroups.Count() > 0); }
private void TryResolvePerWorkItemConflicts( string targetWiId) { string sourceSideItemId = m_targetToSourceWorkItemIdMapping[targetWiId]; TraceManager.TraceInformation("Edit/Edit conflicted work item: {0}", sourceSideItemId); int editeditConflictId = int.MinValue; bool previousConflictIsResolved = true; foreach (var migrInstrActionFromSource in m_perMappedItemEdits[sourceSideItemId]) { if (!previousConflictIsResolved) { // previous revision of the work item has conflict, push *this revision to backlog as well // *: this revision (migration instruction) will be obsoleted and the corresponding delta entry reactived ChangeGroup migrationInstructionEntry = migrInstrActionFromSource.ChangeGroup; ChangeGroup entryToBlock = null; if (migrationInstructionEntry.ReflectedChangeGroupId.HasValue) { entryToBlock = TargetChangeGroupService.ReactivateDeltaEntry(migrationInstructionEntry); } if (entryToBlock == null) { entryToBlock = migrationInstructionEntry; } if (entryToBlock.Actions.Count <= 0) { continue; } // an edit/edit conflict must have been backlogged already Debug.Assert(editeditConflictId != int.MinValue, "Edit/edit conflict Id is not available"); string conflictDetails = ChainOnBackloggedItemConflictType.CreateConflictDetails(sourceSideItemId, migrInstrActionFromSource.Version); string scopeHint = ChainOnBackloggedItemConflictType.CreateScopeHint(sourceSideItemId); MigrationConflict chainedConflict = new ChainOnBackloggedItemConflictType().CreateConflict(conflictDetails, scopeHint, entryToBlock.Actions.First()); ConflictManager.BacklogUnresolvedConflict(ConflictManager.SourceId, chainedConflict, false); } else { ChangeGroup sourceGroupForResolution = null; if (migrInstrActionFromSource.ChangeGroup.ReflectedChangeGroupId.HasValue) { sourceGroupForResolution = TargetChangeGroupService.ReactivateDeltaEntry(migrInstrActionFromSource.ChangeGroup); } if (sourceGroupForResolution == null) { sourceGroupForResolution = migrInstrActionFromSource.ChangeGroup; } ConflictResolutionResult resolutionResult = TryResolveSingleRevConflict(sourceSideItemId, sourceGroupForResolution.Actions.First(), targetWiId, m_perMappedItemEditsTargetDelta[targetWiId]); previousConflictIsResolved = resolutionResult.Resolved; if (previousConflictIsResolved) { // conflict is auto-resolved, // obsolete the reactivated delta entry and reactivate the migration instruction if (sourceGroupForResolution.Status != ChangeStatus.Skipped && sourceGroupForResolution != migrInstrActionFromSource.ChangeGroup) { TargetChangeGroupService.ReactivateMigrationInstruction(migrInstrActionFromSource.ChangeGroup, sourceGroupForResolution); } continue; } else { // save the persisted unresolved conflict id, so that the following changes can "chain-on" it editeditConflictId = resolutionResult.ConflictInternalId; } } } if (!previousConflictIsResolved) { bool chainOnBackloggedItemConflictIsCreated = false; foreach (var targetAction in m_perMappedItemEditsTargetDelta[targetWiId]) { Debug.Assert(editeditConflictId != int.MinValue, "Edit/edit conflict Id is not available"); MigrationConflict chainedConflict = new ChainOnConflictConflictType().CreateConflict( ChainOnConflictConflictType.CreateConflictDetails(editeditConflictId), ChainOnConflictConflictType.CreateScopeHint(editeditConflictId), targetAction); // action is from the target system, use the TargetSystem migration source Id ConflictManager.BacklogUnresolvedConflict(TargetSystemId, chainedConflict, false); if (!chainOnBackloggedItemConflictIsCreated) { string conflictDetails = ChainOnBackloggedItemConflictType.CreateConflictDetails(targetWiId, targetAction.Version); string scopeHint = ChainOnBackloggedItemConflictType.CreateScopeHint(sourceSideItemId); MigrationConflict conflict = new ChainOnBackloggedItemConflictType().CreateConflict(conflictDetails, scopeHint, targetAction); ConflictManager.BacklogUnresolvedConflict(TargetSystemId, conflict, false); chainOnBackloggedItemConflictIsCreated = true; } } } }