現在 style 是 based on dc39a81e (add some students),要改成 based on 64a00b7e (add their ages),也就是
<current base-commit> = dc39a81e
<new base-commit> = 64a00b7e
那就來試試看
git rebase --onto 64a00b7e dc39a81e
果然達到了目的,style 現在是 based on 64a00b7e 了(當然 commit IDs 也都不同了)。
conflict 的處理
接著改 style 的人修改了學生清單的樣式,可是他很機車,他要改 index.html 裡面的東西(實際情況是,list 裡寫了一個 table,但寫 css 總要有些 class 或 id 的 attributes 才能設定)。剛好改 list 的人也在他自己的 branch 裡面改,這時候,在 rebase 試著 re-apply commits 的過程中,必定會產生 conflict。
現在 list 要利用到 style 裡面修飾好的樣式,在這個情況下,就是把 list 給 rebase 到 style 上面,也就是在 list branch 做 git rebase style 。不過你會看到這個:
First, rewinding head to replay your work on top of it...
Applying: add gender column
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Failed to merge in the changes.
Patch failed at 0001 add gender column
When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".
跟預期的一樣出現了 conflict。當然,它會先試著自動 merge ,但如果改到的行有衝突,那就得要手動 merge 了,打開他說有衝突的檔案,改成正確的內容,接著使用 git add <file> (要把該檔案加進去 staging area,處理 rebase 的程式才能 commit),再 git rebase --continue 。
完成以後就會像這樣:
Interactive Mode: 偷天換日,自定重新 commit 的詳細步驟
接著 style 和 list 又陸續改了一些東西,主要是 list 裡面加了表單元件,而 style 則繼續修飾網頁整體設計。到了一個段落,該輪到 style 修飾 list 的表單了。目前的 commit history 長這樣:
不過在 style 要 rebase 到 list 上面之前,管 list 的人想把 list 上面的一些 commits 給整理過,因為他發現有這些問題:
"wrap the form with div" 太後面了,想移到前面
"fix typo of age field name" 跟 "add student id and age..." 可以合併
"add student id and age ..." 裡面東西太多,該拆成兩個
"form to add more *studetns*" 這 message 有錯字 “studetns”
"add gender select box" 裡面的程式碼有打錯字(囧
上面提到了 rebase 運作的方式是重新 commit 過一遍,那這個「重新 commit」的過程,能不能讓程式設計師來干預,達到偷天換日修改 commit 的目的呢?當然可以,只要利用 rebase 的 Interactive Mode。Git 的靈活就在這裡,連 commit 的內容都可以改。
如何啟動 interactive mode 呢?只要加入 -i 的參數就行了。以這個例子來說,list branch 是 based on 0580eab8 (fill in gender column) ,要從這個 commit 後面重新 apply 一次 commits ,也就是:
pick 2c97b26 form to add more studetns
pick fd19f8e add student id and age field into the form
pick 02849bf fix typo of age field name
pick bd73d4d wrap the form with div
pick 74d8a3d add gender select box
# Rebase 0580eab..74d8a3d onto 0580eab
# ...[chunked]
首先我想要把 "wrap the form with div" 移到 "form to add more studetns" 後面,然後 "form to add more studetns" 要改 commit message (有 typo),那就改成這樣:
git rebase -i
reword 2c97b26 form to add more studetns
pick bd73d4d wrap the form with div
pick fd19f8e add student id and age field into the form
pick 02849bf fix typo of age field name
pick 74d8a3d add gender select box
接著儲存檔案後把檔案關掉(如 vim 的 :wq),就開始執行 rebase 啦,遇到 reword 時會再跳出編輯器,讓你重新輸入 commit message 。這時我把 studetns 改正為 students ,然後就跟平常 commit 一樣,存檔並關掉檔案。
git commit
form to add more students
# Please enter the commit message for your changes. Lines starting
# ...[chunked]
完成後會看到:
Successfully rebased and updated refs/heads/list.
再看 commit history ,的確達到了目的,而且 list 這個 branch 一樣還是 based on 0580eab8 ,後面那些剛剛 rebase 過的 commits 統統換了 commit ID :
合併 commits
剩下這些要做:
"fix typo of age field name" 跟 "add student id and age..." 可以合併
pick c3cff8a form to add more students
pick 7e128b4 wrap the form with div
pick 0d450ea add student id and age field into the form
fixup 8f5899e fix typo of age field name
pick e323dbc add gender select bo
pick c3cff8a form to add more students
pick 7e128b4 wrap the form with div
pick 53616de add student id and age field into the form
edit c5b9ad8 add gender select box
最後是拆 commit 。怎麼拆呢?剛剛做了 edit ,不是停在該 commit 之後嗎?這時候就可以偷偷 reset 到 HEAD^ (即目前 HEAD 的**前一個**),等於是退回到 HEAD 指到的 commit 的前一個,於是該 commit 的 changes 就被倒出來了,變成 changed but not staged for commit ,再根據你的需求,把 changes 給一個一個 commit 就行了。
實際的操作如下。首先是用 edit 指令來編輯 commit 內容:
git rebase -i
pick c3cff8a form to add more students
pick 7e128b4 wrap the form with div
edit 53616de add student id and age field into the form
pick 4dbcf49 add gender select box
接著使用
git reset HEAD^
來把目前的 HEAD 指標給指到 HEAD 的前一個,指完之後,原本 HEAD commit 的內容就被倒出來,並且也不存在 stage area 裡面, git 會提示有哪些檔案現在處於 changed but not staged for commit:
Unstaged changes after reset:
M index.html
現在我可以一個一個 commit 了,原本是 add student id and age field ,我想拆成一次加 student id field ,一次加 age field 。commit 完成以後,再打
git rebase --continue
這次因為 staging area 裡面沒東西,所以就繼續 re-apply 剩下的 commits 。
現在打開 log 看,拆成兩個啦!
掌管 list branch 的人折騰完了,便告訴管 style 的說,可以 rebase 了,git 再度拯救了苦難程序員的一天。
Store the completion handler. The completion handler is invoked by the view controller’s checkForAllDownloadsHavingCompleted method (if all the download tasks have been completed).