Git項目協同開發學習筆記1:項目庫開發基礎git命令

這年頭git基本都是項目開發的標配,以前恰好碰到了就花了兩天時間系統學習了下。本文內容基原本自如下tutorial:Learn Git(建議直接去看原文,由於這個網站是有更新的)。這個是我看過對git進行版本控制和項目協做原理講解最清楚的文檔,就記下比較加深理解和記憶。css

1.git是啥

Git是一種分佈式版本控制系統(Distributed Version Control System),這是與以前流行的CVS,SVN之類的存在中央庫的系統明顯差別,即在git中每一個開發者的庫都是完整的。git相比以前的VCS具備如下性能更強也更靈活,更安全(SHA1哈希算法)。在以後的why git for your organization模塊裏面大力安利了一波git給包括開發者,市場,管理,設計師,客戶支持,人力資源一大波吃瓜羣衆。這裏就不細說了,能夠本身去看。html

2.安裝git

git在windows,linux,mac os平臺上均可用,所以無論你在哪一個平臺上進行開發,只要使用git能對源碼進行很好的管理。以ubuntu系統爲例:前端

$ sudo apt-get update
$ sudo apt-get install git

至於windows系統的話直接下載安裝包安裝便可。
通常在安裝完git以後會順便把本身的git帳號和郵箱設爲默認值,簡化之後的操做。linux

$ git config --global user.name "Emma Paris"    
$ git config --global user.email "eparis@atlassian.com"

3.1新建代碼庫--git init

git init命令用於創建新的庫。這既能夠將已經存在可是並未版本化的項目轉化爲git庫,也能夠新建一個新的空庫。所以這個命令是開始新項目的時候的第一個git命令,而且一個項目這個命令只須要一次(以後會用git clone將現存的庫複製到本機的庫中)。執行這個命令之後在項目的根目錄下會產生一個.git的子目錄,這裏包含了一切關於這個庫元數據(metadata)。好比:git

$ git init <directory>

這個命令將會在指定的目下下新建一個git庫,這個文件夾下面除了.git沒有別的內容。github

$ git init --bare <directory>

而指定--bare參數則會新建一個空的git庫可是省略了工做目錄(沒有.git文件夾,本來git中的內容直接在新產生的文件夾中,所以通常文件名中會自帶.git後綴,好比my-project.git)。共享庫通常都是用帶--bare標籤新建的。
爲何共享庫須要用--bare創建
對於須要共享的中央庫,若是庫是non-bare的,那麼在push branch時可能會覆蓋庫中在本機上作的一些修改;而若是時bare的,由於沒有工做目錄,因此不可能在庫中編輯文件以及commit(以後會講這個git的核心命令),也就不存在這個問題。因此通常中央庫時bare的,而各開發者的本地庫時non-bare的。
正則表達式

3.2複製代碼庫--git clone

以前也提到了,這個命令用於複製現存的庫。複製後的庫的操做將徹底獨立於原來的庫,而在複製的同時會自動產生一個名爲origin的遠程連接指向原來的庫,方便之後兩個庫之間的一些交互操做。於git init相似,這個命令在一個項目中也通常只須要執行一次。算法

$ git clone <repo> # 指定將庫複製到當前文件夾
$ git clone <repo> <directory> # 將指定庫複製到指定文件夾
$ git clone ssh://john@example.com/path/to/my-project.git # john是當前開發者的用戶名

原始庫能夠在本地也能夠在遠程機器上經過http或者ssh連接獲取。(ssh的端口可能會被某些防火牆block,因此通常會用http;可是用ssh來鏈接github時不用輸入密碼,更方便一些)。
git的項目協做模式:每一個開發者的庫都是徹底的庫,不須要像SVN那樣經過中間庫而互相之間能夠push和pull commit。
shell

3.3配置代碼庫--git config

這個命令可讓你直接經過命令行配置當前庫的一些參數。ubuntu

