git使用手冊

一、電腦本地初始化一個倉庫

1. git init: 初始化一個電腦上本地倉庫

終端進入項目目錄,輸入:

$ git init  

該命令將創建一個名為 .git 的子目錄,這個子目錄含有你初始化的 Git 倉庫中所有的必須文件,這些文件是 Git 倉庫的骨干。
詳細介紹:https://git-scm.com/book/zh/v2/Git-基礎-獲取-Git-倉庫

2. 創建遠程倉庫: 存放代碼

目前我知道的git網站倉庫:

碼云:
https://gitee.com/
公有倉庫、私有倉庫都免費使用, 國內訪問速度快。

github:
https://github.com/
公有倉庫免費使用, 私有倉庫收費, 有時候訪問速度慢。

Bitbucket:
https://bitbucket.org/
公有倉庫、私有倉庫都免費使用, 但是免費的最多只能有5個用戶對倉庫進行讀寫,超過的就需要付費。
訪問速度有時很慢。

gitlab:
公司自建的git服務器,隨意使用。

創建倉庫都差不多,在網站中點擊新建倉庫,然后選擇倉庫的類型(公有、私有),然后點擊創建即可。

3. 本地Git添加遠程倉庫地址

git remote add <shortname> <url>: 添加倉庫

本地git初始化后,此時還沒有添加遠程倉庫地址,需要添加一個遠程倉庫地址才能上傳代碼到服務器。
可在終端中運行git remote add <shortname> <url> 添加一個新的遠程 Git 倉庫。
shortnameurl的簡寫,當上傳代碼的時候,可用這個簡寫代替url地址。
命令詳細介紹:https://git-scm.com/book/zh/v2/Git-基礎-遠程倉庫的使用

$ git remote add pb https://github.com/paulboone/ticgit

git remote -v:查看本地git的遠程倉庫地址

當添加好遠程倉庫后,可以使用命令來查看添加的倉庫是否正確。

# 列出你指定的每一個遠程服務器的簡寫
$ git remote
origin  

# 指定選項 -v,會顯示需要讀寫遠程倉庫使用的 Git 保存的簡寫與其對應的 URL。
$ git remote -v
origin  https://github.com/schacon/ticgit (fetch)
origin  https://github.com/schacon/ticgit (push)

git remote show [remote-name] :查看遠程倉庫詳細信息

如果想要查看某一個遠程倉庫的更多信息,可以使用 git remote show [remote-name] 命令。
如果想以一個特定的縮寫名運行這個命令,例如 origin,會得到像下面類似的信息:

$ git remote show origin
* remote origin
  Fetch URL: https://github.com/schacon/ticgit
  Push  URL: https://github.com/schacon/ticgit
  HEAD branch: master
  Remote branches:
    master                               tracked
    dev-branch                           tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

遠程倉庫的重命名、移除、修改地址

如果想要重命名引用的名字可以運行 git remote rename 去修改一個遠程倉庫的簡寫名。
例如,想要將 pb 重命名為 paul,可以用 git remote rename 這樣做:

$ git remote rename pb paul
$ git remote
origin
paul

這同樣也會修改你的遠程分支名字。 那些過去引用 pb/master 的現在會引用 paul/master。

如果因為一些原因想要移除一個遠程倉庫 - 可以使用 git remote rm :

$ git remote rm paul
$ git remote
origin

修改地址:

git remote set-url origin www.baidu.com

關于遠程倉庫詳細介紹

二、從遠程倉庫(服務器)獲取代碼:本地什么也沒有,拉取代碼

1. 獲取默認分之代碼:git clone [URL]

如果本地沒有代碼,遠程倉庫有代碼,則需要從遠程倉庫克隆代碼。
克隆倉庫的命令格式是 git clone [url] 。 比如,要克隆 Git 的可鏈接庫 libgit2,可以用下面的命令:

$ git clone https://github.com/libgit2/libgit2   

這會在當前目錄下創建一個名為 “libgit2” 的目錄,并在這個目錄下初始化一個 .git 文件夾,
從遠程倉庫拉取下所有數據放入 .git 文件夾,然后從中讀取最新版本的文件的拷貝。
如果你進入到這個新建的 libgit2 文件夾,你會發現所有的項目文件已經在里面了,
準備就緒等待后續的開發和使用。

① 拉取倉庫時自定義本地倉庫文件名: git clone [URL] [新名字]

如果你想在克隆遠程倉庫的時候,自定義本地倉庫的名字,你可以使用如下命令:

$ git clone https://github.com/libgit2/libgit2 mylibgit    

這將執行與上一個命令相同的操作,不過在本地創建的倉庫名字變為 mylibgit。

關于克隆遠程倉庫命令詳細介紹:https://git-scm.com/book/zh/v2/Git-基礎-獲取-Git-倉庫

2. 直接拉取特定分之的代碼:git clone -b [分支名] [代碼地址]

如果不想克隆默認分之的代碼, 也可以克隆特定分之的代碼:

$ git  clone http://192.168.102.9/jas-paas/cloudlink-front-framework.git
Cloning into 'cloudlink-front-framework'...
remote: Counting objects: 14436, done.
remote: Compressing objects: 100% (3072/3072), done.
remote: Total 14436 (delta 11224), reused 14226 (delta 11075)
Receiving objects: 100% (14436/14436), 17.93 MiB | 4.98 MiB/s, done.
Resolving deltas: 100% (11224/11224), done.

3. 直接拉取特定標簽(tag)的代碼:git clone -b [標簽名] [代碼地址]

git允許直接克隆特定分之的代碼,不過克隆好后,項目里沒有分之,需要自己創建一個分支:

$ git clone -b V2.2 http://192.168.102.9/jas-paas/cloudlink-front-framework.git
Cloning into 'cloudlink-front-framework'...
remote: Counting objects: 14436, done.
remote: Compressing objects: 100% (3072/3072), done.
remote: Total 14436 (delta 11224), reused 14226 (delta 11075)
Receiving objects: 100% (14436/14436), 17.93 MiB | 4.70 MiB/s, done.
Resolving deltas: 100% (11224/11224), done.
Note: checking out 'eeea534cdae1f82c48c7b0de8f9993b54ffa065d'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

從信息中就可以看見,克隆特定標簽的項目后,是沒有分之的,需要進入項目目錄后,使用命令創建一個分支:

$ git checkout -b V2.2

4. 如果已經拉取了代碼,還需要拉取其他分支代碼:git checkout -b [分支名] [遠程倉庫名]/[分支名]

112289165-WX20170420-162329.png

例如,本地就一個master分支,遠程有2個分支(master,develop),把遠程的develop拉取到本地:

# 本地的分支是干凈的,也就是沒有修改的文件
# 獲取遠程所有分支名字
~ git fetch
# 顯示遠程所有分支名字
~ git branch -a
# 提取遠程新分支到本地
~ git checkout -b develop origin/develop       

三、記錄每次更新到倉庫

