概述:在利用git進行管理的時候,除了對不同的分支進行merge以外,往往需要對同一個一個分支上的不同commit進行合并或者撤銷;或者對不同分支上的多次送出進行合并,形成一個線性的送出曆史,等等:這些都要用到git rebase,git reset和git log這三個指令。
1.commit的資訊查詢——git log
如果我們在同一個分支上進行了多次commit,要想查找每次commit的資訊,需要使用git log指令
$git log
commit daffbd39053d4f2e78f88955c2158de6092258a2
Author: hyk
Date: Wed May 29 22:45:23 2013 +0800
this is a test
commit 38d58591fc0607bfd262fc8d363e52047fcf0e9c
Author: Eryu Guan
Date: Wed May 29 08:12:44 2013 -0500
xfstests: fix typo in check
There is no $testname, should be $test_name
Signed-off-by: Eryu Guan
Reviewed-by: Rich Johnston
Signed-off-by: Rich Johnston
如上,commit按照時間的遠近排列,最新的修改在最前面
使用以下兩個指令,可以檢視commit的詳細資訊
$git log --graph
$git log -p
2.commit點的撤銷與分解——git reset
2.1指令概述:
1)指令解釋:
git reset:Reset current HEAD to the specified state
2)指令模式:
git reset [-q] [] [--] ...
git reset (--patch | -p) [] [--] [...]
git reset (--soft | --mixed | --hard | --merge | --keep) [-q] []
3)模式詳解:
上面的第一種和第二種形式中,直接從commit中copy條目到index,進而實作index的回滾。第三種形式中,将目前分支的HEAD設定到commit,同時可以選擇是否對working tree和stage作相應比對的更改。其中,在所有的指令形式中,預設是HEAD。
git reset -- []
This form resets the current branch head to and possibly updates the index (resetting it to the tree
of ) and the working tree depending on , which must be one of the following:
--soft:僅僅更改本地倉庫中的送出曆史,修改HEAD所指向的commit
--hard:重置index和working tree。任何在目前目錄下的tracked檔案,如果在commit之後,都會被廢棄。
注明:如果不指定mode的話,那麼index也将會同步進行更新。這個操作用于實作對index中檔案的撤銷和更改。
2.2指令使用情景:
1)commit 節點的移動
現在有如圖所示的版本情況:
---------(1.1)-----------(1.2)--------(1.3)------------(1.4)
其中HEAD指向1.4。此時想要生成patch,如果直接運作git format-patch的話,将生成1.3~1.4的patch,如果不想要1.2和1.3的送出記錄,直接從實作1.1到1.4的修改,此時需要運作以下指令:
$git reset 1.1
此時,目前工作目錄下位1.4版本的檔案,1.2 ,1.3這兩個版本的修改曆史被丢棄。然後可以繼續 add然後commit
注意,git reset的意義是: git-reset - Reset current HEAD to the specified state,它改變的本地倉庫中的檔案和index中的記錄,并沒有改變目前工作目錄之下的檔案。運作這個指令以後,你可能得到如下的結果:
[email protected]:/home/hyk/software/ltp# git reset 4bdda040bc045dd8d921d54d3b54db5dabeb9958
Unstaged changes after reset:
Mtestcases/kernel/mem/tunable/max_map_count.c
$git add file1
$git commit
$git add file2
$git commit
2)撤銷add到staged中的檔案
情景:add了兩個檔案到index,但是覺得沒有修改好,現在需要撤銷add這個操作。
$ edit (1)
$ git add frotz.c filfre.c
$ mailx (2)
$ git reset (3)
$ git pull git://info.example.com/ nitfol (4)
3)撤銷一次送出,修改之後重做
相對于上個例子,更進一層,add完畢,而且已經commit,然後想要撤銷這次送出,重做以後再進行送出。
Undo a commit and redo
$ git commit ...
$ git reset --soft HEAD^ (1)
$ edit (2)
$ git commit -a -c ORIG_HEAD (3)
3.git rebase:合并不同分支上的送出,形成線性送出曆史
git rebase的說明:git-rebase - Forward-port local commits to the updated upstream head
rebase,顧名思義,rebase的意思就是改變某一個分支的base。本例中,就是将mywork這個分支的base從C2改動到C4
假設你現在基于遠端倉庫"origin",建立一個叫"mywork"的分支。
$ git checkout -b mywork origin
現在我們在這個分支做一些修改,然後生成兩個送出(commit).
$ vi file.txt
$ git commit
$ vi otherfile.txt
$ git commit
...
但是與此同時,有些人也在"origin"分支上做了一些修改并且做了送出了. 這就意味着"origin"和"mywork"這兩個分支各自"前進"了,它們之間"分叉"了。
在這裡,你可以用"pull"指令把"origin"分支上的修改拉下來并且和你的修改合并; 結果看起來就像一個新的"合并的送出"(merge commit):
但是,如果你想讓"mywork"分支曆史看起來像沒有經過任何合并一樣,你也許可以用 git
rebase:
$ git checkout mywork
$ git rebase origin
這些指令會把你的"mywork"分支裡的每個送出(commit)取消掉,并且把它們臨時 儲存為更新檔(patch)(這些更新檔放到".git/rebase"目錄中),然後把"mywork"分支更新 到最新的"origin"分支,最後把儲存的這些更新檔應用到"mywork"分支上。
當'mywork'分支更新之後,它會指向這些新建立的送出(commit),而那些老的送出會被丢棄。 如果運作垃圾收集指令(pruning garbage collection), 這些被丢棄的送出就會删除. (請檢視 git
gc)
現在我們可以看一下用合并(merge)和用rebase所産生的曆史的差別:
在rebase的過程中,也許會出現沖突(conflict). 在這種情況,Git會停止rebase并會讓你去解決 沖突;在解決完沖突後,用"git-add"指令去更新這些内容的索引(index), 然後,你無需執行 git-commit,隻要執行:
$ git rebase --continue
這樣git會繼續應用(apply)餘下的更新檔。
在任何時候,你可以用--abort參數來終止rebase的行動,并且"mywork" 分支會回到rebase開始前的狀态。
$ git rebase --abort
注意:如果C3 C5裡面改動的内容有沖突,那麼這個時候先要解決沖突,然後才能送出。