$ git config user.name <name> # 定義當前庫全部commit所用的用戶名
$ git config --global user.name <name> # 定義當前用戶全部commit使用的用戶名
$ git config --global user.email <email> # 定義當前用戶全部commit使用的email
$ git config --global alias.<alias-name> <git-command> # 建立git命令別稱(懶無止境)
$ git config --system core.editor <editor> # 定義文本編輯器來給當前機器用戶調用git commit之類的命令時使用
$ git config --global --edit # 直接用文本編輯器打開全局配置文件

PS:這些參數也能夠在一下文件中找到:

  • (repo)/.git/config文件儲存當前庫的設置
  • ~/.gitconfig 保存當前用戶的設置。這些設置須要用--global標籤才能更改。
  • $(prefix)/etc/gitconfig儲存當前系統的設置(沒找到,不過感受通常也用不到)
    而當各級配置出現衝突的時候,庫本地設置會覆蓋用戶設置,用戶設置能覆蓋系統設置。

4.保存修改--git add & git commit

項目開發必然包含是編輯-階段緩存-提交這一流程。git add命令將當前工做目錄中的一些改動保存到緩存區域,在正式提交(git commit)以前並不會影響庫中的是實質內容(至關因而工做目錄和項目歷史之間的緩存區)。所以這一命令的使用很是常態化,每編輯修改一個或幾個互相相關文件就能夠git add,而後用git commit生成一個高度相關的快照。

$ git add <file> # 緩存全部指定文件中的變更
$ git add <directory> # 緩存指定文件夾中的變更
$ git add -p # 開啓交互式緩存環節容許選擇文件中具體那些變更存入緩存區

最後一個命令的操做方式在開啓交互環境的時候會展現:y:緩存當前變更塊;n:忽略當前變更塊;s:將當前變更塊分的更小;e:手動編輯變更塊;q:退出交互環境。

而git commit命令則是提交緩存區中的快照到項目歷史中,每個提交的快照均可以認爲是項目的一個安全版本。除了顯式命令以外,git是不會主動修改任何一個提交的快照。

$ git commit # 打開文本編輯器讓你輸入本次提交的快照的描述信息
$ git commit -m <message> # 直接輸入的字符串做爲提交的描述信息
$ git commit -a # 提交工做目錄下全部改動的快照。注意:這裏只包括以前用git add追蹤過的文件

在git中commit的快照是保存在本地庫中的,於以前的SVN保存在中央庫這一點是明顯的差別。與git add的緩存區相似,能夠將本地庫視爲要編輯的代碼和中央庫之間的緩衝區。這種方式帶來的優點有不少:好比能夠將一次功能更新分解成多個commit,讓相關的commit聚在一塊兒,方便提交中央庫以前對本地的commit歷史進行整理。
git的一大核心特徵就是commit的是快照而非SVN中的差別。git每次commit都將文件的全部內容都存快照中;而SVN每次保存的是文件中改動的地方。所以git對各commit快照的操做不須要整合之類的步驟,直接對目標commit進行操做便可。

具體操做實例:

$ git add hello.py
$ git commit
# 在編輯器中輸入如下信息
#Change the message displayed by hello.py
#
#- Update the sayHello() function to output the user's name
#- Change the sayGoodbye() function to a friendlier message

通常輸入信息的格式爲第一行是不超過50各字母的總結,空一行,而後是具體改動的說明。

5.1 隱藏改動--git stash

若是當你一個功能開發到一半忽然有一個緊急任務要立刻作(碼農平常),那麼這個時候就須要用git stash這個命令把上次commit到目前的改動先隱藏起來使其不影響別的開發任務,等到你完成以後調出這個隱藏部分而後繼續開發直至下次commit爲止。

$ git status    # 顯示上次commit與當前狀態下的有差別文件路徑(有改動的,新添加的,以及未被追蹤的)
On branch master        # 有改動的分支
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage #保存stash到本地,將HEAD指向最近一次commit
HEAD is now at 5002d47 our new homepage

$ git status # 這種狀態下就能夠進行隨意操做-好比新建commit,切換branch等,以後有須要再切回到stash的分支繼續操做便可
On branch master
nothing to commit, working tree clean