1. GIT中文件狀態介紹(已跟蹤/未跟蹤)

工作目錄下每個文件只有兩種狀態:

已跟蹤的兩種狀態

指那些被納入了版本控制的文件,在上一次快照中有它們的記錄,在工作一段時間后,它們的狀態可能處于未修改,已修改或已放入暫存區。
初次克隆某個倉庫的時候,工作目錄中的所有文件都屬于已跟蹤文件,并處于未修改狀態。
git會自動管理已跟蹤的文件,記錄文件處于什么狀態中。

已暫存(Changes to be committed)

文件在這個狀態下的,說明已經準備好把文件提交了,可以把這次代碼變動記錄保存在歷史記錄中。
這個狀態為GIT可以提交的內容。

已修改(Changes not staged for commit)

這狀態下的文件, git只是知道修改了那些內容,但是并不會在提交代碼的時候把這部分內容提交上去。
如果需要提交這部分代碼需要使用命令git add 把文件添加到 已暫存中,然后提交代碼。

未跟蹤(Untracked files)

工作目錄中除已跟蹤文件以外的所有其它文件都屬于未跟蹤文件,它們既不存在于上次快照的記錄中,也沒有放入暫存區。
git不會去管理這些文件。

2. git status: 查看當前文件狀態

使用git status命令可以查看文件處于什么狀態,例如:

$ git status
On branch P02                                   // 告訴你當前是哪個分之下
Your branch is up-to-date with 'origin/P02'.    // 當前分之是從哪個倉庫更新的
Changes to be committed:                        // 已暫存狀態
  (use "git reset HEAD <file>..." to unstage)   // 使用這個命令可回退到Changes not staged for commit:

    modified:   fileName.ts

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

Untracked files:                                // 未跟蹤的文件
  (use "git add <file>..." to include in what will be committed)

    src/library/

git status命令輸出的信息中:

Changes to be committed:
已暫存狀態
如果此時提交,那么該文件此時此刻的版本將被留存在歷史記錄中。

Changes not staged for commit:
已跟蹤文件的內容發生了變化,但還沒有放到暫存區。

Untracked files:
未跟蹤的文件,意味著 Git 在之前的快照(提交)中沒有這些文件;
Git 不會自動將之納入跟蹤范圍,除非你明明白白地告訴它“我需要跟蹤該文件”,
這樣的處理讓你不必擔心將生成的二進制文件或其它不想被跟蹤的文件包含進來。

3. git status -s緊湊格式輸出

使用git status -s命令或git status --short命令,你將得到一種更為緊湊的格式輸出。

$ git status -s           
 M README              // 出現在右邊的 M 表示該文件被修改了但是還沒放入暫存區
MM Rakefile            // 文件已經放入暫存區,但是又修改過了,在已修改中也存在
A  lib/git.rb          // 新添加到暫存區中的文件前面有 A 標記
M  lib/simplegit.rb    // 出現在靠左邊的 M 表示該文件被修改了并放入了暫存區
?? LICENSE.txt         // 新添加的未跟蹤文件前面有 ?? 標記

4. GIT代碼管理:改變文件的狀態

git add

git add命令是個多功能命令:可以用它開始跟蹤新文件,或者把已跟蹤的文件放到暫存區,
還能用于合并時把有沖突的文件標記為已解決狀態等。
將這個命令理解為“添加內容到下一次提交中”而不是“將一個文件添加到項目中”要更加合適。
例子:
src/test.txt文件是一個未跟蹤文件,可使用git add命令開始跟蹤:

$ git add src/test.txt    //指定單個文件添加
$ git add src/*           //指定src目錄下所有文件都添加

git checkout -- <file>: 撤消對文件的修改

如果你并不想保留對 CONTRIBUTING.md 文件的修改,
將它還原成上次提交時的樣子(或者剛克隆完的樣子,或者剛把它放入工作目錄時的樣子)
可使用如下命令:

git checkout -- CONTRIBUTING.md

注:
你需要知道 git checkout -- [file] 是一個危險的命令,這很重要。
你對那個文件做的任何修改都會消失 - 你只是拷貝了另一個文件來覆蓋它。
除非你確實清楚不想要那個文件了,否則不要使用這個命令。

git reset HEAD <file>:取消暫存的文件

如果你在提交代碼的時候,不想提交一些文件,可使用該命令把文件從暫存中回退到已修改的文件中。

git reset HEAD CONTRIBUTING.md     

關于撤銷操作的詳細介紹: https://git-scm.com/book/zh/v2/Git-基礎-撤消操作

5. .gitignore:忽略文件,不納入GIT版本控制

一般我們總會有些文件無需納入 Git 的管理,也不希望它們總出現在未跟蹤文件列表。
通常都是些自動生成的文件,比如日志文件,或者編譯過程中創建的臨時文件等。
在這種情況下,我們可以在項目根目錄創建一個名為 .gitignore 的文件,列出要忽略的文件模式。

文件 .gitignore 的格式規范如下:

  1. 所有空行或者以 # 開頭的行都會被 Git 忽略。
  2. 可以使用標準的 glob 模式匹配。
  3. 匹配模式可以以(/)開頭防止遞歸。
  4. 匹配模式可以以(/)結尾指定目錄。
  5. 要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(!)取反。

一個 .gitignore 文件的例子:

# 忽略所有后綴為 .a 的文件
*.a
# 但是要跟蹤lib.a,即使你上面忽略了 .a文件
!lib.a
# 僅忽略當前目錄的TODO文件夾, 不是subdir/TODO 
/TODO
# 忽略build /目錄中的所有文件
build/
# 忽略doc / notes.txt,而不是doc / server / arch.txt
doc/*.txt
# 忽略doc 目錄中的所有.pdf文件
doc/**/*.pdf

GitHub 有一個十分詳細的針對數十種項目及語言的 .gitignore 文件列表:
https://github.com/github/gitignore

這部分詳細介紹: https://git-scm.com/book/zh/v2/Git-基礎-記錄每次更新到倉庫

6. git diff: 查看代碼修改詳細內容

如果你想知道文件具體修改了什么,可以用git diff命令。

Ⅰ. git diff: 查看尚未暫存的文件修改記錄

git diff命令查看修改之后還沒有暫存起來的變化內容。也就是git status命令輸出信息中,
Changes not staged for commit:下面的文件, 例如:

$ git diff         //查看所有詳細修改
$ git diff 1.txt   //只查看 1.txt文件的 詳細修改   

//輸出信息如下   
diff --git a/.gitignore b/.gitignore   //①
index e722882..f98470c 100644          //②
--- a/.gitignore    //③
+++ b/.gitignore    //③
@@ -3,8 +3,8 @@     //④

 *~
 *.sw[mnpcod]
-.DS_Store          //⑤
 *.log              //⑥
+text.t             //⑦
 *.tmp
 *.tmp.*
 log.txt

