private ChangeGroup createChangeGroupForSnapshot(int snapShotChangeset, long executionOrder) { ChangeGroup group = m_changeGroupService.CreateChangeGroupForDeltaTable(snapShotChangeset.ToString()); group.Owner = null; group.Comment = string.Format("Initial Check-in as snapshot at changeset {0}", snapShotChangeset); group.ChangeTimeUtc = DateTime.UtcNow; group.Status = ChangeStatus.Delta; group.ExecutionOrder = executionOrder; return(group); }
/// <summary> /// Creates the change group. /// </summary> /// <param name="changeset">The changeset.</param> /// <param name="executionOrder">The execution order.</param> /// <returns></returns> private ChangeGroup CreateChangeGroup(int changeset, long executionOrder) { ChangeGroup group = changeGroupService.CreateChangeGroupForDeltaTable(changeset.ToString(CultureInfo.CurrentCulture)); group.Owner = null; group.Comment = string.Format(CultureInfo.CurrentCulture, "Changeset {0}", changeset); group.ChangeTimeUtc = DateTime.UtcNow; group.Status = ChangeStatus.Delta; group.ExecutionOrder = executionOrder; return(group); }
private ChangeGroup createChangeGroupForSnapshot(DateTime snapShotTime, long executionOrder) { ChangeGroup group = m_changeGroupService.CreateChangeGroupForDeltaTable(snapShotTime.ToString()); group.Owner = null; group.Comment = string.Format("Initial Check-in as snapshot at changeset {0}", snapShotTime); // ChangeTimeUtc should be set to DateTime.MaxValue since we don't know the actual original time the change was made on the source group.ChangeTimeUtc = DateTime.MaxValue; group.Status = ChangeStatus.Delta; group.ExecutionOrder = executionOrder; return(group); }
internal ChangeGroup GetChangeGroupForLatestFieldValues(ChangeGroupService changeGroupService) { string author = (string)WorkItem.Fields[CoreField.ChangedBy].Value; if (string.IsNullOrEmpty(author)) { author = (string)WorkItem.Fields[CoreField.AuthorizedAs].Value; } DateTime changedDate = (DateTime)WorkItem.Fields[CoreField.ChangedDate].Value; List <Field> fieldsForSync = new List <Field>(); foreach (Field f in WorkItem.Fields) { // filter out System.History with empty new value if (TFStringComparer.FieldName.Equals(f.ReferenceName, CoreFieldReferenceNames.History)) { if (string.IsNullOrEmpty(f.Value as string)) { continue; } } if (MustTakeField(f.FieldDefinition)) { fieldsForSync.Add(f); } } if (fieldsForSync.Count == 0) { return(null); } XmlDocument migrationActionDetails = CreateFieldRevisionDescriptionDoc( WorkItem.Rev, author, changedDate, fieldsForSync, new List <Field>()); ChangeGroup changeGroup = changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}", WorkItem.Id, WorkItem.Rev)); IMigrationAction action = changeGroup.CreateAction( // Always generate Edit even for rev 1 in force sync case // Action will be changed to Add later if history not found for rev 1 WellKnownChangeActionId.Edit, new TfsWITMigrationItem(WorkItem, WorkItem.Rev), WorkItem.Id.ToString(), "", WorkItem.Rev.ToString(), "", WellKnownContentType.WorkItem.ReferenceName, migrationActionDetails); return(changeGroup); }
internal ChangeGroup CreateChangeGroup( ChangeGroupService changeGroupService, ClearQuestMigrationContext migrationContext, bool isLastRevOfThisSyncCycle) { ChangeGroup changeGroup = changeGroupService.CreateChangeGroupForDeltaTable(ChangeGroupName); OAdEntity record = CQWrapper.GetEntity(CQSession, EntityDefName, EntityDispName); if (IsRecordHistory) { XmlDocument recordHistDesc = CreateRecordHistoryDesc(record, HistoryValue, HistoryFieldName + "::" + Version, isLastRevOfThisSyncCycle); changeGroup.CreateAction(WellKnownChangeActionId.Edit, this, MigrationRecordId, "", Version, "", WellKnownContentType.WorkItem.ReferenceName, recordHistDesc); } else { XmlDocument recordDesc = CreateRecordDesc(record, Version, migrationContext, isLastRevOfThisSyncCycle); if (Version.Equals(NewRecordVersion, StringComparison.InvariantCulture)) { changeGroup.CreateAction(WellKnownChangeActionId.Add, this, MigrationRecordId, "", Version, "", WellKnownContentType.WorkItem.ReferenceName, recordDesc); } else { changeGroup.CreateAction(WellKnownChangeActionId.Edit, this, MigrationRecordId, "", Version, "", WellKnownContentType.WorkItem.ReferenceName, recordDesc); } } return(changeGroup); }
/// <summary> /// Analyzes the TFS changeset to generate a change group. /// </summary> /// <param name="logRecord"></param> /// <returns></returns> private int analyzeChangeset(ChangeSet changeSet, int[] mappedChanges) { if (changeSet == null) { throw new ArgumentNullException("changeSet"); } lazyInit(); TraceManager.TraceInformation("Starting analysis of Subversion revision {0}", changeSet.Revision); int changeCount = 0; ChangeGroup group = m_changeGroupService.CreateChangeGroupForDeltaTable(changeSet.Revision.ToString(CultureInfo.InvariantCulture)); populateChangeGroupMetaData(group, changeSet); if (changeSet != null) { m_algorithm.CurrentChangeset = changeSet; foreach (Change change in changeSet.Changes) { // Either no snapshot start point is specified or we already passed the snapshot start point. if (IsPathMapped(change.FullServerPath)) { m_algorithm.Execute(change, group); } else { m_algorithm.ExecuteNonMapped(change, group, mappedChanges); } } m_algorithm.Finish(group); } changeCount = group.Actions.Count; if (group.Actions.Count > 0) { group.Save(); } if (changeCount == 0) { TraceManager.TraceInformation("No relevent changes found in SVN revision {0}", changeSet.Revision); } return(changeCount); }
internal ChangeGroup GetChangeGroupForLatestAttachments(ChangeGroupService changeGroupService) { List <TfsMigrationFileAttachment> files = new List <TfsMigrationFileAttachment>(); if (WorkItem.Attachments.Count == 0) { return(null); } else { foreach (Attachment attachment in WorkItem.Attachments) { files.Add(new TfsMigrationFileAttachment(attachment, m_core.TfsTPC.Uri.AbsoluteUri)); } Guid changeActionId = WellKnownChangeActionId.AddAttachment; ChangeGroup changeGroup = changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}", WorkItem.Id, "Attachments")); foreach (TfsMigrationFileAttachment attachmentFile in files) { XmlDocument migrationActionDetails = CreateAttachmentDescriptionDoc(attachmentFile, WorkItem.Rev.ToString()); changeGroup.CreateAction( changeActionId, attachmentFile, WorkItem.Id.ToString(), "", "0", "", WellKnownContentType.WorkItem.ReferenceName, migrationActionDetails); TraceManager.TraceVerbose(String.Format("Generating AddAttachment change action: Work Item: {0}, Attachment File: {1}", WorkItem.Id.ToString(), attachmentFile.Name)); } return(changeGroup); } }
/// <summary> /// Generate the delta table /// </summary> public void GenerateDeltaTable() { // Load the current high water mark from any previous run of this session m_hwmDelta.Reload(); m_renameList = null; m_renameListUpdated = false; // Removing the in-progress change groups created by this side (fils system) m_changeGroupService.RemoveInProgressChangeGroups(); if (m_changeGroupService.GetInProgressMigrationInstructionCount() > 0) { // If there are in-progress migration instructions translated from the other side, mark this delta table as contentcon flict detection only. m_contentConflictDetectionOnly = true; TraceManager.TraceInformation("Migration instruction in progress, the delta table will be generated for content conflict detection only"); } else { m_contentConflictDetectionOnly = false; } DateTime newHighWaterMarkTime = DateTime.Now; List <String> pathsToBeVerified = new List <String>(); int versionToBeSynced = 0; // In a two-way sync, versionToBeSynced is set to the last sync point - either the latest changeset migrated from TFS or the latest changeset migrated to TFS // In one way sync, versionToBeSynced is always set to the GetLatestChangesetId of TFS if (m_hwmLastSyncedTfsChangeset != null) { // m_hwmLastSyncedTfsChangeset is the latest TFS changeset migrated to the file system side. // This highwater mark will be set when TfsFileSystemAnalysisProvider is combined with another provider to form a two-way sync. m_hwmLastSyncedTfsChangeset.Reload(); // m_lastHighWaterMarkMigratedToPeer is the latest TFS changeset migrated from this TfsFileSystemAnalysisProvider. m_lastHighWaterMarkMigratedToPeer.Reload(); versionToBeSynced = m_lastHighWaterMarkMigratedToPeer.Value > m_hwmLastSyncedTfsChangeset.Value ? m_lastHighWaterMarkMigratedToPeer.Value : m_hwmLastSyncedTfsChangeset.Value; } else { versionToBeSynced = m_destinationTfs.GetLatestChangesetId(); } FileSystemVerifier fileSystemVerifier = new FileSystemVerifier(); ChangeGroup changeGroup = m_changeGroupService.CreateChangeGroupForDeltaTable(DateTime.Now.ToString()); populateChangeGroupMetaData(changeGroup); foreach (MappingEntry m in ConfigurationService.Filters) { if (m.Cloak) { continue; } string canonicalPath = removeTrailingSlash(m.Path); fileSystemVerifier.AddPathForVerification(canonicalPath); analyzeFolder(canonicalPath, versionToBeSynced, changeGroup); } if (!fileSystemVerifier.Verify()) { TraceManager.TraceError( "Analysis failed as the local file system state was changed during the analysis phase. No changes were created."); markChangeGroupAsObsolete(changeGroup); return; } if (changeGroup.Actions.Count > 0) { // We only want to promote deltas to pending if we actually created and saved a change group. changeGroup.Save(); m_changeGroupService.PromoteDeltaToPending(); } else { markChangeGroupAsObsolete(changeGroup); } m_lastTfsChangesetAnalyzed.Update(versionToBeSynced); m_hwmDelta.Update(newHighWaterMarkTime); TraceManager.TraceInformation(String.Format(CultureInfo.InvariantCulture, TfsFileSystemResources.UpdatedHighWaterMark, newHighWaterMarkTime)); }
private void ComputeDeltaPerRecord( OAdEntity aRecord) { OAdEntityDef aEntityDef = CQWrapper.GetEntityDef(m_userSession, CQWrapper.GetEntityDefName(aRecord)); string recordDispName = CQWrapper.GetEntityDisplayName(aRecord); string recordEntDefName = CQWrapper.GetEntityDefName(aRecord); #region process history bool recordContentIsModified = false; bool maybeNewRecord = false; Dictionary <string, List <ClearQuestRecordItem> > historyDelta = new Dictionary <string, List <ClearQuestRecordItem> >(); Dictionary <string, int> perHistoryFieldLastIndex = new Dictionary <string, int>(); // needed for updating processed delta // find all history fields OAdHistoryFields aHistFields = CQWrapper.GetHistoryFields(aRecord); int historyFldCount = CQWrapper.HistoryFieldsCount(aHistFields); bool containsNewHistory = false; for (int histFldIndex = 0; histFldIndex < historyFldCount; histFldIndex++) { object ob = (object)histFldIndex; OAdHistoryField aHistoryField = CQWrapper.HistoryFieldsItem(aHistFields, ref ob); string historyFieldName = CQWrapper.GetHistoryFieldName(aHistoryField); // find last processed history entry for this history field string lookupItemId = CQDeltaComputationProgressLookupService.CreateHistoryItemId(recordEntDefName, recordDispName, historyFieldName); int startHistIndex = 1 + DeltaComputeProgressService.GetLastProcessedItemVersion(lookupItemId); // find all history in a particular history field int historyCount = CQWrapper.HistoryFieldHistoriesCount(aHistoryField); for (int histIndex = startHistIndex; histIndex < historyCount; histIndex++) { object obHistIndex = (object)histIndex; OAdHistory aHistory = CQWrapper.HistoryFieldHistoriesItem(aHistoryField, ref obHistIndex); CQHistory cqHistory = new CQHistory(aHistory); CQMigrationItem migrationItem = new CQHistoryMigrationItem(recordDispName, historyFieldName, histIndex); if (TranslationService.IsSyncGeneratedItemVersion(ClearQuestRecordItem.GetMigrationRecordId(recordEntDefName, recordDispName), migrationItem.MigrationItemVersion, m_configurationService.SourceId)) { continue; } if (histIndex == 0) { maybeNewRecord = true; } // add unprocessed history fields for processing if (!historyDelta.ContainsKey(historyFieldName)) { historyDelta.Add(historyFieldName, new List <ClearQuestRecordItem>(historyCount)); } historyDelta[aHistoryField.fieldname].Add(new ClearQuestRecordItem(aRecord, aHistory, historyFieldName, histIndex.ToString())); containsNewHistory = true; // based on action type, we decide whether content change is needed int actionType = CQWrapper.GetActionDefType(aEntityDef, cqHistory.Action); switch (actionType) { case CQConstants.ACTION_SUBMIT: break; case CQConstants.ACTION_MODIFY: recordContentIsModified = true; break; case CQConstants.ACTION_CHANGE: break; case CQConstants.ACTION_DUPLICATE: break; case CQConstants.ACTION_UNDUPLICATE: break; case CQConstants.ACTION_IMPORT: break; case CQConstants.ACTION_DELETE: TraceManager.TraceInformation(ClearQuestResource.ClearQuest_Msg_RecordDeleted, recordEntDefName, recordDispName); break; case CQConstants.ACTION_BASE: break; case CQConstants.ACTION_RECORD_SCRIPT_ALIAS: break; } } perHistoryFieldLastIndex.Add(historyFieldName, historyCount - 1); } #endregion #region generate delta for content and history if (maybeNewRecord || recordContentIsModified) { // the first revision, i.e. "Submit", of a CQ record is always hard-coded to be '1' CQMigrationItem contentMigrationAction = new CQMigrationItem(recordDispName, ClearQuestRecordItem.NewRecordVersion); bool isNewRecord = false; if (maybeNewRecord) { isNewRecord = !(DeltaComputeProgressService.IsMigrationItemProcessed(recordDispName, ClearQuestRecordItem.NewRecordVersionValue)); } if (!isNewRecord) { // all subsequent record "MODIFICATIONs" are hard-coded to be "update@<Now.Ticks>" contentMigrationAction.MigrationItemVersion = ClearQuestRecordItem.RecordUpdateVersion + "@" + DateTime.Now.Ticks; } ClearQuestRecordItem recordContentItem = new ClearQuestRecordItem(aRecord, contentMigrationAction.MigrationItemVersion); recordContentItem.CQSession = m_userSession; recordContentItem.Version = contentMigrationAction.MigrationItemVersion; ChangeGroup contentChangeGroup = recordContentItem.CreateChangeGroup( m_changeGroupService, m_migrationContext, isNewRecord && m_isLastRevisionAutoCorrectionEnabled); contentChangeGroup.Save(); if (isNewRecord && !containsNewHistory) { DeltaComputeProgressService.UpdateCache(recordDispName, ClearQuestRecordItem.NewRecordVersionValue); } } var lastHistoryRecordItem = historyDelta[historyDelta.Keys.Last()].Last(); foreach (string histFieldName in historyDelta.Keys) { foreach (ClearQuestRecordItem recordHistItem in historyDelta[histFieldName]) { recordHistItem.CQSession = m_userSession; ChangeGroup changeGroup = recordHistItem.CreateChangeGroup( m_changeGroupService, m_migrationContext, (CQStringComparer.FieldName.Equals(recordHistItem.HistoryFieldName, lastHistoryRecordItem.HistoryFieldName) && recordHistItem.Version.Equals(lastHistoryRecordItem.Version, StringComparison.OrdinalIgnoreCase) && m_isLastRevisionAutoCorrectionEnabled)); changeGroup.Save(); } Debug.Assert(perHistoryFieldLastIndex.ContainsKey(histFieldName), "perHistoryFieldLastIndex.ContainsKey(histFieldName) returns false"); string deltaComputeProcessLookupId = CQDeltaComputationProgressLookupService.CreateHistoryItemId(recordEntDefName, recordDispName, histFieldName); DeltaComputeProgressService.UpdateCache(deltaComputeProcessLookupId, perHistoryFieldLastIndex[histFieldName]); } #endregion #region process attachment OAdAttachmentFields aAttachmentFields = CQWrapper.GetAttachmentFields(aRecord); for (int aAttachmentFieldsIndex = 0; aAttachmentFieldsIndex < CQWrapper.AttachmentsFieldsCount(aAttachmentFields); aAttachmentFieldsIndex++) { object ob = (object)aAttachmentFieldsIndex; OAdAttachmentField aAttachmentField = CQWrapper.AttachmentsFieldsItem(aAttachmentFields, ref ob); string fieldName = CQWrapper.GetAttachmentFieldName(aAttachmentField); ChangeGroup changeGroup = m_changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}:{2}", recordDispName, "Attachments", fieldName)); // process all attachments OAdAttachments attachments = CQWrapper.GetAttachments(aAttachmentField); for (int attachmentIndex = 0; attachmentIndex < CQWrapper.AttachmentsCount(attachments); attachmentIndex++) { object obIndex = (object)attachmentIndex; OAdAttachment aAttachment = CQWrapper.AttachmentsItem(attachments, ref obIndex); ClearQuestAttachmentItem attachmentItem = new ClearQuestAttachmentItem(aRecord, aAttachmentField, aAttachment, UserSessionConnConfig); attachmentItem.CQSession = m_userSession; attachmentItem.CreateChangeAction(changeGroup, lastHistoryRecordItem.Version); } if (changeGroup.Actions.Count > 0) { changeGroup.Save(); } } #endregion }
internal void ComputeFieldDelta( ChangeGroupService changeGroupService, DateTime waterMarkChangeStartTime, FieldValueComparer tfsValueComparer, ITranslationService translationService, ConfigurationService configService, List <ChangeGroup> groups, IsWorkItemRevisionProcessed processedRevCallBack) { Guid sourceId = configService.SourceId; List <Revision> revsToBeSynced = FindUnsyncedRevisions(waterMarkChangeStartTime, translationService, sourceId, processedRevCallBack); Dictionary <int, object> fieldValueBaseline = new Dictionary <int, object>(); TryEstablishFieldValueBaseline(fieldValueBaseline, revsToBeSynced); foreach (Revision rev in revsToBeSynced) { // get basic revision info: revision#, author#, and change time int revIndex = (int)rev.Fields[CoreField.Rev].Value; string author = (string)rev.Fields[CoreField.ChangedBy].Value; if (string.IsNullOrEmpty(author)) { author = (string)rev.Fields[CoreField.AuthorizedAs].Value; } DateTime changedDate = (DateTime)rev.Fields[CoreField.ChangedDate].Value; List <Field> fieldForSync = new List <Field>(); List <Field> skippingFields = new List <Field>(); foreach (Field f in rev.Fields) { // filter out System.History with empty new value if (TFStringComparer.FieldName.Equals(f.ReferenceName, CoreFieldReferenceNames.History)) { if (string.IsNullOrEmpty(f.Value as string)) { continue; } } if (MustTakeField(f.FieldDefinition)) { // find out fields that changed in this revision object oldValue; fieldValueBaseline.TryGetValue(f.Id, out oldValue); //***Note: When it is a new work item (unmatched on the other side), // we need to always include fields if ((null != f.Value && (!tfsValueComparer.Equals(oldValue, f.Value) || revIndex == 1)) || (null == f.Value && null != oldValue && revIndex != 1)) { fieldForSync.Add(f); fieldValueBaseline[f.Id] = f.Value; } else if (IsReferencedField(f, configService)) { skippingFields.Add(f); } } else if (IsReferencedField(f, configService)) { skippingFields.Add(f); } } if (fieldForSync.Count == 0) { continue; } XmlDocument migrationActionDetails = CreateFieldRevisionDescriptionDoc( revIndex, author, changedDate, fieldForSync, skippingFields); Guid changeActionId = revIndex == 1 ? WellKnownChangeActionId.Add : WellKnownChangeActionId.Edit; /// TODO: consider batching revs of different workitems in one change group ChangeGroup changeGroup = changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}", WorkItem.Id, revIndex)); IMigrationAction action = changeGroup.CreateAction( changeActionId, new TfsWITMigrationItem(WorkItem, revIndex), WorkItem.Id.ToString(), "", revIndex.ToString(), "", WellKnownContentType.WorkItem.ReferenceName, migrationActionDetails); groups.Add(changeGroup); } }
internal void ComputeAttachmentDelta( ChangeGroupService changeGroupService, DateTime waterMarkChangeStartTime, ITranslationService translationService, Guid sourceId, List <ChangeGroup> groups) { List <TfsMigrationFileAttachment> files = new List <TfsMigrationFileAttachment>(); if (WorkItem.Attachments.Count > 0) { List <Revision> revsToBeSynced = FindUnsyncedRevisions(waterMarkChangeStartTime, translationService, sourceId, null); bool hasAttachmentChanges = false; foreach (Revision rev in revsToBeSynced) { if (rev.Index == 0 && (int)rev.Fields[CoreField.AttachedFileCount].Value > 0) { hasAttachmentChanges = true; break; } if (rev.Index > 0) { int currAttchCount = (int)rev.Fields[CoreField.AttachedFileCount].Value; Revision prevRev = rev.WorkItem.Revisions[rev.Index - 1]; int prevAttchCount = (int)prevRev.Fields[CoreField.AttachedFileCount].Value; if (currAttchCount != prevAttchCount) { hasAttachmentChanges = true; break; } } } if (!hasAttachmentChanges) { return; } foreach (Attachment attachment in WorkItem.Attachments) { if (attachment.AttachedTimeUtc <= waterMarkChangeStartTime || !attachment.IsSaved) { continue; } files.Add(new TfsMigrationFileAttachment(attachment)); } } Guid changeActionId = WellKnownChangeActionId.AddAttachment; ChangeGroup changeGroup = changeGroupService.CreateChangeGroupForDeltaTable( string.Format("{0}:{1}", WorkItem.Id, "Attachments")); foreach (TfsMigrationFileAttachment attachmentFile in files) { XmlDocument migrationActionDetails = CreateAttachmentDescriptionDoc(attachmentFile, WorkItem.Rev.ToString()); changeGroup.CreateAction( changeActionId, attachmentFile, WorkItem.Id.ToString(), "", "0", "", WellKnownContentType.WorkItem.ReferenceName, migrationActionDetails); } // VERY IMPORTANT: use the RelatedArtifactsStore to detect detailed attachment changes WorkItemAttachmentStore store = new WorkItemAttachmentStore(sourceId); List <FileAttachmentMetadata> additionalAttachmentToDelete; store.UpdatePerItemAttachmentChangesByCheckingRelatedItemRecords( WorkItem.Id.ToString(), changeGroup, out additionalAttachmentToDelete); foreach (FileAttachmentMetadata attch in additionalAttachmentToDelete) { TfsMigrationFileAttachment attachmentFile = new TfsMigrationFileAttachment(attch); XmlDocument migrationActionDetails = CreateAttachmentDescriptionDoc(attachmentFile, WorkItem.Rev.ToString()); changeGroup.CreateAction( WellKnownChangeActionId.DelAttachment, attachmentFile, WorkItem.Id.ToString(), "", "0", "", WellKnownContentType.WorkItem.ReferenceName, migrationActionDetails); } groups.Add(changeGroup); }