Tuesday, September 28, 2010

Installing Redmine on Windows

Redmine is an excellent project/issue/bug tracking tool. As of this writing, V1.0.1 is just released. I guess V2 is going to be based on RoR V3.

Most of the installation is just no brainer, following the directions in their website. I used a MySql and setup a initial DB is also very easy, though I did have to follow the installation instruction to download the MySql Dll.

Now you should be able to start the application from command line. But wait, normally you should use mongrel or webrick as your webserver. A "gem install mongrel" will do the trick. And further, you might want to run your website as a service, following most of the advices to run mongrel as a windows service will require you build a native dll. I don't want to install cygwin or gcc to just compile those stupid dlls. Therefore I followed the advice in Redmine's website. Actually if you download the Win 2003 Res Kit, you can find the directions also in help on topic srvany.exe.

Another bloody experience, which took me 3 hours to figure out was, don't run the service under LocalSystem account, which will prevent network access. It seems this will block subversion access!

Integrating SVN to Redmine can be a little tricky.
1. You need the svn.exe file on system path. Don't forget to re-open a cmd window after you have changed the system path.
2. If you use https protocol, another trick is, you have to run below command to accept the certificate (permanantly) from the server, then everything will be fine.
3. And if you specify any special user rights to your repository, don't forget to grant at least read-only access to the user you specified in Redmine.
4. If you are integrating to a repository with huge history, the intial importing can take a long time.

Authenticating using Active Directory is also a little tricky. You can use this image as a reference.

Monday, August 9, 2010

TDD, it's not about testing.

I subscribed a lot of blogs and it seems in their world, TDD is something that needs not to explain, at least the name it self is as clear as the name of "Unix". But sadly I discovered this might only be true in some software house, but not in most of the IT departments in normal companies.

Anyway, I found some good explanation about TDD, which is very insightful. TDD is not about testing, it's about design. The tests to guarantee quality, to weave the safe-net is a valuable byproduct.

I recall one of the API for a report generator. The report format looks complex. I decided to design a model for the report, and once the model is done, generating the report should be straight forward. I started with some tests to generate part of the data the report needs, this forced me to think about the model structure and API in another angle. And finally it turned out a very different, if not completely different, but simple API. I reviewed it and was glad about its elegance. I would not be able to design such a nice API if I test after!

Sunday, August 1, 2010

Setting up Git server using Apache on Windows

Update: Gitblit is a better choice!

I posted an article to setup a Git server on windows using MSysGit, with Git's native transport protocol. Though it works good, the setup of SSH server and client is not a easy cake to some. Git's HTTP support on Windows was known to be less efficient. But given today's hardware and especially if you are using it in an enterprise environment, this can hardly be a problem than the complex setup process of SSH ways. Recently I found a way of creating Git server using Apache and it's both simple and comprehensive. So take a look.

Friday, May 28, 2010

Great FitNesse tutorial!

I was attracted by FitNesse recently and found this great tutorial from Brett who is working with uncle bob at ObjectMentor.

The tutorial is great since it not only introduced the concept, but also a really good example to guide you through the door. Meanwhile, without bending rules by ugly codes.

Is public field still so evil in C#?

In Java, there is a rule: "never make your field public". This rule is borrowed by C# when a lot of Java programmers jumped to .NET. I'm one of them and I loyally kept that habit, you will find no public field defined in classes from my hand. While, not exactly. That changed recently.

One of the main reason of "keep fields private" in Java was, field accessors and method calls are very different. If you expose your field as public, then all dependent applications will use obj.fieldName to access it; while if you encapsulate it using a access method, it's like obj.getField(). So if you have make the field public, and then you need to introduce some business logic on the field, then you've got a headache: you need to change all the depending source codes. With IDEs like IDEA and Eclipse, this kind of change can be much easier and robust than before, but still it's going to take effort, which is a waste.

Since the first day of C#'s birth, it has a special language feature, Property, which I believe was borrowed by the success of Delphi. By this feature, a field can be encapsulated by a property and accessed in the same syntax as field, except under the hood, it's really a method call, and if you disassembly the IL code, there is really setField and getField methods generated. So in this case, is it still so evil to mark your field private and provide a property for it? In most of my applications (and I believe in most of most programmers' applications) the property is nothing but just a dummy get/set pair. And to make this pain less painful, MS invented the "auto-property". But the question is, is this encapsulate really necessary?