① git格式的diff,進行比較的是,a版本的.gitignore(即變動前)和b版本的.gitignore(即變動后)。
② 表示兩個版本的git哈希值(index區域的e722882對象,與工作目錄區域的f98470c對象進行比較),
最后的六位數字是對象的模式(普通文件,644權限)。
③ 表示進行比較的兩個文件。 "---"表示變動前的版本,"+++"表示變動后的版本。
④ 下面代碼所在的行:
-3,7: 修改前下面信息變化的行, 從第3行開始一直到第7行
+3,6: 修改后下面信息變化的行, 從第3行開始一直到第6行
⑤ 以- 開頭表示刪除的代碼
⑥ 以空格開頭表示沒有修改
⑦ 以+開頭便是增加的代碼

git diff輸出結果介紹

Ⅱ. git diff --staged: 查看以暫存文件修改記錄

查看已暫存的將要添加到下次提交里的內容可是使用命令git diff --staged(GIT版本1.6.1以上)。
也可以使用git diff --cached命令。

git diff --staged        // 查看所有已暫存文件的修改記錄
git diff --staged t.txt  // 只查看1.txt文件 已暫存的修改記錄

Ⅲ. git diff 的其他用法: --stat HEAD SHA1

~ git diff --stat         // 查看簡單的diff結果,只查看修改的文件名、修改了多少內容
~ git diff HEAD           // 查看所有修改記錄(已暫存、已修改):顯示工作版本(Working tree)和HEAD的差別
~ git diff topic master   // 直接將兩個分支上最新的提交做diff
~ git diff HEAD^ HEAD     // 比較上次提交commit和上上次提交
~ git diff SHA1 SHA2      // 比較兩個歷史版本之間的差異

7. 把代碼納入版本控制中: 提交更新 git commit

當所有需要提交的代碼都放到暫存區后,就可以提交代碼了:

$ git commit

這種方式會啟動文本編輯器以便輸入本次提交的說明。
一般都是 vim 或 emacs。使用 git config --global core.editor 命令設定你喜歡的編輯軟件。
編輯器會顯示類似下面的文本信息(本例選用 Vim 的屏顯方式展示):

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   new file:   README
#   modified:   CONTRIBUTING.md
#
~
~
".git/COMMIT_EDITMSG" 9L, 283C

可以在這段文本信息中看見, 所有以#符號開頭的行,都會被忽略,也就是#是注釋行,不會放到提交信息中去。
接下來就可以輸入一些提交信息了,關于寫提交信息的建議:

信息應當以少于 50 個字符(25個漢字)的單行開始且簡要地描述變更,接著是一個空白行,再接著是一個更詳細的解釋。
Git 項目要求一個更詳細的解釋,包括做改動的動機和它的實現與之前行為的對比 - 這是一個值得遵循的好規則。

提交信息簡單的模板:

修改的摘要(50 個字符或更少)

如果必要的話,加入更詳細的解釋文字。在
大概 72 個字符的時候換行。在某些情形下,
第一行被當作一封電子郵件的標題,剩下的
文本作為正文。分隔摘要與正文的空行是
必須的(除非你完全省略正文);如果你將
兩者混在一起,那么類似變基等工具無法
正常工作。

關于VIM編輯器使用教程: vim編輯器常用命令與用法總結

commit命令的其他的用法: -m選項,提交信息簡寫

在 commit 命令后添加 -m 選項,將提交信息與命令放在同一行,如下所示:

$ git ci -m "測試提交"
[master f949399] 測試提交
 1 file changed, 1 insertion(+)

從輸出的信息中可以看見:
當前是在哪個分支(master)提交的,本次提交的完整 SHA-1 校驗和是什么(f949399),
以及在本次提交中,有多少文件修訂過,多少行添加和刪改過。

注意:
提交時記錄的是放在暫存區域的快照。 任何還未暫存的仍然保持已修改狀態,可以在下次提交時納入版本管理。
每一次運行提交操作,都是對你項目作一次快照,以后可以回到這個狀態,或者進行比較。

commit命令的其他的用法: -a選項,提交所有修改過的文件(已暫存,已修改)

有時候把修改的提交到暫存區,然后在提交代碼比較繁瑣, GIT提供了一種-a選項,在提交代碼的時候,
跳過暫存區,把所有已經跟蹤的文件暫存起來一并提交,從而跳過git add步驟,例如:

$ 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:   CONTRIBUTING.md

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

$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
 1 file changed, 5 insertions(+), 0 deletions(-)

從輸出信息中可以看到:提交之前不再需要 git add 文件“CONTRIBUTING.md”了。

關于git commit命令詳解:https://git-scm.com/book/zh/v2/Git-基礎-記錄每次更新到倉庫

8. git push: 把本地的跟新推送到遠程倉庫服務器上

當在本地開發完畢,需要把代碼提交到服務器時,可使用it push [remote-name] [branch-name]命令。
你想要將 master 分支推送到 origin 服務器時:

$ git push origin master     // 把更新上傳到 origin服務器的 master分之上。
$ git push                   // 上傳本地所有分支代碼到遠程對應的分支上

只有當你有所克隆服務器的寫入權限,并且之前沒有人推送過時,這條命令才能生效。
當你和其他人在同一時間克隆,他們先推送到上游然后你再推送到上游,你的推送就會毫無疑問地被拒絕。
你必須先將他們的工作拉取下來并將其合并進你的工作后才能推送。

四、 查看提交歷史: git log

在提交了若干更新,又或者克隆了某個項目之后,你也許想回顧下提交歷史。 完成這個任務最簡單而又有效的工具是 git log 命令:

$ git log
commit ca82a6dff817ec66f44342007202690a93763949    // 提交的 SHA-1 校驗和
Author: Scott Chacon <schacon@gee-mail.com>        // 作者的名字和電子郵件地址
Date:   Mon Mar 17 21:52:11 2008 -0700             // 提交時間

    changed the version number                     // 提交說明

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test

默認不用任何參數的話,git log 會按提交時間列出所有的更新,最近的更新排在最上面。

git log 的常用選項: -stat, -p, --name-status, --pretty=oneline, --abbrev-commit

git log --stat:顯示每次更新的文件修改統計信息(修改的文件名,每個文件添加的多少、刪除了多少數字)。

git log --name-only: 只顯示修改的文件名,沒有其他信息

git log --pretty=oneline: 用一行顯示信息

$ git log 1.txt            //查看 1.txt文件的歷史修改記錄。
$ git log -n               //只查看前n次修改記錄,例如查看前2次的記錄: git log -2
$ git log -p               // 按補丁格式顯示每個更新之間的差異。  
$ git log --shortstat      // 只顯示 --stat 中最后的行數修改添加移除統計。
$ git log --name-status    // 顯示新增、修改、刪除的文件清單。     
$ git log --abbrev-commit  // 僅顯示 SHA-1 的前幾個字符,而非所有的 40 個字符。
$ git log --relative-date  // 使用較短的相對時間顯示(比如,“2 weeks ago”)。
$ git log --graph          // 顯示 分支合并歷史。   
$ git log --pretty=oneline // 用一行顯示信息
// 使用其他格式顯示歷史提交信息。可用的選項包括 oneline,short,full,fuller 和 format(后跟指定格式)。

