成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Git 優(yōu)秀實(shí)踐,這樣用就對(duì)了

系統(tǒng)
像git這樣靈活的系統(tǒng),達(dá)到同個(gè)目的往往存在多條路徑。這里提到的這些git最佳實(shí)踐,希望能幫助朋友們找到路徑中最優(yōu)的一條。

作者 | minmingong

很多git的操作,都有多種方法達(dá)到目的。但其實(shí)往往其中只有一種是最佳的。

Git是個(gè)超級(jí)強(qiáng)大也非常流行的版本控制系統(tǒng)(VCS)。它的設(shè)計(jì)理念和其他VCS非常不同??v觀整個(gè)業(yè)界,很多人在用舊的思維方式來解決git的使用問題,有svn方式的、p4方式的、奇怪方式的、錯(cuò)誤方式的,等等,而不是更新成git的思維方式。雖然git非常靈活,確實(shí)可以用這些方式來使用,但其實(shí)操作起來反而更難,而且效率更低,吃力不討好。這里我打算把二十多年的各種版本控制系統(tǒng)的使用經(jīng)驗(yàn)和十多年git的使用經(jīng)驗(yàn),總結(jié)出一些git的最佳實(shí)踐。其實(shí)很多時(shí)候,正確的做法比錯(cuò)誤的更簡(jiǎn)單,更不容易出錯(cuò)。

一、什么是Git

不開玩笑。最常見的Git錯(cuò)誤使用,正是來自于沒意識(shí)到git是什么。大部分git的屬性,可以從定義用邏輯推導(dǎo)出來。邏輯是最重要的,只要邏輯錯(cuò)了,就一定是錯(cuò)了。哪怕所有人都這么做,也是錯(cuò)的。

Git是一個(gè)分布式版本控制系統(tǒng),跟蹤目錄里的修改。它的工作流是非線性的(不同電腦上的平行分支形成了一個(gè)graph)。和主從式的系統(tǒng)不一樣的是,每臺(tái)電腦上的每個(gè)git目錄都是一個(gè)完整的repo,包含全部歷史和完整的版本跟蹤能力。(LFS是個(gè)例外,后面會(huì)提到。)

因?yàn)間it的本質(zhì)是一個(gè)基于目錄的分布式VCS,這里面并沒有中心服務(wù)器的角色。去中心化是未來。同個(gè)項(xiàng)目的所有repo都是平等的端點(diǎn)。一個(gè)repo可以在服務(wù)器、本地目錄、其他人的電腦上。只是為了團(tuán)隊(duì)協(xié)作的目的,會(huì)認(rèn)為指定一個(gè)或多個(gè)端點(diǎn)作為”服務(wù)器“。是的,可以同時(shí)有多個(gè)上游服務(wù)器。很多時(shí)候這么做很有必要。比如對(duì)內(nèi)開發(fā)的repo和對(duì)外開源的repo,就是兩個(gè)不同的端點(diǎn)。可以有不同的分支和推送頻率。本地只要一個(gè)repo就都管理了。

非線性的工作流表示提交和分支操控是一個(gè)常規(guī)的操作。建立分支、rebase、修訂commit、強(qiáng)制推送、cherry-pick、分支復(fù)位,在git都是很正常的使用方式。

二、什么不是Git

很多東西經(jīng)常和git一起出現(xiàn),但是并不是git的一部分。

1.Github/Gitlab

這些都不是git,而是提供git服務(wù)和社區(qū)的網(wǎng)站。Git是個(gè)基于目錄的VCS,并不需要網(wǎng)站服務(wù)或者網(wǎng)絡(luò)訪問才能工作。早期經(jīng)常有人沒法區(qū)分github和git。當(dāng)要說git的時(shí)候,會(huì)說github,制造的混亂不是一星半點(diǎn)。

2.Fork

Fork仍然也是git服務(wù)網(wǎng)站的功能,用來簡(jiǎn)化協(xié)作流程。在沒有fork的時(shí)候,如果你想往開源項(xiàng)目里修bug或者加feature,會(huì)需要這樣的流程:

  • 克隆repo
  • 修改代碼
  • 生成補(bǔ)丁
  • 發(fā)到論壇或者支持的郵件列表
  • 找作者來review,合并補(bǔ)丁

很多項(xiàng)目到現(xiàn)在還是這么做的。如果有了fork,可以簡(jiǎn)化成:

  • Fork并克隆repo
  • 修改代碼
  • 發(fā)出merge request或者pull request

