From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-3.3 required=3.0 tests=AWL,BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,LOTS_OF_MONEY, MAILING_LIST_MULTI,MONEY_NOHTML,RCVD_IN_DNSWL_MED,SPF_HELO_NONE, SPF_PASS shortcircuit=no autolearn=no autolearn_force=no version=3.4.6 Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org [IPv6:2604:1380:45e3:2400::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id A14491F44D for ; Wed, 10 Apr 2024 09:56:24 +0000 (UTC) Authentication-Results: dcvr.yhbt.net; dkim=pass (2048-bit key; unprotected) header.d=archibald.dev header.i=@archibald.dev header.a=rsa-sha256 header.s=protonmail3 header.b=CMDBFWvX; dkim-atps=neutral Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sv.mirrors.kernel.org (Postfix) with ESMTPS id 81B5E282130 for ; Wed, 10 Apr 2024 09:56:24 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2907B158205; Wed, 10 Apr 2024 09:56:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=archibald.dev header.i=@archibald.dev header.b="CMDBFWvX" Received: from mail-4018.proton.ch (mail-4018.proton.ch [185.70.40.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F73A8BE8 for ; Wed, 10 Apr 2024 09:56:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.40.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712742964; cv=none; b=jVkNmLK5zaZJc964rhBiNey7TKOxmRyap2kfRQ6+nlgNMt619FBNxtIDpaLtqyD3d4aFADteK6/dRNyhqybCcZXywiYb5A4B1G7P1s5MZqLclCLcedkMBMfomQoAwHSYr7p9Y1Yciyrex6RSp4r+vHQs3e4mTfd+AX8vLscFepY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1712742964; c=relaxed/simple; bh=Re5XJaOtWNr1Takorx92HfSH+qzhR01lsbCg/GYSprs=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=d7ir94XTiVQY0UiN0L5cWYKLX5uj0skMu7I+e0ZTUGZaz9w44PhaDmP9hO0IGSHvxe7oOQ52eg32O0mGZyGC/Vykl5elitgBqCFMtvH+BAcbZg0mGc1Wlhv2B9eI/qkYKsU2zQSfu8eroY3DDe/1Juhqhuk7qQ/b/tzatt0TtXQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=archibald.dev; spf=pass smtp.mailfrom=archibald.dev; dkim=pass (2048-bit key) header.d=archibald.dev header.i=@archibald.dev header.b=CMDBFWvX; arc=none smtp.client-ip=185.70.40.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=archibald.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=archibald.dev DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=archibald.dev; s=protonmail3; t=1712742959; x=1713002159; bh=monV4BdOZ+dINwnH/aA3DMNNJNEPQh+LuAqPLhrpNzE=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=CMDBFWvXHwYExkzFhgaEbTXeYxqjNFcJE5zg99VskLpzISzLmhGIb9z973Wa9Ul8z Ux7fc8ejZxO/0p2pvENVr7decmCaDG3y80tPrZux/hMNrvT1xgDxuNMoTTen7pSKvL Wt8aiKrLkPUHjE2wLd6z7zdFUvOyIQpvvSC1GPtorC9rCOmwslHiwva488ylVzszhu QE7Y3W7awmsEfJgITaTs5k6NUqxdnnmACTnqKV/vEybdP60pSemSy2rGzzGbXHMMzP PYvILK7JfNJqIG6tBEZFGtFXP3sNivVRuQdf1GqK9hjo8i8Y61Tu13xuTCc6Y7qb2C oo6u11igwqtMA== Date: Wed, 10 Apr 2024 09:55:41 +0000 To: git@vger.kernel.org From: Thalia Archibald Cc: Patrick Steinhardt , Chris Torek , Elijah Newren , Thalia Archibald Subject: [PATCH v3 3/8] fast-import: allow unquoted empty path for root Message-ID: <39879d0a6627b62f685b38b9afcfe19e4c14c38e.1712741871.git.thalia@archibald.dev> In-Reply-To: References: <20240322000304.76810-1-thalia@archibald.dev> Feedback-ID: 63908566:user:proton Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Ever since filerename was added in f39a946a1f (Support wholesale directory renames in fast-import, 2007-07-09) and filecopy in b6f3481bb4 (Teach fast-import to recursively copy files/directories, 2007-07-15), both have produced an error when the destination path is empty. Later, when support for targeting the root directory with an empty string was added in 2794ad5244 (fast-import: Allow filemodify to set the root, 2010-10-10), this had the effect of allowing the quoted empty string (`""`), but forbidding its unquoted variant (``). This seems to have been intended as simple data validation for parsing two paths, rather than a syntax restriction, because it was not extended to the other operations. All other occurrences of paths (in filemodify, filedelete, the source of filecopy and filerename, and ls) allow both. For most of this feature's lifetime, the documentation has not prescribed the use of quoted empty strings. In e5959106d6 (Documentation/fast-import: put explanation of M 040000 "" in context, 2011-01-15), its documentation was changed from =E2=80=9C`` = may also be an empty string (`""`) to specify the root of the tree=E2=80=9D to = =E2=80=9CThe root of the tree can be represented by an empty string as ``=E2=80= =9D. Thus, we can assume that some front-ends have depended on this behavior. Remove this restriction for the destination paths of filecopy and filerename and change tests targeting the root to test `""` and ``. Signed-off-by: Thalia Archibald --- builtin/fast-import.c | 3 - t/t9300-fast-import.sh | 363 +++++++++++++++++++++-------------------- 2 files changed, 190 insertions(+), 176 deletions(-) diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 8f6312fbaf..0da7e8a5a5 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -2419,9 +2419,6 @@ static void file_change_cr(const char *p, struct bran= ch *b, int rename) =20 =09strbuf_reset(&source); =09parse_path_space(&source, p, &p, "source"); - -=09if (!*p) -=09=09die("Missing dest: %s", command_buf.buf); =09strbuf_reset(&dest); =09parse_path_eol(&dest, p, "dest"); =20 diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index de2f1304e8..13f98e6688 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -1059,30 +1059,33 @@ test_expect_success 'M: rename subdirectory to new = subdirectory' ' =09compare_diff_raw expect actual ' =20 -test_expect_success 'M: rename root to subdirectory' ' -=09cat >input <<-INPUT_END && -=09commit refs/heads/M4 -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <input <<-INPUT_END && +=09=09commit refs/heads/M4 +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data <expect <<-EOF && -=09:100644 100644 $oldf $oldf R100=09file2/oldf=09sub/file2/oldf -=09:100755 100755 $f4id $f4id R100=09file4=09sub/file4 -=09:100755 100755 $newf $newf R100=09i/am/new/to/you=09sub/i/am/new/to/you -=09:100755 100755 $f6id $f6id R100=09newdir/exec.sh=09sub/newdir/exec.sh -=09:100644 100644 $f5id $f5id R100=09newdir/interesting=09sub/newdir/inter= esting -=09EOF -=09git fast-import actual && -=09compare_diff_raw expect actual -' +=09=09cat >expect <<-EOF && +=09=09:100644 100644 $oldf $oldf R100=09file2/oldf=09sub/file2/oldf +=09=09:100755 100755 $f4id $f4id R100=09file4=09sub/file4 +=09=09:100755 100755 $newf $newf R100=09i/am/new/to/you=09sub/i/am/new/to/= you +=09=09:100755 100755 $f6id $f6id R100=09newdir/exec.sh=09sub/newdir/exec.s= h +=09=09:100644 100644 $f5id $f5id R100=09newdir/interesting=09sub/newdir/in= teresting +=09=09EOF +=09=09git fast-import actual && +=09=09compare_diff_raw expect actual +=09' +done =20 ### ### series N @@ -1259,49 +1262,52 @@ test_expect_success PIPE 'N: empty directory reads = as missing' ' =09test_cmp expect actual ' =20 -test_expect_success 'N: copy root directory by tree hash' ' -=09cat >expect <<-EOF && -=09:100755 000000 $newf $zero D=09file3/newf -=09:100644 000000 $oldf $zero D=09file3/oldf -=09EOF -=09root=3D$(git rev-parse refs/heads/branch^0^{tree}) && -=09cat >input <<-INPUT_END && -=09commit refs/heads/N6 -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <expect <<-EOF && +=09=09:100755 000000 $newf $zero D=09file3/newf +=09=09:100644 000000 $oldf $zero D=09file3/oldf +=09=09EOF +=09=09root_tree=3D$(git rev-parse refs/heads/branch^0^{tree}) && +=09=09cat >input <<-INPUT_END && +=09=09commit refs/heads/N6 +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data <actual && -=09compare_diff_raw expect actual -' +=09=09from refs/heads/branch^0 +=09=09M 040000 $root_tree $root +=09=09INPUT_END +=09=09git fast-import actual && +=09=09compare_diff_raw expect actual +=09' =20 -test_expect_success 'N: copy root by path' ' -=09cat >expect <<-EOF && -=09:100755 100755 $newf $newf C100=09file2/newf=09oldroot/file2/newf -=09:100644 100644 $oldf $oldf C100=09file2/oldf=09oldroot/file2/oldf -=09:100755 100755 $f4id $f4id C100=09file4=09oldroot/file4 -=09:100755 100755 $f6id $f6id C100=09newdir/exec.sh=09oldroot/newdir/exec.= sh -=09:100644 100644 $f5id $f5id C100=09newdir/interesting=09oldroot/newdir/i= nteresting -=09EOF -=09cat >input <<-INPUT_END && -=09commit refs/heads/N-copy-root-path -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <expect <<-EOF && +=09=09:100755 100755 $newf $newf C100=09file2/newf=09oldroot/file2/newf +=09=09:100644 100644 $oldf $oldf C100=09file2/oldf=09oldroot/file2/oldf +=09=09:100755 100755 $f4id $f4id C100=09file4=09oldroot/file4 +=09=09:100755 100755 $f6id $f6id C100=09newdir/exec.sh=09oldroot/newdir/ex= ec.sh +=09=09:100644 100644 $f5id $f5id C100=09newdir/interesting=09oldroot/newdi= r/interesting +=09=09EOF +=09=09cat >input <<-INPUT_END && +=09=09commit refs/heads/N-copy-root-path +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data <actua= l && -=09compare_diff_raw expect actual -' +=09=09from refs/heads/branch^0 +=09=09C $root oldroot +=09=09INPUT_END +=09=09git fast-import ac= tual && +=09=09compare_diff_raw expect actual +=09' +done =20 test_expect_success 'N: delete directory by copying' ' =09cat >expect <<-\EOF && @@ -1431,98 +1437,102 @@ test_expect_success 'N: reject foo/ syntax in ls a= rgument' ' =09INPUT_END ' =20 -test_expect_success 'N: copy to root by id and modify' ' -=09echo "hello, world" >expect.foo && -=09echo hello >expect.bar && -=09git fast-import <<-SETUP_END && -=09commit refs/heads/N7 -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <expect.foo && +=09=09echo hello >expect.bar && +=09=09git fast-import <<-SETUP_END && +=09=09commit refs/heads/N7 +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data < $GIT_COMMITTER_DAT= E -=09data < $GIT_COMMITTER_= DATE +=09=09data <actual.foo && -=09git show N8:foo/bar >actual.bar && -=09test_cmp expect.foo actual.foo && -=09test_cmp expect.bar actual.bar -' +=09=09M 040000 $tree $root +=09=09M 644 inline foo/foo +=09=09data <actual.foo && +=09=09git show N8:foo/bar >actual.bar && +=09=09test_cmp expect.foo actual.foo && +=09=09test_cmp expect.bar actual.bar +=09' =20 -test_expect_success 'N: extract subtree' ' -=09branch=3D$(git rev-parse --verify refs/heads/branch^{tree}) && -=09cat >input <<-INPUT_END && -=09commit refs/heads/N9 -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <input <<-INPUT_END && +=09=09commit refs/heads/N9 +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data <expect.baz && -=09echo hello, world >expect.qux && -=09git fast-import <<-SETUP_END && -=09commit refs/heads/N10 -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <expect.baz && +=09=09echo hello, world >expect.qux && +=09=09git fast-import <<-SETUP_END && +=09=09commit refs/heads/N10 +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data < $GIT_COMMITTER_DAT= E -=09data < $GIT_COMMITTER_= DATE +=09=09data <actual.baz && -=09git show N11:bar/qux >actual.qux && -=09git show N11:bar/quux >actual.quux && -=09test_cmp expect.baz actual.baz && -=09test_cmp expect.qux actual.qux && -=09test_cmp expect.qux actual.quux' +=09=09M 040000 $tree $root +=09=09M 100644 inline foo/bar/qux +=09=09data <actual.baz && +=09=09git show N11:bar/qux >actual.qux && +=09=09git show N11:bar/quux >actual.quux && +=09=09test_cmp expect.baz actual.baz && +=09=09test_cmp expect.qux actual.qux && +=09=09test_cmp expect.qux actual.quux +=09' +done =20 ### ### series O @@ -3067,6 +3077,7 @@ test_expect_success 'S: ls with garbage after sha1 mu= st fail' ' # There are two sorts of ways a path can be parsed, depending on whether i= t is # the last field on the line. Additionally, ls without a has a s= pecial # case. Test every occurrence of in the grammar against every error= case. +# Paths for the root (empty strings) are tested elsewhere. # =20 # @@ -3321,16 +3332,19 @@ test_path_eol_quoted_fail 'ls (without dataref in c= ommit)' 'ls ' path ### # Setup is carried over from series S. =20 -test_expect_success 'T: ls root tree' ' -=09sed -e "s/Z\$//" >expect <<-EOF && -=09040000 tree $(git rev-parse S^{tree})=09Z -=09EOF -=09sha1=3D$(git rev-parse --verify S) && -=09git fast-import --import-marks=3Dmarks <<-EOF >actual && -=09ls $sha1 "" -=09EOF -=09test_cmp expect actual -' +for root in '""' '' +do +=09test_expect_success "T: ls root ($root) tree" ' +=09=09sed -e "s/Z\$//" >expect <<-EOF && +=09=09040000 tree $(git rev-parse S^{tree})=09Z +=09=09EOF +=09=09sha1=3D$(git rev-parse --verify S) && +=09=09git fast-import --import-marks=3Dmarks <<-EOF >actual && +=09=09ls $sha1 $root +=09=09EOF +=09=09test_cmp expect actual +=09' +done =20 test_expect_success 'T: delete branch' ' =09git branch to-delete && @@ -3432,30 +3446,33 @@ test_expect_success 'U: validate directory delete r= esult' ' =09compare_diff_raw expect actual ' =20 -test_expect_success 'U: filedelete root succeeds' ' -=09cat >input <<-INPUT_END && -=09commit refs/heads/U -=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DAT= E -=09data <input <<-INPUT_END && +=09=09commit refs/heads/U-delete-root +=09=09committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_= DATE +=09=09data <expect <<-EOF && -=09:100644 000000 $f7id $ZERO_OID D=09hello.c -=09EOF +=09test_expect_success "U: validate root ($root) delete result" ' +=09=09cat >expect <<-EOF && +=09=09:100644 000000 $f7id $ZERO_OID D=09hello.c +=09=09EOF =20 -=09git diff-tree -M -r U^1 U >actual && +=09=09git diff-tree -M -r U U-delete-root >actual && =20 -=09compare_diff_raw expect actual -' +=09=09compare_diff_raw expect actual +=09' +done =20 ### ### series V (checkpoint) --=20 2.44.0