ǰÑÔ
ÔÚ¿ª·¢Èí¼þµÄ¹ý³ÌÖУ¬ÍùÍùÊÇÐèÒª¶à¸öÈ˲ÎÓ룬°æ±¾¿ØÖÆÏµÍ³µÄÐͬ¹¤×÷µÄÖØÒªÐÔ²»ÑÔ¶øÓ÷£¬³ý´ËÖ®Í⣬ °æ±¾¿ØÖÆÈí¼þ¶ÔÕû¸ö¿ª·¢Á÷³ÌµÄ¼Ç¼¶ÔÓÚȱÏÝ×·×ÙÒ²ÊǷdz£ÖØÒªµÄ¡£°æ±¾¿ØÖÆÏµÍ³Ò²ÊÇÈí¼þ¿ª·¢µÄ»ù´¡ÉèÊ©¡£
±ÊÕß¿ªÊ¼½Ó´¥°æ±¾¿ØÖÆÏµÍ³ÊÇ´óѧµÄʱºò£¬×ʼ°²×°ÁË TortoiseSVN £¬È»¶ø TortoiseSVN
½ö½öÊÇÕ¼¾ÝÁËÓ²ÅÌ¿Õ¼ä¶øÃ»Óз¢»Ó×÷Ó㬺ܶ࿪·¢ÕßÔÚ½Ó´¥ÐÂÊÂÎïµÄʱºò£¬²¢²»Ò»¶¨»áÓм«´óµÄÈÈÇéÈ¥Á˽⣬ ÓеÄ×ßÁ˺ܶàÍ䷺󷵻ص½ÁËԵأ¬Ö»Óе±ÉîÈëÁ˽âÒԺ󣬲žõµÃÆäÖÐÒì³£µÄ¾«²Ê¡£µ±ÎÒÔÚ
Windows ϱàÒë LLVM µÄʱºò£¬ Subversion ¿ªÊ¼·¢»Ó×÷Ó㬱Ëʱ£¬¼¸ºõËùÓпªÔ´µÄ´óÐÍÈí¼þ¶¼ÊÇʹÓÃ
Subversion ½øÐÐÍйܣ¬µ±È»»¹Óв¿·Ö CVS¡£ GIT ԶԶûÓÐĿǰÁ÷ÐС£ºóÀ´²Î¼Ó¹¤×÷ºó£¬¾ÍÊÇ´úÂëÍйܵŤ×÷£¬¶Ô
Subversion ºÍ Git ÓÐÁËÒ»¶¨³Ì¶ÈµÄÁ˽⣬ Öð½¥ÓÐÁË×Ô¼ºµÄ˼¿¼¡£
´ó¶àÊýÈ˶԰汾¿ØÖÆÏµÍ³µÄ½â¶Á¶¼ÊÇÕ¾ÔÚʹÓÃÕߵĽǶȣ¬¶ø±¾ÎÄÊÇÕ¾ÔÚÒ»¸ö´úÂëÍйܵĿª·¢ÕßÁ¢³¡¡£
°æ±¾¿ØÖÆÏµÍ³¼ûÎÅ
°æ±¾¿ØÖÆÏµÍ³µÄÀúÊ·¿ÉÒÔ×·Ëݵ½20ÊÀ¼Í70Äê´ú£¬ÕâÊÇÒ»¸ö¾ü·½¿ª·¢µÄ CCC £¨±ä¸üºÍÅäÖÿØÖÆ£©ÏµÍ³£¬Ãû×Ö½Ð×ö
CA Software Change Manager Ëæºó£¬°æ±¾¿ØÖÆÏµÍ³¿ªÊ¼·¢Õ¹ÆðÀ´¡£
CVS Ò»¶ÈÔø¾ÊÇ¿ªÔ´Èí¼þµÄµÚһѡÔñ£¬±ÈÈç GNOME¡¢KDE¡¢THE GIMP ºÍ Wine£¬ ¶¼ÔøÊ¹Óùý
CVS À´¹ÜÀí¡£ÕâÊÇÒ»¸ö¼¯ÖÐʽµÄ°æ±¾¿ØÖÆÏµÍ³£¬Í¬ÑùÊǼ¯ÖÐʽµÄ»¹ÓÐ Subversion, Visual
SouceSafe Perforce,Team Foundation Server¡£
ÓÉÓÚÄÑÒÔÈÌÊÜ CVS,CollabNet µÄ¿ª·¢Õß¿ª·¢ÁËÖøÃûµÄ Subversion(SVN) À´È¡´ú
CVS, Subversion µ®ÉúÓÚ 2000 Ä꣬ ʱÖÁ½ñÈÕ£¬SVN ÒÀÈ»ÊÇ×îÁ÷Ðеļ¯ÖÐʽ°æ±¾¿ØÖÆÏµÍ³£¬GCC
,LLVM µÈ¿ªÔ´Èí¼þ¶¼Ê¹Óà SVN ¹ÜÀí£¬´úÂëÍйÜÍøÕ¾·½Ã棬 SourceForge Ìṩ SVN µÄ´úÂëÍйܡ£
Visual SouceSafe£¨VSS£©ÊÇ΢Èí¿ª·¢µÄ°æ±¾¿ØÖÆÏµÍ³£¬µ½ÁË 2008Ä꣬±» Team Foundation
Server£¨TFS£© È¡´ú, TFS ²¢²»ÊÇ´«Í³ÒâÒåµÄ°æ±¾¿ØÖÆÏµÍ³£¬¶øÊÇÔÆ¿ª·¢Ð×÷ƽ̨£¬Ö§³Ö Team
Foundation Version Control ºÍ Git, Ïñ΢ÈíÕâÑùµÄÆóÒµ£¬ÎÞÂÛÊÇ Windows
»¹ÊÇ Office »¹ÊÇ ÆäËûÈí¼þ£¬´úÂëÁ¿¶¼·Ç³£¾Þ´ó£¬Ö»ÓÐÏñ TFS ÕâÑùÁ¿Éí¶¨×öµÄϵͳ²ÅºÏÊÊ¡£
Perforce ÊÇÒ»¸öÉÌÒµµÄ°æ±¾¿ØÖÆÏµÍ³£¬ÔÚÆä¹ÙÍø www.perfoce.com ½éÉÜ£¬ ÓÐ×ų¬¹ý10000¸öÓû§Ê¹ÓÃËûÃǵķþÎñ£¬ÓÐ
NVIDIA ,Sumsung,vmware,adidas µÈÖøÃûÆóÒµ£¬¶øÎÒ¶ÔËûµÄÓ¡ÏóÔÚÊÇ OpenWATCOM
C/C++ ±àÒëÆ÷ÒÔ¼° p4merge ¹¤¾ß¡£p4merge ÊÇ Perforce ÌṩµÄÒ»¸ö»ùÓÚ Qt
¿ª·¢µÄ¿çƽ̨±È½Ï¹¤¾ß¡£
Ó뼯ÖÐʽ°æ±¾¿ØÖÆÏµÍ³¶ÔÓ¦µÄÊÇ·Ö²¼Ê½°æ±¾¿ØÖÆÏµÍ³ (Distribution Version Control
System) ±È½ÏÁ÷ÐеÄÓÐ git ºÍ Mercurial, ¶þÕß¾ùµ®ÉúÓÚ 2005 Äê¡£
Git ÓÉ Linux Ö®¸¸£¬ Linus Torvalds ΪÁËÌæ´ú BitKeeper ¶ø¿ª·¢µÄ£¬¹ØÓÚ
Git µÄµ®Éú£¬¿ÉÒÔ¿´¶Ô Linus ±¾È˵IJɷ㺠10 Years of Git: An Interview
with Git Creator Linus Torvalds Git ·Ç³£Á÷ÐУ¬ Linux, FreeBSD,
.NET Core CLR, .NET Core Fx, Minix, Android µÈÏîÄ¿¶¼Ê¹Óà Git
À´¹ÜÀí£¬ Git µÄÉçÇø·Ç³£³ÉÊ죬Óкܶà´úÂëÍйÜÍøÕ¾ÌṩÍйܷþÎñ£¬Èç Github, Bitbucket,
¹úÄÚÓÐ OSC@GIT£¬coding£¬gitcafe, CSDN code, jd code µÈµÈ¡£
¼¼ÊõÉÏͬÑùÓÅÐãµÄ°æ±¾¿ØÖÆÏµÍ³ Mercurial µÄʹÓÃÕßÉٺܶ࣬ҲÓÐÖøÃûµÄä¯ÀÀÆ÷ Mozilla Firefox,·þÎñÆ÷
Nginx,ÒÔ¼°±à³ÌÓïÑÔ Python¡£ Mercurial ʹÓà Python ʵÏÖ£¬»òÐíÕâÒ»µãÒ²ÏÞÖÆÁË
Mercurial µÄ·¢Õ¹¡£
ÔÚά»ù°Ù¿ÆÖÐÓÐÒ»¸ö VCS ÁÐ±í£º Template:Version control software
¼Ç¼Á˶àÖÖ°æ±¾¿ØÖÆÏµÍ³£¬µ®Éúʱ¼ä£¬·ÖÀà¡£
´ó¶àÊýʱºò£¬¿ª·¢ÕßÐèҪѧϰµÄ°æ±¾¿ØÖÆÏµÍ³Îª Subversion »òÕßÊÇ GIT¡£Õâ¶þÕßÒÑÈ»ÊÇÁ½¸ö°æ±¾¿ØÖÆÁ÷ÅɵĴú±í¡£
Git ¼¼ÊõÄÚÄ»
±¾½ÚÖ÷Òª½éÉÜ Git µÄ´æ´¢ºÍ´«Êä
Git ´æ´¢
git ²Ö¿âÔÚ´ÅÅÌÉÏ¿ÉÒÔ±íÏÖΪÁ½ÖÖÐÎʽ£¬´øÓй¤×÷Ŀ¼µÄÆÕͨ²Ö¿âºÍ²»´ø¹¤×÷Ŀ¼µÄÂã²Ö¿â¡£
ÎÒÃÇ¿ÉÒÔ´´½¨Ò»¸ö±ê×¼²Ö¿â£º
mkdir gitrepo
&&cd gitrepo &&git --init &&tree
-a
|
Ŀ¼½á¹¹ÈçÏÂ
©À©¤©¤ .git
©¦ ©À©¤©¤ branches
©¦ ©À©¤©¤ COMMIT_EDITMSG
©¦ ©À©¤©¤ config
©¦ ©À©¤©¤ description
©¦ ©À©¤©¤ HEAD
©¦ ©À©¤©¤ hooks
©¦ ©¦ ©À©¤©¤ applypatch-msg.sample
©¦ ©¦ ©À©¤©¤ commit-msg.sample
©¦ ©¦ ©À©¤©¤ post-update.sample
©¦ ©¦ ©À©¤©¤ pre-applypatch.sample
©¦ ©¦ ©À©¤©¤ pre-commit.sample
©¦ ©¦ ©À©¤©¤ prepare-commit-msg.sample
©¦ ©¦ ©À©¤©¤ pre-push.sample
©¦ ©¦ ©À©¤©¤ pre-rebase.sample
©¦ ©¦ ©¸©¤©¤ update.sample
©¦ ©À©¤©¤ index
©¦ ©À©¤©¤ info
©¦ ©¦ ©¸©¤©¤ exclude
©¦ ©À©¤©¤ logs
©¦ ©¦ ©À©¤©¤ HEAD
©¦ ©¦ ©¸©¤©¤ refs
©¦ ©¦ ©¸©¤©¤ heads
©¦ ©¦ ©¸©¤©¤ master
©¦ ©À©¤©¤ objects
©¦ ©¦ ©À©¤©¤ 89
©¦ ©¦ ©¦ ©¸©¤©¤ 50b8b1af3c4cc712edb5a995c83a53eb03e6be
©¦ ©¦ ©À©¤©¤ d0
©¦ ©¦ ©¦ ©¸©¤©¤ 2d9281b58703d020c3afe3e2ace204d6d462ae
©¦ ©¦ ©À©¤©¤ e6
©¦ ©¦ ©¦ ©¸©¤©¤ 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
©¦ ©¦ ©À©¤©¤ info
©¦ ©¦ ©¸©¤©¤ pack
©¦ ©¸©¤©¤ refs
©¦ ©À©¤©¤ heads
©¦ ©¦ ©¸©¤©¤ master
©¦ ©¸©¤©¤ tags
©¸©¤©¤ helloworld
|
ʵ¼ÊÉÏÎÒÃÇ´´½¨Ò»¸öÂã²Ö¿â»á·¢ÏÖºÍÆÕͨ²Ö¿âµÄ .git Ŀ¼½á¹¹ÊÇÒ»Öµġ£
mkdir gitbare.git &&cd gitbare.git &&git
init --bare &&tree -a
Ŀ¼½á¹¹£º
.
©À©¤©¤ branches
©À©¤©¤ config
©À©¤©¤ description
©À©¤©¤ HEAD
©À©¤©¤ hooks
©¦ ©À©¤©¤ applypatch-msg.sample
©¦ ©À©¤©¤ commit-msg.sample
©¦ ©À©¤©¤ post-update.sample
©¦ ©À©¤©¤ pre-applypatch.sample
©¦ ©À©¤©¤ pre-commit.sample
©¦ ©À©¤©¤ prepare-commit-msg.sample
©¦ ©À©¤©¤ pre-push.sample
©¦ ©À©¤©¤ pre-rebase.sample
©¦ ©¸©¤©¤ update.sample
©À©¤©¤ info
©¦ ©¸©¤©¤ exclude
©À©¤©¤ objects
©¦ ©À©¤©¤ info
©¦ ©¸©¤©¤ pack
©¸©¤©¤ refs
©À©¤©¤ heads
©¸©¤©¤ tags
9 directories, 13 files |
µ±ÎÒÃÇ´´½¨Ò»¸ö²Ö¿âʱ£¬Ä¬ÈÏÇé¿öÏ»ᴴ½¨¹¤×÷Ŀ¼£¬ÔÚ¹¤×÷Ŀ¼ÏÂÓиö .git µÄ×ÓĿ¼£¬Õâ²ÅÊÇ´æ´¢¿âµÄĿ¼¡£
¶øÎÒÃÇͨ³£Ð޸ĴúÂëµÄĿ¼³ÆÖ®Îª¹¤×÷Ŀ¼¡£
ÖÚËùÖÜÖª£¬git ÊÇ·Ö²¼Ê½°æ±¾¿ØÖÆÏµÍ³£¬Õâ¾ÍÒâζ×Å£¬Ö»Òª»ñµÃÁË .git Ŀ¼µÄÍêÕûÊý¾Ý£¬¾Í¿ÉÒÔÔÚÈÎÒâλÖûָ´³ÉÒ»¸ö´øÓй¤×÷Ŀ¼µÄ²Ö¿â¡£¶ø
GIT ¿Ë¡һ¸ö´æ´¢¿âÒ²½ö½öÊǽ« .git/objects Ŀ¼Ï嵀 object ºÍ .git/refs
(.git/packed-refs|.git/info/refs) Ëù´æ´¢µÄÒýÓÃÁÐ±í´«Êäµ½±¾µØ£¬²¢Ó¦Óá£
¶ÔÓÚ Subversion Ò»ÑùµÄ¼¯ÖÐʽ°æ±¾¿ØÖÆÏµÍ³£¬¾ÍÏ൱ÓÚ .git Ŀ¼±»ÍйÜÔÚÖÐÑë·þÎñÆ÷ÉÏ£¬¶ø±¾µØµÄ
.svn Ö»Êǹ¤×÷Ŀ¼µÄÔªÊý¾Ý¡£
¶þÕß²»Í¬µÄ»úÖÆ´øÀ´µÄÖ±½Ó²î±ð¾ÍÊÇÒ»µ©ÖÐÑë·þÎñÆ÷å´»ú£¬git ¿ÉÒÔѸËÙµÄÇ¨ÒÆµ½ÆäËû·þÎñÆ÷£¬²¢ÇÒÊý¾ÝµÄ¶ªÊ§µÄ¿ÉÄÜÐÔºÜС£¬¶ø
Subversion ·þÎñÆ÷¾ÍûÓÐÕâôºÃµÄÔËÆøÁË¡£
ÿһ´ÎÌá½»£¬git ¶¼»á°ÑÐ޸ĵÄÎļþ¿ìÕÕ£¬»¹ÓиüеÄĿ¼½á¹¹£¬ÒÔ¼°Ìá½»ÐÅÏ¢£¬´ò°ü³ÉÒ»¸ö¸ö object£¬ÕâЩ
object ±»loose object, ËùÒÔ git µÄ object ¿ÉÄÜÊÇ blob tree
commit µÈ¡£´ò°üµÄ¹ý³Ì»áʹÓà zip ѹËõ£¬ÕâÖÖ±»¹ã·ºÔËÓõÄѹËõ¸ñʽʵÉÏѹËõÂʽϵͣ¬Ñ¹ËõËÙ¶ÈÒ²Âý£¬µ«ºÃ´¦Óй㷺µÄÖ§³Ö£¬×¨ÀûÉϱȽÏÓѺá£
Èç¹ûµ÷Óà git gc ÃüÁîºó£¬git-gc »á½«ÕâЩ object ´ò°ü³É pack Îļþ£¬ÕâЩÄÚÈÝÔÚ
proGit ¶¼ÓÐÏêϸ˵Ã÷¡£
Git ´«ÊäÐÒé
Git Ö§³Ö¶àÖÖÐÒé http, git , ssh, file ,ÒÔÄÚ²¿»úÖÆÇø·ÖÎªÑÆÐÒéºÍÖÇÄÜÐÒé£¬ÑÆÐÒé·Ç³£¼òµ¥£¬¼òµ¥µÄ˵£¬
¿Í»§¶Ëͨ¹ý URL Ö±½ÓÄÃÈ¡·þÎñ¶ËµÄÎļþ¡£
Git ÖÇÄÜÐÒéʵÏÖÁËÁ½Àà RPC µ÷Óã¬Ò»¸öÊÇ fetch-pack<->upload-pack,
ÁíÒ»¸öÊÇ send-pack<->receive-pack¡£
ÈκΠGit Ô¶³Ì²Ù×÷¶¼ÐèÒª»ñµÃÔ¶³Ì²Ö¿âµÄÒýÓÃÁÐ±í£¬Óë×ÔÉíµÄÒýÓÃÁÐ±í½øÐбȶÔ
ÕâÀïÒÔ HTTP ΪÀý
1 Fetch-Upload
Step 1:
Request
C: GET $GIT_URL/info/refs?service=git-upload-pack
HTTP/1.0
|
Response
S: 200 OK
S: Content-Type: application/x-git-upload-pack-advertisement
S: Cache-Control: no-cache
S:
S: 001e# service=git-upload-pack\n
S: 004895dcfa3633004da0049d3d0fa03f80589cbcaf31
refs/heads/maint\0multi_ack\n
S: 0042d049f6c27a2244e12041955e262a404c7faba355
refs/heads/master\n
S: 003c2cb58b79488a98d2721cea644875a8dd0026b115
refs/tags/v1.0\n
S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c
refs/tags/v1.0^{}\n
|
Step 2:
Request
C:
POST $GIT_URL/git-upload-pack HTTP/1.0
C: Content-Type: application/x-git-upload-pack-request
C:
C: 0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7\n
C: 0032have 441b40d833fdfa93eb2908e52742248faf0ee993\n
C: 0000 |
Response
S:
200 OK
S: Content-Type: application/x-git-upload-pack-result
S: Cache-Control: no-cache
S:
S: ....ACK %s, continue
S: ....NAK
|
2 Send-Receive ʵ¼ÊÉÏ push µÄ¹ý³ÌÒ²ÊÇ GET ºÍ POST£¬ Ö»²»¹ý£¬git-upload-pack
Òª±ä³É git-receive-pack £¬POST ʱ£¬ºóÕßÇëÇóÌåÖаüº¬ÓÐ ²îÒì package¡£
¶ÔÓÚ git HTTP À´Ëµ£¬È¨ÏÞÑé֤ͨ³£ÊÇ HTTP µÄÒ»Ì×£¬Ò²¾ÍÊÇ WWW-Authenticate£¬
¾ø´ó¶àÊýµÄ HTTP ·þÎñÆ÷Ò²¾ÍÖ§³Ö Basic¡£
¼´£º
user:password
->Base64 encode -->dXNlcjpwYXNzd29yZA==
|
ËùÒÔ´Ó°²È«ÉÏÀ´Ëµ£¬Èç¹ûʹÓà HTTP ¶ø²»ÊÇ HTTPS £¬ ¶Ô GIT Ô¶³Ì²Ö¿â½øÐÐд²Ù×÷¼òÖ±¾ÍÊÇÔÚÂã±¼¡£
git HTTP Ö§³ÖµÄ HTTP ·µ»ØÂë²¢²»¶à£¬ÕâЩÊÇ·µ»ØÂëÊÇÖ§³ÖµÄ£º 200 30x 304 403
404 410
¹ØÓÚ HTTP µÄ¸ü¶àÎĵµÏ¸½Ú¿ÉÒÔÈ¥Õâ¸öµØÖ·²é¿´£º HTTP Protocol
»ùÓÚ HTTP µÄÖÇÄÜÐÒéºÍ»ùÓÚ SSH£¬Git ÐÒé±¾ÖÊÉϲ¢ÎÞÌ«´óµÄ²»Í¬£¬¶¼ÊÇͨ¹ýÕâÁ½Àà RPC µ÷Óã¬ÊµÏÖ±¾µØ²Ö¿âºÍÔ¶³Ì²Ö¿âµÄÊý¾Ý½»»»¡£
HTTP ÐÒéÊÇͨ¹ý http smart server ÔËÐÐ git-xxx-pack£¬¶ÔÆäÊäÈëÊý¾Ý£¬È»ºó¶ÁÈ¡
git-xxx-pack Êä³ö¡£ SSH ÔòÊÇͨ¹ý ssh ·þÎñÆ÷ÔÚÔ¶³Ì»úÆ÷ÉÏÔËÐÐ git-xxx-pack
£¬Êý¾Ý´«ÊäµÄ¹ý³ÌʹÓà SSH ¼ÓÃÜ¡£ ¶ø GIT ÐÒé (git://) ÐÒéÔòÊÇ Í¨¹ýÔ¶³Ì·þÎñÆ÷ git-daemon
ÔËÐÐ git-xxx-pack ʵÏÖÊý¾ÝµÄ½»»¥¡£Í¨³£À´Ëµ git:// ÎÞ·¨ÊµÏÖ²îÒ컯µÄȨÏÞ¹ÜÀí£¬ Ò²¾ÍÊÇҪôȫ²¿Ö»¶Á£¬È«²¿¿Éд¡£
²é¿´ git daemon ³ÌÐò°ïÖú£º
һЩ¸ü¶àµÄ¼¼ÊõÄÚÄ»¿ÉÒԲο¼ ÉçÇø´ó×÷ ¡¶Pro Git¡·
Git ´úÂëÍÐ¹ÜÆ½Ì¨µÄ¿ª·¢Ñݽø
ËäÈ» GIT ÊÇ·Ö²¼Ê½°æ±¾¿ØÖÆ£¬µ«ÊǶÔÓÚ´úÂëÍÐ¹ÜÆ½Ì¨À´ËµÓÖÊÇÒ»»ØÊÂÁË¡£¶ÔÓÚ HTTP ÐÒéÀ´Ëµ£¬Ïñ NGINX
Ò»ÑùµÄ·þÎñÆ÷Ö»ÐèҪʵÏÖ¶¯Ì¬ IP, È»ºóͨ¹ý proxy »òÕßÊÇ upstream µÄ·½Ê½ÊµÏÖ GIT
´úÂëÍÐ¹ÜÆ½Ì¨µÄ ·Ö²¼Ê½¾Í¿ÉÒÔÁË¡£µ«ÊǶÔÓÚ SSH À´Ëµ±È½ÏÂé·³¡£
»ùÓÚ RPC µÄ GIT ·Ö²¼Ê½Éè¼Æ
¿Í»§¶Ë·ÃÎʲֿâʱ£¬Â·ÓÉÖÇÄܵ½´ï DNS Ëù¼Ç¼µÄ»úÆ÷»òÕßÊÇÎÞ²î±ð´úÀíµÄ»úÆ÷(ǰ¶Ë»úÆ÷)£¬ÍùÍù²»Äܵ½´ïÌØ¶¨µÄ´æ´¢»úÆ÷£¬
¿ª·¢ÕßʹÓ÷ֲ¼Ê½Îļþϵͳ»òÕß ·Ö²¼Ê½ RPC »òÕß´úÀíµÈ¶àÖÖ·½°¸ÊµÏÖ Ç°¶Ëµ½´æ´¢µÄ¹Ø¼üÒ»²½¡£ÕâÀïÖ÷Ҫ˵·Ö²¼Ê½
RPC Óë GIT smart µÄÓ¦Óá£
·Ö²¼Ê½ RPC ¿ò¼ÜºÜ¶à£¬ÆäÖÐÖøÃûµÄÓÐ Apache Thrift ,´ËÏîÄ¿ÊÇ Facebook ¿ªÔ´²¢¹±Ï׸ø
Apache »ù½ð»áµÄ£¬Ö§³Ö¶àÖÖÓïÑÔ¡£
¶ÔÓÚ GIT ²Ù×÷£¬Ö»ÐèҪʵÏÖ 4¸öº¯Êý¡£Ò»ÏÂÊÇ Thrift ½Ó¿ÚÎļþµÄÒ»²¿·Ö£º
service GitSmartService{
i32 Checksum(1:i32 client);
string FetchRemoteReferences(1:string repositoryPath);
binary FetchRemoteDiffPackage(1:string repositoryPath,
2:string clientReferences)
string PushRemoteRefereces(1:string repositoryPath);
string PushRemoteDiffPackage(1:string repositoryPath,
2:binary clientPackage);
}
|
È»ºó´æ´¢·þÎñÆ÷ͨ¹ý pipe ¶ÁÈ¡´æ´¢»úÆ÷É쵀 git-upload-pack /git-receive-pack
µÄÊäÈëÊä³ö¡£ ÔÚ Linux ÉÏͨ¹ý¹ÜµÀ¶ÁÈ¡ git upload-pack µÄÊä³ö£º
int FetchRemoteReferencesCli(std::string
&result,const std::string &path){
result.clear();
int pid,fd[2];
if(pipe(fd)<0){
printf("oops\n");
}
if((pid=fork())<0){
printf("fork failed \n");
return -1;
}else if(pid==0){
if(fd[1]!=STDOUT_FILENO){
if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO){
return -1;
}
close(fd[1]);
}
if(execlp("git","git","upload-pack","--stateless-rpc","--advertise-refs",path.c_str(),NULL)==-1){
printf("execlp failed \n");
exit(0);
}
}else{
char buffer[4096]={0};
close(fd[1]);
int n=0;
while((n=read(fd[0],buffer,4096))){
result.append(buffer,n);
}
close(fd[0]);
}
return 0;
}
|
ǰ¶Ë·þÎñÆ÷ÉÏ£¬±àд Ä£Äâ git-upload-pack »òÕßÊÇ git-receive-pack µÄ³ÌÐò¡£Óû§Í¨¹ý
ssh ·ÃÎÊÔ¶³Ì²Ö¿âʱִÐÐµÄ git ¹¤¾ß±ä³ÉÁËÄ£ÄâºóµÄ git-upload-pack /git-receive-pack,
µ±Ê¹Óà HTTP ·ÃÎÊʱ£¬¿ÉÒÔÕûºÏ³É RPC ¿Í»§¶ËÕûºÏÖ±½ÓÕûºÏ½ø HTTP ·þÎñÆ÷£¬±ÈÈç NGINX
Ä£¿é£¬ »òÕßÒ²¿É ʹÓà ´«Í³µÄ Git Smart HTTP ¿âµÄ·½Ê½£¬×ܵÄÀ´Ëµ Thrift ÓжàÖÖÓïÑÔÖ§³Ö£¬Git
Smart HTTP ÕûºÏ Thrift RPC ²¢²»³ÉÎÊÌâ¡£
Õâ¸öΨһµÄÎÊÌâÊÇʵÏÖÒì²½±È½ÏÂé·³£¬Á½Õß¶¼ÐèҪʵÏÖÒ첽ģʽ£¬git ²Ö¿â¿ÉÄܷdz£´ó£¬Ò»´ÎÐÔ¿Ë¡´«ÊäÊý¾Ý¼¸°Ù
MB »òÕßÉÏ GB, Õâ¸öʱºò 4nK ·¢Ëͷdz£±ØÒª¡£
»ùÓÚ libgit2 µÄ smart ÐÒéʵÏÖ
GIT ³ýÁË Linus ±¾ÈËʵÏÖ£¬kernel.org ÍйܵĹٷ½°æ±¾Í⣬»¹ÓÐ jgit,libgit2
µÈ£¬git ÊÇһϵÁÐÃüÁî×é³É£¬¼¸ºõûÓаþÀë³ö¹²Ïí¿âµÄÄÜÁ¦£¬ ÕâÑùµÄºó¹ûµ¼ÖÂÆäËûÓïÑÔʹÓà git ʱ£¬²»µÃ²»Ê¹ÓùܵÀµÈ½ø³Ì¼äͨѶµÄģʽÓë
git ¹¤¾ß½»»¥¡£¶ø jgit ʹÓà Java ʵÏÖ£¬»ù±¾ÉÏûÓÐÆäËûÁ÷ÐÐÓïÑԵİó¶¨ÄÜÁ¦¡£
libgit2 ÊÇÒ»¸ö GIT µÄ¼æÈÝʵÏÖ£¬»ùÓÚ C89 ¿ª·¢£¬Ö§³Ö¾ø´ó¶àÊý git ÌØÐÔ¡£¿ª·¢·Ç³£»îÔ¾£¬ÓжàÖÖÓïÑ԰󶨣¬Èç
C# Ruby µÈ£¬ ÆäÖÐ C# °ó¶¨ Libgit2Sharp ±» VisualStudio, Github
for Windows µÈʹÓ㬶ø Ruby °ó¶¨ Rugged £¬±» Github, GIT@OSC
µÈ´úÂëÍÐ¹ÜÆ½Ì¨Ê¹Óá£
libgit2 ²¢Ã»ÓкÏÊ浀 GIT smart ·þÎñÆ÷ºó¶ËʵÏÖ£¬¶àÊýÇé¿öÏ£¬libgit2 Ö÷ÒªÃæÏòµÄÊǿͻ§¶Ë£¬ÓÉÓÚ
git ÊÇ·Ö²¼Ê½µÄ£¬¶ÔÓÚ²Ö¿âµÄ¶ÁдҲ¾Í¿Í»§¶Ë ºÍ·þÎñÆ÷µÄÐÐΪҲÊÇÀàËÆµÄ¡£
Subversion ÄÚÄ»
´Ë²¿·ÖÖÐ SVN ÐÒé Ö¸ Apache Subversion ³ÌÐò svn£¨ÒÔ¼°¼æÈݵĿͻ§¶Ë£© ÓëÔ¶³Ì·þÎñÆ÷ÉϵÄ
Apache Subversion svnserve £¨ÒÔ¼°¼æÈݵķþÎñÆ÷£© ½ø³ÌͨѶµÄÐÒ飬 ¼´ Subversion
protocol£¬ÐÒéĬÈ϶˿ÚÊÇ 3690£¬»ùÓÚ TCP, ´«ÊäÊý¾ÝʹÓà ABNF ·¶Ê½¡£
ÔÚÕâÀïÖ¸³ö£¬Óë Git ÍêÈ«²»Í¬µÄÊÇ£¬svn µÄ²Ö¿â´æ´¢ÔÚÔ¶³ÌÖÐÑë·þÎñÆ÷ÉÏ£¬¿ª·¢Õß¼ì³öµÄ´úÂëÖ»ÊÇÌØ¶¨°æ±¾£¬Ìض¨Ä¿Â¼µÄ´úÂ룬±¾µØÎª¹¤×÷¿½±´¡£
Subversion HTTP ÐÒéʵÏÖ
Subversion HTTP ÐÒéÊÇÒ»ÖÖ »ùÓÚ WebDAV/DeltaV µÄÐÒ飬WebDAV ÔÚ
HTTP 1.1 µÄ»ù´¡ÉÏÀ©Õ¹Á˶à¸ö Method, ¾ø´ó¶àÊýµÄ·þÎñÆ÷²¢²»Ö§³Ö WebDAV, ÕâÑùµÄºó¹û¾ÍÊÇ£¬³ýÁË
Apache ¿ÉÒÔʹÓà mod_dav_svn ²å¼þ£¬»ù±¾ÉÏÔÙҲûÓÐÆäËûµÄ·þÎñÆ÷ÄÜ¿ìËÙµÄÖ§³Ö Subversion
µÄ HTTP ÐÒéÁË¡£´úÀí»¹ÊÇ¿ÉÒԵġ£
WebDAV ÐÒéÔÚ HTTP 1.1 µÄ»ù´¡ÉÏ Ê¹Óà XML µÄ·½Ê½³ÊÏÖÊý¾Ý£¬¶ÔÓÚ Subversion
ÕâÖÖ¼¯ÖÐʽ°æ±¾¿ØÖÆÏµÍ³À´Ëµ£¬¾ø´ó¶àÊý²Ù×÷¶¼ÊÇÔÚÏߵģ¬ WebDAV °ü¹üÕâЩ²Ù×÷¾Í±äµÃºÜ·±Ëö¡£
±ÈÈçÒ»¸ö update-report ÇëÇó£º
<S:update-report
send-all="true" xmlns:S="svn:">
<S:src-path>http://localhost:8080/repos/test/httpd/support</S:src-path>
<S:target-revision>2</S:target-revision>
<S:entry rev="2" start-empty="true"></S:entry>
</S:update-report>
|
È»ºó·þÎñÆ÷·µ»Ø£º
<S:update-report
xmlns:S="svn:" xmlns:V="..."
xmlns:D="DAV:" send-all="true">
<S:target-revision rev="2"/>
<S:open-directory rev="2">
<D:checked-in>
<D:href>/repos/test/!svn/ver/2/httpd/support</D:href>
</D:checked-in>
<S:set-prop name="svn:entry:committed-rev">2</S:set-prop>
... more set props ...
<S:add-file name="ab.c">
<D:checked-in>
<D:href>/repos/test/!svn/ver/2/httpd/support/ab.c</D:href>
</D:checked-in>
<S:set-prop name="svn:entry:committed-rev">2</S:set-prop>
... more set props for the file ...
<S:txdelta>...base64-encoded file content...</S:txdelta>
</S:add-file>
<S:add-directory name="os" bc-url="/repos/test/!svn/bc/2/httpd/os">
<D:checked-in>
<D:href>/repos/test/!svn/ver/2/httpd/os</D:href>
</D:checked-in>
...directory contents...
</S:add-directory>
</S:open-directory>
</S:update-report>
|
²»Í¬µÄÇëÇó£¬xml µÄÄÚÈÝÒ²ÍêÈ«²»Í¬£¬Subversion HTTP ÐÒéµÄ¸´ÔÓÒ²Èúܶ࿪·¢ÕßÍû¶øÈ´²½¡£
ÔÚ Subversion µÄ·ÏßͼÖУ¬»ùÓÚ WebDAV/DeltaV µÄ HTTP ½ÓÈ뽫±» »ùÓÚ
HTTP v2 µÄʵÏÖÈ¡´ú¡£
A Streamlined HTTP Protocol for Subversion
Subversion SVN ÐÒéʵÏÖ
Óë HTTP ²»Í¬µÄÊÇ£¬Ò»¸öÍêÕûµÄ»ùÓÚ SVN ÐÒéµÄÁ¬½ÓÖУ¬²Ö¿âµÄ²Ù×÷ÊÇÉÏÏÂÎÄÏà¹ØµÄ¡£
µ±¿Í»§¶ËµÄÁ¬½Ó¹ýÀ´Ê±£¬·þÎñÆ÷£¬Í¨³£ËµµÄ svnservice ½«·¢ËÍÒ»¶ÎÐÅÏ¢¸ø¿Í»§¶Ë£¬¸æÖª·þÎñÆ÷µÄÄÜÁ¦¡£
S: ( minver:number
maxver:number mechs:list ( cap:word ... ) )
|
Example:
( success
( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries
depth inherited-props log-revprops ) ) )
|
Õâ¸öʱºò¿Í»§¶Ë»ñÖªÁËÕâЩÊý¾Ý£¬Èç¹ûÎÞ·¨¼æÈÝ£¬·þÎñÆ÷£¬ÄÇô½«¶Ï¿ªÓë·þÎñÆ÷µÄÁ¬½Ó£¬·ñÔò£¬½«·¢ËÍÇëÇóÊý¾Ý¸ø·þÎñÆ÷£¬¸ñʽÈçÏ£º
C: response:
( version:number ( cap:word ... ) url:string
? ra-client:string ( ? client:string ) )
|
Example:
( 2 ( edit-pipeline
svndiff1 absent-entries depth mergeinfo log-revprops
) 36:svn://subversion.io/subversion/trunk 53:SVN/1.8.13-SlikSvn-1.8.13-X64
(x64-microsoft-windows) ( )
|
Óë GIT Êý¾Ý°üÀàËÆµÄµØ·½ÓÐÒ»µã£¬git ÿһÐÐÊý¾Ýǰ 4 ¸ö16½øÖÆ×Ö·û´ú±í±¾Ðеij¤¶È£¬¶ø ÕâÀïµÄ
10 ½øÖÆ×Ö·û´ú±í ×Ö·ûµÄ³¤¶È£¬±ÈÈç URL ³¤¶È36£¬UA 53¡£
·þÎñÆ÷´ËʱµÄÐÐΪ¾ÍµÃͨ¹ý½âÎö URL »ñµÃÖÐÑë²Ö¿âµÄλÖã¬ÅжÏÐÒéÊÇ·ñ¼æÈÝ£¬¶ø UA ÓпÉÄÜΪ¿Õ£¬¸ñʽ²¢²»ÊǷdz£±ê×¼£¬ËùÒÔÕâÊÇÖµµÃ×¢ÒâµÄµØ·½¡£
·þÎñÆ÷½«¾ö¶¨Ê¹ÓÃÄÇÖÖÊÚȨ·½Ê½£¬MD5 Ò»°ãÊÇ Subversion ¿Í»§¶ËĬÈϵģ¬ÎÞ·¨µÚÈý·½¿âÖ§³Ö£¬¶ø
PLAIN ºÍ ANONYMOUS ÐèÒª SASL Ä£¿éµÄÖ§³Ö£¬ ÔÚ Ubuntu ÉϱàÒë svn,ÏȰ²×°
libsasl2-dev¡£
S: ( ( mech:word
... ) realm:string )
|
¿Í»§¶Ë²»Ö§³Ö´ËÊÚȨ·½Ê½Ê±£¬»áÊä³ö´íÎóÐÅÏ¢£¬¡°ÎÞ·¨ÐÉÌÑéÖ¤·½Ê½¡±
ÕâÀïµÄ Realm ÊÇ subversion ¿Í»§¶Ë´æ´¢Óû§ÕË»§Óû§ÃûºÍÃÜÂëÐÅÏ¢µÄÒ»¸ö key,Ö»Òª
realm Ò»Ö£¬¾Í»áÈ¡ÏàͬµÄ Óû§ÃûºÍÃÜÂë¡£ realm RFC2617
Example:
( success
( ( PLAIN ) 36:e967876f-5ea0-4ff2-9c55-ea2d1703221e
) )
|
Èç¹ûÊÇ MD5 £¬ÑéÖ¤ÐÉÌÈçÏ£º
S: ( mech:word
[ token:string ] )
|
Õâ¸ö Token ÊÇËæ»úÉú³ÉµÄ UUID, C++ ¿ÉÒÔʹÓà boost Éú³É£¬Ò²¿ÉÒÔʹÓÃÆ½Ì¨µÄ API
Éú³É¡£
Èç¹ûÊÇ PLAIN ÊÚȨ»úÖÆ£¬ÕâÀï¾ÍÊÇÓû§ÃûºÍÃÜÂë¾ Base64 ±àÂëÁË, Óà NUL(0) ·Ö¸ô
usernameNULpassword
--> Base64 Encoded
|
Example:
( PLAIN (
44:YWRtaW5Ac3VidmVyc2lvbi5pbyU1QzBwYXNzd29yZA==
) )
|
¶ÔÓÚ´¿ svn ÐÒéÀ´Ëµ£¬Ê¹Óà PLAIN ²¢²»°²È«£¬ÇÒµ± Subversion Ö»×÷Ϊ GIT ´úÂëÍÐ¹ÜÆ½Ì¨µÄÒ»¸ö·þÎñÀ´Ëµ£¬
ʹÓà CRAM-MD5 ²¢²»ÀûÓÚ·þÎñÕûºÏ£¬ÕâÒ²ÊÇÒ»¸öȱÏÝÁË¡£
ÕâÊÇ·þÎñÆ÷µÄÏÂÒ»²½Ö裺
S: challenge:
( step ( token:string ) )
S: | ( failure ( message:string ) )
S: | ( success [ token:string ] )
|
Incorrect credentials:
( failure
( 21:incorrect credentials ) )
|
Success
Ëæºó·þÎñÆ÷ÔÙ·¢ËÍ´æ´¢¿â UUID, capabilities ¸ø¿Í»§¶Ë
S:
( uuid:string repos-url:string ( cap:word ...
) )
|
Example:
(
( 36:0f475597-c342-45b4-88c5-7dc0857b8ba4 36:svn://subversion.io/subversion/trunk
( edit-pipeline svndiff1 absent-entries depth
inherited-props log-revprops ))
|
Èç¹ûÊÇ svn up/commit »òÕ߯äËûµÄ²Ù×÷£¬Õâ¸öʱºò»á¼ì²é uuid ÊÇ·ñÆ¥Å䣬µ±È»Ò²»á¼ì²é URL
ÊÇ·ñÆ¥Åä¡£
Èç¹û¿Í»§¶Ë¾õµÃÒ»Çж¼ OK À²£¬ÄÇô¾Í»á¿ªÊ¼ÏÂÒ»½×¶ÎµÄ²Ù×÷£¬command ģʽ£¬ÕâЩ¹æÔò¿ÉÒÔ´Ó Subversion
¹Ù·½´æ´¢¿â²é¿´ Subversion Protocol
Óë GIT »òÕß SVN HTTP ²»Í¬µÄÊÇ£¬Ò»¸öÍêÕûµÄ »ùÓÚ svn ÐÒéµÄ SVN ²Ù×÷£¬Ö»ÐèÒª½¨Á¢Ò»´Î
socket£¬Subversion ¿Í»§¶Ë´ËʱÊÇ×èÈûµÄ£¬²¢ÇÒÆÁ±ÎÁË Ctrl+C µÈ Ðźţ¬ ²Ö¿âÌå»ý¾Þ´óʱ£¬ÕâÖÖ¶ÔÁ¬½Ó×ÊÔ´µÄÕ¼Ó÷dz£Í»³ö£¬ÒòΪÓÐÊý¾Ý¶ÁÈ¡£¬
socket ²¢²»»á³¬Ê±¡£ÕâÑùµÄ»úÖÆÊ¹µÃ svn ·þÎñÆ÷µÄ²¢·¢Êܵ½ÁËÏÞÖÆ¡£
Subversion ¼æÈÝʵÏÖ
Github »ùÓÚ HTTP ÐÒéµÄ·½Ê½ÊµÏÖÁË¶Ô Subversion µÄ¼æÈÝ£¬¶ø GIT@OSC »ùÓÚ
svn ÐÒ鷽ʽʵÏÖÁË¶Ô Subversion µÄ²»ÍêÈ«¼æÈÝ¡£
»ùÓÚ HTTP ÐÒéʵÏÖµÄ Subversion ¼æÈÝ·þÎñºÍ »ùÓÚ SVN ÐÒéµÄ Subversion
¼æÈÝ·þÎñ¶þÕß²¢²»ÄÜ˵˾ÍÒ»¶¨ºÃ£¬HTTP ÐÒéºÜÈÝÒ×µ¼ÖÂÍø¹Ø³¬Ê±£¬ ¶à´óÊýÇé¿öÏ£¬Ò»´ÎÍêÕûµÄ²Ù×÷ʱ³ÉǧÉÏÍòµÄ
HTTP ÇëÇ󹹳ɣ¬HTTP ÐÒéÖ§³ÖÐèÒª HTTP ·þÎñÆ÷Äܹ»Ö§³Ö WebDAV, XML ½âÎö¹ý³Ì±È½ÏÂé·³£¬
Subversion ¹Ù·½Ò²¼Æ»®Ê¹Óà HTTP v2 È¡´ú WebDAV£¬µ« HTTP ÐÒéµÄºÃ´¦»¹ÊÇÓе쬱ÈÈçºÜ¶àÆóÒµ²¢²»Ò»¶¨¿ª·Å
SVN ¶Ë¿Ú 3690£¬ ¿ÉÒÔºÍ gitlab Ö®ÀàµÄ·þÎñÕûºÏ¡£
¶ø SVN ÐÒéÒ²Óв»ºÃµÄµØ·½£¬±ÈÈçÁ¬½Óʱ¼ä¹ý³¤£¬·þÎñÆ÷²¢·¢Éϲ»È¥£¬ÈÝÒ××èÈû£¬Óë HTTP ·þÎñÕûºÏ²»±ã£¬µ«Í¬Ê±
SVN ÐÒéÄܹ»Ö§³Ö½Ï´ó´æ´¢¿â¡£
ʵ¼ÊÉϼæÈÝʵÏÖ SVN ½ÓÈëÍùÍùûÓÐÔÉúµÄ SVN ·þÎñºÃ£¬ÕâµãÊÂÎãÓ¹ÖÃÒɵġ£
Subversion ÐÒé´úÀí·þÎñÆ÷µÄʵÏÖ
Ç°Ãæ²¢²»ÍêÈ«µÄ·ÖÎöÁË SVN ÐÒ飬µ«ÊÇÄÇЩÐÒéÄÚÈÝ×㹻ʵÏÖÒ»¸ö SVN ÐÒ鶯̬´úÀí·þÎñÆ÷ÁË¡£
ÔÚ¿Í»§¶Ë C ºÍ´úÀí·þÎñÆ÷ S ½¨Á¢Á¬½Óºó£¬ S Ïò C ·¢ËÍÒ»¸öÊý¾Ý°ü£º
·þÎñÆ÷Í·
#S to C
#S to C
( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries
depth inherited-props log-revprops ) ) )
|
C ½ÓÊÕµ½ S µÄÊý¾Ýºó£¬±ØÐë×ö³öÑ¡Ôñ£¬²¢·¢Ë͵ÚÒ»¸öÇëÇó¸ø S¡£
#C to S
#C to S
( 2 ( edit-pipeline svndiff1 absent-entries depth
mergeinfo log-revprops ) 43:svn://subversion.io/apache/subversion/trunk
53:SVN/1.8.13-SlikSvn-1.8.13-X64 (x64-microsoft- windows)
( ) )
|
S ½ÓÊÕµ½ C µÄÇëÇóºó£¬½âÎö Êý¾Ý°ü£¬ÌáÈ¡µ½ URL Ϊ svn://subversion.io/apache/subversion/trunk
, ¶ø Gitlab µÄ¹æÔòÊÇ host/user/repo, Èç¹û²»Í¬Óû§µÄ´æ´¢¿â·ÅÔÚ²»Í¬»úÆ÷ÉÏ£¬Õâ¸öʱºòÌáÈ¡µ½Óû§Îª
apache, ½»ÓÉ·ÓÉÑ¡ÔñÄ£¿éÈ¥´¦ÀíµÃµ½ºó¶ËµÄµØÖ·£¬Ò²¾ÍÊÇÕæÊµ svnserve µÄ IP ºÍ¶Ë¿Ú¡£
½¨Á¢Óëºó¶Ë·þÎñÆ÷ B µÄÁ¬½Ó¡£Õâ¸öʱºò S ¶ÁÈ¡ B µÄÊý¾Ý°ü£¬Ò²¾ÍÊÇÇ°ÃæµÄ·þÎñÆ÷Í·,½ÓÊÕÍê±ÏÖ±½Ó¶ªÆú¼´¿É£¬È»ºó½«¿Í»§¶Ë
C µÄÍ·ÇëÇóת·¢¸øºó¶Ë·þÎñÆ÷¡£
#S
to B
( 2 ( edit-pipeline svndiff1 absent-entries depth
mergeinfo log-revprops ) 43:svn://subversion.io/apache/subversion/trunk
53:SVN/1.8.13-SlikSvn-1.8.13-X64 (x64-microsoft- windows)
( ) ) |
ÕâÀïÖµµÃ×¢ÒâµÄÊÇ svnkit£¬Subversion Javahl ²¢Ã»ÓÐÌí¼Ó UA ×Ö·û´®£¬ËùÒÔ½âÎöʱÂÔ¹ý¼´¿É¡£
ÖÁ´Ë£¬´úÀí·þÎñÆ÷µÄºóÃæ¾Í²»±Ø¹ØÏµÏ¸½ÚÁË£¬GIT@OSC ʹÓà Boost.ASIO Òì²½¿ò¼Ü£¬
Client <--->
Proxy Server <---> Backend Subversion Server
|
Ò»¸ö»ù±¾µÄ SVN ÐÒ鶯̬´úÀí·þÎñÆ÷¾ÍʵÏÖÁË¡£
½áβ
Èç¹ûÄã²»ÊÇרҵµÄ Git »òÕß Subversion ¿ª·¢Õߣ¬Äã¿ÉÄÜ»á¾õµÃÉÏÃæµÄÄÚÈÝûʲôÓô¦£¬Êµ¼ÊÉÏҲûʲô¼¼ÊõÄѶȡ£ |