{"thread":{"id":"5626","subject":"[PATCH] add receive.denyNonFastforwards config variable","startedAt":"2006-09-20T22:28:35Z","lastAt":"2006-09-21T09:25:39Z","messageCount":18,"participants":["Johannes Schindelin","Junio C Hamano","Shawn Pearce","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"27302","messageId":"Pine.LNX.4.63.0609210027430.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":null,"subject":"[PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-20T22:28:35Z","receivedAt":"2006-09-20T22:28:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIf receive.denyNonFastforwards is set to true, git-receive-pack will deny\nnon fast-forwards, i.e. forced updates. Most notably, a push to a repository\nwhich has that flag set to true will fail.\n\nAs a first user, 'git-init-db --shared' sets this flag, since in a shared\nsetup, you are most unlikely to want forced pushes to succeed.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n builtin-init-db.c |    1 +\n cache.h           |    1 +\n environment.c     |    1 +\n receive-pack.c    |   12 ++++++++++++\n setup.c           |    2 ++\n 5 files changed, 17 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 36c3088..e6a2d7d 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -310,6 +310,7 @@ int cmd_init_db(int argc, const char **a\n \t\t */\n \t\tsprintf(buf, \"%d\", shared_repository);\n \t\tgit_config_set(\"core.sharedrepository\", buf);\n+\t\tgit_config_set(\"receive.denyNonFastforwards\", \"true\");\n \t}\n \n \treturn 0;\ndiff --git a/cache.h b/cache.h\nindex f2fdc00..2224c83 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -188,6 +188,7 @@ extern int prefer_symlink_refs;\n extern int log_all_ref_updates;\n extern int warn_ambiguous_refs;\n extern int shared_repository;\n+extern int deny_non_fast_forwards;\n extern const char *apply_default_whitespace;\n extern int zlib_compression_level;\n \ndiff --git a/environment.c b/environment.c\nindex 84d870c..63b1d15 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -20,6 +20,7 @@ int warn_ambiguous_refs = 1;\n int repository_format_version;\n char git_commit_encoding[MAX_ENCODING_LENGTH] = \"utf-8\";\n int shared_repository = PERM_UMASK;\n+int deny_non_fast_forwards = 0;\n const char *apply_default_whitespace;\n int zlib_compression_level = Z_DEFAULT_COMPRESSION;\n int pager_in_use;\ndiff --git a/receive-pack.c b/receive-pack.c\nindex 78f75da..c5ea2a1 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -2,6 +2,8 @@ #include \"cache.h\"\n #include \"refs.h\"\n #include \"pkt-line.h\"\n #include \"run-command.h\"\n+#include \"commit.h\"\n+#include \"object.h\"\n \n static const char receive_pack_usage[] = \"git-receive-pack <git-dir>\";\n \n@@ -127,6 +129,16 @@ static int update(struct command *cmd)\n \t\treturn error(\"unpack should have generated %s, \"\n \t\t\t     \"but I can't find it!\", new_hex);\n \t}\n+\tif (deny_non_fast_forwards) {\n+\t\tstruct commit *old_commit, *new_commit;\n+\t\told_commit = (struct commit *)parse_object(old_sha1);\n+\t\tnew_commit = (struct commit *)parse_object(new_sha1);\n+\t\tstruct commit_list *bases = get_merge_bases(old_commit,\n+\t\t\t\tnew_commit, 1);\n+\t\tif (!bases || hashcmp(old_sha1, bases->item->object.sha1))\n+\t\t\treturn error(\"denying non-fast forward;\"\n+\t\t\t\t\t\" you should pull first\");\n+\t}\n \tsafe_create_leading_directories(lock_name);\n \n \tnewfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);\ndiff --git a/setup.c b/setup.c\nindex 2afdba4..9a46a58 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -244,6 +244,8 @@ int check_repository_format_version(cons\n                repository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n \t\tshared_repository = git_config_perm(var, value);\n+\telse if (strcmp(var, \"receive.denynonfastforwards\") == 0)\n+\t\tdeny_non_fast_forwards = git_config_bool(var, value);\n        return 0;\n }\n \n-- \n1.4.2.1.g7521-dirty\n"},{"id":"27305","messageId":"7vfyemf9ah.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210027430.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-20T22:44:06Z","receivedAt":"2006-09-20T22:44:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> If receive.denyNonFastforwards is set to true, git-receive-pack will deny\n> non fast-forwards, i.e. forced updates. Most notably, a push to a repository\n> which has that flag set to true will fail.\n>\n> As a first user, 'git-init-db --shared' sets this flag, since in a shared\n> setup, you are most unlikely to want forced pushes to succeed.\n\nI am Ok with the general idea, but ...\n\n> @@ -127,6 +129,16 @@ static int update(struct command *cmd)\n>  \t\treturn error(\"unpack should have generated %s, \"\n>  \t\t\t     \"but I can't find it!\", new_hex);\n>  \t}\n> +\tif (deny_non_fast_forwards) {\n> +\t\tstruct commit *old_commit, *new_commit;\n> +\t\told_commit = (struct commit *)parse_object(old_sha1);\n> +\t\tnew_commit = (struct commit *)parse_object(new_sha1);\n> +\t\tstruct commit_list *bases = get_merge_bases(old_commit,\n> +\t\t\t\tnew_commit, 1);\n> +\t\tif (!bases || hashcmp(old_sha1, bases->item->object.sha1))\n> +\t\t\treturn error(\"denying non-fast forward;\"\n> +\t\t\t\t\t\" you should pull first\");\n> +\t}\n>  \tsafe_create_leading_directories(lock_name);\n>  \n>  \tnewfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);\n\nI am wondering if there can be more than one base and the old_sha1\nis not returned as the first one.\n"},{"id":"27307","messageId":"20060920224640.GH24415@spearce.org","threadId":"5626","inReplyTo":"7vfyemf9ah.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2006-09-20T22:46:40Z","receivedAt":"2006-09-20T22:46:40Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > If receive.denyNonFastforwards is set to true, git-receive-pack will deny\n> > non fast-forwards, i.e. forced updates. Most notably, a push to a repository\n> > which has that flag set to true will fail.\n> >\n> > As a first user, 'git-init-db --shared' sets this flag, since in a shared\n> > setup, you are most unlikely to want forced pushes to succeed.\n> \n> I am Ok with the general idea, but ...\n> \n> > @@ -127,6 +129,16 @@ static int update(struct command *cmd)\n> >  \t\treturn error(\"unpack should have generated %s, \"\n> >  \t\t\t     \"but I can't find it!\", new_hex);\n> >  \t}\n> > +\tif (deny_non_fast_forwards) {\n> > +\t\tstruct commit *old_commit, *new_commit;\n> > +\t\told_commit = (struct commit *)parse_object(old_sha1);\n> > +\t\tnew_commit = (struct commit *)parse_object(new_sha1);\n> > +\t\tstruct commit_list *bases = get_merge_bases(old_commit,\n> > +\t\t\t\tnew_commit, 1);\n> > +\t\tif (!bases || hashcmp(old_sha1, bases->item->object.sha1))\n> > +\t\t\treturn error(\"denying non-fast forward;\"\n> > +\t\t\t\t\t\" you should pull first\");\n> > +\t}\n> >  \tsafe_create_leading_directories(lock_name);\n> >  \n> >  \tnewfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);\n> \n> I am wondering if there can be more than one base and the old_sha1\n> is not returned as the first one.\n\nNot to mention how does this work when the ref didn't exist?\nIs this entire block of code being bypassed by something not show\nin the context?\n\n-- \nShawn.\n"},{"id":"27309","messageId":"Pine.LNX.4.63.0609210107140.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":"7vfyemf9ah.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-20T23:07:54Z","receivedAt":"2006-09-20T23:07:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nIf receive.denyNonFastforwards is set to true, git-receive-pack will deny\nnon fast-forwards, i.e. forced updates. Most notably, a push to a repository\nwhich has that flag set will fail.\n\nAs a first user, 'git-init-db --shared' sets this flag, since in a shared\nsetup, you are most unlikely to want forced pushes to succeed.\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n\t2nd try.\n\n\tNo longer barfs on new refs, and tries all merge bases (even if I\n\tcannot come up with any scenario where there is more than one merge\n\tbase in the case of a fast forward).\n\n\tAlso is C99.\n\n builtin-init-db.c |    1 +\n cache.h           |    1 +\n environment.c     |    1 +\n receive-pack.c    |   16 ++++++++++++++++\n setup.c           |    2 ++\n 5 files changed, 21 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 36c3088..e6a2d7d 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -310,6 +310,7 @@ int cmd_init_db(int argc, const char **a\n \t\t */\n \t\tsprintf(buf, \"%d\", shared_repository);\n \t\tgit_config_set(\"core.sharedrepository\", buf);\n+\t\tgit_config_set(\"receive.denyNonFastforwards\", \"true\");\n \t}\n \n \treturn 0;\ndiff --git a/cache.h b/cache.h\nindex f2fdc00..2224c83 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -188,6 +188,7 @@ extern int prefer_symlink_refs;\n extern int log_all_ref_updates;\n extern int warn_ambiguous_refs;\n extern int shared_repository;\n+extern int deny_non_fast_forwards;\n extern const char *apply_default_whitespace;\n extern int zlib_compression_level;\n \ndiff --git a/environment.c b/environment.c\nindex 84d870c..63b1d15 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -20,6 +20,7 @@ int warn_ambiguous_refs = 1;\n int repository_format_version;\n char git_commit_encoding[MAX_ENCODING_LENGTH] = \"utf-8\";\n int shared_repository = PERM_UMASK;\n+int deny_non_fast_forwards = 0;\n const char *apply_default_whitespace;\n int zlib_compression_level = Z_DEFAULT_COMPRESSION;\n int pager_in_use;\ndiff --git a/receive-pack.c b/receive-pack.c\nindex 78f75da..a6ec9f9 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -2,6 +2,8 @@ #include \"cache.h\"\n #include \"refs.h\"\n #include \"pkt-line.h\"\n #include \"run-command.h\"\n+#include \"commit.h\"\n+#include \"object.h\"\n \n static const char receive_pack_usage[] = \"git-receive-pack <git-dir>\";\n \n@@ -127,6 +129,20 @@ static int update(struct command *cmd)\n \t\treturn error(\"unpack should have generated %s, \"\n \t\t\t     \"but I can't find it!\", new_hex);\n \t}\n+\tif (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {\n+\t\tstruct commit *old_commit, *new_commit;\n+\t\tstruct commit_list *bases;\n+\n+\t\told_commit = (struct commit *)parse_object(old_sha1);\n+\t\tnew_commit = (struct commit *)parse_object(new_sha1);\n+\t\tfor (bases = get_merge_bases(old_commit, new_commit, 1);\n+\t\t\t\tbases; bases = bases->next)\n+\t\t\tif (!hashcmp(old_sha1, bases->item->object.sha1))\n+\t\t\t\tbreak;\n+\t\tif (!bases)\n+\t\t\treturn error(\"denying non-fast forward;\"\n+\t\t\t\t\t\" you should pull first\");\n+\t}\n \tsafe_create_leading_directories(lock_name);\n \n \tnewfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);\ndiff --git a/setup.c b/setup.c\nindex 2afdba4..9a46a58 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -244,6 +244,8 @@ int check_repository_format_version(cons\n                repository_format_version = git_config_int(var, value);\n \telse if (strcmp(var, \"core.sharedrepository\") == 0)\n \t\tshared_repository = git_config_perm(var, value);\n+\telse if (strcmp(var, \"receive.denynonfastforwards\") == 0)\n+\t\tdeny_non_fast_forwards = git_config_bool(var, value);\n        return 0;\n }\n \n-- \n1.4.2.1.g25e3-dirty\n"},{"id":"27313","messageId":"7vzmcudt3t.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210107140.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-20T23:19:02Z","receivedAt":"2006-09-20T23:19:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> If receive.denyNonFastforwards is set to true, git-receive-pack will deny\n> non fast-forwards, i.e. forced updates. Most notably, a push to a repository\n> which has that flag set will fail.\n>\n> As a first user, 'git-init-db --shared' sets this flag, since in a shared\n> setup, you are most unlikely to want forced pushes to succeed.\n>\n> Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nLooks good.  Care to do a handful more tasks before we forget?\n\n Documentation/git-init-db.txt\n Documentation/config.txt\n Documentation/git-receive-pack.txt\n t/t5400-send-pack.sh or a new test t/t5401-push-into-shared-repo.sh\n"},{"id":"27316","messageId":"7vlkoeds82.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210107140.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-20T23:38:05Z","receivedAt":"2006-09-20T23:38:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> \tNo longer barfs on new refs, and tries all merge bases (even if I\n> \tcannot come up with any scenario where there is more than one merge\n> \tbase in the case of a fast forward).\n\nHmm.  If that is the case (and I think it is although I haven't\ncome up with a proof), the test can be written like this:\n\n\tif (bases && !bases->next &&\n            hashcmp(old_sha1, bases->item->object.sha1))\n        \t; /* happy */\n\telse\n        \treturn error(\"not a fast forward\");\n\nperhaps?\n"},{"id":"27320","messageId":"20060920234548.GA20461@coredump.intra.peff.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210107140.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2006-09-20T23:45:48Z","receivedAt":"2006-09-20T23:45:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 21, 2006 at 01:07:54AM +0200, Johannes Schindelin wrote:\n\n> +\tif (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {\n> +\t\tstruct commit *old_commit, *new_commit;\n> +\t\tstruct commit_list *bases;\n> +\n> +\t\told_commit = (struct commit *)parse_object(old_sha1);\n> +\t\tnew_commit = (struct commit *)parse_object(new_sha1);\n> +\t\tfor (bases = get_merge_bases(old_commit, new_commit, 1);\n> +\t\t\t\tbases; bases = bases->next)\n> +\t\t\tif (!hashcmp(old_sha1, bases->item->object.sha1))\n> +\t\t\t\tbreak;\n> +\t\tif (!bases)\n> +\t\t\treturn error(\"denying non-fast forward;\"\n> +\t\t\t\t\t\" you should pull first\");\n> +\t}\n\nMemory leak on 'bases'. It shouldn't matter much because the program is\nshort-lived, but I couldn't remember if we have a policy on such things\nwith increasing lib-ification.\n\n-Peff\n"},{"id":"27322","messageId":"Pine.LNX.4.63.0609210206320.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":"20060920234548.GA20461@coredump.intra.peff.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-21T00:07:19Z","receivedAt":"2006-09-21T00:07:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\n\nOn Wed, 20 Sep 2006, Jeff King wrote:\n\n> On Thu, Sep 21, 2006 at 01:07:54AM +0200, Johannes Schindelin wrote:\n> \n> > +\tif (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {\n> > +\t\tstruct commit *old_commit, *new_commit;\n> > +\t\tstruct commit_list *bases;\n> > +\n> > +\t\told_commit = (struct commit *)parse_object(old_sha1);\n> > +\t\tnew_commit = (struct commit *)parse_object(new_sha1);\n> > +\t\tfor (bases = get_merge_bases(old_commit, new_commit, 1);\n> > +\t\t\t\tbases; bases = bases->next)\n> > +\t\t\tif (!hashcmp(old_sha1, bases->item->object.sha1))\n> > +\t\t\t\tbreak;\n> > +\t\tif (!bases)\n> > +\t\t\treturn error(\"denying non-fast forward;\"\n> > +\t\t\t\t\t\" you should pull first\");\n> > +\t}\n> \n> Memory leak on 'bases'. It shouldn't matter much because the program is\n> short-lived, but I couldn't remember if we have a policy on such things\n> with increasing lib-ification.\n\nTrue. How about this:\n\n-- snip --\n\n---\n receive-pack.c |    6 ++++--\n 1 files changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/receive-pack.c b/receive-pack.c\nindex a6ec9f9..d84fc2c 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -131,17 +131,19 @@ static int update(struct command *cmd)\n \t}\n \tif (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {\n \t\tstruct commit *old_commit, *new_commit;\n-\t\tstruct commit_list *bases;\n+\t\tstruct commit_list *bases, *backup;\n \n \t\told_commit = (struct commit *)parse_object(old_sha1);\n \t\tnew_commit = (struct commit *)parse_object(new_sha1);\n-\t\tfor (bases = get_merge_bases(old_commit, new_commit, 1);\n+\t\tbackup = get_merge_bases(old_commit, new_commit, 1);\n+\t\tfor (bases = backup;\n \t\t\t\tbases; bases = bases->next)\n \t\t\tif (!hashcmp(old_sha1, bases->item->object.sha1))\n \t\t\t\tbreak;\n \t\tif (!bases)\n \t\t\treturn error(\"denying non-fast forward;\"\n \t\t\t\t\t\" you should pull first\");\n+\t\tfree_commit_list(backup);\n \t}\n \tsafe_create_leading_directories(lock_name);\n \n-- \n1.4.2.1.g6ad2-dirty\n"},{"id":"27323","messageId":"Pine.LNX.4.63.0609210208350.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":"7vzmcudt3t.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-21T00:10:30Z","receivedAt":"2006-09-21T00:10:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 20 Sep 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > If receive.denyNonFastforwards is set to true, git-receive-pack will deny\n> > non fast-forwards, i.e. forced updates. Most notably, a push to a repository\n> > which has that flag set will fail.\n> >\n> > As a first user, 'git-init-db --shared' sets this flag, since in a shared\n> > setup, you are most unlikely to want forced pushes to succeed.\n> >\n> > Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n> \n> Looks good.  Care to do a handful more tasks before we forget?\n> \n>  Documentation/git-init-db.txt\n>  Documentation/config.txt\n>  Documentation/git-receive-pack.txt\n>  t/t5400-send-pack.sh or a new test t/t5401-push-into-shared-repo.sh\n\nI expected Jakub to ask for it ;-)\n\n-- snip --\n[PATCH] Document receive.denyNonFastforwards\n\nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n---\n Documentation/config.txt           |    7 +++++++\n Documentation/git-init-db.txt      |    4 ++++\n Documentation/git-receive-pack.txt |    2 ++\n t/t5400-send-pack.sh               |   10 ++++++++++\n 4 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 844cae4..6802d30 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -267,3 +267,10 @@ whatchanged.difftree::\n imap::\n \tThe configuration variables in the 'imap' section are described\n \tin gitlink:git-imap-send[1].\n+\n+receive.denyNonFastforwads::\n+\tIf set to true, git-receive-pack will deny a ref update which is\n+\tnot a fast forward. Use this to prevent such an update via a push,\n+\teven if that push is forced. This configuration variable is\n+\tset when initializing a shared repository.\n+\t\ndiff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt\nindex 63cd5da..ca7d09d 100644\n--- a/Documentation/git-init-db.txt\n+++ b/Documentation/git-init-db.txt\n@@ -48,6 +48,10 @@ is given:\n  - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository\n    readable by all users.\n \n+By default, the configuration flag receive.denyNonFastforward is enabled\n+in shared repositories, so that you cannot force a non fast-forwarding push\n+into it.\n+\n --\n \n \ndiff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt\nindex f9457d4..0dfadc2 100644\n--- a/Documentation/git-receive-pack.txt\n+++ b/Documentation/git-receive-pack.txt\n@@ -73,6 +73,8 @@ packed and is served via a dumb transpor\n There are other real-world examples of using update and\n post-update hooks found in the Documentation/howto directory.\n \n+git-receive-pack honours the receive.denyNonFastforwards flag, which\n+tells it if updates to a ref should be denied if they are not fast-forwards.\n \n OPTIONS\n -------\ndiff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh\nindex f3694ac..6be3c80 100755\n--- a/t/t5400-send-pack.sh\n+++ b/t/t5400-send-pack.sh\n@@ -64,4 +64,14 @@ test_expect_success \\\n \tcmp victim/.git/refs/heads/master .git/refs/heads/master\n '\n \n+test_expect_success \\\n+        'pushing with --force should be denied with denyNonFastforwards' '\n+\tcd victim &&\n+\tgit-repo-config receive.denyNonFastforwards true &&\n+\tcd .. &&\n+\tgit-update-ref refs/heads/master master^ &&\n+\tgit-send-pack --force ./victim/.git/ master &&\n+\t! diff -u .git/refs/heads/master victim/.git/refs/heads/master\n+'\n+\n test_done\n-- \n1.4.2.1.g6ad2-dirty\n"},{"id":"27324","messageId":"Pine.LNX.4.63.0609210211570.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":"7vlkoeds82.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-21T00:17:22Z","receivedAt":"2006-09-21T00:17:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 20 Sep 2006, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > \tNo longer barfs on new refs, and tries all merge bases (even if I\n> > \tcannot come up with any scenario where there is more than one merge\n> > \tbase in the case of a fast forward).\n> \n> Hmm.  If that is the case (and I think it is although I haven't\n> come up with a proof),\n\n>From git-fetch.sh:\n\n            # Require fast-forward.\n            mb=$(git-merge-base \"$local\" \"$2\") &&\n            case \"$2,$mb\" in\n            $local,*)\n                if test -n \"$verbose\"\n                then\n                        echo >&2 \"* $1: same as $3\"\n                fi\n                ;;\n            *,$local)\n                echo >&2 \"* $1: fast forward to $3\"\n                echo >&2 \"  from $local to $2\"\n                git-update-ref -m \"$rloga: fast-forward\" \"$1\" \"$2\" \"$local\"\n                ;;\n\nSo we indeed assumed that git-merge-base returns the old commit in the \ncase of a fast-forward (git-merge-base returns just the first item of the \nresult of get_merge_bases()).\n\nNote that I have no proof that this assumption is true. It might be wrong \nin this case:\n\n    X - a - b - c - Y\n  /           /\no - d - e - f\n\nwhere X is the old commit, and Y is the new commit. But I am too tired to \ntest it right now.\n\n>... the test can be written like this:\n> \n> \tif (bases && !bases->next &&\n>             hashcmp(old_sha1, bases->item->object.sha1))\n>         \t; /* happy */\n> \telse\n>         \treturn error(\"not a fast forward\");\n\nPlus\n\n\tfree_commit_list(bases);\n\nas Jeff pointed out.\n\nCiao,\nDscho\n"},{"id":"27334","messageId":"7v8xkderhv.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210211570.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T05:08:28Z","receivedAt":"2006-09-21T05:08:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Wed, 20 Sep 2006, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> \n>> > \tNo longer barfs on new refs, and tries all merge bases (even if I\n>> > \tcannot come up with any scenario where there is more than one merge\n>> > \tbase in the case of a fast forward).\n>> \n>> Hmm.  If that is the case (and I think it is although I haven't\n>> come up with a proof),\n>\n> From git-fetch.sh:\n>\n>             # Require fast-forward.\n>             mb=$(git-merge-base \"$local\" \"$2\") &&\n>...\n> So we indeed assumed that git-merge-base returns the old commit in the \n> case of a fast-forward (git-merge-base returns just the first item of the \n> result of get_merge_bases()).\n\nI think this was leftover from the days we naïvely assumed that\nmultiple bases does not matter, and probably needs updating.\n\n> Note that I have no proof that this assumption is true. It might be wrong \n> in this case:\n>\n>     X - a - b - c - Y\n>   /           /\n> o - d - e - f\n>\n> where X is the old commit, and Y is the new commit. But I am too tired to \n> test it right now.\n"},{"id":"27337","messageId":"7vzmctdbou.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210206320.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T05:35:13Z","receivedAt":"2006-09-21T05:35:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Wed, 20 Sep 2006, Jeff King wrote:\n>\n>> Memory leak on 'bases'. It shouldn't matter much because the program is\n>> short-lived, but I couldn't remember if we have a policy on such things\n>> with increasing lib-ification.\n>\n> True. How about this:\n>\n> -- snip --\n>\n> +\t\tbackup = get_merge_bases(old_commit, new_commit, 1);\n> +\t\tfor (bases = backup;\n>  \t\t\t\tbases; bases = bases->next)\n>  \t\t\tif (!hashcmp(old_sha1, bases->item->object.sha1))\n>  \t\t\t\tbreak;\n>  \t\tif (!bases)\n>  \t\t\treturn error(\"denying non-fast forward;\"\n>  \t\t\t\t\t\" you should pull first\");\n> +\t\tfree_commit_list(backup);\n\nStill leaking on the error path ;-).\n\nWill fixup and apply; no need to resend.\n"},{"id":"27338","messageId":"20060921055252.GA29479@coredump.intra.peff.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210211570.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2006-09-21T05:52:53Z","receivedAt":"2006-09-21T05:52:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Sep 21, 2006 at 02:17:22AM +0200, Johannes Schindelin wrote:\n\n> So we indeed assumed that git-merge-base returns the old commit in the \n> case of a fast-forward (git-merge-base returns just the first item of the \n> result of get_merge_bases()).\n> \n> Note that I have no proof that this assumption is true. It might be wrong \n> in this case:\n> \n>     X - a - b - c - Y\n>   /           /\n> o - d - e - f\n\nIn your example, git-merge-base X Y returns X. In fact, I could only get\none merge base out of git-merge-base --all. I tried looking in the tests\nto find a case that produced multiple merge bases, but I was unable to\nfind one. Is there an example floating around somewhere?\n\n-Peff\n"},{"id":"27339","messageId":"7vfyeld9c2.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609210208350.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T06:26:05Z","receivedAt":"2006-09-21T06:26:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh\n> index f3694ac..6be3c80 100755\n> --- a/t/t5400-send-pack.sh\n> +++ b/t/t5400-send-pack.sh\n> @@ -64,4 +64,14 @@ test_expect_success \\\n>  \tcmp victim/.git/refs/heads/master .git/refs/heads/master\n>  '\n>  \n> +test_expect_success \\\n> +        'pushing with --force should be denied with denyNonFastforwards' '\n> +\tcd victim &&\n> +\tgit-repo-config receive.denyNonFastforwards true &&\n> +\tcd .. &&\n> +\tgit-update-ref refs/heads/master master^ &&\n> +\tgit-send-pack --force ./victim/.git/ master &&\n> +\t! diff -u .git/refs/heads/master victim/.git/refs/heads/master\n> +'\n> +\n>  test_done\n\nThe change shown by \"git -SGIT_CONFIG -p t/Makefile\" seems to\ninterfere with this test.  When I run it from shell by hand it\nworks OK, but when I run it like this:\n\n\tcd t && make T=t5400*.sh\n\nit barfs.  s/git-send-pack/strace -f git-send-pack/ seems to\nindicate that the receiving end is trying to open .git/config\n(rightly, because you told it to do so three months ago ;-)\nwhile its $cwd is victim/.git and fails to read the setting you\nare trying to test.  Not good.\n\nI think the change to t/Makefile from 9c3796fc needs to be\nrethought.  Most of the tests would be fine with .git/config\n(they never step outside t/trash/.git repository), but others\nneed tweaking, including unsetting it (while unsetting HOME at\nthe same time perhaps when you do not want to be affected).\nAlso it may make sense to do the default setting of GIT_CONFIG\nin t/test-lib.sh, with a prominent warning why we explicitly\nset it to a known value.\n\nIn the meantime, I got things working with this patch on top of\nyour test update.\n\n---\n\ndiff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh\nindex 6be3c80..be47ee2 100755\n--- a/t/t5400-send-pack.sh\n+++ b/t/t5400-send-pack.sh\n@@ -64,6 +64,10 @@ test_expect_success \\\n \tcmp victim/.git/refs/heads/master .git/refs/heads/master\n '\n \n+unset GIT_CONFIG GIT_CONFIG_LOCAL\n+HOME=`pwd`/no-such-directory\n+export HOME ;# this way we force the victim/.git/config to be used.\n+\n test_expect_success \\\n         'pushing with --force should be denied with denyNonFastforwards' '\n \tcd victim &&\n"},{"id":"27340","messageId":"7vbqp9d8j2.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"20060921055252.GA29479@coredump.intra.peff.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T06:43:29Z","receivedAt":"2006-09-21T06:43:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>>     X - a - b - c - Y\n>>   /           /\n>> o - d - e - f\n>\n> In your example, git-merge-base X Y returns X. In fact, I could only get\n> one merge base out of git-merge-base --all. I tried looking in the tests\n> to find a case that produced multiple merge bases, but I was unable to\n> find one. Is there an example floating around somewhere?\n\nThere are quite a few in git.git itself.  In the recent history,\nthis counts 42, the answer to everything.\n\n#!/bin/sh\nLF='\n'\ngit rev-list --parents master..next |\nwhile read it p1 p2 octo\ndo\n\tcase \"$p2\" in '') continue ;; esac\n        mb=`git merge-base --all \"$p1\" \"$p2\"`\n\tcase \"$mb\" in ?*\"$LF\"?*) echo \"$p1 $p2\" : $mb ;;\n        esac\ndone\n"},{"id":"27352","messageId":"Pine.LNX.4.63.0609211103130.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":"7vbqp9d8j2.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-21T09:08:02Z","receivedAt":"2006-09-21T09:08:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 20 Sep 2006, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> >>     X - a - b - c - Y\n> >>   /           /\n> >> o - d - e - f\n> >\n> > In your example, git-merge-base X Y returns X. In fact, I could only get\n> > one merge base out of git-merge-base --all. I tried looking in the tests\n> > to find a case that produced multiple merge bases, but I was unable to\n> > find one. Is there an example floating around somewhere?\n> \n> There are quite a few in git.git itself.  In the recent history,\n> this counts 42, the answer to everything.\n> \n> #!/bin/sh\n> LF='\n> '\n> git rev-list --parents master..next |\n> while read it p1 p2 octo\n> do\n> \tcase \"$p2\" in '') continue ;; esac\n>         mb=`git merge-base --all \"$p1\" \"$p2\"`\n\nAt this point you should also check if $p2 is in $mb.\n\n> \tcase \"$mb\" in ?*\"$LF\"?*) echo \"$p1 $p2\" : $mb ;;\n>         esac\n> done\n\nThe question was, if there is a fast-forward with more than one merge \nbase, i.e. if the loop we jump^H^H^H^Hdo over bases is necessary.\n\nAnd no, I am not aware of any such example.\n\nProbably because X would mark all ancestors (_including_ all other \npossible merge bases) as uninteresting.\n\nTherefore, I am convinced that there is no fast-forward returning more \nthan one merge base. It is technically impossible. And thus, our loop is \nnot necessary, but what the heck.\n\nCiao,\nDscho\n"},{"id":"27353","messageId":"Pine.LNX.4.63.0609211108140.19042@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"5626","inReplyTo":"7vfyeld9c2.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-09-21T09:10:33Z","receivedAt":"2006-09-21T09:10:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 20 Sep 2006, Junio C Hamano wrote:\n\n> I think the change to t/Makefile from 9c3796fc needs to be\n> rethought.\n\nI looked into 9c3796fc, and the change to t/Makefile is UTTER CRAP! What \nIDIOT has made that change? Oh wait, was me...\n\nWhat about removing that creepy change to t/Makefile, and move your hack \ninto test-lib.sh? It _should_ be safe, as _nothing_ in $HOME should affect \nthe tests anyway.\n\nThoughts?\n\nCiao,\nDscho\n"},{"id":"27356","messageId":"7vd59pa7vw.fsf@assigned-by-dhcp.cox.net","threadId":"5626","inReplyTo":"Pine.LNX.4.63.0609211103130.19042@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] add receive.denyNonFastforwards config variable","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T09:25:39Z","receivedAt":"2006-09-21T09:25:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> Jeff King <peff@peff.net> writes:\n>> \n>> >>     X - a - b - c - Y\n>> >>   /           /\n>> >> o - d - e - f\n>\n> And no, I am not aware of any such example.\n>\n> Probably because X would mark all ancestors (_including_ all other \n> possible merge bases) as uninteresting.\n\nTrue.  Paint me convinced, too.\n"}]}