Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 32143d9

Browse files
committed
Merge 'deny-current-branch' into HEAD
2 parents fb0f5d1 + 620d6e9 commit 32143d9

File tree

3 files changed

+97
-2
lines changed

3 files changed

+97
-2
lines changed

Documentation/config.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2075,6 +2075,11 @@ receive.denyCurrentBranch::
20752075
print a warning of such a push to stderr, but allow the push to
20762076
proceed. If set to false or "ignore", allow such pushes with no
20772077
message. Defaults to "refuse".
2078+
+
2079+
There are two more options that are meant for Git experts: "updateInstead"
2080+
which will run `read-tree -u -m HEAD` and "detachInstead" which will detach
2081+
the HEAD so it does not need to change. Both options come with their own
2082+
set of possible *complications*, but can be appropriate in rare workflows.
20782083

20792084
receive.denyNonFastForwards::
20802085
If set to true, git-receive-pack will deny a ref update which is

builtin/receive-pack.c

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ enum deny_action {
2222
DENY_UNCONFIGURED,
2323
DENY_IGNORE,
2424
DENY_WARN,
25-
DENY_REFUSE
25+
DENY_REFUSE,
26+
DENY_UPDATE_INSTEAD,
27+
DENY_DETACH_INSTEAD,
2628
};
2729

2830
static int deny_deletes;
@@ -100,7 +102,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
100102
}
101103

102104
if (!strcmp(var, "receive.denycurrentbranch")) {
103-
deny_current_branch = parse_deny_action(var, value);
105+
if (value && !strcasecmp(value, "updateinstead"))
106+
deny_current_branch = DENY_UPDATE_INSTEAD;
107+
else if (value && !strcasecmp(value, "detachinstead"))
108+
deny_current_branch = DENY_DETACH_INSTEAD;
109+
else
110+
deny_current_branch = parse_deny_action(var, value);
104111
return 0;
105112
}
106113

@@ -468,6 +475,44 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
468475
return 0;
469476
}
470477

478+
static void merge_worktree(unsigned char *sha1)
479+
{
480+
const char *update_refresh[] = {
481+
"update-index", "--ignore-submodules", "--refresh", NULL
482+
};
483+
const char *read_tree[] = {
484+
"read-tree", "-u", "-m", sha1_to_hex(sha1), NULL
485+
};
486+
struct child_process child;
487+
struct strbuf git_env = STRBUF_INIT;
488+
const char *env[2];
489+
490+
if (is_bare_repository())
491+
die ("denyCurrentBranch = updateInstead needs a worktree");
492+
493+
strbuf_addf(&git_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
494+
env[0] = git_env.buf;
495+
env[1] = NULL;
496+
497+
memset(&child, 0, sizeof(child));
498+
child.argv = update_refresh;
499+
child.env = env;
500+
child.dir = git_work_tree_cfg ? git_work_tree_cfg : "..";
501+
child.stdout_to_stderr = 1;
502+
child.git_cmd = 1;
503+
if (run_command(&child))
504+
die ("Could not refresh the index");
505+
506+
child.argv = read_tree;
507+
child.no_stdin = 1;
508+
child.no_stdout = 1;
509+
child.stdout_to_stderr = 0;
510+
if (run_command(&child))
511+
die ("Could not merge working tree with new HEAD. Good luck.");
512+
513+
strbuf_release(&git_env);
514+
}
515+
471516
static const char *update(struct command *cmd, struct shallow_info *si)
472517
{
473518
const char *name = cmd->ref_name;
@@ -499,6 +544,13 @@ static const char *update(struct command *cmd, struct shallow_info *si)
499544
if (deny_current_branch == DENY_UNCONFIGURED)
500545
refuse_unconfigured_deny();
501546
return "branch is currently checked out";
547+
case DENY_UPDATE_INSTEAD:
548+
merge_worktree(new_sha1);
549+
break;
550+
case DENY_DETACH_INSTEAD:
551+
update_ref("push into current branch (detach)", "HEAD",
552+
old_sha1, NULL, REF_NODEREF, DIE_ON_ERR);
553+
break;
502554
}
503555
}
504556

@@ -527,6 +579,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
527579
refuse_unconfigured_deny_delete_current();
528580
rp_error("refusing to delete the current branch: %s", name);
529581
return "deletion of the current branch prohibited";
582+
default:
583+
die ("Invalid denyDeleteCurrent setting");
530584
}
531585
}
532586
}

t/t5516-fetch-push.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,4 +1277,40 @@ EOF
12771277
git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
12781278
'
12791279

1280+
test_expect_success 'receive.denyCurrentBranch = updateInstead' '
1281+
git push testrepo master &&
1282+
(cd testrepo &&
1283+
git reset --hard &&
1284+
git config receive.denyCurrentBranch updateInstead
1285+
) &&
1286+
test_commit third path2 &&
1287+
git push testrepo master &&
1288+
test $(git rev-parse HEAD) = $(cd testrepo && git rev-parse HEAD) &&
1289+
test third = "$(cat testrepo/path2)" &&
1290+
(cd testrepo &&
1291+
git update-index --refresh &&
1292+
git diff-files --quiet &&
1293+
git diff-index --cached HEAD --
1294+
)
1295+
'
1296+
1297+
test_expect_success 'receive.denyCurrentBranch = detachInstead' '
1298+
(cd testrepo &&
1299+
git reset --hard &&
1300+
git config receive.denyCurrentBranch detachInstead
1301+
) &&
1302+
OLDHEAD=$(cd testrepo && git rev-parse HEAD) &&
1303+
test_commit fourth path2 &&
1304+
test fourth = "$(cat path2)" &&
1305+
git push testrepo master &&
1306+
test $OLDHEAD = $(cd testrepo && git rev-parse HEAD) &&
1307+
test fourth != "$(cat testrepo/path2)" &&
1308+
(cd testrepo &&
1309+
test_must_fail git symbolic-ref HEAD &&
1310+
git update-index --refresh &&
1311+
git diff-files --quiet &&
1312+
git diff-index --cached HEAD --
1313+
)
1314+
'
1315+
12801316
test_done

0 commit comments

Comments
 (0)