From patchwork Mon Jan 29 11:35:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13535417 Received: from mail-ej1-f51.google.com (mail-ej1-f51.google.com [209.85.218.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A63E354FBB for ; Mon, 29 Jan 2024 11:35:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528136; cv=none; b=J845Wf9RxnVHNkNyTLXNCJVg21sdW14ZwK956JnUYKnFcStbjNjl+6lyt4/LEB7vVRQGo0DthSavyV4IwimdJ91CFIf7nv9eQGiNZL71PusB0zw3wrlJEoPNS/ZpFHFlaTkqiPGF9DTqq9glDzzU80eaOmF9M/A91RP5BiSgWe4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528136; c=relaxed/simple; bh=xLRzHVxAb6yE48U+yCsGxkaSKBAxWnHRv+3oV+3kC2Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pWB+nlpN7RKKQhp5iwtrvJ7iOGeY1p8rMVjGqAFdE8E/yDbhqMQz3gVAtE83vW/vsv/VPCDKKqOx1iXbs2ZOi8vSiJiwd0JXq233sJ8nWvBNyv/MdafKw5+ib0sFhsyBD95xArb1xvE8xVmDlz40YJM+lL9ZHdI5c7h7zk536H0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=W72Jmr8l; arc=none smtp.client-ip=209.85.218.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="W72Jmr8l" Received: by mail-ej1-f51.google.com with SMTP id a640c23a62f3a-a349ed467d9so273214566b.1 for ; Mon, 29 Jan 2024 03:35:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1706528132; x=1707132932; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=0QdqheKohmNqkn6q3r847fLESw/iqNJvvwGo0uDUC7k=; b=W72Jmr8lvG49WncCKTlSTLqX1CIZAKYjqWhPSdc7nqVSpKoLVgJOLpJH9W9+YaK6On UNh4HYqqIdDAyTdc691zy7P2R70YT3UnQV2UEG9wBYSemlXTFu9COPin+o2Yv19//wDk sks7CzRJXyT8jIfQ2cfVPvuMKw5K1Fki2qGrWETzCYVvofTfrB9RewEu01OKxYc4f8bu scj34P+uKMESLvvtfMv/xMAz25duey8srnyk6IkVcObldGjpasBwK+ZQ93iiYF68G+1E +nSchLYjubPxmoo9nv0D1I7cmRBnF0vlrVrB322vYRpPtVIGI/nCMGZAD2bO1gnF102W rhjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706528132; x=1707132932; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=0QdqheKohmNqkn6q3r847fLESw/iqNJvvwGo0uDUC7k=; b=xO+auvtePxwC6qqiLcpe5vgGLJ7P8wXWp5zu01oLXuHy7xGYdYriv/ST+8cMTZw38v qREAmoow1dRTSP6TiPhsLZIEjUPFvKal1VJFQLYrstfItOi5NeGlhttwQsdXKTpF0mMT QuUnEGSgisk13ldH1aRyrxhITDrZS1yJyyBqeekmMNqHreOFok1iMpryVzQk40bhQiPP HRfs4JXLDFAE1uxVTTLy75R0q2EXS4z6QzdgTN5QxPLekFcCoMq67vhxiPyZV7JeLF2j UGmrrnuRrMsuS1xCcv9y34X5JTihnY2nL5duipNMqKM6poDfXhPQdM12o3iN4NqCokhG GRaA== X-Gm-Message-State: AOJu0Yx+1DcTZCdSXbAIBDWLzpzWVD8MhkK+JRHn8fPCpwn9ATWTSDX+ YSilVxKy0nMtrV0whIuNnJh65Z+noRcOMg6d4trdiR4ijSqH/vrHpKcOAXoD X-Google-Smtp-Source: AGHT+IG6aNL+BREbZaI41O/8NiX5vYJztTOr2+HkfWnWnCFYGwJ5J7MpZjEphLmxBI8qlKhlYYx54Q== X-Received: by 2002:a17:906:17d6:b0:a35:6767:855c with SMTP id u22-20020a17090617d600b00a356767855cmr2856549eje.7.1706528132361; Mon, 29 Jan 2024 03:35:32 -0800 (PST) Received: from laptop.fritz.box ([2a02:2455:826e:4900:74c1:f49b:306a:744b]) by smtp.gmail.com with ESMTPSA id y9-20020a170906070900b00a35a3e2b90asm1370325ejb.149.2024.01.29.03.35.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jan 2024 03:35:32 -0800 (PST) From: Karthik Nayak To: git@vger.kernel.org Cc: gitster@pobox.com, ps@pks.im, phillip.wood123@gmail.com, Karthik Nayak Subject: [PATCH v3 1/4] refs: introduce `is_pseudoref()` and `is_headref()` Date: Mon, 29 Jan 2024 12:35:24 +0100 Message-ID: <20240129113527.607022-2-karthik.188@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240129113527.607022-1-karthik.188@gmail.com> References: <20240119142705.139374-1-karthik.188@gmail.com> <20240129113527.607022-1-karthik.188@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce two new functions `is_pseudoref()` and `is_headref()`. This provides the necessary functionality for us to add pseudorefs and HEAD to the loose ref cache in the files backend, allowing us to build tooling to print these refs. The `is_pseudoref()` function internally calls `is_pseudoref_syntax()` but adds onto it by also checking to ensure that the pseudoref either ends with a "_HEAD" suffix or matches a list of exceptions. After which we also parse the contents of the pseudoref to ensure that it conforms to the ref format. We cannot directly add the new syntax checks to `is_pseudoref_syntax()` because the function is also used by `is_current_worktree_ref()` and making it stricter to match only known pseudorefs might have unintended consequences due to files like 'BISECT_START' which isn't a pseudoref but sometimes contains object ID. Keeping this in mind, we leave `is_pseudoref_syntax()` as is and create `is_pseudoref()` which is stricter. Ideally we'd want to move the new syntax checks to `is_pseudoref_syntax()` but a prerequisite for this would be to actually remove the exception list by converting those pseudorefs to also contain a '_HEAD' suffix and perhaps move bisect related files like 'BISECT_START' to a new directory similar to the 'rebase-merge' directory. Signed-off-by: Karthik Nayak --- refs.c | 39 +++++++++++++++++++++++++++++++++++++++ refs.h | 3 +++ 2 files changed, 42 insertions(+) diff --git a/refs.c b/refs.c index 20e8f1ff1f..559f5aeea8 100644 --- a/refs.c +++ b/refs.c @@ -859,6 +859,45 @@ static int is_pseudoref_syntax(const char *refname) return 1; } +int is_pseudoref(struct ref_store *refs, const char *refname) +{ + static const char *const irregular_pseudorefs[] = { + "AUTO_MERGE", + "BISECT_EXPECTED_REV", + "NOTES_MERGE_PARTIAL", + "NOTES_MERGE_REF", + "MERGE_AUTOSTASH", + }; + struct object_id oid; + size_t i; + + if (!is_pseudoref_syntax(refname)) + return 0; + + if (ends_with(refname, "_HEAD")) { + read_ref_full(refname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + &oid, NULL); + return !is_null_oid(&oid); + } + + for (i = 0; i < ARRAY_SIZE(irregular_pseudorefs); i++) + if (!strcmp(refname, irregular_pseudorefs[i])) { + read_ref_full(refname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + &oid, NULL); + return !is_null_oid(&oid); + } + + return 0; +} + +int is_headref(struct ref_store *refs, const char *refname) +{ + if (!strcmp(refname, "HEAD")) + return refs_ref_exists(refs, refname); + + return 0; +} + static int is_current_worktree_ref(const char *ref) { return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref); } diff --git a/refs.h b/refs.h index 11b3b6ccea..46b8085d63 100644 --- a/refs.h +++ b/refs.h @@ -1021,4 +1021,7 @@ extern struct ref_namespace_info ref_namespace[NAMESPACE__COUNT]; */ void update_ref_namespace(enum ref_namespace namespace, char *ref); +int is_pseudoref(struct ref_store *refs, const char *refname); +int is_headref(struct ref_store *refs, const char *refname); + #endif /* REFS_H */ From patchwork Mon Jan 29 11:35:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13535418 Received: from mail-ej1-f46.google.com (mail-ej1-f46.google.com [209.85.218.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0BCAC5BAD2 for ; Mon, 29 Jan 2024 11:35:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528137; cv=none; b=BWzzZw9FgzMpWlmynihEgynlubX4nEo/UzMB5wCUVtXDeUY50DH1gOohzi5clBhDq1z0T1FcizB0LLgSbLnisUU482NofC8p9ho7DtQXfrfyhvFfJGpdwgCH155CmnsCu40Ps0n+m3+Ef6Y0JTQo+e7nqpCUpYFcUupPMLottIY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528137; c=relaxed/simple; bh=HVrGRwlhKtHb+eMYjfeEB+V3IjJZypVoGABR0ohVoQg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VZKoeVL2dgsff7gAk2p3FVmHbof0IqeCDoTmKY5j9fu6t+X7LIXYKLSOPdlUnY6DpfyzgPdPISh1Ixn6M0bnkB7l4HhSizw5JXvdHZp1YdURPu+z2wtN1m3Joih+ouOBLdfv6rAooyHu+xy5mdk26wzJ3OiLpCeAVAWUCO1mc1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=EKNYPeU5; arc=none smtp.client-ip=209.85.218.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="EKNYPeU5" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-a353f5407f1so200359066b.3 for ; Mon, 29 Jan 2024 03:35:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1706528134; x=1707132934; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8FxnNBtkEIr9Vr7LOwdU5jTdmDcPZNIOUxI8CHhGmXU=; b=EKNYPeU5txtKG4crUSnXVZMirECr4l2NTcm+TT2ESpfQGg0FtjGjxvZwbOPdq3YvWN ezu6Wtlc7IQ9+5pa7dy/STlrSleuh2+eKCCAq7E4NkfrkIkQHWH4wN2lgiyH9Xw7TrdZ z5guuosEbQMnJ9670sMVjfgaU/CdXFkzs/a/pCxXQfHnlI++kWXwQX/XuLQCrDd92nI1 BB5P+xkRoLd+iKqHj5FIqyAtA8/rglf9eWsB94zkxNgBB27M3c5P2E7HnYep3+kKz7bF CBRBO2dM5w7vEU3hZ0x9gDsvQpnJhakVgQ2M0PM20QaKRSjInadCnLmBLPL162f3OtJS xcUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706528134; x=1707132934; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8FxnNBtkEIr9Vr7LOwdU5jTdmDcPZNIOUxI8CHhGmXU=; b=leXY3MLv6RoPSrdLBxTQyUtU8hzG3KHZk6AmrZKD3q9HX4NmLb16giSAcKFhntUdIh UfSBWnpioAZo9mnhCAYyAEZD1cyaQ/uVlP355OyFtD5RP+p5sMH9XkT+S++adivt/O38 8lcV4ZiMZ9JwVGw2aXgfJfM2rNCG/69ypJ6r8kLZ7vnGM/vvUhRUajvgcbm3QWtn58yN j/hmaecAGXu2//3itLCBwrxw6jTBNZFfEXk5I1uTYC7phs+Lo7VJXyVEWuFcIc31GauR eSa/O/QRAcG6kuMHKLXq16gcA3yAm0Z075Y3+VJPUeDSUV3KkJRxAyfXvTtE0Z+byrew mruw== X-Gm-Message-State: AOJu0YxK8Io6/uK1a/Ezovi8606Pjp4uKM/wmVA7t8/fszGERbvBinoa i82o2Odf2+gGtcxl7h2tFc0uLGRcKlzLBXkyM7JozTLzWCNbmcJfI+ZFUmSs X-Google-Smtp-Source: AGHT+IHYCKPyJEovQgx4hC9uTe9bBS5mwHP9JwMwfBNtgZZxvUMWn6U1vNtgq0N+vMCC72aRUa2tbw== X-Received: by 2002:a17:906:2b47:b0:a35:7b14:dc50 with SMTP id b7-20020a1709062b4700b00a357b14dc50mr2763256ejg.17.1706528133506; Mon, 29 Jan 2024 03:35:33 -0800 (PST) Received: from laptop.fritz.box ([2a02:2455:826e:4900:74c1:f49b:306a:744b]) by smtp.gmail.com with ESMTPSA id y9-20020a170906070900b00a35a3e2b90asm1370325ejb.149.2024.01.29.03.35.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jan 2024 03:35:32 -0800 (PST) From: Karthik Nayak To: git@vger.kernel.org Cc: gitster@pobox.com, ps@pks.im, phillip.wood123@gmail.com, Karthik Nayak Subject: [PATCH v3 2/4] refs: extract out `loose_fill_ref_dir_regular_file()` Date: Mon, 29 Jan 2024 12:35:25 +0100 Message-ID: <20240129113527.607022-3-karthik.188@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240129113527.607022-1-karthik.188@gmail.com> References: <20240119142705.139374-1-karthik.188@gmail.com> <20240129113527.607022-1-karthik.188@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Extract out the code for adding a single file to the loose ref dir as `loose_fill_ref_dir_regular_file()` from `loose_fill_ref_dir()` in `refs/files-backend.c`. This allows us to use this function independently in the following commits where we add code to also add pseudorefs to the ref dir. Signed-off-by: Karthik Nayak --- refs/files-backend.c | 62 +++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index b288fc97db..22495a4807 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -229,6 +229,38 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir } } +static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs, + const char *refname, + struct ref_dir *dir) +{ + struct object_id oid; + int flag; + + if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING, + &oid, &flag)) { + oidclr(&oid); + flag |= REF_ISBROKEN; + } else if (is_null_oid(&oid)) { + /* + * It is so astronomically unlikely + * that null_oid is the OID of an + * actual object that we consider its + * appearance in a loose reference + * file to be repo corruption + * (probably due to a software bug). + */ + flag |= REF_ISBROKEN; + } + + if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(refname)) + die("loose refname is dangerous: %s", refname); + oidclr(&oid); + flag |= REF_BAD_NAME | REF_ISBROKEN; + } + add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag)); +} + /* * Read the loose references from the namespace dirname into dir * (without recursing). dirname must end with '/'. dir must be the @@ -257,8 +289,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_add(&refname, dirname, dirnamelen); while ((de = readdir(d)) != NULL) { - struct object_id oid; - int flag; unsigned char dtype; if (de->d_name[0] == '.') @@ -274,33 +304,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, create_dir_entry(dir->cache, refname.buf, refname.len)); } else if (dtype == DT_REG) { - if (!refs_resolve_ref_unsafe(&refs->base, - refname.buf, - RESOLVE_REF_READING, - &oid, &flag)) { - oidclr(&oid); - flag |= REF_ISBROKEN; - } else if (is_null_oid(&oid)) { - /* - * It is so astronomically unlikely - * that null_oid is the OID of an - * actual object that we consider its - * appearance in a loose reference - * file to be repo corruption - * (probably due to a software bug). - */ - flag |= REF_ISBROKEN; - } - - if (check_refname_format(refname.buf, - REFNAME_ALLOW_ONELEVEL)) { - if (!refname_is_safe(refname.buf)) - die("loose refname is dangerous: %s", refname.buf); - oidclr(&oid); - flag |= REF_BAD_NAME | REF_ISBROKEN; - } - add_entry_to_dir(dir, - create_ref_entry(refname.buf, &oid, flag)); + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); } strbuf_setlen(&refname, dirnamelen); } From patchwork Mon Jan 29 11:35:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13535419 Received: from mail-ej1-f45.google.com (mail-ej1-f45.google.com [209.85.218.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E3B285C8E9 for ; Mon, 29 Jan 2024 11:35:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528138; cv=none; b=lIuRWEd+jcP2xg+lq2T6dXrIZMEuAuM+F+I3TVp/2K9MOIyGspRJOiBgx5XiD71abt9YvltxI6V0hsP0Vu8QXrXx5kSfZg3osjGH0nbnNeIcBRLxKpeBnGijBcVDtk81z3ONbt40+zyjCffLHXC6elZ1YCaFC2i84ezBp5DF11Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528138; c=relaxed/simple; bh=UyKR66fdm6MpcoopjID9w0IMIsh+oiDgUSK8wUIBs7A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SQbPtV+EjJ6HWLDxC2jGWmeP9Y9gvQoYhkigRW13VCl6zWfD5R8kO/DZdRzMQpP/taqPEaaTp88gQdKyW6onbC4t2w/DHguPQt1bG2eP5JjM0oosgJniQBurk7M+sR0zVzCiXT1Ju7CNqLs5tZLENOm823qi5+KWRcJNhhcL+t8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=f1WKSq5R; arc=none smtp.client-ip=209.85.218.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="f1WKSq5R" Received: by mail-ej1-f45.google.com with SMTP id a640c23a62f3a-a26f73732c5so283321366b.3 for ; Mon, 29 Jan 2024 03:35:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1706528135; x=1707132935; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LDo90hDl+W3KyjtTxut4xf8dtU7O7VPa53c7rE6gSko=; b=f1WKSq5RcC3IKb/WqlBf4dPRiSY99PCAL2zSr3YIaPTbJ/8VcQF/gaLoQrFYppw27B M4FN5Ta9W5j/D7EFfNXd/yjiTNRd+AvnvDGo5OScbGetYVZj/oxJ0o2eYRWVs02P4gid pxTE+zl9ZLkHMu1W/dG/4hiqElncogOzM7dlwHlt7ifjv5w/Hjua5Q73rtTUYaBEVNmU rSxaiLmttO1bANt2ljnLmY3enPP0RLhbJE7w+t+CvI806XAbaVpJpfaCOoKCL5KM4abZ Tp59FN1rjbthSTkNN6pRmnarxYoe8/SJ9msYkYBnxMihCAIgU7QI+eLrqRLriHelNkNF k9zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706528135; x=1707132935; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LDo90hDl+W3KyjtTxut4xf8dtU7O7VPa53c7rE6gSko=; b=hEPUdN4r7iqQE6p3AEZk8+AojMgF6AA0vCtCV6+wOcMFtIBFqUVLp6AhXUkeQs5E0N JRh2RhtbrLLX7/VIptQ/tuhBOIXCjyDNfoqDF2nANXpY6lKjw4aZrjXeWQbPyjUw3Ai0 ci/PGswUcgg5IZzDP8bmd87MRhgEhT29XdF9DtkXXW+5Cv/layQkWMmhOb1tkj8EMCeu x0wkXaqERoWTKqD50pLHPvKJHHcSvhF6zcXphMfXPLu0HZHvUvgyUuY5biXIUCrdtpPY YQ2+vOr3xepiyHYJsCS855bo9SLzxsa9Lhi5IzAnk5eMf2V1hwJym4i2IfphPisVLcuN hucA== X-Gm-Message-State: AOJu0YyWT6mSeI9nXQyM+0Jr8IA2WLdzDumQ0YLZb6Efk+wNdNI7c1E3 l3z663GKLKYlm2VKSHUtZqBmYY5zNTA17BIFDTrk9GNzD+oxOYK+55VIZ0u0 X-Google-Smtp-Source: AGHT+IEMRUcrReeCpgsEzEKL06jFS9EWspgU+TTjYFo6V1cn2++aFU27qEIBhRyfnNf1g0XLQTbSEQ== X-Received: by 2002:a17:906:34ca:b0:a35:9cf0:56d8 with SMTP id h10-20020a17090634ca00b00a359cf056d8mr2611434ejb.47.1706528134379; Mon, 29 Jan 2024 03:35:34 -0800 (PST) Received: from laptop.fritz.box ([2a02:2455:826e:4900:74c1:f49b:306a:744b]) by smtp.gmail.com with ESMTPSA id y9-20020a170906070900b00a35a3e2b90asm1370325ejb.149.2024.01.29.03.35.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jan 2024 03:35:34 -0800 (PST) From: Karthik Nayak To: git@vger.kernel.org Cc: gitster@pobox.com, ps@pks.im, phillip.wood123@gmail.com, Karthik Nayak Subject: [PATCH v3 3/4] refs: introduce `refs_for_each_all_refs()` Date: Mon, 29 Jan 2024 12:35:26 +0100 Message-ID: <20240129113527.607022-4-karthik.188@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240129113527.607022-1-karthik.188@gmail.com> References: <20240119142705.139374-1-karthik.188@gmail.com> <20240129113527.607022-1-karthik.188@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce a new ref iteration flag `DO_FOR_EACH_INCLUDE_ALL_REFS`, which will be used to iterate over all refs. In the files backend this is limited to regular refs, pseudorefs and HEAD. For other backends like the reftable this is the universal set of all refs stored in the backend. Refs which fall outside the `refs/` and aren't either pseudorefs or HEAD are more of a grey area. This is because we don't block the users from creating such refs but they are not officially supported. In the files backend, we can isolate such files from other files. Introduce `refs_for_each_all_refs()` which calls `do_for_each_ref()` with this newly introduced flag. In `refs/files-backend.c`, introduce a new function `add_pseudoref_and_head_entries()` to add pseudorefs and HEAD to the `ref_dir`. We then finally call `add_pseudoref_and_head_entries()` whenever the `DO_FOR_EACH_INCLUDE_ALL_REFS` flag is set. Any new ref backend will also have to implement similar changes on its end. Signed-off-by: Karthik Nayak --- refs.c | 7 +++++ refs.h | 6 ++++ refs/files-backend.c | 65 ++++++++++++++++++++++++++++++++++++++++---- refs/refs-internal.h | 7 +++++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/refs.c b/refs.c index 559f5aeea8..89b925719f 100644 --- a/refs.c +++ b/refs.c @@ -1762,6 +1762,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data); } +int refs_for_each_all_refs(struct ref_store *refs, each_ref_fn fn, + void *cb_data) +{ + return do_for_each_ref(refs, "", NULL, fn, 0, + DO_FOR_EACH_INCLUDE_ALL_REFS, cb_data); +} + static int qsort_strcmp(const void *va, const void *vb) { const char *a = *(const char **)va; diff --git a/refs.h b/refs.h index 46b8085d63..77ecb820f9 100644 --- a/refs.h +++ b/refs.h @@ -396,6 +396,12 @@ int for_each_namespaced_ref(const char **exclude_patterns, int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data); int for_each_rawref(each_ref_fn fn, void *cb_data); +/* + * Iterates over all ref types, regular, pseudorefs and HEAD. + */ +int refs_for_each_all_refs(struct ref_store *refs, each_ref_fn fn, + void *cb_data); + /* * Normalizes partial refs to their fully qualified form. * Will prepend to the if it doesn't start with 'refs/'. diff --git a/refs/files-backend.c b/refs/files-backend.c index 22495a4807..104f2e1ac7 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -315,9 +315,59 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, add_per_worktree_entries_to_dir(dir, dirname); } -static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) +/* + * Add pseudorefs to the ref dir by parsing the directory for any files + * which follow the pseudoref syntax. + */ +static void add_pseudoref_and_head_entries(struct ref_store *ref_store, + struct ref_dir *dir, + const char *dirname) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir"); + struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT; + struct dirent *de; + size_t dirnamelen; + DIR *d; + + files_ref_path(refs, &path, dirname); + + d = opendir(path.buf); + if (!d) { + strbuf_release(&path); + return; + } + + strbuf_addstr(&refname, dirname); + dirnamelen = refname.len; + + while ((de = readdir(d)) != NULL) { + unsigned char dtype; + + if (de->d_name[0] == '.') + continue; + if (ends_with(de->d_name, ".lock")) + continue; + strbuf_addstr(&refname, de->d_name); + + dtype = get_dtype(de, &path, 1); + if (dtype == DT_REG && (is_pseudoref(ref_store, de->d_name) || + is_headref(ref_store, de->d_name))) + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); + + strbuf_setlen(&refname, dirnamelen); + } + strbuf_release(&refname); + strbuf_release(&path); + closedir(d); +} + +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs, + unsigned int flags) { if (!refs->loose) { + struct ref_dir *dir; + /* * Mark the top-level directory complete because we * are about to read the only subdirectory that can @@ -328,12 +378,17 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) /* We're going to fill the top level ourselves: */ refs->loose->root->flag &= ~REF_INCOMPLETE; + dir = get_ref_dir(refs->loose->root); + + if (flags & DO_FOR_EACH_INCLUDE_ALL_REFS) + add_pseudoref_and_head_entries(dir->cache->ref_store, dir, + refs->loose->root->name); + /* * Add an incomplete entry for "refs/" (to be filled * lazily): */ - add_entry_to_dir(get_ref_dir(refs->loose->root), - create_dir_entry(refs->loose, "refs/", 5)); + add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5)); } return refs->loose; } @@ -861,7 +916,7 @@ static struct ref_iterator *files_ref_iterator_begin( * disk, and re-reads it if not. */ - loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags), prefix, ref_store->repo, 1); /* @@ -1222,7 +1277,7 @@ static int files_pack_refs(struct ref_store *ref_store, packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err); - iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, + iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL, the_repository, 0); while ((ok = ref_iterator_advance(iter)) == ITER_OK) { /* diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 8e9f04cc67..1cf7506435 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -260,6 +260,13 @@ enum do_for_each_ref_flags { * INCLUDE_BROKEN, since they are otherwise not included at all. */ DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), + + /* + * Include all refs in the $GIT_DIR in contrast to generally only listing + * references having the "refs/" prefix. In the files-backend this is + * limited to regular refs, pseudorefs and HEAD. + */ + DO_FOR_EACH_INCLUDE_ALL_REFS = (1 << 3), }; /* From patchwork Mon Jan 29 11:35:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13535420 Received: from mail-ed1-f48.google.com (mail-ed1-f48.google.com [209.85.208.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8438E5D8FD for ; Mon, 29 Jan 2024 11:35:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528139; cv=none; b=F/Jwl7LrMIj9RBGceeCoxhrxUx4IB5A+TQVtZMaRGCZ4CLs50ISlFjuyhXB8XXcs9kASIEyUTxBLtH6E5ChokkP5WADUuSk290nCz8cE99O+fE4wTy4W2uWDyBI7G/wozMMEBskrwjd1DRnimczVlvtUZSKWcavsECWEhtFi8+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706528139; c=relaxed/simple; bh=C7vDmXLEbXtY3oiqVbuMkZJlKMjB4TZLmmsS529QZQY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=exEJNnxPgNm1E40jKQVqNhokG8oUQTMbeyaBI4qQSyPbGpT4RkqLaa8mFEXIDxkR7M+Ms9Mp6EVFGkI5FUwLkwdS+WDgSyxRLP+Z8bTInKVoPOLZJqlW432YrSLJo3By5GoA0j3kHiELGmu/26GtULepol/AaZAaPPz2ml69uL0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=WTxPp16m; arc=none smtp.client-ip=209.85.208.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WTxPp16m" Received: by mail-ed1-f48.google.com with SMTP id 4fb4d7f45d1cf-55eee4a042eso1303691a12.0 for ; Mon, 29 Jan 2024 03:35:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1706528135; x=1707132935; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=mZfgvTKUs0RX0aUMcHBIQZV5urECTbu3T6yTamxXDFk=; b=WTxPp16m6qrUmCeG0dN11VFJ2nrMz/3dlFISYsCThTVlqIwGFPdxq2Cr8fMtpAzIHF zFvLcaAl7O2fJWiwp5gaLsfvxHHPeYCDO94Gm2LGMCQ2p3D2VStCjT5jpvBjUob/c0hn FyAng7xZi4QubEwiJAHlCq1JxkfVj1WeUK6Cj+FXLzoBkzoiKIJNwNRIYQLjvjbfS3BE J4ysbVkvomYqNl1I1KXsKK+s3KGslvKrM4qTslJo4XAXkWGJgAfQgIi/utdmygv/Kq1J jgGg2EntQov49kyN4OsZ3iqQb91WUjgMXeziQlugVbmatj3jIui8wf3Dud+OtCrdknhO jaLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706528135; x=1707132935; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mZfgvTKUs0RX0aUMcHBIQZV5urECTbu3T6yTamxXDFk=; b=aMTNdyeH8BWZPU+fcPJ7zf3kAUrPlwfn9DgFx+1McQX8M4JQEomB6liwIZhujWyxUk zil57FixR/nvP5WMcAwD/FKOPIRjGoMzarF3C09W4C3wrnla9NZAQExtJpSXaWxX9HiF FU6FARGCEPwZf/s5Hq50sej80pihWw5XF8svFm24huYzMCTmuVFsGZMQm7gV+phvVKXq YxscRQShbGkys2ujBU/TdKcVsADGbeEo7PqXHffBh642nA95KGTL4O3vZs80tHSLDpWb kC25yaD/oaaaxKBTyc8hMH+rCPhFJ4Mg4tepU/XV9fzzHnisQBDL3bb7X1sT1KUCU2sY nW/A== X-Gm-Message-State: AOJu0YxyEWF6HSxTTIakz5cCeqd8lODq9XaZPst4GGFX7DVMgRadKmrV meVqTr1rReTo8DnrB/2fA0QSkpbuXXwn+XjRyHswjSHA0cd92UTlmYEoWzg7 X-Google-Smtp-Source: AGHT+IEPvxSs37YA55xYHOkUputDmSBBb93PKrU03vAnk73wtqjXm6SXFRi3l9f4oEWVx61uz6cxxA== X-Received: by 2002:a17:906:1b09:b0:a34:a6af:22f1 with SMTP id o9-20020a1709061b0900b00a34a6af22f1mr3973257ejg.62.1706528135251; Mon, 29 Jan 2024 03:35:35 -0800 (PST) Received: from laptop.fritz.box ([2a02:2455:826e:4900:74c1:f49b:306a:744b]) by smtp.gmail.com with ESMTPSA id y9-20020a170906070900b00a35a3e2b90asm1370325ejb.149.2024.01.29.03.35.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 29 Jan 2024 03:35:34 -0800 (PST) From: Karthik Nayak To: git@vger.kernel.org Cc: gitster@pobox.com, ps@pks.im, phillip.wood123@gmail.com, Karthik Nayak Subject: [PATCH v3 4/4] for-each-ref: avoid filtering on empty pattern Date: Mon, 29 Jan 2024 12:35:27 +0100 Message-ID: <20240129113527.607022-5-karthik.188@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240129113527.607022-1-karthik.188@gmail.com> References: <20240119142705.139374-1-karthik.188@gmail.com> <20240129113527.607022-1-karthik.188@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 When the user uses an empty string pattern (""), we don't match any refs in git-for-each-ref(1). This is because in git-for-each-ref(1), we use path based matching and an empty string doesn't match any path. In this commit we change this behavior by making empty string pattern match all references. This is done by introducing a new flag `FILTER_REFS_NO_FILTER` in `ref-filter.c`, which uses the newly introduced `refs_for_each_all_refs()` function to iterate over all the refs in the repository. Signed-off-by: Karthik Nayak --- Documentation/git-for-each-ref.txt | 3 ++- builtin/for-each-ref.c | 21 +++++++++++++++++- ref-filter.c | 13 ++++++++++-- ref-filter.h | 4 +++- t/t6302-for-each-ref-filter.sh | 34 ++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 5 deletions(-) diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index be9543f684..b1cb482bf5 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -32,7 +32,8 @@ OPTIONS If one or more patterns are given, only refs are shown that match against at least one pattern, either using fnmatch(3) or literally, in the latter case matching completely or from the - beginning up to a slash. + beginning up to a slash. If an empty string is provided all refs + are printed, including HEAD and pseudorefs. --stdin:: If `--stdin` is supplied, then the list of patterns is read from diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 3885a9c28e..5aa879e8be 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -25,6 +25,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) struct ref_format format = REF_FORMAT_INIT; int from_stdin = 0; struct strvec vec = STRVEC_INIT; + unsigned int flags = FILTER_REFS_ALL; struct option opts[] = { OPT_BIT('s', "shell", &format.quote_style, @@ -93,11 +94,29 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) /* vec.v is NULL-terminated, just like 'argv'. */ filter.name_patterns = vec.v; } else { + size_t i; + filter.name_patterns = argv; + + /* + * Search for any empty string pattern, if it exists then we + * print all refs without any filtering. + */ + i = 0; + while (argv[i]) { + if (!argv[i][0]) { + flags = FILTER_REFS_NO_FILTER; + /* doing this removes any pattern from being matched */ + filter.name_patterns[0] = NULL; + break; + } + + i++; + } } filter.match_as_path = 1; - filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format); + filter_and_format_refs(&filter, flags, sorting, &format); ref_filter_clear(&filter); ref_sorting_release(sorting); diff --git a/ref-filter.c b/ref-filter.c index 35b989e1df..6dac133b87 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2622,6 +2622,11 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, each_ref_fn cb, void *cb_data) { + if (filter->kind & FILTER_REFS_NO_FILTER) { + return refs_for_each_all_refs( + get_main_ref_store(the_repository), cb, cb_data); + } + if (!filter->match_as_path) { /* * in this case, the patterns are applied after @@ -2775,8 +2780,12 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */ kind = filter_ref_kind(filter, refname); - if (!(kind & filter->kind)) + if (filter->kind & FILTER_REFS_NO_FILTER) { + if (kind == FILTER_REFS_DETACHED_HEAD) + kind = FILTER_REFS_OTHERS; + } else if (!(kind & filter->kind)) { return NULL; + } if (!filter_pattern_match(filter, refname)) return NULL; @@ -3041,7 +3050,7 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref ret = for_each_fullref_in("refs/remotes/", fn, cb_data); else if (filter->kind == FILTER_REFS_TAGS) ret = for_each_fullref_in("refs/tags/", fn, cb_data); - else if (filter->kind & FILTER_REFS_ALL) + else if (filter->kind & FILTER_REFS_ALL || filter->kind & FILTER_REFS_NO_FILTER) ret = for_each_fullref_in_pattern(filter, fn, cb_data); if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) head_ref(fn, cb_data); diff --git a/ref-filter.h b/ref-filter.h index 07cd6f6da3..1eab325ce0 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -22,7 +22,9 @@ #define FILTER_REFS_ALL (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \ FILTER_REFS_REMOTES | FILTER_REFS_OTHERS) #define FILTER_REFS_DETACHED_HEAD 0x0020 -#define FILTER_REFS_KIND_MASK (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD) +#define FILTER_REFS_NO_FILTER 0x0040 +#define FILTER_REFS_KIND_MASK (FILTER_REFS_ALL | FILTER_REFS_DETACHED_HEAD | \ + FILTER_REFS_NO_FILTER) struct atom_value; struct ref_sorting; diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index 82f3d1ea0f..3922326cab 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -31,6 +31,40 @@ test_expect_success 'setup some history and refs' ' git update-ref refs/odd/spot main ' +cat >expect <<-\EOF + HEAD + ORIG_HEAD + refs/heads/main + refs/heads/side + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two +EOF + +test_expect_success 'empty pattern prints pseudorefs' ' + git update-ref ORIG_HEAD main && + git for-each-ref --format="%(refname)" "" >actual && + test_cmp expect actual +' + +test_expect_success 'empty pattern with other patterns' ' + git update-ref ORIG_HEAD main && + git for-each-ref --format="%(refname)" "" "refs/" >actual && + test_cmp expect actual +' + +test_expect_success 'empty pattern towards the end' ' + git update-ref ORIG_HEAD main && + git for-each-ref --format="%(refname)" "refs/" "" >actual && + test_cmp expect actual +' + test_expect_success 'filtering with --points-at' ' cat >expect <<-\EOF && refs/heads/main