Fork me on GitHub
【C#】纯托管实现一个Git服务端

【C#】纯托管实现一个Git服务端

 有传闻说,这年头不用Git就不是个程序员。传闻归传闻,近些年来Git的发展是如火如荼。除了一些公共的Git平台外,大多的Git服务端都是在Linux上的,Windows的可选方案实在甚少。作为一个.Net码农,当然希望能有个纯托管代码的Git服务端。经过一晚上的学习,用纯托管代码写了个Git服务端供大家参考。

 

    学习资料:暂无。

 

    接下来开始码代码。首先加入引用:GitSharp.Core,GitSharp。可以从git://github.com/henon/GitSharp.git获取。然后,

   1:  using GitSharp.Core.Transport;
   2:  using System;
   3:  using System.IO;
   4:  using System.Net;
   5:  using System.Text;
   6:  using System.Text.RegularExpressions;
   7:   
   8:  namespace SampleGitServer
   9:  {
  10:      class Program
  11:      {
  12:          const string PREFIX = @"http://localhost:2034/";
  13:          const string REPOSITORY_PATH = @"F:\Repositories\git-debug-ONLY";
  14:   
  15:          readonly static Regex GetInfoRefsRegex = new Regex(PREFIX + @"\w{3,10}/info/refs\?service=.*");
  16:          readonly static Regex GitUploadPackRegex = new Regex(PREFIX + @"\w{3,10}/git-upload-pack");
  17:          readonly static Regex GitRecivePackRegex = new Regex(PREFIX + @"\w{3,10}/git-receive-pack");
  18:   
  19:          static void Main(string[] args)
  20:          {
  21:              var listener = new HttpListener();
  22:              listener.Prefixes.Add(PREFIX);
  23:   
  24:              Console.WriteLine("Listening: " + PREFIX);
  25:              listener.Start();
  26:   
  27:              while (true)
  28:              {
  29:                  var context = listener.GetContext();
  30:                  var url = context.Request.Url.ToString();
  31:   
  32:                  Console.WriteLine(url);
  33:   
  34:                  if (GetInfoRefsRegex.Match(url).Success)
  35:                      GetInfoRefs(context);
  36:                  else if (GitUploadPackRegex.Match(url).Success)
  37:                      GitUploadPack(context);
  38:                  else if (GitRecivePackRegex.Match(url).Success)
  39:                      GitRecivePack(context);
  40:              }
  41:          }
  42:   
  43:          private static void GetInfoRefs(HttpListenerContext context)
  44:          {
  45:              var Request = context.Request;
  46:              var Response = context.Response;
  47:              var project = Request.Url.PathAndQuery.Split('/')[1];
  48:              var service = Request.QueryString["service"];
  49:   
  50:              var directory = GetDirectoryInfo(project);
  51:              if (GitSharp.Repository.IsValid(directory.FullName, true))
  52:              {
  53:                  Response.StatusCode = 200;
  54:                  Response.ContentType = String.Format("application/x-{0}-advertisement", service);
  55:                  SetNoCache(Response);
  56:   
  57:                  var sb = new StringBuilder();
  58:                  sb.Append(FormatMessage(String.Format("# service={0}\n", service)));
  59:                  sb.Append(FlushMessage());
  60:                  var bytes = Encoding.ASCII.GetBytes(sb.ToString());
  61:                  Response.OutputStream.Write(bytes, 0, bytes.Length);
  62:   
  63:                  using (var repository = new GitSharp.Repository(directory.FullName))
  64:                  {
  65:                      if (String.Equals("git-receive-pack", service, StringComparison.InvariantCultureIgnoreCase))
  66:                      {
  67:                          using (var pack = new ReceivePack(repository))
  68:                          {
  69:                              pack.SendAdvertisedRefs(new RefAdvertiser.PacketLineOutRefAdvertiser(new PacketLineOut(Response.OutputStream)));
  70:                          }
  71:   
  72:                      }
  73:                      else if (String.Equals("git-upload-pack", service, StringComparison.InvariantCultureIgnoreCase))
  74:                      {
  75:                          using (var pack = new UploadPack(repository))
  76:                          {
  77:                              pack.SendAdvertisedRefs(new RefAdvertiser.PacketLineOutRefAdvertiser(new PacketLineOut(Response.OutputStream)));
  78:                          }
  79:                      }
  80:                  }
  81:              }
  82:              else
  83:              {
  84:                  Response.StatusCode = 404;
  85:              }
  86:              Response.Close();
  87:          }
  88:   
  89:          private static void GitUploadPack(HttpListenerContext context)
  90:          {
  91:              var Request = context.Request;
  92:              var Response = context.Response;
  93:              var project = Request.Url.PathAndQuery.Split('/')[1];
  94:   
  95:              Response.ContentType = "application/x-git-upload-pack-result";
  96:              SetNoCache(Response);
  97:   
  98:              var directory = GetDirectoryInfo(project);
  99:              if (GitSharp.Repository.IsValid(directory.FullName, true))
 100:              {
 101:                  using (var repository = new GitSharp.Repository(directory.FullName))
 102:                  using (var pack = new UploadPack(repository))
 103:                  {
 104:                      pack.setBiDirectionalPipe(false);
 105:                      pack.Upload(Request.InputStream, Response.OutputStream, Response.OutputStream);
 106:                  }
 107:              }
 108:              else
 109:              {
 110:                  Response.StatusCode = 404;
 111:              }
 112:              Response.Close();
 113:          }
 114:   
 115:          private static void GitRecivePack(HttpListenerContext context)
 116:          {
 117:              var Request = context.Request;
 118:              var Response = context.Response;
 119:              var project = Request.Url.PathAndQuery.Split('/')[1];
 120:   
 121:              Response.ContentType = "application/x-git-receive-pack-result";
 122:              SetNoCache(Response);
 123:   
 124:              var directory = GetDirectoryInfo(project);
 125:              if (GitSharp.Repository.IsValid(directory.FullName, true))
 126:              {
 127:                  using (var repository = new GitSharp.Repository(directory.FullName))
 128:                  using (var pack = new ReceivePack(repository))
 129:                  {
 130:                      pack.setBiDirectionalPipe(false);
 131:                      pack.receive(Request.InputStream, Response.OutputStream, Response.OutputStream);
 132:                  }
 133:              }
 134:              else
 135:              {
 136:                  Response.StatusCode = 404;
 137:              }
 138:              Response.Close();
 139:          }
 140:   
 141:          private static String FormatMessage(String input)
 142:          {
 143:              return (input.Length + 4).ToString("X").PadLeft(4, '0') + input;
 144:          }
 145:   
 146:          private static String FlushMessage()
 147:          {
 148:              return "0000";
 149:          }
 150:   
 151:          private static DirectoryInfo GetDirectoryInfo(String project)
 152:          {
 153:              return new DirectoryInfo(Path.Combine(REPOSITORY_PATH, project));
 154:          }
 155:   
 156:          private static void SetNoCache(HttpListenerResponse Response)
 157:          {
 158:              Response.AddHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
 159:              Response.AddHeader("Pragma", "no-cache");
 160:              Response.AddHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
 161:          }
 162:      }
 163:  }

    好吧,你看完代码会有一种上当的感觉。我必须承认我只是实现了服务端的接口而已。PS,我不会告诉你这点代码都是抄来的。

 

      重点是,所有的代码都是托管代码。在Github.com上有个正在发展中的ASP.NET MVC + EF + Sqlite实现的Git服务端(Dudu推荐:用开源 ASP.NET MVC 程序 Bonobo Git Server 搭建 Git 服务器)。经过几天的试用之后觉得比较稳定。重点的重点是此服务端正处在发展初期,你完全可以根据自己需求打造一个最适合自己的Git服务端。PS,此repo的主人最近很活跃。