關于git log命令詳解

五、 打標簽(一個版本發布) git tag

像其他版本控制系統(VCS)一樣,Git 可以給歷史中的某一個提交打上標簽,以示重要。
比較有代表性的是人們會使用這個功能來標記發布版本節點(v1.0 等等)。

1. 什么是標簽?

標簽可以認為是版本管理工具的一個版本,也就是說像很多第三方開源軟件安裝包表示的版本號,
比如說1.0,2.0等等,它代表修復了一個bug,或者重大的重構,git的標簽也是這樣的一個作用,
可以對版本的不同階段做一個標示。

2. 查看現有的標簽

① 查看所有標簽:git tag

$ git tag   
v0.1
v1.3
v1.4

這個命令以字母順序列出標簽;但是它們出現的順序并不重要。

② 查看符合條件的標簽:git tag -l 'v1.*'

也可以使用特定的模式查找標簽, 如果只對 1.0以上tag感興趣,可以運行:

$ git tag -l 'v1.*'

③ 查看一個標簽的詳細信息:git tag show V1.4

該命令會輸出打標簽者的信息、打標簽的日期時間、附注信息,然后顯示具體的提交信息。

3. 創建標簽:Git 使用兩種主要類型的標簽:

git tag -a:創建附注標簽(annotated)

附注標簽是存儲在 Git 數據庫中的一個完整對象。
它們是可以被校驗的;其中包含打標簽者的名字、電子郵件地址、日期時間;還有一個標簽信息;
并且可以使用 GNU Privacy Guard (GPG)簽名與驗證。 通常建議創建附注標簽,這樣你可以擁有以上所有信息。

創建一個附注標簽,使用tag命令時指定-a選項:

$ git tag -a v1.4 -m 'my version 1.4'    

-m選項指定了一條將會存儲在標簽中的信息。 如果沒有為附注標簽指定一條信息,Git 會運行編輯器要求你輸入信息。

如果你有自己的私鑰,還可以用 GPG 來簽署標簽,只需要把之前的-a改為-s(取 signed 的首字母)即可:

git tag -s v1.5 -m 'my signed 1.5 tag'

再運行 git show 會看到對應的 GPG 簽名也附在其內.

git tag V1.4: 創建輕量標簽(lightweight)

如果你只是想用一個臨時的標簽,或者因為某些原因不想要保存那些信息,則可以選擇使用輕量標簽。
輕量標簽本質上是將提交校驗和存儲到一個文件中 - 沒有保存任何其他信息。
創建輕量標簽,不需要使用 -a、-s 或 -m 選項,只需要提供標簽名字:

$ git tag v1.4-lw

git tag -a v1.2 9fceb02:對某個歷史提交打標簽(后期打標簽)

如果你在項目版本發布的時候忙忘記打標簽,你可以在之后補上標簽。
要在那個提交上打標簽,你需要在命令的末尾指定提交的校驗和(或部分校驗和):

git tag -a v1.2 9fceb02    

4. 把創建的標簽推送到服務器上:git push origin [tagname]

默認情況下,git push命令并不會傳送標簽到遠程倉庫服務器上。 在創建完標簽后你必須顯式地推送標簽到共享服務器上。

$ git push origin v1.5

Counting objects: 14, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done.
Total 14 (delta 3), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new tag]         v1.5 -> v1.5

如果想要一次性推送很多標簽,也可以使用帶有--tags選項的git push命令。
這將會把所有不在遠程倉庫服務器上的標簽全部傳送到服務器。

$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new tag]         v1.4 -> v1.4
 * [new tag]         v1.4-lw -> v1.4-lw

現在,當其他人從倉庫中克隆或拉取,他們也能得到你的那些標簽。

5. 檢出標簽: 代碼變成標簽的樣子

在 Git 中你并不能真的檢出一個標簽,因為它們并不能像分支一樣來回移動。
如果你想要工作目錄與倉庫中特定的標簽版本完全一樣,
可以使用git checkout -b [branchname] [tagname]在特定的標簽上創建一個新分支:

$ git checkout -b version2 v2.0.0
Switched to a new branch 'version2'

6. 刪除標簽

git tag -d v1.0:刪除本地標簽

$ git tag -d v1.0  
Deleted tag 'v1.0' (was 2ffea56)

git push origin :refs/tags/v0.1:刪除服務器標簽

$ git push origin :refs/tags/v0.1
To http://192.168.95.95/user/test.git
 - [deleted]         v0.1

如果服務器的標簽刪除后,本地的也需要自己刪除。

關于git tag詳解

六、 GIT分支

1. 分支介紹作用

Git 的分支,其實本質上僅僅是指向提交對象的可變指針。 Git 的默認分支名字是 master。
在多次提交操作之后,你其實已經有一個指向最后那個提交對象的 master 分支。 它會在每次的提交操作中自動向前移動。

分支作用:

  1. 開發多個項目任務,比如說我有兩個任務都比較緊急,任務1需要兩天完成,任務2需要一天完成,而任務1是之前就已經開始進行的,任務二是中間加的新任務,所以需要第一天就完成任務2.
  2. master分支始終要保證可發布的狀態,用dev分支和bug分支進行開發和錯誤調試,這樣能夠保證主干代碼的干凈、可發布。
  3. 自己開發測試或者修復BUG等等,可以避免代碼的丟失。

2. 本地分支的創建、切換、刪除:

① 創建分支:git branch testing

比如,創建一個 testing 分支, 你需要使用 git branch 命令:

$ git branch testing

這條命令會根據當前的分支來創建一個新分支,分支中內容與當前分支內容完全一樣。
git branch命令僅僅 創建 一個新分支,并不會自動切換到新分支中去。

② 切換分支:git checkout testing

要切換到一個已存在的分支,你需要使用git checkout命令。 我們現在切換到新創建的 testing 分支去:

$ git checkout testing 

③ 創建并且換:git checkout -b testing

$ git checkout -b testing
Switched to a new branch "testing"

這個命令會創建一個分支并切換到新分支中去, 實際上他是上面2條命令的簡化版。

④ 刪除分支:git branch -d testing

刪除一個分支的時候,你不能在要刪除的分支中,要切換到別的分支中,否則會報錯。
如果要刪除 testing分支,可以使用帶 -d 選項的 git branch 命令來刪除分支:

$ git br -d testing
Deleted branch testing (was 4baf2a3).

3. 遠程倉庫分支的新建與刪除

①遠程倉庫分支的新建: git push origin testing

如果想把本地的新分支推送到遠程倉庫,可使用 git push [遠程倉庫名] [分支名]

$ git br -a

  testing
* master
  remotes/origin/master

$ git push origin testing

