顯示具有 GIT 標籤的文章。 顯示所有文章
顯示具有 GIT 標籤的文章。 顯示所有文章

2014年3月29日 星期六

Git (九) a successful git branching model

在使用git的時候由於開branch非常方便,大部分的人都會非常隨性的開branch,並沒有一定的方式,網路上有人整理出一套流程叫做a successful git branching model,個人也覺得非常不錯所以就在這邊做介紹

首先先介紹一般的開發流程通常會有兩個主要的branch , develop 和 master,

develop:平常開發的branch,任何的功能或bug fix都在該branch上開發。

master:永遠處在production ready,master上的每個點都是一個版本,always可release,是master branch的特色,這麼做的好處是,假設是自動化的開發,通常會有一個server在build並且test,這個server就可以在master上面不斷的build and test。
除了上述提到的兩個主要的branch之外,還有三個支援型的branch,分別是feature branch, release branch, 以及hotfix branch, 

feature branch:開發新功能,從develop分支出來,完成後merge回develop 
release branch:準備release的版本,只修bug,從develop分支出來,完成後merge回 master and develop
hotfix:等不及release必須馬上修趕上線的情況,會從master分支出來,完成後merge回master 和 develop

用開發的流程來說明的話就是.....

假設我們在開發一支app,然後希望下個版本會有登入/登出的功能,撥放影片功能以及產品介紹頁面,

這時候開發人員就會從develop開始開發,然後分別創建三個feature branch
feature/login、feature/playvideo、feature/productIntro
三個branch,當這些功能差不多開發完成時,就會將這些feature branch merge回develop branch
接著就會準備進測試階段,接著就會將develop branch 切換到 release branch,

在release branch上就只會修bug,不再增加新的功能,當bug修正到一定的程度已經可以release出去了就會切到master branch並且加上tag 1.0,如果沒有意外一個周期的開發流程就已經結束,

但是如果在切到master branch之後,又發現了緊急的bug (例如app crash)這時候並不會直接在master上面做修正,而是切到hotfix這個branch,並且在這個branch將bug fix 並且驗證完成後再merge回master  



在使用這套理論的時候我有個習慣,所有的feature branch在merge回develop之前都是使用 rebase
這可以讓我們git的線型看起來比較乾淨,有興趣了解rebase的人可以去Git 常用指令 (二) Rebase vs Merge看看,在這裡要再一次強調千萬不要rebase已經在push到server端的branch,由於rebase是會改變歷史紀錄的,這會造成其他已經pull該branch的人遇到一些問題。

還有一個值得注意的地方是,當使用rebase將自己的feature測試完並且確認沒有問題之後,想要將feature merge回develop時,記得要使用 merge --no-ff (no fast-ward的意思)
這會讓我們merge回develop時會出現合併的節點而不是單純的一條線



有興趣的人可以去看看我最近做的ppt,如果有疑問也歡迎大家一起討論。

最後附上ppt的網址 http://www.slideshare.net/ssuser324e5a/git-32879866

2014年3月3日 星期一

Git(八) 還原被修改的檔案

今天要談的是如何還原被修改的檔案,檔案被修改之後有分成兩種情況

  1. 修改的檔案尚未被push上remote端
  2. 修改的檔案已經被push上remote端

修改的檔案尚未被push上Remote端




如上圖,我在local端做了一些修改做了3個commit ha, hb, hc
但是我尚未push 到remote端,這時候我發現hb這個commit 改了我不希望被更動的東西,我希望remove hb這個commit ,這時候可以使用 git rebase -i  HEAD_SHA  (其中 HEAD_SHA = origin/HEAD的 SHA)

接著會出現如上圖,這時候只需要將 hb這行刪除就可以了。
然後就會看到
hb這個commit 已經消失了。


如果只是想要移除上一個commit 例如  我commit 了ha hc  ,然後我希望將hc這個commit 移除
這時候也可以直接使用 git reset --hard HEAD^ 這樣就會將版本退回ha commit 
對reset這個command有興趣的話可以參考

Git 常用指令 (一) git reset

Git 常用指令 (五) reset 應用 merge 和 rebase之後的倒回


上面的方法很方便,但是這些方法僅限於使用在commit 尚未被push到remote端之前
假設commit 已經被push到remote端這就麻煩了,就只能只用下面的方式來修改。


修改的檔案已經被push上Remote端

如下圖,假設我commit並push了 ha 到remote端上

在我commit之後我希望reverse這個commit ,這時候可以使用
git revert  ha_SHA  (其中 ha_SHA為 ha這個commit 的SHA)
這樣就可以將 ha這個commit reverse 。


