Merge branch 'ak/protect-any-current-branch'

"git fetch" without the "--update-head-ok" option ought to protect
a checked out branch from getting updated, to prevent the working
tree that checks it out to go out of sync.  The code was written
before the use of "git worktree" got widespread, and only checked
the branch that was checked out in the current worktree, which has
been updated.
(originally called ak/fetch-not-overwrite-any-current-branch)

* ak/protect-any-current-branch:
  branch: protect branches checked out in all worktrees
  receive-pack: protect current branch for bare repository worktree
  receive-pack: clean dead code from update_worktree()
  fetch: protect branches checked out in all worktrees
  worktree: simplify find_shared_symref() memory ownership model
  branch: lowercase error messages
  receive-pack: lowercase error messages
  fetch: lowercase error messages
This commit is contained in:
Junio C Hamano
2021-12-21 15:03:16 -08:00
11 changed files with 198 additions and 131 deletions

View File

@@ -175,7 +175,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
strbuf_addf(&fsck_msg_types, "%c%s=%s",
fsck_msg_types.len ? ',' : '=', var, value);
else
warning("Skipping unknown msg id '%s'", var);
warning("skipping unknown msg id '%s'", var);
return 0;
}
@@ -1434,29 +1434,22 @@ static const char *push_to_checkout(unsigned char *hash,
static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
{
const char *retval, *work_tree, *git_dir = NULL;
const char *retval, *git_dir;
struct strvec env = STRVEC_INIT;
if (worktree && worktree->path)
work_tree = worktree->path;
else if (git_work_tree_cfg)
work_tree = git_work_tree_cfg;
else
work_tree = "..";
if (!worktree || !worktree->path)
BUG("worktree->path must be non-NULL");
if (is_bare_repository())
if (worktree->is_bare)
return "denyCurrentBranch = updateInstead needs a worktree";
if (worktree)
git_dir = get_worktree_git_dir(worktree);
if (!git_dir)
git_dir = get_git_dir();
git_dir = get_worktree_git_dir(worktree);
strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
if (!hook_exists(push_to_checkout_hook))
retval = push_to_deploy(sha1, &env, work_tree);
retval = push_to_deploy(sha1, &env, worktree->path);
else
retval = push_to_checkout(sha1, &env, work_tree);
retval = push_to_checkout(sha1, &env, worktree->path);
strvec_clear(&env);
return retval;
@@ -1471,19 +1464,22 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct object_id *old_oid = &cmd->old_oid;
struct object_id *new_oid = &cmd->new_oid;
int do_update_worktree = 0;
const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
struct worktree **worktrees = get_worktrees();
const struct worktree *worktree =
find_shared_symref(worktrees, "HEAD", name);
/* only refs/... are allowed */
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name);
return "funny refname";
ret = "funny refname";
goto out;
}
strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
free(namespaced_name);
namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
if (worktree) {
if (worktree && !worktree->is_bare) {
switch (deny_current_branch) {
case DENY_IGNORE:
break;
@@ -1495,7 +1491,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
rp_error("refusing to update checked out branch: %s", name);
if (deny_current_branch == DENY_UNCONFIGURED)
refuse_unconfigured_deny();
return "branch is currently checked out";
ret = "branch is currently checked out";
goto out;
case DENY_UPDATE_INSTEAD:
/* pass -- let other checks intervene first */
do_update_worktree = 1;
@@ -1506,13 +1503,15 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
error("unpack should have generated %s, "
"but I can't find it!", oid_to_hex(new_oid));
return "bad pack";
ret = "bad pack";
goto out;
}
if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
if (deny_deletes && starts_with(name, "refs/heads/")) {
rp_error("denying ref deletion for %s", name);
return "deletion prohibited";
ret = "deletion prohibited";
goto out;
}
if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
@@ -1528,9 +1527,11 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (deny_delete_current == DENY_UNCONFIGURED)
refuse_unconfigured_deny_delete_current();
rp_error("refusing to delete the current branch: %s", name);
return "deletion of the current branch prohibited";
ret = "deletion of the current branch prohibited";
goto out;
default:
return "Invalid denyDeleteCurrent setting";
ret = "Invalid denyDeleteCurrent setting";
goto out;
}
}
}
@@ -1548,25 +1549,28 @@ static const char *update(struct command *cmd, struct shallow_info *si)
old_object->type != OBJ_COMMIT ||
new_object->type != OBJ_COMMIT) {
error("bad sha1 objects for %s", name);
return "bad ref";
ret = "bad ref";
goto out;
}
old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_object;
if (!in_merge_bases(old_commit, new_commit)) {
rp_error("denying non-fast-forward %s"
" (you should pull first)", name);
return "non-fast-forward";
ret = "non-fast-forward";
goto out;
}
}
if (run_update_hook(cmd)) {
rp_error("hook declined to update %s", name);
return "hook declined";
ret = "hook declined";
goto out;
}
if (do_update_worktree) {
ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
ret = update_worktree(new_oid->hash, worktree);
if (ret)
return ret;
goto out;
}
if (is_null_oid(new_oid)) {
@@ -1574,9 +1578,9 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (!parse_object(the_repository, old_oid)) {
old_oid = NULL;
if (ref_exists(name)) {
rp_warning("Allowing deletion of corrupt ref.");
rp_warning("allowing deletion of corrupt ref");
} else {
rp_warning("Deleting a non-existent ref.");
rp_warning("deleting a non-existent ref");
cmd->did_not_exist = 1;
}
}
@@ -1585,17 +1589,19 @@ static const char *update(struct command *cmd, struct shallow_info *si)
old_oid,
0, "push", &err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
return "failed to delete";
ret = "failed to delete";
} else {
ret = NULL; /* good */
}
strbuf_release(&err);
return NULL; /* good */
}
else {
struct strbuf err = STRBUF_INIT;
if (shallow_update && si->shallow_ref[cmd->index] &&
update_shallow_ref(cmd, si))
return "shallow error";
update_shallow_ref(cmd, si)) {
ret = "shallow error";
goto out;
}
if (ref_transaction_update(transaction,
namespaced_name,
@@ -1603,14 +1609,16 @@ static const char *update(struct command *cmd, struct shallow_info *si)
0, "push",
&err)) {
rp_error("%s", err.buf);
strbuf_release(&err);
return "failed to update ref";
ret = "failed to update ref";
} else {
ret = NULL; /* good */
}
strbuf_release(&err);
return NULL; /* good */
}
out:
free_worktrees(worktrees);
return ret;
}
static void run_update_post_hook(struct command *commands)
@@ -2476,9 +2484,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
if (argc > 1)
usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
usage_msg_opt(_("too many arguments"), receive_pack_usage, options);
if (argc == 0)
usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
usage_msg_opt(_("you must specify a directory"), receive_pack_usage, options);
service_dir = argv[0];