雖然fork很有用,但這仍然不是git的一部分。它用到的是git的分布式能力。本質(zhì)上,在fork的時(shí)候,它會(huì)克隆一份repo,把原來的repo設(shè)置成上游。所以其實(shí)如果你的目標(biāo)不是為了繼續(xù)把repo放在網(wǎng)絡(luò)服務(wù)上,那就克隆到本地就是了。太多的人把fork當(dāng)作like來用,根本就是錯(cuò)的。如果沒打算改代碼,fork是沒意義的。機(jī)器學(xué)習(xí)界這個(gè)問題尤其嚴(yán)重。經(jīng)常放一個(gè)README就假開源了,還有幾百個(gè)fork,都不知道能fork到什么。

3.Merge request/Pull request

Github上叫Pull Request,gitlab上叫merge request,其實(shí)是一個(gè)東西的不同視角。這些都是code review和合并的流程,不是git的一部分。

需要注意的是,它們的重點(diǎn)在“request”,而不是merge或者pull。如果你要把一個(gè)分支merge到你自己的,沒必要開一個(gè)MR然后自己給自己通過。在本地merge就是了,更簡(jiǎn)單更快。

4.Import

很多git服務(wù)支持“Import”,用來從別的git、svn、cvs、p4等VCS導(dǎo)入一個(gè)庫。如果原本的repo已經(jīng)是git,那直接push到新的地方就是了,比import更簡(jiǎn)單。而且這樣絕對(duì)不會(huì)丟失歷史記錄或者搞錯(cuò)文件。如果是其他VCS的repo,那也可以用插件或腳本來先轉(zhuǎn)成一個(gè)本地的git repo,然后再push到新的地方。

三、對(duì)工具

Git本身是個(gè)命令行工具。但是,非線性工作流的本質(zhì)就讓它沒可能在字符界面顯示出分支的graph。選個(gè)好的GUI非常關(guān)鍵。不但可以大幅度增加工作效率,更重要的是,減少出錯(cuò)的機(jī)會(huì)。第二個(gè)常見的git使用錯(cuò)誤來源,正是因?yàn)橛缅e(cuò)了工具造成了。

Windows上最好的git GUI是TortoiseGit,沒有之一。它只是個(gè)GUI,git命令行需要事先安裝。和其他Tortoise打頭的工具(TortoiseCVS、TortoiseSVN)一樣,它的風(fēng)格是沒有主UI,而集成到Windows的文件管理器里面。Repo里的文件(也就是目錄里的)圖標(biāo)上會(huì)覆蓋上狀態(tài)。右鍵點(diǎn)擊這個(gè)目錄,菜單里可以看到TortoiseGit的子菜單,包含git的一些操作。大部分VCS的GUI工具,比如P4V、SourceTree,UGit,都有個(gè)主UI顯示映射了的工作空間,而不是目錄本身。對(duì)于git來說,這其實(shí)是個(gè)錯(cuò)誤,因?yàn)間it是基于目錄的,不存在工作空間這個(gè)概念。而且,這種情況下非常常見的錯(cuò)誤就是忘記提交新增的文件。在TortoiseGit里,除了蓋在圖標(biāo)上的狀態(tài)之外,提交窗口也可以顯示出哪些文件還沒添加,不會(huì)出現(xiàn)遺漏的情況。

另外,TortoiseGit有一個(gè)獨(dú)特的版本graph查看器,里面可以顯示出repo的整個(gè)分支結(jié)構(gòu)。通過這個(gè)查看器,可以很方便地看出來repo是怎么成長的,有那些不必要的分支,如何從一個(gè)分支跳到另一個(gè),等等。這是TortoiseGit比其他git UI好的一個(gè)重要原因。不管是Visual Studio里的、SourceTree、還是UGit,在UI設(shè)計(jì)上都像用傳統(tǒng)的VCS思路來套用到git上,而不是git的思路。主它們的共同問題就是,基本只關(guān)注于當(dāng)前分支。而有能力同時(shí)看所有分支,對(duì)git來說非常重要,因?yàn)間it的工作流是非線性的。

其他高級(jí)功能,比如打補(bǔ)丁、處理submodule(非常重要),都可以在TortoiseGit的GUI里完成。但它沒法覆蓋所有的功能。有些很少用的,還是得通過命令行。

四、盡量在本地

所有的git操作都可以在本地repo上完成,因?yàn)榉?wù)端的并沒有更高優(yōu)先級(jí)。雖然大部分提供git服務(wù)的網(wǎng)站都在網(wǎng)頁界面里有cherry-pick、新建分支、合并這些操作,但是在本地執(zhí)行更容易,而且比在服務(wù)端執(zhí)行了再拉下來要更快。

五、分支策略

Git的工作流是基于分支的。不但每個(gè)repo是平等的,每個(gè)分支也是。Master/main、develop這些只是為了簡(jiǎn)化管理而人工指定的有特殊含義的分支。這里的分支策略是為了更好地協(xié)作而產(chǎn)生的習(xí)慣規(guī)范,不是git的工作流本身必須定義的。分支可以分為幾個(gè)層次。

