receive-pack: add another option for receive.denyCurrentBranch
When synchronizing between working directories, it can be handy to update the current branch via 'push' rather than 'pull', e.g. when pushing a fix from inside a VM, or when pushing a fix made on a user's machine (where the developer is not at liberty to install an ssh daemon let alone know the user's password). The common workaround – pushing into a temporary branch and then merging on the other machine – is no longer necessary with this patch. The new option is: 'updateInstead': Update the working tree accordingly, but refuse to do so if there are any uncommitted changes. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
66edfe9ddc
commit
1404bcbb6b
@ -2129,6 +2129,13 @@ 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".
|
||||
+
|
||||
Another option is "updateInstead" which will update the working
|
||||
directory (must be clean) if pushing into the current branch. This option is
|
||||
intended for synchronizing working directories when one side is not easily
|
||||
accessible via interactive ssh (e.g. a live web site, hence the requirement
|
||||
that the working directory be clean). This mode also comes in handy when
|
||||
developing inside a VM to test and fix code on different Operating Systems.
|
||||
|
||||
receive.denyNonFastForwards::
|
||||
If set to true, git-receive-pack will deny a ref update which is
|
||||
|
@ -26,7 +26,8 @@ enum deny_action {
|
||||
DENY_UNCONFIGURED,
|
||||
DENY_IGNORE,
|
||||
DENY_WARN,
|
||||
DENY_REFUSE
|
||||
DENY_REFUSE,
|
||||
DENY_UPDATE_INSTEAD
|
||||
};
|
||||
|
||||
static int deny_deletes;
|
||||
@ -76,6 +77,8 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
|
||||
return DENY_WARN;
|
||||
if (!strcasecmp(value, "refuse"))
|
||||
return DENY_REFUSE;
|
||||
if (!strcasecmp(value, "updateinstead"))
|
||||
return DENY_UPDATE_INSTEAD;
|
||||
}
|
||||
if (git_config_bool(var, value))
|
||||
return DENY_REFUSE;
|
||||
@ -730,11 +733,89 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *update_worktree(unsigned char *sha1)
|
||||
{
|
||||
const char *update_refresh[] = {
|
||||
"update-index", "-q", "--ignore-submodules", "--refresh", NULL
|
||||
};
|
||||
const char *diff_files[] = {
|
||||
"diff-files", "--quiet", "--ignore-submodules", "--", NULL
|
||||
};
|
||||
const char *diff_index[] = {
|
||||
"diff-index", "--quiet", "--cached", "--ignore-submodules",
|
||||
"HEAD", "--", NULL
|
||||
};
|
||||
const char *read_tree[] = {
|
||||
"read-tree", "-u", "-m", NULL, NULL
|
||||
};
|
||||
const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
|
||||
struct argv_array env = ARGV_ARRAY_INIT;
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
|
||||
if (is_bare_repository())
|
||||
return "denyCurrentBranch = updateInstead needs a worktree";
|
||||
|
||||
argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
|
||||
|
||||
child.argv = update_refresh;
|
||||
child.env = env.argv;
|
||||
child.dir = work_tree;
|
||||
child.no_stdin = 1;
|
||||
child.stdout_to_stderr = 1;
|
||||
child.git_cmd = 1;
|
||||
if (run_command(&child)) {
|
||||
argv_array_clear(&env);
|
||||
return "Up-to-date check failed";
|
||||
}
|
||||
|
||||
/* run_command() does not clean up completely; reinitialize */
|
||||
child_process_init(&child);
|
||||
child.argv = diff_files;
|
||||
child.env = env.argv;
|
||||
child.dir = work_tree;
|
||||
child.no_stdin = 1;
|
||||
child.stdout_to_stderr = 1;
|
||||
child.git_cmd = 1;
|
||||
if (run_command(&child)) {
|
||||
argv_array_clear(&env);
|
||||
return "Working directory has unstaged changes";
|
||||
}
|
||||
|
||||
child_process_init(&child);
|
||||
child.argv = diff_index;
|
||||
child.env = env.argv;
|
||||
child.no_stdin = 1;
|
||||
child.no_stdout = 1;
|
||||
child.stdout_to_stderr = 0;
|
||||
child.git_cmd = 1;
|
||||
if (run_command(&child)) {
|
||||
argv_array_clear(&env);
|
||||
return "Working directory has staged changes";
|
||||
}
|
||||
|
||||
read_tree[3] = sha1_to_hex(sha1);
|
||||
child_process_init(&child);
|
||||
child.argv = read_tree;
|
||||
child.env = env.argv;
|
||||
child.dir = work_tree;
|
||||
child.no_stdin = 1;
|
||||
child.no_stdout = 1;
|
||||
child.stdout_to_stderr = 0;
|
||||
child.git_cmd = 1;
|
||||
if (run_command(&child)) {
|
||||
argv_array_clear(&env);
|
||||
return "Could not update working tree to new HEAD";
|
||||
}
|
||||
|
||||
argv_array_clear(&env);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *update(struct command *cmd, struct shallow_info *si)
|
||||
{
|
||||
const char *name = cmd->ref_name;
|
||||
struct strbuf namespaced_name_buf = STRBUF_INIT;
|
||||
const char *namespaced_name;
|
||||
const char *namespaced_name, *ret;
|
||||
unsigned char *old_sha1 = cmd->old_sha1;
|
||||
unsigned char *new_sha1 = cmd->new_sha1;
|
||||
|
||||
@ -760,6 +841,11 @@ 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:
|
||||
ret = update_worktree(new_sha1);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -784,10 +870,13 @@ static const char *update(struct command *cmd, struct shallow_info *si)
|
||||
break;
|
||||
case DENY_REFUSE:
|
||||
case DENY_UNCONFIGURED:
|
||||
case DENY_UPDATE_INSTEAD:
|
||||
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";
|
||||
default:
|
||||
return "Invalid denyDeleteCurrent setting";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1330,4 +1330,30 @@ test_expect_success 'fetch into bare respects core.logallrefupdates' '
|
||||
)
|
||||
'
|
||||
|
||||
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 -q --refresh &&
|
||||
git diff-files --quiet -- &&
|
||||
git diff-index --quiet --cached HEAD -- &&
|
||||
echo changed >path2 &&
|
||||
git add path2
|
||||
) &&
|
||||
test_commit fourth path2 &&
|
||||
test_must_fail git push testrepo master &&
|
||||
test $(git rev-parse HEAD^) = $(git -C testrepo rev-parse HEAD) &&
|
||||
(cd testrepo &&
|
||||
git diff --quiet &&
|
||||
test changed = "$(cat path2)"
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user