假設我今天merge 了一個branch helloo,但是在merge之後後悔了,希望將這個merge revert
如下圖

這時候我必須使用 git revert -m 1 Merge_Branch_helloo_SHA
(其中 Merge_Branch_helloo_SHA的 SHA為 merge branch helloo的SHA)

假設未來希望將revert merge的版本還原,就必須再一次revert版本(merge branch將不會work),
這樣就能將原本的版本覆原。


使用git 還原的時候要確切的知道自己在幹嘛,如果不是很確定的話,記得做備份的動作,否則一不小心可能會將寫好的檔案revert掉。









2014年1月15日 星期三

Git(七) pull vs fetch

甚麼是git fetch

git fetch就是將遠端的資料update 到local端,簡單的說就是讓local端知道遠端最新的位置是在哪
舉個例子 如下圖
先不管server端的部分,
origin/hello2 為遠端的本地參照位置,hello2為目前實際的位置。

假設 遠端原本只有 aaa bbb ccc三個commit 
然後user A和B 皆使用 git clone , 他們的origin/hello2都會是在 ccc的位置,
hello2也會在ccc的位置

假設user B  commit 並 push 了 ddd,這時user A的 origin/hello2依然是在 ccc
那 user A 該如何更新origin/hello2到 ddd呢?
user A只需要使用 git fetch origin hello2 
這樣 user A的 origin/hello2就會更新到 ddd的位置

fetch 會去更新origin/hello2(本地參照)

解釋完git fetch之後,要解釋git pull就相對簡單許多



甚麼是git pull


git pull 實際作用為 git fetch  +  git merge

舉個例子

假設目前在遠端上面有人 commit 並push了  aaa 和 bbb
而我在本地端commit 了 ccc的功能

假設打入指令 git fetch origin hello2 這時會將origin/hello2 更新到bbb的位置
接著使用git merge origin hello2就會變成 如下圖
然而這個結果跟直接使用 git pull的結果是一樣的

git pull 不只會更新本地參照還會自動進行merge branch。


比較安全的用法

從server上拉資料之前  
總是先使用git fetch更新本地參照
接著先確認是否有其他的分支
若無其他分支,如下圖  則使用git pull  fast forward到 Origin/hello2


若有其他分支,如下圖
則檢查是否有需要merge 若有則使用 git merge。




結論

git fetch是相對較安全的做法,假設想要知道server上最新的版本但是還不想本地端還有一些功能還沒開發完,或是基於某些原因還不想merge的話可以使用git fetch,這樣本地只會取得server上最新版本但是並不會merge到本地端,

假設使用git pull 代表本地端功能已經開發完成可以進行合併,使用git pull就會直接將本地端跟server上做合併的動作。



2013年12月25日 星期三

Git (六) 分支

分支是甚麼?

分支就是在專案中開闢新的開發線,當切換分支時檔案也會同步為新分支的檔案
例如
在 hello2中
做了三個commit
vi MasterA
vi MasterB
vi MasterC
git add MasterA
git commit -m "MasterA"
git add MasterB
git commit -m "MasterB"
git add MasterC
git commit -m "MasterC"

然後 git checkout -b pp   創建一個分支名叫 pp 並將分支切換到  pp
並做一個commit
vi  branch
git add branch
git commit -m "branch"
這時候開發圖會長得如下圖所示


這章圖代表甚麼呢?

代表在同一個專案中目前有兩個分支分別是 pp 以及 hello2
兩個專案中所擁有的檔案並不會相同 hello2中擁有 masterA masterB masterC
而 pp中只有 branch這個檔案

簡單的說可以在同一個資料夾底下同時存有多個不同版本的檔案


為什麼要開分支不開另一個資料夾?

原因其實很簡單,使用分支的好處太多了,
舉個例子,假設我今天有兩個分支
ACompany 和 BCompany 這兩個分支所代表的意義分別是給A公司的客製化版本,另一個是給B公司的客製化版本,基本上程式架構都相同,只有某些功能有些差異,
假設今天在ACompany分支上完成了一個新的feature,
然後BCompany分支也要導入這個功能時,我們只需要使用cherry-pick就可以將功能導入,
我們可以很輕易的將功能導入。

如果是使用另一個資料夾就必須花時間apply patch或者重新將功能導入(我想不會有人想這麼做)
而且很難做兩個版本之間的控管


