小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

git 使用詳解(10)

 北漂之鄔 2014-02-24

遠(yuǎn)程分支

遠(yuǎn)程分支(remote branch)是對遠(yuǎn)程倉庫中的分支的索引。它們是一些無法移動的本地分支;只有在Git 進(jìn)行網(wǎng)絡(luò)交互時才會更新。遠(yuǎn)程分支就像是書簽,提醒著你上次連接遠(yuǎn)程倉庫時上面各分支的位置。

我們(遠(yuǎn)程倉庫名)/(分支名) 這樣的形式表示 遠(yuǎn)程分支。比如我們想看看上次同origin 倉庫通訊時master 的樣子,就應(yīng)該查看origin/master 分支。如果你和同伴一起修復(fù)某個問題,但他們先推送了一個iss53 分支到遠(yuǎn)程倉庫,雖然你可能也有一個本地的iss53 分支,但指向服務(wù)器上最新更新的卻應(yīng)該是origin/iss53 分支。

可能有點亂,我們不妨舉例說明。假設(shè)你們團(tuán)隊有個地址為 git.ourcompany.com 的 Git 服務(wù)器。如果你從這里克隆,Git 會自動為你將此遠(yuǎn)程倉庫命名為origin,并下載其中所有的數(shù)據(jù),建立一個指向它的master 分支 的指針,在本地命名為origin/master,但你無法在 本地更改其數(shù)據(jù)。接著,Git 建立一個屬于你自己的本地master 分支,始于originmaster 分支相同的位置,你可以就此開始工作(見圖 3-22):


圖 3-22.  一次Git 克隆 會建立你自己的 本地分支master 和 遠(yuǎn)程分支origin/master,它們都指向 origin/master 分支的 最后一次提交。

如果你在本地 master 分支做了些改動,與此同時,其他人向 git.ourcompany.com 推送了他們的更新,那么服務(wù)器上的 master 分支就會向前推進(jìn),而于此同時,你在本地的提交歷史正朝向不同方向發(fā)展。不過只要你不和服務(wù)器通訊,你的origin/master指針仍然保持原位不會移動見圖 3-23)。


圖 3-23. 在本地工作的同時有人向遠(yuǎn)程倉庫推送內(nèi)容會讓提交歷史開始分流。

可以運行 git fetch origin 來同步 遠(yuǎn)程服務(wù)器 上的數(shù)據(jù)到 本地。該命令首先找到origin 是哪個服務(wù)器(本例為git.ourcompany.com),從上面獲取你尚未擁有的數(shù)據(jù),更新你本地的數(shù)據(jù)庫,然后把origin/master 的指針移到它最新的位置上(見圖 3-24)。


圖 3-24. git fetch 命令會更新 remote 索引。

為了演示擁有多個遠(yuǎn)程分支(在不同的遠(yuǎn)程服務(wù)器上)的項目是如何工作的,我們假設(shè)你還有另一個僅供你的敏捷開發(fā)小組使用的內(nèi)部服務(wù)器 git.team1.ourcompany.com??梢杂玫诙轮刑岬降?code>git remote add 命令把它加為當(dāng)前項目的遠(yuǎn)程分支之一。我們把它命名為teamone,以便代替原始的 Git 地址(見圖 3-25)。


圖 3-25. 把另一個服務(wù)器加為遠(yuǎn)程倉庫

現(xiàn)在你可以用 git fetch teamone 來獲取小組服務(wù)器上你還沒有的數(shù)據(jù)了。由于當(dāng)前該服務(wù)器上的內(nèi)容是你 origin 服務(wù)器上的子集,Git 不會下載任何數(shù)據(jù),而只是簡單地創(chuàng)建一個名為teamone/master 的分支,指向teamone 服務(wù)器上master 分支所在的提交對象31b8e(見圖 3-26)。


圖 3-26. 你在本地有了一個指向 teamone 服務(wù)器上 master 分支的索引。

push推送 本地分支

