Merge 'deny-current-branch' into HEAD

This commit is contained in:
Stepan Kasal
2014-05-15 09:43:59 +02:00
3 changed files with 97 additions and 2 deletions

View File

@@ -2016,6 +2016,11 @@ receive.denyCurrentBranch::
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 "refuse".
+
There are two more options that are meant for Git experts: "updateInstead"
which will run `read-tree -u -m HEAD` and "detachInstead" which will detach
the HEAD so it does not need to change. Both options come with their own
set of possible *complications*, but can be appropriate in rare workflows.
receive.denyNonFastForwards::
If set to true, git-receive-pack will deny a ref update which is

View File

@@ -22,7 +22,9 @@ enum deny_action {
DENY_UNCONFIGURED,
DENY_IGNORE,
DENY_WARN,
DENY_REFUSE
DENY_REFUSE,
DENY_UPDATE_INSTEAD,
DENY_DETACH_INSTEAD,
};
static int deny_deletes;
@@ -100,7 +102,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "receive.denycurrentbranch")) {
deny_current_branch = parse_deny_action(var, value);
if (value && !strcasecmp(value, "updateinstead"))
deny_current_branch = DENY_UPDATE_INSTEAD;
else if (value && !strcasecmp(value, "detachinstead"))
deny_current_branch = DENY_DETACH_INSTEAD;
else
deny_current_branch = parse_deny_action(var, value);
return 0;
}
@@ -468,6 +475,44 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
return 0;
}
static void merge_worktree(unsigned char *sha1)
{
const char *update_refresh[] = {
"update-index", "--ignore-submodules", "--refresh", NULL
};
const char *read_tree[] = {
"read-tree", "-u", "-m", sha1_to_hex(sha1), NULL
};
struct child_process child;
struct strbuf git_env = STRBUF_INIT;
const char *env[2];
if (is_bare_repository())
die ("denyCurrentBranch = updateInstead needs a worktree");
strbuf_addf(&git_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
env[0] = git_env.buf;
env[1] = NULL;
memset(&child, 0, sizeof(child));
child.argv = update_refresh;
child.env = env;
child.dir = git_work_tree_cfg ? git_work_tree_cfg : "..";
child.stdout_to_stderr = 1;
child.git_cmd = 1;
if (run_command(&child))
die ("Could not refresh the index");
child.argv = read_tree;
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
if (run_command(&child))
die ("Could not merge working tree with new HEAD. Good luck.");
strbuf_release(&git_env);
}
static const char *update(struct command *cmd, struct shallow_info *si)
{
const char *name = cmd->ref_name;
@@ -499,6 +544,13 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (deny_current_branch == DENY_UNCONFIGURED)
refuse_unconfigured_deny();
return "branch is currently checked out";
case DENY_UPDATE_INSTEAD:
merge_worktree(new_sha1);
break;
case DENY_DETACH_INSTEAD:
update_ref("push into current branch (detach)", "HEAD",
old_sha1, NULL, REF_NODEREF, DIE_ON_ERR);
break;
}
}
@@ -527,6 +579,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
refuse_unconfigured_deny_delete_current();
rp_error("refusing to delete the current branch: %s", name);
return "deletion of the current branch prohibited";
default:
die ("Invalid denyDeleteCurrent setting");
}
}
}

View File

@@ -1277,4 +1277,40 @@ EOF
git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
'
test_expect_success 'receive.denyCurrentBranch = updateInstead' '
git push testrepo master &&
(cd testrepo &&
git reset --hard &&
git config receive.denyCurrentBranch updateInstead
) &&
test_commit third path2 &&
git push testrepo master &&
test $(git rev-parse HEAD) = $(cd testrepo && git rev-parse HEAD) &&
test third = "$(cat testrepo/path2)" &&
(cd testrepo &&
git update-index --refresh &&
git diff-files --quiet &&
git diff-index --cached HEAD --
)
'
test_expect_success 'receive.denyCurrentBranch = detachInstead' '
(cd testrepo &&
git reset --hard &&
git config receive.denyCurrentBranch detachInstead
) &&
OLDHEAD=$(cd testrepo && git rev-parse HEAD) &&
test_commit fourth path2 &&
test fourth = "$(cat path2)" &&
git push testrepo master &&
test $OLDHEAD = $(cd testrepo && git rev-parse HEAD) &&
test fourth != "$(cat testrepo/path2)" &&
(cd testrepo &&
test_must_fail git symbolic-ref HEAD &&
git update-index --refresh &&
git diff-files --quiet &&
git diff-index --cached HEAD --
)
'
test_done