用开源 ASP.NET MVC 程序 Bonobo Git Server 搭建 Git 服务器

 

现在不用Git,都不好意思说自己是程序员。

当你想用Git,而源代码服务器是Windows系统时,你将面临一个问题:如何在Windows上搭建Git服务器?

看看这篇文章(Setting up a Msysgit Server with copSSH on Windows)中的配置步骤,你会忘而却步吗?你会感叹“Linux世界的精彩,Windows世界的无奈”?

但程序员的天性是解决问题并以此为乐。不管生活在哪个世界,解决问题才是王道。于是,有程序员解决了这个问题,用ASP.NET MVC写了一个开源的Git服务器程序——Bonobo Git Server

Bonobo Git Server基于.NET Framework 4.0+ASP.NET MVC 3开发,数据库用的是SQLite,Git部分用的是GitSharp - Git for .NET and Mono

运行Bonobo Git Server,只需要IIS+WebDAV(IIS 7.5自带WebDAV, IIS 7.0需要下载安装)。

下面简单分享一下安装配置步骤,该步骤经过实践检验,我们已经成功部署。

第一部分 Bonobo Git Server站点的安装与配置

1. 下载Bonobo Git Server并解压

下载地址:http://www.chodounsky.net/bonobo-git-server/

如果想看源代码,请在这里下载:https://github.com/jakubgarfield/Bonobo-Git-Server