Total 0 (delta 0), reused 0 (delta 0)
To http://192.168.132.55/user/test.git
 * [new branch]      testing -> testing

② 刪除遠程倉庫的無用分支:git push origin --delete testing

如果遠程倉庫某個分支無用了,想要刪除,可以運行帶有 --delete 選項的 git push 命令來刪除一個遠程分支:

git push origin --delete testing
To http://192.168.132.55/user/test.git
 - [deleted]         testing

4. 分支合并:git merge

如果一個分支的功能開發完畢了,需要把開發的內容合并的其他分支中去,則需要使用 git的 merge命令。
例如, 現在有2個分支(master, ningning), master分支的代碼要合并到ningning,2個分支代碼都已經git commit過了。
如果沒有commit,需要先commit,否則不能合并代碼。

① 合并命令:git merge [分支名]

// 切換到需要添加新功能的分支上
$ git checkout ningning
// 把 master分支上的代碼合并到ningning分支上
$ git merge master

② 合并很完美,直接合并成功的提示

如果合并成功了則輸出的信息類似于下面:

$ git merge master 
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)   

// 或者下面的輸出信息
Merge made by the 'recursive' strategy.
index.html |    1 +
1 file changed, 1 insertion(+)

Git 將合并的結果做了一個新的快照并且自動創建一個新的提交指向它。

③ 合并過程中代碼有沖突,解決沖突

3353411608-WX20170420-172823.png

錯誤信息類似于:

Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Auto-merging是指已經自動合并的文件,
CONFLICT (content)是指有沖突的文件,需要解決沖突的文件。

打開有沖突的文件,沖突部分代碼類似于下面:

<<<<<<< HEAD:ningning
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> master  

<<<<< ======= 中的是 ningning分支的代碼。
======= >>>>>> 中的是 master分支的代碼。
經過對比后刪除沖突部分的代碼, 并把 <<< ==== >>>> 所在行全部刪除。
保存后,使用git add添加修改的文件。
使用git commit命令來完成合并提交:

解決沖突文件:git checkout --ours/--theirs放棄其中一個分支的沖突代碼

如果想放棄一個分支文件的沖突代碼,只保留一個分支的代碼,可使用如下命令。

假如:沖突文件名為 1.txt,要放棄master分支的修改,可使用如下命令:

$ git checkout --ours 1.txt

放棄ningning分支沖突代碼,可使用如下命令:

$ git checkout --theirs 1.txt 

④ 取消合并

沖突太多,解決沖突亂了, 可取消合并:git reset --hard HEAD

如果沖突代碼太多了,解決沖突代碼過程中產生了混亂,想要重新合并,可使用下面命令取消這次合并:

git reset --hard HEAD
HEAD is now at 9e791f3 提交信息
合并已經commit,還沒上傳遠程倉庫,取消合并:git reset --hard HEAD^

如果合并的代碼產生了錯誤,或者合并有問題,但是已經commit了,但是還沒有把合并提交到遠程倉庫,則可以使用如下命令取消這次合并:

$ git reset --hard HEAD^ 



GIT的高級用法(平時用的很少,了解就好)




GIT工具: 儲藏與清理

1. git stash : 修改的代碼暫存起來,把工作目錄變干凈,方便切換分支等做一些其他事情

當你的工作已經做了一段時間后,所開發的功能還沒有完成, 而這時候你想要切換分支做一點別的事情。
但你又不想應為過一會就回到這一點而為做了一半工作就創建一次提交,這個時候你就可以使用git stash命令。
git stash命令會處理工作目錄的臟的狀態,即修改的跟蹤文件與暫存的改動。然后將未完成的修改保存到一個棧上,
而你可以在任何時候重新應用這些改動。

① 把代碼儲藏起來: git stash

$ git status
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   index.html

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:   lib/simplegit.rb

//儲藏代碼
$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")

$ git status
# On branch master
nothing to commit, working directory clean

上面使用了git stash 儲藏了代碼,這時候在看工作目錄就變成干凈了,這個時候你就可以做別的事情了。
還可以使用git stash save命令,與 git stash 是一樣的。

② 查看儲藏的代碼: git stash list

要查看儲藏的東西,可以使用git stash list命令:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log

最新的是stash@{0}

③ 把儲藏的代碼取出來: git stash apply

如果想把儲藏的代碼應用回來,可使用git stash apply命令,如果不指定一個儲藏,Git 認為指定的是最近的儲藏。
如果想要應用其中一個更舊的儲藏,可以通過名字指定它,像這樣:git stash apply stash@{2}

$ git stash apply --index
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   index.html
#      modified:   lib/simplegit.rb
#
git stash apply --index

文件的改動被重新應用了,但是之前已修改未暫存的文件卻沒有回到原來的位置,想要完全回到之前的樣子,
必須使用--index選項來運行git stash apply命令,來嘗試重新應用暫存的修改。
如果已經那樣做了,那么你將回到原來的位置:

$ git stash apply --index
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#

④ 刪除儲藏堆棧上的某個儲藏:git stash drop stash@{0}

使用git stash apply 命令只會嘗試應用暫存的工作 ,在堆棧上還有它。
可以運行git stash drop加上將要移除的儲藏的名字來移除它:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

⑤ 應用儲藏然后立即從棧上扔掉它:git stash pop

這個命令只會應用最新的,然后丟掉它。

git stash --keep-index: 不要儲藏任何你通過 git add 命令已暫存的東西

當你做了幾個改動并只想提交其中的一部分,過一會兒再回來處理剩余改動時,這個功能會很有用。

$ git status -s
M  index.html
 M lib/simplegit.rb

$ git stash --keep-index
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
M  index.html

git stash -u: 把任何創建的未跟蹤文件也儲藏

默認情況下,git stash只會儲藏已經在索引中的文件。
如果指定 --include-untracked-u標記,Git 也會儲藏任何創建的未跟蹤文件。

$ git status -s
M  index.html
 M lib/simplegit.rb
?? new-file.txt

$ git stash -u
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
$

從儲藏創建一個分支:解決應用儲藏沖突問題

如果儲藏了一些工作,將它留在那兒了一會兒,然后繼續在儲藏的分支上工作,在重新應用工作時可能會有問題。
如果應用嘗試修改剛剛修改的文件,你會得到一個合并沖突并不得不解決它。
如果想要一個輕松的方式來再次測試儲藏的改動,可以運行git stash branch創建一個新分支,
檢出儲藏工作時所在的提交,重新在那應用工作,然后在應用成功后扔掉儲藏:

$ git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

清理工作目錄

有時候你想清除工作目錄一些無用的文件,或是為了運行一個干凈的構建而移除之前構建的殘留。
這個時候你可以使用git clean命令。 它被設計為從工作目錄中移除未被追蹤的文件。
如果你改變主意了,你也不一定能找回來那些文件的內容。
一個更安全的選項是運行 git stash --all 來移除每一樣東西并存放在棧中。

git clean -f -d:移除工作目錄中所有未追蹤的文件以及空的子目錄

