From patchwork Sat Apr 4 13:43:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474281 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0C94F15AB for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DF014206E9 for ; Sat, 4 Apr 2020 13:44:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="G47jODpm" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726229AbgDDNnx (ORCPT ); Sat, 4 Apr 2020 09:43:53 -0400 Received: from mail-pg1-f193.google.com ([209.85.215.193]:35909 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725837AbgDDNnx (ORCPT ); Sat, 4 Apr 2020 09:43:53 -0400 Received: by mail-pg1-f193.google.com with SMTP id c23so5074535pgj.3 for ; Sat, 04 Apr 2020 06:43:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=54ARs/rJ+7YLjVgK0rQ34rNYuCZvNX0U2em7KXn83LM=; b=G47jODpmj4T9eujG/0C9opODCQK7xyNr/WMWNzK6LOeTJlGQs3f696zVJ9nQEOG2ye 2n+Rvqmtmzd7uJZRAiznM+Dul/nzTfKO1zfCn8yUix7kvKGx8d4D4Ql/m3YhqKpUFdso AyLNsBV05HZVLJLHIuD3sdJazmKMrSaC8YtScLmdjVYZi8mnALnssT42PqQW5B1kA/yj rg/lazbSJaB/LrbgOu5dXZHIY8XU/8DwS2k+eV1EQOooCAOBf0VQ9DVh8mwVOs+FY2F2 fxWtKfYTae83zYkVwxzw2h1AWc41xAu19C78lxuaIaspfe4UbruYQOxxBvMaacsv/109 3AvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=54ARs/rJ+7YLjVgK0rQ34rNYuCZvNX0U2em7KXn83LM=; b=QXdhgzZBF41IjkCb53Qmfou03y5g/p8DVRIZ3rQXPVklkHo74ebJYLZbg6HrrA9kM/ UJbGb2TgiodVVvYMWp9FIsimksXZTjgGxdn/asry+UamYn0GHfFMp+iqgoGOMgbJs4ks ERvuW8prhzpeJG+6hXd6iRIZTY6CZuS2/gUM3igyMJh49eScbGOa+n2cWFtRq1PnFnsh uNSAejsK1PJc7TJp3uogfxTTUNCxb6wEDh/X0dooaRnZ4iTiQeKfzyxAh6NK2WekSSh6 6HyqckqE19nxxV+mcGRwIvcRdHS0w2H2woLf6g10JfeTsayj11AHsIpBZ+sYy/Y9aPsw jl6A== X-Gm-Message-State: AGi0PuZkIl/DmgmJMcqN+fGrLCpQIsZvQ9g07x8Z5WKdlXiIVSz95Sax RVvCdTDP0HcD/7SqIgW/XyA= X-Google-Smtp-Source: APiQypKB2JPKmVrqYcjr786F+IwZB3QXj2L1SFqV00c3xnsJ0H/Lwh6oXFijWjBJGtKSY7Slc4p/TA== X-Received: by 2002:a63:7a07:: with SMTP id v7mr3893834pgc.302.1586007831980; Sat, 04 Apr 2020 06:43:51 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:51 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 1/7] transport: not report a non-head push as a branch Date: Sat, 4 Apr 2020 09:43:39 -0400 Message-Id: <20200404134345.10655-2-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin When pushing a new reference (not a head or tag), report it as a new reference instead of a new branch. Signed-off-by: Jiang Xin --- t/t5411-proc-receive-hook.sh | 153 +++++++++++++++++++++++++++++++++++ t/t5516-fetch-push.sh | 2 +- transport.c | 9 ++- 3 files changed, 160 insertions(+), 4 deletions(-) create mode 100755 t/t5411-proc-receive-hook.sh diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh new file mode 100755 index 0000000000..1784bcb584 --- /dev/null +++ b/t/t5411-proc-receive-hook.sh @@ -0,0 +1,153 @@ +#!/bin/sh +# +# Copyright (c) 2020 Jiang Xin +# + +test_description='Test proc-receive hook' + +. ./test-lib.sh + +# Create commits in and assign each commit's oid to shell variables +# given in the arguments (A, B, and C). E.g.: +# +# create_commits_in A B C +# +# NOTE: Never calling this function from a subshell since variable +# assignments will disappear when subshell exits. +create_commits_in () { + repo="$1" && + if ! parent=$(git -C "$repo" rev-parse HEAD^{}) + then + parent= + fi && + T=$(git -C "$repo" write-tree) && + shift && + while test $# -gt 0 + do + name=$1 && + test_tick && + if test -z "$parent" + then + oid=$(echo $name | git -C "$repo" commit-tree $T) + else + oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T) + fi && + eval $name=$oid && + parent=$oid && + shift || + return 1 + done && + git -C "$repo" update-ref refs/heads/master $oid +} + +# Format the output of git-push, git-show-ref and other commands to make a +# user-friendly and stable text. We can easily prepare the expect text +# without having to worry about future changes of the commit ID and spaces +# of the output. We also replce single quotes with double quotes, because +# it is boring to prepare unquoted single quotes in expect txt. +make_user_friendly_and_stable_output () { + sed \ + -e "s/ *\$//" \ + -e "s/ */ /g" \ + -e "s/'/\"/g" \ + -e "s/$A//g" \ + -e "s/$B//g" \ + -e "s/$TAG//g" \ + -e "s/$ZERO_OID//g" \ + -e "s/[0-9a-f]\{7,\}//g" +} + +# Refs of upstream : master(B) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "setup" ' + git init --bare upstream && + git init workbench && + create_commits_in workbench A B && + ( + cd workbench && + # Try to make a stable fixed width for abbreviated commit ID, + # this fixed-width oid will be replaced with "". + git config core.abbrev 7 && + git remote add origin ../upstream && + git update-ref refs/heads/master $A && + git tag -m "v123" v123 $A && + git push origin \ + $B:refs/heads/master \ + $A:refs/heads/next + ) && + TAG=$(git -C workbench rev-parse v123) && + + # setup pre-receive hook + cat >upstream/hooks/pre-receive <<-\EOF && + #!/bin/sh + + echo >&2 "# pre-receive hook" + + while read old new ref + do + echo >&2 "pre-receive< $old $new $ref" + done + EOF + + # setup post-receive hook + cat >upstream/hooks/post-receive <<-\EOF && + #!/bin/sh + + echo >&2 "# post-receive hook" + + while read old new ref + do + echo >&2 "post-receive< $old $new $ref" + done + EOF + + chmod a+x \ + upstream/hooks/pre-receive \ + upstream/hooks/post-receive +' + +# Refs of upstream : master(B) next(A) +# Refs of workbench: master(A) tags/v123 +# git-push -f : master(A) NULL tags/v123 refs/review/master/topic(A) a/b/c(A) +test_expect_success "normal git-push command" ' + git -C workbench push -f origin \ + refs/tags/v123 \ + :refs/heads/next \ + HEAD:refs/heads/master \ + HEAD:refs/review/master/topic \ + HEAD:refs/heads/a/b/c \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/tags/v123 + remote: pre-receive< refs/review/master/topic + remote: pre-receive< refs/heads/a/b/c + remote: # post-receive hook + remote: post-receive< refs/heads/master + remote: post-receive< refs/heads/next + remote: post-receive< refs/tags/v123 + remote: post-receive< refs/review/master/topic + remote: post-receive< refs/heads/a/b/c + To ../upstream + + ... HEAD -> master (forced update) + - [deleted] next + * [new tag] v123 -> v123 + * [new reference] HEAD -> refs/review/master/topic + * [new branch] HEAD -> a/b/c + EOF + test_cmp expect actual && + git -C upstream show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/a/b/c + refs/heads/master + refs/review/master/topic + refs/tags/v123 + EOF + test_cmp expect actual +' + +test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 9ff041a093..9e4b9313b5 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1039,7 +1039,7 @@ test_force_fetch_tag "annotated tag" "-f -a -m'tag message'" test_expect_success 'push --porcelain' ' mk_empty testrepo && echo >.git/foo "To testrepo" && - echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" && + echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new reference]" && echo >>.git/foo "Done" && git push >.git/bar --porcelain testrepo refs/heads/master:refs/remotes/origin/master && ( diff --git a/transport.c b/transport.c index 75c5c9fe98..4f91c8ea17 100644 --- a/transport.c +++ b/transport.c @@ -500,9 +500,12 @@ static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_widt porcelain, summary_width); else if (is_null_oid(&ref->old_oid)) print_ref_status('*', - (starts_with(ref->name, "refs/tags/") ? "[new tag]" : - "[new branch]"), - ref, ref->peer_ref, NULL, porcelain, summary_width); + (starts_with(ref->name, "refs/tags/") + ? "[new tag]" + : (starts_with(ref->name, "refs/heads/") + ? "[new branch]" + : "[new reference]")), + ref, ref->peer_ref, NULL, porcelain, summary_width); else { struct strbuf quickref = STRBUF_INIT; char type; From patchwork Sat Apr 4 13:43:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474287 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A1A421744 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6C08C206C3 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QVk49Gwf" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726294AbgDDNn5 (ORCPT ); Sat, 4 Apr 2020 09:43:57 -0400 Received: from mail-pl1-f194.google.com ([209.85.214.194]:33320 "EHLO mail-pl1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725837AbgDDNn5 (ORCPT ); Sat, 4 Apr 2020 09:43:57 -0400 Received: by mail-pl1-f194.google.com with SMTP id ay1so3977148plb.0 for ; Sat, 04 Apr 2020 06:43:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=PhA2eJ07XkkhauRMK/W0MqKTbjSH8PZTRSDwpbZ/na4=; b=QVk49GwfedDk9BI0fUmSCMdGchUIXUoB02lay5MzplxDNwuV1sQLz4wdpn6jRsAh+M 4Ad2UxapOI3lkcIVqmSZZyCVc781C1nNnnVx0ckX5qpu02yvTfR26qNvOiFk8ko40q0F J8OvKBptPSpxiN1mjj8pfAQIXKzEwkzZVJmSF5PPljWsW72WGkuOSD2SppHP/liQcMmx dFEzbT4Bm8g1Vx2eXSiO3OD0KrNaeIsJmhcelJ8NJ2VAlbfRGj+JF6FO/FSRtpVl3MxE eSDMnp+Itd8o4SSRx46d3APBZeXhvP6gt3Bmw3VnXItR21nFAVGJCYCL7ZWULjONpEgl Gq/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=PhA2eJ07XkkhauRMK/W0MqKTbjSH8PZTRSDwpbZ/na4=; b=Ssu0Conl9xYIKMtF9lqVAzIoPWgQvwCamr8+SLGyG+oxwVOboaNZox6Phbf5UYYHvF U8pCX2c4bsipB9jHBl9lOqWBKA6fhbVgbeXrLiyw/AfT3MhQS15YQ3dezHEWbSIyWgjz owYXwV3wlaBG9o6EJ1gM5dFxYh6sHIlSQhiLMYnx8Ze+tISEmjW8yqIQPvrpn7rF2ZJ5 pZ8T1dnst90ql/GYWGSD23iRxUcEgrCI9bqymGm+W7p+qFg2NhddAQhaBFHC5WH+XIof snGQb2lzjpm4m2lP0JJXDgAOd8rN32Vgxqmmrw2PqqO+og/j9lFgHJE5tT6dBuf4VvO8 D2Tg== X-Gm-Message-State: AGi0PuZIozfzFlpAiNjOI1Fm4N/n1Msvk19VVUmJEhS/AchwKY2vTslb PyByxaEJkUIKRiraeR3ilqE= X-Google-Smtp-Source: APiQypI9NMXaV84zvSHLWM/zzjaCe5j4XSTSg2qajwuyzezr2g1p3tnL+UjhhbaWcv8H9FQrckxO0g== X-Received: by 2002:a17:902:507:: with SMTP id 7mr12490926plf.42.1586007832971; Sat, 04 Apr 2020 06:43:52 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.52 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:52 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 2/7] receive-pack: add new proc-receive hook Date: Sat, 4 Apr 2020 09:43:40 -0400 Message-Id: <20200404134345.10655-3-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin Git calls an internal `execute_commands` function to handle commands sent from client to `git-receive-pack`. Regardless of what references the user pushes, git creates or updates the corresponding references if the user has write-permission. A contributor who has no write-permission, cannot push to the repository directly. So, the contributor has to write commits to an alternate location, and sends pull request by emails or by other ways. We call this workflow as a distributed workflow. It would be more convenient to work in a centralized workflow like what Gerrit provided for some cases. For example, a read-only user who cannot push to a branch directly can run the following `git push` command to push commits to a pseudo reference (has a prefix "refs/for/", not "refs/heads/") to create a code review. git push origin \ HEAD:refs/for// The `` in the above example can be as simple as "master", or a more complicated branch name like "foo/bar". The `` in the above example command can be the local branch name of the client side, such as "my/topic". We cannot implement a centralized workflow elegantly by using "pre-receive" + "post-receive", because Git will call the internal function "execute_commands" to create references (even the special pseudo reference) between these two hooks. Even though we can delete the temporarily created pseudo reference via the "post-receive" hook, having a temporary reference is not safe for concurrent pushes. So, add a filter and a new handler to support this kind of workflow. The filter will check the prefix of the reference name, and if the command has a special reference name, the filter will turn a specific field (`run_proc_receive`) on for the command. Commands with this filed turned on will be executed by a new handler (an hook named "proc-receive") instead of the internal `execute_commands` function. We can use this "proc-receive" command to create pull requests or send emails for code review. Suggested by Junio, this "proc-receive" hook reads the commands, push-options (optional), and send result using a protocol in pkt-line format. In the following example, The letter "S" stands for "receive-pack" and letter "H" stands for the hook. # Version and capabilities negotiation. S: PKT-LINE(version=1\0push-options atomic...) S: flush-pkt H: PKT-LINE(version=1\0push-options...) H: flush-pkt # Send commands from server to the hook. S: PKT-LINE(old-oid new-oid ref) S: ... ... S: flush-pkt # Only if push-options have been negotiated. S: PKT-LINE(push-option) S: ... ... S: flush-pkt # Receive result from the hook. # OK, run this command successfully. H: PKT-LINE(old-oid new-oid ref ok) # NO, I reject it. H: PKT-LINE(old-oid new-oid ref ng reason) # OK, but use an alternate reference. (in latter commit) H: PKT-LINE(old-oid new-oid ref ok ref:alt-ref) # It will fallthrough to receive-pack to execute. (in latter commit) H: PKT-LINE(old-oid new-oid ref ft) H: ... ... H: flush-pkt After receiving a command, the hook can create/update another alternate reference. For example, a command for a reference "refs/for/master" may create a special reference, such as "refs/pull/123/head". The alternate reference can be returned from the result in an extensible format like " []". The result will be stored in a command list, and "receive-pack" will use the result to replace the commands that have specific `run_proc_receive` field turned on. Suggested-by: Junio C Hamano Signed-off-by: Jiang Xin --- Makefile | 1 + builtin/receive-pack.c | 264 +++++++++++++++- t/helper/test-proc-receive.c | 172 ++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t5411-proc-receive-hook.sh | 592 ++++++++++++++++++++++++++++++++++- 6 files changed, 1017 insertions(+), 14 deletions(-) create mode 100644 t/helper/test-proc-receive.c diff --git a/Makefile b/Makefile index ef1ff2228f..1c52c280ce 100644 --- a/Makefile +++ b/Makefile @@ -725,6 +725,7 @@ TEST_BUILTINS_OBJS += test-parse-pathspec-file.o TEST_BUILTINS_OBJS += test-path-utils.o TEST_BUILTINS_OBJS += test-pkt-line.o TEST_BUILTINS_OBJS += test-prio-queue.o +TEST_BUILTINS_OBJS += test-proc-receive.o TEST_BUILTINS_OBJS += test-progress.o TEST_BUILTINS_OBJS += test-reach.o TEST_BUILTINS_OBJS += test-read-cache.o diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 2cc18bbffd..0760536d58 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -308,11 +308,14 @@ static void write_head_info(void) packet_flush(1); } +#define RUN_PROC_RECEIVE_SCHEDULED 1 +#define RUN_PROC_RECEIVE_RETURNED 2 struct command { struct command *next; const char *error_string; unsigned int skip_update:1, - did_not_exist:1; + did_not_exist:1, + run_proc_receive:2; int index; struct object_id old_oid; struct object_id new_oid; @@ -817,6 +820,234 @@ static int run_update_hook(struct command *cmd) return finish_command(&proc); } +static struct command *find_command_by_refname(const struct command *list, + const char *refname) +{ + for ( ; list; list = list->next) + if (!strcmp(list->ref_name, refname)) + return (struct command *)list; + return NULL; +} + +static int read_proc_receive_result(struct packet_reader *reader, + struct command *commands, + struct strbuf *errmsg) +{ + struct command *hint; + struct command *cmd; + int code = 0; + + hint = NULL; + for (;;) { + struct object_id old_oid, new_oid; + const char *refname; + const char *p; + char *status; + char *msg = NULL; + + if (packet_reader_read(reader) != PACKET_READ_NORMAL) + break; + if (parse_oid_hex(reader->line, &old_oid, &p) || + *p++ != ' ' || + parse_oid_hex(p, &new_oid, &p) || + *p++ != ' ') { + strbuf_addf(errmsg, "proc-receive expected 'old new ref status [msg]', got '%s'", + reader->line); + return -1; + } + + refname = p; + status = strchr(p, ' '); + if (!status) { + strbuf_addf(errmsg, "proc-receive expected 'old new ref status [msg]', got '%s'", + reader->line); + return -1; + } + *status++ = '\0'; + if (strlen(status) > 2 && *(status + 2) == ' ') { + msg = status + 2; + *msg++ = '\0'; + } + if (strlen(status) != 2) { + strbuf_addf(errmsg, "proc-receive has bad status '%s' for '%s'", + status, reader->line); + return -1; + } + + /* first try searching at our hint, falling back to all refs */ + if (hint) + hint = find_command_by_refname(hint, refname); + if (!hint) + hint = find_command_by_refname(commands, refname); + if (!hint) { + strbuf_addf(errmsg, "proc-receive reported status on unknown ref: %s\n", + refname); + continue; + } + if (!hint->run_proc_receive) { + strbuf_addf(errmsg, "proc-receive reported status on ref of builtin command: %s\n", + refname); + continue; + } + hint->run_proc_receive |= RUN_PROC_RECEIVE_RETURNED; + oidcpy(&hint->old_oid, &old_oid); + oidcpy(&hint->new_oid, &new_oid); + if (!strcmp(status, "ng")) { + if (msg) + hint->error_string = xstrdup(msg); + else + hint->error_string = "failed"; + code = -1; + } else if (strcmp("ok", status)) { + strbuf_addf(errmsg, "proc-receive has bad status '%s' for '%s'", + status, reader->line); + return -1; + } + } + + for (cmd = commands; cmd; cmd = cmd->next) + if (cmd->run_proc_receive && + !(cmd->run_proc_receive & RUN_PROC_RECEIVE_RETURNED)) { + cmd->error_string = "no report from proc-receive"; + code = -1; + } + + return code; +} + +static int run_proc_receive_hook(struct command *commands, + const struct string_list *push_options) +{ + struct child_process proc = CHILD_PROCESS_INIT; + struct async muxer; + struct command *cmd; + const char *argv[2]; + struct packet_reader reader; + struct strbuf cap = STRBUF_INIT; + struct strbuf errmsg = STRBUF_INIT; + int pr_use_push_options = 0; + int version = 0; + int code; + + argv[0] = find_hook("proc-receive"); + if (!argv[0]) { + rp_error("cannot find hook 'proc-receive'"); + return -1; + } + argv[1] = NULL; + + proc.argv = argv; + proc.in = -1; + proc.out = -1; + proc.trace2_hook_name = "proc-receive"; + + if (use_sideband) { + memset(&muxer, 0, sizeof(muxer)); + muxer.proc = copy_to_sideband; + muxer.in = -1; + code = start_async(&muxer); + if (code) + return code; + proc.err = muxer.in; + } else { + proc.err = 0; + } + + code = start_command(&proc); + if (code) { + if (use_sideband) + finish_async(&muxer); + return code; + } + + sigchain_push(SIGPIPE, SIG_IGN); + + /* Version negotiaton */ + packet_reader_init(&reader, proc.out, NULL, 0, + PACKET_READ_CHOMP_NEWLINE | + PACKET_READ_DIE_ON_ERR_PACKET); + if (use_atomic) + strbuf_addstr(&cap, " atomic"); + if (use_push_options) + strbuf_addstr(&cap, " push-options"); + if (cap.len) { + packet_write_fmt(proc.in, "version=1%c%s\n", '\0', cap.buf + 1); + strbuf_release(&cap); + } else { + packet_write_fmt(proc.in, "version=1\n"); + } + packet_flush(proc.in); + + for (;;) { + int linelen; + + if (packet_reader_read(&reader) != PACKET_READ_NORMAL) + break; + + if (reader.pktlen > 8 && starts_with(reader.line, "version=")) { + version = atoi(reader.line + 8); + linelen = strlen(reader.line); + if (linelen < reader.pktlen) { + const char *feature_list = reader.line + linelen + 1; + if (parse_feature_request(feature_list, "push-options")) + pr_use_push_options = 1; + } + } + } + + if (version != 1) { + strbuf_addf(&errmsg, "proc-receive version '%d' is not supported", + version); + code = -1; + goto cleanup; + } + + /* Send commands */ + for (cmd = commands; cmd; cmd = cmd->next) { + char *old_hex, *new_hex; + + if (!cmd->run_proc_receive || cmd->skip_update || cmd->error_string) + continue; + old_hex = oid_to_hex(&cmd->old_oid); + new_hex = oid_to_hex(&cmd->new_oid); + packet_write_fmt(proc.in, "%s %s %s", + old_hex, new_hex, cmd->ref_name); + } + packet_flush(proc.in); + + /* Send push options */ + if (pr_use_push_options) { + struct string_list_item *item; + + for_each_string_list_item(item, push_options) + packet_write_fmt(proc.in, "%s", item->string); + packet_flush(proc.in); + } + + /* Read result from proc-receive */ + code = read_proc_receive_result(&reader, commands, &errmsg); + +cleanup: + close(proc.in); + close(proc.out); + if (use_sideband) + finish_async(&muxer); + if (finish_command(&proc)) + code = -1; + if (errmsg.len >0) { + char *p = errmsg.buf; + + p += errmsg.len - 1; + if (*p == '\n') + *p = '\0'; + rp_error("%s", errmsg.buf); + strbuf_release(&errmsg); + } + sigchain_pop(SIGPIPE); + + return code; +} + static char *refuse_unconfigured_deny_msg = N_("By default, updating the current branch in a non-bare repository\n" "is denied, because it will make the index and work tree inconsistent\n" @@ -1392,7 +1623,7 @@ static void execute_commands_non_atomic(struct command *commands, struct strbuf err = STRBUF_INIT; for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) + if (!should_process_cmd(cmd) || cmd->run_proc_receive) continue; transaction = ref_transaction_begin(&err); @@ -1432,7 +1663,7 @@ static void execute_commands_atomic(struct command *commands, } for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) + if (!should_process_cmd(cmd) || cmd->run_proc_receive) continue; cmd->error_string = update(cmd, si); @@ -1468,6 +1699,7 @@ static void execute_commands(struct command *commands, struct iterate_data data; struct async muxer; int err_fd = 0; + int run_proc_receive = 0; if (unpacker_error) { for (cmd = commands; cmd; cmd = cmd->next) @@ -1497,6 +1729,20 @@ static void execute_commands(struct command *commands, reject_updates_to_hidden(commands); + /* Try to find commands that have special prefix in their reference names, + * and mark them to run an external "proc-receive" hook later. + */ + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + /* TODO: replace the fixed prefix by looking up git config variables. */ + if (!strncmp(cmd->ref_name, "refs/for/", 9)) { + cmd->run_proc_receive = RUN_PROC_RECEIVE_SCHEDULED; + run_proc_receive = 1; + } + } + if (run_receive_hook(commands, "pre-receive", 0, push_options)) { for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) @@ -1523,6 +1769,18 @@ static void execute_commands(struct command *commands, free(head_name_to_free); head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL); + if (run_proc_receive) { + int code; + + code = run_proc_receive_hook(commands, push_options); + if (code) { + for (cmd = commands; cmd; cmd = cmd->next) { + if (!cmd->error_string && (cmd->run_proc_receive || use_atomic)) + cmd->error_string = "fail to run proc-receive hook"; + } + } + } + if (use_atomic) execute_commands_atomic(commands, si); else diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c new file mode 100644 index 0000000000..ed30817905 --- /dev/null +++ b/t/helper/test-proc-receive.c @@ -0,0 +1,172 @@ +#include "cache.h" +#include "connect.h" +#include "parse-options.h" +#include "pkt-line.h" +#include "string-list.h" +#include "test-tool.h" + +static const char *proc_receive_usage[] = { + "test-tool proc-receive [...]", + NULL +}; + +static int version = 1; +static int verbose = 0; +static int no_push_options = 0; +static int use_atomic = 0; +static int use_push_options = 0; +static struct string_list returns = STRING_LIST_INIT_NODUP; + +struct command { + struct command *next; + const char *error_string; + unsigned int skip_update:1, + did_not_exist:1; + int index; + struct object_id old_oid; + struct object_id new_oid; + char ref_name[FLEX_ARRAY]; /* more */ +}; + +static void proc_receive_verison(struct packet_reader *reader) { + int server_version = 0; + + for (;;) { + int linelen; + + if (packet_reader_read(reader) != PACKET_READ_NORMAL) + break; + + if (reader->pktlen > 8 && starts_with(reader->line, "version=")) { + server_version = atoi(reader->line+8); + linelen = strlen(reader->line); + if (linelen < reader->pktlen) { + const char *feature_list = reader->line + linelen + 1; + if (parse_feature_request(feature_list, "atomic")) + use_atomic= 1; + if (parse_feature_request(feature_list, "push-options")) + use_push_options = 1; + } + } + } + + if (server_version != 1) + die("bad protocol version: %d", server_version); + + packet_write_fmt(1, "version=%d%c%s\n", + version, '\0', + use_push_options && !no_push_options ? "push-options": ""); + packet_flush(1); +} + +static void proc_receive_read_commands(struct packet_reader *reader, + struct command **commands) +{ + struct command **tail = commands; + + for (;;) { + struct object_id old_oid, new_oid; + struct command *cmd; + const char *refname; + const char *p; + + if (packet_reader_read(reader) != PACKET_READ_NORMAL) { + break; + } + + if (parse_oid_hex(reader->line, &old_oid, &p) || + *p++ != ' ' || + parse_oid_hex(p, &new_oid, &p) || + *p++ != ' ') + die("protocol error: expected 'old new ref', got '%s'", + reader->line); + refname = p; + FLEX_ALLOC_STR(cmd, ref_name, refname); + oidcpy(&cmd->old_oid, &old_oid); + oidcpy(&cmd->new_oid, &new_oid); + + *tail = cmd; + tail = &cmd->next; + } +} + +static void proc_receive_read_push_options(struct packet_reader *reader, + struct string_list *options) +{ + + if (no_push_options || !use_push_options) + return; + + while (1) { + if (packet_reader_read(reader) != PACKET_READ_NORMAL) + break; + + string_list_append(options, reader->line); + } +} + +int cmd__proc_receive(int argc, const char **argv) +{ + struct packet_reader reader; + struct command *commands; + struct string_list push_options = STRING_LIST_INIT_DUP; + struct string_list_item *item; + struct option options[] = { + OPT_BOOL(0, "no-push-options", &no_push_options, + "disable push options"), + OPT_STRING_LIST('r', "return", &returns, "old/new/ref/status/msg", + "return of results"), + OPT__VERBOSE(&verbose, "be verbose"), + OPT_INTEGER('V', "version", &version, + "use this protocol version number"), + OPT_END() + }; + + argc = parse_options(argc, argv, "test-tools", options, proc_receive_usage, 0); + if (argc > 0) + usage_msg_opt("Too many arguments.", proc_receive_usage, options); + + packet_reader_init(&reader, 0, NULL, 0, + PACKET_READ_CHOMP_NEWLINE | + PACKET_READ_DIE_ON_ERR_PACKET); + + proc_receive_verison(&reader); + proc_receive_read_commands(&reader, &commands); + proc_receive_read_push_options(&reader, &push_options); + + if (verbose) { + struct command *cmd; + + if (use_push_options || use_atomic) + fprintf(stderr, "proc-receive:%s%s\n", + use_atomic? " atomic": "", + use_push_options ? " push_options": ""); + + for (cmd = commands; cmd; cmd = cmd->next) { + char *old_hex, *new_hex; + + old_hex = oid_to_hex(&cmd->old_oid); + new_hex = oid_to_hex(&cmd->new_oid); + fprintf(stderr, "proc-receive< %s %s %s\n", + old_hex, new_hex, cmd->ref_name); + } + + if (push_options.nr > 0) { + for_each_string_list_item(item, &push_options) + fprintf(stderr, "proc-receive< %s\n", item->string); + } + + if (returns.nr) { + for_each_string_list_item(item, &returns) + fprintf(stderr, "proc-receive> %s\n", item->string); + } + } + + if (returns.nr) { + for_each_string_list_item(item, &returns) + packet_write_fmt(1, "%s\n", item->string); + } + packet_flush(1); + + return 0; +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 31eedcd241..f865433a61 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -44,6 +44,7 @@ static struct test_cmd cmds[] = { { "path-utils", cmd__path_utils }, { "pkt-line", cmd__pkt_line }, { "prio-queue", cmd__prio_queue }, + { "proc-receive", cmd__proc_receive}, { "progress", cmd__progress }, { "reach", cmd__reach }, { "read-cache", cmd__read_cache }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 4eb5e6609e..059008cf22 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -34,6 +34,7 @@ int cmd__parse_pathspec_file(int argc, const char** argv); int cmd__path_utils(int argc, const char **argv); int cmd__pkt_line(int argc, const char **argv); int cmd__prio_queue(int argc, const char **argv); +int cmd__proc_receive(int argc, const char **argv); int cmd__progress(int argc, const char **argv); int cmd__reach(int argc, const char **argv); int cmd__read_cache(int argc, const char **argv); diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh index 1784bcb584..dbcf9f165c 100755 --- a/t/t5411-proc-receive-hook.sh +++ b/t/t5411-proc-receive-hook.sh @@ -43,8 +43,11 @@ create_commits_in () { # Format the output of git-push, git-show-ref and other commands to make a # user-friendly and stable text. We can easily prepare the expect text # without having to worry about future changes of the commit ID and spaces -# of the output. We also replce single quotes with double quotes, because -# it is boring to prepare unquoted single quotes in expect txt. +# of the output. Single quotes are replaced with double quotes, because +# it is boring to prepare unquoted single quotes in expect txt. We also +# remove some locale error messages, which break test if we turn on +# `GIT_TEST_GETTEXT_POISON=true` in order to test unintentional translations +# on plumbing commands. make_user_friendly_and_stable_output () { sed \ -e "s/ *\$//" \ @@ -54,13 +57,16 @@ make_user_friendly_and_stable_output () { -e "s/$B//g" \ -e "s/$TAG//g" \ -e "s/$ZERO_OID//g" \ - -e "s/[0-9a-f]\{7,\}//g" + -e "s/[0-9a-f]\{7,\}//g" \ + -e "s#To ../upstream.git#To #" \ + -e "/^error: / d" } # Refs of upstream : master(B) next(A) # Refs of workbench: master(A) tags/v123 test_expect_success "setup" ' - git init --bare upstream && + upstream=upstream.git && + git init --bare "$upstream" && git init workbench && create_commits_in workbench A B && ( @@ -68,7 +74,7 @@ test_expect_success "setup" ' # Try to make a stable fixed width for abbreviated commit ID, # this fixed-width oid will be replaced with "". git config core.abbrev 7 && - git remote add origin ../upstream && + git remote add origin ../$upstream && git update-ref refs/heads/master $A && git tag -m "v123" v123 $A && git push origin \ @@ -78,7 +84,7 @@ test_expect_success "setup" ' TAG=$(git -C workbench rev-parse v123) && # setup pre-receive hook - cat >upstream/hooks/pre-receive <<-\EOF && + cat >"$upstream/hooks/pre-receive" <<-\EOF && #!/bin/sh echo >&2 "# pre-receive hook" @@ -90,7 +96,7 @@ test_expect_success "setup" ' EOF # setup post-receive hook - cat >upstream/hooks/post-receive <<-\EOF && + cat >"$upstream/hooks/post-receive" <<-\EOF && #!/bin/sh echo >&2 "# post-receive hook" @@ -102,8 +108,8 @@ test_expect_success "setup" ' EOF chmod a+x \ - upstream/hooks/pre-receive \ - upstream/hooks/post-receive + "$upstream/hooks/pre-receive" \ + "$upstream/hooks/post-receive" ' # Refs of upstream : master(B) next(A) @@ -131,7 +137,7 @@ test_expect_success "normal git-push command" ' remote: post-receive< refs/tags/v123 remote: post-receive< refs/review/master/topic remote: post-receive< refs/heads/a/b/c - To ../upstream + To + ... HEAD -> master (forced update) - [deleted] next * [new tag] v123 -> v123 @@ -139,7 +145,7 @@ test_expect_success "normal git-push command" ' * [new branch] HEAD -> a/b/c EOF test_cmp expect actual && - git -C upstream show-ref >out && + git -C "$upstream" show-ref >out && make_user_friendly_and_stable_output actual && cat >expect <<-EOF && refs/heads/a/b/c @@ -150,4 +156,568 @@ test_expect_success "normal git-push command" ' test_cmp expect actual ' +# Refs of upstream : master(A) tags/v123 refs/review/master/topic(A) a/b/c(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + ( + cd "$upstream" && + git update-ref -d refs/review/master/topic && + git update-ref -d refs/tags/v123 && + git update-ref -d refs/heads/a/b/c + ) +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : next(A) refs/for/master/topic(A) +test_expect_success "no proc-receive hook, fail to push special ref" ' + test_must_fail git -C workbench push origin \ + HEAD:next \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: error: cannot find hook "proc-receive" + remote: # post-receive hook + remote: post-receive< refs/heads/next + To + * [new branch] HEAD -> next + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + git -C "$upstream" update-ref -d refs/heads/next +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push --atomic: next(A) refs/for/master/topic(A) +test_expect_success "no proc-receive hook, fail all for atomic push" ' + test_must_fail git -C workbench push --atomic origin \ + HEAD:next \ + HEAD:refs/for/master/topic >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: error: cannot find hook "proc-receive" + To + ! [remote rejected] HEAD -> next (fail to run proc-receive hook) + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (bad version)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v --version 2 + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic(A) +test_expect_success "proc-receive bad protocol: unknown version" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: fatal: the remote end hung up unexpectedly + remote: error: proc-receive version "2" is not supported + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_i18ncmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (no report)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : next(A) refs/for/master/topic(A) +test_expect_success "proc-receive bad protocol: no report" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/heads/next \ + HEAD:refs/for/master/topic >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: # post-receive hook + remote: post-receive< refs/heads/next + To + * [new branch] HEAD -> next + ! [remote rejected] HEAD -> refs/for/master/topic (no report from proc-receive) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + git -C "$upstream" update-ref -d refs/heads/next + +' + +test_expect_success "setup proc-receive hook (bad oid)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "bad-id new-id ref ok" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: bad oid" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic\ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> bad-id new-id ref ok + remote: error: proc-receive expected "old new ref status [msg]", got "bad-id new-id ref ok" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (no status)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: no status" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic + remote: error: proc-receive expected "old new ref status [msg]", got " refs/for/master/topic" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (unknown status)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic xx msg" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: unknown status" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic xx msg + remote: error: proc-receive has bad status "xx" for " refs/for/master/topic" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (bad status)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic bad status" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: bad status" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic bad status + remote: error: proc-receive has bad status "bad status" for " refs/for/master/topic" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (ng)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic ng" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive: fail to update (no message)" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic ng + To + ! [remote rejected] HEAD -> refs/for/master/topic (failed) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (ng message)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic ng error msg" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive: fail to update (has message)" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic ng error msg + To + ! [remote rejected] HEAD -> refs/for/master/topic (error msg) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (report status on builtin command)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/heads/master ok" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : (B) refs/for/master/topic +test_expect_success "proc-receive: warning on report for builtin command" ' + test_must_fail git -C workbench push origin \ + $B:refs/heads/master \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/heads/master ok + remote: error: proc-receive reported status on ref of builtin command: refs/heads/master + remote: # post-receive hook + remote: post-receive< refs/heads/master + To + .. -> master + ! [remote rejected] HEAD -> refs/for/master/topic (no report from proc-receive) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "cleanup" ' + git -C "$upstream" update-ref refs/heads/master $A +' + +test_expect_success "setup proc-receive hook (ok)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive: ok" ' + git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/for/master/topic + To + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/a/b/c/my/topic +test_expect_success "proc-receive: no report from proc-receive" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/a/b/c/my/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/a/b/c/my/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/a/b/c/my/topic + remote: proc-receive> refs/for/master/topic ok + remote: error: proc-receive reported status on unknown ref: refs/for/master/topic + To + ! [remote rejected] HEAD -> refs/for/a/b/c/my/topic (no report from proc-receive) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push -o ... : refs/for/master/topic +test_expect_success "not support push options" ' + test_must_fail git -C workbench push \ + -o issue=123 \ + -o reviewer=user1 \ + origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + fatal: the receiving end does not support push options + fatal: the remote end hung up unexpectedly + EOF + test_i18ncmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "enable push options" ' + git -C "$upstream" config receive.advertisePushOptions true +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push -o ... : next(A) refs/for/master/topic +test_expect_success "push with options" ' + git -C workbench push \ + --atomic \ + -o issue=123 \ + -o reviewer=user1 \ + origin \ + HEAD:refs/heads/next \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive: atomic push_options + remote: proc-receive< refs/for/master/topic + remote: proc-receive< issue=123 + remote: proc-receive< reviewer=user1 + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/heads/next + remote: post-receive< refs/for/master/topic + To + * [new branch] HEAD -> next + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + test_done From patchwork Sat Apr 4 13:43:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474283 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 370E5912 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 15CF7206C3 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QwMWEQgk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726278AbgDDNnz (ORCPT ); Sat, 4 Apr 2020 09:43:55 -0400 Received: from mail-pg1-f193.google.com ([209.85.215.193]:40601 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726230AbgDDNnz (ORCPT ); Sat, 4 Apr 2020 09:43:55 -0400 Received: by mail-pg1-f193.google.com with SMTP id t24so5062211pgj.7 for ; Sat, 04 Apr 2020 06:43:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=k0TTErNib/oVDKYqMrFH1TE4YX/C72t6kSBmdz23lsc=; b=QwMWEQgku+5FnqFnMP0IMZeHoLG0Iu2KR4DVe5bjLEgFVDSuvp4sO1YfGb/CltC4UX pBk3nal9uAPj7kq6yhjnU4gCAm1pb3LULpPTnR4PadkFLeSuD/UqLKcDIHxvNRo/pwd/ CJLDDfwbxSG61wiy4KlPlNzhX+tVZtSzOAckn9WrLOl5Hl0VyMLZ8zx2/zR0XRqjSKtK l0z3lYF9pLJUgejNY4GyV1YV8Dcz+C0khwgRNKP9MGTQYgj3yPB+GQ8LRwNZIHEgzuIQ 3FBovoy9vF6yDzjiAQPMjGZZA6GiY0nDGbjeYujYIzyVzQa0BueCR3pkEiaTk4ZLyJ6a MYLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=k0TTErNib/oVDKYqMrFH1TE4YX/C72t6kSBmdz23lsc=; b=ApxAPCkPphQJKn4knYCPm2qqNRZdUh1+R0syhW0yqHVmjqO28GF5CDAba79KiVDQgI i9upZ6t6mC1cnaT4yUyGvRaCw2N+UMxmlt4FMy2H+PhiU/Df3i/ytdTOhsTnS1OyIZi9 gb51sKJ0d0muHkmSD5/0G/v+fAXZ/VstjUa4Lu5IUsLWmbv+ug3ygv1mVDJAKzCxr3gs MQmYd7xjHKC2o8DDfuZ+SHqpxqEw/I61DY82O+bRg85lBl4qoaIo6N6cfPQmSftLutvX WoGfVL0aMOX+y/0xYjORhQd3skVtsoE3E0KYmg6GPLes+sMwGMTE/eI7qW3X2LbF9KXX a00w== X-Gm-Message-State: AGi0PubK7rSupLp+nvgRN5OceX93s3Y9AYb8D/344A3qo0Klddwd7LaI 9LZOenBuE9dYYdj9PI9tO9IojmUU X-Google-Smtp-Source: APiQypKwH9hY2c6jUTyk80OAJ8JDxUiRhO2MX4R0j6xetkuZ9xZ52+flNszpmn4jtuEx6sWEBRDPyw== X-Received: by 2002:a65:5881:: with SMTP id d1mr12803329pgu.378.1586007833683; Sat, 04 Apr 2020 06:43:53 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:53 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 3/7] refs.c: refactor to reuse ref_is_hidden() Date: Sat, 4 Apr 2020 09:43:41 -0400 Message-Id: <20200404134345.10655-4-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin Add new function `ref_is_matched()` to reuse `ref_is_hidden()`. Will use this function for `receive-pack` to check commands with specific prefixes. Test case t5512 covered this change. Signed-off-by: Jiang Xin --- refs.c | 11 ++++++++--- refs.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 1ab0bb54d3..229159ea1a 100644 --- a/refs.c +++ b/refs.c @@ -1389,13 +1389,18 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti } int ref_is_hidden(const char *refname, const char *refname_full) +{ + return ref_is_matched(hide_refs, refname, refname_full); +} + +int ref_is_matched(struct string_list *match_refs, const char *refname, const char *refname_full) { int i; - if (!hide_refs) + if (!match_refs) return 0; - for (i = hide_refs->nr - 1; i >= 0; i--) { - const char *match = hide_refs->items[i].string; + for (i = match_refs->nr - 1; i >= 0; i--) { + const char *match = match_refs->items[i].string; const char *subject; int neg = 0; const char *p; diff --git a/refs.h b/refs.h index 545029c6d8..a2ea043f7f 100644 --- a/refs.h +++ b/refs.h @@ -739,6 +739,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *); * parameter always points to the full ref name. */ int ref_is_hidden(const char *, const char *); +int ref_is_matched(struct string_list *, const char *, const char *); enum ref_type { REF_TYPE_PER_WORKTREE, /* refs inside refs/ but not shared */ From patchwork Sat Apr 4 13:43:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474285 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 624DC1892 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 40A89206C3 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="i3QJmfE3" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726283AbgDDNn4 (ORCPT ); Sat, 4 Apr 2020 09:43:56 -0400 Received: from mail-pg1-f177.google.com ([209.85.215.177]:45852 "EHLO mail-pg1-f177.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725870AbgDDNn4 (ORCPT ); Sat, 4 Apr 2020 09:43:56 -0400 Received: by mail-pg1-f177.google.com with SMTP id o26so5049419pgc.12 for ; Sat, 04 Apr 2020 06:43:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=n32idmwM5lC1hpS9HwTmJTU5cnQFTjaTuEXB1ao6kEA=; b=i3QJmfE3HmTOtbet8fPiuz98l4Ep5db7353pV997eZZrCYlUkTLVXYgBWz3WqIzb8X RbSfghNUTyu0WNDVx1NbirZXfU3aC3zJpXoIhqIuP2LKnmwU8kDzaDrFf/b3Oqf//EzV 0ddvS27CCQ6jgc9zoFhop3/ptn6AuNPiwIdbfzthIWhmz0G555eOS1FqnhT2/qI/xd1P wQV8wBW6cTubUzkU0AgFDWvZnbIuQkhVCx3t4hghsxXEsKKDefD2LfbmUZ6XCZ09QBj7 wTUGTjYotpoyN4e0ryBB19t4+Bowl00q5vVBJd0GAbaiTbVVzt0TJVxmixZOTrpf7mvs Tf1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=n32idmwM5lC1hpS9HwTmJTU5cnQFTjaTuEXB1ao6kEA=; b=n1rgk4rn00njEPRcAq+XLPbkReyznjd9BvcG3kA4q5LQ0xUUEJyCNdhA81dyOTT2nT wRbhFNDXU+pf0etomq8/u6BZ9a8RczNR7cwif6KfpYbnvJMOm2UtEleyZ9WkskHyt/li QXJmZMo2Jj83bEAgiHSBP8XtyTNimac6Dr6CzpSQ9qBRLYpA7uQq7/p2wd5wYts4S7DX mUlKy4TtcFrrdk/bw0IeQYg/66R4TlgHwuqJBNONqGb4AyrwYHzVvw3RMoevZ8xMYfOj orVDGzB8YCwg7/Pn0HTIsoJ2R7T6zg0pZGmHBul+0P+vFDKI9dpr53h6qTSXIRPTlYcX 9ubA== X-Gm-Message-State: AGi0PuZ507RrCxca5MxVCaXa6SP54y1SqDb7EViZsXg3JjC2ctTx86Mc mg9m7Y23i1Y6VmvfMYYz4zCI3pW2 X-Google-Smtp-Source: APiQypKsylaI2QpEjp+4/jpzeDAJNnO9vdkS9RKc9DIpeAW2r79H/Fl6WGLEeuNTpgUPfNANxfh7vA== X-Received: by 2002:a63:d351:: with SMTP id u17mr12688186pgi.396.1586007834526; Sat, 04 Apr 2020 06:43:54 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:54 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 4/7] receive-pack: new config receive.procReceiveRefs Date: Sat, 4 Apr 2020 09:43:42 -0400 Message-Id: <20200404134345.10655-5-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin Add a new multi-valued config variable "receive.procReceiveRefs" for `receive-pack` command, like the follows: git config --system --add receive.procReceiveRefs refs/for/ git config --system --add receive.procReceiveRefs refs/drafts/ If the specific prefix strings match the reference names of the commands which are sent by git client to `receive-pack`, these commands will be executed by an external hook (named "proc-receive"), instead of the internal `execute_commands` function. For example, if it is set to "refs/for/", pushing to a reference such as "refs/for/master" will not create or update reference "refs/for/master", but may create or update a pull request directly by running the external hook. Signed-off-by: Jiang Xin --- Documentation/config/receive.txt | 14 ++++ builtin/receive-pack.c | 43 ++++++++-- t/t5411-proc-receive-hook.sh | 130 +++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 7 deletions(-) diff --git a/Documentation/config/receive.txt b/Documentation/config/receive.txt index 65f78aac37..0178f2d478 100644 --- a/Documentation/config/receive.txt +++ b/Documentation/config/receive.txt @@ -114,6 +114,20 @@ receive.hideRefs:: An attempt to update or delete a hidden ref by `git push` is rejected. +receive.procReceiveRefs:: + This is a multi-valued variable that defines reference prefixes + to match the commands in `receive-pack`. Commands matching the + prefixes will be executed by an external hooks "proc-receive", + instead of the internal `execute_commands` function. If this + variable is not defined, the "proc-receive" hook will never be + used, and all commands will be executed by the internal + `execute_commands` function. + + For example, if this variable is set to "refs/for/", pushing to + reference such as "refs/for/master" will not create or update a + reference named "refs/for/master", but may create or update a + pull request directly by running an external hook. + receive.updateServerInfo:: If set to true, git-receive-pack will run git-update-server-info after receiving data from git-push and updating refs. diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 0760536d58..26e561d5b8 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -76,6 +76,7 @@ static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; static const char *cert_nonce_seed; +static struct string_list proc_receive_refs; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; static const char *NONCE_BAD = "BAD"; @@ -228,6 +229,20 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.procreceiverefs") == 0) { + char *prefix; + int len; + + if (!value) + return config_error_nonbool(var); + prefix = xstrdup(value); + len = strlen(prefix); + while (len && prefix[len - 1] == '/') + prefix[--len] = '\0'; + string_list_insert(&proc_receive_refs, prefix); + return 0; + } + return git_default_config(var, value, cb); } @@ -1732,15 +1747,26 @@ static void execute_commands(struct command *commands, /* Try to find commands that have special prefix in their reference names, * and mark them to run an external "proc-receive" hook later. */ - for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) - continue; + if (proc_receive_refs.nr > 0) { + struct strbuf refname_full = STRBUF_INIT; + size_t prefix_len; + + strbuf_addstr(&refname_full, get_git_namespace()); + prefix_len = refname_full.len; - /* TODO: replace the fixed prefix by looking up git config variables. */ - if (!strncmp(cmd->ref_name, "refs/for/", 9)) { - cmd->run_proc_receive = RUN_PROC_RECEIVE_SCHEDULED; - run_proc_receive = 1; + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd)) + continue; + + strbuf_setlen(&refname_full, prefix_len); + strbuf_addstr(&refname_full, cmd->ref_name); + if (ref_is_matched(&proc_receive_refs, cmd->ref_name, refname_full.buf)) { + cmd->run_proc_receive = RUN_PROC_RECEIVE_SCHEDULED; + run_proc_receive = 1; + } } + + strbuf_release(&refname_full); } if (run_receive_hook(commands, "pre-receive", 0, push_options)) { @@ -2200,6 +2226,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) OPT_END() }; + string_list_init(&proc_receive_refs, 0); + packet_trace_identity("receive-pack"); argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0); @@ -2315,5 +2343,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) oid_array_clear(&shallow); oid_array_clear(&ref); free((void *)push_cert_nonce); + string_list_clear(&proc_receive_refs, 0); return 0; } diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh index dbcf9f165c..b04a7ec814 100755 --- a/t/t5411-proc-receive-hook.sh +++ b/t/t5411-proc-receive-hook.sh @@ -167,6 +167,14 @@ test_expect_success "cleanup" ' ) ' +test_expect_success "add two receive.procReceiveRefs settings" ' + ( + cd "$upstream" && + git config --add receive.procReceiveRefs refs/for && + git config --add receive.procReceiveRefs refs/review/ + ) +' + # Refs of upstream : master(A) # Refs of workbench: master(A) tags/v123 # git push : next(A) refs/for/master/topic(A) @@ -720,4 +728,126 @@ test_expect_success "push with options" ' test_cmp expect actual ' +# Refs of upstream : master(A) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + git -C "$upstream" update-ref -d refs/heads/next +' + +test_expect_success "setup proc-receive hook" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/review/a/b/c/topic ok" \ + -r "$ZERO_OID $A refs/for/next/topic ok" \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/next/topic(A) refs/review/a/b/c/topic(A) refs/for/master/topic(A) +test_expect_success "report update of all special refs" ' + git -C workbench push origin \ + HEAD:refs/for/next/topic \ + HEAD:refs/review/a/b/c/topic \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/next/topic + remote: pre-receive< refs/review/a/b/c/topic + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/next/topic + remote: proc-receive< refs/review/a/b/c/topic + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/review/a/b/c/topic ok + remote: proc-receive> refs/for/next/topic ok + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/for/next/topic + remote: post-receive< refs/review/a/b/c/topic + remote: post-receive< refs/for/master/topic + To + * [new reference] HEAD -> refs/for/next/topic + * [new reference] HEAD -> refs/review/a/b/c/topic + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/next/topic ok" \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : bar(A) baz(A) refs/for/next/topic(A) foo(A) refs/for/master/topic(A) +test_expect_success "report mixed refs update" ' + git -C workbench push origin \ + HEAD:refs/heads/bar \ + HEAD:refs/heads/baz \ + HEAD:refs/for/next/topic \ + HEAD:refs/heads/foo \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/bar + remote: pre-receive< refs/heads/baz + remote: pre-receive< refs/for/next/topic + remote: pre-receive< refs/heads/foo + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/next/topic + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/next/topic ok + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/heads/bar + remote: post-receive< refs/heads/baz + remote: post-receive< refs/for/next/topic + remote: post-receive< refs/heads/foo + remote: post-receive< refs/for/master/topic + To + * [new branch] HEAD -> bar + * [new branch] HEAD -> baz + * [new reference] HEAD -> refs/for/next/topic + * [new branch] HEAD -> foo + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/bar + refs/heads/baz + refs/heads/foo + refs/heads/master + EOF + test_cmp expect actual +' + test_done From patchwork Sat Apr 4 13:43:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474289 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CCA641984 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AB11D206C3 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ETfiB8RK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726307AbgDDNn6 (ORCPT ); Sat, 4 Apr 2020 09:43:58 -0400 Received: from mail-pg1-f176.google.com ([209.85.215.176]:36635 "EHLO mail-pg1-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725943AbgDDNn5 (ORCPT ); Sat, 4 Apr 2020 09:43:57 -0400 Received: by mail-pg1-f176.google.com with SMTP id c23so5074592pgj.3 for ; Sat, 04 Apr 2020 06:43:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=+UyFFw5IWy4XfDyi6MUkXAIfAMJJbIw6k2aUeGCpPlE=; b=ETfiB8RK0P2PI2kdHNgIigKTco7DuEuygeHXnSaeXxUI/QEDug37XOb6MEoxbdiRIh 8DqDFxyQkl/ZTKnA3k7O5XSXGUqquWFjI/riLm6IKZ7RU5eRJWy0I4yyHCs+ej2qI6Y7 FvnKMmm/YhJs/Q/G4Cw6WMGUFzs+N27VfSMBkujTL+5XB2v8VAX0+A5IZDpePP9IPRWP gbhQtopd2saF9E713YmrTpaCKX4WKHz971FKEFhqWKvfUZhAK+Ma300uzTevxpB7CwNw YRywhl1G8v68B8V14Zdn8FiBli1wCZiWRzXukeZsgpjJTjs/Rfm9SCNJb5qxWqQvHX2G hkyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+UyFFw5IWy4XfDyi6MUkXAIfAMJJbIw6k2aUeGCpPlE=; b=q65ElSxJPkwDlK6tR//mgYeU6kE4D47XjSCmiV8ulnbak53TGmHj5UCHDc0Z9h+39t bV0UFq0a9G/Nx8GhFra/u0k30SYM+bPfDAhzcCT1S5X5TXyFJRI4mqhho7o068fC4+N9 KWju5XXq78aW7gT25UwFmEPrVMZC2XWb6hCv+6u9XntEHzJz1I9oEWVgIvlZA70lcucZ 6rrskHPsg2BAvc/bN2imryeEeJwp6xpxZRyYwqHwW/WnX+zimLFKV1UqIuADhg9vXAS5 jbWj41QRbP2xvT+jrzosmSl41gvXAz1Ei2vIwCNM0J2jU+OQYfrnXCw0EiGML3oOpMwm yEVg== X-Gm-Message-State: AGi0PuYB36/RuEWU98nXvPW91+MYBNIwHvY1e0wMMGORRR87DpIPvPbn 3eB1uBXSpj3Dpgl6eR+faB4= X-Google-Smtp-Source: APiQypKuwMjJH9p2hFxRa6OXk1U1sVz9XbvDVkSA/VJGgwmzIwZGM4GqcXO3ZhvQcuCpU3QI8VnmmQ== X-Received: by 2002:a63:784f:: with SMTP id t76mr13505070pgc.204.1586007835480; Sat, 04 Apr 2020 06:43:55 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:54 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 5/7] receive-pack: refactor report for proc-receive Date: Sat, 4 Apr 2020 09:43:43 -0400 Message-Id: <20200404134345.10655-6-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin The "proc-receive" may update one or more references, and will send its result one by one in pkt-line format. Each line of the result has four fields and one optional message field, as " []". See the following example: # OK, run this command successfully. PKT-LINE(old-oid new-oid ref ok) # NO, I reject it. PKT-LINE(old-oid new-oid ref ng reason) # OK, but use an alternate reference. PKT-LINE(old-oid new-oid ref ok ref:alt-ref) # It will fallthrough to receive-pack to execute. PKT-LINE(old-oid new-oid ref ft) The first three fields have the same foramt as a command. The forth field has a two-letter status code. Available status code: * ok: The command runs successfully. If the optional message has a prefix "ref:", the hook has created/updated an alternate reference instead. * ng: Fail to run the command. Error message is in the optional message field. * ft: Will fallthrough to receive-pack to execute. Suggested-by: Junio C Hamano Signed-off-by: Jiang Xin --- builtin/receive-pack.c | 21 +++++++-- t/t5411-proc-receive-hook.sh | 90 +++++++++++++++++++++++++++++++++--- transport-helper.c | 64 ++++++++++++------------- transport.c | 59 +++++++++++++---------- 4 files changed, 168 insertions(+), 66 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 26e561d5b8..3843ae0fe9 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -328,6 +328,7 @@ static void write_head_info(void) struct command { struct command *next; const char *error_string; + const char *extra_string; unsigned int skip_update:1, did_not_exist:1, run_proc_receive:2; @@ -913,7 +914,12 @@ static int read_proc_receive_result(struct packet_reader *reader, else hint->error_string = "failed"; code = -1; - } else if (strcmp("ok", status)) { + } else if (!strcmp("ok", status)) { + hint->extra_string = xstrdup_or_null(msg); + } else if (!strcmp("ft", status)) { + /* Reset "run_proc_receive" field, and continue to run in "receive-pack" */ + hint->run_proc_receive = 0; + } else { strbuf_addf(errmsg, "proc-receive has bad status '%s' for '%s'", status, reader->line); return -1; @@ -2183,12 +2189,17 @@ static void report(struct command *commands, const char *unpack_status) packet_buf_write(&buf, "unpack %s\n", unpack_status ? unpack_status : "ok"); for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - packet_buf_write(&buf, "ok %s\n", - cmd->ref_name); - else + if (!cmd->error_string) { + if (!cmd->extra_string) + packet_buf_write(&buf, "ok %s\n", + cmd->ref_name); + else + packet_buf_write(&buf, "ok %s%c%s\n", + cmd->ref_name, ' ', cmd->extra_string); + } else { packet_buf_write(&buf, "ng %s %s\n", cmd->ref_name, cmd->error_string); + } } packet_buf_flush(&buf); diff --git a/t/t5411-proc-receive-hook.sh b/t/t5411-proc-receive-hook.sh index b04a7ec814..6104641759 100755 --- a/t/t5411-proc-receive-hook.sh +++ b/t/t5411-proc-receive-hook.sh @@ -742,8 +742,8 @@ test_expect_success "setup proc-receive hook" ' test-tool proc-receive -v \ -r "$ZERO_OID $A refs/review/a/b/c/topic ok" \ - -r "$ZERO_OID $A refs/for/next/topic ok" \ - -r "$ZERO_OID $A refs/for/master/topic ok" + -r "$ZERO_OID $A refs/for/next/topic ok ref:refs/pull/123/head" \ + -r "$ZERO_OID $A refs/for/master/topic ok ref:refs/pull/124/head" EOF chmod a+x "$upstream/hooks/proc-receive" ' @@ -768,16 +768,16 @@ test_expect_success "report update of all special refs" ' remote: proc-receive< refs/review/a/b/c/topic remote: proc-receive< refs/for/master/topic remote: proc-receive> refs/review/a/b/c/topic ok - remote: proc-receive> refs/for/next/topic ok - remote: proc-receive> refs/for/master/topic ok + remote: proc-receive> refs/for/next/topic ok ref:refs/pull/123/head + remote: proc-receive> refs/for/master/topic ok ref:refs/pull/124/head remote: # post-receive hook remote: post-receive< refs/for/next/topic remote: post-receive< refs/review/a/b/c/topic remote: post-receive< refs/for/master/topic To - * [new reference] HEAD -> refs/for/next/topic + * [new reference] HEAD -> refs/pull/123/head * [new reference] HEAD -> refs/review/a/b/c/topic - * [new reference] HEAD -> refs/for/master/topic + * [new reference] HEAD -> refs/pull/124/head EOF test_cmp expect actual && git -C "$upstream" show-ref >out && @@ -806,6 +806,7 @@ test_expect_success "setup proc-receive hook" ' # git push : bar(A) baz(A) refs/for/next/topic(A) foo(A) refs/for/master/topic(A) test_expect_success "report mixed refs update" ' git -C workbench push origin \ + $B:refs/heads/master \ HEAD:refs/heads/bar \ HEAD:refs/heads/baz \ HEAD:refs/for/next/topic \ @@ -815,6 +816,7 @@ test_expect_success "report mixed refs update" ' make_user_friendly_and_stable_output actual && cat >expect <<-EOF && remote: # pre-receive hook + remote: pre-receive< refs/heads/master remote: pre-receive< refs/heads/bar remote: pre-receive< refs/heads/baz remote: pre-receive< refs/for/next/topic @@ -826,12 +828,14 @@ test_expect_success "report mixed refs update" ' remote: proc-receive> refs/for/next/topic ok remote: proc-receive> refs/for/master/topic ok remote: # post-receive hook + remote: post-receive< refs/heads/master remote: post-receive< refs/heads/bar remote: post-receive< refs/heads/baz remote: post-receive< refs/for/next/topic remote: post-receive< refs/heads/foo remote: post-receive< refs/for/master/topic To + .. -> master * [new branch] HEAD -> bar * [new branch] HEAD -> baz * [new reference] HEAD -> refs/for/next/topic @@ -845,6 +849,80 @@ test_expect_success "report mixed refs update" ' refs/heads/bar refs/heads/baz refs/heads/foo + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "config receive.procReceiveRefs for all ref/" ' + git -C "$upstream" config --add receive.procReceiveRefs refs/ +' + +test_expect_success "setup proc-receive hook" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$B $A refs/heads/master ft" \ + -r "$A $ZERO_OID refs/heads/foo ft" \ + -r "$A $B refs/heads/bar ft" \ + -r "$A $B refs/for/master/topic ok ref:refs/pull/123/head" \ + -r "$B $A refs/for/next/topic ok ref:refs/pull/124/head" + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(B) foo(A) bar(A)) baz(A) +# Refs of workbench: master(A) tags/v123 +# git push -f : (NULL) (B) refs/for/master/topic(A) refs/for/next/topic(A) +test_expect_success "report test: fallthrough" ' + git -C workbench push -f origin \ + HEAD:refs/heads/master \ + :refs/heads/foo \ + $B:refs/heads/bar \ + HEAD:refs/for/master/topic \ + HEAD:refs/for/next/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/bar + remote: pre-receive< refs/heads/foo + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/for/master/topic + remote: pre-receive< refs/for/next/topic + remote: # proc-receive hook + remote: proc-receive< refs/heads/bar + remote: proc-receive< refs/heads/foo + remote: proc-receive< refs/heads/master + remote: proc-receive< refs/for/master/topic + remote: proc-receive< refs/for/next/topic + remote: proc-receive> refs/heads/master ft + remote: proc-receive> refs/heads/foo ft + remote: proc-receive> refs/heads/bar ft + remote: proc-receive> refs/for/master/topic ok ref:refs/pull/123/head + remote: proc-receive> refs/for/next/topic ok ref:refs/pull/124/head + remote: # post-receive hook + remote: post-receive< refs/heads/bar + remote: post-receive< refs/heads/foo + remote: post-receive< refs/heads/master + remote: post-receive< refs/for/master/topic + remote: post-receive< refs/for/next/topic + To + .. -> bar + - [deleted] foo + + ... HEAD -> master (forced update) + * [new reference] HEAD -> refs/pull/123/head + * [new reference] HEAD -> refs/pull/124/head + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/bar + refs/heads/baz refs/heads/master EOF test_cmp expect actual diff --git a/transport-helper.c b/transport-helper.c index a46afcb69d..0c835c5495 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -747,37 +747,39 @@ static int push_update_ref_status(struct strbuf *buf, msg = xstrdup(msg); strbuf_release(&msg_buf); - if (!strcmp(msg, "no match")) { - status = REF_STATUS_NONE; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "up to date")) { - status = REF_STATUS_UPTODATE; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "non-fast forward")) { - status = REF_STATUS_REJECT_NONFASTFORWARD; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "already exists")) { - status = REF_STATUS_REJECT_ALREADY_EXISTS; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "fetch first")) { - status = REF_STATUS_REJECT_FETCH_FIRST; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "needs force")) { - status = REF_STATUS_REJECT_NEEDS_FORCE; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "stale info")) { - status = REF_STATUS_REJECT_STALE; - FREE_AND_NULL(msg); - } - else if (!strcmp(msg, "forced update")) { - forced = 1; - FREE_AND_NULL(msg); + if (status != REF_STATUS_OK) { + if (!strcmp(msg, "no match")) { + status = REF_STATUS_NONE; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "up to date")) { + status = REF_STATUS_UPTODATE; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "non-fast forward")) { + status = REF_STATUS_REJECT_NONFASTFORWARD; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "already exists")) { + status = REF_STATUS_REJECT_ALREADY_EXISTS; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "fetch first")) { + status = REF_STATUS_REJECT_FETCH_FIRST; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "needs force")) { + status = REF_STATUS_REJECT_NEEDS_FORCE; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "stale info")) { + status = REF_STATUS_REJECT_STALE; + FREE_AND_NULL(msg); + } + else if (!strcmp(msg, "forced update")) { + forced = 1; + FREE_AND_NULL(msg); + } } } diff --git a/transport.c b/transport.c index 4f91c8ea17..ee9b2a0d8e 100644 --- a/transport.c +++ b/transport.c @@ -459,15 +459,18 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v } } -static void print_ref_status(char flag, const char *summary, +static void print_ref_status(char flag, const char *summary, char *target_refname, struct ref *to, struct ref *from, const char *msg, int porcelain, int summary_width) { + if (!target_refname) + target_refname = to->name; + if (porcelain) { if (from) - fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to->name); + fprintf(stdout, "%c\t%s:%s\t", flag, from->name, target_refname); else - fprintf(stdout, "%c\t:%s\t", flag, to->name); + fprintf(stdout, "%c\t:%s\t", flag, target_refname); if (msg) fprintf(stdout, "%s (%s)\n", summary, msg); else @@ -481,9 +484,9 @@ static void print_ref_status(char flag, const char *summary, fprintf(stderr, " %s%c %-*s%s ", red, flag, summary_width, summary, reset); if (from) - fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name)); + fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(target_refname)); else - fputs(prettify_refname(to->name), stderr); + fputs(prettify_refname(target_refname), stderr); if (msg) { fputs(" (", stderr); fputs(msg, stderr); @@ -495,18 +498,26 @@ static void print_ref_status(char flag, const char *summary, static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_width) { + char *refname; + + if (ref->remote_status && !strncmp(ref->remote_status, "ref:", 4)) + refname = ref->remote_status + 4; + else + refname = ref->name; + if (ref->deletion) - print_ref_status('-', "[deleted]", ref, NULL, NULL, + print_ref_status('-', "[deleted]", refname, ref, NULL, NULL, porcelain, summary_width); - else if (is_null_oid(&ref->old_oid)) + else if (is_null_oid(&ref->old_oid)) { + print_ref_status('*', - (starts_with(ref->name, "refs/tags/") + (starts_with(refname, "refs/tags/") ? "[new tag]" - : (starts_with(ref->name, "refs/heads/") + : (starts_with(refname, "refs/heads/") ? "[new branch]" : "[new reference]")), - ref, ref->peer_ref, NULL, porcelain, summary_width); - else { + refname, ref, ref->peer_ref, NULL, porcelain, summary_width); + } else { struct strbuf quickref = STRBUF_INIT; char type; const char *msg; @@ -525,7 +536,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_widt strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); - print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, + print_ref_status(type, quickref.buf, refname, ref, ref->peer_ref, msg, porcelain, summary_width); strbuf_release(&quickref); } @@ -542,56 +553,56 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, switch(ref->status) { case REF_STATUS_NONE: - print_ref_status('X', "[no match]", ref, NULL, NULL, + print_ref_status('X', "[no match]", NULL, ref, NULL, NULL, porcelain, summary_width); break; case REF_STATUS_REJECT_NODELETE: - print_ref_status('!', "[rejected]", ref, NULL, + print_ref_status('!', "[rejected]", NULL, ref, NULL, "remote does not support deleting refs", porcelain, summary_width); break; case REF_STATUS_UPTODATE: - print_ref_status('=', "[up to date]", ref, + print_ref_status('=', "[up to date]", NULL, ref, ref->peer_ref, NULL, porcelain, summary_width); break; case REF_STATUS_REJECT_NONFASTFORWARD: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "non-fast-forward", porcelain, summary_width); break; case REF_STATUS_REJECT_ALREADY_EXISTS: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "already exists", porcelain, summary_width); break; case REF_STATUS_REJECT_FETCH_FIRST: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "fetch first", porcelain, summary_width); break; case REF_STATUS_REJECT_NEEDS_FORCE: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "needs force", porcelain, summary_width); break; case REF_STATUS_REJECT_STALE: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "stale info", porcelain, summary_width); break; case REF_STATUS_REJECT_SHALLOW: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "new shallow roots not allowed", porcelain, summary_width); break; case REF_STATUS_REMOTE_REJECT: - print_ref_status('!', "[remote rejected]", ref, + print_ref_status('!', "[remote rejected]", NULL, ref, ref->deletion ? NULL : ref->peer_ref, ref->remote_status, porcelain, summary_width); break; case REF_STATUS_EXPECTING_REPORT: - print_ref_status('!', "[remote failure]", ref, + print_ref_status('!', "[remote failure]", NULL, ref, ref->deletion ? NULL : ref->peer_ref, "remote failed to report status", porcelain, summary_width); break; case REF_STATUS_ATOMIC_PUSH_FAILED: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, + print_ref_status('!', "[rejected]", NULL, ref, ref->peer_ref, "atomic push failed", porcelain, summary_width); break; case REF_STATUS_OK: From patchwork Sat Apr 4 13:43:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474293 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 386891892 for ; Sat, 4 Apr 2020 13:44:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0D0CD206C3 for ; Sat, 4 Apr 2020 13:44:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cSF572y2" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726327AbgDDNoA (ORCPT ); Sat, 4 Apr 2020 09:44:00 -0400 Received: from mail-pj1-f66.google.com ([209.85.216.66]:38246 "EHLO mail-pj1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725870AbgDDNn6 (ORCPT ); Sat, 4 Apr 2020 09:43:58 -0400 Received: by mail-pj1-f66.google.com with SMTP id m15so4331925pje.3 for ; Sat, 04 Apr 2020 06:43:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=DLeRjq6px1tQjCGK5wutri/M8dBOeZR9U+PhHmNP3vI=; b=cSF572y2SExrMPOjZJNlcKe5Ix3dttK6koI6qPglnclY0zCO3it+mcDo//SkUnkvxH Eci7dPUDXaGXzNbG3Ay7wxfa3XpGXMK5CGao9dxaEmqPLOX7l5uqN2gaG6YAFYatNz2O 8P90tUbDbRnluHALjuRc2XwDVLUP7f8HU+ZIgCDFcsSXoj8xuPDdlb0TJPlMkovCPAQ9 A37d0d1gd7WI9CC5ylFNFH3EOguNhXP3zpXne3wBdzvEXjvgl2Whu3dAieCwks0xkPSb avFlZmJlQMrK17y4goQGjuqDPXNFV5bjZXovIkQrnKhvM+CNqdGO/UiZPwmt+TjjbO8T g8Kw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=DLeRjq6px1tQjCGK5wutri/M8dBOeZR9U+PhHmNP3vI=; b=lEoPyzfL0OcyJE8uoC2xkQLCtu2ltnNavjIbj9zdKRAlbdiZQ7m/qurLPHDAENNMKv pLI+cDbsuHsljitBMlZn/RfyhhG00QGITvY+HMtUSLFDMfzSx1WS5vULTYsqrKLagTwO 09xIrqVCVcy68XvISjB9UJv82sb+njAGqPNHdL9m32Us2ytnafk3hyEYRG3OPu02l+Lg TicmOEHKYKcHkui+ZOI3/Jwd8kLAHDbd8LEwv8Gta9MJaZ0EPQs8RUhLb79hHYcUeelc ZUfppRaIZc3Mhw0P+TnC3LfUj6zcHoVf6fd6/+AsCYE0iakbtALZHofa+X7rGdTd0WOZ vj2Q== X-Gm-Message-State: AGi0Pub4B/saIeQ1jJ+kBNng/LUf8OkcPq30Zp4c77UOu/r0nnzyHyg1 Yj3rAqwgDQVYkpUWPlZ3cXrYUzg8 X-Google-Smtp-Source: APiQypLQqEYZaSCyQyYxOFMuLygg/3El6usioFKIlto0W/la5gAZIvu9jAdCIXzbOkit/rJuVjvSjg== X-Received: by 2002:a17:902:b40e:: with SMTP id x14mr10498477plr.154.1586007836300; Sat, 04 Apr 2020 06:43:56 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:55 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 6/7] t5412: test the proc-receive hook on HTTP protocol Date: Sat, 4 Apr 2020 09:43:44 -0400 Message-Id: <20200404134345.10655-7-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin Copy from t5411 to test the "proc-receive" hook on the HTTP protocol. Signed-off-by: Jiang Xin --- t/t5412-proc-receive-hook-http-protocol.sh | 945 +++++++++++++++++++++ 1 file changed, 945 insertions(+) create mode 100755 t/t5412-proc-receive-hook-http-protocol.sh diff --git a/t/t5412-proc-receive-hook-http-protocol.sh b/t/t5412-proc-receive-hook-http-protocol.sh new file mode 100755 index 0000000000..b16056e024 --- /dev/null +++ b/t/t5412-proc-receive-hook-http-protocol.sh @@ -0,0 +1,945 @@ +#!/bin/sh +# +# Copyright (c) 2020 Jiang Xin +# + +test_description='Test proc-receive hook for HTTP protocol' + +. ./test-lib.sh + +ROOT_PATH="$PWD" +. "$TEST_DIRECTORY"/lib-gpg.sh +. "$TEST_DIRECTORY"/lib-httpd.sh +. "$TEST_DIRECTORY"/lib-terminal.sh +start_httpd + +# Create commits in and assign each commit's oid to shell variables +# given in the arguments (A, B, and C). E.g.: +# +# create_commits_in A B C +# +# NOTE: Never calling this function from a subshell since variable +# assignments will disappear when subshell exits. +create_commits_in () { + repo="$1" && + if ! parent=$(git -C "$repo" rev-parse HEAD^{}) + then + parent= + fi && + T=$(git -C "$repo" write-tree) && + shift && + while test $# -gt 0 + do + name=$1 && + test_tick && + if test -z "$parent" + then + oid=$(echo $name | git -C "$repo" commit-tree $T) + else + oid=$(echo $name | git -C "$repo" commit-tree -p $parent $T) + fi && + eval $name=$oid && + parent=$oid && + shift || + return 1 + done && + git -C "$repo" update-ref refs/heads/master $oid +} + +# Format the output of git-push, git-show-ref and other commands to make a +# user-friendly and stable text. We can easily prepare the expect text +# without having to worry about future changes of the commit ID and spaces +# of the output. Single quotes are replaced with double quotes, because +# it is boring to prepare unquoted single quotes in expect txt. We also +# remove some locale error messages, which break test if we turn on +# `GIT_TEST_GETTEXT_POISON=true` in order to test unintentional translations +# on plumbing commands. +make_user_friendly_and_stable_output () { + sed \ + -e "s/ *\$//" \ + -e "s/ */ /g" \ + -e "s/'/\"/g" \ + -e "s/$A//g" \ + -e "s/$B//g" \ + -e "s/$TAG//g" \ + -e "s/$ZERO_OID//g" \ + -e "s/[0-9a-f]\{7,\}//g" \ + -e "s#To .*/upstream.git#To #" \ + -e "/^error: / d" +} + +# Refs of upstream : master(B) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "setup" ' + git init --bare upstream && + git -C upstream config http.receivepack true && + git init workbench && + create_commits_in workbench A B && + ( + cd workbench && + # Try to make a stable fixed width for abbreviated commit ID, + # this fixed-width oid will be replaced with "". + git config core.abbrev 7 && + git remote add origin ../upstream && + git update-ref refs/heads/master $A && + git tag -m "v123" v123 $A && + git push origin \ + $B:refs/heads/master \ + $A:refs/heads/next + ) && + TAG=$(git -C workbench rev-parse v123) && + + # setup pre-receive hook + cat >upstream/hooks/pre-receive <<-\EOF && + #!/bin/sh + + echo >&2 "# pre-receive hook" + + while read old new ref + do + echo >&2 "pre-receive< $old $new $ref" + done + EOF + + # setup post-receive hook + cat >upstream/hooks/post-receive <<-\EOF && + #!/bin/sh + + echo >&2 "# post-receive hook" + + while read old new ref + do + echo >&2 "post-receive< $old $new $ref" + done + EOF + + chmod a+x \ + upstream/hooks/pre-receive \ + upstream/hooks/post-receive && + + upstream="$HTTPD_DOCUMENT_ROOT_PATH/upstream.git" && + mv upstream "$upstream" && + git -C workbench remote set-url origin $HTTPD_URL/smart/upstream.git +' + +setup_askpass_helper + +# Refs of upstream : master(B) next(A) +# Refs of workbench: master(A) tags/v123 +# git-push -f : master(A) NULL tags/v123 refs/review/master/topic(A) a/b/c(A) +test_expect_success "normal git-push command" ' + git -C workbench push -f origin \ + refs/tags/v123 \ + :refs/heads/next \ + HEAD:refs/heads/master \ + HEAD:refs/review/master/topic \ + HEAD:refs/heads/a/b/c \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/tags/v123 + remote: pre-receive< refs/review/master/topic + remote: pre-receive< refs/heads/a/b/c + remote: # post-receive hook + remote: post-receive< refs/heads/master + remote: post-receive< refs/heads/next + remote: post-receive< refs/tags/v123 + remote: post-receive< refs/review/master/topic + remote: post-receive< refs/heads/a/b/c + To + + ... HEAD -> master (forced update) + - [deleted] next + * [new tag] v123 -> v123 + * [new reference] HEAD -> refs/review/master/topic + * [new branch] HEAD -> a/b/c + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/a/b/c + refs/heads/master + refs/review/master/topic + refs/tags/v123 + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) tags/v123 refs/review/master/topic(A) a/b/c(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + ( + cd "$upstream" && + git update-ref -d refs/review/master/topic && + git update-ref -d refs/tags/v123 && + git update-ref -d refs/heads/a/b/c + ) +' + +test_expect_success "add two receive.procReceiveRefs settings" ' + ( + cd "$upstream" && + git config --add receive.procReceiveRefs refs/for && + git config --add receive.procReceiveRefs refs/review/ + ) +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : next(A) refs/for/master/topic(A) +test_expect_success "no proc-receive hook, fail to push special ref" ' + test_must_fail git -C workbench push origin \ + HEAD:next \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: error: cannot find hook "proc-receive" + remote: # post-receive hook + remote: post-receive< refs/heads/next + To + * [new branch] HEAD -> next + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + git -C "$upstream" update-ref -d refs/heads/next +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push --atomic: next(A) refs/for/master/topic(A) +test_expect_success "no proc-receive hook, fail all for atomic push" ' + test_must_fail git -C workbench push --atomic origin \ + HEAD:next \ + HEAD:refs/for/master/topic >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: error: cannot find hook "proc-receive" + To + ! [remote rejected] HEAD -> next (fail to run proc-receive hook) + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (bad version)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v --version 2 + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic(A) +test_expect_success "proc-receive bad protocol: unknown version" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + # Apache calls "receive-pack" with system locale settings, which + # makes the message "remote: fatal: ..." unstable for test. + sed -e "/^remote: fatal: / d" out | \ + make_user_friendly_and_stable_output >actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: error: proc-receive version "2" is not supported + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (no report)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : next(A) refs/for/master/topic(A) +test_expect_success "proc-receive bad protocol: no report" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/heads/next \ + HEAD:refs/for/master/topic >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: # post-receive hook + remote: post-receive< refs/heads/next + To + * [new branch] HEAD -> next + ! [remote rejected] HEAD -> refs/for/master/topic (no report from proc-receive) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + git -C "$upstream" update-ref -d refs/heads/next + +' + +test_expect_success "setup proc-receive hook (bad oid)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "bad-id new-id ref ok" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: bad oid" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic\ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> bad-id new-id ref ok + remote: error: proc-receive expected "old new ref status [msg]", got "bad-id new-id ref ok" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (no status)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: no status" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic + remote: error: proc-receive expected "old new ref status [msg]", got " refs/for/master/topic" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (unknown status)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic xx msg" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: unknown status" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic xx msg + remote: error: proc-receive has bad status "xx" for " refs/for/master/topic" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (bad status)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic bad status" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive bad protocol: bad status" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic bad status + remote: error: proc-receive has bad status "bad status" for " refs/for/master/topic" + To + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (ng)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic ng" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive: fail to update (no message)" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic ng + To + ! [remote rejected] HEAD -> refs/for/master/topic (failed) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (ng message)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic ng error msg" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive: fail to update (has message)" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic ng error msg + To + ! [remote rejected] HEAD -> refs/for/master/topic (error msg) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook (report status on builtin command)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/heads/master ok" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : (B) refs/for/master/topic +test_expect_success "proc-receive: warning on report for builtin command" ' + test_must_fail git -C workbench push origin \ + $B:refs/heads/master \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/heads/master ok + remote: error: proc-receive reported status on ref of builtin command: refs/heads/master + remote: # post-receive hook + remote: post-receive< refs/heads/master + To + .. -> master + ! [remote rejected] HEAD -> refs/for/master/topic (no report from proc-receive) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "cleanup" ' + git -C "$upstream" update-ref refs/heads/master $A +' + +test_expect_success "setup proc-receive hook (ok)" ' + cat >"$upstream/hooks/proc-receive" <<-EOF + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/master/topic +test_expect_success "proc-receive: ok" ' + git -C workbench push origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/for/master/topic + To + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/a/b/c/my/topic +test_expect_success "proc-receive: no report from proc-receive" ' + test_must_fail git -C workbench push origin \ + HEAD:refs/for/a/b/c/my/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/a/b/c/my/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/a/b/c/my/topic + remote: proc-receive> refs/for/master/topic ok + remote: error: proc-receive reported status on unknown ref: refs/for/master/topic + To + ! [remote rejected] HEAD -> refs/for/a/b/c/my/topic (no report from proc-receive) + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push -o ... : refs/for/master/topic +test_expect_success "not support push options" ' + test_must_fail git -C workbench push \ + -o issue=123 \ + -o reviewer=user1 \ + origin \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + fatal: the receiving end does not support push options + fatal: the remote end hung up unexpectedly + EOF + test_i18ncmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "enable push options" ' + git -C "$upstream" config receive.advertisePushOptions true +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push -o ... : next(A) refs/for/master/topic +test_expect_success "push with options" ' + git -C workbench push \ + --atomic \ + -o issue=123 \ + -o reviewer=user1 \ + origin \ + HEAD:refs/heads/next \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/next + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive: atomic push_options + remote: proc-receive< refs/for/master/topic + remote: proc-receive< issue=123 + remote: proc-receive< reviewer=user1 + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/heads/next + remote: post-receive< refs/for/master/topic + To + * [new branch] HEAD -> next + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + +# Refs of upstream : master(A) next(A) +# Refs of workbench: master(A) tags/v123 +test_expect_success "cleanup" ' + git -C "$upstream" update-ref -d refs/heads/next +' + +test_expect_success "setup proc-receive hook" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/review/a/b/c/topic ok" \ + -r "$ZERO_OID $A refs/for/next/topic ok ref:refs/pull/123/head" \ + -r "$ZERO_OID $A refs/for/master/topic ok ref:refs/pull/124/head" + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : refs/for/next/topic(A) refs/review/a/b/c/topic(A) refs/for/master/topic(A) +test_expect_success "report update of all special refs" ' + git -C workbench push origin \ + HEAD:refs/for/next/topic \ + HEAD:refs/review/a/b/c/topic \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/next/topic + remote: pre-receive< refs/review/a/b/c/topic + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/next/topic + remote: proc-receive< refs/review/a/b/c/topic + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/review/a/b/c/topic ok + remote: proc-receive> refs/for/next/topic ok ref:refs/pull/123/head + remote: proc-receive> refs/for/master/topic ok ref:refs/pull/124/head + remote: # post-receive hook + remote: post-receive< refs/for/next/topic + remote: post-receive< refs/review/a/b/c/topic + remote: post-receive< refs/for/master/topic + To + * [new reference] HEAD -> refs/pull/123/head + * [new reference] HEAD -> refs/review/a/b/c/topic + * [new reference] HEAD -> refs/pull/124/head + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "setup proc-receive hook" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$ZERO_OID $A refs/for/next/topic ok" \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(A) +# Refs of workbench: master(A) tags/v123 +# git push : bar(A) baz(A) refs/for/next/topic(A) foo(A) refs/for/master/topic(A) +test_expect_success "report mixed refs update" ' + git -C workbench push origin \ + $B:refs/heads/master \ + HEAD:refs/heads/bar \ + HEAD:refs/heads/baz \ + HEAD:refs/for/next/topic \ + HEAD:refs/heads/foo \ + HEAD:refs/for/master/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/heads/bar + remote: pre-receive< refs/heads/baz + remote: pre-receive< refs/for/next/topic + remote: pre-receive< refs/heads/foo + remote: pre-receive< refs/for/master/topic + remote: # proc-receive hook + remote: proc-receive< refs/for/next/topic + remote: proc-receive< refs/for/master/topic + remote: proc-receive> refs/for/next/topic ok + remote: proc-receive> refs/for/master/topic ok + remote: # post-receive hook + remote: post-receive< refs/heads/master + remote: post-receive< refs/heads/bar + remote: post-receive< refs/heads/baz + remote: post-receive< refs/for/next/topic + remote: post-receive< refs/heads/foo + remote: post-receive< refs/for/master/topic + To + .. -> master + * [new branch] HEAD -> bar + * [new branch] HEAD -> baz + * [new reference] HEAD -> refs/for/next/topic + * [new branch] HEAD -> foo + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/bar + refs/heads/baz + refs/heads/foo + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "config receive.procReceiveRefs for all ref/" ' + git -C "$upstream" config --add receive.procReceiveRefs refs/ +' + +test_expect_success "setup proc-receive hook" ' + cat >"$upstream/hooks/proc-receive" <<-EOF && + #!/bin/sh + + printf >&2 "# proc-receive hook\n" + + test-tool proc-receive -v \ + -r "$B $A refs/heads/master ft" \ + -r "$A $ZERO_OID refs/heads/foo ft" \ + -r "$A $B refs/heads/bar ft" \ + -r "$A $B refs/for/master/topic ok ref:refs/pull/123/head" \ + -r "$B $A refs/for/next/topic ok ref:refs/pull/124/head" + EOF + chmod a+x "$upstream/hooks/proc-receive" +' + +# Refs of upstream : master(B) foo(A) bar(A)) baz(A) +# Refs of workbench: master(A) tags/v123 +# git push -f : (NULL) (B) refs/for/master/topic(A) refs/for/next/topic(A) +test_expect_success "report test: fallthrough" ' + git -C workbench push -f origin \ + HEAD:refs/heads/master \ + :refs/heads/foo \ + $B:refs/heads/bar \ + HEAD:refs/for/master/topic \ + HEAD:refs/for/next/topic \ + >out 2>&1 && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/bar + remote: pre-receive< refs/heads/foo + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/for/master/topic + remote: pre-receive< refs/for/next/topic + remote: # proc-receive hook + remote: proc-receive< refs/heads/bar + remote: proc-receive< refs/heads/foo + remote: proc-receive< refs/heads/master + remote: proc-receive< refs/for/master/topic + remote: proc-receive< refs/for/next/topic + remote: proc-receive> refs/heads/master ft + remote: proc-receive> refs/heads/foo ft + remote: proc-receive> refs/heads/bar ft + remote: proc-receive> refs/for/master/topic ok ref:refs/pull/123/head + remote: proc-receive> refs/for/next/topic ok ref:refs/pull/124/head + remote: # post-receive hook + remote: post-receive< refs/heads/bar + remote: post-receive< refs/heads/foo + remote: post-receive< refs/heads/master + remote: post-receive< refs/for/master/topic + remote: post-receive< refs/for/next/topic + To + .. -> bar + - [deleted] foo + + ... HEAD -> master (forced update) + * [new reference] HEAD -> refs/pull/123/head + * [new reference] HEAD -> refs/pull/124/head + EOF + test_cmp expect actual && + git -C "$upstream" show-ref >out && + make_user_friendly_and_stable_output actual && + cat >expect <<-EOF && + refs/heads/bar + refs/heads/baz + refs/heads/master + EOF + test_cmp expect actual +' + +test_done From patchwork Sat Apr 4 13:43:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11474291 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 036A7912 for ; Sat, 4 Apr 2020 13:44:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D6492206C3 for ; Sat, 4 Apr 2020 13:44:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="A53bMO2D" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726315AbgDDNn7 (ORCPT ); Sat, 4 Apr 2020 09:43:59 -0400 Received: from mail-pj1-f68.google.com ([209.85.216.68]:39594 "EHLO mail-pj1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725837AbgDDNn6 (ORCPT ); Sat, 4 Apr 2020 09:43:58 -0400 Received: by mail-pj1-f68.google.com with SMTP id z3so4325769pjr.4 for ; Sat, 04 Apr 2020 06:43:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=xao2PpbLEeHz8Q7brQeSrXDbuQXW7E1m7gDBH+k/EMk=; b=A53bMO2DBBKpd4yw/yXZTKvPerAptw2UqWmVT08F0D9z8vrha//D2IW97AA86m8Bi/ J6zD0JLYutwR+I4E4lr1ARLNkF/2X6T8du8aEd+0qU8wSLecoSEzBIjnyqqXGEiFG1qi 9YIiJ3NOKgm/DL0XNGiDjnmOIWNx5r9npppnv///bohMAp/pUMeVYXaZi5D1aY1cOhbc 9LE1lRANysA4gtC+ZF+uV0DhqNmhzTFYUSAe7iupX9oneRAI/G9C1EAoxFIShl4P5gGD g2m10xFZX7+6ybj1HDftEr1xOf9ioVo2EbfJ0WITBJr992mz0G+VPKg7+E96a6lIvYvd f0Mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=xao2PpbLEeHz8Q7brQeSrXDbuQXW7E1m7gDBH+k/EMk=; b=mqJYlB64fi6ylnAN1gaFgG75dKoYjubH0APYUWqT1/D426X54JLy+2CVigTM3hpxoF 3tjPGyyZ9VoolaClZSGeq7gFPCk7IGDj2b4mEWnOcvqSUI3LE/4x88Ga64dbuusL29ic d0wsREoXTiUu3/Jr0/5W/9NHm6ArktXfnvauZ0nlPkGBmMuI4ZJNrjdWHJEWlFYTQEKP +ssMdmJ6IRAdFpRwJRp0ZnMtqQabMdUZHLcmh5OB1sksTL9iwbAIOBCn5AZ6GDybzlx0 hOZzi295bBI56gu4Gp48cEHdvCc7aXyhml0v7U7u3+nQxVtBOQpZ5rnef7UUDZl5S4i7 p6dw== X-Gm-Message-State: AGi0PuaBUqgm3yfbtZKWtxg/kd9621k9K9ial9yMJYPjzjyFfOzbFFEN FJX9Fv/Q4hQ8FQs8T+PN4JIBp6zp X-Google-Smtp-Source: APiQypLgMNLsGrdMBv7yBfbtG2guhaXXKcKQ/Yp+SFo4a2SjIik9nARpWriDcGCwYldDkg4Ezbxn5A== X-Received: by 2002:a17:90a:fb41:: with SMTP id iq1mr15474672pjb.38.1586007837203; Sat, 04 Apr 2020 06:43:57 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id y15sm7755531pfc.206.2020.04.04.06.43.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 04 Apr 2020 06:43:56 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v8 7/7] doc: add documentation for the proc-receive hook Date: Sat, 4 Apr 2020 09:43:45 -0400 Message-Id: <20200404134345.10655-8-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200403160838.6252-1-worldhello.net@gmail.com> References: <20200403160838.6252-1-worldhello.net@gmail.com> MIME-Version: 1.0 Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Jiang Xin Add documentation for the new "proc-receive" hook. Signed-off-by: Jiang Xin --- Documentation/githooks.txt | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 3dccab5375..10ea5c1f18 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -333,6 +333,76 @@ The default 'update' hook, when enabled--and with `hooks.allowunannotated` config option unset or set to false--prevents unannotated tags to be pushed. +[[proc-receive]] +proc-receive +~~~~~~~~~~~~ +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +special `git push` command. According to refnames of the commands which +`git push` sends to 'git-receive-pack', the commands will be devided +into two groups by matching what the `receive.procReceiveRefs` +configuration variable defines. One group of the commands will execute +the internal `execute_commands` function to update the corresponding +refnames, and the other group of commands which have matching refnames +will execute this 'proc-receive' hook to create pull requests, etc. +If there is no `receive.procReceiveRefs` settings, this hook won't +execute at all, and all commands are sent to the internal +`execute_commands` function. + +Its exit status only determines the success or failure of the group of +commands with special refnames, unless atomic push is in use. + +This hook executes once for the receive operation. It takes no +arguments, but will talk a protocol in pkt-line format with the +'receive-pack' for reading commands, push-options (optional), and +sending result. In the following example, The letter "S" stands for +"receive-pack" and letter "H" stands for the hook. + + # Version and capabilities negotiation. + S: PKT-LINE(version=1\0push-options atomic...) + S: flush-pkt + H: PKT-LINE(version=1\0push-options...) + H: flush-pkt + + # Send commands from server to the hook. + S: PKT-LINE(old-oid new-oid ref) + S: ... ... + S: flush-pkt + # Only if push-options have been negotiated. + S: PKT-LINE(push-option) + S: ... ... + S: flush-pkt + + # Receive result from the hook. + # OK, run this command successfully. + H: PKT-LINE(old-oid new-oid ref ok) + # NO, I reject it. + H: PKT-LINE(old-oid new-oid ref ng reason) + # OK, but use an alternate reference. + H: PKT-LINE(old-oid new-oid ref ok ref:alt-ref) + # It will fallthrough to receive-pack to execute. + H: PKT-LINE(old-oid new-oid ref ft) + H: ... ... + H: flush-pkt + +The "proc-receive" hook may update one or more references, and will send +its result one by one in pkt-line format. Each line of the result has +four fields and one optional message field, like " + []". + +The first three fields are the same as those of the commands for +"receive-pack". + +The forth field has a two-letter status code. Available status codes: + +* ok: The command runs successfully. If the optional message has a + prefix "ref:", the hook has created/updated an alternate reference + instead. + +* ng: Fail to run the command. Error message is given in the optional + message field. + +* ft: Will fallthrough to receive-pack to execute. + [[post-receive]] post-receive ~~~~~~~~~~~~