You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As per the [documentation]({{< ref "docs/reference/tools/tfs-node-structure-tool" >}}), you need to add Iteration Maps and Area Maps that adapt the old locations to new ones that are valid in the Target.
11
11
12
+
**NOTE: It is NOT possible to migrate a work item if the Area or Iteration path does not exist on the target project. This is because the work item will be created with the same Area and Iteration path as the source work item. If the path does not exist, the work item will not be created. _There is no way around this!_**
13
+
12
14
Before your migration starts, it will validate that all of the Areas and Iterations from the **Source** work items revisions exist on the **Target**. Any that do not exist will be flagged in the logs, and if you have `"StopMigrationOnMissingAreaIterationNodes": true,` set, the migration will stop just after it outputs a list of the missing nodes.
13
15
14
16
Our algorithm that converts the Source nodes to Target nodes processes the [mappings]({{< ref "docs/reference/tools/tfs-node-structure-tool" >}}) at that time. This means that any valid mapped nodes will never be caught by the `This path is not anchored in the source project` message, as they are already altered to be valid.
15
17
16
18
> We recently updated the logging for this part of the system to more easily debug both your mappings and to see what the system is doing with the nodes and their current state. You can set `"LogLevel": "Debug"` to see the details.
17
19
18
-
To add a mapping, you can follow [the documentation]({{< ref "docs/reference/tools/tfs-node-structure-tool" >}}) with this being the simplest way:
20
+
## Configuration Format
21
+
22
+
The modern configuration uses structured mappings with `Match` and `Replacement` properties, utilizing the new `Iterations.Mappings` and `Areas.Mappings` format:
If the old iteration path was `\oldproject1\Custom Reporting\Sprint 13`, this would result in a match for each Iteration node after the project node. You would then be able to reference any of the nodes using "$" and the number of the match.
52
107
108
+
### Important Notes About Escaping
109
+
53
110
Regular expressions are much more difficult to build and debug, so it is a good idea to use a [regular expression tester](https://regex101.com/) to check that you are matching the right things and to build them in ChatGPT.
54
111
55
-
_NOTE: You need `\\` to escape a `\` in the pattern, and `\\` to escape a `\` in JSON. Therefore, on the left of the match, you need 4 `\` to represent the `\\` for the pattern and only 2 `\` in the match._
112
+
**Special Character Escaping Warning:** Special characters in the acceptation of regular expressions _and_ JSON both need to be escaped. For the Match property, this means, for example, that a literal backslash must be escaped for the regular expression language `\\`_and_ each of these backslashes must then be escaped for the JSON encoding: `\\\\`. In the Replacement property, a literal `$` must be escaped with an additional `$` if it is followed by a number (due to the special meaning in regular expression replacement strings), while a backslash must be escaped (`\\`) due to the special meaning in JSON.
113
+
114
+
**Advice:** To avoid unexpected results, always match terminating backslashes in the search pattern and replacement string: if a search pattern ends with a backslash, you should also put one in the replacement string, and if the search pattern does not include a terminating backslash, then none should be included in the replacement string.
115
+
116
+
_NOTE: You need `\\` to escape a `\` in the pattern, and `\\` to escape a `\` in JSON. Therefore, in the Match property you need 4 `\` to represent the `\\` for the pattern and only 2 `\` in the Replacement property._
117
+
118
+
## Some Useful Mapping Patterns
119
+
120
+
### Simple Project Rename with Path Preservation
121
+
122
+
```json
123
+
"Iterations": {
124
+
"Mappings": [
125
+
{
126
+
"Match": "^OldProjectName([\\\\]?.*)$",
127
+
"Replacement": "NewProjectName$1"
128
+
}
129
+
]
130
+
},
131
+
"Areas": {
132
+
"Mappings": [
133
+
{
134
+
"Match": "^OldProjectName([\\\\]?.*)$",
135
+
"Replacement": "NewProjectName$1"
136
+
}
137
+
]
138
+
}
139
+
```
140
+
141
+
This maps all `OldProjectName` area or iterations to a similar new node, preserving the entire path structure. If you have `ShouldCreateMissingRevisionPaths` enabled, it will create missing nodes automatically.
56
142
57
-
##Some pretty cool mappings
143
+
### Project Rename to Root Only
58
144
59
145
```json
60
-
"^OldProjectName([\\\\]?.*)$": "NewProjectName$1"
146
+
"Iterations": {
147
+
"Mappings": [
148
+
{
149
+
"Match": "^OldProjectName([\\\\]?.*)$",
150
+
"Replacement": "NewProjectName"
151
+
}
152
+
]
153
+
},
154
+
"Areas": {
155
+
"Mappings": [
156
+
{
157
+
"Match": "^OldProjectName([\\\\]?.*)$",
158
+
"Replacement": "NewProjectName"
159
+
}
160
+
]
161
+
}
61
162
```
62
163
63
-
or
164
+
This maps all `OldProjectName` paths to just the new project name root, effectively flattening the hierarchy.
165
+
166
+
### Replacing PrefixProjectToNodes
167
+
168
+
The deprecated `PrefixProjectToNodes` option can be replaced with explicit mappings:
64
169
65
170
```json
66
-
"^OldProjectName([\\\\]?.*)$": "NewProjectName"
171
+
"Iterations": {
172
+
"Mappings": [
173
+
{
174
+
"Match": "^SourceServer\\\\(.*)$",
175
+
"Replacement": "TargetServer\\SourceServer\\$1"
176
+
}
177
+
]
178
+
},
179
+
"Areas": {
180
+
"Mappings": [
181
+
{
182
+
"Match": "^SourceServer\\\\(.*)$",
183
+
"Replacement": "TargetServer\\SourceServer\\$1"
184
+
}
185
+
]
186
+
}
67
187
```
68
188
69
-
The first one maps all `OldProjectName` area or iterations to a similar new node. If you have `CreateMissingNodes` enabled, it will create that. The second will just map all `OldProjectName` to the new project name root.
189
+
This prepends the source project name to the target set of nodes, useful when the target project already has nodes and you don't want to merge them together.
Here's a complete example showing the TfsNodeStructureTool configuration with both Areas and Iterations mappings:
223
+
224
+
```json
225
+
{
226
+
"$type": "TfsNodeStructureToolOptions",
227
+
"Enabled": true,
228
+
"Areas": {
229
+
"Filters": [],
230
+
"Mappings": [
231
+
{
232
+
"Match": "^Skypoint Cloud$",
233
+
"Replacement": "MigrationTest5"
234
+
}
235
+
]
236
+
},
237
+
"Iterations": {
238
+
"Filters": [],
239
+
"Mappings": [
240
+
{
241
+
"Match": "^Skypoint Cloud\\\\Sprint 1$",
242
+
"Replacement": "MigrationTest5\\Sprint 1"
243
+
}
244
+
]
245
+
},
246
+
"ShouldCreateMissingRevisionPaths": true,
247
+
"ReplicateAllExistingNodes": true
248
+
}
75
249
```
250
+
251
+
For more detailed information and advanced configuration options, refer to the complete [TFS Node Structure Tool documentation]({{< ref "docs/reference/tools/tfs-node-structure-tool" >}}).
0 commit comments