I don't think so! For C# applications, the "private-field-rule" is not applicable any more. Since if you want to change a field to a property, none of the dependent codes should change (unless if you use reflection). The only thing a field can not do is, it can not be marked as virtual while properties can; and fields can not be put into interfaces while properties can be (you can make a property writeonly and not field, might be useful for setting injection by IoC). So if you need to make a NHibernate friendly entity, or if you need to define interfaces to other module, use property. For most of rest, try to use field, until you find the needs to introduce property. This will help you get code clean.

Friday, May 14, 2010

Content types if you deploy ClickOnce applications with non-IIS web server.

Found below info from MSDN. BTW, mongoose is a very good candidate for this...

File name extension

Content type

.application

application/x-ms-application

.manifest

application/x-ms-manifest

.deploy

application/octet-stream

Wednesday, May 12, 2010

Using Teamcity to build(publish) .net application

Since .NET 2, there is a nice feature to "publish" your application, called ClickOnce, to a website, file share or to CD/DVD. If your application only contains managed code (MS termed this smartclient), this feature is a easy way to simplify the deployment of the application and keep your users update date. I happen to have such a application with a group of users up to ~50 distributed from Europe to Asia, so deployment always bothers me. To make things worse (or better:), I follow the agile way of development. That means frequent change/deployment of software. If it is a website, there won't be a problem, but I'm still stuck to desktop and I need an easy way to handle this. Luckily there is a built in function named "publish" in VS2005 (the aged IDE I'm using), which can make a simple html page and some xml files so you can put them on a website and the users can just click the page and start the application. This only works for IE, unfortunately if you can only use .NET2. Since .NET 3.5, there is a Firefox plugin from MS, and there has always been a unofficial Firefox plugin, but I never tried. Anyway it's enough in my case. One thing to notice is, the application is not downloaded for every startup. The timestamp is used to check if there is a new version on server, and if the return value is 304, http-not-changed, then the copy from IE's local cache is used , deployment file (.application) is downloaded and the version embeded in is compared to the version installed in the local application cache. If the version on server is newer, then the application is downloaded again, or the local copy is used. So this suits local network perfectly, unless your deployment is too large (>10MB?). And it seems the application is running in a full trusted mode, with user's confirmation through a dialog showing the publisher can not be verified (maybe a valid certification key would even remove this dialog, I did not tried).

BTW, it's better to use the context menu "Publish..." in project explorer. I've read/experienced unstable publish when using the "publish wizard"/"publish now" button in the publish tab of project properties page.

I have a build server using Teamcity and each time there is a change in my source code, it will grab it and build the software, and notify some colleagues to deploy it. The tool has great web gui and is free if you limit your project to 20 and users to 20. With some money, you can get more "enterprise" features like active directory integration, etc.

I have been using the build server to build "normal" projects (with out "publish") for quite a while and it all worked fine, until I added publish as one of the targets. The actual build still runs well, but there was a wired error occurred when the publish target is running:
warning MSB3155: Item 'Microsoft.Net.Framework.2.0' could not be located in
I searched through the net and found some solutions. But it does not fit all my situation. I could not find the folder specified in the article and copy the whole folder is a little overkill. In my case (VS2005+.NET 2.0), the folder is under
%ProgramFiles%\Microsoft Visual Studio 8\SDK\v2.0
So the register key
sdkInstallRootv2.0 under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
should point to this folder and I did not copy everything, only the "BootStrapper\Engine" folder, and the "signtool.exe" under the "bin" folder (keep the bin folder). Since the signtool.exe needs capicom.dll, I copied it from %ProgramFiles%\Microsoft CAPICOM 2.1.0.2\Lib\X86. Then build again and bingo, it worked!

Thursday, April 29, 2010

When there is a language feature, you don't have to use it.

In my opinion, C# language pushed a lot of features that can help programmers do bad things. One thing I don't like is the "var" keyword. In some cases, it saves some typing, but a lot of miss-using of it can cause the code hard to read. Can you figure out what the type of below variable is?

var key = retriveKeyToLanguage();

Another one I don't like is the miss use of #region tag. Normally if you keep a good coding principle and design, your class should not be long enough to make use of this tag. But strangely, some people just like to use the tag to mass the code. I recently read some source code from NInject. In general the codes are well written and easy to read. The problem I had was, it seems the author likes those garbage parts from C# too much, even in small classes, he use it to mass the otherwise beautiful code. Take a look at below code, except the first 2 regions which wrap the license nonsense and using nonsense, the rest are miss using. On my not big screen, the class should be able to be fully displayed on one page (which is extremely good!), with the mess-around-tags-and-comments, it exceeds the screen.

#region License
//
// Author: Nate Kohari
// Copyright (c) 2007-2008, Enkari, Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
#region Using Directives
using System;
using Ninject.Core.Infrastructure;
#endregion

namespace Ninject.Conditions.Builders
{
///
/// A condition that takes the input of a chain of converter delegates and passes the result to
/// a predicate delegate, determining the result of the condition. This class supports Ninject's
/// EDSL and should generally not be used directly.
///
/// The root type of the conversion chain.
/// The subject type that this condition will examine.
public class TerminatingCondition : ConditionBase
{
/*----------------------------------------------------------------------------------------*/
#region Fields
private readonly IConditionBuilder _previous;
private readonly Predicate _directPredicate;
private readonly Predicate _predicate;
#endregion
/*----------------------------------------------------------------------------------------*/
#region Constructors
///
/// Creates a new TerminatingCondition.
///
/// A predicate delegate that directly examines the root of the condition chain to determine the result.
public TerminatingCondition(Predicate predicate)
{
Ensure.ArgumentNotNull(predicate, "predicate");
_directPredicate = predicate;
}
/*----------------------------------------------------------------------------------------*/
///
/// Creates a new TerminatingCondition.
///
/// The last condition builder in the condition chain.
/// A predicate delegate that determines the result of the condition.
public TerminatingCondition(IConditionBuilder last, Predicate predicate)
{
Ensure.ArgumentNotNull(last, "last");
Ensure.ArgumentNotNull(predicate, "predicate");

_previous = last;
_predicate = predicate;
}
#endregion
/*----------------------------------------------------------------------------------------*/
#region Public Methods
///
/// Determines whether the specified object matches the condition.
///
/// The object to test.
/// if the object matches, otherwise .
public override bool Matches(TRoot value)
{
if (_previous == null)
{
return _directPredicate(value);
}
else
{
TSubject subject = _previous.ResolveSubject(value);
return _predicate(subject);
}
}
#endregion
/*----------------------------------------------------------------------------------------*/
}
}

Wednesday, April 28, 2010

Passing external configuration data to NInject instantiated objects.

I used to be a spring.net user and was quite happy about it. To me, the xml is the best advantage and the worst pain. It's good because it's straight forward, easy to embed external data in; it's bad because it make refactoring a pain, and verbose. With Reshaper's help, the pain is a little less. But still it's less pleasant. Therefore I have been searching for replacements, and NInject (referred as NI) is one of the candidates. The biggest problem to NI is the document. It has a lot of unit test cases, but very limited documents, samples. You can only find a very un-realistic sample from the wiki pages on the website. It reveals the tip of the iceberg, but too simple to be too much usage.

Before I make a migration, I need to make sure all my usage scenarios are covered by NI. I used spring.net's mostly as a IOC container, therefore I assume NI should be able to provide most of the functions. In my case, some of my components needs some configuration data to startup, and if the configuration is wrong or invalid, the whole application should fail to start and administrators will know immediately (it's a server side app).

