一、電腦本地初始化一個倉庫
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 倉庫。
shortname
是url
的簡寫,當上傳代碼的時候,可用這個簡寫代替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 [分支名] [遠程倉庫名]/[分支名]
例如,本地就一個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 的格式規范如下:
- 所有空行或者以 # 開頭的行都會被 Git 忽略。
- 可以使用標準的 glob 模式匹配。
- 匹配模式可以以(/)開頭防止遞歸。
- 匹配模式可以以(/)結尾指定目錄。
- 要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(!)取反。
一個 .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 --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 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分支
1. 分支介紹作用
Git 的分支,其實本質上僅僅是指向提交對象的可變指針。 Git 的默認分支名字是 master。
在多次提交操作之后,你其實已經有一個指向最后那個提交對象的 master 分支。 它會在每次的提交操作中自動向前移動。
分支作用:
- 開發多個項目任務,比如說我有兩個任務都比較緊急,任務1需要兩天完成,任務2需要一天完成,而任務1是之前就已經開始進行的,任務二是中間加的新任務,所以需要第一天就完成任務2.
- master分支始終要保證可發布的狀態,用dev分支和bug分支進行開發和錯誤調試,這樣能夠保證主干代碼的干凈、可發布。
- 自己開發測試或者修復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 將合并的結果做了一個新的快照并且自動創建一個新的提交指向它。
③ 合并過程中代碼有沖突,解決沖突
錯誤信息類似于:
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 add
或git rm
然后git commit --amend
如果你已經完成提交,又因為之前提交時忘記添加一個新創建的文件,想通過添加或修改文件來更改提交的快照,
也可以通過類似的操作來完成。 通過修改文件然后運行git add
或git 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~3
或git 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-filte
r選項:
$ 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-file
與ls-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 | |
| | +--------------------> | |
| | | | |
+-------------------------------------------------------------------+
假設我們進入到一個新目錄,其中有一個文件。 我們稱其為該文件的 v1 版本,將它標記為藍色。
現在運行git init
,這會創建一個 Git 倉庫,其中的 HEAD 引用指向未創建的分支(master 還不存在)。此時,只有工作目錄有內容。 現在我們想要提交這個文件,所以用
git add
來獲取工作目錄中的內容,并將其復制到索引(Index)中。接著運行
git commit
,它首先會移除索引中的內容并將它保存為一個永久的快照,
然后創建一個指向該快照的提交對象,最后更新 master 來指向本次提交。此時如果我們運行
git status
,會發現沒有任何改動,因為現在三棵樹完全相同。
現在我們想要對文件進行修改然后提交它。 我們將會經歷同樣的過程;首先在工作目錄中修改文件。
我們稱其為該文件的 v2 版本,并將它標記為紅色。如果現在運行 git status,我們會看到文件顯示在 “Changes not staged for commit,” 下面并被標記為紅色,
因為該條目在索引與工作目錄之間存在不同。 接著我們運行git add
來將它暫存到索引中。此時,由于索引和 HEAD 不同,若運行
git status
的話就會看到 “Changes to be committed” 下的該文件變為綠色.
也就是說,現在預期的下一次提交與上一次提交不同。 最后,我們運行git commit
來完成提交。現在運行
git status
會沒有輸出,因為三棵樹又變得相同了。
切換分支或克隆的過程也類似。 當檢出一個分支時,它會修改 HEAD 指向新的分支引用,
將 索引 填充為該次提交的快照,然后將 索引 的內容復制到 工作目錄 中。
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
所指向的分支來使其指向該提交。當你將它reset
回HEAD~
HEAD 的父結點)時,
其實就是把該分支移動回原來的位置,而不會改變索引和工作目錄。
現在你可以更新索引并再次運行git commit
來完成git commit --amend
所要做的事情了。
2. 移動HEAD,更新index: git reset HEAD~
或git reset --mixed HEAD~
它依然會撤銷一上次 提交,但還會 取消暫存 所有的東西。 于是,我們回滾到了所有git add
和git commit
的命令執行之前。
3. 移動HEAD,更新index,更新工作目錄(working Directory): git reset --hard HEAD~
必須注意,--hard
標記是reset
命令唯一的危險用法,它也是Git
會真正地銷毀數據的僅有的幾個操作之一。
其他任何形式的reset
調用都可以輕松撤消,但是--hard
選項不能,因為它強制覆蓋了工作目錄中的文件。
在這種特殊情況下,我們的Git
數據庫中的一個提交內還留有該文件的v3
版本,我們可以通過reflog
來找回它。
但是若該文件還未提交,Git
仍會覆蓋它從而導致無法恢復。
reset
命令會以特定的順序重寫這三棵樹,在你指定以下選項時停止:
移動 HEAD 分支的指向 (若指定了 --soft,則到此停止)
使索引看起來像 HEAD (若未指定 --hard,則到此停止)
使工作目錄看起來像索引
4. 使用git reflog
命令來查看所有已經提交過的commit
5. 通過路徑來重置:git reset file.txt
假如我們運行git reset file.txt
(這其實是git reset --mixed HEAD file.txt
的簡寫形式,
因為你既沒有指定一個提交的SHA-1
或分支,也沒有指定--soft
或--hard
),它會:
移動
HEAD
分支的指向 (已跳過)讓索引看起來像
HEAD
(到此處停止)
所以它本質上只是將file.txt
從HEAD
復制到索引中。
它還有取消暫存文件的實際效果。
我們可以不讓Git從HEAD拉取數據,而是通過具體指定一個提交來拉取該文件的對應版本。
我們只需運行類似于git reset eb43bf file.txt
的命令即可。
git checkout
介紹
切換分支:git checkout [分支名]
運行git checkout [branch]
與運行git reset --hard [branch]
非常相似,
它會更新所有三棵樹使其看起來像 [branch],不過有兩點重要的區別。
首先不同于reset --hard
,checkout
對工作目錄是安全的,它會通過檢查來確保不會將已更改的文件弄丟。
其實它還更聰明一些。它會在工作目錄中先試著簡單合并一下,這樣所有還未修改過的
文件都會被更新。
而reset --hard
則會不做檢查就全面地替換所有東西。
第二個重要的區別是如何更新HEAD
。reset
會移動HEAD
分支的指向,而checkout
只會移動HEAD
自身來指向另一個分支。
例如,假設我們有master
和develop
分支,它們分別指向不同的提交;我們現在在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中的修改則不會改動。