From patchwork Thu Mar 11 02:10:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130075 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D3427C433E0 for ; Thu, 11 Mar 2021 02:11:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A2C4764FAA for ; Thu, 11 Mar 2021 02:11:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229704AbhCKCLD (ORCPT ); Wed, 10 Mar 2021 21:11:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42140 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229544AbhCKCKx (ORCPT ); Wed, 10 Mar 2021 21:10:53 -0500 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 79972C061760 for ; Wed, 10 Mar 2021 18:10:53 -0800 (PST) Received: by mail-qk1-x749.google.com with SMTP id g18so14331931qki.15 for ; Wed, 10 Mar 2021 18:10:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=B9BO0k8weuBoUdor5eYmxBnJyAyB0QuaCNZCptqko1s=; b=EAyaDV3PAGbiHz9RN3MGdDN+KLWlLjY8hv8JDDdqQJbIQ3De6DtLk822SvjnMnMCPm 3ARYdcQQjR0V/ByiqwzoWn14JjZ1eZ6TDWJJj6atXt9GTMNUXqC/73N/9jKLUmynbadP wpL3ZJ7vmasUvz30lH3/SYERdI5eBb+noRCX1vfGvqMtX1CnVQZHHE/eHWfdhfDrT685 O0kstV48Q3fD286mOKIt27KqkYkM7Zyx5urO37XQYO/a9P/jQQs35ggwZdNQhSBrf45C cabwUxOXsWOzCcV6VBxIedD5xuzupt7ms3/WriqMByDmGCMCIUfg4U0872RporoHkQ63 WdDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=B9BO0k8weuBoUdor5eYmxBnJyAyB0QuaCNZCptqko1s=; b=Tu7cRtUaNANcOaUuXRmd5OBOnj7U/eBR4TCJyLpOoHIy8pytsmKHG6YwyGAQJvjQi1 XC/KSpHsQ6AH9cFpWDN+N+apwVZFNKu3ehII4Vk0sAdgDdRblpTXScp9yIq7DQbQBHON WMaIPJo0sVePeDleU8Y+8bGMYtUmIwv8ekzuGssY1Xgrt3wCi9wGzcZ5JGznBZOg/Ll1 c3O3W/flxTJWP3JQ6yGie3mHKV57oiv2zfrN1YSBBj59HqzTQagpEfR3KAzf7zqa+Se2 cDfNliOwHKy2fI6z2NS5+zr8OTfosWKtKhQ0vJrnImUjdXjtA7u0DSDt14zO95tptkfb erLA== X-Gm-Message-State: AOAM530vNDTQxbt+NBQYtSUmNi+xRXdWY/d8eoYHAWQywQF93+ermZrt W+zw4YEiNNKGDe2r+M1zvjdFvxgo8Fnc2XLtPrptNOLU/7TfO9VhLkBE5QZdSNmxkaUmZ8Sqs2I /fFuwr5d4TyWM5DBmKBTgDeUGJQv7cuvnnikYcv+iX51eHOQYg8JLvXGlTfcKz56c+LtEkGiIkA == X-Google-Smtp-Source: ABdhPJzK0e+hGDH+9scBhT6sABlsah744TEOJZJy2cEyqmZTuoy7G2QuMd0ymwtJZ2BCtPADGqiILOb174N9zOSv9O0= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:b929:: with SMTP id u41mr5776784qvf.30.1615428652653; Wed, 10 Mar 2021 18:10:52 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:01 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-2-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 01/37] doc: propose hooks managed by the config From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Begin a design document for config-based hooks, managed via git-hook. Focus on an overview of the implementation and motivation for design decisions. Briefly discuss the alternatives considered before this point. Also, attempt to redefine terms to fit into a multihook world. Signed-off-by: Emily Shaffer --- Notes: Since v7, made some wording changes based on reviewer comments (mostly Junio's, I think). - Emily Documentation/Makefile | 1 + .../technical/config-based-hooks.txt | 369 ++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 Documentation/technical/config-based-hooks.txt diff --git a/Documentation/Makefile b/Documentation/Makefile index 81d1bf7a04..2743de8995 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -82,6 +82,7 @@ SP_ARTICLES += $(API_DOCS) TECH_DOCS += MyFirstContribution TECH_DOCS += MyFirstObjectWalk TECH_DOCS += SubmittingPatches +TECH_DOCS += technical/config-based-hooks TECH_DOCS += technical/hash-function-transition TECH_DOCS += technical/http-protocol TECH_DOCS += technical/index-format diff --git a/Documentation/technical/config-based-hooks.txt b/Documentation/technical/config-based-hooks.txt new file mode 100644 index 0000000000..1f973117e4 --- /dev/null +++ b/Documentation/technical/config-based-hooks.txt @@ -0,0 +1,369 @@ +Configuration-based hook management +=================================== +:sectanchors: + +[[motivation]] +== Motivation + +Replace the `.git/hook/hookname` path as the only source of hooks to execute; +allow users to define hooks using config files, in a way which is friendly to +users with multiple repos which have similar needs - hooks can be easily shared +between multiple Git repos. + +Redefine "hook" as an event rather than a single script, allowing users to +perform multiple unrelated actions on a single event. + +Make it easier for users to discover Git's hook feature and automate their +workflows. + +[[user-interfaces]] +== User interfaces + +[[config-schema]] +=== Config schema + +Hooks can be introduced by editing the configuration manually. There are two new +sections added, `hook` and `hookcmd`. + +[[config-schema-hook]] +==== `hook` + +Primarily contains subsections for each hook event. The order of variables in +these subsections defines the hook command execution order; hook commands can be +specified by setting the value directly to the command if no additional +configuration is needed, or by setting the value as the name of a `hookcmd`. If +Git does not find a `hookcmd` whose subsection matches the value of the given +command string, Git will try to execute the string directly. Hooks are executed +by passing the resolved command string to the shell. In the future, hook event +subsections could also contain per-hook-event settings; see +<> for more details. + +Also contains top-level hook execution settings, for example, `hook.runHookDir`. +(These settings are described more in <>.) + +---- +[hook "pre-commit"] + command = perl-linter + command = /usr/bin/git-secrets --pre-commit + +[hook "pre-applypatch"] + command = perl-linter + # for illustration purposes; error behavior isn't planned yet + error = ignore + +[hook] + runHookDir = interactive +---- + +[[config-schema-hookcmd]] +==== `hookcmd` + +Defines a hook command and its attributes, which will be used when a hook event +occurs. Unqualified attributes are assumed to apply to this hook during all hook +events, but event-specific attributes can also be supplied. The example runs +`/usr/bin/lint-it --language=perl `, but for repos which +include this config, the hook command will be skipped for all events. +Theoretically, the last line could be used to "un-skip" the hook command for +`pre-commit` hooks, but this hasn't been scoped or implemented yet. + +---- +[hookcmd "perl-linter"] + command = /usr/bin/lint-it --language=perl + skip = true + # for illustration purposes; below hasn't been defined yet + pre-commit-skip = false +---- + +[[command-line-api]] +=== Command-line API + +Users should be able to view, run, reorder, and create hook commands via the +command line. External tools should be able to view a list of hooks in the +correct order to run. Modifier commands (`edit` and `add`) have not been +implemented yet and may not be if manually editing the config proves usable +enough. + +*`git hook list `* + +*`git hook run [-a ]... [-e ]...`* + +*`git hook edit `* + +*`git hook add `* + +[[hook-editor]] +=== Hook editor + +The tool which is presented by `git hook edit `. Ideally, this +tool should be easier to use than manually editing the config, and then produce +a concise config afterwards. It may take a form similar to `git rebase +--interactive`. This has not been designed or implemented yet and may not be if +the config proves usable enough. + +[[implementation]] +== Implementation + +[[library]] +=== Library + +`hook.c` and `hook.h` are responsible for interacting with the config files. The +hook library provides a basic API to call all hooks in config order with more +complex options passed via `struct run_hooks_opt`: + +*`int run_hooks(const char *hookname, struct run_hooks_opt *options)`* + +`struct run_hooks_opt` allows callers to set: + +- environment variables +- command-line arguments +- behavior for the hook command provided by `run-command.h:find_hook()` (see + below) +- a method to provide stdin to each hook, either via a file containing stdin, a + `struct string_list` containing a list of lines to print, or a callback + function to allow the caller to populate stdin manually +- a method to process stdout from each hook, e.g. for printing to sideband + during a network operation +- parallelism +- a custom working directory for hooks to execute in + +And this struct can be extended with more options as necessary in the future. + +The "legacy" hook provided by `run-command.h:find_hook()` - that is, the hook +present in `.git/hooks/` or +`$(git config --get core.hooksPath)/` - can be handled in a number of +ways, providing an avenue to deprecate these "legacy" hooks if desired. The +handling is based on a config `hook.runHookDir`, which is checked against a +number of cases: + +- "no": the legacy hook will not be run +- "error": Git will print a warning to stderr before ignoring the legacy hook +- "interactive": Git will prompt the user before running the legacy hook +- "warn": Git will print a warning to stderr before running the legacy hook +- "yes" (default): Git will silently run the legacy hook + +In case this list is expanded in the future, if a value for `hook.runHookDir` is +given which Git does not recognize, Git should discard that config entry. For +example, if "warn" was specified at system level and "junk" was specified at +global level, Git would resolve the value to "warn"; if the only time the config +was set was to "junk", Git would use the default value of "yes" (but print a +warning to the user first to let them know their value is wrong). + +`struct hookcmd` is expected to grow in size over time as more functionality is +added to hooks; so that other parts of the code don't need to understand the +config schema, `struct hookcmd` should contain logical values instead of string +pairs. + +By default, hook parallelism is chosen based on the semantics of each hook; +callsites initialize their `struct run_hooks_opt` via one of two macros, +`RUN_HOOKS_OPT_INIT_SYNC` or `RUN_HOOKS_OPT_INIT_ASYNC`. The default number of +jobs can be configured in `hook.jobs`; this config applies across all hook +events. If unset, the value of `online_cpus()` (equivalent to `nproc`) is used. + +[[builtin]] +=== Builtin + +`builtin/hook.c` is responsible for providing the frontend. It's responsible for +formatting user-provided data and then calling the library API to set the +configs as appropriate. The builtin frontend is not responsible for calling the +config directly, so that other areas of Git can rely on the hook library to +understand the most recent config schema for hooks. + +[[migration]] +=== Migration path + +[[stage-0]] +==== Stage 0 + +Hooks are called by running `run-command.h:find_hook()` with the hookname and +executing the result. The hook library and builtin do not exist. Hooks only +exist as specially named scripts within `.git/hooks/`. + +[[stage-1]] +==== Stage 1 + +`git hook list --porcelain ` is implemented. `hook.h:run_hooks()` is +taught to include `run-command.h:find_hook()` at the end; calls to `find_hook()` +are replaced with calls to `run_hooks()`. Users can opt-in to config-based hooks +simply by creating some in their config; otherwise users should remain +unaffected by the change. + +[[stage-2]] +==== Stage 2 + +The call to `find_hook()` inside of `run_hooks()` learns to check for a config, +`hook.runHookDir`. Users can opt into managing their hooks completely via the +config this way. + +[[stage-3]] +==== Stage 3 + +`.git/hooks` is removed from the template and the hook directory is considered +deprecated. To avoid breaking older repos, the default of `hook.runHookDir` is +not changed, and `find_hook()` is not removed. + +[[caveats]] +== Caveats + +[[security]] +=== Security and repo config + +Part of the motivation behind this refactor is to mitigate hooks as an attack +vector.footnote:[https://lore.kernel.org/git/20171002234517.GV19555@aiede.mtv.corp.google.com/] +However, as the design stands, users can still provide hooks in the repo-level +config, which is included when a repo is zipped and sent elsewhere. The +security of the repo-level config is still under discussion; this design +generally assumes the repo-level config is secure, which is not true yet. This +assumption was made to avoid overcomplicating the design. So, this series +doesn't particularly improve security or resistance to zip attacks. + +[[ease-of-use]] +=== Ease of use + +The config schema is nontrivial; that's why it's important for the `git hook` +modifier commands to be usable. Contributors with UX expertise are encouraged to +share their suggestions. + +[[alternatives]] +== Alternative approaches + +A previous summary of alternatives exists in the +archives.footnote:[https://lore.kernel.org/git/20191116011125.GG22855@google.com] + +The table below shows a number of goals and how they might be achieved with +config-based hooks, by implementing directory support (i.e. +'.git/hooks/pre-commit.d'), or as hooks are run today. + +.Comparison of alternatives +|=== +|Feature |Config-based hooks |Hook directories |Status quo + +|Supports multiple hooks +|Natively +|Natively +|With user effort + +|Supports parallelization +|Natively +|Natively +|No (user's multihook trampoline script would need to handle parallelism) + +|Safer for zipped repos +|A little +|No +|No + +|Previous hooks just work +|If configured +|Yes +|Yes + +|Can install one hook to many repos +|Yes +|With symlinks or core.hooksPath +|With symlinks or core.hooksPath + +|Discoverability +|Findable with 'git help git' or tab-completion via 'git hook' subcommand +|Findable via improved documentation +|Same as before + +|Hard to run unexpected hook +|If configured +|Could be made to warn or look for a config +|No +|=== + +[[status-quo]] +=== Status quo + +Today users can implement multihooks themselves by using a "trampoline script" +as their hook, and pointing that script to a directory or list of other scripts +they wish to run. + +[[hook-directories]] +=== Hook directories + +Other contributors have suggested Git learn about the existence of a directory +such as `.git/hooks/.d` and execute those hooks in alphabetical order. + +[[future-work]] +== Future work + +[[execution-ordering]] +=== Execution ordering + +We may find that config order is insufficient for some users; for example, +config order makes it difficult to add a new hook to the system or global config +which runs at the end of the hook list. A new ordering schema should be: + +1) Specified by a `hook.order` config, so that users will not unexpectedly see +their order change; + +2) Either dependency or numerically based. + +Dependency-based ordering is prone to classic linked-list problems, like a +cycles and handling of missing dependencies. But, it paves the way for enabling +parallelization if some tasks truly depend on others. + +Numerical ordering makes it tricky for Git to generate suggested ordering +numbers for each command, but is easy to determine a definitive order. + +[[parallelization]] +=== Parallelization with dependencies + +Currently hooks use a naive parallelization scheme or are run in series. But if +one hook depends on another's output, then users will want to specify those +dependencies. If we decide to solve this problem, we may want to look to modern +build systems for inspiration on how to manage dependencies and parallel tasks. + +[[nontrivial-hooks]] +=== Multihooks and nontrivial output + +Some hooks - like 'proc-receive' - don't lend themselves well to multihooks at +all. In the case of 'proc-receive', for now, multiple hook definitions are +disallowed. In the future we might be able to conceive a better approach, for +example, running the hooks in series and using the output from one hook as the +input to the next. + +[[securing-hookdir-hooks]] +=== Securing hookdir hooks + +With the design as written in this doc, it's still possible for a malicious user +to modify `.git/config` to include `hook.pre-receive.command = rm -rf /`, then +zip their repo and send it to another user. It may be necessary to teach Git to +only allow inlined hooks like this if they were configured outside of the local +scope (in other words, only run hookcmds, and only allow hookcmds to be +configured in global or system scope); or another approach, like a list of safe +projects, might be useful. It may also be sufficient (or at least useful) to +teach a `hook.disableAll` config or similar flag to the Git executable. + +[[submodule-inheritance]] +=== Submodule inheritance + +It's possible some submodules may want to run the identical set of hooks that +their superrepo runs. While a globally-configured hook set is helpful, it's not +a great solution for users who have multiple repos-with-submodules under the +same user. It would be useful for submodules to learn how to run hooks from +their superrepo's config, or inherit that hook setting. + +[[per-hook-event-settings]] +=== Per-hook-event settings + +It might be desirable to keep settings specifically for some hook events, but +not for others - for example, a user may wish to disable hookdir hooks for all +events but pre-commit, which they haven't had time to convert yet; or, a user +may wish for execution order settings to differ based on hook event. In that +case, it would be useful to set something like `hook.pre-commit.executionOrder` +which would not apply to the 'prepare-commit-msg' hook, for example. + +[[glossary]] +== Glossary + +*hook event* + +A point during Git's execution where user scripts may be run, for example, +_prepare-commit-msg_ or _pre-push_. + +*hook command* + +A user script or executable which will be run on one or more hook events. From patchwork Thu Mar 11 02:10:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130087 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 99AC5C433E9 for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6360C64FC5 for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229787AbhCKCLh (ORCPT ); Wed, 10 Mar 2021 21:11:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42148 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229741AbhCKCLG (ORCPT ); Wed, 10 Mar 2021 21:11:06 -0500 Received: from mail-qv1-xf4a.google.com (mail-qv1-xf4a.google.com [IPv6:2607:f8b0:4864:20::f4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6BF07C061760 for ; Wed, 10 Mar 2021 18:10:55 -0800 (PST) Received: by mail-qv1-xf4a.google.com with SMTP id bt20so5266329qvb.0 for ; Wed, 10 Mar 2021 18:10:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Hsejj3QOv3VNOHJVFzz1trgAk7BJM3wGQFZt6KLQh7g=; b=v7f/44R1N/z6muOWI9QV6DmRUcdbUNAgKTY42JgIj3k6PKXLDPOQNDi65wl8AUqIJ+ UIuF9Xl1/+3hWKQ8KHR7UNRKgv5C2/Bf7zlz5UpDTXnou1oTb7Ij27mKSLMuHXbUFSPk qKcTyR00nxGmMwYzhKaSZZPCZ+TXmseY7K2+PVGdcwd0MlApoKYLs28DT1Qfknw2XMbV laef/B4E/N7ySZTUOOUkttlMxxElU8NdVsTPdp/i9NZrSl317jKDvnswZvyEG86IK463 UcST90VCUngwsooRvS8EMTM4ux20xTrMyLiW52QRE8EzJzGyap0lROr+okiGHnB73FHN PAMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Hsejj3QOv3VNOHJVFzz1trgAk7BJM3wGQFZt6KLQh7g=; b=hFcHr2mCHVqWHs64EEFQmTXv+KFiIODHZIZnf1O9WpOB/zVPGXu8M7Vx/Nru4Udluk IofQx5xky0ttSGDg0vEi3Y8Zl6rwFNjJ2vH01AAMKEbbpnC50C3j9z8K+5+YzWjux8ZA 0LaUXLieHRr4m+oe8RYTt5RSmvnvdfqSPKnP09IVadGy3tcApkS+23DhgE92cHxlI6wx 81EEwzlD8KWRDlCxh8jbXXwsI4eied2SZjoaIkx/vZpcTM5uBIygUy6O4e7miI7nhZGu CtDvREgMsrD4nbf6Kf6RTIOjGi6IkRDk9R5ru3Qla8OucYCEoyOuGuQShI+QHe2Rrxki kJ7Q== X-Gm-Message-State: AOAM531VTFSwZG0uYmbCkz6SyR6ClhDgBAP5JGNFclbz6YG8+qm4yZ4Y m20A8pa1OVm9pBKi67M0CawaqB6fGFEIgDtPcW20vGO7YlBARVGsgUCEAAtfwTX49FesMEiWdZ2 AC8EsL3fsJ9/RHf/httAT8flbnVppnCRdf7Ld5nW2JXqDBsRKNLKHV2r0OMHl9jOqbbKgaYxbKw == X-Google-Smtp-Source: ABdhPJwVVgzr3qSJA7kFxqheVhZj1hoz1Y+SDwy53vRJW0QgYBb5MvmwaiEivay/T3Op/1A53zIh3PRebszsqCG2u8U= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:ad4:4904:: with SMTP id bh4mr5598311qvb.53.1615428654491; Wed, 10 Mar 2021 18:10:54 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:02 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-3-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 02/37] hook: scaffolding for git-hook subcommand From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Introduce infrastructure for a new subcommand, git-hook, which will be used to ease config-based hook management. This command will handle parsing configs to compose a list of hooks to run for a given event, as well as adding or modifying hook configs in an interactive fashion. Signed-off-by: Emily Shaffer --- No change since v7. .gitignore | 1 + Documentation/git-hook.txt | 20 ++++++++++++++++++++ Makefile | 1 + builtin.h | 1 + builtin/hook.c | 21 +++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + t/t1360-config-based-hooks.sh | 11 +++++++++++ 8 files changed, 57 insertions(+) create mode 100644 Documentation/git-hook.txt create mode 100644 builtin/hook.c create mode 100755 t/t1360-config-based-hooks.sh diff --git a/.gitignore b/.gitignore index 3dcdb6bb5a..3608c35b73 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,7 @@ /git-grep /git-hash-object /git-help +/git-hook /git-http-backend /git-http-fetch /git-http-push diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt new file mode 100644 index 0000000000..9eeab0009d --- /dev/null +++ b/Documentation/git-hook.txt @@ -0,0 +1,20 @@ +git-hook(1) +=========== + +NAME +---- +git-hook - Manage configured hooks + +SYNOPSIS +-------- +[verse] +'git hook' + +DESCRIPTION +----------- +A placeholder command. Later, you will be able to list, add, and modify hooks +with this command. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Makefile b/Makefile index dfb0f1000f..8e904a1ab5 100644 --- a/Makefile +++ b/Makefile @@ -1087,6 +1087,7 @@ BUILTIN_OBJS += builtin/get-tar-commit-id.o BUILTIN_OBJS += builtin/grep.o BUILTIN_OBJS += builtin/hash-object.o BUILTIN_OBJS += builtin/help.o +BUILTIN_OBJS += builtin/hook.o BUILTIN_OBJS += builtin/index-pack.o BUILTIN_OBJS += builtin/init-db.o BUILTIN_OBJS += builtin/interpret-trailers.o diff --git a/builtin.h b/builtin.h index b6ce981b73..8df1d36a7a 100644 --- a/builtin.h +++ b/builtin.h @@ -163,6 +163,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix); int cmd_grep(int argc, const char **argv, const char *prefix); int cmd_hash_object(int argc, const char **argv, const char *prefix); int cmd_help(int argc, const char **argv, const char *prefix); +int cmd_hook(int argc, const char **argv, const char *prefix); int cmd_index_pack(int argc, const char **argv, const char *prefix); int cmd_init_db(int argc, const char **argv, const char *prefix); int cmd_interpret_trailers(int argc, const char **argv, const char *prefix); diff --git a/builtin/hook.c b/builtin/hook.c new file mode 100644 index 0000000000..b2bbc84d4d --- /dev/null +++ b/builtin/hook.c @@ -0,0 +1,21 @@ +#include "cache.h" + +#include "builtin.h" +#include "parse-options.h" + +static const char * const builtin_hook_usage[] = { + N_("git hook"), + NULL +}; + +int cmd_hook(int argc, const char **argv, const char *prefix) +{ + struct option builtin_hook_options[] = { + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, builtin_hook_options, + builtin_hook_usage, 0); + + return 0; +} diff --git a/command-list.txt b/command-list.txt index a289f09ed6..9ccd8e5aeb 100644 --- a/command-list.txt +++ b/command-list.txt @@ -103,6 +103,7 @@ git-grep mainporcelain info git-gui mainporcelain git-hash-object plumbingmanipulators git-help ancillaryinterrogators complete +git-hook mainporcelain git-http-backend synchingrepositories git-http-fetch synchelpers git-http-push synchelpers diff --git a/git.c b/git.c index 9bc077a025..14adac716f 100644 --- a/git.c +++ b/git.c @@ -528,6 +528,7 @@ static struct cmd_struct commands[] = { { "grep", cmd_grep, RUN_SETUP_GENTLY }, { "hash-object", cmd_hash_object }, { "help", cmd_help }, + { "hook", cmd_hook, RUN_SETUP_GENTLY }, { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT }, { "init", cmd_init_db }, { "init-db", cmd_init_db }, diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh new file mode 100755 index 0000000000..34b0df5216 --- /dev/null +++ b/t/t1360-config-based-hooks.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +test_description='config-managed multihooks, including git-hook command' + +. ./test-lib.sh + +test_expect_success 'git hook command does not crash' ' + git hook +' + +test_done From patchwork Thu Mar 11 02:10:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130093 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65DBBC4332D for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1B53464FCE for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229804AbhCKCLi (ORCPT ); Wed, 10 Mar 2021 21:11:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42158 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229747AbhCKCLI (ORCPT ); Wed, 10 Mar 2021 21:11:08 -0500 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 110DAC061761 for ; Wed, 10 Mar 2021 18:10:57 -0800 (PST) Received: by mail-qk1-x74a.google.com with SMTP id c1so14346916qke.8 for ; Wed, 10 Mar 2021 18:10:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=/v6QJ8Vi69yDpIyUgrQx1IJrINit+yIQWtxF4KHWiV8=; b=VW5VbBXtkHR6llRiHEtNpNgIv/D8/ySP6uOLUIarV2z2DBBrpQxpfnvW1dUWKCWTyh Wvz91ejbweVz3IKkwcYJCWOUHsrcICsPoM9CLJOdeFmK0x7c6iB51ol2CTdtBmj9DQLz Gidv7ihOQ8Ns7PYUR5giUFTjjGUKqDY2oP9VR2MeAnrhDlvQf5bm/DIwEh+mx7CupoOp WJlN9usOzH9qqc+HDeYLEuGIiqYE36YaWkGyvV3w++LT/NKtgb8ZrygAA4E5HBiss3Da V5upVWZ9TnukCIPMZzuDDpasXBmo37BKpL2mcdw8uT2gigT+exWjl88L1T0UAXGY2rsq icmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=/v6QJ8Vi69yDpIyUgrQx1IJrINit+yIQWtxF4KHWiV8=; b=l8J18/PX/IYrqHlP/t1goncSiRW0rheBXuY0odyN2DLS2R1JNaxmOyah4P7WVPTt5M wiuGFxKQ0tJCIfHbpUr6c/2ile2E7QeZNt37DoygkgY7Rcz/AW/Dv7Wc8xJif0LX1LHI cW1fjfHzwxu6HWp/5XSHIGwtGJUdX1DqM8zsED4GstC9bPCsGpwdlSuy7vvZvnO9dRAH aSRyEq/+mQFrXsJ9pkZo9m3xcWauYQdPYIKKoJBCBtAgTgaLmWKsgqH6kzB+YFrClsgi HBG5Td/H8ZZtEtkyGTHS2v9WFpkzkeCVSGUqXH3oKjutFfGR24dg6htRoB1XewJ1MP5n gjyA== X-Gm-Message-State: AOAM531scFM6+olCVtDPUCgXtKxwv43exxDZs5Nwc7ShJeX7H4GIjw9w 1oYHmaNedJ1t4JflN9XOfXgISwCk42RsYWqz3jhEb0jl5GjboJUMa532JWH0tMBGxNMCws1ibua 0Aq+soriO8wYdwSNyPu6IGUkl3EmhbUVUBWittEvpSnMz3A01eexe+NZAwsrMYdGikVHWgEXohw == X-Google-Smtp-Source: ABdhPJw5VHWaXF6poOm4YJ8lszCDFfvq3e2s5LTNysDN8m3ZTAygMHsLdI1s/kd/3uMmc0PuxcQx9OTtimDoyeGIEJk= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:bd82:: with SMTP id n2mr5766918qvg.62.1615428656205; Wed, 10 Mar 2021 18:10:56 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:03 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-4-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 03/37] hook: add list command From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Teach 'git hook list ', which checks the known configs in order to create an ordered list of hooks to run on a given hook event. Multiple commands can be specified for a given hook by providing multiple "hook..command = " lines. Hooks will be run in config order. If more properties need to be set on a given hook in the future, commands can also be specified by providing "hook..command = ", as well as a "[hookcmd ]" subsection; this subsection should contain a "hookcmd..command = " line. For example: $ git config --list | grep ^hook hook.pre-commit.command=baz hook.pre-commit.command=~/bar.sh hookcmd.baz.command=~/baz/from/hookcmd.sh $ git hook list pre-commit global: ~/baz/from/hookcmd.sh local: ~/bar.sh Signed-off-by: Emily Shaffer --- Notes: Since v7, fixed some nits from Jonathan Tan, one of which revealed a bug in how I was adding hooks to the list. Documentation/config/hook.txt | 9 +++ Documentation/git-hook.txt | 59 ++++++++++++++++- Makefile | 1 + builtin/hook.c | 56 ++++++++++++++-- hook.c | 120 ++++++++++++++++++++++++++++++++++ hook.h | 25 +++++++ t/t1360-config-based-hooks.sh | 81 ++++++++++++++++++++++- 7 files changed, 341 insertions(+), 10 deletions(-) create mode 100644 Documentation/config/hook.txt create mode 100644 hook.c create mode 100644 hook.h diff --git a/Documentation/config/hook.txt b/Documentation/config/hook.txt new file mode 100644 index 0000000000..71449ecbc7 --- /dev/null +++ b/Documentation/config/hook.txt @@ -0,0 +1,9 @@ +hook..command:: + A command to execute during the hook event. This can be an + executable on your device, a oneliner for your shell, or the name of a + hookcmd. See linkgit:git-hook[1]. + +hookcmd..command:: + A command to execute during a hook for which has been specified + as a command. This can be an executable on your device or a oneliner for + your shell. See linkgit:git-hook[1]. diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index 9eeab0009d..f19875ed68 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -8,12 +8,65 @@ git-hook - Manage configured hooks SYNOPSIS -------- [verse] -'git hook' +'git hook' list DESCRIPTION ----------- -A placeholder command. Later, you will be able to list, add, and modify hooks -with this command. +You can list configured hooks with this command. Later, you will be able to run, +add, and modify hooks with this command. + +This command parses the default configuration files for sections `hook` and +`hookcmd`. `hook` is used to describe the commands which will be run during a +particular hook event; commands are run in the order Git encounters them during +the configuration parse (see linkgit:git-config[1]). `hookcmd` is used to +describe attributes of a specific command. If additional attributes don't need +to be specified, a command to run can be specified directly in the `hook` +section; if a `hookcmd` by that name isn't found, Git will attempt to run the +provided value directly. For example: + +Global config +---- + [hook "post-commit"] + command = "linter" + command = "~/typocheck.sh" + + [hookcmd "linter"] + command = "/bin/linter --c" +---- + +Local config +---- + [hook "prepare-commit-msg"] + command = "linter" + [hook "post-commit"] + command = "python ~/run-test-suite.py" +---- + +With these configs, you'd then see: + +---- +$ git hook list "post-commit" +global: /bin/linter --c +global: ~/typocheck.sh +local: python ~/run-test-suite.py + +$ git hook list "prepare-commit-msg" +local: /bin/linter --c +---- + +COMMANDS +-------- + +list ``:: + +List the hooks which have been configured for ``. Hooks appear +in the order they should be run, and print the config scope where the relevant +`hook..command` was specified, not the `hookcmd` (if applicable). +This output is human-readable and the format is subject to change over time. + +CONFIGURATION +------------- +include::config/hook.txt[] GIT --- diff --git a/Makefile b/Makefile index 8e904a1ab5..3fa51597d8 100644 --- a/Makefile +++ b/Makefile @@ -891,6 +891,7 @@ LIB_OBJS += hash-lookup.o LIB_OBJS += hashmap.o LIB_OBJS += help.o LIB_OBJS += hex.o +LIB_OBJS += hook.o LIB_OBJS += ident.o LIB_OBJS += json-writer.o LIB_OBJS += kwset.o diff --git a/builtin/hook.c b/builtin/hook.c index b2bbc84d4d..bb64cd77ca 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -1,21 +1,67 @@ #include "cache.h" - #include "builtin.h" +#include "config.h" +#include "hook.h" #include "parse-options.h" +#include "strbuf.h" static const char * const builtin_hook_usage[] = { - N_("git hook"), + N_("git hook list "), NULL }; -int cmd_hook(int argc, const char **argv, const char *prefix) +static int list(int argc, const char **argv, const char *prefix) { - struct option builtin_hook_options[] = { + struct list_head *head, *pos; + struct strbuf hookname = STRBUF_INIT; + + struct option list_options[] = { OPT_END(), }; - argc = parse_options(argc, argv, prefix, builtin_hook_options, + argc = parse_options(argc, argv, prefix, list_options, builtin_hook_usage, 0); + if (argc < 1) { + usage_msg_opt(_("You must specify a hook event name to list."), + builtin_hook_usage, list_options); + } + + strbuf_addstr(&hookname, argv[0]); + + head = hook_list(&hookname); + + if (list_empty(head)) { + printf(_("no commands configured for hook '%s'\n"), + hookname.buf); + strbuf_release(&hookname); + return 0; + } + + list_for_each(pos, head) { + struct hook *item = list_entry(pos, struct hook, list); + if (item) + printf("%s: %s\n", + config_scope_name(item->origin), + item->command.buf); + } + + clear_hook_list(head); + strbuf_release(&hookname); + return 0; } + +int cmd_hook(int argc, const char **argv, const char *prefix) +{ + struct option builtin_hook_options[] = { + OPT_END(), + }; + if (argc < 2) + usage_with_options(builtin_hook_usage, builtin_hook_options); + + if (!strcmp(argv[1], "list")) + return list(argc - 1, argv + 1, prefix); + + usage_with_options(builtin_hook_usage, builtin_hook_options); +} diff --git a/hook.c b/hook.c new file mode 100644 index 0000000000..fede40e925 --- /dev/null +++ b/hook.c @@ -0,0 +1,120 @@ +#include "cache.h" + +#include "hook.h" +#include "config.h" + +void free_hook(struct hook *ptr) +{ + if (ptr) { + strbuf_release(&ptr->command); + free(ptr); + } +} + +static void append_or_move_hook(struct list_head *head, const char *command) +{ + struct list_head *pos = NULL, *tmp = NULL; + struct hook *to_add = NULL; + + /* + * remove the prior entry with this command; we'll replace it at the + * end. + */ + list_for_each_safe(pos, tmp, head) { + struct hook *it = list_entry(pos, struct hook, list); + if (!strcmp(it->command.buf, command)) { + list_del(pos); + /* we'll simply move the hook to the end */ + to_add = it; + break; + } + } + + if (!to_add) { + /* adding a new hook, not moving an old one */ + to_add = xmalloc(sizeof(*to_add)); + strbuf_init(&to_add->command, 0); + strbuf_addstr(&to_add->command, command); + } + + /* re-set the scope so we show where an override was specified */ + to_add->origin = current_config_scope(); + + list_add_tail(&to_add->list, head); +} + +static void remove_hook(struct list_head *to_remove) +{ + struct hook *hook_to_remove = list_entry(to_remove, struct hook, list); + list_del(to_remove); + free_hook(hook_to_remove); +} + +void clear_hook_list(struct list_head *head) +{ + struct list_head *pos, *tmp; + list_for_each_safe(pos, tmp, head) + remove_hook(pos); +} + +struct hook_config_cb +{ + struct strbuf *hookname; + struct list_head *list; +}; + +static int hook_config_lookup(const char *key, const char *value, void *cb_data) +{ + struct hook_config_cb *data = cb_data; + const char *hook_key = data->hookname->buf; + struct list_head *head = data->list; + + if (!strcmp(key, hook_key)) { + const char *command = value; + struct strbuf hookcmd_name = STRBUF_INIT; + + /* + * Check if a hookcmd with that name exists. If it doesn't, + * 'git_config_get_value()' is documented not to touch &command, + * so we don't need to do anything. + */ + strbuf_addf(&hookcmd_name, "hookcmd.%s.command", command); + git_config_get_value(hookcmd_name.buf, &command); + + if (!command) { + strbuf_release(&hookcmd_name); + BUG("git_config_get_value overwrote a string it shouldn't have"); + } + + /* + * TODO: implement an option-getting callback, e.g. + * get configs by pattern hookcmd.$value.* + * for each key+value, do_callback(key, value, cb_data) + */ + + append_or_move_hook(head, command); + + strbuf_release(&hookcmd_name); + } + + return 0; +} + +struct list_head* hook_list(const struct strbuf* hookname) +{ + struct strbuf hook_key = STRBUF_INIT; + struct list_head *hook_head = xmalloc(sizeof(struct list_head)); + struct hook_config_cb cb_data = { &hook_key, hook_head }; + + INIT_LIST_HEAD(hook_head); + + if (!hookname) + return NULL; + + strbuf_addf(&hook_key, "hook.%s.command", hookname->buf); + + git_config(hook_config_lookup, &cb_data); + + strbuf_release(&hook_key); + return hook_head; +} diff --git a/hook.h b/hook.h new file mode 100644 index 0000000000..e48dfc6d27 --- /dev/null +++ b/hook.h @@ -0,0 +1,25 @@ +#include "config.h" +#include "list.h" +#include "strbuf.h" + +struct hook { + struct list_head list; + /* + * Config file which holds the hook.*.command definition. + * (This has nothing to do with the hookcmd..* configs.) + */ + enum config_scope origin; + /* The literal command to run. */ + struct strbuf command; +}; + +/* + * Provides a linked list of 'struct hook' detailing commands which should run + * in response to the 'hookname' event, in execution order. + */ +struct list_head* hook_list(const struct strbuf *hookname); + +/* Free memory associated with a 'struct hook' */ +void free_hook(struct hook *ptr); +/* Empties the list at 'head', calling 'free_hook()' on each entry */ +void clear_hook_list(struct list_head *head); diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index 34b0df5216..6e4a3e763f 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -4,8 +4,85 @@ test_description='config-managed multihooks, including git-hook command' . ./test-lib.sh -test_expect_success 'git hook command does not crash' ' - git hook +ROOT= +if test_have_prereq MINGW +then + # In Git for Windows, Unix-like paths work only in shell scripts; + # `git.exe`, however, will prefix them with the pseudo root directory + # (of the Unix shell). Let's accommodate for that. + ROOT="$(cd / && pwd)" +fi + +setup_hooks () { + test_config hook.pre-commit.command "/path/ghi" --add + test_config_global hook.pre-commit.command "/path/def" --add +} + +setup_hookcmd () { + test_config hook.pre-commit.command "abc" --add + test_config_global hookcmd.abc.command "/path/abc" --add +} + +test_expect_success 'git hook rejects commands without a mode' ' + test_must_fail git hook pre-commit +' + + +test_expect_success 'git hook rejects commands without a hookname' ' + test_must_fail git hook list +' + +test_expect_success 'git hook runs outside of a repo' ' + setup_hooks && + + cat >expected <<-EOF && + global: $ROOT/path/def + EOF + + nongit git config --list --global && + + nongit git hook list pre-commit >actual && + test_cmp expected actual +' + +test_expect_success 'git hook list orders by config order' ' + setup_hooks && + + cat >expected <<-EOF && + global: $ROOT/path/def + local: $ROOT/path/ghi + EOF + + git hook list pre-commit >actual && + test_cmp expected actual +' + +test_expect_success 'git hook list dereferences a hookcmd' ' + setup_hooks && + setup_hookcmd && + + cat >expected <<-EOF && + global: $ROOT/path/def + local: $ROOT/path/ghi + local: $ROOT/path/abc + EOF + + git hook list pre-commit >actual && + test_cmp expected actual +' + +test_expect_success 'git hook list reorders on duplicate commands' ' + setup_hooks && + + test_config hook.pre-commit.command "/path/def" --add && + + cat >expected <<-EOF && + local: $ROOT/path/ghi + local: $ROOT/path/def + EOF + + git hook list pre-commit >actual && + test_cmp expected actual ' test_done From patchwork Thu Mar 11 02:10:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130073 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9F4FBC433DB for ; Thu, 11 Mar 2021 02:11:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 74DE164FAF for ; Thu, 11 Mar 2021 02:11:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229714AbhCKCLE (ORCPT ); Wed, 10 Mar 2021 21:11:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42160 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229673AbhCKCK7 (ORCPT ); Wed, 10 Mar 2021 21:10:59 -0500 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0C9FC061762 for ; Wed, 10 Mar 2021 18:10:58 -0800 (PST) Received: by mail-qk1-x74a.google.com with SMTP id b78so14304097qkg.13 for ; Wed, 10 Mar 2021 18:10:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=PGWwGCCl7uxnqzvZ0XsMsktSth1oIo2y9XcFCqZtCBc=; b=lWR5T5ps9R7j5DSRcokNjXXUrYxFM8vyUXLXLviglkG7TOWX/RVGKUKXg0uMflThzr DHM4zIztRa4sWIWlAZL+aLu3cw2kEtLU3KHz4PLlWxTxWQGkixOdSv0JWro278Fk9Xj7 oji0NKV54K2GFH7HL720geuN8hbyYppLqbVlvuDwltC6TzZe9qcEWafHhnhW6oY7bnXZ BXgMIPZbvxzaYjisN+w/bZw7eik0dO5czf+Wjnh5PJT5ROCH+H+8KLNDEyE2FeBNfQkC unG+DZhl15eKRdq9mr9fIle9aVzaMVZArRlY2gwMpXeOPId2EirX6WFriWUqAOvLxGGG q3fA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=PGWwGCCl7uxnqzvZ0XsMsktSth1oIo2y9XcFCqZtCBc=; b=s0ieiR78VQVVF7/vgXt9pt9KaAdB/4jGao/CWOVbDdguEf79eFT85F694lf3MoAqz3 rGWGxKxZtY3hOSLi3Laow7pGwEJ4VbRvTXA2OGsiMomBaFbxk/33jW0nDPzraqNl/kAk hM0bJq2jUHGDbfLs2NPKr/Ydbzl2o/YIhIXYb1AOljUmSpczl1ZI2EPQGV5cPdEPWPox tWB8IDVCUW/ukObsh/GdpEgLNHiZaXOAcUL7BOKYroszeG9PYVN/EKT4hJTRS/L/433J J/qh8/zmFJgmMQhnDseXzRfuHDV8UpfH6nQqk/QMFxVn3qPM1/2DFvWISq8vL/gszlZT ycGA== X-Gm-Message-State: AOAM533Jk0g0DFyaH1OjmZP1dGQhGtMGk1bLyx2uZRHu9KElxwoahIHu f0AyhOuLmrYkITdF0uH9doN1QEZc3w0avUkeOHPL2yduOy3XaC0EqpGR9WAQ1Q1/dl4Ssn/T4aI W4q2LyJBJwvwOVQFaayQ6Z6AKjYWHBxwuDBqRowMq2E1LaT2IZSUiLRNCivMRDA2cpdE12SZDUw == X-Google-Smtp-Source: ABdhPJzvCNqQ3tYG7XKE191V+WgRySxjPme9mUGUJk6bI0XKkGi4yW/Wzyc5hlCy+iaSjTKsT3CqFui7qP+DRnauqMg= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:1484:: with SMTP id bn4mr6066096qvb.8.1615428657927; Wed, 10 Mar 2021 18:10:57 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:04 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-5-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 04/37] hook: include hookdir hook in list From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Historically, hooks are declared by placing an executable into $GIT_DIR/hooks/$HOOKNAME (or $HOOKDIR/$HOOKNAME). Although hooks taken from the config are more featureful than hooks placed in the $HOOKDIR, those hooks should not stop working for users who already have them. Let's list them to the user, but instead of displaying a config scope (e.g. "global: blah") we can prefix them with "hookdir:". Signed-off-by: Emily Shaffer --- Notes: Since v7, fix some nits from Jonathan Tan. The largest is to move reference to "hookdir annotation" from this commit to the next one which introduces the hook.runHookDir option. builtin/hook.c | 11 +++++++++-- hook.c | 17 +++++++++++++++++ hook.h | 1 + t/t1360-config-based-hooks.sh | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/builtin/hook.c b/builtin/hook.c index bb64cd77ca..c8fbfbb39d 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -40,10 +40,15 @@ static int list(int argc, const char **argv, const char *prefix) list_for_each(pos, head) { struct hook *item = list_entry(pos, struct hook, list); - if (item) + item = list_entry(pos, struct hook, list); + if (item) { + /* Don't translate 'hookdir' - it matches the config */ printf("%s: %s\n", - config_scope_name(item->origin), + (item->from_hookdir + ? "hookdir" + : config_scope_name(item->origin)), item->command.buf); + } } clear_hook_list(head); @@ -60,6 +65,8 @@ int cmd_hook(int argc, const char **argv, const char *prefix) if (argc < 2) usage_with_options(builtin_hook_usage, builtin_hook_options); + git_config(git_default_config, NULL); + if (!strcmp(argv[1], "list")) return list(argc - 1, argv + 1, prefix); diff --git a/hook.c b/hook.c index fede40e925..080e25696b 100644 --- a/hook.c +++ b/hook.c @@ -2,6 +2,7 @@ #include "hook.h" #include "config.h" +#include "run-command.h" void free_hook(struct hook *ptr) { @@ -35,6 +36,7 @@ static void append_or_move_hook(struct list_head *head, const char *command) to_add = xmalloc(sizeof(*to_add)); strbuf_init(&to_add->command, 0); strbuf_addstr(&to_add->command, command); + to_add->from_hookdir = 0; } /* re-set the scope so we show where an override was specified */ @@ -115,6 +117,21 @@ struct list_head* hook_list(const struct strbuf* hookname) git_config(hook_config_lookup, &cb_data); + if (have_git_dir()) { + const char *legacy_hook_path = find_hook(hookname->buf); + + /* Unconditionally add legacy hook, but annotate it. */ + if (legacy_hook_path) { + struct hook *legacy_hook; + + append_or_move_hook(hook_head, + absolute_path(legacy_hook_path)); + legacy_hook = list_entry(hook_head->prev, struct hook, + list); + legacy_hook->from_hookdir = 1; + } + } + strbuf_release(&hook_key); return hook_head; } diff --git a/hook.h b/hook.h index e48dfc6d27..a97d43670d 100644 --- a/hook.h +++ b/hook.h @@ -11,6 +11,7 @@ struct hook { enum config_scope origin; /* The literal command to run. */ struct strbuf command; + unsigned from_hookdir : 1; }; /* diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index 6e4a3e763f..0f12af4659 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -23,6 +23,14 @@ setup_hookcmd () { test_config_global hookcmd.abc.command "/path/abc" --add } +setup_hookdir () { + mkdir .git/hooks + write_script .git/hooks/pre-commit <<-EOF + echo \"Legacy Hook\" + EOF + test_when_finished rm -rf .git/hooks +} + test_expect_success 'git hook rejects commands without a mode' ' test_must_fail git hook pre-commit ' @@ -85,4 +93,15 @@ test_expect_success 'git hook list reorders on duplicate commands' ' test_cmp expected actual ' +test_expect_success 'git hook list shows hooks from the hookdir' ' + setup_hookdir && + + cat >expected <<-EOF && + hookdir: $(pwd)/.git/hooks/pre-commit + EOF + + git hook list pre-commit >actual && + test_cmp expected actual +' + test_done From patchwork Thu Mar 11 02:10:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130079 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-21.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT, USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 06D7DC433E6 for ; Thu, 11 Mar 2021 02:11:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CAE5264FC9 for ; Thu, 11 Mar 2021 02:11:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229725AbhCKCLE (ORCPT ); Wed, 10 Mar 2021 21:11:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42170 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229674AbhCKCLA (ORCPT ); Wed, 10 Mar 2021 21:11:00 -0500 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 78917C061762 for ; Wed, 10 Mar 2021 18:11:00 -0800 (PST) Received: by mail-qk1-x74a.google.com with SMTP id e17so2410328qkg.3 for ; Wed, 10 Mar 2021 18:11:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=RpSEtPsAi8tbtJobuTrBN0rmJ1JQzaRPVpGjy54khnY=; b=WiZ5myK6hDD+x2xFQirUhheSiJ0Fu4mZHN8lul3EC04T7W+heWKfDpE3sUcfN1HVps lopOnfxAKRVl7l2JxArYO3JyCVv6YECOK0kN8OFItkzeGMBoozFK3hnnxGl9UMQiBTwJ uPjTVZpOajEZ2BNt3P7sd7r1L21wsf2nnsdP7BUUAqTq9bn9i43HaXgxCForwlyGeMWV x4rIWPJqqsaBWghurl8Z/mjWnSjRVXYVB/Fexo3jPAAZblU7Y1S2tpQoJ5VotJZeJ0Tn 0vtsiULqHllmaP50sNX0KxRNK37JCxLgU44Hp+eEQN/B0+EFVMBemDwClRZlXCCgI8cx ghkw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=RpSEtPsAi8tbtJobuTrBN0rmJ1JQzaRPVpGjy54khnY=; b=FZfYGcAO2oUGoiO+6hrILhSZQ/I+eqeWcv5SDWFGpoIeRZsPuhMBr2Ft4v68IzgYe8 TMTMB/AiutMTkxaD1i4nGCV/01EB7WN6WusfYTQpKIoa0cY/pWST5Y7xp8jiZ42gLt3/ 8Y78cZnSkdq0RScWXGLg9/d2YPB1Zxh5/LwkhFGjwEaFD1Ytpi+PQDUn6l5rPvSN0TGs ZmahvVIFPnJ2ClYGAfCm2tSR45ttDef+/vlJUASurZN5cjWzqxmEiJxIfX96p+1pbxO3 eQyc9IDCJhU4Qk9sC6XrDMJhrU4mebIz8nqBTXY6V/mlEvuZUnP1omvru/YgFRCWdO1m LGMQ== X-Gm-Message-State: AOAM530fpIBT7flUNiTHyN3zGoocj2HWkea06+lGGQGua/muTkoZeG2U 1bJ+nZvY3d9FRraaDOTPSz8n9Jn8PFUMaoC9/QiBBjOJL+vTHugFrYuaJaXWClRaukAp+/7mrKJ yls4ZHiY46QsBTJ85AJrM13B7WA0HgOX5MvkzYwmQm/F3zG/PpXTtnPbJuRUjKq+SV9oT1eL+CA == X-Google-Smtp-Source: ABdhPJx5/jhMeRmIp0/tWKVuU7YHLoWmKml/CHnOXWZvpe8oICRaIfBqRMUYVSVJsa43vL713Iwi3GX/wWVOXTtIXZY= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:2b06:: with SMTP id jx6mr5896953qvb.48.1615428659694; Wed, 10 Mar 2021 18:10:59 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:05 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-6-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 05/37] hook: teach hook.runHookDir From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org For now, just give a hint about how these hooks will be run in 'git hook list'. Later on, though, we will pay attention to this enum when running the hooks. --- Notes: Since v7, tidied up the behavior of the HOOK_UNKNOWN flag and added a test to enforce it - now it matches the design doc much better. Also, thanks Jonathan Tan for pointing out that the commit message made no sense and was targeted for a different change. Rewrote the commit message now. Plus, added HOOK_ERROR flag per Junio and Jonathan Nieder. - Emily Documentation/config/hook.txt | 5 +++ builtin/hook.c | 69 +++++++++++++++++++++++++++++++--- hook.c | 24 ++++++++++++ hook.h | 16 ++++++++ t/t1360-config-based-hooks.sh | 71 +++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+), 5 deletions(-) diff --git a/Documentation/config/hook.txt b/Documentation/config/hook.txt index 71449ecbc7..75312754ae 100644 --- a/Documentation/config/hook.txt +++ b/Documentation/config/hook.txt @@ -7,3 +7,8 @@ hookcmd..command:: A command to execute during a hook for which has been specified as a command. This can be an executable on your device or a oneliner for your shell. See linkgit:git-hook[1]. + +hook.runHookDir:: + Controls how hooks contained in your hookdir are executed. Can be any of + "yes", "warn", "interactive", or "no". Defaults to "yes". See + linkgit:git-hook[1] and linkgit:git-config[1] "core.hooksPath"). diff --git a/builtin/hook.c b/builtin/hook.c index c8fbfbb39d..310f696ebf 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -10,10 +10,13 @@ static const char * const builtin_hook_usage[] = { NULL }; +static enum hookdir_opt should_run_hookdir; + static int list(int argc, const char **argv, const char *prefix) { struct list_head *head, *pos; struct strbuf hookname = STRBUF_INIT; + struct strbuf hookdir_annotation = STRBUF_INIT; struct option list_options[] = { OPT_END(), @@ -38,20 +41,48 @@ static int list(int argc, const char **argv, const char *prefix) return 0; } + switch (should_run_hookdir) { + case HOOKDIR_NO: + strbuf_addstr(&hookdir_annotation, _(" (will not run)")); + break; + case HOOKDIR_ERROR: + strbuf_addstr(&hookdir_annotation, _(" (will error and not run)")); + break; + case HOOKDIR_INTERACTIVE: + strbuf_addstr(&hookdir_annotation, _(" (will prompt)")); + break; + case HOOKDIR_WARN: + strbuf_addstr(&hookdir_annotation, _(" (will warn but run)")); + break; + case HOOKDIR_YES: + /* + * The default behavior should agree with + * hook.c:configured_hookdir_opt(). HOOKDIR_UNKNOWN should just + * do the default behavior. + */ + case HOOKDIR_UNKNOWN: + default: + break; + } + list_for_each(pos, head) { struct hook *item = list_entry(pos, struct hook, list); item = list_entry(pos, struct hook, list); if (item) { /* Don't translate 'hookdir' - it matches the config */ - printf("%s: %s\n", + printf("%s: %s%s\n", (item->from_hookdir ? "hookdir" : config_scope_name(item->origin)), - item->command.buf); + item->command.buf, + (item->from_hookdir + ? hookdir_annotation.buf + : "")); } } clear_hook_list(head); + strbuf_release(&hookdir_annotation); strbuf_release(&hookname); return 0; @@ -59,16 +90,44 @@ static int list(int argc, const char **argv, const char *prefix) int cmd_hook(int argc, const char **argv, const char *prefix) { + const char *run_hookdir = NULL; + struct option builtin_hook_options[] = { + OPT_STRING(0, "run-hookdir", &run_hookdir, N_("option"), + N_("what to do with hooks found in the hookdir")), OPT_END(), }; - if (argc < 2) + + argc = parse_options(argc, argv, prefix, builtin_hook_options, + builtin_hook_usage, 0); + + /* after the parse, we should have " " */ + if (argc < 1) usage_with_options(builtin_hook_usage, builtin_hook_options); git_config(git_default_config, NULL); - if (!strcmp(argv[1], "list")) - return list(argc - 1, argv + 1, prefix); + + /* argument > config */ + if (run_hookdir) + if (!strcmp(run_hookdir, "no")) + should_run_hookdir = HOOKDIR_NO; + else if (!strcmp(run_hookdir, "error")) + should_run_hookdir = HOOKDIR_ERROR; + else if (!strcmp(run_hookdir, "yes")) + should_run_hookdir = HOOKDIR_YES; + else if (!strcmp(run_hookdir, "warn")) + should_run_hookdir = HOOKDIR_WARN; + else if (!strcmp(run_hookdir, "interactive")) + should_run_hookdir = HOOKDIR_INTERACTIVE; + else + die(_("'%s' is not a valid option for --run-hookdir " + "(yes, warn, interactive, no)"), run_hookdir); + else + should_run_hookdir = configured_hookdir_opt(); + + if (!strcmp(argv[0], "list")) + return list(argc, argv, prefix); usage_with_options(builtin_hook_usage, builtin_hook_options); } diff --git a/hook.c b/hook.c index 080e25696b..039ff0a378 100644 --- a/hook.c +++ b/hook.c @@ -102,6 +102,30 @@ static int hook_config_lookup(const char *key, const char *value, void *cb_data) return 0; } +enum hookdir_opt configured_hookdir_opt(void) +{ + const char *key; + if (git_config_get_value("hook.runhookdir", &key)) + return HOOKDIR_YES; /* by default, just run it. */ + + if (!strcmp(key, "no")) + return HOOKDIR_NO; + + if (!strcmp(key, "error")) + return HOOKDIR_ERROR; + + if (!strcmp(key, "yes")) + return HOOKDIR_YES; + + if (!strcmp(key, "warn")) + return HOOKDIR_WARN; + + if (!strcmp(key, "interactive")) + return HOOKDIR_INTERACTIVE; + + return HOOKDIR_UNKNOWN; +} + struct list_head* hook_list(const struct strbuf* hookname) { struct strbuf hook_key = STRBUF_INIT; diff --git a/hook.h b/hook.h index a97d43670d..1c4b953aec 100644 --- a/hook.h +++ b/hook.h @@ -20,6 +20,22 @@ struct hook { */ struct list_head* hook_list(const struct strbuf *hookname); +enum hookdir_opt +{ + HOOKDIR_NO, + HOOKDIR_ERROR, + HOOKDIR_WARN, + HOOKDIR_INTERACTIVE, + HOOKDIR_YES, + HOOKDIR_UNKNOWN, +}; + +/* + * Provides the hookdir_opt specified in the config without consulting any + * command line arguments. + */ +enum hookdir_opt configured_hookdir_opt(void); + /* Free memory associated with a 'struct hook' */ void free_hook(struct hook *ptr); /* Empties the list at 'head', calling 'free_hook()' on each entry */ diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index 0f12af4659..66b0b6b7ad 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -104,4 +104,75 @@ test_expect_success 'git hook list shows hooks from the hookdir' ' test_cmp expected actual ' +test_expect_success 'hook.runHookDir = no is respected by list' ' + setup_hookdir && + + test_config hook.runHookDir "no" && + + cat >expected <<-EOF && + hookdir: $(pwd)/.git/hooks/pre-commit (will not run) + EOF + + git hook list pre-commit >actual && + # the hookdir annotation is translated + test_i18ncmp expected actual +' + +test_expect_success 'hook.runHookDir = error is respected by list' ' + setup_hookdir && + + test_config hook.runHookDir "error" && + + cat >expected <<-EOF && + hookdir: $(pwd)/.git/hooks/pre-commit (will error and not run) + EOF + + git hook list pre-commit >actual && + # the hookdir annotation is translated + test_i18ncmp expected actual +' + +test_expect_success 'hook.runHookDir = warn is respected by list' ' + setup_hookdir && + + test_config hook.runHookDir "warn" && + + cat >expected <<-EOF && + hookdir: $(pwd)/.git/hooks/pre-commit (will warn but run) + EOF + + git hook list pre-commit >actual && + # the hookdir annotation is translated + test_i18ncmp expected actual +' + + +test_expect_success 'hook.runHookDir = interactive is respected by list' ' + setup_hookdir && + + test_config hook.runHookDir "interactive" && + + cat >expected <<-EOF && + hookdir: $(pwd)/.git/hooks/pre-commit (will prompt) + EOF + + git hook list pre-commit >actual && + # the hookdir annotation is translated + test_i18ncmp expected actual +' + +test_expect_success 'hook.runHookDir is tolerant to unknown values' ' + setup_hookdir && + + test_config hook.runHookDir "junk" && + + cat >expected <<-EOF && + hookdir: $(pwd)/.git/hooks/pre-commit + EOF + + git hook list pre-commit >actual && + # the hookdir annotation is translated + test_i18ncmp expected actual +' + test_done From patchwork Thu Mar 11 02:10:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130097 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6FB31C43331 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 41B1A64FD7 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229878AbhCKCLk (ORCPT ); Wed, 10 Mar 2021 21:11:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42178 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229767AbhCKCLN (ORCPT ); Wed, 10 Mar 2021 21:11:13 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2CC64C061762 for ; Wed, 10 Mar 2021 18:11:02 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 194so23716429ybl.5 for ; Wed, 10 Mar 2021 18:11:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=CfJYpN1tLI2mdf1xn93vbCpKvf0qZd+KNB4jGw3x4ug=; b=Y+4ukGDL2HOIwuDJOiIW4o0X+Wk3WpANrLLooq3t17sW9e8rQAlzMbSvlfnlJhuua2 xjufzHMF4izL8VuQMF7YisPd9BfgypvSTRQhv5p1+vqxHKjfvl+TxtctodfXsLiLfsKV Qc7eLgo5sIuyPro6QBYoV1EqSAA9Bx5emWXGfn120nk4hCsLrw0m5ilcX4Wg6adlmQ/K 85V51IjZ8xPDTbpqWjksNGyC0DJSY0wka+qX5ThGKsZ/KBOR8V1Cjfilu3DIouRbiDcd riBRYtl7LGm8jWG4E6YnpbislAblISleM5BMF0fqim19d07t1qTFXWdHOWX+nZbSIc7e SaoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=CfJYpN1tLI2mdf1xn93vbCpKvf0qZd+KNB4jGw3x4ug=; b=BbgIsOvkUrE+VhB3B+Q9Ygf1NWnDtWjey0V1cJFFEWNdUMNBuHbXSoSIfUH7oVhbf2 US1FivqubDkrCaBleof8h326S0YNm9ZxJU8TZuJN1PIwUP9+9iVq19BAii4CvugLGSee etUnyYwMf8ylY22QzSFFDTd4xIrj23GnzX6pj/wm+dwhRozmk21/Y5NFhJuQRPvQUVwJ 7bqkP825TZx2mzwUNy8RGRhhrCxhAFFBFpDuFNjoz3XHLvkl3k/rdshWAF4s0Ls8MymP aM7WxIrbZZxlDa4QU6voke5ClfcsfXsdTFQAF3p2OFm9ayIbPA7ln2mqfCgWexS3d15l fZ5A== X-Gm-Message-State: AOAM531Lf7nzoqv7eQlSNQ/S+dslQ2piIxk2mCrHa4AGX3U9sce3YYPf 3QlRqbt6yCaPxyiNsGtV0mUWUtBSGI77iBgwvzi91s9oL6PssmURoj5+rMlppek2d4dB9X5rQP2 mkqJb++Nhx6DZcaqU42D2sgRdVwch7RbI91o/tJSdRfxPY4KB8pFMPjLyIhSFb9XZusiCbxtnTg == X-Google-Smtp-Source: ABdhPJzg7j5E7bDUc3mzAcOx8a1VrmIntfJXAPsWLGNerlo4tWlhxD5HMt+AJvEJEzm0FjWscYdVr9s0cf2NEUuVZ9A= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:bc14:: with SMTP id i20mr9693407ybh.40.1615428661361; Wed, 10 Mar 2021 18:11:01 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:06 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-7-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 06/37] hook: implement hookcmd..skip From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org If a user wants a specific repo to skip execution of a hook which is set at a global or system level, they will be able to do so by specifying 'skip' in their repo config: ~/.gitconfig [hook.pre-commit] command = skippable-oneliner command = skippable-hookcmd [hookcmd.skippable-hookcmd] command = foo.sh $GIT_DIR/.git/config [hookcmd.skippable-oneliner] skip = true [hookcmd.skippable-hookcmd] skip = true Later it may make sense to add an option like "hookcmd..-skip" - but for simplicity, let's start with a universal skip setting like this. Signed-off-by: Emily Shaffer --- Documentation/config/hook.txt | 8 ++++++++ Documentation/git-hook.txt | 33 +++++++++++++++++++++++++++++++++ hook.c | 35 ++++++++++++++++++++++++++--------- t/t1360-config-based-hooks.sh | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 9 deletions(-) diff --git a/Documentation/config/hook.txt b/Documentation/config/hook.txt index 75312754ae..8b12512e33 100644 --- a/Documentation/config/hook.txt +++ b/Documentation/config/hook.txt @@ -8,6 +8,14 @@ hookcmd..command:: as a command. This can be an executable on your device or a oneliner for your shell. See linkgit:git-hook[1]. +hookcmd..skip:: + Specify this boolean to remove a command from earlier in the execution + order. Useful if you want to make a single repo an exception to hook + configured at the system or global scope. If there is no hookcmd + specified for the command you want to skip, you can use the value of + `hook..command` as as a shortcut. The "skip" setting + must be specified after the "hook..command" to have an effect. + hook.runHookDir:: Controls how hooks contained in your hookdir are executed. Can be any of "yes", "warn", "interactive", or "no". Defaults to "yes". See diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index f19875ed68..c84520cb38 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -54,6 +54,39 @@ $ git hook list "prepare-commit-msg" local: /bin/linter --c ---- +If there is a command you wish to run in most cases but have one or two +exceptional repos where it should be skipped, you can use specify +`hookcmd..skip`, for example: + +System config +---- + [hook "pre-commit"] + command = check-for-secrets + + [hookcmd "check-for-secrets"] + command = /bin/secret-checker --aggressive +---- + +Local config +---- + [hookcmd "check-for-secrets"] + skip = true + # This works for inlined hook commands, too: + [hookcmd "~/typocheck.sh"] + skip = true +---- + +After these configs are added, the hook list becomes: + +---- +$ git hook list "post-commit" +global: /bin/linter --c +local: python ~/run-test-suite.py + +$ git hook list "pre-commit" +no commands configured for hook 'pre-commit' +---- + COMMANDS -------- diff --git a/hook.c b/hook.c index 039ff0a378..37b740d58d 100644 --- a/hook.c +++ b/hook.c @@ -12,24 +12,25 @@ void free_hook(struct hook *ptr) } } -static void append_or_move_hook(struct list_head *head, const char *command) +static struct hook * find_hook_by_command(struct list_head *head, const char *command) { struct list_head *pos = NULL, *tmp = NULL; - struct hook *to_add = NULL; + struct hook *found = NULL; - /* - * remove the prior entry with this command; we'll replace it at the - * end. - */ list_for_each_safe(pos, tmp, head) { struct hook *it = list_entry(pos, struct hook, list); if (!strcmp(it->command.buf, command)) { list_del(pos); - /* we'll simply move the hook to the end */ - to_add = it; + found = it; break; } } + return found; +} + +static void append_or_move_hook(struct list_head *head, const char *command) +{ + struct hook *to_add = find_hook_by_command(head, command); if (!to_add) { /* adding a new hook, not moving an old one */ @@ -74,12 +75,22 @@ static int hook_config_lookup(const char *key, const char *value, void *cb_data) if (!strcmp(key, hook_key)) { const char *command = value; struct strbuf hookcmd_name = STRBUF_INIT; + int skip = 0; + + /* + * Check if we're removing that hook instead. Hookcmds are + * removed by name, and inlined hooks are removed by command + * content. + */ + strbuf_addf(&hookcmd_name, "hookcmd.%s.skip", command); + git_config_get_bool(hookcmd_name.buf, &skip); /* * Check if a hookcmd with that name exists. If it doesn't, * 'git_config_get_value()' is documented not to touch &command, * so we don't need to do anything. */ + strbuf_reset(&hookcmd_name); strbuf_addf(&hookcmd_name, "hookcmd.%s.command", command); git_config_get_value(hookcmd_name.buf, &command); @@ -94,7 +105,13 @@ static int hook_config_lookup(const char *key, const char *value, void *cb_data) * for each key+value, do_callback(key, value, cb_data) */ - append_or_move_hook(head, command); + if (skip) { + struct hook *to_remove = find_hook_by_command(head, command); + if (to_remove) + remove_hook(&(to_remove->list)); + } else { + append_or_move_hook(head, command); + } strbuf_release(&hookcmd_name); } diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index 66b0b6b7ad..a9b1b046c1 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -146,6 +146,41 @@ test_expect_success 'hook.runHookDir = warn is respected by list' ' test_i18ncmp expected actual ' +test_expect_success 'git hook list removes skipped hookcmd' ' + setup_hookcmd && + test_config hookcmd.abc.skip "true" --add && + + cat >expected <<-EOF && + no commands configured for hook '\''pre-commit'\'' + EOF + + git hook list pre-commit >actual && + test_i18ncmp expected actual +' + +test_expect_success 'git hook list ignores skip referring to unused hookcmd' ' + test_config hookcmd.abc.command "/path/abc" --add && + test_config hookcmd.abc.skip "true" --add && + + cat >expected <<-EOF && + no commands configured for hook '\''pre-commit'\'' + EOF + + git hook list pre-commit >actual && + test_i18ncmp expected actual +' + +test_expect_success 'git hook list removes skipped inlined hook' ' + setup_hooks && + test_config hookcmd."$ROOT/path/ghi".skip "true" --add && + + cat >expected <<-EOF && + global: $ROOT/path/def + EOF + + git hook list pre-commit >actual && + test_cmp expected actual +' test_expect_success 'hook.runHookDir = interactive is respected by list' ' setup_hookdir && From patchwork Thu Mar 11 02:10:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130081 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A89FEC433E0 for ; Thu, 11 Mar 2021 02:12:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6A5EB64FAA for ; Thu, 11 Mar 2021 02:12:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229512AbhCKCLf (ORCPT ); Wed, 10 Mar 2021 21:11:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42188 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229712AbhCKCLE (ORCPT ); Wed, 10 Mar 2021 21:11:04 -0500 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 115C2C061574 for ; Wed, 10 Mar 2021 18:11:04 -0800 (PST) Received: by mail-qk1-x74a.google.com with SMTP id c7so14308593qka.6 for ; Wed, 10 Mar 2021 18:11:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=OWYnZbPFGX9FT/eauTj5CPL93ENtK/W91SmNK7BDIjE=; b=XruIqkMn9VxPDPfo0yoa08dH93oOnT3HZcmZSteGbjKXD1sc4w0fdO7cJiClL6T7XD 6UYoBld4AYFMBpvOJcTrMoz2wODmkdblVCMON0NPm4x7uEACQ05O/11EoBJQqg1nBzs5 UBXstGei4fZ6Y8VcsGg2zHkiSU/vI4x/3k19kPSNAu7E4RIBXgRQUslatluC92X5nPEq eR6zeGprAA4wsh4AIFoA2MwpRuohu6ftHMavy5bipSuVUiJfpvV27Lw4rtrycLSdmVCz ue1dzGGTRJ3fZ6+y2/NYRarN9N5TU42AyqGuW0EkNLEFQCQFeTXDyD7A2f1PyKevIl2G 9ehQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=OWYnZbPFGX9FT/eauTj5CPL93ENtK/W91SmNK7BDIjE=; b=jfwLRlEPQFrRe76VIK9TLawD0sPJLLMbPc/+GWqD+bGtftTh55acKdhqPWb9woB0Zi MrGhe/GI3n6V/fpQiYPU9T3up9UV7z7qG33i84ZDn92f5F/MxS1D9UDBe0QzuLwVRD/P X/+3UnZ3hTIZhUBoma1GaI9orcx80k+m1dryT1Ocn5ve07TbEIwSOmaS50HPjbxuFIMZ JMR3Gta4KNZXy9oLZrRYCM/axZJ4AROuOtMStRcQt3ExznCs0r+5IOluc66QKuThHWea zymYahk/q6Q9x6Q6c5gp5be+m3KX89NNhU/xTdEKEFu6bWuWciyW6paYo+vtR21iXuVI kdzA== X-Gm-Message-State: AOAM531sWiJbbbxyCiWwf9MQQi+O2NVy3RjidxC2rbp1xkRfdhBicK8C BltM0vRkr+KXyn+YQGGd4EjjsNU9EqzKlWzaR1M8hEVnkkULHz3SZYMif8K8yAGSz2nnh0VyPgS Xhzz/KJ0lNZ6BdIu2khEcBJCDv4NvAP1xwhw4x4VQGTBmRJB+odq3fiRmgUcqdndeE5vy3+Nuyg == X-Google-Smtp-Source: ABdhPJx3DcyR2ePx6Okm3+SspuA9rNEc/EccJXRZBT0/r/1YVYVpExLvEpNfsVuSLG55hWvvSzxB4UxFPp0H/n0ESfI= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:ad4:50c7:: with SMTP id e7mr5677172qvq.58.1615428663227; Wed, 10 Mar 2021 18:11:03 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:07 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-8-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 07/37] parse-options: parse into strvec From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org parse-options already knows how to read into a string_list, and it knows how to read into an strvec as a passthrough (that is, including the argument as well as its value). string_list and strvec serve similar purposes but are somewhat painful to convert between; so, let's teach parse-options to read values of string arguments directly into an strvec without preserving the argument name. This is useful if collecting generic arguments to pass through to another command, for example, 'git hook run --arg "--quiet" --arg "--format=pretty" some-hook'. The resulting strvec would contain { "--quiet", "--format=pretty" }. The implementation is based on that of OPT_STRING_LIST. Signed-off-by: Emily Shaffer --- Notes: Since v7, updated the reference doc to make the intended usage for OPT_STRVEC more clear. Since v4, fixed one or two more places where I missed the argv_array->strvec rename. Documentation/technical/api-parse-options.txt | 7 +++++++ parse-options-cb.c | 16 ++++++++++++++++ parse-options.h | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 5a60bbfa7f..f79b17e7fc 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -173,6 +173,13 @@ There are some macros to easily define options: The string argument is stored as an element in `string_list`. Use of `--no-option` will clear the list of preceding values. +`OPT_STRVEC(short, long, &struct strvec, arg_str, description)`:: + Introduce an option with a string argument, meant to be specified + multiple times. + The string argument is stored as an element in `strvec`, and later + arguments are added to the same `strvec`. + Use of `--no-option` will clear the list of preceding values. + `OPT_INTEGER(short, long, &int_var, description)`:: Introduce an option with integer argument. The integer is put into `int_var`. diff --git a/parse-options-cb.c b/parse-options-cb.c index 4542d4d3f9..c2451dfb1b 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -207,6 +207,22 @@ int parse_opt_string_list(const struct option *opt, const char *arg, int unset) return 0; } +int parse_opt_strvec(const struct option *opt, const char *arg, int unset) +{ + struct strvec *v = opt->value; + + if (unset) { + strvec_clear(v); + return 0; + } + + if (!arg) + return -1; + + strvec_push(v, arg); + return 0; +} + int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset) { return 0; diff --git a/parse-options.h b/parse-options.h index ff6506a504..44c4ac08e9 100644 --- a/parse-options.h +++ b/parse-options.h @@ -177,6 +177,9 @@ struct option { #define OPT_STRING_LIST(s, l, v, a, h) \ { OPTION_CALLBACK, (s), (l), (v), (a), \ (h), 0, &parse_opt_string_list } +#define OPT_STRVEC(s, l, v, a, h) \ + { OPTION_CALLBACK, (s), (l), (v), (a), \ + (h), 0, &parse_opt_strvec } #define OPT_UYN(s, l, v, h) { OPTION_CALLBACK, (s), (l), (v), NULL, \ (h), PARSE_OPT_NOARG, &parse_opt_tertiary } #define OPT_EXPIRY_DATE(s, l, v, h) \ @@ -296,6 +299,7 @@ int parse_opt_commits(const struct option *, const char *, int); int parse_opt_commit(const struct option *, const char *, int); int parse_opt_tertiary(const struct option *, const char *, int); int parse_opt_string_list(const struct option *, const char *, int); +int parse_opt_strvec(const struct option *, const char *, int); int parse_opt_noop_cb(const struct option *, const char *, int); enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx, const struct option *, From patchwork Thu Mar 11 02:10:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130089 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 76D27C433E6 for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 38BD964FBF for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229469AbhCKCLg (ORCPT ); Wed, 10 Mar 2021 21:11:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42198 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229675AbhCKCLG (ORCPT ); Wed, 10 Mar 2021 21:11:06 -0500 Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DB016C061574 for ; Wed, 10 Mar 2021 18:11:05 -0800 (PST) Received: by mail-qt1-x849.google.com with SMTP id m8so12375122qtp.14 for ; Wed, 10 Mar 2021 18:11:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=p4F1JvYDQ75IJIS63+nBocPfvaxsm7Y8tpbLacpxf+8=; b=NGW97OLYnJXxQ5/UrZIV5HuJnVuVsmKuJDxVXW3Lc6FJ19hNuSSukt0t7o6kzRmlNV S+yl4MWJ9UFA/cDo4joj1PypyGK81hckks4ico6OyQ7RqZPdcOSxWNhNG8PlX5f+5q4a MaF2+F/dLiKsiRfHUxUpDfhzJ49UG0/UCfHZu+fVH9+ewS/5l76e3gREJ9im7mKZGqNR XWmn9A0XYtJs+ww5YOSpJZQTlJlAr9dS38eC3rL9HKDTeS8FDKWQwzwxuuRUBJR9iUMF 0WVLteMmBWMFhq7NsHyr4SjZMrugJdd2FXcRdhjfn32MRrGbCkgcKpdhOMQmZJbs49MF w8pg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=p4F1JvYDQ75IJIS63+nBocPfvaxsm7Y8tpbLacpxf+8=; b=sl0YSxfsErLvko92sW+GtStSGBnww5bBFsZvsVwtYUl+wqk9MX/xL2Vx4eU5rPxzFd wLCnfuBG6mYdB6WZdpCDl0I4fDdxeXzJFruWZOq5G50d2784fuc0dPfMkAOsgmv7BSfB NWBWRtXE2pRlfbAxBEsUTlw3lTA0SrKk3EYEPLNU7QTnpjW+ibFWn7mdxYCNICt0HCts iDZTwNu02wry6vVvAMZ307zwJcH9408M19RHcJfiSOmgyp0vsxHSJMR+VPH1tNqwS14Z D7a+BMKt+jhvmDah78/hWhyM5fXsov65bLtvke2qUtnIilO8L0toVWpW9guiLH6WTcrh K8nw== X-Gm-Message-State: AOAM532C1z366TBk2QL8neAP3BxxPCByMk9gkeOZAYGCGUkXMoipFFpd JJBe1WJUPsD+DZzNeLotfNndNu90xJl7XSnZ/Ox6BWPBN5gNeF3VPE0lBP2WbsEBx9h4hPyHbrx dxansSIsSxAxWPXPCc9kS/HwHmiBLgxzA+iOCMr59F149c0xs/tPuBJoPdRCPzauh4QH87cYhJQ == X-Google-Smtp-Source: ABdhPJxhauq8L10eiui98lobey5VmVhFNyt70SVKehGDd8iYhSP2VO2ZKpir+HImIfxZnevk6L7CQJU8tUICoE/hdpg= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:18c3:: with SMTP id cy3mr5856229qvb.1.1615428665026; Wed, 10 Mar 2021 18:11:05 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:08 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-9-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 08/37] hook: add 'run' subcommand From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org In order to enable hooks to be run as an external process, by a standalone Git command, or by tools which wrap Git, provide an external means to run all configured hook commands for a given hook event. For now, the hook commands will run in config order, in series. As alternate ordering or parallelism is supported in the future, we should add knobs to use those to the command line as well. As with the legacy hook implementation, all stdout generated by hook commands is redirected to stderr. Piping from stdin is not yet supported. Legacy hooks (those present in $GITDIR/hooks) are run at the end of the execution list. They can be disabled, or made to print warnings, or to prompt before running, with the 'hook.runHookDir' config. Users may wish to provide hook commands like 'git config hook.pre-commit.command "~/linter.sh --pre-commit"'. To enable this, config-defined hooks are run in a shell. (Since hooks in $GITDIR/hooks can't be specified with included arguments or paths which need expansion like this, they are run without a shell instead.) Signed-off-by: Emily Shaffer --- Notes: Since v7, added support for "error" hook.runHookDir setting. Since v4, updated the docs, and did less local application of single quotes. In order for hookdir hooks to run successfully with a space in the path, though, they must not be run with 'sh -c'. So we can treat the hookdir hooks specially, and warn users via doc about special considerations for configured hooks with spaces in their path. Documentation/git-hook.txt | 31 +++++++- builtin/hook.c | 42 ++++++++++- hook.c | 128 ++++++++++++++++++++++++++++++++++ hook.h | 26 +++++++ t/t1360-config-based-hooks.sh | 72 ++++++++++++++++++- 5 files changed, 292 insertions(+), 7 deletions(-) diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index c84520cb38..8f96c347ea 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -9,11 +9,12 @@ SYNOPSIS -------- [verse] 'git hook' list +'git hook' run [(-e|--env)=...] [(-a|--arg)=...] DESCRIPTION ----------- -You can list configured hooks with this command. Later, you will be able to run, -add, and modify hooks with this command. +You can list and run configured hooks with this command. Later, you will be able +to add and modify hooks with this command. This command parses the default configuration files for sections `hook` and `hookcmd`. `hook` is used to describe the commands which will be run during a @@ -97,6 +98,32 @@ in the order they should be run, and print the config scope where the relevant `hook..command` was specified, not the `hookcmd` (if applicable). This output is human-readable and the format is subject to change over time. +run [(-e|--env)=...] [(-a|--arg)=...] ``:: + +Runs hooks configured for ``, in the same order displayed by `git +hook list`. Hooks configured this way may be run prepended with `sh -c`, so +paths containing special characters or spaces should be wrapped in single +quotes: `command = '/my/path with spaces/script.sh' some args`. + +OPTIONS +------- +--run-hookdir:: + Overrides the hook.runHookDir config. Must be 'yes', 'warn', + 'interactive', or 'no'. Specifies how to handle hooks located in the Git + hook directory (core.hooksPath). + +-a:: +--arg:: + Only valid for `run`. ++ +Specify arguments to pass to every hook that is run. + +-e:: +--env:: + Only valid for `run`. ++ +Specify environment variables to set for every hook that is run. + CONFIGURATION ------------- include::config/hook.txt[] diff --git a/builtin/hook.c b/builtin/hook.c index 310f696ebf..e823a96238 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -4,9 +4,11 @@ #include "hook.h" #include "parse-options.h" #include "strbuf.h" +#include "strvec.h" static const char * const builtin_hook_usage[] = { N_("git hook list "), + N_("git hook run [(-e|--env)=...] [(-a|--arg)=...] "), NULL }; @@ -88,6 +90,40 @@ static int list(int argc, const char **argv, const char *prefix) return 0; } +static int run(int argc, const char **argv, const char *prefix) +{ + struct strbuf hookname = STRBUF_INIT; + struct run_hooks_opt opt; + int rc = 0; + + struct option run_options[] = { + OPT_STRVEC('e', "env", &opt.env, N_("var"), + N_("environment variables for hook to use")), + OPT_STRVEC('a', "arg", &opt.args, N_("args"), + N_("argument to pass to hook")), + OPT_END(), + }; + + run_hooks_opt_init(&opt); + + argc = parse_options(argc, argv, prefix, run_options, + builtin_hook_usage, 0); + + if (argc < 1) + usage_msg_opt(_("You must specify a hook event to run."), + builtin_hook_usage, run_options); + + strbuf_addstr(&hookname, argv[0]); + opt.run_hookdir = should_run_hookdir; + + rc = run_hooks(hookname.buf, &opt); + + strbuf_release(&hookname); + run_hooks_opt_clear(&opt); + + return rc; +} + int cmd_hook(int argc, const char **argv, const char *prefix) { const char *run_hookdir = NULL; @@ -99,10 +135,10 @@ int cmd_hook(int argc, const char **argv, const char *prefix) }; argc = parse_options(argc, argv, prefix, builtin_hook_options, - builtin_hook_usage, 0); + builtin_hook_usage, PARSE_OPT_KEEP_UNKNOWN); /* after the parse, we should have " " */ - if (argc < 1) + if (argc < 2) usage_with_options(builtin_hook_usage, builtin_hook_options); git_config(git_default_config, NULL); @@ -128,6 +164,8 @@ int cmd_hook(int argc, const char **argv, const char *prefix) if (!strcmp(argv[0], "list")) return list(argc, argv, prefix); + if (!strcmp(argv[0], "run")) + return run(argc, argv, prefix); usage_with_options(builtin_hook_usage, builtin_hook_options); } diff --git a/hook.c b/hook.c index 37b740d58d..d166d17fb0 100644 --- a/hook.c +++ b/hook.c @@ -3,6 +3,7 @@ #include "hook.h" #include "config.h" #include "run-command.h" +#include "prompt.h" void free_hook(struct hook *ptr) { @@ -143,6 +144,64 @@ enum hookdir_opt configured_hookdir_opt(void) return HOOKDIR_UNKNOWN; } +static int should_include_hookdir(const char *path, enum hookdir_opt cfg) +{ + struct strbuf prompt = STRBUF_INIT; + /* + * If the path doesn't exist, don't bother adding the empty hook and + * don't bother checking the config or prompting the user. + */ + if (!path) + return 0; + + switch (cfg) + { + case HOOKDIR_ERROR: + fprintf(stderr, _("Skipping legacy hook at '%s'\n"), + path); + /* FALLTHROUGH */ + case HOOKDIR_NO: + return 0; + case HOOKDIR_WARN: + fprintf(stderr, _("Running legacy hook at '%s'\n"), + path); + return 1; + case HOOKDIR_INTERACTIVE: + do { + /* + * TRANSLATORS: Make sure to include [Y] and [n] + * in your translation. Only English input is + * accepted. Default option is "yes". + */ + fprintf(stderr, _("Run '%s'? [Yn] "), path); + git_read_line_interactively(&prompt); + strbuf_tolower(&prompt); + if (starts_with(prompt.buf, "n")) { + strbuf_release(&prompt); + return 0; + } else if (starts_with(prompt.buf, "y")) { + strbuf_release(&prompt); + return 1; + } + /* otherwise, we didn't understand the input */ + } while (prompt.len); /* an empty reply means "Yes" */ + strbuf_release(&prompt); + return 1; + /* + * HOOKDIR_UNKNOWN should match the default behavior, but let's + * give a heads up to the user. + */ + case HOOKDIR_UNKNOWN: + fprintf(stderr, + _("Unrecognized value for 'hook.runHookDir'. " + "Is there a typo? ")); + /* FALLTHROUGH */ + case HOOKDIR_YES: + default: + return 1; + } +} + struct list_head* hook_list(const struct strbuf* hookname) { struct strbuf hook_key = STRBUF_INIT; @@ -176,3 +235,72 @@ struct list_head* hook_list(const struct strbuf* hookname) strbuf_release(&hook_key); return hook_head; } + +void run_hooks_opt_init(struct run_hooks_opt *o) +{ + strvec_init(&o->env); + strvec_init(&o->args); + o->run_hookdir = configured_hookdir_opt(); +} + +void run_hooks_opt_clear(struct run_hooks_opt *o) +{ + strvec_clear(&o->env); + strvec_clear(&o->args); +} + +static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options, + struct child_process *cp) +{ + if (!hook) + return; + + cp->no_stdin = 1; + cp->env = options->env.v; + cp->stdout_to_stderr = 1; + cp->trace2_hook_name = hook->command.buf; + + /* + * Commands from the config could be oneliners, but we know + * for certain that hookdir commands are not. + */ + cp->use_shell = !hook->from_hookdir; + + /* add command */ + strvec_push(&cp->args, hook->command.buf); + + /* + * add passed-in argv, without expanding - let the user get back + * exactly what they put in + */ + strvec_pushv(&cp->args, options->args.v); +} + +int run_hooks(const char *hookname, struct run_hooks_opt *options) +{ + struct strbuf hookname_str = STRBUF_INIT; + struct list_head *to_run, *pos = NULL, *tmp = NULL; + int rc = 0; + + if (!options) + BUG("a struct run_hooks_opt must be provided to run_hooks"); + + strbuf_addstr(&hookname_str, hookname); + + to_run = hook_list(&hookname_str); + + list_for_each_safe(pos, tmp, to_run) { + struct child_process hook_proc = CHILD_PROCESS_INIT; + struct hook *hook = list_entry(pos, struct hook, list); + + if (hook->from_hookdir && + !should_include_hookdir(hook->command.buf, options->run_hookdir)) + continue; + + prepare_hook_cp(hook, options, &hook_proc); + + rc |= run_command(&hook_proc); + } + + return rc; +} diff --git a/hook.h b/hook.h index 1c4b953aec..c24b2c9ecd 100644 --- a/hook.h +++ b/hook.h @@ -1,6 +1,7 @@ #include "config.h" #include "list.h" #include "strbuf.h" +#include "strvec.h" struct hook { struct list_head list; @@ -36,6 +37,31 @@ enum hookdir_opt */ enum hookdir_opt configured_hookdir_opt(void); +struct run_hooks_opt +{ + /* Environment vars to be set for each hook */ + struct strvec env; + + /* Args to be passed to each hook */ + struct strvec args; + + /* + * How should the hookdir be handled? + * Leave the RUN_HOOKS_OPT_INIT default in most cases; this only needs + * to be overridden if the user can override it at the command line. + */ + enum hookdir_opt run_hookdir; +}; + +void run_hooks_opt_init(struct run_hooks_opt *o); +void run_hooks_opt_clear(struct run_hooks_opt *o); + +/* + * Runs all hooks associated to the 'hookname' event in order. Each hook will be + * passed 'env' and 'args'. + */ +int run_hooks(const char *hookname, struct run_hooks_opt *options); + /* Free memory associated with a 'struct hook' */ void free_hook(struct hook *ptr); /* Empties the list at 'head', calling 'free_hook()' on each entry */ diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index a9b1b046c1..1fca83d536 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -115,7 +115,10 @@ test_expect_success 'hook.runHookDir = no is respected by list' ' git hook list pre-commit >actual && # the hookdir annotation is translated - test_i18ncmp expected actual + test_i18ncmp expected actual && + + git hook run pre-commit 2>actual && + test_must_be_empty actual ' test_expect_success 'hook.runHookDir = error is respected by list' ' @@ -129,6 +132,13 @@ test_expect_success 'hook.runHookDir = error is respected by list' ' git hook list pre-commit >actual && # the hookdir annotation is translated + test_i18ncmp expected actual && + + cat >expected <<-EOF && + Skipping legacy hook at '\''$(pwd)/.git/hooks/pre-commit'\'' + EOF + + git hook run pre-commit 2>actual && test_i18ncmp expected actual ' @@ -143,6 +153,14 @@ test_expect_success 'hook.runHookDir = warn is respected by list' ' git hook list pre-commit >actual && # the hookdir annotation is translated + test_i18ncmp expected actual && + + cat >expected <<-EOF && + Running legacy hook at '\''$(pwd)/.git/hooks/pre-commit'\'' + "Legacy Hook" + EOF + + git hook run pre-commit 2>actual && test_i18ncmp expected actual ' @@ -182,7 +200,7 @@ test_expect_success 'git hook list removes skipped inlined hook' ' test_cmp expected actual ' -test_expect_success 'hook.runHookDir = interactive is respected by list' ' +test_expect_success 'hook.runHookDir = interactive is respected by list and run' ' setup_hookdir && test_config hook.runHookDir "interactive" && @@ -193,7 +211,55 @@ test_expect_success 'hook.runHookDir = interactive is respected by list' ' git hook list pre-commit >actual && # the hookdir annotation is translated - test_i18ncmp expected actual + test_i18ncmp expected actual && + + test_write_lines n | git hook run pre-commit 2>actual && + ! grep "Legacy Hook" actual && + + test_write_lines y | git hook run pre-commit 2>actual && + grep "Legacy Hook" actual +' + +test_expect_success 'inline hook definitions execute oneliners' ' + test_config hook.pre-commit.command "echo \"Hello World\"" && + + echo "Hello World" >expected && + + # hooks are run with stdout_to_stderr = 1 + git hook run pre-commit 2>actual && + test_cmp expected actual +' + +test_expect_success 'inline hook definitions resolve paths' ' + write_script sample-hook.sh <<-EOF && + echo \"Sample Hook\" + EOF + + test_when_finished "rm sample-hook.sh" && + + test_config hook.pre-commit.command "\"$(pwd)/sample-hook.sh\"" && + + echo \"Sample Hook\" >expected && + + # hooks are run with stdout_to_stderr = 1 + git hook run pre-commit 2>actual && + test_cmp expected actual +' + +test_expect_success 'hookdir hook included in git hook run' ' + setup_hookdir && + + echo \"Legacy Hook\" >expected && + + # hooks are run with stdout_to_stderr = 1 + git hook run pre-commit 2>actual && + test_cmp expected actual +' + +test_expect_success 'out-of-repo runs excluded' ' + setup_hooks && + + nongit test_must_fail git hook run pre-commit ' test_expect_success 'hook.runHookDir is tolerant to unknown values' ' From patchwork Thu Mar 11 02:10:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130083 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59956C433DB for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1A7D064FAA for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229766AbhCKCLh (ORCPT ); Wed, 10 Mar 2021 21:11:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42198 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229743AbhCKCLH (ORCPT ); Wed, 10 Mar 2021 21:11:07 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AF8DFC061574 for ; Wed, 10 Mar 2021 18:11:07 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id s187so23864319ybs.22 for ; Wed, 10 Mar 2021 18:11:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Kk5R8aI97X58ZOVmCowISGPjO//7aBJKSYMp9czi5eo=; b=q6Zh50j3PUsTiZSgv4Mbm8ZrBy04yoLi/Zto6bVu20Fs2Gpk17KqsSG50dZnQLzvHn H8WGOKucfLYk94fq1oxSnon55al470KDnMxbCA6zF3k/4iUTXYKeNs8mDfKDiNt/skn6 KR/ippUBmm03zLLgw/fyytgvgt32QsQd62FevU3rL9nzJD9sxLs4yrPoeAaFF+L+c038 t850ZwA3ekO0Nm+KHowr3wKl30+x96fkjNhyJmCkKOunzGG7tICjxCGr8Ya5ep1o8uLw S4CMNlVaHZNPuR2q/Uk1VkZE1WalG7j4MwfP8fpVlJVacccfDIeClzDjHxA0yHFRkk0p ehIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Kk5R8aI97X58ZOVmCowISGPjO//7aBJKSYMp9czi5eo=; b=iBsqGcvh8RFpwqaQFR8F2mdQNLs3cyHIG9ZYRRqiOpArqNtNXlyuY6immdqyv3MUCW I7F88HBsl1vqbDXL8wBGFjz30kbjgmNKNWxOuHXruHJ2rU7fdKvI0CHP4faSdVVkFPdz 2OcmlWyXOWnnVIiQw7tcNvpVwxioXmGrXCiCNSUHMC301TiS9Hm3FbDEgWqFqbDYngSd t39pu/7ztr/DHQShUsEiS7W5Gi3gd/Ukam4Dt0bRJ/KKcn5qKBxPI36Mbck5mUo7TooI nwTB/X0Hf+iwST85rcKo7Z1UT64YgMAuohRvYPTDEFsVz3mrCmpUJl/vw8VL9CMnE1me BDmg== X-Gm-Message-State: AOAM531jqYJcCCa3bGKqhmSruKWRbmgEBEFqVUMJgz0MQ1ciGWG0xCb0 ZuezndJv0nqptyM9aE9Gc9yAuvY1IfWYtPBNt57RpjNA23sYRx7H6oZvVKWmUm/k0a+X+ySrLQ3 sGe0ED6c0IrjkYu/H08YUElmyQGvNxWj1uhn14dTFB1FoHQ0nRPVQAIFCHYaL++iev64T1t/1sA == X-Google-Smtp-Source: ABdhPJzBeKtLyAo8DyERj2K9Pb3jlsYgf+c9iX8NX3QHXrEm3Ke7zXxi6pP8NpptOSTIYXJPoNEp6iXachRznZ9czhQ= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:c943:: with SMTP id z64mr8644906ybf.73.1615428666931; Wed, 10 Mar 2021 18:11:06 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:09 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-10-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 09/37] hook: introduce hook_exists() From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Add a helper to easily determine whether any hooks exist for a given hook event. Many callers want to check whether some state could be modified by a hook; that check should include the config-based hooks as well. Optimize by checking the config directly. Since commands which execute hooks might want to take args to replace 'hook.runHookDir', let 'hook_exists()' take a hookdir_opt to override that config. In some cases, external callers today use find_hook() to discover the location of a hook and then run it manually with run-command.h (that is, not with run_hook_le()). Later, those cases will call hook.h:run_hook() directly instead. Once the entire codebase is using hook_exists() instead of find_hook(), find_hook() can be safely rolled into hook_exists() and removed from run-command.h. Signed-off-by: Emily Shaffer --- hook.c | 19 +++++++++++++++++++ hook.h | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/hook.c b/hook.c index d166d17fb0..118931f273 100644 --- a/hook.c +++ b/hook.c @@ -243,6 +243,25 @@ void run_hooks_opt_init(struct run_hooks_opt *o) o->run_hookdir = configured_hookdir_opt(); } +int hook_exists(const char *hookname, enum hookdir_opt should_run_hookdir) +{ + const char *value = NULL; /* throwaway */ + struct strbuf hook_key = STRBUF_INIT; + int could_run_hookdir; + + if (should_run_hookdir == HOOKDIR_USE_CONFIG) + should_run_hookdir = configured_hookdir_opt(); + + could_run_hookdir = (should_run_hookdir == HOOKDIR_INTERACTIVE || + should_run_hookdir == HOOKDIR_WARN || + should_run_hookdir == HOOKDIR_YES) + && !!find_hook(hookname); + + strbuf_addf(&hook_key, "hook.%s.command", hookname); + + return (!git_config_get_value(hook_key.buf, &value)) || could_run_hookdir; +} + void run_hooks_opt_clear(struct run_hooks_opt *o) { strvec_clear(&o->env); diff --git a/hook.h b/hook.h index c24b2c9ecd..0df785add5 100644 --- a/hook.h +++ b/hook.h @@ -23,6 +23,7 @@ struct list_head* hook_list(const struct strbuf *hookname); enum hookdir_opt { + HOOKDIR_USE_CONFIG, HOOKDIR_NO, HOOKDIR_ERROR, HOOKDIR_WARN, @@ -56,6 +57,15 @@ struct run_hooks_opt void run_hooks_opt_init(struct run_hooks_opt *o); void run_hooks_opt_clear(struct run_hooks_opt *o); +/* + * Returns 1 if any hooks are specified in the config or if a hook exists in the + * hookdir. Typically, invoke hook_exsts() like: + * hook_exists(hookname, configured_hookdir_opt()); + * Like with run_hooks, if you take a --run-hookdir flag, reflect that + * user-specified behavior here instead. + */ +int hook_exists(const char *hookname, enum hookdir_opt should_run_hookdir); + /* * Runs all hooks associated to the 'hookname' event in order. Each hook will be * passed 'env' and 'args'. From patchwork Thu Mar 11 02:10:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130091 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3CE6AC4332B for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E5B1364FC5 for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229818AbhCKCLj (ORCPT ); Wed, 10 Mar 2021 21:11:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42214 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229757AbhCKCLJ (ORCPT ); Wed, 10 Mar 2021 21:11:09 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6A950C061574 for ; Wed, 10 Mar 2021 18:11:09 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id j4so23633034ybt.23 for ; Wed, 10 Mar 2021 18:11:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ylnOZikcKWIdIian1z84J6asjhogfYDwv55kcH6FPWQ=; b=IolqxSNEDYHAOJRGlDyuXrvFL8b+ZvClu11YCEQfsEsmlxBhwwv3uEib8PAMErq72u sZbySsG6jv1w2WFT13IDtcZQBWcCLXEB9kfa0EM65Tzs1pOr9I6U6xLo3aX9j+NhRSK9 +dltDTtIS9Z1qynJ/AJWCZwpKs6o4KfZxGX/yrnzY89/SQCB7hPrvwJWyXikYc5abN5i KkqZAu+vT+3THgQzuJH6DW5gLowfW/UHA58TgBX+fNSXiCsWcdmkq17oXkZC3VVrWZj7 hAxbsevQu06m2gyJDP7SqMqZ5Ei4IsrE3OmiH9yMMyC7lO7Ysg9Y1IA8uHT+O8MmvX8S xQug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ylnOZikcKWIdIian1z84J6asjhogfYDwv55kcH6FPWQ=; b=ApWG20+rHLG8xYsLP3SF3Scimk1OMAqKqCKmq93Z7iSW5tLhVORfsOGPdAZJ+IQN10 IUWpupTxxQ2SMQUzmd7omHhy2zRlN9OwUKMjztVzKBAMSZbrM8dIwil8AbTQleE8RMBg oLdYlklgnUW/Kg2m+PyuytiWl9DVzdwLdPSWLtu/2tQcDPRRo6g04z/f8irNGbiWrSKK hoVL+oZfzJA5Nxahmp1OTTuRqj7rnHdMM+5QgibypEVnDYABf7t+Zp2IeLUPfWzSxUSI OJFeyu9gsieSYBe8Vu7NpqarBA30rqPCYIwwxrTnozBpf6WjWaozUO8IfuuipyczMrBt TdFg== X-Gm-Message-State: AOAM533m4MQYefYP8HpCz86RLKoHfJUF/uMV71UFKe+LqfH89PBgmpD+ deCyh84SKJXAtNkxJBHB7UdsftSZGEaAXr4VeGRJfEGgPPK3LVIcG7WIMhm4Fwds6fsiM64X1pY r2pJwH20wRYtyDIPMYVR4R5Tk8q/AZ6nRes/JiRhvYl5owPwz1/llxiXnlFQv4wVux4MFLGXbVw == X-Google-Smtp-Source: ABdhPJwvrmGoKdNtWudCPtEn32ajFotK47fgRePWAz6tE8Rw8Djl5NO76VMrAggsZqELeD0IxKdAazyt9v+E6oqsxnU= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:af4f:: with SMTP id c15mr8722331ybj.85.1615428668622; Wed, 10 Mar 2021 18:11:08 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:10 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-11-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 10/37] hook: support passing stdin to hooks From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Some hooks (such as post-rewrite) need to take input via stdin. Previously, callers provided stdin to hooks by setting run-command.h:child_process.in, which takes a FD. Callers would open the file in question themselves before calling run-command(). However, since we will now need to seek to the front of the file and read it again for every hook which runs, hook.h:run_command() takes a path and handles FD management itself. Since this file is opened for read only, it should not prevent later parallel execution support. On the frontend, this is supported by asking for a file path, rather than by reading stdin. Reading directly from stdin would involve caching the entire stdin (to memory or to disk) and reading it back from the beginning to each hook. We'd want to support cases like insufficient memory or storage for the file. While this may prove useful later, for now the path of least resistance is to just ask the user to make this interim file themselves. Signed-off-by: Emily Shaffer --- Documentation/git-hook.txt | 11 +++++++++-- builtin/hook.c | 5 ++++- hook.c | 8 +++++++- hook.h | 6 +++++- t/t1360-config-based-hooks.sh | 24 ++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index 8f96c347ea..96a857c682 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -9,7 +9,8 @@ SYNOPSIS -------- [verse] 'git hook' list -'git hook' run [(-e|--env)=...] [(-a|--arg)=...] +'git hook' run [(-e|--env)=...] [(-a|--arg)=...] [--to-stdin=] + DESCRIPTION ----------- @@ -98,7 +99,7 @@ in the order they should be run, and print the config scope where the relevant `hook..command` was specified, not the `hookcmd` (if applicable). This output is human-readable and the format is subject to change over time. -run [(-e|--env)=...] [(-a|--arg)=...] ``:: +run [(-e|--env)=...] [(-a|--arg)=...] [--to-stdin=] ``:: Runs hooks configured for ``, in the same order displayed by `git hook list`. Hooks configured this way may be run prepended with `sh -c`, so @@ -124,6 +125,12 @@ Specify arguments to pass to every hook that is run. + Specify environment variables to set for every hook that is run. +--to-stdin:: + Only valid for `run`. ++ +Specify a file which will be streamed into stdin for every hook that is run. +Each hook will receive the entire file from beginning to EOF. + CONFIGURATION ------------- include::config/hook.txt[] diff --git a/builtin/hook.c b/builtin/hook.c index e823a96238..38a4555e05 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -8,7 +8,8 @@ static const char * const builtin_hook_usage[] = { N_("git hook list "), - N_("git hook run [(-e|--env)=...] [(-a|--arg)=...] "), + N_("git hook run [(-e|--env)=...] [(-a|--arg)=...]" + "[--to-stdin=] "), NULL }; @@ -101,6 +102,8 @@ static int run(int argc, const char **argv, const char *prefix) N_("environment variables for hook to use")), OPT_STRVEC('a', "arg", &opt.args, N_("args"), N_("argument to pass to hook")), + OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"), + N_("file to read into hooks' stdin")), OPT_END(), }; diff --git a/hook.c b/hook.c index 118931f273..f906e8c61c 100644 --- a/hook.c +++ b/hook.c @@ -240,6 +240,7 @@ void run_hooks_opt_init(struct run_hooks_opt *o) { strvec_init(&o->env); strvec_init(&o->args); + o->path_to_stdin = NULL; o->run_hookdir = configured_hookdir_opt(); } @@ -274,7 +275,12 @@ static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options, if (!hook) return; - cp->no_stdin = 1; + /* reopen the file for stdin; run_command closes it. */ + if (options->path_to_stdin) + cp->in = xopen(options->path_to_stdin, O_RDONLY); + else + cp->no_stdin = 1; + cp->env = options->env.v; cp->stdout_to_stderr = 1; cp->trace2_hook_name = hook->command.buf; diff --git a/hook.h b/hook.h index 0df785add5..2314ec5962 100644 --- a/hook.h +++ b/hook.h @@ -52,6 +52,9 @@ struct run_hooks_opt * to be overridden if the user can override it at the command line. */ enum hookdir_opt run_hookdir; + + /* Path to file which should be piped to stdin for each hook */ + const char *path_to_stdin; }; void run_hooks_opt_init(struct run_hooks_opt *o); @@ -68,7 +71,8 @@ int hook_exists(const char *hookname, enum hookdir_opt should_run_hookdir); /* * Runs all hooks associated to the 'hookname' event in order. Each hook will be - * passed 'env' and 'args'. + * passed 'env' and 'args'. The file at 'stdin_path' will be closed and reopened + * for each hook that runs. */ int run_hooks(const char *hookname, struct run_hooks_opt *options); diff --git a/t/t1360-config-based-hooks.sh b/t/t1360-config-based-hooks.sh index 1fca83d536..cace5a23c1 100755 --- a/t/t1360-config-based-hooks.sh +++ b/t/t1360-config-based-hooks.sh @@ -276,4 +276,28 @@ test_expect_success 'hook.runHookDir is tolerant to unknown values' ' test_i18ncmp expected actual ' +test_expect_success 'stdin to multiple hooks' ' + git config --add hook.test.command "xargs -P1 -I% echo a%" && + git config --add hook.test.command "xargs -P1 -I% echo b%" && + test_when_finished "test_unconfig hook.test.command" && + + cat >input <<-EOF && + 1 + 2 + 3 + EOF + + cat >expected <<-EOF && + a1 + a2 + a3 + b1 + b2 + b3 + EOF + + git hook run --to-stdin=input test 2>actual && + test_cmp expected actual +' + test_done From patchwork Thu Mar 11 02:10:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130085 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C0B95C43381 for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8426864FBF for ; Thu, 11 Mar 2021 02:12:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229821AbhCKCLj (ORCPT ); Wed, 10 Mar 2021 21:11:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42216 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229762AbhCKCLL (ORCPT ); Wed, 10 Mar 2021 21:11:11 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 23ADAC061574 for ; Wed, 10 Mar 2021 18:11:11 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id a63so23852711yba.2 for ; Wed, 10 Mar 2021 18:11:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=9OCy3vXtF54gXMJlKk7YyB6MxtyztYa4LcpqDc08UtY=; b=eoIRxh9ApeN80U23D9LW+YmOZTCXQ5rR4J9Mbk+oMr+JGEaqsl9vMXTxGk27rHmphB /9eDp5jlwhwLfL2vLEkyQlddi3uz8RDAvSvqo8ii/lRmWPdgms8SC3NWy5MUjEJWcPGR W8qNyQhBm6+3J0PZ8EBhzdAUMVlvh1gyIbd+e8Cv+IqBfHr8jAFgThWGlgtcWcq6YHUg 4YMlFag+xtOHJraPQdbDOUg+srqfbXsJNn24Pcu9sRZC2aAJwes4SZPAxXcqtLowPSNE nFZ94+hmfbWoanBvSsCE+HgZS16fM1WccTcfABS9E4/CROTt8yV5fC2ltMG8UKQAeIZy l9oQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=9OCy3vXtF54gXMJlKk7YyB6MxtyztYa4LcpqDc08UtY=; b=ea392ojSJG2Mc83V2bMTcJrpc4V28l3WKskPoyQ3kPomsqbqJt4dmj74VkE69udaxh OCRs8kVUXCkV/re6guzThadeTzJIjEoqbCDtLlAa0L4LDmFL58Ys8qHhM38vD5pDe6fq rm2SGrJ7mGeA0MEsY6Ksyjp6ajHgwCi94Ti/ev111xrgJU7koXH4uD73+r6V2YcaxF+P +xcRRcajzLbeXQdxbCEhM9xi2aK22zlhvt/+YmIEszOmO4ro74bnUiWnr23UGvOYGgqB 8gQgmL+0RTepI9+FZvNAR+kK5weVcb3o3k1YRC0vXx3PJtLDTq5iwen1WZTb6S17sXT4 67Tg== X-Gm-Message-State: AOAM531XILWERTUZUKGOhDTVuVE8th0fHsfTzTqW3XstRfh+V4Tzk0VW /HEs4Wf/9DB1IXjm/Vi5rxO1ZNBl5tFe1hbYS0QlA4izAxSpRI/KO5snLw2S3FtO0sfdQRQUGbM yD03hIlpiaKiIBKiYCPUQBDWw7iK3XiHPyfFmRCCitEBfP8q+ZAwV/4iZoyEkkjZTDaMlVBQcHg == X-Google-Smtp-Source: ABdhPJwWTrMHi9ZizFqUknXUi6ZML29SpUVt7fTxiRlRE1PG3r5MXOebhiXLGBX3+xbAqjRafDygCm90A1wgT4T45no= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:cfc5:: with SMTP id f188mr8369771ybg.485.1615428670361; Wed, 10 Mar 2021 18:11:10 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:11 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-12-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 11/37] run-command: allow stdin for run_processes_parallel From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org While it makes sense not to inherit stdin from the parent process to avoid deadlocking, it's not necessary to completely ban stdin to children. An informed user should be able to configure stdin safely. By setting `some_child.process.no_stdin=1` before calling `get_next_task()` we provide a reasonable default behavior but enable users to set up stdin streaming for themselves during the callback. `some_child.process.stdout_to_stderr`, however, remains unmodifiable by `get_next_task()` - the rest of the run_processes_parallel() API depends on child output in stderr. Signed-off-by: Emily Shaffer --- run-command.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/run-command.c b/run-command.c index 4e34623e2e..e6d7541b84 100644 --- a/run-command.c +++ b/run-command.c @@ -1693,6 +1693,14 @@ static int pp_start_one(struct parallel_processes *pp) if (i == pp->max_processes) BUG("bookkeeping is hard"); + /* + * By default, do not inherit stdin from the parent process - otherwise, + * all children would share stdin! Users may overwrite this to provide + * something to the child's stdin by having their 'get_next_task' + * callback assign 0 to .no_stdin and an appropriate integer to .in. + */ + pp->children[i].process.no_stdin = 1; + code = pp->get_next_task(&pp->children[i].process, &pp->children[i].err, pp->data, @@ -1704,7 +1712,6 @@ static int pp_start_one(struct parallel_processes *pp) } pp->children[i].process.err = -1; pp->children[i].process.stdout_to_stderr = 1; - pp->children[i].process.no_stdin = 1; if (start_command(&pp->children[i].process)) { code = pp->start_failure(&pp->children[i].err, From patchwork Thu Mar 11 02:10:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130113 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9E399C4332E for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6E5F764FD0 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229843AbhCKCLj (ORCPT ); Wed, 10 Mar 2021 21:11:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42226 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229774AbhCKCLN (ORCPT ); Wed, 10 Mar 2021 21:11:13 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E88CFC061574 for ; Wed, 10 Mar 2021 18:11:12 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 6so23891204ybq.7 for ; Wed, 10 Mar 2021 18:11:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=lkAVMRoNnAO5iSSXdpavWYaf7kpDdRwGqRdd5exn/tY=; b=ZWUdnyroFzJo6ofVTCFp3Z2QrbfLMc5YlwsV+O+izMtACRQgqYc8rQXG50wn5n80bN VNJvm/9fZKUbL+Idyunm9o6oHIcynOZ4BYkvileM5t8MkVQkvvkvHAn5gnr0ywh5XdFr V5pgGcQ+oCuL9Kn9SJSmQ9rBztt+/qDndnSUffZ/2jHDDwiRXHKGPv5/DpD6cp1j5Pzs 03/0PKiJcA1KoyxUjgJRLJYARxp+xXHkCf7wxrEoARuBHrnlJ+bxxKpGJ96d6+rLwzeI WUWED/RMoj0xF78vS3RwCIjl4HZNlMRfcKWrS8GYRP8dB6niTXXH0K/Xex9RjJCrvSVh PsoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=lkAVMRoNnAO5iSSXdpavWYaf7kpDdRwGqRdd5exn/tY=; b=YYieI/9uum7q3MtnDsYfYmQEY1iyp6PY3Ts33LYWC//qqX2GOQM7n3pomOOZI7+6Mr 51s59Eym0944O7DuPD5dYDkbKpM+0DDEV0D//vu0CCgN1E9XZoZxJjWUENlxQRm/nElj xJSLSs/lolZbCcKWEOrG0zFQxG9aD5dNRW+4VVu0FuC8CCeC5lHX3uynzrW0sxKoWu0S BfghX3vdjRf8kc1S8cON7gvRjEXRUniWojNvlUmyrhHICjEP2q8JoJdW6/2irDT7LJAa cjCceABFFFv5hH6wrMvOggjmLbQ5MunMhNY+iLCW+FYexXnF3vVxylQ9cw6QmD+MP9al N2zQ== X-Gm-Message-State: AOAM531nbbtp/HPJWTOXSdkeEhYlNdCpQqHxET3u2HX1cG8Iiu0DQyXn dbWrgkwyD1LL/NkPz+ZRYeiGzbVkOnqkrSiLMUcTa6D9LMXz69dUyN9GAYg++qs+HuQffQ+6nXF tfwq0gHywmCdhCGz8gHzM7ravAXvlfHhrVEavJKIxoi5tI6rBwHKLNSnZG2DGbNy4Qe4XyDR78Q == X-Google-Smtp-Source: ABdhPJxZEufD4J1uOqx6vUL8snHfPpwxfFHhnFKiNDdrRzzxi98o9RXlo/0G0PG7f70vClNSIZzS4SpHNmHfOdjvHug= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6902:4b3:: with SMTP id r19mr8485666ybs.432.1615428672152; Wed, 10 Mar 2021 18:11:12 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:12 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-13-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 12/37] hook: allow parallel hook execution From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org In many cases, there's no reason not to allow hooks to execute in parallel. run_processes_parallel() is well-suited - it's a task queue that runs its housekeeping in series, which means users don't need to worry about thread safety on their callback data. True multithreaded execution with the async_* functions isn't necessary here. Synchronous hook execution can be achieved by only allowing 1 job to run at a time. Teach run_hooks() to use that function for simple hooks which don't require stdin or capture of stderr. Signed-off-by: Emily Shaffer --- Notes: Per AEvar's request - parallel hook execution on day zero. In most ways run_processes_parallel() worked great for me - but it didn't have great support for hooks where we pipe to and from. I had to add this support later in the series. Since I modified an existing and in-use library I'd appreciate a keen look on these patches. - Emily Documentation/config/hook.txt | 5 ++ Documentation/git-hook.txt | 14 ++++- builtin/hook.c | 6 +- hook.c | 108 +++++++++++++++++++++++++++++----- hook.h | 21 ++++++- 5 files changed, 132 insertions(+), 22 deletions(-) diff --git a/Documentation/config/hook.txt b/Documentation/config/hook.txt index 8b12512e33..4f66bb35cf 100644 --- a/Documentation/config/hook.txt +++ b/Documentation/config/hook.txt @@ -20,3 +20,8 @@ hook.runHookDir:: Controls how hooks contained in your hookdir are executed. Can be any of "yes", "warn", "interactive", or "no". Defaults to "yes". See linkgit:git-hook[1] and linkgit:git-config[1] "core.hooksPath"). + +hook.jobs:: + Specifies how many hooks can be run simultaneously during parallelized + hook execution. If unspecified, defaults to the number of processors on + the current system. diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index 96a857c682..81b8e94994 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git hook' list 'git hook' run [(-e|--env)=...] [(-a|--arg)=...] [--to-stdin=] - + [(-j|--jobs) ] DESCRIPTION ----------- @@ -99,7 +99,7 @@ in the order they should be run, and print the config scope where the relevant `hook..command` was specified, not the `hookcmd` (if applicable). This output is human-readable and the format is subject to change over time. -run [(-e|--env)=...] [(-a|--arg)=...] [--to-stdin=] ``:: +run [(-e|--env)=...] [(-a|--arg)=...] [--to-stdin=] [(-j|--jobs)] ``:: Runs hooks configured for ``, in the same order displayed by `git hook list`. Hooks configured this way may be run prepended with `sh -c`, so @@ -131,6 +131,16 @@ Specify environment variables to set for every hook that is run. Specify a file which will be streamed into stdin for every hook that is run. Each hook will receive the entire file from beginning to EOF. +-j:: +--jobs:: + Only valid for `run`. ++ +Specify how many hooks to run simultaneously. If this flag is not specified, use +the value of the `hook.jobs` config. If the config is not specified, use the +number of CPUs on the current system. Some hooks may be ineligible for +parallelization: for example, 'commit-msg' intends hooks modify the commit +message body and cannot be parallelized. + CONFIGURATION ------------- include::config/hook.txt[] diff --git a/builtin/hook.c b/builtin/hook.c index 38a4555e05..b4f4adb1de 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -9,7 +9,7 @@ static const char * const builtin_hook_usage[] = { N_("git hook list "), N_("git hook run [(-e|--env)=...] [(-a|--arg)=...]" - "[--to-stdin=] "), + "[--to-stdin=] [(-j|--jobs) ] "), NULL }; @@ -104,10 +104,12 @@ static int run(int argc, const char **argv, const char *prefix) N_("argument to pass to hook")), OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"), N_("file to read into hooks' stdin")), + OPT_INTEGER('j', "jobs", &opt.jobs, + N_("run up to hooks simultaneously")), OPT_END(), }; - run_hooks_opt_init(&opt); + run_hooks_opt_init_async(&opt); argc = parse_options(argc, argv, prefix, run_options, builtin_hook_usage, 0); diff --git a/hook.c b/hook.c index f906e8c61c..fe8860860b 100644 --- a/hook.c +++ b/hook.c @@ -144,6 +144,14 @@ enum hookdir_opt configured_hookdir_opt(void) return HOOKDIR_UNKNOWN; } +int configured_hook_jobs(void) +{ + int n = online_cpus(); + git_config_get_int("hook.jobs", &n); + + return n; +} + static int should_include_hookdir(const char *path, enum hookdir_opt cfg) { struct strbuf prompt = STRBUF_INIT; @@ -236,12 +244,19 @@ struct list_head* hook_list(const struct strbuf* hookname) return hook_head; } -void run_hooks_opt_init(struct run_hooks_opt *o) +void run_hooks_opt_init_sync(struct run_hooks_opt *o) { strvec_init(&o->env); strvec_init(&o->args); o->path_to_stdin = NULL; o->run_hookdir = configured_hookdir_opt(); + o->jobs = 1; +} + +void run_hooks_opt_init_async(struct run_hooks_opt *o) +{ + run_hooks_opt_init_sync(o); + o->jobs = configured_hook_jobs(); } int hook_exists(const char *hookname, enum hookdir_opt should_run_hookdir) @@ -269,19 +284,26 @@ void run_hooks_opt_clear(struct run_hooks_opt *o) strvec_clear(&o->args); } -static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options, - struct child_process *cp) +static int pick_next_hook(struct child_process *cp, + struct strbuf *out, + void *pp_cb, + void **pp_task_cb) { + struct hook_cb_data *hook_cb = pp_cb; + struct hook *hook = hook_cb->run_me; + if (!hook) - return; + return 0; /* reopen the file for stdin; run_command closes it. */ - if (options->path_to_stdin) - cp->in = xopen(options->path_to_stdin, O_RDONLY); - else + if (hook_cb->options->path_to_stdin) { + cp->no_stdin = 0; + cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY); + } else { cp->no_stdin = 1; + } - cp->env = options->env.v; + cp->env = hook_cb->options->env.v; cp->stdout_to_stderr = 1; cp->trace2_hook_name = hook->command.buf; @@ -298,14 +320,59 @@ static void prepare_hook_cp(struct hook *hook, struct run_hooks_opt *options, * add passed-in argv, without expanding - let the user get back * exactly what they put in */ - strvec_pushv(&cp->args, options->args.v); + strvec_pushv(&cp->args, hook_cb->options->args.v); + + /* Provide context for errors if necessary */ + *pp_task_cb = hook; + + /* Get the next entry ready */ + if (hook_cb->run_me->list.next == hook_cb->head) + hook_cb->run_me = NULL; + else + hook_cb->run_me = list_entry(hook_cb->run_me->list.next, + struct hook, list); + + return 1; +} + +static int notify_start_failure(struct strbuf *out, + void *pp_cb, + void *pp_task_cp) +{ + struct hook_cb_data *hook_cb = pp_cb; + struct hook *attempted = pp_task_cp; + + /* |= rc in cb */ + hook_cb->rc |= 1; + + strbuf_addf(out, _("Couldn't start '%s', configured in '%s'\n"), + attempted->command.buf, + attempted->from_hookdir ? "hookdir" + : config_scope_name(attempted->origin)); + + /* NEEDSWORK: if halt_on_error is desired, do it here. */ + return 0; +} + +static int notify_hook_finished(int result, + struct strbuf *out, + void *pp_cb, + void *pp_task_cb) +{ + struct hook_cb_data *hook_cb = pp_cb; + + /* |= rc in cb */ + hook_cb->rc |= result; + + /* NEEDSWORK: if halt_on_error is desired, do it here. */ + return 0; } int run_hooks(const char *hookname, struct run_hooks_opt *options) { struct strbuf hookname_str = STRBUF_INIT; struct list_head *to_run, *pos = NULL, *tmp = NULL; - int rc = 0; + struct hook_cb_data cb_data = { 0, NULL, NULL, options }; if (!options) BUG("a struct run_hooks_opt must be provided to run_hooks"); @@ -315,17 +382,26 @@ int run_hooks(const char *hookname, struct run_hooks_opt *options) to_run = hook_list(&hookname_str); list_for_each_safe(pos, tmp, to_run) { - struct child_process hook_proc = CHILD_PROCESS_INIT; struct hook *hook = list_entry(pos, struct hook, list); if (hook->from_hookdir && !should_include_hookdir(hook->command.buf, options->run_hookdir)) - continue; + list_del(pos); + } + + if (list_empty(to_run)) + return 0; - prepare_hook_cp(hook, options, &hook_proc); + cb_data.head = to_run; + cb_data.run_me = list_entry(to_run->next, struct hook, list); - rc |= run_command(&hook_proc); - } + run_processes_parallel_tr2(options->jobs, + pick_next_hook, + notify_start_failure, + notify_hook_finished, + &cb_data, + "hook", + hookname); - return rc; + return cb_data.rc; } diff --git a/hook.h b/hook.h index 2314ec5962..2593f932c0 100644 --- a/hook.h +++ b/hook.h @@ -38,6 +38,9 @@ enum hookdir_opt */ enum hookdir_opt configured_hookdir_opt(void); +/* Provides the number of threads to use for parallel hook execution. */ +int configured_hook_jobs(void); + struct run_hooks_opt { /* Environment vars to be set for each hook */ @@ -48,16 +51,30 @@ struct run_hooks_opt /* * How should the hookdir be handled? - * Leave the RUN_HOOKS_OPT_INIT default in most cases; this only needs + * Leave the run_hooks_opt_init_*() default in most cases; this only needs * to be overridden if the user can override it at the command line. */ enum hookdir_opt run_hookdir; /* Path to file which should be piped to stdin for each hook */ const char *path_to_stdin; + + /* Number of threads to parallelize across */ + int jobs; +}; + +/* + * Callback provided to feed_pipe_fn and consume_sideband_fn. + */ +struct hook_cb_data { + int rc; + struct list_head *head; + struct hook *run_me; + struct run_hooks_opt *options; }; -void run_hooks_opt_init(struct run_hooks_opt *o); +void run_hooks_opt_init_sync(struct run_hooks_opt *o); +void run_hooks_opt_init_async(struct run_hooks_opt *o); void run_hooks_opt_clear(struct run_hooks_opt *o); /* From patchwork Thu Mar 11 02:10:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130095 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C6E52C432C3 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A79EE64FD6 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229899AbhCKCLk (ORCPT ); Wed, 10 Mar 2021 21:11:40 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42234 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229775AbhCKCLO (ORCPT ); Wed, 10 Mar 2021 21:11:14 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C5470C061574 for ; Wed, 10 Mar 2021 18:11:14 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id a63so23853250yba.2 for ; Wed, 10 Mar 2021 18:11:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=y5KEi9LP/xaggFvLKu3f7FcevOOqeTYz4zzf4x58B74=; b=fAZtIswBXYBjuHTMq1Q4f1XJD9ej3rWAaYtdaNRMi55/D6ueu8LT+VoYSXkPeC/bux 0yVDy2E3EY+xEJpK0Ak8k7oIXvR8z0ntdtHFM/Wj6AZrKwEaALqibcigZPdxYIzkvrmq nwmy+a2lD4H8bEhMeVYrGOJV2J8or2ozg1MEVWtB0L30aQpQ3+FFLMYLihpXwX5y+hRa CZjLgZ05zPAa4Q1Ldtq/wMKow2cifK3OvBOBeSd6U1tr2syf5yQsv9OfRwyUAzoCE8yD zxXoUXSd7SByp5CpJMoFVqxftzCFybVq2Q20TSLjY/oJ31zIp7iFLKWPiJY4QGV9D9mr bY9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=y5KEi9LP/xaggFvLKu3f7FcevOOqeTYz4zzf4x58B74=; b=N2aThHiLaOKWKXDqfQw6XLVjJ9cL+rxl1GlyV2O4XGM3R6HAVMkD6l4HiosU98DhuY Zy+WumAdqIcLPp4sYe+9D7+wVmwnB/g5HbZ1xn8tuixsHsx+Wx0sIPYugsUL3ujUODXN 5zdIOaKuzriGMK84H9d+wjBI8Nnb1+lg4HMPCiQaZOiR7nk/2hvbuwULRKpToMmWqZIo oXncp8PKaRdClNGnjfBC3ryWLkB5FQhzY4VDWl3S6P7SHKYF0kLNh1rlg8yH04K9Wo1v 95eqhFDPvD7dFzjdh7+rgWA7EfOzlG8YJpIscMxj++Aa04qDMH7F8lkGCmEQU64/1cPB FfkQ== X-Gm-Message-State: AOAM530nuyHh5kgA2rWbPm3NP2IA1TSsLg85qEQOAXDRLa4H+lUpTY1O Vp8FidncsJ1F73tYoj1RNlMzSUFN4vrQ89SKfHYPdtN5mbEAhF1hif7Tkfm/gM3nLCUIGx/0lTf o7wyehAVBssUjfPaFU9ljot8+ARdMGHFsaoStzAhnxMx0VSmPMHZr9pMrUe6C0ruJqGmiD5N33Q == X-Google-Smtp-Source: ABdhPJxlUQjuKafhmmaCVawA6CYgeIwtGcMaZyxfTr/Qs3DCRNQa+ZPcL/Azb8A3UPzXB3k40UZS+gCr4WPrS8kil34= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:c106:: with SMTP id r6mr7559235ybf.136.1615428674036; Wed, 10 Mar 2021 18:11:14 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:13 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-14-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 13/37] hook: allow specifying working directory for hooks From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Hooks like "post-checkout" require that hooks have a different working directory than the initial process. Pipe that directly through to struct child_process. Because we can just run 'git -C hook run ...' it shouldn't be necessary to pipe this option through the frontend. In fact, this reduces the possibility of users running hooks which affect some part of the filesystem outside of the repo in question. Signed-off-by: Emily Shaffer --- Notes: Needed later for "post-checkout" conversion. hook.c | 2 ++ hook.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/hook.c b/hook.c index fe8860860b..67ad3aa747 100644 --- a/hook.c +++ b/hook.c @@ -251,6 +251,7 @@ void run_hooks_opt_init_sync(struct run_hooks_opt *o) o->path_to_stdin = NULL; o->run_hookdir = configured_hookdir_opt(); o->jobs = 1; + o->dir = NULL; } void run_hooks_opt_init_async(struct run_hooks_opt *o) @@ -306,6 +307,7 @@ static int pick_next_hook(struct child_process *cp, cp->env = hook_cb->options->env.v; cp->stdout_to_stderr = 1; cp->trace2_hook_name = hook->command.buf; + cp->dir = hook_cb->options->dir; /* * Commands from the config could be oneliners, but we know diff --git a/hook.h b/hook.h index 2593f932c0..fcd8e99e39 100644 --- a/hook.h +++ b/hook.h @@ -61,6 +61,9 @@ struct run_hooks_opt /* Number of threads to parallelize across */ int jobs; + + /* Path to initial working directory for subprocess */ + const char *dir; }; /* From patchwork Thu Mar 11 02:10:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130099 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BDF5AC43332 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8B6A164FDA for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229920AbhCKCLl (ORCPT ); Wed, 10 Mar 2021 21:11:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42244 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229790AbhCKCLQ (ORCPT ); Wed, 10 Mar 2021 21:11:16 -0500 Received: from mail-qv1-xf49.google.com (mail-qv1-xf49.google.com [IPv6:2607:f8b0:4864:20::f49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AF83EC061574 for ; Wed, 10 Mar 2021 18:11:16 -0800 (PST) Received: by mail-qv1-xf49.google.com with SMTP id u15so14181759qvo.13 for ; Wed, 10 Mar 2021 18:11:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=rJUqqYC0NKQ3YLphoJGTD72X9zm/uxS3ZmMn+jEbwEo=; b=VY+Uni6eWEuwzQeybiw7OR6437k8C2bqa+ysJPnl/Hh4XBL8GFylI6tNOvN8EMc3Pf 5mXhr5ZC3QJdJ+6iDlgWoc32KYiJJnoYDgMTYYMvTVMiHvGGjOAGgjYb9PwwTUeZwnPJ jpHJhWtHIdMhvSuoqxyTsFASOtXxVfuSxqZV7PzZusGTbnIQwpHDwqK3KEA/V6gFkwZU p1VMOHP/VvNEAEu4Tzuuvp3GMXdBBV+qKZY0quFJtcK1gIwdqGXxL/OPjYgxz9tjK9wI BJ/ip9xfuKuOum5vq1O8A9HxziPnFrvFNyufiyXuAN2DaSWAMvUwWS0yq+POqHJVfzfw JP2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=rJUqqYC0NKQ3YLphoJGTD72X9zm/uxS3ZmMn+jEbwEo=; b=eEYi9fx04WxEus/HHr9l1T33woCo21HkBlOREMJ15PmTm7k8W2wB1lu5UNwb3mkqH3 pKqlavUX2Ss7PvzukluysP+deC/TXTwiO7Mx4KxlY3BwuAqc2LLnfb9Ll8nL31Rxwvcc sivuPzTJTvmRFHY1Ynyqgulq2TvDXI+vuExP6L4QkaVKFpNtqgEqAVtLpSiND5+R3skc jlcpFdz0/BugsH+1FVIPbg1I+y4F8xelQviFhXUoNw2KmqzNQdU0u7K66oPYiB0HHaC0 X9aBvxoJ8n3eQwKpuJ3k9NTxqUV2EsN0sn9KOv2M2zc5DOmKkSdDcTJ+nJL6X8kSrz02 HTLQ== X-Gm-Message-State: AOAM531nfwlrLHQJ3t7I2yRr5Fa624X7wKdOs1Qr/7YEY98wnWd07aW2 GZp2z75q34cp4yUSf6RDGC3A851DFu9oXoBS59BvdIXIo/Sd12wEUn6wrmgAw4AhZ4wail1vpwM le+RbfwFzoXoF3OLLyUAUyOP3ojxFmimubRnEe0XY3tu4uEf1i3mSentwDSSTMEe4ncHXbvz5yw == X-Google-Smtp-Source: ABdhPJwNK9lgF5tI3IlVpsCzNu0rjL2bSH8foxm9Zf919XABscazDx7kUOY8UcFjoj+v3FJQg/lyqLqYZ+y2YCYB75E= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:ad4:4745:: with SMTP id c5mr6008604qvx.39.1615428675856; Wed, 10 Mar 2021 18:11:15 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:14 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-15-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 14/37] run-command: add stdin callback for parallelization From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org If a user of the run_processes_parallel() API wants to pipe a large amount of information to stdin of each parallel command, that information could exceed the buffer of the pipe allocated for that process's stdin. Generally this is solved by repeatedly writing to child_process.in between calls to start_command() and finish_command(); run_processes_parallel() did not provide users an opportunity to access child_process at that time. Because the data might be extremely large (for example, a list of all refs received during a push from a client) simply taking a string_list or strbuf is not as scalable as using a callback; the rest of the run_processes_parallel() API also uses callbacks, so making this feature match the rest of the API reduces mental load on the user. Signed-off-by: Emily Shaffer --- builtin/fetch.c | 1 + builtin/submodule--helper.c | 2 +- hook.c | 1 + run-command.c | 54 +++++++++++++++++++++++++++++++++++-- run-command.h | 17 +++++++++++- submodule.c | 1 + t/helper/test-run-command.c | 31 ++++++++++++++++++--- t/t0061-run-command.sh | 30 +++++++++++++++++++++ 8 files changed, 129 insertions(+), 8 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 0b90de87c7..d8e798dc69 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1757,6 +1757,7 @@ static int fetch_multiple(struct string_list *list, int max_children) result = run_processes_parallel_tr2(max_children, &fetch_next_remote, &fetch_failed_to_start, + NULL, &fetch_finished, &state, "fetch", "parallel/fetch"); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 9d505a6329..14f6e4ee8c 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2294,7 +2294,7 @@ static int update_submodules(struct submodule_update_clone *suc) int i; run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task, - update_clone_start_failure, + update_clone_start_failure, NULL, update_clone_task_finished, suc, "submodule", "parallel/update"); diff --git a/hook.c b/hook.c index 67ad3aa747..9088b520f3 100644 --- a/hook.c +++ b/hook.c @@ -400,6 +400,7 @@ int run_hooks(const char *hookname, struct run_hooks_opt *options) run_processes_parallel_tr2(options->jobs, pick_next_hook, notify_start_failure, + NULL, notify_hook_finished, &cb_data, "hook", diff --git a/run-command.c b/run-command.c index e6d7541b84..e7eeb6c49b 100644 --- a/run-command.c +++ b/run-command.c @@ -1558,6 +1558,7 @@ struct parallel_processes { get_next_task_fn get_next_task; start_failure_fn start_failure; + feed_pipe_fn feed_pipe; task_finished_fn task_finished; struct { @@ -1585,6 +1586,13 @@ static int default_start_failure(struct strbuf *out, return 0; } +static int default_feed_pipe(struct strbuf *pipe, + void *pp_cb, + void *pp_task_cb) +{ + return 1; +} + static int default_task_finished(int result, struct strbuf *out, void *pp_cb, @@ -1615,6 +1623,7 @@ static void pp_init(struct parallel_processes *pp, int n, get_next_task_fn get_next_task, start_failure_fn start_failure, + feed_pipe_fn feed_pipe, task_finished_fn task_finished, void *data) { @@ -1633,6 +1642,7 @@ static void pp_init(struct parallel_processes *pp, pp->get_next_task = get_next_task; pp->start_failure = start_failure ? start_failure : default_start_failure; + pp->feed_pipe = feed_pipe ? feed_pipe : default_feed_pipe; pp->task_finished = task_finished ? task_finished : default_task_finished; pp->nr_processes = 0; @@ -1730,6 +1740,37 @@ static int pp_start_one(struct parallel_processes *pp) return 0; } +static void pp_buffer_stdin(struct parallel_processes *pp) +{ + int i; + struct strbuf sb = STRBUF_INIT; + + /* Buffer stdin for each pipe. */ + for (i = 0; i < pp->max_processes; i++) { + if (pp->children[i].state == GIT_CP_WORKING && + pp->children[i].process.in > 0) { + int done; + strbuf_reset(&sb); + done = pp->feed_pipe(&sb, pp->data, + pp->children[i].data); + if (sb.len) { + if (write_in_full(pp->children[i].process.in, + sb.buf, sb.len) < 0) { + if (errno != EPIPE) + die_errno("write"); + done = 1; + } + } + if (done) { + close(pp->children[i].process.in); + pp->children[i].process.in = 0; + } + } + } + + strbuf_release(&sb); +} + static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout) { int i; @@ -1794,6 +1835,7 @@ static int pp_collect_finished(struct parallel_processes *pp) pp->nr_processes--; pp->children[i].state = GIT_CP_FREE; pp->pfd[i].fd = -1; + pp->children[i].process.in = 0; child_process_init(&pp->children[i].process); if (i != pp->output_owner) { @@ -1827,6 +1869,7 @@ static int pp_collect_finished(struct parallel_processes *pp) int run_processes_parallel(int n, get_next_task_fn get_next_task, start_failure_fn start_failure, + feed_pipe_fn feed_pipe, task_finished_fn task_finished, void *pp_cb) { @@ -1835,7 +1878,9 @@ int run_processes_parallel(int n, int spawn_cap = 4; struct parallel_processes pp; - pp_init(&pp, n, get_next_task, start_failure, task_finished, pp_cb); + sigchain_push(SIGPIPE, SIG_IGN); + + pp_init(&pp, n, get_next_task, start_failure, feed_pipe, task_finished, pp_cb); while (1) { for (i = 0; i < spawn_cap && !pp.shutdown && @@ -1852,6 +1897,7 @@ int run_processes_parallel(int n, } if (!pp.nr_processes) break; + pp_buffer_stdin(&pp); pp_buffer_stderr(&pp, output_timeout); pp_output(&pp); code = pp_collect_finished(&pp); @@ -1863,11 +1909,15 @@ int run_processes_parallel(int n, } pp_cleanup(&pp); + + sigchain_pop(SIGPIPE); + return 0; } int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, start_failure_fn start_failure, + feed_pipe_fn feed_pipe, task_finished_fn task_finished, void *pp_cb, const char *tr2_category, const char *tr2_label) { @@ -1877,7 +1927,7 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, ((n < 1) ? online_cpus() : n)); result = run_processes_parallel(n, get_next_task, start_failure, - task_finished, pp_cb); + feed_pipe, task_finished, pp_cb); trace2_region_leave(tr2_category, tr2_label, NULL); diff --git a/run-command.h b/run-command.h index d08414a92e..1e3cf0999f 100644 --- a/run-command.h +++ b/run-command.h @@ -443,6 +443,20 @@ typedef int (*start_failure_fn)(struct strbuf *out, void *pp_cb, void *pp_task_cb); +/** + * This callback is called repeatedly on every child process who requests + * start_command() to create a pipe by setting child_process.in < 0. + * + * pp_cb is the callback cookie as passed into run_processes_parallel, and + * pp_task_cb is the callback cookie as passed into get_next_task_fn. + * The contents of 'send' will be read into the pipe and passed to the pipe. + * + * Return nonzero to close the pipe. + */ +typedef int (*feed_pipe_fn)(struct strbuf *pipe, + void *pp_cb, + void *pp_task_cb); + /** * This callback is called on every child process that finished processing. * @@ -477,10 +491,11 @@ typedef int (*task_finished_fn)(int result, int run_processes_parallel(int n, get_next_task_fn, start_failure_fn, + feed_pipe_fn, task_finished_fn, void *pp_cb); int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn, - task_finished_fn, void *pp_cb, + feed_pipe_fn, task_finished_fn, void *pp_cb, const char *tr2_category, const char *tr2_label); #endif diff --git a/submodule.c b/submodule.c index 9767ba9893..dc4a6a60f4 100644 --- a/submodule.c +++ b/submodule.c @@ -1644,6 +1644,7 @@ int fetch_populated_submodules(struct repository *r, run_processes_parallel_tr2(max_parallel_jobs, get_next_submodule, fetch_start_failure, + NULL, fetch_finish, &spf, "submodule", "parallel/fetch"); diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c index 7ae03dc712..9348184d30 100644 --- a/t/helper/test-run-command.c +++ b/t/helper/test-run-command.c @@ -32,8 +32,13 @@ static int parallel_next(struct child_process *cp, return 0; strvec_pushv(&cp->args, d->argv); + cp->in = d->in; + cp->no_stdin = d->no_stdin; strbuf_addstr(err, "preloaded output of a child\n"); number_callbacks++; + + *task_cb = xmalloc(sizeof(int)); + *(int*)(*task_cb) = 2; return 1; } @@ -55,6 +60,17 @@ static int task_finished(int result, return 1; } +static int test_stdin(struct strbuf *pipe, void *cb, void *task_cb) +{ + int *lines_remaining = task_cb; + + if (*lines_remaining) + strbuf_addf(pipe, "sample stdin %d\n", --(*lines_remaining)); + + return !(*lines_remaining); +} + + struct testsuite { struct string_list tests, failed; int next; @@ -185,7 +201,7 @@ static int testsuite(int argc, const char **argv) suite.tests.nr, max_jobs); ret = run_processes_parallel(max_jobs, next_test, test_failed, - test_finished, &suite); + test_stdin, test_finished, &suite); if (suite.failed.nr > 0) { ret = 1; @@ -413,15 +429,22 @@ int cmd__run_command(int argc, const char **argv) if (!strcmp(argv[1], "run-command-parallel")) exit(run_processes_parallel(jobs, parallel_next, - NULL, NULL, &proc)); + NULL, NULL, NULL, &proc)); if (!strcmp(argv[1], "run-command-abort")) exit(run_processes_parallel(jobs, parallel_next, - NULL, task_finished, &proc)); + NULL, NULL, task_finished, &proc)); if (!strcmp(argv[1], "run-command-no-jobs")) exit(run_processes_parallel(jobs, no_job, - NULL, task_finished, &proc)); + NULL, NULL, task_finished, &proc)); + + if (!strcmp(argv[1], "run-command-stdin")) { + proc.in = -1; + proc.no_stdin = 0; + exit (run_processes_parallel(jobs, parallel_next, NULL, + test_stdin, NULL, &proc)); + } fprintf(stderr, "check usage\n"); return 1; diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 7d599675e3..87759482ad 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -143,6 +143,36 @@ test_expect_success 'run_command runs in parallel with more tasks than jobs avai test_cmp expect actual ' +cat >expect <<-EOF +preloaded output of a child +listening for stdin: +sample stdin 1 +sample stdin 0 +preloaded output of a child +listening for stdin: +sample stdin 1 +sample stdin 0 +preloaded output of a child +listening for stdin: +sample stdin 1 +sample stdin 0 +preloaded output of a child +listening for stdin: +sample stdin 1 +sample stdin 0 +EOF + +test_expect_success 'run_command listens to stdin' ' + write_script stdin-script <<-\EOF && + echo "listening for stdin:" + while read line; do + echo "$line" + done + EOF + test-tool run-command run-command-stdin 2 ./stdin-script 2>actual && + test_cmp expect actual +' + cat >expect <<-EOF preloaded output of a child asking for a quick stop From patchwork Thu Mar 11 02:10:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130101 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 25D21C4360C for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F155864FDF for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229928AbhCKCLl (ORCPT ); Wed, 10 Mar 2021 21:11:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42252 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229792AbhCKCLS (ORCPT ); Wed, 10 Mar 2021 21:11:18 -0500 Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 710AEC061574 for ; Wed, 10 Mar 2021 18:11:18 -0800 (PST) Received: by mail-qt1-x84a.google.com with SMTP id t19so14435529qta.2 for ; Wed, 10 Mar 2021 18:11:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=zmKnU5JQTIiX1yJYC8uTcq37/BNrhHT+TN8H00corv0=; b=T7asWaS/90Ji+ZWJ+e3ygMS4V9FhlrxdvEkT/p/IzlBKe7YxAfLbasnYB/YAuZsgLV qDPzCvqqPuYOBIMlGhlEywlqH+YssZkO3HRLOlYBVY4/s04ubBg+r7c8PGvf+Sx2cMG4 dmwz4JCii7cXb3qbPP9LoJTBih+514mR0gUUfwWx6IeHj8V+/Qq/CNuw4CzoTy3CX9nr W9RqDDNAt8HNo3X0Au7yy2PXVg0fT0lhMNX1QpfCK//7QbAIws2rxuw/mwtp0osW1rs/ 3eq9IA+T/dRP0DOtDtg7jSxO9+pXOzNaAKJ5ccpcUMojY6jyW5IVu3XxRQGoeUzedVrb p5lw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=zmKnU5JQTIiX1yJYC8uTcq37/BNrhHT+TN8H00corv0=; b=I6EUlNyNrBB5qPjZ3wfc20NJ4wBvVgs36oKfYwT5dn+O06x5+B/B31HDUjaNBXrYCz OlH0uUrJl2n/tRrHMBJCFDoyoad/smjZxe5WZPG9zHU2fh5Oey3avxcpfX7565sbwPA/ GFQvkVF5hIXcRScbVeI3+1e70eoDeaMRIT3l7dPVIRSxAxZODsv/toTQRoiK6noaNIfr njoVFT303neuvzXOywBevdXsTKndURrxLqo7wxLIBggISy1//vzHCkZ3B9NuuYaKZyRx 4vhPPSm+APG/35wepT9LePkUM0JG4cBICoYjPm5l9GGYCN57SU+C3QrZoA4tTvhPbjzr DRwA== X-Gm-Message-State: AOAM532599YIwnqGkuNcVeVeLmiQNnJJ8W7gG0hMpt7XRQCulyNATFTl Qq4VjEF6d03Ad/LPANQ2ueJs0NgVe5HfTB8TbaI3d+ds5V5xOSCuY1F7hm+kpfytwllVtMQO5ZC qTM+3g9LCYPR2nFy2Ywsu/uBUdv4eKZk4LWqwhHLkPQNiPIESBuuJoSSIOoa0xEoUp5WlaSuWkg == X-Google-Smtp-Source: ABdhPJxKcfIz75YKHhJT4x/2QQfnabA9x7xSkt1V6bzyH7bypwIeiT4G4EiVtyKm3hM08qwVL0pgYzx+vi6CtaRpp0I= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:c60b:: with SMTP id v11mr5799743qvi.44.1615428677602; Wed, 10 Mar 2021 18:11:17 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:15 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-16-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 15/37] hook: provide stdin by string_list or callback From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org In cases where a hook requires only a small amount of information via stdin, it should be simple for users to provide a string_list alone. But in more complicated cases where the stdin is too large to hold in memory, let's provide a callback the users can populate line after line with instead. Signed-off-by: Emily Shaffer --- hook.c | 35 ++++++++++++++++++++++++++++++++++- hook.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/hook.c b/hook.c index 9088b520f3..a509d2d80e 100644 --- a/hook.c +++ b/hook.c @@ -9,6 +9,7 @@ void free_hook(struct hook *ptr) { if (ptr) { strbuf_release(&ptr->command); + free(ptr->feed_pipe_cb_data); free(ptr); } } @@ -39,6 +40,7 @@ static void append_or_move_hook(struct list_head *head, const char *command) strbuf_init(&to_add->command, 0); strbuf_addstr(&to_add->command, command); to_add->from_hookdir = 0; + to_add->feed_pipe_cb_data = NULL; } /* re-set the scope so we show where an override was specified */ @@ -252,6 +254,8 @@ void run_hooks_opt_init_sync(struct run_hooks_opt *o) o->run_hookdir = configured_hookdir_opt(); o->jobs = 1; o->dir = NULL; + o->feed_pipe = NULL; + o->feed_pipe_ctx = NULL; } void run_hooks_opt_init_async(struct run_hooks_opt *o) @@ -285,6 +289,28 @@ void run_hooks_opt_clear(struct run_hooks_opt *o) strvec_clear(&o->args); } +int pipe_from_string_list(struct strbuf *pipe, void *pp_cb, void *pp_task_cb) +{ + int *item_idx; + struct hook *ctx = pp_task_cb; + struct string_list *to_pipe = ((struct hook_cb_data*)pp_cb)->options->feed_pipe_ctx; + + /* Bootstrap the state manager if necessary. */ + if (!ctx->feed_pipe_cb_data) { + ctx->feed_pipe_cb_data = xmalloc(sizeof(unsigned int)); + *(int*)ctx->feed_pipe_cb_data = 0; + } + + item_idx = ctx->feed_pipe_cb_data; + + if (*item_idx < to_pipe->nr) { + strbuf_addf(pipe, "%s\n", to_pipe->items[*item_idx].string); + (*item_idx)++; + return 0; + } + return 1; +} + static int pick_next_hook(struct child_process *cp, struct strbuf *out, void *pp_cb, @@ -300,6 +326,10 @@ static int pick_next_hook(struct child_process *cp, if (hook_cb->options->path_to_stdin) { cp->no_stdin = 0; cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY); + } else if (hook_cb->options->feed_pipe) { + /* ask for start_command() to make a pipe for us */ + cp->in = -1; + cp->no_stdin = 0; } else { cp->no_stdin = 1; } @@ -379,6 +409,9 @@ int run_hooks(const char *hookname, struct run_hooks_opt *options) if (!options) BUG("a struct run_hooks_opt must be provided to run_hooks"); + if (options->path_to_stdin && options->feed_pipe) + BUG("choose only one method to populate stdin"); + strbuf_addstr(&hookname_str, hookname); to_run = hook_list(&hookname_str); @@ -400,7 +433,7 @@ int run_hooks(const char *hookname, struct run_hooks_opt *options) run_processes_parallel_tr2(options->jobs, pick_next_hook, notify_start_failure, - NULL, + options->feed_pipe, notify_hook_finished, &cb_data, "hook", diff --git a/hook.h b/hook.h index fcd8e99e39..ecf0228a46 100644 --- a/hook.h +++ b/hook.h @@ -2,6 +2,7 @@ #include "list.h" #include "strbuf.h" #include "strvec.h" +#include "run-command.h" struct hook { struct list_head list; @@ -13,6 +14,12 @@ struct hook { /* The literal command to run. */ struct strbuf command; unsigned from_hookdir : 1; + + /* + * Use this to keep state for your feed_pipe_fn if you are using + * run_hooks_opt.feed_pipe. Otherwise, do not touch it. + */ + void *feed_pipe_cb_data; }; /* @@ -58,14 +65,35 @@ struct run_hooks_opt /* Path to file which should be piped to stdin for each hook */ const char *path_to_stdin; + /* + * Callback and state pointer to ask for more content to pipe to stdin. + * Will be called repeatedly, for each hook. See + * hook.c:pipe_from_stdin() for an example. Keep per-hook state in + * hook.feed_pipe_cb_data (per process). Keep initialization context in + * feed_pipe_ctx (shared by all processes). + * + * See 'pipe_from_string_list()' for info about how to specify a + * string_list as the stdin input instead of writing your own handler. + */ + feed_pipe_fn feed_pipe; + void *feed_pipe_ctx; /* Number of threads to parallelize across */ int jobs; /* Path to initial working directory for subprocess */ const char *dir; + }; +/* + * To specify a 'struct string_list', set 'run_hooks_opt.feed_pipe_ctx' to the + * string_list and set 'run_hooks_opt.feed_pipe' to 'pipe_from_string_list()'. + * This will pipe each string in the list to stdin, separated by newlines. (Do + * not inject your own newlines.) + */ +int pipe_from_string_list(struct strbuf *pipe, void *pp_cb, void *pp_task_cb); + /* * Callback provided to feed_pipe_fn and consume_sideband_fn. */ From patchwork Thu Mar 11 02:10:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130115 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 90258C43142 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 78B5164FD7 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230046AbhCKCLo (ORCPT ); Wed, 10 Mar 2021 21:11:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229805AbhCKCL2 (ORCPT ); Wed, 10 Mar 2021 21:11:28 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 33C0BC061574 for ; Wed, 10 Mar 2021 18:11:20 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id l3so23888510ybf.17 for ; Wed, 10 Mar 2021 18:11:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=OvdtPyvUSX9T0E7rDIn7PYUQpwLC5XXdi4eoLFgyEY8=; b=rIgYGVopfckLYeGV+AnZVtqqsbm5ZcoRPpumT+W48eOVgtKgoKNHfcpdrQd8xicUlP 2o9Jp0HcjIEyFyOqm4W+zxcR2IvNYCSrSURv5qjcuFUtvGwmwhatS2XtfpeW5DvUeWUl acH6dZE4Crepx2v7QogA2JqlOfRmTW88Up6McW23CIfB//hXCowR5AGnUqaE3rpTZZ3i 3xjHMrP5z/hAT933zB4DDSos9+YdLGA5b752LtCpQCzJpy25xGxYk3kIvg6+lBQ86kNk icar5iACylxa2D9Ojj0v5bBViXv4r1ksvu81CyYjAZHmEVQP8AK5C4e3LHMeBIKaouwq Lh9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=OvdtPyvUSX9T0E7rDIn7PYUQpwLC5XXdi4eoLFgyEY8=; b=bNTrLcf4BT0nkngKWqIAQnr0iEdDL3Ohiip7CLmluKwN1bexPEgTwI8oI+jorlLQkK sKBvcKBzlKRNxY6bf2A/INPnf9SxwY7Oi8P7Vt6mmWeGnTsl74gxzcaDPEzZBJqiffpE CCK+8b0tFe+IUtX/Xc61YkTL4zT+I+ddEProJ0a9mkHzLMbIn6/gYaXWtEO7EMhCMCot 5U2wjQBphfTQUtgoNGBHJE0UQfDwiIQSjCxTRfVfnrA7a/no9h/mIaGuOLr5/fd11ZIM ecICjqkBJD8BadQYhXJ6Xf9/fA8ZfG2C33Aed9M6pgmUFwNWdhC3hi1GiumgZ2Lh4rKD 1BDA== X-Gm-Message-State: AOAM532NJ/JILtAvHKi2TWS/042bh7wjqNgnhzMe63lKlQBRnUO/LnK+ c4DXZ8d+jujS1r4kNk3DdLcUB4nSDjUvbXn078JKHbeoeFjE093ivxi6KIFQuE2lXqginOgg1q4 qvpwP1+YIi3hpqLQFOUPxJJwzDUEYGug7Sjboh1rrLZEkCKx0YFxyMIS7gWevVaC6WDmJz1foyQ == X-Google-Smtp-Source: ABdhPJwM53JJOHX86XNYHhGW3ZiBlRr2m+puYjJTGOEv52POGa2RX1EK6FNygZQ9LzM9t6IgihALpnVFT3ULFc3YEUk= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:dd43:: with SMTP id u64mr8896750ybg.96.1615428679415; Wed, 10 Mar 2021 18:11:19 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:16 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-17-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 16/37] run-command: allow capturing of collated output From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Some callers, for example server-side hooks which wish to relay hook output to clients across a transport, want to capture what would normally print to stderr and do something else with it. Allow that via a callback. By calling the callback regardless of whether there's output available, we allow clients to send e.g. a keepalive if necessary. Because we expose a strbuf, not a fd or FILE*, there's no need to create a temporary pipe or similar - we can just skip the print to stderr and instead hand it to the caller. Signed-off-by: Emily Shaffer --- builtin/fetch.c | 2 +- builtin/submodule--helper.c | 2 +- hook.c | 1 + run-command.c | 33 +++++++++++++++++++++++++-------- run-command.h | 18 +++++++++++++++++- submodule.c | 2 +- t/helper/test-run-command.c | 25 ++++++++++++++++++++----- t/t0061-run-command.sh | 7 +++++++ 8 files changed, 73 insertions(+), 17 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index d8e798dc69..b6d45f8359 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1757,7 +1757,7 @@ static int fetch_multiple(struct string_list *list, int max_children) result = run_processes_parallel_tr2(max_children, &fetch_next_remote, &fetch_failed_to_start, - NULL, + NULL, NULL, &fetch_finished, &state, "fetch", "parallel/fetch"); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 14f6e4ee8c..136e09a016 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2294,7 +2294,7 @@ static int update_submodules(struct submodule_update_clone *suc) int i; run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task, - update_clone_start_failure, NULL, + update_clone_start_failure, NULL, NULL, update_clone_task_finished, suc, "submodule", "parallel/update"); diff --git a/hook.c b/hook.c index a509d2d80e..e16b082cbd 100644 --- a/hook.c +++ b/hook.c @@ -434,6 +434,7 @@ int run_hooks(const char *hookname, struct run_hooks_opt *options) pick_next_hook, notify_start_failure, options->feed_pipe, + NULL, notify_hook_finished, &cb_data, "hook", diff --git a/run-command.c b/run-command.c index e7eeb6c49b..36a4edbacf 100644 --- a/run-command.c +++ b/run-command.c @@ -1559,6 +1559,7 @@ struct parallel_processes { get_next_task_fn get_next_task; start_failure_fn start_failure; feed_pipe_fn feed_pipe; + consume_sideband_fn consume_sideband; task_finished_fn task_finished; struct { @@ -1624,6 +1625,7 @@ static void pp_init(struct parallel_processes *pp, get_next_task_fn get_next_task, start_failure_fn start_failure, feed_pipe_fn feed_pipe, + consume_sideband_fn consume_sideband, task_finished_fn task_finished, void *data) { @@ -1644,6 +1646,7 @@ static void pp_init(struct parallel_processes *pp, pp->start_failure = start_failure ? start_failure : default_start_failure; pp->feed_pipe = feed_pipe ? feed_pipe : default_feed_pipe; pp->task_finished = task_finished ? task_finished : default_task_finished; + pp->consume_sideband = consume_sideband; pp->nr_processes = 0; pp->output_owner = 0; @@ -1680,7 +1683,10 @@ static void pp_cleanup(struct parallel_processes *pp) * When get_next_task added messages to the buffer in its last * iteration, the buffered output is non empty. */ - strbuf_write(&pp->buffered_output, stderr); + if (pp->consume_sideband) + pp->consume_sideband(&pp->buffered_output, pp->data); + else + strbuf_write(&pp->buffered_output, stderr); strbuf_release(&pp->buffered_output); sigchain_pop_common(); @@ -1801,9 +1807,13 @@ static void pp_buffer_stderr(struct parallel_processes *pp, int output_timeout) static void pp_output(struct parallel_processes *pp) { int i = pp->output_owner; + if (pp->children[i].state == GIT_CP_WORKING && pp->children[i].err.len) { - strbuf_write(&pp->children[i].err, stderr); + if (pp->consume_sideband) + pp->consume_sideband(&pp->children[i].err, pp->data); + else + strbuf_write(&pp->children[i].err, stderr); strbuf_reset(&pp->children[i].err); } } @@ -1842,11 +1852,15 @@ static int pp_collect_finished(struct parallel_processes *pp) strbuf_addbuf(&pp->buffered_output, &pp->children[i].err); strbuf_reset(&pp->children[i].err); } else { - strbuf_write(&pp->children[i].err, stderr); + /* Output errors, then all other finished child processes */ + if (pp->consume_sideband) { + pp->consume_sideband(&pp->children[i].err, pp->data); + pp->consume_sideband(&pp->buffered_output, pp->data); + } else { + strbuf_write(&pp->children[i].err, stderr); + strbuf_write(&pp->buffered_output, stderr); + } strbuf_reset(&pp->children[i].err); - - /* Output all other finished child processes */ - strbuf_write(&pp->buffered_output, stderr); strbuf_reset(&pp->buffered_output); /* @@ -1870,6 +1884,7 @@ int run_processes_parallel(int n, get_next_task_fn get_next_task, start_failure_fn start_failure, feed_pipe_fn feed_pipe, + consume_sideband_fn consume_sideband, task_finished_fn task_finished, void *pp_cb) { @@ -1880,7 +1895,7 @@ int run_processes_parallel(int n, sigchain_push(SIGPIPE, SIG_IGN); - pp_init(&pp, n, get_next_task, start_failure, feed_pipe, task_finished, pp_cb); + pp_init(&pp, n, get_next_task, start_failure, feed_pipe, consume_sideband, task_finished, pp_cb); while (1) { for (i = 0; i < spawn_cap && !pp.shutdown && @@ -1918,6 +1933,7 @@ int run_processes_parallel(int n, int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, start_failure_fn start_failure, feed_pipe_fn feed_pipe, + consume_sideband_fn consume_sideband, task_finished_fn task_finished, void *pp_cb, const char *tr2_category, const char *tr2_label) { @@ -1927,7 +1943,8 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, ((n < 1) ? online_cpus() : n)); result = run_processes_parallel(n, get_next_task, start_failure, - feed_pipe, task_finished, pp_cb); + feed_pipe, consume_sideband, + task_finished, pp_cb); trace2_region_leave(tr2_category, tr2_label, NULL); diff --git a/run-command.h b/run-command.h index 1e3cf0999f..ebc4a95a94 100644 --- a/run-command.h +++ b/run-command.h @@ -457,6 +457,20 @@ typedef int (*feed_pipe_fn)(struct strbuf *pipe, void *pp_cb, void *pp_task_cb); +/** + * If this callback is provided, instead of collating process output to stderr, + * they will be collated into a new pipe. consume_sideband_fn will be called + * repeatedly. When output is available on that pipe, it will be contained in + * 'output'. But it will be called with an empty 'output' too, to allow for + * keepalives or similar operations if necessary. + * + * pp_cb is the callback cookie as passed into run_processes_parallel. + * + * Since this callback is provided with the collated output, no task cookie is + * provided. + */ +typedef void (*consume_sideband_fn)(struct strbuf *output, void *pp_cb); + /** * This callback is called on every child process that finished processing. * @@ -492,10 +506,12 @@ int run_processes_parallel(int n, get_next_task_fn, start_failure_fn, feed_pipe_fn, + consume_sideband_fn, task_finished_fn, void *pp_cb); int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn, - feed_pipe_fn, task_finished_fn, void *pp_cb, + feed_pipe_fn, consume_sideband_fn, + task_finished_fn, void *pp_cb, const char *tr2_category, const char *tr2_label); #endif diff --git a/submodule.c b/submodule.c index dc4a6a60f4..4926642451 100644 --- a/submodule.c +++ b/submodule.c @@ -1644,7 +1644,7 @@ int fetch_populated_submodules(struct repository *r, run_processes_parallel_tr2(max_parallel_jobs, get_next_submodule, fetch_start_failure, - NULL, + NULL, NULL, fetch_finish, &spf, "submodule", "parallel/fetch"); diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c index 9348184d30..d53db6d11c 100644 --- a/t/helper/test-run-command.c +++ b/t/helper/test-run-command.c @@ -51,6 +51,16 @@ static int no_job(struct child_process *cp, return 0; } +static void test_consume_sideband(struct strbuf *output, void *cb) +{ + FILE *sideband; + + sideband = fopen("./sideband", "a"); + + strbuf_write(output, sideband); + fclose(sideband); +} + static int task_finished(int result, struct strbuf *err, void *pp_cb, @@ -201,7 +211,7 @@ static int testsuite(int argc, const char **argv) suite.tests.nr, max_jobs); ret = run_processes_parallel(max_jobs, next_test, test_failed, - test_stdin, test_finished, &suite); + test_stdin, NULL, test_finished, &suite); if (suite.failed.nr > 0) { ret = 1; @@ -429,23 +439,28 @@ int cmd__run_command(int argc, const char **argv) if (!strcmp(argv[1], "run-command-parallel")) exit(run_processes_parallel(jobs, parallel_next, - NULL, NULL, NULL, &proc)); + NULL, NULL, NULL, NULL, &proc)); if (!strcmp(argv[1], "run-command-abort")) exit(run_processes_parallel(jobs, parallel_next, - NULL, NULL, task_finished, &proc)); + NULL, NULL, NULL, task_finished, &proc)); if (!strcmp(argv[1], "run-command-no-jobs")) exit(run_processes_parallel(jobs, no_job, - NULL, NULL, task_finished, &proc)); + NULL, NULL, NULL, task_finished, &proc)); if (!strcmp(argv[1], "run-command-stdin")) { proc.in = -1; proc.no_stdin = 0; exit (run_processes_parallel(jobs, parallel_next, NULL, - test_stdin, NULL, &proc)); + test_stdin, NULL, NULL, &proc)); } + if (!strcmp(argv[1], "run-command-sideband")) + exit(run_processes_parallel(jobs, parallel_next, NULL, NULL, + test_consume_sideband, NULL, + &proc)); + fprintf(stderr, "check usage\n"); return 1; } diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 87759482ad..e99f6c7f44 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -143,6 +143,13 @@ test_expect_success 'run_command runs in parallel with more tasks than jobs avai test_cmp expect actual ' +test_expect_success 'run_command can divert output' ' + test_when_finished rm sideband && + test-tool run-command run-command-sideband 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual && + test_must_be_empty actual && + test_cmp expect sideband +' + cat >expect <<-EOF preloaded output of a child listening for stdin: From patchwork Thu Mar 11 02:10:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130105 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 03171C433E0 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D546164FD6 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229939AbhCKCLm (ORCPT ); Wed, 10 Mar 2021 21:11:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229593AbhCKCLW (ORCPT ); Wed, 10 Mar 2021 21:11:22 -0500 Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1C2E2C061760 for ; Wed, 10 Mar 2021 18:11:22 -0800 (PST) Received: by mail-qt1-x849.google.com with SMTP id e6so14447210qte.0 for ; Wed, 10 Mar 2021 18:11:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=6D12hEJKPM8vudlev8zqDXAnnbiOMjwhGlztJODSEmY=; b=cn9LDCHT9SlCRc34tzvqRa3Z6ZrD3Fstwqm4dv6RJ+pBerrCVQqpPv9NB8W6BQCQ1s PDch0fBOBpJXe9L5sDaIX9qjt69l/4J/6IyJDWVGehW2g2oef6ibcvNf5xP0rMLSCiO+ cjfXFj4aTSCHzVR/7X8BUhvVy1Vt7uz+emFE5+Agdhpb2j0pZWF1hZ7xstBDJLs7AlFA I/b7pP/+NkRhPpIRWWNCgy+zHeX7wGOG2QtkPETFm1JWnOJC37W/n1skcGnf8qD0eN16 U1FNF37bkh9pCshsYtwqRADYy7uZQ/SqQEUdYHnT3SW67O5X/pUzLiRWeoIbSsPVUO7w nGzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=6D12hEJKPM8vudlev8zqDXAnnbiOMjwhGlztJODSEmY=; b=Lp7dt7G7kcD829KmAkpSEN5bPVHJqChXD85ss0VYLxv9k2Z8tMKl/dKrm5mWNUSIYx A5kFfyq0h+Yp6YLyTwsl4ZdriqJE0xGbXjFruQAlutOO9Y1WB2/KUyixwMujk+W4Okxe FYnTdyYl6rfQHi05p7+rvPuwxfbt/GvXygQ4446UOdJiqOAiPzP6brMtIVZoOS9PGYxi JuaTsUUEqJeRQnPoK5Xx4KeUBUQ3SaVXawBYYCWpORaw4V8ssgMvgOlnhN3uEuXkr087 nidwE1V0ODZh1lHw1jsH87/VN5e6H6yv131oIXczVElgDfExlw99fGo1JWAH8ydmgDMI 54cA== X-Gm-Message-State: AOAM531CVj8ZOn7Pim1y3YOKFEBGN+ZnoMgMP24uXc7zpseMFHGK5Qhq WFC5tRSdQZcGPaqr732PLUJc5shan/u/2CLlBqEkwqdtCzVPeurjKTzgEMswLlZ6/HwvfJJgAUT 6gSK+VoUUHIU+sgstHvH7yS42difntT99gdM5PlminLJ7N0UjYNs2S5QcDBKEK4/hcFvabL7SMw == X-Google-Smtp-Source: ABdhPJwXcf5Y7ZtXYWRwk8sYNAD6tDBqmdU2nIo4y9YH7HoN1LPaIdEtuYZZMPKNo05FDVw87D1iskpxtLtwafVbRgs= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:e1c7:: with SMTP id v7mr5764706qvl.30.1615428681322; Wed, 10 Mar 2021 18:11:21 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:17 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-18-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 17/37] hooks: allow callers to capture output From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Some server-side hooks will require capturing output to send over sideband instead of printing directly to stderr. Expose that capability. Signed-off-by: Emily Shaffer --- Notes: You can see this in practice in the conversions for some of the push hooks, like 'receive-pack'. hook.c | 3 ++- hook.h | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hook.c b/hook.c index e16b082cbd..2322720ffe 100644 --- a/hook.c +++ b/hook.c @@ -256,6 +256,7 @@ void run_hooks_opt_init_sync(struct run_hooks_opt *o) o->dir = NULL; o->feed_pipe = NULL; o->feed_pipe_ctx = NULL; + o->consume_sideband = NULL; } void run_hooks_opt_init_async(struct run_hooks_opt *o) @@ -434,7 +435,7 @@ int run_hooks(const char *hookname, struct run_hooks_opt *options) pick_next_hook, notify_start_failure, options->feed_pipe, - NULL, + options->consume_sideband, notify_hook_finished, &cb_data, "hook", diff --git a/hook.h b/hook.h index ecf0228a46..4ff9999b04 100644 --- a/hook.h +++ b/hook.h @@ -78,6 +78,14 @@ struct run_hooks_opt feed_pipe_fn feed_pipe; void *feed_pipe_ctx; + /* + * Populate this to capture output and prevent it from being printed to + * stderr. This will be passed directly through to + * run_command:run_parallel_processes(). See t/helper/test-run-command.c + * for an example. + */ + consume_sideband_fn consume_sideband; + /* Number of threads to parallelize across */ int jobs; From patchwork Thu Mar 11 02:10:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130103 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DF7F5C43333 for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BBC1A64FDF for ; Thu, 11 Mar 2021 02:12:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229955AbhCKCLm (ORCPT ); Wed, 10 Mar 2021 21:11:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42278 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229803AbhCKCLY (ORCPT ); Wed, 10 Mar 2021 21:11:24 -0500 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0C064C061760 for ; Wed, 10 Mar 2021 18:11:24 -0800 (PST) Received: by mail-qk1-x749.google.com with SMTP id b78so14305071qkg.13 for ; Wed, 10 Mar 2021 18:11:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=13jZIdHsfgFOQFkD9nBJQuRRtTWV1F7LsXVAkrLRku8=; b=hczRzTNn8UJB5NrVRuTuPjjLc4+MDFX5M3bXYXR2+mzWcnkjWk0FL/LWOZobawhshH vJEAusJN1nG7jpIH+E1cdaobK0bkYAOn+5OFtM0XEh399ARKsAmXNxokwnVDnADMHXZ0 FT1you3fWZeG0OWBVHd8dsCd5vVwSWaupkSijgoyoJ8StDe6VF7I3KM3CzQ3dNEhNgo6 MmGw+Bib8WjW/X/O953k4Kff3ruaig1duduvL6DamsGnJn8sP/B0ZnYW5zvl1VoScL4r tw/OhHMWxppK/b1RMNVKaU6L6bF7swiI0yrvmQqQlShNFeoEvtEOHNEuBsofJ5Q7qrXW TvwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=13jZIdHsfgFOQFkD9nBJQuRRtTWV1F7LsXVAkrLRku8=; b=b46ykQTbXdAjtJzH21qZGJGlR6l2Sfh3JPlDmI9QSla+kdBGHzC/ZI96wxTHMPmk5H WF5xXclwN1PLET/DvgFqsoNKnWcBnsQjqbDq+SCBgDAXxx5ol+G9Sl1QyagclsuDjADC IdGiN5xjyUr0e0axjD4sEqBg32Zi/pfJweTXgaKDrxy78qIQa3yXBZyCMZr1xElwoWVF lYDz3zto06mi6TZ3uzSji116UdSObMR66Cr5Hr3rMm3B1aYkenlr+YNVUvxLOMto0nXq IDgYEtT3t01hEubMiGYoUfCD6rIN6xseS6z1ShzO1J1b3tytKt97px0fgKaOEr+AQaTI Hffg== X-Gm-Message-State: AOAM532XF621Rvt9QvydFo18Grf07SLjsZHbRblVLF5/031b5cQ1eqf4 m7QiZ/VaFj0X2QVBgiSjUPtT4y7sSHWVrehYjojmlo/4qa9Fwsr3YoTMjAbQXHWp3OZWiAb/vTz pVL/Yww4fi0s4WWHfH+Dkty7G5FHLqXcCO56gazq7tFKTR1FSS/g8haHMpnPo9ElXDAoxzv1zOw == X-Google-Smtp-Source: ABdhPJz24/P9nivzUYyBw056sAqHj87H04Q4ngBxnpJv4xdE7+xk/pji2f2B5W5xX0rcEyCecC0riVrqbiS2HVJSujI= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:1146:: with SMTP id b6mr5683265qvt.62.1615428683189; Wed, 10 Mar 2021 18:11:23 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:18 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-19-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 18/37] commit: use config-based hooks From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org As part of the adoption of config-based hooks, teach run_commit_hook() to call hook.h instead of run-command.h. This covers 'pre-commit', 'commit-msg', and 'prepare-commit-msg'. Additionally, ask the hook library - not run-command - whether any hooks will be run, as it's possible hooks may exist in the config but not the hookdir. Because all but 'post-commit' hooks are expected to make some state change, force all but 'post-commit' hook to run in series. 'post-commit' "is meant primarily for notification, and cannot affect the outcome of `git commit`," so it is fine to run in parallel. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 13 +++++++++++ builtin/commit.c | 11 +++++----- builtin/merge.c | 9 ++++---- commit.c | 22 ++++++++++++++----- commit.h | 3 ++- sequencer.c | 7 +++--- ...3-pre-commit-and-pre-merge-commit-hooks.sh | 17 ++++++++++++-- 7 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 1f3b57d04d..984fb998b2 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -103,6 +103,8 @@ The default 'pre-commit' hook, when enabled--and with the `hooks.allownonascii` config option unset or set to false--prevents the use of non-ASCII filenames. +Hooks executed during 'pre-commit' will not be parallelized. + pre-merge-commit ~~~~~~~~~~~~~~~~ @@ -125,6 +127,8 @@ need to be resolved and the result committed separately (see linkgit:git-merge[1]). At that point, this hook will not be executed, but the 'pre-commit' hook will, if it is enabled. +Hooks executed during 'pre-merge-commit' will not be parallelized. + prepare-commit-msg ~~~~~~~~~~~~~~~~~~ @@ -150,6 +154,9 @@ be used as replacement for pre-commit hook. The sample `prepare-commit-msg` hook that comes with Git removes the help message found in the commented portion of the commit template. +Hooks executed during 'prepare-commit-msg' will not be parallelized, because +hooks are expected to edit the file containing the commit log message. + commit-msg ~~~~~~~~~~ @@ -166,6 +173,9 @@ file. The default 'commit-msg' hook, when enabled, detects duplicate `Signed-off-by` trailers, and aborts the commit if one is found. +Hooks executed during 'commit-msg' will not be parallelized, because hooks are +expected to edit the file containing the proposed commit log message. + post-commit ~~~~~~~~~~~ @@ -175,6 +185,9 @@ invoked after a commit is made. This hook is meant primarily for notification, and cannot affect the outcome of `git commit`. +Hooks executed during 'post-commit' will run in parallel, unless hook.jobs is +configured to 1. + pre-rebase ~~~~~~~~~~ diff --git a/builtin/commit.c b/builtin/commit.c index 739110c5a7..39f387e8f7 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -36,6 +36,7 @@ #include "help.h" #include "commit-reach.h" #include "commit-graph.h" +#include "hook.h" static const char * const builtin_commit_usage[] = { N_("git commit [] [--] ..."), @@ -699,7 +700,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, /* This checks and barfs if author is badly specified */ determine_author_info(author_ident); - if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL)) + if (!no_verify && run_commit_hook(use_editor, 0, index_file, "pre-commit", NULL)) return 0; if (squash_message) { @@ -983,7 +984,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, return 0; } - if (!no_verify && find_hook("pre-commit")) { + if (!no_verify && hook_exists("pre-commit", HOOKDIR_USE_CONFIG)) { /* * Re-read the index as pre-commit hook could have updated it, * and write it out as a tree. We must do this before we invoke @@ -998,7 +999,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, return 0; } - if (run_commit_hook(use_editor, index_file, "prepare-commit-msg", + if (run_commit_hook(use_editor, 0, index_file, "prepare-commit-msg", git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL)) return 0; @@ -1015,7 +1016,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, } if (!no_verify && - run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) { + run_commit_hook(use_editor, 0, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) { return 0; } @@ -1701,7 +1702,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) repo_rerere(the_repository, 0); run_auto_maintenance(quiet); - run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); + run_commit_hook(use_editor, 1, get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { commit_post_rewrite(the_repository, current_head, &oid); } diff --git a/builtin/merge.c b/builtin/merge.c index eb00b273e6..33df744ab0 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -43,6 +43,7 @@ #include "commit-reach.h" #include "wt-status.h" #include "commit-graph.h" +#include "hook.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -837,14 +838,14 @@ static void prepare_to_commit(struct commit_list *remoteheads) struct strbuf msg = STRBUF_INIT; const char *index_file = get_index_file(); - if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL)) + if (!no_verify && run_commit_hook(0 < option_edit, 0, index_file, "pre-merge-commit", NULL)) abort_commit(remoteheads, NULL); /* * Re-read the index as pre-merge-commit hook could have updated it, * and write it out as a tree. We must do this before we invoke * the editor and after we invoke run_status above. */ - if (find_hook("pre-merge-commit")) + if (hook_exists("pre-merge-commit", HOOKDIR_USE_CONFIG)) discard_cache(); read_cache_from(index_file); strbuf_addbuf(&msg, &merge_msg); @@ -865,7 +866,7 @@ static void prepare_to_commit(struct commit_list *remoteheads) append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0); write_merge_heads(remoteheads); write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len); - if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", + if (run_commit_hook(0 < option_edit, 0, get_index_file(), "prepare-commit-msg", git_path_merge_msg(the_repository), "merge", NULL)) abort_commit(remoteheads, NULL); if (0 < option_edit) { @@ -873,7 +874,7 @@ static void prepare_to_commit(struct commit_list *remoteheads) abort_commit(remoteheads, NULL); } - if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(), + if (!no_verify && run_commit_hook(0 < option_edit, 0, get_index_file(), "commit-msg", git_path_merge_msg(the_repository), NULL)) abort_commit(remoteheads, NULL); diff --git a/commit.c b/commit.c index 6ccd774841..b72158cb34 100644 --- a/commit.c +++ b/commit.c @@ -21,6 +21,7 @@ #include "commit-reach.h" #include "run-command.h" #include "shallow.h" +#include "hook.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@ -1681,25 +1682,34 @@ size_t ignore_non_trailer(const char *buf, size_t len) return boc ? len - boc : len - cutoff; } -int run_commit_hook(int editor_is_used, const char *index_file, +int run_commit_hook(int editor_is_used, int parallelize, const char *index_file, const char *name, ...) { - struct strvec hook_env = STRVEC_INIT; + struct run_hooks_opt opt; va_list args; + const char *arg; int ret; - strvec_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file); + run_hooks_opt_init_sync(&opt); + + if (parallelize) + opt.jobs = configured_hook_jobs(); + + strvec_pushf(&opt.env, "GIT_INDEX_FILE=%s", index_file); /* * Let the hook know that no editor will be launched. */ if (!editor_is_used) - strvec_push(&hook_env, "GIT_EDITOR=:"); + strvec_push(&opt.env, "GIT_EDITOR=:"); va_start(args, name); - ret = run_hook_ve(hook_env.v, name, args); + while ((arg = va_arg(args, const char *))) + strvec_push(&opt.args, arg); va_end(args); - strvec_clear(&hook_env); + + ret = run_hooks(name, &opt); + run_hooks_opt_clear(&opt); return ret; } diff --git a/commit.h b/commit.h index 49c0f50396..abea90a3f9 100644 --- a/commit.h +++ b/commit.h @@ -360,7 +360,8 @@ int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused) int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused); LAST_ARG_MUST_BE_NULL -int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...); +int run_commit_hook(int editor_is_used, int parallelize, const char *index_file, + const char *name, ...); /* Sign a commit or tag buffer, storing the result in a header. */ int sign_with_header(struct strbuf *buf, const char *keyid); diff --git a/sequencer.c b/sequencer.c index d2332d3e17..e3a951fbeb 100644 --- a/sequencer.c +++ b/sequencer.c @@ -34,6 +34,7 @@ #include "commit-reach.h" #include "rebase-interactive.h" #include "reset.h" +#include "hook.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -1206,7 +1207,7 @@ static int run_prepare_commit_msg_hook(struct repository *r, } else { arg1 = "message"; } - if (run_commit_hook(0, r->index_file, "prepare-commit-msg", name, + if (run_commit_hook(0, 0, r->index_file, "prepare-commit-msg", name, arg1, arg2, NULL)) ret = error(_("'prepare-commit-msg' hook failed")); @@ -1444,7 +1445,7 @@ static int try_to_commit(struct repository *r, } } - if (find_hook("prepare-commit-msg")) { + if (hook_exists("prepare-commit-msg", HOOKDIR_USE_CONFIG)) { res = run_prepare_commit_msg_hook(r, msg, hook_commit); if (res) goto out; @@ -1536,7 +1537,7 @@ static int try_to_commit(struct repository *r, goto out; } - run_commit_hook(0, r->index_file, "post-commit", NULL); + run_commit_hook(0, 1, r->index_file, "post-commit", NULL); if (flags & AMEND_MSG) commit_post_rewrite(r, current_head, oid); diff --git a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh index 606d8d0f08..e9e3713033 100755 --- a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh +++ b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh @@ -8,8 +8,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh HOOKDIR="$(git rev-parse --git-dir)/hooks" -PRECOMMIT="$HOOKDIR/pre-commit" -PREMERGE="$HOOKDIR/pre-merge-commit" +PRECOMMIT="$(pwd)/$HOOKDIR/pre-commit" +PREMERGE="$(pwd)/$HOOKDIR/pre-merge-commit" # Prepare sample scripts that write their $0 to actual_hooks test_expect_success 'sample script setup' ' @@ -106,6 +106,19 @@ test_expect_success 'with succeeding hook' ' test_cmp expected_hooks actual_hooks ' +# NEEDSWORK: when 'git hook add' and 'git hook remove' have been added, use that +# instead +test_expect_success 'with succeeding hook (config-based)' ' + test_when_finished "git config --unset hook.pre-commit.command success.sample" && + test_when_finished "rm -f expected_hooks actual_hooks" && + git config hook.pre-commit.command "$HOOKDIR/success.sample" && + echo "$HOOKDIR/success.sample" >expected_hooks && + echo "more" >>file && + git add file && + git commit -m "more" && + test_cmp expected_hooks actual_hooks +' + test_expect_success 'with succeeding hook (merge)' ' test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" && cp "$HOOKDIR/success.sample" "$PREMERGE" && From patchwork Thu Mar 11 02:10:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130109 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5C70AC43619 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 399B964FD7 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229971AbhCKCLm (ORCPT ); Wed, 10 Mar 2021 21:11:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42288 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229813AbhCKCLb (ORCPT ); Wed, 10 Mar 2021 21:11:31 -0500 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CBB5DC061760 for ; Wed, 10 Mar 2021 18:11:25 -0800 (PST) Received: by mail-qk1-x749.google.com with SMTP id a137so10638042qkb.20 for ; Wed, 10 Mar 2021 18:11:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=fmVTGH68x2FsqSYInLCVoqvIV47aIbA8dfsXVYpnEoY=; b=fW8GqkiFqsC56IeEH4vYRqZ+dY6IH8ECLmLAW5awsKVny02MYM14I4wBrqqQItL5yx S7EmWC97tVtznvm/tkD41c/AiUGqGL8JDpj8fW4vaZFemqCdrgifKOmI93gUnHswQ+Ta IK4oIkmQ6dNXljiuT4ApUH5oEuSqNBYoFYUNwnRaSq9R4NMPXRFQKyfUU1/rm+lN18bm KZdlHY90P7P+QPjPIXROOoOYSFtOXfu0dm/haAUidCvPmaWCFUFL6GjD7srEDS26QEh7 cIhYwOfYccoeG0Hy07hJ2u3jyex/jFvduWuBSqJpijgBQ/sZF1si0sNj5x2hNAJokdA6 yuCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=fmVTGH68x2FsqSYInLCVoqvIV47aIbA8dfsXVYpnEoY=; b=N5h28E7V0FGcJm6ryA3hkfqLYy5ek/nsxX0tafR+ThOGt91Eyna3uvgzcnplEoU7M5 ojDc3NAR88I6nCMhkMCt9NYguh9VRbPYRfDRoBjEaW/4xW7C92Gm46PLMbSh0s6AWcia jTdVn2Jmu+4DPXX208Zto0+PiLjhJQbPC29QR86mpdSFX+i82o/Lne9IxyeTk58bjf/Q NJ2aVRLcPPKAOzrMAW11qPupEer2213QtrNuuha32YScCiXM+oVu0wdRJf1GL22XB7vz 1n0Xx8VJianpzTMAM3hcot5GTNs6yXhUPoJ7x/Gj5+1Xyna1vYBgE5SrHr7amdLBZ10M UISg== X-Gm-Message-State: AOAM5305P2Lg1lprBTJG85bJGK1mVsVXEKl0o6lGHObXk9e2aDiYcasP BuXzfStFEedHfRNUPRaIc/hLagxdi22flEwlMKDN8m2Y5YV4mO/LoFHYPZOea/bxK+ly3euiL7h /B73IXU2INWpHBnnZAPz+wtP+VemZqAlV2SDKDwzl7KM8px0nAL6+jRCDucAcHB4BxbCi7ubujQ == X-Google-Smtp-Source: ABdhPJyhMWsH6GuMilZ0oUAnJJ/75A8mvn1AGlQER6v5lWNT/oOK1QJNxXLBxOfS3HCINvqtEVaQbZw+w8PYWT1SFaU= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:bf12:: with SMTP id m18mr5761444qvi.40.1615428685000; Wed, 10 Mar 2021 18:11:25 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:19 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-20-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 19/37] am: convert applypatch hooks to use config From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Teach pre-applypatch, post-applypatch, and applypatch-msg to use the hook.h library instead of the run-command.h library. This enables use of hooks specified in the config, in addition to those in the hookdir. These three hooks are called only by builtin/am.c. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 9 +++++++++ builtin/am.c | 14 +++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 984fb998b2..0e7eb972ab 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -58,6 +58,9 @@ the message file. The default 'applypatch-msg' hook, when enabled, runs the 'commit-msg' hook, if the latter is enabled. +Hooks run during 'applypatch-msg' will not be parallelized, because hooks are +expected to edit the file holding the commit log message. + pre-applypatch ~~~~~~~~~~~~~~ @@ -73,6 +76,9 @@ make a commit if it does not pass certain test. The default 'pre-applypatch' hook, when enabled, runs the 'pre-commit' hook, if the latter is enabled. +Hooks run during 'pre-applypatch' will be run in parallel, unless hook.jobs is +configured to 1. + post-applypatch ~~~~~~~~~~~~~~~ @@ -82,6 +88,9 @@ and is invoked after the patch is applied and a commit is made. This hook is meant primarily for notification, and cannot affect the outcome of `git am`. +Hooks run during 'post-applypatch' will be run in parallel, unless hook.jobs is +configured to 1. + pre-commit ~~~~~~~~~~ diff --git a/builtin/am.c b/builtin/am.c index 8355e3566f..4467fd9e63 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -33,6 +33,7 @@ #include "string-list.h" #include "packfile.h" #include "repository.h" +#include "hook.h" /** * Returns the length of the first line of msg. @@ -426,9 +427,13 @@ static void am_destroy(const struct am_state *state) static int run_applypatch_msg_hook(struct am_state *state) { int ret; + struct run_hooks_opt opt; + run_hooks_opt_init_sync(&opt); assert(state->msg); - ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL); + strvec_push(&opt.args, am_path(state, "final-commit")); + ret = run_hooks("applypatch-msg", &opt); + run_hooks_opt_clear(&opt); if (!ret) { FREE_AND_NULL(state->msg); @@ -1558,8 +1563,10 @@ static void do_commit(const struct am_state *state) struct commit_list *parents = NULL; const char *reflog_msg, *author, *committer = NULL; struct strbuf sb = STRBUF_INIT; + struct run_hooks_opt hook_opt; + run_hooks_opt_init_async(&hook_opt); - if (run_hook_le(NULL, "pre-applypatch", NULL)) + if (run_hooks("pre-applypatch", &hook_opt)) exit(1); if (write_cache_as_tree(&tree, 0, NULL)) @@ -1611,8 +1618,9 @@ static void do_commit(const struct am_state *state) fclose(fp); } - run_hook_le(NULL, "post-applypatch", NULL); + run_hooks("post-applypatch", &hook_opt); + run_hooks_opt_clear(&hook_opt); strbuf_release(&sb); } From patchwork Thu Mar 11 02:10:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130111 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A4A90C4361B for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8B32A64FDF for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230035AbhCKCLn (ORCPT ); Wed, 10 Mar 2021 21:11:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42296 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229808AbhCKCL2 (ORCPT ); Wed, 10 Mar 2021 21:11:28 -0500 Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 747AAC061761 for ; Wed, 10 Mar 2021 18:11:27 -0800 (PST) Received: by mail-qt1-x84a.google.com with SMTP id k10so14403353qte.17 for ; Wed, 10 Mar 2021 18:11:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=2NPGx/Kk0vx2WiO4XBV7sErSPH0XaHLjOZp5eGH8ocw=; b=r5o3hjsh0mD734q/57ERzBntx+UVDVYALBub93qxAaiLL9mYk39p67hsma/mB5L93W lHGhigGJAjGaR3BQde9zMKqrH7OnItg0nPSFMs/FlTV6mV4O7NdaTyLCg6jXcLFm3Uq3 WF+PdeJCkDjdogHEn/q4wDXaI0Bhn3w0ugCQ0b0vBvEH1g6G7GiJpqO6/Nvxip92tzE1 RBHTm19mcQgEeamW+6xiHNsga4YAJ+PB2T1d4/uU9HhylSn1bo214RdwsK6pqZjkBizx 1vjrSCP9nhZT6VMCLw8/dGULQG0SsVdVOcEEp0q1WY1Jq9PvZzVRWn2/BVdspIfYKuku WZBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=2NPGx/Kk0vx2WiO4XBV7sErSPH0XaHLjOZp5eGH8ocw=; b=S8yhpBwi1BywslLHWbO3XbQ4b0PXpxwKpAHihWejalE4KxaqqtGevnbYEFwWhipF/f 8PSlf2SD+YgmThYEX3rZsyYEQZ65+Iz02KX0tXUJrWtgwehKF+fVdLl4OKv+2iraxZeh j8dY4eXX4dmzNzhTRaokR2VWXmp233rXlC58shW+FMH1GIooXan6SLng3qSQ/HoD95+L L78LP/qHIa0D9LujtdPjtVzXdA4i9ijZE62yDVioIQHN1N0ORr1ZN/xUnNpehkw0eVUR 1qubwneewP0yAnYx/DS/BUalA62x/yLNZcUG9JPEhKBiPZ3966oeKExCGBK2UnjJrspm ck2Q== X-Gm-Message-State: AOAM531NSlMWCIyxEcPSxlVOw/f/SwZqX2RnMsJVKuEzDpPkb3nrtJVs HMhGymccWaJK8q4/dT3W1yNukQH8bV3zbMBgJC1b0B6hwPAyK3GyarWxB7vaLnCjNwWggBPJQV7 JmtqM4/PHpUtbUkWRIWq9eiaDpNAXKvwfrcTAmBMOgLbl72wvz8gSxCsnjSRtzWB2xftgEMKojA == X-Google-Smtp-Source: ABdhPJx18Vxis0CtatM6DzSpdaFNhyxNP/Yjim0xXC/ef/b8V9Z3B2QimR4AlMG27QpMWSlNwSeO0tt8Wkg5sQW8SsQ= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:12a1:: with SMTP id w1mr5773159qvu.57.1615428686663; Wed, 10 Mar 2021 18:11:26 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:20 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-21-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 20/37] merge: use config-based hooks for post-merge hook From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Teach post-merge to use the hook.h library instead of the run-command.h library to run hooks. This means that post-merge hooks can come from the config as well as from the hookdir. post-merge is invoked only from builtin/merge.c. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 +++ builtin/merge.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 0e7eb972ab..664ad4803e 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -242,6 +242,9 @@ save and restore any form of metadata associated with the working tree (e.g.: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl for an example of how to do this. +Hooks executed during 'post-merge' will run in parallel, unless hook.jobs is +configured to 1. + pre-push ~~~~~~~~ diff --git a/builtin/merge.c b/builtin/merge.c index 33df744ab0..b473c8c5d3 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -444,7 +444,9 @@ static void finish(struct commit *head_commit, const struct object_id *new_head, const char *msg) { struct strbuf reflog_message = STRBUF_INIT; + struct run_hooks_opt opt; const struct object_id *head = &head_commit->object.oid; + run_hooks_opt_init_async(&opt); if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); @@ -485,7 +487,9 @@ static void finish(struct commit *head_commit, } /* Run a post-merge hook */ - run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL); + strvec_push(&opt.args, squash ? "1" : "0"); + run_hooks("post-merge", &opt); + run_hooks_opt_clear(&opt); apply_autostash(git_path_merge_autostash(the_repository)); strbuf_release(&reflog_message); From patchwork Thu Mar 11 02:10:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130107 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 446AAC43603 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1B65964FD8 for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230002AbhCKCLn (ORCPT ); Wed, 10 Mar 2021 21:11:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42306 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229810AbhCKCL3 (ORCPT ); Wed, 10 Mar 2021 21:11:29 -0500 Received: from mail-qv1-xf4a.google.com (mail-qv1-xf4a.google.com [IPv6:2607:f8b0:4864:20::f4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3CCCCC061762 for ; Wed, 10 Mar 2021 18:11:29 -0800 (PST) Received: by mail-qv1-xf4a.google.com with SMTP id u15so14182088qvo.13 for ; Wed, 10 Mar 2021 18:11:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=++sDMC8n1GO52CUP5ppNYXHMDrDDE5ZfUW2YH+saiIQ=; b=SDAnlXoXN1VRq9ZzoEtO8uaymzXOqCMWxVZc9w262r8wucH/NZ3F04AKUoLI+7FQMX mDt58f5Q9Jf+sCD4L2KkSFS7AdBtd4sirU8TQafk3WLiPWriiMltM/4ox793mtHQYvJw MA0gRF8tCyEc44Fw742lmgB+mjQM1iiznEAcs34eGdQaJjR/lybDXv0dwqzdQHIvEqI3 mlNqHszh/jVP6qO79c+B5q4VBYRjBxDN9ETmvzXlwlusp30FVY8ZACamBJHuD9m/pKHE ti0uHJc9zHlNIYkVKxTsuApYwzFY/jbTQD7cX6ezGZfs5xpFxpLOO44kPqvrJo8l/Hio 61+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=++sDMC8n1GO52CUP5ppNYXHMDrDDE5ZfUW2YH+saiIQ=; b=Wofbb4H2aUwZqrCj+rlwQTEknI9n33fJ82XckbY24Zcs4ZXgr0OxBtJgzsfYPOSPq7 9MOLaJoXYFiVUzWoL94OvlhZTPtqNicbLJCt/PdBv4mXmK/fCHGp9Uk77SzYhTAAq30P n485HkEWkgJzUyGwvl2jZ4TYhe1ypiGXWV1kZiDvOrjOwDix7zt++kVJNgNBPxPjpaee wbCXL2A840ZbsnH1v+fBEfaHc20LGolKldJYs5V86TQn2uVn4rPKMSB1SedCWH0xGfm7 6FKAhRHE7hNIAEIcLyGcc7AqkR7Z7MNGNKtm+xW1eYJ7NWGXgiLxteC6NQIBEkOyFgMw bGjA== X-Gm-Message-State: AOAM5333L0qWvuGEJoGcWamGzQfcAA6aXVzcnPf3q9VuX2yVLrr4JQpY /hMgBtPfkCfo73REM5v94eWOcjbMPQ8jTTYV3KJ7GI1FK/r5KifoKum994a/6AEUBIIcS6WaTG5 kvc1EjenICUZSjCxflgrX8SaJDRii+IiHnN5LxnQFR+82dkq0YKm2lhS8E+PkAkS3kKPGXjwhpg == X-Google-Smtp-Source: ABdhPJwhCzWRBiX1C96AuxKTBDa4DrX0mheQGx2sD/R/CFjyIy9skym4ep2jmSNgQtnKRUSeyw5UgSBvzhZmuQQYJXw= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:c68f:: with SMTP id d15mr5946822qvj.20.1615428688390; Wed, 10 Mar 2021 18:11:28 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:21 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-22-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 21/37] gc: use hook library for pre-auto-gc hook From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Using the hook.h library instead of the run-command.h library to run pre-auto-gc means that those hooks can be set up in config files, as well as in the hookdir. pre-auto-gc is called only from builtin/gc.c. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 +++ builtin/gc.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 664ad4803e..00f88912cd 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -560,6 +560,9 @@ This hook is invoked by `git gc --auto` (see linkgit:git-gc[1]). It takes no parameter, and exiting with non-zero status from this script causes the `git gc --auto` to abort. +Hooks run during 'pre-auto-gc' will be run in parallel, unless hook.jobs is +configured to 1. + post-rewrite ~~~~~~~~~~~~ diff --git a/builtin/gc.c b/builtin/gc.c index ef7226d7bc..e62cb510ee 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -32,6 +32,7 @@ #include "remote.h" #include "object-store.h" #include "exec-cmd.h" +#include "hook.h" #define FAILED_RUN "failed to run %s" @@ -348,6 +349,8 @@ static void add_repack_incremental_option(void) static int need_to_gc(void) { + struct run_hooks_opt hook_opt; + run_hooks_opt_init_async(&hook_opt); /* * Setting gc.auto to 0 or negative can disable the * automatic gc. @@ -394,7 +397,7 @@ static int need_to_gc(void) else return 0; - if (run_hook_le(NULL, "pre-auto-gc", NULL)) + if (run_hooks("pre-auto-gc", &hook_opt)) return 0; return 1; } From patchwork Thu Mar 11 02:10:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130117 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87772C4361A for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5429064FDC for ; Thu, 11 Mar 2021 02:12:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230051AbhCKCLo (ORCPT ); Wed, 10 Mar 2021 21:11:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42296 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229814AbhCKCLb (ORCPT ); Wed, 10 Mar 2021 21:11:31 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E2498C061574 for ; Wed, 10 Mar 2021 18:11:30 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id q77so23900371ybq.0 for ; Wed, 10 Mar 2021 18:11:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=LK9AvPeCqTvaxfb5WC9sejNPiAu/1Rrn4PDquj7Aux4=; b=DLSXagUHTTMmeFys3lJn3Y+B5s0XlY3nJF8CSMo8b5R7Wk3zZH6PwLhzpaBVzJTaDz hFomlGjfZ2YNH39mVEisDDkloN0IIL34DZoh8iIxMIRbeXe+VHYBWQ3Q+3HjR3Qd6Csj /xlgTlRUvCGJbE2pp3vSj2JWItcMw6DRWdxWgSJAJgLoZ3IkhtWceX37JmIkxv9bEENo n05oIyUk/TXybAFvJKPm2LoHXJAOt7N5W/cwp495EB4AjZQUrHTdLGQjs0pNVcm7IwO+ i4VU8ySCCbxJsgDR7A8Gw250BDaqs/Zp5oCttsk7b4jNeqwnuuB2lBeKl+8aWtHAUDEI 3boA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=LK9AvPeCqTvaxfb5WC9sejNPiAu/1Rrn4PDquj7Aux4=; b=bgcNECVQlOCJOnWPrMHleRrw8sDWeo61Y8PT2DaD5zoT6QRHe1L7U6YPfwLcDjhrul 1GLOUVMNeqzev5onFR4PLJj3dBt28zq9igZpq93y3jcYZWe5TnadBbpvm+qPXXbXyspv OPfSjqNlinmjwBbny728kpJZX0btNeWsul0Cy1nTT/fnZKoj+zCCskSu30DCS6pbSZZo b/G3O6kD5XPVSdtkBtzQ8k/PLRnMtNOEiTLEaREZtPRk+y5IWUGwyNKl5H9sdjAj+rTk Yae6yqPSz3njzDe/lqXmsCEFwwj6xOagvG3xjm79zhGB4yahisel8btPj0kzEdf/WNiZ S5Cw== X-Gm-Message-State: AOAM533p/8aqLfps9Ii1FJzbTXzTKCgO8O+CXKoThGuO7WRZzBbd4jIr YHzkeWlHA0ZHke78vjiZYxAUUKB4aKB/uyJecwFOaL1sdsDfhlV5zry5kjx+nvsqnSAH1zYNovu grma73hmskFpEl9PO4hTU9dQyQYz2IDgBeP8VOWAwT9p+HTQ+jHgGUaMs9vBY4C8e0wPQX1k7hQ == X-Google-Smtp-Source: ABdhPJyKqclLp6AbQL7FIfKvU13w3s1a3en+HbOej/qhdySbAjJ2/xiXGkRFLLthh79+t8ZcDArYuPZBEz0Kg6/390g= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:6c46:: with SMTP id h67mr8529551ybc.224.1615428690141; Wed, 10 Mar 2021 18:11:30 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:22 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-23-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 22/37] rebase: teach pre-rebase to use hook.h From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using hook.h instead of run-command.h to run hooks, pre-rebase hooks can now be specified in the config as well as in the hookdir. pre-rebase is not called anywhere besides builtin/rebase.c. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 +++ builtin/rebase.c | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 00f88912cd..e3a0375827 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -206,6 +206,9 @@ two parameters. The first parameter is the upstream from which the series was forked. The second parameter is the branch being rebased, and is not set when rebasing the current branch. +Hooks executed during 'pre-rebase' will run in parallel, unless hook.jobs is +configured to 1. + post-checkout ~~~~~~~~~~~~~ diff --git a/builtin/rebase.c b/builtin/rebase.c index de400f9a19..c35b5ba452 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -28,6 +28,7 @@ #include "sequencer.h" #include "rebase-interactive.h" #include "reset.h" +#include "hook.h" #define DEFAULT_REFLOG_ACTION "rebase" @@ -1318,6 +1319,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) char *squash_onto_name = NULL; int reschedule_failed_exec = -1; int allow_preemptive_ff = 1; + struct run_hooks_opt hook_opt; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, N_("revision"), @@ -1431,6 +1433,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) }; int i; + run_hooks_opt_init_async(&hook_opt); + if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_rebase_usage, builtin_rebase_options); @@ -2032,9 +2036,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } /* If a hook exists, give it a chance to interrupt*/ + strvec_pushl(&hook_opt.args, options.upstream_arg, argc ? argv[0] : NULL, NULL); if (!ok_to_skip_pre_rebase && - run_hook_le(NULL, "pre-rebase", options.upstream_arg, - argc ? argv[0] : NULL, NULL)) + run_hooks("pre-rebase", &hook_opt)) die(_("The pre-rebase hook refused to rebase.")); if (options.flags & REBASE_DIFFSTAT) { @@ -2114,6 +2118,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) ret = !!run_specific_rebase(&options, action); cleanup: + run_hooks_opt_clear(&hook_opt); strbuf_release(&buf); strbuf_release(&revisions); free(options.head_name); From patchwork Thu Mar 11 02:10:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130121 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14F34C433E0 for ; Thu, 11 Mar 2021 02:12:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DA83264FAF for ; Thu, 11 Mar 2021 02:12:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229775AbhCKCMH (ORCPT ); Wed, 10 Mar 2021 21:12:07 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42316 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229825AbhCKCLj (ORCPT ); Wed, 10 Mar 2021 21:11:39 -0500 Received: from mail-qv1-xf4a.google.com (mail-qv1-xf4a.google.com [IPv6:2607:f8b0:4864:20::f4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ADFD6C061574 for ; Wed, 10 Mar 2021 18:11:32 -0800 (PST) Received: by mail-qv1-xf4a.google.com with SMTP id h10so14179331qvf.19 for ; Wed, 10 Mar 2021 18:11:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Vc1COTTg+DDFEw5oTnNcR8CtFJOx1prRdNsgtg7H4uw=; b=U/tJR29LTkfEoBro11Bz6Nql8e8i6KBEH8CMNxAUQaqHq5v0FtVT5RthbAzhVcJmxS 2x1PZCVhmCSw7Mgm4gsWfGqLwFowQgOOi032lfrNdDmxZyLhah7QWXXT4FruARA7DHVB H0J5OrvCNR6ocTdMNc5wFKbwUAhYh12ZAUhUp0zpRxu1Z2Mh8E9U/rFKsyknacXgbRKO LvNL7fDxy0za54ml5XeuPgxRIx0J4SOzufU+fkOkMrCEJPlozou65+/UHPl8E6kVKrJr VTjH4Mn/jgQdFdrP2NGqJ6k/tMNo+/ovrMjenWFdEOTprNO5xtueMJ650K0rNmrUAELI V/cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Vc1COTTg+DDFEw5oTnNcR8CtFJOx1prRdNsgtg7H4uw=; b=W67XGkwUNMZb0jC7AYs7X8w95WA6SnD1I8N+tv94PKPow7NG6PqdM7Ef3aMely/rh0 PH2PdlW2PKXadSWval/UstrSewcxwL0BXtCLtNqK5izDbXhfZz712QpzeZ9dU0BxaIEW bDjlXno8YZKObR4ujGe/fcEQbdKfMVW/wKZGb4ZXNWv4gbFq1nv/paX8ByOm9tptXbHY 5SRloEyUFbG6Tjfq7mSh7TUxCGuJUV51KV0crjENu6MP8XqxAgBCEfLELTCssFTnC/BH NU1HhInOCkUrZEc9K+eHOh56mxZFapm0X7irijZbqtsoIvWWYe6DczMFY/7GZoaHD8iC Istw== X-Gm-Message-State: AOAM532viIORJmbPhSv7mvfSyfWHhaBfmrPLPA4GGHxmaCw+HMNO8FgB //8jxXi6qWi/NIGzpVBPtvyAJYO1Kd81dAjD7/goHuexQKzTb7EQ0rwLOBa7hM4y/2LxDpaSenV B2wrWSqtOmbZtRwXLodZlqwzIGS2eTC++sttSgijDIyn6A823mUsudfUyFLXTjTfGoDNp/1K/aA == X-Google-Smtp-Source: ABdhPJzeR1lxYgyyRP4HgaDX/y9ZNurPMyil+7BSvaSuTB6Fan8PBCYoOMJilL22mmeYvjVK4Mp7+bIme4eMLgvd9uw= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:a0c6:: with SMTP id c64mr5883320qva.57.1615428691916; Wed, 10 Mar 2021 18:11:31 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:23 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-24-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 23/37] read-cache: convert post-index-change hook to use config From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using hook.h instead of run-command.h to run, post-index-change hooks can now be specified in the config in addition to the hookdir. post-index-change is not run anywhere besides in read-cache.c. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 +++ read-cache.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index e3a0375827..e5c2cef271 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -720,6 +720,9 @@ and "0" meaning they were not. Only one parameter should be set to "1" when the hook runs. The hook running passing "1", "1" should not be possible. +Hooks run during 'post-index-change' will be run in parallel, unless hook.jobs +is configured to 1. + GIT --- Part of the linkgit:git[1] suite diff --git a/read-cache.c b/read-cache.c index 1e9a50c6c7..fd6c111372 100644 --- a/read-cache.c +++ b/read-cache.c @@ -25,6 +25,7 @@ #include "fsmonitor.h" #include "thread-utils.h" #include "progress.h" +#include "hook.h" /* Mask for the name length in ce_flags in the on-disk index */ @@ -3070,6 +3071,8 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l unsigned flags) { int ret; + struct run_hooks_opt hook_opt; + run_hooks_opt_init_async(&hook_opt); /* * TODO trace2: replace "the_repository" with the actual repo instance @@ -3088,9 +3091,13 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l else ret = close_lock_file_gently(lock); - run_hook_le(NULL, "post-index-change", - istate->updated_workdir ? "1" : "0", - istate->updated_skipworktree ? "1" : "0", NULL); + strvec_pushl(&hook_opt.args, + istate->updated_workdir ? "1" : "0", + istate->updated_skipworktree ? "1" : "0", + NULL); + run_hooks("post-index-change", &hook_opt); + run_hooks_opt_clear(&hook_opt); + istate->updated_workdir = 0; istate->updated_skipworktree = 0; From patchwork Thu Mar 11 02:10:24 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130131 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8FDA0C432C3 for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6B8E164FEE for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230431AbhCKCMO (ORCPT ); Wed, 10 Mar 2021 21:12:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42324 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229814AbhCKCLp (ORCPT ); Wed, 10 Mar 2021 21:11:45 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 74855C061760 for ; Wed, 10 Mar 2021 18:11:34 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id l3so23889161ybf.17 for ; Wed, 10 Mar 2021 18:11:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=0BuRmGZKoOY5XfD4AJjW5pTpDGMeN228GKmYi+Vi1Tc=; b=jSRYkW2gkRNjtN0dTlkbHeUZqytsYE6Ra5rGr6GH0tYBpBgS353RbP9KbBwoWpw1Rl 76bMF8X4AWPkAbWwXjA5NNYrhE/t2X+LH1g/SptNi2Dap2cMCr18qS07AEpGRI4ChLxW b362DWQDdWtdpBkRNEedTIqdkDl4ly8C6eeqPQLznx/ezRX1NwF4DOHkju1Qtd4LE9P2 hcGiphIe3AaPaw7u9mKxEpsl6N/3Gg/q66GFmJ9KaVO8IPVHFfVkGf7DSuRPOcJWbcCK 5pp3kf0u8xf0OA/x+Ojuqr7k71gw1qqfA8lpLop0N5JlvrC45C0UXh7XLFof1W3EGCzx 6dmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=0BuRmGZKoOY5XfD4AJjW5pTpDGMeN228GKmYi+Vi1Tc=; b=XyF1PDzIWLI1X7c8Lv3n5lyE112Wnl+w5BGHEyCU4snTw/HRwbG+Y55aM6hoqqNaXB CF5d6UaZPYJxfSJMbmq2piKy8xCsopGoYvRVaIWtDjp1Ne+ITxQJijiVYCP8wT4XdZwH LfnBEgsVfYsMiT7boN6a05YsVEFlh3d9RYVqemdeQDV9xrqTON7woWJi/44Zg8F/sc8N yZkGIdoglqSMJpqMDEhdeQp73ARxU+rL9wkMqzjs8h8dQ1BWEo5EyLKaZpF7SWY6IlD1 5aPGoYik+wYEh8gUul6L9hyTDh47xxbLk7m2fa8zmnM07e2Ow6vON4az987jxv9XxOp7 RThA== X-Gm-Message-State: AOAM5301U4s2p8+aIlyBFQKA6nsaCv5ApbsICTwyZ2LpLXADPwls0Q+u l2FY7oVzCQEK/L/SA44gwF/CNXOhxP0VwVJAUVwvaY0oD4sDkwCPuzQ8QisrWjh1DR7htkv8oBa X3VTQv1hQCmNjgCqPoMbEP0gkMoslKDKuysH8TwKU7vO7G9CyRnkE6WgOJ0wihH2B1XxkJDD2/w == X-Google-Smtp-Source: ABdhPJyqgPoy3LbjarlnGCQiHV2ULg5EpEtjcMRSzFB1DboVgo3SjD6HCntmrGvLhgvrmCcHnIg1S+pwXXRatZolTI0= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:e752:: with SMTP id e79mr8684278ybh.373.1615428693672; Wed, 10 Mar 2021 18:11:33 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:24 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-25-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 24/37] receive-pack: convert push-to-checkout hook to hook.h From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using hook.h instead of run-command.h to invoke push-to-checkout, hooks can now be specified in the config as well as in the hookdir. push-to-checkout is not called anywhere but in builtin/receive-pack.c. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 1 + builtin/receive-pack.c | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index e5c2cef271..f2178dbc83 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -555,6 +555,7 @@ that switches branches while keeping the local changes in the working tree that do not interfere with the difference between the branches. +Hooks executed during 'push-to-checkout' will not be parallelized. pre-auto-gc ~~~~~~~~~~~ diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index d26040c477..234b70f0d1 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -29,6 +29,7 @@ #include "commit-reach.h" #include "worktree.h" #include "shallow.h" +#include "hook.h" static const char * const receive_pack_usage[] = { N_("git receive-pack "), @@ -1435,12 +1436,19 @@ static const char *push_to_checkout(unsigned char *hash, struct strvec *env, const char *work_tree) { + struct run_hooks_opt opt; + run_hooks_opt_init_sync(&opt); + strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree)); - if (run_hook_le(env->v, push_to_checkout_hook, - hash_to_hex(hash), NULL)) + strvec_pushv(&opt.env, env->v); + strvec_push(&opt.args, hash_to_hex(hash)); + if (run_hooks(push_to_checkout_hook, &opt)) { + run_hooks_opt_clear(&opt); return "push-to-checkout hook declined"; - else + } else { + run_hooks_opt_clear(&opt); return NULL; + } } static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree) @@ -1464,7 +1472,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir)); - if (!find_hook(push_to_checkout_hook)) + if (!hook_exists(push_to_checkout_hook, HOOKDIR_USE_CONFIG)) retval = push_to_deploy(sha1, &env, work_tree); else retval = push_to_checkout(sha1, &env, work_tree); From patchwork Thu Mar 11 02:10:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130133 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B7554C4360C for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9CC8B64F95 for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229660AbhCKCMQ (ORCPT ); Wed, 10 Mar 2021 21:12:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42334 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230075AbhCKCLr (ORCPT ); Wed, 10 Mar 2021 21:11:47 -0500 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 31B9FC061761 for ; Wed, 10 Mar 2021 18:11:36 -0800 (PST) Received: by mail-qk1-x749.google.com with SMTP id k185so14272178qkb.17 for ; Wed, 10 Mar 2021 18:11:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=SnF4+xvrJCimJCe+8zZxVyDOEtpGuUX/xjX/UPmE6mw=; b=O79uHMcbCTzeUFlBR1lykYS2oBdPTzcM6rPrK8SjeW9oDj9kcQyVNL88UXVLbb1zvR aVR8tVutQS1yAOZrN1Z6vAs8EK+Tjvi1rOmmj5LmMBkVFpWYBKBqbbwS+e+j4NnpR+AL ag83MVvLC5275vtgxOgRHclvDzVjvE1zhvZUVSFKOUV9Npg+jiG/K9PJrrsqeWnuVO7X VE3k6t0owXFNLVQuh839IYprtH+m1yFxp/Roy2zlvE7OINJcY+x4sN+AXi01dSU8P/eT 2IeYV9bLbx0k4lOvLfX5jf5m8bTUlJXhqj5xVS+om+ZvbXu5It1w41Tn3pmpFNk4x3en oSOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=SnF4+xvrJCimJCe+8zZxVyDOEtpGuUX/xjX/UPmE6mw=; b=hZzjCqnW9h02ZVZhZZzI3T0CLzVF4YP+OufFSqZPUODqiaKkqLvM+EituoImD/mb+u WNkUmD62L8nut+GkzYM2173eyXhl1ssh9qeNAiu8A5xqpMTEU8oBsmTUAyLFmo4PFHGL TF/phnL1YXfR3I85aeokvI6iaInG9xc5aQFEyHsMFsudh6ZWK15dqiVUa+W2cDKJYdar Dfny1DVFevUSYux66IoxdpHCxA7UJsETqBuEfYmJ2FufUgI/ZMFTTqC3E/0EAHKHGurC ZfgmSloUQVpIR8vOVu4a9zQ9jEabaxy3sANDm2j/1Rj+r1C+MMXK/3vyI+B5+EuoSEZd JUUQ== X-Gm-Message-State: AOAM532TYA9o9dmer1gfaenmDN5ktFSXH8Uqzm5xfoPRYw0Sy9n8szSv cKFFVfkg4KLAxb/sOvUizQE2aKb3KdAD9Q7W7uO1t7yoWPQVqPAXxXVfWGpo0H9XAWDY9yk13Ax kDfmVKE2j8FVpXUJh4mfBKuasaEDYWnjOB8Pc1IXnkbG7NBMflBRCIKR7H6pE6hk1+VBmEld7+w == X-Google-Smtp-Source: ABdhPJw/cud39csES+05Q1FjqUbk9hF2CJkcfxssVLySJ6Bp9yW8AFkpSUkTwNYd38+O36TOM+UXhBgdQOSyi8B88Tg= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:fd41:: with SMTP id j1mr4089072qvs.29.1615428695384; Wed, 10 Mar 2021 18:11:35 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:25 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-26-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 25/37] git-p4: use 'git hook' to run hooks From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Instead of duplicating the behavior of run-command.h:run_hook_le() in Python, we can directly call 'git hook run'. As a bonus, this means git-p4 learns how to find hook specifications from the Git config as well as from the hookdir. Signed-off-by: Emily Shaffer --- Notes: Maybe there is a better way to do this - I had a hard time getting this to run locally, and Python is not my forte, so if anybody has a better approach I'd love to just take that patch instead :) Since v6, removed the developer debug print statements.... :X Maybe there is a better way to do this - I had a hard time getting this to run locally, and Python is not my forte, so if anybody has a better approach I'd love to just take that patch instead :) git-p4.py | 67 +++++-------------------------------------------------- 1 file changed, 6 insertions(+), 61 deletions(-) diff --git a/git-p4.py b/git-p4.py index 09c9e93ac4..4b1c69822c 100755 --- a/git-p4.py +++ b/git-p4.py @@ -208,70 +208,15 @@ def decode_path(path): def run_git_hook(cmd, param=[]): """Execute a hook if the hook exists.""" - if verbose: - sys.stderr.write("Looking for hook: %s\n" % cmd) - sys.stderr.flush() - - hooks_path = gitConfig("core.hooksPath") - if len(hooks_path) <= 0: - hooks_path = os.path.join(os.environ["GIT_DIR"], "hooks") - - if not isinstance(param, list): - param=[param] - - # resolve hook file name, OS depdenent - hook_file = os.path.join(hooks_path, cmd) - if platform.system() == 'Windows': - if not os.path.isfile(hook_file): - # look for the file with an extension - files = glob.glob(hook_file + ".*") - if not files: - return True - files.sort() - hook_file = files.pop() - while hook_file.upper().endswith(".SAMPLE"): - # The file is a sample hook. We don't want it - if len(files) > 0: - hook_file = files.pop() - else: - return True - - if not os.path.isfile(hook_file) or not os.access(hook_file, os.X_OK): + if not cmd: return True - return run_hook_command(hook_file, param) == 0 - -def run_hook_command(cmd, param): - """Executes a git hook command - cmd = the command line file to be executed. This can be - a file that is run by OS association. - - param = a list of parameters to pass to the cmd command - - On windows, the extension is checked to see if it should - be run with the Git for Windows Bash shell. If there - is no file extension, the file is deemed a bash shell - and will be handed off to sh.exe. Otherwise, Windows - will be called with the shell to handle the file assocation. - - For non Windows operating systems, the file is called - as an executable. - """ - cli = [cmd] + param - use_shell = False - if platform.system() == 'Windows': - (root,ext) = os.path.splitext(cmd) - if ext == "": - exe_path = os.environ.get("EXEPATH") - if exe_path is None: - exe_path = "" - else: - exe_path = os.path.join(exe_path, "bin") - cli = [os.path.join(exe_path, "SH.EXE")] + cli - else: - use_shell = True - return subprocess.call(cli, shell=use_shell) + """args are specified with -a -a -a """ + args = (['git', 'hook', 'run'] + + ["-a" + arg for arg in param] + + [cmd]) + return subprocess.call(args) == 0 def write_pipe(c, stdin): if verbose: From patchwork Thu Mar 11 02:10:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130119 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 83DC0C433DB for ; Thu, 11 Mar 2021 02:12:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4035F64FAA for ; Thu, 11 Mar 2021 02:12:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230173AbhCKCMG (ORCPT ); Wed, 10 Mar 2021 21:12:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42342 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229791AbhCKCLi (ORCPT ); Wed, 10 Mar 2021 21:11:38 -0500 Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DB0E4C061762 for ; Wed, 10 Mar 2021 18:11:37 -0800 (PST) Received: by mail-qt1-x849.google.com with SMTP id o7so14419094qtw.7 for ; Wed, 10 Mar 2021 18:11:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Bnq0SMExdgj7ERjG7VLd2BP1kiGs0pvkvTHdIxv+kaE=; b=HoAtqWZWb8Gg/NS5WW/UxdtqamIcVebB1Ouf23Lo2zl3uEZ53ERtsoQW/xF43tVk/u tppt0S6Xqv+BiOiP1MWQIT2N9cgC3zfEUqvG+YFoIWKaPVrH6JPTeKiuwlqvI0FzAOkA dC09PyNM+DjlqgTlA+M1Rcxm3sL/MiiXvlDhTbVekfXoFI7/cXkBChRRGNVezsDveXg6 AW+CLykWMCkJ6Z5XH9Y7ErHTo/OS8G2B+0uT8OxQxgMiMSOpDjKe+PXgHXSh9cer15k7 sJImq/W+wAMPDmo3SqdRY7CDZr2hPN76kZQW/0fYVAZV26UDYB9+NI5lHw6a4J5SyDed KFpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Bnq0SMExdgj7ERjG7VLd2BP1kiGs0pvkvTHdIxv+kaE=; b=M4WmNKgW8YikiX1515GwpLzVvb3NcZTexOabNvw7LZhZS7N36hL31vran484B12Mtm 0I8U27qBchnCe2aQ8dgZ5mVkSLFjfyCsPAKe730shHETaAEvKDvFu+XWPrzbzelGTxIV qXwEeIFLFSutccuMsKK7t/QLa8/3Zr7F+KWJAloGD7hO7H/7hJ7KHr4NBRY8WUtbhIrV tJud36MCRpR7sHXJwgdLJt/9KrS47l9WLYI+qtes7xyBPhaQ1zSNYXw9kMk7XH3bc1gW L2n8AeZlfDnezCJzSFtUFbQxC5obpUWofZVFFkBZeByW+m7GvnLHxRJ+7eEMj+JsxKF3 GZnQ== X-Gm-Message-State: AOAM531CHPiAEC9A8iYZYI8gwy8+PBUuJIGzVsKX4sYxPncGj+Z/gqS2 yzflDLU9BIzEo5bQH8eMc6nwQoQxzpExRhk0Vnd37M/vwT735rNtg+XUaa976Y7gVilAOf9xvcC NuGLa9Xx6pGI0BtbfeVF6qJ1pKzMoo6O7ER84oD4vzDBc4C1NUjDwIZOMRkkKH0NurHoXfUzY/A == X-Google-Smtp-Source: ABdhPJwh9ws8WNEdoYLlZE5iSBg3HGpqVE1CluthIKNwdEadHUxrwt06hARZt5Unl5a8skf9Y2/GpoOeBQvMTvD1xag= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:262a:: with SMTP id gv10mr6016177qvb.50.1615428697038; Wed, 10 Mar 2021 18:11:37 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:26 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-27-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 26/37] hooks: convert 'post-checkout' hook to hook library From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using the 'hook.h' library, 'post-checkout' hooks can now be specified in the config as well as in the hook directory. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 2 ++ builtin/checkout.c | 19 ++++++++++++++----- builtin/clone.c | 8 ++++++-- builtin/worktree.c | 31 +++++++++++++++---------------- reset.c | 16 ++++++++++++---- 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index f2178dbc83..362224a03b 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -231,6 +231,8 @@ This hook can be used to perform repository validity checks, auto-display differences from the previous HEAD if different, or set working dir metadata properties. +Hooks executed during 'post-checkout' will not be parallelized. + post-merge ~~~~~~~~~~ diff --git a/builtin/checkout.c b/builtin/checkout.c index 2d6550bc3c..f287b5e643 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -9,6 +9,7 @@ #include "config.h" #include "diff.h" #include "dir.h" +#include "hook.h" #include "ll-merge.h" #include "lockfile.h" #include "merge-recursive.h" @@ -104,13 +105,21 @@ struct branch_info { static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit, int changed) { - return run_hook_le(NULL, "post-checkout", - oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid), - oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid), - changed ? "1" : "0", NULL); + struct run_hooks_opt opt; + int rc; + + run_hooks_opt_init_sync(&opt); + /* "new_commit" can be NULL when checking out from the index before a commit exists. */ - + strvec_pushl(&opt.args, + oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid), + oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid), + changed ? "1" : "0", + NULL); + rc = run_hooks("post-checkout", &opt); + run_hooks_opt_clear(&opt); + return rc; } static int update_some(const struct object_id *oid, struct strbuf *base, diff --git a/builtin/clone.c b/builtin/clone.c index 51e844a2de..52f2a5ecb4 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -32,6 +32,7 @@ #include "connected.h" #include "packfile.h" #include "list-objects-filter-options.h" +#include "hook.h" /* * Overall FIXMEs: @@ -771,6 +772,8 @@ static int checkout(int submodule_progress) struct tree *tree; struct tree_desc t; int err = 0; + struct run_hooks_opt hook_opt; + run_hooks_opt_init_sync(&hook_opt); if (option_no_checkout) return 0; @@ -816,8 +819,9 @@ static int checkout(int submodule_progress) if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); - err |= run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid), - oid_to_hex(&oid), "1", NULL); + strvec_pushl(&hook_opt.args, oid_to_hex(&null_oid), oid_to_hex(&oid), "1", NULL); + err |= run_hooks("post-checkout", &hook_opt); + run_hooks_opt_clear(&hook_opt); if (!err && (option_recurse_submodules.nr > 0)) { struct strvec args = STRVEC_INIT; diff --git a/builtin/worktree.c b/builtin/worktree.c index 1cd5c2016e..8b06d121e5 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -13,6 +13,7 @@ #include "utf8.h" #include "worktree.h" #include "quote.h" +#include "hook.h" static const char * const worktree_usage[] = { N_("git worktree add [] []"), @@ -383,22 +384,20 @@ static int add_worktree(const char *path, const char *refname, * is_junk is cleared, but do return appropriate code when hook fails. */ if (!ret && opts->checkout) { - const char *hook = find_hook("post-checkout"); - if (hook) { - const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL }; - cp.git_cmd = 0; - cp.no_stdin = 1; - cp.stdout_to_stderr = 1; - cp.dir = path; - cp.env = env; - cp.argv = NULL; - cp.trace2_hook_name = "post-checkout"; - strvec_pushl(&cp.args, absolute_path(hook), - oid_to_hex(&null_oid), - oid_to_hex(&commit->object.oid), - "1", NULL); - ret = run_command(&cp); - } + struct run_hooks_opt opt; + run_hooks_opt_init_sync(&opt); + + strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL); + strvec_pushl(&opt.args, + oid_to_hex(&null_oid), + oid_to_hex(&commit->object.oid), + "1", + NULL); + opt.dir = path; + + ret = run_hooks("post-checkout", &opt); + + run_hooks_opt_clear(&opt); } strvec_clear(&child_env); diff --git a/reset.c b/reset.c index 2f4fbd07c5..85ee75f7fd 100644 --- a/reset.c +++ b/reset.c @@ -7,6 +7,7 @@ #include "tree-walk.h" #include "tree.h" #include "unpack-trees.h" +#include "hook.h" int reset_head(struct repository *r, struct object_id *oid, const char *action, const char *switch_to_branch, unsigned flags, @@ -126,10 +127,17 @@ int reset_head(struct repository *r, struct object_id *oid, const char *action, ret = create_symref("HEAD", switch_to_branch, reflog_head); } - if (run_hook) - run_hook_le(NULL, "post-checkout", - oid_to_hex(orig ? orig : &null_oid), - oid_to_hex(oid), "1", NULL); + if (run_hook) { + struct run_hooks_opt opt; + run_hooks_opt_init_sync(&opt); + strvec_pushl(&opt.args, + oid_to_hex(orig ? orig : &null_oid), + oid_to_hex(oid), + "1", + NULL); + run_hooks("post-checkout", &opt); + run_hooks_opt_clear(&opt); + } leave_reset_head: strbuf_release(&msg); From patchwork Thu Mar 11 02:10:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130127 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6A0B6C43331 for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3D5D264FCD for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230228AbhCKCMK (ORCPT ); Wed, 10 Mar 2021 21:12:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42352 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229865AbhCKCLj (ORCPT ); Wed, 10 Mar 2021 21:11:39 -0500 Received: from mail-qv1-xf4a.google.com (mail-qv1-xf4a.google.com [IPv6:2607:f8b0:4864:20::f4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B2951C061763 for ; Wed, 10 Mar 2021 18:11:39 -0800 (PST) Received: by mail-qv1-xf4a.google.com with SMTP id u17so14192666qvq.23 for ; Wed, 10 Mar 2021 18:11:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=lZ0f+BLxhi0jv9UwqIrAJNMyPRXCDqr/2LXvPk8gE10=; b=C9/OZlKRuzkolXZfe3MGALv1BTonByQTiYNlizmzzAk4zQEnAnWJ6CrzX1I6jkyPmr 7HhU9Gda4SRupyrpXiDMejhymlSkt9xIdNcXgZMJWyx6qr7e8rdF53TVunpHkthvyBGa W0xreBD7NGdncI8+034AX/6BWwxDthMD7dv+O5l+bBNqBdcRdI44Omp2O96j44jmQZDV M7YUgfHacz9BQQFwfLGaJzyhExxbz5gS75ZsIyjZTgmsTl78PUKk6MZVDo7rUOLwGrHV per4Jfv3ZsqEvw6KUzqa1n4kU7MV204i66RDfOWcMB/cIiyvLZ59JvCo6abobVJAadl3 2q/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=lZ0f+BLxhi0jv9UwqIrAJNMyPRXCDqr/2LXvPk8gE10=; b=YRbAJU50Id+ap5kujFVrZoxgiOjP+dNUQiWVeVUxUbQl/jqiTD2ojIP6qUPpxUbo9J 6J2HiNC+y3wiL3BkJBrNq8pbRgbl528Qy3jCINGFnrKYlXvwICkazN0RWABGprOMuzNo M2J7P1Y3gWuztXKNcjJSy+FI3bNIY5KGt1AOCbeZDIHo6hSjowPTjtniNOGUr6mh5BBM KpocoGCGlmb6NHzYree03ATGhcrfIkbqNUpGJEJ/fa27o4tfSrq/izSwkW+kHLvycIqw 3OvQPJcKzZcfHjFpanPT8WkQadohcc2VXMZ45pPAhgDmH+Wbr9nX1bduT4UkI2J+22U/ yg5w== X-Gm-Message-State: AOAM533BSK6bMuVFJNn3pCuqplNusBsPtaCYCXEBvEHvGZ93sRq6PPrx fZEay4HMZGN4HNDrOxMNWIrUcfTL+Gzl8viBcr7mxElzfGlpwPOiX6lCc/upiEgKWEJFxqTXEf6 6LSW6HcN2afUJsxty6KeiiB16xm87IaP3NkAk/JV/EjF2qz7zFfucGqucIZ9q/agUVnIt2duFQg == X-Google-Smtp-Source: ABdhPJwNyS+OfF9esIAclEqMCA2Ggwsma5sb+lzeAFAgqDhxhnmB0FL4mid+uty0jW4Yra4tn6Pyd/eRqevJO8Ad0b8= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:cb0c:: with SMTP id o12mr5674526qvk.54.1615428698884; Wed, 10 Mar 2021 18:11:38 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:27 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-28-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 27/37] hook: convert 'post-rewrite' hook to config From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using 'hook.h' for 'post-rewrite', we simplify hook invocations by not needing to put together our own 'struct child_process' and we also learn to run hooks specified in the config as well as the hook dir. The signal handling that's being removed by this commit now takes place in run-command.h:run_processes_parallel(), so it is OK to remove them here. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 ++ builtin/am.c | 19 +++------ sequencer.c | 83 +++++++++++++++++--------------------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 362224a03b..544238b381 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -594,6 +594,9 @@ The hook always runs after the automatic note copying (see "notes.rewrite." in linkgit:git-config[1]) has happened, and thus has access to these notes. +Hooks run during 'post-rewrite' will be run in parallel, unless hook.jobs is +configured to 1. + The following command-specific comments apply: rebase:: diff --git a/builtin/am.c b/builtin/am.c index 4467fd9e63..45425105e8 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -450,23 +450,16 @@ static int run_applypatch_msg_hook(struct am_state *state) */ static int run_post_rewrite_hook(const struct am_state *state) { - struct child_process cp = CHILD_PROCESS_INIT; - const char *hook = find_hook("post-rewrite"); + struct run_hooks_opt opt; int ret; + run_hooks_opt_init_async(&opt); - if (!hook) - return 0; - - strvec_push(&cp.args, hook); - strvec_push(&cp.args, "rebase"); - - cp.in = xopen(am_path(state, "rewritten"), O_RDONLY); - cp.stdout_to_stderr = 1; - cp.trace2_hook_name = "post-rewrite"; + strvec_push(&opt.args, "rebase"); + opt.path_to_stdin = am_path(state, "rewritten"); - ret = run_command(&cp); + ret = run_hooks("post-rewrite", &opt); - close(cp.in); + run_hooks_opt_clear(&opt); return ret; } diff --git a/sequencer.c b/sequencer.c index e3a951fbeb..8280ba828b 100644 --- a/sequencer.c +++ b/sequencer.c @@ -35,6 +35,7 @@ #include "rebase-interactive.h" #include "reset.h" #include "hook.h" +#include "string-list.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -1146,33 +1147,29 @@ int update_head_with_reflog(const struct commit *old_head, static int run_rewrite_hook(const struct object_id *oldoid, const struct object_id *newoid) { - struct child_process proc = CHILD_PROCESS_INIT; - const char *argv[3]; + struct run_hooks_opt opt; + struct strbuf tmp = STRBUF_INIT; + struct string_list to_stdin = STRING_LIST_INIT_DUP; int code; - struct strbuf sb = STRBUF_INIT; + run_hooks_opt_init_async(&opt); - argv[0] = find_hook("post-rewrite"); - if (!argv[0]) - return 0; + strvec_push(&opt.args, "amend"); - argv[1] = "amend"; - argv[2] = NULL; - - proc.argv = argv; - proc.in = -1; - proc.stdout_to_stderr = 1; - proc.trace2_hook_name = "post-rewrite"; - - code = start_command(&proc); - if (code) - return code; - strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid)); - sigchain_push(SIGPIPE, SIG_IGN); - write_in_full(proc.in, sb.buf, sb.len); - close(proc.in); - strbuf_release(&sb); - sigchain_pop(SIGPIPE); - return finish_command(&proc); + strbuf_addf(&tmp, + "%s %s", + oid_to_hex(oldoid), + oid_to_hex(newoid)); + string_list_append(&to_stdin, tmp.buf); + + opt.feed_pipe = pipe_from_string_list; + opt.feed_pipe_ctx = &to_stdin; + + code = run_hooks("post-rewrite", &opt); + + run_hooks_opt_clear(&opt); + strbuf_release(&tmp); + string_list_clear(&to_stdin, 0); + return code; } void commit_post_rewrite(struct repository *r, @@ -4325,30 +4322,22 @@ static int pick_commits(struct repository *r, flush_rewritten_pending(); if (!stat(rebase_path_rewritten_list(), &st) && st.st_size > 0) { - struct child_process child = CHILD_PROCESS_INIT; - const char *post_rewrite_hook = - find_hook("post-rewrite"); - - child.in = open(rebase_path_rewritten_list(), O_RDONLY); - child.git_cmd = 1; - strvec_push(&child.args, "notes"); - strvec_push(&child.args, "copy"); - strvec_push(&child.args, "--for-rewrite=rebase"); + struct child_process notes_cp = CHILD_PROCESS_INIT; + struct run_hooks_opt hook_opt; + run_hooks_opt_init_async(&hook_opt); + + notes_cp.in = open(rebase_path_rewritten_list(), O_RDONLY); + notes_cp.git_cmd = 1; + strvec_push(¬es_cp.args, "notes"); + strvec_push(¬es_cp.args, "copy"); + strvec_push(¬es_cp.args, "--for-rewrite=rebase"); /* we don't care if this copying failed */ - run_command(&child); - - if (post_rewrite_hook) { - struct child_process hook = CHILD_PROCESS_INIT; - - hook.in = open(rebase_path_rewritten_list(), - O_RDONLY); - hook.stdout_to_stderr = 1; - hook.trace2_hook_name = "post-rewrite"; - strvec_push(&hook.args, post_rewrite_hook); - strvec_push(&hook.args, "rebase"); - /* we don't care if this hook failed */ - run_command(&hook); - } + run_command(¬es_cp); + + hook_opt.path_to_stdin = rebase_path_rewritten_list(); + strvec_push(&hook_opt.args, "rebase"); + run_hooks("post-rewrite", &hook_opt); + run_hooks_opt_clear(&hook_opt); } apply_autostash(rebase_path_autostash()); From patchwork Thu Mar 11 02:10:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130125 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D973C4332E for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 02E1064FDC for ; Thu, 11 Mar 2021 02:12:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230319AbhCKCML (ORCPT ); Wed, 10 Mar 2021 21:12:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42350 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229929AbhCKCLl (ORCPT ); Wed, 10 Mar 2021 21:11:41 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6E623C061574 for ; Wed, 10 Mar 2021 18:11:41 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id v196so23762077ybv.3 for ; Wed, 10 Mar 2021 18:11:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=fdrNjGSbgR3/k0inoRDYCwzDeCzZNtFZVESEGgQH1Xs=; b=en5T94YvrFd3luglCzx3sqWtRmXZjDONjL3gYZR6AwNlaIymTcl6iMn0qKU+bKEE6b ABYHQ4sqpa0VqIg22UtPCCiSVH+N8G+amx5+THqsUEzxMG4lPrmxarw16fDcvw6tR+RI TjAZJGCzZsxPo0uXBOkd3Hj2XO98Y7ca7Nn2EwWddpBcS/rESV1IpFASkfXYvqvpx9Ec S0Wd+uUFS1TI0fJ3EpesGegaLWkP4BurJhun74aYFmFzrKr/xJGdIC4fDwNPed4SzVNb tp0lyrh6mVxBPveQy+Rsom1+ZASN8R6ALr5yikeHYQ8eGfaWgMEa30FJcfMTckf0XuZu 9EGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=fdrNjGSbgR3/k0inoRDYCwzDeCzZNtFZVESEGgQH1Xs=; b=NgahSYvvEu2yMxEcQ74otSlwhXsDC7p2HbkUWc5OF6t/SuOiwkrDU3tYI587D2FTA6 6+cQt/TiRD49HQy+5fFw6nVEYNw+9+0B7qGc+hBOX1q2x2S0CeWZIdWKQS68GuFu1/aF a1fnF4EKQbQybBx+MUDPIF+OfzDzDDrd6N2whYaCi1s+N4z00FFC/0ggGC1emPX2a+vI kzi2WJZf2o9hFHxS5KAvtUn1c69OUegyeAdWo476ff1Dg7/fEruTuWsoRKVRebg6HJwH F/qIhSQ5S7e+0fnUVBTNbQD7qUJ8yaAy2k8SwtZ5Vllf0EYD1jkBrRC7p4kxW78jGcey TuZw== X-Gm-Message-State: AOAM530/wDCi0FbIUzEHc8IZ/MhgQ9E1UTye73NYcZHpQ7Rgu8DWV1ro PuwbZvsB8yPuW84Pwm/q/wTqujTRY0OBamXEGqnzPo/GYJOhVLMpM/7hcQB6VIWQI0ET2uq6196 xH0s7ZUZV/+OcEpCgESVxd7D7EM9QTzWvQz2nHro9Iq0u4SEuSZPW/psJhXY1kc43VLhF7NDkmA == X-Google-Smtp-Source: ABdhPJxdjWVHYhNA2NaCsc2nFA+AA5VxGb3ebkhUa47Q5mtsjGH/t6pdVzgeHS/hzfCCLI7q4GIqpU0kII/PRiAZQbY= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:d251:: with SMTP id j78mr7804221ybg.480.1615428700686; Wed, 10 Mar 2021 18:11:40 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:28 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-29-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 28/37] transport: convert pre-push hook to use config From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using the hook.h:run_hooks API, pre-push hooks can be specified in the config as well as in the hookdir. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 ++ transport.c | 59 +++++++++++--------------------------- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 544238b381..489c93a7cb 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -279,6 +279,9 @@ If this hook exits with a non-zero status, `git push` will abort without pushing anything. Information about why the push is rejected may be sent to the user by writing to standard error. +Hooks executed during 'pre-push' will run in parallel, unless hook.jobs is +configured to 1. + [[pre-receive]] pre-receive ~~~~~~~~~~~ diff --git a/transport.c b/transport.c index b13fab5dc3..286b73881b 100644 --- a/transport.c +++ b/transport.c @@ -22,6 +22,7 @@ #include "protocol.h" #include "object-store.h" #include "color.h" +#include "hook.h" static int transport_use_color = -1; static char transport_colors[][COLOR_MAXLEN] = { @@ -1172,31 +1173,15 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing) static int run_pre_push_hook(struct transport *transport, struct ref *remote_refs) { - int ret = 0, x; + int ret = 0; + struct run_hooks_opt opt; + struct strbuf tmp = STRBUF_INIT; struct ref *r; - struct child_process proc = CHILD_PROCESS_INIT; - struct strbuf buf; - const char *argv[4]; - - if (!(argv[0] = find_hook("pre-push"))) - return 0; - - argv[1] = transport->remote->name; - argv[2] = transport->url; - argv[3] = NULL; - - proc.argv = argv; - proc.in = -1; - proc.trace2_hook_name = "pre-push"; - - if (start_command(&proc)) { - finish_command(&proc); - return -1; - } - - sigchain_push(SIGPIPE, SIG_IGN); + struct string_list to_stdin = STRING_LIST_INIT_DUP; + run_hooks_opt_init_async(&opt); - strbuf_init(&buf, 256); + strvec_push(&opt.args, transport->remote->name); + strvec_push(&opt.args, transport->url); for (r = remote_refs; r; r = r->next) { if (!r->peer_ref) continue; @@ -1205,30 +1190,20 @@ static int run_pre_push_hook(struct transport *transport, if (r->status == REF_STATUS_REJECT_REMOTE_UPDATED) continue; if (r->status == REF_STATUS_UPTODATE) continue; - strbuf_reset(&buf); - strbuf_addf( &buf, "%s %s %s %s\n", + strbuf_reset(&tmp); + strbuf_addf(&tmp, "%s %s %s %s", r->peer_ref->name, oid_to_hex(&r->new_oid), r->name, oid_to_hex(&r->old_oid)); - - if (write_in_full(proc.in, buf.buf, buf.len) < 0) { - /* We do not mind if a hook does not read all refs. */ - if (errno != EPIPE) - ret = -1; - break; - } + string_list_append(&to_stdin, tmp.buf); } - strbuf_release(&buf); - - x = close(proc.in); - if (!ret) - ret = x; - - sigchain_pop(SIGPIPE); + opt.feed_pipe = pipe_from_string_list; + opt.feed_pipe_ctx = &to_stdin; - x = finish_command(&proc); - if (!ret) - ret = x; + ret = run_hooks("pre-push", &opt); + run_hooks_opt_clear(&opt); + strbuf_release(&tmp); + string_list_clear(&to_stdin, 0); return ret; } From patchwork Thu Mar 11 02:10:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130123 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 02AF7C4332B for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C8EF464FD8 for ; Thu, 11 Mar 2021 02:12:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230391AbhCKCMM (ORCPT ); Wed, 10 Mar 2021 21:12:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42368 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229810AbhCKCLn (ORCPT ); Wed, 10 Mar 2021 21:11:43 -0500 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 45BD1C061574 for ; Wed, 10 Mar 2021 18:11:43 -0800 (PST) Received: by mail-qk1-x74a.google.com with SMTP id o70so14289150qke.16 for ; Wed, 10 Mar 2021 18:11:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=GjM4mAmp+AKf53XGmy31JDt5g6a1N4hK21N+qPDtk6E=; b=v8DNhrvGKrntreeJWV2EUg8qZHi+nJy3wbznZr/vDrx8sLvc37CtTtgg5AGSp6AJz/ PZ5LdMwJR6B5C6oo28pAKOMbNGwetXB5dolWoDOxGt946Cg3KCaDIpnZjRSCLmXiraLi hIDbu+diDW8p5vGR3INrkKn0DReUFmHoIILuQy+my9UIs0b7mV/zmDVJX6/YllVdm1du x8CbKPH91VLzsR7ZPSY2iTWYaibmKrBmZvJm3E+auC+keN60792MCyuRuLUj0an1AKLf mJGWwEnWuQJhwVH1EgobCLcqyGH6WMZseZlYgz4yPY84rvZFURY6RLZdx5bTNK20oWyJ koFA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=GjM4mAmp+AKf53XGmy31JDt5g6a1N4hK21N+qPDtk6E=; b=dPt4+lhV9bnMsOPpJmXXDfpjJU6uLd1F/Y6HKHBkCaXUxhC9xaHzNgXznJMFmhRojt qODk6enZUt2MnRi5bGFtWJGC8+8uOe/jPW+ShGp4EOXQBU2DxVXf+BxegGKJX4ZtveNH 8vJnYNkzPAbxG2RPzr378M5zOcIlAdbXu/8k+rVh0TZIjI6U1WldswBbQeRIk0+cKBq7 h+hXb+4bpaSWmU/NYG3N8FsGKp7Q1Zrs0aaRnUfajKK9e+ONV56BGIR3Hnk6i1f4+WV9 RPPyokxm9CAoG/BRn+jTbOWnF61MzgfBZwpxgWXxQy8edlFg+oagYOawkGePgMUfAA3E J6yQ== X-Gm-Message-State: AOAM533jNZYcjwmyW3+slZsGb0D4w2HrkrE+wvlMH5pIvYLUUYHFmQZv Moarh3/tRiegb838X1A3rOPmXz2fsSLqDvfhpiq+lBLFGjtPNdbKNvl4EqHds2OtjHTc7Ic5Gcx YLntuZrAv8Za2E7QS5lt8qVT1ez9JffD1Jdn24An048IM5WyOqYbdjUH2R4Euni15lWf2Y1EClg == X-Google-Smtp-Source: ABdhPJwmsb+/NrbVHWNd5uGyftZ18fpIykQ4kRyLJdsRzjKym1lJQ3OG+TMs5HZsZqITG3M9/F81H5xMvpmBYHe0jBs= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:20a1:: with SMTP id 1mr5957436qvd.30.1615428702475; Wed, 10 Mar 2021 18:11:42 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:29 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-30-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 29/37] reference-transaction: look for hooks in config From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using the hook.h library, reference-transaction hooks can be specified in the config instead. The expected output of the test is not fully updated to reflect the absolute path of the hook called because the 'update' hook has not yet been converted to use hook.h. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 +++ refs.c | 43 +++++++++++++------------------- t/t1416-ref-transaction-hooks.sh | 8 +++--- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 489c93a7cb..dc8b7111d5 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -530,6 +530,9 @@ The exit status of the hook is ignored for any state except for the cause the transaction to be aborted. The hook will not be called with "aborted" state in that case. +Hooks run during 'reference-transaction' will be run in parallel, unless +hook.jobs is configured to 1. + push-to-checkout ~~~~~~~~~~~~~~~~ diff --git a/refs.c b/refs.c index a665ed5e10..4fccbac3e6 100644 --- a/refs.c +++ b/refs.c @@ -18,6 +18,7 @@ #include "strvec.h" #include "repository.h" #include "sigchain.h" +#include "hook.h" /* * List of all available backends @@ -2061,47 +2062,37 @@ int ref_update_reject_duplicates(struct string_list *refnames, static int run_transaction_hook(struct ref_transaction *transaction, const char *state) { - struct child_process proc = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; - const char *hook; + struct run_hooks_opt opt; + struct string_list to_stdin = STRING_LIST_INIT_DUP; int ret = 0, i; + char o[GIT_MAX_HEXSZ + 1], n[GIT_MAX_HEXSZ + 1]; - hook = find_hook("reference-transaction"); - if (!hook) - return ret; - - strvec_pushl(&proc.args, hook, state, NULL); - proc.in = -1; - proc.stdout_to_stderr = 1; - proc.trace2_hook_name = "reference-transaction"; + run_hooks_opt_init_async(&opt); - ret = start_command(&proc); - if (ret) + if (!hook_exists("reference-transaction", HOOKDIR_USE_CONFIG)) return ret; - sigchain_push(SIGPIPE, SIG_IGN); + strvec_push(&opt.args, state); for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + oid_to_hex_r(o, &update->old_oid); + oid_to_hex_r(n, &update->new_oid); strbuf_reset(&buf); - strbuf_addf(&buf, "%s %s %s\n", - oid_to_hex(&update->old_oid), - oid_to_hex(&update->new_oid), - update->refname); - - if (write_in_full(proc.in, buf.buf, buf.len) < 0) { - if (errno != EPIPE) - ret = -1; - break; - } + strbuf_addf(&buf, "%s %s %s", o, n, update->refname); + string_list_append(&to_stdin, buf.buf); } - close(proc.in); - sigchain_pop(SIGPIPE); + opt.feed_pipe = pipe_from_string_list; + opt.feed_pipe_ctx = &to_stdin; + + ret = run_hooks("reference-transaction", &opt); + run_hooks_opt_clear(&opt); strbuf_release(&buf); + string_list_clear(&to_stdin, 0); - ret |= finish_command(&proc); return ret; } diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index 6c941027a8..3a90a59143 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -125,11 +125,11 @@ test_expect_success 'interleaving hook calls succeed' ' cat >expect <<-EOF && hooks/update refs/tags/PRE $ZERO_OID $PRE_OID - hooks/reference-transaction prepared - hooks/reference-transaction committed + $(pwd)/target-repo.git/hooks/reference-transaction prepared + $(pwd)/target-repo.git/hooks/reference-transaction committed hooks/update refs/tags/POST $ZERO_OID $POST_OID - hooks/reference-transaction prepared - hooks/reference-transaction committed + $(pwd)/target-repo.git/hooks/reference-transaction prepared + $(pwd)/target-repo.git/hooks/reference-transaction committed EOF git push ./target-repo.git PRE POST && From patchwork Thu Mar 11 02:10:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130129 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9F954C43603 for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8601864FCC for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230450AbhCKCMP (ORCPT ); Wed, 10 Mar 2021 21:12:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42372 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230052AbhCKCLp (ORCPT ); Wed, 10 Mar 2021 21:11:45 -0500 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 25A3BC061574 for ; Wed, 10 Mar 2021 18:11:45 -0800 (PST) Received: by mail-qk1-x749.google.com with SMTP id h126so14302890qkd.4 for ; Wed, 10 Mar 2021 18:11:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=BdbSVFpR8MOs/iFa2drop73T1SvXVSje6F7PUX2Ry3c=; b=JdsgtcB55c9i+rq13jWWR8IJCaQDnm5YhOIc+eWIRkJmbKyeHCXnPWJKy7n7dCouhK V3mXPAEu3GlFW2lQGLHOhzx+Y4jt5WKtRIeKKapjTmbMvjQfDaw3o00Wc+pwSSPHO3Vl mWxZXBurvUMiFWKTvfrTpcIslkCZJIEqKV6avmECTlOcfZCsAg+FnIXHM3nDmi/dmL18 jwM+IsbLSqHVFYazocfG8+1OSpufuaJIsojpc3V2gv4QIAO/ep45cafBEL9N6IBaeOwE AHo4sI0PiEO6HJMUbqnxWo4nTxKEZGnmPIqhAif14yYoeig2EIg8Y6YMDthmXbNwDZg3 BQHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=BdbSVFpR8MOs/iFa2drop73T1SvXVSje6F7PUX2Ry3c=; b=mer/PROMD0JJ8pD09JjdYj5wN0OSyb2SqmudivaUV+OKBmXkexZdDAP9v5PQ9u57as KjbrLI4aLoOJ3tB0TLoTVqSlljNVmV9BLZ7aVXwku7WNfehzrt9A/aYUibls0vluaibV c173y8Z8jsZWm+/LSTVlfEQAPgFhwSZ+TBWMMWUVDZHqqKvb7gEZnkauPzPRz45E7AYs MqTpJjtv1mS3LMBOUbPAO1UzdYXXJ75lEgqt8A7YdALDOlWIAVBlZ3r3s7QC9qzSXoRC ugvOjVzFRnSITx++g8FoPdHw9C31+7Ak36flLsAGiofhd9br+nWCrZ7a0VS7l30XdP0y uo1Q== X-Gm-Message-State: AOAM531MJp7K3S0nOmvThXyUnbl2urzvqSWH4+c8bv2NEzW0h6NkHzv5 XdzIJEqV9DEKUxwul0SZKd/hNFUL+4p/Cp+KGfTi78nlSWXfSjHyhTh4u5bhWsrWXfMmPMfdGCX ok3X8jPXna0d6z/SokEwXegL+5qOChFYuMItGw3/0zR8t3IrsW2tbPTr8WHwudSZ6cNYZXi9m6g == X-Google-Smtp-Source: ABdhPJz2MR1LNB1YP+Emhtd4azm57PdVWGdWUnoHwrPjbHUwIzpQR417HbJQGZE/f03wunPGczZ5Gtf/SxcQKvZpkaY= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a0c:b611:: with SMTP id f17mr5907300qve.42.1615428704263; Wed, 10 Mar 2021 18:11:44 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:30 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-31-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 30/37] receive-pack: convert 'update' hook to hook.h From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using hook.h to invoke the 'update' hook, now hooks can be specified in the config in addition to the hookdir. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 ++ builtin/receive-pack.c | 66 ++++++++++++++++++++++---------- t/t1416-ref-transaction-hooks.sh | 4 +- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index dc8b7111d5..60fd43d1da 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -368,6 +368,9 @@ The default 'update' hook, when enabled--and with `hooks.allowunannotated` config option unset or set to false--prevents unannotated tags to be pushed. +Hooks executed during 'update' are run in parallel, unless hook.jobs is +configured to 1. + [[proc-receive]] proc-receive ~~~~~~~~~~~~ diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 234b70f0d1..b34a27a303 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -938,33 +938,57 @@ static int run_receive_hook(struct command *commands, return status; } -static int run_update_hook(struct command *cmd) +static void hook_output_to_sideband(struct strbuf *output, void *cb_data) { - const char *argv[5]; - struct child_process proc = CHILD_PROCESS_INIT; - int code; + int keepalive_active = 0; - argv[0] = find_hook("update"); - if (!argv[0]) - return 0; + if (keepalive_in_sec <= 0) + use_keepalive = KEEPALIVE_NEVER; + if (use_keepalive == KEEPALIVE_ALWAYS) + keepalive_active = 1; - argv[1] = cmd->ref_name; - argv[2] = oid_to_hex(&cmd->old_oid); - argv[3] = oid_to_hex(&cmd->new_oid); - argv[4] = NULL; + /* send a keepalive if there is no data to write */ + if (keepalive_active && !output->len) { + static const char buf[] = "0005\1"; + write_or_die(1, buf, sizeof(buf) - 1); + return; + } - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.argv = argv; - proc.trace2_hook_name = "update"; + if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) { + const char *first_null = memchr(output->buf, '\0', output->len); + if (first_null) { + /* The null bit is excluded. */ + size_t before_null = first_null - output->buf; + size_t after_null = output->len - (before_null + 1); + keepalive_active = 1; + send_sideband(1, 2, output->buf, before_null, use_sideband); + send_sideband(1, 2, first_null + 1, after_null, use_sideband); + + return; + } + } + + send_sideband(1, 2, output->buf, output->len, use_sideband); +} + +static int run_update_hook(struct command *cmd) +{ + struct run_hooks_opt opt; + int code; + run_hooks_opt_init_async(&opt); + + strvec_pushl(&opt.args, + cmd->ref_name, + oid_to_hex(&cmd->old_oid), + oid_to_hex(&cmd->new_oid), + NULL); - code = start_command(&proc); - if (code) - return code; if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - return finish_command(&proc); + opt.consume_sideband = hook_output_to_sideband; + + code = run_hooks("update", &opt); + run_hooks_opt_clear(&opt); + return code; } static struct command *find_command_by_refname(struct command *list, diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index 3a90a59143..0a3c3e4a86 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -124,10 +124,10 @@ test_expect_success 'interleaving hook calls succeed' ' EOF cat >expect <<-EOF && - hooks/update refs/tags/PRE $ZERO_OID $PRE_OID + $(pwd)/target-repo.git/hooks/update refs/tags/PRE $ZERO_OID $PRE_OID $(pwd)/target-repo.git/hooks/reference-transaction prepared $(pwd)/target-repo.git/hooks/reference-transaction committed - hooks/update refs/tags/POST $ZERO_OID $POST_OID + $(pwd)/target-repo.git/hooks/update refs/tags/POST $ZERO_OID $POST_OID $(pwd)/target-repo.git/hooks/reference-transaction prepared $(pwd)/target-repo.git/hooks/reference-transaction committed EOF From patchwork Thu Mar 11 02:10:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130137 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E872BC43619 for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C93B964F95 for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230144AbhCKCMQ (ORCPT ); Wed, 10 Mar 2021 21:12:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42380 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230063AbhCKCLr (ORCPT ); Wed, 10 Mar 2021 21:11:47 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D8226C061574 for ; Wed, 10 Mar 2021 18:11:46 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id q77so23901209ybq.0 for ; Wed, 10 Mar 2021 18:11:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=wv0bagwdGF+ihtw9M+5GyHkA/IifsGG8RsRqefVeV7c=; b=TDrcpKxVqDjbM6bciyTAJDnSTpjTJ8Q0Hl7hECUnuHdvVgbRZts6VWw5d7x5D8pCKX 8IzFUSksj4ayQSXIldJMZVCt5VcPbdPalJRPyfOCGpo7KumokmbIcThwqefiUMMmW4CN dOQG4Rf7j7sdUdNMdo0ZJ/vsclvNKr7evMjzqBtjTdIjxIhGW8yBvYIgTnf/6RTJxiNx 5y3TZXrTWrjMCdLuGMNtXFGttePZhQa/1WNg8sOIgvWRFbNjA5H9RE+pXJJCQeLiwXK7 2XTzGrL7PeLOdhplUilOJlVpPO9TAeNpyDjzx/jg/RUoATooB1D43LogAGYKGZssxdcF pINw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=wv0bagwdGF+ihtw9M+5GyHkA/IifsGG8RsRqefVeV7c=; b=Rjxy1yxoy9Wth2VVKtppDqcyrr8rNwd+1biqbpRL8bE+yB+HXakF/zaAckAZVt/qCx TKbjJuvjoSgWXggTZERPkkv9PmuWucId04Uyj99W7KFDv6Q13vd2pBUXwNGsiFoNMBwf ZTc0HlCoVGLx5C57ksYfqaSKA7wHVlc7hWI+bGDUrwiFXF4m4qBoZFDuYMEtD+leJnkv ts4bZoJ2rBaqJpbxmxaOljjcQ6SFgXFt/s313vTqBWRihesxdTR/CxiN8W9sf30vMXDE W7lvVlWmqTKRs2X0doAvWWBSoZDLYyell5PqhI2ggOW3XADvdJGckJXUe6pjHoxZsrb8 QDvg== X-Gm-Message-State: AOAM530JFbwcRy4CStmWklJ/4nEzJUdObKYlXpaKaDoa//wrhKvHlmc9 kfzha73EXCejcWcIeS1+3heoY3mR4SEHO0y23kI9LSW6EsebsK8K20esNYZR+b8UispRUqGrP/0 S8gEOrKUbVi2NrbljVqg2V089iTN3mmhhw3nBtkcbyaqohTdOWBLMw8Ft4oe9TeyhCfJ5m7fOJQ == X-Google-Smtp-Source: ABdhPJzC92YdYxUPJg0lPdFdosmCSai05FiBsPlD2ZAPCfmsYeI8RXc94sxT5fUgG8roM4NFMxhPSmpMq8I+q9E1gJ0= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6902:6ac:: with SMTP id j12mr8190920ybt.440.1615428706072; Wed, 10 Mar 2021 18:11:46 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:31 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-32-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 31/37] proc-receive: acquire hook list from hook.h From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org The proc-receive hook differs from most other hooks Git invokes because the hook and the parent Git process engage in bidirectional communication via stdin/stdout. This bidirectional communication is unsuitable for multiple hooks, whether they are in series or in parallel, and is incompatible with run-command.h:run_processes_parallel: - The proc-receive hook is intended to modify the state of the Git repo. From 'git help githooks': This [proc-receive] hook is responsible for updating the relevant references and reporting the results back to 'receive-pack'. This prevents parallelization and implies, at least, specific ordering of hook execution. - The proc-receive hook can reject a push by aborting early with an error code. If a former hook ran through the entire push contents successfully but a later hook rejects some of the push, the repo may be left in a partially-updated (and corrupt) state. - The callback model of the run_processes_parallel() API is unsuited to the current implementation of proc-receive, which loops through "send-receive-consider" with the child process. proc-receive today relies on stateful communication with the child process, which would be unwieldy to implement with callbacks and saved state. - Additionally, run_processes_parallel() is designed to collate the output of many child processes into a single output (stderr or callback), and would require significant work to tell the caller which process sent the output, and indeed to collect any output before the child process has exited. So, rather than using hook.h:run_hooks() to invoke the proc-receive hook, receive-pack.c can learn to ask hook.h:hook_list() for the location of a hook to run. This allows users to configure their proc-receive in a global config for all repos if they want, or a local config if they just don't want to use the hookdir. Because running more than one proc-receive hook doesn't make sense from a repo state perspective, we can explicitly ban configuring more than one proc-receive hook at a time. If a user wants to globally configure one proc-receive hook for most of their repos, but override that hook in a single repo, they should use 'skip' to manually remove the global hook in their special repo: ~/.gitconfig: [hook.proc-receive] command = /usr/bin/usual-proc-receive ~/special-repo/.git/config: [hookcmd./usr/bin/usual-proc-receive] skip = true [hook.proc-receive] command = /usr/bin/special-proc-receive Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 4 ++ builtin/receive-pack.c | 33 +++++++++++++++- t/t5411/test-0015-too-many-hooks-error.sh | 47 +++++++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 t/t5411/test-0015-too-many-hooks-error.sh diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 60fd43d1da..c16353be2d 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -433,6 +433,10 @@ the input. The exit status of the 'proc-receive' hook only determines the success or failure of the group of commands sent to it, unless atomic push is in use. +It is forbidden to specify more than one hook for 'proc-receive'. If a +globally-configured 'proc-receive' must be overridden, use +'hookcmd..skip = true' to ignore it. + [[post-receive]] post-receive ~~~~~~~~~~~~ diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index b34a27a303..e448956a32 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1146,11 +1146,40 @@ static int run_proc_receive_hook(struct command *commands, int version = 0; int code; - argv[0] = find_hook("proc-receive"); - if (!argv[0]) { + struct strbuf hookname = STRBUF_INIT; + struct hook *proc_receive = NULL; + struct list_head *pos, *hooks; + + strbuf_addstr(&hookname, "proc-receive"); + hooks = hook_list(&hookname); + + list_for_each(pos, hooks) { + if (proc_receive) { + rp_error("only one 'proc-receive' hook can be specified"); + return -1; + } + proc_receive = list_entry(pos, struct hook, list); + /* check if the hookdir hook should be ignored */ + if (proc_receive->from_hookdir) { + switch (configured_hookdir_opt()) { + case HOOKDIR_INTERACTIVE: + case HOOKDIR_NO: + proc_receive = NULL; + break; + default: + break; + } + } + + } + + if (!proc_receive) { rp_error("cannot find hook 'proc-receive'"); return -1; } + + + argv[0] = proc_receive->command.buf; argv[1] = NULL; proc.argv = argv; diff --git a/t/t5411/test-0015-too-many-hooks-error.sh b/t/t5411/test-0015-too-many-hooks-error.sh new file mode 100644 index 0000000000..2d64534510 --- /dev/null +++ b/t/t5411/test-0015-too-many-hooks-error.sh @@ -0,0 +1,47 @@ +test_expect_success "setup too many proc-receive hooks (ok, $PROTOCOL)" ' + write_script "proc-receive" <<-EOF && + printf >&2 "# proc-receive hook\n" + test-tool proc-receive -v \ + -r "ok refs/for/main/topic" + EOF + + git -C "$upstream" config --add "hook.proc-receive.command" proc-receive && + cp proc-receive "$upstream/hooks/proc-receive" +' + +# Refs of upstream : main(A) +# Refs of workbench: main(A) tags/v123 +# git push : next(A) refs/for/main/topic(A) +test_expect_success "proc-receive: reject more than one configured hook" ' + test_must_fail git -C workbench push origin \ + HEAD:next \ + HEAD:refs/for/main/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/main/topic + remote: error: only one "proc-receive" hook can be specified + remote: # post-receive hook + remote: post-receive< refs/heads/next + To + * [new branch] HEAD -> next + ! [remote rejected] HEAD -> refs/for/main/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/main + refs/heads/next + EOF + test_cmp expect actual +' + +# Refs of upstream : main(A) next(A) +# Refs of workbench: main(A) tags/v123 +test_expect_success "cleanup ($PROTOCOL)" ' + git -C "$upstream" config --unset "hook.proc-receive.command" "proc-receive" && + git -C "$upstream" update-ref -d refs/heads/next +' From patchwork Thu Mar 11 02:10:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130135 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CAE76C4321A for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B247964FCC for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230468AbhCKCMS (ORCPT ); Wed, 10 Mar 2021 21:12:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42390 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230087AbhCKCLs (ORCPT ); Wed, 10 Mar 2021 21:11:48 -0500 Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A8D20C061574 for ; Wed, 10 Mar 2021 18:11:48 -0800 (PST) Received: by mail-qt1-x84a.google.com with SMTP id r32so7280734qtd.16 for ; Wed, 10 Mar 2021 18:11:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=P0a9kFDCbvmkpbEruyPjdiAtGPBAH5p8rlp9ObW9PVM=; b=uyS57M2JB5CB/sTVCded4xP6+HmQWBeIzyUdTgf7Dxd6co6A4p4pHKN/K9crrmmJvF bv8FcynbeoiidaLoCmU5u21W/3r15Q6o2LXRS/xARGIiMrxNllZ4t0a7GB1aeehVpAOF LTCiFwCaTMmboKNGn0W9oWqrtGQpnd4QhC/JApInsxSMv5wxdrIlcTh7u4jrUDQ4T3bp aZka+TkCUCGw34/25J+QXfrnz5FNCPk4lTIUTZ4KrBLgLRo2VVC+Q0ycm8gEsZ+3uIjY vMdXRbxiVsIrBRTA2Gtjnrr/OrhJ8C+F7GMLeaqE/+lnCZSsdrt8TEbA48e0CAdYh8Uh 4Huw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=P0a9kFDCbvmkpbEruyPjdiAtGPBAH5p8rlp9ObW9PVM=; b=fe2M7N6gnZlypQbcP1Ru+IbHYAMF27Ht+zJ2RmyYTPEIrN3AClFGwm2ArYO+gnguMO g3V2b+ptDzCWl/tGCjKwiSfSUdP8KXTJ5YAJ+zSUktMZa5LB3cgTjYu9dhcV3K1B4q2g ZP3E1dO3mtAN+YIDxv8B8txke9glAm6xpUTdi57E2BAY7+5AYw5PTJQ4LsnRMnipM9Nx NEHT6NgYUoE1iUHZLNQNSxJ34/S/sZekjlluNiTPF6g1Xcq2Sp0qxzGJH/Mcb0PkrYTc PObXPiWUtdC+wIzGBfJ7EaoiFXknz3RAOsg00qVG4JtMbd+aOpBoFjntUQl44ZBoJG25 kngA== X-Gm-Message-State: AOAM530MfMgqvHxK0e1me2A/OCT84XFVxYNG1E7Kt4jbedFH/vpiLZl6 9Hg7P+Xx03rmWaRA6/FfUVib/KgoEx5APaL7lRsfCm9mq1FO/4b/1r4q1iwSZmgZ6LQ4+511tl7 pL7SWj/BaTkYaFTVsEXTI/kDk4xMdQvDM6xOO646+fNfeYqIG8gt1w+Uhou/04HIcjDwyUBxLeg == X-Google-Smtp-Source: ABdhPJwRiewWchP5Y0an98SMemhLWTH1uQcD8QEU/cSnjPQVUI3lIWNPAtsynHDs0FU76o+Jy6N6xwQgy609YLWaEtg= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:f6d:: with SMTP id iy13mr5995788qvb.24.1615428707877; Wed, 10 Mar 2021 18:11:47 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:32 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-33-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 32/37] post-update: use hook.h library From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using run_hooks() instead of run_hook_le(), 'post-update' hooks can be specified in the config as well as the hookdir. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 3 +++ builtin/receive-pack.c | 27 ++++++++------------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index c16353be2d..fe5381b95b 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -508,6 +508,9 @@ Both standard output and standard error output are forwarded to `git send-pack` on the other end, so you can simply `echo` messages for the user. +Hooks run during 'post-update' will be run in parallel, unless hook.jobs is +configured to 1. + reference-transaction ~~~~~~~~~~~~~~~~~~~~~ diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index e448956a32..955efbdf6d 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1688,33 +1688,22 @@ static const char *update(struct command *cmd, struct shallow_info *si) static void run_update_post_hook(struct command *commands) { struct command *cmd; - struct child_process proc = CHILD_PROCESS_INIT; - const char *hook; - - hook = find_hook("post-update"); - if (!hook) - return; + struct run_hooks_opt opt; + run_hooks_opt_init_async(&opt); for (cmd = commands; cmd; cmd = cmd->next) { if (cmd->error_string || cmd->did_not_exist) continue; - if (!proc.args.nr) - strvec_push(&proc.args, hook); - strvec_push(&proc.args, cmd->ref_name); + strvec_push(&opt.args, cmd->ref_name); } - if (!proc.args.nr) + if (!opt.args.nr) return; - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.trace2_hook_name = "post-update"; + if (use_sideband) + opt.consume_sideband = hook_output_to_sideband; - if (!start_command(&proc)) { - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - finish_command(&proc); - } + run_hooks("post-update", &opt); + run_hooks_opt_clear(&opt); } static void check_aliased_update_internal(struct command *cmd, From patchwork Thu Mar 11 02:10:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130141 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15774C4361A for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E912064FFA for ; Thu, 11 Mar 2021 02:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230478AbhCKCMS (ORCPT ); Wed, 10 Mar 2021 21:12:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42398 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230116AbhCKCLu (ORCPT ); Wed, 10 Mar 2021 21:11:50 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8AF8EC061574 for ; Wed, 10 Mar 2021 18:11:50 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id p136so23934893ybc.21 for ; Wed, 10 Mar 2021 18:11:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=w5SSUoVVGqgBkMc8TDD4aV9hh+m7Z/9W4tm1YCfjE6U=; b=MTYOZrLnmuTjhtHmD5kQNtRCYLT4YzbNbz7EyvGUEYP/Ux6S/5aWPMA4x6zW4IlOMz tipBSXpK4tNwsgqmElZXPJnT+H2HtpeSIWYojUIX19mURjdGl57klPKEhnV93d4PdYoA ZbkNZsEB2GmzXCYr/LyRvt/cVl96s0ABqLW0F/2nlHbdmofmcRpPQGpsAyC27rcAfoaC ox6JFCUk3ByVSzdtse5Ij8A97de6c/f01BRiuFwlB8AvpHEGAaGYHLWEOF4OVndhaBXY RGW2/j99Swb4z4JxONOvQLF2KDEUY13kTTpRLIV1olaQPIAjeJeOn1e+a5E9M/Lzvwyn 0sQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=w5SSUoVVGqgBkMc8TDD4aV9hh+m7Z/9W4tm1YCfjE6U=; b=fQ/y6Yr1VUioEqNWv877nTN3vOgEQ8mJMpPV+TntavKEWUmn8w1Q7K1/7QYWpYy0Qi o2pSoJeOVBY/HqrxiMLFLabfUhovSHVr9UExHG2FlCtsm6z9XEIqaPjd3Uyts8VIs+A1 X9Gv5SjYqKtXQ+q4IRXixnhdV4XHbgUutNjX767X921jIDX0ZgLzRKObG++NKZgTGq4f PzPe6LKsNhCKEOB7MdPk8sPFz+1PFEE+XNP5jlB6wNOLmIPB7k34FcuJuSZvVabNC9LE kB0FzBJprTQTbyXoQWaBHKVrY/hTsx5sro5rYPP8oXFW49w5WJdB7PohnMIAaMKXCEJd sfpQ== X-Gm-Message-State: AOAM531JNaMBofFFsjnxTrSNPfiSfGb77NFqX+w/icvXnVMdEdGgOc4y LPTgKJh+KR6/ZaE98wQlJp84Tc3ORqxYmjk2uFaE7DjHIUBPk+VLmo65/S5IuMAXjqOL9VrxhEi AwAqa9MRHFVNBZcVYm2aSwg3OoebUN7rLEzHg8kqIVQEAljdiwEZxyDBbGLHWuYdQAMxDNKSf9g == X-Google-Smtp-Source: ABdhPJxZ/cCvl56AjPBnBYJUldbW/9BLlDA3Mf5QdEkqN5VwS/uv2nbMbo8etRE3q6dYjCk7TwAGfJCzZNXjMplmsKQ= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:ae14:: with SMTP id a20mr8888473ybj.129.1615428709755; Wed, 10 Mar 2021 18:11:49 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:33 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-34-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 33/37] receive-pack: convert receive hooks to hook.h From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using the hook.h library to run receive hooks, they can be specified in the config as well as in the hookdir. Signed-off-by: Emily Shaffer --- Documentation/githooks.txt | 5 + builtin/receive-pack.c | 199 +++++++++++++++++-------------------- 2 files changed, 97 insertions(+), 107 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index fe5381b95b..b63054b947 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -323,6 +323,8 @@ will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. See the section on "Quarantine Environment" in linkgit:git-receive-pack[1] for some caveats. +Hooks executed during 'pre-receive' will not be parallelized. + [[update]] update ~~~~~~ @@ -476,6 +478,9 @@ environment variables will not be set. If the client selects to use push options, but doesn't transmit any, the count variable will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. +Hooks executed during 'post-receive' are run in parallel, unless hook.jobs is +configured to 1. + [[post-update]] post-update ~~~~~~~~~~~ diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 955efbdf6d..d124718d0b 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -748,7 +748,7 @@ static int check_cert_push_options(const struct string_list *push_options) return retval; } -static void prepare_push_cert_sha1(struct child_process *proc) +static void prepare_push_cert_sha1(struct run_hooks_opt *opt) { static int already_done; @@ -772,110 +772,42 @@ static void prepare_push_cert_sha1(struct child_process *proc) nonce_status = check_nonce(push_cert.buf, bogs); } if (!is_null_oid(&push_cert_oid)) { - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT=%s", + strvec_pushf(&opt->env, "GIT_PUSH_CERT=%s", oid_to_hex(&push_cert_oid)); - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s", + strvec_pushf(&opt->env, "GIT_PUSH_CERT_SIGNER=%s", sigcheck.signer ? sigcheck.signer : ""); - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s", + strvec_pushf(&opt->env, "GIT_PUSH_CERT_KEY=%s", sigcheck.key ? sigcheck.key : ""); - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c", + strvec_pushf(&opt->env, "GIT_PUSH_CERT_STATUS=%c", sigcheck.result); if (push_cert_nonce) { - strvec_pushf(&proc->env_array, + strvec_pushf(&opt->env, "GIT_PUSH_CERT_NONCE=%s", push_cert_nonce); - strvec_pushf(&proc->env_array, + strvec_pushf(&opt->env, "GIT_PUSH_CERT_NONCE_STATUS=%s", nonce_status); if (nonce_status == NONCE_SLOP) - strvec_pushf(&proc->env_array, + strvec_pushf(&opt->env, "GIT_PUSH_CERT_NONCE_SLOP=%ld", nonce_stamp_slop); } } } +struct receive_hook_feed_context { + struct command *cmd; + int skip_broken; +}; + struct receive_hook_feed_state { struct command *cmd; struct ref_push_report *report; int skip_broken; struct strbuf buf; - const struct string_list *push_options; }; -typedef int (*feed_fn)(void *, const char **, size_t *); -static int run_and_feed_hook(const char *hook_name, feed_fn feed, - struct receive_hook_feed_state *feed_state) -{ - struct child_process proc = CHILD_PROCESS_INIT; - struct async muxer; - const char *argv[2]; - int code; - - argv[0] = find_hook(hook_name); - if (!argv[0]) - return 0; - - argv[1] = NULL; - - proc.argv = argv; - proc.in = -1; - proc.stdout_to_stderr = 1; - proc.trace2_hook_name = hook_name; - - if (feed_state->push_options) { - int i; - for (i = 0; i < feed_state->push_options->nr; i++) - strvec_pushf(&proc.env_array, - "GIT_PUSH_OPTION_%d=%s", i, - feed_state->push_options->items[i].string); - strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d", - feed_state->push_options->nr); - } else - strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT"); - - if (tmp_objdir) - strvec_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir)); - - 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; - } - - prepare_push_cert_sha1(&proc); - - code = start_command(&proc); - if (code) { - if (use_sideband) - finish_async(&muxer); - return code; - } - - sigchain_push(SIGPIPE, SIG_IGN); - - while (1) { - const char *buf; - size_t n; - if (feed(feed_state, &buf, &n)) - break; - if (write_in_full(proc.in, buf, n) < 0) - break; - } - close(proc.in); - if (use_sideband) - finish_async(&muxer); - - sigchain_pop(SIGPIPE); - - return finish_command(&proc); -} - -static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) +static int feed_receive_hook(void *state_) { struct receive_hook_feed_state *state = state_; struct command *cmd = state->cmd; @@ -884,9 +816,7 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) state->skip_broken && (cmd->error_string || cmd->did_not_exist)) cmd = cmd->next; if (!cmd) - return -1; /* EOF */ - if (!bufp) - return 0; /* OK, can feed something. */ + return 1; /* EOF - close the pipe*/ strbuf_reset(&state->buf); if (!state->report) state->report = cmd->report; @@ -910,32 +840,36 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) cmd->ref_name); state->cmd = cmd->next; } - if (bufp) { - *bufp = state->buf.buf; - *sizep = state->buf.len; - } return 0; } -static int run_receive_hook(struct command *commands, - const char *hook_name, - int skip_broken, - const struct string_list *push_options) +static int feed_receive_hook_cb(struct strbuf *pipe, void *pp_cb, void *pp_task_cb) { - struct receive_hook_feed_state state; - int status; - - strbuf_init(&state.buf, 0); - state.cmd = commands; - state.skip_broken = skip_broken; - state.report = NULL; - if (feed_receive_hook(&state, NULL, NULL)) - return 0; - state.cmd = commands; - state.push_options = push_options; - status = run_and_feed_hook(hook_name, feed_receive_hook, &state); - strbuf_release(&state.buf); - return status; + struct hook *hook = pp_task_cb; + struct receive_hook_feed_state *feed_state = hook->feed_pipe_cb_data; + int rc; + + /* first-time setup */ + if (!feed_state) { + struct hook_cb_data *hook_cb = pp_cb; + struct run_hooks_opt *opt = hook_cb->options; + struct receive_hook_feed_context *ctx = opt->feed_pipe_ctx; + if (!ctx) + BUG("run_hooks_opt.feed_pipe_ctx required for receive hook"); + + feed_state = xmalloc(sizeof(struct receive_hook_feed_state)); + strbuf_init(&feed_state->buf, 0); + feed_state->cmd = ctx->cmd; + feed_state->skip_broken = ctx->skip_broken; + feed_state->report = NULL; + + hook->feed_pipe_cb_data = feed_state; + } + + rc = feed_receive_hook(feed_state); + if (!rc) + strbuf_addbuf(pipe, &feed_state->buf); + return rc; } static void hook_output_to_sideband(struct strbuf *output, void *cb_data) @@ -971,6 +905,57 @@ static void hook_output_to_sideband(struct strbuf *output, void *cb_data) send_sideband(1, 2, output->buf, output->len, use_sideband); } +static int run_receive_hook(struct command *commands, + const char *hook_name, + int skip_broken, + const struct string_list *push_options) +{ + struct run_hooks_opt opt; + struct receive_hook_feed_context ctx; + int rc; + struct command *iter = commands; + + run_hooks_opt_init_async(&opt); + + /* if there are no valid commands, don't invoke the hook at all. */ + while (iter && skip_broken && (iter->error_string || iter->did_not_exist)) + iter = iter->next; + if (!iter) + return 0; + + /* pre-receive hooks should run in series as the hook updates refs */ + if (!strcmp(hook_name, "pre-receive")) + opt.jobs = 1; + + if (push_options) { + int i; + for (i = 0; i < push_options->nr; i++) + strvec_pushf(&opt.env, "GIT_PUSH_OPTION_%d=%s", i, + push_options->items[i].string); + strvec_pushf(&opt.env, "GIT_PUSH_OPTION_COUNT=%d", push_options->nr); + } else + strvec_push(&opt.env, "GIT_PUSH_OPTION_COUNT"); + + if (tmp_objdir) + strvec_pushv(&opt.env, tmp_objdir_env(tmp_objdir)); + + prepare_push_cert_sha1(&opt); + + /* set up sideband printer */ + if (use_sideband) + opt.consume_sideband = hook_output_to_sideband; + + /* set up stdin callback */ + ctx.cmd = commands; + ctx.skip_broken = skip_broken; + opt.feed_pipe = feed_receive_hook_cb; + opt.feed_pipe_ctx = &ctx; + + rc = run_hooks(hook_name, &opt); + run_hooks_opt_clear(&opt); + return rc; +} + static int run_update_hook(struct command *cmd) { struct run_hooks_opt opt; From patchwork Thu Mar 11 02:10:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130139 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2CD55C4361B for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0ACB564FC5 for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230490AbhCKCMU (ORCPT ); Wed, 10 Mar 2021 21:12:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230137AbhCKCL6 (ORCPT ); Wed, 10 Mar 2021 21:11:58 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 548BEC061574 for ; Wed, 10 Mar 2021 18:11:52 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id n10so23628949ybb.12 for ; Wed, 10 Mar 2021 18:11:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=56kVj9+hUABIsBGN/+1c7mbttDbPs4DT9BJ3q2sOpkk=; b=gHv/KqOzE/nZvUSbVfz9WsswQrYUCmUWppEqEl5x0KRAU2BKl2ihS41bW0GMndh+Uu RY97mWIUP3dewLEdES5cFJsrZTvaUNKmpVYgUKosZyKSJYKhRCypYJLXSn9tuUJdNdMQ LuslWl6lsZS5eFFsW5ufflhMQLAaM6VOz8aKJSMvPYGCxL8/TMHDGLnUbSmEyjTn3Dfl z90gbDpDdPTnM/mz6Jovf656oTXr6uYdIFKVUgK2kxwAi2vkeAlLF5MNd+358f1adcdB b+iCmEsGMNrR24ZJsdUNPKJ1nafLZth0rGoOmn/3lup5qXbH5ECTUVRA5gROSnJ6vRKD GhYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=56kVj9+hUABIsBGN/+1c7mbttDbPs4DT9BJ3q2sOpkk=; b=p3nKBhKGT2w2Xh765Y2oUaOLOGbLf7klyb2FrtlSMgRV5l16hVwGBt/u0bo0peWTNS wzGZ5XIAfJ0fb9hevGqA8CchMP6casaQcSVS2y6cre8Lxf0rHS0bP/4UrAni5W9S230I /vgDNB/GvhoMFYtuD61F+Up/jLXoFbxyHxL8GMdsytiwoCwnIS+4T1aLlNmNvwHbuJbc uV9vKaLy0iPBBWkZrZtwicfd7/xTNY5rYhXqU9Ir9+mQfLkSqMpoOnKg2eRBsxLqKH5c /fQugydGkvI0PxHKS3CdQRNCQNvG6n7NhmHGgXyg09Hla4LWBnfyzsmiD8JB+hpuHwYt 6jNQ== X-Gm-Message-State: AOAM530wjV65VHNtiix5lYR1WCjv+PSN4KMjqfBPG0QEhAaKEr4q034w PmQ0xlpfBGpY/6yTDFbCAxwxuV+aU/uOkDtjOl3qV629U+ienF2NjZpHwAHAnrr7BAocLhNk+o4 0BbUzmFYW3mf5h/TXWO15hDCU85wsYRMg6WJ6KOjVk2PaKvvVODfAGyEgOxGJQbGfWtJwa2X31g == X-Google-Smtp-Source: ABdhPJxr7z6Rp51ObB+rEbsrsrSXGozlYTUI9KQOYXOuIgwDDSl34O91eKIVYX8RUtqtejiWETrzHGrViC+/Hn4md7Y= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:3104:: with SMTP id x4mr9150111ybx.217.1615428711553; Wed, 10 Mar 2021 18:11:51 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:34 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-35-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 34/37] bugreport: use hook_exists instead of find_hook From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using the helper in hook.h instead of the one in run-command.h, we can also check whether a hook exists in the config - not just whether it exists in the hookdir. Signed-off-by: Emily Shaffer --- builtin/bugreport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/bugreport.c b/builtin/bugreport.c index ad3cc9c02f..eac3726527 100644 --- a/builtin/bugreport.c +++ b/builtin/bugreport.c @@ -3,7 +3,7 @@ #include "strbuf.h" #include "help.h" #include "compat/compiler.h" -#include "run-command.h" +#include "hook.h" static void get_system_info(struct strbuf *sys_info) @@ -82,7 +82,7 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit) } for (i = 0; i < ARRAY_SIZE(hook); i++) - if (find_hook(hook[i])) + if (hook_exists(hook[i], HOOKDIR_USE_CONFIG)) strbuf_addf(hook_info, "%s\n", hook[i]); } From patchwork Thu Mar 11 02:10:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130143 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DBB63C43142 for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9F45B64FEE for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230506AbhCKCMU (ORCPT ); Wed, 10 Mar 2021 21:12:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42416 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230143AbhCKCMA (ORCPT ); Wed, 10 Mar 2021 21:12:00 -0500 Received: from mail-qv1-xf4a.google.com (mail-qv1-xf4a.google.com [IPv6:2607:f8b0:4864:20::f4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 28BEEC061760 for ; Wed, 10 Mar 2021 18:11:54 -0800 (PST) Received: by mail-qv1-xf4a.google.com with SMTP id iy2so14134298qvb.22 for ; Wed, 10 Mar 2021 18:11:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Rw+eb/obMKPLBdEDKBTqV2W8PW7N/pZsGxejVqfZVyQ=; b=d3M41RZH4DR9S9nf2/7MG+z1kYs3mUsjsLgalyR3kgJolTv3yRC1d47Tx33nEdJM0s FnFI3sUL5UDRwvgVhylto0lSU6ApmqOboylSGRyxb16KcrxtZDP1i+l3WE7mldvhVnos KdFI1qABNSxDZCLAtHR8o45xxBvIHNyLYHKza5bMxribxlF0t2OPaIo4PCrjzVms4kxk yIIyH+9LD49oBGjAho4ai+rWhfoiW5cTUEY91SYqPjTC85fBuzVjUw1/IDJksc2zZ8nf D/iAuIMngxvdYWUKuKX797vfV2pDxNR4Oljo8s6rxtReftRnuMHOoAULIumr9AnERlZg zM7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Rw+eb/obMKPLBdEDKBTqV2W8PW7N/pZsGxejVqfZVyQ=; b=imdyxmISeh6mWEE3pgCZGy8EajLmvAeYHXJ++kE9gL3zlaIs9OvuhWQ1IVJ4syl3/S 29VBAMdSnevNM8EWcOxcdG864NUwO0P6FSB4ubP8F07o2BQpuPiKjcC0yFCqOkOPYANP mskUGCUcQG3saypUa4HYtxONDFZ+Q2EA5nmt+PeVIvAAHivdKUIKSXpFHGhUcRnjYezM i+udX4kZXXNfSrBrUAeCij9YIDvc/H68I40s+QhTdxT0fstUZIilvZp3WDYy6D0hULz+ ICHnhRQEWn4HsfSfudOviodniQ3hJwu6wsFEOqvxUbmRtO6htCf3DSwaBw84YHe58iJX an8w== X-Gm-Message-State: AOAM53001e4mB40hfo/VVitc1twPgA4Z0PTquUrF0EyiCVRNuB5G5/1W gHp3ygKr75+OBcwamhSHixbdoX6NiZNj+xJyQDOE6uo7kaG6uRRxsgdDww3N12C8FMFVa8c6ZJo WoE7iv4Qr3dtFc4i2hju0XtxZ36QNK9zHI/Yelj0tYbbavHvR1VI2J0+vIHx8CZs/oZ15cTv9bw == X-Google-Smtp-Source: ABdhPJxGuoxZWnWz58v66mC5saCHy/iBUziXQ6LpNqPf98ptjpYfuGtOsFF7q2oeJZjnBLwO/CrylQkBK0yf6ocCrOg= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a05:6214:c8a:: with SMTP id r10mr5880204qvr.13.1615428713355; Wed, 10 Mar 2021 18:11:53 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:35 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-36-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 35/37] git-send-email: use 'git hook run' for 'sendemail-validate' From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By using the new 'git hook run' subcommand to run 'sendemail-validate', we can reduce the boilerplate needed to run this hook in perl. Using config-based hooks also allows us to run 'sendemail-validate' hooks that were configured globally when running 'git send-email' from outside of a Git directory, alongside other benefits like multihooks and parallelization. Signed-off-by: Emily Shaffer --- git-send-email.perl | 21 ++++----------------- t/t9001-send-email.sh | 11 +---------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 1f425c0809..73e1e0b51a 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1941,23 +1941,10 @@ sub unique_email_list { sub validate_patch { my ($fn, $xfer_encoding) = @_; - if ($repo) { - my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'), - 'sendemail-validate'); - my $hook_error; - if (-x $validate_hook) { - my $target = abs_path($fn); - # The hook needs a correct cwd and GIT_DIR. - my $cwd_save = cwd(); - chdir($repo->wc_path() or $repo->repo_path()) - or die("chdir: $!"); - local $ENV{"GIT_DIR"} = $repo->repo_path(); - $hook_error = "rejected by sendemail-validate hook" - if system($validate_hook, $target); - chdir($cwd_save) or die("chdir: $!"); - } - return $hook_error if $hook_error; - } + my $target = abs_path($fn); + return "rejected by sendemail-validate hook" + if system(("git", "hook", "run", "sendemail-validate", "-a", + $target)); # Any long lines will be automatically fixed if we use a suitable transfer # encoding. diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 4eee9c3dcb..456b471c5c 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -2101,16 +2101,7 @@ test_expect_success $PREREQ 'invoke hook' ' mkdir -p .git/hooks && write_script .git/hooks/sendemail-validate <<-\EOF && - # test that we have the correct environment variable, pwd, and - # argument - case "$GIT_DIR" in - *.git) - true - ;; - *) - false - ;; - esac && + # test that we have the correct argument test -f 0001-add-main.patch && grep "add main" "$1" EOF From patchwork Thu Mar 11 02:10:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130145 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BF8E9C433DB for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 830C464FC6 for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230483AbhCKCMT (ORCPT ); Wed, 10 Mar 2021 21:12:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42424 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230122AbhCKCL4 (ORCPT ); Wed, 10 Mar 2021 21:11:56 -0500 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C8739C061761 for ; Wed, 10 Mar 2021 18:11:55 -0800 (PST) Received: by mail-yb1-xb4a.google.com with SMTP id 127so23683277ybc.19 for ; Wed, 10 Mar 2021 18:11:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=JsCprLWLyFSuBzFrBsU3D0YNMKHlYXYbK3AIXZsDlDY=; b=sCf8tmXMw0p6LudblwUVP+pbkDp72kzQA3LXmjggLgfRZoVwU3CNXAB9OgckLXB0X5 zXpkBbh0JAyO0b5M4In5J9C7quHUY6LOiMFgrkUTy+Oa1Xs9bUV2LoWgmkXWr7Whag1a uF+ENxiM+jQxAx0530daL1OX6WWUz7logcrddzqfDDpNq5WbTy1aWray0viZBNfDlN1T V3hdDyNxdR2ciFa2zv+Vc12HdmjJCYcMh5I6rsUWodC1bzbFKeozFO9ysBF99w2SNuLm zjxB1o8adDJ4+5ghG0TgRlGjKQF1O47Uu3z7W0n5TwqAOvvj6a9hgMiyBGSR4EeJXFkN l4AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=JsCprLWLyFSuBzFrBsU3D0YNMKHlYXYbK3AIXZsDlDY=; b=dt0eEX5e8OOE2u73yFmGUqhqJbrHAd2hRqx9y/PSsqToliiXAIO/xzfO0TbpTpTZWa JWZFGW/TgL7CX1pzpB5I0E6DcyvBdcTuNpfcsr3s8CWIgL/O5xf8c/hUufwcyAv2mezB cdOxNeol2ytY8fsOiI+LbaLJKxou9zG8iRIgjUoFRUV6PzoFEFGarC9bwFJtKt31+CRn HBTqVi2ipghRXzSZaDZaNhf1AXwqG//i9CDseV67HtxkqhN7um+Pnr1XpWNIJamlKnyv d5kWwydoXNMuhhksdAqFviZUZdVACa5+ZShT+jeL4p9nA5g80RmYn1p1iPQqYDh/FyCX L4sg== X-Gm-Message-State: AOAM532BsQ5OcP4dt3UjDcQ583Vlx7IwCy9qswmk2tzQ8ndf+g1q/cLz jGRBUPNkuEFA3a9IvlDIQboHEo0vTrxYeuQ2ww9B2q2oNdB/3jUxGuSdbXxB6ZmeYtnyaNeD8U5 7zFLvdiQxo7sbE3GSh0zmwfYffYwfF3SLOj65YC7a7B2RpNeIMH2Fp/Iw9INrtoZCH5RPbBKrOA == X-Google-Smtp-Source: ABdhPJwcGs29KmhMw0pngF36W+0XroEvI5YaSJIXWlrAck6yvhtMFZp4lnojmkq7AtCjwdC9xMsfXW/4SuNefBbRFB0= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a5b:7cd:: with SMTP id t13mr8270069ybq.417.1615428715063; Wed, 10 Mar 2021 18:11:55 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:36 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-37-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 36/37] run-command: stop thinking about hooks From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org hook.h has replaced all run-command.h hook-related functionality. run-command.h:run_hooks_le/ve and find_hook are no longer used anywhere in the codebase. So, let's delete the dead code - or, in the one case where it's still needed, move it to an internal function in hook.c. Signed-off-by: Emily Shaffer --- hook.c | 39 ++++++++++++++++++++++++++++-- run-command.c | 66 --------------------------------------------------- run-command.h | 24 ------------------- 3 files changed, 37 insertions(+), 92 deletions(-) diff --git a/hook.c b/hook.c index 2322720ffe..7f6f3b9a61 100644 --- a/hook.c +++ b/hook.c @@ -212,6 +212,41 @@ static int should_include_hookdir(const char *path, enum hookdir_opt cfg) } } +static const char *find_legacy_hook(const char *name) +{ + static struct strbuf path = STRBUF_INIT; + + strbuf_reset(&path); + strbuf_git_path(&path, "hooks/%s", name); + if (access(path.buf, X_OK) < 0) { + int err = errno; + +#ifdef STRIP_EXTENSION + strbuf_addstr(&path, STRIP_EXTENSION); + if (access(path.buf, X_OK) >= 0) + return path.buf; + if (errno == EACCES) + err = errno; +#endif + + if (err == EACCES && advice_ignored_hook) { + static struct string_list advise_given = STRING_LIST_INIT_DUP; + + if (!string_list_lookup(&advise_given, name)) { + string_list_insert(&advise_given, name); + advise(_("The '%s' hook was ignored because " + "it's not set as executable.\n" + "You can disable this warning with " + "`git config advice.ignoredHook false`."), + path.buf); + } + } + return NULL; + } + return path.buf; +} + + struct list_head* hook_list(const struct strbuf* hookname) { struct strbuf hook_key = STRBUF_INIT; @@ -228,7 +263,7 @@ struct list_head* hook_list(const struct strbuf* hookname) git_config(hook_config_lookup, &cb_data); if (have_git_dir()) { - const char *legacy_hook_path = find_hook(hookname->buf); + const char *legacy_hook_path = find_legacy_hook(hookname->buf); /* Unconditionally add legacy hook, but annotate it. */ if (legacy_hook_path) { @@ -277,7 +312,7 @@ int hook_exists(const char *hookname, enum hookdir_opt should_run_hookdir) could_run_hookdir = (should_run_hookdir == HOOKDIR_INTERACTIVE || should_run_hookdir == HOOKDIR_WARN || should_run_hookdir == HOOKDIR_YES) - && !!find_hook(hookname); + && !!find_legacy_hook(hookname); strbuf_addf(&hook_key, "hook.%s.command", hookname); diff --git a/run-command.c b/run-command.c index 36a4edbacf..837415131d 100644 --- a/run-command.c +++ b/run-command.c @@ -1320,72 +1320,6 @@ int async_with_fork(void) #endif } -const char *find_hook(const char *name) -{ - static struct strbuf path = STRBUF_INIT; - - strbuf_reset(&path); - strbuf_git_path(&path, "hooks/%s", name); - if (access(path.buf, X_OK) < 0) { - int err = errno; - -#ifdef STRIP_EXTENSION - strbuf_addstr(&path, STRIP_EXTENSION); - if (access(path.buf, X_OK) >= 0) - return path.buf; - if (errno == EACCES) - err = errno; -#endif - - if (err == EACCES && advice_ignored_hook) { - static struct string_list advise_given = STRING_LIST_INIT_DUP; - - if (!string_list_lookup(&advise_given, name)) { - string_list_insert(&advise_given, name); - advise(_("The '%s' hook was ignored because " - "it's not set as executable.\n" - "You can disable this warning with " - "`git config advice.ignoredHook false`."), - path.buf); - } - } - return NULL; - } - return path.buf; -} - -int run_hook_ve(const char *const *env, const char *name, va_list args) -{ - struct child_process hook = CHILD_PROCESS_INIT; - const char *p; - - p = find_hook(name); - if (!p) - return 0; - - strvec_push(&hook.args, p); - while ((p = va_arg(args, const char *))) - strvec_push(&hook.args, p); - hook.env = env; - hook.no_stdin = 1; - hook.stdout_to_stderr = 1; - hook.trace2_hook_name = name; - - return run_command(&hook); -} - -int run_hook_le(const char *const *env, const char *name, ...) -{ - va_list args; - int ret; - - va_start(args, name); - ret = run_hook_ve(env, name, args); - va_end(args); - - return ret; -} - struct io_pump { /* initialized by caller */ int fd; diff --git a/run-command.h b/run-command.h index ebc4a95a94..7150da851a 100644 --- a/run-command.h +++ b/run-command.h @@ -201,30 +201,6 @@ int finish_command_in_signal(struct child_process *); */ int run_command(struct child_process *); -/* - * Returns the path to the hook file, or NULL if the hook is missing - * or disabled. Note that this points to static storage that will be - * overwritten by further calls to find_hook and run_hook_*. - */ -const char *find_hook(const char *name); - -/** - * Run a hook. - * The first argument is a pathname to an index file, or NULL - * if the hook uses the default index file or no index is needed. - * The second argument is the name of the hook. - * The further arguments correspond to the hook arguments. - * The last argument has to be NULL to terminate the arguments list. - * If the hook does not exist or is not executable, the return - * value will be zero. - * If it is executable, the hook will be executed and the exit - * status of the hook is returned. - * On execution, .stdout_to_stderr and .no_stdin will be set. - */ -LAST_ARG_MUST_BE_NULL -int run_hook_le(const char *const *env, const char *name, ...); -int run_hook_ve(const char *const *env, const char *name, va_list args); - /* * Trigger an auto-gc */ From patchwork Thu Mar 11 02:10:37 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emily Shaffer X-Patchwork-Id: 12130147 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-31.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A85F1C433E0 for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 525F765004 for ; Thu, 11 Mar 2021 02:12:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230488AbhCKCMU (ORCPT ); Wed, 10 Mar 2021 21:12:20 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42434 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230140AbhCKCL6 (ORCPT ); Wed, 10 Mar 2021 21:11:58 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BFA5CC061761 for ; Wed, 10 Mar 2021 18:11:57 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id j4so23635881ybt.23 for ; Wed, 10 Mar 2021 18:11:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=TDcc7y21l5avJ17QXas+6l7D6Gq5qvI/tmLqrEEjjL0=; b=imSEIgmQ2opeG0KNO32RyNUQO99jJxE2GteTH5Gi4OpPw6UkHt0sDFy5sN3Fa8Kj6t AuJbE1cOETFxlyzFpHG4DXLIM0iu4iPAmWwRaboeAOqBTX/WMfcGQXu0dPSTWr4jp13X FTexm+TVb8qf+26f8nwFk2ksacgfqPDcPP0eHMASz/vwD6HPsMdxAdBTg9Vv7jmmE+tI XKnm/u2QgxS1ln19T16+kFF7TkpydTrgGhepjRks/CO4YsscsqOZdFkTqN05SJrA7TKN BahVo/hspq6bPxT8ZRpzsI/SfHHfWw7v1UAbKj1Mul1sMhQz0Gxwva8Miggd8G0yf+d/ qeKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=TDcc7y21l5avJ17QXas+6l7D6Gq5qvI/tmLqrEEjjL0=; b=HKqgc2TTnrTpRUOWZeHgL5M+pKZzoXc8QKwyLiTLlpypfvOFVNgdgRpQPcRInG1PPp G206VTIbpbzyysWDxCLLKPLbaab06OnhsQ558Nl29T1yA9EfdM4S+pGTr8LqvdAZHuYp S2Y/0CuRsJO6ceY9H2nCuR4mcDma05v0bSygcvXqm7n/X4HgKwUCasB6gu0ikSwNdNdu UwTEsiwas+CWX3dC5VTpm85kNdnHivUT0dHSdZ/rqSTznmWOZ38ygE9nBXSFTxUeIh28 ZN43G5bd7SZiZHG/BhSSo2HWTyRLNWqYhClqCswH/5g5D4fI60rKMwYatzCd3vsXzMtl 8PDg== X-Gm-Message-State: AOAM533aqptGxMa7agLIXo4N1aicKkvGQEwRt29ziDRJm8GmXvRIJzoS RhBwg1r7pUN/hUHTiF7lks+PtTLb5bNY/rxCIUmnZL6m83xEbxMZ9J1M6hRUIZXbsaEk6tbjeEo FquZusQ1P688sSJBB5GaRwjgbNevuLVIej6SBU1lL3KqE8eaYZWB6nSpaqklhFlXSqROWVrQmBw == X-Google-Smtp-Source: ABdhPJwE+SGIb8pJgtzoeZntVSUPELb33PHaHpLR1y3oQsOuy1xzW9kds1YJ+1sB8dcLAL8pNIHvLZ4Vxs3YVSdMX+U= X-Received: from podkayne.svl.corp.google.com ([2620:15c:2ce:0:3521:9495:983c:f6d5]) (user=emilyshaffer job=sendgmr) by 2002:a25:3495:: with SMTP id b143mr7027938yba.431.1615428716952; Wed, 10 Mar 2021 18:11:56 -0800 (PST) Date: Wed, 10 Mar 2021 18:10:37 -0800 In-Reply-To: <20210311021037.3001235-1-emilyshaffer@google.com> Message-Id: <20210311021037.3001235-38-emilyshaffer@google.com> Mime-Version: 1.0 References: <20210311021037.3001235-1-emilyshaffer@google.com> X-Mailer: git-send-email 2.31.0.rc2.261.g7f71774620-goog Subject: [PATCH v8 37/37] docs: unify githooks and git-hook manpages From: Emily Shaffer To: git@vger.kernel.org Cc: Emily Shaffer Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org By showing the list of all hooks in 'git help hook' for users to refer to, 'git help hook' becomes a one-stop shop for hook authorship. Since some may still have muscle memory for 'git help githooks', though, reference the 'git hook' commands and otherwise don't remove content. Signed-off-by: Emily Shaffer --- Documentation/git-hook.txt | 11 + Documentation/githooks.txt | 716 +-------------------------------- Documentation/native-hooks.txt | 708 ++++++++++++++++++++++++++++++++ 3 files changed, 724 insertions(+), 711 deletions(-) create mode 100644 Documentation/native-hooks.txt diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt index 81b8e94994..4ad31ac360 100644 --- a/Documentation/git-hook.txt +++ b/Documentation/git-hook.txt @@ -17,6 +17,13 @@ DESCRIPTION You can list and run configured hooks with this command. Later, you will be able to add and modify hooks with this command. +In general, when instructions suggest adding a script to +`.git/hooks/`, you can specify it in the config instead by running +`git config --add hook..command ` - this way you can +share the script between multiple repos. That is, `cp ~/my-script.sh +~/project/.git/hooks/pre-commit` would become `git config --add +hook.pre-commit.command ~/my-script.sh`. + This command parses the default configuration files for sections `hook` and `hookcmd`. `hook` is used to describe the commands which will be run during a particular hook event; commands are run in the order Git encounters them during @@ -145,6 +152,10 @@ CONFIGURATION ------------- include::config/hook.txt[] +HOOKS +----- +include::native-hooks.txt[] + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index b63054b947..9a25dfdc3f 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -7,15 +7,16 @@ githooks - Hooks used by Git SYNOPSIS -------- +'git hook' $GIT_DIR/hooks/* (or \`git config core.hooksPath`/*) DESCRIPTION ----------- -Hooks are programs you can place in a hooks directory to trigger -actions at certain points in git's execution. Hooks that don't have -the executable bit set are ignored. +Hooks are programs you can specify in your config (see linkgit:git-hook[1]) or +place in a hooks directory to trigger actions at certain points in git's +execution. Hooks that don't have the executable bit set are ignored. By default the hooks directory is `$GIT_DIR/hooks`, but that can be changed via the `core.hooksPath` configuration variable (see @@ -41,714 +42,7 @@ The currently supported hooks are described below. HOOKS ----- - -applypatch-msg -~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-am[1]. It takes a single -parameter, the name of the file that holds the proposed commit -log message. Exiting with a non-zero status causes `git am` to abort -before applying the patch. - -The hook is allowed to edit the message file in place, and can -be used to normalize the message into some project standard -format. It can also be used to refuse the commit after inspecting -the message file. - -The default 'applypatch-msg' hook, when enabled, runs the -'commit-msg' hook, if the latter is enabled. - -Hooks run during 'applypatch-msg' will not be parallelized, because hooks are -expected to edit the file holding the commit log message. - -pre-applypatch -~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-am[1]. It takes no parameter, and is -invoked after the patch is applied, but before a commit is made. - -If it exits with non-zero status, then the working tree will not be -committed after applying the patch. - -It can be used to inspect the current working tree and refuse to -make a commit if it does not pass certain test. - -The default 'pre-applypatch' hook, when enabled, runs the -'pre-commit' hook, if the latter is enabled. - -Hooks run during 'pre-applypatch' will be run in parallel, unless hook.jobs is -configured to 1. - -post-applypatch -~~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-am[1]. It takes no parameter, -and is invoked after the patch is applied and a commit is made. - -This hook is meant primarily for notification, and cannot affect -the outcome of `git am`. - -Hooks run during 'post-applypatch' will be run in parallel, unless hook.jobs is -configured to 1. - -pre-commit -~~~~~~~~~~ - -This hook is invoked by linkgit:git-commit[1], and can be bypassed -with the `--no-verify` option. It takes no parameters, and is -invoked before obtaining the proposed commit log message and -making a commit. Exiting with a non-zero status from this script -causes the `git commit` command to abort before creating a commit. - -The default 'pre-commit' hook, when enabled, catches introduction -of lines with trailing whitespaces and aborts the commit when -such a line is found. - -All the `git commit` hooks are invoked with the environment -variable `GIT_EDITOR=:` if the command will not bring up an editor -to modify the commit message. - -The default 'pre-commit' hook, when enabled--and with the -`hooks.allownonascii` config option unset or set to false--prevents -the use of non-ASCII filenames. - -Hooks executed during 'pre-commit' will not be parallelized. - -pre-merge-commit -~~~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-merge[1], and can be bypassed -with the `--no-verify` option. It takes no parameters, and is -invoked after the merge has been carried out successfully and before -obtaining the proposed commit log message to -make a commit. Exiting with a non-zero status from this script -causes the `git merge` command to abort before creating a commit. - -The default 'pre-merge-commit' hook, when enabled, runs the -'pre-commit' hook, if the latter is enabled. - -This hook is invoked with the environment variable -`GIT_EDITOR=:` if the command will not bring up an editor -to modify the commit message. - -If the merge cannot be carried out automatically, the conflicts -need to be resolved and the result committed separately (see -linkgit:git-merge[1]). At that point, this hook will not be executed, -but the 'pre-commit' hook will, if it is enabled. - -Hooks executed during 'pre-merge-commit' will not be parallelized. - -prepare-commit-msg -~~~~~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-commit[1] right after preparing the -default log message, and before the editor is started. - -It takes one to three parameters. The first is the name of the file -that contains the commit log message. The second is the source of the commit -message, and can be: `message` (if a `-m` or `-F` option was -given); `template` (if a `-t` option was given or the -configuration option `commit.template` is set); `merge` (if the -commit is a merge or a `.git/MERGE_MSG` file exists); `squash` -(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by -a commit SHA-1 (if a `-c`, `-C` or `--amend` option was given). - -If the exit status is non-zero, `git commit` will abort. - -The purpose of the hook is to edit the message file in place, and -it is not suppressed by the `--no-verify` option. A non-zero exit -means a failure of the hook and aborts the commit. It should not -be used as replacement for pre-commit hook. - -The sample `prepare-commit-msg` hook that comes with Git removes the -help message found in the commented portion of the commit template. - -Hooks executed during 'prepare-commit-msg' will not be parallelized, because -hooks are expected to edit the file containing the commit log message. - -commit-msg -~~~~~~~~~~ - -This hook is invoked by linkgit:git-commit[1] and linkgit:git-merge[1], and can be -bypassed with the `--no-verify` option. It takes a single parameter, -the name of the file that holds the proposed commit log message. -Exiting with a non-zero status causes the command to abort. - -The hook is allowed to edit the message file in place, and can be used -to normalize the message into some project standard format. It -can also be used to refuse the commit after inspecting the message -file. - -The default 'commit-msg' hook, when enabled, detects duplicate -`Signed-off-by` trailers, and aborts the commit if one is found. - -Hooks executed during 'commit-msg' will not be parallelized, because hooks are -expected to edit the file containing the proposed commit log message. - -post-commit -~~~~~~~~~~~ - -This hook is invoked by linkgit:git-commit[1]. It takes no parameters, and is -invoked after a commit is made. - -This hook is meant primarily for notification, and cannot affect -the outcome of `git commit`. - -Hooks executed during 'post-commit' will run in parallel, unless hook.jobs is -configured to 1. - -pre-rebase -~~~~~~~~~~ - -This hook is called by linkgit:git-rebase[1] and can be used to prevent a -branch from getting rebased. The hook may be called with one or -two parameters. The first parameter is the upstream from which -the series was forked. The second parameter is the branch being -rebased, and is not set when rebasing the current branch. - -Hooks executed during 'pre-rebase' will run in parallel, unless hook.jobs is -configured to 1. - -post-checkout -~~~~~~~~~~~~~ - -This hook is invoked when a linkgit:git-checkout[1] or -linkgit:git-switch[1] is run after having updated the -worktree. The hook is given three parameters: the ref of the previous HEAD, -the ref of the new HEAD (which may or may not have changed), and a flag -indicating whether the checkout was a branch checkout (changing branches, -flag=1) or a file checkout (retrieving a file from the index, flag=0). -This hook cannot affect the outcome of `git switch` or `git checkout`, -other than that the hook's exit status becomes the exit status of -these two commands. - -It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is -used. The first parameter given to the hook is the null-ref, the second the -ref of the new HEAD and the flag is always 1. Likewise for `git worktree add` -unless `--no-checkout` is used. - -This hook can be used to perform repository validity checks, auto-display -differences from the previous HEAD if different, or set working dir metadata -properties. - -Hooks executed during 'post-checkout' will not be parallelized. - -post-merge -~~~~~~~~~~ - -This hook is invoked by linkgit:git-merge[1], which happens when a `git pull` -is done on a local repository. The hook takes a single parameter, a status -flag specifying whether or not the merge being done was a squash merge. -This hook cannot affect the outcome of `git merge` and is not executed, -if the merge failed due to conflicts. - -This hook can be used in conjunction with a corresponding pre-commit hook to -save and restore any form of metadata associated with the working tree -(e.g.: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl -for an example of how to do this. - -Hooks executed during 'post-merge' will run in parallel, unless hook.jobs is -configured to 1. - -pre-push -~~~~~~~~ - -This hook is called by linkgit:git-push[1] and can be used to prevent -a push from taking place. The hook is called with two parameters -which provide the name and location of the destination remote, if a -named remote is not being used both values will be the same. - -Information about what is to be pushed is provided on the hook's standard -input with lines of the form: - - SP SP SP LF - -For instance, if the command +git push origin master:foreign+ were run the -hook would receive a line like the following: - - refs/heads/master 67890 refs/heads/foreign 12345 - -although the full, 40-character SHA-1s would be supplied. If the foreign ref -does not yet exist the `` will be 40 `0`. If a ref is to be -deleted, the `` will be supplied as `(delete)` and the `` will be 40 `0`. If the local commit was specified by something other -than a name which could be expanded (such as `HEAD~`, or a SHA-1) it will be -supplied as it was originally given. - -If this hook exits with a non-zero status, `git push` will abort without -pushing anything. Information about why the push is rejected may be sent -to the user by writing to standard error. - -Hooks executed during 'pre-push' will run in parallel, unless hook.jobs is -configured to 1. - -[[pre-receive]] -pre-receive -~~~~~~~~~~~ - -This hook is invoked by linkgit:git-receive-pack[1] when it reacts to -`git push` and updates reference(s) in its repository. -Just before starting to update refs on the remote repository, the -pre-receive hook is invoked. Its exit status determines the success -or failure of the update. - -This hook executes once for the receive operation. It takes no -arguments, but for each ref to be updated it receives on standard -input a line of the format: - - SP SP LF - -where `` is the old object name stored in the ref, -`` is the new object name to be stored in the ref and -`` is the full name of the ref. -When creating a new ref, `` is 40 `0`. - -If the hook exits with non-zero status, none of the refs will be -updated. If the hook exits with zero, updating of individual refs can -still be prevented by the <> hook. - -Both standard output and standard error output are forwarded to -`git send-pack` on the other end, so you can simply `echo` messages -for the user. - -The number of push options given on the command line of -`git push --push-option=...` can be read from the environment -variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are -found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,... -If it is negotiated to not use the push options phase, the -environment variables will not be set. If the client selects -to use push options, but doesn't transmit any, the count variable -will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. - -See the section on "Quarantine Environment" in -linkgit:git-receive-pack[1] for some caveats. - -Hooks executed during 'pre-receive' will not be parallelized. - -[[update]] -update -~~~~~~ - -This hook is invoked by linkgit:git-receive-pack[1] when it reacts to -`git push` and updates reference(s) in its repository. -Just before updating the ref on the remote repository, the update hook -is invoked. Its exit status determines the success or failure of -the ref update. - -The hook executes once for each ref to be updated, and takes -three parameters: - - - the name of the ref being updated, - - the old object name stored in the ref, - - and the new object name to be stored in the ref. - -A zero exit from the update hook allows the ref to be updated. -Exiting with a non-zero status prevents `git receive-pack` -from updating that ref. - -This hook can be used to prevent 'forced' update on certain refs by -making sure that the object name is a commit object that is a -descendant of the commit object named by the old object name. -That is, to enforce a "fast-forward only" policy. - -It could also be used to log the old..new status. However, it -does not know the entire set of branches, so it would end up -firing one e-mail per ref when used naively, though. The -<> hook is more suited to that. - -In an environment that restricts the users' access only to git -commands over the wire, this hook can be used to implement access -control without relying on filesystem ownership and group -membership. See linkgit:git-shell[1] for how you might use the login -shell to restrict the user's access to only git commands. - -Both standard output and standard error output are forwarded to -`git send-pack` on the other end, so you can simply `echo` messages -for the user. - -The default 'update' hook, when enabled--and with -`hooks.allowunannotated` config option unset or set to false--prevents -unannotated tags to be pushed. - -Hooks executed during 'update' are run in parallel, unless hook.jobs is -configured to 1. - -[[proc-receive]] -proc-receive -~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-receive-pack[1]. If the server has -set the multi-valued config variable `receive.procReceiveRefs`, and the -commands sent to 'receive-pack' have matching reference names, these -commands will be executed by this hook, instead of by the internal -`execute_commands()` function. This hook is responsible for updating -the relevant references and reporting the results back to 'receive-pack'. - -This hook executes once for the receive operation. It takes no -arguments, but uses a pkt-line format protocol to communicate with -'receive-pack' to read commands, push-options and send results. In the -following example for the protocol, the letter 'S' stands for -'receive-pack' and the letter 'H' stands for this hook. - - # Version and features 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( ) - S: ... ... - S: flush-pkt - # Send push-options only if the 'push-options' feature is enabled. - S: PKT-LINE(push-option) - S: ... ... - S: flush-pkt - - # Receive result from the hook. - # OK, run this command successfully. - H: PKT-LINE(ok ) - # NO, I reject it. - H: PKT-LINE(ng ) - # Fall through, let 'receive-pack' to execute it. - H: PKT-LINE(ok ) - H: PKT-LINE(option fall-through) - # OK, but has an alternate reference. The alternate reference name - # and other status can be given in option directives. - H: PKT-LINE(ok ) - H: PKT-LINE(option refname ) - H: PKT-LINE(option old-oid ) - H: PKT-LINE(option new-oid ) - H: PKT-LINE(option forced-update) - H: ... ... - H: flush-pkt - -Each command for the 'proc-receive' hook may point to a pseudo-reference -and always has a zero-old as its old-oid, while the 'proc-receive' hook -may update an alternate reference and the alternate reference may exist -already with a non-zero old-oid. For this case, this hook will use -"option" directives to report extended attributes for the reference given -by the leading "ok" directive. - -The report of the commands of this hook should have the same order as -the input. The exit status of the 'proc-receive' hook only determines -the success or failure of the group of commands sent to it, unless -atomic push is in use. - -It is forbidden to specify more than one hook for 'proc-receive'. If a -globally-configured 'proc-receive' must be overridden, use -'hookcmd..skip = true' to ignore it. - -[[post-receive]] -post-receive -~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-receive-pack[1] when it reacts to -`git push` and updates reference(s) in its repository. -It executes on the remote repository once after all the refs have -been updated. - -This hook executes once for the receive operation. It takes no -arguments, but gets the same information as the -<> -hook does on its standard input. - -This hook does not affect the outcome of `git receive-pack`, as it -is called after the real work is done. - -This supersedes the <> hook in that it gets -both old and new values of all the refs in addition to their -names. - -Both standard output and standard error output are forwarded to -`git send-pack` on the other end, so you can simply `echo` messages -for the user. - -The default 'post-receive' hook is empty, but there is -a sample script `post-receive-email` provided in the `contrib/hooks` -directory in Git distribution, which implements sending commit -emails. - -The number of push options given on the command line of -`git push --push-option=...` can be read from the environment -variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are -found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,... -If it is negotiated to not use the push options phase, the -environment variables will not be set. If the client selects -to use push options, but doesn't transmit any, the count variable -will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. - -Hooks executed during 'post-receive' are run in parallel, unless hook.jobs is -configured to 1. - -[[post-update]] -post-update -~~~~~~~~~~~ - -This hook is invoked by linkgit:git-receive-pack[1] when it reacts to -`git push` and updates reference(s) in its repository. -It executes on the remote repository once after all the refs have -been updated. - -It takes a variable number of parameters, each of which is the -name of ref that was actually updated. - -This hook is meant primarily for notification, and cannot affect -the outcome of `git receive-pack`. - -The 'post-update' hook can tell what are the heads that were pushed, -but it does not know what their original and updated values are, -so it is a poor place to do log old..new. The -<> hook does get both original and -updated values of the refs. You might consider it instead if you need -them. - -When enabled, the default 'post-update' hook runs -`git update-server-info` to keep the information used by dumb -transports (e.g., HTTP) up to date. If you are publishing -a Git repository that is accessible via HTTP, you should -probably enable this hook. - -Both standard output and standard error output are forwarded to -`git send-pack` on the other end, so you can simply `echo` messages -for the user. - -Hooks run during 'post-update' will be run in parallel, unless hook.jobs is -configured to 1. - -reference-transaction -~~~~~~~~~~~~~~~~~~~~~ - -This hook is invoked by any Git command that performs reference -updates. It executes whenever a reference transaction is prepared, -committed or aborted and may thus get called multiple times. - -The hook takes exactly one argument, which is the current state the -given reference transaction is in: - - - "prepared": All reference updates have been queued to the - transaction and references were locked on disk. - - - "committed": The reference transaction was committed and all - references now have their respective new value. - - - "aborted": The reference transaction was aborted, no changes - were performed and the locks have been released. - -For each reference update that was added to the transaction, the hook -receives on standard input a line of the format: - - SP SP LF - -The exit status of the hook is ignored for any state except for the -"prepared" state. In the "prepared" state, a non-zero exit status will -cause the transaction to be aborted. The hook will not be called with -"aborted" state in that case. - -Hooks run during 'reference-transaction' will be run in parallel, unless -hook.jobs is configured to 1. - -push-to-checkout -~~~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-receive-pack[1] when it reacts to -`git push` and updates reference(s) in its repository, and when -the push tries to update the branch that is currently checked out -and the `receive.denyCurrentBranch` configuration variable is set to -`updateInstead`. Such a push by default is refused if the working -tree and the index of the remote repository has any difference from -the currently checked out commit; when both the working tree and the -index match the current commit, they are updated to match the newly -pushed tip of the branch. This hook is to be used to override the -default behaviour. - -The hook receives the commit with which the tip of the current -branch is going to be updated. It can exit with a non-zero status -to refuse the push (when it does so, it must not modify the index or -the working tree). Or it can make any necessary changes to the -working tree and to the index to bring them to the desired state -when the tip of the current branch is updated to the new commit, and -exit with a zero status. - -For example, the hook can simply run `git read-tree -u -m HEAD "$1"` -in order to emulate `git fetch` that is run in the reverse direction -with `git push`, as the two-tree form of `git read-tree -u -m` is -essentially the same as `git switch` or `git checkout` -that switches branches while -keeping the local changes in the working tree that do not interfere -with the difference between the branches. - -Hooks executed during 'push-to-checkout' will not be parallelized. - -pre-auto-gc -~~~~~~~~~~~ - -This hook is invoked by `git gc --auto` (see linkgit:git-gc[1]). It -takes no parameter, and exiting with non-zero status from this script -causes the `git gc --auto` to abort. - -Hooks run during 'pre-auto-gc' will be run in parallel, unless hook.jobs is -configured to 1. - -post-rewrite -~~~~~~~~~~~~ - -This hook is invoked by commands that rewrite commits -(linkgit:git-commit[1] when called with `--amend` and -linkgit:git-rebase[1]; however, full-history (re)writing tools like -linkgit:git-fast-import[1] or -https://github.com/newren/git-filter-repo[git-filter-repo] typically -do not call it!). Its first argument denotes the command it was -invoked by: currently one of `amend` or `rebase`. Further -command-dependent arguments may be passed in the future. - -The hook receives a list of the rewritten commits on stdin, in the -format - - SP [ SP ] LF - -The 'extra-info' is again command-dependent. If it is empty, the -preceding SP is also omitted. Currently, no commands pass any -'extra-info'. - -The hook always runs after the automatic note copying (see -"notes.rewrite." in linkgit:git-config[1]) has happened, and -thus has access to these notes. - -Hooks run during 'post-rewrite' will be run in parallel, unless hook.jobs is -configured to 1. - -The following command-specific comments apply: - -rebase:: - For the 'squash' and 'fixup' operation, all commits that were - squashed are listed as being rewritten to the squashed commit. - This means that there will be several lines sharing the same - 'new-sha1'. -+ -The commits are guaranteed to be listed in the order that they were -processed by rebase. - -sendemail-validate -~~~~~~~~~~~~~~~~~~ - -This hook is invoked by linkgit:git-send-email[1]. It takes a single parameter, -the name of the file that holds the e-mail to be sent. Exiting with a -non-zero status causes `git send-email` to abort before sending any -e-mails. - -fsmonitor-watchman -~~~~~~~~~~~~~~~~~~ - -This hook is invoked when the configuration option `core.fsmonitor` is -set to `.git/hooks/fsmonitor-watchman` or `.git/hooks/fsmonitor-watchmanv2` -depending on the version of the hook to use. - -Version 1 takes two arguments, a version (1) and the time in elapsed -nanoseconds since midnight, January 1, 1970. - -Version 2 takes two arguments, a version (2) and a token that is used -for identifying changes since the token. For watchman this would be -a clock id. This version must output to stdout the new token followed -by a NUL before the list of files. - -The hook should output to stdout the list of all files in the working -directory that may have changed since the requested time. The logic -should be inclusive so that it does not miss any potential changes. -The paths should be relative to the root of the working directory -and be separated by a single NUL. - -It is OK to include files which have not actually changed. All changes -including newly-created and deleted files should be included. When -files are renamed, both the old and the new name should be included. - -Git will limit what files it checks for changes as well as which -directories are checked for untracked files based on the path names -given. - -An optimized way to tell git "all files have changed" is to return -the filename `/`. - -The exit status determines whether git will use the data from the -hook to limit its search. On error, it will fall back to verifying -all files and folders. - -p4-changelist -~~~~~~~~~~~~~ - -This hook is invoked by `git-p4 submit`. - -The `p4-changelist` hook is executed after the changelist -message has been edited by the user. It can be bypassed with the -`--no-verify` option. It takes a single parameter, the name -of the file that holds the proposed changelist text. Exiting -with a non-zero status causes the command to abort. - -The hook is allowed to edit the changelist file and can be used -to normalize the text into some project standard format. It can -also be used to refuse the Submit after inspect the message file. - -Run `git-p4 submit --help` for details. - -p4-prepare-changelist -~~~~~~~~~~~~~~~~~~~~~ - -This hook is invoked by `git-p4 submit`. - -The `p4-prepare-changelist` hook is executed right after preparing -the default changelist message and before the editor is started. -It takes one parameter, the name of the file that contains the -changelist text. Exiting with a non-zero status from the script -will abort the process. - -The purpose of the hook is to edit the message file in place, -and it is not suppressed by the `--no-verify` option. This hook -is called even if `--prepare-p4-only` is set. - -Run `git-p4 submit --help` for details. - -p4-post-changelist -~~~~~~~~~~~~~~~~~~ - -This hook is invoked by `git-p4 submit`. - -The `p4-post-changelist` hook is invoked after the submit has -successfully occurred in P4. It takes no parameters and is meant -primarily for notification and cannot affect the outcome of the -git p4 submit action. - -Run `git-p4 submit --help` for details. - -p4-pre-submit -~~~~~~~~~~~~~ - -This hook is invoked by `git-p4 submit`. It takes no parameters and nothing -from standard input. Exiting with non-zero status from this script prevent -`git-p4 submit` from launching. It can be bypassed with the `--no-verify` -command line option. Run `git-p4 submit --help` for details. - - - -post-index-change -~~~~~~~~~~~~~~~~~ - -This hook is invoked when the index is written in read-cache.c -do_write_locked_index. - -The first parameter passed to the hook is the indicator for the -working directory being updated. "1" meaning working directory -was updated or "0" when the working directory was not updated. - -The second parameter passed to the hook is the indicator for whether -or not the index was updated and the skip-worktree bit could have -changed. "1" meaning skip-worktree bits could have been updated -and "0" meaning they were not. - -Only one parameter should be set to "1" when the hook runs. The hook -running passing "1", "1" should not be possible. - -Hooks run during 'post-index-change' will be run in parallel, unless hook.jobs -is configured to 1. +include::native-hooks.txt[] GIT --- diff --git a/Documentation/native-hooks.txt b/Documentation/native-hooks.txt new file mode 100644 index 0000000000..6c4aad83e1 --- /dev/null +++ b/Documentation/native-hooks.txt @@ -0,0 +1,708 @@ +applypatch-msg +~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-am[1]. It takes a single +parameter, the name of the file that holds the proposed commit +log message. Exiting with a non-zero status causes `git am` to abort +before applying the patch. + +The hook is allowed to edit the message file in place, and can +be used to normalize the message into some project standard +format. It can also be used to refuse the commit after inspecting +the message file. + +The default 'applypatch-msg' hook, when enabled, runs the +'commit-msg' hook, if the latter is enabled. + +Hooks run during 'applypatch-msg' will not be parallelized, because hooks are +expected to edit the file holding the commit log message. + +pre-applypatch +~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-am[1]. It takes no parameter, and is +invoked after the patch is applied, but before a commit is made. + +If it exits with non-zero status, then the working tree will not be +committed after applying the patch. + +It can be used to inspect the current working tree and refuse to +make a commit if it does not pass certain test. + +The default 'pre-applypatch' hook, when enabled, runs the +'pre-commit' hook, if the latter is enabled. + +Hooks run during 'pre-applypatch' will be run in parallel, unless hook.jobs is +configured to 1. + +post-applypatch +~~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-am[1]. It takes no parameter, +and is invoked after the patch is applied and a commit is made. + +This hook is meant primarily for notification, and cannot affect +the outcome of `git am`. + +Hooks run during 'post-applypatch' will be run in parallel, unless hook.jobs is +configured to 1. + +pre-commit +~~~~~~~~~~ + +This hook is invoked by linkgit:git-commit[1], and can be bypassed +with the `--no-verify` option. It takes no parameters, and is +invoked before obtaining the proposed commit log message and +making a commit. Exiting with a non-zero status from this script +causes the `git commit` command to abort before creating a commit. + +The default 'pre-commit' hook, when enabled, catches introduction +of lines with trailing whitespaces and aborts the commit when +such a line is found. + +All the `git commit` hooks are invoked with the environment +variable `GIT_EDITOR=:` if the command will not bring up an editor +to modify the commit message. + +The default 'pre-commit' hook, when enabled--and with the +`hooks.allownonascii` config option unset or set to false--prevents +the use of non-ASCII filenames. + +Hooks executed during 'pre-commit' will not be parallelized. + +pre-merge-commit +~~~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-merge[1], and can be bypassed +with the `--no-verify` option. It takes no parameters, and is +invoked after the merge has been carried out successfully and before +obtaining the proposed commit log message to +make a commit. Exiting with a non-zero status from this script +causes the `git merge` command to abort before creating a commit. + +The default 'pre-merge-commit' hook, when enabled, runs the +'pre-commit' hook, if the latter is enabled. + +This hook is invoked with the environment variable +`GIT_EDITOR=:` if the command will not bring up an editor +to modify the commit message. + +If the merge cannot be carried out automatically, the conflicts +need to be resolved and the result committed separately (see +linkgit:git-merge[1]). At that point, this hook will not be executed, +but the 'pre-commit' hook will, if it is enabled. + +Hooks executed during 'pre-merge-commit' will not be parallelized. + +prepare-commit-msg +~~~~~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-commit[1] right after preparing the +default log message, and before the editor is started. + +It takes one to three parameters. The first is the name of the file +that contains the commit log message. The second is the source of the commit +message, and can be: `message` (if a `-m` or `-F` option was +given); `template` (if a `-t` option was given or the +configuration option `commit.template` is set); `merge` (if the +commit is a merge or a `.git/MERGE_MSG` file exists); `squash` +(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by +a commit SHA-1 (if a `-c`, `-C` or `--amend` option was given). + +If the exit status is non-zero, `git commit` will abort. + +The purpose of the hook is to edit the message file in place, and +it is not suppressed by the `--no-verify` option. A non-zero exit +means a failure of the hook and aborts the commit. It should not +be used as replacement for pre-commit hook. + +The sample `prepare-commit-msg` hook that comes with Git removes the +help message found in the commented portion of the commit template. + +Hooks executed during 'prepare-commit-msg' will not be parallelized, because +hooks are expected to edit the file containing the commit log message. + +commit-msg +~~~~~~~~~~ + +This hook is invoked by linkgit:git-commit[1] and linkgit:git-merge[1], and can be +bypassed with the `--no-verify` option. It takes a single parameter, +the name of the file that holds the proposed commit log message. +Exiting with a non-zero status causes the command to abort. + +The hook is allowed to edit the message file in place, and can be used +to normalize the message into some project standard format. It +can also be used to refuse the commit after inspecting the message +file. + +The default 'commit-msg' hook, when enabled, detects duplicate +`Signed-off-by` trailers, and aborts the commit if one is found. + +Hooks executed during 'commit-msg' will not be parallelized, because hooks are +expected to edit the file containing the proposed commit log message. + +post-commit +~~~~~~~~~~~ + +This hook is invoked by linkgit:git-commit[1]. It takes no parameters, and is +invoked after a commit is made. + +This hook is meant primarily for notification, and cannot affect +the outcome of `git commit`. + +Hooks executed during 'post-commit' will run in parallel, unless hook.jobs is +configured to 1. + +pre-rebase +~~~~~~~~~~ + +This hook is called by linkgit:git-rebase[1] and can be used to prevent a +branch from getting rebased. The hook may be called with one or +two parameters. The first parameter is the upstream from which +the series was forked. The second parameter is the branch being +rebased, and is not set when rebasing the current branch. + +Hooks executed during 'pre-rebase' will run in parallel, unless hook.jobs is +configured to 1. + +post-checkout +~~~~~~~~~~~~~ + +This hook is invoked when a linkgit:git-checkout[1] or +linkgit:git-switch[1] is run after having updated the +worktree. The hook is given three parameters: the ref of the previous HEAD, +the ref of the new HEAD (which may or may not have changed), and a flag +indicating whether the checkout was a branch checkout (changing branches, +flag=1) or a file checkout (retrieving a file from the index, flag=0). +This hook cannot affect the outcome of `git switch` or `git checkout`, +other than that the hook's exit status becomes the exit status of +these two commands. + +It is also run after linkgit:git-clone[1], unless the `--no-checkout` (`-n`) option is +used. The first parameter given to the hook is the null-ref, the second the +ref of the new HEAD and the flag is always 1. Likewise for `git worktree add` +unless `--no-checkout` is used. + +This hook can be used to perform repository validity checks, auto-display +differences from the previous HEAD if different, or set working dir metadata +properties. + +Hooks executed during 'post-checkout' will not be parallelized. + +post-merge +~~~~~~~~~~ + +This hook is invoked by linkgit:git-merge[1], which happens when a `git pull` +is done on a local repository. The hook takes a single parameter, a status +flag specifying whether or not the merge being done was a squash merge. +This hook cannot affect the outcome of `git merge` and is not executed, +if the merge failed due to conflicts. + +This hook can be used in conjunction with a corresponding pre-commit hook to +save and restore any form of metadata associated with the working tree +(e.g.: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl +for an example of how to do this. + +Hooks executed during 'post-merge' will run in parallel, unless hook.jobs is +configured to 1. + +pre-push +~~~~~~~~ + +This hook is called by linkgit:git-push[1] and can be used to prevent +a push from taking place. The hook is called with two parameters +which provide the name and location of the destination remote, if a +named remote is not being used both values will be the same. + +Information about what is to be pushed is provided on the hook's standard +input with lines of the form: + + SP SP SP LF + +For instance, if the command +git push origin master:foreign+ were run the +hook would receive a line like the following: + + refs/heads/master 67890 refs/heads/foreign 12345 + +although the full, 40-character SHA-1s would be supplied. If the foreign ref +does not yet exist the `` will be 40 `0`. If a ref is to be +deleted, the `` will be supplied as `(delete)` and the `` will be 40 `0`. If the local commit was specified by something other +than a name which could be expanded (such as `HEAD~`, or a SHA-1) it will be +supplied as it was originally given. + +If this hook exits with a non-zero status, `git push` will abort without +pushing anything. Information about why the push is rejected may be sent +to the user by writing to standard error. + +Hooks executed during 'pre-push' will run in parallel, unless hook.jobs is +configured to 1. + +[[pre-receive]] +pre-receive +~~~~~~~~~~~ + +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +`git push` and updates reference(s) in its repository. +Just before starting to update refs on the remote repository, the +pre-receive hook is invoked. Its exit status determines the success +or failure of the update. + +This hook executes once for the receive operation. It takes no +arguments, but for each ref to be updated it receives on standard +input a line of the format: + + SP SP LF + +where `` is the old object name stored in the ref, +`` is the new object name to be stored in the ref and +`` is the full name of the ref. +When creating a new ref, `` is 40 `0`. + +If the hook exits with non-zero status, none of the refs will be +updated. If the hook exits with zero, updating of individual refs can +still be prevented by the <> hook. + +Both standard output and standard error output are forwarded to +`git send-pack` on the other end, so you can simply `echo` messages +for the user. + +The number of push options given on the command line of +`git push --push-option=...` can be read from the environment +variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are +found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,... +If it is negotiated to not use the push options phase, the +environment variables will not be set. If the client selects +to use push options, but doesn't transmit any, the count variable +will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. + +See the section on "Quarantine Environment" in +linkgit:git-receive-pack[1] for some caveats. + +Hooks executed during 'pre-receive' will not be parallelized. + +[[update]] +update +~~~~~~ + +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +`git push` and updates reference(s) in its repository. +Just before updating the ref on the remote repository, the update hook +is invoked. Its exit status determines the success or failure of +the ref update. + +The hook executes once for each ref to be updated, and takes +three parameters: + + - the name of the ref being updated, + - the old object name stored in the ref, + - and the new object name to be stored in the ref. + +A zero exit from the update hook allows the ref to be updated. +Exiting with a non-zero status prevents `git receive-pack` +from updating that ref. + +This hook can be used to prevent 'forced' update on certain refs by +making sure that the object name is a commit object that is a +descendant of the commit object named by the old object name. +That is, to enforce a "fast-forward only" policy. + +It could also be used to log the old..new status. However, it +does not know the entire set of branches, so it would end up +firing one e-mail per ref when used naively, though. The +<> hook is more suited to that. + +In an environment that restricts the users' access only to git +commands over the wire, this hook can be used to implement access +control without relying on filesystem ownership and group +membership. See linkgit:git-shell[1] for how you might use the login +shell to restrict the user's access to only git commands. + +Both standard output and standard error output are forwarded to +`git send-pack` on the other end, so you can simply `echo` messages +for the user. + +The default 'update' hook, when enabled--and with +`hooks.allowunannotated` config option unset or set to false--prevents +unannotated tags to be pushed. + +Hooks executed during 'update' are run in parallel, unless hook.jobs is +configured to 1. + +[[proc-receive]] +proc-receive +~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-receive-pack[1]. If the server has +set the multi-valued config variable `receive.procReceiveRefs`, and the +commands sent to 'receive-pack' have matching reference names, these +commands will be executed by this hook, instead of by the internal +`execute_commands()` function. This hook is responsible for updating +the relevant references and reporting the results back to 'receive-pack'. + +This hook executes once for the receive operation. It takes no +arguments, but uses a pkt-line format protocol to communicate with +'receive-pack' to read commands, push-options and send results. In the +following example for the protocol, the letter 'S' stands for +'receive-pack' and the letter 'H' stands for this hook. + + # Version and features 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( ) + S: ... ... + S: flush-pkt + # Send push-options only if the 'push-options' feature is enabled. + S: PKT-LINE(push-option) + S: ... ... + S: flush-pkt + + # Receive result from the hook. + # OK, run this command successfully. + H: PKT-LINE(ok ) + # NO, I reject it. + H: PKT-LINE(ng ) + # Fall through, let 'receive-pack' to execute it. + H: PKT-LINE(ok ) + H: PKT-LINE(option fall-through) + # OK, but has an alternate reference. The alternate reference name + # and other status can be given in option directives. + H: PKT-LINE(ok ) + H: PKT-LINE(option refname ) + H: PKT-LINE(option old-oid ) + H: PKT-LINE(option new-oid ) + H: PKT-LINE(option forced-update) + H: ... ... + H: flush-pkt + +Each command for the 'proc-receive' hook may point to a pseudo-reference +and always has a zero-old as its old-oid, while the 'proc-receive' hook +may update an alternate reference and the alternate reference may exist +already with a non-zero old-oid. For this case, this hook will use +"option" directives to report extended attributes for the reference given +by the leading "ok" directive. + +The report of the commands of this hook should have the same order as +the input. The exit status of the 'proc-receive' hook only determines +the success or failure of the group of commands sent to it, unless +atomic push is in use. + +It is forbidden to specify more than one hook for 'proc-receive'. If a +globally-configured 'proc-receive' must be overridden, use +'hookcmd..skip = true' to ignore it. + +[[post-receive]] +post-receive +~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +`git push` and updates reference(s) in its repository. +It executes on the remote repository once after all the refs have +been updated. + +This hook executes once for the receive operation. It takes no +arguments, but gets the same information as the +<> +hook does on its standard input. + +This hook does not affect the outcome of `git receive-pack`, as it +is called after the real work is done. + +This supersedes the <> hook in that it gets +both old and new values of all the refs in addition to their +names. + +Both standard output and standard error output are forwarded to +`git send-pack` on the other end, so you can simply `echo` messages +for the user. + +The default 'post-receive' hook is empty, but there is +a sample script `post-receive-email` provided in the `contrib/hooks` +directory in Git distribution, which implements sending commit +emails. + +The number of push options given on the command line of +`git push --push-option=...` can be read from the environment +variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are +found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,... +If it is negotiated to not use the push options phase, the +environment variables will not be set. If the client selects +to use push options, but doesn't transmit any, the count variable +will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. + +Hooks executed during 'post-receive' are run in parallel, unless hook.jobs is +configured to 1. + +[[post-update]] +post-update +~~~~~~~~~~~ + +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +`git push` and updates reference(s) in its repository. +It executes on the remote repository once after all the refs have +been updated. + +It takes a variable number of parameters, each of which is the +name of ref that was actually updated. + +This hook is meant primarily for notification, and cannot affect +the outcome of `git receive-pack`. + +The 'post-update' hook can tell what are the heads that were pushed, +but it does not know what their original and updated values are, +so it is a poor place to do log old..new. The +<> hook does get both original and +updated values of the refs. You might consider it instead if you need +them. + +When enabled, the default 'post-update' hook runs +`git update-server-info` to keep the information used by dumb +transports (e.g., HTTP) up to date. If you are publishing +a Git repository that is accessible via HTTP, you should +probably enable this hook. + +Both standard output and standard error output are forwarded to +`git send-pack` on the other end, so you can simply `echo` messages +for the user. + +Hooks run during 'post-update' will be run in parallel, unless hook.jobs is +configured to 1. + +reference-transaction +~~~~~~~~~~~~~~~~~~~~~ + +This hook is invoked by any Git command that performs reference +updates. It executes whenever a reference transaction is prepared, +committed or aborted and may thus get called multiple times. + +The hook takes exactly one argument, which is the current state the +given reference transaction is in: + + - "prepared": All reference updates have been queued to the + transaction and references were locked on disk. + + - "committed": The reference transaction was committed and all + references now have their respective new value. + + - "aborted": The reference transaction was aborted, no changes + were performed and the locks have been released. + +For each reference update that was added to the transaction, the hook +receives on standard input a line of the format: + + SP SP LF + +The exit status of the hook is ignored for any state except for the +"prepared" state. In the "prepared" state, a non-zero exit status will +cause the transaction to be aborted. The hook will not be called with +"aborted" state in that case. + +Hooks run during 'reference-transaction' will be run in parallel, unless +hook.jobs is configured to 1. + +push-to-checkout +~~~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-receive-pack[1] when it reacts to +`git push` and updates reference(s) in its repository, and when +the push tries to update the branch that is currently checked out +and the `receive.denyCurrentBranch` configuration variable is set to +`updateInstead`. Such a push by default is refused if the working +tree and the index of the remote repository has any difference from +the currently checked out commit; when both the working tree and the +index match the current commit, they are updated to match the newly +pushed tip of the branch. This hook is to be used to override the +default behaviour. + +The hook receives the commit with which the tip of the current +branch is going to be updated. It can exit with a non-zero status +to refuse the push (when it does so, it must not modify the index or +the working tree). Or it can make any necessary changes to the +working tree and to the index to bring them to the desired state +when the tip of the current branch is updated to the new commit, and +exit with a zero status. + +For example, the hook can simply run `git read-tree -u -m HEAD "$1"` +in order to emulate `git fetch` that is run in the reverse direction +with `git push`, as the two-tree form of `git read-tree -u -m` is +essentially the same as `git switch` or `git checkout` +that switches branches while +keeping the local changes in the working tree that do not interfere +with the difference between the branches. + +Hooks executed during 'push-to-checkout' will not be parallelized. + +pre-auto-gc +~~~~~~~~~~~ + +This hook is invoked by `git gc --auto` (see linkgit:git-gc[1]). It +takes no parameter, and exiting with non-zero status from this script +causes the `git gc --auto` to abort. + +Hooks run during 'pre-auto-gc' will be run in parallel, unless hook.jobs is +configured to 1. + +post-rewrite +~~~~~~~~~~~~ + +This hook is invoked by commands that rewrite commits +(linkgit:git-commit[1] when called with `--amend` and +linkgit:git-rebase[1]; however, full-history (re)writing tools like +linkgit:git-fast-import[1] or +https://github.com/newren/git-filter-repo[git-filter-repo] typically +do not call it!). Its first argument denotes the command it was +invoked by: currently one of `amend` or `rebase`. Further +command-dependent arguments may be passed in the future. + +The hook receives a list of the rewritten commits on stdin, in the +format + + SP [ SP ] LF + +The 'extra-info' is again command-dependent. If it is empty, the +preceding SP is also omitted. Currently, no commands pass any +'extra-info'. + +The hook always runs after the automatic note copying (see +"notes.rewrite." in linkgit:git-config[1]) has happened, and +thus has access to these notes. + +Hooks run during 'post-rewrite' will be run in parallel, unless hook.jobs is +configured to 1. + +The following command-specific comments apply: + +rebase:: + For the 'squash' and 'fixup' operation, all commits that were + squashed are listed as being rewritten to the squashed commit. + This means that there will be several lines sharing the same + 'new-sha1'. ++ +The commits are guaranteed to be listed in the order that they were +processed by rebase. + +sendemail-validate +~~~~~~~~~~~~~~~~~~ + +This hook is invoked by linkgit:git-send-email[1]. It takes a single parameter, +the name of the file that holds the e-mail to be sent. Exiting with a +non-zero status causes `git send-email` to abort before sending any +e-mails. + +fsmonitor-watchman +~~~~~~~~~~~~~~~~~~ + +This hook is invoked when the configuration option `core.fsmonitor` is +set to `.git/hooks/fsmonitor-watchman` or `.git/hooks/fsmonitor-watchmanv2` +depending on the version of the hook to use. + +Version 1 takes two arguments, a version (1) and the time in elapsed +nanoseconds since midnight, January 1, 1970. + +Version 2 takes two arguments, a version (2) and a token that is used +for identifying changes since the token. For watchman this would be +a clock id. This version must output to stdout the new token followed +by a NUL before the list of files. + +The hook should output to stdout the list of all files in the working +directory that may have changed since the requested time. The logic +should be inclusive so that it does not miss any potential changes. +The paths should be relative to the root of the working directory +and be separated by a single NUL. + +It is OK to include files which have not actually changed. All changes +including newly-created and deleted files should be included. When +files are renamed, both the old and the new name should be included. + +Git will limit what files it checks for changes as well as which +directories are checked for untracked files based on the path names +given. + +An optimized way to tell git "all files have changed" is to return +the filename `/`. + +The exit status determines whether git will use the data from the +hook to limit its search. On error, it will fall back to verifying +all files and folders. + +p4-changelist +~~~~~~~~~~~~~ + +This hook is invoked by `git-p4 submit`. + +The `p4-changelist` hook is executed after the changelist +message has been edited by the user. It can be bypassed with the +`--no-verify` option. It takes a single parameter, the name +of the file that holds the proposed changelist text. Exiting +with a non-zero status causes the command to abort. + +The hook is allowed to edit the changelist file and can be used +to normalize the text into some project standard format. It can +also be used to refuse the Submit after inspect the message file. + +Run `git-p4 submit --help` for details. + +p4-prepare-changelist +~~~~~~~~~~~~~~~~~~~~~ + +This hook is invoked by `git-p4 submit`. + +The `p4-prepare-changelist` hook is executed right after preparing +the default changelist message and before the editor is started. +It takes one parameter, the name of the file that contains the +changelist text. Exiting with a non-zero status from the script +will abort the process. + +The purpose of the hook is to edit the message file in place, +and it is not suppressed by the `--no-verify` option. This hook +is called even if `--prepare-p4-only` is set. + +Run `git-p4 submit --help` for details. + +p4-post-changelist +~~~~~~~~~~~~~~~~~~ + +This hook is invoked by `git-p4 submit`. + +The `p4-post-changelist` hook is invoked after the submit has +successfully occurred in P4. It takes no parameters and is meant +primarily for notification and cannot affect the outcome of the +git p4 submit action. + +Run `git-p4 submit --help` for details. + +p4-pre-submit +~~~~~~~~~~~~~ + +This hook is invoked by `git-p4 submit`. It takes no parameters and nothing +from standard input. Exiting with non-zero status from this script prevent +`git-p4 submit` from launching. It can be bypassed with the `--no-verify` +command line option. Run `git-p4 submit --help` for details. + + + +post-index-change +~~~~~~~~~~~~~~~~~ + +This hook is invoked when the index is written in read-cache.c +do_write_locked_index. + +The first parameter passed to the hook is the indicator for the +working directory being updated. "1" meaning working directory +was updated or "0" when the working directory was not updated. + +The second parameter passed to the hook is the indicator for whether +or not the index was updated and the skip-worktree bit could have +changed. "1" meaning skip-worktree bits could have been updated +and "0" meaning they were not. + +Only one parameter should be set to "1" when the hook runs. The hook +running passing "1", "1" should not be possible. + +Hooks run during 'post-index-change' will be run in parallel, unless hook.jobs +is configured to 1. +