Git的基本概念與入門

      網友投稿 829 2025-03-31

      基本概念


      在本章中,我們將介紹一個分布式版本控制系統的設計思路,以及它與集中式版本控制系統的不同之處。除此之外,我們還將帶你了解分布式版本庫的具體工作方式,以及為什么我們會說,在Git中創建分支和合并分支不是個大不了的問題。

      1 分布式版本控制,有何過人之處

      在具體探討分布式版本控制的概念之前,讓我們先來快速回顧一下傳統的集中式版本控制架構。

      圖1中所顯示的就是一個集中式版本控制系統(例如CVS或Subversion)的典型布局。每個開發者都在他或她自己的計算機上有一個包含所有項目文件的工作目錄(即工作區)。當該開發者在本地做了修改之后,他或她就會定期將修改提交給某臺中央服務器。然后,開發者在執行更新操作的同時也會從該服務器上撿取出其他開發者所做的修改。這臺中央服務器上存儲著這些文件(即版本庫)的當前版本和歷史版本。因此,這些被并行開發的分支,以及各種被命名(標記)的版本都將會被集中管理。

      圖1 集中式版本控制

      而在分布式版本控制系統(見圖2)中,開發者環境與服務器環境之間是沒有分隔的。每一個開發者都同時擁有一個用于當前文件操作的工作區與一個用于存儲該項目所有版本、分支以及標簽的本地版本庫(我們稱其為一份克隆)。每個開發者的修改都會被載入成一次次的新版本提交(commit), 首先提交到其本地版本庫中。然后,其他開發者就會立即看到新的版本。通過推送(push)和拉回(pull)命令,我們可以將這些修改從一個版本庫傳送到另一個版本庫中。這樣一來,從技術上來看,這里所有的版本庫在分布式架構上的地位是同等的。因此從理論上來講,我們不再需要借助服務器,就可以將某一臺開發工作機上所做的所有修改直接傳送給另一開發工作機。當然在具體實踐中,Git中的服務器版本庫也扮演了重要的角色,例如以下這些特型版本庫。

      圖2 分布式版本控制

      項目版本庫(blessed repository):該版本庫主要用于存儲由“官方”創建并發行的版本。

      共享版本庫(shared repository):該版本庫主要用于開發團隊內人員之間的文件交換。在小型項目中,項目版本庫本身就可以勝任這一角色了。但在多點開發的條件下,我們可能就會需要幾個這樣的專用版本庫。

      工作流版本庫(workflow repository):工作流版本庫通常只用于填充那些代表工作流中某種特定進展狀態的修改,例如審核通過后的狀態等。

      派生版本庫(fork repository):該版本庫主要用于從開發主線分離出某部分內容(例如,分離出那些開發耗時較長,不適合在一個普通發布周期中完成的內容),或者隔離出可能永遠不會被包含在主線中的、用于實驗的那部分開發進展。

      下面,我們再來看看分布式系統相對于集中式的優點有哪些。

      高性能:幾乎所有的操作都無需進行網絡訪問,均可直接在本地執行。

      高效的工作方式:開發者可通過多個本地分支在不同任務之間進行快速切換。

      離線功能:開發者可以在沒有服務器連接的情況下執行提交、創建分支、版本標簽等操作。之后再將其上傳服務器。

      靈活的開發進程:我們可以在團隊和公司中為其他部門建立專用的版本庫,例如為方便與測試人員交流而建的版本庫。這樣相關修改就很容易發布,因為只是特定版本庫上的一次推送。

      備份作用:由于每個開發者都持有一份擁有完整歷史版本的版本庫副本,所以因服務器故障而導致數據丟失的可能性是微乎其微的。

      可維護性:對于那些難以對付的重構工作,我們可以在將成功傳送給其原始版本庫之前,先在該版本庫的副本上嘗試一下。

      2 版本庫,分布式工作的基礎所在

      其實,版本庫本質上就是一個高效的數據存儲結構而已,由以下部分組成。

      文件(即blob):這里既包含了文本也包含了二進制數據,這些數據將不以文件名的形式被保存。

      目錄(即Tree):目錄中保存的是與文件名相關聯的內容,其中也會包含其他目錄。

      對于所有的數據,它們都會被計算成一個十六進制散列值(例如像1632acb65b01 c6b621d6e1105205773931bb1a41這樣的值)。這個散列值將會被用作相關對象的引用,以及日后恢復數據時所需的鍵值(見圖3)。

      圖3 版本庫中的對象存儲

      也就是說,一個提交對象的散列值實際上就是它的“版本號”,如果我們持有某一提交的散列值,就可以用它來檢查對應版本是否存在于某一版本庫中。如果存在,我們就可以將其恢復到當前工作區相應的目錄中。如果該版本不存在,我們也可以從其他版本庫中單獨導入(拉回)該提交所引用的全部對象。

      接下來,我們來看看采用這種散列值和這種既定的版本庫結構究竟有哪些優勢。

      高性能:通過散列值來訪問數據是非常快的。

      冗余度——釋放存儲空間:相同的文件內容只需存儲一次即可。

      版本庫間的高效同步:當我們將某一提交從一個版本庫傳遞給另一個版本庫時,只需要傳送那些目標版本庫中不存在的對象即可。而正是因為有了散列值的幫助,我們才能很快地判斷相關對象是否已經存在。

      Git的基本概念與入門

      數據完整性:由于散列值是根據數據的內容來計算的,所以我們可以隨時通過Git來查看某一散列值是否與相關數據匹配。以檢測該數據上可能的意外變化或惡意操作。

      自動重命名檢測:被重命名的文件可以被自動檢測到,因為根據該文件內容計算出的散列值并沒有發生變化。也正因為如此,Git中并沒有專用的重命名命令,只需移動命令即可。

      3 分支的創建與合并很簡單

      對于大多數版本控制系統來說,分支的創建與合并通常會因其特殊性而被認為是高級拓展操作。但由于Git最初就是為了方便那些散落在世界各地的Linux內核開發者而創建的,合并多方努力的結果一直都是其面臨的最大挑戰之一,所以Git的設計目標之一就是要讓分支的創建與合并操作變得盡可能地簡單且安全。

      在下面的圖4中,我們向你展示了開發者是如何通過創建分支的方式來進行并行開發的。圖中的每一個點都代表了該項目的一個版本(即commit)。而由于在Git中,我們只能對整個項目進行版本化,所以每個點同時也代表了屬于同一版本的各個文件。

      圖4 因開發者的并行開發而出現的分支創建操作

      如上所示,圖中兩位開發者的起點是同一個版本。之后兩人各自做了修改,并提交了修改。這時候,對于這兩位開發者各自的版本庫來說,該項目已經有了兩個不同的版本。也就是說,他們在這里創建了兩個分支。接下來,如果其中一個開發者想要導入另一個人的修改,他/她就可以用Git來進行版本合并。如果合并成功了,Git就會創建一個合并提交,其中會包含兩位開發者所做的修改。這時如果另一位開發者也取回了這一提交,兩位開發者的項目就又回到了同一個版本。

      在上面的例子中,分支的創建是非計劃性的,其原因僅僅是兩個開發者在并行開發同一個軟件罷了。在Git中,我們當然也可以開啟有針對性的分支,即顯式地創建一個分支(見圖5)。顯式分支通常主要用于協調某一種功能性的并行開發。

      圖5 針對不同任務的顯式分支

      版本庫在執行拉回和推送操作時,可以具體指定其針對的是哪一些分支。當然,除了這些簡單的分支創建和合并處理外,我們也可以對分支執行以下動作。

      移植分支:我們可以直接將某一分支中的提交轉移到另一個版本庫中。

      只傳送特定修改:我們可以將某一分支中的某一次或某幾次提交直接復制到另一個分支中。這就是所謂的撿取處理。

      清理歷史:我們可以對分支歷史進行改造、排序和刪除。這有利于為該項目建立更好的歷史文檔。我們稱這種處理為交互式重訂(interactive rebasing)。

      4 本章小結

      在閱讀完本章之后,我們希望你現在基本上熟悉了Git中的這些基本概念。也就是說,即使你現在放下了這本書(當然,希望不會!),你也可以參加與分布式版本控制系統有關的討論,闡述其中使用散列值的必要性和實用性,介紹Git中的分支創建與合并操作了。

      當然,你可能還會有以下疑問。

      我們應該如何利用這些基本概念來管理項目呢?

      我們應該如何協調多個版本庫呢?

      我們究竟需要多少分支呢?

      我們應該如何整合自己的構建服務器呢?

      對于第一個問題,你可以繼續閱讀下一章內容。在下一章中,你將會看到那些具體用于創建版本庫、版本以及版本庫之間更替提交的命令。至于其他問題,你也可以參考詳細介紹工作流的那些章節。

      另外,如果你是一個繁忙的項目管理者,還在猶豫不決是否要采用Git。我們會建議你再看看關于Git的局限性的的討論,請參見第26章。

      入門

      如果你想試著用一下Git的話,那么我們馬上就可以開始了。本章將會帶領你創建自己的第一個項目。我們會為你演示那些用于提交修改版本、查看歷史和與其他開發者交換版本的命令。

      1 準備Git環境

      首先,我們需要安裝好Git。你可以在Git的官網上找到你所需要的一切:

      http://git-scm.com/download

      Git是一個高可配置軟件。首先,我們可以宣布用config命令配置一下用戶名和用戶郵箱:[1]

      >?git?config?--global?user.email?"hans@mustermann.de"

      2 第一個Git項目

      在這里,我們建議你最好能為接下來的Git測試單獨開辟一個項目。總之應先從一個簡單的小項目開始。在我們這個小小的示例項目中,first-steps目錄下只有兩個文本文件,如圖1所示。

      圖1 我們的示例項目

      在開始擺弄這個玩具項目之前,我們建議你最好先做一個備份!盡管在Git中,想要造成永久性的刪除或破壞也不是件容易的事情,而且每當你要做某些“危險”動作的時候,Git通常也會發出相應的警告消息。但是,有備無患總是好的。

      1 創建版本庫

      現在,我們首先需要創建一個版本庫,用于存儲該項目本身及其歷史。為此,我們需要在該項目目錄中使用init命令。對于一個帶版本庫的項目目錄,我們通常稱之為工作區。

      >?cd?/projects/first-steps? >?git?init Initialized?empty?Git?repository?in?/projects/first-steps/.git/

      init命令會在上述目錄中創建一個名為.git的隱藏目錄,并在其中創建一個版本庫。但請注意,該目錄在Windows資源管理器或Mac Finder中可能是不可見的。

      圖2 本地版本庫所在的目錄

      2 首次提交

      接下來,我們需要將foo.txt和bar.txt這兩個文件添加到版本庫中去。在Git中,我們通常將項目的一個版本稱之為一次提交,但這要分兩個步驟來實現。第一步,我們要先用add命令來確定哪些文件應被包含在下次提交中。第二步,再用commit命令將修改傳送到版本庫中,并賦予該提交一個散列值以便標識這次新提交。在這里,我們的散列值為2f43cd0,但可能會有所不同,因為該值取決于文件內容。

      >?git?add?foo.txt?bar.txt? >?git?commit?--message?"Sample?project?imported."?master?(root-commit)?2f43cd0]?Sample?project?imported.2?files?changed,?2?insertions(+),?0?deletions(-)? create?mode?100644?bar.txt? create?mode?100644?foo.txt

      3 檢查狀態

      現在,我們來修改一下foo.txt文件的內容,先刪除bar.txt文件,再添加一個名為bar.html的新文件。然后,status命令就會顯示出該項目自上次提交以來所發生的所有修改。請注意,新文件bar.html在這里被標示成了未跟蹤狀態,這是因為我們還沒有用add命令將其注冊到版本庫。

      >?git?status? #?On?branch?master?#?Changed?but?not?updated:?#?(use?"git?add/rm?..."?to?update?what?will?be?committed)?#?(use?"git?checkout?--?..."?to?discard?changes?in?#?????????????????????????????????????????????????????working?directory)?#?#??????deleted:???bar.txt?#??????modified:??foo.txt?#?#?Untracked?files:?#?(use?"git?add?..."?to?include?in?what?will?be?committed)?#?#??????bar.htmlno?changes?added?to?commit?(use?"git?add"?and/or?"git?commit?-a")

      如果我們還想看到更多細節性的內容,也可以通過diff命令來顯示其每個被修改的行。當然。有很多人可能會覺得diff的輸出是個非常難讀的東西。幸運的是,在這一領域,我們有許多工具和開發環境可用,它們可以將這一切顯示得更為清晰(見圖3)。

      圖3 圖形工具(kdiff3)中的Diff報告

      >?git?diff?foo.txt? diff?--git?a/foo.txt?b/foo.txt?index?191028.090387f?100644?---?a/foo.txt?+++?b/foo.txt? @@?-1?+1?@@? -foo? \?No?newline?at?end?of?file?+foo?foo? \?No?newline?at?end?of?file

      4 提交修改

      接下來,所有的修改都必須要先被歸檔成一次新的提交。我們要對修改過的文件和新文件執行add命令,并對要刪除的文件使用rm命令。

      >?git?add?foo.txt?bar.html?>?git?rm?bar.txt?rm?'bar.txt'

      現在再次調用status命令,我們會看到所有的修改已經被納入了下一次提交中。

      >?git?status? #?On?branch?master?#?Changes?to?be?committed:?#???(use?"git?reset?HEAD?..."?to?unstage)?#?#???????new?file:???bar.html?#???????deleted:????bar.txt?#???????modified:???foo.txt?#

      然后用commit命令提交這些修改。

      >?git?commit?--message?"Some?changes."?[master?7ac0f38]?Some?changes.? 3?files?changed,?2?insertions(+),?2?deletions(-)?? create?mode?100644?bar.html?? delete?mode?100644?bar.txt

      5 顯示歷史

      log命令可用來顯示項目的歷史,所有提交都會按時間順序被降序排列出來。

      >?git?log commit?7ac0f38f575a60940ec93c98de11966d784e9e4f? Author:?Rene?Preissel?? Date:?Thu?Dec?2?09:52:25?2010?+0100? ????Some?changes.? commit?2f43cd047baadc1b52a8367b7cad2cb63bca05b7? Author:?Rene?Preissel?? Date:?Thu?Dec?2?09:44:24?2010?+0100? ????Sample?project?imported.

      3 Git的協作功能

      現在,我們已經有了一個存放項目文件的工作區,以及一個存放項目歷史的版本庫。在一個像CVS和Subversion這樣傳統的集中式版本系統中,盡管每個開發者也都有屬于他/她自己的工作區,但所有人都共享了一個通用的版本庫。而在Git中,每個開發者擁有的是一個屬于他/她自己的、自帶獨立版本庫的工作區,因此這已經是一個不依賴于中央服務器的、完整的版本控制系統了。開發者們可以通過交換各自版本庫中的提交來實現項目合作。下面我們就來做個試驗,先創建一個新的工作區,以便我們模擬第二位開發者的活動。

      3.1 克隆版本庫

      我們的這位新開發者首先要有一個屬于他/她自己的版本庫副本(也稱為克隆體)。該副本中包含了所有的原始信息與整個項目的歷史信息。下面。我們用clone命令來創建一個克隆體。

      >?git?clone?/projects/first-steps?/projects/first-steps-cloneCloning?into?first-steps-clone...? done.

      現在,該項目結構如圖4所示。

      圖4 樣例項目與它的克隆體

      3.2 從另一版本庫中獲取修改

      下面,我們來修改一下first-steps/foo.txt文件,并執行以下操作來創建一次新提交。

      >?cd?/projects/first-steps? >?git?add?foo.txt? >?git?commit?--message?"A?change?in?the?original."

      現在,新的提交已經被存入了我們原來的first-steps版本庫中,但其克隆版本庫(first-stepsclone)中依然缺失這次提交。為了讓你更好地理解這一情況,我們來看一下first-steps的日志。

      >?git?log?--oneline?a662055?A?change?in?the?original.? 7ac0f38?Some?changes.? 2f43cd0?Sample?project?imported.

      在接下來的步驟中,我們再來修改克隆版本庫中的first-steps-clone/bar.html文件,并執行以下操作。

      >?cd?/projects/first-steps-clone? >?git?add?bar.html? >?git?commit?--message?"A?change?in?the?clone."?>?git?log?--oneline?1fcc06a?A?change?in?the?clone.? 7ac0f38?Some?changes.? 2f43cd0?Sample?project?imported.

      現在,我們在兩個版本庫中各做了一次新的提交。接下來,我們要用pull命令將原版本庫中的新提交傳遞給它的克隆體。由于之前我們在創建克隆版本庫時,原版本庫的路徑就已經被存儲在了它的克隆體中,因此pull命令知道該從哪里去取回新的提交。

      >?cd?/projects/first-steps-clone? >?git?pull? remote:?Counting?objects:?5,?done.? remote:?Compressing?objects:?100%?(2/2),?done.? remote:?Total?3?(delta?0),?reused?0?(delta?0)? Unpacking?objects:?100%?(3/3),?done.? From?/projects/first-steps???7ac0f38..a662055?master?->?origin/master? Merge?made?by?recursive.?? foo.txt?|??????2?+-1?files?changed,?1?insertions(+),?1?deletions(-)

      如上所示,pull命令從原版本庫中取回了新的修改,將它們與克隆體中的本地修改進行了對比,并在工作區中合并了兩邊的修改,創建了一次新的提交。這個過程就是所謂的合并(merge)。

      請注意!合并過程在某些情況下可能會帶來沖突。一旦遇到了這種情況,Git中就不能進行自動化的版本合并了。在這種情況下,我們就必須要手動清理一些文件,然后再確認要提交哪些修改。

      在拉回(pull)、合并(merge)的過程完成之后,我們可以用一個新的log命令來查看結果。這次是日志的圖形化版本。

      >?git?log?--graph? 9e7d7b9?Merge?branch?’master’?of?/projects/first-steps? *? |\? |?*?a662055?A?change?in?the?original.? *?|?1fcc06a?A?change?in?the?clone.? |/? *?7ac0f38?Some?changes.? *?2f43cd0?Sample?project?imported.

      這一次,歷史記錄不再是一條直線了。在上面的日志中,我們可以很清晰地看到并行開發的過程(即中間的兩次提交),以及之后用于合并分支的那次合并提交(即頂部的那次提交)。

      3.3 從任意版本庫中取回修改

      在沒有參數的情況下,pull命令只在克隆版本庫中能發揮作用,因為只有該克隆體中有默認的原版本庫的連接。當我們執行pull操作時,也可以用參數來指定任意版本庫的路徑,以便從某一特定開發分支中提取相關修改。

      現在,讓我們將克隆體中的修改pull到原版本庫中吧。

      >?cd?/projects/first-steps? >?git?pull?/projects/first-steps-clone?master? remote:?Counting?objects:?8,?done.? remote:?Compressing?objects:?100%?(4/4),?done.? remote:?Total?5?(delta?0),?reused?0?(delta?0)? Unpacking?objects:?100%?(5/5),?done.? From?/projects/first-steps-clone ?*?branch???????????master?→??FETCH_HEAD Updating?a662055..9e7d7b9? Fast-forward?? bar.html?|????2?+-?? 1?files?changed,?1?insertions(+),?1?deletions(-)

      3.4 創建共享版本庫

      除了可以用pull命令從其他版本庫中取回相關提交外,我們也可以用push命令將提交傳送給其他版本庫。只不過,push命令只適用于那些沒有開發者在上面開展具體工作的版本庫。最好的方法就是創建一個不帶工作區的版本庫,我們稱之為裸版本庫(bare repository)。你可以使用clone命令的--bare選項來創建一個裸版本庫。裸版本庫通常可被用來充當開發者們傳遞提交(使用push命令)的匯聚點,以便其他人可以從中拉回他們所做的修改。下面我們來看一個裸版本庫(見圖5)。

      圖5 裸版本庫(一個沒有工作區的版本庫)

      >?git?clone?--bare?/projects/first-steps? ???????????????????????/projects/first-steps-bare.git Cloning?into?bare?repository?first-steps-bare.git...? done.

      3.5 用push命令上載修改

      為了演示push命令的使用,我們需要再次修改一下firststeps/foo.txt文件,并執行以下操作來創建一次新的提交。

      >?cd?/projects/first-steps? >?git?add?foo.txt? >?git?commit?--message?"More?changes?in?the?original."

      接下來,我們就可以用push命令向共享版本庫傳送該提交了(見圖6)。該指令的參數要求與pull命令相同,我們需要指定目標版本庫的路徑及其分支。

      >?git?push?/projects/first-steps-bare.git?master? Counting?objects:?5,?done.? Delta?compression?using?up?to?2?threads.? Compressing?objects:?100%?(2/2),?done.? Writing?objects:?100%?(3/3),?293?bytes,?done.? Total?3?(delta?0),?reused?0?(delta?0)? Unpacking?objects:?100%?(3/3),?done.? To?/projects/first-steps-bare.git/? ????9e7d7b9..7e7e589?master?->?master

      圖6 經由共享版本庫來進行版本共享

      3.6 Pull命令:取回修改

      現在,為了讓克隆版本庫也得到相應的修改,我們需要在執行pull命令時配置參數指向共享版本庫的路徑參數。

      >?cd?/projects/first-steps-clone? >?git?pull?/projects/first-steps-bare.git?master? remote:?Counting?objects:?5,?done.? remote:?Compressing?objects:?100%?(2/2),?done.? remote:?Total?3?(delta?0),?reused?0?(delta?0)? Unpacking?objects:?100%?(3/3),?done.? From?../first-steps-bare? ?*?branch??????master??????->?FETCH_HEAD? Updating?9e7d7b9..7e7e589? Fast-forward? ?foo.txt?|????2?+-? ?1?files?changed,?1?insertions(+),?1?deletions(-)

      請注意!如果另一個開發者在我們之前已經做過一次push操作,此次push命令就會被拒絕傳送提交。這時候,我們必須要先做一次pull操作,將其他人新上載的更新取回,并在本地合并。

      4 本章小結

      工作區與版本庫:工作區是一個包含.git子目錄(內含版本庫)中的目錄。我們可以用init命令在當前目錄中創建版本庫。

      版本提交:一次版本提交通常定義了版本庫中所有文件的一個版本,它詳細說明了該版本是由何人在何時何地創建的。當然,我們需要用add命令來確定哪些文件將被納入下一次提交,然后再用commit命令創建新的版本提交。

      查看信息:通過status命令,我們可以查看哪些文件已被本地修改,以及哪些修改將被納入下次提交。另外,log命令可用來顯示提交歷史。diff命令可用來顯示兩個版本文件之間的差異。

      克隆:對于用clone命令創建某一個版本庫的副本,我們稱之為該版本庫的克隆體。在一般情況下,每個開發者都會擁有整個項目版本庫的完整克隆體,他/她的工作區中將會包含完整的項目歷史。這使他們可以各自獨立開展工作,無需連接服務器。

      推送與拉回:push與pull命令可用于在本地和遠程版本庫之間共享版本提交。

      [1] 譯者注:示例中似乎少了用戶名的部分:git config --global user.name“Hans”

      本文來自《Git學習指南》

      一本面向專業開發者的書

      如果你在某一團隊中從事開發工作,希望了解如何才能有效地使用Git,那么這本書就是一個正確的選擇。本書既不是那種偏重于理論的大部頭,也不是一本面面俱到的參考書。我們并不打算解釋所有的Git命令(這里可有100多條命令呢)及其全部選項(有些命令甚至有50多個選項)。相反,我們打算在這本書中教你如何在典型的項目環境中使用Git,例如,如何建立起一個Git項目、如何創建一個Git發行版等。

      本書相關內容

      你將在本書中看到以下內容。

      入門教程:這部分會重點演示每一個重要Git命令的用法,篇幅不會超過十幾頁。

      技術介紹:在這部分不足百頁的篇幅中,你將要學習如何使用Git處理一個團隊開發中的各項事務。我們將會用大量的實例為你演示那些主要Git命令的使用方式。此外我們還會為你解釋其中的基本概念,例如提交、版本庫、分支、合并、重訂等,以幫助你了解Git的具體工作方式。在這個過程中,你還會不時地看到一些相關的提示與技巧,你可能未必每天都會用到這些技巧,但它們有時還是會非常有用的。

      工作流:這里的工作流主要指的是你在項目中使用Git的實用場景,例如創建一個項目的發行版等。而對于每個工作流,我們會從以下幾項內來描述其目標場景。

      解決的是什么問題。

      需要增加什么必要條件。

      解決問題的人以及解決的時間。

      “解決方案選用理由”部分:每個工作流中通常都只能有一個具體的解決方案。在Git中,經常會存在著多個非常不同的解決路徑,這些路徑都可以讓我們達成相同的目標。在每一個工作流章節的最后一部分中,我們都會詳細解釋為什么要選用眼下這個解決方案。另外。我們還會提一下相關的可變因素,以及我們因此可能采取的替代方案。

      “分步”指令:這是一組常用命令序列,例如像移動某個分支就屬于一條既定的“分步”指令。

      本文轉載自異步社區。

      分布式 Git

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:未來70%新應用源自低代碼,藍凌6步助力“應用自由”
      下一篇:如何粘貼身份證號碼和銀行卡號(銀行卡怎么換綁身份證號碼)
      相關文章
      亚洲精品又粗又大又爽A片| 亚洲喷奶水中文字幕电影| 国产亚洲国产bv网站在线| 久久久无码精品亚洲日韩蜜臀浪潮 | 国产午夜亚洲精品不卡电影| 亚洲人成自拍网站在线观看| 亚洲人成网站在线在线观看| 亚洲午夜无码毛片av久久京东热| 亚洲国产精品免费在线观看| 亚洲国产品综合人成综合网站| 亚洲激情校园春色| 亚洲特级aaaaaa毛片| 亚洲精品不卡视频| 亚洲乱码一二三四区麻豆| 亚洲免费在线视频播放| 国产精品亚洲午夜一区二区三区| 久久亚洲精品专区蓝色区| 亚洲AV男人的天堂在线观看| 亚洲色大成网站WWW国产| 亚洲精品乱码久久久久蜜桃| 久久亚洲欧美国产精品| 国产亚洲日韩在线a不卡| 亚洲精品国产自在久久| 中文字幕在亚洲第一在线| 亚洲无线观看国产精品| 亚洲AV无码久久寂寞少妇| 亚洲综合在线观看视频| 亚洲成年人电影网站| 涩涩色中文综合亚洲| 亚洲成a人无码亚洲成av无码| 狠狠入ady亚洲精品| 爱情岛论坛网亚洲品质自拍| 亚洲日韩激情无码一区| 亚洲精品在线观看视频| 亚洲男女性高爱潮网站| 天堂亚洲国产中文在线| 国产精品亚洲二区在线| 亚洲自偷自偷图片| 亚洲视频在线播放| 亚洲入口无毒网址你懂的| 亚洲AV色欲色欲WWW|