views:

395

answers:

3

Someone (other than myself) accidentally deleted files in TFS and checked it in. He then discovered his error and wanted to replace the lost files, which he did - from his own hard drive. In between his error and my discovery of it, others have made changes to adjacent files. Now, I want to roll the deleted files back to their state before the delete, but when I try I get a filename collision error between (if I understand it correctly) the original files and his replacements.

I can't roll the entire project back since there has been other work done, I just want to get these files back to how they were "before".

Has anyone had this problem and solved it? Or is there no solution.

A: 
  • Roll back your solution to another working copy.
  • Pull the deleted files from that working copy and them back into your current working copy
  • The checkin those files into the current project.

Only problem I can see here, is you may lose history on the deleted file.

Anthony Shaw
This doesn't address the namespace conflict he's running into.
Richard Berg
A: 

Sounds like a job for the Power Tools. Here you go:

$deletedFiles = 
    Get-TfsChangeset 12345| 
    % { $_.changes } | 
    ? { $_.changetype.tostring().contains("Delete") } |
    % { $_.item.serveritem }

$deletedFiles | 
    Add-TfsPendingChange -Delete | 
    New-TfsChangeset -Comment "Delete mistakenly re-added files"

$deletedFiles |    
    Get-TfsChildItem -Deleted |
    ? { $_.changesetid -eq 12345 } |
    # this bit of ugliness is required by a bug in the Power Tools
    % { "$($_.serveritem);X$($_.deletionid)" } | 
    Add-TfsPendingChange -Undelete |
    New-TfsChangeset -Comment "Undelete old files"

As alluded to in the comment, the ugly string parsing in the final pipeline shouldn't be necessary in an ideal Powershell world. If things worked as intended, you could remove that line (or equivalently, replace it with Select-TfsItem | ). Unfortunately, the Power Tools don't appear to handle deletion IDs as well as I intended them to.

Anyway, this script should do what you asked. Notes:

  • replace 12368 with the changeset where the user accidentally deleted the files
  • if desired, replace the delete + checkin in the 2nd pipeline with Destroy
  • this doesn't revert any other changes in the system (including other changes in #12345), just the ones causing the filename collision
Richard Berg
A: 

Thank you all for your input. For future reference, this is how I finally solved the case:

Firstly, I took a local copy of the file.

In Source Control Explorer, I then rolled back the folder containing the file to the changeset where the old file had been deleted but the new file was not yet present. I undeleted the file and checked in, thus bringing the original back "from beyond".

After that, it was pretty much a case of putting my local copy and the recovered file into a diff tool (I used BeyondCompare BTW) and correct the recovered file to match the new version.

I could do this due to a couple of reasons, namely:

  • The deletion and the re-adding of the file was done in two consecutive changesets.
  • The difference between the recovered file and the local copy was "pretty minor" (i.e., an added method and a couple of change hacks in another).

I have no idea what would have happened if we for instance hadn't missed the file in question until a couple of weeks later, where we would have had several changes made to the surrounding code. I also have no clue as to what any substantial changes made to the local version had meant in terms of recoverability.

Streamcap