GIT教程-動圖

Fast-forward (—ff)
在當(dāng)前分支相比于我們要合并的分支沒有額外的提交(commit)時,可以執(zhí)行 fast-forward 合并。
Git 很懶,首先會嘗試執(zhí)行最簡單的選項:fast-forward!這類合并不會創(chuàng)建新的提交,而是會將我們正在合并的分支上的提交直接合并到當(dāng)前分支。

No-fast-foward (—no-ff)
如果我們在當(dāng)前分支上提交想要合并的分支不具備的改變,那么git將會執(zhí)行 no-fast-forward 合并。
使用no-fast-forward合并時,Git會在當(dāng)前活動分支上創(chuàng)建新的 merging commit。
這個提交的父提交(parent commit)即指向這個活動分支,也指向我們想要合并的分支!

合并沖突
當(dāng)我們想要合并的兩個分支的同一文件中的同一行代碼上有不同的修改,或者一個分支刪除了一個文件而另一個分支修改了這個文件時,Git 就不知道如何取舍了。
在這樣的情況下,Git 會詢問你想要保留哪種選擇?假設(shè)在這兩個分支中,我們都編輯了 README.md 的第一行。

如果我們想把 dev 合并到 master,就會出現(xiàn)一個合并沖突:你想要標(biāo)題是 Hello! 還是 Hey!?
當(dāng)嘗試合并這些分支時,Git 會向你展示沖突出現(xiàn)的位置。我們可以手動移除我們不想保留的修改,保存這些修改,再次添加這個已修改的文件,然后提交這些修改。

變基(Rebasing)
將一個分支的修改融入到另一個分支的方式是執(zhí)行 git rebase。git rebase 會將當(dāng)前分支的提交復(fù)制到指定的分支之上。

變基與合并有一個重大的區(qū)別:Git 不會嘗試確定要保留或不保留哪些文件。我們執(zhí)行 rebase 的分支總是含有我們想要保留的最新近的修改!這樣我們不會遇到任何合并沖突,而且可以保留一個漂亮的、線性的 Git 歷史記錄。
上面這個例子展示了在 master 分支上的變基。但是,在更大型的項目中,你通常不需要這樣的操作。git rebase 在為復(fù)制的提交創(chuàng)建新的 hash 時會修改項目的歷史記錄。
如果你在開發(fā)一個 feature 分支并且 master 分支已經(jīng)更新過,那么變基就很好用。你可以在你的分支上獲取所有更新,這能防止未來出現(xiàn)合并沖突。
交互式變基(Interactive Rebase)
在為提交執(zhí)行變基之前,我們可以修改它們!我們可以使用交互式變基來完成這一任務(wù)。交互式變基在你當(dāng)前開發(fā)的分支上以及想要修改某些提交時會很有用。
在我們正在 rebase 的提交上,我們可以執(zhí)行以下 6 個動作:
- reword:修改提交信息;
- edit:修改此提交;
- squash:將提交融合到前一個提交中;
- fixup:將提交融合到前一個提交中,不保留該提交的日志消息;
- exec:在每個提交上運行我們想要 rebase 的命令;
- drop:移除該提交。
如果你想要移除一個提交,只需 drop 即可。

把多個提交融合到一起以便得到清晰的提交歷史

交互式變基能為你在 rebase 時提供大量控制,甚至可以控制當(dāng)前的活動分支。
重置(Resetting)
當(dāng)我們不想要之前提交的修改時,這時候就要執(zhí)行 git reset。
git reset 能讓我們不再使用當(dāng)前臺面上的文件,讓我們可以控制 HEAD 應(yīng)該指向的位置。
軟重置
軟重置會將 HEAD 移至指定的提交(或與 HEAD 相比的提交的索引),而不會移除該提交之后加入的修改!
假設(shè)我們不想保留添加了一個 style.css 文件的提交 9e78i,而且我們也不想保留添加了一個 index.js 文件的提交 035cc。但是,我們確實又想要保留新添加的 style.css 和 index.js 文件!這是軟重置的一個完美用例。

輸入 git status 后,你會看到我們?nèi)匀豢梢栽L問在之前的提交上做過的所有修改。這很好,這意味著我們可以修復(fù)這些文件的內(nèi)容,之后再重新提交它們!
硬重置
有時候我們并不想保留特定提交引入的修改。不同于軟重置,我們應(yīng)該再也無需訪問它們。Git 應(yīng)該直接將整體狀態(tài)直接重置到特定提交之前的狀態(tài):這甚至包括你在工作目錄中和暫存文件上的修改。

Git 丟棄了 9e78i 和 035cc 引入的修改,并將狀態(tài)重置到了 ec5be 的狀態(tài)。
還原(Reverting)
另一種撤銷修改的方法是執(zhí)行 git revert。通過對特定的提交執(zhí)行還原操作,我們會創(chuàng)建一個包含已還原修改的新提交。
假設(shè) ec5be 添加了一個 index.js 文件。但之后我們發(fā)現(xiàn)其實我們再也不需要由這個提交引入的修改了。那就還原 ec5be 提交吧!

提交 9e78i 還原了由提交 ec5be 引入的修改。在撤銷特定的提交時,git revert 非常有用,同時也不會修改分支的歷史。
揀選(Cherry-picking)
當(dāng)一個特定分支包含我們的活動分支需要的某個提交時,我們對那個提交執(zhí)行 cherry-pick!對一個提交執(zhí)行 cherry-pick 時,我們會在活動分支上創(chuàng)建一個新的提交,其中包含由揀選出來的提交所引入的修改。
假設(shè) dev 分支上的提交 76d12 為 index.js 文件添加了一項修改,而我們希望將其整合到 master 分支中。我們并不想要整個 dev 分支,而只需要這個提交!

現(xiàn)在 master 分支包含 76d12 引入的修改了。
取回(Fetching)
如果你有一個遠(yuǎn)程 Git 分支,比如在 GitHub 上的分支,當(dāng)遠(yuǎn)程分支上包含當(dāng)前分支沒有的提交時,可以使用取回。比如當(dāng)合并了另一個分支或你的同事推送了一個快速修復(fù)時。
通過在這個遠(yuǎn)程分支上執(zhí)行 git fetch,我們就可在本地獲取這些修改。這不會以任何方式影響你的本地分支:fetch 只是單純地下載新的數(shù)據(jù)而已。

現(xiàn)在我們可以看到自上次推送以來的所有修改了。這些新數(shù)據(jù)也已經(jīng)在本地了,我們可以決定用這些新數(shù)據(jù)做什么了。
拉?。≒ulling)
盡管 git fetch 可用于獲取某個分支的遠(yuǎn)程信息,但我們也可以執(zhí)行 git pull。git pull 實際上是兩個命令合成了一個:git fetch 和 git merge。當(dāng)我們從來源拉取修改時,我們首先是像 git fetch 那樣取回所有數(shù)據(jù),然后最新的修改會自動合并到本地分支中。

Reflog
有時候你可能感覺你把 git repo 完全搞壞了,讓你想完全刪了了事。
git reflog 是一個非常有用的命令,可以展示已經(jīng)執(zhí)行過的所有動作的日志。包括合并、重置、還原,基本上包含你對你的分支所做的任何修改。

如果你犯了錯,你可以根據(jù) reflog 提供的信息通過重置 HEAD 來輕松地重做!
假設(shè)我們實際上并不需要合并原有分支。當(dāng)我們執(zhí)行 git reflog 命令時,我們可以看到這個 repo 的狀態(tài)在合并前位于 HEAD@{1}。那我們就執(zhí)行一次 git reset,將 HEAD 重新指向在 HEAD@{1} 的位置。
