我正在启动一个新的 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 个存储库可供使用。因此,如果这是唯一的解决方案,我会放弃这个想法。
我已经阅读过的一些链接让我认为我所要求的是可能的:
这是我的问题:
- 如何创建缩减分支
design
(git checkout -b design
和 git mv/rm?)
- 如何配置 git 来跟踪跨分支的编辑(这样我就可以从主分支进行 git 合并设计,反之亦然)
更新:
我发现了另一种可能的方法对于这两个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 实现,这是根本无法实现的。
我很想被反驳,但我担心这不会发生。
I'm starting a new Zend Framework project in which I will collaborate with a designer. 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. 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.
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.
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. 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. 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:
- How to create the reduced branch
design
(git checkout -b design
and git mv/rm
?)
- How to configure git to keep track of edits across branches (so I can
git merge design
from the master branch and vice versa)
Update:
I found out another possible approach to the problem given by these two SO questions
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
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.
I pushed my experiments on GitHub
Update 2:
I think that currently there is no solution for that. With the current git implementation this is simply not achievable.
I'd like to be contradicted, but I'm afraid that it won't happen.
发布评论
评论(1)
听起来您希望能够限制每个目录的读取访问权限。这是可能的,但我所知道的唯一解决方案远非简单。它涉及服务器上同一存储库的多个版本,每个版本都使用一些复杂的钩子魔术来过滤子目录来保持同步。
我正在利用业余时间致力于实现这些钩子,最终目标是将它们作为开源软件发布(可能作为 gitolite 的一个功能补充),但不幸的是我的业余时间有限。
存储库
一般解决方案至少涉及同一存储库的三个变体: 一个协调两个或多个委托存储库的权威存储库。用户绝不会克隆权限库;仅克隆委托存储库。
代表负责将传入的提交转发到权限存储库。权限存储库负责为每个其他委托存储库适当地过滤传入的提交。然后将结果推送给其他代表。
权限存储库并不是严格必需的 - 代表可以自己执行过滤,然后将结果直接推送给其他代表 - 但使用另一个存储库作为集中协调器可以大大简化实施。
委托存储库
每个委托存储库包含整个项目数据的子集(例如,过滤掉的零个或多个子目录)。所有委托存储库彼此相同,只是每个委托过滤掉了一组不同的文件。它们都具有相同的提交历史记录图,但提交将具有不同的文件内容,因此具有不同的 SHA1 标识符。它们具有相同的分支和标签集(换句话说,如果项目有一个
master
分支,那么每个委托存储库也有一个master
分支),但是因为等效提交的 SHA1 标识符不同,引用将指向不同的 SHA1 标识符。例如,以下是两个委托存储库的内容的图表。
everything.git
存储库没有过滤掉任何内容,但no-foo.git
存储库已过滤掉子目录foo
中的所有内容。请注意,这两个图看起来相同,具有相同的提交消息、相同的分支名称等。唯一的区别是 SHA1 ID,因为文件内容不同。
(旁注:提交也可以被过滤掉,以防止另一个委托的用户知道在过滤出的目录中进行了提交。但是,只有当提交只涉及过滤目录中的文件时,才可以过滤掉提交。 out 目录。否则,会出现钩子无法自动解决的合并冲突。)
权限存储库
权限存储库是所有委托权限的超集。每个委托存储库中的所有提交对象都会通过每个委托存储库中的挂钩自动推送到权限存储库中。因此,如果有两个委托存储库,则权限存储库中将有两个同构 DAG(每个委托一个)(假设委托不共享公共根提交)。
权限存储库还将具有每个委托的每个项目分支的版本,以委托名称为前缀。继续上面的示例,
everything.git
委托存储库有一个master
分支指向提交2faaad9
,而委托no-foo。 git
有一个master
分支,指向已过滤但其他等效的提交81c2189
。在这种情况下,authority.git 将有两个主分支:指向 2faaad9 的everything/master
和指向no-foo/master< /code> 指向
81c2189
。下图说明了这一点。请注意,每个提交都有两个版本,每个委托都有一个版本。另请注意分支名称。
挂钩
委托存储库
每个委托将提交提交到权限存储库。
当用户更新委托存储库中的引用(通过
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
为前缀。当您推送到
everything.git
中的master
时,会发生以下情况:everything.git
中的update
挂钩会将传入的提交推送到authority.git
中的everything/master
分支。authority.git
中的update
挂钩将:application
和public
子目录之外的所有内容。zend-project/master
以指向新的提交。authority.git
中的zend-project/master
推送到zend-project.git
中的master
。当您的设计者推送到
zend-project.git
中的master
时,会发生以下情况:zend-project 中的
会将传入的提交推送到update
挂钩。 gitauthority.git
中的zend-project/master
分支。authority.git
中的update
挂钩将:application
或public
子目录之外创建的。如果是这样,则返回一条错误消息。everything/master
的其他子目录。everything/master
以指向新的提交。authority.git
中的everything/master
推送到everything.git
中的master
。注释
上面描述了一种实现按目录读访问控制的方法。如果您确实不希望某些用户能够访问存储库的部分内容,那么它应该是合适的。就您而言,设计人员的便利可能比限制访问更重要。如果是这样,可能有一种更简单的方法来完成您想要的事情。
我希望我能够足够清楚地解释这一点。
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.
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 (e.g., 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. 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 amaster
branch), but because the SHA1 identifiers for the equivalent commits are different, the references will point to different SHA1 identifiers.For example, the following are graphs of the contents of two delegate repositories. The
everything.git
repository doesn't have anything filtered out, but theno-foo.git
repository has everything in subdirectoryfoo
filtered out.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.
(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.)
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).
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 amaster
branch pointing to commit2faaad9
, while delegateno-foo.git
has amaster
branch pointing to the filtered-but-otherwise-equivalent commit81c2189
. In this scenario,authority.git
would have two master branches:everything/master
pointing to2faaad9
andno-foo/master
pointing to81c2189
. The following graph illustrates this.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'supdate
hook automatically does agit push
into the authority repository. 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 (e.g., if the delegate repository is namedfoo.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
).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: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)zend-project.git
(for your designer)Branches in
authority.git
would be prefixed byeverything
andzend-project
corresponding to the two delegate repositories.When you push to
master
ineverything.git
, the following would happen:update
hook ineverything.git
would push the incoming commits to theeverything/master
branch inauthority.git
.update
hook inauthority.git
would:application
andpublic
subdirectories.zend-project/master
to point to the new commit.zend-project/master
inauthority.git
tomaster
inzend-project.git
.When your designer pushes to
master
inzend-project.git
, the following would happen:update
hook inzend-project.git
would push the incoming commits to thezend-project/master
branch inauthority.git
.update
hook inauthority.git
would:application
orpublic
subdirectories. If so, return with an error message.everything/master
grafted in.everything/master
to point to the new commit.everything/master
inauthority.git
tomaster
ineverything.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.