From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (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 03A632737F9 for ; Sun, 23 Aug 2026 17:19:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787505565; cv=none; b=m2IFzXRo+VCXiNvXSi+khR2khkkDDUoiNf2bUgQ6AqZpwmeNE2t8sfw/9c6XG9TYB0UqxGejU8MmxMXetcLJRLU/rzlpe8rvJn8R3mraTkNyH2KHPICzx50/GcFIlPsb1F6QITX43e/AredVKKrlSXnMmsL+vxmZt/COy5j1yK4= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787505565; c=relaxed/simple; bh=r8BzhHLzjiXZkMwkPNEfdukw74jf8shXVaNl7U9ykrw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pgy3czRY1ldvI9x+Tmb9fjWj8Cz8FIKi2d5KHZkT/RiUSpnvzTfeKm+Fft6qZmv4YszHnosbaPgoyYhf0J+hLgHNxpySrDGgiVxpKobzUUc5wV9wUW3ma3f++aivBFVTR8GkGrHeHzsUpkE4v2IAFF95F9OUvsxOdEpUeutQRAo= 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=a9wXDSx3; arc=none smtp.client-ip=209.85.214.173 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="a9wXDSx3" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-2d5655cc850so30890235ad.3 for ; Sun, 23 Aug 2026 10:19:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787505559; x=1788110359; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=EbKFuEFgJBaG5hn3Jd+xzPg53O4o8IfcggfcfQP7BXo=; b=a9wXDSx3WrBc1IFA8Unky31XhzE3WatXNIWzZqpaEQGt4OG3qJn8XngX9+2YswNqXt dyloNpqTdqWeeG3O/EK8ZuyBXFRokISenHCsT4QdCynP6GBX0qZaubE+CxU2ZCV/jXgW Lp0kt8pW/OC21ec9An3sc6/X4v512ny4umG4qxgq337d9dZYu+BY0+k4hVs+FgXskvlt GBdCGtqwUIfg+cbzofQMVIxICsY+ccfJ0ptwsAG9vxvlW/1ukV/Z/7DZCKM4UePSCUaS XFKg+UbGjRuDm6LzsSapkGTmYdHfrKoG2RiuswMb8Wh3k8GIeGjfk8ACGLAKcZSbW/zm 7rNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787505559; x=1788110359; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=EbKFuEFgJBaG5hn3Jd+xzPg53O4o8IfcggfcfQP7BXo=; b=qwiHjUfuU6O4KJOv2ZGkn3Ntih5eOgc0NvxVdsG7EyooDpaazmNPZrH4VusPpJPBdI rgcJCDiNHRQevaTgWnNjFO2H4TndcReRtZWRda4jegmgOt0xMUsL8bvj2+Is8wNMeU2u BakhmB1rPfvpHFbjfUs53N0cyVxRulFeRRba8hKxQ39a8jd9k2CRJXfO81yJOFfmtFpv R1ZhGnpRBDnfA5rtiUw2iPuHkGn1w4y041VhqlHE3y6UyuMwfMNoLlhg1k1RjtLQUXRM TlRKMCKtXaV0cBEDCqP6Yu/ToKXOkSciEjlrXZvD1RVTyS2tcDQMTkDNibf1i02zEEme +mIg== X-Gm-Message-State: AFuF++kZ1PRA9kIeBB3tBZxeW1jvhPk3iuX8r45bwRJq89AWsQjEgc/W FdWCnYaPPEZf2dWTSvQUFDH5QxBu7uUd3HztigAplTN8H6O8tEo3bVZXa4MHKA== X-Gm-Gg: AR+sD10lnZVbHKWvRJeR98zMrLVRqi/4bW6EZTZoxCmBZNrQM4hazLohO6Nv0hmCVcA PeNxYzOjxARmjPQoT4M6xl6q/xFVJG8vR6KWZB175kiKO9boGN+o2tiyJdIXPQK2N7Y5a9w3W1S UwZBTkYglvAQ+gKAJzVHLLcd9nkwwj7EatUtv53q+OYtDCCejtOTImyH8NW21tDevijcl80CN8o iFPn26vPTeazWyPTGiaBkF3A40Ndrwqcfk0AQhU1UAPTxO1S/U2DVJzK47NiCYVIBCYCWDQ+A0D PO3d+uddS0aarb19G4ytGKUuOgCEzp4BGaNA71phNl4m7KnZbocM5NPggKPfDjrhu4h7y8QHoFD N9BAMPz9Y41LtI141tc3yTwXd/IDPT++ZFkdu2EuR+MfnlzDylP+BAEd5HE0IEn3ZL/IzL/DpPj hZzOzuom7wUwkKPvAhvLUawoFtmrLLkWbXm6BoapBSshedgjWc5LdPhPUywFP4rWfuY0CxjuGsZ B+Jk2I2fnJvf+NHvg5VJZyezi9xX2526X7V1rv3lhf5YxuE+C2i0ZQyREQHwLvUbyZkTUeJfdGV BOb64H9RZLwUEdT+8N69nm33455Mg32Tkw88AFnYne2pdQ== X-Received: by 2002:a17:903:2b03:b0:2c7:f4bd:91b5 with SMTP id d9443c01a7336-2d64a9b349bmr397038375ad.0.1787505558512; Sun, 23 Aug 2026 10:19:18 -0700 (PDT) Received: from localhost (192-184-169-91.fiber.dynamic.sonic.net. [192.184.169.91]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f91d37cesm20925886eec.15.2026.08.23.10.19.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 23 Aug 2026 10:19:17 -0700 (PDT) From: Michael Montalbo To: git@vger.kernel.org Cc: Patrick Steinhardt Subject: [RFC PATCH 01/14] organize: add the git organize builtin Date: Sun, 23 Aug 2026 10:18:45 -0700 Message-ID: <20260823171915.2662373-2-mmontalbo@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260823171915.2662373-1-mmontalbo@gmail.com> References: <20260823171915.2662373-1-mmontalbo@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add "git organize", a builtin that reconciles a source tree against a layout the project declares. A project records where each file belongs, and git organize reports the files that sit elsewhere and, on request, moves them into place. As .gitignore declares what stays untracked and .mailmap declares canonical author names, git organize adds .gitorganize, which declares the directory a file belongs in. .gitorganize has three sections. [scope] lists the pathspecs of the files in scope, one per line; with no [scope] section nothing is in scope. [layout] is the project's placement map, hand-authored: ordered "label:value = directory" rules, and a file takes the directory of the first rule its labels satisfy, so rule order matters. Git lists role:public = . and role:program = . ahead of the component rules, so an interface header or a program that also matches a component stays at root. [labels] records each file's labels, one line per source in scope with its key=value labels. "git organize status" reads [labels] and reports the files that sit outside their directory, the backlog, and a recorded path that no longer exists. It runs nothing and changes nothing. "git organize apply" moves each out-of-place file into its directory as one git apply --index transaction; a content-identical rename lets git log --follow and git blame track the file. It stages the result and repoints each moved file's [labels] line to its new path. The .gitorganize reader and writer live in organize/gitorganize-format.c; the engine that plans and applies the moves lives in organize/organize.c. Signed-off-by: Michael Montalbo --- .gitignore | 1 + Documentation/git-organize.adoc | 82 ++++++++ Documentation/meson.build | 1 + Makefile | 3 + builtin.h | 1 + builtin/organize.c | 110 +++++++++++ command-list.txt | 1 + git.c | 1 + meson.build | 3 + organize/gitorganize-format.c | 261 ++++++++++++++++++++++++++ organize/gitorganize-format.h | 38 ++++ organize/organize.c | 320 ++++++++++++++++++++++++++++++++ organize/organize.h | 77 ++++++++ t/meson.build | 1 + t/t0096-organize.sh | 185 ++++++++++++++++++ 15 files changed, 1085 insertions(+) create mode 100644 Documentation/git-organize.adoc create mode 100644 builtin/organize.c create mode 100644 organize/gitorganize-format.c create mode 100644 organize/gitorganize-format.h create mode 100644 organize/organize.c create mode 100644 organize/organize.h create mode 100755 t/t0096-organize.sh diff --git a/.gitignore b/.gitignore index 4da58c6754..b85ce2b13a 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,7 @@ /git-mv /git-name-rev /git-notes +/git-organize /git-p4 /git-pack-redundant /git-pack-objects diff --git a/Documentation/git-organize.adoc b/Documentation/git-organize.adoc new file mode 100644 index 0000000000..4ff76f5c13 --- /dev/null +++ b/Documentation/git-organize.adoc @@ -0,0 +1,82 @@ +git-organize(1) +=============== + +NAME +---- +git-organize - Reconcile a source tree against a declared layout + + +SYNOPSIS +-------- +[verse] +'git organize status' +'git organize apply' + + +DESCRIPTION +----------- +A project declares where each of its files belongs, and git organize +reports the files that sit elsewhere and, on request, moves them into +place. As `.gitignore` declares what stays untracked and `.mailmap` +declares canonical author names, `.gitorganize` declares the directory a +file belongs in. + +`.gitorganize` at the repository root has three sections. `[scope]` names +the files in scope, one pathspec per line; with no `[scope]` section +nothing is in scope. `[layout]` is the project's placement map, authored by +hand: ordered `