$ git stash pop # 彈出以前最近的stash
On branch master
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

$git stash apply # 與pop相似,只是這邊彈出的stash只是一份拷貝。所以能夠將這個stash用於多個分支。
On branch master
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

在默認狀況下,git stash只會stash已經在緩存區域的變更(staged changes)和正在被git追蹤的的文件中的變更(Unstaged changes),並不會stash工做副本下unstaged文件和被git定義忽略的文件(以後會在gitigore部分有介紹)。因此若是在以前的栗子中添加一個新文件可是沒有用git add命令stage,那麼這個就是untracked文件不會被stash。可是若是在stash命令中加入-u(--include-untracked)標籤,那麼未被追蹤的文件也會被stash;而用-a(--all)標籤則會把被git忽略的文件也stash。

$ script.js

$ git status
On branch master
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Untracked files:

    script.js
# ————————————————————————————————————————
$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch master
Untracked files:

    script.js
# -------------------------------------------------------------------------------
$ git stash -u
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

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

對多個stash進行操做
stash的使用次數並無限制,所以能夠有創建多個stash。默認狀態下stash被認爲是創建stash時分支和commit頂部的WIP(work in process),經過git stash list能夠查看全部的stash。若是stash個數比較多,能夠用git stash save在stash時添加註釋信息以便後續調用時查看。須要回到哪一個stash狀態的時候用git stash pop + stash id便可。

$ git stash list
stash@{0}: WIP on master: 5002d47 our new homepage # 5002d47是創建stash時最近的commmit id,master是stash時的分支名,stash@{0}就是stash的id
stash@{1}: WIP on master: 5002d47 our new homepage
stash@{2}: WIP on master: 5002d47 our new homepage

$ git stash save "add style to our site"
Saved working directory and index state On master: add style to our site
HEAD is now at 5002d47 our new homepage

$ git stash list
stash@{0}: On master: add style to our site
stash@{1}: WIP on master: 5002d47 our new homepage
stash@{2}: WIP on master: 5002d47 our new homepage

$ git stash pop stash@{2}

在不肯定stash到底作了哪些改動的時候能夠用git stash show來查看具體改動信息。

$ git stash show # 差別彙總
 index.html | 1 +
 style.css | 3 +++
 2 files changed, 4 insertions(+)
$ git stash -p # -p/--patch標籤用於顯示具體差別
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+  text-decoration: blink;
+}
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>

部分stash
用git stash -p/--patch命令將會迭代文件中的每一處改動塊來決定是否要stash這個部分。

$ git stash -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+  text-decoration: blink;
+}
Stash this hunk [y,n,q,a,d,/,e,?]? y
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>
Stash this hunk [y,n,q,a,d,/,e,?]? n

操做模式與以前的git add相同。

從stash處建立新分支
若是當前分支的變更偏離了stash中的變更,那麼在調用stash時代時候就極可能產生衝突。這個時候就須要用git stash branch建立新的分支來加載stash中的變更。

$ git stash branch add-style stash@{1} # 從創建stash時的commit處創建並切換到新的分支add-style,而後載入stash中的變更。
Switched to a new branch 'add-stylesheet'
On branch add-stylesheet
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{1} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

刪除stash
若是以前儲存的stash不要了能夠用git stash drop/clear來清楚。

git stash drop stash@{1} # 刪除stash@{1}分支
Dropped stash@{1} (17e2697fd8251df6163117cb3d58c1f62a5e7cdb)
git stash clear # 刪除全部stash

5.2 git stash工做原理

stash實際上是特殊的commit。.git/refs/stash中儲存了一個特殊的標籤指向最近新建的stash,而以前創建的stash則是經過當前stash標籤的引用日誌(reflog)進行指向。因此當指向stash@{n}的時候實際上是指向了當前stash標籤的第n條引用日誌。而當stash命令執行的時候,會根據狀況新建2或3條commit,同時.git/refs/stash文件中會更新一個指針用於指向新創建的commit。此外因爲是commit,因此能夠經過git log(後面會講)來查看stash的歷史記錄。

