Browse Source
* js/ref-namespaces: ref namespaces: tests ref namespaces: documentation ref namespaces: Support remote repositories via upload-pack and receive-pack ref namespaces: infrastructure Fix prefix handling in ref iteration functionsmaint

15 changed files with 326 additions and 24 deletions
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
gitnamespaces(7) |
||||
================ |
||||
|
||||
NAME |
||||
---- |
||||
gitnamespaces - Git namespaces |
||||
|
||||
DESCRIPTION |
||||
----------- |
||||
|
||||
Git supports dividing the refs of a single repository into multiple |
||||
namespaces, each of which has its own branches, tags, and HEAD. Git can |
||||
expose each namespace as an independent repository to pull from and push |
||||
to, while sharing the object store, and exposing all the refs to |
||||
operations such as linkgit:git-gc[1]. |
||||
|
||||
Storing multiple repositories as namespaces of a single repository |
||||
avoids storing duplicate copies of the same objects, such as when |
||||
storing multiple branches of the same source. The alternates mechanism |
||||
provides similar support for avoiding duplicates, but alternates do not |
||||
prevent duplication between new objects added to the repositories |
||||
without ongoing maintenance, while namespaces do. |
||||
|
||||
To specify a namespace, set the `GIT_NAMESPACE` environment variable to |
||||
the namespace. For each ref namespace, git stores the corresponding |
||||
refs in a directory under `refs/namespaces/`. For example, |
||||
`GIT_NAMESPACE=foo` will store refs under `refs/namespaces/foo/`. You |
||||
can also specify namespaces via the `--namespace` option to |
||||
linkgit:git[1]. |
||||
|
||||
Note that namespaces which include a `/` will expand to a hierarchy of |
||||
namespaces; for example, `GIT_NAMESPACE=foo/bar` will store refs under |
||||
`refs/namespaces/foo/refs/namespaces/bar/`. This makes paths in |
||||
`GIT_NAMESPACE` behave hierarchically, so that cloning with |
||||
`GIT_NAMESPACE=foo/bar` produces the same result as cloning with |
||||
`GIT_NAMESPACE=foo` and cloning from that repo with `GIT_NAMESPACE=bar`. It |
||||
also avoids ambiguity with strange namespace paths such as `foo/refs/heads/`, |
||||
which could otherwise generate directory/file conflicts within the `refs` |
||||
directory. |
||||
|
||||
linkgit:git-upload-pack[1] and linkgit:git-receive-pack[1] rewrite the |
||||
names of refs as specified by `GIT_NAMESPACE`. git-upload-pack and |
||||
git-receive-pack will ignore all references outside the specified |
||||
namespace. |
||||
|
||||
The smart HTTP server, linkgit:git-http-backend[1], will pass |
||||
GIT_NAMESPACE through to the backend programs; see |
||||
linkgit:git-http-backend[1] for sample configuration to expose |
||||
repository namespaces as repositories. |
||||
|
||||
For a simple local test, you can use linkgit:git-remote-ext[1]: |
||||
|
||||
---------- |
||||
git clone ext::'git --namespace=foo %s /tmp/prefixed.git' |
||||
---------- |
||||
|
||||
SECURITY |
||||
-------- |
||||
|
||||
Anyone with access to any namespace within a repository can potentially |
||||
access objects from any other namespace stored in the same repository. |
||||
You can't directly say "give me object ABCD" if you don't have a ref to |
||||
it, but you can do some other sneaky things like: |
||||
|
||||
. Claiming to push ABCD, at which point the server will optimize out the |
||||
need for you to actually send it. Now you have a ref to ABCD and can |
||||
fetch it (claiming not to have it, of course). |
||||
|
||||
. Requesting other refs, claiming that you have ABCD, at which point the |
||||
server may generate deltas against ABCD. |
||||
|
||||
None of this causes a problem if you only host public repositories, or |
||||
if everyone who may read one namespace may also read everything in every |
||||
other namespace (for instance, if everyone in an organization has read |
||||
permission to every repository). |
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
#!/bin/sh |
||||
|
||||
test_description='fetch/push involving ref namespaces' |
||||
. ./test-lib.sh |
||||
|
||||
test_expect_success setup ' |
||||
test_tick && |
||||
git init original && |
||||
( |
||||
cd original && |
||||
echo 0 >count && |
||||
git add count && |
||||
test_commit 0 && |
||||
echo 1 >count && |
||||
git add count && |
||||
test_commit 1 && |
||||
git remote add pushee-namespaced "ext::git --namespace=namespace %s ../pushee" && |
||||
git remote add pushee-unnamespaced ../pushee |
||||
) && |
||||
commit0=$(cd original && git rev-parse HEAD^) && |
||||
commit1=$(cd original && git rev-parse HEAD) && |
||||
git init pushee && |
||||
git init puller |
||||
' |
||||
|
||||
test_expect_success 'pushing into a repository using a ref namespace' ' |
||||
( |
||||
cd original && |
||||
git push pushee-namespaced master && |
||||
git ls-remote pushee-namespaced >actual && |
||||
printf "$commit1\trefs/heads/master\n" >expected && |
||||
test_cmp expected actual && |
||||
git push pushee-namespaced --tags && |
||||
git ls-remote pushee-namespaced >actual && |
||||
printf "$commit0\trefs/tags/0\n" >>expected && |
||||
printf "$commit1\trefs/tags/1\n" >>expected && |
||||
test_cmp expected actual && |
||||
# Verify that the GIT_NAMESPACE environment variable works as well |
||||
GIT_NAMESPACE=namespace git ls-remote "ext::git %s ../pushee" >actual && |
||||
test_cmp expected actual && |
||||
# Verify that --namespace overrides GIT_NAMESPACE |
||||
GIT_NAMESPACE=garbage git ls-remote pushee-namespaced >actual && |
||||
test_cmp expected actual && |
||||
# Try a namespace with no content |
||||
git ls-remote "ext::git --namespace=garbage %s ../pushee" >actual && |
||||
test_cmp /dev/null actual && |
||||
git ls-remote pushee-unnamespaced >actual && |
||||
sed -e "s|refs/|refs/namespaces/namespace/refs/|" expected >expected.unnamespaced && |
||||
test_cmp expected.unnamespaced actual |
||||
) |
||||
' |
||||
|
||||
test_expect_success 'pulling from a repository using a ref namespace' ' |
||||
( |
||||
cd puller && |
||||
git remote add -f pushee-namespaced "ext::git --namespace=namespace %s ../pushee" && |
||||
git for-each-ref refs/ >actual && |
||||
printf "$commit1 commit\trefs/remotes/pushee-namespaced/master\n" >expected && |
||||
printf "$commit0 commit\trefs/tags/0\n" >>expected && |
||||
printf "$commit1 commit\trefs/tags/1\n" >>expected && |
||||
test_cmp expected actual |
||||
) |
||||
' |
||||
|
||||
# This test with clone --mirror checks for possible regressions in clone |
||||
# or the machinery underneath it. It ensures that no future change |
||||
# causes clone to ignore refs in refs/namespaces/*. In particular, it |
||||
# protects against a regression caused by any future change to the refs |
||||
# machinery that might cause it to ignore refs outside of refs/heads/* |
||||
# or refs/tags/*. More generally, this test also checks the high-level |
||||
# functionality of using clone --mirror to back up a set of repos hosted |
||||
# in the namespaces of a single repo. |
||||
test_expect_success 'mirroring a repository using a ref namespace' ' |
||||
git clone --mirror pushee mirror && |
||||
( |
||||
cd mirror && |
||||
git for-each-ref refs/ >actual && |
||||
printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/master\n" >expected && |
||||
printf "$commit0 commit\trefs/namespaces/namespace/refs/tags/0\n" >>expected && |
||||
printf "$commit1 commit\trefs/namespaces/namespace/refs/tags/1\n" >>expected && |
||||
test_cmp expected actual |
||||
) |
||||
' |
||||
|
||||
test_done |
Loading…
Reference in new issue