一.提交之前先更新
1. SVN更新的原則是要隨時更新,隨時提交。當完成了一個小功能,能夠通過編譯并且自己測試之后,謹慎地提交。
2. 如果在修改的期間別人也更改了svn的對應文件,那么commit就可能會失敗。如果別人和自 己更改的是同一個文件,那么update時會自動進行合并,如果修改的是同一行,那么合并時會產生沖突,這種情況就需要同之前的開發人員聯系,兩個人一起協商解決沖突,解決沖突之后,需要兩人一起測試保證解決沖突之后,程序不會影響其他功能。
3. 在更新時注意所更新文件的列表,如果提交過程中產生了更新,則也是需要重新編譯并且完成自己的一些必要測試,再進行提交。這樣既能了解別人修改了哪些文件,同時也能避免SVN合并錯誤導致代碼有錯
二.保持原子性的提交
每次提交的間歇盡可能地短,以幾個小時的開發工作為宜。例如在更改UI界面的時候,可以每完成一個UI界面的修改或者設計,就提交一次。在開發功能模塊的時候,可以每完成一個小細節功能的測試,就提交一次,在修改bug的時候,每修改掉一個bug并且確認修改了這個bug,也就提交一次。我們提倡多提交,也就能多為代碼添加上保險。
三.提交時注意不要提交本地自動生成的文件
一般配置管理員都會將項目中一些自動生成的文件或者與本地配置環境有關的文件屏蔽提交(例如eclipse中的.classpath文件等)。如果項目中沒有進行這方面的配置來強行禁止提交這樣的文件,請自覺不要提交這樣的文件。提交了這樣的文件后,別人在更新后就可能與本地的環境沖突從而影響大家的工作。
四.不要提交不能通過編譯的代碼
代碼在提交之前,首先要確認自己能夠在本地編譯。如果在代碼中使用了第三方類庫,要考慮到項目組成員中有些成員可能沒有安裝相應的第三方類庫。項目經理在準備項目工作區域的時候,需要考慮到這樣的情況,確保開發小組成員在簽出代碼之后能夠在統一的環境中進行編譯。
五.不要提交自己不明白的代碼
代碼在提交入SVN之后,你的代碼將被項目成員所分享。如果提交了你不明白的代碼,你看不懂,別人也看不懂,如果在以后出現了問題將會成為項目質量的隱患。因此在引入任何第三方代碼之前,確保你對這個代碼有一個很清晰的了解。
六.提前協調好項目組成員的工作計劃
項目經理應該合理分配工作計劃。每個成員在準備開始進行某項功能的修改之前,如果有可能,先跟工作小組的成員談談自己的修改計劃,讓大家都能了解你的思想,了解你即將對軟件作出的修改,這樣能盡可能的減少在開發過程中可能出現的沖突,提高開發效率。同時你也能夠在和成員的交流中發現自己之前設計的不足,完善你的設計。
七.對提交的信息采用明晰的標注
在一個項目組中使用SVN,如果提交空的標注或者不確切的標注將會讓項目組中其他的成員感到很無奈,項目經理無法很清晰的掌握工作進度,無法清晰的把握此次提交的概要信息。在發現錯誤后也無法準確的定位引起錯誤的文件。所以,在提交工作時,要填寫明晰的標注,能夠概要的描述所提交文件的信息,讓項目組其他成員在看到標注后不用詳細看代碼就能了解你所做的修改。
八.慎用鎖定功能
在項目中要慎用鎖定的功能,在你鎖定了一個文件之后別人就無法繼續修改提交該文件,雖然可以減少沖突的發生率,但是可能會影響項目組中其他人員的工作。平時只有在編輯那些無法合并的文件(例如圖片文件,flash文件等)時,才適當的采用鎖定操作。I would like to point your attention to some best practices that I recommend when working in a team.
I’ve heard this question too often: “Have you put your code safely on SVN?”. That’s a bad question. Storing code to an SVN server is not meant for safety, i.e. for fear of losing it. You are talking about something else, and that’s called backup. Take Darcs, a not so popular versioning system. It can start without a server, and you can just run it locally on your machine without launching any daemon whatsoever. A faulty hard drive can still make you lose all your work, of course. That’s why you have to do backups, of course, but they don’t have anything to do with versioning. Hence, committing to the repository once a day, before taking off home, e.g., is not an acceptable practice, especially if you work in a team. Doing that would be like making a daily backup. An SVN commit, instead, has to have a meaning of some sort, not just “Ok, let’s store to the SVN server the work of today”. Moreover, sometimes, if the schedule is tough and the cooperation is tight, you need to commit very often so your peer will keep up with you all the time, and not just find out, at evening, that he’s got dozens conflicts after checking out your code.
How often should you commit? Theres no such thing as committing too often, or too rarely. You should commit each time your changes represent a logical unit, i.e. something that makes sense. Usually that happens because you’re following a plan, when coding (because you are, aren’t you?). So, you find out a bug in the trunk, plan a strategy about how to fix it, fix it, and then commit. This makes sense because that’s a commit that fixes a bug. So that revision X is buggy, while revision X+1 is not. Don’t be shy about committing too often. Should you just find an insignificant typo in a debug string, or in a comment, don’t be afraid of committing just to fix that. Nobody will be mad at you for being precise. Consider the extreme situation in which, after months and months, you may want to remember “What was the revision where I fixed that typo in that debug string?”. If you dedicated one signle commit for the actual finite logical unit of correcting the typo, you can just scroll back your changelog and find it. But what often happens, is that people will be doing something else, and, while doing that something else, will notice the type, and correct it, and basically merge that correction with the rest of the commit, making that thing losing visibility. To make it simple: your SVN comments shouldn’t explain that you did more than one thing. If your SVN comment looks like “Fixing bugs #1234 and #1235″ or “Fixing bug #4321 and correcting typo in debug sting” then you should’ve used two commits.
The second most annoying thing ever is committing with blank comments. If you’re working in a team, your peer developers will be frustrated about it and possibly mad at you, or will label you in a bad way; possibly publicly humiliate you. If you’re working alone, you will experience what you’re hypothetical development companions would have: frustration in not being able to easily track down what a certain commit did. Comments in commits are important. Please be precise and explain in detail everything you did. In the optimal case, I shouldn’t need to read your code.
This is probably the most annoying thing when dealing with people who can’t use versioning. Breaking the trunk is an habit that will quickly earn you the hatred of your colleagues. Think about it: if you commit a patch that breaks the trunk, and then I check it out, what am I going to do? The project won’t build so I either have to fix it, or come to your desk and complain to you. In both cases I’m wasting some time. And consider the first case again: what should I do after fixing your broken code? Commit it? Sending you a diff? If I’ll commit, chances are that you’ll have conflicts when you checkout, and you’ll have to waste time in resolving them. Maybe sending you a patch would be the best way, but still it’s a waste of time for the both of us. So the thing is: before committing, ALWAYS double check! Make a clean build and make sure that it builds. And don’t forget to add files! It’s a very common mistake: committing good code, but forgetting to add a file. You won’t realize, because the thing builds, but when I’ll checkout, I’ll have troubles, because of missing file(s). If you’re using Darcs, just make a “darcs get” in a new directory, and then build.
There are some ways to handle branches, but here’s my favorite. The most of the work should happen in the trunk, which is always sane, as stated by the previous practice, and the patches should always be small, so that they can be reviewed very easily. If you find yourself in the situation of needing to write a large patch, then you should branch it. In that way you can have small patches that will break your branch over the time, but they can be easily reviewed. After the process is completed, i.e. you’ve achieved your goal of fixing a bug or implementing a new feature, you can test the branch thoroughly, and then merge it to the trunk.
并行軟件開發是企業級環境下軟件開發的一種不可避免的模式,這種開發模式可以說是任何大中型軟件產品和項目所必需的。然而,并行開發在為我們的開發效率提高保證的同時,也會給我們的開發管理帶來諸多問題:
* 什么時候進行分支?
* 什么時候進行合并?
* 如何選擇有效的分支策略?
* 如何保證不同分支上的代碼同步問題?
* 如果建立對分支訪問控制的授權機制?
* 如何避免頻繁的合并沖突?
* 如何處理被復用的代碼?
* ……
可以說并行開發中的分支與合并是一個涉及到環境、方法和技術平臺等諸多因素的綜合性難題,在這種場合下,“模式(Pattern)”可能是解決上述 問題的一個很好的工具。所謂模式,其實就是解決某一類問題的方法論。你把解決某類問題的方法總結歸納到理論高度,那就是模式。Alexander給出的經 典定義是:每個模式都描述了一個在我們的環境中不斷出現的問題,然后描述了該問題的解決方案的核心。通過這種方式,你可以無數次地使用那些已有的解決方 案,無需在重復相同的工作。
在不同的領域有不同的模式,具體到并行開發領域,“分支模式”是專門針對并行開發環境下分支及合并作業中的各種不同的操作方法抽象出來的一套方法論。其主要由以下幾部分組成:
結構模式——通過約束和指導分支/代碼線的整體結構,實現并行開發的組織結構、開發模式及開發過程的約束和指導。
規則模式——通過對特定分支/代碼線實施的約束,實現對該分支/代碼線相關的操作進行約束,如訪問控制及合并等操作的約束。
創建模式——提供對分支/代碼線創建的約束
反模式——以反例的方式展示并行開發中常見的行為誤區和陷阱,并提供有效的解決方案。
分支模式在并行開發中的應用難點有兩個:一是如何根據企業的實際情況選擇適合的分支模式,二是如何構建一個技術平臺來將這些分支模式的理論和方法有 效的應用于實踐。為此,我們專門在此開辟專欄和大家分享并行開發中分支模式相關的理論、方法以及如何將這些理論和方法付諸實現的相關實踐。
一、分支模式的相關定義
模式 主線
別名 主干、主錨線、本線、地線(Main Trunk, Main Anchor Line, Home Line, Ground Line )
場景
在開發和維護周期中,因為各種原因需要創建多條代碼線,典型的代碼線是發布線、維護線和集成線。這在采用每發布代碼線、并行維護/開發線和重疊發布線 (或者其任何變形模式)的情況下尤為如此。隨著項目的進行,會創建出越來越多的代碼線,從而導致項目的版本樹越來越寬。
連續的瀑布式分支(應該避免的情況)
問題
怎樣確保當前活動代碼線的數量在可控范圍內,以及避免項目的版本樹過寬過深?
動機
解決方案
在每一個分支樹中保持一個“主”分支或代碼線作為主干,而不是持續的瀑布式從分支創建分支從而使分支樹變得寬廣而笨重(在每一對父子分支之間需要大量的同步工作)
擁有主線的瀑布式分支
當為主發布創建代碼線的時刻來到時,我們不會從前一個發布線創建新的發布線,而是將先前的發布線合并回主線,然后再從主線創建新的發布線。
為特定代碼線或分支合并回來的過程被稱為“主線化”、“主干化”、“歸位”、“錨定”或“接地”("mainlining," "trunking," "homing," "anchoring," or "grounding.")。
變種 穩定接收線(也稱為穩定主線、主集成線和基礎集成線(Stable Mainline, Main Integration Line, Base Integration Line))
保持一個穩定的,可靠的主要開發主干可以用來從其他代碼線導入(接收)穩定基礎。在代碼線上不會直接發生開發工作,所有集成工作必須來自其他代碼線(不是一個單獨的離散的活動分支)。這個規則唯一的例外是為了保證代碼線構建和功能一致性的集成變更。
(用以接收穩定基線的穩定主線——實際上就是通常所說的基線)
LAG開發線(也稱為主開發線、中央線和主流)
把主干作為最新的最好的(LAG)會一直進展的開發線,所有前面的發布的代碼線都會在其退休后被合并入其中。主干作為下一步/最后的開發發布(不是 維護,而是開發,包括顯著的改進和新特性)的開發線使用,因此當B2發布的工作已經準備好開始,而A1發布已經完成或逐漸停止,則建立一個新的分支來結束 A1的工作(見延遲分支),而LAG線則用來針對B2(最新的和最好的開發工作)發布的工作。
(LAG開發主線)
對于特定代碼線或分支合并到滯后(LAG)開發線的過程也可以稱為"LAGging," "mainlining," "LAG-lining," or "mainstreaming."。
盡管作為一個主線使用,LAG線也可以作為主線與穩定接收線結合:
(有一條穩定主線用于接收發布版本的LAG開發主線)
二、模式的分析
主線模式及其變種主要試圖從分支的結構上約束其不合理的擴展和延伸,而盡量使主線成為其他代碼線創建的來源及合并的目標。穩定接收線(也就是通常所 說的基線)用于接受并保存相對穩定的版本,通常情況下其只接受版本(通常情況下為直接保存版本的鏡像而非合并操作)而不進行其他任何操作。
三、主線模式在Subversion環境下的實現
如上圖所示實際上就是有一條基線伴隨的LAG開發線模式,實現該模式相關的約束包括:
1、所有的代碼線(不包括分支)都從主線創建
2、主線以外的代碼線的合并操作都以主線為目標
3、基線只接受版本(直接保存版本的鏡像而非合并操作)而不進行其他任何操作。
一、分支模式的相關定義
模式 寬松訪問線(Relaxed-Access Line)
問題 如何確定代碼線訪問控制規則的限制或排他程度?
動機
解決方案
如果代碼線是用來開發或維護(而不是排它或集成),并且工作在代碼線上的團隊規模相對較小,人員富于經驗并可靠,那么給開發者有相對寬松自由的區 域,讓他們做他們已經知道如何去做的事情:在一起以及時的方式工作。使用最小檢查和控制,但是要確保代碼線所有者可以認真對待他的工作,并可以一直知道代 碼線的狀態以及確認完整性是否被特定風險/復雜開發任務危害。
相關模式
MYOC(合并你自己的代碼)及其變種PYOC(傳遞你自己的代碼)是使用寬松訪問線最常見的副產品(或原因),如果在你的環境中,風險較小,而且開發者合并自己的變更到代碼線的交流通暢,那么我們有充足的理由使用寬松訪問。
二、對模式的分析
這種訪問模式通常適用于沖突不嚴重(如開發初期或彼此按模塊獨立開發)的情況,要求相關人員具備一定的水準以保證開發的質量,而在開發的后期通常不適用這種模式。
三、寬松訪問線(Relaxed-Access Line)在Subversion環境下的實現
如上圖所示(相關內容只是相關模式實現的一個實例,實際使用時可根據實際需求對角色及授權進行調整):
1、代碼線的所有者對代碼線擁有完全的操作權限
2、主管及EPG成員僅對代碼線擁有有限的操作權限(讀)
3、除測試人員以外的項目成員都對代碼線擁有完全的讀寫權限
4、代碼線的所有者以及項目經理和配置經理擁有對代碼線鎖的權限
一、分支模式的相關定義
陷入的誤區 大爆炸集成
別名 大怪獸集成
癥狀
由于某種原因,一直不選擇集成,直到(軟件)要發布的時候 ,才把所有的分支一下子全部交給倒霉的集成者進行集成。經常性的增量集成看起來是流行的常識性規則(亦稱為:盡早且經常合并),大爆炸(的方式)顯然在隔 離和避免風險方面達到了極致,這是大怪物地結束合并,結果不是一個“大爆炸”,而是以“哭泣”告終。
大爆炸式的集成(需要避免的反模式)
原因
一個原因是我們未能成功找到盡早集成和經常集成的地正確“節奏”和“脈搏”。有時‘大怪獸式集成’是“整合恐懼癥”帶來的附加品,在盡力縮小合并數 量的時候,結果會是一個延期而至的可怕的復雜合并的結尾。因而,在這種情況下,“集成是魔鬼” 變成了自我實現的一種預言,并且在失敗的惡性循環中不斷加強。
影響
太多人都很熟悉所造成的后果,直到一切已經太晚,如系統不能正確的構建,無法通過測試,或部分代碼與其它代碼工作不能協同工作,我們才能發現這問 題。直到項目生命周期的下一個階段,這些信息才得到交流,相關的風險和返工將會非常巨大,會導致“合并是魔鬼”或“合并恐懼”的心理。
修復和預防
使用盡早經常性的集成或使用一個或更多的變種來確保以一定頻率和間隔執行集成,以在時間充裕,額外工作較少時盡早分解風險和盡快交流問題區域。你可 以現在或者將來,或者由你自己決定何時付出,有規律的經常的集成可以迫使你在每次迭代和集成計劃中只需要付出很少的努力,通過分解減少隨時間積累的合并負 擔,從而定義了健康項目的“脈搏”。
二、模式的分析
這種大爆炸式的集成在缺乏有效管理的團隊中是經常發生的,我們經常可以看到這樣的場景:明天就要發版本了(尤其是非計劃的發布),今天所有的相關人員都一起合版本,于是大家驚訝的發現系統出現無數的合并沖突和缺陷,而在這種情況下,延遲發布幾乎是唯一的選項了。
要避免這種最好的方式就是通過某種機制約束分支的周期和合并。
三、大爆炸式集成在Subversion環境下的規避方式
如上圖所示,在創建分支時添加如下約束:
1、創建分支時定義分支的周期(通常任務分支都是需要及時終結的),并強制終結
2、創建分支時定義分支的合并周期和約束方式(包括提醒和強制合并兩種可選方式)
通過上述的約束,可以使分支/代碼線及時的合并和終結,從而避免大爆炸式集成的發生
一、分支模式的相關定義
模式 代碼線所有權(Codeline Ownership)
別名 分支所有權(Branch Ownership )
場景
作為一名程序員,在一組多代碼線的環境下,并至少在一條代碼線上開發。代碼線規則已經為該代碼線定義好檢入/檢出的規則。有些人要在代碼線上進行某些工作,但是該規則并沒有允許這樣的操作,或者就是規則對一些特定事務的描述含糊不清。
問題
能影響代碼線的動作能否執行?在保證代碼線的完整性和連續性的同時,怎樣做上面的決策?
動機
解決方案
為每個代碼線分配一名所有者(owner),其相應的職責如下
所有權(Ownership)并不一定意味著排他式的訪問,但卻表示用戶認證訪問控制。也許只有代碼線的所有者才能檢入文件(受限訪問線);或者, 其他人只要在檢入前獲得代碼線所有者的同意,即可檢入代碼線,又或者在檢入后立刻通知代碼線所有者(寬松訪問線)。代碼線規則必須清楚地定義訪問控制類型 相對應的所有權類型。通常來講,在代碼線上工作的開發人員越多,相應的所有權策略也越嚴格。同樣地,限制程度與代碼線包含活動的風險性或是復雜性,或是對 穩定性的要求成正比。在較小的項目和團隊中的代碼線中所包含較少的關鍵任務,在保證代碼線完整性和連續性的前提下,提供比較隨意,限制較少的訪問控制策 略。
變種 代碼線專屬(Codeline Dictatorship )
代碼線的所有權中極其嚴格的一種形式,其配置項的檢出和分支都是嚴格受限的,當然更包括檢入。專屬者可能是一個人,或是一個小組。一個常見的例子就 是“遠程開發線”。遠程開發人員可能被禁止從非遠程分支中創建新的版本或是分支,因此,本地分支僅僅是“主人(master)”開發的地方。這實質上就是 ClearCase Multisite定義的“分支主人身份(branch mastership)”的概念。
導致的場景
二、模式的分析
代碼線所有權/代碼線專屬模式強調的是代碼線所有者對代碼線的控制權,適用于由專人(或角色)對代碼線內容進行全權負責的情況下使用
三、代碼線專屬(Codeline Dictatorship )在Subversion環境下的實現
如上圖所示(相關內容只是相關模式實現的一個實例,實際使用時可根據實際需求對角色及授權進行調整):
1、代碼線的所有者對代碼線擁有完全的操作權限
2、主管、項目經理及配置經理僅對代碼線擁有有限的操作權限(讀)
3、代碼線的所有者擁有再授權的權限
一、分支模式的相關定義
模式名稱 代碼線規則
別名 每代碼線規則
適用環境 使用多條代碼線開發軟件的情況下。
問題 開發人員如何知道需要將他們的代碼存入哪條代碼線中,并且何時保存?
動機
解決方案
除了給分支/代碼線起一個有意義的名稱之外,要給每條代碼線明確目的,并使用簡捷明了的策略描述其目的。其中應該包括以下一些要點:
讓規則簡短扼要:一個簡單的經驗方法是1-3段(各自25行25個字符,一頁絕對是上限)。
請切記不是所有的代碼線策略都需要上面所有的信息,只需要制定自己所需要的。一些版本控制工具允許在每個分 支、代碼線的名稱上附加詳細的注解,這是存放合適簡短代碼線規則描述的理想地方。開發者可以通過包含代碼線名稱的命令來查看代碼線規則,而無需在別的地方 找文檔。否則,將代碼線規則放在大家都知道的隨手可得的地方(或許提供簡單的命令或宏,對于給定代碼線名稱可以快速顯示規則)。
二、對模式的分析
代碼線規則這種模式實際上就是一種最基本的分支/代碼線使用規范,它強調每條分支/代碼線都應該以快捷而有效的方式記錄其相關的信息,并且這些信息可以隨時被方便的訪問。
作為更進一步的要求,除了將相關信息記錄在案,在某些情況下對其中部分內容(如分支的周期及合并的頻率等)進行提醒甚至約束也是有其必要性的。
三、寬松訪問線(Relaxed-Access Line)在Subversion環境下的實現
如上圖所示:
1、每條分支/代碼線代碼線創建時都有效的記錄相關信息
2、對分支的生命周期和合并周期提供約束控制
注:上述功能的實現是基于在系統底層屏蔽了所有不受控的分支創建操作,而只能在特定應用系統內進行分支/代碼線的創建,從而使所有分支/代碼線相關操作都處于受控狀態