廖雪峰-Git教程-學習筆記

chapter 1: 如何創建版本庫

  1. 初始化一個倉庫

$ git init

  1. 添加文件到Git倉庫的過程:

$ git add readme.txt

(沒有消息提示, Unix哲學: 沒有消息就是好消息)

$git commit -m 'write a readme file'

[master (root-commit) cb926e7] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt

chapter 2: 時光穿梭機

  1. 修改完文件后, 查看結果:

$ git status

$ git status

On branch master

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: readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

git status命令可以讓我們時刻掌握倉庫當前的狀態,上面的命令告訴我們,readme.txt被修改過了,但還沒有準備提交的修改

  1. "Changes not staged for commit": 表示修改沒有add到暫存區中等待提交, 也就是修改還只存在工作區

  2. 經過'$ git add readme.txt'后, 執行'$ git status', 輸出了如下日志:

$ git status

On branch master

Changes to be committed:

(use "git reset HEAD <file>..." to unstage)

modified: readme.txt

"Changes to be committed": 表示修改已經add到了staged(暫存區), 等待被commit到HEAD master.

  1. 執行完'$ git commit - m 'add something'', 再執行"$ git status", 輸出如下日志:

$ git status

On branch master

nothing to commit (working directory clean)

Git告訴我們當前沒有需要提交的修改,而且,工作目錄是干凈(working directory clean)的

  1. 查看修改了什么內容

$ git diff readme.txt

section 1: 版本回退

  1. 查看歷史記錄

$ git log

$ git log
commit 3628164fb26d48395383f8f31179f24e0882e1e0
Author: Michael Liao askxuefeng@gmail.com
Date: Tue Aug 20 15:11:49 2013 +0800

append GPL

commit ea34578d5496d7dd233c827ed32a8cd576c5ee85
Author: Michael Liao askxuefeng@gmail.com
Date: Tue Aug 20 14:53:12 2013 +0800

add distributed

commit cb926e7ea50ad11b8f9e909c05226233bf755030
Author: Michael Liao askxuefeng@gmail.com
Date: Mon Aug 19 17:51:55 2013 +0800

wrote a readme file

git log命令顯示從最新到最久的提交日志

如果想輸出信息更見簡潔:

$ git log --pretty=oneline

$ git log --pretty=oneline
3628164fb26d48395383f8f31179f24e0882e1e0 append GPL
ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed
cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file

3628164fb26d48395383f8f31179f24e0882e1e0 是commit id(版本號).
每提交一個新版本, 實際上Git就會把它們自動串成一條時間線

  1. 如何將文件回退到上一個版本

Git中使用HEAD表示當前版本, 也就是最新提交的commit id 所在的版本. 上一個版本就是"HEAD^", 上上個版本就是"HEAD^^".
上100個版本就是"HEAD~100"

$ git reset --hard HEAD^

$ git reset --hard HEAD^
HEAD is now at ea34578 add distributed

此時, 你如果想回到最新的commit去. 只要找到那個版本的commit id(一般取前7位):

$ git reset --hard 3628164

就可以指定回到未來的某個版本!

Git的版本回退速度非常快,因為Git在內部有個指向當前版本的HEAD指針,當你回退版本的時候,Git僅僅是把HEAD從指向“append GPL”:

git-head

改為指向“add distributed”:

git-head-move

然后順便把工作區的文件更新了。所以你讓HEAD指向哪個版本號,你就把當前版本定位在哪。

如果, 你在當前的命令行中找不到最新版本的commit id. 可以通過如下命令:

$ git reflog

$ git reflog
ea34578 HEAD@{0}: reset: moving to HEAD^
3628164 HEAD@{1}: commit: append GPL
ea34578 HEAD@{2}: commit: add distributed
cb926e7 HEAD@{3}: commit (initial): wrote a readme file

3628164就是你要找的那個版本commit id.

section 2: 工作區和暫存區

工作區: working directory

