@matheusbsbs / matheus-bagliano / commits / 747ca2455a
Fork 0

receive-pack: receive.denyDeleteCurrent

This is a companion patch to the recent 3d95d92 (receive-pack: explain what to do when push updates the current branch, 2009-01-31). Deleting the current branch from a remote will result in the next clone from it not check out anything, among other things. It also is one of the cause that makes remotes/origin/HEAD a dangling symbolic ref. This patch still allows the traditional behaviour but with a big warning, and promises that the default will change to 'refuse' in a future release. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Feb 8, 2009 at 22:31 UTC 747ca2455a8dd4e255358f5f96773f84259408c3
2 files changed +74 -11
builtin-receive-pack.c
+61 -11
@@ -21,11 +21,13 @@ enum deny_action {
21 static int deny_deletes = 0;
22 static int deny_non_fast_forwards = 0;
23 static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
24 +static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
25 static int receive_fsck_objects;
26 static int receive_unpack_limit = -1;
27 static int transfer_unpack_limit = -1;
28 static int unpack_limit = 100;
29 static int report_status;
30 +static const char *head_name;
31
32 static char capabilities[] = " report-status delete-refs ";
33 static int capabilities_sent;
@@ -77,6 +79,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
79 return 0;
80 }
81
82 + if (strcmp(var, "receive.denydeletecurrent") == 0) {
83 + deny_delete_current = parse_deny_action(var, value);
84 + return 0;
85 + }
86 +
87 return git_default_config(var, value, cb);
88 }
89
@@ -203,16 +210,12 @@ static int run_update_hook(struct command *cmd)
210
211 static int is_ref_checked_out(const char *ref)
212 {
206 - unsigned char sha1[20];
207 - const char *head;
208 -
213 if (is_bare_repository())
214 return 0;
215
212 - head = resolve_ref("HEAD", sha1, 0, NULL);
213 - if (!head)
216 + if (!head_name)
217 return 0;
215 - return !strcmp(head, ref);
218 + return !strcmp(head_name, ref);
219 }
220
221 static char *warn_unconfigured_deny_msg[] = {
@@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void)
247 warning(warn_unconfigured_deny_msg[i]);
248 }
249
250 +static char *warn_unconfigured_deny_delete_current_msg[] = {
251 + "Deleting the current branch can cause confusion by making the next",
252 + "'git clone' not check out any file.",
253 + "",
254 + "You can set 'receive.denyDeleteCurrent' configuration variable to",
255 + "'refuse' in the remote repository to disallow deleting the current",
256 + "branch.",
257 + "",
258 + "You can set it to 'ignore' to allow such a delete without a warning.",
259 + "",
260 + "To make this warning message less loud, you can set it to 'warn'.",
261 + "",
262 + "Note that the default will change in a future version of git",
263 + "to refuse deleting the current branch unless you have the",
264 + "configuration variable set to either 'ignore' or 'warn'."
265 +};
266 +
267 +static void warn_unconfigured_deny_delete_current(void)
268 +{
269 + int i;
270 + for (i = 0;
271 + i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
272 + i++)
273 + warning(warn_unconfigured_deny_delete_current_msg[i]);
274 +}
275 +
276 static const char *update(struct command *cmd)
277 {
278 const char *name = cmd->ref_name;
@@ -278,12 +307,30 @@ static const char *update(struct command *cmd)
307 "but I can't find it!", sha1_to_hex(new_sha1));
308 return "bad pack";
309 }
281 - if (deny_deletes && is_null_sha1(new_sha1) &&
282 - !is_null_sha1(old_sha1) &&
283 - !prefixcmp(name, "refs/heads/")) {
284 - error("denying ref deletion for %s", name);
285 - return "deletion prohibited";
310 +
311 + if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
312 + if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
313 + error("denying ref deletion for %s", name);
314 + return "deletion prohibited";
315 + }
316 +
317 + if (!strcmp(name, head_name)) {
318 + switch (deny_delete_current) {
319 + case DENY_IGNORE:
320 + break;
321 + case DENY_WARN:
322 + case DENY_UNCONFIGURED:
323 + if (deny_delete_current == DENY_UNCONFIGURED)
324 + warn_unconfigured_deny_delete_current();
325 + warning("deleting the current branch");
326 + break;
327 + case DENY_REFUSE:
328 + error("refusing to delete the current branch: %s", name);
329 + return "deletion of the current branch prohibited";
330 + }
331 + }
332 }
333 +
334 if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
335 !is_null_sha1(old_sha1) &&
336 !prefixcmp(name, "refs/heads/")) {
@@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd)
424 static void execute_commands(const char *unpacker_error)
425 {
426 struct command *cmd = commands;
427 + unsigned char sha1[20];
428
429 if (unpacker_error) {
430 while (cmd) {
@@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error)
442 return;
443 }
444
445 + head_name = resolve_ref("HEAD", sha1, 0, NULL);
446 +
447 while (cmd) {
448 cmd->error_string = update(cmd);
449 cmd = cmd->next;
t/t5400-send-pack.sh
+13
@@ -190,4 +190,17 @@ test_expect_success 'pushing wildcard refspecs respects forcing' '
190 test "$parent_head" = "$child_head"
191 '
192
193 +test_expect_success 'warn pushing to delete current branch' '
194 + rewound_push_setup &&
195 + (
196 + cd child &&
197 + git send-pack ../parent :refs/heads/master 2>errs
198 + ) &&
199 + grep "warning: to refuse deleting" child/errs &&
200 + (
201 + cd parent &&
202 + test_must_fail git rev-parse --verify master
203 + )
204 +'
205 +
206 test_done