git 分散式版本管理
git 分散式版本管理工具學習筆記
宣告:本文作為自學廖雪峰老師的 Git 教程後,篩選摘抄出工作中常用的操作命令和技巧,補充了部分教程中沒有提到的知識點,編輯而成,僅供學習參考使用。完整教程,歡迎訪問廖雪峰老師的官方教程,教程地址:
Git 誕生
BitMover公司發現Linux社群有人試圖破解BitKeeper,BitMover公司將收回Linux社群的免費使用權。
Linus花了兩週時間自己用C寫了一個分散式版本控制系統,這就是Git!一個月之內,Linux系統的原始碼已經由Git管理了!牛是怎麼定義的呢?大家可以體會一下。
Git迅速成為最流行的分散式版本控制系統,尤其是2008年,GitHub網站上線了,它為開源專案免費提供Git儲存,無數開源專案開始遷移至GitHub,包括jQuery,PHP,Ruby等等。
歷史就是這麼偶然,如果不是當年BitMover公司威脅Linux社群,可能現在我們就沒有免費而超級好用的Git了。
配置
Git 的設定檔案為 .gitconfig ,它可以在使用者主目錄下(全域性配置),也可以在專案目錄下(專案配置)。
git config --list //顯示當前 Git 配置
git config -e [--global] //編輯 Git 配置,vim 編輯,中括號[--global]表示可加可不加,加了的話表示全域性
git config [--global] user.name "[name]" //設定提交程式碼的使用者資訊
git config [-- global] user.email "[email address]"
建立版本倉庫
- 版本庫又名倉庫,英文名 repository。選擇一個合適的地方,建立一個空目錄。
- 通過 git init 命令把這個目錄變成 Git 可以管理的倉庫(初始化倉庫)。
把檔案新增到版本庫
- 使用命令 git add 告訴git,把檔案 新增 到倉庫。
- 使用命令 git commit -m “XXX” 告訴git,把檔案 提交 到倉庫,-m 後面跟的是本次提交的註釋說明,註釋說明不要用中文。因為 commit 一次可以提交很多檔案,所以可以使用多次 add 不同的檔案。
常用commit提交註釋規範
- 用一空行分隔標題與正文。
- 標題使用大寫字母。
- 標題不超過50個字元。
- 標題使用祈使語氣。
- 標題不要使用句號結尾。
- 正文解釋是什麼和為什麼,而不是如何做。
常用關鍵字:
- Add 新增
- Mod 修改
- Del 刪除
- Fixed 修復
檢視倉庫的當前狀態
git status 命令可以檢視倉庫當前的狀態,隨時掌握工作區的狀態,誰被修改過了,還沒有提交等等。
修改檔案內容,未提交到暫存區,此時輸入 git status 後:
changes not staged for commit //改變 沒有 進行 提交
(上面的命令告訴我們,readme.txt 被修改過了,但是還沒有準備提交(暫存))
將修改後的檔案通過 git add 命令提交到暫存區後,此時輸入git status 後:
(上面的命令告訴我們,將要被提交的修改包括 readme.txt)
將檔案通過 git commit 命令提交到倉庫後 ,此時輸入git status後:
(上述命令告訴我們當前沒有需要提交的修改,而且工作目錄是乾淨的)
git diff 命令可以看到工作區和暫存區的區別;git diff head – readme.txt 命令可以檢視工作區和版本庫裡面的區別。
檢視提交的歷史記錄
git log 命令可以檢視所有提交的比較詳細的歷史紀錄。
如果不想要太詳細,可以使用 git log --pretty=oneline
時光穿梭機
6280d3……e547 是 commit id (版本號)。
在 git 中,用 HEAD 表示當前版本,上一個版本就是 HEAD^ ,上上個版本就是 HEAD^^ ,往前一百個版本為 HEAD~100。
回到上一個版本為 git reset --hard “HEAD^” (實測不區分大小寫)
回到上上個版本為 git reset --hard “head^^” (實測不區分大小寫)
假如知道版本號,回到某一個版本為 git reset --hard 126378(版本號,寫前幾位就行)
假如因為一些原因,關閉了命令列視窗,暫時無法知道某一次程式碼的版本號,可以使用命令 git reflog ,這個命令記錄了你之前操作的每一次命令,可以檢視版本號,知道了版本號就可以隨意回退版本了。
工作區和暫存區
Git 和其他版本控制系統如 SVN 的一個不同之處就是有暫存區的概念。
工作區(Working Directory):就是你電腦裡面能看到的目錄。
版本庫(Repository):工作區有一個隱藏目錄 .git ,這個不算工作區,而是 Git 的版本庫。Git 的版本庫裡存了很多東西,其中最重要的是稱為 stage(或者叫做 index)的暫存區,還有 Git為我們自動建立的第一個分支 master,以及指向 master 的一個指標叫做 HEAD。
把檔案往 Git 版本庫裡面新增的時候,是分兩步執行的:
- 用 git add 把檔案新增進去,實際上就是把檔案新增到暫存區;
- 用 git commit 提交更改,實際上就是把暫存區的所有內容提交到當前分支。
因為我們建立 Git 版本庫時候,Git 自動為我們建立了唯一一個 master 分支,所以,現在, git commit 就是往 master 分支上提交更改。
git commit 只負責把暫存區的修改提交到倉庫,不管工作區的內容。
撤銷修改
git checkout – file 可以丟棄工作區的修改:
git cheout -- readme.txt
命令 git checkout – readme.txt 意思就是,把 readme.txt 檔案在工作區的修改全部撤銷,這裡有兩種情況:
- 一種是 readme.txt 自修改後還 沒有被放到暫存區,現在,撤銷修改就回到和版本庫一模一樣的狀態。
- 一種是 readme.txt 已經 新增到暫存區後,又做了修改,現在,撤銷修改就回到新增到暫存區後的狀態。
git checkout – file 命令中的 – 很重要,沒有 – ,就變成了“切換到另一個分支”的命令,我們在後面的分支管理中會再次遇到 git checkout 命令。
假如修改的內容已經被 git add 到了暫存區,可以使用 git reset HEAD file 可以把暫存區的修改撤銷掉,重新放回工作區。
git reset 命令可以回退版本,也可以把暫存區的修改退回到工作區。當我們用 HEAD 時,表示最新的版本。
再用 git status 檢視一下,發現暫存區是乾淨的,工作區又修改。
然後再使用 git checkout – file ,撤銷掉工作區的操作。
假如修改的內容從暫存區 git commit 到了版本庫,回退的話可以參照時光穿梭機,會回退到上一版本,git reset --hard “HEAD^” 。(不過這是有條件的,就是此時本地的程式碼沒有被推送到遠端倉庫)
遠端倉庫
Git 是分散式版本管理系統,同一個 Git 倉庫,可以分佈到不同的機器上。怎麼分佈呢?最早,肯定只有一臺機器有一個原始版本庫,此後,別的機器可以克隆這個原始版本庫,而且每臺機器的版本庫其實都是一樣的,並沒有主次之分。
實際情況是這樣的,找一臺電腦充當伺服器的角色,每天24小時開機,其他每個人都從這個“伺服器”倉庫克隆一份到自己的電腦上,並且各自把各自的提交推送到伺服器倉庫裡,也從伺服器倉庫中拉取別人的提交。GitHub 就類似於“伺服器”的角色,託管程式碼。
檢視遠端倉庫
檢視遠端分支:git branch -a
注意:這條命令並沒有每一次都從遠端更新倉庫資訊,這樣子做是為了效率,我們可以手動更新一下遠端分支:
git fetch origin //更新遠端倉庫
git branch -a //檢視遠端分支
拉取遠端分支、並建立本地分支
方法一:git checkout -b 本地分支XXX origin/遠端分支XXX
git checkout -b gps origin/gps //將遠端的分支 gps 拉取到本地 gps 分支,並且切換到本地 gps 分支
使用該方式會在本地新建分支x,並自動切換到該本地分支x。
方法二:git fetch origin 遠端分支XXX:本地分支XXX
git fetch origin gps:gps //將遠端的分支 gps 拉取到本地 gps 分支,但是不會自動切換到本地的 gps 分支
使用該方式會在本地新建分支x,但是不會自動切換到該本地分支x,需要手動checkout。
新增遠端倉庫
遠端倉庫建立的方法略過,假定現在已經有了線上的遠端倉庫地址。
在本地的 learngit 倉庫下執行命令:
git remote add origin [email protected].com:albert-guo/learngit.git
上面的 albert-guo 替換成自己的 GitHub 帳戶名,否則你在本地關聯的就不一定是你的倉庫,關聯錯了也沒關係,但是以後是無法推上去的,因為你的 SSH Key 公鑰不在這個帳戶的列表中。
新增後,遠端倉庫的名字就是 origin ,這是 Git 預設的叫法,也可以改成別的,但是 origin 這個名字一看就知道是遠端倉庫。
把本地庫的所有內容推送到遠端倉庫上:
git push -u origin master
把本地庫的內容推送到遠端,用 git push 命令,實際上是把當前分支 master 推送到遠端。
由於遠端倉庫是空的,我們第一次推送的 master 分支時候,加上了 -u 引數,git 不但會把本地的 master 分支推送到遠端新的 master 分支,還會把本地的 master分支和遠端的 master 分支關聯起來,在以後的推送或者拉取的時候可以簡化命令。
從現在起,只要本地做了提交,就可以通過命令:
git push origin master
把本地 master 分支的最新修改推送到 GitHub,現在就擁有了真正的分散式版本庫。
分散式版本系統最大的好處之一就是在本地工作的時候不需要考慮遠端倉庫的存在,也就是沒有聯網也可以工作,而 svn 在沒有聯網的時候是拒絕幹活的,當有網路的時候再把本地提交推送一下就完成了同步,比較方便。
從遠端倉庫克隆
要克隆一個倉庫,首先必須知道倉庫地址,然後使用 git clone 命令克隆。
git clone [email protected].com:albert-guo/learngit.git
Git 支援多種協議,包括 https ,但是通過 ssh 支援的原生 git 協議最快。預設的 git:// 使用 ssh,但也可以使用 https 等其他協議。使用 https 除了速度慢以外,還有個最大的麻煩就是每次推送必須輸入口令,只是在某些只開放 http 埠的公司內部就無法使用 ssh 協議而只能用 https。
建立與合併分支
首先建立 dev 分支,然後切換到 dev 分支。
git checkout -b dev
git checkout 命令上加 -b 引數表示建立並且切換,相當於下面兩條命令:
- git branch dev 建立一個叫 dev 的分支。
- git checkout dev 切換到 dev 分支。
git branch dev //建立一個名為 dev 的分支
git checkout dev //切換到 dev 分支
檢視當前所在分支命令 git branch 。該命令會列出所有分支,並且在當前的分支前面加 * 。
在 dev 分支上提交幾次程式碼後,嘗試將 dev 分支和 master 分支合併。
git merge 命令用於合併指定分支到當前分支。比如當前在 master 分支上,然後通過命令 git merge dev 可將 dev 分支合併到當前的 master 分支。
git merge --abort 表示撤銷剛才的合併。
git merge dev //將 dev 分支合併到當前的 merge 分支上
git merge --abort //將剛才合併操作撤銷
注意上面的 Fast-forward 資訊,表示這次合併是“快進模式”,也就是直接把 master 指向 dev 的當前提交,所以合併速度非常快。當然也不是每一次合併都能 Fast-forward ,後續還會有其他方式合併。
合併之後,可以放心刪除 dev 分支。刪除命令 git branch -d dev 。
小結:
檢視分支:git branch
建立分支:git branch
切換分支:git checkout
建立+切換分支:git checkout -b
合併某分支到當前分支:git merge
刪除分支:git branch -d
強制刪除分支(沒有合併):git branch -D
解決衝突
假如兩個分支編輯的內容區塊相同,各分支都有了自己新的提交。這種情況下,git 無法執行“快速合併”,只能試圖把各自的修改合併起來,但是這種合併就會有衝突。
git 告訴我們,合併失敗,必須手動解決衝突後再提交。
此時,用命令 git diff 可以檢視衝突的地方。也可以直接在衝突的檔案裡,找到相關的衝突的位置。
git 會用 <<<<<<< , ======= , >>>>>>> 標記出不同分支的內容,然後在檔案中解決了衝突後儲存。
用帶引數的 git log --graph --pretty=oneline ,簡短顯示分支的合併情況。
git log --graph 命令可以看到詳細分支合併圖,git log --graph --pretty=oneline 命令可以簡單看到分支合併情況。
解決完衝突後,可將 dev 分支刪除,命令為 git branch -d dev。
git branch -d dev //刪除 dev 分支
禁用快速合併
通常情況下,合併分支時,如果可能,git 會用 Fast Forward 模式,但是這種模式下,刪除分支後,會丟掉分支資訊。
如果要強制禁用 Fast Forward 模式,git 就會在 merge 時生成一個新的 commit ,這樣,從分支歷史上就可以看出分支資訊。
比如我們從現在在 master 分支上,要把 dev 分支合併到當前的 master 分支。
可以用 git merge --no-ff -m "強制合併,禁用快速合併” dev。
因為本次合併要建立一個 commit ,所以加了一個 -m 引數,把 commit 描述進去。
gitignore
要養成在專案開始就建立 .gitignore 檔案的習慣,否則一旦 push,處理起來會非常麻煩。
有時候在專案開發過程中,突然心血來潮想把某些目錄或檔案加入忽略規則,按照上述方法定義後發現並未生效,原因是 .gitignore 只能忽略那些原來沒有被 track 的檔案,如果某些檔案已經被納入了版本管理中,則修改 .gitignore 是無效的。解決方法就是先把本地快取刪除(改變成未 track 狀態),然後再提交:
git rm -r --cached .
git add .
git commit -m 'update .gitignore'