日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

使用JGit API探索Git内部

發(fā)布時(shí)間:2023/12/3 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用JGit API探索Git内部 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

您是否想過(guò)提交及??其內(nèi)容如何存儲(chǔ)在Git中? 好吧,我有,在上一個(gè)下雨的周末,我有一些空閑時(shí)間,所以我做了一些研究。

因?yàn)槲覍?duì)Java的感覺(jué)比對(duì)Bash的感覺(jué)要多,所以我使用了JGit和一些學(xué)習(xí)測(cè)試來(lái)探索提交的Git內(nèi)部。 這是我的發(fā)現(xiàn):

Git –對(duì)象數(shù)據(jù)庫(kù)

Git的核心是簡(jiǎn)單的內(nèi)容可尋址數(shù)據(jù)存儲(chǔ)。 這意味著您可以在其中插入任何類(lèi)型的內(nèi)容,并且它將返回一個(gè)密鑰,您可以使用該密鑰在以后的某個(gè)時(shí)間點(diǎn)再次檢索數(shù)據(jù)。

對(duì)于Git,關(guān)鍵是從內(nèi)容計(jì)算出的20字節(jié)SHA-1哈希。 內(nèi)容也被稱(chēng)為在GIT中術(shù)語(yǔ)的對(duì)象 ,因此數(shù)據(jù)存儲(chǔ)也被稱(chēng)為對(duì)象數(shù)據(jù)庫(kù) 。

讓我們看看如何使用JGit來(lái)存儲(chǔ)和檢索內(nèi)容。

斑點(diǎn)

在JGit中,ObjectInserter用于將內(nèi)容存儲(chǔ)到對(duì)象數(shù)據(jù)庫(kù)中。 可以將其視為與Git中的git hash-object大致等效。

使用其insert()方法,您可以將對(duì)象寫(xiě)入數(shù)據(jù)存儲(chǔ),而其idFor()方法僅計(jì)算給定字節(jié)的SHA-1哈希。 因此,用于存儲(chǔ)字符串的代碼如下所示:

ObjectInserter objectInserter = repository.newObjectInserter(); byte[] bytes = "Hello World!".getBytes( "utf-8" ); ObjectId blobId = objectInserter.insert( Constants.OBJ_BLOB, bytes ); objectInserter.flush();

所有代碼示例均假定存儲(chǔ)庫(kù)變量指向在代碼段外部創(chuàng)建的空存儲(chǔ)庫(kù) 。

第一參數(shù)表示要插入的對(duì)象的對(duì)象類(lèi)型 ,在這種情況下,斑點(diǎn)的類(lèi)型。 還有其他對(duì)象類(lèi)型,我們將在后面學(xué)習(xí)。 Blob類(lèi)型用于存儲(chǔ)任意內(nèi)容。

有效載荷必須在第二個(gè)參數(shù)中給出,在這種情況下,必須作為字節(jié)數(shù)組。 也可以使用接受InputStream的重載方法。

最后,需要刷新ObjectInserter,以使其他訪問(wèn)存儲(chǔ)庫(kù)的更改可見(jiàn)。

insert()方法返回根據(jù)類(lèi)型,內(nèi)容長(zhǎng)度和內(nèi)容字節(jié)計(jì)算得出的SHA-1哈希。 但是,在JGit中,SHA-1哈希通過(guò)ObjectId類(lèi)表示,ObjectId類(lèi)是一種不變的數(shù)據(jù)結(jié)構(gòu),可以在字節(jié),整數(shù)和字符串之間來(lái)回轉(zhuǎn)換。

現(xiàn)在,您可以使用返回的blobId取回內(nèi)容,從而確保上面的代碼實(shí)際寫(xiě)入了內(nèi)容。

ObjectReader objectReader = repository.newObjectReader(); ObjectLoader objectLoader = objectReader.open( blobId ); int type = objectLoader.getType(); // Constants.OBJ_BLOB byte[] bytes = objectLoader.getBytes(); String helloWorld = new String( bytes, "utf-8" ) // Hello World!

ObjectReader的open()方法返回一個(gè)ObjectLoader,該對(duì)象可用于訪問(wèn)由給定對(duì)象ID標(biāo)識(shí)的對(duì)象。 借助ObjectLoader,您可以將對(duì)象的類(lèi)型,大小以及內(nèi)容作為字節(jié)數(shù)組或流獲取。

