-->

How to reconcile TFS shelveset correctly after a s

2019-08-03 21:24发布

问题:

Here is the simplified version of our gated check-in flow for a successful check-in:

  1. Apply shelveset (build agent)
  2. Build (build agent)
  3. Revert Shelveset from Workspace (build agent)
  4. Check in gated changes (CheckInGatedChanges activity on the controller)
  5. Get the Changeset resulted from checking in the gated changes. (build agent)

This flow is very problematic. Indeed, suppose user A commits (submits to the gate) 100 source files affecting all the projects in the solution and then user B commits just 1 source file affecting just one project. How big would be the gated check-in build for the user B on the build agent?

The answer is that user B is going to "suffer" the same build as the user A.

The root cause: we undo the shelveset before checking in the gated changes and then get them again, this time in the form of a changeset. This bumps up the timestamps of the source files, making them newer then the binaries produced a moment ago from the same files.

That is a problem.

How do I solve it?

EDIT

Here what happens if I do not revert the shelveset, but get the respective changeset right away:

PS D:\tfs\DFGatedCheckInTest2> dir 1.txt


    Directory: D:\tfs\DFGatedCheckInTest2


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        10/24/2014  10:36 AM         12 1.txt


PS D:\tfs\DFGatedCheckInTest2> tf get /version:C105656
D:\TFS\DFGatedCheckInTest2:
Conflict 1.txt - Unable to perform the get operation because you have a conflicting edit
Automatically resolved conflict: edit: D:\TFS\DFGatedCheckInTest2\1.txt as TakeTheirs
Undoing edit: 1.txt
PS D:\tfs\DFGatedCheckInTest2> dir 1.txt


    Directory: D:\tfs\DFGatedCheckInTest2


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-ar--        10/24/2014  11:42 AM         12 1.txt


PS D:\tfs\DFGatedCheckInTest2>

Notice the timestamp of the file. It was bumped up. We achieved nothing.

回答1:

The reason why tf get /version:C12345 updates the time stamp is due to that being the default behavior for it in 2010. This was changed in 2012 and made configurable.

In Visual Studio 2012/TFS 2012 a feature was added that controls the file timestamp on get, read from Brian Harry's post from way back then:

Restore file modification time

When TFS gets files on to your local disk, it always sets the file modification time to the date/time that the get operation happened. There are some work practices where this is problematic. Some practices use the date stamp on the file for incremental deployment or other kinds of change management. SourceSafe had 3 options for setting the time stamp on files:

The time the file is gotten (this was the default and works very well in concert with make and other similar build dependency trackers). The modification time that the file had when it was last edited before checkin. The date/time that the file was checked in. TFS 2010 and before only supported option #1. In TFS 11, we have added support for option #3. It can be set on a workspace by workspace basis. We plan to add support for #2 in the future but haven’t gotten there yet.

source

Back to the source of the issue

As you mention in your other question, you're using tf checkin /shelfset /force, which is where your problem lies. As the answer in your other question explains, that checkin goes directly against the server, the workspace on the server is unaffected and as such is left with the pending changes of unshelving that same shelfset.

tf checkin /force is also very dangerous in case anther developer had used by-pass gated build to check in changes. TFS will assume you know what you're doing and will overwrite these changes. So:

  1. Developer 1 checks in filaA.txt
  2. Build server starts gated checkin
  3. Developer 2 checks in fileA.txt and bypasses gated checkin
  4. Build server finishes and uses tf checkin /shelfset /force and thus overwrites the changes from developer 2

Instead, what the normal TFS workflow does, is check in the local changes on the workspace of the build agent. tf checkin $/ /recursive and it deletes the shelfset at the end of the build, in case of a succesful build and checkin.

This tells the build server to check-in the local changes in the workspace, and it will now know that it has the latest version, and won't have to update the time stamp. Next time the build is triggered the build agent will start with a get latest (to ensure that any bypass checkins are also fetched) and it will know these files are already up to date.

So in general your workflow, on the agent, should look like:

  1. If workspace does not exist, create workspace and mappings
  2. If there are any pending changes, undo them tf undo $/ /recursive
  3. Perform tf get /recursive /version:T (get latest)
  4. Unshelve your shelfset to the workspace of the agent
  5. Build the code
  6. Check in the local pending changes (tf checkin $/ /recursive), don't use /force
  7. If all of that's succesful, delete the shelfset

If anything fails:

  1. Undo all pending changes to reconcile the workspace
  2. Leave the shelfset in tact
  3. Fail the build.

That way the local workspace will better reflect what's going on and you won't have to perform a tf get /version:c12345 to mess up your dates.



标签: tfs tfs2010