版本庫: Reposity, 工作區中的那個'.git'目錄, 版本庫中存了許多東西, 最重要的就是被稱為'stage(or index)'的暫存區, 還有Git為我們自動創建的第一個分支master, 以及指向master的一個指針叫HEAD

git-repo

暫存區: stage(或index), 作為版本庫的最重要的部分存在與'.git'目錄中.

把文件往Git版本庫里添加的時候,是分兩步執行的:

第一步是用“git add”把文件添加進去,實際上就是把文件修改添加到暫存區;

第二步是用“git commit”提交更改,實際上就是把暫存區的所有內容提交到當前分支;

因為我們創建Git版本庫時,Git自動為我們創建了唯一一個master分支,所以,現在,commit就是往master分支上提交更改

從來沒有add過的文件, 使用'git status'查看, 顯示的是'Untracked files'

執行了git add命令后, 暫存區的狀態如圖:

git-stage

所以,git add命令實際上就是把要提交的所有修改放到暫存區(Stage),然后,執行git commit就可以一次性把暫存區的所有修改提交到分支

執行了git commit命令后, 暫存區就沒有內容了, 修改的文件已經被commit到了master上:

現在版本庫變成了這樣,暫存區就沒有任何內容了:

git-stage-after-commit

section 3: 管理修改

為什么Git比其他版本控制系統設計得優秀,因為Git跟蹤并管理的是修改,而非文件。

你會問,什么是修改?比如你新增了一行,這就是一個修改,刪除了一行,也是一個修改,更改了某些字符,也是一個修改,刪了一些又加了一些,也是一個修改,甚至創建一個新文件,也算一個修改

如果你進行了如下操作:

第一次修改 -> git add -> 第二次修改 -> git commit

則只有第一次修改的內容被提交到了reposity中, 因為只有第一次修改被add到stage中

每次修改,如果不add到暫存區,那就不會加入到commit中

section 4: 撤銷修改

  1. 丟棄工作區的修改:

$ git checkout -- readme.txt
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作區的修改全部撤銷,這里有兩種情況:

一種是readme.txt自修改后還沒有被放到暫存區,現在,撤銷修改就回到和版本庫一模一樣的狀態;

一種是readme.txt已經添加到暫存區后,又作了修改,現在,撤銷修改就回到添加到暫存區后的狀態。

總之,就是讓這個文件回到最近一次git commit或git add時的狀態。

注意: '--'很重要, 如果沒有, 將變成了"創建一個新分支"的命令

  1. 撤銷掉暫存區的修改(unstage) , 重新放回到工作區

$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt

git reset命令既可以回退版本, 也可以把暫存區中的修改回退到工作區

還記得如何回退版本嗎?

section 5: 刪除文件

在本地刪除了文件"rm text.txt", 立刻可以通過"git status"看到變化

  1. 如果你要從版本庫中刪除文件

$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d17efd8] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt

  1. 如果你錯刪了文件, 把誤刪的文件恢復到最新版本:

$ git checkout -- test.txt
git checkout其實是用版本庫里的版本替換工作區的版本,無論工作區是修改還是刪除,都可以“一鍵還原”。

chapter 3: 遠程倉庫

  1. 配置github

第1步:創建SSH Key。在用戶主目錄下,看看有沒有.ssh目錄,如果有,再看看這個目錄下有沒有id_rsa和id_rsa.pub這兩個文件,如果已經有了,可直接跳到下一步。如果沒有,打開Shell(Windows下打開Git Bash),創建SSH Key:

$ ssh-keygen -t rsa -C "youremail@example.com"

如果一切順利的話,可以在用戶主目錄里找到.ssh目錄,里面有id_rsa和id_rsa.pub兩個文件,這兩個就是SSH Key的秘鑰對,id_rsa是私鑰,不能泄露出去,id_rsa.pub是公鑰,可以放心地告訴任何人.

第2步:登陸GitHub,打開“Account settings”,“SSH Keys”頁面:

然后,點“Add SSH Key”,填上任意Title,在Key文本框里粘貼id_rsa.pub文件的內容