要驗(yàn)證JGit編寫(xiě)的對(duì)象與本機(jī)Git兼容,可以使用git cat-file檢索其內(nèi)容。

$ git cat-file -p c57eff55ebc0c54973903af5f72bac72762cf4f4 Hello World! git cat-file -t c57eff55ebc0c54973903af5f72bac72762cf4f4 blob

如果查看存儲(chǔ)庫(kù)的.git/objects目錄,則會(huì)發(fā)現(xiàn)一個(gè)名為'c5'的目錄,其中包含名為'7eff55ebc0c54973903af5f72bac72762cf4f4'的文件。 最初是這樣存儲(chǔ)內(nèi)容的:每個(gè)對(duì)象作為一個(gè)文件,以?xún)?nèi)容的SHA-1哈希命名。 子目錄以SHA-1的前兩個(gè)字符命名,文件名由其余字符組成。

現(xiàn)在您可以存儲(chǔ)文件的內(nèi)容,下一步就是存儲(chǔ)文件的名稱(chēng)。 而且可能不僅僅是一個(gè)文件,因?yàn)樘峤煌ǔS梢唤M文件組成。 為了保存此類(lèi)信息,Git使用了所謂的樹(shù)對(duì)象。

樹(shù)對(duì)象

樹(shù)對(duì)象可以看作是簡(jiǎn)化的文件系統(tǒng)結(jié)構(gòu),其中包含有關(guān)文件和目錄的信息。

它包含任意數(shù)量的樹(shù)條目。 每個(gè)條目都有一個(gè)路徑名,一個(gè)文件模式,并指向一個(gè)文件(一個(gè)blob對(duì)象)或另一個(gè)(子)樹(shù)對(duì)象(如果它代表一個(gè)目錄)的內(nèi)容。 指針當(dāng)然是Blob對(duì)象或樹(shù)對(duì)象的SHA-1哈希。

首先,您可以創(chuàng)建一個(gè)樹(shù),其中包含一個(gè)名為“ hello-world.txt”的文件的單個(gè)條目,該文件指向上面存儲(chǔ)的“ Hello World!”。 內(nèi)容。

TreeFormatter treeFormatter = new TreeFormatter(); treeFormatter.append( "hello-world.txt", FileMode.REGULAR_FILE, blobId ); ObjectId treeId = objectInserter.insert( treeFormatter ); objectInserter.flush();

TreeFormatter在這里用于構(gòu)造內(nèi)存中的樹(shù)對(duì)象。 通過(guò)調(diào)用append(),將添加具有給定路徑名,模式和ID(用于存儲(chǔ)其內(nèi)容)的條目。

從根本上講,您可以自由選擇任何路徑名。 但是,Git希望路徑名是相對(duì)于工作目錄的,且不帶前導(dǎo)“ /”。

此處使用的文件模式表示正常文件。 其他模式是EXECUTABLE_FILE(它是一個(gè)可執(zhí)行文件)和SYMLINK(它指定一個(gè)符號(hào)鏈接)。 對(duì)于目錄條目,文件模式始終為T(mén)REE。

同樣,您將需要一個(gè)ObjectInserter。 其重載的insert()方法之一接受TreeFormatter并將其寫(xiě)入對(duì)象數(shù)據(jù)庫(kù)。

現(xiàn)在,您可以使用TreeWalk檢索和檢查樹(shù)對(duì)象:

TreeWalk treeWalk = new TreeWalk( repository ); treeWalk.addTree( treeId ); treeWalk.next(); String filename = treeWalk.getPathString(); // hello-world.txt

實(shí)際上,TreeWalk旨在對(duì)添加的樹(shù)及其子樹(shù)進(jìn)行迭代。 但是由于我們知道只有一個(gè)條目,因此只需調(diào)用next()就足夠了。

如果使用本地Git查看剛剛編寫(xiě)的樹(shù)對(duì)象,則會(huì)看到以下內(nèi)容:

$ git cat-file -p 44d52a975c793e5a4115e315b8d89369e2919e51 100644 blob c57eff55ebc0c54973903af5f72bac72762cf4f4 hello-world.txt

現(xiàn)在您已經(jīng)擁有提交的必要條件,讓我們創(chuàng)建提交對(duì)象本身。

提交對(duì)象

