5

我正在开始一个新的 Zend Framework 项目,我将在其中与设计师合作。我将使用 git 维护这个项目代码,通常设计师不会说 git(或任何编程语言),所以我想让他的事情变得简单,否则我担心他根本不会使用 git。我的计划是给他一些 Git gui,然后他应该只使用基本的 git 功能,例如提交、差异、获取、合并、推送和拉取。

我正在使用 gitolite 来维护我们的 git 存储库的共享副本,并且由于它具有细粒度的权限系统,我将仅授予设计人员 RW 访问权限以访问专用分支(设计)并读取其他分支的访问权限。

为了简单起见,我只想与他分享主项目中的一些文件夹(遵循ZF 推荐的结构),他确实需要访问这些文件夹才能完成工作。同时我希望我们俩仍然可以相互融合。

他的分支的简化结构应该是这样的:

<project name>/
    application/
        layouts/
            scripts/
        views/
            scripts/
    public/
        css/
        images/
        js/

我知道我可以使用子模块来完成这项任务,但维护起来会很痛苦,因为我应该将我的项目拆分为(至少)4 个子存储库,他应该只能访问子存储库,并且他有 3 个存储库可以使用. 出于这个原因,如果这是唯一的解决方案,我将放弃这个想法。

我已经阅读过的一些链接让我认为我所要求的是可能的:

这是我的问题:

  1. 如何创建缩减分支designgit checkout -b designgit mv/rm?)
  2. 如何配置 git 以跟踪跨分支的编辑(所以我可以git merge design从主分支,反之亦然)

更新:

我发现了解决这两个 SO 问题的另一种可能方法

我尝试git rm all-unneeded-stuff在设计分支中实现第一个之后,我在主分支中进行了提交,其中涉及白名单路径中的一个文件和黑名单路径中的另一个文件,但git merge失败并显示以下消息

CONFLICT (delete/modify): application/Bootstrap.php deleted in HEAD and modified in master. Version master of application/Bootstrap.php left in tree.

然后我在主分支中添加了一个新目录,并在从设计合并时添加了新目录。我在驱动程序中放了一些调试回显,我发现在这两种情况下都没有调用它,可能是因为它不是真正的合并。

我还没有尝试过第二种方法(.gitignore 方法),但如果我理解该方法不符合我的需求,因为它只会忽略设计分支中的黑名单文件,但它们将在设计分支,打破了我的要求。

我在GitHub 上推送了我的实验

更新 2:

我认为目前没有解决方案。使用当前的 git 实现,这根本无法实现。

我很想被反驳,但我又怕它不会发生。

4

1 回答 1

6

听起来您希望能够在每个目录的基础上限制读取访问。这是可能的,但我知道的唯一解决方案远非简单。它涉及服务器上同一个存储库的多个版本,每个版本都使用一些复杂的钩子魔法来过滤掉子目录,从而保持同步。

我在业余时间致力于实现这些钩子,最终目标是将它们作为开源软件发布(也许作为 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.gitdelegate 存储库有一个master指向 commit 的分支2faaad9,而 delegateno-foo.git有一个master指向 filter-but-other-equivalent commit的分支81c2189。在这种情况下,authority.git将有两个主分支: everything/master指向2faaad9no-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钩子:

  1. 检查用户是否尝试在过滤出的目录之一中创建文件。如果是这样,它会以错误退出(否则可能会出现无法自动解决的合并冲突)。
  2. 移植回最初过滤掉的子目录,形成一棵没有过滤掉的树。
  3. 对于每个其他委托,过滤未过滤的树以进行等效提交,并删除适当的内容。
  4. 将等效提交推送到委托存储库。

必须注意避免委托存储库之间的竞争条件并正确处理错误。

你的案例

在您的示例中,您将有两个这样的委托存储库:

  • everything.git(为你)
  • zend-project.git(给你的设计师)

分支authority.git将以两个代理存储库为前缀everythingzend-project对应于两个代理存储库。

当你推入时mastereverything.git会发生以下情况:

  1. update钩子everything.git会将传入的提交推everything/master送到authority.git.
  2. 对于每个传入的提交,update钩子authority.git将:
    1. 创建一个与提交树 100% 相同的新树对象,但删除applicationpublic子目录之外的所有内容。
    2. 使用新树和等效父级创建新提交对象,但重用原始提交消息、作者和时间戳。
    3. 更新zend-project/master以指向新的提交。
  3. zend-project/master入。authority.git_ master_zend-project.git

当您的设计师推入时masterzend-project.git会发生以下情况:

  1. update钩子zend-project.git会将传入的提交推zend-project/master送到authority.git.
  2. 对于每个传入的提交,update钩子authority.git将:
    1. 检查是否在applicationpublic子目录之外创建了任何新文件。如果是,则返回错误消息。
    2. 创建一个与提交的树 100% 相同的新树对象,除了来自everything/master嫁接的其他子目录。
    3. 使用新树和等效父级创建新提交对象,但重用原始提交消息、作者和时间戳。
    4. 更新everything/master以指向新的提交。
  3. everything/master入。authority.git_ master_everything.git

笔记

上面描述了一种实现按目录读取访问控制的方法。如果您真的不希望某些用户能够访问存储库的某些部分,它应该是合适的。在您的情况下,为您的设计师提供便利可能比限制访问更重要。如果是这样,可能有一种更简单的方法来完成你想要的。

我希望我能够足够清楚地解释这一点。

于 2011-09-26T04:31:16.170 回答