1.Main分支

這是整個(gè)項(xiàng)目的穩(wěn)定分支,里面的內(nèi)容可能相對(duì)較老,但是這個(gè)分支里的內(nèi)容都是經(jīng)過測(cè)試和驗(yàn)證的。原先都叫master,因?yàn)檎握_的要求,最近越來越多新項(xiàng)目開始用main。有些快速開發(fā)的項(xiàng)目甚至不采用main分支。

2.Develop分支

開發(fā)主要發(fā)生在develop分支。新特性先放到這個(gè)分支,再去優(yōu)化和增強(qiáng)穩(wěn)定性。

3.大項(xiàng)目可選的團(tuán)隊(duì)develop分支

對(duì)于跨團(tuán)隊(duì)的大項(xiàng)目,每個(gè)團(tuán)隊(duì)都有自己的興趣點(diǎn)和發(fā)布周期。很常見的做法是,每個(gè)團(tuán)隊(duì)有自己的develop分支。每過一段時(shí)間合并到總的develop分支。一般來說,中等大小的團(tuán)隊(duì),專注于repo的某一部分,可以采取這樣的分支形式。小團(tuán)隊(duì)或者個(gè)人沒有必要有自己的develop分支。那樣反而會(huì)浪費(fèi)時(shí)間和增加合并過程中的風(fēng)險(xiǎn)。

4.Feature分支

Feature分支是生命期很短的分支,專注于單個(gè)特性的開發(fā)。和其他VCS不一樣的是,在git里開分支開銷非常低,所以可以高頻地開分支和合并分支。在做一個(gè)特性的時(shí)候,常規(guī)的流程是這樣的:

  • 從develop分支上新建一個(gè)feature分支
  • 提交一些關(guān)于這個(gè)feature的代碼
  • 合并回去
  • 刪除這個(gè)feature分支

對(duì)于本地repo里的feature分支,你可以做任何事。常見的用法是在開發(fā)過程中非常頻繁地提交,走一小步就提交一次。在發(fā)出MR之前,先合并成一個(gè)commit,把這個(gè)分支變整潔,方便后續(xù)操作。

當(dāng)feature分支合并之后,絕對(duì)不存在任何理由讓這個(gè)分支仍然存在于服務(wù)器上。WOA現(xiàn)在有自動(dòng)刪除的選項(xiàng),可以設(shè)置成默認(rèn)開啟。但有時(shí)候仍然會(huì)出些問題,這個(gè)選項(xiàng)會(huì)消失,需要手工刪除分支(其實(shí)就是在MR頁面上點(diǎn)一下的事)。記?。悍?wù)器上只是一個(gè)端點(diǎn),刪掉那邊的一個(gè)分支不會(huì)影響你的本地repo。如果你有后續(xù)工作需要在那個(gè)分支上做,就繼續(xù)在你本地的分支上完成就是了。這和服務(wù)端有沒有這個(gè)分支一點(diǎn)關(guān)系都沒有。

因?yàn)槊總€(gè)分支都是平等的,可以推出在任何一個(gè)分支上都可以新建分支。比如,如果特性B依賴于特性A,你不用等特性A合并了才開始做特性B。只要在特性A的分支上建立一個(gè)特性B的分支就可以了,即便特性A不是你的分支也可以。等到特性A合并了,把特性B的分支rebase一下就是了。少了等待環(huán)節(jié),效率提高很多,也不必催人做code review。

能建立大量feature分支,對(duì)于提高工作效率非常關(guān)鍵。每個(gè)特性建立一個(gè)feature分支,在上面完成特性,發(fā)出MR。在code review通過之前,已經(jīng)可以新建另一個(gè)特性專用的feature分支,切換過去,開始做另一個(gè)特性。在code review過程中還能來回切換,同時(shí)做多個(gè)特性。其他VCS是做不到這一點(diǎn)的,效率也自然低很多。

5.Release分支群

Release不只是一個(gè)分支,而是一群以“release/”打頭的分支。就好像一個(gè)目錄,包含了不同版本給不同產(chǎn)品線的release分支。一般來說他們從main或者develop分支出來。當(dāng)發(fā)現(xiàn)一個(gè)bug的時(shí)候,在main或者develop分支修好,然后cherry-pick到release分支里。這種單向的處理可以方便管理,并且不用擔(dān)心某個(gè)commit是不是只有release分支有。Release分支經(jīng)常在每個(gè)sprint的開頭創(chuàng)建,包含這個(gè)sprint要發(fā)布的東西;或者在每個(gè)sprint的結(jié)尾創(chuàng)建,包含下一個(gè)sprint要發(fā)布的東西。