-d 是 目錄的意思。
-f 意味著 強制 或 “確定移除”。

$ git clean -f -d
Removing 5.txt
Removing tttt/

git clean -f -d -n: 做一次演習然后告訴你 將要 移除什么

git clean -f -d -x: 移除.gitiignore文件中忽略的文件

默認情況下,git clean 命令只會移除沒有忽略的未跟蹤文件。
任何與 .gitiignore 或其他忽略文件中的模式匹配的文件都不會被移除。
如果你也想要移除那些文件,例如為了做一次完全干凈的構建而移除所有由構建生成的 .o 文件,
可以給 clean 命令增加一個 -x 選項。

$ git status -s
 M lib/simplegit.rb
?? build.TMP
?? tmp/

$ git clean -n -d
Would remove build.TMP
Would remove tmp/

$ git clean -n -d -x
Would remove build.TMP
Would remove test.o
Would remove tmp/

重寫歷史:git 提交歷史修改

1. 修改最后一次提交

修改你最近一次提交可能是所有修改歷史提交的操作中最常見的一個。
對于你的最近一次提交,你往往想做兩件事情:修改提交信息,或者修改你添加、修改和移除的文件的快照。

① 只是修改最后一次提交的提交信息:git commit --amend

如果只是想修改最后一次提交的提交信息,可以直接使用如下命令:

$ git commit --amend 
[dev 3fa2e1a] 合并提交測試3號,測試修改最后一次提交。在測試一下。
 Date: Thu Nov 2 11:29:00 2017 +0800
 1 file changed, 1 insertion(+)
 create mode 100644 5.txt

這會把你帶入文本編輯器,里面包含了你最近一條提交信息,供你修改。
當保存并關閉編輯器后,編輯器將會用你輸入的內容替換最近一條提交信息。

② 修改最后一次提交的快照:git addgit rm然后git commit --amend

如果你已經完成提交,又因為之前提交時忘記添加一個新創建的文件,想通過添加或修改文件來更改提交的快照,
也可以通過類似的操作來完成。 通過修改文件然后運行git addgit rm一個已追蹤的文件,
隨后運行git commit --amend拿走當前的暫存區域并使其做為新提交的快照。

使用這個技巧的時候需要小心,因為修正會改變提交的 SHA-1 校驗和。
它類似于一個小的變基 - 如果已經推送了最后一次提交則需要慎重修改。

③ 把修改的最后一次提交信息強推到服務器:git push --f

此操作要慎重,如果要修改服務器上的最后一次提交,可現在本地修改,
然后使用git push --f(force)強推到服務器中去。

git push --force
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 458 bytes | 458.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To http://192.168.102.9/yulilong/test.git
 + 5c0a032...3fa2e1a dev -> dev (forced update)

④ gitlab服務器強推被拒絕: 倉庫 -> 設置 -> 保護分支: 把被保護的分支去掉即可解決問題

2. 使用git rebase合并多次commit

使用git log 查看一下歷史:

$ git log 

commit 3fa2e1a951ff823fbec625a96049e27ef35a85b8 (HEAD -> dev, origin/dev)
Author: yulilong <yulilong222q@outlook.com>
Date:   Thu Nov 2 11:29:00 2017 +0800

    合并提交測試3號,測試修改最后一次提交。在測試一下。

commit f526876def0b80676bfcf52937f78052e2d63955
Author: yulilong <yulilong222q@outlook.com>
Date:   Thu Nov 2 11:27:20 2017 +0800

    合并提交測試2號

commit 4d2b6ce4d5154c0b739e552e679bb7f4ce05fb2c
Author: yulilong <yulilong222q@outlook.com>
Date:   Thu Nov 2 11:25:59 2017 +0800

    合并提交測試一號

commit 9e791f38f4d5f472a371a8c147f37670185582f8
Author: yulilong <yulilong222q@outlook.com>
Date:   Mon Oct 30 10:22:02 2017 +0800

    這是dev分支上的合并提交測試

commit 4baf2a319c0de8f46630ba85db11cc4aebd1d2cd
Author: yulilong <yulilong222q@outlook.com>
Date:   Fri Sep 29 10:20:08 2017 +0800

    這是測試-a -m命令提交記錄

如果想要合并最后三次的提交,可使用git rebase -i HEAD~3git rebase -i 9e791f38f4d命令來壓縮.
該命令執行后,會彈出一個編輯窗口,3次提交的commit倒序排列,最上面的是最早的提交,最下面的是最近一次提交。

  1 pick 4d2b6ce 合并提交測試一號
  2 pick f526876 合并提交測試2號
  3 pick 3fa2e1a 合并提交測試3號,測試修改最后一次提交。在測試一下。
  4 
  5 # Rebase 9e791f3..3fa2e1a onto 9e791f3 (3 commands)
  6 #
  7 # Commands:
  8 # p, pick = use commit
  9 # r, reword = use commit, but edit the commit message
 10 # e, edit = use commit, but stop for amending
 11 # s, squash = use commit, but meld into previous commit
 12 # f, fixup = like "squash", but discard this commit's log message
 13 # x, exec = run command (the rest of the line) using shell
 14 # d, drop = remove commit
 15 #
 16 # These lines can be re-ordered; they are executed from top to bottom.
 17 #
 18 # If you remove a line here THAT COMMIT WILL BE LOST.
 19 #
 20 # However, if you remove everything, the rebase will be aborted.
 21 #
 22 # Note that empty commits are commented out

修改第二行、第三行的第一個單詞pick為squash(這些命令什么意思下面的注釋有說明)。
然后保存退出,git會壓縮提交歷史,
如果有沖突,需要修改,修改的時候要注意,保留最新的歷史,不然我們的修改就丟棄了。
修改以后要記得敲下面的命令:

$ git add .  
# git rebase --continue  

如果你想放棄這次壓縮的話,執行以下命令:

$ git rebase --abort 

如果沒有沖突,或者沖突已經解決,則會出現如下的編輯窗口:

  1 # This is a combination of 3 commits.
  2 # This is the 1st commit message:
  3 
  4 合并提交測試一號
  5 
  6 # This is the commit message #2:
  7 
  8 合并提交測試2號
  9 
 10 # This is the commit message #3:
 11 
 12 合并提交測試3號,測試修改最后一次提交。在測試一下。
 13      
 14 # Please enter the commit message for your changes. Lines starting
 15 # with '#' will be ignored, and an empty message aborts the commit.
 16 #   
 17 # Date:      Thu Nov 2 11:25:59 2017 +0800
 18 #        
 19 # interactive rebase in progress; onto 9e791f3
 20 # Last commands done (3 commands done):
 21 #    squash f526876 合并提交測試2號
 22 #    squash 3fa2e1a 合并提交測試3號,測試修改最后一次提交。在測試一下。
 23 # No commands remaining.
 24 # You are currently rebasing branch 'dev' on '9e791f3'.
 25 #
 26 # Changes to be committed:
 27 #   modified:   1.txt
 28 #   new file:   4.txt
 29 #   new file:   5.txt

