Using Mercurial as a local repository for Team Foundation Server / Start Front’N

This post covers how to setup mercurial(HG) as are local source control repository to sit in front of a Team Foundation Server . I am not going to go into the details of why you would want to run this way in this post. You can look at the StackOverflow question that may give you more insight. For those of you who want to use mercurial for your day to day source control but have to synchronize your source code into a TFS server, this will allow you to do this.

The tools used for this process were lifted from blog posts all over the web.

 

The Tools.

  1. TFS 2010  Powertools
  2. Tortoise HG
  3. MakeWritable Mercurial Extension
  4. Rebase Mercurial Extension
  5. Two Powershell scripts to push and pull files to the TFS server (listed below)

 

Setting up the environment

  1. Download and install, TFS 2010 Powertools, Tortoise HG, and the MakeWritable Mercurial extension
  2. Enable the MakeWritable and Rebase extensions in mercurial.
    1. Edit your profiles Mercurial.ini file.  Add the following section to the file.

      [extensions]
      makewritable = c:program files (x86)TortoiseHgmakewritable.py
      rebase=

  3. Configure the TFS and HG repositories on your local file system.
    1. Setup your project repository from tfs in a folder. C:codeProject-from-TFS . This is the gold repository that is only used for merging changes to and from the TFS repository. You will never make changes to code in this repository.
    2. Make that folder a Mercurial Repository and add all the files from TFS into the mercurial repository and commit them to Mercurial.
    3. Make a second folder. This is your working folder.  In this folder using mercurial clone from the gold repository.
    4. Place the push.ps1 and pull.ps1 files into the working folder and commit them to the local repository.

The Workflow

  1. Start developing your work.. If you want commit to branches and merge to the main when you want to push something to TFS.
  2. Commit your local work.
  3. Run the pull.ps1 command to pull down changes from tfs and merge them.
  4. Keep working and committing to mercurial
  5. When your ready to send your change to the TFS server commit your changes to mercurial and then run the push.ps1 command.
    1. The push.ps1 command will pop up one window for adding new files to TFS (if you have created new files as part of your dev work)
    2. The push.ps1 will pop up a commit dialog for TFS so that you can verifiy all the files going to TFS and enter a commit message.

Rinse and Repeat.

 

Show sample of pull.ps1

image

Show sample of push.ps1

image

This shows the results of running a pull command.  By using the Rebase command needless merge commits just go away. You see the +1 heads text below, that suggests a merge is needed, and it normally is.  By scripting the rebase command I essentially eliminate the need for the merge, except for when a true merge conflict exists.

image

 

Is this useful?  Is there a better way to do this?  Please let me know.

