其實使用git已經(jīng)有兩年多時間了,但是對Git的概念一直懵懵懂懂,平時常用的命令就那么幾個,而且大部分的時候都是直接使用GitHub DeskTop客戶端操作了。一直想要從頭到尾梳理一下Git,網(wǎng)上廖雪峰的Git教程是很不錯的,樓主就參照他的教程按照自己的梳理及操作,再來一遍。就啰嗦這么多,直接開始吧。
1 Git簡介
Git是目前世界上最先進的分布式版本控制系統(tǒng),說到這里我們必須比較一下分布式和集中式版本控制系統(tǒng)的區(qū)別。
- 集中式版本控制系統(tǒng),版本庫是集中存放在中央服務器的,而干活的時候,用的都是自己的電腦,所以要先從中央服務器取得最新的版本,然后開始干活,干完活了,再把自己的活推送給中央服務器。它最大的毛病集就是必須聯(lián)網(wǎng)才能工作。
- 分布式版本控制系統(tǒng)根本沒有“中央服務器”,每個人的電腦上都是一個完整的版本庫,這樣工作的時候,就不需要聯(lián)網(wǎng)了,因為版本庫就在你自己的電腦上。既然每個人電腦上都有一個完整的版本庫,那多個人如何協(xié)作呢?比方說你在自己電腦上改了文件A,你的同事也在他的電腦上改了文件A,這時,你們倆之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。實際使用中分布式版本控制系統(tǒng)通常也有一臺充當“中央服務器”的電腦,但這個服務器的作用僅僅是用來方便“交換”大家的修改,沒有它大家也一樣干活,只是交換修改不方便而已。
2 Git本地庫與遠程庫
2.1 創(chuàng)建本地版本庫
版本庫又名倉庫,英文名repository,你可以簡單理解成一個目錄,這個目錄里面的所有文件都可以被Git管理起來,每個文件的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。
創(chuàng)建一個版本庫非常簡單,首先,選擇一個合適的地方,創(chuàng)建一個空目錄:
fh:Desktop fenghuoMac$ mkdir learngit
fh:Desktop fenghuoMac$ cd learngit/
fh:learngit fenghuoMac$ pwd
/Users/fenghuoMac/Desktop/learngit
第二步,通過git init命令把這個目錄變成Git可以管理的倉庫:
fh:learngit fenghuoMac$ git init
Initialized empty Git repository in /Users/fenghuoMac/Desktop/learngit/.git/
倉庫創(chuàng)建好后,當前目錄下會多一個.git目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄里面的文件,不然改亂了,就把Git倉庫給破壞了。這個目錄一般是隱藏的,使用ls -ah可以看到。
fh:learngit fenghuoMac$ ls -ah
. .. .git
2.2 創(chuàng)建遠程版本庫
現(xiàn)在已經(jīng)在本地創(chuàng)建了一個Git倉庫后,又想在GitHub創(chuàng)建一個Git倉庫,并且讓這兩個倉庫進行遠程同步。
登錄GitHub,New repository ->輸入Repository Name:learngit,其他的就按默認的來 ->create Repository
根據(jù)github的提示,可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫與之關聯(lián),然后,把本地倉庫的內(nèi)容推送到GitHub倉庫。
2.3 本地倉庫與遠程倉庫關聯(lián)
2.3.1 先創(chuàng)建本地倉庫,再關聯(lián)原地倉庫
樓主一般都是先在本地創(chuàng)建一個本地倉庫,參看2.1節(jié)創(chuàng)建本地git倉庫,然后在github創(chuàng)建一個遠程庫learngit,然后調(diào)用以下命令將本地庫與遠程倉庫關聯(lián)起來:
git remote add origin https://github.com/Lisajoy512/learn.git
git push -u origin master
第一次推送master分支時,加上了-u參數(shù),Git不但會把本地的master分支內(nèi)容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯(lián)起來,在以后的推送或者拉取時就可以簡化命令。
在此過程中,執(zhí)行git push -u origin master可能會出現(xiàn)以下錯誤,錯誤原因在于空目錄不可以提交,因此一般需要在本地倉庫提交一兩個文件才可以執(zhí)行成功。
error:src refspec master does not match any
2.3.2 先創(chuàng)建遠程庫,然后clone到本地庫
注意在github上創(chuàng)建時,勾選上initialize this repository with a README選項。
執(zhí)行clone命令,將遠程庫clone到本地
fh:Desktop fenghuoMac$ git clone https://github.com/Lisajoy512/learngit
Cloning into 'learngit'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
fh:Desktop fenghuoMac$ cd learngit/
fh:learngit fenghuoMac$ ls -au
. .. .git README.md
3 工作區(qū)與暫存區(qū)
3.1 工作區(qū) working directory
工作區(qū)就是你在電腦里能看到的目錄,learngit文件夾就是一個工作區(qū):
3.2 版本庫
工作區(qū)有一個隱藏目錄.git
,這個不算工作區(qū),而是Git的版本庫。
Git的版本庫里存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區(qū),還有Git為我們自動創(chuàng)建的第一個分支master
,以及指向master
的一個指針叫HEAD
。
我們把文件往Git版本庫里添加的時候,是分兩步執(zhí)行的:
第一步是用git add把文件添加進去,實際上就是把文件修改添加到暫存區(qū);
第二步是用git commit提交更改,實際上就是把暫存區(qū)的所有內(nèi)容提交到當前分支。
因為我們創(chuàng)建Git版本庫時,Git自動為我們創(chuàng)建了唯一一個master分支,所以,現(xiàn)在,git commit就是往master分支上提交更改。
你可以簡單理解為,需要提交的文件修改通通放到暫存區(qū),然后,一次性提交暫存區(qū)的所有修改。
4 文件提交
4.1 提交文件git add、git commit
fh:learngit fenghuoMac$ git add .
fh:learngit fenghuoMac$ git commit -m "first commit"
[master dc39153] first commit
1 file changed, 2 insertions(+), 1 deletion(-)
fh:learngit fenghuoMac$ git add .
fh:learngit fenghuoMac$ git commit -m "second commit"
[master cc8b902] second commit
1 file changed, 2 insertions(+), 1 deletion(-)
4.2查看倉庫狀態(tài) git status
我現(xiàn)在learngit目錄下增加一個test.txt的空文件,然后修改一下README.md。此時使用git status查看下倉庫的狀態(tài)。此時我們可以看到“Changes not staged for commit:modified:README.md”,“Untracked files:test.txt”
git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
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.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
no changes added to commit (use "git add" and/or "git commit -a")
現(xiàn)在我們使用“git add .”提交所有的文件,然后再用git status查看倉庫狀態(tài):
fh:learngit fenghuoMac$ git add .
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
new file: test.txt
現(xiàn)在,暫存區(qū)的狀態(tài)就變成這樣了:
所以,git add
命令實際上就是把要提交的所有修改放到暫存區(qū)(Stage),然后,執(zhí)行git commit
就可以一次性把暫存區(qū)的所有修改提交到分支。現(xiàn)在我們隊修改進行commit提交:
fh:learngit fenghuoMac$ git commit -m "third commit"
[master 7e59f25] third commit
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 test.txt
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
現(xiàn)在版本庫變成了這樣,暫存區(qū)就沒有任何內(nèi)容了:
接下來我們還要用實踐來證明一點,git commit
只是把暫存區(qū)的文件提交到分支。我們先對README.md進行修改,然后執(zhí)行git add
,然后修改test.txt文件,然后執(zhí)行git commit
,最后執(zhí)行git status
來查看倉庫狀態(tài)。我們會發(fā)現(xiàn)text.txt的修改并沒有被提交。
fh:learngit fenghuoMac$ git add .
fh:learngit fenghuoMac$ git commit -m "forth commit"
[master 977ad71] forth commit
1 file changed, 2 insertions(+), 1 deletion(-)
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
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: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
4.3 查看工作區(qū)與版本庫的區(qū)別 git diff
使用git diff
命令查看,可以看出test.txt確實沒有被提交到版本庫。
fh:learngit fenghuoMac$ git diff
diff --git a/test.txt b/test.txt
index e69de29..e3a9368 100644
--- a/test.txt
+++ b/test.txt
@@ -0,0 +1 @@
+#1
fh:learngit fenghuoMac$ git diff HEAD -- test.txt
diff --git a/test.txt b/test.txt
index e69de29..e3a9368 100644
--- a/test.txt
+++ b/test.txt
@@ -0,0 +1 @@
+#1
4.4 回顧提交歷史git log
git log命令顯示從最近到最遠的提交日志。如果嫌輸出信息過多,可以增加--pretty=oneline參數(shù),其中cc8b9024a4f3789aacfdbc423ac64078d607b1a1類似的長串就是我們提交的commit id了,與SVN以遞增的數(shù)字為commit id不同。
fh:learngit fenghuoMac$ git log
commit cc8b9024a4f3789aacfdbc423ac64078d607b1a1 (HEAD -> master)
Author: xxx
Date: Mon Jan 15 15:31:40 2018 +0800
second commit
commit dc39153d554fb880d37dc3a0eabb63aa0948b757
Author: xxx
Date: Mon Jan 15 15:31:04 2018 +0800
first commit
commit 43167cfad41603614ddc796032fa626aa44bbc44 (origin/master, origin/HEAD)
Author: xxx
Date: Mon Jan 15 15:20:41 2018 +0800
Initial commit
fh:learngit fenghuoMac$
fh:learngit fenghuoMac$ git log --pretty=oneline
cc8b9024a4f3789aacfdbc423ac64078d607b1a1 (HEAD -> master) second commit
dc39153d554fb880d37dc3a0eabb63aa0948b757 first commit
43167cfad41603614ddc796032fa626aa44bbc44 (origin/master, origin/HEAD) Initial commit
4.5 git reflog
那如果已經(jīng)關閉了命令行,無從知道“second commit”的commit id怎么辦。放心,Git提供了一個命令git reflog用來記錄你的每一次命令。可以從命令的歷史中得知head現(xiàn)在指向了哪個commit id。
fh:learngit fenghuoMac$ git reflog
cc8b902 (HEAD -> master) HEAD@{0}: reset: moving to cc8b9024a4f3789aacfdbc423ac64078d607b1a1
dc39153 HEAD@{1}: reset: moving to head^
cc8b902 (HEAD -> master) HEAD@{2}: commit: second commit
dc39153 HEAD@{3}: commit: first commit
43167cf (origin/master, origin/HEAD) HEAD@{4}: clone: from https://github.com/Lisajoy512/learngit
5 分支管理
5.1 分支管理常用命令
查看本地分支:$ git branch
查看遠程分支:$ git branch -r
創(chuàng)建本地分支:$ git branch [name] ----注意新分支創(chuàng)建后不會自動切換為當前分支
切換分支:$ git checkout [name]
創(chuàng)建新分支并立即切換到新分支:$ git checkout -b [name]
刪除分支:$ git branch -d [name] ---- -d選項只能刪除已經(jīng)參與了合并的分支,對于未有合并的分支是無法刪除的。如果想強制刪除一個分支,可以使用-D選項
合并分支:$ git merge [name] ----將名稱為[name]的分支與當前分支合并
創(chuàng)建遠程分支(本地分支push到遠程):$ git push origin [name]
刪除遠程分支:$ git push origin :heads/[name] 或 $ gitpush origin :[name]
檢出倉庫: $ git clone git://github.com/jquery/jquery.git
查看遠程倉庫:$ git remote -v
添加遠程倉庫:$ git remote add [name] [url]
刪除遠程倉庫:$ git remote rm [name]
修改遠程倉庫:$ git remote set-url --push [name] [newUrl]
拉取遠程倉庫:$ git pull [remoteName] [localBranchName]
推送遠程倉庫:$ git push [remoteName] [localBranchName]
如果想把本地的某個分支test提交到遠程倉庫,并作為遠程倉庫的master分支,或者作為另外一個名叫test的分支,如下:
$git push origin test:master // 提交本地test分支作為遠程的master分支
$git push origin test:test // 提交本地test分支作為遠程的test分支
5.2 分支策略
在分支管理策略里,我們常用到的有三種分支:master分支、dev分支、bug分支。
master分支:master主分支應該是非常穩(wěn)定的,也就是用來發(fā)布新版本,一般情況下不允許在上面干活。可以在要發(fā)布,或者說dev分支代碼穩(wěn)定后可以合并到主分支master上來。
dev分支:每個開發(fā)可各自擁有自己的dev分支,平時各自的開發(fā)工作都在dev分支上進行。
bug分支:為了修復bug臨時拉出來的分支。
現(xiàn)在我們著重講一下bug分支。假設現(xiàn)在有一個場景,在dev分支開發(fā)中時接到一個修復bug的任務。但是在dev分支上的任務開發(fā)到一半,目前還無法提交,此時可以采用保留現(xiàn)場功能。
比如我們修改一下README.md,然后git status
查看倉庫狀態(tài)會有一條modified提示,然后git stash
保留工作現(xiàn)場,最后使用git status
查看工作區(qū)就是clean的了。
fh:learngit fenghuoMac$ git status
On branch master
Your branch is up-to-date with 'origin/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.md
no changes added to commit (use "git add" and/or "git commit -a")
fh:learngit fenghuoMac$ git stash
Saved working directory and index state WIP on master: 4fbda2f sixth commit
fh:learngit fenghuoMac$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
修改完bug后將代碼合并到master分支后,我們現(xiàn)在要重新切回dev分支繼續(xù)干活,git stash list
查看工作現(xiàn)場,git stash apply
恢復現(xiàn)場。
fh:learngit fenghuoMac$ git checkout wye
Switched to branch 'wye'
fh:learngit fenghuoMac$ git stash list
stash@{0}: WIP on master: 4fbda2f sixth commit
fh:learngit fenghuoMac$
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
fh:learngit fenghuoMac$ git stash list
stash@{0}: WIP on master: 4fbda2f sixth commit
關于git stash不講太多,還有兩個相關的命令在這里也捎帶列一下:
git stash drop:git stash apply恢復,恢復后,stash內(nèi)容并不刪除,你需要使用git stash drop
命令來刪除。
git stash pop:恢復的同時把stash內(nèi)容也刪除了。
5.3 分支合并
一開始的時候,master
分支是一條線,Git用master
指向最新的提交,再用HEAD
指向master
,就能確定當前分支,以及當前分支的提交點:
每次提交,master
分支都會向前移動一步,這樣,隨著你不斷提交,master
分支的線也越來越長。
當我們創(chuàng)建新的分支,例如dev
時,Git新建了一個指針叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示當前分支在dev
上:
你看,Git創(chuàng)建一個分支很快,因為除了增加一個dev
指針,改改HEAD
的指向,工作區(qū)的文件都沒有任何變化!
不過,從現(xiàn)在開始,對工作區(qū)的修改和提交就是針對dev
分支了,比如新提交一次后,dev
指針往前移動一步,而master
指針不變:
5.3.1 git merge
5.3.1.1 Fast-forward模式
假如我們在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最簡單的方法,就是直接把master
指向dev
的當前提交,就完成了合并。
這種最簡單的合并方式我們稱為Fast forward模式,在命令行里可以看到git給我們的“Fast-forward”的提示:
fh:learngit fenghuoMac$ git merge wye
Updating 3ff16c8..75941ab
Fast-forward
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
5.3.1.2 --no-ff模式
使用Fast forward模式,刪除分支后,會丟掉分支信息。
如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支信息。
fh:learngit fenghuoMac$ git merge --no-ff wye
Merge made by the 'recursive' strategy.
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
不使用Fast forward
模式,merge后就像這樣:
5.3.2 git rebase
merge和rebase命令都是合并目標分支到當前分支的,但區(qū)別是rebase命令執(zhí)行后,實際上是將分支點從C移到了G,這樣分支也就具有了從C到G的功能
6 文件撤銷
6.1 撤銷工作區(qū)的修改git checkout -- file
我們現(xiàn)在編輯一下README.md,然后使用git status
查看下倉庫狀態(tài),然后git checkout -- README.md
撤銷修改,最后再使用git status
查看一下狀態(tài),會發(fā)現(xiàn)README.md已經(jīng)還原到修改之前了。
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
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.md
no changes added to commit (use "git add" and/or "git commit -a")
fh:learngit fenghuoMac$ git checkout -- README.md
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
現(xiàn)在我們調(diào)整下步驟,我們現(xiàn)在編輯一下README.md(現(xiàn)在文件的最后一行內(nèi)容是#5),然后git add
提交到暫存區(qū),然后使用git status
查看下倉庫狀態(tài),然后git checkout -- README.md
撤銷修改,最后再使用cat
查看一下文件內(nèi)容:
fh:learngit fenghuoMac$ git add .
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
fh:learngit fenghuoMac$ cat README.md
# learngit
#1
#2
#3
#4
#5
現(xiàn)在我們繼續(xù)編輯修改README.md(現(xiàn)在文件內(nèi)容最后一行是#6),然后git checkout -- README.md
撤銷修改,最后再使用cat
查看一下文件內(nèi)容:
fh:learngit fenghuoMac$ cat README.md
# learngit
#1
#2
#3
#4
#5
#6
fh:learngit fenghuoMac$ git checkout -- README.md
fh:learngit fenghuoMac$ cat README.md
# learngit
#1
#2
#3
#4
#5
命令git checkout -- README.md意思就是,把readme.txt文件在工作區(qū)的修改全部撤銷,這里有兩種情況:
- 一種是readme.txt自修改后還沒有被放到暫存區(qū),現(xiàn)在,撤銷修改就回到和版本庫一模一樣的狀態(tài);
- 一種是readme.txt已經(jīng)添加到暫存區(qū)后,又作了修改,現(xiàn)在,撤銷修改就回到添加到暫存區(qū)后的狀態(tài)。
總之,就是讓這個文件回到最近一次git commit或git add時的狀態(tài)。git checkout -- file命令中的--很重要,沒有--,就變成了切換分支的命令了。
6.2 撤銷暫存區(qū)的修改 git reset
現(xiàn)在我們修改README.md,且將其add到暫存區(qū),但是我們想撤銷README.md的修改返回到工作區(qū)怎么操作呢?在add之后我們可以使用git reset HEAD README.md
命令把暫存區(qū)的修改撤銷掉(unstage),重新放回工作區(qū)。這里的HEAD
表示的便是最新的版本,HEAD
你也可以自己替換為其他的commit ID表示要回退到哪一次版本。
fh:learngit fenghuoMac$ git add .
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 5 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
fh:learngit fenghuoMac$ git reset HEAD README.md
Unstaged changes after reset:
M README.md
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 5 commits.
(use "git push" to publish your local commits)
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.md
no changes added to commit (use "git add" and/or "git commit -a")
6.3 刪除文件 git rm
我們想要刪除test.txt
,可能我們會直接執(zhí)行rm test.txt
(此時表示只是在工作區(qū)刪除)命令,然后執(zhí)行git add test.txt
,最后執(zhí)行git commit -m "delete test.txt"
,才能真正將刪除提交到版本庫。
git提供了git rm
命令,執(zhí)行git rm test.txt
相當于先在工作區(qū)刪除然后提交到暫存區(qū)。最后執(zhí)行最后執(zhí)行git commit -m "delete test.txt"
將刪除真正提交到版本庫。
在研讀廖雪峰老師的使用git checkout -- test.txt
撤回刪除時我實踐時遇到了這樣的問題
fh:learngit fenghuoMac$ git rm test.txt
rm 'test.txt'
fh:learngit fenghuoMac$ git status
On branch master
Your branch is ahead of 'origin/master' by 6 commits.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: test.txt
fh:learngit fenghuoMac$ git checkout -- test.txt
error: pathspec 'test.txt' did not match any file(s) known to git.
注意這里的報錯原因是:工作區(qū)(已刪除test.txt)與add后的緩存區(qū)(git add test.txt 所以也刪除了test.txt)比較了文件,都是沒有test.txt文件,沒有差異,提示沒有匹配到文件(英文錯誤提示)。此時必須用git checkout HEAD test.txt
或者git reset
命令來恢復誤刪的文件了。
而如果單純使用rm命令刪除了test.txt文件,則可以使用git checkout -- test.txt
恢復。git checkout其實是用版本庫里的版本替換工作區(qū)的版本。
fh:learngit fenghuoMac$ rm test.txt
fh:learngit fenghuoMac$ git checkout -- test.txt
7 版本回退
7.1 git reset
根據(jù)6.2節(jié)我們知道git reset
命令可以暫存區(qū)的修改回退到工作區(qū),現(xiàn)在我們要講的是回退版本。
在Git中,用HEAD表示當前版本,也就是最新的提交cc8b9024a4f3789aacfdbc423ac64078d607b1a1,上一個版本就是HEAD^ ,上上一個版本就是HEAD^^ ,當然往上100個版本寫100個^比較容易數(shù)不過來,所以寫成HEAD~100。
回退到過去某個版本
比如我要回退到上一個版本:
fh:learngit fenghuoMac$ git reset --hard head^
HEAD is now at dc39153 first commit
fh:learngit fenghuoMac$ git log --pretty=oneline
dc39153d554fb880d37dc3a0eabb63aa0948b757 (HEAD -> master) first commit
43167cfad41603614ddc796032fa626aa44bbc44 (origin/master, origin/HEAD) Initial commit
回退到某個未來的版本
比如我們已回退到“first commit”的版本,但現(xiàn)在后悔了想回到“second commit”版本,只要命令行窗口沒關閉我們知道“second commit”的commit id(一般寫commit id頭幾位便可以,git會自動尋找)便可以回退:
fh:learngit fenghuoMac$ git reset --hard cc8b9024a4f3789aacfdbc423ac64078d607b1a1
HEAD is now at cc8b902 second commit
fh:learngit fenghuoMac$ git log --pretty=oneline
cc8b9024a4f3789aacfdbc423ac64078d607b1a1 (HEAD -> master) second commit
dc39153d554fb880d37dc3a0eabb63aa0948b757 first commit
43167cfad41603614ddc796032fa626aa44bbc44 (origin/master, origin/HEAD) Initial commit
git里面回退是非常高效的,因為Git在內(nèi)部有個指向當前版本的HEAD指針,當你回退版本的時候,Git僅僅是把HEAD指向你要回退的那個版本了。
reset參數(shù)詳解:
--soft – 緩存區(qū)和工作目錄都不會被改變
--mixed – 默認選項。緩存區(qū)和你指定的提交同步,但工作目錄不受影響
--hard – 緩存區(qū)和工作目錄都同步到你指定的提交
這些標記往往和HEAD作為參數(shù)一起使用。比如:
git reset --mixed HEAD 將你當前的改動從緩存區(qū)中移除,但是這些改動還留在工作目錄中。
另一方面,如果你想完全舍棄你沒有提交的改動,你可以使用git reset --hard HEAD。這是git reset最常用的兩種用法。
7.2 git revert
Revert撤銷一個提交的同時會創(chuàng)建一個新的提交。這是一個安全的方法,因為它不會重寫提交歷史。比如,下面的命令會找出倒數(shù)第二個提交,然后創(chuàng)建一個新的提交來撤銷這些更改,然后把這個提交加入項目中。
相比git reset,它不會改變現(xiàn)在的提交歷史。因此,git revert可以用在公共分支上,git reset應該用在私有分支上。
你也可以把git revert當作撤銷已經(jīng)提交的更改,而git reset HEAD用來撤銷沒有提交的更改。
然后我們可以通過git revert --abort來取消這次revert。
就像git checkout 一樣,git revert 也有可能會重寫文件。所以,Git會在你執(zhí)行revert之前要求你提交或者緩存你工作目錄中的更改。
8 標簽管理
git中的標簽相關的功能非常簡單,這里就不贅述了。常用命令有:
查看版本:$ git tag
創(chuàng)建版本:$ git tag [name]
刪除版本:$ git tag -d [name]
查看遠程版本:$ git tag -r
創(chuàng)建遠程版本(本地版本push到遠程):$ git push origin [name]
刪除遠程版本:$ git push origin :refs/tags/[name]
合并遠程倉庫的tag到本地:$ git pull origin --tags
上傳本地tag到遠程倉庫:$ git push origin --tags
創(chuàng)建帶注釋的tag:$ git tag -a [name] -m 'yourMessage'
列舉一個工作可能遇到的小意外,假如本應在上周五打tag時忘記了,現(xiàn)在已經(jīng)是下周一了,怎么辦?很簡單,找到上周五提交的commit id,然后在這次的commit id上打tag。
git tag v0.9 6224937
9 忽略文件
有些時候,你必須把某些文件放到Git工作目錄中,但又不能提交它們,比如保存了數(shù)據(jù)庫密碼的配置文件啦,等等,每次git status都會顯示Untracked files ...,有強迫癥的童鞋心里肯定不爽。
這個問題解決起來也很簡單,在Git工作區(qū)的根目錄下創(chuàng)建一個特殊的.gitignore文件,然后把要忽略的文件名填進去,Git就會自動忽略這些文件。
忽略文件這個比較簡單,就提示一下樓主遇到的坑,比如我在.gitignore里添加了README.md文件,然后修改README.md文件再add發(fā)現(xiàn)依然可以提交,這種情況是因為新建的文件在git中會有緩存,如果某些文件已經(jīng)被納入了版本管理中,就算是在.gitignore中已經(jīng)聲明了忽略路徑也是不起作用的,這時候我們就應該先把本地緩存刪除,然后再進行git的push,這樣就不會出現(xiàn)忽略的文件了。git清除本地緩存命令如下:
git rm -r --cached .
git add .
git commit -m 'update .gitignore'
10 參考文章:
廖雪峰的Git教程
Git簡介及常用命令
Git使用詳細教程
代碼回滾:git reset、git checkout和git revert區(qū)別和聯(lián)系
Git的奇技淫巧??
Learn Git Branching