Monday, November 9, 2015

How to move TFS repository to git

At the company I work for we decided to split one gigantic TFS repository into several smaller ones. We use on prem TFS server which supports multiple git repositories within one project, thus allowing us to track all work items in one place. Unfortunately it’s not the case with TFS repository - you can have just one per project, at least with TFS 2015 (at least prior update 1).

We had the following requirements for new repos: when we split repository we wanted to preserve all version history for specific file/folder/sub-folder, we wanted to preserve association of commits with work item numbers.

Here are steps we took. I’m sure there many solutions, here is just one of them.

1. Convert TFS repository to local git repository using an awesome git-tfs bridge (http://git-tfs.com/):
git tfs clone http://tfs:8080/tfs/collection $/my_project

Depending on repository size it might take a lot of time to get entire version history, so keep that in mind.

2. TFS associates git commits with work items using # sign included into commit message. For example “my commit #105” will associate work item #5 with current commit. git-tfs bridge stores all TFS -> work item associations in git notes, so we need to extract them and append to commit message:

git filter-branch -f --msg-filter 'cat && (git notes show $GIT_COMMIT | grep -E "\[(.*)\]" | sed "s/.*\[\(.*\\)\].*$/#\1/g")' -- --all

3. git-tfs also adds original tfs id references to commit messages, so it makes sense to remove them just to keep commit message cleaner:

git filter-branch -f --msg-filter 'sed "s/git-tfs-id:.*$//g"' -- --all

4. The last step is to actually delete all code that is not required for new repository including version history, but preserve history for the rest. For example if TFS repository contains folders CodeA, CodeB, CodeC, CodeD and only CodeA and CodeD are required, use the following command:

git filter-branch --index-filter 'git rm --cached -qr -- . && git reset -q $GIT_COMMIT -- CodeA CodeD' --prune-empty -- --all

So new repository is ready, just push it to your new location.

Sunday, February 8, 2015

Gulp and bower in ASP.NET MVC

There are many tools, libraries and extensions for Visual Studio to do front end web development. There are NuGet packages that can help combine and minify JS and CSS (Microsoft Ajax Minifier), sass and less transformers (BundleTransformer.SassAndScss, BundleTransformer.Less), reactjs integration (React.Web.Mvc4), angular templates bundling (AngularTemplates.Compile). But there is another very efficient way to do front end development using nodejs, gulp and bower. In my experience it feels more natural than NuGet and MSBuild.

First of all we need to install nodejs: http://nodejs.org/download/. It will also install npm package manager. After that we need to install bower and gulp (don’t forget -g flag to install both packages globally):

npm install -g bower
npm install -g gulp
Now we’re ready to start using these tools with ASP.NET MVC.


Bower


Bower (http://bower.io) will help us manage client side libraries. It stores a list of all dependencies in bower.json file. You can create it manually or just run bower init in console and follow a few steps to generate new bower.json file. Let’s say we want to add angular to our project:

bower install angular

By default bower installs all components to bower_components directory. If you’d like to change that just create .bowerrc file and specify another directory:

{
    "directory": "my_new_directory"
}
For more configuration options see http://bower.io/docs/config/. If you already have bower.json file in your project and need to restore all components, just run bower install command. Here is a sample of bower.json file:

{
  "name": "my_project_name",
  "version": "0.0.1",
  "authors": [],
  "description": "my project description",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": ">=1.3.0",
    "angular-route": ">=1.3.0"
  }
}

Gulp


Gulp (http://gulpjs.com) allows to easily build front end assets: convert sass, less to css, combine and minify css and js, lint them, run unit tests, optimize images and much more. There is a huge ecosystem around it and many libraries that can do pretty much anything with your assets. As an example let's combine and minify JS and CSS.

To do it we need to init our npm app and install several npm modules:
1. npm init
2. npm install gulp gulp-concat gulp-if gulp-minify-css gulp-rev gulp-rev-replace gulp-uglify gulp-useref --save-dev
In order to automatically determine JS and CSS bundles without even using ASP.NET MVC bundling mechanism we're going to use gulp-useref module. We need to wrap our JS and CSS tags into special html comments in our .cshtml files:

<!-- build:css /assets/site.css -->
<link href="/assets/components/foundation/css/normalize.css" rel="stylesheet"></link>
<link href="/assets/components/foundation/css/foundation.css" rel="stylesheet"></link>
<link href="/assets/components/metrics-graphics/dist/metricsgraphics.css" rel="stylesheet"></link>
<link href="/assets/styles/app.css" rel="stylesheet"></link>
<!-- endbuild -->

gulp-useref will cut css links between these html comments, combine css files into one file, minify it and replace with one single link tag that will point to /assets/site.css. The same rules apply to JS assets. Here is full gulp script listing:


var gulp = require('gulp');
var useref = require('gulp-useref');
var uglify = require('gulp-uglify');
var gulpif = require('gulp-if');
var minifyCss = require('gulp-minify-css');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');

// Concat & Minify JS/CSS
gulp.task('build_assets', function () {
  var assets = useref.assets({
    searchPath: '.'
  });
  gulp.src('Views/**/*.cshtml', {
      base: './'
    })
    .pipe(assets)
    .pipe(gulpif('*.css', minifyCss()))
    .pipe(gulpif('*.js', uglify()))
    .pipe(rev())
    .pipe(assets.restore())
    .pipe(useref())
    .pipe(revReplace({
      replaceInExtensions: ['.cshtml']
    }))
    .pipe(gulp.dest('./obj/Release/Package/PackageTmp'));
});

gulp.task('default', ['build_assets']);

We definitely want to run this script from Visual Studio. We can add gulp script execution to Pre/Post build events. But when we let's say publish our solution to local folder we need to add a bit of xml to our .csproj file. Just add it before project closing tag:


<Target Name="RunGrunt" AfterTargets="CopyAllFilesToSingleFolderForPackage">
  <Exec Command="gulp" />
</Target>

You'll find combined/minified css and js files in published folder as well as modified .cshtml files. This technique allows to get rid of ASP.NET MVC bundles that are going to be deleted in the next version of MVC. See this github issue: https://github.com/aspnet/Mvc/issues/1577.


Please find working example of ASP.NET MVC project with configured gulp and bower support here: https://github.com/vadimi/gulp-aspnet-sample. Run npm install, bower install, open solution in Visual Studio and hit "Publish".