$ git log --oneline --graph stash@{0} 
*-. 953ddde WIP on master: 5002d47 our new homepage # 新的commit用於存儲當前工做目錄下被追蹤的文件
|\ \ 
| | * 24b35a1 untracked files on master: 5002d47 our new homepage # 新的commit用於存儲當前工做目錄下未追蹤的文件(這個commit只有在工做目錄下存在未追蹤的文件,同時--include-untracked或--all標籤也同時使用的時候纔會被創建)
| * 7023dd4 index on master: 5002d47 our new homepage # 新的commit用於存儲緩存區(git add)
|/ * 5002d47 our new homepage # HEAD指針指向的以前存在的最近的一次commit

在stash以前工做目錄的狀態通常以下所示:

而當stash命令執後的狀態以下:

帶--include-untracked標籤

帶--all標籤

6. 忽略文件配置--.gitignore文件

git把工做目錄下的文件分爲3類:tracked(追蹤的)-以前有stage(git add)或者commit記錄的文件;untracked(未追蹤的)-還沒有stage和commit的文件;ignored(忽略的)-被git特地忽略的文件。通常被忽略的文件包括第三方文件(各類包),源文件可以產生的文件或文件夾,編譯後的文件(.pyc),運行過程當中產生的日誌文件(.log),隱藏的系統文件(.DB_store),我的的IDE配置文件等。而這些被忽略的文件的模式則是存儲在.gitignore文件中。.gitignore文件能夠存在多個,發生衝突的時候本地文件夾的.gitignore的配置會覆蓋庫根目錄下的,根目錄下的則會覆蓋系統層面的,所以爲了方便期間通常都是隻在庫的根目錄下放一個。忽略文件的模式採用正則表達式規則,具體能夠看這裏,下面舉幾個經常使用的栗子:

**/logs # 當前庫下全部名爲logs目錄下的文件(雙*用於匹配當前庫下面的任何文件夾),好比logs/debug.log,/build/logs/foo.log
*.log # 當前庫下全部log後綴的文件,好比logs/debug.log, foo.log
!important.log # 除此之外的全部文件,與前一命令一塊兒使用時則是表示除了important.log文件之外的全部log文件都會被忽略

須要注意的時.gitignore文件位於根目錄下,所以能夠被stage和commit到版本迭代中。若是隻是想定義本身所需的忽略文件並不想commit到代碼庫中,能夠將忽略規則添加到.git/info/exclude文件中,這個文件不會被stage,所以避免了可能被commit到公共庫的狀況。
若是須要將本機下全部庫都設置相同的忽略規則,那麼就能夠用$ git config --global core.excludesFile .gitignore命令,而這同時也就不須要考慮把.gitignore 文件放在哪的問題了。
若是須要忽視以前commit的文件,須要用git rm命令將文件重庫中刪除而後再添加相應規則到.gitignore文件中。

$ echo debug.log >> .gitignore
$ git rm --cached debug.log # --cached表示只刪除庫裏的文件而保留工做目錄下的。若是不用則兩個地方都會被刪掉
rm 'debug.log'
$ git commit -m "Start ignoring debug.log"

若是須要commit被忽略的文件,能夠在git add的時候加上-f/--force標籤;也能夠用以前提到的!來指定進行例外文件。

$ cat .gitignore
*.log
$ git add -f debug.log
$ echo !debug.log >> .gitignore
#-----------------------------
$ cat .gitignore
*.log
!debug.log
$ git add debug.log

若是須要stash被忽略的文件,如以前所說,須要在git stash時添加--all標籤。
而當你不肯定某個文件因爲哪一個規則被忽略時,能夠用git check-ignore命令查看忽略文件的規則。

$ git check-ignore -v debug.log
.gitignore:3:*.log debug.log # 格式未忽略文件的規則:規則所在行;規則;忽略的文件

