听起来您希望能够在每个目录的基础上限制读取访问。这是可能的,但我知道的唯一解决方案远非简单。它涉及服务器上同一个存储库的多个版本,每个版本都使用一些复杂的钩子魔法来过滤掉子目录,从而保持同步。
我在业余时间致力于实现这些钩子,最终目标是将它们作为开源软件发布(也许作为 gitolite 的附加功能),但不幸的是,我的业余时间有限。
存储库
通用解决方案涉及同一存储库的至少三个变体: 一个协调两个或多个委托存储库的权限存储库。用户从不克隆权限库;只有委托存储库被克隆。
代表负责将传入的提交转发到权限存储库。权限存储库负责为每个其他委托存储库适当地过滤传入的提交。然后将结果下推给其他代表。
权限存储库不是严格要求的——代表可以自己执行过滤,然后将结果直接推送给其他代表——但是使用另一个存储库作为集中协调器可以大大简化实施。
委托存储库
每个委托存储库都包含整个项目数据的子集(例如,过滤掉的零个或多个子目录)。所有委托存储库都彼此相同,只是每个委托都有一组不同的文件被过滤掉。它们都具有相同的提交历史图,但提交将具有不同的文件内容,因此具有不同的 SHA1 标识符。它们有相同的分支和标签集(换句话说,如果项目有一个master
分支,那么每个委托仓库也有一个master
分支),但是因为等效提交的 SHA1 标识符不同,所以引用会指向不同的 SHA1身份标识。
例如,以下是两个委托存储库的内容图表。存储库everything.git
没有过滤掉任何内容,但no-foo.git
存储库已过滤掉子目录中的所有内容foo
。
$ cd ~git/repositories/everything.git
$ git log --graph --oneline --decorate --date-order --all
* 2faaad9 (HEAD, master) barbaz
| * c3eb6a9 (release) foobar
* | 8b56913 Merge branch 'release'
|\ \
| |/
| * b8f899c qux
* | aad30f1 baz
|/
* f4acd9f put a new file in subdirectory bar
* 2a15586 put a new file in subdirectory foo
$ cd ~git/repositories/no-foo.git
$ git log --graph --oneline --decorate --date-order --all
* 81c2189 (HEAD, master) barbaz
| * 6bbd85f (release) foobar
* | c579c4b Merge branch 'release'
|\ \
| |/
| * 42c45c7 qux
* | 90ecdc7 baz
|/
* 4d1cd8d put a new file in subdirectory bar
* 9cc719d put a new file in subdirectory foo
请注意,这两个图看起来相同,具有相同的提交消息、相同的分支名称等。唯一的区别是 SHA1 ID,因为文件内容不同。
(旁注:提交也可以被过滤掉,以防止另一个委托的用户甚至知道在过滤掉的目录中进行了提交。但是,只有当它只涉及过滤后的文件时,才能过滤掉提交- out 目录。否则会出现无法通过 hooks 自动解决的合并冲突。)
权威资料库
权限存储库是所有委托权限的超集。每个委托仓库中的所有提交对象都会通过每个委托仓库中的挂钩自动推送到授权仓库中。因此,如果有两个委托存储库,则权限存储库中将有两个同构 DAG(每个委托一个)(假设委托不共享公共根提交)。
权限存储库还将具有来自每个委托的每个项目分支的版本,以委托名称为前缀。继续上面的例子,everything.git
delegate 存储库有一个master
指向 commit 的分支2faaad9
,而 delegateno-foo.git
有一个master
指向 filter-but-other-equivalent commit的分支81c2189
。在这种情况下,authority.git
将有两个主分支: everything/master
指向2faaad9
和no-foo/master
指向81c2189
. 下图说明了这一点。
$ cd ~git/repositories/authority.git
$ git log --graph --oneline --decorate --date-order --all
* 2faaad9 (everything/master) barbaz
| * 81c2189 (no-foo/master) barbaz
| | * c3eb6a9 (everything/release) foobar
| | | * 6bbd85f (no-foo/release) foobar
* | | | 8b56913 Merge branch 'release'
|\ \ \ \
| | |/ /
| |/| |
| | * | c579c4b Merge branch 'release'
| | |\ \
| | | |/
| * | | b8f899c qux
| | | * 42c45c7 qux
* | | | aad30f1 baz
|/ / /
| * | 90ecdc7 baz
| |/
* | f4acd9f put a new file in subdirectory bar
| * 4d1cd8d put a new file in subdirectory bar
* | 2a15586 put a new file in subdirectory foo
/
* 9cc719d put a new file in subdirectory foo
请注意,每个提交有两个版本,每个代表一个版本。还要注意分支名称。
挂钩
委托存储库
每个委托都将提交提交给权限存储库。
当用户更新git push
委托存储库中的引用(通过 )时,该存储库的update
挂钩会自动git push
进入权限存储库。但是,它不使用标准的推送参考规范,而是使用参考规范,该参考规范导致授权存储库中的引用以委托存储库的名称为前缀(例如,如果委托存储库被命名foo.git
,那么它将使用推送参考规范,如+refs/heads/master:refs/heads/foo/master
和+refs/tags/v1.0:refs/tags/foo/v1.0
)。
权威资料库
权限存储库过滤传入的提交并将它们推送到其他委托存储库。
当委托仓库推送到权限仓库时,权限的update
钩子:
- 检查用户是否尝试在过滤出的目录之一中创建文件。如果是这样,它会以错误退出(否则可能会出现无法自动解决的合并冲突)。
- 移植回最初过滤掉的子目录,形成一棵没有过滤掉的树。
- 对于每个其他委托,过滤未过滤的树以进行等效提交,并删除适当的内容。
- 将等效提交推送到委托存储库。
必须注意避免委托存储库之间的竞争条件并正确处理错误。
你的案例
在您的示例中,您将有两个这样的委托存储库:
everything.git
(为你)
zend-project.git
(给你的设计师)
分支authority.git
将以两个代理存储库为前缀everything
并zend-project
对应于两个代理存储库。
当你推入时master
,everything.git
会发生以下情况:
update
钩子everything.git
会将传入的提交推everything/master
送到authority.git
.
- 对于每个传入的提交,
update
钩子authority.git
将:
- 创建一个与提交树 100% 相同的新树对象,但删除
application
和public
子目录之外的所有内容。
- 使用新树和等效父级创建新提交对象,但重用原始提交消息、作者和时间戳。
- 更新
zend-project/master
以指向新的提交。
- 推
zend-project/master
入。authority.git
_ master
_zend-project.git
当您的设计师推入时master
,zend-project.git
会发生以下情况:
update
钩子zend-project.git
会将传入的提交推zend-project/master
送到authority.git
.
- 对于每个传入的提交,
update
钩子authority.git
将:
- 检查是否在
application
或public
子目录之外创建了任何新文件。如果是,则返回错误消息。
- 创建一个与提交的树 100% 相同的新树对象,除了来自
everything/master
嫁接的其他子目录。
- 使用新树和等效父级创建新提交对象,但重用原始提交消息、作者和时间戳。
- 更新
everything/master
以指向新的提交。
- 推
everything/master
入。authority.git
_ master
_everything.git
笔记
上面描述了一种实现按目录读取访问控制的方法。如果您真的不希望某些用户能够访问存储库的某些部分,它应该是合适的。在您的情况下,为您的设计师提供便利可能比限制访问更重要。如果是这样,可能有一种更简单的方法来完成你想要的。
我希望我能够足够清楚地解释这一点。