用 JGit 初始化 Git 庫

jopen 9年前發布 | 27K 次閱讀 JGit Git 版本控制系統

最近,我被問及如何用 JGit 來初始化一個新的 Git 庫,比如實現初始化一個庫 /path/to/repodoes。

當我用 JGit 來創建庫時其實并不難,這里有些細節值得提一提。因為在網上幾乎少有關于這個主題的資料,而且有些還是錯誤的,因此本文就總結了如何使用 JGit API 來初始化一個 Git 庫的方法。

本地庫

為了用 JGit 來初始化一個庫,那就要使用初始化命令。factoryGit 命令擁有一個靜態方法 init() 來創建初始化命令。

Git git = Git.init().setDirectory( directory ).call();

在上述代碼執行后,一個新的Git庫就已經創建完成了。這個庫的存儲位置是通過setDirectory()來給定的,這個庫構成了工作目錄,它包含了已簽出的文件及文件夾。若這個目錄并不存在,那么通過上述方法會一直創建這個工作目錄。

用 JGit 初始化 Git 庫

一個名為 .git 的子目錄在整個工作目錄中處于最頂層。這里面包含了本地庫的歷史日志,配置參數,分支指針,索引(又名中轉區)等等。

上面的截圖中展示了.git 目錄的內部結構。refs 目錄維護了分支和標簽信息。其實際內容將會存儲在對象目錄中。在 logs 目錄中,有關對分區的修改都會被記錄。舉個例子,一個提交和簽出操作都將創建一個日志記錄,這個記錄可以用 gitreflog 命令來查看。多年前我寫的這個帖子 Explore Git Internals with the JGit API 里面詳細的寫明了如何使用 Git 來管理這個庫里內容。

為了確保命令事實上成功執行,狀態命令常用于查詢庫的狀態,很像git status做的一樣。但不幸的是JGit的status實現不同于原生Git,因為如果沒有庫,它不會報錯。這使得檢查一個已存在HEAD ref變得更為必要,它表示事實上有一個庫。

assertNotNull( git.getRepository().getRef( Constants.HEAD ) );
assertTrue( git.status().call().isClean() );

對于最近初始化的庫來說,isClean()返回true,是因為在工作目錄里邊沒有任何改動或者有未跟蹤文件。

斷言使用theInitCommandscall()方法返回的GIT實例。這個類充當工廠的作用,并且常用于創建在庫上執行的Git命令(例如:add,commit,checkout)。

新初始化的庫比較特殊,由于還沒有分支被創建。盡管有一個引用分支的HEAD(指向當前分支),(默認叫做master)這個特有的分支并不存在。

就第一個提交來說通常沒有什么需要擔心的,缺少的分支將會被創建。不管怎樣,像git branch這樣的操作可以創建分支并會在最初的提交被提交之前,會伴隨著略微令人誤導的錯誤信息'Ref HEAD cannot be resolved'而失敗。

確定庫是否包含一個提交,檢查HEAD ref如下:

Ref headRef = git.getRepository().getRef( Constants.HEAD );
if( headRef == null || headRef.getObjectId() == null ) {
  // no commit yet
}

將目錄變成庫

如上所示,在必要的情況下,InitCommand會創建缺少的目錄。但是這個命令也常用于已存在的目錄,從而將其變成一個git庫。

下面的代碼片段在一個包含一個文件的已存在目錄里邊初始化庫,并保留其內容。

F?ile f?ile = new F?ile( "/path/to/existing/directory/readme.txt" );
f?ile.createNewF?ile();
Git git = Git.init().setDirectory( f?ile.getParentF?ile() ).call();
assertTrue( git.status().call().getUntracked().contains( f?ile.getName() ) );

由于文件未被跟蹤,status命令會提示這個文件。直到將這個文件加入索引才能夠被提交到剛剛創建的庫里邊。

如果指定的目錄已經持有一個Git庫,那就沒有必要擔心。既然這樣,JGit將不做任何事并返回一個指向已存在庫的實例。

分離工作和.git目錄

默認情況下,盡管一個庫有一個工作目錄,它的.git目錄直接位于工作目錄下面,但這不是必須的。一個庫完全可以沒有工作目錄(后續討論)或者工作目錄可以位于一個除.git目錄之外完全不同的位置。

下面的初始化命令創建了這樣一個庫

Git git = Git.init().setDirectory( workDir ).setGitDir( gitDir ).call();

最終的庫將會位于gitDir目錄(儲存歷史記錄,分支,標簽等的目錄),但這將會使它的工作目錄在workDir上面。

對于一個已存在的庫來說,工作目錄配置也是可以改變的。要么手動編輯配置文件.git,或者通過JGit API配置。

StoredConfig config = git.getRepository().getConfig();
config.setString( "core", null, "worktree", workDir.getCanonicalPath() );
config.save();

沒有必要把工作目錄內容手動從舊的位置移動到新的位置。

空倉庫

剛剛創建的庫是在本地處理的,也叫非空倉庫。另一種Git庫叫做空倉庫。

這些目的在于用作中心倉庫,將被其他用戶共享。沒有直接的提交可以提交到空倉庫中。一個空倉庫收到提交,是由于它們從用戶本地倉庫被推送。團隊成員從這個倉庫中拿下其他人提交的提交。

下面的代碼片段將創建一個空倉庫。

Git git = Git.init().setDirectory( directory ).setBare( true ).call();

一個空倉庫沒有工作目錄。反之,它的目錄結構可以在一個非空倉庫中的.git目錄下面找到并在指定的目錄中直接被創建。

因為status命令需要一個工作目錄,所以它不能夠驗證上述代碼成功執行。而是倉庫實例應該為空并想下面驗證指向所需目錄。

assertTrue( git.getRepository().isBare() );
assertEquals( directory, git.getRepository().getDirectory() );

值得注意的是為這個工作目錄查詢倉庫(也就是callinggetWorkTree())將會對空倉庫拋出NoWorkTreeException。

可選API: Repository.create()

一個可選的方法來初始化倉庫就是使用Repository的create方法。

Repository repository = new F?ileRepositoryBuilder().setGitDir( directory ).build();
repository.create();

assertNotNull( git.getRepository().getRef( Constants.HEAD ) ); assertTrue( Git.wrap( repository ).status().call().isClean() );</pre>

在FileRepositoryBuilder幫助下,Repository實例被創建,代表沒有現有的庫。Callingcreate()方法具體化倉庫。結果和InitCommand一樣。

為了創建空倉庫,還有一個重載的方法:create(boolean bare)。

結論

我希望這篇文章能夠幫助理解怎樣用JGit創建一個新倉庫。這里使用的代碼收集于學習測試,可以在這里發現它的全部內容:
https://gist.github.com/rherrmann/4bacb68b23be1f12c73d

它詳細說明了對InitCommand API正確和不正確的使用,或許可以作為進一步實驗JGit的一個起點。

如果你有任何難點或問題,可以留下評論或在友善且樂于助人的JGit社區提問以獲得幫助。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!