何時要使用分支?

  • 當出現多種版本要維護時,例如出了1.0版本 2.0版本但是兩個版本都還有人使用這時候就可以開分支,當出現bug時可以同時做兩個版本的修復
  • 開發版本,測試版本,正式版本,這種作法是為了幫助自動化測試,在正式版本或測試版本通常會有dayly build 和 unit test 在run 只要有新的commit 就會做測試, 開發人員將開發好的版本merge 進測試版本 經過測試之後再進到正式版本。
  • 開發新功能時(這是本人較習慣的做法),在開發新功能時先開立一個新的分支在功能完成後再將該分支merge回去。這樣可以避免主支幹擁有尚未完成的功能,而其他人還是可以繼續做commit 的動作
養成好的分支觀念,可以讓開發以及維護更順利,遇到以上的情形,just Branch It  XD



Git (五) reset 應用 merge 和 rebase之後的倒回

由  Git 常用指令 (一) git reset  可以瞭解 reset 可以將檔案倒回,
這裡要說明的是,當merge 或 rebase之後要怎麼將版本倒回尚未下指令之前,
首先要介紹一下幾個特別符號

HEAD  

為分支上最新的送交

ORIG_HEAD

在merge 或 rebase 時 ,會先將原本的HEAD記錄在 ORIG_HEAD ,
所以可以使用ORIG_HEAD來做恢復的動作

FETCH_HEAD

是抓取的上一個分支HEAD,並且僅在抓取指令之後有效,使用這個符號可以找到git fetch 指令抓取的HEAD

MERGE_HEAD

合併再執行時,另一個分支的HEAD會被暫時的記錄在MERGE_HEAD中


所以,回到merge 或 rebase時該怎麼倒回
就是使用 git reset --hard ORIG_HEAD  將檔案倒回就可以了


2013年12月23日 星期一

Git (四) Rebase vs Merge (續)

在上一篇有講述rebase 和 merge上的差異,這篇要講的是線圖上的差異

先看看下面三張圖表


原圖



Merge


Rebase


rebase 可以將線圖拉直,merge則是會將兩個線圖合併
rebase的好處是可以將branch的紀錄消除(在線圖上就不會看到兩條線)
在使用上是看個人喜好,不過在使用rebase的時候要特別注意
網路上有人認為rebase is evil,他們認為

When you rebase a branch, you are changing its history in a way that will cause problems for anyone who already has a copy of the branch in their repository and tries to pull updates from you.

意思是如果在某個很多人使用的branchA下,rebase另一個branchB,然後又將rebase之後的結果push出去,會使得該branchA的共同使用者遇到問題。

大部分的人都建議 rebase只用在private branch,


使用情境大概是

我在master上開一個branch 然後在上面實作了自己的功能,經過一段時間之後,我發現master經歷了許多版本的update,所以我希望將branch update到最新版本,以便於測試是否有問題,
這時候就可以使用 rebase 將我自己的branch update到最新版本。

不過..我的做法是會再新開一個branchC 然後將branchC 做rebase,就如同上面英文所說的,
rebase會更改歷史記錄,假設rebase失敗了,那local的檔案也會跟著毀掉,所以保險起見,我還是會先做個備份。


不建議的做法是

在一個眾多人參考的branch上開發了自己的功能,經過一段時間之後,發現跟server上的branch版本有差異,所以使用rebase 將版本 update到最新版本。

這兩個的差異就是在於一個是私人的branch 不管怎麼改都不會影響到別人
但是共用的branch如果操作錯了不僅整個分支會出問題,還會連帶影響到其他的開發人員,
這是很可怕的事情。


2013年12月19日 星期四

Git (三) cherry-pick

Cherry-Pick

這個指令讓我們能夠將branch中,某一次commit所做的修改,直接套用到另外一個branch
如下圖所示


上圖 develop 為主要的開發支幹,
然後我們開了一個branch "feature/TestFeature" 然後在那個branch做了一些資料的更改,
接著我們希望將其中four 所做的變更套用至 develop 中,這時候就可以使用cherry-pick


接下來我們要將four這個commit 套用至 develop中
所以首先我們要先知道 four 這個commit 所代表的"代號"  也就是 b784dfe 這組數字
接著在develop branch中下指令 git cherry-pick b784dfe 就可以了


如下圖所示,four這個commit直接被套用到 develop中



cherry-pick 可以讓我們將部分的修改直接套用到另一個branch


使用的情境

同時在維護兩個類似的project
當修正完A project的 bug之後  只需要使用cherry-pick將相同的修正套用到B project即可
這算是十分方便的功能

2013年11月25日 星期一

Git (二) Rebase vs Merge

rebase 跟 merge 做的事情其實很像

