Monthly Archives: November 2012

Safe Git Clean with MSBuild

I like to clean my source tree once in a while. Unfortunately msbuild {whateverproject}.csproj /t:Clean often falls short – for more complicated solutions with multiple projects some temporary files generated during the build almost always are left on the disk. Removing these should not be a big deal given that the important files are under source control – I can simply remove everything that is not tracked. With git you do that with git clean -xfd command and the files are gone. Usually, this is the (sad) moment when I realize that there was that file you forgot to add to the index or this directory that was not supposed to be added to the index but had some “temporary” data I have been using for a couple of weeks now. To save myself a grief I came up with an MSBuild target that saves everything that is going to be deleted to a backup directory before the clean up. This way even if I missed something not everything is lost – I can go to the backup directory and restore the file.
The target itself turned out quite simple. The biggest problem was to make git commands work with MSBuild. It’s possible to run git commands with Exec task. However Exec task does not capture results of the command. The only way I found to capture the results was to redirect the command output to a file and then read the file contents to an ItemGroup with ReadLinesFromFile task. Not ideal for sure but seems to work.
Using the target is easy as the target takes only two optional parameters – BackupDir and DeleteBackupDir. BackupDir is the path to a directory where files that are not tracked should be saved. If the value is not provided files will be saved to a subfolder of the %TEMP% folder. DeleteBackupDir tells whether to delete the BackupDir if one exists – ‘false’ is the default value. An exemplary command would be:
MSBuild {projectfile} /t:SafeGitClean /p:BackupDir=C:\temp\backup /p:DeleteBackupDir=false
The target itself looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Target Name="SafeGitClean">
    <PropertyGroup>
      <BackupDir Condition="'$(BackupDir)' == ''" >$(Temp)\SafeGitClean$([System.Guid]::NewGuid())</BackupDir>
      <DeleteBackupDir Condition="'$(DeleteBackupDir)' == ''">false</DeleteBackupDir>
      <TempFile>$(Temp)\SafeGitClean_UntrackedFiles$([System.Guid]::NewGuid())</TempFile>
    </PropertyGroup>
    
    <Message Text="$(BackupDir)" />

    <RemoveDir 
      Condition="'$(DeleteBackupDir)' == 'true' And Exists('$(BackupDir)')" 
      Directories="$(BackupDir)" />
    
    <Exec Command="git ls-files --other &gt; $(TempFile)" /> 

    <ReadLinesFromFile File="$(TempFile)">
      <Output TaskParameter="Lines" ItemName="UntrackedFile" />
    </ReadLinesFromFile>
        
    <ItemGroup>
      <UntrackedFileFixed Include="@(UntrackedFile->Replace('/', '\'))" />
    </ItemGroup>
    
    <Delete Files="$(TempFile)" TreatErrorsAsWarnings="true" />
      
    <Copy 
      SourceFiles="@(UntrackedFileFixed)" 
      DestinationFiles="@(UntrackedFileFixed->'$(BackupDir)\%(Identity)')" 
      OverwriteReadOnlyFiles="true" />
      
    <Exec Command="git clean -xfd" />      
  </Target>

</Project>

You can also find it in my github repo: https://github.com/moozzyk/MSBuild-Tasks

Pawel Kadluczka

Advertisements