玩轉(zhuǎn)Git

其實使用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


image.png

根據(jù)github的提示,可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫與之關聯(lián),然后,把本地倉庫的內(nèi)容推送到GitHub倉庫。


image.png

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選項。


image.png

執(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ū):


image.png

3.2 版本庫

工作區(qū)有一個隱藏目錄.git,這個不算工作區(qū),而是Git的版本庫。

Git的版本庫里存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區(qū),還有Git為我們自動創(chuàng)建的第一個分支master,以及指向master的一個指針叫HEAD

git-repo

我們把文件往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-stage-after-commit

接下來我們還要用實踐來證明一點,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,就能確定當前分支,以及當前分支的提交點:

git-br-initial

每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長。
當我們創(chuàng)建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:

git-br-create

你看,Git創(chuàng)建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區(qū)的文件都沒有任何變化!

不過,從現(xiàn)在開始,對工作區(qū)的修改和提交就是針對dev分支了,比如新提交一次后,dev指針往前移動一步,而master指針不變:

git-br-dev-fd

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(-)
git-br-ff-merge

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后就像這樣:

git-no-ff-mode

5.3.2 git rebase

merge和rebase命令都是合并目標分支到當前分支的,但區(qū)別是rebase命令執(zhí)行后,實際上是將分支點從C移到了G,這樣分支也就具有了從C到G的功能

image

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

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