編輯好合并的提交信息后保存,可看見如下輸出信息:

[detached HEAD 83d65c7] 三次合并提交的測試,成功了。
 Date: Thu Nov 2 11:25:59 2017 +0800
 3 files changed, 7 insertions(+)
 create mode 100644 4.txt
 create mode 100644 5.txt
Successfully rebased and updated refs/heads/dev.

最后把合并的記錄強推到服務器中去:

git push -f
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 596 bytes | 596.00 KiB/s, done.
Total 5 (delta 1), reused 0 (delta 0)
To http://192.168.102.9/yulilong/test.git
 + 3fa2e1a...83d65c7 dev -> dev (forced update)

服務器合并提交前:

服務器合并后:

參考鏈接:http://blog.csdn.net/yangcs2009/article/details/47166361

3. 把一個文件從提交歷史中徹底刪除:git filter-branch --tree-filter 'rm -f 1.txt' HEAD

有些人不經思考使用git add .,意外地提交了一個巨大的二進制文件,你想將它從所有地方刪除。
也許你不小心提交了一個包含密碼的文件,而你想讓你的項目開源。filter-branch大概會是你用來清理整個歷史的工具。
如果想要從整個歷史總刪除1.txt文件,你可以在filter-branch上使用--tree-filter選項:

$ git filter-branch -f --tree-filter 'rm -f 1.txt' HEAD 
Rewrite 83d65c7b098e0ec1f14d9a332187632b49d2ad9f (5/5) (0 seconds passed, remaining 0 predicted)    
Ref 'refs/heads/dev' was rewritten

如果執行命令的時候提示如下錯誤,刪除 .git/refs/original/ 目錄,或使用-f命令強制覆蓋:

$ git filter-branch -f --tree-filter 'rm -f 1.txt' HEAD 
Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f   

git filter-branch -f --tree-filter 'rm -f 1.txt' HEAD 
Rewrite 83d65c7b098e0ec1f14d9a332187632b49d2ad9f (5/5) (0 seconds passed, remaining 0 predicted)    
Ref 'refs/heads/dev' was rewritten

如果后悔刪除了,可使用如下命令恢復:git reset --hard refs/original/refs/heads/[分支名]

$ git reset --hard refs/original/refs/heads/dev
HEAD is now at 83d65c7 三次合并提交的測試,成功了。

把刪除后的項目提交到服務器:git push origin +[分支名], 注意:一旦運行此命令,刪除的文件不能找回

$ git push origin +dev         

Counting objects: 14, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (14/14), 1.40 KiB | 1.40 MiB/s, done.
Total 14 (delta 1), reused 0 (delta 0)
To http://192.168.102.110/user/test.git
 + 83d65c7...4b4da80 dev -> dev (forced update)

注意,分支名字前面的+號一定不能忘記,否則會報如下錯誤:

To http://192.168.102.110/user/test.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'http://192.168.102.110/user/test.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

推送之前要把服務器的分支保護去掉,否則會報如下錯誤:

remote: GitLab: You are not allowed to force push code to a protected branch on this project.
To http://192.168.102.110/user/test.git
 ! [remote rejected] dev -> dev (pre-receive hook declined)
error: failed to push some refs to 'http://192.168.102.110/user/test.git'

刪除一堆類似文件:git filter-branch --tree-filter "find * -type f -name '*~' -delete" HEAD

全局性地更換電子郵件地址

$ git filter-branch --commit-filter '
        if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
        then
                GIT_AUTHOR_NAME="Scott Chacon";
                GIT_AUTHOR_EMAIL="schacon@example.com";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD

參考鏈接: https://git-scm.com/book/zh/v2/Git-工具-重寫歷史

GIT的工作原理

Git 的思維框架(將其作為內容管理器)是管理三棵不同的樹。“樹” 在我們這里的實際意思是 “文件的集合”,而不是指特定的數據結構。

Git 作為一個系統,是以它的一般操作來管理并操縱這三棵樹的:

----------------------------
樹                       用途

HEAD                    上一次提交的快照,下一次提交的父節點

index                   預期的下一次提交的快照

Working Directory       沙盒(工作目錄)
  • HEAD

HEAD 是當前分支引用的指針,它總是指向該分支上的最后一次提交。 這表示 HEAD 將是下一次提交的父結點。
通常,理解 HEAD 的最簡方式,就是將它看做 你的上一次提交 的快照。

下例命令就顯示了HEAD快照實際的目錄列表,以及其中每個文件的SHA-1校驗和:

$ git cat-file -p HEAD
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
author Scott Chacon  1301511835 -0700
committer Scott Chacon  1301511835 -0700

initial commit

$ git ls-tree -r HEAD
100644 blob a906cb2a4a904a152...   README
100644 blob 8f94139338f9404f2...   Rakefile
040000 tree 99f1a6d12cb4b6f19...   lib

cat-filels-tree是底層命令,它們一般用于底層工作,在日常工作中并不使用。不過它們能幫助我們了解到底發生了什么。

  • index

index(索引)是你的預期的下一次提交。也會將這個概念引用為 Git 的 “暫存區域”,這就是當你運行git commit時Git看起來的樣子。

Git 將上一次檢出到工作目錄中的所有文件填充到索引區,它們看起來就像最初被檢出時的樣子。
之后你會將其中一些文件替換為新版本,接著通過 git commit 將它們轉換為樹來用作新的提交。

$ git ls-files -s
100644 a906cb2a4a904a152e80877d4088654daad0c859 0   README
100644 8f94139338f9404f26296befa88755fc2598c289 0   Rakefile
100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0   lib/simplegit.rb

ls-files是底層命令,它會顯示出索引當前的樣子。
確切來說,索引并非技術上的樹結構,它其實是以扁平的清單實現的。不過對我們而言,把它當做樹就夠了。

  • Working Directory

Working Directory(工作目錄),最后,你就有了自己的工作目錄。
另外兩棵樹以一種高效但并不直觀的方式,將它們的內容存儲在 .git 文件夾中。
工作目錄會將它們解包為實際的文件以便編輯。 你可以把工作目錄當做 沙盒。
在你將修改提交到暫存區并記錄到歷史之前,可以隨意更改。

工作流程