要想和其他人分享某個 本地分支,你需要把它推送到一個你擁有寫權(quán)限的遠(yuǎn)程倉庫。你的本地分支不會被自動同步到你引入的遠(yuǎn)程服務(wù)器上,除非你明確執(zhí)行推送操作。換句話說,對于無意分享的分支,你盡管保留為私人分支好了,而只推送那些協(xié)同工作要用到的特性分支。

如果你有個叫 serverfix 的分支 需要和他人一起開發(fā),可以運行git push (遠(yuǎn)程倉庫名) (分支名)

$ git push origin serverfix
Counting objects: 20, done.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (15/15), 1.74 KiB, done.
Total 15 (delta 5), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new branch]      serverfix -> serverfix

這其實有點像條捷徑。Git 自動把 serverfix 分支名擴(kuò)展為 refs/heads/serverfix:refs/heads/serverfix,意為“取出我在本地的 serverfix 分支,推送到遠(yuǎn)程倉庫的 serverfix 分支中去”。我們將在第九章進(jìn)一步介紹refs/heads/ 部分的細(xì)節(jié),不過一般使用的時候都可以省略它。也可以運行git push origin serverfix:serferfix 來實現(xiàn)相同的效果,它的意思是“上傳我本地的 serverfix 分支到遠(yuǎn)程倉庫中去,仍舊稱它為 serverfix 分支”。通過此語法,你可以把本地分支推送到某個命名不同的遠(yuǎn)程分支:若想把遠(yuǎn)程分支叫作awesomebranch,可以用git push origin serverfix:awesomebranch 來推送數(shù)據(jù)。

接下來,當(dāng)你的協(xié)作者再次從服務(wù)器上獲取數(shù)據(jù)時,他們將得到一個新的遠(yuǎn)程分支 origin/serverfix

$ git fetch origin
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 15 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (15/15), done.
From git@github.com:schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix

值得注意的是,在 fetch 操作下載好新的遠(yuǎn)程分支之后,你仍然無法在本地編輯該遠(yuǎn)程倉庫中的分支。換句話說,在本例中,你不會有一個新的serverfix 分支,有的只是一個你無法移動的origin/serverfix 指針。

如果要把該內(nèi)容合并到當(dāng)前分支,可以運行 git merge origin/serverfix。如果想要一份自己的serverfix 來開發(fā),可以在遠(yuǎn)程分支的基礎(chǔ)上分化出一個新的分支來:

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"

這會切換到新建的 serverfix 本地分支,其內(nèi)容同遠(yuǎn)程分支 origin/serverfix 一致,這樣你就可以在里面繼續(xù)開發(fā)了。

跟蹤遠(yuǎn)程分支

從遠(yuǎn)程分支 checkout 出來的本地分支,稱為_跟蹤分支(tracking branch)_。跟蹤分支 是一種和 遠(yuǎn)程分支有直接聯(lián)系的 本地分支在跟蹤分支里 輸入git push,Git 會自行推斷應(yīng)該向哪個服務(wù)器的哪個分支推送數(shù)據(jù)。反過來,在這些分支里運行git pull獲取所有遠(yuǎn)程 索引,并把它們的數(shù)據(jù)都合并到本地分支中來。

在克隆倉庫時,Git 通常會自動創(chuàng)建一個名為 master 的分支來跟蹤 origin/master。這正是git pushgit pull 一開始就能正常工作的原因。當(dāng)然,你可以隨心所欲地設(shè)定為其它跟蹤分支,比如origin 上除了master 之外的其它分支。剛才我們已經(jīng)看到了這樣的一個例子:git checkout -b [分支名] [遠(yuǎn)程名]/[分支名]。如果你有 1.6.2 以上版本的 Git,還可以用--track 選項簡化

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"

要為本地分支設(shè)定不同于遠(yuǎn)程分支的名字,只需在前個版本的命令里換個名字:

$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "sf"

現(xiàn)在你的本地分支 sf自動origin/serverfix 推送和抓取數(shù)據(jù)了。

