{"thread":{"id":"60780","subject":"Git Push Always uses Protocol Version 0","startedAt":"2024-01-22T08:59:48Z","lastAt":"2024-01-22T22:04:42Z","messageCount":4,"participants":["Zsolt Imre","Junio C Hamano","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"487191","messageId":"FD545E92-18EF-44B5-A7D5-61ECADD880E6@gmail.com","threadId":"60780","inReplyTo":null,"subject":"Git Push Always uses Protocol Version 0","fromName":"Zsolt Imre","fromEmail":"imrexzsolt@gmail.com","sentAt":"2024-01-22T08:59:35Z","receivedAt":"2024-01-22T08:59:48Z","isPatch":false,"sender":{"key":"imrexzsolt@gmail.com","avatar":null},"body":"Hi there,\n\nI'm not entirely sure if this is a bug or I am missing something, but I thought I would share in the hope someone can help out. I'm playing around with Git and trying to implement a git server that communicates over HTTP and supports Git protocol version 2 *only*.\n\nWhen I `clone` a repository, the Git client (version 2.43.0), after fetching the capabilities using protocol version 2, it proceeds to fetch the refs, again, via protocol version 2 using the `ls-refs` command.  However, when I try to `push` my changes to the repo, the Git client refuses to use protocol version 2 and tries to obtain the ref list using protocol version 0, even if I pass in the `-c protocol.version=2` command line argument.\n\nIs there a way to make the client use only protocol version 2 instead of mixing the different protocols?\n\nThanks in advance for any help/guidance.\n\n"},{"id":"487211","messageId":"xmqqa5oxurnj.fsf@gitster.g","threadId":"60780","inReplyTo":"FD545E92-18EF-44B5-A7D5-61ECADD880E6@gmail.com","subject":"Re: Git Push Always uses Protocol Version 0","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-01-22T18:52:48Z","receivedAt":"2024-01-22T18:52:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Zsolt Imre <imrexzsolt@gmail.com> writes:\n\n> I'm not entirely sure if this is a bug or I am missing something,\n> but I thought I would share in the hope someone can help out. I'm\n> playing around with Git and trying to implement a git server that\n> communicates over HTTP and supports Git protocol version 2 *only*.\n>\n> When I `clone` a repository, the Git client (version 2.43.0),\n> after fetching the capabilities using protocol version 2, it\n> proceeds to fetch the refs, again, via protocol version 2 using\n> the `ls-refs` command.  However, when I try to `push` my changes\n> to the repo, the Git client refuses to use protocol version 2 and\n> tries to obtain the ref list using protocol version 0, even if I\n> pass in the `-c protocol.version=2` command line argument.\n\nGiven that v0 and v1 in the push direction behave exactly the same,\nand there has been no need to add features that were not supportable\nin v1 in the push direction, it is not surprising to see this code\n\n        int cmd_send_pack(int argc, const char **argv, const char *prefix)\n        {\n\t\t...\n                switch (discover_version(&reader)) {\n                case protocol_v2:\n                        die(\"support for protocol v2 not implemented yet\");\n                        break;\n\nin https://github.com/git/git/blob/master/builtin/send-pack.c#L282\nand also\n\n        int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n        {\n\t\t...\n                switch (determine_protocol_version_server()) {\n                case protocol_v2:\n                        /*\n                         * push support for protocol v2 has not been implemented yet,\n                         * so ignore the request to use v2 and fallback to using v0.\n                         */\n                        break;\n\nin https://github.com/git/git/blob/master/builtin/receive-pack.c#L2538\n\nthat tells the receiving end to demote \"v2\" request down to \"v0\",\nand have the pushing end honor that choice.\n\nWhat specifically did you want to gain by using protocol version 2\nin the \"push\" direction that you cannot do with the current\nversions?\n\n\n"},{"id":"487213","messageId":"E121C312-5771-47EF-9099-BEC8EFC2B9BD@gmail.com","threadId":"60780","inReplyTo":"xmqqa5oxurnj.fsf@gitster.g","subject":"Re: Git Push Always uses Protocol Version 0","fromName":"Zsolt Imre","fromEmail":"imrexzsolt@gmail.com","sentAt":"2024-01-22T19:24:53Z","receivedAt":"2024-01-22T19:25:05Z","isPatch":false,"sender":{"key":"imrexzsolt@gmail.com","avatar":null},"body":"Hi. Thanks for your response. Yes, I saw those switch statements, but probably misunderstood the intent behind them.\n\nChecking `determine_protocol_version_server` the in-code documentation says:\n\n* Used by a server to determine which protocol version should be used based on\n* a client's request, communicated via the 'GIT_PROTOCOL' environment variable\n\nI explicitly set the `GIT_PROTOCOL` environment variable to `version=2`. (Will share more at the end of this email why, as you asked anyways)\n\nThe client-side calls `discover_version` to check the version supported by the server. Which ultimately ends up being done as:\n\n\nenum protocol_version determine_protocol_version_client(const char *server_response)\n{\nenum protocol_version version = protocol_v0;\n\nif (skip_prefix(server_response, \"version \", &server_response)) {\nversion = parse_protocol_version(server_response);\n\nif (version == protocol_unknown_version)\ndie(\"server is speaking an unknown protocol\");\nif (version == protocol_v0)\ndie(\"protocol error: server explicitly said version 0\");\n}\n\nreturn version;\n}\n\nThe server, according to my understanding of the documentations so far, will not return a version identifier if the client is not talking the version 2 (or maybe even version 1) of the protocol. \nThe git clone I mentioned in my previous email was clearly using protocol version 2 and it set the appropriate header to indicate this when discovering capabilities. See full request headers below.\n\nPOST /testing.git/git-upload-pack HTTP/1.1\nHost: 127.0.0.1:9000\nAccept: application/x-git-upload-pack-result\nAccept-Encoding: deflate, gzip\nAccept-Language: en-GB, *;q=0.9\nContent-Length: 122\nContent-Type: application/x-git-upload-pack-request\nGit-Protocol: version=2\nUser-Agent: git/2.43.0\n\nThe server, in this case did not return a version identifier as the first PKT-LINE either, but responded otherwise according to the V2 protocol. Everything was working well.\n\nThen, the above request was followed by by a proper fetch command in which the client again specified the use of V2 protocol:\n\nPOST /testing.git/git-upload-pack HTTP/1.1\nHost: 127.0.0.1:9000\nAccept: application/x-git-upload-pack-result\nAccept-Encoding: deflate, gzip\nAccept-Language: en-GB, *;q=0.9\nContent-Length: 160\nContent-Type: application/x-git-upload-pack-request\nGit-Protocol: version=2\nUser-Agent: git/2.43.0\n\nAccordingly, the cloning worked perfectly fine.\n\nBut then, when I want to push to the repo, the client does not do any capabilities or version discovery, just goes with V0 of the protocol to get the refs:\n\nGET /testing.git/info/refs?service=git-receive-pack HTTP/1.1\nHost: 127.0.0.1:9000\nAccept: */*\nAccept-Encoding: deflate, gzip\nAccept-Language: en-GB, *;q=0.9\nCache-Control: no-cache\nPragma: no-cache\nUser-Agent: git/2.43.0\n\nWhat I was expecting, given things are going stateless, that on push the client first discovers the protocol supported by the server and picks the most recent - in this case, that would be version 2.\n\nAnd to answer your question of what I cannot do with the \"current versions\" of the protocol: I could do everything, of course. But, if there's protocol 0, 1 and 2 and I wanted to implement only version 2, I thought I should be able to. If protocol V2 was complete, I would not have to worry about implementing V0 and V1 (saving some time and headache), especially because I do not care about supporting old clients. I may have misunderstood the word \"version\" and version 2 is more of an \"extension\" to V1?\n\n\n> On 22 Jan 2024, at 18:52, Junio C Hamano <gitster@pobox.com> wrote:\n> \n> Zsolt Imre <imrexzsolt@gmail.com> writes:\n> \n>> I'm not entirely sure if this is a bug or I am missing something,\n>> but I thought I would share in the hope someone can help out. I'm\n>> playing around with Git and trying to implement a git server that\n>> communicates over HTTP and supports Git protocol version 2 *only*.\n>> \n>> When I `clone` a repository, the Git client (version 2.43.0),\n>> after fetching the capabilities using protocol version 2, it\n>> proceeds to fetch the refs, again, via protocol version 2 using\n>> the `ls-refs` command.  However, when I try to `push` my changes\n>> to the repo, the Git client refuses to use protocol version 2 and\n>> tries to obtain the ref list using protocol version 0, even if I\n>> pass in the `-c protocol.version=2` command line argument.\n> \n> Given that v0 and v1 in the push direction behave exactly the same,\n> and there has been no need to add features that were not supportable\n> in v1 in the push direction, it is not surprising to see this code\n> \n>        int cmd_send_pack(int argc, const char **argv, const char *prefix)\n>        {\n> ...\n>                switch (discover_version(&reader)) {\n>                case protocol_v2:\n>                        die(\"support for protocol v2 not implemented yet\");\n>                        break;\n> \n> in https://github.com/git/git/blob/master/builtin/send-pack.c#L282\n> and also\n> \n>        int cmd_receive_pack(int argc, const char **argv, const char *prefix)\n>        {\n> ...\n>                switch (determine_protocol_version_server()) {\n>                case protocol_v2:\n>                        /*\n>                         * push support for protocol v2 has not been implemented yet,\n>                         * so ignore the request to use v2 and fallback to using v0.\n>                         */\n>                        break;\n> \n> in https://github.com/git/git/blob/master/builtin/receive-pack.c#L2538\n> \n> that tells the receiving end to demote \"v2\" request down to \"v0\",\n> and have the pushing end honor that choice.\n> \n> What specifically did you want to gain by using protocol version 2\n> in the \"push\" direction that you cannot do with the current\n> versions?\n> \n> \n\n"},{"id":"487230","messageId":"20240122220440.GB811766@coredump.intra.peff.net","threadId":"60780","inReplyTo":"E121C312-5771-47EF-9099-BEC8EFC2B9BD@gmail.com","subject":"Re: Git Push Always uses Protocol Version 0","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-01-22T22:04:40Z","receivedAt":"2024-01-22T22:04:42Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jan 22, 2024 at 07:24:53PM +0000, Zsolt Imre wrote:\n\n> And to answer your question of what I cannot do with the \"current\n> versions\" of the protocol: I could do everything, of course. But, if\n> there's protocol 0, 1 and 2 and I wanted to implement only version 2,\n> I thought I should be able to. If protocol V2 was complete, I would\n> not have to worry about implementing V0 and V1 (saving some time and\n> headache), especially because I do not care about supporting old\n> clients. I may have misunderstood the word \"version\" and version 2 is\n> more of an \"extension\" to V1?\n\nI think the main confusion is that there simply isn't a \"v2\" push\nprotocol. It has not yet been written.\n\nThere was discussion when v2 was being worked on there that might be a\nsingle \"git serve\" endpoint that would handle both fetch and push. But\nin practice the backwards-compatibility technique we use requires asking\nfor the usual \"upload-pack\" or \"receive-pack\". And hence there isn't\nreally a single protocol, but still a fetch protocol which can be v0 or\nv2, and a push protocol which is always v0.\n\nIt's possible we'd shift direction there, but IMHO there's value in\nhaving separate per-operation endpoints. There's some more discussion\nin this sub-thread:\n\n  https://lore.kernel.org/git/20181213195305.249059-1-jonathantanmy@google.com/\n\nNow of course a v2 push protocol, if it is ever written, will probably\nlook a lot like the v2 fetch protocol, and they can probably share a lot\nof the implementation. But v0 and v2 are not that different either. In a\nhypothetical world where v2 push existed and you could get away with\nskipping v0 push entirely, I'd expect that \"v2 push and v2 fetch\" would\nbe about the same amount of work as the current \"v0 push and v2 fetch\".\n\n-Peff\n\nPS I saw some mention of \"v1\" in this thread; I wasn't sure if this was\n   meant to refer to \"v0\" (a mistake I have made lots of times myself).\n   But if not, \"v1\" is not really of any interest. It was a brief\n   experimental phase for the client-upgrade mechanisms, and it behaves\n   exactly like v0. No version of Git has ever used it by default.\n"}]}