Renaming and deleting files in projects under source control in Visual Studio.
(the good, the bad and the ugly)

One of the often complaints newsgroups about Visual Studio .NET 2003 and older is that when a file in a controlled project is deleted or renamed, the corresponding file in the source control database is not deleted or renamed, too. The user has to spend time opening the source control manager interface, and delete or rename manually the file int he database. If he doesn't do that, in time the database becomes "polluted" with older unused files, or with duplicate files, files might lose their history, etc.

Let's talk first about Visual Studio .NET 2003

In Visual Studio 2003 and older, when you rename or delete a file or folder you might be prompted to remove the file from project or to delete the file on the local disk. This is dependent on the project type you're working with. The dialog is displayed by the project system, and source control has no control over it. (Some source control providers complained the local file is deleted without being notified - please understand this is totally controlled by the project system and outside scci control; you'll just have to deal with this situation...)

As for the file in the source control database, source control integration does not attempt to delete or rename it to match the local disk changes, despite the fact that MSSCCI interface used to communicate with the provider has the necessary functions (SccRename and SccRemove). This might cause some pain in a couple of scenarios, when the user might be required to manually rename or delete the files in the source control database.
Believe me or not, but this behavior was a conscious decision made at that time. In VisualStudio .NET 2002 and 2003 we decided to play on the safe side, even though it might add some ugliness in delete and rename scenarios. The goal was that at any time, a user should be able to enlist in a controlled project, and be able to build it without problems. At any time a Get operation on the solution or project was supposed to leave the user's enlistment in a good, buildable state. Let's analyze a couple of scenarios:

    A) User1 creates a project and adds it to source control
       User1 deletes a file in the project. 
         Result: The project is checked out, the file is removed from project and only the local file is deleted.
       Later, User1 changes his mind, and UndoCheckout the project file.
         Result: The old version of the project is retrieved, the missing local file is got back from source control.
         Life is good, user1 can safely build his project.

    B) User1 creates a solution and adds it to source control
       User1 deletes a file in a project.
         Result: The project is checked out, the file is removed from project and only his local file is deleted.
       User1 does not checkin at this time, because he has other changes that he hasn't verified yet.
       User2 enlists in the solution by opening from source control
         Result: The solution is retrieved from source control, user2 can build safely his project.
               
    C) User1 creates a solution and adds it to source control
       User2 enlists in the solution by opening from source control
       User1 deletes a file in a project.
         Result: The project is checked out, the file is removed from project and only his local file is deleted.
       User1 does not checkin at this time, because he has other changes that he hasn't verified yet.
       User2 does a Get on the solution.
         Result: Nothing is retrieved from source control, user2 can build safely his project.
       3 days later, User1 checks in his changes to the project 
         Result: After checkin, he should manually delete the orphaned file from the source control database (extra pain)
       User2 does a Get on the solution.
         Result: The new project file is retrieved from source control; user2 can build safely his project.
         User2 has now an orphaned file on his drive, that he should delete manually (extra pain)
         It is also possible that User2 worked for 3 days on a file that was deleted (not nice); 
         if he still needs his changes, he still has the file on his drive.

    D) User1 creates a solution and adds it to source control
       User1 renames a file in a project.
         Result: The project is checked out, the file is renamed in the project and only his local file is renamed.
         The new file becomes a "pending add", because it was 
       User1 does not checkin at this time, because he has other changes that he hasn't verified yet.
       (If the renamed file was a header file the user1 will need to change other files, too, to build his solution)
       User2 enlists in the solution by opening from source control
         Result: The solution is retrieved from source control, user2 can build safely his project.
       3 days later, User1 checkin his changes.
         Result: After checkin, both old and new file exist in the database. The renamed file has lost the history. 
         (To prevent that User1 should have renamed the file in the database before checkin)
       User2 does a Get on the solution.
         Result: The new project file is retrieved from source control; user2 can build safely his project.
         User2 has now an orphaned file on his drive, that he should delete manually (extra pain)
	 If User2 had changes in the old filename, he should redo the changes on the new file (not nice), 
	 but the good thing is that he has both the old file and new file on his harddrive.
