nkdAgility/azure-devops-migration-tools

[Bug]: When there are Work Items with changed type and the configuration is set to "SkipToFinalRevisedWorkItemType": true, errors occur and Work Items are created empty.

jivkoko opened this issue ยท 9 comments

Version

  • I confirm that I am using the latest version

Source Version

Azure DevOps Server 2020

Target Version

Azure DevOps Service

Relevant configuration

"Processors": [
    {
      "$type": "WorkItemMigrationConfig",
      "Enabled": true,
      "ReplayRevisions": true,
      "PrefixProjectToNodes": false,
      "UpdateCreatedDate": true,
      "UpdateCreatedBy": true,
      "WIQLQueryBit": "",
      "WIQLOrderBit": "[System.ID]",
      "LinkMigration": true,
      "AttachmentMigration": true,
      "AttachmentWorkingPath": "c:\\temp\\WorkItemAttachmentWorkingFolder\\",
      "FixHtmlAttachmentLinks": true,
      "SkipToFinalRevisedWorkItemType": true,
      "WorkItemCreateRetryLimit": 5,
      "FilterWorkItemsThatAlreadyExistInTarget": true,
      "PauseAfterEachWorkItem": false,
      "AttachmentMaxSize": 480000000,
      "AttachRevisionHistory": true,
      "LinkMigrationSaveEachAsAdded": false,
      "GenerateMigrationComment": true,
      "WorkItemIDs": null,
      "MaxRevisions": 1000,
      "UseCommonNodeStructureEnricherConfig": false,
      "NodeBasePaths": [],
      "AreaMaps": {},
      "IterationMaps": {},
      "MaxGracefulFailures": 0,
      "SkipRevisionWithInvalidIterationPath": false,
      "SkipRevisionWithInvalidAreaPath": false,
      "ShouldCreateMissingRevisionPaths": true
    }

Relevant log output

2023-11-17 16:22:37.423 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Work Item has 4 revisions and revision migration is set to true
2023-11-17 16:22:37.424 +02:00 [INF] Found 4 revisions to migrate on  Work item:28
2023-11-17 16:22:37.424 +02:00 [INF] Found 4 revisions to migrate on  Work item:28
2023-11-17 16:22:37.425 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | WorkItem has changed type at one of the revisions, from Epic to Requirement
2023-11-17 16:22:37.427 +02:00 [INF] Attached a consolidated set of {"RevisionCount":4} revisions.
2023-11-17 16:22:37.492 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null |  Processing Revision [1]
2023-11-17 16:22:37.874 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | ...FAILED to Save
2023-11-17 16:22:37.875 +02:00 [INF] ===============================================================
2023-11-17 16:22:37.876 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Custom.ReflectedWorkItemId (ReflectedWorkItemId) | 
2023-11-17 16:22:37.878 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.SubjectMatterExpert3 (Subject Matter Expert 3) | 
2023-11-17 16:22:37.879 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.SubjectMatterExpert2 (Subject Matter Expert 2) | 
2023-11-17 16:22:37.879 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.SubjectMatterExpert1 (Subject Matter Expert 1) | 
2023-11-17 16:22:37.880 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.UserAcceptanceTest (User Acceptance Test) | Not Ready
2023-11-17 16:22:37.880 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.ImpactAssessmentHtml (Impact Assessment HTML) | 
2023-11-17 16:22:37.880 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.Committed (Committed) | No
2023-11-17 16:22:37.880 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.RequirementType (Requirement Type) | Functional
2023-11-17 16:22:37.881 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.CMMI.Blocked (Blocked) | No
2023-11-17 16:22:37.881 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Scheduling.Size (Size) | null
2023-11-17 16:22:37.881 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.Triage (Triage) | Pending
2023-11-17 16:22:37.882 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Scheduling.FinishDate (Finish Date) | null
2023-11-17 16:22:37.882 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Scheduling.StartDate (Start Date) | null
2023-11-17 16:22:37.882 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Build.IntegrationBuild (Integration Build) | 
2023-11-17 16:22:37.883 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ValueArea (Value Area) | Business
2023-11-17 16:22:37.883 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.StackRank (Stack Rank) | null
2023-11-17 16:22:37.884 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.Priority (Priority) | 2
2023-11-17 16:22:37.884 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ClosedBy (Closed By) | 
2023-11-17 16:22:37.884 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ClosedDate (Closed Date) | null
2023-11-17 16:22:37.885 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ResolvedReason (Resolved Reason) | 
2023-11-17 16:22:37.885 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ResolvedBy (Resolved By) | 
2023-11-17 16:22:37.886 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ResolvedDate (Resolved Date) | null
2023-11-17 16:22:37.886 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ActivatedBy (Activated By) | 
2023-11-17 16:22:37.887 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.ActivatedDate (Activated Date) | null
2023-11-17 16:22:37.887 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Common.StateChangeDate (State Change Date) | null
2023-11-17 16:22:37.888 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Microsoft.VSTS.Scheduling.OriginalEstimate (Original Estimate) | null
2023-11-17 16:22:37.888 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.BoardLane (Board Lane) | 
2023-11-17 16:22:37.888 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.BoardColumnDone (Board Column Done) | null
2023-11-17 16:22:37.889 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.BoardColumn (Board Column) | 
2023-11-17 16:22:37.889 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Tags (Tags) | 
2023-11-17 16:22:37.889 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.RelatedLinkCount (Related Link Count) | 0
2023-11-17 16:22:37.891 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.History (History) | 
2023-11-17 16:22:37.891 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Description (Description) | 
2023-11-17 16:22:37.891 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.CreatedBy (Created By) | 
2023-11-17 16:22:37.895 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.CreatedDate (Created Date) | 
2023-11-17 16:22:37.895 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.WorkItemType (Work Item Type) | Requirement
2023-11-17 16:22:37.896 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.AssignedTo (Assigned To) | 
2023-11-17 16:22:37.896 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Reason (Reason) | New
2023-11-17 16:22:37.896 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.ChangedBy (Changed By) | 
2023-11-17 16:22:37.897 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Rev (Rev) | null
2023-11-17 16:22:37.897 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Watermark (Watermark) | null
2023-11-17 16:22:37.898 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.AuthorizedDate (Authorized Date) | null
2023-11-17 16:22:37.898 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.State (State) | Proposed
2023-11-17 16:22:37.898 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Title (Title) | 
2023-11-17 16:22:37.898 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.AuthorizedAs (Authorized As) | 
2023-11-17 16:22:37.898 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.AreaId (Area ID) | 40
2023-11-17 16:22:37.899 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Id (ID) | 0
2023-11-17 16:22:37.899 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.ChangedDate (Changed Date) | null
2023-11-17 16:22:37.899 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.RevisedDate (Revised Date) | null
2023-11-17 16:22:37.899 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.AreaPath (Area Path) | Third
2023-11-17 16:22:37.900 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.NodeName (Node Name) | Third
2023-11-17 16:22:37.900 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.AttachedFileCount (Attached File Count) | 1
2023-11-17 16:22:37.900 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.HyperLinkCount (Hyperlink Count) | 0
2023-11-17 16:22:37.900 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.CommentCount (Comment Count) | null
2023-11-17 16:22:37.901 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.RemoteLinkCount (Remote Link Count) | null
2023-11-17 16:22:37.901 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.Parent (Parent) | null
2023-11-17 16:22:37.901 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.TeamProject (Team Project) | Third
2023-11-17 16:22:37.901 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.ExternalLinkCount (External Link Count) | 0
2023-11-17 16:22:37.902 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.IterationId (Iteration ID) | 40
2023-11-17 16:22:37.902 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | System.IterationPath (Iteration Path) | Third
2023-11-17 16:22:37.902 +02:00 [INF] ===============================================================
2023-11-17 16:22:37.903 +02:00 [ERR] System.AggregateException: One or more errors occurred. ---> Microsoft.VisualStudio.Services.WebApi.VssServiceResponseException: Page not found.
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<HandleResponseAsync>d__53.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<SendAsync>d__51.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<SendAsync>d__47`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<SendAsync>d__28`1.MoveNext()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at VstsSyncMigrator.Engine.WorkItemMigrationContext.ReplayRevisions(List`1 revisionsToMigrate, WorkItemData sourceWorkItem, WorkItemData targetWorkItem) in D:\a\1\s\src\VstsSyncMigrator.Core\Execution\MigrationContext\WorkItemMigrationContext.cs:line 650
---> (Inner Exception #0) Microsoft.VisualStudio.Services.WebApi.VssServiceResponseException: Page not found.
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<HandleResponseAsync>d__53.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<SendAsync>d__51.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<SendAsync>d__47`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase.<SendAsync>d__28`1.MoveNext()<---

2023-11-17 16:22:37.906 +02:00 [INF] ===============================================================
2023-11-17 16:22:38.308 +02:00 [INF] [         Requirement][Complete:    23/93][sid:    28|Rev:  4][tid:  null | Average time of 4.777 per work item and 0 hours 5 minutes 39.330 seconds estimated to completion

What happened?

Bugs happened!
1. Main bug:
When the configuration is set to "SkipToFinalRevisedWorkItemType": true
and one or both of the settings are
"SkipRevisionWithInvalidIterationPath": false,
"SkipRevisionWithInvalidAreaPath": false,
errors occur and Work Items are created empty.

I have investigated and debugged the code. This happens in "WorkItemMigrationContext.cs" in method
private WorkItemData ReplayRevisions(List<RevisionItem> revisionsToMigrate, WorkItemData sourceWorkItem, WorkItemData targetWorkItem)

line 738 var result = workItemTrackingClient.UpdateWorkItemAsync(patchDocument, id, bypassRules:true).Result;

This is because when "targetWorkItem" is null, it is created by
targetWorkItem = CreateWorkItem_Shell(Engine.Target.WorkItems.Project, sourceWorkItem, skipToFinalRevisedWorkItemType ? finalDestType : targetType);

And until it is not saved its ID is 0.

When the setting is "SkipToFinalRevisedWorkItemType": true,
targetWorkItem.Type is always different than the first revision's type.
Therefore it tries to update the work item on
line 738 (workItemTrackingClient.UpdateWorkItemAsync(patchDocument, id, bypassRules:true).Result;)

But Work Item ID is still 0 because it is not saved yet. This happens on the first revision.
The error occurs and the work item is left with no data except the type.

There is a workaround but still this is a bug caused by the specific configuration.

Workaround:
The workaround is to set "SkipToFinalRevisedWorkItemType": false
and both
"SkipRevisionWithInvalidIterationPath": true,
"SkipRevisionWithInvalidAreaPath": true,

When this configuration is set the first revision has the same type as the WorkItem and the WorkItem is saved after the first revision.
line 785

 if (!skipIterationRevision && !skipAreaRevision)
                    {
                        targetWorkItem.SaveToAzureDevOps();
                    }

Then if next revisions try to update the WorkItem it will have assigned ID already.

2. Another minor bug:
When updating the WorkItem by adding a new revision because of a changed type (the original revision is missing at all, I do not know why) the revision is associated to the current user who executes the migration but not the original user who has made the change.

An improvement could be to add one more field when updating the WorkItem when there is a changed type revision.

patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.ChangedBy",
Value = currentRevisionWorkItem.Fields["System.ChangedBy"].Value.ToString()
}
);

Doing so the changed type new revision will contain the original user who has made the change.

3. One more minor bug:
Documentation is wrong for "SkipToFinalRevisedWorkItemType" in
https://nkdagility.com/learn/azure-devops-migration-tools/Reference/v1/Processors/WorkItemMigrationContext/

beta If enabled this will fix any image attachments URL's, work item mention URL's or user mentions in the HTML fields as well as discussion comments. You must specify a PersonalAccessToken in the Source project for Azure DevOps; TFS should use integrated authentication.

is the same as the "FixHtmlAttachmentLinks" and it is WRONG

Debug in Visual Studio

  • Visual Studio Debug

@jivkoko I plan on removing SkipToFinalRevisedWorkItemType becasue of the issue you see above. As far as I can see, It should never be set to true as it will always result in this issue.

Its original intent needs to be rejigged.

Do you want to submit a PR for 2 above?

I totally agree with removing SkipToFinalRevisedWorkItemType . Its purpose it is not clear to me and bugs are produced because of it. The main issue for the error is saving the work item after trying to update it. About a PR I can try.

I have cloned the repo, created a new branch and committed the changes. But for some reasons I cannot push the changes even though I authorized successfully. The changes are not so many and they are in one file. I haven't removed SkipToFinalRevisedWorkItemType entirely from the project but only from the WorkItemMigrationContext.cs. So here I attach the changed file because of inability to push the changes. Also can copy the code in the nex comment.
WorkItemMigrationContext.zip

private WorkItemData ReplayRevisions(List<RevisionItem> revisionsToMigrate, WorkItemData sourceWorkItem, WorkItemData targetWorkItem)
       {
           try
           {
               //If work item hasn't been created yet, create a shell
               if (targetWorkItem == null)
               {
                   var finalDestType = revisionsToMigrate.Last().Type;
                   var targetType = revisionsToMigrate.First().Type;

                   if (targetType != finalDestType)
                   {
                       TraceWriteLine(LogEventLevel.Information, $"WorkItem has changed type at one of the revisions, from {targetType} to {finalDestType}");
                   }

                   if (Engine.TypeDefinitionMaps.Items.ContainsKey(targetType))
                   {
                       targetType = Engine.TypeDefinitionMaps.Items[targetType].Map();
                   }
                   targetWorkItem = CreateWorkItem_Shell(Engine.Target.WorkItems.Project, sourceWorkItem, targetType);
               }

               if (_config.AttachRevisionHistory)
               {
                   _revisionManager.AttachSourceRevisionHistoryJsonToTarget(sourceWorkItem, targetWorkItem);
               }

               foreach (var revision in revisionsToMigrate)
               {
                   var currentRevisionWorkItem = sourceWorkItem.GetRevision(revision.Number);

                   TraceWriteLine(LogEventLevel.Information, " Processing Revision [{RevisionNumber}]",
                       new Dictionary<string, object>() {
                           {"RevisionNumber", revision.Number }
                       });

                   // Decide on WIT
                   var destType = currentRevisionWorkItem.Type;
                   if (Engine.TypeDefinitionMaps.Items.ContainsKey(destType))
                   {
                       destType = Engine.TypeDefinitionMaps.Items[destType].Map();
                   }
                   bool typeChange = (destType != targetWorkItem.Type);

                   int workItemId = Int32.Parse(targetWorkItem.Id);

                   if (typeChange && workItemId > 0)
                   {
                       ValidatePatTokenRequirement();
                       Uri collectionUri = Engine.Target.Config.AsTeamProjectConfig().Collection;
                       string token = Engine.Target.Config.AsTeamProjectConfig().PersonalAccessToken;
                       VssConnection connection = new VssConnection(collectionUri, new VssBasicCredential(string.Empty, token));
                       WorkItemTrackingHttpClient workItemTrackingClient = connection.GetClient<WorkItemTrackingHttpClient>();
                       JsonPatchDocument patchDocument = new JsonPatchDocument();
                       DateTime changedDate = ((DateTime)currentRevisionWorkItem.Fields["System.ChangedDate"].Value).AddMilliseconds(-3);

                       patchDocument.Add(
                           new JsonPatchOperation()
                           {
                               Operation = Operation.Add,
                               Path = "/fields/System.WorkItemType",
                               Value = destType
                           }
                       );
                       patchDocument.Add(
                           new JsonPatchOperation()
                           {
                               Operation = Operation.Add,
                               Path = "/fields/System.State",
                               Value = (string)currentRevisionWorkItem.Fields["System.State"].Value
                           }
                       );
                       patchDocument.Add(
                           new JsonPatchOperation()
                           {
                               Operation = Operation.Add,
                               Path = "/fields/System.Reason",
                               Value = (string)currentRevisionWorkItem.Fields["System.Reason"].Value
                           }
                       );
                       patchDocument.Add(
                           new JsonPatchOperation()
                           {
                               Operation = Operation.Add,
                               Path = "/fields/System.ChangedDate",
                               Value = changedDate
                           }
                       );
                       patchDocument.Add(
                       new JsonPatchOperation()
                           {
                               Operation = Operation.Add,
                               Path = "/fields/System.ChangedBy",
                               Value = currentRevisionWorkItem.Fields["System.ChangedBy"].Value.ToString()
                           }
                       );
                       var result = workItemTrackingClient.UpdateWorkItemAsync(patchDocument, workItemId, bypassRules: true).Result;
                       targetWorkItem = Engine.Target.WorkItems.GetWorkItem(workItemId);
                   }
                   PopulateWorkItem(currentRevisionWorkItem, targetWorkItem, destType);

                   var fails = ((WorkItem)targetWorkItem.internalObject).Validate();
                   foreach (Field f in fails)
                   {
                       if (f.Name == "Reason")
                       {
                           if (f.AllowedValues.Count > 0)
                           {
                               targetWorkItem.ToWorkItem().Fields[f.Name].Value = f.AllowedValues[0];
                           } else if (f.FieldDefinition.AllowedValues.Count > 0)
                           {
                               targetWorkItem.ToWorkItem().Fields[f.Name].Value = f.FieldDefinition.AllowedValues[0];
                           }
                       }
                   }
                   // Impersonate revision author. Mapping will apply later and may change this.
                   targetWorkItem.ToWorkItem().Fields["System.ChangedDate"].Value = revision.Fields["System.ChangedDate"].Value;
                   targetWorkItem.ToWorkItem().Fields["System.ChangedBy"].Value = revision.Fields["System.ChangedBy"].Value.ToString();
                   targetWorkItem.ToWorkItem().Fields["System.History"].Value = revision.Fields["System.History"].Value;

                   // Todo: Ensure all field maps use WorkItemData.Fields to apply a correct mapping
                   Engine.FieldMaps.ApplyFieldMappings(currentRevisionWorkItem, targetWorkItem);

                   // Todo: Think about an "UpdateChangedBy" flag as this is expensive! (2s/WI instead of 1,5s when writing "Migration")

                   var reflectedUri = (TfsReflectedWorkItemId)Engine.Source.WorkItems.CreateReflectedWorkItemId(sourceWorkItem);
                   if (!targetWorkItem.ToWorkItem().Fields.Contains(Engine.Target.Config.AsTeamProjectConfig().ReflectedWorkItemIDFieldName))
                   {
                       var ex = new InvalidOperationException("ReflectedWorkItemIDField Field Missing");
                       Log.LogError(ex,
                           " The WorkItemType {WorkItemType} does not have a Field called {ReflectedWorkItemID}",
                           targetWorkItem.Type,
                           Engine.Target.Config.AsTeamProjectConfig().ReflectedWorkItemIDFieldName);
                       throw ex;
                   }
                   targetWorkItem.ToWorkItem().Fields[Engine.Target.Config.AsTeamProjectConfig().ReflectedWorkItemIDFieldName].Value = reflectedUri.ToString();

                   ProcessHTMLFieldAttachements(targetWorkItem);
                   ProcessWorkItemEmbeddedLinks(sourceWorkItem, targetWorkItem);

                   var skipIterationRevision = SkipRevisionWithInvalidIterationPath(targetWorkItem);
                   var skipAreaRevision = SkipRevisionWithInvalidAreaPath(targetWorkItem);

                   if (!skipIterationRevision && !skipAreaRevision)
                   {
                       targetWorkItem.SaveToAzureDevOps();
                   }
                   TraceWriteLine(LogEventLevel.Information,
                       " Saved TargetWorkItem {TargetWorkItemId}. Replayed revision {RevisionNumber} of {RevisionsToMigrateCount}",
                      new Dictionary<string, object>() {
                              {"TargetWorkItemId", targetWorkItem.Id },
                              {"RevisionNumber", revision.Number },
                              {"RevisionsToMigrateCount",  revisionsToMigrate.Count}
                          });
               }

               // Until here we impersonate the maker of the revisions. From here we act as ourselves to push the attachments and add the comment
               targetWorkItem.ToWorkItem().Fields["System.ChangedBy"].Value = "Migration";

               if (targetWorkItem != null)
               {
                   ProcessWorkItemAttachments(sourceWorkItem, targetWorkItem, false);
                   if (!string.IsNullOrEmpty(targetWorkItem.Id))
                   {
                       ProcessWorkItemLinks(Engine.Source.WorkItems, Engine.Target.WorkItems, sourceWorkItem, targetWorkItem);
                       // The TFS client seems to plainly ignore the ChangedBy field when saving a link, so we need to put this back in place
                       targetWorkItem.ToWorkItem().Fields["System.ChangedBy"].Value = "Migration";
                   }

                   if (_config.GenerateMigrationComment)
                   {
                       var reflectedUri = targetWorkItem.ToWorkItem().Fields[Engine.Target.Config.AsTeamProjectConfig().ReflectedWorkItemIDFieldName].Value;
                       var history = new StringBuilder();
                       history.Append(
                           $"This work item was migrated from a different project or organization. You can find the old version at <a href=\"{reflectedUri}\">{reflectedUri}</a>.");
                       targetWorkItem.ToWorkItem().History = history.ToString();
                   }
                   targetWorkItem.SaveToAzureDevOps();

                   attachmentEnricher.CleanUpAfterSave();
                   TraceWriteLine(LogEventLevel.Information, "...Saved as {TargetWorkItemId}", new Dictionary<string, object> { { "TargetWorkItemId", targetWorkItem.Id } });
               }
           }
           catch (Exception ex)
           {
               TraceWriteLine(LogEventLevel.Information, "...FAILED to Save");
               Log.LogInformation("===============================================================");
               if (targetWorkItem != null)
               {
                   foreach (Field f in targetWorkItem.ToWorkItem().Fields)
                       TraceWriteLine(LogEventLevel.Information, "{FieldReferenceName} ({FieldName}) | {FieldValue}",
                           new Dictionary<string, object>()
                           {
                               { "FieldReferenceName", f.ReferenceName }, { "FieldName", f.Name },
                               { "FieldValue", f.Value }
                           });
               }
               Log.LogInformation("===============================================================");
               Log.LogError(ex.ToString(), ex);
               Log.LogInformation("===============================================================");
           }

           return targetWorkItem;
       }

This is the reason I cannot push:

Pushing bug/changed-revision-type-error
Remote: Permission to nkdAgility/azure-devops-migration-tools.git denied to jivkoko.
Error encountered while pushing to the remote repository: Git failed with a fatal error.
Git failed with a fatal error.
unable to access 'https://github.com/nkdAgility/azure-devops-migration-tools.git/': The requested URL returned error: 403

Failed to push to the remote repository. See the Output window for more details.

For contributing changes the normal procedure is to "fork" the repo first. https://simpleit.rocks/git/contributing-to-a-github-repository-step-by-step.

Once you have forked, you create a branch in your own copy of the repo. This you will be able to push as you own the repo. Then create PR to sideload your changes from your copy of the repo to me.

Done.
PR created:
#1761

This was my first contribution and that is why I wasn't aware of the process.
In addition to the bugfix it is better unit tests to be added as well. I have tried but the code is hard to be tested easy.

Im so happy that you were able to figure it out :)

You are now immortalised as a contributor.

Note that you can add multiple emails to a single account in GitHub so that you have all your contributions in one place.