简体   繁体   English

Git 子树合并策略或子树命令?

[英]Git subtree merge strategy or subtree command?

I'm starting a new Zend Framework project in which I will collaborate with a designer.我正在开始一个新的 Zend Framework 项目,我将在其中与设计师合作。 I'm going to maintain this project code using git and usually designers don't speak git (or any programming language) so I wanna make things easy for him, otherwise I'm afraid he won't use git at all.我将使用 git 维护这个项目代码,通常设计师不会说 git(或任何编程语言)所以我想让他的事情变得容易,否则我担心他根本不会使用 ZBA9F11ECC3497D6993B933FDCBD2 My plan is to give him some Git gui and with that he should use only basic git features such as commit, diff, fetch, merge, push and pull.我的计划是给他一些 Git gui,然后他应该只使用基本的 git 功能,例如提交、差异、获取、合并、推送和拉取。

I'm using gitolite to maintain the shared copy of our git repository and since it has a granular permission system, I will give the designer RW access only for a dedicated branch (design) and read access to other branches.我正在使用 gitolite 来维护我们的 git 存储库的共享副本,并且由于它具有细粒度的权限系统,因此我将仅授予设计人员对专用分支(设计)的 RW 访问权限和对其他分支的读取权限。

To keep things simple I'd like to share with him only some of the folders in the main project (which follows ZF recommended structure ) for which he does need access for doing its job.为了简单起见,我只想与他分享主项目中的一些文件夹(遵循ZF 推荐的结构),他确实需要访问这些文件夹才能完成工作。 At the same time I want that both of us can still merge from each other.同时我希望我们俩仍然可以相互融合。

The reduced structure for his branch should be this:他的分支的简化结构应该是这样的:

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

I know that I could use submodules for this task, but it will be a pain to maintain because I should split my project in (at least) 4 subrepository, he should have access only to subrepositories and he'd have 3 repository to work with.我知道我可以使用子模块来完成这项任务,但是维护起来会很痛苦,因为我应该将我的项目拆分为(至少)4 个子存储库,他应该只能访问子存储库,并且他有 3 个存储库可以使用. For this reason if this is the only solution I'll give up with this idea.出于这个原因,如果这是唯一的解决方案,我将放弃这个想法。

Some links I've already read that make me think that what I'm asking is possible:我已经阅读过的一些链接让我认为我所要求的是可能的:

Here are my question:这是我的问题:

  1. How to create the reduced branch design ( git checkout -b design and git mv/rm ?)如何创建简化的分支designgit checkout -b designgit mv/rm ?)
  2. How to configure git to keep track of edits across branches (so I can git merge design from the master branch and vice versa)如何配置 git 以跟踪跨分支的编辑(所以我可以git merge design ,反之亦然)

Update:更新:

I found out another possible approach to the problem given by these two SO questions我发现了解决这两个 SO 问题的另一种可能方法

I tried to implement the first after git rm all-unneeded-stuff in the design branch, I make a commit in the master branch which involves a file in the whitelisted paths and another file in the blacklisted paths, but git merge fail with the following message我尝试在设计分支中实现git rm all-unneeded-stuff之后的第一个,我在 master 分支中进行了提交,其中涉及白名单路径中的一个文件和黑名单路径中的另一个文件,但是git merge失败并出现以下问题信息

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

Then I added a new dir in the master branch and when merging from design the new dir is added.然后我在主分支中添加了一个新目录,并在从设计合并时添加了新目录。 I put some debug echo in the driver and I saw that it hasn't be called in both cases, maybe because it's not a real merge.我在驱动程序中放了一些调试回显,我发现在这两种情况下都没有调用它,可能是因为它不是真正的合并。