四、Merge還是rebase

雖然在提及把commit從feature分支放到develop分支的時(shí)候,我們一直說”合并“,但其實(shí)這里存在兩個(gè)維度。是的,不是有兩個(gè)操作,是有兩個(gè)維度。

第一個(gè)維度,是merge還是rebase。這是兩種”合并“的方式。第一種是普通的合并,和傳統(tǒng)的VCS一樣。它會(huì)把一個(gè)分支合并到目標(biāo)分支,在頂上建立一個(gè)commit用來合并,兩個(gè)分支里已有的commit不會(huì)有變化。

另一個(gè)就是rebase。它會(huì)從分支分出來的地方切開,嫁接到目標(biāo)分支的頂端上。(我一直認(rèn)為rebase應(yīng)該翻譯成嫁接,而不是“變基”。)

第二個(gè)維度是是否squash,也就是選擇一個(gè)分支里的一些commit,壓扁成一個(gè)commit。這個(gè)任何時(shí)間都能做,即便不是為了合并也行。在TortoiseGit里,這叫“combine into one commit”。

兩個(gè)維度組合之后,我們就得到了4個(gè)操作。但是“squash再merge”沒有任何意義,所以就剩下”不squash就merge“, ”不squash就rebase“,以及”squash再rebase“。(微軟的devops文檔曾經(jīng)有個(gè)嚴(yán)重的錯(cuò)誤。里面描述成merge表示不squash就merge、rebase表示squash在rebase,而沒有把它們當(dāng)作兩個(gè)維度來看。是我在2018年左右提出了這個(gè)問題,并且要求他們修改,還提供了多個(gè)圖片解釋它們到底有什么區(qū)別。過了大概半年之后才改成對(duì)的。但很多人就是從那里學(xué)的git,都被帶壞了。)

其實(shí)還可以有第三個(gè)維度,修訂與否。但這個(gè)更多的是發(fā)生在merge之前的過程。修訂,amend,表示當(dāng)提交的時(shí)候,是不是要覆蓋掉上一個(gè)commit。打開的話,提交之后還會(huì)只有一個(gè)commit,而不是兩個(gè)。

關(guān)閉amend

打開amend

現(xiàn)在的問題就是,什么時(shí)候用什么。要是要處理的是長生命周期的分支,比如團(tuán)隊(duì)的develop分支、develop分支、main分支,合乎邏輯的選擇是merge。因?yàn)樗鼈兊慕Y(jié)構(gòu)需要保留,而且合并后分支也不打算消失。

對(duì)于feature分支,不同團(tuán)隊(duì)可以有不同選擇。這里我只說最高效,開銷最低的。一個(gè)feature分支里可以有多個(gè)commits,但它們只有合在一起的時(shí)候才會(huì)成為一個(gè)feature。中間的commit以后就再也用不到了。留著只會(huì)浪費(fèi)空間和時(shí)間。所以邏輯上,這些commit就需要被squash。這時(shí)候如果merge一個(gè)只包含一個(gè)commit的分支,就會(huì)出現(xiàn)這樣的graph:

這里有個(gè)什么都不做的commit,只是把兩個(gè)分支抓在一起,以及一個(gè)永遠(yuǎn)掛在外面的commit。即便git里開分支和合并的開銷很低,但這會(huì)一直積累的。這里用merge,就完全是在浪費(fèi)時(shí)間和空間。對(duì)于feature到develop的合并來說,rebase是最佳選擇。

現(xiàn)在,如果早晚需要把多個(gè)commit合成一個(gè),那就該用amend。是的,大部分時(shí)候,一路amend過去,比最后才來squash更好。首先,rebase一個(gè)commit,會(huì)比rebase一串來得容易得多,特別是有代碼沖突的時(shí)候。其次,如果MR的最后才squash & merge,那commit的消息就是沒有經(jīng)過review的,增加了犯錯(cuò)的風(fēng)險(xiǎn)。(是的,非常經(jīng)常發(fā)生)

所有這些操作都可以在本地完成。這比在Web UI上操作遠(yuǎn)程的repo要容易而且高效??偨Y(jié)起來,這里的最佳實(shí)踐是:

  • 在開發(fā)過程中可以用commit或者amend commit
  • 在發(fā)出MR的時(shí)候squash成一個(gè)commit
  • 在MR的迭代內(nèi)持續(xù)用amend commit
  • 在MR通過后用rebase進(jìn)行合并

(其實(shí),p4里面的每一次submit,都是amend + rebase。之前只是因?yàn)闆]有人告訴你這個(gè)事實(shí)。而且p4里只有一種submit的方式,沒有思考和選擇的空間,做就是了。但這絕不代表不需要思考“有沒有更好的做法”這個(gè)問題,這非常重要。)