In real world applications, external configuration in xml, ini, yaml, json format is very common and will affect application behaviors. But I did not find any example, tutorial about that. I asked a question in stackoverflow, but no response yet. So I decide to help myself :).

The code in question is like below:

public interface IServer
{
void start();
void stop();
}

public class ServerImp
{
IDictionary _config;
public ServerImp(IDictionary theConfig)
{
_config = theConfig;
}

public void start()
{
// ... logics
}

public void stop()
{
// ... logics
}
}

The problem is, the dictionary passed to the constructor is data from an xml config file. (Actually I use a dictionary to make it difficult. Most config data in my case are just simple string or numerics.) In spring.net it's a breath to make it happen. But in NI, it turns out also a easy task:

public class ConfigData
{
public IDictionary dic;
//...
}

public class ServerModule : StandardModule
{
ConfigData _config;
public ServerModule(ConfigData data)
{
_config = data;
}

public override void Load()
{
Bind<IServer>().To<ServerImp>().WithConstructorArgument("theConfig", dic);
}
}

In startup process, I just need to read the config data by deserialize the xml and pass the data to the moduel.

// in main
ConfigData cfg = loadConfigData(); // deserialize xml.
using (IKernel kernel = new StandardKernel(new ServerModule(cfg)))
{
IServer transport = kernel.Get();
transport.start();
transport.stop();
}

