【问题标题】:Git subtree merge strategy or subtree command?Git子树合并策略或子树命令?
【发布时间】:2011-09-26 12:31:16
【问题描述】:

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

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

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

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

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

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

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

这是我的问题:

  1. 如何创建缩减分支designgit checkout -b designgit mv/rm?)
  2. 如何配置 git 以跟踪跨分支的编辑(这样我就可以从 master 分支 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.

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

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

我将我的实验推送到GitHub

更新 2:

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

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

【问题讨论】:

  • 我不了解Zend,所以我只会使用注释,但一般来说,您为什么不将项目拆分为他拥有RW权限的部分,其余部分?他所有的提交等都只对 RW 项目进行,其余的他只是使用拉取来获取最新版本。这就是我将如何处理某些库不可修改或我想使用 3rd 方交付的任何系统。
  • @Makis 基本上有两个原因:首先因为他需要访问多个文件夹(在我的情况下至少三个不同的文件夹),所以维护很多子模块会很痛苦,其次因为我希望他只能看到项目的一部分,以便让事情变得简单。
  • 但是你为什么要使用子模块,这就是我要问的?您可以创建一个脚本,将所有其他项目更新到正确的目录。
  • 类似于将更改拉入专用文件夹并将它们复制到不同的地方?可能是一个想法,我什至可以制作符号链接并避免复制。这样,我只需要维护几个存储库。
  • 是的,类似的,取决于您的具体需求。

标签: git branching-and-merging git-merge git-subtree


【解决方案1】:

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

我在业余时间致力于实现这些钩子,最终目标是将它们作为开源软件发布(可能作为 gitolite 的一个附加功能),但不幸的是,我的业余时间有限。

存储库

一般的解决方案至少涉及同一存储库的三个变体:一个authority 存储库协调两个或多个delegate 存储库。用户从不克隆权限库;仅克隆委托存储库。

委托负责将传入的提交转发到权限存储库。权限存储库负责为每个其他委托存储库适当地过滤传入的提交。然后将结果下推给其他代表。

权限存储库不是严格要求的——代表可以自己执行过滤,然后将结果直接推送给其他代表——但是使用另一个存储库作为集中协调器可以大大简化实施。

委托存储库

每个委托存储库都包含整个项目数据的一个子集(例如,过滤掉的零个或多个子目录)。所有委托存储库都彼此相同,只是每个委托都有一组不同的文件被过滤掉。它们都具有相同的提交历史图,但提交将具有不同的文件内容,因此具有不同的 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,因为文件内容不同。

(旁注:提交也可以被过滤掉,以防止其他委托的用户甚至知道在过滤掉的目录中进行了提交。但是,只有在提交仅涉及文件时才能过滤掉一个被过滤掉的目录。否则,会出现无法通过钩子自动解决的合并冲突。)

权威存储库

权限存储库是所有委托权限的超集。每个委托仓库中的所有提交对象都会通过每个委托仓库中的挂钩自动推送到授权仓库中。因此,如果有两个委托存储库,则授权存储库中将有两个同构 DAG(每个委托一个)(假设委托不共享公共根提交)。

权限存储库还将具有来自每个委托的每个项目分支的版本,以委托名称为前缀。继续上面的例子,everything.git 代理存储库有一个 master 分支指向提交 2faaad9,而代理 no-foo.git 有一个 master 分支指向过滤但其他等效提交 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,那么它将使用推送参考规范,如@ 987654343@ 和 +refs/tags/v1.0:refs/tags/foo/v1.0)。

权威存储库

授权存储库过滤传入的提交并将它们推送到其他委托存储库。

当委托仓库推入权限仓库时,权限的update钩子:

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

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

您的情况

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

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

authority.git 中的分支将以 everythingzend-project 为前缀,对应于两个委托存储库。

当您在everything.git 中推送到master 时,会发生以下情况:

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

当您的设计师在zend-project.git 中推送到master 时,会发生以下情况:

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

注意事项

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

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

【讨论】:

  • 我需要更好地阅读您的答案,因为它很长而且很详细。不过听起来不错,感谢您的宝贵时间。您是否已经在某处分享了您工作的概念证明?这对 gitolite 来说是一个很棒的功能,但它需要更加用户友好,你为什么不 fork gitolite 项目并为你的想法添加一个功能分支?
  • @Fabio:不,我还没有在任何地方发布任何代码。我确实打算扩展 gitolite 并将代码提交给 gitolite 开发人员,但我不确定我什么时候有时间这样做。
  • 我奖励你是因为我喜欢你的回答,即使我还不能使用它。如果您发布某些内容,请随时通知我。
猜你喜欢
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 2015-02-28
  • 1970-01-01
  • 2019-06-07
相关资源
最近更新 更多