From: David Turner <dturner@twopensource.com>
To: gitster@pobox.com, git@vger.kernel.org
Cc: David Turner <dturner@twitter.com>
Subject: [PATCH] receive-pack: optionally deny case-clone refs
Date: Tue, 3 Jun 2014 15:14:56 -0400 [thread overview]
Message-ID: <1401822896-816-1-git-send-email-dturner@twitter.com> (raw)
It is possible to have two branches which are the same but for case.
This works great on the case-sensitive filesystems, but not so well on
case-insensitive filesystems. It is fairly typical to have
case-insensitive clients (Macs, say) with a case-sensitive server
(GNU/Linux).
Should a user attempt to pull on a Mac when there are case-clone
branches with differing contents, they'll get an error message
containing something like "Ref refs/remotes/origin/lower is at
[sha-of-lowercase-branch] but expected [sha-of-uppercase-branch]....
(unable to update local ref)"
With a case-insensitive git server, if a branch called capital-M
Master (that differs from lowercase-m-master) is pushed, nobody else
can push to (lowercase-m) master until the branch is removed.
Create the option receive.denycaseclonebranches, which checks pushed
branches to ensure that they are not case-clones of an existing
branch. This setting is turned on by default if core.ignorecase is
set, but not otherwise.
Signed-off-by: David Turner <dturner@twitter.com>
---
builtin/receive-pack.c | 29 ++++++++++++++++++++++++++++-
t/t5400-send-pack.sh | 20 ++++++++++++++++++++
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c323081..0894ded 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -27,6 +27,7 @@ enum deny_action {
static int deny_deletes;
static int deny_non_fast_forwards;
+static int deny_case_clone_branches = -1;
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
static int receive_fsck_objects = -1;
@@ -69,6 +70,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
if (status)
return status;
+ if (strcmp(var, "receive.denycaseclonebranches") == 0) {
+ deny_case_clone_branches = git_config_bool(var, value);
+ return 0;
+ }
+
if (strcmp(var, "receive.denydeletes") == 0) {
deny_deletes = git_config_bool(var, value);
return 0;
@@ -468,6 +474,24 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
return 0;
}
+static int is_case_clone(const char *refname, const unsigned char *sha1,
+ int flags, void *cb_data)
+{
+ const char* incoming_refname = cb_data;
+ return !strcasecmp(refname, incoming_refname) &&
+ strcmp(refname, incoming_refname);
+}
+
+static int ref_is_denied_case_clone(const char *name)
+{
+
+ if (!deny_case_clone_branches)
+ return 0;
+
+ return for_each_ref(is_case_clone, (void *) name);
+
+}
+
static const char *update(struct command *cmd, struct shallow_info *si)
{
const char *name = cmd->ref_name;
@@ -478,7 +502,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct ref_lock *lock;
/* only refs/... are allowed */
- if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
+ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0) ||
+ ref_is_denied_case_clone(name)) {
rp_error("refusing to create funny ref '%s' remotely", name);
return "funny refname";
}
@@ -1171,6 +1196,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
die("'%s' does not appear to be a git repository", dir);
git_config(receive_pack_config, NULL);
+ if (deny_case_clone_branches == -1)
+ deny_case_clone_branches = ignore_case;
if (0 <= transfer_unpack_limit)
unpack_limit = transfer_unpack_limit;
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 0736bcb..099c0e3 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -129,6 +129,26 @@ test_expect_success 'denyNonFastforwards trumps --force' '
test "$victim_orig" = "$victim_head"
'
+if ! test_have_prereq CASE_INSENSITIVE_FS
+then
+test_expect_success 'denyCaseCloneBranches works' '
+ (
+ cd victim &&
+ git config receive.denyCaseCloneBranches true
+ git config receive.denyDeletes false
+ ) &&
+ git checkout -b caseclone &&
+ git send-pack ./victim caseclone &&
+ git checkout -b CaseClone &&
+ test_must_fail git send-pack ./victim CaseClone &&
+ git checkout -b notacaseclone &&
+ git send-pack ./victim notacaseclone &&
+ test_must_fail git send-pack ./victim :CaseClone &&
+ git send-pack ./victim :caseclone &&
+ git send-pack ./victim CaseClone
+'
+fi
+
test_expect_success 'push --all excludes remote-tracking hierarchy' '
mkdir parent &&
(
--
2.0.0.rc1.18.gf763c0f
next reply other threads:[~2014-06-03 19:15 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-03 19:14 David Turner [this message]
2014-06-03 21:33 ` [PATCH] receive-pack: optionally deny case-clone refs Junio C Hamano
2014-06-03 22:02 ` David Turner
2014-06-03 22:13 ` Junio C Hamano
2014-06-04 3:05 ` David Turner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1401822896-816-1-git-send-email-dturner@twitter.com \
--to=dturner@twopensource.com \
--cc=dturner@twitter.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).