And it works!

Friday, April 23, 2010

The story that users are not ready for agile yet!

In most of the agile methods, on the spot customer is a very important roll. And the user stories are decided by him. He provides real time business consultant to the team. But is he really always correct on making business decisions?

I haven't seen any discussion about this. All the materials I've read share the same sentences, the customer will decide which story will go into scope and in which sequence to be implemented, and so on and so on. But still, in a *lot* of companies, agile is just a normal word with no special meaning. IT are usually on the opposite side to the customers. The customers will try their best to push everything in, with the fear that they won't get anything if missed the chance. And normally in big companies, those who provide feature descriptions and use them, are not the same ones who provide money. So mostly it is a user, but not customer who is working with the team. He don't care about cost, he only needs features. Since he's not IT guy, he does not understand software has quality and needs to be maintained; he doesn't understand the more features in the more complicated the software is, the more bug will be. With the fear of getting nothing afterwards, he'd even imagine some feature to be implemented. Anyway, it's already paid, isn't it?

In one of my recent projects, I met such a case. The user needs a very special search feature. It's easy to implement. But there is a potential problem: by miss using it, it can bring a lot of load to database and application server. For this kind of search, DB must do full table scanning. We pointed out this and asked the user under which scenario this feature will be used, so we can figure out some strategy. And it turned out it's an imaginary feature. He *thought* it might be used in some future scenario.

So the team needs to remember that the on the spot user should sometimes be questions, especially when the team is in a transition period from traditional way to agile methods. The users are not used to the transform yet!

Monday, April 12, 2010

Rake default tasks when namespace presents

Rake, the Ruby version of make, is really elegant and powerful. Given the power and terseness of Ruby, it's really easy to build building scrips.

Normally when you type rake at command line, give you have everything installed correctly and a rake file under the same folder, the :default task is executed. But when you have namespaces to help organize the tasks, specifying the default task needs a little trick.

This is what you normally do
task :default => :build
task :build do ..... end

With namespaces, you have to use string, instead of symbol to specify the task name:
task :default => "build:build"
namespace :build do
task :build do ..... end
end

Sunday, April 11, 2010

.Net 2 remoting with security support, simplified