提交對(duì)象 (通過(guò)樹(shù)對(duì)象)引用構(gòu)成提交的文件以及一些元數(shù)據(jù)。 詳細(xì)而言,提交包括:

  • 指向樹(shù)對(duì)象的指針
  • 指向零個(gè)或多個(gè)父提交的指針(稍后會(huì)詳細(xì)介紹)
  • 提交消息
  • 以及作者和提交者

由于提交對(duì)象只是對(duì)象數(shù)據(jù)庫(kù)中的另一個(gè)對(duì)象,因此它也被在其內(nèi)容上計(jì)算出的SHA-1哈希密封。

為了形成提交對(duì)象,JGit提供了CommitBuilder實(shí)用程序類(lèi)。

CommitBuilder commitBuilder = new CommitBuilder(); commitBuilder.setTreeId( treeId ); commitBuilder.setMessage( "My first commit!" ); PersonIdent person = new PersonIdent( "me", "me@example.com" ); commitBuilder.setAuthor( person ); commitBuilder.setCommitter( person ); ObjectInserter objectInserter = repository.newObjectInserter(); ObjectId commitId = objectInserter.insert( commitBuilder ); objectInserter.flush();

使用它很簡(jiǎn)單,它具有針對(duì)提交的所有屬性的setter方法。

作者和提交者通過(guò)PersonIdent類(lèi)表示,該類(lèi)包含名稱(chēng),電子郵件,時(shí)間戳和時(shí)區(qū)。 此處使用的構(gòu)造函數(shù)應(yīng)用給定的名稱(chēng)和電子郵件,并采用當(dāng)前時(shí)間和時(shí)區(qū)。

剩下的應(yīng)該已經(jīng)很熟悉了:ObjectInserter用于實(shí)際寫(xiě)入提交對(duì)象并返回提交ID。

要從存儲(chǔ)庫(kù)中檢索提交對(duì)象,可以再次使用ObjectReader:

ObjectReader objectReader = repository.newObjectReader(); ObjectLoader objectLoader = objectReader.open( commitId ); RevCommit commit = RevCommit.parse( objectLoader.getBytes() );

生成的RevCommit表示具有與CommitBuilder中指定的相同屬性的提交。

再一次-仔細(xì)檢查git cat-file的輸出:

$ git cat-file -p 783341299c95ddda51e6b2393c16deaf0c92d5a0 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 author me <me@example.com> 1412872859 +0200 committer me <me@example.com> 1412872859 +0200My first commit!

父母

父母鏈形成了Git倉(cāng)庫(kù)的歷史,并建模了有向無(wú)環(huán)圖 。 這意味著提交“遵循”一個(gè)方向

一個(gè)提交可以有零個(gè)或多個(gè)父母。 存儲(chǔ)庫(kù)中的第一個(gè)提交沒(méi)有父提交(aka根提交)。 第二個(gè)提交又將第一個(gè)提交作為其父級(jí),依此類(lèi)推。

創(chuàng)建多個(gè)根提交完全合法。 如果使用git checkout --orphan new_branch將創(chuàng)建一個(gè)新的孤立分支并將其切換到。 在此分支上進(jìn)行的第一次提交將沒(méi)有父項(xiàng),并且將成為與所有其他提交斷開(kāi)連接的新歷史記錄的根。


如果開(kāi)始分支并最終合并變化的分歧線,通常會(huì)導(dǎo)致合并提交 。 并且這樣的提交具有分支的父分支作為其父分支。

為了構(gòu)造父提交,需要在CommitBuilder中指定父提交的ID。

commitBuilder.setParents( parentId );

還可以查詢(xún)RevCommit類(lèi),該類(lèi)表示存儲(chǔ)庫(kù)中的提交,并且可以查詢(xún)其父級(jí)。 它的getParents()和getParent(int)方法返回全部或第n個(gè)父RevCommit。

請(qǐng)注意,盡管這些方法返回RevCommits,但這些方法尚未完全解決。 設(shè)置其ID屬性后,所有其他屬性(fullMessage,author,committer等)均未設(shè)置。 因此,例如,嘗試調(diào)用parent.getFullMessage()會(huì)引發(fā)NullPointerException。 為了實(shí)際使用父提交,您需要通過(guò)上面概述的ObjectReader檢索完整的RevCommit,或者使用RevWalk加載和解析提交標(biāo)頭:

RevWalk revWalk = new RevWalk( repository ); revWalk.parseHeaders( parentCommit );