更復(fù)雜的情況是在跨公司的repo上工作,比如UE。這時(shí)候規(guī)則需要做一些改變。一般來說,這種情況下你的feature分支是從release分支上建出來的,而不是develop分支。而且這種feature分支其實(shí)是作為develop分支來用,有長的生命周期。這時(shí)候,如果你要把一個(gè)特性從比如UE 5.1移植到5.2,rebase就不是最佳選擇了。因?yàn)槟菢拥脑挄?huì)把5.1 release分支里的所有commit和你的所有feature commit一起rebase。而你真正想要的是只把你的commit給cherry-pick過去。這其實(shí)還是因?yàn)楣ぞ?。如果用的是TortoiseGit,就不會(huì)有這個(gè)疑惑。因?yàn)槔锩鎟ebase默認(rèn)是交互式的。你可以精確選擇哪些commit需要操作。這就讓rebase和cherry-pick變成一樣的東西。唯一的區(qū)別,是rebase是讓git選一個(gè)commit的列表,讓你從中選哪個(gè)要哪個(gè)不要。而cherry-pick是讓你直接選commit的列表。

五、處理合并沖突

當(dāng)出現(xiàn)合并沖突的時(shí)候,最好的方式是先把你的feature分支rebase到目標(biāo)分支的頂端,這時(shí)候解決沖突,然后force push。如果用WOA的沖突解決(可能有些別的基于web的git服務(wù)也有),它會(huì)每次都做merge。結(jié)果經(jīng)常把簡(jiǎn)單的單個(gè)commit rebase,變成了復(fù)雜的三分支合并。

1.常見錯(cuò)誤:解決合并沖突后建了個(gè)新的MR

因?yàn)闆_突解決的錯(cuò)誤行為,有可能在解決之后,修改被提交到了一個(gè)新的分支。這時(shí)候應(yīng)該把你的分支reset到新的去,force push,再刪掉新的;而不是關(guān)掉原先的MR,在新分支上開個(gè)新MR。

2.常見錯(cuò)誤:把分支搞亂

如果真的遇到了多分支復(fù)雜交錯(cuò)的情況,有兩個(gè)方法可以嘗試清理出來。

  • 強(qiáng)制rebase。Fetch一下整個(gè)repo;把你的分支rebase到目標(biāo)分支上的時(shí)候勾選force;這時(shí)候在列表里選要拿去rebase的commit。大部分時(shí)候這都能行。但有時(shí)候git因?yàn)榉种e(cuò)綜復(fù)雜而搞不清楚commit,在列表里會(huì)有遺漏。
  • Cherry-pick。在目標(biāo)分支上新建一個(gè)臨時(shí)分支;把有用的commit都cherry-pick過去;把你的分支reset到那個(gè)臨時(shí)分支上;最后刪掉那個(gè)臨時(shí)分支。

兩個(gè)方法最后都需要force push。

六、不要pull,要fetch

很多教程都說push和pull是在本地和遠(yuǎn)程repo之間同步的指令。但是其實(shí)push是基礎(chǔ)指令,pull不是。它是fetch當(dāng)前分支->和本地分支合并->reset到合并后的頂端。這里就產(chǎn)生了不必要的合并。你可以打開rebase pull,這就簡(jiǎn)化成fetch當(dāng)前分支->rebase本地分支。

好一些,但是每次pull的時(shí)候都會(huì)開啟rebase的窗口,即便沒什么好rebase的。其實(shí)如果改用手動(dòng)運(yùn)行fetch和rebase,同樣的工作量可以獲得更多。因?yàn)槟J(rèn)的fetch可以拿到所有分支,而不是只有當(dāng)前分支。然后你可以決定哪個(gè)分支rebase到哪里。整個(gè)過程中都可以保證沒有錯(cuò)誤的merge發(fā)生。

七、小而完整的commit

每個(gè)commit都該小而完整,有些人把這個(gè)叫做”原子性“。不要把多個(gè)特性壓到一個(gè)commit里,同時(shí)不要有一堆必須合起來才能用的commit。

1.常見錯(cuò)誤:一個(gè)commit里做多件事情

這是一個(gè)非常常見的錯(cuò)誤。一個(gè)大的commit包含多個(gè)任務(wù)的代碼。這樣的commit必須要拆成多個(gè)才行。在git里,這樣的拆分比較容易。如果一個(gè)分支“Feature”包含了特性A和特性B的代碼,那么,

  • 在“Feature”的頂端建立“Feature A”和“Feature B”兩個(gè)分支
  • 切換到“Feature A”分支,刪掉其中特性B的代碼,開amend提交
  • 把“Feature B”分支rebase到新的“Feature A”分支

這就行了?,F(xiàn)在兩個(gè)分支都分別只包含一個(gè)特性。如果特性B不依賴于特性A,它還可以繼續(xù)rebase到develop分支去。

2.常見錯(cuò)誤:多個(gè)不完整的commit

另一個(gè)非常常見的錯(cuò)誤是不完整的commit,比如不能編譯、不能運(yùn)行、只包含瑣碎的修改、或者僅僅為了未來的使用而做的修改。這樣的commit只是中間結(jié)果,沒法單獨(dú)存在,需要和其他commit合起來才變成一個(gè)完整的commit。那它們就需要合并之后才發(fā)MR。

3.拆分大的commit

是的,有時(shí)候是需要把一個(gè)大的commit拆分成多個(gè),讓MR更容易看。但是這里的拆分并不能讓commit變得不完整。如果一個(gè)大commit中的一部分,本身就能對(duì)現(xiàn)在的代碼庫有幫助,拿著就能提出來變成一個(gè)獨(dú)立的commit。常見的是獨(dú)立的bug修復(fù)、代碼整理、或者重構(gòu)。

八、LFS技巧

LFS是git里蠻特殊的一部分。為了讓git更好地支持大(二進(jìn)制)文件,LFS其實(shí)讓git的設(shè)計(jì)做了一些妥協(xié)。LFS比git晚了9年發(fā)布,而且花了好多年才讓主流git服務(wù)都提供支持。

1.LFS是怎么回事

保存完整歷史的大文件,特別是大的二進(jìn)制文件超級(jí)占空間和處理時(shí)間。在LFS里,默認(rèn)子保存一個(gè)版本的大文件,歷史則放在另一個(gè)端點(diǎn),一般是服務(wù)器。本地其實(shí)也可以這樣拉取完整的歷史:

git lfs fetch --all

當(dāng)從一個(gè)git轉(zhuǎn)移到另一個(gè)的時(shí)候,會(huì)要求做這件事情。其他時(shí)候一個(gè)版本就夠了。

另外,LFS有加鎖解鎖的功能。但是和主從式的VCS不同的是,加鎖解鎖不會(huì)自動(dòng)擴(kuò)散到所有端點(diǎn)。這還是因?yàn)椴⒉淮嬖谥行姆?wù)器的概念。

2.常見錯(cuò)誤:沒開LFS

非常重要的一件事情是,LFS不負(fù)責(zé)鑒別哪些文件是大文件。在添加大文件之前,它們路徑需要加到.gitattributes里,可以用通配符。一旦路徑在.gitattributes里了,文件操作就會(huì)自動(dòng)通過LFS過濾,不需要額外的手工操作。

但是,如果一個(gè)文件在沒有改.gitattributes之前就添加了,那它會(huì)被當(dāng)作普通文件。要糾正這個(gè),需要把文件路徑放到.gitattributes,然后執(zhí)行:

git add --renormalize .

才能把當(dāng)前目錄下的LFS狀態(tài)修正過來。但歷史里面的沒法改,一旦提交了,大文件就會(huì)永遠(yuǎn)在那邊。通過那樣的方法過濾git庫,刪除不小心提交的大文件非常痛苦。過程中會(huì)有很多手工操作和確認(rèn),但至少這件事情是可做的。在實(shí)際項(xiàng)目中,我曾經(jīng)把一個(gè)野蠻生長到1.6GB的git庫,通過去掉沒開LFS的情況下提交的第三方依賴和數(shù)據(jù),精簡(jiǎn)到了10MB,而且所有歷史記錄都在。其他VCS甚至不會(huì)有機(jī)會(huì)這么做,只能無限增長下去,或者砍掉一段歷史記錄。

3.濫用LFS

另一個(gè)極端就是濫用LFS。把所有的文件都當(dāng)做大文件來添加,這樣git repo就表現(xiàn)成了個(gè)svn。當(dāng)然,git相對(duì)svn的大部分優(yōu)點(diǎn)也沒了,開發(fā)效率下降5-10倍。要進(jìn)一步把效率下降10倍,可以鎖上所有的文件。這樣所有人都需要checkout文件才能編輯。這樣的git repo就退化成了一個(gè)p4庫。(要再次把效率下降10倍,就在同個(gè)項(xiàng)目上混合使用git和p4??梢钥隙ǎ讲涣?0次commit,就會(huì)有人搞錯(cuò),把文件同時(shí)放到兩邊,造成兩邊都混亂。)

4.封裝LFS鎖