刪除遠(yuǎn)程分支

如果不再需要某個遠(yuǎn)程分支了,比如搞定了某個特性并把它合并進(jìn)了遠(yuǎn)程的 master 分支(或任何其他存放穩(wěn)定代碼的地方),可以用這個非常無厘頭的語法來刪除它:git push [遠(yuǎn)程名] :[分支名]。如果想在服務(wù)器上刪除serverfix 分支,而不是刪除commit,運行下面的命令:

$ git push origin :serverfix
To git@github.com:schacon/simplegit.git
 - [deleted]         serverfix

咚!服務(wù)器上的分支沒了。你最好特別留心這一頁,因為你一定會用到那個命令,而且你很可能會忘掉它的語法。有種方便記憶這條命令的方法:記住我們不久前見過的 git push [遠(yuǎn)程名] [本地分支]:[遠(yuǎn)程分支] 語法,如果省略[本地分支],那就等于是在說“在這里提取空白然后把它變成[遠(yuǎn)程分支]”。


分支的衍合

把一個分支整合到另一個分支的辦法有兩種:mergerebase(譯注:rebase 的翻譯暫定為“衍合”,大家知道就可以了。)。在本章我們會學(xué)習(xí)什么是衍合,如何使用衍合,為什么衍合操作如此富有魅力,以及我們應(yīng)該在什么情況下使用衍合。

基本的衍合操作

請回顧之前有關(guān)合并的一節(jié)(見圖 3-27),你會看到開發(fā)進(jìn)程分叉到兩個不同分支,又各自提交了更新。


圖 3-27. 最初分叉的提交歷史。

之前介紹過,最容易的整合分支的方法是 merge 命令,它會把兩個分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)進(jìn)行三方合并,合并的結(jié)果是產(chǎn)生一個新的提交對象(C5)。如圖 3-28 所示:


圖 3-28. 通過合并一個分支來整合分叉了的歷史。

其實,還有另外一個選擇:你可以把在 C3 里產(chǎn)生的變化補(bǔ)丁在 C4 的基礎(chǔ)上重新打一遍。在 Git 里,這種操作叫做_衍合(rebase)_。有了 rebase 命令,就可以把在一個分支里提交的改變移到另一個分支里重放一遍。

在上面這個例子中,運行:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

它的原理是回到兩個分支最近的共同祖先,根據(jù)當(dāng)前分支(也就是要進(jìn)行衍合的分支 experiment)后續(xù)的歷次提交對象(這里只有一個 C3),生成一系列文件補(bǔ)丁,然后以基底分支(也就是主干分支master)最后一個提交對象(C4)為新的出發(fā)點,逐個應(yīng)用之前準(zhǔn)備好的補(bǔ)丁文件,最后會生成一個新的合并提交對象(C3’),從而改寫experiment 的提交歷史,使它成為master 分支的直接下游,如圖 3-29 所示:


圖 3-29. 把 C3 里產(chǎn)生的改變到 C4 上重演一遍。

現(xiàn)在回到 master 分支,進(jìn)行一次快進(jìn)合并(見圖 3-30):


圖 3-30. master 分支的快進(jìn)。

現(xiàn)在的 C3’ 對應(yīng)的快照,其實和普通的三方合并,即上個例子中的 C5 對應(yīng)的快照內(nèi)容一模一樣了。雖然最后整合得到的結(jié)果沒有任何區(qū)別,但衍合能產(chǎn)生一個更為整潔的提交歷史。如果視察一個衍合過的分支的歷史記錄,看起來會更清楚:仿佛所有修改都是在一根線上先后進(jìn)行的,盡管實際上它們原本是同時并行發(fā)生的。