I tried a complicated way to initialize the channels to make the caller's identity pass through wire from client to the server. If you need to really impersonate the client or even delegate functions on behalf of the client user, then you have go through that tedious way. I don't like it. The parameters are in plain string and you have to make sure no typo is made. (What's wrong with a strong typed parameter objects?)

Anyway, what all I need to is to know which user is calling some function. I don't need to impersonate or delegation. So there is a simpler way:

On server, just use
IChannel tsc = new TcpServerChannel(8888);
ChannelServices.RegisterChannel(tsc, true);
Note you have to specify ture when you register the channel. Then you are ready to publish objects, either by RemotingConfiguration.RegisterWellKnownServiceType, or by RemotingServices.Marshal.
At client side, you MUST register a TcpClientChannel also with true to the 2nd parameter before activating the server objects. If you forget this, or put false, the client will not be connected correctly.
TcpClientChannel tcp = new TcpClientChannel();
ChannelServices.RegisterChannel(tcp, true);
_Server = (IService)Activator.GetObject(typeof(IService), "tcp://localhost:8888/ASvc");

That's it, no ugly hashtable for parameter passing and still you can pass the caller's identity to server.

Saturday, April 10, 2010

.Net 2 remoting with security support.

Yes I know there is clear statement in MSDN saying this is outdated technology and I should use WCF. But if you are like me who is stuck with .NET 2 on Windows 2000, you still have to use this.

Normally I use spring.net to inject all my objects and control config through external config files, so I don't use the *.config files normal tutorials use. I need to get everything done by code. This is not hard, but needs a little search around.

Below code will create a secure tcp channel at server side:
IDictionary prop = new Hashtable();
prop["port"] = 9999;
prop["secure"] = true;
prop["impersonate"] = false; // when "secure" is true, this is by default, thus optional
TcpServerChannel tcp = new TcpServerChannel(prop, null, new AuthorizationImp()); // auth module can also be specified in property hashtable.
ChannelServices.RegisterChannel(tcp, true);

IService svc = new ServiceImp(); // this makes the server a singleton.
ObjRef or = RemotingServices.Marshal(svc, "Service");

Now the server is ready for servicing clients, under the name "tcp://localhost:9999/Service". We can build a client to connect to it:

IDictionary prop = new Hashtable();
prop["port"] = 9999;
prop["secure"] = true;
prop["tokenImpersonationLevel"] = TokenImpersonationLevel.Identification; // when "scure" is true, this is by default and also optional
IChannel tcp = new TcpClientChannel(prop, null);

ChannelServices.RegisterChannel(tcp, true);

IService svc = (IService) Activator.GetObject(typeof (IService), "tcp://localhost:9999/Service");

IDictionary csp = ChannelServices.GetChannelSinkProperties(svc);

svc.Repeat("From client", 5); // call with default login

csp["username"] = "rrr"; // now use another user to call
csp["password"] = "rrr";
svc.Repeat("From client", 5);

csp["username"] = "mmm"; // another
csp["password"] = "mmm";
svc.Repeat("From client", 5);

From the code you might have figured out that IService is a interface used by both server and client to decouple a concrete dependency to any implementation. It has only 1 method called Repeat.

So how does server know who is calling for service? Even without impersonation, the server code can still retrieve the caller's identity by Thread.CurrentPrincipal.Identity.But the WindowsIdentity.GetCurrent() will still return the current user the process is running on. MSDN made a mistake at this point.

Monday, March 22, 2010

Which version of oracle client I'm running on?

Today I need to tell my DBA on which oracle client my software is running, on my PC. I knew by connecting into a server using sql*plus, the first several lines will give you information about which versions the client and server are. But this seems a little bit silly, you need to connect to a remote server to tell your local information. So I googled around and it turns out that the oracle tool TNSPING spools out it's version when it is started, even you don't provide anything as parameter.

C:\>tnsping

TNS Ping Utility for 32-bit Windows: Version 11.1.0.6.0 - Production on 22-MAR-2
010 11:56:24

Copyright (c) 1997, 2007, Oracle. All rights reserved.

TNS-03502: Insufficient arguments. Usage: tnsping xxxxx

Why sql*plus, the most used tool don't provide this info? Afraid of confusing the users?

Thursday, March 18, 2010

Setup a Git server on Windows machine, with MSysGit and CopSSH.

Update: Gitblit is an easier choice to do this!

I switched to Git from SVN recently, not for fun or fashion but for performance. I was involved in a big project with thousands of files and dozens of sub-projects. The result was, each operation takes longer time than I could bear. I'm not a patient person, so I tried to find better solutions and there Git is :)

I work for a big company and therefore a centralized-repository working pattern is best suited to my team. I have to prepare a central server before we can really switch to Git. But it seems Git was born in Linux and is not so friendly to Windows, which is the mandatory platform I have to use. The gift-MSysGit ripped git-daemon, therefore the simplest, though not the best, option is gone. I googled around, with a cygwin solution and a MSysGit one. I have a small team, so I don't need Gitosis yet, and MSysGit impressed me a lot, so tried the 2nd way.

Tim Davis did a great job making the solution work, but the description is somehow confusing to me, as I'm both new to Git and SSH. So I present my steps here, hope it helps people with just M$ Windows background.

You'll need 2 WinXP boxes (or virtual boxes) doing the setup, 1 as server and 1 as client. I used CopSSH 5.4p1 (file name Copssh_3.1.0_Installer.exe), and MSysGit 1.7.0.2 (file name Git-1.7.0.2-preview20100309.exe). Putty 0.60 is used to generate keys and test connections.

On Server Box

1. install CopSSH
Be sure to install it to a folder without space in its name. I used C:\SSH. Then just press next until it finishes.


2. install MSysGit

Be sure to install it to a folder without space in its name. I used C:\Git. The default values suits me well.
3. Config CopSSH
a. I would like to user a separate user for git access. So create a user from command line


net user git userspassword /add

b. Then, goto "Start | Programs | Copssh | 01. Activate a user" to activate the user.

Note, you should clear the 2nd checkbox, we'll generate keys later.

c. goto C:\SSH\etc folder, open the sshd_config file using wordpad (or notepad2), don't use notepad, it's a UNIX format file. Remove the leading # character for item "PasswordAuthentication" and change the "yes" to "no". Also you can review the rest of the config file and change if necessary. For me, most of the default settings works fine.


d. Goto C:\SSH\home\git\.ssh folder, create a file named authorized_keys. Open this file using wordpad.

e. Install/extract Putty if you have not. Invoke PUTTYGEN.EXE to generate a pair of keys:
I used a 4096 bit SSH2 key. Save the private key to a folder and remember it. We'll use it later. Copy the content in the text box labelled with "Public key for pasting...." to the wordpad window you opened at step d, and save it, close the wordpad.

f. (Re)Start SSH Server. You can either reboot your PC, or use below command line:
net stop opensshserver
net start opensshserver

g. Now it's ready to test SSH connection. Invoke Putty.EXE, put localhost as host name, if you changed port in step c, don't forget to change it here. Navigate to Connection/SSH/Auth node, press the "Browse" button to select the private file you generated and stored in step e, as shown below:
You can save the settings to save you some typing next time. Google it if you don't know how. Now press "Open" button, you will see a warning window on first connection, press "Yes" to accept the key. Then a block terminal window pops up, with prompt "login as:", input "git" (without quote), and you should be prompted for a key-phrase if you set it when you saved the private key. Note, this is not the password of the git user you created during step a!!!. And with some luck (which you don't need if you know what you are doing), you should see some window similar to this:

If you've made it, congratulations, the hardest part is behind you. If you don't see it, then please review carefully what you have missed.

h. invoke a command window if you have not (where have you executed those "net xxx" commands?), and type
cd /d %USERPROFILE%
echo export HOME=/c/SSH/home/git > .bashrc

i. goto C:\SSH\home\git folder, open the .bashrc file (yes, the same name as in the above line). Insert below line to the first line: (AGAIN, THIS IS A UNIX FILE!)
export PATH=/cygdrive/c/Git/bin:/cygdrive/c/Git/libexec/git-core:$PATH

NOTE, above "export PATH=..." must be in the same line!



4. setup a Git repository.

goto C:\SSH\home\git and make a folder named test.git. Right click the folder and select "Git Bash" from context menu. (What? You did not choose the explorer integration? Goto start menu, find Git|Git Bash, and use command line to goto this folder). Then input below lines:
$ git init --bare
Initialized empty Git repository in C:/SSH/home/git/test.git/

Now the server setup is DONE. You might need a cup of coffee of a cake to ease your nerve.

Special notes no 2003 server. It seems the sshd will experience error if the user account used to login is not a member of administrator (thanks for the comment from Raphael). So if you experience problem, try to add the user's account to administrator group and try again.


On Client PC

Setting up multiple accounts


I just realized that I forgot the part on how to make your repository accessible to multiple users. It's simple once you have gone through above steps: adding another user is just to generate another key-pair, and put the public-key into the authorized-key file. That's all. Then the new user can access your repository. (just follow the client setup, no other server settings needs to be changed).



I know a better way is to use Gitosis, but I'm not able to run it without cygwin yet :(


1. Install MSysGit, as on server. You can choose any folder you like (better a folder without space in it's path).

2. Test connection, using Putty. Change the host name and port as in your environment. Don't forget the Connection | SSH | Auth node setting. Copy the private key file to client machine and point the file in this node. Then press "Open" button, you should find it similar to what you have experienced at step g above. But this time, after you logged in, you are actually logged in to another computer! After you logged in, type "git", and see if you have the familiar git help screen before you. If you see

-bash: git: command not found
then please check if you have step i done correctly.

3. Opne a command window, type


cd /d %USERPROFILE%
md .ssh

Then use windows explorer to open this folder. create a files (id_rsa.pub) if they don't exist.

4. Fire up PuttyGen.EXE again, load the private key you used at step 2. Paste the content of the text box to file id_ras.pub, and use menu "Conversions | Export OpenSSH key" to save to the folder you created at step 3, in name "id_rsa". Now the folder %USERPROFILE"\.ssh should have at least 2 files: id_rsa and id_rsa.pub

input "yes" to accept the remote key. Then you should get an echo "something"

6. Now we are ready to clone the empty project we created at server


$ git clone ssh://git@tst/SSH/home/git/test.git

Initialized empty Git repository in D:/g/gt/test/.git/
warning: You appear to have cloned an empty repository.


Now you can make some change and push it back

$cd test
$vim readme.txt
$git add readme.txt
$git commit -a -m "first commit"



[master (root-commit) f216dfe] first commit











1 files changed, 1 insertions(+), 0 deletions(-)
















create mode 100644 readme.txt




$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 236 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://git@oti-tst/SSH/home/git/test.git
* [new branch] master -> master


Congratulations, you can clone and push back. It's not as hard as it sounds, after all :D


5. create a empty folder and invoke the Git Bash and navigate to that folder. Type







ssh git@your.remote.host "echo something"