總而言之,請(qǐng)記住將返回的父提交視為ObjectId而不是RevCommits。

有關(guān)樹(shù)對(duì)象的更多信息

如果要將文件存儲(chǔ)在子目錄中,則需要自己構(gòu)造子樹(shù)。 假設(shè)您要將文件“ file.txt”的內(nèi)容存儲(chǔ)在文件夾“文件夾”中。

首先,為子樹(shù)創(chuàng)建并存儲(chǔ)TreeFormatter,該子樹(shù)具有文件的條目:

TreeFormatter subtreeFormatter = new TreeFormatter(); subtreeFormatter.append( "file.txt", FileMode.REGULAR_FILE, blobId ); ObjectId subtreeId = objectInserter.insert( subtreeFormatter );

然后,創(chuàng)建并存儲(chǔ)TreeFormatter以及一個(gè)表示文件夾的條目,并指向剛創(chuàng)建的子樹(shù)。

TreeFormatter treeFormatter = new TreeFormatter(); treeFormatter.append( "folder", FileMode.TREE, subtreeId ); ObjectId treeId = objectInserter.insert( treeFormatter );


條目的文件模式為T(mén)REE,表示目錄,其ID指向保存文件條目的子樹(shù)。 返回的treeId是將傳遞給CommitBuilder的那個(gè)。

Git對(duì)樹(shù)對(duì)象中的條目要求一定的排序順序。 我在這里找到的“ Git數(shù)據(jù)格式”文檔指出:

樹(shù)條目按包含條目名稱(chēng)的字節(jié)序列排序。 但是,出于排序比較的目的,比較樹(shù)對(duì)象的條目,就好像條目名稱(chēng)字節(jié)序列具有尾隨的ASCII'/'(0x2f)。

要讀取樹(shù)對(duì)象的內(nèi)容,可以再次使用TreeWalk。 但是這一次,如果要訪問(wèn)所有條目,則需要告訴它遞歸到子樹(shù)中。 而且,如果您希望看到指向樹(shù)的條目,請(qǐng)不要忘記將postOrderTraversal設(shè)置為true。 否則將被跳過(guò)。

最終,整個(gè)TreeWalk循環(huán)將如下所示:

TreeWalk treeWalk = new TreeWalk( repository ); treeWalk.addTree( treeId ); treeWalk.setRecursive( true ); treeWalk.setPostOrderTraversal( true ); while( treeWalk.next() ) {int fileMode = Integer.parseInt( treeWalk.getFileMode( 0 ).toString() );String objectId = treeWalk.getObjectId( 0 ).name();String path = treeWalk.getPathString();System.out.println( String.format( "%06d %s %s", fileMode, objectId, path ) ); }

…并將導(dǎo)致以下輸出:

100644 6b584e8ece562ebffc15d38808cd6b98fc3d97ea folder/file.txt 040000 541550ddcf8a29bcd80b0800a142a7d47890cfd6 folder

盡管我發(fā)現(xiàn)API不是很直觀,但它可以完成工作并顯示樹(shù)對(duì)象的所有詳細(xì)信息。

總結(jié)Git內(nèi)部

毫無(wú)疑問(wèn),對(duì)于常見(jiàn)用例,建議將高層Add-和CommitCommands提交到存儲(chǔ)庫(kù)中。 不過(guò),我發(fā)現(xiàn)值得深入研究JGit和Git,希望您也這樣做。 而且,在您需要將文件提交到?jīng)]有工作目錄和/或索引的存儲(chǔ)庫(kù)的情況下(這種情況不太常見(jiàn)),這里提供的信息可能會(huì)有所幫助。

如果您想親自嘗試此處列出的示例,建議您設(shè)置JGit并對(duì)其源代碼和JavaDoc進(jìn)行訪問(wèn),以便您擁有有意義的上下文信息,內(nèi)容幫助,調(diào)試源等。

  • 完整的源代碼托管在這里: https : //gist.github.com/rherrmann/02d8d4fe81bb60d9049e

為簡(jiǎn)便起見(jiàn),此處顯示的示例省略了用于釋放分配的資源的代碼。 請(qǐng)參考完整的源代碼以獲取所有詳細(xì)信息。

翻譯自: https://www.javacodegeeks.com/2014/10/explore-git-internals-with-the-jgit-api.html

總結(jié)

以上是生活随笔為你收集整理的使用JGit API探索Git内部的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。