+-------------------------------------------------------------------+
|   +--------------+      +---------------+      +--------------+   |
|   |  Working     |      |     Index     |      |     HEAD     |   |
|   |  Directory   |      |               |      |              |   |
|   +-----+--------+      +-------+-------+      +-------+------+   |
|         |                       |                      |          |
|         |                       | Checkout the project |          |
|         |  <-------------------------------------------+          |
|         |                       |                      |          |
|         |    Stage Files        |                      |          |
|         +---------------------> |                      |          |
|         |                       |      Commit          |          |
|         |                       +--------------------> |          |
|         |                       |                      |          |
+-------------------------------------------------------------------+
  1. 假設我們進入到一個新目錄,其中有一個文件。 我們稱其為該文件的 v1 版本,將它標記為藍色。
    現在運行git init,這會創建一個 Git 倉庫,其中的 HEAD 引用指向未創建的分支(master 還不存在)。

  2. 此時,只有工作目錄有內容。 現在我們想要提交這個文件,所以用git add來獲取工作目錄中的內容,并將其復制到索引(Index)中。

  3. 接著運行git commit,它首先會移除索引中的內容并將它保存為一個永久的快照,
    然后創建一個指向該快照的提交對象,最后更新 master 來指向本次提交。

  4. 此時如果我們運行git status,會發現沒有任何改動,因為現在三棵樹完全相同。
    現在我們想要對文件進行修改然后提交它。 我們將會經歷同樣的過程;首先在工作目錄中修改文件。
    我們稱其為該文件的 v2 版本,并將它標記為紅色。

  5. 如果現在運行 git status,我們會看到文件顯示在 “Changes not staged for commit,” 下面并被標記為紅色,
    因為該條目在索引與工作目錄之間存在不同。 接著我們運行git add來將它暫存到索引中。

  6. 此時,由于索引和 HEAD 不同,若運行git status的話就會看到 “Changes to be committed” 下的該文件變為綠色.
    也就是說,現在預期的下一次提交與上一次提交不同。 最后,我們運行git commit來完成提交。

  7. 現在運行git status會沒有輸出,因為三棵樹又變得相同了。
    切換分支或克隆的過程也類似。 當檢出一個分支時,它會修改 HEAD 指向新的分支引用,
    將 索引 填充為該次提交的快照,然后將 索引 的內容復制到 工作目錄 中。

git工作詳細介紹

git重置git reset,代碼回退操作介紹

1. 只移動HEAD(相當于取消上一次提交):git reset --soft HEAD~git reset --soft 99ad0ec

reset做的第一件事是移動HEAD的指向。reset移動HEAD指向的分支。
reset --soft本質上是撤銷了上一次git commit命令。 當你在運行git commit時,Git會創建一個新的提交,
并移動HEAD所指向的分支來使其指向該提交。當你將它resetHEAD~HEAD 的父結點)時,
其實就是把該分支移動回原來的位置,而不會改變索引和工作目錄。
現在你可以更新索引并再次運行git commit來完成git commit --amend所要做的事情了。

2. 移動HEAD,更新index: git reset HEAD~git reset --mixed HEAD~

它依然會撤銷一上次 提交,但還會 取消暫存 所有的東西。 于是,我們回滾到了所有git addgit commit的命令執行之前。

3. 移動HEAD,更新index,更新工作目錄(working Directory): git reset --hard HEAD~

必須注意,--hard標記是reset命令唯一的危險用法,它也是Git會真正地銷毀數據的僅有的幾個操作之一。
其他任何形式的reset調用都可以輕松撤消,但是--hard選項不能,因為它強制覆蓋了工作目錄中的文件。
在這種特殊情況下,我們的Git數據庫中的一個提交內還留有該文件的v3版本,我們可以通過reflog來找回它。
但是若該文件還未提交,Git仍會覆蓋它從而導致無法恢復。

reset命令會以特定的順序重寫這三棵樹,在你指定以下選項時停止:

  1. 移動 HEAD 分支的指向 (若指定了 --soft,則到此停止)

  2. 使索引看起來像 HEAD (若未指定 --hard,則到此停止)

  3. 使工作目錄看起來像索引

4. 使用git reflog命令來查看所有已經提交過的commit

5. 通過路徑來重置:git reset file.txt

假如我們運行git reset file.txt(這其實是git reset --mixed HEAD file.txt的簡寫形式,
因為你既沒有指定一個提交的SHA-1或分支,也沒有指定--soft--hard),它會:

  1. 移動HEAD分支的指向 (已跳過)

  2. 讓索引看起來像HEAD(到此處停止)

所以它本質上只是將file.txtHEAD復制到索引中。
它還有取消暫存文件的實際效果。
我們可以不讓Git從HEAD拉取數據,而是通過具體指定一個提交來拉取該文件的對應版本。
我們只需運行類似于git reset eb43bf file.txt的命令即可。

git checkout 介紹

切換分支:git checkout [分支名]

運行git checkout [branch]與運行git reset --hard [branch]非常相似,
它會更新所有三棵樹使其看起來像 [branch],不過有兩點重要的區別。

首先不同于reset --hardcheckout對工作目錄是安全的,它會通過檢查來確保不會將已更改的文件弄丟。
其實它還更聰明一些。它會在工作目錄中先試著簡單合并一下,這樣所有還未修改過的文件都會被更新。
reset --hard則會不做檢查就全面地替換所有東西。

第二個重要的區別是如何更新HEADreset會移動HEAD分支的指向,而checkout只會移動HEAD自身來指向另一個分支。

例如,假設我們有masterdevelop分支,它們分別指向不同的提交;我們現在在develop上(所以HEAD指向它)。
如果我們運行git reset master,那么develop自身現在會和master指向同一個提交。
而如果我們運行git checkout master的話,develop不會移動,HEAD自身會移動。 現在HEAD將會指向master

所以,雖然在這兩種情況下我們都移動HEAD使其指向了提交 A,但做法是非常不同的。
reset 會移動HEAD分支的指向,而checkout則移動HEAD自身。

放棄index與working Directory的改動:git checkout [分支名] file.txt

該命令不會移動HEAD,只會把HEAD中的代碼恢復到index中,同時把工作目錄文件也恢復到HEAD中代碼的樣子。

值放棄修改working Directory 工作目錄中的修改:git checkout file.txt放棄單個修改或使用git checkout .放棄所有修改

這個命令只會放棄工作目錄中的修改,已經提交到index中的修改則不會改動。

reset\checkout命令詳細介紹

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容

  • 一、 Git 分支簡介 幾乎所有的版本控制系統都以某種形式支持分支。 使用分支意味著你可以把你的工作從開發主線上分...
    常大鵬閱讀 2,971評論 2 41
  • git作為時下最流行的代碼管理工具,Git權威指南總結了十條喜歡Git的理由: 異地協同工作; 現場版本控制; 重...
    古斟布衣閱讀 1,836評論 0 12
  • (根據我的親身經歷改編) 我姓末,單名一個云字。 我與世無爭,人畜無害,目前只是一家星級酒店的男服務生而已。 這回...
    Myron馬閱讀 516評論 4 5
  • 全社會都在越來越多的倡導家庭教育的重要性,很多家長朋友們就開始困惑了,既然家庭教育這么重要,那如何開啟科學的家庭教...
    智慧愛家長學院閱讀 485評論 0 0
  • 前言 這本書和Objective-C高級編程-iOS和OS X多線程和內存管理實在是iOS開發人員必讀書. 實在是...
    Jerry4me閱讀 2,646評論 8 41