一般我們使用衍合的目的,是想要得到一個能在遠(yuǎn)程分支上干凈應(yīng)用的補(bǔ)丁 — 比如某些項目你不是維護(hù)者,但想幫點忙的話,最好用衍合:先在自己的一個分支里進(jìn)行開發(fā),當(dāng)準(zhǔn)備向主項目提交補(bǔ)丁的時候,根據(jù)最新的origin/master 進(jìn)行一次衍合操作然后再提交,這樣維護(hù)者就不需要做任何整合工作(譯注:實際上是把解決分支補(bǔ)丁同最新主干代碼之間沖突的責(zé)任,化轉(zhuǎn)為由提交補(bǔ)丁的人來解決。),只需根據(jù)你提供的倉庫地址作一次快進(jìn)合并,或者直接采納你提交的補(bǔ)丁。

請注意,合并結(jié)果中最后一次提交所指向的快照,無論是通過衍合,還是三方合并,都會得到相同的快照內(nèi)容,只不過提交歷史不同罷了。衍合是按照每行的修改次序重演一遍修改,而合并是把最終結(jié)果合在一起。

有趣的衍合

衍合也可以放到其他分支進(jìn)行,并不一定非得根據(jù)分化之前的分支。以圖 3-31 的歷史為例,我們?yōu)榱私o服務(wù)器端代碼添加一些功能而創(chuàng)建了特性分支 server,然后提交 C3 和 C4。然后又從 C3 地方再增加一個client 分支來對客戶端代碼進(jìn)行一些相應(yīng)修改,所以提交了 C8 和 C9。最后,又回到server 分支提交了 C10。


圖 3-31. 從一個特性分支里再分出一個特性分支的歷史。

假設(shè)在接下來的一次軟件發(fā)布中,我們決定先把客戶端的修改并到主線中,而暫緩并入服務(wù)端軟件的修改(因為還需要進(jìn)一步測試)。這個時候,我們就可以把基于 server 分支而非 master 分支的改變(即 C8 和 C9),跳過 server 直接放到master 分支中重演一遍,但這需要用git rebase--onto 選項指定新的基底分支master

$ git rebase --onto master server client

這好比在說:“取出 client 分支,找出 client 分支和 server 分支的共同祖先之后的變化,然后把它們在master 上重演一遍”。是不是有點復(fù)雜?不過它的結(jié)果如圖 3-32 所示,非常酷(譯注:雖然client 里的 C8, C9 在 C3 之后,但這僅表明時間上的先后,而非在 C3 修改的基礎(chǔ)上進(jìn)一步改動,因為serverclient 這兩個分支對應(yīng)的代碼應(yīng)該是兩套文件,雖然這么說不是很嚴(yán)格,但應(yīng)理解為在 C3 時間點之后,對另外的文件所做的 C8,C9 修改,放到主干重演。):


圖 3-32. 將特性分支上的另一個特性分支衍合到其他分支。

現(xiàn)在可以快進(jìn) master 分支了(見圖 3-33):

$ git checkout master
$ git merge client

圖 3-33. 快進(jìn) master 分支,使之包含 client 分支的變化。

現(xiàn)在我們決定把 server 分支的變化也包含進(jìn)來。我們可以直接把 server 分支衍合到 master,而不用手工切換到 server 分支后再執(zhí)行衍合操作 — git rebase [主分支] [特性分支] 命令會先取出特性分支server,然后在主分支master 上重演

$ git rebase master server

于是,server 的進(jìn)度應(yīng)用到 master 的基礎(chǔ)上,如圖 3-34 所示:


圖 3-34. 在 master 分支上衍合 server 分支。

然后就可以快進(jìn)主干分支 master 了:

$ git checkout master
$ git merge server

現(xiàn)在 clientserver 分支的變化都已經(jīng)集成到主干分支來了,可以刪掉它們了。最終我們的提交歷史會變成圖 3-35 的樣子:

$ git branch -d client
$ git branch -d server

圖 3-35. 最終的提交歷史

衍合的風(fēng)險

呃,奇妙的衍合也并非完美無缺,要用它得遵守一條準(zhǔn)則:

一旦分支中的 提交對象 發(fā)布到 公共倉庫,就千萬不要 對該分支進(jìn)行 衍合 操作。