剛提到,LFS鎖所有的東西可以很容易把開發(fā)效率下降2個(gè)數(shù)量級(jí)。但是,對(duì)于非編程的工作流,比如美術(shù)工作,反正是沒有diff的操作。這就會(huì)變成加鎖->check out->修改->提交->解鎖,和主從式VCS的工作流一樣。一個(gè)常見的解決方法是寫一個(gè)腳本來加鎖、擴(kuò)散鎖的狀態(tài),另一個(gè)腳本來做提交、解鎖、擴(kuò)散鎖的狀態(tài)。把LFS鎖封裝之后,工作流既可以符合美術(shù)類,也同時(shí)保持編程類工作流的效率。從另一個(gè)角度想這個(gè)問題:git有機(jī)會(huì)封裝成同時(shí)符合編程類和非編程類工作流,保證兩邊的效率;但是svn/p4卻沒可能封裝成提高編程類工作流效率的。

九、Git的缺點(diǎn)

當(dāng)然,git不是完美的,有些地方仍然比其他VCS有些缺點(diǎn)。解決這些問題的辦法,有,但支持并不廣泛。

1.缺乏分支權(quán)限管理

Git沒有內(nèi)建權(quán)限管理(來自于Linus Torvalds的設(shè)計(jì)理念)。當(dāng)一個(gè)人獲得訪問repo的權(quán)限,所有的分支都能訪問到。有些服務(wù)通過控制“.git/refs/heads”下的文件訪問,提供了基于分支的權(quán)限管理。這就能有基本的權(quán)限管理,又不需要修改git。

2.巨型庫(單一庫)

當(dāng)Linus Torvalds設(shè)計(jì)git的時(shí)候,首要目標(biāo)是支持Linux內(nèi)核的開發(fā),需求限于這樣的中等規(guī)模。對(duì)于一個(gè)巨大的項(xiàng)目,git的性能并不好。想想在“git status”的時(shí)候,git需要窮舉目錄下的所有文件,比較當(dāng)前的和repo里的區(qū)別。這肯定會(huì)花不少時(shí)間。

這幾年,git也在這上面做了一些改進(jìn)。Git 2.25里引入的部分clone和稀疏checkout可以讓你不需要把整個(gè)repo都clone或者checkout,只要你需要的一部分子目錄就行。但這些還比較新,不是所有服務(wù)提供方都支持。

要解決存放Android源代碼的需求,Google有個(gè)工具叫“repo”。它可以管理多個(gè)git repo,就好像一個(gè)巨大的repo一樣。這個(gè)工具支持Linux和macOS,但是Windows上基本沒法用。同時(shí),因?yàn)楸举|(zhì)上其實(shí)還是一堆git庫的集合,把文件從一個(gè)git挪到另一個(gè),就會(huì)丟失歷史。Google的另一個(gè)工作是Git protocol v2。它可以加速repo之間傳輸?shù)乃俣取?/p>

微軟的Windows長期以來一直用的fork的p4,叫做source depot(SD),作為版本控制。在2015年的某個(gè)時(shí)候,p4已經(jīng)無法滿足現(xiàn)代的敏捷開發(fā)和協(xié)作的需求,于是考慮切換到git。即便代價(jià)非常大(切換了一個(gè)用了20年以上的系統(tǒng),大量修改bug跟蹤、自動(dòng)編譯、測(cè)試、部署系統(tǒng),培訓(xùn)部門里的每個(gè)人,配發(fā)大容量SSD。),也要堅(jiān)持去做,因?yàn)槎贾肋@才是未來。直接轉(zhuǎn)的話,單個(gè)git庫的大小是270GB,clone一次得花12小時(shí),checkout花3小時(shí),甚至連“git status”都要10分鐘,簡(jiǎn)直沒法用。于是有人開始考慮通過引入一些主從的特性來改進(jìn)git。但因?yàn)樗麄儗?duì)開源社區(qū)的無知,甚至連搜索一下都不,就給這個(gè)東西起名叫g(shù)vfs(git virtual files ystem),全然不顧已經(jīng)有叫這個(gè)名字的知名項(xiàng)目GNOME virtual file system。被詬病了幾年才改名叫VFSForGit。它不是git的直接替代。首先是引入了一個(gè)新的協(xié)議,用于虛擬化repo里的文件。

在克隆的時(shí)候,不用git clone,而用gvfs clone。在.git和工作目錄下的所有文件都只是個(gè)符號(hào)鏈接,指向服務(wù)器上的真實(shí)文件(有了中心服務(wù)器的概念),在本地硬盤上不占空間。然后有個(gè)后臺(tái)駐留程序在監(jiān)視這個(gè)虛擬化。讀文件的時(shí)候,它就把文件內(nèi)容從服務(wù)器取到本地的cache,修改文件的時(shí)候,它就把符號(hào)鏈接替換成硬盤上的普通文件(相當(dāng)于自動(dòng)checkout)。同時(shí)這個(gè)駐留程序還監(jiān)控文件讀寫的操作。如果文件沒有被寫過,就認(rèn)為內(nèi)容不變。這樣就只需要比較被寫過的文件,而不是目錄下所有文件(相當(dāng)于不按內(nèi)容判斷是否相同)。然而,這其實(shí)破壞了git的很多設(shè)計(jì)原則,以及放棄了按文件內(nèi)容決定是否發(fā)生改變的規(guī)則。顯而易見沒可能被官方的git采納。這些對(duì)規(guī)則的破壞,這也使得VFSForGit無法和很多git GUI很好地配合使用,包括TortoiseGit。

