From patchwork Wed Jun 19 21:57:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704618 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (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 08FD115A847 for ; Wed, 19 Jun 2024 21:58:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834292; cv=none; b=cIxR9fyzNc4uLWLIPinRz474c1zTwHcl9rvbPupgP9XfQN1EbFuyUQuxjwPEYVRY8lbS6TRl5FdC5d1aXp5+TBxr3ZjmysrOC1TZCznff++3mw2YEZOuIVIKXaAQJEgdaD0EWy6JjadywufPhCZGexep0QK+qlP0WjY9/Ri0TU8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834292; c=relaxed/simple; bh=fBat4Yp3gYivyKXamk3+/10ToBE4v9Q8CQR4fkIkCNw=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=ZghlhrwdVJLggpm5qtDTeSzrFbSHzda2MGuiUVXzMUFDehAjkzZ7zPHYyVPALnma2rxbc/yqGs7PO/twxgUX8zKs5XvWfQL+1/bfpafdi8q3bt+PeKr4TvmllKP9OMxQ560jb682O2+CKiqzLMZ2BAbg0LTkgdOb7xZ/nXSy2To= 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=KIQvj51K; arc=none smtp.client-ip=209.85.221.43 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="KIQvj51K" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-35f2c9e23d3so903762f8f.0 for ; Wed, 19 Jun 2024 14:58:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834289; x=1719439089; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=J6phZVwBXrsTJRIGQvTlUYZADysZjBXsqztO7asPnDE=; b=KIQvj51KohIIVa5EGjrwZsh5v6mHrFdGPD0sYZU7L9q3QZQc05wbONWLmdXKquiO4j Wtr/HVQwhvceyTcE4Fh+liyxmdqR1++ojj/xmCBfLCZ32JUjVVy5hNtaAdymDTWpVHxZ nj027v+/EgKHICLk+iG+w1l0fKPe4wSPuVXiLK8EYKzWyIMy6lLYydxm/nuG1uhZOo6L 6HZ9apPI2UuRdrLOXBt8J6Rehc6zjgpltHl5ijEnZoNIuY+9SrhWobMud15R6ushZSrv MRN1jAWxCEWJN4rCGwVEemWyqfJt3PsX02LkNoA2i0j+HyNhadR+qVMYqi7n4ENsCl2z PQzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834289; x=1719439089; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=J6phZVwBXrsTJRIGQvTlUYZADysZjBXsqztO7asPnDE=; b=eEJ3hR8WATXR8rRAMGItftGjLXDXxdbjPimg3HweYmRnemS4gWeYcPTMYUzTA/ogvE P6tLUnEvmFIKpY8WfgjSqggnB8P2GaSLB2ppqOm1UeOM/PMT8vZTAOIp4xQTkNSuy309 OcmC2DVlTN47CnaqL7Wc9L0TLJ2FHScrBB5RdwbM5P/cmfhcqN4GKtZ99GC3JmaSAFFv 0MX1zPVFQtiRQq3CjT3ih1FN8JLF1BP2LbOJ7XBXkfJ+UqtfUNpgztOibdERBKBM2wdI meJtvIypo1HnWtxeLE71+uxmoMgVSw4N666JRRmfNJhmMnNve8OVD15IMzXh0lvqQFbX qtLQ== X-Gm-Message-State: AOJu0Yx0660uSw+mbP+BKdx9d/eZASznLhrFke46WMT1FaFD5yp2FakK a2gUa631nFBY1TySPTCrUWN7VSjejyNdWJrrP6QGe9kdBpM+XWP8zxTIWw== X-Google-Smtp-Source: AGHT+IEIIUYh96ZapVyiOF6LxY2sMsN2Oe8Y46MrBedBAMLC24M3H1etPeXBNXtD5rfbG9hMiInrlQ== X-Received: by 2002:a5d:45c7:0:b0:364:75ab:b06d with SMTP id ffacd0b85a97d-36475abb21amr707336f8f.27.1718834288961; Wed, 19 Jun 2024 14:58:08 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3607a43eedfsm16476699f8f.78.2024.06.19.14.58.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:08 -0700 (PDT) Message-Id: <074dc98acc79e08d07cf4f5c8105b872ec57980c.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:49 +0000 Subject: [PATCH v2 01/17] mktree: use OPT_BOOL Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Replace 'OPT_SET_INT' with 'OPT_BOOL' for the options '--missing' and '--batch'. The use of 'OPT_SET_INT' in these options is identical to 'OPT_BOOL', but 'OPT_BOOL' provides slightly simpler syntax. Signed-off-by: Victoria Dye --- builtin/mktree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index 9a22d4e2773..8b19d440747 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -162,8 +162,8 @@ int cmd_mktree(int ac, const char **av, const char *prefix) const struct option option[] = { OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")), - OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1), - OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1), + OPT_BOOL(0, "missing", &allow_missing, N_("allow missing objects")), + OPT_BOOL(0, "batch", &is_batch_mode, N_("allow creation of more than one tree")), OPT_END() }; From patchwork Wed Jun 19 21:57:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704620 Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) (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 AEDAF15AD9E for ; Wed, 19 Jun 2024 21:58:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834295; cv=none; b=Ay5eId3ob6Bf1UYEI7OFaTXZ/uDIedMtLLRLUShxMRtw2raobBqo2pA7ZEDnr6m/g6v0PXxOBtYwlB90UyANgPwXiuCFAMbBhMwvkrf1/7/1/YUO1XDVPraPVJYr/SQn0hzXbgxUoFDKEZuYTp9LbrzKM66x5zlTiM7vDioTeMQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834295; c=relaxed/simple; bh=Y3uGIenf3VzNzcILgOt57Fdj/xN4aa5LA6kSN4497Ug=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=DUUz62QuPB+m6p4s2iyHRKUID9yo5N/CIO6uNE02pYedw6yTvsNINo2asKiMLhtWrXgQ0LW6WkNF7hvmQ4xQVkE9FHNNVkCiEW4GAKVt2oGUb+03uIJLdI0zjb20ACYjUrM08YH/4ZaxXM8efHv2aG7fe0fnW32bf0rNBqlCSd8= 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=G2Y1TsOl; arc=none smtp.client-ip=209.85.128.43 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="G2Y1TsOl" Received: by mail-wm1-f43.google.com with SMTP id 5b1f17b1804b1-421798185f0so2741555e9.1 for ; Wed, 19 Jun 2024 14:58:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834290; x=1719439090; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=Urh7rNdpn2o/o6whqr985oqkaAGHqzZf69s+OZ65PsE=; b=G2Y1TsOlTWF8AjXKmOKN0L3JahfzX05a8jRTW0iU+BqWeD/uLmBDg2/9EcgIgWOk3z z5pK3lP7S7RbF7vMUPwbWzOOJwRu0dpcLVvE5ixA1bNoYDrfLZzcYOglUixdTjd+ITJt qqWd0TkEd8CxjauRhEwyGiwhmjEp0El3FvU2eul+0LM4D6273J2a/+bRxj3UVF58fw55 xnFs/KRwz+PFOHL8kEGoog70FPDDRA56ZQu72+AwwbFPBRnXCK7BCnEuWu4TC+N7mW0e 2e/39DAYBJoV81aGAWEM0aZYEH9FzbSbQaBegm+Zv6T7SjrA5K3aCabYrHnsLyoexaWM zF7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834290; x=1719439090; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Urh7rNdpn2o/o6whqr985oqkaAGHqzZf69s+OZ65PsE=; b=nLBCIwzq01q2ba+hAZhHrxYweKx4c+qV/C7sIzBwNP9i6sv2fE87Txr0QgWdD/8nq7 vuyftHfBtLKxsTmcyaK6v6W7xfLfJr61/E3CFsF4JXiNgi0McAh8T0VJBPn6SOTGElpJ uywt/yjugT9OMg3Nvvxdzd94K5OE26ZwLSdV2fH033J7yWZbbvAl8T2b61ovFGQfxB+f X7QoIXbPuexCCEL/+LbZUIC7iRsEirg5+5fzhM0j+C9EfOJBNUvnaUiVekEA2v8OWul/ qxqkCIiQVouVAlzEQbSmqEkOguxsUlddab6C+EPzi+NgTk83im09yRHhZZEhpSmZVEpR yorQ== X-Gm-Message-State: AOJu0YwU9I9rqEA+RxZcbhv4IDocvXzi2ldkNbWrOAZCfri6ANM3nrQ+ 5Agns92rQBegfpu+wKhmAIWsy739i7Ea7JLMo/qUrk0Hc4++igEGpBkriQ== X-Google-Smtp-Source: AGHT+IGbQ0/FYciYuJ1xOQVFyBO1lWkbM3iB7z0mEY9VvTTqUO370koc6LuQmMWx4q4maksFL3pbmg== X-Received: by 2002:adf:ef02:0:b0:355:161:b7e6 with SMTP id ffacd0b85a97d-36317b8321emr2667290f8f.41.1718834290300; Wed, 19 Jun 2024 14:58:10 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-364999e27c4sm465311f8f.57.2024.06.19.14.58.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:09 -0700 (PDT) Message-Id: <4558f35e7bf9a1594510951ee54252069bdcfc5b.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:50 +0000 Subject: [PATCH v2 02/17] mktree: rename treeent to tree_entry Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Rename the type for better readability, clearly specifying "entry" (instead of the "ent" abbreviation) and separating "tree" from "entry". Signed-off-by: Victoria Dye --- builtin/mktree.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index 8b19d440747..c02feb06aff 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -12,7 +12,7 @@ #include "parse-options.h" #include "object-store-ll.h" -static struct treeent { +static struct tree_entry { unsigned mode; struct object_id oid; int len; @@ -22,7 +22,7 @@ static int alloc, used; static void append_to_tree(unsigned mode, struct object_id *oid, char *path) { - struct treeent *ent; + struct tree_entry *ent; size_t len = strlen(path); if (strchr(path, '/')) die("path %s contains slash", path); @@ -38,8 +38,8 @@ static void append_to_tree(unsigned mode, struct object_id *oid, char *path) static int ent_compare(const void *a_, const void *b_) { - struct treeent *a = *(struct treeent **)a_; - struct treeent *b = *(struct treeent **)b_; + struct tree_entry *a = *(struct tree_entry **)a_; + struct tree_entry *b = *(struct tree_entry **)b_; return base_name_compare(a->name, a->len, a->mode, b->name, b->len, b->mode); } @@ -56,7 +56,7 @@ static void write_tree(struct object_id *oid) strbuf_init(&buf, size); for (i = 0; i < used; i++) { - struct treeent *ent = entries[i]; + struct tree_entry *ent = entries[i]; strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); } From patchwork Wed Jun 19 21:57:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704621 Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) (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 60C5115A847 for ; Wed, 19 Jun 2024 21:58:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834296; cv=none; b=ZWtEbOqtzezf/If5aaESVqL3KjOfaa3n+6k7EboETfOvKBtylSsY8c2VvY9xtKxZuoHKI6TWeQwVKQFZf1NBn10mT1BB2j3nfsQPTs6YmMLnqVIaZ8QvggaCYVXYUqq5/sLCD+wdM2wpVsYmhgLESPo19IdUr5FKQal9iSDwKDQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834296; c=relaxed/simple; bh=SvYQSH/EaKm+pcs76/PgNXOq55sTuVEqtsHHZqJTFMI=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=dPUBMsYYlMI8TVx+wBFRpFj6TZGx3ijuLNl/e+TRwghPQS4jLSoGpy9H87JMYKP8lL0Qz2VqPsCEzrGWL4+TvAMgBcX77sIvIctjlmfq+D2ZfAYFRHMdIA3r/oBFoaDW/ugYYao0s0qbMJUOEgjpSTExhV1o9x8w80TGdEracDM= 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=PXe4ZdFH; arc=none smtp.client-ip=209.85.128.43 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="PXe4ZdFH" Received: by mail-wm1-f43.google.com with SMTP id 5b1f17b1804b1-421d32fda86so3707335e9.0 for ; Wed, 19 Jun 2024 14:58:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834292; x=1719439092; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=W92hMv9JILmp1Imgm9S/wJe3H8FySBjdrj82quv+BOo=; b=PXe4ZdFHefe+iX3p8LiUzxIMyjCZjvnKn/OwUJi8zaaDFkirRa/Qy74+TbC32H4itB F2e6E+98iQCKMO7H9EHWlp3HQLI5j4xJNCc5R6GfZdDqmqK9JMnyDch2VOzDZ+CAOOV8 bCKxX7DBNUwHAYk/vHP41kJ1Ag0wIVn9hgaqXgZNiZIrK8HYGbdaQz+hFE79lNJbVHmj kHU118BuzUQQFPoQ+cnWkMtu5H3wpfiLl8xGt5numQ8+tFoDTBYABrPa9OtYtYBC+HCK 0d4RfsBkrAidBo98LfgFghaNHoKtOSzMxS5vBujz8X8N+UnfKxICLdE/YNUw9eDXtuU6 U38A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834292; x=1719439092; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=W92hMv9JILmp1Imgm9S/wJe3H8FySBjdrj82quv+BOo=; b=C4Q3katGn/GfKb/j8Md/fjn9Yh7rjXjojScxfbkxR/Z7m963JjHZHp1aYKi0AYHRE4 njhkNB6nHi1GQ/kHcY2SE/B6+MiI1Ntr7wDcPXTxIa18u7zDAmCwRH0TXQfFYC+Fd24b OGtaCnukghBUS7GduFfMxQpCYamw2pvoi6N/PGYXQIV+pKO+bSNxfnBlV2g4PeZMfSEI 5hYkLuXCOTNHfI7ZLdp+i5r/7DLge4Yf5VEEMhKC9UbaYaeESWlNkqQT04VieXb8+JGD eEnaiSetZKjLE0+98kCepoVPRhupzI70dHut+CfU6R/QKR+uRYxsyBrhDiDNQgaShEun +iNw== X-Gm-Message-State: AOJu0YxH6l7fvLcE+O7njI+VTGWlZDKCe9rIml89oZDIJu8P2nyZAyiM w4F1MYYhPZ/hwzCGOHruwlYONRxphnjwPPBxPdU8zRo/x9w62kgaHo9QNw== X-Google-Smtp-Source: AGHT+IFXw6baQyFQgoyJWfq9vKFBOSAbFWJ5eZROSYvMjPKqgL8ft/Egz2RjnPdOd6+dhZutfrrdoQ== X-Received: by 2002:a7b:ce8c:0:b0:421:f04d:ebcc with SMTP id 5b1f17b1804b1-42475182d11mr32615545e9.24.1718834291804; Wed, 19 Jun 2024 14:58:11 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247101aac6sm47309945e9.0.2024.06.19.14.58.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:10 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:51 +0000 Subject: [PATCH v2 03/17] mktree: use non-static tree_entry array Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Replace the static 'struct tree_entry **entries' with a non-static 'struct tree_entry_array' instance. In later commits, we'll want to be able to create additional 'struct tree_entry_array' instances utilizing common functionality (create, push, clear, free). To avoid code duplication, create the 'struct tree_entry_array' type and add functions that perform those basic operations. Signed-off-by: Victoria Dye --- builtin/mktree.c | 69 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index c02feb06aff..a96ea10bf95 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -12,15 +12,42 @@ #include "parse-options.h" #include "object-store-ll.h" -static struct tree_entry { +struct tree_entry { unsigned mode; struct object_id oid; int len; char name[FLEX_ARRAY]; -} **entries; -static int alloc, used; +}; + +struct tree_entry_array { + size_t nr, alloc; + struct tree_entry **entries; +}; + +static void tree_entry_array_push(struct tree_entry_array *arr, struct tree_entry *ent) +{ + ALLOC_GROW(arr->entries, arr->nr + 1, arr->alloc); + arr->entries[arr->nr++] = ent; +} + +static void tree_entry_array_clear(struct tree_entry_array *arr, int free_entries) +{ + if (free_entries) { + for (size_t i = 0; i < arr->nr; i++) + FREE_AND_NULL(arr->entries[i]); + } + arr->nr = 0; +} -static void append_to_tree(unsigned mode, struct object_id *oid, char *path) +static void tree_entry_array_release(struct tree_entry_array *arr, int free_entries) +{ + tree_entry_array_clear(arr, free_entries); + FREE_AND_NULL(arr->entries); + arr->alloc = 0; +} + +static void append_to_tree(unsigned mode, struct object_id *oid, const char *path, + struct tree_entry_array *arr) { struct tree_entry *ent; size_t len = strlen(path); @@ -32,8 +59,7 @@ static void append_to_tree(unsigned mode, struct object_id *oid, char *path) ent->len = len; oidcpy(&ent->oid, oid); - ALLOC_GROW(entries, used + 1, alloc); - entries[used++] = ent; + tree_entry_array_push(arr, ent); } static int ent_compare(const void *a_, const void *b_) @@ -44,19 +70,18 @@ static int ent_compare(const void *a_, const void *b_) b->name, b->len, b->mode); } -static void write_tree(struct object_id *oid) +static void write_tree(struct tree_entry_array *arr, struct object_id *oid) { struct strbuf buf; - size_t size; - int i; + size_t size = 0; - QSORT(entries, used, ent_compare); - for (size = i = 0; i < used; i++) - size += 32 + entries[i]->len; + QSORT(arr->entries, arr->nr, ent_compare); + for (size_t i = 0; i < arr->nr; i++) + size += 32 + arr->entries[i]->len; strbuf_init(&buf, size); - for (i = 0; i < used; i++) { - struct tree_entry *ent = entries[i]; + for (size_t i = 0; i < arr->nr; i++) { + struct tree_entry *ent = arr->entries[i]; strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); } @@ -70,7 +95,8 @@ static const char *mktree_usage[] = { NULL }; -static void mktree_line(char *buf, int nul_term_line, int allow_missing) +static void mktree_line(char *buf, int nul_term_line, int allow_missing, + struct tree_entry_array *arr) { char *ptr, *ntr; const char *p; @@ -146,7 +172,7 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing) } } - append_to_tree(mode, &oid, path); + append_to_tree(mode, &oid, path, arr); free(to_free); } @@ -158,6 +184,7 @@ int cmd_mktree(int ac, const char **av, const char *prefix) int allow_missing = 0; int is_batch_mode = 0; int got_eof = 0; + struct tree_entry_array arr = { 0 }; strbuf_getline_fn getline_fn; const struct option option[] = { @@ -182,9 +209,9 @@ int cmd_mktree(int ac, const char **av, const char *prefix) break; die("input format error: (blank line only valid in batch mode)"); } - mktree_line(sb.buf, nul_term_line, allow_missing); + mktree_line(sb.buf, nul_term_line, allow_missing, &arr); } - if (is_batch_mode && got_eof && used < 1) { + if (is_batch_mode && got_eof && arr.nr < 1) { /* * Execution gets here if the last tree entry is terminated with a * new-line. The final new-line has been made optional to be @@ -192,12 +219,14 @@ int cmd_mktree(int ac, const char **av, const char *prefix) */ ; /* skip creating an empty tree */ } else { - write_tree(&oid); + write_tree(&arr, &oid); puts(oid_to_hex(&oid)); fflush(stdout); } - used=0; /* reset tree entry buffer for re-use in batch mode */ + tree_entry_array_clear(&arr, 1); /* reset tree entry buffer for re-use in batch mode */ } + + tree_entry_array_release(&arr, 1); strbuf_release(&sb); return 0; } From patchwork Wed Jun 19 21:57:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704622 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (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 0295F15B0F5 for ; Wed, 19 Jun 2024 21:58:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834298; cv=none; b=MesEc05JM5kqwj3YXKdx/P2hFamSLrq2HQQ1DAfsEutX/sM+HLH3yfTQ0v5t91Lgd81PWAo/LU3Tc+p+C9vTiTD4Y3TXd1DqPFkSZ+TKCqtXRmNH3gkR3zte2ZIshY9Q9lgsz7CPquRIjY0tC5LWKpOqYaE/cEFXAK+gSz5oSck= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834298; c=relaxed/simple; bh=SxYuvvKaoxzWFs2m+LWd3LTdcn92m5SClq8aPMrayPk=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=RlNfIyW3PQSJCOS4gqLkFY7sMuuLOPlSK1SQPVHOIWHD4RL93MgkkwHSReP/LcdV+1/K50GI+avb32Y3iRzecNNTCADOhJBCxUifqmwrWFdMokI/Hi0tSih3MK5p2Em7C+/D7j3cubCEwuZj4uWye5oj8EmN5NHqATjuMfuZVUA= 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=gS5HYCP3; arc=none smtp.client-ip=209.85.128.44 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="gS5HYCP3" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-42172ed3597so1646455e9.0 for ; Wed, 19 Jun 2024 14:58:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834294; x=1719439094; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=kGKQqxC2lOCg+N5VQXQOAgfNgIOmU5SVsKzh7ojag6I=; b=gS5HYCP3b+M4FmjmXVp17nF2RsRW6+OF4P3eSID0cP+1ANN/QREHcnDkgIH8zAoGMC 7Q4QlFiw/93W3KJdciGEWB2Or9DCkTFJ33hFTEtwCX4TiAeSHLxlpk46UI5Hu3De6sCP bvqoycZ0KBLmT/5AlrLKCw24/OiITkF4gNAJYAUm4sLKpXsLaLpImNz5fLhUnlQrYiyd onpk+M33P1DUGTRkIqB+t8WtvCsB7G6PnjM5VYAPSApT9zDAGlKnukz6mMmeN0PitbeO u5N2p1JTjdyfxnM8pSPJ980ps5DUdQKbq4P+3DFaq0pTxln3DfASnlj1VRegJKZ4tWR4 psoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834294; x=1719439094; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kGKQqxC2lOCg+N5VQXQOAgfNgIOmU5SVsKzh7ojag6I=; b=WilcLHh0Gnib32543OzxJC5o1ZAsVhhGncdTOUEGTCst5BRcQWfeAm8GhwiZDw14gf NpQIecFgZ0V8r8H/Umw2fgTgyfCyLoerFx7TQvvU55ysdmxllKs38QUmjXdk5Epu+Mbq 9d5TUSIKdETdD9M0teYo0h1nh+/w/WdaoW0pjjjWi170t3bsarsx3jiqZeZtFyAPZKih DUvhbySsWPPg7UtuCJ6muNIk+Ky3vn4p2pDL0ZcDrGnO6N1xBdXvbokWsv36qyDJfBT5 6jtHQ4ywheFuhSh0ltuyFhPcH4wdpmw7+G3bn6XPUB7Nrq09eU2La7xVVgqDwYdsHcvA ohRg== X-Gm-Message-State: AOJu0YxcF8jJPoT51v/9bCeGeTvMTrTEjyO2LtidLkY3RvJBqCHqj5ye 2ocu9GeiQmEKCT3cKB/MgG3uawz5uP0brXZCdyavQgXQOCKQlMkM6OYH2Q== X-Google-Smtp-Source: AGHT+IGANyQRXvOad1+aldmfUuWWSfrdPNgunL4yWoe2y91kik9m7hU5XGIIqiH5TvE3cuNyJzRm6g== X-Received: by 2002:a05:600c:3556:b0:421:81eb:7d5c with SMTP id 5b1f17b1804b1-4246f5dbe0emr59512425e9.18.1718834293557; Wed, 19 Jun 2024 14:58:13 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d21226asm3683925e9.47.2024.06.19.14.58.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:12 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:52 +0000 Subject: [PATCH v2 04/17] update-index: generalize 'read_index_info' Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Move 'read_index_info()' into a new header 'index-info.h' and generalize the function to call a provided callback for each parsed line. Update 'update-index.c' to use this generalized 'read_index_info()', adding the callback 'apply_index_info()' to verify the parsed line and update the index according to its contents. Switching to using a callback to validate the parsed entry in 'update-index' results in a slight change to the error message indicating a file could not be removed from the index. The original implementation uses the raw, quoted pathname in the error message, whereas the callback (without access to the raw pathname) uses the unquoted value. However, this change makes the failed removal message consistent with all other error messages in the function, and that consistency is likely more beneficial than not to a user. The motivation for this change is to consolidate the already-similar input parsing logic in 'git update-index' and 'git mktree', avoiding code duplication and the associated maintenance burden. The input formats accepted by 'update-index' are a superset of those accepted by 'mktree', so in a later commit we can replace the input parsing of the latter with 'read_index_info()' without breaking existing usage. Co-authored-by: Junio C Hamano Signed-off-by: Victoria Dye --- Documentation/git-update-index.txt | 16 +--- Documentation/index-info-formats.txt | 13 +++ Makefile | 1 + builtin/update-index.c | 129 +++++++-------------------- index-info.c | 90 +++++++++++++++++++ index-info.h | 11 +++ t/t2107-update-index-basic.sh | 27 ++++++ 7 files changed, 177 insertions(+), 110 deletions(-) create mode 100644 Documentation/index-info-formats.txt create mode 100644 index-info.c create mode 100644 index-info.h diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 7128aed5405..e52aecb845d 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -278,21 +278,9 @@ USING --INDEX-INFO `--index-info` is a more powerful mechanism that lets you feed multiple entry definitions from the standard input, and designed -specifically for scripts. It can take inputs of three formats: +specifically for scripts. It can take inputs in the following formats: - . mode SP type SP sha1 TAB path -+ -This format is to stuff `git ls-tree` output into the index. - - . mode SP sha1 SP stage TAB path -+ -This format is to put higher order stages into the -index file and matches 'git ls-files --stage' output. - - . mode SP sha1 TAB path -+ -This format is no longer produced by any Git command, but is -and will continue to be supported by `update-index --index-info`. +include::index-info-formats.txt[] To place a higher stage entry to the index, the path should first be removed by feeding a mode=0 entry for the path, and diff --git a/Documentation/index-info-formats.txt b/Documentation/index-info-formats.txt new file mode 100644 index 00000000000..037ebd24321 --- /dev/null +++ b/Documentation/index-info-formats.txt @@ -0,0 +1,13 @@ + . mode SP type SP sha1 TAB path ++ +This format is to use `git ls-tree` output. + + . mode SP sha1 SP stage TAB path ++ +This format allows higher order stages to appear and +matches 'git ls-files --stage' output. + + . mode SP sha1 TAB path ++ +This format is no longer produced by any Git command, but is +and will continue to be supported. diff --git a/Makefile b/Makefile index 2f5f16847ae..db9604e59c3 100644 --- a/Makefile +++ b/Makefile @@ -1037,6 +1037,7 @@ LIB_OBJS += hex.o LIB_OBJS += hex-ll.o LIB_OBJS += hook.o LIB_OBJS += ident.o +LIB_OBJS += index-info.o LIB_OBJS += json-writer.o LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o diff --git a/builtin/update-index.c b/builtin/update-index.c index d343416ae26..fddf59b54c1 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -11,6 +11,7 @@ #include "gettext.h" #include "hash.h" #include "hex.h" +#include "index-info.h" #include "lockfile.h" #include "quote.h" #include "cache-tree.h" @@ -509,100 +510,29 @@ static void update_one(const char *path) report("add '%s'", path); } -static void read_index_info(int nul_term_line) +static int apply_index_info(unsigned int mode, struct object_id *oid, int stage, + const char *path_name, void *cbdata UNUSED) { - const int hexsz = the_hash_algo->hexsz; - struct strbuf buf = STRBUF_INIT; - struct strbuf uq = STRBUF_INIT; - strbuf_getline_fn getline_fn; + if (!verify_path(path_name, mode)) { + fprintf(stderr, "Ignoring path %s\n", path_name); + return 0; + } - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - while (getline_fn(&buf, stdin) != EOF) { - char *ptr, *tab; - char *path_name; - struct object_id oid; - unsigned int mode; - unsigned long ul; - int stage; - - /* This reads lines formatted in one of three formats: - * - * (1) mode SP sha1 TAB path - * The first format is what "git apply --index-info" - * reports, and used to reconstruct a partial tree - * that is used for phony merge base tree when falling - * back on 3-way merge. - * - * (2) mode SP type SP sha1 TAB path - * The second format is to stuff "git ls-tree" output - * into the index file. - * - * (3) mode SP sha1 SP stage TAB path - * This format is to put higher order stages into the - * index file and matches "git ls-files --stage" output. + if (!mode) { + /* mode == 0 means there is no such path -- remove */ + if (remove_file_from_index(the_repository->index, path_name)) + die("git update-index: unable to remove %s", path_name); + } + else { + /* mode ' ' sha1 '\t' name + * ptr[-1] points at tab, + * ptr[-41] is at the beginning of sha1 */ - errno = 0; - ul = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ' - || errno || (unsigned int) ul != ul) - goto bad_line; - mode = ul; - - tab = strchr(ptr, '\t'); - if (!tab || tab - ptr < hexsz + 1) - goto bad_line; - - if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { - stage = tab[-1] - '0'; - ptr = tab + 1; /* point at the head of path */ - tab = tab - 2; /* point at tail of sha1 */ - } - else { - stage = 0; - ptr = tab + 1; /* point at the head of path */ - } - - if (get_oid_hex(tab - hexsz, &oid) || - tab[-(hexsz + 1)] != ' ') - goto bad_line; - - path_name = ptr; - if (!nul_term_line && path_name[0] == '"') { - strbuf_reset(&uq); - if (unquote_c_style(&uq, path_name, NULL)) { - die("git update-index: bad quoting of path name"); - } - path_name = uq.buf; - } - - if (!verify_path(path_name, mode)) { - fprintf(stderr, "Ignoring path %s\n", path_name); - continue; - } - - if (!mode) { - /* mode == 0 means there is no such path -- remove */ - if (remove_file_from_index(the_repository->index, path_name)) - die("git update-index: unable to remove %s", - ptr); - } - else { - /* mode ' ' sha1 '\t' name - * ptr[-1] points at tab, - * ptr[-41] is at the beginning of sha1 - */ - ptr[-(hexsz + 2)] = ptr[-1] = 0; - if (add_cacheinfo(mode, &oid, path_name, stage)) - die("git update-index: unable to update %s", - path_name); - } - continue; - - bad_line: - die("malformed index info %s", buf.buf); + if (add_cacheinfo(mode, oid, path_name, stage)) + die("git update-index: unable to update %s", path_name); } - strbuf_release(&buf); - strbuf_release(&uq); + + return 0; } static const char * const update_index_usage[] = { @@ -848,16 +778,23 @@ static enum parse_opt_result stdin_cacheinfo_callback( struct parse_opt_ctx_t *ctx, const struct option *opt, const char *arg, int unset) { - int *nul_term_line = opt->value; + int ret = 0; BUG_ON_OPT_NEG(unset); BUG_ON_OPT_ARG(arg); - if (ctx->argc != 1) - return error("option '%s' must be the last argument", opt->long_name); - allow_add = allow_replace = allow_remove = 1; - read_index_info(*nul_term_line); - return 0; + if (ctx->argc != 1) { + ret = error("option '%s' must be the last argument", opt->long_name); + } else { + int *nul_term_line = opt->value; + + allow_add = allow_replace = allow_remove = 1; + ret = read_index_info(*nul_term_line, apply_index_info, NULL); + if (ret) + ret = -1; + } + + return ret; } static enum parse_opt_result stdin_callback( diff --git a/index-info.c b/index-info.c new file mode 100644 index 00000000000..8ccaac5487b --- /dev/null +++ b/index-info.c @@ -0,0 +1,90 @@ +#include "git-compat-util.h" +#include "index-info.h" +#include "hash.h" +#include "hex.h" +#include "strbuf.h" +#include "quote.h" + +int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata) +{ + const int hexsz = the_hash_algo->hexsz; + struct strbuf buf = STRBUF_INIT; + struct strbuf uq = STRBUF_INIT; + strbuf_getline_fn getline_fn; + int ret = 0; + + getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; + while (getline_fn(&buf, stdin) != EOF) { + char *ptr, *tab; + char *path_name; + struct object_id oid; + unsigned int mode; + unsigned long ul; + int stage; + + /* This reads lines formatted in one of three formats: + * + * (1) mode SP sha1 TAB path + * The first format is what "git apply --index-info" + * reports, and used to reconstruct a partial tree + * that is used for phony merge base tree when falling + * back on 3-way merge. + * + * (2) mode SP type SP sha1 TAB path + * The second format is to stuff "git ls-tree" output + * into the index file. + * + * (3) mode SP sha1 SP stage TAB path + * This format is to put higher order stages into the + * index file and matches "git ls-files --stage" output. + */ + errno = 0; + ul = strtoul(buf.buf, &ptr, 8); + if (ptr == buf.buf || *ptr != ' ' + || errno || (unsigned int) ul != ul) + goto bad_line; + mode = ul; + + tab = strchr(ptr, '\t'); + if (!tab || tab - ptr < hexsz + 1) + goto bad_line; + + if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { + stage = tab[-1] - '0'; + ptr = tab + 1; /* point at the head of path */ + tab = tab - 2; /* point at tail of sha1 */ + } else { + stage = 0; + ptr = tab + 1; /* point at the head of path */ + } + + if (get_oid_hex(tab - hexsz, &oid) || + tab[-(hexsz + 1)] != ' ') + goto bad_line; + + path_name = ptr; + if (!nul_term_line && path_name[0] == '"') { + strbuf_reset(&uq); + if (unquote_c_style(&uq, path_name, NULL)) { + ret = error("bad quoting of path name"); + break; + } + path_name = uq.buf; + } + + ret = fn(mode, &oid, stage, path_name, cbdata); + if (ret) { + ret = -1; + break; + } + + continue; + + bad_line: + die("malformed input line '%s'", buf.buf); + } + strbuf_release(&buf); + strbuf_release(&uq); + + return ret; +} diff --git a/index-info.h b/index-info.h new file mode 100644 index 00000000000..d650498325a --- /dev/null +++ b/index-info.h @@ -0,0 +1,11 @@ +#ifndef INDEX_INFO_H +#define INDEX_INFO_H + +#include "hash.h" + +typedef int (*each_index_info_fn)(unsigned int, struct object_id *, int, const char *, void *); + +/* Iterate over parsed index info from stdin */ +int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata); + +#endif /* INDEX_INFO_H */ diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index cc72ead79f3..794a5b1a184 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -142,4 +142,31 @@ test_expect_success '--index-version' ' test_must_be_empty actual ' +test_expect_success '--index-info fails on malformed input' ' + # empty line + echo "" | + test_must_fail git update-index --index-info 2>err && + test_grep "malformed input line" err && + + # bad whitespace + printf "100644 $EMPTY_BLOB A" | + test_must_fail git update-index --index-info 2>err && + test_grep "malformed input line" err && + + # invalid stage value + printf "100644 $EMPTY_BLOB 5\tA" | + test_must_fail git update-index --index-info 2>err && + test_grep "malformed input line" err && + + # invalid OID length + printf "100755 abc123\tA" | + test_must_fail git update-index --index-info 2>err && + test_grep "malformed input line" err && + + # bad quoting + printf "100644 $EMPTY_BLOB\t\"A" | + test_must_fail git update-index --index-info 2>err && + test_grep "bad quoting of path name" err +' + test_done From patchwork Wed Jun 19 21:57:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704623 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (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 6219715B147 for ; Wed, 19 Jun 2024 21:58:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834299; cv=none; b=jNt4LVE+XyMtNbbli2UVXY936Q4fTlcFRY3riHZ/FXveNFJ1x+i3hzNuDoud3i4ubc4E4Si1oWahzmAq6E85LUpfS0yGWn0gRVlWmnyED0hxh5M+iTnH1XH/39vsF0o0UdQ5O9SSIQ3I8XPPvaLXkv2VgSNI1osg3xgkQn6a880= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834299; c=relaxed/simple; bh=nACDKcxOrv2o0oTNGEq/qQQ6UyyiuFDj+qgkE3JFrWk=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=QERs2DIZTDg7T1m/zMyRjSHoP9KchrRoMVxh654PXWy0j53lYN46lPCjgMG7eUlp69myFkv1SKK7/fJXZ8xlI8H2KxQKJ4srcnkNCVdbrvi4sTmwWW57pkCfl/lXvGhGvqcoUrGmv0WexrTEMzA6M8QZVdEvhrdAUN9QXAR3HIM= 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=T8NoysDI; arc=none smtp.client-ip=209.85.221.43 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="T8NoysDI" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-35f2d723ef0so235861f8f.1 for ; Wed, 19 Jun 2024 14:58:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834295; x=1719439095; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=RRBtzJ5mfgKVDSxfgx1Fb3pde4RRZitCidiTwuQoEKI=; b=T8NoysDIVoQx3H+dl1lyz0fZb9WKAJsE2NpysVwHFw8pOZ+tPoj+5EYDY+qxkfYvq5 u4mjdgCLz5eAuDcXdKN+n/t7fK3BKC9WD+UTXc0FdrWsI1dSw82ONNJciBfod1EzmVYv uxZJNVmwbUuSiTf2B2JZYv9t8EOrCyyPjmYrHsQjkqVzSOVifS9oGPY80rEWgFLdPKXu EQU+b5aTgyUmcDtzF7RkH6LzOAyQ3ER2ZeIpJdtEQ2ZosWaw6h3WJ3JENsutRvzPA4Mx E7kiMPtTDY3u9Yujg4vbO/D8wtNOfXks9NuK5Lt/p7WwJFk5GO53rFWbNF59Ia+rL0WC zwSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834295; x=1719439095; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RRBtzJ5mfgKVDSxfgx1Fb3pde4RRZitCidiTwuQoEKI=; b=g8Le33kVHkYWm+mkVQGF7Z6b/R5NgH5uDqMbeASfWAJHNc4Fj5tN4bzdQ7cw5eDEbT sptZHtbNjRUAb+VaanmhQGlit/uZ41uZk53u/l2MEVTzTkh2nddDyEzB4ySk2+aWBKYA 71Yf6SP1TorCXne816P3wgbMs1zQHUsZAcZrDUW6TlPzSysYXup1lOhiNNcemdd8tvA8 jRXYsJdxVW315XLCWV8Pr0Gv4WNiSBHd+KVSJdqh37fLlx0nfbmvOvqTbzo/bl4r2KJr tUZuaRBKuGOqRfoKv1RgptscwVLLpMK/2vJ8Kjr/h83zg3vlCVQovcFktTcpaVFGOXI0 FMbw== X-Gm-Message-State: AOJu0YyyJpbKW2jPtLdxk88fr784ZZiJZ3QufIgeouOBJvmLPeHlTNhd 5Ygwrf16WL8cmUCZn22wdW0d7DltLB6FTAbwNRWzdD62E5j8MNid90ATGg== X-Google-Smtp-Source: AGHT+IFEd1THSjTBdtVUbbzN6EUP3ig7lqiJNfcEdZf1e1biAJYM4p6cel2M8qjuvxPuCxiDGU8mRg== X-Received: by 2002:adf:fdca:0:b0:35f:1128:250d with SMTP id ffacd0b85a97d-36319990fd8mr2322275f8f.68.1718834295078; Wed, 19 Jun 2024 14:58:15 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0c5485sm3855625e9.21.2024.06.19.14.58.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:13 -0700 (PDT) Message-Id: <4f4d54c8d075a43960af36ccb025b2ddb34266f8.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:53 +0000 Subject: [PATCH v2 05/17] index-info.c: return unrecognized lines to caller Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Update 'read_index_info()' to return INDEX_INFO_UNRECOGNIZED_LINE (value 1), rather than die()-ing when the function encounters a line that cannot be parsed according to one of the accepted formats. This grants the caller the flexibility to fall back on custom handling for such lines rather than a returning a catch-all error. In the case of 'update-index', we'll still exit with a "malformed input line" error. However, when 'read_index_info()' is used to process the input to 'mktree' in a later patch, an empty line return value will signal a new tree in --batch mode. Signed-off-by: Victoria Dye --- builtin/update-index.c | 9 +++++++-- index-info.c | 16 +++++++++------- index-info.h | 5 ++++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/builtin/update-index.c b/builtin/update-index.c index fddf59b54c1..8d0b40a6fd6 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -787,11 +787,16 @@ static enum parse_opt_result stdin_cacheinfo_callback( ret = error("option '%s' must be the last argument", opt->long_name); } else { int *nul_term_line = opt->value; + struct strbuf line = STRBUF_INIT; allow_add = allow_replace = allow_remove = 1; - ret = read_index_info(*nul_term_line, apply_index_info, NULL); - if (ret) + ret = read_index_info(*nul_term_line, apply_index_info, NULL, &line); + + if (ret == INDEX_INFO_UNRECOGNIZED_LINE) + ret = error("malformed input line '%s'", line.buf); + else if (ret) ret = -1; + strbuf_release(&line); } return ret; diff --git a/index-info.c b/index-info.c index 8ccaac5487b..7a02f66426a 100644 --- a/index-info.c +++ b/index-info.c @@ -5,16 +5,16 @@ #include "strbuf.h" #include "quote.h" -int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata) +int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata, + struct strbuf *line) { const int hexsz = the_hash_algo->hexsz; - struct strbuf buf = STRBUF_INIT; struct strbuf uq = STRBUF_INIT; strbuf_getline_fn getline_fn; int ret = 0; getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - while (getline_fn(&buf, stdin) != EOF) { + while (getline_fn(line, stdin) != EOF) { char *ptr, *tab; char *path_name; struct object_id oid; @@ -39,8 +39,8 @@ int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata) * index file and matches "git ls-files --stage" output. */ errno = 0; - ul = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ' + ul = strtoul(line->buf, &ptr, 8); + if (ptr == line->buf || *ptr != ' ' || errno || (unsigned int) ul != ul) goto bad_line; mode = ul; @@ -81,10 +81,12 @@ int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata) continue; bad_line: - die("malformed input line '%s'", buf.buf); + ret = INDEX_INFO_UNRECOGNIZED_LINE; + break; } - strbuf_release(&buf); strbuf_release(&uq); + if (!ret) + strbuf_reset(line); return ret; } diff --git a/index-info.h b/index-info.h index d650498325a..9258011462d 100644 --- a/index-info.h +++ b/index-info.h @@ -5,7 +5,10 @@ typedef int (*each_index_info_fn)(unsigned int, struct object_id *, int, const char *, void *); +#define INDEX_INFO_UNRECOGNIZED_LINE 1 + /* Iterate over parsed index info from stdin */ -int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata); +int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata, + struct strbuf *line); #endif /* INDEX_INFO_H */ From patchwork Wed Jun 19 21:57:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704624 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.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 C4D6F15A847 for ; Wed, 19 Jun 2024 21:58:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834299; cv=none; b=KczhlE4cd+j5MMo//+kuxB29geIJYKGtHfrLnDz1yOunq28+ukaZOurA74+nS9EBeb2Mu5QPzW035cYaqFHKq8g+79rH9YlU9XtGi9Xkx+jmcAqWFYmwQIlweBdrkUoEDhhSZ47xIA6dJat54msL4twQTDF2BztvAfqZ+9XAfsc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834299; c=relaxed/simple; bh=jeBDt4N25odL5gjYkO/1pvS9qP3scKjXFEKA1GSznLw=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=mdOmp4uwf5cuG0eT5vRKHCzyjfi43wmZfhqRi3ND1AqATwWpQ/y0yKu8D2OksGZmSW/4yYWN7FXhH17Kq3xghGAB5IwlY5KAwuWyPfmTiEUktoIyJ5CCoJDv5xgGyAJzYWwdNVIAsJGCQeR+g7GMRM9AAFkXMIm7E1R7s8/1QAs= 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=DIKSnrnM; arc=none smtp.client-ip=209.85.221.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="DIKSnrnM" Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-35f275c7286so198326f8f.2 for ; Wed, 19 Jun 2024 14:58:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834296; x=1719439096; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=Aq4iJbbzTxbfBzgAiCj5MdlIpvCzQiRWIi8aFAvrE50=; b=DIKSnrnMEjt3nswd2XT818OGviWFETJOkoaDrlYI0OVeBjirgWP8LlrNAa2EKiCvVH VDjM3HCRiCLH24dWPc46l3u/kdF5QL5UYgfjZub8nD9OaJmEgDx4yrBYMYklOWLfTouQ 2Cueqp2rl2Zf4ocHzDPtPAFTOO9Tt9t7+sg6wad816/cWChdgzEZe2A/FltR3u8bmnFB 2YeOiNaDeoOvPhlzDp9m0p2yeeOJORYFryLQFzDo2P6XwjKFqSnbWlSc+HloDkkkCKJH rbs3g1dTJC5Tgk/y2s5U8QGLFa2Utsadpq9oVJuZwd7q8w7v+wlVl5ji5lq1Ofb9KIiM VFiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834296; x=1719439096; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Aq4iJbbzTxbfBzgAiCj5MdlIpvCzQiRWIi8aFAvrE50=; b=WW0aRWx8p8BMQPpZ9lOLMsqolDD5iS48x9jNqLxOhliUOC/Hs3Xy2mZ1L4Dch/9M/R /xvUA8iBb3XALdFlpTD7r76H5HM9odZMEEIwyQOeGtiiGQYtUGJUNHj6G/wyr1lSgz0d QEC4fBdsygSJKcaYCKUaWSc5ZL5rt1wd3xu9zhzzkDztQ6q/WXdACamsYyBe0tXudY9n Wy1yX3dt8Hm1Lft+ZyGh/zZAXV7fL3u+ZpCZ9mW6gaJvaIwU1MAQsbOPb/b03G+rHoDp T0Xs3efipuG+De770df3mYc09cW13J51sJ3G6mLsa/RwwaJG2MjuIQtOnfZJUljhJ78Q BwUA== X-Gm-Message-State: AOJu0YypSXE6oXobfWwnGfO+Uqgm2ydikEbZPpJe8KqiYEu8eLBmz+4A 0i1eN/ExJZsLUQ7+DFg0o2ejDsXFYr3w3Af2RqCgGiMyenqBdLbUVBT8Rw== X-Google-Smtp-Source: AGHT+IEsZWSHG7Puam+xKJ3J1Y7wlkdQNotVnyrF2v/esHICE2GB5rC+VoOj7uc9gqM0R/Keg9bs7A== X-Received: by 2002:a5d:408d:0:b0:360:7829:bb93 with SMTP id ffacd0b85a97d-363177a3a72mr2983218f8f.21.1718834295803; Wed, 19 Jun 2024 14:58:15 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0b6355sm3959425e9.2.2024.06.19.14.58.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:15 -0700 (PDT) Message-Id: <472efcaf1dde3dd590f34bde63c5ce6dcf72a531.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:54 +0000 Subject: [PATCH v2 06/17] index-info.c: parse object type in provided in read_index_info Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye If the object type (e.g. "blob", "tree") is identified on a stdin line read by 'read_index_info()' (i.e. on lines formatted like the output of 'git ls-tree'), parse it into an 'enum object_type' and provide it to the 'read_index_info()' callback as an argument. If the type is not provided, pass 'OBJ_ANY' instead. If the object type is invalid, return an error. The goal of this change is to allow for more thorough validation of the provided object type (e.g. against the provided mode) in 'mktree' once 'mktree_line' is replaced with 'read_index_info()'. Note, though, that this change also strengthens the validation done by 'update-index', since invalid type names now trigger an error. Signed-off-by: Victoria Dye --- builtin/update-index.c | 3 ++- index-info.c | 16 ++++++++++++---- index-info.h | 3 ++- t/t2107-update-index-basic.sh | 5 +++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/builtin/update-index.c b/builtin/update-index.c index 8d0b40a6fd6..42a274f9ce4 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -510,7 +510,8 @@ static void update_one(const char *path) report("add '%s'", path); } -static int apply_index_info(unsigned int mode, struct object_id *oid, int stage, +static int apply_index_info(unsigned int mode, struct object_id *oid, + enum object_type obj_type UNUSED, int stage, const char *path_name, void *cbdata UNUSED) { if (!verify_path(path_name, mode)) { diff --git a/index-info.c b/index-info.c index 7a02f66426a..9c986cd9093 100644 --- a/index-info.c +++ b/index-info.c @@ -18,6 +18,7 @@ int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata, char *ptr, *tab; char *path_name; struct object_id oid; + enum object_type obj_type = OBJ_ANY; unsigned int mode; unsigned long ul; int stage; @@ -51,18 +52,17 @@ int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata, if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { stage = tab[-1] - '0'; - ptr = tab + 1; /* point at the head of path */ + path_name = tab + 1; /* point at the head of path */ tab = tab - 2; /* point at tail of sha1 */ } else { stage = 0; - ptr = tab + 1; /* point at the head of path */ + path_name = tab + 1; /* point at the head of path */ } if (get_oid_hex(tab - hexsz, &oid) || tab[-(hexsz + 1)] != ' ') goto bad_line; - path_name = ptr; if (!nul_term_line && path_name[0] == '"') { strbuf_reset(&uq); if (unquote_c_style(&uq, path_name, NULL)) { @@ -72,7 +72,15 @@ int read_index_info(int nul_term_line, each_index_info_fn fn, void *cbdata, path_name = uq.buf; } - ret = fn(mode, &oid, stage, path_name, cbdata); + /* Get the type, if provided */ + if (tab - hexsz - 1 > ptr + 1) { + if (*(tab - hexsz - 1) != ' ') + goto bad_line; + *(tab - hexsz - 1) = '\0'; + obj_type = type_from_string(ptr + 1); + } + + ret = fn(mode, &oid, obj_type, stage, path_name, cbdata); if (ret) { ret = -1; break; diff --git a/index-info.h b/index-info.h index 9258011462d..adea453b197 100644 --- a/index-info.h +++ b/index-info.h @@ -2,8 +2,9 @@ #define INDEX_INFO_H #include "hash.h" +#include "object.h" -typedef int (*each_index_info_fn)(unsigned int, struct object_id *, int, const char *, void *); +typedef int (*each_index_info_fn)(unsigned int, struct object_id *, enum object_type, int, const char *, void *); #define INDEX_INFO_UNRECOGNIZED_LINE 1 diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 794a5b1a184..9e0e77bbf9e 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -153,6 +153,11 @@ test_expect_success '--index-info fails on malformed input' ' test_must_fail git update-index --index-info 2>err && test_grep "malformed input line" err && + # invalid type + printf "100644 bad $EMPTY_BLOB\tA" | + test_must_fail git update-index --index-info 2>err && + test_grep "invalid object type" err && + # invalid stage value printf "100644 $EMPTY_BLOB 5\tA" | test_must_fail git update-index --index-info 2>err && From patchwork Wed Jun 19 21:57:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704626 Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com [209.85.128.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 8D64815ADB3 for ; Wed, 19 Jun 2024 21:58:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834301; cv=none; b=EdVvLX0Lptb3eZn8qHt1N2KZzqDK3RWcCrqB/UY8jZqoo1GcTTE8RFLvfwH0n1A3SmAxmoHOvNABuNQzAMK9Yby1e7Eltee4gNcSY+o11ctImK4E/X9AH4hczB8n7GbgNW9OYRfEtGITh3QqkPJucp8Q4h1rRWmta1oiaHMAFuk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834301; c=relaxed/simple; bh=8m8mkOtGwfkXkie9PVkWv1usJtGprvn9IF7V8Ys1yN8=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=L1DdNYw3cAmgbz3X3Y7Rz/v2LUSRyh7qF0c9+TivB5SazZhnT8lUhtyovNefVHM0w04iDAzDaFCnS8rQ8rocEShvVYqHIGo4Q7AIR08CuisttkzUqIsXZ5JN8PeF/X9pzgaqoxBSwN9Z+EKZrhxVkhHhGlV0P6UjDA5Icm0QHS0= 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=d9LC873S; arc=none smtp.client-ip=209.85.128.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="d9LC873S" Received: by mail-wm1-f46.google.com with SMTP id 5b1f17b1804b1-42249a4f9e4so2030175e9.2 for ; Wed, 19 Jun 2024 14:58:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834297; x=1719439097; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=t2ScA0TQGGNmdtbi7fNH3rCzzw9Qek8q07cfzUAHvNU=; b=d9LC873S2vvTUg1bMNS3yheOyF50H8APT0vndo5Ji/zRU5rVdFOYqv4PXzCiQ1TbiZ Wx/C+VO5IuVeqJB7Enq6Mlko/AjmOx0dfpq9mveMfEiwTDBkzQybL8CWUIUD6/AXF22Z u59PgRNjwgVBhcaJFgB8+dyWHU8A82OJdWzmbLJEcqAH+y5Ycn9kOIRkjpDLTeGTER5B kq+MmfKQLdJL63R5IZF6eTGmTQGoleQqErAqxMemDNHrNhZYwS54JCxwMEMPpGGpnxRR /s6zwKNtH/oU0DWDAmmYw7fBy8nC9zqHGTS70sU3ZA9D9FlYweHyXzk2Rw89Pl2eAwrN 1mIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834297; x=1719439097; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=t2ScA0TQGGNmdtbi7fNH3rCzzw9Qek8q07cfzUAHvNU=; b=GwIPFF+k76pou4zOc9urNXEPFdmwUqBWmt4R9P1Xmmnb426jvYM76Z4kFN7kiA7wpv QRpiB/2E1ifMctskkj9rsWP01Ka4A/r98RI92l6fLKizpGFOb1WcWH+RnUPxx9CU0tQH HGYc7jS18BvgV0jsGzouoxFTxxrhklaUjRzEm8t2CQAZFOvAntGwnvZzbbBaAPFTGQLm dZTbhV2tLAQV3G4YiWLWAzNrHOS0UOUyD47oSqMhfwfmLTWphqgf/GuLQNSNo3MejUMl R+yNyvk2eb0ej58LYtGqu8ER/hEZo584Q9MmwW/N5kHP0zCcoH2+Jb83tv3ma8IjLSaA G0Dg== X-Gm-Message-State: AOJu0Yy0wDN+aSayhPLoiUoZ+C8tsOZMDD6wxakG+0+qFma5U8IKqnEn VPAWyG17QaKHfhFNsbMVf7/bIhTb77u+m2PoSTmLT7nORRXYS2U+/DOh4Q== X-Google-Smtp-Source: AGHT+IFxoUFPQ2cInae52ds2utn5+61sZD2cyzlMkry1yLDUIqiANvwhE1iuQ2hui2L2aZ88A78lqw== X-Received: by 2002:a05:600c:4189:b0:421:81c1:65fa with SMTP id 5b1f17b1804b1-42475178bdfmr25688525e9.13.1718834296902; Wed, 19 Jun 2024 14:58:16 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d2122e7sm3769645e9.40.2024.06.19.14.58.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:16 -0700 (PDT) Message-Id: <9dc8e16a7fca886ec378d74a8e2ac61921a7f6ea.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:55 +0000 Subject: [PATCH v2 07/17] mktree: use read_index_info to read stdin lines Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Replace the custom input parsing of 'mktree' with 'read_index_info()', which handles not only the 'ls-tree' output format it already handles but also the other formats compatible with 'update-index'. This lends some consistency across the commands (avoiding the need for two similar implementations for input parsing) and adds flexibility to mktree. It should be noted that, while the error messages are largely preserved in the refactor, one does change: "fatal: invalid quoting" is now "error: bad quoting of path name". Update 'Documentation/git-mktree.txt' to reflect the more permissive input format, as well as make a note about rejecting stage values higher than 0. Helped-by: Junio C Hamano Signed-off-by: Victoria Dye --- Documentation/git-mktree.txt | 26 ++++-- builtin/mktree.c | 156 +++++++++++++++-------------------- t/t1010-mktree.sh | 66 +++++++++++++++ 3 files changed, 151 insertions(+), 97 deletions(-) diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index 383f09dd333..c187403c6bd 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -3,7 +3,7 @@ git-mktree(1) NAME ---- -git-mktree - Build a tree-object from ls-tree formatted text +git-mktree - Build a tree-object from formatted tree entries SYNOPSIS @@ -13,15 +13,14 @@ SYNOPSIS DESCRIPTION ----------- -Reads standard input in non-recursive `ls-tree` output format, and creates -a tree object. The order of the tree entries is normalized by mktree so -pre-sorting the input is not required. The object name of the tree object -built is written to the standard output. +Reads entry information from stdin and creates a tree object from those +entries. The object name of the tree object built is written to the standard +output. OPTIONS ------- -z:: - Read the NUL-terminated `ls-tree -z` output instead. + Input lines are separated with NUL rather than LF. --missing:: Allow missing objects. The default behaviour (without this option) @@ -35,6 +34,21 @@ OPTIONS optional. Note - if the `-z` option is used, lines are terminated with NUL. +INPUT FORMAT +------------ +Tree entries may be specified in any of the formats compatible with the +`--index-info` option to linkgit:git-update-index[1]: + +include::index-info-formats.txt[] + +Note that if the `stage` of a tree entry is given, the value must be 0. +Higher stages represent conflicted files in an index; this information +cannot be represented in a tree object. The command will fail without +writing the tree if a higher order stage is specified for any entry. + +The order of the tree entries is normalized by `mktree` so pre-sorting the +input by path is not required. + GIT --- Part of the linkgit:git[1] suite diff --git a/builtin/mktree.c b/builtin/mktree.c index a96ea10bf95..03a9899bc11 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -6,6 +6,7 @@ #include "builtin.h" #include "gettext.h" #include "hex.h" +#include "index-info.h" #include "quote.h" #include "strbuf.h" #include "tree.h" @@ -95,123 +96,96 @@ static const char *mktree_usage[] = { NULL }; -static void mktree_line(char *buf, int nul_term_line, int allow_missing, - struct tree_entry_array *arr) +struct mktree_line_data { + struct tree_entry_array *arr; + int allow_missing; +}; + +static int mktree_line(unsigned int mode, struct object_id *oid, + enum object_type obj_type, int stage, + const char *path, void *cbdata) { - char *ptr, *ntr; - const char *p; - unsigned mode; - enum object_type mode_type; /* object type derived from mode */ - enum object_type obj_type; /* object type derived from sha */ + struct mktree_line_data *data = cbdata; + enum object_type mode_type = object_type(mode); struct object_info oi = OBJECT_INFO_INIT; - char *path, *to_free = NULL; - struct object_id oid; + enum object_type parsed_obj_type; - ptr = buf; - /* - * Read non-recursive ls-tree output format: - * mode SP type SP sha1 TAB name - */ - mode = strtoul(ptr, &ntr, 8); - if (ptr == ntr || !ntr || *ntr != ' ') - die("input format error: %s", buf); - ptr = ntr + 1; /* type */ - ntr = strchr(ptr, ' '); - if (!ntr || parse_oid_hex(ntr + 1, &oid, &p) || - *p != '\t') - die("input format error: %s", buf); - - /* It is perfectly normal if we do not have a commit from a submodule */ - if (S_ISGITLINK(mode)) - allow_missing = 1; - - - *ntr++ = 0; /* now at the beginning of SHA1 */ - - path = (char *)p + 1; /* at the beginning of name */ - if (!nul_term_line && path[0] == '"') { - struct strbuf p_uq = STRBUF_INIT; - if (unquote_c_style(&p_uq, path, NULL)) - die("invalid quoting"); - path = to_free = strbuf_detach(&p_uq, NULL); - } + if (stage) + die(_("path '%s' is unmerged"), path); - /* - * Object type is redundantly derivable three ways. - * These should all agree. - */ - mode_type = object_type(mode); - if (mode_type != type_from_string(ptr)) { - die("entry '%s' object type (%s) doesn't match mode type (%s)", - path, ptr, type_name(mode_type)); - } + if (obj_type != OBJ_ANY && mode_type != obj_type) + die("object type (%s) doesn't match mode type (%s)", + type_name(obj_type), type_name(mode_type)); + + oi.typep = &parsed_obj_type; - /* Check the type of object identified by oid without fetching objects */ - oi.typep = &obj_type; - if (oid_object_info_extended(the_repository, &oid, &oi, + if (oid_object_info_extended(the_repository, oid, &oi, OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) - obj_type = -1; - - if (obj_type < 0) { - if (allow_missing) { - ; /* no problem - missing objects are presumed to be of the right type */ - } else { - die("entry '%s' object %s is unavailable", path, oid_to_hex(&oid)); - } - } else { - if (obj_type != mode_type) { - /* - * The object exists but is of the wrong type. - * This is a problem regardless of allow_missing - * because the new tree entry will never be correct. - */ - die("entry '%s' object %s is a %s but specified type was (%s)", - path, oid_to_hex(&oid), type_name(obj_type), type_name(mode_type)); - } + parsed_obj_type = -1; + + if (parsed_obj_type < 0) { + /* + * There are two conditions where the object being missing + * is acceptable: + * + * - We're explicitly allowing it with --missing. + * - The object is a submodule, which we wouldn't expect to + * be in this repo anyway. + * + * If neither condition is met, die(). + */ + if (!data->allow_missing && !S_ISGITLINK(mode)) + die("entry '%s' object %s is unavailable", path, oid_to_hex(oid)); + + } else if (parsed_obj_type != mode_type) { + /* + * The object exists but is of the wrong type. + * This is a problem regardless of allow_missing + * because the new tree entry will never be correct. + */ + die("entry '%s' object %s is a %s but specified type was (%s)", + path, oid_to_hex(oid), type_name(parsed_obj_type), type_name(mode_type)); } - append_to_tree(mode, &oid, path, arr); - free(to_free); + append_to_tree(mode, oid, path, data->arr); + return 0; } int cmd_mktree(int ac, const char **av, const char *prefix) { - struct strbuf sb = STRBUF_INIT; struct object_id oid; int nul_term_line = 0; - int allow_missing = 0; int is_batch_mode = 0; - int got_eof = 0; struct tree_entry_array arr = { 0 }; - strbuf_getline_fn getline_fn; + struct mktree_line_data mktree_line_data = { .arr = &arr }; + struct strbuf line = STRBUF_INIT; + int ret; const struct option option[] = { OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")), - OPT_BOOL(0, "missing", &allow_missing, N_("allow missing objects")), + OPT_BOOL(0, "missing", &mktree_line_data.allow_missing, N_("allow missing objects")), OPT_BOOL(0, "batch", &is_batch_mode, N_("allow creation of more than one tree")), OPT_END() }; ac = parse_options(ac, av, prefix, option, mktree_usage, 0); - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - - while (!got_eof) { - while (1) { - if (getline_fn(&sb, stdin) == EOF) { - got_eof = 1; - break; - } - if (sb.buf[0] == '\0') { + + do { + ret = read_index_info(nul_term_line, mktree_line, &mktree_line_data, &line); + if (ret < 0) + break; + + if (ret == INDEX_INFO_UNRECOGNIZED_LINE) { + if (line.len) + die("input format error: %s", line.buf); + else if (!is_batch_mode) /* empty lines denote tree boundaries in batch mode */ - if (is_batch_mode) - break; die("input format error: (blank line only valid in batch mode)"); - } - mktree_line(sb.buf, nul_term_line, allow_missing, &arr); } - if (is_batch_mode && got_eof && arr.nr < 1) { + + if (is_batch_mode && !ret && arr.nr < 1) { /* * Execution gets here if the last tree entry is terminated with a * new-line. The final new-line has been made optional to be @@ -224,9 +198,9 @@ int cmd_mktree(int ac, const char **av, const char *prefix) fflush(stdout); } tree_entry_array_clear(&arr, 1); /* reset tree entry buffer for re-use in batch mode */ - } + } while (ret > 0); + strbuf_release(&line); tree_entry_array_release(&arr, 1); - strbuf_release(&sb); - return 0; + return !!ret; } diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 22875ba598c..649842fa27c 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -54,11 +54,36 @@ test_expect_success 'ls-tree output in wrong order given to mktree (2)' ' test_cmp tree.withsub actual ' +test_expect_success '--batch creates multiple trees' ' + cat top >multi-tree && + echo "" >>multi-tree && + cat top.withsub >>multi-tree && + + cat tree >expect && + cat tree.withsub >>expect && + git mktree --batch actual && + test_cmp expect actual +' + test_expect_success 'allow missing object with --missing' ' git mktree --missing actual && test_cmp tree.missing actual ' +test_expect_success 'mktree with invalid submodule OIDs' ' + # non-existent OID - ok + printf "160000 commit $(test_oid numeric)\tA\n" >in && + git mktree tree.actual && + git ls-tree $(cat tree.actual) >actual && + test_cmp in actual && + + # existing OID, wrong type - error + tree_oid="$(cat tree)" && + printf "160000 commit $tree_oid\tA" | + test_must_fail git mktree 2>err && + test_grep "object $tree_oid is a tree but specified type was (commit)" err +' + test_expect_success 'mktree refuses to read ls-tree -r output (1)' ' test_must_fail git mktree err && + test_grep "blank line only valid in batch mode" err && + + # bad whitespace + printf "100644 blob $EMPTY_BLOB A" | + test_must_fail git mktree 2>err && + test_grep "input format error" err && + + # invalid type + printf "100644 bad $EMPTY_BLOB\tA" | + test_must_fail git mktree 2>err && + test_grep "invalid object type" err && + + # invalid OID length + printf "100755 blob abc123\tA" | + test_must_fail git mktree 2>err && + test_grep "input format error" err && + + # bad quoting + printf "100644 blob $EMPTY_BLOB\t\"A" | + test_must_fail git mktree 2>err && + test_grep "bad quoting of path name" err +' + +test_expect_success 'mktree fails on mode mismatch' ' + tree_oid="$(cat tree)" && + + # mode-type mismatch + printf "100644 tree $tree_oid\tA" | + test_must_fail git mktree 2>err && + test_grep "object type (tree) doesn${SQ}t match mode type (blob)" err && + + # mode-object mismatch (no --missing) + printf "100644 $tree_oid\tA" | + test_must_fail git mktree 2>err && + test_grep "object $tree_oid is a tree but specified type was (blob)" err +' + test_done From patchwork Wed Jun 19 21:57:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704625 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (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 C225415B548 for ; Wed, 19 Jun 2024 21:58:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834301; cv=none; b=hW8qfPFxVPvPVnuBQ7k9WCrcZaYfG5rJNwLNErwGpt50KvG3WBmG7L3Tg6BITW5bzH6TJ4moC8cMd0jPwCAr5mt45gRcTx4xqrxV2GGgsRJtiFaQ+fbQ7wV39WDerI81Y45PK2kvvO6zxvaK1elkn42zaruFK9vXdN3SqOdt7gA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834301; c=relaxed/simple; bh=U8YRz38tNmhXPCtXr6gaVQCbE+KH6Ev6o5SnhomDvJY=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=k7xno33Jg/2prC+DZ6yclO5CGzIUKIDiTTtZf6Qg/hTr1Q2mMi2BW5xhuXeWNFW3nS4ewWNj8b81b2TYtxhjxEoRCyX26QDkEspU6NVz5Vr2JHXfjzG+PTVHTyCWDE8RYNW4nzANBc1zGk+52GliucWnWiA4ZOHRy90wqRvPl1o= 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=RXqX/Uiq; arc=none smtp.client-ip=209.85.128.52 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="RXqX/Uiq" Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-424798859dfso2942695e9.0 for ; Wed, 19 Jun 2024 14:58:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834298; x=1719439098; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=Pk3GXljznJKO3UKj+ovA2jMmmR2uuECc9hf2eayVXio=; b=RXqX/UiqUjuH0uwadGYTG5Nzt2Pyi+rTRb1EI6f7VU7yR9qr7W+jUUXwsjN3Nrl7UY Wbv85K6QYfJgzA12kn/VUqw1zGHCCGk/f/Vwt4sNNgcE2/fZu4resCWuiwE3oXzcWyCl AMbVPCkMGK24AfezkO/VPif/ZtEEh9kV58cRsOUhuHfkt2FoCXrfjQKo30ao4w6YSxxp ejEEEdAgm6QHV2mfY7DyUGVPugBaOI5hvdvWiZka9LLAkTdzpIhK8H5eCBrWDn7JOsKs 71/sefoZm98dntCbboEy4vOZXyXHY3rLisHF9IRYmfSyuPHU8HL3hIf+oMFonf3M+lK0 Oe+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834298; x=1719439098; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Pk3GXljznJKO3UKj+ovA2jMmmR2uuECc9hf2eayVXio=; b=rPzIRacIgAwYeXaEmgCwwkZxBYt9KZDrLwaArOp6dBmHmvb6MX8Kb62969YCpuQEpJ qX0NDdUwKTp5227BVpphxfr48ZwshlJPqlkyWEV1s2ycmf6peG6bP7yvyiIugGq/RtRr uzfYgmRiB0hYucahPKG8m2vB+Mr6X47/gK6z+AVF+/TTWPxrwibYzL48KpoQKE+cw84V oY9rSSS88t7W5umTN/k8PfEDEyasP6fERAUzJvohTGthvIfzVK1tZ7xIwoEeeB6Bmo3u ZRrVnkKiNrFM/JVUtFA/EUdv21EKhzP9ykkyWwRTIBpn46rJX9QtCPBBs5pq15toqumJ Ixzw== X-Gm-Message-State: AOJu0YyccRXnhhz1YHppPgTKn1Ax0+YqN61lO7we1uvk8gHt4YPWCxxK ZXIIeQTjtuz65WEAkTJjxP9CJBNcugTm1wPiYuSdIedIbMTcQpPZW3LtjA== X-Google-Smtp-Source: AGHT+IEglAy02YzYC8BAnLqYKI1I5Y4CuzJdp7Rr2bZsqpGi3xo8ZPB3E+6lVNjjCLXVmxgTW6wMYQ== X-Received: by 2002:a05:600c:47d1:b0:422:8c48:9908 with SMTP id 5b1f17b1804b1-42475176660mr29057095e9.16.1718834297648; Wed, 19 Jun 2024 14:58:17 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0c0b33sm3767025e9.11.2024.06.19.14.58.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:17 -0700 (PDT) Message-Id: <8a3264afd0c072d10ec0571e2038f009733c4de5.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:56 +0000 Subject: [PATCH v2 08/17] mktree.c: do not fail on mismatched submodule type Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Adjust the 'git mktree' tree entry intake logic to no longer fail if an OID specified with a S_IFGITLINK mode exists in the current repository's object database with a different type. While this scenario likely represents a mistake by the user, submodule OIDs are not validated as part of object writes or in 'git fsck'. In other commands, any object info would be ignored if such an OID was found in the current repository with a different type. Since this check is not needed to avoid creation of a corrupt tree, let's remove it and make 'git mktree' less opinionated as a result. Helped-by: Junio C Hamano Signed-off-by: Victoria Dye --- builtin/mktree.c | 58 ++++++++++++++++++++++------------------------- t/t1010-mktree.sh | 18 ++++++--------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index 03a9899bc11..f509ed1a81f 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -107,8 +107,6 @@ static int mktree_line(unsigned int mode, struct object_id *oid, { struct mktree_line_data *data = cbdata; enum object_type mode_type = object_type(mode); - struct object_info oi = OBJECT_INFO_INIT; - enum object_type parsed_obj_type; if (stage) die(_("path '%s' is unmerged"), path); @@ -117,36 +115,34 @@ static int mktree_line(unsigned int mode, struct object_id *oid, die("object type (%s) doesn't match mode type (%s)", type_name(obj_type), type_name(mode_type)); - oi.typep = &parsed_obj_type; - - if (oid_object_info_extended(the_repository, oid, &oi, - OBJECT_INFO_LOOKUP_REPLACE | + if (!S_ISGITLINK(mode)) { + struct object_info oi = OBJECT_INFO_INIT; + enum object_type parsed_obj_type; + unsigned int flags = OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_QUICK | - OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) - parsed_obj_type = -1; - - if (parsed_obj_type < 0) { - /* - * There are two conditions where the object being missing - * is acceptable: - * - * - We're explicitly allowing it with --missing. - * - The object is a submodule, which we wouldn't expect to - * be in this repo anyway. - * - * If neither condition is met, die(). - */ - if (!data->allow_missing && !S_ISGITLINK(mode)) - die("entry '%s' object %s is unavailable", path, oid_to_hex(oid)); - - } else if (parsed_obj_type != mode_type) { - /* - * The object exists but is of the wrong type. - * This is a problem regardless of allow_missing - * because the new tree entry will never be correct. - */ - die("entry '%s' object %s is a %s but specified type was (%s)", - path, oid_to_hex(oid), type_name(parsed_obj_type), type_name(mode_type)); + OBJECT_INFO_SKIP_FETCH_OBJECT; + + oi.typep = &parsed_obj_type; + + if (oid_object_info_extended(the_repository, oid, &oi, flags) < 0) { + /* + * If the object is missing and we aren't explicitly + * allowing missing objects, die(). Otherwise, continue + * without error. + */ + if (!data->allow_missing) + die("entry '%s' object %s is unavailable", path, + oid_to_hex(oid)); + } else if (parsed_obj_type != mode_type) { + /* + * The object exists but is of the wrong type. + * This is a problem regardless of allow_missing + * because the new tree entry will never be correct. + */ + die("entry '%s' object %s is a %s but specified type was (%s)", + path, oid_to_hex(oid), type_name(parsed_obj_type), + type_name(mode_type)); + } } append_to_tree(mode, oid, path, data->arr); diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 649842fa27c..48fc532e7af 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -71,17 +71,13 @@ test_expect_success 'allow missing object with --missing' ' ' test_expect_success 'mktree with invalid submodule OIDs' ' - # non-existent OID - ok - printf "160000 commit $(test_oid numeric)\tA\n" >in && - git mktree tree.actual && - git ls-tree $(cat tree.actual) >actual && - test_cmp in actual && - - # existing OID, wrong type - error - tree_oid="$(cat tree)" && - printf "160000 commit $tree_oid\tA" | - test_must_fail git mktree 2>err && - test_grep "object $tree_oid is a tree but specified type was (commit)" err + for oid in "$(test_oid numeric)" "$(cat tree)" + do + printf "160000 commit $oid\tA\n" >in && + git mktree tree.actual && + git ls-tree $(cat tree.actual) >actual && + test_cmp in actual || return 1 + done ' test_expect_success 'mktree refuses to read ls-tree -r output (1)' ' From patchwork Wed Jun 19 21:57:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704627 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.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 D54FD15B575 for ; Wed, 19 Jun 2024 21:58:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834302; cv=none; b=OanG4rPxODKp8mrPyr89aITDU8ni+d+jS/CXS7Q1RoclWdzmqgK8jjr8Zqty6qllpj/uHMiKnul/jhzTGuI/V0EWlwvjc1pHYEih2LuLguet8Vsy2JX3Du5hYq9pzkS3S6OxZTZTmNzb1Gq8WQtvxzIh6Lh52T8iPY8cu7TU+74= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834302; c=relaxed/simple; bh=hu1dSm5mkrFnGJZU3TjJkdFqIJtBsRXEyu1I7xdJOcc=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=BfjGJvTEHUSnk/QCsK5MbgUBMfBalA1RRsUdEycEA7YZzpIK0KHmEdrXOsYqs8Y3ChLW+S46Jf0dcu8rkqHuQb426/w0Zr1YIn8uxzLUX2dYdkkt46+hLDTLe6AYBm79xlvpCR1Szz2ZyTsZRjHFOrup343ZpAVuDjcTaKDMMJY= 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=Ui5hHWXQ; arc=none smtp.client-ip=209.85.221.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="Ui5hHWXQ" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-3620ee2cdf7so179805f8f.3 for ; Wed, 19 Jun 2024 14:58:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834299; x=1719439099; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=9gOIz4NOvZ1cAVbjEQfy9WjgSLye9N9mOa1Zt1YN1cc=; b=Ui5hHWXQqic/2wRiN18LOC1keVHwv8qdQxc8PT7huh+FAgWEb2sPYwALch7wXjkz2r GCO2hbDFOarn0oyMq7iEtkHbntWvyJXEQ9yAlv2fOmYtIv2V98QCcoi/u5H1xLNn2Ic3 tD0D0WPbTvZtOxTlaQSoEKHHx+6Cupe9YEggCfIx4VchvOVsOPmm9jDkLTpqd/s1wjmC dVUPfPXqRbBR1V1YZouEGeUKhQrVbt0thqgzXCN6iAaZI5Bequx5vAN3+NG2x0ygs2om 8ub6XGyCUibSshnJ4O/bSlLa6Ry+dufQGp1XdbSUBKl1RVpXX9ZAS+AC5fiX4SWaRrjj dS0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834299; x=1719439099; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9gOIz4NOvZ1cAVbjEQfy9WjgSLye9N9mOa1Zt1YN1cc=; b=mvAd9kmraNslAivXsvoDXRWZ23zMWb9nvuNywllGagkRxxAa4se6Noe5RaDTUqN+on zi/FZBc90GY/EGfeErnpiXl4acj7V22tEKjAIkNk6sc+rf1KCqJd58x3UBBnBWLSkMlx 3Cm8ZuW5luqKYWAn0Eqg7uILPadWFSDB/7XlfRa6hvhITM+2fTDZ0g0/cdlb6/FmrKtX HGai63opF3elVckV4WOVufZtdaxFWqaE/m8veZff+jRFv4ex1OetWROUPpl+zj1HSjIF fTvOjd6TMUBRhub4uInaepYrgkEVCRbUx/KFxIMdx5TUIpqpVzB6+CPfUDP9qcQnhNOG Yj2Q== X-Gm-Message-State: AOJu0Yy4uAzFGbdNIE1BzET61fnO6hpiIi16jzBakxDmItshmOSLL1bg TE9h+3oL2NObMBaCSyNBlPrT9BzXeHFQHNqk9jC693C9p9MIfsMqhdUUjA== X-Google-Smtp-Source: AGHT+IGNJrFr8mL6APJh5ELFZfAxfMXjpSidzlnS+ob2iif8/zL8bx+7drbt3gLhNGWNpFzZ1F8Ozg== X-Received: by 2002:adf:ef91:0:b0:362:4fc3:ff58 with SMTP id ffacd0b85a97d-363192ce396mr2442793f8f.54.1718834298975; Wed, 19 Jun 2024 14:58:18 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-360859b9111sm14893897f8f.63.2024.06.19.14.58.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:18 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:57 +0000 Subject: [PATCH v2 09/17] mktree: add a --literally option Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Add the '--literally' option to 'git mktree' to allow constructing a tree with invalid contents. For now, the only change this represents compared to the normal 'git mktree' behavior is no longer sorting the inputs; in later commits, deduplicaton and path validation will be added to the command and '--literally' will skip those as well. Certain tests use 'git mktree' to intentionally generate corrupt trees. Update these tests to use '--literally' so that they continue functioning properly when additional input cleanup & validation is added to the base command. Note that, because 'mktree --literally' does not sort entries, some of the tests are updated to provide their inputs in tree order; otherwise, the test would fail with an "incorrect order" error instead of the error the test expects. Signed-off-by: Victoria Dye --- Documentation/git-mktree.txt | 9 ++++++- builtin/mktree.c | 36 +++++++++++++++++++++++---- t/t1010-mktree.sh | 40 ++++++++++++++++++++++++++++++ t/t1014-read-tree-confusing.sh | 6 ++--- t/t1450-fsck.sh | 4 +-- t/t1601-index-bogus.sh | 2 +- t/t1700-split-index.sh | 6 ++--- t/t7008-filter-branch-null-sha1.sh | 6 ++--- t/t7417-submodule-path-url.sh | 2 +- t/t7450-bad-git-dotfiles.sh | 8 +++--- 10 files changed, 96 insertions(+), 23 deletions(-) diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index c187403c6bd..5f3a6dfe38e 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -9,7 +9,7 @@ git-mktree - Build a tree-object from formatted tree entries SYNOPSIS -------- [verse] -'git mktree' [-z] [--missing] [--batch] +'git mktree' [-z] [--missing] [--literally] [--batch] DESCRIPTION ----------- @@ -28,6 +28,13 @@ OPTIONS object. This option has no effect on the treatment of gitlink entries (aka "submodules") which are always allowed to be missing. +--literally:: + Create the tree from the tree entries provided to stdin in the order + they are provided without performing additional sorting, + deduplication, or path validation on them. This option is primarily + useful for creating invalid tree objects to use in tests of how Git + deals with various forms of tree corruption. + --batch:: Allow building of more than one tree object before exiting. Each tree is separated by a single blank line. The final newline is diff --git a/builtin/mktree.c b/builtin/mktree.c index f509ed1a81f..4ff99d44d79 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -48,11 +48,11 @@ static void tree_entry_array_release(struct tree_entry_array *arr, int free_entr } static void append_to_tree(unsigned mode, struct object_id *oid, const char *path, - struct tree_entry_array *arr) + struct tree_entry_array *arr, int literally) { struct tree_entry *ent; size_t len = strlen(path); - if (strchr(path, '/')) + if (!literally && strchr(path, '/')) die("path %s contains slash", path); FLEX_ALLOC_MEM(ent, name, path, len); @@ -91,14 +91,35 @@ static void write_tree(struct tree_entry_array *arr, struct object_id *oid) strbuf_release(&buf); } +static void write_tree_literally(struct tree_entry_array *arr, + struct object_id *oid) +{ + struct strbuf buf; + size_t size = 0; + + for (size_t i = 0; i < arr->nr; i++) + size += 32 + arr->entries[i]->len; + + strbuf_init(&buf, size); + for (size_t i = 0; i < arr->nr; i++) { + struct tree_entry *ent = arr->entries[i]; + strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); + strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); + } + + write_object_file(buf.buf, buf.len, OBJ_TREE, oid); + strbuf_release(&buf); +} + static const char *mktree_usage[] = { - "git mktree [-z] [--missing] [--batch]", + "git mktree [-z] [--missing] [--literally] [--batch]", NULL }; struct mktree_line_data { struct tree_entry_array *arr; int allow_missing; + int literally; }; static int mktree_line(unsigned int mode, struct object_id *oid, @@ -145,7 +166,7 @@ static int mktree_line(unsigned int mode, struct object_id *oid, } } - append_to_tree(mode, oid, path, data->arr); + append_to_tree(mode, oid, path, data->arr, data->literally); return 0; } @@ -162,6 +183,8 @@ int cmd_mktree(int ac, const char **av, const char *prefix) const struct option option[] = { OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")), OPT_BOOL(0, "missing", &mktree_line_data.allow_missing, N_("allow missing objects")), + OPT_BOOL(0, "literally", &mktree_line_data.literally, + N_("do not sort, deduplicate, or validate paths of tree entries")), OPT_BOOL(0, "batch", &is_batch_mode, N_("allow creation of more than one tree")), OPT_END() }; @@ -189,7 +212,10 @@ int cmd_mktree(int ac, const char **av, const char *prefix) */ ; /* skip creating an empty tree */ } else { - write_tree(&arr, &oid); + if (mktree_line_data.literally) + write_tree_literally(&arr, &oid); + else + write_tree(&arr, &oid); puts(oid_to_hex(&oid)); fflush(stdout); } diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 48fc532e7af..961c0c3e55e 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -129,4 +129,44 @@ test_expect_success 'mktree fails on mode mismatch' ' test_grep "object $tree_oid is a tree but specified type was (blob)" err ' +test_expect_success '--literally can create invalid trees' ' + tree_oid="$(cat tree)" && + blob_oid="$(git rev-parse ${tree_oid}:one)" && + + # duplicate entries + { + printf "040000 tree $tree_oid\tmy-tree\n" && + printf "100644 blob $blob_oid\ttest-file\n" && + printf "100755 blob $blob_oid\ttest-file\n" + } | git mktree --literally >tree.bad && + git cat-file tree $(cat tree.bad) >top.bad && + test_must_fail git hash-object --stdin -t tree err && + test_grep "contains duplicate file entries" err && + + # disallowed path + { + printf "100644 blob $blob_oid\t.git\n" + } | git mktree --literally >tree.bad && + git cat-file tree $(cat tree.bad) >top.bad && + test_must_fail git hash-object --stdin -t tree err && + test_grep "contains ${SQ}.git${SQ}" err && + + # nested entry + { + printf "100644 blob $blob_oid\tdeeper/my-file\n" + } | git mktree --literally >tree.bad && + git cat-file tree $(cat tree.bad) >top.bad && + test_must_fail git hash-object --stdin -t tree err && + test_grep "contains full pathnames" err && + + # bad entry ordering + { + printf "100644 blob $blob_oid\tB\n" && + printf "040000 tree $tree_oid\tA\n" + } | git mktree --literally >tree.bad && + git cat-file tree $(cat tree.bad) >top.bad && + test_must_fail git hash-object --stdin -t tree err && + test_grep "not properly sorted" err +' + test_done diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh index 8ea8d36818b..762eb789704 100755 --- a/t/t1014-read-tree-confusing.sh +++ b/t/t1014-read-tree-confusing.sh @@ -30,13 +30,13 @@ while read path pretty; do esac test_expect_success "reject $pretty at end of path" ' printf "100644 blob %s\t%s" "$blob" "$path" >tree && - bogus=$(git mktree tree && - bogus=$(git mktree tree && - ok=$(git mktree badtree && - badtree=$(git mktree out && test_grep "$badtree" out && test_grep "error in tree .*contains duplicate file entries" out @@ -614,7 +614,7 @@ while read name path pretty; do tree=$(git rev-parse HEAD^{tree}) && value=$(eval "echo \$$type") && printf "$mode $type %s\t%s" "$value" "$path" >bad && - bad_tree=$(git mktree out && test_grep "warning.*tree $bad_tree" out )' diff --git a/t/t1601-index-bogus.sh b/t/t1601-index-bogus.sh index 4171f1e1410..54e8ae038b7 100755 --- a/t/t1601-index-bogus.sh +++ b/t/t1601-index-bogus.sh @@ -4,7 +4,7 @@ test_description='test handling of bogus index entries' . ./test-lib.sh test_expect_success 'create tree with null sha1' ' - tree=$(printf "160000 commit $ZERO_OID\\tbroken\\n" | git mktree) + tree=$(printf "160000 commit $ZERO_OID\\tbroken\\n" | git mktree --literally) ' test_expect_success 'read-tree refuses to read null sha1' ' diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index ac4a5b2734c..97b58aa3cca 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -478,12 +478,12 @@ test_expect_success 'writing split index with null sha1 does not write cache tre git config splitIndex.maxPercentChange 0 && git commit -m "commit" && { - git ls-tree HEAD && - printf "160000 commit $ZERO_OID\\tbroken\\n" + printf "160000 commit $ZERO_OID\\tbroken\\n" && + git ls-tree HEAD } >broken-tree && echo "add broken entry" >msg && - tree=$(git mktree broken-tree && echo "add broken entry" >msg && - tree=$(git mktree tree && sed "s/sub/sub /" tree.new && - tree=$(git -C super mktree bad-tree ) && - tree=$(git -C $dir mktree <$dir/bad-tree) + tree=$(git -C $dir mktree --literally <$dir/bad-tree) ' test_expect_success "fsck detects symlinked $name ($type)" ' @@ -261,7 +261,7 @@ test_expect_success 'fsck detects non-blob .gitmodules' ' cp ../.gitmodules subdir/file && git add subdir/file && git commit -m ok && - git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree && + git ls-tree HEAD | sed s/subdir/.gitmodules/ | git mktree --literally && test_must_fail git fsck 2>output && test_grep gitmodulesBlob output From patchwork Wed Jun 19 21:57:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704628 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (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 5F0A015B973 for ; Wed, 19 Jun 2024 21:58:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834304; cv=none; b=fFtMZvaIm34e4ra6hqQ705uTeouGp5sc5MKG/W1/L3w+I6J+se75qMn6GtmGW/KASi/okRpVv6rklXUpSyWxz7oriNihfoO2MNKqf/mZJe+dFzbl257afIlh0iPD0axadjv4uoCbe3nnkVyVTc8/2D/xFpXro0NcrAa/uX48pzg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834304; c=relaxed/simple; bh=mgRjPggoTLvIAUq4XUi/VIoWIApwNBSSBMdbT6v3cjE=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=dJtmIAJ0eND07BkZnQJXIySARlTWo3d8OOB0uc5poxmsE8R1NWqXkPfi0KrXwM08T08BvXX2k2Vq2++Ra47Dg0yFSFsQr2kXhF5fT0yctOMM82PALa32M4yDJXYFQqcNqsx1mzjlS7J58qjx6xFXuhuJnqQY0OfVFSoc5AhNrP8= 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=jBwfEX8Q; arc=none smtp.client-ip=209.85.128.47 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="jBwfEX8Q" Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-424720e73e1so2570695e9.1 for ; Wed, 19 Jun 2024 14:58:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834300; x=1719439100; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=4fCagbLsgMzJIiLnvm+JEoj6itaBNRjg7hd5rkSN3Es=; b=jBwfEX8QGvKb0Q27sL1DNjkUsxDTL/J0e/urhiGRReGiBhgOCP2/5aMTQ4u2CzqEM1 IohvEiTvfJW0z5T+L7tBhHHOcongjScvKtO8HgdrzEXQenbInnsKqnWgYUaBTAFXu3Mp p7AIDgm3D+FA007+LVQVcv54UQi8UGc8x/J+P+DgglFXTb/3/Cze9s/gkVr3HGYKHirT 8pu5iAB2U6NqS9qupj6hfeZtRu1RWPFJX7t8tXdgynm6QtnyuUZYRRFllMca2LPKF1eD sm4FzRCJcnJtDs9rGM+3ayRgBAgtigj2YmKQWx/jEdrdqTMP/2zHKrF/nDfut2IKplmh RmvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834300; x=1719439100; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4fCagbLsgMzJIiLnvm+JEoj6itaBNRjg7hd5rkSN3Es=; b=VXIrlGP6qCIna1taLi7JxJV9NgMQMjLWC0I7LWjHeX2Id17lnFznXKJ6rJXd7tK9Ql 5wlIYuy5fvYYBN13KmeElBhE1NDylak2kJsTa/+d+sHIAAPVwqetbLIQ3Mpz+Ffshdi3 0eEb3xHwD8T2iZBNUXR9f8NUNmiIKrl68QBEkfVXWGGBvVZb8TdL7CDgRL/EcFu9jzxR opLIzBSeT4lyqGwHzaUMy9Jiag9lUPTy1X3YBeFjIg6XfQ4HHtQW+w1dcATENGt0fw4j U9Rtp6sBi3tZ2ZP8PccdyWErp1edADuBPmjEWxP1ctV2MCnSnLVseXdbI153htKR1XAz uwGQ== X-Gm-Message-State: AOJu0YxM2C4N6c/uDnTWk1DQXbDrWmiSXj6Xp9MfDy69qtCdKzgp3UQs eBh1KwFKKoXe/S+9mNJS+flitp4Fl0Y3bRdznuOV8CvG9DKfUV61Ji5MjA== X-Google-Smtp-Source: AGHT+IHDRJ64UPec28EWKnyZdKF44X8ub1yrDk2LKYv/p8aBlPlVYO3rsnW4QQ7jV93B8e0YBzkD+Q== X-Received: by 2002:a05:600c:8a9:b0:422:218e:b8d8 with SMTP id 5b1f17b1804b1-4247529b46bmr22518605e9.34.1718834300050; Wed, 19 Jun 2024 14:58:20 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-364c75f1454sm140384f8f.46.2024.06.19.14.58.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:19 -0700 (PDT) Message-Id: <2eb207064f80d48a7db5617feea417a015bb6082.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:58 +0000 Subject: [PATCH v2 10/17] mktree: validate paths more carefully Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Use 'verify_path' to validate the paths provided as tree entries, ensuring we do not create entries with paths not allowed in trees (e.g., .git). Also, remove trailing slashes on directories before validating, allowing users to provide 'folder-name/' as the path for a tree object entry. Signed-off-by: Victoria Dye --- builtin/mktree.c | 20 +++++++++++++++++--- t/t1010-mktree.sh | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index 4ff99d44d79..8f0af24b6b1 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -8,6 +8,7 @@ #include "hex.h" #include "index-info.h" #include "quote.h" +#include "read-cache-ll.h" #include "strbuf.h" #include "tree.h" #include "parse-options.h" @@ -52,10 +53,23 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat { struct tree_entry *ent; size_t len = strlen(path); - if (!literally && strchr(path, '/')) - die("path %s contains slash", path); - FLEX_ALLOC_MEM(ent, name, path, len); + if (literally) { + FLEX_ALLOC_MEM(ent, name, path, len); + } else { + /* Normalize and validate entry path */ + if (S_ISDIR(mode)) { + while(len > 0 && is_dir_sep(path[len - 1])) + len--; + } + FLEX_ALLOC_MEM(ent, name, path, len); + + if (!verify_path(ent->name, mode)) + die(_("invalid path '%s'"), path); + if (strchr(ent->name, '/')) + die("path %s contains slash", path); + } + ent->mode = mode; ent->len = len; oidcpy(&ent->oid, oid); diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 961c0c3e55e..7e750530455 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -169,4 +169,37 @@ test_expect_success '--literally can create invalid trees' ' test_grep "not properly sorted" err ' +test_expect_success 'mktree validates path' ' + tree_oid="$(cat tree)" && + blob_oid="$(git rev-parse $tree_oid:a/one)" && + head_oid="$(git rev-parse HEAD)" && + + # Valid: tree with or without trailing slash, blob without trailing slash + { + printf "040000 tree $tree_oid\tfolder1/\n" && + printf "040000 tree $tree_oid\tfolder2\n" && + printf "100644 blob $blob_oid\tfile.txt\n" + } | git mktree >actual && + + # Invalid: blob with trailing slash + printf "100644 blob $blob_oid\ttest/" | + test_must_fail git mktree 2>err && + test_grep "invalid path ${SQ}test/${SQ}" err && + + # Invalid: dotdot + printf "040000 tree $tree_oid\t../" | + test_must_fail git mktree 2>err && + test_grep "invalid path ${SQ}../${SQ}" err && + + # Invalid: dot + printf "040000 tree $tree_oid\t." | + test_must_fail git mktree 2>err && + test_grep "invalid path ${SQ}.${SQ}" err && + + # Invalid: .git + printf "040000 tree $tree_oid\t.git/" | + test_must_fail git mktree 2>err && + test_grep "invalid path ${SQ}.git/${SQ}" err +' + test_done From patchwork Wed Jun 19 21:57:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704629 Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) (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 B807315B0FD for ; Wed, 19 Jun 2024 21:58:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834305; cv=none; b=XKA/nJyqObF/q//Z5rIqv2/rr0k68aCj+maVLWNwXFP/m6l03c4skKTWFBy9zqZ2yN7r/2bqhokKCUWk4yRuMedmQRqClRMTpIiq1bkB3Z5GNGTo0n6DwtDuzK3Ogl04idiMUR4aBCj52p5ynw3XKAqcQYzxnxA07lLWsxQ3sJo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834305; c=relaxed/simple; bh=OuSq1EvYfSqzdvrqwl8sQiNYVSld8V1YPsltCLoibE8=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=UiMqI//mqPar5Eu5wr/OK3wbcGVvAdb489nEME7sXTNlPIQI+HN6Mihc1OlRZ9VhaLTi/7epWVbCy5g2mr5hTuvVwbpk4YfvpMSveSMM8hOnZSSb62D7Yy2YZSZLBqtIhop6CQkckn94vII6nEkiAL6z3kg38ciiQpN3OGNwXTs= 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=fFdWKGQ0; arc=none smtp.client-ip=209.85.221.50 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="fFdWKGQ0" Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-363bd55bcc2so221993f8f.2 for ; Wed, 19 Jun 2024 14:58:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834301; x=1719439101; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=oE9dkyq4QDse0xBWO2dwDE2MLj7JvTQgHs6uiJjwaFs=; b=fFdWKGQ0rcT6z5S4kHUEmIMZHsCH38Kd0iV53T8nviCAHCXTaj9ZApo9ThsoHWvOaZ YZzh/psD71usKWrzmI2t5/uMxJiq4+ttfUAcnG0l1QQ2jcpx148K2P1YogvYBIyqM80J fqoTkzRlH/J3bYgstIkA8qFoWnitcLwGCaTJcKGc6kHJnQi3vQLBrHoDfwJ44s99iA54 nlU389SzeQOiskE/1rOx2sndOFZ4uIgJXa755feR1/cB2iuFi6LW3+Too4xeIrEpkWtE MNJoT9TvEe0ds8lQ6mU2yAaH/0jcQrV/YlsDYOCdxRddVQCGOUibt9GQrw6I4TuL2EfV D1HA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834301; x=1719439101; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=oE9dkyq4QDse0xBWO2dwDE2MLj7JvTQgHs6uiJjwaFs=; b=a1iqq0A18fNj6BvgZ9pM+GwsfQwawHTiipjQbETo0lR0tMIdHhpx1q1ARbyEvFoyCE W/zhEfhwVyP5gfBUiG1mUesBR0lu+q30qy9tn7D1qv/A5tJpPElQvR7M+0yBRDJAJmT4 XVJ1EsjuMAZ38PsmuBPr2dJ5VagdoeU9/glyX0bBByJ3OCLdmxUpVYJqe108W3YU16p8 DMilUAXuVuUrA/xh2QLQ2q3RPnVWwXTCO8VSJvfyxsYM5DsZO2TNfWyl0YSdSdTw+ISt k25+dJhy3W7BVEiiQcRybXTGcj1EJvQI53zjrYgvqFiCLRXFAQ9Us4gZhSWoLZA5Nb8Q Ov6w== X-Gm-Message-State: AOJu0Yx8psbfkdkyPRdmQajC5SBcmv7UHFgQxyAITETrV1L48HnWxtyM 6daLm/ZulwNkGjUYlG84oJY2MBg+zTcN4YbvNgwOrLg/W6oR4Vgh5U2I5g== X-Google-Smtp-Source: AGHT+IHz9UMHMmyKYpK9qddVzl2uDKXmEu1+XUMGhrlLgUIq3/KcNQ6WheeZULHzbpkSEtU1a4do0w== X-Received: by 2002:a5d:55d1:0:b0:35f:1dce:8671 with SMTP id ffacd0b85a97d-36317898270mr2554771f8f.25.1718834301463; Wed, 19 Jun 2024 14:58:21 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-36318534adfsm3798654f8f.42.2024.06.19.14.58.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:20 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Wed, 19 Jun 2024 21:57:59 +0000 Subject: [PATCH v2 11/17] mktree: overwrite duplicate entries Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye If multiple tree entries with the same name are provided as input to 'mktree', only write the last one to the tree. Entries are considered duplicates if they have identical names (*not* considering mode); if a blob and a tree with the same name are provided, only the last one will be written to the tree. A tree with duplicate entries is invalid (per 'git fsck'), so that condition should be avoided wherever possible. Signed-off-by: Victoria Dye --- Documentation/git-mktree.txt | 3 ++- builtin/mktree.c | 45 ++++++++++++++++++++++++++++++++---- t/t1010-mktree.sh | 36 +++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index 5f3a6dfe38e..cf1fd82f754 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -54,7 +54,8 @@ cannot be represented in a tree object. The command will fail without writing the tree if a higher order stage is specified for any entry. The order of the tree entries is normalized by `mktree` so pre-sorting the -input by path is not required. +input by path is not required. Multiple entries provided with the same path +are deduplicated, with only the last one specified added to the tree. GIT --- diff --git a/builtin/mktree.c b/builtin/mktree.c index 8f0af24b6b1..a91d3a7b028 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -15,6 +15,9 @@ #include "object-store-ll.h" struct tree_entry { + /* Internal */ + size_t order; + unsigned mode; struct object_id oid; int len; @@ -74,15 +77,49 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat ent->len = len; oidcpy(&ent->oid, oid); + ent->order = arr->nr; tree_entry_array_push(arr, ent); } -static int ent_compare(const void *a_, const void *b_) +static int ent_compare(const void *a_, const void *b_, void *ctx) { + int cmp; struct tree_entry *a = *(struct tree_entry **)a_; struct tree_entry *b = *(struct tree_entry **)b_; - return base_name_compare(a->name, a->len, a->mode, - b->name, b->len, b->mode); + int ignore_mode = *((int *)ctx); + + if (ignore_mode) + cmp = name_compare(a->name, a->len, b->name, b->len); + else + cmp = base_name_compare(a->name, a->len, a->mode, + b->name, b->len, b->mode); + return cmp ? cmp : b->order - a->order; +} + +static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) +{ + size_t count = arr->nr; + struct tree_entry *prev = NULL; + + int ignore_mode = 1; + QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode); + + arr->nr = 0; + for (size_t i = 0; i < count; i++) { + struct tree_entry *curr = arr->entries[i]; + if (prev && + !name_compare(prev->name, prev->len, + curr->name, curr->len)) { + FREE_AND_NULL(curr); + } else { + arr->entries[arr->nr++] = curr; + prev = curr; + } + } + + /* Sort again to order the entries for tree insertion */ + ignore_mode = 0; + QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode); } static void write_tree(struct tree_entry_array *arr, struct object_id *oid) @@ -90,7 +127,7 @@ static void write_tree(struct tree_entry_array *arr, struct object_id *oid) struct strbuf buf; size_t size = 0; - QSORT(arr->entries, arr->nr, ent_compare); + sort_and_dedup_tree_entry_array(arr); for (size_t i = 0; i < arr->nr; i++) size += 32 + arr->entries[i]->len; diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 7e750530455..08760141d6f 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -6,11 +6,16 @@ TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' - for d in a a- a0 + for d in folder folder- folder0 do mkdir "$d" && echo "$d/one" >"$d/one" && git add "$d" || return 1 done && + for f in before folder.txt later + do + echo "$f" >"$f" && + git add "$f" || return 1 + done && echo zero >one && git update-index --add --info-only one && git write-tree --missing-ok >tree.missing && @@ -171,7 +176,7 @@ test_expect_success '--literally can create invalid trees' ' test_expect_success 'mktree validates path' ' tree_oid="$(cat tree)" && - blob_oid="$(git rev-parse $tree_oid:a/one)" && + blob_oid="$(git rev-parse $tree_oid:folder.txt)" && head_oid="$(git rev-parse HEAD)" && # Valid: tree with or without trailing slash, blob without trailing slash @@ -202,4 +207,31 @@ test_expect_success 'mktree validates path' ' test_grep "invalid path ${SQ}.git/${SQ}" err ' +test_expect_success 'mktree with duplicate entries' ' + tree_oid=$(cat tree) && + folder_oid=$(git rev-parse ${tree_oid}:folder) && + before_oid=$(git rev-parse ${tree_oid}:before) && + head_oid=$(git rev-parse HEAD) && + + { + printf "100755 blob $before_oid\ttest\n" && + printf "040000 tree $folder_oid\ttest-\n" && + printf "160000 commit $head_oid\ttest.txt\n" && + printf "040000 tree $folder_oid\ttest\n" && + printf "100644 blob $before_oid\ttest0\n" && + printf "160000 commit $head_oid\ttest-\n" + } >top.dup && + git mktree tree.actual && + + { + printf "160000 commit $head_oid\ttest-\n" && + printf "160000 commit $head_oid\ttest.txt\n" && + printf "040000 tree $folder_oid\ttest\n" && + printf "100644 blob $before_oid\ttest0\n" + } >expect && + git ls-tree $(cat tree.actual) >actual && + + test_cmp expect actual +' + test_done From patchwork Wed Jun 19 21:58:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704630 Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com [209.85.128.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 52B6215CD77 for ; Wed, 19 Jun 2024 21:58:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834306; cv=none; b=gaSegG4Ugv+zbGWelUOqTsNymJoU2DcULk/axIZXCQ8/pzfsxOFX1Xq+eE8XWJMbV4vzriSPfTp7m51ZmPGUWMAVueZ2nAX7clv7pqB2QHLodHdSUYFRZfzDcJFeQ/ND0P2o4YOssUr3IZY9oWabis8XQ7TyjGHwn5e94o0WXKA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834306; c=relaxed/simple; bh=SS1VX/bHVOIg70C8ZnfbqUdEsdROiq0mxqgBLnIMsJ8=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=tcjfqU5oV7bg2yd7uIgjCxx0EKKVAJ3jUuS1umkE0CkA/KyEdQZGAFeSB+cl7UzIXp6c2qQhTX9VW+PgLNYWagVxeHKZVvjQk06FZ7vh9U4IoM9eq/GrLXnAhaCJGjB3NDKrtyrZzJ2QuDhs9EVvVtl3qBXaGBKvEJSmqH5YI4o= 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=TOe5YHgo; arc=none smtp.client-ip=209.85.128.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="TOe5YHgo" Received: by mail-wm1-f46.google.com with SMTP id 5b1f17b1804b1-4217a96de38so2268045e9.1 for ; Wed, 19 Jun 2024 14:58:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834303; x=1719439103; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=PQrXYa+jFKbFvvZBMObzmsBQhCfNXAtt4FxFgErbZ7w=; b=TOe5YHgoftAAISywNoU4wAhIthKEzhQ8vqwewGB/O/E1l7mInYCWnQBoqsfEFjtRL8 fMR5Wo9FydcVnuyQ6UhdvQyi7SnEiAxa9YqwHr1hUwEEwdz1sDyqn2Cdiuw6K9ZkIBxk JoKpQG/uvjGnvS3KmPZDNkfy7/7ZNvLPm7kL6TSMzXZ5gZ1IgG8vi8FHrdpRuhk39Dfw An2v1LtAqHBM3lA0xT7hPN78NpoEwo61XQ/C+a1S5hLjOQ89sAcLxnVwNSTf/eMe11ho xfmYGF5DnCpTGG/rht0py9hpFzeyiIJ8HjyIM9qTgpTYZ/Ee6/G49Kxyn292OBOsP/uW q0cQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834303; x=1719439103; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=PQrXYa+jFKbFvvZBMObzmsBQhCfNXAtt4FxFgErbZ7w=; b=wL/ZeJTkx5M0guTReyhAloIgLJNtmI8WssOonCZ8mcaINTv0ezO5siACB6vg0niShR lzEVKJ0jEqeaVYPITTxYddNkfNLahKkAmDCaKRrK4ffYDFKSdAc8HKfRPvhoEgtoiNsU VhqnlqNFLhEXtkgtqfDP9yLzlIdnl0RUbGiQQv4ggHUhuT2DpOph5/OTFWHxRIeB4/+J h1P6YehFKCIxGASMUneImi+ucfAmU88XY0TiqEEv1c5jVx0/lp7zz1LOJJEt+hzNHlmD xCBfwWFmKCjqViIT4ySTsSNmzN8mcmOdoXGYT2c5iq7//bzisMkoEwRlhJAaSMufMFr8 q8cw== X-Gm-Message-State: AOJu0YyHFqxDPas2o839NJ6LM/Y2WFe4WPpy1ROr+rlpA6c89hiCO1oW 2kEyLknkmuvOMvcq12GqQOGnFQjrPguCuFla1GvlxLKYqZHkElDdSWl1pg== X-Google-Smtp-Source: AGHT+IFJTwDuSpUn2tuLYmNKzQDVyeaiwg5zkq6U9wRpq5RCFozxtqRVZKedMMfsDOv2qbtyy5UBeA== X-Received: by 2002:a05:600c:4a18:b0:422:13b2:584 with SMTP id 5b1f17b1804b1-4247529bc53mr24316815e9.37.1718834303032; Wed, 19 Jun 2024 14:58:23 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0b8c5fsm3848905e9.6.2024.06.19.14.58.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:21 -0700 (PDT) Message-Id: <2333775ba5bd71766a6aece87e39a6d189aeaead.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:58:00 +0000 Subject: [PATCH v2 12/17] mktree: create tree using an in-core index Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Rather than manually write out the contents of a tree object file, construct an in-memory sparse index from the provided tree entries and create the tree by writing out its corresponding cache tree. This patch does not change the behavior of the 'mktree' command. However, constructing the tree this way will substantially simplify future extensions to the command's functionality, including handling deeper-than-toplevel tree entries and applying the provided entries to an existing tree. Signed-off-by: Victoria Dye --- builtin/mktree.c | 74 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index a91d3a7b028..3ce8d3dc524 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -4,6 +4,7 @@ * Copyright (c) Junio C Hamano, 2006, 2009 */ #include "builtin.h" +#include "cache-tree.h" #include "gettext.h" #include "hex.h" #include "index-info.h" @@ -24,6 +25,11 @@ struct tree_entry { char name[FLEX_ARRAY]; }; +static inline size_t df_path_len(size_t pathlen, unsigned int mode) +{ + return S_ISDIR(mode) ? pathlen - 1 : pathlen; +} + struct tree_entry_array { size_t nr, alloc; struct tree_entry **entries; @@ -60,17 +66,25 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat if (literally) { FLEX_ALLOC_MEM(ent, name, path, len); } else { + size_t len_to_copy = len; + /* Normalize and validate entry path */ if (S_ISDIR(mode)) { - while(len > 0 && is_dir_sep(path[len - 1])) - len--; + while(len_to_copy > 0 && is_dir_sep(path[len_to_copy - 1])) + len_to_copy--; + len = len_to_copy + 1; /* add space for trailing slash */ } - FLEX_ALLOC_MEM(ent, name, path, len); + ent = xcalloc(1, st_add3(sizeof(struct tree_entry), len, 1)); + memcpy(ent->name, path, len_to_copy); if (!verify_path(ent->name, mode)) die(_("invalid path '%s'"), path); if (strchr(ent->name, '/')) die("path %s contains slash", path); + + /* Add trailing slash to dir */ + if (S_ISDIR(mode)) + ent->name[len - 1] = '/'; } ent->mode = mode; @@ -88,11 +102,14 @@ static int ent_compare(const void *a_, const void *b_, void *ctx) struct tree_entry *b = *(struct tree_entry **)b_; int ignore_mode = *((int *)ctx); - if (ignore_mode) - cmp = name_compare(a->name, a->len, b->name, b->len); - else - cmp = base_name_compare(a->name, a->len, a->mode, - b->name, b->len, b->mode); + size_t a_len = a->len, b_len = b->len; + + if (ignore_mode) { + a_len = df_path_len(a_len, a->mode); + b_len = df_path_len(b_len, b->mode); + } + + cmp = name_compare(a->name, a_len, b->name, b_len); return cmp ? cmp : b->order - a->order; } @@ -108,8 +125,8 @@ static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) for (size_t i = 0; i < count; i++) { struct tree_entry *curr = arr->entries[i]; if (prev && - !name_compare(prev->name, prev->len, - curr->name, curr->len)) { + !name_compare(prev->name, df_path_len(prev->len, prev->mode), + curr->name, df_path_len(curr->len, curr->mode))) { FREE_AND_NULL(curr); } else { arr->entries[arr->nr++] = curr; @@ -122,24 +139,43 @@ static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode); } +static int add_tree_entry_to_index(struct index_state *istate, + struct tree_entry *ent) +{ + struct cache_entry *ce; + struct strbuf ce_name = STRBUF_INIT; + strbuf_add(&ce_name, ent->name, ent->len); + + ce = make_cache_entry(istate, ent->mode, &ent->oid, ent->name, 0, 0); + if (!ce) + return error(_("make_cache_entry failed for path '%s'"), ent->name); + + add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND); + strbuf_release(&ce_name); + return 0; +} + static void write_tree(struct tree_entry_array *arr, struct object_id *oid) { - struct strbuf buf; - size_t size = 0; + struct index_state istate = INDEX_STATE_INIT(the_repository); + istate.sparse_index = 1; sort_and_dedup_tree_entry_array(arr); - for (size_t i = 0; i < arr->nr; i++) - size += 32 + arr->entries[i]->len; - strbuf_init(&buf, size); + /* Construct an in-memory index from the provided entries */ for (size_t i = 0; i < arr->nr; i++) { struct tree_entry *ent = arr->entries[i]; - strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); - strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); + + if (add_tree_entry_to_index(&istate, ent)) + die(_("failed to add tree entry '%s'"), ent->name); } - write_object_file(buf.buf, buf.len, OBJ_TREE, oid); - strbuf_release(&buf); + /* Write out new tree */ + if (cache_tree_update(&istate, WRITE_TREE_SILENT | WRITE_TREE_MISSING_OK)) + die(_("failed to write tree")); + oidcpy(oid, &istate.cache_tree->oid); + + release_index(&istate); } static void write_tree_literally(struct tree_entry_array *arr, From patchwork Wed Jun 19 21:58:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704631 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (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 A8BD415D5B3 for ; Wed, 19 Jun 2024 21:58:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834308; cv=none; b=BDK5JKibWcKA4bw/Qy6pVLwa73Vn8KKLnTSaBP9wjhNRSnOao41zDg5FEq3OWVzahrWeUWmegpwcnQM9y71FbIpuauhztYHLsfUMCvIv7v8kf4muw5nNXLgJUVGj74IAHCYQ1CquSVBrlsjwdKopbtOqiTNdQfvlvQhvpkkBFAE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834308; c=relaxed/simple; bh=jcZMD+8RfTEOcB1+UKG0s/gAmNFV7KsJj1hSTvdFnqg=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=DGkjqww9ByTmyXG/JvZeAAEjyo4B699TRupMg5egBbNv+cEejmeGFOs8w9mcM+qom2UbBklBwNYR2V0+kyAd1OD4k0BmT7P0of6SBrmrBevLgBaM0WJFC9EuRwske9ipvRDpqwtPE8oqPgf+LSh2dW0qxPBkmkJD9Gf81IievUw= 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=aOPv2QOz; arc=none smtp.client-ip=209.85.221.54 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="aOPv2QOz" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-364a39824baso160421f8f.1 for ; Wed, 19 Jun 2024 14:58:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834304; x=1719439104; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=N8aWo1CHlqx8/5ZeKAeCnaltdbcBQkOzvetzcKsMhh8=; b=aOPv2QOzOAAeS8WPuZPn/ZAvGU+Xwi0LeLxbDtvfmz3fRBhPRYEagNSw/FrVZ+zFXw i/zGr4sXV/hVMXJLec/XL6YVpJLELXkUZ8BkkAoE46zsf6m+XV1AzIWB1Od3fTseCbMT sTK/MoqfSMjA3hHG+ntd0DZ4Ac29FOvxqL38oFzc7xZSqJ3ztEcQtUiyY6TPNtCJ/zC7 iBL/ANTEgxGFd1jdbncqUMwhvER81HVO6xuSoy4Jzcl9UIsndIQNpXUFdX0OaE0aDy+V kfyr/LxyU6P+k6o4GyAufXOfWctSuykP2mBMV1sGIRaxEZXGIjSEv+6NZtSFFd5W8lsf RP1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834304; x=1719439104; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=N8aWo1CHlqx8/5ZeKAeCnaltdbcBQkOzvetzcKsMhh8=; b=LGMCwwDRf2zz+1LYV7SdZMYrU4BCmztf/D40woFh4Jxw7CUrSYcMsOgpR0kulAE/nU HzSJVGCgR556z1cRHz4liEtHAPdCKF0pqrPFYQk1odwF9nhoO512nddoHQCrqCJJNIss FX7MyWVHnSebGVzXiTHdx8FxWcFfQjUGROPAnQq5xNzcgtclPD7pCy2Xr7VhA7aIyV0Y Gnx8z2uCOqUGZbXQNH8vivbAdhRI8QV4ig3djDlMRycE5c2WK2knN3UOlPbNv1+m5e3n WdN4bXdZBKzi84h2Ogq20zV2np40zlMvpL2StVVyy8SwZzO4G8mO+AAZYo4G+11ZXl7W u8Dw== X-Gm-Message-State: AOJu0YyO2UAaB2p5cx3ZnEL3NbLclpi5x+1G5SwoOfznurXCh08Aw5Ai L7EQo0WR5LRs4tEDclgwKtqBtGKuyAF9YGI9MMLwQtkqqMC6ulgLOyxPXg== X-Google-Smtp-Source: AGHT+IExqKgpraiuhstAgaLW9ju1iXv7za4P5uQ4vt375O5gu3x5pZo6fp4yUTuaWcrmf909/QoEWQ== X-Received: by 2002:a05:600c:15cc:b0:421:8445:4f8d with SMTP id 5b1f17b1804b1-4247529e08amr23221825e9.38.1718834304472; Wed, 19 Jun 2024 14:58:24 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0c0f35sm3788815e9.18.2024.06.19.14.58.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:23 -0700 (PDT) Message-Id: <56f28efff5404a3fa22bd544d6de8ce2d919b78a.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:58:01 +0000 Subject: [PATCH v2 13/17] mktree: use iterator struct to add tree entries to index Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Create 'struct tree_entry_iterator' to manage iteration through a 'struct tree_entry_array'. Using an iterator allows for conditional iteration; this functionality will be necessary in later commits when performing parallel iteration through multiple sets of tree entries. Signed-off-by: Victoria Dye --- builtin/mktree.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/builtin/mktree.c b/builtin/mktree.c index 3ce8d3dc524..344c9b9b6fe 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -139,6 +139,35 @@ static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode); } +struct tree_entry_iterator { + struct tree_entry *current; + + /* private */ + struct { + struct tree_entry_array *arr; + size_t idx; + } priv; +}; + +static void tree_entry_iterator_init(struct tree_entry_iterator *iter, + struct tree_entry_array *arr) +{ + iter->priv.arr = arr; + iter->priv.idx = 0; + iter->current = 0 < arr->nr ? arr->entries[0] : NULL; +} + +/* + * Advance the tree entry iterator to the next entry in the array. If no + * entries remain, 'current' is set to NULL. + */ +static void tree_entry_iterator_advance(struct tree_entry_iterator *iter) +{ + iter->current = (iter->priv.idx + 1) < iter->priv.arr->nr + ? iter->priv.arr->entries[++iter->priv.idx] + : NULL; +} + static int add_tree_entry_to_index(struct index_state *istate, struct tree_entry *ent) { @@ -157,14 +186,18 @@ static int add_tree_entry_to_index(struct index_state *istate, static void write_tree(struct tree_entry_array *arr, struct object_id *oid) { + struct tree_entry_iterator iter = { NULL }; struct index_state istate = INDEX_STATE_INIT(the_repository); istate.sparse_index = 1; sort_and_dedup_tree_entry_array(arr); - /* Construct an in-memory index from the provided entries */ - for (size_t i = 0; i < arr->nr; i++) { - struct tree_entry *ent = arr->entries[i]; + tree_entry_iterator_init(&iter, arr); + + /* Construct an in-memory index from the provided entries & base tree */ + while (iter.current) { + struct tree_entry *ent = iter.current; + tree_entry_iterator_advance(&iter); if (add_tree_entry_to_index(&istate, ent)) die(_("failed to add tree entry '%s'"), ent->name); From patchwork Wed Jun 19 21:58:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704632 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.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 236E915D5D7 for ; Wed, 19 Jun 2024 21:58:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834309; cv=none; b=U/x9QQmLt87al4u6DPwhZi2noZbqwPvYaXt4sRoU20toTM2yhqwujeL55vxhZtK38DZffwZXfX6S9XkW646E3/s993Mhx3Qwb6TNCD7Ndsd0OTu7bL5buc5e6MLHq7Olaj4pLhumlEj65tZld80p2fTg3pMpCvNi9VJmCQATUGM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834309; c=relaxed/simple; bh=PRVd/xG7+6beWajA3bSpu/PNzCIJaDbT+QMzqieVCvo=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=TR/ac5KGSGgQbt+7ZMCasvYClAZH3XQURvIIxGPlx/UpX7KwSIAjbdOpPA6d8WJD7MUcornJgzgaGYSJCLpyJZVfg9WcgUFTjX8gTjw1O+q+0tVHkfhd/zjImMIzbWRv46n4VcAXTe9ZFN3yZRJY43GOfjODwqwQe6grjfRq3UI= 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=AkyUSaow; arc=none smtp.client-ip=209.85.128.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="AkyUSaow" Received: by mail-wm1-f51.google.com with SMTP id 5b1f17b1804b1-4218314a6c7so2646765e9.0 for ; Wed, 19 Jun 2024 14:58:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834306; x=1719439106; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=W1xjuy9nL0JZHkUaSByuJIWU3op6pW0/jKvj7oChM7k=; b=AkyUSaowc6B9x74WlNmGc0kA1vcmMIPxQXgXFSXtFbSKMKu602JLpd1Pgc2pRciL96 1MpFL6VoPf/4PwFR1tTR4MyQNOjMsseaUvdQDtd1Tj13y78CVkcPvP1Ui6M2Y2wUkgw3 J8zAsA4V1rShyPBv9vQGjTsDDHqwet+pJo6YpnpZSuvOo9bIVE8deBhX+K15pUoCj9Om N6aCxOHrzjzynUqlHjAvwoDi+aGrMYGNpiS0tP74981Pf+fQXXIfDOMZQCdG/zu44Xq0 LGn5ffQMm7M2X0Pc0oXwn9zE6yT3eRF0rhapmj/jBP61g810wKSgbCjmdzrj1b985l1i 8ERQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834306; x=1719439106; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=W1xjuy9nL0JZHkUaSByuJIWU3op6pW0/jKvj7oChM7k=; b=aWQIFkoa1A6p3OblsNRFAYdjX9ySpNb/6F839X8Rh6HYqWlENSyYBnJD1+5chia5Sk XeWNQMaOe4dzC5NVqdVFGQsvY3PKJPKUvnQ+KaSTWhgQAr6U5S0aRkpN7kahnjJqjKG2 APA16w7Vb6YGCW4lMR5TzHPEoWO7zU8/8PAToR24VLDa1FJoy6Gjvjm64cEEt4pdORzV zla62asgwQ6lkbIKlFHz2PxN/Da16i6/Qo3noHksx7MxZsSmx8Xb4KohXSxrJWazzCtw ZsKZ77E4+PAuVJccQDLdpEhORFoFYp8eiyVERi1YezhCWMQBXYyLrpqkZp8sj+pEAFnd rPiA== X-Gm-Message-State: AOJu0YyBOxJYasUvMwzKrgH4fJK7VGVXEq0HwfwBRNvh8PR92qQapyqZ KNrr2XERuNPtiDjZhoLeTF0faOL1W66bAtOGfXh/NdENGEvGdVHywUgHVQ== X-Google-Smtp-Source: AGHT+IFO1D7InHSFOytuUAHZNiMIqW+Or3k2ozPzbMvvV6m9eORZPvHRMAEhcgYpMFpbgxwj2nqigw== X-Received: by 2002:a7b:cb93:0:b0:421:82ed:28d1 with SMTP id 5b1f17b1804b1-424752a3cbcmr22173945e9.41.1718834305799; Wed, 19 Jun 2024 14:58:25 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0c0d56sm3826815e9.13.2024.06.19.14.58.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:24 -0700 (PDT) Message-Id: <6f6d78ae7acb35991afbeaef9b61af892af93ca1.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:58:02 +0000 Subject: [PATCH v2 14/17] mktree: add directory-file conflict hashmap Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Create a hashmap member of a 'struct tree_entry_array' that contains all of the (de-duplicated) provided tree entries, indexed by the hash of their path with *no* trailing slash. This hashmap will be used in a later commit to avoid adding a file to an existing tree that has the same path as a directory, or vice versa. Signed-off-by: Victoria Dye --- builtin/mktree.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/builtin/mktree.c b/builtin/mktree.c index 344c9b9b6fe..b4d71dcdd02 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -16,6 +16,8 @@ #include "object-store-ll.h" struct tree_entry { + struct hashmap_entry ent; + /* Internal */ size_t order; @@ -33,8 +35,33 @@ static inline size_t df_path_len(size_t pathlen, unsigned int mode) struct tree_entry_array { size_t nr, alloc; struct tree_entry **entries; + + struct hashmap df_name_hash; }; +static int df_name_hash_cmp(const void *cmp_data UNUSED, + const struct hashmap_entry *eptr, + const struct hashmap_entry *entry_or_key, + const void *keydata UNUSED) +{ + const struct tree_entry *e1, *e2; + size_t e1_len, e2_len; + + e1 = container_of(eptr, const struct tree_entry, ent); + e2 = container_of(entry_or_key, const struct tree_entry, ent); + + e1_len = df_path_len(e1->len, e1->mode); + e2_len = df_path_len(e2->len, e2->mode); + + return e1_len != e2_len || + name_compare(e1->name, e1_len, e2->name, e2_len); +} + +static void tree_entry_array_init(struct tree_entry_array *arr) +{ + hashmap_init(&arr->df_name_hash, df_name_hash_cmp, NULL, 0); +} + static void tree_entry_array_push(struct tree_entry_array *arr, struct tree_entry *ent) { ALLOC_GROW(arr->entries, arr->nr + 1, arr->alloc); @@ -48,6 +75,7 @@ static void tree_entry_array_clear(struct tree_entry_array *arr, int free_entrie FREE_AND_NULL(arr->entries[i]); } arr->nr = 0; + hashmap_clear(&arr->df_name_hash); } static void tree_entry_array_release(struct tree_entry_array *arr, int free_entries) @@ -137,6 +165,14 @@ static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) /* Sort again to order the entries for tree insertion */ ignore_mode = 0; QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode); + + /* Finally, initialize the directory-file conflict hash map */ + for (size_t i = 0; i < count; i++) { + struct tree_entry *curr = arr->entries[i]; + hashmap_entry_init(&curr->ent, + memhash(curr->name, df_path_len(curr->len, curr->mode))); + hashmap_put(&arr->df_name_hash, &curr->ent); + } } struct tree_entry_iterator { @@ -311,6 +347,8 @@ int cmd_mktree(int ac, const char **av, const char *prefix) ac = parse_options(ac, av, prefix, option, mktree_usage, 0); + tree_entry_array_init(&arr); + do { ret = read_index_info(nul_term_line, mktree_line, &mktree_line_data, &line); if (ret < 0) From patchwork Wed Jun 19 21:58:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704633 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (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 4056B15DBA8 for ; Wed, 19 Jun 2024 21:58:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834311; cv=none; b=OMYjLBdy1YjWbgVtMtou5OWkcwieVSLOXzzc4N/rwX+Fkt6kPy9Umkml4t3vK21/gNJJHobclaTM9Hnz2GpcP8fSPO3VHc70PPq7At3ztAF/EKksdPQ6yacWOO6bejwvKNT0iOYkof3wZaPoSwDi2cYvGeCgJrYGjuH77jDat3c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834311; c=relaxed/simple; bh=EJkvuGbB5Mho8ZJAzXf+luq5+goTwPnEryY86pw2wh8=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=qTcIMGVkYFWpNrMfHBD0yC0F+BoB51ijL60YI+YlQc5c7Tbef62HsmXwHT+ZR4e31FIhPM1abwRd0Uqx9zs9KDKGOzs0dYfjul0qaH+pxrfwzftsfmWg9jwPmeniMQkBkGQABp/SJwaRHJqqglS/cGOmo9bgcpGwUFrwORwgUb4= 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=e0sHLTBy; arc=none smtp.client-ip=209.85.221.43 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="e0sHLTBy" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-3639e27a43dso107030f8f.2 for ; Wed, 19 Jun 2024 14:58:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834307; x=1719439107; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=zRmF5QXkD3MGeDDaTRzsQrGMPbXgrgLXbOYzwoc+vrk=; b=e0sHLTByoPy+ke1ysv1pXtBImYt2iO7hnDSMpa4pfdlFA8f0FwA+FiEFXxN/5r2JZ1 Wl94JIXByNyM05wRS7o7ZJEPCRPZ6W7DbMmwwrtH7x7gwe59HOgw65ZQa+MXQ96aYakz 4QDgyr/g8sNrPtXBCREDmJLeGx0LJXphq6UvA/71B7Ao7+Rx2aGgnWIL7IoVg38d2U7R CQF/Te6ALjPfyykJpNoXMqgkNOZbzzs0je3Z52eeViNtO72uiShkBs0ENSGrPqVvylh6 EuLYpFqoWasu0ueTC3MmGG5CIV0Thi3CaXvuD2BTbQp1BQzIHKhISv1k/5HgW8bmY7Ch hKWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834307; x=1719439107; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zRmF5QXkD3MGeDDaTRzsQrGMPbXgrgLXbOYzwoc+vrk=; b=bXyuZ4efyvam5VQ8XkLo44Wsqg6iB0FdDLXK1HtzkcxRir7qQrHjXZlPe3JMzYPMx/ sOpej4cMvGilTObnM4uEPUxquysC6r3SRJEUGtP2aSY1TkH5ennmefM56OvFBYHeG8A+ xA/ms7O1hS6TDFv4zz8HqdwrjxtfUw08hkh/xos/n6R0J7zkwUFOp3BZs5c9mNZNh/iW i44i8I0hKLibdWnUEpXoTiAkHAS9I3E61bgc9+26pt/qNPcuJn6h/mojGj9rUo+iTx4/ WhUyhaHOY+0IizhIMBv2XvnkQrugb1OyOXMWN32+fl3lZQGa02HzHTw+YvHHtpJno+OC TW7Q== X-Gm-Message-State: AOJu0YxDbF/isG3aHdy93+EJ5Skbl9BEaHGsU6m4WxkkSwnwtVTXH0fg 10+JbtV//Uswn2AP1XE0ITU3RWEwvlcaDYJh1akrBnJF2Bkr2YRPYhbfYw== X-Google-Smtp-Source: AGHT+IGcHv7JqQ4DPVMWD80ryrgmqhEWj7OI8cfewqRMPZ1QCfGwTsSaMtNGbsrZ0+/rVN/qCDKmKg== X-Received: by 2002:a5d:4709:0:b0:361:fd04:95ed with SMTP id ffacd0b85a97d-363176ad7ebmr2803945f8f.15.1718834307247; Wed, 19 Jun 2024 14:58:27 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3607510340fsm18210002f8f.93.2024.06.19.14.58.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:26 -0700 (PDT) Message-Id: <4b88f84b933b1598d12e3620f0c9fb85c559e8fb.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:58:03 +0000 Subject: [PATCH v2 15/17] mktree: optionally add to an existing tree Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Allow users to specify a single "tree-ish" value as a positional argument. If provided, the contents of the given tree serve as the basis for the new tree (or trees, in --batch mode) created by 'mktree', on top of which all of the stdin-provided tree entries are applied. At a high level, the entries are "applied" to a base tree by iterating through the base tree using 'read_tree' in parallel with iterating through the sorted & deduplicated stdin entries via their iterator. That is, for each call to the 'build_index_from_tree callback of 'read_tree': * If the iterator entry precedes the base tree entry, add it to the in-core index, increment the iterator, and repeat. * If the iterator entry has the same name as the base tree entry, add the iterator entry to the index, increment the iterator, and return from the callback to continue the 'read_tree' iteration. * If the iterator entry follows the base tree entry, first check 'df_name_hash' to ensure we won't be adding an entry with the same name later (with a different mode). If there's no directory/file conflict, add the base tree entry to the index. In either case, return from the callback to continue the 'read_tree' iteration. Finally, once 'read_tree' is complete, add the remaining entries in the iterator to the index and write out the index as a tree. Signed-off-by: Victoria Dye --- Documentation/git-mktree.txt | 7 +- builtin/mktree.c | 138 +++++++++++++++++++++++++++++------ t/t1010-mktree.sh | 36 +++++++++ 3 files changed, 159 insertions(+), 22 deletions(-) diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index cf1fd82f754..260d0e0bd7b 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -9,7 +9,7 @@ git-mktree - Build a tree-object from formatted tree entries SYNOPSIS -------- [verse] -'git mktree' [-z] [--missing] [--literally] [--batch] +'git mktree' [-z] [--missing] [--literally] [--batch] [] DESCRIPTION ----------- @@ -41,6 +41,11 @@ OPTIONS optional. Note - if the `-z` option is used, lines are terminated with NUL. +:: + If provided, the tree entries provided in stdin are added to this + tree rather than a new empty one, replacing existing entries with + identical names. Not compatible with `--literally`. + INPUT FORMAT ------------ Tree entries may be specified in any of the formats compatible with the diff --git a/builtin/mktree.c b/builtin/mktree.c index b4d71dcdd02..96f06547a2a 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -12,7 +12,9 @@ #include "read-cache-ll.h" #include "strbuf.h" #include "tree.h" +#include "object-name.h" #include "parse-options.h" +#include "pathspec.h" #include "object-store-ll.h" struct tree_entry { @@ -204,47 +206,124 @@ static void tree_entry_iterator_advance(struct tree_entry_iterator *iter) : NULL; } -static int add_tree_entry_to_index(struct index_state *istate, +struct build_index_data { + struct tree_entry_iterator iter; + struct hashmap *df_name_hash; + struct index_state istate; +}; + +static int add_tree_entry_to_index(struct build_index_data *data, struct tree_entry *ent) { struct cache_entry *ce; - struct strbuf ce_name = STRBUF_INIT; - strbuf_add(&ce_name, ent->name, ent->len); - - ce = make_cache_entry(istate, ent->mode, &ent->oid, ent->name, 0, 0); + ce = make_cache_entry(&data->istate, ent->mode, &ent->oid, ent->name, 0, 0); if (!ce) return error(_("make_cache_entry failed for path '%s'"), ent->name); - add_index_entry(istate, ce, ADD_CACHE_JUST_APPEND); - strbuf_release(&ce_name); + add_index_entry(&data->istate, ce, ADD_CACHE_JUST_APPEND); return 0; } -static void write_tree(struct tree_entry_array *arr, struct object_id *oid) +static int build_index_from_tree(const struct object_id *oid, + struct strbuf *base, const char *filename, + unsigned mode, void *context) { - struct tree_entry_iterator iter = { NULL }; - struct index_state istate = INDEX_STATE_INIT(the_repository); - istate.sparse_index = 1; + int result; + struct tree_entry *base_tree_ent; + struct build_index_data *cbdata = context; + size_t filename_len = strlen(filename); + size_t path_len = S_ISDIR(mode) ? st_add3(filename_len, base->len, 1) + : st_add(filename_len, base->len); + + /* Create a tree entry from the current entry in read_tree iteration */ + base_tree_ent = xcalloc(1, st_add3(sizeof(struct tree_entry), path_len, 1)); + base_tree_ent->len = path_len; + base_tree_ent->mode = mode; + oidcpy(&base_tree_ent->oid, oid); + + memcpy(base_tree_ent->name, base->buf, base->len); + memcpy(base_tree_ent->name + base->len, filename, filename_len); + if (S_ISDIR(mode)) + base_tree_ent->name[base_tree_ent->len - 1] = '/'; + + while (cbdata->iter.current) { + struct tree_entry *ent = cbdata->iter.current; + + int cmp = name_compare(ent->name, ent->len, + base_tree_ent->name, base_tree_ent->len); + if (!cmp || cmp < 0) { + tree_entry_iterator_advance(&cbdata->iter); + + if (add_tree_entry_to_index(cbdata, ent) < 0) { + result = error(_("failed to add tree entry '%s'"), ent->name); + goto cleanup_and_return; + } + + if (!cmp) { + result = 0; + goto cleanup_and_return; + } else + continue; + } + + break; + } + + /* + * If the tree entry should be replaced with an entry with the same name + * (but different mode), skip it. + */ + hashmap_entry_init(&base_tree_ent->ent, + memhash(base_tree_ent->name, df_path_len(base_tree_ent->len, base_tree_ent->mode))); + if (hashmap_get_entry(cbdata->df_name_hash, base_tree_ent, ent, NULL)) { + result = 0; + goto cleanup_and_return; + } + + if (add_tree_entry_to_index(cbdata, base_tree_ent)) { + result = -1; + goto cleanup_and_return; + } + + result = 0; + +cleanup_and_return: + FREE_AND_NULL(base_tree_ent); + return result; +} + +static void write_tree(struct tree_entry_array *arr, struct tree *base_tree, + struct object_id *oid) +{ + struct build_index_data cbdata = { 0 }; + struct pathspec ps = { 0 }; sort_and_dedup_tree_entry_array(arr); - tree_entry_iterator_init(&iter, arr); + index_state_init(&cbdata.istate, the_repository); + cbdata.istate.sparse_index = 1; + tree_entry_iterator_init(&cbdata.iter, arr); + cbdata.df_name_hash = &arr->df_name_hash; /* Construct an in-memory index from the provided entries & base tree */ - while (iter.current) { - struct tree_entry *ent = iter.current; - tree_entry_iterator_advance(&iter); + if (base_tree && + read_tree(the_repository, base_tree, &ps, build_index_from_tree, &cbdata) < 0) + die(_("failed to create tree")); + + while (cbdata.iter.current) { + struct tree_entry *ent = cbdata.iter.current; + tree_entry_iterator_advance(&cbdata.iter); - if (add_tree_entry_to_index(&istate, ent)) + if (add_tree_entry_to_index(&cbdata, ent)) die(_("failed to add tree entry '%s'"), ent->name); } /* Write out new tree */ - if (cache_tree_update(&istate, WRITE_TREE_SILENT | WRITE_TREE_MISSING_OK)) + if (cache_tree_update(&cbdata.istate, WRITE_TREE_SILENT | WRITE_TREE_MISSING_OK)) die(_("failed to write tree")); - oidcpy(oid, &istate.cache_tree->oid); + oidcpy(oid, &cbdata.istate.cache_tree->oid); - release_index(&istate); + release_index(&cbdata.istate); } static void write_tree_literally(struct tree_entry_array *arr, @@ -268,7 +347,7 @@ static void write_tree_literally(struct tree_entry_array *arr, } static const char *mktree_usage[] = { - "git mktree [-z] [--missing] [--literally] [--batch]", + "git mktree [-z] [--missing] [--literally] [--batch] []", NULL }; @@ -334,6 +413,7 @@ int cmd_mktree(int ac, const char **av, const char *prefix) struct tree_entry_array arr = { 0 }; struct mktree_line_data mktree_line_data = { .arr = &arr }; struct strbuf line = STRBUF_INIT; + struct tree *base_tree = NULL; int ret; const struct option option[] = { @@ -346,6 +426,22 @@ int cmd_mktree(int ac, const char **av, const char *prefix) }; ac = parse_options(ac, av, prefix, option, mktree_usage, 0); + if (ac > 1) + usage_with_options(mktree_usage, option); + + if (ac) { + struct object_id base_tree_oid; + + if (mktree_line_data.literally) + die(_("option '%s' and tree-ish cannot be used together"), "--literally"); + + if (repo_get_oid(the_repository, av[0], &base_tree_oid)) + die(_("not a valid object name %s"), av[0]); + + base_tree = parse_tree_indirect(&base_tree_oid); + if (!base_tree) + die(_("not a tree object: %s"), oid_to_hex(&base_tree_oid)); + } tree_entry_array_init(&arr); @@ -373,7 +469,7 @@ int cmd_mktree(int ac, const char **av, const char *prefix) if (mktree_line_data.literally) write_tree_literally(&arr, &oid); else - write_tree(&arr, &oid); + write_tree(&arr, base_tree, &oid); puts(oid_to_hex(&oid)); fflush(stdout); } diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 08760141d6f..435ac23bd50 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -234,4 +234,40 @@ test_expect_success 'mktree with duplicate entries' ' test_cmp expect actual ' +test_expect_success 'mktree with base tree' ' + tree_oid=$(cat tree) && + folder_oid=$(git rev-parse ${tree_oid}:folder) && + before_oid=$(git rev-parse ${tree_oid}:before) && + head_oid=$(git rev-parse HEAD) && + + { + printf "040000 tree $folder_oid\ttest\n" && + printf "100644 blob $before_oid\ttest.txt\n" && + printf "040000 tree $folder_oid\ttest-\n" && + printf "160000 commit $head_oid\ttest0\n" + } >top.base && + git mktree tree.base && + + { + printf "100755 blob $before_oid\tz\n" && + printf "160000 commit $head_oid\ttest.xyz\n" && + printf "040000 tree $folder_oid\ta\n" && + printf "100644 blob $before_oid\ttest\n" + } >top.append && + git mktree $(cat tree.base) tree.actual && + + { + printf "040000 tree $folder_oid\ta\n" && + printf "100644 blob $before_oid\ttest\n" && + printf "040000 tree $folder_oid\ttest-\n" && + printf "100644 blob $before_oid\ttest.txt\n" && + printf "160000 commit $head_oid\ttest.xyz\n" && + printf "160000 commit $head_oid\ttest0\n" && + printf "100755 blob $before_oid\tz\n" + } >expect && + git ls-tree $(cat tree.actual) >actual && + + test_cmp expect actual +' + test_done From patchwork Wed Jun 19 21:58:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704634 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (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 AE05915B107 for ; Wed, 19 Jun 2024 21:58:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834312; cv=none; b=sw8toDtUDiAscIgOyrifvgcHQppKDQD+Xi/b4/W5ewW2hUJEE3XtSzQOHECy3EtejGBcNNbcikjd5CBktuZGzXuzN825LhZt9eaMZZ0svmr3f1NvZWh5jCA6LViHsfoJTycvEN3hjdeR7GfNVQQ/fE548gLndAHzvsR4u7/Homw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834312; c=relaxed/simple; bh=y9kXnukQCaW2qEZVpqJ03InrVGLJdG/hB61ORUmL5vw=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=TiUXEXhAduysz9aiQ25Li+/Rh2CxuPwaMk9+vk9CpfkemCzpwLUNZ+0AXrtYTGXr+X+TXK21s+iy4JcZh4Kt2w331uGomZ6EYXfCFHoZrbfvrWcoVi5W+N+3AXbEg8HMKRcMuR6BO2IzoKefISoRrsL5r4OUqy7N4aVeczpLf9w= 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=fKxCqwPc; arc=none smtp.client-ip=209.85.128.47 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="fKxCqwPc" Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-42172ab4b60so2925045e9.0 for ; Wed, 19 Jun 2024 14:58:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834308; x=1719439108; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=IACMqB6eF9Z/KR274+LycKgdN94U1Sz+Li3LNDTj2qw=; b=fKxCqwPcGHms86pKfOCYy6FGMGU4KNKF5rx5ji6TLqa0hWgNbdeNndw9a/FTPsuGhK eNh5aRlp1KDKGv1JGkHbIoWocrLvAca4LFr5l8xQaw8SfRC/+fMjm7No7Uk0PIbU92Wl fvKgmgdRWRF5eH3ZX8aap/7C6qyOynVGlk/R8BGtkuiDiRn9gzdE2dZMGVPdm9+DORKf RgFTzQELFoJpowYlMGzZA6g23+UpLzSlAgQ1Gm615aCqxnw6my9826wG/YV010QmlBFb SzrWMOZ5q3xBToscAxzGbec2w4UNLbh+u8OXc/ZVyDDydgz+wTM2Ia4ERgf/TMJPaADA BKyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834308; x=1719439108; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=IACMqB6eF9Z/KR274+LycKgdN94U1Sz+Li3LNDTj2qw=; b=o1FJKBYz3vWLCqm218vEJv3t+jduxnnvYT10RyKsx1U4u9COgMAjq09jF5JnSKkdyR RhOvuNqm51/9+vPdWi0SOndlCzx2A97+aKxRvdcs4Em1x8dpkmCccQKALv+3AW3RdKsv ZL2UT1vUrMnITZdo38vWRcurBO4VNK8OmJSwtsc2zy7hFhhdfwmKV4Mr/M6cRNe/kKyL I7GrOLa4gPd+8CPaGuyqaOniQgQN86h6+JBUr1axWecHvspEKANBIh/gFzf4819rWeJN +ew8gwzitsK3kihmke3PqiQwK/GmLBV4nV2jbyRBOgEzwMGtlN9zdVjKRbE7tpE5cQVF MCAA== X-Gm-Message-State: AOJu0Yzzh4Q1mJvxHkqniB/peZajK+48WDmDRn1AXhyyWVcOJCNqxg/k 8hd4S4COsxsJXLa80LJJo/+dhNwDPvcgS4CiPKUr1T+ahCUYP48vTiwzKw== X-Google-Smtp-Source: AGHT+IG8GfKuO2l0KLyrgYys6pj1iwxys1Jsuh4otGspxuMlZUrFNEZ+gy20KLpkkgl16U7F5G5jZg== X-Received: by 2002:a05:600c:2106:b0:422:e7e8:588a with SMTP id 5b1f17b1804b1-424751763ddmr26829345e9.16.1718834308332; Wed, 19 Jun 2024 14:58:28 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0c54c9sm3907805e9.27.2024.06.19.14.58.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:27 -0700 (PDT) Message-Id: <46756c4e3140d34838ad4cd5e7a070d1f9f46b53.1718834285.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Wed, 19 Jun 2024 21:58:04 +0000 Subject: [PATCH v2 16/17] mktree: allow deeper paths in input Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye Update 'git mktree' to handle entries nested inside of directories (e.g. 'path/to/a/file.txt'). This functionality requires a series of changes: * In 'sort_and_dedup_tree_entry_array()', remove entries inside of directories that come after them in input order. * Also in 'sort_and_dedup_tree_entry_array()', mark directories that contain entries that come after them in input order (e.g., 'folder/' followed by 'folder/file.txt') as "need to expand". * In 'add_tree_entry_to_index()', if a tree entry is marked as "need to expand", recurse into it with 'read_tree_at()' & 'build_index_from_tree'. * In 'build_index_from_tree()', if a user-specified tree entry is contained within the current iterated entry, return 'READ_TREE_RECURSIVE' to recurse into the iterated tree. Signed-off-by: Victoria Dye --- Documentation/git-mktree.txt | 5 ++ builtin/mktree.c | 101 ++++++++++++++++++++++++++++++--- t/t1010-mktree.sh | 107 +++++++++++++++++++++++++++++++++-- 3 files changed, 200 insertions(+), 13 deletions(-) diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index 260d0e0bd7b..43cd9b10cc7 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -58,6 +58,11 @@ Higher stages represent conflicted files in an index; this information cannot be represented in a tree object. The command will fail without writing the tree if a higher order stage is specified for any entry. +Entries may use full pathnames containing directory separators to specify +entries nested within one or more directories. These entries are inserted +into the appropriate tree in the base tree-ish if one exists. Otherwise, +empty parent trees are created to contain the entries. + The order of the tree entries is normalized by `mktree` so pre-sorting the input by path is not required. Multiple entries provided with the same path are deduplicated, with only the last one specified added to the tree. diff --git a/builtin/mktree.c b/builtin/mktree.c index 96f06547a2a..74cec92a517 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -22,6 +22,7 @@ struct tree_entry { /* Internal */ size_t order; + int expand_dir; unsigned mode; struct object_id oid; @@ -39,6 +40,7 @@ struct tree_entry_array { struct tree_entry **entries; struct hashmap df_name_hash; + int has_nested_entries; }; static int df_name_hash_cmp(const void *cmp_data UNUSED, @@ -70,6 +72,13 @@ static void tree_entry_array_push(struct tree_entry_array *arr, struct tree_entr arr->entries[arr->nr++] = ent; } +static struct tree_entry *tree_entry_array_pop(struct tree_entry_array *arr) +{ + if (!arr->nr) + return NULL; + return arr->entries[--arr->nr]; +} + static void tree_entry_array_clear(struct tree_entry_array *arr, int free_entries) { if (free_entries) { @@ -109,8 +118,10 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat if (!verify_path(ent->name, mode)) die(_("invalid path '%s'"), path); - if (strchr(ent->name, '/')) - die("path %s contains slash", path); + + /* mark has_nested_entries if needed */ + if (!arr->has_nested_entries && strchr(ent->name, '/')) + arr->has_nested_entries = 1; /* Add trailing slash to dir */ if (S_ISDIR(mode)) @@ -168,6 +179,46 @@ static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) ignore_mode = 0; QSORT_S(arr->entries, arr->nr, ent_compare, &ignore_mode); + if (arr->has_nested_entries) { + struct tree_entry_array parent_dir_ents = { 0 }; + + count = arr->nr; + arr->nr = 0; + + /* Remove any entries where one of its parent dirs has a higher 'order' */ + for (size_t i = 0; i < count; i++) { + const char *skipped_prefix; + struct tree_entry *parent; + struct tree_entry *curr = arr->entries[i]; + int skip_entry = 0; + + while ((parent = tree_entry_array_pop(&parent_dir_ents))) { + if (!skip_prefix(curr->name, parent->name, &skipped_prefix)) + continue; + + /* entry in dir, so we push the parent back onto the stack */ + tree_entry_array_push(&parent_dir_ents, parent); + + if (parent->order > curr->order) + skip_entry = 1; + else + parent->expand_dir = 1; + + break; + } + + if (!skip_entry) { + arr->entries[arr->nr++] = curr; + if (S_ISDIR(curr->mode)) + tree_entry_array_push(&parent_dir_ents, curr); + } else { + FREE_AND_NULL(curr); + } + } + + tree_entry_array_release(&parent_dir_ents, 0); + } + /* Finally, initialize the directory-file conflict hash map */ for (size_t i = 0; i < count; i++) { struct tree_entry *curr = arr->entries[i]; @@ -212,15 +263,40 @@ struct build_index_data { struct index_state istate; }; +static int build_index_from_tree(const struct object_id *oid, + struct strbuf *base, const char *filename, + unsigned mode, void *context); + static int add_tree_entry_to_index(struct build_index_data *data, struct tree_entry *ent) { - struct cache_entry *ce; - ce = make_cache_entry(&data->istate, ent->mode, &ent->oid, ent->name, 0, 0); - if (!ce) - return error(_("make_cache_entry failed for path '%s'"), ent->name); + if (ent->expand_dir) { + int ret = 0; + struct pathspec ps = { 0 }; + struct tree *subtree = parse_tree_indirect(&ent->oid); + struct strbuf base_path = STRBUF_INIT; + strbuf_add(&base_path, ent->name, ent->len); + + if (!subtree) + ret = error(_("not a tree object: %s"), oid_to_hex(&ent->oid)); + else if (read_tree_at(the_repository, subtree, &base_path, 0, &ps, + build_index_from_tree, data) < 0) + ret = -1; + + strbuf_release(&base_path); + if (ret) + return ret; + + } else { + struct cache_entry *ce = make_cache_entry(&data->istate, + ent->mode, &ent->oid, + ent->name, 0, 0); + if (!ce) + return error(_("make_cache_entry failed for path '%s'"), ent->name); + + add_index_entry(&data->istate, ce, ADD_CACHE_JUST_APPEND); + } - add_index_entry(&data->istate, ce, ADD_CACHE_JUST_APPEND); return 0; } @@ -247,10 +323,12 @@ static int build_index_from_tree(const struct object_id *oid, base_tree_ent->name[base_tree_ent->len - 1] = '/'; while (cbdata->iter.current) { + const char *skipped_prefix; struct tree_entry *ent = cbdata->iter.current; + int cmp; - int cmp = name_compare(ent->name, ent->len, - base_tree_ent->name, base_tree_ent->len); + cmp = name_compare(ent->name, ent->len, + base_tree_ent->name, base_tree_ent->len); if (!cmp || cmp < 0) { tree_entry_iterator_advance(&cbdata->iter); @@ -264,6 +342,11 @@ static int build_index_from_tree(const struct object_id *oid, goto cleanup_and_return; } else continue; + } else if (skip_prefix(ent->name, base_tree_ent->name, &skipped_prefix) && + S_ISDIR(base_tree_ent->mode)) { + /* The entry is in the current traversed tree entry, so we recurse */ + result = READ_TREE_RECURSIVE; + goto cleanup_and_return; } break; diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 435ac23bd50..9b0e0cf302f 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -85,12 +85,21 @@ test_expect_success 'mktree with invalid submodule OIDs' ' done ' -test_expect_success 'mktree refuses to read ls-tree -r output (1)' ' - test_must_fail git mktree actual && + test_cmp tree actual ' -test_expect_success 'mktree refuses to read ls-tree -r output (2)' ' - test_must_fail git mktree actual && + test_cmp tree.withsub actual +' + +test_expect_success 'mktree de-duplicates files inside directories' ' + git ls-tree $(cat tree) >everything && + cat top_and_all && + git mktree actual && + test_cmp tree actual ' test_expect_success 'mktree fails on malformed input' ' @@ -234,6 +243,50 @@ test_expect_success 'mktree with duplicate entries' ' test_cmp expect actual ' +test_expect_success 'mktree adds entry after nested entry' ' + tree_oid=$(cat tree) && + folder_oid=$(git rev-parse ${tree_oid}:folder) && + one_oid=$(git rev-parse ${tree_oid}:folder/one) && + + { + printf "040000 tree $folder_oid\tearly\n" && + printf "100644 blob $one_oid\tearly/one\n" && + printf "100644 blob $one_oid\tlater\n" && + printf "040000 tree $EMPTY_TREE\tnew-tree\n" && + printf "100644 blob $one_oid\tnew-tree/one\n" && + printf "100644 blob $one_oid\tzzz\n" + } >top.rec && + git mktree tree.actual && + + { + printf "040000 tree $folder_oid\tearly\n" && + printf "100644 blob $one_oid\tlater\n" && + printf "040000 tree $folder_oid\tnew-tree\n" && + printf "100644 blob $one_oid\tzzz\n" + } >expect && + git ls-tree $(cat tree.actual) >actual && + + test_cmp expect actual +' + +test_expect_success 'mktree inserts entries into directories' ' + folder_oid=$(git rev-parse ${tree_oid}:folder) && + one_oid=$(git rev-parse ${tree_oid}:folder/one) && + blob_oid=$(git rev-parse ${tree_oid}:before) && + { + printf "040000 tree $folder_oid\tfolder\n" && + printf "100644 blob $blob_oid\tfolder/two\n" + } | git mktree >actual && + + { + printf "100644 blob $one_oid\tfolder/one\n" && + printf "100644 blob $blob_oid\tfolder/two\n" + } >expect && + git ls-tree -r $(cat actual) >actual && + + test_cmp expect actual +' + test_expect_success 'mktree with base tree' ' tree_oid=$(cat tree) && folder_oid=$(git rev-parse ${tree_oid}:folder) && @@ -270,4 +323,50 @@ test_expect_success 'mktree with base tree' ' test_cmp expect actual ' +test_expect_success 'mktree with base tree (deep)' ' + tree_oid=$(cat tree) && + folder_oid=$(git rev-parse ${tree_oid}:folder) && + before_oid=$(git rev-parse ${tree_oid}:before) && + folder_one_oid=$(git rev-parse ${tree_oid}:folder/one) && + head_oid=$(git rev-parse HEAD) && + + { + printf "100755 blob $before_oid\tfolder/before\n" && + printf "100644 blob $before_oid\tfolder/one.txt\n" && + printf "160000 commit $head_oid\tfolder/sub\n" && + printf "040000 tree $folder_oid\tfolder/one\n" && + printf "040000 tree $folder_oid\tfolder/one/deeper\n" + } >top.append && + git mktree tree.actual && + + { + printf "100755 blob $before_oid\tfolder/before\n" && + printf "100644 blob $before_oid\tfolder/one.txt\n" && + printf "100644 blob $folder_one_oid\tfolder/one/deeper/one\n" && + printf "100644 blob $folder_one_oid\tfolder/one/one\n" && + printf "160000 commit $head_oid\tfolder/sub\n" + } >expect && + git ls-tree -r $(cat tree.actual) -- folder/ >actual && + + test_cmp expect actual +' + +test_expect_success 'mktree fails on directory-file conflict' ' + tree_oid="$(cat tree)" && + blob_oid="$(git rev-parse $tree_oid:folder.txt)" && + + { + printf "100644 blob $blob_oid\ttest\n" && + printf "100644 blob $blob_oid\ttest/deeper\n" + } | + test_must_fail git mktree 2>err && + test_grep "You have both test and test/deeper" err && + + { + printf "100644 blob $blob_oid\tfolder/one/deeper/deep\n" + } | + test_must_fail git mktree $tree_oid 2>err && + test_grep "You have both folder/one and folder/one/deeper/deep" err +' + test_done From patchwork Wed Jun 19 21:58:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victoria Dye X-Patchwork-Id: 13704635 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) (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 ACB1E15E5B6 for ; Wed, 19 Jun 2024 21:58:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834313; cv=none; b=EXdWhz1fwLlZ0VdJJG9KgV2nUHOa98gWP9V4woMIvK/LCfDGGikx5Dg9wm8Foq1J3PfSj4vY8t/nlQ2rzGdkj4ngvu14U1Maekdwqhwrjlonhx+mZ/YTqIHonS2wxHbkSUZq7QjlzlRXSIAFtn3J6Iv+r+McbNzCKm6eEndZ6Qk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718834313; c=relaxed/simple; bh=1qdLIoFSUgyvssKL+DH0shSkFtdUFYfnIwnU85xRdZY=; h=Message-Id:In-Reply-To:References:From:Date:Subject:Content-Type: MIME-Version:To:Cc; b=GCGJ9jJmaYmjqcfIDnjPW8Lk3YgfUi2Is/8zkLhb0zbvsPONQcck44LUqIDO2wmUqNmmpLniVt52k1ULKsJcvScdIcI+fIeMIU8G6onWQRk48YQQkXQQXdinxAvalibG65S8UkaeRTE7VPuhHmHXgBZZyzTKhs8QWbAaJR1zzXM= 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=HFOlGt4Z; arc=none smtp.client-ip=209.85.128.50 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="HFOlGt4Z" Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-421f4d1c057so2684375e9.3 for ; Wed, 19 Jun 2024 14:58:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718834310; x=1719439110; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:from:to:cc:subject:date :message-id:reply-to; bh=OKVNq4r8msXSpCZMZ10W80EOKH6+V9QNxWEoviIrmrM=; b=HFOlGt4ZF0sUpiI5Xhnozjrv+ScHKd4wnsCU+sGO0EWkqI8gwzdriAYPYy0zYGRxyG aUbVoYVlSvEgWkHUy9oslEtDw/S3jx8T5Yi84HCnHOSrzQ9t37WTHyPR4Zbcr8+eooFT eMh/5ja95UBVVt+17c9ZVaOm0NNhcLKWh/t4WNdusvf4bXtTpn5nKI/yFUglQAXF2JQe 2VCyplqC9r5r4M/eRFdIU04OKaOt0z4yxwdwQTmcZco0rVGM1nkCQr13cKLo6duZlTuz SS/ha29bj83jtSDUUZDIvQuo0j5nnYYMpoNJdQCZS/4PsN9L9T3t0qFDn9dNDi/PPW9u no5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718834310; x=1719439110; h=cc:to:mime-version:content-transfer-encoding:fcc:subject:date:from :references:in-reply-to:message-id:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=OKVNq4r8msXSpCZMZ10W80EOKH6+V9QNxWEoviIrmrM=; b=TJsiBvcdQ57TJ2iTGhJhb/GbDyLCXI/bVKvxq1toNjVMXwcL/RR1olXO8Ixus01HAG y9ktf/FhG7tlnBBbONz3gjHFfgPZCl8xZqHzCPy9ilyuln6la7colLeWzYhu8ur1crYT U6FL+wwD3K9KUf0eaKm7uSwR+HYhmgzwIQQKQmU/vKbB3BhzRmhzipEDxNVYXosP+9v3 erOR+wU3HEYkeVVJChLt5CyxZGrt3ZGHLfn90nMMfhux5Ot/3PSohH9ZsT30yei4JLN/ K3AKsr7k8FW0DEihjfuz6wbpyj2skGZujdMi8wrfsCUuC+qiAx/UOi8Skr6k/n7xkXJh L4rg== X-Gm-Message-State: AOJu0Yx60/Rs9wkNi9AwnOjfVCW/WDp5XnvG8LdWo7wZzBQDerxQPyIs vZaY3M/vf7XQoiOzqscJSI59Z/VfM6UnMgehxO07TejS4s7VqBniSix0JA== X-Google-Smtp-Source: AGHT+IEPP2F8/N+4wecG7jD/zqNAfbKIv2z2uSk0T0OH4jLu0suqEL9KDo6NAg3Qb2hADxqTwAE2dQ== X-Received: by 2002:a05:600c:214c:b0:422:615f:649c with SMTP id 5b1f17b1804b1-42475078374mr29183765e9.7.1718834309773; Wed, 19 Jun 2024 14:58:29 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4247d0c0d56sm3828205e9.13.2024.06.19.14.58.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Jun 2024 14:58:28 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Wed, 19 Jun 2024 21:58:05 +0000 Subject: [PATCH v2 17/17] mktree: remove entries when mode is 0 Fcc: Sent Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To: git@vger.kernel.org Cc: Eric Sunshine , Patrick Steinhardt , Victoria Dye , Victoria Dye From: Victoria Dye From: Victoria Dye If tree entries are specified with a mode with value '0', remove them from the tree instead of adding/updating them. If the mode is '0', both the provided type string (if specified) and the object ID of the entry are ignored. Note that entries with mode '0' are added to the 'struct tree_ent_array' with a trailing slash so that it's always treated like a directory. This is a bit of a hack to ensure that the removal supercedes any preceding entries with matching names, as well as any nested inside a directory matching its name. Signed-off-by: Victoria Dye --- Documentation/git-mktree.txt | 4 ++++ builtin/mktree.c | 16 +++++++++++---- t/t1010-mktree.sh | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index 43cd9b10cc7..52e6005c1d3 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -63,6 +63,10 @@ entries nested within one or more directories. These entries are inserted into the appropriate tree in the base tree-ish if one exists. Otherwise, empty parent trees are created to contain the entries. +An entry with a mode of "0" will remove an entry of the same name from the +base tree-ish. If no tree-ish argument is given, or the entry does not exist +in that tree, the entry is ignored. + The order of the tree entries is normalized by `mktree` so pre-sorting the input by path is not required. Multiple entries provided with the same path are deduplicated, with only the last one specified added to the tree. diff --git a/builtin/mktree.c b/builtin/mktree.c index 74cec92a517..e7adcb384c8 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -32,7 +32,7 @@ struct tree_entry { static inline size_t df_path_len(size_t pathlen, unsigned int mode) { - return S_ISDIR(mode) ? pathlen - 1 : pathlen; + return (S_ISDIR(mode) || !mode) ? pathlen - 1 : pathlen; } struct tree_entry_array { @@ -108,7 +108,7 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat size_t len_to_copy = len; /* Normalize and validate entry path */ - if (S_ISDIR(mode)) { + if (S_ISDIR(mode) || !mode) { while(len_to_copy > 0 && is_dir_sep(path[len_to_copy - 1])) len_to_copy--; len = len_to_copy + 1; /* add space for trailing slash */ @@ -124,7 +124,7 @@ static void append_to_tree(unsigned mode, struct object_id *oid, const char *pat arr->has_nested_entries = 1; /* Add trailing slash to dir */ - if (S_ISDIR(mode)) + if (S_ISDIR(mode) || !mode) ent->name[len - 1] = '/'; } @@ -209,7 +209,7 @@ static void sort_and_dedup_tree_entry_array(struct tree_entry_array *arr) if (!skip_entry) { arr->entries[arr->nr++] = curr; - if (S_ISDIR(curr->mode)) + if (S_ISDIR(curr->mode) || !curr->mode) tree_entry_array_push(&parent_dir_ents, curr); } else { FREE_AND_NULL(curr); @@ -270,6 +270,9 @@ static int build_index_from_tree(const struct object_id *oid, static int add_tree_entry_to_index(struct build_index_data *data, struct tree_entry *ent) { + if (!ent->mode) + return 0; + if (ent->expand_dir) { int ret = 0; struct pathspec ps = { 0 }; @@ -450,6 +453,10 @@ static int mktree_line(unsigned int mode, struct object_id *oid, if (stage) die(_("path '%s' is unmerged"), path); + /* OID ignored for zero-mode entries; append unconditionally */ + if (!mode) + goto append_entry; + if (obj_type != OBJ_ANY && mode_type != obj_type) die("object type (%s) doesn't match mode type (%s)", type_name(obj_type), type_name(mode_type)); @@ -484,6 +491,7 @@ static int mktree_line(unsigned int mode, struct object_id *oid, } } +append_entry: append_to_tree(mode, oid, path, data->arr, data->literally); return 0; } diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index 9b0e0cf302f..5ed4352054a 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -369,4 +369,42 @@ test_expect_success 'mktree fails on directory-file conflict' ' test_grep "You have both folder/one and folder/one/deeper/deep" err ' +test_expect_success 'mktree with remove entries' ' + tree_oid="$(cat tree)" && + blob_oid="$(git rev-parse $tree_oid:folder.txt)" && + + { + printf "100644 blob $blob_oid\ttest/deeper/deep.txt\n" && + printf "100644 blob $blob_oid\ttest.txt\n" && + printf "100644 blob $blob_oid\texample\n" && + printf "100644 blob $blob_oid\texample.a/file\n" && + printf "100644 blob $blob_oid\texample.txt\n" && + printf "040000 tree $tree_oid\tfolder\n" && + printf "0 $ZERO_OID\tfolder\n" && + printf "0 $ZERO_OID\tmissing\n" + } | git mktree >tree.base && + + { + printf "0 $ZERO_OID\texample.txt\n" && + printf "0 $ZERO_OID\ttest/deeper\n" + } | git mktree $(cat tree.base) >tree.actual && + + { + printf "100644 blob $blob_oid\texample\n" && + printf "100644 blob $blob_oid\texample.a/file\n" && + printf "100644 blob $blob_oid\ttest.txt\n" + } >expect && + git ls-tree -r $(cat tree.actual) >actual && + + test_cmp expect actual +' + +test_expect_success 'type and oid not checked if entry mode is 0' ' + # type and oid do not match + printf "0 commit $EMPTY_TREE\tfolder.txt\n" | + git mktree >tree.actual && + + test "$(cat tree.actual)" = $EMPTY_TREE +' + test_done