From patchwork Sun Mar 22 13:18:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11451787 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 E167F6CA for ; Sun, 22 Mar 2020 13:18:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id ADD41206C3 for ; Sun, 22 Mar 2020 13:18:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="pkBaA9gL" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726666AbgCVNS2 (ORCPT ); Sun, 22 Mar 2020 09:18:28 -0400 Received: from mail-pj1-f66.google.com ([209.85.216.66]:54560 "EHLO mail-pj1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725892AbgCVNS2 (ORCPT ); Sun, 22 Mar 2020 09:18:28 -0400 Received: by mail-pj1-f66.google.com with SMTP id np9so4807675pjb.4 for ; Sun, 22 Mar 2020 06:18:27 -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=DMrYhzVG6GMpmze6k7qz6cpaMgWCkFRuRT2dFP9SExU=; b=pkBaA9gL7fdYD0XEuXdH8ZQd8H6LjXWRYN6VN7UnwwgGzeCx39P22rzpiK3va/yGZw uMN5taAIJV2Pk4Smkek4ph2ADA15SUUD9GLwxRZPNdrOlVma26N4DOI87KmzoaMnjOoC uK5B2zeO8dTfw9TVHG4ZQPtuIrKkbsg/qHlSFiJ+7vaqwRRgdVEzUiiSX25KyXDfy/L0 iFwiX4E3jQq5+8TPqT1QKZ4wN9p7Q7Lc3d4l2dG8sX+zHoIEeLOFdtfa6+7S9DQ/Uv3u i0UeXmIfcajA4LV6OEH9bf3eZdb4+IBIm7YOvqjFRaL9BAPBbrd0uakUcSfoMutKN3SK gNBw== 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=DMrYhzVG6GMpmze6k7qz6cpaMgWCkFRuRT2dFP9SExU=; b=m5gFkIxDWqwcE5QhtQIK3+5KnAHC9/4Fe/z0N+sMZyjUqJW9lpz+d76sW9oC7ThQWN cWGgrwcrRtzakXBdIZb9fJpili4TMfzHgsjhGLlvD49WA4eiWlwEjKBJsKF9+nm3jwtj 9FJONNXV+Usl/Le8Quy2HphRKl/Se9jB7LzzL67nVC5YYWMgVxcwfnj9bj2A8oOcHgM2 aSPXrXlLZA8CaGTu4Jp3Mi4XHRfMMgBrelp+LefGunx806MzzXjGF738lIjgcDDjZDXE TS6vxr1ri7PWUvuFGlaGc/XgiIcicwk9rGOl39KIltYXwhuK3gL2h/R503g94JwaOIeH EwuQ== X-Gm-Message-State: ANhLgQ2AbLOGzT1fHRl1OnjrB+AAREi5Nh/PcaAGj4n+NnZCYxvMRfZs ieMdZpmnOBIYl0QdBZfv7/U= X-Google-Smtp-Source: ADFU+vvbNztPguZxXhrz6yTQa4xgy9vX5EvpY/OjFVWunU5mE08HR82MJr47kRbRAOwaK1VzhWtdIw== X-Received: by 2002:a17:90b:282:: with SMTP id az2mr20345121pjb.13.1584883106471; Sun, 22 Mar 2020 06:18:26 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id nh4sm9456432pjb.39.2020.03.22.06.18.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Mar 2020 06:18:26 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v4 1/5] transport: not report a non-head push as a branch Date: Sun, 22 Mar 2020 09:18:11 -0400 Message-Id: <20200322131815.11872-2-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.com> References: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.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 | 144 +++++++++++++++++++++++++++++++++++ transport.c | 3 +- 2 files changed, 146 insertions(+), 1 deletion(-) 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..fe2861f2e6 --- /dev/null +++ b/t/t5411-proc-receive-hook.sh @@ -0,0 +1,144 @@ +#!/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: Avoid 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^{} 2>/dev/null) + 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_git_output () { + sed \ + -e "s/ *\$//g" \ + -e "s/$A//g" \ + -e "s/$B//g" \ + -e "s/$TAG//g" \ + -e "s/$ZERO_OID//g" \ + -e "s/'/\"/g" +} + +test_expect_success "setup" ' + git init --bare upstream && + git init workbench && + create_commits_in workbench A B && + ( + cd workbench && + git remote add origin ../upstream && + git config core.abbrev 7 && + git update-ref refs/heads/master $A && + git tag -m "v1.0.0" v1.0.0 $A && + git push origin \ + $B:refs/heads/master \ + $A:refs/heads/next + ) && + TAG=$(cd workbench; git rev-parse v1.0.0) && + + # setup pre-receive hook + cat >upstream/hooks/pre-receive <<-EOF && + #!/bin/sh + + printf >&2 "# pre-receive hook\n" + + while read old new ref + do + printf >&2 "pre-receive< \$old \$new \$ref\n" + done + EOF + + # setup post-receive hook + cat >upstream/hooks/post-receive <<-EOF && + #!/bin/sh + + printf >&2 "# post-receive hook\n" + + while read old new ref + do + printf >&2 "post-receive< \$old \$new \$ref\n" + done + EOF + + chmod a+x \ + upstream/hooks/pre-receive \ + upstream/hooks/post-receive +' + +test_expect_success "normal git-push command" ' + ( + cd workbench && + git push -f origin \ + refs/tags/v1.0.0 \ + :refs/heads/next \ + HEAD:refs/heads/master \ + HEAD:refs/review/master/topic \ + HEAD:refs/heads/a/b/c + ) >out 2>&1 && + format_git_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/v1.0.0 + 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/v1.0.0 + remote: post-receive< refs/review/master/topic + remote: post-receive< refs/heads/a/b/c + To ../upstream + + ce858e6...1029397 HEAD -> master (forced update) + - [deleted] next + * [new tag] v1.0.0 -> v1.0.0 + * [new reference] HEAD -> refs/review/master/topic + * [new branch] HEAD -> a/b/c + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/a/b/c + refs/heads/master + refs/review/master/topic + refs/tags/v1.0.0 + EOF + test_cmp expect actual +' + +test_done diff --git a/transport.c b/transport.c index 1fdc7dac1a..b5b7bb841e 100644 --- a/transport.c +++ b/transport.c @@ -501,7 +501,8 @@ static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_widt else if (is_null_oid(&ref->old_oid)) print_ref_status('*', (starts_with(ref->name, "refs/tags/") ? "[new tag]" : - "[new branch]"), + (starts_with(ref->name, "refs/heads/") ? "[new branch]" : + "[new reference]")), ref, ref->peer_ref, NULL, porcelain, summary_width); else { struct strbuf quickref = STRBUF_INIT; From patchwork Sun Mar 22 13:18:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11451791 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 7898017D4 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 428E2206C3 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aEpjjNXK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726770AbgCVNSa (ORCPT ); Sun, 22 Mar 2020 09:18:30 -0400 Received: from mail-pg1-f195.google.com ([209.85.215.195]:44953 "EHLO mail-pg1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725972AbgCVNSa (ORCPT ); Sun, 22 Mar 2020 09:18:30 -0400 Received: by mail-pg1-f195.google.com with SMTP id 142so477531pgf.11 for ; Sun, 22 Mar 2020 06:18:28 -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=01w8KPiHkG2SsbnEMti1/FXC1MagSaV8mXqqk9txZhI=; b=aEpjjNXKoC+0ox4Wc+MQNIHwuH/fXICRC3g4Wv19/uxumkk0w7DThFJS8rz6GLAugm Q7JMXOg55kK6CU5oYXfjkBGaUB0AKaMKAAxBg9xPoRwDXmsc/KXz4JURuqSfd0FhV4dA GfCWqGjfNcF7fvP+7CSSHQOAAEExpyKG7Wy1Q/VieiRHlyMlr+Do8As5v7BrQlOGMtab T9c/nGQ6HW+V0veSIoVtWiFtsq781OjtfNNHUbO5LIseHCfMkdoOfkRTWSIfMe2FDQj/ dcy2moELOQ/mEm6YLbZEvM2BosggO37zZmOeUR+oojBv4c1wYCrmB74bJNh6OiIC989t sKXw== 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=01w8KPiHkG2SsbnEMti1/FXC1MagSaV8mXqqk9txZhI=; b=uba4IvEeOYTRJghrXnglT5lXqzzCYJyVbpXMFNVqoyvW50TGEx1ulOb3+GcVFi+yey bRzjaGnVZeunc2ia8itNzWhesQtt879ZdmStrnWjpX03LjcyjLBK19X4vYwa9PZIA5La kaITATEeJp2/ycTPEKMyn5seKyqdZOvsPjJ+lLOVaBPO9RAGeVoF/Tz59MfmdzyZZIUP cvmp/oONiX1jbvOZKYFulOYTLXuw8oHpGbXAfh26fzlbi6OO+XMqtjKDpP5ErxXrP/ea a22EoAbO2N+r5e5ihkZNg9HUrEdTNlycrg8rnfoDlMcjTHpr/akuzbDb2XP/5Ce70s3b ZtKw== X-Gm-Message-State: ANhLgQ2kCLkAgFRQUgJ39W1KPxZh2XftXB3MdJOpM1b2WDu8kgE431cp NYq4zkRKwdz09tonPalnYtw= X-Google-Smtp-Source: ADFU+vvR13MWnNSggv3nkb7mu7heeDeVjFBc7/KTrMk+IAGcSMhz4J5EjyZGQ0WUM1o3FIJtwnXziQ== X-Received: by 2002:a65:4d48:: with SMTP id j8mr10311474pgt.144.1584883107429; Sun, 22 Mar 2020 06:18:27 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id nh4sm9456432pjb.39.2020.03.22.06.18.26 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Mar 2020 06:18:26 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v4 2/5] receive-pack: add new proc-receive hook Date: Sun, 22 Mar 2020 09:18:12 -0400 Message-Id: <20200322131815.11872-3-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.com> References: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.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. This "proc-receive" hook reads 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. S: PKT-LINE(version=1\0push-options ...) S: flush-pkt H: PKT-LINE(version=1\0push-options ...) H: flush-pkt S: PKT-LINE(old-oid new-oid ref) S: ... ... S: flush-pkt # Optional, only if push-options is negotiated. S: PKT-LINE(push-option) S: ... ... S: flush-pkt # 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. Signed-off-by: Jiang Xin --- Makefile | 1 + builtin/receive-pack.c | 275 +++++++++++++++++- t/helper/test-proc-receive.c | 164 +++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t5411-proc-receive-hook.sh | 528 +++++++++++++++++++++++++++++++++++ 6 files changed, 965 insertions(+), 5 deletions(-) create mode 100644 t/helper/test-proc-receive.c diff --git a/Makefile b/Makefile index 9804a0758b..0a08a0504e 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..0aca08eb65 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -312,7 +312,8 @@ 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:1; int index; struct object_id old_oid; struct object_id new_oid; @@ -817,6 +818,241 @@ static int run_update_hook(struct command *cmd) return finish_command(&proc); } +static int read_proc_receive_result(struct packet_reader *reader, + struct command **commands) +{ + struct command **tail = commands; + int code = 0; + + for (;;) { + struct object_id old_oid, new_oid; + struct command *cmd; + 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++ != ' ') + die("protocol error: proc-receive expected 'old new ref status [msg]', got '%s'", + reader->line); + + refname = p; + status = strchr(p, ' '); + if (!status) + die("protocol error: proc-receive expected 'old new ref status [msg]', got '%s'", + reader->line); + *status++ = '\0'; + if (strlen(status) > 2 && *(status + 2) == ' ') { + msg = status + 2; + *msg++ = '\0'; + } + if (strlen(status) != 2) + die("protocol error: proc-receive has bad status '%s' for '%s'", + status, reader->line); + + FLEX_ALLOC_MEM(cmd, ref_name, refname, strlen(refname)); + oidcpy(&cmd->old_oid, &old_oid); + oidcpy(&cmd->new_oid, &new_oid); + cmd->run_proc_receive = 1; + + if (!strcmp(status, "ng")) { + if (msg) + cmd->error_string = xstrdup(msg); + else + cmd->error_string = "failed"; + code = 1; + } else if (strcmp("ok", status)) { + die("protocol error: proc-receive has bad status '%s' for '%s'", + status, reader->line); + } + + *tail = cmd; + tail = &cmd->next; + } + 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 *result_commands = NULL; + struct command *cmd; + const char *argv[2]; + struct packet_reader reader; + struct strbuf cap = 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 to 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_push_options) + strbuf_addstr(&cap, " push-options"); + if (use_atomic) + strbuf_addstr(&cap, " atomic"); + 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) + die("protocol error: unknown proc-receive version '%d'", version); + + /* 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, &result_commands); + close(proc.in); + close(proc.out); + if (use_sideband) + finish_async(&muxer); + if (finish_command(&proc)) + die("proc-receive did not exit properly"); + + sigchain_pop(SIGPIPE); + + /* After receiving the result from the "proc-receive" hook, + * "receive-pack" will use the result to replace commands that + * have specific `run_proc_receive` field. + */ + for (cmd = *commands; cmd; cmd = cmd->next) + if (!cmd->run_proc_receive) + break; + + /* Merge commands with result_commands and sort */ + if (!cmd) { + *commands = result_commands; + } else { + struct command *next_cmd = cmd; + struct command *next_result = result_commands; + struct command *head = NULL; + struct command *tail = NULL; + + if (!next_result || + strcmp(next_cmd->ref_name, next_result->ref_name) < 0) { + head = next_cmd; + next_cmd = next_cmd->next; + } else { + head = next_result; + next_result = next_result->next; + } + tail = head; + + for (;;) { + if (!next_cmd) { + tail->next = next_result; + break; + } else if (next_cmd->run_proc_receive) { + next_cmd = next_cmd->next; + } else if (!next_result) { + tail->next = next_cmd; + next_cmd = next_cmd->next; + tail = tail->next; + } else { + if (strcmp(next_cmd->ref_name, next_result->ref_name) < 0) { + tail->next = next_cmd; + next_cmd = next_cmd->next; + tail = tail->next; + } else { + tail->next = next_result; + next_result = next_result->next; + tail = tail->next; + } + } + } + *commands = head; + } + + 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 +1628,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 +1668,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); @@ -1458,16 +1694,18 @@ static void execute_commands_atomic(struct command *commands, strbuf_release(&err); } -static void execute_commands(struct command *commands, +static void execute_commands(struct command **orig_commands, const char *unpacker_error, struct shallow_info *si, const struct string_list *push_options) { + struct command *commands = *orig_commands; struct check_connected_options opt = CHECK_CONNECTED_INIT; struct command *cmd; 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 +1735,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 = 1; + 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 +1775,19 @@ 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(orig_commands, push_options); + commands = *orig_commands; + 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 @@ -2019,7 +2284,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) update_shallow_info(commands, &si, &ref); } use_keepalive = KEEPALIVE_ALWAYS; - execute_commands(commands, unpack_status, &si, + execute_commands(&commands, unpack_status, &si, &push_options); if (pack_lockfile) unlink_or_warn(pack_lockfile); diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c new file mode 100644 index 0000000000..e725cf9713 --- /dev/null +++ b/t/helper/test-proc-receive.c @@ -0,0 +1,164 @@ +#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_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, "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_MEM(cmd, ref_name, refname, strlen(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; + + 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 c9a232d238..9bff461446 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -43,6 +43,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 c8549fd87f..71e5f1c372 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -33,6 +33,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 fe2861f2e6..d114559792 100755 --- a/t/t5411-proc-receive-hook.sh +++ b/t/t5411-proc-receive-hook.sh @@ -50,6 +50,24 @@ format_git_output () { -e "s/'/\"/g" } +# Asynchronous sideband may generate inconsistent output messages, +# sort before comparison. +test_sorted_cmp () { + if ! $GIT_TEST_CMP "$@" + then + cmd=$GIT_TEST_CMP + for f in "$@" + do + sort "$f" >"$f.sorted" + cmd="$cmd \"$f.sorted\"" + done + if ! eval $cmd + then + $GIT_TEST_CMP "$@" + fi + fi +} + test_expect_success "setup" ' git init --bare upstream && git init workbench && @@ -141,4 +159,514 @@ test_expect_success "normal git-push command" ' test_cmp expect actual ' +test_expect_success "cleanup" ' + ( + cd upstream && + git update-ref -d refs/review/master/topic && + git update-ref -d refs/tags/v1.0.0 && + git update-ref -d refs/heads/a/b/c + ) +' + +test_expect_success "no proc-receive hook, fail to push special ref" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:next \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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 to find hook "proc-receive" + remote: # post-receive hook + remote: post-receive< refs/heads/next + To ../upstream + * [new branch] HEAD -> next + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + error: failed to push some refs to "../upstream" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + +test_expect_success "cleanup" ' + ( + cd upstream && + git update-ref -d refs/heads/next + ) +' + +# TODO: report for the failure of master branch is unnecessary. +test_expect_success "no proc-receive hook, fail all for atomic push" ' + ( + cd workbench && + test_must_fail git push --atomic origin \ + HEAD:next \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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 to find hook "proc-receive" + To ../upstream + ! [rejected] master (atomic push failed) + ! [remote rejected] HEAD -> next (fail to run proc-receive hook) + ! [remote rejected] HEAD -> refs/for/master/topic (fail to run proc-receive hook) + error: failed to push some refs to "../upstream" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive bad protocol: unknown version" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + fatal: protocol error: unknown proc-receive version "2" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive bad protocol: no report" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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 + To ../upstream + ! [remote failure] HEAD -> refs/for/master/topic (remote failed to report status) + error: failed to push some refs to "../upstream" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +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 +' + +test_expect_success "proc-receive bad protocol: bad oid" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + fatal: protocol error: proc-receive expected "old new ref status [msg]", got "bad-id new-id ref ok" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive bad protocol: no status" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + fatal: protocol error: proc-receive expected "old new ref status [msg]", got " refs/for/master/topic" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive bad protocol: unknown status" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + fatal: protocol error: proc-receive has bad status "xx" for " refs/for/master/topic" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive bad protocol: bad status" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + fatal: protocol error: proc-receive has bad status "bad status" for " refs/for/master/topic" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive: fail to update (no message)" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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 ../upstream + ! [remote rejected] HEAD -> refs/for/master/topic (failed) + error: failed to push some refs to "../upstream" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "proc-receive: fail to update (has message)" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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 ../upstream + ! [remote rejected] HEAD -> refs/for/master/topic (error msg) + error: failed to push some refs to "../upstream" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +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 +' + +test_expect_success "proc-receive: ok" ' + ( + cd workbench && + git push origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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 ../upstream + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "proc-receive: report unknown ref" ' + ( + cd workbench && + test_must_fail git push origin \ + HEAD:refs/for/a/b/c/my/topic + ) >out 2>&1 && + format_git_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 + warning: remote reported status on unknown ref: refs/for/master/topic + remote: # post-receive hook + remote: post-receive< refs/for/master/topic + To ../upstream + ! [remote failure] HEAD -> refs/for/a/b/c/my/topic (remote failed to report status) + error: failed to push some refs to "../upstream" + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "not support push options" ' + ( + cd workbench && + test_must_fail git push \ + -o issue=123 \ + -o reviewer=user1 \ + origin \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + fatal: the receiving end does not support push options + fatal: the remote end hung up unexpectedly + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + EOF + test_cmp expect actual +' + +test_expect_success "enable push options" ' + ( + cd upstream && + git config receive.advertisePushOptions true + ) +' + +test_expect_success "push with options" ' + ( + cd workbench && + git push \ + -o issue=123 \ + -o reviewer=user1 \ + origin \ + HEAD:refs/heads/next \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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: 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/for/master/topic + remote: post-receive< refs/heads/next + To ../upstream + * [new branch] HEAD -> next + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/next + EOF + test_cmp expect actual +' + test_done From patchwork Sun Mar 22 13:18:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11451789 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 39BCA17EF for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 18B23206C3 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aRtSE3fE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726748AbgCVNS3 (ORCPT ); Sun, 22 Mar 2020 09:18:29 -0400 Received: from mail-pg1-f193.google.com ([209.85.215.193]:45438 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726623AbgCVNS3 (ORCPT ); Sun, 22 Mar 2020 09:18:29 -0400 Received: by mail-pg1-f193.google.com with SMTP id m15so5702879pgv.12 for ; Sun, 22 Mar 2020 06:18:28 -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=DzqP6YXGD+BQsTS2yuVVIADxgEwBnu1kekSJxUOeFcA=; b=aRtSE3fEsa+taz2OjupZg9SkhJ0GNTeyvFQ6va4xVc+vAG5G6rs1azHbvyIKlUXiSN CsX7s2WG9vEht+lQwMYlqWnne5038B6GaPY84bCmS3B8wO0JNBl+S3IUpUdlIiEJfOz3 Ft+l+o49ib6g3NbjkLWjQNgDuSHYqemnhiaJAZAPyY14WDH6HHMfT7qol1UPhBJLiZxN paqfB8Ge2HZ7rwLwUaUrlvTZtfrwb7o/qvIu/kgcbUmfRg9lmuZUFj+8hcA5CpFY/8jZ Hl1byuDNYp+VtsCL2A/k4kVl5CBiwWYol/hUmAOKLUcv8YSkPkgxzPTi/yLijMmEZV/c +anw== 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=DzqP6YXGD+BQsTS2yuVVIADxgEwBnu1kekSJxUOeFcA=; b=QLLFpJmu4UsTVSl2WE9iHAXqh0W8yBmkLjTqQ2GI4JAsfB2e/BXvgvU/Xqa1eZj7Ix ki0vCEZ2v5qgM7LHm1F115/D1tPvvFCd8VlEzkSsTrRgz5B1y0NNVMQ4tALPOBZ0vuPJ vjlUxMCZb5sWAiTBjq4+642elc3quPN+3bYWa7ACSTHql1/djY2P8uEkj+DHZ9f7/zEK QfUnVnqBXxVskEAINJazpy6mOjPYNpRGw5HiEEk9ysA1y5+20WW8bkrD6mwYLwpDR7lL QyohuYSJ2n5QT6hLmOdbakFThysZecWIprSFk68wu2iTZjRT0N1VCtdIF1dFScVQmuqf fpEQ== X-Gm-Message-State: ANhLgQ1gKpZlIsQP/0kgaEjAzXu8lrY3gt8qHotr2bg2FKxyEXFWwzKH /Ogsde1fpXU+b1lafQfekk0= X-Google-Smtp-Source: ADFU+vvFzYbQ7+krAO14mRJqhMJ2GqRfS60kLtudDvAZF6hqdgHgRpoEUIZocqy4If4uZZt+YJhdqw== X-Received: by 2002:a62:194c:: with SMTP id 73mr19794961pfz.159.1584883108170; Sun, 22 Mar 2020 06:18:28 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id nh4sm9456432pjb.39.2020.03.22.06.18.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Mar 2020 06:18:27 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v4 3/5] refs.c: refactor to reuse ref_is_hidden() Date: Sun, 22 Mar 2020 09:18:13 -0400 Message-Id: <20200322131815.11872-4-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.com> References: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.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 Sun Mar 22 13:18:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11451793 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 A115218A5 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7FEF2206C3 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nVwYqZNE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726785AbgCVNSb (ORCPT ); Sun, 22 Mar 2020 09:18:31 -0400 Received: from mail-pg1-f172.google.com ([209.85.215.172]:40408 "EHLO mail-pg1-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725892AbgCVNSa (ORCPT ); Sun, 22 Mar 2020 09:18:30 -0400 Received: by mail-pg1-f172.google.com with SMTP id t24so5717969pgj.7 for ; Sun, 22 Mar 2020 06:18:29 -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=59Ye2YrJ2JXhHSe6f4fSVZcprQwmdvuNx3+vM7mYsY0=; b=nVwYqZNEvfc7ACuOU0x+/HUjHcNYa6uYh4FkJJ0UyghrXGcNrR6OIp9BlPtfgPbeXy tlPm0iTr+UJidbIJIBkZ8B4zcLxHSHtmtokrqsOwOYNuauLa4vTp4WlETnncbBU5icDf Jk+GqxL+llUDR5loIaNHnnegA1zJwYW7LM4g5N7/lmmyz5BzmHSXyTcqUEZEEgrbjKOX F1roWU2msxN/AxqdojYMAbVWgHOTqOdJsHMTDU8r4z+/6aY9e0m3NlWSi2C1Qm+3pt6q JuN/YNLKM/A13pkHq4pWDs/tcv65psLR9qit2CS83eIbbp4V6gTX2pTSxaULXpx9gCvJ mtLA== 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=59Ye2YrJ2JXhHSe6f4fSVZcprQwmdvuNx3+vM7mYsY0=; b=QvXxe/e4xDydYM54Pap7FZe7V3cKT2vxiMTfqYFn9f0trN7BTgs3z/0v3lt+f9wghA 0XSwm86lpOlP9lm5I/1CMoTxnCZyC43ziURnNJWrh/fhBhy1RWqDMbwkiNjqUnNVfK8U 02sOZV31bB+V4bvK3IppVdullfJuBXSb5VdIm6nwvEKZo3VIA9/A/Rr75tinWu0xwzOM FvrdTlivxMq9dO7C+WyprxKh+YNTi5TjrDixlmspMrhI/GYVSWMvuZ9qLL8NQSJC3WAt mm9wBKLSb/WjkYryeiL3gZYCfGcWl0poV5kHri4dcbWLg+gui6vu609YDAYNmHDv7x4/ eCxw== X-Gm-Message-State: ANhLgQ2ldWzVa4OPsiNBXIpApvQQ/iItvS5qil2ZrVFovQN7scz3/Fqi 6b+L1GcKsmvYC8aTNnWQ0d7uL/ND X-Google-Smtp-Source: ADFU+vtZCIH2W10fKmQ015zCBfEdNWEk2Zx9MhxoVFvVqHwrwWaSMfMadeLJH2md4MgKyJmf84uL7w== X-Received: by 2002:a63:5d04:: with SMTP id r4mr17228877pgb.241.1584883109078; Sun, 22 Mar 2020 06:18:29 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id nh4sm9456432pjb.39.2020.03.22.06.18.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Mar 2020 06:18:28 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v4 4/5] receive-pack: new config receive.procReceiveRefs Date: Sun, 22 Mar 2020 09:18:14 -0400 Message-Id: <20200322131815.11872-5-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.com> References: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.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 | 204 +++++++++++++++++++++++++++++++ 3 files changed, 254 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 0aca08eb65..fef97e6985 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); } @@ -1738,15 +1753,26 @@ static void execute_commands(struct command **orig_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 = 1; - 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 = 1; + run_proc_receive = 1; + } } + + strbuf_release(&refname_full); } if (run_receive_hook(commands, "pre-receive", 0, push_options)) { @@ -2207,6 +2233,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); @@ -2322,5 +2350,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 d114559792..f9681bed34 100755 --- a/t/t5411-proc-receive-hook.sh +++ b/t/t5411-proc-receive-hook.sh @@ -168,6 +168,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/ + ) +' + test_expect_success "no proc-receive hook, fail to push special ref" ' ( cd workbench && @@ -669,4 +677,200 @@ test_expect_success "push with options" ' test_cmp expect actual ' +test_expect_success "cleanup" ' + ( + cd upstream && + git 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/for/next/topic ok" \ + -r "$ZERO_OID $A refs/review/a/b/c/topic ok" \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF + chmod a+x upstream/hooks/proc-receive +' + +test_expect_success "report update of all special refs" ' + ( + cd workbench && + git push origin \ + HEAD:refs/for/next/topic \ + HEAD:refs/review/a/b/c/topic \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_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/for/next/topic ok + remote: proc-receive> refs/review/a/b/c/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 ../upstream + * [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 && + ( + cd upstream && + git show-ref + ) >out && + format_git_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 +' + +test_expect_success "report mixed refs update (head first)" ' + ( + cd workbench && + git push origin \ + HEAD:refs/heads/zzz \ + HEAD:refs/for/next/topic \ + HEAD:refs/heads/yyy \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/zzz + remote: pre-receive< refs/for/next/topic + remote: pre-receive< refs/heads/yyy + 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/for/next/topic + remote: post-receive< refs/for/master/topic + remote: post-receive< refs/heads/zzz + remote: post-receive< refs/heads/yyy + To ../upstream + * [new branch] HEAD -> zzz + * [new reference] HEAD -> refs/for/next/topic + * [new branch] HEAD -> yyy + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/yyy + refs/heads/zzz + EOF + test_cmp expect actual +' + +test_expect_success "cleanup" ' + ( + cd upstream && + git update-ref -d refs/heads/yyy && + git update-ref -d refs/heads/zzz + ) +' + +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/review/a/b/c/topic ok" \ + -r "$ZERO_OID $A refs/for/master/topic ok" + EOF + chmod a+x upstream/hooks/proc-receive +' + +test_expect_success "report mixed refs update (special ref first)" ' + ( + cd workbench && + git push origin \ + HEAD:refs/for/next/topic \ + $B:refs/heads/zzz \ + HEAD:refs/review/a/b/c/topic \ + HEAD:refs/heads/yyy \ + HEAD:refs/for/master/topic + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/for/next/topic + remote: pre-receive< refs/heads/zzz + remote: pre-receive< refs/review/a/b/c/topic + remote: pre-receive< refs/heads/yyy + 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/for/next/topic ok + remote: proc-receive> refs/review/a/b/c/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/heads/zzz + remote: post-receive< refs/heads/yyy + remote: post-receive< refs/review/a/b/c/topic + remote: post-receive< refs/for/master/topic + To ../upstream + * [new reference] HEAD -> refs/for/next/topic + * [new branch] -> zzz + * [new reference] HEAD -> refs/review/a/b/c/topic + * [new branch] HEAD -> yyy + * [new reference] HEAD -> refs/for/master/topic + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/yyy + refs/heads/zzz + EOF + test_cmp expect actual +' + test_done From patchwork Sun Mar 22 13:18:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Xin X-Patchwork-Id: 11451795 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 CB1F91893 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A9FD5206C3 for ; Sun, 22 Mar 2020 13:18:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hbwQyprC" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726797AbgCVNSd (ORCPT ); Sun, 22 Mar 2020 09:18:33 -0400 Received: from mail-pf1-f195.google.com ([209.85.210.195]:45698 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726623AbgCVNSc (ORCPT ); Sun, 22 Mar 2020 09:18:32 -0400 Received: by mail-pf1-f195.google.com with SMTP id j10so6053771pfi.12 for ; Sun, 22 Mar 2020 06:18:30 -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=PESyKR4iPcYjgIHxcQV4H+XVpUpKr0EsxBPuGFVqUdY=; b=hbwQyprC/+E7LOUBwOwOEG35giz9NY56wDwOtTAkvSnpERoxjgev9npNfTwdkrC1N4 ylRrlaph7DUd2+nZxhC82yLtUE88eDl9k4o3BJf/Y++/Yu//ksZ+6NojT5hrxZoP4nOh anPX/vdQgcOvt1l7+/0KMtdcXmK1Dtq9tCC/PNmumPN8JPLZ4EZGLAHOf1qSDyfLZAxc WI2PJDsXPFq+bTaYrr7k6s+ne2MtNYYMngwukuZ6ULskn7KUhA2qMEeJ5SfK9cRFZi5+ FSHTrJgavuzP+H1eh3PwBKXgG5wtWyNquoX+7JSRwAlb+XCm8yFSDVnezwIaJO9mu+aO qeLA== 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=PESyKR4iPcYjgIHxcQV4H+XVpUpKr0EsxBPuGFVqUdY=; b=jjcFySo3nWZdinkuYOkSZRYmBQWFDQojyTF5cZDXD5W0yczKVPR2iY4j6YjkStYjE0 PXGwAE1cXfhBHPuXVaVyLAIZlHx7SNPRkl95R/I0fu1gZnw9uo1s6u27uuaCS9LukPRj a1io420BlLIZymoRUDAVnQvwbTApxStQfnQ0xWw65Xzfba7Qli38bbdoTTUlXqoXRu30 O4AajcSYk84bL0pSwc4EueV5mJEmSgYZai22LywWjOCJRPJf32fX29HXOrdE4CQRX8gi aZMw/g3u3sVCSQ+kFcoijCoifWEOknHgwwG7qRSnQWYqG6M1LNwlc4TTsl4cvsNoHE3k i/Rw== X-Gm-Message-State: ANhLgQ2khIKbquL4oMUiJFnRxeqSsNJapWSk7jNT4WkWXl1QoZMxV9S6 UQJ91figwjF2kUWT/n9tTOQ= X-Google-Smtp-Source: ADFU+vtdUee6K8m/8MRMa+YWTJk3U0CT6PjQIecPGs11CCwAXYuJK4V3azT5f+D4ry8q+T8TxpUhBg== X-Received: by 2002:a63:fd0d:: with SMTP id d13mr15400329pgh.302.1584883109813; Sun, 22 Mar 2020 06:18:29 -0700 (PDT) Received: from tigtog.localdomain.localdomain ([144.34.163.219]) by smtp.gmail.com with ESMTPSA id nh4sm9456432pjb.39.2020.03.22.06.18.29 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Mar 2020 06:18:29 -0700 (PDT) From: Jiang Xin To: Junio C Hamano , Git List Cc: Jiang Xin Subject: [PATCH v4 5/5] receive-pack: refactor report for proc-receive Date: Sun, 22 Mar 2020 09:18:15 -0400 Message-Id: <20200322131815.11872-6-worldhello.net@gmail.com> X-Mailer: git-send-email 2.26.0.rc0 In-Reply-To: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.com> References: <20200313122318.78000-1-zhiyou.jx@alibaba-inc.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. Signed-off-by: Jiang Xin --- builtin/receive-pack.c | 21 +++++++-- t/t5411-proc-receive-hook.sh | 90 +++++++++++++++++++++++++++++++++--- transport.c | 27 +++++++---- 3 files changed, 119 insertions(+), 19 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index fef97e6985..c791f562d0 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -326,6 +326,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:1; @@ -883,7 +884,12 @@ static int read_proc_receive_result(struct packet_reader *reader, else cmd->error_string = "failed"; code = 1; - } else if (strcmp("ok", status)) { + } else if (!strcmp("ok", status)) { + cmd->extra_string = xstrdup_or_null(msg); + } else if (!strcmp("ft", status)) { + /* Reset "run_proc_receive" field, and continue to run in "receive-pack" */ + cmd->run_proc_receive = 0; + } else { die("protocol error: proc-receive has bad status '%s' for '%s'", status, reader->line); } @@ -2190,12 +2196,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 f9681bed34..917988ab57 100755 --- a/t/t5411-proc-receive-hook.sh +++ b/t/t5411-proc-receive-hook.sh @@ -691,9 +691,9 @@ test_expect_success "setup proc-receive hook" ' 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/next/topic ok ref:refs/pull/123/head" \ -r "$ZERO_OID $A refs/review/a/b/c/topic ok" \ - -r "$ZERO_OID $A refs/for/master/topic ok" + -r "$ZERO_OID $A refs/for/master/topic ok ref:refs/pull/124/head" EOF chmod a+x upstream/hooks/proc-receive ' @@ -716,17 +716,17 @@ test_expect_success "report update of all special refs" ' 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/for/next/topic ok + remote: proc-receive> refs/for/next/topic ok ref:refs/pull/123/head remote: proc-receive> refs/review/a/b/c/topic ok - remote: proc-receive> refs/for/master/topic ok + 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 ../upstream - * [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 && ( @@ -873,4 +873,82 @@ test_expect_success "report mixed refs update (special ref first)" ' test_cmp expect actual ' +test_expect_success "config receive.procReceiveRefs for all ref/" ' + ( + cd upstream && + git 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 "$A $ZERO_OID refs/heads/yyy ft" \ + -r "$B $A refs/heads/zzz ft" \ + -r "$A $B refs/for/master/topic ok ref:refs/pull/123/head" \ + -r "$A $B refs/heads/master ft" \ + -r "$B $A refs/for/next/topic ok ref:refs/pull/124/head" + EOF + chmod a+x upstream/hooks/proc-receive +' + +test_expect_success "report test: fallthrough" ' + ( + cd workbench && + git push -f origin \ + :refs/heads/yyy \ + $A:refs/heads/zzz \ + HEAD:refs/for/master/topic \ + HEAD:refs/for/next/topic \ + $B:refs/heads/master + ) >out 2>&1 && + format_git_output actual && + cat >expect <<-EOF && + remote: # pre-receive hook + remote: pre-receive< refs/heads/master + remote: pre-receive< refs/heads/yyy + remote: pre-receive< refs/heads/zzz + remote: pre-receive< refs/for/master/topic + remote: pre-receive< refs/for/next/topic + remote: # proc-receive hook + remote: proc-receive< refs/heads/master + remote: proc-receive< refs/heads/yyy + remote: proc-receive< refs/heads/zzz + remote: proc-receive< refs/for/master/topic + remote: proc-receive< refs/for/next/topic + remote: proc-receive> refs/heads/yyy ft + remote: proc-receive> refs/heads/zzz ft + remote: proc-receive> refs/for/master/topic ok ref:refs/pull/123/head + remote: proc-receive> refs/heads/master ft + remote: proc-receive> refs/for/next/topic ok ref:refs/pull/124/head + remote: # post-receive hook + remote: post-receive< refs/heads/yyy + remote: post-receive< refs/heads/zzz + remote: post-receive< refs/for/master/topic + remote: post-receive< refs/heads/master + remote: post-receive< refs/for/next/topic + To ../upstream + 1029397..ce858e6 -> master + - [deleted] yyy + + ce858e6...1029397 -> zzz (forced update) + * [new reference] HEAD -> refs/pull/123/head + * [new reference] HEAD -> refs/pull/124/head + EOF + test_cmp expect actual && + ( + cd upstream && + git show-ref + ) >out && + format_git_output actual && + cat >expect <<-EOF && + refs/heads/master + refs/heads/zzz + EOF + test_cmp expect actual +' + test_done diff --git a/transport.c b/transport.c index b5b7bb841e..d51af11d48 100644 --- a/transport.c +++ b/transport.c @@ -463,11 +463,16 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain, int summary_width) { + char *to_name = to->name; + + if (to->remote_status && !strncmp("ref:", to->remote_status, 4)) + to_name = to->remote_status + 4; + 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, to_name); else - fprintf(stdout, "%c\t:%s\t", flag, to->name); + fprintf(stdout, "%c\t:%s\t", flag, to_name); if (msg) fprintf(stdout, "%s (%s)\n", summary, msg); else @@ -481,9 +486,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(to_name)); else - fputs(prettify_refname(to->name), stderr); + fputs(prettify_refname(to_name), stderr); if (msg) { fputs(" (", stderr); fputs(msg, stderr); @@ -498,13 +503,19 @@ static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_widt if (ref->deletion) print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain, summary_width); - else if (is_null_oid(&ref->old_oid)) + else if (is_null_oid(&ref->old_oid)) { + char *refname; + + if (ref->remote_status && !strncmp(ref->remote_status, "ref:", 4)) + refname = ref->remote_status + 4; + else + refname = ref->name; print_ref_status('*', - (starts_with(ref->name, "refs/tags/") ? "[new tag]" : - (starts_with(ref->name, "refs/heads/") ? "[new branch]" : + (starts_with(refname, "refs/tags/") ? "[new tag]" : + (starts_with(refname, "refs/heads/") ? "[new branch]" : "[new reference]")), ref, ref->peer_ref, NULL, porcelain, summary_width); - else { + } else { struct strbuf quickref = STRBUF_INIT; char type; const char *msg;