因此,微軟換了個(gè)方向,新做了一個(gè)叫做Scalar的系統(tǒng)。這個(gè)就不用虛擬化了,也不會(huì)改變git的工作流。它是以擴(kuò)展的形式,優(yōu)化原有g(shù)it的部分clone和稀疏checkout,不再修改git的基礎(chǔ)。但它的適用性仍然是個(gè)問題。目前只有微軟fork的git和Azure devops支持這個(gè)。實(shí)際上meta和google也一直在等待著git能更好地支持單一巨型庫,并時(shí)不時(shí)嘗試從自己開發(fā)的系統(tǒng)里切換過去。

但是隨著時(shí)間的發(fā)展,總會(huì)有更多改進(jìn)被合并到官方的git去。這個(gè)問題會(huì)慢慢改善。對(duì)絕大部分項(xiàng)目來說,這些問題并不會(huì)遇到,也不會(huì)是問題。

十、總結(jié)

像git這樣靈活的系統(tǒng),達(dá)到同個(gè)目的往往存在多條路徑。這里提到的這些git最佳實(shí)踐,希望能幫助朋友們找到路徑中最優(yōu)的一條。你越是了解git,越能明白邏輯正確的版本控制應(yīng)該是什么樣的,越會(huì)支持git的使用。而正好相反的是p4。你越是不了解p4,越會(huì)支持p4的使用,因?yàn)樗]有給人思考的余地,所以用再久也沒法了解什么是版本控制。

責(zé)任編輯:趙寧寧 來源: 騰訊技術(shù)工程
相關(guān)推薦

2024-06-26 11:55:44

2017-12-05 15:26:19

2017-12-05 13:12:35

Android軟鍵盤參數(shù)

2021-12-29 21:31:23

Windows 11Windows微軟

2021-03-14 09:37:45

Git倉庫管理代碼

2021-02-01 11:22:23

Windows 10Windows微軟

2022-02-21 10:50:28

SvnGitHub分支

2020-06-01 09:40:06

開發(fā)ReactTypeScript

2021-04-21 07:53:12

Java限流器管理

2020-08-12 07:00:00

開發(fā)代碼技術(shù)

2021-11-09 23:10:24

Windows 11Windows微軟

2018-07-01 08:34:09

緩存數(shù)據(jù)服務(wù)

2019-10-08 10:37:46

設(shè)計(jì)技術(shù)程序員

2023-06-27 06:58:38

機(jī)械鍵盤軸體

2024-01-22 12:46:00

KubernetesAPI接口

2020-05-25 11:14:59

代碼程序開發(fā)

2023-02-07 15:33:16

云遷移數(shù)據(jù)中心云計(jì)算

2024-12-12 09:02:35

2022-09-15 08:41:16

數(shù)據(jù)異構(gòu)分庫分表

2019-11-22 15:27:07

技術(shù)漏洞管理網(wǎng)絡(luò)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲国产欧美91 | 日本一本视频 | 在线一区 | 国产免费xxx | 狠狠操电影 | 一级黄色录像毛片 | 国产h在线 | 中文字幕一区在线观看视频 | 国产一区免费视频 | 亚洲高清在线免费观看 | 中文在线一区二区 | 免费久久久 | 欧美午夜激情在线 | 人人九九精| 久久久久国产精品一区二区 | 亚洲 成人 在线 | 欧美亚洲国产一区二区三区 | 久久综合入口 | 亚洲精品一区在线 | 在线小视频 | 国产日韩精品视频 | 丝袜美腿一区二区三区动态图 | av不卡一区| 国产电影一区二区三区爱妃记 | 91免费在线视频 | 成人av一区二区三区 | 国产欧美一区二区在线观看 | 欧美456| 国产在线精品一区二区 | 久久精品这里 | 亚洲在线免费观看 | 久久亚洲精品国产精品紫薇 | 在线成人福利 | 免费看黄色视屏 | 日韩一区精品 | 午夜视频在线观看网站 | 久久久网 | 国产精品一级 | 狠狠草视频 | 亚洲欧美一区二区三区情侣bbw | 欧美日韩一区精品 |