2. 配置一个IIS站点(比如:git.cnblogs.com),指向Bonobo Git Server所在文件夹。

a) 应用程序池要以.NET Framework 4.0运行。

b) 要安装ASP.NET MVC3。

c) 如果服务器用的是X64的Windows,需要下载64位版本的System.Data.SQLite至bin文件夹替换32位版本的。

3. 通过浏览器访问上一步配置的站点,如下图:

4. 输入默认用户名admin与密码admin,进入下图页面:

5. 点击“Global Settings”,设置一下代码库的根目录,并设置好该目录的文件系统权限(需要有写权限):

6. 点击“Respositories” 》“Create new repository”,创建新的代码库:

7. 创建好之后,点击代码库的名称,就能查看该代码库的信息,比如最重要的信息——Git Repository Location。

8. 安装/配置WebDAV

安装

1) 如果是IIS 7.5(Windows 7, Windows Server 2008 R2),WebDAV是自带的,只要添加该组件即可。具体配置方法参见 Installing WebDAV on IIS 7.5

2) 如果是IIS 7.0,需要另外下载安装,请参见 Installing WebDAV on IIS 7.0 。

配置

添加一条Authoring Rule:

到此,Git 服务器站点的基本配置就完成了。

第二部分 Git客户端简要操作步骤

现在可以通过Git客户端提交代码了,这里用的是TortoiseGit。

1. 安装TortoiseGit,下载地址:http://code.google.com/p/tortoisegit/(需要先安装msysgit),VS2010插件可以使用Git Source Control Provider

2. 在将要放置代码的文件夹点击右键选择"Git Clone",在Url中输入代码库的地址,确定后输入默认用户名与密码,然后就开始Clone...出现如下画面表示Clone成功。

3. 向这个代码库文件夹添加代码文件,然后通过TortoiseGit的菜单[Git Commint -> "master"...”]提交代码(这个提交只是向本地的代码库提交,并没有提交至服务器,这也是Git与SVN的主要区别之一)。

4. 通过TortoiseGit > Push 提交至Git服务器。

第三部分  Git服务器的其他操作

  • 修改管理员密码,通过左侧的 "Users"链接进入用户管理界面进行修改。
  • 添加新用户并授权,管理员无法直创建帐户,需要通过注册页面先注册一个帐户(登录页面右上角有注册链接),然后在用户管理界面将该用户加入Administrator角色。
  • 如果遇到问题,可以查看App_Data中的错误日志文件Bonobo.Git.Server.Errors.log。 
  • 由于是开源程序,有什么不合你意的地方,直接可以修改源代码。

小结

轻松搞定,如愿以偿,现在终于可以理直气壮地说,“我是程序员,我用的是Git!”

 
 
 
标签: Git
posted on 2013-03-28 21:43  HackerVirus  阅读(725)  评论(0编辑  收藏  举报