一 、版本控制系統(tǒng)
為啥要用Git呀胚泌,用來做什么拔猎颉?
??當我們學了基礎的知識后膜钓,總希望自己能做出一些東西來(比如說游戲嗽交、實用的產品啊)颂斜,可又不知道從哪里下手夫壁,同時還有這樣的困惑:我做的有實際價值嗎,別人是否做過了沃疮。這時候就需要了解下Github了盒让,作為世界上最大的項目開源社區(qū),各路牛人分享了自己做的項目和源碼司蔬。我們可以欣賞他們優(yōu)秀的作品邑茄,也可以自己去實現(xiàn)它。這時候就需要一個工具了—— Git 俊啼,Git是分布式的版本控制系統(tǒng)肺缕,我們可以通過Git查看這些優(yōu)秀的是如何一步步實現(xiàn)的。下面將系統(tǒng)的介紹下Git的相關知識授帕。
1. 版本控制系統(tǒng)
??給項目命名時同木,我們通常會根據(jù)版本號或時間來記錄不同的版本。比如像這樣:
??文件進行多次的修改后跛十,我們想要找回某一次的修改記錄泉手,查找起來會很麻煩。隨著不同的版本越來越多偶器,管理起來會很復雜斩萌。如果是多人合作開發(fā)的項目缝裤,修改合并非常容易出現(xiàn)沖突。這時候就需要版本控制系統(tǒng)(Version Control System颊郎,VCS)幫助我們管理文件和項目了憋飞。
??版本控制系統(tǒng)是記錄文件內容變化,查閱特定版本修訂情況的系統(tǒng)姆吭。有了他可以跟蹤文件的改動榛做,回到歷史記錄的某個時刻。
2 版本控制系統(tǒng)管理文件有哪些?
?? 所有的版本控制系統(tǒng)(包括Git内狸,SVN等)检眯,只能跟蹤文本信息的改動,比如文本文件 (.txt) 昆淡、腳本文件 (.py)和各種基于文本信息的文件等;例如對TXT文件做了改動锰瘸,版本控制系統(tǒng)可以告訴你是在文本第幾行加了一個單詞“Linux”,在第幾行刪了一個單詞“Windows”等昂灵。
??對于圖片避凝、視頻等二進制文件或是其他格式的文件,版本控制沒法跟蹤文件的具體的變化(沒法知道這些文件的數(shù)據(jù)修改的具體情況)眨补,只知道發(fā)生了修改管削。
Note: 建議文本信息使用UTF-8編碼(Notepad++的編碼設置為UTF-8 without BOM)
3 版本控制系統(tǒng)分類
版本控制系統(tǒng)可分為:集中式和分布式版本控制系統(tǒng)
??集中式版本控制系統(tǒng)(SVN、CVS等):版本庫集中存放在一個的服務器撑螺,文件的所有修訂含思,開發(fā)人員合作都要通過這臺服務器修改。優(yōu)缺點:管理方便甘晤、邏輯明確茸俭;必須聯(lián)網(wǎng)才能工作.示意圖如下:
??分布式版本控制系統(tǒng)(Git):沒有中央服務器,每個人都可以把倉庫clone到下來(倉庫可理解為所有的項目文件)安皱。然后在自己的電腦上對倉庫做修改调鬓,如果要和別人合作,可以把項目pull給別人酌伊。優(yōu)缺點:適合分布式開發(fā)腾窝,可以離線工作。
4 Git與GitHub居砖、GitLab的區(qū)別虹脯?
??Git 是一個分布式版本控制系統(tǒng)。我們通過Git 在本地創(chuàng)建倉庫奏候,記錄不同文件的版本信息循集。GitHub是Git 倉庫的托管平臺,把自己的Git倉庫存放到GitHub的網(wǎng)絡平臺上(note:因為GitHub只支持Git 作為唯一的版本庫格式蔗草,所以叫GitHub)
?GitHub咒彤、GitLab都是 Git 倉庫托管平臺疆柔,但GitHub創(chuàng)建私有的代碼倉庫需要付費,GitLab允許免費設置私有倉庫(國內還有 碼云 等Git平臺镶柱,下載的網(wǎng)速要快很多)旷档。
二 、Git安裝
1 安裝
??安裝 git 的方式在每種系統(tǒng)中各不相同歇拆,最簡單的是直接去 git 官網(wǎng)下載, 讓網(wǎng)站自動識別你的系統(tǒng), 提供下載文件鞋屈,關于教程官方也給出了文檔,鏈接如下:
?? Git官網(wǎng)下載 ?? Git 官方安裝文檔
??以Windows版本舉個栗子故觅,Git 也為 Windows 系統(tǒng)提供了 .exe
安裝包, 直接從這里下載安裝就可以了: https://git-scm.com/download/win 厂庇;推薦使用默認安裝參數(shù), 然后一路下一步
到底. 安裝好之后, 在桌面將會出現(xiàn) Git Bash
的程序;打開如下圖所示:(Bash窗口是 git 在 Windows 上設置的一個Unix 的環(huán)境.)
2 Git 介紹
Git 項目有三個工作區(qū)域:工作目錄输吏、暫存區(qū)域权旷、倉庫
??工作目錄就是我們修改或寫入文件的地方。 暫存區(qū)域是一個文件评也,保存了下次將提交的文件列表信息炼杖,類似于`‘索引’的作用灭返。 倉庫(Repository)是 Git 用來保存項目的元數(shù)據(jù)和對象數(shù)據(jù)庫的地方盗迟。位于.git文件目錄下(默認下看不到),其它計算機克隆倉庫時熙含,拷貝的就是這里的數(shù)據(jù)罚缕。
Git 的工作流程:1.在工作目錄中修改文件; 2.暫存文件怎静,將文件的快照放入暫存區(qū)域邮弹;3.提交更新,從暫存區(qū)域將快照存儲到 Git 倉庫目錄蚓聘。(快照是Git識別文件的)腌乡。
Git的文件的4種狀態(tài)(status),不同狀態(tài)之間可通過命令轉換:
工作目錄中新建了文件 1.txt
夜牡,還沒被Git識別時与纽; status:untracked
???????????? bash: git add
文件 1.txt
通過 add 命令,將文件進入了暫存區(qū)域 status: Staged
?????? ?????? bash: git commit
文件1.txt
被提交commit到了倉庫里 status: Unmodified
文件提交到倉庫后塘装,任務可以說完成了急迂。如果后面我們對 1.txt
進行了修改 ,狀態(tài)會改變 status: Modified
這是改變的文件需要重新 add 蹦肴,commit到 倉庫僚碎;
整個上述過程可以被這張 git 官網(wǎng)上的流程圖直觀地表現(xiàn):
三 、Git的初始化和配置
1 初始化創(chuàng)建版本庫
??版本庫又名倉庫(repository)阴幌。里面的所有文件的修改勺阐、刪除操作都被記錄下來卷中,任何時刻的更改都可以被還原。我們先確定對哪個文件夾里的文件進行管理. 比如桌面上的一個叫 github_md
的文件夾. 在文件目錄的路徑下運行 Terminal 的命令
$ mkdir C:Users/Qi/Desktop/github_md
$ cd C:Users/Qi/Desktop/github_md
??為了記錄對文件修改的人皆看,需要配置下用戶名 user.name
和用戶的郵箱 user.email
:
$ git config --global user.name "Liu Qi"
$ git config --global user.email "liuqi.steven@qq.com"
??如果使用了--global
選項(只需要運行一次)之后無論你在做任何事情都會使用這些信息仓坞。 設置不同的用戶名稱與郵件地址以運行沒有--global
選項的命令。
??git初始化:該命令將創(chuàng)建一個名為 .git 的子目錄腰吟, 用來管理不同的版本文件(.git
是被隱藏起來的无埃,可執(zhí)行 $ ls -a
查看)
$ git init
Initialized empty Git repository in C:/Users/Qi/Desktop/github_md/.git/
2 添加文件管理 (add)
??建立一個新的 1.py
文件(如果使用命令是$ touch 1.py
):使用 git status
檢查當前文件狀態(tài) :
$ git status
# 輸出
On branch master # 在 master 分支
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
  1.py # 1.py 文件沒有被加入版本庫 (unstaged)
nothing added to commit but untracked files present (use "git add" to track)
??現(xiàn)在 1.py
并沒有被放入版本庫中 (unstaged), 所以我們要使用 add
把它添加進版本庫 (staged):
$ git add 1.py
$ git status # 再次查看狀態(tài) status
# 輸出
On branch master
Initial commit
Changes to be committed: #說明是已暫存狀態(tài)
(use "git rm --cached <file>..." to unstage)
new file: 1.py # 版本庫已識別 1.py (staged)
??如果想一次性添加文件夾中所有未被添加的文件, 可以使用$ git add .
3 提交改變 (commit)
??最后一步就是提交這次的改變, 并在 -m
寫修改的信息:
$ git commit -m "create 1.py"
# 輸出
[master (root-commit) 6bd231e] create 1.py
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 1.py
??現(xiàn)在提交倉庫完整的流程就運行完了,我們可以通過上述方法把自己的項目上傳到倉庫毛雇。
四 嫉称、克隆倉庫和版本管理
1 克隆現(xiàn)有的倉庫
??如果想獲得一份已經(jīng)存在了的 Git 倉庫,也就是把別人的項目下載下來灵疮,使用 git clone [url]
$ git clone https://github.com/jwasham/coding-interview-university
??這會在目錄下創(chuàng)建一個名為 “coding-interview-university” 的目錄织阅,并在目錄下初始化一個 .git 文件夾,從遠程倉庫拉取下所有數(shù)據(jù)放入 .git 文件夾震捣。 所有的項目文件在 coding-interview-university 文件夾里面荔棉。我們可以查看別人是怎么構建項目的。想改下載項目的名字的話
git clone [url] changedName
(note: 忽略文件,有些文件無需納入 Git 的管理蒿赢。比如日志文件润樱,或者編譯過程中創(chuàng)建的臨時文件等。 我們可以創(chuàng)建一個名為 .gitignore的文件羡棵,列出要忽略的文件模式)
2 倉庫的不同版本
?? 項目clone下來后壹若,想查看這個項目是如何修改的,可使用 git log --oneline
查看皂冰, (--graph
可查看當前項目的分支)
$ git log --oneline --graph
# 輸出
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
其中904e1ba
這些就是版本號店展,我們可以通過版本號回到以前的版本。命令git reset --hard 版本號
?? ?Note:在Git中每個版本( commit
)都有自己的 id
數(shù)字秃流,指針Head
指向哪個版本赂蕴,我們就回到了哪個版本。
$ git log --oneline
904e1ba change 2
c6762a1 change 1
13be9a7 create 1.py
---------------------------------------------------------------------
$ git reset --hard c6762a1
$ git reset --hard HEAD^
#^表示回到上一個舶胀,^^表示上上一個概说,依次類推;可簡寫成 ~2(數(shù)字表示前幾個)
?? 回到之前的版本后峻贮,又想回到之后的版本怎么辦席怪? git reflog 查看最近做的所有 HEAD 的改動
$ git reflog
# 輸出
c6762a1 HEAD@{0}: reset: moving to c6762a1
904e1ba HEAD@{1}: commit (amend): change 2
0107760 HEAD@{2}: commit: change 2
c6762a1 HEAD@{3}: commit: change 1
13be9a7 HEAD@{4}: commit (initial): create 1.py
$ git reset --hard 904e1ba
命令 git log
可以回顧歷史記錄,不用參數(shù)的話會按提交時間列出所有的更新纤控,包括作者的名字和電子郵件地址挂捻、提交時間以及提交說明等等。 查看簡略的統(tǒng)計信息船万,可以使用 --oneline刻撒, --stat 選項 骨田;選項 -p 可用來顯示每次提交的內容差異。
五声怔、Git版本管理的其他命令
1 管理不同分支
建立分支 dev
, 并查看所有分支git branch
:
$ git branch dev # 建立 dev 分支
$ git branch # 查看當前分支
dev
* master # * 代表了當前的 HEAD 所在的分支
創(chuàng)建和切換到新建的分支,也可以使用`checkout -b
$ git checkout -b dev
Switched to a new branch 'dev'
$ git checkout dev
$ git branch
# 輸出
* dev # 這時 HEAD 已經(jīng)被切換至 dev 分支
master
刪除dev 分支
$ git branch -d dev
2 將 dev 的修改推送到 master
dev
分支上對文件夾中的文件進行修改,不會影響到 master
分支,比如在dev
分支上我們在 1.py
上加入這一行 #changed in dev branch
, 然后再 commit
:
$ git commit -am "change 3 in dev" # "-am": add 所有改變 并直接 commit
首先我們要切換到 master
, 再將 dev
推送過來.
$ git checkout master # 切換至 master 才能把其他分支合并過來
$ git merge dev # 將 dev merge 到 master 中
$ git log --oneline --graph
# 輸出
* f9584f8 change 3 in dev
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
? 如果直接 git merge dev
, git 會采用默認的 Fast forward
格式進行 merge
, 這樣 merge
的這次操作不會有 commit
信息. log
中也不會有分支的圖案. 我們可以采取 --no-ff
這種方式保留 merge
的 commit
信息,如下所示:
$ git merge --no-ff -m "keep merge info" dev # 保留 merge 信息
$ git log --oneline --graph
# 輸出
* c60668f keep merge info
|\
| * f9584f8 change 3 in dev # 這里就能看出, 我們建立過一個分支
|/
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
3 Git撤銷修改
- 查看具體的修改內容
1.1 查看 unstaged
?? 如果想要查看這次還沒add
(unstaged) 的修改部分和上個已經(jīng)commit
的文件有何不同, 我們將使用
$ git diff
:
1.2 查看 staged (--cached)
?? 如果你已經(jīng)add
了這次修改, 文件變成了 “可提交狀態(tài)” (staged), 我們可以在diff
中添加參數(shù)--cached
來查看修改:
1.3 同時查看 staged & unstaged (HEAD)
?? 還有種方法讓我們可以查看 add
過 (staged) 和 沒 add
(unstaged) 的修改,$ git diff HEAD
2.1 修改已 commit 的版本
?? 有時候我們 commit
后卻發(fā)現(xiàn)漏掉了幾個文件沒有commit
态贤。 比如想 commit
1.pyt和2.py
,結果這樣寫的
$ git add 1.py
$ git commit -m "add two files"
[master 20dff6a] add two files
1 file changed, 1 insertion(+), 1 deletion(-)
但是可以運行帶有 --amend
選項的提交命令重新提交:
$ git add 2.py
$ git commit --amend --no-edit # "--no-edit": 不編輯, 直接合并到上一個commit醋火,可不加
$ git log --oneline # "--oneline": 每個 commit 內容顯示在一行
# 01a6a14 (HEAD -> master) add two files # 合并過的 add two files
2.2 reset 回到 add 之前
??有時我們添加 add
了修改, 但是又后悔, 并想補充一些內容再 add
. 這時, 我們有一種方式可以回到 add
之前. 比如在 1.py
文件中添加這一行:
# 修改 1.py 成為 modified狀態(tài), add 到staged
$ git add 1.py
$ git status -s
M 1.py # staged
---------------------------------------------------------------------
# 回到 modified狀態(tài)重新編輯
$ git reset 1.py
Unstaged changes after reset:
M 1.py
---------------------------------------------------------------------
$ git status -s
# 輸出
M 1.py # unstaged
3 單個文件checkout
??之前我們使用 reset
的時候是針對整個版本庫, 回到版本庫的某個過去. 不過如果我們只想回到某個文件的過去, 又該怎么辦呢?
$ git log --oneline
# 輸出
904e1ba change 2
c6762a1 change 1
13be9a7 create 1.py
# 讓 1.py 回到c6762a1版本下的 1.py悠汽,這時 1.py 文件的內容就變成以前的呢
$ git checkout c6762a1 -- 1.py
??如果直接 git merge dev
, git 會采用默認的 Fast forward
格式進行merge
, 這樣 merge
的這次操作不會有 commit
信息. log
中也不會有分支的圖案. 我們可以采取 --no-ff
這種方式保留 merge
的 commit
信息,如下所示:
$ git merge --no-ff -m "keep merge info" dev # 保留 merge 信息
$ git log --oneline --graph
# 輸出
* c60668f keep merge info
|\
| * f9584f8 change 3 in dev # 這里就能看出, 我們建立過一個分支
|/
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
1.5 merge 分支沖突
?當創(chuàng)建了一個分支后, 我們同時對兩個分支都進行了修改.比如在:
-
master
中的1.py
加上# edited in master
. -
dev
中的1.py
加上# edited in dev
.
??當我們再merge dev
的時候, 沖突就來了. 因為 git 不知道應該怎么處理merge
時, 在master
和dev
的不同修改.
??具體的沖突, git 已經(jīng)幫我們標記出來, 我們打開1.py
就能看到,這時需要我們自己手動修改文件芥驳,然后再commit
現(xiàn)在的文件, 沖突就解決啦.
$ git commit -am "solve conflict"
2.3 遠程倉庫的使用
?? 遠程倉庫是指托管在因特網(wǎng)或其他網(wǎng)絡的你的項目的版本庫柿冲。 你可以有好幾個遠程倉庫,通常有些倉庫對你只讀兆旬,有些則可以讀寫假抄。為了能在任意 Git 項目上協(xié)作,你需要知道如何管理自己的遠程倉庫丽猬。
2 查看遠程倉庫
?? git remote 命令宿饱。 它會列出你指定的每一個遠程服務器的簡寫。 如果你已經(jīng)克隆了自己的倉庫脚祟,那么至少應該能看到 origin - 這是 Git 給你克隆的倉庫服務器的默認名字:
$ git clone https://github.com/schacon/ticgit
$ cd ticgit
$ git remote
# origin
?? 如果你的遠程倉庫不止一個谬以,該命令會將它們全部列出。 例如愚铡,與幾個協(xié)作者合作的蛉签,擁有多個遠程倉庫的倉
庫看起來像下面這樣:
$ cd grit
$ git remote -v
bakkdoor https://github.com/bakkdoor/grit (fetch)
bakkdoor https://github.com/bakkdoor/grit (push)
origin git@github.com:mojombo/grit.git (push)
...
2.1 臨時修改 (stash)
?? 想想有天在開開心心地改進代碼, 突然接到老板的一個電話說要改之前的一個程序. 怎么辦? 雖然還需要很久時間才能改進完自己的代碼, 可我有強迫癥, 又不想把要改的程序和自己改進代碼的部分一起 commit
了.
?? 這時 stash
就是我的救星了. 用 stash
能先將我的那改進的部分放在一邊分隔開來. 再另外單獨處理老板的任務.
?? 假設我們現(xiàn)在在 dev
分支上快樂地改代碼:
$ git checkout dev
?? 在 dev
中的 1.py
中加上一行 # feel happy
, 然后老板的電話來了, 可是我還沒有改進完這些代碼. 所以我就用 stash
將這些改變暫時放一邊.
$ git status -s
# 輸出
M 1.py
------------------
$ git stash
# 輸出
Saved working directory and index state WIP on dev: f7d2e3a change 3 in dev
HEAD is now at f7d2e3a change 3 in dev
-------------------
$ git status
# 輸出
On branch dev
nothing to commit, working directory clean # 干凈得很
2.2 做其它任務
然后我們建立另一個 branch
用來完成老板的任務:
$ git checkout -b boss
# 輸出
Switched to a new branch 'boss' # 創(chuàng)建并切換到 boss
?? 然后苦逼地完成著老板的任務, 比如添加 # lovely boss
去 1.py
. 然后 commit
, 完成老板的任務.
$ git commit -am "job from boss"
$ git checkout master
$ git merge --no-ff -m "merged boss job" boss
2.3 恢復暫存
輕松了, 現(xiàn)在可以繼續(xù)開心的在 dev
上刷代碼了.
$ git checkout dev
$ git stash list # 查看在 stash 中的緩存
# 輸出
stash@{0}: WIP on dev: f7d2e3a change 3 in dev
?? 上面說明在 dev
中, 我們的確有 stash
的工作. 現(xiàn)在可以通過 pop
來提取這個并繼續(xù)工作了.
$ git stash pop
# 輸出
On branch dev
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: 1.py
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (23332b7edc105a579b09b127336240a45756a91c)
----------------------
$ git status -s
# 輸出
M 1.py # 和最開始一樣了
2.4 添加遠程倉庫
?? 運行 git remote add <shortname> <url> 添加一個新的遠程 Git 倉庫胡陪,同時指定一個你可以輕松引用的簡寫:
$ git remote
origin
$ git remote add pb https://github.com/paulboone/ticgit
$ git remote -v
pb https://github.com/paulboone/ticgit (fetch)
pb https://github.com/paulboone/ticgit (push)
?? 現(xiàn)在你可以在命令行中使用字符串 pb 來代替整個 URL沥寥。 例如,如果你想拉取 Paul 的倉庫中有但你沒有的信息柠座,可以運行 git fetch pb:
$ git fetch pb
remote: Counting objects: 43, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 43 (delta 10), reused 31 (delta 5)
Unpacking objects: 100% (43/43), done.
From https://github.com/paulboone/ticgit
[new branch] master -> pb/master
[new branch] ticgit -> pb/ticgit
從遠程倉庫中抓取與拉取
?? 就如剛才所見邑雅,從遠程倉庫中獲得數(shù)據(jù),可以執(zhí)行:
$ git fetch [remote-name]
?? 這個命令會訪問遠程倉庫妈经,從中拉取所有你還沒有的數(shù)據(jù)淮野。 執(zhí)行完成后,你將會擁有那個遠程倉庫中所有分支的引用吹泡,可以隨時合并或查看骤星。
?? 如果你使用 clone 命令克隆了一個倉庫,命令會自動將其添加為遠程倉庫并默認以 “origin” 為簡寫爆哑。 所以洞难,git fetch origin 會抓取克隆(或上一次抓冉页)后新推送的所有工作队贱。 必須注意 git fetch 命令會將數(shù)據(jù)拉取到你的本地倉庫 - 它并不會自動合并或修改你當前的工作色冀。 當準備好時你必須手動將其合并入你的工作。
推送到遠程倉庫
?? 當你想分享你的項目時柱嫌,必須將其推送到上游锋恬。 這個命令很簡單:git push [remote-name] [branch?name]。 當你想要將 master 分支推送到 origin 服務器時(再次說明编丘,克隆時通常會自動幫你設置好那兩個名字)与学,那么運行這個命令就可以將你所做的備份到服務器
$ git push origin master
六、GitHub
??Github客戶端是Git的圖形界面窗口嘉抓,功能不如Git強大癣防,不過使用起來很方便。使用更多的是Github的網(wǎng)站掌眠,先在 github 注冊一個 github 賬戶, 然后添加你的一個 online 版本庫 repository: Github客戶端下載地址
連接本地版本庫
??使用這節(jié)內容的初始例子文件, 然后將本地的版本庫推送到網(wǎng)上:
$ git remote add origin https://github.com/lukkyy/git-demo.git
$ git push -u origin master # 推送本地 master 去 origin
$ git remote rm origin # 刪除 origin蕾盯,添加你自己的倉庫
$ git remote add origin " "
?? git push時會彈出窗口,讓用戶輸入賬號密碼
推送修改
??如果在本地再進行修改, 比如在 1.py
文件中加上 # happy github
, 然后 commit
并推上去:
$ git commit -am "change 5"
$ git push -u origin master