How to setup the Output and Intermeidate directory in visual studio to be dependent on current git branch?

I’m looking for a solution, to avoid lot of recompiling when moving from branch to branch in git.
Using different folders for different branches is solution, but It just created chaos in my workflow.

The best solution I can think of would be to pass the git branch as the options $ variable used in project settings, so I could set the “Output directory” to something like

..\bin\$(branch)\$(Configuration)$(Platform)\

Similar with intermediate directory.

Is something like this possible to configure?

Edit:
The problem is, that even when the output/intermediate directory is different per branch (or moved from archive when changing branches), the precompiled headers are always recompiled anyway, so the project is recompiled as well.

  • Where is Visual Studio storing Publish Profiles?
  • Azure: Deploy from source control using Visual Studio Team Services
  • Visual Studio Team Services - Sync w/ Github Repository
  • How I deal with Visual Studio files in Git?
  • Can I automatically increment the file build version when using Visual Studio?
  • Make GIT ignore DLL,PDB and similar generate files
  • Project references working in a small team in Visual Studio?
  • How to add an asp.net website into a github repository?
  • 3 Solutions collect form web for “How to setup the Output and Intermeidate directory in visual studio to be dependent on current git branch?”

    Solution 1

    I think OP’s way to use $(branch) is cool. But there’s no way to create a user macro with the dynamic value of the branch name in Visual Studio. And it’s either impossible for Visual Studio to reload environment variables if we add it in the pre-build event after the VS starts, unless it’s restarted. It seems the most straightforward way is just to do it in post-checkout hook in git repository as @ConfusedSushi mentioned.

    I just tested the solution and it worked well. The details are as follows.

    Create git hook post-checkout (that the file name is just post-checkout, and put it into GIT_DIR/.git/hooks. It will call a batch file set-branch.bat.

    #!/bin/bash
    
    if [ "$3" -eq "1" ]; then
        cmd.exe /c "`pwd`/.git/hooks/set-branch.bat" 
    fi
    

    The batch file set-branch.bat is as follows. And also put it into GIT_DIR/.git/hooks.

    @echo off
    
    for /f %%i in ('git rev-parse --abbrev-ref HEAD') do setx branch %%i
    

    And then set the Output directory as OP did:

    ..\bin\$(branch)\$(Configuration)$(Platform)\
    

    Now, every time when the branch is changed via git check, an environment variable branch will be set. And you need to restart Visual Studio to get the environment variable value. It just works as you need.

    Solution 2

    Due to OP’s comment, another solution is to let the visual studio use the same folder, but move the Output/intermediate directories in the post-checkout hook.

    Just create a git hook post-checkout with following scripts. And replace my test folder below Test/Test/Debug with your own real folder. And it can be absolute directory or related to git repository. The build folder will be added branch name as suffix if it’s not current branch.

    #!/bin/bash
    
    build_folder=Test/Test/Debug
    if [ "$3" -eq "1" ]; then
        oldref=$(git reflog | awk 'NR==1{ print $6; exit }')
        newref=$(git rev-parse --abbrev-ref HEAD)
    
        if [ "$oldref" != "$newref" ]; then
            if [ -d "$build_folder-$oldref" ]; then
                rm -rf "$build_folder-$oldref"
                echo "Deleted $build_folder-$oldref"
            fi
    
            if [ -d "$build_folder" ]; then
                mv "$build_folder" "$build_folder-$oldref"
                echo "Moved $build_folder to $build_folder-$oldref"
            fi
    
            if [ -d "$build_folder-$newref" ]; then
                mv "$build_folder-$newref" "$build_folder"
                echo "Moved $build_folder-$newref to $build_folder"
            fi
        fi
    fi
    

    [Updated] For the edited question, since the precompiled headers always change when switching branches, the projects are always recompiled. There’s actually a solution, but a bit dangerous.

    VS detects the modified time of a source file to decide whether it needs to be recompiled. So what we can do is to change the modified time to cheat VS. We can do it with the command touch.

    But it’s hard to decide which time is suitable. To make it practical, we can choose a file that doesn’t change by switching branches, and also relatively stable, and set the modified time of the precompiled headers the same as the chosen file. We can also do it in the post-checkout hook.

    touch -r some_stable_chosen_file precompiled_header
    

    But we should remember it’s not safe, because it may also ignore the real changes to the precompiled headers. We have to manually deal with it.

    You could setup a post-checkout hook in git. This hook could run a batch file which is determining the current branch and passing this to an environment variable. You should use SETX to set the environment variable to permanently set it. In MsBuild-Files you can use environment variables. So your suggested project setting should work then. Of course you also can implement your hook in your favorite language instead using a batch file.

    This is the solution based on Landys answer I actually use to avoid the need to restart visual studio to reload new value of the enviromental variable.

    The script just uses the directory branches/<branch> for the intermediate/output directories of the branches. In the post-checkout hook, the previous directories are moved to the archive, and the needed ones are moved to their place.

    #!/bin/bash
    
    prevHEAD=$1                                                                      
    newHEAD=$2                                                                       
    checkoutType=$3                                                                  
    
    if [ $checkoutType -ne 1 ] ; then exit ; fi
    
    prevBranch=`git name-rev --name-only $prevHEAD`
    newBranch=`git name-rev --name-only $newHEAD`
    
    targetDir=vs-2013-files/branches/$prevBranch
    mkdir -p $targetDir
    mv vs-2013-files/*Win32 vs-2013-files/*Win32Lib vs-2013-files/*x64 vs-2013-files/*x64Lib $targetDir 2>/dev/null
    mv bin $targetDir 2>/dev/null
    
    sourceDir=vs-2013-files/branches/$newBranch
    mv $sourceDir/*Win32 $sourceDir/*Win32Lib $sourceDir/*x64 $sourceDir/*x64Lib vs-2013-files    2>/dev/null
    mv $sourceDir/bin . 2>/dev/null
    
    Git Baby is a git and github fan, let's start git clone.