主要的差別在於
假設我在 branch下指令  git merge master,
merge 會一次性的將所有master的commit 直接merge進 branch
rebase的話  則會分成多次commit 進來




舉個例子來看看


上面分別有master and branch 兩個分支
我在 branch 上做了兩次commit  branchC  and branchD
然後我在 master 上做了三次commit masterA ,masterB and masterC



Merge

首先我們先使用merge 指令
在branch分支下指令 git merge master
master的資料將會在一次commit 中將 masterA , masterB and masterC
都加進我的branch分支內,我們看到的commit結果會是如上圖
branchC, branchD ,Merge branch 'master' into branch ,
要倒回去的話只需要下指令git reset --hard HEAD^
就可以恢復merge之前的狀態



Rebase 

接著使用 rebase 指令
在branch分支下指令 git rebase master



可以看到原本分為兩個的分支結合再一起了
可以看到git rebase master之後的結果會是  
masterA , masterB, masterC , branchC and branchD 跟git merge master的結果有些差別
不過上圖並不好解釋會何會變成這樣,下面的圖比較能解釋他的原因

原本的基準點為左圖最下的藍色點,
當我們使用 git rebase master時,branch分支會將基準點從最下方的藍色點rebase到 master 的最上方的點,也就是如右圖所示(可以把他想成pointer 將指標指過去的感覺),
所以最後會只看到一條線。
經過rebase之後的分支要倒回並不是那麼的容易,
我的做法會是在create一個新的分支,然後將這次的Rebase都倒回之後再用cherry-pick將原本的commit 拉回來。



結論

            git merge master是一次性的commit ,然而git rebase master是多次性的commit 。


git merge 的commit是依照順序的 branchC, branchD 然後將master commit 進來masterA masterB masterC,
所以結果會是:branchC, branchD masterA masterB masterC
然而git rebase 的commit是先rebase到master分支再將原本的commit apply回來
所以順序會是: masterA masterB masterC branchC, branchD


Merge和Rebase的用途也不太相同

通常Rebase的用途在於  有同事在遠端commit了新的code進來
我希望我現在修改的版本能base到最新的版本
這時候就可以使用rebase command 讓手邊的程式更新到最新的版本

Merge的話比較像是我在某個分支完成了一個大型修正或功能,而且也經過大量測試之後
想要將這個功能導入我現在的working directory,
這時候就可以使用merge, 記住 merge是一次性的commit 所以所有關於該feature的功能將會一次性的導入你並不會在working directory中看到有關於該feature的其他commit




2013年11月24日 星期日

Git (一) git reset

今天要談的是git  reset的功能, git reset有分三個選項

git reset --soft
git reset --mixed 也就是 git reset
git reset --hard

首先我們要分成三個部分說明






當我們新增了一個 hello.txt 檔 到 working directory之後,
我們希望將該file commit 進  git directory,
首先我們必須下指令 git add hello.txt , 將我們希望更改的檔案放進 staging area,
接著再下指令  git commit -m "add hello"     ("add hello" 為 commit的文字描述),
然後資料就會進到git directory了。
                     
                                            太好了所有的修改都加進了git directory 我可以收工了。

可惜....  事與願違,當初做 add hello 這個修改時,我除了修改了這個檔案 hello.txt之外,我還修改了world.txt這個檔案。
     
                            糟了,我commit出了差錯老闆知道的話一定會把我臭罵一頓。

還好git 有支援倒回的指令,
我們只需要下指令 git reset --soft HEAD^ 就會將這次的commit 取消,
然後我們只需要再下  git add world.txt -> git commit -m "add hello"  ,這樣就神不知鬼不覺的將資料加進這次commit了。

當然我們也可以下指令 git reset --HEAD^ ,直接取消掉 commit 以及 staging area , 回到 working directory ,這個階段所有的資料都尚未被加入 staging area,
然後接著下指令 git add hello.txt -> git add world.txt -> git commit -m "add hello"
就好了。

然後值得注意的是 git reset HEAD^git reset --hard HEAD^ 的差別在於
git reset --hard HEAD^ 會將該次的修改也一併倒回,也就是當我下了這個指令之後
hello.txt and world.txt將回到上一次commit 的版本 (這次所做的所有修改也都會消失)

                            除非你知道正在做甚麼,
                                 否則不要輕易下這個指令,不然辛苦修改的成果可能就在瞬間被銷毀。

git reset 也可以用於 fast forward 只需要知道想要fast forward的SHA
使用 git reset --hard xxxxxx        xxxxxx 為 head的SHA
這樣就會使分支跳到指定的位置