7.1 查看工做目錄和緩存區狀態--git status

工做目錄和緩存區的狀態包括staged變更(存到緩存區),還未staged的變更以及還沒有被追蹤的文件,而那些被忽略的文件信息並不會有所顯示。因此git status的輸出通常以下:

# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
#modified: hello.py
#
# 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: main.py
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
#hello.pyc

在commit以前及時git status能夠防止一些偶然操做致使commit一些想要的變更。

7.2 產看commit歷史記錄--git log


git log只能用來查看commit的歷史記錄,而後選擇須要炒做的commit快照。

$ git log #以默認格式輸出commit歷史
$ git log -n <limit> # 限制輸出最近<limit>個commit
$ git log --oneline # 將每個commit的信息壓縮到一行中顯示,很方便查看大量commit
$ git log --stat # 增長顯示具體變更的文件和每一個文件增減的行數
$ git log -p # -p/--patch具體顯示每一個commit的改動信息
$ git log --author="<pattern>" # 顯示某位開發者的commit,匹配信息能夠是字符串也能夠是正則表達式
$ git log --grep="<pattern>" # 顯示某種模式的commit,匹配信息能夠是字符串也能夠是正則表達式。
$ git log <since>..<until> # 顯示從since到until之間的commit,能夠是commit ID,分支名稱(master..some-brance),HEAD指針(HEAD~2:當前commit的往前數第二個commit)
$ git log <file> # 顯示某個文件相關的commit
$ git log --graph # 用文字圖的形式顯示commit
$ git log --decorate # 增長顯示commit的分支名或者標籤名

注意commit ID爲40爲的SHA1檢驗和(好比3157ee3718e180a9476bf2e5cab8e3f1e78a73b7),這樣用於確保ID的惟一性和檢驗commit的完整性。

8.消除變更--git checkout/git revert/git reset/git clean

這些命令都能用來對歷史commit進行處理,可是原理和效果都不太相同。
git checkout
這個命令的對象能夠有3種:文件,commit,分支(checkout分支主要能夠是用來切換分支用,在以後有具體介紹在這裏就先跳過)。checkout commit時使整個工做目錄都回歸到選擇的commit的狀態且並不對目前的狀態有任何影響;而checkout文件則是將具體文件回到特定commit時的狀態而不影響工做目錄下的其餘文件。要注意的時checkout commit命令只是看一個只讀操做,在查看舊版本或者以前的commit的時候不會對當前庫有任何不良影響,所以是一個十分安全的操做。

$ git checkout <commit> <file>

commit能夠用commit哈希碼或者標籤(tag)做爲指定變量,但此時HEAD指針將會處於分離的狀態(在正常狀態時HEAD指針是指向某一個分支的最前端,而當checkout命令調用時,HEAD指針將會指向給定的commit)。

$ git log --oneline # 假設當前工做分支爲master
b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.py
435b61d Create hello.py
9773e52 Initial import
$ git checkout a1e8fb5 # 切換到a1e8fb5的commit狀態,此時查看編譯甚至修改文件都不會對以前分支的狀態有任何影響
$ git checkout master # 切回以前的分支繼續開發

而當checkout的對象爲文件時,則會確實影響當前庫的狀態。當你從新commit到文件以前的版本時,也就將文件的狀態退回到了特定版本。

$ git checkout a1e8fb5 hello.py # 將hello.py文件的狀態切換到a1e8fb5 commit狀態,文件確實發生改動
$ git checkout HEAD hello.py # 將hello.py文件狀態切回到最近的狀態

git revert
git revert回到以前的狀態的工做方式是新建一個commit而後把須要迴歸的commit的狀態快照複製到這個commit。這樣作的好處就是以前commit的歷史記錄仍舊完整,使協做時不至於commit歷史記錄改變出現衝突。

# Edit some tracked files
# Commit a snapshot
$ git commit -m "Make some changes that will be undone"
# Revert the commit we just created
$ git revert HEAD # 回到了commit以前的狀態