chapter: 創建和合并分支

  1. git中默認有一個主分支, 叫做master. HEAD嚴格來說不是指向提交, 而是指向master分支, 而master才是指向提交的. 所以HEAD就是指向當前的分支.

  2. 每一次提交, master分支都會向前移動一步

  3. 當你創建新的分支如dev時, Git新建了一個指針叫dev, 指向master相同的提交, 再把HEAD指向dev, 就表示當前分支在dev上:

git-br-create

從現在開始, 對工作區的修改和提交都市針對dev分支了, 比如新提交一次后, dev分支就向前移動一步, 而master指針不變:

git-br-dev-fd

假如我們在dev中的工作完成了, 就可以把dev合并到master上.

創建分支, 并切換到分支:

$ git checkout -b 15_learn_git_flow -t master

git checkout命令加上-b參數表示創建并切換,相當于以下兩條命令:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'
然后,用git branch命令查看當前分支:

$ git branch

  • dev
    master
    git branch命令會列出所有分支,當前分支前面會標一個*號。

然后,我們就可以在dev分支上正常提交,比如對readme.txt做個修改,加上一行:

Creating a new branch is quick.
然后提交:

$ git add readme.txt
$ git commit -m "branch test"
[dev fec145a] branch test
1 file changed, 1 insertion(+)
現在,dev分支的工作完成,我們就可以切換回master分支:

$ git checkout master
Switched to branch 'master'
切換回master分支后,再查看一個readme.txt文件,剛才添加的內容不見了!因為那個提交是在dev分支上,而master分支此刻的提交點并沒有變:

git-br-on-master

現在,我們把dev分支的工作成果合并到master分支上:

$ git merge dev
Updating d17efd8..fec145a
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
git merge命令用于合并指定分支到當前分支。合并后,再查看readme.txt的內容,就可以看到,和dev分支的最新提交是完全一樣的。

注意到上面的Fast-forward信息,Git告訴我們,這次合并是“快進模式”,也就是直接把master指向dev的當前提交,所以合并速度非常快。

當然,也不是每次合并都能Fast-forward,我們后面會將其他方式的合并。

合并完成后,就可以放心地刪除dev分支了:

$ git branch -d dev
Deleted branch dev (was fec145a).
刪除后,查看branch,就只剩下master分支了:

$ git branch

  • master
    因為創建、合并和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合并后再刪掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全。

小結
Git鼓勵大量使用分支:

查看分支:git branch

創建分支:git branch name

切換分支:git checkout name

創建+切換分支:git checkout -b name

合并某分支到當前分支:git merge name

刪除分支:git branch -d name

問題: 如何創建遠程的分支?

  1. 先在本地創建分支

$ git checkout -b 18_learn_git_flow -t master

  1. 將本地的分支push到遠程

$ git push origin 18_learn_git_flow

section 4: Bug分支

修復bug時,我們會通過創建新的bug分支進行修復,然后合并,最后刪除;

當手頭工作沒有完成時,先把工作現場git stash一下,然后去修復bug,修復后,再git stash pop,回到工作現場。

查看全部的工作現場 :

$ git stash list

恢復工作現場:

方式一: 通過git stash apply  stash@{0}, 但恢復后, stash內容并不刪除, 需要使用git stash drop來刪除 

方式二: 通過 git stash pop, 恢復的同時把stash內容也刪掉. 

section 5: Feature分支

刪除分支:

$ git branch -d feature1 --- 刪除本地分支1, 如果該分支沒有被merge request, 則不能刪除

$ git branch -D feature1 --- 刪除本地分支, 不管該branch有沒有merge request

刪除遠程分支:

$ git push origin --delete 18_learn_git_flow

或者, 通過推送一個空的branch到遠程達到刪除的目的:

$ git branch -d branchName

$ git push origin :branchName

刪除不存在對應遠程分支的本地分支:

假設這樣一種情況:

1. 我創建了本地分支b1并pull到遠程分支 origin/b1;
2. 其他人在本地使用fetch或pull創建了本地的b1分支;
3. 我刪除了 origin/b1 遠程分支;
4. 其他人再次執行fetch或者pull并不會刪除這個他們本地的 b1分支,運行 git branch -a 也不能看出這個branch被刪除了,如何處理


使用$ git remote show origin 查看b1的狀態



如果顯示b1是stable, 使用 $ git remote prune origin 可以將其從本地版本庫中刪除 

或者, 更簡單的方式是, 使用 $ git fetch -p , 它在fetch之后刪除掉沒有與遠程分支對應的本地分支

重命名本地分支:

$ git branch -m oldName newName 

重命名遠程分支, 其實也就是先刪除遠程分支, 然后將重命名后的本地分支推送上去:

$ git push --delete origin oldName

$ git branch -m oldName newName

$ git push origin newName 

chapter: 多人協作

因此,多人協作的工作模式通常是這樣:

首先,可以試圖用git push origin branch-name推送自己的修改;

如果推送失敗,則因為遠程分支比你的本地更新,需要先用git pull試圖合并;

如果合并有沖突,則解決沖突,并在本地提交;

沒有沖突或者解決掉沖突后,再用git push origin branch-name推送就能成功!

如果git pull提示“no tracking information”,則說明本地分支和遠程分支的鏈接關系沒有創建,用命令git branch --track branch-name origin/branch-name。

這就是多人協作的工作模式,一旦熟悉了,就非常簡單。

小結
查看遠程庫信息,使用git remote -v;

本地新建的分支如果不推送到遠程,對其他人就是不可見的;

從本地推送分支,使用git push origin branch-name,如果推送失敗,先用git pull抓取遠程的新提交;

在本地創建和遠程分支對應的分支,使用git checkout -b branch-name origin/branch-name,本地和遠程分支的名稱最好一致;

建立本地分支和遠程分支的關聯,使用git branch --track branch-name origin/branch-name;

從遠程抓取分支,使用git pull,如果有沖突,要先處理沖突。

chapter: 標簽管理

發布一個版本時,我們通常先在版本庫中打一個標簽,這樣,就唯一確定了打標簽時刻的版本。將來無論什么時候,取某個標簽的版本,就是把那個打標簽的時刻的歷史版本取出來。所以,標簽也是版本庫的一個快照。

Git的標簽雖然是版本庫的快照,但其實它就是指向某個commit的指針(跟分支很像對不對?但是分支可以移動,標簽不能移動),所以,創建和刪除標簽都是瞬間完成的

命令git tag name用于新建一個標簽,默認為HEAD,也可以指定一個commit id;

-a tagname -m "blablabla..."可以指定標簽信息;

-s tagname -m "blablabla..."可以用PGP簽名標簽;

命令git tag可以查看所有標簽;

$ git checkout master

$ git tag v1.0

$ git tag v1.0 36564232

$ git tag -a v1.0 -m "version 1.0 released" 36564232

查看tag的詳情內容:

$ git show tagname

刪除標簽:

$ git tag -d v1.0

因為創建的標簽都只存儲在本地, 不會自動推送到遠程, 所以, 打錯了標簽可以在本地安全刪除.

如果要推送某個標簽到遠程, 使用命令:

$ git push origin tagname

或者, 一次性推送全部尚未推送到遠程的本地標簽:

$ git push origin --tags 

刪除遠程標簽(其實是推送一個空的tag到遠程tag達到刪除目的):

1) 先刪除本地的標簽: $ git tag -d v.0 

2) 然后從遠程刪除:   $ git push origin :refs/tags/v1.0

或者, 1.7以后可以這么做:

$ git push origin --delete tag v1.0

獲取遠程的tag:

$ git fetch origin tag <tagname>

小結
命令git push origin tagname可以推送一個本地標簽;

命令git push origin --tags可以推送全部未推送過的本地標簽;

命令git tag -d tagname可以刪除一個本地標簽;

命令git push origin :refs/tags/tagname可以刪除一個遠程標簽。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容