I haven't tried the second approach (the.gitignore one) yet, but if I've understood the approach doesn't fit my needs because it will only ignore blacklisted files in the design branch, but they will be checked out in the design branch, breaking my requirements.我还没有尝试过第二种方法(.gitignore 一种),但如果我理解这种方法不符合我的需要,因为它只会忽略设计分支中的黑名单文件,但它们会在设计分支,打破了我的要求。

I pushed my experiments on GitHub我在GitHub上进行了实验

Update 2:更新 2:

I think that currently there is no solution for that.我认为目前没有解决方案。 With the current git implementation this is simply not achievable.使用当前的 git 实现,这根本无法实现。

I'd like to be contradicted, but I'm afraid that it won't happen.我想被反驳,但我又怕它不会发生。

Sounds like you want to be able to restrict read access on a per-directory basis.听起来您希望能够在每个目录的基础上限制读取访问。 This is possible, but the only solution I'm aware of is far from simple.这是可能的,但我知道的唯一解决方案远非简单。 It involves multiple versions of the same repository on your server, each kept in sync using some complicated hook magic to filter out the subdirectories.它涉及服务器上同一个存储库的多个版本,每个版本都使用一些复杂的钩子魔法来过滤掉子目录,从而保持同步。

I'm working on implementing the hooks in my spare time with the eventual goal of publishing them as open source software (perhaps as a feature addition to gitolite), but unfortunately my spare time is limited.我在业余时间致力于实现这些钩子,最终目标是将它们作为开源软件发布(也许作为 gitolite 的一个功能),但不幸的是,我的业余时间有限。

Repositories存储库

The general solution involves at least three variants of the same repository: One authority repository that coordinates two or more delegate repositories.通用解决方案涉及同一存储库的至少三个变体: 一个协调两个或多个委托存储库的权限存储库。 Users never clone the authority repository;用户从不克隆权限库; only delegate repositories are cloned.只有委托存储库被克隆。

The delegates are responsible for forwarding incoming commits to the authority repository.代表负责将传入的提交转发到权限存储库。 The authority repository is responsible for filtering the incoming commits appropriately for each other delegate repository.权限存储库负责为每个其他委托存储库适当地过滤传入的提交。 The results are then pushed down to the other delegates.然后将结果下推给其他代表。

The authority repository isn't strictly required—delegates could perform the filtering on their own and then push the results directly to the other delegates—but using another repository as a centralized coordinator simplifies implementation considerably.权限存储库不是严格要求的——代表可以自己执行过滤,然后将结果直接推送给其他代表——但是使用另一个存储库作为集中协调器可以大大简化实施。

Delegate Repositories委托存储库

Each delegate repository contains a subset of the entire project's data (eg, zero or more subdirectories filtered out).每个委托存储库都包含整个项目数据的子集(例如,过滤掉的零个或多个子目录)。 All delegate repositories are identical to each other except each delegate has a different set of files filtered out.所有委托存储库都彼此相同,只是每个委托都有一组不同的文件被过滤掉。 They all have the same commit history graph, but the commits will have different file contents and thus different SHA1 identifiers.它们都具有相同的提交历史图,但提交将具有不同的文件内容,因此具有不同的 SHA1 标识符。 They have the same set of branches and tags (in other words, if the project has a master branch, then each delegate repository also has a master branch), but because the SHA1 identifiers for the equivalent commits are different, the references will point to different SHA1 identifiers.它们具有相同的分支和标签集(换句话说,如果项目有一个master分支,那么每个委托存储库也有一个master分支),但是因为等效提交的 SHA1 标识符不同,所以引用将指向不同的 SHA1 标识符。

For example, the following are graphs of the contents of two delegate repositories.例如,以下是两个委托存储库的内容图表。 The everything.git repository doesn't have anything filtered out, but the no-foo.git repository has everything in subdirectory foo filtered out. 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

Notice that the two graphs look the same, have the same commit messages, the same branch names, etc. The only difference is the SHA1 IDs due to the fact that the file contents are different.请注意,这两个图看起来相同,具有相同的提交消息、相同的分支名称等。唯一的区别是 SHA1 ID,因为文件内容不同。

