寫在前面
Git 的官網上有很詳細的使用教程(當然有翻譯版本),具體地址是 https://git-scm.com/book/zh/v2。唯一不足就是,很多講解並沒有實機演示。但是,毫無疑問,官網資料是最全面的!如果有任何疑問,可以去官網看看!
協議
Git 通常也會有個遠程倉庫。用來協調各個參與者的工作!這與上一代集中式版本控制系統的作用基本類似。一個遠程倉庫通常只是一個裸倉庫(bare repository),即一個沒有當前工作目錄的倉庫。 因為該倉庫僅僅作為合作媒介,不需要從磁盤檢查快照;存放的只有 Git 的資料。 簡單的說,裸存倉庫就是你工程目錄內的 .git 子目錄內容,不包含其他資料。
Git 本地倉庫與遠程倉庫之間的交互需要遵循一定的協議。Git 可以使用四種主要的協議來傳輸資料:本地協議(Local),HTTP 協議,SSH(Secure Shell)協議及 Git 協議。
本地協議(Local)
本地協議主要是針對版本庫就是硬盤內的另一個目錄的情況。常見於團隊每一個成員都對一個共享的文件系統(例如一個掛載的 NFS)擁有訪問權。使用本地協議,就可以從本地版本庫克隆(clone)、推送(push)以及拉取(pull)代碼。這與操作其他遠程倉庫一樣!
如果在 URL 開頭明確的指定 file://,那么 Git 的行為會略有不同。 如果僅是指定路徑,Git 會嘗試使用硬鏈接(hard link)或直接復制所需要的文件。 如果指定 file://,Git 會觸發平時用於網路傳輸資料的進程。如下:
git clone /opt/git/project.git
# 區別於
git clone file:///opt/git/project.git
我們可以使用本地協議來模擬學習 Git。如下:
- 建立一個目錄,用來存放 Git 倉庫(一個目錄作為遠程倉庫,一個目錄作為本地倉庫),然后在遠程倉庫中,創建一個裸存倉庫。如下圖:
在 Git 中,裸存倉庫通常是 名 + .git 的形式! - 在本地倉庫,我們就可以直接克隆我們的遠程倉庫了,如下圖:
Git 會給出提示,我們正在克隆一個裸存倉庫!注意,我們克隆倉庫時,沒有指定路徑(含名稱),因此默認名稱就是遠程倉庫的名字。 - 在本地倉庫添加一個文件,並提交,如下圖:
- 將本地倉庫推送到遠程倉庫,如下圖:
成功推送之后,在遠程倉庫中會有對應的記錄。如上圖所示的遠程倉庫的 objects 目錄。 - 此時,我們就可以再建立一個 Local2,也來克隆我們的本地遠程倉庫,驗證一下有沒有 Local 推送的 a.txt 文件,如下圖:
其實,使用遠程協議的流程與本地協議是一樣的!
HTTP 協議
根據 Git 官網的描述,Git 通過 HTTP 通信有兩種模式。 在 Git 1.6.6 版本之前只有一個方式可用,十分簡單並且通常是只讀模式的;Git 1.6.6 版本引入了一種新的、更智能的協議,讓 Git 可以像通過 SSH 那樣智能的協商和傳輸數據。這兩種模式分別被稱為 啞(Dumb) HTTP 協議 和 智能(Smart) HTTP 協議。
啞 HTTP 協議里 web 服務器僅把裸版本庫當作普通文件來對待,提供文件服務。智能(Smart) HTTP 協議在服務端運行了兩組進程,分別負責上傳[客戶端的進程連接到服務端進程]和下載數據[客戶端的進程連接服務端],它們可以讀取本地數據,理解客戶端有什么和需要什么,並為它生成合適的包文件。
每次推送,Git 服務器會詢問用戶名與密碼。 默認情況下它會在終端中提示服務器是否允許你進行推送。如果不想在每一次推送時都輸入用戶名與密碼,你可以設置一個 “credential cache”。 最簡單的方式就是將其保存在內存中幾分鍾,可以簡單地運行 git config --global credential.helper cache
來設置它。在 Windows 系統中,在輸入過用戶名密碼之后,會被系統給保存下來!我們可以如下圖所示的位置進行修改:
由於我們有搭建服務器,因此這個不好驗證。但是 Git 官網有比較詳細的介紹這兩種協議的具體工作流程:https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE
SSH(Secure Shell)協議
架設 Git 服務器時常用 SSH 協議作為傳輸協議。 因為大多數環境下服務器已經支持通過 SSH 訪問 —— 即使沒有也很容易架設。 SSH 協議也是一個驗證授權的網絡協議;並且,因為其普遍性,架設和使用都很容易。
通過 SSH 協議克隆版本庫,你可以指定一個 ssh:// 的 URL:$ git clone ssh://user@server/project.git
或者使用一個簡短的 scp 式的寫法:$ git clone user@server:project.git
。你也可以不指定用戶,Git 會使用當前登錄的用戶名。
現有的很多平台(例如 Github)都是以 SSH 秘鑰的方式進行通信的!因此如果我們要以 SSH 的方式訪問遠程倉庫,則根據 SSH 協議,必須要生成我們 Key。Git 客戶端默認是帶有 OpenSSH 的,配置方式是使用命令ssh-keygen -t rsa -C "youremail@example.com"
,如下:
Git 協議
這是包含在 Git 里的一個特殊的守護進程,它監聽在一個特定的端口(9418),類似於 SSH 服務,但是訪問無需任何授權。 要讓版本庫支持 Git 協議,需要先創建一個 git-daemon-export-ok 文件 —— 它是 Git 協議守護進程為這個版本庫提供服務的必要條件 —— 但是除此之外沒有任何安全措施。 要么誰都可以克隆這個版本庫,要么誰也不能。 這意味着,通常不能通過 Git 協議推送。 由於沒有授權機制,一旦你開放推送操作,意味着網絡上知道這個項目 URL 的人都可以向項目推送數據。
Git 協議是現存最快的傳輸協議。如果你在提供一個有很大訪問量的公共項目,或者一個不需要對讀操作進行授權的龐大項目,架設一個Git守護進程來供應倉庫是個不錯的選擇。它使用與 SSH 協議相同的數據傳輸機制,但省去了加密和授權的開銷。
Git 協議消極的一面是缺少授權機制。用 Git 協議作為訪問項目的唯一方法通常是不可取的。一般的做法是,同時提供 SSH 接口,讓幾個開發者擁有推送(寫)權限,其他人通過 git:// 擁有只讀權限。Git 協議可能也是最難架設的協議。它要求有單獨的守護進程,需要定制,需要設定 xinetd 或類似的程序,而這些工作就沒那么輕松了。該協議還要求防火牆開放 9418 端口,而企業級防火牆一般不允許對這個非標准端口的訪問。大型企業級防火牆通常會封鎖這個少見的端口。
關聯遠程倉庫
Git 通常也會有個遠程倉庫。使用 git init
方式創建的本地倉庫只能本地使用,其沒有與任何遠程倉庫關聯!遠程倉庫的增、刪、改、查都是通過 git remote
命令來實現。
在 Git 中,會為關聯的遠程倉庫創建一個本地名稱,默認不顯示指定時為 origin,這樣我們在與遠程倉庫通信時,直接使用本地名稱就可以了!例如,我們要將代碼推送到以上關聯的遠程倉庫,直接使用 git push origin
就可以了。git remote
命令的格式如下:
git remote [-v | --verbose]
git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>
git remote rename <old> <new>
git remote remove <name>
git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
git remote set-branches [--add] <name> <branch>…
git remote get-url [--push] [--all] <name>
git remote set-url [--push] <name> <newurl> [<oldurl>]
git remote set-url --add [--push] <name> <newurl>
git remote set-url --delete [--push] <name> <url>
git remote [-v | --verbose] show [-n] <name>…
git remote prune [-n | --dry-run] <name>…
git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)…]
以下是一些常用的命令參數:
-v、--verbose
:用於列出關聯的遠程倉庫,格式為:本地名稱 + 遠程倉庫地址。該參數必須緊跟在 git remote 之后。如下圖:
git remote add
:命令格式:git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>
。關聯一個遠程倉庫 <url>,本地名稱為 <name>。
git remote rename
:命令格式:git remote rename <old> <new>
。將名為 <old> 的遠程倉庫名重命名為 <new>。git remote rm
:命令格式:git remote remove <name>
。其中,參數--rm
是--remove
的簡寫。刪除關聯的名為 <name> 的遠程倉庫的關聯信息。
與遠程倉庫相關的配置,均位於 工作目錄下/.git/config
文件中,這是個文本文件,我們也可以直接打開改文件進行修改。
還有一點就是,一個本地倉庫可以關聯多個遠程倉庫! 如下圖所示是關聯了兩個遠程倉庫之后的情況。
如上所示,建議在關聯遠程倉庫的時候,指定一個本地名稱,這樣我們就可以很容易的知道關聯情況。這個在我們往遠程倉庫提交代碼是很有用處!
關聯遠程分支(upstream)
有一種情況,我們建立的遠程倉庫並不是空的(里面有文件),在使用以上命令關聯本地倉庫和遠程倉庫之后,我們還並不能將本地修改提交到遠程倉庫!第一點是因為,由於遠程倉庫本身有內容,我們的本地倉庫與遠程倉庫並不是同步的!我們必須要將本地倉庫與遠程倉庫同步之后,才可以正常提交代碼。第二點是因為,多分支的問題,我們必須要關聯分支!如下圖所示:
從上圖中我們不難看出兩者的區別!遠程倉庫不空時,具體操作步驟如下:
# git初始化
git init
# 關聯遠程倉庫
git remote add --origin Github_xxx 遠程倉庫地址
# 獲取遠程倉庫 master 分支上的內容
git pull Github_xxx master
# 將本地倉庫分支設置為遠程倉庫的 master 分支
git branch --set-upstream-to=Github_xxx/master master
# 將全部文件加入 git 版本管理。 . 的意思是將當前文件夾下的全部文件放到版本管理中
git add .
# 提交文件 使用 -m 編寫注釋
git commit -m "注釋"
# 推送到遠程分支
git push
當然,如果遠程倉庫本身是空的(沒有任何文件),這步驟如下:
#git初始化
git init
#關聯遠程倉庫
git remote add --origin Github_xxx 遠程倉庫地址
#將全部文件加入git版本管理 .的意思是將當前文件夾下的全部文件放到版本管理中
git add .
#提交文件 使用-m 編寫注釋
git commit -m "注釋"
#推送到遠程分支
git push
克隆遠程倉庫
如果本地倉庫是直接使用 git clone
命令克隆自一個指定的遠程倉庫,則默認本地倉庫就是和克隆的倉庫關聯的!我們可以使用 git remote
命令來修改關聯信息。例如,如果在克隆時,使用的命令是git clone 遠程倉庫地址
而不是 git clone --origin 本地名字
,那么,默認使用 origin 來表示我們關聯的遠程倉庫,我們可以使用 git remote rename
來更改這個名字!
在我們想要保存遠程倉庫的文件夾中打開 Bash 命令框,執行 git clone https://github.com/ZCShou/N_W_Z_1.git
命令即可(注意將以上倉庫地址換為自己的)。如下圖:
該命令將遠程存儲庫克隆到新創建的目錄中,並為本地存儲庫中的每個分支創建遠程分支關聯,並創建和檢出從遠程存儲庫當前活動的分支派生出來的初始分支。
該命令的完整格式如下:
git clone [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch] [--no-tags]
[--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
[--[no-]remote-submodules] [--jobs <n>] [--] <repository>
[<directory>]
該命令常用參數如下:
-o <name>、--origin <name>
:指定遠程倉庫的本地名稱為 <name>,而不使用默認的 origin。-b <name>、--branch <name>
:用於克隆名為 <name> 的遠程分支,而不是以 HEAD 創建分支。在非空的倉庫中執行該命令,表示將指定的分支同步到當前倉庫!<repository>
:要克隆的(可能是遠程的)存儲庫。<directory>
:指定保存克隆的倉庫的本地目錄名稱。 如果沒有明確給出目錄,則使用遠程庫名字在當前目錄。只有當目錄為空時,才允許克隆到現有目錄。
克隆之后,默認會使用 git fetch
將更新所有遠程跟蹤分支,並且沒有參數的 git pull
將另外將遠程主分支合並到當前主分支中(如果有的話)。其中也會有個 .git 的文件夾!
使用以上命令之后,本地倉庫就會和以上指定的遠程倉庫管理關聯。在 Git 中,會為關聯的遠程倉庫創建一個本地名稱,不顯示指定時默認為 origin。我們可以在以上命令中使用參數 --origin 本地名稱
來指定本地名稱(git clone --origin NWZ_xxx https://github.com/ZCShou/N_W_Z_1.git
)。
遠程倉庫名字 “origin” 與分支名字 “master” 一樣,在 Git 中並沒有任何特別的含義一樣。 同時 “master” 是當你運行 git init 時默認的起始分支名字,原因僅僅是它的廣泛使用,“origin” 是當你運行 git clone 時默認的遠程倉庫名字。 如果你運行 git clone -o booyah,那么你默認的遠程分支名字將會是 booyah/master。
git fetch 與 git pull
一旦遠程主機的版本庫有了更新,需要先將這些更新取回本地,否則,在將本地提交到遠程倉庫時會報錯。這時就要用到 git fetch
或者 git pull
命令。
該命令用來從一個或多個其他存儲庫中獲取分支和/或標記(統稱為“refs”),以及完成其歷史記錄所需的對象。 該操作會更新遠程跟蹤分支。行完成后,你將會擁有那個遠程倉庫中所有分支的引用,可以隨時合並或查看。
必須注意 git fetch
命令會將數據拉取到你的本地倉庫——它並不會自動合並或修改你當前的工作。 當准備好時你必須手動將其合並入你的工作。而運行 git pull
通常會從最初克隆的服務器上抓取數據並自動嘗試合並到當前所在的分支。簡單來說,git pull = git fetch + git merge
git pull 命令:
格式:$ git pull <遠程主機名> <遠程分支名>:<本地分支名>
舉例:$ git pull Docs master:master # 將遠程倉庫Docs的master分支克隆到本地master分支
# 如果與當前本地分支,則:后可以省略:
$ git push Docs master # 取回Docs/master分支,合並到當前本地分支
# 在某些場合,Git會自動在本地分支與遠程分支之間,建立一種追蹤關系(tracking)。
# 比如,在git clone的時候,所有本地分支默認與遠程主機的同名分支,建立追蹤關系,也就是說,本地的master分支自動”追蹤”Docs/master分支。
$ git branch --set-upstream master Docs/next
# 如果當前分支與遠程分支存在追蹤關系,git pull就可以省略遠程分支名。
$ git pull origin
# 如果當前分支只有一個追蹤分支,連遠程主機名都可以省略。
$ git pull
git fetch 命令:
# 將某個遠程主機的更新(所有分支),全部取回本地
$ git fetch <遠程主機名>
# 如果只想取回特定分支的更新,可以指定分支名
$ git fetch <遠程主機名> <分支名>
# 注意:fetch后,內容並沒有真正保存,修改手動合並
$ git merge origin/master
# 也可以在它的基礎上,使用git checkout命令創建一個新的分支
# 所取回的更新,在本地主機上要用 ”遠程主機名/分支名” 的形式讀取。比如Docs主機的master,就要用Docsmaster讀取。
$ git checkout -b newBrach Docs/master
git push
該命令用來將本地倉庫(分支)同步到遠程倉庫(分支)。該命令將使用本地引用更新遠程引用,同時發送完成給定引用所必需的對象。
git push -u Docs master #這里的Docs 就是上面 起的別名; -u 表示:以后操作,默認的倉庫為Docs,默認關聯的分支為master
- 1
使用上面的-u 以后,后續很多操作都可以簡化,如下:
git push 命令:
格式:$ git push <遠程主機名> <本地分支名>:<遠程分支名>
舉例:$ git push Docs master:master # 將本地master分支推送到遠程倉庫Docs的master分支
# 如果省略本地分支名,則表示刪除指定的遠程分支,因為這等同於推送一個空的本地分支到遠程分支。例如:
$ git push Docs :master
# 等同於
$ git push Docs --delete master
# 如果當前分支與遠程分支之間存在追蹤關系,則本地分支和遠程分支都可以省略。( 即使用 git push -u 關聯的那個)
$ git push Docs
# 如果當前分支只有一個追蹤分支,那么主機名都可以省略。
$ git push
代碼托管平台
代碼托管平台主要用來輔助項目開發,同時協調項目程序的開發工作。一般的代碼托管平台除了提供基本的源代碼版本控制之外,還通常會集成一系列項目輔助開發工具。
Github
Github 應該是大家最熟悉的一個代碼托管平台。目前應該是最大的開源軟件托管平台。針對於個人以及企業,Github 有不同的套餐,具體如下:
如上圖列出了不同類型的用戶的限制條件。最初,Github 的免費用戶僅可以創建公開倉庫,被微軟在 2018 年 6 月4 日收購之后,現在可以創建無限的公開或者私有倉庫。
登錄自己的賬號,在賬號下面就可以創建我們自己的倉庫了。
Github 的倉庫支持通過 HTTPS 和 SSH 協議來克隆倉庫。打開倉庫的主頁,就會發下如下:
在使用 SSH 方式的時候,我們必須要將 SSH 的公鑰放到 Github 的賬戶下。至於如何生成 SSH 的公鑰,參見之前的博文即可。具體如下:
特別說明一下,就是創建組織,上圖左側列表中的 Organizations 選項下,我們就可以創建組織,創建組織之后,我們就可以直接在組織中,通過 Github 賬戶名來添加組織成員。
GitLab
使用方式與 Github 差不多。https://about.gitlab.com
Bitbucket
使用方式與 Github 差不多。Github 中的組織這里被稱為 Team。
碼雲
碼雲是開源中國旗下的 Git 代碼托管平台,依托國內的程序員較多基於平台中發布項目。目前是獨立平台運營的。
阿里雲Code
https://code.aliyun.com
Azure DevOps
https://dev.azure.com
https://docs.microsoft.com/en-us/azure/devops/?view=azure-devops
參考
- https://www.aneasystone.com/archives/2018/12/build-your-own-git-server.html
- https://gotgit.readthedocs.io/en/latest/03-git-harmony/010-protocol.html
- https://cloud.tencent.com/developer/news/319910
- https://segmentfault.com/a/1190000016959596
- https://cloud.tencent.com/developer/article/1347791
- https://www.cnblogs.com/wyl090512/p/9887795.html
- https://git-scm.com/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E7%94%9F%E6%88%90-SSH-%E5%85%AC%E9%92%A5