git reset
相比於git revert能保存commit歷史的這種相對安全的方法,git set的工做方式是指向要回歸的commit,並將如今的狀態到須要迴歸的commit之間全部的commit快照都刪除,所以調用git reset時候是直接改動了commit的歷史記錄,使用的時候(尤爲是御別人協做的時候)須要格外當心。通常狀況下只在本地庫中使用git reset命令,而避免在共享的公共庫上面進行這項操做。

$ git reset <file> # 移除緩存區中的特定文件,但並不會改變工做目錄的狀態。
$ git reset # 將緩存區狀態迴歸到最近一次的commit狀態,但不會改變工做目錄的狀態(也就是有機會git add回去以前的狀態)
$ git reset --hard # 將緩存區和工做目錄都回歸到以前commit的狀態(消去上次commit以後的全部變更)
$ git reset <commit> # 將緩存區狀態迴歸到指定的commit狀態,但並不改變工做目錄的狀態
$ git reset --hard <commit> # 將緩存區和工做目錄都回歸到特定commit的狀態(消去指定commit以後發生的全部變更)

基於git reset 和git revert的操做方式,通常前者用於本地的撤銷操做,然後者用於共享的commit的撤銷操做。

若是在公共庫中進行reset操做就會出現如下情況,git會認爲你新建了分支,須要用merge操做進行整合。

下面兩個栗子講如下git reset最多見的用途:
1.刪除緩衝區的特定文件(git add的時候不當心加錯了)

# Edit both hello.py and main.py

# Stage everything in the current directory
$ git add .

# Realize that the changes in hello.py and main.py
# should be committed in different snapshots

# Unstage main.py
$ git reset main.py

# Commit only hello.py
$ git commit -m "Make some changes to hello.py"

# Commit main.py in a separate snapshot
$ git add main.py
$ git commit -m "Edit main.py"

2.刪除本地commit歷史(掩蓋本身曾經作過的一些蠢到沒朋友的一些測試)

# Create a new file called `foo.py` and add some code to it

# Commit it to the project history
$ git add foo.py
$ git commit -m "Start developing a crazy feature"

# Edit `foo.py` again and change some other tracked files, too

# Commit another snapshot
$ git commit -a -m "Continue my crazy feature"

# Decide to scrap the feature and remove the associated commits
$ git reset --hard HEAD~2 # 迴歸開發crazy feature以前的commit狀態,並銷燬痕跡

git clean
git clean會移除工做目錄下未被追蹤的文件(避免了用git status查看狀態來一一確認須要刪除的未追蹤文件,很是方便)。須要主要的是這個命令跟rm命令同樣是不可撤銷的,使用的時候須要考慮清楚。而git clean命令也常常御git reset --hard命令一塊兒使用,由於後者能只能對追蹤的文件進行操做,二者一塊兒使用可以確保搞那個錢工做目錄回到特定的版本狀態,這在項目編譯後須要把編譯文件去掉進行打包操做時特別有用。

$ git clean -n # 顯示會被清除的文件,但並無實際運行。(方便確認)
$ git clean -f # 刪除爲追蹤的文件(除非把用git config --global 命令把clean.requireForce設置爲false,-f是必須加的),但並不會刪除未追蹤的文件夾以及忽略的文件
$ git clean -f <path> # 刪除指定文件夾下的未追蹤文件
$ git clean -df # 刪除未追蹤的文件和文件夾
$ git clean -xf # 刪除當前目錄下未追蹤和忽略的文件

9.重寫歷史--git commit --amend/git rebase/git rebase -i /git reflog

與以前的刪除操做相似,git也提供了一些修改commit歷史的命令,雖然這可能會致使一些內容的損失。
git commit --amned
這個命令直接用新的commit快照來替換以前的commit(注意時替換而非修改),這讓咱們可以很方便的修正一些有問題的commit快照。

正如git reset同樣,這個命令也不要用來修正已經共享的commit,由於這至關於以前的commit被刪掉了,若是別人的新功能依賴以前共享的commit那就很容易產生衝突。