As you can see, in all these cases the user was able to build his solution (the good part).
There is the extra pain of having to manually delete or rename files in the VSS database, or redoing changes in the new file, etc.
Now, let's assume Visual Studio would automatically propagate deletes and renames to the file in the source control database, at the moment the local file is deleted or renamed. Let's see what would happen in the above scenarios...
    A) User1 creates a project and adds it to source control
       User1 deletes a file in the project and VS propagates the delete. 
         Result: The project is checked out, the file is removed from project, local disk and scc database.
       Later, User1 changes his mind, and UndoCheckout the project file.
         Result: The old version of the project is retrieved; it references a file that doesn't exist on disk or in the database.
         User1 cannot build his project. Oops!
         If the source control system doesn't have undo capabilities, his file is lost forever. Oops!

    B) User1 creates a solution and adds it to source control
       User1 deletes a file in a project and VS propagates the delete.
         Result: The project is checked out, the file is removed from project, local disk and scc database.
       User1 does not checkin at this time, because he has other changes that he hasn't verified yet.
       User2 enlists in the solution by opening from source control
         Result: The solution is retrieved from source control, but it references a file that doesn't exist in the database.
         User2 cannot build his project until User1 checks in. Oops!

    C) User1 creates a solution and adds it to source control
       User2 enlists in the solution by opening from source control
       User1 deletes a file in a project and VS propagates the delete.
         Result: The project is checked out, the file is removed from project, local disk and scc database.
       User1 does not checkin at this time, because he has other changes that he hasn't verified yet.
       User2 does a Get on the solution.
         Result: The status of the file is refreshed. The file become a "pending add"
         Fortunately, in this case, the solution remains buildable.
         However, if User2 does a checkin, he will add the file back in the source control database. Oops!

    D) User1 creates a solution and adds it to source control
       User1 renames a file in a project and VS propagates the rename.
         Result: The project is checked out, the file is renamed in the project, on the disk and in the database.
       User1 does not checkin at this time, because he has other changes that he hasn't verified yet.
       (If the renamed file was a header file the user1 will need to change other files, too, to build his solution)
       User2 enlists in the solution by opening from source control
         Result: The solution is retrieved from source control, but it references a file that doesn't exist in the database.
         User2 cannot build his project until User1 checks in. Oops!
As you can see, propagating renames and deletions when working in a team might cause even more pain.
Users might have broken enlistments until the user who deleted files checkin his changes.
Even when working alone you might end up with unbuildable solutions.

Behaviour changes in Visual Studio 2005

In Visual Studio 2005 when you rename or delete a file or folder, beside the dialog displayed by the project system, the source control integration will prompt you with a second dialog similar to the ones below. This dialog will allow to specify wich files do you want to delete or rename: only the local one, or to propagate the local change into the source control database.
You will notice that by popular demand, the first option in the dialog and the default is to "propagate" the deletions and renames
(to delete both the local file on disk and the file in the source control database).
The second option (to delete or rename the local file only) is the old behavior, from Visual Studio 2003.

When choosing the first option, it is most likely you'll be causing unbuildable enlistments for other team members (see possible issues above, in Visual Studio 2003 section).
The other team members experience will be dependent on thir source control provider (whether files are deleted immediately in the store or deferred like in SourceDepot's case, whether the provider can detect database changes like SourceSafe 2005 does, etc).
In any case, it is most likely other users will be affected until you checkin the changes to the project file containing the renamed/deleted file.
If you're choosing this option, make sure your enlisting si buildable (e.g. there aren't other project file referencing the old file name) and checkin your changes as soon as possible.

So, which option should I choose?

When working alone on a project, or working in a small team where you can easily share code and communicate changes to all team members, the first options might be a good choice, because it reduces the pain of matching changes in the source control database, of leaving orphaned files, etc.

However, when working in a larger team, I would strongly recommend against this option.
For large teams I prefer the second option and the VS 2003's behavior. Deleting a file, propagating the change and affecting other 500 developpers working on the same solution until you checkin your changes 3 days later is probably not the best idea...

Deleting or renaming files in web projects

When working with web projects, the dialog displayed by source control is simpler (see below).
However, its meaning remains the same, and the options are still to delete/rename the local file only or to propagate the change in the source control database. Because the web projects in Visual Studio 2005 do not have project files anymore to refer inexistent items, it is somewhat safer to propagate deletions and renames for them. Other users enlisting is such web projects or getting latest version might have missing files, but having a web site with a few missing pages or images seems less of a problem than having a project that cannot be built because of a missing header file...

The ideal solution

Probably the ideal solution is for Visual Studio to delete or rename the local files only, and remember the history of these changes. These changes could be listed in the PendingCheckins window, and Visual Studio could propagate these deletions and renames to the database when the associated project file is checked in. Of course, such solution could cause a lot of problems and the users will have to deal with merging namespace conflicts. The source control provider will have to provide a way of tracking namespace changes in the database side, and Visual Studio will have to provide a way to deal with situations like: A renamed locally to B and in database to C, A renamed locally and deleted in the database, A deleted locally and renamed in the database, A deleted or renamed locally and modified in the database, etc. This will still not be perfect and might still leave the user with unbuildable enlistments if the renamed files are referenced by other files in the project. Hopefully we'll see such solution implemented in a future Visual Studio version...

(Back to SourceSafe and source control integration page)