【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!”