# Edit hello.py and main.py
$ git add hello.py
$ git commit

# Realize you forgot to add the changes from main.py
$ git add main.py
$ git commit --amend --no-edit # 不改變以前的commit的信息,在忘了添加文件到緩存區的時候特別有用。

git rebase
這個命令是把某個分支以新的commit的形式整個移到另外一個分支的最前端。因爲有新建commit的操做,也就對分支的歷史有所改動。效果以下:

git rebase <base> # 對象能夠是分支ID,標籤(tag),HEAD指針

這個命令主要用於保持一個線性的項目開發歷史。當你須要把一個分支的新功能merge到master分支上,用rebase命令產生的歷史就會如上圖所示保持線性,之後查看的時候很是清晰。rebase也是把上游公共庫的變更整合到本地庫的常見作法(直接用merge可能會產生打亂原有commit的順序,當你查看的時候可能會不明因此),可是與以前git commit --amend 和git reset命令同樣,git rebase命令也須要避免在已經共享的commit上面。

# Start a new feature
$ git checkout -b new-feature master # 從master分支新建並切換到該分支
# Edit files
$ git commit -a -m "Start developing a feature"
# Create a hotfix branch based off of master
$ git checkout -b hotfix master # 發現有個問題須要hotfix下,就從master新建了hotfix分支
# Edit files
$ git commit -a -m "Fix security hole"
# Merge back into master
$ git checkout master 
$ git merge hotfix # 修復完成後把hotfix分支整合到master分支
$ git branch -d hotfix # 刪除hotfix分支
$ git checkout new-feature # 切換到new-feature分支
$ git rebase master # 把new-feature分支移動到master分支的最前端保持線性歷史(由於以前插入了hotfix的commit,若是這屆合併的話new-featrue的歷史就會在hotfi以前了)
$ git checkout master
$ git merge new-feature

git rebase -i
-i標籤是指進行交互式的rebase操做,能夠對commit歷史進行分割,改動,刪除等操做,而不是一股腦把全部commit都移動到一個分支最前端。這就給了開發者很大的自由,在開發的時候在本機上能夠隨便commit,只要在提交公共庫以前用這個命令把commit調整好、去掉過期的commit、保持每一個commit都是有意義的(裝逼神器)。

# Start a new feature
$ git checkout -b new-feature master
# Edit files
$ git commit -a -m "Start developing a feature"
# Edit more files
$ git commit -a -m "Fix something from the previous commit"

# Add a commit directly to master
$ git checkout master
# Edit files
$ git commit -a -m "Fix security hole"

# Begin an interactive rebasing session
$ git checkout new-feature
$ git rebase -i master
# 在new-feature分支上有兩個commit
pick 32618c4 Start developing a feature # pick是命令,若是須要執行別的操做用別的命令便可
pick 62eed47 Fix something from the previous commit

pick 32618c4 Start developing a feature
squash 62eed47 Fix something from the previous commit # squash命令即表示這個commit在rebase的時候會被去掉(在git log的時候能夠查看到)
# 保存關閉編輯器後開始rebase
$ git checkout master
$ git merge new-feature

git reflog
git用名爲reflog的機制追蹤每一個分支最前端的更新狀態,所以每當HEAD指針的狀態有變化(好比切換分支,pull變更,重寫歷史,增長commit)就有新的條目被加到reflog中。在重寫歷史之後,reflog包含了關於分支的舊狀態,讓你在須要的時候回到這些狀態。注意的是git reflog只是8追蹤狀態變動操做。

$ git reflog # 查看本地庫的reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2 # 最近的活動
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2
$ git reflog --relative-date # 增長顯示reflog的相對日期信息(好比2 days ago)
$ git reset --hard 0254ea7 # 使用git reset命令就能夠回到以前的commit狀態

到這裏爲止就是一些在git上創建項目,對項目進行基本的開發操做所需的最多見的命令。下一步就須要講git進行項目協做時所須要的一些經常使用命令。

相關文章
相關標籤/搜索