Undoing "Get Latest Version"

To start off my blog, I'm going to talk about a feature I've occasionally wished TF Version Control had, and some alternatives that you may find acceptable.

From the documentation for "tf.exe get":

"If you work in a team development environment, Get is the command you use most frequently because getting every file in the current project synchronizes with your team and makes sure that you have the very latest copy of shared work." 

What that doesn't mention is sometimes the very latest copy of some code in a dark corner of your workspace will fail to build - horribly.  You just did a full sync, built and regret it.  You're unwilling or unable to resolve the build issues. If only you could have your old workspace back - you know that built.  But how do you get it back?  "tf undo" undoes pending changes, not the last sync you performed.

If you didn't plan for this, there may not be anything you can do after the fact.  If you have an idea of when you last sunc, you can use "Get Specific Version..." with a date or changeset estimate.  Maybe you can find a recent successful build on your builds drop share and relate that back to a changeset/date.  If syncing and building doesn't take long, maybe you can use trial and error.

Here are some ideas on preparing for a sync in case you've been bitten by this and are now wary:

  • Use "tf get /preview" to see what changes a full sync will make.  Maybe that will help you decide if you're willing to risk syncing.
  • While your workspace is in a good state, figure out when you last sunc.  If your workspace has a single root, you can use "tf history /r /version:C1~W /stopafter:1 D:\my\workspace\root" and note the changeset number.  This is the changeset number you'll want to return to if you don't like tip. (If you have several roots to your workspace you'll have to run it several times and remember the highest number.) There are some cases where this will be inaccurate because there's no one changeset number or point in time that lines up with the file versions you have in your workspace.  For example, you've checked in more recently than you've sunc, you've performed gets that were less than full workspace gets, or you or your team project have "Get Latest on Checkout" turned on.  (Credit to Chandru for the tip.)
  • Label all the files in your current workspace. This gets around the limitation of trying to represent your workspace state with a single changeset. (Delete a label before re-using it for this, because file deletions don't get labeled.)
  • If you always sync from the command-line, write a tf.bat wrapper to log tf calls with the date, time, current directory and arguments.  You can refer to this log to figure out when you previously sunc.  Here's my attempt:

@echo off

rem Only log Gets
if /i "%1" neq "get" goto :RUNTF

rem log to internal\tf_invokes.log, relative to this script's path.
set LOG=%~dp0internal\tf_invokes.log
echo.>>%LOG%

rem format the date in .Net's SortableDateTimePattern. Like UTC but using the local timezone.
for /f "tokens=1,2,3,4 delims=/ " %%a in ("%DATE%") DO set SDTDATE=%%d-%%b-%%c
set SDTTIME=%TIME%
if "%SDTTIME:~0,1%" == " " set SDTTIME=0%SDTTIME:~1%
set BEGIN=%SDTDATE%T%SDTTIME%

echo //[%BEGIN% :: ] [%CD%] [%*]>>%LOG%

:RUNTF
rem actually run the command
tf.exe %*
set TFERRORLEVEL=%ERRORLEVEL%

rem Only log Gets
if /i "%1" neq "get" goto :end

for /f "tokens=1,2,3,4 delims=/ " %%a in ("%DATE%") DO set SDTDATE=%%d-%%b-%%c
set SDTTIME=%TIME%
if "%SDTTIME:~0,1%" == " " set SDTTIME=0%SDTTIME:~1%
set END=%SDTDATE%T%SDTTIME%

rem log the end time as well in case its a long running operation.
echo \\[%BEGIN% :: %END%] [%CD%] [%*]>>%LOG%

:END
exit /b %TFERRORLEVEL%