From patchwork Wed May 3 11:34:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230060 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65591C7EE23 for ; Wed, 3 May 2023 11:34:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230058AbjECLeZ (ORCPT ); Wed, 3 May 2023 07:34:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45600 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230010AbjECLeY (ORCPT ); Wed, 3 May 2023 07:34:24 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C1E7049FF for ; Wed, 3 May 2023 04:34:22 -0700 (PDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.46]) by mailout.nyi.internal (Postfix) with ESMTP id 3DD8D5C0112; Wed, 3 May 2023 07:34:22 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute2.internal (MEProxy); Wed, 03 May 2023 07:34:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113662; x=1683200062; bh=Id q28KO96fzGDjdAYh295dy2isAPObLh1weTI3FCaw4=; b=CAuCeHZJ7y1YRc5gnI bbH6h+FZ73tmlNhn8osfxlJYcjdi04AnwXcMj2m9JM3zIaGF6W/AyHYJShqgOLif cIqxza1E2RtyppOgdJltRnh1JYgPmY9uyvZWhysbpB81JiCJx4LvV8PhZ8OxmhY5 6GJWNRehO2YerAOLS8t+IK3EZlZ26ofBrVhBzIcazOiUc5Eej2Z09fN4IZ5kKN1p hmawlqFAd+RHZBsHuui5IS2SoCmHLjETAcR4dCJjcaHtKRvXs4n0S5jdMQwLg5lL 1GH8o4GR796EYEZiTtoba6A+rPLXQF3FpEwPPNr6VcjVvzL/Wesx8tg+U8W/KjeP 9SYA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113662; x=1683200062; bh=Idq28KO96fzGD jdAYh295dy2isAPObLh1weTI3FCaw4=; b=OS5n5cA81icSa6p+RDlmr3KA1RrWQ ujGccxQ2J4gawo3zCeZDCcbUyQIrT0IvGT2vYKoWteyXmQIkoG2khA/5ZG/3qMEX lfEA97LWx54L0OCMwTN+WlTPimc3QUOCX9nrx4AfoikiV3gt/gG0A69xHBKsC8bI iGhy+ZkFWvHbzbjhXzVJZe965IoctMENoq5HaB5Ag5zgd86gHJAdSrUP6SiZhS3o LxcL8+zhRKrQ117umYefnRM2Rsap7ifIHDp2o6gpDagDQjgOcx7IZcjxrMp8sRVw M24Q6goA73YICcuyApaKNBLCvBUMm5o0+rzKPLczVah06lhlwCnChkWWQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggedtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:20 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id 78ec18a6 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:33:39 +0000 (UTC) Date: Wed, 3 May 2023 13:34:18 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 1/8] fetch: fix `--no-recurse-submodules` with multi-remote fetches Message-ID: <4b2b0cfe15ca6b3c9058bc9cb6944ba05d60a228.1683113177.git.ps@pks.im> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org When running `git fetch --no-recurse-submodules`, the exectation is that we don't fetch any submodules. And while this works for fetches of a single remote, it doesn't when fetching multiple remotes at once. The result is that we do recurse into submodules even though the user has explicitly asked us not to. This is because while we pass on `--recurse-submodules={yes,on-demand}` if specified by the user, we don't pass on `--no-recurse-submodules` to the subprocess spawned to perform the submodule fetch. Fix this by also forwarding this flag as expected. Signed-off-by: Patrick Steinhardt --- builtin/fetch.c | 2 ++ t/t5526-fetch-submodules.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/builtin/fetch.c b/builtin/fetch.c index c310d89878..08d7fc7233 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1876,6 +1876,8 @@ static void add_options_to_argv(struct strvec *argv) strvec_push(argv, "--keep"); if (recurse_submodules == RECURSE_SUBMODULES_ON) strvec_push(argv, "--recurse-submodules"); + else if (recurse_submodules == RECURSE_SUBMODULES_OFF) + strvec_push(argv, "--no-recurse-submodules"); else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) strvec_push(argv, "--recurse-submodules=on-demand"); if (tags == TAGS_SET) diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index dcdbe26a08..162e5bac2f 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -1180,4 +1180,35 @@ test_expect_success 'fetch --all with --recurse-submodules with multiple' ' test_line_count = 2 fetch-subs ' +test_expect_success "fetch --all with --no-recurse-submodules only fetches superproject" ' + test_when_finished "git -C downstream remote remove second" && + + # We need to add a second remote, otherwise --all falls back to the + # normal fetch-one case. + git -C downstream remote add second .. && + git -C downstream fetch --all && + + add_submodule_commits && + add_superproject_commits && + old_commit=$(git rev-parse --short HEAD~) && + new_commit=$(git rev-parse --short HEAD) && + + git -C downstream fetch --all --no-recurse-submodules >actual.out 2>actual.err && + + cat >expect.out <<-EOF && + Fetching origin + Fetching second + EOF + + cat >expect.err <<-EOF && + From $(test-tool path-utils real_path .)/. + $old_commit..$new_commit super -> origin/super + From .. + $old_commit..$new_commit super -> second/super + EOF + + test_cmp expect.out actual.out && + test_cmp expect.err actual.err +' + test_done From patchwork Wed May 3 11:34:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230061 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7ADA5C77B78 for ; Wed, 3 May 2023 11:34:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230142AbjECLef (ORCPT ); Wed, 3 May 2023 07:34:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45672 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230062AbjECLe1 (ORCPT ); Wed, 3 May 2023 07:34:27 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A2AC35269 for ; Wed, 3 May 2023 04:34:26 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 193DB5C0099; Wed, 3 May 2023 07:34:26 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Wed, 03 May 2023 07:34:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113666; x=1683200066; bh=u5 fHQpF8SUyR2Nv09Hx7NPYoRBeGlju6Z+WL8YYDTKI=; b=X3+bgqKI0/8zp8Ff1X FBUpPnzK30dzkyEe7FwYdIyRIESZzoZVB9tTI8YaVt6ESOGGxiziPmzaRxT75pJn XnENQ8X/MFffho7E+smP8GyCdIhrVSxnIH0B7IC49ci6YvF+yx+T1aCs6JDpZGiA pB0in9fd3M3YUEeisWoI7WmrXtNNavz4KHOHhlPAhsoE0UdPlcDWfEoVBkCntCoy 7GzJjixWU7QlA1t/ZjABzyxDuaFTg+7oU5e81hQxZytedBR74+oU2oIJ34yHqZSu x4sh660aUMOfZaVWwd1Jh+Lk5M6FD1cBx44o8B9GaFvk9EzD0lcw1Crh7zrtSR/9 ed5A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113666; x=1683200066; bh=u5fHQpF8SUyR2 Nv09Hx7NPYoRBeGlju6Z+WL8YYDTKI=; b=HwIm5Uq1y+Uki4RCgQ/FkQ8gT1+6t C8oT6LkgYiC75HrgO7nm0UXtbcJApdm/SCQ+RwamzymHDjMJgft19M5RylhwDCsk npQsDxW66ekgHe5y9n1XzcCX9/ChwllykcYtZ6tZJU1pb5ILVw0CoQyat8d+ihFz KHwWQyjkcr7cH+3dsCEGggZgJ7WJot05RI3CIrXzRXWRi/rk8Rd4AOG7z1+ckLrm Om05rqfLZqP8eJ1CW5JB7n9WY0+mn7DtjEObhKd6+46sFnvlPFJjUwR4scM6mmwM rWncI75BEgGJwobMu8Af+iCy2DQDlm9xKQNRGekcwG00Yx7c/K45m887Q== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggeduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:24 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id 2ad417ee (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:33:43 +0000 (UTC) Date: Wed, 3 May 2023 13:34:22 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 2/8] fetch: split out tests for output format Message-ID: <6ebc7450bab51083ae225cad768a013e282749a2.1683113177.git.ps@pks.im> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org We're about to introduce a new porcelain mode for the output of git-fetch(1). As part of that we'll be introducing a set of new tests that only relate to the output of this command. Split out tests that exercise the output format of git-fetch(1) so that it becomes easier to verify this functionality as a standalone unit. As the tests assume that the default branch is called "main" we set up the corresponding GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME environment variable accordingly. Signed-off-by: Patrick Steinhardt --- t/t5510-fetch.sh | 53 ---------------------------------- t/t5574-fetch-output.sh | 63 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 53 deletions(-) create mode 100755 t/t5574-fetch-output.sh diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index dc44da9c79..4f289063ce 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -1118,59 +1118,6 @@ test_expect_success 'fetching with auto-gc does not lock up' ' ) ' -test_expect_success 'fetch aligned output' ' - git clone . full-output && - test_commit looooooooooooong-tag && - ( - cd full-output && - git -c fetch.output=full fetch origin >actual 2>&1 && - grep -e "->" actual | cut -c 22- >../actual - ) && - cat >expect <<-\EOF && - main -> origin/main - looooooooooooong-tag -> looooooooooooong-tag - EOF - test_cmp expect actual -' - -test_expect_success 'fetch compact output' ' - git clone . compact && - test_commit extraaa && - ( - cd compact && - git -c fetch.output=compact fetch origin >actual 2>&1 && - grep -e "->" actual | cut -c 22- >../actual - ) && - cat >expect <<-\EOF && - main -> origin/* - extraaa -> * - EOF - test_cmp expect actual -' - -test_expect_success '--no-show-forced-updates' ' - mkdir forced-updates && - ( - cd forced-updates && - git init && - test_commit 1 && - test_commit 2 - ) && - git clone forced-updates forced-update-clone && - git clone forced-updates no-forced-update-clone && - git -C forced-updates reset --hard HEAD~1 && - ( - cd forced-update-clone && - git fetch --show-forced-updates origin 2>output && - test_i18ngrep "(forced update)" output - ) && - ( - cd no-forced-update-clone && - git fetch --no-show-forced-updates origin 2>output && - test_i18ngrep ! "(forced update)" output - ) -' - for section in fetch transfer do test_expect_success "$section.hideRefs affects connectivity check" ' diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh new file mode 100755 index 0000000000..f91b654d38 --- /dev/null +++ b/t/t5574-fetch-output.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +test_description='git fetch output format' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh + +test_expect_success 'fetch aligned output' ' + git clone . full-output && + test_commit looooooooooooong-tag && + ( + cd full-output && + git -c fetch.output=full fetch origin >actual 2>&1 && + grep -e "->" actual | cut -c 22- >../actual + ) && + cat >expect <<-\EOF && + main -> origin/main + looooooooooooong-tag -> looooooooooooong-tag + EOF + test_cmp expect actual +' + +test_expect_success 'fetch compact output' ' + git clone . compact && + test_commit extraaa && + ( + cd compact && + git -c fetch.output=compact fetch origin >actual 2>&1 && + grep -e "->" actual | cut -c 22- >../actual + ) && + cat >expect <<-\EOF && + main -> origin/* + extraaa -> * + EOF + test_cmp expect actual +' + +test_expect_success '--no-show-forced-updates' ' + mkdir forced-updates && + ( + cd forced-updates && + git init && + test_commit 1 && + test_commit 2 + ) && + git clone forced-updates forced-update-clone && + git clone forced-updates no-forced-update-clone && + git -C forced-updates reset --hard HEAD~1 && + ( + cd forced-update-clone && + git fetch --show-forced-updates origin 2>output && + test_i18ngrep "(forced update)" output + ) && + ( + cd no-forced-update-clone && + git fetch --no-show-forced-updates origin 2>output && + test_i18ngrep ! "(forced update)" output + ) +' + +test_done From patchwork Wed May 3 11:34:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230062 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 54DACC77B75 for ; Wed, 3 May 2023 11:34:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230207AbjECLeu (ORCPT ); Wed, 3 May 2023 07:34:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46380 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230163AbjECLeq (ORCPT ); Wed, 3 May 2023 07:34:46 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C182A59D4 for ; Wed, 3 May 2023 04:34:31 -0700 (PDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.46]) by mailout.nyi.internal (Postfix) with ESMTP id 057975C021B; Wed, 3 May 2023 07:34:31 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute2.internal (MEProxy); Wed, 03 May 2023 07:34:31 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113670; x=1683200070; bh=7Z XBB83o0TfAP4yrgKYQ0hnbs65qks5wwuWiVjo+kM8=; b=CsgZO5DQxOYa3rVHrh lhcpl6W4X43troMAOfs2lbb+rJiiSfJRlVdFZl2ucY1Mw3U4cFw0y8lwUlWyGoIk l2BCBjJVzrRlaAWRcUkVeduDaPG4fW4Mok55+DQtEBVeOiiS/V6XhVXuL2Aui0F3 T2D2TWxTqh8XsNUldWidPVKTNS8ixpVvW3GzPeu7gCppEohCd+oWPj+MYgS/ds0T c48LAlx9xpYU6Ev3Q2G4kDf1gc9x3rpmaDMZTSOX9OlHiWH1EcAbYEUDU1UEV2h9 5x32IBjDa5y0RIQVcWsggpSnrin7oo86Irdgyqb17kd8uoor5hemcXfaiQAuHK+I 9P0Q== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113670; x=1683200070; bh=7ZXBB83o0TfAP 4yrgKYQ0hnbs65qks5wwuWiVjo+kM8=; b=PJSaNFQGOKY2f3kNmB/p582VeA+BP 9qjPgZJhTmnbNev7S32WeFAv4jZB+JBkkyraS0aQ+HzLFNZ6QVfH0mM+s1pXd5X5 x2vhTYWgEbHp5rU/OKrP2VeL8OzN+ioKjB5AVORIlruVAhwpbsts5uzBafhaEW9k nPNDxFxzqegQBs9ZurzNnU5gW3IjtfNufDA+rMjnm3dXnRQH41PLV7I16Fy2o76Q c+MJRfp7vlwNBDw338tkNZmmHy5nOV3JLXIcvEtxygmPz7McNnVjxJruKyAsOBam I9KTiRnHkVeXlxLRuE/bOWnHi1gSIe5Ec7wCFYnouKpM7r7Mtp4bR9wTQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggedtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgepvdenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:29 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id 518a0207 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:33:48 +0000 (UTC) Date: Wed, 3 May 2023 13:34:27 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 3/8] fetch: add a test to exercise invalid output formats Message-ID: <78479922ac38eeb6a74f721da73b5437f62ae464.1683113177.git.ps@pks.im> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Add a testcase that exercises the logic when an invalid output format is passed via the `fetch.output` configuration. Signed-off-by: Patrick Steinhardt --- t/t5574-fetch-output.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh index f91b654d38..a09750d225 100755 --- a/t/t5574-fetch-output.sh +++ b/t/t5574-fetch-output.sh @@ -7,6 +7,25 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh +test_expect_success 'fetch with invalid output format configuration' ' + test_when_finished "rm -rf clone" && + git clone . clone && + + test_must_fail git -C clone -c fetch.output= fetch origin >actual.out 2>actual.err && + cat >expect <<-EOF && + fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ} + EOF + test_must_be_empty actual.out && + test_cmp expect actual.err && + + test_must_fail git -C clone -c fetch.output=garbage fetch origin >actual.out 2>actual.err && + cat >expect <<-EOF && + fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ} + EOF + test_must_be_empty actual.out && + test_cmp expect actual.err +' + test_expect_success 'fetch aligned output' ' git clone . full-output && test_commit looooooooooooong-tag && From patchwork Wed May 3 11:34:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230063 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 19EE5C77B75 for ; Wed, 3 May 2023 11:35:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230198AbjECLfG (ORCPT ); Wed, 3 May 2023 07:35:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229907AbjECLes (ORCPT ); Wed, 3 May 2023 07:34:48 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 71ECF5592 for ; Wed, 3 May 2023 04:34:35 -0700 (PDT) Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id BDBBF5C0099; Wed, 3 May 2023 07:34:34 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Wed, 03 May 2023 07:34:34 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113674; x=1683200074; bh=jV OVYkOjnzVa6tPIIejyJRAasVTCrTYIJ0fXFrfo+SQ=; b=pWEn+cb1d3cAugGnS8 jcWko8bbK3el7qVGQeXJv8zGxgagqe5XqsqNoY7aN2Co7QrRRAw+32xpJQazcX7T 8NcR14nlQnl/0k5JHbCUYHaaiURQESY82zaVaXoBhw6SprMQweyXRO71FyKyByUY fBpSaO8n2Zl5Bwfb77EqhjO8vlmTemUg/lLXxivZnExgIb31Tl3lUiEWimSi3PrH LB4jVcLyphklPNqfmo4IQocSvVngyVkHFOqRdDyjXns0l/OWpmmQ5Q868gcKg9QS wtCezNA/FVLxkuUWYrkWDXicZB21fqQ7iGUAO9M7W6ZhcAShy/uFuEQlS3rnw1nH wYZw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113674; x=1683200074; bh=jVOVYkOjnzVa6 tPIIejyJRAasVTCrTYIJ0fXFrfo+SQ=; b=F2UrNnNNsBDoPifGWjuqwT0MNmsrs tVW+E0Lt/SzXHdwAhAL3C+soDpDhuC8vTEF3NKVjoZRDOE2hJ2R9rzttV0eazXCv E3snCOjJqo7wwFHojkkQ+zuYaUn3LbVBdhHlPJBLts4ufr1zj94yghNvJUGLKJDY XP9wMCoaswmYb/LBiGftzysY+iVAsVEgEdxsjJqKDolSjjSNzozdR7ggA8BkRlJ2 D/gf2hnO96qZ/YX8iUxkdAAhWq3D+hfvnfwHvnRAYNzEzVfjJ7hNN5X4AvCSHqyy 9UgSZ5wzNDT10eP6YP5ZhEeK+lt29mvRTxGH6i+a28z4RL9ASHk7pMgqQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggedtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepleejteffveehgeegteekteeiudeiieeigeeigedtffehgeekhfejheefkefhveel necuffhomhgrihhnpehgihhthhhusgdrtghomhenucevlhhushhtvghrufhiiigvpedtne curfgrrhgrmhepmhgrihhlfhhrohhmpehpshesphhkshdrihhm X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:33 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id cf8c86c8 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:33:52 +0000 (UTC) Date: Wed, 3 May 2023 13:34:31 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 4/8] fetch: fix missing from-reference when fetching HEAD:foo Message-ID: <46e1266ab0b13ace9b50bd98c00dbabb566c6cd2.1683113177.git.ps@pks.im> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org `store_updated_refs()` parses the remote reference for two purposes: - It gets used as a note when writing FETCH_HEAD. - It is passed through to `display_ref_update()` to display updated references in the following format: ``` * branch master -> master ``` In most cases, the parsed remote reference is the prettified reference name and can thus be used for both cases. But if the remote reference is HEAD, the parsed remote reference becomes empty. This is intended when we write the FETCH_HEAD, where we skip writing the note in that case. But it is not intended when displaying the updated references and would cause us to miss the left-hand side of the displayed reference update: ``` $ git fetch origin HEAD:foo From https://github.com/git/git * [new ref] -> foo ``` The HEAD string is clearly missing from the left-hand side of the arrow, which is further stressed by the point that the following commands show the left-hand side as expected: ``` $ git fetch origin HEAD From https://github.com/git/git * branch HEAD -> FETCH_HEAD $ git fetch origin master From https://github.com/git/git * branch master -> FETCH_HEAD * branch master -> origin/master ``` The logic of how we compute the remote reference name that we ultimately pass to `display_ref_update()` is not easy to follow. There are three different cases here: - When the remote reference name is "HEAD" we set the remote reference name to the empty string. This is the case that causes the bug to occur, where we would indeed want to print "HEAD" instead of the empty string. This is what `prettify_refname()` would return. - When the remote reference name has a well-known prefix then we strip this prefix. This matches what `prettify_refname()` does. - Otherwise, we keep the fully qualified reference name. This also matches what `prettify_refname()` does. As the return value of `prettify_refname()` would do the correct thing for us in all three cases, we can fix the bug by passing through the full remote reference name to `display_ref_update()`, which learns to call `prettify_refname()`. At the same time, this also simplifies the code a bit. Note that this patch also changes formatting of the block that computes the "kind" and "what" variables. This is done on purpose so that it is part of the diff, hopefully making the change easier to comprehend. Signed-off-by: Patrick Steinhardt --- builtin/fetch.c | 37 +++++++++++++++++++------------------ t/t5574-fetch-output.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 08d7fc7233..6aecf549e8 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -918,12 +918,14 @@ static void display_ref_update(struct display_state *display_state, char code, } width = (summary_width + strlen(summary) - gettext_width(summary)); + remote = prettify_refname(remote); + local = prettify_refname(local); strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary); if (!display_state->compact_format) - print_remote_to_local(display_state, remote, prettify_refname(local)); + print_remote_to_local(display_state, remote, local); else - print_compact(display_state, remote, prettify_refname(local)); + print_compact(display_state, remote, local); if (error) strbuf_addf(&display_state->buf, " (%s)", error); strbuf_addch(&display_state->buf, '\n'); @@ -934,7 +936,7 @@ static void display_ref_update(struct display_state *display_state, char code, static int update_local_ref(struct ref *ref, struct ref_transaction *transaction, struct display_state *display_state, - const char *remote, const struct ref *remote_ref, + const struct ref *remote_ref, int summary_width) { struct commit *current = NULL, *updated; @@ -946,7 +948,7 @@ static int update_local_ref(struct ref *ref, if (oideq(&ref->old_oid, &ref->new_oid)) { if (verbosity > 0) display_ref_update(display_state, '=', _("[up to date]"), NULL, - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); return 0; } @@ -959,7 +961,7 @@ static int update_local_ref(struct ref *ref, */ display_ref_update(display_state, '!', _("[rejected]"), _("can't fetch into checked-out branch"), - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); return 1; } @@ -970,12 +972,12 @@ static int update_local_ref(struct ref *ref, r = s_update_ref("updating tag", ref, transaction, 0); display_ref_update(display_state, r ? '!' : 't', _("[tag update]"), r ? _("unable to update local ref") : NULL, - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); return r; } else { display_ref_update(display_state, '!', _("[rejected]"), _("would clobber existing tag"), - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); return 1; } } @@ -1008,7 +1010,7 @@ static int update_local_ref(struct ref *ref, r = s_update_ref(msg, ref, transaction, 0); display_ref_update(display_state, r ? '!' : '*', what, r ? _("unable to update local ref") : NULL, - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); return r; } @@ -1030,7 +1032,7 @@ static int update_local_ref(struct ref *ref, r = s_update_ref("fast-forward", ref, transaction, 1); display_ref_update(display_state, r ? '!' : ' ', quickref.buf, r ? _("unable to update local ref") : NULL, - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); strbuf_release(&quickref); return r; } else if (force || ref->force) { @@ -1042,12 +1044,12 @@ static int update_local_ref(struct ref *ref, r = s_update_ref("forced-update", ref, transaction, 1); display_ref_update(display_state, r ? '!' : '+', quickref.buf, r ? _("unable to update local ref") : _("forced update"), - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); strbuf_release(&quickref); return r; } else { display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"), - remote, ref->name, summary_width); + remote_ref->name, ref->name, summary_width); return 1; } } @@ -1252,14 +1254,13 @@ static int store_updated_refs(struct display_state *display_state, if (!strcmp(rm->name, "HEAD")) { kind = ""; what = ""; - } - else if (skip_prefix(rm->name, "refs/heads/", &what)) + } else if (skip_prefix(rm->name, "refs/heads/", &what)) { kind = "branch"; - else if (skip_prefix(rm->name, "refs/tags/", &what)) + } else if (skip_prefix(rm->name, "refs/tags/", &what)) { kind = "tag"; - else if (skip_prefix(rm->name, "refs/remotes/", &what)) + } else if (skip_prefix(rm->name, "refs/remotes/", &what)) { kind = "remote-tracking branch"; - else { + } else { kind = ""; what = rm->name; } @@ -1277,7 +1278,7 @@ static int store_updated_refs(struct display_state *display_state, display_state->url_len); if (ref) { - rc |= update_local_ref(ref, transaction, display_state, what, + rc |= update_local_ref(ref, transaction, display_state, rm, summary_width); free(ref); } else if (write_fetch_head || dry_run) { @@ -1288,7 +1289,7 @@ static int store_updated_refs(struct display_state *display_state, */ display_ref_update(display_state, '*', *kind ? kind : "branch", NULL, - *what ? what : "HEAD", + rm->name, "FETCH_HEAD", summary_width); } } diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh index a09750d225..6e0f7e0046 100755 --- a/t/t5574-fetch-output.sh +++ b/t/t5574-fetch-output.sh @@ -56,6 +56,35 @@ test_expect_success 'fetch compact output' ' test_cmp expect actual ' +test_expect_success 'fetch output with HEAD' ' + test_when_finished "rm -rf head" && + git clone . head && + + git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err && + cat >expect <<-EOF && + From $(test-tool path-utils real_path .)/. + * branch HEAD -> FETCH_HEAD + EOF + test_must_be_empty actual.out && + test_cmp expect actual.err && + + git -C head fetch origin HEAD >actual.out 2>actual.err && + test_must_be_empty actual.out && + test_cmp expect actual.err && + + git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err && + cat >expect <<-EOF && + From $(test-tool path-utils real_path .)/. + * [new ref] HEAD -> foo + EOF + test_must_be_empty actual.out && + test_cmp expect actual.err && + + git -C head fetch origin HEAD:foo >actual.out 2>actual.err && + test_must_be_empty actual.out && + test_cmp expect actual.err +' + test_expect_success '--no-show-forced-updates' ' mkdir forced-updates && ( From patchwork Wed May 3 11:34:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230064 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BAFF4C77B75 for ; Wed, 3 May 2023 11:35:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230184AbjECLfP (ORCPT ); Wed, 3 May 2023 07:35:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46766 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230215AbjECLfB (ORCPT ); Wed, 3 May 2023 07:35:01 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 534FF6193 for ; Wed, 3 May 2023 04:34:40 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 868C35C0099; Wed, 3 May 2023 07:34:39 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Wed, 03 May 2023 07:34:39 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113679; x=1683200079; bh=u3 UK++3VwJNO1Wvev+fFLvilEnwZtJBIorU3L3tQOKM=; b=OtYaAP8Dvwkg4oYupo mYNiNEvCzeB39+3prA03iPAdHZQGa4cTrmY55zG9zx3167H5ZkEjAC3vo12uLTsm sgtK9uymbWamE31kTrpWq8bkEMl+JaUWaRK/71KG8MVaRhFpwJI9Q0XMit9eMRGa sh01U8eqZI6CAqXROhJm4UZKyARa8XCbp7kvEQmH7u9K6/i09AQ1UalvISUCVZUV 2hG6uFHJsNMKeQR8wfymbhKq9KyOxVVP3TbuhgAwX7m6RIgPUGDqUnfYbCyGUTXj Gq2Je2GV5tc4DZ5Fpj9fzWJDIWROrQ+8kkWi7i4W/Q+qpDP67ixw3VIPT/9dy2s6 myKA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113679; x=1683200079; bh=u3UK++3VwJNO1 Wvev+fFLvilEnwZtJBIorU3L3tQOKM=; b=Ha9KpwfG2T2UaSt5aCrzldxf1xwl3 YlkExRwb1S+M/ScxdIgqX9qV+7XdHQCRYG+CsiE5P9nF+dJ57sgDCOqEFpPo/5X+ dNsFXOXIj/ezbhmd0URkl8UmoX8D7ERfK2KAmK3pKEQX+yn6Ue2m/nVtUxz7IvVZ rtYi18oahUHZz1y9zuzuOrUhjKYvJ2K3Ngb8I+CncjcWVI7/LOF+PQVo4sK1KL7d pfYYY5IXfKJV6w7tVqgZ25BkT2a6IxrxL3TaJ1tnHzzBBC812WnwyvZG8uD3+T7i vIyW4Ep65DTiSvDTkpNOPHPjeJjQWFUSXYgvCs0q0cW1eHSdE5EVgQXWA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggeduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgepudenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:37 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id e6613dae (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:33:57 +0000 (UTC) Date: Wed, 3 May 2023 13:34:36 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 5/8] fetch: introduce `display_format` enum Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org We currently have two different display formats in git-fetch(1) with the "full" and "compact" formats. This is tracked with a boolean value that simply denotes whether the display format is supposed to be compacted or not. This works reasonably well while there are only two formats, but we're about to introduce another format that will make this a bit more awkward to use. Introduce a `enum display_format` that is more readily extensible. Signed-off-by: Patrick Steinhardt --- builtin/fetch.c | 101 ++++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 6aecf549e8..9e7e45344d 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -48,11 +48,17 @@ enum { TAGS_SET = 2 }; +enum display_format { + DISPLAY_FORMAT_UNKNOWN = 0, + DISPLAY_FORMAT_FULL, + DISPLAY_FORMAT_COMPACT, +}; + struct display_state { struct strbuf buf; int refcol_width; - int compact_format; + enum display_format format; char *url; int url_len, shown_url; @@ -784,7 +790,6 @@ static int refcol_width(const struct ref *ref, int compact_format) static void display_state_init(struct display_state *display_state, struct ref *ref_map, const char *raw_url) { - struct ref *rm; const char *format = "full"; int i; @@ -809,31 +814,42 @@ static void display_state_init(struct display_state *display_state, struct ref * git_config_get_string_tmp("fetch.output", &format); if (!strcasecmp(format, "full")) - display_state->compact_format = 0; + display_state->format = DISPLAY_FORMAT_FULL; else if (!strcasecmp(format, "compact")) - display_state->compact_format = 1; + display_state->format = DISPLAY_FORMAT_COMPACT; else die(_("invalid value for '%s': '%s'"), "fetch.output", format); - display_state->refcol_width = 10; - for (rm = ref_map; rm; rm = rm->next) { - int width; + switch (display_state->format) { + case DISPLAY_FORMAT_FULL: + case DISPLAY_FORMAT_COMPACT: { + struct ref *rm; - if (rm->status == REF_STATUS_REJECT_SHALLOW || - !rm->peer_ref || - !strcmp(rm->name, "HEAD")) - continue; + display_state->refcol_width = 10; + for (rm = ref_map; rm; rm = rm->next) { + int width; - width = refcol_width(rm, display_state->compact_format); + if (rm->status == REF_STATUS_REJECT_SHALLOW || + !rm->peer_ref || + !strcmp(rm->name, "HEAD")) + continue; - /* - * Not precise calculation for compact mode because '*' can - * appear on the left hand side of '->' and shrink the column - * back. - */ - if (display_state->refcol_width < width) - display_state->refcol_width = width; + width = refcol_width(rm, display_state->format == DISPLAY_FORMAT_COMPACT); + + /* + * Not precise calculation for compact mode because '*' can + * appear on the left hand side of '->' and shrink the column + * back. + */ + if (display_state->refcol_width < width) + display_state->refcol_width = width; + } + + break; + } + default: + BUG("unexpected display format %d", display_state->format); } } @@ -904,30 +920,41 @@ static void display_ref_update(struct display_state *display_state, char code, const char *remote, const char *local, int summary_width) { - int width; - if (verbosity < 0) return; strbuf_reset(&display_state->buf); - if (!display_state->shown_url) { - strbuf_addf(&display_state->buf, _("From %.*s\n"), - display_state->url_len, display_state->url); - display_state->shown_url = 1; + switch (display_state->format) { + case DISPLAY_FORMAT_FULL: + case DISPLAY_FORMAT_COMPACT: { + int width; + + if (!display_state->shown_url) { + strbuf_addf(&display_state->buf, _("From %.*s\n"), + display_state->url_len, display_state->url); + display_state->shown_url = 1; + } + + width = (summary_width + strlen(summary) - gettext_width(summary)); + remote = prettify_refname(remote); + local = prettify_refname(local); + + strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary); + + if (display_state->format != DISPLAY_FORMAT_COMPACT) + print_remote_to_local(display_state, remote, local); + else + print_compact(display_state, remote, local); + + if (error) + strbuf_addf(&display_state->buf, " (%s)", error); + + break; } - - width = (summary_width + strlen(summary) - gettext_width(summary)); - remote = prettify_refname(remote); - local = prettify_refname(local); - - strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary); - if (!display_state->compact_format) - print_remote_to_local(display_state, remote, local); - else - print_compact(display_state, remote, local); - if (error) - strbuf_addf(&display_state->buf, " (%s)", error); + default: + BUG("unexpected display format %d", display_state->format); + }; strbuf_addch(&display_state->buf, '\n'); fputs(display_state->buf.buf, stderr); From patchwork Wed May 3 11:34:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230065 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 72A7BC77B78 for ; Wed, 3 May 2023 11:35:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230205AbjECLf2 (ORCPT ); Wed, 3 May 2023 07:35:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46406 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230202AbjECLfI (ORCPT ); Wed, 3 May 2023 07:35:08 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BD5E549EC for ; Wed, 3 May 2023 04:34:46 -0700 (PDT) Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id 6CFC95C0151; Wed, 3 May 2023 07:34:43 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Wed, 03 May 2023 07:34:43 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113683; x=1683200083; bh=Ag g80R7HLeQBkxGPOjnuE6+FjRZfmWkjyCEnwDmkCN0=; b=VAMZ/g5xSCBPdFCPnH RRK0LnnulbpvHOF9Bm2Az634dusudfSOG7YlXssYeMhVogyEzpKCfuZV4Tu7KkTG LBZ+LUFEzxWv5/a31Qh+nE3sI4ckIJNriVlhphKvXjNlhkIBUgjdbKSjstuniBB0 g9C86tjm1br/RwtCOsm/LWErSSTWBt/icbxUD8elmV1Gmodgsb+glIQd3t6ydOAJ gszhMJEdrDtAk68TlaFIiF32vA9hyNNp/ugpScyHYVLfR9do4MVAgbKVS421pYdW OVmMICGAhU55LpUqfU01M2pemzK/NGWa/+56bR23InvtkDBYsUWyHlKYH5GLKe1i QVIA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113683; x=1683200083; bh=Agg80R7HLeQBk xGPOjnuE6+FjRZfmWkjyCEnwDmkCN0=; b=F8V2lsX4hgkxC+4+EFJpCv8CPZDOG fRWXgNm01rIjowwwlb9mA6LHc4HqGdpHH2xZ//t5l5Yb77YguLikTe9ojveTgI2T cezzlkKrEwOH91h4J/3H8YkgmJYc3HunWCLC8AmYrlQ0/iF/IgAaleE5jxw5bh0n Ndf8oqOJ6Dtm3xgfrY7Nur6f2WwXdxlYIgXxKfNsXqmi4GHFajWw9wFmhGaHr8Sb mwdKC5ibC5ntcNKMCHGx5OfSderUXQq6d85+OzZmbFGkIzEx0kWFvSWXC4l8ao3i bt1TB2NVePcIH17k139Ew9kiQmWobXrqGUTeADDewzCz2bhJ/s0HBD1Lw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggedtucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:41 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id 8c588f1e (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:34:01 +0000 (UTC) Date: Wed, 3 May 2023 13:34:40 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 6/8] fetch: move display format parsing into main function Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Parsing the display format happens inside of `display_state_init()`. As we only need to check for a simple config entry, this is a natural location to put this code as it means that display-state logic is neatly contained in a single location. We're about to introduce a output format though that is intended to be parseable by machines, for example inside of a script. In that case it becomes a bit awkward of an interface if you have to call git-fetch(1) with the `fetch.output` config key set. We're thus going to introduce a new `--output-format` switch for git-fetch(1) so that the output format can be configured more directly. This means we'll have to hook parsing of the display format into the command line options parser. Having the code to determine the actual output format scattered across two different sites is hard to reason about though. Refactor the code such that callers are expected to pass the display format that is to be used into `display_state_init()`. This allows us to lift up the code into the main function, where we can then hook it into command line options parser in a follow-up commit. Signed-off-by: Patrick Steinhardt --- builtin/fetch.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 9e7e45344d..e15d43dc1e 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -788,14 +788,13 @@ static int refcol_width(const struct ref *ref, int compact_format) } static void display_state_init(struct display_state *display_state, struct ref *ref_map, - const char *raw_url) + const char *raw_url, enum display_format format) { - const char *format = "full"; int i; memset(display_state, 0, sizeof(*display_state)); - strbuf_init(&display_state->buf, 0); + display_state->format = format; if (raw_url) display_state->url = transport_anonymize_url(raw_url); @@ -812,15 +811,6 @@ static void display_state_init(struct display_state *display_state, struct ref * if (verbosity < 0) return; - git_config_get_string_tmp("fetch.output", &format); - if (!strcasecmp(format, "full")) - display_state->format = DISPLAY_FORMAT_FULL; - else if (!strcasecmp(format, "compact")) - display_state->format = DISPLAY_FORMAT_COMPACT; - else - die(_("invalid value for '%s': '%s'"), - "fetch.output", format); - switch (display_state->format) { case DISPLAY_FORMAT_FULL: case DISPLAY_FORMAT_COMPACT: { @@ -1614,7 +1604,8 @@ static int backfill_tags(struct display_state *display_state, } static int do_fetch(struct transport *transport, - struct refspec *rs) + struct refspec *rs, + enum display_format display_format) { struct ref_transaction *transaction = NULL; struct ref *ref_map = NULL; @@ -1700,7 +1691,7 @@ static int do_fetch(struct transport *transport, if (retcode) goto cleanup; - display_state_init(&display_state, ref_map, transport->url); + display_state_init(&display_state, ref_map, transport->url, display_format); if (atomic_fetch) { transaction = ref_transaction_begin(&err); @@ -2078,7 +2069,8 @@ static inline void fetch_one_setup_partial(struct remote *remote) } static int fetch_one(struct remote *remote, int argc, const char **argv, - int prune_tags_ok, int use_stdin_refspecs) + int prune_tags_ok, int use_stdin_refspecs, + enum display_format display_format) { struct refspec rs = REFSPEC_INIT_FETCH; int i; @@ -2145,7 +2137,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack_atexit); sigchain_push(SIGPIPE, SIG_IGN); - exit_code = do_fetch(gtransport, &rs); + exit_code = do_fetch(gtransport, &rs, display_format); sigchain_pop(SIGPIPE); refspec_clear(&rs); transport_disconnect(gtransport); @@ -2157,6 +2149,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) { int i; const char *bundle_uri; + enum display_format display_format = DISPLAY_FORMAT_UNKNOWN; struct string_list list = STRING_LIST_INIT_DUP; struct remote *remote = NULL; int result = 0; @@ -2183,6 +2176,19 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); + if (display_format == DISPLAY_FORMAT_UNKNOWN) { + const char *format = "full"; + + git_config_get_string_tmp("fetch.output", &format); + if (!strcasecmp(format, "full")) + display_format = DISPLAY_FORMAT_FULL; + else if (!strcasecmp(format, "compact")) + display_format = DISPLAY_FORMAT_COMPACT; + else + die(_("invalid value for '%s': '%s'"), + "fetch.output", format); + } + if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT) recurse_submodules = recurse_submodules_cli; @@ -2311,7 +2317,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } else if (remote) { if (filter_options.choice || has_promisor_remote()) fetch_one_setup_partial(remote); - result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs); + result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs, + display_format); } else { int max_children = max_jobs; From patchwork Wed May 3 11:34:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230066 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9BD09C77B75 for ; Wed, 3 May 2023 11:35:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230089AbjECLfm (ORCPT ); Wed, 3 May 2023 07:35:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46860 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230217AbjECLfW (ORCPT ); Wed, 3 May 2023 07:35:22 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 086166582 for ; Wed, 3 May 2023 04:35:04 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 5CA4B5C00E1; Wed, 3 May 2023 07:34:48 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Wed, 03 May 2023 07:34:48 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113688; x=1683200088; bh=1x rdZlN4Du3VSgtOZ922WQWBlgum9oqu+yitmmDvNXo=; b=hsFPZULbdQ1A3kPZoQ wsMZPcgD/tiys9ZyWN7u5CCKfPCcRovG+qnV1l1Dn0Y2OpNGmuL0tQaaZeJuMUr1 rCxNQ1d/I4EDWa7dNIjSn7ptGLv7cT6EOxY6IGprnETEIGzF8AVaCgXhBkBaDswn anSXMYqzbS4Yv6rvrYMRBCjvq9WTD/pcs2Yhd/a0cvfhVaxxQmtF1x8Ma50GXhrj E0yHn9u+MK2maFrr/jOHqSy+gLA+aO/wi1l2AY7Qw/ejYxqCIHZp8logugwOSnhz ACL8o9s1fx7FZ5H/7wmnZtKnxjJ4CvRpgxwKurjXW6AfnlQHKxRw4vsRlHrKGpqv Gw5g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113688; x=1683200088; bh=1xrdZlN4Du3VS gtOZ922WQWBlgum9oqu+yitmmDvNXo=; b=G5jUd14qYdAgyjE91ph2yo/c6gmq4 tybtHSmGLaEslGjdflkBjNktr/AtGNL47xzUZSUT5K5kBKOx85w965+zYRYwqEEH 78f+l4mtvLdn66I3Yy/VKNG/ayZTtoo4/IlVYHPvKce7qJgRYyVfUP3+Y82uFYxg Ibs4P+s3R5CIlCqYQoovZ6qaHXFEJOTIk6uCFc6Juwyr+QNromXvYRVSZxsaI6RD YZEgRUic3PRkkzaWEAD8NX+dtoOITFzBbr0BBtP1PjwGCOxBfP6Dw3NnYSCtgsFm 49PkW3ycGHvHP0DmtRyKS2aycHHUANBj5imCeB4n3/Y3OW0IZebNK7ZSA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggeduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgepvdenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:46 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id a0bfa84b (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:34:05 +0000 (UTC) Date: Wed, 3 May 2023 13:34:44 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 7/8] fetch: move option related variables into main function Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org The options of git-fetch(1) which we pass to `parse_options()` are declared globally in `builtin/fetch.c`. This means we're forced to use global variables for all the options, which is more likely to cause confusion than explicitly passing state around. Refactor the code to move the options into `cmd_fetch()`. Move variables that were previously forced to be declared globally and which are only used by `cmd_fetch()` into function-local scope. Signed-off-by: Patrick Steinhardt --- builtin/fetch.c | 197 ++++++++++++++++++++++++------------------------ 1 file changed, 100 insertions(+), 97 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index e15d43dc1e..820ec7285c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -75,13 +75,12 @@ static int fetch_prune_tags_config = -1; /* unspecified */ static int prune_tags = -1; /* unspecified */ #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ -static int all, append, dry_run, force, keep, multiple, update_head_ok; +static int append, dry_run, force, keep, update_head_ok; static int write_fetch_head = 1; static int verbosity, deepen_relative, set_upstream, refetch; static int progress = -1; -static int enable_auto_gc = 1; -static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; -static int max_jobs = -1, submodule_fetch_jobs_config = -1; +static int tags = TAGS_DEFAULT, update_shallow, deepen; +static int submodule_fetch_jobs_config = -1; static int fetch_parallel_config = 1; static int atomic_fetch; static enum transport_family family; @@ -92,17 +91,11 @@ static struct string_list deepen_not = STRING_LIST_INIT_NODUP; static struct strbuf default_rla = STRBUF_INIT; static struct transport *gtransport; static struct transport *gsecondary; -static const char *submodule_prefix = ""; static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; -static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; static struct refspec refmap = REFSPEC_INIT_FETCH; static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; static struct string_list server_options = STRING_LIST_INIT_DUP; static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP; -static int fetch_write_commit_graph = -1; -static int stdin_refspecs = 0; -static int negotiate_only; static int git_fetch_config(const char *k, const char *v, void *cb) { @@ -160,92 +153,6 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset return 0; } -static struct option builtin_fetch_options[] = { - OPT__VERBOSITY(&verbosity), - OPT_BOOL(0, "all", &all, - N_("fetch from all remotes")), - OPT_BOOL(0, "set-upstream", &set_upstream, - N_("set upstream for git pull/fetch")), - OPT_BOOL('a', "append", &append, - N_("append to .git/FETCH_HEAD instead of overwriting")), - OPT_BOOL(0, "atomic", &atomic_fetch, - N_("use atomic transaction to update references")), - OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), - N_("path to upload pack on remote end")), - OPT__FORCE(&force, N_("force overwrite of local reference"), 0), - OPT_BOOL('m', "multiple", &multiple, - N_("fetch from multiple remotes")), - OPT_SET_INT('t', "tags", &tags, - N_("fetch all tags and associated objects"), TAGS_SET), - OPT_SET_INT('n', NULL, &tags, - N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), - OPT_INTEGER('j', "jobs", &max_jobs, - N_("number of submodules fetched in parallel")), - OPT_BOOL(0, "prefetch", &prefetch, - N_("modify the refspec to place all refs within refs/prefetch/")), - OPT_BOOL('p', "prune", &prune, - N_("prune remote-tracking branches no longer on remote")), - OPT_BOOL('P', "prune-tags", &prune_tags, - N_("prune local tags no longer on remote and clobber changed tags")), - OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"), - N_("control recursive fetching of submodules"), - PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), - OPT_BOOL(0, "dry-run", &dry_run, - N_("dry run")), - OPT_BOOL(0, "write-fetch-head", &write_fetch_head, - N_("write fetched references to the FETCH_HEAD file")), - OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), - OPT_BOOL('u', "update-head-ok", &update_head_ok, - N_("allow updating of HEAD ref")), - OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), - OPT_STRING(0, "depth", &depth, N_("depth"), - N_("deepen history of shallow clone")), - OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), - N_("deepen history of shallow repository based on time")), - OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), - OPT_INTEGER(0, "deepen", &deepen_relative, - N_("deepen history of shallow clone")), - OPT_SET_INT_F(0, "unshallow", &unshallow, - N_("convert to a complete repository"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "refetch", &refetch, - N_("re-fetch without negotiating common commits"), - 1, PARSE_OPT_NONEG), - { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), - N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, - OPT_CALLBACK_F(0, "recurse-submodules-default", - &recurse_submodules_default, N_("on-demand"), - N_("default for recursive fetching of submodules " - "(lower priority than config files)"), - PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), - OPT_BOOL(0, "update-shallow", &update_shallow, - N_("accept refs that update .git/shallow")), - OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), - N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), - OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), - OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"), - N_("report that we have only objects reachable from this object")), - OPT_BOOL(0, "negotiate-only", &negotiate_only, - N_("do not fetch a packfile; instead, print ancestors of negotiation tips")), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), - OPT_BOOL(0, "auto-maintenance", &enable_auto_gc, - N_("run 'maintenance --auto' after fetching")), - OPT_BOOL(0, "auto-gc", &enable_auto_gc, - N_("run 'maintenance --auto' after fetching")), - OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates, - N_("check for forced-updates on all updated branches")), - OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph, - N_("write the commit-graph after fetching")), - OPT_BOOL(0, "stdin", &stdin_refspecs, - N_("accept refspecs from stdin")), - OPT_END() -}; - static void unlock_pack(unsigned int flags) { if (gtransport) @@ -2147,13 +2054,109 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int cmd_fetch(int argc, const char **argv, const char *prefix) { - int i; const char *bundle_uri; + const char *submodule_prefix = ""; enum display_format display_format = DISPLAY_FORMAT_UNKNOWN; struct string_list list = STRING_LIST_INIT_DUP; struct remote *remote = NULL; + int all = 0, multiple = 0; int result = 0; int prune_tags_ok = 1; + int enable_auto_gc = 1; + int unshallow = 0; + int max_jobs = -1; + int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT; + int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; + int fetch_write_commit_graph = -1; + int stdin_refspecs = 0; + int negotiate_only = 0; + int i; + + struct option builtin_fetch_options[] = { + OPT__VERBOSITY(&verbosity), + OPT_BOOL(0, "all", &all, + N_("fetch from all remotes")), + OPT_BOOL(0, "set-upstream", &set_upstream, + N_("set upstream for git pull/fetch")), + OPT_BOOL('a', "append", &append, + N_("append to .git/FETCH_HEAD instead of overwriting")), + OPT_BOOL(0, "atomic", &atomic_fetch, + N_("use atomic transaction to update references")), + OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), + N_("path to upload pack on remote end")), + OPT__FORCE(&force, N_("force overwrite of local reference"), 0), + OPT_BOOL('m', "multiple", &multiple, + N_("fetch from multiple remotes")), + OPT_SET_INT('t', "tags", &tags, + N_("fetch all tags and associated objects"), TAGS_SET), + OPT_SET_INT('n', NULL, &tags, + N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), + OPT_INTEGER('j', "jobs", &max_jobs, + N_("number of submodules fetched in parallel")), + OPT_BOOL(0, "prefetch", &prefetch, + N_("modify the refspec to place all refs within refs/prefetch/")), + OPT_BOOL('p', "prune", &prune, + N_("prune remote-tracking branches no longer on remote")), + OPT_BOOL('P', "prune-tags", &prune_tags, + N_("prune local tags no longer on remote and clobber changed tags")), + OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"), + N_("control recursive fetching of submodules"), + PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), + OPT_BOOL(0, "dry-run", &dry_run, + N_("dry run")), + OPT_BOOL(0, "write-fetch-head", &write_fetch_head, + N_("write fetched references to the FETCH_HEAD file")), + OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), + OPT_BOOL('u', "update-head-ok", &update_head_ok, + N_("allow updating of HEAD ref")), + OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), + OPT_STRING(0, "depth", &depth, N_("depth"), + N_("deepen history of shallow clone")), + OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), + N_("deepen history of shallow repository based on time")), + OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), + N_("deepen history of shallow clone, excluding rev")), + OPT_INTEGER(0, "deepen", &deepen_relative, + N_("deepen history of shallow clone")), + OPT_SET_INT_F(0, "unshallow", &unshallow, + N_("convert to a complete repository"), + 1, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "refetch", &refetch, + N_("re-fetch without negotiating common commits"), + 1, PARSE_OPT_NONEG), + { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), + N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, + OPT_CALLBACK_F(0, "recurse-submodules-default", + &recurse_submodules_default, N_("on-demand"), + N_("default for recursive fetching of submodules " + "(lower priority than config files)"), + PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), + OPT_BOOL(0, "update-shallow", &update_shallow, + N_("accept refs that update .git/shallow")), + OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), + N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), + OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), + OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), + TRANSPORT_FAMILY_IPV4), + OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), + TRANSPORT_FAMILY_IPV6), + OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"), + N_("report that we have only objects reachable from this object")), + OPT_BOOL(0, "negotiate-only", &negotiate_only, + N_("do not fetch a packfile; instead, print ancestors of negotiation tips")), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), + OPT_BOOL(0, "auto-maintenance", &enable_auto_gc, + N_("run 'maintenance --auto' after fetching")), + OPT_BOOL(0, "auto-gc", &enable_auto_gc, + N_("run 'maintenance --auto' after fetching")), + OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates, + N_("check for forced-updates on all updated branches")), + OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph, + N_("write the commit-graph after fetching")), + OPT_BOOL(0, "stdin", &stdin_refspecs, + N_("accept refspecs from stdin")), + OPT_END() + }; packet_trace_identity("fetch"); From patchwork Wed May 3 11:34:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Steinhardt X-Patchwork-Id: 13230067 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A308CC7EE22 for ; Wed, 3 May 2023 11:35:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230150AbjECLf4 (ORCPT ); Wed, 3 May 2023 07:35:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46428 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230085AbjECLfh (ORCPT ); Wed, 3 May 2023 07:35:37 -0400 Received: from out3-smtp.messagingengine.com (out3-smtp.messagingengine.com [66.111.4.27]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 73DC25FCB for ; Wed, 3 May 2023 04:35:12 -0700 (PDT) Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id 7ACD15C0099; Wed, 3 May 2023 07:34:52 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Wed, 03 May 2023 07:34:52 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pks.im; h=cc:cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:sender :subject:subject:to:to; s=fm3; t=1683113692; x=1683200092; bh=3Y jDYvLU9XfE8G6EKmz34/nv3k4HAre9mb8g1S4TjMM=; b=aHKzkt9W7JvwXEAaAK WNZpAyL9DJtSJ0hu5xG+5nTzBabrx85OVL2T7Kph7lj5fun851PJV+ZPZU/2EKbj BDUYps8MoWZCjxwik8ncuzOkQJQA0IGS+8B6MSTuewZYBXDplhJ/BmI28NHUuaF5 UU/9btKEjJ8s42KCe351DWkQttcjr7vl1vt3OUaJPAnNdxJGApz7lGZajz+nqgte Rd4T4wHnjW6mPXhutwenrRKo4/eSAqxxktx9eIa8IV02qWb5DFpHlveUUdqWzRee T3l7rcdcFkDm3p5xe7TjwqcIl/yiw+amocg8i8A0OojLZ3ndrPZPH6Wm75rjOV3y ARfQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender :x-sasl-enc; s=fm3; t=1683113692; x=1683200092; bh=3YjDYvLU9XfE8 G6EKmz34/nv3k4HAre9mb8g1S4TjMM=; b=CvpP7qZw/Z34aZIq0tKGR68Z+RbpB NdSOKdzmHWU8CxmZWksJsFhYjf1e6AJlOkDLafeUpOKF1HCVN//VdM0i1boZN9wP xWO7hKQPYwRm4XrBjubQYL5/zIPm9xo/3SHfMBY2emtSzHQgyLeNuVc7zKIDH1lO 7d+crLHt/F7Nvk9YINav4zNHE02oixk/g4fpd6OJUaxTFcl/DsNOqnsXjUVylB+2 HY7HZ0A7Fwmmap9OWtCW0b0j6TVeV0tPo7J7l9BSkKggQJt58Xq+Yp4UxrVINxk8 KhyXdTztNPvFmasq6VvAhDpeAkFowDxfm04HR//aGc2PUZccvPt+PMYCA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrfedvkedggeduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepfffhvfevuffkfhggtggujgesghdtreertddtvdenucfhrhhomheprfgrthhr ihgtkhcuufhtvghinhhhrghrughtuceophhssehpkhhsrdhimheqnecuggftrfgrthhtvg hrnhepueektdevtdffveeljeetgfehheeigeekleduvdeffeeghefgledttdehjeelffet necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepphhsse hpkhhsrdhimh X-ME-Proxy: Feedback-ID: i197146af:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 3 May 2023 07:34:50 -0400 (EDT) Received: by pks.im (OpenSMTPD) with ESMTPSA id 74e086f3 (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Wed, 3 May 2023 11:34:09 +0000 (UTC) Date: Wed, 3 May 2023 13:34:48 +0200 From: Patrick Steinhardt To: git@vger.kernel.org Cc: Junio C Hamano , Felipe Contreras , Glen Choo , Jonathan Tan , Jacob Keller Subject: [PATCH v3 8/8] fetch: introduce machine-parseable "porcelain" output format Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org The output of git-fetch(1) is obviously designed for consumption by users, only: we neatly columnize data, we abbreviate reference names, we print neat arrows and we don't provide information about actual object IDs that have changed. This makes the output format basically unusable in the context of scripted invocations of git-fetch(1) that want to learn about the exact changes that the command performs. Introduce a new machine-parseable "porcelain" output format that is supposed to fix this shortcoming. This output format is intended to provide information about every reference that is about to be updated, the old object ID that the reference has been pointing to and the new object ID it will be updated to. Furthermore, the output format provides the same flags as the human-readable format to indicate basic conditions for each reference update like whether it was a fast-forward update, a branch deletion, a rejected update or others. The output format is quite simple: ``` \n ``` We assume two conditions which are generally true: - The old and new object IDs have fixed known widths and cannot contain spaces. - References cannot contain newlines. With these assumptions, the output format becomes unambiguously parseable. Furthermore, given that this output is designed to be consumed by scripts, the machine-readable data is printed to stdout instead of stderr like the human-readable output is. This is mostly done so that other data printed to stderr, like error messages or progress meters, don't interfere with the parseable data. A notable ommission here is that the output format does not include the remote from which a reference was fetched, which might be important information especially in the context of multi-remote fetches. But as such a format would require us to print the remote for every single reference update due to parallelizable fetches it feels wasteful for the most likely usecase, which is when fetching from a single remote. In a similar spirit, a second restriction is that this cannot be used with `--recurse-submodules`. This is because any reference updates would be ambiguous without also printing the repository in which the update happens. Considering that both multi-remote and submodule fetches are rather niche and likely not going to be useful for the majority of usecases these omissions feel acceptable. If usecases for either of these come up in the future though it is easy enough to add a new "porcelain-v2" format that adds this information. Signed-off-by: Patrick Steinhardt --- Documentation/fetch-options.txt | 6 ++ Documentation/git-fetch.txt | 9 +++ builtin/fetch.c | 91 +++++++++++++++++++----- t/t5574-fetch-output.sh | 120 ++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 18 deletions(-) diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 622bd84768..02a5ae21ef 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -78,6 +78,12 @@ linkgit:git-config[1]. --dry-run:: Show what would be done, without making any changes. +--porcelain:: + Print the output to standard output in an easy-to-parse format for + scripts. See section OUTPUT in linkgit:git-fetch[1] for details. ++ +This is incompatible with `--recurse-submodules=[yes|on-demand]`. + ifndef::git-pull[] --[no-]write-fetch-head:: Write the list of remote refs fetched in the `FETCH_HEAD` diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index fba66f1460..f123139c58 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -204,6 +204,15 @@ representing the status of a single ref. Each line is of the form: -> [] ------------------------------- +When using `--porcelain`, the output format is intended to be +machine-parseable. In contrast to the human-readable output formats it +thus prints to standard output instead of standard error. Each line is +of the form: + +------------------------------- + +------------------------------- + The status of up-to-date refs is shown only if the --verbose option is used. diff --git a/builtin/fetch.c b/builtin/fetch.c index 820ec7285c..187c4d373c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -52,6 +52,7 @@ enum display_format { DISPLAY_FORMAT_UNKNOWN = 0, DISPLAY_FORMAT_FULL, DISPLAY_FORMAT_COMPACT, + DISPLAY_FORMAT_PORCELAIN, }; struct display_state { @@ -745,6 +746,9 @@ static void display_state_init(struct display_state *display_state, struct ref * break; } + case DISPLAY_FORMAT_PORCELAIN: + /* We don't need to precompute anything here. */ + break; default: BUG("unexpected display format %d", display_state->format); } @@ -815,8 +819,12 @@ static void print_compact(struct display_state *display_state, static void display_ref_update(struct display_state *display_state, char code, const char *summary, const char *error, const char *remote, const char *local, + const struct object_id *old_oid, + const struct object_id *new_oid, int summary_width) { + FILE *f = stderr; + if (verbosity < 0) return; @@ -849,12 +857,17 @@ static void display_ref_update(struct display_state *display_state, char code, break; } + case DISPLAY_FORMAT_PORCELAIN: + strbuf_addf(&display_state->buf, "%c %s %s %s", code, + oid_to_hex(old_oid), oid_to_hex(new_oid), local); + f = stdout; + break; default: BUG("unexpected display format %d", display_state->format); }; strbuf_addch(&display_state->buf, '\n'); - fputs(display_state->buf.buf, stderr); + fputs(display_state->buf.buf, f); } static int update_local_ref(struct ref *ref, @@ -872,7 +885,8 @@ static int update_local_ref(struct ref *ref, if (oideq(&ref->old_oid, &ref->new_oid)) { if (verbosity > 0) display_ref_update(display_state, '=', _("[up to date]"), NULL, - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 0; } @@ -885,7 +899,8 @@ static int update_local_ref(struct ref *ref, */ display_ref_update(display_state, '!', _("[rejected]"), _("can't fetch into checked-out branch"), - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 1; } @@ -896,12 +911,14 @@ static int update_local_ref(struct ref *ref, r = s_update_ref("updating tag", ref, transaction, 0); display_ref_update(display_state, r ? '!' : 't', _("[tag update]"), r ? _("unable to update local ref") : NULL, - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return r; } else { display_ref_update(display_state, '!', _("[rejected]"), _("would clobber existing tag"), - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 1; } } @@ -934,7 +951,8 @@ static int update_local_ref(struct ref *ref, r = s_update_ref(msg, ref, transaction, 0); display_ref_update(display_state, r ? '!' : '*', what, r ? _("unable to update local ref") : NULL, - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return r; } @@ -956,7 +974,8 @@ static int update_local_ref(struct ref *ref, r = s_update_ref("fast-forward", ref, transaction, 1); display_ref_update(display_state, r ? '!' : ' ', quickref.buf, r ? _("unable to update local ref") : NULL, - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); strbuf_release(&quickref); return r; } else if (force || ref->force) { @@ -968,12 +987,14 @@ static int update_local_ref(struct ref *ref, r = s_update_ref("forced-update", ref, transaction, 1); display_ref_update(display_state, r ? '!' : '+', quickref.buf, r ? _("unable to update local ref") : _("forced update"), - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); strbuf_release(&quickref); return r; } else { display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"), - remote_ref->name, ref->name, summary_width); + remote_ref->name, ref->name, + &ref->old_oid, &ref->new_oid, summary_width); return 1; } } @@ -1214,7 +1235,9 @@ static int store_updated_refs(struct display_state *display_state, display_ref_update(display_state, '*', *kind ? kind : "branch", NULL, rm->name, - "FETCH_HEAD", summary_width); + "FETCH_HEAD", + &rm->new_oid, &rm->old_oid, + summary_width); } } } @@ -1354,6 +1377,7 @@ static int prune_refs(struct display_state *display_state, for (ref = stale_refs; ref; ref = ref->next) { display_ref_update(display_state, '-', _("[deleted]"), NULL, _("(none)"), ref->name, + &ref->new_oid, &ref->old_oid, summary_width); warn_dangling_symref(stderr, dangling_msg, ref->name); } @@ -1786,7 +1810,8 @@ static int add_remote_or_group(const char *name, struct string_list *list) return 1; } -static void add_options_to_argv(struct strvec *argv) +static void add_options_to_argv(struct strvec *argv, + enum display_format format) { if (dry_run) strvec_push(argv, "--dry-run"); @@ -1822,6 +1847,8 @@ static void add_options_to_argv(struct strvec *argv) strvec_push(argv, "--ipv6"); if (!write_fetch_head) strvec_push(argv, "--no-write-fetch-head"); + if (format == DISPLAY_FORMAT_PORCELAIN) + strvec_pushf(argv, "--porcelain"); } /* Fetch multiple remotes in parallel */ @@ -1830,6 +1857,7 @@ struct parallel_fetch_state { const char **argv; struct string_list *remotes; int next, result; + enum display_format format; }; static int fetch_next_remote(struct child_process *cp, @@ -1849,7 +1877,7 @@ static int fetch_next_remote(struct child_process *cp, strvec_push(&cp->args, remote); cp->git_cmd = 1; - if (verbosity >= 0) + if (verbosity >= 0 && state->format != DISPLAY_FORMAT_PORCELAIN) printf(_("Fetching %s\n"), remote); return 1; @@ -1881,7 +1909,8 @@ static int fetch_finished(int result, struct strbuf *out, return 0; } -static int fetch_multiple(struct string_list *list, int max_children) +static int fetch_multiple(struct string_list *list, int max_children, + enum display_format format) { int i, result = 0; struct strvec argv = STRVEC_INIT; @@ -1894,10 +1923,10 @@ static int fetch_multiple(struct string_list *list, int max_children) strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc", "--no-write-commit-graph", NULL); - add_options_to_argv(&argv); + add_options_to_argv(&argv, format); if (max_children != 1 && list->nr != 1) { - struct parallel_fetch_state state = { argv.v, list, 0, 0 }; + struct parallel_fetch_state state = { argv.v, list, 0, 0, format }; const struct run_process_parallel_opts opts = { .tr2_category = "fetch", .tr2_label = "parallel/fetch", @@ -1921,7 +1950,7 @@ static int fetch_multiple(struct string_list *list, int max_children) strvec_pushv(&cmd.args, argv.v); strvec_push(&cmd.args, name); - if (verbosity >= 0) + if (verbosity >= 0 && format != DISPLAY_FORMAT_PORCELAIN) printf(_("Fetching %s\n"), name); cmd.git_cmd = 1; if (run_command(&cmd)) { @@ -2052,6 +2081,13 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, return exit_code; } +static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset) +{ + enum display_format *format = opt->value; + *format = DISPLAY_FORMAT_PORCELAIN; + return 0; +} + int cmd_fetch(int argc, const char **argv, const char *prefix) { const char *bundle_uri; @@ -2104,6 +2140,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), OPT_BOOL(0, "dry-run", &dry_run, N_("dry run")), + OPT_CALLBACK_F(0, "porcelain", &display_format, NULL, N_("machine-readable output"), + PARSE_OPT_NOARG|PARSE_OPT_NONEG, opt_parse_porcelain), OPT_BOOL(0, "write-fetch-head", &write_fetch_head, N_("write fetched references to the FETCH_HEAD file")), OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), @@ -2222,6 +2260,23 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) fetch_config_from_gitmodules(sfjc, rs); } + if (display_format == DISPLAY_FORMAT_PORCELAIN) { + switch (recurse_submodules_cli) { + case RECURSE_SUBMODULES_OFF: + case RECURSE_SUBMODULES_DEFAULT: + /* + * Reference updates in submodules would be ambiguous + * in porcelain mode, so we reject this combination. + */ + recurse_submodules = RECURSE_SUBMODULES_OFF; + break; + + default: + die(_("options '%s' and '%s' cannot be used together"), + "--porcelain", "--recurse-submodules"); + } + } + if (negotiate_only && !negotiation_tip.nr) die(_("--negotiate-only needs one or more --negotiation-tip=*")); @@ -2341,7 +2396,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) max_children = fetch_parallel_config; /* TODO should this also die if we have a previous partial-clone? */ - result = fetch_multiple(&list, max_children); + result = fetch_multiple(&list, max_children, display_format); } @@ -2363,7 +2418,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (max_children < 0) max_children = fetch_parallel_config; - add_options_to_argv(&options); + add_options_to_argv(&options, display_format); result = fetch_submodules(the_repository, &options, submodule_prefix, diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh index 6e0f7e0046..18949e9323 100755 --- a/t/t5574-fetch-output.sh +++ b/t/t5574-fetch-output.sh @@ -56,6 +56,102 @@ test_expect_success 'fetch compact output' ' test_cmp expect actual ' +test_expect_success 'fetch porcelain output' ' + test_when_finished "rm -rf porcelain" && + + # Set up a bunch of references that we can use to demonstrate different + # kinds of flag symbols in the output format. + MAIN_OLD=$(git rev-parse HEAD) && + git branch "fast-forward" && + git branch "deleted-branch" && + git checkout -b force-updated && + test_commit --no-tag force-update-old && + FORCE_UPDATED_OLD=$(git rev-parse HEAD) && + git checkout main && + + # Clone and pre-seed the repositories. We fetch references into two + # namespaces so that we can test that rejected and force-updated + # references are reported properly. + refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" && + git clone . porcelain && + git -C porcelain fetch origin $refspecs && + + # Now that we have set up the client repositories we can change our + # local references. + git branch new-branch && + git branch -d deleted-branch && + git checkout fast-forward && + test_commit --no-tag fast-forward-new && + FAST_FORWARD_NEW=$(git rev-parse HEAD) && + git checkout force-updated && + git reset --hard HEAD~ && + test_commit --no-tag force-update-new && + FORCE_UPDATED_NEW=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch + - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch + $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward + ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated + * $ZERO_OID $MAIN_OLD refs/unforced/new-branch + $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward + + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated + * $ZERO_OID $MAIN_OLD refs/forced/new-branch + $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward + + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated + * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch + EOF + + # Execute a dry-run fetch first. We do this to assert that the dry-run + # and non-dry-run fetches produces the same output. Execution of the + # fetch is expected to fail as we have a rejected reference update. + test_must_fail git -C porcelain fetch \ + --porcelain --dry-run --prune origin $refspecs >actual && + test_cmp expect actual && + + # And now we perform a non-dry-run fetch. + test_must_fail git -C porcelain fetch \ + --porcelain --prune origin $refspecs >actual 2>stderr && + test_cmp expect actual && + test_must_be_empty stderr +' + +test_expect_success 'fetch porcelain with multiple remotes' ' + test_when_finished "rm -rf porcelain" && + + git clone . porcelain && + git -C porcelain remote add second-remote "$PWD" && + git -C porcelain fetch second-remote && + + test_commit --no-tag multi-commit && + old_commit=$(git rev-parse HEAD~) && + new_commit=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + $old_commit $new_commit refs/remotes/origin/force-updated + $old_commit $new_commit refs/remotes/second-remote/force-updated + EOF + + git -C porcelain fetch --porcelain --all >actual 2>stderr && + test_cmp expect actual && + test_must_be_empty stderr +' + +test_expect_success 'fetch porcelain refuses to work with submodules' ' + test_when_finished "rm -rf porcelain" && + + cat >expect <<-EOF && + fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together + EOF + + git init porcelain && + test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr && + test_cmp expect stderr && + + test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr && + test_cmp expect stderr +' + test_expect_success 'fetch output with HEAD' ' test_when_finished "rm -rf head" && git clone . head && @@ -85,6 +181,30 @@ test_expect_success 'fetch output with HEAD' ' test_cmp expect actual.err ' +test_expect_success 'fetch porcelain output with HEAD' ' + test_when_finished "rm -rf head" && + git clone . head && + COMMIT_ID=$(git rev-parse HEAD) && + + git -C head fetch --porcelain --dry-run origin HEAD >actual && + cat >expect <<-EOF && + * $ZERO_OID $COMMIT_ID FETCH_HEAD + EOF + test_cmp expect actual && + + git -C head fetch --porcelain origin HEAD >actual && + test_cmp expect actual && + + git -C head fetch --porcelain --dry-run origin HEAD:foo >actual && + cat >expect <<-EOF && + * $ZERO_OID $COMMIT_ID refs/heads/foo + EOF + test_cmp expect actual && + + git -C head fetch --porcelain origin HEAD:foo >actual && + test_cmp expect actual +' + test_expect_success '--no-show-forced-updates' ' mkdir forced-updates && (