如何用JGit管理Git子模块(2)

当我们添加了一个子模块之后,我们可以会想知道,它是否对于父仓库来说是可知的。第一项测试中我们做了一个基础的检测,验证了某些文件和目录的存在。我们也可以使用一个API来列出一个仓库的子模块,如下所示:

1

2

3

4

5

6

7

8

9

10

11

 

@Test

public void testListSubmodules() throws Exception {

  addLibrarySubmodule();

 

  Map<String,SubmoduleStatus> submodules

    = parent.submoduleStatus().call();

 

  assertEquals( 1, submodules.size() );

  SubmoduleStatus status = submodules.get( "modules/library" );

  assertEquals( INITIALIZED, status.getType() );

}

 

SubmoduleStatus命令返回了一个子模块的Map集合,其中键是子模块的路径,值是这个模块的状态值。通过以上代码我们能够验证子模块确实已经添加进去,而且它的状态是INITIALIZED的。这个命令还允许添加一个或多个路径来限制子模块状态。

说到状态,JGit的StatusCommand并非原生的Git指令。如果在执行指令时添加选项‐‐ignore-submodules=dirty,那么所有对子模块工作目录的修改都会被忽略。

更新子模块

子模块通常指向他们所在的仓库的一次特殊的提交。如果之后有人克隆了父仓库,他们也会获得与之完全相同的子模块状态,即便子模块的上游有新的提交。

为了修改子模块,你像一下代码一样明确地对其进行更新:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

 

@Test

public void testUpdateSubmodule() throws Exception {

  addLibrarySubmodule();

  ObjectId newHead = library.commit().setMessage( "msg" ).call();

 

  File workDir = parent.getRepository().getWorkTree();

  Git libModule = Git.open( new F‌ile( workDir, "modules/library" ) );

  libModule.pull().call();

  libModule.close();

  parent.add().addF‌ilepattern( "modules/library" ).call();

  parent.commit().setMessage( "Update submodule" ).call();

 

  assertEquals( newHead, getSubmoduleHead( "modules/library" ) );

}

 

这个较长的代码片段中,首先第一件事就是提交一些东西到library仓库中(第四行),接着将子模块更新到最近的一次提交。

为了让这种更新持久化保存下来,子模块必须被提交(第10,11行)。这次提交在子模块的名下(例子中是modules/library)保存了此次更新的commit-id。最后,通常需要将修改push上去,使得他们对其他仓库可用。

在父仓库中更新对子模块的修改

将上游的提交拉取到父仓库中也会修改子模块的配置。然而子模块本身并不会自动得到更新。

SubmoduleUpdateCommand就是用来解决这个问题。使用这个命令并不需要指定其他参数,它会更新所有已注册的子模块。该命令会克隆缺失的子模块并检出其配置中指定的提交。就如其他子模块命令一样,这里也有一个addPath()方法,以保证只更新给定路径下的子模块。

克隆一个包含子模块的仓库

此时你可能已经掌握一个规律,所有对子模块的操作都是手动的。克隆一个包含子模块配置的仓库并不会默认克隆它的子模块。但是,CloneCommand命令有一个cloneSubmodules的属性,如果设置为true,那么将会克隆所有配置的子模块。从内部看,在对父仓库进行克隆之后,SubmoduleInitCommand和SubmoduleUpdateCommand命令会被递归地执行,并且父仓库的工作目录会被检出。

移除一个子模块

如果要移除一个子模块,你会希望可以这样写:

1

 

git.submoduleRm().setPath( ... ).call();

 

但是很不幸,不管是原生的Git或者JGit都没有提供内置的移除子模块的指令,希望将来会添加这样的指令,在这之前,我们必须手动去移除子模块。如果你滚动到removeSubmodule()方法你会发现这并不是一件复杂的事。

首先,各个子模块会从.gitsubmodules和.git/config配置文件中移除。其次,子模块的入口会从索引中被移除。最后,.gitsubmodules文件以及索引的修改会被提交,并且子模块的内容会从工作目录中删除。

遍历子模块

原生的Git提供了git submodule foreach命令为每个子模块执行一个shell指令。JGit并没有直接支持这样的指令,而是提供了SubmoduleWalk。该类可以用来迭代仓库中子模块。以下示例程序实现了为所有子模块拉取上游的提交。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

 

@Test

public void testSubmoduleWalk() throws Exception {

  addLibrarySubmodule();

 

  int submoduleCount = 0;

  Repository parentRepository = parent.getRepository();

  SubmoduleWalk walk = SubmoduleWalk.forIndex( parentRepository );

  while( walk.next() ) {

    Repository submoduleRepository = walk.getRepository();

    Git.wrap( submoduleRepository ).fetch().call();

    submoduleRepository.close();

    submoduleCount++;

  }

  walk.release();

 

  assertEquals( 1, submoduleCount );

}

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/16406.html