Related Articles:

    Post Footer automatically generated by Add Post Footer Plugin for wordpress.

    About Eric Hexter

    I am the CTO for QuarterSpot. I (co)Founded MvcContrib, Should, Solution Factory, and Pstrami open source projects. I have co-authored MVC 2 in Action, MVC3 in Action, and MVC 4 in Action. I co-founded online events like mvcConf, aspConf, and Community for MVC. I am also a Microsoft MVP in ASP.Net.
    This entry was posted in mercurial, source control, TFS. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
    • http://JeffreyPalermo.com Jeffrey Palermo

      This is great. By providing the scripts, you have made the solution much simpler to implement.

    • Jon Newbill

      This was most useful.  I found with VS2010 I had to add options /diff and /deletes to the tftp online command in the push script to get changed and deleted files to be checked in to TFS.  Even with this script I’m getting an error from push when a file has been deleted that hg update is
      “unable to remove “FileXYZ” : access  is denied”.  I have the MakeWritable.py extension installed but apparently that is only used for modified files.  Here are my modified scripts. 

      ———————–FILE: push.ps1—————————————————-

      $projName = “TicTacToeCMMI”
      $tftp = “C:Program FilesMicrosoft Team Foundation Server 2010 Power ToolsTFPT.exe”
      $tf = “C:Program FilesMicrosoft Visual Studio 10.0Common7idetf.exe”

      hg push
      cd ..$projName-tfs

      “Syncing -tfs workspace with TFS server”
      &$tftp scorch /noprompt /exclude:.hg’,_Resharper*’,*.user

      hg update -C -y

      “Resyncing Mercurial changes with TFS Server”
      &$tftp online /adds /deletes /diff /exclude:’.hgignore,.hg,bin,obj,*.ps1,_Resharper*,*.lnk,*.user,*.suo,*.vspscc’

      “Checkin”
      &$tf checkin
      cd ..$projName-working
      cmd /c pause
      ————————————————————————————————

      ———————–FILE: pull.ps1—————————————————-

      $projName = “TicTacToeCMMI”

      $tf = “C:Program FilesMicrosoft Visual Studio 10.0Common7idetf.exe”

      $username = cmd /c set USERNAME

      $username = $username.SubString($username.IndexOf(“=”)+1)

      function pull {

          cd ..$projName-tfs

          &$tf get

          hg commit -A -m “from tfs” –user $username

          cd ..$projName-working

          hg pull –rebase

      }

      pull

      cmd /c pause

      ———————————————————————————————

    • Ben

      Here is a powershell script I created with doesn’t require the makewriteable.py and also pushs renames made in HG back into TFS….

      hgtfs.ps1
      —————————————-
      param([parameter(Position=0, Mandatory=$true)][string] $action)$HGDirectory = Get-Location$TfsDirectory = @(hg paths | where-object { $_.StartsWith(“default = “) })[0].SubString(10)# Pull from TFSfunction pull{    # Todo pull changes one by one brining who did it and the comment into HG    # tf history . /recursive /format:brief /noprompt /version:300~1000 /sort:ascending    # tf properties . /recursive    # Add the changes from TFS into the TFS HG repository    Set-Location $TfsDirectory    tf get . /recursive    hg commit -A -m “Update from TFS”          # Pull / merge the changes from TFS’s HG repository    Set-Location $HGDirectory    hg pull    hg merge –tool internal:fail    hg commit -m “Merged from TFS”        “”    “The you have the following conflicts which need resolving”    hg resolve -l | write-host -foregroundcolor “red”    #thg commit}# Push to TFSfunction push {    Set-Location $HGDirectory    hg push    Set-Location $TfsDirectory    $FilesModified = @()    $FilesRenamed = @{} # Key: old file name …. Val: new file name    $FilesRemoved = @()    $FilesAdded = @()    # Work out what changes have taken place    “Calculating the changes which have been made in HG…”    tfpt scorch /exclude:.hg,*.user | out-null    $AllChanges = hg status –rev .:tip -A     for($i = 0; $i -lt $AllChanges.length ; $i++)    {        $type = $AllChanges[$i].SubString(0, 2)        $fileName = $AllChanges[$i].SubString(2)                    switch($type)        {            “M ” # Modified files                  {                     $FilesModified += $fileName                }             “A ” # New Files                {                      $nextType = $null                    $nextFileName = $null                    if($AllChanges.length -gt ($i+1))                    {                        $nextType = $AllChanges[$i+1].SubString(0, 2)                        $nextFileName = $AllChanges[$i+1].SubString(2)                                    }                                        if($nextType -eq ”  ”)                    {                        # we have a rename                        $FilesRenamed[$nextFileName]=$fileName                        $i++                    }                    else                    {                        # we’re adding the file                        $FilesAdded += $fileName                    }                 }                             “R ” # Removed                {                    if($FilesRenamed.ContainsKey($fileName))                    {                        continue                    }                                        $FilesRemoved += $fileName                }                        “C ” # Same                 {                     continue                 }                             default                 {                     “Unknown HG status line: “+$AllChanges[$i]                     return -1                }        }    }    # perform the TFS operations     “Renaming files in TFS…”    foreach($file in $FilesRenamed.Keys) {           tf checkout $file | out-null        tf rename $file $FilesRenamed[$file] | out-null    }        “Checking out for edit in TFS…”    foreach($file in $FilesModified) { tf checkout $file | out-null }        “Removing files from TFS…”    foreach($file in $FilesRemoved) { tf delete $file | out-null }    # perform the Mercural update    “Pulling changes out of HG….”    hg update –rev .:tip –clean    # perform any POST TFS operations    “Adding new files to TFS…”    foreach($file in $FilesAdded) { tf add $file }        “Cleaning up…”    tfpt uu /noget    tf checkin}if ($action -eq “push”) { push }elseif ($action -eq “pull”) { pull }else { “Unknown action … please supply ‘push’ or ‘pull’” }# return to our starting pointSet-Location $HGDirectory

    • Justinas Urbanavicius

      if you would use tfs repository location:local instead of server there would be no readonly files, and no need to make them writeable