Here is the simplified version of our gated check-in flow for a successful check-in:
- Apply shelveset (build agent)
- Build (build agent)
- Revert Shelveset from Workspace (build agent)
- Check in gated changes (CheckInGatedChanges activity on the controller)
- 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.
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:
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:filaA.txt
fileA.txt
and bypasses gated checkintf checkin /shelfset /force
and thus overwrites the changes from developer 2Instead, 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:
tf undo $/ /recursive
tf get /recursive /version:T
(get latest)tf checkin $/ /recursive
), don't use/force
If anything fails:
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.