如果你遵循這條金科玉律,就不會出差錯。否則,人民群眾會仇恨你,你的朋友和家人也會嘲笑你,唾棄你。

進(jìn)行衍合的時候,實際上拋棄了一些現(xiàn)存的提交對象而創(chuàng)造了一些類似但不同的新的提交對象。如果你把原來分支中的提交對象發(fā)布出去,并且其他人更新下載后在其基礎(chǔ)上開展工作,而稍后你又用git rebase 拋棄這些提交對象,把新的重演后的提交對象發(fā)布出去的話,你的合作者就不得不重新合并他們的工作,這樣當(dāng)你再次從他們那里獲取內(nèi)容時,提交歷史就會變得一團(tuán)糟。

下面我們用一個實際例子來說明為什么公開的衍合會帶來問題。假設(shè)你從一個中央服務(wù)器克隆然后在它的基礎(chǔ)上搞了一些開發(fā),提交歷史類似圖 3-36 所示:


圖 3-36. 克隆一個倉庫,在其基礎(chǔ)上工作一番。

現(xiàn)在,某人在 C1 的基礎(chǔ)上做了些改變,并合并他自己的分支得到結(jié)果 C6,推送到中央服務(wù)器。當(dāng)你抓取并合并這些數(shù)據(jù)到你本地的開發(fā)分支中后,會得到合并結(jié)果 C7,歷史提交會變成圖 3-37 這樣:


圖 3-37. 抓取他人提交,并入自己主干。

接下來,那個推送 C6 上來的人決定用衍合取代之前的合并操作;繼而又用 git push --force 覆蓋了服務(wù)器上的歷史,得到 C4’。而之后當(dāng)你再從服務(wù)器上下載最新提交后,會得到:


圖 3-38. 有人推送了衍合后得到的 C4’,丟棄了你作為開發(fā)基礎(chǔ)的 C4 和 C6。

下載更新后需要合并,但此時衍合產(chǎn)生的提交對象 C4’ 的 SHA-1 校驗值和之前 C4 完全不同,所以 Git 會把它們當(dāng)作新的提交對象處理,而實際上此刻你的提交歷史 C7 中早已經(jīng)包含了 C4 的修改內(nèi)容,于是合并操作會把 C7 和 C4’ 合并為 C8(見圖 3-39):


圖 3-39. 你把相同的內(nèi)容又合并了一遍,生成一個新的提交 C8。

C8 這一步的合并是遲早會發(fā)生的,因為只有這樣你才能和其他協(xié)作者提交的內(nèi)容保持同步。而在 C8 之后,你的提交歷史里就會同時包含 C4 和 C4’,兩者有著不同的 SHA-1 校驗值,如果用git log 查看歷史,會看到兩個提交擁有相同的作者日期與說明,令人費解。而更糟的是,當(dāng)你把這樣的歷史推送到服務(wù)器后,會再次把這些衍合后的提交引入到中央服務(wù)器,進(jìn)一步困擾其他人(譯注:這個例子中,出問題的責(zé)任方是那個發(fā)布了 C6 后又用衍合發(fā)布 C4’ 的人,其他人會因此反饋雙重歷史到共享主干,從而混淆大家的視聽。)。

如果把衍合當(dāng)成一種在推送之前清理提交歷史的手段,而且僅僅衍合那些尚未公開的提交對象,就沒問題。如果衍合那些已經(jīng)公開的提交對象,并且已經(jīng)有人基于這些提交對象開展了后續(xù)開發(fā)工作的話,就會出現(xiàn)叫人沮喪的麻煩。

3.7  小結(jié)

讀到這里,你應(yīng)該已經(jīng)學(xué)會了如何創(chuàng)建分支并切換到新分支,在不同分支間轉(zhuǎn)換,合并本地分支,把分支推送到共享服務(wù)器上,使用共享分支與他人協(xié)作,以及在分享之前進(jìn)行衍合。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多