(Side note: Commits can be filtered out as well to prevent users of another delegate from even knowing that a commit in a filtered-out directory was made. However, a commit can only be filtered out if it only touches files in a filtered-out directory. Otherwise, there would be merge conflicts that could not be automatically resolved by the hooks.) (旁注:提交也可以被过滤掉,以防止另一个委托的用户甚至知道在过滤掉的目录中进行了提交。但是,只有在提交只涉及过滤后的文件时才能过滤掉 - out 目录。否则会出现无法通过 hooks 自动解决的合并冲突。)

Authority Repository权威资料库

The authority repository is a superset of all of the delegate authorities.权限存储库是所有委托权限的超集。 All commit objects in each delegate repository are automatically pushed into the authority repository via a hook in each delegate repository.每个委托仓库中的所有提交对象都会通过每个委托仓库中的挂钩自动推送到授权仓库中。 Thus, if there are two delegate repositories, there will be two isomorphic DAGs (one from each delegate) in the authority repository (assuming the delegates don't share a common root commit).因此,如果有两个委托存储库,则权限存储库中将有两个同构 DAG(每个委托一个)(假设委托不共享公共根提交)。

The authority repository will also have a version of each project branch from each delegate, prefixed by the name of the delegate.权限存储库还将具有来自每个委托的每个项目分支的版本,以委托名称为前缀。 Continuing the above example, the everything.git delegate repository has a master branch pointing to commit 2faaad9 , while delegate no-foo.git has a master branch pointing to the filtered-but-otherwise-equivalent commit 81c2189 .继续上面的示例, everything.git委托存储库有一个master分支指向提交2faaad9 ,而委托no-foo.git有一个master分支指向过滤但其他等效的提交81c2189 In this scenario, authority.git would have two master branches: everything/master pointing to 2faaad9 and no-foo/master pointing to 81c2189 .在这种情况下, authority.git将有两个 master 分支: everything/master指向2faaad9no-foo/master指向81c2189 The following graph illustrates this.下图说明了这一点。

$ 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

Notice that there are two versions of each commit, one for each delegate.请注意,每个提交有两个版本,每个代表一个版本。 Also notice the branch names.还要注意分支名称。

Hooks挂钩

Delegate Repositories委托存储库

Each delegate feeds commits to the authority repository.每个委托都将提交提交给权限存储库。

When a user updates a reference (via git push ) in a delegate repository, that repository's update hook automatically does a git push into the authority repository.当用户更新委托存储库中的引用(通过git push )时,该存储库的update挂钩会自动将git push送到权限存储库中。 However, instead of using the standard push refspec, it uses a refspec that causes the reference in the authority's repository to be prefixed by the delegate repository's name (eg, if the delegate repository is named foo.git then it will use push refspecs like +refs/heads/master:refs/heads/foo/master and +refs/tags/v1.0:refs/tags/foo/v1.0 ).但是,它不使用标准推送 refspec,而是使用 refspec 导致授权存储库中的引用以委托存储库的名称为前缀(例如,如果委托存储库名为foo.git ,那么它将使用推送 refspec,如+refs/heads/master:refs/heads/foo/master+refs/tags/v1.0:refs/tags/foo/v1.0 )。

Authority Repository权威资料库

The authority repository filters incoming commits and pushes them down into the other delegate repositories.权限存储库过滤传入的提交并将它们推送到其他委托存储库。

When a delegate repository pushes into the authority repository, the authority's update hook:当委托仓库推送到权限仓库时,权限的update钩子:

  1. Checks to see if the user is trying to create a file in one of the filtered-out directories.检查用户是否尝试在过滤出的目录之一中创建文件。 If so, it exits with an error (otherwise there could be merge conflicts which can't be resolved automatically).如果是这样,它会以错误退出(否则可能会出现无法自动解决的合并冲突)。
  2. Grafts back in the subdirectories that were originally filtered out to form a tree that has nothing filtered out.移植回最初过滤掉的子目录,形成一棵没有过滤掉的树。
  3. For each other delegate, filter the unfiltered tree to make an equivalent commit with the appropriate contents removed.对于每个其他委托,过滤未过滤的树以进行等效提交,并删除适当的内容。
  4. Push the equivalent commits to the delegate repositories.将等效提交推送到委托存储库。

Care must be taken to avoid race conditions between delegate repositories and to properly handle errors.必须注意避免委托存储库之间的竞争条件并正确处理错误。

Your Case你的案例

In your example, you would have two delegate repositories like this:在您的示例中,您将有两个这样的委托存储库:

  • everything.git (for you) everything.git (给你)
  • zend-project.git (for your designer) zend-project.git (供您的设计师使用)

Branches in authority.git would be prefixed by everything and zend-project corresponding to the two delegate repositories. authority.git中的分支将以everything为前缀,而zend-project对应于两个委托存储库。

When you push to master in everything.git , the following would happen:当你 push to master in everything.git时,会发生以下情况:

  1. The update hook in everything.git would push the incoming commits to the everything/master branch in authority.git . everything.git中的update挂钩会将传入的提交推送到authority.git中的everything/master分支。
  2. For each incoming commit, the update hook in authority.git would:对于每个传入的提交, authority.git中的update挂钩将:
    1. Create a new tree object that is 100% identical to the commit's tree but remove everything outside of the application and public subdirectories.创建一个与提交树 100% 相同的新树 object,但删除applicationpublic子目录之外的所有内容。
    2. Create a new commit object using the new tree and equivalent parent(s), but reuse the original commit message, author, and timestamp.使用新树和等效父级创建新提交 object,但重用原始提交消息、作者和时间戳。
    3. Update zend-project/master to point to the new commit.更新zend-project/master以指向新的提交。
  3. Push zend-project/master in authority.git to master in zend-project.git .zend-project/master中的authority.git推送到zend-project.git中的master中。

When your designer pushes to master in zend-project.git , the following would happen:当您的设计人员推动master zend-project.git时,会发生以下情况:

  1. The update hook in zend-project.git would push the incoming commits to the zend-project/master branch in authority.git . zend-project.git中的update挂钩会将传入的提交推送到authority.git中的zend-project/master分支。
  2. For each incoming commit, the update hook in authority.git would:对于每个传入的提交, authority.git中的update挂钩将:
    1. Check to see if any new files were created outside the application or public subdirectories.检查是否在applicationpublic子目录之外创建了任何新文件。 If so, return with an error message.如果是,则返回错误消息。
    2. Create a new tree object that is 100% identical to the commit's tree except with the other subdirectories from everything/master grafted in.创建一个新的树 object,它与提交的树 100% 相同,除了来自everything/master节点的其他子目录。
    3. Create a new commit object using the new tree and equivalent parent(s), but reuse the original commit message, author, and timestamp.使用新树和等效父级创建新提交 object,但重用原始提交消息、作者和时间戳。
    4. Update everything/master to point to the new commit.更新everything/master以指向新的提交。
  3. Push everything/master in authority.git to master in everything.git .推送everything/master in authority.git to master in everything.git

Notes笔记

The above describes a way to implement per-directory read access control.上面描述了一种实现按目录读取访问控制的方法。 It should be suitable if you really don't want certain users to be able to access parts of the repository.如果您真的不希望某些用户能够访问存储库的某些部分,它应该是合适的。 In your case, convenience for your designer may be more important than limiting access.在您的情况下,为您的设计师提供便利可能比限制访问更重要。 If so, there may be a simpler way to accomplish what you want.如果是这样,可能有一种更简单的方法来完成你想要的。

I hope I was able to explain this clearly enough.我希望我能够足够清楚地解释这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM