Merge branch 'jk/deny-push-to-current'
* jk/deny-push-to-current: receive-pack: detect push to current branch of non-bare repo t5516: refactor oddball tests
This commit is contained in:
commit
902a4a252a
@ -1198,6 +1198,15 @@ receive.denyNonFastForwards::
|
|||||||
even if that push is forced. This configuration variable is
|
even if that push is forced. This configuration variable is
|
||||||
set when initializing a shared repository.
|
set when initializing a shared repository.
|
||||||
|
|
||||||
|
receive.denyCurrentBranch::
|
||||||
|
If set to true or "refuse", receive-pack will deny a ref update
|
||||||
|
to the currently checked out branch of a non-bare repository.
|
||||||
|
Such a push is potentially dangerous because it brings the HEAD
|
||||||
|
out of sync with the index and working tree. If set to "warn",
|
||||||
|
print a warning of such a push to stderr, but allow the push to
|
||||||
|
proceed. If set to false or "ignore", allow such pushes with no
|
||||||
|
message. Defaults to "warn".
|
||||||
|
|
||||||
transfer.unpackLimit::
|
transfer.unpackLimit::
|
||||||
When `fetch.unpackLimit` or `receive.unpackLimit` are
|
When `fetch.unpackLimit` or `receive.unpackLimit` are
|
||||||
not set, the value of this variable is used instead.
|
not set, the value of this variable is used instead.
|
||||||
|
@ -11,8 +11,15 @@
|
|||||||
|
|
||||||
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
|
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
|
||||||
|
|
||||||
|
enum deny_action {
|
||||||
|
DENY_IGNORE,
|
||||||
|
DENY_WARN,
|
||||||
|
DENY_REFUSE,
|
||||||
|
};
|
||||||
|
|
||||||
static int deny_deletes = 0;
|
static int deny_deletes = 0;
|
||||||
static int deny_non_fast_forwards = 0;
|
static int deny_non_fast_forwards = 0;
|
||||||
|
static enum deny_action deny_current_branch = DENY_WARN;
|
||||||
static int receive_fsck_objects;
|
static int receive_fsck_objects;
|
||||||
static int receive_unpack_limit = -1;
|
static int receive_unpack_limit = -1;
|
||||||
static int transfer_unpack_limit = -1;
|
static int transfer_unpack_limit = -1;
|
||||||
@ -22,6 +29,21 @@ static int report_status;
|
|||||||
static char capabilities[] = " report-status delete-refs ";
|
static char capabilities[] = " report-status delete-refs ";
|
||||||
static int capabilities_sent;
|
static int capabilities_sent;
|
||||||
|
|
||||||
|
static enum deny_action parse_deny_action(const char *var, const char *value)
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
if (!strcasecmp(value, "ignore"))
|
||||||
|
return DENY_IGNORE;
|
||||||
|
if (!strcasecmp(value, "warn"))
|
||||||
|
return DENY_WARN;
|
||||||
|
if (!strcasecmp(value, "refuse"))
|
||||||
|
return DENY_REFUSE;
|
||||||
|
}
|
||||||
|
if (git_config_bool(var, value))
|
||||||
|
return DENY_REFUSE;
|
||||||
|
return DENY_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
static int receive_pack_config(const char *var, const char *value, void *cb)
|
static int receive_pack_config(const char *var, const char *value, void *cb)
|
||||||
{
|
{
|
||||||
if (strcmp(var, "receive.denydeletes") == 0) {
|
if (strcmp(var, "receive.denydeletes") == 0) {
|
||||||
@ -49,6 +71,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(var, "receive.denycurrentbranch")) {
|
||||||
|
deny_current_branch = parse_deny_action(var, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return git_default_config(var, value, cb);
|
return git_default_config(var, value, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +200,20 @@ static int run_update_hook(struct command *cmd)
|
|||||||
return hook_status(run_command(&proc), update_hook);
|
return hook_status(run_command(&proc), update_hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_ref_checked_out(const char *ref)
|
||||||
|
{
|
||||||
|
unsigned char sha1[20];
|
||||||
|
const char *head;
|
||||||
|
|
||||||
|
if (is_bare_repository())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
head = resolve_ref("HEAD", sha1, 0, NULL);
|
||||||
|
if (!head)
|
||||||
|
return 0;
|
||||||
|
return !strcmp(head, ref);
|
||||||
|
}
|
||||||
|
|
||||||
static const char *update(struct command *cmd)
|
static const char *update(struct command *cmd)
|
||||||
{
|
{
|
||||||
const char *name = cmd->ref_name;
|
const char *name = cmd->ref_name;
|
||||||
@ -186,6 +227,24 @@ static const char *update(struct command *cmd)
|
|||||||
return "funny refname";
|
return "funny refname";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (deny_current_branch) {
|
||||||
|
case DENY_IGNORE:
|
||||||
|
break;
|
||||||
|
case DENY_WARN:
|
||||||
|
if (!is_ref_checked_out(name))
|
||||||
|
break;
|
||||||
|
warning("updating the currently checked out branch; this may"
|
||||||
|
" cause confusion,\n"
|
||||||
|
"as the index and working tree do not reflect changes"
|
||||||
|
" that are now in HEAD.");
|
||||||
|
break;
|
||||||
|
case DENY_REFUSE:
|
||||||
|
if (!is_ref_checked_out(name))
|
||||||
|
break;
|
||||||
|
error("refusing to update checked out branch: %s", name);
|
||||||
|
return "branch is currently checked out";
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
|
if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
|
||||||
error("unpack should have generated %s, "
|
error("unpack should have generated %s, "
|
||||||
"but I can't find it!", sha1_to_hex(new_sha1));
|
"but I can't find it!", sha1_to_hex(new_sha1));
|
||||||
|
@ -39,6 +39,11 @@ mk_test () {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mk_child() {
|
||||||
|
rm -rf "$1" &&
|
||||||
|
git clone testrepo "$1"
|
||||||
|
}
|
||||||
|
|
||||||
check_push_result () {
|
check_push_result () {
|
||||||
(
|
(
|
||||||
cd testrepo &&
|
cd testrepo &&
|
||||||
@ -425,13 +430,10 @@ test_expect_success 'push with dry-run' '
|
|||||||
|
|
||||||
test_expect_success 'push updates local refs' '
|
test_expect_success 'push updates local refs' '
|
||||||
|
|
||||||
rm -rf parent child &&
|
mk_test heads/master &&
|
||||||
mkdir parent &&
|
mk_child child &&
|
||||||
(cd parent && git init &&
|
|
||||||
echo one >foo && git add foo && git commit -m one) &&
|
|
||||||
git clone parent child &&
|
|
||||||
(cd child &&
|
(cd child &&
|
||||||
echo two >foo && git commit -a -m two &&
|
git pull .. master &&
|
||||||
git push &&
|
git push &&
|
||||||
test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
|
test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
|
||||||
|
|
||||||
@ -439,15 +441,10 @@ test_expect_success 'push updates local refs' '
|
|||||||
|
|
||||||
test_expect_success 'push updates up-to-date local refs' '
|
test_expect_success 'push updates up-to-date local refs' '
|
||||||
|
|
||||||
rm -rf parent child &&
|
mk_test heads/master &&
|
||||||
mkdir parent &&
|
mk_child child1 &&
|
||||||
(cd parent && git init &&
|
mk_child child2 &&
|
||||||
echo one >foo && git add foo && git commit -m one) &&
|
(cd child1 && git pull .. master && git push) &&
|
||||||
git clone parent child1 &&
|
|
||||||
git clone parent child2 &&
|
|
||||||
(cd child1 &&
|
|
||||||
echo two >foo && git commit -a -m two &&
|
|
||||||
git push) &&
|
|
||||||
(cd child2 &&
|
(cd child2 &&
|
||||||
git pull ../child1 master &&
|
git pull ../child1 master &&
|
||||||
git push &&
|
git push &&
|
||||||
@ -457,11 +454,8 @@ test_expect_success 'push updates up-to-date local refs' '
|
|||||||
|
|
||||||
test_expect_success 'push preserves up-to-date packed refs' '
|
test_expect_success 'push preserves up-to-date packed refs' '
|
||||||
|
|
||||||
rm -rf parent child &&
|
mk_test heads/master &&
|
||||||
mkdir parent &&
|
mk_child child &&
|
||||||
(cd parent && git init &&
|
|
||||||
echo one >foo && git add foo && git commit -m one) &&
|
|
||||||
git clone parent child &&
|
|
||||||
(cd child &&
|
(cd child &&
|
||||||
git push &&
|
git push &&
|
||||||
! test -f .git/refs/remotes/origin/master)
|
! test -f .git/refs/remotes/origin/master)
|
||||||
@ -470,15 +464,13 @@ test_expect_success 'push preserves up-to-date packed refs' '
|
|||||||
|
|
||||||
test_expect_success 'push does not update local refs on failure' '
|
test_expect_success 'push does not update local refs on failure' '
|
||||||
|
|
||||||
rm -rf parent child &&
|
mk_test heads/master &&
|
||||||
mkdir parent &&
|
mk_child child &&
|
||||||
(cd parent && git init &&
|
mkdir testrepo/.git/hooks &&
|
||||||
echo one >foo && git add foo && git commit -m one &&
|
echo exit 1 >testrepo/.git/hooks/pre-receive &&
|
||||||
echo exit 1 >.git/hooks/pre-receive &&
|
chmod +x testrepo/.git/hooks/pre-receive &&
|
||||||
chmod +x .git/hooks/pre-receive) &&
|
|
||||||
git clone parent child &&
|
|
||||||
(cd child &&
|
(cd child &&
|
||||||
echo two >foo && git commit -a -m two &&
|
git pull .. master
|
||||||
test_must_fail git push &&
|
test_must_fail git push &&
|
||||||
test $(git rev-parse master) != \
|
test $(git rev-parse master) != \
|
||||||
$(git rev-parse remotes/origin/master))
|
$(git rev-parse remotes/origin/master))
|
||||||
@ -487,11 +479,48 @@ test_expect_success 'push does not update local refs on failure' '
|
|||||||
|
|
||||||
test_expect_success 'allow deleting an invalid remote ref' '
|
test_expect_success 'allow deleting an invalid remote ref' '
|
||||||
|
|
||||||
pwd &&
|
mk_test heads/master &&
|
||||||
rm -f testrepo/.git/objects/??/* &&
|
rm -f testrepo/.git/objects/??/* &&
|
||||||
git push testrepo :refs/heads/master &&
|
git push testrepo :refs/heads/master &&
|
||||||
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
|
(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'warn on push to HEAD of non-bare repository' '
|
||||||
|
mk_test heads/master
|
||||||
|
(cd testrepo &&
|
||||||
|
git checkout master &&
|
||||||
|
git config receive.denyCurrentBranch warn) &&
|
||||||
|
git push testrepo master 2>stderr &&
|
||||||
|
grep "warning.*this may cause confusion" stderr
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'deny push to HEAD of non-bare repository' '
|
||||||
|
mk_test heads/master
|
||||||
|
(cd testrepo &&
|
||||||
|
git checkout master &&
|
||||||
|
git config receive.denyCurrentBranch true) &&
|
||||||
|
test_must_fail git push testrepo master
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'allow push to HEAD of bare repository (bare)' '
|
||||||
|
mk_test heads/master
|
||||||
|
(cd testrepo &&
|
||||||
|
git checkout master &&
|
||||||
|
git config receive.denyCurrentBranch true &&
|
||||||
|
git config core.bare true) &&
|
||||||
|
git push testrepo master 2>stderr &&
|
||||||
|
! grep "warning.*this may cause confusion" stderr
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'allow push to HEAD of non-bare repository (config)' '
|
||||||
|
mk_test heads/master
|
||||||
|
(cd testrepo &&
|
||||||
|
git checkout master &&
|
||||||
|
git config receive.denyCurrentBranch false
|
||||||
|
) &&
|
||||||
|
git push testrepo master 2>stderr &&
|
||||||
|
! grep "warning.*this may cause confusion" stderr
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user