List files that are in the TFS repository but not in the Visual Studio solution - visual-studio

We have big solution(300+ projets), which is evolving since a long time(7 years), and over the time, there has been a lot of refactoring(projects removed, moved, ...). We did notice that sometimes when some projects are deleted from the solution, they are not deleted from the solution, and also that we have some leftover files.
We would like to find all those kind of files in order to delete most of them(not all).
Is there a way to list all those files?
(We have Visual Studio 2015 enterprise)

Check this PowerShell script from this case, which should do what you are looking for. It parses the project file to get the included code files. Then it compares that list to the actual files on disk. The remaining files are your unused/obsolete files.
The script can either delete the unused files from disk or pend them as deletes in TFS.
<#
.SYNOPSIS
Find and process files in a project folder that are not included in the project.
.DESCRIPTION
Find and process files in a project folder that are not included in the project.
Options to delete the files or to add them as pending deletes for TFS. Use TF.exe to pend the deletes and start the check-in process for the files.
This is necessary when trying to delete files that are not currently included in a Visual Studio project.
.PARAMETER Project
The path/name for the project file.
.PARAMETER VsVersion
The Visual Studio version (10, 11, 12). Used to locate the tf.exe file.
.PARAMETER DeleteFromDisk
Just delete the files from disk. No interaction with any source control.
.PARAMETER TfsCheckin
After pending the deletes, open the check-in dialog.
#>
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)]
[string]$Project,
[Parameter(Mandatory=$false)]
[ValidateRange(10,12)]
[int] $VsVersion = 12,
[switch]$DeleteFromDisk,
[switch]$TfsCheckin
)
$ErrorActionPreference = "Stop"
$tfPath = "${env:ProgramFiles(X86)}\Microsoft Visual Studio $VsVersion.0\Common7\IDE\TF.exe"
$projectPath = Split-Path $project
if($Project.EndsWith("csproj"))
{
$fileType = "*.cs"
}
else
{
$fileType = "*.vb"
}
$fileType
$projectFiles = Select-String -Path $project -Pattern '<compile' | % { $_.Line -split '\t' } | `
% {$_ -replace "(<Compile Include=|\s|/>|["">])", ""} | % { "{0}\{1}" -f $projectPath, $_ }
Write-Host "Project files:" $projectFiles.Count
$diskFiles = gci -Path $path -Recurse -Filter $fileType | % { $_.FullName}
Write-Host "Disk files:" $diskFiles.Count
$diff = (compare-object $diskFiles $projectFiles -PassThru)
Write-Host "Excluded Files:" $diff.Count
#create a text file for log purposes
$diffFilePath = Join-Path $projectPath "DiffFileList.txt"
$diff | Out-File $diffFilePath -Encoding UTF8
notepad $diffFilePath
#just remove the files from disk
if($DeleteFileOnly)
{
$diff | % { Remove-Item -Path $_ -Force -Verbose}
}
else #TFS options
{
#this will add the files as pending deletes in TFS (awaiting check-in)
$diff | % {
[Array]$arguments = #("delete", "`"$_`"")
& "$tfPath" $arguments
}
if($Checkin)
{
#start the check-in process for the pending deletes
[Array]$arguments = "checkin", "/recursive", "$projectPath"
& $tfPath $arguments
}
}
Also, other community members expend this script and share the scripts at:
#Marc Climent: I used this script to create a more detailed one that includes other types of files and does not use TFS: https://gist.github.com/mcliment/d9008a9288cea9d088af
#mikesigs: I too used this file as well as #MarcCliment's to create yet another PowerShell script that takes a .sln file instead of a single proj file. It deletes all files excluded from all projects in the provided solution: https://gist.github.com/mikesigs/3512dbccc1767d447977#file-deleteexcludedfiles-ps1

Related

Extension/Tool to perform cleanup on project

I have been recently using Visual Studio 2017 and I'd like to have an external tool/extension/setting which deletes everything besides .sln, .vcxproj and sources. I have already tried CLean Project and Clean Solution extension but neither of those removes Debug folder. I have read something about PowerShell scripts but I have no idea how to use them and I don't want to run unknown code on my console.
PS: I know that VS has the cleanup function, but it only deletes executables. I also read something about modifying the project properties but that would be really unpleasant for many projects.
PSS: I am a student and I have many project directories. All I want is to have a neat way to store them.
PSSS: I have already configured my .gitignore file and I am using git. IS there a way to use it to perform cleanup?
You're using Git. You can simply reset your workspace to the last commit, removing all unversioned and ignored files. First make sure you have no pending changes, then perform a clean:
git clean -xdfn
-x: ignore the ignores (removes bin, obj, *.dll, ...)
-d: remove directories in addition to files
-f: force Git to actually do the job
-n: perform a dry run, which will list the files that will be removed.
Remove the n from the arguments to actually clean the workspace.
See also How do I clear my local working directory in git?.
Here a script I'm using that works well for me.
# PowerShell script that recursively deletes all 'bin' and 'obj' (or any other specified) folders inside current folder
$CurrentPath = (Get-Location -PSProvider FileSystem).ProviderPath
# recursively get all folders matching given includes, except ignored folders
$FoldersToRemove = Get-ChildItem .\ -include bin,obj -Recurse | where {$_ -notmatch '_tools' -and $_ -notmatch '_build'} | foreach {$_.fullname}
# recursively get all folders matching given includes
$AllFolders = Get-ChildItem .\ -include bin,obj -Recurse | foreach {$_.fullname}
# subtract arrays to calculate ignored ones
$IgnoredFolders = $AllFolders | where {$FoldersToRemove -notcontains $_}
# remove folders and print to output
if($FoldersToRemove -ne $null)
{
Write-Host
foreach ($item in $FoldersToRemove)
{
remove-item $item -Force -Recurse;
Write-Host "Removed: ." -nonewline;
Write-Host $item.replace($CurrentPath, "");
}
}
# print ignored folders to output
if($IgnoredFolders -ne $null)
{
Write-Host
foreach ($item in $IgnoredFolders)
{
Write-Host "Ignored: ." -nonewline;
Write-Host $item.replace($CurrentPath, "");
}
Write-Host
Write-Host $IgnoredFolders.count "folders ignored" -foregroundcolor yellow
}
# print summary of the operation
Write-Host
if($FoldersToRemove -ne $null)
{
Write-Host $FoldersToRemove.count "folders removed" -foregroundcolor green
}
else { Write-Host "No folders to remove" -foregroundcolor green }
Write-Host
# prevent closing the window immediately
$dummy = Read-Host "Completed, press enter to continue."
Copy and paste in a new file in the same directory of your .sln file.
I call it "CleanAll.ps1" but you can call it as you prefer.
The easiest way to run it?
Right-Click on the file > Run with powershell
It recursively delete bin and obj files of all sub-folders starting from the current path. You can of course personalize and add "debug" or any other folder once you get more familiar with the script.

ProjectItems.AddFromFile Adds File to Pending Changes

As part of my nuget package, I have an install.ps1 powershell script that I am using to add a reference file to the project (a couple text documents) from the package's tools folder.
Everything is working great, except that when the files are referenced in a TFS solution, they are added to the Team Explorer Pending Changes. How can I remove them from pending changes (or keep them from ever showing up)? I don't want these checked into TFS, since the packages folder shouldn't be there in the first place.
Here's my install.ps1 script:
param($installPath, $toolsPath, $package, $project)
#Add reference text files to the project and opens them
Get-ChildItem $toolsPath -Filter *.txt |
ForEach-Object {
$projItem = $project.ProjectItems.AddFromFile($_.FullName)
If ($projItem -ne $null) {
$projItem.Properties.Item("BuildAction").Value = 0 # Set BuildAction to None
}
}
If you're using local workspaces (TFS 2012+) you can use the .tfignore file to exclude local folders and files from appearing in the Pending Changes page in Team Explorer.
You can configure which kinds of files are ignored by placing text file called .tfignore in the folder where you want rules to apply.
.tfignore file rules
The following rules apply to a .tfignore file:
- \# begins a comment line
- The \* and ? wildcards are supported.
- A filespec is recursive unless prefixed by the \\ character.
- ! negates a filespec (files that match the pattern are not ignored)
.tfignore file example
######################################
# Ignore .cpp files in the ProjA sub-folder and all its subfolders
ProjA\*.cpp
#
# Ignore .txt files in this folder
\*.txt
#
# Ignore .xml files in this folder and all its sub-folders
*.xml
#
# Ignore all files in the Temp sub-folder
\Temp
#
# Do not ignore .dll files in this folder nor in any of its sub-folders
!*.dll
Details: https://www.visualstudio.com/docs/tfvc/add-files-server#customize-which-files-are-ignored-by-version-control
I finally figured out how to do it using tf.exe. Calling tf vc undo with the full filename will undo pending changes for those files. And if the folder isn't tied to TFS, no harm done. It just continues on.
This implementation does require VS 2015 to be installed (due to the hardcodes path to the IDE folder), so I'm looking for a better way to obtain the IDE path of the currently loaded IDE. For now though, this solves my current issue.
param($installPath, $toolsPath, $package, $project)
$idePath = "$env:VS140COMNTOOLS..\IDE"
$tfPath = "$idePath\tf.exe"
Get-ChildItem $toolsPath -Filter *.txt |
ForEach-Object {
$projItem = $project.ProjectItems.AddFromFile($_.FullName)
If ($projItem -ne $null) {
$projItem.Properties.Item("BuildAction").Value = 0 # Set BuildAction to None
$filename = $_.FullName
& $tfPath vc undo `"$filename`" # Remove File from TFS Pending Changes, as AddFromFile can automatically add it
}
}

Remove TFS Connection From Solution

How to make solution as clean copy without mapping to TFS ? The problem is that this message shows when I am trying to open it. I want to open it as normal without TFS connection.
To completely remove TFS source control binding follow these two steps:
Go to your solution's folder, find and delete all files with *.vssscc and *.vspscc extensions.
Open your solution's .sln file in Notepad, and find & remove the GlobalSection(TeamFoundationVersionControl) section.
More details on reference Link
If you want to permanently and completely detach the solution from source control, then try the following:
Click the 'No' button to avoid connecting to TFS.
In the file menu, go to the source control options and clear the bindings. You'll specifically want File - Source Control - Advanced - Change Source Control...
Save the solution.
Next time you open the solution you won't be prompted to connect to TFS.
Edit the solution file and remove the following section from it. It won't be the same but will be similar.
Note:To edit the solution file go to the project folder then open the YouSolutionName.sln file with notepad.
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 2
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = <YourTFSURL>
SccLocalPath0 = .
SccProjectUniqueName1 = .
SccLocalPath1 = .
EndGlobalSection
I don't have enough reputation to comment, but just wanted to add that Tabish's solution does in fact work correctly to completely remove the solution from source control, especially when the TFS server is not reachable for one reason or another (e.g. you downloaded a project that the author did not remove from their own source control before uploading).
However, to completely remove all traces of source control from the project and avoid the warnings that are noted in the other comments to that answer (e.g. "The mappings for the solution could not be found..."), you must also remove the following lines from every project file in the solution (apparently these used to be in the solution file in earlier versions of VS, but in VS2017 they are found in the project file for each project in the solution - e.g. [project].csproj):
SccProjectName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
SccAuxPath = "x"
SccLocalPath = "xxx"
SccProvider = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Thanks to the marked answer and other comments here for pointing this out:
visual source safe - how to remove bindings from solution w/o opening in visual studio
Combining this with Tabish's answer seems to be the most complete method of manually removing a solution from source control.
To remove the binding you can use Visual Studio : Menu File / Source Control / Advanced / Change Source Control.
You can also do it yourself by removing any SCC... from sln and csproj.
If you often export source files, you can use ExportSrc. It has many options such as remove TFS binding (ON by default).
Most of the answers provided a solution, but I would rather use a solution provided by Visual Studio 2017.
On the Menu bar of Visual Studio, go to Team and select Disconnect from Team Foundation Server.
That's it.
I just inherited a collection of TeamFoundation projects following an M&A buyout and tech transfer. There were like 30+ solutions and a buttload of *.vssscc and *.vspscc files.
Based on everyone's input above, I wrote a PowerShell function to recurse a specified root folder, delete the files, then edit the solution files to remove the TeamFoundationVersionControl section.
Usage is Remove_TFSfiles "pathname" $booleanflag.
To see what files would be affected, use $false (uses -whatif):
Remove_TFSfiles "C:\MyDevFolder" $false
To actually delete those files, use $true:
Remove_TFSfiles "C:\MyDevFolder" $true
Here's the function:
Function Remove_TFSfiles {
param(
[string]$FolderName = $(throw "-FolderName is required."),
[bool]$RemoveFiles = $(throw "-RemoveFiles (either $true or $false) is required.")
)
$TFSExtensions = '*.vspscc', '*.vssscc'
if ($RemoveFiles) {
Get-ChildItem -path $FolderName -force -include $TFSExtensions -Recurse | Remove-Item -Force
# Now iterate through any solution files, and whack the TeamFoundationVersionControl section
Get-ChildItem -Path $FolderName -Filter "*.sln" -Recurse | ForEach-Object {
$slnFilename = $_.Fullname
Write-Host -NoNewline "Editing $slnFilename... "
$File = Get-Content $slnFilename -raw
$Filestr = [regex]::escape("" + $File + "")
# The regex escapes must themselves be meta-escaped, therefore "\(" becomes "\\" + "\(" = "\\\(". Did I mention I hate regex?
$Result = $Filestr -replace "\\tGlobalSection\\\(TeamFoundationVersionControl\\\).*?EndGlobalSection\\r\\n", ""
$result = [regex]::unescape($result)
Set-ItemProperty $slnFilename IsReadOnly $false
$result | Set-Content $slnFilename
Write-Host "Done"
}
Write-Host -f red "Finished actually removing files and editing *.sln files"
}
else {
Get-ChildItem -path $FolderName -force -include $TFSExtensions -Recurse | Remove-Item -WhatIf
Write-Host -f green "Finished pretending to remove files"
# Now iterate through any solution files, and whack the TeamFoundationVersionControl section
Get-ChildItem -Path $FolderName -Filter "*.sln" -Recurse | ForEach-Object {
$slnFilename = $_.Fullname
Write-Host "Not Editing $slnFilename"
}
}
}

Compare two folders recursively and save repeated files

I'm trying to do something pretty weird. Any alternative solution could be analyzed but the process where I'm working is very difficult to change.
I have a folder to deploy in an IIS web server. This folder can contain any file such as dll, asmx, web.config, .exe, etc. We have a bat script that performs a full backup but a simple file deploy can last for an hour because of this.
I want some utility or to make a powershell script to compare the folder to deploy with the destination folder, and only backup the files (or folder) that has the same files on the first folder. This has to be done recursively and it also has to preserve folder structure in the destination server.
Edit: I'm currently working on a powershell script that will go like this (i'm definitely not a powershell expert):
Compare-Object $d1 $d2 | Where-Object {$_.SideIndicator -ne "=>" -and $_.InputObject -ne "*.ok*" -and $_.InputObject -ne "*.bat*" }
Any help or recommendation would be appreciated!
Regards
You won't be able to maintain file structure using compare-object... I got bored so here you go.
$sPath = "C:\test\"
$dPath = "C:\test2\"
$src = gci -recurse $sPath
$dest = gci -recurse $dPath
$src | % {
foreach($item in $dest) {
$t1 = ($item.FullName).trimStart($dpath)
$t2 = ($_.FullName).trimStart($sPath)
if($t1 -eq $t2) {copy-item $item.FullName $_.FullName -Force}
}
}
Just set $spath to src directory and $dPath to your deployment directory, and let her rip.

How to get the IDE to update solution explorer window after adding project items in nuget ps?

During nuget install I give the user a command they can run. This command basically scans some files and creates some code templates and then inserts them into the current project. This works just fine - except for the fact that Solution Explorer does not update its tree view with the new files. I know this works because I can unload and reload the project file and the files are there.
In case it helps, here is the code I use to add the files to the project - the second function is what the user actually calls.
function add-to-project ($itemType, $project)
{
process
{
$bogus = $project.Xml.AddItem($itemType, $_)
}
}
# Parse a file
function Write-TTree-MetaData ($Path = $(throw "-Path must be supplied"))
{
$p = Get-Project
Write-Host "Inserting the results of the parsing into project" $p.Name
$ms = Get-MSBuildProject
$destDir = ([System.IO.FileInfo] $p.FullName).Directory
# Run the parse now
CmdTFileParser -d $destDir.FullName $Path
# Now, attempt to insert them all into the project
$allFiles = Get-ChildItem -Path $destDir.FullName
$allFiles | ? {$_.Extension -eq ".ntup"} | add-to-project "TTreeGroupSpec" $ms
$allFiles | ? {$_.Extension -eq ".ntupom"} | add-to-project "ROOTFileDataModel" $ms
# Make sure everything is saved!
$ms.Save()
$p.Save()
}
This code causes a funny dialog to pop up - "The project has been modified on disk - please reload" - and hopefully the user will reload, and then the files show up correctly... But it would be nice to avoid that and just have the script cause whatever is needed to happen. Perhaps I have to figure out how to unload and re-load the project?
What do I need to do to force solution explorer to update? Many thanks!
By using the MSBuild project you are bypassing Visual Studio and directly updating the MSBuild project file on disk. The easiest way to get Visual Studio to update the Solutions Explorer window is to use the Visual Studio project object instead which you get from the Get-Project command.
Below is a simple example which adds a file to the solution and changes its ItemType to be ROOTFileDataModel. The example assumes you have a packages.config file in your project's root directory which is not currently added to the project so it is not showing in Solution Explorer initially.
# Get project's root directory.
$p = Get-Project
$projectDir = [System.IO.Path]::GetDirectoryName($p.FileName)
# Add packages.config file to project. Should appear in Solution Explorer
$newFile = [System.IO.Path]::Combine($projectDir, "packages.config")
$projectItem = $p.ProjectItems.AddFromFile($newFile)
# Change file's ItemType to ROOTFileDataModel
$itemType = $projectItem.Properties.Item("ItemType")
$itemType.Value = "ROOTFileDataModel"
# Save the project.
$p.Save()
The main Visual Studio objects being used here are the Project, ProjectItem and the ProjectItems objects. Hopefully the above code can be adapted to your specific requirements.

Resources